Pytorch开发教程2——张量核心操作完全指南

以下内容覆盖张量创建、基础属性、核心操作、Numpy互转四大模块,所有代码均可直接运行。

一、张量创建:5个核心函数

张量(Tensor)是Pytorch的基础数据结构,可理解为多维数组,对标Numpy的ndarray,但支持GPU加速与自动求导。

1.torch.tensor():从数据直接创建

最通用的创建方式,可从列表、NumPy数组等生成张量。

python 复制代码
import torch
import numpy as np

# 从列表创建
a = torch.tensor([1, 2, 3])
print("从列表创建:", a)  # tensor([1, 2, 3])

# 创建二维张量
b = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
print("二维张量:\n", b)

# 从numpy数组创建(后续会讲内存共享区别)
np_arr = np.array([1, 2, 3])
c = torch.tensor(np_arr)  # 会拷贝数据,不共享内存

输出如下:

注意:torch.tensor()默认会拷贝原始数据;若数据类型不指定,整数默认int64,浮点数默认float32。

2.torch.zeros():创建全0张量

python 复制代码
import torch

# 形状为 (2, 3) 的全0张量
zeros = torch.zeros(2, 3)
print("全0张量:\n", zeros)

# 指定数据类型和设备
zeros_gpu = torch.zeros(2, 3, dtype=torch.float64, device='cuda')

输出如下:

3.torch.ones():创建全1张量

python 复制代码
import torch

# 形状为 (3, 4) 的全1张量
ones = torch.ones(3, 4)
print("全1张量:\n", ones)

输出如下:

4.torch.randn():创建标准正态分布随机张量

数值服从均值为0、方差为1的正态分布,常用于模型权重初始化。

python 复制代码
import torch

# 形状为 (2, 2) 的随机张量
randn = torch.randn(2, 2)
print("正态随机张量:\n", randn)

输出如下:

5.torch.arange():创建等差序列张量

对标Python的range和Numpy的np.arange。

python 复制代码
import torch

# 0 到 9 的等差序列(左闭右开)
arange_1 = torch.arange(10)
print("等差序列:", arange_1)  # tensor([0, 1, 2, ..., 9])

# 指定起点、终点、步长
arange_2 = torch.arange(2, 10, 2)
print("指定步长:", arange_2)  # tensor([2, 4, 6, 8])

输出如下:

二、张量基础三要素:shape、dtype、device

这三个属性是张量的核心标识,也是90%报错的根源(维度不匹配、类型不统一、设备不统一)。

代码示例:

python 复制代码
import torch

x = torch.randn(2, 3, device='cuda')

# 1. 形状
print("形状:", x.shape)        # torch.Size([2, 3])
print("形状:", x.size())       # 与 shape 等价

# 2. 数据类型
print("数据类型:", x.dtype)    # torch.float32
# 转换为 float64
x_double = x.to(dtype=torch.float64)

# 3. 设备
print("所在设备:", x.device)   # device(type='cuda', index=0)
# 转移到 CPU
x_cpu = x.cpu()
# 转移回 GPU
x_gpu = x_cpu.cuda()

踩坑提醒:运算的两个张量必须在同一设备上,CPU张量和GPU张量无法直接计算,会报错。

三、核心常用操作

1.索引与切片

用法和NumPy高度一致,支持多维索引、步长、布尔索引。

python 复制代码
import torch

x = torch.arange(12).reshape(3, 4)
print("原始张量:\n", x)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])

# 取第 0 行
print("第0行:", x[0])

# 取第 1 列
print("第1列:", x[:, 1])

# 取前 2 行,第 1-3 列
print("切片结果:\n", x[:2, 1:4])

# 布尔索引:取大于 5 的元素
print("大于5的元素:", x[x > 5])

输出结果如下:

2.维度重塑:reshape vs view

两者都能改变张量形状,是最常用的操作,但底层有本质区别:

  • view:仅适用于连续张量 (is_contiguous()为True),操作后与原张量共享内存,属于轻量级操作,修改一个另一个会变。
  • reshape:不要求张量连续,底层优先尝试view,不满足时自动拷贝数据,兼容性更强,日常开发优先用reshape
python 复制代码
import torch

x = torch.arange(12)

# reshape 变形为 3行4列
x_reshape = x.reshape(3, 4)
print("reshape 结果形状:", x_reshape.shape)  # torch.Size([3, 4])

# view 变形
x_view = x.view(3, 4)
print("view 结果形状:", x_view.shape)

# 验证内存共享:修改原张量,view 结果同步变化
x[0] = 100
print("原张量修改后,view结果第0个元素:", x_view[0, 0].item())  # 100

输出如下:

3.张量拼接:cat vs stack

这里最容易混淆的一组操作,核心区别:

  • torch.cat:在已有维度上拼接,拼接后维度总数不变。
  • torch.stack:会新增一个维度再拼接,拼接后维度总数+1。
