Skip to content

11. テキストデータを用いた機械学習

第 11 回では、Bag-of-Words(BoW)と TF-IDF を使ったテキストのベクトル化と文書分類について学びます。

11.1 テキストのベクトル化

11.1.1 なぜベクトル化が必要か

機械学習モデルは数値データしか扱えません。テキストを数値ベクトルに変換することで、機械学習の各種アルゴリズムを適用できるようになります。

11.1.2 ベクトル化手法の種類

主要なテキストのベクトル化手法には以下があります。

  • Bag-of-Words (BoW): 単語の出現回数をカウント
  • TF-IDF: 単語の重要度を考慮
  • Word2Vec: 単語の意味をベクトル化
  • BERT/Transformer: 文脈を考慮した表現

今回は、Bag-of-Words と TF-IDF の実装方法を紹介します。

11.1.3 使用するライブラリ

テキストのベクトル化には、scikit-learn の feature_extraction.text モジュールを使用することができます。

CountVectorizer

テキストを BoW(単語の出現回数)ベクトルに変換するクラスです。

TfidfVectorizer

テキストを TF-IDF ベクトルに変換するクラスです。

11.2 Bag-of-Words(BoW)

11.2.1 Bag-of-Words とは

Bag-of-Words(BoW) は、文書を「単語の袋」として表現する手法です。単語の順序を無視し、各単語の出現回数のみを考慮します。

BoW の特徴

  • シンプル: 実装が簡単
  • 語順を無視: "I love cats" と "cats love I" が同じベクトル
  • スパース: ほとんどの要素が 0

11.2.2 BoW の実装例

BoW ベクトルを作成するには、scikit-learn の CountVectorizer の fit_transform() メソッドを使用します。

import MeCab
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd

# MeCab の初期化(分かち書きモード)
mecab = MeCab.Tagger("-Owakati")

# 日本語のサンプル文書
documents = [
    "私は機械学習が好きです",
    "私は機械学習も深層学習も好きです",
    "機械学習は楽しいです"
]

# 形態素解析を実行
tokenized_docs = []
for doc in documents:
    tokenized = mecab.parse(doc).strip()
    tokenized_docs.append(tokenized)

print("形態素解析後:")
for doc in tokenized_docs:
    print(f"  {doc}")

# CountVectorizerの初期化
vectorizer = CountVectorizer()

# BoWベクトルの作成
bow_matrix = vectorizer.fit_transform(tokenized_docs)

# 語彙の確認
vocab = vectorizer.get_feature_names_out()
print("\n語彙:")
print(vocab)

# ベクトルの確認(密行列に変換)
bow_array = bow_matrix.toarray()
print("\nBoW ベクトル:")
print(bow_array)

# DataFrame 表示
df = pd.DataFrame(bow_array, columns=vocab)
print("\nDataFrame 表示:")
print(df)
Output
形態素解析後:
  私 は 機械 学習 が 好き です
  私 は 機械 学習 も 深層 学習 も 好き です
  機械 学習 は 楽しい です

語彙:
['です' '好き' '学習' '楽しい' '機械' '深層']

BoW ベクトル:
[[1 1 1 0 1 0]
 [1 1 2 0 1 1]
 [1 0 1 1 1 0]]

DataFrame 表示:
   です  好き  学習  楽しい  機械  深層
0   1   1   1    0   1   0
1   1   1   2    0   1   1
2   1   0   1    1   1   0
1 文字のトークンについて

CountVectorizer はデフォルトで 1文字のトークンを除外 します。これは token_pattern パラメータのデフォルト値が r"(?u)\b\w\w+\b"(2文字以上の単語のみマッチ)になっているためです。

そのため「は」「が」「も」「私」などの1文字トークンは語彙に含まれません。

1文字のトークンも含めたい場合

vectorizer = CountVectorizer(token_pattern=r"(?u)\b\w+\b")
  • (?u): Unicode モード
  • \b: 単語の境界
  • \w+: 1文字以上の単語文字(\w\w+ だと2文字以上)
