Train Data / Valid Data / Test Data

 

우리가 지금 청소기를 개발하고 있다고 가정하자. 나는 적은 에너지로 흡입력을 높이는 방법을 연구했고 개발했다. 그것을 실제로 청소기에 적용하고자 하여 팀을 꾸렸고 우리 팀은 시제품으로 나갈 청소기를 만들었다. 청소기가 완성되었으니 우리의 일은 끝난걸까? 그렇다면 정말 좋겠지만 우리는 이것이 우리가 원하는대로 제대로 작동하는지 테스트를 해봐야한다. 만약에 우리가 원하는대로 작동하지 않는다면, 원인과 이유를 찾아 내 우리가 원하는대로 작동하도록 만들어야한다.

 

수집한 데이터를 Train용과 Test용으로 나누었다.

 

인공지능도 마찬가지이다. 성능이 원하는만큼 나오는지에 대해 테스트를 해야만한다. 특히 인공지능은 여러 개의 데이터의 유사성과 관계성을 파악하여 알고리즘을 만들기 때문에 테스트가 반드시 필요하다. 학습시킨 데이터로는 정확도가 90% 이상이 나오지만 실제로 처음보는 테스트 데이터에서는 정확도가 60%가 나올 수도 있기 때문이다. (지금 내가 하고 있는 프로젝트가 그렇다 개빡쳐)

 

 


 

 

LG 에어컨의 유명한 일화로, 사람의 음성을 인식하는 AI 에어컨을 만들어 최종 컨펌을 받으려고 했으나 표준어로 학습시킨 AI에 '니 뭐꼬' 를 투척시켜 다양한 사투리를 재학습시킨 일화처럼 (개발자의 눈물이 여기까지 느껴지는 일화) 제품이 실제로 상용화되기전에는 테스트 단계가 반드시 필요하다. 이 LG의 일화같은 경우는 밑처럼 이해할 수 있다.

 

수집한 데이터를 Train / Valid / Test 로 나누었다.

 

개발자들이 표준어(Training Set)로 열심히 학습 시켰고, '너의 이름이 뭐니', '넌 누구니', '당신의 이름은 무엇입니까'와 같은 표준어의 데이터들(Validation Set)로 모델의 성능을 파악했지만 부회장의 '니 뭐꼬'(Test Set) 앞에 무너져 다시 처음부터 Train Data를 재수집하여 새롭게 학습시키는 결과가 나타났다. 이게 올바른 예시라고 볼 수는 없겠으나 어떻게든 Train Data / Valid Data / Test Data 에 대해 이해시키려는 노력으로 받아주길 바란다.

 

예로 든 LG 에어컨의 상황같은 경우를 찬찬히 살펴보자면 개발자들은 Train Data와 Valid Data를 사용해 표준어를 잘 이해하는 인공지능을 만들었다. (그러니까 무려 부회장의 컨펌을 받으러 갔겠지) 아마 부회장이 서울분이셨다면 이 인공지능은 테스트마저 성공적으로 마무리 되었을 가능성이 높다. 이러한 생각은 Valid Data를 사용하는 이유와 밀접한 관련이 있다. Valid Data는 학습을 통해 만들어진 모델이 잘 예측하는지를 평가하기 위해서 사용된다. 이해를 쉽게 하기 위해 밑에 이미지를 참고하자.

 

loss, acc, val_loss, val_acc를 볼 수 있다.

 

우리는 Train Data를 통해 학습을 시켜 알고리즘을 만들기 때문에 당연히 Train Data로는 성능이 좋을 수 밖에 없다. 하지만 처음 보는 데이터에서 우리의 모델은 어떻게 작동될까? 그것을 알기 위해 만든 데이터가 Valid Data이다. 위의 경우는 Train Data의 성능이 좋은 것은 물론이고 Valid Data의 정확도도 평균적으로 95%인 꽤 괜찮은 모델이라고 볼 수 있겠다. 따라서 우리는 Valid Data를 통해 Test Data에서도 우리가 원하는 결과를 얻을 수 있음을 기대할 수 있다. (하지만 '니 뭐꼬'처럼 Test Data에서 예상치 못한 상황이 발생하여 정확도가 떨어지는 경우도 있다. 이런 경우는 눈물을 머금으며 새롭게 시작해야한다.)

 

 

loss, acc는 높지만 val_loss, val_acc가 낮은 경우

 

하지만 이런 경우는 어떤가? 학습시킨 Train Data에서의 정확도는 높지만 처음 보는 데이터인 Valid Data에서의 정확도는 현저히 떨어진다. 이런 경우는 Test Data로 검증을 하기도 전에 모델이 잘못되었음을 알 수 있다. 또한 이런 경우는 Train Data에 대해 Overfitting이 일어났을 가능성을 말해준다. 이렇게 Valid Data를 이용하면 Test Data로 가기 전에 모델을 검증하고 잘못된 부분을 유추하여 모델을 고칠 수 있다.

 

+) Overfitting이란 학습에 사용한 데이터에 지나치게 적합한 것을 의미한다. 우리의 인공지능은 학습된 데이터를 기반으로 새롭게 본 데이터에 대해서도 유추하여 예측해야하는데 학습된 데이터에 지나치게 적합되어 있으면 새로운 데이터에서 효과적인 성능을 나타낼 수 없다. 이런 경우는 정규화를 거치거나 Epoch를 줄여 Overfitting을 줄인다.

 

 


 

 

그런데 왜 Valid Data를 사용하는걸까?

 

