카카오 아레나 3회 대회(Part.1)
자신이 잘 모르는 분야에서 새로운 도전을 할 때 우리는 전문가에게 추천을 받곤 합니다. 분위기를 내러 칵테일바에 가서 술을 고를 때 메뉴를 보고 고르거나 원래 알던 술을 달라고 주문을 할 수도 있지만, 바텐더에게 추천받는다면 더 맛있는 술을 마실 수 있기 때문에, 분위기에 맞는 술을 추천해 달라고 하기도 하죠. 칵테일바에서 술을 추천받듯이, 음악에서도 나를 잘 아는 음악 바텐더가 있으면 얼마나 좋을까요?
지난 2018년 11월 카카오는 '머신러닝으로 문제를 해결하고 최적의 솔루션'을 찾는 아레나 대회를 시작하였습니다. 1회 대회는 쇼핑몰에 등록된 상품의 텍스트, 이미지 정보 등을 활용해 카테고리 분류의 정확도를 높이는 ‘쇼핑몰 상품 카테고리 분류’를 주제로 진행되었고, 2회 대회는 카카오의 콘텐츠 플랫폼브런치(brunch)의 데이터를 활용하여 '사용자의 취향에 맞는 글을 예측하고 추천' 주제로 진행되었습니다.
[1회] 문제 해결을 위한 머신러닝 오픈 플랫폼, 카카오 아레나
[2회] 브런치 데이터의 탐색과 시각화, 브런치 추천의 힘에 대한 6가지 기술(記述)
4/27일 부터 시작하는 카카오 아레나 3회 대회 Melon Playlist Continuation에서는 멜론 데이터를 사용해서 참가자들의 음악 추천 실력을 겨뤄보고자 합니다. 이번 글은 대회 참여자들에게 참고가 될 수 있도록 멜론의 음악 추천 방법 일부를 다루고 있으며, 2019년 if(kakao) 발표 둥꿍둥꿍 느낌 아는 음악 바텐더를 기반으로 작성되었습니다.
새로운 것을 추천받는 입장에서, 칵테일과 음악은 어떤 면에서 다를까요? 일단 가장 정량적으로 차이가 나는 것은 그 개수가 아닐까 싶습니다. 칵테일 바에서는 수십 개, 많아야 백여 개의 메뉴가 있으니 마음만 먹으면 언젠가는 다 마셔볼 수 있겠지만, 멜론에서는 현재 수천만 개가 넘는 곡을 서비스하고 있습니다. 이 수많은 곡들 중 분명히 내가 좋아할 만한 보석 같은 곡이 숨어있을 텐데, 그렇다고 그 곡을 찾으려고 모든 곡을 다 들어보기는 어렵습니다. 매일매일 새로운 곡을 들을 일도 없겠지만, 우리가 새로운 곡을 하루에 한 곡씩 꾸준히 듣는다고 가정해도, 10년간 들어봤자 고작 3000여 곡밖에 듣지 못합니다. 따라서, 그동안 좋은 곡들을 최대한 많이 들으려면 탐색 지점을 줄여주는 것이 필요하고, 사람들의 다양한 음악 감상 니즈를 충족시킬 수 있는 좋은 추천 시스템을 구축해야 합니다. 그렇다면 과연 추천 시스템이란 무엇일까요?
추천 시스템이란 아이템에 대한 선호도를 예측해서,
사용자가 관심을 가질만한 정보를 추려서 제공하는 것
추천 시스템을 위해서 기술적으로 어떤 것을 이용해서 무엇을 해야 할까요? 결국 우리는 데이터를 활용해서 아이템별 선호도나 유사도를 계산하는 모델을 만드는 것을 목표로 한다고 볼 수 있습니다. 이 추천 시스템의 정의에 맞게, 우리가 무엇을 하고 싶은지와 그것을 달성하기 위해서 사용할 데이터, 그리고 어떤 모델을 사용해서 해당 태스크를 수행할지를 결정한다면 우리가 달성하려는 추천 시스템의 얼개를 대략 완성할 수 있을 것 같습니다.
우리는 시스템이 칵테일바의 바텐더와 유사한 역할을 해주었으면 합니다. 즉, 내가 좋아하는 곡들과 느낌에 대해 음악 바텐더에게 얘기하면 바텐더는 이것들과 비슷한 곡들을 추천해주고, 해당 곡들이 어떤 곡인지 바로 알 수 없으니 그 곡들의 느낌을 요약해서 설명해주는 것이 우리 시스템의 목표라고 할 수 있습니다.
이것은 시드(seed) 곡과 내가 원하는 느낌을 표현하는 단어 리스트가 주어졌을 때 그 결과로 추천곡 리스트와 이를 설명하는 단어 리스트를 받는 태스크라고 할 수 있는데, 결국 주어진 곡과 비슷한 느낌의 곡들이 수록되어 있는 플레이리스트를 생성하는 태스크라고 볼 수 있습니다.
위에서 결정한 태스크를 달성하기 위해서 어떤 추천 모델을 사용할지 결정해야 합니다. 추천에서 주로 사용하는 추천 방법론에는 크게 두 가지가 있는데, 하나는 콘텐츠 기반 필터링(content-based filtering), 다른 하나는 협업 필터링(collaborative filtering)입니다.
콘텐츠 기반 필터링은 대상 자체의 특성을 바탕으로 추천하는 방법론입니다. 처음에 나왔던 칵테일바의 비유를 들자면, 비슷한 술을 추천하기 위해 해당 칵테일이 무엇으로 만들어졌는지를 분석하는 것입니다. 올드패션드(Old Fashioned)가 버번위스키 베이스의 술이니, 위스키 베이스 칵테일 중에 추천을 하는 식입니다.
콘텐츠 기반 필터링은 곡 자체에서 정보를 뽑아내기 때문에 딥러닝이 개입할 여지가 많습니다. 과거에는 손으로 만든 피쳐(feature)들을 모아서 사용하기도 했었는데, 컴퓨터 성능이 좋아지고 데이터가 훨씬 많아진 오늘날에는 손으로 피쳐를 만들기보다는 딥러닝을 많이 사용하고 있습니다.
우리가 이번 문제에서 다루는 것은 음악, 즉 오디오 데이터입니다. 오디오 데이터를 처리하기 위해서 어떤 형태로 오디오 정보를 입력받을지 결정해야 하는데, 가장 단순한 방법으로는 오디오 웨이브 데이터를 있는 그대로 넣어서 처리하는 모델을 생각해 볼 수 있습니다. 이런 형태를 엔드 투 엔드(end-to-end)라고 하는데, 웨이브 형태의 데이터를 그대로 넣기 때문에 별다른 사전 처리를 하지 않아도 된다는 장점이 있지만, 모델이 배워야 할 필터의 종류가 많기 때문에 모델이 충분히 학습되려면 전문 지식을 통해서 처리한 결과를 넣어주는 것보다 훨씬 많은 데이터를 필요로 합니다.
웨이브 시그널을 짧은 시간별로 잘라서 각 조각마다 푸리에(Fourier) 변환을 취한 후 붙이면 해당 오디오의 2D 표현을 얻을 수 있습니다. 그런데 우리의 달팽이관은 인접한 주파수와 높은 주파수 대역은 잘 구분하지 못하기 때문에, 이런 인간의 청각적 지각 능력에 맞춰서 로그 스케일로 스펙트로그램의 주파수 축을 줄이고 이 값들을 몇 개의 주파수 대역대로 묶으면 크기를 줄이는 동시에 가장 중요한 정보들을 보존할 수 있는 멜 스펙트로그램(mel-spectrogram)을 만들 수 있습니다. 멜론에서는 음원 데이터를 활용할 때 이 방식을 활용하고 있습니다.
오디오에서 추출한 멜 스펙트로그램이 이미지 형태의 데이터는 아니지만, 최근 이미지에서 잘 동작하는 컨볼루셔널(convolutional) 모델이 오디오 데이터에서도 잘 동작한다는 연구 결과가 빈번하게 발표되고 있습니다. 멜론에서는 이런 컨볼루셔널 레이어를 활용한 아키텍처를 사용해 오디오의 임베딩(embedding)을 만들고, 이것을 사용해서 음악의 높은 수준의 피쳐(High level feature)들을 추출하거나, 임베딩 간 비슷한 정도를 활용해서 유사곡을 추천하고 있습니다.
곡 자체의 내용물을 살펴보는 콘텐츠 기반 필터링과는 다르게, 협업 필터링은 '이것을 좋아했던 다른 사람들은 또 어떤 걸 좋아해요?'라는 물음을 기반으로 추천하는 것으로 올드패션드(Old Fashioned)를 마셨던 사람들이 선택한 다른 종류의 칵테일 데이터를 바탕으로 추천하는 것과 유사한 방식입니다. 예를 들어 "이 칵테일을 선택하셨던 분들이 맨해튼(Manhattan)과 새저랙(Sazerac)을 주문하였으니 이 칵테일을 마셔보지 않겠느냐"라고 추천하는 것입니다. 이런 식으로 협업 필터링은 다른 사람들이 들은 곡들을 바탕으로 어떤 곡들이 서로 비슷한지를 알아내는 방법입니다.
위의 콘텐츠 기반 필터링의 경우 딥러닝 방식을 사용하지만, 모든 데이터에서 무조건 딥(Deep) 구조를 사용한다고 결과가 뛰어나지는 않습니다. 데이터의 구조와 특성에 따라서 모델을 선택해야 하는데, 협업 필터링을 활용한 추천에서는 얕은 모델들이 아직까지는 상당 부분 우위를 점하고 있습니다.
추천에서 협업 필터링으로 가장 많이 사용되고, 좋은 성능을 보이는 모델은 등장한 지 오래된 간단한 모델인데, 추천을 할 때 유별난 요구사항이 없는 경우에 주로 사용되는 것이 행렬 분해(Matrix factorization) 모델입니다.
행렬 분해(matrix factorization)
행렬 분해(Matrix Factorization)란 단어 그대로 매트릭스를 분해하는 방법입니다. 유저가 어떤 곡을 소비했는지 여부를 유저 x 곡 매트릭스로 표현한 후 이 매트릭스를 랭크가 작은 두 개의 매트릭스로 나누고, 이렇게 두 개로 나눈 매트릭스의 곱이 원래의 매트릭스와 비슷하게 나오도록 학습하는 방법입니다. 원래의 유저 x 곡 매트릭스의 랭크보다 적은 숫자로 인풋을 학습하는 것이기 때문에, 압축하고 복원하는 과정에서 원래 유저가 소비하지 않았던 곡들에 대한 숫자들이 나오게 되는데, 이 숫자들을 유저에 대한 해당 곡의 계산된 선호도라고 볼 수 있습니다.
오토인코더(autoencoder)
모델에서 더 다양한 정보를 얻고 싶을 경우 위의 행렬 분해 보다 더 유연한 오토인코더를 사용하여 추천을 할 수도 있습니다. 오토인코더는 행렬 분해와 비슷하게 입력 데이터를 원래의 차원(dimension) 보다 적은 데이터로 줄인 후에, 변환한 데이터를 기반으로 원래의 데이터를 복원하는 구조로 되어있습니다.
오토인코더가 학습하고 데이터를 생성하는 과정은 한 문장의 설명을 듣고 몽타주를 그리는 과정과 유사합니다. 몽타주는 초상화와 다르게 제한된 정보를 이용해 최대한 원본에 가깝게 그려내는 것이기 때문에, 주어지는 정보는 높은 가치가 있어야 합니다. 물론 설명을 듣고 그림을 그리는 사람의 실력이 좋아야 합니다. 또한, 대상을 설명하는 사람 역시 경험이 부족할 경우 몽타주를 그리는 데 있어 별로 도움이 되지 않는 내용을 설명을 할 수도 있습니다. 예를 들어, [그림 5]와 같이 "얼굴과 눈, 코, 입이 있다"는 쓸모없는 정보를 몽타주 대상에 대한 설명이라고 할 수도 있습니다.
몽타주를 그리는 사람이 뛰어난 실력을 갖추었다면 대상에 대한 설명이 부족하더라도 어느 정도 형태를 갖춘 몽타주를 그려 내겠지만, 그렇지 않은 상황에서 그린 몽타주는 당연히 이상한 결과가 나올 것입니다.
오토인코더는 몽타주를 그리는 과정에서 한 문장을 설명하는 사람과 이를 듣고 몽타주를 그리는 사람이 서로 합을 맞춰가는 과정과 유사합니다. 몽타주 대상을 설명하는 문장에 최대한 많은 정보를 담고, 결과물인 몽타주가 최대한 원본에 가깝도록 학습해 나가는 모델입니다.
원래는 "얼굴과 눈, 코, 입이 있다"는 쓸데없는 정보만 있었으나, 이후에는 "귤색 타원형 얼굴, 뚜렷한 이목구비"라는 중요한 정보를 알려주는 형태로 바뀌었습니다. 이렇게 '한 문장 설명을 자동으로 인코딩한다'라는 뜻에서 오토인코더라는 이름이 붙은 모델인데, 강제로 정보를 축약해서 표현하고 이를 복원하는 과정에서 원본 값이 그대로 나오기보다 원본에서 가장 중요한 핵심 값들이 나오는 방식입니다. 이런 오토인코더의 특징은 추천 태스크에 활용할 수 있습니다.
음악 플레이리스트에서도 오토인코더를 적용할 수 있는데, 현재 플레이리스트에 해당 곡이 들어있다면 이 값을 1로 두고, 들어있지 않다면 값을 0으로 두는 방식으로 플레이리스트를 표현할 수 있습니다. 이 방식을 적용하면 몽타주를 그리는 과정과 비슷한 과정을 거쳐서, 인코더 스텝에서 나온 한 문장 설명(100여 개의 숫자들 = 임베딩)을 기반으로 원래 플레이리스트에 들어있었을 곡들이 무엇인지를 유추하는 작업을 진행할 수 있습니다.
디노이징 오토인코더(denoising autoencoder)
모델을 더 강하게 키우기 위해 환경을 더 어렵게 만들 수도 있습니다. 지금까지와 같이 모든 곡을 입력으로 넣지 않고 내가 들은 곡의 일부만 보여주고 나머지 곡들을 맞추는 태스크를 학습시킨다면, 시드 곡들을 모두 보여주는 경우에도 이 곡들을 들었을 때 어울리는 곡을 추천해 줄 수 있을 것이라 기대할 수 있습니다.
플레이리스트에 걸그룹 노래만 있다고 가정해보겠습니다. [그림 8]에서 보시는 것처럼 플레이리스트에 있는 있지, 아이즈원, 블랙핑크, 트와이스의 곡 중에서 학습을 위해 절반의 곡을 숨긴 다음, 나머지 절반의 곡을 바탕으로 숨겨진 곡들을 맞추게 하는 태스크를 학습하기로 하겠습니다. 만약 아이즈원과 트와이스의 곡을 숨겨놓고 있지와 블랙핑크의 곡을 보여줬을 때, 보이는 두 곡을 단서로 숨겨진 두 곡을 맞추는 것을 학습하면 꼭 맞추지는 못하더라도, 비슷한 곡을 추출해 내는 모델이 학습될 것이라고 기대해 볼 수 있습니다.
만약 트레이닝 과정에서 아이즈원은 맞췄으나 트와이스 대신 에미넴으로 잘못 맞췄다면, 틀린 부분은 다음 시도에는 맞출 수 있도록 진행해가며 모델을 학습시킬 수 있습니다. 이렇게 노이즈가 낀 인풋을 가지고 원본을 복원하는 태스크를 수행하는 오토인코더를 디노이징 오토인코더라고 합니다.
유연한 모델 형태를 가지는 오토인코더는 곡만 맞추는 것이 아닌 태그 등의 부차적인 정보도 같이 넣을 수 있습니다. 그래서 트레이닝을 할 때 입력으로 곡 목록을 넣어주고 이 곡 목록에서 레이턴트 피쳐(latent feature) 피쳐를 뽑은 후에, 디코딩 시 곡 목록과 태그도 같이 학습을 함으로써 이 태그들이 곡을 설명하는 역할을 하도록 할 수 있습니다. 곡들을 넣고 만들어진 레이턴트 피쳐를 보고 거기서 바로 태그를 맞추는 태스크로 설정할 수도 있고, 입력에서부터 태그를 넣어 레이턴트 피쳐에도 태그의 영향이 있도록 모델을 구성할 수도 있습니다.
지금까지의 긴 여정을 거쳐 학습을 시킨 임베딩을 사용해서 어떤 태스크를 수행할 수 있을지 알아보겠습니다. 이 임베딩을 활용해 여러 가지 유사도 태스크를 진행할 수 있는데, 원래 트레이닝 목적인 곡들을 넣고 해당 곡과 유사한 곡과 태그들을 추천받는 것뿐만 아니라, 학습된 임베딩을 가지고 임베딩 간 유사도를 활용해서 여러 가지 흥미로운 값들을 뽑아낼 수 있습니다. 곡 간 유사도, 태그 간 유사도, 그리고 곡과 태그의 유사도 등을 구할 수도 있고, 곡 임베딩과 태그 임베딩이 같은 공간 상에 분포해있다고 가정하면 마치 사칙연산을 하듯이 해당 임베딩들을 더하고 빼서 그 값이 어떤 곡의 태그와 유사한지 확인할 수도 있습니다.
가장 간단하게 볼 수 있는 태그 간 유사도를 실험해보겠습니다. "맥주"라는 태그와 어떤 태그가 유사한지를 살펴보면, 술과 관련되거나 시원한 여름밤에 노상 맥주가 연상되는 태그들을 확인할 수 있습니다.
"감각적인"이라는 태그는 세련된, 트렌디 등의 태그와 유사하다는 것을 확인할 수 있습니다. 이 중 "편집샵"이라는 태그가 눈에 띄는데, 특정 매장에서 자주 들을 수 있는 힙한 음악들의 경우를 보더라도 태그 간 유사도를 알 수 있습니다.
"나이먹기싫다"라는 태그는 어떤 태그들과 유사한지 살펴보겠습니다. 오른쪽에 나타난 결과는 위에서부터 아래로 시드 태그와의 유사도 순으로 정렬한 데이터인데, 순서를 보면 어떤 나이 순서로 가장 나이가 들기 싫어하는지 알 수 있을 것 같습니다. 스물한 살 그룹이 스물아홉 그룹보다 나이 드는 것에 더 민감하다는 것을 알 수 있습니다.
태그 임베딩 간 더하고 빼는 것도 가능할까요? "커플"에 해당하는 임베딩과 "후회"에 해당하는 임베딩을 더하고, 이 값이 어떤 태그들과 유사한지 보면 사람의 욕심과 실수에 대한 태그 결괏값을 확인할 수 있습니다.
반대로 빼기의 경우를 살펴보겠습니다. 아래 [그림 16]에서 보시는 것과 같이 "사랑"에서 "설렘"을 뺐더니 종교와 관련된 태그 결과 값들이 도출되었습니다. 사랑에서 에로스(eros)가 빠지면 아가페(agape)적 특성이 남는다는 것을 알 수 있는 결과였습니다.
처음으로 돌아가서, 이제 원래의 목표였던 곡과 태그를 바탕으로 플레이리스트를 추천받는 태스크를 시도해 보겠습니다. 바텐더에게 "스웩 있고 힙한 곡이 듣고 싶으며, 평소 프라이머리와 딘의 음악을 들었다"라고 전달하는 과정으로, 프라이머리와 딘의 대표곡 몇 개를 시드 곡으로 설정하고 '스웩'과 '힙한'이라는 태그를 넣어 추천을 받을 수 있습니다. 아래 [그림 17]과 같이 요청한 음악과 비슷한 스타일을 가진 아티스트들의 추천리스트 결과 값을 확인할 수 있습니다.
이 추천 결과를 설명하는 단어들을 찾기 위해 추천된 결과와 가장 유사한 태그들을 확인해보면, R&B, 감성, 드라이브, 트렌디, 힙합 등의 단어로 추천 결과를 요약할 수 있다는 것을 알 수 있습니다.
지금까지 멜론 추천 시스템에서 곡과 태그에 대한 임베딩을 추출하는 방법, 이를 활용한 몇 가지 예제들을 알아보았습니다. 이 글은 아레나 3회 대회에 앞서 참가자분들의 이해를 돕기 위해 작성한 글이며, 대회 참가자분들은 위에서 설명드린 방법보다 더 창의적이고 혁신적인 방법으로 유의미한 결과를 얻으실 수 있기를 희망합니다.
글 | 카카오 추천팀, 전재호 jay.jh@kakaocorp.com