【深度学习PyTorch】4组对照实验说明为什么跑模型优先用GPU而非CPU
前言初学 PyTorch 的时候经常会看到一句话训练深度学习模型建议使用 GPU。 很多同学会有疑问GPU 到底能快多少CPU 是不是完全不能跑云端 GPU 和本地消费级显卡差距有多大本文搭建一个简单的 CNN 网络控制所有变量不变仅仅更换运行硬件做 4 组对照实验用真实训练耗时直观展示 GPU 带来的加速效果。实验统一配置数据集CIFAR‑10网络自定义小型 CNNbatch_size64epoch10框架PyTorch说明GPU 存在 CUDA 初始化预热现象统计平均耗时舍弃前 2 轮取第 3‑10 轮计算稳定平均时间。1 四组实验环境介绍四套实验使用完全同一套模型代码超参、数据集全部固定仅改变运行硬件。表格序号运行环境硬件说明1Kaggle NotebookTesla T4 云端 GPU2Kaggle Notebook加速器 None谷歌云端 CPU3本地 JupyterLab本机 RTX3050 GPU4本地 JupyterLab强制 CPU本机主机 CPU⚠重要区分Kaggle 是云端虚拟机Kaggle 的 CPU 是谷歌服务器的 CPU并不是我自己电脑的 CPU本地 JupyterLab 运行在个人电脑硬件之上。2 完整实验代码GPU 版本本地 RTX3050 / Kaggle‑T4 通用import time import torchvision.datasets from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter import torch import torch.nn as nn # 1. 创建设备 device torch.device(cuda if torch.cuda.is_available() else cpu) print(f使用设备: {device}) if torch.cuda.is_available(): print(GPU型号名称:, torch.cuda.get_device_name(device)) # 数据集与数据加载器 train_datasetstorchvision.datasets.CIFAR10(root./datasets,trainTrue,transformtorchvision.transforms.ToTensor(),downloadTrue) test_datasetstorchvision.datasets.CIFAR10(root./datasets,trainFalse,transformtorchvision.transforms.ToTensor(),downloadTrue) train_loaderDataLoader(datasettrain_datasets,batch_size64) test_loaderDataLoader(datasettest_datasets,batch_size64) # 定义网络 class TX(nn.Module): def __init__(self): super(TX, self).__init__() self.modelnn.Sequential( nn.Conv2d(in_channels3,out_channels32,kernel_size5,stride1,padding2), nn.MaxPool2d(kernel_size2), nn.Conv2d(in_channels32,out_channels32,kernel_size5,stride1,padding2), nn.MaxPool2d(kernel_size2), nn.Conv2d(in_channels32,out_channels64,kernel_size5,stride1,padding2), nn.MaxPool2d(kernel_size2), nn.Flatten(), nn.Linear(in_features64*4*4,out_features64), nn.Linear(in_features64,out_features10) ) def forward(self,input): outputself.model(input) return output # 2. 模型迁移到GPU txTX().to(device) # 损失函数、优化器 loss_functionnn.CrossEntropyLoss() optimizertorch.optim.SGD(tx.parameters(),lr0.01) train_total_step0 test_total_step0 epochs10 loss_total0 writeSummaryWriter(complete_project) # 保存每一轮耗时的列表 epoch_time_list [] for i in range(epochs): start_time time.time() print(f-----------第{i1}轮训练开始-----------) for data in train_loader: imgs,targetsdata imgs imgs.to(device) targets targets.to(device) outputtx(imgs) lossloss_function(output,targets) optimizer.zero_grad() loss.backward() optimizer.step() train_total_steptrain_total_step1 if train_total_step % 100 0: print(f训练次数{train_total_step},损失为{loss.item()}) write.add_scalar(train_loss, loss, train_total_step) with torch.no_grad(): loss_total 0 for data in test_loader: img,targetdata img img.to(device) target target.to(device) outputstx(img) lossloss_function(outputs,target) loss_totalloss_totalloss test_total_steptest_total_step1 write.add_scalar(test_loss, loss, test_total_step) print(f测试集损失{loss_total.item()}) end_timetime.time() one_epoch_time end_time - start_time epoch_time_list.append(one_epoch_time) print(f第{i1}轮训练结束时间消耗为{one_epoch_time:.2f}秒) write.close() # 训练结束输出完整耗时统计 print(\n 全部Epoch耗时汇总 ) for index, t in enumerate(epoch_time_list): print(fEpoch {index1}{t:.2f} s) avg_all_epoch sum(epoch_time_list) / len(epoch_time_list) warm_up 2 stable_time epoch_time_list[warm_up:] avg_stable sum(stable_time) / len(stable_time) print(f\n全部10轮平均耗时{avg_all_epoch:.2f} s) print(f舍弃前{warm_up}轮预热第3~10轮稳定平均耗时{avg_stable:.2f} s)CPU 版本强制只用 CPU 运算import time import torchvision.datasets from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter import torch import torch.nn as nn # 1. 强制使用CPU设备 device torch.device(cpu) print(f使用设备: {device}) # 数据集与数据加载器 train_datasetstorchvision.datasets.CIFAR10(root./datasets,trainTrue,transformtorchvision.transforms.ToTensor(),downloadTrue) test_datasetstorchvision.datasets.CIFAR10(root./datasets,trainFalse,transformtorchvision.transforms.ToTensor(),downloadTrue) train_loaderDataLoader(datasettrain_datasets,batch_size64) test_loaderDataLoader(datasettest_datasets,batch_size64) # 定义网络 class TX(nn.Module): def __init__(self): super(TX, self).__init__() self.modelnn.Sequential( nn.Conv2d(in_channels3,out_channels32,kernel_size5,stride1,padding2), nn.MaxPool2d(kernel_size2), nn.Conv2d(in_channels32,out_channels32,kernel_size5,stride1,padding2), nn.MaxPool2d(kernel_size2), nn.Conv2d(in_channels32,out_channels64,kernel_size5,stride1,padding2), nn.MaxPool2d(kernel_size2), nn.Flatten(), nn.Linear(in_features64*4*4,out_features64), nn.Linear(in_features64,out_features10) ) def forward(self,input): outputself.model(input) return output # 2. 模型部署到CPU txTX().to(device) # 损失函数、优化器 loss_functionnn.CrossEntropyLoss() optimizertorch.optim.SGD(tx.parameters(),lr0.01) train_total_step0 test_total_step0 epochs10 loss_total0 writeSummaryWriter(complete_project_cpu) # 保存每一轮耗时列表 epoch_time_list [] for i in range(epochs): start_time time.time() print(f-----------第{i1}轮训练开始-----------) for data in train_loader: imgs,targetsdata imgs imgs.to(device) targets targets.to(device) outputtx(imgs) lossloss_function(output,targets) optimizer.zero_grad() loss.backward() optimizer.step() train_total_steptrain_total_step1 if train_total_step % 100 0: print(f训练次数{train_total_step},损失为{loss.item()}) write.add_scalar(train_loss, loss, train_total_step) with torch.no_grad(): loss_total 0 for data in test_loader: img,targetdata img img.to(device) target target.to(device) outputstx(img) lossloss_function(outputs,target) loss_totalloss_totalloss test_total_steptest_total_step1 write.add_scalar(test_loss, loss, test_total_step) print(f测试集损失{loss_total.item()}) end_timetime.time() one_epoch_time end_time - start_time epoch_time_list.append(one_epoch_time) print(f第{i1}轮训练结束时间消耗为{one_epoch_time:.2f}秒) write.close() # 训练结束输出完整耗时统计 print(\n 全部Epoch耗时汇总 ) for index, t in enumerate(epoch_time_list): print(fEpoch {index1}{t:.2f} s) avg_all_epoch sum(epoch_time_list) / len(epoch_time_list) warm_up 2 stable_time epoch_time_list[warm_up:] avg_stable sum(stable_time) / len(stable_time) print(f\n全部10轮平均耗时{avg_all_epoch:.2f} s) print(f舍弃前{warm_up}轮预热第3~10轮稳定平均耗时{avg_stable:.2f} s)3 实验结果下面是四组实验控制台输出截图包含每轮 epoch 耗时以及统计结果。【图 1 Kaggle‑T4 运行输出】【图 2 Kaggle 云端 CPU 运行输出】【图 3 本地 RTX3050 运行输出】【图 4 本地本机 CPU 运行输出】耗时汇总表表格运行环境硬件每轮稳定平均耗时 (s)Kaggle NotebookTesla T4云端 GPU11.82本地 JupyterLabRTX3050本机 GPU12.50本地 JupyterLab本机家用 CPU27.35Kaggle NotebookKaggle 云端 CPU55.624 结果分析云端 T4 与本地 RTX3050 对比本次实验网络规模很小使用 CIFAR‑10 小尺寸图片两块显卡训练耗时差距很小。如果换成更深的网络、更大分辨率图像、调大 batch_size显卡之间性能差距会明显体现。GPU 与 CPU 的加速对比CPU 不是不能跑深度学习是跑的慢。在本轻量 CNN 上 GPU 就已经体现出速度优势当模型变为深层 CNN 或者 Transformer 大模型GPU 相对 CPU 加速倍数会进一步拉大。 原理CPU 擅长串行任务GPU 拥有大量并行计算单元非常适配卷积这种大规模矩阵并行运算。两份 CPU 性能差距明显Kaggle 免费版提供的云端 CPU 性能很差不能拿它当做普通家用 CPU 的参考基准做对照实验这点一定要区分开。5 实验踩坑记录Kaggle 选择 GPU 加速器时有可能随机分配到老旧 P100 显卡。新版 PyTorch 已经移除对 P100 的支持会报no kernel image is available for execution on the device错误。注意torch.cuda.is_available() True仅代表驱动识别显卡不等于可以正常执行卷积运算做实验尽量使用 T4。GPU 训练前 1‑2 个 epoch 时间会偏高属于 CUDA warm‑up 硬件初始化预热统计性能指标建议舍弃前两轮保证实验严谨。Kaggle 上通过代码downloadTrue下载的数据集保存在谷歌云端虚拟机不会占用本地电脑磁盘本地 JupyterLab 执行下载数据集会保存到本机硬盘。6 总结小型 CNN 任务下Kaggle 云端 T4 和本地消费级 RTX3050 性能接近。GPU 可以有效缩短模型训练时间模型越大GPU 带来的收益越明显。做硬件性能对照实验要固定全部超参只改变硬件GPU 实验需要剔除预热轮次。不要把 Kaggle 云端 CPU 性能等同于个人家用电脑 CPU 性能。说明基础 CNN 网络参考 B 站小土堆 PyTorch 入门课程本人自主增加耗时统计模块设计四组硬件在kaggle平台与本地jupyter Lab进行对照实验完成本次性能测试仅作为学习记录分享非商用。