지금까지 설명한 내용을 보면 이것들은 Test Data를 사용해서도 충분히 할 수 있는 일이라는 생각이 든다. 필자도 처음에는 그랬다. 하지만 Test Data는 말 그대로 Test이다. 이게 무슨 말이냐면, 시중에 나가기 직전에 하는 간단한 테스트정도라고 이해하면 되겠다. 시중에 나가기 직전이라함은, 모든 오류들을 잡은 모델을 말 그대로 간단히 테스트한다고 보면 되겠다. 따라서 이 Test Data는 모델의 성능에 영향을 미치지 않고 단지 최종적으로 모델의 성능을 평가한다. Valid Data같은 경우는 val_loss, val_acc 등을 통해 나타난 수치를 통해 모델을 새롭게 만들고 조정하는 역할을 한다.

 

 

약간 쳇바퀴도는 설명같다고 느낄 수 있겠다. 그렇다면 더 단순히 말하겠다. Valid Data는 Train Data의 일부로 개발자가 이미 정답을 알고 있다. 그래서 개발자는 val_loss, val_acc같은 값을 알 수 있다. 하지만 Test Data는 개발자가 정답을 알고 있지 않다고 가정한다. 정답이 없는 테스트용 데이터들을 이미 만들어진 모델에 적용시켜 예측한 정답 값을 만들면 개발자는 모르는 Test Data의 정답과 비교하여 최종적인 정확도를 나타낸다. 만약 Test Data에서의 정확도가 낮다면 개발자는 모델을 다시 만들어야겠으나, 모델을 다시 설정할 때 Test Data는 어떤것도 관여하지 않는다. 왜냐하면 답을 모르기 때문이다. 이것은 정말 말 그대로 단지 검증용이다. 여기서 Test Data의 정답을 알아내 모델에 적용하면 그것은 더 이상 Test Data가 아니고 Valid Data가 된다.

 

 

 


 

 

 

Cross Validation (교차 검증)

 

하지만 위와 같이 Train Data를 Train Data와 Valid Data로 나누는 것은 Train Data가 많은 경우이다. 하지만 Train Data가 충분하지 않은 경우에서 Train Data와 Valid Data로 나누는 것은 매우 비효율적인 짓이다. (애초에 학습시킬 데이터도 적어 죽겠는데 거기서 또 나누는거니까) 따라서 이렇게 데이터가 적은 경우에는 Cross Validation(교차 검증)을 사용한다. 

 

Hold out / 이미지 재탕

 

단순히 Train Data와 Test Data로 나누면 무슨 문제가 발생할까? 이것은 매우 간단한 방식이지만 앞서 말한것처럼 모델을 만드는데 Test Data가 도움을 주지 못하므로 적은 양의 Train Data로는 좋은 성능을 기대하기 어려울 것이다. 또 데이터양이 너무 적기 때문에 Train Data와 Test Data를 어떻게 나누느냐에 따라 모델의 성능이 천차만별로 달라진다.

 

 


 

 

그렇다면 위와 같은 경우를 피하기 위해, 가지고 있는 데이터를 모두 사용하여 모델을 생성하면 이 모델은 학습에 사용한 데이터에는 성능이 좋지만 새로운 데이터에 대해서는 성능이 나쁜 모델이 된다(Overfitting). 이런 것을 막기 위해, 적은 데이터로도 Overfitting을 방지하고 일반화할 수 있는 방법을 평가하는 모델 검증 기법이 바로 Cross Validation(교차 검증)이다.

 

또한 추가적으로 Cross Validation을 하면 Hyperparameter Tuning을 할 수 있다. 여기서 Hyperparameter란, KNN에서는 k, 나이브베이즈에서 laplace, 의사결정트리의 trials, SVM의 lambda와 gamma, 신경망의 learning rate와 hidden을 의미한다. Hyperparameter Tuning으로 최적의 파라미터를 찾을 수도 있다.

 

Cross Validation(교차 검증)은 Train Data와 Test Data로 한 번 나누는 것보다 더 안정적이고 뛰어난 동계적 평가 방법으로 데이터를 여러 번 반복해서 나누고 여러 모델을 학습한다. 교차 검증에는 여러가지 방법이 있다.

 

1. K-Fold

 

K-Fold

K-Fold는 Train Data Set을 K개로 나누고 나눈 K개 중 한 개를 Valid, 나머지를 Training 용으로 사용하여 학습한다. Train Data로 학습을 한 후 한 개의 Valid Data로 모델을 테스트하여 성능을 측정한다. 이 때의 성능 평가는 Valid Data를 통해 얻은 정밀도의 평균과 표준편차 등으로 평가 할 수 있다. 같은 방식으로 K개의 모델을 반복한다. 최종 성능은 각각의 모델들의 성능 평가의 산술 평균으로 나타낸다. K-Fold는 모든 데이터를 학습과 평가용으로 사용할 수 있기 때문에 적은 데이터로 높은 정확도를 얻을 수 있으나, 학습 속도가 느리다.

 

 

2. Stratified K-Fold

 

K-Fold 와 Stratified K-Fold

 

 

예를 들어 데이터가 000000000011111111112222222222 로 구성되어 있다고 하자. 이걸 기존의 K-Fold로 나눈다면 어떻게 될까. 무조건 성능이 좋지 않게 나올 것이다. 이렇게 데이터가 특이하게 나뉜 경우를 위해 Stratified K-Fold를 사용한다. Stratified K-Fold는 각 Fold의 데이터 분포를 같게 해준다. 예로 든 데이터 같은 경우는 0,1,2로 10개의 Fold가 구성될 것이다. 이렇게 되면 데이터가 편향되지 않아 원래의 K-Fold의 장점을 잘 살리며 학습될 것이다.

 

 

3. Leave One Out (LOO)

 

K-Fold 교차 검증에서 K가 데이터 개수와 같을 때를 Leave One Out (LOO)라고 한다. 즉, 데이터 한 개를 하나의 모델로 하여 K-Fold 교차 검증을 한다는 말이다. 만약 데이터가 0부터 9까지 총 10개라고 하면 각 모델은 0,1,2,3,4,5,6,7,8,9 하나씩만을 갖는 모델이 될 것이고 이 모델로 K-Fold를 진행한다는 뜻이다. 이것은 적은 데이터에서 효과가 있으나 데이터가 많아지면 매우 느려진다.

 

 

4. Leave P Out

 

LOO와 비슷하지만, 여기서는 P개 만큼을 제외하고 Train Set과 Test Set을 만든다. 즉, N개의 데이터에 대해 Train_size=N-P, Valid_size=P 로 모델을 만들고 총 N개의 데이터에 대해 만큼 연산한다. LOO보다 더 느리다.

 

 

 

5. Shuffle Split

 

Shuffle Split


반복 횟수를 Train Fold, Valid Fold 개수와 독립적으로 조절할 수 있다. 또 Train Fold, Valid Fold의 합을 전체 Fold의 수와 다르게 설정도 가능하다.

 

 

6. Bootstrap

 

계산하기 어려운 데이터들의 분산, 평균, 편차 등을 구하는 통계기법으로 모집단의 분포도를 알 수 없을 때 사용한다. 데이터 양이 적을 때 모델의 통계적 신뢰도를 높이기 위해 사용되며, 데이터 셋 내에서 복원추출 방법을 이용해 새로운 샘플을 만든다. 이런 방식으로 충분히 많은 샘플이 생기면 모집단의 통계치를 추정할 수 있다.

'인공지능 > 인공지능 이론' 카테고리의 다른 글

8. Regression (회귀)  (0) 2019.09.08
7. 머신러닝종류  (0) 2019.09.08
5. 타이타닉(3) / 학습모델설정  (0) 2019.08.26
4. 타이타닉 (2) / 전처리  (0) 2019.08.22
3. 타이타닉 / EDA (탐색적 자료 분석)  (0) 2019.08.22

 

 

3. Predictive Modeling

 

참고하고 있는 이 옹골찬 케글은 학습 모델을 무려 7가지나 사용해준다. 킹갓이 아닐 수 없다. 나같은 초짜에게는 다양한 예시가 제일 이해도를 높여준다. 하지만 지금까지 머신러닝 Workflow만 설명한 것 치고는 꽤나 어려운 주제라고 할 수 있겠다. 따라서 앞으로 여기서 사용한 학습 모델을 설명하는 글을 작성할 때마다 링크를 걸어 두겠다. 일단 지금은 이런 것들이 있다, 이렇게 학습시킨다 정도로 알고 넘어가자.

 

from sklearn.model_selection import train_test_split #training and testing data split
from sklearn import metrics #accuracy measure

train,test=train_test_split(train_data,test_size=0.3,random_state=0,stratify=train_data['Survived'])
train_X=train[train.columns[1:]]
train_Y=train[train.columns[:1]]
test_X=test[test.columns[1:]]
test_Y=test[test.columns[:1]]
X=train_data[train_data.columns[1:]]
Y=train_data['Survived']

 

1)Logistic Regression

from sklearn.linear_model import LogisticRegression

model = LogisticRegression()
model.fit(train_X,train_Y)
prediction3=model.predict(test_X)
print('The accuracy of the Logistic Regression is',metrics.accuracy_score(prediction3,test_Y))
The accuracy of the Logistic Regression is 0.8134328358208955

 

 

2)Support Vector Machines(Linear and radial)

  • Linear Support Vector Machine (linear-SVM)
from sklearn import svm

model=svm.SVC(kernel='linear',C=0.1,gamma=0.1)
model.fit(train_X,train_Y)
prediction1=model.predict(test_X)
print('Accuracy for linear SVM is',metrics.accuracy_score(prediction1,test_Y))
Accuracy for linear SVM is 0.8171641791044776

 

  • Radial Support Vector Machine (rbf-SVM)
model=svm.SVC(kernel='rbf',C=1,gamma=0.1)
model.fit(train_X,train_Y)
prediction2=model.predict(test_X)
print('Accuracy for rbf SVM is ',metrics.accuracy_score(prediction2,test_Y))
Accuracy for rbf SVM is  0.8022388059701493

 

 

3)Random Forest

from sklearn.ensemble import RandomForestClassifier

model=RandomForestClassifier(n_estimators=100)
model.fit(train_X,train_Y)
prediction7=model.predict(test_X)
print('The accuracy of the Random Forests is',metrics.accuracy_score(prediction7,test_Y))
The accuracy of the Random Forests is 0.7873134328358209

 

 

4)K-Nearest Neighbours (KNN)

from sklearn.neighbors import KNeighborsClassifier

model=KNeighborsClassifier() 
model.fit(train_X,train_Y)
prediction5=model.predict(test_X)
print('The accuracy of the KNN is',metrics.accuracy_score(prediction5,test_Y))
The accuracy of the KNN is 0.7873134328358209

 

n_neighbours 속성의 값을 변경하면 KNN 모델의 정확도도 바뀐다.

a_index=list(range(1,11))
a=pd.Series()

x=[0,1,2,3,4,5,6,7,8,9,10]

for i in list(range(1,11)):
    model=KNeighborsClassifier(n_neighbors=i) 
    model.fit(train_X,train_Y)
    prediction=model.predict(test_X)
    a=a.append(pd.Series(metrics.accuracy_score(prediction,test_Y)))
    
plt.plot(a_index, a)
plt.xticks(x)
fig=plt.gcf()
fig.set_size_inches(12,6)
plt.show()

print('Accuracies for different values of n are:',a.values)
print("Max Accuracy ",a.values.max())

주어진 N의 범위 안에서의 정확도 그래프

Accuracies for different values of n are: [0.75746269 0.76119403 0.79477612 0.76492537 0.78731343 0.77985075
 0.76865672 0.7761194  0.75       0.76119403]
Max Accuracy  0.7947761194029851

 

 

5)Naive Bayes

from sklearn.naive_bayes import GaussianNB

