深度学习


什么是深度学习

前不久,我在机器学习专栏介绍了机器学习的相关概念。机器学习应用在日常生活中的方方面面,且我认为如今已经不需要进一步举例说明了。事实上,无论什么类型的机器学习问题,都会遇到如下这些组件

  1. 可以用来学习的数据(Data)
  2. 如何转换数据的模型(Model)
  3. 一个目标函数(Objective Function),用来量化模型的有效性
  4. 调整模型参数以优化目标函数的算法(Algorithm)

机器学习问题往往可以分为以下几大类型

  • 监督学习
  • 无监督学习
  • 强化学习
  • 深度学习

深度学习(Deep Learning)特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。在机器学习日志(五)中,我简单介绍了一个基础神经网络模型的训练与使用,然而其只是神经网络方法的冰山一角。

20世纪中后期,神经网络模型已经得到初步发展,但是其模仿生物神经网络的特点注定了对数据训练集、算力的依赖性。大约从2010年开始,那些在计算上看起来不可行的神经网络算法变得热门起来,实际上是以下两点导致的

  1. 互联网的普及使得优质、大规模的数据集更加容易获取
  2. 廉价算力、廉价数据存储的普及,以及GPU算力的普及和发展

近些年来,深度神经网络模型已经深刻融入我们的日常生活和社会发展

  • 智能设备的数字助理,如SIRI
  • 物体视觉识别,如人脸识别、无人驾驶
  • 游戏产业的发展,如AI敌人,AI优化算法
  • 个性化推荐系统,如Google、Amazon等平台

深度学习的一个关键优势是它不仅取代了传统学习管道末端的浅层模型,而且还取代了劳动密集型的特征工程过程。此外,通过取代大部分特定领域的预处理,深度学习消除了以前分隔计算机视觉、语音识别、自然语言处理、医学信息学和其他应用领域的许多界限,为解决各种问题提供了一套统一的工具。

日志安排

在正式开始深度学习领域的学习之前,建议先了解或学习以下内容

  • 高等数学、线性代数、概率论与数理统计基础
  • 机器学习基础
  • Python语法基础
  • GPU算力与云端算力

本日志按照李沐大佬的《动手学深度学习v2》课程,按照下图顺序进行知识梳理与代码梳理。

有关课程链接和资料,请移步至文章末尾♪(・ω・)ノ

对于上述提到的部分课程基础,可参考本篇日志的后半部分,了解这些已经暂时足够了。此外,相比于机器学习篇,本次开荒的深度学习篇将融入大量的Pytorch代码实践知识,以培养课程的动手能力。

Pytorch


Pytorch介绍

PyTorch 是一个开源的,基于 Python 语言的机器学习库,主要用于进行深度学习领域如计算机视觉、自然语言处理、语音识别等方向研究和开发。PyTorch 主要有以下几大优势特征

  • 动态计算图:PyTorch 的计算图是动态的,这意味着它们在运行时构建,并且可以随时改变。这为实验和调试提供了极大的灵活性,因为开发者可以逐行执行代码,查看中间结果
  • 自动微分:PyTorch 的自动微分系统允许开发者轻松地计算梯度,这对于训练深度学习模型至关重要。它通过反向传播算法自动计算出损失函数对模型参数的梯度
  • 张量计算:PyTorch 提供了类似于 NumPy 的张量操作,这些操作可以在 CPU 和 GPU 上执行,从而加速计算过程。张量是 PyTorch 中的基本数据结构,用于存储和操作数据
  • 丰富的 API:PyTorch 提供了大量的预定义层、损失函数和优化算法,这些都是构建深度学习模型的常用组件
  • 多语言支持:PyTorch 虽然以 Python 为主要接口,但也提供了 C++ 接口,允许更底层的集成和控制

与 PyTorch 类似的深度学习框架还有 TensorFlow 等,本课程主要以前者为主。

使用与安装

PyTorch 的安装教程可以参考B站小土堆教程,支持多系统的 CPU 和 GPU 计算。由于 PyTorch 是一个 Python 的包,所以最简单的方式便是通过终端 pip 的形式下载。读者可以根据个人的环境配置选择对应的下载版本。

