[진짜 재난 뉴스 판별기] - 2
지난 글에 진짜 재난 뉴스 판별기 시각화까지 진행했다.
이번 글에서는 본격적으로 판별하기 위한 피처 엔지니어링을 진행할 것이다.
피처 엔지니어링
데이터 전처리를 진행할 것이다. 먼저 결측치가 있는지 확인해보자.
def check_na(data):
isnull_na = (data.isnull().sum() / len(data)) * 100
data_na = isnull_na.drop(isnull_na[isnull_na == 0].index).sort_values(ascending = False)
missing_data = pd.DataFrame({'Missing Ratio' : data_na,
'Data Type' : data.dtypes[data_na.index]})
print('결측치 데이터 칼럼과 건수 : \n', missing_data)
check_na(train)
check_na(test)
독립 변수로 활용할 text 변수에는 결측치가 없음을 확인할 수 있다.
나머지 변수는 모두 제거하고, test_id만 따로 추출해 저장하자.
test_id = test['id']
for datas in [train, test]:
datas = datas.drop(['id', 'keyword', 'location'], axis = 1, inplace = True)
train.shape, test.shape
텍스트 전처리 함수 만들기
텍스트 전처리 함수의 내부 구성은 다음과 같다.
- URL에 해당하는 문자들을 삭제한다.
- HTML 태그에 해당하는 문자들을 삭제한다.
- 이모티콘들을 삭제한다.
- 영어가 아닌 특수 문자를 공백으로 바꾼다.
- 구두점과 같은 기호들을 삭제한다.
- 영어의 경우 대소문자가 존재하는데, 대문자를 소문자로 변환한다.
- 불용어를 제거한다.
전처리 함수를 만들어보자.
# 텍스트 전처리 함수
import string
import re
from nltk.corpus import stopwords
import nltk
nltk.download('stopwords')
def data_cleansing(text, remove_stopwords = False):
url = re.compile(r'https?://\S+|www\.\S|')
cleaned_text = url.sub(r'', text)
html = re.compile(r'<.*?<')
cleaned_text = html.sub(r'', cleaned_text)
emoji_pattern = re.compile("["
u"\U0001F600-\U0001F64F"
u"\U0001F300-\U0001F5FF"
u"\U0001f680-\U0001F6FF"
u"\U0001F1E0-\U0001F1FF"
u"\U00002702-\U000027B0"
u"\U000024C2-\U0001F251"
"]+", flags = re.UNICODE)
cleaned_text = emoji_pattern.sub(r'', cleaned_text)
cleaned_text = re.sub("[^a-zA-Z]", " ", cleaned_text)
table = str.maketrans('', '', string.punctuation)
cleaned_text = cleaned_text.translate(table)
cleaned_text = cleaned_text.lower().split()
if remove_stopwords:
stops = set(stopwords.words('english'))
cleaned_text = [word for word in cleaned_text if not word in stops]
clean_review = ' '.join(cleaned_text)
else:
clean_review = ' '.join(cleaned_text)
return clean_review
clean_train_reviews = []
for datas in [train, test]:
datas['cleaned_text'] = datas['text'].apply(lambda x: data_cleansing(x, remove_stopwords=True))
train.head(5)
텍스트 정보를 단위별로 나누는 것이 일반적인 절차이다. 이를 Tokenizer라고 부른다. 한 문장을 단어 단위로 쪼개서 분석하는 방법으로, 대표적인 라이브러리는 NLTK(Natural Language Tool-Kit)이 가장 많이 활용된다.
TfidfVectorizer
일반적으로 TF-IDF는 Term Frequency-Inverse Document Frequency의 약자로, 단어의 빈도와 역 문서(문장) 빈도를 사용해 문서(문장) 내의 각 단어마다 가중치를 부여하는 것을 말한다.
TF(Term Frequency)는 특정 단어가 하나의 데이터 안에서 등장하는 횟수를 말한다. DF(Document Frequency)는 문서의 빈도 값을 말하며, 특정 단어가 여러 문서에 얼마나 자주 등장하는지를 알려 주는 지표이다.
IDF는 역수를 취해서 구하는데, 이것이 의미하는 바는 특정 단어가 다른 문서에 등장하지 않을수록 중요도가 커진다는 의미이다.
실습을 통해 알아보자.
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = ['Can I have lunch with you?',
'No, I cannot have it with you.',
'Because, I need to study later']
tfidfv = TfidfVectorizer().fit(corpus)
print(np.round(tfidfv.transform(corpus).toarray(), 2))
print(tfidfv.vocabulary_)
가장 높은 점수를 기록한 단어는 첫 번째 문장의 can과 lunch로 0.52를 기록한다. 가장 낮은 점수를 기록한 것은 두 번째 문장의 have, with, you로 0.35를 기록하는데, 이는 첫 번째 문장에서도 나타나기 때문이다. 이렇게 TF-IDF 값을 사용하면 단순 횟수를 이용하는 것보다 훨씬 더 Text 데이터의 특성을 반영해, 일반적으로 TfidfVectorizer 클래스를 활용하는 것을 볼 수 있다. 그러나 TfidfVectorizer의 단점은 희소 행렬의 발생으로 인한 저장 공간 및 머신러닝 연산 학습 시의 메모리 낭비 등이 거론되기도 한다.
본 데이터에서는 TfidfVectorizer 클래스를 활용한다. 최종 변환된 데이터를 x 객체로 저장하고 종속 변수인 target을 y값으로 저장한다.
vectorizer = TfidfVectorizer(min_df = 0.0, analyzer='char', sublinear_tf=True, ngram_range=(1, 3), max_features=10000)
X = vectorizer.fit_transform(train['cleaned_text']).todense()
y = train['target'].values
print(X.shape)
print(y.shape)
7613개의 학습할 데이터의 숫자, 9445는 현재 데이터에 사용된 전체 단어의 개수를 말한다.
머신러닝 모형 학습 및 평가
이제 모델을 적용해 모형을 학습할 것이다. 학습 데이터와 검증 데이터를 분류해 학습을 진행한 뒤 우선 평가 없이 결괏값을 제출하는 코드를 작성한다.
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size = 0.3, random_state = 0)
X_train.shape, X_valid.shape, y_train.shape, y_valid.shape
from sklearn.linear_model import LogisticRegression
lgs = LogisticRegression(class_weight = 'balanced')
lgs.fit(X_train, y_train)
로지스틱 회귀 모델을 적용해 모형을 학습한다. 이때 class_weight = 'balanced'의 의미를 간단히 설명하자면, 통상적으로 분류 모형은 데이터가 비대칭을 이룬다. 따라서 이러한 특성을 반영하도록 디자인하는 로지스틱 회귀 분석의 파라미터이다.
X_testset = vectorizer.transform(test['cleaned_text']).todense()
print('The Shape of Test Dataset :', X_testset.shape)
y_test_pred = lgs.predict(X_testset)
print('The Predict Value :', y_test_pred)
y_test_pred = np.where(y_test_pred > 0.5, 1, 0)
print('The Predict Class', y_test_pred)
submission_file = pd.DataFrame({'id' : test_id, 'target' : y_test_pred})
print(submission_file.head())
이렇게 하면 캐글에 제출할 파일을 만든것이다. 분류한 파일을 저장한 후 캐글에 제출해보자.
Score 점수는 F1 Score를 의미한다. 0.77536이 나왔다. 그렇다면 F1 Score는 무엇이고 Score 점수를 어떻게 해석해야 하는지에 대해서 궁금할 것이다.
머신러닝 모형의 평가
평가 지표는 일반적으로 예측하려는 모델이 분류인지 회귀인지에 따라 달라진다. 분류 모형의 기초적인 평가 지표에는 무엇이 있는지 확인해보자.
- 정확도(Accuracy)
- 정밀도(Precision)
- 재현율(Recall)
- F1 Score
- Area Under the ROC Curve or simply AUC
이외에도 Log Loss나 G-Mean과 같은 평가 지표가 있지만, 위에 리스트에 있는 것들을 주로 사용한다.
혼동 행렬
이진 분류에서 성능 지표로 활용되는 혼동 행렬도 실제값과 예측값의 차이를 구분한다.
F1 Score
위의 제출한 모델의 성능 평가 지표로 사용되었던 F1 Score는 정밀도와 재현율을 결합하는 지표이다. 그런데 왜 F1 Score를 사용할까?
정밀도와 재현율은 트레이드오프 관계이다.
F1 Score는 1에 가까울수록 더 좋은 모델이라고 판단한다.
지금까지 캐글에서 진행했던 진짜 재난 뉴스 판별기 만들기를 진행해보았다.
텍스트 마이닝을 활용해 분류 모형 설계 및 평가 지표에 대해서 집중적으로 배웠고, 다양한 평가 지표에 대해서도 알아보았다.
'Kaggle' 카테고리의 다른 글
[샌프란시스코 범죄 분류] - 2 (0) | 2021.10.06 |
---|---|
[샌프란시스코 범죄 분류] - 1 (0) | 2021.10.05 |
[진짜 재난 뉴스 판별기] - 1 (0) | 2021.10.01 |
[자전거 수요 예측] - 1 (0) | 2021.09.10 |
[타이타닉 생존자 분류] - 2 (0) | 2021.09.09 |