深度学习入门笔记(一)

从 Tensor 到逻辑回归:云原生开发的 PyTorch 深度学习入门笔记(小白版)

最近正在做云原生相关的项目,为了更好的跟AI推理打交道,所以便自学了深度学习。

本篇文章是基于我的学习深度学习时,对一些相关问题的思考,整理而来的。

所以本博客不追求 手推反向传播,也不追求从零造框架,也不会 真让你一行行手搓偏导、矩阵、loss、梯度下降等计算。

而是为了让你,在吃透本篇博客后,

不仅能 更好服务你的工作,

如:看懂模型的训练日志,理解推理服务为什么吃 GPU,知道 Tensor 的 shapedtypedevice 出错时该往哪里查;

也能让你和算法同事把接口、模型输入、loss 曲线和评估指标说清楚;

本篇之后会重点说的:

  • PyTorch 官方文档把 torch.Tensor 定义为单一数据类型元素组成的多维矩阵,俗称张量,这是理解模型输入、权重、梯度和 GPU 迁移的起点。
  • 深度学习训练可以先记成六步:数据进入模型,模型输出预测,loss 衡量差距,backward() 求梯度,optimizer 更新参数,再重复很多轮。
  • 云原生工程师不用手推优化器,但要能看懂 Tensor、Autograd、DataLoader、loss 曲线、二分类指标,以及推理阶段为什么要用 torch.no_grad()
    面试复习用法

本片文章只是用来回顾细节的,适合第一次看。

如果已经看本篇,只是想要复习的话,可以看另一篇。

为什么AI侧云原生开发者也要懂深度学习?

云原生工程师学深度学习,目标不是和算法工程师抢模型结构设计,而是补齐 AI 工程链路里的共同语言。

PyTorch 官方文档把 torch 包描述为包含多维 Tensor 数据结构和这些 Tensor 上数学运算的包,并提供 CUDA 对应能力来运行 NVIDIA GPU 计算。

从工程角度看,你会遇到这些问题:

  • 模型服务启动后,为什么输入必须是 [batch, feature],不能只传 [feature]
  • 为什么同样的代码,训练时显存爆了,推理时加上 torch.no_grad() 就能降下来?
  • 为什么 DataLoader 要管 batch_sizeshuffle
  • 为什么训练集 loss 很低,验证集 loss 很高,反而说明模型不能直接上线?

这些问题不要求你会发明神经网络,但要求你知道训练和推理在做什么。你越懂这个边界,越容易排查 AI 服务里的真实故障。

深度学习不是云原生岗位的主战场,但 Tensor、设备迁移、batch、loss、评估指标和推理模式,是 AI Infra / MLOps 工程师必须能沟通的基础。

什么是深度学习?

深度学习 是一种用多层神经网络从数据里学习规律的机器学习方法。《Deep Learning》把它描述为让计算机从经验中学习,并通过概念层级理解世界。

这句话听起来抽象,拆开就好懂了:

  1. 从经验中学习:经验就是训练数据。
  2. 概念层级:浅层学简单特征,深层组合成复杂特征。
  3. 多层结构:神经网络一层接一层,所以叫"深"。
  4. 参数调整:模型通过 loss 和梯度,不断调整权重。

传统机器学习 常常需要人先设计特征,比如手工提取用户年龄、点击次数、地区、活跃天数。

而深度学习更强调让网络自己从数据中学表示。图片模型可以从边缘学到纹理,再学到局部形状,最后识别物体。文本模型可以从 token 表示学到句法、语义和上下文关系。

那大模型微调算不算深度学习?算,但它不是"从零开始学世界"。微调通常是在预训练模型已有能力上,用更小的数据和更低的学习率改变模型行为,例如输出格式、任务风格或领域回答方式。梯度依然是发动机,但起点和目标都不同。

常见误区

"学深度学习"不等于 "立刻学大模型训练"。

