🎯 STEP 25: 独立プロジェクト
3つのプロジェクトから選択して、自分の力で完成させよう
📋 このステップでやること
- 3つのプロジェクトから1つを選択
- 要件定義から実装まで自力で完成
- 評価指標での性能検証
- ポートフォリオとして使える成果物を作成
提出物: Jupyter Notebook + README.md + デモアプリ(任意)
⚠️ 実行環境について
このプロジェクトはGoogle Colab(GPU必須)で実行してください。 「ランタイム」→「ランタイムのタイプを変更」→「GPU」を選択します。
🎨 プロジェクト一覧
以下の3つのプロジェクトから1つを選択して実装してください。 どれを選んでも、NLPエンジニアとしての実力を示すポートフォリオになります。
| プロジェクト | 難易度 | 主要技術 | 目標指標 |
|---|---|---|---|
| A: Twitter感情分析 | 中級 | BERT分類、可視化 | F1 ≥ 0.75 |
| B: 商品レビュー要約 | 上級 | T5/BART要約 | ROUGE-L ≥ 0.40 |
| C: FAQチャットボット | 中級 | BERT埋め込み、検索 | Acc@5 ≥ 0.80 |
📊 プロジェクトA: Twitter感情分析ダッシュボード
中級A-1. プロジェクト概要
【プロジェクトA: Twitter感情分析】
■ 目的
ツイートをポジティブ/ネガティブ/ニュートラルに分類し、
感情の傾向を可視化するダッシュボードを構築
■ データセット
Kaggle: Twitter Sentiment Analysis
または: 日本語Twitter感情分析データセット
■ モデル
BERT日本語版(cl-tohoku/bert-base-japanese-v2)
→ 3クラス分類にファインチューニング
■ 目標性能
F1スコア: 0.75以上
■ 成果物
1. 分類モデル
2. 時系列グラフ
3. ワードクラウド
4. Gradioダッシュボード
A-2. 実装の流れ
【実装ステップ】
STEP 1: データ収集・準備
↓
STEP 2: テキスト前処理(ハッシュタグ、メンション除去)
↓
STEP 3: BERTでの3クラス分類モデル構築
↓
STEP 4: 学習と評価
↓
STEP 5: 可視化(時系列、ワードクラウド)
↓
STEP 6: Gradioダッシュボード作成
A-3. ツイート前処理のポイント
ツイートには特殊な要素(@メンション、#ハッシュタグ、URL)があります。 これらを適切に処理することが重要です。
※モバイルでは横スクロールできます
# ========================================
# ツイート前処理関数
# ========================================
import re
def clean_tweet(text):
“””
ツイートテキストをクリーニング
処理内容:
1. @メンション(@username)を除去
2. URLを除去
3. ハッシュタグの#記号を除去(単語は残す)
4. 改行を空白に変換
5. 連続する空白を1つに
“””
# @メンション除去
# \w+ は英数字とアンダースコアの連続
text = re.sub(r’@\w+’, ”, text)
# URL除去(http:// または https://)
text = re.sub(r’https?://\S+’, ”, text)
# ハッシュタグの#を除去(単語は残す)
# 例: “#AI” → “AI”
text = text.replace(‘#’, ”)
# 改行を空白に
text = text.replace(‘\n’, ‘ ‘)
# 連続する空白を1つに
text = re.sub(r’\s+’, ‘ ‘, text)
# 前後の空白を削除
text = text.strip()
return text
# テスト
sample = “@user これは #AI の時代です https://example.com”
print(f”Before: {sample}”)
print(f”After: {clean_tweet(sample)}”)
実行結果:
Before: @user これは #AI の時代です https://example.com
After: これは AI の時代です
A-4. 感情分類モデルの構築
# ========================================
# 3クラス感情分類モデル
# ========================================
from transformers import BertForSequenceClassification
# ラベルマッピング
id2label = {0: ‘ネガティブ’, 1: ‘ニュートラル’, 2: ‘ポジティブ’}
label2id = {‘ネガティブ’: 0, ‘ニュートラル’: 1, ‘ポジティブ’: 2}
# モデルの読み込み
model = BertForSequenceClassification.from_pretrained(
‘cl-tohoku/bert-base-japanese-v2’,
num_labels=3, # 3クラス分類
id2label=id2label,
label2id=label2id
)
print(f”ラベル: {list(id2label.values())}”)
A-5. 可視化の実装
# ========================================
# 時系列グラフとワードクラウド
# ========================================
import matplotlib.pyplot as plt
from wordcloud import WordCloud
# 時系列グラフ(感情の推移)
def plot_sentiment_timeline(df):
“””
日付ごとの感情分布を可視化
“””
# 日付でグループ化し、感情ごとにカウント
daily_sentiment = df.groupby([‘date’, ‘sentiment’]).size().unstack(fill_value=0)
# プロット
fig, ax = plt.subplots(figsize=(12, 6))
daily_sentiment.plot(kind=’area’, stacked=True, ax=ax, alpha=0.7)
ax.set_xlabel(‘日付’)
ax.set_ylabel(‘ツイート数’)
ax.set_title(‘感情の時系列推移’)
ax.legend(title=’感情’)
plt.tight_layout()
return fig
# ワードクラウド(感情別)
def create_wordcloud(texts, title):
“””
テキストからワードクラウドを生成
“””
# 日本語フォントのパス(環境に合わせて変更)
font_path = ‘/usr/share/fonts/truetype/fonts-japanese-gothic.ttf’
# テキストを結合
all_text = ‘ ‘.join(texts)
# ワードクラウド生成
wc = WordCloud(
font_path=font_path,
width=800,
height=400,
background_color=’white’
).generate(all_text)
# プロット
fig, ax = plt.subplots(figsize=(10, 5))
ax.imshow(wc, interpolation=’bilinear’)
ax.axis(‘off’)
ax.set_title(title)
return fig
A-6. Gradioダッシュボード
# ========================================
# Gradio ダッシュボード
# ========================================
import gradio as gr
def analyze_sentiment(text):
“””
単一ツイートの感情分析
“””
# 前処理
text = clean_tweet(text)
# 推論
result = classifier.predict(text, return_probs=True)
return result[‘category’], result[‘all_probabilities’]
# ダッシュボード
demo = gr.Interface(
fn=analyze_sentiment,
inputs=gr.Textbox(
lines=3,
placeholder=”ツイートを入力してください…”,
label=”ツイートテキスト”
),
outputs=[
gr.Textbox(label=”感情”),
gr.Label(label=”確率分布”, num_top_classes=3)
],
title=”📊 Twitter感情分析ダッシュボード”,
description=”ツイートの感情(ポジティブ/ネガティブ/ニュートラル)を分析します”,
examples=[
[“今日は天気が良くて気分最高!”],
[“電車が遅延して最悪だ…”],
[“明日の会議の準備をしています”]
]
)
demo.launch(share=True)
✅ プロジェクトAのチェックリスト
- □ ツイートの前処理関数を実装した
- □ 3クラス分類モデルを学習した
- □ F1スコア 0.75以上を達成した
- □ 時系列グラフを作成した
- □ ワードクラウドを作成した
- □ Gradioダッシュボードを作成した
📝 プロジェクトB: 商品レビュー要約システム
上級B-1. プロジェクト概要
【プロジェクトB: 商品レビュー要約】
■ 目的
複数の商品レビューを自動的に要約し、
購入判断に役立つ情報を提供
■ データセット
Amazon Product Reviews
楽天市場レビュー
価格.comレビュー
■ モデル
T5またはBART(要約用)
■ 目標性能
ROUGE-Lスコア: 0.40以上
■ 成果物
1. 要約モデル
2. ポジティブ・ネガティブ分離
3. 評価レポート
4. Gradioデモアプリ
B-2. 実装の流れ
【実装ステップ】
STEP 1: データ収集・準備
↓
STEP 2: レビューの前処理
↓
STEP 3: 感情分析でポジティブ/ネガティブを分離
↓
STEP 4: T5/BARTで要約モデル構築
↓
STEP 5: 評価(ROUGE、人間評価)
↓
STEP 6: デモアプリ作成
B-3. 複数レビューの統合要約
# ========================================
# 複数レビューの統合要約
# ========================================
from transformers import T5Tokenizer, T5ForConditionalGeneration
# T5モデルの読み込み
tokenizer = T5Tokenizer.from_pretrained(‘t5-base’)
model = T5ForConditionalGeneration.from_pretrained(‘t5-base’)
def summarize_reviews(reviews, max_reviews=10):
“””
複数のレビューを統合して要約
Args:
reviews: レビューテキストのリスト
max_reviews: 使用するレビュー数の上限
Returns:
str: 要約文
“””
# 上位のレビューを選択(長すぎる場合は制限)
selected_reviews = reviews[:max_reviews]
# レビューを結合
# 各レビューを「レビュー:」で区切る
combined_text = ‘ ‘.join([
f”レビュー: {review}”
for review in selected_reviews
])
# T5の入力形式
input_text = f”summarize: {combined_text}”
# トークン化
inputs = tokenizer(
input_text,
max_length=1024,
truncation=True,
return_tensors=’pt’
)
# 要約生成
outputs = model.generate(
**inputs,
max_length=150,
min_length=50,
num_beams=4,
length_penalty=2.0,
early_stopping=True
)
# デコード
summary = tokenizer.decode(outputs[0], skip_special_tokens=True)
return summary
B-4. ポジティブ・ネガティブの分離要約
# ========================================
# ポジティブ・ネガティブを分けて要約
# ========================================
def analyze_and_summarize(reviews, ratings):
“””
レビューを感情で分けて要約
Args:
reviews: レビューテキストのリスト
ratings: 評価スコア(1-5)のリスト
Returns:
dict: ポジティブ要約、ネガティブ要約、総合要約
“””
# 評価スコアで分類
# 4-5点: ポジティブ
# 1-2点: ネガティブ
# 3点: ニュートラル
positive_reviews = [r for r, s in zip(reviews, ratings) if s >= 4]
negative_reviews = [r for r, s in zip(reviews, ratings) if s <= 2]
# それぞれ要約
positive_summary = summarize_reviews(positive_reviews) if positive_reviews else "ポジティブなレビューはありません"
negative_summary = summarize_reviews(negative_reviews) if negative_reviews else "ネガティブなレビューはありません"
overall_summary = summarize_reviews(reviews)
return {
'positive': positive_summary,
'negative': negative_summary,
'overall': overall_summary,
'stats': {
'total': len(reviews),
'positive_count': len(positive_reviews),
'negative_count': len(negative_reviews)
}
}
B-5. ROUGE評価
# ========================================
# ROUGE評価
# ========================================
from rouge_score import rouge_scorer
def evaluate_summary(generated, reference):
“””
要約の品質をROUGEで評価
Args:
generated: 生成された要約
reference: 参照要約(人手で作成)
Returns:
dict: ROUGEスコア
“””
scorer = rouge_scorer.RougeScorer(
[‘rouge1’, ‘rouge2’, ‘rougeL’],
use_stemmer=True
)
scores = scorer.score(reference, generated)
return {
‘rouge1’: scores[‘rouge1’].fmeasure,
‘rouge2’: scores[‘rouge2’].fmeasure,
‘rougeL’: scores[‘rougeL’].fmeasure
}
# テスト
generated = “この商品は品質が良く、価格も手頃です。”
reference = “この商品は高品質で、コストパフォーマンスに優れています。”
scores = evaluate_summary(generated, reference)
print(f”ROUGE-L: {scores[‘rougeL’]:.4f}”)
✅ プロジェクトBのチェックリスト
- □ レビューデータを収集・前処理した
- □ 複数レビューの統合要約を実装した
- □ ポジティブ/ネガティブの分離要約を実装した
- □ ROUGE-L 0.40以上を達成した
- □ デモアプリを作成した
💬 プロジェクトC: FAQチャットボット
中級C-1. プロジェクト概要
【プロジェクトC: FAQチャットボット】
■ 目的
よくある質問(FAQ)に自動的に回答するチャットボットを構築
■ 仕組み
1. FAQの質問をBERTで埋め込み
2. ユーザーの質問をBERTで埋め込み
3. コサイン類似度で最も近い質問を検索
4. 対応する回答を返す
■ モデル
BERT日本語版(埋め込み用)
■ 目標性能
Accuracy@5: 0.80以上
(上位5件に正解が含まれる割合)
■ 成果物
1. FAQ埋め込みシステム
2. 類似検索機能
3. チャットインターフェース
C-2. 実装の流れ
【実装ステップ】
STEP 1: FAQデータの準備(質問・回答ペア)
↓
STEP 2: BERTで全質問を埋め込み
↓
STEP 3: コサイン類似度での検索機能
↓
STEP 4: 閾値処理(該当なしの判定)
↓
STEP 5: Gradioチャットインターフェース
C-3. FAQデータの準備
# ========================================
# FAQデータの準備
# ========================================
# FAQデータ(質問と回答のペア)
faq_data = [
{
‘question’: ‘配送にかかる時間はどれくらいですか?’,
‘answer’: ‘通常、ご注文から3〜5営業日でお届けします。お届け先や商品によっては異なる場合があります。’
},
{
‘question’: ‘返品はできますか?’,
‘answer’: ‘商品到着後30日以内であれば返品可能です。未使用・未開封の状態に限ります。’
},
{
‘question’: ‘支払い方法は何がありますか?’,
‘answer’: ‘クレジットカード、銀行振込、コンビニ決済、代金引換をご利用いただけます。’
},
{
‘question’: ‘送料はいくらですか?’,
‘answer’: ‘全国一律500円です。5,000円以上のお買い上げで送料無料となります。’
},
{
‘question’: ‘ポイントの有効期限はありますか?’,
‘answer’: ‘ポイントの有効期限は最終ご利用日から1年間です。’
},
# … さらに追加
]
print(f”FAQ数: {len(faq_data)}”)
C-4. BERTによる質問の埋め込み
# ========================================
# BERTで質問を埋め込み
# ========================================
import torch
from transformers import BertJapaneseTokenizer, BertModel
# モデルとトークナイザー
tokenizer = BertJapaneseTokenizer.from_pretrained(‘cl-tohoku/bert-base-japanese-v2’)
bert_model = BertModel.from_pretrained(‘cl-tohoku/bert-base-japanese-v2’)
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
bert_model = bert_model.to(device)
bert_model.eval()
def embed_text(text):
“””
テキストをBERTで埋め込みベクトルに変換
Args:
text: 入力テキスト
Returns:
torch.Tensor: 埋め込みベクトル(768次元)
“””
# トークン化
inputs = tokenizer(
text,
max_length=128,
padding=’max_length’,
truncation=True,
return_tensors=’pt’
)
inputs = {k: v.to(device) for k, v in inputs.items()}
# BERTで埋め込み
with torch.no_grad():
outputs = bert_model(**inputs)
# [CLS]トークンの埋め込みを使用
# [CLS]は文全体の意味を表す
embedding = outputs.last_hidden_state[:, 0, :]
return embedding
# ========================================
# 全FAQを埋め込み
# ========================================
def embed_all_faqs(faq_data):
“””
全FAQの質問を埋め込み
“””
embeddings = []
for faq in faq_data:
emb = embed_text(faq[‘question’])
embeddings.append(emb)
# 結合して1つのテンソルに
return torch.cat(embeddings, dim=0)
# 埋め込み実行
print(“FAQを埋め込み中…”)
faq_embeddings = embed_all_faqs(faq_data)
print(f”埋め込み形状: {faq_embeddings.shape}”)
実行結果:
FAQを埋め込み中…
埋め込み形状: torch.Size([5, 768])
C-5. 類似質問の検索
# ========================================
# 類似質問の検索
# ========================================
def find_similar_faq(user_question, top_k=3, threshold=0.7):
“””
ユーザーの質問に最も近いFAQを検索
Args:
user_question: ユーザーの質問
top_k: 返す候補数
threshold: 類似度の閾値
Returns:
list: 類似FAQのリスト
“””
# ユーザー質問を埋め込み
user_emb = embed_text(user_question)
# コサイン類似度を計算
# cos_sim = (A・B) / (||A|| * ||B||)
similarities = torch.nn.functional.cosine_similarity(
user_emb, # shape: (1, 768)
faq_embeddings # shape: (N, 768)
)
# 上位k件を取得
top_scores, top_indices = similarities.topk(top_k)
results = []
for score, idx in zip(top_scores, top_indices):
score_value = score.item()
idx_value = idx.item()
results.append({
‘question’: faq_data[idx_value][‘question’],
‘answer’: faq_data[idx_value][‘answer’],
‘score’: score_value,
‘above_threshold’: score_value >= threshold
})
return results
# ========================================
# テスト
# ========================================
user_question = “届くまでどのくらいかかりますか?”
results = find_similar_faq(user_question)
print(f”質問: {user_question}\n”)
for i, result in enumerate(results, 1):
print(f”[候補 {i}] スコア: {result[‘score’]:.4f}”)
print(f” Q: {result[‘question’]}”)
print(f” A: {result[‘answer’]}”)
print()
実行結果:
質問: 届くまでどのくらいかかりますか?
[候補 1] スコア: 0.8923
Q: 配送にかかる時間はどれくらいですか?
A: 通常、ご注文から3〜5営業日でお届けします。
[候補 2] スコア: 0.6234
Q: 送料はいくらですか?
A: 全国一律500円です。5,000円以上で送料無料。
[候補 3] スコア: 0.4567
Q: 支払い方法は何がありますか?
A: クレジットカード、銀行振込、コンビニ決済…
C-6. チャットボットの実装
# ========================================
# FAQチャットボット
# ========================================
def faq_chatbot(user_question):
“””
FAQチャットボットの回答関数
Args:
user_question: ユーザーの質問
Returns:
str: 回答
“””
# 類似FAQを検索
results = find_similar_faq(user_question, top_k=3, threshold=0.7)
# 最も類似度が高いものを確認
top_result = results[0]
if top_result[‘above_threshold’]:
# 閾値を超えていれば回答
response = f”{top_result[‘answer’]}\n\n”
response += f”(類似度: {top_result[‘score’]:.2%})”
else:
# 閾値以下なら回答できない
response = “申し訳ございません。該当するFAQが見つかりませんでした。\n\n”
response += “以下の質問に近いでしょうか?\n”
for i, r in enumerate(results[:3], 1):
response += f”{i}. {r[‘question’]}\n”
return response
# ========================================
# Gradioチャットインターフェース
# ========================================
import gradio as gr
demo = gr.ChatInterface(
fn=faq_chatbot,
title=”💬 FAQチャットボット”,
description=”ご質問をどうぞ。よくある質問にお答えします。”,
examples=[
“届くまでどのくらいかかりますか?”,
“返品したいのですが”,
“支払い方法を教えてください”
]
)
demo.launch(share=True)
✅ プロジェクトCのチェックリスト
- □ FAQデータを準備した
- □ BERTで質問を埋め込んだ
- □ コサイン類似度での検索を実装した
- □ 閾値処理(該当なし判定)を実装した
- □ Accuracy@5 0.80以上を達成した
- □ チャットインターフェースを作成した
📋 提出物の要件
必須提出物
| 提出物 | 内容 | 形式 |
|---|---|---|
| Jupyter Notebook | 全コード、説明、結果 | .ipynb |
| README.md | 概要、使い方、結果 | Markdown |
| 評価レポート | 性能指標、考察 | Notebook内 |
オプション提出物(推奨)
デモアプリ
- GradioまたはStreamlit
- 公開URL(Hugging Face Spacesなど)
GitHubリポジトリ
- 整理されたコード
- requirements.txt
- LICENSE
📝 README.mdテンプレート
# [プロジェクト名]
## 📋 プロジェクト概要
[プロジェクトの目的・背景を記載]
## 🛠️ 使用技術
– **言語:** Python 3.9
– **ライブラリ:** transformers, torch, scikit-learn, gradio
– **モデル:** [使用したモデル名]
## 📊 データセット
– **名称:** [データセット名]
– **サイズ:** [サンプル数]
– **ソース:** [入手先URL]
## 🚀 セットアップ
### インストール
pip install -r requirements.txt
### 実行
python app.py
## 📈 結果
– **Accuracy:** 0.XX
– **F1-score:** 0.XX
## 💡 工夫した点
– [工夫1]
– [工夫2]
## 🔧 改善案
– [今後の改善案]
## 👤 作成者
[あなたの名前]
📝 STEP 25 のまとめ
🎉 自然言語処理(NLP)コース完走おめでとうございます!
25ステップの学習、お疲れ様でした!
あなたは今、NLPエンジニアとして活躍できる十分な知識とスキルを身につけています。
✅ このコースで習得した技術
- Part 1-2: テキスト前処理、Word2Vec、GloVe、fastText
- Part 3: RNN、LSTM、GRU、Seq2Seq、Attention
- Part 4: Transformer、Self-Attention、Position Encoding
- Part 5: BERT、GPT、事前学習モデル
- Part 6: 感情分析、NER、QA、テキスト生成、要約
- Part 7: 実プロジェクトの完成
🚀 次のステップ
さらにスキルアップするには:
- 大規模言語モデル(LLM)の学習: GPT-4、Claude、RAG
- 実務経験: Kaggleコンペ、オープンソースへの貢献
- 最新技術のキャッチアップ: 論文(arXiv)、技術ブログ
- キャリア構築: ポートフォリオの充実、技術面接の準備
🎓 最後に
自然言語処理は急速に進化し続けている分野です。 このコースで学んだ基礎は、最新技術を理解する上で必ず役に立ちます。 学び続けることを忘れずに、NLPエンジニアとして活躍してください!
あなたの成功を心から応援しています!
学習メモ
自然言語処理(NLP) - Step 25
📋 過去のメモ一覧
▼