Djangoで機械学習アプリケーション ~データ収集・モデル構築と評価~

#画像認識モデルの構築評価まで

画像共有サイトFlickrから画像を取得し、CNNで機械学習(ML)を行なった。

サイト作成:https://qiita.com/T_Na/items/f87bbcecb3edfca8f07d
##データ収集
flickrapiにより画像取得。

```python:data.py
from flickrapi import FlickrAPI
from urllib.request import urlretrieve
import sys,os,time
from pprint import pprint

key="###"
sercret="###"
imagename=sys.argv[1]
#コマンドプロンプト(Win)、ターミナル(Mac)上で、python data.py catなど.pyの後を指定して入力できるようにする。
save_dir="./image/"+imagename
if not os.path.exists(save_dir):os.mkdir(save_dir)
flickr=FlickrAPI(key,sercret,format="parsed-json")
flickr_result=flickr.photos.search(text=imagename,per_page=300,media="photos",
              sort="relevance",safe_search=1,extras="url_q,licence")
photos=flickr_result['photos']
#pprint(flickr_result)
for photo in photos['photo']:
    url_q = photo['url_q']
    filepath=save_dir+"/"+photo["id"]+".jpg"
    if os.path.exists(filepath):continue
    urlretrieve(url_q,filepath)
    time.sleep(1)
```
```
print(flickr_result)を実行
得られた情報から必要なものを利用して画像取得。(ここではphotos内のphotoで各画像の情報を取り出しurl_q:画像のURLを取り出し画像を得ている。)

{'photos': {'page': 1,
            'pages': 1,
            'perpage': 300,
            'photo': [{'farm': 6,
                       'height_q': '150',
                       'id': '11556879965',
                       'isfamily': 0,
                       'isfriend': 0,
                       'ispublic': 1,
                       'owner': '58701173@N07',
                       'secret': 'f4e362d0e8',
                       'server': '5490',
                       'title': 'monkry 006',
                       'url_q': 'https://live.staticflickr.com/5490/11556879965_f4e362d0e8_q.jpg',
                       'width_q': '150'},
```

##機械学習用のデータセット作成
```python:get_data.py
import numpy as np
from PIL import Image
from sklearn.model_selection import train_test_split
import os,glob

imagename= []#取得した画像のラベル(ex:動物名や名称)
image_size=100
X,Y=[],[]

for labels ,imgname in enumerate(imagename):
    imgdir="./image/"+imgname
    files=glob.glob(imgdir+"/*.jpg")
    for index ,file in enumerate(files):
        if index>=300:break
        else:
            img=Image.open(file)
            img=img.convert("RGB")
            img=img.resize((image_size,image_size))
            #データ増量(-30~30°に15°ずつ回転し、それを左右反転することでデータを倍にする)
            #以下の工程では1枚を10枚に増量
            for angle in range(-30,30,15):
                img_rot=img.rotate(angle)
                img_tra=img.transpose(Image.FLIP_LEFT_RIGHT)
                data=np.asarray(img_tra)
                X.append(data)
                Y.append(labels)

x=np.array(X)
y=np.array(Y)
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=123)
#test_size:全体の0.2(20%)をテスト用にする
data_t=(x_train,x_test,y_train,y_test)
#データ保存
np.save("./data/data-1.npy",data_t)
```
##機械学習モデル

```python:cnn.py

import keras
import numpy as np
from keras.models import Sequential
from keras.layers import Conv2D,MaxPooling2D,Dropout,Dense,Activation,Flatten
from keras.utils import np_utils
from keras.optimizers import Adam

imagename=[]#取得した画像のラベル(ex:動物名や名称)
size=120
epoch=50
image_size=100
image_len=len(imagename)
x_train,x_test,y_train,y_test=np.load("./data/data-1.npy")
x_train=x_train.astype("float")/255.0
x_test=x_test.astype("float")/255.0
y_train=y_train.astype("int")
y_test=y_test.astype("int")
y_train=np_utils.to_categorical(y_train,image_len)
y_test=np_utils.to_categorical(y_test,image_len)
#MLモデル構築
model=Sequential()
model.add(Conv2D(32,(3,3),padding="same",input_shape=x_train.shape[1:]))
model.add(Activation("relu"))
model.add(Conv2D(32,(3,3),padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.2))

model.add(Conv2D(64,(3,3),padding="same"))
model.add(Activation("relu"))
model.add(Conv2D(64,(3,3),padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.2))

model.add(Conv2D(128,(3,3),padding="same"))
model.add(Activation("relu"))
model.add(Conv2D(128,(3,3),padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.2))

model.add(Flatten())
model.add(Dense(1024))
model.add(Activation("relu"))
model.add(Dropout(0.5))
model.add(Dense(y_train.shape[1]))
model.add(Activation("softmax"))

model.compile(loss="categorical_crossentropy",optimizer="Adam",metrics=["accuracy"])
model.fit(x_train,y_train,batch_size=size,epochs=epoch,validation_split=0.1,shuffle=True,verbose=1)
#モデルを保存
model.save("./model/model-1.h5")
score=model.evaluate(x_test,y_test,verbose=0)
print("Test Loss:",score[0],"Test Accuracy:",score[1])
```
##学習結果
```
Train on 6472 samples, validate on 720 samples
Epoch 1/50
6472/6472 [==============================] - 101s 16ms/step - loss: 0.9608 - acc: 0.5246 - val_loss: 0.8161 - val_acc: 0.6444
Epoch 2/50
6472/6472 [==============================] - 100s 16ms/step - loss: 0.7509 - acc: 0.6673 - val_loss: 0.7241 - val_acc: 0.7014
Epoch 3/50
6472/6472 [==============================] - 100s 15ms/step - loss: 0.6099 - acc: 0.7494 - val_loss: 0.5272 - val_acc: 0.8028
(途中省略)
Epoch 47/50
6472/6472 [==============================] - 189s 29ms/step - loss: 0.0057 - acc: 0.9978 - val_loss: 3.0830e-04 - val_acc: 1.0000
Epoch 48/50
6472/6472 [==============================] - 189s 29ms/step - loss: 0.0084 - acc: 0.9963 - val_loss: 0.0066 - val_acc: 0.9972
Epoch 49/50
6472/6472 [==============================] - 189s 29ms/step - loss: 0.0052 - acc: 0.9988 - val_loss: 0.0065 - val_acc: 0.9986
Epoch 50/50
6472/6472 [==============================] - 190s 29ms/step - loss: 0.0106 - acc: 0.9960 - val_loss: 0.0135 - val_acc: 0.9986

Test Loss: 0.0032594245270682726 Test Accuracy: 0.9988876529477196
```
##モデルを評価する
学習させていない画像で判定

```python:predict.py
import numpy as np
import keras,sys
from keras.models import load_model
from PIL import Image

imagename=[]#取得した画像のラベル(ex:動物名や名称)
image_len=len(imagename)
image_size=100

img_dir="./pics/"+sys.argv[1]
image=Image.open(img_dir)
image=image.convert("RGB")
image=image.resize((image_size,image_size))
data=np.asarray(image)/255.0
X=[]
X.append(data)
X=np.array(X)

model=load_model("./model/model.h5")
result=model.predict([X])[0]#[5.9185062e-19 9.9720252e-01 2.7975517e-03]ラベルの数で個数が変化
predicted=result.argmax()#resultの中で最大値のみ取得
percentage=int(result[predicted]*100)
print(imagename[predicted],percentage)
```
判定画像
![img2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402159/dbe4cfb7-972d-2de6-dbf1-dda4ae56126c.jpeg)

```
結果
サル 99
```