初心者が生成系AI（主にChatGPT)についてのネガポジ判定してみました

# 目次
1. 動機
2. 目標
3. 環境
4. 実装
4. 結論
5. ワードクラウド

## 1.動機
自己紹介として講座始める前からpythonの初心者レベルくらいは独学でしていましたが講座を始めてから本格的に学習し、この記事書いてる時点で講座２か月経過しています。
そもそも自分がAIに興味を持ったのが今年の頭にChatGPTが話題になり始め時にちょっと触ってみたところ「チャット形式で質問して自然な言葉ですぐに回答がくるってすごいな！！」と単純に思ったからです。当時から転職を考えておりIT系に興味を持ち始めていたが漠然としていました。このChatGPTを知ってからはAIの方向に進みたいと思うようになりそのきっかけになったChatGPTはヤフーコメントではどんな評価もしくは感情なのか知りたくて調べてみました。

## 2.目標
- 最初にネガポジ判定とは、「感情分析」（Sentiment Analysis）と呼ばれる技術の一種であるということができます。これは、文章などに含まれる評価・感情に関する表現を抽出して、文章中の感情を解析する技術などを指します。ネガ・ポジ分析の手法として、極性辞書を使った単語ごとの分類や、ディープラーニングを使ったものもありますが今回は極性辞書を用いての分類としました。ディープラーニングできるほどテキストがないためです。
- 最終的な目標としては時期別にヤフーコメントをスクレイピングをしてまとめて各月のネガポジ判定をしてどう変わったかを調べます。それと同時に比較対象として比較的ポジティブであろうと思われる野球の大谷選手の記事と比較的ネガティブであろうマイナーカードの記事も同様に調べて比較対象としました。
- 手順としてはまずは目標の生成系AIについてヤフー記事から探し出してスクレイピング。本当は自分が知りだした２月や３月くらいから抜きたかったのですが、ヤフーの記事がだいたい３か月くらい前のしかなく自分が調べたときは４月の中頃からの記事しなかったので実質４月の20～７月１４日までの記事にあるコメントを1つの記事で頭から10～50個に限ってだいたいひと月200～300個（４月だけ１４０個しかないですが）集めてきました。調べて分かったのですがなかなかChatGPT等の生成系AIへのコメントが少なくかなり手間取りました。比較対象の記事も同様にスクレイピング。
- 各月ごとに集計を取ってre.sub()メソッドで正規化してからMeCabで形態素解析。それを極性辞書に当てはめてネガポジ判定し、ワードクラウドを用いて視覚的にどんな言葉の頻度が高いかのグラフにしました。

## 3.環境
Google Colaboratory
Python 3.10.6

## 4.実装
#### 1. スクレイピング
最初に躓いたのは資料集めです。この講座を始める前に少しPython初心者としてスクレイピング学びましたがほぼ忘れており、講座や他の資料で再度学び今回はさらにHTMLとCSSの基本操作を学習。以下ヤフー記事のコメントスクレイピング
```
from bs4 import BeautifulSoup
import requests

base_url = "URL"  # ヤフーコメント最初のページの基本URL
total_pages = 1  # スクレイピングする総ページ数

come_list = []  # コメントを保存するリスト

for page in range(1, total_pages + 1):
    url = base_url +  "?page=" + str(page)  # ページごとのURLを生成

    response = requests.get(url)
    soup = BeautifulSoup(response.text, "html.parser")

    comments = soup.find_all("p", attrs={"class": "sc-ejCsHr hkKqR"})　

    for comment in comments:
        if comment.text not in come_list:
            come_list.append(comment.text)

for comment in come_list:
    print(comment)

import pandas as pd

# データフレームを作成
df = pd.DataFrame(come_list, columns=["コメント"])

# エクセルに保存
df.to_excel("ファイル名.xlsx", index=False)
```
<details><summary>記事のコメント一例</summary>
人の代替ができるかどうか、先駆的に取り組んでくれるのはある意味ありがたい。すべての企業や事業で検証するのは難しいだろうけど、これから起業をする人によっては、初めから人件費をかけずに事業を行う際の参考にはなるかもしれない。
浮いた人件費を事業成長に充てることもできるし、極端な話を言えば、雑多な事務作業であれば、AIで賄える範囲のレベルにしてしまう、のもアリだろうし。無駄を除外し、よりシンプルに、より効率的を目指すのであれば、どうすれば人を無くせるか、の視点に立つのも重要だと思う。
AIでなくても，コンピュータシステム導入の目的の一つは，業務の効率化による人件費の削減．生成AI以前より，この手の人員整理は頻繁に行われていた．30年ほど前にもOAという言葉が流行ったときには，仕事をコンピュータにとられるとまで言われたものだ．しかし，仕事は無くならず，人の仕事はより高度なものや，コンピュータをメンテナンスするための仕事に変化した．
AIの活用により，研究開発や芸術などの創造を伴う仕事は，より高度な仕事として人の手が引き続き必要となる一方で，AIを学習や評価するための仕事も増えるだろう．困るのはちょっと得意な程度で，開発や芸術の分野にいる人は，素人がAIを駆使して作る作品に駆逐される可能性がある．
そらそうなるわな。
人件費削減の効果も、人材管理の難しさに翻弄されなくなる。

今までの産業革命とは、ステージの違う問題のため今後のサービスは
人間よりAI又はAI制御下のロボットが担う。

率直に言って、人間が人間を管理する社会は終わる。
もう一つ付け加えれば、資本家が人間に対して行った手段では管理出来ない。
経済、そして資本の力でGAFAMなどを買収、又は資本支配し、経営者に意に染まる人物を
そして、内部に監視管理スパイ役の人材を配置、又は同様の機能をするAIに
任せようとするだろう。

爆笑する程、幼稚。
その手は使えなくなる。
30年ぐらい前、月一回の表計算の単純なテーブルののようなものを、ワープロでタイプして仕上げるだけの社員が3年居ました。（電話応対はしてたけど）当時誰もその存在を疑問に思わなかった。今同じことが起きているだけ。
テレックスのオペレーターやキーパンチャーも大量にいたけど、みんな失業したわけではないですね。
AIに仕事を奪われて失業者があふれる。
AIによって新しい仕事も生まれますが、
新しい仕事はおそらくAIに負けない高度な知性を必要とされるでしょう。
多くの一般市民は適応できず仕事を奪われる一方になります。
社会が崩壊する前に、AIで稼ぐ企業から多くの税金を取って、すべての人に再分配する仕組みが急務です。
AIに働かして人間に給料が振り込まれたらよいのでは？
仕事中に命を事故で落とすこともなくなるだるし、体力ない人でもAIに働かしていたら、助かるはず！何が悪い？
生身の人間は安全な場所にいてなんが悪いんだ！
IBMのAI「Watson（ワトソン）」君はどうするのだろう？
ITベンダーがエンジンとして有償契約しているハズだけど、ChatGPTに取られてしまうかな。
だったら真っ先にCEOの首を切るべきでは？
</details>
注意として soup.find_all　あとのクラス名ですが時期により変わったりしますのでその場合は本文pタグのすぐ上のpタグから引っ張ることです。他のコメントと同じpタグがあります。

#### 2. 正規化
テキストに余計な数字やアルファベット等あるのでそれらを取り除きます。
```
# エクセルファイルの読み込み
df = pd.read_excel("ファイル名.xlsx")

# コメント列の内容を正規表現により数字と不要な記号を削除
df["コメント"] = df["コメント"].apply(lambda x: re.sub(r"[0-9０-９]+|[^\w\s]+", "", str(x)))

# 結果を表示
print(df)

# エクセルに保存
df.to_excel("新しいファイル名.xlsx", index=False)
```

#### 3.形態素解析
形態素解析方法としてはMeCabとjanomeがありますが今回はMeCabを採用しています。
```
### MeCab辞書をインストール
!sudo apt install mecab
!sudo apt install libmecab-dev
!sudo apt install mecab-ipadic-utf8

### 辞書を使える場所に移す
!mv /etc/mecabrc /usr/local/etc/

### pipでインストール
!pip3 install mecab-python3
```
このMeCabを用いて、形態素解析するのですが感情や意見を含む文章の重要な要素を把握しネガポジ判定の精度の向上を期待したいので名詞、形容詞、動詞のみ抽出します。他の品詞の余計な情報を削除し見やすくしたいと思います。特にワードクラウドでもわかりやすくする目的もあります。
```
import pandas as pd
import MeCab

# エクセルファイルの読み込み
df = pd.read_excel("ファイル名")

m = MeCab.Tagger()

parses = []
for comment in df["コメント"]:
    parses.append(m.parse(comment)[:-5])

results = []
for parse in parses:
    parts = parse.split("\n")
    for part in parts:
        part = part.split(",")
        results.append(part)

dic = []
for result in results:
    surface, hinshi1 = result[0].split("\t")
    hinshi2 = result[1]
    base = result[-3]
    dic.append(dict(表層形=surface, 品詞１=hinshi1, 品詞２=hinshi2, 基本形=base))

# 辞書のリストをデータフレームに変換
df_parsed = pd.DataFrame(dic)

# 名詞、形容詞、動詞のみを抽出
df_filtered = df_parsed[df_parsed["品詞1"].isin(["名詞", "形容詞", "動詞"])]

# エクセルに保存
df_filtered.to_excel("新しいファイル名.xlsx", index=False)
```
以上のコードを各月各比較対象記事に全てしていきます。

#### 4.極性辞書を用いてのネガポジ判定
今回極性辞書は単語感情極性対応表を用います。これは「岩波国語辞書（岩波書店）」を参考に、-1から+1の実数値を割り当ています。 -1に近いほどnegative、+1に近いほどpositiveとなっています。
DLは　http://www.lr.pi.titech.ac.jp/~takamura/pndic_ja.html
今回はこれをエクセルに保存してからの実装をしています
```
import numpy as np
import pandas as pd

# 極性辞書のExcelファイルを読み込み
df_dic = pd.read_excel('/content/lr.pi.titech.ac.jp_~takamura_pubs_pn_ja.dic.xlsx', header=None, names=["Word", "読み", "品詞", "Score"])

keys = df_dic["Word"].tolist()
values = df_dic["Score"].tolist()
dic = dict(zip(keys, values))

# コメントデータの読み込み
df_comments = pd.read_excel('5月分のファイル名')

# ネガポジ分析
scores = []
for word in df_comments["表層形"]:
    score = dic.get(word, np.nan)
    scores.append(score)

df_comments["Score"] = scores
df_comments["Negativity"] = df_comments["Score"].apply(lambda x: "Negative" if x < 0 else "Positive")

# 欠損値を除外してスコアの平均値計算
mean_score = df_comments["Score"].mean(skipna=True)

# 結果表示
print("コメントデータ:")
print(df_comments)
print("\nスコアの平均値:", mean_score)
```
コードの結果が
コメントデータ:
       表層形 品詞1   品詞2   基本形     Score Negativity
0        人  名詞    一般     人       NaN   Positive
1       代替  名詞  サ変接続    代替 -0.771942   Negative
2      できる  動詞    自立   できる       NaN   Positive
3       先駆  名詞  サ変接続    先駆 -0.297840   Negative
4        的  名詞    接尾     的 -0.573711   Negative
...    ...  ..   ...   ...       ...        ...
9974    ある  動詞    自立    ある       NaN   Positive
9975    現状  名詞    一般    現状 -0.156964   Negative
9976    仕事  名詞  サ変接続    仕事 -0.958117   Negative
9977    使え  動詞    自立   使える       NaN   Positive
9978  おもちゃ  名詞    一般  おもちゃ       NaN   Positive

[9979 rows x 6 columns]

スコアの平均値: -0.5386188368310557
となりました。これらを４月中頃から７月中頃までのスコアのまとめが
| 4月 | ５月 | ６月 |７月 |
|:-:|:-:|:-:|:-:|
|  -0.5269304592790 |  -0.5386188368310 |   -0.5115191339271 |  -0.5085346262046 |
|  -0.27355345799368 |  -0.27252094469986  |  -0.2471554186356 |   -0.2513515615209|

この表なぜ２段あるかというと上は極性辞書に載っていない単語はNoneとして扱い平均スコアには含まれていない集計で、下の段は辞書に載っていない単語は0点のニュートラルとして集計に含めたものです。
今回は上段のスコアに含めない数値を採用します。
以上は今回調べたかった生成系AIについてのネガポジ判定で全く同じ方法で比較対象である大谷選手のコメントとマイナンバーカードのコメントの集計が以下
| | 4月 | 5月 |６月 | ７月 |
|:-:|:-:|:-:|:-:|:-:|
|大谷選手の記事   |-0.46783093195317| -0.46300107184079  |-0.43243207999999| -0.473802265605154  |
|マイナカードの記事|-0.530231977579|-0.5480626272935   |-0.5369033157084|-0.5372772192312|


## 5.結論
まずスコアの平均値から読み取れるのは、全体的にネガティブ寄りになっていることです。後から知ったのですがこの極性辞書を使ってのネガポジ判定は基本的にネガティブ寄りになることが多いようで、さらにヤフーコメントも全体的にネガティブ寄りな意見になりがちです。
今回はおそらくポジティブ寄りと思われる大谷選手の記事でさえ最も高くて約-0.43となり他の２つよりは比較的に高くなっています。
では各月のコメントのポジティブとネガティブの割合を各々算出したところ
<details><summary>割合の算出</summary>

```
import numpy as np
import pandas as pd

# 極性辞書のExcelファイルを読み込み
df_dic = pd.read_excel('/content/lr.pi.titech.ac.jp_~takamura_pubs_pn_ja.dic.xlsx', header=None, names=["Word", "読み", "品詞", "Score"])

keys = df_dic["Word"].tolist()
values = df_dic["Score"].tolist()
dic = dict(zip(keys, values))

# コメントデータの読み込み
df_comments = pd.read_excel('ファイル名.xlsx')

# ネガポジ分析
scores = []
positives = []
negatives = []
neutral = []
for word in df_comments["表層形"]:
    score = dic.get(word, np.nan)
    scores.append(score)
    if score > 0:
        positives.append(score)
        negatives.append(np.nan)
        neutral.append(np.nan)
    elif score < 0:
        positives.append(np.nan)
        negatives.append(score)
        neutral.append(np.nan)
    else:
        positives.append(np.nan)
        negatives.append(np.nan)
        neutral.append(0)

df_comments["Score"] = scores
df_comments["Positives"] = positives
df_comments["Negatives"] = negatives
df_comments["Neutral"] = neutral

# 結果をエクセルに保存
df_comments.to_excel("ファイル名.xlsx", index=False)

# 割合の計算と表示
total_count = len(df_comments)
positives_count = df_comments["Positives"].count()
negatives_count = df_comments["Negatives"].count()
neutral_count = df_comments["Neutral"].count()

positives_ratio = positives_count / total_count * 100
negatives_ratio = negatives_count / total_count * 100
neutral_ratio = neutral_count / total_count * 100

print("Positives割合: {:.2f}%".format(positives_ratio))
print("Negatives割合: {:.2f}%".format(negatives_ratio))
print("Neutral割合: {:.2f}%".format(neutral_ratio))
```
</details>

|  | 4月 |5月 | 6月 | 7月 |
|:-:|:-:|:-:|:-:|:-:|
|ポジティブの割合   |2.93%   |3.07%   |3.52%   |3.45%   |
|ネガティブの割合   |48.99%   |47.53%   |44.8%   |45.98%   |
|ニュートラルの割合   |48.09%   |49.4%   |51.68%   |50.57%   |

読み取れるのはポジティブが異常に少ない一方ニュートラルつまり辞書に載っていない単語がおおよそ50％もあります。上記でこのニュートラル含めるか含めないかの件についてはここで問題となり、ニュートラルが多すぎるのでこれを０点として含めると平均値がかなり変わってしまいました。これを参照記事も当てはめると。
※(大)は大谷選手の記事のコメント　(マ)はマイナカードの記事のコメント
|  | 4月(大)| 5月(大)| 6月(大)| 7月(大)| ４月(マ)| 5月(マ)|6月(マ)|7月(マ)|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| ポジティブ割合  |5.34%   |5.22%   |5.97%   | 4.53%  |3.05%   |2.31%   |2.65% |2.86%   |
| ネガティブ割合  |43.03%   |42.85%   |40.8%   | 41.64%  |51.19%   |51.44%   |52%|52.76%   |
| ニュートラル割合  |51.63%   |51.93%   |53.23%   |53.83%   |45.76%   |46.25%   |45.35%   |44.38%   |

比較対象である2つのコメントには明確な差がでており、こちらの想定通りポジティブ寄りとネガティブ寄りになっているかと思われます。
以上のことより、ネガポジ判定によると生成系AIについてヤフーコメントではネガティブ寄りにはなってしまうがより否定的な記事よりは幾分かはポジティブにも取られているのではないかと思われます。記事自体３か月しかとれななかったこともあり、スコア的には少しはポジティブ寄りになったのかなと（誤差の範囲かもしれませんが）。自身が記事を収集しているときには記事のタイトルにも生成系AIにより仕事がなくなるや子供や学生への影響等記事自体にも否定的なのが多い印象でした。
今回は極性辞書を用いてのネガポジ判定でしたが、一番の問題は辞書に載っていない単語が多すぎるではないかと思いました。3つとも約50％は載っていない単語のためやはり正確性には欠けるとは思いますが、数値としておおまかにネガティブかポジティブかはわかるものですね。

## 5.ワードクラウド
まずはGoogleColabにインストールする必要があるので
```
from wordcloud import WordCloud
import matplotlib.pyplot as plt
import codecs
!apt-get -y install fonts-ipafont-gothic #フォントのインストール
```
最初フォントのインストールをしていなかったのでワードクラフト当てると文字化けして使えなかったので必ずフォントのインストールはしましょう。
```
import pandas as pd
from wordcloud import WordCloud
import matplotlib.pyplot as plt

# エクセルファイルの読み込み
df = pd.read_excel("ファイル名.xlsx")

# コメントのテキストデータを抽出
comments = df["表層形"].tolist()

# テキストデータをスペースで結合
text = " ".join(comments)
# ストップワードの作成
stop_words = ["し","よう","の","さ","れ","ん","こと","てる","せ","いる","ある","する","なる","ない","い","もの","それ","でき","れる","あっ","なっ","これ","て","いい","すれ","しれ","そう","なり",
              "いう","せり","やっ","られ","いけ","いっ","くれ","しまっ","き","せる","なん","なら","ほう"]
# ワードクラウドの生成
wordcloud = WordCloud(font_path = '/usr/share/fonts/truetype/fonts-japanese-mincho.ttf', stopwords=stop_words,
    width=1000, height=600, background_color="white").generate(text)

# プロット
plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.show()
```
注意としてストップワードに設定していますが、形態素解析した時点でワードクラフトにするには不要と思われる言葉を除外すると結果がすっきりとしたものになるので、ここは手動で一個一個消しました。その結果が左上から７月～4月の順です。画像にポインタ当てると月がでます。
<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488838/f3d862d4-a1fe-d457-9996-305d02530288.png" title="７月">    <img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488838/8e93347e-4dd0-1567-12e5-e327454459a8.png" title="6月">
<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488838/681c5497-c653-4254-a27c-a980b08a8871.png" title="5月"> <img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488838/5ff07150-33a7-9aa0-02b4-cb9298e4965f.png" title="4月">
ワードクラウドは出現回数が多いほど大きい文字となるのですが、当然AIは頻出であと多いのは人間と仕事、必要、情報、規制、問題とかですね。ChatGPT4.0も暫く経過しAPI等利用した様々なサービスが展開されこれからもこういった生成系AIは発展すると思いますが、仕事に必要となり情報の漏えいなども問題もあり規制されるかもしれないですが、悪いのはシステムやAIではなく使う人間が正しく使っていけばいいのではないかと思います。

### 今後してみたいこと
BERTやTransformerも学習し単語だけの意味ではなく文章としての意味、Appleはリンゴなのか大手IT企業なのかを文面から推測でできるようなモデルを使ってみたいです。