PyTorch 张量操作

什么是张量

张量(Tensor)是 PyTorch 里最核心的数据结构,可以把它理解成一种多维数组:0 维是标量,1 维是向量(Vector),2 维是矩阵(Matrix),更高维也都可以继续表示。

  • 和 NumPy 很像:基础用法接近 NumPy 数组。
  • 能力更强:可以通过 CUDA 放到 GPU 上加速。
张量的维度图解

1733817329667

多个二维张量组成三维张量

1733817373409

多个三维张量组成四维张量

多个四维张量组成五维张量

1733817419397

张量创建

#掌握:
	torch.tensor        # 根据现有数据创建张量
	torch.rand          # 创建 [0, 1) 均匀分布的随机张量
	torch.randint       # 创建指定范围的随机整数张量
	torch.manual_seed   # 设置随机种子(确保结果可复现)
	torch.zeros         # 创建全 0 张量
	torch.ones          # 创建全 1 张量
	torch.type          # 查看或转换张量的数据类型

基本创建方式

  • 在 torch 中 CPU 和 GPU 张量分别有 8 种数据类型:
    数据类型 dtype CPU tensor GPU tensor
    32位浮点型 torch.float32 / torch.float torch.FloatTensor torch.cuda.FloatTensor
    64位浮点型 torch.float64 / torch.double torch.DoubleTensor torch.cuda.DoubleTensor
    16位浮点型 torch.float16 / torch.half torch.HalfTensor torch.cuda.HalfTensor
    8位无符号整型 torch.uint8 torch.ByteTensor torch.cuda.ByteTensor
    8位有符号整型 torch.int8 torch.CharTensor torch.cuda.CharTensor
    16位有符号整型 torch.int16 / torch.short torch.ShortTensor torch.cuda.ShortTensor
    32位有符号整型 torch.int32 / torch.int torch.IntTensor torch.cuda.IntTensor
    64位有符号整型 torch.int64 / torch.long torch.LongTensor torch.cuda.LongTensor
  • 张量中默认的数据类型是float32(torch.FloatTensor)

PyTorch安装:

pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple
  • torch.tensor(data=, dtype=) 根据指定数据创建张量
  import torch  # 需要安装torch模块
  import numpy as np
  
  # 1. 创建张量标量
  data = torch.tensor(10)
  print(data)
  
  # 2. numpy 数组, 由于data为float64, 张量元素类型也是float64
  data = np.random.randn(2, 3)
  print(data,data.dtype)
  data = torch.tensor(data)
  print(data,data.dtype)
  
  # 3. 传递容器数据类型
  # 整数默认是int64
  data = torch.tensor([11,22,33])
  print(data,data.dtype)
  
  # 浮点数默认是float32
  data = torch.tensor([1.1, 2.2, 3.3])
  print(data,data.dtype)
  • torch.Tensor(size=) 根据形状创建张量, 其也可用来创建指定数据的张量
  # 创建2行3列的张量。元素类型默认是float32
  data_1 = torch.Tensor(2,3)
  print(data_1,data_1.dtype)
  
  # 注意:如果传递一个标量进去,实际是创建一个长度为5的向量
  data_1 = torch.Tensor(5)
  print(data_1, data_1.dtype)
  
  # 如果传递列表, 则创建包含指定元素的张量
  data_1 = torch.Tensor([5])
  print(data_1, data_1.dtype)
  
  data_1 = torch.Tensor([10, 20])
  print(data_1)
  • torch.IntTensor()/torch.FloatTensor() 创建指定类型的张量
  # 1. 创建2行3列,dtype 为 int32 的张量
  data_2 = torch.IntTensor(2,3)
  print(data_2,data_2.dtype)
  
  # 2. 可以通过传递列表,指定张量具体元素
  data_2 = torch.IntTensor([11,22,33])
  print(data_2, data_2.dtype)
  
  # 3. 注意:创建张量时,如果传递的元素值类型与张量类型不匹配,会自动进行类型转换(向下取整)
  data_2 = torch.IntTensor([11.1, 22.5, 33.9])
  print(data_2)
  
  # 4. 其他的类型
  data = torch.ShortTensor()  # int16
  print(data_2)
  data = torch.LongTensor()   # int64
  print(data_2)
  data = torch.FloatTensor()  # float32
  print(data_2)
  data = torch.DoubleTensor() # float64
  print(data_2)
  

线性和随机张量