fit, transform, fit_transform の違い

CountVectorizer や TfidfVectorizer には 3 つの主要なメソッドがあります。

メソッド 説明
fit(X) データから語彙を学習する(語彙の構築のみ)
transform(X) 学習済みの語彙を使ってデータをベクトル化する
fit_transform(X) fit と transform を同時に実行する

使い分け

  • 訓練データ: fit_transform() を使用(語彙の学習とベクトル化を同時に行う)
  • テストデータ: transform() のみを使用(訓練データで学習した語彙を使う)
# 訓練データ: 語彙を学習しながらベクトル化
X_train_vec = vectorizer.fit_transform(X_train)

# テストデータ: 学習済みの語彙でベクトル化(fit しない)
X_test_vec = vectorizer.transform(X_test)

テストデータに fit_transform() を使うと、テストデータ独自の語彙が作られてしまい、訓練データと異なる特徴空間になるため注意が必要です。

11.2.3 BoW のパラメータ

CountVectorizer の主要なパラメータ

import MeCab
from sklearn.feature_extraction.text import CountVectorizer

# MeCab の初期化(分かち書きモード)
mecab = MeCab.Tagger("-Owakati")

# パラメータの例
vectorizer = CountVectorizer(
    max_features=100,      # 最大語彙数
    min_df=2,              # 最低文書頻度(2文書以上に出現)
    max_df=0.8,            # 最大文書頻度(80%以下の文書に出現)
    ngram_range=(1, 2)     # n-gram(単語と2単語の組み合わせ)
)

documents = [
    "私は機械学習が好きです",
    "私は機械学習も深層学習も好きです",
    "機械学習は楽しいです",
    "深層学習は面白いです"
]

# 形態素解析を実行
tokenized_docs = []
for doc in documents:
    tokenized = mecab.parse(doc).strip()
    tokenized_docs.append(tokenized)

bow_matrix = vectorizer.fit_transform(tokenized_docs)

print("語彙(n-gram含む):")
print(vectorizer.get_feature_names_out())

print("\nBoW ベクトル:")
print(bow_matrix.toarray())

11.2.4 N-gram

N-gram は、連続する N 個の単語を一つの特徴として扱う手法です。

  • Unigram(1-gram): 単語単位(例: "機械", "学習")
  • Bigram(2-gram): 2 単語の組み合わせ(例: "機械 学習")
  • Trigram(3-gram): 3 単語の組み合わせ(例: "機械 学習 は")
import MeCab
from sklearn.feature_extraction.text import CountVectorizer

# MeCab の初期化(分かち書きモード)
mecab = MeCab.Tagger("-Owakati")

documents = [
    "私は機械学習が好きです",
    "機械学習は楽しいです"
]

# 形態素解析を実行
tokenized_docs = []
for doc in documents:
    tokenized = mecab.parse(doc).strip()
    tokenized_docs.append(tokenized)

# Unigram(デフォルト)
vectorizer_1gram = CountVectorizer(ngram_range=(1, 1))
bow_1gram = vectorizer_1gram.fit_transform(tokenized_docs)

# Bigram
vectorizer_2gram = CountVectorizer(ngram_range=(2, 2))
bow_2gram = vectorizer_2gram.fit_transform(tokenized_docs)

# Unigram + Bigram
vectorizer_both = CountVectorizer(ngram_range=(1, 2))
bow_both = vectorizer_both.fit_transform(tokenized_docs)

print("Unigram:")
print(vectorizer_1gram.get_feature_names_out())

print("\nBigram:")
print(vectorizer_2gram.get_feature_names_out())

print("\nUnigram + Bigram:")
print(vectorizer_both.get_feature_names_out())
Output
Unigram:
['です' '好き' '学習' '楽しい' '機械']

Bigram:
['好き です' '学習 が' '学習 は' '機械 学習']

Unigram + Bigram:
['です' '好き' '好き です' '学習' '学習 が' '学習 は' '楽しい' '機械' '機械 学習']

11.3 TF-IDF

