d2l_线性回归实现详解及代码

1. 线性回归从零开始实现

1.1 生成数据集

我们根据带有噪声的线性模型构造一个人造数据集,我们将使用有限的数据集来恢复这个模型的参数。在下面代码中,我们生成一个包含1000个样本的数据集,每个样本包含从标准正态分布中采样的两个特征。我们使用线性模型参数 w = [ 2 , − 3.4 ] T w = [2,-3.4]^T w=[2,3.4]T​、 b = 4.2 b=4.2 b=4.2​和噪声项 ε \varepsilon ε​生成数据集及其标签。 y = X w + b + ε y = \mathbf{X}\mathbf{w} + b + \varepsilon y=Xw+b+ε​)

def synthetic_data(w, b, num_examples):
    """生成 y=Xw + b + 噪声"""
    X = torch.normal(0, 1, (num_examples, len(w))) # 正态分布(均值为0,标准差为1)
    y = torch.matmul(X, w) + b # 矩阵相乘
    y += torch.normal(0, 0.01, y.shape) # 加入噪声项
    # 得到的y为行向量的形式,为了使其变为一列的形式需要进行reshape
    return X, y.reshape((-1, 1))

1.2 读取数据集

我们要实现每次读取一个小批量,并且使用他们来更新我们的模型。因此我们定义一个函数,该函数可以打乱数据集中的样本,并以小批量方式获取数据。

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]

注意:1)该函数的作用类似于生成迭代器yield相当于return,调用函数时每for循环一次返回一个值。

1.3 初始化模型参数

在使用小批量梯度优化我们的模型参数之前,我们需要首先初始化模型参数,有了初始化的模型参数,我们就可以更新这些参数,直到这些参数足够拟合我们的数据。因为每次更新参数的时候都需要计算损失函数关于模型参数的梯度,以便向减小损失函数的方向更新模型参数,所以我们引入自动微分requires_grad来计算梯度。

# 初始化模型参数
w = torch.normal(0,0.01,(2,1),requires_grad=True)
b = torch.zeros(1,requires_grad=True)

1.4 定义模型

到此,我们需要定义一个模型,将模型的输入和参数同模型的输出关联起来

def linear(X,w,b):
    """定义模型"""
    return torch.matmul(X,w)+b

1.5定义损失函数

采用之前文章中线性回归问题中较为常用的损失函数:平方损失函数。

def squared_loss(y_hat,y):
    return (y_hat-y.reshape(y_hat.shape))**2/2

1.6 定义优化算法

接下来,我们将朝着减小损失函数的方向更新我们的参数。以下函数实现小批量随机梯度更新。因为我们定义的损失函数是一个批量的总和,所以我们使用batch_size归一化步长,这样使得步长大小不会取决于我们对批量大小的选择。

def sgd(params, lr, batch_size):
    """小批量梯度下降"""
    with torch.no_grad():
        for param in params:  # 参数b和w
            param -= lr*param.grad/batch_size
            param.grad.zero_()

注:1)torch.no_grad()表示对以下语句不生成计算图,即无法其下表达式求解参数的导数。这么做可以节省计算机的计算量。

​ 2)param.grad.zero_()对参数的梯度值归零,不然下一次反向传播求解的梯度会累加到本次的梯度值上。

1.7 训练

训练过程如下:在每次迭代中我们选取一小批量训练样本,并通过我们定义的模型来获得一组预测。计算完损失后,我们进行反向传播,存储每个参数的梯度。最后,我们使用自定义的sgd优化算法来更新模型的参数。

每次取一个小的batch_size,直到所有样本采集结束,定义为完成一个epoch.我们定义epoch的个数不停循环上述过程。

    for epoch in range(num_epochs):
        for X, y in data_iter(batch_size,features,labels):
            y_hat = linear(X,w,b)
            loss = squared_loss(y_hat,y)
            loss.sum().backward() # 进行反向传播得到梯度
            sgd((w,b),lr,batch_size)
        with torch.no_grad():
            train_l = squared_loss(net(features,w,b),labels)
            # print(train_l)
            print(f'epoch{epoch+1},loss{float(train_l.mean()):f}')

注:1)得到梯度时一定要先进行反向传播l.sum().backward()

​ 2) tensor形式变量可以使用tensor.sum(),tensor.mean()求均值和求和。

2. 线性回归简洁实现

