PROJECT/텍스트 마이닝을 활용하여 문자 대화내용 분석

Naver sentiment movie corpus(NSMC)를 활용 감정분석

HeyTeddy 2023. 4. 18. 18:04
반응형

한국어 데이터 중 가장 유명한 감정분석 데이터는 naver sentiment movie corpus이다.

 

google에 nsmc검색하여 github을 들어간다.

https://github.com/e9t/nsmc

 

GitHub - e9t/nsmc: Naver sentiment movie corpus

Naver sentiment movie corpus. Contribute to e9t/nsmc development by creating an account on GitHub.

github.com

 

간단하게 영화가 긍정/부정을 알려준다. 긍정은 9~10점, 부정은 0~4점으로 나누었다.

총 20만개 리뷰 데이터가 있다. (수집된 64만개 데이터 중 샘플링)

훈련 데이터는 15만, 테스트 데이터는 5만개가 존재한다.

1. 데이터 불러오기

우선 데이터를 불러오는 함수를 만들어 보았다.

def read_documents(filename):
    with open(filename, encoding='utf-8') as f: # 인코딩 중요, Windows 사용자
        documents = [line.split('\t') for line in f.read().splitlines()]
        documents = documents[1:] # 첫줄은 카테고리 이름 제거
        
    return documents

train_docs = read_documents("data/nsmc/ratings_train.txt")
test_docs = read_documents("data/nsmc/ratings_test.txt")

print(len(train_docs))
print(len(test_docs))

# 150000
# 50000

파일을 열 때, 윈도우 사용자는 인코딩을 하지 않으면 한국어를 인식이 안될 수 있어서 인코딩은 매우 중요하다.

훈련 데이터 150000개, 테스트 데이터 50000개 확인되었다.

 

이 포스팅 내용에서 사용할 함수(명사 추출, 형태소 추출, 정제 등)를 이전 포스팅 내용에서 가져왔다.

https://kjws0712.tistory.com/106

 

형태소 추출기 만들기

