テーマに沿った楽曲でBUMPやRADWIMPSのアルバムを自動生成してみた【第一弾】

こんにちは、ぐぐりら(@guglilac)です。
突然ですが.
自分の好きなアーティストが新しくアルバムを発表するのってとってもワクワクしますよね.
自分の好きな楽曲が並んでいる文字列を眺めるだけでテンションが上がります.
自分が行ってないライブのセトリとかも気になったりします.

しかし,アルバムはなかなか出ません.
アーティストの方も忙しい.仕方ない.
無理せず自分たちのペースで活動してほしい・・・

とはいっても!!アルバムとかセトリとかを初めて見る興奮を味わいたい!!

というわがままを叶えるために,アルバム自動生成器を作って遊んでみました.
セトリの方も作ってみたいのですが,セトリのデータがファイルの形式で用意できなかったので今回はアルバムを自動で作ってみます.

今回作成したプログラムを簡単に説明すると

  • 作りたいアルバムのテーマと収録曲数とアーティストを指定すると
  • テーマと意味の近いタイトルの楽曲を集めてきて
  • 既存のアルバムでの楽曲順に近い順番にして出力する!
という感じ.

最後の「既存のアルバムでの楽曲順に近い順番にして出力する」とは,
アルバムの最初の方に来がちな楽曲を前の方に,トリっぽい曲は最後にくるように出力するということです.

BUMPでいうと,「星の鳥」はアルバム「orbital period」の一曲目なので最初の方に出力され,アルバム「Butterflies」の最後の曲「ファイター」は最後の方に収録されやすくなる,という感じになります. 


セトリの方もファイル形式でまとめたものがあればやってみたい.だれかーー.(他力本願)

作ったプログラムの説明をしてから,実際に使って遊んでみます.
興味ない方はソースコードのとこはすっとばしてください笑

ソースコードとその説明

Python3でかきました.クラスのところだけ載せます.
 
#AlbumGeneratorクラス

class AlbumGenerator():

    def __init__(self, filename):
        self.position = {}
        self.song_vectors = {}
        album = []
        for line in open(filename, 'r'):
            line = line.strip()
            if line != "END":
                album.append(line)
            else:
                album_len = len(album)
                for i in range(album_len):
                    self.position[album[i]] = i / album_len
                    self.song_vectors[album[i]] = self.bagOfMeans(album[i])
                album = []

    def __repr__(self):
        return "使用可能な曲数:{}曲".format(len(self.position))

    def genarate(self, album_title, album_num):
        title_vector = self.getTitleVector(album_title)
        if str(title_vector) != "nan":
            songs_list = self.similar_songs(title_vector, album_num)
            tuples = self.sort_title(songs_list)
            self.output(tuples, album_title, album_num)
            return
        else:
            print("Invalid Title Error")
            return

    def sort_title(self, selected_music_list):
        tuples = []
        for title in selected_music_list:
            tuples.append((title, self.position[title]))
        tuples = list(sorted(tuples, key=lambda x: x[1]))
        return tuples

    def output(self, tuples, album_name, album_num):
        print("テーマ:{} 収録曲数:{}曲".format(album_name, album_num))
        for i in range(len(tuples)):
            print(str(i + 1) + ":" + tuples[i][0])
        return

    def Wakati(self, text):
        m = MeCab.Tagger(
            "-Ochasen -d ./mecab-ipadic-neologd -Owakati")
        result = m.parse(text)
        ws = re.compile(" ")
        words = [word for word in ws.split(result)]
        if words[-1] == u"\n":
            words = words[:-1]
        return words

    def bagOfMeans(self, title):
        words = self.Wakati(title)
        word_vectors = []
        for word in words:
            try:
                word_vectors.append(model[word])
            except:
                pass
        if len(word_vectors) == 0:
            return "nan"
        else:
            return np.average(np.array(word_vectors), axis=0)

    def similar_songs(self, title_vector, album_num):
        distances = []
        for k, v in self.song_vectors.items():
            if str(v) != "nan":
                distances.append((k, np.linalg.norm(title_vector - v)))
        tuples = list(sorted(distances, key=lambda x: x[1]))
        return [tuples[i][0] for i in range(album_num)]

    def getTitleVector(self, album_title):
        title_vector = self.bagOfMeans(album_title)
        if len(title_vector) != 0:
            return title_vector
        else:
            return "nan"

  

人に見せるつもりでもなかったのであんまりエレガントじゃないかもですが笑

まず,クラスを初期化する時にアルバムの情報を持ったファイルを読み込み,各楽曲のアルバム中の位置を保持する辞書(position)と,各楽曲のタイトルの意味を表すベクトル表現を保持する辞書(song_vectors)を作成します.
初期化の際の挙動は__init__において指定することができます.

ちなみに__repr__はprint()を行ったときの挙動を指定できます.
今回は使用可能な楽曲数を表示するように指定しました.

__init__とか__repr__などの特殊メソッドのお話は以前書いたのでこちらも参考にしてみてください.

pythonの__str__と__repr__とかその周りを調べた

ファイルを読み込んだ後の実装は以下のように作りました.

テーマに沿った楽曲のリストアップ

Word2Vecというものを用いました.
自然言語処理をかじっている方はご存知かと思いますが,簡単にいうと
単語を入力すると単語の意味を考慮したベクトルを返してくれるツールです.

単語の意味を考慮するベクトル,とは?という気持ちになるかもしれません.
近い意味を持つ単語のベクトルどうしの距離が近くなり,意味の異なる単語同士は遠いベクトルになる,といったイメージを持ってもらえるといいかも.

「雨」と「雪」は近いベクトルが割り当てられ,「加湿器」と「メタボ」とかは比較的離れたベクトルになるということです(いい例が思いつかん・・・)

