Python
自然言語処理
機械学習
scikit-learn

自然言語処理の分類タスクを試行錯誤する

More than 1 year has passed since last update.

自然言語処理 Advent Calendar 2017の23日目の記事です。


自然言語処理で必要なフロー


  • データを集める

  • 前処理 (x)

  • アルゴリズム適用 (x)

  • 過、未学習への対策

  • パラメーターチューニング (x)

  • モデルの評価

機械学習の一般的なフローは上記で今回は色んなパターンでxの部分を試行錯誤したいと思います。

タスクはLivedoorが提供しているニュースの文章とそのカテゴリを分類する機械学習プログラムを作ります。


github

https://github.com/akichim21/natural-language-classifier


初期バージョンのコード


01_janome_bow_lr.py

# This Python file uses the following encoding: utf-8

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from preprocessings.tokenizer import JanomeTokenizer
from preprocessings.normalizer import normalize
from preprocessings.livedoor import load_df

# janomeでトークナイズ
# BOWで文をベクトル化
# LogisticRegressionで分類

# janomeでトークナイズ
janome = JanomeTokenizer()
def tokenize(word):
tokens = [normalize(janome.surface(token)) for token in janome.tokenize(word)]
return " ".join(tokens)

# livedoorの記事をラベル付きでDataFrameとして読み込み
df = load_df()

# 文全てをtokenize
df['docs'] = df['docs'].apply(tokenize)

# BOWで文をベクトル化
count = CountVectorizer()
X_count = count.fit_transform(df['docs'].values)

# トレーニング:テスト = 8:2で分ける
X_train, X_test, Y_train, Y_test = train_test_split(X_count, df['labels'], test_size=0.2,random_state=3)

# LogisticRegressionで分類
clf = LogisticRegression()
clf.fit(X_train, Y_train)

# テストデータで正確性(accuracy)を表示
print(clf.score(X_test, Y_test))



解説

df = load_df()


  • データを集める


    • 今回はlivedoorの記事の文章とカテゴリを使う



# janomeでトークナイズ

janome = JanomeTokenizer()
def tokenize(word):
tokens = [normalize(janome.surface(token)) for token in janome.tokenize(word)]
return " ".join(tokens)

# 文全てをtokenize
df['docs'] = df['docs'].apply(tokenize)

# BOWで文をベクトル化
count = CountVectorizer()
X_count = count.fit_transform(df['docs'].values)


  • 前処理


    • 文章の特徴量を取り出したいので、単語に分割してベクトル化(コンピュータが扱える数値化)する

    • 単語分割でjanome, ベクトル化でBag Of Wordを使う



# LogisticRegressionで分類

clf = LogisticRegression()
clf.fit(X_train, Y_train)


  • アルゴリズム適用


    • X_train: 文章をベクトル化した行列

    • Y_train: categoryの数値

    • 今回はLogisticRegressionを使う



# トレーニング:テスト = 8:2で分ける

X_train, X_test, Y_train, Y_test = train_test_split(X_count, df['labels'], test_size=0.2,random_state=3)

# テストデータで正確性(accuracy)を表示
print(clf.score(X_test, Y_test))


  • モデルの評価


    • 全体のデータをテストデータとトレーニングデータに分ける

    • accuracyで性能評価




前処理を試行錯誤

mecab = MeCabTokenizer()

def tokenize(word):
tokens = [mecab.surface(token) for token in mecab.tokenize(word)]
return " ".join(tokens)

形態素解析をjanomeからmecabに変えてみる

# TFIDFで文をベクトル化

count = CountVectorizer()
X_count = count.fit_transform(df['docs'].values)
tfidf_transformer = TfidfTransformer()
X_tfidf = tfidf_transformer.fit_transform(X_count)

ベクトル化をTFIDFにしてみる

さらに知りたい

形態素解析

ベクトル化


アルゴリズムを試行錯誤

# MultinomialNBで分類

nb = MultinomialNB()
nb.fit(X_train, Y_train)

# SVMで分類

svm = SVC()
svm.fit(X_train, Y_train)

# LogisticRegressionで分類

clf = LogisticRegression()
clf.fit(X_train, Y_train)

さらに知りたい


 パラメータチューニング

grid_list = [

# CountVectorizer, LogisticRegressionを検証
# CountVectorizer: tokenizer
# Logisticregression: penalty, C
{
'param_grid': {
'vect__ngram_range': [(1, 1)],
'vect__tokenizer': [tokenize, tokenize_by_pos],
'clf__penalty': ['l1', 'l2'],
'clf__C': [1.0, 10.0, 50.0, 100.0]
},
'pipeline': Pipeline([
('vect', CountVectorizer()),
('clf', LogisticRegression())
])
},
# TfidfVectorizer, LogisticRegressionを検証
# TfidfVectorizer: tokenizer, use_idf
# Logisticregression: penalty, C
{
'param_grid': {
'vect__ngram_range': [(1, 1)],
'vect__tokenizer': [tokenize, tokenize_by_pos],
'vect__use_idf': [True, False],
'clf__penalty': ['l1', 'l2'],
'clf__C': [1.0, 10.0, 50.0, 100.0]
},
'pipeline': Pipeline([
('vect', TfidfVectorizer()),
('clf', LogisticRegression())
])
},
]
for grid in grid_list:
gs = GridSearchCV(grid["pipeline"], grid["param_grid"], n_jobs = -1, cv = 10, scoring = 'accuracy', verbose = 1)
gs.fit(X_train, Y_train)
print('Best parameter set: %s' % gs.best_params_)
print('CV Accuracy: %.3f' % gs.best_score_)
clf = gs.best_estimator_
print('Test CV Accuracy: %.3f' % clf.score(X_test, Y_test))

アルゴリズム、ベクトル化、パラメータを複数実行して評価し、一番良いものを採用する


まとめ

自然言語処理の概要と試行錯誤の例をあげてみました。

精度が上がらないときはどこがボトルネックか、どういう手法が他にあるかを把握しながらやるとうねるかと思います。


Appendix

Scikit learnで学ぶ機械学習入門

自然言語処理における前処理の種類とその威力

Python機械学習プログラミング 達人データサイエンティストによる理論と実践