ラップのボットで遊ぼっと。

# はじめに

こんにちは。ヴァイブス高まってますか？
この記事は [ボット(Bot) Advent Calendar 2017](https://qiita.com/advent-calendar/2017/bot) の18日目です。

ドープな読者のみなさんならご存知かもしれませんが、[昨年のアドベントカレンダー](https://qiita.com/masaki925/items/eafd0b3c46baeccc5507) にて、自由奔放にライムをかますチャットボットを紹介させていただきました。

今年はその続編です。

前回のおわりに、こんなことを書いていました。

> チャットボットは導入のハードルはすごく下がりましたが、そこからどういうコンテンツが生成できるかはアイデアや工夫が必要だなと感じました。
> 今回はRhyme の生成にあんまり賢いロジックを入れられてないので、そのあたりをうまいこと処理して、ヴァイブスの高みを目指せるようにしていきたいですね。

ということでいいアイデアがないかと探していたところ、[DopeLearning: A Computational Approach to Rap Lyrics Generation](https://arxiv.org/pdf/1505.04771.pdf) という論文に出会いました。 (※ Deep ではなくDope)

昔の人がこういうことを言っています。
「二流は技術を真似する。一流は技術を盗む。」

僕は論文を読んだ瞬間、こう思いました。
「よし、真似をしよう。」

そして生まれた今年のラップボットがこちら、パンのピーさんです。

<img width="394" alt="Screen Shot 2017-12-18 at 4.11.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/27788/0a519d98-adbe-d32f-4b6f-c718000e9595.png">

うーん、絶妙な距離感。笑

今回は機械学習を使用しているため、どこかで聞いたことのあるようなライムが返ってくる仕様になっています。
あまりにノスタルジックなライミングに、彼の頭に乗っている文字が英語でおしっこという意味だなんて吹き飛んでしまいますよね。

そんなピーさんも、昨年のファーさんと同じくLINE で遊ぶことができます。
ぜひ自分の腕を試してみてくださいね。

![LINE_pan_pee_QRcode.png](https://qiita-image-store.s3.amazonaws.com/0/27788/ee060024-8e5c-0e6d-ca5b-2d12476b9602.png)
https://line.me/R/ti/p/7OwcJKcubK

※ 現在、レスポンスがめちゃくちゃ遅いです。。すみません。そのうち直したいです。

# ピーさんが生まれるまで

ソースはこちらです。去年と同じリポジトリを使いました。
https://github.com/masaki925/cypher-bot

また、Heroku でsvm_rank コマンドを利用するため、下記のbiuldpack を作りました。
https://github.com/masaki925/heroku-buildpack-svmrank

heroku push するとビルド時にめっちゃワーニングでるけど。。ビッグアイでルッキングしてください。

## 参考にした論文について

あらためて、[DopeLearning: A Computational Approach to Rap Lyrics Generation](https://arxiv.org/pdf/1505.04771.pdf) の内容についてさらっと触れておきます。

ざっくり言うとタイトルの通り、機械にラップの歌詞を生成させるという研究論文です。

この中で、ラップの生成を「ある歌詞が来たときに、次にどんな歌詞が来るか」という問題と捉え、検索結果などでの「あるクエリが来たときに、どんなページをどんな順番で出すか」という"情報選択問題" に置き換えて問題を解く、というアプローチを取っています。

今回はこの考えを真似させてもらいました。

もう少し具体的には下記のことをやっています。

- ある歌詞と次の歌詞の相関スコアを計算する、評価関数を定義
    - EndRhyme: 語尾の母音の一致性 (韻を踏んでいるか)
    - EndRhyme-1: ある歌詞の前の歌詞との、語尾の母音の一致性 (韻を踏んでいるか)
    - LineLength: 文字数の一致性
    - BOW(Bag of Words): 出て来る単語の一致性
    - etc...
- 世の中に存在するラップの歌詞を学習データとして用意
- [RankSVM](https://www.cs.cornell.edu/people/tj/svm_light/svm_rank.html) という学習モデルを利用して、ラップの歌詞を学習
- ある歌詞に対して、あらかじめ用意された歌詞候補との相関スコアを計算し、ランキング化
- 正解の歌詞をどのくらいランキングの上位に表示できたかの精度を検証

だいぶざっくり書いているので、興味のある方はぜひ原文を参照ください!


## やったこと

ベースとなるLINE ボットの仕組みは、[去年](https://qiita.com/masaki925/items/eafd0b3c46baeccc5507) と同じものを使いました。

昨年と同様 [Rhymer](https://github.com/suzuki86/rhymer) を利用していますが、今年はライムの生成ではなく、歌詞から母音の抽出をするために使わせていただきました。

ということで主にやったことは、

- 評価関数の実装
    - 時間の都合上、EndRhyme とLineLength しか実装できませんでした。。もっと増やしたいです。
- 歌詞の収集・学習
    - こちらも時間の都合上、30件くらい。。
- 歌詞の学習
    - 件数が少ないので、学習自体は一瞬で終わります。
- LINE からテキストを受け取ったら、そのテキストから歌詞候補の相関ランキングを計算して、一位の歌詞を返す実装

といったかんじです。

## 大変だったこと

RankSVM について理解することが大変でした。
学習データの形式や具体的な使い方がよくわからず、試行錯誤して自分なりの理解でやってたりします。。
詳しい人がいたら教えてほしいです。

学習データ

```
# 一つのクエリにつき、1位(本物の次の歌詞) と2位(ランダムで選んだ不正解の歌詞) のみをランキングしている
1 qid:0 1:1 2:0.7142857142857143
2 qid:0 1:0 2:0.8571428571428572
1 qid:1 1:2 2:0.5
2 qid:1 1:3 2:0.9
1 qid:2 1:0 2:0.7
2 qid:2 1:4 2:0.7
```

学習

```
# c オプションの意味がイマイチよくわかっていない
$ svm_rank_learn -c 3 ./train.dat ./model.dat
```

あるクエリに対して、歌詞候補について、相関スコアの計算 (実際はRails から呼び出している)

```
$ svm_rank_classify ./candidates.dat ./model.dat ./rank_score.dat
```

candidates.dat はこんなかんじ

```
# 一つのクエリに対して、候補の相関スコアが並ぶ。左のtarget は適当に連番。
0 qid:1 1:0 2:0.7142857142857143
1 qid:1 1:0 2:0.7142857142857143
2 qid:1 1:1 2:1.0
3 qid:1 1:0 2:0.7
4 qid:1 1:0 2:0.7
...
```

で、rank_score.dat がこんなかんじ

```
-0.00955737
-0.00955737
-0.19418036
-0.00936622
-0.00936622
...
```

rank_score が最小のものを選択して表示。

あと、svm_rank_classify を使うためにRails からsystem call してたりするのでやばげです。
リクエストごとに毎回スコアをファイルに吐き出したりしてるのもやばげです。

てへぺろ。

# 今後の展望

- レスポンス速度を改善したい
- 評価関数を増やしたい
- 学習データを増やしたい
- 学習データを精査したい

要するに、もっとヴァイブス高めたい！！
ということですね。

こちらからは以上です。


# Special Thanks

パンのピーさんは絵本作家のいわもとあいりんさんに描いていただきました :)