파이썬으로 퍼셉트론 학습 알고리즘 구현

머신러닝 교과서 with 파이썬,사이킷런,텐서플로 세바스찬 라시카, 바히드 미자리리 지음, 박해선 옮김

머신러닝 교과서

한국외대 DSC(Developer Student Club) 멤버들과 함께 머신러닝 스터디를 진행하면서 “머신 러닝 교과서“를 블로그에 정리한 내용이다.


2장에서는 분류를 위한 초창기 머신 러닝 알고리즘인 퍼셉트론적응형 선형 뉴런 두개를 사용한다. 이 두 알고리즘에 대해 이해하고, 파이썬을 사용한 효율적인 구현 방법을 익히는데 도움이 될 것이라고 한다. 아래 3가지를 2장에서 주로 다룬다고 한다.

  • 머신 러닝 알고리즘을 직관적으로 이해하기
  • Pandas, Numpy, Matplotlib으로 데이터를 읽고 처리하고 시각화하기
  • 파이썬으로 선형 분류 알고리즘 구현하기

객체 지향 퍼셉트론 API

퍼셉트론 인터페이스를 가진 파이썬 클래스를 정의한다.

  • Perceptron 객체를 초기화
  • fit 메서드로 데이터 학습
  • predict 메서드로 예측 결과를 만든다.

다른 메서드를 호출하여 만든 속성은 밑줄(_)을 추가한다. 예를 들어 self.w 와 같다.

import numpy as np

class Percpetron(object):
    """퍼셉트론 분류기

    매개변수
    ------------
    eta : float
      학습률 (0.0과 1.0 사이)
    n_iter : int
      훈련 데이터셋 반복 횟수
    random_state : int
      가중치 무작위 초기화를 위한 난수 생성기 시드

    속성
    -----------
    w_ : 1d-array
      학습된 가중치
    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=1 + X.shape[1])
        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_[1:] += update * xi
                self.w_[0] += update
                errors += int(update != 0.0)
            self.errors_.append(errors)
        return self

    def net_input(self, X):
        """최종 입력 계산"""
        return np.dot(X, self.w_[1:]) + self.w_[0]

    def predict(self, X):
        """단위 계단 함수를 사용하여 클래스 레이블을 반환합니다"""
        return np.where(self.net_input(X) >= 0.0, 1, -1)

출처: https://github.com/rickiepark/python-machine-learning-book-2nd-edition/blob/master/code/ch02/ch02.ipynb

fit 메서드에서 self.w_ 가중치를 벡터 $R^{m+1}$ 로 초기화한다. (m은 데이터셋의 차원(특성)개수) $m+1$인 이유는 벡터의 첫 번째 원소인 절편을 위해 1을 더했기 때문이다.

가중치 0으로 초기화 하지 않아야 한다. 이유는 가중치가 0으로 초기화되어 있다면 학습률 파라미터 eta는 가중치 벡터의 방향이 아니라 크기에만 영향을 미치기 때문이다. 이 문제는 삼각법으로 나타낼 수 있다.

v1 = np.array([1, 2, 3])
v2 = 0.5 * v1
np.arccos(v1.dot(v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))

# output: 0.0

붓꽃 데이터셋에서 퍼셉트론 훈련

붓꽃 데이터를 UCI 머신 러닝 저장소에서 직접 로드해서 사용한다.

import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/'
        'machine-learning-databases/iris/iris.data', header=None)
df.tail()

"""
결과값
    0	  1   2  	3   4
145	6.7	3.0	5.2	2.3	Iris-virginica
146	6.3	2.5	5.0	1.9	Iris-virginica
147	6.5	3.0	5.2	2.0	Iris-virginica
148	6.2	3.4	5.4	2.3	Iris-virginica
149	5.9	3.0	5.1	1.8	Iris-virginica
"""

붓꽃 데이터셋 시각화

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

# setosa와 versicolor를 선택합니다
y = df.iloc[0:100, 4].values
# setosa일 경우 -1, 아니면(versicolor) 1
y = np.where(y == 'Iris-setosa', -1, 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='x', label='versicolor')

plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

plt.show()

첫 번째 줄에서 %matplotlib inline 의 역할은 notebook을 실행한 브라우저에서 바로 그림을 볼 수 있게 해주는 것 이다.

출처: https://korbillgates.tistory.com/85 [생물정보학자의 블로그]

붓꽃 데이터셋의 일부로 퍼셉트론 알고리즘으로 훈련

ppn = Perceptron(eta=0.1, n_iter=10)

ppn.fit(X, y)

plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Number of errors')

plt.show()

결과

error

2차원 데이터셋의 결정 경계 시각화하기

from matplotlib.colors import ListedColormap


def plot_decision_regions(X, y, classifier, resolution=0.02):

    # 마커와 컬러맵을 설정합니다
    markers = ('s', 'x', 'o', '^', '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))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, 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=cl, 
                    edgecolor='black')
plot_decision_regions(X, y, classifier=ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')

plt.show()

결과값

결론

퍼셉트론이 학습한 결정 경계는 두개의 붓꽃으로 구성된 데이터셋의 모든 샘플을 완벽하게 분류한다.

Share: Facebook