惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
IT之家
IT之家
GbyAI
GbyAI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
博客园 - 【当耐特】
The Cloudflare Blog
宝玉的分享
宝玉的分享
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
Visual Studio Blog
小众软件
小众软件
博客园_首页
Last Week in AI
Last Week in AI
J
Java Code Geeks
V
V2EX
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
博客园 - 司徒正美
Engineering at Meta
Engineering at Meta
The GitHub Blog
The GitHub Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
N
Netflix TechBlog - Medium
F
Full Disclosure
B
Blog
H
Help Net Security
C
Check Point Blog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
L
LangChain Blog
P
Proofpoint News Feed
D
Docker
Microsoft Security Blog
Microsoft Security Blog

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12) Snack JSONPath 项目架构分析 Claude Code Buddy 小析:一个非核心功能,如何体现产品的细节完成度 AI新时代下的图床管理方案-Cloudflare图床+MCP+Skills方案指南 化繁为简:顺丰速运App如何通过 HarmonyOS SDK实现专业级空间测量 从零实现富文本编辑器#13-React非编辑节点的内容渲染 AI开发-python-langchain框架(3-23-OpenAI Functions风格Tool Calling智能助手) .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut PbootCMS 网站内容数量多导致访问慢?这些实用优化方案帮你提速! - 家兴网络技术工作室 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 网站漏洞怎么发现并修复?一篇实用指南(附完整流程) - 家兴网络技术工作室 开了 TUN 模式还是直连?90% 的人都踩过这个坑 Github日报|2026年04月12日 - AI一族 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术 Etsy 把 1000 个 MySQL 分片迁进 Vitess:425TB 数据背后的真正问题不是性能,而是运维规模 MicroPython LVGL基础知识和概念:底层渲染与性能优化 - FreakStudio 数据库草图算法 Python 潮流周刊#146:CPython 引入 Rust 的进展 - 豌豆花下猫 最小生成树 - mofei1116 红日靶场七:从外网入口、容器逃逸到 AD 接管的完整利用链复盘 - YouDiscovered1t 分享四款开源且实用的 Kafka 管理工具 - 追逐时光者 vLLM 权重加载机制全解析:从挑战到理想架构 LCT 学习笔记 - ACehomoxue Avalonia UI 12.0.0 正式发布:架构演进和性能飞跃 - 张善友 当 AI Agent 把调用链拉长,延迟开始成为一门生意 conhost.exe 无法显示 U+2717 - 145a 太秀了,我把自己蒸馏成了 Skill!已开源 - 程序员鱼皮 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 基于 Ghostty 带有分割标签页和为 Claude 编程设计的通知终端 - BugShare AI 焊死入口:教育的“操作系统级”重塑 - 郝hai 初级Java开发工程师使用sql脚本编写代码的过程是简单而且不糊涂 - CoderOilStation Claude Code通关手册(六):MCP协议完全指南 - 暮色之狐 边框灯光环绕动画特效实现指南 - Newbe36524 开源:子木蒸馏版的 SEO 审计工具 seo-audit-skill v1.0 我所理解的Python元模型 【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite - 程序员Seven Claude 和 Codex 在审计 Skill 上性能差异探究 - ACai_sec AScript如何实现中文脚本引擎 - rockey627 【渗透测试】HTB Season10 Garfield 全过程wp - dynasty_chenzi Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革 树状数组正确性证明 - AC-wyr 你的 AI 焦虑,可能比 AI 本身更危险——ATM 机没有消灭银行柜员,但恐慌消灭了你的判断力 - 我没有三颗心脏 一个拉胯的分库分表方案有多绝望?整个部门都在救火! - 冰河团队 动态规划入门必学之走方格问题 - Ofnoname PostgREST 与 PostgreSQL 角色权限配置全解析(生产级实践) - SheepDog1998 使用 UEFI 图形输出协议 GOP 在屏幕上显示图像的方法 - 阿源- Claude Code通关手册(五):组建你的AI专家团队,子代理系统 - 暮色之狐 一个程序员到架构师的催婚路之感悟(整整10年后的催婚相亲感悟) - MisterLip 用 Agent Skill 自动生成工作周报 - 赵康
【动手学大语言模型】神经网络启蒙:PyTorch 入门实战
吾辈亦有感 · 2026-05-13 · via 博客园_首页

