1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【後編】元自衛隊のエンジニア1年目がPythonで感情予測をした話 25.5%→33.6%

Last updated at Posted at 2023-03-05

はじめに

本記事は、下記のとおり【前編】から続く内容になっています。


【流れ】

 1.~ 5.【前編】

  1. 分析の設計
  2. データの収集
  3. データの整形(1回目:最小限)
  4. モデルの構築(ランダムフォレスト)
  5. 可視化・考察
    以下【後編】
  6. データの整形(文字化け対策) ←以下はここからのお話です
  7. データの整形(不要語の削除)
  8. データの整形(語幹の抽出・統合)
  9. 目的変数修正
  10. 上記を実装
  11. 結論・今後の展望

全体を通して、そもそも何をしようとしているのか?

本記事ではTwitterのデータを利用して、ツイートのデータを感情分析しています。
感情分析の高精度なモデルが出来れば、ツイートに限らず様々な文章の感情を定量的に判定できるようになると考えます。
全体を通して、精度が高くなるようにデータの前処理やモデルの性能向上を実施しています。
(この感情分析で何をどう分類したいのか? 具体例は【前編】のこちら で挙げております)
結果を先に申し上げると、当初 24.0% の精度だった感情予測モデルを 33.6% に引き上げました。
私は機械学習初心者であるため、試行錯誤の過程も残しておきたいと考えております。

1. 分析の設計~ 5. 可視化・考察

【前編】元自衛隊のエンジニア1年目がPythonで感情予測をした話 24.0%→25.5%
上記をご参照ください。

自己紹介、目的、実行環境等は前編と同じなので畳んでおきます

1. 当記事の概要

1.1 自己紹介

期間 経歴
2011~2022 陸上自衛隊
2022~現在 IT企業

私は陸上自衛隊の最前線で11年働いていました。
その中で災害現場やレンジャーなどを経験しています。
任務の中で、人の感情に強く興味を持つようになりました。
2022年、データ分析やエンジニアリングにとても興味があったためIT業界へ転職しました。
普段はQlik SenseというBIツールでお客様のデータを可視化したり、PythonやVBAを書いています。



1.2 目的

感情分析をしようと思ったきっかけは、IT企業で働く中でお客様である「コールセンターの業務改善」に携わったことです。
コールセンターが受電するクレームを文章化して「感情分析」できたら面白いのではないかと思いました。
クレームの文章化は音声のテキスト化や個人情報が障害となり、データ準備は困難です。
ですが、まずは足掛かりとして余暇の時間を使いデータ準備予測モデルの構築をしてみようと思います。
もう一つの目的があり、それは私自身のスキルアップです。

目的についてQ&A
Q. A.
結局やりたいことは? [文章] を与えると [この文章の感情は●●です] と高精度で返してくれるような仕組みの作成(●●に入るのはhapiness sadnessなど13クラス)
どのようなデータを使うの? [文章] [文章の感情] という2列のデータセット
具体的に何をやるの? 機械学習モデル:ランダムフォレストなどによる分類器作成

1.3 環境

  • Google Colaboratory
  • Python 3.8
  • ChatGPT ver.Feb 13

【前編】で構築したモデルの精度は25.5%でした。
【後編】で以下の解決策を検討し、精度の向上を目指します。

No. 未解決の課題 解決策 精度 実行前との差
6 "quot"の多発が気になる 文字化け対策 ??% ??%
7.1 ストップワード未使用 ストップワードの削除 ??% ??%
7.2 不要語未処理 自力でNGワードリストを作成 ??% ??%
8 語幹の抽出をしていない nltk形態素解析・語幹の抽出 ??% ??%
9 感情ごとの正答率差が激しい 目的変数修正 ??% ??%

6. データの整形(文字化け対策)

6.1 目的

分析初期の時期にcsvファイルをWindowsエクスプローラから開いたとき、Excelにて開いていました。
当時、文字コードを確認せず閉じていたことに分析途中で気が付いたため、凡ミスですが是正のため修正します。

6.2 手段

