卷积神经网络通过所设计的卷积核与图像进行卷积操作,提取图像中的某些特征。通过卷积网络层数的加深,提取的特征从局部到整体,从而对物体进行识别。
假如有一张图像,想让计算机搞清楚图片上有什么物体,可以做的事情是检测图像的水平边缘与垂直边缘。
(1)卷积操作
如图1所示,是一个6*6的灰度图像,构造一个3*3的矩阵,在卷积神经网络中,通常称为filter(过滤器),对6*6的图像进行卷积操作得到4*4的矩阵。
如图2所示,3*3的filter与6*6的灰度图像左上角3*3区域进行卷积3*1+0*0+1*(-1)+1*1+5*0+8*(-1)+2*1+7*0+2*(-1)=-5,从而得到4*4左上角的-5。
(2)边缘提取
为什么这种卷积操作可以得到图像的边缘?
如图3所示,原图是6*6的灰度图像,10的部分为亮区域,0的部分为暗区域。从10->0为垂直边缘。用一个3*3的过滤器,对图像进行卷积操作,得到图像中间亮,两边暗。亮暗交接处为边缘。
(3)过滤器类型
通过图4的垂直过滤器与水平过滤器可实现垂直边缘与水平边缘检测。
图5列出了一些常用的过滤器,如sobel算子,scharr算子等。在卷积神经网络中,把这些过滤器当成我们要学习的参数,卷积网络训练的目标就是去理解过滤器的参数。
(1)为什么进行padding?
按照上述的描述,图片每经过一次卷积运算,会存在以下两个问题:
(2)怎样进行padding?
以上讲述的卷积都是灰度图像的,如果想要在RGB图像上进行卷积,过滤器的大小不再是3*3,而是3*3*3,最后的3对应为通道数(channels)。卷积生成图像中,每个位置的像素值,为3*3*3的过滤器与图像相应位置相乘累加。如图8所示,过滤器依次在RGB图像上滑动,最终生成的图像大小为4*4。
另外一个问题是,如果我们在不仅仅在图像总检测一种类型的特征,而是要同时检测垂直边缘、水平边缘、45度边缘等,也就是多个过滤器的问题。如果有两个过滤器,最终生成图像为4*4*2的立方体,这里的2来源于我们采用了2个过滤器。
写成通用的形式:
在卷积神经网络中,除了使用卷积层外,还使用池化层来缩减模型大小,提高计算速度。池化层分为最大池化层(max pooling)与平均池化层(average pooling)。池化层中的max pooling是求每个过滤器滑动区域内的最大值;average pooling是求每个过滤器滑动区域内的平均值。
LeNet(LeNet-5)由两个卷积层和三个全连接层构成。
(1)下载数据集
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import time
from matplotlib import pyplot as plt
pipline_train = transforms.Compose([
#随机旋转图片
transforms.RandomHorizontalFlip(),
#將圖片尺寸resize到32x32
transforms.Resize((32,32)),
#將圖片轉化為Tensor格式
transforms.ToTensor(),
#正則化(當模型出現過擬合的情況時,用來降低模型的複雜度)
transforms.Normalize((0.1307,),(0.3081,))
])
pipline_test = transforms.Compose([
#將圖片尺寸resize到32x32
transforms.Resize((32,32)),
transforms.ToTensor(),
transforms.Normalize((0.1307,),(0.3081,))
])
#下載数据集
train_set = datasets.MNIST(root="./data", train=True, download=True, transform=pipline_train)
test_set = datasets.MNIST(root="./data", train=False, download=True, transform=pipline_test)
#載入数据集
trainloader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
testloader = torch.utils.data.DataLoader(test_set, batch_size=32, shuffle=False)
(2)搭建LeNet-5网络结构,并确定前向传递过程
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(1, 6, 5)
self.relu = nn.ReLU()
self.maxpool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.maxpool2 = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.conv1(x)
x = self.relu(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = x.view(-1, 16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
output = F.log_softmax(x, dim=1)
return output
(3)将定义好的网络结构部署至CPU/GPU上,并定义优化器
#建立模型,部署gpu或cpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = LeNet().to(device)
#定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)
(4)定义训练过程
def train_runner(model, device, trainloader, optimizer, epoch):
#訓練模型, 啟用 BatchNormalization 和 Dropout, 將BatchNormalization和Dropout置為True
model.train()
total = 0
correct =0.0
#enumerate迭代已載入的数据集
for i, data in enumerate(trainloader, 0):
inputs, labels = data
#把模型部署到device上
inputs, labels = inputs.to(device), labels.to(device)
#初始化梯度
optimizer.zero_grad()
#儲存訓練結果
outputs = model(inputs)
#計算損失和
#多分類情況通常使用cross_entropy(交叉熵損失函式), 而對於二分類問題, 通常使用sigmoid
loss = F.cross_entropy(outputs, labels)
#获取最大概率的預測結果
#dim=1表示返回每一行的最大值對應的列下標
predict = outputs.argmax(dim=1)
total += labels.size(0)
correct += (predict == labels).sum().item()
#反向傳播
loss.backward()
#更新参数
optimizer.step()
if i % 1000 == 0:
#loss.item()表示當前loss的數值
print("Train Epoch{} Loss: {:.6f}, accuracy: {:.6f}%".format(epoch, loss.item(), 100*(correct/total)))
Loss.append(loss.item())
Accuracy.append(correct/total)
return loss.item(), correct/total
(5)定义测试过程
def test_runner(model, device, testloader):
#模型驗證, 必須要寫, 否則只要有輸入数据, 即使不訓練, 它也會改變權值
model.eval()
#統計模型正確率, 設定初始值
correct = 0.0
test_loss = 0.0
total = 0
#torch.no_grad將不會計算梯度, 也不會進行反向傳播
with torch.no_grad():
for data, label in testloader:
data, label = data.to(device), label.to(device)
output = model(data)
test_loss += F.cross_entropy(output, label).item()
predict = output.argmax(dim=1)
#計算正確數量
total += label.size(0)
correct += (predict == label).sum().item()
#計算損失值
print("test_avarage_loss: {:.6f}, accuracy: {:.6f}%".format(test_loss/total, 100*(correct/total)))
(6)执行训练与测试
if __name__=="__main__":
epoch = 5
Loss = []
Accuracy = []
for epoch in range(1, epoch + 1):
print("start_time", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
loss, acc = train_runner(model, device, trainloader, optimizer, epoch)
Loss.append(loss)
Accuracy.append(acc)
test_runner(model, device, testloader)
print("end_time: ", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), '
')
print(model)
torch.save(model, './models/model-mnist.pth') #儲存模型
print('Finished Training')
plt.subplot(2, 1, 1)
plt.plot(Loss)
plt.title('Loss')
plt.show()
plt.subplot(2, 1, 2)
plt.plot(Accuracy)
plt.title('Accuracy')
plt.show()
(7)保存网络模型
print(model)
torch.save(model, './models/model-mnist.pth') #儲存模型
页面更新:2024-03-28
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号