File Icons
採用情報
BLOG
(ch-6) テキストデータを整形して、ロジスティック回帰 + BoWによる感情分析を改善する

目次

  1. 前回の結果の振り返り
  2. 今回やること
  3. データの前処理
    1. データの準備
    2. テキストの前処理
  4. 精度の比較
    1. 文章の分かち書き
    2. クロスバリデーションのための分割
    3. モデルの作成と評価
  5. まとめ

前回の結果の振り返り

前回の記事では、感情分析のためのベンチマークとして、ロジスティック回帰と、Bag of Words特徴量の組み合わせのモデルを作成しました。

ベンチマークの予測精度として、クロスバリデーションを行ったときの「検証データに対する正解率:88.8%」という結果が得られました。

今回やること

前回は、特にテキストデータを加工せず、そのままBag of Words特徴量を作成しました。今回は、テキストデータを前処理した上で、BoW特徴量を作成して、精度が上がるかを試してみます。

データの前処理

データの準備

■python

  1. import pandas as pd
  2. import spacy
  3. from sklearn.model_selection import StratifiedKFold
  4. from sklearn.feature_extraction.text import CountVectorizer
  5. from sklearn.linear_model import LogisticRegression
  6. from sklearn.metrics import accuracy_score

■python

  1. # データの読み込み
  2. df = pd.read_csv('../data/interim/chabsa-sentiment-analysis.csv')
  3. df.head()
image.png (36.8 kB)

テキストの前処理

ここが今回の記事のメイン部分になります。テキストから意味を抜き出しやすくするために、テキストデータを前処理します。具体的には、以下の処理を行います。

  • 全角・半角文字を統一する
  • 桁区切りの数字を削除する
  • テキストを小文字に寄せる
  • 記号をスペースに変換する

全角・半角文字を統一する

■python

  1. # 全角・半角文字を統一する
  2. import neologdn
  3.  
  4. def apply_neologdn(text):
  5. return neologdn.normalize(text)
  6.  
  7. # 全角・半角文字を統一する
  8. df['sentence'] = df['sentence'].apply(apply_neologdn)
  9. df.head()
image.png (36.8 kB)

桁区切りの数字を削除する

■python

  1. # 桁区切りの数字を削除する
  2. import re
  3.  
  4. def apply_remove_comma(text):
  5. """文字列中の数値を0に置き換える"""
  6. text_removed_comma = re.sub(r'(\d+)[,.](\d+)', r'\1\2', text)
  7. text_replaced_number = re.sub(r'\d+', '0', text_removed_comma)
  8.  
  9. return text_replaced_number
  10.  
  11. # 数字を削除する
  12. df['sentence'] = df['sentence'].apply(apply_remove_comma)
  13. df.head()
image.png (37.2 kB)

テキストを小文字に寄せる

■python

  1. # テキストを小文字に寄せる
  2. def apply_lower(text):
  3. return text.lower()
  4.  
  5. # テキスト中の英単語を小文字に寄せる
  6. df['sentence'] = df['sentence'].apply(apply_lower)
  7. df.head()
スクリーンショット 2023-09-14 19.18.51.png (34.5 kB)

記号をスペースに変換する

■python

  1. # 記号をスペースに変換する
  2. def apply_replace_symbol(text):
  3. """記号をスペースに置き換える"""
  4. replaced_text = re.sub(r'[!-/:-@[-`{-~]', ' ', text)
  5. return replaced_text
  6.  
  7. # 記号をスペースに変換する
  8. df['sentence'] = df['sentence'].apply(apply_replace_symbol)
  9. df.head()
image.png (37.2 kB)

改行コードを削除する

■python

  1. # 改行コードを削除する
  2. def apply_remove_newline(text):
  3. """改行コードを削除する"""
  4. removed_text = re.sub(r'\\n|\n', '', text)
  5. return removed_text
  6.  
  7. # 記号をスペースに変換する
  8. df['sentence'] = df['sentence'].apply(apply_replace_symbol)
  9. df.head()
image.png (37.1 kB)

精度の比較

前処理したテキストを使って、Bag of Words特徴量を作成し、モデルの精度が向上したかを確認します。

文章の分かち書き

■python

  1. # sentence列を分かち書きする
  2. nlp = spacy.load('ja_core_news_md')
  3.  
  4. def apply_nlp(text):
  5. doc = nlp(text)
  6. return [token.text for token in doc]
  7.  
  8. # tokensに分かち書きされた単語が入る
  9. df['tokens'] = df['sentence'].apply(apply_nlp)
  10.  
  11. # 分かち書きのリストが作成できているかを確認する
  12. df.head(5)
image.png (56.3 kB)

クロスバリデーションのための分割

