mini project

네 번째 미니프로젝트 - 1대1 문의 내용 유형 분류기

민주82 2023. 5. 6. 17:28

자연어 처리 관련 미니 프로젝트

 

AIVLE-EDU 1:1 문의 내용 기반 문의 유형 자동 분류기

교육 수강생의 질문이 질문 게시판 통해 올라옴

→ 질문 유형을 수동으로 분류하는게 오래 걸리고 번거로워서 자동으로 분류할 수 있는 분류기를 만들었다.

 

 

 

데이터 탐색

 

필요한 라이브러리 설치 및 불러오기

!pip install konlpy pandas seaborn gensim wordcloud python-mecab-ko wget

from mecab import MeCab
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
from IPython.display import display
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
from wordcloud import WordCloud
from collections import Counter
import wget, os

 

구글 코랩을 이용하기 때문에 구글 드라이브 연결

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

 

데이터 셋 불러오기

train_path = '/content/drive/MyDrive/4차미니프로젝트/2023.04.03_미니프로젝트4차_실습자료/train.csv'
train = pd.read_csv(train_path)

test_path = '/content/drive/MyDrive/4차미니프로젝트/2023.04.03_미니프로젝트4차_실습자료/test.csv'
test = pd.read_csv(test_path)

 

train

train 데이터는 text, label 두 개의 열로 이루어졌다

text에는 문의 내용, label에는 문의 유형이 저장되어있다

 

 

 

print("-"*120)
text_length = train.text.str.len()
print('최대 길이: ', train.text[text_length.argmax()], text_length.argmax())
print('최소 길이: ', train.text[text_length.argmin()], text_length.argmin())
plt.figure(figsize=(12,8))
plt.subplot(2,1,1, title="max string length : " + str(max(text_length)) )
sns.histplot(text_length, bins=50)
plt.grid()

plt.subplot(2,1,2)
sns.boxplot(x=text_length)
plt.grid()
plt.show()

 

 

문의 내용의 길이 분포를 확인

 

최대길이 text는 코드 오류를 그대로 붙인 것

 

 

최소길이 text는 원격 요청 문의

 

 

import seaborn as sns

sns.countplot(data=train, x='label')
plt.show()

문의 내용 길이 분포를 보면 코드 문의가 가장 많은 것을 알 수 있다

 

 

 

 

명사 추출, 품사 태깅

 

from konlpy.tag import Okt
from mecab import MeCab

okt = Okt()
mecab = MeCab()

# pos : 품사 태깅
# nouns : 명사 추출
okt_pos = train.text.apply(lambda t:okt.pos(str(t), norm=True, stem=True, join=True))
okt_nouns = train.text.apply(lambda t:okt.nouns(str(t)))

mecab_pos = train.text.apply(lambda t:mecab.nouns(str(t)))
mecab_nouns = train.text.apply(lambda t:mecab.nouns(str(t)))

 

 

 

 

 

Wordcloud 만들기

cloud = WordCloud(
    max_font_size=100, max_words=50,
    background_color='white', relative_scaling=.5,
    width=800, height=600, font_path=FONT_PATH).generate(" ".join(nltk_nouns))

plt.figure(figsize=(12,6))
plt.imshow(cloud, interpolation='bilinear')
plt.axis('off')
plt.show()

 

 

 

 

 

데이터 전처리

label_dict = {
    '코드1': 0,
    '코드2': 0,
    '웹': 1,
    '이론': 2,
    '시스템 운영': 3,
    '원격': 4
}
preprocessed_df = df.replace({'label': label_dict}).copy()

X_tr, X_val, Y_tr, Y_val = train_test_split(preprocessed_df.text, preprocessed_df.label, test_size=0.15, random_state=42)
X_tr, X_te, Y_tr, Y_te = train_test_split(X_tr, Y_tr, test_size=0.15, random_state=42)

label_dict에 현재 라벨 유형을 정수로 바꾸고 Train, Validation, Test split

 

특수문자 제거는 진행하지 않음!

 

 

N-grams

mecab = MeCab()

def mecab_tokenizer(string):
	return list("/".join(res)for res in mecab.pos(str(string)))

N-grams로 텍스트 데이터를 분리

mecab을 활용한 tokenizer 하는 함수 생성

 

 

x_tr_counts = count_vectorizer.fit_transform(X_tr)
x_val_counts = count_vectorizer.transform(X_val)
x_te_counts = count_vectorizer.transform(X_te)

x_tr_mecab_counts = count_mecab_vectorizer.fit_transform(X_tr)
x_val_mecab_counts = count_mecab_vectorizer.transform(X_val)
x_te_mecab_counts = count_mecab_vectorizer.transform(X_te)

scikit-learn의 CountVectorizer를 활용해 토큰화한 것과 위에서 정의 한 함수로 토큰화

 

 

 