在从零开始实现线性回归中,我们只依赖了torch框架为我们提供的两个功能:1.使用张量来存储数据和进行线性代数。2.通过自动微分来计算梯度。由于数据迭代器损失函数优化器神经网络很常用,所以深度学习库,已经为我们实现了这些组件。

2.1 生成数据集

  • 导入程序依赖库

    import numpy as np
    import torch
    from torch.utils import data
    from d2l import torch as d2l
    

注:d2l中的函数均为之前 从零实现内容 中定义好的

  • 生成数据集

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

2.2 读取数据集

def load_array(data_arrays, batch_size, is_train=True):
    """构建一个pytorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

说明:

  1. data.DataLoader()为pytorch中重要的数据读取类,其传入参数dataset需要满足特定的数据结构(映射形(map-style)或可迭代形(Iterable-style)),batch_size为每次迭代读取样本的大小,shuffle表示是否打乱数据集。
  2. data.TensorDataset类需要传入n个第一个维度相同的张量,便可将第一个维度的索引为数据和对应标签的索引。由此建立map-style类型数据集。
data_iter = load_array((features,labels),batch_size)
print(next(iter(data_iter)))

>>> [output]
[tensor([[ 0.1447, -0.2136],
        [-1.0608,  0.5082],
        [ 1.7026,  0.0333],
        [ 1.8841,  0.2664],
        [ 0.0773, -0.1409],
        [-1.3836, -0.6195],
        [ 0.7591, -0.2314],
        [-0.1942, -0.4251],
        [ 0.4559, -1.2682],
        [ 0.1983, -0.6629]]), 
 tensor([[5.2203],
        [0.3465],
        [7.5039],
        [7.0671],
        [4.8408],
        [3.5432],
        [6.5057],
        [5.2781],
        [9.4210],
        [6.8420]])]

创建python迭代器,观察数据集,注意iter中传入的一定要是可迭代的数据,通过next可以以此从迭代器中获取数据。

2.3 定义模型

对于标准操作,我们可以使用框架预定义好的层。这使我们只需要关注使用哪些层来构造模型,而不必关心层的实现细节。

Sequential类为串联在一起的多个层定义了一个容器。当给定输入数据,Sequential实例将数据传入到第一层,然后第一层的输出作为第二层的输入,以此类推。

线性网络为全连接层,即每一个输入都通过矩阵向量乘法连接到它的每一个输出。全连接层在Linear类中定义,其需要传入两个参数,第一个为输入特征的形状(本例中两个特征即 y = w 1 ∗ x 1 + w 2 ∗ x 2 + b y = w_1*x_1+ w_2*x_2+b y=w1x1+w2x2+b中的 x 1 x 2 x_1 \quad x_2 x1x2,第二个为输出特征的形状(本例为输出单个标量)

net = nn.Sequential(nn.Linear(2,1))

2.4 初始化模型参数

上一步中我们已经定义了好了模型,并实例化为net,我们可以通过net[index]选择容器中任一层,并通过net[index].weight.datanet[index].bias.data访问线性层的参数。此后便可以使用替换方法,重写参数值。

net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)

注:一般方法名称中带有_的,都表示替换覆盖参数值。

2.5 定义损失函数

计算均方误差使用MSELoss类,其默认返回所有样本损失的平均值。

loss = nn.MSELoss()

2.6 定义优化算法

小批量随机梯度下降(SGD)算法是一种优化神经网络的标准工具。在Pytorch的optim模块中实现了该算法许多变种。当我们实例化SGD时,我们需要指定需要优化的参数优化算法所需要的超参数字典。小批量随机梯度下降算法只需要设置lr值。这里设置为0.03.

trainer = torch.optim.SGD(net.parameters(),0.03)

注:可以通过net.parameters()获取网络层中所有参数。

2.7 训练

定义好损失函数,优化算法等基本组件后,模型的训练思路于上文从零实现基本一致。

num_epoch = 3
for epoch in range(num_epoch):
    for X,y in data_iter:
        l = loss(net(X),y)
        trainer.zero_grad()
        l.backward()
        trainer.step() # 优化
    l = loss(net(features),labels)
    print(f'epoch{epoch+1},loss{l:f}')

注:1)trainer.zero_grad()可以归零参数梯度。

​ 2)trainer.step()为按照规定方法进行参数优化。其一定要在反向传播得到梯度后进行。

3. 完整程序代码

:线性回归简洁实现及从零实现的python完整程序代码见d2l_线性回归完整python程序代码

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340