神经网络机器学习

机器学习之神经网络

2019-01-15  本文已影响21人  Vophan

什么是神经网络

就是使用了平滑的激活函数的多层感知机

激活函数

什么是激活函数呢?

激活函数就是从输入值到对应输出值的映射关系


image.png

这是阶跃函数的图像

对比二者,我们可以发现阶跃函数只有0,1两种状态,而sigmoid可以取到之间的每一个值,这也是感知机与神经网络的区别


相同之处:

阶跃函数和 sigmoid 函数虽然在平滑性上有差异,但是如果从宏观视角看图 3-8,可以发现它们具有相似的形状。实际上,两者的结构均是“输入小时,输出接近 0(为 0);随着输入增大,输出向 1 靠近(变成 1)”。

还有,阶跃函数与sigmoid都是非线性函数6

而且,这里只能使用非线性函数,为什么不能使用线性函数?

因为如果使用线性函数就无法体现多层网络带来的优势。

比如:

我们使用线性函数
h(x) = c*x
我们构建了三层网络,但是最后一层输出值是什么呢?
h(x) = c^3*x
这么做没有任何意义。

\begin{equation} h(x)=\left\{ \begin{array}{**lr**} x\qquad(x>0)\\ 0\qquad(x\leq0)\\ \end{array} \right. \end{equation}
实现也很简单:

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函数


损失函数:

用来度量模型性能好坏程度的指标

通常使用:

均方误差和交叉熵误差


接着损失函数,我还想说一下:

mini-batch学习,我们刚才说道损失函数,对于一定数量的数据集,我们要求取损失函数,就要把每一个样本的损失函数加到一起在除以N,进行规范化。

但是有一个问题就是,如果数据量特别的大,那么这个过程就变得很耗时,所以我们要采用mini-batch的学习方法,对数据集进行抽样,来计算损失函数。


为什么要设置损失函数?

我们来理一下这个过程,就是我们要通过训练数据来得到最好的参数值,权重和偏置,但是我们怎么知道现在选取的参数值的好坏呢?

我们就是通过损失函数来量度模型的好坏,同样的这样也给我们的训练有了一个目标,就是尽可能的让损失变得最小。

可是我们怎么才能让损失函数最小呢?

这就用到了梯度下降法。通过对参数的梯度的判断,比如说在我们取到某一个参数的时候,他的梯度(导数)不等于零,那么我们就要在这个点的梯度下降的点取点,知道参数对应的梯度为0

梯度,就是对应每一个参数的偏导数汇总到一起的向量就是梯度
\Bigl(\frac{\partial f}{\partial x_0},\frac{\partial f}{\partial x_1}\Bigr)%20\Bigl(\frac{\partial%20f}{\partial%20x_0},\frac{\partial%20f}{\partial%20x_1}\Bigr%29)


其实,损失函数就是对于输出层的一个说法。

只有在输出层才有输出值与监督值的对比。


对于梯度的求法其实就是:

对于每个参数求导数,导数怎么求,就是去一个特别短的区间:
f(x) = \frac{f(x+h)-f(x)}{h}
这个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

实验结果表明,学习率过大的话,会发散成一个很大的值;反过来,学习率过小的话,基本上没怎么更新就结束了。也就是说,设定合适的学习率是一个很重要的问题。


误差反向传播:

根据链式法则:

我们可以知道每一个计算图上的误差都等于上游传来的局部导数*当前的局部导数。

上一篇下一篇

猜你喜欢

热点阅读