我是一名 AI 算法工程师,在学习大模型的过程中踩过很多坑,我把自己的经验写成了一本免费电子书《动手学大语言模型:写给程序员的手搓LLM实战指南》

本书的主要目标是帮助读者通过动手实践的方式快速地学懂 LLM。

📘 整本书已免费开源阅读全文

我设计了一系列的实战任务串联大模型的技术演进脉络,并运用大量视觉语言,使用数百张插图帮助读者对LLM学习过程中的主要概念和流程建立直观认识,降低理解的难度。

亲手制作的数百幅全彩插图,给你提供极致视觉化呈现,让所有的技术难点都可视化、易理解。

本文是其中一个章节的节选,完整版包含更多实战项目和深度内容。

📘 整本书已免费开源,欢迎阅读完整版阅读全文


神经网络启蒙:PyTorch 入门实战

本章我们将正式迈入神经网络的大门,从零开始构建并训练你的第一个神经网络模型。通过本次任务,你将会了解神经网络模型的基本结构、训练流程,并掌握如何使用深度学习框架 PyTorch 快速搭建并进行训练一个多参数回归模型。

任务背景

你是一名程序员。自从上次与小美合作,成功教会机器人制作柠檬水后,你的无人柠檬水摊位生意渐火。看着账户里稳定增长的收入,一个更大胆的想法浮现出来:如果只卖经典原味柠檬水,市场天花板显而易见;但如果能让机器人学会开发多种口味的特调柠檬水,必将大大提升产品的吸引力和营收潜力。

你尝试在基础柠檬水中加入薄荷叶、百香果、黄瓜片或少许玫瑰盐,以创造不同的风味。薄荷带来清凉感,黄瓜增加清新的后韵,而一点点玫瑰盐则能神奇地提升整体的层次感——但这些配料之间会相互影响,如何平衡它们才是真正的艺术。

你不断尝试各种配比组合,邀请顾客品尝并记录评分,逐步优化配方。然而,这种试错过程漫长且成本高昂。于是你想:如果能训练一个模型,自动根据配料配比预测顾客评分,问题便可迎刃而解。

最少必要知识

  • 神经网络概论
  • PyTorch 基础

任务鸟瞰

本次的任务是:训练一个模型,根据薄荷叶、百香果、黄瓜片和玫瑰盐的配比,预测顾客对柠檬水的评分。

预测一杯柠檬水的口感评分,看似简单,实则受到多种参数的复杂影响。薄荷叶的多少、黄瓜片的厚薄、玫瑰盐的用量……这些细微变量都在悄然塑造着最终的口感体验。面对如此纷繁的因素,我们难以通过手动方式去管理每一个参数,因为很难精确描述它们与口感评分之间的复杂关系。

此时,不妨观察人类如何对柠檬水进行评分,并尝试从生物神经元的机制中汲取灵感。

神经网络

在大脑中,神经元是信息处理的基本单元。一个神经元会接收外界的刺激信号,神经突触根据输入信号的重要性进行加权处理;当加权后的信号总和超过某个阈值时,神经元被激活,产生输出信号。

神经元工作过程可分为两步:

  • 加权求和:对多个输入信号进行加权融合;
  • 点火激活:若加权和超过阈值,则触发输出。

当我们品尝柠檬水并给出评分时,大脑中的神经元正在进行类似处理。味觉、嗅觉等感官接收到的特征信息(薄荷的清香、黄瓜的清爽、玫瑰盐的风味等)被传递至相关神经元,经过加权整合,最终形成对口感的综合评判,即评分。

我们可以将上述过程抽象为人工神经元模型。单个神经元的计算可表示为:**输出 = 激活函数(Σ(权重 × 输入) + 偏置)**。

计算的过程如下图所示:

其中:

  • :输入信号
  • :权重
  • :偏置(可视为阈值)
  • :加权求和
  • :激活函数
  • :输出信号

更进一步,我们可以将神经元结构表示为数学公式:

模型结构

由大量神经元相互连接形成的系统称为神经网络(Neural Network)。本次任务我们使用神经网络,构建一个用于预测柠檬水评分的模型,学习三种配料与评分之间的关系。其模型结构如下:

本次的任务也是一个简单的多参数回归模型,所以我们只它使用一个神经网络层构建模型即可,这个神经网络层有3个输入和1个输出。

