1.反向传播:在神经网络训练过程中,反向传播算法就是带有链式法则的梯度下降法。
I--->H--->O==>L
z1 z2 z3 z4
上图表示一个简单的例子,输入层I,隐藏层H,输出层O,损失值L
我们假设输入为z1,经过隐藏层得到了z2,经过输出层得到z3,最后通过损失函数算出损失值z4,然后正向传播到此结束,接着进行反向传播(backpropagation alg),我们先定义一个,不用考虑是什么东西,我们将上图的传播方式颠倒一下。
I<---H<---O<==L
1 2 3 4
我们将这个设为4=dL/dL作为L的输入,然后经过O得到3,然后经过H得到2,最后经过I得到1,这里的经过其实是反向传播,在这个过程中利用z3和3和链接H,O的权重矩阵W(H,O)可以求得梯度值,L/W(H,O),同理z2,2得到L/W(I,H)。
从总体抽象上反向传播得流程大致如此。具体数学实现其他地方资料非常充足^^
2.卷积神经网络识别物体模仿人脑视觉机制,感光,提取边缘,提取形状,抽象概念。
3.卷积的话,其实是相关性计算,滑动窗口的滑动值叫stride,又称步长,滑动到边缘时停止,如果此时停止了,有些时候中间像素会参与多次卷积,因此我们需要往图像外填充一些像素,叫做填充,padding。
4.池化的话,通常是降低维度,过程比较简单,下采样。比如我们有个特征图是8*8,池化窗口是4*4,每4*4进行一次采样,就会得到2*2的池化特征图。通常池化特征有最大池化和平均池化,一个是取池化窗口在特征图内最大值作为池化特征图值,平均也同样。
下面我们来用pytorch实现一个MNIST手写数据库的识别。
import torch
from torchvision import datasets, transforms
from torch import nn, optim
import torch.nn.functional as F
import os
# 图像预处理
# 对数据进行标准化,先减去 mean(均值)再除以 std(标准差)。
# 在这里,(0.1307,) 是均值(mean)而 (0.3081,) 是标准差。
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
# 下载数据集
trainset = datasets.MNIST('data', train=True, download=True, transform=transform)
testset = datasets.MNIST('data', train=False, download=True, transform=transform)
# 构建一个LeNet
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
# 输入一张灰度图,输出6个特征图,第三个参数可以写为5,代表5*5的滤波器
self.c1 = nn.Conv2d(1, 6, (5, 5))
self.c3 = nn.Conv2d(6, 16, 5)
# 全连接层,将16个特征图大小为4*4连接到120个点,4*4与mnist数据集有关
self.fc1 = nn.Linear(16 * 4 * 4, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# c1卷积后,使用relu增加非线性拟合能力,然后池化,核为2*2
x = F.max_pool2d(F.relu(self.c1(x)), 2)
x = F.max_pool2d(F.relu(self.c3(x)), 2)
# 将x转化为1维向量,num_flat_features计算特征点数
x = x.view(-1, self.num_flat_features(x))
# 全连接fc1,fc2,fc3
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
# 获取张量x除了第一个维度(通常是batch size)之外的所有维度大小,将其存储在名为size的列表中
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features
CUDA = torch.cuda.is_available()
if CUDA:
lenet = LeNet().cuda()
else:
lenet = LeNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(lenet.parameters(), lr=1e-3, momentum=0.9)
# batch_size一次性加载的数据量,shuffle=True表示遍历不同批次的数据时打乱顺序,num_workers=2表示使用两个子进程加载数据
# 在windows中,num_workers 通常是0,可以避免问题
trainLoader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=0)
# 训练
def Train(model, criterion, optimizer, epochs=1):
for epoch in range(epochs):
running_loss = 0.0
for i, data in enumerate(trainLoader, 0):
inputs, labels = data
if CUDA:
inputs, labels = inputs.cuda(), labels.cuda()
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
# 调用优化器的step()方法来更新模型参数
optimizer.step()
running_loss += loss.item()
if i%1000 == 999:
print('[Epoch:%d, Batch:%5d] Loss: %.3f' % (epoch + 1, i + 1, running_loss / 1000))
running_loss = 0.0
print('finished')
# Train(lenet, criterion, optimizer, epochs = 2)
# 保存和加载
# torch.save(lenet, 'model.pkl')
# lenet = torch.load('model.pkl')
torch.save(lenet.state_dict(), 'model.pkl')
lenet.load_state_dict(torch.load('model.pkl'))
def load_param(model, path):
if os.path.exists(path):
model.load_state_dict(torch.load(path))
def save_param(model,path):
torch.save(model.state_dict(),path)
# 测试集测试
testset = datasets.MNIST('data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False,num_workers=0)
def Test(testloader, model):
correct = 0
total = 0
for data in testloader:
images, labels = data
if CUDA:
images = images.cuda()
labels = labels.cuda()
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy on the test set: %d %%' % (100 * correct / total))
load_param(lenet, 'model.pkl')
Train(lenet, criterion, optimizer, epochs = 2)
save_param(lenet,'model.pkl')
Test(testloader,lenet)