一、Tensor的创建
在Torch中张量以 "类" 的形式封装起来,对张量的一些运算、处理的方法被封装在类中,官方文档:
torch — PyTorch 2.7 documentation
1. 基本创建方式
以下讲的创建tensor的函数中有两个有默认值的参数dtype和device, 分别代表数据类型和计算设备,可以通过属性dtype和device获取。
1.1 torch.tensor
注意这里的tensor是小写,该API是根据指定的数据创建张量。
import torch
import numpy as npdef test01():device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# tensor数据类型根据输入的数据类型自动判断# dtype:指定数据类型# device:指定计算设备t = torch.tensor([1, 2, 3], device=device, dtype=torch.float)print(t)print(t.dtype)print(t.device)# 通过numpy创建t1 = torch.tensor(np.array([1, 2, 3, 4]))print(t1)# 通过标量创建t2 = torch.tensor(5)print(t2)if __name__ == '__main__':test01()
注:如果出现如下错误:
UserWarning: Failed to initialize NumPy: _ARRAY_API not found
一般是因为numpy和pytorch版本不兼容,可以降低numpy版本,步骤如下:
1.anaconda prompt中切换虚拟环境:
conda activate 虚拟环境名称
2.卸载numpy:
pip uninstall numpy
3.安装低版本的numpy:
pip install numpy==1.26.0
1.2 torch.Tensor
注意这里的Tensor是大写,该API根据形状创建张量,其也可用来创建指定数据的张量。
import torch
import numpy as npdef test01():device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# tensor数据类型根据输入的数据类型自动判断# dtype:指定数据类型# device:指定计算设备t = torch.tensor([1, 2, 3], device=device, dtype=torch.float)print(t)print(t.dtype)print(t.device)# 通过numpy创建t1 = torch.tensor(np.array([1, 2, 3, 4]))print(t1)# 通过标量创建t2 = torch.tensor(5)print(t2)def test02():# 不支持数据类型dtype属性# 不支持device属性# 指定形状创建t =torch.Tensor(2, 3)print(t)print(t.dtype)print(t.device)# 通过数组创建t1 = torch.Tensor([1, 2, 3])print(t1)# 3. 使用linspace创建等差序列张量# 创建从1到10(包含10)的10个等间隔元素组成的张量t2 = torch.linspace(1, 10, 10)print(t2)if __name__ == '__main__':# test01()test02()
torch.Tensor与torch.tensor区别
特性 | torch.Tensor() | torch.tensor() |
---|---|---|
数据类型推断 | 强制转为 torch.float32 | 根据输入数据自动推断(如整数→int64 ) |
显式指定 dtype | 不支持 | 支持(如 dtype=torch.float64 ) |
设备指定 | 不支持 | 支持(如 device='cuda' ) |
输入为张量时的行为 | 创建新副本(不继承原属性) | 默认共享数据(除非 copy=True ) |
推荐使用场景 | 需要快速创建浮点张量 | 需要精确控制数据类型或设备 |
1.3 torch.IntTensor
用于创建指定类型的张量,还有诸如Torch.FloatTensor、 torch.DoubleTensor、 torch.LongTensor......等。
如果数据类型不匹配,那么在创建的过程中会进行类型转换,要尽可能避免,防止数据丢失。
import torch
def test003():# 1. 创建指定形状的张量tt1 = torch.IntTensor(2, 3)print(tt1)
tt2 = torch.FloatTensor(3, 3)print(tt2, tt2.dtype)tt3 = torch.DoubleTensor(3, 3)print(tt3, tt3.dtype)tt4 = torch.LongTensor(3, 3)print(tt4, tt4.dtype)tt5 = torch.ShortTensor(3, 3)print(tt5, tt5.dtype)
if __name__ == "__main__":test003()
2. 创建线性和随机张量
在 PyTorch 中,可以轻松创建线性张量和随机张量。
2.1 创建线性张量
使用torch.arange 和 torch.linspace 创建线性张量:
import torch
import numpy as np
# 不用科学计数法打印
torch.set_printoptions(sci_mode=False)
def test004():# 1. 创建线性张量r1 = torch.arange(0, 10, 2)print(r1)# 2. 在指定空间按照元素个数生成张量:等差r2 = torch.linspace(3, 10, 10)print(r2)r2 = torch.linspace(3, 10000000, 10)print(r2)
if __name__ == "__main__":test004()
2.2 随机张量
使用torch.randn 创建随机张量。
2.2.1 随机数种子
随机数种子(Random Seed)是一个用于初始化随机数生成器的数值。随机数生成器是一种算法,用于生成一个看似随机的数列,但如果使用相同的种子进行初始化,生成器将产生相同的数列。
2.2.2 随机张量
在 PyTorch 中,种子影响所有与随机性相关的操作,包括张量的随机初始化、数据的随机打乱、模型的参数初始化等。通过设置随机数种子,可以做到模型训练和实验结果在不同的运行中进行复现。
import torch
import numpy as npdef test03():# 随机数种子:每次运行生成随机数相同torch.manual_seed(0)# 随机生成符合标准正态分布的数,均值-0,方差-1t = torch.randn(2, 3)print(t)# 生成随机整数# start# end# sizet = torch.randint(0, 10, (2, 3))print(t)# shape和size():表示张量的形状print(t.shape)print(t.size())# 生成0-1之间的随机数# size:形状t = torch.rand(2, 3)print(t)
注:不设置随机种子时,每次打印的结果不一样。
五、Tensor常见属性
张量有device、dtype、shape等常见属性,知道这些属性对我们认识Tensor很有帮助。
1. 获取属性
掌握代码调试就是掌握了一切~import torch
def test001():data = torch.tensor([1, 2, 3])print(data.dtype, data.device, data.shape)
if __name__ == "__main__":test001()
2. 切换设备
import torchdef test04():# 设备切换方法演示:CPU与GPU之间的张量迁移# 1. 首先判断当前环境是否有可用的CUDA(GPU)# 若有则使用GPU设备,否则使用CPU设备device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 方法1:创建张量时,通过device参数显式指定设备# 直接在创建张量时将其分配到指定设备(GPU优先,无则CPU)t = torch.tensor([1, 2, 3], device=device)print(t) # 打印张量,可观察设备信息(如cuda:0或cpu)# 方法2:使用to()方法切换设备# 先创建一个默认在CPU上的张量t1 = torch.tensor([1, 2, 3])# 通过to(device)将张量迁移到目标设备(与上面的device一致)t1 = t1.to(device)print(t1) # 此时t1已迁移到指定设备# 方法3:使用cuda()方法直接迁移到GPU(若可用)# 创建默认在CPU上的张量t2 = torch.tensor([1, 2, 3])# 使用cuda()方法将张量迁移到GPU(若GPU不可用会报错)t2 = t2.cuda()print(t2) # 若有GPU,此时t2在GPU上# 方法3补充:使用cpu()方法迁移到CPU# 将GPU上的t2迁移回CPUt3 = t2.cpu()print(t3) # 此时t3在CPU上
六、Tensor数据转换
import numpy as np
import torchdef test01():# 演示:PyTorch张量(tensor)转换为NumPy数组的两种方式(浅拷贝与深拷贝)# 创建一个PyTorch张量(1维张量,元素为[1,2,3])t = torch.tensor([1, 2, 3])print(t) # 打印原始张量# 方法1:使用numpy()方法将张量转为NumPy数组# 注意:这是浅拷贝(共享内存),修改数组会同步影响原张量a = t.numpy()print(a) # 打印转换后的NumPy数组# 修改NumPy数组的第一个元素a[0] = 100# 查看原张量是否被影响(由于浅拷贝,张量的值会同步改变)print(t) # 输出 tensor([100, 2, 3])# 方法2:使用numpy().copy()进行深拷贝# 深拷贝会创建独立副本,修改副本不会影响原张量b = t.numpy().copy()# 修改深拷贝得到的数组b[0] = 200# 查看原张量是否被影响(深拷贝不会影响,张量保持之前的值)print(t) # 输出 tensor([100, 2, 3])# 演示:NumPy数组转换为PyTorch张量的两种方式
def test02():# 创建一个NumPy数组(元素为[1,2,3])a = np.array([1, 2, 3])print(a) # 打印原始NumPy数组# 方法1:使用torch.from_numpy(a)将NumPy数组转为张量# 注意:这是浅拷贝(共享内存),修改张量会同步影响原数组t = torch.from_numpy(a)print(t) # 打印转换后的张量# 修改张量的第一个元素t[0] = 10# 查看原NumPy数组是否被影响(浅拷贝会同步改变)print(a) # 输出 [10 2 3]# 方法2:使用torch.tensor(a)将NumPy数组转为张量# 这是深拷贝(创建独立副本),修改张量不会影响原数组t1 = torch.tensor(a)# 修改深拷贝得到的张量t1[0] = 100# 查看原NumPy数组是否被影响(深拷贝不会影响,数组保持之前的值)print(a) # 输出 [10 2 3]if __name__ == '__main__':# 若取消注释则执行test01(张量转NumPy)# test01()# 执行test02(NumPy转张量)test02()
注释核心说明:
-
深浅拷贝区别:
- 浅拷贝(
tensor.numpy()
、torch.from_numpy()
):共享内存,一方修改会同步影响另一方 - 深拷贝(
tensor.numpy().copy()
、torch.tensor(numpy数组)
):创建独立副本,修改互不影响
- 浅拷贝(
-
转换方向:
- 张量→NumPy:
tensor.numpy()
(浅)、tensor.numpy().copy()
(深) - NumPy→张量:
torch.from_numpy()
(浅)、torch.tensor()
(深)
- 张量→NumPy:
-
print作用:通过输出结果直观展示修改操作对原数据的影响,验证深浅拷贝特性
七、Tensor常见操作
在深度学习中,Tensor是一种多维数组,用于存储和操作数据,我们需要掌握张量各种运算。
import torchdef test01():# 演示item()方法:获取单个元素张量的值# 适用场景:无论张量维度如何,只要内部只有一个元素,均可使用item()提取其Python标量值# 创建0维张量(单个元素)t = torch.tensor(10)print(t.item()) # 输出:10(提取单个元素值)# 创建2维张量(形状为[1,1],仍只有一个元素)t1 = torch.tensor([[10]])print(t1.item()) # 输出:10(即使是高维单元素张量,也能提取值)def test02():# 演示PyTorch中运算方法的两种形式:# 1. 不带下划线的方法(如add):返回新张量,不修改原张量# 2. 带下划线的方法(如add_):原地修改原张量,返回修改后的自身# 创建初始张量t = torch.tensor(10)# 使用add():返回新张量(原张量t不变)t1 = t.add(1) # 等价于 t + 1print(t1) # 输出:tensor(11)(新张量)print(t) # 输出:tensor(10)(原张量未修改)# 使用add_():原地修改原张量print(t.add_(2)) # 输出:tensor(12)(修改后的t)print(t) # 输出:tensor(12)(原张量已被修改)def test03():# 演示阿达码积(元素-wise乘法)# 前提:两个张量必须形状相同# 运算规则:对应位置的元素直接相乘(C[i][j] = A[i][j] * B[i][j])# 可用运算符:mul()方法 或 * 符号# 创建两个形状相同的2D张量t1 = torch.tensor([[1, 2, 3], [4, 5, 6]])t2 = torch.tensor([[1, 2, 3], [4, 5, 6]])# 计算阿达码积(两种等价方式)# t3 = t1.mul(t2) # 使用mul()方法t3 = t1 * t2 # 使用*运算符print(t3) # 输出:[[1, 4, 9], [16, 25, 36]](对应元素相乘结果)def test04():# 演示矩阵乘法(线性代数中的矩阵乘积)# 前提:第一个张量形状为(m×n),第二个为(n×p),即前一个的列数=后一个的行数# 运算规则:第一个张量的行与第二个张量的列对应元素相乘后求和,结果为(m×p)形状# 可用运算符:matmul()方法 或 @ 符号# 创建两个符合矩阵乘法维度要求的张量t1 = torch.tensor([[1, 2, 3], [4, 5, 6]]) # 形状:2×3t2 = torch.tensor([[1, 1], [2, 2], [3, 3]]) # 形状:3×2# 计算矩阵乘法(两种等价方式)# t3 = t1 @ t2 # 使用@运算符t3 = t1.matmul(t2) # 使用matmul()方法print(t3) # 输出:[[14, 14], [32, 32]](矩阵乘积结果,形状2×2)def test05():# 演示view()方法:修改张量形状(重塑)# 前提:1. 张量在内存中必须是连续的(is_contiguous()返回True);2. 重塑前后总元素数必须相同# 连续性:数据在内存中按C顺序存储(通常新建张量都是连续的,转置等操作可能破坏连续性)# -1作为占位符:自动计算该维度的大小(需保证总元素数不变)# 创建2×3的连续张量t1 = torch.tensor([[1, 2, 3], [4, 5, 6]])print(t1.is_contiguous()) # 输出:True(新建张量默认连续)# 交换维度(0和1维度交换,形状变为3×2)t2 = t1.transpose(1, 0) # 等价于t1.T(转置)print(t2) # 输出:[[1, 4], [2, 5], [3, 6]]print(t2.is_contiguous()) # 输出:False(转置后张量不连续)# 尝试用view()重塑不连续张量(可能报错,视PyTorch版本而定)# 注意:实际运行时可能因版本优化不报错,但不建议对不连续张量使用view()t3 = t2.view(2, 3)print(t3) # 输出:[[1, 4, 2], [5, 3, 6]](内存顺序未变,重塑后值可能不符合预期)def test06():# 演示维度交换方法:# 1. transpose(dim1, dim2):一次只能交换两个维度# 2. permute(dims):可同时交换任意多个维度(需指定新的维度顺序)# 创建3×4×5的3D随机整数张量(值范围1-9)t1 = torch.randint(1, 10, (3, 4, 5))print(t1.size()) # 输出:torch.Size([3, 4, 5])(原维度:[0,1,2])# 使用transpose交换维度1和0(原维度[0,1,2] → [1,0,2])t2 = torch.transpose(t1, 1, 0)print(t2.size()) # 输出:torch.Size([4, 3, 5])(交换后维度)# 使用permute同时交换多个维度(原维度[0,1,2] → [1,2,0])t3 = torch.permute(t1, (1, 2, 0))print(t3.size()) # 输出:torch.Size([4, 5, 3])(新维度顺序)def test07():# 演示维度调整方法:# 1. squeeze(dim):删除尺寸为1的维度(默认删除所有尺寸为1的维度)# 2. unsqueeze(dim):在指定位置添加一个尺寸为1的维度# 创建形状为(1, 3, 1)的3D张量(含两个尺寸为1的维度)t1 = torch.randint(1, 10, (1, 3, 1))# 不指定dim:删除所有尺寸为1的维度(1×3×1 → 3)t2 = torch.squeeze(t1)print(t2.size()) # 输出:torch.Size([3])# 指定dim=0:仅删除第0维(尺寸为1)(1×3×1 → 3×1)t3 = torch.squeeze(t1, dim=0)print(t3.size()) # 输出:torch.Size([3, 1])# 指定dim=1:第1维尺寸为3(非1),不做操作(保持原形状)t4 = torch.squeeze(t1, dim=1)print(t4.size()) # 输出:torch.Size([1, 3, 1])# 在dim=0位置添加新维度(1×3×1 → 1×1×3×1)t5 = torch.unsqueeze(t1, dim=0)print(t5.size()) # 输出:torch.Size([1, 1, 3, 1])def test08():# 演示广播机制(Broadcasting):# 当两个张量形状不同时,PyTorch会自动扩展维度使它们兼容,以便进行元素-wise运算# 规则:从最后一个维度开始匹配,维度要么相同,要么其中一个为1,要么其中一个不存在# 创建1D张量(形状[3])和2D张量(形状[3,1])data1d = torch.tensor([1, 2, 3])data2d = torch.tensor([[4], [2], [3]])print(data1d.shape, data2d.shape) # 输出:torch.Size([3]) torch.Size([3, 1])# 广播机制自动扩展:# data1d扩展为[3,3](复制行),data2d扩展为[3,3](复制列),再相加print(data1d + data2d) # 输出:[[5,6,7], [3,4,5], [4,5,6]]def test09():# 演示高维张量的广播机制# 创建2D张量(形状[2,3])和3D张量(形状[2,1,3])a = torch.tensor([[1, 2, 3], [4, 5, 6]]) # 2×3b = torch.tensor([[[2, 3, 4]], [[5, 6, 7]]]) # 2×1×3print(a.shape, b.shape) # 输出:torch.Size([2, 3]) torch.Size([2, 1, 3])# 广播机制:# a自动扩展为[2,1,3](添加中间维度),与b形状匹配后相加result = a + bprint(result, result.shape) # 输出结果形状:torch.Size([2, 1, 3])if __name__ == '__main__':# 取消对应注释可运行不同测试函数# test01() # 测试item()方法# test02() # 测试原地/非原地运算# test03() # 测试阿达码积# test04() # 测试矩阵乘法# test05() # 测试view()重塑# test06() # 测试维度交换# test07() # 测试维度调整(squeeze/unsqueeze)# test08() # 测试广播机制(低维)test09() # 测试广播机制(高维)
八、总结
一、核心知识点梳理:构建 Tensor 知识体系
1. Tensor 的创建:从基础到灵活运用
Tensor 的创建是入门第一步,需重点掌握不同方法的适用场景:
-
基础创建方法:
torch.tensor(data)
:推荐优先使用,支持自动推断数据类型(如整数默认 int64,浮点数默认 float32),可显式指定dtype
和device
(CPU/GPU),输入为 NumPy 数组时默认深拷贝。torch.Tensor(shape)
:大写开头,更适合按形状创建未初始化张量(值为内存随机值),默认数据类型为 float32,不支持直接指定device
,灵活性低于torch.tensor
。- 类型专属创建:如
torch.IntTensor
(整数型)、torch.FloatTensor
(浮点型)等,适合需要固定数据类型的场景,但需注意类型不匹配时的自动转换可能导致数据丢失。
-
特殊张量创建:
- 线性张量:
torch.arange(start, end, step)
(按步长生成序列)、torch.linspace(start, end, num)
(按元素个数生成等差序列),常用于生成索引或固定范围的测试数据。 - 随机张量:
torch.randn(shape)
(标准正态分布)、torch.randint(low, high, shape)
(整数随机数)、torch.rand(shape)
(0-1 均匀分布),配合torch.manual_seed(seed)
可固定随机数,保证实验可复现。
- 线性张量:
2. 张量属性:理解张量的 "身份信息"
张量的属性是操作的基础,需熟练掌握 3 个核心属性:
dtype
:数据类型(如torch.float32
、torch.int64
),决定计算精度和内存占用,需根据场景选择(如深度学习常用 float32,整数索引用 int64)。device
:计算设备(cpu
或cuda:0
),决定张量存储和计算的硬件,需通过torch.device('cuda' if torch.cuda.is_available() else 'cpu')
自动适配环境。shape/size()
:张量的维度信息(如(2,3)
表示 2 行 3 列),是后续形状修改、运算的前提。
3. 设备切换:打通 CPU 与 GPU 的计算链路
深度学习中需频繁在 CPU 和 GPU 间迁移张量,3 种核心方法:
- 创建时指定:
torch.tensor(data, device=device)
,直接在目标设备初始化张量,避免后续迁移开销。 to(device)
:通用方法,支持动态切换到任意设备(如tensor.to('cuda')
或tensor.to(device)
),推荐优先使用。- 快捷方法:
tensor.cuda()
(迁移到 GPU)、tensor.cpu()
(迁移到 CPU),但需注意cuda()
在无 GPU 环境下会报错,不如to()
灵活。
4. 数据转换:Tensor 与 NumPy 的 "双向奔赴"
PyTorch 与 NumPy 的交互是数据预处理的高频操作,需区分深浅拷贝:
- Tensor 转 NumPy:
tensor.numpy()
:浅拷贝,共享内存(修改一方会同步影响另一方),适合临时转换且不修改数据的场景。tensor.numpy().copy()
:深拷贝,创建独立副本,修改互不影响,适合需要独立操作的场景。
- NumPy 转 Tensor:
torch.from_numpy(numpy_arr)
:浅拷贝,共享内存,适合快速转换且需保持数据同步的场景。torch.tensor(numpy_arr)
:深拷贝,创建独立副本,推荐用于需要独立操作张量的场景(如避免原始数据被意外修改)。
5. 核心操作:从基础运算到维度调整
张量的操作是构建计算逻辑的核心,需重点掌握以下几类:
- 单元素提取:
tensor.item()
,用于从单元素张量(无论维度)中提取 Python 标量(如损失值的提取)。 - 运算类型:
- 元素 - wise 运算(阿达码积):
*
或mul()
,要求两张量形状相同,对应位置元素直接相乘。 - 矩阵乘法:
@
或matmul()
,要求前张量列数 = 后张量行数,结果为线性代数中的矩阵乘积。 - 原地 / 非原地运算:不带下划线的方法(如
add()
)返回新张量,带下划线的方法(如add_()
)原地修改原张量(节省内存但会覆盖原始数据)。
- 元素 - wise 运算(阿达码积):
- 形状与维度操作:
- 形状修改:
view(shape)
,要求张量连续(is_contiguous()
为 True)且总元素数不变,-1
可自动计算维度大小(如(2,3)
转(3,-1)
得到(3,2)
)。 - 维度交换:
transpose(dim1, dim2)
(交换两个维度)、permute(dims)
(交换任意多个维度,如(3,4,5)
转(4,5,3)
),常用于调整数据的维度顺序(如图像数据的(H,W,C)
转(C,H,W)
)。 - 维度调整:
squeeze(dim)
(删除尺寸为 1 的维度)、unsqueeze(dim)
(添加尺寸为 1 的维度),常用于匹配运算中的维度要求(如给标量添加批次维度)。
- 形状修改:
- 广播机制:当两张量形状不同时,PyTorch 会自动扩展维度使其兼容(规则:从最后一维开始匹配,维度需相同、其中一个为 1 或不存在),简化不同形状张量的元素 - wise 运算(如
(3,)
与(3,1)
相加时自动扩展为(3,3)
)。
二、学习重点与避坑点:这些细节决定熟练度
torch.tensor
vstorch.Tensor
:前者更灵活(支持device
和dtype
),后者默认 float32 且不支持设备指定,实际开发中优先用torch.tensor
。- 深浅拷贝的影响:浅拷贝(
numpy()
/from_numpy()
)共享内存,修改时需谨慎(尤其在数据预处理 pipeline 中);深拷贝(copy()
/torch.tensor()
)虽独立但内存开销更大,按需选择。 - 张量连续性:转置(
transpose
)等操作会导致张量不连续,此时使用view()
可能报错或得到异常结果,可先用contiguous()
转为连续张量(如tensor.transpose(0,1).contiguous().view(shape)
)。 - 随机种子的作用:
torch.manual_seed(seed)
可固定随机数生成,是复现实验结果的关键(如模型初始化、数据打乱),务必在代码开头设置。 - 广播机制的边界:广播虽方便,但过度依赖可能导致维度匹配混乱,建议运算前先用
shape
确认张量维度,避免隐式扩展带来的错误。
三、实践建议:从 "看懂" 到 "会用"
- 边学边练,聚焦调试:Tensor 的很多细节(如设备、数据类型)需通过调试发现,推荐用
print(tensor.dtype, tensor.device, tensor.shape)
打印属性,结合 IDE 断点观察张量变化。 - 针对性练习:
- 基础操作:用不同方法创建相同形状的张量,对比数据类型和内存占用。
- 设备迁移:在有 GPU 的环境下测试
to()
/cuda()
/cpu()
的效果,观察张量位置变化。 - 运算练习:用相同数据分别做元素 - wise 乘法和矩阵乘法,对比结果差异。
- 结合实际场景:在数据预处理(如将图像 NumPy 数组转为 Tensor 并迁移到 GPU)、模型推理(如调整输入张量维度匹配模型要求)中强化应用,将知识点与实际任务绑定。
总结
Tensor 是 PyTorch 的 "基石",其操作的熟练度直接影响代码效率和正确性。学习时需从「创建 - 属性 - 转换 - 操作」逐步深入,重点关注细节(如数据类型、设备、拷贝方式),通过大量实践将 API 的使用内化为直觉。掌握 Tensor 后,无论是搭建模型还是调试训练过程,都会更加得心应手。