🎉

CLAPを使ってMusicCapsの楽曲を検索してみる

2024/01/08に公開

概要

以前の記事でLAION/CLAPを使った効果音の検索をやりました。今回は楽曲の検索をやってみます。今回の検索は「楽曲」で「楽曲」を検索します。最後におまけで「テキスト」で「楽曲」を検索しています。

MusicCapsのダウンロード

今回は検索用のデータセットとしてMusicCapsを使います。過去記事で紹介したやり方でMusicCapsの楽曲をwav形式でダウンロードします。
https://zenn.dev/tatexh/articles/f416347ac18d50

全部落とす必要はありませんが、500曲ぐらいはあったほうが結果がわかりやすいと思います。今回はmusic_caps_dataというフォルダにダウンロードしています。

$ ls music_caps_data/
00M9FhCet6s.wav  0TV9zvfwFhs.wav  1ToIyrmWFjw.wav  2uvHgwAljPA.wav  3TO4C7SiC7I.wav  4sD0Bvt3FKk.wav  5s0yPPrQWxs.wav  6u1ckcErgcQ.wav  7YtKrL6ScXA.wav
01hjVJN9xCg.wav  0u1sk49gAU0.wav  1TyOPtg0Yfk.wav  2UY_-oF1vqo.wav  3TP1itJqv-E.wav  -4SYC2YgzL8.wav  5tNOauvQWQQ.wav  6uIOGE36tWo.wav  7YWMPBHKdyY.wav
01PzcPKT3_E.wav  0u4gY1bBUwQ.wav  1tz4xNRRR4M.wav  2VFVe0RCn7g.wav  3TQmts_MxyQ.wav  4T2KBwRxi_g.wav  5Tq56BN8PCQ.wav  6UJhTZgnVro.wav  7ZW8xO37bA4.wav
0298WjE3_tk.wav  0u5-WiBKam8.wav  1uHF1-8TcEk.wav  2_Vk3tmqz-0.wav  3tSPMzvuQpk.wav  4T_5clu_0OM.wav  5tRNPTLRZqI.wav  6VJ_auuKzss.wav  7ZXz3Xa7APs.wav
02Qntw26enM.wav  0Ubu4BqSWmU.wav  -1UWSisR2zo.wav  2vQTq4QLP8U.wav  3tyb0cXoX2g.wav  4TDtUHo5cSE.wav  5tt_GKV13G0.wav  6vM7Kv42Uv0.wav  -88me9bBzrk.wav
...

MusicCapsのキャプションを取得

MusicCapsをload_dataset()で読み込みます。ytidにYoutubeのIDが入っています。captionが楽曲の説明文です。今回はこちらをdictionaryに突っ込んでJSONで保存しておきます。

import os
import json
from datasets import load_dataset

ds = load_dataset('google/MusicCaps', split='train')

filename_to_caption_dic = {}
for d in ds:    
    ytid = d['ytid'] # YoutubeのID
    filename_to_caption_dic[ytid] = d['caption'] # キャプション
    
with open('filename_to_caption.json', 'w') as f:
    json.dump(filename_to_caption_dic, f)

楽曲用のCLAPの学習済みモデルをダウンロード

楽曲用の学習済みモデルが music_audioset_epoch_15_esc_90.14.pt です。こちらをhuggingfaceからダウンロードします。下のリンク先のdownloadからダウンロードします。
https://huggingface.co/lukewys/laion_clap/blob/main/music_audioset_epoch_15_esc_90.14.pt

今回はこちらをmodelsフォルダに入れておきます。

学習済みモデルをロード

サンプルコードのままではロード時にエラーになります。githubのissuesに回避策が書いてありました。

まずtransformersのバージョンを4.30.2にします。

transformers==4.30.2

次にこちらで書いてあるようにlaion_clap.CLAP_Module()でamodel='HTSAT-base'を指定する必要があります
https://github.com/LAION-AI/CLAP/issues/113

import numpy as np
import librosa
import torch
import laion_clap
import glob

# quantization
def int16_to_float32(x):
    return (x / 32767.0).astype(np.float32)

def float32_to_int16(x):
    x = np.clip(x, a_min=-1., a_max=1.)
    return (x * 32767.).astype(np.int16)

