线性回归详解

1.1 回归

回归是能为一个或多个自变量与因变量之间的关系进行建模的方法,在机器学习领域中,想要预测一个数值时,就会涉及到回归问题,比如预测房屋、股票价格,预测销售量、需求量等。

1.2 线性回归的基本元素

为了解释线性回归,在房屋估价问题中,希望用房屋的面积和房屋的年龄来估算房屋的价格。为了实现这样的预测房价模型,首先需要收集一个真实的数据集,该数据集包含了房屋的价格,面积和年龄,也叫作训练集。预测的目标(房屋的价格)称为标签,预测所依据的自变量(面积、年龄)称为特征。

我们使用n来表示数据集中的样本数,对索引为i的样本,输入两个特征表示为:

x^{(i)} = [x_{_{1}}^{i}, x_{_{2}}^{i}]^{T}

对应的标签是 y^{(i)}

1.3 线性模型

在预测房屋价格模型中,最终的预测价格可以表示成特征的加权和:

price = w_{area} * area + w_{age} * age + b

其中w是权重,b是偏置,如果没有偏置,该模型的表达能力会受到限制,我们的目标是通过给定的数据集,寻找模型的权重 w 和偏置 b ,当输入包含 d 个特征时,预测结果用 \widehat{y} 表示:

\widehat{y} = w_{}x_{1}+...+w_{d}x_{d} + b

将所有特征放到向量 x \in \mathbb{R}^{d}  中,所有权重放到向量 w\in \mathbb{R}^{d} 中,可以用点积来表示模型:

\widehat{y} = w^{T} x + b

对于所有的特征集合 X,预测值 \widehat{y} \in \mathbb{R}^{n}可以通过矩阵-向量乘法表示:

\widehat{y} = X w + b

给定训练数据特征 X 和对应的已知标签 y ,线性回归的目标是找到一组权重向量 w 和偏置 b ,当从 X 中取新的样本特征时,这组权重向量和偏置能够使得新样本预测标签的误差尽可能小。

1.4 损失函数

损失函数是用来量化目标的实际值与预测值之间的差距,在回归问题中常用的损失函数是平方误差函数,当样本 i 的预测值是 \widehat{y}^{_{(i)}},其相应的真实标签是为 y^{(i)}  时,平方误差为:

l ^{(i)} (w, b) = \frac{1}{2} (\widehat{y}^{(i) } - y^{(i)})^{2}

       

由于平方误差函数中的二次方项,估计值和观测值之间较大的差异将导致更大的损失,我们需计算在训练集 n 个样本上的损失均值:

L(w, b) = \frac{1}{n} \sum_{i=1}^{n} \frac{1}{2}(w^{T}x^{(i)} + b - y^{(i)})^{2}

1.5 随机梯度下降

当无法对损失函数直接进行求解时,我们仍可以有效的训练模型。使用梯度下降方法,其中最简单的用法是计算损失函数关于模型参数的导数,但是这样更新会计算数据集中所有样本的损失均值,执行可能很慢,因此在每次需要计算更新的时候随机抽取一小批样本,这种方法叫做小批量随机梯度下降。

在每次迭代中,先随机抽样一个小批量 B ,它包含固定数量的训练样本,然后计算小批量的平均损失关于模型参数的导数:

(w, b) \leftarrow (w, b)-\frac{\eta }{B}\sum_{i\in B}^{}\frac{\partial l^{(i)}}{\partial (w, b)}

B代表每个小批量中的样本数,\eta表示学习率,在训练了预先确定的若干迭代次数后,得到的估计值也不会使损失函数真正地达到最小值。 因为算法会使得损失向最小值缓慢收敛,但却不能在有限的步数内非常精确地达到最小值。

 2.1生成数据集

导入所需要的包,这里使用的Pytorch框架。

%matplotlib inline
import random
import torch
from d2l import torch as d2l

我们将根据带有噪声的线性模型构造一个人造数据集。 我们的任务是使用这个有限样本的数据集来恢复这个模型的参数。我们生成一个包含1000个样本的数据集,每个样本包含两个特征,合成数据集是X\in \mathbb{R}^{1000\times2 }

使用线性模型参数 w = [2, -3.4]^{T}b = 4.2 和噪声项 \varepsilon 生成数据集和标签:

y = Xw + b + \varepsilon

噪声项视为模型预测和标签时的潜在观测误差。

def synthetic_data(w, b, num_examples):  #@save
    """生成y=Xw+b+噪声"""
    X = torch.normal(0, 1, (num_examples, len(w)))
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X, y.reshape((-1, 1))

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)

在features中每一行包含一个二维数据样本,它拥有两个特征,而labels中每一行包含一维标签

print('features:', features[0],'\nlabel:', labels[0])
features: tensor([-0.1413,  0.9253])
label: tensor([0.7524])

通过生成第二个特征 feature 和 labels 的散点图,可以看出两者线性关系

d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);

    

2.2 读取数据集

训练模型时要对数据集进行遍历,每次抽取一小批量样本,并使用它们来更新我们的模型。定义一个函数, 该函数能打乱数据集中的样本并以小批量方式获取数据。

在下面的代码中,我们定义一个data_iter函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。 每个小批量包含一组特征和标签。

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    # 这些样本是随机读取的,没有特定的顺序
    random.shuffle(indices)
    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(
            indices[i: min(i + batch_size, num_examples)])
        yield features[batch_indices], labels[batch_indices]

读取第一个小批量数据样本。

batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break
tensor([[-0.0929,  0.3136],
        [-0.4081,  0.5990],
        [ 1.2006, -0.8625],
        [ 2.8351,  1.2113],
        [ 0.4811,  1.6206],
        [-1.5946,  0.7590],
        [-0.7296,  2.0734],
        [ 1.4357, -0.4068],
        [-1.1405, -0.0359],
        [ 0.6749,  0.9677]])
 tensor([[ 2.9562],
        [ 1.3347],
        [ 9.5308],
        [ 5.7467],
        [-0.3549],
        [-1.5650],
        [-4.3218],
        [ 8.4510],
        [ 2.0353],
        [ 2.2612]])

2.3 初始化模型参数

我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

初始化参数之后,我们需要更新这些参数,直到这些参数足够拟合我们的数据。 每次更新都需要计算损失函数关于模型参数的梯度。 有了这个梯度,我们就可以向减小损失的方向更新每个参数。

2.4 定义模型 

def linreg(X, w, b):  #@save
    """线性回归模型"""
    return torch.matmul(X, w) + b

2.5 定义损失函数

在实现中,我们需要将真实值 y 的形状转换为和预测值 y_hat 的形状相同。

def squared_loss(y_hat, y):  #@save
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

2.6 定义优化算法

在每一步中,使用从数据集中随机抽取的一个小批量,然后根据参数计算损失的梯度。 接下来,朝着减少损失的方向更新我们的参数。因为我们计算的损失是一个批量样本的总和,所以我们用批量大小来规范化步长。

def sgd(params, lr, batch_size):  #@save
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

2.7 训练

在每次迭代中,我们读取一小批量训练样本,并通过我们的模型来获得一组预测。 计算完损失后,我们开始反向传播,存储每个参数的梯度。 最后,我们调用优化算法sgd来更新模型参数。

lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y)  # X和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()
        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
epoch 1, loss 0.026352
epoch 2, loss 0.000093
epoch 3, loss 0.000054
w的估计误差: tensor([ 0.0002, -0.0001], grad_fn=<SubBackward0>)
b的估计误差: tensor([0.0006], grad_fn=<RsubBackward1>)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值