티스토리 뷰

랩실에서는 여전히 크롤링이 진행 중이고, 실험 정신을 발휘해 보기 위해 약 8000개의 데이터만 뺴왔다. 눈으로 쓰윽 봤는데, 아... 분석이 될 데이터 같지는 않은데 해보자😎😎

[Word Tokenization] : 단어 토큰화

쉽게 생각해서 단어 뭉탱이로 쪼개는 것을 뜻한다. 물론 Sentence Tokenization을 할 경우 그 뭉탱이의 단위가 문장이 된다는 차이점이 있지만 필요에 따른 기준에 의해 쪼개는 것을 뜻하는 것은 변함 없다. Word Tokenization(WT)의 예시로는 punctuation을 제거하고 space를 기준으로 잘라내는 것이라고 할 수 있겠다. 그러나 이런 식의 어절 토큰화는 한국어 NLP(Natural Language Process)에는 부적합하다. 왜냐하면 1)한국어는 조사가 띄어쓰기 없이 바로 붙어있고 2)띄어쓰기를 잘 지키지 않기 때문이다. 그래서 한국어를 토큰화하는 과정에 쓰일 알고리즘은 섬세해야 한다.

품사 부착(Part-of-speech tagging)은 단어의 의미를 파악하기 위한 용도로 쓰일 수 있다. '못'이라는 단어는 nail이라는 뜻으로 쓰이기도 하지만 부정부사로 쓰이기도 한다. 즉 단어가 어떤 품사로 쓰였는지 보면 뜻을 파악하는데 중요한 지표가 된다. KoNLPy라는 package에는 다섯가지의 형태소 분석기가 있다. 속도 면에서 Mecab이 빠르던데 윈도우에서는 지원을 안한다고 한다. 내 주 노트북은 맥북이더라도 랩실 컴퓨터는 윈도우니까 메캅은 못쓰는 것이고... 자연어 분석에 대해서는 현재로서 아는 것이 없으니 잘 아시는 (것 같은) 분께서(Thx to 돌팽이.님) 한나눔과 꼬꼬마는 각각 분석 품질과 시간에서 아쉬운 점이 보인다고 하셨으니 나에게 남은 건 코모란과 Okt인데 걸리는 시간으로 비교를 해봐야 할 것 같다. 아,  Kkma가 제외되는 이유는 공식문서에 Kkma는 긴 문장과 띄어쓰기가 없을 때 분석력이 매우 떨어진다고 언급되어있기 때문.

macOS Mojave(Ver 10.14.3) - Intel i5 2.3GHz - 16G 2133MHz LPDDR3

현재 df는 ['TAG', 'TITLE', 'CONTENT']로 3가지 필드로 구성이 되어 있으며 각각 네이버 카페 내의 ['말머리', '제목', '내용']을 뜻한다. 15000개의 레코드로 구성되어있다. Title만 돌려서 그런지 대체적으로 소요시간이 길진 않은 것 같다. 본문 내용까지 포함하면 얼마나 걸릴지... 말 나온 김에 Okt와 Komoran으로만 비교를 해봐야 겠다.(돌리는데 맥북에서 소리난다... 아가 미안)

동일사양

속도 만 본다면 코모란을 고르겠고, 코모란이 오탈자 및 띄워쓰기가 제대로 지켜지지 않았을 때도 어느 정도 분석 품질이 보장 된다고 하니 배제할 이유는 없지만 막상 명사들만 뽑아보면 만족스럽거나 그렇지는 않다. 형태소 분석 결과를 Okt와 비교해보니 Okt가 더 잘 쪼개는 것 같지만 전문가가 아니라서 확신할 수는 없다. 결국 두 형태소 분석기를 다 사용해서 비교를 해봐야 할 것 같다. 챗봇을 만들고 있지만 새로운 input data가 될 텍스트에 대해서 속도 측면에서 Komoran과 Okt는 큰 차이가 없을 것 같다.

토큰화를 하다보면 선택의 기로에 놓일 수 있다. 가령 (온점)의 용도가 4.5와 같이 유의미할 수 있는 소수를 나타내기 위해 쓰였다면 온점을 단순 구두점으로 처리하면 안된다. 이런 선택의 기로에 놓일 땐 데이터를 사용할 용도에 영향을 미치지 않는 방향으로 기준을 정하면 된다. 내게 소수점으로서의 .이 별 의미가 없다면 제외시켜도 분석에 큰 영향을 미치지 않는 것이다.

[Cleansing & Normalization] : 정제 & 정규화

텍스트 데이터의 정제와 정규화의 목적은 노이즈 데이터를 제거하고 표현 방법이 다른 단어들을 통합해서 같은 단어로 만드는 것에 있다. 노이즈 데이터라고 하면 자연어가 아니거나, 특수 문자와 같이 무의미한 문자이거나, 분석의 목적과 맞지 않은 단어들이 해당된다. 산모들의 질문 데이터를 모을 때 개월 수를 'TAG'로 달아 놓았기 때문에 '28주차'와 같은 단어는 제거해도 괜찮은 것이다. 이렇게 불필요한 단어들을 제거할 때 쓸 수 있는 기법으로는 1) 불용어 제거 2) 등장 빈도 적은 단어 제거가 있고 노이즈 데이터들에 패턴이 있다면 정규 표현식을 활용하여 보다 쉽게 할 수 있다.

불용어(Stopword) 제거