线性张量:通俗来说,就是生成一个等差数列。你可以把它想象成爬楼梯,要么规定好每一步迈多大(固定步长),要么规定好一共走几步(固定数量),系统会自动把中间的台阶按等比例铺好。

左闭右开 是左侧的数包含,右侧的数不包含

  • torch.arange(start=, end=, step=):固定步长线性张量

  • torch.linspace(start=, end=, steps=):固定元素数量线性张量

  # arange区间是[start,end)左闭右开
  data_3 = torch.arange(start=1,end=10,step=2)
  print(data_3,data_3.dtype)
  
  # 生成一维张量。linspace区间是[start,end]左右都是闭区间。注意steps不表示步长,表示生成的元素个数
  data_3 = torch.linspace(start=1,end=10,steps=6)
  print(data_3)
  • torch.randn/rand(size=) 创建随机浮点类型张量
  • torch.randint(low=, high=, size=) 创建随机整数类型张量 左闭右开
  • torch.initial_seed()torch.manual_seed(seed=) 随机种子设置
  # 创建2行3列的随机值张量。元素值区间在[0,1)之间
  data_3 = torch.rand(2,3)
  print(data_3)
  
  # 创建2行3列的随机值张量。元素值符合标准正态分布
  data_3 = torch.randn(2,3)
  print(data_3)
  
  # 创建随机整数张量:
  # - low=1, high=10:取值范围是 [1, 10),即包含 1 但不包含 10(左闭右开)
  # - size=(2, 3):指定张量形状为 2 行 3 列
  data_3 = torch.randint(low=1,high=10,size=(2,3))
  print(data_3)
  
  # 查看随机数种子
  seed = torch.initial_seed()
  print(seed)
  
  # 手动设置随机数种子
  # 设置以后,生成的随机数将会固定
  torch.manual_seed(4)
  data_3 = torch.randn(2,3)
  print(data_3)
  

指定值张量

  • torch.zeros(size=) 和 torch.zeros_like(input=) 创建全0张量
  # 1. 创建指定形状2行3列,值全0张量
  data = torch.zeros(2, 3)
  print(data)
  
  # 2. 根据张量形状创建全0张量
  data = torch.zeros_like(data)
  print(data)
  • torch.ones(size=)torch.ones_like(input=) 创建全1张量
  # 1. 创建指定形状全1张量
  data = torch.ones(2, 3)
  print(data)
  
  # 2. 根据张量形状创建全1张量
  data = torch.ones_like(data)
  print(data)
  • torch.full(size=, fill_value=)torch.full_like(input=, fill_value=) 创建全为指定值张量
  # 创建全为指定值张量
  data_4 = torch.full(size=(2,3),fill_value=99)
  print(data_4)
  
  # 根据张量形状创建指定值的张量
  data_5 = torch.full_like(data_4, 20)
  print(data_5)

指定元素类型张量

  • data.type(dtype=)
  data = torch.full(size=(2,3),fill_value=10)
  print(data, data.dtype) #torch.int64
  
  # 神经网络中要求的数据类型就是float32
  data_1 = data.type(torch.float32)
  print(data_1, data_1.dtype) #torch.float32
  
  # 转换为其他类型
  data_1 = data.type(torch.float64)
  print(data_1)
  
  # 还有其他的写法
  data_1 = data.type(torch.FloatTensor)
  print(data_1)
  
  data_1 = data.type(torch.DoubleTensor)
  print(data_1)
  # data = data.type(torch.ShortTensor)
  # data = data.type(torch.IntTensor)
  # data = data.type(torch.LongTensor)
  # data = data.type(torch.FloatTensor)
  # data = data.type(dtype=torch.float16)
  
  • data.half/float/double/short/int/long()
  data = torch.full(size=(2,3),fill_value=10)
  print(data, data.dtype)
  
  # float16
  data_1 = data.half()
  print(data_1)
  
  # float64
  data_1 = data.double() 
  print(data_1)
  
  # int16
  data_1 = data.short()
  print(data_1)

张量类型转换

张量转换为NumPy数组

  • 使用 t.numpy() 函数可以将张量转换为 ndarray 数组,但是共享内存,可以使用 copy 函数避免共享。
  import torch
  import numpy as np
  
  # 张量 转 numpy的ndarray
  t_1 = torch.tensor([11,22,33])
  print(t_1, type(t_1))
  
  # 共享内存
  arr_1 = t_1.numpy()
  print(arr_1, type(arr_1))
  
  # 可以在后面使用copy(),不共享内存
  arr_2 = t_1.numpy().copy()
  print(arr_2, type(arr_2))
  
  t_1[0] = 100
  print(f"t_1={t_1},arr_1={arr_1},arr_2={arr_2}")

