从零开始手撕线性回归:代码实战带你深入理解机器学习基础

从零开始手撕线性回归:代码实战带你深入理解机器学习基础

a6a8b433-2865-4068-a5f6-3bc083500a04

线性回归是机器学习中最基础且重要的算法之一,广泛应用于预测分析和数据建模。本文将带领读者从零开始理解线性回归的核心原理,并通过Python代码实战一步步实现一个简单的线性回归模型。无论你是机器学习初学者还是希望巩固基础的开发者,本文都将为你提供清晰的学习路径。

线性回归是一种监督学习算法,用于建立输入变量(特征)与输出变量(目标)之间的线性关系。其核心思想是找到一条最佳拟合直线,使得预测值与真实值之间的误差最小。假设我们有一组数据点 (x, y),线性回归试图找到参数 wb,使得以下公式成立:
y = w x + b y = wx + b y=wx+b
其中,w 是权重(表示特征对目标的影响程度),b 是偏置(表示目标值的基准)。为了衡量模型的拟合效果,我们通常使用均方误差(MSE)作为损失函数:
M S E = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 MSE=n1i=1n(yiy^i)2

实验代码

import random
import torch
from typing import Union, Tuple
import matplotlib.pyplot as plt


def synthetic_data(w: torch.Tensor, b: float, num_examples: int) -> Tuple[torch.Tensor, torch.Tensor]:
    """
    根据w和b生成线性回归曲线y
    :param w: 权重参数
    :param b: 偏执项
    :param num_examples: 样本的数量
    :return:
    """
    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))


def data_iter(batch_size: int, features: torch.Tensor, labels: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
    """
    生成器获取数据集
    :param batch_size: 小批量大小
    :param features: 特征矩阵X
    :param labels: 标签Y
    :return:
    X, Y

    """
    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]


def linreg(X, w, b):
    """
    线性回归模型
    y = w*x+b
    """
    return torch.matmul(X, w) + b


def sgd(params, lr, batch_size):
    """
    随机梯度下降优化器
    :param:params 权重参数w
    :param:lr 学习lv
    :param:batch_size 批量大小
    """
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()


def squared_loss(y_hat: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
    """
    均方损失函数
    :param: y_hat 预测值
    :param: y 真实值
    :return
    损失值
    """
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2


def plt_loss(loss_list: list):
    """
    画损失值随epoch变化图
    :param loss_list:
    :return:
    """
    plt.figure(figsize=(10, 5))
    epochs = list(range(1, len(loss_list) + 1))
    # 绘制损失随轮次变化的折线图
    plt.plot(epochs, loss_list, label='Training Loss', color='blue')

    # 添加标题和坐标轴标签
    plt.title('Training Loss Over Epochs')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')

    # 显示网格
    plt.grid(True)

    # 添加图例
    plt.legend()

    # 展示图形
    plt.show()


def main():
    true_w = torch.tensor([2, -3.4])
    true_b = 4.2
    # 获取样本X和真实的标签值
    features, labels = synthetic_data(true_w, true_b, 1000)

    # 画真实值的散点图
    plt.scatter(features[:, (1,)].detach().numpy(), labels.detach().numpy(), 1)
    plt.show()
    plt.close()

    batch_size = 10
    # 初始化w和b
    w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)
    b = torch.zeros(1, requires_grad=True)
    lr = 0.03
    num_epochs = 30
    net = linreg
    loss = squared_loss
    loss_list = []
    for epoch in range(num_epochs):
        i = 0
        total_loss = 0
        for X, y in data_iter(batch_size, features, labels):
            i += 1
            l = loss(net(X, w, b), y).sum()
            total_loss += l.item()
            print(f"train epoch:{epoch + 1} batch:{i} loss:{l}")
            # 反向传播自动计算梯度
            l.backward()
            # 使用优化器将梯度更新到参数w和b中
            sgd([w, b], lr, batch_size)
        loss_list.append(total_loss)
        with torch.no_grad():
            train_l = loss(net(features, w, b), labels)
            print(f'val epoch {epoch + 1}, loss {float(train_l.mean()):f}')
    plt_loss(loss_list)


if __name__ == '__main__':
    main()

image-20250315152958523

image-20250315153016193

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平时不搬砖

创造不易,请动动你发财的小手

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值