知识点复习

一、PyTorch 张量(Tensor)基础

1. 张量创建方式
函数 说明
torch.ones(rows, cols) 全1张量
torch.zeros(rows, cols) 全0张量
torch.rand(rows, cols) 均匀分布 [0,1) 随机数
torch.randn(rows, cols) 标准正态分布(均值0,方差1)随机数
torch.arange(start, end) 生成一维整数序列,不包含 end
tensor.reshape(r, c) 改变张量形状,元素总数必须一致
2. 张量切片与索引
  • 格式:tensor[起始行:结束行, 起始列:结束列]

  • 遵循 左闭右开 规则,例如 [2:6, 2:6] 取行索引2~5,列索引2~5

  • 示例:16×16图像的中心4×4区域 → [6:10, 6:10]

3. 张量拼接:torch.cat()
  • torch.cat([A, B], dim=0) → 垂直拼接(增加行数),要求列数相同

  • torch.cat([A, B], dim=1) → 水平拼接(增加列数),要求行数相同

4. 广播机制(Broadcasting)
  • 规则:从最后一位维度开始向前对齐,每个对应维度满足以下条件之一即可:

    • 相等

    • 其中一个为1

    • 其中一个缺失(视为1)

  • 结果形状:取各维度最大值

  • 示例

    • (3,1) + (1,4) → (3,4)

    • (5,4) + (5,1) → (5,4)

    • (3,4) + (2,4) → ❌ 不满足(3≠2且都不是1)


二、自动微分(Autograd)

1. 基本概念
  • requires_grad=True:张量需要被记录梯度

  • loss.backward():反向传播计算梯度,累积到 .grad 属性中

  • tensor.grad:存储计算得到的梯度

2. 梯度停止方法
方法 特点 适用场景
tensor.detach() 返回共享数据的新张量,requires_grad=False,不影响原张量 从计算图中取出数值,但不需要梯度回传(如强化学习目标网络更新)
torch.no_grad() 上下文管理器或装饰器,内部所有操作不跟踪梯度 测试/评估阶段,或手动更新参数(如梯度下降更新)
3. 梯度下降优化步骤
x = torch.tensor(3.0, requires_grad=True)
lr = 0.1
for _ in range(steps):
    loss = f(x)          # 1. 计算损失
    loss.backward()      # 2. 计算梯度
    with torch.no_grad():
        x -= lr * x.grad # 3. 更新参数(不记录梯度)
    x.grad.zero_()       # 4. 梯度清零(防止累积)

简单说:.grad 是“梯度的存放处”,是自动微分和优化之间的关键桥梁。

  • 必须清零梯度:否则下次 backward() 会在原梯度上累加,导致错误

  • 手动更新时使用 with torch.no_grad(): 避免构建不必要的计算图


    .grad 是 PyTorch 中张量的一个属性,用于存储该张量的梯度值

    具体作用:

  • 当您对某个张量设置了 requires_grad=True,并参与运算后调用 backward(),PyTorch 会自动计算该张量相对于最终标量结果的梯度,并将其累加到 .grad 属性中。

  • 在梯度下降优化中,通过 x.grad 获取梯度,然后手动或通过优化器更新参数(例如 x -= learning_rate * x.grad)。

  • 每次反向传播后,梯度会累加,因此通常需要在更新参数后调用 x.grad.zero_() 将梯度清零,避免下一次累加旧梯度。


三、可视化

  • plt.imshow(tensor.numpy(), cmap='gray'/'viridis'):显示图像或热力图

  • plt.subplots() 创建多子图布局


考题:张量操作与自动微分

出题人说明:本套考题基于实验1的知识点,涵盖张量创建、切片拼接、广播机制、自动微分及梯度下降优化等内容。题型包括单选题、填空题、判断题、简答题和代码填空题,总分100分。建议完成时间60分钟。


