오늘 정리해본 내용은 그러한 Text 처리에 있어, 전처리 Word Embedding 기법 중 간단하면서도 뛰어난 효율을 보이는 Word2Vec 에 대한 내용이다.
- Word2Vec 알고리즘에 대하여?
- 정의는 Learning4j 에서의 정의를 인용하였다.
- "데이터의 양이 충분하면 Word2vec은 단어의 의미를 꽤 정확하게 파악합니다. 그리고 이를 이용하면 단어의 뜻 뿐만 아니라 여러 단어의 관계를 알아냅니다. 예를 들어 단어의 관계를 이용해 ‘남자’:’소년’ = ‘여자’:x 같은 관계식을 주면 x=’소녀’라는 답을 구할 수 있습니다. 단어 뿐만 아니라 더 큰 단위의 텍스트인 문장이나 문서를 분류하는데에도 Word2vec을 사용합니다. 예를 들어 문서를 군집화한 뒤에 결과를 이용하면 검색 엔진에서 문서의 분야별 검색(과학, 법률, 경제 등)이나 문장의 감정 분석, 추천 시스템을 만들 수 있습니다."
- Word2Vec 은 텍스트를 처리하는 인공 신경망이며, 두 개의 층으로 구성되어 있다. 심층 신경망은 아니지만, 심층 신경망의 전처리 단계로 많이 쓰인다.
- 인공 신경망 작동 방식은 크게 위 2가지로 나뉜다.
- Word2Vec 알고리즘에 대하여 좀더 자세한 내용을 알고싶어 하는 분들은 이 글을 참고하면 될것 같다.
- 자연어 기계학습의 혁명적 진화! (나프다에 기재된 글)
- http://www.moreagile.net/2014/11/word2vec.html
오늘 내가 정리하는 내용은 Word2Vec 을 실제 구현하는 2가지 큰 방법( Python vs Scala )에 대하여 비교 분석한 내용이다.
- 구현방법 1
- Python 계열로 구현해 보기.
- Gensim 으로 구현하면, Word2Vec 구현하기가 너무 쉽다.
- 게다가 다음과 같은 장점이 있다.
- Python 계열은 우선 설치도 간단하다.
- Anaconda 를 설치하자. 이거 하나로 모든게 해결된다.
- https://docs.continuum.io/anaconda/install
- Word2Vec 에서 생각해볼 수 있는 거의 모든 내용이, 매우 쉽게 사용가능한, 잘 정리된 메소드로, 거의 다 만들어져 있다.
- 사용예, 셈플도 매우 많다.
- 한글의 경우 좀 쓸만한 형태소 분석기들은 죄다, R이나 Python 계열이다. 때문에, 자연어 처리에 있어, Python으로의 선택은 이런 모듈들과 궁합이 좋아, 우선 가장 안전한 접근 일 수 있다.
- 특히, Konlp 나 Konlpy 가 좋다.
- 요즘 Python 은 GPU 를 쓰기도 좋고, CPython 등으로 인하여 속도도 매우 빠르다.
- 물론 다음과 같은 단점도 있다.
- 초 대용량으로 가는 경우 답이 없다.
- 기본적으로 아직 까지는 Single 머신으로 돌리는 것이 Default 이다. 물론 병렬로 돌리기 위한 몇몇 마이너한 방법들이 있으나, 아직 까지 그 길은 최적화 되어 있지 못하다.
- 기본적인 코드들은 인터넷에 널려 있다.
- 참고로 너무 쉽고, 매우 짧다.
- 살짝 드려다 보자면...
- 학습을 위해 준비한 데이타.
- 나무위키 데이타, 위키피디아 데이타, 그리고 모사의 블로그 크롤링 데이타 등이 사용되었다.
- 데이타를 조사제거하고, 의미없이 너무 긴단어 제거하고, 몇몇 전처리를 해 줘야 하는데, 이 역시 Konlpy 등을 이용하면, 어렵지 않게 전처리 가능하다. (물론 Data가 매우 매우 클 때는 Python 으로는 매우 오래 걸리는 작업이긴 하지만..)
- 대표적인 Vector Algebra 인 King:Man = Queen:Woman 예제를 돌려 보자.
- 참고로 이 예제는 한글에서는 잘 맞지 않다. 그 이유는 왕 이라는 단어가, 한글에서는 동음 이의어가 존재하기 때문이다. (왕서방, 왕짜장, 왕 좋다. 킹 왕짱 등등 때문에 그렇다.)
- 그래서 중국:북경 = 대한민국:서울 의 Algebra로 실험을 해 보았다.
중국 - 북경 = X - 서울 요렇게 표기하는 것도 가능할것이다.답은 대한민국이 나와야 한다. X 를 구하기 위해 위 식을 아래처럼 다시 바꾸어 보았다.
X = 중국 - 북경 + 서울 - Algebra 수식 부분 수행코드는 아래와 같다.
- 코드는 허무하게시리 간단하다.
- 위 결과에서처럼 대한민국과 한국이 1, 2등 이다. 놀랍지 아니한가??? 나는 처음 이 결과 봤을때 깜짝 놀랐었는데...
- 구현방법2
- Scala On Spark 로 구현해 보기.
- Spark 로 구현해 보자. Spark ML 안에 Word2Vec 이 있으니, 망설일 필요가 없다.
- Spark 는 Mesos Cluster 로 구동하였고, 모든 데이타는 Hadoop 내 공유 저장소에 존재한다.
- 하지만, 막상 구현을 해보니, 일단 학습하고, 저장하고, 로드하는 Spark 가 제공하는 Example 외에, 실무에서 활용할 만한 Sample 이 너무 너무 없다.게다가, 메뉴얼이 매우 빈약하다.
- 여튼 코드는 간단하다. Sample을 찾긴 힘들지만...
- Algebra 예제는 2시간을 Goggling 해도 제대로 수행된는 Code를 찾기가 힘들다. 몇몇 Code가 stackoverflow 사이트 등에 메뉴얼 Base, 답변 글 형태로 Code가 존재하긴 하는데 작동하지 않는다. (아래에서 좀더 상세 설명)
- API Document 를 보고 Algebra 구현을 시도해 보았다.
- 1차 시도
- 메뉴얼만 보면, 위처럼 하면 되야만 한다.
- 하지만 위처럼 하면, model.getVectors() 쪽에서 인자가 없다고 에러가 난다. 분명 API Doc 에는 getVectors() 가 인자 없는 메소드 인데 말이다.
- () 를 하지 말고 그냥 가져오면 되긴 한다.
- val w2v_map = model.getVectors # 메뉴얼상의 getVectors() 메소드는 존재하진 않는다.
- 소스를 까보면 getVectors 가 Map[String, Array[Float]] 형태의 자료 구조이다. 즉, 뒤에서 다시 언급하겠지만, 그냥 Map 이라고 생각하고 getVectors(key:String) 으로 변수를 끄집어 낼수는 있지만, getVectors()(key:String)는 에러가 난다.
- 위에서 getVectors()를 getVectors 로 고치면 위 에러는 해결 되지만, 사칙연산이 안되는 새로운 에러가 발생한다. 이는 뒤에서 다시 언급...
- 2차 시도.
- 메뉴얼을 보면 이렇게 해도 되야 될거 같은데, 일단, 에러...
- Vectors 를 사칙연산 가능하게 breeze Vector로 형변환 해주면 되긴 한다. 맨 뒤에서 다시 언급...
- IDE 로 가 보았다. (개인적으로 서버에서 Scala 코딩은 zepplin 이나 sbt 로 바로 코딩 하고 확인 하는걸 선호 하는데... 이런 경우는 IDE에서의 확인이 필요하다.)
- 확인 결과 getVectors 는 메소드가 아니었다. 공식 API Document 에는 getter method 처럼 되어 있음에도 말이다...
- Github 의 소스 코드를 직접 확인해 보니 아래와 같다.
- getVectors 의 형이 요런식이다. 확실히 메소드가 아니다.TT
- 3차시도
- getVectors 자체에 args(N)을 인자로 주고, vector algebra 연산을 해보았다.
- 역시나.... algebra 연산에서 에러가 난다. Python 이 아니니까...
- linear Algebra 를 위한 breeze Vector 로 형변환을 하고 시도하였다. 성공!
- findSynonmys 안에 넣을때는 다시 dense 로 형변환이 필요하다. Python 이 얼마나 편한지... 비교 되는 부분이다.
- Algebra 를 수행한 결과는 아래와 같다. Python 의 경우와 Data Set 이 살짝 달라서 결과도 약간 다르다.(시점 차이로..) 여튼 요런식으로 결과가 나온다.
최종 결론은 다음과 같다.
- Word2Vec 을 구현하기 위해
- Gensim + Python
- 구현을 하기 위한 가장 손쉽고 빠른 접근 방법이다.
- 왠만한 모든 가공 처리가 일사천리로 진행 가능하다..막히는 부분이 거의 없다.
- 데이타 전처리 부 또한 걸출한 Open 모듈들이 매우 많다.
- 단, 데이타 크기가 수십기가가 넘어가면, 단일머신에서는 고사양이 아닌 경우 급격히 느려 진다.
- GPU 를 사용하여 한번 더 성능을 점프 업 할 수 있으나, 이 역시 한계가 있다.
- 최고사양의 GPU 머신이라 할지라도 수백기가가 넘는 텍스트는 불가능 한 것으로 보여진다. (각종 변태 같은 다른 방법은 고려하지 않음.)
- Spark + Scala (or Python) : (실험에 사용한 환경은 Spark + Mesos + Hadoop)
- Spark 의 MLlib 를 이용하는 것이므로, Scala 가 아닌 Python 으로도 가능하다.
- Python on Spark 인 경우 형태소 분리 등 전처리는 Python 의 모듈을 그대로 활용 할 수 있으며, Python 형태소 분리 등의 전처리를 Gensim 과 달리 병렬 처리 구현 하기 수훨해 진다.
- 단일 머신에서 돌릴때, Gensim 에 비하여 매우 느리다. 일단, Spark 는 분산처리를 위한 소잡는 칼이기 때문일 것으로 위안을 삼아 본다.
- gensim 고사양 1대 vs Spark 고사양 5대를 비교 했는데, 극한의 Spark 튜닝을 하기 전에는 기본 비교 시 gensim 이 좀 더 빨랐다.
- 하지만 여러가지 튜닝을 해주니, 중사양 Spark 20대가 고사양 Gensim 1대보다 5배 정도 빠른 결과를 내 주었다. (이 부분은 별도로 Posting 을 해보겠다. 속도를 끌어 올리는데 매우 많은 실험과 극한의 config 설정 튜닝이 필요하였다.)
- 무엇보다, Gensim 고성능 서버 1대에서 절대 돌지 않는 양 (수백 GB 이상)의 데이타가 Spark Dev 클러스터 중사양 20대에서는 그런데로의 속도로 도는 것이 가능하였다. ( 이 부분도 별도로 Posting 해 보도록 하겠다. 각종 Memory 관련 이슈가 등장하는데, 이 부분을 해결하는것 또 한 매우 많은 실험과 극한의 설정 튜닝이 필요하였다. 속도도 속도 지만, 대 용량을 Spark ML 로 돌릴때에는 Memory 의 제약으로 인하여 각종 Trick 이 필요한데. 이 부분의 작업은 매우 지난한 작업 이었다.)
- 수 ~ 수십 TB 단위의 데이타로의 실험을 해보진 않았는데, 돌지 않을지도 모른다는 생각이 든다, 실험결과 Spark Submit 수행시 주는 옵션인 Driver Memory가 학습할 Text 데이타의 크기에 비례하여 많이 필요하였기 때문이다.
- 결론
- 작은 양의 데이타는 Gensim + Python Win!
- 중간 이상 크기의 데이타는 어쩔 수 없이 Spark!
- 그 이상 크기의 데이타는, 곧 훨씬 더 큰 데이타와 훨씬 고사양의 훨씬 많은 Spark 노드에서 다시 한번 테스트 해볼 예정이지만, 위 2-7의 가설이 맞다면, 어쩔 수 없이... deeplearning4j! (물론 deeplearning4j 도 내부에서 Spark를 이용하고는 있지만..)
- 어느정도 이상의 데이타는 도메인을 나누어 학습을 시켜도 될 듯 싶다.
- 어느정도 이상의 데이타는 표본이 전수를 대표하는 성격을 가질 것이기 때문이다. 굳이 이세상의 모든 데이타를 하나의 model 로 합쳐서 학습할 필요가 없다는 생각이 든다.
- 백과 사전 몇개를 학습하여, 일반어 간의 거리를 관리하는 모델을 하나 만들어 놓고, 이후부터는 특정 도메인 별로 데이타를 수집하여 별개로 학습해도 될 것 같다.
- 예를 들어, 네이버 요리 카테고리 블로그를 따로 쭉 학습해서 요리 단어에 대한 학습을 하고 나면, 요리 박사가 될 수 있다.
- 상품명을 가지고 크롤링 하여 쭉 학습을 하고 나면 또 상품 박사가 될 수 있다.
- 결국은 중간 Classification 분류 대표명이 중요하다. 중분류 대표명이 있다면, 세상의 모든 단어에 해당하는 백과사전 Model 과 특정 분야에 특화된 Domain 사전 간에 중분류 대표 단어를 매개로 거리 Mapping 을 통하여, 다양한 조합 쿼리가 가능하기 때문이다. (단, 방향성 연산은 이 시나리오가 통하지 않는다. 복수 모델을 조합할때는 similarity 연산만 유용하다.)
- 백과사전 Model 로 부터 다양한 형태의 자연어 대화 질문을 Classification 대표 질문으로 분류해주고, Classfication 대표 질문으로 부터 해당 Class 에 맞는 Domain 특화 model 에 질의 하여 정확한 전문 용어를 끄집어 내는 것이 가능 할 것이기 때문이다.