【後編】Pytorchの様々な最適化手法(torch.optim.Optimizer)の更新過程や性能を比較検証してみた!
IT技術
後編~最適化手法の更新過程や性能を比較検証してみよう!~
前回に引き続き、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 を使用して、各種最適化手法の違いについて検証しました。
最適化手法は、ここで紹介したものだけでなく、どんどん新しいものも提案されています。
目的に合わせて、自分の環境や設定の中で変数を変えながら、色々な最適化手法を試してみてください!
前回の記事
こちらの記事もオススメ!
2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit
おすすめ記事
immichを知ってほしい
2024.10.31