pizzaplanet

[참관후기] if kakao dev 2019 본문

Participation

[참관후기] if kakao dev 2019

scio 2019. 12. 13. 14:53

if (kakao) dev 2019

if (kakao) dev 2019 참관기를 정리합니다.

Korean의 Korean 체험기 | PDF | VIDEO

박용규(keith.gotkeys) / 카카오페이

한국어 자연어 처리를 위한 전처리와 모델링, 그리고 이를 활용한 웹 데이터의 감정분석 태스크를 진행했습니다.
한국어는 형태소 분석이 어렵고, 캐릭터의 개수가 많습니다.
또한 웹 데이터에서 발생하는 오탈자 및 인터넷 용어 사용으로 자연어 처리에 어려움이 있습니다.
이를 구조적 어려움과 활용적 어려움으로 구분하고, 두 가지 문제를 해결하기 위한 제 접근법을 공유합니다.
두 가지 문제는 각각 한국어에 맞게 수정한 전처리 방법과, 이를 BERT 모델로 해결하였습니다.
제안한 방법은 영화 리뷰 감정분석 태스크에서 성능을 측정하였고, 한국어를 지원하는 Google과 ETRI의 BERT 모델과 비교하였습니다.
추가로, 실험하면서 겪은 몇 가지 문제점들과 해결 방법을 발표합니다.

#AI #NLP #BERT #BPE

형태소 분석에는 크게 2가지 종류가 있다. POS tagging: 단어를 쪼갠 다음 품사를 붙여주는 작업 Stemming: 단순히 어간을 분리하는 작업이 있다. 본 발표에선 Stemming을 이용했다.

stemming을 하는 이유는 사람이 이해가능한 word를 컴퓨터가 이해 가능한 vector로 변환해주기 위함이고 아래 이슈들을 컨트롤 하기 위함이다.

  1. 여왕이라는 단어를 어떻게 볼 것인가?

    • 여왕: 여왕이라는 단어를 정확하게 표현 가능

    • : 를 포함한 다른 곳에서도 학습이 되고 도 이와 마찬가지. 그래서 여왕을 robust하게 표현 한다.

  2. Vocab Size 결정

    • high vocab size: 연산량 상승으로 학습시간 오래 걸림 --> efficiency 저하

    • low vocab size: out of vocabulary 발생 --> flexibility 저하

그렇다면 Stemming를 어떻게 할 것인가? 크게 4가지로 나눌 수 있다.

  1. 전통적인 언어학적 지식을 활용한다.
  2. 기존 알고리즘 적용(HMM, CRF, ANN)한다.
  3. 단순히 character level로 쪼갠다.
  4. data를 보고 어떻게 쪼개는 것이 맞는지 직접 확인한다.

한글은 영어와 달리 stacking 구조이기에 형태소 분석이 더욱 쉽지 않다. 그러다 보니 한 캐릭터가 여러 의미를 가짐. 같아서 좋닼ㅋㅋ을 예로 보자.

Okt 같아서 ㅋㅋ  
Mecab 아서 ㅋㅋ
Khaiii

에서 구조적 어려움이 존재하는 것을 알 수 있다.

Word Piece Embedding(WPE)

Google은 다국어 번역 서비스를 제공해야 하므로 Word Piece Embedding(WPE)를 이용했다. 이는 언어학적 지식 없이도 data driven하게 stemming이 가능하며, 많이 사용하는 sub-word를 기준으로 flexibility, efficiency 사이의 균형을 잡아준다.

WPE는 get_status()merge_vocab()이 계속 반복되며 진행된다. 기존 _로 바꾼 후 우측 character에 붙인다. 그리고 각 character로 구분 후 get_stats()을 통해 주어진 데이터에서 두 토큰씩 붙이며 붙여진 토큰이 몇 번씩 등장했는지 count 한다. 예를 들면 오늘을 보고, 늘을을 보고 이런 식. 그리고 merge_vocab()을 통해 가장 많이 등장했던 2개 토큰을 합치게 된다.

def get_status(vocab):
    paris = collections.defaultdict(int)
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols)-1):
            pairs[symbols[i], symbols[i+1]] += freq
    return pairs

