python—pytorch学习笔记
目录:
问题1:layer层是什么? 问题2:Tensor张量是什么? 问题3:Pytorch能做什么呢?
问题4:requires_grad=True 是什么? 问题5:为什么需要梯度?
问题6:要更新参数,就要知道每个参数对 loss 的导数(梯度)。 这是为什么呢,为什么更新参数就需要对loss的导数呢?
问题7:这个更新的操作,是用户写的吗,还是pytorch框架已经写好了?
问题8:为什么必须写 optimizer.zero_grad()? 问题9:不同优化器(SGD/Adam/...)有啥区别?
问题10:Pytorch是如何存储和更新梯度的? 问题11:torch.tensor是什么?
问题12:Tensor的维度是什么? 问题13:Tensor如何在GPU上移动?
问题14:pd.read.csv('temps,csv')
问题1:layer层是什么?
答案:在神经网络中,layer(层)就是网络的组成单元
可以把整个神经网络想象成多层结构组合的嘛
Input ->层1->层2->层3->Output
常见的layer有:
-
torch.nn.Linear ->全连接层
-
torch.nn.Conv2d ->卷积层
-
torch.nn.ReLU ->激活层
-
torch.nn.BatchNorm2d ->归一化层
torch.nn.Linear(100, 10) 表示 输入维度:100,输出维度:10
每次输入100个特征,然后输出10个特征
这个层内部自动创建了两个参数:
权重矩阵:(10,100),偏置向量:(10,)
手动初始化权重矩阵
layer.weight.data = torch.randn(10, 100)
用 正态分布随机数 生成一个 10×100 的矩阵,把它赋值给 layer 的权重
问题2:Tensor张量是什么?
张量就是一个可以放数字的容器,类似:
-
标量(数字):0D 张量
-
向量(list):1D 张量
-
矩阵(表格):2D 张量
-
图像(3D 或 4D)
-
视频(5D)
PyTorch 中所有数据(图片、文本、特征)都是用 Tensor 表示的。
Tensor 就是 PyTorch 的“基本生命单元”。
问题3:Pytorch能做什么呢?
一、科学计算(类Numpy)——算数字
二、自动求导——帮你算梯度
三、构建神经网络——搭积木一样搭模型
问题4:requires_grad=True 是什么?
告诉 PyTorch:这个张量需要计算梯度。
x = torch.tensor([1.0], requires_grad=True)
表示:
-
x是一个参与训练的参数(比如权重) -
PyTorch 会追踪 x 的运算
-
可以对 x 求导
问题5:为什么需要梯度?
因为深度学习的训练步骤是:
前向传播 → 计算损失 loss → 反向传播求梯度 → 更新参数
要更新参数,就要知道每个参数对 loss 的导数(梯度)。
有梯度 → 能训练参数 无梯度 → 参数不会更新
问题6:要更新参数,就要知道每个参数对 loss 的导数(梯度)。 这是为什么呢,为什么更新参数就需要对loss的导数呢?
我们可以把梯度就叫成导数,理解起来合适一点。