model=GaussianNB()
model.fit(train_X,train_Y)
prediction6=model.predict(test_X)
print('The accuracy of the NaiveBayes is',metrics.accuracy_score(prediction6,test_Y))
The accuracy of the NaiveBayes is 0.8171641791044776

 

 

6)Decision Tree

from sklearn.tree import DecisionTreeClassifier

model=DecisionTreeClassifier()
model.fit(train_X,train_Y)
prediction4=model.predict(test_X)
print('The accuracy of the Decision Tree is',metrics.accuracy_score(prediction4,test_Y))
The accuracy of the Decision Tree is 0.7947761194029851

 

 

앞서했던 전처리와 EDA에 비해 함수가 매우 간략한 것을 알 수 있다. 사실 인공지능을 하다 보면 알고리즘을 돌려 정확도를 내는 것은 정말 쉽다. 단순히 함수만 사용하면 되니까 말이다. 하지만 이 함수들의 내용을 잘 알아야 함수를 설정할 때 사용자가 지정해야하는 파라미터들을 잘 설정할 수 있고 원하는 결과가 나오지 않았을 때 대체할만한 알고리즘을 선택할 수 있다. 하지만 일단 여기까지하자. 정확도가 높진 못하지만, 어쨌든 우리는 진짜 머신러닝을 해봤기 때문이다. 앞으로 더 공부하면서 차차 정확도를 높여가보고 여기에 쓰인 알고리즘은 어떤 원리로 동작되는지 알아가보자.

 

 

 

2. Feature Engineering and Data Cleaning

 

데이터를 대충 확인했으니 해야 할 일은 무엇인가? 바로 전처리이다! 사실 앞선 내용에서는 전처리가 EDA 보다 먼저라고 했지만 사실 EDA와 전처리는 같이 왔다갔다 하며 하기 때문에 이 문제에서는 우선적으로 EDA로 데이터를 확인했다. EDA로 데이터의 특성을 얼추 파악했으니 이제 전처리를 해야한다. 무엇을 전처리해야할까?

 

전처리할 것을 파악하기 위해 null값의 개수를 확인해보겠다.

 

train_data.isnull().sum()
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

 

Age와 Cabin은 앞선 EDA에서 누락된 데이터가 꽤나 있음을 알았는데 Embarked도 2개의 누락된 데이터가 있었다. 앞서 말했지만 Cabin은 누락된 데이터가 너무 많으므로 과감히 지운다. 그럼 Age와 Embarked의 누락된 데이터를 채워보자. 어떻게 채워야 누락된 데이터를 잘 채웠다고 소문이날까?

 

Age를 채우는 방법은 다양할 것이다. 누락된 데이터에 전체 나이의 평균치를 넣을 수도 있고 EDA를 통해 알게 된 데이터의 특징에 따라 채울 수도 있다. 필자는 참고한 케글의 방식을 따르겠다. 참고한 케글은 사람의 이름을 기준으로 Age를 나누었다. 탑승자 이름의 중간에 다양한 title이 존재하는데 이 title을 한 번 확인해보자.

 

train_data['Initial']= train_data.Name.str.extract('([A-Za-z]+)\.')

test_data['Initial']= test_data.Name.str.extract('([A-Za-z]+)\.')

# A-Z 또는 a-z 사이에 있고 .(점)이 오는 문자열을 찾는다. 중간에 위치한 title 찾는 코드
# 성별로 분류하여 Initial을 확인

pd.crosstab(train_data.Initial,train_data.Sex).T.style.background_gradient(cmap='summer_r')

성별을 기준으로 Initial을 확인한다.

 

여성은 Miss와 Mrs를 제일 많이 쓰고, 남성은 Mr와 Master를 많이 사용한다. 그럼 우리는 이제 누락된 데이터 중에서 Miss와 Mrs, Mr와 Master를 title로 갖는 값에 어떤 값을 채워야할까? 채워야 하는 값을 생각하기에 앞서, Miss, Mrs, Mr, Master와 같이 유의미하게 많은 데이터가 아닌 것들을 유의미한 데이터에 병합시킨다. 여기서 Mlle과 Mme는 Miss의 오타로 추정되기 때문에 Miss로 들어간다.

 

# 라벨(title)을 유의미한 데이터로 대체한다.

train_data['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don'],['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr'],inplace=True)
test_data['Initial'].replace(['Mlle','Mme','Ms','Dr','Major','Lady','Countess','Jonkheer','Col','Rev','Capt','Sir','Don'],['Miss','Miss','Miss','Mr','Mr','Mrs','Mrs','Other','Other','Other','Mr','Mr','Mr'],inplace=True)

# 라벨에 따라 평균 값을 나타낸다.

train_data.groupby('Initial')['Age'].mean()
Initial
Master     4.574167
Miss      21.860000
Mr        32.739609
Mrs       35.981818
Other     45.888889
Name: Age, dtype: float64

 

그럼 이런 결과가 나타난다. Master는 답지 않게 어린 아이들을 뜻하는 것같고 Miss는 약 22세, Mr는 약 33세, Mrs는 약 36세, 기타의 title들은 약 46세의 평균값을 가진다. 이 평균값들을 누락된 데이터에 title을 기준으로 집어넣는다.

 

train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Mr'),'Age']=33
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Mrs'),'Age']=36
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Master'),'Age']=5
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Miss'),'Age']=22
train_data.loc[(train_data.Age.isnull())&(train_data.Initial=='Other'),'Age']=46

test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Mr'),'Age'] = 33
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Mrs'),'Age'] = 36
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Master'),'Age'] = 5
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Miss'),'Age'] = 22
test_data.loc[(test_data.Age.isnull())&(test_data.Initial=='Other'),'Age'] = 46


# 쉽게 하는 방법
title = train_data.Name.str.split('.').str.get(0).str.split(',').str.get(1)
train_data['title'] = title

a = train_data.groupby(train_data.title).Age.mean().round().reset_index()

