Überspringen Sie zu Inhalten

aaron.de

Primäres Menü
  • Über mich
  • Heim
  • 2025
  • April
  • J
  • LLMs sind kein Allheilmittel: Praxistest zur Musik-Klassifikation anhand von Metadaten
  • Allgemein

LLMs sind kein Allheilmittel: Praxistest zur Musik-Klassifikation anhand von Metadaten

aaron 3. April 2025

Die Fragestellung war, ob aktuelle Large Language Models (LLMs) wie GPT-4 oder DeepSeek in der Lage sind, Musikstücke – speziell Salsa-Songs – anhand von Titel, Künstler, Songtext und Metadaten automatisch und zuverlässig in „Salsa Cubana“ oder „Salsa Línea“ zu klassifizieren. Es war bekannt, dass die Informationslage (Metadaten, Genre-Tags, Lyrics) lückenhaft und teilweise uneinheitlich ist. Der Test diente explizit dazu, die praktischen Grenzen heutiger LLMs in diesem Kontext zu ermitteln.

Vorgehen
Für jeden Song einer Spotify-Playlist wurden sämtliche verfügbaren Metadaten erfasst: Titel, Künstler, Album, Genres, Veröffentlichungsdatum, Lyrics (über Genius), zusätzliche Tags und Biografien (über Last.fm). Diese Daten wurden in strukturierter Form an das LLM übergeben. Der Prompt war detailliert gestaltet: Neben Rollendefinition und Klassifikationskriterien enthielt er Beispiele und klare Instruktionen, damit das Modell ausschließlich „Cubana“ oder „Línea“ auswählt.

You are a highly specialized music expert with in-depth knowledge of all salsa dance variants and their musical characteristics.

Reference songs and artists for Cuban Style ("Cubana"):
- Los Van Van ("Soy Todo", "Sandunguera")
- Havana D’Primera ("Pasaporte", "Me Dicen Cuba")
- Maykel Blanco ("Recoge y Vete")
- Buena Vista Social Club ("Chan Chan")
- Adalberto Álvarez ("Para Bailar Casino")

Reference songs and artists for Salsa Línea ("Línea"):
- Marc Anthony ("Valió La Pena")
- Frankie Ruiz ("Tu Con El")
- Victor Manuelle ("Tengo Ganas")
- Grupo Galé ("Ven a Medellín")

Classification criteria:
- "Cubana": Lyrics are playful, social, Cuban-themed, humorous, or political; music is polyrhythmic, improvisational, references Cuba or uses Cuban artists.
- "Línea": Lyrics are romantic, dramatic, or about heartbreak; music is polished, commercial, typically NY/LA/PR based.

Instructions:
1. If the artist or song is in the Cubana reference list, answer "Cubana".
2. If the artist or song is in the Línea reference list, answer "Línea".
3. If genres/tags include "romantica", "salsa romantica", "balada", "latin pop", "bachata", or if lyrics are about love, heartbreak, or have a dramatic/romantic style, answer "Línea".
4. If genres/tags include "son cubano", "timba", "charanga", "cuban", "guaguancó", or lyrics are playful/social/political, answer "Cubana".
5. If mixed, **decide based on what dominates most (count the indicators).**
6. If unsure, pick the most likely based on genres/tags and lyrics. NEVER output "Cubana" by default if unsure, always follow these rules.

Your output must be exactly one of: Cubana or Línea.

EXAMPLES:

Song: "Valió La Pena"
Artist: Marc Anthony
Genres: salsa, salsa romantica
Tags: salsa romantica, latin pop
Lyrics: romantic
Output: Línea

Song: "Chan Chan"
Artist: Buena Vista Social Club
Genres: son cubano, salsa, guajira
Tags: cuba, son cubano, timba
Lyrics: references Cuba, social themes
Output: Cubana

Now classify this song:

Song: {track_name}
Artist: {artist_name}
Album: {album_name}
Release Date: {release_date}
Genres: {artist_genres}
Last.fm Info: {lastfm_info}
Lyrics: {lyrics}
prompt.txt
You are a salsa music expert. Only reply with Cubana or Línea. Never reply with "Both" or "Unknown" or any explanation.
system_prompt.txt

Beobachtungen und Erkenntnisse

  • Die Klassifikation durch das LLM erfolgte rein textbasiert. Ein wirkliches Musikverständnis ist damit ausgeschlossen.
  • Die verfügbaren Metadaten reichten für die Stilunterscheidung nicht aus: Genre-Tags waren allgemein, Labels wie „Salsa Cubana“ oder „Línea“ fehlten meistens.
  • Songtexte lieferten selten eindeutige Anhaltspunkte, da die thematische Bandbreite in der Salsa sehr groß ist.
  • LLMs zeigten bei identischen Prompts und Daten zum Teil inkonsistente Ergebnisse. Unterschiedliche Modelle (z.B. DeepSeek, GPT-4o) kamen bei gleicher Eingabe teilweise zu unterschiedlichen Resultaten.
  • Auch durch das Hinzufügen weiterer Metadaten und externer Quellen wie Last.fm ließ sich die Qualität der Zuordnung nicht steigern. Die Ergebnisse blieben unzuverlässig, oft wurde pauschal „Cubana“ zugeordnet, unabhängig von tatsächlichen Stilmerkmalen.
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import requests
import time
import lyricsgenius

