深度学习,复习,模型训练与验证
转 Datawhale
import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
from torch.autograd.variable import Variable
import torch.nn.functional as F
import torch.optim as optim
import time
1.数据预处理、加载
转化为tensor, 将元素转化为0-1 的数字,Normalize将其归一化
transforms = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
训练集需要训练
train_set = torchvision.datasets.CIFAR10(’…/…/…/dataset’,
train=True,
trainform=None,
target_transform=None,
download=True)
batch_size设置了批量大小,shuffle设置为True在装载过程中为随机乱序,num_workers>=1 表示多线程读取数据
train_loader = torch.utils.data.Dataloader(train_set, batch_size=4, shuffle=False, num_workers=0)
测试集不需要训练
test_set = torchvision.datasets.CIFAR10(’…/…/…/dataset’,
train=False,
transform=None,
target_transform=None,
download=True)
test_load = torch.utils.data.Dataloader(test_set, batch_size=4, shuffle=False, num_workers=0)
指定类别标签
classes = (‘plane’, ‘car’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’)
查看图像
def img_show(img):
img = img / 2 + 0.5 # unnormalize
np_img = img.numpy()
plt.imshow(np.transpose(np_img, (1, 2, 0)))
plt.show()
data_iter = iter(train_loader)
images, labels = data_iter.next()
img_show(torchvision.utils.make_grid(images))
print(’’.join(’%5s’ % classes[labels[j]] for j in range(4)))
模型训练,调参
定义神经网络
‘’’
1.卷积层
self.conv1 = nn.conv2d(3, 8, 3, padding=1)
定义一次卷积运算,其中第一个3表示输入为3通道对应到本次测试为图片的RGB三个通道,数字8的意思为8个卷积核,第二个3表示卷积核的大小为3x3,padding=1表示在图片的周围增加一层像素值用来保存图片的边缘信息。
class torch.nn.conv2d(in_channels, not _channels, kernel_size, stride=1, padding=0, dilation=1,groups=1, bias=True)
in_channels(int) :输入信号的通道
out_channels(int) :卷积产生的通道
kerner_size(int or tuple) :卷积核的尺寸
stride(int or tuple, optional) :卷积步长
padding(int or tuple, optional) :输入的每一条边补充0的层数
dilation(int or tuple, optional) :卷积核元素之间的间距
groups(int, optional) :从输入通道到输出通道的阻塞连接数
bias(bool, optional) :如果bias=True,添加偏置
2.池化层
self.pool1 = nn.MaxPool2d(2,2)
二维池化其中第一个2表示池化窗口的大小为2x2,第二个2表示窗口移动的步长
class torch.nn.MaxPool2d(kernel_size, stride=None, padding=o, dilation=1, return_indices=False, ceil_mode=False)
kernel_size(int or tuple) :max pooling的窗口大小
stride(int or tuple, optional) :max pooling的窗口移动的步长。默认值是kernel_size
padding(int or tuple, optional) :输入的每一条边补充0的层数
dilation(int or tuple, optional) :一个控制窗口中元素步幅的参数
return_indices : 如果等于True,会返回输出最大值的序号,对于上采样操作会有帮助
ceil_mode - 如果等于True,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作
3.归一化
self.bn1 = nn.BatchNorm2d(64)
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
num_features: 来自期望输入的特征数,该期望输入的大小为batch_size x num_features [x width]
eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
momentum: 动态均值和动态方差所使用的动量。默认为0.1。
affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
track_running_stats:布尔值,当设为true,记录训练过程中的均值和方差;
在进行训练之前,一般要对数据做归一化,使其分布一致,但是在深度神经网络训练过程中,通常以送入网络的每一个batch训练,
这样每个batch具有不同的分布;此外,为了解决internal covarivate shift问题,这个问题定义是随着batch normalizaiton这篇论文提出的,
在训练过程中,数据分布会发生变化,对下一层网络的学习带来困难。
所以batch normalization就是强行将数据拉回到均值为0,方差为1的正太分布上,这样不仅数据分布一致,而且避免发生梯度消失。
4.ReLU激活函数
self.relu1 = nn.ReLU()
引入激活函数是为了增加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。
如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机(Perceptron)。
如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。
5.全连接层
self.fc14 = nn.Linear(512, 4, 4, 1024)
全连层输入数据个数为一维数据512x4x4,输出个数为1024.
6.Dropout
self.drop1 = nn.Dropout2d()
Dropout:删除掉隐藏层随机选取的一半神经元,然后在这个更改的神经元网络上正向和反向更新,然后再恢复之前删除过的神经元,
重新选取一般神经元删除,正向反向,更新w,b.重复此过程,最后学习出来的神经网络中的每个神经元都是在一半神经元的基础上学习的,
当所有神经元被恢复后,为了补偿,我们把隐藏层的所有权重减半。
‘’’
‘’’
conv卷积的结果是32x32x18,可以理解为32x32大小的图片共有18张(18通道)
卷积操作后relu激活函数增加非线性拟合能力
maxpool之后结果是16x16x18 ,可以理解为16x16大小的图片共有18张(18通道)
全连接层fc1,输入层18x16x16 = 4608个神经元,输出层有64个神经元
全连接层fc2,输入层有64个神经元,输出层有10个神经元,对应的是十个标签
‘’’
2.1 定义神经网络
class SimpleCNN(torch.nn.Module):
def init(self):
super(SimpleCNN, self).init()
self.conv1 = torch.nn.Conv2d(3, 18, kernel_size=3, padding=1, stride=1)
self.pool = torch.nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
self.fc1 = torch.nn.Linear(18 * 16 * 16, 64)
self.fc2 = torch.nn.Linear(64, 10)
# 前向传播
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(x)
# view将池化后的张量拉伸,-1的意思是未知数的意思,根据其他位置(这里是18*16*16)来推断这个-1是几
x = x.view(-1, 18 * 16 * 16)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
2.2 定义损失函数及优化
def creatlossandoptimizer(net, learning_rate=0.01):
loss = torch.nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.Adam(net.parameters(), lr=learning_rate) # Adam 优化算法是随机梯度下降算法的扩展式
print(optimizer)
return loss, optimizer
2.3 定义训练、验证、预测模块
def get_train_loader(batch_size):
train_sample = 0
# train_loader, 一次性加载了sample中全部的样本数据,每次以batch_size为一组循环
train_loader = torch.utils.data.Dataloader(train_set,
batch_size=batch_size,
sampler=train_sample,
num_workers=0)
return train_loader
val_loader = torch.utils.data.Dataloader(train_set, batch_size=64, sampler=validation_sample, num_worker=0)
test_loader = torch.utils.data.Dataloader(test_set, batch_size=4, sampler=test_sample, num_worker=0)
2.4 迭代训练
def trianNet(net, batchsize, n_epochs, learning_rate):
print(“HYPERPARAMETERS:”)
print(“batch_size=”, batchsize)
print(“n_epochs=”, n_epochs)
print(“learning_rate”, learning_rate)
print("batchszie:", batchsize)
train_loader = get_train_loader(batchsize)
n_batches = len(train_loader) # n_batches * batchsize = 20000 (样本数目)
print("n_batches", n_batches)
loss, optimizer = creatlossandoptimizer(net, learning_rate)
train_start_time = time.time()
print("training start:")
for epoch in range(n_epochs):
running_loss = 0.0
print_every = n_batches
print("print_every:", print_every)
start_time = time.time()
total_train_loss = 0
for i, data in enumerate(train_loader, 0):
input, labels = data
# print(input, labels)
inputs, labels = Variable(inputs), Variable(labels)
# 将所有的梯度置零,原因是防止每次backwards的时候梯度会累加
optimizer.zero_grad()
# forward
outputs = net(inputs)
# loss
loss_size = loss(outputs, labels)
# backward
loss_size.backward()
# updata weights
optimizer.step()
print(loss_size)
running_loss += loss_size.data[0]
print("running_loss:", running_loss)
total_train_loss += loss_size.data[0]
print("total_train_loss:", total_train_loss)
# 在一个epoch里,每十组 batchsize大小的数据输出一次结果,即以batch_size 大小的数据为一组
if (i + 1) % 10 == 0:
print("epoch{}, {:d} \t train_loss: {:.2f}s".format(epoch + int(100 * (i + 1) / n_batches),
running_loss / 10, time.time() - start_time))
running_loss = 0.0
start_time = time.time()
total_val_loss = 0
for inputs, labels in val_loader:
# Wrap tensors in Variables
inputs, labels = Variable(inputs), Variable(labels)
# Forward pass
val_outputs = net(inputs)
val_loss_size = loss(val_outputs, labels)
# print("-----", val_loss_size)
total_val_loss += val_loss_size.data[0]
# 验证集的平均损失
print("Validation loss = {:.2f}".format(total_val_loss / len(val_loader)))
# 所有的Epoch结束,也就是训练结束,计算花费的时间
print("Training finished, took {:.2f}s".format(time.time() - train_start_time))
torch.save(model_object.state_dict(), ‘model.pt’)
model.load_state_dict(torch.load(’ model.pt()’))