第3章 单层感知器与线性神经网络
3.1 生物神经网络
一个多输入单输出非线性系统的动态过程
3.2 单层感知器
3.2.1单层感知器介绍
f(x)称为激活函数,可以理解为信号在轴突上进行的线性或非线性变化。
sign(x)激活函数
当x>0 时,输出值为1;当x=0 时,输出值为0,;当x<0 时,输出值为-1
3.2.2单层感知器计算举例
假如有一个单层感知器有3 个输入
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3,同时已知b=-0.6,
𝑤
1
=
𝑤
2
=
𝑤
3
𝑤_1=𝑤_2=𝑤_3
w1=w2=w3=0.5,
单层感知器的计算公式𝑓(Σi(𝑤i𝑥i) + 𝑏),则:
若
𝑥
1
=
0
,
𝑥
2
=
0
,
𝑥
3
𝑥_1=0, 𝑥_2=0, 𝑥_3
x1=0,x2=0,x3=0:sign(0.5 × 0 + 0.5 × 0 + 0.5 × 0 − 0.6) = −1
若
𝑥
1
=
0
,
𝑥
2
=
0
,
𝑥
3
𝑥_1=0, 𝑥_2=0, 𝑥_3
x1=0,x2=0,x3=1:sign(0.5 × 0 + 0.5 × 0 + 0.5 × 1 − 0.6) = −1
3.2.3单层感知器的另一种表达形式
偏置也作为一个输入神经元,b =
x
0
w
0
x_0w_0
x0w0 =
w
0
w_0
w0
表达形式更加简洁,更适合使用矩阵来进行运算。
3.3 单层感知器的学习规则
x1,x2,…xn对应于w1,w2,…wn等权值,然后利用公式$\Delta w = \eta(\hat y - y) $ 不断调整,更新权值,知道与真实值一样为止。
3.3.1单层感知机的学习规则介绍
3.3.2单层感知机的学习规则计算举例
见下面代码段1,用两种方法展现,一个是普通算法,一个是向量化,很明显向量化高效。
3.4 学习率
(1)学习率𝜼取值一般取0-1之间;
(𝟐)学习率太大容易造成权值调整不稳定;
(3)学习率太小,模型参数调整太慢,迭代次数太多。
3.5模型的收敛条件
通常模型的收敛条件可以有以下3 个:
(1)loss小于某个预先设定的较小的值;
(2)两次迭代之间权值的变化已经很小了;
(3)设定最大迭代次数,当迭代超过最大次数就停止。
3.6模型的超参数和参数的区别
超参数:人为设置的一些模型相关的参数。比如说前面提到的学习率,学习
率需要根据经验来人为设置。比如模型的迭代次数,也是需要在模型训练之前预先进行人为设
置。
参数:模型中需要训练的变量。如权值和偏置。
3.7单层感知器分类案例
题目:假设我们有4 个2 维的数据,数据的特征分别是(3,3),(4,3),(1,1),(2,1)。(3,3),(4,3)
这两个数据的标签为1,(1,1),(2,1)这两个数据的标签为-1。构建神经网络来进行分类。
-
通常数据有几个特征(几维)就设几个输入神经元,偏置可以做一个输入神经元也可以不做
-
向量化同时计算所有数据的预测值,利用矩阵乘法,维度为保持一致,可以加一个1,在乘法过程中,会发现转化为偏置。
-
,但是使用单层感知器来做分类的时候,最后得到的分类边界距离某一个类别比较近,而距离另一个类别比较远,并不是一个特别理想的分类效果。图3.7 中的分类效果应该才是比较理想的分类效果,分界线在两个类别比较中间的位置。
代码见下面。(第三个)
3.8 线性神经网络
3.8.1线性神经网络介绍
线性神经网络跟单层感知器非常类似,只是把单层感知器的sign激活函数改成了purelin
函数:
𝑦 = 𝑥
3.8.2线性神经网络分类案例
3.9 线性神经网络处理异或问题
- 异或规则:
(1) 0与0异或等于0;
(2) 0与1异或等于1;
(3) 1与0异或等于1;
(4) 1与1异或等于0。
总结:相同的为0,相异的为1
- 线性神经网络引入非线性解决异或问题
线性神经网络解决不了异或问题,在图像中可以很明显的看出一条直线是不可能将两个类别划分开的,必须引入非线性特征,非线性特征可以从原有特征拼凑。
代码见下面:引入非线性特征前与引入非线性特征后的对比。
#单层感知器学习规则计算举例(普通计算)
#导入 numpy科学计算包
import numpy as np
#定义输入
x0 = 1
x1 = 0
x2 = -1
#定义权值
w0 = -5
w1 = 0
w2 = 0
#定义正确的标签
t = 1
#定义学习率lr(learning rate)
lr = 1
#定义偏置值
b = 0
#循环一个比较大的次数,比如100
for i in range(100):
#打印权值
print(w0,w1,w2)
#计算感知器的输出
y = np.sign(w0*x0 + w1*x1 + w2*x2)
#如果感知器的输出不等于正确的标签
if(y != t):
#更新权值
w0 = w0 + lr*(t-y)*x0
w1 = w1 + lr*(t-y)*x1
w2 = w2 + lr*(t-y)*x2
#如果感知器输出等于正确的标签
else:
# 训练结束
print('done')
#退出循环
break
-5 0 0
-3 0 -2
-1 0 -4
done
#单层感知器学习规则计算举例(矩阵运算)
#导入numpy科学计算包
import numpy as np
#定义输入,用大写字母表示矩阵
#一般我们习惯用一行来表示一个数据,如果存在多个数据就用多行来表示
X = np.array([[1,0,-1]])
#定义权值,用大写字母表示矩阵
#神经网络中权值的定义可以参考神经网络的输入是输出神经元的个数
#在本例子中输入神经元个数是3个,输出神经元个数是1个,所以可以定义3行1列的W
W = np.array([[-5],
[0],
[0]])
#定义正确的标签
t = 1
#定义学习率lr(learning rate)
lr = 1
#定义偏置值
b = 0
#循环一个比较大的次数,比如100
for i in range(100):
#打印权值
print(W)
#计算感知器的输出,np.dot可以看做是矩阵乘法
y = np.sign(np.dot(X,W))
#如果感知器输出不等于正确的标签
if(y != t):
#更新权值
#X.T表示X矩阵的转置
#这里一个步骤可以完成代码3-1中下面3行代码完成的事情
#w0 = w0 + lr*(t-y)*x0
#w1 = w1 + lr*(t-y)*x1
#w2 = w2 + lr*(t-y)*x2
W = W + lr*(t-y)*X.T
#如果感知器输出等于正确的标签
else:
#训练结束
print('done')
#退出循环
break
[[-5]
[ 0]
[ 0]]
[[-3]
[ 0]
[-2]]
[[-1]
[ 0]
[-4]]
done
#单层感知器案例
import numpy as np
import matplotlib.pyplot as plt
#定义输入,我们习惯上用一行代表一个数据
X = np.array([[1,3,3],
[1,4,3],
[1,1,1],
[1,2,1]])
#定义标签,我们习惯上用一行表示一个数据的标签
T = np.array([[1],
[1],
[-1],
[-1]])
#权值初始化,3行1列
#np.random.random可以生成0-1的随机数
#括号里填的是数据形状,可以用[],也可以用()
W = np.random.random([3,1])
#print(W)
#学习率设置
lr = 0.1
#神经网络输出(初始化预测值为0)
Y = 0
#更新一次权值
def train():
#使用全局变量W
global W
#同时计算4个数据的预测值
#Y的形状为(4,1)-4行1列
Y = np.sign(np.dot(X,W))
# T - Y 得到4个的标签值与预测值的误差E。形状为(4,1)
E = T - Y
#X.T表示X的转置矩阵,形状为(3,4)
# 我们一共有4 个数据,每个数据3 个值。定义第i 个数据的第j 个特征值为xij
# 如第1 个数据,第2 个值为x12
# X.T.dot(T - Y)为一个3 行1 列的数据:
# 第1 行等于:x00×e0+x10×e1+x20×e2+x30×e3,它会调整第1 个神经元对应的权值
# 第2 行等于:x01×e0+x11×e1+x21×e2+x31×e3,它会调整第2 个神经元对应的权值
# 第3 行等于:x02×e0+x12×e1+x22×e2+x32×e3,它会影调整3 个神经元对应的权值
# X.shape 表示X的形状X.shape[0]得到X的行数,表示有多少个数据
# X.shape[1]得到列数,表示每个数据有多少个特征值。
# 这里的公式跟书中公式3.2看起来有些不同,原因是这里的计算是矩阵运算,书中公式3.2是单个元素的计算。如果在草稿子上仔细推算的话你会发现它们的本质是一样的
delta_W = lr*(X.T.dot(E))/X.shape[0]
W = W + delta_W
#训练100次
for i in range(100):
#更新一次权值
train()
#打印当前训练次数
print('epoch:',i+1)
#打印当前权值
print('weighets:',W)
#计算当前输出
Y = np.sign(np.dot(X,W))
#.all()表示Y中的所有值跟T中所有值都对应相等
if(Y==T).all():
print("当前周期数:",i+1)
print("当前权值:",W)
print("当前结果:",Y)
#print("finished")
print('Finished')
#跳出循环
break
epoch: 1
weighets: [[ 0.2335144 ]
[-0.08645354]
[-0.07396917]]
epoch: 2
weighets: [[0.2835144 ]
[0.21354646]
[0.17603083]]
epoch: 3
weighets: [[0.1835144 ]
[0.06354646]
[0.07603083]]
epoch: 4
weighets: [[ 0.0835144 ]
[-0.08645354]
[-0.02396917]]
epoch: 5
weighets: [[0.1835144 ]
[0.26354646]
[0.27603083]]
epoch: 6
weighets: [[0.0835144 ]
[0.11354646]
[0.17603083]]
epoch: 7
weighets: [[-0.0164856 ]
[-0.03645354]
[ 0.07603083]]
epoch: 8
weighets: [[-0.0664856 ]
[-0.08645354]
[ 0.02603083]]
epoch: 9
weighets: [[0.0335144 ]
[0.26354646]
[0.32603083]]
epoch: 10
weighets: [[-0.0664856 ]
[ 0.11354646]
[ 0.22603083]]
epoch: 11
weighets: [[-0.1664856 ]
[-0.03645354]
[ 0.12603083]]
当前周期数: 11
当前权值: [[-0.1664856 ]
[-0.03645354]
[ 0.12603083]]
当前结果: [[ 1.]
[ 1.]
[-1.]
[-1.]]
Finished
#--------------------以下为画图部分--------------------#
#正样本的xy坐标
x1 = [3,4]
y1 = [3,3]
#负样本的xy坐标
x2 = [1,2]
y2 = [1,1]
#计算分类边界线的斜率以及截距
#神经网络的信号总和为w0*x0+w1*x1+w2*x2
#当信号总和大于0再进过激活函数,模型的预测值会得到1
#当信号总和小于0再进过激活函数,模型的预测值会得到-1
#所以当信号总和w0*x0+w1*x1+w2*x2=0时为分类边界线表达式
#我们在画图的时候把x1,x2分别看成是平面坐标系中的x和y
#可以得到,w0+w1*x+w2*y = 0
#经过通分:y = -w0/w2 - w1*x/w2,因此可以得到:
k = -W[1]/W[2]
d = -W[0]/W[2]
print(k,d)
#设定两个点
xdata = (0,5)
#通过两个点来确定一条直线,用红色的线来画出分界线
plt.figure(figsize=(6.8,4.5),dpi=200)
plt.plot(xdata,xdata*k+d,'r')
#用蓝色的点画出正样本
sample_p = plt.scatter(x1,y1,c='b')
#用黄色的点来画出负样本
sample_n = plt.scatter(x2,y2,c='y')
#添加图例
#设置图例并且设置图例的字体及大小
font1 = {'family' : 'Times New Roman',
'weight' : 'bold',
'size' : 11,
}
legend = plt.legend(handles=[sample_p,sample_n],labels=["positive sample","negative sample"],loc="lower right",prop=font1)
#plt.legend(handles=[sample_p,sample_n],labels=["positive sample","negative sample"],loc="lower right",fontsize=12)
#显示图案
#plt.savefig("D:\\yyx\\图片\\python_fig1.svg",dpi=500)
plt.show()
[0.28924306] [1.32099103]
#线性神经网络--处理异或问题
#(1)---旧模型无法解决异或问题
import numpy as np
import matplotlib.pyplot as plt
#输入数据
#4个数据分别对应0与0异或,0与1异或,1与0异或,1与1异或
X = np.array([[1,0,0],
[1,0,1],
[1,1,0],
[1,1,1]])
#标签,分别对应4种异或情况的结果
#注意这里我们使用-1作为负标签
T = np.array([[-1],
[1],
[1],
[-1]])
#权值初始化,3行1列
#np.random.random可以生成0-1的随机数
W = np.random.random([3,1])
#学习率设置
lr = 0.1
#神经网络输出
Y = 0
#更新一次权值
def train():
#使用全局变量W
global W
#计算网络预测值
Y = np.dot(X,W)
#计算权值的改变
delta_W = lr*(X.T.dot(T-Y))/X.shape[0]
#更新权值
W = W + delta_W
#训练100次
for i in range(100):
#更新一次权值
train()
#计算预测结果并打印
Y = np.dot(X,W)
print(Y)
#---------------------以下为画图部分-----------------#
#正样本
x1 = [0,1]
y1 = [1,0]
#负样本
x2 = [0,1]
y2 = [0,1]
#计算分界线的斜率以及截距
# ̄へ ̄#
k = -W[1]/W[2]
d = -W[0]/W[2]
#设定两个点
xdata = (-2,3)
#通过两个点来确定一条直线,用红色的线来画出分界线
plt.figure(figsize=(3.2,4.5),dpi=200)
plt.plot(xdata,xdata*k+d,'r')
#用蓝色的点画出正样本
plt.scatter(x1,y1,c='b')
#用黄色的点来画出负样本
plt.scatter(x2,y2,c='y')
#添加图例
#设置图例并且设置图例的字体及大小
font1 = {'family' : 'Times New Roman',
'weight' : 'bold',
'size' : 7.5,
}
legend = plt.legend(handles=[sample_p,sample_n],labels=["positive sample","negative sample"],loc="lower right",prop=font1)
#保存图片
#plt.savefig("D:\\yyx\\图片\\python_fig2.svg",dpi=500)
#显示图案
plt.show()
[[-0.1089797 ]
[-0.00796449]
[-0.02623974]
[ 0.07477546]]
#线性神经网络映入非线性特征解决异或问题
import numpy as np
import matplotlib.pyplot as plt
#输入数据
#原来X的3个特征分别为:x0,x1,x2
#X = np.array([[1,0,0],
# [1,0,1],
# [1,1,0],
# [1,1,1]])
# 给网络输入非线性特征
# 现在X的6个特征分别为:x0,x1,x2,x1*x1,x1*x2,x2*x2
X = np.array([[1,0,0,0,0,0],
[1,0,1,0,0,1],
[1,1,0,1,0,0],
[1,1,1,1,1,1]])
#标签,分别对应4种异或情况的结果
T = np.array([[-1],
[1],
[1],
[-1]])
#权值初始化,6行1列
#np.random.randon可以生成0-1的随机数
W = np.random.random([6,1])
#学习率设置
lr = 0.1
#神经网络输出
Y = 0
#更新一次权值
def train():
#使用全局变量W
global W
#计算网络预测值
Y = np.dot(X,W)
#计算误差
E = T-Y
#计算权值的改变
delta_W = lr * (X.T.dot(T - Y)) / X.shape[0]
#delta_W = lr*np.dot(X.T,E)/X.shape[0]
#更新权值
W = W+delta_W
#训练1000次
for i in range(1000):
#更新一次权值
train()
#计算预测结果并打印
Y = np.dot(X,W)
print(Y)
#-------------------以下为画图部分-------------------#
#正样本
x1 = [0,1]
y1 = [1,0]
#负样本
x2 = [0,1]
y2 = [0,1]
#神经网络信号的总合为:w0x0+w1x1+w2x2+w3x1x1+w4x1x2+w5x2x2
#当w0x0+w1x1+w2x2+w3x1x1+w4x1x2+w5x2x2=0时为分类边界线(-1与1肯定在0两边)
#其中x0为1,我们可以把x1,x2分别看成是平面坐标洗中的x和y
#可以得到:w0+w1x+w2y+w3xx+w4xy+w5yy = 0
#通分可得:w5y^2 + (w2+w4x)y + w0 + w1x + w3x^2 = 0
#其中a = w5,b = w2+w4x,c = w0 +w1x + w3x^2
#根据一元二次方程的求根公式:ay^2+b^y+c=0,y=[-b+/-(b^2-4ac)^(1/2)]/2a
def calculate(x,root):
#定义参数
a = W[5]
b = W[2]+x*W[4]
c = W[0]+x*W[1]+x*x*W[3]
#有两个根
if root == 1:
return (-b+np.sqrt(b*b-4*a*c))/(2*a)
if root==2:
return (-b-np.sqrt(b*b-4*a*c))/(2*a)
#从-1到2之间均匀生成100个点
xdata = np.linspace(-1,2,100)
#使用第一个求根公式计算出来的结果画出第一条红线
plt.figure(figsize=(3.2,4.5),dpi=200)
plt.plot(xdata,calculate(xdata,1),'r')
#使用第二个求根公式计算出来的结果画出第二条红线
plt.plot(xdata,calculate(xdata,2),'r')
#蓝色点表示正样本
plt.plot(x1,y1,'bo')
#黄色点表示负样本
plt.plot(x2,y2,'yo')
#添加图例
#设置图例并且设置图例的字体及大小
font1 = {'family' : 'Times New Roman',
'weight' : 'bold',
'size' : 7.5,
}
legend = plt.legend(handles=[sample_p,sample_n],labels=["positive sample","negative sample"],loc="lower right",prop=font1)
#保存图片
#plt.savefig("D:\\yyx\\图片\\python_fig2.svg",dpi=500)
#绘图
plt.show()
[[-0.9864406 ]
[ 0.99094536]
[ 0.99094536]
[-0.99299371]]