第六章 PyTorch基础

简介: PyTorch是美国互联网巨头Facebook在深度学习框架Torch的基础上使用Python重写的一个全新的深度学习框架,它更像NumPy的替代物,不仅继承了NumPy的众多优点,还支持GPUs计算,在计算效率上要比NumPy有更明显的优势;不仅如此,,PyTorch还有许多高级功能,比如拥有丰富的API可以快速完成深度神经网络模型的搭建和训练
1.Python中的Tensor
PyTorch的安装:
【1】新建环境,避免与基础环境出现冲突
conda create -n pytorch_gpu python=3.7
【2】切换到pytorch环境
conda activate pytorch_gpu
【3】安装基本框架
conda install pandas
conda install jupyter notebook

【4】安装pytorch
进入官网,下拉到PyTorch Build,选择Windows,复制选框下的一行代码,粘贴运行后,漫长的等待。
cuda版本查看:
windows:
桌面右键–>NVDIA控制面板–>帮助–>系统信息
组件–>NVCUDA.DLL查看CUDA版本
【5】验证是否安装成功
在命令行左边为 pytorch 环境中,输入 python
之后,输入 import torch,如果没有报错,意味着 PyTorch 已经顺利安装了
接下来,输入 torch.cuda.is_available,如果是 True,意味着你可以使用 GPU,如果是 False,意味着只能使用CPU。
下面展示一些 内联代码片

(pytorch_gpu) C:\WINDOWS\system32>python
Python 3.7.7 (default, May  6 2020, 11:45:54) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.cuda.is_available()
False
>>>   

这里显示的是False,证明只能使用CPU。
首先,我们需要学会使用PyTorch中的Tensor。Tensor在PyTorch中负责存储基本数据,PyTorch针对Tensor也提供了丰富的函数和方法,所以PyTorch中的Tensor与NumPy的数组具有极高的相似性。Tensor是一种高级的API,我们在使用Tensor时并不用了解PyTorch中的高层次架构,也不用明白什么是深度学习、什么是后向传播、如何对模型进行优化、什么是计算图等技术细节。更重要的是,在PyTorch中定义的Tensor数据类型的变量还可以在GPUs上进行运算,而且只需要对变量做一些简单的类型转换就能够轻易实现。
(1)Tensor的数据类型
在使用Tensor时,我们首先要掌握如何使用Tensor来定义不同数据类型的变量。和NumPy差不多,PyTorch中的Tensor也有自己的数据类型定义方式,常用的如下:
【1】torch.FloatTensor:用于生成数据类型为浮点型的Tensor,传递给torch.FloatTensor的参数可以是一个列表,也可以是一个纬度值。
输入以下代码出现问题:
ModuleNotFoundError: No module named ‘torch’
解决办法:
打开 Anaconda Navigator;
切换到安装 PyTorch 虚拟环境 ;
安装 Jupyter Notebook;
重启 Joupyter Notebook,即可;
import torch
a=torch.FloatTensor(2,3)
b=torch.FloatTensor([2,3,4,5])

print(a)
print(b)

运行结果:
tensor([[1.4603e-19, 1.1578e+27, 1.8143e+25],
[7.0060e+22, 7.8675e+34, 1.6907e-01]])
tensor([2., 3., 4., 5.])
可以看到,打印输出的两组变量数据类型都显示为浮点型,不同的是,前面的一组是按照我们指定的维度随机生成的浮点型Tensor,而另外一组是按照我们给定的列表生成的浮点型Tensor。
【2】tor.IntTensor:用于生成数据类型为整型的Tensor,传递给torch.IntTensor的参数可以是一个列表,也可以是一个维度值。
import torch
a=torch.IntTensor(2,3)
b=torch.IntTensor([2,3,4,5])

print(a)
print(b)
运行结果:
tensor([[0, 0, 0],
[0, 0, 0]], dtype=torch.int32)
tensor([2, 3, 4, 5], dtype=torch.int32)
可以看到,以上生成的两组Tensor最后显示的数据类型都为整型。
【3】torch.rand:用于生成数据类型为浮点型且维度指定的随机Tensor,和在NumPy中使用numpy.rand生成随机数的方法类似,随机生成的浮点数据在0~1区间均匀分布
import torch

a=torch.rand(2,3)

print(a)
运行结果:
tensor([[0.8702, 0.5914, 0.2079],
[0.6388, 0.3002, 0.4641]])
【4】torch.randn:用于生成数据类型为浮点型且维度指定的随机Tensor,和在NumPy中使用numpy.randn生成随机数的方法类似,随机生成的浮点数的取值满足均值为0,方差为1的正态分布。
import torch

	a=torch.randn(2,3)

	print(a)
	运行结果:
	tensor([[ 0.3021, -2.4493,  1.3850],
    [ 0.8588,  1.9458, -0.5570]])

【5】torch.range:用于生成数据类型为浮点型且自定义起始范围和结束范围的Tensor,所以传递给torch.range的参数有三个,分别是范围的起始值、范围的结束值和步长,其中,步长用于指定从起止值到结束值的每步的数据间隔。
import torch
a=torch.range(1,20,1)
print(a)
运行结果:
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20.]
【6】torch.zeros:用于生成数据类型为浮点型且维度指定的Tensor,不过这个浮点型的Tensor中的元素值全部为0。
import torch
a=torch.zeros(2,3)
print(a)
运行结果:
tensor([[0., 0., 0.],
[0., 0., 0.]])
(2)Tensor的运算
通过对Tensor数据类型的变量进行运算,来组合一些简单或者复杂的算法,常用的Tensor运算如下:
【1】torch.abs:将参数传递到torch.abs后返回输入参数的绝对值作为输出,输入参数必须是一个Tensor数据类型的变量。
import torch
a=torch.randn(2,3)
print(a)

b=torch.abs(a)
print(b)
运算结果:
tensor([[-0.4043, 0.0277, -0.8388],
[-0.1013, 2.4982, -0.5582]])
tensor([[0.4043, 0.0277, 0.8388],
[0.1013, 2.4982, 0.5582]])
【2】torch.add:将参数传递到torch.add后返回输入参数的求和结果作为输出,输入参数既可以全部是Tensor数据类型的变量,也可以一个是Tensor数据类型的变量,另一个是标量:
import torch
a=torch.randn(2,3)
print(a)

