텍스트 데이터 수집
텍스트 전처리
토큰화
특징 값 추출
데이터 분석
토큰화(n-gram)
텍스트 : “어제 러시아에 갔다가 러시아 월드컵을 관람했다”
단어 토큰 : {”어제”, “러시아”, “갔다”, “월드컵”, “관람”}
2-gram 토큰 : {”어제 러시아”, “러시아 갔다”, “갔다 월드컵”, “월드컵 관람”}
n-gram을 허용하면 토큰화 대상의 수가 크게 증가한다.
토큰화 한 결과를 수치로 만드는 방법
→ 원 핫(one-hot) 인코딩
→ BOW(단어모음)
→ 단어벡터(Word Vector) 방법
tf-idf(term frequency-inverse document frequency)
많이 나올수록 중요함 / 여러 문서에서 나오면 안중요함
tf란 단어가 각 문서에서 발생한 빈도 (단어가 등장한 ‘문서’의 빈도를 df라 한다.)
적은 문서에서 발견될수록 가치 있는 정보라고 할 수 있다.
많은 문서에 등장하는 단어일수록 일반적인 단어이며 이러한 공통 적인 단어는 tf가 크다고 하여도 비중을 낮추어야 분석이 제대로 이루어질 수 있다.
따라서 단어가 특정 문서에만 나타나는 희소성을 반영하기 위해서 idf(df의 역수)를 tf에 곱한 값을 사용한다.
tf(d,t) : 특정 문서 d에서의 특정 단어 t의 등장 횟수
df(t) : 특정 단어 t가 등장한 문서의 수
idf(t) : df(t)에 반비례하는 수
— TFIDF 방법론
많이 나왔는가 → 각 문서에서 발생한 빈도
문서에서 단어가 발생한 빈도 → 전체 문서 중에서 해당 단어가 들어가있는 문서의 수
→ 역수를 사용 → 적은 문서에서 발견될수록 가치있는 정보다
1번 값과 2번값을 곱해서 큰 수가 나올수록 중요한 단어
-여러 개 파일을 한 번에 읽어오는 함수
from sklearn.datasets import load_files
train_data_url = 'aclImdb/train'
test_data_url = 'aclImdb/test'
reviews_train = load_files(train_data_url, shuffle=True)
reviews_train
reviews_test = load_files(test_data_url, shuffle=True)
reviews_test
reviews_train.keys()
-b가 붙어있는 문자열 = 바이트 자료형
reviews_train['data'][0]
-train 데이터 : 25000개
len(reviews_train['data'])
-test 데이터 : 25000개
len(reviews_test['data'])
-정답 데이터 비율 확인
import numpy as np
np.bincount(reviews_train['target'])
# 데이터의 갯수는 고르게 분포해있는게 좋다.
# 한쪽에 치우쳐져 있으면 모델이 규칙을 잘 찾지 못함
reviews_train['target_names']
reviews_train['data'][0].replace(b'<br />', b' ')
# 리스트 내포
# 리스트 안에서 다양한 작업을 포함
text_train = [txt.replace(b"<br />", b" ") for txt in reviews_train['data']]
text_test = [txt.replace(b"<br />", b" ") for txt in reviews_test['data']]
text_train = text_train[:5000]
text_test = text_test[:2000]
y_train = reviews_train['target'][:5000]
y_test = reviews_test['target'][:2000]
# 토큰화 (BOW)
# 띄어쓰기를 기준으로 데이터를 나누기
# BOW = CountVectorizer
# 나온 단어의 갯수를 세어준다
from sklearn.feature_extraction.text import CountVectorizer
test_bow = CountVectorizer()
text = [
'수고했어요~ 날 추우니까 다들 따뜻하게 입고다니고 집에 조심히들 가요~',
'그리고 지각 그만해요 이번주 지각 자들은 목요일에 나랑 이야기할꺼니까~',
'다들 종례 시트 확인하시고 당번들은 잘진행하세용~'
]
# 띄어쓰기 기준으로 데이터를 나눴을 때 중복 제거하고 어떤 단어들이 있는지 파악
# = 단어사전 구축
test_bow.fit(text)
-단어사전 확인하기
# 숫자는 가나다 순으로 결정
# 배치된 순서는 문자열의 순서
test_bow.vocabulary_
transform = test_bow.transform(text)
transform
transform.toarray()
-실제 데이터 적용
movie_bow = CountVectorizer()
movie_bow.fit(text_train)
X_train = movie_bow.transform(text_test)
X_test = movie_bow.transform(text_test)
-단어사전 확인하기
len(movie_bow.vocabulary_)
-세부조정하기
# min_df : 단어사전에 등록하기 위한 단어의 최소 등장 횟수
# max_df : 단어사전에 등록하기 위한 단어의 최대 등장 횟수
# ngram_range : 띄어쓰기 한 번이 기준이 아니라 2번 이상도 기준이 됨
movie_bow = CountVectorizer(min_df=20, max_df=500, ngram_range=(1, 2))
movie_bow.fit(text_train)
X_train = movie_bow.transform(text_train)
X_test = movie_bow.transform(text_test)
-단어사전 확인하기
len(movie_bow.vocabulary_)
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import LinearSVC
dt = DecisionTreeClassifier()
svm = LinearSVC()
-교차검증
# 여러 모델 사용해보고 결과 좋은 모델 찾을 때 사용하기도 함
from sklearn.model_selection import cross_val_score
-dt
cross_val_score(dt, X_train, y_train, cv=5)
-svm
cross_val_score(svm, X_train, y_train, cv=5)
svm.fit(X_train, y_train)
svm.score(X_train, y_train)
-test
svm.score(X_test, y_test)
-세부조정하고 학습하고 예측 및 평가하면
# 예측하기 위한 데이터에도 기존 전처리를 전부 진행해야함
# 1. 데이터 형식을 맞추기 위해서
# 2. 머신러닝 학습엔 숫자데이터만 가능
# 3. 기존 방법이 아닌 다른 방법으로 전처리를 진행하면 데이터의 의미가 달라짐
reviews = ['This movie so good'] # 긍정
# br태그 제거
text_pred = [txt.replace("<br />", " ") for txt in reviews]
# bow → 토큰화하기
X_pred = movie_bow.transform(text_pred)
# 0 : 부정, 1 : 긍정
svm.predict(X_pred)
# 부정
reviews = [''' ( SPOILERS) Absolute garbage and a waste of time. Full of plot twists that end up being nothing. Vision having holes in his body had nothing to do with the plot. Pietro having holes in his body had nothing to do with the plot. Pietro being from X'men was just a random coincidence. Also, every time a new male character walked into the show you knew he was either a wimp or evil. They even made pietros real last name "bohner" to make fun of manhood. Imagine if a female character everyone was stoked on turned out to be some random lady named "Vachina". Also, the physical vision just flew off for no reason, and digital vision never decided to tell wanda about his existence. Why? Lazy writing. Additionally at the end rhambeaou tells wanda "they will never know what you sacrificed". What the heck?! Like maybe apologize for trapping and tormenting these people every day for like a month. How on earth is wanda the victim or the "good-guy" in this show. She is literally a villain causing everyone pain, but it is "ok" because she did it out of a place of pain. Im sorry, almost all villains do evil out of a place of pain, that doesnt make it ok. Stupid, sexist show with bad plot that treats its audience like idiots. ''']
# br태그 제거
text_pred = [txt.replace("<br />", " ") for txt in reviews]
# bow → 토큰화하기
X_pred = movie_bow.transform(text_pred)
# 0 : 부정, 1 : 긍정
svm.predict(X_pred)
# 단어가 나온 순서대로 나열
voca = movie_bow.vocabulary_
# 0번 단어부터 38955개까지 차례대로 나열
word_weight = svm.coef_
len(word_weight[0])
import pandas as pd
# 단어사전을 번호 기준으로 정렬
df = pd.DataFrame([voca.keys(), voca.values()])
df = df.T # 행과 열 바꾸기
df.head()
df_sorted = df.sort_values(by = 1)
df_sorted.head()
df_sorted['weight'] = word_weight[0]
df_sorted
# 1번 컬럼기준으로 정렬했던 이유 = weight가 단어 번호로 정렬되어있었기 떄문
# 단어별 가중치를 확인해야하기 때문에 weight 기준으로 재정렬
df_sorted.sort_values(by = 'weight', inplace=True)
-weight의 숫자가 작다 = 부정에 많은 영향을 끼쳤다.
# 상위 20개 단어 확인
df_sorted.head(20)
-weight의 숫자가 크다 = 긍정에 많은 영향을 끼쳤다.
df_sorted.tail(20)
top20_df = pd.concat(
[df_sorted.head(20), df_sorted.tail(20)]
)
top20_df
import matplotlib.pyplot as plt
plt.figure(figsize=(15, 5)) # 그래프 크기 조절
plt.bar(top20_df[0], top20_df['weight'])
plt.xticks(rotation = 90) # x축 값들의 방향 돌리기
plt.show()
from sklearn.pipeline import make_pipeline
# Bow + SVM이 합쳐진 pipe model 완성
pipe_model = make_pipeline(CountVectorizer(), LinearSVC())
-파이프 모델 학습하기
pipe_model.fit(text_train, y_train)
-train
pipe_model.score(text_train, y_train)
-test
pipe_model.score(text_test, y_test)
-predict
pipe_model.predict(reviews)
-pipe model이 가지고 있는 단계
pipe_model.steps
-파이프모델 GridSearch
from sklearn.model_selection import GridSearchCV
param = {
'countvectorizer__max_df' : [500, 700, 900],
'countvectorizer__min_df' : [20, 40, 60],
'countvectorizer__ngram_range' : [(1, 1), (1, 2)],
'linearsvc__C' : [0.5, 1, 1.5]
}
grid = GridSearchCV(pipe_model, param, cv = 5)
grid.fit(text_train, y_train)
grid.best_score_
grid.best_params_
final_model = make_pipeline(CountVectorizer(max_df=700, min_df=20, ngram_range=(1, 2)),
LinearSVC(C=0.5)
)
final_model.fit(text_train, y_train)
-train
final_model.score(text_train, y_train)
-test
final_model.score(text_test, y_test)