深度学习框架PyTorch实战 前言 PyTorch作为最受欢迎的深度学习框架之一,以其灵活性和易用性赢得了研究者和工程师的青睐。本文将带你从零开始学习PyTorch,通过实战案例掌握深度学习的核心技能。
一、PyTorch基础 1.1 安装PyTorch 1 2 3 4 5 6 7 8 pip install torch torchvision torchaudio pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
1.2 张量(Tensor)基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import torchimport numpy as npx = torch.tensor([1 , 2 , 3 ]) print (x) arr = np.array([1 , 2 , 3 ]) x = torch.from_numpy(arr) zeros = torch.zeros(2 , 3 ) ones = torch.ones(2 , 3 ) random = torch.randn(2 , 3 ) uniform = torch.rand(2 , 3 ) arange = torch.arange(0 , 10 , 2 ) print (x.shape) print (x.dtype) print (x.device)
1.3 张量操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 a = torch.tensor([1.0 , 2.0 , 3.0 ]) b = torch.tensor([4.0 , 5.0 , 6.0 ]) c = a + b c = torch.add(a, b) c = a * b c = torch.matmul(a, b) x = torch.randn(2 , 3 , 4 ) y = x.view(2 , 12 ) z = x.reshape(2 , -1 ) w = x.transpose(1 , 2 ) x = torch.randn(3 , 4 ) print (x[0 , :]) print (x[:, 1 ]) print (x[0 :2 , 1 :3 ]) x = torch.randn(2 , 3 ) y = torch.randn(2 , 3 ) z = torch.cat([x, y], dim=0 ) z = torch.cat([x, y], dim=1 ) z = torch.stack([x, y], dim=0 )
1.4 GPU加速 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu' ) print (f'Using device: {device} ' )x = torch.randn(1000 , 1000 ) x = x.to(device) x = x.cuda() x = x.cpu() x = torch.randn(1000 , 1000 , device=device) a = torch.randn(1000 , 1000 , device='cuda' ) b = torch.randn(1000 , 1000 , device='cuda' ) c = torch.matmul(a, b)
二、自动微分 2.1 基础自动微分 1 2 3 4 5 6 7 8 9 10 11 12 x = torch.tensor([2.0 ], requires_grad=True ) y = x ** 2 + 3 * x + 1 y.backward() print (x.grad) x.grad.zero_()
2.2 复杂计算图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 x = torch.tensor([1.0 , 2.0 , 3.0 ], requires_grad=True ) w = torch.tensor([0.5 , 0.3 , 0.2 ], requires_grad=True ) b = torch.tensor([1.0 ], requires_grad=True ) y = torch.sum (x * w) + b loss = y ** 2 loss.backward() print (f'x.grad: {x.grad} ' ) print (f'w.grad: {w.grad} ' ) print (f'b.grad: {b.grad} ' )
2.3 梯度管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 with torch.no_grad(): y = x * 2 print (y.requires_grad) @torch.no_grad() def inference (x ): return x * 2 x = torch.tensor([1.0 ], requires_grad=True ) y = x ** 2 z = y.detach() with torch.enable_grad(): y = x * 2
三、构建神经网络 3.1 nn.Module基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import torch.nn as nnimport torch.nn.functional as Fclass SimpleNN (nn.Module): def __init__ (self, input_size, hidden_size, output_size ): super (SimpleNN, self ).__init__() self .fc1 = nn.Linear(input_size, hidden_size) self .fc2 = nn.Linear(hidden_size, output_size) def forward (self, x ): x = F.relu(self .fc1(x)) x = self .fc2(x) return x model = SimpleNN(784 , 128 , 10 ) print (model)for name, param in model.named_parameters(): print (f'{name} : {param.shape} ' ) x = torch.randn(32 , 784 ) output = model(x) print (output.shape)
3.2 常用层 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fc = nn.Linear(in_features=128 , out_features=64 ) conv2d = nn.Conv2d(in_channels=3 , out_channels=64 , kernel_size=3 , padding=1 ) maxpool = nn.MaxPool2d(kernel_size=2 , stride=2 ) avgpool = nn.AvgPool2d(kernel_size=2 ) dropout = nn.Dropout(p=0.5 ) bn = nn.BatchNorm2d(num_features=64 ) relu = nn.ReLU() sigmoid = nn.Sigmoid() tanh = nn.Tanh()
3.3 Sequential容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 model = nn.Sequential( nn.Linear(784 , 256 ), nn.ReLU(), nn.Dropout(0.2 ), nn.Linear(256 , 128 ), nn.ReLU(), nn.Dropout(0.2 ), nn.Linear(128 , 10 ) ) x = torch.randn(32 , 784 ) output = model(x)
3.4 CNN示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class CNN (nn.Module): def __init__ (self, num_classes=10 ): super (CNN, self ).__init__() self .conv1 = nn.Conv2d(1 , 32 , kernel_size=3 , padding=1 ) self .conv2 = nn.Conv2d(32 , 64 , kernel_size=3 , padding=1 ) self .pool = nn.MaxPool2d(kernel_size=2 , stride=2 ) self .fc1 = nn.Linear(64 * 7 * 7 , 128 ) self .fc2 = nn.Linear(128 , num_classes) self .dropout = nn.Dropout(0.5 ) def forward (self, x ): x = F.relu(self .conv1(x)) x = self .pool(x) x = F.relu(self .conv2(x)) x = self .pool(x) x = x.view(x.size(0 ), -1 ) x = F.relu(self .fc1(x)) x = self .dropout(x) x = self .fc2(x) return x model = CNN(num_classes=10 )
四、模型训练 4.1 完整训练流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import torchimport torch.nn as nnimport torch.optim as optimfrom torch.utils.data import DataLoader, TensorDatasetX_train = torch.randn(1000 , 784 ) y_train = torch.randint(0 , 10 , (1000 ,)) train_dataset = TensorDataset(X_train, y_train) train_loader = DataLoader(train_dataset, batch_size=32 , shuffle=True ) model = SimpleNN(784 , 128 , 10 ) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu' ) model = model.to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001 ) num_epochs = 10 for epoch in range (num_epochs): model.train() running_loss = 0.0 correct = 0 total = 0 for batch_idx, (data, target) in enumerate (train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() outputs = model(data) loss = criterion(outputs, target) loss.backward() optimizer.step() running_loss += loss.item() _, predicted = torch.max (outputs.data, 1 ) total += target.size(0 ) correct += (predicted == target).sum ().item() if (batch_idx + 1 ) % 10 == 0 : print (f'Epoch [{epoch+1 } /{num_epochs} ], ' f'Step [{batch_idx+1 } /{len (train_loader)} ], ' f'Loss: {loss.item():.4 f} ' ) epoch_loss = running_loss / len (train_loader) epoch_acc = 100 * correct / total print (f'Epoch [{epoch+1 } /{num_epochs} ] - ' f'Loss: {epoch_loss:.4 f} , Accuracy: {epoch_acc:.2 f} %' )
4.2 验证和测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def evaluate (model, data_loader, device ): model.eval () correct = 0 total = 0 with torch.no_grad(): for data, target in data_loader: data, target = data.to(device), target.to(device) outputs = model(data) _, predicted = torch.max (outputs.data, 1 ) total += target.size(0 ) correct += (predicted == target).sum ().item() accuracy = 100 * correct / total return accuracy test_acc = evaluate(model, test_loader, device) print (f'Test Accuracy: {test_acc:.2 f} %' )
4.3 学习率调度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10 , gamma=0.1 ) scheduler = optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode='min' , factor=0.5 , patience=5 ) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10 ) for epoch in range (num_epochs): train(...) val_loss = validate(...) scheduler.step() scheduler.step(val_loss)
五、实战:MNIST手写数字识别 5.1 加载数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from torchvision import datasets, transformstransform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307 ,), (0.3081 ,)) ]) train_dataset = datasets.MNIST( root='./data' , train=True , download=True , transform=transform ) test_dataset = datasets.MNIST( root='./data' , train=False , transform=transform ) train_loader = DataLoader(train_dataset, batch_size=64 , shuffle=True ) test_loader = DataLoader(test_dataset, batch_size=1000 , shuffle=False )
5.2 定义模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class MNISTNet (nn.Module): def __init__ (self ): super (MNISTNet, self ).__init__() self .conv1 = nn.Conv2d(1 , 32 , 3 , 1 ) self .conv2 = nn.Conv2d(32 , 64 , 3 , 1 ) self .dropout1 = nn.Dropout(0.25 ) self .dropout2 = nn.Dropout(0.5 ) self .fc1 = nn.Linear(9216 , 128 ) self .fc2 = nn.Linear(128 , 10 ) def forward (self, x ): x = self .conv1(x) x = F.relu(x) x = self .conv2(x) x = F.relu(x) x = F.max_pool2d(x, 2 ) x = self .dropout1(x) x = torch.flatten(x, 1 ) x = self .fc1(x) x = F.relu(x) x = self .dropout2(x) x = self .fc2(x) return F.log_softmax(x, dim=1 ) model = MNISTNet().to(device)
5.3 训练和评估 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 optimizer = optim.Adam(model.parameters(), lr=0.001 ) criterion = nn.NLLLoss() def train (model, device, train_loader, optimizer, epoch ): model.train() for batch_idx, (data, target) in enumerate (train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() if batch_idx % 100 == 0 : print (f'Train Epoch: {epoch} [{batch_idx * len (data)} /{len (train_loader.dataset)} ] ' f'Loss: {loss.item():.6 f} ' ) def test (model, device, test_loader ): model.eval () test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += criterion(output, target).item() pred = output.argmax(dim=1 , keepdim=True ) correct += pred.eq(target.view_as(pred)).sum ().item() test_loss /= len (test_loader.dataset) accuracy = 100. * correct / len (test_loader.dataset) print (f'\nTest set: Average loss: {test_loss:.4 f} , ' f'Accuracy: {correct} /{len (test_loader.dataset)} ({accuracy:.2 f} %)\n' ) for epoch in range (1 , 11 ): train(model, device, train_loader, optimizer, epoch) test(model, device, test_loader)
六、模型保存与加载 6.1 保存和加载模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 torch.save(model, 'model.pth' ) model = torch.load('model.pth' ) torch.save(model.state_dict(), 'model_weights.pth' ) model = MNISTNet() model.load_state_dict(torch.load('model_weights.pth' )) model.eval () checkpoint = { 'epoch' : epoch, 'model_state_dict' : model.state_dict(), 'optimizer_state_dict' : optimizer.state_dict(), 'loss' : loss, } torch.save(checkpoint, 'checkpoint.pth' ) checkpoint = torch.load('checkpoint.pth' ) model.load_state_dict(checkpoint['model_state_dict' ]) optimizer.load_state_dict(checkpoint['optimizer_state_dict' ]) epoch = checkpoint['epoch' ] loss = checkpoint['loss' ]
七、总结 本文介绍了PyTorch的核心概念和实战应用:
关键知识点
张量操作 :PyTorch的基础数据结构
自动微分 :反向传播的核心
nn.Module :构建神经网络的基石
训练循环 :完整的模型训练流程
下一步学习
迁移学习(Transfer Learning)
对抗生成网络(GAN)
Transformer架构
模型部署(TorchScript、ONNX)
掌握PyTorch,开启你的深度学习之旅!
参考资料