이번 시간에는 형태소 분석기를 제작해 보겠습니다. 형태소 뜻을 다시 집어보면, '어떤 대상 어절을 최소의 의미 단위'라는 뜻합니다. 사용할 형태소 분석기 불러오기(import) 텍스트 전처리 하기(

kjws0712.tistory.com

 

2. 함수 정의

def mecab_nouns(text):
    nouns = []
    
    # 우리가 원하는 TOKEN\tPOS의 형태를 추출하는 정규표현식.
    pattern = re.compile(".*\t[A-Z]+") 
    
    # 패턴에 맞는 문자열을 추출하여 konlpy의 mecab 결과와 같아지도록 수정.
    temp = [tuple(pattern.match(token).group(0).split("\t")) for token in mecab.parse(text).splitlines()[:-1]]
        
    # 추출한 token중에 POS가 명사 분류에 속하는 토큰만 선택.
    for token in temp:
        if token[1] == "NNG" or token[1] == "NNP" or token[1] == "NNB" or token[1] == "NNBC" or token[1] == "NP" or token[1] == "NR":
            nouns.append(token[0])
        
    return nouns


def mecab_morphs(text):
    morphs = []
    
    # 우리가 원하는 TOKEN\tPOS의 형태를 추출하는 정규표현식.
    pattern = re.compile(".*\t[A-Z]+") 
    
    # 패턴에 맞는 문자열을 추출하여 konlpy의 mecab 결과와 같아지도록 수정.
    temp = [tuple(pattern.match(token).group(0).split("\t")) for token in mecab.parse(text).splitlines()[:-1]]
        
    # 추출한 token중에 문자열만 선택.
    for token in temp:
        morphs.append(token[0])
    
    return morphs

def mecab_pos(text):
    pos = []
    # 우리가 원하는 TOKEN\tPOS의 형태를 추출하는 정규표현식.
    pattern = re.compile(".*\t[A-Z]+") 
    
    # 패턴에 맞는 문자열을 추출하여 konlpy의 mecab 결과와 같아지도록 수정.
    pos = [tuple(pattern.match(token).group(0).split("\t")) for token in mecab.parse(text).splitlines()[:-1]]
        
    return pos
    
 def text_cleansing(doc): # 텍스트 정제
    # 한국어를 제외한 글자를 제거하는 함수.
    doc = re.sub("[^ㄱ-ㅎㅏ-ㅣ가-힇 ]", "", doc)
    
    return doc

def define_stopwords(path):
    
    SW = set()
    # 불용어를 추가하는 방법 1.
    # SW.add("있다")
    
    # 불용어를 추가하는 방법 2.
    # stopwords-ko.txt 파일 마지막 줄에 직접 추가하기
    with open(path, encoding='utf-8') as f: # 파일 경로, 파일을 자동으로 open, 함수 끌나면 자동으로 close
        for word in f:    # 각 줄을 져와 집합에 추가
            SW.add(word)
            
    return SW

def text_tokenizing(doc):
    # list comprehension을 풀어서 쓴 코드
    
    tokenized_doc = []

    return [word for word in mecab_morphs(doc) if word not in SW and len(word) > 1]

 

3. 불러온 데이터를 품사 태그를 붙여서 Tokenizing

텍스트 데이터가 20만개가 존재하므로 정제하는데 많은 시간이 걸린다. 한 번 만들고 코드를 다시 실행시켰을 때, 또 정제할 필요없이 불러오게끔 코드를 작성하였다.

from konlpy.tag import Mecab
from konlpy.tag import Okt
import json
import os
import re
from pprint import pprint

import MeCab
import re 

# 형태소 분석기
mecab = MeCab.Tagger()
okt = Okt()

SW = define_stopwords("data/stopwords-ko.txt")

# 텍스트가 20만개로 정제하는데 시간이 걸린다.
# 한 번 만들고 나서는 코드를 다시 실행시켰을 때, 또 정제할 필요없이 불러오게끔 코드

if os.path.exists('train_docs.json'): # 경로가 존재하면 True/False
    with open("train_docs.json", encoding='utf-8') as f: # 불러오기
        train_data = json.load(f)

else:
    train_data = [(text_tokenizing(line[1]), line[2]) for line in train_docs if text_tokenizing(line[1])]
#     train_data = []
#     for line in train_docs:
#         if text_tokenizing(line[1]):
#             train_data.append((text_tokenzing(line[1]), line[2]))
    
    
    with open("train_docs.json", 'w', encoding='utf-8') as f:
        json.dump(train_data, f, ensure_ascii=False, indent='\t') # 탭 구분
        
if os.path.exists('test_docs.json'):
    with open("test_docs.json", encoding='utf-8') as f:
        test_data = json.load(f)
else:
    test_data = [(text_tokenizing(line[1]), line[2]) for line in test_docs if text_tokenizing(line[1])]
    
    with open("test_docs.json", 'w', encoding='utf-8') as f:
        json.dump(test_data, f, ensure_ascii=False, indent='\t')

pprint(train_data[0]) # pretty print
pprint(test_data[0])


# [['진짜', '짜증', '네요', '목소리'], '0']
# [['GDNTOPCLASSINTHECLUB'], '0']

결과가 나왔지만, 별 큰 의미는 없어보인다. '진짜', '짜증', '목소리' 키워드를 봐서 영화 내용이 부정적이었다는 것을 예측할 수 있다.

 

4. NLTK를 이용한 histogram 분석

NLTK는 데이터 분석을 하기 위해 기본적인 정보들을 확인할 수 있다. NLTK 라이브러리를 이용하여 전처리 학습을 해보았다.

import nltk

total_tokens = [token for doc in train_data for token in doc[0]]
# 이중 for문, doc[0] = 리뷰, doc[1] = 레이블

print(len(total_tokens)) # 토큰 총 개수

# 1206841

훈련 데이터의 0번째 인덱스는 리뷰, 1번째 인덱스는 레이블을 의미. 리뷰 토큰이 약 120만개 있다.

 

text = nltk.Text(total_tokens, name = 'NMSC') # Text : 처리
print(len(set(text.tokens)))
pprint(text.vocab().most_common(10))

# 51722
[('영화', 57614),
 ('..', 22813),
 ('는데', 11543),
 ('너무', 11002),
 ('정말', 9783),
 ('으로', 9322),
 ('네요', 9053),
 ('재밌', 9022),
 ('지만', 8366),
 ('진짜', 8326)]

text변수의 토큰을 집합 함수를 사용하여 중복된 값을 제거하였고, 토큰 개수가 가장 많이 등장한 10개 토큰과 토큰 개수를 나타냈다. 명사 추출을 하지 않아서 결과(빈도, 프리퀀시)가 큰 의미가 있어보이진 않는다. 의미있는 내용이 제거될 수 있기 때문에 보통 소셜 데이터는 전처리를 먼저 하지 않고 본다.

 

결과를 histogram으로 그려보았다.

import matplotlib.pyplot as plt
import platform
from matplotlib import font_manager, rc
%matplotlib inline

path = "c:/Windows/Fonts/malgun.ttf"
if platform.system() == 'Darwin': # mac
    rc('font', family = 'AppleGothic')
elif platform.system() == 'Windows': # windows
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family = font_name)
else:
    print('Unknown system... sorry----')
    
plt.figure(figsize = (16,10))
text.plot(50)

 

matplotlib inline을 사용할 떄 한글 글꼴을 설정해야 한다. 미국에서 제작하다 보니 한글 글꼴은 필수.

 

5. WordCloud 그리기

우선 WordCloud를 그리기 위해 WordCloud를 설치해야 한다.

!pip install wordcloud
from wordcloud import WordCloud

data = text.vocab().most_common(50)

# for windows : font_path = 'c:/Windows/Fonts/malgun.ttf'
wordcloud = WordCloud(font_path = 'c:/Windows/Fonts/malgun.ttf',
                      relative_scaling = 0.2,
                      # stopwords = STOPWORDS,
                      background_color = 'white',
                      ).generate_from_frequencies(dict(data))
plt.figure(figsize=(16,8))
plt.imshow(wordcloud)
plt.axis('off')
plt.show()

WordCloud도 matplotlib inline처럼 한글 폰트를 사용해야 한다. 보통 WordCloud 할 때 형태소 분석 결과보다 명사 분석 결과를 많이 쓴다. mecab_morphs대신 mecab_nouns를 사용하면 훨씬 더 낫다.

반응형