NumPy数组转换为张量

  • 使用 torch.from_numpy(ndarray=) 可以将ndarray数组转换为 tensor张量,默认共享内存,使用 copy 函数避免共享。
  • 使用 torch.tensor(data=) 可以将ndarray数组转换为tensor张量,默认不共享内存。
  # numpy的ndarray 转 张量
  arr = np.array([11,22,33])
  print(arr,type(arr))
  
  # 共享变量
  t_1 = torch.from_numpy(arr)
  print(t_1, type(t_1))
  
  # 不共享变量
  t_2 = torch.tensor(arr)
  print(t_2, type(t_2))
  
  arr[0] = 99
  print(f"arr={arr},t_1={t_1},t_2={t_2}")

提取标量张量的数值

  • 对于只有一个元素的张量,使用 item() 函数将该值从张量中提取出来
  # 标量 和 张量 互转
  # 1- 标量 转 张量
  # t_1 = torch.tensor(24)
  t_1 = torch.tensor([24])
  print(t_1, type(t_1))
  
  
  # 2- 张量 转 标量
  value = t_1.item()
  print(value, type(value))
  
  # 注意:张量中只有一个值的时候才能够使用item()
  t_2 = torch.tensor([11,22])
  print(t_2)
  
  values = t_2.item()
  # 会报错 : RuntimeError: a Tensor with 2 elements cannot be converted to Scalar
  print(values)

张量数值计算

掌握:+ - * / @

基本运算

加减乘除取负号:

  • +-*/-

  • add(other=)submuldivneg

  • add_(other=)sub_mul_div_neg_(其中带下划线的版本会修改原数据)

  import torch
  
  # 1---- 基本运算 ----
  t1 = torch.tensor([[1,2,3],[4,5,6]])
  print(t1)
  
  # 张量 和 数值运算,张量中每个元素都会和该数值进行运算
  t2 = t1 + 10
  print(t2)
  
  t3 = t1 * 10
  print(t3)
  
  # 运算函数
  # 不带下划线的函数,不会修改源数据
  # 下面的两种调用方式都行
  # t4 = torch.add(t3,10)
  t4 = t3.add(10)
  print(t3, "\n", t4)
  
  # 带下划线的函数,会修改源数据
  # 同时注意调用方式。只能这么调用
  t5 = t3.add_(10)
  print(t3, "\n", t5)
  
  # neg()、neg_()取反函数。正数变负数,负数变正数
  t1 = torch.tensor([[1, -2, 3], [4, -5, -6]])
  print(t1)
  
  # 不会修改源数据
  t2 = t1.neg()
  print(t1, "\n", t2)
  
  # 会修改源数据
  t3 = t1.neg_()
  print(t1, "\n", t3)
  
  # 其他函数
  t1 = torch.tensor([[1, -2, 3], [4, -5, -6]])
  print(t1.sub(100)) # 减法
  print(t1.mul(100)) # 乘法
  print(t1.div(100)) # 除法
  
 

点乘运算

点乘(Hadamard 积)也称为元素级乘积,指的是相同形状的张量对应位置的元素相乘,使用 mul 或运算符 * 实现。

例如:

$$ A = \begin{bmatrix} 1 & 2 \\\\ 3 & 4 \end{bmatrix}, B = \begin{bmatrix} 5 & 6 \\\\ 7 & 8 \end{bmatrix} $$

则 $A, B$ 的 Hadamard 积为:

$$ A \circ B = \begin{bmatrix} 1 \times 5 & 2 \times 6 \\\\ 3 \times 7 & 4 \times 8 \end{bmatrix} = \begin{bmatrix} 5 & 12 \\\\ 21 & 32 \end{bmatrix} $$
# 定义张量.   3行2列
t1 = torch.tensor([[1, 2], [3, 4], [5, 6]])

# 定义张量.   3行2列
t2 = torch.tensor([[7, 8], [9, 10], [11, 12]])
# t2 = torch.tensor([[7, 8], [9, 10]])
print(f't1: {t1}\n t2: {t2}')


# 点乘
# 要求:两个张量的形状要相同,
#否则报错(RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 0)
# 结果:对应位置元素相乘
t3 = t1 * t2
print(t3)

