资讯动态

PyTorch核心实战:从张量到自动求导,掌握深度学习训练全链路

发布时间:2026/10/16 15:22:58 来源:尧图企业网站定制
学 PyTorch别只学个 API。这篇文章我从头到尾走了一遍 PyTorch 的核心链路张量、自动求导、数据集加载、训练循环、模型保存再到实际踩坑记录。没有空泛的理论全是可以直接照着敲的实操内容附带我实际项目中沉淀下来的经验教训。1. 项目概述PyTorch 到底是什么很多人听到 PyTorch 这个词第一反应是一个深度学习框架再深入一点可能知道它是搞神经网络、训练模型的。但 PyTorch 真正解决的核心问题是什么是你需要一种工具能把你的想法快速变成可训练的模型同时这个过程要足够灵活能让你随时调整结构、打印中间结果、改算法逻辑而不是被框架本身的抽象规则锁死。我当初第一次接触 PyTorch 的时候最直接的感受就是这玩意写起来像 NumPy。实际上 PyTorch 里的核心数据结构 Tensor 确实和 NumPy 的 ndarray 长得非常像但它多了一个关键的能力自动求导。这一点才是它和普通数值计算库的本质区别。你定义了一系列张量运算PyTorch 会在背后记录整个计算图调用backward()之后自动把梯度算出来。这就意味着你写反向传播算法的时候根本不用手推链式法则也不用自己实现梯度下降更新逻辑框架帮你全干完了。这篇内容适合谁看如果你刚入门深度学习想在具体实践中搞懂 PyTorch 的运作机制而且不想只看文档硬啃这篇文章就是给你准备的。如果你已经跑通了一些小项目但遇到显存溢出、训练不收敛、数据加载慢这类问题后面专门有一章排查实录你大概率能对号入座。我理解很多人学 PyTorch 的时候会陷入一个怪圈教程看了很多代码也抄了不少但换个任务、换个模型就不会了。归根结底是因为只学了怎么调 API没搞懂背后的计算流转逻辑。所以这篇文章不止给代码还会把每个关键环节的为什么讲透让你真正获得举一反三的能力。2. 核心设计拆解动态计算图和自动求导2.1 为什么 PyTorch 选择动态计算图深度学习的框架大致分两类动态图和静态图。PyTorch 走的是动态图路线这个决策深刻影响了它的使用体验。静态图的思路是你先把整个网络结构定义清楚然后把数据喂进去框架基于静态结构做优化。听起来很高效但问题是你在设计实验的时候经常要根据中间结果动态改变网络结构比如打印某个中间层的输出做检查或者根据条件分支执行不同的子网络这在静态图框架里做起来非常别扭。动态图的思路则简单得多边运行边建图。每一步执行 Tensor 运算的时候PyTorch 自动把运算信息记录到一张图上而且这张图每轮迭代都会重新构建。你可以把动态计算图理解成一个现场直播你写一句代码框架立刻记录一句你决定在哪一步停下来看结果就直接打印不需要管框架的编译流程。这个特性带来的直接优势就是调试体验极好。你在写 PyTorch 代码的时候完全可以像写普通 Python 脚本一样在任何位置插入print()或者断点你看到的就是此刻真实的数据。我在实际项目里经常需要检查输入的维度对不对、梯度有没有正常传播、某一层的权重是否出现 NaN这些在动态图框架下都是几秒钟的事。换做静态图框架通常得借助专门的可视化工具甚至要重新编译整个计算图才能定位问题。2.2 自动求导机制的执行流水线PyTorch 的自动求导核心是torch.autograd这个包。当你创建一个 Tensor 并设置requires_gradTrue的时候之后所有基于这个张量的运算都会被跟踪。每个张量在计算图里都是一个节点节点上挂着它的数据值、梯度值和grad_fn——也就是它是通过什么运算得到的。整个流程是这样的你从前到后执行神经网络的前向传播每一步操作都会产生新的张量同时也会记录一个grad_fn这个函数保存着参与运算的输入张量和当前的运算类型。当你调用最终损失张量的backward()方法时PyTorch 从损失的节点开始沿着grad_fn的链路反向遍历通过链式法则最算出每个张量的梯度然后存储到对应张量的.grad属性里。注意一个容易被忽略的点默认情况下张量的.grad是累加模式的。也就是说如果你在循环里没有清空梯度两次backward()的梯度会叠加在一起。这就是为什么训练循环里每轮都要调用optimizer.zero_grad()清空梯度否则梯度一直累计参数更新就会乱套。实操中还有人踩过更隐蔽的坑不小心在推理阶段创建了计算图导致显存持续攀升。所以要记得用torch.no_grad()包裹推理逻辑这样计算图不会被建立省显存也更高效。2.3 张量操作PyTorch 和 NumPy 的协同既然张量长得那么像 NumPy 数组两者必然可以互转。PyTorch 提供了torch.from_numpy()和.numpy()两个入口。但要小心一个细节当你用torch.from_numpy()从一个 NumPy 数组创建张量时两者共享底层内存改了一边另一边跟着变这不算 bug设计如此目的是减少不必要的内存拷贝。如果你不希望共享内存就用torch.tensor(ndarray)显式创建一个新张量。实际操作中图片类数据特别适合用这种互转方式。比如你用 OpenCV 或 PIL 读取的图片在 NumPy 数组里是 HWC 格式高度、宽度、通道而 PyTorch 卷积神经网络默认输入是 CHW 格式通道、高度、宽度就需要用.permute()做维度交换。这种维度细节掌握不好新手容易在训练时报维度不匹配的错误错误信息又含含糊糊排查起来很头疼。我的习惯是在数据加载的第一个环节就把格式统一成模型需要的格式后面所有环节都不再纠结维度问题。3. 动手实操从搭环境到训练第一个模型3.1 环境准备和安装避坑PyTorch 的安装说简单也简单说复杂也复杂关键在于版本匹配。简单用 CPU 版本的话pip install torch一条命令就能搞定。但如果你有 NVIDIA 显卡想用 CUDA 加速就得考虑 PyTorch 版本、CUDA 版本和显卡驱动版本三者之间的兼容关系。我比较推荐用 Anaconda 管理环境理由是隔离性。必要时可以创建独立环境避免和你的其他项目互相污染。创建环境的命令是conda create -n torch_env python3.10然后激活环境再安装 PyTorch。Python 版本不能太新也不能太旧推荐 3.9 或 3.10因为 PyTorch 官方对这两个版本的预编译包支持得最全。安装 GPU 版本之前在终端里执行nvidia-smi查看你的驱动支持的 CUDA 版本这个信息很重要。然后在 PyTorch 官网找到对应的安装命令一般情况下推荐安装带 CUDA 支持的包例如pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118这类形式。版本号里的 cu118 代表兼容 CUDA 11.8需要你的显卡驱动支持。装完之后不要急着写代码先跑一个简单的验证脚本import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果这三行分别输出版本号、True 和你的显卡型号说明环境基本通了。如果torch.cuda.is_available()返回 False大概率是安装的 PyTorch 是 CPU 版或者 CUDA 版本不匹配。这里要提醒一下初次安装别贪图最新版本挑一个稳定版长期支持版本能省很多不必要的折腾。3.2 Dataset 和 DataLoader数据流水线设计做深度学习项目数据读取是第一个正式环节。PyTorch 提供了torch.utils.data.Dataset和DataLoader两个核心抽象把数据准备和模型训练解耦。Dataset的核心任务是按索引返回一条样本。你需要继承它并实现__len__和__getitem__两个方法。__len__返回数据集大小__getitem__根据索引 idx 返回该样本的输入数据和标签。这里有个设计建议尽量在__getitem__里做预处理因为DataLoader会并行地调用多个子进程去读取数据预处理放在这里可以充分利用多核 CPU 的并行能力。DataLoader则是包装器它接收一个Dataset实例负责把单条样本拼成 batch并且提供打乱顺序、多进程加载、内存锁页等能力。我在实际工程里最常用的参数是这样的from torch.utils.data import Dataset, DataLoader class MyDataset(Dataset): def __init__(self, data, labels): self.data data self.labels labels def __len__(self): return len(self.data) def __getitem__(self, idx): x self.data[idx] y self.labels[idx] return x, y dataset MyDataset(train_data, train_labels) dataloader DataLoader( dataset, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue )在实际代码里参数怎么选很有讲究。batch_size在显存允许的情况下尽量大一点训练会更稳定但太大也会导致收敛变慢。shuffle参数在训练集要设为 True因为打乱数据能减少批次间的相关性提升收敛效果但在验证集上通常设 False确保每一次验证都能完整体验数据集。num_workers表示并行读取数据的进程数我之前在 Windows 上踩过坑如果设得过大多进程加载反而会因为频繁的进程通信开销导致速度更慢而且如果数据本身在本地内存中就没必要开太多 worker。常规情况下设 2 到 4 就够用了。3.3 模型构建用 nn.Module 组织网络结构PyTorch 里所有神经网络模型的基类是torch.nn.Module。你自己的模型要继承它并且在__init__方法里初始化各种层在forward方法里定义数据的前向传播路径。举个最直观的例子一个简单的全连接分类网络长这样import torch.nn as nn class SimpleNet(nn.Module): def __init__(self, in_dim784, hidden_dim128, out_dim10): super().__init__() self.fc1 nn.Linear(in_dim, hidden_dim) self.relu nn.ReLU() self.fc2 nn.Linear(hidden_dim, out_dim) def forward(self, x): x self.fc1(x) x self.relu(x) x self.fc2(x) return x注意这里的几个细节。第一forward里的操作就是普通张量运算你可以在中间插入打印、条件分支、循环等任何 Python 逻辑这正是动态图带来的自由度。第二nn.ReLU和torch.relu功能一样但nn.ReLU是一个模块类它所在的子模块会被model统一管理后续调用model.to(device)时所有含参数的模块都会被移动到位而纯函数你不会记得到处处理。第三继承nn.Module之后模型的参数会被自动收集到model.parameters()里优化器直接拿着这个列表去更新就行不需要你自己维护字典。如果你想把神经网络堆叠起来搭建深度模型还有一种更简化的方式nn.Sequential。它把多个层按顺序串起来适合层结构固定的网络。但当网络存在分支结构比如要同时处理两条路径再融合时nn.Sequential就不合适了得用自定义forward的方式。模型写好后下面这行代码至关重要model SimpleNet() device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device)to(device)会把模型里所有参数和缓冲区都移动到目标设备上。如果你的模型在某些层里手动创建了张量比如初始化了一个掩码张量别忘了也把它移到对应设备。跨设备运算会报类似 Expected all tensors to be on the same device 的错误排查起来倒是不难但确实浪费过很多人时间。3.4 训练循环的完整代码实现训练循环是整个项目的发动机。我先给出一段标准的训练代码再逐个环节拆解它的设计逻辑。import torch.optim as optim import torch.nn.functional as F model SimpleNet().to(device) optimizer optim.Adam(model.parameters(), lr0.001) criterion nn.CrossEntropyLoss() epochs 20 for epoch in range(epochs): model.train() total_loss 0.0 for inputs, labels in dataloader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() avg_loss total_loss / len(dataloader) print(fEpoch [{epoch1}/{epochs}], Loss: {avg_loss:.4f})逐行解释一下。model.train()是告诉模型当前处于训练模式这会影响某些层的行为比如Dropout层在训练模式会随机丢弃神经元在评估模式则不过滤BatchNorm层在训练模式用当前 batch 的统计量评估模式用全局统计量。如果你忘了切换模式轻则评估指标不准重则模型训练出问题却排查不出来。optimizer.zero_grad()前面已经说过清空上一轮留下的梯度。注意必须在backward()之前执行顺序错了梯度就叠加了。这里我给一个新手的建议写代码时养成固定顺序的习惯先 zero_grad、再 forward、再 loss、再 backward、最后 step这个顺序就是你思维的主干不要乱。loss.item()是把损失值从张量中取成 Python 的标量数字。为什么不直接用loss本身因为loss是一个拥有梯度信息的 Tensor如果把它保存在列表里累积计算图就不会被释放导致训练越跑越慢。用.item()取出来的是普通数字不参与计算图内存安全。optimizer.step()是根据已经计算好的梯度按照优化器的更新规则去更新参数。到这里一个完整的训练迭代才算结束。还要注意的一点是数据迁移。inputs, labels inputs.to(device), labels.to(device)这行在训练循环里必须存在如果模型在 GPU 上而数据还在 CPU 上就会报设备不一致的错误。如果数据量不太大另一条思路是数据预处理时直接统一放到 GPU 上省掉每一步的迁移开销。但要注意GPU 显存有限数据一次性全放上去对大项目不现实。3.5 模型验证、保存与再加载训练完成后验证模型效果是少不了的环节。评估模式下model.eval() correct 0 total 0 with torch.no_grad(): for inputs, labels in test_dataloader: inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() accuracy correct / total print(fTest Accuracy: {accuracy:.4f})这里有三个关键点model.eval()切换状态torch.no_grad()关闭梯度计算省去不必要的计算图和显存开销torch.max(outputs.data, 1)在输出张量的第一个维度类别维度上取最大值得到预测类别。保存模型有两种方式。第一种只保存模型参数推荐日常使用torch.save(model.state_dict(), model_weights.pth)如果要继续训练或做断点续训就得保存更多信息比如优化器的状态、当前 epoch 数、模型参数等。可以打包成字典存储checkpoint { model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), epoch: epoch, loss: avg_loss } torch.save(checkpoint, checkpoint.pth)加载模型的代码要特别注意注意是先实例化模型再用load_state_dict填充参数而不是直接加载一个模型对象model SimpleNet() model.load_state_dict(torch.load(model_weights.pth)) model model.to(device)如果你保存的时候模型在 GPU 上加载时默认也会按 GPU 加载。如果设备变了比如在 CPU 机器上做推理需要指定map_location参数torch.load(model_weights.pth, map_locationcpu)。这个参数是实际部署时最高频踩坑点。4. 训练进阶损失函数、优化器与关键技巧4.1 损失函数和优化器怎么搭配PyTorch 的nn模块里内置了几十种损失函数但日常用得最多的就那么几个。分类任务首选nn.CrossEntropyLoss注意这个函数在 PyTorch 里已经内置了 Softmax 操作所以你模型的最后一层直接输出原始 logits 就行不要自己再加 Softmax。如果加了等于是 Softmax 之后又算一次交叉熵数值上没问题但梯度效果会受影响收敛变慢。回归任务通常用nn.MSELoss即均方误差。如果碰到目标值范围很大或者存在离群点的情况可以试试nn.SmoothL1Loss它对离群点的惩罚比 MSE 温和不少训练更稳定。优化器选择方面新手推荐直接用Adam因为它自带学习率自适应调节对学习率的敏感度比 SGD 低通常默认 0.001 就有不错的效果。如果你需要有更强的泛化能力且数据量充足可以考虑SGD加动量项momentum0.9配合合适的学习率调度往往能达到更好的收敛效果。还有一个折中的选择是AdamW它在 Adam 的基础上修正了权重衰减的实现方式和 PyTorch 里的 L2 正则配合更正确现在很多预训练模型的官方实现都用的 AdamW。4.2 学习率调度别让模型卡在狭窄区一个经典的现象是训练一开始 loss 降得很快到了某个阶段loss 突然就不动了小半天也不下降然后调低学习率又能往前降一点。这其实是学习率过大导致的震荡问题参数在最优点附近来回横跳永远靠不近。解决这个问题有两个思路。第一个是手动调整学习率训练一段时间loss 不降了就lr lr * 0.1。第二个是用内置的学习率调度器比如ReduceLROnPlateau它会监测验证集的损失如果连续多个 epoch 没有下降就自动降低学习率。scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.1, patience5) # 每个 epoch 结束之后调用 scheduler.step(val_loss)这个方法非常省心我现在的项目基本都是用它。还有另一类按 epoch 预定义调整的策略比如StepLR每 N 个 epoch 衰减一次但ReduceLROnPlateau更智能按照实际训练反馈来更适合做研究性实验。4.3 过拟合的判断和处理思路训练 loss 一路狂降到接近 0验证 loss 却先降后升这是典型的过拟合信号。说白了就是模型把训练样本背下来了而不是学到普适规律。常见的处理手段按效果预期排序大致是这么几个增大数据量是最笨但最有效的办法其次是数据增强比如对图像做随机翻转、裁剪、色彩抖动然后是正则化例如权重衰减weight_decay参数和 Dropout 层。这里的核心技巧是不要一开始就上全套正则化因为你不知道该约束哪个环节。我通常的做法是先让模型过拟合观察它确实能学得很好然后逐步加入正则化手段每加一个观察验证集效果的变化加到验证 loss 不在下降为止。这种方法能帮你精准定位到哪个环节最需要约束而不是一股脑全塞进去把模型整废了。还有一个容易被忽略但很重要的技巧如果你的训练集数据分布和验证集差异很大比如训练集全是某一个场景下的照片验证集是另一个场景的那么无论怎么调都会过拟合。先确认数据分布的一致性再做模型调优这是排查问题时最先应考虑的环节。4.4 数据增强低成本提升泛化能力以图像分类为例数据增强就是在训练过程中对图片做随机的变换每次迭代模型看到的是同一个样本的变体。这样既能扩大有效训练样本量又能在一定程度上提升模型鲁棒性。torchvision.transforms提供了一套很方便的图像处理管线。我的典型组合如下from torchvision import transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(10), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])重点注意ToTensor会把 PIL 图片或 NumPy 数组转成张量同时把像素值从 0~255 归一化到 0~1。Normalize用 ImageNet 数据集统计出的均值和标准差做标准化使得数据分布更接近标准正态分布训练更稳定。下面几个操作有人会忽视RandomHorizontalFlip对某些领域不适合比如文本方向敏感的 OCR 任务翻转会把字变成反的模型反而学坏RandomRotation的角度不宜太大10 度内比较安全ColorJitter调过头会让模型难以收敛。所以数据增强不是越大越好要根据任务特点制定策略。验证集不要做随机增强最多做Resize和Normalize保证评估结果和无损图片直接对比才有意义。5. 常见问题与排查实录5.1 显存溢出不是调小 batch_size 就完事了报错信息最常见的是 CUDA out of memory。新手第一反应是把 batch_size 调小这个方法立竿见影但不是每次都有效。我遇到过一次模型结构比较大batch_size 调到 2 仍然溢出这时候就需要用别的手段了。第一个利器是梯度累积。核心思路是模拟大批量训练但每个 step 只走一小块数据。做法是让多个小批次的梯度累加后再更新参数accumulation_steps 4 optimizer.zero_grad() for i, (inputs, labels) in enumerate(dataloader): inputs, labels inputs.to(device), labels.to(device) outputs model(inputs) loss criterion(outputs, labels) loss loss / accumulation_steps loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()注意这里要把 loss 除以累积步数否则梯度相当于被放大了 accumulation_steps 倍学习率的名义效果就变了训练很容易发散。第二个利器是自动混合精度。GPU 上默认使用 FP32 精度做运算但现代显卡对 FP16半精度的运算速度更快显存占用减半。PyTorch 从 1.6 开始内置了torch.cuda.amp用起来很简单from torch.cuda.amp import autocast, GradScaler scaler GradScaler() for inputs, labels in dataloader: inputs, labels inputs.to(device), labels.to(device) optimizer.zero_grad() with autocast(): outputs model(inputs) loss criterion(outputs, labels) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()还有个小技巧torch.cuda.empty_cache()可以清空未使用的缓存显存但它只是释放了 PyTorch 的缓存池并不能解决内存碎片化问题。真正的高效做法是减少计算图保留比如在不必要的地方断开张量链方法是使用.detach()或者保证在no_grad上下文里做推理操作。5.2 损失变成 NaN排查顺序比盲目调参重要训练到一半看到 loss 是 nan心态很容易崩。出现这个情况我建议按如下顺序排查首先检查数据。训练前做一个数据扫描看有没有缺失值、无穷值以及标签是否越界。对图像数据来说检查像素值范围是否异常。对有监督学习来说检查标签是否有 NaN。然后检查学习率。学习率过大会导致梯度更新幅度过大参数直接越界。把学习率降到原来的 0.1 倍试试这个方法能快速判断是不是学习率的问题。其次注意模型输出层和损失函数的搭配。如前所述CrossEntropyLoss自带 Softmax如果输出层又加了一次 Softmax数值上容易因为 log(0) 出现 NaN。如果上述都不行就在训练循环里逐层检查梯度打印每一层权重的数值。定位到哪一层梯度异常再针对性处理。老项目里有个野路子的经验如果个别层在线性层的初始权重出现全零或者数值极大会导致梯度消失或发散换一种初始化方法也是有效手段。5.3 数据加载太慢CPU 进程跑不满训练时显卡利用率不高GPU 一直等数据这种问题叫 IO-bound。数据读取的速度跟不上 GPU 的计算速度变成瓶颈。常见解决办法有这么几条先确认是否合理设置num_workers太小导致读取慢太大导致进程之间频繁切换和内存开销过大。设置pin_memoryTrue前提是放在DataLoader里主机内存做锁页可以加速数据传输到 GPU。数据预处理环节如果做了较多的 CPU 解码那把预处理的输出缓存到本地文件比如处理成 npy 或图像格式训练时直接读缓存。另外一个不太引人注意的点是如果你在__getitem__里做了非常重的计算比如实时解码大图这部分才是真正拖慢速度的元凶。在内存允许的条件提前把这些数据离线处理好存下来训练时直接加载效果立竿见影。5.4 模型不收敛先从梯度流动看起训练时如果 loss 一直横着不动先别急着改网络结构按下面的思路排查梯度。第一步把最后一个 batch 的数据单独跑一次看loss.backward()之后模型第一层参数的梯度范数如果是 0说明梯度根本没传回来问题在反向传播路径如果梯度异常大超过 100说明梯度爆炸需要降低学习率或者加梯度裁剪。第二步检查网络输出层输出值范围如果所有输出值都一样意味着模型没法区分不同类别可能是特征提取环节太弱。如果输出值一直是某种固定 pattern考虑初始化方式。还有人会遇到另一种情况模型在训练集上表现好但一跑验证集就发疯。这时候先确认model.eval()调用了没有。Dropout 和 BatchNorm 的训练/评估行为完全不一样忘调用评估模式指标会失真。6. 实操心得与后续扩展建议如果只让我说一条 PyTorch 实践里最重要的经验那就是小而美地验证再大规模地训练。新手包括我自己早期特别喜欢一开始就把模型做得又大又复杂数据集全量加载跑了一个 epoch 就要花几个小时最后调试成本极高。我的习惯是先拿一个很小的子集跑通整个流程几十条数据也行确认前向传播、反向传播、参数更新、保存加载这一套链路都通了再放开手脚用全量数据训练。整个流程通了大训练只是时间问题流程没通再大的显存也救不了你的 bug。第二条经验打印日志比你想的重要得多。训练循环里除了打印 loss 和准确率我还会定期打印每个 epoch 的学习率、当前 batch 的输入输出形状、时间开销、显存占用情况。这些看起来琐碎但问题是难排查的时候多一条日志就多一条线索。有个小插曲有次我模型性能反复异常查了一天最后找出根因是想用内存里的缓存数据做训练但忘了数据的内存对象在某些操作后被释放数据内容早已不是预期的版本。如果当时在训练循环一开始就打印对数据取样的摘要信息这个问题几分钟就能定位。第三条经验学会利用官方模型库。多数任务根本不需要你从零搭建模型torchvision.models里提供了像 ResNet、VGG、EfficientNet 这种经过大规模数据集验证的成熟结构你只需要替换最后的分类头然后加载预训练权重做微调。这种做法在数据量有限的情况下特别有效效果远超从零随机初始化训练。而且官方模型库的代码实现是经过无数社区用户检验过的比自己随手搭的网络稳定得多。后续如果你想更进一步建议重点研究这几个方向TorchScript 和torch.compile做模型加速优化ONNX 导出做跨平台部署torch.distributed做多卡分布式训练Transformers 库做预训练语言模型的下游任务。每一个方向都是一扇新的大门但万变不离其宗核心还是你对张量流转和自动求导的理解够不够扎实。这篇内容到这里就结束了。里面写到的每一个坑、每一个技巧都是我实际项目里一遍遍踩出来的。PyTorch 的入门门槛并不高但想真正用好它还是得在一次次实操中打磨对计算流、内存流和数据流的敏感度。希望你读完之后不仅会照着敲代码更能理解代码背后发生了什么。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