祝你好运的技术博客

Published on

《动手学深度学习》学习笔记(二)下

Authors
  • avatar
    Name
    祝你好运
    Twitter

注:说实话如果单纯看书,我觉得难度很大,但现在有了ChatGPT,不懂的直接甩过去,给我讲的明明白白,顺带一连串的彩虹屁,让我觉得我马上就能训练出ChatGPT 6!

微积分

导数和求导

导数就是一个函数在他的参数作用下,变化的速率。

并不是所有函数都可以求导,对那些不可以求导的,如果我们仍然想在深度学习里面去训练,可以用中间结果,而不是最终结果(举例比如分类问题,他们的值不连续)。

函数的导数可以有多种表示形式:

f(x)=y=dydx=dfdx=ddxf(x)\boxed{f'(x)} = \boxed{y'} = \boxed{\frac{dy}{dx}} = \boxed{\frac{df}{dx}} = \boxed{\frac{d}{dx}f(x)}

下面是一些常见函数的导数:

ddxC=0for any constant Cddxxn=nxn1ddxex=exddxlnx=x1\begin{aligned} \frac{d}{dx} C &= 0 && \text{for any constant } C \\ \frac{d}{dx} x^n &= n x^{n-1} \\ \frac{d}{dx} e^x &= e^x \\ \frac{d}{dx} \ln x &= x^{-1} \end{aligned}

然后是一些求导时候函数组合的常用规则:

ddx[Cf(x)]=Cddxf(x)ddx[f(x)+g(x)]=ddxf(x)+ddxg(x)ddx[f(x)g(x)]=f(x)ddxg(x)+g(x)ddxf(x)ddxf(x)g(x)=g(x)ddxf(x)f(x)ddxg(x)g2(x)\begin{aligned} \frac{d}{dx}[Cf(x)] &= C\frac{d}{dx}f(x) \\ \frac{d}{dx}[f(x)+g(x)] &= \frac{d}{dx}f(x) + \frac{d}{dx}g(x) \\ \frac{d}{dx}[f(x)g(x)] &= f(x)\frac{d}{dx}g(x) + g(x)\frac{d}{dx}f(x) \\ \frac{d}{dx}\frac{f(x)}{g(x)} &= \frac{g(x)\frac{d}{dx}f(x) - f(x)\frac{d}{dx}g(x)}{g^2(x)} \end{aligned}

偏导数和梯度下降

偏导数

首先偏导数是指固定其他变量,只研究一个变量的导数。用数学公式的话就是:

yxi=limh0f(x1,,xi1,xi+h,xi+1,,xn)f(x1,,xi,,xn)h\frac{\partial y}{\partial x_i} = \lim_{h \to 0} \frac{ f(x_1,\ldots,x_{i-1},x_i+h,x_{i+1},\ldots,x_n) - f(x_1,\ldots,x_i,\ldots,x_n) }{h}

简单一点也就是这个公式:

yxi=fxi=xif=if=fxi=fi=Dif=Dxif\begin{aligned} \frac{\partial y}{\partial x_i} &= \frac{\partial f}{\partial x_i} = \partial_{x_i} f = \partial_i f \\ &= f_{x_i} = f_i = D_i f = D_{x_i} f \end{aligned}

公式不太好理解,看下面的例子:

z=x2+y2z=x^2+y^2

然后 xx 的偏导数就是:计算时把除 xx 以外的变量当作常数。对 z=x2+y2z = x^2 + y^2 来说,y2y^2 相对于 xx 就是常数项:

x2+常数x^2 + \text{常数}

那结果就是:

fx=2x\boxed{ \frac{\partial f}{\partial x}=2x }

梯度下降

梯度下降的公式就是,对所有的变量分别求偏导数,然后把他们放到一个向量里面,然后再把这个向量转置一下,就行了:

xf(x)=[x1f(x),x2f(x),...,xnf(x)]T\nabla_{\mathbf x}f(\mathbf x) = [ \partial_{x_1}f(\mathbf x), \partial_{x_2}f(\mathbf x), ..., \partial_{x_n}f(\mathbf x) ]^T