Kaggleから再度データセットをダウンロードし、Windowsで開かずに文字コードUTF-8で実行環境へ読み込みます。
このパートはそれだけなので結果だけ簡単に記載いたします。7. ストップワード除去 からが本番です。

6.3 結果

  1. 文字化けはなくなりました。
  2. モデルの予測精度は期待に反して約0.5%下がりました。

【出力】ランダムフォレスト: 0.24966599866399466

6.4 可視化・考察

何が起きたかを詳しく見るために、差分を見てみます。
修正前の混同行列 - 修正後の混同行列 = 以下の差分の混同行列
image.png

  • 大きな変化はhappinessと予測する回数が418回増えたことです。
  • 文字化けの有無だけでは、予測精度は大きく変化しないと言えそうです。
No. 未解決の課題 解決策 精度 実行前との差
6 "quot"の多発が気になる 文字化け対策 25.0% -0.5%
7.1 ストップワード未使用 ストップワードの削除
7.2 不要語未処理 自力でNGワードリストを作成
8 語幹の抽出をしていない nltk形態素解析・語幹の抽出
9 感情ごとの正答率差が激しい 目的変数修正


7. データの整形(ストップワード除去)

7.1 nltkライブラリのstop_word(English)を使う

7.1.1 目的
  • 無意味な情報排除:文章の内容と直接関係のない単語を除外することで、解析精度を向上します
  • メモリ使用量低減
7.1.2 手段
  • nltkライブラリの英語stopwordが有名です。まずはこれを実行してみます。
  • …が、その前にどんなワードが含まれているのか、気になるので確認します。
import nltk

# まずは内容確認
nltk.download('averaged_perceptron_tagger') # 品詞タグをダウンロード
def sort_words_by_pos(words):
    pos_tags = nltk.pos_tag(words) # 品詞タグ付け
    sorted_words = {}
    for word, pos_tag in pos_tags:
        if pos_tag not in sorted_words:
            sorted_words[pos_tag] = [word]
        else:
            sorted_words[pos_tag].append(word)
    return sorted_words