我们把 loss 看成一个山坡:
-
你现在站在山坡上的某个位置(当前参数值)
-
你想往山底走(最小 loss)
-
但你不知道各个方向的坡度
这时候你需要:
梯度(导数)告诉你: “如果你往某个方向移动,loss 会变大还是变小,变多少?”
导数 = 坡度 = 方向信息
梯度为正 → 往这个方向走 loss 会变大
梯度为负 → 往这个方向走 loss 会变小
导数告诉你“最快下降方向”
假设你有一个参数 w,loss 是:
loss = f(w)
你想让 loss 变小,就要改变 w,但改变多少方向呢?
我们观察导数:
-
如果 f’(w) > 0 → loss 随 w 的增加变大 → 要减小 w(朝负方向走)
-
如果 f’(w) < 0 → loss 随 w 增加反而变小 → 要增加 w(朝正方向走)
也就是说:
梯度指向“让 loss 增大的方向”, 所以我们沿着“负梯度”方向更新,loss 就下降。
用非常简单的例子让你秒懂
假设:
loss = (w - 3)²
这是一个凸函数,最小点在 w = 3。
假设当前 w = 6
计算导数:
loss = (6 - 3)² = 9 d(loss)/dw = 2*(w - 3) = 2*(6 - 3) = 6
梯度是 正的,说明:
loss会随着w的增大,而增大。所以我们要往反方向走,就是减小w【因为我们最终的目的是要loss减到0】【Because 深度学习训练的目标:让loss最小】
于是更新:
w := w - 学习率 * grad w := 6 - 0.1*6 = 5.4
loss 从 9 降到 5.76 成功下降!
如果没有梯度会怎样?
假如你完全不知道导数,只能:
-
乱试
-
瞎调
-
猜方向
根本无法收敛,模型不会学任何东西。
深度学习有 千万级参数(ResNet、Transformer),你根本不可能靠猜方向训练。
梯度 = 高维空间里的指南针 它告诉你:
“往哪个方向动,能让 loss 降得最快”
梯度下降(Gradient Descent)本质一句话
深度学习训练的本质,就是不断沿着负梯度方向下山,直到找到最小 loss。
问题7:这个更新的操作,是用户写的吗,还是pytorch框架已经写好了
这个更新公式(w = w - lr * grad)本质是数学原理,但实际代码更新 不是你自己写的,而是由 PyTorch 的优化器(optimizer)帮你写好的。
你只需要调用:
optimizer.step()
PyTorch 就会自动做:
w := w - lr * grad
也就是:
-
你不用手动改 w
-
PyTorch 会帮你修改模型里所有
requires_grad=True的参数 -
修改的规则就是你说的那个公式(梯度下降)
再说得更清楚一点:训练过程谁负责什么?
(1)用户负责的代码
用户需要写:
optimizer.zero_grad() # 梯度清空 loss.backward() # 自动求梯度 optimizer.step() # 更新参数(PyTorch 来写)
(2)PyTorch 负责的事情
当你写 optimizer.step() 时:
PyTorch 会自动做:
for param in model.parameters(): param = param - lr * param.grad
你完全不用写上面这些。
换句话说:
优化器 step() 就是把你学过的“梯度下降公式”自动执行一遍。
问题8:为什么必须写 optimizer.zero_grad()?
PyTorch 的梯度默认是“累积”的!
比如你有:
loss.backward()
PyTorch 会把梯度加到原来的梯度上:
param.grad = param.grad + 新计算的梯度
如果你不清空,第二次 backward 会变成:
param.grad = 第一次梯度 + 第二次梯度
梯度会越积越多,参数更新就会变得乱七八糟。
所以每次训练循环必须清空:
optimizer.zero_grad()
相当于:
param.grad = 0
🔥为什么 PyTorch 设计成累积?
为了方便多次 backward 再一次更新(比如 RNN、梯度累积),是一种「灵活性的设计」。
问题9:不同优化器(SGD / Adam / …)有啥区别?
SGD(最基础)—— w := w - lr * grad
特点:
-
方向 = 纯梯度方向
-
简单、稳定,但有时收敛慢
就像找路下山:
SGD 是盲走,只看当前坡度。
Momentum(带动量的 SGD)
特点:
-
会记录之前的方向
-
前后方向一致时,会“加速”
比喻:
Momentum 就像下坡时你顺着惯性冲下去。
Adam(深度学习最常用)
Adam = Momentum + RMSProp(缩放梯度)
特点:
-
会根据不同参数的梯度大小,自动调节学习率
-
收敛速度快,效果普遍好
-
对新手非常友好
比喻:
Adam 像人工智能导航,不仅看坡度,还会自动调整步长。
什么时候用哪一个?
| 任务 | 用哪个? |
|---|---|
| 新手训练一般模型 | Adam(最稳) |
| 训练 CNN | Adam 或 SGD |
| 大规模模型(ResNet/Transformer) | AdamW(Adam + L2) |
| 理论研究 / 保守优化 | SGD |
问题10:PyTorch 是如何存储和更新梯度的?
每个参数都有一个 “param.grad” 属性
当你写:
loss.backward()
之后:
print(w.grad)
就能看到它的梯度。
optimizer.step() 根据 param.grad 更新参数
例如 Adam 的一步更新做了这些事:
-
收集所有 parameters
-
获取每个 param.grad
-
用 Adam 公式更新
-
写回 param.data(新的参数值)
问题11:torch.tensor 是什么?
1.简单来说:
torch.tensor() 用来创建一个 PyTorch 张量(Tensor)。
Tensor 是 PyTorch 世界里的“数据容器”。 就像 Python 的 list / NumPy 的 array,但更强,因为它支持:
-
GPU 加速(CUDA)
-
自动求导(autograd)
-
高维矩阵计算
-
神经网络训练
例子:
import torch x = torch.tensor([1, 2, 3])
输出:
tensor([1, 2, 3])
-
常见用法(最重要)
(1)从列表创建张量
x = torch.tensor([1, 2, 3])
注意:默认类型是 int64 或 float32(取决于输入)。
(2)创建浮点数张量
神经网络训练一般需要浮点数!
x = torch.tensor([1, 2, 3], dtype=torch.float32)
(3)创建多维张量
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
这是一个 2×3 矩阵。
(4)创建需要梯度的张量
x = torch.tensor([1.0, 2.0], requires_grad=True)
这意味着:
-
PyTorch 会记录它的操作
-
你可以反向传播计算梯度
(5)指定设备(CPU / GPU)
x = torch.tensor([1,2,3], device="cuda") # 创建在 GPU
-
tensor 的常用属性
创建一个 tensor:
x = torch.tensor([[1., 2.], [3., 4.]])
| 属性 | 含义 |
|---|---|
x.dtype |
数据类型(float32、int64…) |
x.device |
CPU 还是 GPU |
x.shape |
张量的维度 |
x.requires_grad |
是否需要梯度 |
x.grad |
梯度(backward 后会出现) |
-
和其他创建 Tensor 的函数有什么区别?
difference 1:torch.tensor vs torch.Tensor
torch.tensor() # 推荐! torch.Tensor() # 不推荐,新手容易出错
为什么不推荐 torch.Tensor()?
因为:
-
它默认创建 未初始化的浮点数(垃圾值)
-
容易导致奇怪 bug
a = torch.Tensor(2,3) print(a)