# 点乘函数mul,推荐直接用*
t4 = t1.mul(t2)
print(t4)

矩阵乘法运算

矩阵乘法运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。
使用 matmul 或运算符 @ 实现。

矩阵乘法详情:A2×3 × B3×4 = C2×4
矩阵 A (2行3列)
4
4
1
3
1
3
×
矩阵 B (3行4列)
3
1
2
3
3
3
2
2
2
2
2
2
=
结果 C (2行4列)
26
18
18
22
18
12
14
17
🔍 C11 = 26 的计算过程(A第1行 · B第1列)
4 × 3 + 4 × 3 + 1 × 2   =   12 + 12 + 2   =   26
📋 全部 8 个元素的点积计算
C₁₁ = 26
4×3 + 4×3 + 1×2 = 12+12+2
C₁₂ = 18
4×1 + 4×3 + 1×2 = 4+12+2
C₁₃ = 18
4×2 + 4×2 + 1×2 = 8+8+2
C₁₄ = 22
4×3 + 4×2 + 1×2 = 12+8+2
C₂₁ = 18
3×3 + 1×3 + 3×2 = 9+3+6
C₂₂ = 12
3×1 + 1×3 + 3×2 = 3+3+6
C₂₃ = 14
3×2 + 1×2 + 3×2 = 6+2+6
C₂₄ = 17
3×3 + 1×2 + 3×2 = 9+2+6
💡 矩阵乘法规则:左矩阵的与右矩阵的对应元素相乘再求和(点积)
结果矩阵维度:m行 × n列 乘 n行 × k列 = m行 × k列
  • 运算符 @ 用于进行两个矩阵的乘积运算
  • torch.matmul(input=, other=)
  # 3---------------- 张量的矩阵乘法 ----------------
  """
      矩阵乘法的总结:
          注意: 前一个矩阵的列数与后一个矩阵的行数,必须相同
  """
  
  torch.manual_seed(129)
  A = torch.randint(low=1, high=5, size=(2,3))    # 2行3列
  B = torch.randint(low=1, high=5, size=(3,4))    # 3行4列
  
  # 下面B的行与A的列不等,因此会报错
  # B = torch.randint(low=1, high=5, size=(2,4))    # 2行4列
  
  print(f"A-->{A}")
  print(f"B-->{B}")
  
  result_2 = A @ B    # 推荐掌握
  # result_2 = A.matmul(B)
  
  print(f"结果: {result_2}")

张量运算函数

  • tensor.mean(dim=):平均值

  • tensor.sum(dim=):求和。掌握

  • tensor.min/max(dim=):最小值/最大值

  • tensor.pow(exponent=):幂次方 $x^n$

  • tensor.sqrt():平方根

  • tensor.exp():指数 $e^x$

  • tensor.log():对数 以e为底

  import torch
  
  # 定义张量, 浮点型.
  t1 = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float)
  
  print(t1, t1.shape)
  
  # 1- sum求和
  # dim=0,按列求和
  r1 = t1.sum(dim=0)
  print(r1)
  
  # dim=1,按行求和
  r2 = t1.sum(dim=1)
  print(r2)
  
  # dim不设置值,对所有元素求和
  r3 = t1.sum()
  print(r3)
  
  
  # 2- 均值,元素数据类型必须是float,不能是整数
  t1 = torch.tensor([[1, 2, 3], [4, 5, 6]],dtype=torch.float32)
  # t1 = torch.tensor([[1, 2, 3], [4, 5, 6]],dtype=torch.int32)
  
  # r1 = t1.mean(dim=0)
  r1 = t1.mean(dim=1)
  print(r1)
  
  
  # 3- 平方/立方/平方根/e的n次幂/对数
  t1 = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float)
  
  print(t1.pow(2)) # 平方
  print(t1.pow(3)) # 立方
  print(t1.sqrt()) # 开根号
  print(t1.exp()) # e的n次幂,元素作为幂使用
  print(t1.log()) # 以e为底求对数
  print(t1.log2()) # 以2为底的对数
  print(t1.log10()) # 以10为底的对数
  print(torch.log(t1) / torch.log(torch.tensor(3))) # 以3为底的对数(了解)
  
 

:dim 与 keepdim

  • dim 表示按哪个维度聚合;支持负索引,-1 表示最后一个维度
  • dim 可为元组,一次性对多个维度求和
  • keepdim=True 会保留被聚合维度(形状变为 1),便于后续广播运算
  • dim=0按第0维(也就是行)计算,dim=1按第1维(也就是列)计算 (此说法仅适用2维张量)
