在本文中,我将简要描述LeNet-5体系结构,并展示如何在PyTorch中实现LeNet-5。本文假定您对卷积神经网络的基础知识有一定的了解,包括卷积层、池化层、全连接层等概念。 理论介绍LeNet-5是一个7层卷积神经网络,是在大小为32x32像素的灰度图像上进行训练的。 命名约定:
鉴于LeNet-5是相对简单的现代标准,我们可以分别检查每一层,以获得对架构的良好理解。但是,在继续之前,有必要提醒一下卷积层输出大小的计算公式。 公式为:(W−F+2P)/S+1,其中W是输入的高度/宽度(通常图像是正方形,因此不需要区分两者),F是filter/核的大小,P是padding,S是stride。 看了上面的体系结构架构和公式后,我们可以遍历LeNet-5的每一层。
PyTorch实现现在,我将展示如何在PyTorch中实现LeNet-5(略有简化)。我们将在MNIST数据集上训练网络。我们首先导入必需的Python库。 import numpy as npfrom datetime import datetime import torchimport torch.nn as nnimport torch.nn.functional as Ffrom torch.utils.data import DataLoaderfrom torchvision import datasets, transformsimport matplotlib.pyplot as plt# check deviceDEVICE = 'cuda' if torch.cuda.is_available() else 'cpu' 此外,我们还检查了GPU是否可用,并相应地设置DEVICE变量。在下一步中,我们设置一些参数(例如随机种子、学习率、batch size、epochs数等),稍后将在建立神经网络时使用这些参数。
接下来我们定义一些用于训练神经网络的辅助函数。由于大多数情况下的总体思想非常相似,您可以根据自己的需要稍微修改函数,并将它们用于训练各种网络。我们从训练部分的函数开始: def train(train_loader, model, criterion, optimizer, device): ''' Function for the training step of the training loop ''' model.train() running_loss = 0 for X, y_true in train_loader: optimizer.zero_grad() X = X.to(device) y_true = y_true.to(device) # Forward pass y_hat, _ = model(X) loss = criterion(y_hat, y_true) running_loss += loss.item() * X.size(0) # Backward pass loss.backward() optimizer.step() epoch_loss = running_loss / len(train_loader.dataset) return model, optimizer, epoch_loss 在train函数中,对于每一批实例,我们执行以下步骤:
请注意,对于训练阶段,模型处于训练模式(model.train())中,我们还需要将每个批次的梯度归零。此外,我们还计算了训练步骤中的运行损失。 然后,我们定义负责验证的函数。
验证函数与训练函数非常相似,区别在于缺少实际的学习步骤(backward pass)。最后,我们在训练循环中将它们结合起来: def get_accuracy(model, data_loader, device): ''' Function for computing the accuracy of the predictions over the entire data_loader ''' correct_pred = 0 n = 0 with torch.no_grad(): model.eval() for X, y_true in data_loader: X = X.to(device) y_true = y_true.to(device) _, y_prob = model(X) _, predicted_labels = torch.max(y_prob, 1) n += y_true.size(0) correct_pred += (predicted_labels == y_true).sum() return correct_pred.float() / ndef plot_losses(train_losses, valid_losses): ''' Function for plotting training and validation losses ''' # temporarily change the style of the plots to seaborn plt.style.use('seaborn') train_losses = np.array(train_losses) valid_losses = np.array(valid_losses) fig, ax = plt.subplots(figsize = (8, 4.5)) ax.plot(train_losses, color='blue', label='Training loss') ax.plot(valid_losses, color='red', label='Validation loss') ax.set(title='Loss over epochs', xlabel='Epoch', ylabel='Loss') ax.legend() fig.show() # change the plot style to default plt.style.use('default')def training_loop(model, criterion, optimizer, train_loader, valid_loader, epochs, device, print_every=1): ''' Function defining the entire training loop ''' # set objects for storing metrics best_loss = 1e10 train_losses = [] valid_losses = [] # Train model for epoch in range(0, epochs): # training model, optimizer, train_loss = train(train_loader, model, criterion, optimizer, device) train_losses.append(train_loss) # validation with torch.no_grad(): model, valid_loss = validate(valid_loader, model, criterion, device) valid_losses.append(valid_loss) if epoch % print_every == (print_every - 1): train_acc = get_accuracy(model, train_loader, device=device) valid_acc = get_accuracy(model, valid_loader, device=device) print(f'{datetime.now().time().replace(microsecond=0)} --- ' f'Epoch: {epoch}\t' f'Train loss: {train_loss:.4f}\t' f'Valid loss: {valid_loss:.4f}\t' f'Train accuracy: {100 * train_acc:.2f}\t' f'Valid accuracy: {100 * valid_acc:.2f}') plot_losses(train_losses, valid_losses) return model, optimizer, (train_losses, valid_losses) 在训练循环中,对于每个epoch,我们同时运行训练和验证函数,后者使用torch.no_grad()运行,以便不更新权重和节省一些计算时间。除了用于训练的损失函数之外,我们还使用定制的get_accuracy函数计算模型对于训练和验证步骤的准确性。 定义了辅助函数后,就该准备数据了。我们使用MNIST机器学习数据集。
在上面的代码段中,我们首先定义了一组要应用于源图像的转换。我们首先将图像调整为32×32(LeNet-5的输入大小),然后将其转换为张量。transforms.ToTensor()自动将图像缩放到[0,1]范围内。除了调整图像的大小,我们还可以对图像应用某种填充。在最简单的情况下,我们只需在原始图像的每一侧添加2个零。 第二步是定义机器学习数据集。对于训练对象,我们指定了download=True来下载数据集。 最后,我们进行DataLoaders实例化。 接下来,您可以查看来自训练集的50张图像的预览。 ROW_IMG = 10N_ROWS = 5fig = plt.figure()for index in range(1, ROW_IMG * N_ROWS + 1): plt.subplot(N_ROWS, ROW_IMG, index) plt.axis('off') plt.imshow(train_dataset.data[index], cmap='gray_r')fig.suptitle('MNIST Dataset - preview'); 最后,是时候定义LeNet-5架构了。
从上面的类定义,您可以看到与原始网络相比的一些简化:
定义类之后,我们需要实例化模型(并将其发送到正确的设备)、优化器(本例中为ADAM)和损失函数(交叉熵)。最后两个与论文中最初使用的方法不同。 torch.manual_seed(RANDOM_SEED)model = LeNet5(N_CLASSES).to(DEVICE)optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)criterion = nn.CrossEntropyLoss() 一切就绪后,我们可以通过运行以下Python代码来训练网络:
输出如下: 为了评估机器学习模型的预测,我们可以运行以下代码,它显示来自验证集的一组数字,以及预测的标签和网络分配给该标签的概率。 ROW_IMG = 10N_ROWS = 5fig = plt.figure()for index in range(1, ROW_IMG * N_ROWS + 1): plt.subplot(N_ROWS, ROW_IMG, index) plt.axis('off') plt.imshow(valid_dataset.data[index], cmap='gray_r') with torch.no_grad(): model.eval() _, probs = model(valid_dataset[index][0].unsqueeze(0)) title = f'{torch.argmax(probs)} ({torch.max(probs * 100):.0f}%)' plt.title(title, fontsize=7)fig.suptitle('LeNet-5 - predictions'); 结论在本文中,我描述了LeNet-5的体系结构,并展示了如何使用著名的MNIST数据集来进行实现和训练。由于这是最早的CNN架构之一,因此相对简单易懂。 为了进一步提高网络性能,可能有必要尝试一些数据增强。我们可以对图像应用旋转或剪切等转换(使用torchvision.transforms),以创建更多样化的数据集。我们还应该注意,并不是所有的转换都适用于数字识别,例如通过翻转图像来创建镜像反射。 |
|
来自: taotao_2016 > 《计算机》