LoginSignup
8
5

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-01-26

本記事について

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

うまくいきました。

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

次の記事

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5