def merge_vocab(pair, v_in):
    v_out = {}
    bigram = re.escape(' '.join(pair))
    p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')
    for word in v_in:
        w_out = p.sub(''.join(pair), word)
        v_out[w_out] = v_in[word]
    return v_out

위의 과정을 통해 문장을 처리하면 아래와 같이 나온다.

>>> spm.EncodeAsPieces('오늘의 연합뉴스 기사입니다')
>>> ['_오늘', '의', '_연합뉴스', '_기사', '입니다']

Jamo Piece Embedding(JPE)

한국어는 캐릭터가 아닌 자음과 모음으로 이루어져 는데 이러한 특성을 반영하여 인터넷 용어 및 오탈자에 보다 적합하다. 구현은 자음과 모음으로 캐릭터를 다 나눈 다음 WPE를 적용하였다.

Okt 같아서 ㅋㅋ  
Mecab 아서 ㅋㅋ
Khaiii
JPE 아서 ㅋㅋㅋ

JPE의 첫번째 문제

Okt 정말 신선한 영화 입니다 .      
Mecab 정말 신선 영화 입니다 .    
Khaiii 정말 영화 ㅂ니다 .
JPE 정말 선한 영화 ㅂ니다 .  

"정말신선한 영화입니다."의 제대로 된 결과는 Mecab의 결과라 생각한다. 하지만 JPE에서는 '신선한'이 '신'과 '선한'으로 분리된다. 이유는 띄어쓰기가 잘못되어 있었기 때문이다. 그래서 okt 토크나이즈를 미리 달아 띄어씌기를 제대로 달은 후 JPE 진행하였다.

JPE의 두번째 문제

JPE의 결과에 [UNK]가 너무 많이 등장했다. [UNK] 처리 된 단어는 모두 받침이 있는 단어임을 알 수 있다. 원인은 구글이 제공해주는 WPE에서 왔다. WPE의 알고리즘이 값을 리턴할 때 첫가끝 형태로 리턴을 하기 때문(첫가끝: 첫소리, 가운데소리, 끝소리의 줄임말이다. 에서 초성의 과 받침의 이 다르다.). 이는 첫가끝과 호환형을 변환해주는 딕셔너리를 제작하여 이 문제를 해결했다.

JPE 결과 [UNK] 마다 [UNK] [UNK] [UNK] [UNK]
JPE 예상결과 마다 다른 생각 드는 영화

실험 및 결과

Model: BIdirectional LSTM + Self-Attention

F1 Score NSMC_BI LSTM NSMC_BI LSTM- SA 3i4k_BI LSTM 3i4k_BI LSTM - SA
(i)shin2017 0.8203 0.8316 0.8694 0.8769
(ii)Cho2018c 0.7895 0.7973 0.8688 0.8728
(iii)Cho2018a - Sparse 0.8271 0.8321 0.8694 0.8722
(iv)Cho2018a - Dense 0.8312 0.8382 0.8799 0.8844
(v)Song2018 0.8271 0.8314 0.8696 0.8761
(vi)JamoPiece 0.8526 0.8596 0.8660 0.8614
(vii)JamoPiece + tknzr 0.8552 0.8622 0.8732 0.8668
(viii)ETRI 0.8473 0.8490 0.8755 0.8681

문장 구조를 파악하는 3i4k에서는 Jamo / character 단위 등 일정한 크기로 나누니 더 높은 성능을 보였다. 이는 JamoPiece가 문장 구조를 파기하기때문으로 해석된다.

문장의미 분석인 NSCM에서는 JamoPiece가 우수한 성능을 보였으며 tknzr이 성능향상에 도움을 주었다. 하지만 JPE의 문제를 발견함. 단어 안에 단어가 있는 경우 제대로 된 의미파악을 하지 못하는 것. 예를 들면 아래와 같다.

  • 어머니 -> 어 머니
  • 주머니 -> 주 머니
  • 쇼미더머니 -> 쇼 미 더 머니
  • 샤머니즘 -> 샤 머니 즘

