PyTorch负责张量计算,NumPy擅长通用数值处理和数据交换。在数据预处理、绘图和调试时,我们经常要在Tensor与ndarray之间切换。转换方法并不难,真正需要注意的是:转换后是否仍共享同一块内存。

先记住这三个常用写法

  • 张量转NumPy:tensor.detach().cpu().numpy()
  • NumPy转独立张量:torch.tensor(array)
  • 从单元素张量取值:tensor.item()

一、转换方法总览

场景写法内存关系
Tensor → NumPytensor.numpy()通常共享CPU内存
Tensor → NumPytensor.detach().cpu().numpy().copy()独立复制
NumPy → Tensortorch.from_numpy(array)通常共享CPU内存
NumPy → Tensortorch.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()

六、怎么选择

转换之前先问自己一个问题:我希望转换后的对象与原对象共享数据,还是各自独立?