「メディカルAI専門コース オンライン講義資料」を理解

# 2. 機械学習ライブラリの基礎
## 2.4. Scikit-learnによる機械学習アルゴリズムの実行
### 2.4.2. Scikit-learn 応用編
#### 2.4.2.2. データセットの分割
>つぎに，この学習データを 訓練データ と テストデータ に分割する方法をご紹介します．もし，学習の時に使ったデータを使ってモデルの性能を評価した場合，学習データの性能が高くても学習中に見たことが無い（同じような分布からとられた）未知のデータはうまくいかない場合があります．これを 過学習 とよびます．機械学習ではこれを防ぐために学習データと別に性能を評価するテストデータを分けて評価します．このように分割して検証することを ホールドアウト法 とよびます．

→データセットを分割する理由は「汎化能力の確認」のため。
 なお最も利用されているテスト方法は交差検定（クロスバリデーション）[とのこと](http://aiweeklynews.com/archives/50219779.html)。


# 3. ニューラルネットワークの基礎
## 3.3. ニューラルネットワークの最適化
### 3.3.1. パラメータ更新量の算出

<img width="707" alt="スクリーンショット 2018-12-26 18.44.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/242023/91c48b28-9c80-8597-16b5-ff2cb403a91f.png">
パッと見でわからなかったので図で示して理解することに。
<img width="711" alt="スクリーンショット 2018-12-26 18.44.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/242023/8da93296-8edd-4211-75d6-483ed65f6da1.png">
図にするとこういうことですね。

# 4. Deep Learningフレームワークの基礎
## 4.2. Chainerの基本的な使い方
### 4.2.4. ネットワークの定義
#### 4.2.4.4. Chainを継承したネットワークの定義

```python

import chainer
import chainer.links as L
import chainer.functions as F

class MLP(chainer.Chain):

    def __init__(self, n_mid_units=100, n_out=10):
        super(MLP, self).__init__()

        # パラメータを持つ層の登録
        with self.init_scope():
            self.l1 = L.Linear(None, n_mid_units)
            self.l2 = L.Linear(n_mid_units, n_mid_units)
            self.l3 = L.Linear(n_mid_units, n_out)

    def forward(self, x):
        # データを受け取った際のforward計算を書く
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)

gpu_id = 0  # CPUを用いる場合は，この値を-1にしてください

net = MLP()

if gpu_id >= 0:
    net.to_gpu(gpu_id)
```
まじでわからないので順番に行きます。オイオイって思わないでください。
##### `class MLP(chainer.Chain):`
「MLP」という名称のクラスを定義している。
錯乱して色々わからなくなったので文章にすると
関数「chainer」を用いて、「Link層(`import chainer.links as L`で定義)をまとめておくためのクラス」。
>複数のLinkを組み合わせて構成されるニューラルネットワークでは、Chainクラスの配下にLinkを作ってまとめ上げることで、一つのChainオブジェクトとして管理することが出来ます。

参考ページを見ると「VariableとLinksで十分ニューラルネットワークは組めますが、長いコードを書く際に有効」とのこと。

参考:[【Chainer基礎3】Chainってなに？分かりやすく５分で解説](http://aiiine.com/chain/)

##### `def __init__(self, n_mid_units=100, n_out=10):`

もう基礎的すぎて講義資料には書いてないですね。

[【Python入門】クラスの使い方やオブジェクト指向の概念を理解しよう](https://www.sejuku.net/blog/28182) より引用。
>コンストラクタとは、「`__init__`」という名前の部分です。
コンストラクタは、インスタンスを作成する際に重要な処理を含むものです。
どのように生成するか、どのようなデータを持たせるかなど、といった情報を定義するのに必要なメソッドになります。

>引数にselfというものがあるのに注意してください。
selfとはインスタンス自身を指し、基本的にはこの引数は必須なので忘れないように気を付けてくださいね。
では実際にクラスを作成してみましょう。


```python

class MyClass():
    def __init__(self, message):
        self.value = message
 
myinstance = MyClass("Hello!")
print(myinstance.value)
```
>こちらのコードでは、コンストラクタの引数にselfとmessageを指定しました。

→実行結果「Hallo!」になると。MyClassで定義したコンストラクタに"Hallo!"を突っ込んで、`print(myinstance.value)`で「MyClassのvalue」をprintさせているわけですか。

[Pythonで複数のイニシャライザを定義する](https://qiita.com/_ha1f/items/532e67c3590ebc0988e8)より、

```python

class Pokemon(object):
    def __init__(self, id=25, name="ピカチュウ"):
        self._id = id
        self._name = name
    def __str__(self):
        return "Pokemon<{0._id}, {0._name}>".format(self)

pika = Pokemon()
print(pika)

mew = Pokemon(151, "ミュウ")
print(mew)
```
>実行結果
>Pokemon<25, ピカチュウ>
>Pokemon<151, ミュウ>

となると`def __init__(self, n_mid_units=100, n_out=10):`は「n_mid_units=100」と「n_out=10」という引数が入っていて、`MLP()`のとき`with`以下が「n_mid_units=100」と「n_out=10」として実行されるっぽい...
一旦ここは理解として次へ。

##### `super(MLP, self).__init__()`
`super()`はクラスの継承に用いられるもの。つまり
親クラス`class A()`を考えたとき、内包されるinitを`init(x)`とする
この`init(x)`のパラメータを用いて子クラス`class B()`を作りたいときに用いる関数。

ここでは「Chainクラスを継承した」MLPクラスを作っている。
(MLPの子クラスでMLPクラスのスーパークラスであるChainクラスを呼び出している。なおPython2では`super(MLP,self).__init__`と書かないとダメなようだが、Python3では単に`super().__init__`で良い様子。[こちら](https://ja.stackoverflow.com/questions/39769/%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%8F%82%E7%85%A7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6))

参考：[[Python]クラス継承(super)](https://qiita.com/Kodaira_/items/42dfe18c81af98bf0db3) / [【Python入門】クラスの継承についてやさしく解説](https://blog.codecamp.jp/python-class-2)

##### `with self.init_scope():`
>継承した MLP クラスのコンストラクタ内で with self.init_scope() が呼ばれており，その中でネットワークに登場するLink （具体的には，全結合層の L.Linear ）が定義されています．

[スーパークラスの参照について](https://ja.stackoverflow.com/questions/39769/%E3%82%B9%E3%83%BC%E3%83%91%E3%83%BC%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%8F%82%E7%85%A7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)より、

>`self.init_scope():`はPython の with 文で、as が省略された場合の書き方です。with 文は、処理が実行される前後に特定の処理を入れたい場合などに使われます。

そもそものwithの役割とは。
>with構文を使うと、close()の呼び出しが不要です。withブロックを抜けると、自動でclose()を呼び出してくれます。withブロック内で例外が起きた場合も同様です。ただし、withの行で例外が発生した場合は、close()は呼ばれません。

合ってるかすら自信ないですが「with構文でMLPクラスの挙動に`self.init_scope()`を差し込んでいる」っぽいことはわかりました。
`.init_scope()`が何してるのかわからないのでchainerのソースコードを見てしまった、これは深淵とばっちり目が合っている感覚...
一旦[ソースコード](https://github.com/chainer/chainer/blob/v5.1.0/chainer/link.py#L186)のテキストを翻訳。

>初期化スコープを作成します。
このメソッドは代入によるパラメータの登録（および：class： `〜chainer.Chain`へのリンク）を可能にするコンテキストマネージャオブジェクトを返します。
：class： `〜chainer.Parameter`オブジェクトは、このコンテキストマネージャの下の属性に割り当てることで自動的に登録されます。

...。
>Scopeとは、ある名前空間から(直接)アクセスすることができる名前空間の範囲のこと
直接アクセスするとはa.bのように.を使って対象を指定せず、bのようにアクセスする方法のこと

参考：[Python における名前空間と、Scopeについて](https://qiita.com/Liesegang/items/085f53a13ac140b84202)
これは...流石に...。

##### `self.l1 = L.Linear(None, n_mid_units)`
**`self.l2 = L.Linear(n_mid_units, n_mid_units)`**
**`self.l3 = L.Linear(n_mid_units, n_out)`**
これはself.l1が入力層、self.l2が中間層、self.l３が出力層ということか。
ベースで`n_mid_units`は100個で、`n_out`は10個で定義されているので、入力〜中間層のユニット数は100個、出力は10個でやりなさいねという理解。
MLP(n_mid_units,n_out)で指定できるわけですよね？（合ってる？）

参考：[PythonのChainerでのニューラルネットの書き方](http://enjoy-programing.com/python%E3%81%AEchainer%E3%81%A7%E3%81%AE%E3%83%8B%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%AB%E3%83%8D%E3%83%83%E3%83%88%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9/)

##### `def forward(self, x):`
ここで順伝播の関数を定義。

##### `h1 = F.relu(self.l1(x))`
**`h2 = F.relu(self.l2(h1))`**
**`return self.l3(h2)`**
.reluは活性化関数。h1,h2はそれぞれ繋がっているので逆伝播もできるようになる..とのこと。参考:[Chainerのソースを解析。順伝播と逆伝播の仕組み](http://ailaby.com/chainer_foward_backward/)

##### `gpu_id = 0`
**`net = MLP()`**
**`if gpu_id >= 0:`**
**`net.to_gpu(gpu_id)`**

`gpu_id=0`でGPUの使用を指定して、netにMLP関数を代入、GPUの使用が指定されている場合、netをGPU用のデータに変換する(`to_gpu()`)。とのこと。

参考：[chainerにてＧＰＵを使用する方法](https://groups.google.com/forum/#!topic/chainer-jp/FTJ0LSsuxQo)

読み終わった...。
with文で行われている処理が正直わかっていないが、
その他は「所謂Deeplearningの説明のあの部分をやっているっぽい」ということはわかった。
これらの定義があって、
>`MLP()`のとき`with`以下が「n_mid_units=100」と「n_out=10」として実行されるっぽい...
>`MLP(n_mid_units,n_out)`でノード数とアウトプット数を指定できるわけですよね？

こうなるということで合ってるのだろうか。