MENU

問い合わせ


    【YOLOで作る画像認識ツール 第1回】信頼度のしきい値とJSON出力:認識結果を「使える形」にする

    前回の第0回では、YOLOで果物の写真を1枚認識し、種類・信頼度・位置を表示しました。今回は、その結果を業務で使える形に整えます。

    画像認識で最初に迷うのが、「信頼度のしきい値をいくつにするか」です。数字を少し変えるだけで、見つかる件数が大きく変わります。この回では、しきい値を変えると何が起きるかを実際の写真で確かめ、結果をJSON(プログラム同士でやり取りしやすいデータの形式)で出力できるようにします。

    目次

    この回で作るもの

    • 認識結果を、信頼度の高い順に並べて表示する
    • --json を付けると、結果をJSONで出力する
    • しきい値を変えたときの検出件数を、表で比べるスクリプト

    信頼度としきい値とは

    AIは、見つけた物体ごとに「これはorangeだと思う。自信は0.87」のように信頼度(0〜1の数字)を付けます。1に近いほど自信がある、という意味です。

    しきい値は、「この数字以上のものだけを結果として採用する」という線引きです。

    • しきい値を高くする:確かなものだけ残る。誤検出(違うものを見つける)は減るが、見逃し(写っているのに見つからない)が増える
    • しきい値を低くする:見逃しは減るが、誤検出が増える

    どちらが正しいということはなく、業務で「見逃しと誤検出のどちらがより困るか」で決めます。

    実装

    1. 結果を整える app/results.py

    結果を信頼度の高い順に並べ、辞書・JSONに変える関数を用意します。

    # app/results.py(抜粋)
    def sort_by_confidence(detections: list[Detection]) -> list[Detection]:
        """信頼度の高い順に並べる(同じ信頼度なら左上にあるものを先に)。"""
        return sorted(detections, key=lambda d: (-d.confidence, d.box[1], d.box[0]))
    
    
    def to_dict(image_path: str | Path, confidence: float, detections: list[Detection]) -> dict[str, object]:
        """画像1枚分の結果を、JSONにできる辞書にする。"""
        return {
            "image": Path(image_path).name,
            "confidence_threshold": confidence,
            "count": len(detections),
            "detections": [
                {"label": d.label, "confidence": round(d.confidence, 3), "box": list(d.box)}
                for d in sort_by_confidence(detections)
            ],
        }
    
    
    def to_json(image_path: str | Path, confidence: float, detections: list[Detection]) -> str:
        """日本語をそのまま読める形の JSON 文字列にする。"""
        return json.dumps(to_dict(image_path, confidence, detections), ensure_ascii=False, indent=2)
    • ensure_ascii=False:日本語のファイル名などを、文字化けした記号(写...)ではなくそのまま出します
    • 画像のパスは、ファイル名だけを出します。フォルダ名に社内の情報が含まれても、結果に残さないためです

    2. しきい値ごとの件数を数える関数

    しきい値を変えるたびにAIを再実行すると時間がかかります。そこで、いちばん低いしきい値で1回だけ認識し、あとから絞り込む方法にします。

    # app/results.py(抜粋)
    def counts_by_threshold(detections: list[Detection], thresholds: list[float]) -> dict[float, int]:
        """しきい値ごとに「何件残るか」を数える。低いしきい値で検出した結果を、後から絞り込む前提。"""
        return {t: sum(1 for d in detections if d.confidence >= t) for t in thresholds}

    3. コマンドに --json を追加

    app/main.py に、JSON出力の選択肢を足します。

    # app/main.py(変更部分)
    parser.add_argument("--json", action="store_true", help="結果を JSON で出力する(ほかのプログラムに渡す用)")
    args = parser.parse_args(argv)
    
    detections = Detector(confidence=args.conf).detect(args.image)
    if args.json:
        print(to_json(args.image, args.conf, detections))
        return 0
    print(f"{len(detections)} 件見つかりました(しきい値 {args.conf})")
    for d in sort_by_confidence(detections):
        print(f"- {d.label} 信頼度 {d.confidence:.2f} 位置 {d.box}")

    4. しきい値を比べるスクリプト

    # tools/conf_sweep.py(抜粋)
    THRESHOLDS = [0.10, 0.25, 0.50, 0.75, 0.90]
    
    
    def main(image: str) -> None:
        # いちばん低いしきい値で一度だけ推論し、あとは件数を数え直す(推論を何度も走らせない)
        detections = Detector(confidence=min(THRESHOLDS)).detect(image)
        print(f"{image}: しきい値ごとの件数")
        print("しきい値  件数")
        for threshold, count in counts_by_threshold(detections, THRESHOLDS).items():
            print(f"{threshold:>6.2f}  {count:>4}")

    動かして確かめる

    しきい値ごとの件数

    python tools/conf_sweep.py samples/fruits.jpg

    執筆時の開発環境での結果です。

    samples/fruits.jpg: しきい値ごとの件数
    しきい値  件数
      0.10     8
      0.25     4
      0.50     2
      0.75     1
      0.90     0

    同じ写真でも、しきい値が0.10なら8件、0.90なら0件です。しきい値の決め方だけで、結果が大きく変わることが分かります。

    しきい値0.10で増える結果の中身

    しきい値を0.10にして、増えた分の中身を見てみます。

    python -m app.main samples/fruits.jpg --conf 0.1
    8 件見つかりました(しきい値 0.1)
    - orange 信頼度 0.87 位置 (317, 247, 512, 477)
    - orange 信頼度 0.72 位置 (69, 44, 348, 470)
    - orange 信頼度 0.47 位置 (0, 277, 130, 477)
    - orange 信頼度 0.40 位置 (321, 121, 512, 305)
    - orange 信頼度 0.21 位置 (0, 95, 146, 241)
    - orange 信頼度 0.19 位置 (0, 1, 158, 105)
    - dining table 信頼度 0.13 位置 (5, 3, 512, 480)
    - orange 信頼度 0.10 位置 (0, 230, 76, 294)

    増えた4件の枠を写真に重ねて確認すると、次のようでした(枠の位置と写真を照らし合わせて、人が判断しています)。

    • 信頼度0.19(左上):写真の左上に写っている本物のオレンジで、しきい値0.25だと見逃していたもの
    • 信頼度0.21(左の中ほど):バナナの上側を「orange」と誤って答えている
    • 信頼度0.13:写真全体を「dining table(食卓)」と答えている。果物とは無関係な誤検出
    • 信頼度0.10(左の下寄り):これもバナナの下側で、「orange」の誤検出

    つまり、しきい値を下げると本物の見逃しは減るが、誤検出も増える、という関係が実際の写真で確認できました。

    JSON出力

    python -m app.main samples/fruits.jpg --json
    {
      "image": "fruits.jpg",
      "confidence_threshold": 0.25,
      "count": 4,
      "detections": [
        {
          "label": "orange",
          "confidence": 0.867,
          "box": [317, 247, 512, 477]
        }
      ]
    }

    (実際の出力は4件分が続きます。紙面の都合で1件に短縮しています)

    このJSONは、次の回以降で画像に描く処理や、集計・APIで再利用します。

    テスト

    pytest -q      # 8件成功
    ruff check .   # 問題なし

    結果の並び替え・JSON化・しきい値ごとの件数は、モデルを読み込まなくても確かめられるので、テストが速く済みます。

    つまずきやすい点

    • しきい値を「精度」と混同しない:しきい値0.25は「25%の確率で正しい」という意味ではありません。信頼度は、モデルの内部の目安で、そのまま正解率を表すわけではありません(正しく測る方法は第6回で扱います)
    • しきい値は1つの正解がない:対象物や写真の撮り方が変わると、ちょうどよい値も変わります。実際の業務の写真で決めます
    • JSONの box の順序:左・上・右・下の順です。開発会社ごとに「左上と幅・高さ」形式などがあるため、連携するときは形式を確認します

    発注者向けメモ:「精度○%」の前に決めること

    画像認識の相談では、「精度は何%出ますか」と聞かれがちですが、しきい値の決め方だけで結果が変わるように、「精度」という1つの数字だけでは判断できません。

    発注者がやること チェックリスト

    • ☐ 見逃しと誤検出のどちらがより困るかを、業務の言葉で決めた(例:不良品の見逃しは許されないが、誤検出は人が確認すればよい)
    • ☐ 誤検出・見逃しが起きたときに、人が確認・修正する手順を決めた
    • ☐ 判定結果を、どのシステムにどんな形(JSON・CSV等)で渡すかを決めた
    • ☐ 実際の現場写真で、しきい値の違いによる結果を見比べた

    開発会社への質問例

    • 「しきい値は、どの写真を使って、どう決めますか。見逃しと誤検出のどちらを優先する設定ですか」
    • 「精度の数字を出す場合、どのテスト画像で、何枚で測りますか」
    • 「信頼度が低い結果は、自動で採用せず、人の確認に回す設計にできますか」
    • 「結果を当社の既存システムに渡すときのデータ形式(項目・座標の表し方)は、どう決めますか」

    まとめと次回予告

    第1回では、次のことを行いました。

    • 信頼度としきい値の関係を、実際の写真で確かめた(0.10で8件、0.90で0件)
    • しきい値を下げると、見逃しは減るが誤検出が増えることを確認した
    • 結果を信頼度順に並べ、JSONで出力できるようにした
    • しきい値ごとの件数を比べるスクリプトを作った

    次回の第2回では、認識した位置を画像に枠とラベルで描いて、目で確認できるようにします。

    この連載の記事一覧

    この記事は連載「YOLOで作る画像認識ツール」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      目次