HIPHOPで学ぶDoc2Vecの使い方




去る11月13日、BAD HOP武道館ワンマン見に行ってきました。いやぁ----、めっちゃいいライブでした。
BAD HOPとは、川崎は池上出身のHip Hopクルーです。お世辞無しでいま一番勢いのあるHip Hopクルーなのではないでしょうか。

「俺たちがここ（武道館）まで最速で来れた理由ってのはなんだと思う？ 才能、努力、そんなんじゃねえよ。俺たちが跨ってるのが世界のKawasakiだからだよ」

こんなMCのあとに始まった[Kawasaki Drift](https://www.youtube.com/watch?v=I4t8Fuk-SCQ)。しびれましたね。
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/watch?v=I4t8Fuk-SCQ/0.jpg)](http://www.youtube.com/watch?v=I4t8Fuk-SCQ)

私はそんなに昔からHip Hopを聴いているわけでは無いですが、twitterでの反応を見ると、やっぱり日本語ラップ史に残る歴史的なライブになるんだろうなあと思いました。

最近はBAD HOPとかchelmicoとかTENG GANG STARRを聴いていますが、いまだ一番好きなラッパーはZORNです。


# ZORNについて

僕がはじめてZORNを知ったのが[このMV](https://www.youtube.com/watch?v=BRooYuDIifI)です。
いやー、すでに公開されてから4年も経ってしまいましたが、まあかっこいいですね！半端ないです。
MCが5人いますが、ダントツでZORNがヤバイですね。次点でMARIAでしょうか。
彼のバースの初めの方にこんなリリックがあります。

```
金に女 派手な車そんなもんは目の錯覚
誰も彼もエゴばかり 俺は俺の絵の中
娘達と手を繋いで散歩してる江戸川
```
`目の錯覚`
`エゴばかり`
`絵の中`
`江戸川`

でバッチリ韻を踏んでますね。`目の錯覚`と`エゴばかり`は最後ちょっと怪しいですが、**こまかいこたぁいいんです**。
`娘たちと手をつないで散歩してる江戸川`なんかもいいですね。ほっこりします。これで私の一個上です。信じられません。
この曲の出番の終わりの方ではこんなことも言っています。

```
みんな違ってみんないい 金子みすゞみたい
どうせ空は飛べない だから俺は俺なり
```

ちゃんと金子みすゞをサンプリングしてますね。実にHip Hopだと思います。みんな元ネタがわかるところも得点高いですね。無駄に誇張して自分を表現することはしていませんが、それでも高みを目指しているようなギラギラ感が垣間見える、とてもいいバースだと思います。


ZORNはアルバムもいくつか出していますが、私のお気に入りは[THE DOWNTOWN](https://zorn.tokyo/the-downtown)です。私はマレーシアのジャングルにある工事現場で半年間働いていた事があるのですが(もう竣工済みでなかなかに利益を出しているらしい)、朝6時半に会社のバンにのって現場に向かう際に毎日聴いていたので、個人的には[ドナドナ](https://www.youtube.com/watch?v=t0w-g2733Vk)的なアルバムです。40分くらいで終わるので通勤時に聴くのもちょうどよいボリューム感です。


# さて
そろそろ本題に移りたいと思います。

今回はそんなZORNの楽曲のリリックを題材にして自然言語処理の技術に触れてみます。

Hip Hopの起源をたどると、本来そのリリックに大した意味や思いは与えられていませんでしたが、現在ではリリックそのもの内容そのものが非常に重要視されています。

そこで、ZORNの楽曲のリリック`Doc2Vec`を用いてZORNの楽曲のリリックをベクトルで表して見ます。
今回はとりあえずグラフを出力するところまで実施しようと思います。

Doc2Vecの実装はフリ−のライブラリであるgensimを用いました。
Doc2Vecの詳細については今回記載しません。。


## やってみた

###　歌詞ファイルの読み込み

学習には[lyric master](http://www.kenichimaehashi.com/lyricsmaster/)というツールを用いて集めた2,490曲分の歌詞と、ZORNの楽曲の中から7曲を用いました。



```python
import glob

def read_lyrics(path):
    with open(path,"r") as f:
        lyric = f.read()
    return lyric

path = glob.glob("./lyrics/*.txt")
lyrics = [read_lyrics(p) for p in path]
titles = [p.split('/')[-1][:-4] for p in path]

path = glob.glob("./corpus/*.txt")
lyrics.extend([read_lyrics(p) for p in path])
titles.extend([p.split('/')[-1][:-4] for p in path])

titles[:7]
>>> ['my life',
 'walk this way',
 '東京watch',
 'Enter the downtown',
 'Reverse',
 'Weekend',
 'Fall asleep'] # zornの曲
```

### MeCabで分かち書き

そのままでは学習に使えないので、MeCabを用いて形態素解析を施します。

```python
import MeCab
def mecab(lyrics: list):
    dict_path = "/usr/local/lib/mecab/dic/mecab-ipadic-neologd/"
    m = MeCab.Tagger(f'-Ochasen -d  {dict_path}')
    unrequired_parts = ["記号", "BOS/EOS", "助詞", "接続詞", "副詞", "助動詞" ]
    
    m.parse("")
    wakachi = list()
    for i, sentense in enumerate(lyrics):
        node = m.parseToNode(sentense)
        temp = list()
        while node:

            if node.feature.split(',')[0] not in unrequired_parts: # 不要な品詞は追加しない
                temp.append(node.surface)
            node = node.next

        if len(temp) > 0:
            wakachi.append(temp)

    return wakachi


wakachi_lyrics = mecab(lyrics)
```

### TaggedDocumentメソッドでタグ付け

gensimの`TaggedDecument`を用いて、曲のタイトルと、歌詞を紐づけます。

```python
from gensim.models.doc2vec import Doc2Vec, TaggedDocument
print(len(titles),len(lyrics))
def tagging_document(titles, lyrics):
    tagged_documents = list()
    for t, l in zip(titles, lyrics):
        tagged_documents.append(TaggedDocument(words=l, tags = [t]))
    return tagged_documents

tagged_lyrics = tagging_document(titles, wakachi_lyrics)
```

### Doc2Vecの学習

学習を実施します。

```python
model = Doc2Vec(vector_size=50, window=50, min_count=1, workers=4,epochs = 500)
model.build_vocab(tagged_lyrics)
model.train(tagged_lyrics,total_examples=model.corpus_count, epochs=model.epochs)

model.save('doc2vec.model')
```

###　歌詞ベクトルを取得

それぞれの曲のタイトルに紐付いたベクトルを取得します。

```python
def get_lyricvectors(titles, model):

    vectors = list()
    for t in titles:
        vectors.append(model.docvecs[t])
    print(type(vectors))
    return vectors

lyric_vector = get_lyricvectors(titles,model)
```

### [umap](https://umap-learn.readthedocs.io/en/latest/) を用いて３次元に圧縮

次元圧縮にはumapを用いてみます。umapは[こちら](https://qiita.com/cheerfularge/items/27a55ebde4a671880666)の記事で解説されています。

```python
import umap
reducer = umap.UMAP(n_components=3)
embedding_vector = reducer.fit_transform(lyric_vector)
embedding_vector.shape
```

###　プロットの色とラベルを指定
図示した際にわかりやすいように色とタイトルを設定します。
適当です。

```python
color = [1 for i in range(7)]
color.extend([0.2 for i in range(2490)])
import copy
title_text = copy.copy(titles)
title_text[7:] = ''  #すべてのタイトルを表示させるとplotlyがおもすぎる
```

### plotlyで可視化
グラフの作成には[plotly](https://plot.ly/python/)を使ってみました。
初めて使いましたが、結構簡単です。

```python
import plotly.plotly as py
import plotly
import plotly.graph_objs as go
import numpy as np
plotly.offline.init_notebook_mode(connected=True)

x, y, z = embedding_vector[:,0], embedding_vector[:,1], embedding_vector[:,2]

trace1 = go.Scatter3d(
    x=x,
    y=y,
    z=z,
    mode='markers+text',
    marker=dict(
        size=5,
        color= color,
        opacity=0.3
    ),
    text = title_text
)

layout = go.Layout(
    margin=dict(
        l=0,
        r=0,
        b=0,
        t=0
    )
)
data = [trace1]
fig = go.Figure(data=data, layout=layout)
plotly.offline.plot(fig, filename='./simple-3d-scatter.html')
```

[出来上がったグラフはこちら](https://plot.ly/~yukiyamada/3/)


# おわりに

後半はやっつけ感が多少出てしましましたが、とりあえずグラフを出力するところまでは実施できました。
今回は考察もクソも何もできていないので、今後少しずつ続きをやっていこうと思います。