b=torch.randn(2,3)
print(b)

c=torch.add(a,b)
print©

d=torch.randn(2,3)
print(d)

e=torch.add(d,10)
print(e)
运行结果:
tensor([[-0.8077, -2.5341, 0.2572],
[ 1.1124, 1.9801, -1.6262]])
tensor([[ 0.9383, 1.5825, -1.6489],
[-0.0783, -0.4793, -0.4769]])
tensor([[ 0.1306, -0.9516, -1.3918],
[ 1.0341, 1.5008, -2.1030]])
tensor([[ 1.2670, -0.9547, -0.6811],
[ 1.3372, 0.7549, -1.0042]])
tensor([[11.2670, 9.0453, 9.3189],
[11.3372, 10.7549, 8.9958]])
无论是调用torch.add对两个Tensor数据类型的变量进行计算,还是完成Tensor数据类型的变量和标量的计算,计算方式都和NumPy中数组的加法运算如出一辙。
【3】torch.clamp:对输入参数按照自定义的范围进行裁剪,最后将参数裁剪的结果作为输出。所以输入参数一共有三个,分别是需要进行裁剪的Tensor数据类型的变量、裁剪的上边界和下边界,**具体裁剪过程是:**使用变量中的每个元素分别和裁剪的上边界和下边界的值进行比较,如果元素的值小于裁剪的下边界的值,该元素就被重写成下边界的值;同理,如果元素的值大于裁剪的上边界的值,该元素就被重写成裁剪的上边界的值。
import torch
a=torch.randn(2,3)
print(a)

b=torch.clamp(a,-0.1,0.1)
print(b)
运行结果:
tensor([[-0.2927, -1.9389, 0.9459],
[ 1.3697, -0.1858, -0.3662]])
tensor([[-0.1000, -0.1000, 0.1000],
[ 0.1000, -0.1000, -0.1000]])
【4】torch.div:将参数传递到torch.div后返回输入参数的求商结果作为输出,同样,参与运算的参数可以全部是Tensor数据类型的变量,也可以是Tensor数据类型的变量和标量的组合:
import torch
a=torch.randn(2,3)
print(a)

b=torch.randn(2,3)
print(b)

c=torch.div(a,b)
print©

d=torch.randn(2,3)
print(d)

e=torch.div(d,10)
print(e)
运行结果:
tensor([[-0.4400, -0.0776, -0.3033],
[-0.0907, 1.6035, -1.2887]])
tensor([[ 1.0065, -1.2483, -1.2972],
[-1.0694, 0.0830, -1.7357]])
tensor([[-0.4372, 0.0621, 0.2338],
[ 0.0848, 19.3297, 0.7425]])
tensor([[-0.1236, 1.3008, -0.2204],
[ 1.7619, -0.6815, 0.2107]])
tensor([[-0.0124, 0.1301, -0.0220],
[ 0.1762, -0.0682, 0.0211]])
【5】torch.mul:将参数传递到torch.mul后返回输入参数求积的结果作为输出,参与运算的参数可以全部是Tensor数据类型的变量,也可以是Tensor数据类型的变量和标量的组合:
import torch
a=torch.randn(2,3)
print(a)

b=torch.randn(2,3)
print(b)

c=torch.mul(a,b)
print©

d=torch.randn(2,3)
print(d)

e=torch.mul(d,10)
print(e)
运行结果:
tensor([[ 0.2900, 0.0243, 0.6281],
[-0.5610, 1.1297, -0.3510]])
tensor([[-0.1385, 1.6199, 1.5262],
[ 0.2773, 1.4120, -0.8315]])
tensor([[-0.0402, 0.0393, 0.9586],
[-0.1555, 1.5951, 0.2918]])
tensor([[ 0.4824, -0.2389, 0.2833],
[ 0.1063, 1.2907, 0.2990]])
tensor([[ 4.8244, -2.3892, 2.8330],
[ 1.0625, 12.9071, 2.9897]])
【6】torch.pow:将参数传递到torch.pow后返回输入参数的求幂结果作为输出,参与运算的参数可以全部是Tensor数据类型的变量,也可以是Tensor数据类型的变量和标量的组合:
import torch
a=torch.randn(2,3)
print(a)

b=torch.pow(a,2)
print(b)
运行结果:
tensor([[-1.2830, -1.6715, -1.0019],
[-1.2250, 0.5539, 0.3647]])
tensor([[1.6462, 2.7938, 1.0038],
[1.5006, 0.3068, 0.1330]])
【7】torch.mm:将参数传递到torch.mm后返回输入参数的求积结果作为输出,不过这个求积的方式和之前的torch.mul运算方式不太一样,torch.mm运用矩阵之间的乘法规则进行计算,所以被传入的参数会被当做矩阵进行处理,参数的维度自然也要满足矩阵乘法的前提条件,即前一个矩阵的行数必须和后一个矩阵的列数相等,否则不能进行计算。
import torch
a=torch.randn(2,3)
print(a)

b=torch.randn(3,2)
print(b)

c=torch.mm(a,b)
print©
运算结果:
tensor([[-0.2065, 0.4530, -0.5609],
[-0.8725, -0.0789, -1.2774]])
tensor([[ 1.3619, 2.5437],
[-0.2422, 0.7099],
[-0.2900, -1.7730]])
tensor([[-0.2283, 0.7908],
[-0.7987, -0.0107]])
【8】torch.mv:将参数传递到torch.mv后返回输入参数的求积结果作为输出,torch.mv运用矩阵与向量之间的乘法规则进行计算,被传入的参数中的第1个参数代表矩阵,第2个参数代表向量,顺序不能颠倒:
import torch
a=torch.randn(2,3)
print(a)

b=torch.randn(3)
print(b)