那为什么最后要转置一下呢?因为比如有x,y,z三个变量,那这个函数的输入就是

f=[xyz]\mathbf{f} = \begin{bmatrix} x\\ y\\ z \end{bmatrix}

同时假设他的梯度是:

f=[456]\nabla f = \begin{bmatrix} 4\\ 5\\ 6 \end{bmatrix}

学习率用这个:

η=0.1\eta=0.1

那么计算结果就是:

[123]0.1[456]=[0.61.52.4]\begin{bmatrix} 1\\ 2\\ 3 \end{bmatrix} - 0.1 \begin{bmatrix} 4\\ 5\\ 6 \end{bmatrix} = \begin{bmatrix} 0.6\\ 1.5\\ 2.4 \end{bmatrix}

如果求梯度的时候不转置一下,计算的时候就要先转置一下,要不然无法计算。

链式法则

据说这是深度学习里面最重要的公式。用最简单的话描述就是:把从 x_i 到 y 的所有路径都找出来,每条路径上的导数相乘,最后全部相加。公式的简单版本是:

dydx=dydududx\boxed{ \frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx} }

而它的超长版本就是:

yxi=yu1u1xi+yu2u2xi++yumumxi\frac{\partial y}{\partial x_i} = \frac{\partial y}{\partial u_1} \frac{\partial u_1}{\partial x_i} + \frac{\partial y}{\partial u_2} \frac{\partial u_2}{\partial x_i} +\cdots+ \frac{\partial y}{\partial u_m} \frac{\partial u_m}{\partial x_i}

举一个实际的例子:

u1=x+yu_1=x+y u2=xyu_2=xy z=u1+u2z=u_1+u_2

而我们要求的就是:

zx\frac{\partial z}{\partial x}

计算结果就是:

zx=1+y\boxed{ \frac{\partial z}{\partial x} = 1+y }

自动微分

我们大学的微积分里面讲求导一般是2种方法,一种是数值微分,另一种是符号函数求导。但这里新增了一种方法就是自动微分。

数值微分

最笨的方法,根据导数定义:

f(x)=f(x+h)f(x)hf'(x) = \frac{f(x+h)-f(x)}{h}

比如下面的方程:

f(x)=x2x=3h=0.0001f(x)=x² x=3 h=0.0001

那对应的计算过程就是:

(3.0001232)/0.00016.0001(3.0001²-3²)/0.0001 ≈6.0001

但这种方法很慢,有误差,基本不会用,只是一种定义

符号求导

比如下面的求导过程:

x2+3x+52x+3x²+3x+5 ↓ 2x+3

当这个函数越来越长的时候,表达式会爆炸,在深度学习里面不适用

自动微分

想要理解自动微分,我们有个前提条件得知道,深度学习里面,我们是知道计算步骤的,步骤里面的参数的值我们也知道(只是不知道最优质,最初一般是用随机数填充,慢慢调整),然后我们把计算步骤全部记下来,然后反向求导。这个反向过程是用了上面的链式法则

为什么要反向求导呢?

因为正向求导和反向求导的计算量不一样,反向求导计算量少,比如下面的例子,我们分别用正向求导和反向求导都来一遍,对比一下。

a=w1xb=w2aL=b2\begin{aligned} a &= w_1 x \\ b &= w_2 a \\ L &= b^2 \end{aligned}

假设这一轮的输入是:

w1=2,x=3,w2=4w_1 = 2,\quad x = 3,\quad w_2 = 4

正向求导

w1w_1

我们先计算w1w_1的偏导数,也就是说我们想计算:

Lw1\frac{∂L}{∂w_1}

然后开始我们的表演,下面用到了链式法则,如果忘记了记得复习一下。先计算a=w1xa = w_1 x

a=w1x=23=6对应的导数:aw1=(w1x)w1=xw1w1=x=3\begin{aligned} a = w_1 x = 2 * 3 = 6 \\ \text{对应的导数:} \\ \frac{∂a}{∂w_1} = \frac{∂(w_1x)}{∂w_1} = x\frac{∂w_1}{∂w_1} = x = 3 \end{aligned}

