Ch1-2 머신러닝이란, Workflow(연습문제) 01-15 (머신러닝 1-4)

김민지·2023년 5월 17일
0

Part 09. 머신러닝

목록 보기
1/4
  1. 머신러닝(Machine Learning)이란
  • 명시적으로 프로그래밍하지 않고도 컴퓨터에 학습할 수 있는 능력을 부여하는 학문
  • (과거 데이터로부터 얻은) 경험이 쌓여감에 따라 주어진 task의 성능이 점점 좋아질 때, 컴퓨터 프로그램은 경험으로부터 학습한다고 할 수 있음
    -> 명시적인 프로그램에 의해서가 아니라, 주어진 데이터를 통해 규칙을 찾는 것

  1. Decision Tree
  • Decision Tree의 분할 기준 (Split Criterion)
    1) 정보 획득(Information Gain)
    - 정보의 가치를 반환하는 데 발생하는 사전의 확률이 작을수록 정보의 가치는 커짐
    - 정보 이득이란 어떤 속성을 선택함으로 인해서 데이터를 더 잘 구분하게 되는 것
    2) 엔트로피 개념
    - 확률 분포의 무질서도(disorder)나 불확실성(uncertainty)
    - 얼마만큼의 정보를 담고 있는가?에 대한 것
    - 낮을수록 좋음!
    3) 지니계수
    - Gini Index 혹은 불순도율 (역시 낮을수록 좋음!)
    - 엔트로피와 비슷한 개념이면서 엔트로피보다 계산량이 적어서 지니계수를 사용하는 경우가 많음

  1. Scikit Learn
  • 현재 파이썬에서 가장 유명한 기계 학습 오픈소스 라이브러리
from sklearn.tree import DecisionTreeClassifier

iris_tree = DecisionTreeClassifier()
iris_tree.fit(iris.data[:, 2:], iris.target)

-> iris.data의 모든 행의 2,3 컬럼의 데이터를 학습시킴. 정답은 iris.target이라고 알려줌.

y_pred_tr = iris_tree.predict(iris.data[:, 2:])
y_pred_tr

-> 해당 데이터의 length, width만 가지고 정답을 predict해보라고 시킴

from sklearn.metrics import accuracy_score
accuracy_score(iris.target, y_pred_tr)

-> (정답, 예측값)을 입력해서 예측값의 정확도를 알려줌


  1. 과적합
  • 지도 학습 : 학습 대상이 되는 데이터에 정답(label)을 붙여서 학습시키고 모델을 얻어서, 완전히 새로운 데이터에 모델을 사용해서 '답'을 얻고자 하는 것
from sklearn.tree import plot_tree

plt.figure(figsize=(12,8))
plot_tree(iris_tree);

-> decision tree를 그려줌 (어떤 조건에 의해 구분했는가)

from mlxtend.plotting import plot_decision_regions

plt.figure(figsize=(14,8))
plot_decision_regions(X=iris.data[:, 2:], y=iris.target, clf=iris_tree, legend=2)
plt.show()

-> 결정(decision) 경계를 확인할 수 있음

  • accuracy가 높게 나왔다고 해도 일반화할 수 없음
  • 어차피 얻은 데이터는 유한하기 때문에 일반화는 힘듬
  • 따라서 복잡한 경계면은 모델의 성능을 나쁘게 만들뿐임
    -> 과적합이란 내가 가진 데이터에만 딱 적합하기 때문에 다른 일반적인 데이터에는 제성능이 못나오거나 아예 틀릴 수 있을 때를 말함

  1. 데이터 분리 (훈련/검증/평가)
  • 확보한 데이터 중에서 일부만 훈련시키고, 모델 학습에 사용하지 않고 빼둔 데이터를 가지고 모델을 테스트함
from sklearn.model_selection import train_test_split

features = iris.data[:, 2:]
labels = iris.target

X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=13)

-> 테스트 0.2, 훈련 0.8 비율로 데이터를 랜덤으로 분리시킴

import numpy as np

np.unique(y_test, return_counts=True)

-> 분리된 데이터 종류별 개수 확인해보기
-> 일정하게 맞춰주기 위해서는 stratify 옵션 사용

X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, stratify=labels, random_state=13)
  • train 데이터만 대상으로 결정 나무 모델 만들기 (훈련시키기)
from sklearn.tree import DecisionTreeClassifier
iris_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
iris_tree.fit(X_train, y_train)

-> 학습할 때마다 일관성을 위해 random_state만 고정하기
-> 모델을 단순화시키기 위해(과적합을 막기 위해) max_depth(나무 모델 깊이)를 조정 (모델의 성능을 제한함)

from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
plt.figure(figsize=(12,8))
plot_tree(iris_tree);

-> 나무 모델 확인

from sklearn.metrics import accuracy_score

y_pred_tr = iris_tree.predict(iris.data[:, 2:])
accuracy_score(iris.target, y_pred_tr)

-> accuracy 확인

from mlxtend.plotting import plot_decision_regions

plt.figure(figsize=(14,8))
plot_decision_regions(X=X_train, y=y_train, clf=iris_tree, legend=2)
plt.show()

