技术交流QQ群:1027579432,欢迎你的加入!
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2019-02-26 10:41:22
# @Author : cdl ([email protected])
# @Link : https://github.com/cdlwhm1217096231/python3_spider
# @Version : $Id$
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
import matplotlib.pyplot as plt
from torchvision.datasets import CIFAR10
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader # utils是工具集的意思
import numpy as np
"""
1.torch.nn是专门为神经网络设计的模块化接口,nn构建在autograd上,可以用来定义和运行神经网络。
2.约定:torch.nn设置别名nn
3.nn.functional这个包中包含了神经网络中使用的一些常用函数,一般把nn.functional设置别名为F
"""
# 一、定义一个网络
"""
pytorch已经为我们准备好了现成的网络模型,只要继承nn.Module,并实现它的forward()方法即可,pytorch会自动进行求导,实现反向传播backward()。在forward()函数中可以使用任何tensor支持的函数,还可以使用if for循环 print log等python语法
"""
class Net(nn.Module):
def __init__(self):
# nn.Module子类的函数必须在构造函数中执行分类的构造函数
super(Net, self).__init__()
# 卷积层1表示输入的是单通道的图片,6表示输出通道数,3表示卷积核3*3
self.conv1 = nn.Conv2d(1, 6, 3)
# 线性层输入是1350个特征,输出是10个特征
self.fc1 = nn.Linear(1350, 10) # 1350个特征是如何计算得到的,要看后面的forward()函数
def forward(self, x):
print(x.size()) # 结果是[1, 1, 32, 32]
# 卷积--->激活---->池化
x = self.conv1(x) # 根据卷积的尺寸计算公式,计算结果是30
x = F.relu(x)
print(x.size()) # 结果是[1, 6, 30, 30]
x = F.max_pool2d(x, (2, 2)) # 池化层后,计算的结果是15
x = F.relu(x)
print(x.size()) # 结果是[1, 6, 15, 15]
# flatten操作,将[1,6,15,15]变为[1,1350]
x = x.view(1, -1)
print(x.size()) # 这里就是fc1层的输入1350
x = self.fc1(x)
return x
net = Net()
print(net)
# 网络可学习的参数,通过net.parameters()返回
for parameters in net.parameters():
print(parameters)
# net.named_parameters可同时返回可学习的参数及名称
for name, parameters in net.named_parameters():
print(name, ":", parameters.size())
# forward()函数的输入和输出都是Tensor
inputs = torch.randn(1, 1, 32, 32)
output = net(inputs)
print(output.size())
print(inputs.size())
# 反向传播前,必须将所有参数的梯度清零
net.zero_grad()
output.backward(torch.ones(1, 10)) # 反向传播自动实现
"""
注意:torch.nn只支持mini-batches,不支持一次只输入一个样本,即一次必须是一个batch
即使输入的是一个样本,也会对样本进行分组。所以,所有的输入都会增加一个维度
"""
# 损失函数
targets = torch.arange(0, 10).view(1, 10).float()
criterion = nn.MSELoss()
loss = criterion(output, targets)
print(loss.item())
# 优化器
"""
在反向传播计算完所有参数的梯度后,还需使用优化方法来更新网络的权重和参数,如SGD的更新策略w = w - learing_rate * gradient,在torch.optim中实现了大多数的优化方法,如RMSProp、Adam、SGD等
"""
output = net(inputs)
criterion = nn.MSELoss()
loss = criterion(output, targets)
# 新建一个优化器,SGD只需要调整参数和学习率
optimizer = optim.SGD(net.parameters(), lr=1e-3)
# 先梯度清零----下面的语句与net.zero_grad()效果一样
optimizer.zero_grad()
loss.backward()
optimizer.step() # 更新参数
print("--------------------------------------完美的分隔线-------------------------------")
"""
1.使用torch.nn包来创建神经网络,nn包依赖autograd包来定义模型并求导。一个nn.Module包含各个层和一个forward(input)的方法,该方法返回output。
2.神经网络的典型训练过程如下:
a.定义包含一些可以学习的参数(或叫权重)的神经网络
b.在数据集上迭代
c.通过神经网络处理
d.计算损失(输出结果和正确值的差值大小)
e.将梯度反向传播回网络的参数
f.更新网络的参数,主要使用如下简单的规则: weight = weight - learning_rate * gradient
"""
# a~c.
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel,6 output channels,5*5 filters
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# fully connect layer
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# max pooling over a (2,2) windows
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# if the size is square,you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x)) # flatten
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
print("size:", size)
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
# 在模型中必须要定义forward函数,backward函数会被autograd自动创建。可以在forward函数中使用任何针对tensor的操作
# net.parameters():返回可以被学习的参数(权重)列表和值
params = list(net.parameters())
print("parameters: \n", params)
print("list length is:", len(params))
print("conv1 weight is:", params[0].size())
# 测试随机输入32*32
input = torch.randn(1, 1, 32, 32)
out = net(input)
print("out: \n", out)
# 然后将所有的参数梯度缓存清零,再进行随机梯度的反向传播,必须先清零!
net.zero_grad()
out.backward(torch.randn(1, 10))
"""
注:torch.nn只支持小批量的输入。整个torch.nn包都支持小批量样本,不支持单个样本。例如,nn.Conv2d接收一个4维的张量,每一维分别是samples * n_channel * n_h * n_w(样本数,通道数,高度,宽度)。如果有单个样本,只需使用input.unsqueeze(0)来添加其他的维数。
"""
# d.损失函数:一个损失函数接收一对(output,target)作为输入,计算一个值来估计网络的输出和目标值相差多少
output = net(input)
target = torch.randn(10) # 随机值作为期望输出
target = target.view(1, -1) # 使得期望值与output的shape相同
criterion = nn.MSELoss()
loss = criterion(output, target)
print("loss =", loss)
"""
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
-> view -> linear -> relu -> linear -> relu -> linear
-> MSELoss
-> loss
"""
print("MSELoss:", loss.grad_fn)
print("Linear:", loss.grad_fn.next_functions[0][0])
print("ReLU:", loss.grad_fn.next_functions[0][0].next_functions[0][0])
# e.反向传播
"""
调用loss.backward()获得反向传播的误差,在调用前需要清除已存在的梯度,否则梯度将会被累加到已存在的梯度上!
"""
net.zero_grad() # 清除梯度
print("conv1.bias.grad before backward:", net.conv1.bias.grad)
loss.backward()
print("conv1.bias.grad after backward:", net.conv1.bias.grad)
# f.更新权重
"""
在实践中,最简单的权重更新规则是随机梯度下降SGD: weight = weight - learning_rate * gradient
可以使用python代码实现这个简单的规则:
learning_rate= 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
但是当使用神经网络要想实现各种不同的更新规则时,如SGD,Adam,RMSPROP等,pytorch中构建了一个包torch.optim实现了所有的规则
"""
import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
# in your training loop
optimizer.zero_grad() # zero the gradient buffers!!!,一定不要少了
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update