본 글은 책 <머신러닝 교과서 파이토치편, 세바스찬 라시카, 유시 (헤이든) 류, 바히드 마지리리 지음, 박해선 옮김, 길벗>, K-MOOC 강의 <모두를 위한 머신러닝, 공성근, 세종대학교, 2024-1학기>, 세종대학교 강의 <기계학습, 권순일, 세종대학교, 2024-1학기> 내용과 추가 자료를 바탕으로 재구성하였습니다.
일반적으로 우리가 머신러닝을 정의할 때, 우리는 기계를 <학습>시킨다 한다. 그럼 <학습> 이란 무엇일까?
학습 <표준국어대사전>
- 배워서 익힘
- 경험의 결과로 나타나는, 비교적 지속적인 행동의 변화나 그 잠재력의 변화. 또는 지식을 습득하는 과정
표준국어대사전에 등제된 학습의 정의를 보자. 위의 정의를 보면 <학습>이란 경험의 결과로서 나타나는 결과라고 볼 수 있다.
우리는 어렸을 때 부터 다양한 경험을 통해 지식을 습득한다. 어린아이가 글자를 배우는 과정을 보자. 아이에게 글자를 가리키기 위해서는 우리는 '가', '나', '다' 가 각각 어떻게 다른지 보여주고 설명해야 한다. 예를 들어 '나'의 특징은 '가'에서 'ㄱ'을 돌리면 나온다는 것 처럼 말이다. 이 방법을 계속 반복하다보면 언젠가 아이는 글자를 다 익힐 수 있다.
위와 같은 방법을 지식기반 방식(규칙기반의 방법, Rule Based)이라고 한다. 초창기에는 컴퓨터를 학습시키기 위한 방법으로 이 방식이 주를 이루었다.
예를 들어 컴퓨터에게 이미지를 통해 단추임을 알아내는 알고리즘을 만든다고 해보자. 단추의 형태는 일반적으로 "가운데 구멍이 몇 개 있는 물체" 라고 규정 할 수 있다. 그러나 이를 기반으로 실제 단추를 구분하다보면 문제가 발생한다.
위와 같이 단추와 숫자가 있을 때, 이 둘은 모두 단추의 특징인 "가운데 구멍이 몇 개 있는 물체"에 해당한다. 때문에 이 둘을 해당 특징으로 구분할 수 없다. 사람은 변화가 심한 장면을 아주 쉽게 인식할 수 있지만, 컴퓨터는 그럴 수 없는것이다.
지식기반의 학습은 사람과는 달리 컴퓨터에게는 매우 힘든 방식이다. 이 문제를 해결할 수 있는 방법 중 하나가 기계 학습이다.
Machine Learning(ML: 머신러닝, 기계학습)은 인간이 경험으로부터 새로운 것을 학습하고 점차 정확도를 향상시켜 나가는 방식을 컴퓨터에 구현하는 알고리즘이다. 다만 지식기반의 학습처럼 사람이 데이터의 특징을 구분하여 입력하는 것과는 다르게 데이터를 기반으로 알고리즘이 스스로 개선된다는 점이 다르다. 머신러닝의 가장 대표적인 예시인 아이리스 분류 문제를 보자.