11.3.1 TF-IDF とは

TF-IDF(Term Frequency-Inverse Document Frequency) は、単語の重要度を考慮したベクトル化手法です。

TF(Term Frequency): 文書内での単語の出現頻度

\[ \text{TF}(t, d) = \frac{\text{文書 } d \text{ に単語 } t \text{ が出現する回数}}{\text{文書 } d \text{ の総単語数}} \]

IDF(Inverse Document Frequency): レアな単語ほど高い値

\[ \text{IDF}(t) = \log \frac{\text{全文書数}}{\text{単語 } t \text{ が出現する文書数}} \]

TF-IDF

\[ \text{TF-IDF}(t, d) = \text{TF}(t, d) \cdot \text{IDF}(t) \]

11.3.2 TF-IDF の直感的理解

例: "私" と "機械学習" の比較

  • "私": ほとんどの文書に出現 → IDF が低い → 重要度が低い
  • "機械学習": 特定の文書のみに出現 → IDF が高い → 重要度が高い
import numpy as np

# サンプル文書
documents = [
    "私は猫が好きです",
    "私は犬が好きです",
    "猫と犬は動物です"
]

# 単語 "私" の出現状況
# 文書1: 1回, 文書2: 1回, 文書3: 0回 → 3文書中2文書に出現

# 単語 "動物" の出現状況
# 文書1: 0回, 文書2: 0回, 文書3: 1回 → 3文書中1文書に出現

# IDF の計算
total_docs = 3
docs_with_watashi = 2
docs_with_animal = 1

idf_wa = np.log(total_docs / docs_with_watashi)
idf_animal = np.log(total_docs / docs_with_animal)

print(f"IDF('私'): {idf_wa:.4f}")
print(f"IDF('動物'): {idf_animal:.4f}")
print(f"\n'動物' の IDF は '私' の {idf_animal / idf_wa:.2f} 倍")
Output
IDF('私'): 0.4055
IDF('動物'): 1.0986

'動物' の IDF は '私' の 2.71 倍

11.3.3 TF-IDF の実装

TF-IDF ベクトルを作成するには、scikit-learn の TfidfVectorizer の fit_transform() メソッドを使用します。

import MeCab
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

# MeCab の初期化(分かち書きモード)
mecab = MeCab.Tagger("-Owakati")

# 日本語のサンプル文書
documents = [
    "私は機械学習が好きです",
    "私は機械学習も深層学習も好きです",
    "機械学習は楽しいです",
    "猫が窓辺に座っています"
]

# 形態素解析を実行
tokenized_docs = []
for doc in documents:
    tokenized = mecab.parse(doc).strip()
    tokenized_docs.append(tokenized)

# TF-IDF ベクトライザ
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(tokenized_docs)

# 語彙
vocab = tfidf_vectorizer.get_feature_names_out()
print("語彙:")
print(vocab)

# TF-IDF 行列
tfidf_array = tfidf_matrix.toarray()
print("\nTF-IDF ベクトル:")
print(tfidf_array)

# DataFrame 表示
df = pd.DataFrame(tfidf_array, columns=vocab)
print("\nDataFrame 表示:")
print(df.round(3))

# IDF 値の確認
print("\nIDF 値:")
idf_df = pd.DataFrame({
    '単語': vocab,
    'IDF': tfidf_vectorizer.idf_
}).sort_values('IDF', ascending=False)
print(idf_df)
Output
語彙:
['です' 'ます' '好き' '学習' '座っ' '楽しい' '機械' '深層' '窓辺']

TF-IDF ベクトル:
[[0.47006328 0.         0.58062167 0.47006328 0.         0.
  0.47006328 0.         0.        ]
 [0.3165406  0.         0.39099061 0.63308119 0.         0.
  0.3165406  0.49592201 0.        ]
 [0.42817512 0.         0.         0.42817512 0.         0.67081906
  0.42817512 0.         0.        ]
 [0.         0.57735027 0.         0.         0.57735027 0.
  0.         0.         0.57735027]]

DataFrame 表示:
      です     ます     好き     学習     座っ    楽しい     機械     深層     窓辺
0  0.470  0.000  0.581  0.470  0.000  0.000  0.470  0.000  0.000
1  0.317  0.000  0.391  0.633  0.000  0.000  0.317  0.496  0.000
2  0.428  0.000  0.000  0.428  0.000  0.671  0.428  0.000  0.000
3  0.000  0.577  0.000  0.000  0.577  0.000  0.000  0.000  0.577

IDF 値:
    単語       IDF
1   ます  1.916291
8   窓辺  1.916291
4   座っ  1.916291
7   深層  1.916291
5  楽しい  1.916291
2   好き  1.510826
0   です  1.223144
3   学習  1.223144
6   機械  1.223144

11.4 ナイーブベイズ

11.4.1 ナイーブベイズとは

ナイーブベイズ(Naive Bayes) は、ベイズの定理を使った確率的な分類手法です。「ナイーブ(単純)」という名前は、各特徴量が独立であるという素朴な仮定から来ています。実際のテキストでは単語間に依存関係がありますが、この仮定の下でも実用的に動作します。

graph LR
    A[文書] --> B[単語頻度]
    B --> C[クラスごとの<br/>単語出現確率]
    C --> D[ベイズの定理]
    D --> E[クラス確率<br/>を計算]

    style A fill:#e6f3ff,stroke:#66b3ff,stroke-width:3px
    style B fill:#fff4cc,stroke:#ffcc00,stroke-width:3px
    style C fill:#ccffcc,stroke:#66cc66,stroke-width:3px
    style D fill:#ffffcc,stroke:#ffcc00,stroke-width:3px
    style E fill:#ffcccc,stroke:#ff6666,stroke-width:3px

ナイーブベイズの特徴

  • 学習が高速(パラメータをカウントするだけ)
  • 少ないデータでも安定する
  • BoW・TF-IDF と相性が良い
  • スパムフィルタなど古典的なテキスト分類で広く使われている

11.4.2 多項ナイーブベイズ

テキスト分類で最もよく使われるのが 多項ナイーブベイズ(Multinomial Naive Bayes) です。単語の出現回数を「多項分布のサンプル」と見なし、各クラスでの単語頻度から確率を計算します。

scikit-learn では sklearn.naive_bayes.MultinomialNB を使います。

import MeCab
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB

# サンプル文書とラベル
documents = [
    'AI は機械学習の一分野です',
    '機械学習は深層学習の基礎です',
    'AI は楽しいです',
    '深層学習は楽しいです',
]
labels = ['基礎', '基礎', '応用', '応用']

# 形態素解析(分かち書き)
tagger = MeCab.Tagger("-Owakati")
docs_wakati = [tagger.parse(d).strip() for d in documents]

# BoW でベクトル化
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(docs_wakati)

# 多項ナイーブベイズで学習
model = MultinomialNB()
model.fit(X, labels)

# 新しい文書を予測
test = ['AI は深層学習の応用です']
test_wakati = [tagger.parse(t).strip() for t in test]
X_test = vectorizer.transform(test_wakati)
print(f'予測: {model.predict(X_test)[0]}')
print(f'クラス確率: {dict(zip(model.classes_, model.predict_proba(X_test)[0]))}')

11.4.3 ナイーブベイズの直感的理解

ナイーブベイズの計算はシンプルです。「クラス \(C\) のときに単語 \(w\) が現れる確率 \(P(w|C)\)」をデータから数え、文書中の全単語についてその確率の積をとります。

\[ P(C \mid ext{文書}) \propto P(C) \prod_{w \in ext{文書}} P(w \mid C) \]

たとえばスパム判定なら、「無料」「割引」「当選」などの単語がスパムに頻出することがデータから分かれば、それらを含むメールは自動的にスパム確率が高く判定されます。

ナイーブベイズが使われる場面

  • スパムフィルタ:定番の用途。少ないルールで高速に判定できる
  • トピック分類:ニュース記事のカテゴリ分けなど
  • 高速なベースライン:本格的なモデルを試す前の比較対象として優秀