sorted_words = sort_words_by_pos(stop_words)
どんな単語が含まれているか品詞ごとにまとめてみた
dictionary = {
    'IN': ['i', 'if', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'from', 'in', 'on', 'off', 'over', 'under', 'than'], 
    'PRP': ['me', 'myself', 'we', 'ours', 'ourselves', 'you', 'yourself', 'he', 'him', 'himself', 'she', 'her', 'it', 'itself', 'they', 'them', 'themselves'], 
    'PRP$': ['my', 'our', 'your', 'his', 'its', 'their'], 
    'VBP': ["you're", 'am', 'are', 'ain', 'didn', "weren't"], 
    'JJ': ["you've", 'theirs', 'further', 'few', 'other', 'such', 'own', 'same', 's', 'll', 'aren', "hadn't", "haven't", 'isn', 'mightn', "mustn't", 'needn', "shan't", "wasn't", "won't"], 
    'NN': ["you'll", 'do', 'about', 't', "don't", 'm', 'o', 're', 've', 'y', "aren't", 'couldn', "couldn't", "didn't", 'doesn', "doesn't", 'hasn', "hasn't", "isn't", "mightn't", "needn't", 'shouldn', "shouldn't", 'wouldn', "wouldn't"], 
    'VB': ["you'd", 'be', 'have', 'don', "should've"], 
    'NNS': ['yours', 'hers', 'weren'], 
    'VBZ': ['yourselves', "it's", "that'll", 'is', 'has', 'does', 'd'], 
    'VBD': ["she's", 'herself', 'was', 'were', 'did', 'hadn', 'haven', 'ma', 'mustn', 'shan', 'wasn', 'won'], 
    'WP': ['what', 'who', 'whom'], 
    'WDT': ['which', 'that'], 
    'DT': ['this', 'these', 'those', 'a', 'an', 'the', 'all', 'any', 'both', 'each', 'some', 'no'], 
    'VBN': ['been', 'had'], 
    'VBG': ['being', 'having', 'doing'], 
    'CC': ['and', 'but', 'or', 'nor'], 
    'TO': ['to'], 
    'RP': ['up', 'out'], 
    'RB': ['down', 'again', 'then', 'once', 'here', 'not', 'only

  • 前提として、nltkのストップワードとは検索システムの運用において除外したいワードだそうです。
  • 感情分析用ではないので、鵜呑みにするのは良くないですね。
  • i, you, she などの代名詞や、will, must, had などの助動詞が多いです。
  • nor, not, only など感情が含まれそうな単語も存在します。
実行します
# 下記のstopsを単語リストに適用して、削除します
stops = set(stopwords.words("english")) 
7.1.3 結果
  • 単語数は150語減りました。
    登場単語の種類:30933語登場単語の種類:30783語

  • しかし、モデルの予測精度は期待に反して2.3%下がりました。
    ランダムフォレスト: 0.23224455611390285


7.1.4 可視化・考察

再び、何が起きたかを詳しく見るために、差分を見てみます。
修正前の混同行列 - 修正後の混同行列 = 以下の差分の混同行列
image.png

  • happiness sadness love の予測件数が目立ちます
  • 上記の3つの感情増えた=平滑化分類しやすくなった
  • しかしnltkのストップワード適用だけでは、予測精度は大きく変化しないと言えそうです。
  • 前述のとおり「検索機能のための単語リスト」なので、他に「感情予測用の単語リスト」が作れないか検討します。
No. 未解決の課題 解決策 精度 実行前との差
6 "quot"の多発が気になる 文字化け対策 25.0% -0.5%
7.1 ストップワード未使用 ストップワードの削除 23.2% -2.3%
7.2 不要語未処理 自力でNGワードリストを作成
8 語幹の抽出をしていない nltk形態素解析・語幹の抽出
9 感情ごとの正答率差が激しい 目的変数修正


7.2 自力でNGワードリストを作成

7.2.1 目的
  • nltkのストップワードを今回のモデル構築にそのまま使うのは、やや乱暴だと考えます。
  • 自分なりの工夫として、より精度の高い不要語の削除を狙います。
7.2.1 手段
  • 【前編】で「単語ごとの出現回数をカウント」をしていることを活用し、出現数TOP100を確認します。
  • 出現数TOP100を形態素解析し、最後は自分の判断で削除するかどうかアノテーションします。
データセット全体の単語の出現割合の概観
!pip install wordcloud
from wordcloud import WordCloud

# ワードクラウドの作成
wordcloud = WordCloud(background_color='white', width=1500, height=700).generate_from_frequencies(dict(zip(counts_df_sorted['word'], counts_df_sorted['count'])))

# ワードクラウドの表示
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()

image.png

概観より具体的に確認するため、形態素解析
import nltk
# 品詞判定用のライブラリをダウンロード
nltk.download('averaged_perceptron_tagger')

# 各単語の品詞を取得
pos_tags = nltk.pos_tag(word100)

以下のように品詞ごとに並べることで、アノテーション(deleteフラグを立てる)しやすくします

word and but one the a that this no some an in for of on with at like from about if as off m good quot happy u new miss great last more can will i s t day http today work lol time com im amp night home oh ll re morning need hope get thanks you it me we they he so just not all now too back really think well still much here my your its up to be out go do know there don see was got had going have are love am want is what how when
count 7,468 3,410 1,408 12,986 9,844 4,307 2,281 1,928 1,124 932 5,443 5,291 4,518 4,169 2,968 2,723 1,939 1,431 1,246 1,096 1,015 969 3,443 2,311 1,855 1,485 1,422 1,081 892 877 876 919 2,266 1,298 24,185 5,336 4,629 3,221 1,838 1,664 1,640 1,449 1,390 1,349 1,327 1,159 1,125 1,072 1,009 1,000 888 860 856 844 2,017 1,024 7,790 7,741 4,180 1,543 1,124 963 3,758 3,210 2,659 2,347 2,274 1,633 1,348 1,279 1,044 1,035 982 955 950 8,077 1,676 1,183 2,165 14,387 2,907 2,186 1,823 1,700 1,356 1,294 1,260 1,186 2,661 1,636 1,143 1,574 3,773 1,915 1,641 1,430 1,017 5,740 1,479 1,064 1,020
pos 'CC') 'CC') 'CD') 'DT') 'DT') 'DT') 'DT') 'DT') 'DT') 'DT') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'IN') 'JJ') 'JJ') 'JJ') 'JJ') 'JJ') 'JJ') 'JJ') 'JJ') 'JJ') JJR') 'MD') 'MD') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') 'NN') NNS') NNS') PRP') PRP') PRP') PRP') PRP') PRP') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') 'RB') RP$') RP$') RP$') 'RP') 'TO') 'VB') 'VB') 'VB') 'VB') 'VB') 'VB') 'VB') 'VB') VBD') VBD') VBD') VBG') VBP') VBP') VBP') VBP') VBP') VBZ') 'WP') WRB') WRB')
delete 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