对工程入门来说,先把 Tensor、loss、梯度、线性回归、逻辑回归和 DataLoader 等基础知识串起来,比一上来读 Transformer 更稳。

并且,深度学习 可以理解成用多层神经网络从数据里学习表示。它不是 靠人手工写死规则,而是通过 loss 衡量预测和真实答案的差距,再用梯度不断调整参数

如何学习深度学习?

对于开发者来说,最顺的路线不是从复杂模型开始,而是从 PyTorch 最小闭环开始。

因为 PyTorch 的入门资料和 API 文档都围绕 Tensor、Autograd、nn.Module、loss、optimizer、Dataset/DataLoader 这些组件展开。

所以本项目的顺序如下:

  1. Tensor :先会看 shapedtypedevice
  2. 线性回归 :理解模型参数 wb 如何被训练出来。
  3. loss 和梯度:知道 loss 是误差标尺,梯度是参数调整方向。
  4. Autograd:知道 PyTorch 如何自动记录计算图并求梯度。
  5. 逻辑回归:从回归过渡到二分类。
  6. Dataset/DataLoader:理解训练数据如何批量喂给模型。
  7. 过拟合和指标 :知道模型是否真的能泛化。

图 1

深度学习训练闭环:数据前向流动,loss 产生误差信号,backward() 计算梯度,optimizer.step() 更新参数/修改权重。

PyTorch 是什么?为什么代码里写 import torch?

PyTorch 是一个深度学习框架,代码中 import torch 导入的是它的核心 Python 包。

PyTorch 官方文档说,torch 包包含多维 Tensor 的数据结构,并定义了这些 Tensor 上的数学运算。

可以这样记:

  • PyTorch:框架名字。
  • torch:代码里导入的核心包。
  • Tensor :PyTorch 里的核心数据容器,有多种用途,比如可以用来存放输入、标签、权重、梯度和模型输出。
  • Autograd:自动求梯度系统。
  • torch.nn:神经网络层、损失函数等模块。
  • torch.optim:SGD、Adam 等优化器。

PyTorch 之所以对工程师友好,是因为它把训练中的几件麻烦事封装好了:

如:

python 复制代码
import torch
from torch import nn

# 创建输入数据 Tensor,形状是 [3, 1],表示 3 条样本,每条 1 个特征
x = torch.tensor([[1.0], [2.0], [3.0]])  

# 定义一个线性层:输入 1 个特征,输出 1 个预测值,本质是 y = wx + b
model = nn.Linear(1, 1)  

y_pred = model(x)  # 把输入 x 喂给模型,得到预测结果 y_pred

这几行代码背后已经包含 Tensor 创建、线性层参数、前向计算和输出 Tensor。

你不用手动管理每个权重矩阵的乘法,也不用自己写求导器。

一句话总结

PyTorch 是深度学习框架,torch 是代码里的核心包。它给我三个最重要的能力:Tensor 表示数据,Autograd 自动求梯度,nnoptim 帮我定义模型和更新参数。

Tensor:深度学习里的数字容器

Tensor 是 PyTorch 里的核心数字容器。

PyTorch 官方文档把 torch.Tensor 定义为"包含单一数据类型元素的多维矩阵 "。

这句话很关键:Tensor 既有维度,也有数据类型,还可能在 CPU 或 GPU 上。

所以可把 Tensor 想成"带设备属性的多维数组"。普通 Python list 只是数据,Tensor 除了保存数据,还知道:

python 复制代码
import torch

t = torch.tensor([[1.0, 2.0, 3.0]])

print(t.shape)   # torch.Size([1, 3])
print(t.dtype)   # torch.float32
print(t.device)  # 使用的设施:cpu

shape、dtype、device 是排错三件套

模型报错时,先看这三个属性:

  • shape :形状,比如 [1, 3] 表示 1 条样本、3 个特征。
  • dtype :数据类型,比如 float32int64
  • device :数据在哪,比如 cpucuda:0

