【深度学习|Day01】PyTorch 深度学习笔记(上):框架认知与张量基础

文章目录

  • [PyTorch 深度学习笔记(上):框架认知与张量基础](#PyTorch 深度学习笔记(上):框架认知与张量基础)
  • [1. PyTorch 概述](#1. PyTorch 概述)
    • [1.1 什么是 PyTorch](#1.1 什么是 PyTorch)
    • [1.2 PyTorch vs TensorFlow](#1.2 PyTorch vs TensorFlow)
    • [1.3 PyTorch 的核心特点](#1.3 PyTorch 的核心特点)
  • [2. 张量概述](#2. 张量概述)
    • [2.1 什么是张量](#2.1 什么是张量)
    • [2.2 张量与 PyTorch 的关系](#2.2 张量与 PyTorch 的关系)
    • [2.3 PyTorch 张量 vs NumPy 数组](#2.3 PyTorch 张量 vs NumPy 数组)
    • [2.4 张量的数据类型](#2.4 张量的数据类型)
    • [2.5 张量的核心属性](#2.5 张量的核心属性)
  • [3. 张量的创建](#3. 张量的创建)
    • [3.1 根据已有数据创建:torch.tensor()](#3.1 根据已有数据创建:torch.tensor())
    • [3.2 根据形状创建:torch.Tensor()](#3.2 根据形状创建:torch.Tensor())
    • [3.3 创建指定类型的张量:torch.XXXTensor()](#3.3 创建指定类型的张量:torch.XXXTensor())
    • [3.4 创建线性张量](#3.4 创建线性张量)
      • [3.4.1 torch.arange():按步长生成](#3.4.1 torch.arange():按步长生成)
      • [3.4.2 torch.linspace():按元素个数生成](#3.4.2 torch.linspace():按元素个数生成)
    • [3.5 创建随机张量](#3.5 创建随机张量)
      • [3.5.1 torch.rand():均匀分布](#3.5.1 torch.rand():均匀分布)
      • [3.5.2 torch.randn():标准正态分布](#3.5.2 torch.randn():标准正态分布)
      • [3.5.3 torch.randint():离散整数随机](#3.5.3 torch.randint():离散整数随机)
      • [3.5.4 随机种子](#3.5.4 随机种子)
    • [3.6 创建指定值张量](#3.6 创建指定值张量)
      • [3.6.1 全零张量](#3.6.1 全零张量)
      • [3.6.2 全一张量](#3.6.2 全一张量)
      • [3.6.3 全指定值张量](#3.6.3 全指定值张量)
  • [4. 张量与 NumPy 互转](#4. 张量与 NumPy 互转)
      • [4.1 张量 → NumPy](#4.1 张量 → NumPy)
      • [4.2 NumPy → 张量](#4.2 NumPy → 张量)
      • [4.3 为什么带梯度的张量不能直接 .numpy()?](#4.3 为什么带梯度的张量不能直接 .numpy()?)

PyTorch 深度学习笔记(上):框架认知与张量基础


1. PyTorch 概述

1.1 什么是 PyTorch

PyTorch 是一个基于 Python 的开源深度学习框架,由 Facebook AI Research(FAIR)团队主导开发。它的核心设计思路可以概括为一句话:将数据封装为张量(Tensor),在动态构建的计算图上执行自动微分,从而高效完成神经网络模型的训练与推理。

理解这个定义,可以拆成三层:

层次 做什么 对应模块
数据层 将数值数据统一封装为张量,支持 CPU 和 GPU 两种设备 torch.Tensor
计算层 基于动态计算图自动追踪每一步运算,实现自动求导 torch.autograd
模型层 提供预置的神经网络层、损失函数、优化器,快速搭建模型 torch.nntorch.optim

PyTorch 的主要应用场景包括计算机视觉(CV)、自然语言处理(NLP)、语音识别、强化学习等几乎全部深度学习领域。


1.2 PyTorch vs TensorFlow

两大主流框架的对比如下:

对比维度 PyTorch TensorFlow
计算图模式 动态图(Define-by-Run),代码执行即建图 早期为静态图(先定义后运行),2.x 后引入 Eager Execution 支持动态
代码风格 Pythonic,贴近原生 Python 编程习惯 Keras 为高级接口,底层仍偏声明式
调试体验 可直接使用 printpdb 等 Python 调试工具 2.x 改善很多,1.x 时期调试困难
学术/工业倾向 学术论文首选,近年来工业界快速普及,论文复现多基于 PyTorch 工业部署生态成熟(TF Serving、TF Lite)

动态图是核心差异点

  • 动态图 = 计算图随着代码的执行,逐行即时构建。定义即执行,你写代码的那一刻,计算就发生了。指框架构建计算图这个动作与你的Python代码执行是同步的、实时的、可随代码逻辑变化的,而非预先定义好整个图再喂数据。这带来的直接好处是:你可以像调试普通 Python 程序一样使用 print、breakpoint,极大提升了开发和排错效率。
  • 静态图先构建后执行。先把整个计算图定义好,再启动会话执行。图和代码执行是分离的,图定义阶段没法打印,因为没数据。

1.3 PyTorch 的核心特点

① 动态图计算

是什么:动态图(Define-by-Run)是指计算图在代码实际执行时才被逐步构建,而不是像早期 TensorFlow 1.x 那样先完整定义图结构再喂入数据。

具体到代码层面,动态图意味着:

python 复制代码
import torch

# 每一个运算步骤都在执行时立即构建对应的计算图节点
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x * 2          # 图节点1:乘法
z = y.mean()       # 图节点2:求均值
z.backward()       # 沿动态构建的图反向传播
print(x.grad)      # tensor([0.6667, 0.6667, 0.6667])

为什么重要

  • 调试直观:可以用 print 在任何位置查看中间变量的值,而不需要执行整个图
  • 研究友好:模型结构可以灵活实验,不需要预先声明全部输入输出维度
  • 控制流自然:可以使用 Python 原生的条件判断、循环来控制网络结构。ifforwhile 直接作用于张量,每次迭代可以产生不同形状的图(这对处理变长序列的 RNN 至关重要)

一个体现动态图优势的典型场景:变长输入的处理:

python 复制代码
# 对于变长句子,每个样本长度不同,动态图每次构建不同的计算路径
for sentence in variable_length_sentences:
    emb = embedding(sentence)       # 不同长度的句子产生不同形状的张量
    output = rnn_model(emb)         # 计算图随输入长度动态调整

② 强大的 GPU 加速能力

是什么:PyTorch 中的张量可以存放在两种设备上:CPU(主机内存)和 GPU(显存)。当张量位于 GPU 上时,所有运算都会在 GPU 的数千个计算核心上并行执行,速度远超 CPU。

这里澄清一个概念:不是"张量具有 GPU 加速能力",而是 PyTorch 提供了将张量无缝迁移到 GPU 的能力,并利用 NVIDIA 的 CUDA 平台在 GPU 上执行高效的并行计算。 张量只是一个数据容器,GPU 加速是 PyTorch 框架底层为你做的事情。

为什么需要 GPU 加速 :深度学习模型的训练本质上是海量的矩阵乘法和加法运算。以一个简单的全连接层 y = Wx + b 为例,当 W 是 1000×1000 的矩阵,一次前向传播就需要 100 万次乘法。整个训练过程需要重复数十万甚至百万次这样的运算,CPU 的少数几个核心串行处理根本跑不动,而 GPU 拥有数千个核心可以同时处理。

python 复制代码
import torch

# 创建一个张量,默认在 CPU 上
x_cpu = torch.randn(1000, 1000)

# 检查 CUDA 是否可用,将张量移到 GPU
if torch.cuda.is_available():
    x_gpu = x_cpu.to('cuda')       # 或 x_cpu.cuda()
    # 此时对 x_gpu 的所有运算都在 GPU 上执行
    y = x_gpu @ x_gpu.T             # GPU 上做矩阵乘法,极快

设备切换的核心 API

方法 说明
tensor.to('cuda') / tensor.cuda() 将张量迁移到 GPU
tensor.to('cpu') / tensor.cpu() 将张量迁回 CPU
torch.cuda.is_available() 检查当前环境是否有可用 GPU
tensor.device 查看张量当前所在的设备
torch.cuda.device_count() 获取可用 GPU 数量

③ 完备的神经网络工具箱(torch.nn)

PyTorch 通过 torch.nn 子模块提供了构建神经网络的完整积木集合:

  • 预置网络层 :全连接层(nn.Linear)、卷积层(nn.Conv2d)、循环层(nn.LSTMnn.GRU)、池化层(nn.MaxPool2d)、Dropout(nn.Dropout)、批归一化(nn.BatchNorm2d)等
  • 损失函数 :交叉熵损失(nn.CrossEntropyLoss)、均方误差(nn.MSELoss)、二元交叉熵(nn.BCELoss)等
  • 激活函数:ReLU、Sigmoid、Tanh、GELU、Softmax 等

这意味着你不需要从零实现反向传播或参数更新公式,只需定义好网络结构,前向计算、损失评估、反向传播由框架自动衔接。

python 复制代码
import torch.nn as nn

# 一个简单的三层全连接网络
model = nn.Sequential(
    nn.Linear(784, 256),    # 输入层 → 隐藏层1
    nn.ReLU(),
    nn.Linear(256, 128),    # 隐藏层1 → 隐藏层2
    nn.ReLU(),
    nn.Linear(128, 10),     # 隐藏层2 → 输出层(10分类)
)

④ 自动微分系统(torch.autograd)

这是 PyTorch 最核心的特性,也是深度学习框架区别于 NumPy 等数值计算库的本质所在。

核心能力 :当你设置 requires_grad=True 后,PyTorch 会自动追踪该张量参与的每一次运算,构建出完整的计算图 。当调用 .backward() 时,框架沿计算图自动计算出损失函数对每个参数的偏导数(梯度),无需手动推导和编码反向传播公式。

后续详细展开自动微分的完整工作流程

⑤ 跨平台与分布式支持

PyTorch 支持 CPU、GPU(CUDA)、TPU,可在 Linux、Windows、macOS 上运行,并提供 torch.distributed 模块支持多 GPU 乃至多机分布式训练。


2. 张量概述

2.1 什么是张量

张量(Tensor)是 PyTorch 中最基本的数据结构,它是一个可以包含任意维度的、元素类型相同的多维数组。

简单来说:张量是一个所有元素数据类型相同多维数组。

两个关键约束

  1. 元素类型必须相同 :一个张量中所有元素的数据类型是统一的,不能同时混合 intfloat
  2. 维度不设上限:可以构建 0 维(标量)到任意 N 维的张量

关于数据类型:张量主要支持数值类型(包括整数、浮点数、布尔值),字符串类型在深度学习中极少使用,因为模型参数和输入必须是数值才能进行数学运算。

从数学和计算机科学的角度来看,张量是对标量、向量、矩阵的统称与推广:

复制代码
标量(Scalar)    0 维张量    torch.tensor(3.14)          形状: ()
向量(Vector)    1 维张量    torch.tensor([1, 2, 3])     形状: (3,)
矩阵(Matrix)    2 维张量    torch.tensor([[1,2],[3,4]]) 形状: (2, 2)
三维张量          3 维张量    形状如 (batch, rows, cols)
N 维张量          N 维张量    任意维度,不设上限

2.2 张量与 PyTorch 的关系

这是一个容易混淆的点,需要明确:张量是 PyTorch 框架内定义的一个类(torch.Tensor),它是 PyTorch 提供的数据容器 ,正如 NumPy 数组(numpy.ndarray)是 NumPy 库的核心数据结构。

复制代码
PyTorch 框架
  ├── torch.Tensor       ← 数据容器(张量)
  ├── torch.autograd     ← 自动微分(依赖 Tensor 的梯度追踪)
  ├── torch.nn           ← 神经网络模块(各层接收和返回 Tensor)
  └── torch.optim        ← 优化器(更新 Tensor 中的参数值)

这里有一个重要的观念需要理清:优秀的是 PyTorch 这个框架,而非张量本身。 张量只是 PyTorch 提供的众多工具中的"数据载体"。真正厉害的是 PyTorch 围绕张量构建的一整套基础设施,自动微分引擎、GPU 计算后端、神经网络抽象、优化器体系。张量本身就是一个存数据的容器,但因为它是 PyTorch 生态的一环,所以天然享受了这些能力的加持。

换句话说:

  • 同样一个矩阵,在 NumPy 中就是普通的多维数组,只能做常规数值计算
  • 同样的数据放在 PyTorch 张量中,就可以被自动追踪梯度、搬到 GPU 上并行加速、作为神经网络层的输入输出

2.3 PyTorch 张量 vs NumPy 数组

对比维度 PyTorch Tensor NumPy ndarray
GPU 支持 支持(.to('cuda') 不支持(仅 CPU)
自动微分 支持(requires_grad=True 后自动追踪) 不支持
默认数据类型 float32torch.float32 float64numpy.float64
深度学习集成 无缝对接 nn 模块、DataLoader 等 需手动转换为张量
数学运算 基本运算齐全,专为 DL 优化 科学计算生态更广泛(线性代数、FFT 等)
内存布局 行优先(row-major),与 NumPy 一致 行优先(C 风格)

使用场景

  • 用 PyTorch Tensor:构建和训练深度学习模型时(前向传播、反向传播、参数更新全流程)
  • 用 NumPy ndarray:数据预处理阶段(读取 CSV、图像归一化、特征变换等),处理完再转为张量送入模型

实际工作流往往是:NumPy 预处理 → 转为 PyTorch 张量 → 送入模型训练 → 转回 NumPy 做结果分析/可视化


2.4 张量的数据类型

PyTorch 支持的数据类型及选择建议:

数据类型 dtype 简写 说明 典型场景
torch.float32 torch.float / torch.float32 32 位浮点,默认类型 绝大多数模型训练与推理的首选
torch.float64 torch.double / torch.float64 64 位双精度浮点 高精度科学计算、对梯度精度要求极高的场景
torch.float16 torch.half / torch.float16 16 位半精度浮点 混合精度训练,节省显存和加速计算
torch.int32 torch.int / torch.int32 32 位有符号整数 标签、索引等离散值
torch.int64 torch.long / torch.int64 64 位有符号整数 通常作为类别标签的默认类型
torch.bool torch.bool 布尔类型 掩码、条件筛选
torch.uint8 torch.uint8 8 位无符号整数 图像像素值(0--255)
torch.complex64 torch.complex64 64 位复数 信号处理、量子计算等特殊场景

关于默认类型的说明 :PyTorch 创建张量时默认使用 torch.float32(32 位浮点),而不是 float64。这是深度学习领域的共识选择,原因有二:

  1. 精度足够:32 位浮点提供约 7 位有效数字,对于神经网络的梯度计算和参数更新完全够用
  2. 速度快、省内存:float32 占 4 字节,是 float64(8 字节)的一半。在大规模模型和大批量训练时,内存占用和计算速度的优势非常显著

2.5 张量的核心属性

python 复制代码
# 常用属性
import torch

t = torch.randn(2, 3, dtype=torch.float32)
print(t.shape)      # torch.Size([2, 3]) ------ 每个维度的大小
print(t.dtype)      # torch.float32 ------ 数据类型
print(t.device)     # cpu ------ 存储设备
print(t.requires_grad)  # False ------ 是否需要自动微分
属性 含义 示例值 说明
x.shape / x.size() 张量的形状(各维度大小) torch.Size([2, 3]) shape 是属性访问;size() 是方法调用,效果相同,传入维度索引可获取该维大小
x.dtype 张量中元素的数据类型 torch.float32 可通过 .to(dtype=...) 修改
x.device 张量所在的设备 device(type='cpu') 可通过 .to('cuda') 迁移
x.requires_grad 是否需要计算梯度 False(默认) 设为 True 后 autograd 会追踪该张量的所有运算
x.grad 存储反向传播后的梯度值 None / 同形状张量 初始为 None,调用 .backward() 后被赋值为梯度张量
x.data 返回一个与当前张量共享底层数据但剥离了计算图的张量 x 同值、同形状 用于需要修改张量值但不希望被 autograd 追踪的场景
x.ndim 张量的维度数(秩) 2 等价于 len(x.shape)
x.numel() 张量中元素的总个数 6(= 2×3) numel = number of elements

关于 grad 属性

  • grad 存储在张量自身,但这个梯度值是由 backward() 方法计算并回填的,不是张量主动拥有的功能
  • 只有调用了 .backward() 且该张量的 requires_grad=Truegrad 才有实际值
  • grad 是一个和原张量同形状的张量,存储了损失函数对该张量每个元素的偏导数

关于 data 属性

  • 返回一个与原张量共享底层数据requires_grad=False 的新张量
  • 主要用途:在需要修改张量的数值、但不想让这次修改变动被 autograd 记录时使用。例如在手动梯度更新中:x.data = x.data - lr * x.grad,这样可以更新 x 的值但不会被 autograd 视为一次运算从而追加到计算图中
  • 注意:data 是比较早期的用法,现代代码中更推荐使用 torch.no_grad() 上下文管理器或 .detach() 方法,安全性更好。data 的隐患在于它绕过了 autograd 的追踪,可能导致梯度计算错误而不报错(静默 bug)

3. 张量的创建

PyTorch 提供了丰富的张量创建方式,可以根据已有数据创建,也可以根据形状、规则或概率分布生成。

3.1 根据已有数据创建:torch.tensor()

python 复制代码
torch.tensor(data, dtype=None, device=None, requires_grad=False)

参数说明

参数 含义 默认值 说明
data 用于创建张量的数据 必填 支持 Python 列表、元组、NumPy 数组等可迭代容器
dtype 指定张量数据类型 None(自动推断) 不指定则根据 data 的值自动推断(整数→int64,浮点→float32)
device 指定设备 None(CPU) 可设为 'cuda''cuda:0'
requires_grad 是否追踪梯度 False 训练参数通常设为 True
python 复制代码
import torch
import numpy as np

# 从 Python 列表创建
t1 = torch.tensor([1, 2, 3])                     # 1维,自动推断为 int64
t2 = torch.tensor([[1.0, 2.0], [3.0, 4.0]])     # 2维,自动推断为 float32

# 从 NumPy 数组创建(注意:不共享内存,是拷贝)
arr = np.array([1.0, 2.0, 3.0])
t3 = torch.tensor(arr)                            # 等价于 torch.from_numpy(arr) 的拷贝版本

# 指定数据类型
t4 = torch.tensor([1, 2, 3], dtype=torch.float32)  # 强制转为 float32

# 指定设备和梯度追踪
t5 = torch.tensor([1.0, 2.0], device='cuda', requires_grad=True)

注意torch.tensor() 总是拷贝数据 ,不共享内存。如果想共享内存(零拷贝),用 torch.from_numpy()torch.as_tensor()

data 参数的可接受类型 :不仅限于字面量列表,支持一切实现了 Python 序列协议的可迭代对象(listtuplenumpy.ndarray、甚至另一个张量)。但它不能直接指定形状,形状完全由传入的数据结构决定。


3.2 根据形状创建:torch.Tensor()

python 复制代码
# 注意大写 T!这是类构造函数,行为和小写版本不同
torch.Tensor(*sizes)

torch.Tensor() 是张量类的构造函数,接收形状参数,创建指定形状的张量,元素为未初始化的随机值(内存中的残留数据)。也可以传入已有的可迭代数据来创建张量。

python 复制代码
# 通过形状创建(不推荐直接使用,因为值是未初始化的垃圾数据)
t1 = torch.Tensor(2, 3)        # 形状 (2, 3),内容是随机残留值

# 也可以传入数据(但不常用,torch.tensor() 更常用)
t2 = torch.Tensor([1, 2, 3])   # 从数据创建

3.3 创建指定类型的张量:torch.XXXTensor()

python 复制代码
# 直接通过类型命名的构造函数创建
torch.FloatTensor([1, 2, 3])      # 等同于 torch.tensor([1., 2., 3.]),创建 float32
torch.DoubleTensor([1, 2, 3])     # float64
torch.IntTensor([1, 2, 3])        # int32
torch.LongTensor([1, 2, 3])       # int64
torch.BoolTensor([True, False])   # bool

这些是历史遗留的 API,功能可以用 torch.tensor(data, dtype=torch.float32) 完全覆盖。推荐统一使用 torch.tensor()dtype 参数 ,代码意图更明确,API 更统一。

python 复制代码
# 两种写法等价,推荐第二种
a = torch.FloatTensor([1, 2, 3])
b = torch.tensor([1, 2, 3], dtype=torch.float32)

3.4 创建线性张量

当需要快速生成有规律的一维数值序列时使用。

3.4.1 torch.arange():按步长生成

python 复制代码
torch.arange(start=0, end, step=1, dtype=None)

步长[start, end) 区间(左闭右开)内生成等差数列。

参数 含义 默认值
start 起始值 0
end 终止值(不包含) 必填
step 步长(相邻元素间隔) 1
python 复制代码
torch.arange(0, 10, 2)    # tensor([0, 2, 4, 6, 8])  → 左闭右开,不包含 10
torch.arange(3)            # tensor([0, 1, 2])         → start 默认 0

3.4.2 torch.linspace():按元素个数生成

python 复制代码
torch.linspace(start, end, steps, dtype=None)

元素个数[start, end] 区间(左闭右闭,包含两端)内生成等间隔数列。

参数 含义 说明
start 起始值 必填,包含在结果中
end 终止值 必填,包含在结果中
steps 生成元素个数 必填,决定结果张量的长度
python 复制代码
torch.linspace(0, 10, 5)  # tensor([0.0, 2.5, 5.0, 7.5, 10.0]) → 包含10

需要精确控制元素数量时用 linspace(如生成画图用的 x 轴坐标点);步长明确时用 arange(如生成卷积核的偏移量)。


3.5 创建随机张量

3.5.1 torch.rand():均匀分布

python 复制代码
torch.rand(*size, dtype=None)

[0, 1) 区间内按均匀分布随机生成浮点数。

python 复制代码
torch.rand(2, 3)   # 形状 (2,3),值均匀分布在 [0, 1) 之间,默认 float32

3.5.2 torch.randn():标准正态分布

python 复制代码
torch.randn(*size, dtype=None)

标准正态分布(均值 μ=0,标准差 σ=1)随机生成浮点数。

注意:它不是 0-1 之间!值可以出现负数,约有 95% 的值落在 -2, 2 之间,但也可能出现绝对值更大的值。

python 复制代码
torch.randn(3, 4)  # 形状 (3,4),值从 N(0,1) 分布中采样

3.5.3 torch.randint():离散整数随机

python 复制代码
torch.randint(low=0, high, size, dtype=torch.int64)

[low, high) 区间(左闭右开)随机生成整数。返回类型默认是 int64

python 复制代码
torch.randint(0, 10, (2, 3))    # 在 [0, 10) 范围内随机整数,形状 (2,3)
torch.randint(0, 2, (100,))     # 随机生成 100 个 0 或 1,用于随机标签/掩码

3.5.4 随机种子

为什么需要随机种子:深度学习实验中,权重初始化、数据打乱、Dropout 等步骤都依赖随机数。如果不固定种子,每次运行结果都不同,无法复现实验结果,无法调试,写论文时也无法让审稿人复现你的实验。

随机种子的原理

  • 计算机生成的"随机数"本质上是伪随机数,由一个确定的算法(伪随机数生成器,PRNG)根据一个初始种子值生成
  • 相同的种子 + 相同的生成次序列 → 完全相同的随机数序列
  • 种子本身只是一个整数,设什么值都可以(42 是流行的"梗值",源于《银河系漫游指南》)

关键理解 :设置了 manual_seed(42) 后,连续调用两次 torch.randn(3)

  • 第一次调用返回随机序列 A
  • 第二次调用返回随机序列 B(A ≠ B,序列是往下推进的)
  • 但如果重新运行整个脚本,两次调用的结果分别和上次运行时相同(A 和 B 都重现了)
python 复制代码
torch.manual_seed(42)
print(torch.randn(3))   # 每次运行输出: tensor([0.3367, 0.1288, 0.2345])  ← 固定
print(torch.randn(3))   # 每次运行输出: tensor([0.2303, -1.1229, -0.1863]) ← 也固定

常用种子 API

API 作用 使用场景
torch.manual_seed(seed) 设置 CPU 随机种子 必须调用的基础设置
torch.cuda.manual_seed(seed) 设置当前 GPU 随机种子 使用 GPU 时配合使用
torch.cuda.manual_seed_all(seed) 设置所有 GPU 随机种子 多 GPU 训练
torch.initial_seed() 返回当前的种子值(不设置) 查看当前种子,调试用

torch.initial_seed() 返回的是当前生成器使用的种子值,它的设计目的是让你检查当前种子,而非设置种子。在一次运行中,它的返回值固定不变。


3.6 创建指定值张量

3.6.1 全零张量

python 复制代码
torch.zeros(*size, dtype=None)           # 根据形状创建
torch.zeros_like(input, dtype=None)      # 根据已有张量的形状创建

典型场景

  • 初始化偏置(bias)为零
  • 创建注意力掩码(mask)时,先建全零再填充
  • 占位:知道形状和 dtype,不知道具体值时先占位

3.6.2 全一张量

python 复制代码
torch.ones(*size, dtype=None)
torch.ones_like(input, dtype=None)

典型场景

  • 初始化某些缩放因子为 1
  • 创建全一掩码
  • 在门控机制(gate)中初始化遗忘门偏置为 1(LSTM 中常用技巧)

3.6.3 全指定值张量

python 复制代码
torch.full(size, fill_value, dtype=None)          # 根据形状创建
torch.full_like(input, fill_value, dtype=None)    # 根据已有张量的形状创建

典型场景

  • 创建全为 -inf 的注意力掩码
  • 创建全为某个填充值(如 padding value)的张量

*_like 系列函数(zeros_likeones_likefull_like)会继承输入张量的 dtype、device、requires_grad 等属性,除非在参数中显式覆盖。这是设计上的便利,当你需要创建一个形状和属性都和已有张量相同的模板张量时,不需要再手动传一遍。

python 复制代码
x = torch.randn(3, 4, dtype=torch.float64, device='cuda')
y = torch.zeros_like(x)  # 形状 (3,4), float64, 在 cuda 上 --- 全部自动继承

关于创建张量的默认 dtype

  • torch.rand()torch.randn()torch.zeros() 等未指定 dtype 时,默认使用全局默认类型 torch.float32。可通过 dtype 参数修改。
  • torch.tensor() 在未指定 dtype 时会根据输入数据自动推断

4. 张量与 NumPy 互转

这是实际项目中最频繁的操作之一,核心要理解共享内存 vs 拷贝的区别。

4.1 张量 → NumPy

python 复制代码
# 方式1: .numpy() --- 共享内存
x = torch.tensor([1.0, 2.0, 3.0])
arr = x.numpy()          # arr 和 x 指向同一块内存!
arr[0] = 999
print(x)                 # tensor([999., 2., 3.])  ← 张量也变了!

# 方式2: .numpy().copy() --- 不共享
arr_safe = x.numpy().copy()

# 注意:requires_grad=True 的张量不能直接 .numpy()
x_grad = torch.tensor([1.0, 2.0], requires_grad=True)
# arr = x_grad.numpy()   # RuntimeError! 需要先 detach
arr = x_grad.detach().numpy()   # 正确做法

4.2 NumPy → 张量

python 复制代码
import numpy as np
arr = np.array([1.0, 2.0, 3.0])

# 方式1: torch.from_numpy() --- 共享内存
t1 = torch.from_numpy(arr)    # t1 和 arr 共享底层数据
arr[0] = 999
print(t1)                      # tensor([999., 2., 3.]) ← 张量也变了

# 方式2: torch.tensor() --- 拷贝(不共享内存)
t2 = torch.tensor(arr)         # 拷贝数据,各自独立
arr[0] = 0
print(t2)                      # tensor([999., 2., 3.]) ← 不受影响

# 避免共享内存的写法
t_safe = torch.from_numpy(arr).copy()   

4.3 为什么带梯度的张量不能直接 .numpy()?

python 复制代码
x = torch.tensor([1.0, 2.0], requires_grad=True)
# x.numpy()  # RuntimeError: Can't call numpy() on Tensor that requires grad.

原因:NumPy 没有自动微分机制。如果一个张量还在计算图中(requires_grad=True),直接转为 NumPy 会丢失梯度追踪信息,造成数据一致性问题。PyTorch 选择直接报错,强制你必须显式调用 .detach()

python 复制代码
x_np = x.detach().numpy()  # detach 断开计算图,返回 requires_grad=False 的张量

detach() 的作用与原理

  • 返回一个与原张量共享底层数据requires_grad=False 的新张量
  • 它从当前计算图中"脱离"出来,之后对它的一切操作都不会被 autograd 追踪
  • 常用于:获取模型中间结果做可视化、保存 checkpoint 时去掉梯度信息节省存储、将梯度的张量转为 NumPy

detach() vs data

  • data 是老式写法,存在安全隐患(可能绕过 autograd 检查导致错误梯度)
  • detach() 更安全,是现代 PyTorch 的推荐做法
  • 结论:优先用 detach(),尽量不用 data

以上为个人学习总结,旨在梳理个人理解。如有疏漏或不当之处,欢迎指正与交流。如果文章对你有帮助,别忘了点个赞、留个言,让更多的小伙伴看到~ 我们下篇再见!

下篇将深入张量的计算、索引、形状操作、拼接以及自动微分的完整工作流程~

相关推荐
逻辑君1 小时前
认知思维导图生成器
人工智能·深度学习·机器学习
workflower2 小时前
案例:大模型驱动的“发现- 研判- 整改- 验证”全闭环治理体系
人工智能·深度学习·机器学习·设计模式·机器人
x17388062732 小时前
目录遍历漏洞
笔记·目录遍历漏洞
ZhouDevin2 小时前
算法论文/模型微调1——CNN架构做lora微调训练
人工智能·深度学习·算法·架构·cnn
砚凝霜3 小时前
软考网络工程师|第 5 章 广域网基础技术完整备考笔记
网络·笔记
oier_Asad.Chen3 小时前
【OI学习笔记】Floyed-Warshall算法解决传递闭包问题
c++·笔记·学习·算法·图论·最短路·传递闭包
鱼听禅3 小时前
C#学习笔记-添加编译参数到程序集自动更新程序编译时间
笔记·学习·c#
香菜TTT3 小时前
LangChain框架_学习笔记
笔记·学习·langchain
春水碧于天,画船听雨眠3 小时前
LangChain学习笔记(二)
笔记·学习·langchain