文系SE3年生、軽々に機械学習に挑んだ結果

　こんにちは、エンジニア3年生のSawamuraです。
　今回は文系大学を卒業してからこの3年間でCOBOL（さわり程度）、VB.net（コード読んでいくつかのバグを見つけられる程度）、SQLと触って基本情報技術者試験に合格できる程度の力を身に着けた人間が機械学習の知識を身に着けようとした結果を紹介します。

# その前に
　今回の学習に当たってAidemyさんの「AIアプリ開発講座」の6か月コースを利用させていただきました。
　[Aidemy Premium](https://aidemy.net/grit/premium/curriculums/?utm_source=aidemy&utm_medium=hp&utm_campaign=fv)

　ですが、開始早々体調を崩し、ようやくまともに取り組めるようになったのが終了1か月前（もったいない！）。
　本来このコースでは適宜わからない点をチューターの方に質問しながら進められるのですが、その利点をあまり活用できないままでした。
　ですからかなり短期間での促成栽培感は否めません。

# 目次
- 学習の動機
- 講座の感想
- 環境
- 成果物に悪戦苦闘
- 結果
- 考察
- 今後の展望

# 学習の動機
　そもそもなぜ機械学習を学ぼうとしたのか、結論から言えば囲碁と将来性です。
　しかし原点は高校生まで遡ります。
　自分は囲碁が趣味で、実力もDeepLearning登場前のAIになら大体勝てる程度でした。
　その頃の囲碁AIというと、プロ棋士に3子のハンデ（大体県大会出場者がプロ棋士にもらうレベル）をもらって互角といったところで、プロ棋士を追い越すにはまだまだかかるだろうといった雰囲気があったのを覚えています。
　ところがそんな折にalphaGoが登場し、当時世界最強と言われていたプロ棋士を4勝1敗と完勝してしまったのですから驚きました。
　その1敗も、対局相手の李プロがalphaGoの着手候補から剪定されていた手を打った（当時中国のトッププロであった古力プロはこれを神の一手と評しました）ことでの神懸かり的勝利です。
　私はこれに大きな衝撃を受けました。
　
　このAIとは何ぞやと、大学に入ってからはAI関連の書籍を読んでみることもありましたが、多くの数式の前に表層的な概念を知る程度にとどまりました。
　数学は苦手で、入試で大ポカをやらかしたのでどうにも苦手意識が強く…。

　さて大学を卒業し、SEなら食いはぐれないだろうとSEになった結果思ったことは。
　「もっと専門的なスキル身につけないと収入もできることも増えないな」
　ということでした。

　実質SESだったので給料はあまり高くなく、人間関係や納期も商流が絡んだりしてかなり気を遣うことが多く、学ぶ技術はその時々で会社が持ってくる案件に左右される状態。
　私がそう経験も多くないという理由もあったのだと思いますが、そんな現状に不満を抱えていました。
　
　なら自分でもっと専門的な知識を身に着けよう、できるなら将来性があるものを。
　ということで見つけたのがAI関連の技術です。
　また前々からAIに対しての好奇心がもやもやと残っていたこともあり、AIのスキルを身に着けようと思いました。

# 講座の感想
　前述したとおり速足で駆け抜けたので何とも言い難いところはあるのですが、入門としては良いものだと思いました。
　学び始めて気づく、機械学習というものの複雑さ。
　出てくる出てくる未知の単語。
　なんのあてもなく調べ始めたらキリがない迷路に入り込みかねません。
　その点この講座では入門として理解しておくべきポイントに絞って紹介されているので、初心者にとてもやさしいものとなっています。
　ですが根本にある数学的な要素や複雑で紹介されていない要素も多く、この講座だけで十分な実力が付くかと言われるとそうではないと思います。
　結局、この業界自走力がものを言いますね。

# 環境
 GoogleColaboratory
 git
 heroku

# 成果物に悪戦苦闘
　さて講座を一通り駆け抜けたところで最後に提出する成果物、期限はあと4日……。
　学生時代の夏休みを思い出しますね。
　成果物のテーマとして選んだのは「猫種の判別器」です。
　これは12種類の猫種を対象に、アップロードされた画像の猫がどの種類に該当するかを判定します。
　選んだ理由としては、私が猫好きだからです。猫の可愛さは種類に依存するものではありませんが、画像から種類が分かったら猫とよりお近づきになれるかもと思いました。
　使用したデータセットはオックスフォード大学が公開しているものです。
　[The Oxford-IIIT Pet Dataset]（https://www.robots.ox.ac.uk/~vgg/data/pets/）
　おそらく似たような判別器は多く作られているのでしょうが、そこは気にしません。
　というわけで、VGG16からの転移学習にいくつかの畳み込み層とプーリング層を付け加えて学習させればある程度いけるだろうという甘い見込みのもと始めました。
　結果、見事に躓きまくりました。
　・gzファイルの解凍をGoogle　Colaboratory上でしようとして「gzファイルではありません」とエラー
　　使用したオックスフォード大学の研究データはgzipファイルで提供されていたのですが、pythonのgzipライブラリで
　　解凍しようとするとなぜか「gzファイルではありません」とエラーが発生しました。
　　色々調べてみたのですが、どうにも糸口が見つからず、最終的に解凍したファイルをGoogleDriveに
　　アップロードしてGoogleColaboratoryにマウントして解決しました。

　・なぜかRGBの3チャンネルに分解できない画像がいくつか存在している
　　ダウンロードした画像をcv2.splitしようとするとエラーが発生しました。
　　このエラーについて調べると大体はパスの指定ミスやグレースケールの画像を指定することで
　　起きることが分かったのですが、該当の画像のパスは取得できているようであるし、グレースケールでもない
　　ので何故？と思いつつ、エラーが発生した画像を削除（5枚ほど）することで対応しました。

### そして精度が上がらない
　　これが一番難点でした。まず初めに造ったモデルの精度が検証用データに対して50％……。使い物にはなりません。
　　色々ハイパーパラメータをいじってみるものの、50％を超える精度が出ない。
　　どれをどういじればよいのか、経験と知識が圧倒的に足りない！
　　結果色々変えながら試すという泥臭い方法に。
　　小さいデータセット（全部で画像2000枚）なので処理時間は問題なかったのですが
　　もういっそ猫の種類など関係なく猫は可愛いということでいいのでは？などと考えながら……。
　　
### 試したこと
　　・全結合層を増やす
　　　全結合層を3個にしたりそれ以上にしてみたり。結果的に2個以降は逆に精度が下がっていました。

　　・標準化を行う
　　　BatchNormalizationを畳み込み層の前に実行。結果としてはこれで5％くらいの精度向上が得られました。
　　　箇所は1か所だけでそれ以上はあまり効果が無いようでした。
　　　
　　・Dropoutの割合を変更
　　　Dropout(0.3)などで特徴量を削っていました。意外だったのはDropoutの検証データへの正解率が数％単位と想像以上に
　　　大きかったことです。これはデータ数が少ないことも影響しているのかもしれません。
　　　
　　・学習回数を増やす
　　　20→40→80と変更してみたものの、大体のパラメーターでは40回、
　　　場合によっては80回で精度が上昇しなくなっていました。
　　　どうやら学習回数の影響ではなさそうです。
　　　ただ、特徴量を減らした最終的なモデルでは150回ほどの学習になりました。
　　　特徴量と学習回数の関係性に関しては考察の余地がありそうです。

　　・活性化関数の変更
　　　当初はreluを使用していたが、sigmoidに変更したところ数％単位で精度が向上しました。
　　　ただ、知識不足のため理由を分析できていないです。

　　・転移学習で重みを固定する層の数
　　　今回はVGG16からの転移学習を行ったのですが、重みを固定する層の数を9、12、15と試しました。
　　　15層ならVGG16での学習を活かして高精度にしてくれるだろうと思いきや、今回の場合9層や12層のほうが
　　　好成績でした。

　　・Kernel_sizeの変更
　　　元の画像を50×50のサイズに変更していたため、5×5や3×3など試しましたが3×3と小さめのほうが好成績でした。

　　　トレーニングデータに対しての精度は90％を超えているのに、検証用データに対しては50％台。　
　　　過学習か、特徴を正しく拾えていないかだと考え、特徴量を減らしてみたものの、変わらず50％台と変わることはなく……。

### 最終的なコード(モデル部分）
```model.py
#モデルの構築（転移学習）
vgg16 = VGG16(include_top=False,weights ='imagenet',input_tensor=Input(shape=(50,50,3)))

top_model= Sequential()
##top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Conv2D(input_shape=vgg16.output_shape[1:],
                     filters=256,kernel_size=(3,3),strides=(1,1),padding='same'))
top_model.add(MaxPooling2D(pool_size=(3,3),strides=(1,1),padding='same'))
top_model.add(Dense(1000))
top_model.add(Activation('sigmoid'))
top_model.add(Dropout(0.2))
top_model.add(Dense(500))
top_model.add(BatchNormalization())
top_model.add(Activation('sigmoid'))
top_model.add(Dropout(0.3))
top_model.add(Conv2D(filters=128,kernel_size=(3,3),padding='same'))
top_model.add(MaxPooling2D(pool_size=(2,2),strides=(1,1),padding='same'))
top_model.add(Flatten())
top_model.add(Dense(12,activation='softmax'))
model = Model(inputs =vgg16.input,outputs=top_model(vgg16.output))

for layer in model.layers[:11]:
  layer.trainable=False

model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.SGD(learning_rate=1e-4,momentum=0.9),
              metrics = ['accuracy'])

model.fit(X_train,y_train,validation_data=(X_test,y_test)
          ,epochs=150)
　　　
```

[成果物のリンク](https://anartifact.herokuapp.com/]
# 結果
　Test loss: 1.4520084857940674
　Test accuracy 0.5991649031639099
　
　上記のように、検証データに対する最終的な精度は約60％となりました。当初から10％向上させることができたものの、
現在の自分に思いつくモデルの構築、ハイパーパラメータの調整ではここが限界のようです。　

# 考察
　今回のモデルで精度が低かったことの大きな要因はデータ数にあると考えられます。各猫種に対し約200枚、全体で2400枚と
いうことで、当初はこれで十分だと考えていたのですが、カウンセラーの方にアドバイスをいただいたところ画像認識は
多くの場合データ量がものをいうとのことで、どうやら不十分だったようです。
　また各ハイパーパラメータの調整も手探りでやっており効率が悪く何が最適かわからない状態になっていたのは反省点です。
ハイパーパラメータのチューニングは経験がものをいうところだとは思うのですが、もう少し学んだことを活かして効率よく
行えたと思います。

# 今後の展望
　今回機械学習を学んだことで、兼ねてより抱いていたAIへの好奇心は少し満たされました。ですが本音を言えば、
入門したことでわからないことがもっと増えたと感じています。DeepLearningは機械が学習してくれるとはいえ、その土台を作るのは
人間であり、多岐にわたる手法の中から試行錯誤を重ねて選択していく必要があります。
　今後必要になるのはやはり機械学習の根幹をなす数学の論理、機械学習を応用しやすい統計学、そして何より多くのモデル構築に触れる
経験だと思います。講座が終わっても複雑怪奇なこの技術に対して十分な力を得たとは感じられないので、しばらくはKaggleでの武者修行に専念することで今回の経験を活かしていきます。