이 외에도 "진짜 재밌어 요 ㅠㅠㅠ 정말정말"가 실 문맥에선 "ㅠㅠㅠ"가 "재밌어요"를 꾸미는 말이지만 word2vec에서는 슬픈 것으로 인식한다. 그래서 word2vec만으로는 감성분석에 무리가 있다. 이를 해결하기 위해 다른 모델들을 사용하는데 다른 모델들을 살펴보자.

CNN

  • 커널사이즈 거리의 관계를 고려할 수가 있다.
  • 연산간의 디펜던시가 없기 떄문에 병렬처리가 가능하다.
  • 하지만 먼 거리의 관계는 레이어를 쌓는것 만으로는 어렵다.

RNN

  • 모든 입력값을 고려한다.
  • 뒷 연산이 앞 연산에 디펜던시 하기 떄문에 병렬처리 불가
  • Vanishing Gradient 문제 존재(가장 최근 값에만 Attention)

BERT

  • 양방향 문맥 정보를 반영한 벡터로 표현, 병렬처리 가능하게 하여 CNN과 RNN의 문제점을 해결
  • Unlabeled data로 pre-train 후 transfer learning

RNN, CNN 같은 경우 input의 순서를 반영하는 구조를 가고 있다. BERT는 모든 input을 한번에 받기 때문에 순서를 반영하지 못하는 문제가 생긴다. 이는 positional encoding을 통해 해결한다. 이렇게 처리된 input들은 벡터화 되어 multi head attention으로 들어간다. D라는 dimension에 벡터가 들어오면 그걸 h로 쪼갠 다음 attention을 진행한다. 결과 h를 concat 후 add norm을 적용하고 feed foward 통과 시킨다. 이러한 작업(인코더)가 10 layer 이상(base - 12, large - 24) 존재하는 것이 BERT이다.

BERT 논문에서는 BERT가 진정한 Bidirection을 구현했음을 주장하며 이전의 model들을 디스하고 있다.

  • ELMo: embedding을 language model로 한다는 ELMo는 bidirection LSTM을 사용하고 있다. 하지만 이는 forward, backward 독립적인 연산을 하기 떄문에 진정한 bidirection이 아님
  • OpenAI GPT: transformer 앞의 input 만을 사용하기 때문에 unidirection이다.

Tokenizer

"진쨔 재밌어 요ㅠㅠㅠ 정말정말"을 각 Tokenizer로 돌려보자

Google [UNK] [UNK] [UNK]        
ETRI KoBERT 정말 정말
JPE 재밌어 ㅠㅠㅠ 정말 정말        

위 셋은 모두 같은 알고리즘을 사용하지만 결과가 모두 다르다. 이유는 이 알고리즘이 data driven 하기 때문이다.

  Google ETRI KoBERT JPE
데이터 출처 및 처리 방법 40여만건 위키피디아 10년간 신문기사 및 백과사전의 47억개 형태소
BasicTokenizer + WPE
세종코퍼스 + 위키피디아 + 나무위키
Tesla v100 1대 약 240시간

실험 및 결과(NSMC)

Models with out pretrain loss / accuracy with pretrain loss / accuracy
Google BERT 0.4244 / 0.8022 0.3673 / 0.8671
ETRI KoBERT 0.3892 / 0.8268 0.2649 / 0.8973
JamoBERT 0.3559 / 0.8479 -
JamoBERT +tknzr 0.3484 / 0.8526 0.3426 / 0.8755

with out pretrain의 결과 중에서는 JamoBERT의 결과가 가장 높았지만 with pretrain의 결과 중에서는 ETRI KoBERT가 가장 고성능을 내고 있다. 이는 아마도 pretrain 데이터의 양과 질의 차이가 아닐까라고 추측된다.

BERT 이후 좋은 모델이 많이 나왔다. 그 중 carnegie mellon의 XLNet, Facebook의 RoBERTa.

XLNet: Maked LM 대신 Permutatuin LM을 사용했다. input이 들어왔을때 각 단어를 factorization order로 autoregressive한 방법으로 학습을 적용하여 NSP를 하지 않고 BERT를 압도하였다.

