참고 서적 | Introduction to Machine Learning with Python 파이썬 라이브러리를 활용한 머신러닝[개역개정판] |
텐서 플로우 블로그 (Tensor ≈ Blog)
머신러닝(Machine Learning), 딥러닝(Deep Learning) 그리고 텐서(Tensor) 또 파이썬(Python)
tensorflow.blog
+블로그의 내용을 참조했습니다.
Table of Contents
- 들어가기 전에(용어정리)
- 머신러닝의 정의
- 지도학습의 방법(각각의 장단점)
1. 들어가기 전에...
참고: (이번 포스팅은 참고 서적의 1,2장의 내용입니다.)
용어 정리
1. Data point: (자주 사용되는 말이니 꼭 알아둘 것.)처리되는 하나의 개체, 또는 sample을 가리키는 말이다. point로 불리는 이유는 산점도(scatter diagram) 위에 각 개체를 점으로 나타내기 때문이다. 산점도가 아니라 행렬로 나타낼 경우, 한 '행'(가로)이 이에 해당한다. 반대로 한 '열'(세로)은 특성이라고 한다. Data 자체를 엑셀 테이블로 생각했을 때, 가로줄(행)에는 여러 개체들의 값이 나열된다. 이를테면 개개의 이메일, 고객, 거래 등. 반면 엑셀 테이블 상 열에는 데이터를 구성하는 열에는 각 속성(고객의 나이나 거래 가격, 지역 등)이 나열된다.
2. 지도학습, 비지도학습, 강화학습:
서론: 머신러닝이 본격적으로 연구되기 전, 지능형 application들은 사용자의 입력을 다룰 때, if와 else의 조건을 나누어 각 조건에 따라 다른 행동을 취하는 하드코딩 방식을 따랐다. 이 방식에는 두가지 단점이 존재했는데, 새로운 변수 하나만 생겨도 전체 시스템을 다시 설정해야 한다는 것과 해당 조건에 대한 행동양식의 결과를 매우 잘 알아야 한다는 것이다. 가령 로봇에게 마켓에서 과자를 사오도록 명령했다고 하자. 마켓으로 가는 길에는 수많은 변수가 존재한다. 인간은 모든 변수를 통제할 수 없다. 하늘에서 운석이라도 떨어져봐라. 어떻게 되겠는가? 그리고 로봇은 그 조건에 대한 행동양식을 전혀 알지 못한다. 이에 대한 대책으로 지도학습 개념이 등장했다.
본론:
- 지도학습은 이미 알려진 사례를 바탕으로 일반화된 모델을 만들어 의사 결정 프로세스를 자동화하는 방식을 말한다. 이 방식에서 요구되는 학습은, 입력 데이터로부터 기대한 출력이 도출되도록 하는 것이다. 즉 입력과 출력으로부터 학습한다. 지도 학습의 예시로 의료 영상 이미지에 기반한 종양판단이나 의심되는 신용카드 거래를 감지하는 사례가 있다.
- 비지도 학습은 입력은 주어지지만 출력은 주어지지 않는 경우다. 예를 들어 블로그 글의 카테고리를 구분하는 것, 취향이 비슷한 그룹으로 고객을 묶는 경우 등이 있다.
- 강화학습(reinforcement learning)은 분류할 수 있는 데이터가 존재하지 않고, 데이터가 존재하더라도 정답이 정해져있지 않은 방식이다. 현재 상태를 인식한 에이전트가 자신이 선택할 수 있는 여러 행동들 중에서 보상을 최대화하는 행동이나 순서를 결정하는 방식으로 알려져있다. 로봇공학이나 자율주행, 스케줄링 문제에서 활용되고 있다. 가령 자율주행 문제에서 신경망은 운전자(에이전트)를 대신해 자신의 상황을 파악하고(여러 측정값) 최적의 보상이 보장되는 행동을 선택한다.
3. 산점도: 데이터의 한 특성을 x축에 놓고 다른 특성을 y축에 놓아 각 데이터 포인트를 하나의 점으로 나타내는 그래프.
3. 산점도: 데이터의 한 특성을 x축에 놓고 다른 특성을 y축에 놓아 각 데이터 포인트를 하나의 점으로 나타내는 그래프.
2차원 뿐만 아니라 3차원 이상의 공간으로 확장 가능하다. 물론 4차원 이상은 그림으로 나타낼 수 없다.
4. 클래스: 붓꽃 사진이 주어지고 그 붓꽃의 품종이 (setosa, versicolor, virginica) 중에 무엇인지 판별하는 머신러닝 application이 있다고 했을 때, 주어진 3개의 품종 각각을 클래스라고 한다.
5. 레이블: 위에서 언급한 개별 붓꽃의 품종을 레이블이라고 한다. 4번과 5번을 비교해보면, 클래스는 단 3개로 정리되지만 레이블은 3개로 정리할 수 없고, 각 개체에 대응되는 하나의 값으로 생각해야 한다.
6. 과소적합(underfitting) , 과대적합(overfitting): 꼭 기억해야 하는 용어.
1) underfitting은 너무 간단한 모델을 채택한 경우로, training set와 test set모두에서 정확도가 떨어지는 상황을 말한다.
이 모델은 너무 상세하고 너무 적은 데이터에 의존하기 때문에 문제가 된다. (너무 구체적인 기준으로 데이터를 판단한다.)
2) overfitting은 너무 복잡한 모델을 채택한 경우로, traing set의 정확도는 높지만, test set에서 정확도가 떨어지는 상황이다. 이 모델은 데이터의 면면과 다양성을 잡아내지 못하기 때문에 문제가 된다.(너무 두루뭉술한 기준으로 데이터를 판단한다.)
![]() |
![]() |
2. What is Machine Learning?
'머신 러닝(machine learning)은 경험을 통해 자동으로 개선하는 컴퓨터 알고리즘의 연구'라 한다.
통 뭔소린지 모르겠다. 그래서 좀 더 공신력 있는 브리태니커 백과사전의 정의를 찾아보았다.
machine learning, in artificial intelligence (a subject within computer science),
discipline concerned with the implementation of computer software that can learn autonomously.
Expert systems and data mining programs are the most common applications for improving algorithms through the use of machine learning. Among the most common approaches are the use of artificial neural networks (weighted decision paths) - (aka. 인공신경망) and
genetic algorithms (aka. 유전 알고리즘, symbols “bred” and culled by algorithms to produce successively fitter programs).
간단히 말해서, 컴퓨터가 인간처럼 사고하도록 자동으로 학습하게 만든 인공지능 시스템이다.
흔히 받은 이메일을스팸인지(or not) 확인하는 과정, 종양이 악성인지(or not) 확인하는 과정에서 사용된다.
위키백과에 따르면 기계 학습의 핵심은 표현(representation)과 일반화(generalization)에 있다고 하는데...

