파이썬을 사용하면서 계속 머신 러닝 학습에 도전하고 있습니다. 작년 초만 해도 많이 어렵게 느껴졌는데 최근에는 많이 쉬워지고 있습니다. 한빛 미디어에서 번역서로 나온 "Introduction to Machine Learning with Python"이라는 책이 제가 보기에는 가장 쉽네요. 공부하고 싶은 분들은 한번 도전해 보시면 좋을 것 같습니다.
데이터 과학이나 머신러닝을 공부할 때 통계에 관련된 용어들을 미리 학습하면 도움이 됩니다. 같이 있는 강사님들 중에 통계책을 추천하신 분들이 있어서 2권 추가 합니다.
[세상에서 가장 재미있는 통계학] (http://www.yes24.com/24/goods/2509960?scode=032&OzSrank=1)
혹시 파이썬이 처음이라면 미리 공부해야 할 내용들이 조금 있습니다.
첫번째: 파이썬을 공부하기 쉬운 사이트를 하나 알려드립니다. 아래의 사이트에서 파이썬 언어의 기본 문법을 공부하면 됩니다.
위키 독스의 점프투파이썬
두번째: Pandas나 NumPy에 대한 기본 지식을 가지고 있어야 머신 러닝의 기초 데이터를 이해할 수 있습니다.
파이썬 문법이 어느정도 눈에 익으면 보면 좋은 중급책입니다.
"엔지니어를 위한 파이썬"(제이법, 나카쿠키 켄지 지음)
엔지니어를 위한 파이썬
세번째: 머신러닝에 학습을 시작할 수 있는 단계입니다. 두권을 추천 드립니다.
"파이썬을 이용한 머신러닝, 딥러닝 실전 개발 입문"(위키북스, 쿠지라 히코우즈쿠애 지음, 윤인성 옮김)
파이썬을 이용한 머신러닝, 딥러닝 실전 개발 입문
그리고 제가 최근에 보고 있는 "Introduction to Machine Learning with Python"을 추천합니다.
Introduction to Machine Learning with Python
아래의 내용은 저도 공부하면서 써머리한 내용입니다. ^^
기본 개발툴은 아나콘다에서 배포하는 패키지를 사용하면 전부 설치 됩니다. 머신러닝에서 가장 사용하기 쉽다는 사이킷런을 사용하면 됩니다. 이 책의 저자인 안드레아스 뮐러, 세라 가이도가 만든 mglearn의 경우 파이썬의 pip명령으로 설치하거나 깃허브에서 소스를 받아서 파이썬 아래의 site-packages폴더에 복사해서 사용해도 됩니다.
머신러닝은 데이터에서 지식을 추출하는 작업입니다. 머신러닝은 통계학, 인공지능 그리고 예측분석으로도 불립니다. 페이스북, 아마존, 넷플릭스와 같은 복잡한 웹사이트들은 여러가지 머신러닝 모델을 사용해서 구성 요소의 대부분을 만들고 있습니다.
왜 머신러닝인가?
지도학습과 비지도학습
편지 봉투에 손으로 쓴 우편번호 숫자 인식
의료 영상이미지에 기반한 종양 판단
의심되는 신용카드 거래 감지
불로그 글의 주제 구분
고객들의 취향이 비슷한 그룹으로 묶기
비정상적인 웹사이트 접근 탐지
머신러닝에서는 하나의 객체 또는 행을 샘플(sample) 또는 데이터 포인트(data point)라고 합니다. 그리고 샘플의 속성 즉 열을 특성(attribute)라고 합니다. 좋은 입력 데이터를 만들어내는 특성 추출(feature extraction) 혹은 특성 공학(feature engineering)이라는 주제도 다루게 됩니다. 유념해야 할 것은 어떤 머신러닝 알고리즘도 아무런 정보가 없는 데이터로는 그 어떤 것도 예측할 수 없다는 사실입니다.
설치는 아래와 같이 합니다.
pip install numpy scipy matplotlib ipython scikit-learn pandas pillow
scikit-learn에서 NumPy배열은 기본 데이터 구조입니다. NumPy의 핵심은 다차원 배열인 ndarray클래스입니다. 이 배열의 모든 원소는 동일한 데이터 타입이어야 합니다.
import numpy as np
x = np.array([[1,2,3], [4,5,6]])
print("x:\n{}".format(x))
x:
[[1 2 3]
[4 5 6]]
SciPy는 과학 계산용 함수를 모아놓은 파이썬 패키지입니다. 고성능 선형 대수, 함수 최적화, 신호 처리, 특수한 수학함수와 통계 분포 등을 포함한 많은 기능을 제공합니다. 가장 중요한 기능은 scipy.sparse입니다. 이 모듈은 scikit-learn에서 데이터를 표현하는 또 하나의 방법인 희소 행렬 기능을 제공합니다. 희소 행렬(sparse matrix)는 0을 많이 포함한 2차원 배열을 저장할 때 사용합니다.
from scipy import sparse
#대각선 원소는 1이고 나머지는 0인 2차원 NumPy배열을 만듭니다.
eye = np.eye(4)
print("NumPy 배열:\n{}".format(eye))
NumPy 배열:
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. 1. 0.]
[ 0. 0. 0. 1.]]
#NumPy배열을 CSR포맷의 SciPy희소 행렬로 변환합니다.
#0이 아닌 원소만 저장됩니다.
sparse_matrix = sparse.csr_matrix(eye)
print("SciPy의 CSR행렬:\n{}".format(sparse_matrix))
SciPy의 CSR행렬:
(0, 0) 1.0
(1, 1) 1.0
(2, 2) 1.0
(3, 3) 1.0
data = np.ones(4)
row_indices = np.arange(4)
col_indices = np.arange(4)
eye_coo = sparse.coo_matrix((data, (row_indices, col_indices)))
print("COO 표현:\n{}".format(eye_coo))
COO 표현:
(0, 0) 1.0
(1, 1) 1.0
(2, 2) 1.0
(3, 3) 1.0
matplotlib
사인 곡선 출력
%matplotlib inline
import matplotlib.pyplot as plt
#-10에서 10까지 100개의 간격으로 나뉜 배열을 생성한다.
x = np.linspace(-10, 10, 100)
#사인 함수를 사용해서 y배열을 생성한다.
y = np.sin(x)
plt.plot(x, y, marker="x")
pandas
데이터의 처리와 분석을 위한 파이썬 라이브러리입니다. R의 data.frame을 비슷하게 만든 DataFrame이라는 데이터 구조를 기반으로 만들어졌습니다. 엑셀의 스프레트시트와 비슷한 테이블 형태라고 할 수 있습니다. SQL처럼 테이블에 쿼리나 조인을 수행할 수 있습니다. 전체 배열의 원소가 동일한 타입이어야 하는 NumPy와는 달리 pandas는 각 열의 타입이 달라도 됩니다.
import pandas as pd
data = {'Name':["John","Anna","Peter","Linda"], "Location":["New York","Paris","Berlin","London"], "Age":[24,13,53,33]}
data_pandas = pd.DataFrame(data)
display(data_pandas)
Age Location Name
0 24 New York John
1 13 Paris Anna
2 53 Berlin Peter
3 33 London Linda
#Age열의 값이 30이상인 모든 행을 선택한다.
display(data_pandas[data_pandas.Age > 30])
Age Location Name
2 53 Berlin Peter
3 33 London Linda
mglearn
이 라이브러리는 그래프나 데이터 적재와 관련된 세세한 코드를 일일이 쓰지 않아도 되게끔 이 책을 위해 만든 유틸리티 함수들입니다. pip install mglearn명령으로 설치하면 됩니다.
예제들을 따라할 경우 아래와 같이 임포트해야 합니다.
from IPython.display import display
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn
붓꼿의 품종 분류
한 아마추어 식물학자가 들에서 발견한 붓꽃의 품종을 알고 싶다고 가정합니다. 이 식물학자는 붓꼿의 꽃잎(petal)과 꽃받침(sepal)의 폭과 길이를 센티미터 단위로 측정했습니다. 또 전문 식물학자가 setosa, versicolor, virginica종으로 분류한 붓꼿의 측정 데이터도 가지고 있습니다. 이 측정값을 사용해서 앞에서 채집한 붓꽃이 어떤 품종인지 구분하려고 합니다.
이 경우 붓꽃의 품종을 정확하게 분류한 데이터를 가지고 있으므로 지도 학습에 해당합니다. 이 예는 분류(Classification)문제에 해당합니다. 출력될 수 있는 값(붓꽃의 종류)들을 클래스(Class)라고 합니다. 데이터셋에 있는 붓꽃 데이터는 모두 세 클래스 중 하나에 속합니다.
데이터 포인트 하나(붓꽃 하나)에 대한 기대 출력은 꽃의 품종이 됩니다. 이런 특정 데이터 포인트에 대한 출력 즉 품종을 레이블(label)이라고 합니다.
아래와 같이 데이터를 로딩합니다.
from sklearn.datasets import load_iris
iris_dataset = load_iris()
print("iris_dataset의 키: \n{}".format(iris_dataset.keys()))
iris_dataset의 키:
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])
#(간단한 설명을 193자만 읽어온다.)
print(iris_dataset['DESCR'][:193] + '\n...')
Iris Plants Database
====================
Notes
-----
Data Set Characteristics:
:Number of Instances: 150 (50 in each of three classes)
:Number of Attributes: 4 numeric, predictive att
...
print('타깃의 이름: {}'.format(iris_dataset['target_names']))
타깃의 이름: ['setosa' 'versicolor' 'virginica']
print("특성의 이름: \n{}".format(iris_dataset['feature_names']))
특성의 이름:
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
print("data의 타입: {}".format(type(iris_dataset['data'])))
data의 타입: <class 'numpy.ndarray'>
print("data의 크기: {}".format(iris_dataset['data'].shape))
data의 크기: (150, 4)
#(이 데이터로부터 다섯 붓꽃의 꽃잎 폭은 모두 0.2cm이고 첫번째 꽃이 가장 긴 5.1센치의 꽃밭침을 가졌을 알 수 #있다. )
print("data의 처음 다섯 행:\n{}".format(iris_dataset['data'][:5]))
data의 처음 다섯 행:
[[ 5.1 3.5 1.4 0.2]
[ 4.9 3. 1.4 0.2]
[ 4.7 3.2 1.3 0.2]
[ 4.6 3.1 1.5 0.2]
[ 5. 3.6 1.4 0.2]]
#(target배열도 샘플 붓꽃의 품종을 담은 NumPy배열이다.)
print("target의 타입: {}".format(type(iris_dataset['target'])))
target의 타입: <class 'numpy.ndarray'>
#(target은 각 원소가 붓꽃 하나에 해당하는 1차원 배열이다.)
print("target의 크기: {}".format(iris_dataset['target'].shape))
target의 크기: (150,)
#(붓꽃의 종류는 0에서 2까지 정수로 기록되어 있다. 숫자의 의미는 iris[‘target_names’]배열에서 확인할 수 있다. #0은 setosa, 1은 versicolor, 2는 virginica이다.)
print("타깃:\n{}".format(iris_dataset['target']))
타깃:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
성과 측정:훈련 데이터와 테스트 데이터
이 데이터로 머신러닝 모델을 만들고 새로운 데이터의 품종을 예측하려고 합니다. 불행히도 모델을 만들 때 쓴 데이터는 평가 목적으로 사용할 수 없습니다. 모델이 훈련 데이터를 그냥 전부 기억할 수 있으니 훈련 데이터에 속한 어떤 데이터라도 정확히 맞출 수 있기 때문입니다. 이렇게 데이터를 기억한다는 것은 모델을 잘 일반화하지 않았다는 뜻입니다.
우리가 가지고 있는 레이블된 데이터(150개의 붓꽃 데이터)를 두 그룹으로 나눕니다. 이중에 하나는 머신러닝 모델을 만들 때 사용하며 훈련 데이터 혹은 훈련 세트(training set)이라고 합니다. 나머지는 모델이 얼마나 잘 작동하는지 측정하는데 사용하며 이를 테스트 데이터, 테스트 세트(test set)또는 홀드아웃 세트(hold-out set)라고 부릅니다.
scikit-learn은 데이터셋을 섞어서 나누어주는 train_test_split함수를 제공합니다. 이 함수는 전체의 75%를 레이블 데이터와 함께 훈련 세트로 뽑습니다. 나머지 25%는 레이블 데이터와 함께 테스트 세트가 됩니다. 전체의 25%를 테스트 세트로 사용하는 것은 일반적으로 좋은 선택입니다.
scikit-learn에서 데이터는 대문자 X로 표시하고 레이블은 소문자 y로 표기합니다.
train_test_split함수로 데이터를 나누기 전에 유사 난수 생성기를 사용해 데이터셋을 무작위로 섞어야 합니다. 만약 섞지 않은 상태로 데이터셋 뒤쪽의 25%를 테스트 세트로 이용하면 그 데이터의 레이블은 모두 2가 됩니다. 데이터 포인트가 레이블 순서대로 정렬되어 있기 때문입니다. 세 클래스가 모두 포함되도록 해야 합니다.
이 함수를 여러 번 실행해도 결과가 똑같이 나오도록 유사 난수 생성기에 넣을 난수 초깃값을 random_state매개변수로 전달합니다. 이렇게 하면 이 코드는 항상 같은 결과를 출력합니다.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], random_state=0)
train_test_split함수의 반환값은 X_train, X_test, y_train, y_test이며 모두 NumPy배열입니다. X_train은 전체 데이터셋의 75%를, X_test는 나머지 25%를 담고 있습니다.
print("X_train 크기: {}".format(X_train.shape))
X_train 크기: (112, 4)
print("y_train 크기: {}".format(y_train.shape))
y_train 크기: (112,)
print("X_test 크기: {}".format(X_test.shape))
X_test 크기: (38, 4)
print("y_test 크기: {}".format(y_test.shape))
y_test 크기: (38,)
데이터 살펴보기
시각화는 데이터를 조사하는 아주 좋은 방법입니다. 산점도(scatter plot)가 그 중에 하나입니다. 산점도는 데이터에서 한 특성을 x축에 놓고 다른 하나는 y축에 놓아 각 데이터 포인트를 하나의 점으로 나타내는 그래프입니다.
아래의 코드를 실행하려면 mglearn이 필요합니다. 윈도우에서 커맨드창을 오픈해서
pip3 install mglearn을 실행합니다. (이 부분은 테스트가 필요. 맥에서는 설치가 잘 됨. 윈도우에서는 설치되었는데 없다고 나옴. 18/2/26일 체크. 해결 직접 download를 받아서 mglearn을 site-packages폴더에 복사해 주면 된다.)
첫번째 머신러닝 모델:k-최근접 이웃 알고리즘
새로운 데이터 포인트에 대한 예측이 필요하면 알고리즘은 새 데이터 포인트에서 가장 가까운 훈련 데이터 포인트를 찾습니다. 그런 다음 찾은 훈련 데이터의 레이블을 새 데이터 포인트의 레이블로 지정합니다.
k-최근접 이웃 알고리즘에서 k는 가장 가까운 이웃 ‘하나’가 아니라 훈련 데이터에서 새로운 데이터 포인트에 가장 가까운 ‘k개’의 이웃을 찾는다는 뜻입니다.
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)
knn객체는 훈련 데이터로 모델을 만들고 새로운 데이터 포인트에 대해 예측하는 알고리즘을 캡슐화 한 것입니다. KNeighborsClassifier의 경우 훈련 데이터 자체를 저장하고 있습니다.
fit메서드는 knn객체 자체를 반환합니다. 그리고 knn객체 자체를 변경시킵니다. 그래서 knn객체가 문자열 형태로 출력됩니다. 거의 모든 매개변수가 기본값이고 n_neighbors=1은 우리가 지정한 값입니다.
knn.fit(X_train, y_train)
Out[9]:
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=1, p=2,
weights='uniform')
예측하기
이제 이 모델을 사용해서 정확한 레이블을 모르는 새 데이터에 대해 예측을 만들 수 있습니다. 야생에서 꽃받침의 길이가 5센치미터, 폭이 2.9cm이고 꽃잎의 길이가 1cm, 폭이 0.2cm인 붓꽃을 보았다고 가정합시다. 샘플의 수(1)에 특성의 수(4)를 곱한 크기의 NumPy배열로 만들어 봅니다.
import numpy as np
X_new = np.array([[5, 2.9, 1, 0.2]])
print("X_new.shape: {}".format(X_new.shape))
X_new.shape: (1, 4)
scikit-learn은 항상 데이터가 2차원 배열일 것으로 예상합니다. knn객체의 predict메서드로 예측을 합니다.
prediction = knn.predict(X_new)
print("예측: {}".format(prediction))
예측: [0]
print("에측한 타깃의 이름: {}".format(iris_dataset['target_names'][prediction]))
에측한 타깃의 이름: ['setosa']
모델평가하기
앞에서 만든 테스트 세트를 사용할 때가 왔습니다. 테스트 데이터에 있는 붓꽃의 품종을 예측하고 실제 레이블(품종)과 비교할 수 있습니다.
y_pred = knn.predict(X_test)
print("테스트 세트에 대한 예측값:\n {}".format(y_pred))
테스트 세트에 대한 예측값:
[2 1 0 2 0 2 0 1 1 1 2 1 1 1 1 0 1 1 0 0 2 1 0 0 2 0 0 1 1 0 2 1 0 2 2 1 0
2]
print("테스트 세트의 정확도: {:.2f}".format(np.mean(y_pred == y_test)))
테스트 세트의 정확도: 0.97
knn객체의 score 메서드로 테스트 세트의 정확도를 계산할 수 있습니다. 97%의 정확도
print("테스트 세트의 정확도: {:.2f}".format(knn.score(X_test, y_test)))
테스트 세트의 정확도: 0.97