[빅데이터 직무연구회] 6회차 모임 정리 (1)
Chapter 4. 데이터 표현과 특성 공학
일반적인 특성의 전형적인 형태 = 범주형 특성(categorical feature) 또는 이산형 특성(discrete feature)
특성 공학(feature engineering) : 특정 애플리케이션에 가장 적합한 데이터 표현을 찾는 것
성능에 더 도움되는 행동 : 올바른 데이터 표현 >> 지도 학습 모델에서 적절한 매개변수를 선택하는 것
4.1 범주형 변수
4.1.1 원-핫-인코딩(가변수)
4.1.2 숫자로 표현된 범주형 특성
범주형 변수가 문자열로 인코딩되어 있을 수도 있지만, 바로 숫자로 인코딩된 경우가 대다수다. 그러나 이 숫자들이 범주형인지 연속형인지 한 눈에 어떻게 알아낼 수 있을까?
pandas의 get_dummies 함수는 숫자 특성은 모두 연속형이라고 가정한다. 대신 어떤 열이 연속형인지 범주형인지 지정해주는 scikit-learn의 OneHotEncoder 를 사용한다.
또는 DataFrame에 있는 숫자 열을 모두 문자열로 바꿀 수도 있다.
4.2 구간 분할, 이산화 그리고 선형 모델, 트리 모델
구간 분할 (bining, 이산화) : 한 특성을 여러 특성으로 나눔. 연속형 데이터로 이루어진 선형 모델을 강력하게 만들어 줌.
np.linspace 함수로 구간을 생성할 수 있다. np.linspace(-3, 3, 11) 코드는 -3과 3 사이에 같은 간격으로 11개의 지점을 생성해 10개의 구간(두 지점 사이의 간격)을 생성해준다.
그 다음 각 데이터 포인트가 어느 구간에 속하는지 저장한다. np.digitize 함수를 사용한다. 이로써 데이터셋에 있는 연속형 특성을 각 데이터 포인트가 어느 구간에 속했는지로 인코딩한 범주형 특성으로 변환했다.
예제에서 선형 모델은 더 이상 하나의 직선이 아니고, 각 구간마다 직선을 만들어 더 유연한 모델이 되었다.
4.3 상호작용과 다항식
원본 데이터에 상호작용(interaction)과 다항식(polynomial)을 추가해 특성을 풍부하게 만들 수 있다. 통계적 모델링에서 자주 사용할 뿐 아니라 일반적인 머신러닝 애플리케이션에도 많이 적용한다.
각 구간에 선형 모델을, 상호작용과 다항식 특성 공학을 적용해 훨씬 부드러운 곡선을 만들 수 있다. 고차원 다항식은 데이터가 부족한 영역에서 너무 민감하다는 단점이 있다.
더 복잡한 모델인 커널 SVM을 데이터 변환없이 사용할 때와 비슷한 복잡도다.
4.4 일변량 비선형 변환
4.5 특성 자동 선택
4.5.1 일변량 통계
scikit-learn 에서는 계산 속도를 높이기 위해 이 식에서 유도된 간소화된 식을 사용하여 계산한다.
f_regression 에서는 각 특성에 대해 상관계수를 계산하고 이를 이용하여 F-값과 p-값을 계산한다.
매우 높은 p-값(타깃값과 연관성이 작을 것으로 예상)을 가진 특성을 제외할 수 있도록 임계값을 조정하는 매개변수 사용한다.
=> 클래스들의 평균이 같다는 가설을 세울 때 p-값은 이 가설을 지지하는 확률을 나타내는 것이다. F-분포에서는 F-값 이후의 오른쪽 꼬리 부분의 면적이다. 이 면적은 pvalues_ 속성에 저장되어 있다. pvalues_ 값이 큰 특성은 클래스들의 평균이 비슷하므로 타깃에 미치는 영향이 적다고 판단한다.
임계값을 계산하는 방법은 각각 다르다. 가장 간단한 SelectKBest는 고정된 k개의 특성을 선택한다. SelectPercentile 은 지정된 비율만큼 특성을 선택한다.
get_support 메서드는 선택된 특성을 불리언 값으로 표시해주어 어떤 특성이 선택되었는지 확인할 수 있다.
4.5.2 모델 기반 특성 선택
지도 학습 머신러닝 모델을 사용하여 특성의 중요도를 평가해 가장 중요한 특성들만 선택한다.
특성 선택에 사용하는 지도 학습 모델은 최종적으로 사용할 지도 학습 모델과 같을 필요가 없다.
특성 선택을 위한 모델은 각 특성의 중요도를 측청해 순서를 매길 수 있어야 한다. 결정 트리와 이를 기반으로 한 모델들은 각 특성의 중요도가 담겨 있는 feature_importances_ 속성을 제공한다. 선형 모델 계수의 절댓값도 특성의 중요도를 재는 데 사용할 수 있다. L1 규제를 사용한 선형 모델은 일부 특성의 계수만 학습한다. 이런 특징을 이용해 다른 모델의 특성 선택으로 전처리 단계에 사용할 수 있다.
일변량 분석과는 반대로 모델 기반특성 선택은 한 번에 모든 특성을 고려한다. 따라서 상호작용 부분을 반영할 수 있다. 모델 기반의 특성 선택은 SelectFromModel 에 구현되어 있다.
SelectFromModel은 지도 학습 모델로 계산된 중요도가 지정한 임계치보다 큰 모든 특성을 선택한다. 매우 복잡한 모델을 사용해 일변량 분석과는 훨씬 강력하게 특성을 선택할 수 있다.
4.5.3 반복적 특성 선택
일변량 분석은 모델을 사용하지 않았다. 모델 기반 선택은 하나의 모델만을 사용해 특성을 선택했다.
그러나 반복적 특성 선택(Iterative Feature Selection)에서는 특성의 수가 각기 다른 일련의 모델이 만들어진다기본적으로 다음의 두 가지 방법이 있다.
첫 번째는 특성을 하나도 선택하지 않은 상태로 시작해 어떤 종료 조건이 될 때까지 특성을 하나씩 제거해나가는 방법이다.
두 번째는 모든 특성을 가지고 시작해 어떤 종료 조건에 도달할 때 까지 하나씩 추가하는 방법이다.
일련의 모델이 만들어지기 때문에, 계산 비용이 훨씬 많이 든다. 재귀적 특성 제거(RFE, recursive feature elimination)이 그 예다. 모든 특성으로 시작해 모델을 만든다. 특성 중요도가 가장 낮은 특성을 제거한다. 그 제거한 특성을 뺀 나머지 특성으로 새로운 모델을 만든다. 미리 정의한 특성 개수가 남을 때 까지 반복한다.
4.6 전문가 지식 활용
from IPython.display import display
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
# 폰트 관련 자료
# https://programmers.co.kr/learn/courses/21/lessons/950
import matplotlib
matplotlib.rc("font", family="NanumGothicCoding")
import os
data = pd.read_csv(
os.path.join(mglearn.datasets.DATA_PATH, "adult.data"),
header=None, index_col=False,
names=['age', 'workclass', 'fnlwgt', 'education', 'education-num',
'marital-status', 'occupation', 'relationship', 'race', 'gender',
'capital-gain', 'capital-loss', 'hours-per-week', 'native-country', 'income'])
data = data[['age', 'workclass', 'education', 'gender', 'hours-per-week', 'occupation', 'income']]
display(data.head())
print(data.gender.value_counts())
print("원본 특성:\n", list(data.columns), "\n")
data_dummies = pd.get_dummies(data)
print("get_dummies 후의 특성:\n", list(data_dummies.columns))
data_dummies.head()
features = data_dummies.loc[:, 'age':'occupation_ Transport-moving']
# NumPy 배열 추출
X = features.values
y = data_dummies['income_ >50K'].values
print("X.shape: {} y.shape: {}".format(X.shape, y.shape))
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
print("테스트 점수: {:.2f}".format(logreg.score(X_test, y_test)))
demo_df = pd.DataFrame({'숫자 특성': [0, 1, 2, 1], '범주형 특성': ['양말', '여우', '양말', '상자']})
display(demo_df)
display(pd.get_dummies(demo_df))
demo_df['숫자 특성'] = demo_df['숫자 특성'].astype(str)
display(pd.get_dummies(demo_df, columns=['숫자 특성', '범주형 특성']))
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
X, y = mglearn.datasets.make_wave(n_samples=100)
line = np.linspace(-3 ,3, 1000, endpoint=False).reshape(-1, 1)
reg = DecisionTreeRegressor(min_samples_split=3).fit(X, y)
plt.plot(line, reg.predict(line), label="결정 트리")
reg = LinearRegression().fit(X, y)
plt.plot(line, reg.predict(line), '--', label="선형 회귀")
plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel("회귀 출력")
plt.xlabel("입력 특성")
plt.legend(loc="best")
bins = np.linspace(-3 ,3, 11)
print("구간: {}".format(bins))
which_bin = np.digitize(X, bins=bins)
print("\n데이터 포인트:\n", X[:5])
print("\n데이터 포인트의 소속 구간:\n", which_bin[:5])
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse=False)
encoder.fit(which_bin)
X_binned = encoder.transform(which_bin)
print(X_binned[:5])
print("X_binned.shape: {}".format(X_binned.shape))
line_binned = encoder.transform(np.digitize(line, bins=bins))
reg = LinearRegression().fit(X_binned, y)
plt.plot(line, reg.predict(line_binned), label='구간 선형 회귀')
reg = DecisionTreeRegressor(min_samples_split=3).fit(X_binned, y)
plt.plot(line, reg.predict(line_binned), '--', label='구간 결정 트리')
plt.plot(X[:, 0], y, 'o', c='k')
plt.vlines(bins, -3, 3, linewidth=1, alpha=.2)
plt.legend(loc="best")
plt.ylabel("회귀 출력")
plt.xlabel("입력 특성")
X_combined = np.hstack([X, X_binned])
print(X_combined.shape)
reg = LinearRegression().fit(X_combined, y)
line_combined = np.hstack([line, line_binned])
plt.plot(line, reg.predict(line_combined), label='원본 특성을 더한 선형 회귀')
for bin in bins:
plt.plot([bin, bin], [-3, 3], ':', c='k', linewidth=1)
plt.legend(loc="best")
plt.ylabel("회귀 출력")
plt.xlabel("입력 특성")
plt.plot(X[:, 0], y, 'o', c='k')
X_product = np.hstack([X_binned, X * X_binned])
print(X_product.shape)
reg = LinearRegression().fit(X_product, y)
line_product = np.hstack([line_binned, line * line_binned])
plt.plot(line, reg.predict(line_product), label='원보 특성을 곱한 선형 회귀')
for bin in bins:
plt.plot([bin, bin], [-3, 3], ':', c='k', linewidth=1)
plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel('회귀 출력')
plt.xlabel('입력 특성')
plt.legend(loc="best")
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=10, include_bias=False)
poly.fit(X)
X_poly = poly.transform(X)
print("X_poly.shape: {}".format(X_poly.shape))
print("X 원소:\n{}".format(X[:5]))
print("X_poly 원소:\n{}".format(X_poly[:5]))
print("항 이름:\n{}".format(poly.get_feature_names()))
reg = LinearRegression().fit(X_poly, y)
line_poly = poly.transform(line)
plt.plot(line, reg.predict(line_poly), label='다항 선형 회귀')
plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel("회귀 출력")
plt.xlabel("입력 특성")
plt.legend(loc="best")
from sklearn.svm import SVR
for gamma in [1, 10]:
svr = SVR(gamma=gamma).fit(X, y)
plt.plot(line, svr.predict(line), label='SVR gamma={}'.format(gamma))
plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel("회귀 출력")
plt.xlabel("입력 특성")
plt.legend(loc="best")
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
boston = load_boston()
X_train, X_test, y_train, y_test = train_test_split(boston.data, boston.target, random_state=0)
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
poly = PolynomialFeatures(degree=2).fit(X_train_scaled)
X_train_poly = poly.transform(X_train_scaled)
X_test_poly = poly.transform(X_test_scaled)
print("X_train.shape: {}".format(X_train.shape))
print("X_train_poly.shape: {}".format(X_train_poly.shape))
print("다항 특성 이름:\n{}".format(poly.get_feature_names()))
from sklearn.linear_model import Ridge
ridge = Ridge().fit(X_train_scaled, y_train)
print("상호작용 특성이 없을 때 점수: {:.3f}".format(ridge.score(X_test_scaled, y_test)))
ridge = Ridge().fit(X_train_poly, y_train)
print("상호작용 특성이 있을 때 점수: {:.3f}".format(ridge.score(X_test_poly, y_test)))
from sklearn.ensemble import RandomForestRegressor
rf = RandomForestRegressor(n_estimators=100, random_state=0).fit(X_train_scaled, y_train)
print("상호작용 특성이 없을 때 점수: {:.3f}".format(rf.score(X_test_scaled, y_test)))
rf = RandomForestRegressor(n_estimators=100, random_state=0).fit(X_train_poly, y_train)
print("상호작용 특성이 있을 때 점수: {:.3f}".format(rf.score(X_test_poly, y_test)))
rnd = np.random.RandomState(0)
X_org = rnd.normal(size=(1000, 3))
w = rnd.normal(size=3)
X = rnd.poisson(10 * np.exp(X_org))
y = np.dot(X_org, w)
print(X[:10, 0])
print("특성 출현 횟수:\n{}".format(np.bincount(X[:, 0])))
bins = np.bincount(X[:, 0])
plt.bar(range(len(bins)), bins, color='grey')
plt.ylabel("출현 횟수")
plt.xlabel("값")
from sklearn.linear_model import Ridge
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
score = Ridge().fit(X_train, y_train).score(X_test, y_test)
print("테스트 점수: {:.3f}".format(score))
X_train_log = np.log(X_train + 1)
X_test_log = np.log(X_test + 1)
plt.hist(X_train_log[:, 0], bins=25, color='gray')
plt.ylabel("출현 횟수")
plt.xlabel("값")
score = Ridge().fit(X_train_log, y_train).score(X_test_log, y_test)
print("테스트 점수: {:.3f}".format(score))
from sklearn.datasets import load_breast_cancer
from sklearn.feature_selection import SelectPercentile
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()
rng = np.random.RandomState(42)
noise = rng.normal(size=(len(cancer.data), 50))
X_w_noise = np.hstack([cancer.data, noise])
X_train, X_test, y_train, y_test = train_test_split(X_w_noise, cancer.target, random_state=0, test_size=.5)
select = SelectPercentile(percentile=50)
select.fit(X_train, y_train)
X_train_selected = select.transform(X_train)
print("X_train.shape: {}".format(X_train.shape))
print("X_train_selected.shape: {}".format(X_train_selected.shape))
mask = select.get_support()
print(mask)
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("특성 번호")
from sklearn.linear_model import LogisticRegression
X_test_selected = select.transform(X_test)
lr = LogisticRegression()
lr.fit(X_train, y_train)
print("전체 특성을 사용한 점수: {:.3f}".format(lr.score(X_test, y_test)))
lr.fit(X_train_selected, y_train)
print("선택된 일부 특성을 사용한 점수: {:.3f}".format(lr.score(X_test_selected, y_test)))
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier
select = SelectFromModel(
RandomForestClassifier(n_estimators=100, random_state=42),
threshold="median")
select.fit(X_train, y_train)
X_train_l1 = select.transform(X_train)
print("X_train.shape: {}".format(X_train.shape))
print("X_train_l1.shape: {}".format(X_train_l1.shape))
mask = select.get_support()
plt.matshow(mask.reshape(1, -1), cmap="gray_r")
plt.xlabel("특성 번호")
X_test_l1 = select.transform(X_test)
score = LogisticRegression().fit(X_train_l1, y_train).score(X_test_l1, y_test)
print("테스트 점수: {:.3f}".format(score))
from sklearn.feature_selection import RFE
select = RFE(RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=40)
select.fit(X_train, y_train)
mask = select.get_support()
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("특성 번호")
X_train_rfe = select.transform(X_train)
X_test_rfe = select.transform(X_test)
score = LogisticRegression().fit(X_train_rfe, y_train).score(X_test_rfe, y_test)
print("테스트 점수: {:.3f}".format(score))
print("테스트 점수: {:.3f}".format(select.score(X_test, y_test)))
citibike = mglearn.datasets.load_citibike()
print("시티바이크 데이터:\n{}".format(citibike.head()))
plt.figure(figsize=(10, 3))
xticks = pd.date_range(start=citibike.index.min(), end=citibike.index.max(), freq='D')
week = ["일", "월", "화", "수", "목", "금", "토"]
xticks_name = [week[int(w)]+d for w, d in zip(xticks.strftime("%w"), xticks.strftime(" %m-%d"))]
plt.xticks(xticks, xticks_name, rotation=90, ha="left")
plt.plot(citibike, linewidth=1)
plt.xlabel("날짜")
plt.ylabel("대여횟수")
y = citibike.values
X = citibike.index.astype("int64").values.reshape(-1, 1)
n_train = 184
def eval_on_features(features, target, regressor):
X_train, X_test = features[:n_train], features[n_train:]
y_train, y_test = target[:n_train], target[n_train:]
regressor.fit(X_train, y_train)
print("테스트 세트 R^2: {:.2f}".format(regressor.score(X_test, y_test)))
y_pred = regressor.predict(X_test)
y_pred_train = regressor.predict(X_train)
plt.figure(figsize=(10, 3))
plt.xticks(range(0, len(X), 8), xticks_name, rotation=90, ha="left")
plt.plot(range(n_train), y_train, label="훈련")
plt.plot(range(n_train, len(y_test) + n_train), y_test, '-', label="테스트")
plt.plot(range(n_train), y_pred_train, '--', label="훈련 예측")
plt.plot(range(n_train, len(y_test) + n_train), y_pred, '--', label="테스트 예측")
plt.legend(loc=(1.01, 0))
plt.xlabel("날짜")
plt.ylabel("대여횟수")
from sklearn.ensemble import RandomForestRegressor
regressor = RandomForestRegressor(n_estimators=100, random_state=0)
eval_on_features(X, y, regressor)
X_hour = citibike.index.hour.values.reshape(-1, 1)
eval_on_features(X_hour, y, regressor)
X_hour_week = np.hstack([citibike.index.dayofweek.values.reshape(-1, 1), citibike.index.hour.values.reshape(-1, 1)])
eval_on_features(X_hour_week, y, regressor)
from sklearn.linear_model import LinearRegression
eval_on_features(X_hour_week, y, LinearRegression())
enc = OneHotEncoder()
X_hour_week_onehot = enc.fit_transform(X_hour_week).toarray()
eval_on_features(X_hour_week_onehot, y, Ridge())
poly_transformer = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)
X_hour_week_onehot_poly = poly_transformer.fit_transform(X_hour_week_onehot)
lr = Ridge()
eval_on_features(X_hour_week_onehot_poly, y, lr)
hour = ["%02d:00" % i for i in range(0, 24, 3)]
day = ["월", "화", "수", "목", "금", "토", "일"]
features = day + hour
features_poly = poly_transformer.get_feature_names(features)
features_nonzero = np.array(features_poly)[lr.coef_ != 0]
coef_nonzero = lr.coef_[lr.coef_ != 0]
plt.figure(figsize=(15, 2))
plt.plot(coef_nonzero, 'o')
plt.xticks(np.arange(len(coef_nonzero)), features_nonzero, rotation=90)
plt.xlabel("특성 이름")
plt.ylabel("계수 크기")
'인공지능' 카테고리의 다른 글
[빅데이터 직무연구회] 7회차 모임 정리 (0) | 2018.06.26 |
---|---|
[빅데이터 직무연구회] 6회차 모임 정리 (2) (0) | 2018.06.06 |
[빅데이터 직무연구회] 5회차 모임 예제 소스 (3) (0) | 2018.05.19 |
[빅데이터 직무연구회] 5회차 모임 예제 소스 (2) (0) | 2018.05.19 |
[빅데이터 직무연구회] 5회차 모임 예제 소스 (1) (0) | 2018.05.19 |
댓글