云原生推理服务里,经常是 Go 网关传 JSON 数组,Python 服务转 Tensor,再送进模型。

只要 shape、dtype、device 有一个不匹配,就可能出错。

unsqueeze 和 squeeze 到底在干什么?

unsqueeze(dim) 是在指定位置加一个长度为 1 的维度。squeeze(dim) 是删除指定位置长度为 1 的维度。

python 复制代码
import torch

x = torch.tensor([1.0, 2.0, 3.0])
print(x.shape)  # torch.Size([3])

batch_x = x.unsqueeze(0)
print(batch_x.shape)  # torch.Size([1, 3])

back = batch_x.squeeze(0)
print(back.shape)  # torch.Size([3])

为什么要加这个维度?因为模型通常按 batch 处理输入。单条样本 [3] 只是 3 个特征,加上 batch 维度后才是 [1, 3],意思是"1 条样本,每条 3 个特征"。

图 2

unsqueeze(0) 给单条样本补 batch 维度,squeeze(0) 再把这个长度为 1 的维度去掉。

广播机制是自动补形状

PyTorch 的广播规则来自 NumPy 风格语义。

官方广播说明中,两个 Tensor 如果从尾部维度开始比较时,每个维度相等、其中一个为 1,或其中一个维度不存在,就可以广播。

例子:

python 复制代码
grades = torch.tensor([
    [80.0, 90.0, 70.0],
    [60.0, 75.0, 85.0],
])
weights = torch.tensor([0.3, 0.4, 0.3])

weighted = grades * weights
print(weighted.shape)  # torch.Size([2, 3])

grades[2, 3]weights[3]。PyTorch 会把 [3] 逻辑上看成 [1, 3],再扩展成 [2, 3] 参与逐元素计算。

常见误区

squeeze() 不写维度会删除所有长度为 1 的维度。推理服务里要谨慎用,因为你可能不小心把 batch 维度也删掉。
细节

Tensor 不只是数组,它还带着 shape、dtype 和 device。线上排查模型输入问题时,我会先看这三个属性,因为很多错误本质是维度、类型或 CPU/GPU 设备不一致。

计算图与自动求梯度:知道原理,不追底层

PyTorch 的 Autograd 是反向自动微分系统。

官方文档说明,前向计算时 Autograd 会同时执行运算并构建一张表示梯度函数的图;

沿着这张图从输出回到输入,就能用链式法则自动计算梯度。

不用被"计算图"吓到。你可以这样理解:

python 复制代码
import torch

w = torch.tensor([2.0], requires_grad=True)
loss = w ** 2
loss.backward()

print(w.grad)  # tensor([4.])

这里的过程是:

  1. w 开启 requires_grad=True,PyTorch 开始追踪它。
  2. loss = w ** 2 产生计算关系。
  3. loss.backward() 从 loss 往回算梯度。
  4. w.grad 得到 d(loss)/dw,也就是 4。

我的经验是,很多推理服务显存问题不是模型真的太大,而是代码还在按训练模式保存梯度。

训练阶段需要梯度,所以会保留计算图。推理阶段只做预测,不需要求导,所以应该用:

python 复制代码
with torch.no_grad():
    y_pred = model(x)

这能减少不必要的梯度记录,也能降低显存占用。对模型服务来说,这是很实用的工程习惯。

记忆点

Autograd 不是"魔法求导"。它是在前向计算时记录操作关系,形成计算图;backward() 再沿图反向应用链式法则,得到每个参数的梯度。
所以说

我会把 Autograd 解释成"自动记录账本"。前向计算时 PyTorch 记住每一步怎么算出来,到了 loss.backward(),它就沿着这条记录反向算每个参数该承担多少误差。

用 PyTorch 实现线性回归

PyTorch 的 nn.Linear 会对输入做仿射线性变换,官方文档写成 y = xA^T + b

