前言
本文将介绍感知机和神经网络相关的基本知识
一、感知机
1、感知机是什么
感知机接收多个输入信号,输出一个信号,这里的信号像电流一样有流动性。感知机的信号只有0/1两种取值,0 表示不传递信号,1表示传递信号。
这里x1,x2是输入信号,y是输出信号,w1和w2是权重,b是偏置用于控制神经元被激活的容易程度。
2、用感知机实现逻辑电路
1)与门:两个输入值均为1时结果为1,其他情况下都为0。与门的真值表如下
代码实现
def AND(x1,x2):
w1,w2,theta = 0.5,0.5,0.7
tmp = x1*w1 + x2*w2
if(tmp <= theta):
return 0
else:
return 1
改进后的与门
def AND(x1,x2):
x = np.array([x1,x2])
w = np.array([0.5,0.5])
b = -0.7
tmp = np.sum(x*w) + b
if(tmp <= 0):
return 0
else:
return 1
2)与非门:与门对应的结果取反。只有两个输入值均为1时结果为0,其他情况下都为1。与非门的真值表如下
代码实现部分和与门的类似,只需要修改权重w和偏置b的值即可。
def NAND(x1,x2):
x = np.array([x1, x2])
w = np.array([-0.5, -0.5])
b = 0.7
tmp = np.sum(x * w) + b
if (tmp <= 0):
return 0
else:
return 1
或门:当输入的值均为0的时候结果为0,其他情况下均为1。或门的真值表如下
代码实现部分和与门的类似,只需要修改偏置b的值为-0.2
def OR(x1,x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.2
tmp = np.sum(x * w) + b
if (tmp <= 0):
return 0
else:
return 1
综上:通过设置w和b不同的值来实现与门、与非门、或门。
注意:感知机可以表示与门、与非门、或门的逻辑电路,但是不能表示异或门。
异或门可以通过两层感知机实现(与非门 和 或门),“叠加层”实现异或门是重点!
输入信号x1,x2经过与非门得到 s1, 经过或门得到s2,然后s1,s2经过与门得到输出信号y。异或门对应真值表如下,当两个输入信号相同是结果为0,不同为1。
代码实现直接调用上面实现的与非门、或门、与门的函数即可
def XOR(x1,x2):
s1 = NAND(x1,x2)
s2 = OR(x1,x2)
y = AND(s1,s2)
return y
print(XOR(1,1)) #0
二、神经网络
从上面感知机实现与门、非门、与非门中可以得知,唯一不同的是需要人工设置
不同的权重和偏置参数;而神经网络可以自动的
从数据中学习到合适的权重参数。
神经网络的结构
神经网络是由输入层、中间层和输出层组成的
我们可以将上面感知机的公式改写成下面更简洁的公式
这里的h()函数就是激活函数,下面介绍几种常见的激活函数。
1、激活函数
1)阶跃函数
感知机采用的是阶跃函数作为激活函数,当输入超过一定阈值的时候会激活输出
简单实现,但只能接收实数
def step_function(x):
if x > 0: return 1
else: return 0
改进后,允许接收Numpy数组作为参数
def step_function(x):
return np.array(x>0,dtype = np.int)
绘制图像
x = np.arange(-5.0,5.0,0.1)
y = step_function(x) #改成sigmod 或 Lelu 可得到下面的两种激活函数对应的图像
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()
2)sigmod函数
def sigmod(x):
return 1 / (1 + np.exp(-x))
图像
sigmod VS 阶跃函数
不同点:sigmod平滑输出的是连续的0~1的实数值信号,阶跃函数输出0或者1的二元信号。
相同点:sigmod和阶跃函数都是非线性函数
3)Leru函数
1)数学表达
若x > 0则取自身,否则取0;这里直接用的Numpy 的 maximum()函数,从输入的值中选择大的输出
2)代码
def relu(x):
return np.maximum(0,x)
3)图像
2、实现三层神经网络
初始化神经网络
def init_network():
network = {}
network['W1'] = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
network['b1'] = np.array([0.1,0.2,0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5],[0.3,0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
前向处理
def forward(network,x):
W1,W2,W3 = network['W1'],network['W2'],network['W3']
b1,b2,b3 = network['b1'],network['b2'],network['b3']
a1 = np.dot(x,W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1,W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2,W3) + b3
y = identity_function(a3)
return y
其中identity_function恒等函数作为输出层的激活函数,一般回归问题(例如预测房价)用恒等函数,分类问题(例如图像中的人是男性还是女性)用softmax函数
def identity_function(x):
return x
调用
network = init_network()
x = np.array([1.0,0.5])
y = forward(network,x)
print(y) #[0.31682708 0.69627909]
3、输出层函数softmax
数学公式:
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a #这里用到了numpy数组的广播用法,即对exp_a中每一个元素都进行除法运算
return y
指数函数输入值很大时导致溢出问题
a = np.array([1010,1000,990])
print(np.exp(a)/np.sum(np.exp(a))) #结果[nan nan nan]
改进
c = np.max(a)
print(np.exp(a-c)/np.sum(np.exp(a - c))) #结果修正 [9.99954600e-01 4.53978686e-05 2.06106005e-09]
改进后的softmax
def softmax(a):
c = np.max(a)
exp_a = np.exp(a - c)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
softmax重要特征:输出总和是1,softmax的输出是0.0~1.0的实数,理解为概率
a = np.array([0.3,2.0,4.0])
print(sum(softmax(a))) # 1.0
4、应用 - 手写数字识别
使用的是MNIST数据集,这里数据集下载地址http://yann.lecun.com/exdb/mnist
在跑源码的过程中urllib.request.urlretrieve(url_base + file_name, file_path)
执行这一句代码报错urllib.error.HTTPError: HTTP Error 403: Forbidden
http请求错误,资源下载未成功。解决后再更新,欢迎有遇到同样问题的小伙伴指教~
更新:上次下载遇到了网络问题,看了下源码,如果在本地项目中已存在目的文件路径就不会去官网下载对应的数据集。附上os模块学习网址
os.path.exists(path)
如果 path 指向一个已存在的路径或已打开的文件描述符,返回 True。对于失效的符号链接,返回 False。
所以尝试将需要的文件放在指定的目录下,这里将用到的数据集放在dataset目录下。数据集的4个文件在网盘里分享给大家。
通过百度网盘分享的文件:手写数字识别
链接:https://pan.baidu.com/s/1WZ2oIRToDoHWp6soLBh-Tw?pwd=6mep
提取码:6mep
复制这段内容打开「百度网盘APP 即可获取」
然后python mnist_show.py 执行文件可以正常运行,并输出结果
总结
主要介绍了感知机和神经网络的基本知识,包括常见的几种激活函数这个在后面的学习中肯定还会深入接触。以及这输出层激活函数的应用场景,适用于分类还是回归。一般来说,输出层 回归问题
用恒等函数
,分类问题
用softmax函数
。