c=torch.mv(a,b)
print©
运行结果:
tensor([[-0.2727, 0.5979, 0.6235],
[-0.4714, -0.6065, 1.0088]])
tensor([ 0.8592, -1.6003, -0.1062])
tensor([-1.2573, 0.4585])
(3)搭建一个简易神经网络
【1】相关包的导入:
import torch
batch_n=100
hidden_layer=100
input_data=1000
output_data=10
我们先通过import torch导入必要的包,然后定义4个整型变量,其中:batch_n是在一个批次中输入数据的数量,值是100,这意味着我们在一个批次中输入100个数据,同时,每个数据包含的数据特征又input_data个,因为input_data的值是1000,所以每个数据的数据特征就是1000个;hidden_layer用于定义经过隐藏层后保留的数据特征的个数,这里有100个,因为我们的模型只考虑一层隐藏层,所以在代码中仅定义了一个隐藏层的参数;output_data是输出的数据,值是10,我们可以将输出的数据看作一个分类结果值的数量,个数10表示我们最后要得到10个分类结果值。
一个批次的数据从输入到输出的完整过程是:先输入100个具有1000个特征的数据,经过隐藏层后变成100个具有100个特征的数据,再经过输出层后输出100个具有10个分类结果值的数据,在得到输出结果之后计算损失并进行后向传播,这样一次模型的训练就完成了,然后循环这个流程就可以完成指定次数的训练,并达到优化模型参数的目的。
下面演示如何完成从输入层到隐藏层、从隐藏层到输出层的权重初始化定义工作
x=torch.randn(batch_n,input_data)
y=torch.randn(batch_n,output_data)

w1=torch.randn(input_data,hidden_layer)
w2=torch.randn(hidden_layer,output_data)
在以上代码中定义的从输入层到隐藏层、从隐藏层到输出层对应的权重参数,同在之前说到的过程中使用的参数维度是一致的,由于我们现在并没有好的权重参数的初始化方法,所以选择通过torch.randn来生成指定维度的随机参数作为其初始化参数,尽管这并不是一个好主意。
可以看到,在代码中定义的输入层维度为(100,1000),输出层维度为(100,10),同时,从输入层到隐藏层的权重参数维度为(1000,100),从隐藏层到输出层的权重参数维度为(100,10)。
权重参数的维度是怎么定义的?
把整个过程看作矩阵连续的乘法运算即可。

因为在代码中我们的真实值y也是通过随机的方式生成的,所以一开始在使用损失函数计算损失值时得到的结果会比较大。
在定义好输入、输出和权重参数之后,就可以开始训练模型和优化权重参数了
在此之前,还需要明确训练的总次数和学习速率:
epoch_n=20
learning_rate=1e-6
因为接下来会使用梯度下降的方法来优化神经网络的参数所以必须定义后向传播的次数和梯度下降使用的学习速率。在以上代码中使用了epoch_n定义训练的次数,epoch_n的值为20,所以我们需要通过循环的方式让程序进行20次训练,来完成对初始化权重参数的优化和调整。在优化过程中使用的学习速率learning_rate的值为1e-6,表示1乘以10的-6次方,即0.000001。
接下来对模型进行正式训练并对参数进行优化:
for epoch in range(epoch_n):
(前向传播过程中,通过两个连续的矩阵乘法计算出预测结果,在计算的过程中还对矩阵乘积的结果使用clamp方法进行裁剪,将小于零的值全部重新赋值为0,这就像加上了一个ReLU激活函数的功能)
h1=x.mm(w1) #1001000
h1=h1.clamp(min=0)
y_pred=h1.mm(w2) #100
10
(前向传播得到的预测结果通过y_pred来表示,在得到了预测值后就可以使用预测值和真实值来计算误差值了。)
(我们用loss来表示误差值,对误差值的计算使用了均方误差函数。)
loss=(y_pred-y).pow(2).sum()
print(“Epoch:{},Loss:{:.4f}”.format(epoch,loss))
(之后的代码就是通过后向传播来对权重参数进行优化,为了计算方便,我们的代码实现使用的是每个节点的链式求导结果,在通过计算之后,就能够得到每个权重参数对应的梯度分别是grad_w1和grad_w2。)
grad_y_pred=2
(y_pred-y)
grad_w2=h1.t().mm(grad_y_pred)
|
grad_h=grad_y_pred.clone()
grad_h=grad_h.mm(w2.t())
grad_h.clamp_(min=0)
grad_w1=x.t().mm(grad_h)
(在得到参数的梯度值之后,按照之前定义好的学习速率对w1和w2的权重参数进行更新)
w1-=learning_rategrad_w1
w2-=learning_rate
grad_w2
以上代码通过最外层的一个大循环来保证我们的模型可以进行20次训练,循环内的是神经网络模型具体的前向传播和后向传播代码,参数的优化和更新使用梯度下降来完成。
在代码中每次训练时,我们都会对loss的值进行打印输出,以方便看到整个优化过程的效果,所以最后会有20个loss值被打印显示。
运行结果如下:
Epoch:0,Loss:42686332.0000
Epoch:1,Loss:99243136.0000
Epoch:2,Loss:401049248.0000
Epoch:3,Loss:845738944.0000
Epoch:4,Loss:80541144.0000
Epoch:5,Loss:25088058.0000
Epoch:6,Loss:13474749.0000
Epoch:7,Loss:8424040.0000
Epoch:8,Loss:5715401.0000
Epoch:9,Loss:4099028.5000
Epoch:10,Loss:3070559.7500
Epoch:11,Loss:2387241.5000
Epoch:12,Loss:1918319.1250
Epoch:13,Loss:1587843.0000
Epoch:14,Loss:1349358.3750
Epoch:15,Loss:1173396.2500
Epoch:16,Loss:1040568.0000
Epoch:17,Loss:937937.7500
Epoch:18,Loss:856755.2500
Epoch:19,Loss:791111.0625
可以看,loss值从之前的巨大误差逐渐缩减,这说明我们的模型经过20次训练和权重参数优化之后,得到的预测的值和真实值之间的差距越来越小了。
2.自动梯度
我们在实践中搭建的网络模型都是层次更深的神经网络模型,即深度神经网络模型,结构会有所变化,代码也会更复杂。对于深度的神经网络模型的前向传播使用简单的代码就能实现,但是
很难实现涉及该模型中后向传播梯度计算部分的代码
*,其中最困难的就是对模型计算逻辑的梳理。
在PyTorch中,通过使用torch.autograd包,可以使模型参数自动计算在优化过程中需要用到的梯度值,在很大程度上帮助降低了实现后向传播代码的复杂度。
(1)torch.autograd和Variable
torch.autograd包的主要功能是完成神经网络后向传播中的链式求导,​手动实现链式求导的代码会给我们带来很大的困扰,而torch.autograd包中丰富的类减少了这些不必要的麻烦。
实现自动梯度功能的过程大致为:先通过输入Tensor数据类型的变量在神经网络的前向传播过程中生成一张计算图,然后根据这个计算图和输出结果准确计算出每个参数需要更新的梯度,并通过完成后向传播完成对参数的梯度更新。
在实践中完成自动梯度需要用到torch.autograd包中的Variable类对我们定义的Tensor数据类型变量进行封装,在封装后,计算图中的各个节点就是一个Variable对象,这样才能应用自动梯度的功能。
如果已经按照上面的方式完成了相关操作,则选中了计算图的某个节点时,这个节点必定会是一个Variable对象,用X来代表我们选中的节点,那么X.data代表Tensor数据类型的变量,X.grad也是一个Variable对象,不过它表示的是X的梯度,在想访问梯度值时需要使用X.grad.data。
实例:
import torch
from torch.autograd import Variable
batch_n=100
hidden_layer=100
input_data=1000
output_data=10
同样,一开始导入必要的包和类,但是在代码中增加了“from torch.autograd import Variable”。定义的4个变量和之前的代码是一样的,其中batch_n是一个批次输入的数据量,input_data是输入数据的特征个数,hidden_layer是通过隐藏层后输出的特征数,output_data是最后输出的分类结果数。
x=Variable(torch.randn(batch_n,input_data),requires_grad=False)
y=Variable(torch.randn(batch_n,output_data),requires_grad=False)

