• トップ
  • ブログ一覧
  • 【後編】Pytorchの様々な最適化手法(torch.optim.Optimizer)の更新過程や性能を比較検証してみた!
  • 【後編】Pytorchの様々な最適化手法(torch.optim.Optimizer)の更新過程や性能を比較検証してみた!

    メディアチームメディアチーム
    2020.07.09

    IT技術

    後編~最適化手法の更新過程や性能を比較検証してみよう!~

    前回に引き続き、Pytorchに用意されている各種最適化手法(torch.optim.Optimizer)の学習過程がどのように異なるのかについて、「損失関数」や「精度の比較」により検証します。

    本記事は「前編」「後編」でお届けいたします。

    前編はこちら

    featureImg2020.07.08【前編】Pytorchの様々な最適化手法(torch.optim.Optimizer)の更新過程や性能を比較検証してみた!最適化手法の更新過程や性能を比較検証してみよう!機械学習では、学習を行う際に、「損失関数の値」と「勾配」からパラメータ...

    全結合層ニューラルネットワークによる検証

    では続いて、ニューラルネットワークのモデルで実際にデータを与えて、各種最適化手法の検証を行っていきたいと思います。

    学習データには、「MNISTデータセット」を用います。

    コード

    1import torch
    2from torchvision import datasets, transforms
    3from torch import nn, optim
    4import torch.nn.functional as F
    5
    6# transforms
    7transform = transforms.Compose([transforms.ToTensor(), 
    8                                transforms.Normalize((0.5,), (0.5,))])
    9
    10# dataset
    11train_dataset = datasets.MNIST(root='./content/',
    12                               train=True,
    13                               transform=transform,
    14                               download=True)
    15
    16test_dataset = datasets.MNIST(root='./content/',
    17                              train=False,
    18                              transform=transform)
    19
    20# Data Loader
    21batch_size = 256
    22train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
    23                                           batch_size=batch_size,
    24                                           shuffle=True)
    25
    26test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
    27                                          batch_size=batch_size,
    28                                          shuffle=False)

    学習モデルはシンプルな全結合層ネットワークで、上記で行ったように各 optimizer 毎に学習を行い、「学習中の損失関数」と「精度の推移」を記録します。

    各optimizerのパラメータ

    各 optimizer には、「model.parameters()」によるモデルのパラメータと学習率 (lr = 0.01) を渡しています。

    また、それ以外はデフォルトの値となっています。

    1#Model
    2class Net(nn.Module):
    3  def __init__(self):
    4    super(Net, self).__init__()
    5    self.fc1 = nn.Linear(28*28, 100)
    6    self.fc2 = nn.Linear(100, 100)
    7    self.fc3 = nn.Linear(100, 10)
    8
    9  def forward(self, x):
    10    x = F.relu(self.fc1(x))
    11    x = F.relu(self.fc2(x))
    12    return self.fc3(x)
    13
    14criterion = nn.CrossEntropyLoss()
    15
    16classes = ['SGD', 'Adagrad', 'RMSprop', 'Adadelta', 'Adam', 'AdamW']
    17loss_fc = {}
    18acc_fc = {}
    19optimizers = {}
    20for key in classes:
    21  loss_fc[key] = []
    22  acc_fc[key] = []
    23
    24lr = 0.01
    25
    26for key in classes:
    27 
    28  model = Net()
    29
    30  optimizers['SGD'] = optim.SGD(model.parameters(), lr)
    31  optimizers['Adagrad'] = optim.Adagrad(model.parameters(), lr)
    32  optimizers['RMSprop'] = optim.RMSprop(model.parameters(), lr)
    33  optimizers['Adadelta'] = optim.Adadelta(model.parameters(), lr)
    34  optimizers['Adam'] = optim.Adam(model.parameters(), lr)
    35  optimizers['AdamW'] = optim.AdamW(model.parameters(), lr)
    36
    37  for epoch in range(20):
    38    model.train()
    39
    40    for image, label in train_loader:
    41      image = image.view(-1, 28*28)
    42      optimizers[key].zero_grad()
    43      output = model(image)
    44      loss = criterion(output, label)
    45      loss.backward()
    46      optimizers[key].step()
    47      loss_fc[key].append(loss.item())
    48
    49    model.eval()
    50    with torch.no_grad():
    51      for t_image, t_label in test_loader:
    52        t_image = t_image.view(-1, 28*28)
    53        output = model(t_image)
    54        _, predicted = torch.max(output, 1)
    55        class_correct = (predicted == t_label).sum().item()
    56        acc = class_correct / len(predicted) * 100
    57        acc_fc[key].append(acc)

    損失関数の値の比較

    まず以下が、損失関数の値の推移の結果になります。

    「Adagrad」、「Adam」、「AdamW」は早期に損失関数の値が小さい値に収束しています。

    それに比べて、「Adadelta」、「RMSprop」、「SGD」は学習の速度が遅いということがわかります。

    また「RMSprop」については、他と比べて値が大きく上下しており安定していない様子が見られます。

    精度の比較

    さらに、以下のグラフが精度の推移になります。

    損失関数の値と同様に、「Adagrad」、「Adam」、「AdamW」の精度は他と比べ高いことがわかります。

    また、損失関数のグラフにノイズが大きかった「RMSprop」では、精度も安定していません。

    学習初期を比較

    精度の学習初期(~100 iterations)を拡大すると、以下のようになります。

    いずれも学習が進めば精度が向上するものの、 「Adadelta」や「SGD」は初期の精度が他と比べると低いことが分かります。

    また、損失関数の挙動にも現れているように、学習収束速度は比較的遅いという結果になりました。

    CNNモデルによる検証

    次に、 CNN モデルにより、先ほどと同様に検証したいと思います。

    コード

    1#CNN Model
    2class Cnn(nn.Module):
    3  def __init__(self):
    4    super(Cnn, self).__init__()
    5    self.conv1 = nn.Conv2d(1, 6, 5)
    6    self.conv2 = nn.Conv2d(6, 16, 5)
    7    self.pool = nn.MaxPool2d(2, 2)
    8    self.fc1 = nn.Linear(16*4*4, 100)
    9    self.fc2 = nn.Linear(100, 10)
    10
    11  def forward(self, x):
    12    x = self.pool(F.relu(self.conv1(x)))
    13    x = self.pool(F.relu(self.conv2(x)))
    14    x = x.view(-1, 16*4*4)
    15    x = F.relu(self.fc1(x))
    16    return self.fc2(x)
    17
    18classes = ['SGD', 'Adagrad', 'RMSprop', 'Adadelta', 'Adam', 'AdamW']
    19loss_cnn = {}
    20acc_cnn = {}
    21for key in classes:
    22  loss_cnn[key] = []
    23  acc_cnn[key] = []
    24
    25lr = 0.01
    26criterion = nn.CrossEntropyLoss()
    27optimizers = {}
    28
    29for key in classes:
    30 
    31  model = Cnn()
    32
    33  optimizers['SGD'] = optim.SGD(model.parameters(), lr)
    34  optimizers['Adagrad'] = optim.Adagrad(model.parameters(), lr)
    35  optimizers['RMSprop'] = optim.RMSprop(model.parameters(), lr)
    36  optimizers['Adadelta'] = optim.Adadelta(model.parameters(), lr)
    37  optimizers['Adam'] = optim.Adam(model.parameters(), lr)
    38  optimizers['AdamW'] = optim.AdamW(model.parameters(), lr)
    39
    40  for epoch in range(20):
    41    model.train()
    42    for image, label in train_loader:
    43      optimizers[key].zero_grad()
    44      output = model(image)
    45      loss = criterion(output, label)
    46      loss.backward()
    47      optimizers[key].step()
    48      loss_cnn[key].append(loss.item())
    49
    50    model.eval()
    51    with torch.no_grad():
    52      for t_image, t_label in test_loader:
    53        output = model(t_image)
    54        _, predicted = torch.max(output, 1)
    55        class_correct = (predicted == t_label).sum().item()
    56        acc = class_correct / len(predicted) * 100
    57        acc_cnn[key].append(acc)

    比較結果

    モデルの違いにより、損失関数の収束値と精度は共に向上しています。

    特に、「RMSprop」はノイズが抑えられ、精度共に改善されたと思います。

    全結合層ネットワークの場合と同様に、「Adadelta」と「SGD」では学習速度が他に比べ遅い結果となりました。

    以下は、各比較結果のグラフです。

    損失関数の値の比較

    精度の比較

    学習初期の比較

    学習初期は、やはり学習速度に違いが出ていることがわかります。

    まとめ

    今回は、「Adagrad」や「Adam」が、学習速度や精度共に優れているという結果となりました。

    しかし厳密には、各 optimizer 毎の詳細なチューニング(学習率など各パラメータの設定)や、ネットワークモデル、学習データにより結果が変わってきます

    また、向き不向きが方法によって違うので、optimizer 毎に詳細な検討は必要かと思います。

    さいごに

    今回は、 Pytorch を使用して、各種最適化手法の違いについて検証しました。

    最適化手法は、ここで紹介したものだけでなく、どんどん新しいものも提案されています。

    目的に合わせて、自分の環境や設定の中で変数を変えながら、色々な最適化手法を試してみてください!

    前回の記事

    featureImg2020.07.08【前編】Pytorchの様々な最適化手法(torch.optim.Optimizer)の更新過程や性能を比較検証してみた!最適化手法の更新過程や性能を比較検証してみよう!機械学習では、学習を行う際に、「損失関数の値」と「勾配」からパラメータ...

    こちらの記事もオススメ!

    featureImg2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background