你会看到随机垃圾值。
difference 2:torch.tensor vs torch.randn / zeros / ones
torch.tensor(data)
根据你的数据值创建张量
torch.randn(size)
创建一个 服从正态分布的随机数张量
torch.zeros(size)
全部为 0 的张量
torch.ones(size)
全部为 1 的张量
总结表格
| 函数 | 创建方式 |
|---|---|
torch.tensor |
用指定的数据创建 |
torch.randn |
随机数(正态分布) |
torch.rand |
随机数(0-1) |
torch.zeros |
全 0 |
torch.ones |
全 1 |
torch.Tensor |
未初始化的随机垃圾值(尽量不要用) |
5.常见坑(一定要注意)
坑 1:数字是 int,结果 tensor 是 int
x = torch.tensor([1, 2, 3]) print(x.dtype)
得到 torch.int64
但深度学习需要 float32,所以你要写:
x = torch.tensor([1, 2, 3], dtype=torch.float32)
坑 2:用 torch.Tensor() 误创建垃圾值
不要这样:
x = torch.Tensor(2,3)
这样创建的张量内容未初始化,里面是随机垃圾! 应该用:
x = torch.zeros(2,3)
或
x = torch.randn(2,3)
问题12:Tensor的维度是什么?
tensor—张量其实就是一个多维数组
它的维度决定了它的结构。
0维——标量——>说人话就是一个数字
tensor(5)

1维——向量
tensor([1,2,3])

2维——矩阵
tensor([[1, 2, 3], [4, 5, 6]])
shape = (2, 3) 表示 2 行 3 列。

3维——多矩阵堆叠
tensor([ [[...]], [[...]], [[...]] ])
比如一个形状 (3, 2, 4) 的张量可以理解成:
-
有 3 个矩阵
-
每个矩阵 2 行 4 列

4维——图像数据常用
PyTorch 图像一般用:
(batch_size, channels, height, width)
例如:
(32, 3, 224, 224)
表示:
-
32 张图片
-
每张 3 通道 (RGB)
-
尺寸 224×224
如何查看 Tensor 维度?
x.shape x.size()

