Ahogrammer

Deep Dive Into NLP, ML and Cloud

ゼロショットで画像を分類し、種類ごとにLLMでテキスト化・構造化する

RAGで使われる検索システムでは、テキストデータを対象とすることが多いですが、実際の文書には画像として重要な情報が含まれることも多くあります。これを踏まえ、LLMを用いて画像をテキスト化し検索できるようにする試みがありますが、人物写真や背景画像など本文との関連性が低い画像まで対象にするとノイズとなる課題がありました。以下、パワポなどに含まれる関連性の低い画像の例です。

UnsplashMapboxが撮影した写真

そこで、本記事ではドメインに関連する画像(今回はグラフとワークフローとする)だけを選別するために、ラベル付きデータがなくても活用可能なゼロショット画像分類を導入し、分類後にLLMによってテキスト化・構造化するパイプラインを試してみます。全体像は次の図のとおりです。

全体像

ゼロショット画像分類

画像の分類をする方法はいくつかありますが、ここではゼロショット画像分類を使います。ゼロショット画像分類は、事前学習したモデルを使って、ラベル付きデータがない状態で画像を分類する方法です。そのための方法としてはいくつか考えられますが、今回は画像とラベル候補の埋め込みを計算し、それらの類似度を計算してもっとも近いラベルを選びます。

ゼロショット画像分類。画像と各ラベル候補の類似度を計算して、出力を決める

画像とラベル候補の埋め込みをもとに類似度を計算するためには、マルチモーダル埋め込みモデルを用いて、双方を同じ空間に埋め込む必要があります。画像とテキストに対応したマルチモーダル埋め込みモデルは、訓練の過程で画像とテキストを同じ空間に埋め込むことを学習しています。これにより、画像とテキストの類似度を計算できます。そのようなモデルとしてはCLIPが有名ですが、今回はGoogleが公開しているSigLip2[1]を使います。

マルチモーダル埋め込みモデル

分類のコードは次のとおりです。ゼロショット画像分類をするために、TransformersのZeroShotImageClassificationPipeline[2][3]を使用しています。このパイプラインの内部では、画像とラベル候補の埋め込みを計算し、コサイン類似度を計算しています。正確には、ラベル候補のテキストがそのまま使われるわけではなく、"This is a photo of {ラベル候補}"という形式に変換したうえで、埋め込みを計算しています。

from PIL import Image
from transformers import pipeline

# ゼロショット分類パイプラインの読み込み
image_classifier = pipeline(
    task="zero-shot-image-classification",
    model="google/siglip2-so400m-patch16-384",
)

# 画像の読み込み
image_file = "image.png"
image = Image.open(image_file)

# ラベル候補の定義
candidate_labels = [
    "bar chart",         # 棒グラフ
    "line chart",        # 折れ線グラフ
    "workflow diagram",  # ワークフロー
    "human",             # 人間
    "snapshot",          # スクリーンショット
    "pie chart",         # 円グラフ
    "scatter plot",      # 散布図
]
# ゼロショット画像分類の実行
outputs = image_classifier(image, candidate_labels=candidate_labels)
outputs = [
    {
        "score": round(output["score"], 4),
        "label": output["label"],
    } for output in outputs
]

4つの画像を分類した結果を次の表に示します。画像としては棒グラフ、折れ線グラフ、ワークフロー、そして人間の画像を用意しました。予測結果としては、もっともスコアの高いラベル候補を選んでいます。結果を見ると、すべての画像が正しく分類されていることがわかります。実際に分類に利用する場合には、さらに多様な画像を用意し、ラベル候補名やプレフィックスとして使う文言を調整し、テストする必要があります。

画像 種類 予測

[4]より引用
棒グラフ bar chart

[4]より引用
折れ線グラフ line chart
ワークフロー workflow diagram
人間 human

グラフとワークフローのテキスト化

画像の分類ができたので、次はグラフとワークフローのテキスト化を行います。テキスト化にはマルチモーダルなLLMを使用します。ここでは、OpenAIのGPT-4oを使用します。GPT-4oは、画像とテキストの両方を入力として受け取り、テキストを生成できます。

棒グラフ

まずは、次の棒グラフからデータを抽出して表にしてみます。

プロンプトは次のとおりです。

次の画像は積み上げ棒グラフです。この画像を読み取り、棒グラフの各カテゴリとそれぞれの内訳(スタックされた各セグメントのラベルと値)を抽出し、Markdown形式の表にしてください。

  • 表の1列目はカテゴリ名(棒グラフの横軸ラベル)としてください。
  • 2列目以降には、それぞれのスタックセグメントのラベルを列名とし、対応する値を記載してください。
  • 単位がある場合は読み取って明記してください。
  • 不明な値や読み取れなかったラベルは「N/A」と記入してください。

表の形式は次のようにしてください:

カテゴリ ラベルA ラベルB ラベルC
カテゴリ1 10 15 5
カテゴリ2 8 20 7

画像を正確に読み取れない場合は、読み取れた部分のみを表にしてください。

結果は次のようになります。目で確認した限りでは、正しく抽出できています。

