์ด๋ฒ ์๊ฐ์๋ ๋๋์ด ๋ง์นจ๋ด ํ๋ก๊ทธ๋๋ฐ์ด ๋ฑ์ฅํฉ๋๋ค. ์ด์ ์น์ ์์ ๊ณต๋ถํ ์์๋ค์ ํ์ด์ฌ ํด๋์ค์ ํจ์๋ก ๊ตฌํํ๊ณ , ๋จธ์ ๋ฌ๋์ ๋ํ์ ์ธ ๋ฐ์ดํฐ ์ค ํ๋์ธ ๋ถ๊ฝ ๋ฐ์ดํฐ์ ์ ํ์ต์ํค๋ ๊ฒ ๊น์ง ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ฐ์ ์ ์ธ์ ์์ ์ด์ผ๊ธฐํ ์๋์ฝ๋ค๋ ํ์ด์ฌ์ ์ค์นํ์ ๋ ์ข์ต๋๋ค. ์ ๋ google colaboratory๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํฉ๋๋ค. ์ค์น์์ด ๊ฐํธํ๋ ์ฌ์ฉํ์ ๋ ์ข์ต๋๋ค :) https://colab.research.google.com/
1. ๊ฐ์ฒด ์งํฅ ํผ์ ํธ๋ก API
์ฝ๋๋ฅผ ๋ณด๊ธฐ ์ ์, ๋จผ์ ๊ฐ๋จํ ์ฌ์ ์ฝ๋ ์ ์์ ๋ํด์ ์๋ ค๋๋ฆฌ๊ฒ ์ต๋๋ค. ์๋ Perception ํด๋์ค๋ฅผ ์ด๊ธฐํํ ํ fit ๋ฉ์๋๋ก ํ์ตํ๊ณ , predict ๋ฉ์๋๋ก ์์ธกํฉ๋๋ค. ์๋ก ์์ฑํ๋๊ฒ ์๋ ๋ค๋ฅธ ๋ฉ์๋๋ฅผ ํธ์ถํ ์์ฑ์ ์ธ๋๋ฐ(_)๋ฅผ ์ถ๊ฐํด ๊ตฌ๋ถํฉ๋๋ค.
eta ๋ณ์๋ ํ์ต๋ฅ ๋ก float ์๋ฃํ์ ๊ฐ๊ณ , n_iter ์ intํ์ผ๋ก epoch๋ฅผ ๋ํ๋ ๋๋ค. random_state ๋ intํ์ผ๋ก ๊ฐ์ค์น๋ฅผ ๋ฌด์์๋ก ์ด๊ธฐํํ๊ธฐ ์ํ ๋์ ์์ฑ๊ธฐ์ ๋๋ค. w_๋ ํ์ต๋ ๊ฐ์ค์น ๋ฒกํฐ๊ณ , errors_๋ ๋์ ๋ ์ค๋ฅ๋ค์ ๋ฒกํฐ๋ก ์ ์ฅํ ๊ฐ์ ๋๋ค. ๋จผ์ ์ฝ๋๋ฅผ ๋ณด์ฌ๋๋ฆฌ๊ณ , ๊ทธ ๋ค์์ ํด์ค์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
import numpy as np
class Perceptron(object):
"""
eta : float
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
"""
X : {array-like}, shape = [n_samples, n_feaures] -> {}์์ ๋ฐ์ดํฐ, n_samples๊ฐ ์ํ๊ณผ n_features๊ฐ์ ํน์ฑ, ํ๋ จ๋ฐ์ดํฐ
y : array-like, shape = [n_samples] -> {}์์ ๋ฐ์ดํฐ, n_samples๊ฐ์ ์ ๋ต ํ๊น
"""
def fit(self, X, y):
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)
๋จผ์ , fit ํจ์์์ self.w_ ๋ก ํํ๋ ๊ฐ์ค์น๋ ๋ฒกํฐ R^(m+1)๋ก ์ด๊ธฐํ๋ฉ๋๋ค. m์ ๋ฐ์ดํฐ ์ ์ ์ฐจ์์ ๋ปํฉ๋๋ค. 2์ฐจ์๋ฐ์ดํฐ๋ผ๋ฉด R์ R^3์ด ๋๊ฒ ์ฃ . size ์ 1์ ๋ํ ์ด์ ๋ ์์ ์น์ ์์ ์ด์ผ๊ธฐํ ๊ฒ์ฒ๋ผ ์ ํธ์ ๋ง๋ค๊ธฐ ์ํจ์ ๋๋ค. ๋ฐ๋ผ์ self.w_[0]์ ์ ํธ์ด ๋ฉ๋๋ค.
fit ์์ ์ฐ์ด๊ณ ์๋ self.w_๋ฒกํฐ๋ rgen.normal(loc = 0.0, scale=0.01, size = 1+X.shape[1])๋ก ํ์คํธ์ฐจ(scale)์ด 0.01์ธ ์ ๊ท๋ถํฌ์์ ๋ฝ์ ๋๋คํ ์์ ์๋ฅผ ๋ด๊ณ ์์ต๋๋ค. ์ฌ์ค ์ ๊ท๋ถํฌ์ธ ๊ฒ๋, 0.01์ธ ๊ฒ๋ ํฌ๊ฒ ์๋ฏธ๋ ์์ผ๋ ์ ๊ฒฝ์ฐ์ง ์์ผ์ ๋ ๋ฉ๋๋ค. ์ด ์์ ์๋ก ๊ฐ์ค์น๋ฅผ ์ด๊ธฐํํ๋ ๊ฒ์ ๋๋ค. ์ฐ์ธ rgenํจ์๋ numpy์์ ์ ๊ณตํ๋ ๋์ ์์ฑ๊ธฐ๋ก, ์๋๋ฅผ ํตํด ๊ฐ์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ์ด์ ๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ ์ ๊ณตํ ์ ์์ต๋๋ค. ์คํ์ ๋ฐ๋ผ ๊ฐ์ด ๋ฐ๋๋ ๊ฒ์ ์๋๋ผ๋ ๋ง์ด์ฃ .
์ด๋ ์ฐ๋ฆฌ๋ ์ ๊ฐ์ค์น๋ฅผ 0์ผ๋ก ์ด๊ธฐํํ์ง ์์ง? ๋ผ๋ ์๋ฌธ์ด ๋ค ์ ์์ต๋๋ค. ๊ทธ ์ด์ ๋ ๊ฐ์ค์น๋ฅผ 0์ผ๋ก ์ด๊ธฐํํ๋ฉด ํ์ต๋ฅ ์ด ๊ฐ์ค์น ๋ฒกํฐ์ ๋ฐฉํฅ์ด ์๋ ํฌ๊ธฐ์๋ง ์ํฅ์ ๋ง์น๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ด๊ธฐ ๊ฐ์ค์น๊ฐ 0์ด๋ผ๋ฉด ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ๊ฒ ๋ฉ๋๋ค.
v1 = np.array([1,2,3])
v2 = 0.5 * v1
np.arccos(v1.dot(v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
...
0.0
์ ์ฝ๋์์ ์ฆ๋ช ํ ์ ์์ต๋๋ค. np.arccos ํจ์๋ ์ญ์ฝ์ฌ์ธ ํจ์์ด๊ณ np.linalg.norm์ ๋ฒกํฐ์ ๊ธธ์ด๋ฅผ ๊ณ์ฐํฉ๋๋ค. ์ด ์ฝ๋๋ฅผ ์คํํด๋ณด๋ฉด v1๊ณผ v2 ์ฌ์ด ๊ฐ์ด 0๋๊ฐ ๋จ์ ํ์ธํ ์ ์์ต๋๋ค. ์๋ฌดํผ๊ฐ์ ์ด๊ธฐ ๊ฐ์ค์น๊ฐ 0์ด ์๋ ํธ์ด ์ข๋ค๋ ๊ฒ๋ง ๊ธฐ์ตํ์๋ฉด ๋ฉ๋๋ค.
fit ๋ฉ์๋๋ ๊ฐ์ค์น๋ฅผ ์ด๊ธฐํํ ํ X์ ์๋ ๋ชจ๋ ์ํ์ ๊ฐ์ค์น ์ ๋ฐ์ดํธ๋ฅผ ํ๋ฌ ๋์๋ค๋๋๋ค. ์ด ํ๋ จ ์ํ์ด ์ด๋ ํด๋์ค์ธ์ง๋ predict ๋ฉ์๋์์ ์์ธกํฉ๋๋ค. fit์์ ๊ฐ์ค์น ์ ๋ฐ์ดํธ๋ฅผ ์ํด predict ๋ฉ์๋๋ฅผ ํธ์ถํด ์์ธก์ ์ป์ต๋๋ค. ์ด ๊ณผ์ ์ ๋ฐ๋ณตํ๋ฉด์ epoch๋ง๋ค self.errors_๋ฆฌ์ค๋์ ์๋ชป ๋ถ๋ฅ๋ ํ๋ฃจ๋ฅผ ๊ธฐ๋กํฉ๋๋ค. ์ด ๊ณผ์ ์ ๋์ค์ ํ๋ จ๊ณผ์ ์ ๋ถ์ํ๋๋ฐ ๋์์ด ๋ฉ๋๋ค. net_input ๋ฉ์๋์์ ์ฌ์ฉํ np.dot ํจ์๋ w^T์ x์ ์ ๊ณฑ์ ์ฐ์ฐํฉ๋๋ค.
2. ๋ถ๊ฝ ๋ฐ์ดํฐ์ ํ๋ จ!
์์์ ๋ง๋ ๊ตฌํ์ ํ ์คํธํ๊ธฐ ์ํด์ ๋ถ๊ฝ ๋ฐ์ดํฐ์ ์ค์์ ๋ฑ ๋ ๊ฐ์ ๊ฝ, Setosa ์ Versicolor ๋ ๊ฐ์ ํด๋์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ฌผ๋ก ํผ์ ํธ๋ก ์๊ณ ๋ฆฌ์ฆ์ ์ผ๋๋ค ์ ๋ต ๋ฑ ๋ค์ค ํด๋์ค ๋ถ๋ฅ๋ก ํ์ฅ๋ ๊ฐ๋ฅํฉ๋๋ค.
๋จผ์ , ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํด์์ผ๊ฒ ์ฃ ? ๊ตณ์ด ๋ฐ์ดํฐ๋ฅผ ์ปดํจํฐ์ ๋ฐ์์ผํ ์ด์ ๋ ์์ต๋๋ค. pandas ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ UCI ๋จธ์ ๋ฌ๋ ์ ์ฅ์์์ ๋ถ๊ฝ ๋ฐ์ดํฐ์ ์ ๋ก๋ํฉ๋๋ค. tail()ํจ์๋ฅผ ์ด์ฉํ๋ฉด ๊ทธ๋ฆผ 2์ ๊ฐ์ด ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋๋ค.
import pandas as pd
df = pd.read_csv('https://archive.ics.uci.edu/ml/'
'machine-learning-databases/iris/iris.data', header=None)
df.tail()
๋ง์ฝ ๋ฐ์ดํฐ๊ฐ ๋ก์ปฌ ๋ํ ํ ๋ฆฌ์ ์๋ค๋ฉด, ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
df = pd.read.cvs('your/local/path/to/iris.data', header = None)
๊ทธ ๋ค์ 50๊ฐ์ iris setosa์ 50๊ฐ์ iris versicolor์ ํด๋นํ๋ 100๊ฐ ๋ ์ด๋ธ์ ์ถ์ถํ๊ณ , ๊ฐ ๊ฝ์ 1๊ณผ -1 ํด๋์ค๋ก ๋ฐ๊พผ ํ์ y์ ์ ์ฅํฉ๋๋ค. ๋น์ทํ๊ฒ ์ํ์ ์ฒซ๋ฒ์งธ ํน์ฑ ๊ฝ๋ฐ์นจ ๊ธธ์ด์ ์ธ ๋ฒ์งธ ํน์ฑ ๊ฝ์ ๊ธธ์ด๋ฅผ X์ ์ ์ฅํฉ๋๋ค. plt.show()๋ ๊ทธ๋ํ๋ฅผ ์๊ฐ์ ์ผ๋ก ๋ณด์ฌ์ค๋๋ค.
import matplotlib.pyplot as plt
import numpy as np
# setosa์ versicolor๋ฅผ ์ ํํฉ๋๋ค
y = df.iloc[0:100, 4].values
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()
์ ๊ทธ๋ํ๋ ์ ํ ๊ฒฐ์ ๊ฒฝ๊ณ๋ก ๋ ๊ฝ์ ๋ถ๋ฅํ๊ธฐ์ ์์ฃผ ์์ ์ฐ์ ๋๋ค์. ์ด์ ์ถ์ถํ ๋ฐ์ดํฐ๋ค์์ ์๊ณ ๋ฆฌ์ฆ์ ํ๋ จํด๋ณด๊ฒ ์ต๋๋ค. epoch์ ๋ฐ๋ผ ์๋ชป ๋ถ๋ฅ๋ ์ค์ฐจ ์ ๋๋ฅผ ๊ทธ๋ํ๋ก ๊ทธ๋ฆฌ๊ณ , ์๊ณ ๋ฆฌ์ฆ์ด ์๋ ด์ ํตํด์ ๊ฒฐ์ ๊ฒฝ๊ณ๋ฅผ ์ฐพ๋ ๊ณผ์ ์ ๋์ผ๋ก ํ์ธํ ์ ์์ต๋๋ค.
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()
์ ์ฝ๋๋ฅผ ์คํํ๋ฉด epoch์ ๋ฐ๋ผ ์ค์ฐจ๊ฐ ์ผ๋ง๋ ๋ฌ๋ผ์ง๋์ง ํ์ธํ ์ ์์ต๋๋ค. 1๋ฒ์์ ๊ณต๋ถํ ์ฝ๋๋ฅผ ๋ฏธ๋ฆฌ ์คํ์ํจ ๋ค์์ ๋ง๋ถ์ฌ์ผํ๋ค๋ ๊ฒ์ ์์ง ๋ง์ธ์! ์ด๋ฒ ๊ณผ์ ์ Perceptron ๋ฉ์๋์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ณ ํ์ต์ํค๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ ๊ฒฐ์ ๊ฒฝ๊ณ๋ฅผ ๊ทธ๋ํ ์์ ํํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์๋ ํจ์๋ ์ฐ์ ๋์ ๊ฒฐ์ ๊ฒฝ๊ณ๋ฅผ ๊ทธ๋ฆฌ๋ ๊ฐ๋จํ ํจ์์ ๋๋ค.
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()
์ฝ๋๋ฅผ ์คํํ๋ฉด ์๋์ ๊ฐ์ด ์์ ๊ฒฐ์ ๊ฒฝ๊ณ ๊ทธ๋ํ๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค.
์ด๋ฅผ ํตํด ํผ์ ํธ๋ก ์ ๊ฒฐ์ ๊ฒฝ๊ณ๊ฐ ์๋ฒฝํ๊ฒ ๋ ๊ฐ์ ๋ถ๊ฝ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฅํ๋ค๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์ด๋ ์ จ๋์? ์ฒ์์ผ๋ก ์์ฒญ ๋ง์ ์ฝ๋๋ค์ ์ ํ์ จ์ํ ๊ณ , ํ์ด์ฌ์ด ์์ํ์ จ์ ์๋ ์์ต๋๋ค. ํ์ง๋ง ์ฌ์ค ํท๊ฐ๋ฆฌ๋ ๋ฌธ๋ฒ๋ค๊ณผ ์ฌํ ๋ณธ ์ ์๋ numpy ํจ์๋ค, pandas ํจ์๋ค์ด ์ด๋ ค์ด ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐํ๋ ์ด์ ๊ฐ ๋ ์ ์์ต๋๋ค. ๋ค ์ดํดํ์๊ณ ์ธ์ฐ์ค ํ์๋ ์์ต๋๋ค.
ํผ์ ํธ๋ก ํจ์๊ฐ ์ด๋ ๊ฒ ๋์๊ฐ๊ณ , ์ด ์ด๋ ค์ด ์์์ ์ฝ๋๋ก๋ ์ด๋ ๊ฒ ํํํ ์ ์๊ตฌ๋, ๋ผ๋ ๊ฒ์ ๋ฐ์๋ค์ด์๋๊ฒ ์ค์ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ๋น์ฅ ๋ชจ๋ธ์ ๋ง๋ค์ง ์์ ๊ฑฐ๋๊น์! ๊ณ์ํด์ ์ค์ต ์ฝ๋๋ค์ ๋์ค๊ฒ ์ง๋ง, ์ฌ๋ฌ๋ถ์ ์น์ํด์ง์๋ ๊ฒ์ ๋ชฉํ๋ก ํ์๊ธธ ๋ฐ๋๋๋ค.
๋ค์ ๋ง์ ๋๋ฆฌ์ง๋ง, ๊ตณ์ด ๋ชจ๋ ์ฝ๋๋ฅผ ๋ค ์ดํดํ์ค ํ์๋ ์์ต๋๋ค! ์ฑ ์ ๋ค ๋ณด๊ณ , ๋ค์ ๋์์์ ์ฒ์ฒํ ํด๋ ๋ฆ์ง ์์ต๋๋ค. ๋ชจ๋ ๊ณต๋ถ๋ ์ฌ๋ฏธ๊ฐ ์์ด์ผํ๋๋ฐ, ๊ทธ๋ ๊ฒ ํ๋ฉด ์ฌ๋ฏธ์์์ง๋ ๋ชฐ๋ผ์.
๊ทธ๋ผ ์กฐ๊ธ ๋ ๊ฐ๋ฒผ์ด ๋ง์์ผ๋ก, ๋ค์ ๋ฒ์๋ ์ ์ํ ์ ํ ๋ด๋ฐ๊ณผ ํ์ต์ ์๋ ด์ ๋ํ ๋ด์ฉ์ ๋ค๊ณ ์ค๊ฒ ์ต๋๋ค. ์์ฃผ ์ค์ํ ๊ฒฝ์ฌํ๊ฐ๋ฒ์ด ๋์ค๋ ๋์น์ง ๋ง์ธ์!