问题13:Tensor 如何在 GPU 上移动?
PyTorch 的 Tensor 可以放在:
-
CPU(默认)
-
GPU(CUDA)
-
多块 GPU
为什么要放 GPU?
因为 GPU 并行能力强,训练快数十倍。
把 Tensor 放到 GPU 上
x = torch.tensor([1,2,3], device="cuda")
或者:
x = torch.tensor([1,2,3])
x = x.to("cuda") # 常用
把 Tensor 移回 CPU
x = x.to("cpu")
检查设备在哪
x.device

模型也要放在 GPU
model = model.to("cuda")
否则:
-
模型在 GPU
-
数据在 CPU 会报错。
所以深度学习常见套路:
device = "cuda" if torch.cuda.is_available() else "cpu" model = model.to(device) x = x.to(device) y = y.to(device)
注意:Tensor 只能在同一个设备上计算
CPU tensor + GPU tensor 会报错
必须:
都在 CPU 或 都在 GPU
插曲:【这个插曲有点长】
“jupyter notebook里面无法用 cuda吗?”
其实是可以的,我们应该要明白Pytorch有CPU版本喝GPU版本,默认安装是CPU版本。
然后如果当前的虚拟环境没有安装GPU版本的Pytorch是肯定没法使用的。
因为我这里使用的Jupyter Notebook,所以更应该要去看一下他这里启动的python虚拟环境是哪个?
import sys print(sys.executable)

我已经知道了在这个环境中,我没有安装GPU版的!
其实,准确的说这里都应该是Conda环境,因为我是用Anaconda提供的Anaconda Powershell Prompt 终端去打开的jupyter~
你可以从路径中看出:
-
当前 python.exe 属于哪个 conda 环境
-
或者 pip 环境路径
例如:
-
如果路径里有
envs/myenv→ 你正在用 conda 的 myenv 环境 -
如果是
anaconda3/python.exe→ 用的是 base 环境 -
如果是普通路径 → pip 环境
小问题:什么是pip环境?
pip 是 Python 的官方包管理工具,即 Python 的安装器。
pip 环境 = 一个只用 pip 来管理包的 Python 环境。
Python 安装后,会有一个默认环境:
C:\Python310\
里面包含:
-
python.exe(解释器)
-
pip(包管理工具)
-
已安装的库(torch、numpy 等)
所有你安装的包,pip 都会放在:
site-packages/
这个环境就是:
pip 的默认 Python 环境,也叫 pip 环境。
为什么它叫“pip 环境”?
因为:
-
它不是 conda 创建的环境
-
只能用 pip 安装包
-
所有包共享在一个目录中
所以人们就习惯叫它“pip 环境”。
你平时在命令行输入:
pip install xxx
安装的包一般就进了这个环境。
pip 环境 vs conda 环境(你一定会遇到)
| 对比点 | pip 环境 | conda 环境 |
|---|---|---|
| 管理工具 | pip | conda |
| 可以创建多个独立环境? | ❌ 不行(默认只有一个) | ✔ 可以 |
| DLL / C 库管理 | ❌ 不行 | ✔ 处理更好 |
| CUDA 兼容性 | 不可靠 | ✔ 最安全 |
| 是否适合深度学习 | 可以,但容易冲突 | ✔ 最推荐 |
| 环境隔离 | 差 | 很好 |
pip 环境的最大问题:所有包都混在一起
pip 默认只有一个环境,所有库都装进同一个地方:
torch、tensorboard、opencv ... 都堆在一起
如果你装了错误版本的库,就会冲突。
比如:
pip install torch==2.2 pip install torch==1.9
会直接把旧的覆盖! 这就是 pip 环境不可靠的原因。
那 pip 环境适合什么时候用?
适合:
-
学习 Python 基础
-
写小脚本
-
安装简单库(numpy、pandas、matplotlib)
conda环境的优势——conda 环境:可以创建多个独立环境,每个环境互相隔离,是深度学习的最佳选择。
很多人能装好环境,但 Jupyter Notebook 却一直用不了 CUDA,就是因为没有给该环境安装 kernel。
Kernel = Jupyter Notebook 使用的 Python 解释器。
Notebook 自己不带 Python。 它必须靠某一个“环境”里的 python.exe 才能运行你的代码。
所以每次打开 Notebook,你右上角看到的:
Python 3 (myenv) Python 3 (base) Python 3 (cpu) Python 3 (gpu)
这些就是 kernels。