'불용어'는 문장 내에서 별 역할을 하지 않는 단어를 뜻 한다. 보편적으로 선택할 수 있는 한국어 불용어 리스트가 이 링크에 있다는데, 목록을 살펴보니 함부로 적용해서 될 목록은 아닌 것 같다. 그 대신 불용어는 사용자가 직접 정의할 수도 있다. 불용어 제거는 모인 데이터에 대한 파악을 조금 더 하고 차차 해나가기로ㅎㅎ 가령 [임신3개월까지]인 임산부들의 질문에 대해서는 '임신 초기'라는 것은 기정 사실이다. 따라서 이 경우에 대해서는 '초기'라는 단어는 불용어가 되는 것이다. 실제로 최빈명사의 목록에서 개수를 추출해보면 '초기'라는 단어가 9번째로 많이 나온 단어이다. 

등장 빈도 적은 단어(Rare words) 제거

등장 빈도가 어떻던 단어 수를 줄여야겠다고 체감한 것은 komoran으로 코드를 돌리다 다음과 같은 에러를 맞닥뜨리고 나서다. java.lang.OutOfMemoryError : Java heap space라고 뜨니 힙 메모리가 부족한가보다 싶었는데... setUserDic()을 한 것도 아닌데 왜?질문을 해결해야겠더라. 사실 원인을 찾을 수 있었던 것은 아니지만 아마 CONTENT부분에서 call이 너무 많이 일어났기 때문인 것 같다. (아니라면 댓글로 남겨주세요!!) 무튼, 명사들의 빈도를 구해서 지울 건 지우자.

제목과 내용을 합쳐서 하나의 텍스트로 만들고, 가장 많이 나온 20개의 단어를 record 별로 뽑고 나온 빈도 수를 뽑아봤다. 아래 코드로 진행했는데 쥬피터에서 작성한 것이라 편집기로 가면 돌아갈지는 모르겠네 허허

import pandas as pd
from tqdm import trange
from collections import Counter
from konlpy.tag import Okt

text = df3['TITLE_2'] + " " + df3['CONTENT_2']

okt = Okt()
top20_nouns=[]
temp_total = (i for i in text)
for i in trange(len(text)):
    t_t = next(temp_total)
    c = Counter(okt.nouns(t_t))   
    top20_nouns.append([word for word,cnt in c.most_common(20)])

# record 별로 뽑힌 20개의 최빈명사들을 nouns에 합치기
nouns = []
for word_chunk in top20_nouns :
    for word in word_chunk :
        nouns.append(word)
nouns.sort() # ㄱ-ㅎ 정렬

unique_nouns = list(set(nouns)) # 명사 별로 한개씩만 남김(명사 별 개수 세기 위함)
unique_nouns.sort() # 정렬

noun_count = [] # 명사 별 개수 들어감
for noun in unique_nouns :
    count = 0
    for word in nouns :
        if count != 0 and word != noun :
            break;
        if word == noun : 
            count = count + 1
    noun_count.append(count)

# Data Frame 으로 만들기
nouns_df = pd.DataFrame()
nouns_df.loc[:, 'NOUN'] = unique_nouns
nouns_df.loc[:, 'COUNT'] = noun_count

# csv 파일로 별도 저장
nouns_df.to_csv('NOUNS.csv')

이렇게 뽑고 sort하면 정렬이 되는데, 이렇게 보고나니까 내가 가지고 있는 데이터에 대한 insight가 조금 생기는 듯 하다. 너무 많이 나온 단어도 지워야 되고 너무 적게 나온 단어도 지워야 한다는게 무슨 말인지 알 수가 있다. 그런데 이렇게 해놓고 나니까 20개씩 뽑고 단어 별 빈도 수를 구하는 것이 아니라 아예 모든 명사를 뽑고 거기서 빈도 수를 구하는 것이 맞을 것 같다. 그건 다음 글에 써놓아야겠다.

정규 표현식 (Regular Expression)

여태까지는 replace 함수를 주로 썼던 것 같은데 정규식을 쓰면 조금 짧게 코드를 쓸 수 있을 것 같아 한번 써봤다. 정규식만의 장점이 있는지는 추후에 더 찾아보는 것으로 하고 내가 모은 데이터로는 .(dot), 한글, 숫자, 띄워쓰기를 제외하고는 다 ' '로 대체하게끔 해놓았다.

그래 나에게 '.'은 필요한 존재였어

여담) 지난 학기 리눅스 수업 들으면서 정규표현식 배우고, 신기해서 맥북으로 실습해본다고 뚱땅뚱땅 거리다가 뭘 잘못 입력해서 모든 문서 파일을 다 날렸었다. 인간은 멍청해서 똑같은 실수를 반복하지^^ 안드로이드 스튜디오의 잔재들을 싹 다 지워보고자 터미널에서 rm 잘못썼다가 재시동하면 바탕화면이 모하비 사막으로 바뀌고 Macintosh HD가 나오고 Finder가 텅텅 비는 마술 쨔라랑. 이 이후로 나스에 백업하는  습관이 생겼다. 백업이 생명이다 Essence of life.

표제어 추출 (Lemmatization) & 어간 추출 (Stemming)

표제어 추출과 어간 추출이라는 방법도 단어 수를 줄이기 위해 사용할 수 있다. '가려워요', '가려움', '가렵다' 등을 대체해야 하는 건데, 아직 표제어라던지 어간보다 걱정할 것이 산더미다. 기회가 되면(아니 무조건 맞닥뜨릴 문제이지만) 정리를 해야겠다.

 

참고 ) 텍스트 전처리를 위해 Wiki Docs의 '딥 러닝을 이용한 자연어 처리 입문'을 참고함

참고) 한국어 형태소 분석기 성능 비교 참고

donaricano-btn

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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 29 30
글 보관함