TF-IDFを使ったテキストベクトル化

テキスト分類問題等で使うことが多いテキストベクトル化について説明します。

1. テキストベクトル化

テキストベクトル化とは主に自然言語処理で使われるテキスト前処理手法の1つです。例えば"This is a pen."のようなテキストデータを[0.1, 1.2, 0.4, 0.8, -0.01, 0.3]のようなベクトルデータに変換することです。

こうすることでテキストデータをLightGBMやXGBoost等の入力として使用できるようになります。

2. TF-IDF

TF-IDF(索引語頻度逆文書頻度)とは文章中に出てくる単語の頻度を使って文章をベクトル化する手法です。

ただ頻度を使って特徴量を計算すると、文章の特徴にあまり関係ない高頻度の単語(say, tell等のよく使われる単語など)が邪魔をして良い特徴量を得られないことがあります。TF-IDFはこれを改善したものになります。

こちらの記事(機械学習 〜 テキスト特徴量(CountVectorizer, TfidfVectorizer) 〜)が分かりやすいと思います。

3. TfidfVectorizerクラス

TF-IDFを使ったテキストベクトル化手法の実装の1つとして、sklearn.feature_extraction.text.TfidfVectorizerを使うものがあります。

以下はモデルへの影響が大きい大事なパラメーターです。

名前
analyzer ‘word’, ‘char’, ‘char_wb’のどれかもしくは関数。デフォルトは’word’
ngram_range (int, int)形式のタプル。デフォルトは(1,1)
max_features intの値。デフォルトはNone

各パラメーターについてサンプルプログラムを元に説明します。

4. サンプルで見るTfidfVectorizer

サンプルコードを見てみましょう。まずはデフォルトでTfidfVectorizerクラスを使う例です。

from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.sparse import hstack

X = ['This is a pen.', 'This is not a hat']
vectorizer = TfidfVectorizer() # デフォルト設定
vectorizer.fit(X)
features = vectorizer.transform(X)
print(type(features))
print(features)

実行すると以下のような出力が出てくると思います。

$ python vectorize.py
  <class 'scipy.sparse.csr.csr_matrix'>
  (0, 4)        0.5015489070943787
  (0, 3)        0.7049094889309326
  (0, 1)        0.5015489070943787
  (1, 4)        0.40993714596036396
  (1, 2)        0.5761523551647353
  (1, 1)        0.40993714596036396
  (1, 0)        0.5761523551647353

This is a pen.というテキストとThis is not a hatという2つのテキストが入ったリストを使って各文章をTF-IDFでベクトル化しています。

4.1 analyzerをセットする

analyzerを変えてどのように動くか確認してみます。

X = ['This is a pen.', 'This is not a hat']
vectorizer = TfidfVectorizer(
        analyzer='char'
        )
vectorizer.fit(X)
features = vectorizer.transform(X)
print(features)

analyzer='char'をセットして実行します。

$ python vectorize.py
  (0, 10)       0.19271437346720413
  (0, 9)        0.38542874693440826
  (0, 8)        0.2708533277390812
  (0, 6)        0.19271437346720413
  (0, 5)        0.38542874693440826
  (0, 4)        0.19271437346720413
  (0, 3)        0.2708533277390812
  (0, 2)        0.19271437346720413
  (0, 1)        0.2708533277390812
  (0, 0)        0.5781431204016123
  (1, 10)       0.45239384799197957
  (1, 9)        0.30159589866131975
  (1, 7)        0.21194125615850537
  (1, 6)        0.15079794933065988
  (1, 5)        0.30159589866131975
  (1, 4)        0.30159589866131975
  (1, 2)        0.30159589866131975
  (1, 0)        0.6031917973226395

analyzerのデフォルトは’word’なので先程の例では単語数分の次元が出力されていましたが、‘char’にセットすることで文字単位のベクトルを出力することができます。

4.2 ngram_rangeをセットする

ここではngram_range=(1, 2)をセットします。

この例だと1単語ずつの特徴量作成と連続した2単語をまとめた組み合わせでベクトル化を行うことができます。これをセットすると特徴量は増えます

from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.sparse import hstack

X = ['This is a pen.', 'This is not a hat']
vectorizer = TfidfVectorizer(
        ngram_range=(1, 2)
        )
vectorizer.fit(X)
features = vectorizer.transform(X)
print(features)

出力は以下のようになります。

$ python vectorizer.py
  (0, 8)        0.3793034928087496
  (0, 7)        0.3793034928087496
  (0, 6)        0.5330978245262535
  (0, 3)        0.5330978245262535
  (0, 1)        0.3793034928087496
  (1, 8)        0.3028728072833121
  (1, 7)        0.3028728072833121
  (1, 5)        0.42567716283146345
  (1, 4)        0.42567716283146345
  (1, 2)        0.42567716283146345
  (1, 1)        0.3028728072833121
  (1, 0)        0.42567716283146345

['This', 'is', 'a', 'pen', 'not', 'hat']だけじゃなく['This is', 'is a', 'a pen', 'is not', 'not a', 'a hat']も特徴量に含まれたことになります。

4.3 max_featuresをセットする

TfidfVectorizerのオプションとしてmax_featuresをセットします。下を見ると分かるのですがmax_featuresを設定すると特徴量を減らすことができます。

例えば合計単語数が100万単語のデータセットを使う時に特徴量を1000次元とかに設定できます。

from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.sparse import hstack

X = ['This is a pen.', 'This is not a hat']
vectorizer = TfidfVectorizer(
        max_features=4
        )
vectorizer.fit(X)
features = vectorizer.transform(X)
print(features)

実行すると以下になります。

$ python vectorize.py
  (0, 3)        0.5015489070943787
  (0, 2)        0.7049094889309326
  (0, 0)        0.5015489070943787
  (1, 3)        0.5015489070943787
  (1, 1)        0.7049094889309326
  (1, 0)        0.5015489070943787

max_features=4を設定したので特徴量が減ったのが分かると思います。

デフォルトの例と見比べてみると分かるように単語の出現頻度順に上から4つを使ったデータになっていますね。

5. 実践的なベクトル化関数

さて、実際の分類問題ではどのようにTfidfVectorizerが使われているのか、Kaggleのnotebookを例に見ていきましょう。

from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.sparse import hstack

def vectorize(X):
    word_vectorizer = TfidfVectorizer(
        sublinear_tf=True,
        strip_accents='unicode',
        analyzer='word',
        token_pattern=r'\w{1,}',
        stop_words='english',
        ngram_range=(1, 3),
        max_features=30000)
    word_vectorizer.fit(X)
    train_word_features = word_vectorizer.transform(X)

    char_vectorizer = TfidfVectorizer(
        sublinear_tf=True,
        strip_accents='unicode',
        analyzer='char',
        stop_words='english',
        ngram_range=(2, 5),
        max_features=30000)
    char_vectorizer.fit(X)

    train_char_features = char_vectorizer.transform(X)

    vectorized = hstack([train_char_features, train_word_features])
    return vectorized

上記のnotebookを参考に関数化したものが上のvectorize()関数です。関数の入力はTfidfVectorizer.fitの入力と同じiterableなオブジェクトです。

単語ベースのベクトルと文字ベースのベクトルを組み合わせている部分に工夫を感じます。

なおこれは大きめのデータセット向きのパラメーターなので小さめのデータさえっとのときはmax_featuresの値を小さくしてみるなどすればいいと思います。

6. 参考