自力で作った疑似ストップワードリストで不要語の削除を行います

不要語の削除
path = '/content/drive/MyDrive/Data for Colab Notebooks/20230303200335_頻出ワード100.xlsx'
df = pd.read_excel(path)
delete_list = df['word'].tolist()
delete_list
# '@'が含まれる文字とストップワードを削除
stops = set(delete_list)  
meaningful_words = [w for w in words if not w in stops and not re.match("^[@]", w)]
7.2.3 結果
  • モデルの予測精度は期待に反して約1.4%下がりました。
    ランダムフォレスト: 0.24060464339402038

7.2.4 可視化・考察

再び、何が起きたかを詳しく見るために、差分を見てみます。
修正前の混同行列 - 修正後の混同行列 = 以下の差分の混同行列
image.png

  • 強いて言えば neutral worry happiness sadness の予測件数増減があります。
  • しかし、私の技術レベルにおいて自前のストップワード適用だけでは、予測精度は上げられないと言えそうです。
  • 次に同じく形態素解析を活用し、語幹の抽出による単語の統合により精度向上できないか検討します。
No. 未解決の課題 解決策 精度 実行前との差
6 "quot"の多発が気になる 文字化け対策 25.0% -0.5%
7.1 ストップワード未使用 ストップワードの削除 23.2% -2.3%
7.2 不要語未処理 自力でNGワードリストを作成 24.1% -1.4%
8 語幹の抽出をしていない nltk形態素解析・語幹の抽出
9 感情ごとの正答率差が激しい 目的変数修正


8. データの整形(nltk形態素解析、語幹の抽出)

8.1 目的

  • 同じ語幹を持つ単語をまとめることができ、より正確な感情分析を行う
  • 単語の形態変化によって生じる情報量の違いを排除し、感情分析の精度を向上させる

8.2 手段

  1. まずは使うライブラリを検討
  2. 関数を作成し、データを処理
  3. 語幹抽出による単語数の削減効果と、モデルの精度を確認
ライブラリ名 語幹の精度 処理速度 メリット デメリット
Polyglot 多言語処理に特化しており、様々な言語に対応している。 語幹の精度がNLTKなどに比べて低いことがある。
NLTK 語幹の精度が高い。多くの自然言語処理タスクに対応している。情報量が多い。→初めてなのでこちらを採用 処理速度が遅いことがある。
TreeTagger 語幹の精度が高い。多くの言語に対応している。 インストールや使用に専門的な知識が必要な場合がある。英語以外の言語では精度が低いことがある。

  • 語幹の具体的な抽出方法の例として、以下の様に行います。
単語 語幹の抽出方法 語幹の例
running 動詞を原型にする run
mice 名詞を単数形にする mouse
biggest 形容詞を原形にする big
関数のコードが長くなったため畳みます
形態素解析
import nltk
!pip install -U nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
nltk.download('omw-1.4')
nltk.download('wordnet')
# 単語の正規化
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()

def lemmatize_word(word, tag):
    if tag.startswith('NN'):
        return lemmatizer.lemmatize(word, pos='n')
    elif tag.startswith('VB'):
        return lemmatizer.lemmatize(word, pos='v')
    elif tag.startswith('JJ'):
        return lemmatizer.lemmatize(word, pos='a')
    elif tag.startswith('R'):
        return lemmatizer.lemmatize(word, pos='r')
    else:
        return word

