😄 NLP - 한국어 영화 리뷰 감정분석
Ai한국어 데이터에 대해서 텍스트 분석을 해보자. 아래 데이터는 한국어 분석 학습을 위해 다양한 방식으로 사용되고 있다. 여기서는 한글 분석을 위해 Konlpy를 사용하고, 텐서플로 케라스를 이용해 모델을 만들도록 하겠다. 데이터셋 : Naver sentiment movie corpus (다운로드 링크 : https://github.com/e9t/nsmc/) NSMC 약어까지 사용할 정도로 많이들 사용하는 데이터 인듯 싶다. 데이터 설명 영화 리뷰 중 영화당 100개의 리뷰이고 총 200,000개의 리뷰(train:15만, test:5만) 1점 ~ 10점 까지의 평점 중에서 중립적인 평점(5점~8점)을 제외하고 분류를 하였다. 부정 : 1점 ~ 4점 긍정 : 9점 ~ 10점 칼람정보: id, document, label id: 리뷰 아이디 document: 리뷰 내용 label: 레이블 (0: negative, 1: positive) 각 파일에 대한 리뷰 갯수 ratings.txt: All 20만 ratings_test.txt: 5만 ratings_train.txt: 15만 모든 리뷰텍스트는 140자 이내이고, 각 감정 분류는 동일하게 샘플링 된다.(i.e., random guess yields 50% accuracy) 10만개의 부정적인 리뷰 10만개의 긍정적인 리뷰 중립적인 리뷰는 제외 데이터 준비 다운로드 받은 데이터를 pandas를 이용해 읽어보자. 필드 구문이 탭으로 되어 있기 때문에 로 구분자를 지정해주어야 한다. 데이터 전처리 데이터를 학습 시키기 위해 전처리를 진행해야 하는데, Konlpy를 이용해 을 하도록 하자. 영어의 경우 주어진 단어의 빈도만을 사용해서 처리해도 크게 문제는 없지만 한국어는 영어와는 달리 띄어쓰기로 의미를 구분짓기에는 한계가 있고, 리뷰 특성상 맞춤법이나 띄어쓰기가 제대로 되어있지 않는 경우가 있을 수 있기 때문에 정확한 분류를 위해서는 Konlpy를 이용하는 것이 좋다. >Konlpy는 띄어쓰기 알고리즘과 정규화를 이용해서 맞춤법이 틀린 문장도 어느 정도 고쳐주면서 형태소 분석과 품사를 태깅해주는 여러 클래스를 제공하고 있다.^^! [('흔들리는', 'Verb'), ('꽃', 'Noun'), ('들', 'Suffix'), ('속', 'Noun'), ('에서', 'Josa'), ('네', 'Noun'), ('샴푸', 'Noun'), ('향', 'Noun'), ('이', 'Josa'), ('느껴진거야', 'Verb')] 테스트 삼아 간단한 문장을 넣고 확인 해보면 이런 형태로 분리를 해주는 것을 알수 있다. 토크나이즈 함수를 만들어 사용하도록 한다. >norm은 정규화, stem은 근어로 표시하기를 나타냄 리뷰가 null인 경우 위 위 함수에서 오류가 발생할 수 있으니 사전에 null값 확인해보고 빈민자열로 대체하자! 이제 학습데이터와 테스트데이터를 분석하여 저장해두자. 분석결과가 끝났으면 다음과 같은 형태로 데이터가 변형 되었을 것이다. (['아/Exclamation', '더빙/Noun', '../Punctuation', '진짜/Noun', '짜증나다/Adjective', '목소리/Noun'], 0) (['굳다/Adjective', 'ㅋ/KoreanParticle'], 1) 15만 학습데이터에 분리된 토큰 개수를 살펴보자. 토큰개수: 2159921 이제 이데이터를 가지고 nltk를 이용해 전처리를 한다. 클래스는 문서를 편리하게 탐색할 수 있는 다양한 기능을 제공하고 있다. 여기서는 매서드를 이용해 데이터가 가장 자주 사용되는 단어를 가져올 때 사용하겠다. 2159921 49894 [('./Punctuation', 67778), ('영화/Noun', 50818), ('하다/Verb', 41209), ('이/Josa', 38540), ('보다/Verb', 38538), ('의/Josa', 30188), ('../Punctuation', 29055), ('가/Josa', 26627), ('에/Josa', 26468), ('을/Josa', 23118)] 데이터 탐색 출력빈도가 높은 상위 토큰 10개를 matplotlib을 이용해 그래프로 확인해보자. !png 모델을 만들기 위해 백터화를 해야 하는데, 자주 사용되는 토큰 10000개를 사용해 데이터를 백터화 하자.(원 핫 인코딩 대신 CountVectorization을 사용) 문서 집합에서 단어 토큰을 생성하고 각 단어의 수를 세어 BOW(Bag of Words) 인코딩한 벡터를 만드는 역할을 한다. 시간이 오래 걸리므로 100개만 해보자... 이 과정은 데이터 양이 큰 만큼 시간이 오래 걸리기 때문에 이 작업을 반복하지 않도록 태깅을 마친 후에는 json파일로 저장하는 것도 좋은 방법이다. 문서에서 상위로 선택된 단어들중 몇개가 포함이 되는지를 알아야 한다. 이렇게 하면 x축 데이터에는 단어들이 빈도수 정보?, y축에는 분류 결과를 깔끔하게 정리할 수 있다. 이제 데이터를 float로 형 변환 시켜주면 데이터 전처리 과정은 끝~~ 데이터 모델링 텐서플로 케라스를 이용해 모델을 만들어 보자. 레이어 구성은 두개의 Danse층은 64개의 유닛을 가지고 활성함수는 relu를 사용하고, 마지막층은 sigmoid 활성화 함수를 사용해 긍정 리뷰일 확률을 출력할 것이다. 손실 함수는 binary_crossentropy, RMSprop 옵티마이저를 통해 경사하강법을 진행 배치 사이즈는 512, 에포크는 10번으로 학습 자, 이제 학습을 시켜 모델을 만들어 보자! 먼가 있어 보이는 진행률 상태를 볼 수 있다.:) WARNING:tensorflow:From C:\Users\DESKTOP\.conda\envs\nlp\lib\site-packages\tensorflow\python\ops\mathops.py:3066: toint32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead. Epoch 1/10 150000/150000 [==============================] - 17s 115us/sample - loss: 0.5611 - binaryaccuracy: 0.6948s - loss: 0.6134 - binaryaccuracy: - ETA: 21s - loss: 0.6108 - binaryaccuracy: 0. - ETA: 20s - - ETA: 15s - loss: 0.5957 - binaryaccuracy: 0.67 - ETA: 15s - loss: 0.5955 - binaryaccuracy: 0.67 - ETA: 15s - loss: 0.5951 - binaryaccura - ETA: 14s - loss: 0.5930 - binaryaccuracy: 0. - ETA: 14s - loss: 0.5923 - binaryaccuracy: - ETA: 13s - loss: 0.5911 - binaryaccura - ETA: 8s - loss: 0.5785 - binaryaccuracy: 0. - ETA: 8s - loss: 0.5771 - binaryaccuracy: 0.68 - ETA: 8s - loss: 0.5764 - binaryaccuracy: - ETA: 7s - loss: 0.5751 - binaryaccura - ETA: 6s - loss: 0.5727 - binaryaccuracy: - ETA: 6s - loss: 0.5713 - binaryaccuracy: 0.6 - ETA: 5s - loss: 0.5708 - binarya - ETA: 4s - loss: 0.5685 - binaryaccuracy: - ETA: 4s - loss: 0.5676 - binaryaccuracy: 0.691 - ETA: 4s - loss: 0.5674 - binarya - ETA: 2s - loss: 0.5652 - binaryaccuracy: 0.692 - ETA: 2s - loss: 0.5651 - binaryaccurac - ETA: 1s - loss: 0.5639 - binaryaccuracy - ETA: 1s - loss: 0.5628 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5627 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5626 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5623 - binaryaccuracy: 0.694 - ETA: 1s - loss: 0.5623 - binaryaccuracy: - ETA: 0s - loss: 0.5615 - binaryaccuracy: 0.69 - ETA: 0s - loss: 0.5612 - binaryaccuracy: 0.694 - ETA: 0s - loss: 0.5613 - binaryaccuracy: 0.6 Epoch 2/10 150000/150000 [==============================] - 12s 83us/sample - loss: 0.5313 - binaryaccuracy: 0.71254s - loss: 0.5221 - binaryac - ETA: 15s - loss: 0.5345 - binaryaccuracy: - ETA: 16s - loss: 0.5377 - binaryaccuracy - ETA: 14s - loss - ETA: 13s - lo - ETA: 7s - loss: 0.5341 - binaryaccuracy: 0.71 - ETA: 7s - - ETA: 4s - loss: 0.5340 - binaryaccur - ETA: 3s - loss: 0.5335 - binaryaccuracy: 0.71 - ETA: 3s - loss: 0.5335 - binary - ETA: 2s - loss: 0.5325 - binary_accuracy: 0 - ETA: 1s - loss: 0. Epoch 3/10 150000/150000 [==============================] - 13s 86us/sample - loss: 0.5236 - binaryaccuracy: 0.7170s - loss: 0.5284 - binaryaccuracy - ETA: 10s - los - ETA: 9s - loss: 0.5255 - binaryaccurac - ETA: 9s - loss: 0.5250 - binaryaccuracy: 0. - ETA: 9s - loss: 0.5255 - binaryaccuracy: 0.716 - ETA: 9s - loss: 0.5254 - binary - ETA: 8s - loss: 0.5247 - binaryacc - ETA: 7s - loss: 0.5251 - binaryaccuracy: 0.7 - ETA: 7s - loss: 0.5252 - binaryaccuracy: 0.71 - ETA: 7s - loss: 0.5249 - binaryaccuracy: 0.716 - ETA: 7s - loss: 0.5248 - binaryaccuracy: 0.7 - ETA: 6s - loss: 0.5245 - binaryaccuracy: - ETA: 6s - loss: 0.5246 - binaryaccuracy: 0 - ETA: 5s - loss: 0.5247 - bina - ETA: 5s - loss: 0.5249 - b - ETA: 3s - loss: 0.5246 - binaryac - ETA: 2s - loss: 0.5246 - binaryac - ETA: 2s - loss: 0.5244 - binaryaccuracy: 0.71 - ETA: 2s - loss: 0.5244 - binaryaccuracy: - ETA: 1s - loss: 0.5242 - binaryaccur - ETA: 1s - loss: 0.5243 - binaryaccuracy: 0 - ETA: 0s - loss: 0.5243 - binaryaccuracy - ETA: 0s - loss: 0.5239 - binaryaccuracy: 0.7 - ETA: 0s - loss: 0.5237 - binaryaccuracy: 0.7 - ETA: 0s - loss: 0.5236 - binaryaccuracy: 0.71 Epoch 4/10 150000/150000 [==============================] - 13s 89us/sample - loss: 0.5179 - binaryaccuracy: 0.7219s - loss: 0.5201 - binaryaccuracy: 0 - ETA: 8s - loss: 0.5211 - binarya - ETA: 9s - loss: 0.5208 - binaryaccuracy: 0.721 - ETA: 8s - loss: 0.5201 - binaryaccuracy: 0.72 - ETA: 8s - loss: 0.5210 - - ETA: 7s - loss: 0.5180 - binaryac - ETA: 7s - loss: 0.5187 - binar - ETA: 6s - loss: 0.5186 - binaryaccura - ETA: 6s - loss: 0.5183 - binaryaccuracy: 0.7 - ETA: 6s - loss: 0.5182 - binaryac - ETA: 5s - loss: 0.5179 - binaryaccuracy: 0.723 - ETA: 5s - loss: 0.5179 - binaryaccurac - ETA: 4s - loss: 0.5187 - binaryaccuracy: 0.722 - ETA: 4s - lo Epoch 5/10 150000/150000 [==============================] - 13s 87us/sample - loss: 0.5132 - binaryaccuracy: 0.72531s - loss: 0.5093 - binaryaccuracy: 0.72 - ETA: 11s - ETA: 9s - loss: 0.5156 - binaryaccuracy: 0.7 - ETA: 9s - loss: 0.5163 - binaryaccuracy: - ETA: 9s - loss: 0.5154 - binaryaccurac - ETA: 8s - loss: 0.5164 - bin - ETA: 7s - loss: 0.5156 - binary - ETA: 6s - loss: 0.5168 - binaryaccuracy: 0. - ETA: 6s - loss: 0.5163 - binaryaccuracy: 0.72 - ETA: 6s - loss: 0.5160 - binaryaccur - ETA: 5s - loss: 0.5154 - binaryaccuracy: 0.723 - ETA: 5s - loss: 0.5153 - binarya - ETA: 4s - los - ETA: 2s - loss: 0.5136 - binaryacc - ETA: 1s - loss: 0.5135 - binaryaccuracy: 0.724 - ETA: 1s - loss: 0.5134 - binaryaccuracy - ETA: 1s - loss: 0.5132 - binaryac Epoch 6/10 150000/150000 [==============================] - 13s 87us/sample - loss: 0.5094 - binaryaccuracy: 0.72850s - loss: 0.4971 - binaryaccuracy: 0.73 - ETA: 11s - loss: 0.5000 - binary - ETA: 9s - loss: 0.5043 - binaryaccuracy: - ETA: 10s - loss: 0.5081 - binaryaccuracy: - ETA: 9s - loss: 0.5086 - binarya - ETA: 9s - loss: 0.5106 - binaryaccuracy: 0 - ETA: 9s - loss: 0.5111 - binaryaccuracy: 0.72 - ETA: 9s - loss: 0.5112 - binaryaccuracy: - ETA: 9s - loss: 0.5122 - binaryaccuracy: 0.724 - ETA: 9s - loss: 0.5122 - binaryaccuracy: - ETA: 8s - loss: 0.5114 - binaryaccuracy: 0. - ETA: 8s - loss: 0.5115 - binaryaccuracy: 0. - ETA: 8s - loss: 0.5118 - binaryaccuracy: 0 - ETA: 8s - loss: 0.5111 - binaryaccuracy: 0.725 - ETA: 8s - loss: 0.5114 - - ETA: 6s - loss: 0.5099 - binaryaccuracy - ETA: 6s - loss: 0.5108 - binaryaccuracy - ETA: 5s - loss: 0.5100 - binaryaccuracy: 0.726 - ETA: 5s - loss: 0.5102 - binaryacc - ETA: 4s - loss: 0.5102 - binaryaccurac - ETA: 4s - loss: 0.5102 - binaryaccuracy: 0.7 - ETA: 4s - l - ETA: 1s - loss: 0.5102 - binaryaccu - ETA: 1s - loss: 0.5098 - bi - ETA: 0s - loss: 0.5094 - binary_accuracy: 0.72 Epoch 7/10 150000/150000 [==============================] - 12s 79us/sample - loss: 0.5064 - binaryaccuracy: 0.73061s - loss: 0.5050 - binaryaccura - ETA: 9s - loss: 0.5096 - binaryaccuracy: 0.72 - ETA: 9s - loss: 0.5090 - binaryaccuracy - ETA: 8s - loss: 0.5083 - binaryaccuracy: 0.7 - ETA: 8s - loss: 0.5082 - binaryaccuracy: 0.7 - ETA: 8s - loss: 0.5083 - binaryaccur - ETA: 7s - loss: 0.5085 - binaryaccuracy: 0 - ETA: 6s - loss: 0.5079 - binarya - ETA: 5s - loss: 0.5079 - binaryaccuracy: 0. - ETA: 5s - loss: 0.5080 - binaryaccuracy: - ETA: 5s - loss: 0.5078 - binaryaccuracy - ETA: 4s - loss: 0.5081 - binaryaccuracy: 0 - ETA: 4s - loss: 0.5080 - binaryaccuracy: 0.730 - ETA: 4s - loss: 0.5080 - binaryaccuracy: 0.730 - ETA: 4s - loss: 0.5078 - binaryaccuracy: - ETA: 4s - loss: 0.5072 - binaryaccuracy: 0 - ETA: 3s - loss: 0.5072 - binary - ETA: 2s - lo - ETA: 0s - loss: 0.5064 - binary_accuracy: 0.730 Epoch 8/10 150000/150000 [==============================] - 13s 85us/sample - loss: 0.5037 - binaryaccuracy: 0.73210s - loss: 0.5053 - binaryacc - ETA: 9s - loss: 0.5045 - binaryaccuracy: - ETA: 9s - loss: 0.5024 - binaryaccu - ETA: 8s - loss: 0.5013 - binaryaccu - ETA: 8s - loss: 0.5014 - binaryaccuracy: 0.73 - ETA: 8s - loss: 0.5007 - binar - ETA: 6s - loss: 0.5013 - binarya - ETA: 6s - loss: 0.5016 - binaryaccuracy: - ETA: 6s - loss: 0.5019 - binaryaccuracy: 0.73 - ETA: 5s - loss: 0.5019 - binaryaccuracy: 0 - ETA: 5s - loss: 0.5023 - binaryaccuracy: 0.73 - ETA: 5s - loss: 0.5021 - binaryaccuracy: 0.73 - ETA: 5s - l - ETA: 3s - loss: 0.5027 - ETA: 2s - loss: 0.5036 - binaryaccuracy: - ETA: 2s - loss: 0.5033 - binaryaccuracy: 0.732 - ETA: 2s - loss: 0.5033 - binaryaccuracy: 0.73 - ETA: 2s - loss: 0.5034 - binaryaccur - ETA: 1s - loss: 0.5035 - binaryaccuracy: - ETA: 0s - loss: 0.5033 - binaryaccuracy: - ETA: 0s - loss: 0.5035 - binary_acc Epoch 9/10 150000/150000 [==============================] - 13s 85us/sample - loss: 0.5015 - binaryaccuracy: 0.7337s - loss: 0.5023 - binaryaccu - ETA: 13s - loss: 0.4956 - binaryaccuracy: 0. - ETA: 14s - loss: 0.4949 - binaryaccu - ETA: 15s - loss: 0.4938 - binaryaccuracy: 0. - ETA: 15s - loss: 0.4977 - binaryaccuracy: 0.73 - ETA: 15s - loss: 0.4981 - binaryaccuracy: 0. - ETA: 14s - loss: - ETA: 12s - loss: 0.4989 - binaryaccuracy - ETA: 12s - loss: 0.4992 - binaryaccuracy - ETA: 11s - loss: 0.4990 - binary - ETA: 10s - loss: 0.4982 - binaryaccuracy: 0.73 - ETA: 10s - loss: 0.4982 - binar - ETA: 9s - loss: 0.4997 - binaryaccuracy: 0. - ETA: 9s - loss: 0.4997 - binaryaccuracy: 0 - ETA: 8s - loss: 0.4996 - binaryaccuracy: 0.7 - ETA: 8s - loss: 0.4997 - binary - ETA: 7s - loss: 0.5011 - bina - ETA: 6s - loss: 0.5014 - binaryaccuracy: 0.7 - ETA: 6s - loss: 0.5011 - binary - ETA: 4s - loss: 0.5016 - binaryaccuracy: 0.733 - ETA: 4s - loss: 0.5017 - binaryaccurac - ETA: 4s - loss: 0.5018 - binaryac - ETA: 2s - loss: 0.5022 - binaryaccuracy: 0.7 - ETA: 2s - loss: 0.5023 - binaryaccuracy: 0.73 - ETA: 2s - loss: 0.5021 - binaryaccuracy: - ETA: 1s - loss: 0.5018 - binaryaccuracy: 0 - ETA: 1s - loss: 0.5019 - binaryaccuracy - ETA: 1s - loss: 0.5018 - binaryaccuracy: 0.73 - ETA: 1s - loss: 0.5018 - binaryaccuracy: 0.7 - ETA: 1s - loss: 0.5017 - binary_a Epoch 10/10 150000/150000 [==============================] - 14s 91us/sample - loss: 0.4995 - binaryaccuracy: 0.73620s - loss: 0.4948 - binaryaccuracy: - ETA: 14s - loss: 0.4965 - binaryaccura - ETA: 14s - loss: 0.4980 - binaryaccu - ETA: 12s - loss: 0.4973 - binaryaccuracy - ETA: 12s - loss: 0.5014 - binar - ETA: 12s - loss: 0.4992 - binaryaccuracy - ETA: 12s - loss: 0.4996 - binaryaccuracy: - ETA: 12s - loss: 0.5001 - binaryaccuracy: 0.73 - ETA: 12s - loss: 0.5003 - binaryaccura - ETA: 11s - loss: 0.5011 - b - ETA: 9s - loss: 0.5027 - binaryaccuracy: - ETA: 9s - loss: 0.5026 - binaryaccuracy - ETA: 8s - loss: 0.5013 - binaryaccur - ETA: 8s - loss: - ETA: 6s - loss: 0.5013 - binary - ETA: 5s - loss: 0.5006 - binaryac - ETA: 4s - loss: 0.4997 - binaryaccurac - ETA: 4s - loss: 0.5001 - binaryac - ETA: 3s - loss: 0.5001 - b - ETA: 1s - loss: 0.4989 - binaryaccuracy: 0.736 - ETA: 1s - loss: 0.4989 - binaryaccuracy: 0.736 - ETA: 1s - loss: 0.4988 - binaryaccuracy: 0.736 - ETA: 1s - loss: 0.4989 - binaryaccurac - ETA: 1s - loss: 0.4991 - binaryaccuracy: 0. - ETA: 0s - loss: 0.4993 - binaryaccura - ETA: 0s - loss: 0.4995 - binary_accuracy: 0.73 모델 평가 학습데이터를 이용해 모델 학습이 끝났다면 테스트 데이터를 가지고 모델을 평가해보자. 50000/50000 [==============================] - 12s 234us/sample - loss: 0.5198 - binaryaccuracy: 0.7184s - loss: 0.5197 - b - ETA: 11s - loss: 0.5199 - binaryaccuracy: 0. - ETA: 1s - loss: 0.5198 - b [0.5197769028568268, 0.71842] >여기서는 100건으로 했기때문에 좀 낮은 71%의 정확도가 나왔다. 아마 사용한 토큰수를 100개가 아닌 10000개로 했다면 85%정도의 정확도를 확인할 수 있을 것이다. 팁으로 힘들게 만든 모델을 아래와 같이 저장해두고 나중에 로드해서 사용할수 있으니 꼭 알아두자. 결과 예측하기 이제 리뷰 문자열을 받아 바로 결과를 예측하는 함수를 만들어 보자 데이터 형태를 맞추기 위해 np.expand_dims 매서드를 이용해 array의 축을 확장 시켜주어야 한다. 최종 확률이 0.5 이상이면 긍정, 그렇지 않으면 부정이라고 예측을 하겠다. 대략 테스트를 먼저 해보고... ['아주/Noun', '재미/Noun', '있다/Adjective'] 0.9102853536605835 테스트한 로직을 함수화해서 사용하자. 재미 정말 없어요 ==> 부정 (93%) 이제 리뷰텍스트 만으로 긍정인지 혹은 부정인지를 어느정도 판단 할 수 있게 되었다. 지금까지 영화리뷰 데이터를 통해서 감정분석을 해보았는데 상품, 게임, 음식등의 사용자 의견이 담긴 데이터를 잘 모아서 활용한다면 다양한 곳에 활용할 수 있을 것이다.
2020년 05월 24일22분NLP - Bag of words, n-gram
Ai자연어 처리(natural language processing)는 인간의 언어 현상을 기계적으로 분석해서 컴퓨터가 이해할 수 있는 형태로 만드는 자연 언어 이해 혹은 그러한 형태를 다시 인간이 이해할 수 있는 언어로 표현하는 제반 기술을 의미한다. (위키피디아) 간단하게 말하면, 자연어의 의미를 분석하여 컴퓨터가 처리할 수 있도록 하는 일 이라고 생각하면 될 것 같다. 텍스트 기계학습 모델을 만들기 위해서는 데이터를 모델에 맞게 변형시켜 주어야 한다. 알고리즘에서 텍스트를 그대로 받아들일수 없기 때문에 받아들일 수 있는 어떤 숫자값으로 변환을 해주어야 한다. 하지만 텍스트는 일단 언어가 제각기 다르기 떄문에 텍스트 자체를 어떻게 숫자화 할지 부터 시작해야한다. 그럼 어떤 방법들이 있는지 살펴보자. BOW(bag of words) !nlp-1 BOW(Bag of words)는 텍스트 데이터를 표현하는 방법 중 하나로 가장 간단하지만 효과적이라 기계학습에서 널리 쓰이는 방법이다. BOW는 텍스트의 구조와 상관없이 단어들을 담는 가방(Bag)으로 생각하면 된다. 세 문장에서 나타나는 단아들을 모으고 세 문장을 각각 binary vector로 표현하는 것이 BOW이다. 각각의 문장을 binary vector로 표현하면 다음과 같다. | the | game | is | fun | interesting | not | funny | |-----|------|----|-----|-----|------|------| | 1 | 1 | 1 | 1 | 0 | 0 | 0 | >The game is fun : [1, 1, 1, 1, 0, 0, 0] | the | game | is | fun | interesting | not | funny | |-----|------|----|-----|-----|------|------| | 1 | 1 | 1 | 0 | 1 | 0 | 0 | >The game is interesting : [1, 1, 1, 0, 1, 0, 0] | the | game | is | fun | interesting | not | funny | |-----|------|----|-----|-----|------|------| | 1 | 1 | 1 | 0 | 0 | 1 | 1 | >The game is not funny : [1, 1, 1, 0, 0, 1, 1] 만약 이 가방에 들어있지 않는 단어가 포함된 문장이 있어도 BOW는 그 없는 단어는 제외하고 있는 단어만을 가지고 벡터로 만들 것이다. 그럼 이 수치로 표현된 값을 어떻게 활용할까? 머신러닝 모델에 입력값으로 사용할 수도 있디만 단순히 계산만으로 문장간의 유사도(Sentence similarity)를 알 수도 있다. 과 문장의 유사도를 구해보자. | 문장 | 벡터값 | |------|-------| | the game is fun | [, , , 1, 0, 0, 0] | | The game is interesting | [, , , 0, 1, 0, 0] | >유사도 = (1x1) + (1x1) + (1x1) + (1x0) + (0x1) + (0x0) + (0x0) = 3 또한 수치로 표한된 값을 이용해 기계학습 모델에 입력값으로 사용할 수가 있다. 문장에 대한 감성분석을 해주는 모델이 있다면, 벡터화된 값을 모델에 입력값으로 사용할 수 있고 모델은 우리가 원하는 또는 의 결과를 출력해 줄 수 있다. !bow 하지만 BOW는 몇 가지 단점이 있다. Sparsity 실제 사전에는 100만개가 넘는 단어들이 있을 수도 있다. 그렇게 되면 벡터의 차원이 100만개가 넘어가기 때문에 실제 문장하나를 표현할 때 대부분의 값이 0이고 그외의 값들은 상당히 적을 것이다. 결국 학습량이 많아지고 컴퓨터 자원도 상당히 많이 사용하게 된다. (the game is fun [1,1,1,1,0,0,0,0,0,0,0,,,,,,0,0,0,0,0]) 빈번한 단어는 더 많은 힘을 가진다. 많이 출현한 단어는 힘이 세진다. 만약 의미없는 단어들이 많이 사용 되었다면 우리가 원하는 결과를 얻기는 어려울 것이다. Out of vocabulary 오타, 줄임말 등의 단어들이 포함되면 굉장히 난감해진다.^^; 단어의 순서가 무시됨 단어의 출현 횟수만 셀수 있고 단어의 순서는 완전히 무시 된다. 단어의 순서가 무시된다는 것은 다른 의미를 가진 문장이 동일한 결과로 해석될 수 있다는 것이다. 전혀 반대의 의미를 가진 두 문장을 보자. !nlp-1 두 문장은 의미가 전혀 반대이지만 BOW를 이용해 처리한다면, 동일한 결과를 반환하게 될 것이다. 이런 단점을 보완하기 위해 좀더 개선된 n-gram이란 것이 있다. BOW는 하나의 토큰을 사용하지만 n-gram은 n개의 토큰을 사용하여 어느정도 단어의 순서를 반영 결과에 반영해 준다. N-Gram BOW를 조금 더 개선하여 단어 하나만을 보는 것이 아니라 주변의 n개 단어를 뭉쳐서 보는 것이다. 뭉쳐진 n개의 단어들을 gram이라고 한다. 단어 개수에 따라 부르는 명칭이 다른데 2개의 단어를 묶어서 사용하면 , 3개면 이라고 부른다. (1-gram은 uni-gram이라고 한다.) 다음 문장을 bi-gram를 사용하여 처리 한다면, "home run" 과 "run home" >bag of words : [home, run] , [run, home] >bi-gram : [home run], [run home] BOW를 사용한다면 두 문장은 같은 백터의 값을 갖게 되겠지만 bi-gram을 사용하면 2개를 뭉쳐서 사용하므로 어느정도의 순서가 보장되는 효과를 볼수 있게 되어 다른 결과 값을 가지게 될 것이다. 이런 특성을 이용해 n-gram은 다음 단어 예측하거나 어떤 단어를 입력 했을때 오타를 발견하고 다른 단어를 추천해 주는데 활용할 수 있다. 텍스트 전처리 (Preprocessing) !nlp-1 BOW나 n-gram이나 모두 많이 쓰이지만 가장 중요한 것은 단어의 전처리가 확실해야 한다는 것이다. 이 글에서는 설명을 위해 간단한 문장만을 사용하여 크게 신경을 쓸 필요는 없겠지만, 자연어 처리를 하다보면 다양한 케이스의 문장들을 접하게 될 것이며 이런 문장들을 토큰화하고 불필요한 단어들은 제거하고 같은 의미의 단어들은 치환하는 등의 고단한 작업 들을 해야 할 것이다. 하지만 다행인 것은 이런 전처리 작업들을 편하게 할 수 있도록 도와주는 좋은 라이브러리들이 있다. 다음 포스팅에서는 전처리에 대해 자세히 살펴보도록 하겠다...
2019년 08월 07일9분TF - 텍스트 분류
Ai지난번에 TF-IDF 및 Word2Vec을 이용해 텍스트를 분류해 보았다. 이번에는 텐서플로를 이용하여 텍스트를 분류 해보자. 여기에서는 인터넷 영화 데이터베이스(Internet Movie Database)에서 수집한 50,000개의 영화 리뷰 텍스트를 담은 IMDB 데이터셋을 사용하겠다. 25,000개 리뷰는 훈련용으로, 25,000개는 테스트용으로 나뉘어져 있고, 훈련 세트와 테스트 세트의 클래스는 균형이 잡혀 있다. 즉 긍정적인 리뷰와 부정적인 리뷰의 개수가 동일하다. 데이터는 Keras에서 제공하는 datasets를 이용하여 다운로드 하자. Text Classification https://www.tensorflow.org/tutorials/keras/basictextclassification?hl=ko 1.13.1 1.16.2 만약 numpy 버전이 1.16.2 이상의 버전이라면 삭제 후 버전을 지정하여 설치를 해야 한다. (imdb.load_data를 하는 도중 오류 발생) > conda install numpy=1.16.2 데이터 준비 keras dataset에 있는 imdb를 사용한다. train과 test 데이터 셋은 각각 25,000개 이며 데이터는 review 자체로 구성 labels는 0또는 1값으로 긍정 부정을 나타냄 > test데이터를 이용하여 해당 Review가 영화에 대해 긍정 또는 부정적인지를 예측해 본다. 훈련 샘플: 25000, 레이블: 25000 실제 데이터를 살펴보면 문자가 아닌 숫자 값이 리스트로 들어있다. [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32] 이렇게 되어 있는 이유는 추가적인 dictionary에 각 숫자와 단어가 매칭되어 있기 때문이다. 또한 아래와 같이 각각의 데이터 길이도 다른것을 확인 할 수 있다. (218, 189) 하지만 실제로 ML모델에 넣어줄때는 입력 길이는 모두 같아야 한다. 따라서 입력길이를 모두 동일하게 해주는 작업이 필요하다. 먼저 데이터를 보다 자세히 확인해보기 위해 각 데이터의 숫자를 단어로 치환해 보자. word_index word_index.items() pad, start, unknown, unused 값을 나타내기 위해 각 value에 3을 더하고 비어있게 되는 0~3에 각각을 할당한다. 실제로 필요한 dictionary는 숫자가 key이고, 단어가 value인 dictionary이기 때문에 reversewordindex라는 dictionary를 구성하고 숫자로 이루어진 입력데이터를 단어로 치환해 주며 문장으로 출력하는 decode_review 함수를 만든다. decode example 앞서 추가해주었던 , 등이 추가되어 보여지는 것을 확인할 수 있다. 데이터 전치리 위에서 언급했던 각 데이터의 길이가 상이한 것을 처리한다. keras에서 제공하는 preprocessing 함수를 이용하여 모든 데이터를 최대길이로 늘려주면서 빈공간에는 위에서 dictionary에 추가적으로 넣어주었던 pad값을 이용한다. 신경망에 주입하기 전에 텐서로 변환되어야 하는데, 변환하는 방법에는 몇 가지가 있다. 원-핫 인코딩(one-hot encoding) : 정수 배열을 0과 1로 이루어진 벡터로 변환한다. 예를 들어 배열 [3, 5]을 인덱스 3과 5만 1이고 나머지는 모두 0인 10,000차원 벡터로 변환할 수 있다. 그다음 실수 벡터 데이터를 다룰 수 있는 층-Dense 층-을 신경망의 첫 번째 층으로 사용한다. 이 방법은 numwords * numreviews 크기의 행렬이 필요하기 때문에 메모리를 많이 사용하게 된다는 단점이 있다. 다른 방법으로는, 정수 배열의 길이가 모두 같도록 패딩(padding)을 추가해 maxlength * numreviews 크기의 정수 텐서를 만드는 방법이다. 이런 형태의 텐서를 다룰 수 있는 임베딩(embedding) 층을 신경망의 첫 번째 층으로 사용할 수 있다. 여기서는 두 번째 방식을 사용해보자. 영화 리뷰의 길이가 같아야 하므로 pad_sequences 함수를 사용해 길이를 맞추자. 이 작업을 통해 변경된 것을 확인해 보면, 1. traindata, testdata 길이가 동일 2. 배열내 모든 데이터가 256 3. 데이터 형태는 맨 뒤에 0값, 즉 pad값이 포함되어 있음 (256, 256) [ 1 14 22 16 43 530 973 1622 1385 65 458 4468 66 3941 4 173 36 256 5 25 100 43 838 112 50 670 2 9 35 480 284 5 150 4 172 112 167 2 336 385 39 4 172 4536 1111 17 546 38 13 447 4 192 50 16 6 147 2025 19 14 22 4 1920 4613 469 4 22 71 87 12 16 43 530 38 76 15 13 1247 4 22 17 515 17 12 16 626 18 2 5 62 386 12 8 316 8 106 5 4 2223 5244 16 480 66 3785 33 4 130 12 16 38 619 5 25 124 51 36 135 48 25 1415 33 6 22 12 215 28 77 52 5 14 407 16 82 2 8 4 107 117 5952 15 256 4 2 7 3766 5 723 36 71 43 530 476 26 400 317 46 7 4 2 1029 13 104 88 4 381 15 297 98 32 2071 56 26 141 6 194 7486 18 4 226 22 21 134 476 26 480 5 144 30 5535 18 51 36 28 224 92 25 104 4 226 65 16 38 1334 88 12 16 283 5 16 4472 113 103 32 15 16 5345 19 178 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 모델 구성하기 이제 text classification을 수행할 ML모델을 만들어보자. vocab_size는 영화리뷰에 사용되는 단어의 개수이다. 실제로 위에서 단어와 숫자를 매칭하는 dictionary의 사이즈 보다 크지만, 해당 데이터에서는 10000개의 단어 이내로 리뷰가 작성되었다. 각 레이어에 대한 설명은 다음과 같다. 1. embedding : 숫자로 인코딩 되어있는 각 단어를 사용하며 각 단어 인덱스에 대한 벡터를 찾는다. 이러한 데이터는 추후 모델이 핫습하는데 사용된다. 2. GlobalAveragePooling1D : 각 예시에 대해 sequence 차원을 평균하여 고정된 길이의 벡터를 출력한다. 3. Dense : 첫번째 Dense 레이어를 통해서 고정길이로 출력된 vector값을 통해 16개의 hidden unit을 가진 fully-connected layer를 통과시킨다. 이후 두번째 Dense 레이어는 단일 출력 노드를 가지고 시그모이드 활성화 함수를 사용함으로써 결과에 대해 0~1 사이의 값을 가지도록 한다. _ Layer (type) Output Shape Param # ================================================================= embedding_2 (Embedding) (None, None, 16) 160000 _ globalaveragepooling1d_2 ( (None, 16) 0 _ dense_4 (Dense) (None, 16) 272 _ dense_5 (Dense) (None, 1) 17 ================================================================= Total params: 160,289 Trainable params: 160,289 Non-trainable params: 0 _ 모델 구성에 마지막으로 loss function과 optimizer를 설정한다. 모델 훈련하기 모델을 훈련하기에 앞서 10000개의 데이터를 분리하여 validation set을 만들자. 모델이 새롭게 접하는 데이터에 대한 accuracy와 loss등을 확인하기 위함이다. Train on 15000 samples, validate on 10000 samples Epoch 1/40 15000/15000 [==============================] - 1s 99us/sample - loss: 0.6925 - acc: 0.5343 - valloss: 0.6918 - valacc: 0.5347 Epoch 2/40 15000/15000 [==============================] - 1s 69us/sample - loss: 0.6899 - acc: 0.6281 - valloss: 0.6880 - valacc: 0.6741 Epoch 3/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.6835 - acc: 0.7101 - valloss: 0.6792 - valacc: 0.7429 Epoch 4/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.6699 - acc: 0.7408 - valloss: 0.6623 - valacc: 0.7469 Epoch 5/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.6463 - acc: 0.7775 - valloss: 0.6360 - valacc: 0.7647 Epoch 6/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.6127 - acc: 0.7950 - valloss: 0.6017 - valacc: 0.7821 Epoch 7/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.5707 - acc: 0.8089 - valloss: 0.5601 - valacc: 0.7975 Epoch 8/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.5240 - acc: 0.8279 - valloss: 0.5180 - valacc: 0.8142 Epoch 9/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.4775 - acc: 0.8426 - valloss: 0.4773 - valacc: 0.8298 Epoch 10/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.4343 - acc: 0.8581 - valloss: 0.4416 - valacc: 0.8392 Epoch 11/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.3966 - acc: 0.8707 - valloss: 0.4114 - valacc: 0.8505 Epoch 12/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.3645 - acc: 0.8798 - valloss: 0.3880 - valacc: 0.8537 Epoch 13/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.3385 - acc: 0.8867 - valloss: 0.3670 - valacc: 0.8615 Epoch 14/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.3154 - acc: 0.8932 - valloss: 0.3519 - valacc: 0.8651 Epoch 15/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.2967 - acc: 0.8977 - valloss: 0.3393 - valacc: 0.8694 Epoch 16/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.2800 - acc: 0.9027 - valloss: 0.3290 - valacc: 0.8711 Epoch 17/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2651 - acc: 0.9080 - valloss: 0.3207 - valacc: 0.8753 Epoch 18/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2519 - acc: 0.9119 - valloss: 0.3132 - valacc: 0.8780 Epoch 19/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2401 - acc: 0.9159 - valloss: 0.3070 - valacc: 0.8791 Epoch 20/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.2295 - acc: 0.9203 - valloss: 0.3029 - valacc: 0.8798 Epoch 21/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2190 - acc: 0.9240 - valloss: 0.2990 - valacc: 0.8792 Epoch 22/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2101 - acc: 0.9261 - valloss: 0.2951 - valacc: 0.8828 Epoch 23/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.2011 - acc: 0.9291 - valloss: 0.2934 - valacc: 0.8819 Epoch 24/40 15000/15000 [==============================] - 1s 72us/sample - loss: 0.1933 - acc: 0.9325 - valloss: 0.2905 - valacc: 0.8837 Epoch 25/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1853 - acc: 0.9369 - valloss: 0.2883 - valacc: 0.8846 Epoch 26/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1783 - acc: 0.9395 - valloss: 0.2880 - valacc: 0.8839 Epoch 27/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1715 - acc: 0.9435 - valloss: 0.2868 - valacc: 0.8846 Epoch 28/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1650 - acc: 0.9460 - valloss: 0.2861 - valacc: 0.8845 Epoch 29/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1596 - acc: 0.9487 - valloss: 0.2868 - valacc: 0.8839 Epoch 30/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1537 - acc: 0.9511 - valloss: 0.2859 - valacc: 0.8855 Epoch 31/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1477 - acc: 0.9538 - valloss: 0.2861 - valacc: 0.8857 Epoch 32/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1423 - acc: 0.9558 - valloss: 0.2867 - valacc: 0.8857 Epoch 33/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1370 - acc: 0.9571 - valloss: 0.2882 - valacc: 0.8858 Epoch 34/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1324 - acc: 0.9605 - valloss: 0.2892 - valacc: 0.8868 Epoch 35/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1282 - acc: 0.9607 - valloss: 0.2907 - valacc: 0.8871 Epoch 36/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1234 - acc: 0.9636 - valloss: 0.2923 - valacc: 0.8866 Epoch 37/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1189 - acc: 0.9657 - valloss: 0.2941 - valacc: 0.8860 Epoch 38/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1148 - acc: 0.9671 - valloss: 0.2968 - valacc: 0.8852 Epoch 39/40 15000/15000 [==============================] - 1s 70us/sample - loss: 0.1113 - acc: 0.9683 - valloss: 0.2990 - valacc: 0.8846 Epoch 40/40 15000/15000 [==============================] - 1s 71us/sample - loss: 0.1073 - acc: 0.9699 - valloss: 0.3009 - valacc: 0.8841 Epoch 1에서 40까지 진행이 되고 해당 Epoch에서의 acc 및 loss를 확인할 수 있다. 이제 모델 평가 결과를 확인해 보자. 25000/25000 [==============================] - 1s 21us/sample - loss: 0.3204 - acc: 0.8736 [0.3203906687307358, 0.8736] 실제로 더 진보된 모델이라고 하기 위해서는 약 95% 이상의 정확도를 필요로 한다. 하지만 매우 단순한 방식을 사용했기 때문에 약 87% 정도의 정확도를 달성한 것을 확인 할 수 있다. 결과를 정확도와 오차의 그래프로 확인해 보자. 정확도와 손실 그래프 그리기 은 History 객체를 반환한다. 여기에는 훈련하는 동안 일어난 모든 정보가 dictionary로 담겨 있다. dictkeys(['loss', 'acc', 'valloss', 'val_acc']) 키를 확인해 보면 네개의 항목이 있다. 이는 훈련과 검증 단계에서 모니터링하는 지표들이다. 훈련 손실과 검증 손실을 그래프로 그려 보고, 훈련 정확도와 검증 정확도도 그래프로 그려 비교해 보자. !png !png 점선은 훈련 loss와 accuracy이고 실선은 검증 loss와 accuracy를 나타낸다. 훈련(점선)에서 loss는 epoch가 진행되면서 감소하고 accuracy는 증가한다. 하지만 검증(실선)에서는 약 20번째 epoch 이후가 최적점인 것 같다. 이는 훈련 데이터에서만 잘 동작하는 과대적합 때문일 것이다. 이 지점 부터는 모델이 과도하게 최적화되어 테스트 데이터에서 일반화되기 어려운 훈련 데이터 특정 표현을 학습하게 된다. 여기에서는 과대적합을 막기 위해 단순히 20번째 epoch 근처에서 훈련을 멈출 수 있다.
2019년 08월 07일20분NLP - 텍스트 분류 (Word2Vec) -
Ai이번에는 word2vec을 활용하여 모델을 구현 해보자. word2vec을 활용한 모델 구현 word2vec을 활용해 모델을 만들기 위해서는 먼저 각 단어에 대해 word2vec으로 백터화해야 한다. word2vec의 경우 단어로 표현된 리스트를 입력값으로 넣어야 한다. word2vec 백터화 이제 word2vec 모델 학습을 진행하기 앞서 word2vec 모델의 하이퍼 파라미터를 설정해야 한다. num_fratures : 각 단어에 대한 임베딩된 벡터의 차원을 정한다. minwordcount : 모델에 의미 있는 단어를 가지고 학습하기 위해 적은 빈도 수의 단어들은 학습하지 않는다. num_workers : 모델에 의미 있는 단어를 가지고 학습하기 위해 적은 빈도 수의 단어들은 학습하지 않는다. context : word2vec을 수행하기 위한 컨텍스트 윈도우 크기를 지정한다. downsampling : word2vec 학습을 수행할 때 더 빠른 학습을 위해 정답 단어 라벨에 대한 다운샘플링 비율을 지정한다. (보통 0.001이 좋은 성능을 낸다고 한다) 로깅을 할 떄 format을 위와 같이 지정하고, 로그 수준을 INFO로 하면 word2vec의 학습과정에서 로그 메시지를 양식에 맞게 INFO 수준으로 볼 수 있다. 2019-07-12 21:56:43,872 : INFO : collecting all words and their counts 2019-07-12 21:56:43,872 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types Training model... 2019-07-12 21:56:44,092 : INFO : PROGRESS: at sentence #10000, processed 1205223 words, keeping 51374 word types 2019-07-12 21:56:44,305 : INFO : PROGRESS: at sentence #20000, processed 2396605 words, keeping 67660 word types 2019-07-12 21:56:44,406 : INFO : collected 74065 word types from a corpus of 2988089 raw words and 25000 sentences 2019-07-12 21:56:44,406 : INFO : Loading a fresh vocabulary 2019-07-12 21:56:44,443 : INFO : min_count=40 retains 8160 unique words (11% of original 74065, drops 65905) 2019-07-12 21:56:44,443 : INFO : min_count=40 leaves 2627273 word corpus (87% of original 2988089, drops 360816) 2019-07-12 21:56:44,454 : INFO : deleting the raw counts dictionary of 74065 items 2019-07-12 21:56:44,469 : INFO : sample=0.001 downsamples 30 most-common words 2019-07-12 21:56:44,469 : INFO : downsampling leaves estimated 2494384 word corpus (94.9% of prior 2627273) 2019-07-12 21:56:44,481 : INFO : estimated required memory for 8160 words and 300 dimensions: 23664000 bytes 2019-07-12 21:56:44,481 : INFO : resetting layer weights 2019-07-12 21:56:44,577 : INFO : training model with 4 workers on 8160 vocabulary and 300 features, using sg=0 hs=0 sample=0.001 negative=5 window=10 2019-07-12 21:56:45,578 : INFO : EPOCH 1 - PROGRESS: at 53.88% examples, 1354613 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:46,589 : INFO : EPOCH 1 - PROGRESS: at 97.12% examples, 1208841 words/s, inqsize 8, outqsize 0 2019-07-12 21:56:46,625 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:46,631 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:46,639 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:46,645 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:46,649 : INFO : EPOCH - 1 : training on 2988089 raw words (2494785 effective words) took 2.1s, 1208654 effective words/s 2019-07-12 21:56:47,643 : INFO : EPOCH 2 - PROGRESS: at 51.68% examples, 1296637 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:48,565 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:48,565 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:48,579 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:48,587 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:48,587 : INFO : EPOCH - 2 : training on 2988089 raw words (2494872 effective words) took 1.9s, 1287927 effective words/s 2019-07-12 21:56:49,584 : INFO : EPOCH 3 - PROGRESS: at 52.03% examples, 1307272 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:50,495 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:50,504 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:50,504 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:50,513 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:50,513 : INFO : EPOCH - 3 : training on 2988089 raw words (2494507 effective words) took 1.9s, 1297134 effective words/s 2019-07-12 21:56:51,508 : INFO : EPOCH 4 - PROGRESS: at 52.60% examples, 1319590 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:52,421 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:52,438 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:52,438 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:52,444 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:52,444 : INFO : EPOCH - 4 : training on 2988089 raw words (2494292 effective words) took 1.9s, 1293999 effective words/s 2019-07-12 21:56:53,445 : INFO : EPOCH 5 - PROGRESS: at 54.20% examples, 1360524 words/s, inqsize 7, outqsize 0 2019-07-12 21:56:54,212 : INFO : worker thread finished; awaiting finish of 3 more threads 2019-07-12 21:56:54,228 : INFO : worker thread finished; awaiting finish of 2 more threads 2019-07-12 21:56:54,235 : INFO : worker thread finished; awaiting finish of 1 more threads 2019-07-12 21:56:54,241 : INFO : worker thread finished; awaiting finish of 0 more threads 2019-07-12 21:56:54,242 : INFO : EPOCH - 5 : training on 2988089 raw words (2494012 effective words) took 1.8s, 1390220 effective words/s 2019-07-12 21:56:54,242 : INFO : training on a 14940445 raw words (12472468 effective words) took 9.7s, 1291189 effective words/s word2vec으로 학습시킨 모델의 경우 모델을 따로 저장해두면 이후 다시 사용할수 있기 때문에 저장해두자 2019-07-12 22:00:40,807 : INFO : saving Word2Vec object under 300features40minwords10context, separately None 2019-07-12 22:00:40,807 : INFO : not storing attribute vectors_norm 2019-07-12 22:00:40,807 : INFO : not storing attribute cum_table C:\Users\nicey\.conda\envs\nlp\lib\site-packages\smartopen\smartopenlib.py:398: UserWarning: This function is deprecated, use smartopen.open instead. See the migration notes for details: https://github.com/RaRe-Technologies/smart_open/blob/master/README.rst#migrating-to-the-new-open-function 'See the migration notes for details: %s' % MIGRATIONNOTES_URL 2019-07-12 22:00:41,229 : INFO : saved 300features40minwords10context 에제 만들어진 word2vec 모델을 활용해 선형 회귀 모델을 학습해보자. 우선 학습을 위해 하나의 리뷰를 같은 형태의 입력값으로 만들어야 한다. 지금은 word2vec 모델에서 각 단어가 벡터로 표현돼 있다. 그리고 리뷰마다 단어의 개수가 모두 다르기 때문에 입력값을 하나으 형태로 만들어야 한다. 가장 단순한 방법은 문장에 있는 모든 단어의 벡터값에 대해 평균을 내서 리뷰 하나당 하나의 벡터로 만드는 방법이 있다. 그럼 하나의 리뷰에 대해 전체 단어의 평균값을 계산하는 함수를 구현하자 words : 단어의 모음인 하나의 리뷰가 들어간다. model : word2vec 모델을 넣는 공이며, 우리가 학습한 word2vec 모델이 들어간다. num_features : word2vec으로 임베딩할 때 정했던 벡터의 차원 수를 뜻한다. 하나의 벡터를 만드는 과정을 빠르게 하기 위해 np.zeros를 사용해 미리 모두 0값을 가지는 벡터를 만든다. 그리고 문장의 단어가 모델 단어사전에 속하는지 보기 위해 model.wv.index2word를 set객체로 생성해서 index2word_set 변수에 할당한다. 다음 반복문을 통해 리뷰를 구성하는 단어에 대해 임베딩된 벡터가 있는 단어 벡터의 합을 구하고 사용한 단어의 전체 개수로 나누어 평균 벡터의 값을 구한다. 문장에 특징값을 만들 수 있는 함수를 구현했다면 이제 앞에서 정의한 함수를 사용해 전체 리뷰에 대해 각 리뷰의 평균 벡터를 구하는 함수를 정의하자 reviews : 학습 데이터인 전체 리뷰 데이터를 입력 model : word2vec 모델을 입력 num_features : word2vec으로 임베딩할 때 정했던 벡터의 차원 수 전체 리뷰에 대한 평균 벡터를 담을 0으로 채워진 numpy 배열을 미리 만든다. 배열은 2차원, 배열의 행에는 각 문장에 대한 길이, 열에는 평균 벡터의 차원수 즉 크기를 입력. 그리고 각 리뷰에 대해 반복문을 돌면서 각 리뷰에 대해 특징 값을 만든다. 구현한 함수를 사용해 실제 학습에 사용될 입력값을 만들어 보자. C:\Users\nicey\.conda\envs\nlp\lib\site-packages\ipykernel_launcher.py:13: DeprecationWarning: Call to deprecated (Method will be removed in 4.0.0, use self.wv.getitem() instead). del sys.path[0] 학습과 검증 데이터셋 분리 모델 선언 및 학습 C:\Users\nicey\.conda\envs\nlp\lib\site-packages\sklearn\linear_model\logistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning. FutureWarning) LogisticRegression(C=1.0, class_weight='balanced', dual=False, fitintercept=True, interceptscaling=1, l1_ratio=None, maxiter=100, multiclass='warn', n_jobs=None, penalty='l2', random_state=None, solver='warn', tol=0.0001, verbose=0, warm_start=False) class_weight을 로 설정했다. 이는 각 라벨에 대해 균형있게 학습하기 위함이다. 검증 데이터셋을 이용한 성능 평가 Accuracy: 0.874200 Precision: 0.869141 Recall: 0.883287 F1-Score: 0.876157 AUC: 0.940927 학습 결과를 확인해 보면 TF-IDF를 사용해서 학습한 것보다 상대적으로 성능이 조금 떨어지는 것을 볼 수 있다. word2vec이 단어 간의 유사도를 보는 관점에서는 분명히 효과적일 수는 있지만 항상 좋은 성능을 보장하지는 않는다는 점을 알 수 있다. 데이터 제출 | | review | id | |-|-|-| | 0 | naturally film main themes mortality nostalgia... | "12311_10" | | 1 | movie disaster within disaster film full great... | "8348_2" | | 2 | movie kids saw tonight child loved one point k... | "5828_4" | | 3 | afraid dark left impression several different ... | "7186_2" | | 4 | accurate depiction small time mob life filmed ... | "12128_7" | C:\Users\nicey\.conda\envs\nlp\lib\site-packages\ipykernel_launcher.py:13: DeprecationWarning: Call to deprecated (Method will be removed in 4.0.0, use self.wv.getitem() instead). del sys.path[0]
2019년 07월 19일14분NLP - 텍스트 분류 (TF-IDF) -
Ai이전 글에서는 데이터를 모델에 적용하기 전에 데이터에 대해 이해하고 정제하는 과정인 데이터 전처리 과정을 진행했다. 이제 전처리된 데이터를 가지고 TF-IDF를 활용한 모델을 구현할 것이다. > 선형 회귀 모델 : 종속변수와 독립변수 간의 상관관계를 모델링하는 방법 > 로지스틱 회귀 모델 : 선형 모델의 결과값에 로지스틱 함수를 적용하여 0 ~ 1 사이의 값을 갖게 하여 확률로 표현 > TF-IDF : TF(Term Frequency, 단어의 빈도), IDF(역문서 빈도, Inverse Document Frequency) 쉽게 말하자면 문장에서 단어의 빈도수를 계산하되 너무 자주 등장하는 단어는 크게 의미를 두지 않도록 가중치를 낮게 주자는 의미. TF-IDF를 활용해 문장 벡터를 만들기 위한 TfidfVectorizer를 사용하기 위해서는 입력값이 텍스트로 이루어진 데이터 형태여야 하기 때문에 전처리한 결과 중 numpy배열이 아닌 정제된 텍스트 데이터를 사용해야 한다. >훈련 데이터 : train_clean.csv >테스트 데이터 : test_clean.csv | | review | sentiment | |-|-|-| | 0 | stuff going moment mj started listening music ... | 1 | | 1 | classic war worlds timothy hines entertaining ... | 1 | | 2 | film starts manager nicholas bell giving welco... | 0 | | 3 | must assumed praised film greatest filmed oper... | 0 | | 4 | superbly trashy wondrously unpretentious explo... | 1 | min_df : 설정한 값보다 특정 토큰의 df값이 더 적게 나오면 벡터화 과정에서 제거 analyzer : 분석하기 위한 기준 단위(word:단어 하나를 단위로, char:문자 하나를 단위로) sublinear_tf : 문서의 단어 빈도 수에 대한 스무딩(smoothing) 여부 ngram_range : 빈도의 기본 단위를 설정할 n-gram 범위 max_features : 각 벡터의 최대 길이 학습과 검증 데이터셋 분리 해당 입력값을 모델에 적용하기전 학습데이터의 일부를 검증 데이터로 따로 분리하자. 입력값인 X와 정답 라벨을 numpy 배열로 만든 y에 대해 적용해서 학습데이터와 검증데이터로 나누었다. 모델 선언 및 학습 선형 회귀 모델을 만들기 위해 LogisticRegression을 사용하고, class_weight를 'balanced'로 설정해서 각 라벨에 대해 균형 있게 학습할 수 있게 하자. C:\Users\nicey\.conda\envs\nlp\lib\site-packages\sklearn\linear_model\logistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning. FutureWarning) LogisticRegression(C=1.0, class_weight='balanced', dual=False, fitintercept=True, interceptscaling=1, l1_ratio=None, maxiter=100, multiclass='warn', n_jobs=None, penalty='l2', random_state=None, solver='warn', tol=0.0001, verbose=0, warm_start=False) 검증 데이터로 성능 평가 Accuracy : 0.859600 성능 평가 방법으로 정밀도(precision), 재현율(recall), f1-score, auc 등의 다양한 지표가 있지만 여기서는 정확도(Accuracy)만 측정하였다. 평가 결과 약 86%의 정확도를 보였다. 성능이 생각보다 나오지 않을 때는 하이퍼파라미터를 수정하거나 다른 기법들을 추가해서 성능을 올려보자. 검증 데이터의 성능이 만족할 만큼 나온다면 평가 데이터를 적용하면 된다. 데이터 제출하기 생성한 모델을 활용해 평가 데이터 결과를 예측하고 캐글에 제출할 수 있도록 파일로 저장하자. 우선 전처리한 텍스트 형태의 평가 데이터를 불러오자. | | review | id | |-|-|-| | 0 | naturally film main themes mortality nostalgia... | "12311_10" | | 1 | movie disaster within disaster film full great... | "8348_2" | | 2 | movie kids saw tonight child loved one point k... | "5828_4" | | 3 | afraid dark left impression several different ... | "7186_2" | | 4 | accurate depiction small time mob life filmed ... | "12128_7" | 해당 데이터를 대상으로 이전에 학습 데이터에 대해 사용했던 객체를 사용해 TF-IDF 값으로 벡터화한다. 백터화할 때 평가 데이터에 대해서는 fit을 호출하지 않고 그대로 transform만 호툴한다. 이미 학습 데이터에 맞게 설정했고, 그 설정에 맞게 평가 데이터도 변환을 하면 된다. 이제 이 값으로 예측한 후 예측값을 하나의 변수로 할당하고 출력해보자. [1 0 1 ... 0 1 0] 결과를 보면 각 데이터에 대해 긍정, 부정 값을 가지고 있다. 이제 이 값을 캐글에 제출하기 위해 csv 파일로 저장하자. 캐글에 제출하기 위한 데이터 형식은 각 데이터의 고유한 id 값과 결과값으로 구성되어야 한다.
2019년 07월 02일7분딥러닝 - 딥러닝의 이해
Ai개념 딥러닝이라는 용어는 2006년 캐나다 토론토 대학교의 제프리 힌튼(Geoffrey Hinton) 교수의 논문을 통해 처음 사용이 되었다. 하지만 딥러닝은 사실 새로운 개념이 아니다. 오래전부터 있어오던 인공신경망(Artificial Neural Network, ANN)과 크게 다를 바 없다. '인공신경망'이라고 하면 복잡한 뇌 구조를 생각하면서 어렵게 생각이 들겠지만 실제 뉴런의 행동을 살펴보면 어떻게 이렇게 단순한 행동으로 이루어질까 싶을 정도로 심플하다. !deep-1 입력신호가 오면 길이에 따른 가중치(weight)를 부여하고 임계값(bias)을 더한 후 축색돌기를 통해 이동한다. 그리고 그 값이 조건에 만족한다면 다음 뉴런으로 넘어가고 그렇지 않다면 넘어가지 않게 된다. 이러한 실제 뉴런에서 이뤄지는 과정을 그대로 수학적으로 구현한 것이다. 컴퓨터가 사진 속에서 고양이를 검출해내야 한다고 생각해보자. '고양이'라는 추상적 이미지는 아마 선, 면, 형상, 색깔, 크기 등 다양한 요소들이 조합된 결과물일 것이다. 이것은 아마 '선 30cm 이상은 고양이, 이하는 고양이 아님', 또는 '갈색은 고양이, 빨간색은 고양이 아님' 처럼 간단한 선형 구분으로는 식별해 낼 수 없는 문제이다. 딥러닝은 이 과제를 선 긋고 왜곡하고 합하고를 반복하며 복잡한 공간 속에서의 최적의 구분선을 만들어 내는 목적을 가지고 있다. !deep-1 파란선과 빨간선의 영역을 구분한다고 생각해보자. 그냥 구분 선을 긋는다면 아마 왼쪽처럼 불완전하게 그을 수 있을 것이다. 하지만 공간을 왜곡하면 오른쪽 같이 아름답게 구분선을 그릴 수 있다. 이처럼 인공신경망은 선 긋고, 구기고, 합하고를 반복하여 데이터를 처리한다. 그럼 어떠한 규칙으로 선을 긋고 공간을 왜곡할까? 바로 데이터에 근거하는 것이다. 일단 대충 선을 긋고 그것들을 살살 움직여가며 구분 결과가 더 좋게 나오도록 선을 움직이는 것인데 이러한 과정을 최적화(optimization)이라고 한다. 딥러닝은 아주 많은 데이터와 아주 오랜 시간의 최적화를 통해 데이터를 학습하게 된다. (양에는 장사 없다고나 할까...) !deep-1 딥러닝은 하나의 뉴런이 아닌 수천 수억개의 뉴런들이 연결되어 이뤄진다. 즉 인공 신경 또한 수많은 연결들을 통해 망을 이룬다. 크게 입력층(input layer)과 출력층(output layer) 그리고 그 사이에 있는 layer들을 묶어서 은닉층(hidden layer)이라고 한다. 위 그림에서는 모든 노드가 연결되어있지만, 꼭 다 연결되어있어야 하는 것은 아니다. 단지 내가 어떻게 신경망을 구성하냐에 따라 정해지는 것이다. 저렇게 망이 이뤄져 있는 것은 알겠는데, 대체 어떻게 학습을 한다는건지 이해가 안 될 수 있다. 내가 고양이 사진을 입력에 넣었다면 출력에는 고양이라는 결과가 나와야 맞는 것이다. 고양이가 맞다면 그냥 넘어가면 되지만, 고양이가 아니라고 결과가 나왔다면 이것은 문제가 있는 것이다. 따라서 다시 뒤로 돌아가면서 각 인공 신경의 가중치(weight)와 임계값(bias) 값을 수정하게 된다. 이러한 과정을 반복하며 weight와 bias 값을 조정하고 고양이라는 결과가 나오도록 '학습' 하는 것이다. !deep-1 2012년 이미지넷에서 이미지 분류 대회가 있었다. 1000개의 카테고리와 100만개의 이미지로 구성되어 분류 정확도를 겨루는 대회이고, 아래 사진만 보더라도 딥러닝이 이미지 인식 부문에서 얼마나 큰 영향을 끼치고 있는지 알 수 있다. 이 대회 이전에는 기계의 이미지 인식률이 75%를 넘지 못했고 80% 이상의 인식률은 불가능이라는 인식이 있었다. 하지만 이 대회에서 힌튼 교수의 제자 알렉스가 알렉스넷(AlexNet)이라는 딥러닝 기반 알고리즘으로 84.7%를 찍었고, 이후로는 대부분의 참가자들이 딥러닝으로 방향을 돌렸다. 현재는 오류율이 5% 이하의 정확도로 인간의 인식수준을 뛰어넘었다. 딥러닝이 대단한 것은 일반적인 기계 학습과 달리 특징 추출(feature extraction)이 자동적으로 이루어지는 점 이다. 기존에는 효과적인 특징을 추출하기 위해 관련 분야 전문가가 오랜 시간동안 직접 특징을 추출하는 수식이나 방법을 고안해야 했다. 이 방법은 개발, 평가 및 보완에 많은 시간이 걸리는데 딥러닝은 이런 과정을 컴퓨터가 대신 하도록 알고리즘을 짠 것으로, 사람에 비해 훨씬 빠르고 효과적으로 수행해도록 학습시켜준다. 발전과정 딥러닝의 발전과정을 보면 아주 오래전부터 시작이 되었고 지금에 이르기까지 많은 이들의 노력이 있었다. 퍼셉트론은 인간의 두뇌 움직임을 수학적으로 구성하였다. !deep-1 !deep-1 그 당시 AND와 OR의 경우 퍼셉트론을 통한 선형 분리(linearly separable)가 가능했다. 따라서 AND / OR 연산이 가능하게 훈련한 퍼셉트론을 보고 많은 사람들은 기계의 학습 가능성에 대해 큰 기대를 하게 되었다. 하지만 이러한 그들의 엄청난 기대에 찬물을 끼얹어버린 것이 XOR 연산에 대한 불가능이었다. 당시 하나의 인공 신경. 즉 퍼셉트론으로는 선형 분리가 불가능해 XOR 연산에 대한 학습이 불가능했다. 1969년 이 문제를 해결하기 위해 퍼셉트론을 다중으로 겹치면 이 문제를 해결할 수 있음을 증명한 다중 계층 퍼셉트론이 등장하지만 레이어가 복잡해질수록 연산이 복잡해져서 현실적으로 파라미터값을 구하는 것이 불가능 하였다. 데이터가 모델을 스스로 찾아내는 역전파(Backpropagation) 등장 즉 앞의 진행방향에서 고쳐가는 것이 아니라 결과를 보고 뒤로 가면서 weight와 bias를 조정하는 방법을 고안 하였고, XOR 뿐 아니라 좀 더 복잡한 과정도 해결할 수 있음을 보이며 다시 인공 신경망은 사람들의 관심을 끌기 시작했다. !deep-1 (터미네이터2 심판의 날 (1991) 대사 中) 영화 중반부에 터미네이터인 아놀드의 대사 중에 "My CPU is a neural-net processor." 라는 말이 나온다. 스스로 학습할 수 있는 기계를 neural-net processor 라는 말로 설명했고 이 당시에도 neural network에 관심이 있었음을 알 수 있다. 신경망이 깊어질수록 원하는 결과를 얻을 수 없다. 오히려 성능이 저하되는 경우가 발생. 다음의 문제를 해결하는데 10년이 걸리게 된다. 1. Overfitting (과하거나) 2. Vanishing Gradient (덜하거나) 3. Too slow (느리거나) 이런 문제를 해결하기 위해 연구하는 10년 동안 GUP를 활용한 연산시도, 알고리즘의 발전이 있게 된다.(SVM, Random Forest 등장) 1. 과한 적합 해결 Droupout 2. 덜한 적합 해결 ReLU(Rectified Linear Unit) : 수렴속도가 시그모이드류 함수 대비 6배 빠름 3. 느린 적합 해결 Adam : 확룰적 경사 하강법(SGD)에서 더 나아가 학습속도와 운동량을 고려한 옵티마이저가 등장 이러한 여러번의 실패로 인해 이후 나온 논문에서는 neural net이라는 단어를 찾을 수 없었고 neural 대신 deep 이라는 단어를 사용했다. 이 당시 neural network 라는 말만 들어가면 논문에서 거절당한다는 말이 있을 정도로 신경망이 외면받던 시기였기 때문에 좀 더 사람들의 이목을 끌 수 있는 단어를 택했던 것 같다. 마치며 딥 러닝이 화두가 되고 있는 것은 비교적 최근의 일이지만, 딥 러닝의 기본 구조인 인공 신경망(Artificial Neural Network)의 역사는 오래되었다. 1957년에 등장한 초기 신경망인 퍼셉트론(Perceptron)으로 시작해 최근에 쓰이고 있는 발전된 신경망 RNN, LSTM에 대해서도 좀더 자세히 알아보면 좋을 것 같다.
2019년 05월 16일13분NLP - 자연어처리 - KoNLPy (코엔엘파이)
Ai자연어 처리에서 각 언어마다 모두 특징이 다르기 때문에 동일한 방법을 사용하기는 어려울 것이다. 한글에도 NLTK나 Spacy 같은 도구를 사용할 수 있으면 좋겠지만 언어 특성상 영어를 위한 도구를 사용하기에는 적합하지 않다. 하지만 많은 사람들의 노력으로 개발된 한글 자연어 처리를 돕는 훌륭한 도구를 사용할 수있다. 그중 한글 자연어 처리에 많이 사용하는 파이썬 라이브버리 KoNLPy에 대해 알아보겠다. KoNLPy는 한글 자연어 처리를 쉽고 간결하게 처리할 수 있도록 만들어진 오픈소스 라이브러리다. 또한 국내에 이미 만들어져 사용되고 있는 여러 형태소 분석기를 사용할 수 있게 허용한다. 일반적인 어절 단위에 대한 토크나이징은 NLTK로 충분히 해결할 수 있으므로 형태소 단위에 대한 토크나이징에 대해 알아보도록 하겠다. 설치 리눅스 또는 macOS에서는 다음과 같이 pip를 이용해 간단하게 설치할 수 있다. 형태소 단위 토크나이징 한글 텍스트의 경우 형태소 단위 토크나이징이 필요할 떄가 있는데 KoNLPy에서는 여러 형태소 분석기를 제공하며, 각 형태소 분석기별로 분석한 결과는 다를 수 있다. 각 형태소 분석기는 클래스 형태로 되어 있고 이를 객체로 생성한 후 매서드를 호출해서 토크나이징할 수 있다. 형태소 분석 및 품사 태깅 형태소란 의미를 가지는 가장 작은 단위로서 더 쪼개지면 의미를 상실하는 것들을 말한다. 따라서 형태소 분석이란 의미를 가지는 단위를 기준으로 문장을 살펴보는 것을 의미한다. KoNLPy는 기존에 C, C++, Java 등의 언어를 통해 형태소 분석을 할 수 있는 좋은 라이브러리들을 파이썬 라이브러리로 통합해서 사용할 수 있록 하여 한국어 구문 분석을 쉽게 할 수 있도록 만들어진 라이브러리이다. KoNLPy에는 다양한 형태소 분석기들이 객체 형태로 포함돼 있으며 다음과 같은 각 형태소 분석기 목록이 있다. Hannanum Kkma Komoran Mecab Okt(Twitter) 모두 동일한 형태소 분석기능을 제공하는데, 각기 성능이 조금씩 다르다고 하니 직접 비교해보고 자신의 데이터를 가장 잘 분석하는 분석기를 사용하는 것이 좋다. (단, Mecab는 윈도우에서 사용할 수 없다.) 여기에서는 Okt 예로 들어 설명 하도록 하겠다. Okt는 원래 이름이 Twitter였으나 0.5.0 버전 이후부터 이름이 Okt로 바뀌었다. Okt 에서 제공되는 함수를 살펴보자. okt.morphs() 텍스트를 형태소 단위로 나눈다. 옵션으로 norm과 stem이 있다. norm은 문장을 정규화. stem은 각 단어에서 어간을 추출.(기본값은 둘다 False) okt.nouns() 텍스트에서 명사만 뽑아낸다. okt.phrases() 텍스트에서 어절을 뽑아낸다. okt.pos() 각 품사를 태깅하는 역할을 한다. 품사를 태깅한다는 것은 주어진 텍스트를 형태소 단위로 나누고, 나눠진 각 형태소를 그에 해당하는 품사와 함께 리스트화하는 것을 의미한다. 옵션으로 norm, stem, join이 있는데 join은 나눠진 형태소와 품사를 '형태소/품사' 형태로 같이 붙여서 리스트화한다. 다음 문장을 직접 각 함수에 적용해서 살펴보자. 모바일 게임은 재밌다 열심히 해서 만랩을 찍어야지~ ㅎㅎㅎ 어간 추출을 한 경우 의 어간인 로 추출된 것을 볼 수 있다. 이제 명사와 어절을 추츨헤 보자 nouns 함수를 사용한 경우에는 명사만 추출되었고 phrases 함수의 경우 어절 단위로 나뉘어서 추출 되었다. 품사 태깅을 하는 함수 pos를 사용해보자. join 옵션을 True로 설정 하면 형태소와 품사가 함께 나오는 것을 볼 수 있다. 경우에 따라 옵션을 설정하면서 사용하면 된다. KoNLPy 데이터 KoNLPy 라이브러리는 한글 자연어 처리에 활용할 수 있는 한글 데이터를 포함하고 있어 라이브러리를 통해 데이터를 바로 사용할 수 있다. kolaw 한국 법률 말뭉치. 'constitution.txt'파일 kobill 대한민국 국회 의안 말뭉치. 각 id값을 가지는 의안으로 구성. 파일은 '1809890.txt' 부터 '1809899.txt'까지로 구성. 라이브러리를 사용해 각 말뭉치를 불러오자. 위 데이터들을 가지고 여러 가지 한글 자연어 처리 문제를 연습하는 데 활용할 수 있다. 마치며 KoNLPy 홈페이지에 가보면 나름의 철학을 가지고 프로젝트를 진행하는 듯한 글을 볼 수 있다. > KoNLPy는 같은 기능을 하는 또 하나의 도구를 만들려는 것이 아닙니다. > 이 프로젝트에는 세 가지 철학이 있습니다. > - 사용법이 간단해야 한다. > - 누구나 쉽게 이용할 수 있어야 한다. > - "인터넷 민주주의는 효과적이다." 개인적으로 마음에 드는 글귀이다. 인터넷 민주주의를 위해 직접 참여도 가능하니 한국어 NLP에 관심이 많다면 아래 공식홈을 방문하여 살펴보길 바란다. KoNLPy 공식홈 : (https://konlpy-ko.readthedocs.io)
2019년 04월 18일9분NLP - 데이터 정제
Ai!nlp-pre Data Cleaning and Text Preprocessing 기계가 텍스트를 이해할 수 있도록 텍스트를 정제하고 신호와 소음을 구분하여 아웃라이어 데이터로 인한 오버피팅을 방지하기 위해서는 다음과 같은 처리를 해주어야 한다. HTML 태그, 특수문자, 이모티콘 처리 토근화(Tokenization) : 문장의 단어를 분리하는 단계 불용어(Stopword) 제거 : 자주 등장하지만 특별한 의미를 갖지 않는 단어 제거 어간 추출(Stemming) 및 음소표기법(Lemmatization) 정규 표현식 >텍스트 데이터 전처리 이해 > (입니닼ㅋㅋ -> 입니다 ㅋㅋ, 샤릉해 -> 사랑해) > 한국어를 처리하는 예시입니닼ㅋㅋㅋㅋㅋ -> 한국어를 처리하는 예시입니다 ㅋㅋ > >한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하는Verb, 예시Noun, 입Adjective, 니다Eomi ㅋㅋKoreanParticle > (입니다 -> 이다) >한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하다Verb, 예시Noun, 이다Adjective, ㅋㅋKoreanParticle > >한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어, 처리, 예시, 처리하는 예시 >(출처 : 트위터 한국어 형태소 분석기) BeautifulSoup 많이 쓰이는 파이썬용 파서로 html, xml을 파싱할때 주로 많이 사용한다. BeautifulSoup에는 기본적으로 파이썬 표준 라이브러리인 html파서를 지원하지만, lxml을 사용하면 성능 향상이 있다. BeautifulSoup(markup, ) html.parser : 빠르지만 유연하지 않기 때문에 단순한 html문서에 사용 lxml : 매우 빠르고 유연 xml : xml 파일에 사용 html5lib : 복접한 구조의 html에 대해서 사용.(속도가 느린편) ^^;" soup = BeautifulSoup(data, "html5lib") removetag = soup.gettext() result_text = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\ 문장에서 태그와 특수문자를 제거하기 위해 BeautifulSoup 와 정규표현식을 사용하였다. 토큰화 (Tokenization) 코퍼스(corpus)에서 토큰(token)이라 불리는 단위로 나누는 작업을 토큰화(Tokenization)라고 부른다. 토큰의 단위가 상황에 따라 다르지만, 보통 의미있는 단위로 토큰 정의한다. 토큰의 기준을 단어로 하는 경우 가장 간단한 방법은 띄어쓰기를 기준으로 자르는 것이다. I loved you. machine learning 에서 구두점을 제외시키고 토큰화 한 결과는 다음과 같다. 하지만 보통 토큰화 작업은 단순히 구두점이나 특수문자를 전부 제거하는 작업을 수행하는 것만으로 해결되지 않는다. 구두점이나 특수문자를 전부 제거하면 토큰이 의미를 잃어 버리는 경우가 발생하기도 하기때문이다. 띄어쓰기 단위로 자르면 사실상 단어 토큰이 구분되는 영어와 달리, 한국어는 띄어쓰기반으로는 단어 토큰을 구분하기 어렵다. >한국어는 단어의 다양한 의미와 높낮이 그리고 띄어쓰기가 어렵다 보니 잘못된 데이터를 많이 받게 되어 자연어처리를 하는데 있어 어려움이 많이 따른다. 하지만 다양한 곳에서 한국어 처리를 위한 형태소 분석기를 연구하고 있다. 얼마전 카카오에서도 카이라는 딥러닝 기술 기반의 형태소 분석기를 오픈소스로 공개 하였고 그외에 트위터, 코엔엘파이등 꽤 쓸만한 것들이 있다. 불용어 (Stopword) 일반적으로 코퍼스에서 자주 나타나는 단어로 학습이나 예측 프로세스에 실제로 기여를 하지 않는다. (조사, 접미사 - 나,나,은,는,이,가,하다,합니다....등) NLTK에는 17개의 언어에 대해 불용어가 정의되어 있다. 하지만 아쉽게도 한국어는...없다. 간단하게 10개 정도만 영어의 불용어를 보면, >결과 : ['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', 'your'] 간단하다, 불용어 사전에 있는 단어들은 제거하면 된다. 한국어의 경우 불용어를 제거하는 방법으로는 위에서 언급한 형태소 분석 후 조사, 접속사 등을 제거할 수 있다. 어간 추출 (Stemming) 스태밍이라고 하는데, 어간이란 말은 단어의 의미를 담고 있는 단어의 핵심부분이라 생각하면 된다. 쉽게, 단어를 축약형으로 바꿔주는 작업이라고도 할 수 있다. 한국어가, 한국어는, 한국어처럼 -> 대표적으로 포터 스태머(PorterStemmer)와 랭커스터 스태머(LancasterStemmer)가 있는데 포터는 보수적이고 랭커스터는 좀 더 적극적이다. PorterStemmer >maxim >running >> run >runs >> run >run >> run LancasterStemmer >maxim >running >> run >runs >> run >run >> run 음소표기법 (Lemmatization) 언어학에서 음소표기법 (Lemmatization)은 단어의 보조 정리 또는 사전 형식에 의해 식별되는 단일 항목으로 분석 될 수 있도록 굴절 된 형태의 단어를 그룹화하는 과정이다. 어간 추출(Stemming)과는 달리 단어의 형태가 적절히 보존되는 양상을 보이는 특징이 있다. 하지만 그럼에도 의미를 알 수 없는 적절하지 못한 단어로 변환 하기도 하는데 음소표기법(Lemmatizer)은 본래 단어의 품사 정보를 알아야만 정확한 결과를 얻을 수 있기 때문이다. 품사정보가 보존된 형태의 기본형으로 변환. 단어가 명사로 쓰였는지 동사로 쓰였는지에 따라 적합한 의미를 갖도록 추출하는 것. >결과 : ['have', 'going', 'love', 'life', 'fly', 'dy', 'ha', 'starting'] 결과에서 보면 알수 있듯이 나 는 그 의미를 알수 없는 적절하지 못한 단어로 변환이 되었다. 하지만 dies나 has가 동사로 쓰였다는 것을 알려준다면 좀더 정확한 결과를 얻을 수 있게 된다. > 'die' > 'have' 음소표기법은 문맥을 고려하며, 수행했을 때의 결과는 해당 단어의 품사 정보를 보존한다. 하지만 어간 추출은 품사 정보가 보존이 되지 않는다. 마치며.. 이런 작업들이 갖고있는 의미는 눈으로 봤을 때는 서로 다른 단어들이지만, 하나의 단어로 일반화시킬 수 있다면 하나의 단어로 일반화시켜서 문서 내의 단어 수를 줄여보자는 것이고 자연어 처리에서 전처리의 지향점은 언제나 갖고 있는 코퍼스로부터 복잡성을 줄이는 일이다.
2019년 03월 20일10분Jupyter Notebook 설치 및 셋팅
Ai머신러닝 관련 코드를 작성해보기 위해 환경을 세팅해보자. 다른 방법들도 많겠지만 가장 많이 쓰는 주피터 노트북을 설치하고 파이썬 패키지를 어떻게 관리할 것인지를 살펴보도록 하자. 주피터 노트북 주피터 노트북(jupyter notebook)은 웹 브라우저에서 파이썬 코드를 작성하고 실행까지 해볼 수 있는 REPL방식의 개발도구이다. 머신러닝이나 데이터분석 용도로 파이썬을 사용하는 사람들이 사용하는 툴로써 가벼우며 코드를 실행하고 수정하기가 간편하다. 또한 notebook형태로 파일이 공유가 가능하다. > REPL(Read–Eval–Print Loop) : 명령어를 한줄씩 입력하며 실행 상황을 지켜보는 방식(대화형) 먼저 파이썬을 설치하자. 파이썬이 이미 설치가 되어있다면 생략 하자. 파이썬 pip를 설치한 후 로 주피터 노트북을 설치 할 수 있지만 일반적으로 를 설치하면 이 함께 설치되어 주피터를 사용할 수 있다. 물론 를 이용하면 원하는 패키지만 그떄 그때 설치하여 사용할 수는 있지만, 아나콘다를 설치하면 데이터 처리 및 분석에 필요한 패키지가 모두 포함되어 설치가 되기 때문에 약간 설치시간이 오래 걸리긴 하지만 아나콘다를 사용하는 것을 추천한다. 아나콘다(Anaconda) 설치 이 글에서는 Ubuntu18.04 기준으로 설명하도록 하겠다. 또한 최신버전을 위해 반드시 아나콘다 사이트에서 최신 URL을 확인하여 설치를 진행하길 바란다. >계속 엔터를 눌러 설치한다(bashrc등록 여부는 Y로 입력해준다 - 그렇지 않으면 수동설정) 아나콘다(Anaconda) 공식홈 참고 설치 확인 만약 삭제하길 원한다면... 주피터 노트북 실행 !jupyter-run > 화면에 출력된 으로 접속하면 된다. > ( 명령으로 파일을 생성하면 해당 파일에서 세부설정이 가능하다.) 접속화면 !jupyter 우측 상단에 New > Python3를 선택하면 새로운 창이 생성된다. !jupyter 이제 웹상에서 파이썬 코드를 작성하고 실행 할 수 있다. 물론 matplotlib 패키지를 이용하여 가공된 데이터를 그래프로 표현 할 수 있다. !jupyter 주피터 노트북은 개인 로컬환경에 설치하여 직접 띄울수도 있지만, Microsoft에서는 azure notebook, Google에서는 Colab에서 클라우드상의 주피터환경을 제공해주고 있으니 간단한 학습용로라면 이를 활용하는 방법도 좋을 것이다. 공짜다! Microsoft Azure Notebooks Google Colab (주피터 노트북 사용법에 대한 부분은 다루지 않겠다.) 더 살펴보자... 아나콘다 가상환경 개발을 위한 파이썬 패키지 가상환경을 별도로 만들고 싶다면 conda에서 지원하는 명령어로 사용이 가능하다. 만약 아나콘다를 사용하지 않고 를 사용한다면 로 가상환경을 관리 할 수 있다. >가상환경을 선택하여 활성화를 하게 되면 와 같이 프롬프트 앞에 해당 가상환경명이 표시가 된다. 이후 패키지를 설치하게 되면 활성화된 가상환경에만 패키지가 설치가 된다. 다시 가상환경을 비활성화하거나 삭제하려면, 파이썬에서는 한 라이브러리에 대해 하나의 버전만 설치가 가능한데 이는 여러개의 프로젝트를 진행하게 되면 문제가 될 수 있다. 작업을 바꿀때마다 다른 버전의 라이브러리를 설치해야 해야 하는 번거러움을 방지하기 위해 독립된 가상환경이 필요한 것이다. 아나콘다에 패키지 설치 가상환경을 활성화 하지 않고 가상환경명을 지정하여 패키지를 설치 할 수도 있다. 라는 가상환경에 를 설치 및 삭제를 해보자. 주피터 설치 라는 가상환경을 활성화 하고 jupyter를 설치해보자. 주피터 확장기능 설치 주피터 확장기능을 사용하면 좀더 편리한 기능들을 사용할 수 있다. > 확장을 활성화 하려면 을 클릭하거나 http://localhost:8888/nbextensions에서 확인 할 수 있다. 주피터 font 수정 font가 가독성이 좀 떨어진다. custom.css를 추가하여 css를 적용할 수 있다. 주피터 상세 설정 기본 디렉토리, 토큰, 브라우저옵션 등 상세한 설정을 제어 하고 싶다면 config파일을 생성하여 해당 파일의 옵션값들을 수정해주면 된다. 결과 Writing default config to: /home/uss/.jupyter/jupyternotebookconfig.py Matplotlib 설정 matplotlib 주요 설치 정보 우분투에 폰트는 에 있다. 글꼴 설치 > 에 nanum 폰트 파일이 설치가 된다. 설치한 nanum폰트를 matplotlib에 복사하고 matplotlib의 폰트 캐시를 삭제하자. > 설치 위치는 /usr/local/lib/python3.?/dist-packages/matplotlib가 기본적이지만 matplotlib.file 명령으로 설치위치를 확인하자.(가상환경의 경우 위치가 다를 수 있음) > 캐시 위치는 보통 사용자 디렉토리에 위치한다. 폰트 지정 matplotlib.rcParams을 통해 matplotlib의 다양한 설정값을 읽거나 지정할 수 있다. [(f.name, f.fname) for f in matplotlib.font_manager.fontManager.ttflist if 'Nanum' in f.name] 으로 Nanum폰트명을 확인 할 수 있다. 폰트 설정파일 매번 폰트를 지정하는것이 번거롭다면 matplotlibrc 파일에 설정하면 된다. > 레이블에 '-'가 있는 경우 유니코드의 '-'문자를 그대로 출력하면 '-' 부호만 깨져 보인다. 이를 방지하기 위해 'axes.unicode_minus' 옵션을 False로 지정한다. 이제 jupyter notebook를 재구동하고 그래프에 한글이 정상적으로 표시가 되는지 확인해보자. 윈도우에서 아나콘다를 사용한다면.. 1. matplotlib 설정파일에 설정 추가 (matplotlib.matplotlib_fname()) font.family : NanumGothic font.size : 10.0 axes.unicode_minus : False 2. .ttf 폰트를 matplotlib으로 복사한다. 아나콘다를 사용하는 경우 보통 아래 디렉토리이다.(NanumGothic 추천) C:\Users\(사용자명)\Anaconda3\Lib\site-packages\matplotlib\mpl-data\fonts\ttf 3. matplotlib cache 폴더의 내용 삭제 matplotlib.get_cachedir() 4. 커널 재시작(주피터 노트북 재시작) 참고 Jupyter Notebook
2019년 02월 14일12분NLP - 텍스트 분류 (전처리) -
Ai캐글(Kaggle)의 "Bag of Words meets bag of popcorns" (https://www.kaggle.com/c/word2vec-nlp-tutorial) 워드팝콘 데이터의 처리 과정 > 케글 데이터 불러오기 -> EDA -> 데이터정제 -> 모델링 데이터정제 HTML 및 문장 보호 제거 불용어 제거 단어 최대 길이 설정 단어 패딩 백터 표상화 NanumGothic.ttf ratings.txt test_id.npy labeledTrainData.tsv ratingstest.txt trainclean.csv labeledTrainData.tsv.zip ratingstrain.txt traininput.npy nsmctestinput.npy sampleSubmission.csv train_label.npy nsmctestlabel.npy testData.tsv unlabeledTrainData.tsv nsmctraininput.npy testData.tsv.zip unlabeledTrainData.tsv.zip nsmctrainlabel.npy test_clean.csv id sentiment review "5814_8" 1 "With all this stuff going down at the moment with MJ i've started listening to his music, watching the odd documentary here and there, watched The Wiz and watched Moonwalker again. Maybe i just want to get a certain insight into this guy who i thought was really cool in the eighties just to maybe make up my mind whether he is guilty or innocent. Moonwalker is part biography, part feature film which i remember going to see at the cinema when it was originally released. Some of it has subtle messages about MJ's feeling towards the press and also the obvious message of drugs are bad m'kay.Visually impressive but of course this is all about Michael Jackson so unless you remotely like MJ in anyway then you are going to hate this and find it boring. Some may call MJ an egotist for consenting to the making of this movie BUT MJ and most of his fans would say that he made it for the fans which if true is really nice of him.The actual feature film bit when it finally starts is only on for 20 minutes or so excluding the Smooth Criminal sequence and Joe Pesci is convincing as a psychopathic all powerful drug lord. Why he wants MJ dead so bad is beyond me. Because MJ overheard his plans? Nah, Joe Pesci's character ranted that he wanted people to know it is he who is supplying drugs etc so i dunno, maybe he just hates MJ's music.Lots of cool things in this like MJ turning into a car and a robot and the whole Speed Demon sequence. Also, the director must have had the patience of a saint when it came to filming the kiddy Bad sequence as usually directors hate working with one kid let alone a whole bunch of them performing a complex dance scene.Bottom line, this movie is for people who like MJ on one level or another (which i think is most people). If not, then stay away. It does try and give off a wholesome message and ironically MJ's bestest buddy in this movie is a girl! Michael Jackson is truly one of the most talented people ever to grace this planet but is he guilty? Well, with all the attention i've gave this subject....hmmm well i don't know because people can be different behind closed doors, i know this for a fact. He is either an extremely nice but stupid guy or one of the most sickest liars. I hope he is not the latter." | | id | sentiment | review | |-|-|-|-| | 0 | 5814_8 | 1 | With all this stuff going down at the moment w... | | 1 | 2381_9 | 1 | "The Classic War of the Worlds" by Timothy Hi... | | 2 | 7759_3 | 0 | The film starts with a manager (Nicholas Bell)... | | 3 | 3630_4 | 0 | It must be assumed that those who praised this... | | 4 | 9495_8 | 1 | Superbly trashy and wondrously unpretentious 8... | 데이터는 "id", "sentiment", "review"로 구분되어 있어며, 각 리뷰('review')에 대한 감정('sentiment')이 긍정(1), 부정(0)인지 나와있다. 이제 다음과 같은 순서로 데이터 분석을 해보자. 1. 데이터 크기 2. 데이터 개수 3. 각 리뷰 문자 길이 분포 4. 많이 사용된 단어 5. 긍정, 부정 데이터의 분포 6. 각 리뷰의 단어 개수 분포 7. 특수문자 및 대문자, 소문자 비율 데이터 크기 파일 크기 : labeledTrainData.tsv 33.56MB testData.tsv 32.72MB unlabeledTrainData.tsv 67.28MB 데이터 개수 전체 학습 데이터의 개수: 25000 각 리뷰의 문자 길이 분포 0 2302 1 946 2 2449 3 2245 4 2231 Name: review, dtype: int64 Text(0, 0.5, 'Number of review') !png figsize: (가로, 세로) 형태의 튜플로 입력 bins: 히스토그램 값들에 대한 버켓 범위 range: x축 값의 범위 alpha: 그래프 색상 투명도 color: 그래프 색상 label: 그래프에 대한 라벨 대부분 6000 이하 그중에서도 2000 이하에 분포되어 있음을 알 수 있다. 그리고 일부 데이터의 경우 이상치로 10000 이상의 값을 가지고 있다. 길이데 대해 몇가지 통계를 확인해 보자 리뷰 길이 최대 값: 13708 리뷰 길이 최소 값: 52 리뷰 길이 평균 값: 1327.71 리뷰 길이 표준편차: 1005.22 리뷰 길이 중간 값: 981.0 리뷰 길이 제 1 사분위: 703.0 리뷰 길이 제 3 사분위: 1617.0 리뷰의 길이가 히스토그램에서 확인했던 것과 비슷하게 평균이 1300정도 이고, 최댓값이 13000이로 확인이 된다. !png labels : 입력한 데이터에 대한 라벨 showmeans : 평균값을 마크함 데이터의 길이가 대부분 2000 이하로 평균이 1500 이하인데, 길이가 4000 이상인 이상치 데이터도 많이 분포되어 있는 것을 확인할 수 있다. 이제 리뷰에서 많이 사용된 단어로 어떤 것이 있는지 알아보자. 많이 사용된 단어 (-0.5, 799.5, 599.5, -0.5) !png 워드 클라우드를 통해 살펴보면 가장 많이 사용된 단어는 br로 확인이 된다. br은 HTML 태그 이기때문에 이 태그들을 모두 제거 하는 전처리 작업이 필요하다. 긍정/부정 데이터의 분포 !png 거의 동일한 개수로 분포되어 있음을 확인할 수 있다. 좀더 정확한 값을 확인해 보자. 긍정 리뷰 개수: 12500 부정 리뷰 개수: 12500 각 리뷰의 단어 개수 분포 각 리뷰를 단어 기준으로 나눠서 각 리뷰당 단어의 개수를 확인해 보자. 단어는 띄어쓰기 기준으로 하나의 단어라 생각하고 개수를 계산한다. 우선 각 단어의 길이를 가지는 변수를 하나 설정하자. Text(0, 0.5, 'Number of reviews') !png 대부분의 단어가 1000개 미만의 단어를 가지고 있고, 대부분 200개 정도의 단어를 가지고 있다. 리뷰 단어 개수 최대 값: 2470 리뷰 단어 개수 최소 값: 10 리뷰 단어 개수 평균 값: 233.79 리뷰 단어 개수 표준편차: 173.74 리뷰 단어 개수 중간 값: 174.0 리뷰 단어 개수 제 1 사분위: 127.0 리뷰 단어 개수 제 3 사분위: 284.0 통계를 살펴보면 평균이 233개, 최댓값은 2470개의 단어를 가지고 있다. 그리고 3사분위 값이 284개로 리뷰의 75%가 300개 이하의 단어를 가지고 있음을 확인 할 수 있다. 특수문자 및 대/소문자 비율 물음표가있는 질문: 29.55% 마침표가 있는 질문: 99.69% 첫 글자가 대문자 인 질문: 92.84% 대문자가있는 질문: 99.59% 숫자가있는 질문: 56.66% 결과를 보면 대부분 마침표를 포함하고 있고, 대문자도 대부분 사용하고 있다. 따라서 전처리 과정에서 대문자의 경우 모두 소문자로 바꾸고 특수 문자의 경우 제거해야 한다. 이 과정은 학습에 방해가 되는 요소들을 제거하기 위함이다. 데이터 전처리 어떤 방향으로 전처리해야 할지 결정하기 위해 데이터 하나를 자세히 보자. "With all this stuff going down at the moment with MJ...bad m'kay....I hope he is not the latter." 우선 과 같은 HTML 태그와 , 같은 특수문자가 포함되어 있는것을 확인할 수 있는데, 이는 일반적으로 문장의 의미에 크게 영향을 주지 않기 때문에 BeautifulSoup과 re.sub을 이용해 제거하자. With all this stuff going ... I hope he is not the latter ['stuff', 'going', 'moment', 'mj', 'started', 'listening',,,'hope', 'latter'] stuff going moment mj started listening ... iars hope latter 전체 데이터에 적용하기 위해 함수화 하자. 'stuff going moment mj started listening ... sickest liars hope latter' 위와 같이 하면 각 리뷰가 텍스트가 아닌 인덱스의 벡터로 구성될 것이다. [404, 70, 419, 8815, 506, 2456, 115, 54, 873, ,,, ,18688, 18689, 316, 1356] 전체 데이터가 인덱스로 구성됨에 따라 각 인덱스가 어떤 단어를 의미하는지 확인할 수 있어야 하기 때문에 단어 사전이 필요하다. 총 74,000개 정도의 단어 이다. 단어 사전뿐 아니라 전체 단어의 개수도 이후 모델에서 사용되기 때문에 저장해 둔다. Shape of train data: (25000, 174) 패딩 처리를 위해 함수를 사용하였다. 최대 길이를 174로 설정한 것은 앞서 단어 개수의 통계를 계산했을 때 나왔던 중간값이다. 보통 평균이 아닌 중간값을 사용하는 경우가 많은데, 일부 이상치 데이터로 인해 평균값이 왜곡될 우려가 있기때문이다. 패딩 처리를 통해 데이터의 형태가 25,000개의 데이터가 174라는 길이를 동일하게 가지게 되었다. Shape of label tensor: (25000,) numpy 배열로 만든 후 라벨의 형태를 확인해 보면 길이가 25,000인 벡터임을 확인할 수 있다. 이렇게 라벨까지 numpy 배열로 저장하면 모든 전처리 과정이 끝난다. 원본 데이터를 벡터화하는 과정을 그림을 통해 이해해 보자. !oorigin-vector 이제 전처리한 데이터를 이후 모델링 과정에서 사용하기 위해 저장을 하도록 하자. test 데이터도 동일하게 저장하자. 다만 test 데이터는 라벨 값이 없기 때문에 라벨은 따로 저장하지 않아도 된다. 추가로 test 데이터네 대해 저장해야 하는 값이 있는데 각 리뷰 데이터에 대해 리뷰에 대한 값을 저장해야 한다. test 데이터를 전처리할 떄 한 가지 중요한 점은 토크나이저를 통해 인덱스 벡터로 만들 때 토크나이징 객체로 새롭게 만드는 것이 아니라, 이다. 만약 새롭게 만들 경우 Train 데이터와 Test 데이터에 대한 각 단어들의 인덱스가 달라져서 모델에 정상적으로 적용할 수 없기 때문이다. 지금까지의 결과를 아래와 같은 파일들에 각각 저장을 하였다. >단어 인덱스 사전 및 개수 : data_configs.json >훈련 데이터 : traininput.npy, trainlabel.npy, train_clean.csv >테스트 데이터 : testinput.npy, testclean.csv, test_id.npy 이제 저장된 데이터를 이용해 다음 스탭을 진행하도록 하겠다.
2019년 01월 30일20분ML - 머신러닝?!
Ai인공지능, 머신러닝, 딥러닝에 대해 자세히는모르지만 부분 한번쯤을 어보았을 것이다. 분명 3가지는 차이가 있으며 어떤 차이가 있는지부터 알아보자. 머신러닝은 말 그데로 "기계가 학습?" 그럼 인공지능과 딥러닝은? 마찬가지로 기계학습이라 해도 틀린것은 아니다. 인공지능은 매우 포괄적인 개념으로 가장 많이 사용되는 대중적인 단어이다. 특정 기술 분야 뿐아니라 지능적인 요소를 가진 모든 부분에 대해 부르는 이름이다. 예를들어 게임상의 Bot이나 음성인식을 이용한 검색, 통계를 기반으로한 예측시스템들도 인공지능이라 부른다. 반면 머신러닝은 인공지능 안에서의 특정분야를 지칭하는 단어이다. 그리고 그안에 파생된 기술도 딥러닝 뿐만 아니라 다른 많은 기술도 있다. 사실 기계학습, 인공지능에 대한 연구는 예전부터 존재했지만 발전이 없었고 단지 소수에 연구원들에 의한 주제였기에 대중화 될 수 없었다고 한다. 하지만 풍부한 데이터의 확보, 컴퓨팅 성능향상, 오픈소스 라이브러리로 인해 많은 개발자들이 인공지능 연구에 참여하게 되면서부터 크게 부각이 되기 시작했다. 머신러닝 머신러닝은 데이터로부터 학습하도록 컴퓨터를 프로그래밍하는 과학이다. 스팸필터로 예를 들면 스팸필터는 스팸메일과 일반메일의 샘플을 이용해 스팸메일 구분법을 배울수 있는 머신러닝 프로그램이다. 왜 머신러닝을 사용할까? 스팸 필터를 예로 전통적인 프로그래밍 기법과 비교해서 살펴보자. 스팸필터를 만들기 위해서는.. > 1. 스팸에 어떤 단어즐이 주로 나타나는지? '4U', '신용카드', '무료' 등등 > 2. 발견한 각 패턴을 감지하는 알고리즘을 작성하여 메일을 스팸으로 분류 > 3. 프로그램을 테스트하고 충분한 성능이 나올 때까지 1,2단계를 반복 전통적인 접근 방법에는 문제가 단순하지 않아 규칙이 복잡해지기 때문에 유지보수가 매우 힘들다. 반면 머신러닝 기법을 이용한다면 일반 메일에 비해 스팸에 자주 나타나는 패턴을 감지하여 특정 단어나 구절이 스팸 메일을 판단하는 데 좋은 기준인지 자동으로 학습한다. 따라서 유지보수가 쉬우며 정확도가 더 높다. 만약 스팸 메일 발송자가 '4U'를 포함한 모든 메일이 차단된다는 것을 안다면 '4U' 대신 'For U'를 쓰기 시작할지도 모른다. 전통적인 프로그래밍 방식의 스팸 필터는 'For U'메일을 구분하기 위해 수정이 필요하며 단어를 바꾸면 새로운 규칙을 계속 추가해 주어야 한다. 하지만 머신러닝 기반의 스팸 필터는 사용자가 스팸으로 지정한 메일에 유동 'For U'가 자주 나타나는 것을 자동으로 인식하고 별도 작업 없이도 자동으로 이 단어를 스팸으로 분류한다. !ori-ml 따라서 머신러닝 기술을 적용해서 대용량 데이터를 분석하면 겉으로는 보이지 않던 패턴을 발견할 수 있다. 머신러닝 시스템의 종류 머신러닝 시스템은 크게 세가지로 구분된다. > - 지도학습(Supervised Learning), > - 비지도학습(Unsupervised Learning), > - 강화학습(Reinforcement Learning) !ml-2 그림 출처 : (https://www.techleer.com/articles/203-machine-learning-algorithm-backbone-of-emerging-technologies/) 각각의 어떤 특징들을 가지고 있는지 살펴보도록 하자. 지도 학습(Supervised Learning) "지도학습은 훈련 데이터(Training Data)로부터 하나의 함수를 유추해내기 위한 기계학습의 한 방법이다." - 위키피디아 정답이 있는 데이터를 기반으로 모델을 만들어 새로운 데이터가 들어왔을때 정답을 맞추는 것이다. 지도학습은 크게 분류(Classification)과 회귀(Regression) 두가지로 분류할 수 있다. 분류 : 이진분류(예 or 아니오), 다중분류(고양이, 사자, 새..) 회귀 : 어떤 데이터들의 특징을 토대로 값을 예측(결과값은 실수, 키가 170면 몸게는?) 다음과 같이 지도학습에 사용되는 알고리즘이 있다. k-최근접 이웃 (k-Nearest Neighbors) 선형 회귀 (Linear Regression) 로지스틱 회귀 (Logistic Regression) 서포트 벡터 머신 (Support Vector Machines (SVM)) 결정 트리 (Decision Tree) 와 랜덤 포레스트 (Random Forests) 신경망 Neural networks 지도학습 알고리즘에 대해서는 다음에 다루도록 하겠다. 비지도 학습(Unsupervised Learning) 비지도 학습에 대해 이야기 하기 전에 먼저 지도 학습과 비지도 학습을 비교한 그림을 보자. !supervised-learning-diagram "데이터가 어떻게 구성되었는지를 알아내는 문제의 범주에 속한다." - 위키피디아 지도학습과는 다르게 정답이 없는 데이터를 가지고 컴퓨터를 학습시키는 방법이다. 즉 데이터의 형태(특징?)으로 학습을 진행한다. (흔히 알고 있는 군집화..) 그리고 데이터를 이용해 컴퓨터는 스스로 어떤 관계를 찾아낸다. 비지도학습도 마찬가지로 다음과 같은 알고리즘이 있다 군집 (Clustering) k-평균k-Means 계층 군집 분석Hierarchical Cluster Analysis (HCA) 기댓값 최대화Expectation Maximization 시각화 (Visualization)와 차원 축소(Dimensionality reduction) 주성분 분석 (Principal Component Analysis (PCA)) 커널 (kernel PCA) 지역적 선형 임베딩 (Locally-Linear Embedding (LLE)) 연관 규칙 학습 (Association rule learning) 어프라이어리(Apriori) 이클렛(Eclat) 강화학습(Reinforcement Learning) !reinforce (그림 출처 : Hands-On Machine Learning 도서 - 한빛미디어) 강화학습은 상과 벌이라는 보상을 주며 하도록 학습하는 방식이다. 주로 제어나 게임 플레이 등 상호작용을 통해서 최적의 동작을 학습해야 할 때 많이 사용된다. 데이터가 먼저 주어지는 지도학습이나 비지도학습과 달리 강화학습은 환경이 있고 에이전트가 그 환경 속에서 어떤 액션을 취하고 그 액션에 따른 어떤 보상을 얻게 되면서 학습이 진행된다. 에이전트가 보상을 최대화 하도록 하면서 학습이 진행되기 때문에 데이터 수집까지 포함하는 동적인 개념의 학습을 하게 된다. 우리의 인생으로 비유를 하자면 마치 시행 착오를 겪으며 경험이 쌓이고 경험을 토데로 옳고 그름을 판단을 수 있게 되는 것과 마찬가지다.
2019년 01월 30일10분K-최근접 이웃법 (Nearest Neighbor)
Ai최근접 이웃법은 새로운 데이터를 입력받았을 때 가장 가까이 있는 것이 무엇이냐를 중심으로 새로운 데이터의 종류를 정해주는 알고리즘이다. !knn 그림에서 보는것 처럼 기존 데이터가 파랑색과 주황색으로 데이터가 분류되었다고 한다면 물음표 데이터가 들어왔을때 이 데이터는 어떤 색상으로 분류가 되어야 할까? 최근접 이웃법 말그대로 가까운 것에 따른 분류이기 때문에 주황색으로 분류할 것이다. 정말 간단하고 직관적인 알고리즘 이다. 하지만 단순히 가장 가까이에 있는 것으로 분류를 하는것이 문제가 되는 경우도 있다. 다음의 경우를 살펴 보자. !knn 이번에는 문제가 약간 복잡해 진다. 이론대로라면 가장 가까운 것은 파란색이기 때문에 파랑색으로 분류를 해야 할것 같지만, 주변을 보면 대부분 주황색이 보인다. 왠지 파랑색으로 분류하면 안될 것 같다는 생각이 든다.... 단순히 가까운 것에 따른 분류에서 이제 주변에 있는 몇개의 것들 중에 많은 것으로 분류하게 되면 이 문제는 어느정도 해결이 된다. 물음표 주변(큰원)에는 4개의 데이터가 있는데 그중 세개가 주황색, 한개가 파란색 이다. 따라서 물음표는 주황색으로 분류한다. 그렇다면 주변의 범위 즉 주변 데이터의 갯수에 대한 기준이 있어야 할 것 같은데.. 위에서는 4개로 정하였다. 즉 K=4가 되는 최근접 이웃법이다. !knn 만약 K=1로 한다면 처음 정의하였던 것처럼 물음표는 가장 가까운 한개의 요소만 바라보게 될 테니 파랑색으로 분류를 하게 될 것이다. 즉, K의 값에 따라 물음표가 어느 범주로 분류 될 것인지가 결정 된다. 최근접 이웃법 앞에 K라는 단어가 붙어 KNN알고리즘이라고 하는 이유를 이제야 알 것 같다. 그럼 과연 K는 몇이어야 좋은 것일까? 최선의 K값을 선택하는것은 데이터마다 다르게 접근해야 한다. 실습 간단하게 mglearn 라이브러리에 있는 데이터셋을 이용하여 실습해 보자. (mglearn : 그래프와 데이터셋을 손쉽게 다루기 위한 샘플데이터 라이브러리) 결과 !knn 즉 K=3으로 할 경우 테스트 데이터가 어느 분류로 될지를 보여주는 그래프이다. 앞에서 설명했듯이 가장 가까운 3개의 점들에 의해 분류가 결정이 된다. 여기서는 세개의 별이 테스트로 들어온 데이터고 결과적으로는 두개는 주황색, 한개는 파랑색으로 분류가 되었다. 결과 !knn 만약 즉 K=1으로 할 경우 다른 결과를 얻게 된다. mglearn의 make_forge 함수를 이용하여서 data와 target을 x와 y에 대입하여 해당 데이터로 모델을 만들고 검증해보자. 결과 !knn 이 말은 모델이 테스트 데이터셋에 있는 데이터들중 86%를 제대로 맞췄다는 의미이다. 실제 핵심 로직은 sklearn에서 제공해 주고 있기 때문에 코드는 굉장히 간단해 보일 것이다. K-NN알고리즘은 지도학습의 분류 알고리즘의 하나로 로직이 간단하여 구현하기 쉽다. 하지만 학습 모델이 따로 없고, 전체 데이터를 스캔하여 데이터를 분류하기 때문에 데이터의 양이 많아지면 분류 속도가 현저하게 느려진다. 그래서 이라고도 한다.
2018년 02월 14일6분데이터와 알고리즘
Ai!ml-1 머신러닝은 어떤 로 어떤 학습 을 사용할 것인가를 결정하는 작업이라고 할수 있다. 여기서 문제가 될수 있는 나쁜 알고리즘과 나쁜 데이터에 대해 알아보도록 하자. 충분하지 않은 데이터 어린아이에게 사과에 대해 알려주려면 사과를 가리키면서 ‘사과’라고 말하기만 하면 된다(아마도 이 과정을 여러 번 반복해야 하겠지만..). 그러면 아이는 결국 색깔과 모양이 달라도 모든 종류의 사과를 구분할 수 있게 된다. 하지만 머신러닝에서 사과를 구분할 수 있도록 알고리즘이 잘 작동하려면 데이터가 많아야 한다. 아주 간단한 문제에서 조차도 수천 개의 데이터가 필요하고 이미지나 음성 인식 같은 복잡한 문제라면 수백만 개가 필요할지도 모른다. >"It's not who has the best algorithm that wins. It's who has the most data" AI 전문가이자 스탠포드 교수인 앤듀르 응(Andrew Ng)의 말이다. 정교한 알고리즘을 설계하는 것 이상으로 많은 데이터를 이용하는 것이 효과적이다. 정교한 알고리즘과 많은 데이터가 가장 정확한 결과를 얻을 수 있겠지만, 시간과 돈을 알고리즘 개발에 쓰는 것과 데이터를 잘 활용하는것 사이의 트레이드 오프에 대해 다시한번 생각해 봐야 한다는 것이다. !ml-data (참고 논문 : 믿을 수 없는 데이터의 효과 http://goo.gl/KNZMEA) 대표성 없는 학습 데이터 일반화가 잘되려면 우리가 일반화하기 원하는 새로운 사례를 학습 데이터가 잘 대표하는 것이 중요하다. 대표성을 가지는 데이터를 사용하는 것이 가장 좋지만, 결코 쉬운일은 아니다. 샘플이 작으면 샘플링 잡음(Sampling Noise)이 생기고, 표본 추출 방법이 잘못되면 대표성을 띄지 못할 수 있습니다. 이를 샘플링 편향(Sampling Bias)이라고 한다. 샘플링 편향 사례 샘플링 편향에 대한 유명한 사례중 랜던(Randon)과 루즈벨트(Roosevelt)가 경쟁했던 1936년 미국 대통령 선거에서 Literary Digest 잡지사가 천만 명에게 우편물을 보내 수행한 대규모 여론조사가 있다. 그 당시 240만 명의 응답을 받았고 랜던이 선거에서 57% 득표를 얻을 것이라고 높은 신뢰도로 예측했지만, 루즈벨트가 62% 득표로 당선되었다. 문제는 Literary Digest의 샘플링 방법에 있었다고 한다. 첫째, 여론조사용 주소를 얻기 위해 전화번호부, 자사의 구독자 명부, 클럽 회원 명부 등을 사용했다. 이런 명부는 모두 공화당(따라서 랜던)에 투표할 가능성이 높은 부유한 계층에 편중된 경향이 있다. 둘째, 우편물 수신자 중 25% 미만의 사람이 응답했다. 이는 정치에 관심 없는 사람, Literary Digest를 싫어하는 사람과 다른 중요한 그룹을 제외시킴으로써 역시 표본을 편향되게 만들게 된 것이다. 특히 이러한 종류의 샘플링 편향을 비응답 편향(nonresponse bias)이라고 한다. 낮은 품질의 데이터 학습 데이터가 에러, 이상치, 잡음으로 가득하다면 머신러닝 시스템이 내재되어 있는 패턴을 찾기 어려울 수 있다. 그 만큼 데이터의 전처리가 매우 중요하다. 한 설문조사에서는 데이터과학자의 80% 시간을 데이터 수집 및 전처리에 사용한다고 한다. 아마도 전처리 과정은 특히나 지루하고 반복 작업의 연속이라 고단하고 시간도 많이 들어어가게 된다. !clean-data 관련 없는 특성 학습 데이터에 관련 없는 특성이 적고 관련 있는 특성이 충분해야 학습을 진행할 수 있다. 훈련에 사용할 좋은 특성들을 찾아야 하며, 특성을 찾기 위해서는 아래 사항을 고려 해야 한다. 특성 선택 (Feature Selection) : 가지고 있는 특성 중에서 학습에 가장 유용한 특성을 선택. 특성 추출 (Feature Extraction) : 특성을 결합하여 더 유용한 특성을 만듬. 혹은 새로운 데이터를 수집하여 새로운 특성을 만듬. 지금까지 나쁜 데이터의 사례를 살펴보았고 이제 나쁜 알고리즘의 경우를 살펴보자. 학습 데이터의 과대적합 모델이 학습 데이터에 너무 잘 맞지만 일반성이 떨어진다는 의미이다. 훈련 데이터에 있는 잡음의 양에 비해 모델이 너무 복잡할 때 발생하기 때문에 좀더 단순화 시킬 필요가 있다. 파라미터 수가 적은 모델 선택, 훈련데이터에 특성수를 줄임, 모델에 제약을 가하여 단순화 훈련 데이터를 더 많이 모음 훈련 데이터의 잡음을 줄임 학습 데이터 과소적합 모델이 너무 단순해서 데이터의 내재된 구조를 학습하지 못한것을 의미한다. 파라미터가 더 많은 강력한 모델 선택 더 좋은 특성 제공(특성 엔지니어링) 모델의 제약을 줄임(규제 하이퍼파라미터를 감소) 좋은 모델을 만들기 위해서는 테스트와 검증이 필요하다. 이는 일반화 오류를 최소화 하기위한 방법이기도 하다. 일반적으로 모델을 만들기 위해서는 데이터를 훈련셋, 검증셋, 테스트셋으로 나누어 학습을 진행을 한다. !ml-data-set 마치며... "Big Data is the next Natural Resource..." 과거 원유가 가장 중요한 자원이었다고 한다면 이제는 데이터가 가장 중요한 자원일 것이다. 그런데 데이터는 과거의 자원들과는 아주 많이 다르다. 데이터는 지속적인 재생, 고갈되지 않는.. 그리고 스스로 증폭하는 새로운 자원이다. 이렇게 중요한 데이터를 올바르게 잘 활용을 하는것이 정말 중요할 것이다. 참고한 자료 강연 : 4차 산업혁명시대 어떻게 살 것인가? - EBS 미래강연Q 도서 : Hands-On Machine Learning - 한빛미디어
2018년 02월 14일9분