RoBERTa: BERT가 아직 fit 하지 않기에 더 큰 data, batch size로 더 길게 train하여 성능향상을 증명. 그리고 NSP가 성능저하를 일으켜 사용하지 않았다고 함.

트랙 참석 후기: Linewalks에서는 MIMIC의 영문 데이터만을 이용하여 BERT로 학습 및 예측 중이었다. 본 트랙에서는 영어와 전혀 다른 특성을 가진 한국어 처리 방법에 대해 아이디어를 제시하였으며 국내 병원의 진료기록 분석에 도움이 될 것이다. 이를 대비하여 연사자 분께 직접 물어 정보를 얻어 왔다.

Q: Jamo는 직접 구현한 것인가
A: HGTK 이용

Q: 한국어 데이터로 처음 접근하기 위해 팁이 있는가
A: ETRI KoBERT로 우선 작업해보는 것을 추천한다

Q: BERT는 한 input으로 n개 이상의 token을 넣을 수 없다. 만약 한 input의 token 갯수가 n을 넘는다면 어떻게 해야하나
A: 자르거나, 중요한 부분을 직접 캐치하거나, window sliding 같은 개념을 적용하는 수 밖에 없다.

Q. Vocab size를 30,000으로 설정하였다. 왜 30,000인가
A. 다른 곳에서 대부분 30,000으로 진행하였다

Q. 30,000으로 한 이유는 단순히 다른 곳에 많이 했기에 따라 한 것인가.
A. 다른 곳과 비교해보기 위함이었다

Q. 그럼 30,000 이외의 size로도 테스트 해보았는가
A. 30,000 이전으로는 상승 하긴 했으나 그 이후로도 쭉 상승할지는 모르겠다

둥꿍둥꿍 느낌 아는 음악 바텐더 | PDF | VIDEO

어떤 곡의 느낌을 너무 좋아해서 그런 곡들만 계속 듣고 싶었던 적 있으신가요? 바텐더에게 새로운 칵테일을 추천받듯, 마음에 드는 곡 몇 개와 키워드를 가지고 취향에 맞는 음악을 추천받을 수 있다면 좋지 않을까요? 이번 발표에서는 어떤 곡이 어떤 곡들과 비슷한지, 어떤 키워드들과 어울리는지를 표현하는 방법들을 알아보고, 이를 활용한 멜론 자동 플레이리스트 생성 기술에 대해 공유합니다.

#CollaborativeFiltering #ContentBasedFiltering #머신러닝 #ML

추천시스템이란 아이템에 대한 선호도를 예측해서, 사용자가 관심을 가질만한 정보를 추천하는 것이다. 추천에도 여러 추천 방법이 있겠지만 본 트랙에서는 seed music과 비슷한 느낌의 play list를 생성하는 추천시스템을 소개한다.

추천 방법론은 크게 2개로 나뉜다.

  1. Collaborative Filtering - seed를 좋아한 타인들이 다른 어떤 곡들을 들었는지를 참고하여 추천한다.
  2. Content-based Filtering - seed의 특성을 분석하여 추천한다.

현재까지는 정확도는 Collaborative Filtering > Content-based Filtering 양상을 띄고 있다.

Content-based Filtering

곡 자체를 가지고 정보를 뽑기에 딥러닝이 개입할 여지가 많다. 오디오 데이터를 처리하기 위해서 어떤 형식의 input을 받을지 결정해야한다. input 형식은 크게 2가지로 나뉜다.

1. Input data type - Waveform

데이터 원본을 쓰는 end to end model이며 1d로 변환된다.

  • 장점: 사전처리 하지 않아도 됨
  • 단점: 모델이 배워야 할 피쳐가 많으므로 많은 데이터 필요

2. Input data type - Mel-spectorogram

최근 연구에서 많이 쓰인다. 음원을 조각으로 나누어 조각마다 퓨리에 변환을 취해 2d로 변환한다.

  • 장점: 주파수에 log scale 적용(인간의 달팽이관이 고주파수를 구분하지 못하는 아이디어에 착안)하여 몇개 주파수 대역대로 사이즈를 줄이는 동시에 중요한 정보를 보존한다.

