KAIBER NN Editor for PyTorch の概要

# はじめに
　KAIBER NN Editor for PyTorch は [DeepInsight](https://www.deepinsight.co.jp/) が提供している PyTorch 向けニューラルネットワーク作成ソフトウェアです。
　この記事では KAIBER NN Editor の概要をサンプルネットワークを作りながら説明します。
　（本記事ではKAIBER NN Editor for PyTorch バージョン0.1.0 を対象としていますが、より新しいバージョンでの記述になっている部分があります。バージョンアップ情報は末尾に追記します。）

# アクセス
　KAIBER NN Editor for PyTorch は Web アプリケーションです。Chromeブラウザで [KAIBER NN Editor for PyTorch](https://kaiber.biz/nne-py) アプリケーションにアクセスするとメイン画面が表示されます。

# 画面構成
　メイン画面は次のように表示されます。
![KaiberNNEditor_1_SS01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/29241cc9-03e8-b042-3d2a-cfedcf7f6265.jpeg)<!-- KaiberNNEditor_1_SS01.jpg -->
　上部にツールバーがあってボタンが並んでいます。
　左側に利用可能なノードが並ぶ”パレット”があります。
　中央は編集エリアでニューラルネットワークの作成を行います。
　右側はモデルやノードのプロパティ設定エリアです。

# 操作方法
　ニューラルネットワークを実際に作成しながら操作方法を説明します。例として手書き数字画像データMNISTを利用して手書き数字を認識するニューラルネットワークを作っていきます。

### モデル名設定
　プロパティエリアでモデルの名前を設定します。今回はDigitRecognizerとします。
![KaiberNNEditor_1_SS02r1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/219e798b-9a93-b160-4ca0-11adb49daed3.jpeg)<!-- KaiberNNEditor_1_SS02r1.jpg -->

### Input ノード追加
　ニューラルネットワークへの入力を表すInputノードをパレットからドラッグして編集画面に追加します。
![KaiberNNEditor_1_SS03r1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/598e57da-15d7-e38e-44b9-180166dda1d5.jpeg)<!-- KaiberNNEditor_1_SS03r1.jpg -->

### エラーチェック
　追加したノードの横に付いている”！”マークはノード単位のエラーを表しています。エラー内容はマウスカーソルを合わせると表示されます。今回は必須フィールドであるにもかかわらず値が設定されていないという意味のエラーが表示されます。
![KaiberNNEditor_1_SS04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/0f359b48-58a4-1121-9260-8ba59c1fd9e0.jpeg)<!-- KaiberNNEditor_1_SS04.jpg -->

### プロパティ設定
　プロパティの設定を行います。Input ノードが選択状態になっていないときはクリックしてください。プロパティエリアに Input ノードのプロパティが表示されます。確認すると"dims"というプロパティの左側に×印がついています。これはプロパティ単位のエラーを表していて、×印にマウスカーソルを乗せるとエラーメッセージが表示されます。
　Input ノードの dims プロパティには入力テンソルのシェイプを設定します。入力が画像である場合、入力は一般的に4次元となり [バッチサイズ, チャネル数, 縦のピクセル数, 横のピクセル数] のシェイプとなります。今回はMNISTのデータを使いますのでデータの仕様に合わせてチャネルサイズは1（グレースケール）、縦横のピクセル数はどちらも28ピクセルとなります。バッチサイズは1とします。以上から dims には [1, 1, 28, 28] と設定します。入力した値は Enter キーを押すか、テキストボックスからフォーカスが外れたタイミングで確定されます。
![KaiberNNEditor_1_SS05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/5a8991af-2419-5eb8-3e4c-cc5482a527cc.jpeg)<!-- KaiberNNEditor_1_SS05.jpg -->
　dims プロパティの左側のマークがチェックマークに変わり、ノード右のアイコンもチェックマークに変わります。これでエラーが解消されたことが分かります。

### リンク
　続いてこの入力データを畳み込み処理したいと思います。画像に対して畳み込み処理を行うため"Conv2d"を追加します。Conv2dを編集エリアにドラッグします。
![KaiberNNEditor_1_SS06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/4b80c43c-6fb2-ea54-794a-4a9ad67008b4.jpeg)<!-- KaiberNNEditor_1_SS06.jpg -->
　次にInputのOutポートからConv2dのInポートに向けてドラッグします。するとポート間に矢印が描かれます。これはデータが流れる方向を示す矢印で「リンク」と呼びます。
![KaiberNNEditor_1_SS07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/d1041b1e-c93c-e748-a614-a5fe2f8b4233.jpeg)<!-- KaiberNNEditor_1_SS07.jpg -->
　リンクが追加されると矢印に重なって数値"1,1,28,28"が表示されます。この数字はリンク上を流れるテンソルのシェイプです。ここでは Input の dims で設定した値がそのままシェイプとして出力されます。
![KaiberNNEditor_1_SS08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/08285074-88c1-31ba-a04a-77cf45323756.jpeg)<!-- KaiberNNEditor_1_SS08.jpg -->
続けてConv2dの設定を行っていきます。in_channelsは入力テンソルのチャネル数が自動入力されてここでは１となっています。out_channlesは今回は 8 に設定します。カーネルサイズは 5 にしましょう。paddingは 2 とします。これで入力データに対してカーネルサイズ 5、パディングサイズ 2 の畳み込み処理を行い8チャネルの出力が生成されます。
![KaiberNNEditor_1_SS09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/da9ce09f-4b3b-61b4-cbeb-7d228cd78980.jpeg)<!-- KaiberNNEditor_1_SS09.jpg -->

### ヘルプ
　Conv2d の out_channels、padding 等のプロパティはPyTorchに準拠しています。ノードの情報を詳しく知りたい時はノード名の横にある"?"をクリックします。すると次のような PyTorch のドキュメントページが表示されます。リンクしています。
![KaiberNNEditor_1_SS10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/48920b72-1876-6dac-90cc-4d001e145743.jpeg)<!-- KaiberNNEditor_1_SS10.jpg -->

### さらにノード追加
　Conv2dの出力に対して活性化関数"ReLU"を適用しましょう。ノードリストからReLUを追加しConv2dと接続します。ReLUには設定すべきプロパティはありませんので接続すればOKです。
![KaiberNNEditor_1_SS11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/30f7272a-9811-985d-2009-9b82c4c99b40.jpeg)<!-- KaiberNNEditor_1_SS11.jpg -->
　次にReLUの出力に対してマックスプーリングを適用します。MaxPool2dを追加して接続します。カーネルサイズはここでは2としましょう。
![KaiberNNEditor_1_SS12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/e8c063ae-54e2-e75f-ebfb-abd8189c8022.jpeg)<!-- KaiberNNEditor_1_SS12.jpg -->

### コピー＆ペースト
　さて、これまでに追加したConv2d、ReLU、MaxPool2dという一連の処理をもう一度繰り返して行いたいとしましょう。今までと同じようにパレットからノードを一つずつ追加することもできますが既に存在するノードをコピーすることもできます。
　まずConv2dをコピーしてみます。編集エリアのConv2dをクリックして選択状態にします。この状態でツールボタンのコピーをクリック、続いて貼り付けボタンをクリックします。こうするとコピー、ペーストが行われます。
![KaiberNNEditor_1_SS14r1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/6a96e6f3-547b-a725-310f-38e738c08310.jpeg)<!-- KaiberNNEditor_1_SS14.jpg -->
　コピー元とコピー先が重なって表示されているためマウスで移動します。
![KaiberNNEditor_1_SS15 r1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/3aa86e8a-b603-b1e4-e061-632244623ff3.jpeg)<!-- KaiberNNEditor_1_SS15.jpg -->
　MaxPool2dから新しいConv2dノードに対してリンクを接続します。次にノードプロパティを設定しますが、コピーを行った場合設定値もコピーされていますので必要に応じて修正を行います。ここでは out_channles を 16 とします。
![KaiberNNEditor_1_SS17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/787cf939-cb1e-a231-bfc9-89b1d2976ae5.jpeg)<!-- KaiberNNEditor_1_SS17.jpg -->

### 範囲選択
　続けて ReLU と MaxPool2d も追加していきましょう。マウス左ボタンを少し長めに押してからドラッグすると一度に複数のノードを選択することができます。
![KaiberNNEditor_1_SS18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/9a91f351-fc2d-9498-5e64-ee88cb8b7ef6.jpeg)<!-- KaiberNNEditor_1_SS18.jpg -->
　この状態でコピー、ペーストを行うと、選択したものがまとめて複製されます。
![KaiberNNEditor_1_SS19.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/dbae2ba7-3622-ca53-912d-ef4262d4141d.jpeg)<!-- KaiberNNEditor_1_SS19.jpg -->
　新しい ReLU にリンクを設定し MaxPool2d に対して必要な変更を行います。ここでは MaxPool2d のカーネルサイズを3にしておきます。
![KaiberNNEditor_1_SS20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/a9555e40-3613-7897-9237-6b1d062f6462.jpeg)<!-- KaiberNNEditor_1_SS20.jpg -->

### Undo & Redo
　途中で操作を誤った場合について考えてみましょう。例えばあるノードをコピーしようとして間違えてカットボタンを押してしまったとします。このカット操作により選択したノードおよびノードに接続されていたリンクは削除されてしまいます。
![KaiberNNEditor_1_SS21.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/bd5aad69-1239-703d-ebde-92fcb0170e9f.jpeg)<!-- KaiberNNEditor_1_SS21.jpg -->
　このような時はあわてずに Undo ボタンをクリックしましょう。操作を元に戻すことができます。Redo をクリックすることにより Undo した操作を再実行することも可能です。
![KaiberNNEditor_1_SS22r1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/4dee430e-9f52-34d1-0d15-8ece8bd6b8ca.jpeg)<!-- KaiberNNEditor_1_SS22r1.jpg -->

### さらにノード追加
　さてニューラルネットワークの作成を続けていきましょう。MaxPool2d の先に Flatten を追加し、さらにその先に Linear を追加します。手書き数字の判定は0～9の10通りの出力になるので出力クラス数は10となります。これに合わせてLinearノードの out_features プロパティを 10 に設定します。
![KaiberNNEditor_1_SS23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/b50f35a7-fa3e-c1f9-c166-5a5a3b2f7766.jpeg)<!-- KaiberNNEditor_1_SS23.jpg -->

### Output ノード追加
　最後にOutputノードを追加しLinearの出力をOutputノードに渡します。Outputノードに入力された値がニューラルネットワークの出力になります。
![KaiberNNEditor_1_SS24.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/3303cd32-b50e-00b1-fc0b-48785de13f08.jpeg)<!-- KaiberNNEditor_1_SS24.jpg -->
　以上でニューラルネットワークの設計が完了しました。

### Pythonコード生成（v0.4からインターフェースが変更されました。）
　Pythonコード生成ボタンを押すとコードが生成されソースコードプレビューが表示されます。
　ファイル名を指定して保存したりクリップボードへコピーすることができます。
![v0.4_code.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/da09c8bb-6ff1-8378-838c-850da92c09c5.jpeg)

# 出力結果
　こちらが出力されたソースコードです。これをPyTorchのモデルとして組み込むことができます。
```python:DigitRecognizer.py
import torch

class DigitRecognizer(torch.nn.Module):
    def __init__(self):
        super(DigitRecognizer, self).__init__()
        self.conv2d_3 = torch.nn.Conv2d(in_channels=1, out_channels=8, kernel_size=5, padding=2)
        self.relu_9 = torch.nn.ReLU()
        self.maxpool2d_4 = torch.nn.MaxPool2d(kernel_size=2)
        self.conv2d_8 = torch.nn.Conv2d(in_channels=8, out_channels=16, kernel_size=5, padding=2)
        self.relu_7 = torch.nn.ReLU()
        self.maxpool2d_6 = torch.nn.MaxPool2d(kernel_size=3)
        self.flatten_10 = torch.nn.Flatten()
        self.linear_5 = torch.nn.Linear(in_features=256, out_features=10)

    def forward(self, input):
        _x0 = self.conv2d_3(input)
        _x0 = self.relu_9(_x0)
        _x0 = self.maxpool2d_4(_x0)
        _x0 = self.conv2d_8(_x0)
        _x0 = self.relu_7(_x0)
        _x0 = self.maxpool2d_6(_x0)
        _x0 = self.flatten_10(_x0)
        output = self.linear_5(_x0)
        return output
```

# v0.1 -> v0.3 変更点
v0.3で追加された機能について説明します。

### 保存・読み込み
　モデルの保存と読み込みに対応しました。読み込みボタン、保存ボタンからアクセスできます。
![v0.3_load_save.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/b229ea18-9a50-728b-fd4e-2465065a5685.jpeg)

### ノード削除
　選択されたノードを削除します。「切り取り」と異なりクリップボードには保存されません。
![v0.3_delete.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/5922bd1d-6316-55c1-3a74-744ac21fe7eb.jpeg)


### オペレータ検索
　パレット内のオペレータに対して名前での検索が可能になりました（前方一致検索）。大文字・小文字は区別されます。
![v0.3_op_search.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/b8cc4dbb-2c8b-6a31-b6af-ef2104198a9b.jpeg)

### 自動レイアウト ON/OFF 切り替え
　自動レイアウトのON/OFFが切り替えられるようになりました。初期状態では自動レイアウト ON となっています。
　自動レイアウトが ON の場合 1 つ以上のリンクを持つノードは自動的にレイアウトされます。
　自動レイアウトが OFF の場合リンク数にかかわらずノードが自動的にレイアウトされることはありません。自動レイアウト OFF の状態でも左隣のレイアウトボタンをクリックすることにより任意のタイミングでレイアウトを実行することができます。
![v0.3_auto_layout.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/fc47bcb1-1c05-a71c-d32a-ae649e8d5e7c.jpeg)

### コンテキストメニュー
　右クリックによりコンテキストメニューが表示されるようになりました。その時点で実行できるコマンドが表示されます（実行可能なコマンドがない場合はコンテキストメニューは表示されません）。
![v0.3_context_menu.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/c48329f0-a3c6-1668-5832-f65020e3cc7c.jpeg)

# v0.3 -> v0.4 変更点
v0.4で追加された機能について解説します。

### 新規作成ボタン
　現在のモデルを破棄して新しいモデルを作成します。従来はブラウザのリロードが必要でした。
![v0.4_new.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/cde22841-439a-c843-965f-f7ce819cd632.jpeg)

### コメント機能
　モデル、ノードにコメントを追加できるようになりました。コメントはノードにカーソルを合わせるとツールチップとして表示される他、生成したPythonソースコードにも出力されます。
![v0.4_comment.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/e7348d9d-1756-5f34-99d7-4714b34d470b.jpeg)

### コードプレビュー
　Pythonコード生成時コードプレビューダイアログが表示されます。
![v0.4_preview.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/3775994e-d6b3-cfc6-9ce4-fe6511ba3392.jpeg)

### パネル幅が可変に
　モデルやノードの情報を表示する画面右側のパネルの幅が調節可能になりました。境界部分を左右にマウスドラッグして変更します。
![v0.4_panelsize.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/15a59e64-70f1-1068-a10f-acab0fd3cfa1.jpeg)

# v0.4 -> v0.5 変更点
v0.5で追加された機能について説明します。

### エクスポート
　モデルの一部を切り出して新たなモデルとして保存することができます。
![v0.5_export.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/65e7ea4c-57be-73f2-4615-44d48e4e0b5c.jpeg)

　ファイル名は自動で "モデル名_parts.ltm" となります。通常のモデルと同様に開くことも可能です。
![v0.5_parts.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/6fa4fee9-497d-7eeb-d658-b434a73e8ea1.jpeg)

### インポート
　編集中のモデルに対して別のモデルの内容を追加することができます。
![v0.5_import.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/3cf4ab24-7f9f-91b7-eb2e-dbbb3cec8e34.jpeg)

# v0.5 -> v0.6 変更点
v0.6で追加された機能について説明します。

### サブモジュール
　サブモジュールを使うと、モジュール内に別のモジュールを含めることができるようになります。これによりモジュールを階層状に構成することができるようになります。
　サブモジュールを追加するにはパレットから "[[ Submodule ]]" を編集エリアにドラッグ＆ドロップします。ノード表示がサブモジュールクラス名になります（下の例では"Module_2"）。この段階ではサブモジュールは中身が空の状態です。
　サブモジュールの内容を編集するにはサブモジュールノードが選択された状態で「下の階層へ」ボタンをクリックします（サブモジュールノードをダブルクリックでも可）。
![v0.6_submodule_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/8bacec70-b216-7ca7-6a17-8da1a1c4bf92.png)

　サブモジュール編集画面になります。画面下部に階層が表示されます。
　ここでのモジュールの名前がサブモジュールクラス名になります。必要であれば変更します。
![v0.6_submodule_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/b118d893-6960-4a54-8336-64323cbf2ff8.png)

　サブモジュールを作成します。この例では1入力1出力のシンプルなモジュールを作成しました。
　サブモジュール内に別のサブモジュールを追加することも可能です。
　編集が終了したら「上の階層へ」ボタンをクリックして最初のモジュールに戻ります（画面下部の階層表示から戻りたい階層をクリックして戻ることもできます）。
![v0.6_submodule_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/c3da8915-b143-99bd-d847-c52e8e6d6879.png)

　サブモジュールの"Input"と"Output"に対応するポートがそれぞれ追加されています。
![v0.6_submodule_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/b5e97d0b-564f-461b-71c4-7b9893b51dfc.png)

　サブモジュールの周辺を作成していきます。
![v0.6_submodule_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/4f7420fc-b448-9ef2-ad41-db785367fe7c.png)


コードに変換する際、メインモジュールとサブモジュールは別のクラスとして生成されます。
```python:Module_1.py
import torch


class Module_2(torch.nn.Module):
    def __init__(self):
        super(Module_2, self).__init__()
        self.conv2d_3 = torch.nn.Conv2d(in_channels=1, out_channels=8, kernel_size=5, padding=2)
        self.relu_9 = torch.nn.ReLU()

    def forward(self, input):
        _x0 = self.conv2d_3(input)
        output = self.relu_9(_x0)
        return output


class Module_1(torch.nn.Module):
    def __init__(self):
        super(Module_1, self).__init__()
        self.submodule_3 = Module_2()

    def forward(self, input_1):
        output_2 = self.submodule_3(input_1)
        return output_2
```

　最後にサブモジュールを使用したニューラルネットワークの例を示します。
![v0.6_submodule_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/7b3bcc76-1321-421f-fe3c-77c0111f7b03.png)

### リンクパネル
　リンクを選択するとリンクパネルが表示されるようになりました。
![v0.6_linkpanel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/5afa6ef9-b870-397b-119a-13f423a33827.png)

### インスタンス検索
　編集画面内のノードを名前で検索することができます（前方一致検索）。
![v0.6_instance_search.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/499fac5f-b9b1-52ab-f838-cc9fcb34530c.png)

### スクリーンショット
スクリーンショットボタンをクリックすると作成中のモジュールを画像として保存することができます。
![v0.6_screenshot_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/82788088-f1cd-ed4c-dd3a-f0d418bdc552.png)

以下はスクリーンショットの例です。
![GoogLeNet_screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2735569/c704057b-0297-b111-62f2-a57cfc112a13.png)