《Python深度学习》Chapter 2——神经网络的数学基础

《Deep Learning with Python 》由Keras之父、现任Google人工智能研究员的弗朗索瓦•肖莱(François Chollet)执笔,详尽介绍了用Python和Keras进行深度学习的探索实践,涉及计算机视觉、自然语言处理、生成式模型等应用。
本书以读书笔记的形式摘抄书中的重点,会加入一些自己的备注和理解(标红及斜体)。
编程语言:Python
深度学习框架:Keras
书籍下载:《Python 深度学习》

注: 这本书,偏重于快速上手, 本身用keras写深度学习代码,就是类似搭积木一般,对于深度学习原理各部分讲解不多。
如果想稍深入的了解深度学习中的反向传播算法、归一化、优化算法等问题, 还需要学习吴恩达的机器学习教程:
吴恩达老师的深度学习系列视频
吴恩达老师深度学习笔记整理

要理解深度学习,需要熟悉很多简单的数学概念:张量、张量运算、微分、梯度下降等。本章目的是用不那么技术化的文字帮你建立对这些概念的直觉。特别地,我们将避免使用数学符号,因为数学符号可能会令没有任何数学背景的人反感,而且对解释问题也不是绝对必要的。

初识神经网络

神经网络示例,使用 Python 的 Keras 库来学习手写数字分类MNIST:

#1.加载 Keras 中的 MNIST 数据集
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
#查看训练数据
print(train_images.shape)    #(60000, 28, 28)
print(len(train_labels))     #60000
print(train_labels)    #array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)
#查看测试数据
print(test_images.shape)  #(10000, 28, 28)
print(len(test_labels))   #10000
print(test_labels)   #array([7, 2, 1, ..., 4, 5, 6], dtype=uint8)
#2.构建网络架构
from keras import models
from keras import layers
#本例中的网络包含 2 个 Dense 层,它们是密集连接(也叫全连接)的神经层。
#第二层(也是最后一层)是一个 10 路 softmax 层,
#它将返回一个由 10 个概率值(总和为 1)组成的数组。
#每个概率值表示当前数字图像属于 10 个数字类别中某一个的概率
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))

要想训练网络,我们还需要选择编译(compile)步骤的三个参数。

  • 损失函数(loss function):网络如何衡量在训练数据上的性能,即网络如何朝着正确的方向前进。
  • 优化器(optimizer):基于训练数据和损失函数来更新网络的机制。
  • 在训练和测试过程中需要监控的指标(metric):本例只关心精度accuracy,即正确分类的图像所占的比例。
#3.编译步骤
network.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])

在开始训练之前,我们将对数据进行预处理,将其变换为网络要求的形状,并缩放到所有值都在 [0, 1] 区间。比如,之前训练图像保存在一个 uint8 类型的数组中,其形状为(60000, 28, 28),取值区间为 [0, 255]。我们需要将其变换为一个 float32 数组,其形状为 (60000, 28 * 28),取值范围为 0~1。 即归一化处理

#4.准备图像数据
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255
#5.准备标签
from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

现在我们准备开始训练网络,在 Keras 中这一步是通过调用网络的 fit 方法来完成的——我们在训练数据上拟合(fit)模型。

#6.训练数据
network.fit(train_images, train_labels, epochs=5, batch_size=128)
output:
Epoch 1/5
60000/60000 [=============================] - 9s - loss: 0.2524 - acc: 0.9273
Epoch 2/5
51328/60000 [=======================>.....] - ETA: 1s - loss: 0.1035 - acc: 0.9692
#训练过程中显示了两个数字:
#一个是网络在训练数据上的损失(loss),另一个是网络在训练数据上的精度(acc)

等训练完成,在训练数据上达到了 0.989(98.9%)的精度。

test_loss, test_acc = network.evaluate(test_images, test_labels)
print('test_acc:', test_acc)
output:test_acc: 0.9785

测试集精度为 97.8%,比训练集精度低不少。训练精度和测试精度之间的这种差距是过拟合(overfit)造成的。

神经网络的数据表示:张量(tensor)

一般来说,当前所有机器学习系统都使用张量作为基本数据结构。张量对这个领域非常重要,重要到 Google 的TensorFlow 都以它来命名。

张量这一概念的核心在于,它是一个数据容器。它包含的数据几乎总是数值数据,因此它是数字的容器。你可能对矩阵很熟悉,它是二维张量。张量是矩阵向任意维度的推广。

  • 标量:0D张量 0轴
  • 向量:1D张量 1轴
  • 矩阵:2D张量 2轴
    -3D 张量更高维张量:深度学习处理的一般是 0D 到 4D 的张量,但处理视频数据时可能会遇到 5D 张量。

注意,张量的维度(dimension)通常叫作(axis),张量轴的个数也叫做(rank),这在 Numpy 等 Python 库中也叫张量的 ndim,可通过ndim属性查看轴的个数。

张量的关键属性

  1. 轴的个数(,ndim)。例如, 3D 张量有 3 个轴,矩阵有 2 个轴。
  2. 形状(shape)。这是一个整数元组,表示张量沿每个轴的维度大小(元素个数)。例如,矩阵的形状为 (3, 5), 3D 张量示例的形状为 (3, 3, 5)。向量的形状只包含一个元素,比如 (5,),而标量的形状为空,即 ()。
  3. 数据类型dtype)。这是张量中所包含数据的类型,例如,张量的类型可以是 float32、 uint8、 float64 等。在极少数情况下,你可能会遇到字符(char)张量。注意, Numpy(以及大多数其他库)中不存在字符串张量,因为张量存储在预先分配的连续内存段中,而字符串的长度是可变的,无法用这种方式存储。
print(train_images.ndim)   #3
print(train_images.shape) # (60000, 28, 28)
print(train_images.dtype) #uint8

在 Numpy 中操作张量

选择张量的特定元素叫作张量切片(tensor slicing):

print(train_images[10:100])  #(90, 28, 28)

数据批量的概念

通常来说,深度学习中所有数据张量的第一个轴(0 轴,因为索引从 0 开始)都是样本轴(samples axis,有时也叫样本维度)

#第 n 个批量。
batch = train_images[128 * n:128 * (n + 1)]

对于这种批量张量,第一个轴(0 轴)叫作批量轴(batch axis)或批量维度(batch dimension)。

现实世界中的数据张量

  • 向量数据: 2D 张量,形状为 (samples, features)。其中第一个轴是样本轴,第二个轴是特征轴。eg:人口统计数据集、文本文档数据集。
  • 时间序列数据或序列数据: 3D 张量,形状为 (samples,timesteps, features)。eg:股票价格数据集、推文数据集。在这里插入图片描述
  • 图像(彩色): 4D 张量,形状为 (samples, height, width, channels) 或 (samples, channels,height, width)。图像张量的形状有两种约定: 通道在后(channels-last)的约定(在 TensorFlow 中使用)和通道在前(channels-first)的约定(在 Theano 中使用)。 在这里插入图片描述
  • 视频: 5D 张量,形状为 (samples, frames, height, width,channels) 或 (samples,frames, channels, height, width)。视频数据是现实生活中需要用到 5D 张量的少数数据类型之一。举个例子,一个以每秒 4 帧采样的 60 秒 YouTube 视频片段,视频尺寸为 144×256,这个视频共有 240 帧。 4 个这样的视频片段组成的批量将保存在形状为 (4, 240, 144, 256, 3)的张量中。

神经网络的“齿轮”:张量运算tensor operation

逐元素(element-wise)运算

relu 运算和加法都是逐元素的运算,即该运算独立地应用于张量中的每个元素,也就是说,这些运算非常适合大规模并行实现(向量化实现),避免使用for循环。