pd.merge(train_data,a,on='title')

 

이제 train_data와 test_data에 누락된 값이 있는지, 평균값들이 잘 채워져 있는지 확인해본다.

 

train_data.Age.isnull().any()
test_data.Age.isnull().any()
False

 

Embarked 값은 train_data에서만 누락되어있고 누락된 데이터가 단지 2개 뿐이니, 가장 많이 탑승한 탑승구인 S를 채워넣겠다.

 

train_data['Embarked'].fillna('S',inplace=True)
train_data.Embarked.isnull().any()

 

 


 

1) Age_band

 

Age 데이터는 연속성을 띈다. 그냥 사용해도 되겠으나 우리는 다양한 방법을 공부하고 있기 때문에 이것을 그룹화시키려고 한다. 

 

# Age를 그룹화하기

train_data['Age_band']=0
train_data.loc[train_data['Age']<=16,'Age_band']=0
train_data.loc[(train_data['Age']>16)&(train_data['Age']<=32),'Age_band']=1
train_data.loc[(train_data['Age']>32)&(train_data['Age']<=48),'Age_band']=2
train_data.loc[(train_data['Age']>48)&(train_data['Age']<=64),'Age_band']=3
train_data.loc[train_data['Age']>64,'Age_band']=4
train_data.head()

train_data.head()를 통해 Age_band를 확인할 수 있다.

 

train_data['Age_band'].value_counts().to_frame().style.background_gradient(cmap='summer')

 

 

sns.factorplot('Age_band','Survived',data=train_data,col='Pclass')
plt.show()

Pclass 별 Age_band의 생존률

 

연속적인 Age 데이터를 Age_band로 그룹화하고 그것에 대해 EDA를 해보았다. 역시나 어떤 클래스던지간에 나이가 어린 사람일수록 생존률이 높음을 확인할 수 있다.

 

 

2) Family_Size and Alone

 

Family_size 와 Alone으로 분류한다. 이것은 Parch와 SibSp의 압축이다. 생존율이 승객의 가족 규모와 연관이 있는지를 판단하기 위해 분류한다.

 

# train_data를 Family_Size와 Alone으로 분류

#family size
train_data['Family_Size']=0
train_data['Family_Size']=train_data['Parch']+train_data['SibSp']

#Alone
train_data['Alone']=0
train_data.loc[train_data.Family_Size==0,'Alone']=1

sns.factorplot('Family_Size','Survived',data=train_data)
plt.show()

혼자라고 생존률이 높은것은 아니다.

 

2~4인 가족의 생존률이 다른 가족 수보다 더 높음을 알 수 있다.

 


 

얼추 데이터를 그룹화했으니 이제 데이터들이 얼마나 서로 유의미하게 연관되어있는지를 확인해볼 차례이다.

앞선 내용에서 생존과 굉장히 밀접한 관련이 있던 3가지 부분 또한 위와 같이 숫자로 그룹화하겠다.

 

train_data['Sex'].replace(['male','female'],[0,1],inplace=True)
train_data['Embarked'].replace(['S','C','Q'],[0,1,2],inplace=True)
train_data['Initial'].replace(['Mr','Mrs','Miss','Master','Other'],[0,1,2,3,4],inplace=True)

 

그 후 무의미하다고 여겨지는 데이터들을 Drop한다.

 

train_data.drop(['Name','Age','Ticket','Cabin','PassengerId','SibSp','Parch'],axis=1,inplace=True)
sns.heatmap(train_data.corr(),annot=True,cmap='RdYlGn',linewidths=0.2,annot_kws={'size':20})
fig=plt.gcf()
fig.set_size_inches(18,15)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()

상관관계를 나타내는 표가 만들어진다.

 

위의 이미지는 상관관계를 나타내주는 것이다. 색이 진할수록 관련도가 높은것이다. 이 표를 보면 생존률은 PClass, Sex, Fare, Initial과 많이 연관되어 있음을 알 수 있다. 상관관계 또한 EDA중의 하나이다. 개발자는 상관관계 표를 통해 유의미하게 연관되어 있는 데이터를 찾을 수 있다. 지금도 PClass, Sex, Initial에 대한 것은 이전 EDA를 통해 알았지만 Fare는 처음 알지 않았는가!

 

또 길어졌으니 학습하는 방법은 다음에 설명하겠다.

 

 

 

 

킹갓 케글을 아는가? 사실 필자도 최근에 알았다. (왜냐면 인공지능을 최근에 시작했음) 인공지능을 하면서 단언코 최고의 커뮤니티는 케글이 아닐까 한다. 다양한 대회들이 열리는 것은 물론이고 세계 각국의 인공지능 능력자들이 서로의 코드를 공유하기 때문에 개인적으로 깃허브보다 낫다고 생각한다. 그리고 Notebook을 사용해서 코드를 진행하면 흐름을 잘 이해할 수 있어서 처음 인공지능을 하는 사람들에게는 적격인 사이트라고 생각한다.

 

https://www.kaggle.com/c/titanic

 

Titanic: Machine Learning from Disaster

Start here! Predict survival on the Titanic and get familiar with ML basics

www.kaggle.com

 

앞서 2. 머신러닝 진행과정 에서 말했다시피 이론만 하고 넘어가면 제대로 모른다. 그러니 위의 Titanic 대회에 참가해 머신러닝의 진행과정을 실전을 통해 이해해보자!

 

참고로 케글 사용법과 타이타닉에 대해 잘 설명해주는 유튜브 동영상은 여기 를 누르면 되고, 필자가 참고한 케글은 여기 를 누르면 된다.

 

 

1.  EDA (탐색적 자료 분석)

 

import numpy as np 
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno
plt.style.use('fivethirtyeight')
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline

# 데이터를 불러오고 보여준다.
data=pd.read_csv('../input/titanic-machine-learning-from-disaster/train.csv')