# --------- Toggles ----------
USE_LOCAL_DEEPSEEK = False # True = use local DeepSeek via Ollama, False = use OpenAI online
USE_GENIUS = True # True = use Genius for lyrics, False = skip lyrics
USE_LASTFM = True # True = use Last.fm for additional info, False = skip
SHOW_LLM_PROMPT_DEBUG = False # True = show the full prompt with all fields, False = skip printing it

# ---------- Spotify API credentials ----------
CLIENT_ID = '6a....9f'
CLIENT_SECRET = '56....7'
PLAYLIST_ID = '1mZELHyKrXNyLOumaDiQwh'

# ---------- Genius API credentials ----------
GENIUS_TOKEN = 'cu....25'

# ---------- Last.fm API credentials ----------
LASTFM_API_KEY = 'd1....8g'

# ---------- Local DeepSeek/Ollama ----------
OLLAMA_URL = 'http://localhost:11434/api/generate'
MODEL_LOCAL = 'deepseek-coder-v2'

# ---------- OpenAI Online (GPT-3.5/4/4o) ----------
OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions'
OPENAI_API_KEY = 'sk....1B'
MODEL_ONLINE = 'gpt-4o'

# ---------- Prompt templates ----------
with open('prompt.txt', encoding='utf-8') as f:
PROMPT_TEMPLATE = f.read()
with open('system_prompt.txt', encoding='utf-8') as f:
SYSTEM_PROMPT = f.read()

sp = spotipy.Spotify(
auth_manager=SpotifyClientCredentials(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET
)
)

genius = lyricsgenius.Genius(GENIUS_TOKEN, skip_non_songs=True, verbose=False, remove_section_headers=True)

def clean_track_name(name):
import re
name = re.sub(r"\(.*?\)", "", name)
name = name.replace("- En Vivo", "")
name = name.strip()
return name

def get_lyrics(track_name, artist_name):
if not USE_GENIUS:
return ""
cleaned_name = clean_track_name(track_name)
song = genius.search_song(cleaned_name, artist_name)
if song and getattr(song, "lyrics", None):
return song.lyrics[:1000]
song = genius.search_song(cleaned_name)
if song and getattr(song, "lyrics", None):
return song.lyrics[:1000]
return ""

def get_lastfm_info(track_name, artist_name):
if not USE_LASTFM:
return ""
try:
url_track = (
f"https://ws.audioscrobbler.com/2.0/?method=track.getInfo"
f"&api_key={LASTFM_API_KEY}&artist={requests.utils.quote(artist_name)}"
f"&track={requests.utils.quote(track_name)}&format=json"
)
resp_track = requests.get(url_track, timeout=10)
info_track = resp_track.json().get("track", {})
tags = [t["name"] for t in info_track.get("toptags", {}).get("tag", [])]
wiki = info_track.get("wiki", {}).get("summary", "")
url_artist = (
f"https://ws.audioscrobbler.com/2.0/?method=artist.getInfo"
f"&api_key={LASTFM_API_KEY}&artist={requests.utils.quote(artist_name)}&format=json"
)
resp_artist = requests.get(url_artist, timeout=10)
info_artist = resp_artist.json().get("artist", {})
artist_tags = [t["name"] for t in info_artist.get("tags", {}).get("tag", [])]
bio = info_artist.get("bio", {}).get("summary", "")
extra = ""
if tags:
extra = f"Last.fm track tags: {', '.join(tags)}. "
if artist_tags:
extra = f"Last.fm artist tags: {', '.join(artist_tags)}. "
if wiki:
extra = f"Track summary: {wiki[:300]}... "
if bio:
extra = f"Artist bio: {bio[:300]}... "
return extra.strip()
except Exception:
return ""

