PyTorch 0.4.1 examples (コード解説) : 画像分類 – Oxford 花 17 種 (VGG)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/07/2018 (0.4.1)
* 本ページは、github 上の以下の pytorch/examples と keras/examples レポジトリのサンプル・コードを参考にしています:
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
VGG
PyTorch 0.4.1 の自作のサンプルをコードの簡単な解説とともに提供しています。
初級チュートリアル程度の知識は仮定しています。
MNIST / Fashion-MNIST / CIFAR-10 & CIFAR-100 について一通りウォークスルーした後、
17 Category Flower Dataset を題材にして AlexNet を試してみました。
今回は VGG で試します。
University of Oxford: 17 種フラワー・データセット
ここでは University of Oxford が提供している古典的な題材を利用します。
データセットの詳細は 17 Category Flower Dataset を参照してください。
VGG 11 モデル定義
VGG の実装は torchvision に含まれていますが、まずは自前で実装してみます。
※ VGG の詳細は Very Deep Convolutional Networks for Large-Scale Visual Recognition (by Karen Simonyan & Andrew Zisserman) を参照してください。
VGG は一般には VGG 16 と VGG 19 を指しますが、最初に見当をつけるためにもう少し単純化した VGG 11 を定義しました 。
この場合、抽出器の層スタックは次のようなものになります :
* ブロック 1 * Conv2d (out_channels=64, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 2 * Conv2d (out_channels=128, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 3 * Conv2d (out_channels=256, kernel_size=3) Conv2d (out_channels=256, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 4 * Conv2d (out_channels=512, kernel_size=3) Conv2d (out_channels=512, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 5 * Conv2d (out_channels=512, kernel_size=3) Conv2d (out_channels=512, kernel_size=3) MaxPool2d(kernel_size=2, stride=2)
そしてストレートにコーディングすれば以下のようになります。
ブロックに分けたのは便宜的なもので、nn.Sequential でまとめてかまいません :
class VGG11(nn.Module):
def __init__(self, num_classes):
super(VGG11, self).__init__()
self.block1_output = nn.Sequential (
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block2_output = nn.Sequential (
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block3_output = nn.Sequential (
nn.Conv2d(128, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block4_output = nn.Sequential (
nn.Conv2d(256, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block5_output = nn.Sequential (
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes),
)
def forward(self, x):
x = self.block1_output(x)
x = self.block2_output(x)
x = self.block3_output(x)
x = self.block4_output(x)
x = self.block5_output(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
VGG11 モデルを訓練 / 評価する
学習率 0.008, 0.006 と 0.004 で 50 エポック訓練してみます :


結果をまとめると :
- 0.008 : 69.29 %
- 0.006 : 81.89 %
- 0.004 : 81.89 %
5.0e-3 前後であれば問題なさそうです。
VGG 13 (with バッチ正規化層) モデル定義
VGG 11 が一応機能しているようなので、次に VGG 13 を定義します :
* ブロック 1 * Conv2d (out_channels=64, kernel_size=3) Conv2d (out_channels=64, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 2 * Conv2d (out_channels=128, kernel_size=3) Conv2d (out_channels=128, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 3 * Conv2d (out_channels=256, kernel_size=3) Conv2d (out_channels=256, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 4 * Conv2d (out_channels=512, kernel_size=3) Conv2d (out_channels=512, kernel_size=3) MaxPool2d(kernel_size=2, stride=2) * ブロック 5 * Conv2d (out_channels=512, kernel_size=3) Conv2d (out_channels=512, kernel_size=3) MaxPool2d(kernel_size=2, stride=2)
◆ そのコーディングですが、バッチ正規化層と初期化を付け加えました :
class VGG13(nn.Module):
def __init__(self, num_classes):
super(VGG13, self).__init__()
self.block1_output = nn.Sequential (
nn.Conv2d(3, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block2_output = nn.Sequential (
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block3_output = nn.Sequential (
nn.Conv2d(128, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block4_output = nn.Sequential (
nn.Conv2d(256, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.block5_output = nn.Sequential (
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes),
)
self._initialize_weights()
def forward(self, x):
x = self.block1_output(x)
x = self.block2_output(x)
x = self.block3_output(x)
x = self.block4_output(x)
x = self.block5_output(x)
#print(x.size())
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
#init.orthogonal_(m.weight.data, gain=init.calculate_gain('relu'))
init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
if m.bias is not None:
init.constant_(m.bias, 0)
elif isinstance(m, nn.BatchNorm2d):
init.constant_(m.weight, 1)
init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
init.normal_(m.weight, 0, 0.01)
init.constant_(m.bias, 0)
VGG 13 (with バッチ正規化層) モデルを訓練 / 評価する


まとめると :
- 0.001 : 72.44 %
- 0.0005 : 83.46 %
- 0.00025 : 81.10 %
- 0.0001 : 82.68 %
以上