这里不再赘述安装方法,仅仅来介绍如何验证 PyTorch 的安装。在当前虚拟环境创建 .py 文件,键入并运行

1
2
3
# 注意包名是 torch 而不是 PyTorch
import torch
print(torch.__version__)

输出结果为安装的 PyTorch 版本时,则代表安装成功。对于 GPU 版本的安装,需要测试 CUDA 是否安装且与当前 PyTorch 版本、Python 解释器版本是否兼容。键入并运行

1
2
import torch
print(torch.cuda.is_available())

输出结果为 True 则代表环境配置成功。

机器学习中主要使用的数据结构为 nn 维数组。比如,一个数字表示一个标量,一维数组表示一个向量,二维数组表示一个矩阵,三维数组表示一个图片的RGB数值等等。我们并称这些数组为张量(Tensor),其是 PyTorch 使用的数据结构基础。

数学基础


线性代数

一个列向量 x\boldsymbol{x} 通常被描述为 Rn\mathbb{R}^n 空间的有序数组,记作

x=(x1,x2,,xn)T\boldsymbol{x}=(x_1,x_2,\cdots,x_n)^T

向量 x\boldsymbol{x}L2L_2 范数定义为

x2=i=1nxi2\|\boldsymbol{x}\|_2=\sqrt{\sum_{i=1}^nx_i^2}

对于两个同维度的列向量 a,b\boldsymbol{a},\boldsymbol{b},它们的内积或点乘记作

a,b=aTb=i=1naibi\langle \boldsymbol{a},\boldsymbol{b}\rangle=\boldsymbol{a}^T\boldsymbol{b}=\sum_{i=1}^n a_ib_i

当两个向量的内积为0时,记他们相互正交

一个 m×nm\times n 的矩阵可以看做 nnmm 维列向量组成的二维数组,记作

X=[x1x2xn]=[x11x12x1nx21x22x2nxm1xm2xmn]\boldsymbol{X}=\begin{bmatrix}\boldsymbol{x_1}&\boldsymbol{x_2}&\cdots&\boldsymbol{x_n}\end{bmatrix}=\begin{bmatrix}x_{11}&x_{12}&\cdots&x_{1n}\\x_{21}&x_{22}&\cdots&x_{2n}\\\vdots&\vdots&\ddots&\vdots\\x_{m1}&x_{m2}&\cdots&x_{mn}\end{bmatrix}

m=nm=n 时,称该矩阵为一个方阵。矩阵 Am×s\boldsymbol{A}_{m\times s}Bs×n\boldsymbol{B}_{s\times n} 的点乘是一个 m×nm\times n 的矩阵,且有

(Am×sBs×n)ij=k=1saikbkj(\boldsymbol{A}_{m\times s}\cdot\boldsymbol{B}_{s\times n})_{ij}=\sum_{k=1}^s a_{ik}b_{kj}

称满足 QTQ=I\boldsymbol{Q}^T\boldsymbol{Q}=\boldsymbol{I} 的矩阵为正交矩阵,其中 QT\boldsymbol{Q}^T 表示矩阵 Q\boldsymbol{Q} 的转置,I\boldsymbol{I} 是同阶单位矩阵。特别地,如果一个矩阵的转置等于其自身,则是一个对称矩阵。元素是实数的对称方阵 A\boldsymbol{A} 一定可以分解为如下形式

A=QΛQT\boldsymbol{A}=\boldsymbol{Q\Lambda Q}^T

其中 Q\boldsymbol{Q} 是正交矩阵,Λ\boldsymbol{\Lambda} 是一个对角矩阵,其对角元素都是方阵 A\boldsymbol{A}特征值 λi(i=1,2,,n)\lambda_i(i=1,2,\cdots,n),这些特征值满足

det(AλiI)=0\det(\boldsymbol{A}-\lambda_i\boldsymbol{I})=0

矩阵 Q=[q1q2qn]\boldsymbol{Q}=\begin{bmatrix}\boldsymbol{q_1}&\boldsymbol{q_2}&\cdots&\boldsymbol{q_n}\end{bmatrix} 满足 Aqi=λiqi\boldsymbol{Aq}_i=\lambda_i\boldsymbol{q}_i。特征值有如下性质

