本記事について
本記事は,深層学習ライブラリPyTorchを用いてTorch7のモデル(しばしば.t7
の拡張子で保存される)を読み込むまでの苦闘を記録したものです。
具体的に何をするか
Torch7のモデル'model.t7'を,コンバータを使ってPyTorchで読み込める型nn.Sequential
に変換します。そして,PyTorchでパラメータを読み込み,ニューラルネットワークの入出力をテストします。
扱うモデルについて
今回私が使おうとしているのは,こちらのモデルです。このモデルはCNNで,画像を入力し,特徴量を128次元ベクトルとして出力します。モデルのパラメータは'stylenet.t7'に保存されています。
環境
- OS: Ubuntu 16.04
- Python: Python3.6
- torch:0.3.0
- torchvision:0.2.0
Torch7のモデルをPyTorchで読み込む
この方法には知っている限りで2つあって,
の2種類があります。前者の方法ですとモデルがtorch.legacy.nn.Sequential.Sequential
として読み込まれ,Variable
やautograd
といった便利なパッケージが利用できません。PyTorch discussionにも,その旨書いてあります。
PyTorchのパラダイムでニューラルネットワークするために,後者の方法を選びました。
コンバータ:convert_torch_to_pytorch
それではconvert_torch_to_pytorchを使ってコンバートしていきます。
リポジトリをクローンします。
$ git clone https://github.com/clcarwin/convert_torch_to_pytorch
おなじディレクトリにコンバータと,もとのtorchモデル(ここでは'stylenet.t7')があります。
$ ls
stylenet.t7 convert_torch.py
とりあえず動かしてみます
input
$ python convert_torch.py -m stylenet.t7
output
Traceback (most recent call last):
File "convert_torch.py", line 317, in <module>
torch_to_pytorch(args.model,args.output)
File "convert_torch.py", line 264, in torch_to_pytorch
model.gradInput = None
AttributeError: 'NoneType' object has no attribute 'gradInput'
よくわからないエラーが出ました。このエラーが出たという人は**これ**で治ると思います。
上手くいけば、stylenet.py
とstylenet.pth
というファイルが生成されます。
stylenet.py
は次のような感じになっています:
import torch
import torch.nn as nn
import torch.legacy.nn as lnn
from functools import reduce
from torch.autograd import Variable
class LambdaBase(nn.Sequential):
def __init__(self, fn, *args):
super(LambdaBase, self).__init__(*args)
self.lambda_func = fn
def forward_prepare(self, input):
output = []
for module in self._modules.values():
output.append(module(input))
return output if output else input
class Lambda(LambdaBase):
def forward(self, input):
return self.lambda_func(self.forward_prepare(input))
class LambdaMap(LambdaBase):
def forward(self, input):
return list(map(self.lambda_func,self.forward_prepare(input)))
class LambdaReduce(LambdaBase):
def forward(self, input):
return reduce(self.lambda_func,self.forward_prepare(input))
stylenet = nn.Sequential( # Sequential,
nn.Conv2d(3,64,(3, 3),(1, 1),(1, 1)),
nn.ReLU(),
nn.Conv2d(64,64,(3, 3),(1, 1),(1, 1)),
nn.ReLU(),
nn.Dropout(0.25),
nn.MaxPool2d((4, 4),(4, 4)),
nn.BatchNorm2d(64,0.001,0.9,True),
nn.Conv2d(64,128,(3, 3),(1, 1),(1, 1)),
nn.ReLU(),
nn.Conv2d(128,128,(3, 3),(1, 1),(1, 1)),
nn.ReLU(),
nn.Dropout(0.25),
nn.MaxPool2d((4, 4),(4, 4)),
nn.BatchNorm2d(128,0.001,0.9,True),
nn.Conv2d(128,256,(3, 3),(1, 1),(1, 1)),
nn.ReLU(),
nn.Conv2d(256,256,(3, 3),(1, 1),(1, 1)),
nn.ReLU(),
nn.Dropout(0.25),
nn.MaxPool2d((4, 4),(4, 4)),
nn.BatchNorm2d(256,0.001,0.9,True),
nn.Conv2d(256,128,(1, 1)),
nn.ReLU(),
Lambda(lambda x: x.view(x.size(0),-1)), # Reshape,
nn.Sequential(Lambda(lambda x: x.view(1,-1) if 1==len(x.size()) else x ),nn.Linear(3072,128)), # Linear,
)
ここで,変数stylenet
を出力(print)してみると:
input
print(stylenet)
output
Sequential(
(0): Conv2d (3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU()
(2): Conv2d (64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(3): ReLU()
(4): Dropout(p=0.25)
(5): MaxPool2d(kernel_size=(4, 4), stride=(4, 4), dilation=(1, 1))
(6): BatchNorm2d(64, eps=0.001, momentum=0.9, affine=True)
(7): Conv2d (64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(8): ReLU()
(9): Conv2d (128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(10): ReLU()
(11): Dropout(p=0.25)
(12): MaxPool2d(kernel_size=(4, 4), stride=(4, 4), dilation=(1, 1))
(13): BatchNorm2d(128, eps=0.001, momentum=0.9, affine=True)
(14): Conv2d (128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(15): ReLU()
(16): Conv2d (256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(17): ReLU()
(18): Dropout(p=0.25)
(19): MaxPool2d(kernel_size=(4, 4), stride=(4, 4), dilation=(1, 1))
(20): BatchNorm2d(256, eps=0.001, momentum=0.9, affine=True)
(21): Conv2d (256, 128, kernel_size=(1, 1), stride=(1, 1))
(22): ReLU()
(23): Lambda(
)
(24): Sequential(
(0): Lambda(
)
(1): Linear(in_features=3072, out_features=128)
)
)
パラメータのロード
さて,コンバータによって生成されたコードにはstylenet
をnn.Sequential
として宣言していますが,この中身にはパラメータが入っていません。次に,このモデルにパラメータを読み込んでいきます。
stylenet.load_state_dict(torch.load('stylenet.pth'))
適当なテンソルをforward()
してみる
これでパラメータがロードできているはずなので,適当なテンソルを流し込んでみます。テンソルのサイズはsample_size
×channel
×width
×height
です。
とりあえず,一つのサンプルを生成します:
x = Variable(torch.randn(1,3,256,384))
流し込みます:
input
stylenet.train(False) # Dropoutの効果をなくす
output = stylenet.forward(x)
print(output)
output
Variable containing:
Columns 0 to 9
0.6072 -1.0018 -0.4702 -0.4552 -0.4962 1.2272 -0.6443 -0.1887 0.6009 -0.7570
Columns 10 to 19
1.3234 0.2133 0.6178 0.8532 -1.2871 -0.4161 -4.3493 -0.1983 -0.7142 -1.2298
Columns 20 to 29
1.1643 -0.5848 0.0840 -0.2319 1.1919 1.4954 0.0088 -0.4057 0.7903 -1.0468
Columns 30 to 39
1.0912 -0.8682 -0.3281 -0.1538 -0.7733 0.1199 -0.0626 -0.0468 -0.5074 0.7080
Columns 40 to 49
-0.5085 -0.6603 1.6927 -0.5328 0.6732 1.1758 1.3706 0.0144 1.1350 -0.3365
Columns 50 to 59
-0.5287 1.3089 0.7659 -1.3911 0.2314 -0.9387 -0.2180 -0.4172 0.5906 -1.3044
Columns 60 to 69
0.2141 -0.1064 0.8990 1.7209 -0.0275 1.0274 -0.3997 1.1963 -0.5017 -0.3467
Columns 70 to 79
1.2800 0.3330 0.6327 1.1333 0.2330 -0.3172 0.3001 0.8193 -0.6384 -0.4202
Columns 80 to 89
-0.0884 0.7873 -0.2461 0.2694 0.2428 0.8148 -0.4201 -0.7954 -0.7733 0.2765
Columns 90 to 99
1.2593 0.7386 1.5303 -0.8679 0.1041 -0.3388 0.2556 -0.7885 0.5229 -1.2931
Columns 100 to 109
0.9629 0.3085 -0.1893 0.1238 -0.4162 0.4876 -0.2001 0.3029 0.3221 0.4819
Columns 110 to 119
0.2393 -0.8898 -0.2264 0.4281 -1.3006 0.5075 -0.7340 -0.6656 0.1713 -0.6931
Columns 120 to 127
0.6223 -0.8066 0.1698 -0.4471 0.5786 0.9276 -0.1602 -0.2247
[torch.FloatTensor of size 1x128]
うまくいきました。
次回は,実際の画像から特徴量抽出を行ってみます。