良品学習とは?不良品の画像検査を良品だけで学習する仕組みと、しきい値の決め方(Pythonの実行例つき)

良品学習とは、正常な製品(良品)の画像やデータだけを学習させ、「良品らしくないもの」を不良として見つける異常検知の考え方です。不良品のサンプルがほとんど集まらない工場の外観検査で使われ、学習した良品との差(異常度)にしきい値を引いて合否を決めます。

この記事では、良品学習と「良品・不良品の両方を学習する方法」の違い、代表的な手法、しきい値の決め方を整理し、Pythonで小さな良品学習を動かして、見逃しと過検出がどう入れ替わるかを実際の出力で確かめます。

良品学習とは

工場の検査をAIに任せるとき、素直に考えると「良品の画像」と「不良品の画像」を大量に集めて分類モデルに学習させます。ところが実際の製造ラインでは、不良はめったに出ません。出たとしても傷・欠け・異物・変色など種類がばらばらで、まだ一度も出ていない種類の不良もあります。

そこで、集めやすい良品だけでモデルを作り、「学習した良品の範囲から外れているかどうか」で判定するのが良品学習です。機械学習の用語では、学習データに異常が混ざっていない前提で新しいデータの異常を見つける問題をノベルティ検出(novelty detection)と呼び、scikit-learnの公式ドキュメントでも、学習データに外れ値が混ざる「外れ値検出」と区別して説明されています(scikit-learn: Novelty and Outlier Detection)。

良品学習と、不良品も学習する方法の違い

項目 良品学習(異常検知) 良品・不良品の両方を学習(分類・物体検出)
必要なデータ 良品の画像が中心。不良品は評価用に少しあればよい 良品と不良品の両方。不良の種類ごとに枚数が必要
初めて出る種類の不良 良品と違えば拾える可能性がある 学習していない種類は見逃しやすい
不良の種類の判別 基本は「良品か、そうでないか」だけ 「傷」「欠け」など種類ごとに分けられる
苦手なこと 照明や置き方のぶれなど、良品の中のばらつきを不良と誤認しやすい 不良品が少ないとデータの偏りが大きくなる

実務では、まず良品学習で「怪しいもの」をはじき、はじいたものを人が見て不良の種類を記録し、データが貯まったら種類の分類も加える、という順番で組み合わせることがよくあります。

良品学習の代表的な手法

良品学習の手法は、大きく次の3つの考え方に分けられます。どれも「良品の特徴を覚えて、そこからの距離やずれを異常度にする」という点は同じです。

考え方 代表的な手法 異常度の出し方
再構成型 PCA、オートエンコーダ 良品だけで「圧縮して元に戻す」仕組みを作り、うまく戻せなかった量(再構成誤差)を異常度にする
特徴量の距離型 学習済みCNNの特徴量+近傍探索(PatchCoreなど) 画像の小さな領域ごとの特徴量を良品の特徴量の集まりと比べ、いちばん近いものとの距離を異常度にする
密度・境界型 One-Class SVM、Isolation Forest、局所外れ値因子法(LOF) 良品が集まる領域の境界を学習し、境界からの外れ具合を異常度にする

画像の外観検査でよく名前が挙がるPatchCoreは、論文「Towards Total Recall in Industrial Anomaly Detection」(arXiv:2106.08265)で提案された手法です。このような画像向けの手法をまとめて試せるオープンソースのライブラリとして、Apache-2.0ライセンスのanomalibがあります(PyPIの最新版は2.7.0、2026年10月16日確認)。

数値のセンサーデータ(温度・振動・電流など)なら、scikit-learnのOneClassSVMやIsolationForest、novelty=Trueを指定したLocalOutlierFactorで始められます。LOFを良品学習に使う場合、学習に使ったデータそのものにpredictを使うと正しい結果にならない点が公式ドキュメントで注意されています。

Pythonで良品学習を試す

ここでは、16×16画素の「板」の画像を人工的に作り、良品だけでPCA(主成分分析)を学習させて、再構成誤差で不良を見つけます。良品はなめらかなグラデーションにノイズと明るさのばらつきを加えたもの、不良品はそこに短い暗い傷を1本入れたものです。実際の製品画像ではありませんが、良品学習の流れ(良品で学習→良品だけでしきい値を決める→混ぜたテストで評価)はそのまま同じです。

実行環境はPython 3.12、NumPy 2.5.4、scikit-learn 1.9.1です。

import numpy as np
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix, roc_auc_score

rng = np.random.default_rng(0)
SIZE = 16

