动手学深度学习——线性回归(原理解释+代码详解)

news/2025/2/11 18:24:59/

目录

  • 1、线性回归
  • 2、线性回归模型
    • 2.1 线性模型
    • 2.2 损失函数
      • 2.2.1 平方差损失函数
      • 2.2.2 整个数据集上的损失函数
    • 2.3 随机梯度下降
    • 2.4 用模型进行预测
  • 3、线性回归的简单实现
    • 3.1 生成数据集
    • 3.2 读取数据集
    • 3.3 初始化模型参数
    • 3.4 定义模型
    • 3.5 定义损失函数
    • 3.6 定义优化算法
    • 3.7 训练
  • 4、线性回归的简洁实现
    • 4.1 生成数据集
    • 4.2 读取数据集
    • 4.3 定义模型
    • 4.4 初始化模型参数
    • 4.5 定义损失函数
    • 4.6 定义优化算法
    • 4.7 训练
    • 4.8 小结

1、线性回归

回归是能够为一个或多个自变量与因变量之间关系建模的一类方法。

当函数为未知参数的线性函数时,称为线性回归模型。

机器学习领域中大多数任务通常都与预测有关,当我们预测一个数值时,就会涉及到回归问题。常见的例子有:预测价格,预测需求等。

2、线性回归模型

假设自变量x与因变量y满足线性关系,数学中一般的线性方程为:y=ax+b
我们将y可以表示为x中元素的加权和,并且允许包含观测值的一些噪声,可以将式子设为:
在这里插入图片描述

在机器学习中,线性回归模型建立的步骤一般是:
先假设一个线性模型——>计算该模型预测值与实际值的差距loss(损失函数)——>通过优化算法(如随机梯度下降)来更新参数如w,b来降低误差

2.1 线性模型

一般我们将线性假设可以表示为特征的加权和:

x表示元素的特征。
w称为权重,权重决定了每个特征对我们预测值的影响。
b称为偏置或者偏移量,偏移量指当所有特征值为0时,预测值应该为多少。(对于偏置的加入可以帮助模型有更好的泛化能力,即该模型在未见过的数据也有较好的表现)

如果是房屋的价格可以表示为:

2.2 损失函数

对于我们假设的线性模型,预测值可能离实际值还有差距,为了表示这一差距,我们引入损失函数这一概念。

损失函数能够量化目标的实际值与预测值之间的差距,通常选择非负数作为损失,且数值越小表示损失越小。

2.2.1 平方差损失函数

回归问题中最常用的损失函数是平方误差函数。在这里插入图片描述
常数1/2便于我们在对损失函数(平方项)求导时,能够将系数化为1

2.2.2 整个数据集上的损失函数

为了度量模型能够在整个数据集上的质量,我们需计算训练集n个样本的损失均值。
即上式求和再除以n(含参数w,b的为展开式)
在这里插入图片描述
我们的目标则是为了寻得一组参数w*,b*,使最小化训练样本的总损失
在这里插入图片描述

2.3 随机梯度下降

如上文所说,我们为了使总损失最小,我们需要对参数w和b进行更新。
而更新方法则是使用到一种叫做梯度下降的方法,它不断在损失函数递减的方向上更新参数。

梯度下降的简单用法是计算损失函数关于模型参数的导数(可以称为梯度),损失函数为数据集中所有样本的损失均值。

实际过程中由于数据集较大,每次更新参数会遍历整个数据集,执行会非常缓慢。我们通常会在每次更新的时候抽取一小批量样本,这种变体叫做小批量随机梯度下降。

每次迭代中,该过程为:
1、首先随机抽取一个小批量β,它是由固定数量的训练样本组成
2、计算小批量的平均损失关于模型参数的导数
3、将梯度乘以一个预先确定的正数η,并从当前参数的值中减掉
在这里插入图片描述
其中∂表示偏导数,η表示学习率,批量大小和学习率的值通常是手动预先设定的,这些可以调整但不在训练过程中更新的参数称为超参数。

2.4 用模型进行预测

给定“已学习”的线性回归模型在这里插入图片描述
我们现在可以通过房屋面积x1和房龄x2来估计一个(未包含在训练数据中的)新房屋价格。
给定特征估计目标的过程通常称为预测或推断。

3、线性回归的简单实现