model = laion_clap.CLAP_Module(enable_fusion=False, amodel= 'HTSAT-base')
model.load_ckpt('models/music_audioset_epoch_15_esc_90.14.pt')

楽曲をベクトルに変換

ここは以前の効果音の検索とほぼ同じです。MusicCapsのキャプションは英語なので、ここでdeep_translatorを使って日本語に変換しています。このほうが結果比較がやりやすいです。

import os
import json
from tqdm import tqdm
from deep_translator import GoogleTranslator

def translate(text):
    return GoogleTranslator(source='auto',target='ja').translate(text)

input_dir = 'music_caps_data/*.wav'
file_list = [p for p in glob.glob(input_dir, recursive=True) if os.path.isfile(p)]

with open('filename_to_caption.json','r') as f:
    filename_to_caption_dic = json.load(f)

info_list = []
for file in tqdm(file_list):
    try:
        audio_embed = model.get_audio_embedding_from_filelist(x=[file], use_tensor=False)
        key = os.path.basename(file).replace('.wav', '')
        caption = filename_to_caption_dic[key]
            
        info = {}
        info['file'] = file
        info['caption'] = translate(caption)
        info['embed'] = audio_embed[0]
        info_list.append(info)
            
    except Exception as e:
        print('error:', e)
        continue

指定した楽曲に近い順番でソートする

ランダムで1曲選んで、それに近い順番にソートして、日本語のキャプションを表示してみます。

import numpy as np
import random

# ソートしてみる
find_index = random.randint(0, len(info_list)-1)

embed_list = [info['embed'] for info in info_list]
dot_product_list = []

find_embed = embed_list[find_index]

nrm = np.linalg.norm(find_embed)
find_embed = find_embed / nrm

for embed in embed_list:
    nrm = np.linalg.norm(embed)
    embed = embed / nrm
    dot_product_list.append(np.dot(find_embed, embed))
    
sorted_index_list = list(np.argsort(dot_product_list))
sorted_index_list.reverse()

print('find index caption:', info_list[find_index]['caption'])
print('-------------------------')
for i in sorted_index_list[:10]:
    print(info_list[i]['caption'], ' : ', dot_product_list[i])
    print('---')

結果

結果その1

最初のfind index captionが検索対象の楽曲のキャプションです。検索結果の1番目に同じ曲が出ているので結果は正しいと言えそうです。今回は「幻想的、アンビエント、悲しい」曲のようです。検索結果の上位に似たような傾向の曲が来ていることがわかります。

find index caption: 女性の声が主旋律を歌うスローバラードです。これにピアノのメロディーが繰り返されます。ベースはコードのルート音を 8 分音符パターンでスタッカート スタイルで演奏します。シンセホーンがさまざまなピッチでバックグラウンドで演奏されます。この曲にはアンビエントで幻想的な雰囲気があります。曲の雰囲気が忘れられない。映画の悲しいシーンで流れるこの曲。
-------------------------
女性の声が主旋律を歌うスローバラードです。これにピアノのメロディーが繰り返されます。ベースはコードのルート音を 8 分音符パターンでスタッカート スタイルで演奏します。シンセホーンがさまざまなピッチでバックグラウンドで演奏されます。この曲にはアンビエントで幻想的な雰囲気があります。曲の雰囲気が忘れられない。映画の悲しいシーンで流れるこの曲。  :  1.0
---
この音楽は心安らぐインストゥルメンタルです。ゆっくりとしたテンポで、美しいヴァイオリンの交響曲、ロマンチックなピアノの伴奏、ハープの旋律、管楽器、シェイカーが響きます。その音楽は穏やかで、優しく、物思いにふけり、ノスタルジックで、心を痛め、メランコリックで、
ロマンチックで、魅惑的で、謎めいていて、夢のような、穏やかで、幻想的です。  :  0.7005424
---
これは新時代の音楽作品です。この作品には歌手は登場しません。アコースティックギターが主旋律を奏でています。ピアノが曲の和音を演奏しており、背景ではかすかなシンセストリングスの音が聞こえます。雰囲気はリラックスして癒されます。この曲は瞑想ビデオのBGMに最適です。スパやウェルネスセンターのバックグラウンドで再生されることもあります。  :  0.67039245
---
曲はインストゥルメンタルです。テンポはゆっくりで、感動的なヴァイオリンのソロ、キーボードの伴奏、パーカッションのドラム、力強いベースライン、ギターの伴奏が含まれます。この曲は感情的であり、悲しみと畏怖の念を持っています。この曲はドキュメンタリーのサウンドトラックです。  :  0.6660481
---
この曲は、言葉を使わずに音声だけを使ってオペラ風に歌う女性の声が特徴です。これには、演奏されているコードに応じて変化する 8 分音符のパターンを演奏するヴァイオリンが伴奏されます。ベースはコードのルート音に従います。ミュートされたギターまたは弦楽器がアルペジオ和音を演奏します。この曲はファンタジーゲームでプレイできます。  :  0.64572895
---
この曲はフルートが主旋律を奏でます。これに、遊牧的なビートを演奏するパーカッションが伴います。弦楽器がバックグラウンドで音を弾きます。アンビエントなシンセサウンドが演奏され、曲に厚みを与えます。この曲には中東の影響があり、夢のような雰囲気があります。ゲームのイントロロビーでプレイできます。  :  0.6444316
---
この音楽はまろやかで贅沢なインストゥルメンタルです。ゆっくりとしたテンポで、美しいヴァイオリンのハーモニー、ピアノ伴奏、シンセサイザーアレンジによる安定したベースラインが響きます。音楽は穏やかで、心を落ち着かせ、心地よく、感傷的で、感情的で、切なく、切なく、メランコリックで、幸福感に満ちています。この曲は絶妙な中東のインストゥルメンタルです。  :  0.6429268