然后计算b=w2ab = w_2 a

b=w2a=46=24bw1=(w2a)w1=w2aw1=w23=12\begin{aligned} b = w_2 a = 4 * 6 = 24 \\ \frac{∂b}{∂w_1} = \frac{∂(w_2a)}{∂w_1} = w_2 * \frac{∂a}{∂w_1} = w_2 * 3 = 12 \end{aligned}

然后我们再计算L=b2L = b^2

L=b2=242=576Lb=b2b=2bbb=2bLw1=b2w1=2bbw1=2b12=22412=576\begin{aligned} L = b^2 = 24^2 = 576 \\ \frac{∂L}{b} = \frac{∂b^2}{∂b} = 2b\frac{∂b}{∂b} = 2b \\ \frac{∂L}{∂w_1} = \frac{∂b^2}{∂w_1} = 2b\frac{∂b}{∂w_1} = 2b * 12 = 2 * 24 * 12 = 576 \end{aligned}
w2w_2

这次我们是想要计算

Lw2\frac{∂L}{∂w_2}

然后我们要逐步开始计算,a=w1xa = w_1 xa=6不变,然后求导:

aw2=w1xw2=0\begin{aligned} \frac{∂a}{∂w_2} = \frac{∂w_1x}{∂w_2} = 0 \end{aligned}

第二步b=w2ab = w_2 ab=24不变,然后求导:

bw2=(w2a)w2=aw2w2=6\frac{∂b}{∂w_2} = \frac{∂(w_2 a)}{∂w_2} = a\frac{∂w_2}{∂w_2} = 6

第三步计算L=b2L = b^2L=576不变,然后求导:

Lw2=b2w2=2bbw2=2b6=2246=288\frac{∂L}{∂w_2} = \frac{∂b^2}{∂w_2} = 2b\frac{∂b}{∂w_2} = 2b * 6 = 2 * 24 * 6 = 288

反向求导

那我们先正向算一下结果:

a=w1x=2×3=6b=w2a=4×6=24L=b2=24×24=576\begin{aligned} a &= w_1 x = 2 \times 3 = 6 \\ b &= w_2 a = 4 \times 6 = 24 \\ L &= b^2 = 24 \times 24 = 576 \end{aligned}

现在开始反向传播,L=b2L = b^2,那么:

Lb=2b=48\frac{∂L}{∂b} = 2b = 48

然后看b=w2ab = w_2a

bw2=(w2a)w2=a=6Lw2=Lbbw2=486=288ba=(w2a)a=w2aa=4La=Lbba=484=192\begin{aligned} \frac{∂b}{w_2} = \frac{∂(w_2a)}{w_2} = a = 6\\ \frac{∂L}{∂w_2} = \frac{∂L}{∂b} * \frac{∂b}{∂w_2} = 48 * 6 = 288 \\ \frac{∂b}{∂a} = \frac{∂(w_2a)}{∂a} = w_2 \frac{∂a}{∂a} = 4 \\ \frac{∂L}{∂a} = \frac{∂L}{∂b} * \frac{∂b}{∂a} = 48 * 4 = 192 \end{aligned}

最后看a=w1xa = w_1x

aw1=(w1x)w1=xw1w1=3ax=(w1x)x=w1xx=2Lw1=Laaw1=1923=576Lx=Laax=1922=384\begin{aligned} \frac{∂a}{∂w_1} = \frac{∂(w_1x)}{∂w_1} = x\frac{∂w_1}{∂w_1} = 3 \\ \frac{∂a}{∂x} = \frac{∂(w_1x)}{x} = w_1\frac{∂x}{∂x} = 2 \\ \frac{∂L}{∂w_1} = \frac{∂L}{∂a} * \frac{∂a}{∂w_1} = 192 * 3 = 576 \\ \frac{∂L}{∂x} = \frac{∂L}{∂a} * \frac{∂a}{∂x} = 192 * 2 = 384 \\ \end{aligned}

所以通过上面的对比可以看出,反向求导顺手得多,而且计算步骤也比较节省。