了解到关键思路后,我们可以通过代码来实现线性回归。
下面的代码有些是调用或者定义的函数,刚开始学习的时候不必纠结代码,主要了解代码的整体实现过程。

步骤函数
生成数据集在这里插入图片描述
读取数据集在这里插入图片描述
初始化模型参数在这里插入图片描述
定义模型在这里插入图片描述
定义损失函数在这里插入图片描述
定义优化算法在这里插入图片描述
训练在这里插入图片描述

3.1 生成数据集

生成一个包含1000个样本的数据集,每个样本包含从标准正态分布中采用的2个特征。

线性模型参数
在这里插入图片描述
生成的数据集及标签:
在这里插入图片描述
噪声项ε可以视为模型预测和标签时的潜在观测误差,它服从均值为0的正态分布,标准差为0.01。

#matplotlib包用于作图,且设置成嵌入显示
%matplotlib inline
import random
import torch
from d2l import torch as d2l
def synthetic_data(w, b, num_examples):  #@save"""生成y=Xw+b+噪声"""# normal:返回一个张量,包含从指定均值means和标准差std的离散正态分布抽取的一组随机数X = torch.normal(0, 1, (num_examples, len(w))) #均值为0,方差为1# matmul:矩阵乘法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中的每一行都包含一维标签值(一个标量)。
在这里插入图片描述

3.2 读取数据集

训练模型要对数据集进行遍历,每次抽取一小批量,并使用它们来更新模型。

data_iter函数,该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量。每个小批量包含一组特征和标签。
在这里插入图片描述

random.shuffle()——打乱顺序
indices[i: min(i + batch_size, num_examples)])——按batch_size进行切片,得到小批量

# features:特征矩阵,labels:标签向量
def data_iter(batch_size, features, labels):# len():求矩阵时长度相当于输出行的数目num_examples = len(features)# 生成标号,列表形式储存indices = list(range(num_examples))# 这些样本是随机读取的,没有特定的顺序random.shuffle(indices)# 构造随机样本,打乱样本,等间隔访问,达到随机# 从0开始,到num_exampls结束,每次间隔batc_sizefor i in range(0, num_examples, batch_size):batch_indices = torch.tensor(indices[i: min(i + batch_size, num_examples)])# yield:返回一个值,并且记住这个返回的位置,下次迭代从这个位置开始yield features[batch_indices], labels[batch_indices]

3.3 初始化模型参数

我们通过从均值为0、标准差为0.01的正态分布中采样随机数来初始化权重, 并将偏置初始化为0。
初始化参数后,我们的任务是更新这些参数,直到这些参数足够拟合我们的数据。

每次更新都需要计算损失函数关于模型参数的梯度。 有了这个梯度,我们就可以向减小损失的方向更新每个参数。

torch.normal(means, std, out=None)均值,标准差,可选的输出张量

# 此时size定义两行一列
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)#  requires_grad用于说明当前向量是否需要在计算中保留对应的梯度信息
b = torch.zeros(1, requires_grad=True)

3.4 定义模型

线性回归模型:y=wx+b

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

3.5 定义损失函数

采取平方损失函数
在这里插入图片描述
y.reshape(y_hat.shape)将y的形状统一为y_hat

# y_hat是预测值, y是真实值
def squared_loss(y_hat, y):  #@save"""均方损失"""# 返回平方误差return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2

3.6 定义优化算法

小批量随机梯度下降:在每一步中,使用从数据集中抽取的一个小批量,然后根据参数计算损失的梯度。接下来,朝着减少损失的方向更新我们的参数。

每一步更新的大小由学习速率lr决定这里是引用

with torch.no_grad()
强制之后的内容不计算梯度。
在使用pytorch时,并不是所有的操作都需要进行计算图的生成(计算过程的构建,以便梯度反向传播等操作)。而对于tensor的计算操作,默认是要进行计算图的构建的。


def sgd(params, lr, batch_size):  #@save"""小批量随机梯度下降"""with torch.no_grad():for param in params:# param.grad是求梯度param -= lr * param.grad / batch_size# 因为pytorch不会自动将梯度设置为0,设置为零后下次计算就不会与上次相关了param.grad.zero_()

3.7 训练

训练过程可以概括为:

  1. 在每次迭代中,读取一小批量训练样本,并通过模型来获得一组预测。
  2. 计算完损失后,开始反向传播,存储每个参数的梯度。
  3. 调用优化算法sgd来更新模型参数。
    这里是引用