开发神经网络模型和机器学习模型一样,通常也遵循以下四个步骤:数据准备、模型定义、模型训练与模型评估。下文将依此流程组织内容。在开始之前,我们先配置环境,避免因为环境不同而导致程序不能复现。

环境配置

安装依赖

!pip install --upgrade dsxllm -i https://pypi.org/simple

环境版本

from dsxllm.util import show_version

show_version()

本书愿景:
+------+--------------------------------------------------------+
| Info |                  《动手学大语言模型》                  |
+------+--------------------------------------------------------+
| 作者 |                       吾辈亦有感                       |
| 哔站 |      https://space.bilibili.com/3546632320715420       |
| 定位 | 基于'从零构建'的理念,用实战帮助程序员快速入门大模型。 |
| 愿景 | 若让你的AI学习之路走的更容易一点,我将倍感荣幸!祝好😄 |
+------+--------------------------------------------------------+
环境信息:
+-------------+--------------+------------------------+
| Python 版本 | PyTorch 版本 | PyTorch Lightning 版本 |
+-------------+--------------+------------------------+
|   3.12.12   |    2.10.0    |         2.6.1          |
+-------------+--------------+------------------------+

数据准备

数据集下载

如果你想要亲自动手复现运行结果,可以点击此链接下载相关数据集。

点击下载数据集

观察数据

加载并预览数据集前5行,了解数据结构。

# 读取数据
import pandas as pd

# 读取训练集数据
train_data_frame = pd.read_csv('./dataset/craft_lemonade_train.csv')
train_data_frame[:5]

薄荷叶 黄瓜片 玫瑰盐 顾客评分
0 9 10 6 8.60
1 10 9 7 8.90
2 5 3 7 4.80
3 3 4 6 4.10
4 4 9 2 5.25

从输出结果可以看到数据包含四个字段:薄荷叶、黄瓜片、玫瑰盐(输入特征)和 顾客评分(目标值)。我们需要将每一条训练数据转化成 <输入特征, 目标值> 数据对的形式。

定义数据加载方法

根据文件路径加载 csv 数据,分离出输入特征与对应的目标值标签。

def load_data(file_path):
    # 读取 csv 数据
    data_frame = pd.read_csv(file_path)

    # 将DataFrame数据转换为列表格式
    features = data_frame[['薄荷叶''黄瓜片''玫瑰盐']].values.tolist()
    target_scores = data_frame[['顾客评分']].values.tolist()

    return features, target_scores

加载训练集和评估集

加载训练数据和评估数据,并打印训练集和评估集的样本数量,确认数据加载成功。

train_features, train_targets = load_data('./dataset/craft_lemonade_train.csv')
val_features, val_targets = load_data('./dataset/craft_lemonade_val.csv')

print(f"训练数据集样本数量:{len(train_features)}")
print(f"评估数据集样本数量:{len(val_features)}")

训练数据集样本数量:800
评估数据集样本数量:200

模型定义

PyTorch 核心概念

要构建并训练一个神经网络模型,离不开强大的深度学习框架。PyTorch 以其灵活性、Python 友好的设计而广受欢迎,它提供了直观的接口来构建和调试复杂神经网络,是目前入门人工智能的主流选择之一。

为了顺利搭建柠檬水评分预测模型,我们需要了解以下 PyTorch 的核心概念:

概念 说明
张量(Tensor) PyTorch 中数据的基本单元,配料配比、评分等参数都需转换为张量。
网络层(Layer) 模型处理数据的基本模块,需明确其输入输出维度。
模型(Model) 多个网络层的有序堆叠,形成完整的神经网络模型。

Pytorch详细教程可参考:https://www.runoob.com/pytorch/pytorch-basic.html

柠檬水评分预测模型的模型结构

本次任务我们使用 PyTorch 训练一个预测 3 种配料与顾客评分之间关系的神经网络模型。

回顾一下模型结构:

柠檬水评分预测模型的代码实现

定义一个继承自 nn.Module 的柠檬水评分模型类 LemonadeRatingModel,这是 PyTorch 中定义神经网络模型的标准方式。

import torch
import torch.nn as nn