1. 표현(representation)과 일반화(generalization)는 무엇인가?
당연한 말이지만, 특정 모델의 성능을 측정하기 위해서는 입력된 데이터에 대한 알맞은 결과값을 출력하는지 테스트하는 과정이 필요하다. 일반적으로 지도학습에 한하여, 머신러닝 모델을 만들 때 사용되는 데이터를 training data(or training set)이라고 하며, 모델의 성능을 측정하기 위해 테스트할 때 사용되는 데이터를 test data(or test set, hold out set)이라고 한다. scikit learn에서 제공하는 train _test_split 함수는 전체 행(샘플들)의 3/4를 training set으로, 1/4를 test set으로 뽑는다. 당연한 말이지만, 뽑기 전에 무작위로 섞는 과정이 선행된다. 이 때, training 하는 과정을 표현(representation)이라고 하고, test하는 과정을 일반화(generalization)한다고 한다.
지도학습에 사용되는 모든 데이터가 표현단계에 이용되면 모델의 성능(보통 정확도(accuracy)로 표현)을 파악할 수 없기에 두 단계로 구분하는 것이다. 일반화가 잘 되었다면 새로운 데이터에 대해서 잘 동작한다는 뜻이다.
2. 지도학습은 어떤 경우에 사용되는가?
- 분류 (Classification)
- 회귀 (Regression)
분류는 두 개 이상의 클래스가 있을 때, 주어진 입력값 데이터 포인트가 어느 클래스에 속하는지 추측하는 과정이다. 이 때, 두 개의 클래스로 분류하는 것을 이진분류, 세 개 이상의 클래스로 분류하는 것을 다중 분류라고 한다.
회귀는 부동소수점수(float)값 또는 어떤 값이 속하는 일정 범위를 추측하는 것을 말한다. 클래스가 이산적으로 구분되는 분류와 다르게 연속적인 값을 찾는다는 점에서 차별된다.
3. 지도학습(Supervised Learning) 방법에는 어떤것들이 있는지 알아보자.
- k-최근접 이웃(k-nearest neighbors),(aka. knn)
- 선형모델(linear model)
- 나이브 베이즈 분류기(naive bayes)
- 결정 트리(decision tree)
- 랜덤 포레스트(random forest) (⊂ 결정트리 앙상블 ensemble)
- 그레이디언트 부스팅 회귀 트리(gradient boosting regression tree) (⊂ 결정트리 앙상블 ensemble)
- 커널 서포트 벡터 머신(kernelized support vector machines)
- 신경망-딥러닝(deep learning by multilayer perceptrons aka.MLP)

