携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
在上一篇文章中,我们介绍神经网络的基本概念,包括感知器的基本概念,前向计算,反向传播,分类与回归,过拟合和欠拟合,正则化问题。
今天,我们来利用神经网络解决分类和回归问题
-
2.1 Pytorch完成手写数字分类模型搭建
- 数据集为黑白图,大小为28x28
- 其中,包括60000张训练图片和10000张测试图片
- 数据集下载:yann.lecun.com/exdb/mnist/
- 分类问题采用Loss:交叉熵损失
- CrossEntropyLoss
- 本质是0-9的十分类问题
回归问题和分类问题最本质的区别是一个是预测连续的值,一个是预测离散的值。
-
2.1.1 数据集的加载
手写数字识别的数据集已经集成在torchvision这个包中,在这个包中还有cifar10,coco等一系列数据集,我们可以调用它来完成对数据集的加载
train_data = dataset.MNIST(root="mnist",
train=True,
transform=transforms.ToTensor(),
download=True)
在下载完数据集之后,可以看到一个mnist的文件夹,在这个文件夹下包含了原始的数据和处理过的数据
采用batchsize的方式,选择一小部分的数据来丢掉网络里来进行训练,这一小部分数据就叫做batchsize,利用这一小部分数据来对网络进行训练,进行参数的调整
所以我们在训练图片的时候,通常采用min batch的方式
#batchsize
train_loader = data_utils.DataLoader(dataset=train_data,
batch_size=64,
shuffle=True)
test_loader = data_utils.DataLoader(dataset=test_data,
batch_size=64,
shuffle=True)
-
2.1.2 定义网络
import torch
class CNN(torch.nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv =torch.nn.Sequential(
torch.nn.Conv2d(1, 32, kernel_size=5, padding=2),
torch.nn.BatchNorm2d(32),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.fc = torch.nn.Linear(14 * 14 * 32, 10)
def forward(self, x):
out = self.conv(x)
out = out.view(out.size()[0], -1)
out = self.fc(out)
return out
cnn = CNN()
-
2.1.3 定义损失函数和优化器
# loss
loss_func = torch.nn.CrossEntropyLoss()
#optimizer
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.01)
-
2.1.4 训练数据
#training
for epoch in range(10):
for i, (images, labels) in enumerate(train_loader):
images = images.cuda()
labels = labels.cuda()
outputs = cnn(images)
loss = loss_func(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print("epoch is {}, ite is "
"{}/{}, loss is {}".format(epoch+1, i,
len(train_data) // 64,
loss.item()))
可以看到,在一个epoch内,需要迭代937次才能将全部样本训练完,loss处于不断的收敛的过程,从最开始的十几降到0.02
-
2.1.5 计算测试集的准确率
#eval/test
loss_test = 0
accuracy = 0
for i, (images, labels) in enumerate(test_loader):
# images = images.cuda()
# labels = labels.cuda()
outputs = cnn(images)
#[batchsize]
#outputs = batchsize * cls_num
loss_test += loss_func(outputs, labels)
_, pred = outputs.max(1)
accuracy += (pred == labels).sum().item()
accuracy = accuracy / len(test_data)
loss_test = loss_test / (len(test_data) // 64)
print("epoch is {}, accuracy is {}, "
"loss test is {}".format(epoch + 1,
accuracy,
loss_test.item()))
-
2.1.6 保存模型
torch.save(cnn, "mnist_model.pkl")
-
2.1.7 利用保存的模型进行推理
import torch
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
from CNN import CNN
# net
test_data = dataset.MNIST(root="mnist",
train=False,
transform=transforms.ToTensor(),
download=False)
test_loader = data_utils.DataLoader(dataset=test_data,
batch_size=64,
shuffle=True)
cnn = torch.load("mnist_model.pkl")
# cnn = cnn.cuda()
# loss
# eval/test
loss_test = 0
accuracy = 0
import cv2
for i, (images, labels) in enumerate(test_loader):
outputs = cnn(images)
_, pred = outputs.max(1)
accuracy += (pred == labels).sum().item()
#batchsize * 1 * 28 * 28
for idx in range(images.shape[0]):
im_data = images[idx]
im_label = labels[idx]
im_pred = pred[idx]
im_data = im_data.numpy()
im_data = im_data.transpose(1, 2, 0)
print("label", im_label)
print("pred", im_pred)
cv2.imshow("imdata", im_data)
cv2.waitKey(0)
accuracy = accuracy / len(test_data)
print(accuracy)
运行结果:
0.9833
-
2.2 模型的性能评价——交叉验证
- 简单交叉验证:将数据集划分成:train/test
- 2-折交叉验证:将数据集划分成:train/test,先将train作为训练集,将tset作为测试集,然后将test作为训练集,将train作为测试集
- k-折交叉验证:将数据分成k份,每次使用k-1次来训练,1份用来做测试,然后循环k次,并结合k次的训练结果进行评价
- 留一交叉验证(数据缺失的情况下使用):保留一份样本,使用剩下的样本进行训练,优点是训练的时候使用了基本上所有的数据,缺点是需要进行多次的训练,训练的周期比较久。