【初心者向け】機械学習の雑なまとめ

# 対象は初心者の方
`Stock Magnitude`という急騰株を予想するAIを作りました。
（気になる人は検索してみてください。iPhone、Android）

初めて機械学習を行ってから何度も試行錯誤を行う上で感じたことをまとめてみます。
あくまでも`自分の場合`なので必ずしもそうではないと思う。

主に`機械学習を始めようとしている人`か、`始めたばかりでどうすればよいかわからない人`向け。
いろんなことを雑にまとめています。

# まさか`mac`ではGPUを使用して機械学習できないなんて…
最近のmac（もう10年規模で）では`Nvidia`のGPUを採用していないため、GPUによる機械学習が行えません。
外付けGPUを使用すれば使えるのですが、`Nvidia`はmacでのサポートを終了したので現実的ではないでしょう。
CPUとGPUで機械学習したときの計算速度の差ですが大体40倍と思ってください。
（もちろんCPUとGPUの性能によって全く変わりますが、目安として）
機械学習には正解がなく、パラメータを変えて何度も試行錯誤をすることが必要です。
ですがその１つのパターンを試すのにCPUだと4日のフルパワー演算が必要だったりします。
もし、GPUが使用できればほんの3時間程度で終わるでしょう。

機械学習を行う場合GPUは必須級です。
ですが、CPUでは絶対に無理というほどでもありません。
最初の一歩はCPUでもいいと思います。

というのも、例えばJavaで機械学習を行おうとしている人で高速なプログラムをかけない人は
GPUを使用するよりCPUで機械学習をしたほうが圧倒的に早いパターンさえ起こりえます。

とはいえ、それはその人の機械学習以外の基本的な力量不足であり、普通はGPU必須。

# 過学習とは
機械学習でとてもよく聞くワードであり、とても大事な問題です。
また、`機械学習をする上では必ずこの問題にぶち当たる`ため、他人事に考えずきちんと意識する必要があります。
過学習とは何が起こっている状態なのか認識することはとても簡単です。
株価の予想で例えると、
よく学習できているニューラルネットワークは`「○日前からの株価が○%以内の値動きで○日間継続した後、○のロウソク足が出現したら株価が上がる！　でも○日間継続しなくても○%だけ一旦下げてそこから○日以内に急激に○%だけ上昇したら同じだけ株価が上がる！」`というような思考をしてほしいものです。
これが過学習をしてしまった状態になると`「初日の株価が120円なら今後株価が上がるよ＾＾　それか、今から4日前の株価から4.523%上昇していたら今後株価が上がるよ＾＾」`というような思考をしてしまいます。
どう考えても後者の方では汎用的な予想は行えないですよね？
`このように無理矢理な思考をしても学習データによっては100%近い精度で正しい判定をできてしまう`わけです。
もちろん、`実用した時は全く役に立たないAI`が出来上がっています。
このように`過学習をしてしまうと全く使い物にならないレベルまで精度が落ちてしまう`可能性があります。
「学習データが少なくてもったいないから検証データは学習させたデータの中からセレクトすればいいや。」
というのは論外です。
きちんと`学習データ`と`学習に使用しない検証データ`を分けて過学習していないか検証しながら学習を行う必要があります。

# 学習の早期停止
学習を長く行えば行うほど精度が向上すると思っているかもしれません。
ですが、`殆どの場合で学習を長く行うと過学習が発生し、精度が下がってしまいます。`
最初は良い精度で学習できていても学習を続行することで過学習が起こり精度が悪くなります。
これを回避するためには`学習の早期停止を行うことが大事`です。
`学習中に一定間隔でニューラルネットワークを評価`し、`最高精度を更新したときのニューラルネットワークを保存`しておくべきです。
それを繰り返しているうちに学習が進まなくなったり（6時間最高精度を更新しないとか）過学習が始まったりしたときは学習を終了し、
事前に保存していた最高精度のニューラルネットワークを使用することがベストな方法です。

# ニューロン数はどのくらいから試せばいいか
これは学習するものやデータ数など色々な要素によって決まりますが、
全くの初心者で経験がない場合`256`から始めるといいと思います。
多くすれば良いと思っているかもしれませんが、この数を極端に少なくしたほうが精度が向上する状況もありました。

# 層の数はどのくらいから試せばいいか
ニューロン数と同じく、学習するものやデータ数など色々な要素によって決まりますが、
全くの初心者で経験がない場合は入力層と出力層をあわせて`4層`ぐらいから始めるといいと思います。
つまり、`中間層は2層`です。
`層が多くなるほどニューラルネットワークは複雑に絡み合った高度な問題を解けるように`なります。
ではなぜこの層の数をとても多くしないのでしょうか？
それは、`層が多くなるにつれて学習が困難になる`からです。
10層程度なら学習できる場合もありますが、実は`中間層が2層程度でも学習できてしまうような状況が殆ど`かもしれません。
特に、LSTMの場合それ自体が高性能なのでなおさら2層で試してみるのが良いと思います。
`層を多くしすぎるとうまく学習できず、逆に精度が落ちてしまう`ため層はあまり多くしないほうが良いです。
層の数はできるだけ多くではなく、`思考できる範囲で少なくすべき`と思います。
（思考でき、精度が得られる最小の層数を目指す）