11.5 文書分類の実践

11.5.1 問題設定

感情分析(Sentiment Analysis)のタスクを実装することを考えます。具体的には、コメディ映画のレビュー文がポジティブであるかネガティブであるかの分類を行います。

処理の流れ:

  1. CSV ファイルからデータを読み込み
  2. MeCab で形態素解析(分かち書き)
  3. BoW によるベクトル化
  4. ロジスティック回帰による分類
  5. 予測結果の評価

11.5.2 データの準備

演習では、以下のような日本語のコメディ映画レビュー(positive, negative ともに 100 件)を含む movie_reviews.csv ファイルを使用します。本ファイルは、CHIKUWA Editor の sample フォルダ内にも置いてあります。

movie_reviews.csv
label,text
positive,笑いのセンスが抜群で、映画館中が爆笑に包まれた。何度でも観たいと思える作品だった。
positive,ギャグのタイミングが絶妙で、何度も声を出して笑った。久しぶりに腹を抱えて笑える映画だった。
positive,主演俳優のコミカルな演技が光っていて、終始ニヤニヤが止まらなかった。楽しい時間を過ごせた。
...
...
...
negative,笑いを狙いすぎている感じが透けて見えて、空回りしているコメディ作品だった。冷めてしまった。
negative,ギャグのタイミングが悪く、笑うべき場面で笑うことができなかった。間の取り方に問題がある。
negative,下ネタばかりで品がなく、見ていて不快な気持ちになってしまうコメディ映画だった。趣味が悪い。
...
...
...
import pandas as pd

# データの読み込み
df = pd.read_csv('movie_reviews.csv')

print("データの確認:")
print(df.head(10))

print(f"\n総データ数: {len(df)}")
print(f"\nラベルの分布:")
print(df['label'].value_counts())
Output
データの確認:
      label                                             text
0  positive        笑いのセンスが抜群で、映画館中が爆笑に包まれた。何度でも観たいと思える作品だった。
1  positive    ギャグのタイミングが絶妙で、何度も声を出して笑った。久しぶりに腹を抱えて笑える映画だった。
2  positive    主演俳優のコミカルな演技が光っていて、終始ニヤニヤが止まらなかった。楽しい時間を過ごせた。
3  positive   シュールなユーモアが随所に散りばめられていて、製作陣のセンスの良さが伝わってくる作品だった。
4  positive     脚本が練られていて、伏線を回収するギャグには思わず膝を打った。笑いの構成が本当に上手い。
5  positive  テンポの良いボケとツッコミの応酬が続き、あっという間に上映時間が過ぎた。退屈する暇がなかった。
6  positive  笑いながらも心温まるストーリーで、観終わった後に幸せな気持ちになれた。大切な人と観たい映画だ。
7  positive     コメディとしての完成度が高く、何度でも繰り返し観たくなる作品だと思う。友人にも勧めたい。
8  positive      脇役陣のボケが絶妙で、主役を食うほどの存在感を見せていた。キャスティングがとても良い。
9  positive      下品になりすぎず、子供から大人まで誰でも楽しめる上質なユーモアが詰まった作品だと思う。

総データ数: 200

ラベルの分布:
label
positive    100
negative    100
Name: count, dtype: int64

11.5.3 前処理から分類まで

前処理から学習、分類、評価までの一連の流れは、以下のようなコードになります。

# 注:以下は、df に movie_reviews.csv を読み込んでいることを前提としたコード

import MeCab
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix

# MeCab の初期化(分かち書きモード)
tagger = MeCab.Tagger('-Owakati')

# 形態素解析
tokenized_list = []
for text in df['text']:
    tokenized = tagger.parse(text).strip()
    tokenized_list.append(tokenized)

df['text_tokenized'] = tokenized_list

# ラベルを数値に変換(positive=1, negative=0)
df['label_num'] = (df['label'] == 'positive').astype(int)

# 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
    df['text_tokenized'], df['label_num'],
    test_size=0.2,
    random_state=42
)

