深度学习笔记2-神经网络的基本内容

「学习内容总结自 coursera 和 udacity 的深度学习课程,部分截图来自 udacity 的课件」

一.最简单的神经网络及其输出

深度学习笔记2-神经网络的基本内容_第1张图片

如上图所示是最简单的神经网络模型,包含输入x,神经元(又称为感知器)和输出y。
对这个神经网络再具体一点就是下图所示,对输入的x给与权重(在其他较复杂的神经网络中,往往有很多个输入,一个较大的权重意味着神经网络认为这个输入比其它输入更重要,较小的权重意味着该数据不是那么重要)和偏置,并把中间的神经元细分为左右两部分,左半边为神经元的输入,右半边为神经元的输出。


深度学习笔记2-神经网络的基本内容_第2张图片

神经元的输入为:z=wx+b,其中w表示为权重,b表示偏置;神经元的输出为a=g(z)=g(wx+b),其中g(z)表示激活函数。最终该神经网络的输出y=g(z)=g(wx+b)

二.多元(层)神经网络及其输出

深度学习笔记2-神经网络的基本内容_第3张图片

拿上面这张图举个例子,x1,x2是输入层(layer0);中间的两层(layer1,layer2)称为 隐藏层,隐藏层里的单元称为 隐藏单元;y(layer3)是 输出层。所以这个多元神经网络也称为 3层神经网络
接下来也像上面一样增加权重w和偏置b,并对神经元进行细分。

PS:[ ]内的数字表示层数,下标数字表示该层网络中的单元数

  • 对于layer1:
    深度学习笔记2-神经网络的基本内容_第4张图片

    即有(Z,W,b都是矩阵):
  • 对于layer2:
  • 对于layer3:
  • 对于该3层网络的输出y:
  • 推广至L层网络:

三.神经网络的激活函数

参考:浅谈深度学习中的激活函数

1. 什么是激活函数

上面出现的g(z)就是激活函数(activation function),激活函数不是为了“激活”什么,只是在神经网络中添加一些非线性的学习和处理能力,解决线性模型所不能解决的复杂问题。

2. 为什么使用非线性的激活函数

在深层的神经网络中,如果隐藏层仍然使用线性的激活函数,经过网络层层传递,其计算结果仍然是线性的,这与没有添加隐藏层是一样的效果,这样做的话,“深度”反而是没有意义的,并不能帮助我们解决复杂性的问题。所以在构建深度神经网络的时候,要引入非线性的因素,赋予神经元自我学习和适应的能力,从而提高模型的学习能力,能够处理复杂的非线性的数据集问题。

3. 常见的激活函数

常见激活函数有对数几率(又称作 sigmoid),tanh函数 ,ReLU函数和ReLU的变形-leakyReLU函数。

  • sigmoid函数
import numpy as np
def sigmoid(x):
    return 1/(1+np.exp(-x))
深度学习笔记2-神经网络的基本内容_第5张图片
sigmoid.png
  • tanh函数
import numpy as np
def tanh(x):
    return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
深度学习笔记2-神经网络的基本内容_第6张图片
tanh.png
  • ReLU函数
import numpy as np
def relu(x):
    return np.maximum(0,x)
深度学习笔记2-神经网络的基本内容_第7张图片
ReLU.png
  • ReLU的变形-leaky ReLU函数
import numpy as np
def leaky_relu(x):
    return np.maximum(0.01*x,x)
深度学习笔记2-神经网络的基本内容_第8张图片
leaky_ReLU.png

四.用代码实现一个简单的神经网络

运用上面的知识,实现一个简单的神经网络,它有两个输入节点,一个输出节点,使用sigmoid函数。代码实现过程:

import numpy as np

def sigmoid(x):
    # 实施sigmoid函数
    return 1/(1+np.exp(-x))

inputs = np.array([0.7, -0.3]) #输入
weights = np.array([0.1, 0.8]) #权重
bias = -0.1 #偏置

# 计算神经网络的输出
output = sigmoid(np.dot(inputs,weights)+bias)

五.神经网络的梯度下降

PS: 此处为了简便,忽略了偏置 b

  • 误差函数
    给神经网络输入x,我们需要其输出的预测值ŷ近似的等于期望值y。为了能够衡量,我们需要有一个指标来了解预测的好坏,也就是误差(error)。常用的指标有误差平方:
  • 梯度下降法
    要使网络预测的误差尽可能小,我们所要做的就是尽可能的使这个指标要小,即要找出它的最小值。权重 w 是让我们能够实现这个目标的调节旋钮。我们的目的是寻找权重 w​ 使得误差平方 E 最小。通常来说神经网络通过梯度下降法来实现这一点。
    所谓梯度下降法就是,沿着能够使误差最小化的方向一步步更新权重 w 来减小误差(error)。要找到误差最小化的方向,我们需要计算误差平方对于权重 w 的梯度(对单个输入变量来说是导数,梯度是多个输入变量的导数的泛化)。
  • 梯度下降的数学实现过程