w1=Variable(torch.randn(input_data,hidden_layer),requires_grad=True)
w1=Variable(torch.randn(hidden_layer,output_data),requires_grad=True)

“Variable(torch.randn(batch_n,input_data),requires_grad=False)"这段代码就是之前讲到的用Variable类对Tensor数据类型进行封装操作。**在以上代码中还使用了一个requires_grad参数,这个参数的赋值类型是布尔型,如果requires_grad的值是False,那么表示该变量在进行自动梯度计算的过程中不会保留梯度值。**我们将输入数据x和输出数据y的requires_grad参数均设置为False,这是因为这两个变量并不是我们的模型需要优化的参数,而两个权重w1和w2的requires_grad参数值为True。
之后的代码用于定义模型的训练次数和学习速率:
epoch_n=20
learning_rate=1e-6
新的模型和参数优化的代码如下:
for epoch in range(epoch_n):
y_pred=x.mm(w1).clamp(min=0).mm(w2)
loss=(y.pred-y).pow(2).sum()
print(“Epoch:{},Loss:{:.4f}”.format(epoch,loss.data[0]))

loss.backward()

w1.data-=learning_rate*w1.grad.data
w2.data-=learning_rate*w2.grad.data

w1.grad.data.zero_()
w2.grad.data.zero_()

和之前的代码相比,当前的代码更简洁了,之前的代码中的后向传播计算部分变成了新代码中的 loss.backward(),这个函数的功能在于让模型根据计算他自动计算每个节点的梯度值并根据需求进行保留,有了这一步,我们的权重参数w1.data和w2.data就可以直接使用在自动梯度过程中求得的梯度值w1.data.grad和w2.data.grad,并结合学习率来对现有的参数进行更新优化了。
在代码的最后还要将本次计算得到的各个参数节点的梯度值通过grad.data.zero_()全部置为0,如果不置为0,则计算的梯度会被一直累加,这样就会影响到后续的计算。
(2)自定义传播函数
其实除了可以采用自动梯度方法,我们还可以通过构建一个继承了torch.nn.Module的新类,来完成对前向传播函数和后向传播函数的重写。在这个新类中,我们使用forward作为前向传播函数的关键字,使用backward作为后向传播函数的关键字。
使用自定义传播函数的方法,来调整之前具备自动梯度功能的简易神经网络模型:
和之前的代码一样,在代码的开始部分同样是导入必要的包、类,并定义需要用到的4个变量:
import torch
from torch.autograd import Variable
batch_n=64
hidden_layer=100
input_data=1000
output_data=10
下面看看新的代码部分是如何定义我们的前向传播forward函数和后向传播backward函数:
class Model(torch.nn.Module):
def init(self):
super(Model,self).init()
def forward(self,input,w1,w2):
x=torch.mm(input,w1)
x=torch.clamp(x,min=0)
x=torch.mm(x,w2)
return x
def backward(self):
pass
以上代码展示了一个比较常用的Python类的构造方式:首先通过class Model(torch.nn.Module)完成了类继承的操作,之后分别是类的初始化,以及forward函数和backward函数。forward函数实现了模型的前向传播中的矩阵运算,backward实现了模型的后向传播中的自动梯度计算,后向传播如果没有特别的需求,则在一般情况下不用进行调整。
对定义好的类进行调用:
model=Model()
这一系列操作相当于完成了对简易神经网络的搭建,然后就只剩下对模型进行训练和对参数进行优化的部分了:
x=Variable(torch.randn(batch_n,input_data),requires_grad=False)
y=Variable(torch.randn(batch_n,output_data),requires_grad=False)

w1=Variable(torch.randn(input_data,hidden_layer),requires_grad=True)
w2=Variable(torch.randn(hidden_layer,output_data),requires_grad=True)

epoch_n=20
learning_rate=1e-6

for epoch in range(epoch_n):
y_pred=model(x,w1,w2)

loss=(y_pred-y).pow(2).sum()
print("Epoch:{},Loss:{:.4f}".format(epoch,loss.item()))
#print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data[0]))
loss.backward()

w1.data-=learning_rate*w1.grad.data
w2.data-=learning_rate*w2.grad.data

w1.grad.data.zero_()
w2.grad.data.zero_()
运行结果:
Epoch:0,Loss:26784102.0000

Epoch:1,Loss:22426678.0000
Epoch:2,Loss:23338560.0000
Epoch:3,Loss:25894938.0000
Epoch:4,Loss:27295346.0000
Epoch:5,Loss:24706262.0000
Epoch:6,Loss:18663448.0000
Epoch:7,Loss:11619079.0000
Epoch:8,Loss:6444245.0000
Epoch:9,Loss:3429344.5000
Epoch:10,Loss:1925450.7500
Epoch:11,Loss:1193680.3750
Epoch:12,Loss:828125.6250
Epoch:13,Loss:628542.7500
Epoch:14,Loss:506840.7500
Epoch:15,Loss:423903.5625
Epoch:16,Loss:362220.9062
Epoch:17,Loss:313701.1562
Epoch:18,Loss:274066.1250
Epoch:19,Loss:240934.0312
这里,变量的赋值、训练次数和学习速率的定义,以及模型训练和参数优化使用的代码,和之前的代码没有太大的差异,不同的是,我们的模型通过"y_pred=model(x,w1,w2)"来完成对模型预测值的输出,并且整个训练部分的代码被简化了。
遇见的问题:
print(“Epoch:{},Loss:{:.4f}”.format(epoch,loss.data[0]))
报错:
IndexError: invalid index of a 0-dim tensor. Use tensor.item() in Python or tensor.item<T>() in C++ to convert a 0-dim tensor to a number
解决办法:
将这句代码改为:
print(“Epoch:{},Loss:{:.4f}”.format(epoch,loss.item()))
3.模型搭建和参数优化
如何基于PyTorch深度学习框架用简单快捷的方式搭建出复杂的神经网络模型,同时让模型参数的优化方法趋于高效:
如同使用PyTorch中的自定义梯度方法一样,在搭建复杂的神经网络模型的时候,我们也可以使用PyTorch中已定义的类和方法,这些类和方法覆盖了神经网络中的线性变换、激活函数、卷积层、全连接层、池化层等常用神经网络结构的实现。在完成模型的搭建之后,我们还可以使用PyTorch提供的类型丰富的优化函数来完成对模型参数的优化,除此之外,还有很多防止模型训练过程发生过拟合的类。
(1)PyTorch之torch.nn
PyTorch中的torch.nn包提供了很多与实现神经网络中的具体功能相关的类,这些类涵盖了深度神经网络模型在搭建和参数优化过程中的常用内容,比如神经网络中的卷积层、池化层、全连接层这类层次构造的方法、防止过拟合的参数归一化方法,Dropout方法,还有激活函数部分的线性激活函数、非线性激活函数相关的方法,等等。在学会使用PyTorch的torch.nn进行神经网络模型的搭建和参数优化后,我们就会发现实现一个神经网络应用并没有我们想象中的那么难。
下面使用PyTorch的torch.nn包来简化我们之前的代码,开始部分代码变化不大:
import torch
from torch.autograd import Variable
batch_n=64
hidden_layer=100
input_data=1000
output_data=10

x=Variable(torch.randn(batch_n,input_data),requires_grad=False)
y=Variable(torch.randn(batch_n,output_data),requires_grad=False)
首先导入必要的包,类并定义了4个变量,不过这里仅定义了输入和输出的变量,之前定义神经网络模型中的权重参数的代码被删减了,这和我们之后在代码中使用的torch.nn包中的类有关,因为这个类能够帮助我们自动生成和初始化对应维度的权重参数。
模型搭建的代码如下:
models=torch.nn.Sequential(
torch.nn.Linear(input_data,hidden_layer),
torch.nn.ReLU(),
torch.nn.Linear(hidden_layer,output_data)
)
torch.nn.Sequential括号内的内容就是我们搭建的神经网络模型的具体结构,这里首先通过torch.nn.Linear(input_data,hidden_layer)完成从输入层到隐藏层的线性变化,然后通过激活函数及torch.nn.Linear(hidden_layer,output_data)完成从隐藏层到输出层的线性变换。
【1】torch.nn.Sequential**:torch.nn.Sequential类是torch.nn中的一种序列容器**,通过在容器中嵌套各种实现神经网络中具体功能相关的类,来完成对神经网络模型的搭建,最主要的是,参数会按照我们定义好的序列自动传递下去。我们可以将嵌套在容器中的各个部分看作各种不同的模块,这些模块可以自由组合。模块的加入一般有两种方式,一种是以上代码中使用的直接嵌套,另一种是以orderdict有序字典的方式进行传入,这两种方式的唯一区别是,使用后者搭建的模型的每个模块都有我们自定义的名字,而前者默认使用从0开始的数字序列作为每个模块的名字。

​【2】torch.nn.Linear:torch.nn.Linear类用于定义模型的线性层,即完成前面提到的不同的层之间的线性变换。torch.nn.Linear类接收的参数有三个,分别是输入特征数、输出特征数和是否使用偏置,设置是否使用偏置的参数是一个布尔值,默认为True,即使用偏置。在使用过程中,我们只需要将输入的特征数和输出的特征数传递给torch.nn.Linear类,就会自动生成对应维度的权重参数和偏置,对于生成的权重参数和偏置,我们模型默认使用了一种比之前的简单随机方式更好的参数初始化方法。
根据我们搭建模型的输入、输出和层次结构需求,它的输入是在一个批次中包含100个特征数为1000的数据,最后得到100个特征数为10的输出数据,中间需要经过两次线性变换,所以要使用两个线性层,两个线性层的代码分别是 torch.nn.Linear(input_data,hidden_layer)
torch.nn.Linear(hidden_layer,output_data)
可看到,其代替了之前使用矩阵乘法方式的实现,代码更精炼、简洁。
【3】 torch.nn.ReLU():torch.nn.ReLU()类属于非线性激活分类,在定义时默认不需要传入参数。
当然,在torch.nn包中还有许多非线性激活函数类可供选择,比如之前讲到的:PReLU、LeakyReLU、Tanh、Sigmoid、Softmax等。
在掌握torch.nn.Sequential、 torch.nn.Linear、 torch.nn.ReLU的使用方法后,快速搭建更复杂的多层神经网络模型变为可能,而且在整个模型的搭建过程中不需要对在模型中使用到的权重参数和偏置进行任何定义和初始化说明,因为参数已经完成了自动生成。
接下来对已经搭建好的模型进行训练并对参数进行优化:
epoch_n=10000
learning_rate=1e-4
loss_fn=torch.nn.MSELoss()
前两句代码和之前的代码没有多大区别,知识单纯地增加了学习速率和训练次数,学习速率现在是0.0001,训练次数增加到了10000次,这样做是为了让最终得到的结果更好。不过计算损失函数的代码发生了变化,这里使用的是在torch.nn包中已经定义好的均方误差函数类torch.nn.MSELoss来计算损失值。
torch.nn包中常用的损失函数的具体方法:
【1】torch.nn.MSELoss:torch.nn.MSELoss类使用均方误差函数对损失值进行计算,在定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数方可进行计算:
import torch
from torch.autograd import Variable
loss_f=torch.nn.MSELoss()
x=Variable(torch.randn(100,100))#输入两个维度一样的参数
y=Variable(torch.randn(100,100))
loss=loss_f(x,y)
print(loss.data)
以上代码首先通过随机方式生成了两个维度都是(100,100)的参数,然后使用均方误差函数来计算两组参数的损失值。
运行结果:
tensor(2.0140)

