그동안 복습한 내용들을 바탕으로 머신러닝 실습을 진행해볼 것이다.
피파 선수들의 능력치를 바탕으로 Best Position을 예측하는 실습이다.
항상 느끼는 건, 내가 뭘 하고자 하는지를 정확하게 알고 가는 것이 중요하다는 것이다. 가장 중요한 Target을 설정하는 부분은 제대로 머릿속에 담고 가면서 분석을 해야한다. 휘황찬란한 분석 방법때문에 내가 뭘 해야하는지 놓치고 가면, 산으로 가버린다. 그래서 차분히 EDA를 하는 과정이 꼭 필요한 것 같다.
그리고 분석을 해가면서 가장 필요성을 느낀 부분은, 모델학습을 할 때 먼저 Pipeline을 만들어 제대로 적용해야한다는 점이다. 보통 훈련 데이터의 성능을 높이는 방향으로 많이 진행하는데, 결국엔 train과 test데이터에 동일한 전처리를 진행해야 의도한 결과가 나오기 때문에 Pipeline을 설계하는 것이 편리하고 중요하다. 또한, 다양한 하이퍼파라미터튜닝법을 통해 분석을 쪼개서 제대로 하고, 성능을 높이는 것도 정말 필요한 과정이라고 생각이 들었다.
sns.set_style('whitegrid')
를 통해 시각화 결과물에 대한 기본 설정 가능# 기본적으로 필요한 시각화 라이브러리를 불러오기
from matplotlib import font_manager, rc, rcParams
import matplotlib.pyplot as plt
import seaborn as sns
# 시각화 설정
sns.set_style('whitegrid')
# Pandas 및 정규표현식 불러오기
import pandas as pd
import re
path
를 지정import os
path = '/content/drive/MyDrive/...'
filename = 'FIFA22_official_data.csv'
df = pd.read_csv(os.path.join(path, filename) , encoding = 'utf-8'); df
df.info()
를 통해 데이터 타입과 결측치 확인 가능# 포지션 예측을 위한 컬럼 확인
df[['Name','Position','Best Position']]
Best Position
을 예측하기로 결정 -> Target
포지션 확인
print(df['Best Position'].unique(), '포지션 개수', len(df['Best Position'].unique()), '개')
>>>
['CAM' 'CM' 'ST' 'LB' 'CDM' 'CB' 'RB' 'LM' 'RW' 'LW' 'CF' 'LWB' 'RM' 'RWB' 'GK']
포지션 개수 15 개
# 포지션별 카운트
plt.figure(figsize = (6,3), dpi = 150)
sns.countplot(x = df['Best Position'], data = df)
plt.show()
LabelEncoder
사용# 데이터 레이블 인코딩
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder() # 레이블 인코더는 정수 인덱스로 바꿈
# 정답 레이블 인코딩
y = encoder.fit_transform(df['Best Position']);y
# 정답 레이블을 정수 인덱스로 반환함
# 다시 정수를 문자열로 알아보고 싶다면 encoder.inverse_transform(y)를 실행
- 세미콜론(;)은 파이썬에서 여러 문장을 한 줄에 작성할 때 사용할 수 있는 구분자
- 세미콜론을 사용하지 않고 코드를 여러 줄로 나누어 작성해도 동일한 결과를 얻음
df.info()
를 통해 확인 가능27:61
까지 학습데이터로 설정# 컬럼 인덱스 확인하기
for i, v in enumerate(df.columns):
print(i, v)
# 학습 데이터 생성
x = df.iloc[:,27:61]; x
# 결측치 개수 확인
x.isnull().sum()
Marking
은 분석에 방해가 되므로 axis 1
로 제거 inplace = True
중요!!
axis=0
은 행 방향으로 동작하고,axis=1
은 열 방향으로 동작
inplace = True
는 기존 데이터 프레임에 변경된 설정으로 덮어쓰겠다는 의미
# 컬럼 제거
x.drop('Marking', axis = 1, inplace = True)
# 결측 데이터 확인
df[df['Volleys'].isnull()]
# 은최한 선수임을 알 수 있음
axis = 1
열로 보간# 결측 데이터 보간
x.interpolate(axis = 1, inplace = True)
import pandas as pd
data = {'x': [0, 2, 4, 6, 8],
'y': [1, None, 9, None, 25]}
df = pd.DataFrame(data)
df.interpolate()
>>>
x y
0 0 1.0
1 2 5.0
2 4 9.0
3 6 17.0
4 8 25.0
train_test_split
를 활용x
변수와y
변수를 따로 지정하여 총 4개의 변수를 구함x_train, x_test, y_train, y_test
# 학습-훈련 데이터셋 분리
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.3, random_state = 319)
- 데이터 전처리 결과를 데이터 자체에 반영할 수도 있지만, 모델 파이프라인을 만들어 적용할 수 있음
- 보통 훈련 데이터의 성능을 높이는 방향으로 전처리하지만, 동일한 전처리 방법을 테스트 데이터에도 적용해야 의도한 결과가 나올 수 있다.
- 이를 위해 전처리부터 학습까지 하나의 모델로 적용할 수 있는 파이프라인을 설계하는 것이 편리
⇒ Scaling으로 더 많은 함수들을 적용할 수도 있지만, 여기서는 빠른 훈련을 위해 데이터를 표준화하는 함수만 반영함
# 파이프라인 설정_여기서는 StandardScaler() 사용
pipeline = Pipeline([('scaler', StandardScaler()),
('lr', LogisticRegression(solver = 'lbfgs', max_iter = 1000))
])
- 'scaler'라는 이름을 가지고, StandardScaler()를 사용
- 'lr'이라는 이름을 가지고, LogisticRegression 모델을 설정
'lbfgs' 최적화 알고리즘과 최대 반복 횟수 1000으로 설정
Logistic Regression은 각 특성별로 가중치를 결정하고 각각의 가중치와 특성을 가중합하여 결정을 내립니다. 아마도 Logistic Regression의 이런 특징 때문에 성능이 높게 나오지 않았을까 싶습니다.
# Logistic Regression
from sklearn.linear_model import LogisticRegression
pipeline.fit(x_train, y_train)
y_pred = pipeline.predict(x_test)
acc = accuracy_score(y_test, y_pred)
print('Logistic Regression 모델의 정확도:', '%.4f'%acc)
>>> Logistic Regression 모델의 정확도: 0.7578
pipeline['lr'].coef_
: 로지스틱 회귀 모델의 계수
x_train.columns
: 입력 특성들의 이름
encoder.inverse_transform(range(15))
는 레이블 역변환을 통해 '0'에서 '14'까지의 클래스 레이블을 다시 원래의 범주형 레이블로 변환한 것
⇒ 예측값이 15개임
# Logistic Regression의 계수
# 'lr'이라는 이름으로 파이프라인에서 로지스틱 회귀 모델의 계수를 추출하고, 데이터프레임으로 변환
matrix = pd.DataFrame(pipeline['lr'].coef_, columns = x_train.columns,
index = encoder.inverse_transform(range(15)))
plt.figure(figsize = (13,3), dpi = 150)
# 'matrix'는 계수 데이터프레임, 'cmap'는 사용할 색상 맵
sns.heatmap(matrix, cmap = 'vlag')
plt.show()
GridSearch
는 일일이 수작업으로 해야 하는 것을 변수만 넣으면 쉽게 작동하도록 만든 모델임# 하이퍼파라미터 설정
solvers = ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']
c_values = [100, 10, 1.0, 0.1, 0.01]
# GridSearch 적용
grid = dict(lr__solver = solvers, lr__C = c_values)
- solvers 리스트: 로지스틱 회귀 모델의 최적화 알고리즘인 solver에 대한 여러 옵션을 포함
- c_values 리스트: 로지스틱 회귀 모델의 규제 강도를 나타내는 하이퍼파라미터인 C에 대한 여러 가능한 값들을 포함
- grid 딕셔너리: 그리드 탐색을 설정하기 위한 파라미터 그리드를 정의
➣ 가능한 모든 하이퍼파라미터 조합을 시도하여 최적의 조합을 찾음
➣ 딕셔너리의 키는 파이프라인에서 사용한 모델의 이름과 하이퍼파라미터 이름을 조합한 것임
➣lr__solver
는 로지스틱의 최적화 알고리즘(solver)을 나타내며,lr__C
는 규제 강도(C)를 나타냄
➣ 각 키에 대한 값은 해당 하이퍼파라미터에 대한 여러 가능한 값들을 리스트로 지정함.
train_test_split
하고, 이를 반복해서 모델의 성능을 검증⇢ 데이터를 K개의 Fold로 분할하고, 각 Fold 를 한번씩 테스트셋으로 이용하면서 나머지 (K-1)개의 폴드를 훈련 데이터로 사용
⇢ 이 과정을 K번 반복하여 K개의 성능 추정치를 얻게 됨
⇢ K개의 성능 추정치의 평균을 최종 성능 추정치로 사용
⇢ 각 클래스의 비율이 훈련-테스트 데이터셋에서 동일하게 유지되도록 폴드를 구성하는 방식
⇢ 불균형한 분포를 가진 데이터셋에 특히 유용함
# CrossValidation 설정
cv = StratifiedKFold(n_splits=3, random_state = 319, shuffle = True)
# GridSearchCV 학습
grid_search = GridSearchCV(estimator=pipeline, param_grid=grid, n_jobs=-1, cv=cv, scoring='accuracy', error_score=0)
grid_result = grid_search.fit(x_train, y_train)
K-Fold Cross Validation으로 구한 결과 중에서 가장 높은 값을 보인 파라미터가 C = 1.0 solver = ‘lbfgs’
입니다.
# 결과 요약
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
>>> Best: 0.799094 using {'lr__C': 1.0, 'lr__solver': 'lbfgs'}
[출처 | 딥다이브 Code.zip 매거진]