# 定义柠檬水评分预测模型,继承自 nn.Module,这是PyTorch中定义神经网络的标准方式
class LemonadeRatingModel(nn.Module):
    def __init__(self):
        super(LemonadeRatingModel, self).__init__()
        # 创建一个线性层:接受3个输入特征(薄荷叶、黄瓜片、玫瑰盐)并输出1个值(顾客评分)
        self.rating_predictor_layer = nn.Linear(in_features=3, out_features=1, bias=False)

    def forward(self, input_features):
        # 前向计算,定义数据在网络中的流动方式,预测顾客评分,forward 方法会被自动调用
        predicted_rating = self.rating_predictor_layer(input_features)
        return predicted_rating

自定义模型时, 需要重写模型的__init__forward方法,分别完成模型的初始化和前向计算。

  • __init__():定义模型的参数和层结构,创建一个拥有 3 个输入特征,1 个输出特征的线性层(Linear)。
  • forward():定义数据在网络中的流动方式,通过前向计算根据输入特征预测顾客评分。

线性层

在 PyTorch 中,线性层(Linear Layer)是最基础、最常用的神经网络层之一,由于它的每一个神经元都接收来自上一层所有神经元的输入信号,并与每个输入都有一个独立的权重进行连接,所以也称它为“全连接层”。

在PyTorch中,线性层由 torch.nn.Linear 类实现。它的主要参数有:

  • in_features:输入特征的数量(即输入的维度)
  • out_features:输出特征的数量(即输出的维度)
  • bias:布尔值,表示是否使用偏置项,默认为True

当创建一个 Linear 实例时,它会自动初始化权重和偏置。

柠檬水评分预测模型的结构详情

创建一个模型实例,并打印模型结构,确认模型创建成功。

from dsxllm.util import print_red

# 创建模型实例
model = LemonadeRatingModel()

print_red("模型结构:")
print(model)

模型结构:
LemonadeRatingModel(
  (rating_predictor_layer): Linear(in_features=3, out_features=1, bias=False)
)

从运行结果可以看到,柠檬水评分模型中只有一个预测评分的线性层 rating_predictor_layer,它有 3 个输入(薄荷叶、黄瓜片、玫瑰盐)、1 个输出(顾客评分)。

模型训练

神经网络的训练

既然生物神经网络能产生智能,那么仿照其结构构建的人工神经网络也应具备某种“学习能力”。神经网络的“学习”是指从训练数据中自动获取最优权重参数的过程。神经网络学习思路极其简单:计算神经网络得出的预测值与正解的误差,根据误差更新神经网络的权重,最终使得模型的误差总和达到最小。

使用 PyTorch 训练神经网络模型的流程如下:

训练神经网络模型和机器学习的流程基本一致,但也稍有不同:

  1. 初始化模型:确定模型结构,并对模型参数进行随机初始化。
  2. 前向传播:通过 forward() 将输入数据输入模型,得到预测结果。
  3. 损失计算:使用损失函数 loss_fn() 计算模型预测结果与真实结果之间的误差,评估模型性能。
  4. 梯度计算:由于神经网络的参数成千上万,无法再手动计算每一个参数的更新方式,利用反向传播算法 backward() 自动计算损失对各参数的梯度,指导参数更新方向。
  5. 参数更新:同样由于参数规模的暴增,手动更新参数也不太可能。另外学习率调度也非常复杂,需要借助优化器 optimizer 更新模型参数。
  6. 重复执行 2~5 步,通过持续训练逐步优化模型,最终得到使损失最小化的模型参数。

每完成一次在数据集上的训练循环,我们称之为一个轮次,也就是 epoch。下面我们定义一个训练方法开始训练顾客评分模型。

定义训练方法

PyTorch 经典的训练循环如图所示:

下面定义训练神经网络模型的方法 train_model,指定训练轮次并在每一轮中执行完整的训练循环。同时,需定期记录训练过程中损失值,以便追踪模型训练的进度。

