[핸즈온 LLM] 추천시스템을 위한 임베딩(2.5)
🎧 Word2Vec을 이용한 음악 추천 시스템 만들기
— 재생목록 데이터를 바탕으로 비슷한 곡을 찾아보자!
최근 몇 년간 자연어 처리(NLP) 기술이 다양한 분야에 확장되면서, 우리가 듣는 음악마저도 "언어처럼" 분석하고 추천할 수 있는 시대가 되었습니다. 이번 포스팅에서는 Word2Vec이라는 단어 임베딩 기법을 활용하여, 사람이 만든 음악 재생목록으로부터 비슷한 노래를 추천하는 시스템을 직접 구현해 보겠습니다.
🎯 목표:
- 음악을 단어, 재생목록을 문장처럼 보고
- Word2Vec 모델을 통해 노래 간 유사도를 학습한 뒤
- 특정 노래와 유사한 노래들을 추천하는 것
1️⃣ 준비: 패키지 설치 및 개념 정리
먼저, gensim 라이브러리를 설치해야 합니다. 이 라이브러리는 Word2Vec 모델을 간편하게 사용할 수 있도록 도와줍니다.
!pip install gensim
📌 Word2Vec이란?
Word2Vec은 단어를 고정된 크기의 실수 벡터로 변환하는 알고리즘입니다.
핵심 아이디어는 다음과 같습니다:
같은 문맥에서 자주 등장하는 단어는 비슷한 의미를 가진다.
이 아이디어를 음악에 적용하면 이렇게 생각할 수 있죠.
같은 재생목록에 자주 등장하는 노래는 비슷한 분위기/장르/용도로 재생되었을 가능성이 높다.
즉, Word2Vec으로 음악을 벡터 공간에 매핑하면, 이 공간 내 거리(코사인 유사도 등)를 활용해 유사한 노래를 찾아낼 수 있습니다.
2️⃣ 데이터셋 로드 및 전처리
이번에 사용할 데이터는 코넬대학교의 Shuo Chen 교수가 수집한 재생목록 데이터입니다.
미국 전역의 라디오 방송국에서 수집된 실제 재생 목록들이며, 아래와 같은 형식을 따릅니다:
- 재생목록 1 : 노래1 - 노래13 - 노래2 - 노래 400
- 재생목록 2 : 노래2 - 노래81 - 노래13 - 노래82 - 노래77
- 재생목록 3 : 노래13 - 노래2
📦 코드로 데이터 로딩 및 파싱하기
# 데이터 분석에 사용할 pandas 모듈을 불러옵니다.
import pandas as pd
# 인터넷에서 파일을 불러오기 위해 urllib의 request 모듈을 임포트합니다.
from urllib import request
# 1. 재생목록 데이터 로딩
# 재생목록 데이터셋이 저장된 URL에 접근합니다.
data = request.urlopen('https://storage.googleapis.com/maps-premium/dataset/yes_complete/train.txt')
# 파일 내용을 문자열로 읽고 UTF-8로 디코딩한 후, 줄바꿈 문자('\n')를 기준으로 나눕니다.
lines = data.read().decode("utf-8").split('\n')
# 앞의 두 줄은 메타데이터(예: 날짜, 수집 환경 등)로 유용하지 않으므로 제외합니다.
lines = lines[2:]
# 줄마다 곡 ID들을 공백 기준으로 나누고, 곡이 2개 이상 포함된 경우만 재생목록으로 사용합니다.
# (Word2Vec은 문맥이 필요하기 때문에 최소 2개 이상이 필요합니다.)
playlists = [s.rstrip().split() for s in lines if len(s.split()) > 1]
# 2. 곡 정보 메타데이터 로딩
# 곡 ID와 그에 해당하는 제목, 아티스트 정보를 포함한 파일에 접근합니다.
songs_file = request.urlopen('https://storage.googleapis.com/maps-premium/dataset/yes_complete/song_hash.txt')
# 파일 내용을 문자열로 읽고 줄바꿈 문자('\n')를 기준으로 나눕니다.
songs_file = songs_file.read().decode("utf-8").split('\n')
# 각 줄을 탭('\t')을 기준으로 나누어 곡 ID, 제목, 아티스트 정보를 리스트로 만듭니다.
songs = [s.rstrip().split('\t') for s in songs_file]
# 리스트 형태의 곡 정보를 DataFrame으로 변환합니다.
songs_df = pd.DataFrame(data=songs, columns=['id', 'title', 'artist'])
# 곡 ID를 인덱스로 설정하여 추후 검색 속도를 향상시킵니다.
songs_df = songs_df.set_index('id')
- playlists는 곡 ID들의 리스트로 구성된 재생목록 데이터입니다.
- songs_df는 곡 ID에 대한 제목과 아티스트 정보를 담은 DataFrame입니다.
3️⃣ Word2Vec 모델 학습하기
이제 재생목록 데이터를 활용해 Word2Vec을 학습해보겠습니다.
from gensim.models import Word2Vec
model = Word2Vec(
playlists, # 시퀀스 데이터 (곡 ID 리스트)
vector_size=32, # 각 곡을 32차원의 벡터로 임베딩
window=20, # 컨텍스트 윈도우: 앞뒤 20개 곡까지 고려
negative=50, # 네거티브 샘플링 개수
min_count=1, # 1번이라도 등장한 곡은 모두 학습 대상
workers=4 # 병렬 처리 CPU 코어 수
)
🔍 파라미터 설명
- vector_size=32: 각 곡은 32차원의 실수 벡터로 표현됩니다.
- window=20: 하나의 노래를 기준으로 앞뒤 20개의 노래를 문맥(Context)으로 간주합니다.
- negative=50: 부정 샘플링을 통해 학습 속도를 높이며 노이즈 제거
- min_count=1: 재생목록에 한 번만 등장해도 포함 (데이터가 작기 때문에 낮게 설정)
이렇게 학습하면 Word2Vec은 각 노래 ID에 대해 32차원 임베딩 벡터를 생성합니다.
이제 이 벡터 간의 코사인 유사도를 기준으로 비슷한 노래를 찾을 수 있습니다.
4️⃣ 비슷한 노래 추천하기
특정 곡 ID에 대해 비슷한 노래를 찾고 싶다면, 다음처럼 most_similar 메서드를 사용합니다:
song_id = 2172
model.wv.most_similar(positive=str(song_id))
예를 들어, 2172번 노래가 헤비메탈이라면, 추천된 곡들도 비슷한 장르인 하드락, 메탈 계열로 모이는 걸 볼 수 있습니다.
이러한 장르 유사성이 자연스럽게 벡터 공간 상에서 가까운 거리로 나타나는 것이 Word2Vec의 강점입니다.
5️⃣ 추천 결과 보기 (데이터프레임 형태)
이제 추천된 곡 ID를 사람이 읽을 수 있도록 아티스트/제목 정보로 바꿔 출력해보겠습니다.
import numpy as np
def print_recommendations(song_id):
similar_songs = np.array(
model.wv.most_similar(positive=str(song_id), topn=5)
)[:, 0]
return songs_df.loc[similar_songs]
print_recommendations(2172)
- most_similar 함수는 (곡 ID, 유사도 점수) 쌍을 반환하며, 여기서 곡 ID만 추출
- songs_df에서 해당 ID의 제목과 아티스트를 가져와 출력합니다.
🧠 마무리: 단어를 넘어서, 노래까지
Word2Vec은 NLP에서 시작된 기술이지만, 이처럼 시퀀스 구조를 가진 모든 데이터에 응용이 가능합니다.
음악, 사용자 행동, 구매 이력 등 연속된 패턴을 가진 데이터에서 “비슷한 것”을 찾고 싶을 때 강력한 방법이 될 수 있습니다.
이 글은 『핸즈온 LLM』 (박해선 옮김, 한빛미디어, p93-98)을 바탕으로 정리하였습니다.
또한, 생성형 AI의 도움을 받아 작성되었습니다.