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

# 本記事について
本記事は，深層学習ライブラリPyTorchを用いてTorch7のモデル（しばしば`.t7`の拡張子で保存される）を読み込むまでの苦闘を記録したものです。


### 具体的に何をするか
Torch7のモデル'model.t7'を，[コンバータ](https://github.com/clcarwin/convert_torch_to_pytorch)を使ってPyTorchで読み込める型`nn.Sequential`に変換します。そして，PyTorchでパラメータを読み込み，ニューラルネットワークの入出力をテストします。

### 扱うモデルについて

今回私が使おうとしているのは，[こちら](https://github.com/bobbens/cvpr2016_stylenet)のモデルです。このモデルはCNNで，画像を入力し，特徴量を128次元ベクトルとして出力します。モデルのパラメータは'stylenet.t7'に保存されています。

### 環境

- OS: Ubuntu 16.04
- Python: Python3.6
- torch：0.3.0
- torchvision：0.2.0


# Torch7のモデルをPyTorchで読み込む
この方法には知っている限りで２つあって，

- `torch.util.load_lua()`を使う方法([詳細](https://discuss.pytorch.org/t/convert-import-torch-model-to-pytorch/37))
- Torch7のモデルをPyTorchで使えるようにしてくれるコンバータ([GitHub](https://github.com/clcarwin/convert_torch_to_pytorch))を使う方法
 
の2種類があります。前者の方法ですとモデルが`torch.legacy.nn.Sequential.Sequential`として読み込まれ，`Variable`や`autograd`といった便利なパッケージが利用できません。[PyTorch discussion](https://discuss.pytorch.org/t/convert-import-torch-model-to-pytorch/37)にも，その旨書いてあります。

PyTorchのパラダイムでニューラルネットワークするために，後者の方法を選びました。

## コンバータ：convert_torch_to_pytorch
それでは[convert_torch_to_pytorch](https://github.com/clcarwin/convert_torch_to_pytorch)を使ってコンバートしていきます。

リポジトリをクローンします。

```bash
$ git clone https://github.com/clcarwin/convert_torch_to_pytorch
```

おなじディレクトリにコンバータと，もとのtorchモデル（ここでは'stylenet.t7'）があります。

```bash
$ ls 
stylenet.t7    convert_torch.py
```

とりあえず動かしてみます
**input**

```bash
$ python convert_torch.py -m stylenet.t7
```

**output**

```bash
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'
```

よくわからないエラーが出ました。このエラーが出たという人は**[これ](https://github.com/clcarwin/convert_torch_to_pytorch/issues/24)**で治ると思います。

上手くいけば、`stylenet.py`と`stylenet.pth`というファイルが生成されます。
`stylenet.py`は次のような感じになっています：

```python: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**

```python
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`として宣言していますが，この中身にはパラメータが入っていません。次に，このモデルにパラメータを読み込んでいきます。

```python
stylenet.load_state_dict(torch.load('stylenet.pth'))
```

## 適当なテンソルを`forward()`してみる

これでパラメータがロードできているはずなので，適当なテンソルを流し込んでみます。テンソルのサイズは`sample_size`×`channel`×`width`×`height`
です。
とりあえず，一つのサンプルを生成します：

```python
x = Variable(torch.randn(1,3,256,384))
```

流し込みます：
**input**

```python
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]
```
うまくいきました。

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

[次の記事](https://qiita.com/kenmikamin/items/cef5afb1a1a4b1617a48)