我们用这个公式来更新权重 w :

其中w表示权重更新步长,式子如下:


η 表示learning rate,有些地方也用α表示。
接下来运用 链式法则求误差平方 E 对权重 w 的偏导数:

又因为

所以偏差公式化解为:

在这里定义一下误差项(error term) δ :

综上,权重 w 更新的公式为:


以上过程自己推导一边,写代码就会神清气爽:)

  • 梯度下降的代码实现过程
    假设只有一个输出单元,用 sigmoid 来作为激活函数 f(h),代码实现的过程如下:
import numpy as np

def sigmoid(x):
    """
    计算sigmoid
    """
    return 1/(1+np.exp(-x))

def sigmoid_prime(x):
    """
    # sigmoid函数的导数,后面调用作为输出的梯度
    """
    return sigmoid(x) * (1 - sigmoid(x))

learnrate = 0.5
x = np.array([1, 2, 3, 4])
y = np.array(0.5)

# 初始化权重
w = np.array([0.5, -0.5, 0.3, 0.1])

# 计算节点的输入
h = np.dot(w,x)

# 计算神经网络的输出
nn_output = sigmoid(h)

# 计算神经网络的误差
error = y-nn_output 

# 计算误差项 sigmoid_prime(h)为输出的梯度
error_term = error*sigmoid_prime(h)

# w
del_w = learnrate*error_term*x

# 权重更新
w = w + del_w

六.神经网络的反向传播

  • 反向传播
    前面我们已经学习了一个简单神经网络的输出层的误差项 δ ,并用它来更新权重 w 。但对于多层神经网络(以一个两层网络为例),前面的做法更新的只是隐藏层-输出层的权重,对于输入层-隐藏层的权重,我们该如何使用误差项 δ 来更新?这个误差项又该如何计算得出?
    深度学习笔记2-神经网络的基本内容_第9张图片
    利用链式法则我们可以计算输入层-隐藏层间权重的误差项:
    因为输出层的误差项是由隐藏层的权重决定的,把这个误差项当做输入反推回网络,就能像前面逐层正向的输出结果一样逐层地反向推导出误差项。这种类似正向传输的过程我们称之为反向传播
    深度学习笔记2-神经网络的基本内容_第10张图片

反向传播是训练神经网络的基本原理,因此对于构建深度学习模型,理解反向传播至关重要。- 引自udacity课件

  • 反向传播的实现
    反向传播的实现包括正向和反向两个操作。正向操作是利用权重和输入,从输入层到输出层,计算出预测值ŷ;反向操作则是利用预测值ŷ,从输出层到输入层,求解出误差,误差项和w(权重更新步长),最后更新权重,完成一次迭代。
    代码实现过程如下:
import numpy as np
from data_prep import features, targets, features_test, targets_test

np.random.seed(21)

def sigmoid(x):
    """
    Calculate sigmoid
    """
    return 1 / (1 + np.exp(-x))


# Hyperparameters
n_hidden = 2  # number of hidden units
epochs = 900
learnrate = 0.005

n_records, n_features = features.shape
last_loss = None
# Initialize weights
weights_input_hidden = np.random.normal(scale=1 / n_features ** .5,
                                        size=(n_features, n_hidden))
weights_hidden_output = np.random.normal(scale=1 / n_features ** .5,
                                         size=n_hidden)

for e in range(epochs):
    del_w_input_hidden = np.zeros(weights_input_hidden.shape)
    del_w_hidden_output = np.zeros(weights_hidden_output.shape)
    for x, y in zip(features.values, targets):
        ## 正向操作 ##
        # TODO: Calculate the output
        hidden_input = np.dot(x,weights_input_hidden)
        hidden_output = sigmoid(hidden_input)
        output = sigmoid(hidden_output)

        ## 反向操作##
        # TODO: Calculate the network's prediction error
        error = y-output

        # TODO: Calculate error term for the output unit
        output_error_term = error*output*(1-output)

        ## propagate errors to hidden layer

        # TODO: Calculate the hidden layer's contribution to the error
        hidden_error = weights_hidden_output*output_error_term
        
        # TODO: Calculate the error term for the hidden layer
        hidden_error_term = hidden_error*hidden_output*(1-hidden_output)
        
        # TODO: Update the change in weights
        del_w_hidden_output += output_error_term*hidden_output
        del_w_input_hidden += hidden_error_term*x[:,None]

    # TODO: Update weights
    weights_input_hidden += del_w_input_hidden*learnrate/n_records
    weights_hidden_output += del_w_hidden_output*learnrate/n_records

你可能感兴趣的:(深度学习笔记2-神经网络的基本内容)