深度学习必备Python基础:从环境搭建到NumPy张量思维
发布时间:2026/10/1 14:29:16来源:尧图网络
说实话写这篇笔记之前我犹豫了一下。深度学习这个系列走到第3篇按理说应该开始碰模型了我却停下来补Python基础——而且标题里那个“Pyhton”我都没改。这恰恰说明一个问题基础里那些看似“很简单”的细节才是真正卡住新手的东西。环境装完、教程打开满屏的transpose、broadcast、view、tensor.shape每一条都认识但放一起就是不知道怎么用。这篇不是给你背语法的Python教程而是专门为深度学习准备的那部分Python基础环境怎么搭不踩坑、语法里哪些写法在模型代码里出现频率最高、NumPy要从什么角度去学才能无缝过渡到张量操作以及最常见的报错和误区到底怎么排查。写给我的目标读者只有两类人一是跑完了环境配置教程但一打开模型源码就发懵的新手二是会用Python写爬虫、写脚本但一进深度学习就总觉得哪里不对的同学。你只要跟着这篇把思维扭过来后面啃PyTorch源码会轻松一大截。1. 先把框架搭起来这份Python基础笔记到底学什么1.1 一条主线从“写Python”到“搭网络”的思维切换我见过太多人学Python学得很努力速成课刷了好几遍列表、字典、循环都用得很溜结果一打开深度学习的开源项目还是看不懂。问题不在于他Python差而在于他学的Python和深度学习需要的Python根本是两个方向。普通Python开发的核心是“业务逻辑”处理文件、写接口、爬数据、操作数据库。深度学习的核心则是“数据张量流”数据进来是什么形状经过一层层变换变成什么形状最后输出什么形状。你在深度学习代码里看到的绝大多数操作都可以归到两个词上一个是reshape一个是变换。所以你的Python基础必须围绕一个核心主线来学——从数据结构思维转向张量形状思维。拿最常见的例子来说。一个新手指着一行代码问“为什么这里要加x x.view(-1, 784)”因为它要把三维或四维的图片数据摊平成二维矩阵才能喂给全连接层。这在普通Python开发里你永远碰不到但在深度学习里到处都是。所以这篇笔记所有内容都会朝着这个方向去组织不是罗列知识点而是告诉你哪个知识点在读模型代码、训练脚本、数据预处理时真的会反复出现。1.2 Python凭什么成为深度学习的事实标准这个问题值得想清楚因为想清楚了你就知道为什么“Python基础”不是可学可不学而是必学。市面上做深度学习的语言不少C、Java、Julia都有人用但最后大家默认选的还是Python原因不只是“简单”。最核心的原因有两个。第一生态。PyTorch、TensorFlow、JAX、scikit-learn这些库全是Python优先深度学习领域的新论文基本当天或一周内就会放出Python版本的复现代码。你想复现别人的模型不用Python等于把自己排除在整个学术社区之外。第二开发效率。深度学习的核心运算其实不归Python管而是C和CUDA在底层执行Python只是一个“指挥者”。你用Python写一行model.train()、loss.backward()底层是几千行高性能代码在跑。Python本身慢不慢根本不重要重要的是它能让你快速把想法组装起来。为了让你有个直观对比我把主流深度学习开发方案的差异列一下方案开发效率性能生态成熟度适合人群Python PyTorch/TensorFlow极高底层C/CUDA优化性能无损最成熟资料最多绝大多数研究者、工程师、学生C TensorFlow C API低优中等需要极致的部署性能或嵌入式场景Java DJL/Deeplearning4j中等中等较弱后端整合到Java系统的场景MATLAB中等中等较弱传统工科实验教学、信号处理现在大家应该明白了Python在深度学习里的地位不是因为它“容易学”而是因为它站在了正确的位置上——既能让你快速表达想法又把高性能计算外包给了底层语言。所以接下来环境搭建、语法速通、NumPy思维全部奔着一个目的去让你尽早写出第一行能跑起来的深度学习代码并且每写一行都知道它在干什么。2. 环境搭建别偷懒Anaconda与虚拟环境的正确打开方式2.1 别从官网裸装Python直接用Miniconda我见过很多小白第一步就走错了去python.org下载一个Python装好然后开始pip install。你要是在深度学习这条路上也这么干后面大概率会踩到两个坑一是Python版本和某个库不兼容二是不同项目依赖的库版本互相打架。这两个坑都极其难排查浪费的时间绝对超过你省下的那几分钟安装时间。正确的做法是直接用Miniconda。Miniconda和Anaconda是同一个东西的两个版本Anaconda自带了一堆科学计算包装完即用但体积巨大Miniconda只带一个conda包管理器和最基本的Python需要什么自己装。我更推荐Miniconda因为干净、可控你能清楚地知道自己装了什么而不是装了一堆用不上的库。安装完之后不要急着用默认的base环境。我的习惯是单独建一个专门跑深度学习的虚拟环境命令是这样conda create -n dl python3.10 numpy matplotlib jupyter ipykernel这条命令做了三件事创建一个叫dl的虚拟环境、指定Python 3.10版本、顺手装好numpy和matplotlib这两个后面一定会用到的库。等它跑完激活环境conda activate dl你会看到命令行前面多了个(dl)这就说明当前在虚拟环境里了。后面所有pip install、跑代码、启动Jupyter都要确保在这个环境下进行。激活环境这个动作说真的我见过太多人忘了然后明明装好的库运行脚本时却报ModuleNotFoundError就是这个原因。2.2 每个项目一个虚拟环境这个习惯能救你一命虚拟环境的好处用一句话说就是不同项目之间依赖隔离。举个例子。你项目A用的是PyTorch 1.13项目B需要PyTorch 2.1两个版本的API有差异。如果你把两个都装到同一个环境里轻则每次运行都要小心翼翼看版本重则直接互相覆盖导致环境崩溃。用虚拟环境就很简单项目A建一个env_a项目B建一个env_b各装各的互不干扰。我在实际中建议再进一步把一个环境里的依赖关系导出成文件方便换机器或分享给你同学时一键复现conda env export environment.yml conda env create -f environment.yml这个习惯在刚入门时可能感觉不到价值等你训练到一半要换GPU服务器、或者实验室新同学要复现你的环境时你会感谢自己当初多敲了这一行。还有一个小细节装包时优先用conda装conda装不到再用pip。原因是conda不仅管Python包还会帮你处理一些非Python的底层依赖比如某些库需要C库支持conda能一并搞定而pip只管Python层面的东西。混用的时候也不可怕记住“先conda后pip”的顺序就好。2.3 Notebook做探索、VSCode写工程环境搭好了还得有趁手的工具。我见过一些新手只用Jupyter Notebook写代码也有人只用VSCode其实这两个工具不是二选一而是各司其职。Jupyter Notebook适合做探索性实验。你会想加载数据、看形状、试几个变换、画个图看看效果这种“写一行看一行”的场景Notebook是最舒服的。它还能直接内嵌显示图像和训练曲线做数据可视化探索非常直观。我通常在Notebook里完成数据探索、模型原型验证、可视化这几个步骤。VSCode适合写正式的可复用代码。比如你开始把数据处理封装成函数、把模型定义写到单独的.py文件里、要训练一个完整的脚本这时就需要一个正经的编辑器。VSCode装好Python插件后调试、代码提示、查看变量都很好用。它对新人非常友好的一点是集成了可视化断点调试你可以在某一行设个断点代码跑到这里就会停下来然后左侧面板能看到当前所有变量的值——这对排查bug的理解效率比print高一个量级。顺带说一个画图的实际小坑。训练完画曲线时横坐标是几百个epoch就会挤成一团看得眼晕。解决方案很简单import matplotlib.pyplot as plt plt.plot(range(1, 101), losses) plt.xticks(rotation45) # 横坐标旋转45度避免标签重叠 plt.show()或者用plt.locator_params(nbins20)让横坐标只显示20个刻度。这些都是实战里经常要用到的小技巧教程里一般不教你但自己跑起来一定会碰到。3. Python语法速通深度学习里真正高频的那些写法3.1 变量是引用不是盒子先懂这一条能省一半调试点很多新手对Python变量有一个错误的理解变量就像一个盒子往里装值。比如b a会以为把a复制了一份给b。但这个理解是错的而且如果在深度学习代码里抱着这个想法你会遇到一些非常诡异的问题。Python里变量更像是贴在对象上的标签。执行b a时并没有复制对象而是给同一个对象又贴了一张标签。你通过b修改内容a看到的也跟着变因为本就是同一个对象。看这个例子a [1, 2, 3] b a b.append(4) print(a) # 输出 [1, 2, 3, 4]a 也被改了这在深度学习代码里特别危险。比如你加载了一个数据集列表不小心写了new_data old_data然后对new_data做了一些预处理比如归一化、打乱顺序结果old_data也被一并改了训练集和验证集就混在一起了。要让复制真正发生得显式写b a.copy()或者import copy; b copy.deepcopy(a)。还要区分可变和不可变类型。整数、浮点数、字符串、元组是不可变的你的操作会生成新对象列表、字典、集合、NumPy数组、PyTorch张量是可变的原地操作会直接影响原对象。我在调试里遇到最多的问题之一就是有人无意中共享了同一个list对象改了这里影响了那里。入门期就把“变量是引用”这条刻在脑子里后面能省掉一半的调试点。3.2 列表、元组、字典选对容器代码自然就顺了Python常用的容器就是列表、元组、字典这三样深度学习里各有各的用途。列表用来装同类的样本集合比如一个训练集就是一堆图像的列表每个元素是一张图。列表可以增删改查天然适合“动态收集”的场景。元组和列表长得很像但它创建后就不能修改通常用来表示“固定结构的数据”比如数据集里每一条样本是(image, label)这样的二元组前半张图片后半标签这个结构不会变用元组就比用列表更合适。PyTorch的Dataset类里重写__getitem__方法时返回的基本都是元组。字典专门用来做“名称到值”的映射。在深度学习代码里字典最常见的用法是保存超参数配置然后在训练脚本里读取config { batch_size: 32, learning_rate: 0.001, epochs: 50, optimizer: adam, }这种写法比你写一堆散落的变量要清晰得多。更高级一点的做法是把所有参数集中在一个.py文件里后面想复现实验时一键读取。还有一类代码里经常会看到**kwargs它本质上就是帮你把字典里的键值对拆开作为函数参数传入理解了字典这个语法也顺理成章了。选对容器的判断标准其实很简单如果你需要“一对一”的键值映射就选字典如果是“一串东西”且中途会增删就选列表如果是“固定结构的一包数据”就选元组。顺着这个标准走代码会自然变得可读、可维护。3.3 切片是深度学习的“第一语法”这一小节我想起强调再强调都不为过。切片就是类似a[1:3]这种写法看起来只是取数据的一部分但在深度学习里它是你操作张量数据的核心手段。先说最基础的形态。切片的完整语法是start:stop:stepstart是起始位置包含stop是结束位置不包含step是步长。三个都可以省略a[:]取全部a[::2]隔一个取一个a[::-1]倒序。字符串、列表、元组、NumPy数组、PyTorch张量全都支持这套规则。但真正体现切片威力的是在多维数据上的应用。深度学习里图像数据通常是四维张量形状是(batch, channel, height, width)代表“这一批有多少张图、每张图几个通道、高多少、宽多少”。你想取出批里的第一张图就写x[0, :, :, :]你想把所有图的R通道抽出来就写x[:, 0, :, :]你想取前8张图、每张图只取左上角64x64区域就写x[:8, :, :64, :64]。这一点和普通Python列表的切片没本质区别但因为它作用在多维数据上覆盖了多个维度很多人一开始会懵。我的建议是别空想直接在Notebook里创建一个形状为(4, 3, 32, 32)的随机数组比如np.random.rand(4, 3, 32, 32)然后一步一步用切片切它每切一次就打印结果的shape亲手操作几次你就彻底懂了。3.4 函数与类PyTorch模型就是挂着类的外衣的函数流函数在Python里很好理解一段能重复调用的逻辑块。深度学习里你到处都会遇到函数定义数据预处理函数、定义损失计算函数、定义训练循环函数、定义评估函数。把这些逻辑拆成函数是让代码可读、可调试的关键。类在深度学习里的地位更特殊因为PyTorch的模型就是这样定义的。给你看一个最典型的模型定义代码import torch.nn as nn class MyModel(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(784, 256) self.relu nn.ReLU() self.fc2 nn.Linear(256, 10) def forward(self, x): x self.fc1(x) x self.relu(x) x self.fc2(x) return x看不懂细节没关系你已经能看出骨架__init__里“搭架子”把网络各层定义好forward里“传数据”把输入一层层传下去最后返回输出。这个类做的事情本质上就是把一串函数串联起来所以我说它“挂着类的外衣的函数流”。当你打印一个模型实例时print(model)会把每层的信息展示出来这是什么原理因为类的__repr__方法被nn.Module实现了所以打印时会输出易读的层次结构。这类底层机制你现在不需要深究但理解“类属性 方法”会帮助你后面看懂面向对象的深度学习代码。4. 用NumPy建立张量思维形状、轴与广播是核心4.1 为什么必须先过NumPy这关Tensor的底层就是ndarray如果你直接跳进PyTorch虽然也能跑但很多操作你会知其然不知其所以然。PyTorch的Tensor底层设计思路和NumPy的ndarray一脉相承你在NumPy里学的形状操作、广播机制、轴的概念几乎都能平移到Tensor上。而且TensorFlow和PyTorch的数据预处理阶段用NumPy的地方比用深度学习库本身还多。读图片、做归一化、拼接batch、数据增强前后的形状管理这些全都跑在NumPy上。换句话说NumPy就是深度学习中“数据操作”这门课本身。NumPy的入门关键又是一个核心概念万物皆数组数组皆有形状。创建一个数组import numpy as np a np.array([[1, 2, 3], [4, 5, 6]]) print(a.shape) # (2, 3) print(a.ndim) # 2 print(a.dtype) # int64shape返回一个元组描述了每个维度上有几个元素。ndim告诉你有几个维度。这组数字就是你接下来要每天盯着看的核心线索。在调试时我几乎默认在任何可疑操作后都打印一下.shape。4.2 shape、axis、广播三个最容易懵的概念很多新手的困惑集中在三个概念上shape、axis和广播。我逐个说透。shape就是一个元组描述数组各维度大小。(3, 4)表示3行4列的二维数组(2, 3, 4)表示由2个(3, 4)矩阵组成的三维数组。你可以把多维数组想象成俄罗斯套娃维度每增加一层就在上一层套娃里面多套一层。axis是指操作沿哪个方向进行。这条真的要好好理解因为你会在各种函数的参数里看到它。拿二维数组来说axis0表示沿行方向从上到下操作axis1表示沿列方向从左到右操作。np.sum(a, axis0)得到的结果是对每一列求和形状从(2, 3)变成(3,)np.sum(a, axis1)则是对每一行求和形状变成(2,)。广播是让不同形状的数组能直接做运算的规则也是初学者最懵的地方。最简单的理解NumPy会从尾部开始对比两个数组的维度如果两个维度相等或其中一个为1就可以对齐并广播。看个实际例子a np.zeros((3, 1)) # 形状 (3, 1) b np.ones((1, 4)) # 形状 (1, 4) c a b # 结果形状 (3, 4)这里a的(3,1)和b的(1,4)都不完全相等但通过广播a被隐式扩展成了对每一列重复b也被隐式扩展成了对每一行重复最后加出来一个(3, 4)的矩阵。这个机制在深度学习里到处都是比如给所有样本加上同一个偏置向量、批量归一化BatchNorm之类的操作底层全是广播。4.3 一个手写数字的例子帮你把形状变化刻进直觉里理论说再多都抽象直接看一个实际例子。假设你加载了一批手写数字图片形状是(32, 1, 28, 28)意思是32张图、每张单通道、28x28像素。第一件事很多模型要求输入是一维向量所以要把每张图“摊平”x_flat x.reshape(32, 784) # 28*28 784这里reshape(32, 784)把四维数据变成了二维32行每行是784个像素值。-1这个参数在任何维度上都可以用表示“这一维大小由系统自动推导”。比如写x.reshape(-1, 784)系统会根据总元素数自动算出第一维是32。我特别推荐你养成用-1的习惯因为代码里常会出现你不确定具体批大小的情况。但有时候你又会遇到反向的问题。图片已经是一维了但卷积神经网络需要二维的矩阵形式于是x_2d x_flat.reshape(32, 1, 28, 28) # 变回四维再比如某模型要求通道在最后一维很多深度学习库的数据格式是(batch, height, width, channel)但你的数据是(batch, channel, height, width)这时候要用transposex_transposed x.transpose(0, 2, 3, 1)这行代码的意思是把原数组的第0维放在新数组第0维原第2维放在新第1维原第3维放在新第2维原第1维放在新第3维。如果你只做一次可能会绕晕我的建议是完整跑一遍下面的流程创建形状(32, 1, 28, 28)的随机数组→reshape(32, 784)→打印形状→reshape(32, 1, 28, 28)→打印形状→transpose(0, 2, 3, 1)→打印形状。每一步的shape变化亲手打出来胜过背十遍规则。我当时就是靠这一套操作把“形状”这个概念彻底焊死在脑子里了。5. 调试与避坑别人踩过的坑你最好别踩第二次5.1 新手最容易踩的六个坑速查表我在带新人和自己学习的过程中见过、踩过的坑其实蛮有共性的。整理成一个速查表比只说一个具体报错更有价值坑点表现原因解决环境没激活明明装好了库运行却提示ModuleNotFoundError当前终端不在虚拟环境里或Notebook内核选错conda activate dl确认命令行前缀Notebook里检查kernel是否对应dlpip装错版本装PyTorch后import torch报错装了不匹配的版本或CPU版当GPU版用按照官网对应CUDA版本的命令安装用nvidia-smi确认驱动版本shape搞错报错信息里出现“size mismatch”之类数据维度不是模型期望的输入维度逐层打印x.shape定位到具体哪一层不匹配广播规则破防两个形状完全不兼容的数组直接相加报错对广播规则理解不到位回到(3,1)与(1,4)的例子重学广播三条规则数据没归一化训练loss不降或非常大输入值域差距过大比如0-255和0-1除以255或标准化检查数据范围matplotlib显示问题中文变方块、横坐标挤成一团、图不显示字体配置、刻度重叠、Notebook缺魔法命令plt.rcParams[font.sans-serif] [SimHei]加rotation45Notebook里加%matplotlib inline第3个坑我单独多说几句shape不匹配是新手报错里出现频率最高的一类。它会让你盯着屏幕怀疑人生但排查思路其实很清晰。比如报错信息写mat1 and mat2 shapes cannot be multiplied (32x784 and 256x10)就是在提示你左边矩阵是32x784右边是256x10矩阵乘法要求左边列数右边行数这里784不等于256所以炸了。定位方法就是在出错的层前打印一下输入形状print(x.shape)一路打印过去总能在第一处对不上的地方发现问题的根源。5.2 关于“parameter是不是mb”一句话说清参数量纲这个问题是我从搜索引擎里看到很多人在问的确实值得专门讲一下。有人看到模型描述里写“参数量7B”有人看到模型文件大小是4GB就开始疑惑parameter到底是不是MB一句话回答parameter的数量单位是“个”不是MBMB是存储大小单位。一个线性层nn.Linear(784, 256)的参数个数是784 * 256 256 200960大约20万个。多出来的256是偏置项。这个参数数量跟字节数怎么换算要看权重存成什么精度。用float32表示每个参数占4字节那么这20万个参数占200960 * 4 803840字节约786KB。如果一个模型有70亿个参数比如大型语言模型的“7B”用float16表示每个参数占2字节那么光权重就需要70e9 * 2 140e9字节也就是约140GB。所以你看参数数量和文件大小是两个维度的事别把它们混在一起。在深度学习讨论中大家说“模型的参数量”就是指有多少个可学习的权重说“模型大小”通常是指这些权重的存储字节数。这两个指标一个看模型“规模有多大”一个看“跑起来要占多大空间”各有各的用途。理解了这一点你在看模型规格时就不会再把“7B”直接当成“7GB”了。5.3 三个调试习惯比任何技巧都管用最后分享三个我从实践中总结出来的调试习惯不花哨但比任何奇技淫巧都管用。第一个习惯打印形状。我在调试深度学习代码时最常用的语句就是print(x.shape)。输入数据出来打一次、第一层后打一次、reshape之后打一次、输出之前打一次。四个print打下去整个数据流向就清楚了问题往往马上自己现形。这个习惯土但极其有效。第二个习惯断点调试而不是print满天飞。print能看变量值但看不到代码执行到哪一步时的完整上下文。VSCode里在行号左侧点一下就能设断点运行到断点时程序暂停左侧面板可以看到所有当前的变量值还能单步执行。你只需要在出错那一行之前设个断点看一眼变量们的形状和值很多时候一眼就能定位问题。我的经验是print用来快速确认断点用来深入排查。第三个习惯最小复现。遇到一个诡异的bug别在完整的训练脚本里埋头查。把报错的这部分代码抽出来用一小段最简数据单独测试。比如你怀疑广播有问题就构造两个形状最简单的数组在一个新的Notebook单元格里做同样的操作看结果对不对。这样能帮你快速排除大量无关因素精准锁定问题源头。这个习惯不仅适用于深度学习任何工程问题都适用——把问题逼到最小规模它就没地方躲了。这篇笔记写到这里其实内容本身没什么“高深”的东西但我们这行就是这样模型结构再复杂最后决定你能不能把代码跑起来、能不能定位到问题根源的永远是这些基础环节。我自己回头翻过往的学习记录惊觉那些困扰了我一整晚的问题最终几乎都回到了环境、形状、依赖、语法习惯这几个点上。你如果能在入门阶段把这些基础打踏实后面读论文复现代码的路会顺非常多。
网站建设高端定制企业官网