【2】torch.nn.L1Loss:torch.nn.L1Loss类使用平均绝对误差函数对损失值进行计算,同样,在定义类的对象时不用传入任何参数,但在使用实例时需要输入两个维度一样的参数进行计算:
import torch
from torch.autograd import Variable
loss_f=torch.nn.L1Loss()
x=Variable(torch.randn(100,100))
y=Variable(torch.randn(100,100))
loss=loss_f(x,y)
print(loss.data)
以上代码也是通过随机方式生成了两个维度都是(100,100)的参数,然后使用平均绝对值误差函数来计算两组参数的损失值,运行结果:
tensor(1.1258)
【3】torch.nn.CrossEntropyLoss:torch.nn.CrossEntropyLoss类用于计算交叉熵,在定义类的对象时不用传入任何参数,在使用实例时需要输入两个满足交叉熵的计算条件的参数:
import torch
from torch.autograd import Variable
loss_f=torch.nn.CrossEntropyLoss()
x=Variable(torch.randn(3,5))
y=Variable(torch.LongTensor(3).random_(5))
loss=loss_f(x,y)
print(loss.data)
这里生成的第一组参数是一个随机参数,维度为(3,5);第2组参数是3个范围为0~4的随机数字。计算这两组参数的损失值,运行结果:
tensor(2.7991)
在学会使用PyTorch中的优化函数之后,我们就可以对自己建立的神经网络模型进行训练并对参数进行优化了:
for epoch in range(epoch_n)
y_pred=models(x)
loss=loss_fn(y_pred,y)
if epoch%1000==0:
print(“Epoch:{},Loss:{:.4f}”.format(epoch,loss.data[0]))
models.zero_grad()

loss.backward()

for param in models.parameters():
    param.data-=param.grad.data*learning_rate

遇到问题:
bool value of Tensor with more than one value is ambiguous
解决办法:
函数或者可调用对象使用时候没有加括号。
以上代码中的绝大部分和之前训练和优化部分的代码是一样的,但是参数梯度更新的方式发生了改变。因为使用了不同的模型搭建方法,所以访问模型中的全部参数是通过对“ models.parameters()”进行遍历完成的,然后才对每个遍历的参数进行梯度更新。
(2)PyTorch之torch.optim
到目前为止,代码中的神经网络权重的参数优化和更新还没有实现自动化,并且目前使用的优化方法都有固定的学习速率,所以优化函数相对简单,如果我们自己实现一些高级的参数优化算法,则优化函数部分的代码会变得较为复杂。
在PyTorch的torch.optim包中提供了非常多的可以实现参数自动优化的类,比如SGD、AdaGrad、RMSProp、Adam等,这些类都可以被直接调用,使用起来也非常方便。
我们使用自动化的优化函数实现方法对之前的代码进行替换:
import torch
from torch.autograd import Variable
batch_n=64
hidden_layer=100
input_data=1000
output_data=10

x=Variable(torch.randn(batch_n,input_data),requires_grad=False)
y=Variable(torch.randn(batch_n,output_data),requires_grad=False)

models=torch.nn.Sequential(
torch.nn.Linear(input_data,hidden_layer),
torch.nn.ReLU(),
torch.nn.Linear(hidden_layer,output_data)
)

epoch_n=10000
learning_rate=1e-4
loss_fn=torch.nn.MSELoss

optimzer=torch.optim.Adam(models.parameters(),lr=learning_rate)
这里使用了torch.optim包中的torch.optim.Adam类作为我们的模型参数的优化函数,在torch.optim.Adam类中输入的是被优化的参数和学习速率的初始值,如果没有输入学习速率的初始值,那么默认使用0.001这个值。因为我们需要优化的是模型中的全部参数,所以传递给torch.optim.Adam类的参数是models.parameters。
另外,Adam优化函数还有一个强大的功能,就是可以对梯度更新使用到的学习速率进行自适应调节,所以最后得到的结果自然会比之前的代码更理想。
#进行模型训练
for epoch in range(epoch_n):
y_pred = models(x)
loss = loss_fn(y_pred,y)
print(“Epoch:{}, Loss:{:.4f}”.format(epoch, loss.data[0]))
optimzer.zero_grad()
loss.backward()
#进行梯度更新
optimzer.step()
报错(同上,未解决):
RuntimeError Traceback (most recent call last)
in
2 for epoch in range(epoch_n):
3 y_pred = models(x)
----> 4 loss = loss_fn(y_pred,y)
5 print(“Epoch:{}, Loss:{:.4f}”.format(epoch, loss.data[0]))
6 optimzer.zero_grad()

~\anaconda3\envs\pytorch_gpu\lib\site-packages\torch\nn\modules\loss.py in init(self, size_average, reduce, reduction)
427
428 def init(self, size_average=None, reduce=None, reduction=‘mean’):
–> 429 super(MSELoss, self).init(size_average, reduce, reduction)
430
431 def forward(self, input, target):

~\anaconda3\envs\pytorch_gpu\lib\site-packages\torch\nn\modules\loss.py in init(self, size_average, reduce, reduction)
10 super(_Loss, self).init()
11 if size_average is not None or reduce is not None:
—> 12 self.reduction = _Reduction.legacy_get_string(size_average, reduce)
13 else:
14 self.reduction = reduction

~\anaconda3\envs\pytorch_gpu\lib\site-packages\torch\nn_reduction.py in legacy_get_string(size_average, reduce, emit_warning)
34 reduce = True
35
—> 36 if size_average and reduce:
37 ret = ‘mean’
38 elif reduce:

