見出し画像

異常検知の「異常データがない」問題を生成AIで解決する (検証編)

分析屋の町田です。

前回の「ソリューション編」では、異常検知における「異常データが無い」問題を解決するソリューションの概要についてお伝えしました。

今回の「検証編」では、実際に異常データを生成するプロセスを体験しながら、実践上のノウハウについても紹介していきたいと思います。


1. 概要

1.1. 実験設定

今回は、「ペットボトルの製造工場における異常検知の自動化」というケースを想定し、製造されるペットボトルの凹み、変形といった異常の生成を行います。また、セキュリティが厳しい条件でも再現できるように、オープンソースのモデルを用いて異常生成を行います。

1.2. 異常画像生成の全体像

可能な限りリアルな異常画像を生成したいので、予め①正常画像、②マスク画像、③プロンプトの3つの情報を用意します。これらをすべて生成AIに与えることにより、現実に限りなく近い異常画像を再現することができます。

1.3. 実験環境

今回は手っ取り早く異常画像生成を行うため、Google ColaboratoryのGPU環境を利用しました。

2. 実験

2.1. 正常画像の準備

今回はなるべく綺麗なペットボトルの画像を使いたかったので、「trash-plastic-bottle-detection dataset」という公開データセットの中からペットボトルの画像を1枚選定しました。今回はこの画像を正常画像とし、

この画像から異常画像を生成していきます。

2.2. マスク画像の作成

高品質な異常画像を生成するためには、「画像の中のどの領域に異常を生じさせるか」を指定する必要があります。この作業を行うために、LabelMeというアノテーションツールを用いて画像中の領域を指定し、領域情報をJSONファイルとして出力しました。

https://github.com/labelmeai/labelme

領域情報は、後の画像生成のためにマスク画像に変換しておく必要があります。今回はPythonを用いてその変換処理を行うプログラムを実装しました(コード自体もAIに生成させています)。

import json
import os
from pathlib import Path


import cv2
import numpy as np




def create_mask_from_labelme(
    json_file: str | os.PathLike, fg_color: int = 255, bg_color: int = 0
):
    # JSONファイルを読み込む
    with open(json_file, "r") as f:
        data = json.load(f)


    # 画像サイズを取得
    img_height = data["imageHeight"]
    img_width = data["imageWidth"]


    # マスク画像を初期化(黒色で塗りつぶし)
    mask = np.full((img_height, img_width), bg_color, dtype=np.uint8)


    # 各シェイプ(ポリゴン)を塗りつぶす
    for shape in data["shapes"]:
        if shape["shape_type"] == "polygon":
            # ポリゴンの座標を整数に変換
            points = np.array(shape["points"], dtype=np.int32)


            # ポリゴンを白色で塗りつぶす
            cv2.fillPoly(mask, [points], color=fg_color)


    return mask




def main():
    # パスの設定
    polygon_dir = Path("./data/annotations/polygons/")
    mask_dir = Path("./data/annotations/masks/")


    # マスク画像の格納用フォルダを作成
    mask_dir.mkdir(parents=True, exist_ok=True)


    # 各画像に対してマスク画像を生成
    for annotation_file in polygon_dir.glob("*.json"):
        mask = create_mask_from_labelme(annotation_file)
        mask_file = mask_dir / (annotation_file.stem + "_mask.png")
        cv2.imwrite(str(mask_file), mask)




if __name__ == "__main__":
    main()

2.3. プロンプトの作成

プロンプトの言語やフォーマットは、モデルの仕様に従う必要があります。今回使用するモデルは後ほど紹介しますが、プロンプトを英語で指定する必要があるモデルだったので、「dented plastic bottle」(凹んだペットボトル)というプロンプトを作成しました。

2.4. 異常画像の生成

ここまで来れば、あとは異常画像を生成するだけです。

領域指定して異常を生成させるには、「インペインティング」という手法に対応した画像生成モデルを用いる必要があります。インペインティングとは、画像中の特定の領域のみを指定して、プロンプトの指示に基づいて加工を行う手法です。例えば、「椅子の画像の上に猫を生成」「画像中の余計な障害物を削除」といった用途で使用します。