一、单选题(每题3分,共15分)

  1. 以下关于 torch.rand(4, 4) 和 torch.randn(4, 4) 的说法,正确的是( )
    A. 两者都生成服从标准正态分布的随机数
    B. torch.rand 生成均匀分布,torch.randn 生成标准正态分布
    C. torch.rand 生成标准正态分布,torch.randn 生成均匀分布
    D. 两者生成的都是 [0,1) 均匀分布的随机数

  2. 有一个形状为 (16, 16) 的张量 img,想要裁剪出中心 4×4 的区域,下列切片写法正确的是( )
    A. img[4:8, 4:8]
    B. img[5:9, 5:9]
    C. img[6:10, 6:10]
    D. img[7:11, 7:11]

  3. 关于广播机制,下列哪一组形状的两个张量无法进行广播运算?( )
    A. (3, 1) 和 (1, 4)
    B. (2, 3, 4) 和 (4,)
    C. (3, 4) 和 (2, 4)
    D. (5, 1, 6) 和 (1, 5, 1)

  4. 以下代码中,y.requires_grad 的值是( )

    x = torch.tensor(2.0, requires_grad=True)
    with torch.no_grad():
        y = x ** 2

    A. True
    B. False
    C. None
    D. 运行时报错

  5. 在梯度下降优化中,每次更新参数后必须执行的操作是( )
    A. loss.backward()
    B. optimizer.step()
    C. 梯度清零(如 x.grad.zero_()
    D. 重新计算损失函数


二、填空题(每空1分,共12分)

  1. 使用 torch.arange(0, 256).reshape(16, 16) 创建渐变图像,arange 生成的元素个数是 ______,reshape 后张量的形状是 ______。若想改为 32×8 的图像,应写为 torch.arange(0, 256).reshape(______, ______)

  2. 将两个形状分别为 (8, 8) 的张量 A 和 B 进行左右拼接(增加列数),应使用 torch.cat([A, B], dim=______),拼接后的形状是 ______。

  3. 对于形状 (3, 1) 的列向量和形状 (1, 4) 的行向量,广播相加后的结果形状是 ______。

  4. 在自动微分中,若要计算 f = x**3 + 2*x 在 x=2.0 处的导数,需要设置 x.requires_grad=______,然后调用 f.______(),最后通过 x.______ 获取导数值。

  5. 使用 detach() 方法会返回一个 ______ (填“共享数据”或“深拷贝”)且 ______ (填“记录”或“不记录”)梯度的新张量。


三、判断题(正确打“√”,错误打“×”,每题2分,共8分)

  1. ( )torch.ones(4, 4) 与 torch.zeros(4, 4) 相加得到的结果张量仍然会记录梯度(假设它们原本没有 requires_grad=True)。

  2. ( )形状为 (3, 4) 和形状为 (4, 3) 的两个张量可以进行广播相加。

  3. ( )在 with torch.no_grad(): 块内创建的张量,其 requires_grad 始终为 False

  4. ( )对于函数 f(x)=x⁴-4x²+5,使用梯度下降法寻找最小值时,学习率越大收敛越快,且不会发散。


四、简答题(共29分)

简答题1(5分,实验2.1节思考题)

(1)torch.rand 和 torch.randn 生成的随机数有什么不同?
(2)torch.arange(0, 256) 生成的序列有多少个元素?为什么刚好能 reshape 成 16×16?
(3)如果想把渐变图像变成 32×8,代码应该怎么改?

简答题2(5分,实验2.2节思考题)

(1)16×16 的图像,中心 4×4 区域的起始索引为什么是 6 和 6?
(2)切片操作 [2:6, 2:6] 取出的是第几行到第几行?第几列到第几列?
(3)dim=0 和 dim=1 在拼接时分别对应什么方向?
(4)如果两个张量形状不匹配,cat 会报什么错?

简答题3(6分,实验2.3节思考题)

判断以下形状对能否进行广播。如果可以,写出广播后的形状;如果不可以,写出理由。
① (5,4) 和 (5,1)
② (3,2) 和 (2,)
③ (2,3,4) 和 (4,)
④ (2,3,4) 和 (3,4)
⑤ (3,4,5) 和 (4,5)
⑥ (3,4) 和 (2,4)

简答题4(5分,实验3.1节思考题)

(1)在自动微分中,如果张量的 requires_grad=False 会发生什么?
(2)对于一元函数 f(x)=x³+2x²+3x+4,若把 x=2.0 改成 x=3.0x.grad 的值会如何变化?
(3)手动写出函数 f(x,y)=x²y+2xy² 的偏导公式 ∂f/∂x 和 ∂f/∂y,并验证当 x=2, y=3 时程序输出的结果是否正确。

简答题5(4分,实验3.2节思考题)

(1)detach() 和 torch.no_grad() 有什么区别?
(2)请列举至少两个需要停止梯度追踪的典型场景。

简答题6(4分,实验3.3节思考题)

(1)函数 f(x)=x⁴-4x²+5 的最小值点在哪里?(提示:求导解方程)
(2)为什么在梯度下降更新参数时要放在 with torch.no_grad(): 里?
(3)如果注释掉梯度清零的代码(即去掉 x.grad.zero_()),重新运行,会发生什么?为什么?


五、代码填空题(每空3分,共36分)

  1. 张量操作(补全代码)

    import torch
    # 创建均匀分布随机张量 4×4
    rand_tensor = torch.______(4, 4)
    # 创建正态分布随机张量 4×4
    randn_tensor = torch.______(4, 4)
    
    # 渐变图像:0~255 并 reshape 为 16×16
    gradient = torch.arange(0, 256).reshape(______, ______)
    
    # 裁剪左上角 8×8
    crop_tl = gradient[______, ______]
    # 裁剪右下角 8×8(已给出)
    crop_br = gradient[8:16, 8:16]
    
    # 上下拼接(增加行数)
    concat_v = torch.cat([crop_tl, crop_br], dim=______)
    # 左右拼接(增加列数)
    concat_h = torch.cat([crop_tl, crop_br], dim=______)
  2. 自动微分与梯度下降(补全代码)

    x = torch.tensor(3.0, requires_grad=______)   # 开启梯度
    
    learning_rate = 0.1
    steps = 50
    
    for i in range(steps):
        f = x**4 - 4*x**2 + 5
        f.______()   # 反向传播计算梯度
        
        with torch.______():   # 不记录梯度上下文
            x -= learning_rate * x.grad
        
        x.______.______()   # 清零梯度(两个点号填空)
        
        if i % 10 == 0:
            print(f"第{i}步: x={x.item():.4f}, f={f.item():.4f}")

参考答案与详细解析

一、单选题

  1. 答案:B
    解析torch.rand 生成 [0,1) 均匀分布的随机数;torch.randn 生成均值为0、方差为1的标准正态分布随机数。

  2. 答案:C
    解析:16×16 图像的中心区域,起始索引 = (16-4)//2 = 6,结束索引 = 6+4 = 10,因此为 [6:10, 6:10]

  3. 答案:C
    解析:广播规则:从后向前对齐维度,要求每个对应维度要么相等,要么其中一个为1,要么缺失。C项 (3,4) 与 (2,4):第一个维度 3 ≠ 2 且均不为1,因此无法广播。

  4. 答案:B
    解析torch.no_grad() 上下文内所有操作都不会追踪梯度,因此 y.requires_grad 为 False

  5. 答案:C
    解析:梯度下降更新参数后,必须将梯度清零,否则下次反向传播会累加旧梯度。通常使用 x.grad.zero_() 或优化器的 zero_grad()


二、填空题

  1. 答案:256;(16, 16);32;8
    解析arange(0,256) 生成0~255共256个整数。reshape(32,8) 后行数为32,列数为8,32×8=256。

  2. 答案:1;(8, 16)
    解析:左右拼接增加列数,对应 dim=1(第二维)。两个 (8,8) 沿 dim=1 拼接后为 (8, 8+8) = (8,16)

  3. 答案:(3, 4)
    解析(3,1) 与 (1,4) 广播:第一维取3,第二维取4,结果为 (3,4)

  4. 答案:True;backward;grad
    解析:标准自动微分流程。

  5. 答案:共享数据;不记录
    解析detach() 返回与原始张量共享内存的新张量,但 requires_grad=False,不参与计算图。


三、判断题

  1. 答案:×
    解析ones 和 zeros 默认 requires_grad=False,相加结果也不记录梯度。若要记录梯度需显式设置。

  2. 答案:×
    解析:广播要求从后向前对齐维度,(3,4) 与 (4,3) 最后一维 4≠3 且均不为1,无法广播。

  3. 答案:√
    解析torch.no_grad() 内部所有操作生成的张量 requires_grad=False

  4. 答案:×
    解析:学习率过大可能导致震荡甚至发散,无法收敛到最小值。


四、简答题

简答题1(5分)

(1)torch.rand 生成 [0,1) 均匀分布随机数;torch.randn 生成均值为0、方差为1的标准正态分布随机数。
(2)256个元素;因为 16×16 = 256,元素总数与reshape目标形状一致。
(3)torch.arange(0,256).reshape(32, 8)

简答题2(5分)

(1)中心起始索引 = (16-4)//2 = 6,所以行和列都从6开始。
(2)第2行到第5行(行索引2,3,4,5),第2列到第5列(列索引2,3,4,5)。
(3)dim=0 对应垂直方向(增加行数),dim=1 对应水平方向(增加列数)。
(4)报错:RuntimeError: Sizes of tensors must match except in dimension ...(形状不匹配错误)。

简答题3(6分)

① 能,(5,4)
② 能,(3,2) ((2,) 补为 (1,2)
③ 能,(2,3,4) ((4,) 补为 (1,1,4)
④ 能,(2,3,4) ((3,4) 补为 (1,3,4)
⑤ 能,(3,4,5) ((4,5) 补为 (1,4,5)
⑥ 不能,因为第一维 3 ≠ 2 且均不为1,无法对齐。

简答题4(5分)

(1)该张量不会参与梯度计算,其 .grad 为 None,反向传播时不会对其求导。
(2)x.grad 会从 23 变为 3×3² + 4×3 + 3 = 27+12+3=42(具体值:f'(3)=3*9+4*3+3=27+12+3=42)。
(3)∂f/∂x = 2xy + 2y²,∂f/∂y = x² + 4xy。代入 x=2,y=3:∂f/∂x = 2×2×3 + 2×9 = 12+18=30,∂f/∂y = 4 + 4×2×3 = 4+24=28,与程序输出一致。

简答题5(4分)

(1)区别:

  • detach() 是张量方法,返回一个共享数据但 requires_grad=False 的新张量,不影响原张量的计算图。

  • torch.no_grad() 是上下文管理器,块内所有操作均不追踪梯度,退出后恢复。
    (2)场景:① 模型评估/测试时,不需要计算梯度以节省内存和计算;② 在强化学习中,从当前网络复制目标网络的值但不希望梯度回传;③ 手动更新参数时(如梯度下降中的 x -= lr * x.grad)。

简答题6(4分)

(1)最小值点:令 f'(x)=4x³-8x=4x(x²-2)=0,得 x=0, ±√2。二阶导 f''(x)=12x²-8,在 x=±√2 处 f''=16>0,故为极小值点,最小值 f(±√2)=4-8+5=1x=0 为极大值点(f''=-8<0)。
(2)因为参数更新操作不需要构建计算图,且如果放在计算图内会导致不必要的内存消耗和梯度追踪错误。
(3)梯度会不断累积(每步梯度累加),导致参数更新幅度越来越大,最终发散或无法收敛到最小值。


五、代码填空题

  1. 答案

    rand_tensor = torch.rand(4, 4)
    randn_tensor = torch.randn(4, 4)
    gradient = torch.arange(0, 256).reshape(16, 16)
    crop_tl = gradient[0:8, 0:8]
    concat_v = torch.cat([crop_tl, crop_br], dim=0)
    concat_h = torch.cat([crop_tl, crop_br], dim=1)

    解析[0:8, 0:8] 取左上角8行8列;上下拼接 dim=0,左右拼接 dim=1

  2. 答案

    x = torch.tensor(3.0, requires_grad=True)
    f = x**2  # 前向计算
    f.backward()  # 反向传播
    print(x.grad)  # 输出梯度值
    with torch.no_grad():
        x.grad.zero_()  # 梯度清零
     
    

    解析requires_grad=Truebackward() 计算梯度;no_grad() 更新参数;梯度清零使用 x.grad.zero_(),注意两个点号:先访问 grad 属性再调用 zero_() 方法。

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