尧图精选

深度学习必学Python核心语法,从变量到张量一次讲透

🕒 发布时间:2026/9/26 21:23:08 📁 来源:尧图网络
很多人问我想学深度学习是不是得先把Python整本学完我的答案一直是不用但该学的核心语法一个也不能漏。我见过不少朋友拿着一本几百页的Python教程啃了一个月列表推导式背得滚瓜烂熟但打开PyTorch写模型时依然不知道nn.Module的__init__和forward到底怎么配合反过来也有人一行Python没系统学过直接硬抄GitHub代码跑通了但完全不敢改参数。这篇内容就是冲着这个问题来的——把深度学习中真正高频用到的Python语法挑出来讲清楚配合最典型的深度学习代码场景让你少走弯路直接具备读得懂模型代码、改得动训练脚本的基本功。1. 深度学习里的Python你其实只需要这一部分语法1.1 为什么深度学习偏偏选了Python深度学习框架那么多底层实现大多是C和CUDA但用户侧的编程接口却统一选择了Python这不是偶然。核心原因是Python特别适合描述网络怎么搭、数据怎么流这类逻辑定义网络层就是创建对象前向传播就是调用方法数据变换就是函数组合。C写一个全连接层可能要处理内存分配、模板特化Python里一行nn.Linear(128, 10)就完了。而且Python是动态语言调试非常方便。你可以随时print张量的形状用一个assert快速检查维度这些在编译型语言里都要多绕很多弯。对搞深度学习的人来说快速验证想法比极致运行性能重要太多——性能问题交给框架底层去优化就行你关注的是模型结构本身。这就是为什么你在PyTorch、TensorFlow、Keras、PaddlePaddle这些主流框架里看到的用户代码几乎全是Python风格。所以掌握Python语法不是学一门前置课程而是在学深度学习本身的表达方式。1.2 学语法要有边界感哪些必须精哪些可以先放我接触过不少零基础转深度学习的学员最典型的问题不是语法看不懂而是不知道学语法的边界在哪里。Python的语法面其实很宽但深度学习场景下真正高频使用的部分是有明确范围的。必须掌握的语法点在深度学习中的典型用途变量与基础数据类型定义超参数学习率、批次大小、记录训练轮数列表、元组、字典、集合组织训练数据、管理标签映射、存储配置参数切片与索引切割数据集的训练/验证子集、从张量中取子区域if/for/while流程控制实现训练循环、条件判断、早停逻辑函数定义与lambda封装数据预处理、定义损失函数、自定义操作类与继承定义神经网络模型结构继承nn.ModuleNumPy/PyTorch张量操作数据处理、归一化、维度变换、矩阵运算先不用碰的包括装饰器的复杂用法、生成器的高级场景、多线程多进程、元类、上下文管理器细节、Python的GIL机制、GUI开发、网络爬虫相关。不是说这些没用而是在入门阶段它们会严重分散你的注意力。我见过太多人花两周研究__slots__和weakref结果自己的第一个模型还没跑起来。务实一点把上面表格里的内容吃透深度学习代码对你来说就已经没有语法层面的障碍了。2. 从变量到函数模型训练代码里高频出现的核心语法2.1 变量与数据类型别小看这些常识深度学习代码里最常见的变量类型就是int、float、bool、str但有几个细节值得单独拎出来。第一个是Python的动态类型——变量本身不绑定类型同一个名字可以先后赋值为整数、浮点数、字符串。这在写训练脚本时很方便但也容易埋坑如果你不小心把epoch从10改成了10range(10)会直接报错。所以我的习惯是在关键超参数处加assert isinstance(..., int)之类的小检查。第二个是bool类型。在Python里True和False是int的子类True 1、False 0。这在计算指标时特别容易出问题比如你写准确率时用sum(preds labels) / len(labels)如果preds labels的结果是布尔数组sum是可以直接算出正确个数的但如果你不小心对布尔值做了位运算结果可能完全出乎意料。第三个是字符串的格式化。深度学习中大量涉及路径拼接、日志输出Python 3.6以后推荐用f-stringepoch 10 loss 0.1234567 print(fEpoch: {epoch}, Loss: {loss:.4f}) # 输出Epoch: 10, Loss: 0.1235这个:.4f会保留四位小数在训练日志里非常实用。字符串拼接用虽然简单但路径拼接最好用os.path.join()否则跨平台时容易踩反斜杠的坑。2.2 四个核心容器列表、元组、字典、集合这四种容器在深度学习代码里各有分工几乎每天都会碰到。列表list是深度学习代码中出现频率最高的容器。数据加载器返回的批次、训练过程中收集的损失值、预测结果的存储基本都是列表。它支持任意类型混存可以动态扩容也支持切片和嵌套。下面这段代码就是最典型的量表写法train_losses [] for epoch in range(num_epochs): epoch_loss train_one_epoch(model, train_loader) train_losses.append(epoch_loss)元组tuple和列表长得很像但它不可变。这恰恰是它的价值有些数据你不希望被意外修改。比如模型输入张量的形状(batch_size, channels, height, width)用元组保存更安全。另外元组可以作为字典的键列表不行这就是为什么有些映射关系你要用元组。字典dict在深度学习里的地位怎么强调都不过分。模型的配置参数、数据集的类别映射、优化器的参数字典全部是字典。比如config { lr: 0.001, batch_size: 32, num_epochs: 50, optimizer: adam, device: cuda } print(config[lr]) # 取值 config[lr] 0.0001 # 修改值很多框架的API都接受字典作为参数比如torch.optim.Adam(model.parameters(), **config)这里的**config会把字典自动拆成一堆关键字参数这是Python里非常经典的语法模式。集合set主要是去重和快速判断成员关系。比如你检查一组标签里有多少个类别直接len(set(labels))就行。它的查找时间复杂度是O(1)在数据量大的时候比列表的in操作快得多。2.3 切片与索引张量操作的基本功索引从0开始这种规则我不多说但深度学习中切片的使用频率远超普通Python开发。你要从一张(1, 3, 224, 224)的图像张量里取第一部分、取某个通道、取子区域全靠切片语法tensor[start:stop:step]。data [10, 20, 30, 40, 50, 60] print(data[1:4]) # [20, 30, 40] print(data[:3]) # [10, 20, 30] print(data[::2]) # [10, 30, 50] print(data[::-1]) # [60, 50, 40, 30, 20, 10]注意切片是左闭右开的data[1:4]只包含索引1、2、3。这个边界细节搞错你调试维度匹配时会非常痛苦——比如你以为取了三行实际只取了两行。负索引data[-1]取最后一个元素也是高频操作比如看验证集最后一个batch的结果。2.4 流程控制训练循环的骨架深度学习训练过程本身就是一个大循环遍历epoch遍历batch前向传播、计算loss、反向传播、更新参数。所以for循环是训练代码的主体结构。你需要掌握的有两点range生成整数序列enumerate同时拿到索引和值。for batch_idx, (images, labels) in enumerate(train_loader): print(fBatch {batch_idx}, images shape: {images.shape})enumerate里的batch_idx就是当前批次的编号这在打印进度、保存中间检查点时非常常用。if-elif-else则用于各种分支判断比如根据epoch调整学习率if epoch 20: lr 0.01 elif epoch 40: lr 0.001 else: lr 0.0001while循环在深度学习里用得少一些但实现早停early stopping时会用到。核心思路是当验证集指标连续N个epoch不提升就跳出循环。2.5 函数与lambda封装你的逻辑函数是代码复用的基础。在深度学习项目中数据预处理、模型构建、指标计算、可视化等都会拆成函数。你需要掌握的基础语法包括默认参数和*args、**kwargs。def normalize_image(image, mean(0.5, 0.5, 0.5), std(0.5, 0.5, 0.5)): return (image - mean) / std*args能接收任意多个位置参数**kwargs能接收任意多个关键字参数。在很多框架的接口设计中你会见到这种写法比如自定义数据集类的__init__经常会有**kwargs来透传配置。lambda是匿名函数一行搞定一个简单逻辑。在数据预处理、自定义采样器时经常出现normalize lambda x: (x - x.min()) / (x.max() - x.min() 1e-8)2.6 列表推导式让代码更像人话列表推导式是Python最优雅的语法之一也是深度学习代码中辨识度很高的写法。核心格式是[表达式 for 变量 in 可迭代对象 if 条件]。我举几个实际场景# 把每轮epoch的损失取出来 losses [epoch_loss for epoch_loss in train_losses] # 过滤掉太小的梯度 gradients [g for g in model.parameters() if g.grad is not None] # 将数据集按比例拆分时的索引 indices [i for i in range(len(dataset)) if i % 5 ! 0]有些初学者会把列表推导式写得非常复杂嵌套三四个循环我觉得没必要可读性优先。但基本的一层推导式强烈建议掌握因为它在处理数据时比for循环加append简洁很多。3. 类与继承搭建神经网络模型绕不开的语法基础3.1 为什么深度学习框架都用类来定义模型这个问题我每次讲语法都会被问到定义模型能不能只用函数技术上可以但框架选择类是有道理的。一个神经网络模型拥有的东西不只是计算过程还有大量参数。这些参数需要注册、需要管理、需要保存和加载。类的天然特性——属性存储数据、方法定义行为——正好匹配这个需求。在PyTorch里你自定义的网络都要继承torch.nn.Module然后再实现__init__和forward两个方法。__init__负责定义网络里有哪些层比如卷积层、全连接层forward负责定义数据在这些层之间怎么流动。框架会通过这两部分自动完成参数管理。3.2 掌握__init__和forward你就能自己搭网络了看一个最简单的全连接网络定义import torch import torch.nn as nn class SimpleNet(nn.Module): def __init__(self, input_dim, hidden_dim, num_classes): super(SimpleNet, self).__init__() self.fc1 nn.Linear(input_dim, hidden_dim) self.relu nn.ReLU() self.fc2 nn.Linear(hidden_dim, num_classes) def forward(self, x): x self.fc1(x) x self.relu(x) x self.fc2(x) return x逐个语法点拆开讲。class SimpleNet(nn.Module)是定义一个继承自nn.Module的子类这里的括号就是继承的写法。super(SimpleNet, self).__init__()是调用父类的初始化方法这行代码的缺失会导致模型内部的参数管理机制没法正常启动——这是新手最容易漏掉的漏掉之后运行时会报各种莫名其妙的错误。self.fc1 nn.Linear(...)是关键它会把层对象绑定到实例属性上框架靠这个自动识别哪些是模型参数。如果你不用self.而用一个普通局部变量这个层就不会被纳入参数管理梯度更新时会被忽略。forward方法接收输入x按顺序经过全连接层、激活函数、全连接层最后返回输出。这里可以看到函数调用和对象方法的组合使用也是return语句的经典场景。3.3 继承与重写框架设计哲学在语法层面的体现继承让继承别人已有的能力再定制自己的逻辑变得非常自然。你不需要自己实现参数的初始化、保存、加载、设备迁移这些复杂机制只需要继承nn.Module然后重写forward。这个重写就是面向对象里方法覆盖的概念。PyTorch中nn.Module提供了丰富的现成方法比如model.parameters()、model.train()、model.eval()、model.state_dict()、model.load_state_dict()。你在自己的训练脚本里几乎天天用到这些方法它们全部来自父类。继承和组合的思想理解透了你再看框架源码里的class ResNet(nn.Module)、class VGG(nn.Module)就能很自然地猜到它的结构。3.4 类的其他常用语法点初始化、实例化与self还有几个类相关的语法细节值得留意。实例化就是model SimpleNet(784, 256, 10)这里的参数会传入__init__。创建实例之后系统自动调用__init__完成初始化self始终指向当前实例本身。self是方法内部访问实例属性和其他方法的入口。在深度学习代码里模型里每个层的设计都用self挂在实例上。除了__init__模型类中常见的还有forward、自定义的train_step、validate等。如果你要定义一个带动量或自定义初始化方式的层同样是通过在__init__里设置额外属性来实现。4. NumPy与张量用Python语法操作多维数据的正确姿势4.1 先理解维度再谈语法深度学习的数据本质上就是高维数组图像是(高度, 宽度, 通道数)一批图像是(批次大小, 通道数, 高度, 宽度)文本序列是(批次大小, 序列长度, 词向量维度)。这个张量的形状概念如果建立不起来后面看任何框架代码都会一头雾水。我习惯用嵌套列表来具象化一维数组是普通列表二维数组是一个列表里装了很多一维数组三维数组就是列表套列表再套列表。PyTorch里你随时可以通过tensor.shape查看维度也可以通过len(tensor.shape)判断是几维张量。调试时报错说shape不匹配八成就是你对维度的理解和对数据的实际形状对不上。4.2 张量的创建与转换深度学习代码里张量的创建主要靠torch.tensor、torch.zeros、torch.ones、torch.randn、torch.arange等函数。从Python列表直接创建张量是最直观的方式import torch data [[1, 2, 3], [4, 5, 6]] tensor torch.tensor(data) print(tensor.shape) # torch.Size([2, 3])这里的data是一个普通Python嵌套列表torch.tensor把它转换成了张量。同样你也可以用.tolist()把张量转回Python列表这在调试和可视化时会用上。数值计算方面NumPy和PyTorch之间的自由转换最重要torch.from_numpy(np_array)、tensor.numpy()。但注意默认情况下两个框架的数组在CPU上共享内存修改一个会影响另一个不熟悉时容易出诡异问题。4.3 广播机制语法背后的计算规则广播broadcasting是NumPy和PyTorch张量运算最核心也最容易让新手困惑的机制。它的本质是当两个数组形状不一致但满足一定条件时系统自动把较小的一方扩展成较大的形状再运算。举一个数据归一化的真实例子。假设你有一批形状为(32, 3, 224, 224)的图像数据你想对每个通道减去一个均值(3,)直接x - mean就能实现因为PyTorch会自动把mean在通道维度上广播。这里省略了很多繁琐的复制操作大幅简化了代码。但使用广播时一定要留意逻辑上的正确性否则有时不会报错但计算结果完全不对。稳妥的做法是最初阶段多用assert验证形状比如assert x.shape (32, 3, 224, 224)。4.4 形状变换reshape、view、permute、squeeze、unsqueeze深度学习代码里有一类高频操作——形状变换它们的语法看起来很简单但每个函数的逻辑区别非常大。reshape和view都能改变张量形状但view要求张量在内存中是连续存储的而reshape会自动处理不连续情况。换句话说view能做的reshape基本都能做但view速度更快遇到view报错时改成reshape通常就解决了。permute用于维度重排最常见的是把图像张量从(H, W, C)变成(C, H, W)这个转换在图像数据预处理中是必学的。squeeze删除长度为1的维度unsqueeze则增加一个长度为1的维度这两个操作常用来对齐维度x torch.randn(32, 1, 224, 224) x x.squeeze(1) # (32, 224, 224) x x.unsqueeze(0) # (1, 32, 224, 224) # unsqueeze(0) 表示在第0维增加一个维度5. 环境搭建与第一个小例子跑通一个真实训练流程5.1 安装Python与创建虚拟环境在动手写深度学习代码之前先把环境搭好。我建议直接安装Python 3.8以上版本因为这些主流的深度学习框架对旧版本的支持越来越差很多新语法特性也需要新版本解释器。安装完成之后在终端输入python --version确认版本号输入pip --version确认包管理工具可用。然后我强烈建议创建虚拟环境。虚拟环境的目的是给每个项目提供一套独立的Python依赖环境避免不同项目之间因为包版本互相冲突导致崩溃。创建方法有两种主流选择一是直接用Python自带的venvpython -m venv dl_env二是使用Anaconda或Minicondaconda create -n dl_env python3.9我个人的建议如果只是学深度学习和PyTorchvenv够用如果你还要用到很多科学计算包Anaconda的环境管理会更顺手。环境创建好后激活方式一个是source dl_env/bin/activatemacOS/Linux或dl_env\Scripts\activateWindows一个是conda activate dl_env。5.2 安装PyTorch先跑CPU版还是GPU版深度学习框架的安装是很多人入门时最头疼的一步。我的建议入门阶段先用CPU版跑通代码环境搭建简单、兼容性好也不涉及显卡驱动问题。等你已经能熟练写出训练脚本、真正开始大规模训练了再装GPU版那时你也能更冷静地处理驱动、CUDA版本这些复杂问题。安装CPU版的PyTorch直接用pip就可以pip install torch torchvision装完后用一个简单命令验证是否成功import torch print(torch.__version__) x torch.randn(3, 3) print(x)如果能正常打印版本和随机张量说明环境已经就绪。GPU版安装相对复杂要根据你的显卡驱动来选择CUDA版本这一步的具体操作离语法主题较远这里先不展开等你有需要时再单独处理。5.3 第一个小例子用20行代码跑通张量到训练的完整闭环环境就绪后我强烈建议不要急着啃复杂的模型代码先跑通一个最小训练循环。下面的示例用随机生成的数据做线性回归完整展示了张量创建、模型定义、损失函数、优化器、梯度清零和反向传播这几个关键环节import torch import torch.nn as nn # 1. 随机生成模拟数据 X torch.randn(100, 1) y 3 * X 2 0.1 * torch.randn(100, 1) # 2. 定义模型一个最简单的线性层 model nn.Linear(1, 1) loss_fn nn.MSELoss() optimizer torch.optim.SGD(model.parameters(), lr0.01) # 3. 训练循环 for epoch in range(200): pred model(X) # 前向传播 loss loss_fn(pred, y) # 计算损失 optimizer.zero_grad() # 梯度清零 loss.backward() # 反向传播 optimizer.step() # 更新参数 if epoch % 20 0: print(fEpoch {epoch}, Loss: {loss.item():.4f}) # 4. 查看拟合结果 print(model.weight.item(), model.bias.item())这里有一个新手特别容易漏掉的语法点optimizer.zero_grad()必须放在loss.backward()之前否则每个batch的梯度会累加在一起。这也是PyTorch框架设计的一个坑很多教程里提过但自己动手跑一遍才会真正记住。这个例子虽然简单但它包含了深度学习训练的完整骨架后面无论你写多复杂的网络训练流程的语法结构都是这个套路。6. 新手最容易踩的Python语法坑与我的学习建议6.1 可变默认参数不写None就会出问题Python有一个著名的坑函数的默认参数如果是可变对象比如列表、字典这个对象在函数定义时就被创建了后续所有调用共享同一个对象。在深度学习代码中你可能会写出这样的函数def add_loss(loss_list[]): loss_list.append(1) return loss_list第一次调用返回[1]第二次调用返回[1, 1]这个loss_list在多次调用间被共享结果完全不符合预期。正确写法是def add_loss(loss_listNone): if loss_list is None: loss_list [] loss_list.append(1) return loss_list在训练脚本里记录日志或累积损失时非常容易踩到这个坑。还有一点——当累积整个epoch的损失时记得每个epoch结束要用epoch_loss 0.0重新初始化而不是继续往旧列表里追加。6.2 深拷贝与浅拷贝数据被意外修改时先检查这里Python的变量赋值本质是引用绑定不是复制。b a之后修改b通常也会影响a。对于列表切片[:]能复制一层但嵌套列表依然共享内层。对于张量.clone()能创建深拷贝.detach()能切断梯度传递但共享数据内存。深度学习代码里最常见的坑是你把验证集的张量拿去做了原地修改比如tensor.add_(1)结果验证集数据被污染了后面的评估指标全错。正确做法是如果要在验证时做临时修改先.clone()一份。我看到太多人调试半天最后发现是原地操作惹的祸。6.3 原地操作与梯度x 1和x x 1不一样在PyTorch中x 1是原地操作in-placex x 1会创建新的张量。这个区别对梯度计算影响极大。对需要梯度的张量做原地操作会导致某些功能报错虽然具体报错信息因版本而异但本质都是自动微分系统无法追踪原地修改的历史。建议在自定义网络或自定义损失函数时默认写成x x 1这种非原地形式能少很多麻烦。6.4 我给你的几条学习路径建议第一语法学习和跑代码并行推进。不要先花两个月专门学Python再碰深度学习而是边跑模型边查语法哪种方式记得最牢固。第二每个语法概念都找一个深度学习代码中的真实例子比如学列表推导式就去看数据加载部分的源码学类就去看一个预训练模型的定义。第三把一个简单模型比如线性回归或两层全连接网络从零写三遍每遍都不看参考代码直到能完全默写出来这时候你对Python语法的理解已经能支撑绝大多数深度学习代码的阅读和改写了。我自己的体会是深度学习里的Python与其说是一门编程语言不如说是一种阅读和表达模型逻辑的工具。你不用掌握它的全部只要把高频语法点练到形成肌肉记忆剩下的都是查文档就能解决的问题。真正卡住你的永远是数学和模型理解而语法花两周时间有针对性的练习足够带你跨过这道门槛了。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →