Bag of Words

import pandas as pd

テキストに対して、タスクを達成するのに 十分な言語的特性を捉えた表現 をもった特徴ベクトルに変換することが目的です。

なるべく次元を小さくして次元の呪いを受けにくい表現にすることも重要です。

Bag of Words (BoW)

BoWはテキストを単語の集合として表現する方法です。

sklearn.feature_extraction.text.CountVectorizer を使います。 CountVectorizerはtokenizer引数をとり、デフォルトでは ドキュメント に書かれている通り次のように動きます。

The default configuration tokenizes the string by extracting words of at least 2 letters.

このtokenizerを日本語用に置き換えて利用します。

# トークナイザを定義
import spacy

nlp = spacy.load("ja_core_news_sm")

def tokenize(text):
   return [token.text for token in nlp(text)]
2022-05-09 02:46:55.703718: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory
2022-05-09 02:46:55.703825: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
# tokenizerに日本語用のトークナイザを渡して初期化します
from sklearn.feature_extraction.text import CountVectorizer

cv = CountVectorizer(tokenizer=tokenize)

Note

もしも保存されているデータが事前に空白でトークナイズされているのであれば、 tokenizerには CountVectorizer(tokenizer=str.split) と設定してください。

tokenizerに何も指定しないと、 token_pattern のデフォルトの挙動で 一文字のトークンは削除されてしまいます。 この挙動は日本語では不適切になります。

Feature extractionの Some tips and tricks に str.split について記載があります。

texts = ["テスト用のテキストでのテストです。", "複数をリストで渡します"]
cv.fit(texts)
/usr/local/lib/python3.8/site-packages/sklearn/feature_extraction/text.py:516: UserWarning: The parameter 'token_pattern' will not be used since 'tokenizer' is not None'
  warnings.warn(
CountVectorizer(tokenizer=<function tokenize at 0x7fde6a625430>)
cv.get_feature_names_out()
array(['。', 'で', 'です', 'の', 'ます', 'を', 'テキスト', 'テスト', 'リスト', '渡し', '用',
       '複数'], dtype=object)
cv.vocabulary_
{'テスト': 7,
 '用': 10,
 'の': 3,
 'テキスト': 6,
 'で': 1,
 'です': 2,
 '。': 0,
 '複数': 11,
 'を': 5,
 'リスト': 8,
 '渡し': 9,
 'ます': 4}
bow = cv.transform(texts)
bow
<2x12 sparse matrix of type '<class 'numpy.int64'>'
	with 13 stored elements in Compressed Sparse Row format>
bow.shape
(2, 12)

結果をnumpy arrayに変換して表示します。

bow.toarray()
array([[1, 1, 1, 2, 0, 0, 1, 2, 0, 0, 1, 0],
       [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1]])
pd.DataFrame(bow.toarray(), columns=cv.get_feature_names_out())
です ます テキスト テスト リスト 渡し 複数
0 1 1 1 2 0 0 1 2 0 0 1 0
1 0 1 0 0 1 1 0 0 1 1 0 1

CountVectorizerはデフォルトでは頻度を考慮して単語をカウントします。 頻度を考慮せずに単語が出現したかどうかのみを考える場合はbinaryオプションを指定して初期化します。

cv = CountVectorizer(tokenizer=tokenize, binary=True)
cv.fit_transform(["テスト用のテキストでのテストです。", "複数をリストで渡します"]).toarray()
array([[1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0],
       [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1]])

Bag of N-gram

N-gramにより小さな単位での語順を考慮できるので、テキストの意味を小さな単位で捉えることができます。 一方で、語彙数が増えてしまうため特徴量が高次元になり、次元の呪いの問題に注意する必要があります。

cv = CountVectorizer(tokenizer=tokenize, ngram_range=(1, 2))
bon = cv.fit_transform(texts)
pd.DataFrame(bon.toarray(), columns=cv.get_feature_names_out())
で の で 渡し です です 。 の テキスト の テスト ます ... テスト です テスト 用 リスト リスト で 渡し 渡し ます 用 の 複数 複数 を
0 1 1 1 0 1 1 2 1 1 0 ... 1 1 0 0 0 0 1 1 0 0
1 0 1 0 1 0 0 0 0 0 1 ... 0 0 1 1 1 1 0 0 1 1

2 rows × 25 columns

TF-IDF

単語の重要度を考慮してベクトル化します。 sklearn.feature_extraction.text.TfidfVectorizer を使います。

from sklearn.feature_extraction.text import TfidfVectorizer

cv = TfidfVectorizer(tokenizer=tokenize)
tfidf = cv.fit_transform(texts)
pd.DataFrame(tfidf.toarray(), columns=cv.get_feature_names_out())
です ます テキスト テスト リスト 渡し 複数
0 0.282772 0.201195 0.282772 0.565544 0.00000 0.00000 0.282772 0.565544 0.00000 0.00000 0.282772 0.00000
1 0.000000 0.303216 0.000000 0.000000 0.42616 0.42616 0.000000 0.000000 0.42616 0.42616 0.000000 0.42616

Note

出力からスケーリングされていることがわかります。

TfidfはTransformerとしても使うことができます。 この使い方をする場合には、CountVectorizerのあとに sklearn.feature_extraction.text.TfidfTransformer を指定します。 Pipelineとして構成するのが便利です。

from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.pipeline import Pipeline

pipe = Pipeline([
    ("count", CountVectorizer(tokenizer=tokenize)),
    ("tfidf", TfidfTransformer()),
])
tfidf_transform = pipe.fit_transform(texts)
pd.DataFrame(tfidf_transform.toarray(), columns=cv.get_feature_names_out())
です ます テキスト テスト リスト 渡し 複数
0 0.282772 0.201195 0.282772 0.565544 0.00000 0.00000 0.282772 0.565544 0.00000 0.00000 0.282772 0.00000
1 0.000000 0.303216 0.000000 0.000000 0.42616 0.42616 0.000000 0.000000 0.42616 0.42616 0.000000 0.42616