深度学习入门笔记(二)

神经网络

神经网络的一个重要的性质就是它可以自动地从数据中学习到合适的权重参数。

一.从感知机到神经网络

感知机与神经网络存在很多的共同点。
下图为一种简单的全连接网络形式
在这里插入图片描述

神经网络的例子

其中,我们把最左边一列称为输入层,最右边一列称为输出层,中间的一列称为中间层隐藏层)。

1.激活函数

一般,我们将把输入信号的总和转换为输出信号的函数称为激活函数(activation function)。
在这里插入图片描述

激活函数的计算过程

如上图所示,b为偏置,w1、w2为权重,神经元的○中明确显示了激活函数的计算过程,即信号的加权总和为节点a,然后节点a被激活函数h()转换成节点y。

  • sigmoid函数
    神经网络中经常使用的一个激活函数就是sigmoid函数。
    在这里插入图片描述
    式子中的exp(−x)表示e−x的意思。e是纳皮尔常数2.7182…。
    神经网络中用sigmoid函数作为激活函数来进行信号的转换,转换后的信号被传递给下一个神经元。实际上,感知机和神经网络主要区别就在于这个激活函数。其他方面,比如神经元的多层连接的构造、信号的传递方法等,基本上和感知机是一样的。
  • 阶跃函数的实现
def step_function(x):
    if x > 0:
        return 1
    else:
        return 0

这个实现虽然简单,易于理解,但是参数x只能接受实数(浮点数),不允许参数取Numpy数组,可考虑下面的实现方法。

def step_function(x):
    y = x > 0
    return y.astype(np.int)

上面的代码会将输入的numpy数组的各个元素通过y = x > 0转换为True或者False,此时y是一个布尔型数组,然后利用astype()方法转换为Numpy数组的类型。astype()方法通过参数指定期望的类型,这个例子中是np.int型,将布尔型转换为int型后,True会转换为1,False转换为0.
下面来看一看节约函数的图形

import numpy as np
import matplotlib.pylab as plt

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)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)  # 指定y轴的范围
plt.show()

其中np.arange(-5.0, 5.0, 0.1)在-5.0到5.0的范围内,以0.1为单位,生成NumPy数组。step_function()以该NumPy数组为参数,对数组的各个元素执行阶跃函数的运算,并以数组形式返回运算结果。对数组x、y进行绘图。
在这里插入图片描述

  • sigmoid函数的实现
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

之所以sigmoid寒素的实现能够支持NumPy数组,在于NumPy的广播功能,如果标量和NumPy数组之间进行运算,则标量会和NumPy数组的各个元素进行运算。
下面把sigmoid函数画出来

import numpy as np
import matplotlib.pylab as plt

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

x = np.arange([1.0, 2.0, 3.0])
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()

在这里插入图片描述

  • sigmoid和阶跃函数的对比:

    • 首先是平滑性的不同,sigmoid函数是一条光滑的曲线,输出随着输入发生连续性的变化。而阶跃函数以0为界,输出发生急剧性的变化。sigmoid的函数对后面要学习的神经网络的训练和学习很有帮助。
    • 另一个是,相对于阶跃函数之恩返回0和1,sigmoid函数可以返回多个分散的实数。也就是说,感知机中神经元之间流动的是0或1的二元信号,而神经网络中流动的是实数值信号。
    • 一个共同点是,虽然二者在平滑性上有差异,但是宏观上看,它们的形状相似
    • 另一个共同点是,不服按输入信号有多小,或者有多大,输出信号的值都在0到1之间。
  • ReLU函数
    在看relu之前,我们先明确线性函数及非线性函数这个概念。即上述学到的阶跃函数和sigmoid函数均为非线性函数。函数本来是输入某个值后会返回一个值的转换器。向这个转换器输入某个值后,输出值是输入值的常数倍的函数称为线性函数。因此线性函数表现为一条笔直的直线。神经网络的激活函数必须要使用非线性函数。原因在于:如果使用线性函数,不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”,无法发会多层网络带优势。为了具体地(稍微直观地)理解这一点,我们来思 考下面这个简单的例子。这里我们考虑把线性函数h(x) = cx作为激活 函数,把y(x) = h(h(h(x)))的运算对应3层神经网络A。这个运算会进行 y(x) = c×c×c×x的乘法运算,但是同样的处理可以由y(x) = ax(注意, a = c 3)这一次乘法运算(即没有隐藏层的神经网络)来表示。简而言之如果使用线性函数,加深神经网络变得毫无意义。

