Edited at

TF-IDF変換を保存する

More than 1 year has passed since last update.


前置き

TF-IDF変換を未知の入力に対して使うため、変換した内容を保存する必要があるため、今回はその保存方法について残しておく。


mecabで形態素解析

tyamagu2.xyzさんの記事を引用してクラス定義してみます。

MeCabをimport忘れずに

※ストップワードは、京大のSlothlibを使っているので、引っ張って来てください

import MeCab

'''
MeCabで名詞だけを抜き取るクラスの定義
辞書は"mecab-ipadic-neologd"を使用
'''

class WordDividor:
INDEX_CATEGORY = 0
TARGET_CATEGORY = ['名詞']

def __init__(self, dictionary="mecabrc"):
self.dictionary = dictionary
self.tagger = MeCab.Tagger(self.dictionary)

def extract_words(self, text):
if not text:
return []

# テキストの余分なデータの削除(ex : httpなど)
text = re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
text = re.sub(r'[!-~]', "", text) # 半角記号,数字,英字
text = re.sub(r'[︰-@]', "", text) # 全角記号
text = re.sub('\n', " ", text) # 改行文字

words = []
# 文字列がGCされるのを防ぐ
self.tagger.parse('')
node = self.tagger.parseToNode(text)
while node:
# ","で文字列の分割を行い, ターゲットになる品詞と比較を行う.
if node.feature.split(',')[self.INDEX_CATEGORY] in self.TARGET_CATEGORY:
# ストップワードの判定を行う(stopwordsに引っかかってない名詞を入れる)
if node.surface not in stopwords:
words.append(node.surface)
node = node.next

return words

使う時は、

wd = WordDividor()

extract_word = wd.extract_word(text="ほげほげテスト")

このように使うと、一時的に用意した変数extract_wordにmecabにより形態素解析され、名詞だけ抜き出されたものが入ります。


TF-IDF変換

次に、TF-IDF変換を行います。

これも下記のようにクラス定義してあげます.

TfidfVectorizerをimport忘れずに

from sklearn.feature_extraction.text import TfidfVectorizer

'''
TF-IDF変換を行うクラスの定義
1. TfidfVectorizerインスタンスの作成(analyzerにmecabの形態素解析手法を指定)
2. fitメソッドを行ったTfidfVectorizerインスタンスを返す(新しいデータに用いるため)
'''

class WordPreProcessor:

# コンストラクタ
def __init__(self, analyze_method):
self.tfidf = TfidfVectorizer(analyzer=analyze_method, use_idf=True, norm='l2', smooth_idf=True)

def wordspreprocess(self, raw_data):
# fitの状態を格納(TfidfVectorizerインスタンスが格納される)
tfidf_condition = self.tfidf.fit(raw_data)
# 形態素解析されたワードを、fitで作ったTF-IDF特徴空間行列に変換する
tfidf_vector = self.tfidf.transform(raw_data).toarray()

return tfidf_condition, np.matrix(tfidf_vector)

ここで2つpointがあり、

英語などを形態素解析する場合はそもそもMeCabが必要ではないので、python機械学習プログラミングなんかは

TfidfVectorizer(analyzer='word', use_idf=True, norm='l2', smooth_idf=True)

デフォルト指定で生成しています。

なのでインスタンス生成する際は

wpp = WordPreProcessor(analyze_method=wd.extract_words)

こんな感じで、wdインスタンスのメソッドを指定してあげます。


  • fit_transformを使わずfitだけ使う

fitメソッドは公式ライブラリを引用すると


Learn vocabulary and idf from training set.


訓練データからidfと語彙を学習します。返り値は学習後のTfidfVectorizerインスタンスです。

そのインスタンスを使ってTF-IDF変換を行うメソッドがtransformメソッドになります。

公式のライブラリではこの2つを一緒くたにしたfit_transformメソッドが親切に用意されているので、用途で使い分けるべきだと思います。

ということで、fitした状態をpickleなどで保存すれば、新しい文章を保存した規準でTF−IDF変換できるということです。


TF-IDF保存

# オブジェクト保存ライブラリの導入 

import pickle
# インスタンスの生成
wpp = WordPreProcessor(analyze_method=wd.extract_words)
tfidf_condition, feature_matrix = wpp.wordspreprocess(raw_data="ほげほげテスト", is_dimension_compact=is_dimension_compact)
# 保存
pickle.dump(tfidf_condition, open("tfidf.pkl", "wb"))

しかしながらこれだとエラーになります。

SwigPyObject????

どうやら、analyze_methodに形態素クラスのメソッドを指定しているのでクラスインスタンスのネストになっているため保存できないということなのだ。

なので、この解決策は

tfidf_condition.set_params(analyzer='word')

# 保存
pickle.dump(tfidf_condition, open("tfidf.pkl", "wb"))

一旦analyzer='word'と置き換えることで保存する('word'はデフォルトである)

使う際は,

from dataPreProcessor import WordDividor

import pickle

def main():
wd = WordDividor()
tfidf_vectorizer = pickle.load(open("tfidf.pkl", "rb"))
# 一時的に置き換えていたanalyzerを指定
tfidf_vectorizer.set_params(analyzer=wd.extract_words)
tfidf_vector = tfidf_vectorizer.transform(['新しくものあるほげほげテスト'])

if __name__ == '__main__':
main()

こんな感じで、保存したものをロードして、set_paramsメソッドを使って再度MeCabクラスのメソッドを指定し直して、使ってあげる。

fitで学習済みなので、analyzeメソッドが一時的に変更されても問題ありません。


最後に

意外と回りくどいやり方をしているので、そもそもpickleがナンセンスとか他にいい方法があればコメントください。


参考