lr = 0.03 #学习率(超参数)
num_epochs = 3 #把整个数据扫3遍
net = linreg #模型生成函数
loss = squared_loss #均方损失函数for epoch in range(num_epochs):for X, y in data_iter(batch_size, features, labels):# 'X'和'y'的小批量损失# 因为'l'形状是('batch_size',1),而不是一个标量。l中的所有元素被加到一起,# 并以此计算关于[w,b]的梯度l = loss(net(X, w, b), y)  # X和y的小批量损失# 求和之后算梯度,这里backward()会对w和b进行求导l.sum().backward()sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数# net(features, w, b):预测值with torch.no_grad():train_l = loss(net(features, w, b), labels) print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')

输出:损失与估计误差
在这里插入图片描述
在这里插入图片描述

4、线性回归的简洁实现

这里采用深度学习框架来简洁实现线性回归模型,我们不必单独分配参数、不必定义我们的损失函数,也不必手动实现小批量随机梯度下降。,可以直接调用API来进行训练。

API(Application Programming Interface)应用程序接口。

4.1 生成数据集

d2l包:由李沐老师等人开发的《动手学深度学习》配套的包,提供一些数学运算工具。
synthetic_data():合成数据函数。

import numpy as np
import torch
from torch.utils import data
from d2l import torch as d2ltrue_w = torch.tensor([2, -3.4])
true_b = 4.2
# 合成数据
features, labels = d2l.synthetic_data(true_w, true_b, 1000)

next(iter(data_iter))使用iter构造Python迭代器,并使用next从迭代器中获取第一项。

4.2 读取数据集

TensorDataset 将张量的第一个维度视为数据集大小的维度,数据集在传入 DataLoader 后,该维度也是 batch_size 所在的维度。
shuffle表示对数据打乱顺序。

def load_array(data_arrays, batch_size, is_train=True):  #@save"""构造一个PyTorch数据迭代器"""# 将传入的特征和标签作为list传到TensorDataset,里面得到一个pytorch的数据集dataset = data.TensorDataset(*data_arrays)# 调用Dataloader每次从dataset里面挑选batch_size个样本出来return data.DataLoader(dataset, batch_size, shuffle=is_train)batch_size = 10
# 将特征和标签传入load_array
data_iter = load_array((features, labels), batch_size)

4.3 定义模型

对于标准深度学习模型,我们可以使用框架的预定义好的层。
首先定义一个模型变量net,它是一个Sequential类的实例。 Sequential类将多个层串联在一起。 当给定输入数据时,Sequential实例将数据传入到第一层, 然后将第一层的输出作为第二层的输入,以此类推。

Sequential类可以看成一个容器包装各层,当一个模型较简单的时候,我们可以使用torch.nn.Sequential类来实现简单的顺序连接模型。

# nn是神经网络的缩写
from torch import nn# 第一个指定输入特征形状为2,第二个指定输出特征形状为单个标量为1
net = nn.Sequential(nn.Linear(2, 1))

4.4 初始化模型参数

在使用net之前,我们需要初始化模型参数。在这里,我们指定每个权重参数应该从均值为0、标准差为0.01的正态分布中随机采样, 偏置参数将初始化为零。

# net[0] 表示第0层,.weight访问w,data就是w的值,下划线的意思是,使用正态分布,替换掉权重data的值
net[0].weight.data.normal_(0, 0.01)
# 使用填充0,data是偏置b,替换掉偏差data的值
net[0].bias.data.fill_(0)

4.5 定义损失函数

计算均方误差使用的是MSELoss类,也称为平方L2范数。 默认情况下,它返回所有样本损失的平均值。

loss = nn.MSELoss()

4.6 定义优化算法

这里使用小批量随机梯度下降算法,该算法在PyTorch在optim模块中。
当我们实例化一个SGD实例时,我们要指定优化的参数 (可通过net.parameters()从我们的模型中获得)以及优化算法所需的超参数字典。 小批量随机梯度下降只需要设置lr值,这里设置为0.03。

net.parameters()是一个生成器,用于生成模型中的参数。其中,第一个参数是生成器的第一个元素,第二个参数是该元素的具体参数值。该函数用循环获取所有参数,并将其作为参数传递。