i=1nλi=trA=i=1naii,i=1nλi=det(A)\sum_{i=1}^n\lambda_i=\mathrm{tr}\boldsymbol{A}=\sum_{i=1}^na_{ii},\quad \prod_{i=1}^n\lambda_i=\det(\boldsymbol{A})

矩阵求导

对于一元可导函数 y=f(x)y=f(x),其导数定义为

yx=limh0f(x+h)f(x)h\frac{\partial y}{\partial x}=\lim_{h\to 0}\frac{f(x+h)-f(x)}{h}

函数在某一点的导数可以看做在该点的斜率。中学学习的求导对象都是标量,当将其拓展为向量甚至矩阵时,其定义会有所变化。下假定出现的向量 xRn,yRm\boldsymbol{x}\in\mathbb{R}^n,\boldsymbol{y}\in\mathbb{R}^m。当自变量是向量,因变量是标量时,其导数定义为

yx=[yx1yx2yxn]R1×n\frac{\partial y}{\partial \boldsymbol{x}}=\begin{bmatrix}\dfrac{\partial y}{\partial x_1}&\dfrac{\partial y}{\partial x_2}&\cdots&\dfrac{\partial y}{\partial x_n}\end{bmatrix}\in\mathbb{R}^{1\times n}

nn 元可导函数 y=f(x)y=f(\boldsymbol{x}),其在 x=x0\boldsymbol{x}=\boldsymbol{x}_0 处的梯度定义为

xyx0=yxx0=[yx1yx2yxn]x0R1×n\nabla_{\boldsymbol{x}}y\Big|_{\boldsymbol{x}_0}=\frac{\partial y}{\partial \boldsymbol{x}}\Bigg|_{\boldsymbol{x}_0}=\begin{bmatrix}\dfrac{\partial y}{\partial x_1}&\dfrac{\partial y}{\partial x_2}&\cdots&\dfrac{\partial y}{\partial x_n}\end{bmatrix}\Bigg|_{\boldsymbol{x}_0}\in\mathbb{R}^{1\times n}

注意,这是一个行向量,而不是列向量。

aa 是常数,u,vu,v 是关于 x\boldsymbol{x} 的函数。下表给出了几个常用的求导结果

aa auau xi\displaystyle\sum x_i i=1nxi2\sqrt{\displaystyle\sum_{i=1}^nx_i^2} u+vu+v uvuv
0T\boldsymbol{0}^T auxa\dfrac{\partial u}{\partial \boldsymbol{x}} 1T\boldsymbol{1}^T 2xT2\boldsymbol{x}^T ux+vx\dfrac{\partial u}{\partial \boldsymbol{x}}+\dfrac{\partial v}{\partial \boldsymbol{x}} uxv+vxu\dfrac{\partial u}{\partial \boldsymbol{x}}v+\dfrac{\partial v}{\partial \boldsymbol{x}}u

当自变量是标量,因变量是向量时,其导数定义为

yx=[y1xy2xymx]TRm\frac{\partial \boldsymbol{y}}{\partial x}=\begin{bmatrix}\dfrac{\partial y_1}{\partial x}&\dfrac{\partial y_2}{\partial x}&\cdots&\dfrac{\partial y_m}{\partial x}\end{bmatrix}^T\in\mathbb{R}^m

当自变量和因变量都是向量时,其导数定义为

yx=[y1xy2xymx]=[y1x1y1x2y1xny2x1y2x2y2xnymx1ymx2ymxn]Rm×n\frac{\partial \boldsymbol{y}}{\partial \boldsymbol{x}}=\begin{bmatrix}\dfrac{\partial y_1}{\partial \boldsymbol{x}}\\\dfrac{\partial y_2}{\partial \boldsymbol{x}}\\\vdots\\\dfrac{\partial y_m}{\partial \boldsymbol{x}}\end{bmatrix}=\begin{bmatrix}\dfrac{\partial y_1}{\partial x_1}&\dfrac{\partial y_1}{\partial x_2}&\cdots&\dfrac{\partial y_1}{\partial x_n}\\\dfrac{\partial y_2}{\partial x_1}&\dfrac{\partial y_2}{\partial x_2}&\cdots&\dfrac{\partial y_2}{\partial x_n}\\\vdots&\vdots&\ddots&\vdots\\\dfrac{\partial y_m}{\partial x_1}&\dfrac{\partial y_m}{\partial x_2}&\cdots&\dfrac{\partial y_m}{\partial x_n}\end{bmatrix}\in\mathbb{R}^{m\times n}

