深度学习基础:3.反向传播和梯度下降

2022-06-14 14:00:26 浏览数 (1)

动态计算图

计算图可以用来表示两个变量之间的关系。例如,构建y=x^2,则可用一张简单计算图进行表示。 pytorch支持动态计算图,动态意义在于,当创建其它由计算图中变量而来的新变量时,新变量会自动添加到计算图内。 而Tensorflow v1仅支持静态计算图,需要提前定义各个变量的关系,灵活性不如动态计算图。

反向传播

通过反向传播,可以计算各变量的导数。

代码语言:javascript复制
x = torch.tensor(1.,requires_grad = True) #requires_grad=True表示x可导
y = x ** 2
y.backward()
# 在y=x**2函数关系基础上,x取值为1时的导数值
x.grad

输出:

代码语言:javascript复制
tensor(2.)

梯度下降

梯度下降的代数表示

令多元线性回归方程为

f(x) = w_1x_1 w_2x_2 ... w_dx_d b

hat w = (w_1,w_2,...,w_d,b)
hat x = (x_1,x_2,...,x_d,1)

出于加快迭代收敛速度的目标,我们在定义梯度下降的损失函数L时,在原SSE基础上进行比例修正,新的损失函数

L(w_1,w_2,...,w_d,b) = frac{1}{2m}SSE

,其中,m为样本个数。

损失函数有:

L(w_1,w_2,...,w_d,b) = frac{1}{2m}sum_{j=0}^{m}(f(x_1^{(j)}, x_2^{(j)}, ...1) - y_j)^2

并且,根据此前描述过程,在开始梯度下降求解参数之前,我们首先需要设置一组参数的初始取值

(w_1, w_2..., w_d, b)

,以及学习率alpha ,然后即可执行迭代运算,其中每一轮迭代过程需要执行以下三步

Step 1.计算梯度表达式

对于任意一个参数w_i ,其梯度计算表达式如下:

frac{partial}{partial w_i}L(w_1, w_2..., w_d, b)

Step 2.用学习率乘以损失函数梯度,得到迭代移动距离

alpha frac{partial}{partial w_i}L(w_1, w_2..., w_d, b)

Step 3.用原参数减Step 2中计算得到的距离,更新所有的参数w

w_i = w_i - alpha frac{partial}{partial w_i}L(w_1, w_2..., w_d, b)

更新完所有参数,即完成了一轮的迭代,接下来就能以新的一组w_i 参与下一轮迭代。

举例说明

有数据集表示如下:

x

y

1

2

2

4

3

6

假设,我们使用

y = wx

进行拟合,则SSE为:

SSE = (2-1*w)^2 (4-2*w)^2 (6-3*w)^2 \ = w^2-4w 4 4w^2-16w 16 9w^2-36w 36 \ = 14w^2-56w 56 \ = 14(w^2-4w 4)

此时,SSE就是一个关于w的一元函数。当使用最小二乘法进行求解时,SSE就是损失函数,并且SSE对于w求导为0的点就是最小值点,因此有:

frac{partial{SSE_{(a)}}}{partial{(a)}} = 14(2w-4) = 28(w-2) = 0
w=2

但我们使用梯度下降求解时:

grad^* = frac{partial{SSE_{(a)}}}{partial{(a)}} = 14(2w-4) = 28(w-2)

由于梯度表示方向,在某些情况下我们可以对其绝对数值进行一定程度上的“缩放”,此时我们规定有效梯度是原梯度的1/28,则有

grad = w-2

设步长$alpha=

0.5,初始值点取为

w_0=0$,则迭代过程如下:

第一轮迭代:

grad(w_0)=grad(0)=-2,w_0=0,w_1=w_0-alpha*grad(w_0)=0-frac{1}{2}(-2)=1

第二轮迭代:

grad(w_1)=grad(1)=-1,w_1=1,w_2=w_1-alpha*grad(w_1)=1-frac{1}{2}(-1)=frac{3}{2}

第三轮迭代:

grad(w_2)=grad(frac{3}{2})=-frac{1}{2},w_2=frac{3}{2},w_3=w_2-alpha*grad(w_2)=frac{3}{2}-frac{1}{2}(-frac{1}{2})=frac{7}{4}

第四轮迭代:

