티스토리 뷰
이번엔 multi classification을 해볼 것이다.
내가 가진 것은 페이스북 게시글로, 약 4000개 정도 된다.
supervised learning을 진행하려고 labeling을 쭉 하는데 너무 많아서 2000개만 하고 일단 돌려봤다.
labeling을 하는데, 내가 분류를 해도 헷갈리는 것이나 여러 항목에 걸쳐 있는 글들이 많았다.
경계도 모호하고 분류도 완벽하지 않다.
아마 학습시킬 때도 문제가 될 것 같다.
분류를 통합하거나 태그 형식으로 바꾸거나.
마치 도서관에서 일할 때 도서 분류를 하는 느낌이다.
개요
글을 분류하는 데에는 여러가지 방법이 있더라.
검색해보니 Doc2Vec, fasttext, Word2Vec(문장은 단어 vector의 평균으로), LSTM 또는 RNN, CNN 등이 있었다.
그 중 몇 개만 아래 접기에 넣어뒀다.
Doc2vec
https://www.slideshare.net/lucypark/nltk-gensim
https://aileen93.tistory.com/128
fasttext (Word2vec 평균)
http://fbsight.com/t/word2vec-cnn/103388
https://ratsgo.github.io/from%20frequency%20to%20semantics/2017/07/06/fasttext/
https://ratsgo.github.io/natural%20language%20processing/2017/03/08/word2vec/
LSTM (RNN)
CNN
https://ratsgo.github.io/natural%20language%20processing/2017/03/19/CNN/
뭘 해볼까 하다가 Word2Vec은 너무 많이 썼으니 LSTM을 써보기로 했다.
LSTM은 RNN이 역전파 과정에서 손실되는 gradient의 문제점을 고친 알고리즘이다.
수학적으로는 잘 모르겠다...
이 블로그에 너무 잘 나와있다.
본격적으로 순서를 살펴보면,
0. 데이터 추출
1. 자연어처리
2. LSTM
LSTM은 keras를 이용했다.
pip install keras로 설치하면 된다.
0. 데이터 추출
내가 가진 페이스북(문서) 데이터는 2개로 구성되어 있다.
글 내용과 분류.
간단하다.
글 내용은 "헬스장에 블루투스 이어폰 두고가신분 찾아가세요!", "1시에 터미널로 같이 택시 타실 분 있나요?", "[공지] 선형대수학 다음 주 수업은 공강입니다." 등의 내용이다.
나는 8가지 분류로 나눴는데, 0: 분류 불가(내용이 없을 때), 1: 택시 동승, 2: 분실 ... 7: 단순 질문 등이다.
2000개까지 내가 직접 분류했다.
현재 data는 2000개의 글이 있고 각각 내용과 분류, 2가지 정보가 있는 것이다.
1. 자연어처리
data의 첫 번째 컬럼(내용)을 KoNLPy의 Mecab으로 tokenize 한다.
결과는 ['헬스','장','에','블루투스', ... ]와 같이 된다.
from konlpy.tag import Mecab
def tokenize_sentense(text):
mecab=Mecab()
return mecab.morphs(text)
이제 각 단어를 번호에 매핑해야 한다.
['헬스','장','에','블루투스', ... ] -> [1,2,3,4, ...] 형태로 만드는 것이다.
num_list, w2n_dic, n2w_dic = word2num(tokenized_data)
#list_2d는 2차원 list이다. 2000개의 글과 각 글이 tokenized 된 단어들이다.
def word2num(list_2d):
w2n_dic = dict() # word가 key이고 index가 value인 dict
n2w_dic = dict() # index가 key이고 word가 value인 dict. 나중에 번호에서 단어로 쉽게 바꾸기 위해.
idx = 1
num_list = [[] for _ in range(len(list_2d))] # 숫자에 매핑된 글의 리스트
for k,i in enumerate(list_2d):
if not i:
continue
elif isinstance(i, str):
# 내용이 단어 하나로 이루어진 경우, for loop으로 ['단어']가 '단'과 '어'로 나뉘지 않게 한다.
if w2n_dic.get(i) is None:
w2n_dic[i] = idx
n2w_dic[idx] = i
idx += 1
num_list[k] = [dic[i]]
else:
for j in i:
if w2n_dic.get(j) is None:
w2n_dic[j] = idx
n2w_dic[idx] = j
idx += 1
num_list[k].append(w2n_dic[j])
return num_list, w2n_dic, n2w_dic
편한 라이브러리가 있겠지만, 그냥 내가 구현했다.
(편한 라이브러리 아시는 분 알려주세요 ㅠ)
2.LSTM
이제 LSTM에 쓸 training data와 test data로 나눈다.
비율은 8:2다.
(이것도 제가 구현해서 썼어요...)
import np
def divide_data(x, y, train_prop=0.8):
x = np.array(x)
y = np.array(y)
tmp = np.random.permutation(np.arange(len(x)))
x_tr = x[tmp][:round(train_prop * len(x))]
y_tr = y[tmp][:round(train_prop * len(x))]
x_te = x[tmp][-(len(x)-round(train_prop * len(x))):]
y_te = y[tmp][-(len(x)-round(train_prop * len(x))):]
return x_tr, x_te, y_tr, y_te
x는 위에서 번호로 매핑한 단어들의 list가 되겠고(num_list) y는 사전에 분류한 데이터가 되겠다.
LSTM에 쓸 input은 크기가 일정해야 하고, output은 one-hot 벡터여야 한다.
그래서 조금 손을 봐줘야 한다.
from keras.preprocessing import sequence
from keras.utils import np_utils
x_tr = sequence.pad_sequences(x_tr, maxlen=50) # 50개 넘으면 자르고 안되면 0으로 채움
x_te = sequence.pad_sequences(x_te, maxlen=50)
y_tr = np_utils.to_categorical(y_tr) # one_hot으로 변형
y_te = np_utils.to_categorical(y_te) # 3 -> [0,0,0,1,0, ..]
이건 keras에서 함수를 지원해준다.
너무 감사합니다.
아, maxlen을 50으로 만든 이유는, 데이터의 평균 단어(token) 수가 79개였는데 편차도 크고 짧은 건 20개 수준이어서 50개가 적당해보였다.
자신의 상황에 맞는 수를 정하면 될 것 같다.
이제 LSTM 모델을 구현해보자.
keras를 사용했다.
(그게 예제로 있었어서... 파이토치나 텐서플로우도 잘 나온 게 있었다면 그걸로 했을 듯)
from keras.models import Sequential
from keras.layers import Dense, LSTM, Embedding
# x_tr,x_te,y_tr,y_te
# words_num은 총 단어의 종류. +1을 해준 이유는 단어 수가 적은 글의 경우 빈 칸에 0이 있기 때문에.
model = Sequential()
model.add(Embedding(words_num+1, len(x_tr[0]))) # 사용된 단어 수 & input 하나 당 size
model.add(LSTM(len(x_tr[0])))
model.add(Dense(len(y_tr[0]), activation='softmax')) # 카테고리 수
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# 어... 그냥 따라했음...
history = model.fit(x_tr, y_tr, batch_size=100, epochs=20, validation_data=(x_te,y_te))
# epoch 수를 적당히 조절하길. 너무 많이 하면 끝이 안남.
# history에서 출력되는 내용
Epoch 14/20
100/1673 [>.............................] - ETA: 1s - loss: 0.4775 - acc: 0.820
200/1673 [==>...........................] - ETA: 1s - loss: 0.4224 - acc: 0.840
300/1673 [====>.........................] - ETA: 1s - loss: 0.4116 - acc: 0.840
400/1673 [======>.......................] - ETA: 1s - loss: 0.3728 - acc: 0.860
500/1673 [=======>......................] - ETA: 1s - loss: 0.3712 - acc: 0.856
600/1673 [=========>....................] - ETA: 1s - loss: 0.3919 - acc: 0.845
700/1673 [===========>..................] - ETA: 1s - loss: 0.3990 - acc: 0.841
800/1673 [=============>................] - ETA: 1s - loss: 0.3951 - acc: 0.841
900/1673 [===============>..............] - ETA: 0s - loss: 0.4028 - acc: 0.838
1000/1673 [================>.............] - ETA: 0s - loss: 0.4061 - acc: 0.836
1100/1673 [==================>...........] - ETA: 0s - loss: 0.4063 - acc: 0.834
1200/1673 [====================>.........] - ETA: 0s - loss: 0.3967 - acc: 0.838
1300/1673 [======================>.......] - ETA: 0s - loss: 0.3886 - acc: 0.843
1400/1673 [========================>.....] - ETA: 0s - loss: 0.3866 - acc: 0.843
1500/1673 [=========================>....] - ETA: 0s - loss: 0.3817 - acc: 0.845
1600/1673 [===========================>..] - ETA: 0s - loss: 0.3740 - acc: 0.850
1673/1673 [==============================] - 2s 1ms/step - loss: 0.3748 - acc: 0.8500
- val_loss: 0.6193 - val_acc: 0.7895
epoch마다 val_acc를 출력해준다.
20 epoch가 지났을 때 정확도는 82.30%였다.
생각보다 낮았다.
데이터를 살펴보니 내가 분류할 때도 애매했던 것들은 제대로 학습이 안 됐다.
상대적으로 경계가 명확한 분류는 잘 됐다.
다음은 카테고리 별 정확성이다.
즉, 결과를 binary( 1을 1로 예측했는가 & 1이 아닌 것을 1이 아닌 것으로 예측했는가)로 만들었을 때 정확도이다.
분류 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
정확도 | 100% | 99.28% | 94.26% | 94.50% | 96.41% | 92.82% | 91.87% | 95.45% |
무슨 의미가 있겠냐마는...
1의 경우, 내가 확실하게 분류할 수 있는 카테고리였다.
역시나 인공지능도 잘 분류했다.
내가 가장 헷갈렸던 것이 5,6이었다. 둘은 경계가 모호했다.
내가 그래서 얘도 헷갈렸나보다.
그래서 5,6을 통합하고, 3,7을 통합했더니 정확도가 87.08%로 올라갔다.
그래도 90%이 안되긴 하지만 카테고리가 여러 개인 것을 감안하면.... ㅎ...
끝!
다음엔 분류 방식을 조금 바꿔봐야겠다.
'인공지능' 카테고리의 다른 글
Memory-based Collaborative Filtering (협업 필터링) 구현 - GISTALK 강의평가 데이터 (0) | 2019.05.11 |
---|---|
Word2Vec (gensim), KMeans 이용해서 unsupervised learning하기 (자연어처리) (0) | 2019.04.17 |
Word2Vec (gensim), pytorch 이용해서 binary classification하기 (자연어처리) (0) | 2019.03.15 |
KoNLPy Mecab 오류 : NameError: name 'Tagger' is not defined (4) | 2019.01.20 |
- Total
- Today
- Yesterday
- memory-based
- KAKAO
- 협업필터링
- IBCF
- python3.8
- Collaborative Filtering
- windows subsystem for lunux
- 아이템기반
- matrix market
- pytorch
- 메모리기반
- ifkakao
- pythonpython
- item-based
- javascrip
- MachineLearning #KMenas #KoNLPy #Word2Vec #AI #ML #인공지능 #Unsupervised #Clustering #Classification
- MBCF
- 추천
- WSL2
- matrix factorization
- Python
- django
- django3
- buffalo
- django mysql database sqlite
- 서버환경
- queryset
- Supervised Learning #KoNLPy #Keras #NLP #자연어처리 #글 분류 #LSTM
- gensim
- n core setting
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |