250x250
반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- TensorFlow
- Counterfactual Explanations
- correlation
- 상관관계
- top_k
- login crawling
- GCP
- Airflow
- youtube data
- chatGPT
- flask
- BigQuery
- requests
- 유튜브 API
- API Gateway
- integrated gradient
- Retry
- GenericGBQException
- subdag
- gather_nd
- API
- tensorflow text
- UDF
- spark udf
- XAI
- airflow subdag
- grad-cam
- session 유지
- 공분산
- hadoop
Archives
- Today
- Total
데이터과학 삼학년
Ch4. The Effects of Feature Scaling: From Bag-of-Words to Tf-Idf 본문
Feature Engineering
Ch4. The Effects of Feature Scaling: From Bag-of-Words to Tf-Idf
Dan-k 2020. 4. 2. 16:11반응형
BoW (Bag-of-Words)의 한계점¶
- Emma knowed on the door. No answer. She knoced again and waited. There was a large maple tree next to the house. Emma looked up the tree and saw a giant raven perched at the treetop. Under the afternoon sun, the raven gleamed magnificently. Its beak was hard and pointed, its claws sharp and strong. It looked regal and imposing. It reigned the tree it stood on. The raven was looking straight at Emma with its beady black eye, Emma felt slightly intimidated. She took a step back from the door and tentatively said, "Hello?"
- 'magnificently', 'gleamed', 'intimidated', 'tentatively', 'reigned' 등과 같이 중요한 단어들도 BoW로는 찾아내기 힘듦
In [1]:
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
In [41]:
sentence = 'Emma knowed on the door. No answer. She knoced again and waited. There was a large maple tree next to the house. Emma looked up the tree and saw a giant raven perched at the treetop. Under the afternoon sun, the raven gleamed magnificently. Its beak was hard and pointed, its claws sharp and strong. It looked regal and imposing. It reigned the tree it stood on. The raven was looking straight at Emma with its beady black eye, Emma felt slightly intimidated. She took a step back from the door and tentatively said, "Hello?"'
bow_converter = CountVectorizer()
X = bow_converter.fit_transform([sentence])
features = bow_converter.get_feature_names()
df = pd.DataFrame()
df['features'] = features
df['cnt'] = X.toarray()[0]
df[df['cnt']>2]
Out[41]:
tf-idf: BoW 비틀기¶
- 의미있는 단어를 강조
- Term frequency - inverse document frequency
- bow(w, d) = 문서 d에서 단어 w가 나타나는 수
- tf-idf(w, d) = bow(w, d) X log(N/(단어 w가 나타나는 문서 수))
- 많이 나오되, 다른 문서에서 자주 나오지 않는 단어 (희귀어는 두드러지게, 상용어는 무시)
tf-idf 테스트¶
- tf-idf는 한 상수와의 곱셈을 통한 단어 카운트 피처의 변환임 => 피처 스케일링의 한 예
- 스케일링된 피처와 그렇지 않은 피처 간의 성능 비교 (텍스트 분류)
In [42]:
import json
import pandas as pd
# Yelp 비즈니스 데이터 로드
biz_f = open('yelp_academic_dataset_business.json', encoding='UTF8')
biz_df = pd.DataFrame([json.loads(x) for x in biz_f.readlines()])
biz_f.close()
In [44]:
# Yelp 리뷰 데이터 로드
review_file = open('review.json', encoding='UTF8')
review_df = pd.DataFrame([json.loads(x) for x in review_file.readlines()])
review_file.close()
In [46]:
biz_df.head()
Out[46]:
In [47]:
review_df.head()
Out[47]:
In [60]:
# Nightlife 또는 Restaurants 비즈니스만 추출
# None data 삭제
# categories == None인 데이터 제거
nan_idx = biz_df.loc[biz_df.categories.isnull()].index
biz_df.drop(biz_df.index[[nan_idx]], inplace=True)
two_biz = biz_df[biz_df.apply(lambda x: 'Nightlife' in x['categories'] or 'Restaurants' in x['categories'], axis=1)]
In [62]:
# 비즈니스 데이터와 리뷰 데이터를 하나로 통합
twobiz_reviews = two_biz.merge(review_df, on='business_id', how='inner')
twobiz_reviews.head()
Out[62]:
In [75]:
# 사용하지 않는 피처 제거
twobiz_reviews = twobiz_reviews[['business_id',
'name',
'stars_y',
'text',
'categories']]
# Nightlif 비즈니스면 True, 아니면 False Column 생성
twobiz_reviews['target'] = twobiz_reviews.apply(lambda x: 'Nightlife' in x['categories'], axis=1)
리뷰 Text를 활용하여 레스토랑 혹은 나이트 라이프 범주로 분류¶
- 불균형 데이터셋 (두 범주의 리뷰 카운트에 큰 차이가 있음) => 모델이 클래스가 큰 데이터에 더 많은 시간을 사용해 피팅
- 여기서는 더 큰 클래스를 작은 클래스와 동일한 사이즈로 다운 샘플링
In [76]:
print('Number of restaurant: ' + str(len(twobiz_reviews[twobiz_reviews['target']==False])))
print('Number of nightlife: ' + str(len(twobiz_reviews[twobiz_reviews['target']==True])))
In [77]:
# 모델에 사용하기 위해 클래스 균형을 맞춘 샘플링 데이터 생성
nightlife = twobiz_reviews[twobiz_reviews.apply(lambda x: 'Nightlife' in x['categories'], axis=1)]
restaurants = twobiz_reviews[twobiz_reviews.apply(lambda x: 'Restaurants' in x['categories'], axis=1)]
In [81]:
nightlife_subset = nightlife.sample(frac=0.1, random_state=123)
restaurant_subset = restaurants.sample(frac=0.03, random_state=123)
combined = pd.concat([nightlife_subset, restaurant_subset])
In [88]:
# 트레이닝셋과 테스트셋으로 분할
from sklearn.model_selection import train_test_split
training_data, test_data = train_test_split(combined, train_size=0.7, test_size=0.3, random_state=123)
print(training_data.shape)
print(test_data.shape)
선형 분류에 대한 BoW, tf-idf, l2 정규화¶
- 트레이닝셋에 대한 스케일링 결과가 테스트셋에 깔끔하게 매핑되지 않음
- 누락되는 데이터가 생김 (테스트셋에만 있는 단어의 경우)
- 보통 새로운 단어는 제외
- 혹은 'Garbage' 단어를 만들어서 트레이닝셋에서 명시적으로 학습하고 테스트셋의 이 단어들을 여기에 매핑
In [95]:
# 리뷰 텍스트를 BoW로 변환
from sklearn.feature_extraction import text
bow_transform = text.CountVectorizer()
X_tr_bow = bow_transform.fit_transform(training_data['text'])
X_te_bow = bow_transform.transform(test_data['text'])
len(bow_transform.vocabulary_)
Out[95]:
In [92]:
y_tr = training_data['target']
y_te = test_data['target']
In [93]:
# BoW 행렬을 사용해 tf-idf 표현 생성
tfidf_trfm = text.TfidfTransformer(norm=None)
X_tr_tfidf = tfidf_trfm.fit_transform(X_tr_bow)
X_te_tfidf = tfidf_trfm.transform(X_te_bow)
In [98]:
# 비교를 위해 BoW 표현을 L2 정규화
import sklearn.preprocessing as preproc
X_tr_l2 = preproc.normalize(X_tr_bow, axis=0)
X_te_l2 = preproc.normalize(X_te_bow, axis=0)
로지스틱 회귀¶
- 시그모이드 함수: 1/(1+e-wTx-b</sup>) (Range: 0~1)
- <0.5 => False, >0.5 => True
In [110]:
from sklearn.linear_model import LogisticRegression
def simple_logistic_classify(X_tr, y_tr, X_test, y_test, description, _C=1.0):
m = LogisticRegression(C=_C).fit(X_tr, y_tr)
s = m.score(X_test, y_test)
print('Test score with', description, 'features:', s)
return m
m1 = simple_logistic_classify(X_tr_bow, y_tr, X_te_bow, y_te, 'bow')
m2 = simple_logistic_classify(X_tr_l2, y_tr, X_te_l2, y_te, 'l2-normalized')
m3 = simple_logistic_classify(X_tr_tfidf, y_tr, X_te_tfidf, y_te, 'tf-idf')
Regularization을 통한 로지스틱 회귀 튜닝¶
- Feature 수가 데이터 포인트 수보다 큰 경우 최상의 모델을 찾는 문제가 과소평가되는 경향 (Overfitting?) => Regularization으로 해결
- Hyperparameter 튜닝 -> 그리드 검색 -> Deep learning에서는 Random한 방식
- K-fold cross validation
- GridSearchCV -> K-fold와 그리드 검색 함께 실행
In [102]:
# 그리드 검색을 통한 로지스틱 회귀 하이퍼파라미터 튜닝
import sklearn.model_selection as modsel
# 검색할 그리드를 지정. 각 피처 집합에 댛해 5등분 그리드 검색을 수행
param_grid_ = {'C': [1e-5, 1e-3, 1e-1, 1e0, 1e1, 1e2]}
# BoW 표현에 대한 분류기 튜닝
bow_search = modsel.GridSearchCV(LogisticRegression(), cv=5,
param_grid=param_grid_)
bow_search.fit(X_tr_bow, y_tr)
Out[102]:
In [103]:
# L2 정규화된 단어 벡터에 대한 분류기 튜닝
l2_search = modsel.GridSearchCV(LogisticRegression(), cv=5,
param_grid=param_grid_)
l2_search.fit(X_tr_l2, y_tr)
Out[103]:
In [104]:
# tf-idf에 대한 분류기 튜닝
tfidf_search = modsel.GridSearchCV(LogisticRegression(), cv=5,
param_grid=param_grid_)
tfidf_search.fit(X_tr_tfidf, y_tr)
Out[104]:
In [105]:
# BoW 결과 출력
bow_search.cv_results_
Out[105]:
In [106]:
# 교차 검증 결과를 BoxPlot으로 나타내 분류기들의 성능을 시각화 및 비교
search_results = pd.DataFrame.from_dict({
'bow': bow_search.cv_results_['mean_test_score'],
'tfidf': tfidf_search.cv_results_['mean_test_score'],
'l2': l2_search.cv_results_['mean_test_score']
})
In [108]:
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
ax = sns.boxplot(data=search_results, width=0.4)
ax.set_ylabel('Accuracy', size=14)
ax.tick_params(labelsize=14)
In [111]:
# 최선의 하이퍼파라미터를 사용한 모델 학습
m1 = simple_logistic_classify(X_tr_bow, y_tr, X_te_bow, y_te, 'bow',
_C=bow_search.best_params_['C'])
m2 = simple_logistic_classify(X_tr_l2, y_tr, X_te_l2, y_te, 'l2',
_C=l2_search.best_params_['C'])
m3 = simple_logistic_classify(X_tr_tfidf, y_tr, X_te_tfidf, y_te, 'tf-idf',
_C=tfidf_search.best_params_['C'])
심층 분석¶
- BoW 벡터: 문서-단어 행렬 ==> 희소 행렬 (대부분 항목은 0의 값)
- 피처 스케일링 기법 ==> 데이터 행렬의 열에 대해서 작동
- 선형 분류기를 학습시키는 것은 데이터 행렬의 열 벡터인 피처에 대해서 최선의 선형 조합을 찾는 것
- Column space: 데이터 행렬의 열 벡터의 선형 조합으로 만들어낼 수 있는 벡터들의 공간 (Ax =b)
- Null space: Ax=0를 만족시키는 벡터들의 공간, 기존 데이터의 선형 조합으로는 형성될 수 없는 '새로운' 데이터 포인트
- 데이터 행렬의 Null space는 두 가지 이유에서 매우 클 수 있음 ==> 행 공간과 열 공간의 Rank가 작음
- 비슷한 데이터 포인트를 포함할 때 (희소 행렬)
- 데이터 포인트 < 피처 수
- 피처 스케일링은 데이터 행렬의 랭크 부족 문제를 해결하기 어려움
- 피처 스케일링을 통해 행렬의 조건 수를 향상시킴으로써 선형 모델을 보다 빠르게 학습시킬 수 있음
- 피처 엔지니어링의 효과를 분석하는 것이 어려움
728x90
반응형
LIST
'Feature Engineering' 카테고리의 다른 글
Ch.6 Dimensionality Reduction: Squashing the Data Pancake with PCA (0) | 2020.05.06 |
---|---|
Ch.5 Categorical Variables: Counting Eggs in theAge of Robotic Chickens (0) | 2020.04.14 |
Ch3. Text Data: Flattening, Filtering,and Chunking (0) | 2020.03.27 |
Ch2. Fancy Tricks with Simple Numbers (0) | 2020.03.27 |
Ch1. The Machine Learning Pipeline (0) | 2020.03.27 |
Comments