每个 kernel 对应着:
-
一个 Python 环境
-
一套独立的包
-
自己的 CUDA / PyTorch / NumPy / Scipy 等
如何查看自己有哪些环境?
conda env list

就因为这里默认选中的是base,所以直接打开jupyter的时候才会直接用base版的pyhton解释器
我们要进入需要的环境:
conda activate 环境名


为什么切换环境之后,就无法打开jupyter了呢?
原因:这个环境里根本没有安装 Jupyter。
为什么 base 环境能打开 Jupyter?
因为 Anaconda 默认只在 base 环境安装了 jupyter / jupyterlab, 当你切换到别的环境(比如 pytorch12.8)后:
-
如果这个环境没有安装 jupyter
-
那命令行输入
jupyter notebook当然会提示 “无法识别 jupyter”
就像你在一个新的房间(环境)里, 你没有放“jupyter.exe”进去, 自然就打不开。
解决方法非常简单:在当前环境安装 Jupyter
你现在在环境 pytorch12.8 中(从提示符可以看出来):
(pytorch12.8) PS D:\PY\PythonNotebook>
所以你只需要运行:
在当前环境安装 Jupyter
(推荐):
conda install jupyter
安装完成后,再运行:
jupyter notebook
就能正常打开了。
但是,注意更正确的方式:
通常我们 不直接在每个环境里装 Jupyter 而是在 base 环境运行 Jupyter, 然后给其他环境安装 kernel。
为什么要在当前环境安装 kernel?
因为:安装 kernel = 把当前 Python 环境注册给 Jupyter,让它知道并可以选择这个环境运行代码。
例如:
你在 myenv 环境中运行:
pip install ipykernel python -m ipykernel install --user --name myenv --display-name "Python (myenv)"
你就告诉 Jupyter:
“喂,我有一个叫 myenv 的 Python 环境,请你记住它。”
然后你就能在 Notebook 里点:
Kernel → Change kernel → Python (myenv)
一旦切换:
-
Notebook 就使用 myenv 的 python
-
就可以使用 myenv 里的 PyTorch GPU 版本
-
就可以访问 myenv 里的所有包
一句话总结(你一定要记住)
Jupyter 是“外壳”,kernel 是“发动机”。
安装 kernel = 告诉 Jupyter 我这个环境可以作为发动机来跑你的代码。
没有 kernel,Jupyter 就永远不知道你的环境存在。
流程如下👇
推荐的正确做法(最专业最稳)
Step 1:激活你的 GPU 环境
conda activate pytorch12.8
Step 2:安装 kernel(不是 Jupyter)
pip install ipykernel python -m ipykernel install --user --name pytorch12.8 --display-name "Python (pytorch12.8)"
这一步告诉 Jupyter:
我这个环境也可以作为 kernel 来用。
Step 3:回到 base 环境启动 jupyter
conda activate base jupyter notebook
Step 4:在 Jupyter 右上角选择:
Kernel → Change Kernel → Python (pytorch12.8)
这样 Jupyter 就能用 GPU 环境运行代码,但本体仍然用 base 启动。 这是最干净、最推荐的方式。

这样就能看到jupyter里已经多了 ”Pytorch12.8“这个新环境了,这个环境中我是安装了GPU版的Pytorch的。
但是上面图片是新建文件用的,我们想要打开之前写好的文件,使用新的环境就得看下面的图片


然后就切换过来了!

然后我就能看到cuda是可用的啦~~~

