画像識別アプリの作成プロセスと成果物

# **◆アプリ制作のプロセス◆**

## **STEP０.はじめに**
　AidemyでAIアプリコースを受講しました。
　もともとプログラミングに興味があり、プログラミングスクールでRubyや
　Javaの習得を検討していましたが、似たようなコンテンツとサービスの
　スクールが多く、どちらで受講しようか悩んでいました。
　そんな中、Pythonという今後需要が高まるであろう言語で、AIアプリを作る
　コースを提供しているAidemyに出会い、折角習うなら面白そうなものをという
　思いで、Aidemyを選択しました。

　本稿では、AIアプリ作成コースでの最終課題の作成プロセスと成果物を紹介します。
  
　【実行環境】
　　PC:Windows10
　　Python:-3.9.12

## **STEP１.アプリのテーマを決める**
　作りたいと思ったのは以下の4つ。
　中でもKaggleにデータがあったキノコの判別にしました。

　<INS>候補テーマ</INS>
　　◇雲の形から名称を判断
　　◇空の写真から、晴れ・曇り等の天候を判断
　　◆キノコの種類を判別　←←←採用！
　　◇動物の足跡の識別

## **STEP２.データの取得・****
　データの取得方法は幾つかありますが、
　今回は目的にかなうデータがあったKaggleから調達しました。
　ダウンロードした後、解凍してGoogleDrive内に保存し、ディレクトリを取得します。
<br>

## **STEP３．Flaskサイド（環境：Google Colaboratery)**
　モデルの生成のプログラムとして最初に作成したものがこちら↓

