机器学习之神经网络
什么是神经网络
就是使用了平滑的激活函数的多层感知机
激活函数
什么是激活函数呢?
激活函数就是从输入值到对应输出值的映射关系
-
阶跃函数
-
sigmoid函数
image.png
这就是sigmoid函数图像
这是阶跃函数的图像
对比二者,我们可以发现阶跃函数只有0,1两种状态,而sigmoid可以取到之间的每一个值,这也是感知机与神经网络的区别
相同之处:
阶跃函数和 sigmoid 函数虽然在平滑性上有差异,但是如果从宏观视角看图 3-8,可以发现它们具有相似的形状。实际上,两者的结构均是“输入小时,输出接近 0(为 0);随着输入增大,输出向 1 靠近(变成 1)”。
还有,阶跃函数与sigmoid都是非线性函数6
而且,这里只能使用非线性函数,为什么不能使用线性函数?
因为如果使用线性函数就无法体现多层网络带来的优势。
比如:
我们使用线性函数
我们构建了三层网络,但是最后一层输出值是什么呢?
这么做没有任何意义。
-
ReLu函数
{88%}
实现也很简单:
def relu(x):
return np.maximum(0,x)
刚才看了一下pytorch的教程视频,他提到了这个激活函数,有一点新的理解:
为啥激活函数必须是非线性函数?
一部分原因是前面说的,为了发挥多层网络的优势,但是这么说其实还是很模糊,其实就是线性函数在对输入值的加权和(以及偏差)分类时,做的太差,所以只能引入非线性函数解决这个问题。
补充:
在手写神经网络的时候,对偏置有一些新的理解:
偏置其实表示的是每一个神经元的激活的容易程度,那么这么看来其实就是每个神经元都有一个偏置,而对于输入层的神经元,其实偏置并没有什么卵用。
但是在表示的时候,我们可以发现,第一层偏置其实对应的是第二层的神经元的偏置。
代码:
import numpy as np
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 sigmoid(x):
return 1/(1+np.exp(-x))
def identity_function(x):
return x
def forword(network, x):
w1, w2, w3 = network["w1"], network["w2"], network["w3"]
b1, b2, b3 = network["b1"], network["b2"], network["b3"]
# print(b1,b2,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
network = init_network()
x = np.array([5.0,3.0])
y = forword(network, x)
print(y)
补充:
输出层的激活函数不同于前几层的激活函数,
对于回归问题,使用恒等函数,对于分类问题,使用softmax函数
损失函数:
用来度量模型性能好坏程度的指标
通常使用:
均方误差和交叉熵误差
-
均方误差
-
交叉熵误差
交叉熵误差就得提到one-hot表示方法,tk就是one-hot表示的标签向量,(如果第二个标签为真,则向量为:[0,1,0,0...0])
yk是神经网络的输出,输出越大,交叉熵误差越小,也是说这个标签是正确的可能性越大。
实际上,使用的时候,会给Yk加一个delta值,防止Yk趋于零的时候,log趋于无穷。
接着损失函数,我还想说一下:
mini-batch学习,我们刚才说道损失函数,对于一定数量的数据集,我们要求取损失函数,就要把每一个样本的损失函数加到一起在除以N,进行规范化。
但是有一个问题就是,如果数据量特别的大,那么这个过程就变得很耗时,所以我们要采用mini-batch的学习方法,对数据集进行抽样,来计算损失函数。
为什么要设置损失函数?
我们来理一下这个过程,就是我们要通过训练数据来得到最好的参数值,权重和偏置,但是我们怎么知道现在选取的参数值的好坏呢?
我们就是通过损失函数来量度模型的好坏,同样的这样也给我们的训练有了一个目标,就是尽可能的让损失变得最小。
可是我们怎么才能让损失函数最小呢?
这就用到了梯度下降法。通过对参数的梯度的判断,比如说在我们取到某一个参数的时候,他的梯度(导数)不等于零,那么我们就要在这个点的梯度下降的点取点,知道参数对应的梯度为0
梯度,就是对应每一个参数的偏导数汇总到一起的向量就是梯度
其实,损失函数就是对于输出层的一个说法。
只有在输出层才有输出值与监督值的对比。
对于梯度的求法其实就是:
对于每个参数求导数,导数怎么求,就是去一个特别短的区间:
这个h我们取做1e-4,之前取做1e-5,但是后来存在一个舍入误差,就是当数据特别小的时候,会被计算机处理时当成0。
而且为了减小误差,我们还采用了中心差分的方法来求取导数。
梯度的方向是数值下降最快的方向。
梯度法
enter image description hereη 是学习率,learning rate就是确定每一次梯度下降多少(多大程度更新参数)
在下降过程中,参数会不断更新,learning data既不能太大也不能太小。
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f, x)
x -= lr * grad
return x
实验结果表明,学习率过大的话,会发散成一个很大的值;反过来,学习率过小的话,基本上没怎么更新就结束了。也就是说,设定合适的学习率是一个很重要的问题。
误差反向传播:
根据链式法则:
我们可以知道每一个计算图上的误差都等于上游传来的局部导数*当前的局部导数。