深度学习入门之线性模型和梯度下降

线性模型和梯度下降

这是神经网络的第一课,我们会学习一个非常简单的模型,线性回归,同时也会学习一个优化算法-梯度下降法,对这个模型进行优化。线性回归是监督学习里面一个非常简单的模型,同时梯度下降也是深度学习中应用最广的优化算法,我们将从这里开始我们的深度学习之旅。

一元线性回归

一元线性模型非常简单,假设我们有变量 xix_ixi 和目标 yiy_iyi,每个 i 对应于一个数据点,希望建立一个模型

y^i=wxi+b \hat{y}_i = w x_i + b y^i=wxi+b

y^i\hat{y}_iy^i 是我们预测的结果,希望通过 y^i\hat{y}_iy^i 来拟合目标 yiy_iyi,通俗来讲就是找到这个函数拟合 yiy_iyi 使得误差最小,即最小化

1n∑i=1n(y^i−yi)2 \frac{1}{n} \sum_{i=1}^n(\hat{y}_i - y_i)^2 n1i=1n(y^iyi)2

那么如何最小化这个误差呢?

这里需要用到梯度下降,这是我们接触到的第一个优化算法,非常简单,但是却非常强大,在深度学习中被大量使用,所以让我们从简单的例子出发了解梯度下降法的原理

梯度下降法

在梯度下降法中,我们首先要明确梯度的概念,随后我们再了解如何使用梯度进行下降。

梯度

梯度在数学上就是导数,如果是一个多元函数,那么梯度就是偏导数。比如一个函数f(x, y),那么 f 的梯度就是

(∂f∂x, ∂f∂y) (\frac{\partial f}{\partial x},\ \frac{\partial f}{\partial y}) (xf, yf)

可以称为 grad f(x, y) 或者 ∇f(x,y)\nabla f(x, y)f(x,y)。具体某一点 (x0, y0)(x_0,\ y_0)(x0, y0) 的梯度就是 ∇f(x0, y0)\nabla f(x_0,\ y_0)f(x0, y0)

上面是原理部分,下面通过一个例子来进一步学习线性模型

import torch
import numpy as np
from torch.autograd import Variable
import matplotlib.pyplot as plt

torch.manual_seed(2017)
# 读入数据 x 和 y
x_train = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
                    [9.779], [6.182], [7.59], [2.167], [7.042],
                    [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)

y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
                    [3.366], [2.596], [2.53], [1.221], [2.827],
                    [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)

# 画出图像
plt.plot(x_train, y_train, 'bo')
plt.show()

在这里插入图片描述

# 转换成 Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)

# 定义参数 w 和 b
w = Variable(torch.randn(1), requires_grad=True)  # 随机初始化
b = Variable(torch.zeros(1), requires_grad=True)  # 使用 0 进行初始化

# 构建线性回归模型
x_train = Variable(x_train)
y_train = Variable(y_train)


def linear_model(x):
    return x * w + b


y_ = linear_model(x_train)
"""
经过上面的步骤我们就定义好了模型,在进行参数更新之前,我们可以先看看模型的输出结果长什么样
"""
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()

在这里插入图片描述

思考:红色的点表示预测值,似乎排列成一条直线,请思考一下这些点是否在一条直线上?

这个时候需要计算我们的误差函数,也就是

1n∑i=1n(y^i−yi)2 \frac{1}{n} \sum_{i=1}^n(\hat{y}_i - y_i)^2 n1i=1n(y^iyi)2


# 计算误差
def get_loss(y_, y):
    return torch.mean((y_ - y_train) ** 2)

loss = get_loss(y_, y_train)

# 打印一下看看 loss 的大小
print(loss)

Variable containing:
153.3520
[torch.FloatTensor of size 1]

定义好了误差函数,接下来我们需要计算 w 和 b 的梯度了,这时得益于 PyTorch 的自动求导,我们不需要手动去算梯度,有兴趣的同学可以手动计算一下,w 和 b 的梯度分别是

∂∂w=2n∑i=1nxi(wxi+b−yi)∂∂b=2n∑i=1n(wxi+b−yi) \frac{\partial}{\partial w} = \frac{2}{n} \sum_{i=1}^n x_i(w x_i + b - y_i) \\ \frac{\partial}{\partial b} = \frac{2}{n} \sum_{i=1}^n (w x_i + b - y_i) w=n2i=1nxi(wxi+byi)b=n2i=1n(wxi+byi)

# 自动求导
loss.backward()

# 查看w和b的梯度
print(w.grad)
print(b.grad)

# 更新一次参数
w.data = w.data - 1e-2 * w.grad.data
b.data = b.data - 1e-2 * b.grad.data

# 更新参数之后,再一次查看模型输出的结果
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()

在这里插入图片描述

从上面的例子可以看到,更新之后红色的线跑到了蓝色的线下面,没有特别好的拟合蓝色的真实值,所以我们需要在进行几次更新

# 进行10次更新
for e in range(10):
    y_ = linear_model(x_train)
    loss = get_loss(y_, y_train)

    # 梯度归0
    w.grad.zero_()
    b.grad.zero_()
    loss.backward()

    # 更新一次参数
    w.data = w.data - 1e-2 * w.grad.data
    b.data = b.data - 1e-2 * b.grad.data

    print('epoch {},loss {}'.format(e, loss.data))