ReLU函数在输入大于0时,直接输出该值;在输入小于等于0时,输出0。
在这里插入图片描述

def relu(x):
    return np.maximum(0, x)

在这里插入图片描述

这里使用了numpy的maximum函数。maximum函数会从输入的数值中选择较大的那个值进行输出。

二. 3层神经网络的实现

在这里插入图片描述

3层神经网络:输入层(第0层)有2个神经元,第1个隐藏层(第1层)有3个神经元,第2个隐藏层有2个神经元,输出层(第3层)有2个神经元。

首先我们来确认以下要用到的符号
在这里插入图片描述

1.各层间信号传递的实现

先来看从输入层到第1层的第1个神经元的信号传递过程。
在这里插入图片描述

图中增加了表示偏置的神经元“1”.注意,偏置的右下角的索引号只有一个。这是因为前一层的偏置神经元(神经元“1”)只有一个。通过加权信号和偏 置的和按如下方式进行计算。
在这里插入图片描述

如果使用矩阵的乘法运算,可以将第一层的加权和表示为下面的式子。
在这里插入图片描述

下面我们用NumPy多维数组来实现式(3.9),这里将输入信号、权重、 偏置设置成任意值。

X = np,array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])

print(W1.shape) # (2, 3)
print(X.shape)  # (2,)
print(B1.shape) # (3,)

A1 = np.dot(X, W1) + B1

W1是2×3的数组,X是元素个 数为2的一维数组。这里,W1和X的对应维度的元素个数也保持了一致。
接下来观察第1层中激活函数的计算过程。
在这里插入图片描述

隐藏层的加权和(加权信号和偏置的总和)用a表示,被 激活函数转换后的信号用z表示。此外,图中h()表示激活函数,这里我们使用的是sigmoid函数。用Python来实现,代码如下所示。

Z1 = sigmoid(A1)

print(A1) # [0.3, 0.7, 1.1]
print(Z1) # [0.57444252, 0.66818777, 0.75026011]

第1层到第2层的信号传递与前两段代码同理。
在这里插入图片描述

W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]]) 
B2 = np.array([0.1, 0.2])
print(Z1.shape) # (3,) 
print(W2.shape) # (3, 2) 
print(B2.shape) # (2,)

A2 = np.dot(Z1, W2) + B2 
Z2 = sigmoid(A2)

最后是第2层到输出层的信号传递。输出层所使用的激活函数与前两层有所不同。这里使用了恒等函数
在这里插入图片描述

def identity_function(x):
    return x
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])

A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3) # 或者Y = A3

这里我们定义了identity_function()函数(也称为“恒等函数”),并将 其作为输出层的激活函数。恒等函数会将输入按原样输出,因此,这个例子 中没有必要特意定义identity_function()。这里这样实现只是为了和之前的流程保持统一。

  • 输出层所用的激活函数,要根据求解问题的性质决定。一般的,回归问题可以使用恒等函数,二元分类问题可以使用sigmoid函数,多元分类问题可以使用softmax函数。
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

network = init_network() 
x = np.array([1.0, 0.5]) 
y = forward(network, x) 
print(y) # [ 0.31682708  0.69627909]

这里定义了init_network()和forward()函数.init_network()函数会进 行权重和偏置的初始化,并将它们保存在字典变量network中。这个字典变量network中保存了每一层所需的参数(权重和偏置)。forward()函数中则封装了将输入信号转换为输出信号的处理过程。

另外,这里出现了forward(前向)一词,它表示的是从输入到输出方向 的传递处理。后面在进行神经网络的训练时,我们将介绍后向(backward,从输出到输入方向)的处理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值