线性回归正好可以用它做最小例子:输入 x,学习一条直线,让预测接近 y = 2x + 3

下面代码可以直接保存成 linear_regression_demo.py 运行:

python 复制代码
import torch
from torch import nn

# 1. 造一组简单数据:真实规律 y = 2x + 3
x = torch.tensor([[1.0], [2.0], [3.0], [4.0], [5.0]])
y = torch.tensor([[5.0], [7.0], [9.0], [11.0], [13.0]])

# 2. 定义模型:输入1个特征,输出1个数
model = nn.Linear(in_features=1, out_features=1)

# 3. 定义损失函数和优化器
loss_fn = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

loss_history = []

# 4. 训练
for epoch in range(1000):
    y_pred = model(x)
    loss = loss_fn(y_pred, y)
	
		# 以下是核心三步:
    optimizer.zero_grad() # 先清掉旧的
    loss.backward()
		# 根据这个错误,反过来计算每个参数该怎么改。
		# 它不负责真正修改参数。
		# 它只是把"修改提示"算出来,放到参数的 .grad 里面。

    optimizer.step() 
    # 它会读取刚才 backward() 算出来的 .grad,然后更新模型里的权重。

    loss_history.append(loss.item())

    if epoch % 100 == 0:
        print(f"epoch={epoch}, loss={loss.item():.6f}")

# 5. 查看学到的参数
w = model.weight.item()
b = model.bias.item()
print(f"learned: y = {w:.3f}x + {b:.3f}")

# 6. 推理
with torch.no_grad():
    x_new = torch.tensor([[6.0]])
    print("x=6 prediction:", model(x_new).item())

MSELoss 是均方误差,PyTorch 官方文档说它衡量输入和目标之间的 squared L2 norm。

这里的输入是预测值,目标是标签。

训练循环里最重要的是三行:

python 复制代码
optimizer.zero_grad()  # 清空上一轮梯度
loss.backward()        # 自动求导
optimizer.step()       # 更新参数

为什么先清梯度?因为 PyTorch 默认会累加梯度。如果不清空,上一轮的梯度会混进下一轮,训练方向就乱了。

图 3

线性回归的健康 loss 曲线通常是整体下降,后期逐渐变平。中间有轻微波动不一定是坏事。

什么是优美的 loss 曲线?

一条好的 loss 曲线不需要每一步都严格下降,但整体趋势应该下降,并在后期趋于平稳。

PyTorch 官方优化器文档中的典型训练模板也是:

清梯度、前向计算、算 loss、backward()optimizer.step(),然后重复。

你可以把 loss 曲线当成模型训练的心电图:

曲线形态 可能含义 工程处理
稳定下降后趋平 正常学习 继续观察验证集
大幅上下震荡 学习率可能太大,batch 太小,数据噪声大 降学习率,增大 batch,检查数据
一直不下降 模型没学到,特征或代码可能有问题 检查标签、loss、输入 shape
训练 loss 降,验证 loss 升 过拟合 加正则、早停、更多数据、简化模型
loss 变成 nan 数值爆炸或非法计算 降学习率,检查除零、归一化、梯度裁剪

图 4

欠拟合时训练和验证 loss 都降不下来;正常时两者一起下降;过拟合时训练 loss 继续下降,但验证 loss 开始回升。

"优美"不是为了好看,而是为了判断训练是否健康。曲线能告诉你模型有没有学、学得稳不稳、是否开始背训练集。

记忆点

只看训练 loss 不够。训练 loss 低只能说明模型记住训练数据,验证 loss 才能反映泛化能力。

Normalization:为什么归一化会影响训练?

Normalization 是把不同尺度的数据拉到相近范围。

它不是装饰步骤,而是会改变梯度下降的难度。因为深度学习训练依赖数值稳定的优化过程,而输入尺度会影响优化过程的形状。

举个直观例子。一个样本有两个特征:

  • 年龄:20 到 60。
  • 年收入:50,000 到 500,000。

