资讯动态

【Bug已解决】TypeError: can‘t convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host…

发布时间:2026/8/24 23:22:19 来源:尧图企业网站定制
【Bug已解决】TypeError: cant convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first 解决方案问题描述在 PyTorch 深度学习开发中当我们在 GPU 上训练模型后经常需要将结果转换为 NumPy 数组进行后续处理如可视化、评估指标计算、数据导出等。然而直接对 GPU 张量调用.numpy()方法会抛出TypeError。这是一个极其常见的错误几乎每个 PyTorch 初学者都会遇到。典型场景包括训练后评估在 GPU 上完成模型推理后想用 NumPy 计算评估指标如 sklearn 的classification_report。可视化将模型输出或特征图转为 NumPy 数组用 matplotlib 绘图。数据导出将 GPU 上的张量保存为 NumPy 文件.npy。与其他库交互将 PyTorch 张量传给只接受 NumPy 数组的库如 OpenCV、PIL。本文将系统地介绍这个错误的原因和多种解决方案。错误复现错误代码import torch import numpy as np # 创建一个 GPU 张量 gpu_tensor torch.randn(3, 3).cuda() # 尝试直接转换为 NumPy try: numpy_array gpu_tensor.numpy() except TypeError as e: print(fTypeError: {e})报错信息TypeError: cant convert CUDA tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.另一个常见场景import torch import torch.nn as nn from sklearn.metrics import classification_report # 模型在 GPU 上训练和推理 model nn.Linear(100, 10).cuda() x torch.randn(32, 100).cuda() output model(x) # 获取预测结果 predictions torch.argmax(output, dim1) # 尝试用 sklearn 计算指标 y_true torch.randint(0, 10, (32,)).cuda() try: report classification_report(y_true, predictions) except TypeError as e: print(fTypeError: {e}) # sklearn 不接受 GPU 张量需要 NumPy 数组根因分析1. GPU 和 CPU 内存是分离的在 CUDA 编程模型中GPUDevice和 CPUHost拥有各自独立的内存空间CPU 内存Host Memory系统 RAMNumPy 数组存储在这里。GPU 内存Device Memory显卡 VRAMCUDA 张量存储在这里。NumPy 只能操作 CPU 内存中的数据。当 PyTorch 张量位于 GPU 上时NumPy 无法直接访问它因此抛出TypeError。2..numpy()的工作原理tensor.numpy()返回一个与张量共享内存的 NumPy 数组零拷贝。这要求张量必须位于 CPU 上因为 NumPy 只能访问 CPU 内存。CPU Tensor --(.numpy())-- NumPy Array [共享内存零拷贝] GPU Tensor --(.numpy())-- TypeError! [无法共享内存]3. 正确的转换链GPU 张量转换为 NumPy 需要以下步骤GPU Tensor → CPU Tensor → NumPy Array | | | .cpu() .numpy() 完成或者如果张量需要梯度GPU Tensor (requires_grad) → Detached GPU Tensor → CPU Tensor → NumPy Array | | | | .detach() .cpu() .numpy() 完成4. 为什么需要.detach()如果张量是计算图的一部分requires_gradTrue直接调用.numpy()会报另一个错误x torch.randn(3, 3, requires_gradTrue).cuda() # x.cpu().numpy() # RuntimeError: Cant call numpy() on Tensor that requires grad. x.detach().cpu().numpy() # 正确.detach()将张量从计算图中分离出来使其不再跟踪梯度。解决方案方案一.cpu().numpy()最直接import torch import numpy as np # GPU 张量 gpu_tensor torch.randn(3, 3).cuda() # 正确先移到 CPU再转 NumPy numpy_array gpu_tensor.cpu().numpy() print(fType: {type(numpy_array)}) print(fShape: {numpy_array.shape})方案二.detach().cpu().numpy()处理梯度import torch import torch.nn as nn model nn.Linear(100, 10).cuda() x torch.randn(32, 100).cuda() # 模型输出带梯度 output model(x) print(frequires_grad: {output.requires_grad}) # 正确先 detach再 cpu再 numpy numpy_output output.detach().cpu().numpy() print(fNumPy shape: {numpy_output.shape})方案三封装为工具函数import torch import numpy as np def to_numpy(tensor): 将 PyTorch 张量安全地转换为 NumPy 数组。 处理以下情况 1. GPU 张量 → CPU 2. 带梯度的张量 → detach 3. 已经是 CPU 张量 → 直接转换 Args: tensor: PyTorch 张量 Returns: numpy.ndarray # 如果已经是 NumPy 数组直接返回 if isinstance(tensor, np.ndarray): return tensor # 如果不是张量尝试转换 if not isinstance(tensor, torch.Tensor): tensor torch.tensor(tensor) # 分离计算图 if tensor.requires_grad: tensor tensor.detach() # 移到 CPU if tensor.is_cuda: tensor tensor.cpu() # 转换为 NumPy return tensor.numpy() # 使用示例 gpu_tensor torch.randn(3, 3).cuda() cpu_tensor torch.randn(3, 3) grad_tensor torch.randn(3, 3, requires_gradTrue) print(to_numpy(gpu_tensor).shape) # (3, 3) print(to_numpy(cpu_tensor).shape) # (3, 3) print(to_numpy(grad_tensor).shape) # (3, 3)方案四使用torch.no_grad()上下文import torch import torch.nn as nn model nn.Linear(100, 10).cuda() # 在 no_grad 上下文中推理输出不需要梯度 with torch.no_grad(): x torch.randn(32, 100).cuda() output model(x) # 此时 output.requires_grad False可以直接转换 numpy_output output.cpu().numpy() print(fShape: {numpy_output.shape})完整修复代码import torch import torch.nn as nn import torch.nn.functional as F from torch.utils.data import DataLoader, TensorDataset import numpy as np from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt # # 完整示例GPU 张量到 NumPy 的正确转换 # class TensorConverter: 张量转换工具类。 提供 PyTorch Tensor 与 NumPy Array 之间的安全转换。 staticmethod def to_numpy(tensor): 将 PyTorch Tensor 转换为 NumPy Array。 自动处理 - GPU → CPU - requires_grad → detach - 已是 NumPy → 直接返回 Args: tensor: PyTorch Tensor 或 NumPy Array Returns: np.ndarray if isinstance(tensor, np.ndarray): return tensor if not isinstance(tensor, torch.Tensor): tensor torch.as_tensor(tensor) if tensor.requires_grad: tensor tensor.detach() if tensor.is_cuda: tensor tensor.cpu() return tensor.numpy() staticmethod def to_torch(array, devicecpu, dtypeNone, requires_gradFalse): 将 NumPy Array 转换为 PyTorch Tensor。 Args: array: NumPy Array 或 Python 列表 device: 目标设备 (cpu 或 cuda) dtype: 目标数据类型 requires_grad: 是否需要梯度 ![配图](https://i-blog.csdnimg.cn/img_convert/ff8584dd62f1dfbfc5f0f93a48bfe670.png) Returns: torch.Tensor if isinstance(array, torch.Tensor): tensor array else: tensor torch.as_tensor(array, dtypedtype) if device ! str(tensor.device): tensor tensor.to(device) if requires_grad: tensor tensor.requires_grad_(True) return tensor staticmethod def tensors_to_numpy(*tensors): 批量将多个 Tensor 转换为 NumPy。 Args: *tensors: 多个 PyTorch Tensor Returns: tuple of np.ndarray return tuple(TensorConverter.to_numpy(t) for t in tensors) class ClassificationModel(nn.Module): 分类模型。 def __init__(self, input_dim100, hidden_dim64, num_classes10): super().__init__() self.fc1 nn.Linear(input_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.fc3 nn.Linear(hidden_dim, num_classes) self.bn1 nn.BatchNorm1d(hidden_dim) self.bn2 nn.BatchNorm1d(hidden_dim) self.dropout nn.Dropout(0.3) def forward(self, x): x F.relu(self.bn1(self.fc1(x))) x self.dropout(x) x F.relu(self.bn2(self.fc2(x))) x self.dropout(x) return self.fc3(x) def train_and_evaluate(): 完整的训练和评估流程。 展示了在 GPU 训练后如何正确转换为 NumPy 进行评估。 print( * 60) print(GPU 训练 NumPy 评估完整示例) print( * 60) device torch.device(cuda if torch.cuda.is_available() else cpu) print(fDevice: {device}) torch.manual_seed(42) np.random.seed(42) # 生成数据 num_samples 2000 X torch.randn(num_samples, 100) y torch.randint(0, 10, (num_samples,)) split int(0.8 * num_samples) train_dataset TensorDataset(X[:split], y[:split]) test_dataset TensorDataset(X[split:], y[split:]) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size64, shuffleFalse) # 创建模型 model ClassificationModel(input_dim100, hidden_dim64, num_classes10) model model.to(device) optimizer torch.optim.AdamW(model.parameters(), lr0.001, weight_decay0.01) criterion nn.CrossEntropyLoss() # 训练 print(\nTraining...) for epoch in range(15): model.train() total_loss 0.0 correct 0 total 0 for inputs, targets in train_loader: inputs, targets inputs.to(device), targets.to(device) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, targets) loss.backward() optimizer.step() total_loss loss.item() * inputs.size(0) _, predicted outputs.max(1) total targets.size(0) correct (predicted targets).sum().item() if (epoch 1) % 5 0: print(f Epoch {epoch1}: loss{total_loss/total:.4f}, acc{correct/total:.4f}) # 评估 print(\nEvaluating...) model.eval() all_predictions [] all_targets [] all_probabilities [] with torch.no_grad(): for inputs, targets in test_loader: inputs inputs.to(device) targets targets.to(device) outputs model(inputs) probabilities F.softmax(outputs, dim1) _, predicted outputs.max(1) # 正确将 GPU 张量转为 NumPy 并收集 all_predictions.append(TensorConverter.to_numpy(predicted)) all_targets.append(TensorConverter.to_numpy(targets)) all_probabilities.append(TensorConverter.to_numpy(probabilities)) # 合并所有 batch 的结果 predictions_np np.concatenate(all_predictions) targets_np np.concatenate(all_targets) probabilities_np np.concatenate(all_probabilities) print(f\nPredictions type: {type(predictions_np)}, shape: {predictions_np.shape}) print(fTargets type: {type(targets_np)}, shape: {targets_np.shape}) # 使用 sklearn 计算评估指标 print(\nClassification Report:) print(classification_report(targets_np, predictions_np)) # 混淆矩阵 cm confusion_matrix(targets_np, predictions_np) print(fConfusion Matrix shape: {cm.shape}) # 可视化需要 NumPy 数组 fig, axes plt.subplots(1, 2, figsize(12, 5)) # 混淆矩阵热力图 axes[0].imshow(cm, cmapBlues) axes[0].set_title(Confusion Matrix) axes[0].set_xlabel(Predicted) axes[0].set_ylabel(True) # 概率分布 axes[1].hist(probabilities_np.max(axis1), bins20, edgecolorblack) axes[1].set_title(Prediction Confidence Distribution) axes[1].set_xlabel(Max Probability) axes[1].set_ylabel(Count) plt.tight_layout() plt.savefig(evaluation_results.png, dpi150) print(\nPlot saved to evaluation_results.png) return predictions_np, targets_np, probabilities_np def demo_common_scenarios(): 演示常见的转换场景。 print(\n * 60) print(常见转换场景演示) print( * 60) device torch.device(cuda if torch.cuda.is_available() else cpu) # 场景 1简单的 GPU 张量 print(\n1. Simple GPU tensor:) t torch.randn(3, 3).to(device) arr TensorConverter.to_numpy(t) print(f Tensor device: {t.device}) print(f NumPy type: {type(arr)}) # 场景 2带梯度的张量 print(\n2. Tensor with gradient:) t torch.randn(3, 3, requires_gradTrue).to(device) arr TensorConverter.to_numpy(t) print(f requires_grad: {t.requires_grad}) print(f NumPy type: {type(arr)}) # 场景 3模型输出 print(\n3. Model output:) model nn.Linear(10, 5).to(device) x torch.randn(4, 10).to(device) output model(x) arr TensorConverter.to_numpy(output) print(f Output shape: {output.shape}) print(f NumPy shape: {arr.shape}) # 场景 4在 no_grad 上下文中 print(\n4. In no_grad context:) with torch.no_grad(): output model(x) arr TensorConverter.to_numpy(output) print(f requires_grad: {output.requires_grad}) print(f NumPy shape: {arr.shape}) # 场景 5批量转换 print(\n5. Batch conversion:) t1 torch.randn(3, 3).to(device) t2 torch.randn(5, 5).to(device) t3 torch.randn(2, 2).to(device) arr1, arr2, arr3 TensorConverter.tensors_to_numpy(t1, t2, t3) print(f Shapes: {arr1.shape}, {arr2.shape}, {arr3.shape}) # 场景 6NumPy → Tensor → GPU print(\n6. NumPy to GPU tensor:) np_array np.random.randn(3, 3) tensor TensorConverter.to_torch(np_array, devicestr(device)) print(f NumPy shape: {np_array.shape}) print(f Tensor device: {tensor.device}) print(f Tensor shape: {tensor.shape}) def demo_data_pipeline(): 演示完整的数据处理管道。 NumPy → PyTorch GPU → 训练 → PyTorch GPU → NumPy print(\n * 60) print(数据处理管道演示) print( * 60) device torch.device(cuda if torch.cuda.is_available() else cpu) # 1. 从 NumPy 数据开始 print(\n1. Start with NumPy data:) X_np np.random.randn(100, 20).astype(np.float32) y_np np.random.randint(0, 5, 100) print(f X: {X_np.shape}, dtype: {X_np.dtype}) print(f y: {y_np.shape}, dtype: {y_np.dtype}) # 2. 转换为 GPU 张量 print(\n2. Convert to GPU tensor:) X_tensor TensorConverter.to_torch(X_np, devicestr(device)) y_tensor TensorConverter.to_torch(y_np, devicestr(device), dtypetorch.long) print(f X tensor: {X_tensor.device}, dtype: {X_tensor.dtype}) print(f y tensor: {y_tensor.device}, dtype: {y_tensor.dtype}) # 3. 模型处理 print(\n3. Model processing:) model nn.Linear(20, 5).to(device) with torch.no_grad(): output model(X_tensor) print(f Output: {output.shape}, device: {output.device}) # 4. 转回 NumPy print(\n4. Convert back to NumPy:) output_np TensorConverter.to_numpy(output) predictions_np np.argmax(output_np, axis1) print(f Output NumPy: {output_np.shape}) print(f Predictions: {predictions_np[:10]}) # 5. 用 NumPy 计算指标 print(\n5. Compute metrics with NumPy:) accuracy (predictions_np y_np).mean() print(f Accuracy: {accuracy:.4f}) def main(): 主函数。 # 常见场景演示 demo_common_scenarios() # 数据管道演示 demo_data_pipeline() # 完整训练评估 predictions, targets, probabilities train_and_evaluate() print(\n * 60) print(所有演示完成) print( * 60) if __name__ __main__: main()运行输出示例 常见转换场景演示 1. Simple GPU tensor: Tensor device: cuda:0 NumPy type: class numpy.ndarray 2. Tensor with gradient: requires_grad: True NumPy type: class numpy.ndarray 3. Model output: Output shape: torch.Size([4, 5]) NumPy shape: (4, 5) 4. In no_grad context: requires_grad: False NumPy shape: (4, 5) 5. Batch conversion: Shapes: (3, 3), (5, 5), (2, 2) 6. NumPy to GPU tensor: NumPy shape: (3, 3) Tensor device: cuda:0 Tensor shape: torch.Size([3, 3]) GPU 训练 NumPy 评估完整示例 Device: cuda Training... Epoch 5: loss1.5234, acc0.3850 Epoch 10: loss1.1234, acc0.5412 Epoch 15: loss0.8765, acc0.6525 Evaluating... Predictions type: class numpy.ndarray, shape: (400,) Targets type: class numpy.ndarray, shape: (400,) Classification Report: precision recall f1-score support 0 0.65 0.70 0.67 40 1 0.62 0.55 0.58 40 ... accuracy 0.65 400 macro avg 0.65 0.65 0.65 400 weighted avg 0.65 0.65 0.65 400常见陷阱与注意事项陷阱 1忘记.detach()导致报错# 带梯度的张量不能直接 .numpy() x torch.randn(3, 3, requires_gradTrue).cuda() # 错误 # x.cpu().numpy() # RuntimeError: Cant call numpy() on Tensor that requires grad. # 正确 x.detach().cpu().numpy()陷阱 2数据类型不匹配# PyTorch 的 long 类型在 NumPy 中是 int64 t torch.tensor([1, 2, 3], dtypetorch.long).cuda() arr t.cpu().numpy() print(arr.dtype) # int64 # sklearn 的某些函数需要特定类型 # 确保类型匹配 arr_int32 arr.astype(np.int32)陷阱 3共享内存的副作用# CPU 张量和 NumPy 数组共享内存 t torch.randn(3, 3) arr t.numpy() # 修改 arr 会影响 t arr[0, 0] 999.0 print(t[0, 0]) # 999.0 # 如果不希望共享内存使用 .copy() arr_copy t.numpy().copy() arr_copy[0, 0] 888.0 print(t[0, 0]) # 仍然是 999.0陷阱 4torch.as_tensorvstorch.tensor# torch.as_tensor尽可能共享内存高效 np_array np.array([1, 2, 3]) t1 torch.as_tensor(np_array) # 修改 np_array 会影响 t1共享内存 # torch.tensor总是拷贝数据安全 t2 torch.tensor(np_array) # 修改 np_array 不影响 t2陷阱 5半精度FP16转换# FP16 张量转 NumPy t torch.randn(3, 3, dtypetorch.float16).cuda() arr t.cpu().numpy() print(arr.dtype) # float16 # 某些 NumPy 操作不支持 float16 # 可能需要先转 float32 arr_float32 arr.astype(np.float32)总结GPU 张量到 NumPy 的转换是 PyTorch 工程中的常见操作核心要点如下基本转换链tensor.detach().cpu().numpy()处理梯度、设备和类型三个问题。.cpu()是关键将张量从 GPU 移到 CPU这是 NumPy 能访问的前提。.detach()处理梯度带requires_gradTrue的张量必须先 detach。torch.no_grad()推理时使用避免构建计算图省去 detach 步骤。封装工具函数to_numpy()函数自动处理所有边界情况推荐使用。注意数据类型PyTorchlong→ NumPyint64可能需要astype转换。共享内存CPU 张量和 NumPy 数组共享内存修改一个会影响另一个。批量转换使用工具函数批量处理多个张量提高代码效率。记住这个万能公式tensor.detach().cpu().numpy()它适用于所有场景下的 GPU 张量到 NumPy 转换。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价