RuntimeError: bool value of Tensor with more than one value is ambiguous
在以上代码中有几处代码和之前的训练代码不同,这是因为我们引入了优化算法,所以通过直接调用optimzer.zero_grad来完成对模型参数梯度的归零;并且在以上代码中增加了optimzer.step,它的主要功能是使用计算得到的梯度值对各个节点的参数进行梯度更新
4.实战手写数字识别
基于PyTorch框架使用神经网络来解决一个关于手写数字识别的计算机视觉问题,评价我们搭建的模型的标准是它能否准确的对手写数字图片进行识别。
其具体过程是:先使用已经提供的训练数据对搭建好的神经网络模型进行训练并完成参数优化;然后使用优化好的模型对测试数据进行预测,对比预测值和真实值之间的损失值,同时计算出结果预测的准确率,在将要搭建的模型中会用到卷积神经网络模型。
(1)torch和torchvision
在PyTorch中有两个核心的包,分别是torch和torchvision。我们之前已经接触了torch包的一部分内容,比如使用例如torch.nn中的线性层加激活函数配合torch.optim完成了神经网络模型的搭建和模型参数的优化,并使用torch.autograd实现自动梯度的功能,接下来会介绍如何使用torch.nn中的类来搭建卷积神经网络。
torchvision包的主要功能是实现数据的处理、导入和预览等,所以如果需要对计算机视觉的相关问题进行处理,就可以借用在torchvision包中提供的大量的类来完成相应的工作。
首先,导入必要的包。对这个手写数字识别问题的解决只用到了torchvision中的部分功能,所以这里通过from torchvision import方法导入其中的两个子包datasets和transfroms,我们将会用到这两个包。
import torch
from torchvision import datasets,transforms
from torch.autograd import Variable
之后,我们就要想办法获取手写数字的训练集和测试集。
使用torchvision.datasets可以轻易实现对这些数据集的训练集和测试集的下载,只需要使用torchvision.datasets再加上需要下载的数据集的名称就可以了。
比如,在这个问题中我们要用到手写数字数据集,它的名称是MNIST,那么实现下载的代码就是torchvision.datasets.MNIST。其他常用的数据集如COCO、ImageNet、CIFCAR等都可以通过这个方法快速下载和载入。
实现数据集下载的代码如下:
data_train=datasets.MNIST(root="./data/",
transform=transform,
train=True,
download=True)
data_test=datasets.MNIST(root="./data/",
transform=transform,
train=False)
报错:
NameError Traceback (most recent call last)
in
4 download=True)
5 data_test=datasets.MNIST(root="./data/",
----> 6 transform=transform,
7 train=False)

NameError: name ‘transform’ is not defined
解决办法:
transform=transforms
其中,root用于指定数据集在下载之后的存放路径,这里存放在根目录下的data文件夹中;transform用于指定导入数据集时需要对数据进行哪种变换操作,在后面会介绍详细的变换操作类型,注意,要提前定义这些变换操作;train用于指定在数据集下载完成后需要载入哪部分数据,如果设置为True,则说明载入的是该数据集的训练数据集部分;如果设置为False,则说明载入的是该数据集的测试数据集部分。
(2)PyTorch之torch.transforms
在前面讲到过,在torch.transforms中提供了丰富的类对载入的数据进行变换
在计算机视觉中处理的数据集有很大一部分是图片类型的,而在PyTorch中实际进行计算的是Tensor数据类型的变量,所以我们首先需要解决的是数据类型转换的问题,如果获取的数据是格式或者大小不一的图片,则还需要进行归一化和大小放缩等操作,庆幸的是,这些方法在torch.transforms中都能找到。
在torch.transforms中有大量的数据变换类,其中有很大一部分可以用于实现数据增强(Data Argumentation)。若在我们需要解决的问题上能够参与到模型训练中的图片数据非常有限,则这时就要通过对有限的图片数据进行各种变换,来生成新的训练集了,这些变换可以是缩小或者放大图片的大小、对图片进行水平或者垂直翻转等,都是数据增强的方法。
不过在手写数字识别的问题上可以不使用数据增强的方法,因为可用于模型训练的数据已经足够了。
对数据进行载入及有相应变化的代码如下:
transform=transforms.Compose([transforms.ToTensor(),
transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])])
我们可以将以上代码中的torchvision.transforms.Compose类看作一种容器,它能够同时对多种数据变换进行组合。传入的参数是一个列表,列表中的元素就是对载入的数据进行各种变换操作。
在以上代码中,在torchvision.transforms.Compose中使用了一个类型的转换变换transforms.ToTensor和一个数据标准化变换 transforms.Normalize。这里使用的标准化变换也叫做标准差变换法, 这种方法需要使用原始数据的
均值(Mean)标准差(Standard Deviation)来进行数据的标准化
,在经过标准化变换之后,数据全部符合均值为0、标准差为1的标准正态分布。
transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])
这里均值和标准差的值并非来自原始数据,而是自行定义了一个,不过仍然能够达到我们的目的。
下面看看在torchvision.transforms中常用的数据变换操作:
【1】torchvision.transforms.Resize:用于对载入的图片数据按照我们需求的大小进行缩放。传递给这个类的参数可以是一个整形数据,也可以是一个类似于(h,w)的序列,其中,h代表高度,w代表宽度,但是如果使用的是一个整型数据,那么表示缩放的宽度和高度都是这个整型数据的值。
【2】torchvision.transforms.Scale:用于对载入的图片数据按照我们需求的大小进行缩放,用法和torchvision.transforms.Resize类似。
【3】torchvision.transforms.CenterCrop:用于对载入的图片以图片中心为参考点,按我们需要的大小进行裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。
【4】torchvision.transforms.RandomCrop:用于对载入的图片按我们需要的大小进行随即裁剪。传递给这个类的参数可以是一个整型数据,也可以是一个类似于(h,w)的序列。
【5】torchvision.transforms.RandomHorizontalFlip:用于对载入的图片按随机概率进行水平翻转。我们可以通过传递给这个类的参数自定义随机概率,如果没有定义,则使用默认的概率值0.5。
【6】torchvision.transforms.RandomVerticalFlip:用于对载入的图片按随机概率进行垂直翻转。我们可以通过传递给这个类的参数自定义随机概率,如果没有定义,则使用默认的概率值0.5。
【7】torchvision.transforms.ToTensor:用于对载入的图片数据进行类型转换,将之前构成PIL图片的数据转换成Tensor数据类型的变量,让PyTorch能够对其进行计算和处理。
【8】torchvision.transforms.ToPILImage:用于将Tensor变量的数据转换成PIL图片数据,主要是为了方便图片内容的显示。
(3)数据预览和数据装载
在数据下载完成并载入后,我们还需要对数据进行装载。我们可以将数据的载入理解为对图片的处理,在处理完成后,我们就需要将这些图片打包好送给我们的模型进行训练了,而装载就是这个打包的过程。在装载时通过batch_size的值来确认每个包的大小,通过shuffle的值来确定是否在装载的过程中打乱图片的顺序。
装载图片的代码如下:
data_loader_train=torch.utils.data.DataLoader(dataset=data_train,
batch_size=64,
shuffle=True)
data_loader_test=torch.utils.data.DataLoader(dataset=data_test,
batch_size=64,
shuffle=True)
对数据的装载使用的是torch.utils.data.DataLoader类,类中的dataset参数用于指定我们载入的数据集名称,batch_size参数设置了每个包中图片数据个数,代码中的值是64,所以在每个包中会包含64张图片。将shuffle参数设置为True,在装载的过程中会将数据随机打乱顺序并进行打包。
在装载完成后,我们可以选择其中一个批次的数据进行预览。
进行数据预览的代码如下:
images,labels=next(iter(data_loader_train))
img=torchvision.utils.make_grid(images)

