[기계학습] 1.1 머신러닝이란?

Serun1017·2024년 3월 19일

기계학습

목록 보기
1/3

본 글은 책 <머신러닝 교과서 파이토치편, 세바스찬 라시카, 유시 (헤이든) 류, 바히드 마지리리 지음, 박해선 옮김, 길벗>, K-MOOC 강의 <모두를 위한 머신러닝, 공성근, 세종대학교, 2024-1학기>, 세종대학교 강의 <기계학습, 권순일, 세종대학교, 2024-1학기> 내용과 추가 자료를 바탕으로 재구성하였습니다.

1.1 머신러닝이란?


학습이란?

일반적으로 우리가 머신러닝을 정의할 때, 우리는 기계를 <학습>시킨다 한다. 그럼 <학습> 이란 무엇일까?

학습 <표준국어대사전>

  1. 배워서 익힘
  2. 경험의 결과로 나타나는, 비교적 지속적인 행동의 변화나 그 잠재력의 변화. 또는 지식을 습득하는 과정

표준국어대사전에 등제된 학습의 정의를 보자. 위의 정의를 보면 <학습>이란 경험의 결과로서 나타나는 결과라고 볼 수 있다.

우리는 어렸을 때 부터 다양한 경험을 통해 지식을 습득한다. 어린아이가 글자를 배우는 과정을 보자. 아이에게 글자를 가리키기 위해서는 우리는 '가', '나', '다' 가 각각 어떻게 다른지 보여주고 설명해야 한다. 예를 들어 '나'의 특징은 '가'에서 'ㄱ'을 돌리면 나온다는 것 처럼 말이다. 이 방법을 계속 반복하다보면 언젠가 아이는 글자를 다 익힐 수 있다.

지식기반의 한계

위와 같은 방법을 지식기반 방식(규칙기반의 방법, Rule Based)이라고 한다. 초창기에는 컴퓨터를 학습시키기 위한 방법으로 이 방식이 주를 이루었다.

예를 들어 컴퓨터에게 이미지를 통해 단추임을 알아내는 알고리즘을 만든다고 해보자. 단추의 형태는 일반적으로 "가운데 구멍이 몇 개 있는 물체" 라고 규정 할 수 있다. 그러나 이를 기반으로 실제 단추를 구분하다보면 문제가 발생한다.

단추 숫자

위와 같이 단추와 숫자가 있을 때, 이 둘은 모두 단추의 특징인 "가운데 구멍이 몇 개 있는 물체"에 해당한다. 때문에 이 둘을 해당 특징으로 구분할 수 없다. 사람은 변화가 심한 장면을 아주 쉽게 인식할 수 있지만, 컴퓨터는 그럴 수 없는것이다.


머신러닝이란?

지식기반의 학습은 사람과는 달리 컴퓨터에게는 매우 힘든 방식이다. 이 문제를 해결할 수 있는 방법 중 하나가 기계 학습이다.

Machine Learning(ML: 머신러닝, 기계학습)은 인간이 경험으로부터 새로운 것을 학습하고 점차 정확도를 향상시켜 나가는 방식을 컴퓨터에 구현하는 알고리즘이다. 다만 지식기반의 학습처럼 사람이 데이터의 특징을 구분하여 입력하는 것과는 다르게 데이터를 기반으로 알고리즘이 스스로 개선된다는 점이 다르다. 머신러닝의 가장 대표적인 예시인 아이리스 분류 문제를 보자.

iris dataset

위와 같이 세 가지 아이리스를 분류하는 문제가 있다. 기존 지식기반 학습으로 아이리스를 구분하기 위해서는 아이리스의 특징을 사람이 구분하고 그를 기반으로 알고리즘을 작성해야 한다. 그러나 이는 굉장히 시간과 비용이 많이 드는 작업이다.

반면 머신러닝 알고리즘을 사용하면 다음과 같이 구분할 수 있다. 먼저 아이리스의 꽃받침 길이와 꽃잎 길이를 추출한다. 이들 데이터를 아래와 같이 평면 그래프 상에 두면 Setosa와 Versicolor가 특정 경계를 두고 구분된다는 것을 알 수 있다. 머신러닝은 이러한 결정경계를 수학적 알고리즘을 통해 계산한다.

