Aidemyでの学習の振り返り

##はじめに
「Aidemy Premium Plan(AIアプリ開発コース ６ヶ月)」を受講したことについてまとめてみた。
受講期間：２０２０年１０月〜２０２１年４月

##本記事の目次
●自己紹介
●Aidemyを受講した理由
●Aidemyでの学習内容
●最終成果物の作成について振り返り
●受講後の感想

##自己紹介
・２0代後半（受講時）
・男性（既婚・子１）
・建設業界所属（東証一部上場）　※育児休業期間あり
・地方国立大学、大学院卒業
・大学、大学院時代にR[プログラミング言語]の使用経験あり（少し使える程度）
・入社後、業務効率化のために簡単なプログラムをVBAで作成し活用した
・G検定不合格（２０１９年１１月９日）

##Aidemyを受講した理由
・建設業界は効率が悪いことが多く、嫌気がさしていた。
・大学院時代からPythonに興味があり、業務に活かせるのではないかと思った。
・AIブームが来ていたので、興味が増した。
・専門実践教育訓練給付金を使うと受講料の負担が半分程度で済むことを知った。
・将来に不安があったので、出来ることは増やしておきたいと思っていた。
・講義費用を一括で支払える余力が少しあった。
・他のプログラミングスクールよりPythonに強い気がした。

##アイデミーでの学習内容
・環境構築
・Python入門
・Numpy
・Pandas
・Matplotlib
・データクレンジング
・機械学習概論
・教師あり学習（分類）
・スクレイピング
・ディープラーニング基礎
・CNN
・男女認識
・HTML/CSS
・Flask入門
・MNISTを用いた手書き文字認識アプリ作成
・アプリ制作
・コマンドライン入門
・Git入門
・デプロイ

##最終成果物の作成について振り返り
####最終作成物(建設機械判定機）
_https://kikai-judgment.herokuapp.com/_

####判定機作成から公開まで
<details><summary>**1.画像の収集**</summary><div>
判定したい画像を収集する。
自分で持っている写真があるなら、それを使っても良い。
今回は１００枚の画像を収集した。
ImageDataGeneratorを使った画像を増やすための処理等もあるが、今回は実施していない。
</div></details>
<details><summary>**2.モデル構築と保存**</summary><div>
　
<details><summary>作成したコード※説明付き</summary><div>