如果不处理,收入这个特征的数字大很多,模型更新时很容易被它支配。梯度下降看到的 loss 地形会变得像狭长山谷:一个方向很陡,一个方向很平。学习率稍大就震荡,学习率太小又走得很慢。

常见归一化方法有两种:

方法 公式 结果 适合直觉
Min-Max (x - min) / (max - min) 压到 [0, 1] 把不同分值转成百分比位置
Z-score (x - mean) / std 均值约 0,标准差约 1 看一个值离平均水平多远

深度学习里还会遇到 Batch Normalization。它不是对原始输入做归一化,而是在网络中间层对一批数据的激活值做规范化。你入门阶段知道它用于稳定训练即可,不需要先追底层推导。

重点!如何说

归一化的核心是让不同特征处在相近尺度。这样梯度下降不会在某个尺度特别大的方向上来回震荡,训练会更稳定。线上推理时必须复用训练阶段的归一化参数。

逻辑回归:名字叫回归,本质做分类

逻辑回归 虽然叫 regression,但主要用于分类,尤其是二分类。

它先做线性计算,再用 Sigmoid 把原始分数压到 0 到 1 之间,解释成"属于正类的概率"。

这和 PyTorch 的二分类 loss 设计直接相关。

线性回归和逻辑回归的区别可以这样记:

模型 输出 任务
线性回归 任意连续数值 预测房价、温度、分数
逻辑回归 0 到 1 的概率 判断是否恶意、是否流失、是否异常

一元逻辑回归只有一个输入特征:

text 复制代码
score = w * x + b
probability = sigmoid(score)

多元逻辑回归有多个输入特征:

text 复制代码
score = w1*x1 + w2*x2 + ... + wn*xn + b
probability = sigmoid(score)

如果概率大于阈值,比如 0.5,就判为正类;否则判为负类。阈值不是天生固定的,业务里会根据误报和漏报成本调整。

重点!怎么说

逻辑回归可以看成最简单的二分类模型。它先用线性层算一个原始分数,再通过 Sigmoid 转成概率,最后根据阈值判断正类或负类。

逻辑回归的损失函数:二分类交叉熵

二分类交叉熵用来衡量预测概率和真实标签之间的差距。

PyTorch 官方文档说明,BCEWithLogitsLoss 把 Sigmoid 层和 BCELoss 合在一个类里,并利用 log-sum-exp 技巧获得更好的数值稳定性。

二分类交叉熵公式是:

text 复制代码
Loss = - [ y * log(p) + (1 - y) * log(1 - p) ]

这里:

  • y 是真实标签,只能是 0 或 1。
  • p 是模型预测正类的概率。
  • Loss 越小,说明预测越接近真实标签。

如果真实标签是 1,而模型预测 p=0.9,loss 会比较小。

模型猜得对,而且很有信心。

如果真实标签是 1,而模型预测 p=0.1,loss 会很大。

模型不但错了,还错得很自信。

实际写 PyTorch 代码时,推荐这样做:

python 复制代码
loss_fn = nn.BCEWithLogitsLoss()
logits = model(x)          # 原始分数,不手动 sigmoid
loss = loss_fn(logits, y)  # loss 内部处理 sigmoid + BCE

推理时再把 logits 转成概率:

python 复制代码
prob = torch.sigmoid(logits)
pred = (prob >= 0.5).float()

面试时怎么说

二分类训练里我更倾向用 BCEWithLogitsLoss,因为它把 Sigmoid 和二分类交叉熵合在一起,数值上更稳定。

训练时喂 logits,推理时再手动 sigmoid 得到概率。

数据集划分、欠拟合与过拟合

训练模型不能只看训练集效果。

常见做法是把数据拆成训练集、验证集和测试集。

训练集用于更新参数,验证集用于调模型和观察过拟合,测试集用于最后评估。

