感情分析を使って半生を振り返ってみた

本記事は、[ハンズラボ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/handslab) 16日目の記事です。

## 前置き
この記事で技術的に突っ込んだ話はしません。
分析系のライブラリを使ってみたよという体験談的な記事になります。

### はじめに
ブログのネタがないので自分の半生について考えることにしました。
ブログのネタは常にないです。 ネタまみれの半生を送ってみたかったです。

こんぴゅーた君と仲良くなるために大学に入り、それから10年が経とうとしているので振り返るなら良い頃合いだろうと思いました。

### どうやって振り返るの？
手元にちょうどいいデータがあったのでそれを使って振り返ろうと思います。
そうです。 自分の過去のツイートです。

大学入学してから息を吐くようにツイートをしていたので自分の手元には20万ツイートほどのデータがありました。

何かに使うかもしれないと手元にデータを落としておいてくれた過去の自分に感謝しています。　偉いですね。

しかもTwitterはもうやっていない（アカウントも削除した）ので記事を書いても垢バレの心配もありません。 偉いですね。

これらのツイートに対して感情分析をかけて数値化することで統一された基準の元、振り返ることができそうです。

## 振り返ってみる
今回は簡単に以下の手順で進めました。

1. 全ツイートをDBに入れる
2. ツイートに対して感情分析をかけてスコアをつける
3. スコアを分析してなんとなくで振り返る

使ったライブラリは以下になります。
```
torch 
torchvision 
transformers # BERTを使った感情分析のため
fugashi # 形態素解析用ライブラリ
ipadic # 形態素解析用辞書
sqlalchemy # ORMライブラリ
pymysql # MySQL接続ライブラリ
alembic # マイグレーション用
seaborn # グラフ描画用
```

分析のついでにORMに慣れたいという気持ちがあったのでORM系のライブラリが一緒に入っています。

### 全ツイートをDBに入れる
ツイートをDBに入れる部分は以下の記事を参考にしながら行いました。
- [メンヘラがツイートを形態素解析して気分の波を調べた結果www - Qiita](https://qiita.com/yusuke_astro/items/dc38802b81f348189a98#下準備)

1. ツイートデータをjson形式に変換
2. jsonをロードして辞書型にする
3. sqlalchemyを使ってinsert文にする
4. bulk_insertする

実際に使ったものが以下のコードです。

```python
import json
from datetime import datetime
from database.db_base import session
from database.models.university_tweets import UniversityTweets

# json形式にしたツイートデータをロードする
with open("resources/tweet.json", "r") as f:
    tweets = json.load(f)

# bulk_insert用のlist
insert_list = []

# ツイートデータ読み取り
for single_tweet in tweets:
    # 画像のみツイートは分析の邪魔になるので弾く
    if single_tweet["full_text"][:6] != "https:":
        value = {
            "full_text": single_tweet["full_text"], # 本文
            "favorite_count": single_tweet["favorite_count"], # いいね数
            "retweet_count": single_tweet["retweet_count"], # RT数
            "created_at": datetime.strptime(
                single_tweet["created_at"], "%a %b %d %H:%M:%S %z %Y"
            ),
        } # 投稿日
        insert_list.append(value)

session.begin()
try:
    # データを登録
    session.bulk_insert_mappings(UniversityTweets, insert_list)
    session.commit()
finally:
    session.close()
```

こうしてこの世にただ一つの黒歴史が詰まったテーブルが完成しました。

こんな感じです。 
これは大学時代のツイートのうち、ツイートの内容が同じもの（完全一致）で数が多い順に並べたものです。
![スクリーンショット 2022-12-15 17.28.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527463/dccaa141-03bf-3032-e60b-62d39d3e3ccb.png)

息をするようにツイートしていたことが分かりますね。


### ツイートに対して感情分析をかけてスコアをつける
スコアをつける部分に関しては**transformers**というライブラリを利用しました。
今回は簡単に試したかったので学習済みデータを利用しています。

以下の記事を参考に実装しています。
[【Python】日本語による感情分析をTransformersで行う \| ジコログ](https://self-development.info/%E3%80%90python%E3%80%91%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AB%E3%82%88%E3%82%8B%E6%84%9F%E6%83%85%E5%88%86%E6%9E%90%E3%82%92transformers%E3%81%A7%E8%A1%8C%E3%81%86/)

```python
from transformers import (
    AutoModelForSequenceClassification,
    BertJapaneseTokenizer,
    pipeline,
)
from database.db_base import session
from database.models.university_tweets import UniversityTweets

# 事前トレーニングデータからトークナイザーとモデルの準備
model = AutoModelForSequenceClassification.from_pretrained(
    "daigo/bert-base-japanese-sentiment"
)
tokenizer = BertJapaneseTokenizer.from_pretrained(
    "cl-tohoku/bert-base-japanese-whole-word-masking"
)
# パイプラインで感情分析を設定
exe_analysis = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)

session.begin()
try:
    # DBからツイート引っこ抜く
    result = session.query(
        UniversityTweets.id,  # ID
        UniversityTweets.full_text,  # ツイート全文
    ).order_by(UniversityTweets.id)
    # レコード更新リスト
    summary_dict = []
    for record in result:
        # 感情分析の実行
        text_set = exe_analysis(record.full_text)
        # ツイートにスコアをつけていく
        temp_dict = {
            "id": record.id,
            # ネガティブと判定された場合は-扱いにする
            "emote_type": 1 if text_set[0]["label"] == "ポジティブ" else -1,
            "score": text_set[0]["score"],
        }
        summary_dict.append(temp_dict)
    if summary_dict:
        # 更新
        session.bulk_update_mappings(UniversityTweets, summary_dict)
        # コミット
        session.commit()
finally:
    session.close()
```

これでツイートテーブルに対してスコアを付与させることができました。
ついでにこの処理は完了までに3時間近くかかったので、
本気で分析をやるとしたらその辺の対策から始める必要があるなと感じました。

### スコアを分析してなんとなくで振り返る
「スコアをつけたら面白いかもしれない」という見切り発車でスタートしたため、どうやって分析するかを考えていませんでした。
とりあえず1日のツイートのスコアを平均化して可視化してみました。
ハズレ値を無くすため1日のツイートが20件以下のものは除外しています。
可視化したものがこちらです。

- 大学時代のツイートのスコア
![大学生_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527463/7a2bbaf1-dde0-dc63-0366-2a9f62392563.png)
- 社会人時代のツイートのスコア
![社会人_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527463/add9cc2a-bdda-94b6-0781-13a4c316bb9b.png)

基本的に0.6のあたりを推移していることがわかります。
赤い補助線は、全ツイートの平均スコアの±0.1を表しています。
社会人時代のグラフが、19年夏頃と20年春先に大きく乱れています。
これはメンタルが不安定な時期ほどツイート数が減るという自分の特性を表しています。
この時期はちょうど案件が燃えていた時期やテレワークが始まった時期と重なっていました。

ただ、これだけではよく分かりませんね？
スコアの評価として、補助線の中は何もない平凡な一日だったと考えられるため、範囲から外れたものを抜き出そうと思います。

補助線の範囲から外れたものだけを抜き出して分布を見たものが以下になります。
- 大学時代のツイートのスコア分布
![大学生_map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527463/af75df8d-795e-f69e-5213-417d68eadee8.png)
- 社会人時代のツイートのスコア分布
![社会人_map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527463/75e3d507-5fb3-5d4c-1c46-2105d804d415.png)

今回は補助線の範囲の上に出たものを比較的ポジティブな日、下に出たものを比較的ネガティブな日とすることにしました。
こうしてみるとポジティブな日に比べるとネガティブな日の方が多いなと思います。 体感的にもあっていると感じます。


最後に年を関係なく月と日だけで集計して1年の中でネガティブな日を抽出してみました。
　例:2017〜2021年の1/1にツイートされたものを抽出。 スコアの平均が0.5を下回っていた場合はネガティブとする。

```SQL
SELECT DATE_FORMAT(created_at, '%m/%d'), avg(score * emote_type), count(*) AS tweet_count
FROM university_tweets
GROUP BY DATE_FORMAT(created_at, '%m/%d')
HAVING count(*) > 20 and (avg(score * emote_type) < 0.55);
```


- 大学時代（計:21日）

|1〜3月|4〜6月|7〜9月|10〜12月|
|:--:|:--:|:--:|:--:|
|1/1|なし| 8/23| 11/01|
|1/2||9/23| 11/26|
|1/3|||12-31|
|1/12||||
|1/15||||
|1/16||||
|1/18||||
|1/19||||
|1/21||||
|1/26||||
|1/27||||
|1/29||||
|2/09||||
|2/18||||
|3/08||||
|3/12||||

- 社会人時代(計:52日)

|1〜3月|4〜6月|7〜9月|10〜12月|
|:--:|:--:|:--:|:--:|
|1/4|4/1|7/2|10/3|
|1/12|4/4|8/7|10/11|
|1/14|4/5|8/16|10/15|
|1/22|4/6|8/17|10/23|
|1/24|4/12|9/04|11/4|
|1/27|4/14|9/16|11/14|
|1/28|4/15|9/17|12/18|
|2/3|4/25|9/23|12/21|
|2/4|5/19| |12/27|
|2/6|5/31|
|2/9|6/18|
|2/13|6/23|
|2/22|6/25|
|2/24|6/26|
|2/25|6/28|
|2/28|
|3/2|
|3/5|
|3/30|
|3/31|

抜き出してみて思ったことは、

- 寒い時期が苦手すぎる（1〜3月）
- 大学時代の年末年始がネガティブ（バイトが忙しかったから）
- 社会人時代はネガティブな日が多い
- GWと夏休みはネガティブにならない

・・・

## 結論
***労働向いてない***
※注釈 ハンズラボに入社してからすぐTwitterをやめたので社会人時代のデータは現状と大きく異なると思います。

## future work
- 頻出単語の抽出をして自分の口癖を調べる
- 自分のポジティブな発言だけをつぶやくslackのbotを作る

## 参考
https://qiita.com/yusuke_astro/items/dc38802b81f348189a98

https://self-development.info/%E3%80%90python%E3%80%91%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AB%E3%82%88%E3%82%8B%E6%84%9F%E6%83%85%E5%88%86%E6%9E%90%E3%82%92transformers%E3%81%A7%E8%A1%8C%E3%81%86/

----
明日の[ハンズラボ Advent Calendar 2022 17日目](https://qiita.com/advent-calendar/2022/handslab)は@bakupenさんです👏👏 楽しみですね👏👏