Processing math: 100%

ADALINE とは

Adaptive Linear Neuron の略。
分類問題を解くアルゴリズムで、パーセプトロンの改良のようなもの。

問題設定

入力値(特徴量) x1,,xm に対し、分類ラベル y を出力するモデルを作る。

仕組み

基本原理

各入力値に重み w1,,wm をかけて和を取った

z=mj=1wjxj

総入力 と呼び、z が閾値 θ 以上か否かで二値分類を行う。
また、閾値 θ をゼロ番目の重み w0=θ として総入力に加える(x0=1)ことで、ラベル判定の閾値を0にできる。
したがって、問題は重み w0,,wm の最適化問題に帰着する。

ここまではパーセプトロンと同様。

パーセプトロンでは、総入力 z に対する活性化関数を

ϕ(z)={1(z0)1(z<0)

と定義し、ラベルの正解と予測の一致・不一致の区別のみを行い重みの更新を行った。
ADALINE では、

ϕ(z)=z=mj=0wjxj

という線形活性化関数を導入する。
単純に正解ラベルと一致するかしないかではなく、「どれほど正解ラベルに近いか」という連続値による評価ができる

ADALINE では、全サンプルに対するラベル判定誤差の平方和

J(w)=12i(y(i)ϕ(z(i)))2=12i(y(i)mj=0wjx(i)j)2

を目的関数(コスト関数)とし、これが最小になるよう、勾配降下法による学習を進めていく。
式の先頭の 12 は、後述の微分で余計な係数が消えるよう調整するパラメータなので重要ではない。

J(w) の勾配 J(w) の各成分は、

J(w)wj=i(y(i)ϕ(z(i)))x(i)j

よって、重み w の各成分を以下の式で更新するとコスト関数を小さくできる。

wjwjηJ(w)wj=wj+ηi(y(i)ϕ(z(i)))x(i)j

ここで η(0<η1) は学習率であり、1度に重みを更新する大きさを表す。

学習規則

  1. 重みをゼロ or 値の小さい乱数で初期化
  2. すべての学習サンプル x(i)=(x(i)0,,x(i)m) を使い、コスト関数 J(w) の勾配 J(w) を計算
  3. 勾配に沿って、コスト関数が小さくなるように重みを更新
  4. 収束するまで2,3を繰り返す

学習率と収束

「学習率が大きい = 1度の重み更新の幅が大きい」
→ 学習率が大きすぎると収束しない

download-4

実装

コード

import numpy as np
class Adaline:
def __init__(self, d, eta=0.001, epoch=100, max_err=10):
"""
Parameters
----------
d : 次元(変数の数)
eta : 学習率
epoch : エポック
max_err : 許容する判定誤りの最大数
"""
self.d = d
self.eta = eta
self.epoch = epoch
self.max_err = max_err
self.weight = np.zeros(d+1) # 閾値を重みと見做す分、1つ増える
def predict(self, x):
"""
Parameters
----------
x : 分類したいデータ(d次元ベクトル)
"""
return 1 if np.dot(x, self.weight[:-1]) + self.weight[-1] > 0 else -1
def fit(self, data, labels):
"""
Parameters
----------
data :
labels :
"""
self.labels = labels
self.data = np.append(data, np.array([[1.0] for _ in range(len(data))]), axis=1)
for t in range(self.epoch):
cnt_err = self.__cycle()
if cnt_err <= self.max_err:
break
print('Converged in {} cycles.'.format(t+1))
def __cycle(self):
cnt_err = 0
dw = np.zeros(len(self.weight))
for i in range(len(self.data)):
z = np.dot(self.data[i], self.weight)
dw += (self.labels[i] - z) * self.data[i]
if self.labels[i] < 0 <= z or z < 0 < self.labels[i]:
cnt_err += 1
dw *= self.eta
self.weight += dw
return cnt_err
view raw adaline.py hosted with ❤ by GitHub

動作確認

  • 点:学習データ
  • 背景:モデルの決定領域
# 学習データ作成
N = 1000
data = np.reshape(np.random.rand(2*N), (N, 2))
labels = np.array([1 if 2*x[0] - x[1] > 0 else -1 for x in data])
# 学習
adaline = Adaline(2, max_err=N//100)
adaline.fit(data, labels)
view raw ~fit1.py hosted with ❤ by GitHub

download-1

# 学習データ作成
N = 1000
c1 = [1, 1]
c2 = [2, 3]
r1 = 1.5*np.random.rand(N//2)
r2 = 1.0*np.random.rand(N//2)
theta1 = np.random.rand(N//2) * 2 * np.pi
theta2 = np.random.rand(N//2) * 2 * np.pi
data1 = np.array([r1 * np.sin(theta1) + c1[0], r1 * np.cos(theta1) + c1[1]]).T
data2 = np.array([r2 * np.sin(theta2) + c2[0], r2 * np.cos(theta2) + c2[1]]).T
data = np.concatenate([data1, data2])
labels = np.array([1 if i < N//2 else -1 for i in range(N)])
# 学習
adaline = Adaline(2, max_err=N//50, eta=1e-4, epoch=500)
adaline.fit(data, labels)
view raw ~fit2.py hosted with ❤ by GitHub

download-2