并且这三份数据通常写成 train / validation / testtrain 负责学习参数,validation 负责调参和早停,test 负责最后报告一次相对客观的效果。

你可以这样理解三份数据:

数据集 作用 类比
train 让模型学习 平时刷题
validation 调参数和早停 模拟考试
test 最终评估 正式考试

欠拟合是模型太弱,训练集都学不好。

表现是训练 loss 和验证 loss 都高。解决方向是换更合适的模型、加特征、训练更久,或降低过强的正则。

过拟合是模型把训练集背熟了,但没学到通用规律。

表现是训练 loss 很低,验证 loss 明显更高。解决方向是更多数据、数据增强、正则化、Dropout、早停、简化模型。

面试时怎么说

train 是给模型学习的,validation 是给我调参和判断过拟合的,test 是最后评估用的。不能边看测试集边调参,否则测试集就不再客观。

二分类模型怎么评价?

二分类模型不能只看 accuracy。

scikit-learn 的指标文档列出 accuracy、precision、recall、F1、ROC AUC 等分类指标;

其中一些指标特别适合二分类任务。

先看四个基本计数:

名称 含义 恶意账号例子
TP 预测恶意,实际恶意 抓对坏账号
FP 预测恶意,实际正常 误伤正常用户
TN 预测正常,实际正常 放过正常用户
FN 预测正常,实际恶意 漏掉坏账号

再看指标:

指标 公式直觉 适合看什么
Accuracy 全部预测里有多少对 类别比较均衡时的整体表现
Precision 判成正类的样本里有多少真是正类 控制误报
Recall 真实正类里有多少被抓到 控制漏报
F1 Precision 和 Recall 的折中 误报漏报都重要
ROC-AUC 不同阈值下的整体区分能力 比较模型排序能力

图 5

跟别人说起时,要优先把 Precision 和 Recall 讲清楚:Precision 控制误报,Recall 控制漏报。

为什么 accuracy 容易误导?

假设异常日志只有很少一部分,一个模型永远预测"正常",accuracy 可能看起来不低,但它没有抓到任何异常。

工程上要结合业务成本看指标。

风控 更怕漏掉欺诈时,Recall 更重要;

内容审核更怕误伤正常内容时,Precision 更重要。

记忆点

Precision 回答"我报警的里面有多少是真的";Recall 回答"真正的问题里我抓到了多少"。
重点!怎么说

如果业务怕误伤,我会更关注 Precision;如果业务怕漏掉问题,我会更关注 Recall。比如风控漏掉欺诈很危险,所以 Recall 往往更关键;内容审核误伤用户也很敏感,所以 Precision 也要看。

梯度下降算法有哪些改进?

PyTorch 的 torch.optim 包提供多种优化算法。

官方文档说,构造 optimizer 时要传入待优化参数,并指定学习率、权重衰减等选项。

最基础的是梯度下降:沿着梯度的反方向更新参数,让 loss 下降。

text 复制代码
new_param = old_param - learning_rate * gradient

常见变体:

方法 每次看多少数据 特点
Batch Gradient Descent 全部训练集 方向稳定,但大数据时很慢
SGD 1 条样本 更新快,但方向抖动大
Mini-batch GD 一小批样本 深度学习最常见折中

PyTorch 的 SGD 实现支持 momentum 参数。

Adam 是另一个常用优化器,PyTorch 文档说明它实现 Adam 算法,并维护一阶矩和二阶矩估计。

入门阶段可以把 Adam 理解成"会自适应调整不同参数步长"的优化器。

重点!怎么说

SGD 是按梯度方向更新参数,Mini-batch 是工程上最常见的折中。Momentum 给更新加惯性,减少抖动;Adam 会自适应调整不同参数的步长,通常更容易上手。

Dataset 和 DataLoader:训练数据怎么喂给模型?

PyTorch 教程明确说,Dataset 保存样本和对应标签,DataLoader 在 Dataset 外面包一层可迭代对象,方便访问样本。