在实践中处理 Numpy 数组时,这些运算都是优化好的 Numpy 内置函数,这些函数将大量运算交给安装好的基础线性代数子程序BLAS, basic linear algebra subprograms)实现,BLAS 是低层次的、高度并行的、高效的张量操作程序,通常用 Fortran或 C 语言来实现。

广播(broadcast)

如果将两个形状不同的张量相加,会发生什么?
如果没有歧义的话,较小的张量会被广播(broadcast),以匹配较大张量的形状。广播包含以下两步。
(1) 向较小的张量添加轴(叫作广播轴),使其 ndim 与较大的张量相同。
(2) 将较小的张量沿着新轴重复,使其形状与较大的张量相同。

张量点积\张量积(tensor product)

点积运算,也叫张量积(tensor product,不要与逐元素的乘积弄混),是最常见也最有用的张量运算。

在 Numpy 和 Keras 中,都是用标准的 dot 运算符来实现点积:

import numpy as np
z = np.dot(x, y)

数学符号中的点(.)表示点积运算:

z=x.y

点积可以推广到具有任意个轴的张量。最常见的应用可能就是两个矩阵之间的点积。
对于两个矩阵 x 和 y,当且仅当 x.shape[1] == y.shape[0] 时,你才可以对它们做点积(dot(x, y))。得到的结果是一个形状为 (x.shape[0], y.shape[1]) 的矩阵,其元素为 x的行与 y 的列之间的点积。如下:
在这里插入图片描述
更一般地说,你可以对更高维的张量做点积,只要其形状匹配遵循与前面 2D 张量相同的原则:
(a, b, c, d) . (d,) -> (a, b, c)
(a, b, c, d) . (d, e) -> (a, b, c, e)

张量变形(tensor reshaping)

张量变形是指改变张量的行和列,以得到想要的形状。变形后的张量的元素总个数与初始张量相同。

x = np.array([[0., 1.],[2., 3.],[4., 5.]])
print(x.shape)  #(3, 2)
x = x.reshape((6, 1))
print(x)
# array([[ 0.],[ 1.],[ 2.],[ 3.],[ 4.],[ 5.]])
x = x.reshape((2, 3))
print(x)
#array([[ 0., 1., 2.],[ 3., 4., 5.]])

一种特殊的张量变形是转置(transposition)。对矩阵做转置是指将行和列互换,使 x[i, :] 变为 x[:, i]。常用,尤其线性回归的正规方程

x = np.zeros((300, 20))
x = np.transpose(x)
print(x.shape)  #(20, 300)

张量运算的几何解释

对于张量运算所操作的张量,其元素可以被解释为某种几何空间内点的坐标,因此所有的张量运算都有几何解释。如两个张量相加:
在这里插入图片描述
通常来说,仿射变换、旋转、缩放等基本的几何操作都可以表示为张量运算。

深度学习的几何解释

神经网络完全由一系列张量运算组成,而这些张量运算都只是输入数据的几何变换。因此,你可以将神经网络解释为高维空间中非常复杂的几何变换,这种变换可以通过许多简单的步骤来实现。

想象有两张彩纸:一张红色,一张蓝色。将其中一张纸放在另一张上。现在将两张纸一起揉成小球。这个皱巴巴的纸球就是你的输入数据,每张纸对应于分类问题中的一个类别。神经网络(或者任何机器学习模型)要做的就是找到可以让纸球恢复平整的变换,从而能够再次让两个类别明确可分。
在这里插入图片描述
深度学习将复杂的几何变换逐步分解为一长串基本的几何变换,这与人类展开纸球所采取的策略大致相同。深度网络的每一层都通过变换使数据解开一点点——许多层堆叠在一起,可以实现非常复杂的解开过程。

神经网络的“引擎”:基于梯度的优化

