문서 행렬 ( DTM )

  • Document Term Matrix
  • 어휘의 빈도수를 파악하고 기록하여 훈련 데이터를 만듦

셋팅

from google.colab import drive
drive.mount('/content/gdrive')

!apt-get update
!apt-get install g++ openjdk-8-jdk

!pip install JPype1
!pip install rhinoMorph

데이터 로딩

def read_data(filename, encoding='cp949'): # 읽기 함수 정의
  with open(filename, 'r', encoding=encoding) as f:
    data = [line.split('\t') for line in f.read().splitlines()]
    data = data[1:] # txt 파일의 헤더(id document label)는 제외하기
  return data

def write_data(data, filename, encoding='cp949'): # 쓰기 함수도 정의
  with open(filename, 'w', encoding=encoding) as f:
    f.write(data)
data = read_data('/content/gdrive/My Drive/pytest/ratings.txt', encoding='cp949')
#data = read_data('ratings.txt', encoding='cp949')

 

훈련 데이터와 테스트데이터 자동 분리

# 훈련데이터와 테스트 데이터 분리 (자동)- 7.5 + 2.5
data_text = [line[1] for line in data]    # 데이터 본문
data_senti = [line[2] for line in data]   # 데이터 긍부정 부분

from sklearn.model_selection import train_test_split  # 본문과 라벨을 각각 분리
train_data_text, test_data_text, train_data_senti, test_data_senti = train_test_split(data_text, data_senti, stratify=data_senti)

# Counter 클래스를 이용해 각 분류가 훈련데이터와 테스트데이터에 같은 비율로 들어갔는지 확인해 본다
from collections import Counter
train_data_senti_freq = Counter(train_data_senti)
print('train_data_senti_freq:', train_data_senti_freq)

test_data_senti_freq = Counter(test_data_senti)
print('test_data_senti_freq:', test_data_senti_freq)

훈련데이터와 테스트 데이터를 분리하는데 70, 30 인지 80,20인지 싸워서 

그냥 자동이면 75% 25%를 사용한다고 한다. 

 

  • 데이터 본문과 긍부정 부분을 분리를 한다. 
  • 본문과 라벨을 각각 분리한 뒤
  • 훈련데이터와 테스트 데이터를 분리한다. 
  • 같은 비율로 들어갔는지 확인. 

train_data_senti_freq: Counter({'0': 186, '1': 184})
test_data_senti_freq: Counter({'1': 62, '0': 62})

 

훈련데이터, 테스트 데이터 수동 분리

# 훈련데이터, 테스트데이터 분리(수동)
import random
random.shuffle(data) # 랜덤하게 섞는다

data_70 = int(len(data)*0.7) # 전체 데이터 크기의 70% 숫자를 찾는다
train_data = data[:data_70] # 앞에서 70% 부분을 잘라 훈련데이터로
test_data = data[data_70:] # 그 뒷부분을 테스트데이터로

print('train data length:', len(train_data)) # 345
print('test data length:', len(test_data)) # 149

# 훈련데이터 요소 분리
train_data_text = [line[1] for line in train_data] # 훈련데이터 본문
train_data_senti = [line[2] for line in train_data] # 훈련데이터 긍부정 부분

# 테스트데이터 요소 분리
test_data_text = [line[1] for line in test_data] # 테스트데이터 본문
test_data_senti = [line[2] for line in test_data] # 테스트데이터 긍부정 부분
  • 먼저 전체 데이터의 70% 찾아서 훈련데이터로
  • 나머지는 테스트 데이터로
  • 훈련데이터와 테스트데이터 요소를 분리한다. 

 

행렬 형태로 변환

from sklearn.feature_extraction.text import CountVectorizer
vect = CountVectorizer(min_df=5).fit(train_data_text) # 최소 문서 빈도 5이상의 단어만 대상
X_train = vect.transform(train_data_text) # 행렬 생성
print("X_train:\n", repr(X_train)) # 생성된 행렬 개요
  • 대장이 되는 데이터가 train_data_text이다. 
  • 하나의 문장에 사용되는 단어가 엉청 많은데
  • 컬럼에 걸릴 확률은 1/100000000
  • 0.05% 채워지는 것은 양호하다.

X_train: <345x71 sparse matrix of type '<class 'numpy.int64'>' with 971 stored elements in Compressed Sparse Row format>

345 X 71 행렬 쌩성

그 중 971 셀에만 데이터 입력

 

vect.transform(train_data_text).toarray()

array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 4, ..., 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, 1, 0]])

 

feature_names = vect.get_feature_names()
print("특성 개수: ",len(feature_names))
print("\n처음 20개 특성:", feature_names[:20])
print("\n3000~5000까지의 특성:", feature_names[3000:5000])

특성 개수:  71
처음 20개 특성: ['10점', 'ㅋㅋ', 'ㅎㅎ', 'ㅠㅠ', 'ㅡㅡ', '가다', '감독', '감동', '같다', '그냥', '그런', '나오다', '내내', '내용', '너무', '느낌', '다시', '대하다', '되다', '드라마']
3000~5000까지의 특성: []

 

머신러닝 알고리즘 적용

import pandas as pd
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
y_train = pd.Series(train_data_senti) # 리스트 형태를 종속변수가 될 수 있는 1차원 배열(Series)로 만든다
lr = LogisticRegression(solver="liblinear")
lr.fit(X_train, y_train)

-> 모델 생성 및 모델 훈련

LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
                   intercept_scaling=1, l1_ratio=None, max_iter=100,
                   multi_class='auto', n_jobs=None, penalty='l2',
                   random_state=None, solver='liblinear', tol=0.0001, verbose=0,
                   warm_start=False)

 

테스트 데이터 입력

X_test = vect.transform(test_data_text)
y_test = pd.Series(test_data_senti)

print("테스트 데이터 점수:", lr.score(X_test, y_test))
  • countVectorizer (새로운 vect만들어서) 안 했다. 
  • test에 대해 만들어진다. 
    • countVectorizer행과 여기서 만들어진 행이 다르다 (행의 길이가 다르면 연산 불가)
    • 우연히 train, test의 열의 수가 같아서 열의 순서가 달라지는 문제가 생긴다. 

테스트 데이터 점수: 0.6912751677852349

 

1개 데이터 예측

# 형태소분석기 시작
import rhinoMorph
rn = rhinoMorph.startRhino()
new_input ='오늘은 정말 재미있는 하루구나!'
inputdata = []
morphed_input = rhinoMorph.onlyMorph_list(
  rn, new_input, pos=['NNG', 'NNP', 'VV', 'VA', 'XR', 'IC', 'MM', 'MAG', 'MAJ'])
morphed_input = ' '.join(morphed_input) # ['오늘', '정말', '재미있', '하루']를 한 개 문자열로 변환

# 공백 조인의 결과는 스트링
inputdata.append(morphed_input) # 분석 결과를 리스트로 만들기
print('input data:', inputdata)

input data: ['오늘 정말 재미있 하루']

# 1개 데이터 예측
  
X_input = vect.transform(inputdata) # 앞에서 만든 11445 컬럼의 행렬에 적용
result = lr.predict(X_input) # 0은 부정,1은 긍정
if result =="0" : # 문자열 형태로 출력된다
  print("부정적인 글입니다")
else:
  print("긍정적인 글입니다")

부정적인 글입니다

+ Recent posts