🎵

🎵Supabase×OpenAIで楽曲データをベクトル化するノウハウ

に公開

はじめに

楽曲データベースから「楽曲の特徴」をベクトル(特徴量)化し、類似検索やレコメンドに応用する実装例を解説します。SupabaseとOpenAIを活用し、実際に動くPythonコードを分かりやすく説明します。

全体の仕組み

このスクリプトは、

  • Supabaseから楽曲データ(タイトル、アーティスト、テーマなど)を取得し
  • OpenAIの埋め込みAPIを使ってAIベクトル化
  • ベクトルを正規化し、データベースに保存
    という流れを自動で処理します。

コードのポイント解説

必要なライブラリと環境設定

Supabase(PostgreSQLベースのクラウドDB)、OpenAI API、dotenv(環境変数管理)、numpy(ベクトル計算)を使います。

import os
import numpy as np
from openai import OpenAI
from dotenv import load_dotenv
from supabase import create_client, Client

load_dotenv()
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
supabase: Client = create_client(SUPABASE_URL, SUPABASE_SERVICE_KEY)
openai_client = OpenAI(api_key=OPENAI_API_KEY)
embedding_model = "text-embedding-3-small"

楽曲情報を「埋め込み向けテキスト」に変換

リスト型情報(テーマ、参加アーティスト)は.join()でカンマ区切りの文字列に変換。各項目には「title:」「artist:」などラベルを明示するので、区切りの混乱が起きにくい設計です。

def generate_embedding_text(track):
summary = track.get('lyric_summary') or ""
themes_list = track.get('themes') or []
featured_list = track.get('featured_artists') or []
themes_str = ', '.join(themes_list)
featured_str = ', '.join(featured_list)

return (
    f"title: {track.get('title', '')}, "
    f"artist: {track.get('artist', '')}, "
    f"featured: {featured_str}, "
    f"year: {track.get('year', '')}, "
    f"themes: {themes_str}, "
    f"summary: {summary}"
)

メイン処理

楽曲テーブルから全件取得して、1件ずつ

  • 入力テキスト生成
  • ベクトル化API呼び出し
  • ベクトル正規化
  • DBのctx_vecカラムに更新
    を繰り返します。エラー時も確認しやすい出力付きです。
def main():
print("データベースの全楽曲ベクトルを更新します...")
select_columns = 'id, title, artist, year, themes, lyric_summary, featured_artists'
response = supabase.table('hiphop_tracks').select(select_columns).execute()

if not response.data:
    print("  - データベースに楽曲データが見つかりません。")
    return
all_tracks = response.data
print(f"  - {len(all_tracks)}件の楽曲が見つかりました。")

for track in all_tracks:
    track_id = track['id']
    track_title = track['title']
    print(f"\n  - ID: {track_id} ({track_title}) のベクトルを更新中...")
    input_text = generate_embedding_text(track)
    print(f"    - AIへの入力テキスト: 「{input_text[:100]}...」")

    embedding_response = openai_client.embeddings.create(input=input_text, model=embedding_model)
    vector = np.array(embedding_response.data.embedding)
    normalized_vector = (vector / np.linalg.norm(vector)).tolist()

    update_response = supabase.table('hiphop_tracks').update({'ctx_vec': normalized_vector}).eq('id', track_id).execute()

    if len(update_response.data) > 0:
        print(f"    - ID: {track_id} のベクトルを正常に更新しました。")
    else:
        print(f"    - ID: {track_id} の更新に失敗した可能性があります。")

print("\nすべての更新処理が完了しました。")

まとめ・応用例

ベクトルがDBに保存されることで、

  • 類似楽曲検索(ユークリッド距離・コサイン類似度など)
  • レコメンド
  • 構造化データ分析
    が簡単にできるようになります。

特にAI×DBを使った楽曲・文化データのアーカイブ運用をしたい人には、ベース実装として参考になるはずです。コードを自分のDBやモデル構成に合わせて拡張してみてください

Discussion