iris dataset

다만 이러한 데이터 사이의 결정경계를 예측(prediction)하는 것은 쉽지않다. 눈대중으로 봤을 때 위 그래프를 본다면 Setosa와 Versicolor를 구분하는 결정경계를 그리는 것은 어렵지 않다. 그런데 이를 컴퓨터는 어떻게 수행해야 할까?

(아이리스 분류를 수행하는 머신러닝 코드는 아래와 같다. 코드에 대한 자세한 설명은 후에 하겠다.)

Iris Classification Perceptron Code

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를 알고 싶다. 이를 알 수 있는 가장 쉬운 방법은 그래프 상의 점들을 지나는 단순 선형 함수를 그리면 된다. 이 함수를 예측 함수라고 부른다.

Engine_power-Car_Price

h(x)=wx+bh(x)=wx+b

이때 h(x)h(x)예측함수(predict function, hypothesis: 가설함수), ww가중치(weight), bb편향(bias)이라고 부른다. 우리는 머신러닝을 통해 가장 적절한 예측함수 h(x)h(x)를 찾기위해 학습 데이터에 가장 알맞은 가중치 ww와 편향 bb를 찾아야 한다. 이를 위해 기계학습에서는 훈련 데이터를 통해 예측함수를 실제값과 오차를 줄여나가는 방향으로 개선해간다. 이때 사용하는 것이 목적 함수(Objective function, Cost function: 비용함수)이다.

목적 함수란 예측함수의 출력 y^\hat{y}과 실제 값 yy의 오차를 계산하는 함수로 실제 값과 가장 오차가 작은 예측함수를 찾기위해 사용한다. 일반적으로 평균제곱오차(Mean Squared Error: MSE)를 많이 사용한다.

J(θ)=1ni=1n(y^y)2J(\theta)=\frac{1}{n}\displaystyle\sum_{i=1}^{n}{(\hat{y}-y)^2}

평균제곱오차(MSE)에서 y^y\hat{y}-y는 데이터로부터 예측한 예측함수 값과 실제 값의 오차를 의미한다. 이를 제곱함으로서 (y^y)2(\hat{y}-y)^2가 음수가 되지 않도록 한 후, 모든 데이터에 대한 예측 값-실제값의 제곱오차를 더한다. 이후 데이터의 개수로 나누면 전체 데이터에 대한 예측 값과 실제 값 사이의 제곱오차 평균을 알 수 있다.

처음 학습 데이터를 이용해 학습을 할 때는 가중치 ww와 편향 bb를 난수로 설정한다. 이 후 목적함수가 작아지는 방향으로 예측함수를 개선한다. 앞선 Engine power-Car price 관계를 통해 예측함수를 구해보자.

예제

먼저 XX는 Engine power, YY는 Car price라고 할 때, 훈련집합 XX, YY의 값과 예측함수, 목적함수는 다음과 같다.

X={x1=(40),x2=(60),x3=(80),x4=(110),x5=(125),x6=(150),x7=(160)}X=\{x_1=(40), x_2=(60), x_3=(80), x_4=(110), x_5=(125), x_6=(150), x_7=(160)\}
Y={y1=1100,y2=2000,y3=1950,y4=2300,y5=3400,y6=2800,y7=2750}Y=\{y_1=1100, y_2=2000, y_3=1950, y_4=2300, y_5=3400, y_6=2800, y_7=2750\}
h(x)=wx+bh(x)=wx+b
J(θ)=1ni=1n(hθ(xi)yi)2,n=7J(\theta)=\frac{1}{n}\displaystyle\sum_{i=1}^{n}{(h_\theta(x_i)-y_i)^2}, n=7

예측함수의 최적 매개변수 값을 바로 알 수 없으므로 난수 θi=(wi,bi)T\theta_i=(w_i,b_i)^T로 설정한다. 여기서 T^T는 전치(Tranpose)를 의미하며, 행렬인 (wi,bi)(w_i,b_i)를 열벡터로 표현한 것이다.

