BLIP-2 による Visual Question Answering(ソースコードと実行結果)

Python開発環境,ライブラリ類
ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。
Python 3.12 のインストール
インストール済みの場合は実行不要。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
【関連する外部ページ】
Python の公式ページ: https://www.python.org/
AI エディタ Windsurf のインストール
Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。
winget install --scope machine Codeium.Windsurf -e --silent
【関連する外部ページ】
Windsurf の公式ページ: https://windsurf.com/
必要なライブラリのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する
pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install transformers pillow opencv-python
BLIP-2 による Visual Question Answering プログラム
概要
このプログラムは、画像の視覚的内容を理解し、それに基づいて自然言語(英語)による質問に適切に回答する[1]。
主要技術
- BLIP-2(Bootstrapping Language-Image Pre-training)
事前学習済みの画像エンコーダと大規模言語モデルを凍結した状態で、軽量なQuerying Transformer(Q-Former)のみを訓練することで視覚と言語のモダリティ結合を実現する。Q-Formerは188Mパラメータという少ない学習可能パラメータで視覚情報と言語情報の橋渡しを果たす[1]。
- Transformersライブラリによるマルチモーダル処理
テキスト、コンピュータビジョン、音声、動画、マルチモーダルモデルのための機械学習モデルのフレームワークとして機能し、事前学習済みモデルの推論と訓練の両方をサポートする[2]。
主要技術
[1] Li, J., Li, D., Savarese, S., & Hoi, S. (2023). BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models. arXiv preprint arXiv:2301.12597.
[2] Wolf, T., Debut, L., Sanh, V., Chaumond, J., Delangue, C., Moi, A., ... & Rush, A. M. (2020). Transformers: State-of-the-art natural language processing. In Proceedings of the 2020 conference on empirical methods in natural language processing: system demonstrations (pp. 38-45).
# BLIP-2 による Visual Question Answering プログラム
# 特徴技術名: BLIP-2 (Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models)
# 出典: Li, J., Li, D., Savarese, S., & Hoi, S. (2023). BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models. arXiv preprint arXiv:2301.12597.
# 特徴機能: 軽量Querying Transformer(Q-Former)による効率的な視覚言語間のモダリティ結合。事前学習済みの画像エンコーダ(CLIP-like)と大規模言語モデル(OPT-2.7B)を完全に凍結した状態で、Q-Formerのみを2段階で事前学習することで、大幅に少ない学習可能パラメータで最先端性能を実現する。
# 学習済みモデル: Salesforce/blip2-opt-2.7b - OPT-2.7Bを凍結言語モデル、CLIP-like画像エンコーダを凍結視覚エンコーダとして使用したマルチモーダルモデル。VQAv2でゼロショット性能、画像キャプション生成、視覚的質問応答に対応、メモリ効率的な推論が可能、https://huggingface.co/Salesforce/blip2-opt-2.7b
# 方式設計:
# 関連利用技術: transformers(HuggingFace提供のTransformerモデルライブラリ)、PIL(画像処理ライブラリ)、OpenCV(コンピュータビジョンライブラリ)、tkinter(GUI操作)、urllib(HTTP通信)
# 入力と出力: 入力: 静止画像(ユーザは「0:画像ファイル,1:カメラ,2:サンプル画像」のメニューで選択.0:画像ファイルの場合はtkinterで複数ファイル選択可能.1の場合はOpenCVでカメラが開き,スペースキーで撮影(複数回可能).2の場合はhttps://github.com/opencv/opencv/raw/master/samples/data/fruits.jpg とhttps://github.com/opencv/opencv/raw/master/samples/data/messi5.jpgとhttps://github.com/opencv/opencv/raw/master/samples/data/aero3.jpgを使用)
# 出力: 処理結果が画像化できる場合にはOpenCV画面でリアルタイムに表示.OpenCV画面内に処理結果(質問と回答)をテキストで表示.1秒間隔で,print()で処理結果を表示.プログラム終了時にprint()で表示した処理結果をresult.txtファイルに保存し,「result.txtに保存」したことをprint()で表示.プログラム開始時に,プログラムの概要,ユーザが行う必要がある操作(英語で質問入力)をprint()で表示.
# 処理手順: 1.BLIP-2モデル(blip2-opt-2.7b)とプロセッサの読み込み,2.入力画像の前処理(BGRからRGBへの変換,PIL形式への変換),3.ユーザから英語で質問を受け付け,4.Q-Formerによる画像特徴の抽出と質問のエンコード,5.凍結されたOPT-2.7B言語モデルによる回答生成,6.生成された回答のデコードと表示
# 前処理、後処理: 前処理: 画像のリサイズ(224x224)、RGB正規化、テンソル変換、後処理: 生成トークンのデコード、特殊トークン除去、信頼度に基づく回答品質評価
# 追加処理: 画像品質チェック(入力画像の解像度とアスペクト比確認による推論精度向上)、回答一貫性確保(同一画像に対する複数回推論結果の統合処理)
# 調整を必要とする設定値: max_new_tokens(生成する最大トークン数、デフォルト50)、do_sample(サンプリング有無、True/False)
# 将来方策: max_new_tokensの動的調整機能(質問の複雑さに応じて、簡単な質問には短い回答(20トークン)、詳細な説明が必要な質問には長い回答(100トークン)を自動設定する機能をプログラム内で実装可能)
# その他の重要事項: GPU利用推奨(モデルサイズ約5.5GB),初回実行時のモデルダウンロード時間への配慮が必要,質問は英語で入力する必要がある
# 前準備: pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# pip install transformers pillow opencv-python
import cv2
import tkinter as tk
from tkinter import filedialog
import urllib.request
import os
from transformers import Blip2Processor, Blip2ForConditionalGeneration
from PIL import Image
import torch
import platform
# 設定値
MAX_NEW_TOKENS = 50 # VQA回答の最大トークン数
FONT_SCALE = 0.6 # OpenCV表示用フォントサイズ
FONT_THICKNESS = 1 # フォント太さ
TEXT_COLOR_WHITE = (255, 255, 255) # 質問テキスト色
TEXT_COLOR_GREEN = (0, 255, 0) # 回答テキスト色
TEXT_POS_Q = (10, 30) # 質問表示位置
TEXT_POS_A = (10, 60) # 回答表示位置
MODEL_NAME = 'Salesforce/blip2-opt-2.7b' # 使用モデル名
SAMPLE_URLS = [ # サンプル画像URL
'https://github.com/opencv/opencv/raw/master/samples/data/fruits.jpg',
'https://github.com/opencv/opencv/raw/master/samples/data/messi5.jpg',
'https://github.com/opencv/opencv/raw/master/samples/data/aero3.jpg'
]
def load_model():
try:
print('BLIP-2モデルを読み込み中...')
processor = Blip2Processor.from_pretrained(MODEL_NAME)
# GPU/CPU自動選択
device = 'cuda' if torch.cuda.is_available() else 'cpu'
dtype = torch.float16 if device == 'cuda' else torch.float32
if device == 'cuda':
model = Blip2ForConditionalGeneration.from_pretrained(
MODEL_NAME, torch_dtype=dtype, device_map='auto'
)
print('GPU使用でモデルを読み込みました')
else:
model = Blip2ForConditionalGeneration.from_pretrained(
MODEL_NAME, torch_dtype=dtype
)
print('CPU使用でモデルを読み込みました')
return processor, model, device
except Exception as e:
print(f'モデルの読み込みに失敗しました: {e}')
exit()
def generate_answer(img, question, processor, model, device):
# VQA推論処理
pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
prompt = f'Question: {question} Answer:'
inputs = processor(pil_img, text=prompt, return_tensors='pt')
if device == 'cuda':
inputs = inputs.to(device, torch.float16)
with torch.no_grad():
ids = model.generate(**inputs, max_new_tokens=MAX_NEW_TOKENS)
text = processor.batch_decode(ids, skip_special_tokens=True)[0].strip()
return text.split('Answer:')[-1].strip()
def process_image(img, processor, model, device, results):
if img is None:
print('画像の読み込みに失敗しました')
return
cv2.imshow('Image', img)
cv2.waitKey(1)
while True:
question = input('質問を入力してください(英語、quitで終了): ')
if question.lower() == 'quit':
break
try:
answer = generate_answer(img, question, processor, model, device)
result = f'Q: {question} A: {answer}'
results.append(result)
print(f'回答: {answer}')
# 結果を画像に重畳
img_show = img.copy()
cv2.putText(img_show, f'Q: {question}', TEXT_POS_Q,
cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE,
TEXT_COLOR_WHITE, FONT_THICKNESS)
cv2.putText(img_show, f'A: {answer}', TEXT_POS_A,
cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE,
TEXT_COLOR_GREEN, FONT_THICKNESS + 1)
cv2.imshow('Image', img_show)
cv2.waitKey(1)
except Exception as e:
print(f'VQA処理でエラーが発生しました: {e}')
# メイン処理
print('BLIP-2 Visual Question Answering プログラム')
print('このプログラムは画像に関する質問に答えます')
print('操作方法:')
print(' - 画像選択後、英語で質問を入力してください')
print(' - "quit"と入力すると次の画像に移動します')
print(' - カメラモードではスペースキーで撮影、qキーで終了')
print()
processor, model, device = load_model()
results = []
print('0: 画像ファイル')
print('1: カメラ')
print('2: サンプル画像')
choice = input('選択: ')
if choice == '0':
root = tk.Tk()
root.withdraw()
paths = filedialog.askopenfilenames()
if not paths:
exit()
for path in paths:
process_image(cv2.imread(path), processor, model, device, results)
elif choice == '1':
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) if platform.system() == 'Windows' else cv2.VideoCapture(0)
try:
print('カメラモード: スペースキーで撮影、qキーで終了')
while True:
cap.grab()
ret, frame = cap.retrieve()
if not ret:
break
cv2.imshow('Camera', frame)
key = cv2.waitKey(1) & 0xFF
if key == ord(' '):
process_image(frame, processor, model, device, results)
cv2.imshow('Camera', frame) # カメラ画面を再表示
elif key == ord('q'):
break
finally:
cap.release()
elif choice == '2':
files = []
for i, url in enumerate(SAMPLE_URLS):
fname = f'sample_{i}.jpg'
try:
urllib.request.urlretrieve(url, fname)
files.append(fname)
process_image(cv2.imread(fname), processor, model, device, results)
except Exception as e:
print(f'画像のダウンロードに失敗しました: {url}')
print(f'エラー: {e}')
# ダウンロードファイルの削除
for f in files:
try:
os.remove(f)
except OSError:
pass
else:
print('無効な選択です')
exit()
cv2.destroyAllWindows()
# 結果保存
if results:
with open('result.txt', 'w') as f:
for i, r in enumerate(results):
f.write(f'{i+1}: {r}\n')
print('result.txtに保存')