# 참고로 data.head()는 앞의 5개만을 보여준다.
data.head() 
 
 

데이터를 확인하면 이렇게 나온다.

 

Survived 0은 사망, 1은 생존을 의미
Pclass 선실 클래스
SibSp 탑승한 형제자매, 배우자의 수
Parch 탑승한 부모, 자식의 수
Ticket 티켓 번호
Fare 운임
Cabin 객실번호
Embarked 탑승장소(C: Cherbourg, Q: Queenstown, S: Southampton)

 

위의 라벨들이 의미하는 것을 표로 나타냈다. 당장 저 데이터만 봐도 NaN 값을 볼 수 있는데 그러면 얼마나 많은 데이터들이 누락되어있을까?

 

# train_data의 누락된 자료 비율 파악

for col in train_data.columns :
    msg = '항목 {:>10}\t 비어있는 자료의 비율 : {:.2f}%'.format(col, 100 * (train_data[col].isnull().sum() / train_data[col].shape[0]))
    print(msg)
항목 PassengerId	 비어있는 자료의 비율 : 0.00%
항목   Survived	 비어있는 자료의 비율 : 0.00%
항목     Pclass	 비어있는 자료의 비율 : 0.00%
항목       Name	 비어있는 자료의 비율 : 0.00%
항목        Sex	 비어있는 자료의 비율 : 0.00%
항목        Age	 비어있는 자료의 비율 : 19.87%
항목      SibSp	 비어있는 자료의 비율 : 0.00%
항목      Parch	 비어있는 자료의 비율 : 0.00%
항목     Ticket	 비어있는 자료의 비율 : 0.00%
항목       Fare	 비어있는 자료의 비율 : 0.00%
항목      Cabin	 비어있는 자료의 비율 : 77.10%
항목   Embarked	 비어있는 자료의 비율 : 0.22%

 

# test_data의 누락된 자료 비율 파악

for col in test_data.columns :
    msg = '항목 {:>10}\t 비어있는 자료의 비율 : {:.2f}%'.format(col, 100 * (test_data[col].isnull().sum() / test_data[col].shape[0]))
    print(msg)
항목 PassengerId	 비어있는 자료의 비율 : 0.00%
항목     Pclass	 비어있는 자료의 비율 : 0.00%
항목       Name	 비어있는 자료의 비율 : 0.00%
항목        Sex	 비어있는 자료의 비율 : 0.00%
항목        Age	 비어있는 자료의 비율 : 20.57%
항목      SibSp	 비어있는 자료의 비율 : 0.00%
항목      Parch	 비어있는 자료의 비율 : 0.00%
항목     Ticket	 비어있는 자료의 비율 : 0.00%
항목       Fare	 비어있는 자료의 비율 : 0.24%
항목      Cabin	 비어있는 자료의 비율 : 78.23%
항목   Embarked	 비어있는 자료의 비율 : 0.00%

 

# 누락된 데이터를 시각화 해보자

msno.matrix(df=train_data.iloc[:,:], figsize=(8,8), color=(0.7,0.4,0.2))

train_data의 데이터 시각화 / 디스크 정리 보는 기분

 

 

Cabin과 Age 데이터가 꽤 많이 누락되어있고 Cabin 데이터는 사실상 없다고 보는게 맞겠다. 하지만 데이터가 많이 없다고 무의미한 데이터일까? 대답은 그건 아무도 모른다이다. Cabin같은 경우는 누락된 데이터가 너무 많아 평균값으로 채울 수도 없으니 지우는게 맞다고 본다. 하지만 Age같이 애매하게 누락된 데이터들은 최대한 살려야한다. 사실 이게 또 좋다고 볼 수도 없다. 하지만 데이터가 많을수록 더 정확한 모델이 만들어지기 때문에 최대한 많은 데이터가 필요하므로 사람들은 열심히 열심히 누락된 데이터를 매꾸는 전처리 과정을 거치게 된다. 일단 계속해서 EDA를 하자.

 

