在Windows上使用PyTorch训练模型时,任务管理器里的GPU占用率比较低,这是正常的。可以使用nvidia-smi查看真实的GPU占用率。
任务管理器里的GPU占用率
使用nvidia-smi查看真实的GPU占用率
命令(dmon表示以实时监控的形式查看):
nvidia-smi dmon
各列含义:
=列名 | =全称 / 解释 | =单位/说明 |
gpu | GPU 索引 (Idx) | 系统中的 GPU 编号(从 0 开始),多 GPU 时用于区分不同显卡。 |
pwr | Power Draw | 当前 GPU 功耗(瓦特,W)。 |
gtemp | GPU Temperature | GPU 核心温度(摄氏度,℃)。 |
mtemp | Memory Temperature | 显存温度(℃),- 表示无传感器或未支持。 |
sm | Streaming Multiprocessor | 流处理器利用率(百分比,%),反映 CUDA 核心的计算负载。 |
mem | Memory Utilization | 显存带宽利用率(%),非显存容量占用,表示显存控制器的繁忙程度。 |
enc | Encoder Utilization | 视频编码器(如 NVENC)利用率(%)。 |
dec | Decoder Utilization | 视频解码器(如 NVDEC)利用率(%)。 |
mclk | Memory Clock | 显存时钟频率(MHz),越高表示显存速度越快。 |
pclk | Processor Clock | GPU 核心时钟频率(MHz),反映 GPU 核心的当前运行速度。 |
sm 和 mem 高百分比表明 GPU 计算或显存带宽成为瓶颈。在深度训练时,需要关注 sm 和 mem 是否接近 100%,确保 GPU 满载。
GPU占用率确实低的解决方案
可以在PyTorch DataLoader里设置num_workers和pin_memory参数解决。注意:在windows上设置num_workers后,训练函数必须放到if ~_~_name~_~_ == "~_~_main~_~_"里面,否则会报错。
注意:不要把num_workers设置的太大,创建worker也需要很多时间。可以从2开始加,只要保证使用nvidia-smi查看的GPU利用率在90%以上即可。然后调整batch_size,让显存占用率也在90%左右即可。
示例代码:
import torch
import torch.nn as nn
import torch.optim as optim
import torch.utils.data as data
from torchvision import datasets, transforms
num_epochs = 20
batch_size = 100
learning_rate = 0.001
transform = transforms.Compose(
[transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]
)
train_dataset = datasets.MNIST(
root="./data", train=True, transform=transform, download=True
)
test_dataset = datasets.MNIST(
root="./data", train=False, transform=transform, download=True
)
train_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2, pin_memory=True)
test_loader = data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class CNNModel(nn.Module):
def __init__(self):
super().__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.layer2 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.dropout = nn.Dropout()
self.fc1 = nn.Linear(7 * 7 * 64, 1000)
self.fc2 = nn.Linear(1000, 10)
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = out.reshape(out.size(0), -1)
out = self.dropout(out)
out = self.fc1(out)
out = self.fc2(out)
return out
model = CNNModel().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
def train():
model.train()
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
images = images.to(device)
labels = labels.to(device)
output = model(images)
loss = criterion(output, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (i + 1) % 100 == 0:
print(
"Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}".format(
epoch + 1, num_epochs, i + 1, len(train_loader), loss.item()
)
)
def test():
model.eval()
with torch.no_grad():
total = 0
correct = 0
for images, labels in test_loader:
images = images.to(device)
labels = labels.to(device)
output = model(images)
_, predict = torch.max(output, 1)
total += labels.size(0)
correct += (predict == labels).sum().item()
print("Accuracy of 10000 test images: {} %".format(correct / total * 100))
if __name__ == "__main__":
train()
test()