-> 결정 경계 확인

y_pred_test = iris_tree.predict(X_test)
accuracy_score(y_test, y_pred_test)

-> 분리해서 남겨둔 다른 데이터로 accuracy test해보기
-> 과적합 여부 확인 (정확도가 높은지)

scatter_highlight_kwargs = {'s':150, 'label':'Test data', 'alpha':0.9}
scatter_kwargs = {'s':120, 'edgecolor':None, 'alpha':0.9}

plt.figure(figsize=(12,8))
plot_decision_regions(X=features, y=labels,
                      X_highlight=X_test, clf=iris_tree, legend=2,
                      scatter_highlight_kwargs=scatter_highlight_kwargs,
                      scatter_kwargs=scatter_kwargs,
                      contourf_kwargs={'alpha':0.2}
                      )

-> train 데이터에 하이라이트 해서 전체 데이터 확인

features = iris.data
labels = iris.target

X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2,
                                                    stratify=labels, random_state=13)
iris_tree = DecisionTreeClassifier(max_depth=2, random_state=13)
iris_tree.fit(X_train, y_train)

-> feature 4개 전부 사용해서 훈련시키기

test_data = np.array([[4.3, 2., 1.2, 1.]])
iris_tree.predict(test_data)

-> 새로운 데이터로 결과 predict 시켜보기 (추론)

iris_tree.predict_proba(test_data)

-> 새로운 데이터의 predict 확률 확인

iris.target_names[iris_tree.predict(test_data)]

-> 이렇게 코드를 작성하면 결과를 숫자가 아닌 이름으로 바로 확인 가능

iris_tree.feature_importances_

-> feature를 구분할 때의 각 기준에 대한 중요도 확인 가능

dict(zip(iris.feature_names, iris_tree.feature_importances_))

-> 깔끔하게 중요도 결과 확인 가능


  1. zip과 언패킹
  • 리스트를 튜플로 zip
list1 = ['a', 'b', 'c']
list2 = [1, 2, 3]

pairs = [pair for pair in zip(list1, list2)]
pairs
  • 결과값
[('a', 1), ('b', 2), ('c', 3)]
  • 튜플을 딕셔너리로 만들기
dict(pairs)
  • 한번에 하기
dict(zip(list1, list2))
  • 언패킹
x, y = zip(*pairs)

-> 결과값이 튜플로 나옴

list(x)
list(y)

-> 튜플을 리스트로 바꿈


  1. 타이타닉 생존자 분석
  • 데이터 가져오기
import pandas as pd

titanic_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial' + \
                    '/master/dataset/titanic.xls'

titanic = pd.read_excel(titanic_url)
titanic.head()
  • 생존자 비율, 명 수(count) 구하기
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

f, ax = plt.subplots(1, 2, figsize=(18, 8))  # 그래프 두개를 연달아 그리기

titanic['survived'].value_counts().plot.pie(ax=ax[0], autopct='%1.1f%%',
                                           explode=[0, 0.1], shadow=True)
ax[0].set_title('Pie plot - Survived')
ax[0].set_ylabel('')

sns.countplot(x='survived', data=titanic, ax=ax[1])
ax[1].set_title('Count plot - Survived')

plt.show()

-> ax[0], ax[1]은 그래프의 위치를 정하는 것

  • 성별 별로 생존자수 구하기
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

f, ax = plt.subplots(1, 2, figsize=(18, 8))  # 그래프 두개를 연달아 그리기

titanic['survived'].value_counts().plot.pie(ax=ax[0], autopct='%1.1f%%',
                                           explode=[0, 0.1], shadow=True)
ax[0].set_title('Pie plot - Survived')
ax[0].set_ylabel('')

sns.countplot(x='survived', data=titanic, ax=ax[1])
ax[1].set_title('Count plot - Survived')

plt.show()

-> 남성의 생존율이 낮음을 알 수 있음

  • 배의 객실 등급별 생존자 수 구하기
pd.crosstab(titanic['pclass'], titanic['survived'], margins=True)

# 'margins=True' -> 합계를 볼 수 있음

-> 1등실의 생존율이 가장 높음.
-> 여성의 생존율이 높으니 1등실에 여성이 많이 타고 있었는지 의문

  • 객실 등급별, 성별로 나눠 나이별 분포를 보여주는 그래프
grid = sns.FacetGrid(titanic, row='pclass', col='sex', height=4, aspect=2)
grid.map(plt.hist, 'age', alpha=.8, bins=20)
grid.add_legend();

-> 3등실에는 특히 20대 남성이 엄청 많았음

  • 나이별 승객 현황 확인하기
import plotly.express as px

fig = px.histogram(titanic, x='age')
fig.show()

-> 아이들과 20-30대가 많았음

  • 등실별 생존율을 연령별로 확인하기
grid = sns.FacetGrid(titanic, col='survived', row='pclass', height=4, aspect=2)
grid.map(plt.hist, 'age', alpha=.5, bins=20)
grid.add_legend();

-> 선실 등급이 높으면 생존율이 높음

  • 나이를 5단계로 나누기
