◆アプリ制作のプロセス◆
STEP0.はじめに
AidemyでAIアプリコースを受講しました。
もともとプログラミングに興味があり、プログラミングスクールでRubyや
Javaの習得を検討していましたが、似たようなコンテンツとサービスの
スクールが多く、どちらで受講しようか悩んでいました。
そんな中、Pythonという今後需要が高まるであろう言語で、AIアプリを作る
コースを提供しているAidemyに出会い、折角習うなら面白そうなものをという
思いで、Aidemyを選択しました。
本稿では、AIアプリ作成コースでの最終課題の作成プロセスと成果物を紹介します。
【実行環境】
PC:Windows10
Python:-3.9.12
STEP1.アプリのテーマを決める
作りたいと思ったのは以下の4つ。
中でもKaggleにデータがあったキノコの判別にしました。
候補テーマ
◇雲の形から名称を判断
◇空の写真から、晴れ・曇り等の天候を判断
◆キノコの種類を判別 ←←←採用!
◇動物の足跡の識別
STEP2.データの取得・**
データの取得方法は幾つかありますが、
今回は目的にかなうデータがあったKaggleから調達しました。
ダウンロードした後、解凍してGoogleDrive内に保存し、ディレクトリを取得します。
STEP3.Flaskサイド(環境:Google Colaboratery)
モデルの生成のプログラムとして最初に作成したものがこちら↓
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))
ここでの精度は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 | 80.1 | 88.5 | None |
epochs=60 | 76.4 | 89.3 | 85.1 |
epochs=70 | None | 87.7 | 93.1 |
epochs=80 | None | None | 90.2 |
epochs=100 | 75.3 | None | None |
→結果、4種類のタイプのキノコの識別(epochs=70)としました。
STEP4.サーバーサイド(HTML/CSS)
こちらの工程は、Aidemyの講座で使用したものをベースに
少しアレンジして使用。
STEP5.Qiitaでブログ作成
こちらの工程は、モデルの精度向上でプログラムを実行している間に
少しずつ進めてきました。
マークアップ手法も幅広く、興味と工夫次第で凝った仕上がりにできます。
◆出来上がったアプリのサイトはこちら◆
***URL***
キノコの判別アプリ

結果:これはタマゴダケです(正解!)

結果:これは コタマゴテングダケです(正解!)

結果:これは テングダケです(正解!)

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