# L2正規化とドロップアウトはどっちを使うべき？
これも状況によるのでしょう。
`私の場合は圧倒的にドロップアウトを使用したほうが精度が良かった`です。
ドロップアウト率は`0.5`～`0.2`の範囲で決めるといいかもしれません。
最初は入力層と出力層を除いた層に適用を試すといいかもしれないです。
入力層と出力層のドロップアウトは一般的ではないとも聞きます。
私が試したところ、入力層に対するドロップアウトはとても精度が落ち、出力層に対するドロップアウトでは`0.2`程度なら精度が向上した例もありました。
機械学習ライブラリによってはこの数値の指定方法が間逆な場合もあるので注意してください。
例えば、DeepLearning4Jではドロップアウトされる確率ではなく、ドロップアウトされない確率を指定します。

# 活性化関数は`ReLU`が流行りだから何にでも使おう！は間違い
今では活性化関数として`ReLU`を使用するのが当たり前なほど良いとされています。
ですが、必ずしもそうではありません。
ではなぜ、`ReLU`を使用したほうがいいと言われるのでしょうか。
それは、機械学習を行うほとんどの人が深層学習で画像認識系の学習を行っているからです。
画像認識以外でも役に立ちますが、私は
`深層学習になるほど層が多い`か`画像系`もしくは`畳み込み層`以外ではあまり使用しないようにしています。

# LSTMの活性化関数は冒険しなくて良い。
LSTMの活性化関数は`TANH`で良いです。
`DL4J`の`cuDNN`ではLSTMの活性化関数は`TANH`しかサポートされていません。
その件やいくつかの書籍を読んだ結果`LSTM`の活性化関数は悩むことなく`TANH`でよい。と私は判断しました。

# 出力層の損失関数と活性化関数は冒険するべきものではない。
出力層の損失関数と活性化関数はニューラルネットワークで何を出力したいか（ニューラルネットワークに質問した結果、どう答えてほしいか）により設定します。
いろんな組み合わせを試すようなものではありません。
ニューラルネットワークで何を出力したいかとは
- 2パターンの中から選択（猫と犬どちらに近いか）
- 複数の中から1つ選択（猫、犬、鳥、ハムスター、蛇、亀...のどれに近いか一つだけ判定）
- 複数の分類（猫、犬、鳥、ハムスター、蛇、亀　何が映っているか）
- 連続値（株価は何円まで上がるか）
というような感じ。
それぞれ`何をしたいかによって組み合わせはある程度決まっています。`

# アップデータに迷っている暇があればもっとやるべきことがある
とりあえず最初は黙って`Adam`を使用すべき。
基本的にうまくいきます。
他にもそれぞれのニューラルネットワークにあった良いものがあるかもしれません。
でも`ここを試す前にもっとやるべきことがある`と思います。
重要な設定項目でありますが、いざ変更しても精度がほぼ変わらなかったりするものです。
ここを迷って試行錯誤するのは最後にしたほうが良いと思います。
ここを完璧にする前に、AIに与えるデータを変えたり、正規化、学習データの数を増やしたり、ニューラルネットワークの構造を変えたり
もっとやるべきことは山程あります。

# ライブラリに用意された正規化と自前の正規化どちらが良いか
AIにわたすデータは正規化しなければうまく学習できません。
とくに、株データの場合1日の正常値幅が5円だったり、1000円だったり銘柄によって幅が大きいのでなおさら正規化が必要です。

機械学習ライブラリには`正規化を手伝ってくれるヘルパー関数が用意されているかも`しれないですが、
それらを使用せず、`独自の理論で正規化したほうが圧倒的に学習がよく進みました。`

独自理論の正規化とは、、、
AIに与えるデータをセレクトしたのはあなた自身なのでどういった特性のデータか理解していると思います。
`AIの思考に役たちそうな情報を削らず`、なおかつ`AIが認識しやすいように正規化`することが大事です。

例えば、
その日の株価を過去20日の平均で割れば数値のスケールが小さくなっただけで直接株価を渡すのと同じようにAIに認識されるでしょう。
その日の株価を前日の株価で割れば、株価がどのように推移したかの情報が消失しますが、（消失したものもAIが与えられたデータをつなぎ合わせて生成するかもしれないので消失自体が問題ないときもある）ロウソク足の形を認識しやすくなるでしょう。

# 乱数のシードを変えて精度向上するなんて…
する場合もあります。
ニューラルネットワークを構築する時、重荷などの初期値を決める乱数のシードを指定できると思います。
学習がうまく行かない場合、このシードを変えれば違う初期値を持ったニューラルネットワークから学習を再スタートできます。
しょせん乱数の種…と思っていたのですが…今まで何度やっても超えられなかった精度を超えられたりしたんですよね。
基本的に最終手段でしょうが「どうしてもこのネットワークでもうちょっと精度がほしい！」というようなときは他のシードを試してみるといいかもしれないです。
たかが乱数が変わるだけでも無数に散らばったニューロンの初期の結びつきが変わるだけで何かが変わるかもしれないです。
いずれにしても最後の望みです。ですが、結構最高精度更新できたりします。