θ1=(10,800)T\theta_1=(10, 800)^T
x1,y1(hθ1(40)1100)2=((10×40+800)1100)2=10000x_1,y_1\to(h_{\theta_1}(40)-1100)^2=((10\times40+800)-1100)^2=10000
x2,y2(hθ1(60)2000)2=((10×60+800)2000)2=360000x_2,y_2\to(h_{\theta_1}(60)-2000)^2=((10\times60+800)-2000)^2=360000
x3,y3(hθ1(80)1950)2=((10×80+800)1950)2=122500x_3,y_3\to(h_{\theta_1}(80)-1950)^2=((10\times80+800)-1950)^2=122500
x4,y4(hθ1(110)2300)2=((10×110+800)2300)2=160000x_4,y_4\to(h_{\theta_1}(110)-2300)^2=((10\times110+800)-2300)^2=160000
x5,y5(hθ1(125)3400)2=((10×125+800)3400)2=1822500x_5,y_5\to(h_{\theta_1}(125)-3400)^2=((10\times125+800)-3400)^2=1822500
x6,y6(hθ1(150)2800)2=((10×150+800)2800)2=250000x_6,y_6\to(h_{\theta_1}(150)-2800)^2=((10\times150+800)-2800)^2=250000
x7,y7(hθ1(160)2750)2=((10×160+800)2750)2=122500x_7,y_7\to(h_{\theta_1}(160)-2750)^2=((10\times160+800)-2750)^2=122500

J(θ1)=17i=17(hθ1(xi)yi)2=17×2847500406,786\begin{aligned} \therefore J(\theta_1)&=\frac{1}{7}\displaystyle\sum_{i=1}^{7}{(h_{\theta_1}(x_i)-y_i)^2} \\ &=\frac{1}{7}\times 2847500\\ &\fallingdotseq406,786 \end{aligned}

θ1=(10,800)T\theta_1=(10, 800)^T라고 했을 때, 목적 함수의 값은 약 406,786으로 매우 큰 것을 알 수 있다. 이번에는 θ2=(13,900)T\theta_2=(13, 900)^T 이라고 했을 때의 목적함수를 구해보자.

θ2=(13,900)T\theta_2=(13, 900)^T
x1,y1(hθ2(40)1100)2=((13×40+900)1100)2=102400x_1,y_1\to(h_{\theta_2}(40)-1100)^2=((13\times40+900)-1100)^2=102400
x2,y2(hθ2(60)2000)2=((13×60+900)2000)2=102400x_2,y_2\to(h_{\theta_2}(60)-2000)^2=((13\times60+900)-2000)^2=102400
x3,y3(hθ2(80)1950)2=((13×80+900)1950)2=100x_3,y_3\to(h_{\theta_2}(80)-1950)^2=((13\times80+900)-1950)^2=100
x4,y4(hθ2(110)2300)2=((13×110+900)2300)2=900x_4,y_4\to(h_{\theta_2}(110)-2300)^2=((13\times110+900)-2300)^2=900
x5,y5(hθ2(125)3400)2=((13×125+900)3400)2=765625x_5,y_5\to(h_{\theta_2}(125)-3400)^2=((13\times125+900)-3400)^2=765625
x6,y6(hθ2(150)2800)2=((13×150+900)2800)2=2500x_6,y_6\to(h_{\theta_2}(150)-2800)^2=((13\times150+900)-2800)^2=2500
x7,y7(hθ2(160)2750)2=((13×160+900)2750)2=52900x_7,y_7\to(h_{\theta_2}(160)-2750)^2=((13\times160+900)-2750)^2=52900

J(θ2)=17i=17(hθ2(xi)yi)2=17×1026825146,689\begin{aligned} \therefore J(\theta_2)&=\frac{1}{7}\displaystyle\sum_{i=1}^{7}{(h_{\theta_2}(x_i)-y_i)^2} \\ &=\frac{1}{7}\times 1026825\\ &\fallingdotseq 146,689 \end{aligned}

