PyTorch负责张量计算,NumPy擅长通用数值处理和数据交换。在数据预处理、绘图和调试时,我们经常要在Tensor与ndarray之间切换。转换方法并不难,真正需要注意的是:转换后是否仍共享同一块内存。
先记住这三个常用写法
- 张量转NumPy:
tensor.detach().cpu().numpy() - NumPy转独立张量:
torch.tensor(array) - 从单元素张量取值:
tensor.item()
一、转换方法总览
| 场景 | 写法 | 内存关系 |
|---|---|---|
| Tensor → NumPy | tensor.numpy() | 通常共享CPU内存 |
| Tensor → NumPy | tensor.detach().cpu().numpy().copy() | 独立复制 |
| NumPy → Tensor | torch.from_numpy(array) | 通常共享CPU内存 |
| NumPy → Tensor | torch.tensor(array) | 独立复制 |
| 单元素Tensor → Python值 | tensor.item() | 返回Python标量 |
二、Tensor转换为NumPy数组
对于位于CPU上、且不需要梯度的张量,可以直接调用numpy()。得到的数组通常与原张量共享底层内存,因此修改数组也会改变张量。
import torch
tensor = torch.tensor([1, 2, 3, 4, 5])
array = tensor.numpy()
array[0] = 100
print(array) # [100 2 3 4 5]
print(tensor) # tensor([100, 2, 3, 4, 5])
如果希望两者互不影响,就在转换后调用copy(),创建拥有独立数据的NumPy数组:
tensor = torch.tensor([1, 2, 3, 4, 5])
array = tensor.detach().cpu().numpy().copy()
array[0] = 100
print(array) # [100 2 3 4 5]
print(tensor) # tensor([1, 2, 3, 4, 5])
detach()把张量从当前自动求导计算图中分离,cpu()确保数据位于CPU,numpy()完成类型转换,最后的copy()才真正切断内存共享。对于普通CPU张量,前两个步骤可能没有实际变化,但这条写法能覆盖训练代码中更常见的情况。
三、NumPy数组转换为Tensor
torch.from_numpy()通常不会复制数据,而是让张量与NumPy数组共享内存。修改张量后,原数组也会变化。
import numpy as np
import torch
array = np.array([11, 22, 33])
tensor = torch.from_numpy(array)
tensor[0] = 100
print(tensor) # tensor([100, 22, 33])
print(array) # [100 22 33]
如果需要一份独立数据,使用torch.tensor()更直接:
array = np.array([11, 22, 33])
tensor = torch.tensor(array)
tensor[0] = 100
print(tensor) # tensor([100, 22, 33])
print(array) # [11 22 33]
torch.tensor()还可以在创建时指定数据类型,例如torch.tensor(array, dtype=torch.float32)。这比先创建张量再调用旧式的type()更清晰。
四、用item()提取Python标量
零维张量经常出现在损失值、准确率或单个预测结果中。调用item()可以取出普通Python数字:
tensor = torch.tensor(100)
value = tensor.item()
print(tensor, type(tensor)) # tensor(100) <class 'torch.Tensor'>
print(value, type(value)) # 100 <class 'int'>
item()要求张量只能包含一个元素。多元素张量调用它会报错;这类张量应先索引到单个元素,或根据用途转换成列表、NumPy数组。
五、完整可运行示例
import numpy as np
import torch
def tensor_to_numpy():
tensor = torch.tensor([1, 2, 3, 4, 5])
array = tensor.detach().cpu().numpy().copy()
array[0] = 100
print(f"array: {array}, type: {type(array)}")
print(f"tensor: {tensor}, type: {type(tensor)}")
def numpy_to_tensor():
array = np.array([11, 22, 33])
tensor = torch.tensor(array)
tensor[0] = 100
print(f"tensor: {tensor}, type: {type(tensor)}")
print(f"array: {array}, type: {type(array)}")
def extract_scalar():
tensor = torch.tensor(100)
value = tensor.item()
print(f"tensor: {tensor}, type: {type(tensor)}")
print(f"value: {value}, type: {type(value)}")
if __name__ == "__main__":
tensor_to_numpy()
numpy_to_tensor()
extract_scalar()
六、怎么选择
- 只读交换数据:可以使用
numpy()或from_numpy(),省去复制开销。 - 后续会修改数据:优先使用
copy()或torch.tensor(),避免一处修改意外影响另一处。 - 训练中的张量要转NumPy:使用
tensor.detach().cpu().numpy()。 - 只需要一个数:确认张量只有一个元素,再调用
item()。
转换之前先问自己一个问题:我希望转换后的对象与原对象共享数据,还是各自独立?