자연어 처리 관련 미니 프로젝트
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 데이터는 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])

그나마 높았던 성능...
'mini project' 카테고리의 다른 글
| 세 번째 미니프로젝트 - 저시력자를 위한 원화 화폐 분류 (0) | 2023.05.06 |
|---|---|
| 다섯 번째 미니 프로젝트 - 스마트폰 센서 데이터 기반 모션 분류 (0) | 2023.04.14 |
| 세 번째 미니프로젝트 - 차량 공유업체의 차량 파손여부 분류 (0) | 2023.03.22 |
| 두 번째 미니프로젝트 - 악성 사이트 탐지 (0) | 2023.03.07 |
| 두 번째 미니 프로젝트 - 미세먼지 예측 머신러닝 모델링 (0) | 2023.03.07 |