线性回归从零开始实现

本文详细介绍了线性回归的基本原理,包括其假设和目标,以及在Python中使用PyTorch实现线性回归模型的步骤,包括数据收集、特征选择、模型构建、参数估计、模型评估和预测。通过实例演示了如何训练模型并分析参数估计的误差。
摘要由CSDN通过智能技术生成

原理

线性回归是一种用于建立变量之间线性关系的统计模型。其原理基于以下假设:

  1. 线性关系:线性回归假设自变量(输入变量)和因变量(输出变量)之间存在一个线性关系。

  2. 独立性:自变量与误差项之间应该是独立的,即自变量的取值不受误差项的影响。

  3. 同方差性:对于所有自变量的取值,误差项的方差应该是恒定的。

  4. 无多重共线性:自变量之间应该是线性独立的,不存在严格的线性依赖关系。

基于这些假设,线性回归的目标是找到最佳拟合直线,以最小化观测数据与模型之间的误差。常用的拟合准则是最小二乘法,即最小化观测数据的残差平方和。

                                                                一元线性回归


 

具体实现步骤文字说明

  1. 收集数据:获取包含自变量和因变量的训练数据样本。

  2. 特征选择:根据实际问题选择合适的自变量作为模型输入。

  3. 模型建立:建立线性回归模型,假设因变量与自变量之间存在线性关系。

  4. 参数估计:使用最小二乘法或其他优化算法,估计模型参数,即拟合直线的斜率和截距。

  5. 模型评估:通过评估拟合程度和统计指标(如均方误差、决定系数等),判断模型的好坏。

  6. 预测与应用:利用训练好的模型进行预测,对新的自变量进行因变量的估计或预测。


 

具体实现

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

        生成数据集,torch.normal函数,该函数返回从单独的正态分布中提取的随机数的张量,该正态分布的均值是mean,也就是噪声。标准差是std。torch.matmul函数,是tensor的乘法,输入可以是高维的。当输入都是二维时,就是普通的矩阵乘法,和tensor.mm函数用法相同。synthetic_data函数是d2l的封装函数,具体作用是根据相关参数,生成随机数据集。

def synthetic_data(w, b, num_examples):  #@save
    """生成 y = Xw + b + 噪声。"""
    X = torch.normal(0, 1, (num_examples, len(w)))  #最后一个参数指定X维度,len(w)特征个数
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X, y.reshape((-1, 1))

        features中的每一行都包含一个二维数据样本,labels中的每一行都包含一维标签值(一个标量) 

print('features:', features[0],'\nlabel:', labels[0])

        d21.set figsize函数是Deep Learning框架中的一个函数,用于设置Matplotlib图像的大小。该函数可以接受两个参数,分别为宽度和高度,单位为英寸。使用该函数可以方便地调整图形的大小,以适应不同的显示设备和打印需求。plt.scatter( ),它用于散点图的绘制。d2l.plt.scatter 中的参数其实就是matplotlib中的scatter。 

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

         定义一个data iter函数,该函数接收批量大小特征矩阵和标签向量作为输入,生成大小为batch_size的小批量]。每个小批量包含一组特征和标签。random.shuffle()用于将一个列表中的元素打乱顺序,这个方法不会生成新的列表,只是将原列表的次序打乱。

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相等。

batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break

 初始化模型参数,初始值为0.后续就是逐步更新参数,使得其更加符合我们的模型。

w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
#w = torch.zeros((2,1) ,requires_grad=True)#权重变为0
b = torch.zeros(1, requires_grad=True)

 定义模型,主要是将模型的输入和参数同模型的输出结合起来,注意存在偏置b

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

         定义损失函数,通过广播,将真实值y的形状转化为与预测值y_hat形状相同

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

定义优化算法

在pytorch中,tensor有一个requires_grad参数,如果设置为True,则反向传播时,该tensor就会自动求导。tensor的requires_grad的属性默认为False,若一个节点(叶子变量:自己创建的tensor)requires_grad被设置为True,那么所有依赖它的节点requires_grad都为True(即使其他相依赖的tensor的requires_grad = False)。当requires_grad设置为False时,反向传播时就不会自动求导了,因此大大节约了显存或者说内存。

torch.no_grad的作用:在该模块下,所有计算得出的tensor的requires_grad都自动设置为False。即使一个tensor(命名为x)的requires_grad = True,在with torch.no_grad计算,由x得到的新tensor(命名为w-标量)requires_grad也为False,且grad_fn也为None,即不会对w求导。

param.grad其实是batch中所有样本的grad总和,所以这个时候除以batch_size就是相当于取一个平均值,这样就算下一次传入的batch_size改变了,最后也不会影响得到的平均数。

def sgd(params, lr, batch_size):  #@save
    """小批量随机梯度下降。"""
    with torch.no_grad():       #当前计算不需要反向传播
        for param in params:
            param -= lr * param.grad / batch_size   #手动实现参数更新
            param.grad.zero_()

        训练

数据准备:准备训练数据集,包括输入特征和对应的输出值。通常将输入特征表示为一个矩阵X,每一行表示一个样本的特征值,输出值表示为一个向量y。

参数初始化:初始化模型参数,包括权重w和偏置b。

定义模型:定义线性回归模型,使用参数w和b构建模型的预测函数,一般为线性函数 y = Xw + b。

定义损失函数:定义损失函数,衡量模型预测值与真实值之间的差距。线性回归通常使用均方误差(MSE)作为损失函数,即损失函数为 L = (1/2m) * sum((y_pred - y)^2),其中m为样本数。

定义优化算法:定义优化算法,用于更新模型参数以最小化损失函数。常用的优化算法是梯度下降法,通过计算损失函数对参数的梯度来更新参数。梯度下降法的更新公式为 w = w - learning_rate * gradient_w,b = b - learning_rate * gradient_b,其中learning_rate为学习率,gradient_w和gradient_b分别为损失函数对w和b的梯度。

训练模型:使用训练数据集进行模型训练。迭代训练过程中,根据当前模型参数计算预测值,并计算损失函数。然后根据损失函数的梯度更新模型参数,重复迭代直到收敛或达到指定的迭代次数。

lr = 0.2   #0.1效果也不错
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}')

参数比较

print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')


 

实验小结

        PyTorch提供的函数往往具有更好的数值稳定性,要善于使用pytorch提供的函数,更简洁地实现softmax回归等方法。

        从零开始实现了线性回归模型。首先,我们使用了一个简单的数据集来训练模型,并使用梯度下降算法来更新模型的参数。我们还实现了模型的预测函数和评估函数,以便对模型的性能进行评估。通过实验,我们发现,线性回归模型可以很好地拟合线性关系的数据。当我们使用更多的训练样本和更小的学习率时,模型的拟合效果更好。然而,当训练样本较少或学习率过大时,模型的拟合效果会变差。

        通过本次实验,使我对线性回归模型有了更深入的理解。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值