from __future__ import print_function
# 如果某个版本中出现了某个新的功能特性,而且这个特性和当前版本中使用的不兼容,也就是它在该版本中不是语言标准,那么我如果想要使用的话就需要从future模块导入。
import argparse
# argparse 是 Python 内置的一个用于命令项选项与参数解析的模块,通过在程序中定义好我们需要的参数,argparse 将会从 sys.argv 中解析出这些参数,并自动生成帮助和使用信息。
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
# 数据集加载预处理操作
from torch.optim.lr_scheduler import StepLR
# 提供了一些根据epoch训练次数来调整学习率(learning rate)的方法
"""Pytorch中神经网络模块化接口nn的了解"""
"""
torch.nn是专门为神经网络设计的模块化接口。nn构建于autograd之上,可以用来定义和运行神经网络。
nn.Module是nn中十分重要的类,包含网络各层的定义及forward方法。
定义自已的网络:
需要继承nn.Module类,并实现forward方法。
一般把网络中具有可学习参数的层放在构造函数__init__()中,
不具有可学习参数的层(如ReLU)可放在构造函数中,也可不放在构造函数中(而在forward中使用nn.functional来代替)
只要在nn.Module的子类中定义了forward函数,backward函数就会被自动实现(利用Autograd)。
在forward函数中可以使用任何Variable支持的函数,毕竟在整个pytorch构建的图中,是Variable在流动。还可以使用
if,for,print,log等python语法.
注:Pytorch基于nn.Module构建的模型中,只支持mini-batch的Variable输入方式,
比如,只有一张输入图片,也需要变成 N x C x H x W 的形式:
input_image = torch.FloatTensor(1, 28, 28)
input_image = Variable(input_image)
input_image = input_image.unsqueeze(0) # 1 x 1 x 28 x 28
我们在定义自已的网络的时候,需要继承nn.Module类,并重新实现构造函数__init__构造函数和forward这两个方法。但有一些注意技巧:
(1)一般把网络中具有可学习参数的层(如全连接层、卷积层等)放在构造函数__init__()中,当然我也可以吧不具有参数的层也放在里面;
(2)一般把不具有可学习参数的层(如ReLU、dropout、BatchNormanation层)可放在构造函数中,也可不放在构造函数中,如果不放在构造函数__init__里面,则在forward方法里面可以使用nn.functional来代替
(3)forward方法是必须要重写的,它是实现模型的功能,实现各个层之间的连接关系的核心。
模板:
from torch import nn
import torch.nn.functional as F
class net_name(nn.Module):
def __init__(self):
super(net_name, self).__init__()
# 可以添加各种网络层,例如添加self.conv1 = nn.Conv2d(3,10,3) 即:in_channels=3, out_channels=10, kernel_size=3
self.conv1 = nn.Conv2d(3, 10, 3)
# 具体每种层的参数可以去查看文档
def forward(self, x):
# 定义向前传播
out = self.conv1(x)
return out
神经网络的实例化调用
data = ..... #输入数据
# 实例化一个对象
module = net_name()
# 前向传播
module(data)
# 而不是使用下面的
# module.forward(data)
"""
class Net(nn.Module):
def __init__(self):
# nn.Module的子类函数必须在构造函数中执行父类的构造函数
super(Net, self).__init__()
# 等价与nn.Module.__init__(),调用父类的构造函数
self.conv1 = nn.Conv2d(1, 32, 3, 1) # (3*3*1)*32 1为输入通道数,32为输出通道数,3为卷积核大小,1为步长
self.conv2 = nn.Conv2d(32, 64, 3, 1) # (3*3*32)*64
# nn.Conv2d返回的是一个Conv2d class的一个对象,该类中包含forward函数的实现
# 当调用self.conv1(input)的时候,就会调用该类的forward函数
# 卷积运算图像尺寸变小了,原始是mxn的话,卷积核sxs,卷积后尺寸为:(m-s+1)x(n-s+1)。
self.dropout1 = nn.Dropout(0.25)
# 我们在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征
self.dropout2 = nn.Dropout(0.5)
self.fc1 = nn.Linear(9216, 128)
'''
in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。
out_features指的是输出的二维张量的大小,即输出的二维张量的形状为[batch_size,output_size],当然,它也代表了该全连接层的神经元个数。
从输入输出的张量的shape角度来理解,相当于一个输入为[batch_size, in_features]的张量变换成了[batch_size, out_features]的输出张量。
'''
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.conv1(x)
x = F.relu(x)
x = self.conv2(x)
x = F.relu(x) # 强制稀疏处理
x = F.max_pool2d(x, 2) # X为卷积核大小,2为步长
x = self.dropout1(x)
x = torch.flatten(x, 1) #降维,与后面的全连接层进行计算
x = self.fc1(x)
x = F.relu(x)
x = self.dropout2(x)
x = self.fc2(x)
output = F.log_softmax(x, dim=1)
'''
Softmax函数常用的用法是指定参数dim就可以:
(1)dim=0:对每一列的所有元素进行softmax运算,并使得每一列所有元素和为1。
(2)dim=1:对每一行的所有元素进行softmax运算,并使得每一行所有元素和为1。
LogSoftmax其实就是对softmax的结果进行log,即Log(Softmax(x))
'''
return output
# 分批次读入数据,优化器梯度置零,数据通过网络,计算损失,反向传播,权重更新
def train(args, model, device, train_loader, optimizer, epoch):
model.train()
# 设置train模式
for batch_idx, (data, target) in enumerate(train_loader): # enumerate枚举
# 分批读入数据
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
# 优化器梯度置0
output = model(data)
# 前向传播
loss = F.nll_loss(output, target)
# 计算损失
loss.backward()
# 反向传播
optimizer.step()
# 权重更新
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
if args.dry_run:
break
# 设置为test模式,这是要防止改变已训练好的权重。接着在with torch.no_grad()中让数据通过网络,计算损失和预测是否正确即可
def test(model, device, test_loader):
model.eval()
# 设置test模式
test_loss = 0
correct = 0
with torch.no_grad():
# 不进行图的构建,即没有grad_fn属性
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
# 前向传播
test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss
pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability
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)))
def main():
# Training settings
# 声明一个parser
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
#添加参数
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=14, metavar='N',
help='number of epochs to train (default: 14)')
parser.add_argument('--lr', type=float, default=1.0, metavar='LR',
help='learning rate (default: 1.0)')
parser.add_argument('--gamma', type=float, default=0.7, metavar='M',
help='Learning rate step gamma (default: 0.7)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--dry-run', action='store_true', default=False,
help='quickly check a single pass')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
parser.add_argument('--save-model', action='store_true', default=False,
help='For Saving the current Model')
args = parser.parse_args()
# 读取命令行参数
use_cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
device = torch.device("cuda" if use_cuda else "cpu")
train_kwargs = {'batch_size': args.batch_size}
test_kwargs = {'batch_size': args.test_batch_size}
if use_cuda:
cuda_kwargs = {'num_workers': 1,
'pin_memory': True,
'shuffle': True}
train_kwargs.update(cuda_kwargs)
test_kwargs.update(cuda_kwargs)
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
dataset1 = datasets.MNIST('../data', train=True, download=True,
transform=transform)
dataset2 = datasets.MNIST('../data', train=False,
transform=transform)
train_loader = torch.utils.data.DataLoader(dataset1,**train_kwargs)
test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs)
model = Net().to(device)
optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)
for epoch in range(1, args.epochs + 1):
train(args, model, device, train_loader, optimizer, epoch)
test(model, device, test_loader)
scheduler.step()
if args.save_model:
torch.save(model.state_dict(), "mnist_cnn.pt")
if __name__ == '__main__':
main()