内容简介:本文将介绍用于解决实际问题的深度学习架构的不同模块。前一章使用PyTorch的低级操作构建了如网络架构、损失函数和优化器这些模块。本章将介绍用于解决真实问题的神经网络的一些重要组件,以及PyTorch如何通过提供大量高级函数来抽象出复杂度。本章还将介绍用于解决真实问题的算法,如回归、二分类、多类别分类等。本文将讨论如下主题:上一章已经介绍了训练深度学习算法需要的几个步骤。
本文将介绍用于解决实际问题的深度学习架构的不同模块。前一章使用PyTorch的低级操作构建了如网络架构、损失函数和优化器这些模块。本章将介绍用于解决真实问题的神经网络的一些重要组件,以及PyTorch如何通过提供大量高级函数来抽象出复杂度。本章还将介绍用于解决真实问题的算法,如回归、二分类、多类别分类等。
本文将讨论如下主题:
- 详解神经网络的不同构成组件;
- 探究PyTorch中用于构建深度学习架构的高级功能;
- 应用深度学习解决实际的图像分类问题。
1详解神经网络的组成部分
上一章已经介绍了训练深度学习算法需要的几个步骤。
1.构建数据管道。
2.构建网络架构。
3.使用损失函数评估架构。
4.使用优化算法优化网络架构的权重。
上一章中的网络由使用PyTorch数值运算构建的简单线性模型组成。尽管使用数值运算为玩具性质的问题搭建神经架构很简单,但当需要构建解决不同领域的复杂问题时,如计算机视觉和自然语言处理,构建一个架构就迅速变得复杂起来。大多数深度学习框架,如PyTorch、TensorFlow和Apache MXNet,都提供了抽象出很多复杂度的高级功能。这些深度学习框架的高级功能称为层(layer)。它们接收输入数据,进行如同在前面一章看到的各种变换,并输出数据。解决真实问题的深度学习架构通常由1~150个层组成,有时甚至更多。抽象出低层的运算并训练深度学习算法的过程如图3.1所示。
图3.1
1.1层——神经网络的基本组成
在本章的剩余部分,我们会见到各种不同类型的层。首先,先了解其中最重要的一种层:线性层,它就是我们前面讲过的网络层结构。线性层应用了线性变换:
Y
= Wx
+ b
线性层之所以强大,是因为前一章所讲的功能都可以写成单一的代码行,如下所示。
from torch.nn import Linear myLayer= Linear(in_features=10,out_features=5,bias=True)
上述代码中的 myLayer
层,接受大小为 10
的张量作为输入,并在应用线性变换后输出一个大小为 5
的张量。下面是一个简单例子的实现:
inp = Variable(torch.randn(1,10)) myLayer = Linear(in_features=10,out_features=5,bias=True) myLayer(inp)
可以使用属性 weights
和 bias
访问层的可训练参数:
myLayer.weight Parameter containing: -0.2386 0.0828 0.2904 0.3133 0.2037 0.1858 -0.2642 0.2862 0.2874 0.1141 0.0512 -0.2286 -0.1717 0.0554 0.1766 -0.0517 0.3112 0.0980 -0.2364 -0.0442 0.0776 -0.2169 0.0183 -0.0384 0.0606 0.2890 -0.0068 0.2344 0.2711 -0.3039 0.1055 0.0224 0.2044 0.0782 0.0790 0.2744 -0.1785 -0.1681 -0.0681 0.3141 0.2715 0.2606 -0.0362 0.0113 0.1299 -0.1112 -0.1652 0.2276 0.3082 -0.2745 [torch.FloatTensor of size 5x10] myLayer.bias Parameter containing: -0.2646 -0.2232 0.2444 0.2177 0.0897 [torch.FloatTensor of size 5
线性层在不同的框架中使用的名称有所不同,有的称为dense层,有的称为全连接层(fully connected layer)。用于解决真实问题的深度学习架构通常包含不止一个层。在PyTorch中,可以用多种方式实现。
一个简单的方法是把一层的输出传入给另一层:
myLayer1 = Linear(10,5) myLayer2 = Linear(5,2) myLayer2(myLayer1(inp))
每一层都有自己的学习参数,在多个层的架构中,每层都学习出它本层一定的模式,其后的层将基于前一层学习出的模式构建。把线性层简单堆叠在一起是有问题的,因为它们不能学习到简单线性表示以外的新东西。我们通过一个简单的例子看一下,为什么把线性层堆叠在一起的做法并不合理。
假设有具有如下权重的两个线性层:
层 |
权重 |
---|---|
Layer1 |
3.0 |
Layer2 |
2.0 |
以上包含两个不同层的架构可以简单表示为带有另一不同层的单层。因此,只是堆叠多个线性层并不能帮助我们的算法学习任何新东西。有时,这可能不太容易理解,我们可以用下面的数学公式对架构进行可视化:
Y
= 2(3 X
1
) -2 Linear layers
Y
= 6( X
1
) -1 Linear layers
为解决这一问题,相较于只是专注于线性关系,我们可以使用不同的非线性函数,帮助学习不同的关系。
深度学习中有很多不同的非线性函数。PyTorch以层的形式提供了这些非线性功能,因为可以采用线性层中相同的方式使用它们。
一些流行的非线性函数如下所示:
- sigmoid
- tanh
- ReLU
- Leaky ReLU
1.2非线性激活函数
非线性激活函数是获取输入,并对其应用数学变换从而生成输出的函数。我们在实战中可能遇到数个非线性操作。下面会讲解其中几个常用的非线性激活函数。
1.sigmoid
sigmoid激活函数的数学定义很简单,如下:
简单来说,sigmoid函数以实数作为输入,并以一个0到1之间的数值作为输出。对于一个极大的负值,它返回的值接近于0,而对于一个极大的正值,它返回的值接近于1。图3.2所示为sigmoid函数不同的输出。
图3.2
sigmoid函数曾一度被不同的架构使用,但由于存在一个主要弊端,因此最近已经不太常用了。当sigmoid函数的输出值接近于0或1时,sigmoid函数前一层的梯度接近于0,由于前一层的学习参数的梯度接近于0,使得权重不能经常调整,从而产生了无效神经元。
2.tanh
非线性函数tanh将实数值输出为-1到1之间的值。当tanh的输出极值接近-1和1时,也面临梯度饱和的问题。不过,因为tanh的输出是以0为中心的,所以比sigmoid更受偏爱,如图3.3所示。
图3.3
3.ReLU
近年来ReLU变得很受欢迎,我们几乎可以在任意的现代架构中找到ReLU或其某一变体的身影。它的数学公式很简单:
f
( x
) =max
( 0,x
)
简单来说,ReLU把所有负值取作0,正值保持不变。可以对ReLU函数进行可视化,如图3.4所示。
图3.4
使用ReLU函数的一些好处和弊端如下。
- 有助于优化器更快地找到正确的权重集合。从技术上讲,它使随机梯度下降收敛得更快。
- 计算成本低,因为只是判断了阈值,并未计算任何类似于sigmoid或tangent函数计算的内容。
- ReLU有一个缺点,即当一个很大的梯度进行反向传播时,流经的神经元经常会变得无效,这些神经元称为无效神经元,可以通过谨慎选择学习率来控制。我们将在第4章中讨论调整学习率的不同方式时,了解如何选择学习率。
4.Leaky ReLU
Leaky ReLU尝试解决一个问题死角,它不再将饱和度置为0,而是设为一个非常小的数值,如0.001。对某些用例,这一激活函数提供了相较于其他激活函数更优异的性能,但它不是连续的。
1.3PyTorch中的非线性激活函数
PyTorch已为我们实现了大多数常用的非线性激活函数,我们可以像使用任何其他的层那样使用它们。让我们快速看一个在PyTorch中使用 ReLU
激活函数的例子:
sample_data = Variable(torch.Tensor([[1,2,-1,-1]])) myRelu = ReLU() myRelu(sample_data) 输出: Variable containing: 1 2 0 0 [torch.FloatTensor of size 1x4]
在上面这个例子中,输入是包含两个正值、两个负值的张量,对其调用 ReLU
函数,负值将取为 0
,正值则保持不变。
现在我们已经了解了构建神经网络架构的大部分细节,我们来构建一个可用于解决真实问题的深度学习架构。上一章中,我们使用了简单的方法,因而可以只关注深度学习算法如何工作。后面将不再使用这种方式构建架构,而是使用PyTorch中正常该用的方式构建。
1.PyTorch构建深度学习算法的方式
PyTorch中所有网络都实现为类,创建PyTorch类的子类要调用 nn.Module
,并实现 __init__
和 forward
方法。在 init
方法中初始化层,这一点已在前一节讲过。在 forward
方法中,把输入数据传给 init
方法中初始化的层,并返回最终的输出。非线性函数经常被 forward
函数直接使用, init
方法也会使用一些。下面的代码片段展示了深度学习架构是如何用PyTrorch实现的:
class MyFirstNetwork(nn.Module): def __init__(self,input_size,hidden_size,output_size): super(MyFirstNetwork,self).__init__() self.layer1 = nn.Linear(input_size,hidden_size) self.layer2 = nn.Linear(hidden_size,output_size) def __forward__(self,input): out = self.layer1(input) out = nn.ReLU(out) out = self.layer2(out) return out
如果你是 Python 新手,上述代码可能会比较难懂,但它全部要做的就是继承一个父类,并实现父类中的两个方法。在Python中,我们通过将父类的名字作为参数传入来创建子类。 init
方法相当于Python中的构造器, super
方法用于将子类的参数传给父类,我们的例子中父类就是 nn.Module
。
2.不同机器学习问题的模型架构
待解决的问题种类将基本决定我们将要使用的层,处理序列化数据问题的模型从线性层开始,一直到长短期记忆(LSTM)层。基于要解决的问题类别,最后一层是确定的。使用机器学习或深度学习算法解决的问题通常有三类,最后一层的情况通常如下。
- 对于回归问题,如预测T恤衫的销售价格,最后使用的是有一个输出的线性层,输出值为连续的。
- 将一张给定的图片归类为T恤衫或衬衫,用到的是sigmoid激活函数,因为它的输出值不是接近1就是接近0,这种问题通常称为二分类问题。
- 对于多类别分类问题,如必须把给定的图片归类为T恤、牛仔裤、衬衫或连衣裙,网络最后将使用softmax层。让我们抛开数学原理来直观理解softmax的作用。举例来说,它从前一线性层获取输入,并输出给定数量样例上的概率。在我们的例子中,将训练它预测每个图片类别的4种概率。记住,所有概率相加的总和必然为1。
3.损失函数
一旦定义好了网络架构,还剩下最重要的两步。一步是评估网络执行特定的回归或分类任务时表现的优异程度,另一步是优化权重。
优化器(梯度下降)通常接受一个标量值,因而 loss
函数应生成一个标量值,并使其在训练期间最小化。某些用例,如预测道路上障碍物的位置并判断是否为行人,将需要两个或更多损失函数。即使在这样的场景下,我们也需要把损失组合成一个优化器可以最小化的标量。最后一章将详细讨论把多个损失值组合成一个标量的真实例子。
上一章中,我们定义了自己的 loss
函数。PyTorch提供了经常使用的 loss
函数的实现。我们看看回归和分类问题的 loss
函数。
回归问题经常使用的 loss
函数是均方误差(MSE)。它和前面一章实现的 loss
函数相同。可以使用PyTorch中实现的 loss
函数,如下所示:
loss = nn.MSELoss() input = Variable(torch.randn(3, 5), requires_grad=True) target = Variable(torch.randn(3, 5)) output = loss(input, target) output.backward()
对于分类问题,我们使用交叉熵损失函数。在介绍交叉熵的数学原理之前,先了解下交叉熵损失函数做的事情。它计算用于预测概率的分类网络的损失值,损失总和应为1,就像softmax层一样。当预测概率相对正确概率发散时,交叉熵损失增加。例如,如果我们的分类算法对图3.5为猫的预测概率值为0.1,而实际上这是只熊猫,那么交叉熵损失就会更高。如果预测的结果和真实标签相近,那么交叉熵损失就会更低。
图3.5
下面是用Python代码实现这种场景的例子。
def cross_entropy(true_label, prediction): if true_label == 1: return -log(prediction) else: return -log(1 - prediction)
为了在分类问题中使用交叉熵损失,我们真的不需要担心内部发生的事情——只要记住,预测差时损失值高,预测好时损失值低。PyTorch提供了 loss
函数的实现,可以按照如下方式使用。
loss = nn.CrossEntropyLoss() input = Variable(torch.randn(3, 5), requires_grad=True) target = Variable(torch.LongTensor(3).random_(5)) output = loss(input, target) output.backward()
PyTorch包含的其他一些 loss
函数如表3.1所示。
表3.1
L1 loss |
通常作为正则化器使用;第4章将进一步讲述 |
---|---|
MSE loss |
均方误差损失,用于回归问题的损失函数 |
Cross-entropy loss |
交叉熵损失,用于二分类和多类别分类问题 |
NLL Loss |
用于分类问题,允许用户使用特定的权重处理不平衡数据集 |
NLL Loss2d |
用于像素级分类,通常和图像分割问题有关 |
4.优化网络架构
计算出网络的损失值后,需要优化权重以减少损失,并改善算法准确率。简单起见,让我们看看作为黑盒的优化器,它们接受损失函数和所有的学习参数,并微量调整来改善网络性能。PyTorch提供了深度学习中经常用到的大多数优化器。如果大家想研究这些优化器内部的动作,了解其数学原理,强烈建议浏览以下博客:
●http://colah.github.io/posts/2015-08-Backprop/
PyTorch提供的一些常用的优化器如下:
- ADADELTA
- Adagrad
- Adam
- SparseAdam
- Adamax
- ASGD
- LBFGS
- RMSProp
- Rprop
- SGD
第4章中将介绍更多算法细节,以及一些优势和折中方案考虑。让我们看看创建任意 optimizer
的一些重要步骤:
optimizer=optim.SGD(model.parameters(),lr= 0.01)
在上面的例子中,创建了 SGD
优化器,它把网络的所有学习参数作为第一个参数,另外一个参数是学习率,学习率决定了多大比例的变化调整可以作用于学习参数。第4章将深入学习率和动量(momentum)的更多细节,它们是优化器的重要参数。创建了优化器对象后,需要在循环中调用 zero_grad()
方法,以避免参数把上一次 optimizer
调用时创建的梯度累加到一起:
for input, target in dataset: optimizer.zero_grad() output = model(input) loss = loss_fn(output, target) loss.backward() optimizer.step()
再一次调用 loss
函数的 backward
方法,计算梯度值(学习参数需要改变的量),然后调用 optimizer.step()
方法,用于真正改变调整学习参数。
现在已经讲述了帮助计算机识别图像所需要的大多数组件。我们来构建一个可以区分狗和猫的复杂深度学习模型,以将学到的内容用于实践。
1.4使用深度学习进行图像分类
解决任何真实问题的重要一步是获取数据。Kaggle提供了大量不同数据科学问题的竞赛。我们将挑选一个2014年提出的问题,然后使用这个问题测试本章的深度学习算法,并在第5章中进行改进,我们将基于卷积神经网络(CNN)和一些可以使用的高级技术来改善图像识别模型的性能。大家可以从 https://www.kaggle.com/c/dogs-vs-cats/data
下载数据。数据集包含25,000张猫和狗的图片。在实现算法前,预处理数据,并对训练、验证和测试数据集进行划分是需要执行的重要步骤。数据下载完成后,可以看到对应数据文件夹包含了如图3.6所示的图片。
图3.6
当以图3.7所示的格式提供数据时,大多数框架能够更容易地读取图片并为它们设置标签的附注。也就是说每个类别应该有其所包含图片的独立文件夹。这里,所有猫的图片都应位于 cat
文件夹,所有狗的图片都应位于 dog
文件夹。
图3.7
Python可以很容易地将数据调整成需要的格式。请先快速浏览一下代码,然后,我们将讲述重要的部分。
path = '../chapter3/dogsandcats/' #读取文件夹内的所有文件 files = glob(os.path.join(path,'*/*.jpg')) print(f'Total no of images {len(files)}') no_of_images = len(files) #创建可用于创建验证数据集的混合索引 shuffle = np.random.permutation(no_of_images) #创建保存验证图片集的validation目录 os.mkdir(os.path.join(path,'valid')) #使用标签名称创建目录 for t in ['train','valid']: for folder in ['dog/','cat/']: os.mkdir(os.path.join(path,t,folder)) #将图片的一小部分子集复制到validation文件夹 for i in shuffle[:2000]: folder = files[i].split('/')[-1].split('.')[0] image = files[i].split('/')[-1] os.rename(files[i],os.path.join(path,'valid',folder,image)) #将图片的一小部分子集复制到training文件夹 for i in shuffle[2000:]: folder = files[i].split('/')[-1].split('.')[0] image = files[i].split('/')[-1] os.rename(files[i],os.path.join(path,'train',folder,image))
上述代码所做的处理,就是获取所有图片文件,并挑选出2,000张用于创建验证数据集。它把图片划分到了cats和dogs这两个类别目录中。创建独立的验证集是通用的重要实践,因为在相同的用于训练的数据集上测试算法并不合理。为了创建 validation
数据集,我们创建了一个图片数量长度范围内的数字列表,并把图像无序排列。在创建 validation
数据集时,我们可使用无序排列的数据来挑选一组图像。让我们详细解释一下每段代码。
下面的代码用于创建文件:
files=glob(os.path.join(path,'*/*.jpg'))
glob
方法返回特定路径的所有文件。当图片数量巨大时,也可以使用 iglob
,它返回一个迭代器,而不是将文件名载入到内存中。在我们的例子中,只有25,000个文件名,可以很容易加载到内存里。
可以使用下面的代码混合排列文件:
shuffle=np.random.permutation(no_of_images)
上述代码返回25,000个0~25,000范围内的无序排列的数字,可以把其作为选择图片子集的索引,用于创建 validation
数据集。
可以创建验证代码,如下所示:
os.mkdir(os.path.join(path,'valid')) for t in ['train','valid']: for folder in ['dog/','cat/']: os.mkdir(os.path.join(path,t,folder))
上述代码创建了 validation
文件夹,并在 train
和 valid
目录里创建了对应的类别文件夹(cats和dogs)。
可以用下面的代码对索引进行无序排列:
for i in shuffle[:2000]: folder = files[i].split('/')[-1].split('.')[0] image = files[i].split('/')[-1] os.rename(files[i],os.path.join(path,'valid',folder,image))
在上面的代码中,我们使用无序排列后的索引随机抽出 2000
张不同的图片作为验证集。同样地,我们把训练数据用到的图片划分到 train
目录。
现在已经得到了需要格式的数据,我们来快速看一下如何把图片加载成PyTorch张量。
1.把数据加载到PyTorch张量
PyTorch的 torchvision.datasets
包提供了一个名为 ImageFolder
的 工具 类,当数据以前面提到的格式呈现时,它可以用于加载图片以及相应的标签。通常需要进行下面的预处理步骤。
1.把所有图片转换成同等大小。大多数深度学习架构都期望图片具有相同的尺寸。
2.用数据集的均值和标准差把数据集归一化。
3.把图片数据集转换成PyTorch张量。
PyTorch在 transforms
模块中提供了很多工具函数,从而简化了这些预处理步骤。例如,进行如下3种变换:
- 调整成256 ×256大小的图片;
- 转换成PyTorch张量;
- 归一化数据(第5章将探讨如何获得均值和标准差)。
下面的代码演示了如何使用 ImageFolder
类进行变换和加载图片:
simple_transform=transforms.Compose([transforms.Scale((224,224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) train = ImageFolder('dogsandcats/train/',simple_transform) valid = ImageFolder('dogsandcats/valid/',simple_transform)
train
对象为数据集保留了所有的图片和相应的标签。它包含两个重要属性:一个给出了类别和相应数据集索引的映射;另一个给出了类别列表。
●train.class_to_idx- {'cat': 0, 'dog': 1} ●train.classes- ['cat', 'dog']
把加载到张量中的数据可视化往往是一个最佳实践。为了可视化张量,必须对张量再次变形并将值反归一化。下面的函数实现了这样的功能:
def imshow(inp): """Imshow for Tensor.""" inp = inp.numpy().transpose((1, 2, 0)) mean = np.array([0.485, 0.456, 0.406]) std = np.array([0.229, 0.224, 0.225]) inp = std * inp + mean inp = np.clip(inp, 0, 1) plt.imshow(inp)
现在,可以把张量传入前面的 imshow
函数,将张量转换成图片:
imshow(train[50][0])
上述代码生成的输出如图3.8所示。
图3.8
2.按批加载PyTorch张量
在深度学习或机器学习中把图片进行批取样是一个通用实践,因为当今的图形处理器(GPU)和CPU都为批量图片的操作进行了优化。批尺寸根据我们使用的GPU种类而不同。每个GPU都有自己的内存,可能从2GB到12GB不等,有时商业GPU内存会更大。PyTorch提供了 DataLoader
类,它输入数据集将返回批图片。它抽象出了批处理的很多复杂度,如应用变换时的多worker的使用。下面的代码把前面的 train
和 valid
数据集转换到数据加载器(data loader)中:
train_data_gen = torch.utils.data.DataLoader(train,batch_size=64,num_workers=3) valid_data_gen = torch.utils.data.DataLoader(valid,batch_size=64,num_workers=3)
DataLoader
类提供了很多选项,其中最常使用的选项如下。
shuffle num_workers
3.构建网络架构
对于大多的真实用例,特别是在计算机视觉中,我们很少构建自己的架构。可以使用已有的不同架构快速解决我们的真实问题。在我们的例子中,使用了流行的名为ResNet的深度学习算法,它在2015年赢得了不同竞赛的冠军,如与计算机视觉相关的ImageNet。为了更容易理解,我们假设算法是一些仔细连接在一起的不同的PyTorch层,并不关注算法的内部。在第5章学习卷积神经网络(CNN)时,我们将看到一些关键的ResNet算法的构造块。PyTorch通过 torchvision.models
模块提供的现成应用使得用户更容易使用这样的流行算法。因而,对于本例,我们快速看一下如何使用算法,然后再详解每行代码:
model_ft = models.resnet18(pretrained=True) num_ftrs = model_ft.fc.in_features model_ft.fc = nn.Linear(num_ftrs, 2) if is_cuda: model_ft = model_ft.cuda()
models.resnet18(pertrained = True)
对象创建了算法的实例,实例是PyTorch层的集合。我们打印出 model_ft
,快速地看一看哪些东西构成了ResNet算法。算法的一小部分看起来如图3.9所示。这里没有包含整个算法,因为这很可能会占用几页内容。
图3.9
可以看出,ResNet架构是一个层的集合,包含的层为 Conv2d
、 BatchNorm2d
和 MaxPool2d
,这些层以一种特有的方式组合在一起。所有这些算法都将接受一个名为 pretrained
的参数。当 pretrained
为 True
时,算法的权重已为特定的ImageNet分类问题微调好。ImageNet预测的类别有1000种,包括汽车、船、鱼、猫和狗等。训练该算法,使其预测1000种ImageNet类别,权重调整到某一点,让算法得到最高的准确率。我们为用例使用这些保存好并与模型共享的权重。与以随机权重开始的情况相比,算法以微调好的权重开始时会趋向于工作得更好。因而,我们的用例将从预训练好的权重开始。
ResNet算法不能直接使用,因为它是用来预测1,000种类别,而对于我们的用例,仅需预测猫和狗这两种类别。为此,我们拿到ResNet模型的最后一层—— linear
层,并把输出特征改成2,如下面的代码所示:
model_ft.fc=nn.Linear(num_ftrs, 2)
如果在基于GPU的机器上运行算法,需要在模型上调用 cuda
方法,让算法在GPU上运行。强烈建议在装备了GPU的机器上运行这些算法;有了GPU后,用很少的钱就可以扩展出一个云实例。下面代码片段的最后一行告知PyTorch在GPU上运行代码:
if is_cuda: model_ft=model_ft.cuda()
4.训练模型
前一节中,我们已经创建了 DataLoader
实例和算法。现在训练模型。为此我们需要 loss
函数和 optimizer
:
# 损失函数和优化器 learning_rate = 0.001 criterion = nn.CrossEntropyLoss() optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9) exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
在上述代码中,创建了基于 CrossEntropyLoss
的 loss
函数和基于 SGD
的优化器。 StepLR
函数帮助动态修改学习率。第4章将讨论用于调优学习率的不同策略。
下面的train_model函数获取模型输入,并通过多轮训练调优算法的权重降低损失:
def train_model(model, criterion, optimizer, scheduler, num_epochs=25): since = time.time() best_model_wts = model.state_dict() best_acc = 0.0 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) print('-' * 10) #每轮都有训练和验证阶段 for phase in ['train', 'valid']: if phase == 'train': scheduler.step() model.train(True) # 模型设为训练模式 else: model.train(False) # 模型设为评估模式 running_loss = 0.0 running_corrects = 0 #在数据上迭代 for data in dataloaders[phase]: # 获取输入 inputs, labels = data # 封装成变量 if is_cuda: inputs = Variable(inputs.cuda()) labels = Variable(labels.cuda()) else: inputs, labels = Variable(inputs), Variable(labels) #梯度参数清0 optimizer.zero_grad() #前向 outputs = model(inputs) _, preds = torch.max(outputs.data, 1) loss = criterion(outputs, labels) #只在训练阶段反向优化 if phase == 'train': loss.backward() optimizer.step() #统计 running_loss += loss.data[0] running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / dataset_sizes[phase] epoch_acc = running_corrects / dataset_sizes[phase] print('{} Loss: {:.4f} Acc: {:.4f}'.format( phase, epoch_loss, epoch_acc)) #深度复制模型 if phase == 'valid' and epoch_acc > best_acc: best_acc = epoch_acc best_model_wts = model.state_dict() print() time_elapsed = time.time() – since print('Training complete in {:.0f}m {:.0f}s'.format( time_elapsed // 60, time_elapsed % 60)) print('Best val Acc: {:4f}'.format(best_acc)) #加载最优权重 model.load_state_dict(best_model_wts) return model
上述函数的功能如下。
1.传入流经模型的图片并计算损失。
2.在训练阶段反向传播。在验证/测试阶段,不调整权重。
3.每轮训练中的损失值跨批次累加。
4.存储最优模型并打印验证准确率。
上面的模型在运行 25
轮后,验证准确率达到了87%。下面是前面的 train_model
函数在 Dogs vs. Cats
数据集上训练时生成的日志;为了节省篇幅,本书只包含了最后几轮的结果。
Epoch 18/24 ---------- train Loss: 0.0044 Acc: 0.9877 valid Loss: 0.0059 Acc: 0.8740 Epoch 19/24 ---------- train Loss: 0.0043 Acc: 0.9914 valid Loss: 0.0059 Acc: 0.8725 Epoch 20/24 ---------- train Loss: 0.0041 Acc: 0.9932 valid Loss: 0.0060 Acc: 0.8725 Epoch 21/24 ---------- train Loss: 0.0041 Acc: 0.9937 valid Loss: 0.0060 Acc: 0.8725 Epoch 22/24 ---------- train Loss: 0.0041 Acc: 0.9938 valid Loss: 0.0060 Acc: 0.8725 Epoch 23/24 ---------- train Loss: 0.0041 Acc: 0.9938 valid Loss: 0.0060 Acc: 0.8725 Epoch 24/24 ---------- train Loss: 0.0040 Acc: 0.9939 valid Loss: 0.0060 Acc: 0.8725 Training complete in 27m 8s Best val Acc: 0.874000
接下来的章节中,我们将学习可以以更快的方式训练更高准确率模型的高级技术。前面的模型在Titan X GPU上运行了30分钟的时间,后面将讲述有助于更快训练模型的不同技术。
2小结
本章通过使用SGD优化器调整层权重,讲解了PyTorch中神经网络的全生命周期——从构成不同类型的层,到加入激活函数、计算交叉熵损失,再到优化网络性能(即最小化损失)。
本章还介绍了如何应用流行的ResNet架构解决二分类和多类别分类问题。
同时,我们尝试解决了真实的图像分类问题,把猫的图片归类为cat,把狗的图片归类为dog。这些知识可以用于对不同的实体进行分类,如辨别鱼的种类,识别狗的品种,划分植物种子,将子宫癌归类成Type1、Type2和Type3型等。
本文摘自 《PyTorch深度学习》
作者:[印度]毗湿奴•布拉马尼亚(Vishnu Subramanian)
译者:王海玲, 刘江峰
本书在不深入数学细节的条件下,给出了多个先进深度学习架构的直观解释,如ResNet、DenseNet、Inception和Seq2Seq等,也讲解了如何进行迁移学习,如何使用预计算特征加速迁移学习,以及如何使用词向量、预训练的词向量、LSTM和一维卷积进行文本分类。
阅读完本书后,读者将会成为一个熟练的深度学习人才,能够利用学习到的不同技术解决业务问题。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 一文看懂神经网络初始化!吴恩达Deeplearning.ai最新干货
- 干货 | 地平线:面向低功耗 AI 芯片上视觉任务的神经网络设计 | 职播间第 2 期
- 神经网络 – 序列预测LSTM神经网络落后
- 神经网络历史以及浅析神经网络与感知机
- 【神经网络】11行Python代码实现的神经网络
- 常见的五种神经网络(三):循环神经网络(上篇)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
人人都是产品经理——写给产品新人
苏杰 / 电子工业出版社 / 2017-6 / 66.60
《人人都是产品经理——写给产品新人》为经典畅销书《人人都是产品经理》的内容升级版本,和《人人都是产品经理2.0——写给泛产品经理》相当于上下册的关系。对于大量成长起来的优秀互联网产品经理、众多想投身产品工作的其他岗位从业者,以及更多有志从事这一职业的学生而言,这《人人都是产品经理——写给产品新人》曾是他们记忆深刻的启蒙读物、思想基石和行动手册。作者以分享经历与体会为出发点,以“朋友间聊聊如何做产品......一起来看看 《人人都是产品经理——写给产品新人》 这本书的介绍吧!
图片转BASE64编码
在线图片转Base64编码工具
HTML 编码/解码
HTML 编码/解码