위의 방법을 반복적으로 수행해서 트리를 형성(hyper parameter:tree의 level)
전체 feature에서 선정된 특정갯수의 feature로만 트리를 생성함
hypter parameter
n_estimators : 트리의 갯수 : 많을수록 과적합을 피할수 있다 : 2000개 이상
max_depth : 트리의 레벨
max_features : 최대 변수의 갯수
from sklearn.datasets import make_moons
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import mglearn
# 샘플 데이터
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
# 데이터셋 분리 : stratify : y 데이터의 비율을 맞춤
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)
# 모델 생성 및 학습
model = RandomForestClassifier(n_estimators=5, random_state=2).fit(X_train, y_train)
fig, axes = plt.subplots(2, 3, figsize=(20, 10))
for i, (ax, tree) in enumerate(zip(axes.ravel(), model.estimators_)):
ax.set_title("tree {}".format(i))
mglearn.plots.plot_tree_partition(X, y, tree, ax=ax)
mglearn.plots.plot_2d_separator(model, X, fill=True, ax=axes[-1, -1], alpha=.4)
axes[-1, -1].set_title("random forest")
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.show()
*train_test_split
Train set 과 Test set 을 분류 -> 모델 overfitting 방지. train set으로 학습 후 test set 으로 validation 해준다.
test_size: 테스트 셋 구성의 비율을 나타냅니다. train_size의 옵션과 반대 관계에 있는 옵션 값이며, 주로 test_size를 지정해 줍니다. 0.2는 전체 데이터 셋의 20%를 test (validation) 셋으로 지정하겠다는 의미입니다. default 값은 0.25 입니다.
shuffle: default=True 입니다. split을 해주기 이전에 섞을건지 여부입니다. 보통은 default 값으로 놔둡니다.
stratify: default=None 입니다. classification을 다룰 때 매우 중요한 옵션값입니다. stratify 값을 target으로 지정해주면 각각의 class 비율(ratio)을 train / validation에 유지해 줍니다. (한 쪽에 쏠려서 분배되는 것을 방지합니다) 만약 이 옵션을 지정해 주지 않고 classification 문제를 다룬다면, 성능의 차이가 많이 날 수 있습니다.
random_state: 세트를 섞을 때 해당 int 값을 보고 섞으며, 하이퍼 파라미터를 튜닝시 이 값을 고정해두고 튜닝해야 매번 데이터셋이 변경되는 것을 방지할 수 있습니다.
after_entropy_1,2,3은 각각 (0, 0.9709505944546686, 0.9709505944546686)
가중평균의 엔트로피 after_entropy = 0.6935361388961919
information_gain = before_entropy - after_entropy
[노드를 설정하는 방법]
information gain이 가장 높은 변수를 root 변수로 설정
나머지 변수들을 information gain을 구하고 information gain이 가장 높은 변수를 다음 노드로 설정
[Iris 예제]
sklearn datasets에 내장되어있는 iris 데이터를 불러온다.
from sklearn import datasets
from sklearn.tree import DecisionTreeClassifier
iris = datasets.load_iris()
feature와 target 이 이미 나뉘어 있다.
iris data를 보기 쉽게 데이터 프레임 형태로 변환하면,
import pandas as pd
df = pd.DataFrame(iris.data, columns=iris.feature_names)
target 값들은 나오지 않는데, target 컬럼을 아래와 같이 추가한다.
df['label'] = iris.target
df.tail()
[모델의 학습]
model = DecisionTreeClassifier(max_depth=2, random_state=1).fit(iris.data, iris.target)
max_depth : 몇번 분할할 것인지. 너무 많이 분할하면 overfitting 된다.
[모델 시각화]
from sklearn.tree import export_graphviz
export_graphviz(model, out_file="tree.dot", class_names = iris.target_names,
feature_names = iris.feature_names, impurity=True, filled=True)
import graphviz
# 위에서 생성된 tree.dot 파일을 Graphiviz 가 읽어서 시각화
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
※ 지니계수 : 불순도를 나타내는 지표. value = [ ] 로 주어진 데이터 분포에서의 지니계수.
[모델 평가]
Confusion Matrix
True Positives : 1인 레이블을 1이라 하는 경우를 True Positives라고 한다. -> 관심 범주를 정확하게 분류한 값.
False Negatives : 1인 레이블을 0이라 하는 경우를 False Negatives라고 한다. -> 관심 범주가 아닌것으로 잘못 분류함.
False Positives : 0인 레이블을 1이라 하는 경우를 False Positives라고 한다. -> 관심 범주라고 잘못 분류함.
True Negatives : 0인 레이블을 0이라 하는 경우를 True Negatives라고 한다. -> 관심 범주가 아닌것을 정확하게 분류.
TP/ TN 이 높고 FN/FP가 낮은게 좋음.
from sklearn.metrics import confusion_matrix
confusion_matrix(iris.target, model.predict(iris.data))
이 케이스는 종속데이터가 0/1로 나오고, 종속/독립 모두 범주형이니, 베르누이나 Multinomial 나이브 베이즈를 쓰는게 맞다.
[Multinomial NB] : 가우시안이랑 모듈만 다르고 방법은 똑같다.
from sklearn.naive_bayes import MultinomialNB
# 모델 학습
model_2 = MultinomialNB().fit(feature, target)
# 예측
pred_2 = model_2.predict(feature)
# 평가 : GaussianNB 보다 성능이 좋다. : 독립변수가 정규분포가 아니기 때문에
len(pred_2[pred_2 == target.values]) / len(target)
새로운 데이터를 주고 모델을 통해 예측을 해보자.
입력데이터는 다음과 같은 형태이구나 파악.
결과("Play Tennis")를 제외한 평가용 샘플데이터를 만든다.
sample = np.array([1, 0, 0, 0, 1])
MultinomialNB 클래스의 메서드인 .predict_proba 를 이용해 확률을 구한다.
.predict와의 차이점은 .predict는 예측에 의해서 확률이 가장 높은 값을 반환하는 것이고,
.predict_proba 는 가능한 결과값들에 대한 확률을 반환한다. ( target의 인덱스 순서대로 확률이 나타남.)
안의 feature는 벡터값인 [sample]을 넣어준다.
(Jupyter notebook은 Shift+Tab 을 하면 설명을 볼 수 있다. )
proba = model_2.predict_proba([sample])[0]
result = model_2.predict([sample])[0]
proba, result
다항 회귀할 때 봤던 것 처럼,
predict_proba([sample]) 는 벡터,
proba = predict_proba([sample])[0] 은 스칼라 값이다.
위에서 target = tennis["PlayTennis"].map({'Yes': 0, 'No': 1}) 라고 정의했기 때문에,
이 식을 풀기 위해서 앞서 공부했던 선형회귀분석을 해보고, 그 모델을 평가해볼 것이다. (오차가 당연히 클 것.)
그 다음 다항회귀 분석을 통해서 모델을 만들고 결과를 분석해보자.
결론부터 말하자면, 다항회귀분석이라고 해서 다른 모듈을 사용하는게 아니라
선형회귀분석과 동일한 모듈을 사용한다. (OLS, LinearRegression 등)
다만, feature를 polynomial로 바꿔주는 절차가 필요하다.
1. 샘플 데이터 생성.
m = 100
np.random.seed(0)
x = 6 * np.random.rand(m, 1) - 3
y = 0.5 * x ** 2 + x + 2 + np.random.randn(m, 1)
x는 랜덤함수로 뽑은 100개의 난수.
y 는 0.5 X^2+X+2+ 0~1 사이의 난수 오차 100개.
plt.scatter(x, y)
plt.show()
오차를 입력했기 때문에 정확한 2차함수그래프가 아니라 scattered 된 plot을 볼 수 있다.
만들어진 샘플을 분석해보자.
2. 만들어진 샘플로 모델을 학습시킨다.
import statsmodels.api as sm
from sklearn.metrics import mean_absolute_error
# 상수항 추가
feature = sm.add_constant(x, has_constant='add')
x[:2], feature[:2]
상수항 추가를 해주면 다음과 같이 맨 앞열에 1이 들어간다.
(statsmodel로 회귀분석할 때 해줘야 함. sklearn은 알아서 1 붙여서 계산함.)
OLS - 최소제곱법을 통해 모델을 만든다. ( 일단 선형회귀분석을 해서 상태를 본다;)
# 모델 학습
model_1 = sm.OLS(y, feature).fit()
# 예측값 구하기
pred_1 = np.dot(feature, model_1.params)
아까 만들어 놓은 샘플데이터의 y값을 학습에 사용한다. (오차를 포함한 raw data)
feature 값과, 모델의 구해진 파라미터 값을 내적해 모델을 통해 예측한 pred_1 값을 구한다.
3. 샘플 데이터(정답)와 모델로 예측한 추세선을 비교해본다.
plt.scatter(x, y) # 실제 데이터
plt.plot(x, pred_1, "r") # 예측 데이터 추세선
plt.show()
많이 안맞아 보인다 ..
4. 선형회귀분석을 통해 도출된 모델을 평가해보자.
model_1.params, mean_absolute_error(pred_1, y)
- sklearn을 통해 학습시켜도 결과는 똑같다. (참고)
from sklearn.linear_model import LinearRegression
# 모델 학습
model_2 = LinearRegression().fit(x, y)
# 예측값 구하기
pred_2 = model_2.predict(x)
# 그래프 그리기
plt.scatter(x, y) # 실제 데이터
plt.plot(x, pred_2, "r") # 예측 데이터 추세선
plt.show()
# 회귀계수 출력, 모델 평가(MAE)
model_2.intercept_, model_2.coef_, mean_absolute_error(pred_2, y)
feature = df[['AT', 'V', 'AP', 'RH']]
target = df["PE"]
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(feature, target) # 선형 모델 학습 (생성)
from sklearn.metrics import mean_absolute_error
pred = model.predict(feature)
mae = np.round(mean_absolute_error(target, pred), 2) # 모델 성능 확인.
<statsmodel을 이용한 모델링 >
import statsmodels.api as sm
feature = df[['AT', 'V', 'AP', 'RH']]
target = df["PE"]
sm_feature = sm.add_constant(feature) # add constant 해줘야함. sklearn에서는 필요없음.
model = sm.OLS(target, sm_feature).fit() # 선형모델 학습(생성)
print(model.summary2()) # 모델 성능 확인.
<불필요한 컬럼(feature)들 제거> - feature 전체 넣고, 하나씩 빼고 각각 모델 만든다음 VIF 측정해본다. 높은데 영향주는 feature는 제거.
feature = df[['AT', 'RH']]
target = df["PE"]
sm_feature = sm.add_constant(feature)
model = sm.OLS(target, sm_feature).fit()
print(model.summary2())
pred = np.dot(sm_feature, model.params)
mae = np.round(mean_absolute_error(target, pred), 2)
3.VIF :Variance Inflation Factor. 를 계산해본다. (모델 생성 전 ) 분산 팽창 요인. (역행렬 연산 시 1/(ad-bc) 로 연산을 하는데 다중 공선성이 높으면 ad-bc =0 이 되어 분모가 0, 발산하게 된다. ) VIF 지표가 높을 수록 다중공선성이 높다고 판단. 일반적으로 VIF가 10 이상인 경우, 다중공선성으로 판단. (절대적이지는 않음)
특정 feature를 y값으로 설정하여 모델을 만들었을때 나오는 결정계수(r-squared)로 아래의 수식을 연산한다.
VIF 구하는 방법 1) 직접 계산
feature 끼리 돌아가면서 target으로 설정하고 나머지를 feature로 삼아 모델을 생성했을 때, VIF 값을 측정한다.
import statsmodels.api as sm
feature = features_df[["radio", "newspaper"]]
target = features_df["TV"]
model = sm.OLS(target, feature).fit()
1 / (1 - model.rsquared)
-> VIF Factor of TV target으로 삼은 녀석의 VIF 값. 높으면 사용하지 않는게 좋다. , radio, newspaper에 대해서도 각각 진행.
2) 함수로 확인.
statsmodels에 이미 VIF를 구하는 함수가 있다.
variance_inflation_factor( feature.values, feature index )
ex) ["TV", "Radio","Newspaper"]
feature index
0 : TV,
1: Radio
2 : Newspaper
from statsmodels.stats.outliers_influence import variance_inflation_factor
feature = df[ ['', '' ] ]
target = df [ ]
pd.DataFrame({
"feature": feature.columns,
"VIF": [variance_inflation_factor(feature.values, idx)
for idx in range(feature.shape[1])]
})
4. (모델 생성 후) summary 참조. (05. linear_regression_summary_table)
***summary에서 참고할 만한 수치 :
Coef : 회귀계수 . 기울기에 해당. 0에 가까울 수록 상관도가 낮다 (target과의). 너무 작은 애는 쓸모없다. R-squared : 모델의 결정계수 : 높을수록 좋다. 분산설명력. p-value = P> |t| : 우연에 의해 나올 확률 . 보통 0.05 보다 높으면 안좋다고 본다. 5%. t = t test : (two sample test) : 회귀계수가 우연인지 확인하는 지표. 0과 가까울 수록 우연일 확률이 높다. 작은게 좋음.
주어진 점들과의 오차의 제곱이 최소가 되는 추세선을 구한다. ( python에서는 statsmodel 혹은 Scikit Learn을 이용한다. )
1) 샘플 데이터(학습시킬 raw data, 주어진 점들)를 데이터프레임으로 만든다 . 2) 산점도를 그려 추세를 파악 ( plt.scatter(df['x'], df['y']) plt.show() ) 3) y = ax +b +e (모든 점들이 한 직선 상에 있지않을 경우 오차가 있음. )라고 놓고 sklearn이나 statsmodel을 사용해 parameter값들을 구한다.
*statsmodels: 추정 및 검정, 회귀분석, 시계열분석 등의 기능을 제공하는 파이썬 패키지.
여기에 최소제곱법을 이용해 추세선을 구하는 코드(OLS)가 내장되어있다. statsmodels에는 아래와 같은 기능들이 있다.
예제 데이터셋 검정 및 모수추정 회귀분석 선형회귀 강건회귀 일반화 선형모형 혼합효과모형 이산종속변수 시계열 분석 SARIMAX 모형 상태공간 모형 벡터 AR 모형 생존분석 요인분석 ...
------------------------------------------------------------------ 최소제곱법(OLS)을 이용해서 추세선 구하기 OLS(OLS: Ordinary Least Squares), 최소제곱법, 또는 최소자승법, 최소제곱근사법, 최소자승근사법(method of least squares, least squares approximation)이라고도 하며, 어떤 계의 해방정식을 근사적으로 구하는 방법으로서 근사적으로 구하려는 해와 실제 해의 오차의 제곱의 합이 최소가 되는 해를 구하는 방법이다.
import statsmodels.api as sm
ols('종속변수명 ~ 독립변수명', data=데이터프레임명).fit().summary()
statsmodels를 사용할 때는 feature, target, model을 지정해줘야함. feature는 입력시켜 줄 학습값 중 독립변수 (x),
target은 종속변수 (y) model 은 statsmodel 내의 여러가지 방법 중에 어떤 함수를 쓸 것인지. (여기서는 ols)
OLS, 최소제곱법 역시 선형대수를 기반으로 하기 때문에, 행렬의 연산 시 상수항 1을 맨 앞열에 독립변수 데이터에 추가해줘야한다. (상수항 결합).
def error_func(df, a, b):
e = 0
for idx, data in df.iterrows():
e += (data.y - a - b * data.x) ** 2 # feature 데이터 x, y 값과 비교.
return e # 오차들 제곱합 값을 반환한다.
df = (x, y) 형태의 데이터 프레임, (a, b) 는 추세선 함수의 가중치 벡터이다.
아래 식에서
data.y : 실제값
a+b*data.x : 추정치
* 참고) iterrow() : 데이터프레임의 각 행 정보를 담은 객체.
(행 넘버, 해당 행 데이터) .. (마지막 행 넘버, 해당 행 데이터)
범위 내의 모든 a, b에 대해서 error_func를 돌려 각각 (a,b)에 대한 e를 저장한다.
error_datas = []
for a_data in a_datas:
for b_data in b_datas:
error_datas.append({
"a_data": a_data,
"b_data": b_data,
"error": error_func(df, a_data, b_data),
})
# error_datas : a, b, 오차 제곱값 딕셔너리 형태로 반환
error_df = pd.DataFrame(error_datas)
error_df
OLS 함수 안쓰고 a,b 찾기 2. 경사하강법으로 추세선 구하기 : a, b의 미분값으로 기울기 구하기.
위의 오차제곱법은 a, b의 범위를 정해놓고 그 범위 내의 a, b를 전부 입력해서 최소값을 찾는데, a, b를 각각 다 넣으면 a x b 만큼의 경우의 수가 나오게 된다. (batch 사이즈 고려하면 더 많아짐. )
b 값의 범위만 구해도 구할 수 있다.
1) b 의 범위를 지정해준다.
2) 오차 제곱의 합을 구하는 함수를 만든다.
b_datas = np.arange(0, 5, 0.5)
def error_func(df, b):
e = 0
for idx, data in df.iterrows():
e += (data.y - b * data.x) ** 2
return e # 오차 제곱합값을 반환함.
error_datas = []
# 0 ~ 4.5 까지 에러를 출력
for b_data in b_datas:
e = error_func(df, b_data)
error_datas.append({
"b_data":b_data,
"error": e,
})
# b 값이 2.5일때 가장 오차제곱합이 가장 작다.
error_df = pd.DataFrame(error_datas)
error_df
error_df[error_df.error == np.min(error_df.error)]
--------------------------------------------------- 경사하강법2 (일단 b 초기값 아무거나 정하고, 기울기 때려맞히기)
- 함수의 최소값은 기울기가 0이 되는 지점이다.
- 오차의 제곱 = (data.y - (b*data.x+a)) **2
- 오차의 제곱은 b에 대한 2차 방정식으로도 볼 수 있다.
- 오차의 제곱이 가장 작아지는 부분은 함수 기울기가 0일 때, b 값이 양수이면 값을 낮추고, 음수이면 값을 높여 error 가 0에 가장 가까운 b값을 채택한다.
1) b 의 초기값을 정해주고 최소의 기울기를 찾아가본다.
2) epoch : 몇번의 시도를 통해 기울기값을 찾을 지 정한다.
# b의 초기값
b = 50
# 최적값을 찾기위한 실행 횟수
# epoch가 낮으면 정확한 값을 찾기 어려움
epoch = 50
# 한번에 이동하는 크기제어
batch_size = 0.01
log_datas = []
def slope_func(df, b):
slope = 0
for idx, data in df.iterrows():
slope += -2 * (data.y - b * data.x) * data.x
return round(slope, 2)
for _ in range(epoch):
# b 값에 대한 기울기 구하기
slope = slope_func(df, b)
# print(np.round(b, 2), slope)
# 기울기가 0이면 반복문 빠져나가기
if slope == 0:
break
# 기울기가 0 미만이면 b값이 증가 (slope가 음수이므로 b 값이 증가)
# 기울기가 0 초과이면 b값이 증가 (slope가 양수이므로 b 값이 감소)
b -= batch_size * slope
b = np.round(b, 5)
# 에러 구하기
e = error_func(df, b)
# 데이터 저장
log_datas.append({
"b": np.round(b, 2),
"e": e,
"slope": slope,
})
log_df = pd.DataFrame(log_datas)
log_df.head(2)
len(log_df), b
--------------------------------------------------------------------- statsmodel 말고 sklearn을 이용해서 선형회귀분석 해보기. feature : 독립변수. target : 종속변수 .