这里我们通过网络上比较流行的mnist进行简单的卷积神经网络的搭建,其中卷积神经网络与神经网络的区别就是卷积神经网络中包含了对数据的卷积和池化,但是这一块的代码都是pytorch给我们写好的,我们只需要调用就可以。
其中简单的介绍一下mnist:主要的功能就是识别人们写的数字,然后输出预测的结果
大量的数据,在程序中都通过网上进行了下载,注意输入的数据是1*28*28的格式,其中1表示通道(表示黑白图片的意思),28*28分别表示图片的长和高
直接上代码,代码中有很多的注释
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import numpy as np
# 定义一个卷积神经网络的类
class Net(nn.Module):
# 一般在init中定义一个网路的结构
def __init__(self):
super(Net, self).__init__()
# 定义卷积层
# 图像中的通道数(in_channels)1,由卷积产生的通道数(out_channels)20
# 卷积核的大小(滤波器kernel_size)5,卷积的步数(stride 默认是1)1
self.conv1 = nn.Conv2d(1, 20, 5, 1)
self.conv2 = nn.Conv2d(20, 50, 5, 1)
# 定义全连接层
# 这里4*4*50是因为经过卷积层第二层之后,图像变成了4*4的大小,然后第二层卷积层产生50个通道
self.fc1 = nn.Linear(4 * 4 * 50, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x):
# x: 1 * 28 * 28----这是最开始的时候
x = F.relu(self.conv1(x)) # 卷积层1加relu激活
# x: 20 * 24 * 24----这是经历了第一层卷积之后的形状(24=28+1-5),第一次卷积后通道变成了20
x = F.max_pool2d(x, 2, 2) # 进行池化,后面两个参数分别是盒的大小和步幅
# X: 20 * 12 * 12----这是经历了第一次池化后的形状
x = F.relu(self.conv2(x)) # 卷积层2加relu激活
# X: 50 * 8 * 8----这是经历了第二次卷积之后的形状
x = F.max_pool2d(x, 2, 2) # 进行池化,后面两个参数分别是盒的大小和步幅
# X: 50 * 4 * 4----这是经历了第二次池化后的形状
x = x.view(-1, 4 * 4 * 50) # 因为有第二层卷积出来之后有50个channel
x = F.relu(self.fc1(x)) # 全连接层加激活函数
x = self.fc2(x)
return F.log_softmax(x, dim=1) # 按照维度为1的x进行log_softmax,拿到的是log probability
# 加载数据集。如果没有,就进行下载----获取方式mnist_data[222][0].shape表示一张图片
mnist_data = datasets.MNIST('./mnist_data', train=True, download=True,
transform=transforms.Compose([transforms.ToTensor()]))
# 这里我们进行标准化(nom)的处理,从而更好地进行学习,算出每个照片的平均值和标准差
# d[0] for d in mnist_data,这样之后d[0]就是mnist_data[i][0]
data = [d[0].data.cpu().numpy() for d in mnist_data]
data_mean = np.mean(data) # 这是所有像素的平均值
data_std = np.std(data) # 这是所有像素的方差
# 判断是不是能使用cpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
batch_size = 32 # 批处理的大小
# 下面进行数据的加载,定义一个数据加载器。
# shuffle表示每次都进行洗牌;num workers表示数据加载器一次性创建num_worker个工作进程;pin_memory=true表示预存数据
# 加载训练数据
train_loader = DataLoader(datasets.MNIST('./mnist_data', train=True, download=True,
transform=transforms.Compose(
[transforms.ToTensor(), # 将数据转换为tensor
transforms.Normalize((data_mean,), (data_std,))])), # 进行标准化
batch_size=batch_size, shuffle=True, num_workers=1, pin_memory=True)
# 加载测试数据,这里修改了MNIST中train,让其等于False
test_loader = DataLoader(datasets.MNIST('./mnist_data', train=False, download=True,
transform=transforms.Compose(
[transforms.ToTensor(), # 将数据转换为tensor
transforms.Normalize((data_mean,), (data_std,))])), # 进行标准化
batch_size=batch_size, shuffle=True, num_workers=1, pin_memory=True)
# 实现网络框架
lr = 0.01 # 学习率
momentum = 0.05 # 优化器中需要用到的动能
model = Net().to(device) # 去gpu还是cpu
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum) # 优化器,其中model.parameters()表示获取模型的参数
save_path = "./learning_data/mnist_cnn.pt" # 学习的数据保存的位置
# 定义训练的方法,拿到的数据是batch_size个
def train(model, device, train_loader, optimizer, epoch):
model.train() # 把模型变成训练的模式
# idx表示下标,data表示32张图片,target表示正确的标签
for idx, (data, target) in enumerate(train_loader): # 这里idx最大是1874 = (train_loader=6000)/32
data, target = data.to(device), target.to(device) # 变成gpu
prediction = model(data) # 输出结果是batch_size*10,自动调用Net中的forward方法,返回的是10个类的概率
loss = F.nll_loss(prediction, target) # The negative log likelihood loss. 就是一种损失函数
# SGD优化的过程,同时进行反向求导
optimizer.zero_grad() # 清零
loss.backward() # 误差反向求导
optimizer.step() # 下一步
# 输出一些内容
if idx % 100 == 0:
print("Train Epoch:{}, iteration:{}, loss{}".format(epoch, idx, loss.item()))
# 定义测试的方法,拿到的数据是batch_size个
def test(model, device, test_loader):
model.eval() # 把模型变成评估的模式
total_loss = 0. # 全部的损失
correct = 0. # 查看有多少是正确的
with torch.no_grad():
# idx表示下标,data表示32张图片,target表示正确的标签
for idx, (data, target) in enumerate(test_loader): # 这里idx最大是1874 = (test_loader=10000)/32
data, target = data.to(device), target.to(device) # 变成gpu
output = model(data) # 输出结果是batch_size*10,然后再第二个维度上找到最大值
# 默认的reduction是求loss的平均,这里我们可以进行相加,计算全部的损失函数
total_loss += F.nll_loss(output, target,
reduction="sum").item() # The negative log likelihood loss. 就是一种损失函数
prediction = output.argmax(dim=1) # 输出结果是batch_size*1
correct += prediction.eq(target.view_as(prediction)).sum().item() # 计算有多少个是正确的
total_loss /= len(test_loader.dataset) # 平均每张图片的损失值
acc = correct / len(test_loader.dataset) * 100. # 计算预测成功的占比
print("Test loss: {}, Accuracy: {}".format(total_loss, acc) + "%")
num_epochs = 2 # 遍历完一边数据称为一个epoch
if __name__ == '__main__':
for epoch in range(num_epochs):
# 训练数据,这里需要注意,我们拿到的测试数据是batch_size个
train(model, device, train_loader, optimizer, epoch)
# 测试数据
test(model, device, test_loader)
# 保存数据
torch.save(model.state_dict(), save_path)
到这里,一个简单的卷积神经网络就完成了。