```python
#モジュールオブジェクトのインポート
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from keras.utils.np_utils import to_categorical
from keras.layers import Dense, Dropout, Flatten, Input
from keras.applications.vgg16 import VGG16
from keras.models import Model, Sequential
from keras import optimizers
from os import listdir

#イメージサイズの定義をする
image_size = 50

#listdir('./xx/')ファイルの中にある画像を入れる。
path_bh = [filename for filename in listdir('./bh/') if not filename.startswith('.')]
path_bu = [filename for filename in listdir('./bu/') if not filename.startswith('.')]
path_rollar = [filename for filename in listdir('./rollar/') if not filename.startswith('.')]



#各ファイルを順番に呼び出す。（ファイルにある個数分繰り返す。）
#OpenCVで画像サイズをリサイズして統一する。
#img_にリサイズしたものを加える。

img_bh = []
img_bu = []
img_rollar = []
for i in range(len(path_bh)):
   img = cv2.imread('./bh/'+ path_bh[i])
   img = cv2.resize(img,(image_size,image_size))
   img_bh.append(img)
for i in range(len(path_bu)):
   img = cv2.imread('./bu/'+ path_bu[i])
   img = cv2.resize(img,(image_size,image_size))
   img_bu.append(img)
for i in range(len(path_rollar)):
   img = cv2.imread('./rollar/'+ path_rollar[i])
   img = cv2.resize(img,(image_size,image_size))
   img_rollar.append(img)



X = np.array(img_bh + img_bu + img_rollar) #行列をくっつける。
y = np.array([0]*len(img_bh) + [1]*len(img_bu) + [2]*len(img_rollar)) #０、１、２と種類ごとに番号をつける。
rand_index = np.random.permutation(np.arange(len(X))) #np.arange(x)の配列をランダムに並べ替えてrand_indexに入れる。
X = X[rand_index] #Xをrand_index順に並び替える
y = y[rand_index] #yをrand_index順に並び替える

#データを分割する。
X_train = X[:int(len(X)*0.8)] #トレーニングデータ８割未満
y_train = y[:int(len(y)*0.8)]
X_test = X[int(len(X)*0.8):] #トレーニングデータ以外
y_test = y[int(len(y)*0.8):]

#各属性を表示する。
print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)

y_train = to_categorical(y_train) #One-Hotベクトルを作成する。
y_test = to_categorical(y_test) #One-Hotベクトルを作成する。

# 転移学習（VGG16モデル)を活用する。ImageNetで事前学習した重みを読み込む。
input_tensor = Input(shape=(image_size, image_size, 3)) 
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

#特徴抽出部分以降に新しく他の層を追加するにために、あらかじめVGGとは別のモデル（ここではtop_model）を定義する。
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:])) #VGGを結合する。

top_model.add(Dense(256, activation="sigmoid")) #1つ目の全結合層の出力ユニット数は256,活性化関数はshigmoid.
top_model.add(Dropout(0.5))                     #過学習を防ぐ。
top_model.add(Dense(64, activation='sigmoid'))
top_model.add(Dropout(0.5))
top_model.add(Dense(32, activation='sigmoid'))
top_model.add(Dropout(0.5))
top_model.add(Dense(3, activation='softmax')) #ソフトマックス関数を用いて3次元で出力する。

## vgg16とtop_modelを連結してください
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))

#15番目の層までは固定し、それ以降のものを繰り返し学習する。
for layer in model.layers[:15]:
   layer.trainable = False

#コンパイルする。
model.compile(loss='categorical_crossentropy', #多クラス分類の損失関数を定義する。
             optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), #最適化関数を定義する。
             metrics=['accuracy']) #評価関数を定義する。

#X_train, y_trainを用いて学習を行う。テストデータは、X_test, y_test。
history = model.fit(X_train, y_train, batch_size=32, epochs=75, verbose=1, validation_data=(X_test, y_test))

#精度の評価する。
score = model.evaluate(X_test, y_test, batch_size=32, verbose=0)
print('validation loss:{0[0]}\nvalidation accuracy:{0[1]}'.format(score))

#モデルを保存する。
model.save("my_model.h5")
```
</div></details>
　
<details><summary>参照したサイト</summary><div>
_https://qiita.com/HotAllure/items/0aa05256a33b63e7eb7e_
_https://www.headboost.jp/python-if-not/_
_https://qiita.com/kenfukaya/items/dfa548309c301c7087c4_
_https://www.javadrive.jp/python/list/index3.html_
_https://arakan-pgm-ai.hatenablog.com/entry/2018/11/07/090000_
_https://arakan-pgm-ai.hatenablog.com/entry/2018/11/07/090000_
_https://qiita.com/Ishotihadus/items/c2f864c0cde3d17b7efb_
_https://deepage.net/deep_learning/2016/10/17/deeplearning_dropout.html#dropoutとは何か_
_https://spjai.com/keras-fine-tuning/#i-5_
</div></details>
</div></details>

<details><summary>**3.Flaskを用いたWebアプリケーションの作成**</summary><div>
　
<details><summary>Flaskとは</summary><div>
FlaskとはPythonのWebアプリケーションフレームワークのこと。
小規模向けの簡単なWebアプリケーションを作るのに適している。
Webフレームワークとは、ウェブサイトやウェブアプリケーションを作るための機能を提供し、ウェブフレームワークを使わない時よりもより容易にWebアプリケーションを作ることができるもの。
</div></details>
<details><summary>Flaskサイド制作で作成したコード※説明付き</summary><div>

