唐揚げ?orトイプードル?機械学習で判別してみた
はじめに
機械学習の勉強を始めて1ヶ月ほどたったので、実際に自分でデータを集めて簡単にモデルを作ってみたいと思いこの問題を考えました。
この画像を見て即座に唐揚げかトイプードルか判断できますか?
一瞬唐揚げに見えた人も多いはず!(実際自分は最初からあげに見えました。)
そこで唐揚げとトイプードルの分類器を作ってみようと思いました。
**目標はこのプードルがプードルと認識できること
データ分析の流れ
1.データの収集
2.データの作成
3.モデルの選択(Gridsearch)+学習
4.テストデータで予測、出力
必要なモジュールのインポート
#必要モジュールのインポート
import os
import glob
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from PIL import Image
from sklearn.model_selection import train_test_split
from icrawler.builtin import BingImageCrawler #画像スクレイピング
from sklearn.preprocessing import StandardScaler
import seaborn as sns
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import roc_curve, recall_score, confusion_matrix, accuracy_score
#使わないものもあるかも...
1.データの収集
データの収集では一つ一つ画像検索してダウンロードして...はめんどくさすぎるのでicrawlerを使います。icrawlerのBingImageCrawlerはBingで指定したキーワードを画像検索して帰ってきた画像を自動でダウンロードしてくれるPythonライブラリです。
参考:https://atmarkit.itmedia.co.jp/ait/articles/2010/28/news018.html
#画像スクレイピング
def scrape_img(keyword,max_num,path):
crawler = BingImageCrawler(
downloader_threads = 4,
storage = {'root_dir' : path}
)
crawler.crawl(
keyword = keyword,
max_num = max_num
)
print('Done')
#pathの定義
karaage_path = 'karaage/*.jpg'
poodle_path = 'poodle/*.jpg'
#scrape
scrape_img('唐揚げ',600,'karaage/')
scrape_img('トイプードル',600,'poodle/')
2.データの作成
まずそのままの画像だとサイズが大きかったりサイズが統一されていないのでリサイズを行います。
sizeは300×300に設定
# 画像のリサイズ関数
def img_resize_func(path,size):
img_paths = glob.glob(path)
for i in img_paths:
img = Image.open(i)
img_resize = img.resize((size,size)) #1:1
img_resize.save(i) #上書き
print('resize Done')
img_resize_func(karaage_path,300)
img_resize_func(poodle_path,300)
次にデータセットの作成をして、pandasのDataframeの形にしておきます。
目的変数として唐揚げをcluster==0,プードルをcluster==1とします。
#データセットの作成
def make_dataset(img_paths):
dataset = []
for i in img_paths:
img = Image.open(i)
img_array = np.asarray(img)
s = img_array.shape[0] * img_array.shape[1] * img_array.shape[2]
img_width = img_array.reshape(1,s)
dataset.append(img_width[0])
dataset = np.array(dataset)
return dataset
karaage_df = make_dataset(img_paths=glob.glob(karaage_path))
poodle_df = make_dataset(img_paths=glob.glob(poodle_path))
#karaage_dfとpoodle_dfの結合
karaage_df = pd.DataFrame(karaage_df)
poodle_df = pd.DataFrame(poodle_df)
karaage_df['cluster'] = '0'
poodle_df['cluster'] = '1'
df = pd.concat([karaage_df,poodle_df],axis=0)
#説明変数と目的変数に分割
cluster = df.cluster
X = df.drop(['cluster'],axis=1)
#トレインとテストに分割
X_train,X_val,y_train,y_val = train_test_split(X,cluster,test_size=0.3,random_state=42)
3.モデルの選択(Gridsearch)+学習
#ランダムフォレスト
#param_grid
RFC_grid = {
"n_estimators": [i for i in range(1, 21)],
"criterion": ["gini", "entropy"],
"max_depth":[i for i in range(1, 5)],
}
#グリッドサーチの実行
cv = GridSearchCV(
estimator=RandomForestClassifier(),
param_grid=RFC_grid
)
cv.fit(X_train,y_train)
print('best_params',cv.best_params_)
print('best_score',cv.best_score_)
best_params {'criterion': 'gini', 'max_depth': 4, 'n_estimators': 18}
best_score 0.8183486238532109
predict = cv.predict(X_val)
score = cv.score(X_val, y_val)
score
グリッドサーチのテストスコア 0.7777777777777778
#ハイパーパラメータを調整しない場合との比較
model = RandomForestClassifier()
model.fit(X_train, y_train)
score = model.score(X_val, y_val)
predict = model.predict(X_val)
print("")
print("デフォルトスコア:", score)
デフォルトスコア: 0.811965811965812
デフォルトのスコアの方が良いみたい...
他の探索範囲と評価指標も試したいけど今回はこのままデフォルトを採用します。
一応混同行列を出しときます。
confusion_matrix(y_val,predict)
混同行列
[101, 19],
[ 19, 95]
4. テストデータで予測、出力
predict = model.predict(test_data)
predict_df = pd.DataFrame(data=img_paths,columns=['name'])
predict_df['predict'] = predict
for i in img_paths:
plt.figure(figsize=(8,5))
judge = predict_df.loc[predict_df['name']==i,'predict'].astype(int).values
if judge[0] == 0:
judge = '唐揚げ'
elif judge[0] == 1:
judge = 'プードル'
im = Image.open(i)
im_list = np.asarray(im)
plt.imshow(im_list)
plt.title('予測結果→'+judge)
plt.show()
plt.tight_layout()
そして目標の画像は!?
唐揚げに分類してしまいました...
最後に
予測結果を見ていると色と固まりの個数で判断しているような気がします。
まだまだ精度を上げられる余地はあるので他のモデルや評価指標を用いて再チャレンジしたいです。
プードルが食べられてしまわないように!!
続編
コードまとめ
#必要モジュールのインポート
import os
import glob
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from PIL import Image
from sklearn.model_selection import train_test_split
from icrawler.builtin import BingImageCrawler #画像スクレイピング
from sklearn.preprocessing import StandardScaler
import seaborn as sns
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import roc_curve, recall_score, confusion_matrix, accuracy_score
# 画像のリサイズ関数
def img_resize_func(path,size):
img_paths = glob.glob(path)
for i in img_paths:
img = Image.open(i)
img_resize = img.resize((size,size)) #1:1
img_resize.save(i) #上書き
print('resize Done')
#データセットの作成
def make_dataset(img_paths):
dataset = []
for i in img_paths:
img = Image.open(i)
img_array = np.asarray(img)
s = img_array.shape[0] * img_array.shape[1] * img_array.shape[2]
img_width = img_array.reshape(1,s)
dataset.append(img_width[0])
dataset = np.array(dataset)
return dataset
#画像スクレイピング
def scrape_img(keyword,max_num,path):
crawler = BingImageCrawler(
downloader_threads = 4,
storage = {'root_dir' : path}
)
crawler.crawl(
keyword = keyword,
max_num = max_num
)
print('Done')
#pathの定義
karaage_path = 'karaage/*.jpg'
poodle_path = 'poodle/*.jpg'
#scrape
scrape_img('唐揚げ',600,'karaage/')
scrape_img('トイプードル',600,'poodle/')
#scraping-resize-make_dataset
img_resize_func(karaage_path,300)
karaage_df = make_dataset(img_paths=glob.glob(karaage_path))
img_resize_func(poodle_path,300)
poodle_df = make_dataset(img_paths=glob.glob(poodle_path))
#karaage_dfとpoodle_dfの結合
karaage_df = pd.DataFrame(karaage_df)
poodle_df = pd.DataFrame(poodle_df)
karaage_df['cluster'] = '0'
poodle_df['cluster'] = '1'
df = pd.concat([karaage_df,poodle_df],axis=0)
#test dataの読み込み
test_img_paths = 'test_img/*.jpg'
img_paths = glob.glob(test_img_paths)
img_resize_func(test_img_paths,300)
test_data = make_dataset(img_paths)
cluster = df.cluster
X = df.drop(['cluster'],axis=1)
from sklearn.model_selection import train_test_split
X_train,X_val,y_train,y_val = train_test_split(X,cluster,test_size=0.3,random_state=42)
#ハイパーパラメータを調整しない場合との比較
model = RandomForestClassifier()
model.fit(X_train, y_train)
score = model.score(X_val, y_val)
predict = model.predict(X_val)
print("")
print("デフォルトスコア:", score)
predict = model.predict(test_data)
predict_df = pd.DataFrame(data=img_paths,columns=['name'])
predict_df['predict'] = predict
for i in img_paths:
plt.figure(figsize=(8,5))
judge = predict_df.loc[predict_df['name']==i,'predict'].astype(int).values
if judge[0] == 0:
judge = '唐揚げ'
elif judge[0] == 1:
judge = 'プードル'
im = Image.open(i)
im_list = np.asarray(im)
plt.imshow(im_list)
plt.title('予測結果→'+judge)
plt.show()
plt.tight_layout()