def train_model(model, train_features, train_targets, loss_fn, optimizer, num_epochs=100, log_interval=5):
    """
    训练模型并记录训练日志

        参数:
    model: 要训练的模型
    train_features: 训练特征数据张量
    train_targets: 训练标签数据张量
    loss_fn: 损失函数
    optimizer: 优化器
    num_epochs: 训练轮数,默认为100
    log_interval: 日志记录间隔,默认为5

        返回:
    training_logs: 包含训练过程记录的列表
    """

    training_logs = []

    for epoch in range(num_epochs):
        # 1. 前向传播:使用模型预测评分
        predictions = model(train_features)

        # 2. 计算损失:使用损失函数计算预测评分与真实分数的差距
        loss = loss_fn(predictions, train_targets)

        # 3. 清空梯度:将模型参数的梯度清空,避免梯度累积
        optimizer.zero_grad()

                # 4. 反向传播,计算梯度
        loss.backward()

        # 5. 使用优化器,更新参数
        optimizer.step()

        # 记录训练日志
        if (epoch + 1) % log_interval == 0:
            # 记录模型参数
            with torch.no_grad():
                for param_name, param in model.named_parameters():
                    if param.requires_grad:  # 筛选需要计算计算梯度进行更新的参数
                        weights_list = param.data.numpy()[0].tolist()

                        # 获取模型权重
                        mint_leaf_weight, cucumber_slice_weight, rose_salt_weight = weights_list[0], weights_list[1], weights_list[2]

                        # 记录训练日志
                        training_logs.append({
                            "Epoch": epoch + 1,
                            "Loss": loss.item(),
                            "薄荷叶权重": mint_leaf_weight,
                            "黄瓜片权重": cucumber_slice_weight,
                            "玫瑰盐权重": rose_salt_weight
                        })

    return training_logs

在神经网络模型的训练循环中,每个训练循环通常包含以下核心操作:

  • 前向传播:通过 model() 自动执行前向传播计算,模型会基于输入特征(如薄荷叶、黄瓜片、玫瑰盐的用量)计算预测输出(例如顾客评分)。
  • 损失计算:调用 loss_fn() 利用损失函数量化模型预测值与真实标签之间的差异,反映当前预测的误差大小。
  • 清空历史梯度:执行 optimizer.zero_grad() 将优化器中之前累积的梯度清零,以避免梯度累加影响本轮参数更新。
  • 反向传播:通过 loss.backward() 系统会自动计算损失对模型各参数的梯度,从而确定参数调整的方向与幅度。
  • 参数更新:使用 optimizer.step() 优化器将根据计算得到的梯度更新模型参数,使模型预测逐步向真实值靠近。

这些步骤在训练过程中循环执行,直至训练结束。

训练模型

定义好了训练方法后,可以开始训练模型了。训练模型需要以下步骤:

  1. 超参数配置:定义训练轮数、日志记录间隔、学习率等超参数
  2. 准备训练数据:将训练的输入数据和标签数据转换为张量
  3. 初始化模型训练组件:创建模型实例、损失函数和优化器
    • 损失函数:因为此任务为回归任务,使用均方误差损失函数(MSE)
    • 优化器:使用随机梯度下降(SGD)优化器优化模型参数,学习率为 0.001
  4. 执行训练循环:调用 train_model 函数进行 500 轮训练,每 20 轮记录一次训练日志。

损失函数:均方误差(MSE)

MSE 是 均方误差 的缩写,英文全称是 Mean Squared Error。它是评估预测模型(如回归模型)精度最常用、最重要的指标之一。MSE 衡量的是模型预测值与真实值之间差异(即误差)的平方的平均值。

简单来说:

  • 先算出每一个预测值和对应的真实值差多少。
  • 把这个差值平方(目的是消除正负号的影响,并放大较大误差)。
  • 把所有样本的“平方误差”求平均。

你可以把 MSE 理解为“平均平方误差”。数值越小,说明模型的预测值(拟合值)与真实值越接近,模型越好。

优化器:随机梯度下降(SGD)

SGD 是 随机梯度下降 的缩写,英文全称是 Stochastic Gradient Descent。它是一种用于优化机器学习模型参数的核心算法,目的是找到使模型损失函数最小化的参数值。

我们打一个比方,可以帮助你更好的理解 SGD 的工作原理:

有一个性情古怪的探险家。他在广袤的干旱地带旅行,坚持寻找幽深的山谷。他的目标是要到达最深的谷底(他称之为“至深之地”)。这也是他旅行的目的。并且,他给自己制定了两个严格的“规定”:一个是不看地图;另一个是把眼睛蒙上。因此,他并不知道最深的谷底在这个广袤的大地的何处,而且什么也看不见。在这么严苛的条件下,这位探险家如何前往“至深之地”呢?他要如何迈步,才能迅速找到“至深之地”呢?

