1.데이터 전처리
ML 알고리즘 만큼 중요하다.
어떠한 데이터에 기반하냐에 따라 결과도 달라진다 .
* ML 알고리즘을 적용하기 전 데이터에 대해 미리 처리해야할 사항
1) 결손값, Null 값은 허용되지 않는다. 따라서 Null 값을 다른 값으로 변환해야 한다.
-> 평균값으로 대체하거나 더 정밀한 대체값을 선정해햐 한다.
2) 모든 문자열 값은 이코딩 되서 숫자형으로 변환해야 한다.
1. 데이터 인코딩
- 방법: 레이블 인코딩(Label encoding) // 원-핫 인코딩(One Hot encoding)
1. 레이블 인코딩
: 카테고리 피처를 코드형 숫자 값으로 변환하는 것
ex) 데이터의 상품 구분이 냉장고, TV, 전자레인지 등으로 돼 있다면,
냉장고:1, TV:2, 전자레인지 :3 이런식으로 숫자형 값으로 변환해준다.
from sklearn.preprocessing import LabelEncoder
items = ['TV','냉장고','전자레인지','컴퓨터','선풍기','믹서','믹서']
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
print('인코딩 변환 값 :',labels)
인코딩 변환 값 : [0 1 4 5 3 2 2]
이런 방법으로 사용이 가능하며, 각 카테고리별로 인코딩 된 것을 확인할 수 있다.
또한, invers_transform()을 통해 인코딩 된 값을 다시 디코딩 할 수도 있다.
#classes_ : 0부터 순서대로 변환된 인코딩에 대한 원본값을 가짐
print('원본 레이블: ', encoder.classes_, '\n')
# inverse_transform: 인코딩된 레이블을 다시 디코딩
print('디코딩 원본값:',encoder.inverse_transform([4,3,1,1,0,2,4]))
원본 레이블: ['TV' '냉장고' '믹서' '선풍기' '전자레인지' '컴퓨터']
디코딩 원본값: ['전자레인지' '선풍기' '냉장고' '냉장고' 'TV' '믹서' '전자레인지']
레이블링은 이렇게 간단하게 문자열 값을 숫자형 카테고리 값으로 변환한다.
하지만 몇몇 ML 알고리즘에 이렇게 적용할 경우 예측 성능이 떨어질 수 있다.
-> 이유는, 숫자 값으로 변환이 되며 크고 작은 숫자의 특성이 작용하기 때문이다.
따라서 레이블 인코딩은 선형회귀와 같은 ML 알고리즘에는 적용하지 않아야 한다.
트리계열의 알고리즘은 이러한 특성을 반영하지 않으므로 레이블 인코딩에도 별 문제는 없다.
2. 원 핫 인코딩(One - Hot Encoding)
피처 값의 유형에 따라 새로운 피처를 추가하여 고유 값에 해당하는 칼럼에만 1을 표시하고,
나머지 칼럼에는 0을 표시하는 형식
해당 고유 값에 매칭되는 피처만 1이 되고 , 나머지 피처는 0을 입력하며 이러한 특성으로 원 핫 인코딩(One - Hot Encoding)으로 불린다.
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import LabelEncoder
import numpy as np
items = ['TV','냉장고','전자레인지','컴퓨터','선풍기','선풍기','믹서','믹서']
# LabelEncoder 작업진행
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
#2차원 데이터로 변환
labels = labels.reshape(-1,1)
#원핫 인코딩 적용
oh_encoder = OneHotEncoder()
oh_encoder.fit(labels)
oh_labels=oh_encoder.transform(labels)
print('원 핫 인코딩데이터')
print(oh_labels.toarray())
print('원 핫 인코딩 차원')
print(oh_labels.shape)
원 핫 인코딩데이터
[[1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 1.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0.]
[0. 0. 1. 0. 0. 0.]]
원 핫 인코딩 차원
(8, 6)
#OneHotEncoding 을 더 쉽게하는 get_dummies()
import pandas as pd
df = pd.DataFrame({'item':['TV','냉장고','전자레인지','컴퓨터','선풍기','선풍기','믹서','믹서']})
pd.get_dummies(df)
3. 피처 스케일링과 정규화
서로 다른 변수의 값 범위를 일정한 수준으로맞추는 작업을 피처 스케일링(feature scaling)이라고 한다.
대표적인 방법으로는 표준화, 정규화가 있다.
- 표준화: 데이터의 피처의 평균이 0이고, 분산이 1인 경우 가우시안 정규분포를 가진 값으로 변환하는 의미
ex) 새로운 데이터 x_new의 값에서 피처 x의 평균을 빼고 x의 표준편차로 나눈값
- 정규화: 데이터의 값 범위가 1~ 1000000 정도 된다면 변수를 모두 동일한 크기 단위로 비교하기 위해 값을
최소 0 /최대 1의 값으로 변화
ex) 새로운 데이터 x_new 의 값에서 피처 x 의 최솟값을 뺀 값을 피처 x의 최댓값과 최솟값의 차이로 나눈 값으로 변환
1) 표준화(StandardScaler)
- Sklearn에서 구현한 RBF 커널을 이용하는 SVM, 선형회귀, 로지스틱 회귀는 데이터가 가우시안 분포를 가지고 있다고 가정하고 구현됐기 때문에 표준화 작업을 하는 것이 성능 향상에 도움
from sklearn.datasets import load_iris
import pandas as pd
iris =load_iris()
iris_data=iris.data
df=pd.DataFrame(data=iris_data, columns=iris.feature_names)
print('features들의 평균값:', df.mean())
print('\n features들의 분산값:', df.var())
features들의 평균값: sepal length (cm) 5.843333
sepal width (cm) 3.057333
petal length (cm) 3.758000
petal width (cm) 1.199333
dtype: float64
features들의 분산값: sepal length (cm) 0.685694
sepal width (cm) 0.189979
petal length (cm) 3.116278
petal width (cm) 0.581006
dtype: float64
from sklearn.preprocessing import StandardScaler
#StandardSCcaler 작업시 스케일 변환 된 numpy ndarray 형식으로 반환되, DataFrame로 변환
scaler= StandardScaler()
scaler.fit(df)
iris_scaled=scaler.transform(df)
iris_df_scaled=pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
print('feature들의 평균 값',iris_df_scaled.mean())
print('\n feature들의 분산 값',iris_scaled.mean())
feature들의 평균 값 sepal length (cm) -1.690315e-15
sepal width (cm) -1.842970e-15
petal length (cm) -1.698641e-15
petal width (cm) -1.409243e-15
dtype: float64
feature들의 분산 값 -1.4684549872375404e-15
2) MinMaxScaler
- 데이터 값을 0과 1사이의 범위 값으로 변환(음수 값이 있으며 -1에서 1 값으로 변환)
- 데이터의 분포가 가우시안 분포가 아닐 경우에 적용
from sklearn.preprocessing import MinMaxScaler
min=MinMaxScaler()
min.fit(df)
iris_scaled = min.transform(df)
iris_df_scaled=pd.DataFrame(data=iris_scaled,columns=iris.feature_names)
print('feature 들의 최소값', iris_df_scaled.min())
print('\n feature들의 최대값',iris_df_scaled.max())
feature 들의 최소값 sepal length (cm) 0.0
sepal width (cm) 0.0
petal length (cm) 0.0
petal width (cm) 0.0
dtype: float64
feature들의 최대값 sepal length (cm) 1.0
sepal width (cm) 1.0
petal length (cm) 1.0
petal width (cm) 1.0
dtype: float64
4. 학습 데이터와 테스트 데이터의 스케일링 변환 시 유의점
- 학습 데이터로만 fit을 수행해야하며, 테스트 데이터에는 학습데이터에서 fit()을 수행한 결과를 transform()해야 함
- 만약 학습, 테스트 값 둘다 fit()을 진행하게 된다면 서로 다른 원본 값이 동일한 값으로 변할 수도 있음.
- 머신러닝 모델은 학습 데이터를 기반으로 학습이 되기 때문에 반드시 test 데이터는 학습 데이터의 기준에 적용되어야 함.
* 가능하다면 전체 데이터의 스케일링을 적용한 뒤에 학습과 테스트 데이터 분리
from sklearn.preprocessing import MinMaxScaler
train_array = np.arange(0, 11).reshape(-1 ,1)
test_array = np.arange(0, 6).reshape(-1 ,1)
min_scaler = MinMaxScaler()
min_scaler.fit(train_array)
train_scaled = min_scaler.transform(train_array)
print('원본 train_array 데이터: ', np.round(train_array.reshape(-1), 2))
print('Scaled된 train_array 데이터: ', np.round(train_scaled.reshape(-1), 2))
test_scaled = min_scaler.transform(test_array)
print('원본 test_array 데이터: ', np.round(test_array.reshape(-1), 2))
print('Scaled된 test_array 데이터: ', np.round(test_scaled.reshape(-1), 2))
원본 train_array 데이터: [ 0 1 2 3 4 5 6 7 8 9 10]
Scaled된 train_array 데이터: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
원본 test_array 데이터: [0 1 2 3 4 5]
Scaled된 test_array 데이터: [0. 0.1 0.2 0.3 0.4 0.5]
5. 사이킷런으로 수행하는 타이타닉 생존자
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# 타이타닉 데이터는 kaggle에서 다운
df = pd.read_csv('각자 파일 경로/train.csv')
df
1) NULL 값 채우기
df['Age'].fillna(df['Age'].mean(), inplace = True)
df['Cabin'].fillna('N', inplace = True)
df['Embarked'].fillna('N', inplace = True)
df.isnull().sum()
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 0
Embarked 0
dtype: int64
2) 값 분포 확인
print('Age 컬럼의 값 분포 \n', df['Age'].value_counts())
print('Cabin 컬럼의 값 분포 \n', df['Cabin'].value_counts())
print('Embarked 컬럼의 값 분포 \n', df['Embarked'].value_counts())
Age 컬럼의 값 분포
29.699118 177
24.000000 30
22.000000 27
18.000000 26
28.000000 25
...
36.500000 1
55.500000 1
0.920000 1
23.500000 1
74.000000 1
Name: Age, Length: 89, dtype: int64
Cabin 컬럼의 값 분포
N 687
C23 C25 C27 4
G6 4
B96 B98 4
C22 C26 3
...
E34 1
C7 1
C54 1
E36 1
C148 1
Name: Cabin, Length: 148, dtype: int64
Embarked 컬럼의 값 분포
S 644
C 168
Q 77
N 2
Name: Embarked, dtype: int64
- Cabin 속성에서 중복이 발생하기 때문에 앞글자만 추출
df['Cabin'] = df['Cabin'].str[:1]
print(df['Cabin'].head())
0 N
1 C
2 N
3 C
4 N
Name: Cabin, dtype: object
3) 중간확인 -어떤 유형의 승객 생존 확률이 높은지
df.groupby(['Sex', 'Survived'])['Survived'].count()
Sex Survived
female 0 81
1 233
male 0 468
1 109
Name: Survived, dtype: int64
sns.barplot(x = 'Sex', y = 'Survived', data = df)
sns.barplot(x = 'Pclass', y = 'Survived', hue = 'Sex',data = df)
# 입력 age에 따른 구분 값을 반환하는 함수 설정. DataFrame의 apply lambda 식에 사용
def get_category(age):
cat = ''
if age <= -1: cat = 'Unkown'
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
# 막대 그래프의 크기 설정
plt.figure(figsize = (10, 10))
# X축의 값을 순차적으로 표현하기 위한 설정
group_names = ['Unkown','Baby','Child','Teenager','Student','Young Adult','Adult','Elderly']
# lambda 식에 위헤서 생성한 get_category() 함수를 반환값으로 지정
# get_category(x)는 입력값으로 'Age' 칼럼 값을 받아서 해당하는 cat 반환
df['Age_cat'] = df['Age'].apply(lambda x : get_category(x))
sns.barplot(x = 'Age_cat', y = 'Survived', hue = 'Sex', data = df, order = group_names)
df.drop('Age_cat', axis = 1, inplace = True)
# 문자열 LabelEncoder화
from sklearn.preprocessing import LabelEncoder
label = LabelEncoder()
def encode_features(dataDF):
features = ['Cabin', 'Sex','Embarked']
for i in features:
le= label.fit(dataDF[i])
dataDF[i] = le.transform(dataDF[i])
return dataDF
df = encode_features(df)
df.head()
print(df['Cabin'].value_counts())
7 687
2 59
1 47
3 33
4 32
0 15
5 13
6 4
8 1
Name: Cabin, dtype: int64
4) EDA 함수 총 정리
def fillna(df):
df['Age'].fillna(df['Age'].mean(), inplace = True)
df['Cabin'].fillna('N', inplace = True)
df['Embarked'].fillna('N', inplace = True)
df['Fare'].fillna(0, inplace = True)
return df
def drop_features(df):
df.drop(['PassengerId','Name','Ticket'], axis = 1, inplace = True)
return df
def format_features(df):
features = ['Cabin', 'Sex','Embarked']
for i in features:
le= label.fit(df[i])
df[i] = le.transform(df[i])
return df
def transform_features(df):
df = fillna(df)
df = drop_features(df)
df = format_features(df)
return df
5) 데이터 재로드 후 전처리 함수 적용
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
df = pd.read_csv('개인 파일 경로/train.csv')
df = transform_features(df)
X = df.drop(columns = ['Survived'])
y = df[['Survived']]
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size = 0.2, random_state = 11)
dt_clf = DecisionTreeClassifier(random_state = 11)
rf_clf = RandomForestClassifier(random_state = 11)
lr_clf = LogisticRegression()
# DecisionTreeClassifier의 학습 / 예측 / 평가
dt_clf.fit(X_train, y_train)
predict_1 = dt_clf.predict(X_val)
print('DecisionTreeClassifier 정확도 : ' , accuracy_score(y_val, predict_1))
# RandomForestClassifier 학습 / 예측 / 평가
rf_clf.fit(X_train, y_train)
predict_2 = rf_clf.predict(X_val)
print('RandomForestClassifier 정확도 : ' , accuracy_score(y_val, predict_2))
# LogisticRegression 학습 / 예측 / 평가
lr_clf.fit(X_train, y_train)
predict_3 = lr_clf.predict(X_val)
print('LogisticRegression 정확도 : ' , accuracy_score(y_val, predict_3))
DecisionTreeClassifier 정확도 : 0.7988826815642458
RandomForestClassifier 정확도 : 0.8435754189944135
LogisticRegression 정확도 : 0.8659217877094972
5) 데이터 재로드 후 전처리 함수 적용
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
df = pd.read_csv('개인 파일 경로/train.csv')
df = transform_features(df)
X = df.drop(columns = ['Survived'])
y = df[['Survived']]
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size = 0.2, random_state = 11)
dt_clf = DecisionTreeClassifier(random_state = 11)
rf_clf = RandomForestClassifier(random_state = 11)
lr_clf = LogisticRegression()
# DecisionTreeClassifier의 학습 / 예측 / 평가
dt_clf.fit(X_train, y_train)
predict_1 = dt_clf.predict(X_val)
print('DecisionTreeClassifier 정확도 : ' , accuracy_score(y_val, predict_1))
# RandomForestClassifier 학습 / 예측 / 평가
rf_clf.fit(X_train, y_train)
predict_2 = rf_clf.predict(X_val)
print('RandomForestClassifier 정확도 : ' , accuracy_score(y_val, predict_2))
# LogisticRegression 학습 / 예측 / 평가
lr_clf.fit(X_train, y_train)
predict_3 = lr_clf.predict(X_val)
print('LogisticRegression 정확도 : ' , accuracy_score(y_val, predict_3))
DecisionTreeClassifier 정확도 : 0.7988826815642458
RandomForestClassifier 정확도 : 0.8435754189944135
LogisticRegression 정확도 : 0.8659217877094972
6) 교차검증 - KFold, cross_var_score(), GridSearchCV / 폴드 수 = 5
1.KFold
from sklearn.model_selection import KFold
def exec_kfold(clf, folds = 5):
kfold = KFold(n_splits = folds)
scores = []
# KFold 교차 검증 수행
for iter_count, (train_index, test_index) in enumerate(kfold.split(X)):
# X 데이터에서 교차 검증별로 학습과 검증 데이터를 가리키는 index 생성
X_train, X_val = X.values[train_index], X.values[test_index]
y_train, y_val = y.values[train_index], y.values[test_index]
clf.fit(X_train, y_train)
predictions = clf.predict(X_val)
accuracy = accuracy_score(y_val, predictions)
scores.append(accuracy)
print('교차 검증 {0} 정확도: {1:.4f}'.format(iter_count, accuracy))
mean_score = np.mean(scores)
print('평균 정확도: {0:.4f}'.format(mean_score))
exec_kfold(dt_clf, folds = 5)
교차 검증 0 정확도: 0.7486
교차 검증 1 정확도: 0.7640
교차 검증 2 정확도: 0.8202
교차 검증 3 정확도: 0.7809
교차 검증 4 정확도: 0.7921
평균 정확도: 0.7812
2. cross_val_score()
- KFold와 성능 차이가 있는 이유는 StratifiedKFold를 사용해 폴드 세트를 구분하기 때문이다.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(dt_clf, X, y, cv = 5)
for iter_count, accuracy in enumerate(scores):
print("교차 검증 {0} 정확고 : {1:.4f}".format(iter_count, accuracy))
print("평균 정확도 : {0:.4f}".format(np.mean(scores)))
교차 검증 0 정확고 : 0.7486
교차 검증 1 정확고 : 0.7753
교차 검증 2 정확고 : 0.8090
교차 검증 3 정확고 : 0.7584
교차 검증 4 정확고 : 0.8034
평균 정확도 : 0.7789
3. 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_clf = GridSearchCV(dt_clf, param_grid = parameters, scoring = 'accuracy', cv = 5)
grid_clf.fit(X_train, y_train)
print("최적의 파라미터 : ", grid_clf.best_params_)
print("최고의 정확도 : {0:.4f}".format(grid_clf.best_score_))
best_clf = grid_clf.best_estimator_
# GridSearchCV의 최적 하이퍼 파라미터로 학습된 Estimator로 예측 및 수행 평가 수행
dpredictions = best_clf.predict(X_val)
accuracy = accuracy_score(y_val, dpredictions)
print('테스트 세트에서의 정확도 : {0:.4f}'.format(accuracy))
최적의 파라미터 : {'max_depth': 5, 'min_samples_leaf': 1, 'min_samples_split': 5}
최고의 정확도 : 0.7993
테스트 세트에서의 정확도 : 0.8659
'Data > 머신러닝' 카테고리의 다른 글
[파이썬머신러닝완벽가이드]03.평가(2) (0) | 2023.05.13 |
---|---|
[파이썬머신러닝완벽가이드]03.평가(1) (0) | 2023.05.06 |
[파이썬머신러닝완벽가이드]02.사이킷런으로 시작하는 머신러닝(1) (0) | 2023.04.20 |
[파이썬머신러닝완벽가이드]01.파이썬 기반의 머신러닝과 생태계 이해: 판다스 (1) | 2023.04.15 |
[파이썬머신러닝완벽가이드]01.파이썬 기반의 머신러닝과 생태계 이해: 넘파이 (0) | 2023.04.13 |