transformer = TfidfTransformer()
x_tr_tfidf = transformer.fit_transform(x_tr_mecab_counts)
x_val_tfidf = transformer.transform(x_val_mecab_counts)
x_te_tfidf = transformer.transform(x_te_mecab_counts)

tfidf_vectorizer = TfidfVectorizer(tokenizer = mecab_tokenizer)
x_tr_tfidfv = tfidf_vectorizer.fit_transform(X_tr)
x_val_tfidfv = tfidf_vectorizer.transform(X_val)
x_te_tfidfv = tfidf_vectorizer.transform(X_te)

TfidfTransformer로 단어 빈도수 기반 벡터 →  TF-IDF 벡터로 변환

 

 

Sequence

from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.preprocessing import text

TOP_K = 5000
MAX_SEQUENCE_LENGTH = 500
X_mor_tr_str = X_tr.apply(lambda x:' '.join(mecab_tokenizer(x)))
X_mor_val_str = X_val.apply(lambda x:' '.join(mecab_tokenizer(x)))
X_mor_te_str = X_te.apply(lambda x:' '.join(mecab_tokenizer(x)))
X_mor_tr = X_tr.apply(lambda x:mecab_tokenizer(x))
X_mor_val = X_val.apply(lambda x:mecab_tokenizer(x))
X_mor_te = X_te.apply(lambda x:mecab_tokenizer(x))
tokenizer = text.Tokenizer(num_words=TOP_K, char_level=False)
tokenizer.fit_on_texts(x_mor_tr_str)

x_tr_sequences = tokenizer.texts_to_sequences(x_mor_tr_str)
x_val_sequences = tokenizer.texts_to_sequences(x_mor_val_str)
x_te_sequences = tokenizer.texts_to_sequences(x_mor_te_str)

max_length=len(max(x_tr_sequences, key=len))
if max_length > MAX_SEQUENCE_LENGTH:
    max_length = MAX_SEQUENCE_LENGTH
print(max_length)
 
x_tr_sequences= sequence.pad_sequences(x_tr_sequences, maxlen=max_length)
x_val_sequences=sequence.pad_sequences(x_val_sequences, maxlen=max_length)
x_te_sequences=sequence.pad_sequences(x_te_sequences, maxlen=max_length)

기존의 데이터를 Sequence 데이터 형태로 변환

 

 

Text Classification(Machine Learning)

# 로지스틱 회귀 모델 학습
lr_classifier = LogisticRegression(max_iter=5000,penalty='l2')
lr_classifier.fit(x_train, y_train)

# 검증 데이터에 대한 예측 수행
y_val_pred = lr_classifier.predict(x_val)

# 검증 데이터에 대한 정확도 계산
accuracy = accuracy_score(y_val, y_val_pred)
# macro F1 score 계산
f1score_macro = f1_score(y_val, y_val_pred, average='macro')

print(f"Accuracy: {accuracy}")
print(f"f1_score: {f1score_macro}")

 

 

 

랜덤포레스트

 

 

 

그래디언트 부스팅

 

 

 

LinearSCV

 

 

 

 

 

Text Classification(Deep Learning)

from sklearn.utils.class_weight import compute_class_weight
from tensorflow.keras.layers import Embedding, Conv1D, MaxPooling1D, GlobalMaxPooling1D, Dense, Dropout, BatchNormalization
from sklearn.utils.class_weight import compute_class_weight
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.optimizers import Adam

#vocab_size = tokenizer.num_words
class_names = np.unique(y_tr)
class_weights = compute_class_weight(class_weight='balanced', classes=class_names, y=y_tr)
class_weight_dict = dict(zip(class_names, class_weights))

tf.keras.backend.clear_session()

model = Sequential()

model.add(Embedding(input_dim=10000, output_dim=32, input_length=500))
model.add(Conv1D(filters=64, kernel_size=5, activation='relu'))
model.add(MaxPooling1D(pool_size=3))
model.add(GlobalMaxPooling1D())
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(5, activation='softmax'))

optimizer = Adam(learning_rate=0.001)
model.compile(loss='sparse_categorical_crossentropy', metrics=['accuracy'])

history = model.fit(X_mor_tr_seq_str, y_tr, validation_data=(X_mor_val_seq_str, y_val), epochs=20, batch_size=32, class_weight=class_weight_dict)




# 조기 종료 콜백 정의
early_stopping = EarlyStopping(monitor='val_loss', patience=10, mode='min', verbose=1)

# 모델 학습
model.fit(X_mor_tr_seq_str, y_tr, validation_data=(X_mor_val_seq_str, y_val), epochs=50, batch_size=64, class_weight=class_weight_dict, callbacks=[early_stopping])

 

그나마 높았던 성능...