年度 政府・地方公共団体 信託銀行 生・損保・その他金融 都銀・地銀等 事業法人等 証券会社 個人・その他 外国法人等
2018 0.2 21.5 5.0 3.1 21.7 2.3 17.2 29.1
2019 0.1 21.7 4.8 2.9 22.3 2.0 16.5 29.6
2020 0.1 22.5 4.7 2.7 20.4 2.5 16.8 30.2
2021 0.2 22.9 4.7 2.5 20.0 2.7 16.6 30.4
2022 0.2 22.6 4.6 2.3 19.6 2.9 17.6 30.1
2023 0.2 22.1 4.6 2.1 19.3 3.0 16.9 31.8

折れ線グラフ

次は、折れ線グラフからデータを抽出して表にしてみます。

プロンプトは次のとおりです。

次の画像には折れ線グラフが描かれています。これをステップバイステップで読み取り、Markdown形式の表に変換してください。

ステップ 1: 横軸(X軸)の目盛りを特定

  • 例:年(2020, 2021, 2022...)や月(Jan, Feb...)など
  • 数値やラベルが判読できない場合は「N/A」としてください

ステップ 2: 各線の凡例(系列名)を抽出

  • 線ごとに色やマーカーで示された凡例名を確認
  • 判別できない場合は仮に「Series 1」「Series 2」などと記載し、補足でその旨を記述

ステップ 3: 各線のデータポイントを読み取る

  • X軸の目盛りごとに、各系列のY軸の値を読み取る
  • 線が交差・途切れていて正確な値が読み取れない場合は「N/A」
  • 可能であれば、線形補完で近似値を推定しても構いません(その場合は注釈を記載)

ステップ 4: Markdown形式で表を作成

以下の形式に従って表を出力してください:

X軸ラベル 系列Aの名前 系列Bの名前 系列Cの名前
ラベル1
ラベル2
... ... ... ...

ステップ 5: 単位・補足情報を明記(必要な場合)

  • Y軸の単位(例:万円、人、%)がある場合は表の下に注釈として記載
  • 不明点や推測した点がある場合も記載してください

結果は次のようになります(表だけ抜粋)。一見すると、それっぽい数字が並んでいるのですが、よく見ると正しくない数値であることがわかります。プロンプトでは段階を踏んで読み取るように指示していますが、それでも正確に読み取るのは難しいようです。正確な値を取得したい場合は、グラフから数値を読み取るためのプログラムを作成したほうが良さそうです。

年度 個人・その他 都銀・地銀等、生・損保等 事業法人等 外国法人等 信託銀行
1970 32 39 29 5 N/A
1975 37 30 28 4 N/A
1980 38 27 30 8 N/A
1985 37 22 30 11 9
1990 34 20 28 6 10
1995 30 20 27 6 14
2000 27 20 26 17 16
2005 18 17 25 26 17
2010 12 17 24 28 20
2015 9 17 23 30 22
2020 7 16 21 31 23

正確な数値を表にすることはできませんでしたが、折れ線グラフからはトレンドを読み取りたいことが多いと考えられるため、そのためのプロンプトを作成しました。トレンドを読み取るためのプロンプトは次のとおりです。

次の折れ線グラフ画像を読み取り、各系列の全体的な傾向(トレンド)を簡潔に記述してください。

出力フォーマット(必須)

各系列ごとに、以下の形式で記述してください:

  • 系列名(凡例): 上昇傾向/下降傾向/横ばい/変動が大きい など
  • 補足説明:いつ頃から増加/減少したか、急激な変化があった時期などがあれば簡単に記載

観点(参考)

  1. 全体として増加傾向か、減少傾向か、横ばいか
  2. 急激な変化(急上昇・急降下)はいつ発生したか
  3. 他の系列と比べて特徴的な動きがあるか

制約事項

  • 数値やグラフの詳細な読み取りは不要。視覚的な傾向のみを記述してください。
  • 系列名が判別できない場合は、「Series 1」などの仮名で記述してください。

画像を以下に添付します。

結果は次のようになりました。年度に関する記載にあやしいところもありますが、全体的な傾向は正しく読み取れているようです。

折れ線グラフのトレンド分析(図4:主要投資部門別株式保有比率の推移)

  • 個人・その他(赤線): 下降傾向

    • 1970年頃は30%超だったが、以降徐々に減少。1990年代以降は20%前後で横ばい気味。
  • 事業法人等(黄線): ゆるやかな下降傾向

    • 1970年から2000年代まで横ばい〜微減。2000年以降は緩やかに減少傾向。
  • 都銀・地銀等、生・損保、その他金融(緑線): 急激な下降傾向

    • 1990年代終盤までは30%台を維持していたが、2000年を境に急降下し、2020年には10%未満に。
  • 外国法人等(青線): 上昇傾向

    • 1980年代後半から増加を開始し、2000年以降に急上昇。以降も30%前後で高い水準を維持。
  • 信託銀行(桃線): 上昇傾向

    • 1990年頃からゆるやかに上昇し始め、2000年代以降は20%前後で安定している。

ワークフロー

次は、ワークフローからデータを抽出して表にしてみます。社内の文書には、このようなワークフロー図が含まれていることも多いのではないでしょうか?

プロンプトは次のとおりです。

以下の画像には、ある業務のワークフロー図が示されています。図中の要素(ステップ、矢印、条件分岐など)を漏れなく抽出し、以下のフォーマットでテキスト化してください。

  1. 全体概要
    図全体が何を表しているのか、簡潔に1–2文でまとめる。

  2. ステップ一覧

  3. ステップ番号: 図中の順序を示す番号(例:Step 1, Step 2…)
  4. 名称: 図に書かれたラベル(例:データ収集、承認依頼など)
  5. 説明: ステップの意味や処理内容を補足。図に詳細が書かれていない場合は推測でなく「省略」または「不明」と記載。

  6. 遷移・矢印

  7. 起点→終点: どのステップからどのステップへつながっているか(例:Step 1 → Step 2)
  8. 分岐条件: 条件付きの矢印があれば、その条件(例:承認あり/承認なし)

  9. 特記事項

  10. ループや並列処理、例外フローなど図上の特殊な要素
  11. 図中にある注釈やアイコンなど、ワークフロー理解に必要な情報

[出力例]

全体概要: この図は「新規ユーザー登録から承認完了までの業務フロー」を示しています。

ステップ一覧:

  • Step 1: ユーザー登録フォーム入力 説明: Web ページ上で必要情報を入力し、登録申請を行う。

  • Step 2: 登録情報のバリデーション 説明: システムが入力データをチェックし、形式エラーがないか確認する。

  • Step 3: 管理者への承認依頼 説明: バリデーション通過後、管理者に承認メールを自動送信する。

…(以降同様)

遷移・矢印:

  • Step 1 → Step 2
  • Step 2 → Step 3 (バリデーションOK)
  • Step 2 → Step 1 (エラー時)

特記事項:

  • Step 4〜6 は並列処理で、同時に実行可
  • 「エラー通知」アイコンは、メール送信に失敗した場合を示す

このフォーマットをそのまま活用して、画像内のワークフローを抜け漏れなくテキスト化してください。

結果は次のようになります。悪くない結果に見えますが、ステップや遷移をLLMにも理解しやすい形式言語で表しても良かったかもしれません。

全体概要

この図は「申請金額に応じて承認ルートが分岐する業務承認プロセス」を示しています。主に、10万円未満と10万円超で承認ルートが異なる点が特徴です。

ステップ一覧

  • Step 1: 開始 説明: 担当者が処理を開始するステップ。

  • Step 2: 起票 説明: 担当者が申請書を起票(作成)する。

  • Step 3: 所属長承認中(10万円未満の場合) 説明: 所属長が内容を確認し、承認する。

  • Step 4: 所属長確認中(10万円超の場合) 説明: 所属長が金額を確認する。

  • Step 5: 部長承認中 説明: 所属長の確認後、部長が承認する。

  • Step 6: 承認完了(いずれかの承認が通過した後) 説明: 最終承認後、申請処理が完了する。

遷移・矢印

  • Step 1 → Step 2

  • Step 2 → Step 3 分岐条件: 金額が10万円未満の場合

  • Step 2 → Step 4 分岐条件: 金額が10万円超の場合

  • Step 4 → Step 5 分岐条件: 所属長確認後に部長承認依頼

  • Step 3 → Step 6 分岐条件: 所属長が承認した場合(10万円未満)

  • Step 5 → Step 6 分岐条件: 部長が承認した場合(10万円超)

特記事項

  • 金額が「10万円未満」か「10万円超」かで承認経路が分岐する条件分岐フロー。
  • 所属長が確認し、部長に承認依頼を行うルートでは「確認」→「承認」という段階的なプロセスを踏む。
  • 最終的な「完了」ステップは、どのルートでも承認が得られれば到達する。
  • ステータスラベル(例: 未処理、承認中、確認中など)が各ステップに付与されており、現在の進捗を明確に表している。

まとめ

ゼロショット画像分類を用いて、グラフやワークフローの画像を分類し、さらにLLMを用いてテキスト化する方法を紹介しました。今回は画像分類とテキスト化の2段階で処理しましたが、マルチモーダルなLLMを用いることで、画像を直接テキスト化することも可能です。その場合は、画像分類のエラーが伝播しないというメリットがありますが、入力トークン数が増えるというデメリットがあります。出力されるテキストの質や処理速度、コストを考慮して、どういった方法が良いかを検討する必要がありそうです。

また、今回は関連性の高い画像としてグラフとワークフローを選びましたが、実際にはドメインだけでなく文書の文脈に依存するので、それほど単純な話でもないでしょう。そのため、テキスト化した後に、周辺のテキストとの類似度で判断するような仕組みを用意してもいいかもしれません。

参考文献

  1. google/siglip2-so400m-patch16-384
  2. Zero-shot image classification
  3. class transformers.ZeroShotImageClassificationPipeline
  4. 2023年度株式分布状況調査の調査結果について