This repo contains notebooks and related code for Udacity's Deep Learning with PyTorch lesson. This lesson appears in our AI Programming with Python Nanodegree program.
目录
Part 1: Introduction to PyTorch and using tensors
Neural Networks
Tensors
Numpy 和 Tensor 的互转换
Part 2: Building fully-connected neural networks with PyTorch
Part 3: How to train a fully-connected network with backpropagation on MNIST
反向传播
Pytorch中的损失函数
Autograd
Loss and Autograd together
训练神经网络
实战训练
Part 4: Exercise - train a neural network on Fashion-MNIST
分类Fashion-MNIST数据集
构建网络
训练网络
Part 5: Using a trained network for making predictions and validating networks
过拟合情况
使用dropout
Part 6: How to save and load trained models
Part 7: Load image data with torchvision, also data augmentation
Part 8: Use transfer learning to train a state-of-the-art image classifier for dogs and cats
迁移学习
深度学习是以人工神经网络为基础的,自20世纪50年代末以来,人工神经网络以某种形式出现。网络是由近似神经元的各个部分组成的,通常称为单元或简单的“神经元”。每个单元都有一定数量的加权输入。这些加权输入相加(线性组合),然后通过激活函数得到单元组的输出。
从数学上讲,这看起来像:
对于向量,这是两个向量的点/内积:
神经网络的计算只是张量的一系列线性代数运算,是矩阵的推广。向量是一维张量,矩阵是二维张量,具有三个索引的数组是三维张量(例如RGB彩色图像)。神经网络的基本数据结构是张量,PyTorch(以及几乎所有其他深度学习框架)都是围绕张量构建的。(下图是一二三维张量诗意图)
在介绍了基本知识之后,是时候探索如何使用PyTorch构建一个简单的神经网络了。
import torch
def activation(x):
""" Sigmoid 激励函数
Arguments
---------
x: torch.Tensor
"""
return 1/(1+torch.exp(-x))
torch.manual_seed(7) # 定下随机种子,可使随机数每次都固定。
# 初始化输入特征
features = torch.randn((1, 2)) # 输入特征变量是2个服从标准正态分布的值,例如:tensor([[-0.1468, 0.7861]])
# 初始化权重
weights = torch.randn_like(features) # tensor([[ 0.9468, -1.1143]])
# 初始化偏置
bias = torch.randn((1, 1)) # tensor([[1.6908]])
# 计算网络输出(两种方式)
y = activation(torch.sum(features * weights) + bias) # 结果:tensor([[0.5048]])
y = activation((features * weights).sum() + bias) # 结果:tensor([[0.5048]])
这就是计算单个神经元输出的方法。当你开始将这些单独的单元层层叠加,形成一个神经元网络时,这个算法的真正威力就显现出来了。一层神经元的输出成为下一层神经元的输入。对于多个输入单元和输出单元,我们现在需要将权重表示为矩阵。
隐藏层的输出:
底部显示的第一层是输入,可以理解为输入层。中间层称为隐藏层,最后一层是输出层。我们可以再次用矩阵来表示这个网络,并用矩阵乘法在一次运算中得到每个单元的线性组合。例如,隐藏层(h1和h2)的输出结果为:
最终的输出:
进一步,通过将隐藏层作为输出单元的输入,可以找到该小网络的最终输出。网络输出简单表示:
import torch
def activation(x):
""" Sigmoid激励函数
Arguments
---------
x: torch.Tensor
"""
return 1/(1+torch.exp(-x))
torch.manual_seed(7) # 定下随机种子,可使随机数每次都固定。
# 初始化输入特征
features = torch.randn((1, 3))
# 定义每层的单元个数
n_input = features.shape[1]
n_hidden = 2
n_output = 1
# 定义 “输入层” 到 “隐藏层” 的权重组
W1 = torch.randn(n_input, n_hidden)
# 定义 “隐藏层” 到 “输出层” 的权重组
W2 = torch.randn(n_hidden, n_output)
# 定义每层的偏置
B1 = torch.randn(1, n_hidden)
B2 = torch.randn(1, n_output)
# 计算 “隐藏层” 的输出
h = activation(torch.matmul(features, W1) + B1) # tensor([[0.6813, 0.4355]])
# 计算 “输出层” 的输出
output = activation(torch.matmul(h, W2) + B2) # tensor([[0.3171]])
隐单元数是网络的一个参数,通常称为超参数,以区别于权值和偏差参数。当我们稍后讨论训练神经网络时,您将看到,网络的隐藏单元越多,层数越多,从数据中学习和做出准确预测的能力就越强。
import numpy as np
import torch
a = np.random.rand(4, 2)
"""
array([[0.00367108, 0.39036655],
[0.24232557, 0.58723213],
[0.79328271, 0.37408149],
[0.20636911, 0.82519521]])
"""
b = torch.from_numpy(a) # numpy 转 tensor
"""
tensor([[0.0037, 0.3904],
[0.2423, 0.5872],
[0.7933, 0.3741],
[0.2064, 0.8252]], dtype=torch.float64)
"""
b.numpy() # tensor 转 numpy
"""
array([[0.00367108, 0.39036655],
[0.24232557, 0.58723213],
[0.79328271, 0.37408149],
[0.20636911, 0.82519521]])
"""
"""
特别注意:Numpy数组和Torch张量之间的转换,是共享内存的,因此如果更改一个对象的值,另一个也会更改。
"""
深度学习网络往往是具有几十层或几百层的庞大网络,这就是“深度”一词的由来。您可以像我们在上一个笔记本中所做的那样,仅使用权重矩阵来构建其中一个深度网络,但一般来说,它非常麻烦,而且很难实现。PyTorch有一个很好的模块nn,它提供了一个很好的方法来有效地构建大型神经网络。
现在我们要建立一个更大的网络来解决一个(以前的)难题,识别图像中的文本。这里我们将使用MNIST数据集,它由灰度手写数字组成。每个图像是28x28像素,您可以看到下面的示例:
我们的目标是建立一个神经网络,可以识别图像中的数字。
import torch
import numpy as np
from torchvision import datasets, transforms
import torchvision
from torch import nn
import torch.nn.functional as F
import helper
import matplotlib.pyplot as plt
class Network(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 128)
self.fc2 = nn.Linear(128, 64)
self.fc3 = nn.Linear(64, 10)
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.relu(x)
x = self.fc3(x)
x = F.softmax(x, dim=1)
return x
# 1 加载数据
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,)),
])
trainset = datasets.MNIST('./MNIST_data/', download=False, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
# Grab some data
dataiter = iter(trainloader)
images, labels = dataiter.next()
"""
images:torch.Size([64, 1, 28, 28])
labels:torch.Size([64])
"""
# 2 模型初始化
model = Network()
"""
Network(
(fc1): Linear(in_features=784, out_features=128, bias=True)
(fc2): Linear(in_features=128, out_features=64, bias=True)
(fc3): Linear(in_features=64, out_features=10, bias=True)
)
"""
# 3 计算一次前向传播
img_idx = 0
ps = model.forward(images[img_idx,:])
"""
ps:tensor([[0.1025, 0.0931, 0.1103, 0.0969, 0.1028, 0.0995, 0.0913, 0.0868, 0.1063, 0.1105]], grad_fn=)
"""
# 4 可视化该图片一次前向传播预测的分类概率
img_idx = 0
img = images[img_idx]
helper.view_classify(img.view(1, 28, 28), ps)
结果:
我们在上一部分建立的网络不是那么智能,它对我们手写的数字一无所知。具有非线性激活的神经网络就像通用函数逼近器一样工作。有一个函数可以将输入映射到输出。例如,对手写数字图像进行分类概率。神经网络的强大之处在于我们可以训练它们来逼近这个函数,基本上任何函数只要有足够的数据和计算时间。
起初,网络是幼稚的,它不知道将输入映射到输出的函数。我们通过显示实际数据的示例来训练网络,然后调整网络参数,使其近似于此函数。
为了找到这些参数,我们需要知道网络对实际输出的预测有多差。为此,我们计算了一个损失函数(也称为成本),这是我们预测误差的一个度量。例如,均方损失函数通常用于回归和二元分类问题:
其中,n是训练样本数,y是真实标签值,y^是预测标签值。
通过最小化与网络参数相关的损失,我们可以找到损失最小的配置,并且网络能够以高精度预测正确的标签。我们通过一个叫做梯度下降的过程找到这个最小值。梯度是损失函数的斜率,指向变化最快的方向。为了在最短的时间内达到最小值,我们需要跟随梯度(向下)。你可以把这想象成沿着最陡的斜坡下山到山脚。
对于单层网络,梯度下降很容易实现。然而,对于更深层的多层神经网络来说就更复杂了,就像我们之前建立的那个神经网络。复杂到研究人员花了大约30年才弄清楚如何训练多层网络。
多层网络的训练是通过反向传播来完成的,这实际上只是微积分链式规则的一个应用。如果我们把一个两层的网络转换成一个图形表示,这是最容易理解的。
在向前通过网络的过程中,我们的数据和操作从下到上。我们将输入x通过一个线性变换L1,该线性变换具有权重W1和偏差b1。然后输出经过sigmoid运算S和另一个线性变换L2。最后我们计算损失ℓ. 我们用损失来衡量网络的预测有多糟糕。然后,目标是调整权重和偏差以最小化损失。
为了训练梯度下降的权值,我们通过网络向后传播损失的梯度。每个操作在输入和输出之间都有一定的梯度。当我们向后发送梯度时,我们将传入的梯度与操作的梯度相乘。从数学上讲,这实际上只是用链式法则计算损失相对于权重的梯度。
注意:我在这里强调的一些细节需要一些向量微积分的知识,但它们不是理解发生了什么的必要条件。
我们使用这个梯度以一定的学习率来更新我们的权重α.
让我们先看看如何用PyTorch计算损失。通过 nn 模块,PyTorch提供诸如交叉熵损失(nn.CrossEntropyLoss)之类的损失。你通常会看到损失分配给 criterion。如前一部分所述,对于一个分类问题,例如MNIST,我们使用softmax函数来预测类概率。对于softmax输出,您希望使用交叉熵作为损失。要实际计算损失,首先定义 criterion,然后传入网络的预测值和标签值。
这里有件很重要的事要注意。查看nn.CrossEntropyLoss的文档,
这意味着我们需要将网络的原始输出传入loss,而不是softmax函数的输出。这个原始输出通常称为 logits 或 scores 。我们使用logits是因为softmax提供的概率通常非常接近于0或1,但浮点数不能准确地表示接近0或1的值(请参阅此处)。通常最好避免使用概率进行计算,通常我们使用对数概率。
既然我们知道了如何计算损失,那么如何使用它来执行反向传播呢?Torch提供了一个模块autograd,用于自动计算张量的梯度。我们可以用它来计算所有参数相对于损失的梯度。Autograd的工作原理是跟踪在张量上执行的操作,然后向后遍历这些操作,计算沿途的梯度。为了确保PyTorch跟踪张量上的操作并计算梯度,需要在张量上设置requires_grad=True。您可以在创建时使用requires_grad关键字执行此操作,也可以随时使用x.requires_grad_(True)。
如果你想关闭梯度计算,可以如下操作:
当我们用PyTorch创建一个网络时,所有参数都用requires_grad=True初始化。这意味着在计算loss和call loss.backward()时,会计算参数的梯度。这些梯度用于使用梯度下降更新权重。下面您可以看到一个使用反向传播计算梯度的示例。
# Build a feed-forward network
model = nn.Sequential(nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 10),
nn.LogSoftmax(dim=1))
criterion = nn.NLLLoss()
images, labels = next(iter(trainloader))
images = images.view(images.shape[0], -1)
logps = model(images)
loss = criterion(logps, labels)
print('Before backward pass: \n', model[0].weight.grad)
loss.backward()
print('After backward pass: \n', model[0].weight.grad)
pytorch中训练网络的一般流程:
下面我将通过一个训练step,打印出权重和梯度,这样你就可以看到它是如何变化的。注意,我有一行代码优化器optimizer.zero_grad()。当您使用相同的参数进行多次反向传播时,梯度将累积。这意味着您需要在每个训练过程中将梯度归零,否则您将保留以前训练batchs的梯度。
现在我们将把这个算法放到一个循环中,这样我们就可以遍历所有的图像。在某些术语中,遍历整个数据集的一个过程称为epoch。所以在这里,我们将通过trainloader循环来获得我们的训练批。对于每一批,我们将进行一次前向传播,计算损失,反向传播,并更新权重。
通过网络的训练,我们可以检验它的预测能力:
现在我们的网络很好。它能准确地预测图像中的数字。接下来,您将编写在更复杂的数据集上训练神经网络的代码。
现在轮到你建立和训练神经网络了。您将使用时尚MNIST数据集,它是MNIST数据集的替代品。MNIST实际上对于神经网络来说是非常微不足道的,在那里你可以很容易地获得超过97%的准确率。Fashion MNIST是一组28x28的灰色衣服图像。它比MNIST更复杂,因此它更好地表示了网络的实际性能,并且更好地表示了将在现实世界中使用的数据集。
在这个笔记本里,你将建立你自己的神经网络。在大多数情况下,您可以复制并粘贴Part 3的代码,但这样你学不会。对你来说,自己编写代码并让它工作是很重要的。不过,当你完成这项工作时,请随意查阅之前的笔记本。
import torch
from torchvision import datasets, transforms
import helper
# Define a transform to normalize the data
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))])
# Download and load the training data
trainset = datasets.FashionMNIST('./F_MNIST_data/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
# Download and load the test data
testset = datasets.FashionMNIST('./F_MNIST_data/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)
在这里我们可以看到其中一张图片:
在这里你应该定义你的网络。与MNIST一样,每个图像是28x28,总共784个像素,有10个类。应该至少包含一个隐藏层。我们建议您对层使用ReLU激活,并从前向传播返回logits或log-softmax。这取决于你添加了多少层以及这些层的大小。
from torch import nn, optim
import torch.nn.functional as F
# TODO: Define your network architecture here
class Classifier(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 64)
self.fc4 = nn.Linear(64, 10)
def forward(self, x):
# make sure input tensor is flattened
x = x.view(x.shape[0], -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.relu(self.fc3(x))
x = F.log_softmax(self.fc4(x), dim=1)
return x
通过调整超参数(隐藏单位,学习率等),你应该能够得到低于0.4的训练损失。
# TODO: Create the network, define the criterion and optimizer
model = Classifier()
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.003)
# TODO: Train the network here
epochs = 5
for e in range(epochs):
running_loss = 0
for images, labels in trainloader:
log_ps = model(images)
loss = criterion(log_ps, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
else:
print(f"Training loss: {running_loss/len(trainloader)}")
现在你有了一个训练好的网络,你可以用它来做预测。这通常被称为inference,一个借用自统计学的术语。然而,神经网络往往在训练数据上表现得太好,无法推广到以前没有见过的数据。这称为过拟合,它会影响推理效果。我们通过正则化避免过拟合。在本笔记本中,我将向您展示如何在PyTorch中执行此操作。
像往常一样,让我们首先通过torchvision加载数据集。在后面的部分中,您将了解有关torchvision和加载数据的更多信息。这次我们将利用test 集,您可以通过在此处设置train=False来获得该测试集:
通常,您会看到10-20%的原始数据集用于test和validation ,其余的用于train。
import torch
from torchvision import datasets, transforms
from torch import nn, optim
import torch.nn.functional as F
class Classifier(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 64)
self.fc4 = nn.Linear(64, 10)
def forward(self, x):
# make sure input tensor is flattened
x = x.view(x.shape[0], -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.relu(self.fc3(x))
x = F.log_softmax(self.fc4(x), dim=1)
return x
# Define a transform to normalize the data
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))])
# Download and load the training data
trainset = datasets.FashionMNIST('./F_MNIST_data/', download=False, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
# Download and load the test data
testset = datasets.FashionMNIST('./F_MNIST_data/', download=False, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)
model = Classifier()
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.003)
epochs = 30
train_losses, test_losses = [], []
for e in range(epochs):
tot_train_loss = 0
for images, labels in trainloader:
optimizer.zero_grad()
log_ps = model(images)
loss = criterion(log_ps, labels)
tot_train_loss += loss.item()
loss.backward()
optimizer.step()
else:
tot_test_loss = 0
test_correct = 0 # Number of correct predictions on the test set
# Turn off gradients for validation, saves memory and computations
with torch.no_grad():
for images, labels in testloader:
log_ps = model(images)
loss = criterion(log_ps, labels)
tot_test_loss += loss.item()
ps = torch.exp(log_ps)
top_p, top_class = ps.topk(1, dim=1)
equals = top_class == labels.view(*top_class.shape)
test_correct += equals.sum().item()
# Get mean loss to enable comparison between train and test sets
train_loss = tot_train_loss / len(trainloader.dataset)
test_loss = tot_test_loss / len(testloader.dataset)
# At completion of epoch
train_losses.append(train_loss)
test_losses.append(test_loss)
print("Epoch: {}/{}.. ".format(e+1, epochs),
"Training Loss: {:.3f}.. ".format(train_loss),
"Test Loss: {:.3f}.. ".format(test_loss),
"Test Accuracy: {:.3f}".format(test_correct / len(testloader.dataset)))
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
plt.plot(train_losses, label='Training loss')
plt.plot(test_losses, label='Validation loss')
plt.legend(frameon=False)
结果:
从上图可看出,训练集的loss持续下降过程中,test集的loss已经停滞,这表示模型在开始过拟合了。
在网络中新增dropout来减缓过拟合
## TODO: Define your model with dropout added
class Classifier(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 64)
self.fc4 = nn.Linear(64, 10)
self.dropout = nn.Dropout(p=0.2) #0.2的概率失活
def forward(self, x):
x = x.view(x.shape[0], -1)
x = self.dropout(F.relu(self.fc1(x)))
x = self.dropout(F.relu(self.fc2(x)))
x = self.dropout(F.relu(self.fc3(x)))
x = F.log_softmax(self.fc4(x),dim=1)
return x
重新训练网络后的结果:
从上图可看出,网络中增加dropout后,test集的loss基本和训练集接近。
在本笔记本中,我将向您展示如何使用PyTorch保存和加载模型。这一点很重要,因为您通常希望加载以前训练过的模型,以便用于进行预测或继续训练新数据。
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torchvision import datasets, transforms
import helper
import fc_model
# Define a transform to normalize the data
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))])
# Download and load the training data
trainset = datasets.FashionMNIST('./F_MNIST_data/', download=False, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
# Download and load the test data
testset = datasets.FashionMNIST('./F_MNIST_data/', download=False, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)
image, label = next(iter(trainloader))
helper.imshow(image[0,:]);
为了使这里的内容更简洁,我将模型体系结构和训练代码放到了fc_model.py的文件中。导入这个,我们可以很容易地用fc_model.network创建一个全连接的网络,并使用fc_model.train对网络进行训练。我将使用这个模型(一旦它经过训练)来演示如何保存和加载模型。
# Create the network, define the criterion and optimizer
model = fc_model.Network(784, 10, [512, 256, 128])
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
fc_model.train(model, trainloader, testloader, criterion, optimizer, epochs=2)
正如你所想象的,每次你需要使用一个网络时训练它是不切实际的。相反,我们可以保存经过训练的网络,然后在以后加载它们,以便进行更多的训练或使用它们进行预测。
PyTorch网络的参数存储在模型的状态dict中。我们可以看到状态dict包含我们每个层的权重和偏差矩阵。
print("Our model: \n\n", model, '\n')
print("The state dict keys: \n\n", model.state_dict().keys())
最简单的方法就是用torch.save保存state dict。例如,我们可以将其保存到文件“checkpoint.pth”中。
然后我们可以用torch.load加载state dict。
要将state dict加载到网络中,需要model.load_state_dict(state_dict)。
注意:只有当模型网络结构与checkpoint结构完全相同时,加载状态dict才有效。如果我用不同的网络结构创建一个模型,这将失败。
这意味着我们需要完全按照训练时的样子重建模型。有关模型结构的信息需要与状态dict一起保存在检查点中。为此,您需要构建一个字典,其中包含完全重建模型所需的所有信息。
现在,checkpoint具有重建训练模型所需的所有信息(网络结构+权重信息)。如果你想的话,你可以很容易地把它变成一个函数。类似地,我们可以编写一个函数来加载checkpoint。
到目前为止,我们一直在处理相当人工的数据集,这些数据集通常不会在实际项目中使用。取而代之的是,你可能要处理从智能手机摄像头得到的全尺寸图像。在本笔记本中,我们将了解如何加载图像并使用它们来训练神经网络。
我们将使用Kaggle提供的猫狗照片数据集。以下是几个示例图像:
我们将使用这个数据集来训练一个能够区分猫和狗的神经网络。现在看来,这并不是什么大的成就,但在五年前,这对计算机视觉系统来说是一个严峻的挑战。
加载图像数据的最简单方法是使用torchvision中的datasets.ImageFolder。一般来说,您将使用ImageFolder,如下所示:
dataset = datasets.ImageFolder('path/to/data', transform=transform)
其中'path/to/data'是数据目录的文件路径,transform是使用torchvision的transforms模块构建的预处理。ImageFolder希望文件和目录的结构如下:
其中每个类都有自己的图像目录(cat和dog)。然后用从目录名中获取的类来标记图像。所以在这里,图片123.png将加载类标签cat。您可以从这里下载已经这样构造的数据集。我还把它分为训练集和测试集。
Transforms
在用ImageFolder加载数据时,需要定义一些转换。例如,图像大小不同,但我们需要它们的大小都相同,以便进行训练。您可以使用transforms.resize()调整它们的大小,也可以使用transforms.CenterCrop()、transforms.RandomResizedCrop()等进行裁剪。我们还需要使用transforms.ToTensor()将图像转换为PyTorch张量。通常,您会使用transforms.Compose()将这些转换组合到一个管道中,后者接受转换列表并按顺序运行它们。它看起来像这样缩放,然后裁剪,然后转换成张量:
这里有很多可用的转换,我将在稍后介绍更多,您可以阅读文档。
Data Loaders
加载ImageFolder后,必须将其传递给DataLoader。DataLoader获取一个数据集(如您将从ImageFolder获得的数据集),并返回一批图像和相应的标签。您可以设置各种参数,如批处理大小和数据是否在每个epoch后重新打乱顺序。
这里dataloader是一个生成器。要从中获取数据,需要循环遍历它或将其转换为迭代器并调用next()。
练习:从Cat_Dog_data/train文件夹加载图像,定义一些转换,然后构建dataloader。
data_dir = 'Cat_Dog_data/train'
transform = transforms.Compose([transforms.Resize(255),
transforms.CenterCrop(224),
transforms.ToTensor()])
dataset = datasets.ImageFolder(data_dir, transform=transform)
dataloader = torch.utils.data.DataLoader(dataset, batch_size=32, shuffle=True)
数据增强
训练神经网络的一种常用策略是在输入数据本身引入随机性。例如,您可以在训练期间随机旋转、镜像、缩放和/或裁剪图像。这将有助于你的网络泛化,因为它看到相同的图像,但在不同的位置,不同的大小,在不同的方向,等等。
要随机旋转、缩放和裁剪图像,然后翻转图像,您可以这样定义变换:
通常还需要使用transforms.normalize对图像进行规格化。您传入一个平均值列表和标准偏差列表,然后颜色通道被标准化,如下所示:
减去平均值将数据集中在零附近,除以标准差将值压缩到-1和1之间。标准化有助于保持网络权重接近零,从而使反向传播更加稳定。如果没有标准化,网络往往无法学习。
在本笔记本中,您将学习如何使用预先训练好的网络来解决计算机视觉中具有挑战性的问题。具体来说,您将使用在torchvision提供的ImageNet上培训的网络。
ImageNet是一个巨大的数据集,有超过100万个标记图像,分为1000个类别。它是用来训练深层神经网络使用的架构称为卷积层。我不打算在这里讨论卷积网络的细节,但是如果你想了解更多,请看这个。
一旦经过训练,这些模型就可以很好地作为他们没有训练过的图像的特征检测器。对不在训练集中的图像使用预先训练的网络称为转移学习。在这里,我们将使用转移学习来训练一个网络,它可以以近乎完美的精度对猫和狗的照片进行分类。
使用torchvision.models,您可以下载这些经过预先培训的网络,并在应用程序中使用它们。
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torchvision import datasets, transforms, models
大多数预训练模型需要输入224x224个图像。此外,我们还需要匹配模型训练时使用的规范化。每个颜色通道分别标准化,平均值为[0.485,0.456,0.406],标准偏差为[0.229,0.224,0.225]。
data_dir = 'Cat_Dog_data'
# TODO: Define transforms for the training data and testing data
train_transforms = transforms.Compose([transforms.RandomRotation(30),
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])])
test_transforms = transforms.Compose([transforms.Resize(255),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])])
# Pass transforms in here, then run the next cell to see how the transforms look
train_data = datasets.ImageFolder(data_dir + '/train', transform=train_transforms)
test_data = datasets.ImageFolder(data_dir + '/test', transform=test_transforms)
trainloader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
testloader = torch.utils.data.DataLoader(test_data, batch_size=64)
我们可以加载一个模型,比如DenseNet。让我们打印出模型架构,这样我们就可以看到发生了什么。
model = models.densenet121(pretrained=True)
print(model)
Downloading: "https://download.pytorch.org/models/densenet121-a639ec97.pth" to C:\Users\yinm/.cache\torch\hub\checkpoints\densenet121-a639ec97.pth 100%|██████████| 30.8M/30.8M [00:13<00:00, 2.34MB/s]
enseLayer(
(norm1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer2): _DenseLayer(
(norm1): BatchNorm2d(288, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(288, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer3): _DenseLayer(
(norm1): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(320, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer4): _DenseLayer(
(norm1): BatchNorm2d(352, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(352, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer5): _DenseLayer(
(norm1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(384, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer6): _DenseLayer(
(norm1): BatchNorm2d(416, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(416, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer7): _DenseLayer(
(norm1): BatchNorm2d(448, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(448, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer8): _DenseLayer(
(norm1): BatchNorm2d(480, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(480, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer9): _DenseLayer(
(norm1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer10): _DenseLayer(
(norm1): BatchNorm2d(544, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(544, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer11): _DenseLayer(
(norm1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(576, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer12): _DenseLayer(
(norm1): BatchNorm2d(608, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(608, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer13): _DenseLayer(
(norm1): BatchNorm2d(640, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(640, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer14): _DenseLayer(
(norm1): BatchNorm2d(672, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(672, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer15): _DenseLayer(
(norm1): BatchNorm2d(704, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(704, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer16): _DenseLayer(
(norm1): BatchNorm2d(736, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(736, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer17): _DenseLayer(
(norm1): BatchNorm2d(768, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(768, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer18): _DenseLayer(
(norm1): BatchNorm2d(800, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(800, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer19): _DenseLayer(
(norm1): BatchNorm2d(832, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(832, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer20): _DenseLayer(
(norm1): BatchNorm2d(864, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(864, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer21): _DenseLayer(
(norm1): BatchNorm2d(896, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(896, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer22): _DenseLayer(
(norm1): BatchNorm2d(928, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(928, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer23): _DenseLayer(
(norm1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(960, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer24): _DenseLayer(
(norm1): BatchNorm2d(992, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(992, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
)
(transition3): _Transition(
(norm): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(pool): AvgPool2d(kernel_size=2, stride=2, padding=0)
)
(denseblock4): _DenseBlock(
(denselayer1): _DenseLayer(
(norm1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer2): _DenseLayer(
(norm1): BatchNorm2d(544, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(544, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer3): _DenseLayer(
(norm1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(576, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer4): _DenseLayer(
(norm1): BatchNorm2d(608, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(608, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer5): _DenseLayer(
(norm1): BatchNorm2d(640, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(640, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer6): _DenseLayer(
(norm1): BatchNorm2d(672, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(672, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer7): _DenseLayer(
(norm1): BatchNorm2d(704, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(704, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer8): _DenseLayer(
(norm1): BatchNorm2d(736, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(736, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer9): _DenseLayer(
(norm1): BatchNorm2d(768, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(768, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer10): _DenseLayer(
(norm1): BatchNorm2d(800, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(800, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer11): _DenseLayer(
(norm1): BatchNorm2d(832, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(832, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer12): _DenseLayer(
(norm1): BatchNorm2d(864, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(864, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer13): _DenseLayer(
(norm1): BatchNorm2d(896, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(896, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer14): _DenseLayer(
(norm1): BatchNorm2d(928, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(928, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer15): _DenseLayer(
(norm1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(960, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
(denselayer16): _DenseLayer(
(norm1): BatchNorm2d(992, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu1): ReLU(inplace=True)
(conv1): Conv2d(992, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(norm2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu2): ReLU(inplace=True)
(conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
)
)
(norm5): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(classifier): Linear(in_features=1024, out_features=1000, bias=True)
)
该模型主要由特征和分类器两部分组成。特征部分是一堆卷积层,总体上是一个特征检测器,可以输入分类器。分类器部分是一个全连接层(分类器): Linear(in_features=1024, out_features=1000)
。这个层是在ImageNet数据集上训练的,所以它不适用于我们的特定问题。这意味着我们需要替换分类器,但是特征提取部分可以完美的工作。一般来说,我认为预先训练好的网络是非常好的特征检测器,可以作为简单前馈分类器的输入。
# Freeze parameters so we don't backprop through them
for param in model.parameters():
param.requires_grad = False
from collections import OrderedDict
classifier = nn.Sequential(OrderedDict([
('fc1', nn.Linear(1024, 500)),
('relu', nn.ReLU()),
('fc2', nn.Linear(500, 2)),
('output', nn.LogSoftmax(dim=1))
]))
model.classifier = classifier
建立模型后,我们需要训练分类器。然而,现在我们使用的是一个非常深的神经网络。如果你试着像平常一样在CPU上训练这个,那将需要很长很长的时间。相反,我们将使用GPU来进行计算。线性代数计算在GPU上并行完成,训练速度提高了100倍。也可以在多个GPU上训练,进一步减少训练时间。
PyTorch和几乎所有其他深度学习框架一样,使用CUDA高效地计算GPU上的向前和向后传递。在PyTorch中,使用model.to('cuda')将模型参数和其他张量移动到GPU内存中。您可以将它们从带有model.to('cpu')的GPU移回,当您需要在PyTorch之外的网络输出上操作时,通常会这样做。为了演示速度的提高,我将比较使用和不使用GPU进行向前和向后传球所需的时间。
import time
for device in ['cpu', 'cuda']:
criterion = nn.NLLLoss()
# Only train the classifier parameters, feature parameters are frozen
optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)
model.to(device)
for ii, (inputs, labels) in enumerate(trainloader):
# Move input and label tensors to the GPU
inputs, labels = inputs.to(device), labels.to(device)
start = time.time()
outputs = model.forward(inputs)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if ii==3:
break
print(f"Device = {device}; Time per batch: {(time.time() - start)/3:.3f} seconds")
结果:
您可以编写与设备无关的代码,如果启用了CUDA,该代码将自动使用CUDA,如下所示:
从这里开始,我让你完成模型的训练。这个过程和以前一样,只是现在你的模型功能更强了。你应该很容易得到超过95%的准确率。
练习:训练一个预训练的模型对猫和狗的图像进行分类。继续使用DenseNet模型,或者尝试ResNet,它也是一个先试用的好模型。确保您只是在训练分类器,并且特征提取部分的参数是冻结的。
# Use GPU if it's available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = models.densenet121(pretrained=True)
# Freeze parameters so we don't backprop through them
for param in model.parameters():
param.requires_grad = False
model.classifier = nn.Sequential(nn.Linear(1024, 256),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(256, 2),
nn.LogSoftmax(dim=1))
criterion = nn.NLLLoss()
# Only train the classifier parameters, feature parameters are frozen
optimizer = optim.Adam(model.classifier.parameters(), lr=0.003)
model.to(device)
epochs = 1
steps = 0
running_loss = 0
print_every = 5
for epoch in range(epochs):
for inputs, labels in trainloader:
steps += 1
# Move input and label tensors to the default device
inputs, labels = inputs.to(device), labels.to(device)
logps = model.forward(inputs)
loss = criterion(logps, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
if steps % print_every == 0:
test_loss = 0
accuracy = 0
model.eval()
with torch.no_grad():
for inputs, labels in testloader:
inputs, labels = inputs.to(device), labels.to(device)
logps = model.forward(inputs)
batch_loss = criterion(logps, labels)
test_loss += batch_loss.item()
# Calculate accuracy
ps = torch.exp(logps)
top_p, top_class = ps.topk(1, dim=1)
equals = top_class == labels.view(*top_class.shape)
accuracy += torch.mean(equals.type(torch.FloatTensor)).item()
print(f"Epoch {epoch+1}/{epochs}.. "
f"Train loss: {running_loss/print_every:.3f}.. "
f"Test loss: {test_loss/len(testloader):.3f}.. "
f"Test accuracy: {accuracy/len(testloader):.3f}")
running_loss = 0
model.train()