# BoW ベクトル化
bow_vectorizer = CountVectorizer()
X_train_bow = bow_vectorizer.fit_transform(X_train)
X_test_bow = bow_vectorizer.transform(X_test)

print(f"\n語彙数: {len(bow_vectorizer.get_feature_names_out())}")
print(f"訓練データの形状: {X_train_bow.shape}")

# ロジスティック回帰で学習
model_bow = LogisticRegression(random_state=42, max_iter=1000)
model_bow.fit(X_train_bow, y_train)

# 予測
y_pred_bow = model_bow.predict(X_test_bow)

# 評価
accuracy_bow = accuracy_score(y_test, y_pred_bow)
print(f"\nBoW の結果:")
print(f"精度: {accuracy_bow:.2%}")

print("\n分類レポート:")
print(classification_report(y_test, y_pred_bow, target_names=['Negative', 'Positive']))
Output
語彙数: 755
訓練データの形状: (160, 755)

BoW の結果:
精度: 75.00%

分類レポート:
              precision    recall  f1-score   support

    Negative       0.70      0.84      0.76        19
    Positive       0.82      0.67      0.74        21

    accuracy                           0.75        40
   macro avg       0.76      0.75      0.75        40
weighted avg       0.76      0.75      0.75        40

考えてみよう

混同行列を見て、予測結果を詳しく確認してみましょう。また、誤分類されたテキストがどのようなものか調べ、なぜ誤分類されたのか考えてみましょう。

11.5.4 新しいレビューの予測

新しいレビューに対し、学習済みモデル model_bow を用いて予測を行いたい場合は、以下のようにします。

# 新しいレビューを予測
new_review = "こんなに面白いコメディ映画は生まれて初めて。"

# トークン化
new_review_tokenized = tagger.parse(new_review).strip()

# BoWベクトル化(リストで渡す必要がある)
new_review_bow = bow_vectorizer.transform([new_review_tokenized])

# 予測
prediction = model_bow.predict(new_review_bow)
probability = model_bow.predict_proba(new_review_bow)

sentiment = "ポジティブ" if prediction[0] == 1 else "ネガティブ"
confidence = probability[0][prediction[0]] * 100

print(f"レビュー: {new_review}")
print(f"予測: {sentiment} (確信度: {confidence:.1f}%)")
Output
レビュー: こんなに面白いコメディ映画は生まれて初めて。
予測: ポジティブ (確信度: 79.0%)

11.6 演習問題

演習 11-1: BoW + ナイーブベイズ

演習 11-1(初級)

映画レビューのテキストと感情ラベル(positive / negative)が各 100 件・計 200 件入った movie_reviews.csv を使って、Bag-of-Words(BoW)+ 多項ナイーブベイズ による感情分析モデルを作り、テスト精度を確認してください。

タスク

  1. CSV を読み込み、MeCab.Tagger("-Owakati") で全レビューを分かち書きする
  2. ラベルを数値化(positive=1, negative=0)し、訓練 80%・テスト 20% に分割する(random_state=42, stratify=y)
  3. CountVectorizer で BoW ベクトル化する
  4. MultinomialNB() で学習し、テスト精度と classification_report を表示する
テンプレートコード
import pandas as pd
import MeCab
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, classification_report

# 1. データ読み込み + 分かち書き

# 2. ラベル数値化 + 訓練・テストに分割

# 3. CountVectorizer で BoW ベクトル化

# 4. MultinomialNB で学習・評価

演習 11-2: TF-IDF + ロジスティック回帰

演習 11-2(初級)

映画レビューのテキストと感情ラベル(positive / negative)が各 100 件・計 200 件入った movie_reviews.csv を使って、TF-IDF + ロジスティック回帰 による感情分析モデルを作り、テスト精度を確認してください。

