LESSON

画像分析ツールの実装

「エージェントの核となる画像分析ツールを実装しよう。」

田中VPoEが説明する。

「分類モデルの推論、Grad-CAMによる判断根拠の可視化、そしてVLMによる所見生成。この3つを一つのツールにまとめる。」

画像分類ツール

from langchain_core.tools import tool
import torch
from torchvision import transforms, models
from PIL import Image

class ImageClassifier:
    """画像分類器"""

    def __init__(self, model_path, class_names):
        self.model = models.resnet50(pretrained=False)
        self.model.fc = torch.nn.Linear(2048, len(class_names))
        self.model.load_state_dict(torch.load(model_path))
        self.model.eval()
        self.class_names = class_names

        self.transform = transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
        ])

    def predict(self, image_path):
        """画像を分類"""
        image = Image.open(image_path).convert('RGB')
        tensor = self.transform(image).unsqueeze(0)

        with torch.no_grad():
            outputs = self.model(tensor)
            probs = torch.softmax(outputs, dim=1)

        top_prob, top_idx = torch.max(probs, dim=1)

        return {
            'class': self.class_names[top_idx.item()],
            'confidence': round(top_prob.item(), 3),
            'all_probs': {
                name: round(probs[0][i].item(), 4)
                for i, name in enumerate(self.class_names)
            },
        }

@tool
def classify_image(image_path: str) -> dict:
    """画像を分類し、Grad-CAM付きの結果を返す"""
    result = classifier.predict(image_path)
    grad_cam_path = generate_grad_cam(image_path, classifier.model)
    result['grad_cam_path'] = grad_cam_path
    return result

Grad-CAM可視化

import numpy as np
import cv2

def generate_grad_cam(image_path, model, target_layer='layer4'):
    """Grad-CAMによる注目領域の可視化"""
    from pytorch_grad_cam import GradCAM
    from pytorch_grad_cam.utils.image import show_cam_on_image

    target_layers = [getattr(model, target_layer)[-1]]
    cam = GradCAM(model=model, target_layers=target_layers)

    image = Image.open(image_path).convert('RGB')
    transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])
    input_tensor = transform(image).unsqueeze(0)

    grayscale_cam = cam(input_tensor=input_tensor)
    grayscale_cam = grayscale_cam[0, :]

    # 元画像にヒートマップを重畳
    rgb_img = np.array(image.resize((224, 224))) / 255.0
    visualization = show_cam_on_image(rgb_img, grayscale_cam, use_rgb=True)

    output_path = image_path.replace('.jpg', '_gradcam.jpg')
    cv2.imwrite(output_path, cv2.cvtColor(visualization, cv2.COLOR_RGB2BGR))

    return output_path

VLMによる所見生成ツール

@tool
def generate_findings(image_path: str, classification: dict) -> str:
    """VLMで画像の所見を生成する"""
    import base64

    with open(image_path, "rb") as f:
        image_data = base64.b64encode(f.read()).decode()

    prompt = f"""
    この画像を分析し、以下の形式で所見を生成してください。

    分類結果: {classification['class']}(確信度: {classification['confidence']}

    ## 出力形式
    1. 観察所見: 画像に見られる特徴を客観的に記述
    2. 異常箇所: 異常が認められる場合、その位置と特徴
    3. 重症度評価: 軽度/中度/重度
    4. 追加検査: 推奨される追加検査や確認事項

    ※ 確信度が低い場合は明確に不確実性を記載してください。
    """

    from langchain_openai import ChatOpenAI
    llm = ChatOpenAI(model="gpt-4o", temperature=0)

    messages = [
        {"role": "user", "content": [
            {"type": "text", "text": prompt},
            {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}},
        ]}
    ]

    response = llm.invoke(messages)
    return response.content

まとめ

項目ポイント
画像分類ResNet + Softmax確率で分類
Grad-CAM判断根拠をヒートマップで可視化
VLM所見GPT-4oで構造化された所見を生成
統合分類結果をVLMのコンテキストとして活用

チェックリスト

  • 画像分類ツールを実装できる
  • Grad-CAMの生成と解釈ができる
  • VLMを使った所見生成を実装できる
  • 分類結果とVLM所見の統合方法を理解した

次のステップへ

画像分析ツールを実装した。次は所見生成の高度化を学ぼう。

推定読了時間: 30分