def make_good(n):
    """良品:明るさが少しずつ違う、なめらかなグラデーションの板"""
    base = np.linspace(0.3, 0.7, SIZE)[None, :].repeat(SIZE, axis=0)
    imgs = base[None] + rng.normal(0, 0.03, size=(n, SIZE, SIZE))
    imgs += rng.uniform(-0.05, 0.05, size=(n, 1, 1))
    return imgs

def make_defect(n):
    """不良品:良品の上に、位置がばらばらの暗い傷(線)が入る"""
    imgs = make_good(n)
    for img in imgs:
        r = rng.integers(2, SIZE - 2)
        c0 = rng.integers(0, SIZE - 6)
        img[r, c0:c0 + 3] -= 0.12
    return imgs

# 学習には良品だけを使う(良品学習)
train = make_good(300).reshape(300, -1)
pca = PCA(n_components=5).fit(train)

def score(x):
    recon = pca.inverse_transform(pca.transform(x))
    return ((x - recon) ** 2).mean(axis=1)   # 再構成誤差=異常度

# しきい値は「良品だけの検証データ」の99パーセンタイル
val = make_good(200).reshape(200, -1)
th = np.percentile(score(val), 99)

test_x = np.vstack([make_good(100), make_defect(20)]).reshape(120, -1)
test_y = np.array([0] * 100 + [1] * 20)       # 1=不良
s = score(test_x)
pred = (s > th).astype(int)

print(f"しきい値: {th:.5f}")
print("混同行列 [[良品→良品, 良品→不良], [不良→良品, 不良→不良]]")
print(confusion_matrix(test_y, pred))
print(f"ROC AUC: {roc_auc_score(test_y, s):.3f}")

# しきい値を動かすと、見逃しと過検出が入れ替わる
for q in [90, 99, 99.9]:
    t = np.percentile(score(val), q)
    cm = confusion_matrix(test_y, (s > t).astype(int))
    print(f"{q:>5}パーセンタイル: 過検出(良品を不良) {cm[0,1]:>2}件 / 見逃し(不良を良品) {cm[1,0]:>2}件")

実行結果です。

しきい値: 0.00107
混同行列 [[良品→良品, 良品→不良], [不良→良品, 不良→不良]]
[[100   0]
 [ 10  10]]
ROC AUC: 0.950
   90パーセンタイル: 過検出(良品を不良)  6件 / 見逃し(不良を良品)  3件
   99パーセンタイル: 過検出(良品を不良)  0件 / 見逃し(不良を良品) 10件
 99.9パーセンタイル: 過検出(良品を不良)  0件 / 見逃し(不良を良品) 14件

しきい値を「良品の99パーセンタイル」にすると、良品を不良と判定する過検出は0件ですが、不良品20個のうち10個を見逃しています。しきい値を下げて90パーセンタイルにすると、見逃しは3件に減る代わりに過検出が6件に増えます。ROC AUCが0.950あっても、しきい値の置き方しだいで現場での使い勝手が大きく変わることがわかります。

不良の場所を示す

再構成誤差は画素ごとにも計算できるので、どこが良品と違うのかを示せます。次の例では、10行目の4〜9列目に傷を入れた画像で、誤差の大きい画素を取り出しています。

import numpy as np
from sklearn.decomposition import PCA

rng = np.random.default_rng(1)
SIZE = 16
base = np.linspace(0.3, 0.7, SIZE)[None, :].repeat(SIZE, axis=0)
good = base[None] + rng.normal(0, 0.03, size=(300, SIZE, SIZE))
pca = PCA(n_components=5).fit(good.reshape(300, -1))

# 10行目の4〜9列目に傷を入れた不良品を1枚つくる
img = base + rng.normal(0, 0.03, size=(SIZE, SIZE))
img[10, 4:10] -= 0.2

