《Day2-PyTorch Tensor 从入门到实践:核心操作与避坑指南》

发布于:2025-08-11 ⋅ 阅读:(32) ⋅ 点赞:(0)

一、Tensor的创建

在Torch中张量以 "类" 的形式封装起来,对张量的一些运算、处理的方法被封装在类中,官方文档:

torch — PyTorch 2.7 documentation

1. 基本创建方式

以下讲的创建tensor的函数中有两个有默认值的参数dtype和device, 分别代表数据类型和计算设备,可以通过属性dtype和device获取。

1.1 torch.tensor

注意这里的tensor是小写,该API是根据指定的数据创建张量。

import torch
import numpy as np


def 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 np


def 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)是一个用于初始化随机数生成器的数值。随机数生成器是一种算法,用于生成一个看似随机的数列,但如果使用相同的种子进行初始化,生成器将产生相同的数列。

import torch
def test01():
    # 设置随机数种子
    torch.manual_seed(0)
    # 获取随机数种子
    print(torch.initial_seed())

if __name__ == '__main__':
    test01()
2.2.2 随机张量

在 PyTorch 中,种子影响所有与随机性相关的操作,包括张量的随机初始化、数据的随机打乱、模型的参数初始化等。通过设置随机数种子,可以做到模型训练和实验结果在不同的运行中进行复现。

import torch
import numpy as np


def test03():
    # 随机数种子:每次运行生成随机数相同
    torch.manual_seed(0)
    # 随机生成符合标准正态分布的数,均值-0,方差-1
    t = torch.randn(2, 3)
    print(t)

    # 生成随机整数
    # start
    # end
    # size
    t = 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 torch

def 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迁移回CPU
    t3 = t2.cpu()
    print(t3)  # 此时t3在CPU上

3. 类型转换

在训练模型或推理时,类型转换也是张量的基本操作,是需要掌握的。

import torch


def test001():
    data = torch.tensor([1, 2, 3])
    print(data.dtype)  # torch.int64

    # 1. 使用type进行类型转换
    data = data.type(torch.float32)
    print(data.dtype)  # float32
    data = data.type(torch.float16)
    print(data.dtype)  # float16

    # 2. 使用类型方法
    data = data.float()
    print(data.dtype)  # float32
    # 16 位浮点数,torch.float16,即半精度
    data = data.half()
    print(data.dtype)  # float16
    data = data.double()
    print(data.dtype)  # float64
    data = data.long()
    print(data.dtype)  # int64
    data = data.int()
    print(data.dtype)  # int32

    #  使用dtype属性
    data = torch.tensor([1, 2, 3], dtype=torch.half)
    print(data.dtype)


if __name__ == "__main__":
    test001()

六、Tensor数据转换

1. Tensor与Numpy

Tensor和Numpy都是常见数据格式,惹不起~

1.1 张量转Numpy

此时分内存共享和内存不共享~

1.1.1 浅拷贝

调用numpy()方法可以把Tensor转换为Numpy,此时内存是共享的。

import torch


def test003():
    # 1. 张量转numpy
    data_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
    data_numpy = data_tensor.numpy()
    print(type(data_tensor), type(data_numpy))
    # 2. 他们内存是共享的
    data_numpy[0, 0] = 100
    print(data_tensor, data_numpy)


if __name__ == "__main__":
    test003()
1.1.2 深拷贝

使用copy()方法可以避免内存共享:

import torch


def test003():
    # 1. 张量转numpy
    data_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
    
    # 2. 使用copy()避免内存共享
    data_numpy = data_tensor.numpy().copy()
    print(type(data_tensor), type(data_numpy))
    
    # 3. 此时他们内存是不共享的
    data_numpy[0, 0] = 100
    print(data_tensor, data_numpy)


if __name__ == "__main__":
    test003()

1.2 Numpy转张量

也可以分为内存共享和不共享~

1.2.1 浅拷贝

from_numpy方法转Tensor默认是内存共享的

import numpy as np
import torch


def test006():
    # 1. numpy转张量
    data_numpy = np.array([[1, 2, 3], [4, 5, 6]])
    data_tensor = torch.from_numpy(data_numpy)
    print(type(data_tensor), type(data_numpy))

    # 2. 他们内存是共享的
    data_tensor[0, 0] = 100
    print(data_tensor, data_numpy)