結果その2

別の結果も見てみます。今度は激しいリズミカルな曲が上位に来ているようです。こちらも合っていますね。

find index caption: リードエレキギターがメロディーを奏で、リズムエレキギターがコードをかき鳴らすインストゥルメンタルサーフロック。ミックスではライド シンバルが大音量で、スプリング リバーブがエフェクトとして使用されています。エレキベースがウォーキングパターンを演奏しています。
-------------------------
リードエレキギターがメロディーを奏で、リズムエレキギターがコードをかき鳴らすインストゥルメンタルサーフロック。ミックスではライド シンバルが大音量で、スプリング リバーブがエフェクトとして使用されています。エレキベースがウォーキングパターンを演奏しています。  :  1.0
---
ブルース曲の生演奏です。楽器演奏です。リードのエレキギターがブルースのソロを演奏し、別のエレキギターとベースギターがバックグラウンドで演奏しています。リズミカルな背景は、スローテンポのブルースのアコースティック ドラムのビートで構成されています。この演奏にはグルーヴィーな雰囲気が漂っている。この曲はロックバーのバックグラウンドで流れているかもしれない。  :  0.60694575
---
これはパンクロックの音楽作品です。男性ヴォーカルがうなり声のような歌い方をしている。メロディーはエレキギターで演奏され、ベースギターがバックグラウンドで演奏されます。リズムはやや速めのロックなアコースティックドラムのビートで構成されています。アグレッシブな雰囲気を持った作品です。アクション満載のビデオ ゲームのサウンドトラックに使用できます。  :  0.5868683
---
これはサイケデリックなロック音楽です。男性ボーカリストがリードしてメロディックに歌います。エレキギターとベースギターをバックにキーボードで主旋律を奏でます。リズミカルな背景は、ロックのアコースティック ドラムのビートで構成されています。雰囲気は冷たくて奇抜です。この作品は、幻覚旅行のシーンが含まれるドラマ映画や TV シリーズのサウンドトラックに使用できます。ヒッピーコーヒーショップのバックグラウンドで再生されている可能性もあります。  :  0.57881033
---
このアマチュア録音にはクラシックな曲が含まれています。ヴァイオリンソロをフィーチャーしたインスト曲です。これに、シンプルなビートを演奏するパーカッションが伴います。キーボードがバックグラウンドでコードを演奏し、小節の間にフィルが入ります。ベースはルート音とコードの下5度を演奏します。この曲には声がありません。音質が低いため、他の楽器は不明瞭です。この曲はクラシック映画でも流れます。  :  0.56185186
---
低品質の録音には、ロック曲のカバーが含まれており、バックグラウンドでロック楽器の再生が再生され、その上でエレキギターのソロのメロディーが再生されます。おそらく低品質のマイクで録音されたため、くぐもってうるさく聞こえますが、それでも情熱的で感情的です。  :  0.5446205

