
機械学習で探すJ-POPあるある
はじめまして。aidemy の川島と申します。 最近いろんな音楽を積極的に聞こうとしています。なかでもJ-POPは知っている曲の幅を広げようと頑張っております。 J-POP、僕は好きです(ここ大事)。これは僕がJ-POPを好きだという前提で聞いてもらいたいのですが、J-POPの歌詞って臭いのが多くないですか? あいつらはすぐ誰かを愛しますし、手をつないで歩きますし、多分君のことが世界で一番大事ですし。これ、少しは感じたことのある人も多いはずです。
この件に関しては、専門家(?)の非常に有用な先行研究がありました。パーマ大佐さん(以下敬称略)という芸人の方の、「J-POPの歌詞あるあるの歌」(以下「あるあるの歌」)というものが存在します。
動画: パーマ大佐 - 「J-POPの歌詞あるあるの歌」 Music Video
こちらがその、パーマ大佐の「あるあるの歌」です。暇な人はぜひ見てみてください。暇じゃなければ見なくてもいいです(でも面白いと思うよ)。 ここから先、少しだけ「あるあるの歌」のネタバレが入ることもありますので、ご了承ください。 この歌、ある程度J-POPを聞いたことのある人なら、ニヤッとさせられたりクスッと来たりする場面が多々あると思います。
でも、「よくよく考えると具体的なのがすぐには思いつかないな……。本当にあったっけ?」ってこともないでしょうか。……僕はいくつかありました(多分J-POPに精通していないせい)。というわけで前置きが長くなりましたが、今回はpythonを用いてJ-POPの歌詞によく見られるフレーズを自分なりに分析していきたいと思います。
目次[非表示]
- 1.環境
- 2.フレーズから見る「あるある」
- 2.1.スクレイピングによる歌詞の取得
- 2.2.形態素解析を用いた歌詞の分析
- 2.3.結果の表示
- 2.4.実行結果
- 3.単語から見る「あるある」
- 3.1.doc2vecを利用した準備
- 3.2.Doc2Vecによる対象箇所のベクトル化
- 3.3.クラスタリングによる「あるある」の抽出
- 3.4.実行結果
- 4.おわりに
環境
主なものを載せておきます。
- python 3.6.5
- anaconda 1.6.14
- jupyter 4.4.0
- numpy 1.14.3
- Janome 0.3.6
- gensim 3.4.0
- scikit-learn 0.19.1
フレーズから見る「あるある」
最初は、J-POPにおける「あるある」なフレーズを探していくのを目標にしたいと思います。
スクレイピングによる歌詞の取得
まずはスクレイピングにより、分析対象となる歌詞の一覧を引っ張ってきましょう。 今回は、Uta-Netさんのこちらのリンクから引っ張ってきました。楽曲ごとの月間アクセス数です。2018年6月9日現在、米津玄師、RADWIMPS、WANIMAなどのアーティストが多く見られます。
コードは以下です。引数urlにより上記のURLを渡すと、4ページ目で50曲ずつ、計200曲を取り出してきてくれます。
改行ごとに区切ったりアーティスト名や曲名を取得したりといった作業は分析には使いませんが、あとあと分析結果の表示の際に使うので一緒にやっちゃっています。 また、たまに同じ曲が2回ランクインしている場合があるので、それもあらかじめ抜いておきます。
同じ曲が2回ってどういうこと?と思われるかもしれませんが、今回だと以下の2組がそれに該当します。
- 打上花火(米津玄師) と 打上花火(DAOKO×米津玄師)
- スパークル [original ver.](RADWIMPS) と スパークル (movie var.)(RADWIMPS)
実は最初はこれがあるのに気づかず、バグかと思いずっとバグを探していました……。
形態素解析を用いた歌詞の分析
上記の作業により、二重配列lyricsには配列で各曲の歌詞が、さらに配列で行ごとに分けられて入っています。これを基に歌詞によく出てくるフレーズを分析していきましょう。具体的な手順としては、シンプルですが以下のようなものです。フレーズはおおよそ2単語の組み合わせにより作られるという考えに基づいています。
- 歌詞から形容詞、形容動詞、名詞、動詞を順に抜き出す。
- 曲ごとに、ある程度近くにあった2単語に組に加点をする。
- 一定数以上の曲にあった(一定以上の点を取った)単語の組を抽出する。
歌詞から形容詞、形容動詞、名詞、動詞を抜き出すのは、janomeを用いて形態素解析をすることで可能となります。形態素解析とは、文を単語ごとに抜き出して品詞や活用形などを特定することを指します。さらに形態素解析では、動詞などの原形を取得したり品詞の細かい分類を取得できたりします。便利……!
今回のコードではこれを利用し、細分類が「非自立」の単語は抜いておきました。「知ってる」の「てる」や「君のこと」の「こと」が非自立に含まれます。大雑把に言えば特に意味のない単語というわけですね。ただ、それでもたまに不要な単語が抽出されるので、手作業で不要な単語を登録しそれは抽出しないようにしておきます。
しかし今回の場合は不要な単語が大きく分けて2種類存在します。具体的には、
- 意味のあまりない単語
- 異常に出現するけどそこまで興味のない単語
なぜこれを分けるのかは少しあとの方で説明するとして、以下が無視する単語リストです。
前者trashが「意味のあまりない単語=ほかの助動詞などと同様にすっ飛ばす単語」、後者blockが「異常に出現するけど興味のない単語=ほかの名詞などと同様にカウントされるが加点のランキングに参加しない単語」です。
このストップワードリストについては少し前の記事「Pythonで国会議事録から、話題の政治ワードを抽出してみた」でも触れられていますが、そことは少し違う語録になっていると思います。だってJ-POPのやつら、やたら笑うしやたら愛するし、好きだの勇気だの未来だの笑顔だの、びっくりするくらい出現するんで……。
あと、サビにめっちゃ英語になるんで、英語もある程度リストに入れておきました。「あるあるの歌」にある「間が埋められなくなるとすぐLaLaLaとかHeyとかBabyって言う」っていうの、まさにですね。
さて、trashとblockをわざわざ分けた意味について少しだけ触れておきます。もしblockをtrashと同様にすっ飛ばしてしまうと、何連続にも単語がすっ飛ばされ、その結果あまり近くにいない単語の組に加点してしまうことがしばしば起こるのです。そのため、 blockの単語は「加点には参加しないがすっ飛ばしはしない単語」ということでtrashと分けたのです。 また、加点対象となる単語の近さの基準ですが、これは前後2単語までとしました。
例えば「暗闇であなたの背をなぞった」(米津玄師 Lemon)という歌詞では、 「暗闇」「あなた」「背」「なぞる」が抽出され、「あなた」はblockの対象なので「あなた」を含む組には加点されず、「暗闇」「背」や「背」「なぞる」といった2単語の組に加点され、「暗闇」「なぞる」の組は遠すぎるとみなし加点されません。 少し説明が長くなりましたが、コードは以下です。
これが終われば、二次元配列matにはそれぞれの2単語の組の点数が保存されています。
結果の表示
さて、分析作業はこれで終わりなので、出力はこの関数にお願いします。1曲分の歌詞を渡すと対象となった単語の登場部分を探索し、もしあればその該当付近の行、アーティスト、曲名を見せてくれるものです。
そしてこの関数を以下のように実行します。
とりあえず前半はこれで作業終了です。あとは実行して寝るだけ。おやすみなさい。
実行結果
さて、結果を見てみましょう。例えば、こんな感じで表示されます。9個はかなり多い方ですね。
ということで、4曲以上に登場した「あるある」をいくつか以下に抽出しました。ついでに、「あるあるの歌」に似たようなもの、関連がありそうなものがあれば一緒に載せています。参考にしてください(何の?)。
ちなみに3曲以上、2曲以上という条件にすれば、もっと「あるあるの歌」とかぶるものが出てきます。 4曲以上だと、思っていた以上に「あるある」っぽいものが的確に抽出されているように思います。3曲だとやはり、「何だこれは」というのもたまに入っていたりするのですが。当然、2曲以上という条件で抽出してみるとそれなりの頻度で変なのが入ってきました。偶然かぶるというのはわりとありますからね……。
あるある |
抽出された単語 |
登場曲数 |
「あるあるの歌」対応箇所 |
やたら胸が痛む |
「胸」 「痛い」 |
5 |
|
大事なものは すぐ遠く離れる |
「離れる」 「遠い」 |
5 |
「何故J-POPの歌詞っていつも 大切な人がすぐいなくなるの」 |
すぐ涙を流す |
「涙」 「流す」 |
8 |
「すぐに涙を流す」 |
すぐ風が吹く |
「風」 「吹く」 |
11 |
「すぐ風になる」 (惜しい……) |
すぐ夜が明ける |
「夜」 「明ける」 |
5 |
|
空がやたら青い |
「青い」 「空」 |
7 |
|
めっちゃ空を 見上げる |
「空」 「見上げる」 |
5 |
「どうせ愛している君は今日も 同じ空の下にいるのだろう」 |
生きる意味を やけに考える |
「生きる」 「意味」 |
5 |
|
すぐ目を閉じる |
「目」 「閉じる」 |
11 |
「どうせ愛している君は僕の 瞼の裏に潜んでいるのだろう」 |
なぜか夏ばっかり 終わりが来る |
「夏」 「終わる」 |
4 |
|
すぐ花が咲く |
「花」 「咲く」 |
6 |
「すぐ桜咲く」 (※「桜」「舞う」は3曲) |
やたら手をつなぐ |
「手」 「繋ぐ」 |
10 |
|
しかも手を掴む |
「掴む」 「手」 |
7 |
わりと「言われてみれば……!」というのが多く感じるのではないでしょうか(もし感じなかったとしても、僕は多く感じましたので許してください)。
単語から見る「あるある」
さて、ここから後半です。先ほど配列blockにより「やたら登場するけど興味のない単語」を抜いておきました。単体であまりに頻繁に登場するし他の単語との組み合わせも興味がないので抜いたわけですが、よくよく考えてみればこれらの単語自身が単体で「あるある」です。
ということで、このような単語が登場する箇所を見たい気持ちがあります。……とは言っても、単純に抽出して表示するとやばいんです。というのも、例えば「笑う」だと198曲中64曲に登場するのです。そんなにたくさん表示されると流石に見ていられないというのが率直な気持ちです。じゃあいくつかピックアップすればいいじゃないかという話ですが、いろんな文脈で「笑う」が使われるわけで、ランダムにピックアップしてもあまり面白い結果は得られません。
というわけでここでは、「対象の単語がある程度似たような雰囲気で使われている箇所だけに絞ってピックアップ」ということを、pythonの機械学習にかんするライブラリの力を借りて行いたいと思います。
doc2vecを利用した準備
まずは取得した歌詞を用いて、Doc2Vecに学習させモデルを作成します。Doc2Vecは、大雑把に言えば文書をベクトル化することにより、文書の類似度などを定量的に示すことを可能にしてくれるものです。自然言語を処理しやすい数値に変換してくれるという点で、とても便利な機能なわけです。学習は以下のように行います。
一方で、対象となる単語が出てくる箇所は予め抽出しておきます。対象の単語が「笑う」の場合、以下のようになります。
Doc2Vecによる対象箇所のベクトル化
これで、モデルと調べる対象が作成されました。作成したモデルに、「笑う」が出てくる近辺の文をベクトル化してもらいます。ここでは、特に引数を指定していないので、それぞれの文が100次元ベクトルで表されます。
というわけで、言語がベクトルに置換されました。ここからは、ベクトルの値がなるべく近いものを集め、最も近かったものたちを表示します。これにより、「笑う」という単語が歌詞の中では主にどのように使われるのかが見られるわけです。
クラスタリングによる「あるある」の抽出
では、どのようにベクトル値が近いものを集めるかという話ですが、ここではクラスタリングを利用します。クラスタリングは教師なし学習の一つであり、点の集合を指定されたグループの数に、なるべく近いものどうしでグループ分けしてくれるものです。pythonではsklearnというライブラリにこのアルゴリズムを行ってくれるものがあるので、これを使うことにします。今回の例では、64個の100次元空間の点のクラスタリングを行うことになります。以下がそのコードです。 今回は最終的に10個弱の例を表示すればいいかなということで、全体を8で割った数をグループ数にしています。
これで、ベクトルがグループ分けされました。それぞれのグループにおける分散を求め、一番分散が小さいグループの歌詞を表示することにします。分散が小さいということはそれだけ文書の意味がグループ内で似ているということなので、最も「あるある」らしき歌詞群が得られるわけです。
実行結果
これまでのコードを実行すると、以下のような歌詞が表示されます。とりあえず眺めてみてください。
これだけ見せられてもちゃんと抽出されているのか分からないと思うので、比較としてほかの歌詞を載せておきます。これらは、一つや二つだけの歌詞でグループを形成しているものであり、いわば外れ値です。全体的に上の歌詞と雰囲気が違うものになっていることが分かるかと思います。
というか2曲目はもはやよく分からないですが……。
こういった外れ値が抜かれたことを考えれば、ある程度は洗練されて抽出されていることが分かるかと思います。とは言っても、見てもらうとわかる通り、完全にすべて同じような意味とはいかないようです。というのも同じ単語が含まれている時点で外れ値があるとは言え基本的に近いベクトル値であるみたいで、いろいろ試してはみたのですがそれらの分類は難しかったです。
今回は、表示する歌詞の数の都合上このグループ数にしたわけですが。 今回は高々200曲ほどですので、何千、何万のオーダーで歌詞を読み取ればもう少し細かな分類も可能になってくるかもしれません。また、分析の対象となった歌詞を見ると、歌詞ならではの文脈が分かりにくいような言葉遣いが多く存在したので、それも分類を難しくする一因となっているのではないかと思います。
何はともあれ、おおよその傾向として、J-POPでは「笑う」が登場する場合は大概君が笑っているのであったりということはわかるかと思います。あとは、人に笑われるパターンのものがいくつか入っていますね。 他の例として、「愛」(含まれるのは全55曲)のものを載せておきます。
幸せいっぱいかと思いきや、案外ややグロテスクな表現や寂しい表現が多いですね。他のグループを除くと、幸せそうな歌詞が集まっているところもありました。例としてはこちらの方が上手くいったかもしれないです……。
おわりに
いかがだったでしょうか。個人的には、上手くいくかかなり不安だったので、思ったよりはそれらしい結果が出てわりと安心しているところはあります。今回いろいろ試してみたわけですが、pythonの機械学習ライブラリを用いてあとは簡単なアルゴリズムを適用するだけで、自然言語とかいうよくわからないものを定量的に扱えるようになることに感動しました。
しかし一方で、歌詞ならではの難しさのようなものも後半では浮かび上がってきたように思います。 今回は自然言語処理の技術の一部を利用したわけですが、調べれば調べるほど興味深い分野ですので、興味がある方はぜひ勉強してみてほしいです(とはいえ僕も勉強を始めたばかりですが)。
今回の記事では自然言語処理を用いて歌詞の分析をしました。AI、機械学習それからディープラーニングの経験はないけど、「AIをどのように活用するのだろう?」や「Aidemy Premium Planを受講して、私でもきちんと続けられるだろうか?」など、少しでも気になることがございましたら、ぜひ一度気軽に無料相談会にお越しいただき、お悩みをお聞かせください!
最後までご覧くださりありがとうございました。