2013년 등장한 Word2Vec은 강력한 단어 임베딩 기법입니다. 의미적으로 유사한 단어를 벡터 공간 상에 가까운 위치에 배치시키도록 학습하는 신경망을 이용해서 성공적으로 단어를 임베딩시켰습니다. 유사한 단어가 유사한 위치에 배치된다는 특징 덕분에 여러 자연언어처리 기법에서 이미 널리 쓰이고 있는데요, 사실 이 기법에는 한가지 약점이 있습니다. 오직 단어의 형태로만 단어를 구분하기 때문에 형태가 같아도 의미가 전혀 다른 단어를 구분해내지 못한다는 것입니다.
예를 들어 한국어 단어 '밤'을 생각해봅시다. 해 진 이후의 시간대를 뜻하는 '밤'과 견과류의 일종인 '밤'이 같은 형태로 적히기 때문에 Word2Vec은 학습과정에서 두 단어를 동일한 단어로 간주하고 처리합니다. 실제로 두 단어는 다른 단어인데 말이죠. 이 때문에 동음이의어(사실 텍스트를 처리하는 입장에서는 동철이의어(철자가 같지만 다른 의미인 단어)라고 말하는게 맞겠네요)를 전혀 구분하지 못하고 둘을 같은 단어로 벡터 공간 상에 매핑합니다.
다행히도 이런 단어가 (영어에는) 매우 많지 않기 때문에 Word2Vec이 성공적으로 쓰일 수 있었지만, 좀더 발전된 자연언어처리를 위해서는 넘어야할 벽이겠죠. 다행히 2016년에 디리클레 프로세스(Dirichlet Process)를 이용해서 모호성을 해소하는 Word2Vec 기법이 제시되었습니다.
Bartunov, S., Kondrashkin, D., Osokin, A., & Vetrov, D. (2016, May). Breaking sticks and ambiguities with adaptive skip-gram. In Artificial Intelligence and Statistics (pp. 130-138).
위 논문인데요, 개인적으로 제목을 아주 잘 지었다고 생각합니다. Breaking sticks는 이 논문에서 사용된 디리클레 프로세스를 설명하는데에 자주 쓰이는 예시인 막대 부러뜨리기(Stick Breaking)를 나타내는 것이고, Breaking ambiguities는 이 기법이 모호성을 해소하는데 쓰일 수 있다는 걸 잘 드러내고 있죠. 아주 재밌게 지은 제목인것 같아요.
이 논문에서 소개된 AdaGram(Adaptive Skip-gram)의 예시 코드(https://github.com/sbos/AdaGram.jl)는 Julia라는 언어로 작성되어 있는데요, 제가 이 언어에 익숙하지 않은 관계로 실험을 돌려보는데에 지장이 있어서 연휴를 이용해서 c++로 포팅했습니다. (성능 향상은 덤이구요. 이에 대해서는 뒷부분에 소개하겠습니다.)
디리클레 프로세스에 대해서는 예전에 비모수 토픽 모델링 기법 중 하나인 HDP를 소개할 때 같이 설명한 적이 있습니다. 막대 부러뜨리기 과정에 대해서는 이 블로그에 잘 설명되어 있구요. 디리클레 프로세스의 주요 특징 중 하나는 모분포를 따르는 이산 분포를 잘 묘사할 수 있다는 것이고, 또 다른 특징은 비모수 분포(쉽게 말해 그 갯수가 사전에 정해져 있지 않다고 생각하시면 되겠습니다)라는 것입니다.
예를 들어 길이가 1인 빼빼로를 여러 사람이 적당히 나눠가져야 한다고 생각해봅시다. 사람이 3명으로 정해져 있다면야 세 조각으로 (공평하든 공평하지않든) 적당히 자를 수 있겠지만, 나눠 먹을 사람의 수가 정해져 있지 않은 경우 어떻게 나누면 좋을까요?
우리는 이론적으로 빼빼로가 0.00001이라도 남아있는 한 이 과정을 계속 반복할 수 있습니다. 물론 뒷사람들이 코딱지만큼 남은 빼빼로를 보고 기분나빠할순 있겠지만 그건 수학자가 신경쓸 바가 아닙니다.
[0, 1] 범위에서 임의의 수 r을 어떻게 뽑느냐에 따라서 사람들이 나눠먹는 빼빼로의 비중이 달라질 수 있습니다. 임의의 수 r로 1에 가까운 값이 더 많이 뽑힌다면, 앞쪽의 사람이 대부분의 빼빼로를 먹어치울테지요. 반면, r이 작은 값 위주로 뽑힌다면, 앞사람들이 조금만 먹고 뒷쪽으로 넘겨줄테니 뒷 사람들도 많은 빼빼로를 먹을 수 있을 겁니다. 이때 [0, 1]에서 임의의 실수를 뽑는 분포로 베타 분포 B(1, α)를 사용하는데요, 여기에 사용되는 파라미터 α가 바로 이를 조절합니다. α가 작을수록 전반적인 r값이 커지구요, 반대로 α가 클수록 전반적인 r값이 작아집니다.
이렇게 장황하게 디리클레 프로세스를 설명한 이유는, 우리가 데이터를 보기 전까지 한 단어 형태에 얼마나 다양한 의미가 포함되어 있을지 모르기 때문에, 이를 해결하기 위해 각 단어의 의미가 디리클레 프로세스를 따르도록 논문에서 설정하고 있기 때문입니다.
즉 각 단어 A, B, ... 에 대해
A: 의미A1, 의미A2, 의미A3...
B: 의미B1, 의미B2, 의미B3...
처럼 두고, 각각의 의미 단위에 대해서 Word2Vec 학습을 진행하는게 이 논문의 아이디어입니다.
https://ronxin.github.io/wevi/ 에서 Word2Vec 학습 과정의 시각화를 살펴볼 수 있습니다.
복잡해보이지만 의외로 간단한 구조입니다. 입력, 출력이 각각 V개가 있고, 가운데 은닉층 N개가 이를 이어주는 구조입니다. 위의 예시 그림에서는 V = 8, N = 5겠죠. "eat apple drink"라는 문장이 입력되었고, Window 크기를 1이라고 한다면(좌우 1개의 단어를 보겠다는 뜻입니다.) apple이라는 단어의 좌우 문맥은 eat와 drink가 됩니다. 따라서 apple이라는 단어를 입력했을때 eat이 나오도록 학습을 하고, 또 apple이 입력되었을때 drink가 나오도록 학습을 하죠. (skip-gram 기법) 혹은 반대로 문맥이 주어졌을 때 가운데의 단어를 예측하도록 학습을 할 수도 있구요. (cbow 기법)
학습시 모든 edge의 weight을 계산하려면, 그 연산량이 굉장히 많기 때문에 이를 줄이기 위해 Hierarchical Softmax나 Negative Sampling 기법을 사용하여 그 연산량을 줄입니다. Hierarchical Softmax는 간단히 설명하자면 단어의 출현빈도를 바탕으로 Huffman 트리를 구축하고, 그 트리의 leaf로 단어를 배치하여, 한 단어의 출현확률을 계산하는데 모든 노드를 추적할 필요 없이, 트리구조를 따르며 경로 상의 노드만 계산할 수 있도록 구조를 개량한 것입니다.
Negative Sampling의 경우는 모든 노드를 계산하는 대신, 문맥에 등장하는 단어 전부(positive sample) + 출현확률에 비례해 문맥에 출현하지 않는 단어 중 일부를 추출하여(negative sample) 이를 학습에 쓰는 것입니다. 이에 대해서는 https://shuuki4.wordpress.com/2016/01/27/word2vec-%EA%B4%80%EB%A0%A8-%EC%9D%B4%EB%A1%A0-%EC%A0%95%EB%A6%AC/ 블로그에 잘 설명되어 있으니 참고하시면 되겠습니다.
AdaGram에서는 위의 네트워크 구조에서 입력 쪽을 수정하여 다양한 의미를 반영할 수 있게 했습니다.
각 단어가 최대 2가지의 의미를 가질수 있다고 세팅하면 왼쪽과 같이 입력층의 노드를 2배로 설정해줍니다. 출력층은 그대로 두고요. 여기서 "eat apple drink"라는 문장을 학습할 경우에는 먼저 이 맥락에 속한 apple이 apple1에 가까울지 apple2에 가까울지 그 확률을 계산한 다음 그 확률에 비례해 (apple1, eat), (apple1, drink), (apple2, eat), (apple2, drink)를 학습합니다. 그리고 다시 apple의 apple1, apple2 분포 확률을 업데이트하구요. 복잡한 수식은 설명하지 않도록 하겠습니다. 해당 논문과 코드에 잘 드러나므로 굳이 여기서 다시 설명할 필요도 없을것 같구요!
원래 디리클레 프로세스 상에서는 무한한 개수의 빼빼로 조각을 다룰 수 있습니다만, 실제 계산 상에서는 메모리와 속도의 한계로 그런 무한한 빼빼로 조각을 허용해줄 수 없습니다. 따라서 보통 최대 T개의 조각을 허용하겠다고 maximum을 정해두고, 또한 각 조각의 크기가 threshold보다 작으면 이 조각은 무시하는 것으로 문턱값도 설정합니다.
AdaGram의 학습 과정은 다음과 같습니다.
2번과 4번이 AdaGram에서 추가된 부분입니다. 처음에는 모든 단어가 각자 하나씩의 의미만을 가지고 시작하지만, 학습을 반복하면서 어떤 단어 w가 기존의 맥락과 맞지 않는 새로운 맥락에서 등장하게 될 경우 이를 새로운 의미에 할당하게 됩니다. 이를 계속 반복하면서 결국 가중치 값들은 수렴하고 되구요.
도입부에 설명했듯이 원 코드가 Julia로 써져 있어서 돌려보기가 어려운 관계로 빠르게 Julia를 독학하고 Documentation을 펼쳐놓고 코드를 C++로 옮겼습니다. (다행히도 Julia가 직관적으로 쉽게 만들어진 언어라서 어려운 작업을 아니었습니다.) https://github.com/bab2min/AdaGram-Cpp 에 C++로 포팅된 코드를 공유해드리니 필요하신 분들은 사용하시면 되겠습니다.
처음에 C++로 옮겼을때는 그 성능이 Julia로 짜여진것과 별 차이가 없었는데요 (초당 7,060단어 처리) C++의 Eigen 라이브러리로 최적화를 실시하고, SSE2 옵션을 켜니 약 3배 (초당 30,110단어 처리) 향상되었고, AVX까지 켤 경우 4~5배 (초당 45,000단어 처리) 빨라지는 것을 확인했습니다! 오예! (Eigen 라이브러리의 최적화가 아주 좋네요.) 아직 멀티스레딩 부분을 제대로 개발하지 않아서, 병목이 걸리는 관계로 멀티코어에서의 성능 향상은 거의 없습니다만, 이 멀티스레딩도 구현한다면 10배 이상의 속도를 낼 수 있을 것으로 예상됩니다.
먼저 영어 텍스트를 가지고 실험을 돌려본 결과입니다. 위키백과 영문 판의 임의 문서 3000개를 가지고 M = 100 (단어의 벡터 차원수), T = 5 (한 단어가 가질수 있는 의미 최대 개수), alpha = 0.1 (디리클레 프로세스의 하이퍼 파라미터)로 돌렸습니다. 다음의 표들은 단어의 각 의미에 대해서 코사인 유사도가 가장 높은 상위 15개 단어를 추려서 보여주고 있습니다.
appl | |||||
---|---|---|---|---|---|
의미 0 | 0.630 | 의미 1 | 0.370 | ||
macintosh | 0 | 0.873 | cooki | 0 | 0.601 |
wozniak | 0 | 0.814 | pomegran | 0 | 0.587 |
iig | 0 | 0.807 | peach | 0 | 0.570 |
iie | 0 | 0.804 | cider | 0 | 0.551 |
iic | 0 | 0.797 | pepsi | 0 | 0.545 |
beo | 0 | 0.787 | candi | 0 | 0.541 |
messagepad | 0 | 0.776 | woodi | 0 | 0.539 |
amiga | 0 | 0.769 | grape | 0 | 0.531 |
iphon | 0 | 0.758 | sweet | 0 | 0.530 |
desktop | 0 | 0.758 | sneak | 0 | 0.528 |
visicalc | 0 | 0.756 | fruit | 0 | 0.527 |
microarchitectur | 0 | 0.756 | bundi | 0 | 0.522 |
amstrad | 0 | 0.755 | cocktail | 0 | 0.520 |
powerpc | 0 | 0.755 | stout | 0 | 0.520 |
chipset | 0 | 0.754 | pear | 0 | 0.519 |
stemming 후 돌린거라 단어 어미가 조금 잘리긴 했지만 단어 알아보는데에는 어려움이 없으시죠? 먼저 Apple이라는 단어를 보니 AdaGram이 총 2가지의 의미를 발견했습니다. 의미 0은 약 0.630의 비중을 가지고 있고, 의미 1은 약 0.370의 비중을 가지고 있었대요. 의미 0의 경우 MacIntosh, Wozniak, iPhone등의 단어가 나오는걸로 봐서 기업 Apple을 가리킨다는 것을 알 수 있죠. 의미 1은 cookie, peach, cider 등의 단어로 보건대 먹는 사과를 가리킨다는 것을 알수가 있구요.
tree | |||||
---|---|---|---|---|---|
의미 0 | 0.489 | 의미 1 | 0.469 | ||
b-tree | 0 | 0.850 | shrub | 0 | 0.762 |
avl | 0 | 0.846 | grass | 0 | 0.741 |
node | 0 | 0.832 | evergreen | 0 | 0.739 |
delet | 0 | 0.810 | conif | 0 | 0.714 |
search | 0 | 0.802 | pine | 0 | 0.713 |
rebalanc | 0 | 0.762 | mimosa | 0 | 0.701 |
subtre | 0 | 0.755 | woodland | 0 | 0.697 |
binari | 0 | 0.748 | vine | 0 | 0.691 |
insert | 0 | 0.739 | pinu | 0 | 0.689 |
leftmost | 0 | 0.720 | acacia | 0 | 0.689 |
root | 0 | 0.718 | oak | 0 | 0.689 |
rb | 0 | 0.717 | foliag | 0 | 0.687 |
unbalanc | 0 | 0.691 | fern | 0 | 0.678 |
lookup | 0 | 0.654 | quercu | 0 | 0.668 |
recurs | 0 | 0.654 | edibl | 0 | 0.664 |
tree도 2가지 의미가 있다고 하네요. 의미 0의 경우 컴퓨터 과학에서 말하는 트리, 의미 1은 식물 나무를 가리키고 있습니다.
base | ||||||||
---|---|---|---|---|---|---|---|---|
의미 0 | 0.290 | 의미 1 | 0.320 | 의미 2 | 0.391 | |||
batter | 0 | 0.779 | step-by-step | 0 | 0.773 | oper | 0 | 0.616 |
bat | 0 | 0.776 | gerald | 1 | 0.743 | classifi | 1 | 0.615 |
hit | 0 | 0.765 | modifi | 0 | 0.737 | aviat | 0 | 0.607 |
at-bat | 0 | 0.748 | higher-ord | 0 | 0.730 | depend | 1 | 0.599 |
infield | 0 | 0.742 | non-standard | 0 | 0.729 | system | 1 | 0.595 |
rbi | 0 | 0.741 | combin | 0 | 0.729 | air | 0 | 0.593 |
catcher | 0 | 0.740 | simpl | 0 | 0.727 | brest | 0 | 0.590 |
stolen | 0 | 0.728 | essenti | 0 | 0.725 | reorganis | 0 | 0.589 |
score | 0 | 0.724 | known | 1 | 0.720 | catalyst | 1 | 0.584 |
hitter | 0 | 0.718 | approach | 0 | 0.714 | ron | 1 | 0.581 |
fielder | 0 | 0.714 | franz | 1 | 0.712 | constabulari | 0 | 0.578 |
scorer | 0 | 0.713 | exampl | 0 | 0.712 | cr | 1 | 0.577 |
pitcher | 0 | 0.701 | relat | 0 | 0.712 | third-largest | 0 | 0.573 |
roquet | 0 | 0.692 | term | 1 | 0.711 | unit | 0 | 0.569 |
single-season | 0 | 0.688 | simplest | 0 | 0.710 | addit | 1 | 0.568 |
항상 사람이 생각하는것처럼 깔끔하게 결과가 나오지는 않더라구요. base의 경우 의미 0은 야구에 관한 단어들이라는 걸 확실히 알겠는데, 의미 1과 2는 정확하게 어떤 걸 가리키는지 쉽게 감이 잡히지는 않습니다.
한국어에 대해서도 돌려봐야겠죠? 세종 말뭉치 중 문어체 텍스트만 가지고, 어미, 조사, 특수 기호 등은 제거한 뒤 AdaGram을 돌려보았습니다. (M = 300, T = 5, alpha = 0.1)
열/VV | |||||
---|---|---|---|---|---|
의미 0 | 0.466 | 의미 1 | 0.534 | ||
개최/NNG | 0 | 0.601 | 닫/VV | 0 | 0.758 |
열리/VV | 0 | 0.565 | 열어젖히/VV | 0 | 0.647 |
창립/NNG | 0 | 0.562 | 닫히/VV | 0 | 0.637 |
심포지엄/NNG | 0 | 0.548 | 잠그/VV | 0 | 0.614 |
문학자/NNG | 0 | 0.503 | 쾅/MAG | 0 | 0.576 |
심포지움/NNG | 0 | 0.491 | 드르륵/MAG | 0 | 0.573 |
주역/NNG | 0 | 0.484 | 열리/VV | 1 | 0.567 |
기념/NNG | 0 | 0.483 | 덧문/NNG | 0 | 0.561 |
토론회/NNG | 0 | 0.479 | 여닫/VV | 0 | 0.554 |
제천/NNG | 0 | 0.477 | 벌컥/MAG | 0 | 0.548 |
학술회의/NNG | 0 | 0.476 | 노크/NNG | 0 | 0.546 |
세미나/NNG | 0 | 0.474 | 찰칵/MAG | 0 | 0.533 |
전당/NNG | 0 | 0.472 | 문틈/NNG | 0 | 0.532 |
학술/NNG | 0 | 0.469 | 유리문/NNG | 0 | 0.530 |
융성/NNG | 0 | 0.465 | 문/NNG | 0 | 0.522 |
공간/NNG | |||||
의미 0 | 0.648 | 의미 1 | 0.352 | ||
리이만/NNP | 0 | 0.680 | 후면/NNG | 0 | 0.693 |
존현/NNG | 0 | 0.636 | 존현/NNG | 0 | 0.670 |
강학/NNG | 0 | 0.595 | 강학/NNG | 0 | 0.655 |
유클리드/NNP | 0 | 0.529 | 누정/NNG | 0 | 0.642 |
주체자/NNG | 0 | 0.479 | 안채/NNG | 0 | 0.641 |
여백/NNG | 0 | 0.478 | 남향/NNG | 0 | 0.636 |
무한히/MAG | 0 | 0.471 | 행랑채/NNG | 0 | 0.631 |
위계/NNG | 0 | 0.465 | 내삼문/NNG | 0 | 0.623 |
선험/NNG | 0 | 0.461 | 벽체/NNG | 0 | 0.616 |
공간/NNG | 1 | 0.455 | 회랑/NNG | 0 | 0.611 |
유한/NNG | 0 | 0.452 | 안마당/NNG | 0 | 0.607 |
생활공간/NNG | 0 | 0.452 | 우물마루/NNG | 0 | 0.607 |
개조/NNG | 0 | 0.450 | 아담/XR | 0 | 0.606 |
다차원/NNG | 0 | 0.448 | 달아내/VV | 0 | 0.605 |
협소/XR | 0 | 0.446 | 사랑채/NNG | 0 | 0.604 |
이상/NNG | |||||
의미 0 | 0.562 | 의미 1 | 0.438 | ||
퍼센트/NNG | 0 | 0.679 | 일층/MAG | 0 | 0.581 |
밀리그램/NNB | 0 | 0.651 | 상대주의/NNG | 0 | 0.578 |
약/MM | 0 | 0.615 | 유토피아/NNG | 0 | 0.577 |
그램/NNB | 0 | 0.607 | 선험/NNG | 0 | 0.574 |
이하/NNG | 0 | 0.605 | 상통/NNG | 0 | 0.566 |
미만/NNG | 0 | 0.590 | 기대/VV | 1 | 0.558 |
비만/NNG | 0 | 0.589 | 구극/NNG | 0 | 0.556 |
발병/NNG | 0 | 0.578 | 한갓/MAG | 0 | 0.553 |
평균/NNG | 0 | 0.566 | 순전/XR | 0 | 0.551 |
폐경/NNG | 0 | 0.564 | 콩트/NNP | 0 | 0.549 |
절반/NNG | 0 | 0.555 | 소산/NNG | 0 | 0.540 |
당뇨병/NNG | 0 | 0.551 | 심미/NNG | 0 | 0.540 |
이환/NNG | 0 | 0.545 | 원초/NNG | 0 | 0.539 |
섭취량/NNG | 0 | 0.545 | 과학주의/NNG | 0 | 0.532 |
뇌종양/NNG | 0 | 0.542 | 즉물/NNG | 0 | 0.531 |
전기/NNG | |||||
의미 0 | 0.363 | 의미 1 | 0.636 | ||
회로/NNG | 0 | 0.638 | 후기/NNG | 0 | 0.714 |
기기/NNG | 0 | 0.591 | 통일신라/NNP | 0 | 0.650 |
콘덴서/NNG | 0 | 0.585 | 하타다/NNP | 0 | 0.646 |
연소/NNG | 0 | 0.582 | 이행기/NNG | 0 | 0.630 |
금속/NNG | 0 | 0.580 | 중세/NNG | 0 | 0.625 |
연접/NNG | 0 | 0.576 | 구석기/NNG | 0 | 0.619 |
전극/NNG | 0 | 0.573 | 고려/NNP | 0 | 0.618 |
방전/NNG | 0 | 0.564 | 사대부/NNG | 0 | 0.611 |
수소/NNG | 0 | 0.561 | 통일신라기/NNG | 0 | 0.607 |
연료/NNG | 0 | 0.560 | 고려사상/NNG | 0 | 0.603 |
변압기/NNG | 0 | 0.559 | 중기/NNG | 0 | 0.602 |
직류/NNG | 0 | 0.559 | 이규보/NNP | 0 | 0.600 |
증기/NNG | 0 | 0.557 | 고려사/NNG | 0 | 0.598 |
이온/NNG | 0 | 0.555 | 근세/NNG | 0 | 0.598 |
스위치/NNG | 0 | 0.549 | 실학파/NNG | 0 | 0.597 |
전기(eletronic)와 전기(first-half)처럼 동음이의어를 구분해낸 경우도 있고, 열다(축제를 개최하다)와 열다(문을 열다) 처럼 같은 단어지만 맥락에 따라 다르게 쓰이는 갈래뜻을 구분한 경우도 있네요. 물론 코퍼스의 한계인지, 구분해내지 못한 동음이의어도 많았습니다. 눈, 배, 밤과 같은 단어는 자주 쓰이는 단어 쪽만 학습이 되어 신체 부위 눈, 신체 부위 배, 해가 진 이후의 시간대 밤만 잡아냈고, 다른 동음이의어는 잡지 못했습니다. 이 경우 alpha값을 높임으로써 더 세부적으로 의미 분별이 가능하도록 학습할 수는 있습니다만, 그 경우 또 같은 의미의 단어가 여러 의미로 잘못 갈라지는 경우가 생길 수도 있겠죠... 말뭉치에 따라 최적의 파라미터를 설정하는게 중요할듯 합니다.
위의 결과에서 확인하셨다시피 AdaGram은 비지도학습 기반으로 텍스트 데이터에서 단어의 세부의미를 분별해내는 데 강점을 보이고 있습니다. 각 단어의 의미별 벡터값과 그 비중이 주어지기 때문에, 거꾸로 새로운 텍스트를 주면 거기에 포함된 각 단어들이 어떤 의미에 속하는지도 계산이 가능하구요. 논문에 따르면 Word Sense Induction 작업에 있어서 기존까지 제시된 기법 중 가장 높은 성능을 보이고 있다고 합니다. 말 그대로 Word2vec의 막강함에 단어 의미 변별 기능이 추가되어 더욱 막강한 기법이 된것이라고 할 수 있겠습니다.
다만 한계 역시 존재합니다. 단어 분포만을 가지고 의미를 학습해내는 것이기 때문에 입력되는 데이터가 편향되어 있을 경우 잘못된 의미 분포를 학습하게 된다는 (어찌보면 당연한) 문제가 있구요, 또한 사람이 인지하는 (사전적) 의미와 컴퓨터가 학습해낸 의미 간의 괴리가 존재할 수 있다는 점이 있습니다. 때에 따라서는 사전적인 의미에 따라 의미를 나누고 싶을 수 있는데(Word Sense Disambiguation), 이런 경우에는 아직 적용이 불가하다는 한계가 있습니다.
하지만 역으로 생각해보면, 인간이 사전을 편찬해 낼때 그 단어의 용례를 직접 찾아보며 의미를 정의하는데, 이 과정에 AdaGram이 큰 도움이 될 수도 있습니다. 방대한 텍스트 자료를 자동으로 학습하여 해당 단어가 가지고 있는 의미를 보여줌으로써 사람이 놓칠 가능성이 있는 의미를 잡아주거나, 그 용례를 제시하는데 쓰일 수도 있을듯합니다.
Python용 토픽 모델링 패키지 - tomotopy 개발 (12) | 2019.05.19 |
---|---|
[Kiwi] 지능형 한국어 형태소 분석기 0.6버전 업데이트 (1) | 2018.12.09 |
[Tensorflow] 문자 인식용 신경망 Python3 코드 (2) | 2018.11.14 |
Word2Vec을 이용한 한국어 관련어 네트워크 보기 (0) | 2018.09.17 |
Kneser-Ney 언어 모형을 활용한 한국어 초성체 해석기 개발 (0) | 2018.06.17 |
간편한 토픽 모델링 툴 Tomoto Gui (25) | 2018.06.09 |
댓글 영역