위와 같이 세 가지 아이리스를 분류하는 문제가 있다. 기존 지식기반 학습으로 아이리스를 구분하기 위해서는 아이리스의 특징을 사람이 구분하고 그를 기반으로 알고리즘을 작성해야 한다. 그러나 이는 굉장히 시간과 비용이 많이 드는 작업이다.
반면 머신러닝 알고리즘을 사용하면 다음과 같이 구분할 수 있다. 먼저 아이리스의 꽃받침 길이와 꽃잎 길이를 추출한다. 이들 데이터를 아래와 같이 평면 그래프 상에 두면 Setosa와 Versicolor가 특정 경계를 두고 구분된다는 것을 알 수 있다. 머신러닝은 이러한 결정경계를 수학적 알고리즘을 통해 계산한다.
다만 이러한 데이터 사이의 결정경계를 예측(prediction)하는 것은 쉽지않다. 눈대중으로 봤을 때 위 그래프를 본다면 Setosa와 Versicolor를 구분하는 결정경계를 그리는 것은 어렵지 않다. 그런데 이를 컴퓨터는 어떻게 수행해야 할까?
(아이리스 분류를 수행하는 머신러닝 코드는 아래와 같다. 코드에 대한 자세한 설명은 후에 하겠다.)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
class Perceptron :
""" 퍼셉트론 분류기
매개변수
eta : float
학습률 (0.0과 1.0 사이)
n_iter : int
훈련 데이터셋 반복 횟수 (epoch)
random_state : int
가중치 무작위 초기화를 위한 난수 생성기 시드
속성
w_ : 1d-array
학습된 가중치
b_ : 스칼라
학습된 bias 유닛
errors_ : list
에포크마다 누적된 분류 오류
"""
def __init__(self, eta=0.01, n_iter=50, random_state=1) :
self.eta = eta
self.n_iter = n_iter
self.random_state = random_state
def fit(self, X, y) :
""" 훈련 데이터 학습
매개 변수
X : {array-like}, shape = [n_samples, n_features]
n_samples개의 샘플과 n_features개의 특성으로 이루어진 훈련 데이터
y : array-like, shape = [n_samples]
타깃 값
반환값
self : object
"""
rgen = np.random.RandomState(self.random_state)
self.w_ = rgen.normal(loc=0.0, scale=0.01, size=X.shape[1])
self.b_ = np.float_(0.)
self.errors_ = []
for _ in range(self.n_iter) :
errors = 0
for xi, target in zip(X, y) :
update = self.eta * (target - self.predict(xi))
self.w_ += update * xi
self.b_ += update
errors += int(update!= 0.0)
self.errors_.append(errors)
return self
def net_input(self, X) :
"""입력 계산"""
return np.dot(X, self.w_) + self.b_
def predict(self, X) :
""" 계단 함수를 사용하여 클래스 레이블을 반환한다. """
return np.where(self.net_input(X) >= 0.0, 1, 0)
def plot_decision_regions(X, y, classifier, resolution=0.02) :
# 마커와 컬러맵을 설정
markers = ('o', 's', '^', 'v', '<')
colors = ['red', 'blue', 'lightgreen', 'gray', 'cyan']
cmap = ListedColormap(colors[:len(np.unique(y))])
# 결정경계를 그림
x1_min, x1_max = X[:, 0].min() -1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() -1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
lab = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
lab = lab.reshape(xx1.shape)
plt.contourf(xx1, xx2, lab, alpha=0.3, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# 샘플의 산전도를 그림
for idx, cl in enumerate(np.unique(y)) :
plt.scatter(x=X[y == cl, 0],
y=X[y == cl, 1],
alpha=0.8,
c=colors[idx],
marker=markers[idx],
label=f'Class {cl}',
edgecolor='black')
if __name__ == "__main__" :
# dataloader
s = 'https://archive.ics.uci.edu/ml/'\
'machine-learning-databases/iris/iris.data'
print('URL:', s)
df = pd.read_csv(s, header=None, encoding='utf-8')
df.tail()
# setosa와 versicolor를 선택한다.
y = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', 0, 1)
# 꽃받침 길이와 꽃잎 길이를 추출
X = df.iloc[0:100, [0, 2]].values
# 산점도를 그린다.
plt.scatter(X[:50, 0], X[:50, 1],
color='red', marker='o', label='Setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
color='blue', marker='s', label='Versicolor')
plt.xlabel('Sepal length [cm]')
plt.ylabel('Petal length [cm]')
plt.legend(loc='upper left')
plt.show()
# perceptron 생성
ppn = Perceptron(eta=0.1, n_iter=10)
# perceptron 훈련
ppn.fit(X, y)
# visualization
plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Number of updates')
plt.show()
# 결정경계 시각화
plot_decision_regions(X=X, y=y, classifier=ppn)
plt.xlabel('Sepal length [cm]')
plt.ylabel('Petal length [cm]')
plt.legend(loc='upper left')
plt.show()
print(ppn.errors_)
좀 더 쉬운 예로 살펴보자. 다음과 같이 Engine Power(hp)와 Car Price($)가 일정한 관계를 갖고 있다고 가정해보자. 이때 우리는 Engine Power가 100일 때 Car Price를 알고 싶다. 이를 알 수 있는 가장 쉬운 방법은 그래프 상의 점들을 지나는 단순 선형 함수를 그리면 된다. 이 함수를 예측 함수라고 부른다.

이때 를 예측함수(predict function, hypothesis: 가설함수), 를 가중치(weight), 를 편향(bias)이라고 부른다. 우리는 머신러닝을 통해 가장 적절한 예측함수 를 찾기위해 학습 데이터에 가장 알맞은 가중치 와 편향 를 찾아야 한다. 이를 위해 기계학습에서는 훈련 데이터를 통해 예측함수를 실제값과 오차를 줄여나가는 방향으로 개선해간다. 이때 사용하는 것이 목적 함수(Objective function, Cost function: 비용함수)이다.
목적 함수란 예측함수의 출력 과 실제 값 의 오차를 계산하는 함수로 실제 값과 가장 오차가 작은 예측함수를 찾기위해 사용한다. 일반적으로 평균제곱오차(Mean Squared Error: MSE)를 많이 사용한다.
평균제곱오차(MSE)에서 는 데이터로부터 예측한 예측함수 값과 실제 값의 오차를 의미한다. 이를 제곱함으로서 가 음수가 되지 않도록 한 후, 모든 데이터에 대한 예측 값-실제값의 제곱오차를 더한다. 이후 데이터의 개수로 나누면 전체 데이터에 대한 예측 값과 실제 값 사이의 제곱오차 평균을 알 수 있다.
처음 학습 데이터를 이용해 학습을 할 때는 가중치 와 편향 를 난수로 설정한다. 이 후 목적함수가 작아지는 방향으로 예측함수를 개선한다. 앞선 Engine power-Car price 관계를 통해 예측함수를 구해보자.
먼저 는 Engine power, 는 Car price라고 할 때, 훈련집합 , 의 값과 예측함수, 목적함수는 다음과 같다.
예측함수의 최적 매개변수 값을 바로 알 수 없으므로 난수 로 설정한다. 여기서 는 전치(Tranpose)를 의미하며, 행렬인 를 열벡터로 표현한 것이다.
라고 했을 때, 목적 함수의 값은 약 406,786으로 매우 큰 것을 알 수 있다. 이번에는 이라고 했을 때의 목적함수를 구해보자.
으로 예측함수를 사용하였을 때 보다 목적함수의 값이 감소한 것을 알 수 있다. 따라서 가 보다 더 적합한 예측함수라고 할 수 있다.
이번에는 이라고 했을 때의 목적함수를 구해보자.
으로 예측함수를 사용하였을 때 보다 목적함수의 값이 감소한 것을 알 수 있다. 따라서 가 보다 더 적합한 예측함수라고 할 수 있다.
이와 같은 방식으로 계속해서 와 값을 수정하다보면 어느 순간 더이상 목적함수의 값이 감소하지 않는 순간이 온다. 이때의 를 최적해 라고 한다. 이를 공식화하면 다음과 같다.
이를 알고리즘 형식으로 쓰면 다음과 같다.
입력: 훈련집합 와
출력: 최적의 매개변수난수를 생성하여 초기 해 θ_1을 설정한다. t=1 while(J(θ)가 0.0에 충분히 가깝지 않음) //수렴 여부 검사 J(θ_t)가 작아지는 방향 ∆θ_t θ_t+1 = θ_t + ∆θ_t t = t + 1 hat(θ) = θ_t
이처럼 머신러닝은 작은 개선을 반복하여 최적해를 찾아가는 수치적 방법임을 알 수 있다.
필자는 기계학습을 공부하는 학부생일뿐이므로 잘못된 정보가 있을 수 있습니다. 본 글에 잘못된 정보, 추가 정보, 오타 등이 있으면 댓글로 달아주세요.