반응형
데이터 전처리
- 데이터를 불러오고 전처리하는 과정이다.
- 데이터에 기본적인 필요없는 부분을 제거하고, 문장을 단어들로 나눈다.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
spam_data = pd.read_csv("../data/spam.csv", encoding="latin1")
# spam, ham 값을 숫자로 변환
spam_data['v1'] = spam_data['v1'].replace('spam', 1)
spam_data['v1'] = spam_data['v1'].replace('ham', 0)
# 정규표현식을 써서, 단어가 아니면 공백으로
spam_data['v2'] = spam_data['v2'].str.replace("[^\w]|br", " ")
# 혹시 공백이 있으면 제거
spam_data['v2'] = spam_data['v2'].replace("", np.nan)
spam_data['v1'] = spam_data['v1'].replace("", np.nan)
# null array 없애는 함수
spam_data = spam_data.dropna(axis=1)
spam_data = spam_data.dropna(how='any')
spam_data.columns = ["label", "mail"]
print("# preprocessing done")
- 현재 spam.csv에는 v1에 spam여부가, v2에 내용이 적혀있다.
- 결과(스팸여부)를 숫자형으로 바꿔 머신러닝을 진행할 수 있는 형태로 한다. (spam->1, ham->0)
- 단어가 아닌 부분은 공백으로 바꾸고, 내용이 공백인 부분은 nan로 결측치로 취급한다.
- 결측치를 제거하고 columns 이름을 알아보기 쉽게 label과 mail로 바꿨다.
review_train, review_test, y_train, y_test = train_test_split(spam_data['mail'], spam_data['label'], test_size=0.25, shuffle=False, random_state=23)
print("# split done")
- train_test_split 메소드를 사용해 주어진 데이터를 학습 데이터와 테스트 데이터로 분류한다.
stopwords = ['a', 'an']
X_train = []
for stc in review_train:
token = []
words = stc.split()
for word in words:
if word not in stopwords:
token.append(word.lower())
X_train.append(token)
X_test = []
for stc in review_test:
token = []
words = stc.split()
for word in words:
if word not in stopwords:
token.append(word.lower())
X_test.append(token)
print("# tokenization done")
- stopwords를 제거한다. (임의로 'a'와 'an'만을 stopword 취급했다.)
- 그와 동시에 문장으로 되어 있던 데이터들을 단어들의 리스트들로 변경한다.
단어 인덱싱
- 머신러닝에 사용될 수 있도록 각 단어를 숫자정보로 변경한다.
- 추후 Word Embedding 과정에 들어가 적절히 임베딩될 수 있도록 하는 것이다.
from tensorflow.keras.preprocessing.text import Tokenizer
# 인덱스 개수 기준
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)
print(len(tokenizer.word_counts))
==결과==
7512
- Tokenizer 메소드를 불러와 객체를 만들고, X_train에 fitting한다.
- 해당 객체의 word_counts에는 인덱싱한 총 단어가 적혀있다.
- 우리는 전체 단어를 인덱싱하지 않는다. 빈도수가 낮은 단어까지 학습에 이용할 필요는 없기 때문이다.
- 따라서 적절한 값을 탐색한다.
count = 0
for word, word_count in tokenizer.word_counts.items():
if word_count > 1:
count += 1
print(count)
==결과==
3607
- 빈도수가 1초과, 즉 적어도 2번 이상 등장한 단어만 카운팅한다.
- 3607, 약 4000개 정도만 인덱싱하기로 하자.
tokenizer = Tokenizer(4000)
tokenizer.fit_on_texts(X_train)
- tokenizer 객체가 X_train 내부의 4000개 단어를 인덱싱하도록 fitting 했다.
from tensorflow.keras.preprocessing.sequence import pad_sequences
# 부여된 정수 인덱스로 변환
X_train = tokenizer.texts_to_sequences(X_train)
X_test = tokenizer.texts_to_sequences(X_test)
print("# int_encoding done")
- 최종적으로 데이터에 대해 정수 인코딩을 진행한다.
- X_train에 대해서만 fitting을 진행했기 때문에 X_test도 그를 기준으로 인덱싱될 것이다.
- X_train과 X_test로 나누기 전에 전체 X에 대해 fitting을 시켜줘도 프로세스 상 큰 문제는 없다.
데이터 패딩
- 각 벡터가 크기가 다르면 원활한 진행을 할 수 없으므로 크기를 통일화해준다.
# 패딩 결정, 임베딩 레이어로 넣을 벡터 길이를 정함
# 해당 벡터길이는 최대길이 or 평균길이
max_len = 50
X_train = pad_sequences(X_train, maxlen=max_len)
X_test = pad_sequences(X_test, maxlen=max_len)
- pad_sequences 메소드를 활용해 max_len값만큼 데이터를 늘리거나 줄인다. (늘릴 경우 앞을 0으로 채운다.)
- 보통 최대길이 혹은 평균길이를 활용한다. 각각 장단점이 있다.
- 최대길이 : 데이터 손실은 없지만, 효율 떨어짐
- 평균길이 : 데이터 손실은 있지만, 효율이 좋음
- 본 코드에서는 최대길이도 평균길이 사이 정도의 값을 선택했다.
모델 구축
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, SimpleRNN, LSTM, Embedding
# 모델 구축
# 레이어들을 쌓을 모델을 생성
model = Sequential()
model.add(Embedding(4000, 32))
model.add(LSTM(32))
model.add(Dense(1, activation='sigmoid'))
- Sequential에 각 레이어를 쌓는다.
- 지금까지 구축한 데이터(단어들이 인덱싱된 데이터)가 Embedding에 들어가 워드 임베딩화 된다. (4000개의 단어를 32차원으로 보낸다.)
- 그렇게 임베딩화된 데이터가 LSTM을 통화하며 특정 값들을 도출한다. (입력은 32차원으로 보내온 임베딩 데이터)
- 그들을 sigmoid함수에 통과시켜 최종 값을 찾는다.
최적 모델 찾기
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# 테스트 데이터 손실함수값(val_loss)이 patience회 이상 연속 증가하면 학습을 조기 종료하는 콜백
early_stop = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=5)
# 훈련 도중 테스트 데이터 정확도(val_acc)가 높았던 순간을 체크포인트로 저장해 활용하는 콜백
model_check = ModelCheckpoint('the_best.h5', monitor='val_acc', mode='max', verbose=1, save_best_only=True)
- 학습을 무조건 많이 시킨다고 좋은 것은 아니다.
- 학습이 과해지면 오히려 테스트 데이터에 대한 손실함수와 정확도가 떨어질 수 있다.
모델 학습
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc'])
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=1, batch_size=64, callbacks=[early_stop, model_check])
- 손실함수는 교차엔트로피(binary_crossentropy), 최적화방법은 아담(adam), 평가기준은 정확도(acc)로 한다.
- validation_data엔 검증 데이터가 들어간다.
- 앞서 만든 콜백도 callbacks 변수에 넣어준다.
정확도 측정
# 정확도 측정
# 출력하면 [loss, acc]
print(model.evaluate(X_test, y_test))
- 정확도를 측정한다.
테스트
user_input = input().split()
user_data = [[]]
for word in user_input:
if word not in stopwords:
user_data[0].append(word.lower())
user_data = tokenizer.texts_to_sequences(user_data)
user_data = pad_sequences(user_data, maxlen=maxlen)
if (model.predict(user_data) > 0.5):
print(f"[{user_input}] is spam")
else:
print(f"[{user_input}] is non-spam")
- predict에 들어가는 데이터는 전처리 및 토큰화, 정수인코딩이 완료된 데이터이어야 한다.
- 따라서 사용자 입력 문장에 그에 맞는 처리를 하고 최종 평가를 한다.
- 스팸문자로 인식할 것으로 예상되는 'lucky strike jackpot gamble free' 문장을 입력했더니 [['lucky', 'strike', 'jackpot', 'gamble', 'free']] is spam 문장을 출력했다.
- 반면 'hi my name is inwoo'는 [['hi', 'my', 'name', 'is', 'inwoo']] is non-spam 문장을 출력했다.
반응형
'🛠 기타 > Data & AI' 카테고리의 다른 글
문자열 특수문자 제거 (0) | 2020.08.06 |
---|---|
[scikit-learn 라이브러리] DecisionTreeClassifier (결정트리분류기) (0) | 2020.08.05 |
합성곱 신경망(CNN) - 기본구조 이론 (0) | 2020.08.03 |
다층퍼셉트론 - 기본구조 구현 (0) | 2020.08.02 |
다층 퍼셉트론 - 기본구조 이론 (0) | 2020.08.02 |