그리고 kakao의 Embedding을 진행한다.

  1. Data를 Mel-spectorogram을 이용하여 2d로 변환
  2. VGG와 비슷한* convolution layer와 max pool convolution를 이용하여 Embedding 만듦
  3. Embedding을 이용하여 High-level features 추출 혹은 비슷한 곡 추천

VGG와 비슷함이라 함은 input, output size만 다를 뿐 layer가 점점 작아진다라는 것에서는 동일하다.

High-level features은 전문가가 태깅해놓은 감성태그를 이용하여 비태깅 음원에 대해 태깅한다. 하지만 성능이 좋지는 않다. 음악이 서로 비슷하다고 좋은 음악이라거나 사람이 좋아하는 음악이라는 보장이 없기 때문.

Collaborative Filtering

먼저 카카오가 가지고 있는 데이터를 먼저 살펴보자.

  • 유저 스트리밍 로그
    • 하루 1.5억 스트리밍
    • 들은 순서에 대한 시퀀스는 있지만 상세정보(태깅 등)는 없음
  • 유저 플레이리스트
    • 8000만개
    • 비슷한 음악끼리 묶여있을 가능성은 있지만 누구나 생성 가능하여 퀄리티는 보장 하지 못함
    • 제목, 소개글 정보가 있지만 개인용이라 제대로 입력하지 않을 가능성이 있어 노이즈로 적용할 가능성 존재 + 제목은 자연어라 정제되어있지 않아 처리 까다로움
  • DJ 플레이리스트
    • 14만개 플레이리스트, 60만 곡, 3만개 태그
    • 사전 인증된 유저가 플레이리스트 생성하기에 제목, 소개글, 태깅이 잘 되어있어 퀄리티가 좋다

DJ 플레이리스트를 사용하여 플레이리스트 + 태그 정보를 이용하여 주어진 곡과 비슷한 느낌의 플레이리스트를 생성해보자.

Model

Collaborative Filtering에서는 아직까지는 shallow model들이 우위를 점하고 있다. 주로 추천에서 Collaborative Filtering으로 제일 잘되는 모델은 matrix factorization이다.

matrix factorization

  1. 유저가 어떠한 곡을 소비했는지 여부를 User x Item matrix로 표현한다.
  2. rank가 작은 두개의 matrix로 나눈다.
  3. 나뉘어진 matrix의 곱이 원래의 matrix와 비슷하게 나오도록 학습한다.

원본 데이터보다 적은 숫자로 학습하기 때문에 압축, 복원 과정에서 원래 듣지 않았던 곡에 대해 값이 추출된다. 이 값이 유저의 선호가 될 수 있다. 하지만 더 다양한 정보를 얻고싶으므로 더 유연한 모델을 써보자. 가정 상황이 유저에 대해서 몰라도 되고 곡에 대해서만 알아도 되는 상황이니 auto encoder을 써보자.

Autoencoder

  1. 60만 rows(60만곡)의 input layer에 플레이리스트 내의 곡과 일치하는 row에 1 부여, 아닌 row에 0 부여
  2. hidden layer에서 input을 작은 차원으로 압축
  3. output layer에서 hidden layer의 결과를 보고 input layer을 유추

autoencoder은 유연한 모델이기에 곡에 해당되는 tag들도 같이 input으로 입력 가능하다. 그럼 output으로는 추천 플레이리스트와 플레이르스트를 설명하는 태그도 같이 추출할 수 있다.

이것보다 좀 더 어려운 task로 플레이리스트 일부만 input으로 쓴 후 원본 input을 유추하는 task를 수행해보자.

Denoising autoencoder

  1. input에서 일부 데이터를 수정 후 output에서 제대로 복원이 되었는지를 예측

일반적인 autoencoder보다 로버스트한 동작을 한다.

어떻게 학습을 빠르게 할 것인가?

input: O(K * d) = 3000, (K - DJ 플레이 리스트당 약 30곡, d - dense)

output: O(N * d) = 60,000,000 (N - 멜론이 보유한 모든 곡 60만, d - dense)

