本記事ではscikit-learnを用いて自然言語処理モデルを構築する際に、tfidfに加えてそれ以外の特徴量を利用する方法をサンプルコード付きで紹介します。 scikit-learnで自然言語処理モデルを構築する際は、scikit-learnで用意されているクラスを用いて簡単にテキストをtfidfベクトルに変換することができます。 さらにscikit-learnでは種類の異なる特徴を容易に組み合わせるためのAPIも提供しています。 このAPIを用いることでtfidfに加えて、独自で実装した特徴量を考慮できます。 本記事を読むことで、独自の特徴を抽出するクラスを定義する方法に加えて、複数の特徴を組み合わせて利用するための方法を理解できます。

目次

本記事では以下のバージョンに基づいています。

scikit-learn==0.23.2

tfidf

tfidfは、機械学習を用いた自然言語処理や情報検索において、広く用いられているテキストの表現1 の一つです。 tfidfは単語2に対してある種の重要度を与える方法で、他のテキストにはあまり出現せず、そのテキストにはよく出現する単語はそのテキストを特徴づける単語であるという仮定に基づき、高いスコアを与えます。

例えば「です」や「ます」などの単語は一つのテキストに多く出現する一方で、他のテキストにも多く出現するため、tfidfは低くなり、スポーツ関係のテキストであれば、「スコア」や「ボール」などの単語が多く出現する一方で、他のテキストにはそれほど出現するわけではないので、スポーツ関係のテキストにおいては高いtfidf値となります。

scikit-learnではTfidfVectorizerを用いることで、学習データのテキストから頻度などの統計量を構築したり、テキストをtfidfベクトルに変換することができます。

from sklearn.feature_extraction.text import TfidfVectorizer

tfidf以外の特徴量

tfidfはテキストに出現する単語に対して、ある種の重要度を与える方法でした。 テキストをスポーツや政治・経済などの予め決めたカテゴリに分類するような文書分類問題には有効な方法だと思います。 しかし、文書分類問題には上記のようなカテゴリに分類する問題の他、例えばスパムメールの分類や、エッセイの品質の分類などもあります。 たとえばエッセイの品質を特徴づけるのが、内容を表す単語に加えて、テキストがある程度長い字数である可能性もあります3

そこで、単語の出現を考慮するtfidfに加えて、文書の長さも利用して分類をおこなうことを考えます。 tfidfはscikit-learnのTfidfVectorizerを用いれば容易に利用できます。 そのため、scikit-learnでは提供されていない、文書の長さを特徴量として抽出するクラスを別途定義します。

scikit-learnのAPIに従った独自の特徴抽出を実施するクラスを定義するにはTransformerMixinを継承します。 また定義したクラスではfitメソッドおよびtransformerメソッドを実装します。

from sklearn.base import TransformerMixin
from sklearn.preprocessing import MinMaxScaler


class CustomVectorizer(TransformerMixin):
    def __init__(self):
        super().__init__()
        self.scaler = MinMaxScaler()

    def fit(self, raw_documents, y=None):
        lengths = np.zeros((len(raw_documents), 1))
        for doc_id, document in enumerate(raw_documents):
            length = len(document)
            lengths[doc_id, 0] = length
        self.scaler.fit(lengths)
        return self

    def transform(self, raw_documents):
        lengths = np.zeros((len(raw_documents), 1))
        for doc_id, document in enumerate(raw_documents):
            length = len(document)
            lengths[doc_id, 0] = length
        lengths = self.scaler.transform(lengths)
        return lengths

FeatureUnion

tfidfとそれ以外の特徴量を組み合わせるにはFeatureUnionを用います。

from sklearn.pipeline import FeatureUnion


vectorizers = [
  ('tfidf', TfidfVectorizer()),
  ('length', CustomVectorizer())
]
vectorizer = FeatureUnion(vectorizers)

またはmake_unionを使っても同様のことができます。

from sklearn.pipeline import make_union

vectorizer = FeatureUnion(
  TfidfVectorizer(),
  CustomVectorizer()
)

利用例

実際にテキストをベクトルに変換してみます。

texts = [
    'A short text',
    'This text describes long detail',
]

vectorizer = make_union(
    TfidfVectorizer(),
    CustomVectorizer()
)

x = vectorizer.fit_transform(texts)

print(vectorizer.transformer_list[0][1].get_feature_names())
print('size of tfidf vector', len(vectorizer.transformer_list[0][1].get_feature_names()))
print(x)

実行結果は以下のようになります。

['describes', 'detail', 'long', 'short', 'text', 'this', 'very']
size of tfidf vector 7
(0, 3)        0.8148024746671689
(0, 4)        0.5797386715376657
(1, 0)        0.42615959880289433
(1, 1)        0.42615959880289433
(1, 2)        0.42615959880289433
(1, 4)        0.3032160644503863
(1, 5)        0.42615959880289433
(1, 6)        0.42615959880289433
(1, 7)        1.0

まずTfidfVectorizerでは、7つの単語を対象として扱っていることがわかります。

テキストの長さに対応する次元は8 (0から始まるので7番目の次元) となります。 今回はテキストの長さをMinMaxScalerで正規化しています。 texts[0]の正規化後長さは0となります。疎行列表現では値が0の特徴量は効率化のため扱いません。そのため出力結果には7番目の次元の値は出力されません。 texts[1]の7番目の特徴量、つまり (1, 7) で始まる以下の行が正規化後のテキストの長さとなり、tfidfと合わせて特徴量として考慮されている事がわかります。

おわり

本記事ではscikit-learnを活用して自然言語処理モデルを構築する際に、tfidfに加えて、それ以外の特徴量も使うための方法を紹介しました。 具体的には、利用したい特徴を抽出するvectorizerを定義し、TfidfVectorizerと合わせてFeatureUnionインスタンスを作成する方法をサンプルコード付きで説明しました。


  1. もっと具体的に言うとベクトル ↩︎

  2. scikit-learnではn個の単語の連続であるn-gramも対象とします ↩︎

  3. テキストが長ければ高品質であるとは言えないかもしれませんが、あまりに短いテキストはエッセイとして低品質な可能性があるかもしれません ↩︎


関連記事






最近の記事