Word2Vecはおもしろくて,いろいろ遊べます.
単語同士の足し算引き算みたいなのもできるようになったりします.

たとえば
王様 ー 男 + 女 = 女王

みたいな.王様から男成分をひいて,女成分を足したら何になる?という一見コンピュータが苦手そうなとこもできるようになったりします.(うまくいかない例も結構ありますが.)

話が逸れましたが,このWord2Vecを用いて,楽曲や入力したテーマをMeCabというツールで単語ごとに区切り,それぞれベクトルに直してからその平均をとることで意味を表すベクトルとして計算することができます(ちなみに,Word2Vecを使ってできたベクトル列の平均を用いる手法をBag-of-Meansというらしい.)

その意味を表すベクトルの距離を計算し,意味の近いものをピックアップするという方法で実装しました.

既存のアルバムでの順番っぽく並び替え

こっちは単純な方法です.
すでにあるアルバムの中で,何番目に出て来たかを計算します.
同じ楽曲が違うアルバムに出てくることはないものとします,
(このためベストアルバムは対象から外しました)

アルバムごとに収録曲数が違うので,何番目に出て来たかをそのまま使うのではなく,収録曲数で割ることでいい感じにしてます.

あとは上の方法でピックアップされた楽曲たちを先ほど計算した値を見て並び替えます.
アルバムだとラクです.
(セトリだと同じ曲が違うライブにも登場するのでこのままではだめだから,やるなら工夫しなければ)

遊んでみる

おまちかねの遊んでみるタイム.
まずBUMPから.
初期からButterfliesまでつかってます.
使用可能曲は108曲でした.

テーマ:星 収録曲数:12曲
1:星の鳥
2:彼女と星の椅子
3:サザンクロス
4:流星群
5:ハンマーソングと痛みの塔
6:時空かくれんぼ
7:魔法の料理 ~君から君へ~
8:夢の飼い主
9:飴玉の唄
10:星の鳥 reprise
11:太陽
12:イノセント

おおお.割と星っぽいけどよくわからんのも入ってる.
順番はいい感じ!星の鳥とrepriseがいい感じの位置笑


テーマ:涙 収録曲数:10曲
1:バイバイサンキュー
2:くだらない唄
3:彼女と星の椅子
4:キャッチボール
5:ハンマーソングと痛みの塔
6:夢の飼い主
7:リリィ
8:飴玉の唄
9:真っ赤な空を見ただろうか
10:孤独の合唱

あれ.涙のふるさとがいない.
孤独の合唱はタイトル的には涙感あるしいいかも.

テーマ:宝石 収録曲数:12曲
1:星の鳥
2:グングニル
3:彼女と星の椅子
4:ハンマーソングと痛みの塔
5:宝石になった日
6:魔法の料理 ~君から君へ~
7:トーチ
8:夢の飼い主
9:リリィ
10:飴玉の唄
11:星の鳥 reprise
12:ダイヤモンド

ダイヤモンドと宝石になった日が入るよね.うんうん.

テーマ:唄 収録曲数:12曲
1:三ツ星カルテット
2:オンリー ロンリー グローリー
3:バイバイサンキュー
4:くだらない唄
5:才悩人応援歌
6:続・くだらない唄
7:魔法の料理 ~君から君へ~
8:メロディーフラッグ
9:飴玉の唄
10:東京賛歌
11:ダンデライオン
12:beautiful glider

車輪の唄がなああーーい

RADWIMPSも試してみました.
楽曲数は82曲で,初期から人間開花まで使いました,
君の名はアルバムは入れてないです.


テーマ:ロック 収録曲数:12曲
1:DADA
2:アイアンバイブル
3:バグパイプ
4:リユニオン
5:五月の蝿
6:ブレス
7:棒人間
8:メルヘンとグレーテル
9:スパークル
10:トレモロ
11:ラストバージン
12:最後の歌

ロック.
タイトルだけでは曲調まではわかるはずもないですよね.

テーマ:英語 収録曲数:14曲
1:Lights go out
2:DADA
3:アイアンバイブル
4:バグパイプ
5:me me she
6:学芸会
7:‘I’ Novel
8:螢
9:棒人間
10:最大公約数
11:スパークル
12:へっくしゅん
13:Bring me the morning
14:最後の歌

英語は英語の曲が入ってくるのか.

テーマ:君の名は 収録曲数:12曲
1:DADA
2:透明人間18号
3:バグパイプ
4:リユニオン
5:学芸会
6:螢
7:棒人間
8:最大公約数
9:スパークル
10:へっくしゅん
11:叫べ
12:最後の歌

君の名は感がまるでないセレクト笑

考察

それっぽい曲が選ばれているのもありましたが,テーマにかかわらず選ばれる楽曲がいくつか見られました.(スパークル何回入ってくるん.笑)
あと,これは絶対入ってるだろ!みたいなのが抜けてたりしました.

MeCabを使った単語列への分割の際や,Word2Vecでベクトルに変換する際に,単語として認識されていないと無視されるように作ったのですが,予想よりもこの未知語が多かったのかもしれません.

あとは,タイトルだけでは不十分という気がします.歌詞の情報も合わせて意味の近さを計算したい.そうすれば未知語の問題も緩和されてもっといろんな曲がセレクトされるようになるはず!

順番は良い感じでした.

まとめ

とりあえず,それっぽいものはできたので楽しかった.
考察したところを改良してもっといい感じにセレクトするようにしたいですね.
改良できたら第二弾も書きたいと思います.

セトリもデータがあればやってみたい!だれかーーー(他力本願2回目)

読んでくれてありがとうございました!




コメント