超簡単なautokerasの使い方

Autokerasは自動でニューラルネットワークの最適化を行うフレームワークです。今回はAutokerasの使い方をまとめました。超簡単なので流行るはずです。

# ニューラルネットワークの最適化
Neural Architecture Searchという、ネットワークそのものも学習する手法です。学習するというのは最適化するということです。機械学習における「学習」とは定義した損失関数を最小化することで、ネットワークの学習では、どんな層を組み合わせれば損失関数を少なくできるネットワークを構築できるかという問題を解いていることになります。ニューラルネットワークの層は、例えばConv層やPool層などの複数の選択肢から選択する問題で一般的には組み合わせ最適化と呼ばれる計算量が巨大になりやすい問題で、効率的に計算するためにベイズ最適化や強化学習を用いることが多いです。
Neural Architecture Searchは考え方的にはハイパーパラメータの最適化をニューラルネットワークに拡張したような感じです。とりあえずそれだけ覚えておけば充分だと思います。

# インストール
https://github.com/jhfjhfj1/autokeras
これをインストールします。以下を参考にしました。
https://qiita.com/hideki/items/24f6c776828ac918a109

# 使い方
ソースコード全体はここに置きました。
https://github.com/shiibashi/sample/blob/master/autokeras.ipynb
<font color="Red">このコードではテーブルデータを生成して無理やりautokerasを使っていますが、あまり良い使い方ではありません。ネットワーク学習で探索する層は画像向きのもので、一般のテーブルデータではうまくいかないことが多いはずです。</font>
バージョンは以下
Python 3.6.7 :: Anaconda, Inc.
autokeras==0.3.5
keras==2.2.2
torch==0.4.1
torchvision==0.2.1
tensorflow==1.10.0

## データ作成

特徴量ベクトルをx,yとし、ターゲットカラムをzとします。

```python
n = 1000
df = pandas.DataFrame(
    {
        "x": numpy.random.rand(n),
        "y": numpy.random.rand(n)
    }
)
df["z"] = df["x"].apply(lambda x: 1 if x >= 0.5 else 0)
df_train = df[0:800]
df_test = df[800:]

x_train = numpy.array(df_train[["x", "y"]])
y_train = numpy.array(df_train["z"])

x_test = numpy.array(df_test[["x", "y"]])
y_test = numpy.array(df_test["z"])

```

## 学習

画像分類用のモデルを定義します。

```python
model = autokeras.ImageClassifier()
```

ネットワーク構築の学習をします。time_limitは学習にかける時間を定義しています。

```python
model.fit(x_train.reshape(800, 2, 1), y_train, time_limit=0.05*60*60)

```

一番良かったニューラルネットワークで再学習します。retrain引数は重みを初期化するならTrue, そうでないならFalseを指定します。重みは初期化しないほうがたぶん収束が早いです。

```python

model.final_fit(
    x_train.reshape(800, 2, 1),
    y_train,
    x_test.reshape(200, 2, 1),
    y_test,
    retrain=False)
```

## 予測
predictで予測します。xが0.5以上のときzが1になるように設定していたことが学習できているっぽいですね。

```python
model.predict(numpy.array([0.6, 0.1]).reshape(1,2,1)) # array([1], dtype=int64)
model.predict(numpy.array([0.4, 0.1]).reshape(1,2,1)) # array([0], dtype=int64)
```

## その他使えそうなコード
### 学習性能の推移
下のコードでは2つのネットワークが生成され、一つ目のネットワークではlossが0.059という具合です。

```python
print(model.cnn.searcher.history)
↓出力
[{'model_id': 0,
  'loss': 0.05906128436326981,
  'metric_value': 0.9727272727272727},
 {'model_id': 1,
  'loss': 0.07171969339251519,
  'metric_value': 0.9575757575757576}]
```

### ネットワークの確認
出来上がったニューラルネットワークを確認します。いくつか確認する方法があります。

