歌詞からコード進行の生成 Attentionによる可視化

#はじめに
初めてのAdvent calendar参加です。

以前歌詞からコード進行を生成するseq2seqモデルを作りました。
[歌詞からコード進行を生成する](https://qiita.com/a2kiti/items/a83db8fd3932ee6d78d5)

本記事ではAttentionを用いてどの言葉に着目してコード進行を生成したのか可視化できるように改良してみます。
さらに、前のモデルでは単語の埋め込み層を1から学習しましたが、今回はsudachiで提供されている学習済みの分散表現を用います。

成果物はこちらです
[Lyrics to Chords](http://lyrics2chords.a2kiti.com/)

#Seq2seq with attention
seq2seqは時系列入力を特徴ベクトルにまとめるエンコーダーと特徴ベクトルから時系列出力を生成するデコーダーで構成されます。それぞれRNNとなっており、LSTMなどを用いることで長い時系列の入力もうまく扱うことができます。分かりやすい例は機械翻訳で、日本語の文を単語に分け順番にエンコーダーに入力したあと、デコーダーによって対応する英語の文を出力するように学習させたりします。

ここでAttentionという機構を導入すると、出力文中のある単語に対する、入力文中のそれぞれの単語の重みを得ることが出来るため、重みが大きいものがネットワークが着目している単語と推定することができます。
[Attention Seq2Seqで対話モデルを実装してみた](http://www.ie110704.net/2017/08/21/attention-seq2seq%E3%81%A7%E5%AF%BE%E8%A9%B1%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)
[今更ながらchainerでSeq2Seq（2）〜Attention Model編〜](https://qiita.com/kenchin110100/items/eb70d69d1d65fb451b67)

私はPytorchで実装しましたが、[pytorchのチュートリアルのコード](https://github.com/spro/practical-pytorch/blob/5e79b652c199421f54e11534b6bdf6257637a49b/seq2seq-translation/seq2seq-translation-batched.ipynb)だとattentionの計算に無駄にループが回っており非常に遅いため行列演算に書き換えたほうがいいです。

seq2seqなので入力と出力のそれぞれに対応する重みを2次元行列として得ることができます。しかし，ある単語を歌っているときにはその時刻に対応するコードがなっているはずなのに，他の時刻のコードの重みが大きいとなんだかよくわからなくなってしまいます。なので今回は出力方向には積分してしまい，そのコード進行全体に対してどの単語が大きく寄与したか，を可視化しました。

#学習済み分散表現の利用
Seq2seqに日本語文を入力する場合、RNNの前に全結合層を通して、one-hotベクトルから分散表現への変換を行います。

この全結合層も学習しても良いのですが、歌詞とコード進行が対応した学習データをたくさん集めるのは難しいですし、この世のすべての曲を集めたところで、世の中の文章データからすると僅かなものです。そこで、すでに大量の日本語文章から学習された分散表現を使ったほうが、ある程度言葉の意味を正しく捉えられると期待できます。

ワークスアプリケーションズが公開している学習済み分散表現はなんと100億語規模の国語研日本語ウェブコーパスのデータによって学習されているらしく、単語と分散表現の書かれたテキストファイルが12GBもあります。
使い方は簡単でgensimで簡単に読み込むことが出来ます。
[Sudachiベースの学習済みWord2Vecモデルを使う](https://ohke.hateblo.jp/entry/2019/06/01/120000)


ところで、個人で趣味でサーバーを借りてwebアプリを公開しようとすると、せいぜいメモリは1〜2GBとかです。よってgensimで12GBの分散表現を使おうとすると、メモリが全く足りなくなってしまいます。
ニューラルネットワークを学習させるときは、処理速度が欲しいのでメモリの多いマシンを使えばいいですが、予測に使う場合はそこまで速度は入らないので、毎回HDD（SSD）から読み込むようにします。いろいろ試した結果、下記のようにするのが一番速かったです。
[Sudachipyの学習済みword2vecを低メモリ環境で使用する](https://qiita.com/a2kiti/items/a93d762bd89a84316c12)

#結果

[『コード進行と歌詞の関係性』その２。定番のコード進行の使い分けによって同じ歌詞でも意味が違って聴こえます。](https://past-orange.com/po_sp/?p=976)
前回と同じように上記のサイトに乗っているコード進行のイメージを入力してどんなコード進行が生成されるか見てみます。
今回はどの言葉に注目したか可視化されます。
重みが大きい単語ほど赤く大きく表示されるようにしています。

### 長調系
Dm→G→Cの表現「伝わるだろうか？でも言おう！愛してる！」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201397/e7564480-5575-9582-ed82-2a626b7e79e9.png)


Dm→G→CM7の表現「伝わるだろうか？でも言おう！愛してる．．．これで良かったのだろうか」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201397/0fcd460f-d791-d2d2-c14f-5b33f23d338d.png)

A♭→B♭→Cの表現「愛してる！愛してる！！A・I・SHI・TE・RU！！！！我と共にあれ！！！！！！」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201397/1f46e0b3-e670-96f9-b9a9-9675cbfe3cd4.png)

### 短調系
Dm→Em→Amの表現「あぁ、愛したとも。愛してしまったのさ。叶わぬ恋さ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201397/ce1e5b1f-7bc3-feea-fbc0-146c6fa53f72.png)

Dm→E7→Amの表現「あぁ、愛したとも。とってもとっても！！愛した俺儚い切ない悲劇の主人公」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201397/63d59979-6c60-3cb4-7322-3288d0658dc8.png)

D7→E7→Amの表現「言うぞ、言うぞ！！愛し．．．やっぱりダメだ。人妻じゃないか」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201397/811bb520-c5d5-79ca-f7f7-70cd0b52f654.png)

# おわり
やっぱりなんとも言い難い結果。でもキーワードっぽいものに反応してそうな気はします。愛してるとか儚いとか人妻とか。
結局こういうのは評価が難しいですね。

ところでsudachiはApache License 2.0なのですが，webアプリの場合は表記しなくてもいいんですね。それともhelpあたりには書いとくほうがいいんでしょうか。なんとなく何も書かないのも気になりますし。