# 形態素解析
from nltk.tokenize import word_tokenize
# from nltk.corpus import stopwords

# stop_words = set(stopwords.words('english'))

def tokenize_text(text):
    tokens = word_tokenize(text)
    tokens = [token.strip() for token in tokens]
    tagged_tokens = nltk.pos_tag(tokens)
    lemmatized_tokens = [lemmatize_word(token, tag) for token, tag in tagged_tokens]
    return [token.lower() for token in lemmatized_tokens if token.isalpha()]

clean_tweet_df['tokens'] = clean_tweet_df['content'].apply(tokenize_text)

# 変換履歴の表示
import itertools

def print_transformation_history(original_token, final_token):
    if original_token != final_token:
        print(f"{original_token}{final_token}")

def print_top_transformation_history(df, n=10):
    all_tokens = list(itertools.chain.from_iterable(df['tokens']))
    transformed_tokens = list(itertools.chain.from_iterable(df['tokens_transformed']))
    transformed_count = len([t for t in transformed_tokens if t != ''])
    print(f"Total number of transformations: {transformed_count}")
    print("\nTop transformation history:")
    for original_token, final_token in itertools.zip_longest(all_tokens, transformed_tokens):
        if original_token is None or final_token is None:
            break
        print_transformation_history(original_token, final_token)
        n -= 1
        if n == 0:
            break

# 語幹の変換
import inflect
p = inflect.engine()

def stem_transform(token):
    return p.singular_noun(token) or token

clean_tweet_df['tokens_transformed'] = clean_tweet_df['tokens'].apply(lambda x: [stem_transform(token) for token in x])

# 過去形、進行形の変換
from textblob import TextBlob

def verb_transform(token):
    blob = TextBlob(token)
    if blob.tags[0][1] == 'VBD' or blob.tags[0][1] == 'VBN':
        return blob.words[0].lemmatize('v')
    elif blob.tags[0][1] == 'VBG':
        return blob.words[0].lemmatize('v')[:-1]
    else:
        return token

clean_tweet_df['tokens_transformed'] = clean_tweet_df['tokens_transformed'].apply(lambda x: ' '.join([verb_transform(token) for token in x]))

clean_tweet_df

image.png

8.3 結果

  • 単語数は4,838語減りました
    登場単語の種類:30,933語登場単語の種類:26,095語

  • モデルの予測精度は期待に反して約0.2%下がりました。
    ランダムフォレスト: 0.25334001336005346

8.4 可視化・考察

再び、何が起きたかを詳しく見るために、差分を見てみます。
修正前の混同行列 - 修正後の混同行列 = 以下の差分の混同行列
image.png

  • 目立った差異がありませんでした
  • 語幹の抽出による単語数削減だけでは、予測精度は大きく変化しないと言えそうです。
  • ここで特徴量の調整を一旦離れて、目的変数の修正で精度向上を図ります。
No. 未解決の課題 解決策 精度 実行前との差
6 "quot"の多発が気になる 文字化け対策 25.0% -0.5%
7.1 ストップワード未使用 ストップワードの削除 23.2% -2.3%
7.2 不要語未処理 自力でNGワードリストを作成 24.1% -1.4%
8 語幹の抽出をしていない nltk形態素解析・語幹の抽出 25.3% -0.2%
9 感情ごとの正答率差が激しい 目的変数修正



9. 目的変数修正

9.1 目的変数削減(13個→8個)

image.png

9.1.1 目的
  • 互いに密接に関連するカテゴリを減らすことで、予測性能を向上させることが出来る。
  • データの偏りや不均衡をなくして、カテゴリへの過剰な適合を防ぐことが出来る。
  • モデルの計算時間を低減することが出来る。
9.1.2 手段

 ①モデルがどの目的変数に分類しているか
 ②偏りや不均衡を発生させているのはどこか
 下図のように混同行列から確認していきます。

image.png

中間考察

