문서 행렬 ( 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("긍정적인 글입니다")
부정적인 글입니다