设有常数 cc,常向量 aRn\boldsymbol{a}\in\mathbb{R}^n 和常矩阵 ARm×n\boldsymbol{A}\in\mathbb{R}^{m\times n},向量 u,vRm\boldsymbol{u},\boldsymbol{v}\in\mathbb{R}^m 是关于 x\boldsymbol{x} 的函数。下表给出了几个常用的求导结果,其中涉及的部分求导结果对于后续章节的应用十分重要。

a\boldsymbol{a} x\boldsymbol{x} Ax\boldsymbol{Ax} xTA\boldsymbol{x}^T\boldsymbol{A} cuc\boldsymbol{u} Au\boldsymbol{Au} u+v\boldsymbol{u}+\boldsymbol{v}
0\boldsymbol{0} In\boldsymbol{I}_{n} A\boldsymbol{A} AT\boldsymbol{A}^T cuxc\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}} Aux\boldsymbol{A}\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}} ux+vx\dfrac{\partial \boldsymbol{u}}{\partial \boldsymbol{x}}+\dfrac{\partial \boldsymbol{v}}{\partial \boldsymbol{x}}

对于求导对象包含矩阵的情况,这里不再列出。

链式法则

对于标量函数 y=f(u),u=g(x)y=f(u),u=g(x),有链导法则

yx=yuux\frac{\partial y}{\partial x}=\frac{\partial y}{\partial u}\frac{\partial u}{\partial x}

当我们把求导对象拓展到向量时,链式法则仍然成立,请看下例。

设向量 x,wRn\boldsymbol{x},\boldsymbol{w}\in\mathbb{R}^n,标量 yRy\in\mathbb{R},令

z=(x,wy)2z=\big(\langle \boldsymbol{x},\boldsymbol{w} \rangle-y\big)^2

想要计算 zw\dfrac{\partial z}{\partial \boldsymbol{w}},先把原函数拆成复合函数的形式

a=x,w,b=ay,z=b2a=\langle \boldsymbol{x},\boldsymbol{w} \rangle,\quad b=a-y,\quad z=b^2

由链式法则有

zw=zbbaaw=(2b)(1)(xT)=2(x,wy)xTR1×n\begin{aligned} \dfrac{\partial z}{\partial \boldsymbol{w}}&=\dfrac{\partial z}{\partial b}\dfrac{\partial b}{\partial a}\dfrac{\partial a}{\partial \boldsymbol{w}}\\&=(2b)\cdot(1)\cdot(\boldsymbol{x}^T)\\&=2\big(\langle \boldsymbol{x},\boldsymbol{w} \rangle-y\big)\boldsymbol{x}^T\in \mathbb{R}^{1\times n} \end{aligned}

下面是课程中的另一个例题,可自行参考题目和答案。

设矩阵 XRm×n\boldsymbol{X}\in\mathbb{R}^{m\times n},向量 wRn,yRm\boldsymbol{w}\in\mathbb{R}^n,\boldsymbol{y}\in\mathbb{R}^m,令

z=Xwy2z=\|\boldsymbol{Xw}-\boldsymbol{y}\|^2

计算导数 zw\dfrac{\partial z}{\partial \boldsymbol{w}}

zw=2(Xwy)TX\dfrac{\partial z}{\partial \boldsymbol{w}}=2\big(\boldsymbol{Xw}-\boldsymbol{y}\big)^T\boldsymbol{X}

你做对了吗(〃‘▽’〃)

在 PyTorch 中,求导运算通常采用自动求导的方法,即隐式生成一个有向无环的运算图。自动求导的本质是把复杂的复合函数求导通过链式法则拆成若干个简单函数求导,然后通过运算合并在一起。自动求导包括两种模式

  • 正向累积:按照输入顺序计算
  • 反向累积:按照输入逆序计算

上述两种模式实际上是链式法则的不同计算顺序,下图展现了其计算图原理,后续会再次提到。

一些数学基础知识到此已介绍完毕,恭喜你可以继续了ヾ(✿゚▽゚)ノ