目次
- 前回の結果の振り返り
- 今回やること
- データの前処理
- データの準備
- テキストの前処理
- 精度の比較
- 文章の分かち書き
- クロスバリデーションのための分割
- モデルの作成と評価
- まとめ
前回の結果の振り返り
前回の記事では、感情分析のためのベンチマークとして、ロジスティック回帰と、Bag of Words特徴量の組み合わせのモデルを作成しました。
ベンチマークの予測精度として、クロスバリデーションを行ったときの「検証データに対する正解率:88.8%」という結果が得られました。
今回やること
前回は、特にテキストデータを加工せず、そのままBag of Words特徴量を作成しました。今回は、テキストデータを前処理した上で、BoW特徴量を作成して、精度が上がるかを試してみます。
データの前処理
データの準備
■python
import pandas as pd import spacy from sklearn.model_selection import StratifiedKFold from sklearn.feature_extraction.text import CountVectorizer from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score
■python
# データの読み込み
df = pd.read_csv('../data/interim/chabsa-sentiment-analysis.csv')
df.head()

テキストの前処理
ここが今回の記事のメイン部分になります。テキストから意味を抜き出しやすくするために、テキストデータを前処理します。具体的には、以下の処理を行います。
- 全角・半角文字を統一する
- 桁区切りの数字を削除する
- テキストを小文字に寄せる
- 記号をスペースに変換する
全角・半角文字を統一する
■python
# 全角・半角文字を統一する
import neologdn
def apply_neologdn(text):
return neologdn.normalize(text)
# 全角・半角文字を統一する
df['sentence'] = df['sentence'].apply(apply_neologdn)
df.head()

桁区切りの数字を削除する
■python
# 桁区切りの数字を削除する
import re
def apply_remove_comma(text):
"""文字列中の数値を0に置き換える"""
text_removed_comma = re.sub(r'(\d+)[,.](\d+)', r'\1\2', text)
text_replaced_number = re.sub(r'\d+', '0', text_removed_comma)
return text_replaced_number
# 数字を削除する
df['sentence'] = df['sentence'].apply(apply_remove_comma)
df.head()

テキストを小文字に寄せる
■python
# テキストを小文字に寄せる
def apply_lower(text):
return text.lower()
# テキスト中の英単語を小文字に寄せる
df['sentence'] = df['sentence'].apply(apply_lower)
df.head()

記号をスペースに変換する
■python
# 記号をスペースに変換する
def apply_replace_symbol(text):
"""記号をスペースに置き換える"""
replaced_text = re.sub(r'[!-/:-@[-`{-~]', ' ', text)
return replaced_text
# 記号をスペースに変換する
df['sentence'] = df['sentence'].apply(apply_replace_symbol)
df.head()

改行コードを削除する
■python
# 改行コードを削除する
def apply_remove_newline(text):
"""改行コードを削除する"""
removed_text = re.sub(r'\\n|\n', '', text)
return removed_text
# 記号をスペースに変換する
df['sentence'] = df['sentence'].apply(apply_replace_symbol)
df.head()

精度の比較
前処理したテキストを使って、Bag of Words特徴量を作成し、モデルの精度が向上したかを確認します。
文章の分かち書き
■python
# sentence列を分かち書きする
nlp = spacy.load('ja_core_news_md')
def apply_nlp(text):
doc = nlp(text)
return [token.text for token in doc]
# tokensに分かち書きされた単語が入る
df['tokens'] = df['sentence'].apply(apply_nlp)
# 分かち書きのリストが作成できているかを確認する
df.head(5)

クロスバリデーションのための分割
■python
# クロスバリデーションのために、kfoldの列を追加する
# k-fold
df['kfold'] = -1
# データをシャッフルする
df = df.sample(frac=1).reset_index(drop=True)
# StratifiedKFoldを使う
skf = StratifiedKFold(
n_splits=5,
shuffle=False
)
# データを分割する
for fold, (train_idx, val_idx) in enumerate(skf.split(X=df, y=df.rating.values)):
# foldの列にkfoldの分割番号を入れる
df.loc[val_idx, 'kfold'] = fold
# kfoldの分割番号が正しく振られていることを確認する
df.kfold.value_counts()
# データを書き出す
df.to_csv('../data/interim/chabsa-sentiment-analysis-with-kfold-preprocessed.csv', index=False)
モデルの作成と評価
前回の記事と同様に、分割したデータセットごとに、以下の処理を実行します。
- 学習用データと検証用データに分割する
- Bag of Words特徴量を作成する
- モデルを学習する
- 正解率を計算する
評価指標としては、正解率(Accuracy)を使用します。
■python
# foldごとに以下の処理を行う
# 1. 学習用データと検証用データに分割する
# 2. BoW特徴量を作成する
# 3. モデルを学習する
# 4. 正解率を計算する
for fold_ in range(5):
# 学習用データと検証用データに分割する
df_train = df[df.kfold != fold_].reset_index(drop=True)
df_valid = df[df.kfold == fold_].reset_index(drop=True)
# CountVectorizerインスタンスを作成する
count_vec = CountVectorizer(
tokenizer=lambda x: x,
token_pattern=None,
lowercase=False
)
# 学習用データのtokens列を使ってBoW特徴量を作成する
count_vec.fit(df_train.tokens)
# 正解ラベルと特徴量を作成する
# 特徴量は、学習用データ文章にfitしたCountVectorizerを使って、学習用データと検証用データのBoW特徴量を作成する
y_train = df_train.rating
y_valid = df_valid.rating
X_train = count_vec.transform(df_train.tokens)
X_valid = count_vec.transform(df_valid.tokens)
# ロジスティック回帰のインスタンスを作成する
# BoWは疎な特徴量なので、L1正則化を使うことにする
lr = LogisticRegression(penalty='l1', solver='liblinear')
# ロジスティック回帰のモデルを学習する
lr.fit(X_train, y_train)
# 検証用データに対する予測を行う
preds = lr.predict(X_valid)
# 正解率(Accuracy)を計算する
accuracy = accuracy_score(
y_valid,
preds
)
# 予測結果をdfに追加する
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.8845 Fold=1 検証用データに対する正解率: 0.8899 Fold=2 検証用データに対する正解率: 0.9023 Fold=3 検証用データに対する正解率: 0.8808 Fold=4 検証用データに対する正解率: 0.8950 トータルの正解率: 0.8905
検証データに対する正解率は89.05%という結果が得られました。
まとめ
テキストに前処理をすることによって、意味をうまく抜き出すことができるようになり、予測精度が上がるかを確かめてみましたが、前処理をしたことによる効果はほぼ見られませんでした。
ロジスティック回帰とBag of Wordsを使った結果、検証データに対する正解率は、89.2% → 89.05%にわずかに下がってしまいました。
次回以降は、異なる特徴量を使うことで、精度を上げることが出来るかを試してみます。