```python
#ライブラリとモジュールをインポートする。
import os
from flask import Flask, request, redirect, url_for, render_template, flash
from werkzeug.utils import secure_filename
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.preprocessing import image
import numpy as np


classes = ["バックホウ","ブルドーザー","ローラー"] #分類したいクラス名を定義する。
num_classes = len(classes) #リストの長さを取得し定義する。
image_size = 50 #イメージサイズを定義する。

#UPLOAD_FOLDERはアップロードされたファイルを格納する場所を指定する。
UPLOAD_FOLDER = "uploads"
#アップロードを許可するファイルを指定する。
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif']) 

app = Flask(__name__) #Flaskクラスのメソッドを使えるようにする。　例：@app.route、app.run()

#ファイル名が正しいフォーマットになっているかを確認している。
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS 

model = load_model('./my_model.h5')#学習済みモデルをロードする

@app.route('/', methods=['GET', 'POST']) #URLが受け入れるHTTPメソッドを指定する。
def upload_file():
    if request.method == 'POST': #request.methodはページへの接続方式を探知する機能。POSTかを判別する。
        if 'file' not in request.files: #ファイルがない場合
            flash('ファイルがありません')
            return redirect(request.url) # リクエストされたページに転送する。
        file = request.files['file']
        if file.filename == '': #ファイル名がない場合　　　
            flash('ファイルがありません')
            return redirect(request.url) #リクエストされたページに転送する。
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename) #サニタイズするする。
            file.save(os.path.join(UPLOAD_FOLDER, filename)) #ファイルの保存する。
            filepath = os.path.join(UPLOAD_FOLDER, filename) #保存先をfilepathに格納する。

            #受け取った画像を読み込み、np形式に変換する。
            img = image.load_img(filepath, grayscale=False, target_size=(image_size,image_size))
            img = image.img_to_array(img)
            data = np.array([img])
            #変換したデータをモデルに渡して予測する。
            result = model.predict(data)[0]
            predicted = result.argmax()
            pred_answer = "これは " + classes[predicted] + " です"
            #render_templateの引数にanswer=pred_answerと渡すことで、index.htmlに書いたanswerにpred_answerを代入する。
            #この引数に渡すhtmlファイルはtemlpatesフォルダに置いておく必要がある。
            return render_template("index.html",answer=pred_answer) 
    #POSTリクエストがなされないときはindex.htmlのanswerには何も表示しない。
    return render_template("index.html",answer="") 

#Pythonスクリプトを実行したときのみ、if __name__ == '__main__':以下の処理を実行させるようにする。
if __name__ == "__main__":
    port = int(os.environ.get('PORT', 8080)) #os.environ.getで取得した環境変数の文字列型(str)の値を数値方(int)に変更して代入する。
    app.run(host ='0.0.0.0',port = port) #app.run()が実行され、サーバが立ち上げる。

```
</div></details>
<details><summary>その他（HTML&CSSサイド制作等）</summary><div>
<details><summary>HTML&CSSサイド作成</summary><div>
<details><summary>HTMLのコード</summary><div>

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>建設機械判定機</title>
    <link rel="stylesheet" href="./static/stylesheet.css">
</head>

<body>
    <header>   
        <a class="header-logo" href="#">建設機械判定機</a>
    </header>

    <div class="main">    
        <h2> AIが送信された建設機械画像の種類を識別します</h2>
        <p>バックホウ ・ブルドーザー・ローラーの３種類のみ識別可能</p> 
    <div class="sub">    
        <a href= "https://kcsj.komatsu/products/construction_machine/img/excavator_02_im01.jpg" >バックホウ画像</a>
        <a href= "https://kcsj.komatsu/products/construction_machine/img/bulldozer_02_im15.jpg" >ブルドーザー画像</a>
        <a href= "https://www.sakainet.co.jp/products/assets_c/2017/12/tz704-thumb-558x380-1284.png" >ローラー画像</a>
    </div>
        <p>必要があれば上のリンクから画像をダウンロードして送信してください</p>
        <form method="POST" enctype="multipart/form-data">
            <input class="file_choose" type="file" name="file">
            <input class="btn" value="submit!" type="submit">
        </form>
        <div class="answer">{{answer}}</div>
    </div>

    <footer>
        <small>&copy; 2021 Osaka_taro.</small>   
    </footer>
</body>
</html>
```

</div></details>
<details><summary>CSSのコード</summary><div>

```css
header {
    background-color: #5d74c0;
    height: 60px;
    margin: -8px;
    display: flex;
    flex-direction: row-reverse;
    justify-content: space-between;
}

.header-logo {
    color: rgb(255, 252, 75);
    font-size: 25px;
    margin: 15px 25px;
}

.header_img {
    height: 25px;
    margin: 15px 25px;
}

.main {
    height: 480px;
}

h2 {
    color: #444444;
    margin: 90px 0px 0px 0px;
    text-align: center;
}

p {
    color: #444444;
    margin: 10px 0px 30px 0px;
    text-align: center;
}

.sub {
    text-align: center;
}

p {
    color: #444444;
    margin: 60px 0px 30px 0px;
    text-align: center;
}

.answer {
    color: #000000;
    font-size: 32px;
    margin: 30px 0px 30px 0px;
    text-align: center;
}

form {
    text-align: center;
}

footer {
    background-color: #c4c1bf;
    height: 40px;
    margin: -8px;
    position: relative;
}

.footer_img {
    height: 25px;
    margin: 15px 25px;
}

