[新手小白也能学会] 01-PyTorch框架使用(上)

**摘要:**PyTorch 是一个基于 Python 语言的深度学习框架,它将数据封装成张量(Tensor)来进行处理(官方早期定位是"基于 Python 的科学计算包",如今已发展为深度学习的主流框架)。PyTorch 提供了灵活且高效的工具,用于构建、训练和部署机器学习和深度学习模型。PyTorch 广泛应用于学术研究和工业界,特别是在计算机视觉、自然语言处理、强化学习等领域。张量是 PyTorch 中的核心数据抽象:由同一种数据类型的元素组成的多维数组,与 NumPy 数组类似。

目录

[一、PyTorch 框架简介](#一、PyTorch 框架简介)

[1.1 什么是 PyTorch](#1.1 什么是 PyTorch)

[1.2 PyTorch 的特点](#1.2 PyTorch 的特点)

[1.3 PyTorch 与 TensorFlow 的比较](#1.3 PyTorch 与 TensorFlow 的比较)

[1.4 PyTorch 发展历史](#1.4 PyTorch 发展历史)

[1.5 安装与验证](#1.5 安装与验证)

[二、张量(Tensor):PyTorch 的核心数据结构](#二、张量(Tensor):PyTorch 的核心数据结构)

[2.1 什么是张量](#2.1 什么是张量)

[2.2 张量的数据类型](#2.2 张量的数据类型)

[2.3 基本创建方式](#2.3 基本创建方式)

[方式一:torch.tensor(data, dtype) ------ 根据数据创建](#方式一:torch.tensor(data, dtype) —— 根据数据创建)

[方式二:torch.Tensor(形状) ------ 根据形状创建](#方式二:torch.Tensor(形状) —— 根据形状创建)

[方式三:torch.IntTensor() 等 ------ 创建指定类型的张量](#方式三:torch.IntTensor() 等 —— 创建指定类型的张量)

[2.4 线性区间张量](#2.4 线性区间张量)

[torch.arange(start, end, step) ------ 固定步长](#torch.arange(start, end, step) —— 固定步长)

[torch.linspace(start, end, steps) ------ 固定元素个数](#torch.linspace(start, end, steps) —— 固定元素个数)

[2.5 随机张量](#2.5 随机张量)

[torch.randn / torch.rand ------ 随机浮点张量](#torch.randn / torch.rand —— 随机浮点张量)

[torch.randint(low, high, size) ------ 随机整数张量](#torch.randint(low, high, size) —— 随机整数张量)

[随机种子:torch.initial_seed() 与 torch.manual_seed(seed)](#随机种子:torch.initial_seed() 与 torch.manual_seed(seed))

[2.6 指定值张量](#2.6 指定值张量)

[2.7 元素类型转换](#2.7 元素类型转换)

[方式一:创建时用 dtype= 指定](#方式一:创建时用 dtype= 指定)

[方式二:data.type(dtype) ------ 通用转换](#方式二:data.type(dtype) —— 通用转换)

[方式三:.double() 等快捷方法(每一种如下,默认类型已标注)](#方式三:.double() 等快捷方法(每一种如下,默认类型已标注))

[三、张量与 NumPy 的互转](#三、张量与 NumPy 的互转)

[3.1 张量 → NumPy:.numpy()(共享内存)](#3.1 张量 → NumPy:.numpy()(共享内存))

[3.2 NumPy → 张量:from_numpy() 与 torch.tensor()](#3.2 NumPy → 张量:from_numpy() 与 torch.tensor())

[3.3 从标量张量中提取数值:.item()](#3.3 从标量张量中提取数值:.item())

四、张量数值计算

[4.1 基本运算:加减乘除取负](#4.1 基本运算:加减乘除取负)

[4.2 点乘运算(Hadamard 积)](#4.2 点乘运算(Hadamard 积))

[4.3 矩阵乘法运算](#4.3 矩阵乘法运算)

五、张量运算函数

六、张量索引操作

[6.1 准备数据](#6.1 准备数据)

[6.2 简单行、列索引](#6.2 简单行、列索引)

[6.3 列表索引](#6.3 列表索引)

[6.4 范围索引(切片)](#6.4 范围索引(切片))

[6.5 布尔索引](#6.5 布尔索引)

[6.6 多维索引](#6.6 多维索引)


一、PyTorch 框架简介

1.1 什么是 PyTorch

  • PyTorch 是一个基于 Python 语言的深度学习框架 ,它将数据封装成**张量(Tensor)**来进行处理(官方早期定位是"基于 Python 的科学计算包",如今已发展为深度学习的主流框架)。
  • PyTorch 提供了灵活且高效的工具,用于构建、训练和部署机器学习和深度学习模型。
  • PyTorch 广泛应用于学术研究和工业界,特别是在计算机视觉、自然语言处理、强化学习等领域。

1.2 PyTorch 的特点

特点 说明
类似 NumPy 的张量计算 支持高效的数值计算
自动微分系统 支持自动梯度计算
深度学习库 提供丰富的神经网络组件
动态计算图 灵活构建计算图
GPU 加速 支持 CUDA
跨平台支持 支持多种操作系统
  • 类似于 NumPy 的张量计算
    • 基本数据结构是张量(Tensor),与 NumPy 数组类似,但张量具有 GPU 加速能力(通过 CUDA),深度学习模型可以高效地在 GPU 上运行。
  • 自动微分系统(autograd)
    • 提供 autograd 模块,能自动计算模型中每个参数的梯度。
    • 《00-深度学习简介》里反向传播最繁琐的一步就是求 ∂L/∂w、∂L/∂b------autograd 就是把这一步自动化的模块。梯度计算简洁高效,且支持复杂的模型和动态计算图。
  • 完整的深度学习库(torch.nn)
    • torch.nn 子模块包含大量预构建的层 (全连接层、卷积层、循环神经网络层等)、损失函数 (交叉熵、均方误差等)和优化算法(SGD、Adam 等)。
    • torch.nn.Module 是构建神经网络的基础类,继承该类即可定义自己的网络架构。
  • 动态计算图
    • 运行时构建和修改模型结构,灵活性高,适合研究和模型调试。
  • GPU 加速(CUDA 支持)
    • 只需把数据和模型转移到 GPU,PyTorch 会自动优化计算过程;通过 tensor.to(device) 一行代码即可在 CPU、GPU 之间(甚至 GPU 之间)搬运。
  • 跨平台支持
    • 支持多种硬件平台(CPU、GPU、TPU 等)、多种操作系统(Linux、Windows、macOS)以及分布式计算环境(多 GPU、分布式训练)。

1.3 PyTorch 与 TensorFlow 的比较

对比项 PyTorch TensorFlow
计算图 动态图(边写边执行,像普通 Python 代码一样调试) 1.x 是静态图(先建图再执行);2.x 引入 Eager Execution 后也支持动态图
API 风格 更贴近 Python(Pythonic),易学易用、调试方便 1.x 封装较重,调试不便
适用场景 研究和原型设计的首选,学术界与部分工业界更为流行 生态偏工程部署(1.x 时代优势明显)

PyTorch vs. TensorFlow: Key Differences and Performance Comparison | Toptal®--推荐文章

PyTorch 与 TensorFlow 深度学习 |内置设备--推荐文章

结论呼应《00-深度学习简介》:当前学界与工业界新项目的主流是 PyTorch------灵活性和调试体验优于早期 TensorFlow,这是它"后期制胜"的关键。

1.4 PyTorch 发展历史

阶段 时间 关键进展
Torch 2002 由 Ronan Collobert 等人开发,基于 Lua 语言的科学计算框架,提供多维张量操作和科学计算工具
Torch7 2011 Torch 的最后一个主要版本(由 Ronan Collobert、Clement Farabet 等人维护),在深度学习领域取得一定成功;随着 PyTorch 的普及,Torch 停止维护
PyTorch 0.1.0 2017 年初 Facebook 人工智能研究院(FAIR)在 Torch 基础上发布第一个版本,全面改用 Python 风格(Pythonic)接口,模型定义和调试更直观灵活
PyTorch 0.2.0 2017 年中 PyTorch 从诞生起就采用动态图(define-by-run)机制,0.2.0 持续完善相关能力------这正是它后期超越 TensorFlow 1.x 的关键
PyTorch 1.0.0 2018 年底 首个稳定版本(合并 Caffe2),进一步简化模型的构建、训练与部署;引入 TorchScript 支持生产环境。PyTorch 的代码本身就是"立即执行"(Eager)模式
PyTorch 2.0 2023 引入 torch.compile 加速训练;TorchDynamo 替代 torch.jit.trace / torch.jit.script;编译器性能大幅提升,分布式训练也做了优化

1.5 安装与验证

bash 复制代码
pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple
  • -i 用于指定镜像源:国内直接访问 PyPI 官方源速度慢,改用清华镜像源可显著加速下载。
  • GPU 版本需要与本机 CUDA 版本匹配,按需从 PyTorch 官网选择对应安装命令。

安装完成后验证:

python 复制代码
import torch

print(torch.__version__)  # 能打印版本号即安装成功

二、张量(Tensor):PyTorch 的核心数据结构

2.1 什么是张量

  • 张量是 PyTorch 中的核心数据抽象 :由同一种数据类型 的元素组成的多维数组 ,与 NumPy 数组类似。
    • 严格来说"矩阵"只是二维张量,张量是多维数组的统称。
  • 在 PyTorch 中,张量以类 (torch.Tensor)的形式封装,数值计算、矩阵操作、自动求导等处理方法都定义在这个类中。

维度是逐级堆叠出来的:

  • 0 维 :标量(一个数);1 维 :向量;2 维:矩阵
  • 多个二维张量组成三维张量(如一张图像:高 × 宽 × 通道)
  • 多个三维张量组成四维张量(如一批图像:样本数 × 通道 × 高 × 宽,即《00-深度学习简介》提到的 NCHW)
  • 多个四维张量组成五维张量(如一批视频片段)

此为层级视图:

2411.16094 Very Basics of Tensors with Graphical Notations: Unfolding, Calculations, and Decompositions--推荐文章

呼应《00-深度学习简介》2.5 节:张量是深度学习底层的统一数据结构,且只能存放数值;ANN 的输入是二维(样本数 × 特征数),图像是三维 HWC------学完本章的创建方法,这些维度就都能亲手造出来了。

2.2 张量的数据类型

常用数据类型与对应的构造器:

类别 dtype 对应构造器 说明
浮点 torch.float16 torch.HalfTensor 半精度浮点
浮点 torch.float32 torch.FloatTensor 默认类型
浮点 torch.float64 torch.DoubleTensor 双精度浮点
整型 torch.int16 torch.ShortTensor 短整型
整型 torch.int32 torch.IntTensor 整型
整型 torch.int64 torch.LongTensor 长整型(整数默认)
其他 torch.uint8 / torch.bool --- 无符号 8 位整型 / 布尔型

类型里的数字------比特位

  • float16 里的 16 指的是 16 个比特位(bit) ,float32 就是 32 个比特位,以此类推。比特位越多,能表示的数值范围越大、精度越高。
  • 单位换算:1 字节(Byte)= 8 比特(bit);1 KB = 1024 B,往上是 MB、GB、TB、PB、EB、ZB、YB(1024 进制)。
    • 大小关系:bit < B(字节) < KB < MB < GB < TB < PB < EB < ZB < YB
    • 示例数据:2022 年全球数据总量已达 ZB 级别(约 44 ZB)。
  • 由此可知一个 float32 元素占 4 字节------张量越大越占内存,这也是"半精度训练"能省显存的原因。

张量中默认的数据类型是 float32(torch.FloatTensor);整数的默认类型是 int64。创建时元素类型由源数据决定,下一节代码可以验证。

2.3 基本创建方式

方式一:torch.tensor(data, dtype) ------ 根据数据创建

知识点:元素类型跟随源数据 ------NumPy 数组是 float64,创建的张量就是 float64;Python 浮点列表默认 float32;想指定类型就传 dtype= 参数。

python 复制代码
import torch  # 需要安装 torch 模块
import numpy as np

# 1. 标量 -> 0 维张量, 整数默认 int64
data = torch.tensor(10)
print(data)

# 2. NumPy 数组: 源数据是 float64, 张量元素类型也是 float64
data = np.random.randn(2, 3)
data = torch.tensor(data)
print(data)

# 3. 列表: Python 浮点数默认 float32
data = [[10., 20., 30.], [40., 50., 60.]]
data = torch.tensor(data)
print(data)
#结果
# tensor([[10., 20., 30.],
#         [40., 50., 60.]])

# 4. 用 dtype= 显式指定元素类型: 以指定的为准, 不再跟随源数据
data = torch.tensor(10, dtype=torch.float32)          # 整数指定成浮点
print(data, data.dtype)        # tensor(10.) torch.float32

data = torch.tensor([1, 2, 3], dtype=torch.float64)   # int64 指定成 float64
print(data, data.dtype)        # tensor([1., 2., 3.]) torch.float64

data = torch.tensor([1.5, 2.7], dtype=torch.int64)    # 浮点指定成整数: 截断小数
print(data, data.dtype)        # tensor([1, 2]) torch.int64

# 常用别名: torch.float = torch.float32, torch.double = torch.float64
data = torch.tensor([1, 2, 3], dtype=torch.float)
print(data, data.dtype)        # tensor([1., 2., 3.]) torch.float32

注意:浮点转整数和后面IntTensor 一样是 截断小数 (1.5→1),不是四舍五入。

方式二:torch.Tensor(形状) ------ 根据形状创建

先补一个概念------形状(shape):张量每个维度上的元素个数 ,按维度顺序写成一组数。比如"2 行 3 列"的矩阵,形状就是 (2, 3):第 0 维有 2 个元素(2 行),第 1 维有 3 个元素(3 列)。前面 np.random.randn(2, 3)、torch.zeros(2, 3) 里反复出现的 2, 3,说的都是形状。

这个函数有两种行为,取决于你传的是什么:

行为一:传几个整数 → 当作形状,创建"未初始化"的张量

python 复制代码
# 创建 2 行 3 列的张量, 默认 dtype 为 float32
data = torch.Tensor(2, 3)
print(data)

运行一次,打印出这样的东西(内存残留数据):

python 复制代码
tensor([[3.0702e-31, 2.5223e-26, 4.5889e-41],
        [2.5223e-26, 4.5889e-41, 5.0584e-31]])

什么叫"内存残留数据"

torch.Tensor(2, 3) 对计算机发出的语言是------"给我一块能装 6 个 float32 的内存",但只申请,不填数。但这块内存是计算机之前用过又释放掉的,里面还留着上一个程序(或上一个变量)写的旧数据。就像酒店房间退房后没打扫,你直接住进去,抽屉里还有上一位客人的东西------打印出来的就是这些"上一位客人"留下的乱七八糟的数。

实验情况 :多运行几次程序,每次打印的值都不一样------因为每次申请到的内存块不同,残留的数据也不同。这些数没有规律,也不保证是 0,所以任何情况下都不能假设未初始化张量的值。

行为二:传一个列表 → 当作数据,按数据创建(等价 torch.tensor)

python 复制代码
# 传递列表时, 创建包含指定元素的张量 (注意元素被转成了浮点 10. 20.)
data = torch.Tensor([10])
print(data)        # tensor([10.])
data = torch.Tensor([10, 20])
print(data)        # tensor([10., 20.])

两种行为对比:

写法 传的是什么 结果
torch.Tensor(2, 3) 几个整数 → 形状 2×3 的未初始化张量,值是随机的
torch.Tensor([10, 20]) 一个列表 → 数据 一维张量 [10., 20.],等价于 torch.tensor([10, 20])
方式三:torch.IntTensor() 等 ------ 创建指定类型的张量

知识点:整型构造器收到浮点数时会直接截断小数部分(不是四舍五入);无参调用时同样是创建未初始化张量。

python 复制代码
# 1. 创建 2 行 3 列, dtype 为 int32 的张量
data = torch.IntTensor(2, 3)
print(data)

# 2. 注意: 传入的元素类型不正确时, 会进行类型转换 (截断小数: 2.5 -> 2)
data = torch.IntTensor([2.5, 3.3])
print(data)

# 3. 其他类型构造器
data = torch.ShortTensor()   # int16
data = torch.LongTensor()    # int64
data = torch.FloatTensor()   # float32
data = torch.DoubleTensor()  # float64

2.4 线性区间张量

torch.arange(start, end, step) ------ 固定步长

知识点:在 [start, end) 区间按步长step生成元素,左闭右开。

python 复制代码
# 在 [0, 10) 区间按步长 2 生成: [0, 2, 4, 6, 8]
data = torch.arange(0, 10, 2)
print(data)
torch.linspace(start, end, steps) ------ 固定元素个数

知识点:在 [start, end] 区间生成 steps 个元素,左闭右闭------首尾两个数都包含。

它和 arange 的分工正好相反:

函数 你指定的 它自己算的
torch.arange 步长 step 生成几个元素(区间长度 ÷ 步长)
torch.linspace 生成几个元素 steps 步长 step(用下面公式)

步长公式为什么是 (end − start) / (steps − 1)

想象把一根从 start 到 end 的线段均匀打点 。要放 steps 个点,点与点之间的间隔只有 steps − 1 段(不是 steps 段!就像 5 根手指之间只有 4 个缝隙)。间隔数知道了,每段长度就出来了:

复制代码
step = (end - start) / (steps - 1)      ← 总长度 ÷ 间隔数

然后从起点出发,每次往前走一个 step,第 i 个点(i 从 0 数起)就是:

复制代码
value_i = start + step × i        i = 0, 1, ..., steps-1

拿 linspace(0, 9, 10) 逐项走一遍:

python 复制代码
10 个点落在 [0, 9] 上, 间隔有 10 - 1 = 9 段
step = (9 - 0) / (10 - 1) = 9 / 9 = 1

i=0:  0 + 1×0 = 0     ← 第 1 个点(左端点, 包含)
i=1:  0 + 1×1 = 1
i=2:  0 + 1×2 = 2
...
i=9:  0 + 1×9 = 9     ← 第 10 个点(右端点, 也包含)

结果: [0, 1, 2, ..., 9]  共 10 个数
python 复制代码
data = torch.linspace(0, 9, 10)
print(data)
# tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

除不尽时 step 就是小数,点照样均匀分布,且终点一定取到:

python 复制代码
# 5 个点落在 [0, 9]: 间隔 4 段, step = 9/4 = 2.25
data = torch.linspace(0, 9, 5)
print(data)
# tensor([0.0000, 2.2500, 4.5000, 6.7500, 9.0000])   9 包含在内

对比记忆:arange(0, 10, 2) → [0, 2, 4, 6, 8],10 取不到(右开);

linspace(0, 10, 5) → [0, 2.5, 5, 7.5, 10],10 一定取到(右闭)。一句话:想要包含终点,用 linspace。
为什么叫"线性":以下两个函数生成的元素是等差数列------相邻两个元素之间的差(步长)固定不变,元素在数轴上均匀线性排列,所以叫线性(区间)张量。区别在于:arange 由"步长"决定生成几个,linspace 由"个数"决定步长是多少。

2.5 随机张量

torch.randn / torch.rand ------ 随机浮点张量

知识点:randn 服从标准正态分布 (均值 0、方差 1);rand 服从 [0, 1) 均匀分布。

下图为正态分布曲线:说明数据更集中在均值附近。

下图为均匀分布示意图:

python 复制代码
# 创建 2 行 3 列的标准正态分布张量
data = torch.randn(2, 3)
print(data)
torch.randint(low, high, size) ------ 随机整数张量

知识点:取值区间 [low, high),左闭右开。

python 复制代码
# 在 [0, 10) 中随机取整数, 生成 2 行 3 列张量
data = torch.randint(0, 10, (2, 3))
print(data)
随机种子:torch.initial_seed() 与 torch.manual_seed(seed)

误区:计算机不会真正的"随机"

计算机是确定性机器,只会按公式算数。所谓随机数是伪随机数 :从一个起始数字(种子,seed)出发,用一套写死的公式不断推算出下一个数。公式人人相同、永远不变,于是有一个必然结论:

种子相同 → 推算出的整条数列完全相同

由此回答几个常见疑问:

  • 为啥设置了就不变了:起点固定 → 往后抄到的每个数都固定 → 每次运行程序都从同一页开始抄,结果自然一模一样。
  • 为啥我和别人跑出来一模一样:因为你们翻的是同一种子,且生成的函数、参数、调用顺序都相同------不是巧合,是为了保证结果可对照、可复现。
  • 参数可以随便填吗 :可以。任何整数都行,1、24、2026 只是"种子"编号,本身没有好坏或特殊含义。
  • 不设置种子为啥每次都变 :PyTorch 默认拿系统时间戳 当种子------时间一直在走torch.initial_seed() 查到的就是当前生效的种子值。
python 复制代码
# 查看当前随机数种子
print('随机数种子:', torch.initial_seed())

# 设置随机数种子后, 每次运行生成的随机数都相同
torch.manual_seed(100)
data = torch.randn(2, 3)
print(data)
print('随机数种子:', torch.initial_seed())

再验证一遍"固定起点"的效果:

python 复制代码
import torch

# 不设种子 -> 用时间戳当种子, 每次运行结果都不同
print(torch.randn(3))

# 设种子 -> 起点固定, 每次运行打印的都是同一组数
torch.manual_seed(100)
print(torch.randn(3))

# 重新回同一个起点, 数列从头再来一遍
torch.manual_seed(100)
print(torch.randn(3))     # 和上一行完全相同

# 换个种子, 序列整体改变
torch.manual_seed(24)
print(torch.randn(3))     # 又是另一组, 但以后每次用 24 都出这一组

用途:论文、调试、课堂练习都要求"结果可复现"------固定种子后,别人跑你的代码能得到和你逐位相同的结果,出了问题才好对答案。知识点:initial_seed() 查看(或获取)当前随机种子;manual_seed(n) 固定随机种子 ------之后每次运行生成的随机数序列都相同,用于实验复现 。

输出结果:

python 复制代码
tensor([[-0.5209, -0.2439, -1.1780],
        [ 0.8133,  1.1442,  0.6790]])
随机数种子: 4508475192273306739
tensor([[ 0.3607, -0.2859, -0.3938],
        [ 0.2429, -1.3833, -2.3134]])
随机数种子: 100

小结:

方法 说明
torch.arange(start, end, step) 创建线性张量(按步长)
torch.linspace(start, end, steps) 创建线性张量(按个数)
torch.initial_seed() 查看随机数种子
torch.manual_seed(seed) 设置随机数种子
torch.randn(size) 创建随机浮点类型张量

2.6 指定值张量

知识点:zeros / ones / full 按指定形状创建;对应的 *_like 系列 按已有张量的形状创建同形张量------这组函数可以避免重复手写形状。

python 复制代码
# 1. 创建指定形状的全 0 张量
data = torch.zeros(2, 3)
print(data)
# tensor([[0., 0., 0.],
#         [0., 0., 0.]])

# 2. 根据已有张量的形状创建全 0 张量
data = torch.zeros_like(data)
print(data)
# tensor([[0., 0., 0.],
#         [0., 0., 0.]])      形状、类型都跟着 data 走
python 复制代码
# 1. 创建指定形状的全 1 张量
data = torch.ones(2, 3)
print(data)
# tensor([[1., 1., 1.],
#         [1., 1., 1.]])

# 2. 根据已有张量的形状创建全 1 张量
data = torch.ones_like(data)
print(data)
# tensor([[1., 1., 1.],
#         [1., 1., 1.]])
python 复制代码
# 1. 创建 2 行 3 列、值全为 10 的张量 (整数填充默认 int64)
data = torch.full([2, 3], 10)
print(data)
# tensor([[10, 10, 10],
#         [10, 10, 10]])

# 2. 根据已有张量的形状创建值全为 20 的张量
data = torch.full_like(data, 20)
print(data)
# tensor([[20, 20, 20],
#         [20, 20, 20]])

看输出的细节:

zeros / ones 的打印是 0. / 1.------带小数点 ,因为这两个函数默认 float32;

full 填整数时打印 10------不带点 ,默认 int64。*_like 不光抄形状,也抄元素类型。
开发建议:

要按数据 创建 → 用 torch.tensor;

要按形状 创建 → 用 torch.zeros / ones / full / randn 等,而不是 torch.Tensor(2, 3)------后者不仅语义不明(要回看参数才能猜出意图),还会出现"未初始化"的问题。

2.7 元素类型转换

三种方式:创建时用 dtype= 指定 ;创建后用 data.type() 转换;或用 .double() 等快捷方法。

方式一:创建时用 dtype= 指定
python 复制代码
# dtype=torch.float 即 float32 (张量的默认类型)
t1 = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float)
print(f"t1:{t1},元素类型:{t1.dtype},张量类型:{type(t1)}")
方式二:data.type(dtype) ------ 通用转换
python 复制代码
# 将 t1 转换为 int16
t2 = t1.type(torch.int16)
print(f"t2:{t2},元素类型:{t2.dtype}")

data = torch.full([2, 3], 10)
print(data.dtype)  # torch.int64

# 将 data 元素类型转换为 float64
data = data.type(torch.DoubleTensor)
print(data.dtype)  # torch.float64

# 转换为其他类型
# data = data.type(torch.float64)       # 与上一行等价
# data = data.type(torch.ShortTensor)   # int16
# data = data.type(torch.IntTensor)     # int32
# data = data.type(torch.LongTensor)    # int64
# data = data.type(torch.FloatTensor)   # float32
# data = data.type(torch.float16)       # 半精度浮点
方式三:.double() 等快捷方法(每一种如下,默认类型已标注)
python 复制代码
data = torch.full([2, 3], 10)

print(data.half())    # -> float16 (半精度)
print(data.float())   # -> float32 ==张量的默认类型==
print(data.double())  # -> float64
print(data.short())   # -> int16
print(data.int())     # -> int32
print(data.long())    # -> int64  ==整数张量的默认类型==

# 与 data.type() 功能等价, 写法更短, 按需选用

三、张量与 NumPy 的互转

Tensors --- PyTorch Tutorials 2.14.0+cu130 documentationhttps://docs.pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html#bridge-with-numpy--推荐文章

为什么要互转:NumPy 是 Python 数据科学的基础库(读数据、预处理常用 ndarray),而 PyTorch 计算用张量------数据在两个世界之间流转,就需要这一组转换函数。

3.1 张量 → NumPy:.numpy()(共享内存)

知识点:张量.numpy() 转出的 ndarray 与原张量共享内存 ------改一个,另一个跟着变;链式加 .copy() 拷贝一份,就不共享了。

python 复制代码
import torch

# 1. 创建张量
t1 = torch.tensor([1, 2, 3, 4, 5])
print(f"t1:{t1},元素类型:{t1.dtype},张量类型:{type(t1)}")

# 2. 张量 -> numpy
# n1 = t1.numpy()          # 共享内存: 改一个, 另一个跟着变
n1 = t1.numpy().copy()     # 链式写法 copy(): 拷贝一份, 不共享内存
print(f"n1:{n1},元素类型:{n1.dtype},类型:{type(n1)}")

# 3. 验证是否共享内存: 只改 n1
n1[0] = 100
print(f"t1:{t1}")   # tensor([1, 2, 3, 4, 5])  t1 没变
print(f"n1:{n1}")   # [100   2   3   4   5]    只有 n1 变了

对比:不加 .copy() 时的共享内存效果(示例):

python 复制代码
data_tensor = torch.tensor([2, 3, 4])
data_numpy = data_tensor.numpy()   # 共享内存
data_numpy[0] = 100
print(data_tensor)  # tensor([100,  3,  4])  张量也变了!
print(data_numpy)   # [100   3   4]

3.2 NumPy → 张量:from_numpy() 与 torch.tensor()

知识点:两种方式只差一个共享内存------

  • torch.from_numpy(ndarray):共享内存(零拷贝,效率高、省内存,但一方修改另一方跟着变),dtype 跟随 ndarray;
  • torch.tensor(ndarray):不共享内存 (拷贝数据,安全独立),还能顺手用 dtype= 转类型。(推荐)
python 复制代码
import numpy as np
import torch

# 1. 创建 numpy 数组
n1 = np.array([11, 22, 33])
print(f"n1:{n1},元素类型:{n1.dtype}")

# 2. numpy -> 张量, 两种方式
t1 = torch.from_numpy(n1)     # 共享内存; 需要 float32 时可再 .type(torch.float32)
t2 = torch.tensor(n1)         # 不共享内存 (拷贝)

# 3. 验证: 修改 numpy 数组
n1[0] = 99
print(f"n1:{n1}")   # [99 22 33]
print(f"t1:{t1}")   # tensor([99, 22, 33])  跟着变了 (共享)
print(f"t2:{t2}")   # tensor([11, 22, 33])  没变   (拷贝)

3.3 从标量张量中提取数值:.item()

知识点:.item() 把只有一个元素的标量张量(0 维)提取成 Python 数值,后面算损失、看精度时经常用。

python 复制代码
# 标量张量, 类型随数据: 100.34 是 Python 浮点 -> float32
t1 = torch.tensor(100.34)
print(f"t1:{t1},元素类型:{t1.dtype},张量类型:{type(t1)}")

# 提取内容 -> Python float
value = t1.item()
print(f"value:{value},类型:{type(value)}")   # 100.34 <class 'float'>

四、张量数值计算

4.1 基本运算:加减乘除取负

函数 全称 符号等价写法 说明
add() add(加) a + b 加法
sub() subtract(减) a - b 减法
mul() multiply(乘) a * b 乘法
div() divide(除) a / b 除法**(结果自动变浮点)**
neg() negative(取负) -a 取负号

知识点:

  1. 带下划线的版本(add_、sub_、mul_、div_、neg_)会直接修改原数据(原地操作),效果类似 pandas 的 inplace=True;
  2. 符号 + - * / 可以替代对应的加减乘除函数,实际代码里更常用;
  3. 张量和数值运算 = 该数值与张量中每个元素依次运算。
python 复制代码
import torch

data = torch.randint(0, 10, [2, 3])
print(data)
# tensor([[3, 7, 4],
#         [0, 0, 6]])

# 1. 不修改原数据, 返回新张量
new_data = data.add(10)         # 等价 new_data = data + 10
print(new_data)

# 2. 直接修改原数据 (带下划线的函数)
data.add_(10)                   # 等价 data += 10
print(data)

# 3. 其他运算同理
print(data.sub(100))            # 减法  等价 data - 100
print(data.mul(100))            # 乘法  等价 data * 100
print(data.div(100))            # 除法  等价 data / 100, 结果是浮点数
print(data // 2)                # 整除(向下取整), 结果仍是整数
print(data.neg())               # 取负  等价 -data
python 复制代码
# 符号写法 vs 下划线写法
t1 = torch.tensor([1, 2, 3])

t2 = t1 + 10        # 符号写法: 不修改 t1
t2 = t1.add(10)     # 不修改 t1

t1.add_(10)         # 下划线写法: 修改 t1 本身
t1 += 10            # 符号的"原地"等价形式

print(f"t1:{t1}")   # tensor([21, 22, 23])
print(f"t2:{t2}")   # tensor([11, 12, 13])

4.2 点乘运算(Hadamard 积)

点乘 指的是相同形状 的张量对应位置 的元素各自相乘(逐元素乘法,不是线性代数里的点积),使用 mul 或运算符 * 实现。

python 复制代码
data1 = torch.tensor([[1, 2], [3, 4]])
data2 = torch.tensor([[5, 6], [7, 8]])

# 第一种方式: mul 函数
data = torch.mul(data1, data2)
print(data)
# 第二种方式: * 运算符
data = data1 * data2
print(data)
# tensor([[ 5, 12],      1*5=5   2*6=12
#         [21, 32]])      3*7=21  4*8=32  一一对应, 位置不变
python 复制代码
# 示例: 2 行 3 列的同形状张量点乘
t1 = torch.tensor([[1, 2, 3], [3, 4, 5]])
t2 = torch.tensor([[1, 2, 3], [3, 4, 5]])
t3 = t1 * t2        # 与 t1.mul(t2) 等价, 两种方式均可
print(f"t3:{t3}")

4.3 矩阵乘法运算

矩阵乘法的要求(和点乘完全不同):

  • 第一个矩阵形状 (n, m),第二个矩阵形状 (m, p)------第一个矩阵的列数 必须等于 第二个矩阵的行数(A 列 = B 行);
  • 结果形状为 (n, p)(A 的行数 × B 的列数)。

怎么算 :结果的第 i 行第 j 列 = A 的第 i 行 与 B 的第 j 列 对应相乘再求和("行 × 列")。

以 data1 @ data2((3,2) 乘 (2,2) → (3,2))为例:

python 复制代码
data1 = [[1, 2],        data2 = [[5, 6],
         [3, 4],                 [7, 8]]
         [5, 6]]

结果[0][0] = data1 第0行 [1, 2] · data2 第0列 [5, 7] = 1×5 + 2×7 = 19
结果[0][1] = data1 第0行 [1, 2] · data2 第1列 [6, 8] = 1×6 + 2×8 = 22
结果[1][0] = data1 第1行 [3, 4] · data2 第0列 [5, 7] = 3×5 + 4×7 = 43
...其余同理, 得到 [[19, 22], [43, 50], [67, 78]]

为什么深度学习离不开它:《00-深度学习简介》里全连接层的加权求和 z = w₁x₁ + ... + wₙxₙ + b,写成矩阵形式就是 Z = X @ W + b------一次矩阵乘法就算完了一整层所有神经元。

方法 说明
@ 运算符 两个矩阵的乘积运算(推荐)
torch.matmul() 效果同 @;高维张量时要求最后两个维度符合矩阵运算规则
torch.dot() 仅支持一维张量(向量内积:对应相乘再求和),二维会报错
python 复制代码
data1 = torch.tensor([[1, 2], [3, 4], [5, 6]])   # 形状 (3, 2)
data2 = torch.tensor([[5, 6], [7, 8]])           # 形状 (2, 2)

# 方式一: @ 运算符
data3 = data1 @ data2
print("data3->", data3)
# data3-> tensor([[19, 22],
#                 [43, 50],
#                 [67, 78]])      形状 (3, 2) = A行3 × B列2

# 方式二: torch.matmul, 效果相同
data4 = torch.matmul(data1, data2)
print("data4->", data4)

# dot() 仅支持一维: 向量内积
t4 = torch.tensor([1, 2, 3])
t5 = torch.tensor([4, 5, 6])
t6 = t4.dot(t5)
print(f't6: {t6}')   # tensor(32) = 1×4 + 2×5 + 3×6 = 4+10+18

# 二维张量用 dot() 会直接报错:
# t1 = torch.tensor([[1, 2, 3], [3, 4, 5]])
# t2 = torch.tensor([[1, 2], [3, 3], [4, 5]])
# t1.dot(t2)   # RuntimeError: 1D tensors expected

本章小结:

运算类型 方法 要求
基本运算 add、sub、mul、div、neg(带下划线修改原数据) 同形状或"张量 vs 数值"
点乘运算 torch.mul() 或 * 形状必须相同,对应元素相乘
矩阵乘法 @ 或 torch.matmul() A 列 = B 行,结果 = A 行 B 列;dot() 仅一维

五、张量运算函数

PyTorch 为每个张量封装了很多实用的计算函数:

函数 说明 dim 参数
mean() 均值 有(要求 Float/Double 类型)
sum() 求和 有
max() / min() 最大值 / 最小值 有(返回值 + 索引)
sqrt() 平方根 无(逐元素)
pow() 幂运算 无(逐元素)
exp() 自然指数 eˣ 无(逐元素)
log() / log2() / log10() 对数(以 e / 2 / 10 为底) 无(逐元素)

dim 参数怎么理解 :dim 指定"被压扁(消除)的那个维度"。对形状 (2, 3) 的张量:

  • dim=0:压掉第 0 维(行)→ 按列 聚合,结果形状 (3,);
  • dim=1:压掉第 1 维(列)→ 按行 聚合,结果形状 (2,);
  • 不传 dim:全部压掉,结果是一个标量。

max(dim) 的返回值 :不是单个张量,而是一个 named tuple------values 是最大值,indices 是最大值所在的下标位置(分类任务里常用来取"预测类别")。

python 复制代码
import torch

t1 = torch.tensor([[1, 2, 3],
                   [4, 5, 6]])
print(f't1:{t1}')

# 1. sum 求和
print(t1.sum(dim=0))   # 按列求和 -> tensor([5, 7, 9])    (1+4, 2+5, 3+6)
print(t1.sum(dim=1))   # 按行求和 -> tensor([ 6, 15])    (1+2+3, 4+5+6)
print(t1.sum())        # 全部求和 -> tensor(21)
print('-' * 30)

# 2. max 求最大值 (min 同理), 返回 values + indices
print(t1.max(dim=0))
# 每列最大: values=tensor([4, 5, 6]), indices=tensor([1, 1, 1])  都来自第 1 行
print(t1.max(dim=1))
# 每行最大: values=tensor([3, 6]),   indices=tensor([2, 2])     都在第 2 列
print(t1.max())        # 全局最大 -> tensor(6)
print('-' * 30)

# 3. mean 求均值: ==要求张量为 Float/Double 类型==
# print(t1.mean())      # 整数张量会报错, 先转 float 或创建时指定 dtype
print(t1.float().mean())          # 全体均值 -> 3.5
print(t1.float().mean(dim=0))     # 按列均值 -> tensor([2.5, 3.5, 4.5])

data = torch.randint(0, 10, [2, 3], dtype=torch.float64)  # 创建时就指定, 省得转换
print(data.mean())                 # tensor(4.1667)

# 4. 幂运算: ** 与 pow 均可
print(t1.float() ** 2)             # 运算符写法
print(t1.float().pow(2))           # 方法写法
print(torch.pow(t1.float(), 2))    # 函数写法, 三者等价

# 5. sqrt / exp / log (逐元素)
d = torch.tensor([4.0, 9.0, 16.0])
print(d.sqrt())     # tensor([2., 3., 4.])
print(d.exp())      # e^x
print(d.log())      # 以 e 为底
print(d.log2())     # 以 2 为底
print(d.log10())    # 以 10 为底

六、张量索引操作

从 0 开始的行列索引和高亮单元格, t[row, col]。

PyTorch Tensor Indexing and Slicinghttps://www.cosmiclearn.com/pytorch/tensor-indexing.php--推荐文章(索引专题讲解(含切片/高级索引/布尔掩码对比表))

https://blog.wangxm.com/2026/08/torch-visualized-the-operations/https://blog.wangxm.com/2026/08/torch-visualized-the-operations/--推荐文章(50 个 PyTorch 操作,每个一张图,全部在同一坐标系下------淡色块是输入、实色块是输出,代码面板附带形状变化)

6.1 准备数据

python 复制代码
import torch

# 随机种子固定为 24: 这个数字本身没有特殊含义, 任意整数都行,
# 目的只是让每次运行生成同一批随机数, 保证运行结果一致
torch.manual_seed(24)
t1 = torch.randint(1, 10, (5, 5))   # [1, 10) 左闭右开 -> 元素是 1~9, 形状 5 行 5 列
print(t1)
# tensor([[6, 9, 9, 2, 8],
#         [7, 8, 5, 8, 4],
#         [7, 4, 3, 9, 3],
#         [6, 1, 4, 2, 8],
#         [1, 2, 5, 7, 4]])

6.2 简单行、列索引

语法格式:t1[行, 列],逗号前后各写一个"行/列的下标或范围";省略就表示全取。

python 复制代码
print(t1[1])       # 第 1 行 (下标从 0 数, 即人脑理解第二行,上一行是第0行)
# tensor([7, 8, 5, 8, 4])
print(t1[1, :])    # 等价写法: 第 1 行的所有列
# tensor([7, 8, 5, 8, 4])
print(t1[:, :])    # 注意: 行列都省略 = 整个张量, 不是"某一行"
# (打印结果和 t1 一模一样, 一个都没筛)
print(t1[:, 2])    # 所有行的第 2 列 (第三列)
# tensor([9, 5, 3, 4, 5])

6.3 列表索引

语法格式:t1[行下标列表, 列下标列表],示例t1[[行1, 行2], [列1, 列2]]逗号前后写的不再是单个下标或切片,而是一组下标组成的列表 (如 [0, 1]指的是第0行和第一行);某一方省略则该方全取(如 t1[[0, 2]] 取第 0、2 两整行)。

配对规则:两个列表按位置一一配对 成坐标------[0,1] 配 [1,2] (这个示例数据恰好取行,行,列,列和和原本数据一致)得到坐标 (0,1) 和 (1,2),每个坐标各取一个元素,所以返回 2 个值。它取的是"两个点",不是"第 0、1 行 × 第 1、2 列"的整块区域!

python 复制代码
print(t1[[0, 1], [1, 2]])   # 坐标 (0,1), (1,2) 两个元素
# tensor([9, 5])        即 t1[0,1]=9, t1[1,2]=5
# 以下为原列表 便于查看
# tensor([[6, 9, 9, 2, 8],
#         [7, 8, 5, 8, 4],
#         [7, 4, 3, 9, 3],
#         [6, 1, 4, 2, 8],
#         [1, 2, 5, 7, 4]])

print(t1[[1, 3], [2, 4]])   # 坐标 (1,2), (3,4) 两个元素
# tensor([5, 8])        即 t1[1,2]=5, t1[3,4]=8

# 需求: 取第 0、1 行的第 1、2 列, 共 4 个元素 -> 一块 2x2 区域
# 行索引写成列向量 [[0],[1]], 它与 [1,2] 自动组合出所有坐标:
# (0,1) (0,2) / (1,1) (1,2), 结果排成 2x2
print(t1[[[0], [1]], [1, 2]])
# tensor([[9, 9],
#         [8, 5]])

6.4 范围索引(切片)

语法格式:[start:stop:step],包左不包右(stop 不包含),step 是步长;留空表示"从头/到尾"。

python 复制代码
print(t1[:3, :2])     # 前 3 行的前 2 列 (行 0~2, 列 0~1)
# tensor([[6, 9],
#         [7, 8],
#         [7, 4]])

# 以下为原列表 便于查看
# tensor([[6, 9, 9, 2, 8],
#         [7, 8, 5, 8, 4],
#         [7, 4, 3, 9, 3],
#         [6, 1, 4, 2, 8],
#         [1, 2, 5, 7, 4]])

print(t1[1:, :2])     # 第 1 行到最后一行的前 2 列
# tensor([[7, 8],
#         [7, 4],
#         [6, 1],
#         [1, 2]])
print(t1[:2, :2])     # 第 0、1 行的第 0、1 列, 共 4 个元素
# tensor([[6, 9],
#         [7, 8]])
print(t1[1::2, ::2])  # 行从 1 开始步长 2 -> 第 1、3 行(奇数行)
                      # 列步长 2 -> 第 0、2、4 列(偶数列)
# tensor([[7, 5, 4],
#         [6, 4, 8]])

6.5 布尔索引

知识点:条件表达式(如 > 5)会生成一个布尔张量 (True/False);把它放进 [] 就变成了掩码------只保留 True 对应的位置。

python 复制代码
# 需求1: 第 3 列大于 5 的行数据
# 拆开看: t1[:, 3] 取出第 3 列 [2,8,9,2,7] -> >5 得到 [F,T,T,F,T]
#        -> 当作行掩码, 第 1、2、4 行被保留
print(t1[t1[:, 3] > 5])
# tensor([[7, 8, 5, 8, 4],
#         [7, 4, 3, 9, 3],
#         [1, 2, 5, 7, 4]])

# 需求2: 第 1 行大于 5 的数据, 两种写法结果不同
# 第 1 行 = [7,8,5,8,4] -> >5 -> [T,T,F,T,F] -> 保留第 0、1、3 列
print(t1[:, t1[1] > 5])      # 所有行 × "第1行>5"的那些列 -> 二维 (5, 3)
# tensor([[6, 9, 2],
#         [7, 8, 8],
#         [7, 4, 9],
#         [6, 1, 2],
#         [1, 2, 7]])
print(t1[1, t1[1, :] > 5])   # 只取第 1 行中 >5 的值       -> 一维 (3,)
# tensor([7, 8, 8])
# 区别: 逗号前是 [:] 还是 [1], 决定了"哪些行"参与

6.6 多维索引

知识点:轴(维度)按 shape 从左到右编号 。形状 (2, 3, 4):0 轴长度 2、1 轴长度 3、2 轴长度 4。可以想象成 2 张 3×4 的表格摞在一起:0 轴是"第几张表",1 轴是"第几行",2 轴是"第几个"。

python 复制代码
t2 = torch.randint(1, 10, (2, 3, 4))
print(t2)
# tensor([[[3, 4, 6, 5],
#          [8, 8, 8, 3],
#          [4, 9, 6, 7]],
#
#         [[2, 8, 8, 5],
#          [6, 4, 2, 2],
#          [2, 7, 9, 4]]])

print(t2[0, :, :])   # 0 轴取第 0 个: 一张完整的 3x4 表格
# tensor([[3, 4, 6, 5],
#         [8, 8, 8, 3],
#         [4, 9, 6, 7]])
print(t2[:, 0, :])   # 1 轴取第 0 个: 每张表各取第 0 行 -> 2x4
# tensor([[3, 4, 6, 5],
#         [2, 8, 8, 5]])
print(t2[:, :, 0])   # 2 轴取第 0 个: 每行取第 0 个元素 -> 2x3
# tensor([[3, 8, 4],
#         [2, 6, 2]])

小结:

索引类型 示例 要点
简单行列索引 data[0]、data[:, 0] t[行, 列],省略即全取
列表索引 data[[0, 1], [1, 2]] 行列列表按位置配对成坐标,取"点"
范围索引 data[:3, :2]、data[2:, :2] 切片包左不包右,支持 step
布尔索引 data[data[:, 2] > 5] 条件生成掩码,保留 True 的行/列
多维索引 data[0, :, :]、data[:, 0, :] 轴按 shape 从左到右编号
相关推荐
sell-cloud1 小时前
Prompt Engineering要凉了?Fable 5.5能“猜“意图,但银行正在封杀AI Agent——企业AI的真正护城河是什么
人工智能·prompt
Navicat中国2 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI3 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区3 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris5 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy5 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V5 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI5 小时前
AI英语教培智能体的开发
人工智能
物联网软硬件开发-轨物科技5 小时前
【技术白皮书】光伏清检一体化机器人系统解决方案技术白皮书
大数据·运维·网络·人工智能·机器人
gCode Teacher 格码致知5 小时前
Pandas教学-17:Series与Dataframe的关系
开发语言·python·pandas