아ㅆ... 너무 많다... 다 쓰려면 오늘도 자긴 글렀네
1) k-최근접 이웃
중요한 매개변수: 점 사이의 거리 재는 방법(보통 유클리디안 거리 방식이용),이웃의 개수 k
여기서 k값은 n_neighbors값을 의미한다. 아래 그림에서 추가된 별모양 데이터 포인트가 있을 때, 그 포인트로부터 가장 가까이 있는 최근접 점들 중 선택할 이웃의 수가 바로 k값이다.
![]() |
![]() |
mglearn.plots.plot_knn_classification(n_neighbors=1) # 왼쪽 사진에 해당
mglearn.plots.plot_knn_classification(n_neighbors=3) #오른쪽 사진에 해당
위 그림에서 test 포인트는 별모양으로 표시되어 있다. 만약 k가 2 이상이면, 추가된 test 포인트는 클래스 0에 속하는 이웃과 클래스 1에 속하는 이웃의 수를 비교해 그 수가 더 많은 클래스로 레이블을 지정한다. 따라서 왼쪽과 오른쪽 그림을 비교했을 때, 3개 test 포인트 중 가장 왼쪽 포인트는 레이블이 다르다. 물론 이진분류 체계가 아닌 다중 클래스에서도 이 방식을 똑같이 적용할 수 있다.
아래는 train_test_split 방식에 따라 3/4 는 training set으로, 나머지는 test set으로 사용한 후, k = 3으로 설정해 knn을 이용하는 과정을 나타낸 코드다. 아래 fit()함수는 knn 객체 자체를 반환할 수 있고, 객체를 변형할 수 있는 함수로, training set을 이용해 모델을 구현하기 위해 사용된다.
print 출력하는 부분에서 predict()함수를 통해 X_test라는 입력 test set으로 정확도를 검증한다. 주어진 test set가 7개로 주어진 것으로 보아, training set에는 총 21개의 데이터 포인트가 사용되었다.
두번째 print 출력하는 부분에서 score()함수를 이용하면 X_test 값과 y_test값을 인자로 전달해 정확도를 구할 수 있다. 이 부분을 약간 변형해 print("테스트 세트 정확도: {:.2f}".format(np.mean(y_pred == y_test))) 처럼 나타낼 수 있다. 이는 y_pred의 예측값과 y_test의 실제 결과값을 비교해 평균을 냄으로써 정확도를 구하는 과정을 직관적으로 보여준다.
from sklearn.model_selection import train_test_split
X, y = mglearn.datasets.make_forge()
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train, y_train)
print("테스트 세트 예측: {}".format(clf.predict(X_test)))
print("테스트 세트 정확도: {:.2f}".format(clf.score(X_test, y_test)))
## 테스트 세트 예측: [1 0 1 0 1 0 0]
## 테스트 세트 정확도: 0.86
#random_state??
random_state = 0 , random_state = 42 .. 이런건 다 뭔가..
사실 0,42 같은 상수는 전혀 중요하지 않다. 이 값이 정수인지 None인지의 여부가 중요하다.
random_state가 None이면 데이터를 정말 랜더믹하게 뽑게 되지만, 정수값이면 난수 생성에 특정 규칙이 존재해, 동일한 결과가 출력되는 것을 말한다. 가령 0을 집어넣어 0,2,3..으로 이어진다면 또 다른 상황에서 0을 집어넣었을 때도 0,2,3..의 결과가 나타나는 것이다. 이는 random seed역할을 한다.
KNeighborsClassifier 분석
클래스 0과 클래스 1로 지정한 영역으로 나뉘는 결정 경계decision boundary를 그려본다.
아래 코드를 통해 k값이 증가할 때 나타나는 변화를 관찰한다.
fig, axes = plt.subplots(1, 3, figsize=(10, 3))
for n_neighbors, ax in zip([1, 3, 9], axes): # k값이 1,3,9로 증가
clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(X, y)
mglearn.plots.plot_2d_separator(clf, X, fill=True, eps=0.5,
ax=ax, alpha=.4)
# 결정 경계 구획부, 알파 매개변수는 0.4, fill = True값으로 점들은 산점도 위에서 색깔이 칠해진다.
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
ax.set_title("{} 이웃".format(n_neighbors))
ax.set_xlabel("특성 0")
ax.set_ylabel("특성 1")
axes[0].legend(loc=3)
위 결과를 참고하면 다음과 같이 결론 내릴 수 있다.
1/k ∝fit
앞서 계속 분류를 knn 방식으로 구현하는 방법을 살펴봤는데, 회귀방식도 다를 것 없다. 다만 k가 2 이상일 때 test case의 예측값은 그 이웃들의 평균 타깃값이 된다.
![]() |
![]() |
mglearn.plots.plot_knn_regression(n_neighbors=1)
mglearn.plots.plot_knn_regression(n_neighbors=3)
KNeighborsRegressor분석
(KNeighborsClassifier 코드와 다를 게 1도 없는데, KNeighborsClassifier() 대신 KNeighborsRegressor() 사용해야 한다.)
분류와 회귀에서 결정적으로 다른 값은 모델의 정확도를 구할 때, 회귀는 R^2값을 이용한다는 것이다.
print("테스트 세트 R^2: {:.2f}".format(reg.score(X_test, y_test)))
테스트 세트 R^2: 0.83
아래 그림에서 보듯, R^2은 타깃 값에서 모델의 타깃 예측값을 뺀 값을 타깃 값에서 training 타깃 값의 평균을 뺀 값으로 나눠준 값이다. 타깃값과 모델의 타깃 예측값이 동일한 경우, 주어진 분수의 분자값이 0이 되어 R^2이 1이 된다. 이는 예측이 타깃값과 정확히 맞아떨어졌음을 의미한다. 반대로 R^2값이 0이라는 것은 모델의 타깃 예측값을 training 타깃의 평균 값으로 설정하는 경우를 말한다.
knn 의 장단점
장점 | 단점 | 단점 보완방법 |
이해하기 쉽움 | 1) (training set가 매우 클 경우)예측이 느리고 특성을 처리하는 능력이 떨어짐, 현업에서 잘 쓰지 않음 2) k 매개변수를 잘 조정해야 함. 보통 3-5개에서 잘 작동하는데, 이를 확인해야 하는 번거로움이 있음 |
선형모델 |
2) 선형 모델
예측함수의 형태
ŷ = w[0] × x[0] + w[1] × x[1] + … + w[p] × x[p] + b
W는 가중치 또는 계수라고 하고(coef_속성에 저장되어 있다.), b는 편향 또는 절편이라 한다. (intercept_ 속성에 저장되어 있다. )그리고 이 둘을 아울러 파라미터라 한다. (b 역시 가중치로 보기도 한다.)
mglearn.plots.plot_linear_regression_wave()
출력값
w[0]: 0.393906 b: -0.031804
보다시피 W는 각 특성 X[]에 대한 기울기 역할, b는 절편 역할을 한다. 선형모델은 특성이 하나일 때는 직선, 두개 일 때는 평면이며, 그 이상에서는 hyperplane이라고 불리는 초평면이다.
선형회귀법에는
1) 최소제곱법(ordinary least squares)
2) 리지 회귀(Ridge)
3) 라소 (Lasso)가 있다.
1) 최소제곱법(ordinary least squares)
선형 회귀는 예측과 훈련 세트에 있는 타깃 y 사이의 평균제곱오차mean squared error를 최소화하는 파라미터 w와 b를 찾는다. 평균제곱오차는 예측값과 타깃값의 차이를 제곱하여 더한 후에 샘플의 개수로 나눈다. 이는 마치 분산 공식과 유사하다.(편차제곱의 평균)
이 방식은 가장 간단하기도 하고 오래된 알고리즘이다. 따라서 저차원 data set에서는 모델이 매우 단순해 underfitting 문제가 두드러지게 드러나고, 특성이 늘어난 고차원 data set에서는 overfitting문제가 두드러지게 드러난다. 이를 극복하기 위해 리지 회귀가 사용된다.
2) 리지 회귀(Ridge) & 3) 라소 (Lasso)
2) 리지 회귀에서는 가중치의 절댓값을 가능한 작게 만든다. 이는 모든 특성이 출력에 주는 영향이 줄어들도록 한다. 즉 기울기값을 작게 만드는 역할이다. 이를 계수에 대한 제약(규제)regularization이 늘어난다고 표현한다. 리지 회귀에 사용하는 규제 방식은 L2 규제다. 앞선 평균제곱오차 식에 아래 그림의 식이 추가되어 norm (vector)의 제곱을 페널티로 적용한다.
이 때 위의 수식에서 시그마 앞에 붙는 알파 매개변수 값에 따라 페널티 효과가 달라지는데, 알파 매개변수값이 커질수록, 페널티 효과가 커져 w의 모든 원소가 0에 가까워진다. (계수에 대한 제약이 커진다 = 계수가 0에 가까워진다 = 계수값이 감소한다 = training set의 성능이 나빠지는 대신 일반화하는데 도움을 주어 test set 성능이 개선된다.)
반면 알파 매개변수 값이 작을수록 페널티 효과가 작아져 w의 모든 원소가 0에서 멀어진다.
위의 결론을 아래와 같이 맺겠다.
1 / (α 매개변수) ∝ 계수값 ∝fit
알파 파라미터 값이 고정되고 training data set의 크기가 바뀔 때, LinearRegression과 Ridge 곡선을 비교해보면 다음과 같은 learning curve(학습 곡선이 나타난다.)
이를 통해 알 수 있는 사실:
1. training set R^2 점수
Ridge < Linear Regression
- 이는 리지에 규제가 적용되기 때문이다.
2. test set R^2 점수
Ridge >= Linear Regression
-training set의 크기가 400 후반부일 때 두 점수 값은 같아진다.
3. Linear Regression의 단점이 부각된다. training set의 크기가 커질수록 training set에서 성능이 조금씩 떨어지고 있다.(데이터가 많을수록 과대적합하기 어려워진다는 Linear Regression의 약점이 반영되었다. )
3) 리지 회귀가 w의 원소를 0에 가깝도록 만들었다면 라소 회귀는 몇몇 계수에 대해 0이 되도록 한다. 이는 L2규제가 아니라 L1규제를 따르기 때문이다. 평균제곱오차 식에 아래와 같은 식을 페널티로 추가해 계산한다. 앞선 리지 회귀와 마찬가지로 알파 파라미터의 영향을 받는다.(과정은 동일하니 생략.)
from sklearn.linear_model import Lasso
lasso = Lasso().fit(X_train, y_train)
print("훈련 세트 점수: {:.2f}".format(lasso.score(X_train, y_train)))
print("테스트 세트 점수: {:.2f}".format(lasso.score(X_test, y_test)))
print("사용한 특성의 수: {}".format(np.sum(lasso.coef_ != 0)))
#훈련 세트 점수: 0.29
#테스트 세트 점수: 0.21
#사용한 특성의 수: 4
위 R^2값은 터무니 없이 작다. 이는 underfitting됐다는 사실을 보여주기에, 알파 파라미터를 줄여서 overfitting형태로 변화시켜야 하는데, 당장은 할 수 없다. max_iter(반복 실행 최대 횟수)가 너무 작기 때문이다. 이 값을 일정 수준 이상으로 올려주면 알파 파라미터를 줄여 최적화 fitting을 찾아나설 수 있다. (아래 코드)
# "max_iter" 기본값을 증가시키지 않으면 max_iter 값을 늘리라는 경고가 발생.
lasso001 = Lasso(alpha=0.01, max_iter=100000).fit(X_train, y_train)
print("훈련 세트 점수: {:.2f}".format(lasso001.score(X_train, y_train)))
print("테스트 세트 점수: {:.2f}".format(lasso001.score(X_test, y_test)))
print("사용한 특성의 수: {}".format(np.sum(lasso001.coef_ != 0)))
#훈련 세트 점수: 0.90
#테스트 세트 점수: 0.77
#사용한 특성의 수: 33
이 때, 사용한 특성의 수가 기존의 4에서 33으로 증가한 것을 볼 수 있는데, 이는 알파 파라미터 값이 줄어들어 0에서 멀어지는 계수가 많아졌기 때문으로 해석된다. 즉, 알파 파라미터를 계속 줄이면 사용한 특성의 수는 더욱 많아지게 될 것이다.
lasso00001 = Lasso(alpha=0.0001, max_iter=100000).fit(X_train, y_train)
print("훈련 세트 점수: {:.2f}".format(lasso00001.score(X_train, y_train)))
print("테스트 세트 점수: {:.2f}".format(lasso00001.score(X_test, y_test)))
print("사용한 특성의 수: {}".format(np.sum(lasso00001.coef_ != 0)))
#훈련 세트 점수: 0.95
#테스트 세트 점수: 0.64
#사용한 특성의 수: 94
추가로, 리지 회귀와 라소에서 알파 파라미터 값을 변형시킴에 따라 R^2(정확도)는 어떻게 달라지는지 살펴보자. 이를 통해 최적의 알파 파라미터를 찾을 수 있을 것이다. (알파 파라미터는 보통 로그 스케일로 증가시킨다.) - 아래 reference 블로그 내용 참조
# alpha 값을 0.001에서 100까지 10배씩 늘려가며 릿지 회귀모델 훈련
import matplotlib.pyplot as plt
train_score = []
test_score = []
alpha_list = [0.001, 0.01, 1, 10, 100]
for alpha in alpha_list:
ridge = Ridge(alpha=alpha)
ridge.fit(train_scaled, train_target)
train_score.append(ridge.score(train_scaled, train_target))
test_score.append(ridge.score(test_scaled, test_target))
plt.plot(np.log10(alpha_list), train_score)
plt.plot(np.log10(alpha_list), test_score)
plt.xlabel('alpha')
plt.ylabel('R²')
plt.show()
리지 회귀에서 가장 적절한 알파 파라미터 값은 0.1이다. (두 곡선이 가장 가까이 있는 지점이기 때문.)
from sklearn.linear_model import Lasso
lasso = Lasso()
lasso.fit(train_scaled, train_target)
print(lasso.score(train_scaled, train_target)) # 0.989789897208096
print(lasso.score(test_scaled, test_target)) # 0.9800593698421883
train_score = []
test_score = []
alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
for alpha in alpha_list:
lasso = Lasso(alpha=alpha, max_iter=10000)
lasso.fit(train_scaled, train_target)
train_score.append(lasso.score(train_scaled, train_target))
test_score.append(lasso.score(test_scaled, test_target))
라소 모델에서 발견되는 최적 알파 파라미터 값은 1이다.
쓰다보니 양이 너무 많다... 분류용 선형 모델부터는 다음에..
댓글