f,ax=plt.subplots(1,2,figsize=(18,8))
train_data['Survived'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.1f%%',ax=ax[0],shadow=True)
ax[0].set_title('Survived')
ax[0].set_ylabel('')
sns.countplot('Survived',data=train_data,ax=ax[1])
ax[1].set_title('Survived')
plt.show()

위의 코드로 코딩을 하면 이와 같은 그림이 나온다.

 

앞서 표에서 말했다시피 0은 사망을 뜻한다. 많은 사람들이 죽었음을 알 수 있는 대목이다. 그러면 여기서 궁금해지는 것이 있다. 어떤 사람들이 살아남았을까? 우리는 이것을 확인하기위해 EDA를 사용한다. 그리고 이것을 확인하는 것은 예측을 더 쉽게할 수 있다.

 

# 성별에 따른 사망자와 생존자

f,ax=plt.subplots(1,2,figsize=(18,8))
train_data[['Sex','Survived']].groupby(['Sex']).mean().plot.bar(ax=ax[0])
ax[0].set_title('Survived vs Sex')
sns.countplot('Sex',hue='Survived',data=train_data,ax=ax[1])
ax[1].set_title('Sex:Survived vs Dead')
plt.show()

여자가 압도적으로 생존률이 높았다.

남자가 약 600명, 여자가 약 300명으로 남자 승객이 여성에 비해 압도적으로 높았음에도 불구하고 여성의 생존률이 남성의 생존률보다 더 높은 것을 확인할 수 있다. (사스가 Lady first의 나라) 이런 시각적인 데이터를 보면 타이타닉에 탔던 사람들은 약자를 우선으로 여겼을 확률이 매우 높다. 이런 직관을 판단하기 위해 EDA과정이 필요하다. 그렇다면 약자인 노인과 영아의 생존률은 얼마인지 확인해보자.

 

# 나이에 따른 사망자와 생존자

f,ax=plt.subplots(1,2,figsize=(20,10))
train_data[data['Survived']==0].Age.plot.hist(ax=ax[0],bins=20,edgecolor='black',color='red')
ax[0].set_title('Survived= 0')
x1=list(range(0,85,5))
ax[0].set_xticks(x1)
train_data[train_data['Survived']==1].Age.plot.hist(ax=ax[1],color='green',bins=20,edgecolor='black')
ax[1].set_title('Survived= 1')
x2=list(range(0,85,5))
ax[1].set_xticks(x2)
plt.show()

15세 미만의 아이들의 생존자는 대체로 사망자보다 많다.

 

fig, ax = plt.subplots(1,1, figsize=(9,5))
sns.kdeplot(train_data[train_data['Survived'] == 1]['Age'], ax=ax)
sns.kdeplot(train_data[train_data['Survived'] == 0]['Age'], ax=ax)
plt.legend(['Survived','Dead'])
plt.xlabel('Age')
plt.ylabel('Survived')
plt.show()

위의 막대그래프를 더 직관적으로 나타냈다.

 

위의 그래프들을 보면 죽은 사람과 산 사람이 비슷비슷한 20~60대와는 달리 확실히 15세 미만의 아이들이 생존률이 더 높음을 알 수 있다. (직관이 먹혔다!) 그렇다면 클래스에 따라서는 생존률이 어떻게 나타날까? 대부분 사람들의 직관으로 부자들이 많이 생존했을 것이라고 예측할 수 있다. 과연 그럴까?

 

# 클래스에 따른 사망자와 생존자

pd.crosstab(train_data.Pclass,data.Survived,margins=True).style.background_gradient(cmap='summer_r')

클래스에 따른 생존자와 사망자 수

 

f,ax=plt.subplots(1,2,figsize=(18,8))
train_data['Pclass'].value_counts().plot.bar(color=['#CD7F32','#FFDF00','#D3D3D3'],ax=ax[0])
ax[0].set_title('Number Of Passengers By Pclass')
ax[0].set_ylabel('Count')
sns.countplot('Pclass',hue='Survived',data=train_data,ax=ax[1])
ax[1].set_title('Pclass:Survived vs Dead')
plt.show()

클래스에 따른 데이터를 시각화하였다.

 

3등급의 사람들이 제일 많았으나 3등급의 사망자가 제일 많았다. 1등급은 예상대로 생존자가 사망자보다 더 많았다. (직관이 먹혔다!2) 그렇다면 여기서 갑자기 궁금한 것이 있다. (사실 궁금하다기보다는 코드가 있으니 써먹으려고ㅋㅎ) 클래스 간 성별에 따른 생존률이 위에서 보여진것과 동일할까? 3등급의 사람들은 전체 성별 생존률과는 무관하게 남성이 여성보다 더 많이 살았을 수도 있지 않을까?

 

# 클래스 별 성별에 따른 사망자와 생존자

sns.factorplot('Pclass','Survived',hue='Sex',data=train_data)
plt.show()

응 아니야

 

이건 나의 직관이 틀렸다! 이렇게 이런 저런 직관과 데이터의 분류, 흐름 등을 파악하기 위해 EDA 과정은 반드시 필요하다. 개발자가 데이터를 잘 이해해야 좋은 알고리즘을 선택해 좋은 모델을 만들 수 있기 때문이다. 이렇게 사람의 직관으로 어느정도 추정할 수 있는 데이터와 달리, 전혀 감도 잡을 수 없는 데이터들은 어떻게 사용해야할까? 이 역시 EDA를 통해 데이터의 숨겨져 있는 특징들을 파악할 수 있다. 

 

# 탑승장소를 기반으로한 다양한 데이터 조합들

f,ax=plt.subplots(2,2,figsize=(20,15))
sns.countplot('Embarked',data=train_data,ax=ax[0,0])
ax[0,0].set_title('No. Of Passengers Boarded')
sns.countplot('Embarked',hue='Sex',data=train_data,ax=ax[0,1])
ax[0,1].set_title('Male-Female Split for Embarked')
sns.countplot('Embarked',hue='Survived',data=train_data,ax=ax[1,0])
ax[1,0].set_title('Embarked vs Survived')
sns.countplot('Embarked',hue='Pclass',data=train_data,ax=ax[1,1])
ax[1,1].set_title('Embarked vs Pclass')
plt.subplots_adjust(wspace=0.2,hspace=0.5)
plt.show()

 

sns.factorplot('Pclass','Survived',hue='Sex',col='Embarked',data=train_data)
plt.show()

역시 Lady Frist

 

전체적으로 봤을 때 S 에서 가장 많은 사람이 탑승했다. C와 Q 는 남녀의 비율이 비슷하고, S는 남자가 더 많으며 S의 경우 생존률 낮다.  Pclass와 함께 보면 C가 생존률 높은 이유와 S가 생존률이 낮은 이유가 클래스와 연관이 있음을 알 수 있다. 이렇게 EDA를 사용하면 자칫 보지 않았을 연관성에 대해서도 판단할 수 있게 된다.

 

너무 길어졌으니 전처리 과정은 다음에 설명하겠다.

 

 

 

머신러닝 Workflow

 

대부분의 머신러닝은 위와 같은 방식으로 진행된다.

 

1) Data Preprocessing (데이터 전처리)

 