分類タスクの典型的な例として
ガンの判定において「ガン発症者をガンでないと看過することを避けたい」 というものがあります。

感情を分類する時も同じく 「ネガティブな感情を看過することを避けたい」 と考えられます。

以上の検討から、分類を困難にしていると考えらえるneutral,worry, empty, boredom, enthusiasm を削減します。

目的変数削除
# 削除したいクラスを書いて下記で~を記述する、あるいは残したいクラスを書く
drop_class = ["neutral","worry", 'empty', 'boredom', 'enthusiasm']
# 削除項目を~で指定して、削除項目以外をすべて取り出す
drop_Tweet = Tweet[~Tweet["sentiment"].isin(drop_class)]

 → 20642 rows × 2 columns
  40,000行のデータが約半分になりました。

どんな割合になるか?
image.png
angerは特に少ないものの、感情分析タスクにおいて重要な目的変数であると考え、残しています。

9.1.3 結果

【出力】ランダムフォレスト: 0.282807570977918

 → 約28.3%とします

9.1.4 考察

image.png
対角線で正解できた割合を見ていきます。

love以上の3つは正答率が比較的高い
② 下位5つは正答率0に近い
happiness saddness とその他の目的変数との混同が多い
 → 下位の③で囲んでいる中において、混同数÷正解ラベル数9割を超えています。ほとんどがhappiness saddness に割り振られてしまっています。

  • 一旦目的変数を13個に戻します。
  • 次に別の手段として目的変数の統合を行います

9.2 目的変数統合(13個→6個)

image.png

9.2.1 目的
  • データセットの行数を減らすことなく、目的変数だけを減らすため
  • 互いに密接に関連するカテゴリをグループ化して、予測性能を向上させるため
  • データの偏りや不均衡をなくして、カテゴリへの過剰な適合を防ぐため
  • モデルの計算時間を削減するため
9.2.2 手段
  • 言語として意味が近いものを統合する(例:empty=空虚 と boredom=物足りない)
  • その他として極端に少ない正解ラベルをまとめる(例:otherとしてempty boredum relief=安心 を統合)

具体的には以下の様に、データを操作しました。

目的変数統合
Tweet['sentiment'] = Tweet['sentiment'].apply(lambda x : x if x in ['happiness', 'sadness', 'worry', 'neutral', 'love'] else "other")

 ↓ 今回は割合の少なかった下位7個の目的変数を、otherと定義しました。
image.png
        other
image.png

9.2.3 結果

【出力】ランダムフォレスト: 0.25250501002004005

 → 約25.3%とします

9.2.4 考察

image.png
other に多くの予測が集まっています。
neutral が上位3件のうち、やや少なめです。
other neutral worry は正解率が約30~40%
happiness sadness love は正解率が約3~8%
 →ラベルの正解数合計は①が2,000個台に対して②は1,000個台であり、その差は約2倍
 →しかし、正解率は①と②の間で3~10倍の差が開いている
  →つまり happiness sadness love が苦手、 と考えられる
  → neutral が文章の特徴が低いクラスなので、worryotherに分類されやすい と考えられる
   理由: neutral には「特徴的な単語が少ない文章」が分類されると考えられる
       worryneutral を比べると、neutralの方が単語量が多いと考えられる

④ 悲しい文章などは特徴的。日常会話で使う種類の多い単語はneutralなものが多そう。
 →worryotherに割り振られる確率は低いと考えられる

neutralで使われているのは30,000種類、worryは1,000種類のようなイメージ
 →ランダムフォレストの中では、該当の単語が出てきているかどうかで行き先が決められている。
  →単語が多くても、一個でもworry的なものがあればworryに行ってしまうと考えられる。
   →アノテーション作業を考える。
    「特徴的な単語がないな」→neutral行き
    「少しでもworryだな」worryとなりそう


happiness以下は1,000個台でデータ量が少ない。ランダムフォレストは単純なので、精度向上のため2,000個台の多いクラスに割り振ろうとする
  → 結果、予測値が2,000台のクラスになると思われる。もう少しサンプリングするなどして、1,500ぐらいに揃えてみるのも良さそう。


ここまでのまとめ
No. 未解決の課題 解決策 精度 実行前との差
6 "quot"の多発が気になる 文字化け対策 25.0% -0.5%
7.1 ストップワード未使用 ストップワードの削除 23.2% -2.3%
7.2 不要語未処理 自力でNGワードリストを作成 24.1% -1.4%
8 語幹の抽出をしていない nltk形態素解析・語幹の抽出 25.3% -0.2%
9 感情ごとの正答率差が激しい 目的変数修正(5個削除) 28.3% +2.8%

10. 上記を組み合わせて実装

 6. 文字化け対策7.2 不要語削除8. 形態素解析による語幹の抽出9.1 目的変数の削減

10.1 目的

  • 精度30%以上を目指すため

10.2 手段

  1. 6. 文字化け対策7.2 不要語削除8. 形態素解析による語幹の抽出9.1 目的変数の削減 を実装
  2. ランダムフォレストのパラメータを調整

10.3 結果

  1. を実装した結果:ランダムフォレスト: 0.3305993690851735
    約33.6%:目標とする精度に到達しました。
  2. ランダムフォレストのパラメータn_estimatorを1~501までの間で調整
    image.png
     実装した結果:ランダムフォレスト: 0.3359621451104101
      → 約33.6%

10.4 考察

image.png
happiness の正解率は46% sadness の正解率は69%
suprise より下の感情の正解率は2%以下
  happinesssadness に分類されてしまっているのが大きな要因と考えられます。



11.結論・今後の展望

11.1 結論

現状のモデルだとこれ以上の大幅な精度向上は見込めなそうです。

11.1.1 精度が上がりづらくなっている理由

① ランダムフォレストは比較的単純なモデルなので限界がありそうです。

内容 詳細
ランダムフォレスト
の特徴
・複数の決定木を組み合わせて全体的な精度を向上させるアンサンブル学習法
・多数の特徴を持つ高次元データを扱える
・欠損値を扱え、データセットが不均衡でも精度を維持可能
・解釈性が高く、意思決定プロセスを比較的簡単に解釈・可視化可能
精度向上できない場合
の理由
・単語間の複雑な関係を捉えたり、シーケンシャルなデータを分析するようなタスクには、LSTMやBERTなどのディープラーニングモデルほど効果的ではない
・クラスの数が多いタスクや、クラスが不均衡な場合には、うまく機能しない可能性がある

② データセットの特徴として、ツイートなので以下の理由から綺麗なテキストデータではないです。

  • 話し言葉と書き言葉が混在している
  • クラス数に偏りがある

11.2 今後の展望

11.2.1 他の手法を使った感情分析に挑戦してみます
手法 メリット デメリット
TF-IDF 高次元データから特徴的な情報を抽出しやすい。 意味が似ているが異なる単語を区別しない。
勾配ブースティング 高精度、重要度算出、外れ値に強い 過学習に陥りやすい、パラメータ調整が難しい
LSTM 高精度、説明変数自動抽出 過学習に陥りやすい、学習に時間がかかる
Transformer 高精度、長文処理、並列処理に適している 多くのデータ量、計算資源が必要で、解釈性が低い
11.2.2 感情予測モデルの汎用性について
  • 当初目的のコールセンターのお客様の声だけでなく、「LINEの感情分析」「レビューの感情分析」などに、精度次第では横展開できそうだと感じました。
  • 昨今の翻訳ツールや翻訳WebAPIをフル活用すれば、英語でモデルに学習させたとしても、日本語のテキストを予測することは出来るのではないかと考えます。
11.2.3 感情分析の言語の違いについて

今回は英語だったので、今後日本語のデータが準備できれば日本語でもやってみようと思います。

おわりに

以上となります。
機械学習に初めて挑戦しましたので、まだ入り口に立ったばかりと感じます。
今回の感情予測をきっかけに、データ分析のスキルをどんどん磨いていければと思います。

ここまでお読みいただき、本当にありがとうございました。


【前編】元自衛隊のエンジニア1年目がPythonで感情予測をした話 24.0%→25.5%

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?