Word2Vec 과 같은 word embedding 은 distributed representation 을 이용하여 의미가 비슷한 단어를 비슷한 벡터값으로 표현합니다. 그러나 Word2Vec 역시 모르는 단어 (out of vocabulary) 에 대해서는 word representation vector 를 얻을 수 없습니다. 더하여, Word2Vec 은 infrequent words 에 대하여 학습이 불안한 특징도 있습니다. FastText 는 이를 보완하기 위하여 개발된 word representation 입니다. FastText 는 typo 와 같은 노이즈에 강하며, 새로운 단어에 대해서는 형태적 유사성을 고려한 word representation 을 얻습니다.
Introduction
Word2Vec 과 같은 word embedding 은 distributed representation 을 이용하여 의미가 비슷한 단어를 비슷한 벡터값으로 표현합니다. 그러나 Word2Vec 역시 모르는 단어 (out of vocabulary) 에 대해서는 word representation vector 를 얻을 수 없습니다. 더하여, Word2Vec 은 infrequent words 에 대하여 학습이 불안한 특징도 있습니다. FastText 는 이를 보완하기 위하여 Word2Vec 을 제안한 Mikolov 가 2 년 뒤 추가로 제안한 word representation 입니다. Word2Vec 은 한 단어의 벡터값을 직접 학습하지만, FastText 는 단어를 구성하는 subwords (substrings) 의 벡터의 합으로 단어 벡터를 표현합니다. 이 방법은 typo 가 있는 단어라 할지라도 비슷한 word representation 을 얻을 수 있으며, 새로운 단어에 대해서도 단어의 형태적 유사성을 고려한 적당한 word representation 을 얻도록 도와줍니다.
FaceBook Research 의 FastText repository 에 들어가면 세 종류의 논문이 참조되어 있습니다. 이 중 Word2Vec 과 같은 unsupervised word representation 은 첫번째 논문이며, 2 와 3 번째 논문은 supervised word representation 과 효율적인 document classifiers 에 대한 내용입니다. 이번 포스트에서는 첫번째 논문에 대하여 이야기합니다.
- P. Bojanowski, E. Grave, A. Joulin, T. Mikolov, Enriching Word Vectors with Subword Information
- A. Joulin, E. Grave, P. Bojanowski, T. Mikolov, Bag of Tricks for Efficient Text Classification
- A. Joulin, E. Grave, P. Bojanowski, M. Douze, H. Jégou, T. Mikolov, FastText.zip: Compressing text classification models
Out of vocabulary, infrequent words (Word2Vec)
자연어처리 문제에서 언제나 등장하는 어려움은 아마도 (1) 미등록 단어 (out of vocablary) 와 (2) 모호성 일 것입니다. Word2Vec 역시 미등록 단어 문제를 겪습니다. Softmax regression 을 이용하는 Word2Vec 은 앞/뒤에 등장하는 단어로 가운데에 위치한 단어를 예측하는 과정을 통하여 문맥이 비슷한 단어를 비슷한 벡터로 표현합니다. 그렇기 때문에 한 번도 보지 못한 단어에 대해서는 단어 벡터를 학습할 기회가 없습니다.
그리고 단어가 작성되는 과정에서 typo 는 자연스럽게 발생합니다. 문제는 이러한 typo 들의 빈도수는 대체로 작고, 이들은 Word2Vec 을 학습하는 과정에서 min count filtering 에 의하여 제가된다는 점입니다.
사실 infrequent words 는 제거가 되지 않더라도 Word2Vec 에서 학습이 잘 되지 않습니다. 아래는 네이버 영화와 IMDB review 에서 학습한 Word2Vec 모델을 이용하여 유사 단어를 검색한 예시입니다. 괄호 안은 단어의 빈도수 이며, 첫 row 는 queries 입니다. 영화의 유사 단어는 애니, 애니메이션, 작품 과 같은 단어입니다. 형태와 관계없이 의미가 비슷한 단어들이 잘 학습되며, 영화의 typo 인 엉화, 양화 역시 유사어로 검색이 됩니다. 그리고 typo 를 제외하면 대체로 빈도수가 큽니다.
영화 (1412516) | 관람객 (585858) | 재미 (344634) | 연기 (255673) | 관상 (988) | 클로버필드 (136) |
---|---|---|---|---|---|
애니 (6075) | 굿굿 (14681) | 제미 (630) | 케미 (2257) | 광해 (4143) | 투모로우 (598) |
애니메이션 (7456) | 그치만 (1616) | 재이 (197) | 가창 (104) | 베를린 (2441) | 다이하드 (277) |
작품 (39544) | 이지만 (8276) | 잼이 (730) | 영상미 (11800) | 도둑들 (2954) | 쿵푸팬더 (94) |
명화 (708) | 유쾌하고 (2810) | 잼 (13098) | 목소리 (3489) | 역린 (1256) | 매트릭스 (928) |
드라마 (16306) | but (809) | 짜임새 (3739) | 캐미 (562) | 놈놈놈 (529) | 실미도 (337) |
에니메이션 (577) | 그러나 (9951) | 기다린보람이 (98) | 아역 (4463) | 부당거래 (676) | 헝거게임 (121) |
엉화 (126) | 듯하면서도 (72) | 잼미 (120) | 카리스마 (3034) | 과속스캔들 (850) | 레지던트이블 (199) |
수작 (5048) | 아주 (24571) | ㅈㅐ미 (27) | 노래 (24689) | 감시자들 (654) | 메트릭스 (121) |
양화 (164) | 다만 (9957) | 특색 (164) | 열연 (3326) | 전우치 (1863) | 분노의질주 (194) |
블록버스터 (5015) | 였지만 (5319) | 잼도 (39) | 배우 (139416) | 숨바꼭질 (470) | 새벽의저주 (215) |
그러나 infrequent words 의 유사어는 infrequent 합니다. 영화 제목 ‘클로버필드’의 typo 인 ‘클러버필드’는 7 번 등장하였고, 그 단어의 유사어들은 대부분 빈도수가 작습니다.
켄시로 (5) | 나우유씨 (5) | 클러버필드 (7) | 와일더 (5) |
---|---|---|---|
클러버필드 (7) | 씨미 (47) | characters (5) | 짱예 (11) |
디오디오디오디오디오 (8) | 로보 (408) | 미라클잼 (5) | 생스터 (23) |
역스 (5) | 트레 (42) | 유월에 (5) | 룰라 (13) |
qf (5) | 뱅 (13) | 디오디오디오디오디오 (8) | 존섹 (20) |
숨도못쉴만큼 (5) | 죤 (19) | 잡잡잡잡 (5) | 윌터너 (39) |
좋갯다 (5) | 썩시딩 (9) | 내꼬야 (5) | 이뻐이뻐 (16) |
구웃구웃 (9) | 니이이 (6) | qf (5) | 이뿌구 (13) |
굳ㅋ굳ㅋ굳ㅋ굳ㅋ굳ㅋ (5) | 피아 (469) | 굳굿굳굿굳 (5) | 77ㅑ (10) |
마니마니마니 (7) | 빠이 (50) | 애앰 (6) | 긔요미 (19) |
유월에 (5) | 합류하 (14) | romantic (5) | 세젤예 (5) |
이러한 현상은 토크나이징이 대체로 쉬운 영어 데이터인 IMDB 에서도 벌어집니다. game 의 유사어로 games 와 같은 복수형을 포함한 tournament 와 같은 유사어가 잘 검색이 되고, 그들의 빈도수는 큽니다.
offer (70274) | source (70065) | point (69646) | game (69570) | clear (69270) | lost (68763) |
---|---|---|---|---|---|
offering (35315) | sources (66331) | moment (19169) | games (34483) | sure (31732) | gained (28668) |
bid (56866) | official (118857) | points (94945) | match (30192) | obvious (4959) | regained (2074) |
purchase (20995) | person (36569) | juncture (650) | movie (27569) | unclear (15638) | dropped (36074) |
proposal (37150) | aide (10990) | stage (31008) | tournament (18732) | surprising (5217) | slumped (5126) |
buy (73425) | diplomat (12020) | point; (35) | format (3497) | true (13114) | plummeted (2188) |
receive (22050) | banker (7946) | time (270178) | season (62459) | helpful (2757) | plunged (7605) |
offers (16412) | staffer (834) | level (65495) | franchise (7941) | correct (5363) | tumbled (7720) |
deal (208557) | matter (46652) | outset (1212) | lineup (3504) | clearly (16796) | surged (10280) |
appeal (23098) | participant (1081) | least (99186) | comedy (9174) | wrong (15615) | soared (6401) |
unsolicited (1427) | spokesperson (3471) | bhatt (11) | matchup (512) | question (34575) | climbed (12394) |
하지만 infrequent words 의 유사어가 infrequent 한 현상은 동일합니다.
Shellback (5) | Reflektor (5) | Lazaretto (5) | Suchman (5) | Kissin (5) | Maccabees (5) |
---|---|---|---|---|---|
keyboardist (82) | naveen (5) | MINISERIES/TELEVISION (6) | Doctorow (29) | GAPEN (8) | ici (9) |
Frideric (7) | Kaczorowski; (5) | Groupings (5) | Bub (7) | anup (6) | (Outside (7) |
Chick (48) | com/gen92k (8) | Kinosis (5) | deWitt (6) | roy (6) | ET/GMT (17) |
co-writer (146) | alonso (7) | Davis/Greg (5) | Ross: (11) | ASHWORTH (6) | CGC-12-520719 (27) |
singer) (17) | Davis/Greg (5) | 2017-2027 (9) | Helfer (6) | samajpati (10) | GRIZZLIES (6) |
Sings (15) | guttsman (5) | 09-md-02036 (11) | Swank: (14) | DETROIT/PARIS (10) | Government-Related (7) |
Ralph: (5) | yoon (8) | Acquino (8) | Screenwriters: (27) | Maso (5) | Place/Paolo (5) |
saxophonist (56) | LUZERNE (6) | SAUGUS (7) | Pittendrigh (6) | neetha (5) | emploi (7) |
Menken; (5) | SKOLKOVO (6) | 13-900 (17) | producer/director (9) | 6386 (7) | Only) (10) |
Amis (53) | 13-09173 (6) | 2017-2041 (6) | Manganiello (11) | 9202 (5) | (Brooklyn) (19) |
이는 Word2Vec 의 구조적 특성상 frequent words 에 집중하여 학습이 이뤄지기 때문입니다. 사실상 infrequent words 는 포기한다고 생각해도 됩니다. 그러나 Zipf’s law 를 따르는 단어의 특성상 언제나 infrequent words 의 종류가 많습니다. 많은 단어들이 제대로 학습이 이뤄지지 않습니다.
FastText 는 이러한 현상도 함께 해결하고 싶었습니다. ‘클로버필드’가 잘 학습되었기 때문에 형태적으로 유사한 ‘클러버필드’ 역시 비슷한 학습 벡터를 가지길 원합니다.
Subword representation
Out of vocabulary 문제를 tokenization 단계에서 해결하기 위하여 Word Piece Model (WPM) 이 제안되기도 했습니다. WPM 은 ‘appear’ 라는 단어가 잘 알려지지 않았다면, 이를 잘 알려진 subword units 인 ‘app + ear’ 으로 나눕니다. appear 의 의미가 제대로 인식되는 것은 Recurrent Neural Network (RNN) 에게 맏기더라도 out of vocabulary 는 만들지 않겠다는 의미입니다. 그러나 appear 의 의미가 app 과 ear 로부터 composition 이 일어나기는 어렵습니다.
FastText 는 단어를 bag of character n-grams 로 표현합니다. 이를 위하여 먼저 단어의 시작과 끝부분에 <, > 를 추가합니다. 예를 들어 character 3 grams 를 이용한다면 ‘where’ 이라는 단어는 5 개의 3 글자짜리 subwords 로 표현됩니다.
where -> <wh, whe, her, ere, re>
그리고 실제 단어를 나타낼 때에는 3 ~ 6 grams 를 모두 이용합니다.
where -> <wh, whe, her, ere, re>
<whe, wheh, here, ere>
...
마지막으로, 길이와 상관없이 단어에 <, > 를 더한 subword 는 special unit 으로 추가합니다.
where -> <wh, whe, her, ere, re>
<whe, wheh, here, ere>
...
<where>
단어 ‘where’ 의 벡터는 subword vectors의 합으로 표현합니다.
v(where) = v(<wh> + v(whe) + ... v(<where) + v(where>) + v(<where>)
이처럼 단어를 subwords 로 표현하면 typo 에 대하여 비슷한 단어 벡터를 얻을 수 있습니다. Character 3 gram 기준으로 where 와 wherre 는 두 개의 subwords 만 다르고 대부분의 subwords 가 공통으로 존재하기 때문입니다.
where -> <wh, whe, her, *ere*, re>
wherre -> <wh, whe, her, *err*, *rre*, re>
그리고 단어 벡터가 아닌 subwords 의 벡터들을 학습합니다. 그 외에는 Word2Vec 과 동일합니다. Word2Vec 에서 word look-up 을 하는 과정 대신, subwords look-up 을 수행합니다.
아래 식에서의 는 context 와 word 에 대한 engative samples 입니다.
Word2Vec loss:
FastText loss:
where , g is subwords
그 결과 (word, context) pairs 에 포함된 subwords 끼리 가까워집니다. 문맥적인 의미가 비슷한 young 과 adole, adoles, doles 는 높은 similarity 를 지닙니다 (빨간색일수록 두 subword vectors 간의 Cosine similarity 가 큽니다).
이 과정을 통하여 학습 때 포함되지 않은 단어들에 대해서도 형태적 유사성을 고려한 단어의 벡터를 표현할 수 있으며, infrequent words 에 대해서는 상대적으로 자주 등장했던 subwords 들의 정보를 이용하여 word vector 를 enriching 할 수 있습니다.
한국어를 위한 FastText: 초/중/종성 분리
FastText 는 하나의 단어에 대하여 벡터를 직접 학습하지 않습니다. 대신에 subwords 의 벡터들을 바탕으로 word 의 벡터를 추정합니다. 마치 Doc2Vec 에서 word vector 를 이용하여 document vector를 추정하는 것과 같습니다.
좀 더 자세히 말하자면 v(어디야)는 직접 학습되지 않습니다. 하지만 v(어디야)는 [v(어디), v(디야)]를 이용하여 추정됩니다. 즉 ‘어디야’라는 단어는 ‘어디’, ‘디야’라는 subwords 를 이용하여 추정되는 것입니다.
그런데, 이 경우에는 오탈자에 민감하게 됩니다. ‘어딛야’ 같은 경우에는 [v(어딛), v(딛야)]를 이용하기 때문에 [v(어디), v(디야)]와 겹치는 subwords 가 없어서 비슷한 단어로 인식되기가 어렵습니다.
이는 Edit distance 에서 언급한 것과 같습니다. 한국어의 오탈자는 초/중/종성에서 한군데 정도가 틀리기 때문에 자음/모음을 풀어서 FastText 를 학습하는게 좋습니다. 즉 어디야는 ‘ㅇㅓ-ㄷㅣ-ㅇㅑ-‘로 표현됩니다. 종성이 비어있을 경우에는 -으로 표시하였습니다. FastText 가 word 를 학습할 때 띄어쓰기를 기준으로 나누기 때문입니다.
아래는 초/중/종성이 완전한 한글을 제외한 다른 글자를 제거하며 음절을 초/중/종성으로 분리하는 코드입니다. 이를 이용하여 단어를 초/중/종성으로 나눠놓은 jamo_corpus 를 만들어서 skipgram_jamo_model 을 학습 해야 합니다.
학습에 이용하는 jamo_corpus 는 아래와 같습니다. 첫줄은 ‘크리스토퍼 놀란 에게 우리는 놀란다’ 입니다.
ㅋㅡ-ㄹㅣ-ㅅㅡ-ㅌㅗ-ㅍㅓ- ㄴㅗㄹㄹㅏㄴ ㅇㅔ-ㄱㅔ- ㅇㅜ-ㄹㅣ-ㄴㅡㄴ ㄴㅗㄹㄹㅏㄴㄷㅏ-
ㅇㅣㄴㅅㅔㅂㅅㅕㄴ ㅈㅓㅇㅁㅏㄹ ㅎㅡㅇㅁㅣ-ㅈㅣㄴㅈㅣㄴㅎㅏ-ㄱㅔ- ㅂㅘㅆㅇㅓㅆㄱㅗ-
ㄴㅗㄹㄹㅏㄴㅇㅣ-ㅁㅕㄴ ㅁㅜ-ㅈㅗ-ㄱㅓㄴ ㅂㅘ-ㅇㅑ- ㄷㅚㄴㄷㅏ- ㅇㅙ-ㄴㅑ-ㅎㅏ-
Package
FastText 는 Facebook Research 에서 공식으로 release 한 package 와 Gensim 에서 이를 다른 word embedding 방법들과 interface 를 통일한 package, 두 가지 버전이 자주 이용됩니다. 이번 포스트에서는 Facebook Research 의 official package 를 이용합니다. 설치는 pip install 로 가능합니다.
pip install fasttext
이는 C 코드의 실행 스크립스트를 python 에서 실행시키는 것입니다. input 과 output 으로 위에서 만든 텍스트 파일의 path 와 학습된 모델의 path 를 입력해야 합니다. 그 외의 default parameters 은 아래와 같습니다.
이처럼 모델을 학습하고 난 뒤에는 단어 간 유사도를 검색할 때에도 초/중/종성을 분리해야 합니다. 이 과정을 포함한 cosine_similarity 함수를 따로 만듭니다.
아래는 ‘어디야 라는 단어에 대하여 (Word2Vec, FastText) 모델을 이용한 단어 간 similarity 입니다. 어디야? 에서는 ? 가 제거되었기 때문에 similarity = 1.000 이 나타난 것이며, 대체로 형태가 비슷한 단어의 similarity 가 높아졌습니다. 예를 들어 ‘어디야’의 축약형인 ‘어댜’는 0.658 -> 0.886 으로 증가하였습니다. 이 경우에 similarity 에 영향을 크게 주는 subwords 가 ‘ㅇㅓ-ㄷ’ 라는 것도 알 수 있습니다.
어댜 (0.825, 0.886) | 어디야? (0.744, 1.000) | 어디여? (0.661, 0.909) |
어디여 (0.821, 0.909) | 어디야?? (0.719, 1.000) | 어댜? (0.658, 0.886) |
어디얌 (0.816, 0.934) | 어디에요 (0.705, 0.768) | 오디야? (0.655, 0.893) |
어디고 (0.778, 0.695) | 어딘뎅 (0.701, 0.865) | 어디고? (0.637, 0.695) |
어디양 (0.774, 0.921) | 어딘데? (0.696, 0.882) | 어디얌? (0.635, 0.934) |
어디니 (0.758, 0.873) | 어뎌 (0.693, 0.770) | 오디양 (0.634, 0.737) |
어디임 (0.753, 0.886) | 어딘데?? (0.673, 0.882) | 어딘뎅? (0.632, 0.865) |
어딘데 (0.751, 0.882) | 어디쯤이야 (0.672, 0.832) | 어디에여 (0.630, 0.792) |
어디냐 (0.748, 0.850) | 어디예요 (0.668, 0.777) | 오디얌 (0.628, 0.757) |
오디야 (0.745, 0.893) | 어디니? (0.662, 0.873) | 어디쯤이야? (0.627, 0.832) |
다른 예시로 Word2Vec 을 이용하여 단어 벡터를 학습한 뒤 ‘짜파게티’의 유사 단어를 검색하면 다음의 결과를 얻을 수 있습니다.
word2vec_model.most_similar('짜파게티')
[('비빔면', 0.9303897023200989),
('불닭볶음면', 0.9284998178482056),
('토스트', 0.9267774820327759),
('베이글', 0.9165289402008057),
('비빔국수', 0.9150125980377197),
('라볶이', 0.914039134979248),
('갈비찜', 0.9139703512191772),
('삼각김밥', 0.9129242897033691),
('부침개', 0.9123827219009399),
('라묜', 0.9121387004852295)]
하지만 typo 에 대해서는 유사어 검색이 되지 않습니다.
word2vec_model.most_similar('짭파게티')
# message ...
1231 all_words.add(self.vocab[word].index)
1232 else:
-> 1233 raise KeyError("word '%s' not in vocabulary" % word)
1234 if not mean:
1235 raise ValueError("cannot compute similarity with no input")
KeyError: "word '짭파게티' not in vocabulary"
그러나 FastText 는 ‘짭파게티’의 유사어도 검색이 가능합니다. ‘짭파게티’에 대한 단어 벡터를 얻을 수 있기 때문입니다. 그리고 ‘짜파게티’의 유사어들과 ‘짭파게티’의 similarity 도 매우 높습니다. 즉 FastText 는 단어의 형태적 유사성과 문맥을 모두 고려하는 word embedding 방법입니다.
FastText sim (짭파게티, word) | Word2Vec sim (짜파게티, word) |
---|---|
비빔면 (0.840) | 비빔면 (0.930) |
불닭볶음면 (0.860) | 불닭볶음면 (0.928) |
토스트 (0.788) | 토스트 (0.927) |
베이글 (0.688) | 베이글 (0.917) |
비빔국수 (0.847) | 비빔국수 (0.915) |
라볶이 (0.816) | 라볶이 (0.914) |
갈비찜 (0.740) | 갈비찜 (0.914) |
삼각김밥 (0.803) | 삼각김밥 (0.913) |
부침개 (0.759) | 부침개 (0.912) |
라묜 (0.713) | 라묜 (0.912) |
Reference
- Bojanowski, P., Grave, E., Joulin, A., & Mikolov, T. (2016). Enriching word vectors with subword information. arXiv preprint arXiv:1607.04606.