教程还说明,训练时通常希望用 minibatch、每个 epoch 重新打乱数据,并用多进程加速数据获取。

先记住分工:

  • Dataset:定义"怎么拿到第 i 条数据"。
  • DataLoader:定义"怎么批量拿数据、是否打乱、是否并行加载"。

最小自定义 Dataset:

python 复制代码
import torch
from torch.utils.data import Dataset, DataLoader

class SimpleDataset(Dataset):
    def __init__(self):
        self.x = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
        self.y = torch.tensor([[0.0], [0.0], [1.0], [1.0]])

    def __len__(self):
        return len(self.x)

    def __getitem__(self, idx):
        return self.x[idx], self.y[idx]

dataset = SimpleDataset()
loader = DataLoader(dataset, batch_size=2, shuffle=True)

for batch_x, batch_y in loader:
    print(batch_x.shape, batch_y.shape)

这段代码会每次返回一批样本。真实训练时,训练循环里遍历的就是 DataLoader。

代码关键行

__len__ 告诉 PyTorch 数据集有多长;__getitem__ 告诉 PyTorch 如何取一条样本;DataLoader(..., batch_size=..., shuffle=True) 决定如何组成 batch。
重点!怎么说

Dataset 关心"怎么取一条数据",DataLoader 关心"怎么把数据批量喂给模型"。工程上 DataLoader 还会影响吞吐,因为它控制 batch、shuffle 和并行加载。

用 PyTorch 定义逻辑回归

逻辑回归可以用一个 nn.Linear 表示。二分类训练时,用 BCEWithLogitsLoss 接收模型输出的 logits 和真实标签;推理时再用 torch.sigmoid 把 logits 转成概率。PyTorch, BCEWithLogitsLoss

下面是一份可运行的最小逻辑回归代码,不依赖 scikit-learn:

python 复制代码
import torch
from torch import nn
from torch.utils.data import TensorDataset, DataLoader

torch.manual_seed(42)

# 1. 构造模拟二分类数据
# 规则:x1 + x2 > 0 视为正类
n = 200
x = torch.randn(n, 2)
y = ((x[:, 0] + x[:, 1]) > 0).float().unsqueeze(1)

# 2. 划分训练集和测试集
train_size = int(0.8 * n)
x_train, x_test = x[:train_size], x[train_size:]
y_train, y_test = y[:train_size], y[train_size:]

train_ds = TensorDataset(x_train, y_train)
train_loader = DataLoader(train_ds, batch_size=16, shuffle=True)

# 3. 定义逻辑回归模型:本质是一层线性层
model = nn.Linear(in_features=2, out_features=1)
loss_fn = nn.BCEWithLogitsLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