# net.parameters():net里面的所有参数,包括w,b,然后指定学习率lr
trainer = torch.optim.SGD(net.parameters(), lr=0.03)

4.7 训练

在每个迭代周期里,我们将完整遍历一次数据集(train_data), 不停地从中获取一个小批量的输入和相应的标签。 对于每一个小批量,我们会进行以下步骤:

  1. 通过调用net(X)生成预测并计算损失l(前向传播)。
  2. 通过进行反向传播来计算梯度。
  3. 通过调用优化器来更新模型参数。
num_epochs = 3 #迭代3次
for epoch in range(num_epochs):for X, y in data_iter:l = loss(net(X) ,y) #因为net自带参数,所以和之前不同的是不需要再传进去w和btrainer.zero_grad() #梯度清零l.backward()  #计算梯度trainer.step() #调用step函数进行更新 l = loss(net(features), labels)print(f'epoch {epoch + 1}, loss {l:f}')
w = net[0].weight.data
print('w的估计误差:', true_w - w.reshape(true_w.shape))
b = net[0].bias.data
print('b的估计误差:', true_b - b)

输出:损失和w、b的估计误差
在这里插入图片描述
在这里插入图片描述

4.8 小结

  • 可以使用PyTorch的高级API更简洁地实现模型。
  • 在PyTorch中,data模块提供了数据处理工具,nn模块定义了大量的神经网络层和常见损失函数。

参考资料:
[1]动手学深度学习:http://zh-v2.d2l.ai/index.html
[2]跟李沐学AI:https://space.bilibili.com/1567748478


http://www.ppmy.cn/news/879535.html

相关文章

数据中台建设方案-基于大数据平台(下)

数据中台建设方案 -基于大数据平台(下) 1数据中台建设方案 1.1 总体建设方案 1.2大数据集成平台 1.3大数据计算平台 1.3.1数据计算层建设

MySQL技术内幕-InnoDB存储引擎第2版-学习笔记-01

MySQL技术内幕-InnoDB存储引擎第2版-学习笔记-01 1. MySQL体系结构和存储引擎 1.1 定义数据库和实例 数据库database: 物理操作系统文件或其他形式文件类型的集合。 当使用NDB引擎时,数据库的文件可能不是操作系统上的文件,而是存放于内存中…

计算机基础知识——Linux命令简介

1、Linux操作系统基础知识 在了解linux命令之前,我们先了解一点基本概念。 1.1、操作系统概念 操作系统(operating system 简称OS)是一个大型的程序系统,他负责计算机的全部软、硬件资源的分配、调度工作,控制并协调多…

计算机系统类词汇

abbreviate vt.缩写,省略 abbreviation n.缩短,省略,简称 abend 异常结束 abnormal 异常 abort 异常中止 aboveboard ad.照直,公开的 absence n.缺少,没有 absolute 绝对 absorption 吸收 abstract 抽象 acceleration 加速度 accelerator n.加速装置,加速剂 …

编程术语英汉对照

abstract 抽象的 抽象的 abstraction 抽象体、抽象物、抽象性 抽象体、抽象物、抽象性 access 存取、取用 存取、访问 access level 存取级别 访问级别 access function 存取函式 访问函数 activate 活化 激活 active 作用中的 adapter 配接器 适配器 address 位址 地址…

MYSQL学习笔记1

MySQL技术指南 > MYSQL学习笔记 第一章: 第一节:数据库和实例 数据库:物理操作希望文件或其他形式文件类型的集合。 在MySQL中,数据库文件可以是frm、myd、myi、 ibd结尾的文件。当使用NDB引擎时,数据库的文件可以…

《MySQL技术内幕:InnoDB存储引擎》第2版笔记

第1章 MySQL体系结构和存储引擎 1.1 定义数据库和实例 在MySQL数据库中,数据库文件可以是fm、MYD、MYI、ibd结尾的文件。MySQL数据库由后台线程以及一个共享内存区组成。MySQL被设计为一个单进程多线程架构的数据库,这点与SQL Server比较类似,但与Oracle多进程的架构有所不…

野火洋桃STM32开发版学习指导完整版

该文章是我历时一个月整理总结而成,专门针对想要通过野火&洋桃STM32开发板入门stm32的读者。由于csdn编辑限制,该学习指导只包含文字信息。如需查看含图片的完整版可进入我的博客下载页。完整版内容详实,保证您看完该文对野火&洋桃ST…