θ2=(13,900)T\theta_2=(13, 900)^T으로 예측함수를 사용하였을 때 θ1=(10,800)T\theta_1=(10, 800)^T 보다 목적함수의 값이 감소한 것을 알 수 있다. 따라서 θ2=(13,900)T\theta_2=(13, 900)^Tθ1=(10,800)T\theta_1=(10, 800)^T보다 더 적합한 예측함수라고 할 수 있다.

이번에는 θ3=(14,900)T\theta_3=(14, 900)^T이라고 했을 때의 목적함수를 구해보자.

θ3=(14,900)T\theta_3=(14, 900)^T
x1,y1(hθ3(40)1100)2=((14×40+900)1100)2=129600x_1,y_1\to(h_{\theta_3}(40)-1100)^2=((14\times40+900)-1100)^2=129600
x2,y2(hθ3(60)2000)2=((14×60+900)2000)2=67600x_2,y_2\to(h_{\theta_3}(60)-2000)^2=((14\times60+900)-2000)^2=67600
x3,y3(hθ3(80)1950)2=((14×80+900)1950)2=4900x_3,y_3\to(h_{\theta_3}(80)-1950)^2=((14\times80+900)-1950)^2=4900
x4,y4(hθ3(110)2300)2=((14×110+900)2300)2=19600x_4,y_4\to(h_{\theta_3}(110)-2300)^2=((14\times110+900)-2300)^2=19600
x5,y5(hθ3(125)3400)2=((14×125+900)3400)2=562500x_5,y_5\to(h_{\theta_3}(125)-3400)^2=((14\times125+900)-3400)^2=562500
x6,y6(hθ3(150)2800)2=((14×150+900)2800)2=40000x_6,y_6\to(h_{\theta_3}(150)-2800)^2=((14\times150+900)-2800)^2=40000
x7,y7(hθ3(160)2750)2=((14×160+900)2750)2=152100x_7,y_7\to(h_{\theta_3}(160)-2750)^2=((14\times160+900)-2750)^2=152100

J(θ3)=17i=17(hθ3(xi)yi)2=17×976300139,471\begin{aligned} \therefore J(\theta_3)&=\frac{1}{7}\displaystyle\sum_{i=1}^{7}{(h_{\theta_3}(x_i)-y_i)^2} \\ &=\frac{1}{7}\times 976300\\ &\fallingdotseq 139,471 \end{aligned}

θ3=(14,900)T\theta_3=(14, 900)^T으로 예측함수를 사용하였을 때 θ2=(13,900)T\theta_2=(13, 900)^T보다 목적함수의 값이 감소한 것을 알 수 있다. 따라서 θ3=(14,900)T\theta_3=(14, 900)^Tθ2=(13,900)T\theta_2=(13, 900)^T보다 더 적합한 예측함수라고 할 수 있다.

이와 같은 방식으로 계속해서 wwbb 값을 수정하다보면 어느 순간 더이상 목적함수의 값이 감소하지 않는 순간이 온다. 이때의 θ\theta를 최적해 θ^\hat{\theta} 라고 한다. 이를 공식화하면 다음과 같다.

θ^=argminθJ(θ)\hat{\theta}=\underset{\theta}\mathrm{argmin}J(\theta)

이를 알고리즘 형식으로 쓰면 다음과 같다.

입력: 훈련집합 XXYY
출력: 최적의 매개변수 θ^\hat{\theta}

난수를 생성하여 초기 해 θ_1을 설정한다.
t=1
while(J(θ)가 0.0에 충분히 가깝지 않음) //수렴 여부 검사
	J(θ_t)가 작아지는 방향 ∆θ_t
    θ_t+1 = θ_t + ∆θ_t
    t = t + 1
hat(θ) = θ_t

이처럼 머신러닝은 작은 개선을 반복하여 최적해를 찾아가는 수치적 방법임을 알 수 있다.


참고자료

필자는 기계학습을 공부하는 학부생일뿐이므로 잘못된 정보가 있을 수 있습니다. 본 글에 잘못된 정보, 추가 정보, 오타 등이 있으면 댓글로 달아주세요.

0개의 댓글