def classify_track(
track_name, artist_name, lyrics, album_name, release_date, artist_genres, lastfm_info, temperature=0.0
):
prompt = PROMPT_TEMPLATE.format(
track_name=track_name,
artist_name=artist_name,
album_name=album_name,
release_date=release_date,
artist_genres=artist_genres,
lastfm_info=lastfm_info,
lyrics=lyrics
)
# --- Print FULL prompt for debugging and transparency if toggle is active ---
if SHOW_LLM_PROMPT_DEBUG:
print("\n================= LLM PROMPT =================")
print(f"Song: {track_name}")
print(f"Artist: {artist_name}")
print(f"Album: {album_name}")
print(f"Release Date: {release_date}")
print(f"Genres: {artist_genres}")
print(f"Last.fm Info: {lastfm_info}")
print(f"Lyrics: {lyrics[:400]}")
print("==============================================\n")
# ------------------------------------------------------
if USE_LOCAL_DEEPSEEK:
data = {
"model": MODEL_LOCAL,
"prompt": prompt,
"stream": False
}
try:
response = requests.post(OLLAMA_URL, json=data, timeout=120)
if response.ok:
answer = response.json().get('response', '').strip()
if answer in ["Cubana", "Línea"]:
return answer
return None
else:
return None
except Exception:
return None
else:
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"Content-Type": "application/json"
}
data = {
"model": MODEL_ONLINE,
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt}
],
"max_tokens": 5,
"temperature": temperature
}
try:
response = requests.post(OPENAI_API_URL, headers=headers, json=data, timeout=120)
if response.ok:
answer = response.json()['choices'][0]['message']['content'].strip()
if answer in ["Cubana", "Línea"]:
return answer
return None
else:
return None
except Exception:
return None

# -- Fetch tracks --
tracks = []
offset = 0
limit = 100
while True:
results = sp.playlist_items(PLAYLIST_ID, offset=offset, limit=limit)
items = results.get('items', [])
if not items:
break
tracks.extend(items)
offset = len(items)

for idx, item in enumerate(tracks):
track = item.get('track')
if not track:
continue
name = track.get('name', 'Unknown Title')
artist = ', '.join([a.get('name', 'Unknown Artist') for a in track.get('artists', [])])
album = track.get('album', {}).get('name', 'Unknown Album')
release_date = track.get('album', {}).get('release_date', 'Unknown Date')
print(f"\n[{idx 1}] {name} - {artist}")
# Last.fm info
#print(" Fetching Last.fm info ...")
lastfm_info = get_lastfm_info(name, artist)
# Lyrics
#print(" Fetching lyrics ...")
lyrics = get_lyrics(name, artist)
# Genres for first artist
artist_id = track.get('artists', [{}])[0].get('id', None)
artist_genres = []
if artist_id:
try:
artist_info = sp.artist(artist_id)
artist_genres = artist_info.get('genres', [])
except Exception:
artist_genres = []
genres_str = ', '.join(artist_genres)
# Klassifikation (zeigt Prompt im classify_track)
style = classify_track(
track_name=name,
artist_name=artist,
album_name=album,
release_date=release_date,
artist_genres=genres_str,
lastfm_info=lastfm_info,
lyrics=lyrics
)
if style not in ["Cubana", "Línea"]:
style = classify_track(
track_name=name,
artist_name=artist,
album_name=album,
release_date=release_date,
artist_genres=genres_str,
lastfm_info=lastfm_info,
lyrics=lyrics,
temperature=0.3
)
if style not in ["Cubana", "Línea"]:
style = "Cubana"
print(f" => {style}")
time.sleep(1.5)
analyzer.py

Fazit
Eine zuverlässige Musikstil-Klassifikation nur anhand von Metadaten und Lyrics ist mit aktuellen LLMs nicht möglich. Die Resultate sind inkonsistent, die Fehlerquote hoch und die Zuordnungen meist nicht nachvollziehbar. Zusätzliche Metadaten und aufwändige Prompt-Templates mit Beispielen und Instruktionen konnten die Grenzen der Modelle nicht überwinden. Für eine präzise stilistische Musik-Klassifikation bleibt menschliches Fachwissen oder eine spezifische musikalische Analyse weiterhin erforderlich.

Table of Contents

Toggle
  • Über den Autor
      • aaron

Über den Autor

Avatar-Foto

aaron

Administrator

Besuchen Sie die Website Alle Beiträge anzeigen

Post navigation

Previous: Omniverse Tutorial
Next: KI-gestützter Event-Agent für Veranstaltungen

Verwandte Geschichten

BotFather_BG_Aaron_Kreis
  • Allgemein

Telegram Bot API Tutorial

aaron 15. September 2025
kameras
  • Allgemein

Web Application Firewall (WAF): Ein praktischer Leitfaden

aaron 8. September 2025
AsciiDoc_005
  • Allgemein

AsciiDoc & Kroki: eine bewährte Kombination für klare Softwaredokumentation

aaron 6. September 2025

Sie haben vielleicht verpasst

BotFather_BG_Aaron_Kreis
  • Allgemein

Telegram Bot API Tutorial

aaron 15. September 2025
kameras
  • Allgemein

Web Application Firewall (WAF): Ein praktischer Leitfaden

aaron 8. September 2025
AsciiDoc_005
  • Allgemein

AsciiDoc & Kroki: eine bewährte Kombination für klare Softwaredokumentation

aaron 6. September 2025
Data
  • Allgemein

Datensouveränität als Schlüssel für erfolgreiche KI

aaron 3. September 2025
Impressum & Datenschutz
Copyright © All rights reserved. | MoreNews von AF themes.
Diese Website benutzt Cookies. Wenn du die Website weiter nutzt, gehen wir von deinem Einverständnis aus.