LESSON

演習:Transformerモデルを活用しよう

田中VPoE:「Attention、Transformer、BERT、GPT と一通り学んだ。ここでは Hugging Face を使って、NetShop のレビュー感情分析を実際に構築してみよう。」

あなた:「BERT の Fine-tuning で、レビューのポジティブ/ネガティブを判定するんですね。」

田中VPoE:「そうだ。さらに、Attention の可視化でモデルがどこに注目しているかも確認してみよう。ブラックボックスの中身を少しでも理解することが大切だ。」

ミッション概要

Hugging Face Transformers を使って、レビュー感情分析モデルを構築し、Transformer の実践的な活用方法を体験します。


Mission 1: Hugging Face pipeline でテキスト分類を試す

Hugging Face の pipeline API を使って、事前学習済みモデルによるテキスト分類を体験してください。

要件

  1. pipeline でテキスト分類パイプラインを作成する
  2. NetShop のレビュー文を5件以上入力し、感情分析結果を確認する
  3. 日本語レビューも試して、多言語対応の可能性を確認する
from transformers import pipeline

# ここに実装を書く
解答例
from transformers import pipeline

# 感情分析パイプライン
classifier = pipeline("sentiment-analysis")

# 英語レビュー
reviews_en = [
    "This product is amazing! Best purchase ever.",
    "Terrible quality. Broke after one week.",
    "Delivery was slow but the product is decent.",
    "Excellent customer service and fast shipping.",
    "Not worth the price. Very disappointed.",
]

print("=== 英語レビューの感情分析 ===")
for review in reviews_en:
    result = classifier(review)[0]
    print(f"  [{result['label']:>8s}] ({result['score']:.3f}) {review[:50]}")

# 日本語レビュー(多言語モデル)
classifier_multi = pipeline(
    "sentiment-analysis",
    model="nlptown/bert-base-multilingual-uncased-sentiment"
)

reviews_ja = [
    "この商品は品質が素晴らしいです。大満足!",
    "配送が遅すぎます。二度と利用しません。",
    "普通の商品です。可もなく不可もなく。",
    "コスパ最高!友達にも勧めたい。",
    "説明と違う商品が届きました。返品します。",
]

print("\n=== 日本語レビューの感情分析 ===")
for review in reviews_ja:
    result = classifier_multi(review)[0]
    print(f"  [{result['label']:>10s}] ({result['score']:.3f}) {review}")

Mission 2: BERT で感情分類モデルを Fine-tuning する

IMDb データセット(映画レビュー)を使って、BERT の Fine-tuning を行ってください。

要件

  1. IMDb データセットのサブセット(各クラス500件)を用意する
  2. DistilBERT をベースに分類モデルを構築する
  3. 3エポックの Fine-tuning を実施する
  4. テストデータで精度を評価する
from transformers import (
    DistilBertForSequenceClassification,
    DistilBertTokenizer,
    TrainingArguments,
    Trainer
)
from datasets import load_dataset

# ここに実装を書く
解答例
from transformers import (
    DistilBertForSequenceClassification,
    DistilBertTokenizerFast,
    TrainingArguments,
    Trainer
)
from datasets import load_dataset
import numpy as np
from sklearn.metrics import accuracy_score, f1_score

# === データ準備 ===
dataset = load_dataset("imdb")

# サブセットを作成(学習効率のため)
train_dataset = dataset["train"].shuffle(seed=42).select(range(1000))
test_dataset = dataset["test"].shuffle(seed=42).select(range(500))

# === トークナイズ ===
tokenizer = DistilBertTokenizerFast.from_pretrained("distilbert-base-uncased")

def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        padding="max_length",
        truncation=True,
        max_length=256
    )

train_tokenized = train_dataset.map(tokenize_function, batched=True)
test_tokenized = test_dataset.map(tokenize_function, batched=True)

# === モデル準備 ===
model = DistilBertForSequenceClassification.from_pretrained(
    "distilbert-base-uncased",
    num_labels=2
)

# === 評価関数 ===
def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return {
        "accuracy": accuracy_score(labels, predictions),
        "f1": f1_score(labels, predictions, average="binary"),
    }

# === 学習設定 ===
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=32,
    learning_rate=2e-5,
    weight_decay=0.01,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
    logging_steps=50,
)

# === Trainer ===
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_tokenized,
    eval_dataset=test_tokenized,
    compute_metrics=compute_metrics,
)

# === 学習 ===
trainer.train()

# === 評価 ===
results = trainer.evaluate()
print(f"\n=== 最終評価 ===")
print(f"  Accuracy: {results['eval_accuracy']:.4f}")
print(f"  F1 Score: {results['eval_f1']:.4f}")

Mission 3: Attention の可視化

Fine-tuning したモデルの Attention 重みを可視化し、モデルがどのトークンに注目しているかを確認してください。

要件

  1. モデルから Attention 重みを取得する
  2. 入力テキストの各トークンに対する注目度をヒートマップで表示する
  3. ポジティブレビューとネガティブレビューで注目パターンが異なるか確認する
import matplotlib.pyplot as plt
import seaborn as sns

# ここに Attention 可視化のコードを書く
解答例
import torch
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

def visualize_attention(text, model, tokenizer):
    """Attention 重みを可視化"""
    inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=64)

    model.eval()
    with torch.no_grad():
        outputs = model(**inputs, output_attentions=True)

    # 予測結果
    logits = outputs.logits
    predicted = torch.argmax(logits, dim=-1).item()
    probs = torch.softmax(logits, dim=-1)
    label = "Positive" if predicted == 1 else "Negative"
    confidence = probs[0][predicted].item()

    # Attention 重み(最終層の平均)
    attention = outputs.attentions[-1]  # 最終層
    attention_avg = attention.mean(dim=1).squeeze()  # ヘッドの平均

    # トークンに変換
    tokens = tokenizer.convert_ids_to_tokens(inputs["input_ids"][0])

    # [CLS] トークンの注目度を表示
    cls_attention = attention_avg[0].numpy()

    # 可視化
    fig, ax = plt.subplots(figsize=(12, 3))
    display_tokens = tokens[:20]  # 最初の20トークンのみ表示
    display_attn = cls_attention[:20]

    bars = ax.bar(range(len(display_tokens)), display_attn)
    ax.set_xticks(range(len(display_tokens)))
    ax.set_xticklabels(display_tokens, rotation=45, ha='right')
    ax.set_title(f"Prediction: {label} ({confidence:.2%})")
    ax.set_ylabel("Attention Weight")

    # 上位3トークンを赤色にハイライト
    top_indices = np.argsort(display_attn)[-3:]
    for idx in top_indices:
        bars[idx].set_color('red')

    plt.tight_layout()
    plt.show()

# テスト
reviews = [
    "This product is absolutely fantastic and high quality!",
    "Terrible experience. The product broke immediately.",
    "Shipping was slow but the item itself is pretty good.",
]

for review in reviews:
    visualize_attention(review, model, tokenizer)
    print()

達成度チェック

  • Hugging Face pipeline で感情分析を実行できた
  • DistilBERT の Fine-tuning を実施し、精度を評価できた
  • Attention 重みを可視化し、モデルの注目点を確認できた
  • ポジティブ/ネガティブレビューでの注目パターンの違いを観察した

推定所要時間: 90分