PyTorch使用-张量类型转换

文章目录

  • 张量类型转换
  • [1. 张量转换为 numpy 数组](#1. 张量转换为 numpy 数组)
    • [1.1. 默认行为:共享内存](#1.1. 默认行为:共享内存)
    • [1.2. 避免内存共享](#1.2. 避免内存共享)
      • [1.2.1. 使用 .copy()](#1.2.1. 使用 .copy())
      • [1.2.2. 使用 torch.clone() + .numpy()](#1.2.2. 使用 torch.clone() + .numpy())
    • [1.3. 处理 GPU 张量](#1.3. 处理 GPU 张量)
    • [1.4. 分离梯度跟踪](#1.4. 分离梯度跟踪)
    • [1.5. 代码示例](#1.5. 代码示例)
    • [1.6. 关键注意事项](#1.6. 关键注意事项)
    • [1.7. 总结](#1.7. 总结)
  • [2. 标量张量和数字的转换](#2. 标量张量和数字的转换)
    • [2.1. torch.from_numpy():共享内存](#2.1. torch.from_numpy():共享内存)
    • [2.2. torch.tensor():独立内存](#2.2. torch.tensor():独立内存)
    • [2.3. 关键对比](#2.3. 关键对比)
    • [2.4. 注意事项](#2.4. 注意事项)
      • [2.4.1. 数据类型一致性](#2.4.1. 数据类型一致性)
      • [2.4.2. GPU 张量转换](#2.4.2. GPU 张量转换)
      • [2.4.3. 梯度跟踪](#2.4.3. 梯度跟踪)
    • [2.5. 完整代码示例](#2.5. 完整代码示例)
    • [2.6. 最佳实践](#2.6. 最佳实践)
  • [3. 标量张量和数字的转换](#3. 标量张量和数字的转换)
    • [3.1. 提取标量张量的值:item() 方法](#3.1. 提取标量张量的值:item() 方法)
    • [3.2. 强制类型转换(不推荐)](#3.2. 强制类型转换(不推荐))
    • [3.3. 安全操作流程](#3.3. 安全操作流程)
      • [3.3.1. 确保张量为标量](#3.3.1. 确保张量为标量)
      • [3.3.2. 处理设备与梯度](#3.3.2. 处理设备与梯度)
    • [3.4. 代码示例](#3.4. 代码示例)
    • [3.5. 关键注意事项](#3.5. 关键注意事项)
    • [3.6. 总结](#3.6. 总结)

张量类型转换

张量的类型转换也是经常使用的一种操作,是必须掌握的知识点。

1. 张量转换为 numpy 数组

在 PyTorch 中,张量(Tensor)与 NumPy 数组(ndarray)之间的转换是常见操作,但需要注意 内存共享机制。

1.1. 默认行为:共享内存

当张量位于 CPU 上时,直接使用 .numpy() 会生成共享底层内存的 NumPy 数组。修改其中一个对象会影响另一个:

python 复制代码
import torch
import numpy as np

# 创建 CPU 张量
tensor = torch.tensor([1, 2, 3])  # 默认在 CPU 上
numpy_array = tensor.numpy()      # 共享内存

# 修改 NumPy 数组
numpy_array[0] = 100

# 张量也会被修改!
print(tensor)  # 输出: tensor([100,   2,   3])

1.2. 避免内存共享

若需独立的数据副本,使用 .copy() 方法或 torch.clone():

1.2.1. 使用 .copy()

python 复制代码
tensor = torch.tensor([1, 2, 3])
numpy_array = tensor.numpy().copy()  # 创建独立副本

numpy_array[0] = 100
print(tensor)  # 输出: tensor([1, 2, 3])(原数据不变)

1.2.2. 使用 torch.clone() + .numpy()

python 复制代码
tensor = torch.tensor([1, 2, 3])
cloned_tensor = tensor.clone()      # 创建张量副本
numpy_array = cloned_tensor.numpy() # 仍共享 cloned_tensor 的内存

numpy_array[0] = 100
print(cloned_tensor)  # 输出: tensor([100, 2, 3])
print(tensor)         # 输出: tensor([1, 2, 3])

1.3. 处理 GPU 张量

若张量在 GPU 上,需先移动到 CPU 再转换:

python 复制代码
# 创建 GPU 张量
tensor_gpu = torch.tensor([1, 2, 3], device="cuda")

# 错误操作:直接转换会报错
try:
    numpy_array = tensor_gpu.numpy()
except RuntimeError as e:
    print("错误:", e)  # 需先将张量移至 CPU

# 正确操作:移动到 CPU 再转换
tensor_cpu = tensor_gpu.cpu()
numpy_array = tensor_cpu.numpy()  # 共享内存

1.4. 分离梯度跟踪

若张量带有梯度(requires_grad=True),需先分离计算图:

python 复制代码
tensor = torch.tensor([1.0, 2.0], requires_grad=True)

# 错误操作:直接转换会警告
try:
    numpy_array = tensor.numpy()
except RuntimeError as e:
    print("错误:", e)

# 正确操作:先分离梯度
detached_tensor = tensor.detach()  # 返回一个无梯度的新张量
numpy_array = detached_tensor.numpy()

1.5. 代码示例

python 复制代码
import torch
import numpy as np

# 示例1:共享内存(CPU 张量)
tensor_cpu = torch.tensor([1, 2, 3], dtype=torch.float32)
numpy_shared = tensor_cpu.numpy()
numpy_shared[0] = 100
print("共享内存 - 原张量:", tensor_cpu)  # tensor([100., 2., 3.])

# 示例2:独立副本(使用 copy)
numpy_copy = tensor_cpu.numpy().copy()
numpy_copy[0] = 200
print("独立副本 - 原张量:", tensor_cpu)  # tensor([100., 2., 3.])

# 示例3:GPU 张量处理
tensor_gpu = torch.tensor([4, 5, 6], device="cuda")
tensor_cpu = tensor_gpu.cpu()
numpy_gpu = tensor_cpu.numpy()
print("GPU 转 CPU 后数组:", numpy_gpu)  # [4 5 6]

# 示例4:带梯度的张量
x = torch.tensor([3.0], requires_grad=True)
y = x * 2
detached_x = x.detach()
numpy_detached = detached_x.numpy()
numpy_detached[0] = 5.0
print("分离梯度后张量:", x)  # tensor([3.], requires_grad=True)

1.6. 关键注意事项

场景 处理方式
避免内存共享 使用 .copy() 或先 .clone() 再转换
GPU 张量 先 .cpu() 移动至 CPU,再转换
梯度跟踪张量 先 .detach() 分离计算图
数据类型一致性 确保张量与 NumPy 数组数据类型兼容(如 float32 对应 np.float32)

1.7. 总结

共享内存 :默认情况下,CPU 张量与 NumPy 数组共享内存,修改会同步。
独立副本 :使用 .copy() 或 clone() + .numpy() 创建独立数据。
设备与梯度:处理 GPU 张量或带梯度张量时,需先移至 CPU 并分离梯度。

2. 标量张量和数字的转换

2.1. torch.from_numpy():共享内存

特点:
默认共享内存 :生成的张量与原始 NumPy 数组共享底层内存,修改其中一个会影响另一个。
高效但风险:适合处理大型数据时节省内存,但需谨慎操作避免意外修改。

示例:

python 复制代码
import numpy as np
import torch

# 创建 NumPy 数组
numpy_array = np.array([1, 2, 3], dtype=np.float32)

# 转换为张量(共享内存)
tensor_shared = torch.from_numpy(numpy_array)

# 修改 NumPy 数组会影响张量
numpy_array[0] = 100
print("共享内存张量:", tensor_shared)  # 输出: tensor([100., 2., 3.])

# 修改张量也会影响 NumPy 数组
tensor_shared[1] = 200
print("原始 NumPy 数组:", numpy_array)  # 输出: [100. 200.   3.]

避免共享内存:

若需独立副本,显式复制数据:

python 复制代码
# 方法1:通过 NumPy 的 copy()
numpy_copy = numpy_array.copy()
tensor_independent = torch.from_numpy(numpy_copy)

# 方法2:通过张量的 clone()
tensor_clone = tensor_shared.clone()

2.2. torch.tensor():独立内存

特点:

默认独立内存:生成的新张量会复制数据,与原始 NumPy 数组无内存共享。

安全但略低效:适合需要数据隔离的场景(如训练时预处理)。

示例:

python 复制代码
numpy_array = np.array([1, 2, 3], dtype=np.float32)

# 转换为张量(独立内存)
tensor_new = torch.tensor(numpy_array)

# 修改 NumPy 数组不影响张量
numpy_array[0] = 100
print("独立内存张量:", tensor_new)  # 输出: tensor([1., 2., 3.])

# 修改张量也不影响原数组
tensor_new[1] = 200
print("原始 NumPy 数组:", numpy_array)  # 输出: [100. 2. 3.]

2.3. 关键对比

方法 内存共享 性能 适用场景
torch.from_numpy() 大数据处理,需减少内存复制时使用
torch.tensor() 需要数据隔离的场景(如训练数据)

2.4. 注意事项

2.4.1. 数据类型一致性

torch.from_numpy() 会保留 NumPy 数组的数据类型。

torch.tensor() 可手动指定 dtype:

python 复制代码
tensor = torch.tensor(numpy_array, dtype=torch.float64)

2.4.2. GPU 张量转换

若需直接在 GPU 上创建张量,需显式指定设备:

python 复制代码
# 共享内存 + GPU(需先复制到 CPU)
numpy_array = np.array([1, 2, 3])
tensor_gpu = torch.from_numpy(numpy_array).cuda()  # 复制到 GPU,不共享内存

# 独立内存 + GPU
tensor_gpu = torch.tensor(numpy_array, device="cuda")

2.4.3. 梯度跟踪

若需张量参与梯度计算,推荐使用 torch.tensor():

python 复制代码
x_np = np.array([3.0], dtype=np.float32)
x_tensor = torch.tensor(x_np, requires_grad=True)  # 可跟踪梯度
y = x_tensor**2
y.backward()
print(x_tensor.grad)  # 输出: tensor([6.])

2.5. 完整代码示例

python 复制代码
import numpy as np
import torch

# 示例1:共享内存转换
numpy_shared = np.array([1, 2, 3], dtype=np.int32)
tensor_shared = torch.from_numpy(numpy_shared)
numpy_shared[0] = 100
print("共享内存张量:", tensor_shared)  # tensor([100, 2, 3], dtype=torch.int32)

# 示例2:独立内存转换
numpy_independent = np.array([4, 5, 6], dtype=np.float64)
tensor_independent = torch.tensor(numpy_independent, dtype=torch.float32)
numpy_independent[0] = 400
print("独立内存张量:", tensor_independent)  # tensor([4., 5., 6.])

# 示例3:避免共享内存的显式复制
numpy_original = np.array([7, 8, 9])
tensor_copy = torch.from_numpy(numpy_original.copy())  # 独立副本
numpy_original[0] = 700
print("显式复制后的张量:", tensor_copy)  # tensor([7, 8, 9])

2.6. 最佳实践

优先选择 torch.tensor(): 默认数据隔离更安全,避免意外修改。

谨慎使用 torch.from_numpy(): 仅在明确需要共享内存且能控制同步时使用。

显式复制数据: 使用 .copy() 或 .clone() 确保数据独立。

统一数据类型和设备: 避免因类型或设备不匹配导致的错误。

3. 标量张量和数字的转换

在 PyTorch 中,处理标量张量(即只含有一个元素的张量)与 Python 数字之间的转换是常见操作,尤其是在训练过程中提取损失值、指标值等场景。以下是详细的方法说明及注意事项:

3.1. 提取标量张量的值:item() 方法

功能: 将标量张量(元素数量为 1)转换为 Python 数字(int 或 float)。

要求: 张量必须为标量(即 tensor.numel() == 1)。

示例:

python 复制代码
import torch

# 标量张量(单个元素)
scalar_tensor = torch.tensor(3.1415)
value = scalar_tensor.item()
print(value)          # 输出: 3.1415
print(type(value))   # 输出: <class 'float'>

# 非标量张量会报错
vector_tensor = torch.tensor([1, 2, 3])
try:
    vector_tensor.item()  # 报错:ValueError
except ValueError as e:
    print("错误:", e)  # 输出: only one element tensors can be converted to Python scalars

3.2. 强制类型转换(不推荐)

对于单元素张量,可通过强制类型转换(如 float()、int())直接提取值,但需注意:

要求:张量必须在 CPU 上且无梯度跟踪。

风险:若张量包含多个元素,会触发隐式的维度压缩(可能引发意外错误)。

示例:

python 复制代码
# 标量张量
scalar_tensor = torch.tensor(5.0)
value = float(scalar_tensor)  # 5.0

# 单元素张量(非标量)
single_element_tensor = torch.tensor([5.0])
value = float(single_element_tensor)  # 5.0(自动压缩维度)

# 多元素张量会报错
vector_tensor = torch.tensor([1, 2])
try:
    float(vector_tensor)  # 报错:ValueError
except ValueError as e:
    print("错误:", e)

3.3. 安全操作流程

3.3.1. 确保张量为标量

使用 .squeeze() 或 .flatten() 去除冗余维度:

python 复制代码
# 冗余维度张量(如形状为 (1, 1))
tensor = torch.tensor([[3.14]])
scalar_tensor = tensor.squeeze()  # 形状变为空张量 []
value = scalar_tensor.item()      # 3.14

3.3.2. 处理设备与梯度

若张量在 GPU 或带有梯度,需先移至 CPU 并分离计算图:

python 复制代码
# GPU 上的标量张量
tensor_gpu = torch.tensor(2.0, device="cuda")
value = tensor_gpu.cpu().item()  # 移动到 CPU 后提取

# 带梯度的标量张量
x = torch.tensor(3.0, requires_grad=True)
y = x**2
y.backward()
value = x.detach().item()  # 分离梯度后提取

3.4. 代码示例

python 复制代码
import torch

# 示例1:标准提取
scalar = torch.tensor(42)
print("标量值:", scalar.item())  # 42

# 示例2:处理冗余维度
tensor = torch.tensor([[5.0]])
squeezed_tensor = tensor.squeeze()
print("压缩后形状:", squeezed_tensor.shape)  # torch.Size([])
print("提取值:", squeezed_tensor.item())    # 5.0

# 示例3:GPU 张量处理
if torch.cuda.is_available():
    tensor_gpu = torch.tensor(6.28, device="cuda")
    tensor_cpu = tensor_gpu.cpu()
    print("GPU 张量值:", tensor_cpu.item())  # 6.28

# 示例4:错误处理(非标量)
vector = torch.tensor([1, 2, 3])
try:
    vector.item()
except ValueError as e:
    print("错误信息:", e)  # only one element tensors can be converted to Python scalars

3.5. 关键注意事项

场景 处理方式
标量张量 直接使用 .item()
单元素但非标量 先用 .squeeze() 压缩维度,再用 .item()
GPU 上的张量 先 .cpu() 移动到 CPU,再 .item()
带梯度的张量 先 .detach() 分离计算图,再 .item()
强制类型转换 仅限单元素张量,需谨慎使用(推荐优先使用 .item())

3.6. 总结

优先使用 .item():安全且明确,专为标量设计。

避免强制类型转换:可能隐藏维度不匹配或设备不一致的问题。

处理复杂情况:通过 .squeeze()、.cpu()、.detach() 确保张量符合要求。

相关推荐
星哥说事8 分钟前
开源通义万相本地部署方案,文生视频、图生视频、视频生成大模型,支持消费级显卡!
人工智能·开源·音视频
来自于狂人10 分钟前
当大模型训练遇上“双向飙车”:DeepSeek开源周 DualPipe解析指南
人工智能·算法·系统架构·gpu算力
搏博13 分钟前
人工智能的数学基础之概率论与统计学(含示例)
图像处理·人工智能·机器学习·数据分析·概率论
栀子清茶22 分钟前
Towards Universal Soccer Video Understanding——论文学习(足球类)
论文阅读·人工智能·深度学习·学习·算法·计算机视觉·论文笔记
木易:_/23 分钟前
【004】deepseek本地化部署后,python的调用方式_#py
开发语言·python
小诸葛IT课堂41 分钟前
PyTorch 生态概览:为什么选择动态计算图框架?
人工智能·pytorch·python
杜子腾dd43 分钟前
16.使用读写包操作Excel文件:XlsxWriter 包
大数据·开发语言·python·excel·pandas
带娃的IT创业者44 分钟前
Flask应用调试模式下外网访问的技巧
后端·python·flask
雅菲奥朗1 小时前
4大观点直面呈现|直播回顾-DeepSeek时代的AI算力管理
人工智能·ai算力·deepseek
print('name')1 小时前
将景区天气数据存储到Excel文件中
开发语言·数据结构·python·pycharm·excel·visual studio code