2018. 1. 11. 01:15ㆍML(머신러닝)/BASIC
ㅇ우리는 이제까지 보통 2차원 실수행 배열로 데인터 포인트 특성 - 연속형 특성
하지만 일반적인 특성의 전형적인 형태는 범주형 특성 또는 이산형 특성 -> 보통 숫자가 아님
범주형 특성과 연속형 특성의 차이 분류와 회귀 차이와 비슷하지만, 출력이 아닌 입력에 대한 점
연속형 - 픽셀 밝기, iris 측정값
범주형 - 제품의 브랜드, 색상, 판매분류 (연속된 값으로 나타나지 않는다)
책과 옷사이에는 중간 값 x , 순서도 x
하지만 데이터가 어떤 형태의 특성으로 구성되어 있는가 보다 -> 어떻게 표현하는지에 따라 머신러닝 성능의 영향을 크게 준다.
지도, 비지도에서는 스케일이 중요하다 함 / 스케일 조정 x -> 측정치가 cm / m 에 따라 차이가 생긴다.
상호작용 이나 다항식을 추가 특성으로 넣는 것도 도움이 된다.
가장 적합한 데이터 표현을 찾는 것을 특성공학 / 실제 문제를 풀기 위해 주요 작업 중 하나
올바른 데이터 표현 > 매개변수 보다 성능에 더 큰 영향 줌
범주형 변수
남자 / 여자, 직업별 = 정성적 데이터 -> 다른 방식으로 표현 해야함
1) one-hot-encoding(가변수)
=one-out-of-N encoding = dummy variable
dummy variable 은 범주형 변수 -> 0 / 1 값을 가진 하나 이상의 새로운 특성
0, 1 변수 -> 선형 이진 분류 공식에 적용 할 수 있어서 -> 개수에 상관없이 범주마다 하나의 특성으로 표현
a ,b ,c ,d -> a (1,0,0,0) b(0,1,0,0) ... 이런식으로
print("원본 특성:\n", list(data.columns), "\n") data_dummies = pd.get_dummies(data) print("get_dummies 후의 특성:\n", list(data_dummies.columns))
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)))
테스트 점수: 0.81
scikit-learn 에서는 문자열도 타깃값으로 가능하다 - 그대로 사용 가능
get_dummies() = 훈련과 테스트 세트 범주형이 같은 방식으로 표현되어야 함
-> 안그러면 성능이 나빠질 경우가 생김
2) 숫자로 표현된 범주형 특성
범주형 변수 -> 숫자로 분류 함
a,b,c -> 0 ,1, 2
예컨데 별 다섯개 만점 같은 데이터 경우 적절한 인코딩 방법은 풀려는 문제나 데이터, 알고리즘에 따라서 판단
pandas get_dummies -> 숫자 특성은 모두 -> 연속형이라고 생각해서 -> 가변수 안만듬
대신 어떤 열이 연속형인지 범주형인지 지정 scikit-learn -> OneHotEncoder
숫자로 된 열 -> 문자열로 바꿀 수 있다.
# 숫자 특성과 범주형 문자열 특성을 가진 DataFrame을 만듭니다 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=['숫자 특성', '범주형 특성']))
구간 분할, 이산화 그리고 선형 모델, 트리 모델
데이터 가장 잘표현 하는 법 -> 데이터가 가진 의미 + 어떤 모델 사용할지
폭넓게 사용하는 알고리즘 -> 1. 선형모델 2. 트리기반(결정 ,그래디언트 등등)
-> 표현 방식으로 인해 미치는 영향이 매우 다름 !!
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")
선형 모델 - 선형 관계 - 직선
결정트리 - 훨씬 복잡한 모델 그러나 데이터 표현 형태에 따라 굉장히 달라짐
연속형 데이터 - 아주 강력한 선형 모델을 만드는 방법 하나는 -> 구간 분할(이산화)(binding)
bins = np.linspace(-3, 3, 11)
print("bins: {}".format(bins))
bins: [-3. -2.4 -1.8 -1.2 -0.6 0. 0.6 1.2 1.8 2.4 3. ]
which_bin = np.digitize(X, bins=bins)
which_bin = np.digitize(X, bins=bins)
print("\n데이터 포인트:\n", X[:5])
print("\n데이터 포인트의 소속 구간:\n", which_bin[:5])
==> 이렇게 함으로 써 -> 연속형 특성 -> 각 데이터 포인트 어느 구간에 속했는지 -> 인코딩한 범주형 특성
scikit-learn 모델 적용하기 위해 - preprocessing 모듈의 OneHotEncoder로 이산적인 특성으로 -> onehot-encoding으로 변환
onehotencoder 는 pandas.get_dummies 와 같지만 -> 숫자로 된 범주형 변수에만 적용
from sklearn.preprocessing import OneHotEncoder # 변환을 위해 OneHotEncoder를 사용합니다 encoder = OneHotEncoder(sparse=False) # encoder.fit은 which_bin에 나타난 유일한 값을 찾습니다 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("입력 특성")
==> 선형 트리와 결정 트리 실선이 완전히 겹친다.
이 두 모델이 예측한 것은 상숫값 / 각 구간 안에서는 특성의 값이 상수
-> 어떤 모델이든 그 구간 포인트에 대해서는 같은 값을 예측
-> 구간 나눈 것을 보면 -> 선형 트리 훨씬 유리 / 결정 트리 덜 유연 해짐
트리 모델은 데이터를 자유롭게 나눌 수 있어서 -> 오히려 덜 유연해짐
결정 트리는 한번에 여러 특성 고려 가능 / 선형트리는 나눔에 따라서 큰 이득
용량이 크고 고차원 -> 선형 모델 사양한다면 구간분할 모델 성능 높이는데 도움 준다.
상호작용과 다항식
특성을 풍부하게 -> 원본데이터 상호 작용(interaction) 과 다항식(polynomial) 추가
-> 통계적 모델링에 주로 사용 -> 일반 어플리케이션에도 많이 적용
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 축 특성이 하나이므로 기울기도 하나
-> 기울기가 모두 같으니 유익해보이지 않음 -> 다른 기울기 효과 주고 싶음
-> 상호작용 추가하기
X_product = np.hstack([X_binned, X * X_binned])
print(X_product.shape)
# 즉 이 값은 구간 안에서는 원본 특성 다른 곳에서는 0
# X_binned = encoder.transform(which_bin) 이므로 해당 구간 외에는 0
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
# x ** 10까지 고차항을 추가합니다
# 기본값인 "include_bias=True"는 절편에 해당하는 1인 특성을 추가합니다
poly = PolynomialFeatures(degree=10, include_bias=False)
poly.fit(X)
X_poly = poly.transform(X)
# include_bias=True 이면 -> 절편을 고려하여 11개의 특성이 만들어 진다.
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")
아무런 변환하지 않은 원본 데이터 -> SVM -> 다항 회귀와 비슷한 복잡도로 가진 예측 만듬
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) # DEFAULT = include_bias=True
# DEGREE=2 -> 원본 특성에서 2개를 뽑아 모든 곱을 얻는다
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()))
다항 특성 이름: ['1', 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12',
'x0^2', 'x0 x1', 'x0 x2', 'x0 x3', 'x0 x4', 'x0 x5', 'x0 x6', 'x0 x7', 'x0 x8', 'x0 x9',
'x0 x10', 'x0 x11', 'x0 x12', 'x1^2', 'x1 x2', 'x1 x3', 'x1 x4', 'x1 x5', 'x1 x6', 'x1 x7',
'x1 x8', 'x1 x9', 'x1 x10', 'x1 x11', 'x1 x12', 'x2^2', 'x2 x3', 'x2 x4', 'x2 x5', 'x2 x6',
'x2 x7', 'x2 x8', 'x2 x9', 'x2 x10', 'x2 x11', 'x2 x12', 'x3^2', 'x3 x4', 'x3 x5',
'x3 x6', 'x3 x7', 'x3 x8', 'x3 x9', 'x3 x10', 'x3 x11', 'x3 x12', 'x4^2', 'x4 x5', 'x4 x6',
'x4 x7', 'x4 x8', 'x4 x9', 'x4 x10', 'x4 x11', 'x4 x12', 'x5^2', 'x5 x6', 'x5 x7', 'x5 x8',
'x5 x9', 'x5 x10', 'x5 x11', 'x5 x12', 'x6^2', 'x6 x7', 'x6 x8', 'x6 x9', 'x6 x10', 'x6 x11',
'x6 x12', 'x7^2', 'x7 x8', 'x7 x9', 'x7 x10', 'x7 x11', 'x7 x12', 'x8^2', 'x8 x9', 'x8 x10',
'x8 x11', 'x8 x12', 'x9^2', 'x9 x10', 'x9 x11', 'x9 x12', 'x10^2', 'x10 x11', 'x10 x12', 'x11^2', 'x11 x12', 'x12^2']
# 만약 degree=3 이면 -> 원본 특성,중복을 허용하여 2개를 뽑아 -> 곱의 항 중복을 허용 -> 3개를 뽑아 만들 수 있는 항
1+13+91+455 = 560 interation_only=True -> 거듭제곱이 포함된 항은 모두 제외
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)))
상호작용 특성이 없을 때 점수: 0.621 상호작용 특성이 있을 때 점수: 0.753
#ridge 다항식과 상호작용으로 -> 성능 크게
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)))
일변량 비선형 변환
앞에서 제곱 항, 세제곱 항 추가 -> 선형 회귀 모델에 도움이 된다
-> log / exp / sin 수학 함수를 적용하는 방법도 특성 변환에 유용
트리기반 -특성의 순서에만 영향
선형 모델과 신경망 - 각 특성의 스케일과 분포에 밀접하게 연관
특성과 타깃값 사이 비선형 -> 특히 선형 회귀에서더 더욱 어려움
log / exp -> 데이터 스케일을 변경해 선형 모델과 신경망의 성능 도움 됨
sin / cos -> 주기적인 패턴이 들어있는 데이터 -> 편리
대부분의 모델 -> 정규분포와 비슷 할 때 최고의 성능을 냅니다.(종모양)
-> log / exp 사용하는 것은 편법어지만 -> 쉽고 효과적인 방법
도움 되는 경우 -> 정수 카운트 데이터 다룰 때
카운트 데이터 (사용자가 얼마나 자주 로그인 하지는) ->음수값 x
-> 특별한 통계 패턴을 따르는 경우가 많다
확률적 요소를 가진 많은 알고리즘의 이론-> 정규분포 근간
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].astype('int'))))
plt.xlim(0, 160) plt.ylim(0, 70) 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))
데이터셋 + 모델의 조합 -> 최적의 변환 방법 찾기 -> 예술에 가까움
-> 이 예에서는 모든 특성이 같은 속성을 가지고 있다 -> 하지만 이런 경우 드뭄 -> 일부 특성만 변환하거나 모두 다르게 변환
-> 트리 모델에서는 불필요 / 선형 모델에서는 필수
가끔 회귀에서 y변수 -> 해주는 것도 좋음 -> 주문 횟수 같은 경우 -> log(y+1)를 사용하면 도움 됨
구간 분할 / 다항식/ 상호작용 주어진 상황에서 성능에 큰 영향 -> 선형 모델/ 나이브 베이즈 같은 경우
반면에 트리 기반 모델 -> 스스로 상호 작용 잘 찾음 / 변환 안해도 됨
svm . knn, 신경망 -> 구간 분할 /상호작용 /다항식 이득 그러나 선형모델이 가장 영향을 많이 받는다.
특성 자동 선택
새로운 특성 추가 -> 차원의 수 이상으로 증가 -> 모델 복잡 -> 과대 적합 가능성 UP
새로운 특성 추가/ 고차원 데이터 셋 -> 가장 유용한 특성만 선택, 나머지 무시해서 -> 특성의 수 주이기
-> 모델 간단 -> 일반화 성능 UP -> 하지만 어떻것이 좋은지 어떻게 알 수 있을까??
1. 일변량 통계(univariate statistics)
2. 모델 기반 선택(model-based selection)
3. 반복적 선택(iterative selection)
모두 지도 학습 방법이므로 최적값 찾으려면 타깃값 필요
-> 그리고 훈련 세트/ 테스트 세트 나눈 다음 -> 훈련 데이터만 특성 선택에 사용
1. 일변량 통계(univariate statistics)
개개의 특성과 타깃 사이에 중요한 통계적 관계가 있는지 계산
-> 깊게 관련되어 있는 것만 선택
분류 - 분산 분석(anova) == 데이터를 클래스 별로 나누어 평균을 비교하는 방법
F-통계값이 높으면 그 특성은 클래스별로 평균이 서로 다르다는 뜻
일변량 , 즉 각특성이 독립적으로 평가된다
따라서 다른 특성과 깊게 연관된 특성은 선택 x -> 일변량 분석은 계산 매우 빠르고 평가를 위해 모델 만들 필요 X
한편으로 이 방식은 특성을 선택한 후 적용하려는 모델에 상관없이 사용
SCIKIT_LEARN 에서 일변량 분석 분류 : f_classif(기본값) / 회귀 : f_regression 선택
-> 테스트 하고 p-value 기초하여 특성을 제외하는 방식
높은 p-value 특성을 제외 할 수 있도록 임계값을 조정하는 매개변수를 사용한다
가장 간단 : SelectKBest 는 고정된 K개의 특성을 선택하고 -> SelectPercentile은 지정된 비율만큼 특성을 선택
sklearn.feature_selection - selectkbest 처럼 score_func 매개변수를 사용하여 지정
분류 문제 : f_classif
에서는 클래스별 평균의 분산() 을 전체() 에서
클래스별 평균 분산 () 을 뺀 값으로 나누어 F-통게량을 계산 클래스가 K 개 / 샘플 N개
,
회귀 문제 : f_regression
F- 통계값과 P-VALUE 계산
p-values_ 속성에 f-통계값 이후의 오른쪽 꼬리 부분의 면적
p-values_ 값이 큰 특성은 클래스들의 평균과 비슷 -> 타깃에 미치는 영향이 적다고 판단
SelectKBest SelectPercentile 선택한 기준 F-통계값이며 값이 클수록 평균의 분산의 비교적 크다는 것
F-통계값 (scores_ 속석에 저장 된다)
from sklearn.datasets import load_breast_cancer
from sklearn.feature_selection import SelectPercentile, f_classif
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))
# 데이터에 노이즈 특성을 추가합니다
# 처음 30개는 원본 특성이고 다음 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)
# f_classif(기본값)와 SelectPercentile을 사용하여 특성의 50%를 선택합니다
select = SelectPercentile(score_func=f_classif, 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() # 선택된 특성을 boolen 값
print(mask)
# True는 검은색, False는 흰색으로 마스킹합니다
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("특성 번호")
plt.yticks([0])
마스킹된 그래프 -> 대부분 원본 특성 / 노이즈 특성이 거의 모두 제거
-> 그러나 원본 특성이 완벽하게 복원된 것은 아니다
-> 전체와 선택된 특성 비교
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)))
2. 모델 기반 특성 선택
지도 학습 머신러닝 모델 사용 -> 특성의 중요도 평가 -> 가장 중요한 것만 선택
지도학습 모델은 최종적으로 사용할 학습 모델과 같은 필요는 없다.
특성 선택을 위한 모델은 각 특성의 중요도 측정 -> 순서 매길 수 있다
-> 결정 트리와 이를 기반으로 한 모델은 각 특성의 중성의 중요도 담겨 있는 feature_importances_ 속성을 제공
선형 모델 계수의 절대값도 특성의 중요도를 재는데 사용 할 수 있습니다.
L1 규제를 사용한 선형 모델 -> 일부 특성의 계수만 학습 -> 이를 그 모델 자체를 위해 특성이 선택된다고 생각하지만
-> 다른 모델의 특성 선택은 한 번에 모든 특성을 고려하믈 (상호 작용 잡아낼 수 있다면) -> 상호작용 부분을 반영
-> SelectFromModel 에 구현
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier
select = SelectFromModel(
RandomForestClassifier(n_estimators=100, random_state=42),
threshold="median")
# SelectFromModel 은 (지도 학습 모델로 계산된) 중요도가 지정한 임게치보다 큰 모든 특성 선택
# 일변량 분석으로 선택한 특성과 결과를 비교하기 위해 절반 가량의 특성이 선택될수 있도록 -> 중간값을 임계치로 사용한다.
# 트리 100개 랜덤 포레스트 분류 -> 특성 중요도 계산 -> 일변량 분석 보다는 강력한 모델
# L1 규제가 없는 모델을 사용하는 경우 -> SelectFromModel threshold 매개변수의 기본 값은 평균값을 나타내는 "mean" 이다.
# 1.2*mean . 1.3*mean 가능
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()
# True는 검은색, False는 흰색으로 마스킹합니다
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("특성 번호")
plt.yticks([0])
이번에는 두 개를 제외한 모든 원본 특성이 선택 되었습니다. -> 특성을 40개 선택 -> 일부 노이즈도 선택
X_test_l1 = select.transform(X_test)
score = LogisticRegression().fit(X_train_l1, y_train).score(X_test_l1, y_test)
print("Test score: {:.3f}".format(score))
3. 반복적 특성 선택
일변량 분석 -> 모델 사용 x
모델기반 선택 -> 하나의 모델 사용해 특성 선택
반복적 특성 선택 -> 특성의 수가 각기 다른 일련의 모델
기본적으로 2가지 방법
1. 특성을 하나도 선택하지 않은 상태로 시작해 -> 어떤 종료 조건을 도달 할때까지 하나씩 추가하는 방법
2. 모든 특성을 가지고 시작해 -> 어떤 종료 조건이 될 때까지 하나씩 추가하는 방법
모델이 만들어지기 때문에 -> 계산 비용이 훨씬 많이 듬
재귀적 특성 제거가 이런 방법의 하나
이 방법은 모든 특성으로 시작해 모델 만들고 가장 낮은 특성부터 제거
-> 그런 다음 제거한 특성 빼고 나머지 특성 전체로 새로운 모델 -> 미리 정의한 특성개수 남을 때 까지 계속
-> 특성의 중요도를 결정하는 방법 제공
from sklearn.feature_selection import RFE select = RFE(RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=40) # 40번 실행 select.fit(X_train, y_train) # 선택된 특성을 표시합니다 mask = select.get_support() plt.matshow(mask.reshape(1, -1), cmap='gray_r') plt.xlabel("특성 번호") plt.yticks([0])
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)))
테스트 점수: 0.951
RFE 안에 있는 랜덤 포레스트의 성능이 로지스틱 회귀의 성능과 같다.
다른 말로 특성 선택이 제대로 되면 -> 선형 모델의 성능은 랜덤 포레스트와 견줄만 한다.
어떤 입력값 넣을지 확신 X -> 특성 자동 선택이 도움이 된다.
->예측 속도를 높이거나 해석하기 더 쉬운 모델을 만드는데 필요한 만큼 특성의 수를 줄이는데 효과적
_> 대부분 실전에서는 큰 향상 끌어내진 못한다.
그래도 특정 선택은 중요한 도구이다.
# 반복적 선택 방법 1.전진 선택법 2. 후진 선택법 3. 교차 선택법 scikit-learn 제공 x
하지면 회귀 모델 score R^2 값을 어들 수 잇으므로 직접 구현 할 수있다.
전진 선택법 -> 하나씩 교대로 포함 -> 가장 높은 R^2 특성 추가해 나가고
후진 선택법은 -> 하나씩 빼면서 가장 낮은 R^2 되는 특성 제거하면 된다.
전문가 지식 활용
특성 공학 - 특정한 어플리케이션을 위해 전문가적 지식을 사용할 수 있는 중요한 영역
많은 경우 / 머신러닝의 목적 - 전문가가 설계하는 규칙을 만들지 않기 위해서지만
그 분야의 전문 지식이 무시된다는 뜻은 아님 / 종종 분야 전문가는 초기 데이터에서 더 유용한 선택 도움줌
여행사 / 항공료 예측 - 중요한 몇가지는 학습 X
EX) 휴가 성수기 공휴일 근처 -> 항공료 비싸다 일부 공휴일은 고정되어 잇어서 날짜로부터 학습가능
음력 공휴일도 있고 기관이 지정합니다.
-> 이런 이벤트들은 데이터가 날짜를 사용해서만 기록되어서는 학습 X
그러나 공휴일과 방학 전후에 비행 스케줄이 기로된 특성 추가하는 일 어렵지 않음
-> 사전 지식이 특성으로 추가 -> 머신러닝 알고리즘에 도움이 된다
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.astype(int), xticks_name, rotation=90, ha="left") plt.plot(citibike, linewidth=1) plt.xlabel("날짜") plt.ylabel("대여횟수")
데이터 24시간 간격 낮과 밤 구분 가능 / 주중과 주말 패턴 꽤 차이
시계열 가능
어떤 날 까지는 훈련용 그 이후에는 테스트 데이터
# 타깃값 추출 (대여 횟수)
y = citibike.values
# POSIX 시간을 10**9로 나누어 변경
X = citibike.index.astype("int64").values.reshape(-1, 1) // 10**9
# 처음 184개 데이터 포인트를 훈련 세트로 사용하고 나머지는 테스트 세트로 사용합니다
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("대여횟수")
regressor = RandomForestRegressor(n_estimators=100, random_state=0)
eval_on_features(X, y, regressor)
eval_on_features(X, y, regressor) - POSIX 시간을 가진 특성 X 와 랜덤포레스트 회귀 모델을 eval_on_features(X, y, regressor) 함수에 전달
더 전문가 지식 들어가서
-> 훈련 데이터의 대여 데이터를 시간과 요일 이라는 두 요소가 중요한 것으로 보인다.
POSIX 학습 되지 않아서 -> 특성 제외
시간만 사용해보기
-> 주간 패턴 예측 X
X_hour = citibike.index.hour.values.reshape(-1, 1) eval_on_features(X_hour, y, regressor)
테스트 세트 R^2: 0.60
요일 정보도 추가하기
-> 주기적인 패턴 정보 찾는다 학습 시킨거 (요일별 / 시간당 평균 대여 횟수)
-> 굳이 복잡한 랜덤 말고 더 간단한 선형
랜덤 포레스트 회귀로 만든 예측은 여러트리가 에측한 값들의 평균 -> 그래서 8.24~8.31일의 예측값은 동일하다.
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())
테스트 세트 R^2: 0.13
성능 나쁘고 주기 패턴 이상 -> 요일과 시간이 정수로 인코딩 -> 연속형 변수로 해석 됨
-> 선형 모델은 시간을 선형 함술모나 학습해서 -> 하루에서 시간이 흐를 수록 대여수가 늘어나게 학습되 어있음
-> 하지만 실제 패턴 이보다 복잡 -> 이 패턴 잡기 위해 AUTOENCODER 사용
-> 정수형을 범주형 변수로 해석
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) # 모든 상호작용 특성에 이름을 달아줌
# 그리고 계수가 0이 아닌 것만 출력
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("계수 크기")
요약 및 정리
범주형 변수 다루는 법 배움
ONE-HOT-ENCODING 범주형 변수처럼 -> 머신러닝 알고리즘에 적한 방식으로 표현하는 것이 아주 중요
그리고 새로운 특성을 만드는 것과 데이터 표현하는 것이 아주 중요함
그리고 새로운 특성을 만드는 것과 데이터 특성을 유도하기 위해 전문가 지식 활용하는 것 중요함
선형 모델 :
구간 분할 / 다항식과 상호작용 - 큰이득 가능
랜덤 포레스트, SVM (비선형) :
특성을 늘리지 않고도 복잡한 문제 학습 가능
실제로는 어떤 특성 사용하냐고 중요
'ML(머신러닝) > BASIC' 카테고리의 다른 글
Binary Classification 중 주의해야 할 것과 팁 (0) | 2019.05.30 |
---|---|
모델평가와 성능평가 _미완성 (0) | 2018.01.15 |
지도_비지도 요약 및 정리 (0) | 2018.01.09 |
[ Python ]비지도 군집, 병합군집, 덴드로그램, DBSCAN (2) | 2018.01.09 |
비지도_PCA, NMF, 매니폴드 학습(T-SNE) (0) | 2018.01.07 |