Python
DeepLearning
PyTorch

(Part1)PyTorchに学習済みのTorch7モデル(.t7)を読み込んで,画像特徴量を抽出する

本記事について

本記事は,深層学習ライブラリ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つあって,

  • torch.util.load_lua()を使う方法(詳細)
  • Torch7のモデルをPyTorchで使えるようにしてくれるコンバータ(GitHub)を使う方法

の2種類があります。前者の方法ですとモデルがtorch.legacy.nn.Sequential.Sequentialとして読み込まれ,Variableautogradといった便利なパッケージが利用できません。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.pystylenet.pthというファイルが生成されます。
stylenet.pyは次のような感じになっています:

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)
  )
)

パラメータのロード

さて,コンバータによって生成されたコードにはstylenetnn.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]

うまくいきました。

次回は,実際の画像から特徴量抽出を行ってみます。

次の記事