fastText는 Facebook의 AI Research lab에서 만든 단어 임베딩 및 텍스트 분류 학습을위한 라이브러리입니다. 이 모델을 사용하면 단어에 대한 벡터 표현을 얻기 위해 비지도 학습 또는지도 학습 알고리즘을 만들 수 있습니다.
fastText에 대한 위키에 있는 간단한 정의입니다.
fastText is a library for learning of word embeddings and text classification created by Facebook‘s AI Research (FAIR) lab[3][4][5][6]. The model allows to create an unsupervised learning or supervised learning algorithm for obtaining vector representations for words. Facebook makes available pretrained models for 294 languages.[7] fastText uses a neural network for word embedding.
Algorithm of fasttext is based on these two papers:[8]
- Enriching Word Vectors with Subword Information , Piotr Bojanowski, Edouard Grave, Armand Joulin and Tomas Mikolov, 2016
- Bag of Tricks for Efficient Text Classification, Armand Joulin, Edouard Grave, Piotr Bojanowski, Tomas Mikolov, 2016
gensim 패키지를 활용하면 간단히 활용할 수 있습니다.
word2vec을 사용하는 것보다 더 좋은 성능을 얻을 수 있고 또 입력 시에 나온 오타도 어느 정도 해결할 수 있다고 합니다. 그 이유는 char-ngram 방식을 사용하기 때문입니다.
fastText attempts to solve this by treating each word as the aggregation of its subwords. For the sake of simplicity and language-independence, subwords are taken to be the character ngrams of the word. The vector for a word is simply taken to be the sum of all vectors of its component char-ngrams.
https://radimrehurek.com/gensim/auto_examples/tutorials/run_fasttext.html
다만 계산량이 word2vec을 사용할 때보다 많아서 시간이 더 걸립니다. 아래의 공식 문서를 참고하시기 바랍니다.
Training time for fastText is significantly higher than the Gensim version of Word2Vec (
https://radimrehurek.com/gensim/auto_examples/tutorials/run_fasttext.html15min 42s
vs6min 42s
on text8, 17 mil tokens, 5 epochs, and a vector size of 100).
Test Code
테스트를 위해 인터넷에 공개 되어 있는 2018년도 데이터를 다운 받았습니다. 파일 포맷은 csv형태로 되어 있습니다.
def read_data(filename):
with open(filename, 'r',encoding='utf-8') as f:
data = [line.split(',t') for line in f.read().splitlines()]
data = data[1:] # header 제외 #
return data
train_data = read_data('./data/2018_simpan_newgroup.csv')
테스트를 위해 받은 텍스트 파일의 형태는 다음과 같습니다.
[['0|이 건 심판청구는 처분청의 직권경정으로 인하여 심리일 현재 청구의 대상이 되는 처분이 존재하지 아니하므로 부적법한 청구로 판단됨|0'],
['1|처분청의 2016년 제2기 부가가치세 경정결정 후 청구인이 심판청구를 제기하여 2017.10.24. 이미 기각결정을 받았으므로 이 건 심판청구는 동일한 처분에 대하여 중복하여 제기된 점, 청구인은 당초 심판청구와 동일한 내용의 경정청구를 하였고, 그에 대한 처분청의 거부통지는 민원회신에 불과한 것이어서 심판청구의 대상이 되는 처분으로 볼 수 없는 점 등에 비추어 이 건 심판청구는 부적법한 청구로 판단됨|0'],
['2|처분청이 청구주장을 받아들여 이 건 과세처분을 직권으로 감액경정하였으므로 이 건 심판청구는 심리일 현재 불복 대상이 되는 처분이 존재하지 아니하여 부적법한 청구에 해당하는 것으로 판단됨|0'],
['3|쟁점건물은 종교인과 일반인을 상대로 종교서적 등을 판매하는 매장으로 사용되는 것으로 나타나고, 달리 종교용도로 직접 사용되었다고 인정할 만한 사실이 확인되지 아니하므로 처분청이 종교목적으로 직접 사용하지 아니한 것으로 보아 이 건 재산세 등을 부과한 처분은 잘못이 없다고 판단됨.|1']...
리스트 형태로 데이터가 들어오고 2372문장을 테스트로 사용합니다.
해당 문장에는 여러가지 특수기호가 있기 때문에 적절히 전처리를 해줍니다.
전처리 후에 konlpy.Okt()를 활용하여 각 문장을 형태소 단위로 나눠줍니다.
def tokenize(doc):
s = doc[0].split('|')
# 이부분에 특수문자 제거 등의 전처리를 해주시면 됩니다.
return ['/'.join(t) for t in pos_tagger.pos(s, norm=True, stem=True)]
tokens = [tokenize(row) for row in train_data]
[['이/Noun','건/Noun','심판/Noun','청구/Noun','는/Josa','처분/Noun','청/Noun','의/Josa','직권/Noun','경정/Noun','으로/Josa','인하다/Adjective','심리/Noun','일/Noun','현재/Noun','청구/Noun','의/Josa','대상/Noun','이/Josa','되다/Verb','처분/Noun','이/Josa','존재/Noun','하다/Verb','아니다/Adjective','부/Noun','적법하다/Adjective','청구/Noun','로/Josa','판단/Noun','되다/Verb']]
위와 같은 형태로 분리됩니다.
model = gensim.models.fasttext.FastText(size=100)
model.build_vocab(tokens)
model = gensim.models.fasttext.FastText(size=100)
model.build_vocab(tokens)
model.train(tokens, window=5, epochs=model.epochs, total_examples=model.corpus_count)
model.alpha -= 0.002
model.min_alpha = model.alpha
다음과 같이 수행합니다. 필요에 따라서 다양한 옵션을 사용하여 훈련을 진행하시면 됩니다. gensim 사이트에 가시면 이에 대한 내용이 설명되어 있습니다.
부동산을 입력했더니 아래와 같은 결과를 얻었습니다.
model.wv.similar_by_word('부동산/Noun')
[('취득/Noun', 0.9003169536590576),
('가액/Noun', 0.8994468450546265),
('정산/Noun', 0.8911253213882446),
('환산/Noun', 0.8888809084892273),
('연말정산/Noun', 0.8887563943862915),
('자산/Noun', 0.8879978656768799),
('재산/Noun', 0.8878995180130005),
('전액/Noun', 0.8871012330055237),
('분산/Noun', 0.8870201110839844),
('거액/Noun', 0.8869702816009521)]
해당 모델은 100차원으로 되어 있는데 그것은 위에 모델을 선언할 때에 size=100으로 설정했기 때문입니다.
model.wv['부동산/Noun']
array([ 0.23454253, -0.7865744 , -0.46801254, -0.11220518, 0.49738216,
0.51051146, 0.28836748, 0.24520665, -0.2823485 , 0.12481502,
0.31313908, 0.09823137, 0.9331261 , -0.63185096, 0.79251087,
0.07525934, 0.5575937 , 0.6052933 , -0.36211282, 0.43174762,
0.0608188 , 0.18941545, 0.35179955, -0.43175125, -0.48578402,
0.7635253 , 0.19132383, 0.83176637, -0.4213232 , 0.2916827 ,
0.06576332, 0.03166943, -0.5215866 , -0.9714561 , -0.43011758,
0.14605877, 0.77329254, 0.18222107, 0.5664433 , 0.971345 ,
0.65927994, 0.3893743 , -0.09935822, 0.2923206 , 0.12915374,
-0.14681472, 0.05491441, -0.27698728, 0.01709399, 0.26082256,
0.07673132, -0.227397 , -0.15840591, -0.10292988, -0.6830837 ,
-0.23510128, 0.6165825 , 0.11153345, -0.4144705 , 0.09626016,
-0.11291514, 0.8256664 , -0.49922696, 0.26332954, -0.35839406,
0.6881266 , 0.6718516 , 0.0867641 , 0.24843903, 0.6920707 ,
0.37919027, -0.27192804, 0.5573388 , -1.0683383 , -0.45235977,
-0.5060888 , -0.693835 , -0.33676928, 0.5679421 , -0.4563976 ,
0.4198934 , -0.06000128, 0.6072741 , -1.1808567 , 0.09339973,
-0.4496738 , 0.02826241, -0.01418105, 0.01322413, -0.16594794,
-0.8327613 , -0.02719802, 0.5258091 , -0.6739192 , -0.7354652 ,
-0.6937513 , -0.28029326, -0.36118436, -0.41617483, 0.8403618 ],
dtype=float32)