多维张量计算原理示例
import torch

# ==================================================
# 1. 3维张量测试:形状 (2, 3, 4)
# 维度索引对应:
#   dim=0  → 第1个维度,大小为 2(最外层)
#   dim=1  → 第2个维度,大小为 3(中间层)
#   dim=2  → 第3个维度,大小为 4(最内层)
#   dim=-1 → 最后一个维度,等价于 dim=2
# ==================================================
print("=" * 50)
print("【3维张量测试】原始形状: (2, 3, 4)")
print("=" * 50)

# 使用 arange 生成 0~23 的连续整数(共 2*3*4=24 个数)
# 再用 reshape 塑造成 (2, 3, 4) 的形状
# 用连续数字的好处:方便你手动核对求和结果,直观看到维度变化
t3 = torch.arange(2 * 3 * 4).reshape(2, 3, 4)

print("原始张量内容:")
print(t3)
print(f"\n原始张量形状: {tuple(t3.shape)}")
print()

# 逐个维度执行 sum 聚合操作
# 核心规律:指定哪个 dim,运算后就会把该维度「压缩消失」
dim_list = [0, 1, 2, -1]
for d in dim_list:
    # 按维度 d 求和:该维度上的所有元素被加在一起,维度本身消失
    res = t3.sum(dim=d)

    # 打印结果
    print(f"▶ sum(dim={d})")
    print(f"  结果形状: {tuple(res.shape)}")
    print(res)
    print("-" * 30)
"""
# ==================================================
# 2. 4维张量测试:形状 (2, 3, 4, 5)
# 维度索引对应:
#   dim=0  → 第1维,大小 2
#   dim=1  → 第2维,大小 3
#   dim=2  → 第3维,大小 4
#   dim=3  → 第4维,大小 5
#   dim=-1 → 倒数第1维 = dim=3
#   dim=-2 → 倒数第2维 = dim=2
#   dim=-3 → 倒数第3维 = dim=1
# ==================================================
print("\n" + "=" * 50)
print("【4维张量测试】原始形状: (2, 3, 4, 5)")
print("=" * 50)

# 生成 0~119 的连续整数(共 2*3*4*5=120 个数),塑造成 4 维
t4 = torch.arange(2 * 3 * 4 * 5).reshape(2, 3, 4, 5)

print(f"原始张量形状: {tuple(t4.shape)}")
print(t4)
print("="*23)
# 测试正索引和负索引
res = t4.sum(dim=1)

# 直观展示:原始形状去掉对应位置的数字,就是结果形状
print(f"结果形状: {tuple(res.shape)}")
print(res)

print("\n💡 核心结论:")
print("  1. 维度编号从 0 开始,shape 元组的第几个位置,就是第几维")
print("  2. 聚合操作(sum/mean/max等)会「消掉」指定的那个维度")
print("  3. dim=-n 表示从后往前数第 n 个维度,dim=-1 永远是最后一维")

"""
三维张量原理图
三维张量 sum(dim) 计算原理
示例张量 t9,shape = (2, 3, 4) → 2个块 × 3行 × 4列
📦 原始张量结构
第 0 块 (dim=0 索引0)
4
4
1
3
1
3
3
1
2
3
3
3
第 1 块 (dim=0 索引1)
2
2
2
2
2
2
1
2
1
4
2
2
dim=0:块维度(深度)  |  dim=1:行维度(高度)  |  dim=2:列维度(宽度)
1. sum(dim=0):沿「块维度」求和
4
4
1
3
1
3
3
1
2
3
3
3
+
2
2
2
2
2
2
1
2
1
4
2
2
=
6
6
3
5
3
5
4
3
3
7
5
5
4 + 2 = 6      两个块「同行同列」的元素对应相加
✅ 结果 shape:(3, 4)(消除 dim=0,剩余行和列维度)
2. sum(dim=1):沿「行维度」求和
第 0 块内按列求和
4
4
1
3
1
3
3
1
2
3
3
3
↓ 得到 1 行 4 列
第 1 块内按列求和
2
2
2
2
2
2
1
2
1
4
2
2
↓ 得到 1 行 4 列
结果 (2行4列)
7
10
7
7
5
8
5
6
4 + 1 + 2 = 7      每个块内,「同一列」的 3 个元素纵向相加
✅ 结果 shape:(2, 4)(消除 dim=1,剩余块和列维度)
3. sum(dim=2) / sum(dim=-1):沿「列维度」求和
第 0 块内按行求和
4
4
1
3
1
3
3
1
2
3
3
3
↓ 得到 3 行 1 列
第 1 块内按行求和
2
2
2
2
2
2
1
2
1
4
2
2
↓ 得到 3 行 1 列
结果 (2行3列)
12
8
11
8
7
9
4 + 4 + 1 + 3 = 12      每行内的 4 个元素横向相加;dim=-1 等价于最后一维 dim=2
✅ 结果 shape:(2, 3)(消除 dim=2,剩余块和行维度)
💡 核心规律总结
1. dim 指哪就沿哪条轴求和,求和后该维度会被「消除压缩」,剩余维度顺序不变
2. 负索引从后往前数:dim=-1 = 最后一维,dim=-2 = 倒数第二维
3. 对于 shape=(2,3,4):
  • sum(dim=0) → shape=(3,4)
  • sum(dim=1) → shape=(2,4)
  • sum(dim=2) → shape=(2,3)
  • sum() 不指定dim → 全部元素求和,得到标量