img=img.numpy().transpose(1,2,0)
std=[0.5,0.5,0.5]
mean=[0.5,0.5,0.5]
img=img*std+mean
print([labels[i] for i in range(64)])
plt.imshow(img)
报错(未解决):
TypeError: ‘module’ object is not callable
在以上代码中使用了iter和next来获取一个批次的图片数据和其对应的图片标签,然后使用torchvision.utills中的make_grid类方法将一个批次的图片构造成网络模式。需要传递给torchvision.utils.make_grid的参数就是一个批次的装载数据,每个批次的装载数据都是4维的,维度的构成从前往后分别是batch_size、channel、height和weight,分别对应一个批次中的数据个数、每张图片的色彩通道数、每张图片的高度和宽度。在通过torchvision.utils.make_grid之后,图片的维度变成了(channel、height、weight),这个批次的图片全部被整合到了一起,所以在这个维度中对应的值也和之前不一样了,但是色彩通道数保持不变。
若我们想使用Matplotlib将数据显示成正常的图片形式,则使用的数据首先必须是数组,其次这个数组的维度必须是(height,weight,channel),即色彩通道数在最后面。所以我们要通过numpy和transpose完成初始数据类型的转换和数据维度的变换,这样才能够使用Matplotlib绘制出正确的图像。
(4)模型搭建和参数优化
在顺利完成数据装载后,我们就可以开始编写卷积神经网络模型的搭建和参数优化的代码了。
因为我们想要搭建一个包含了卷积层、激活函数、池化层、全连接层的卷积神经网络来解决这个问题,所以模型在结构上会和之前简单的神经网络有所区别,各个部分的功能实现依然是通过torch.nn中的类来完成的,比如卷积层使用torch.nn.Conv2d类来搭建,激活层使用torch.nn.ReLU类方法来搭建,池化层使用torch.nn.MaxPool2d类方法类搭建,全连接层使用torch.nn.Linear类方法来搭建。
实现卷积神经网络模型搭建的代码如下:
class Model(torch.nn.Module):
def init(self):
super(Model,self).init()
self.conv1=torch.nn.Sequential(
torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),
torch.nn.ReLU(),
torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(stride=2,kernel_size=2))

    self.dense=torch.nn.Sequential(
    torch.nn.Linear(14*14*128,1024),
    torch.nn.ReLU(),
    torch.nn.Dropout(p=0.5),
    torch.nn.Linear(1024,10))
    
def forward(self,x):
    x=self.conv1(x)
    x=x.view(-1,14*14*128)
    x=self.dense(x)
    return x

因为这个问题并不复杂,所以我们选择搭建一个在结构层次上有所简化的卷积神经网络模型,在结构上使用了两个卷积层:一个最大池化层和两个全连接层,这里对其具体的使用方法进行说明:
【1】torch.nn.Conv2d:用于搭建卷积神经网络的卷积层,主要的输入参数有:
输入通道数(整型,用于确定输入数据的层数)
输出通道数(整型,用于确定输出数据的层数)
卷积核大小(整型,用于确定卷积核的大小)
卷积核移动步长(整型,用于确定卷积核每次滑动的步长)
Paddingde(整型,值为0时表示不进行边界像素的填充,如果值大于0,那么增加数字所对应的边界像素层数)
【2】torch.nn.MaxPool2d:用于实现卷积神经网络中的最大池化层,主要的输入参数是:
池化窗口大小(整型,用于确定池化窗口的大小)
池化窗口移动步长(整型,用于确定池化窗口每次移动的步长)
Paddingde值同上
【3】torch.nn.Dropout:torch.nn.Dropout类用于防止卷积神经网络在训练的过程中发生过拟合,其工作原理简单来说就是在模型训练的过程中,以一定的随机概率将卷积神经网络模型的部分参数归零,以达到减少相邻两层神经连接的目的。
每轮训练中选择丢弃的神经连接不同,可以使得最后训练出来的模型对各部分的权重参数不产生过度依赖,从而防止过拟合。对于torch.nn.Dropout类,我们可以对随机概率值的大小进行设置,如果不做任何设置,就使用默认的概率值0.5。
前向传播forward函数中的内容。首先,经过self.conv1进行卷积处理;然后进行x.view(-1,1414128),对参数实现扁平化,因为之后紧接着就是全连接层,所以如果不进行扁平化,则全连接层的实际输出的参数维度和其定义输入的维度将不匹配,程序会报错;最后通过self.dense定义全连接进行最后的分类。
在编写完搭建卷积神经网络模型的代码后,我们就可以开始对模型进行训练和对参数进行优化了。
首先,定义在训练之前使用哪种损失函数和优化函数:
model=Model()
cost=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters())
在以上代码中定义了计算损失值的损失函数使用的是交叉熵,也确定了优化函数使用的是Adam自适应优化算法,需要优化的参数是在Model中生成的全部参数,因为没有定义学习速率的值,所以使用默认值;
最后,卷积神经网络模型进行模型训练和参数优化。
为了验证我们训练的模型是不是真的已如结果显示的一样准确,最好的方法就是随机选取一部分测试集中的图片,用训练好的模型进行预测,看看和真实值有多大的偏差,并对结果进行可视化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值