small {
    margin: 15px 25px;
    position: absolute;
    left: 0;
    bottom: 0;
}
```

</div></details>
</div></details>
<details><summary>フォルダの作成等</summary><div>
（１）デスクトップなどに適当な名前のフォルダを作成する。
（２）ｘｘフォルダの中に〇〇.pyを作成し、上記のコードをコピー＆ペーストする。
（３）ｘｘフォルダの中にtemplatesフォルダを作成し、その中にindex.htmlを作成する。ｘｘフォルダの中にstaticフォルダを作成し、その中にstylesheet.cssを作成する（Flaskはtemplatesフォルダの中からhtmlファイルを探し、staticフォルダの中からcssファイルを探すことになっている。）
（４）ｘｘフォルダの中の〇〇.pyと同じ階層にuploadsフォルダを作成する。
（５）ｘｘフォルダの中の〇〇.pyと同じ階層に学習させたモデルを保存したmodel.h5を入れる。
（６）デプロイの際に「Procfile」というものも一緒にアップロードするので作成する。web: python main.py
（７）requirements.txt に必要なライブラリを記述する。
（８）runtime.txtに使用するPythonのバージョンを記述する。python-3.8.7
※（６）〜（８）もｘｘフォルダの中に保存する。
</div></details>
<details><summary>ターミナル操作手順</summary><div>

```
$ cd my-project/　#カレントディレクトリにあるファイルに移動する
$ python 〇〇.py　
```

</div></details>
</div></details>
<details><summary>参照したサイト</summary><div>
_https://qiita.com/keimoriyama/items/7c935c91e95d857714fb_
_https://www.subarunari.com/entry/2017/09/28/001946_
_https://keras.io/ja/models/model/_
_https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/patterns/fileuploads.html#_
_https://www.javadrive.jp/python/string/index12.html_
</div></details>
</div></details>



<details><summary>**4.公開（Herokuへデプロイ）**</summary><div>
　
<details><summary>Herokuに登録する</summary><div>
_https://jp.heroku.com/home_
</div></details>
<details><summary>Heroku操作手順</summary><div>
(1)herokuのmypageに移動する。
(2)Create New App をクリックする。
(3)App nameを入力し、国をUnited Statesで登録する。
(4)アプリのページに遷移し、Setteingsに移動する。
(5)Add buildpackをクリックし、pythonを追加する。
</div></details>
<details><summary>ターミナル操作手順</summary><div>

```
$ heroku login
$ cd my-project/　#カレントディレクトリにあるファイルに移動する
$ git init    　　#初回のみ
$ git add .
$ git commit -am "make it better"
$ git push heroku master
```

</div></details>
</div></details>



##受講後の感想
６ヶ月間受講してPythonの基礎、簡単なアプリの作成について学ぶことができた。
独学でもできないことはないレベルではあると思うが、わからないことがあれば、すぐにSlack等で質問でき、何度でも丁寧に答えてくれるので挫折せずに修了することができた。
また給付金の支給条件としてある、すべての課題を提出しなければならないということも修了の支えとなった。

３ヶ月プランと６ヶ月プランの違いは、受講日数とメンタリングできる回数であり、内容は変わらないので３ヶ月プランにしておけば良かったなと思った。その理由は、内容的には３ヶ月もあれば十分だったこと、Slackでは無限に質問が可能でレスポンスが速いこと、メンタリングは事前予約が必要だったこと等がある。実際、メンタリングは１０回も使わなかった。

受講料が高いか安いかは使い方次第だと思う。サービスを使い倒せれば安い。与えられた課題のみをクリアしているだけでは高いというのが私の感想である。受講期間内にコースが修了すれば他のコースの受講が可能であることからもそういえるのではないだろうか。

余談ではあるが、私は一つのコースを早く終わらせて他のコースを受講するために６ヶ月コースに申し込んだが、期間が長いことで気が緩み１コース修了したのが受講修了の１週間前であった。このレベルでは転職して通用するほどのスキルは身についていない。ただ自分の日々の業務を改善するための基礎の力は身についたと思う。

##おわりに
少しの出費はあったが、Aidemyで学ぶことができて良かった。
メンターの方は、質問に対して的確に答えてくれた。
こちらが理解できるかどうかは素養もあるので、別だが何回でもわかるまで教えてくれた。
今回学んだことを自分の日々の業務に活かして行きたい。
基礎ができたので、あとは実践で習得していこうと思う。

 <font color="Red">注：コードやその説明について間違っている可能性がありますので、専門家や他のサイトでの確認をお願いいたします。Aidemyのサービス、教育訓練給付金については最新の情報を確認してください。このサイトの内容については、いかなる保証をするものではなく、一切の責任を負いません。</font>