```python
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import Dense, Dropout, Flatten, Input
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras import optimizers
#import pickle 
#from google.colab import drive

#データの取得 12種類のキノコ
path_Amanita_Caesarea= os.listdir('/content/drive/MyDrive/Mashroomdata/Amanita_Caesarea-Edible')
path_Amanita_Citrina= os.listdir('/content/drive/MyDrive/Mashroomdata/Amanita_Citrina-Edible')
path_Amanita_Pantherina= os.listdir('/content/drive/MyDrive/Mashroomdata/Amanita_Pantherina-NotEdible')
path_Boletus_Regius= os.listdir('/content/drive/MyDrive/Mashroomdata/Boletus_Regius-Edible')
path_Entoloma_Lividum= os.listdir('/content/drive/MyDrive/Mashroomdata/Entoloma_Lividum-NotEdible')
path_Gyromitra_Esculenta= os.listdir('/content/drive/MyDrive/Mashroomdata/Gyromitra_Esculenta-NotEdible')
path_Helvella_Crispa= os.listdir('/content/drive/MyDrive/Mashroomdata/Helvella_Crispa-Edible')
path_Hydnum_Rufescens= os.listdir('/content/drive/MyDrive/Mashroomdata/Hydnum_Rufescens-NotEdible')
path_Morchella_Deliciosa= os.listdir('/content/drive/MyDrive/Mashroomdata/Morchella_Deliciosa-Edible')
path_Phallus_Impudicus= os.listdir('/content/drive/MyDrive/Mashroomdata/Phallus_Impudicus-NotEdible')
path_Russula_Cyanoxantha= os.listdir('/content/drive/MyDrive/Mashroomdata/Russula_Cyanoxantha-Edible')
path_Russula_Delica= os.listdir('/content/drive/MyDrive/Mashroomdata/Russula_Delica-NotEdible')


#データの処理

img_Amanita_Caesarea=[]    #タマゴダケ
img_Amanita_Citrina=[]     #コタマゴテングダケ
img_Amanita_Pantherina=[]  #テングダケ
img_Boletus_Regius=[]      #ヤマドリダケ   
img_Entoloma_Lividum=[]    #イッポンシメジ
img_Gyromitra_Esculenta=[] #シャグマアミガサタケ
img_Helvella_Crispa=[]     #ノボリリュウ
img_Hydnum_Rufescens=[]    #アミタケ
img_Morchella_Deliciosa=[]        #アシボソアミガサタケ
img_Phallus_Impudicus=[]          #スッポンタケ
img_Russula_Cyanoxantha=[]        #カワリハツ
img_Russula_Delica=[]             #シロハツ

for i in range(len(path_Amanita_Caesarea)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Amanita_Caesarea-Edible/'+path_Amanita_Caesarea[i])
  img=cv2.resize(img,(50,50))
  img_Amanita_Caesarea.append(img)

for i in range(len(path_Amanita_Citrina)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Amanita_Citrina-Edible/'+path_Amanita_Citrina[i])
  img=cv2.resize(img,(50,50))
  img_Amanita_Citrina.append(img)

for i in range(len(path_Amanita_Pantherina)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Amanita_Pantherina-NotEdible/'+path_Amanita_Pantherina[i])
  img=cv2.resize(img,(50,50))
  img_Amanita_Pantherina.append(img)

for i in range(len(path_Boletus_Regius)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Boletus_Regius-Edible/'+path_Boletus_Regius[i])
  img=cv2.resize(img,(50,50))
  img_Boletus_Regius.append(img)

for i in range(len(path_Entoloma_Lividum)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Entoloma_Lividum-NotEdible/'+path_Entoloma_Lividum[i])
  img=cv2.resize(img,(50,50))
  img_Entoloma_Lividum.append(img)

for i in range(len(path_Gyromitra_Esculenta)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Gyromitra_Esculenta-NotEdible/'+path_Gyromitra_Esculenta[i])
  img=cv2.resize(img,(50,50))
  img_Gyromitra_Esculenta.append(img)

for i in range(len(path_Helvella_Crispa)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Helvella_Crispa-Edible/'+path_Helvella_Crispa[i])
  img=cv2.resize(img,(50,50))
  img_Helvella_Crispa.append(img)

for i in range(len(path_Hydnum_Rufescens)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Hydnum_Rufescens-NotEdible/'+path_Hydnum_Rufescens[i])
  img=cv2.resize(img,(50,50))
  img_Hydnum_Rufescens.append(img)

for i in range(len(path_Morchella_Deliciosa)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Morchella_Deliciosa-Edible/'+path_Morchella_Deliciosa[i])
  img=cv2.resize(img,(50,50))
  img_Morchella_Deliciosa.append(img)

for i in range(len(path_Phallus_Impudicus)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Phallus_Impudicus-NotEdible/'+path_Phallus_Impudicus[i])
  img=cv2.resize(img,(50,50))
  img_Phallus_Impudicus.append(img)

for i in range(len(path_Russula_Cyanoxantha)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Russula_Cyanoxantha-Edible/'+path_Russula_Cyanoxantha[i])
  img=cv2.resize(img,(50,50))
  img_Russula_Cyanoxantha.append(img)

for i in range(len(path_Russula_Delica)):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Russula_Delica-NotEdible/'+path_Russula_Delica[i])
  img=cv2.resize(img,(50,50))
  img_Russula_Delica.append(img)

#学習データと学習データに対応するラベルを作成
X=np.array(img_Amanita_Caesarea+img_Amanita_Citrina+img_Amanita_Pantherina+img_Boletus_Regius+img_Entoloma_Lividum+img_Gyromitra_Esculenta+img_Helvella_Crispa+img_Hydnum_Rufescens+img_Morchella_Deliciosa+img_Phallus_Impudicus+img_Russula_Cyanoxantha+img_Russula_Delica)
y=np.array([0]*len(img_Amanita_Caesarea)+[1]*len(img_Amanita_Citrina)+[2]*len(img_Amanita_Pantherina)+[3]*len(img_Boletus_Regius)+[4]*len(img_Entoloma_Lividum)+[5]*len(img_Gyromitra_Esculenta)+[6]*len(img_Helvella_Crispa)+[7]*len(img_Hydnum_Rufescens)+[8]*len(img_Morchella_Deliciosa)+[9]*len(img_Phallus_Impudicus)+[10]*len(img_Russula_Cyanoxantha)+[11]*len(img_Russula_Delica))
#画像をシャッフル
rand_index=np.random.permutation(np.arange(len(X)))
X=X[rand_index]
y= 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):]


#モデル定義
#ワンホットベクトル
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

input_tensor = Input(shape=(50,50,3))
vgg16 = VGG16(include_top=False,weights="imagenet",input_tensor=input_tensor)

top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256,activation="relu"))
top_model.add(Dense(64,activation="relu"))
top_model.add(Dropout(rate=0.5))
top_model.add(Dense(12, activation="softmax"))

#モデルの連結
model = Model(inputs = vgg16.input, outputs = top_model(vgg16.output))

# vgg16の重みの固定
for layer in model.layers[:19]:
   layer.trainable=False

model.compile(loss="categorical_crossentropy",optimizer=optimizers.Adam(), metrics=["accuracy"])

model.fit(X_train,y_train,batch_size=128,epochs=40,validation_data=(X_test,y_test))

#モデルの保存（精度調整後）
model.save('model.h5')

#判定する関数を定義
#画像を一枚受け取り、キノコの種類を判定して返す関数
def pred_mashroom(img):
  img = cv2.resize(img,(50,50))
  pred = np.argmax(model.predict(img.reshape(1,50,50,3)))
  if pred ==0:
    return "タマゴダケ"
  elif pred == 1:
    return "コタマゴテングダケ"
  elif pred == 2:
    return "テングダケ"
  elif pred == 3:
    return "ヤマドリダケ"
  elif pred == 4:
    return "イッポンシメジ"
  elif pred == 5:
    return "シャグマアミガサタケ"
  elif pred == 6:
    return "ノボリリュウ"
  elif pred == 7:
    return "アミタケ"
  elif pred == 8:
    return "アシボソアミガサタケ"
  elif pred == 9:
    return "スッポンタケ"
  elif pred == 10:
    return "カワリハツ"
  else:
    return "シロハツ"

#精度の評価
scores=model.evaluate(X_test,y_test,verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

#識別評価を行う
for i in range(5):
  img=cv2.imread('/content/drive/MyDrive/Mashroomdata/Amanita_Caesarea-Edible/'+ path_Amanita_Caesarea[i])
  b,g,r = cv2.split(img) 
  img = cv2.merge([r,g,b])
  plt.imshow(img)
  plt.show()
  print(pred_mashroom(img))
```
<br>