# 10次更新参数之后,再一次查看模型输出的结果
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(), y_train.data.numpy(), 'bo', label='real')
plt.plot(x_train.data.numpy(), y_.data.numpy(), 'ro', label='estimated')
plt.legend()
plt.show()

在这里插入图片描述

经过 10 次更新,我们发现红色的预测结果已经比较好的拟合了蓝色的真实值。

多项式回归模型

下面我们更进一步,讲一讲多项式回归。什么是多项式回归呢?非常简单,根据上面的线性回归模型

y^=wx+b \hat{y} = w x + b y^=wx+b

这里是关于 x 的一个一次多项式,这个模型比较简单,没有办法拟合比较复杂的模型,所以我们可以使用更高次的模型,比如

y^=w0+w1x+w2x2+w3x3+⋯ \hat{y} = w_0 + w_1 x + w_2 x^2 + w_3 x^3 + \cdots y^=w0+w1x+w2x2+w3x3+

这样就能够拟合更加复杂的模型,这就是多项式模型,这里使用了 x 的更高次,同理还有多元回归模型,形式也是一样的,只是除了使用 x,还是更多的变量,比如 y、z 等等,同时他们的 loss 函数和简单的线性回归模型是一致的。

首先我们可以先定义一个需要拟合的目标函数,这个函数是个三次的多项式

# 定义一个多变量函数
w_target = np.array([0.5, 3, 2.4])
b_target = np.array([0.9])

# 打印函数
f_des = 'y={:.2f}+{:.2f}*x+{:.2f}*x^2+{:.2f}*x^3' \
    .format(b_target[0], w_target[0], w_target[1], w_target[2])
print(f_des)

y=0.90+0.50*x+3.00*x^2+2.40*x^3

我们可以先画出这个多项式的图像

x_sample = np.arange(-3., 3., 0.1)
y_sample = b_target[0] + w_target[0] * x_sample + w_target[1] * x_sample ** 2 + w_target[2] * x_sample ** 3

plt.plot(x_sample, y_sample, label='real curve')
plt.show()

在这里插入图片描述

接着我们可以构建数据集,需要 x 和 y,同时是一个三次多项式,所以我们取了 x, x2,x3x,\ x^2, x^3x, x2,x3


# 构建数据 x 和 y
# x 是一个如下矩阵 [x, x^2, x^3]
# y 是函数的结果 [y]

x_train = np.stack([x_sample ** i for i in range(1, 4)], axis=1)
x_train = torch.from_numpy(x_train).float() # 转换成 float tensor

y_train = torch.from_numpy(y_sample).float().unsqueeze(1) # 转化成 float tensor 

接着我们可以定义需要优化的参数,就是前面这个函数里面的 wiw_iwi


# 定义参数和模型
w = Variable(torch.randn(3, 1), requires_grad=True)
b = Variable(torch.zeros(1), requires_grad=True)

# 将 x 和 y 转换成 Variable
x_train = Variable(x_train)
y_train = Variable(y_train)

def multi_linear(x):
    return torch.mm(x, w) + b

我们可以画出没有更新之前的模型和真实的模型之间的对比


# 画出更新之前的模型
y_pred = multi_linear(x_train)

plt.plot(x_train.data.numpy()[:, 0], y_pred.data.numpy(), label='fitting curve', color='r')
plt.plot(x_train.data.numpy()[:, 0], y_sample, label='real curve', color='b')
plt.legend()

在这里插入图片描述

可以发现,这两条曲线之间存在差异,我们计算一下他们之间的误差


# 计算误差,这里的误差和一元的线性模型的误差是相同的,前面已经定义过了 get_loss
loss = get_loss(y_pred, y_train)
print(loss)

# 自动求导
loss.backward()

# 查看梯度
print(w.grad)
print(b.grad)

# 更新一下参数
w.data = w.data - 0.001 * w.grad.data
b.data = b.data - 0.001 * b.grad.data

# 画出更新一次之后的模型
y_pred = multi_linear(x_train)

plt.plot(x_train.data.numpy()[:, 0], y_pred.data.numpy(), label='fitting curve', color='r')
plt.plot(x_train.data.numpy()[:, 0], y_sample, label='real curve', color='b')
plt.legend()
plt.show()

在这里插入图片描述

因为只更新了一次,所以两条曲线之间的差异仍然存在,我们进行 100 次迭代


# 进行 100 次参数更新
for e in range(100):
    y_pred = multi_linear(x_train)
    loss = get_loss(y_pred, y_train)
    
    w.grad.data.zero_()
    b.grad.data.zero_()
    loss.backward()
    
    # 更新参数
    w.data = w.data - 0.001 * w.grad.data
    b.data = b.data - 0.001 * b.grad.data
    if (e + 1) % 20 == 0:
        print('epoch {}, Loss: {:.5f}'.format(e+1, loss.data[0]))

可以看到更新完成之后 loss 已经非常小了,我们画出更新之后的曲线对比


# 画出更新之后的结果
y_pred = multi_linear(x_train)

plt.plot(x_train.data.numpy()[:, 0], y_pred.data.numpy(), label='fitting curve', color='r')
plt.plot(x_train.data.numpy()[:, 0], y_sample, label='real curve', color='b')
plt.legend()

在这里插入图片描述

可以看到,经过 100 次更新之后,可以看到拟合的线和真实的线已经完全重合了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值