titanic['age_cat'] = pd.cut(titanic['age'], bins=[0,7,15,30,60,100],  # 구간을 잡아줌
                            include_lowest=True,
                            labels=['baby', 'teen', 'young', 'adult', 'old'])
titanic.head()
  • 객실등급, 나이, 성별 별로 생존자 수 한번에 보기
plt.figure(figsize=(14,6))

plt.subplot(131)    # 1행 3열 중에서 첫 번째 자리
sns.barplot(x='pclass', y='survived', data=titanic)

plt.subplot(132)    # 1행 3열 중에서 두 번째 자리
sns.barplot(x='age_cat', y='survived', data=titanic)

plt.subplot(133)    # 1행 3열 중에서 세 번째 자리
sns.barplot(x='sex', y='survived', data=titanic)

plt.subplots_adjust(top=1, bottom=0.1, left=0.1, right=1, hspace=0.5, wspace=0.5)

-> 나이가 어리고, 여성이고, 1등실일수록 생존율이 높음

  • 성별, 나이별 생존자 수 비교해서 보기
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14,6))

women = titanic[titanic['sex']=='female']
men = titanic[titanic['sex']=='male']

ax = sns.distplot(women[women['survived']==1]['age'], bins=20,
                  label='survived', ax=axes[0], kde=False)
ax = sns.distplot(women[women['survived']==0]['age'], bins=40,
                  label='not_survived', ax=axes[0], kde=False)
ax.legend(); ax.set_title('Female')

ax = sns.distplot(men[men['survived']==1]['age'], bins=18,
                  label='survived', ax=axes[1], kde=False)
ax = sns.distplot(men[men['survived']==0]['age'], bins=40,
                  label='not_survived', ax=axes[1], kde=False)
ax.legend(); ax.set_title('Male')
  • 탑승자 이름 패턴으로 신분 알아보기
  • 이름에서 호칭을 잘라서 새로운 칼럼에 넣음
import re

title = []
for idx, dataset in titanic.iterrows():
    tmp = dataset['name']
    title.append(re.search('\,\s\w+(\s\w+)?\.', tmp).group()[2:-1])

titanic['title'] = title
titanic.head()
  • 성별별로 호칭 확인
pd.crosstab(titanic['title'], titanic['sex'])
  • 호칭 정리하기
titanic['title'] = titanic['title'].replace('Mlle', 'Miss')
titanic['title'] = titanic['title'].replace('Ms', 'Miss')
titanic['title'] = titanic['title'].replace('Mme', 'Mrs')

Rare_f = ['Dona', 'Dr', 'Lady', 'the Countess']
Rare_m = ['Capt', 'Col', 'Don', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Master']

for each in Rare_f:
    titanic['title'] = titanic['title'].replace(each, 'Rare_f')

for each in Rare_m:
    titanic['title'] = titanic['title'].replace(each, 'Rare_m')

titanic['title'].unique()
  • 호칭별 생존율 확인하기
titanic[['title', 'survived']].groupby(['title'], as_index=False).mean()

-> 귀족 남성은 평민 여성보다 생존율이 낮음
-> 평민남성 < 귀족남성 < 평민여성 < 귀족여성 순으로 생존율이 낮음


  1. 머신러닝을 이용한 타이타닉 생존자 예측
  • 머신러닝을 위해 몇몇 컬럼은 숫자로 변경
from sklearn.preprocessing import LabelEncoder   # 문자열을 숫자로 변경해주는 기능

le = LabelEncoder()
le.fit(titanic['sex'])
titanic['gender'] = le.transform(titanic['sex'])
titanic.head()
  • 결측치는 포기함(notnull인 것만 가져옴)
titanic = titanic[titanic['age'].notnull()]
titanic = titanic[titanic['fare'].notnull()]
titanic.info()
  • 특성을 선택하고, 데이터를 나눔(train, test)
from sklearn.model_selection import train_test_split

X = titanic[['pclass', 'age', 'sibsp', 'parch', 'fare', 'gender']]
y = titanic['survived']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=13)
  • 훈련시키고, 테스트함
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

dt = DecisionTreeClassifier(max_depth=4, random_state=13)
dt.fit(X_train, y_train)
pred = dt.predict(X_test)
print(accuracy_score(y_test, pred))
  • 디카프리오의 생존율 예측하기
# ['pclass', 'age', 'sibsp', 'parch', 'fare', 'gender']

import numpy as np

dicaprio = np.array([[3, 18, 0, 0, 5, 1]])
print('Dicaprio : ', dt.predict_proba(dicaprio)[0,1])

-> Dicaprio : 0.22950819672131148

  • 윈슬릿의 생존율 예측하기
winslet = np.array([[1, 16, 1, 1, 100, 0]])
print('Winslet : ', dt.predict_proba(winslet)[0,1])

-> Winslet : 1.0


  1. 기억해 둘 것
  • 마크다운 활용하기

  • 파이썬은 읽기 쉬운 언어

  • 언어가 빠르게 바뀌기 때문에 하나하나에 집중하지 말고 전체 흐름에 집중할 것

<제로베이스 데이터 취업 스쿨>

0개의 댓글