Machine Learning: NLPの基礎 — テキスト前処理と感情分析ガイド
最終更新:2026-08-26
すべてのレビューの裏には感情があります — NLPを使えば、顧客フィードバックに隠された怒り、失望、喜びを機械が読み取れるようになります。
1. この章で学ぶこと
- テキスト前処理:トークン化(Jieba/NLTK)、ストップワード除去、ステミング/レンマ化
- テキスト表現:Bag-of-Words、TF-IDF、N-gram
- 単語埋め込み:Word2Vec(Gensim)の原理と学習、単語類似度の計算
- テキスト分類の実践:TF-IDF + LogisticRegressionによる商品レビューの感情分析
- Bobのレビューインサイト:ネガティブレビューの重要な問題語を自動特定し、製品改善に活かす
2. ECカスタマーサービスマネージャーの実体験
(1) 課題:1日5千件のレビュー、手作業での分析は不可能
Bobのプラットフォームには毎日5千件の商品レビューが届きます — ポジティブ、ネガティブ、提案がすべて混在しています。カスタマーサービスチームはわずか1%しかサンプリングできず、膨大な品質問題を見逃しています。先月は不良品バッチが2千件のネガティブレビューを引き起こしましたが、発見されたのは3週間後で、すでに50万ドルの返品損失が発生していました。大量のテキストデータは、未開拓の金鉱であると同時に地雷原でもあるのです。
(2) NLPによる解決策
NLPを使えば、レビューの感情を自動分析し、重要な問題語を抽出し、ネガティブレビューの傾向をリアルタイムで監視できます。
PYTHON
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
pipe = Pipeline([
("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),
("clf", LogisticRegression(max_iter=500)),
])
pipe.fit(X_train_reviews, y_train_sentiment)
print(f"Sentiment accuracy: {pipe.score(X_test, y_test):.3f}")
(3) 成果:問題を3日以内に発見、返品率40%削減
BobがNLPによるレビュー自動分析を導入した結果、商品品質の問題を発見してから対応するまでの時間が3週間から3日に短縮され、返品率は40%低下し、年間約100万ドルのコスト削減を実現しました。
3. テキスト前処理
(1) トークン化とクリーニング
▶ サンプル:英語テキストの前処理パイプライン
PYTHON
import re
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer, WordNetLemmatizer
import nltk
nltk.download("punkt_tab", quiet=True)
nltk.download("stopwords", quiet=True)
nltk.download("wordnet", quiet=True)
def preprocess_text(text):
# Lowercase
text = text.lower()
# Remove special characters and numbers
text = re.sub(r"[^a-zA-Z\s]", "", text)
# Tokenize
tokens = word_tokenize(text)
# Remove stopwords
stop_words = set(stopwords.words("english"))
tokens = [t for t in tokens if t not in stop_words and len(t) > 2]
# Stemming
stemmer = PorterStemmer()
tokens_stemmed = [stemmer.stem(t) for t in tokens]
return " ".join(tokens_stemmed)
# Test
reviews = [
"This product is absolutely amazing! Best purchase ever.",
"Terrible quality, broke after 2 days. Very disappointed.",
"Decent product for the price, but shipping was slow.",
]
for review in reviews:
cleaned = preprocess_text(review)
print(f"Original: {review}")
print(f"Cleaned: {cleaned}\n")
出力:
TEXT
📖 参照専用
# Function defined successfully
▶ サンプル:単語トークン化(NLTK)
PYTHON
# Word tokenization with NLTK
# import nltk
# nltk.download('punkt')
# from nltk.tokenize import word_tokenize
# text = "This product has great quality and fast shipping"
# tokens = word_tokenize(text)
# print(tokens) # ['This', 'product', 'has', 'great', 'quality', 'and', 'fast', 'shipping']
# For English reviews in SalesPredict (international scenario)
from sklearn.feature_extraction.text import CountVectorizer
reviews = [
"Great laptop fast performance",
"Terrible screen quality",
"Good price fast delivery",
]
vectorizer = CountVectorizer()
X_bow = vectorizer.fit_transform(reviews)
print(f"Vocabulary: {vectorizer.vocabulary_}")
print(f"BoW matrix shape: {X_bow.shape}")
出力:
TEXT
📖 参照専用
# Executed successfully
| 前処理ステップ | 英語 | 中国語 |
|---|---|---|
| トークン化 | NLTK/spaCy(空白区切り) | Jieba/LAC(スペースなし) |
| ストップワード | NLTK stopwords | HIT/Baiduストップワードリスト |
| ステミング | PorterStemmer | 適用不可 |
| レンマ化 | WordNetLemmatizer | 適用不可 |
4. テキスト表現手法
テキストを「文字列」から「数値ベクトル」に変換するには、表現変換パイプラインが必要です:
graph LR
TEXT[Raw Text] --> TOKEN[Tokenize<br/>split / jieba] --> REMOVE[Remove Stopwords] --> STEM[Stem/Lemmatize] --> BOW[Bag-of-Words<br/>Count Vector] --> TFIDF[TF-IDF<br/>Weighted Vector] --> EMBED[Word2Vec<br/>Dense Embedding]
style BOW fill:#fff3cd
style TFIDF fill:#d1ecf1
style EMBED fill:#d4edda
(1) Bag-of-WordsとTF-IDF
▶ サンプル:3つのテキスト表現の比較
PYTHON
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
import numpy as np
reviews = [
"great product fast delivery happy customer",
"terrible quality broke disappointed waste money",
"great quality decent price fast shipping",
"awful product terrible experience never buy again",
"excellent quality great value fast delivery",
]
labels = [1, 0, 1, 0, 1] # 1=positive, 0=negative
# Method 1: Bag-of-Words (count)
bow = CountVectorizer()
X_bow = bow.fit_transform(reviews)
print(f"BoW vocabulary size: {len(bow.vocabulary_)}")
# Method 2: TF-IDF
tfidf = TfidfVectorizer(max_features=1000)
X_tfidf = tfidf.fit_transform(reviews)
print(f"TF-IDF shape: {X_tfidf.shape}")
# Method 3: N-gram (capture phrases)
tfidf_ngram = TfidfVectorizer(ngram_range=(1, 2), max_features=1000)
X_ngram = tfidf_ngram.fit_transform(reviews)
print(f"TF-IDF with bigrams shape: {X_ngram.shape}")
print(f"Top bigrams: {[w for w in tfidf_ngram.get_feature_names_out() if ' ' in w][:10]}")
出力:
TEXT
📖 参照専用
# Executed successfully
| 表現手法 | 情報量 | 次元数 | 用途 |
|---|---|---|---|
| BoW(単語頻度) | 低 | 高 | シンプルな分類 |
| TF-IDF | 中 | 高 | テキスト分類の標準手法 |
| N-gram | 中 | 非常に高 | フレーズの捕捉 |
| Word2Vec | 高 | 低(100-300) | 意味タスク |
(2) TF-IDFの直感的理解
- TF(単語頻度):ある文書内でその単語がどれだけ頻繁に出現するか
- IDF(逆文書頻度):その単語がどれだけ稀か(稀な単語ほど高い重みがつく)
- TF-IDF = TF × IDF:この文書では頻出するが他の文書では稀な単語が最も高い重みを得る
5. Word2Vecによる単語埋め込み
(1) Word2Vecの仕組み
Word2Vecは、文脈語を予測する(CBOW)か、ある単語から文脈を予測する(Skip-gram)ことで単語ベクトルを学習し、意味的に似た単語が近いベクトルを持つようにします。
▶ サンプル:Word2Vecの学習と単語類似度
PYTHON
from gensim.models import Word2Vec
from gensim.test.utils import common_texts
import numpy as np
# Train Word2Vec on sample corpus
# In practice, use real review data corpus
sentences = [
["great", "product", "fast", "delivery", "happy"],
["terrible", "quality", "broke", "disappointed"],
["great", "quality", "decent", "price"],
["awful", "product", "terrible", "experience"],
["excellent", "quality", "great", "value"],
["fast", "shipping", "good", "price", "recommend"],
["poor", "quality", "slow", "delivery", "angry"],
["amazing", "product", "best", "purchase", "happy"],
]
model = Word2Vec(sentences, vector_size=50, window=3, min_count=1, workers=4, epochs=50)
# Word similarity
print("Similar to 'great':")
for word, sim in model.wv.most_similar("great", topn=5):
print(f" {word}: {sim:.3f}")
# Word vector
print(f"\nVector for 'quality': {model.wv['quality'][:5]}... (50-dim)")
# Analogy: great - good + terrible = ?
# result = model.wv.most_similar(positive=["terrible"], negative=["good"], topn=3)
出力:
TEXT
📖 参照専用
Similar to
| パラメータ | 意味 | 推奨値 |
|---|---|---|
| vector_size | ベクトルの次元数 | 100-300 |
| window | 文脈ウィンドウのサイズ | 5 |
| min_count | 最小単語頻度 | 5 |
| sg | 0=CBOW、1=Skip-gram | 1(データが少ない場合) |
▶ サンプル:単語埋め込みの可視化
PYTHON
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
words = ["great", "terrible", "happy", "angry", "fast", "slow",
"quality", "price", "product", "delivery"]
vectors = [model.wv[w] for w in words]
vectors_2d = PCA(n_components=2).fit_transform(vectors)
fig, ax = plt.subplots(figsize=(10, 8))
ax.scatter(vectors_2d[:, 0], vectors_2d[:, 1], s=100)
for i, word in enumerate(words):
ax.annotate(word, (vectors_2d[i, 0], vectors_2d[i, 1]), fontsize=12)
ax.set_title("Word2Vec Embedding Visualization")
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("word2vec_vis.png", dpi=150)
出力:
TEXT
📖 参照専用
# Executed successfully
6. 商品レビューの感情分析実践
▶ サンプル:感情分類の完全なワークフロー
PYTHON
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np
# Simulate product reviews
rng = np.random.default_rng(42)
positive_templates = [
"great product love it highly recommend",
"excellent quality fast delivery satisfied",
"amazing value best purchase ever happy",
"good product decent price works well",
"love this item perfect condition great",
]
negative_templates = [
"terrible quality broke after one day",
"awful product waste money very disappointed",
"poor quality slow delivery never again",
"worst purchase defective product angry",
"bad product cheap material not recommend",
]
# Generate 2000 reviews
reviews = []
labels = []
for _ in range(1000):
reviews.append(rng.choice(positive_templates))
labels.append(1)
reviews.append(rng.choice(negative_templates))
labels.append(0)
X_train, X_test, y_train, y_test = train_test_split(reviews, labels, test_size=0.2, random_state=42)
# TF-IDF + Logistic Regression pipeline
pipe = Pipeline([
("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2), min_df=2)),
("clf", LogisticRegression(max_iter=500, C=1.0)),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print(f"Accuracy: {pipe.score(X_test, y_test):.4f}")
print(f"\nClassification Report:\n{classification_report(y_test, y_pred, target_names=['Negative', 'Positive'])}")
# Extract most important words for each sentiment
feature_names = pipe.named_steps["tfidf"].get_feature_names_out()
coefs = pipe.named_steps["clf"].coef_[0]
top_positive = np.argsort(coefs)[-10:][::-1]
top_negative = np.argsort(coefs)[:10]
print("\nTop positive words:")
for idx in top_positive:
print(f" {feature_names[idx]:20s}: {coefs[idx]:.3f}")
print("\nTop negative words:")
for idx in top_negative:
print(f" {feature_names[idx]:20s}: {coefs[idx]:.3f}")
出力:
TEXT
📖 参照専用
\nTop positive words:
\nTop negative words:
📌 ポイント: 係数が最も大きい単語はポジティブ感情の最も強い指標であり、係数が最も小さい(最も負の)単語はネガティブ感情の最も強い指標です。Bobはこれらのキーワードを使ってレビューの傾向を監視し、商品品質の問題を素早く発見できます。
❓ よくある質問
Q TF-IDFとWord2Vec、どちらを使うべきですか?
A テキスト分類にはTF-IDF(シンプルで効果的、解釈可能)、意味タスク(類似度/アナロジー)にはWord2Vec、ディープラーニングには事前学習済み埋め込み(BERTなど)を使ってください。初心者はまずTF-IDFから始めるのがおすすめです。
Q N-gramのnはどう選べばいいですか?
A テキスト分類では(1,2)、つまりユニグラム+バイグラムを使いましょう。感情分析ではバイグラムが非常に重要です(例:"not good")。nが大きい(3以上)のは通常コスパが悪いです(次元数が爆発しデータがスパースになるため)。
Q 中国語のトークン化にはどのツールを使えばいいですか?
A Jieba(最も人気)、pkuseg(高精度)、LAC(Baidu製、多機能)があります。ECレビューの場合、カスタム辞書にブランド名やドメイン用語を追加しましょう。
Q Word2Vecの学習にはどのくらいのデータが必要ですか?
A 意味のあるベクトルを学習するには最低でも1000万語必要です。データが少ない場合は、事前学習済みベクトル(例:GoogleNews-vectors)を使うか、TF-IDFにとどめておくのがよいでしょう。
Q 感情分析でどのくらいの精度なら良いと言えますか?
A シンプルな二値分類(ポジティブ/ネガティブ)では、TF-IDF + ロジスティック回帰で通常80〜85%に到達します。BERTなどのディープモデルでは90〜95%も可能です。ただし、ノイズの多い実際のビジネス環境では、70〜80%でも十分実用的です。
Q 皮肉や暗黙の否定はどう処理しますか?
A シンプルなモデル(TF-IDF)ではこれが苦手です。対処するには、1) N-gram特徴量を増やす、2) 事前学習済み言語モデル(BERT)を使う、3) 皮肉なサンプルを手動でアノテーションして専用モデルを学習する、といった方法があります。
📖 まとめ
- テキスト前処理パイプライン:小文字化 → 特殊文字除去 → トークン化 → ストップワード除去 → ステミング/レンマ化
- TF-IDFはテキスト分類の標準的な表現手法:単語頻度 × 逆文書頻度により、稀だが重要な単語に最も高い重みを与える
- N-gramはフレーズ情報(例:"not good")を捕捉するが、次元数が膨らむためmax_featuresで制御する
- Word2Vecは文脈予測を通じて単語ベクトルを学習し、意味的に似た単語は近いベクトルを持つ
- テキスト分類パイプライン:TfidfVectorizer + LogisticRegression — シンプルかつ高効率
- モデルの係数からキーワードを抽出でき、ポジティブ/ネガティブ指標語がビジネス改善を直接導く
📝 練習問題
- 基礎(難易度 ⭐):TfidfVectorizerを使って5つの商品レビューをベクトル化し、語彙とTF-IDF行列を出力してください。ヒント:第4章のサンプルを参照してください。
- 中級(難易度 ⭐⭐):TF-IDF + LogisticRegressionの感情分類パイプラインを構築し、ユニグラムのみと(1,2)-gramでF1スコアの差を比較してください。ヒント:ngram_range=(1,1) vs (1,2)。
- チャレンジ(難易度 ⭐⭐⭐):Bobのレビューインサイトシステムを実装してください — 感情分類器の学習後、ネガティブキーワードの上位20件を抽出し、頻度順にソートして「最も緊急に対応すべき問題」ランキングを出力してください。ヒント:coef_のソート + 特徴量名へのマッピング + 頻度カウント。