python 复制代码
import torch

a = torch.zeros(2, 3)
b = torch.ones(2, 3)

# cat 拼接:dim=0 按行拼,dim=1 按列拼
cat0 = torch.cat([a, b], dim=0)
print("cat dim=0 形状:", cat0.shape)  # torch.Size([4, 3])

cat1 = torch.cat([a, b], dim=1)
print("cat dim=1 形状:", cat1.shape)  # torch.Size([2, 6])

# stack 拼接:新增维度
stack0 = torch.stack([a, b], dim=0)
print("stack dim=0 形状:", stack0.shape)  # torch.Size([2, 2, 3])

stack1 = torch.stack([a, b], dim=1)
print("stack dim=1 形状:", stack1.shape)  # torch.Size([2, 2, 3])

输出如下:

适用场景:cat常用于拼接数据集batch、特征;stack常用于将多个样本堆叠成batch。

4.维度增减:unsqueeze vs squeeze

  • unsqueeze(dim):在指定位置新增一个维度(大小为1),常用于给单条数据加batch维度。
  • squeeze(dim):删除指定位置大小为1的维度;不指定dim时删除所有大小为1的维度。
python 复制代码
import torch

x = torch.randn(3, 4)

# 新增第 0 维:模拟 batch 维度
x_unsq = x.unsqueeze(0)
print("unsqueeze后形状:", x_unsq.shape)  # torch.Size([1, 3, 4])

# 压缩第 0 维
x_sq = x_unsq.squeeze(0)
print("squeeze后形状:", x_sq.shape)     # torch.Size([3, 4])

#  squeeze 只能删除大小为 1 的维度,非 1 维度无影响
x_test = torch.randn(2, 1, 3, 1)
print("全压缩后形状:", x_test.squeeze().shape)  # torch.Size([2, 3])

输出如下:

四、与NumPy的互转及内存共享机制

1.基础互转操作

python 复制代码
import numpy as np
import torch

# 张量 → NumPy 数组
tensor_a = torch.ones(3)
np_a = tensor_a.numpy()
print("NumPy数组:", np_a)

# NumPy 数组 → 张量
np_b = np.array([1, 2, 3])
tensor_b = torch.from_numpy(np_b)
print("张量:", tensor_b)

输出如下:

2.核心:内存共享机制

CPU张量通过.numpy()和torch.from_numpy()转换时,默认共享底层内存,修改其中一个,另一个会同步变化。

python 复制代码
import numpy as np
import torch

np_arr = np.array([1, 2, 3])
tensor_arr = torch.from_numpy(np_arr)

# 修改 numpy 数组,张量同步变化
np_arr[0] = 100
print("修改numpy后张量值:", tensor_arr[0].item())  # 100

# 修改张量,numpy 数组同步变化
tensor_arr[1] = 200
print("修改张量后numpy值:", np_arr[1])  # 200

输出如下:

3.不共存内存的场景

  1. GPU张量:.numpy()只能用于CPU张量,GPU张量必须先.cpu()再转换,且会拷贝数据。
  2. torch.tensor()创建:从NumPy数组用torch.tensor()创建时,会强制拷贝数据,不共享内存。
  3. 使用.clone():显式克隆张量,断开内存共享。
python 复制代码
import numpy as np
import torch

# 断开内存共享的方式
np_arr = np.array([1, 2, 3])
tensor_clone = torch.from_numpy(np_arr).clone()  # 克隆一份
np_arr[0] = 100
print("克隆后不受numpy影响:", tensor_clone[0].item())  # 1

输出如下:

相关推荐
qq29532 小时前
2026 财搭子决策模拟器:多智能体投研架构能力拆解
大数据·人工智能·架构
码流怪侠2 小时前
【GitHub】Bend:让 GPU 并行编程像写 Python 一样简单
python·github
满怀冰雪3 小时前
12-PaddlePaddle, 飞桨, 分类模型, 训练循环, 损失函数, 优化器, cross_entropy, Adam
人工智能·深度学习·分类·paddlepaddle
2401_894915533 小时前
GEO 搜索优化完整源码从零部署:环境配置、集群搭建全流程
开发语言·python·tcp/ip·算法·unity
冬奇Lab4 小时前
AI 评测系列(08):评测 CI/CD——持续质量门控
人工智能
冬奇Lab4 小时前
每日一个开源项目(第169篇):AIRI - 开源自托管 AI 虚拟伴侣,能打 Minecraft 的 Neuro-sama 复现
人工智能·开源·资讯
MartinYeung54 小时前
[论文学习]自主性如何重塑个性化对LLM智能体隐私关切与信任的影响
人工智能·学习
LDZKKJ4 小时前
OpenAI模型“越狱“入侵Hugging Face——AI安全史上的至暗时刻
网络·人工智能·安全