ステップ2:機械学習のHello World(Scikit-learn)
前のステップ(ステップ1)でできるようになったこと:
OpenCVで画像をグレースケール変換・二値化・輪郭抽出し、「キズの面積」「色の濃さ」などの数値(特徴量)を画像から抽出できるようになりました。ただし、閾値は人間が手動で決める必要がありました。
■ 目的とゴール
ステップ1(OpenCV)では、「面積が100以上ならNG」というように人間がルール(閾値)を決めて判定しました。これを「ルールベース」と呼びます。
ステップ2では、「AIに過去のデータを見せて、ルール(境界線)を自動で作らせる」という機械学習の根本的なアプローチを学びます。
工場現場において、ディープラーニングは大げさすぎる(データが集まらない、計算コストが高すぎる)ケースも多々あります。その際、この「古典的な機械学習」が非常に強力な武器になります。
■ 環境構築
ステップ0で作成した仮想環境を使います。
cd factory_ai_tutorial
source venv/bin/activate
pip install scikit-learn
■ 「特徴量(とくちょうりょう)」という考え方
ディープラーニングは画像(ピクセルの集まり)をそのまま丸飲みしますが、古典的な機械学習では、人間が画像から「判定に使えそうな数値(特徴量)」を抽出してAIに渡します。
- 例:ステップ1のOpenCVで抽出した「キズの面積」「キズの周囲の長さ」「色の濃さ」など。
■ サンプルプログラムの作成
今回は、画像そのものを読み込むのではなく、「OpenCVで抽出した架空のデータ(キズの面積と色の濃さ)」がすでに手元にあると仮定し、サポートベクターマシン(SVM)という強力なアルゴリズムにOK/NGを分類させます。
以下のコードを step2_ml.py という名前で保存してください。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# ==========================================
# 1. 架空の工場データ(特徴量)を作成
# 本来はステップ1のOpenCVで実際の画像から特徴量を抽出しますが、
# 今回は学習の流れに集中するためダミーデータを使います
# ==========================================
np.random.seed(42)
# 【OK品 100個】キズの面積が小さく、色が薄い傾向
ok_area = np.random.normal(30, 10, 100)
ok_color = np.random.normal(80, 15, 100)
ok_labels = np.zeros(100) # OKは「0」
# 【NG品 100個】キズの面積が大きく、色が濃い傾向
ng_area = np.random.normal(70, 15, 100)
ng_color = np.random.normal(130, 20, 100)
ng_labels = np.ones(100) # NGは「1」
# データを結合
X = np.vstack((np.column_stack((ok_area, ok_color)), np.column_stack((ng_area, ng_color))))
y = np.concatenate((ok_labels, ng_labels))
# ==========================================
# 2. データを「学習用」と「テスト用」に分割
# ==========================================
# 全データの80%を学習に使い、残り20%を「カンニングなしの小テスト」に使います
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# ==========================================
# 3. AIモデルの定義と学習
# ==========================================
model = SVC(kernel='linear')
model.fit(X_train, y_train)
# ==========================================
# 4. テストデータで予測と答え合わせ
# ==========================================
predictions = model.predict(X_test)
accuracy = accuracy_score(y_test, predictions)
print(f"AIの正解率: {accuracy * 100:.1f}%")
# ==========================================
# 5. 結果をグラフで可視化
# ==========================================
plt.figure(figsize=(8, 6))
plt.scatter(X[y==0][:, 0], X[y==0][:, 1], color='blue', label='OK (Class 0)', alpha=0.6)
plt.scatter(X[y==1][:, 0], X[y==1][:, 1], color='red', label='NG (Class 1)', alpha=0.6)
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xx = np.linspace(xlim[0], xlim[1], 30)
yy = np.linspace(ylim[0], ylim[1], 30)
YY, XX = np.meshgrid(yy, xx)
xy = np.vstack([XX.ravel(), YY.ravel()]).T
Z = model.decision_function(xy).reshape(XX.shape)
ax.contour(XX, YY, Z, colors='k', levels=[0], alpha=0.5, linestyles=['--'])
plt.title(f"SVM Decision Boundary (Accuracy: {accuracy * 100:.1f}%)")
plt.xlabel("Feature 1: Area (Size of Defect)")
plt.ylabel("Feature 2: Color (Darkness)")
plt.legend()
plt.grid(True)
plt.show()
■ プログラムの実行と確認
python step2_ml.py
実行すると、青い点(OK品)と赤い点(NG品)の間に、AIが自動で引いた「黒い破線(境界線)」が表示されます。人間が「面積が50以上ならNG」とプログラムを書かなくても、AIがデータ全体の傾向を見て「ここが一番綺麗に分けられるラインだ」と判断した結果です。
■ ステップ1+2を組み合わせた現場応用イメージ
- カメラから画像を取得(OpenCV)
- 画像から「キズらしき影」の面積や色合いを計算(OpenCV)
- その数値をSVMに渡し、AIが「OK」か「NG」かを判定(Scikit-learn)
このアプローチは計算量が非常に軽いため、Raspberry Pi 5などの小型デバイスでもサクサク動くというメリットがあります。
■ まとめと次のステップへ
このステップでAIが「データから線を引く」感覚を掴めたら、次はいよいよ画像のピクセル自体を直接読み込んで複雑な特徴を捉える、ステップ3の「ディープラーニング(PyTorch)」へ進みます。