# 4. 训练
for epoch in range(100):
    for batch_x, batch_y in train_loader:
        logits = model(batch_x)
        loss = loss_fn(logits, batch_y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    if epoch % 20 == 0:
        print(f"epoch={epoch}, loss={loss.item():.4f}")

# 5. 评估
with torch.no_grad():
    logits = model(x_test)
    prob = torch.sigmoid(logits)
    pred = (prob >= 0.5).float()

    tp = ((pred == 1) & (y_test == 1)).sum().item()
    fp = ((pred == 1) & (y_test == 0)).sum().item()
    tn = ((pred == 0) & (y_test == 0)).sum().item()
    fn = ((pred == 0) & (y_test == 1)).sum().item()

    accuracy = (tp + tn) / (tp + fp + tn + fn)
    precision = tp / (tp + fp + 1e-8)
    recall = tp / (tp + fn + 1e-8)
    f1 = 2 * precision * recall / (precision + recall + 1e-8)

print(f"TP={tp}, FP={fp}, TN={tn}, FN={fn}")
print(f"accuracy={accuracy:.3f}")
print(f"precision={precision:.3f}")
print(f"recall={recall:.3f}")
print(f"f1={f1:.3f}")

这份代码包含完整链路:

  1. Tensor 构造数据。
  2. 划分训练集和测试集。
  3. TensorDatasetDataLoader 批量取数据。
  4. nn.Linear 定义模型。
  5. BCEWithLogitsLoss 训练二分类。
  6. 用 Accuracy、Precision、Recall、F1 做评估。

从云原生视角看,这段逻辑回归代码比模型准确率本身更有价值。它把线上推理最容易出错的几件事都暴露出来:输入必须是二维 Tensor,标签 shape 要和 logits 对齐,训练时要保留梯度,推理时要关梯度,最终输出要从 Tensor 转成普通数值或 JSON。

重点卡片

逻辑回归训练链路:输入特征 Tensor -> nn.Linear 输出 logits -> BCEWithLogitsLoss 算 loss -> backward() 求梯度 -> optimizer 更新参数 -> 推理时 sigmoid(logits) 得到概率 -> 阈值转分类。

最后总结:你应该掌握到什么程度?

如果你的方向是云原生、AI Infra 或 MLOps,因为不需要成为算法专家。

所以更应该掌握模型工程中高频出现的基础对象:Tensor、shape、device、batch、loss、梯度、DataLoader、评估指标和推理模式。

最小学习清单如下:

必须掌握 为什么
Tensor 的 shape/dtype/device 线上模型输入错误最常见
unsqueeze/squeeze/permute/cat/stack 处理 batch 和维度
requires_grad/backward/no_grad 区分训练和推理
zero_grad/backward/step 看懂训练循环
loss 曲线 判断训练是否健康
train/validation/test 判断泛化能力
Precision/Recall/F1 看懂二分类模型是否可用
Dataset/DataLoader 理解数据如何进入模型

暂时不用深挖:

  • 手推反向传播公式。
  • 从零实现优化器。
  • 自研神经网络框架。
  • 分布式 Tensor 并行细节。

我曾经的疑惑

云原生工程师要学到会训练模型吗?

不一定。你至少要会读懂训练代码、loss 曲线、Tensor shape 和推理输入输出。真正的模型结构设计可以交给算法工程师,但模型部署、服务稳定性、GPU 利用率、batch 策略和排障链路,往往需要工程师理解这些基础。

线性回归和逻辑回归哪个更适合先学?

先学线性回归。线性回归能帮你理解参数、loss、梯度和 optimizer。再学逻辑回归时,你只是在这条线性计算后面加上分类概率和交叉熵损失,概念迁移更顺。

loss 降了就说明模型能上线吗?

不能。训练 loss 下降只说明模型在训练集上拟合得更好。上线前还要看验证集、测试集、二分类指标、业务阈值和失败样本。训练 loss 很低但验证 loss 很高,通常是过拟合信号。

相关推荐
小刘|1 小时前
ChatClient和ChatModel区别解析
人工智能
小飞侠在吗1 小时前
AI Agent Plan-and-Execute 技术范式详细文档总结
人工智能·agent·ai编程
东方佑1 小时前
碱基互补语言模型
人工智能·语言模型·自然语言处理
纽约恋情1 小时前
通俗易懂的解释Sparse Convolution稀疏卷积
深度学习·稀疏卷积
Java面试题总结1 小时前
Python 文件基本操作
大数据·人工智能·python
weixin_509138341 小时前
科技报道:从“概率生成”到“认知领航”
人工智能·agi·智能体·智能体认知
MartinYeung51 小时前
[论文学习]差分隐私在机器学习中的演进:从符号式AI到大型语言模型
人工智能·学习
AdCj31 小时前
GitHub 日榜速递 (2026-06-08):AI 基础设施正在“下沉“
人工智能·github·agent
实在智能RPA1 小时前
RPA-Agent的自主规划边界在哪里?——2026:从指令执行到目标驱动的技术跨越
大数据·人工智能·ai·rpa