不过为什么cuda版的,会多打印一个index=0呢?
因为 CPU 只有 1 个,但 CUDA 可能有多个 GPU
你的电脑只有一个 CPU,所以 PyTorch 写成:
device(type='cpu')
就足够描述这个设备了。
但 GPU 不一样:
一台电脑可能有:
-
一张 GPU:cuda:0
-
两张 GPU:cuda:0、cuda:1
-
八张 GPU:cuda:0 ~ cuda:7
-
集群里甚至几十张
所以 PyTorch 必须明确告诉你:
device(type='cuda', index=0)
表示:
-
CUDA 设备
-
第 0 号 GPU(也叫 GPU0)
什么是 index=0?
就是 GPU 的编号(device id)。
如果你有多个 GPU,可以指定:
x = torch.tensor([1,2,3], device="cuda:1")
打印:
device(type='cuda', index=1)
说明它被放到了第二张显卡上。
GPU就是显卡吗? ——也不能这么绝对的说其实!——GPU(图形处理器)
在深度学习、PyTorch、CUDA 语境下,GPU 就是指“显卡”(准确说是显卡上的图形处理芯片)。
当我们说:
-
“用 GPU 训练”
-
“CUDA 运行在 GPU 上”
-
“把 tensor 放到 cuda:0”
这里的 GPU 就是显卡,尤其是:
NVIDIA 显卡(唯一支持 CUDA,加速 PyTorch 的显卡)
常见型号:
-
RTX 3060 / 3070 / 3080 / 3090
-
RTX 4060 / 4070 / 4080 / 4090
-
Tesla V100 / A100
-
RTX A6000 -等等
这些都算 GPU。
但更准确地说:GPU 是显卡的“大脑”
显卡其实是一个硬件卡片,由:
-
GPU 芯片(核心,大脑)
-
显存(VRAM)
-
供电部分
-
散热部分
-
PCB 板
组成。
而 CUDA 训练真正使用的是:
显卡上的 GPU 芯片(计算单元) 和显卡上的显存(VRAM)
问题14:pd.read_csv('temps.csv')怎么看?
pd.read_csv('temps.csv') 是 pandas 中最常见、最核心的一个函数。
-
pd.read_csv('temps.csv')是什么?
这是 pandas 用来 读取 CSV 文件 的函数。
什么是csv文件?——Comma-Separated Values(逗号分隔值)可以把它看成“轻量版 Excel”。
CSV 文件长什么样?
例如 temps.csv 可能长这样:
date,temp 2023-01-01,12 2023-01-02,15 2023-01-03,10
第一行通常是表头(列名):
date,temp
后面是每一行数据:
2023-01-01,12
CSV 文件其实就是普通文本文件
CSV 为什么在机器学习中最常用?
因为:
-
体积小
-
格式简单
-
各种语言都能轻松读写
-
和 pandas / numpy 完全兼容
-
不会有复杂格式(样式、表格结构)干扰数据
简单、纯粹、直接。
pandas 会把它读成一个表格形式:
| date | temp |
|---|---|
| 2023-01-01 | 12 |
| 2023-01-02 | 15 |
| 2023-01-03 | 10 |
并存成 DataFrame。
read_csv() 返回什么?——返回一个 DataFrame。
DataFrame 是 pandas 中的“表格数据结构”,就像 Excel 表格一样。
4.read_csv() 功能非常强大(重要参数)
常用参数:
指定分隔符(默认逗号)
pd.read_csv('file.csv', sep=';')
指定编码
有些中文 CSV 需要:
pd.read_csv('file.csv', encoding='utf-8')
pd.read_csv('file.csv', encoding='gbk')
指定列名
文件没有表头:
12,3 15,2 17,8
你可以自己给列名:
pd.read_csv('file.csv', names=['temp','count'])
只读某些列
pd.read_csv('file.csv', usecols=['temp'])
解析日期
pd.read_csv('temps.csv', parse_dates=['date'])
features = pd.read_csv('temps.csv')
features.head()
这是 DataFrame 的函数。
.head() 作用:
显示前 5 行数据(默认)
DataFrame 的维度 = 样本数 × 特征数
在机器学习里:
-
行数 = 数据量(samples)
-
列数 = 特征维度(features)
这是非常关键的,因为训练模型时:
-
X 的 shape 是 (样本数, 特征数)
-
y 的 shape 是 (样本数,)
y.shape = (3,) 为什么不写成y.shape = (3)
(3,) 和 (3) 是两种不同的东西
(3,) —— 一个只包含一个元素的元组(tuple)
它表示:
一个 1 维的结构,长度是 3——只有一条“数字的直线队列”,里面有 3 个数字。
这正是 y 的 shape(形状)。形状必须是 tuple
例子:
[10, 20, 30]
更多推荐
所有评论(0)