目次
- 前回の結果の振り返り
- 今回やること
- データの前処理
- データの準備
- テキストの前処理
- 精度の比較
- 文章の分かち書き
- クロスバリデーションのための分割
- モデルの作成と評価
- まとめ
前回の結果の振り返り
前回の記事では、感情分析のためのベンチマークとして、ロジスティック回帰と、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%にわずかに下がってしまいました。
次回以降は、異なる特徴量を使うことで、精度を上げることが出来るかを試してみます。