수집한 데이터가 깨끗할거라는 기대는 버리는게 좋다. 애초에 데이터를 수집하는 것 부터가 일이다. 예를 들어 스타일링 변화의 실패를 줄이고자 다양한 헤어스타일을 가상으로 나의 얼굴에 대입해본다고 생각하자. 여기서의 헤어스타일은 굉장히 자연스럽게 연출되기를 기대한다. 그럼 이 모델을 만들기 위해서는 우선 데이터가 필요하다. 앞서 말했지만 다량의 데이터들이 수학적인 알고리즘을 통해 데이터들끼리 자체적인 로직을 만들기 때문에 우리는 다!량!의! 데이터들이 필요하다. 당장 구글에 머리스타일을 검색해 보자. 다양한 각도와 구도의 이미지들을 볼 수 있다. 우리의 똑똑하지만 멍청한 인공지능은 깨끗하게 가공된 데이터가 아니면 학습률/예측률이 난리가 난다. (사실 인공지능은 영화들이 만들어낸 허구다. 나도 당장 로봇이 말하는 그런 AI만 생각했지 현재까지의 인공지능은 그냥 인간이 거의 다함ㅠ 이렇게 전처리가 빡셀줄 몰랐지) 그래서 인간은 노동을 통해 다량의 데이터들을 깨끗하고 정제된 데이터로 만들어 학습에 사용해야한다. 따라서 데이터 전처리는 단순 노동과 노동과 노동의 연속이다. (전처리 방법도 다양하기 때문에 진짜 죽음 뿐)

 

전처리를 하다보면 이런 상황 발생

 

 

2) EDA (Exploratory Data Analysis)  & Feature Engineering (탐색적 데이터분석)

 

EDA란 수집한 데이터를 다양한 각도에서 관찰하고 이해하는 과정이다. 한마디로 데이터를 알고리즘에 때려 박기 전에 그래프나 통계적인 방법으로 자료를 직관적으로 바라보며 특징을 찾고, 숨겨진 패턴을 발견하는 과정이다. 사실 필자는 이것의 중요성에 대해 잘 인식하지 못했다. 근데 이게 사실상 제일 중요하다. 전처리는 노동과 노동의 연속이라면 이 단계에서는 직관이 중시되기 때문이다. 생각보다 인공지능을 직접 하다보면 개발자의 직관이 많이 요구된다. 분명히 사용하는 알고리즘은 더럽게 복잡한 수학적 공식을 사용하지만 그건 컴퓨터의 영역이라면 개발자는 이 과정을 통해 데이터의 특징을 파악하여 발견한 특징들을 잘 활용하여 모델을 구성해야한다. 그래야 학습률이 좋다. 근데 이 과정도 전처리 못지 않게 노동과 노동과 노동의 연속이다. (사실 인공지능은 말만 번지르르하지 실제로 만들려면 그냥 단순 노동과 직관이 90%라고 해도 과언이 아니다.) 

 

 

3) Algorithm selection (알고리즘 선택)

 

WOW 드디어 알고리즘을 선택하는 단계이다! 여기가 이제 진짜 인공지능이라고 보면 되겠다만 문제는 여기도 노동과 노동의 연속이라는 것이다. (도망쳐) 앞으로 차차 다양한 알고리즘에 대해 글을 쓰겠지만 수학적인 내용은 굉장히 복잡하다. 필자는 전공에서 사용하는 수학 공식들부터도 어떻게 이런 수식들이 이런 결과를 도출할 수 있는가에 대한 경이로움을 느꼈는데 인공지능은 그거의 곱절로 놀랍고 놀랍다. (문제는 그만큼 복잡함) 내가 수학을 잘하고 좋아했더라면 알고리즘을 만드는 사람이 되지 않았을까 하는 헛된 꿈도 잠깐 꾸어봤지만, 나는 새로운 수식을 만들어낼 정도로 의지있고 똑똑한 사람이 아니기 때문에 (당장 남이 만든 공식 이해하기도 벅참) 있는 알고리즘이나 잘 활용하자는 생각이다. 문제는 다양한 알고리즘을 수학적으로 이해했다고 해도 적용과는 또 별개라는 것이다. 알고리즘들은 다양하고, 거기에 사용되는 속성들도 굉장히 다양하며 그것은 사람이 모두 설정한다. (인공지능 당신 하는게 도대체 뭔데) 그래서 앞과정을 다끝내도 여기와서도 멘탈이 터진다. 프로그래밍을 하려면 초월과 달관하는 자세는 필수라고 생각한다.

 

코딩이 오류가 나도, 기껏 오류 고쳐서 돌아가도 원하는 결과가 안나와도 나는 괜찮다.

 

 

4) Training / Evaluation / Parameter Tuning

 

이걸 한 번에 묶은 이유는 학습시키면 평가하고 평가가 원하는 결과가 안나오면 하이퍼 파라미터를 다시 조정하는 과정을 계속해서 반복해야 하기 때문이다. 이 과정은 그래도 앞의 과정들보단 낫다. 나는 기다리고 컴퓨터가 돌아가기 때문이다. 문제는 반복했는데도 일정수준 이상으로 높아지지 않으면 앞 과정들을 다시 반복해야한다는 것이다. 이런 결과가 생기면 또 멘탈이 터진다. 앞의 과정들을 잘 하면 이런 불상사는 없을 수 있겠으나 글쎄... 지금 당장 하고 있는 프로젝트도 이 과정에서 결과값이 안나오면 전처리를 다시 만지는걸 보아 불상사는 항상 생길 것 같다. (그것이 개발자의 인생이지 노답)

 

 

5) Deployment (배포)

 

이 과정까지 도달했다면 당신은 축배를 들어야한다. 당신은 진정한 인공지능을 만들어 냈다. 

 

 

 

 

 

당신은 이 글을 보면 아~ 인공지능은 대충 이런 구조로 되어있구나를 알 수 있게 될것이다.

문제는 코딩은 하나도 모르고 대충 느낌으로 알게 된다는 것이다! 나는 항상 이게 불만이었다. 모든 이론들은 이론상으로는 알겠다라고 느끼지만 실전에 들어가면 까막눈처럼 막막하기 때문이다. 따라서 다음에는 이 과정들을 함축적이면서도 자세하게 알려주는 문제에 대해 다루고자 한다. 커밍쑨!

+ Recent posts