卷积神经网络
BP算法
在介绍卷积神经网络之前,我们了解一下传统神经网络的问题,神经网络隐层在进行传递的时候会遇到一个计算问题,因为每个隐层都会有自己的参数值,当隐层增加的时候,参数值的个数以及需要的计算量是越来越多的。
所以提出了BP算法,用来解决参数值过多难以维护修改的问题。
BP算法是一种按照误差逆向传播算法训练的多层前馈神经网络。
下面图是引用的别人博客中的图片。
神经网络是有前向传播和反向传播的,我们在前向传播的时候输出一个求得的损失值,如果损失值在给定的误差范围内,那么就继续原定的流程,继续进行下一层的运算;如果不在损失范围内,那么我们就降误差分摊给各层的单元内,修改原先的参数。
BP算法其实也是一种贪心算法的应用,我们只能求得局部的最优解,并不能保证当前的修改,是不是对后续的操作有正向的优化。
卷积神经网络
既然BP算法解决了参数太多难以修改的问题,那么能不能减少参数的值,这样计算就比较简便了,这就是CNN的其中一个优点:权值共享。这里的权值共享不是整个流程的权值一样,而是一次epoch的参数共享。
卷积层
用它来进行特征提取,就是用卷积核进行计算,也是为了减少计算量和特征值,
激活函数
为什么使用激活函数,那是因为卷积运算也是一种线性运算,线性运算的问题就是表达力不够,所以需要加入非线性映射。
-
如果输入变化很小,导致输出结构发生截然不同的结果,这种情况是我们不希望看到的,为了模拟更细微的变化,输入和输出数值不只是0到1,可以是0和1之间的任何数,
这句话字面的意思很容易理解,但是在具体处理图像的时候是什么情况呢?我们知道在神经网络中,对于图像,我们主要采用了卷积的方式来处理,也就是对每个像素点赋予一个权值,这个操作显然就是线性的。但是对于我们样本来说,不一定是线性可分的,为了解决这个问题,我们可以进行线性变化,或者我们引入非线性因素,解决线性模型所不能解决的问题。 -
这里插一句,来比较一下上面的那些激活函数,因为神经网络的数学基础是处处可微的,所以选取的激活函数要能保证数据输入与输出也是可微的,运算特征是不断进行循环计算,所以在每代循环过程中,每个神经元的值也是在不断变化的。
这就导致了tanh特征相差明显时的效果会很好,在循环过程中会不断扩大特征效果显示出来,但有是,在特征相差比较复杂或是相差不是特别大时,需要更细微的分类判断的时候,sigmoid效果就好了。 -
还有一个东西要注意,sigmoid 和 tanh作为激活函数的话,一定要注意一定要对 input 进行归一话,否则激活后的值都会进入平坦区,使隐层的输出全部趋同,但是 ReLU 并不需要输入归一化来防止它们达到饱和。
-
构建稀疏矩阵,也就是稀疏性,这个特性可以去除数据中的冗余,最大可能保留数据的特征,也就是大多数为0的稀疏矩阵来表示。其实这个特性主要是对于Relu,它就是取的max(0,x),因为神经网络是不断反复计算,实际上变成了它在尝试不断试探如何用一个大多数为0的矩阵来尝试表达数据特征,结果因为稀疏特性的存在,反而这种方法变得运算得又快效果又好了。所以我们可以看到目前大部分的卷积神经网络中,基本上都是采用了ReLU 函数。
线性函数性质
(1)非线性。线性激活层对于深层神经网络没有作用,因为其作用以后仍然是输入的各种线性变换。。
(2)连续可微。梯度下降法的要求。
(3)范围最好不饱和,当有饱和的区间段时,若系统优化进入到该段,梯度近似为0,网络的学习就会停止。
(4)单调性,当激活函数是单调时,单层神经网络的误差函数是凸的,好优化。
(5)在原点处近似线性,这样当权值初始化为接近0的随机值时,网络可以学习的较快,不用可以调节网络的初始值。
目前常用的激活函数都只拥有上述性质的部分,没有一个拥有全部的~~
真实使用的时候最常用的还是ReLU函数,注意学习率的设置以及死亡节点所占的比例即可
池化层
对输入的特征图进行压缩,一方面使特征图变小,简化网络计算复杂度;一方面进行特征压缩,提取主要特征。
池化操作一般有两种,一种是Avy Pooling,一种是max Pooling,一个是求平均值,一个是取出里面的最大值
在卷积神经网络中,我们经常会碰到池化操作,而池化层往往在卷积层后面,通过池化来降低卷积层输出的特征向量,同时改善结果(不易出现过拟合)。
为什么可以通过降低维度呢?
因为图像具有一种“静态性”的属性,这也就意味着在一个图像区域有用的特征极有可能在另一个区域同样适用。因此,为了描述大的图像,一个很自然的想法就是对不同位置的特征进行聚合统计,例如,人们可以计算图像一个区域上的某个特定特征的平均值 (或最大值)来代表这个区域的特征。
全连接层
连接所有的特征,将输出值送给分类器(如softmax分类器)。
工作过程。
首先进行卷积运算
首先对输入的1 * 28 * 28图像张量,进行一次卷积运算,其中1是指通道数channel,两个28分别是宽和高。
下面讲解下如何从28降到24的。
- padding表示的是填充添加到输入的所有四个侧面的格子数。 默认值:0
- dilation表示内核元素之间的距离 默认是1。
- kernel_size[0]指内核的高 [1] 指内核的宽
- 顺带说一下stride参数的含义:就是步长,每次滑动窗口移动的长度。
这样其实在不改变以上三个参数的情况下,最终的结果就是输入的高减4。
然后进行一次采样
选择的采样是2*2的,也就是从上面4个通道中选取一半的张量进行后续操作。简简单单的就是用输入的宽和高分别除以采样的长和高。
后续就是重复进行卷积和采样。
最后进行一次全链接:将输出的张量展开成一个一维的向量,方便后续的计算。
卷积过程代码实现
import torch
# 输入输出的通道数
in_channels, out_channels = 5, 10
width, height = 100, 100
# 内核的维数
kernel_size = 3
bitch_size = 1
input = torch.randn(batch_size,
in_channels,
width,
height)
# 卷
conv_layer = torch.nn.Conv2d(in_channels,
out_channels,
kernel_size = kernel_size)
output = conv_layer(input)
print(input.shape)
print(output.shape)
print(conv_layer.weight.shape)
完整代码
课程代码
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
batch_size = 64
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)
self.pooling = torch.nn.MaxPool2d(2)
self.fc = torch.nn.Linear(320, 10)
def forward(self, x):
batch_size = x.size(0)
x = F.relu(self.pooling(self.conv1(x)))
x = F.relu(self.pooling(self.conv2(x)))
x = x.view(batch_size, -1)
x = self.fc(x)
return x
model = Net()
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d, %5d] loss: %.3f' % (epoch+1, batch_idx+1, running_loss/300))
running_loss = 0.0
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
images, labels = data
outputs = model(images)
_, predicted = torch.max(outputs.data, dim=1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('accuracy on test set: %d %% ' % (100*correct/total))
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test()
作业
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
batch_size = 64
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
train_dataset = datasets.MNIST(root='./datasets/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='./datasets/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=3)
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=3, padding = 1)
self.conv3 = torch.nn.Conv2d(20, 30, kernel_size=3)
self.pooling = torch.nn.MaxPool2d(2)
self.fc = torch.nn.Linear(120,60)
self.fc1 = torch.nn.Linear(60,30)
self.fc2 = torch.nn.Linear(30,10)
def forward(self, x):
batch_size = x.size(0)
x = F.relu(self.pooling(self.conv1(x)))
x = F.relu(self.pooling(self.conv2(x)))
x = F.relu(self.pooling(self.conv3(x)))
x = x.view(batch_size, -1)
x = self.fc(x)
x = self.fc1(x)
x = self.fc2(x)
return x
model = Net()
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d, %5d] loss: %.3f' % (epoch+1, batch_idx+1, running_loss/300))
running_loss = 0.0
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
images, labels = data
outputs = model(images)
_, predicted = torch.max(outputs.data, dim=1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('accuracy on test set: %d %% ' % (100*correct/total))
if __name__ == '__main__':
for epoch in range(2):
train(epoch)
test()