寻找最优参数时,我们所处的状况和这位探险家一样,是一个漆黑的世界。我们必须在没有地图、不能睁眼的情况下,在广袤、复杂的地形中寻找“至深之地”。大家可以想象这是一个多么难的问题。

在这么困难的状况下,地面的坡度显得尤为重要。探险家虽然看不到周围的情况,但是能够知道当前所在位置的坡度(通过脚底感受地面的倾斜状况)。于是,朝着当前所在位置的坡度最大的方向前进,就是SGD的策略。勇敢的探险家心里可能想着只要重复这一策略,总有一天可以到达“至深之地”。

from dsxllm.util import to_dataframe

# 1️⃣ 超参数配置
num_epochs=500
log_interval=20
learning_rate = 0.001

# 2️⃣ 准备训练数据:将原始特征和目标值数组转换为 PyTorch 张量格式,指定数据类型为float32
train_features_tensor = torch.tensor(train_features, dtype=torch.float32)
train_targets_tensor = torch.tensor(train_targets, dtype=torch.float32)

# 3️⃣ 初始化模型训练组件:创建模型实例、损失函数和优化器
model = LemonadeRatingModel()  # 创建模型实例
loss_fn = nn.MSELoss()  # 使用均方误差损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)  # 使用随机梯度下降优化器

# 4️⃣ 执行训练循环:调用训练方法进行500轮训练
training_logs = train_model(model, train_features_tensor, train_targets_tensor, loss_fn, optimizer, num_epochs=num_epochs, log_interval=log_interval)

# 5️⃣ 训练过程监控:以表格形式展示训练过程中的关键指标变化
print_red("模型训练日志:")
df = to_dataframe(training_logs)
display(df)

模型训练日志:
Epoch Loss 薄荷叶权重 黄瓜片权重 玫瑰盐权重
0 20 1.8945 0.1887 0.7240 0.0711
1 40 0.9545 0.2538 0.6208 0.1263
2 60 0.4852 0.2960 0.5431 0.1616
3 80 0.2466 0.3260 0.4877 0.1868
4 100 0.1254 0.3474 0.4482 0.2048
5 120 0.0637 0.3626 0.4200 0.2177
6 140 0.0324 0.3734 0.3999 0.2269
7 160 0.0165 0.3811 0.3856 0.2335
8 180 0.0084 0.3865 0.3754 0.2382
9 200 0.0043 0.3904 0.3681 0.2415
10 220 0.0022 0.3932 0.3629 0.2440
11 240 0.0011 0.3952 0.3592 0.2457
12 260 0.0006 0.3966 0.3566 0.2469
13 280 0.0003 0.3976 0.3547 0.2478
14 300 0.0001 0.3983 0.3533 0.2484
15 320 0.0001 0.3988 0.3524 0.2489
16 340 0.0000 0.3991 0.3517 0.2492
17 360 0.0000 0.3994 0.3512 0.2494
18 380 0.0000 0.3996 0.3509 0.2496
19 400 0.0000 0.3997 0.3506 0.2497
20 420 0.0000 0.3998 0.3504 0.2498
21 440 0.0000 0.3998 0.3503 0.2498
22 460 0.0000 0.3999 0.3502 0.2499
23 480 0.0000 0.3999 0.3502 0.2499
24 500 0.0000 0.3999 0.3501 0.2499

从训练日志中可以看出随着训练的进行,损失值不断下降,逐步降至接近 0,表示模型预测准确性不断提高。

训练过程可视化

绘制训练过程中损失值和重要权重的变化曲线,更直观地观察损失值和各参数权重在训练过程中的变化趋势。

from dsxllm.util import plot_training_curves

# 绘制训练日志图像
plot_training_curves(training_logs)

从训练的过程中我们可以看到,所有参数在约 200 轮后基本收敛,三个配料权重都趋于稳定,说明模型确实找到了它们的配比与顾客评分之间的关系。而且总体来看,变化的趋势很平滑,说明模型训练过程非常稳定。

模型评估

模型训练完成后,需要评估模型在未见过的新数据上的表现,即评估模型的泛化能力。具体步骤如下:

  1. 评估时,使用 torch.no_grad() 关闭梯度计算,可以在推理过程中不存储中间结果,减少显存占用
  2. 将评估数据转换为 Pytorch 张量
  3. 使用训练好的模型对新的柠檬水进行评分
  4. 计算预测评分和真实评分的偏差和精度