■python

  1. # クロスバリデーションのために、kfoldの列を追加する
  2.  
  3. # k-fold
  4. df['kfold'] = -1
  5.  
  6. # データをシャッフルする
  7. df = df.sample(frac=1).reset_index(drop=True)
  8.  
  9. # StratifiedKFoldを使う
  10. skf = StratifiedKFold(
  11. n_splits=5,
  12. shuffle=False
  13. )
  14.  
  15. # データを分割する
  16. for fold, (train_idx, val_idx) in enumerate(skf.split(X=df, y=df.rating.values)):
  17. # foldの列にkfoldの分割番号を入れる
  18. df.loc[val_idx, 'kfold'] = fold
  19.  
  20. # kfoldの分割番号が正しく振られていることを確認する
  21. df.kfold.value_counts()
  22.  
  23. # データを書き出す
  24. df.to_csv('../data/interim/chabsa-sentiment-analysis-with-kfold-preprocessed.csv', index=False)

モデルの作成と評価

前回の記事と同様に、分割したデータセットごとに、以下の処理を実行します。

  1. 学習用データと検証用データに分割する
  2. Bag of Words特徴量を作成する
  3. モデルを学習する
  4. 正解率を計算する

評価指標としては、正解率(Accuracy)を使用します。

■python

  1. # foldごとに以下の処理を行う
  2. # 1. 学習用データと検証用データに分割する
  3. # 2. BoW特徴量を作成する
  4. # 3. モデルを学習する
  5. # 4. 正解率を計算する
  6.  
  7. for fold_ in range(5):
  8. # 学習用データと検証用データに分割する
  9. df_train = df[df.kfold != fold_].reset_index(drop=True)
  10. df_valid = df[df.kfold == fold_].reset_index(drop=True)
  11.  
  12. # CountVectorizerインスタンスを作成する
  13. count_vec = CountVectorizer(
  14. tokenizer=lambda x: x,
  15. token_pattern=None,
  16. lowercase=False
  17. )
  18. # 学習用データのtokens列を使ってBoW特徴量を作成する
  19. count_vec.fit(df_train.tokens)
  20.  
  21. # 正解ラベルと特徴量を作成する
  22. # 特徴量は、学習用データ文章にfitしたCountVectorizerを使って、学習用データと検証用データのBoW特徴量を作成する
  23. y_train = df_train.rating
  24. y_valid = df_valid.rating
  25.  
  26. X_train = count_vec.transform(df_train.tokens)
  27. X_valid = count_vec.transform(df_valid.tokens)
  28.  
  29. # ロジスティック回帰のインスタンスを作成する
  30. # BoWは疎な特徴量なので、L1正則化を使うことにする
  31. lr = LogisticRegression(penalty='l1', solver='liblinear')
  32.  
  33. # ロジスティック回帰のモデルを学習する
  34. lr.fit(X_train, y_train)
  35.  
  36. # 検証用データに対する予測を行う
  37. preds = lr.predict(X_valid)
  38.  
  39. # 正解率(Accuracy)を計算する
  40. accuracy = accuracy_score(
  41. y_valid,
  42. preds
  43. )
  44.  
  45. # 予測結果をdfに追加する
  46. df.loc[df.kfold == fold_, 'preds'] = preds
  47.  
  48. # 正解率を出力する
  49. print(f'Fold={fold_} 検証用データに対する正解率: {accuracy:.4f}')
  50.  
  51. # トータルの正解率を出力する
  52. accuracy = accuracy_score(
  53. df.rating,
  54. df.preds
  55. )
  56.  
  57. print(f'トータルの正解率: {accuracy:.4f}')
  1. Fold=0 検証用データに対する正解率: 0.8845
  2. Fold=1 検証用データに対する正解率: 0.8899
  3. Fold=2 検証用データに対する正解率: 0.9023
  4. Fold=3 検証用データに対する正解率: 0.8808
  5. Fold=4 検証用データに対する正解率: 0.8950
  6. トータルの正解率: 0.8905

検証データに対する正解率は89.05%という結果が得られました。

まとめ

テキストに前処理をすることによって、意味をうまく抜き出すことができるようになり、予測精度が上がるかを確かめてみましたが、前処理をしたことによる効果はほぼ見られませんでした。

ロジスティック回帰とBag of Wordsを使った結果、検証データに対する正解率は、89.2% → 89.05%にわずかに下がってしまいました。

次回以降は、異なる特徴量を使うことで、精度を上げることが出来るかを試してみます。

CONTACT
ご依頼やご相談、サービスについてのご質問やご要望がございましたら、お気軽にお問い合わせください。
送付いただいた内容を確認の上、担当者からご連絡させていただきます。
お問い合わせ