ここでの精度は57.3%

そこで幾つか要素を変更し、最も良い精度を出す条件を探しました。

　◇size  50　→　100
　　判別するデータのサイズは大きい方が実行時間は長くなるものの、
　　精度は上がるたため、100に固定
　◇判別する種類　12種類　＜　6種類  ＜　4種類
　　判別させるキノコの数を減らせば、精度は高くなります。
　　アプリとして判別できる種類が減らすのは本末転倒ではあるが、
　　試しに4種類まで減らして実行　　
　◇epochs 40　～　100
　　epochsは高すぎても精度上がらないが、判別数との相性があるかと思い、
　　幾つか試しました。

epochsと判別数を変更した場合の精度:
|   |12type  |6type  |4type  |
|---|--- |---|---|
|epochs=40　 |76.3   |None  |None  |
|epochs=50   |<font color="Red">80.1</font>   |88.5  |None  |
|epochs=60   |76.4   |<font color="Red">89.3</font>  |85.1  |
|epochs=70   |None   |87.7  |<font color="Red">93.1</font> |
|epochs=80   |None   |None  |90.2 |
|epochs=100  |75.3   |None  |None  |

→結果、4種類のタイプのキノコの識別（epochs=70)としました。

## **STEP４．サーバーサイド（HTML/CSS）**
　こちらの工程は、Aidemyの講座で使用したものをベースに
　少しアレンジして使用。

## **STEP５．Qiitaでブログ作成**
　こちらの工程は、モデルの精度向上でプログラムを実行している間に
　少しずつ進めてきました。
　マークアップ手法も幅広く、興味と工夫次第で凝った仕上がりにできます。

## **◆出来上がったアプリのサイトはこちら◆**
　＊＊＊URL＊＊＊
  [キノコの判別アプリ](https://mushroom-app-5m95.onrender.com/)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985267/f03d1ce6-7e21-eded-3938-f6020b3f5bb7.jpeg" width=80%>

結果：これはタマゴダケです（正解！）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985267/f5bc9274-3d31-d3bc-3798-be0b667d2fc3.jpeg" width=80%>

結果：これは コタマゴテングダケです（正解！）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985267/99492703-4d4e-dd4b-510b-b514cbe41323.jpeg" width=80%>

結果：これは テングダケです（正解！）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985267/5ab17191-a6d3-3935-b43f-bca8dc91a180.jpeg" width=80%>

結果：これは テングダケです（不正解！正解はイッポンシメジ）



## **◆◆NEXT STEP◆◆**
　◆データの水増し
　（データ数はキノコ1種に対して100～300と、あまり多くないため。）
　◆レイヤー構成を変更

## **◆◆感想◆◆**
　Aidemyを受講してみて、プログラミング未経験者が自走でスキルを身に着けるのは、
　難しいと感じました。
　講義のスタイルは基本、与えられた教材を自分で理解して進めていく方式。
　わからないことがあれば、技術カウンセリングかSlackなどで質問できますが、
　わからないことが多く、わかった気になれない中で進めている感じ。
　教材も、全ても説明することはないので、疑問点は自分で適宜検索して消化していく
　必要があります。
　
　振り返れば反省点は、早いうちからカウンセリングを使えばよかったな～ということ。
　技術サポートは回数制限されているため、出し惜しんで最初の方は、自走できるよう
　にならなくては！と気負って、何日も悩んで疲れて、終いにはちょっと嫌になってPC
　を開かなくなる時期もありました:laughing:
　でも、カウンセラーさんに相談すれば親切に且つ多面的に教えてくださるので、一人
　で調べているより、理解が深まり、視界が開けます。
　今後受講する方、特に初学者の方は是非、負担になる前にカウンセラーさんに相談す
　ることをお勧めします。
　
　受講を終えて、まだまだ理解できていない部分が多いですが、
　何とか課題を終えられたのは、技術カウンセリングでサポートいただいたお陰です！
　感謝:blush: