雲のメモ帳

猫とクラウドと旅行が好きなインフラエンジニアです。 日々の調べたことや興味が持ったことをこのブログにアウトプットします。

ポルカドットスティングレイの歌詞データをPythonで機械学習、分析してみた -word2vec‐

これはなに?

前回自然言語処理をする環境構築をやってみたので、GWの宿題として実際に分析をやってみました。
せっかくなので自分が好きな分野で分析してみたかったので、好きなアーティストのポルカドットスティングレイの歌詞データを自然言語処理、分析してみました。

やること

ポルカドットスティングレイの歌詞データをMecabを使って形態素解析、コーパスの作成、作成したコーパスからWord2Vecを使ってモデルを作成します。作成したモデルに単語をインプットし、類推する言葉をアウトプットとして出力していきます。

f:id:ykoomaru:20210509110347p:plain

有名どころで言うとWikipediaのデータを学習することがよくあります。WikiPediaを学習したモデルに、「音楽」といれると、類推語に「クラシック音楽」や「ゴスペル」など、近い言葉がアウトプットとして出力されるのが、利用イメージです。
word2vecの詳しい説明は以下のリンクを参照してください。

qiita.com

ポルカドットスティングレイとは

福岡出身の雫さんという女性がメインボーカルのバンドです!フェスに行ったら必ず出演されてるくらい人気のバンドで、最近CMソングちょくちょく楽曲聞くくらい知名度は上がってきています。とりあえず最高なので聞いたことなかったら、テレキャスター・ストライブをまず聞いてみてください!
polkadot-stingray.jp

ポルカドットスティングレイ「テレキャスター・ストライプ」MV - YouTube

環境

  • インフラ: Docker
  • 言語: Python3
  • 作業環境: Jupyter Lab
  • 機械学習
    • 形態素解析: Mecab
    • Word2Vec: gensim

JupayterLabの環境を前提として記載していきます。
環境の構築については、以下の過去記事を参照してください。

www.cloudnotes.tech

モデル作成

1.歌詞データ取得

それではモデルの早速モデルの作成からやっていきます。
歌詞データについては、以下のブログのコードそのまま使えば取得できるので参照してください。
歌詞をそのまま載せると怖いのでぼかしていますが、Dataframeの形としては図の通りとなります。

qiita.com

f:id:ykoomaru:20210508224302p:plain

2. コーパス作成

取得した歌詞データからコーパスを作成します。
各曲の歌詞を形態素解析した結果を分かち書きにしてDataframeに格納しています。 Word2Vecでモデルを作成する際に、コーパスは分かち書きである必要があるので、分かち書きで処理しています。

# 分かち書きの文章を返す
def lang_extract(text):
    m = MeCab.Tagger("-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/")
    node = m.parseToNode(text)

    corpus = []
    pos_list = ['名詞','動詞','形容詞']
    while node:
        word = node.feature.split(',')[6]
        pos = node.feature.split(",")[0]
        if pos in pos_list:
            corpus.append(word)
        node = node.next
    
    return " ".join(corpus)


artist_df['Corpus'] = artist_df['Lyric'].apply(lambda x: lang_extract(x))

# コーパスの作成
corpus = []
tmp_corpus = []

for text in artist_df["Corpus"]:
    text_list = text.split(' ')   
    tmp_corpus.append(text_list)

f:id:ykoomaru:20210508224446p:plain

3. ストップワードの抽出/削除

歌詞データから抽出したコーパスの中には、’れる’、’こと’ 等の分析しても意味を持たない文字があるので予め分析対象から外します。

# 歌詞データの中で頻出単語表示(上位100位)
tmp_corpus = list(itertools.chain.from_iterable(tmp_corpus))
collections.Counter(tmp_corpus).most_common(100)

ひらがな一文字とかいらないワードが結構あるので、抽出します。

f:id:ykoomaru:20210508224754p:plain

抽出したストップワードをリスト化して、コーパスから削除します。

# 意味を持たない文字はストップワードとしてリスト化。学習の対象外とする。
stop_word = ['くれ','とめ','きっと','とれる','それぞれ','つけ','とっ','つけ','いる','の','し','い','こと','さ','ない','ん','よう','なっ','いい','しまっ','てる','みたい','する','れ','て','a','もん','き','まま','なる','is','ちゃっ','in','・','the','そう','でき','m','あっ','よ','行っ','く','しよう','つい','せ','これ','なり','やっ','み','かけ','うち','たっ','なら','られる','よそ','なん','ため','いか','あな','いろ','こ','なれ','なれる','したっ','しよ','まみれ','した','しまい','っ','はず','もと','あれ','いら','ちゃえ','ほう','したい']

 # ストップワードをコーパスから除外する
for text in tmp_corpus:
    result = list(filter(lambda x: x not in stop_word, text))
    corpus.append(result)

# ストップワード除外前/後の件数
print(len(list(itertools.chain.from_iterable(tmp_corpus))),len(list(itertools.chain.from_iterable(corpus))))

4. 学習(word2vec)

ストップワードを除外したコーパスをgensimを使って学習しモデルを作成します。
gensimのパラメータは結構適当で、何度かトライ&エラーしてそれっぽい値が出たものにしています。
コーパスが少ないので、min_countは2回としています。

# Word2Vec学習
from gensim.models import word2vec

model = word2vec.Word2Vec(corpus,
                          size=100,  # ベクトルの次元
                          min_count=2,  # 最小2回以上出ている単語のみの使用
                          window=5,  # window幅
                          iter=3000  # 学習の繰り返し数
                         )

# 学習した言語数
len(model.wv.vocab)

分析

歌詞データを学習したモデルに、色々と質問してみました。
学習したデータによって出力される類似ワードが異なってくるので、雫さん作詞のデータを学習した今回のモデルはAI雫さんといっても過言でないはず!

1. 類似度

"word" に単語を入れることで、類似するワードを出力してくれます。

posi_word = ['<word>']

result = model.wv.most_similar(positive=posi_word,topn=10) 
df_result = pd.DataFrame(result,columns=["単語","類似度"])
display(df_result.T)

「音楽」とは何か <AI雫さんモデル>

f:id:ykoomaru:20210508234732p:plain

「ぶちまける」がそれっぽい感じがします笑
テレキャスター・ストライプを学習した結果が強く出てますね。

「恋」とは何か <AI雫さんモデル>

f:id:ykoomaru:20210508234953p:plain

恋とは「放課後」とか甘酸っぱい感じがしますが、「困る」や「ネバーランド」というワードがあるので、現実はそんなに甘くないのかもしれない。。。

2. 言葉の足し算

せっかくなので言葉の足し引きもやってみます。

足し算は、”word” に複数の単語を指定すればOKです。

posi_word = ['<word1>','<word2>']

result = model.wv.most_similar(positive=posi_word,topn=10) 
df_result = pd.DataFrame(result,columns=["単語","類似度"])
display(df_result.T)

「恋」 +「音楽」 = ? <AI雫さんモデル>

f:id:ykoomaru:20210509000241p:plain

恋と音楽を足すと「わがまま」となりました。
類似度が低いのでちょっと微妙ですね。

3. 言葉の引き算

”nega_word”に、”word” から引きたい単語を指定すると、言葉の引き算ができます。

posi_word = ['<word>']
nega_word = ['<nega_word>']
result = model.wv.most_similar(positive=posi_word,negative=nega_word,topn=10) 
df_result = pd.DataFrame(result,columns=["単語","類似度"])
display(df_result.T)

「人生」 - 「音楽」= ? <AI雫さんモデル>

f:id:ykoomaru:20210508235333p:plain

人生から音楽を引くと「Blue」になるみたいですね。うーん?

他のモデルとの比較

モデルによって類似語にどれだけ差異がでるか簡単に比較してみました。

「音楽」とは何か <AI康司君モデル>

私が好きなフレデリックの歌詞データを学習したAI康司君にも雫さんと同じく音楽とは何か質問してみました。結果としてはAI雫さんとAI康司君で、結果が全然違いますね!
「音楽という名前の服」の学習結果から「着る」との距離が近くなっているようです。

f:id:ykoomaru:20210509000614p:plain

「カスタネット」とは何か <AI康司君モデル>

ついでに、音楽とは の回答にも入っていますが、フレデリックといえば「カスタネット」だと思うので、カスタネットについて質問してみましょう。 もしカスタネットがピンとこない方は、「オドループ」という曲を聴いてみてください!

フレデリック「オドループ」Music Video | Frederic "oddloop" - YouTube

f:id:ykoomaru:20210509000937p:plain

カスタネットの類推語として「たんたん」が出力されたので、大満足でした笑

「音楽」とは何か <Wikipediaモデル>

ちなみにWikipediaモデルを使った類似語と以下のような結果となります。
学習しているデータ数が歌詞データとは桁違いなので、類似度がかなり高い言葉が返ってきます。ただ、歌詞データを学習させたモデルのほうが個性を感じるので分析としては歌詞データのほうが面白いですね。

f:id:ykoomaru:20210509172903p:plain

最後に

初めて歌詞データを使った分析をしてみましたが、それっぽい分析結果が出力されて結構満足しました。Pythonはライブラリが充実しているの環境さえ用意できれば、簡単に分析ができました。
備忘録としては、コーパスが少ないため、結構未学習単語によるエラーが発生しました。猫を入力したら雫さんの愛猫のビビがでてきてほしかったのですが、歌詞データでは学習できずエラーとなりました。。。ビビの歌でもできてくれたら、より回答が雫さんっぽくなる気がしています。 今回学習したコーパスが1000くらいだったので、最低でも1万語くらいは必要じゃないかと思いました。

次はWord2Vecの実行結果を可視化してみようかなと思っています。

参考サイト

Github

github.com

gist2729cce02799adad35bed865623de85c