produce_modelはTorchModelというクラスのインスタンスを返します。TorchModelはautokerasのソースコードで定義されたクラスで、PyTorch内部のクラスではありません。

```python
print(model.cnn.best_model.produce_model())
↓出力
TorchModel(
  (0): ReLU()
  (1): BatchNorm1d(1, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (2): Conv1d(1, 64, kernel_size=(3,), stride=(1,), padding=(1,))
  (3): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (4): ReLU()
  (5): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (6): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,))
  (7): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (8): ReLU()
  (9): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (10): Conv1d(64, 64, kernel_size=(3,), stride=(1,), padding=(1,))
  (11): MaxPool1d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (12): GlobalAvgPool1d()
  (13): Dropout(p=0.25)
  (14): Linear(in_features=64, out_features=64, bias=True)
  (15): ReLU()
  (16): Linear(in_features=64, out_features=2, bias=True)
)
```

別の確認方法。nn.layers.Stub*はそれぞれがニューラルネットワークの1層に対応していて、kerasかpytorchで使える層に裏側で変換する処理が入っています。

```pyhton
model.cnn.best_model.layer_list
↓出力
[<autokeras.nn.layers.StubReLU at 0x1f1a0fa6518>,
 <autokeras.nn.layers.StubBatchNormalization1d at 0x1f1a0fa6128>,
 <autokeras.nn.layers.StubConv1d at 0x1f1a0fa6320>,
 <autokeras.nn.layers.StubPooling1d at 0x1f1a0d9ea20>,
 <autokeras.nn.layers.StubReLU at 0x1f1a0d9e940>,
 <autokeras.nn.layers.StubBatchNormalization1d at 0x1f1a0d9e6a0>,
 <autokeras.nn.layers.StubConv1d at 0x1f1a0d9ecc0>,
 <autokeras.nn.layers.StubPooling1d at 0x1f1a0d9e588>,
 <autokeras.nn.layers.StubReLU at 0x1f1a0d9e630>,
 <autokeras.nn.layers.StubBatchNormalization1d at 0x1f1a0d9e160>,
 <autokeras.nn.layers.StubConv1d at 0x1f1a0d9e0b8>,
 <autokeras.nn.layers.StubPooling1d at 0x1f1a0d9ec50>,
 <autokeras.nn.layers.StubGlobalPooling1d at 0x1f1a0d9e710>,
 <autokeras.nn.layers.StubDropout1d at 0x1f1a0d9e6d8>,
 <autokeras.nn.layers.StubDense at 0x1f1a0eafef0>,
 <autokeras.nn.layers.StubReLU at 0x1f1a0eafa90>,
 <autokeras.nn.layers.StubDense at 0x1f1a0eafb38>]
```
ここで気をつけたいのが上記2つとも上から順番に層が作られているとは限らないことです。内部では.graph.topological_orderと.graph.reverse_adj_listの変数にこれらの層の結合手順が記されています。

### セーブ
学習したモデルを出力します。

```python
path = "model.pkl"
model.export_autokeras_model(path)
```

### ロード
保存したモデルを読み込みます。読み込んだモデルはmodel変数そのものではなく、PortableClassというpredictとevaluateくらいしか搭載されておらず、推論専用のモデルになります。

```python
output_model = autokeras.utils.pickle_from_file(path)
output_model.predict(numpy.array([[0.45, 0.1], [0.55, 0.8], [0.49, 0.1]]).reshape(3,2,1))
↓predict結果
array([0, 1, 0], dtype=int64)
```

# 最後に
autokerasの使い方をまとめました。簡単だったことが伝わったと思います。今までディープラーニングにおいて適切なネットワークを作るのは非常に難しかったですが、autokerasで解決しやすくなったはずです。公式https://autokeras.com/
にはプレリリース版と書かれていて、バージョンが変わると大幅に使い方が変わる可能性があります。いずれにしろ今後のアップデートが楽しみなフレームワークです。