# 模型评估

# 1️⃣ 关闭梯度计算
with torch.no_grad():

    # 2️⃣ 将输入特征转换为张量格式
    val_features_tensor = torch.tensor(val_features, dtype=torch.float32)

    # 3️⃣ 使用训练好的模型对柠檬水进行评分
    predicted = model(val_features_tensor).numpy()

    # 4️⃣ 计算预测评分和真实评分的偏差和精度
    data = []
    for i in range(len(val_features)):
        residual = predicted[i][0] - val_targets[i][0]  # 预测的偏差
        accuracy_ratio = 1 - abs(residual) / val_targets[i][0]  # 预测的精度

        data.append({
            "薄荷叶": val_features[i][0],
            "黄瓜片": val_features[i][1],
            "玫瑰盐": val_features[i][2],
            "真实评分": val_targets[i][0],
            "模型预测": predicted[i][0],
            "模型偏差"f"{residual:.4f}",
            "模型精度"f"{accuracy_ratio * 100:.2f}%"
        })

    df = to_dataframe(data)
    display(df)

薄荷叶 黄瓜片 玫瑰盐 真实评分 模型预测 模型偏差 模型精度
0 1 8 2 3.7000 3.7007 0.0007 99.98%
1 1 2 3 1.8500 1.8500 0.0000 100.00%
2 9 8 3 7.1500 7.1502 0.0002 100.00%
3 1 10 9 6.1500 6.1506 0.0006 99.99%
4 1 3 9 3.7000 3.6998 -0.0002 99.99%
... ... ... ... ... ... ... ...
195 9 1 2 4.4500 4.4495 -0.0005 99.99%
196 10 4 6 6.9000 6.8995 -0.0005 99.99%
197 9 5 6 6.8500 6.8497 -0.0003 100.00%
198 4 7 3 4.8000 4.8004 0.0004 99.99%
199 4 2 9 4.5500 4.5495 -0.0005 99.99%

200 rows × 7 columns

从评估结果中可以看出,模型在预测柠檬水评分时,基本保持了预测的偏差在 ±0.01 的范围内,准确率也基本保持在 99.99% 以上。说明我们的模型成功学习到了薄荷叶、黄瓜片、玫瑰盐与顾客评分之间的关系,并且在验证集上的表现良好,说明训练出的模型具有较好的泛化能力,完美的完成了本次的任务。

为什么模型总是训不好?

虽然我们已经完美的完成了本次任务,但是很多同学在刚开始手动训练模型时,常常会遇到训练失败的情况。在前面的训练过程中,我们可以看到和模型训练相关的因素主要有两个:训练数据量学习率

下面我们设计3个实验,对比不同数据量和学习率对训练结果的影响:

实验 实验设置
实验 1 样本数多,学习率低
实验 2 样本数多,学习率高
实验 3 样本数少,学习率高

实验1:样本数多,学习率低

  • 训练轮数:100
  • 样本数:800
  • 学习率:0.001
# 1️⃣ 超参数配置
num_epochs = 100
samples_num = 800
lr = 0.001

# 2️⃣ 将数组转换为 PyTorch 张量
train_features_tensor = torch.tensor(train_features[:samples_num], dtype=torch.float32)
train_targets_tensor = torch.tensor(train_targets[:samples_num], dtype=torch.float32)

# 3️⃣ 定义损失函数和优化器
model = LemonadeRatingModel()  # 创建模型实例
loss_fn = nn.MSELoss()  # 使用均方误差损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=lr)  # 使用随机梯度下降优化器

# 4️⃣ 调用训练方法
training_logs = train_model(model, train_features_tensor, train_targets_tensor, loss_fn, optimizer, num_epochs=num_epochs, log_interval=1)

# 5️⃣ 可视化训练过程
plot_training_curves(training_logs)

实验1 中,我们可以看到,损失下降速度较慢但稳定,训练过程平滑,没有有剧烈波动。

实验2:样本数多,学习率高

  • 训练轮数:100
  • 样本数:800
  • 学习率:0.009
# 1️⃣ 超参数配置
num_epochs = 100
samples_num = 800
lr = 0.009