タスク

  1. CSV を読み込み、MeCab.Tagger("-Owakati") で全レビューを分かち書きする
  2. ラベルを数値化(positive=1, negative=0)し、訓練 80%・テスト 20% に分割する(random_state=42, stratify=y)
  3. TfidfVectorizer で TF-IDF ベクトル化する
  4. LogisticRegression(max_iter=1000, random_state=42) で学習し、テスト精度と classification_report を表示する
テンプレートコード
import pandas as pd
import MeCab
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report

# 1. データ読み込み + 分かち書き

# 2. ラベル数値化 + 訓練・テストに分割

# 3. TfidfVectorizer で TF-IDF ベクトル化

# 4. LogisticRegression で学習・評価

演習 11-3: TF-IDF × 複数モデルの比較と重要特徴語

演習 11-3(中級)

映画レビューのテキストと感情ラベル(positive / negative)が各 100 件・計 200 件入った movie_reviews.csv を使って、TF-IDF で特徴量化したテキスト を ロジスティック回帰・多項ナイーブベイズ・ランダムフォレスト の 3 つのモデルで分類し、テスト精度を比較してください。さらに、ロジスティック回帰が学習した係数から「ポジティブ寄り」「ネガティブ寄り」の重要語を確認します。

タスク

  1. CSV を読み込み、MeCab.Tagger("-Owakati") で全レビューを分かち書きする
  2. ラベルを数値化(positive=1, negative=0)し、訓練 80%・テスト 20% に分割する(random_state=42, stratify=y)
  3. TfidfVectorizer で TF-IDF 化したあと、以下 3 モデルでテスト精度を求め、結果を表示する
    • LogisticRegression(max_iter=1000, random_state=42)
    • MultinomialNB()
    • RandomForestClassifier(n_estimators=100, random_state=42)
  4. ロジスティック回帰の coef_ を使って、係数が 最大の 10 単語(ポジティブ寄り)と 最小の 10 単語(ネガティブ寄り)を表示し、コメントで考察する
テンプレートコード
import numpy as np
import pandas as pd
import MeCab
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import MultinomialNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

# 1. データ読み込み + 分かち書き

# 2. ラベル数値化 + 訓練・テスト分割

# 3. TF-IDF 化 + 3 モデルで精度比較・表示

# 4. ロジスティック回帰の係数から上位 10 / 下位 10 単語を表示・考察

11.7 課題 11: メール 3 クラス分類

課題 11

メールのテキストをまとめた emails.csv を使って、メールを 「ビジネスメール(business)」「プライベートメール(private)」「スパムメール(spam)」 の 3 クラスに分類するモデルを構築し、テスト精度・混同行列・考察をレポートにまとめてください。データは各クラス 100 件ずつ、計 300 件です。

※ 加点要素:複数手法の比較、前処理の工夫(品詞絞り込み・ストップワード除去など)、ベクトル化パラメータの調整、誤分類分析 など。

提出物

  • Word ファイルで作成し、PDF 形式で出力して提出(学生番号_氏名_11.pdf)
  • 問題文・ソースコード・結果・考察を PDF 内に含めること(参考:機械学習_レポートサンプル.docx)

提出期限:2026 年 7 月 12 日(日)23:59

提出先:manaba

11.8 まとめ

本章では、Bag-of-Words と TF-IDF を使ったテキストのベクトル化と文書分類について学びました。

第 11 回のまとめ

テキストのベクトル化

  • 機械学習モデルは数値データが必要
  • テキストを数値ベクトルに変換
  • 代表的手法:BoW、TF-IDF

Bag-of-Words(BoW)

  • 単語の出現回数をカウント
  • シンプルで実装が容易
  • 語順を無視する
  • スパースなベクトル

TF-IDF

  • 単語の重要度を考慮
  • TF(文書内頻度)× IDF(レア度)
  • 頻出単語の重要度を下げる
  • BoW より高精度なことが多い

N-gram

  • 連続する N 個の単語を特徴とする
  • Bigram、Trigram など
  • 語順の情報を部分的に保持

文書分類

  • 感情分析、トピック分類など
  • TF-IDF + ロジスティック回帰が基本
  • Naive Bayes、SVM、Random Forest も有効