P&Dアドベントカレンダー21日目の記事です。もうすぐクリスマスですね
クリスマスプレゼントは最近iPhone6sが頻繁に強制終了をするので新しいiPhoneが欲しいです
## はじめに
いきなりですが、恋していますか?
Wikipediaには、
恋(こい)とは、特定の相手のことを好きだと感じ、大切に思ったり、一緒にいたいと思う感情。
とは書いてありますが、「恋」とは何なのかがよく分からなかったので、恋愛ソングを数多く書いている秋元康先生に聞いてみる事にしました。
どうやって聞くのか
さすがに、直接秋元康先生に尋ねる事はできないので、先生が執筆された歌詞を使って
「恋」と類似度が高い言葉は何かを解析して求めることにしました。
使う技術
- Python 3.6.5
- スクレイピング(BeautifulSoup)
- MeCab(形態素解析)
- Word2Vec(単語をベクトル化)
## 方法
1.歌詞データ収集
歌詞検索サービスUta-netから歌詞を取得しテキストファイルに保存するPythonのスクリプトを作成しました。
https://github.com/KohheiAdachi/getWordofSong/blob/master/getSong.py
このスクリプトを使用し、Uta-netで各アーティストをキーワード検索した時に出てきた曲をテキストファイルに保存しました。
from getWordofSong import getSong as gt
import os
song = gt.GetSong()
# ディレクトリ作成
Input_dirTitle = input("dirTitle:")
make_dir = "kashi/"+Input_dirTitle
os.mkdir(make_dir)
# 歌詞サイトのURL入力(検索ページ)
Input_url = input("urlを入力:")
print(Input_url)
# 歌詞ページのURL取得
kashiList = song.getSongList(str(Input_url),match="search")
for kashi in kashiList:
title,data = song.getWordofsong(kashi)
print(title)
song.save_kashi(title,data,make_dir)
アーティスト | 曲数 |
---|---|
AKB48 | 609 |
SKE48 | 230 |
NMB48 | 187 |
HKT48 | 78 |
NGT48 | 28 |
STU48 | 17 |
乃木坂46 | 183 |
欅坂46 | 81 |
合計1413曲分の歌詞を収集しました。
2.データの前処理
歌詞には英語も含まれており、英数字、記号を正規表現で削除し、日本語のみの歌詞にし、解析結果に影響が出ないようにします。
def read_doc(directory):
text = ""
for path in directory:
with open(path,'r',errors='ignore') as f:
text += f.read()
return text
from glob import glob
# ファイルパスを取得
filePathList = glob.glob("kashi/*/*")
# すべての歌詞を1つの文字列にする
kashi = read_doc(filePathList)
# 英数字の削除
kashi = re.sub("[a-xA-Z0-9_]","",kashi)
# 記号の削除
kashi = re.sub("[!-/:-@[-`{-~]","",kashi)
###3.形態素解析
MeCabを使用し取得した歌詞に対して形態素解析を行い、解析に使用する単語である名詞、動詞、形容詞、形容動詞を取り出し、動詞、形容詞、形容動詞に対しては基本形に直しました。
また、ストップワードを設定し、関係なさそうな単語は除きました。
def split_into_words(doc):
mecab = MeCab.Tagger("-Ochasen")
lines = mecab.parse(doc).splitlines()
words = []
stop_word = create_stop_word()
for line in lines:
chunks = line.split('\t')
if len(chunks) > 3 and not chunks[2] in stop_word:
if chunks[3].startswith('動詞'):
words.append(chunks[2])
if chunks[3].startswith('名詞') and not chunks[0] in stop_word:
words.append(chunks[0])
if chunks[3].startswith('形容詞'):
words.append(chunks[2])
if chunks[3].startswith('形容動詞'):
words.append(chunks[2])
return words
def create_stop_word():
target_url = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt'
r =requests.get(target_url)
soup=BeautifulSoup(r.text, "html.parser")
stop_word=str(soup).split()
return stop_word
4.Word2vecで学習
Word2Vecで学習させ各単語をベクトル化させました。
from gensim.models import word2vec
def learnig_word2vec(kashi)
sentence = [kashi]
model = word2vec.Word2Vec(sentence, size=200, min_count=4, window=5, iter=40)
# モデルを保存
model.save("AkimotoYasushi.model")
5.類似度の計算
学習したモデルに対して、「恋」という単語に対して類似度の高い上位10個の単語を出力します。
# モデルの読み込み
model = gensim.models.Word2Vec.load('AkimotoYasushi.model')
word = model.wv.most_similar(positive=[u"恋"], topn=10)
# 結果を出力
for i, lv in enumerate(word):
print(str(i) + " " + lv[0] + " " + str(lv[1]))
結果
「恋」という単語に対して類似度の高かった単語を出力した結果を以下に示します。
0 証拠 0.9997125864028931
1 浮気 0.999591588973999
2 ぃ 0.9995723962783813
3 縄 0.9995304346084595
4 キス 0.9995051622390747
5 頂戴 0.9995023012161255
6 傾向 0.9994885325431824
7 せる 0.9994684457778931
8 喧嘩 0.9994481801986694
9 起きる 0.999412477016449
証拠??
浮気??
縄(意味深)!!??
キス!!??
結果がいろいろと辛いので、さらにストップワードを追加したいと思います。
my_stop_word = ["角","無理","予約","ボウリング","立ち読み","ファッション","雑誌","糖","缶","ただ","すぎ","縄","発","偏差","ぃ","せる","頂戴","浮気","くる","びっくり","いじめ","しまう","くん","ふう","ちょうだい","コーヒー"]
# ストップワード追加
stop_word.extend(my_stop_word)
0 キス 0.9993709325790405
1 弱い 0.9993308186531067
2 存在 0.9993278384208679
3 負け 0.9993095397949219
4 生物 0.9992963075637817
5 起きる 0.9992321133613586
6 傾向 0.9991904497146606
7 克服 0.9991508722305298
8 逆転 0.9991446137428284
9 喧嘩 0.9991180300712585
恋とは、キスに弱くて、存在に負けて?、生物的で、起きる傾向があって?、克服して、逆転して、喧嘩するものなんですね!(適当)
グループごとで再計算してみた
学習する歌詞を各グループごとに絞って再びWord2Vecで学習させ、「恋」と類似度の高い単語を再計算させました。
AKB48にとって**「恋」**とは
0 美学 0.9974193572998047
1 理由 0.9970353245735168
2 閉じる 0.99690842628479
3 恋人 0.996814489364624
4 事件 0.9967169761657715
5 恥ずかしい 0.9966646432876587
6 舐める 0.9965479969978333
7 とこ 0.9963464140892029
8 わかる 0.996084451675415
9 奥 0.9960130453109741
乃木坂46にとって**「恋」**とは
0 意外 0.9484642148017883
1 告白 0.948411226272583
2 ハート 0.9470661282539368
3 キス 0.9470518827438354
4 ひらく 0.9429705142974854
5 絶対 0.9425839781761169
6 秘密 0.9415342807769775
7 大人 0.9398925304412842
8 許す 0.9393779635429382
9 磨く 0.9379326701164246
欅坂46にとっての**「恋」**とは
0 輝く 0.9968976974487305
1 ときめき 0.9954394102096558
2 奥 0.9930803775787354
3 大切 0.9909152388572693
4 過ごす 0.9906127452850342
5 日常 0.9903433918952942
6 昨日 0.9902833700180054
7 感じる 0.9896537661552429
8 思い出 0.9886585474014282
9 教える 0.9869102239608765
AKB48は美学
乃木坂46は意外
欅坂46は輝く
でした。
※それぞれに対して、ストップワード変更してます。
西野カナ先生にも聞いてみた
0 知る 0.9580930471420288
1 追いかける 0.9487483501434326
2 出会える 0.9459880590438843
3 失う 0.9414538145065308
4 向く 0.9401863813400269
5 言い聞かせる 0.9368431568145752
6 傷つく 0.9362004995346069
7 押す 0.9356939196586609
8 不思議 0.934490442276001
9 無理 0.9331055879592896
まとめ
- ストップワードをうまく設定しないと意味深な結果となってしまう。
- 秋元康の歌詞のエキスパートくらいの知識がないと結果に対して考察が難しい。
- 西野カナ先生に聞いたほうが良い結果が得られた。
最後に
結局「恋」とはなんだろうか...
ソースコードはGitHubにまとめてあります。
## 参考文献
西野カナに「恋」とは何か聞いてみた
https://blog.aidemy.net/entry/2018/06/15/174827