画像生成モデルは、オープンソースの画像生成モデル「stable-diffusion-xl-1.0-inpainting-0.1」を用いました。このモデルはHugging Face上で公開されており、Pythonから簡単に利用することが可能です。

https://huggingface.co/diffusers/stable-diffusion-xl-1.0-inpainting-0.1

インペインティングでは、1枚のベース画像から複数枚の画像を生成することも可能です。今回は出力品質を検証するため、1枚の正常画像から4枚の異常画像を生成しました。

from pathlib import Path


import torch
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image




# 画像生成モデル
pipe = AutoPipelineForInpainting.from_pretrained(
    "diffusers/stable-diffusion-xl-1.0-inpainting-0.1",
    torch_dtype=torch.float16,
    variant="fp16",
).to("cuda")


# データの読み込み
image = load_image("/content/data/images/plastic_bottle.jpg").resize((1024, 1024))
mask_image = load_image("/content/data/annotations/masks/plastic_bottle_mask.png").resize((1024, 1024))


# 画像生成に関する設定
prompt = "dented plastic bottle"
generator = torch.Generator(device="cuda").manual_seed(0)


# 異常画像の生成
anomaly_images = pipe(
    # 生成内容を指定するプロンプト
    prompt=prompt,
    # ソース画像
    image=image,
    # マスク画像
    mask_image=mask_image,
    # 1プロンプト当たりの画像生成数
    num_images_per_prompt=4,
    # プロンプトに対する忠実度
    guidance_scale=8.0,
    # 生成画像に対するノイズ除去の強さ
    num_inference_steps=20,
    # 入力画像に対して付与するノイズの大きさ
    # NOTE: 0~1の値を指定し、値が高いほどソース画像の面影が残る
    strength=0.99,
    # 乱数の生成器
    generator=generator,
).images

2.5. 生成結果

生成された画像を見ると、人間の目で見ても不自然さを感じないような異常が生成されていることが分かります。また、画像によって凹みの位置や大きさ、角度が大きく異なっており、たった1枚の正常画像から多様な異常データを生成出来ていることが分かります。一方でプロンプト通りの異常を生成できていないケースもあり、こういったデータはきちんと人間が確認して取り除く必要があることが分かります。

3. まとめ

2回の記事を通して、異常検知における生成AIの活用アプローチについて紹介しました。AIによる異常検知の需要は年々高まっているものの、実際には「異常データが無い」といった障壁に阻まれて実現できないというケースが多く存在します。そんな中で、近年の高性能な生成AIは、今までに無かった新たなソリューションを創造する可能性があります。今後も生成AIを正しく活用していくためには、従来よりも広い視点で物事を俯瞰し、既存の考え方に囚われないゼロベース思考が重要になってくると考えられます。


ここまでお読みいただき、ありがとうございました!
この記事が少しでも参考になりましたら「スキ」を押していただけると幸いです!

株式会社分析屋について

弊社が作成を行いました分析レポートを、鎌倉市観光協会様HPに掲載いただきました。

ホームページはこちら。

noteでの会社紹介記事はこちら。

【データ分析で日本を豊かに】
分析屋はシステム分野・ライフサイエンス分野・マーケティング分野の知見を生かし、多種多様な分野の企業様のデータ分析のご支援をさせていただいております。 「あなたの問題解決をする」をモットーに、お客様の抱える課題にあわせた解析・分析手法を用いて、問題解決へのお手伝いをいたします!

【マーケティング】
マーケティング戦略上の目的に向けて、各種のデータ統合及び加工ならびにPDCAサイクル運用全般を支援や高度なデータ分析技術により複雑な課題解決に向けての分析サービスを提供いたします。

【システム】
アプリケーション開発やデータベース構築、WEBサイト構築、運用保守業務などお客様の問題やご要望に沿ってご支援いたします。

【ライフサイエンス】
機械学習や各種アルゴリズムなどの解析アルゴリズム開発サービスを提供いたします。過去には医療系のバイタルデータを扱った解析が主でしたが、今後はそれらで培った経験・技術を工業など他の分野の企業様の問題解決にも役立てていく方針です。

【SES】
SESサービスも行っております。