일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- hadoop
- requests
- GenericGBQException
- API Gateway
- gather_nd
- BigQuery
- Retry
- Counterfactual Explanations
- top_k
- correlation
- XAI
- TensorFlow
- airflow subdag
- youtube data
- GCP
- login crawling
- subdag
- tensorflow text
- grad-cam
- 공분산
- flask
- integrated gradient
- chatGPT
- UDF
- session 유지
- spark udf
- API
- 유튜브 API
- Airflow
- 상관관계
- Today
- Total
데이터과학 삼학년
BTS 불건전 팬픽 분류 분석 (Naive Bayes, Logistic Regression, RNN) 본문
BTS 불건전 팬픽 분류 분석 (Naive Bayes, Logistic Regression, RNN)
Dan-k 2020. 6. 1. 12:08BTS 불건전 팬픽 데이터를 모아 label=0, 건전 팬팩은 label=1로 구성한 데이터 셋을 이용해 분류 문제를 푼다.
일단 불건전 팬픽의 경우, 팬픽 데이터를 일단 다 내려받은 후에 팬픽의 특정 불건전 단어가 들어가는 문장을 뽑아 이러한 문장을 모아 재구성하였다.
건전 팬픽의 경우, GCP bigquery에 데이터를 올려놓고 이를 가져와 사용한다.
import numpy as np
import pandas as pd
from konlpy.tag import Mecab
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import BernoulliNB
from sklearn.linear_model import SGDClassifier, LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
%load_ext google.cloud.bigquery
%%bigquery df
#standardSQL
SELECT * FROM `project.dataset.tabe_story`
## 정상데이터 label 매기기
df['label'] = 1
df.shape
## 정상 데이터
posit_df = df[['contents','label']]
## 불건전 데이터
negat_df = pd.read_csv('unhealth_data.csv',index_col=0)
posit_df.shape
posit_df = posit_df.dropna(axis=0)
posit_df.shape
posit_df.head()
negat_df.head()
negat_df.shape
data = pd.concat([posit_df,negat_df], axis = 0)
print(data.shape)
data.head()
data.to_csv('btsws_label_data.csv',encoding='utf-8')
1. nouns, morphs 데이#53552;별 contents 재구성¶
def noun_parsing(contents):
mecab = Mecab()
noun_data = ' '.join(mecab.nouns(contents))
return noun_data
def morphs_parsing(contents):
mecab = Mecab()
noun_data = ' '.join(mecab.morphs(contents))
return noun_data
data['contents'] = data['contents'].astype(str)
data['noun_contents'] = data['contents'].apply(noun_parsing)
data['morphs_contents'] = data['contents'].apply(morphs_parsing)
data.morphs_contents.head()
2. 학습, 테스트 데이터 분리¶
X = data.morphs_contents
# X = data.contents
y = data.label
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
3. Vectorizer : n_gram vectorize (n-gram : 1~2)¶
tfidv = TfidfVectorizer(encoding='utf-8',ngram_range=(1,2)).fit(X_train)
X_train = tfidv.transform(X_train).toarray()
X_test = tfidv.transform(X_test).toarray()
X_train.shape
y = list(df.label.values)
4. 모델학습¶
### NB
model = BernoulliNB()
model.fit(X_train, y_train)
### LR
clf = LogisticRegression(random_state=42).fit(X_train, y_train)
5. 모델 평가¶
NB¶
y_pred = model.predict(X_test)
target_names = ['부정', '긍정']
print(classification_report(y_test, y_pred, target_names=target_names))
confusion_matrix(y_test, y_pred)
LR¶
y_pred = clf.predict(X_test)
target_names = ['부정', '긍정']
print(classification_report(y_test, y_pred, target_names=target_names))
confusion_matrix(y_test, y_pred)
LR 해석¶
importance_dic = { ind:v for ind,v in enumerate(clf.coef_[0])}
# value의 절대값을 기준으로 내림차순 정렬
importance_dic_reverse = sorted(importance_dic.items(),
reverse=True,
key=lambda item: abs(item[1]))
# coefficient가 높은 상위 5개 feature 추출
importance_feature_lst = importance_dic_reverse[:5]
features_name = tfidv.get_feature_names()
for i,_ in importance_feature_lst:
print(features_name[i])
데이터는 명사 추출, 형태소 추출하여 (일부 pos-tag에 대해 추출) 하려 하였으나, 일단 형태소 단위로 자른 tokenizer로 단어를 벡터화한다.
머신러닝의 종류인 나이브베이즈, 로지스틱 회귀 적용을 위해 tf-idf vectorizer(n-gram =(1,2))를 이용하여 벡터화하였으며,
이주 불건전 팬픽의 recall 이 0.96으로 가장 좋은 로지스틱 회귀 모델을 해석하기 위해 coef_이 가장 크게 부여된 단어 feature를 살펴 보았다.
가중치가 높은 상위 5개의 단어 추출 결과, 불건전한 단어에 큰 가중치가 부여된 것을 확인하여, 모델의 해석과 정확도를 판단할 수 있었다.
6. 예측¶
test_text = '벌써 일주일이나 지났다.'
X_pred = tfidv.transform([test_text])
y_pred = clf.predict(X_pred)
y_pred
추가로 RNN을 이용한 분석 결과, 딥러닝의 특성에 따라 모델 학습 시행에 따라 결과가 계속 달라졌으며, 전처리에 큰 영향을 미치는 것을 알 수 있었다.
명사만 추출하여 분석한 결과, 형태소 단위로 모두 추출하여 분석한 결과를 비교하면, 좀 더 정보가 많았던 형태소 단위 추출에서 더 좋은 성능을 냄을 알 수 있었다.
온라인 예측으로 일부 문장을 예측한 결과, 생각보다 잘 맞지 않는 경향도 띠어서 보다 많은 데이터와 체계적인 데이터 전처리 과정을 통해 고도화할 필요를 느낀다.
sequenceVec -> Embedding layer -> RNN¶
import matplotlib.pyplot as plt
from tensorflow.keras.layers import SimpleRNN, Embedding, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
data = pd.read_csv('btsws_label_data.csv',encoding='utf-8',index_col=0)
data['contents'] = data['contents'].astype(str)
data['noun_contents'] = data['contents'].apply(noun_parsing)
data['morphs_contents'] = data['contents'].apply(morphs_parsing)
X = data.morphs_contents
# X = data.contents
y = data.label
## X,y
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X)
sequences = tokenizer.texts_to_sequences(X) ### vovab을 만들고 각 단어마다 고유번호를 매긴 후 번호를 순서에 따라 매핑하는 방법
X_data = sequences
word_to_index = tokenizer.word_index
vocab_size = len(word_to_index) + 1 ## padding 할때 필요한 0 index 추가
print(vocab_size)
X_data = sequences
print('문장 최대 길이 : %d' % max(len(l) for l in X_data))
print('문장 평균 길이 : %f' % (sum(map(len, X_data))/len(X_data)))
plt.hist([len(s) for s in X_data], bins=50)
plt.xlabel('length of samples')
plt.ylabel('number of samples')
plt.show()
max_len = max(len(l) for l in X_data)
# 전체 데이터셋의 길이는 max_len으로 맞춥니다.
data = pad_sequences(X_data, maxlen = max_len, padding='pre') ### 비어있는 사이즈를 0으로 채워주는 것, padding은 앞에 0을 채울지 뒤에 채울지 설정할 수 있음
print("훈련 데이터의 크기(shape): ", data.shape)
len(data)
X_train, X_test, y_train, y_test = train_test_split(data, y, test_size=0.3, random_state=42)
### RNN
model = Sequential()
model.add(Embedding(vocab_size, 32)) # 임베딩 벡터의 차원은 32
model.add(SimpleRNN(32)) # RNN 셀의 hidden_size는 32
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
history = model.fit(X_train, y_train, epochs=15, batch_size=64, validation_split=0.2)
결과 시각화¶
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'r', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'r', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
y_pred = model.predict(X_test)
target_names = ['부정', '긍정']
print(classification_report(y_test, y_pred.round(), target_names=target_names))
confusion_matrix(y_test, y_pred.round())
'Natural Language Processing' 카테고리의 다른 글
soynlp 한국어 형태소 분석기(학습형 형태소 분리기) (0) | 2020.06.08 |
---|---|
[Text preprocessing] 문장 형태소별 토큰화 및 벡터화 (0) | 2020.06.03 |
[Text preprocessing] 텍스트 데이터의 encoding 형식을 알아내기 (0) | 2020.05.28 |
[Text preprocessing] 한국어 문장 splitter (0) | 2020.05.27 |
[Text preprocessing] Lemmatization and Stemming (0) | 2020.03.24 |