if __name__ == "__main__":
    test006()
1.2.2 深拷贝

使用传统的torch.tensor()则内存是不共享的~

import numpy as np
import torch


def 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()

注释核心说明:

  1. 深浅拷贝区别:

    • 浅拷贝(tensor.numpy()torch.from_numpy()):共享内存,一方修改会同步影响另一方
    • 深拷贝(tensor.numpy().copy()torch.tensor(numpy数组)):创建独立副本,修改互不影响
  2. 转换方向:

    • 张量→NumPy:tensor.numpy()(浅)、tensor.numpy().copy()(深)
    • NumPy→张量:torch.from_numpy()(浅)、torch.tensor()(深)
  3. print作用:通过输出结果直观展示修改操作对原数据的影响,验证深浅拷贝特性

七、Tensor常见操作

在深度学习中,Tensor是一种多维数组,用于存储和操作数据,我们需要掌握张量各种运算。

import torch


def 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 + 1
    print(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×3
    t2 = 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×3
    b = torch.tensor([[[2, 3, 4]], [[5, 6, 7]]])  # 2×1×3
    print(a.shape, b.shape)  # 输出:torch.Size([2, 3]) torch.Size([2, 1, 3])
    
    # 广播机制:
    # a自动扩展为[2,1,3](添加中间维度),与b形状匹配后相加
    result = a + b
    print(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),可显式指定dtypedevice(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.float32torch.int64),决定计算精度和内存占用,需根据场景选择(如深度学习常用 float32,整数索引用 int64)。
  • device:计算设备(cpucuda: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_())原地修改原张量(节省内存但会覆盖原始数据)。
  • 形状与维度操作
    • 形状修改: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))。

二、学习重点与避坑点:这些细节决定熟练度

  1. torch.tensor vs torch.Tensor:前者更灵活(支持devicedtype),后者默认 float32 且不支持设备指定,实际开发中优先用torch.tensor
  2. 深浅拷贝的影响:浅拷贝(numpy()/from_numpy())共享内存,修改时需谨慎(尤其在数据预处理 pipeline 中);深拷贝(copy()/torch.tensor())虽独立但内存开销更大,按需选择。
  3. 张量连续性:转置(transpose)等操作会导致张量不连续,此时使用view()可能报错或得到异常结果,可先用contiguous()转为连续张量(如tensor.transpose(0,1).contiguous().view(shape))。
  4. 随机种子的作用torch.manual_seed(seed)可固定随机数生成,是复现实验结果的关键(如模型初始化、数据打乱),务必在代码开头设置。
  5. 广播机制的边界:广播虽方便,但过度依赖可能导致维度匹配混乱,建议运算前先用shape确认张量维度,避免隐式扩展带来的错误。

三、实践建议:从 "看懂" 到 "会用"

  1. 边学边练,聚焦调试:Tensor 的很多细节(如设备、数据类型)需通过调试发现,推荐用print(tensor.dtype, tensor.device, tensor.shape)打印属性,结合 IDE 断点观察张量变化。
  2. 针对性练习
    • 基础操作:用不同方法创建相同形状的张量,对比数据类型和内存占用。
    • 设备迁移:在有 GPU 的环境下测试to()/cuda()/cpu()的效果,观察张量位置变化。
    • 运算练习:用相同数据分别做元素 - wise 乘法和矩阵乘法,对比结果差异。
  3. 结合实际场景:在数据预处理(如将图像 NumPy 数组转为 Tensor 并迁移到 GPU)、模型推理(如调整输入张量维度匹配模型要求)中强化应用,将知识点与实际任务绑定。

总结

Tensor 是 PyTorch 的 "基石",其操作的熟练度直接影响代码效率和正确性。学习时需从「创建 - 属性 - 转换 - 操作」逐步深入,重点关注细节(如数据类型、设备、拷贝方式),通过大量实践将 API 的使用内化为直觉。掌握 Tensor 后,无论是搭建模型还是调试训练过程,都会更加得心应手。


网站公告

今日签到

点亮在社区的每一天
去签到