[파이썬 머신러닝 완벽 가이드 (권철민)]
2장. 사이킷런으로 시작하는 머신러닝
데이터 전처리(Data Preprocessing)
ML 알고리즘은 어떤 데이터를 입력하는지에 따라 결과가 크게 바뀔 수 있음(Garbage In, Garbage Out)
사이킷런의 ML 알고리즘 적용 전에 처리해야 할 사항이 있습니다,
- 결손값(NaN, Null)을 허용하지 않는다 따라서 고정된 다른 값으로 변환해야 함
- Null 값이 얼마 되지 않는 경우 feature의 평균값 등으로 간단 대체 가능
- Null 값이 대부분인 경우 해당 feature은 drop 하는 것이 좋음(제거)
- Null 값이 일정 수준 이상(명확한 기준은 없음)인 경우 해당 feature가 중요도가 높다면 단순 평균 등으로 대체할 경우 예측 왜곡 심할 수 있으므로 더 정밀한 대체값 선정 필요
- 문자열 값을 입력값으로 허용하지 않는다. 따라서 모든 문자열 값은 인코딩 되어 숫자형으로 변환해야 함
- 카테고리형 feature : 코드 값
- 텍스트형 feature : 피처 벡터화(feature vectorization) 등으로 벡터화하거나 불필요한 feature일 경우 삭제
(주민번호, 아이디 등은 단순히 데이터 로우를 식별하는 용도이므로 중요한 요소 될수 없으므며, 알고리즘 복합하게 만드고 예측 성능을 떨어뜨린다.)
데이터 인코딩
- 대표적 ML 데이터 인코딩 방식
- 레이블 인코딩(Label Encoding)
- 원-핫 인코딩(One-Hot Encoding)
레이블 인코딩
사이킷런의 레이블 인코딩(Label enconding)은 LabelEncoder 클래스로 구현합니다.
- LabelEncoder 를 객체로 생선한 후 fit(), transform() 호출해 레이블 인코딩 수행
- 간단하게 문자열 값을 숫자형 카테고리 값으로 변환
- 주의점 :'01', '02'도 문자열이므로 1, 2와 같은 숫자형으로 변환해야 함
- 어떤 문자열이 어떤 숫자로 인코딩 되었는지 확인 힘들 경우 LabelEncoder 객체의 classes_ 속성 이용
- 인코딩 값을 다시 디코딩하려면 inverse_transform()
- 일괄적인 숫자값으로 변환되며, 숫자값의 크고 작음, 순서나 중요도의 특성으로 작용할 수 있음
> 특정 ML 알고리즘에서 가중치가 더 부여되거나 더 중요하게 인식할 가능성이 발생
(숫자 변환 값은 단순 코드이기에 순서나 중요도로 인식되어서는 안됩니다.)
> 선형 회귀와 같은 ML알고리즘에 적용 X, 트리 계역 ML알고리즘은 숫자의 특성을 반영하지 않아 OK
원-핫 인코딩(One-hot_encoding)
원-핫 인코딩은 피처 값의 유형에 따라 새로운 피처를 추가해 고유 값에 해당하는 칼럼에만 1을 표시. 나머지는 0 표시
(여러 개의 속성 중 단 한 개의 속성만 1로 표시)
>> 행 형태로 된 피처 고유 값을 열 형태로 차원 변환한 뒤, 고유 값에 해당하는 칼럼에만 1을 표시하고 나머지는 0으로
- 사이킷런에서 OneHotEncoder 클래스로 변환 가능
※주의점 : LabelEncoder와 다르게 2차원 데이터가 필요하다.
- OneHotEncoder를이용해 변환한 값이 최소 행렬(Sparse Matrix) 형태이므로 이를 다시 toarray()메서드를 이용해 밀집 행렬(Dense Matrix)로 변환해야한다.
- 원-핫 인코딩은 순서가 없는 카테고리형 변수를 머신러닝 모델에 올바르게 표현할 수 있지만, 특성의 수가 크게 증가할 수 있다는 단점이 있다. 이는 고차원의 데이터로 인해 모델 학습 시간이 증가하거나, 과적합(overfitting)이 발생할 수 있다.
예를 들어, '색깔'이라는 범주형 변수가 있고 이 변수에는 '빨강', '파랑', '녹색'이라는 세 가지 값이 있을 때 원핫인코딩을 적용하면 다음과 같이 변환됩니다.
- 빨강 → [1, 0, 0]
- 파랑 → [0, 1, 0]
- 녹색 → [0, 0, 1]
이때 원핫인코딩을 적용하기 위해서는 입력값으로 2차원 데이터가 필요하다는 말은, 각 범주형 값이 하나의 행(row)이 되어야 하고, 각각의 범주는 별도의 열(column)로 표현되어야 한다는 의미입니다. 다시 말해, 범주형 데이터가 들어 있는 열이 여러 개의 열로 확장되어 각 범주를 독립적으로 표현할 수 있는 구조가 필요합니다.
예를 들어, 단일 열에 '색깔'이라는 데이터가 있는 1차원 배열이 있다면, 이를 원핫인코딩을 적용하려면 '빨강', '파랑', '녹색' 각각을 표현하는 3개의 열을 갖는 2차원 형태로 변환해야 합니다.
get_dummies() :
- 원-핫인코딩을 쉽게 지원하는 API. 문자열 카테고리 값을 숫자형으로 변환할 필요 없이 바로 변환 가능
★ 이전 수업 들었던 당시 get_dummies()를 사용 하면 0, 1로 값을 반환을 했었으나, 지금은 변경된 것 같다.
지금은 그냥 사용하면 True, False 값이 나온다.
* 자세한건 여기 문서를 참조하세요: https://pandas.pydata.org/docs/reference/api/pandas.get_dummies.html
-기본적으로 ( 또는 대신) dtypes를 pd.get_dummies()반환하도록 업데이트
-.bool, float, int를 설정하여 진행 ex) pd.get_dummies(df, dtype=int).
피처 스케일링과 정규화
피처 스케일링(Feature Scaling) :
서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업을 말한다.
[표준화와 정규화가 대표적]
표준화(Standardization) : 데이터 피처 각각이 평균이 0이고 분산이 1인 가우시안 정규 분포를 가진 값으로 변환하는 것
표준화를 통해 변환될 피처 x의 새로운 i 번째 데이터는 (원래 값에서 피처 x의 평균을 뺀 값)을 (피처 x의 표준편차)로 나눈 값
StandardScaler : 새로운 x = (x - 평균)/표준편차
정규화(Normalization) : 서로 다른 피처의 크기를 통일하기 위해 크기를 변환해주는 개념
예로드어 A피처는 값이 0~100Km 거리를 나타내고 B피처는 0~100,000,000,00,000으로 금액을 나타낸다고 했을때 이 변수 들을 동일한 크기 단위로 비교하기 위해 값을 모두 0~1 값으로 변환
-> 개별 데이터의 크기를 모두 똑같은 단위로 변경
표준화(StandardScaler)
- StandardScaler은 표준화를 쉽게 지원해 주는 함수입니다. 즉 개별 피처들을 평균이 0이고 분산이 1인 값으로 변환을 시켜줍니다. 이렇게 가우시안 정규 분포를 가질 수 있도록 데이터를 변환하는 것은 몇몇 알고리즘에서 매우 중요합니다.
서포트 벡터 머신(Support Vector Machine), 선형 회귀(Linear Regression), 로지스틱 회귀(Logistic Regression)과 같은 가우시안 분포를 가지고 있다고 가정을 하고 구현된 ML 알고리즘들은 표준화를 적용하는 것이 예측 성능 향상에 중요한 요소로 작용될 수 있습니다.
▶ 모든 캄럼 값의 평균이 0에 아주 가까운 값으로, 그리고 분산은 1에 아주 가까운 값으로 변환됐음을 알 수 있다.
MinMaxScaler
- MinMaxScaler은 데이터의 값들을 0과 1사이의 범위 값으로 변환하여 줍니다.
- 데이터값을 0과 1사이의 범위값으로 변환. 음수값이 있으면 -1에서 1값으로 변환
- 데이터 분포가 가우시간 분포가 아닐 경우 Min, Max Scale을 적용해볼 수 있음
▶ 모든 피처에 0에서 1 사이의 값으로 변환되는 스케일링이 적용됐음을 알 수 있다.
학습 데이터와 테스트 데이터의 스케일링 변환 시 유의점
Scaler 객체 이용해 데이터 스케일링 변환 시 fit(), transform(), fit_transform() 메소드 이용합니다.
- fit() : 데이터 변환을 위한 기준 정보 (예; 데이터셋의 최대/최소값 설정 등) 설정 적용
- transform() : 설정된 정보를 이용해 데이터 변환
- fit_transform() : 두 메소드를 한 번에 적용
그런데, 학습데이터 세트와 테스트 데이터 세트에 이 fit()과 transform()을 적용할 때 주의가 필요합니다.
> '학습 데이터'로 fit()이 적용된 스케일링 기준정보를 그대로 테스트 데이터에 적용해야 함
> 테스트 데이터로 다시 새로운 스케일링 기준 정보를 만들게 되면 학습 데이터와 테스트 데이터의 스케일링 기준 정보가 서로 달라지기 때문에 올바른 예측 결과를 도출하지 못할 수 있다.
MinMaxScaler 객체의 fit()과 transform()적용하여 1->0.1, 2->0.2로 그리고 5는 0.5, 10은 1로 변환 확인
테스트 데이터의 fit()을 호출해 스케일링 기준 정보를 다시 적용한 뒤 transform() 수행한 결과 확인하기
결과를 보면 학습 데이터와 테스트의 스케일링이 맞지 않은것을 알 수 있습니다. 테스트의 경우 최솟값 0, 최댓값 5가 되어 1->0.2, 2->0.4로 변환이 되었습니다. ML 모델은 학습 데이터를 기반으로 학습이 되기 때문에 반드시 테스트 데이터는 학습 데이터의 스케일링 기준을 따라야 합니다. 따라서 테스트 데이터에는 다시 fit()을 적용해서는 안됩니다.
다음은 테스트 데이터에 fit()을 적용하지 않고 학습 데이터의 fit()을 적용한 것을 활용해 보겠습니다.
fit_transform()을 적용할 떄도 마찬가지입니다.
학습데이터에서는 상관없지만 테스트 데이터에서는 절대 사용해서는 안된다.
[학습, 테스트 데이터의 fit(), transform(), fit_transform() 메소드 이용해 스케팅일 변환시 유의사항 정리]
> 가능하다면 전체 데이터의 스케일링 변환을 적용한 뒤 학습과 테스트 데이터로 분리하는 것이 좋다.
> 그것이 여의치 않다면 테스트 데이터 변환 시에는 fit()이나 fit_transform()을 적용하지 않고 학습 데이터로 이미 fit()된 Scaler 객체를 이용해 transform으로 변환
이 주의사항은 앞으로 배울 사이킷런 기반의 PCA 등의 차원 축소 변환, 텍스트의 피처 벡터화 변환에도 동일 적용됩니다.
사이킷런으로 수행하는 타이타닉 생존자 예측
데이터 불러오기
A. 머신러닝 알고리즘을 적용해서 예측을 수행하기 전에 데이터를 먼저 탐색해보겠습니다.( EDA)
1. 데이터 전처리
- 결측값/ 중복값 확인
- 결측값 처리
> Age 나이 : 평균으로 결측값 체우기
> Cabin , Embarked 는 타입이 object / 'N'으로 채우기
성별에 따른 생존 확률
탑승객 중 남성은 : 577명 / 여성은 314명
- 여성 314명 중 233명 약 74.2% 생존
- 남성 577명중 109명 약 18.8%가 생존
전체 생존자 중 남여 비율 체크
- 선실 일등실, 이등실, 삼등실에 따라 생존 비율 체크(부자와 가난한 사람의 생존 비율)
Age에 따른 생존 비율
바다에서 사고가 날 경우 여성과 아이들, 그리고 노약자가 제일 먼저 구조대상입니다. 그리고 아마도 부자나 유명인이 다음 구조 대상이었을 것이다. 안타깝게도 삼등실에 탄 많은 가난한 이는 타이타닉호와 함께 사망했을 것이다.
학습, 테스트 데이터 분리
ML알고리즘 결정 트리, 랜덤 포레스트, 로지스틱 회귀를 이용하여 생존자 예측
- train_test_split()을 통해 데이터 분리
- 모델 학습, 예측
- accurancy_score() API를 통해 예측 성능 평가 진행 (정확도)
▶ 특별한 파라미터를 조정하지않은 상태에서 가장 높은 정확도는 logisticRegression 모델이 해당 데이터를 가장 잘 표현하는 모델인것을 확인 할 수 있습니다.
▶ 아직 최적화 작업을 수행하지 않았고, 데이터양도 충분하지 않기 떄문에 어떤 알고리즘이 가장 성능이 좋다고 평가를 할 수는 없다.
▶ 교차검증으로 좀 더 평가를 진행
- KFold 클래스
- cross_cal_score()
- GridSearchcv 클래스
[조건]
- K-Fold, dt_clf, 사용해서 score 값 산출, n_splits=5
- cross_val_score, dt_clf, 사용, score 값 산출, cv = 5
- GridSearchCV, dt_clf, best_estimator_ 산출한 scoe 값, cv=5
{'max_depth':[1,2,3,5,10], 'min_samples_split':[2,3,5]}
- K-Fold, dt_clf, 사용해서 score 값 산출, n_splits=5
2. cross_val_score, dt_clf, 사용, score 값 산출, cv = 5
3. GridSearchCV, dt_clf, best_estimator_ 산출한 scoe 값, cv=5
{'max_depth':[1,2,3,5,10], 'min_samples_split':[2,3,5]}
머신러닝은 데이터 가공 및 변환 과정의 전처리 작업, 데이터를 학습 데이터와 테스트 데이터로 분리하는 데이터 세트 분리 작업을 거춘 후에 학습 데이터를 기반으로 테스트 데이터에 대한 예측을 수행하고, 이렇게 예측된 결괏값과 비교해 머신러닝 모델에 대한 평가를 수행하는 방식으로 구성됩니다.
데이터 가공 및 변환/ 전처리 ▶ 데이터 분리 ▶ 학습 ▶ 예측 ▶ 평가
[전체 소스코드]
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
titanic = pd.read_csv('./drive/MyDrive/data/titanic_train.csv')
titanic.head()
titanic.info()
titanic.isnull().sum()
titanic.duplicated().sum()
titanic['Age'].fillna(titanic['Age'].mean(), inplace=True)
titanic['Cabin'].fillna('N', inplace=True)
titanic['Embarked'].fillna('N', inplace=True)
titanic.isnull().sum()
titanic.groupby(['Sex','Survived'])['Survived'].count()
## 데이터 확인
titanic.groupby(['Sex','Survived'])['Survived'].count()
sns.barplot(x='Sex', y = 'Survived',hue='Sex', data= titanic)
import matplotlib.pyplot as plt
# titanic 데이터셋에서 성별에 따른 생존자 수를 계산합니다.
survived_counts = titanic[titanic['Survived'] == 1]['Sex'].value_counts()
# 파이 차트를 그립니다.
plt.figure(figsize=(3, 3))
plt.pie(survived_counts, labels=survived_counts.index, autopct='%1.1f%%', startangle=140)
plt.title('Survivor rates by gender')
plt.show()
sns.barplot(x='Pclass', y='Survived', hue='Sex', data=titanic)
## 입력 age에 따라 구분값을 반환하는 함수 설정. DataFrame의 apply lambda식에 사용.
def get_category(age):
cat = ''
if age <= -1: cat = 'Unknown'
elif age <= 5: cat = 'Baby'
elif age <= 12: cat = 'Child'
elif age <= 18: cat = 'Teenager'
elif age <= 25: cat = 'Student'
elif age <= 35: cat = 'Young Adult'
elif age <= 60: cat = 'Adult'
else : cat = 'Elderly'
return cat
# 막대그래프의 크기 figure를 더 크게 설정
plt.figure(figsize=(10,6))
#X축의 값을 순차적으로 표시하기 위한 설정
group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult', 'Elderly']
# lambda 식에 위에서 생성한 get_category( ) 함수를 반환값으로 지정.
# get_category(X)는 입력값으로 'Age' 컬럼값을 받아서 해당하는 cat 반환
titanic['Age_cat'] = titanic['Age'].apply(lambda x : get_category(x))
sns.barplot(x='Age_cat', y = 'Survived', hue='Sex', data=titanic, order=group_names)
titanic.drop('Age_cat', axis=1, inplace=True)
#=======================================================================================================
# 결측치 제거
def fillna(df):
df['Age'].fillna(df['Age'].mean(), inplace=True)
df['Cabin'].fillna('N', inplace=True)
df['Embarked'].fillna('N', inplace=True)
return df
# 머신러닝 알고리즘에 불필요한 속성 제거
def drop_features(df):
df.drop(['PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)
return df
# 레이블 인코딩 수행.
def format_features(df):
df['Cabin'] = df['Cabin'].str[:1]
features = ['Cabin', 'Sex', 'Embarked']
for feature in features:
le = LabelEncoder()
le.fit(df[feature])
df[feature] = le.transform(df[feature])
return df
# MinMaxscaler 스케일링
def adj_scale(df):
scaler = MinMaxScaler()
scaler.fit(df)
scaled_array = scaler.transform(df)
df_scaled = pd.DataFrame(data=scaled_array, columns = df.columns)
return df_scaled
# 앞에서 설정한 함수 호출
def transform_features(df):
df = fillna(df)
df = drop_features(df)
df = format_features(df)
df = adj_scale(df)
return df
# ================================================================================
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
titanic_df = pd.read_csv('./drive/MyDrive/data/titanic_train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df= titanic_df.drop('Survived',axis=1)
# 함수 호출과 함께 전처리
X_titanic_df = transform_features(X_titanic_df)
X_train, X_test, y_train, y_test=train_test_split(X_titanic_df, y_titanic_df,
test_size=0.2, random_state=11)
X_train.shape, y_train.shape, X_test.shape, y_test.shape
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# 결정트리, Random Forest, 로지스틱 회귀를 위한 사이킷런 Classifier 클래스 생성
dt_clf = DecisionTreeClassifier(random_state=11)
rf_clf = RandomForestClassifier(random_state=11)
lr_clf = LogisticRegression()
# 학습/예측/평가 함수
def fit_predict(estimator, X_train, y_train, X_test, y_test):
estimator.fit(X_train, y_train)
pred = estimator.predict(X_test)
return (accuracy_score(y_test, pred))
# DecisionTreeClassifier 학습/예측/평가
dt_pred = fit_predict(dt_clf, X_train, y_train, X_test, y_test )
print('DecisionTreeClassifier 정확도: {0:.4f}'.format(dt_pred))
# RandomForestClassifier 학습/예측/평가
rf_pred = fit_predict(dt_clf, X_train, y_train, X_test, y_test )
print('RandomForestClassifier 정확도:{0:.4f}'.format(rf_pred))
# LogisticRegression 학습/예측/평가
lr_pred = fit_predict(dt_clf, X_train, y_train, X_test, y_test )
print('LogisticRegression 정확도: {0:.4f}'.format(lr_pred))
#===============================================================
# KFold 사용
from sklearn.model_selection import KFold
titanic_df = pd.read_csv('./drive/MyDrive/data/titanic_train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df= titanic_df.drop('Survived',axis=1)
# 함수 호출과 함께 전처리
X_titanic_df = transform_features(X_titanic_df)
X_train, X_test, y_train, y_test=train_test_split(X_titanic_df, y_titanic_df,
test_size=0.2, random_state=11)
def fit_predict(estimator, X_train, y_train, X_test, y_test):
estimator.fit(X_train, y_train)
pred = estimator.predict(X_test)
return (accuracy_score(y_test, pred))
n_iter=0
kfold = KFold(n_splits=5)
score = []
#DecisionTreeClassifier 객체 생성
dt_clf = DecisionTreeClassifier()
for train_index, test_index in kfold.split(X_train):
X_train = X_titanic_df.iloc[train_index]
X_test = X_titanic_df.iloc[test_index]
y_train = y_titanic_df.iloc[train_index]
y_test = y_titanic_df.iloc[test_index]
s_item = fit_predict(dt_clf, X_train, y_train, X_test, y_test)
score.append(s_item)
n_iter+=1
print("교차 검증 {0} 정확도: {1:.4f}".format(n_iter, s_item))
# 5개 fold에서의 평균 정확도 계산
mean_score = np.mean(score)
print('평균 정확도: {0:.4f}'.format(mean_score))
#예측
pred = dt_clf.predict(X_test)
#정확도 확인
print('final : ', accuracy_score(y_test, pred))
# cross_val_score 사용
from sklearn.model_selection import cross_val_score
scores = cross_val_score(dt_clf, X_titanic_df , y_titanic_df , cv=5)
for iter_count,accuracy in enumerate(scores):
print("교차 검증 {0} 정확도: {1:.4f}".format(iter_count, accuracy))
print("평균 정확도: {0:.4f}".format(np.mean(scores)))
# GridSearchCV
from sklearn.model_selection import GridSearchCV
parameters = {'max_depth':[2,3,5,10],
'min_samples_split':[2,3,5], 'min_samples_leaf':[1,5,8]}
grid_dclf = GridSearchCV(dt_clf , param_grid=parameters , scoring='accuracy' , cv=5)
grid_dclf.fit(X_train , y_train)
print('GridSearchCV 최적 하이퍼 파라미터 :',grid_dclf.best_params_)
print('GridSearchCV 최고 정확도: {0:.4f}'.format(grid_dclf.best_score_))
best_dclf = grid_dclf.best_estimator_
# GridSearchCV의 최적 하이퍼 파라미터로 학습된 Estimator로 예측 및 평가 수행.
dpredictions = best_dclf.predict(X_test)
accuracy = accuracy_score(y_test , dpredictions)
print('테스트 세트에서의 DecisionTreeClassifier 정확도 : {0:.4f}'.format(accuracy))
|
cs |
'데이터 공부 > 머신러닝 공부' 카테고리의 다른 글
파이썬 머신러닝 완벽 가이드 (권철민) - 분류 - ① 결정트리 (0) | 2024.05.02 |
---|---|
파이썬 머신러닝 완벽 가이드(권철민) - Chapter 03 평가 -② (0) | 2024.04.19 |
통계적 머신러닝 - Sklearn / 교차분석/ KFold / cross_val_score (0) | 2024.04.17 |
01. 머신러닝(marchine learning)이란? (1) | 2024.04.15 |