x = img.reshape(1, -1)
diff = (x - pca.inverse_transform(pca.transform(x))).reshape(SIZE, SIZE) ** 2
r, c = np.unravel_index(diff.argmax(), diff.shape)
print(f"誤差が最大の画素: 行{r} 列{c}")
top = np.argsort(diff, axis=None)[-6:]
print("誤差が大きい上位6画素:", sorted((int(i) // SIZE, int(i) % SIZE) for i in top))
誤差が最大の画素: 行10 列9
誤差が大きい上位6画素: [(10, 4), (10, 5), (10, 6), (10, 7), (10, 8), (10, 9)]

誤差の大きい上位6画素が、傷を入れた10行目の4〜9列目とぴったり一致しました。実際の外観検査では、この画素ごとの誤差を色で重ねた「ヒートマップ」を検査員に見せ、AIがどこを怪しいと判断したのかを確認できるようにします。

しきい値の決め方

良品学習のモデルが出すのは「異常度」という数値だけで、どこから不良にするかは人が決めます。決め方の基本は次のとおりです。

  1. 学習に使っていない良品で異常度の分布を見る:上の例では、学習用とは別の良品200枚の異常度から、99パーセンタイルなどの値をしきい値の候補にしました。
  2. 見逃しと過検出のどちらが重いかを決める:不良品が客先に流れると重大な事故になる部品なら見逃しを減らす側(しきい値を下げる)に、過検出で人の再検査が増えすぎるなら逆側に寄せます。
  3. 少数でも本物の不良品で確かめる:学習には使わなくても、評価には実物の不良サンプルが必要です。混同行列で、見逃しと過検出の件数を確認します。
  4. 「グレーゾーン」を設ける:しきい値を2本引き、その間に入ったものだけ人が見る運用にすると、全数を人が見るより負担を減らしつつ見逃しを抑えられます。

評価に使う適合率・再現率・ROC AUCの考え方は、混同行列から計算できます。用語の整理は教師なし学習とは?教師あり学習との違いと代表的な手法をPythonで試すも参考になります。

現場で精度が落ちる原因

良品学習は「学習した良品と違うもの」を全部異常とみなすため、不良ではない変化にも反応します。導入前に、次の点を揃えておくことが大切です。

  • 撮影条件:照明の向きや明るさ、カメラの位置、製品の置き方が変わると、良品でも異常度が上がります。治具で位置を固定し、照明を囲うのが基本です。
  • 良品のばらつきを学習データに入れる:ロットや材料の違い、季節による色味の変化など、「不良ではないが見た目が変わる」ものを良品として学習させておきます。
  • 学習用の良品に不良を混ぜない:見落とした不良が学習データに入ると、その不良を「良品らしい」と覚えてしまいます。
  • ラインの変更後は学習し直す:品種追加や設備交換のあとは、しきい値を含めて評価をやり直します。
  • 処理時間:ラインの速度に判定が間に合うかを確認します。工場内の機器で推論する方法はエッジAIとは?クラウドAIとの違い・動かす仕組み・量子化をわかりやすく解説で解説しています。

試すときに使える公開データセット

自社の画像が集まる前に手法を比べたい場合、外観検査向けの公開データセットとしてMVTec社のMVTec ADがよく使われます。15種類の物体・テクスチャについて、学習用は欠陥のない画像だけ、テスト用は欠陥のある画像とない画像が入っており、良品学習の評価にそのまま使える構成です。ただし、ライセンスはCC BY-NC-SA 4.0で、商用目的での利用は認められていません(2026年10月16日確認)。社内の製品開発に使う前に、利用目的が規約に合うかを確認してください。

手法の考え方を学ぶなら、PCAやオートエンコーダで再構成誤差を出すところから始め、画像の検査で精度が足りなければ学習済みCNNの特徴量を使う方法へ進むのが順番としてわかりやすいでしょう。製造現場でのPythonの使いどころは製造業・建設業の現場のPython活用にまとめています。

PCAによる次元削減はテックジムのAIエンジニア養成コース(16章で機械学習とディープラーニングを学ぶコース)の第1章で、混同行列やROC/AUCによる評価は第11章で扱っています。

よくある質問

Q. 良品学習には不良品の画像はまったく要りませんか?

学習には要りませんが、評価には必要です。しきい値を決めたあと、実物の不良品で見逃しと過検出の件数を確かめないと、現場で使える精度かどうか判断できません。

Q. 良品学習と教師なし学習は同じものですか?

良品学習は「良品」というラベルの付いたデータだけを使うので、厳密には完全な教師なし学習ではなく、異常検知の中のノベルティ検出にあたります。手法としてはPCAやオートエンコーダなど、教師なし学習と同じものを使うことが多いです。

Q. しきい値はどうやって決めればよいですか?

学習に使っていない良品の異常度の分布から候補を決め、見逃しと過検出のどちらが重いかで上下させます。2本のしきい値の間だけ人が見るグレーゾーンを設ける運用もあります。

Q. 良品学習は不良の種類まで判別できますか?

基本は「良品か、そうでないか」の判定だけです。種類を分けたい場合は、はじいた画像に人が種類を記録してデータを貯め、分類や物体検出のモデルを別に作って組み合わせます。

機械学習の基礎の関連記事

同じテーマの記事をまとめています。あわせて読むと理解が早くなります。

AI企業・スタートアップの関連記事

AIの分野ごとに、国内外の企業を公式情報で確認してまとめています。

機械学習の全体像は機械学習とは?仕組み・種類・できることを簡単に解説|Pythonで動く例つきにまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら