#対象読者
本投稿は、以下のようなテーマに関心がある!あるいは実装したい!というかたにうってつけです。
- 青い鳥のSNSをさくっと自由にスクレイピングしてみたい
- 日本語の自然言語処理の前処理のお作法をかるく知りたい
- トピックモデルのひとつ**「LDA」を実装し、わかりよく可視化**してみたい
#自己紹介
はじめまして、三度の飯より味噌が好き、味噌アライグマと申します(ここでは味噌は調味料といたします)
スイカもたしなむ
わたくし、勤め先にて旅行にかんする営業を担当している関係で、かねてよりマーケティング手法としてのデータサイエンスに高い関心を抱いておりました
たとえば社内で旅行企画ひとつ提案するにしても、経験知にたよるのみでなく、データをもとにそれなりの論拠をもってつよくアピールできればな、などと常日頃考えており・・・
巷で勢いあるAI・データサイエンスなどのスキルは、きっとその後押しをしてくれるだろうと考えていたのです
「いえ!データによりますと・・・これがウケます!!(はい論破w)」
そこで一念発起、AIスクールとして有名どころであるAidemyさんのPremium Plan受講を決断いたしました。
まったくのデータサイエンス初心者であった味噌アライグマでしたが・・
Aidemyさんによる愛ある(?)教育をとおし、プログラミング言語“python”の基礎から機械学習モデルの選定・学習・性能評価まで、ひととおりのスキルを網羅的に身につけることができたと自負しております(まだまだ修行中の身ではございますものの)。
さて、これより先は、Aidemyさんでの成果物としてトライした事のひとつ、すなわち表題にありますとおり、「日本のみなさんはコロナ状況下で“旅行”についてなにを考えているのか?を分析してみた」 際の方法と、その結果・考察についてご報告したいとおもいます。
#やったこと
旅行関係という勤め先の業界柄、コロナのあおりをはげしくうけております。
端的に、「やっちまったなあ!」というかんじです
しぜん、不肖わたくし味噌グマは、このような状況下で世間の皆さまが**「旅行」一般についていったいなにを**考えていらっしゃるのかに、おおいなる関心を抱いたのです。
またあわよくば、マーケティングに活かせないかなぁ、などと画策したり・・
そこで、Aidemyさんにおける学習成果物として・・
「旅行」にかんする「大衆意見」の「傾向」的なものをスマートに把握するため、以下のような実験を考案いたしました。
- 「SNS」から「旅行・コロナ」と同時につぶやいているものをスクレイピングする
- 自然言語処理ツールによる前処理をほどこす
- 「教師なし学習」の一手法**「LDA」によって、「旅行・コロナ」のつぶやき群からトピック**(観点あるいはテーマ)を抽出してくる
おっと、すでに「スクレイピング」・「教師なし学習」・「LDA」など、とくに初心者の方には耳慣れない用語を並べてしまったようにおもいます(ドヤァ)
次の章では、それらもふくめ、わたくしの実験をばくっと理解するのにひつような前提知識たちを導入しておきます(「とくに問題ないで~^^」という方は、**「実装と結果」**の章までスキップ頂くとよいかと存じます)
#前提知識
#実装と結果
##環境
-
開発環境:Google Collaboratory
- Hardware Accelerator: None
- Runtime Shape: Standard
-
日本語解析ツール:mecab-python3-0.7
-
スクレイピングツール:twint
環境につきまして、上から順にコメントしてまいります。
- より多くの方に気軽に再現して頂きたく、今回はローカルでなく、Google Colabを使用しております。
- 日本語解析ツールとして、今回はmecabを使用しております(他にjanomeなどがメジャーですが、好みでよいかと存じます)
注意点としまして、Colabにはmecabはプリインストールされておらず、手動でのインストールが必要となります。
以下を順に実行し、適切なインストールを試みていただけますと幸いです。
!apt install aptitude
!aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y
!pip install mecab-python3==0.7
3. 青い鳥SNSからのスクレイピングには、pythonで書かれた高度なツール"twint"を使用いたしました。
ただしこちらはローカルにて使用しております。
インストールはターミナル(ないしgit bashなどで)以下のように可能です(pip3が入っていることが前提です!)
pip3 install twint
##実装
順を追ってご説明します。
まず、青い鳥アイコンのSNSからから、上述のTwint:Pythonベーススクレイピングツールを用い、以下条件に絞ってつぶやきをかき集め、.csv形式で保存いたしました。
- 「コロナ」「旅行」をかならず含む
- 2020/1/1 以降に投稿されている
- いいねが5件以上ついている
具体的には、以下をターミナルにて実行したのみです。
アカウントすら必要なく、めちゃくちゃ簡単でした。
twint -s "コロナ, 旅行" --since 2020-01-01 --min-likes 5 -o covid_travel_sinse2020.csv --csv
結果、92908件のつぶやきを収集いたしました。
出力先は“covid_travel_sinse2020.csv”と命名しております。
twintの使用法は以下にくわしいです。
https://github.com/twintproject/twint/wiki/Basic-usage
なお、いいね数5件以上にしぼった理由は、集まってくるつぶやきたちが(「正常」なトピック分類を阻害しない程度に)あまりにクセ強でないことを担保できるかな、と考えたためです。
データがあつまりましたので、以降は完全にColab環境にて開発しております。
まずは必要なモジュールのインポートです。
import pandas as pd
import MeCab
import gensim
import numpy as np
import matplotlib.pyplot as plt
先述のMecabインストールが済んでいないと、ここで持ってくることはできませんので、ご注意を。
次に、さきほどスクレイピングしたつぶやきファイルをDataFrameにします。(Driveのマウントをお忘れなく!)
df = pd.read_table("covid_travel_sinse2020.csv")
不要なカラムをガンガン削ります。2度に分けていることに特に意味はありません。
df = df.drop(['id', 'conversation_id', 'created_at', 'timezone', 'user_id', 'name', 'place', 'geo', 'source', 'user_rt_id', 'user_rt', 'retweet_id', 'reply_to', 'retweet_date', 'translate', 'trans_src', 'trans_dest'], axis=1)
df = df.drop(['language', 'mentions', 'urls', 'photos','hashtags', 'cashtags', 'link', 'retweet', 'quote_url', 'video', 'thumbnail', 'near'], axis=1)
ここらでつぶやきがきちんと取得できているか、ちょっぴりのぞいてみましょう。
df.head()
いろんな方が、いらっしゃいますね。。
いろんなつぶやきが、ありますね。。
でも今はあんまり考えすぎず先にすすみましょう。9万件ありますから
さあ、ここからはLDAが処理できるよう、つぶやきたちを加工して参ります。
まずは形態素解析用の関数を定義します。
Mecabを使用しておりますが、とくに変わったことはしておりません。
品詞分類までできた結果は、一度DataFrameにしてしまう予定です。
def parse(tweet_temp):
t = MeCab.Tagger()
temp1 = t.parse(tweet_temp)
temp2 = temp1.split("\n")
t_list = []
for keitaiso in temp2:
if keitaiso not in ["EOS",""]:
word,hinshi = keitaiso.split("\t")
t_temp = [word]+hinshi.split(",")
if len(t_temp) != 10:
t_temp += ["*"]*(10 - len(t_temp))
t_list.append(t_temp)
return t_list
def parse_to_df(tweet_temp):
return pd.DataFrame(parse(tweet_temp),
columns=["単語","品詞","品詞細分類1",
"品詞細分類2","品詞細分類3",
"活用型","活用形","原形","読み","発音"])
つぎは、つぶやきたちから一般名詞と固有名詞のみを抽出し、それら単語をBag-of-Wordsで出力する関数を定義します。
def make_docs_for_lda(texts):
docs = []
for i, text in enumerate(texts):
print(str(i+1) + " th parse START")
df = parse_to_df(text)
extract_df = df[(df["品詞"]+"/"+df["品詞細分類1"]).isin(["名詞/一般","名詞/固有名詞"])]
extract_df = extract_df[extract_df["原形"]!="*"]
doc = []
for genkei in extract_df["原形"]:
doc.append(genkei)
docs.append(doc)
return docs
ではじっさいにつぶやきを加工してあげます
9万件なので、わりと果てしないです。無我です。
Colabの「90分ルール」などお気をつけて。
texts = df["tweet"].values # ndarrayにしてあげる
docs = make_docs_for_lda(texts)
ここで心配性な味噌グマはいったんセーブしたりします。
けっこう時間かかりましたからね。
import pickle
with open("./docs_01", mode="wb") as jugemu:
pickle.dump(docs, jugemu)
"""
docs = []
with open("./docs_01", mode="rb") as gokoh:
docs = pickle.load(gokoh)
"""
おつぎはLDAのための辞書とコーパス作成 です。
ここも特に変わったことはしておりません。gensimで一発です。
セーブ癖は健在です
dictionary = gensim.corpora.Dictionary(docs)
corpus = [dictionary.doc2bow(doc) for doc in docs]
corpus = []
for i, doc in enumerate(docs): # gonna rep 92908 times
corpus.append(dictionary.doc2bow(doc))
print(str(i+1) + " th doc2bow DONE")
with open("./coupus_01", mode="wb") as kaijarisuigyo:
pickle.dump(corpus, kaijarisuigyo)
"""
corpus = []
with open("./coupus_01", mode="rb") as suigyomatsu:
corpus = pickle.load(suigyomatsu)
"""
ではおまちかね、LDAの学習です。
「前提知識」セクションでご説明のとおり、トピック数は任意です。
今回は本当に勘で6に設定してみました。
n_topics = 6
lda = gensim.models.ldamodel.LdaModel(corpus=corpus,
id2word=dictionary,
num_topics=n_topics,
random_state=0)
いろいろと定量的な分析も試みましたが、うまくまとまらなかったため(汗)、定性的分析で締めることにいたしました。
そうなると、やはりWordCloudによる可視化がファーストチョイスかなとおもい、迷わず実装いたしました。
def normarize_then_square(x, alpha, axis=None):
#2次元配列を列方向に正規化し2乗する関数
x_min = x.min(axis=axis, keepdims=True)
x_max = x.max(axis=axis, keepdims=True)
return ((x - x_min) / (x_max - x_min))**2 + alpha
def weight_dict(dict, alpha):
dict_arr = np.random.randn(1,2)
#print(dict_arr.shape)
for i, value in dict.items():
row_i = np.array([i, value]).reshape([1,2])
dict_arr = np.block([[dict_arr], [row_i]])
#print(dict_arr.shape)
dict_arr = dict_arr[1:,:]
#print(dict_arr.shape)
#print(dict_arr)
keys = dict_arr[:,0] .reshape([len(dict),1])
values = dict_arr[:,1].reshape([len(dict),1]).astype('float64')
values_weighted = normarize_then_square(values, alpha, axis=0)
arr_weighted = np.concatenate([keys, values_weighted], 1)
#print(arr_weighted)
#dict_weighted = dict(arr_weighted)
return arr_weighted
from wordcloud import WordCloud
import math
FONT = "C:/Windows/Fonts/07YasashisaAntique_0.otf"
ncols = math.ceil(n_topics/2)
nrows = math.ceil(lda.num_topics/ncols)
fig, axs = plt.subplots(ncols=ncols, nrows=nrows, figsize=(15,7))
axs = axs.flatten()
def color_func(word, font_size, position, orientation, random_state, font_path):
return 'darkturquoise'
for i, t in enumerate(range(lda.num_topics)):
print(str(i)+ ":" + str(t))
x = dict(lda.show_topic(t, 40))
x.pop('コロナ')
try:
x.pop('ウイルス')
except KeyError:
pass
try:
x.pop('ウィルス')
except KeyError:
pass
print(x)
#x = dict(weight_dict(x,0.01))
print(x)
im = WordCloud(
font_path=FONT,
background_color='white',
color_func=color_func,
random_state=0
).generate_from_frequencies(x)
axs[i].imshow(im)
axs[i].axis('off')
axs[i].set_title('Topic '+str(t))
plt.tight_layout()
plt.savefig("./visualize.png")
##結果
以下のとおりでした!!
なお「WordCloud」は、特定トピックにおける出現確率のより高い単語をより大きく表示するよう、うまいぐあいに1枚の図に収めたものです(縦書き、横書きのちがいに意味はありません)
あるトピックがどういった性質をもつのか視覚化しやすいので、トピック分類の定性的分析にはもってこいなのです。
##考察
WordCloudによる可視化結果から、各トピックが何を示すか、さらりと推測してみました。
-
Topic 0: 感染源、または水際対策
「中国」や中国国内特定地名、「マスク」あるいは「クルーズ」、などに着目しました。「日本に入るんか!?入らへんか!?入るんか~い!!!」みたいな時代もありましたねえ(遠い目)。。 -
Topic 1: 国内地域別状況への関心、あるいはあおりを受けた業界への注目
国内地名、「株」・「ホテル」・「航空」・「見送り」などの単語が目立ちました。どこは大丈夫・どこは危険、はたまた解雇やら倒産やら、暗いニュースにかんするつぶやきが多く成分になっていそうです。直視するにつらいものがありますね。 -
Topic 2: 海外、特に東アジアへの興味関心
2021年度初期・「春節」の頃穴場の地域であったり、渡航可能な目的地の候補挙げて指している。ように見えました。「沖縄」なども流行りましたね。いっぽう「月」は謎です。こういうときは、個々のツイートを見てみないといけませんね。 -
Topic 3: 「わたくしごと」としてのコロナ蔓延
Topic0と近いようにおもいました。ただ、病状にかんする単語「新型」「肺炎」「症状」「患者」、あるいはなんらか政治的レスポンスを想起させる単語(「政府」「国民」)から、多くの方がコロナが自分自身にも降りかかること意識しはじめ、いよいよ危機感・焦燥感をつのらせている、そういった観点に見えます。 -
Topic 4: 各国の状況および対応
各国の状況が明らかになった時点のツイートがおもな成分ではないでしょうか。(例:「台湾」での優秀人材による封じ込め成功と先進国「イタリア」の早期医療崩壊の対比、あるいは我が国のトップによるユニークな政策) -
Topic 5: 私生活の変化・心情、あるいは適応
見るからに、コロナで我慢を強いられることになった事柄への心情を吐露している群でしょう。「イベント」「修学旅行」「思い出」「楽しみ」、どの単語をみても胸がふたがるような気がします。しかしこういった赤裸々な観点こそマーケティングに活かしやすいのではないかとも考えました。また一部は「自分なりに適応しだしたよ」いう観点かもしれません。
##Future Plan
以下は次にためしてみたいことのリストです。
- 時期をずらしてみる。第n波ごとに時期を分割してみる
- 別のクエリの組み合わせで現れる変化を試す
- 特定トピックを掘り下げるような分析をする
#おわりに
教師なし学習、トピックモデルの概念や解釈や難しかったりしましたが、Aidemyさんでまなんだ知識と技術をフル活用でき、われながら良い題材を選んだものだと関心(?)しました。
いっぽうとくにLDAのトピック分類結果は解釈がつきづらいこともしばしばだと、多くの文献で目にしましたが、今回はきれいな結果が得られたように見え、感動しております。まだまだ分析の余地はありますが。
今後も学習を継続してまいります。
ご高覧いただきありがとうございました。
#参考文献
文献自然言語処理による文書分類の基礎の基礎、トピックモデルを学ぶ
https://qiita.com/icoxfog417/items/7c944cb29dd7cdf5e2b1
【入門】トピックモデルとは
https://spjai.com/topic-model/
トピックモデルの学習で初学者に分かりづらいポイントについての解説
https://acro-engineer.hatenablog.com/entry/2017/12/11/120000 MeCab で形態素解析をしてみよう
https://toukei-lab.com/natural-language-python
LDA によるトピック解析https://qiita.com/Spooky_Maskman/items/0d03ea499b88abf56819
PythonでLDA(トピックモデル)の実装
http://mathshingo.chillout.jp/blog27.html
LDA の学習、WordCloud
https://ie110704.net/2018/12/29/wordcloud%E3%81%A8pyldavis%E3%81%AB%E3%82%88%E3%82%8Blda%E3%81%AE%E5%8F%AF%E8%A6%96%E5%8C%96%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/