指定したテキストで楽曲を検索してみる

今度はテキストで検索してみます。

model.get_text_embedding()でテキストをベクトルに変換します。こちらの関数が要素を2つ渡さないとエラーになってしまうようです。2つ渡して最初だけ使います。

text_data = ['cheerful', 'dummy'] 
text_embed = model.get_text_embedding(text_data)[0]

print(text_embed.shape)

embed_list = [info['embed'] for info in info_list]
dot_product_list = []

nrm = np.linalg.norm(text_embed)
text_embed = text_embed / nrm

for embed in embed_list:
    nrm = np.linalg.norm(embed)
    embed = embed / nrm
    dot_product_list.append(np.dot(text_embed, embed))
    
sorted_index_list = list(np.argsort(dot_product_list))
sorted_index_list.reverse()

print('find index caption:', text_data[0])
print('-------------------------')
for i in sorted_index_list[:10]:
    print(info_list[i]['caption'], ' : ', dot_product_list[i])
    print('---')

結果

「cheerful(陽気な)」というテキストで検索してみます。cosine類似度は低いですが、陽気な曲が上位にきているのではないでしょうか。

find index caption: ['cheerful', 'dummy']
-------------------------
男性ボーカリストがこのメロウなラップを歌います。テンポはミディアムで、グルーヴィーなベースライン、キーボードのハーモニー、滑らかなドラム演奏、ボーカルのバックアップが特徴です。この曲は若々しく、物語性があり、情熱的で、インパクトがあり、説得力があり、魅力的です。この曲は現代的なヒップホップ/ラップです。  :  0.39054695
---
男性ボーカリストが外国語でこの甘いアニメーションソングを歌います。テンポが速く、同様に生き生きとした陽気なピアノ伴奏が伴います。この曲は、シンプルで、甘く、愛らしく、キャッチーで、愛らしく、キャッチーで、陽気で、キュートで、魅力的で、魅力的で、魅力的です。この曲はアニメシリーズの童謡です。  :  0.3703621
---
男性ボーカルがしっとりとしたラブソングを歌います。テンポはゆっくりで、ロマンチックなピアノ伴奏、グルーヴィーなベースライン、安定したドラム演奏、アンビエントなサックスハーモニーが響きます。この曲はロマンチックで、感情的で、穏やかで、感傷的な、プロポーズやウェディングソングです。この曲はR&B/ソウルの曲です。  :  0.35007432
---
この曲には2本のアコースティックギターがメロディーを弾いています。男性の声がバックボーカルとともに高音域で歌い始めると、デジタルドラムがシンプルなグルーヴを奏でます。するとメロディーが1オクターブ下がります。ギターはスピーカーの左右にパンされます。この曲は友達との旅行中に流れているかもしれません。  :  0.35001284
---
録音の品質が悪いです。スピーカーから聞こえてくるようなバックの楽器に合わせて、男性の声がソウルフルに歌っています。ピアノの演奏があり、電子ドラムの音が聞こえます。この曲が家で歌の練習中に流れているかもしれません。  :  0.34857455
---
これは、陽気で陽気で活気に満ちたジャズ風のインストゥルメンタルです。これはスイングダンスに適しています。リックを演奏するエレクトリック・ギタリスト、パンチの効いたピアノ、そしてグルーヴィーで複雑なドラム・パターンが特徴です。この曲は4つ打ちの4拍子です。  :  0.34316605
---
このオーディオには、アコースティック ドラム セットが高速のスネア ロールを演奏し、各ビートにキックといくつかのクラッシュ ヒットが追加されています。シンセサイザーが高音域の速いリードメロディーを演奏しています。その後、曲は次のパートに突入し、ブラス/ストリングスのセクションがキックとともにビートごとにメロディーを変化させる音を演奏します。この曲はペースの速いビデオゲームで流れているのかもしれません。  :  0.34108448
---
このヒップホップ ソングは、反復的でエコーするピアノのメロディー、手拍子、パンチの効いたキック ヒットの上でラップするフラットな男性ボーカルを特徴としています。ループの最初で、たどたどしいハイハット、持続的なシンセベース、フィルターをかけられたピッチアップした女性の聖歌の演奏があります。ラッパーのラップの仕方のおかげで、グルーヴィーで中毒性のあるサウンドに聞こえます。  :  0.3331912

Discussion