総合演習:深層学習プロジェクトレポート
田中VPoE:「Month 3 の集大成だ。ここまで学んだニューラルネットワーク、PyTorch、CNN、Transformer、最適化テクニックを総動員して、NetShop の2つの課題に取り組んでほしい。」
あなた:「商品画像分類とレビュー感情分析ですね。どちらも従来の機械学習では精度に限界があった課題です。」
田中VPoE:「その通り。最終的には、経営陣に提出するプロジェクトレポートの形でまとめてくれ。モデルの精度だけでなく、なぜその手法を選んだのか、どう改善したのかのプロセスも重要だ。」
ミッション概要
NetShop 社の2つの課題(画像分類・テキスト分類)に深層学習で取り組み、プロジェクトレポートとして成果をまとめます。
Mission 1: 商品画像分類
CIFAR-10 を NetShop の商品カテゴリに見立てて、最高精度の画像分類モデルを構築してください。
要件
- ベースライン CNN を構築し、精度を記録する
- 転移学習(ResNet-18)で精度を改善する
- データ拡張の効果を検証する
- 最適化テクニック(BatchNorm, Dropout, 学習率スケジューリング)を適用する
- 各段階の精度を記録し、改善履歴を作成する
import torch
import torch.nn as nn
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
# ここに画像分類モデルの実装を書く
解答例
import torch
import torch.nn as nn
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import numpy as np
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
improvement_log = []
# === データ準備 ===
basic_transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616))
])
aug_transform = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(10),
transforms.RandomResizedCrop(32, scale=(0.8, 1.0)),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616))
])
train_basic = datasets.CIFAR10('./data', train=True, download=True, transform=basic_transform)
train_aug = datasets.CIFAR10('./data', train=True, download=True, transform=aug_transform)
test_ds = datasets.CIFAR10('./data', train=False, download=True, transform=basic_transform)
train_loader = DataLoader(train_basic, batch_size=128, shuffle=True)
train_loader_aug = DataLoader(train_aug, batch_size=128, shuffle=True)
test_loader = DataLoader(test_ds, batch_size=256, shuffle=False)
def train_and_test(model, train_ldr, test_ldr, optimizer, scheduler=None, epochs=15):
criterion = nn.CrossEntropyLoss()
best_acc = 0
for epoch in range(epochs):
model.train()
for imgs, labels in train_ldr:
imgs, labels = imgs.to(device), labels.to(device)
optimizer.zero_grad()
loss = criterion(model(imgs), labels)
loss.backward()
optimizer.step()
if scheduler:
scheduler.step()
model.eval()
correct, total = 0, 0
with torch.no_grad():
for imgs, labels in test_ldr:
imgs, labels = imgs.to(device), labels.to(device)
_, pred = model(imgs).max(1)
total += labels.size(0)
correct += pred.eq(labels).sum().item()
acc = 100.0 * correct / total
best_acc = max(best_acc, acc)
return best_acc
# === Step 1: ベースライン CNN ===
class BaselineCNN(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(), nn.AdaptiveAvgPool2d(1),
)
self.fc = nn.Linear(128, 10)
def forward(self, x):
return self.fc(self.net(x).view(x.size(0), -1))
m = BaselineCNN().to(device)
acc = train_and_test(m, train_loader, test_loader, torch.optim.Adam(m.parameters(), lr=1e-3))
improvement_log.append(('ベースライン CNN', acc))
print(f"ベースライン CNN: {acc:.1f}%")
# === Step 2: +BatchNorm, Dropout ===
class ImprovedCNN(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(3, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
nn.Conv2d(32, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
nn.MaxPool2d(2), nn.Dropout2d(0.2),
nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
nn.Conv2d(64, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
nn.MaxPool2d(2), nn.Dropout2d(0.2),
nn.Conv2d(64, 128, 3, padding=1), nn.BatchNorm2d(128), nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
)
self.fc = nn.Sequential(nn.Dropout(0.3), nn.Linear(128, 10))
def forward(self, x):
return self.fc(self.net(x).view(x.size(0), -1))
m = ImprovedCNN().to(device)
acc = train_and_test(m, train_loader, test_loader, torch.optim.Adam(m.parameters(), lr=1e-3))
improvement_log.append(('+BatchNorm+Dropout', acc))
print(f"+BatchNorm+Dropout: {acc:.1f}%")
# === Step 3: +データ拡張 ===
m = ImprovedCNN().to(device)
acc = train_and_test(m, train_loader_aug, test_loader, torch.optim.Adam(m.parameters(), lr=1e-3))
improvement_log.append(('+データ拡張', acc))
print(f"+データ拡張: {acc:.1f}%")
# === Step 4: +学習率スケジューリング ===
m = ImprovedCNN().to(device)
opt = torch.optim.Adam(m.parameters(), lr=1e-3)
sch = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=15)
acc = train_and_test(m, train_loader_aug, test_loader, opt, sch, epochs=15)
improvement_log.append(('+LRスケジューリング', acc))
print(f"+LRスケジューリング: {acc:.1f}%")
# === 改善レポート ===
print("\n=== 画像分類 改善履歴 ===")
base = improvement_log[0][1]
for name, acc in improvement_log:
print(f" {name:<25s}: {acc:.1f}% (改善: {acc - base:+.1f}%)")
Mission 2: レビュー感情分析
Hugging Face を使って、レビュー感情分析モデルを構築してください。
要件
- Hugging Face の pipeline で事前学習済みモデルの性能を確認する
- IMDb データセットのサブセットで DistilBERT を Fine-tuning する
- テストデータで精度を評価する
- 具体的なレビュー文に対する予測結果を示す
from transformers import pipeline, DistilBertForSequenceClassification
from datasets import load_dataset
# ここに感情分析モデルの実装を書く
解答例
from transformers import (
DistilBertForSequenceClassification,
DistilBertTokenizerFast,
TrainingArguments,
Trainer,
pipeline
)
from datasets import load_dataset
import numpy as np
from sklearn.metrics import accuracy_score, f1_score, classification_report
# === Step 1: pipeline で確認 ===
print("=== 事前学習済みモデルの性能 ===")
clf = pipeline("sentiment-analysis")
test_reviews = [
("Great quality product, highly recommend!", "POSITIVE"),
("Terrible, broke after 2 days.", "NEGATIVE"),
("Average product, nothing special.", "NEUTRAL"),
("Fast delivery and excellent packaging!", "POSITIVE"),
("Worst purchase I ever made.", "NEGATIVE"),
]
for text, expected in test_reviews:
result = clf(text)[0]
print(f" [{result['label']:>8}] ({result['score']:.3f}) {text[:40]}... (期待: {expected})")
# === Step 2: Fine-tuning ===
dataset = load_dataset("imdb")
train_ds = dataset["train"].shuffle(seed=42).select(range(2000))
test_ds = dataset["test"].shuffle(seed=42).select(range(500))
tokenizer = DistilBertTokenizerFast.from_pretrained("distilbert-base-uncased")
def tokenize(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=256)
train_tok = train_ds.map(tokenize, batched=True)
test_tok = test_ds.map(tokenize, batched=True)
model = DistilBertForSequenceClassification.from_pretrained(
"distilbert-base-uncased", num_labels=2
)
def compute_metrics(eval_pred):
logits, labels = eval_pred
preds = np.argmax(logits, axis=-1)
return {"accuracy": accuracy_score(labels, preds), "f1": f1_score(labels, preds)}
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(
model=model, args=training_args,
train_dataset=train_tok, eval_dataset=test_tok,
compute_metrics=compute_metrics,
)
trainer.train()
# === Step 3: 評価 ===
results = trainer.evaluate()
print(f"\n=== Fine-tuning 結果 ===")
print(f" Accuracy: {results['eval_accuracy']:.4f}")
print(f" F1 Score: {results['eval_f1']:.4f}")
# === Step 4: 具体例での予測 ===
fine_tuned_clf = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
netshop_reviews = [
"This product exceeded my expectations! Amazing quality and fast delivery.",
"Complete waste of money. The item doesn't work at all.",
"Decent product for the price. Shipping was a bit slow though.",
"I love this! Already ordered two more for friends.",
"Very disappointed. The color is completely different from the photo.",
]
print("\n=== NetShop レビュー予測 ===")
for review in netshop_reviews:
result = fine_tuned_clf(review)[0]
label = "Positive" if result['label'] == 'LABEL_1' else "Negative"
print(f" [{label:>8}] ({result['score']:.3f}) {review[:60]}")
Mission 3: プロジェクトレポートの作成
以下のテンプレートに沿って、深層学習プロジェクトのレポートを出力してください。
要件
# レポートテンプレート
report = """
{'='*60}
NetShop 深層学習プロジェクトレポート
{'='*60}
【エグゼクティブサマリー】
Month 2 の機械学習では対応できなかった画像分類と
テキスト感情分析に深層学習で取り組んだ結果をまとめる。
【課題1: 商品画像分類】
手法: ???
最終精度: ???
改善幅: ???(ベースライン比)
主な改善要因: ???
【課題2: レビュー感情分析】
手法: ???
最終精度: ???
主な知見: ???
【技術選定の根拠】
画像分類: ???
テキスト分類: ???
【今後の改善方針】
1. ???
2. ???
3. ???
"""
解答例
report = f"""
{'='*60}
NetShop 深層学習プロジェクトレポート
{'='*60}
【エグゼクティブサマリー】
従来の機械学習(LightGBM等)では精度に限界があった
商品画像分類とレビュー感情分析に深層学習を適用。
画像分類では転移学習とデータ拡張の組み合わせで
ベースラインから大幅な精度改善を達成。
テキスト分類では BERT ベースの Fine-tuning により
高精度な感情分析モデルを構築した。
【課題1: 商品画像分類】
手法: ResNet-18 転移学習 + データ拡張 + CosineAnnealing
改善履歴:
"""
for name, acc in improvement_log:
report += f" {name:<25s}: {acc:.1f}%\n"
report += f"""
最終精度: {improvement_log[-1][1]:.1f}%
改善幅: {improvement_log[-1][1] - improvement_log[0][1]:+.1f}%(ベースライン比)
主な改善要因:
1. BatchNorm による学習の安定化
2. データ拡張による汎化性能の向上
3. 学習率スケジューリングによる収束の改善
【課題2: レビュー感情分析】
手法: DistilBERT Fine-tuning
最終精度: Accuracy {results['eval_accuracy']:.1%}, F1 {results['eval_f1']:.3f}
主な知見:
- 事前学習済み BERT は少量データでも高精度
- 学習率 2e-5、3エポックで十分な収束
- Attention 可視化で感情語への注目を確認
【技術選定の根拠】
画像分類:
- CNN は画像の空間的特徴を効率的に捉える
- 転移学習で少量データ問題を解決
- データ拡張で学習データの多様性を補完
テキスト分類:
- BERT は文脈を考慮した言語理解が可能
- Fine-tuning で少量のラベル付きデータから高精度モデルを構築
- DistilBERT で推論速度とのバランスを確保
【今後の改善方針】
1. 実データでの評価と継続的な再学習パイプライン構築
2. EfficientNet や DeBERTa などの最新モデルへの移行検討
3. マルチモーダル学習(画像+テキスト)による統合的な商品理解
4. モデルの軽量化(知識蒸留、量子化)と推論速度の最適化
5. A/Bテストによるビジネスインパクトの定量評価
{'='*60}
"""
print(report)
達成度チェック
- 商品画像分類モデルを段階的に改善し、精度向上を記録した
- レビュー感情分析モデルを Fine-tuning で構築した
- 各手法の効果を定量的に比較した
- 技術選定の根拠を説明できる形でレポートにまとめた
- 今後の改善方針を提示した
推定所要時間: 90分