PyTorch使用(3)-张量类型转换

devtools/2025/3/21 3:01:54/

文章目录

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


http://www.ppmy.cn/devtools/168787.html

相关文章

第五章-动态规划

第五章-动态规划 写在前面&#xff1a; 本笔记是根据acwing网站:算法基础课进行制作的&#xff0c;欢迎大家支持y总&#xff0c;听过y总的课&#xff0c;你绝对会对于算法产生更深的理解和更浓厚的兴趣&#xff01; 本笔记可能会有部分视频的截图&#xff0c;我不知道是不是会造…

涨薪技术|Kubernetes(k8s)之Pod环境变量

01Pod设置环境变量 在创建pod时&#xff0c;可以为其下的容器设置环境变量&#xff0c;通过配置文件的env或envForm字段来设置环境变量。 编写pod_env.yaml文件&#xff0c;内容如下&#xff0c;通过env关键字来定义环境变量&#xff0c;环境变量通过name&#xff0c;value对…

【NLP】 9. 处理创造性词汇 词组特征(Creative Words Features Model), 词袋模型处理未知词,模型得分

处理创造性词汇 & 词组特征&#xff08;Creative Words & Features Model&#xff09;&#xff0c; 词袋模型处理未知词&#xff0c;模型得分 处理创造性词汇 & 词组特征&#xff08;Creative Words & Features Model&#xff09;1. 处理否定&#xff08;Negat…

【蓝桥杯速成】| 7.01背包练习生

题目一&#xff1a;分割等和子集 问题描述 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例 1&#xff1a; 输入&#xff1a…

TCP 通信流程图

下面给出一个详细的 TCP 通信流程图&#xff0c;演示 客户端&#xff08;Client&#xff09; 与 服务器&#xff08;Server&#xff09; 之间通过 TCP 协议进行通信时的各个步骤。这里假设&#xff1a; 服务器 IP&#xff1a;192.168.1.100&#xff0c;监听 80 端口客户端 IP&…

Android Fresco 框架兼容模块源码深度剖析(六)

Android Fresco 框架兼容模块源码深度剖析 一、引言 在 Android 开发的多元环境中&#xff0c;兼容性是衡量一个框架优劣的重要指标。Fresco 作为一款强大的图片加载框架&#xff0c;其兼容模块在确保框架能在不同 Android 版本、不同设备和不同图片格式下稳定运行方面发挥着…

MySQL常用函数详解及SQL代码示例

MySQL常用函数详解及SQL代码示例 引言当前日期和时间函数字符串函数数学函数聚合函数结论 引言 MySQL作为一种广泛使用的关系型数据库管理系统&#xff0c;提供了丰富的内置函数来简化数据查询、处理和转换。掌握这些函数可以大大提高数据库操作的效率和准确性。本文将详细介绍…

VUE中使用路由router跳转页面

在 Vue 中&#xff0c;this.$router.push 是用来跳转到另一个路由的方法&#xff0c;它可以传递参数。Vue Router 提供了多种方式来传递路由参数&#xff0c;常见的有 查询参数&#xff08;query&#xff09; 和 路由参数&#xff08;params&#xff09;。 1. 查询参数&#x…