今回のゴール!
下記動画 のように、服の画像 を送るだけで、その服がオフィスカジュアルなのか、カジュアルなのかを判断するモデル を一緒に作っていきましょう!
オフィスカジュアルってなんだ?
現在私は北海道の大手小売業でお惣菜担当して働いています。店舗勤務なので普段は制服を着て仕事をしているのですが、なんと本社は私服勤務らしいのです…
私はいずれ本社で働きたいと思っているのでその時のために
「その服、本当にオフィスカジュアル?」
の疑問を解消すべく、このアプリを作りました!
事前準備
・Teachable Machine のアカウント登録
・学習データの準備(私が使用したデータはこちら)
※学習モデルについては自身で撮影したものでも私が使用したデータセット利用してもどちらでも大丈夫です。
なんとこれだけ!
Teachable Machine のいいところはサクッと作れてすぐ試せるところにあるのです!
それでは早速作っていきましょう!
作成手順
・Teachable Machine を開き画像プロジェクトを選択、選ぶモデルは標準モデル
2.クラス名を入力後、アップロードボタンからデータをアップロード
3.モデルをトレーニングし、モデル評価を行う
出てきたグラフを確認しましょう!
上から順に解説していきます。
このグラフは、クラスごとの精度について書かれています。
つまり、どちらのクラスの判定が苦手かを表しています。
CLASS : 自分で作ったクラス。今回はOKとNG
ACCURACY : そのクラスだけに注目した正解率
SAMPLES : Teachable Machine が自動で Train/Test に分割したうち、テスト側へ入った枚数
このグラフからは、ACCURACY が0.8以上で高い水準にあり、OKクラスがやや得意であると読み取れます。
改めて Train/Test を自動分割してくれるのは便利ですね…
次のグラフはこちらです。
このグラフは、「 実際のラベル(行)と モデルが下した予測(列)のクロス集計表 」 です。
つまり、どんな間違いが多いのかを現しています
つまりこのグラフからは、基本対称だが 「 NG を OK と誤認 」 することがややあるという事が読み取れます。
自分で作るモデルだと罰則重み付け(データや損失に重要度の係数(重み)を掛けることで、学習時のモデルの“注目度”を調整する手法)等でモデル改良しますが、 Teachable Machine ではできないのでこのまま進みましょう!
次のグラフはこちらです。
このグラフは、横軸が学習回数、縦軸が正解率を表しているグラフです。
つまり、何周回せば精度が頭打ちになるかを表しています。
Epoch : データセットを何回学習したか
Accuracy : 正解率
acc : 学習用の画像での精度
test acc : 未知画像での精度
このグラフからは以下のことが読み取れます
・モデルは早期に大枠を学習している
・ acc と test acc に0.10~0.15の差があり軽い過学習(デルが学習データにだけ異常に適合してしまい、新しいデータでは誤差が大きくなる現象)傾向
・ Epoch 20 以降はほぼ横ばい
※ Epoch 14〜16 で橙線が 0.75→0.55 近くまで落ちて再浮上の原因は不明ですが、その後回復して安定しているのでおそらく問題ありません。学習モデルあるあるですね(笑)
Teachable Machine は Early Stopping の設定ができないのでグラフ見て判断して Epoch 変更するのがいいです!
次のグラフはこちらです。
このグラフは予測と正解のズレを現したグラフです。
・Epoch : 学習回数
・Loss : 損失地
・Training Loss : 学習に使った画像での誤差
・Test Loss : 初めて見る画像での誤差
このグラフからは下記のことが読み取れます
・Training Loss は Epoch ≈ 15 でほぼ 0つまり、学習画像を完全暗記
・Test Loss は Epoch ≈ 10 で最小 0.55 → その後 0.6 へ微増。
つまり、軽い過学習が始まり、以降は学習を続けても未知画像の損失が悪化
・ Epoch 12〜15 で打ち切る と効率・精度バランスが良さそう
3.モデルを改良
グラフで得た情報をもとに、下記のように設定して再学習
結果がこちら
グラフを見てわかる通り、判定制度自体は低下しましたが、過学習傾向は消えました。
逆に学習不足傾向なことがわかります。
Loss 曲線を見ればわかりやすいですが、緩やかに右肩下がりでまだ Epoch を伸ばしても良さそうなので下記画像のように再設定して学習させてみました
結果がこちら
かなり改善しましたね!
軽い過学習傾向はありますが、設定だけいじって過学習対策は悪手です
学習用画像の追加やクラスの追加などその時に応じて最も効果が期待できる手法を使うのが大切です。
サクッと作るが目的なので今回はこのモデルを完成としましょう!
5.実際に使ってみよう!
生成系AIにカジュアルな服装とオフィスカジュアルな画像を作ってもらい、判定してみましょう!
真逆の結果になってしまいましたね(笑)
これも醍醐味です
原因としては学習用データが海外の服装、データ数が足りない、過学習がおきているなど様々な要因が考えられます。
今後はチューニングして使いやすくしてみたいものです(笑)
おまけ
Teachable Machine では満足できないというあなたのために、同じデータを使用して勾配ブースティングで分類モデルを作ってみましょう!
コードは下記の通りで環境は GoogleColab です。
# 0. ライブラリインストール
!pip install -q lightgbm scikit-learn pillow matplotlib tqdm
# 1. 画像アップロード(1 回目 OK、2 回目 NG)
from google.colab import files
from pathlib import Path
import shutil, glob
base_dir = Path('/content/data')
(ok_dir, ng_dir) = (base_dir/'OK', base_dir/'NG')
for d in (ok_dir, ng_dir):
d.mkdir(parents=True, exist_ok=True)
print(" OK 画像をまとめて選択してください")
ok_files = files.upload()
for name in ok_files: shutil.move(name, ok_dir/name)
print(" NG 画像をまとめて選択してください")
ng_files = files.upload()
for name in ng_files: shutil.move(name, ng_dir/name)
print(f"OK: {len(list(ok_dir.iterdir()))} 枚, NG: {len(list(ng_dir.iterdir()))} 枚")
# 2. MobileNetV2 で特徴量抽出
import numpy as np
from PIL import Image
import tensorflow as tf
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
IMG_SIZE = 160
feature_extractor = MobileNetV2(weights='imagenet', include_top=False, pooling='avg')
def load_and_featurize(path):
img = Image.open(path).convert('RGB').resize((IMG_SIZE, IMG_SIZE))
x = preprocess_input(np.array(img, dtype=np.float32)[None, ...])
feat = feature_extractor.predict(x, verbose=0)
return feat.flatten()
X, y = [], []
label_map = {'OK': 0, 'NG': 1}
for cls in label_map:
for p in glob.glob(str(base_dir/cls/'*')):
X.append(load_and_featurize(p))
y.append(label_map[cls])
X = np.stack(X)
y = np.array(y)
print("特徴量 shape:", X.shape)
# 3. LightGBM で学習
from sklearn.model_selection import train_test_split
from lightgbm import LGBMClassifier
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import matplotlib.pyplot as plt
import seaborn as sns
X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.3, stratify=y, random_state=42)
clf = LGBMClassifier(
n_estimators=300,
learning_rate=0.05,
num_leaves=64,
objective='binary'
)
clf.fit(X_tr, y_tr)
y_pred = clf.predict(X_te)
print(f"Test Accuracy: {accuracy_score(y_te, y_pred):.3f}")
cm = confusion_matrix(y_te, y_pred)
plt.figure(figsize=(4,4))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['OK','NG'], yticklabels=['OK','NG'])
plt.xlabel('Predicted'); plt.ylabel('True')
plt.show()
print(classification_report(y_te, y_pred, target_names=['OK','NG']))
# 4. ボタンで画像を選んで単発判定
def test_single_image():
print(" 判定したい画像を 1 枚選択してください")
up = files.upload()
if not up: return
fname = next(iter(up))
tmp_path = f"/content/{fname}"
predict_image(tmp_path)
おまけ結果
オフィスカジュアルなほうがNG判定でした…NG率60.83%だからギリギリなのかな…
カジュアルなほうはNG判定で正解!しかもNG率99.66%と自信持ってますね!
最後に
今回は Teachable Machine を利用してサクッと分類アプリを作ってみました!
皆さんもいろんなものを分類してみてくださいね
最後まで読んでいただきありがとうございました!