grad(w_3)=grad(frac{7}{4})=-frac{1}{4},w_3=frac{7}{4},w_4=w_3-alpha*grad(w_3)=frac{7}{4}-frac{1}{2}(-frac{1}{4})=frac{15}{8}

依次类推:

w_5 = frac{15}{8} frac{1}{16} = frac{31}{16}; w_6 = frac{31}{16} frac{1}{32} = frac{63}{32}; w_7 = frac{63}{32} frac{1}{64} = frac{127}{64}; ...
w_n=frac{2^n-1}{2^{n-1}} = 2-frac{1}{2^{n-1}}
lim_{n→infty} (w_n) = lim_{n→infty} (2-frac{1}{2^{n-1}}) = 2

梯度下降的矩阵表示

令多元线性回归方程为

f(x) = w_1x_1 w_2x_2 ... w_dx_d b

hat w = (w_1,w_2,...,w_d,b)
hat x = (x_1,x_2,...,x_d,1)

因此,方程可表示为

f(x) = hat w * hat x^T

另外,我们将所有自变量的值放在一个矩阵中,有

X = left [begin{array}{cccc} x_{11} &x_{12} &... &x_{1d} &1 \ x_{21} &x_{22} &... &x_{2d} &1 \ ... &... &... &... &1 \ x_{m1} &x_{12} &... &x_{md} &1 \ end{array}right]
y = left [begin{array}{cccc} y_1 \ y_2 \ . \ . \ . \ y_m \ end{array}right]

此时,SSE可表示为:

SSE = ||y - Xhat w^T||_2^2 = (y - Xhat w^T)^T(y - Xhat w^T) = E(hat w)

梯度下降损失函数为:

L(hat w) = frac{1}{2m} SSE =frac{1}{2m} (y - Xhat w^T)^T(y - Xhat w^T)

同样,我们需要设置初始化参数

(w_1, w_2..., w_d, b)

,以及学习率alpha ,然后即可开始执行迭代过程,同样,每一轮迭代需要有三步计算:

Step 1.计算梯度表达式

对于参数向量hat w ,其梯度计算表达式如下:

frac{partial}{partial hat w}L(hat w) = frac{1}{m}X^T(Xhat w ^T - Y)

Step 2.用学习率乘以损失函数梯度,得到迭代移动距离

alpha frac{partial}{partial hat w}L(hat w)

Step 3.用原参数减Step 2中计算得到的距离,更新所有的参数w

hat w = hat w - alpha frac{partial}{partial hat w}L(hat w) = hat w - frac{alpha}{m}X^T(Xhat w ^T - Y)

更新完所有参数,即完成了一轮的迭代,接下来就能以新的hat w 参与下一轮迭代。

编程实现

编写一个函数求梯度,默认学习率为0.01,迭代次数为1000次。

代码语言:javascript复制
def gradDescent(X, y, eps = torch.tensor(0.01, requires_grad = True), numIt = 1000):
    m, n = X.shape
    weights = torch.zeros(n, 1, requires_grad = True)
    for k in range(numIt):
        grad = torch.mm(X.t(), (torch.mm(X, weights) - y))/2
        weights = weights - eps * grad
    return weights
代码语言:javascript复制
X = torch.tensor([[1.,1],[3, 1]], requires_grad = True)
X
代码语言:javascript复制
tensor([[1., 1.],
        [3., 1.]], requires_grad=True)
代码语言:javascript复制
y = torch.tensor([2.,4], requires_grad = True).reshape(2,1)
y
代码语言:javascript复制
tensor([[2.],
        [4.]], grad_fn=<ViewBackward>)
代码语言:javascript复制
gradDescent(X, y)
代码语言:javascript复制
tensor([[1.0372],
        [0.9102]], grad_fn=<SubBackward0>)
代码语言:javascript复制
weights = gradDescent(X, y, numIt = 10000)
weights
代码语言:javascript复制
tensor([[1.0000],
        [1.0000]], grad_fn=<SubBackward0>)
SSE =(y - Xhat w^T)^T(y - Xhat w^T)
代码语言:javascript复制
torch.mm((torch.mm(X,weights)-y).t(), torch.mm(X,weights)-y)
代码语言:javascript复制
tensor([[2.8518e-10]], grad_fn=<MmBackward>)

0 人点赞