import torch

t = torch.tensor([[1, 2, 3],
                  [4, 5, 6]], dtype=torch.float32)
print(t, t.shape)            # torch.Size([2, 3])

# 1) 单维聚合
print(t.sum(dim=0))          # 按列求和 -> [5., 7., 9.]
print(t.sum(dim=1))          # 按行求和 -> [6., 15.]
print(t.sum(dim=-1))         # 等价于 dim=1(最后一维)

# 2) 多维聚合(元组)
print(t.sum(dim=(0, 1)))     # 对 0、1 两个维度同时聚合,等价于 t.sum()

# 3) 保留维度
print(t.sum(dim=1, keepdim=True).shape)  # torch.Size([2, 1])
print(t.sum(dim=0, keepdim=True).shape)  # torch.Size([1, 3])

张量索引操作

掌握:范围索引	

我们在操作张量时,经常需要去获取某些元素就进行处理或者修改操作,在这里我们需要了解在torch中的索引操作。

原理图
图片
import torch

# 设置随机种子
torch.manual_seed(4)

# 创建张量
t1 = torch.randint(1, 10, (4,5))
print(t1)

# 1---- 行列索引 ----
print(t1[0]) # 获取第一行
print("-"*30)
print(t1[:, 0]) # 获取第一列
print("-"*30)
print(t1[2,4]) # 获取 第3行和第5列限定的内容

# 2---- 列表索引 ----
# 需求1: 返回(0, 1), (1, 2)两个位置的元素
# [0,1]第0行、第1行
# [1,2]第1列、第2列
print(t1[[0,1], [1,2]])
print("-"*30)

# 需求2: 返回(0, 3), (2,4)两个位置的元素.
print(t1[[0,2], [3,4]])
print("-"*30)

# 需求3: 获取第0行的 第3列和第4列; 第2行的 第3列和第4列
# 共计: 4个元素
print(t1[[[0], [2]], [3,4]])

# 3---- 范围索引 ----
# 含头不含尾
# 需求1: 前3行, 前2列
print(t1[:3, :2])

# 需求2: 第2行到最后, 前2列
print(t1[2:, :2])

# 4---- 布尔索引 ----
# 需求1: 第3列中值大于等于5,对应行数据
print(t1[:, 2]>=5)
print("-"*30)

print(t1[torch.tensor([True, True, False, True])])
print("-"*30)

print(t1[t1[:, 2]>=5])

# 需求2: 第2行中值大于等于5,对应列数据
print(t1[2]>=5)
print("-"*30)

print(t1[:,torch.tensor([ True,  True, False,  True,  True])])
print("-"*30)

print(t1[:,t1[2]>=5])

# 5---- 多维索引 ----
t2 = torch.randint(1, 10, (3, 4, 5))
print(t2)
print("-"*30)

# 获取0轴上的第1个数据.
print(t2[0, :, :])
print("-"*30)

# 获取1轴上的第1个数据.
print(t2[:, 0, :])
print("-"*30)

# 获取2轴上的第1个数据.
print(t2[:, :, 0])

张量形状操作*

掌握:
	reshape     # 重新调整张量形状,不改变元素总数
	squeeze     # 删除长度为 1 的维度,常用于降维
	unsqueeze   # 在指定位置增加长度为 1 的维度,常用于升维
	transpose   # 交换两个维度的位置
	permute     # 按指定顺序重排多个维度