입력을 latent feature로 바꾸는 것은 굉장히 빠른 반면, latent feature를 output으로 바꾸는데는 굉장히 많은 시간이 든다. 병목현상이 생기는 것.

병목현상이 생기는 부분을 제거하여 속도를 향상시켜보자. 로스가 특정 꼴로 되어있으면 output을 다 구하지 않아도 loss를 구하고 back propagation이 가능하다. 이 방법은 loss에서 weight가 제곱의 꼴로 등장할때 사용 가능하다. metric는 mse를 사용한다.

L = ||Wh - y||^2
(y - input, h - latent feature, h - hidden layer와 output 사이)

DJ플레이리스트가 14만개가 있으니 모두 트레이닝에 사용 하면 똑같은 loss를 14만번 계산해야한다. 하지만 위 식을 풀어보면 재활용하는 식을 찾을 수 있다.

L= h^t(Qh - 2W^ty) + y^ty

미리 Q라는 matrix로 계산 및 저장을 해두면 계산량을 줄일 수 있다. 마지막 line에서 각 항목의 time complexity를 보자면.
Qh = O(d^2)
2W^ty = O(Kd)
y^ty = O(K)

그럼 6000만번 계산해야하는 상황이 약 13,000번만 계산하는 상황으로 바뀐다.

h(latent feature)에서 Gradient를 구할때에도 위 방법과 마찬가지로 계산량을 아래의 식으로 바로줄일 수 있다.
2(Qh - W^ty)
한 batch안에서는 Q의 값이 일정하지만 batch종료 후에는 weight를 업데이트 해야하니 Q값이 달라진다. 그럼 매 배치마다 W를 update해야하고 Q도 다시 계산을 해주어야한다. 하지만 Q와 W의 값들도 O(d^2)에 가능하다. 자세한 것은 논문을 참조하도록 하자

결론적으로 autoencoder의 loss로 mse를 사용한다면 마지막 layer 계산에서 최대 500배 이상의 속도향상을 볼 수 있다.

  before precomputation after precomputation
CPU (16 cores) 1672 sec/epoch 3 sec/epoch
GPU (v100 x 1) 21 sec/epoch less than 1 sec/epoch

그럼 이것을 어디에 사용할 수 있을까

  • 곡 출력 임베딩
  • 태그 출력 임베딩
  • 곡 간 유사도
  • 태그 간 유사도
  • 곡 - 태그 유사도
  • 태그 + 태그 ?
  • 태그 - 태그?

위를 이용하여 곡 + 태그를 이용한 추천을 할 수 있다. 예로 "스웩있는 곡이 듣고 싶어요. 프라이머리와 Dean을 듣고 있는데, 이런 힙한 곡들이 듣고싶어요"라는 요청이 들어왔을때를 가정하자.

input

  • 곡: Dean과 프라이머리의 대표곡 N개
  • Tag: 스웩, 힙한

output

  • 박재범, Crush, Zion.T 노래
  • input에서 사용되지 않은 Dean과 프라이머리의 곡
  • 위 곡들과 가장 유사한 단어를 찾아보면 감성, 트렌디, Drive 등의 단어가 나온다.

만약 output에서 특정 가수의 곡이 너무 많거나 등의 문제는 post processing을 통해 해결한다.

Buffalo: Open Source Project for Recommender System

김광섭(lucas.kim) / 카카오

카카오 추천시스템에 활용중인 Matrix Factorization 알고리즘을 엮은 버팔로 오픈소스를 소개한다. 널리 알려진 알고리즘을 카카오 데이터와 환경에 맞게 개선하고 변경한 내용을 설명하고 기존에 공개된 유사한 목적의 오픈 소스 프로젝트와의 밴치마킹 결과 비교를 통해 장점과 단점을 알아보고자 한다.

개요

후기

autoencoder을 이용한 추천도 중요한 내용이지만 무엇보다 matrix factorization를 통한 input size 감소, precomputation를 통한 학습 속도 향상이 매우 인상적이다. 이를 참고하면 부족한 컴퓨팅 파워 환경속에서도 좋은 결과를 낼 수 있을 것으로 기대한다.

Comments