# 2️⃣ 将数组转换为 PyTorch 张量
train_features_tensor = torch.tensor(train_features[:samples_num], dtype=torch.float32)
train_targets_tensor = torch.tensor(train_targets[:samples_num], dtype=torch.float32)

# 3️⃣ 定义损失函数和优化器
model = LemonadeRatingModel()  # 创建模型实例
loss_fn = nn.MSELoss()  # 使用均方误差损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=lr)  # 使用随机梯度下降优化器

# 4️⃣ 调用训练方法
training_logs = train_model(model, train_features_tensor, train_targets_tensor, loss_fn, optimizer, num_epochs=num_epochs, log_interval=1)

# 5️⃣ 可视化训练过程
plot_training_curves(training_logs)

实验2 中,我们可以看到,当学习率增大时,损失下降速度较快,在第 20 轮权重的值就接近最优值了。虽然增大学习率能加快模型的训练速度,但训练的过程存在剧烈波动,训练过程不平滑,有较大的风险训练失败。

实验3:样本数少,学习率高

  • 训练轮数:100
  • 样本数:5
  • 学习率:0.009
# 1️⃣ 超参数配置
num_epochs = 100
samples_num = 5
lr = 0.009

# 2️⃣ 将数组转换为 PyTorch 张量
train_features_tensor = torch.tensor(train_features[:samples_num], dtype=torch.float32)
train_targets_tensor = torch.tensor(train_targets[:samples_num], dtype=torch.float32)

# 3️⃣ 定义损失函数和优化器
model = LemonadeRatingModel()  # 创建模型实例
loss_fn = nn.MSELoss()  # 使用均方误差损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=lr)  # 使用随机梯度下降优化器

# 4️⃣ 调用训练方法
training_logs = train_model(model, train_features_tensor, train_targets_tensor, loss_fn, optimizer, num_epochs=num_epochs, log_interval=1)

# 5️⃣ 可视化训练过程
plot_training_curves(training_logs)

从结果中我们可以看到 实验3 训练彻底失败。和 实验2 相比,它只训练了 5 个样本,训练数据过少导致训练失败。这说明:“数据就是智能的燃料”,就像一个火箭就算有再强的引擎,如果没有足够的燃料也无法到达期望的目的地。

这是本实验的关键教训:

  • 当数据量不足时,模型无法学习到真正的规律。
  • 越是复杂的模型训练所需的数据量越大。

实验的启示

通过这三组对比实验,我们系统分析了不同样本量与学习率的组合对模型训练的影响,具体实验设置及结果如下:

  • 实验1:采用多样本量、低学习率设置 → 训练过程稳定,但收敛速度缓慢;
  • 实验2:采用多样本量、高学习率设置 → 训练收敛速度加快,但过程波动剧烈;
  • 实验3:采用少样本量、高学习率设置 → 训练直接失败。

上述三组实验结果为我们提供了一些关键启示:

  • 首先,学习率对训练效果存在双重影响,需精准平衡。
    • 过小的学习率会显著延长训练周期,会降低训练效率,浪费大量的时间和算力成本;
    • 提升学习率虽能有效加速训练收敛,但会破坏训练稳定性,甚至可能导致训练失效。

因此,好的学习率调度策略对模型的训练至关重要。在后续项目中,我们会采用更先进的优化器动态调度学习率,让模型在训练过程又快又好。

  • 其次,数据对模型训练效果具有根本性决定作用。 数据量不足或质量欠佳,会直接导致训练失败。不少 AI 项目失败的原因,都在于对高质量数据采集的重视不足,反而过度将资源倾斜于模型开发与改进。事实上,数据采集的优先级应高于模型本身,其质量与数量直接决定项目成败,是 AI 项目实施过程中的至关重要的环节。

本章小结

本章使用 PyTorch 训练了我们的第一个神经网络模型。我们掌握了如何使用 PyTorch 训练一个多参数的回归模型,并理解了神经网络模型的构建、训练过程以及损失函数和优化器的作用。这为理解深度学习框架的工作机制奠定了坚实基础。但是,在训练过程中,我们的数据还是比较低效的手动管理,在下一个章节中我们将学习如何使用更深的网络、更高效的数据管理方式处理更复杂的多分类问题。

答疑讨论

如果你有任何学习上的疑问,可以评论留言,和我一起讨论。我会抽空回复你的问题,也欢迎你回答其他人的问题。

参与答疑讨论