张量形状操作是指对张量的维度进行变换的一系列操作。

张量的形状则描述了每个维度上的元素数量。

reshape

保证张量数据个数不变的前提下改变维度

import torch

data = torch.tensor([[10, 20, 30], [40, 50, 60]])
# 1. 使用 shape 属性或者 size 方法都可以获得张量的形状
print(data.shape, data.shape[0], data.shape[1])
print(data.size(), data.size(0), data.size(1)) # 效果同上

# 2. 使用 reshape 函数修改张量形状
# reshape(1, 6) 表示重塑成 1 行 6 列
# 参数含义:
#   1 表示第 0 维长度为 1,也就是 1 行
#   6 表示第 1 维长度为 6,也就是 6 列
new_data = data.reshape(1, 6)
print(new_data, new_data.shape)
# 输出:
# tensor([[10, 20, 30, 40, 50, 60]]) torch.Size([1, 6])

# reshape(1, -1) 表示第一维固定为 1,第二维让 PyTorch 自动推导
# 参数含义:
#   1 仍然表示重塑后有 1 行
#   -1 表示这一维不用手动写,系统会根据元素总数自动算出是 6 列
new_data = data.reshape(1, -1)
print(new_data, new_data.shape)
# 输出:
# tensor([[10, 20, 30, 40, 50, 60]]) torch.Size([1, 6])
  • reshape(*shape):按给定的新形状返回张量,前提是前后元素总数必须一致
  • reshape(1, 6):手动指定新形状是 1行6列
  • reshape(1, -1):只固定前面的 1,后面的 -1 交给 PyTorch 自动计算
  • 这里原张量一共有 2 × 3 = 6 个元素,所以 -1 最终会被推导成 6

squeeze和unsqueeze

squeeze:删除指定位置形状为1的维度,不指定位置则删除所有形状为1的维度,降维

unsqueeze:在指定位置添加形状为1的维度,升维

import torch

# squeeze和unsqueeze
# 定义张量, 5个元素
t1 = torch.tensor([1, 2, 3, 4, 5])
print(t1,t1.shape)

# unsqueeze增加形状为1的维度
t2 = t1.unsqueeze(dim=0) # 1行5列
print(t2,t2.shape)
print("-"*30)

t3 = t1.unsqueeze(dim=1) # 5行1列
print(t3,t3.shape)

# squeeze删除所有形状为1的维度
t4 = t3.squeeze()
print(t4,t4.shape)

# 重新定义多维, 且包含1的维度.
t5 = torch.randint(1, 10, (2, 1, 3, 1, 5))
print(t5,t5.shape)

# 不设置参数,表示删除所有为1的维度
t6 = t5.squeeze() # 形状[2, 3, 5]
print(t6, t6.shape)
print("-"*30)

# 可以通过dim精准删除哪个轴上的1的维度
t7 = t5.squeeze(dim=1) # 形状[2, 3, 1, 5]
print(t7, t7.shape)

transpose和permute

transpose:实现交换张量形状的指定维度, 例如: 一个张量的形状为 (2, 3, 4) ,把 3 和 4 进行交换, 将张量的形状变为 (2, 4, 3)

permute:一次交换更多的维度

t1 = torch.randint(1,10,(2,3,5))
print(t1, t1.shape)

# 需求1: 交换0轴 和 1轴.  (2, 3, 5) -> (3, 2, 5)
"""
    transpose(参数1,参数2):注意每次只能交换两个维度的位置
        参数1、参数2 表示的是要交换哪几个轴的位置。参数传递顺序无所谓
"""
# 下面两个写法效果一样
# t2 = t1.transpose(dim0=1,dim1=0)
t2 = t1.transpose(dim0=0,dim1=1)
print(t2, t2.shape)
print("-"*30)


# 需求2: 从 (2, 3, 5) -> (5, 2, 3)
"""
    permute(dims):同一时刻可以交换多个维度的位置。参数中传递的是维度顺序
"""
t3 = t1.permute(dims=[2,0,1])
print(t3, t3.shape)

张量拼接操作

张量拼接操作用于组合来自不同来源或经过不同处理的数据。

cat/concat

torch.cattorch.concat 没有区别torch.concat 只是 torch.cat别名(兼容命名),参数、拼接规则、运行结果完全相同。