如果按照sklearn中网格搜索的方式,设定参数的值,进行神经网络的前向传播,计算损失值,可这样计算量巨大,并且需要人工参与。

一种更好的方法是利用网络中所有运算都是可微(differentiable)的这一事实,计算损失相对于网络系数的梯度(gradient),然后向梯度的反方向改变系数,从而使损失降低。

导数 derivative

对于每个可微函数 f(x)(可微的意思是“可以被求导”。例如,光滑的连续函数可以被求导),都存在一个导数函数 f’(x),将 x 的值映射为 f 在该点的局部线性近似的斜率导数)。例如, cos(x)的导数是 -sin(x), f(x) = a * x 的导数是 f’(x) = a,等等。 严格的数学定义,是采用双边逼近的思想
在这里插入图片描述

张量运算的导数:梯度 gradient

梯度(gradient)是张量运算的导数。它是导数这一概念向多元函数导数的推广。多元函数是以张量作为输入的函数。

单变量函数 f(x) 的导数可以看作函数 f 曲线的斜率。同样, gradient(f)
(W0) 也可以看作表示 f(W) 在 W0 附近曲率(curvature)的张量。

随机梯度下降

用解析法求出最小损失函数对应的所有权重值。可以通过对方程 gradient(f)(W) = 0 求解 W 来实现这一方法。
在这里插入图片描述
为 step 因子(步长)选取合适的值是很重要的。如果取值太小,则沿着曲线的下降需要很多次迭代,而且可能会陷入局部极小点。如果取值太大,则更新权重值之后可能会出现在曲线上完全随机的位置。
在这里插入图片描述

  • 小批量随机梯度下降(mini-batch stochastic gradient descent,
    又称为小批量 SGD)。术语随机(stochastic)是指每批数据都是随机抽取的(stochastic 是 random在科学上的同义词 ):每次迭代时抽取一批数据。吴恩达教程中每批量b建议在[2,100],一般取10,但是在实际操作中,一般用batch size表示,GPU对2的幂次的batch可以发挥更佳的性能,一般都取64等,具体要视具体情况而定
  • 真SGD:每次迭代时只抽取一个样本和目标;
  • 批量SGD:每一次迭代都在所有数据上运行。

此外, SGD 还有多种变体,其区别在于计算下一次权重更新时还要考虑上一次权重更新,而不是仅仅考虑当前梯度值,比如带动量的 SGDAdagradRMSProp 等变体。这些变体被称为优化方法(optimization method)或优化器(optimizer)。其中动量的概念尤其值得关注,它在许多变体中都有应用。动量解决了 SGD 的两个问题:收敛速度局部极小点

动量的思想:灵感来源于物理学。有一种有用的思维图像,就是将优化过程想象成一个小球从损失函数曲线上滚下来。如果小球的动量足够大,那么它不会卡在峡谷里,最终会到达全局最小点。动量方法的实现过程是每一步都移动小球,不仅要考虑当前的斜率值(当前的加速度),还要考虑当前的速度(来自于之前的加速度)。这在实践中的是指,更新参数 w 不仅要考虑当前的梯度值,还要考虑上一次的参数更新。

优化算法在吴恩达的深度学习课程中有专门细讲,感兴趣的朋友可以参考:优化算法

链式求导:反向传播算(backpropagation)

链式法则(chain rule): (f(g(x)))’ = f’(g(x)) * g’(x)。将链式法则应用于神经网络梯度值的计算,得到的算法叫作反向传播(backpropagation,有时也叫反式微分, reverse-mode differentiation)。反向传播从最终损失值开始,从最顶层反向作用至最底层,利用链式法则计算每个参数对损失值的贡献大小。

人们将使用能够进行符号微分(symbolic differentiation)的现代框架来实现神经网络,比如 TensorFlow。也就是说,给定一个运算链,并且已知每个运算的导数,这些框架就可以利用链式法则来计算这个运算链的梯度函数,将网络参数值映射为梯度值

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值