import os
os.environ["CUDA_VISIBLE_DEVICES"] = '0' #默认为显卡0
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# torchvision 包收录了若干重要的公开数据集、网络模型和计算机视觉中的常用图像变换
from torchvision import datasets, transforms #做图片处理的库
#from torch.autograd import Variable
import time
# Training settings
BATCH_SIZE = 512 # 一个batch 的大小,大概需要2G的显存
EPOCHS = 10 # 总共训练批次
# 设备配置
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(DEVICE.type)#输出设备类型
# MNIST Dataset
# transform=transforms.ToTensor():将图像转化为Tensor,在加载数据的时候,就可以对图像做预处理
train_dataset = datasets.MNIST(root='./data/',
train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1037,), (0.3081,))
]))
test_dataset = datasets.MNIST(root='./data/',
train=False,download=False,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1037,), (0.3081,))
]))
# Data Loader (Input Pipeline)
# 训练数据集的加载器,自动将数据分割成batch,顺序随机打乱
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=BATCH_SIZE, #显存有限制
shuffle=True)
# 训测试数据集的加载器,自动将数据分割成batch,顺序随机打乱
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=BATCH_SIZE,
shuffle=True)
# 定义模型
class ConvNet(nn.Module):
def __init__(self):
super().__init__()
#输入图像1*1*28*28
self.conv1 = nn.Conv2d(1, 10, 5)
# 第一层卷积1 input channel, 10 output channels, 5x5 square convolution kernel
self.conv2 = nn.Conv2d(10, 20, 3)
# 第二层卷积:10input channel, 20 output channels, 3x3 square convolution kernel
self.fc1 = nn.Linear(20 * 10 * 10, 500)
# 线性连接层的输入尺寸为最后一层立方体的平铺,输出层500个节点
self.fc2 = nn.Linear(500, 10)
# 最后一层线性分类单元,输入为500,输出为要做分类的类别数
def forward(self, x): #forward() 函数ConvNet的前向传播
# x尺寸:(batch_size, image_channels, image_width, image_height)
#卷积层->池化层->激活函数,两个卷积层
in_size = x.size(0)
out= self.conv1(x) # 1* 10 * 24 *24 第一层卷积
out = F.relu(out) # 激活函数用ReLU,防止过拟合
#最大池化层:将图像分成小块,在每一块中寻找最大值,返回给输出,
out = F.max_pool2d(out, 2, 2) # 1* 10 * 12 * 12 第二层池化,将图片变小
out = self.conv2(out) # 1* 20 * 10 * 10 # 第三层又是卷积
out = F.relu(out) # 非线性函数
# 将立体的特征图 tensor 压成一个一维的向量
# view 函数可以将一个tensor 按指定的方式重新排布
out = out.view(in_size, -1) # 1 * 2000
# 一个线性连接层,输入尺寸为最后一层立方体的线性平铺,输出层 500个节点
out = self.fc1(out) # 1 * 500
out = F.relu(out) # 第五层为全连接,ReLU激活函数
# 最后一层线性分类单元,输入为 500,输出为要做分类的类别数10个
out = self.fc2(out) # 1 * 10
# out的尺寸:(batch_size, num_classes)
# # 输出层为 log_Softmax,即概率对数值 log(p(×))。采用log_softmax可以使后面的交叉熵计算更快
# dim=0 ,即softmax后横向的和为1
out = F.log_softmax(out, dim = 1)
return out
# 定义训练函数
def train(model, device, train_loader, optimizer, epoch):
model.train() # 新建一个卷积神经网络的实例
for batch_idx, (data, target) in enumerate(train_loader): # 针对容器中的每一个批进行循环
# 将数据送入GPU中计算 data 为一批图像,target 为一批标签
data, target = data.to(device), target.to(device)
# optimizer.zero_grad() # 清空梯度
output = model(data) ## 神经网络完成一次前馈的计算过程,得到预测输出output
loss = F.nll_loss(output, target) # 将output与标签target比较,计算误差
optimizer.zero_grad() # 清空梯度
loss.backward() # 反向传播
optimizer.step() ## 一步随机梯度下降算法
if (batch_idx + 1) % 30 == 0: # 每间隔batch_idx + 1个batch 执行一次打印操作
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
# 定义测试函数
def test(model, device, test_loader):
model.eval() # 给网络楧型做标记,标志着模型在训练集上训练
test_loss =0 #损失率
correct = 0 #准确率
with torch.no_grad(): #不需要计算梯度
for data, target in test_loader: #遍历数据集中的每一个batch
data, target = data.to(device), target.to(device) #保存测试的输入和输出,将数据送入GPU
output = model(data) #模型输出
test_loss += F.nll_loss(output, target, reduction = 'sum') # 将一批的损失相加
pred = output.max(1, keepdim = True)[1] # 找到概率最大的下标
correct += pred.eq(target.view_as(pred)).sum().item() #准确率
test_loss /= len(test_loader.dataset) #错误率
print("\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%) \n".format(
test_loss, correct, len(test_loader.dataset),
100.* correct / len(test_loader.dataset))) #输出准确率,损失率
start = time.perf_counter() #perf_counter()返回当前的计算机系统时间
#生成模型和优化器
model = ConvNet().to(DEVICE)
optimizer = optim.Adam(model.parameters())
# 最后开始训练和测试
for epoch in range(1, EPOCHS + 1): # 总共训练批次为EPOCHS+1
train(model, DEVICE, train_loader, optimizer, epoch) #训练数据
test(model, DEVICE, test_loader) #测试数据
end = time.perf_counter() #系统终止时间
print(DEVICE.type,'时间',end) #输出当前时间
????????整个卷积神经网络的运作分成了两个阶段:前馈运算阶段和反馈学习阶段。在网络的前馈阶段(从输人图像到输出数字),所有连接的权重值都不改变,系统会根据输人图像计算输出分类,并根据网络的分类与数据中的标签(标准答案)进行比较计算出交叉熵作为损失函数。接下来,在反馈阶段,根据前馈阶段的损失两数调整所有连接上的权重值,从而完成神经网络的学习过程。