沿着现有维度连接一系列张量。所有输入张量除了指定的拼接维度外其他维度必须一样

"""
    cat:
        1- 不能修改张量的维度个数。例如:不能将2维变3维
        2- 除了拼接的维度以外,其他维度必须相同
"""
t1 = torch.randint(1,10,size=(2,3))
t2 = torch.randint(1,10,size=(2,3))
print(t1,t1.shape)
print(t2,t2.shape)


cat_1 = torch.cat([t1,t2],dim=0)
print(cat_1,cat_1.shape)

cat_2 = torch.cat([t1,t2],dim=1)
print(cat_2,cat_2.shape)


# 不能将2维变3维
# torch.cat([t1,t2],dim=2)


t1 = torch.randint(1,10,size=(2,3))
# 除了拼接的维度以外,其他维度必须相同
t2 = torch.randint(1,10,size=(5,3))
# t2 = torch.randint(1,10,size=(2,4))
print(t1,t1.shape)
print(t2,t2.shape)

cat_1 = torch.cat([t1,t2],dim=0)
print(cat_1,cat_1.shape)

stack

在一个新的维度上连接一系列张量,这会增加一个新维度,并且所有输入张量的形状必须完全相同

import torch

"""
    stack:
        1- 两个拼接的张量形状必须完全一样
        2- 会产生新维度,在新维度上进行拼接操作
"""
t1 = torch.randint(1,10,size=(5,6))
t2 = torch.randint(1,10,size=(5,6))
print(t1,t1.shape)
print(t2,t2.shape)

stack_1 = torch.stack([t1,t2],dim=0)
print(stack_1,stack_1.shape) # [2,5,6]

stack_2 = torch.stack([t1,t2],dim=1)
print(stack_2.shape) # [5,2,6]

stack_3 = torch.stack([t1,t2],dim=2)
print(stack_3.shape) # [5,6,2]
stack扩展

torch.stack 的核心意义在于 创建新维度来保留多个独立张量的层次结构关系,而 torch.cat 仅能扩展现有维度。当输入张量形状完全相同时,两者结果的维度数量和语义含义完全不同,这是 stack 不可替代的关键原因。


一、核心区别

1. 维度变化的本质差异

  • torch.cat
    现有维度上拼接不新增维度。例如两个形状为 (3,) 的张量:
    torch.cat([a, b], dim=0) → 结果形状为 (6,)6个独立元素,无分组信息)。
  • torch.stack
    新维度上堆叠强制新增一个维度。例如相同输入:
    torch.stack([a, b], dim=0) → 结果形状为 (2, 3)2组,每组3个元素,保留分组结构)。

2. 输入要求的关键差异

  • torch.cat 只要求非拼接维度一致(例如拼接行时列数需相同)。
  • torch.stack 要求所有维度必须完全相同,且必须新增一个维度来标识“这是多个独立张量的集合”。

二、stack 的不可替代场景

1. 保留序列/批次的层次信息

  • 典型场景:处理时间序列或批次数据时,需明确区分“时间步”或“样本”维度。
    • 例如:RNN 每一步输出形状为 (batch_size, hidden_dim),若直接 cat 拼接:
      torch.cat(outputs, dim=0) → 形状 (seq_len * batch_size, hidden_dim)丢失时间步顺序
    • 正确做法:torch.stack(outputs, dim=0) → 形状 (seq_len, batch_size, hidden_dim)保留时间步维度

2. 构建多维数据结构

  • 图像批次处理
    单张图像形状为 (C, H, W),10 张图像需构建成批次:
    torch.stack(images, dim=0) → 形状 (10, C, H, W)新增 batch_size 维度)。
    若用 cattorch.cat(images, dim=0) → 形状 (10*C, H, W)通道信息被破坏

3. 避免维度混淆

  • 关键区别
    stack 的结果中,新维度明确标识了“这是多个独立张量”,而 cat 会将所有数据视为同一维度的连续元素。
    • 例如两个向量
      • stack[, ]2个独立向量)。
      • cat → ``(1个长向量,原始分组信息丢失)。

三、简单总结

  • cat:当只需扩展现有维度(如拼接特征、合并行/列数据)。
  • stack:当需要明确保留多个张量的独立性(如构建批次、时间序列、多模态对齐)。

本质stack 不是拼接数据,而是为数据添加一个“容器维度”,确保后续操作能区分原始输入的边界。若强行用 cat 替代,会导致维度语义错误关键结构信息丢失