赞
踩
赞
踩
感知器是模仿人类神经元的用于线性可分分类的最简单的神经网络模型。线性可分分类即可以通过一个线性的超平面将数据分开。其基本模型如下图所示,其中 a 1 , a 2 , . . . a n a_{1},a_{2},...a_{n} a1,a2,...an是输入数据, w 1 , w 2 , . . . , w n w_{1},w_{2},...,w_{n} w1,w2,...,wn是权重可以理解为可分超平面的斜率, b b b是偏执保证超平面不过原点, s u m sum sum是简单的求和操作, f f f在感知器中被称为激活函数,实际上是一个限幅器,将输出映射为对应的类别的值域中。
感知器的数学模型如下:
f
(
x
)
=
{
1
if
w
⋅
x
+
b
>
0
−
1
else
f(x)=\left\{
公式中的-1,1只是表示两个类别不具备任何具体的含义,可以用其他两个不同的数字代替,其中
∑
i
=
1
n
w
i
x
i
+
b
=
0
\sum_{i=1}^{n} w_{i} x_{i}+b=0
∑i=1nwixi+b=0便是感知器学习到的可分超平面。感知器一般使用梯度下降,最小二乘和有感知器学习对权重
w
\textbf{w}
w进行迭代来调整超平面。
对于感知器收敛是可证明的下面就给出相应的收敛定理证明,另外,感知器收敛的前提是数据是线性可分的。
在感知器中一般 b b b只会选取一个固定的值比如+1,-1作为偏置,这里选择+1,因此可以将 b b b和 w w w合并,因此相应的输入 x x x和 w w w变成:
x = [ + 1 , x 1 , x 2 , . . . , x n ] T x=[+1, x_{1}, x_{2},...,x_{n}]^{T} x=[+1,x1,x2,...,xn]T
w = [ w 0 , w 1 , w 2 , . . . , w n ] T w=[w_{0}, w_{1}, w_{2},...,w_{n}]^{T} w=[w0,w1,w2,...,wn]T
其中 x x x和 w w w分别在第一位添加了一个维度,公式变得更加紧蹙,但是计算结果和过程都不会受到影响。原感知器模型就变成:
f ( x ) = ∑ i = 1 n w i x i + b ⇒ f ( x ) = ∑ i = 1 n w i x i = w T x f(x)=\sum_{i=1}^{n}w_{i}x_{i}+b \Rightarrow f(x)=\sum_{i=1}^{n}w_{i}x_{i}=\textbf{w}^T\textbf{x} f(x)=i=1∑nwixi+b⇒f(x)=i=1∑nwixi=wTx
感知器更新
w
w
w的方式很简单基本就是惩罚措施,对于正确分类的情况
w
w
w不做任何更新,对于错分的情况才更新
w
w
w,可以认为感知器做错了就打一巴掌,指导他所有都做对了为止。基本的更新规则如下(第一个公式就是对于正确分类,第二个公式就是对于错误分类,
D
1
D_1
D1和
D
2
D_2
D2分别为两个类别):
w
n
+
1
=
w
n
x
n
w
n
T
>
0
且
x
n
∈
D
1
w
n
+
1
=
w
n
x
n
w
T
⩽
0
且
x
n
∈
D
2
w
n
+
1
=
w
n
−
η
n
x
n
x
n
w
n
T
>
0
且
x
n
∈
D
2
w
n
+
1
=
w
n
+
η
n
x
n
x
n
w
T
⩽
0
且
x
n
∈
D
1
η \eta η表示学习率,通常学习率只会和学习的快慢相关不会和最总的结果有很大的关系。因此我们现在假定学习率 η = 1 \eta=1 η=1且为常量,而且 w 0 = 0 w_0=0 w0=0,因此对于 w n w_n wn的迭代可以得到: w n + 1 = x 1 + x 2 + . . . + x n w_{n+1}=x_1+x_2+...+x_n wn+1=x1+x2+...+xn,因为两个类别是线性可分的因此一定存在一个线性超平面得到最终的解,假定最终的解为 w o \textbf{w}_o wo,因此可以定义一个常数 α \alpha α,存在
α
=
min
x
n
∈
D
1
w
o
T
x
n
⇒
w
o
T
w
n
+
1
=
w
o
T
x
1
+
w
o
T
x
2
+
⋯
+
w
o
T
x
n
⇒
w
o
T
w
n
+
1
⩾
n
α
⇒
∥
w
o
∥
2
∥
w
n
+
1
∥
2
⩾
n
2
α
2
因
为
∥
w
o
∥
2
∥
w
n
+
1
∥
2
⩾
[
w
o
T
w
n
+
1
]
2
⇒
∥
w
n
+
1
∥
2
⩾
n
2
α
2
∥
w
o
∥
2
这里证明了 w n w_n wn的下限,另一方面证明其的上限:
w
n
+
1
=
w
n
+
x
n
⇒
∥
w
n
+
1
∥
2
=
∥
w
n
∥
2
+
∥
x
n
∥
2
+
2
w
n
T
x
n
,
对
上
式
两
边
同
时
平
方
⇒
∥
w
n
+
1
∥
2
⩽
∑
k
=
1
n
∥
x
k
∥
2
⩽
n
β
,
其
中
β
=
max
x
n
∈
D
1
∥
x
n
∥
2
因此有:
n
2
α
2
∥
w
o
∥
2
⩽
∥
w
n
+
1
∥
2
⩽
∑
k
=
1
n
∥
x
k
∥
2
⩽
n
β
,
其
中
β
=
max
x
n
∈
D
1
∥
x
n
∥
2
,
α
=
min
x
n
∈
D
1
w
o
T
x
n
⇒
n
max
2
α
2
∥
w
o
∥
2
=
n
max
β
∃
n
m
a
x
⇒
n
m
a
x
=
β
∣
∣
W
o
∣
∣
2
α
2
因此对于线性可分的数据集,对于 η = 0 , w 0 = 0 \eta=0,w_0=0 η=0,w0=0一定能在一定的迭代次数之后终止。而对于 η \eta η非固定时,感知器总是能够在一定步数到达固定的 η \eta η中的某个状态,也就是将固定 η \eta η分解为多个任务,以不同的 η \eta η进行迭代,但最终的效果是相同的,唯一不同的是需要迭代训练的次数增加或者减少。
感知器算法完整描述:
输入:数据: x = [ + 1 , x 1 , x 2 , . . . , x n ] T x=[+1,x_1,x_2,...,x_n]^T x=[+1,x1,x2,...,xn]T, 权值 w = [ b , w 1 , w 2 , . . . , w n ] T w=[b,w_1, w_2,...,w_n]^T w=[b,w1,w2,...,wn]T,实际响应 y n y_n yn期望响应: d n d_n dn,学习率 η \eta η
其中,
d
n
=
{
+
1
x
∈
D
1
−
1
x
∈
D
2
d_n=\left\{
在学习过程需要注意的是,虽然感知器对线性可分模型一定收敛但是在实际应用中,需要慎重选取 η \eta η,希望稳定的更新就需要比较小的 η \eta η,可能速度过慢,希望快速更新就需要比较大的 η \eta η可能会出现更新过快震荡的情况。
贝叶斯分类器对于二分类问题(两个类别分别为
D
1
D_1
D1,
D
2
D_2
D2),其平均风险为:
R
=
c
11
p
1
∫
D
1
p
x
(
x
∣
D
1
)
d
x
+
c
22
p
2
∫
D
2
p
x
(
x
∣
D
2
)
d
x
+
c
21
p
1
∫
D
1
p
x
(
x
∣
D
1
)
d
x
+
c
12
p
2
∫
D
2
p
x
(
x
∣
D
2
)
d
x
其中:
令:
D
=
D
1
+
D
2
D=D_1+D_2
D=D1+D2,可以将上式改写为:
R
=
c
11
p
1
∫
D
1
p
x
(
x
∣
D
1
)
d
x
+
c
22
p
2
∫
D
−
D
1
p
x
(
x
∣
D
2
)
d
x
+
c
21
p
1
∫
D
1
p
x
(
x
∣
D
1
)
d
x
+
c
12
p
2
∫
D
−
D
1
p
x
(
x
∣
D
2
)
d
x
又因
c
11
<
c
21
,
c
22
<
c
12
c_{11}<c_{21},c_{22}<c_{12}
c11<c21,c22<c12且有
∫
D
p
x
(
x
∣
D
1
)
d
x
=
∫
D
p
x
(
x
∣
D
2
)
d
x
=
1
\int_Dp_x(x|D_1)dx=\int_Dp_x(x|D_2)dx=1
∫Dpx(x∣D1)dx=∫Dpx(x∣D2)dx=1
则上式简化为
R
=
c
21
p
1
+
c
22
p
2
+
∫
D
1
[
p
2
(
c
12
−
c
22
)
p
x
(
x
∣
D
2
)
−
p
1
(
c
21
−
c
11
)
p
(
x
)
(
x
∣
D
1
)
]
d
x
上式中第一项为固定项,为了最小化代价应该最小化第二项,因此最优的分类策列是将使得
p
x
(
x
∣
D
2
)
p_x(x|D_2)
px(x∣D2)越小越好,
p
x
(
x
∣
D
1
)
p_x(x|D_1)
px(x∣D1)越大越好,假设条件
p
1
(
c
21
−
c
11
)
p
x
(
x
∣
D
1
)
>
p
2
(
c
12
−
c
22
)
p
x
(
x
∣
D
2
)
定义
Λ
(
x
)
=
p
x
(
x
∣
D
1
)
p
x
(
x
∣
D
2
)
和
ξ
=
p
2
(
c
12
−
c
22
)
p
1
(
c
21
−
c
11
)
其中
Λ
\Lambda
Λ是拟然比,
ξ
\xi
ξ是检验阈值,二者恒正,则贝叶斯分类器可以表述为:
{
x
∈
D
1
,
Λ
(
x
)
>
ξ
x
∈
D
2
,
Λ
(
x
)
≤
ξ
很明显这个分类器和感知器的分类策略很相似,因此贝叶斯分类器和感知器等价。
对于高斯分布的情况存在下面的情况:
{
E
[
X
]
=
μ
1
,
E
[
(
X
−
μ
1
)
(
X
−
μ
1
)
T
]
=
C
X
∈
D
1
E
[
X
]
=
μ
2
,
E
[
(
X
−
μ
2
)
(
X
−
μ
2
)
T
]
=
C
X
∈
D
2
其中C为协方差,是非对角,即
D
1
D_1
D1
D
2
D_2
D2是相关的,假设C是非奇异的,即逆矩阵
C
−
1
C^{-1}
C−1存在。
x
x
x的条件概率密度韩式表示为多变量高斯分布为:
p
x
(
x
∣
D
1
)
=
1
(
2
π
)
m
/
2
(
Δ
(
C
)
)
1
/
2
e
−
1
2
(
x
−
μ
)
T
C
−
1
(
x
−
μ
1
)
,
i
=
1
,
2
,
m
为
x
的
维
数
可以进一步假设,数据时均衡的也就是分类成任何一个类的机会是等价的则有,且假设不同的错分类的代价相同,即:
p
1
=
p
2
=
1
2
c
21
=
c
12
,
c
11
=
c
22
=
0
则根据贝叶斯分类器中的情况可以得到
Λ
\Lambda
Λ和
ξ
\xi
ξ,并对其进行取对数有:
log
Λ
(
x
)
=
−
1
2
(
x
−
μ
1
)
T
C
−
1
(
x
−
μ
1
)
+
(
x
−
μ
2
)
T
C
−
1
(
x
−
μ
2
)
=
(
μ
1
−
μ
2
)
T
C
−
1
x
+
1
2
(
μ
2
T
C
−
1
μ
2
−
μ
1
T
C
−
1
μ
1
)
log
ξ
=
0
可以看到上述的表达式完全是一个线性的分类器的模型:
y
=
w
T
x
+
b
w
=
(
μ
1
−
μ
2
)
T
C
−
1
b
=
1
2
(
μ
2
T
C
−
1
μ
2
−
μ
1
T
C
−
1
μ
1
)
需要注意的是虽然高斯环境下的贝叶斯分类器和感知器类似,但是二者不同:
双月分类实验,目的是通过感知器对双月数据进行分类,实验分为两部分:第一部分为双月数据为线性可分的情况;第二部分为双月数据为线性不可分,非线性可分的情况:
如下图为线性可分的数据:
下图为分类过程的损失和结果代码(中间的线为决策边界,即
w
x
+
b
=
0
wx+b=0
wx+b=0),可以看到损失函数下降的很快,基本很快就到0了:
非线性数据:
分类结果,可以看到孫然損失函數很快收斂但是後面還是在不斷震蕩:
git链接:perceptron
双月数据生成代码:
# -*- coding: utf-8 -*- #生成半月数据 import numpy as np import matplotlib.pyplot as plt def halfmoon(rad, width, d, n_samp): '''生成半月数据 @param rad: 半径 @param width: 宽度 @param d: 距离 @param n_samp: 数量 ''' if n_samp%2 != 0: n_samp += 1 data = np.zeros((3,n_samp)) aa = np.random.random((2,int(n_samp/2))) radius = (rad-width/2) + width*aa[0,:] theta = np.pi*aa[1,:] x = radius*np.cos(theta) y = radius*np.sin(theta) label = np.ones((1,len(x))) # label for Class 1 x1 = radius*np.cos(-theta) + rad y1 = radius*np.sin(-theta) - d label1= -1*np.ones((1,len(x1))) # label for Class 2 data[0,:]=np.concatenate([x,x1]) data[1,:]=np.concatenate([y,y1]) data[2,:]=np.concatenate([label,label1],axis=1) return data def halfmoon_shuffle(rad, width, d, n_samp): data = halfmoon(rad, width, d, n_samp) shuffle_seq = np.random.permutation(np.arange(n_samp)) data_shuffle = data[:,shuffle_seq] return data_shuffle if __name__ == "__main__": dataNum = 1000 data = halfmoon(10,5,5,dataNum) pos_data = data[:,0: int(dataNum/2)] neg_data = data[:, int(dataNum/2):dataNum] np.savetxt('halfmoon.txt', data.T,fmt='%4f',delimiter=',') plt.figure() plt.scatter(pos_data[0,:],pos_data[1,:],c="b",s=10) plt.scatter(neg_data[0,:],neg_data[1,:],c="r",s=10) plt.savefig('./imgs/moon.png') plt.show()
感知器分类实验代码:
#通过感知机分类半月数据 import numpy as np import matplotlib.pyplot as plt def sgn(y): y[y > 0] = 1 y[y < 0] = -1 return y class Perceptron(object): '''单层感知机 ''' def __init__(self, shape): super(Perceptron, self).__init__() self.w = np.ones(shape) #weigth self.b = 1.5 #the bias self.activate_func = sgn def update(self,x,y,out,learning_rate): self.w += learning_rate * x.T * (y - out) def calclate(self, x): return self.activate_func(np.dot(self.w, x.T) + self.b) def loss_func(self, pre_y, gt_y): return (pre_y - gt_y) ** 2 def train(self, x, y, epochs, learning_rate): losses = [] for epoch in range(epochs): loss_tmp = [] for i in range(x.shape[0]): out = self.calclate(x[i]) loss_tmp.append(self.loss_func(out, y[i])) self.update(x[i], y[i], out, learning_rate) losses.append(sum(loss_tmp)/len(loss_tmp)) return losses def predict(self, x): out = self.calclate(x) return out def test(self, x,y): label = self.predict(x) gt_count = np.sum(label==y) wrong_count = np.sum(label!=y) return wrong_count/(wrong_count+gt_count),gt_count/(wrong_count+gt_count) def get_params(self): return {'weight':self.w, 'bias':self.b} def draw(self): axis = [i for i in range(1000)] out = [self.w * i + self.b for i in axis] plt.plot(axis, out) plt.show() def load_data(file): x = [] y = [] with open(file, 'r') as f: lines = f.readlines() for line in lines: line = line.strip().split(',') x_item = [float(line[0]), float(line[1])] y_item = float(line[2]) x.append(x_item) y.append(y_item) return np.array(x), np.array(y) def split_data(x, y): train_x, test_x = x[:int(x.shape[0]*0.7)], x[int(x.shape[0]*0.7):] train_y, test_y = y[:int(y.shape[0]*0.7)], y[int(y.shape[0]*0.7):] return train_x, train_y, test_x, test_y if __name__ == '__main__': #进行非线性数据的分类实验时,只需要将数据的间隔缩小保证二者重合即可 desc = 'nonlinear' file = './halfmoon.txt' x,y = load_data(file) train_x, train_y, test_x, test_y = split_data(x, y) neur = Perceptron((1,2)) losses = neur.train(train_x,train_y,100, 0.0001) err,acc = neur.test(test_x, test_y) print('rate of error:', err) print('rate of accuracy:', acc) #画损失曲线 axis = [i for i in range(len(losses))] plt.figure() plt.plot(axis, losses) plt.savefig('../imgs/%s_mse_loss.png' % desc) #plt.show() #画决策面 x_aixs = x[:,0] y_aixs = x[:,1] neg_x_axis = x_aixs[y==-1] neg_y_axis = y_aixs[y==-1] pos_x_axis = x_aixs[y==1] pos_y_axis = y_aixs[y==1] #感知机的参数 params = neur.get_params() w = params['weight'] b = params['bias'] k = -1 * w[0][0] / w[0][1] b = -1 * b / w[0][1] divid_x = [i for i in range(-15,25)] divid_y = [k * i + b for i in divid_x] plt.figure() plt.plot(divid_x, divid_y, c='r') plt.scatter(neg_x_axis,neg_y_axis,c="b",s=10) plt.scatter(pos_x_axis,pos_y_axis,c="g",s=10) plt.savefig('../imgs/%s_divide.png' % desc) #保存决策面
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。