感情分析のためのモデルを作成していきます。
ここでは、Word embeddingを使った特徴量を作成し、それをつかってロジスティック回帰を学習して、精度が上がるかを確認します。
目次
はじめに
Word Embeddingとは
Word embeddingとは、単語を固定長の連続的なベクトルにマッピングすることで、単語の意味を捉えるための技術やそのベクトルを指します。自然言語処理タスクにおける、単語の表現方法の一つです。
word embeddingsには、意味的に類似した単語はベクトル空間上で近くに位置するという性質があります。例えば、”ハンバーガー”と”ファストフード”のような意味的に関連する単語は、ベクトル空間上で互いに近い位置に配置されることが期待されます。
FastTextとは
FastTextは、Facebook AI Research (FAIR) によって開発されたWord embeddingとテキスト分類のためのライブラリです。日本語にも対応しており、Word2Vecなどの先発のモデルよりも学習が早いというメリットがあります。
実装
Word embeddingを使って、精度が上がるかを検証してみます。
FastTextのダウンロード
fastTextの公式ページからモデルをダウンロードします。ページの下部、「Models」の中にあるJapaneseモデルを選択してダウンロードします。
bin形式と、text形式の2つの形式がありますが、ここではbin形式をダウンロードします。(ファイルサイズは4GB程度あります。)
・gz形式で圧縮されているので、ダウンロード後に圧縮ファイルを展開します。
データの読み込み
前々回の記事で作成した、前処理済みのデータセットを使用します。
■python
import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score import ast
■python
# データの読み込み
df = pd.read_csv('../data/interim/chabsa-sentiment-analysis-with-kfold-preprocessed.csv')
# tokens列のデータ型をstrからlistに変換
df['tokens'] = df['tokens'].apply(lambda x: ast.literal_eval(x))
df.head()
TF-IDF特徴量を使った精度の計算
TF-IDF特徴量を作成する
■python
def sentence_to_vec(list_words, ft_model):
"""
文章を受け取り、ベクトル化する(ベクトルの次元数は300)
:param list_words: 単語のリスト
:param ft_model: fastTextの学習済みモデル
"""
# 単語埋め込みを格納するリスト
M = []
for w in list_words:
# 各単語に対して、単語埋め込みを取得する
M.append(ft_model.get_word_vector(w))
# ベクトルがなかった場合はゼロベクトルを返す
if len(M) == 0:
return np.zeros(300)
# 単語埋め込みのリストをNumpy配列に変換する
M = np.array(M)
# 文章の各単語埋め込みの合計を取り、それを文章ベクトルとする
v = M.sum(axis=0)
# 正規化する
return v / np.sqrt((v ** 2).sum())
■python
# fastTextの学習済みモデルを読み込む
ft_model = fasttext.load_model("../model/cc.ja.300.bin")
vectors = []
for tokens in df.tokens.values:
vectors.append(sentence_to_vec(tokens, ft_model))
# ベクトルをNumpy配列に変換する
vectors = np.array(vectors)
予測を行う
■python
# foldごとに以下の処理を行う
# 1. 学習用データと検証用データに分割する
# 2. モデルを学習する
# 3. 正解率を計算する
y = df.rating.values
# foldごとに処理を行う
for fold_ in range(5):
# 学習用データと検証用データに分割する
X_train = vectors[df.kfold != fold_, :]
y_train = y[df.kfold != fold_]
X_valid = vectors[df.kfold == fold_, :]
y_valid = y[df.kfold == fold_]
# モデルの定義
lr = LogisticRegression(penalty='l1', solver='liblinear')
# モデルの学習
lr.fit(X_train, y_train)
# 検証用データに対する予測を行う
preds = lr.predict(X_valid)
# 正解率(Accuracy)を計算する
accuracy = accuracy_score(
y_valid,
preds
)
# 検証用データの予測値を計算する
preds = lr.predict(X_valid)
df.loc[df.kfold == fold_, 'preds'] = preds
# 正解率を出力する
print(f'Fold={fold_} 検証用データに対する正解率: {accuracy:.4f}')
# トータルの正解率を出力する
accuracy = accuracy_score(
df.rating,
df.preds
)
print(f'トータルの正解率: {accuracy:.4f}')
Fold=0 検証用データに対する正解率: 0.8171 Fold=1 検証用データに対する正解率: 0.8117 Fold=2 検証用データに対する正解率: 0.8188 Fold=3 検証用データに対する正解率: 0.8238 Fold=4 検証用データに対する正解率: 0.8078 トータルの正解率: 0.8159
まとめ
TF-IDFを使った場合と比較して、正解率は90.05% → 81.59%に下がってしまいました。Word embeddingは強力な手法であるものの、今回の問題に対してはTF-IDFの方が良い精度が出るという結果が得られました。
これは、今回の問題のラベルの付け方に起因している事が予想できます。今回の問題では、positive/negativeのラベル付けを、「単語ごとのpositive/negativeの文章内の総和」として計算しています。つまり、特定の単語が使われているかどうかが、直接ラベルの値に影響しています。そのため、今回の問題ではWord embeddingよりも、単語の出現割合を扱ったBag of WordsやTF-IDFのほうが良い精度が出ていると考えられます。
