迁移学习
使用迁移学习来实现猫狗分类。
迁移学习的个人理解:就是把一个训练好的神经网络从中分开,在拼接到其他的神经网络上,在迁移的过程中,卷积模块的结构不会发生改变,并且权重也不会发生改变。迁移学习分为两种:1.预训练模式,迁移过来的权重作为新网络的初始权重,然后不断训练,改变参数的值。2.固定模式,迁移过来的网络结构和权重都不会改变,训练过程只针对全连接层,在反向传播过程中,在迁移模块停止,不改变迁移模块中的权重,只训练全连接层的参数。
Alexnet网络的结构
Alexnet网络是由五个卷积层和三个全连接层构成,其中最后的输出被送到1000维的softmax函数
在卷积层的第一、第二和第五层使用了最大池化函数,并且在卷积层的第一和第二层使用了标准化
LRN函数。在全连接层的前两层使用了Dropout函数,来解决神经网络的过拟合问题。Relu激活函
数应用在每个卷积层和全连接层。
第一个卷积层的输入为224×224×3的图像,对其使用96个大小为11×11×3、步长为4的卷积核来处
输入图像。第二个卷积层将第一个卷积层的输出(响应归一化以及池化)作为输入,并使用256个
卷积核处理图像,每个内核大小为5×5×48。第三个、第四个和第五个卷积层彼此连接而中间没有任
何池化或归一化层。第三个卷积层有384个卷积核,每个的大小为3×3×256,其输入为第二个卷积层
的输出。第四个卷积层有384个内核,每个卷积核大小为3×3×192。第五个卷积层有256个卷积核
每个核大小为3×3×192。全连接层各有4096个神经元。
Alexnet网络的特点
- 使用了两个GPU 。由于数据量较大,将网络分配给两个GPU,GPU之间交换数据只会在某些层上发生,不会在所有层上发生第三层从第二层获取数据时会跨GPU,而第四层从第三层获取数据时,只会在同一GPU上进行;
- 在数据预处理阶段,将图片随机截取224*224大小,以及他们的水平翻转形成 的图片作为总的样本。在测试时,截取5个224×224的图像块以及它们的水平映射作为样本
- 使用了Relu非线性激活函数,训练时间更快;
- .在Relu后使用了LRN作为归一化的方法,局部响应归一化;
- 使用了最大池化函数,并且重叠池化,池化核为3*3,步长为2,训练效果更好,传统的池化核在平移过程中,区域不会重合;
- 在全连接层的前两层使用Dropout,以概率p将某些神经元的值变为0,这些神经元不会参加正向和反向传播;但是对于不同的输入,构建的不同的网络模型使用相同的权重;测试时使用全部的神经元,但是神经元的值会减半
实现Alexnet网络框架
使用pytorch初步实现Alexnet网络结构
class Alexnet(nn.Module):
def __init__(self):
super(Alexnet, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(in_channels=3,
out_channels=96,
kernel_size=11,
stride=4),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(in_channels=96,
out_channels=256,
kernel_size=5,
stride=1,
padding=2),
nn.ReLU(),
nn.
nn.MaxPool2d(kernel_size=3, stride=2)
)
self.conv3 = nn.Sequential(
nn.Conv2d(in_channels=256,
out_channels=384,
kernel_size=3,
stride=1,
padding=1),
nn.ReLU()
)
self.conv4 = nn.Sequential(
nn.Conv2d(in_channels=384,
out_channels=384,
kernel_size=3,
stride=1,
padding=1),
nn.ReLU()
)
self.conv5 = nn.Sequential(
nn.Conv2d(in_channels=384,
out_channels=256,
kernel_size=3,
stride=1,
padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2)
)
self.fcalex = nn.Sequential(
nn.Linear(256*6*6, 4096),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 1000)
)
Alexnet实现猫狗分类
import os
import torch
import torch.nn as nn
from torchvision.datasets import ImageFolder
from torch import optim
from torchvision import transforms, models
from torch.autograd import Variable
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
transform = transforms.Compose([
transforms.Resize(size=(227, 227)),
transforms.RandomRotation(20),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
normalize
])
train_dataset = ImageFolder(root=’./data/train’, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, shuffle=True)
test_dataset = ImageFolder(root=’./data/test’, transform=transform)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=16, shuffle=True)
alexnet = models.alexnet(pretrained=True)
class Alexnetl(nn.Module):
def init(self):
super(Alexnetl, self).init()
self.features = alexnet.features
fc1 = nn.Linear(9216, 4096)
fc1.bias = alexnet.classifier[1].bias
fc1.weight = alexnet.classifier[1].weight
fc2 = nn.Linear(4096, 4096)
fc2.bias = alexnet.classifier[4].bias
fc2.weight = alexnet.classifier[4].weight
fc31 = nn.Linear(4096, 2)
self.classifier = nn.Sequential(
nn.Dropout(),
fc1,
nn.ReLU(inplace=True),
nn.Dropout(),
fc2,
nn.ReLU(inplace=True),
fc31
)
def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), 256 * 6 * 6)
x = self.classifier(x)
return x
net = Alexnetl()
“”"
pre_para = alexnet.state_dict()
net_para = net.state_dict()
pre_para = {k: v for k, v in pre_para.items() if k in net_para}
net_para.update(pre_para)
net.load_state_dict(net_para)
print(net)
“”"
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001)
n_epoch = 1
for epoch in range(n_epoch):
for i , data in enumerate(train_loader):
images, labels = data
images, labels = Variable(images), Variable(labels)
output = net(images)
loss = criterion(output, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
i += 1
if i % 10 == 0:
print('epoch:{}, loss: {:.3f}'.format(epoch, loss))
correct = 0
total = 0
for i,data in enumerate(test_loader):
i += 1
images, labels = data
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
if i % 10 ==0:
Accuracy = correct / total
print(‘Accuracy of test is {:.3f}’.format(Accuracy))
结果:
vggnet网络的结构
相对于Alexnet网络来看,Vgg网络具有 更深的网络结构,一般为16层到19层,其中最后三层为全连接层。卷积时使用了多个小的卷积核堆叠起来,卷积核的大小都为33,步长为1,而且并不是在每个卷积后都跟有池化操作,整个网络包含有五个池化层,采用最大池化操作,池化核大小为22,步长为1。
vgg网络结构图:
vgg网络的特点:
1.数据预处理只有一个操作,在训练阶段就是每一个的值减去RGB的均值,在测试阶段就是重新定义图片的大小。
2.卷积过程中,使用多个小的卷积核(33)堆叠来代替大的卷积核,其中2个33的可以代替1个55的卷积核。在感受野相同时,卷积核越小,所需要的参数也越少。这样操作使用更少参数,同时使用多次relu函数,有更强的分辨能力。
为什么2个33的卷积核可以代替1个55的卷积核:(strid=1)
55的感受野:感受野为5,
第1个33的感受野:第一层卷积层的输出特征图像素的感受野的大小等于滤波器的大小,就是3,第二个33的感受野:(3-1)1+3=5。感受野相同,或者计算这两者的feature map也是相同的。
参数减少:(通道为3)
2个33卷积的参数:3332=54
1个55卷积的参数:5531=75
3.在全连接的前两层也使用了Dropout,概率为p将某些神经元置零,这些神经元不会参加正向和反向传播;但是对于不同的输入,构建的不同的网络模型使用相同的权重;测试时使用全部的神经元,但是神经元的值会减半
4.在测试阶段,将最后三层全连接层替换为三个卷积层,卷积核大小分别为77,11 ,11,因为卷积核大小为1*1,不会影响输出维度,同时又增加了relu进行非线性处理
实现Vgg网络框架
部分代码:
self.conv1_1 = nn.Conv2d(3, 64, 3)
self.conv1_2 = nn.Conv2d(64, 64, 3, padding=(1, 1))
self.maxpool1 = nn.MaxPool2d((2, 2), padding=(1, 1))
self.conv2_1 = nn.Conv2d(64, 128, 3) # 128 * 110 * 110
self.conv2_2 = nn.Conv2d(128, 128, 3, padding=(1, 1))
self.maxpool2 = nn.MaxPool2d((2, 2), padding=(1, 1))
out = self.conv1_1(x)
out = F.relu(out)
out = self.conv1_2(out)
out = F.relu(out)
out = self.maxpool1(out)
Vgg实现猫狗分类
将vgg16作为预训练好的模型迁移过来,参数保持不变,修改全连接层,将最后一层的输出改为2,对新的模型进行训练。
实现代码:
import torch
import torch.nn as nn
from torchvision.datasets import ImageFolder
from torch import optim
from torchvision import transforms, models
from torch.autograd import Variable
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
transform = transforms.Compose([transforms.CenterCrop(224),
transforms.ToTensor(),
normalize])
train_dataset = ImageFolder(root='./data/train', transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, shuffle=True)
test_dataset = ImageFolder(root='./data/test', transform=transform)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=16, shuffle=True)
model = models.vgg16(pretrained=True)
model.classifier._modules['6'] = nn.Linear(4096, 2)
loss_func = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001)
for epoch in range(2):
total1 = 0
correct1 = 0
model.parameters()
for i, data in enumerate(train_loader):
i += 1
images, labels = data
images = Variable(images)
labels = Variable(labels)
optimizer.zero_grad()
output = model(images)
loss = loss_func(output, labels)
loss.backward()
optimizer.step()
_, predicted = torch.max(output.data, 1)
total1 += labels.size(0)
correct1 += (predicted == labels).sum()
if i % 5 == 0:
print('train loss is : ', loss)
acc = correct1/total1
print(' train acc is :', acc)
correct = 0
total = 0
for data in test_loader:
images, labels = data
outputs = model(Variable(images))
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
print('Accuracy of network test images: %d %%' % (100 * correct / total))
vgg的结果:
Alexnet和vgg比较:
vgg网络具有更深的网络结构,使用的卷积核和池化核都是统一大小,两者对于数据的预处理也是不同的。可以看出,在结果方面,vgg网络的评估结果更好,损失更低,精确度更高,但是vgg网络运行时间更久,由于网络更深,参数更多,耗费了更多的资源,占据了更多的内存。
来源:https://blog.csdn.net/weixin_44428467/article/details/100572774