1算法思想 卷积神经网络通过所设计的卷积核与图像进行卷积操作,提取图像中的某些特征。通过卷积网络层数的加深,提取的特征从局部到整体,从而对物体进行识别。2算法推导2。1边缘特征检测示例 图1。图像边缘检测 假如有一张图像,想让计算机搞清楚图片上有什么物体,可以做的事情是检测图像的水平边缘与垂直边缘。 (1)卷积操作 如图1所示,是一个66的灰度图像,构造一个33的矩阵,在卷积神经网络中,通常称为filter(过滤器),对66的图像进行卷积操作得到44的矩阵。 图2。卷积操作 如图2所示,33的filter与66的灰度图像左上角33区域进行卷积31001(1)11508(1)21702(1)5,从而得到44左上角的5。 (2)边缘提取 图3。垂直边缘提取 为什么这种卷积操作可以得到图像的边缘? 如图3所示,原图是66的灰度图像,10的部分为亮区域,0的部分为暗区域。从100为垂直边缘。用一个33的过滤器,对图像进行卷积操作,得到图像中间亮,两边暗。亮暗交接处为边缘。 (3)过滤器类型 图4。垂直过滤器与水平过滤器 通过图4的垂直过滤器与水平过滤器可实现垂直边缘与水平边缘检测。 图5。过滤器类型 图5列出了一些常用的过滤器,如sobel算子,scharr算子等。在卷积神经网络中,把这些过滤器当成我们要学习的参数,卷积网络训练的目标就是去理解过滤器的参数。2。2边缘填充padding 图6。padding示意图 (1)为什么进行padding? 按照上述的描述,图片每经过一次卷积运算,会存在以下两个问题:图片会缩小导致无法进行深层卷积运算;原始图片边缘信息对输出贡献得少,输出图片丢失边缘信息。 (2)怎样进行padding?假设输入的图片大小: 过滤器的大小: 两个水平与垂直边缘padding大小: 则经过卷积操作的输出: 2。3卷积步长stride 图7。卷积步长为2对于input77,filter33,stride2,padding0;经卷积操作输出:;通用表示:,表示向下取整。2。4彩色图像的卷积 以上讲述的卷积都是灰度图像的,如果想要在RGB图像上进行卷积,过滤器的大小不再是33,而是333,最后的3对应为通道数(channels)。卷积生成图像中,每个位置的像素值,为333的过滤器与图像相应位置相乘累加。如图8所示,过滤器依次在RGB图像上滑动,最终生成的图像大小为44。 图8。单一filter彩色图像卷积 另外一个问题是,如果我们在不仅仅在图像总检测一种类型的特征,而是要同时检测垂直边缘、水平边缘、45度边缘等,也就是多个过滤器的问题。如果有两个过滤器,最终生成图像为442的立方体,这里的2来源于我们采用了2个过滤器。 图9。多个filter彩色图像卷积 写成通用的形式:输入维度: 每个滤波器的维度: 权重维度: 偏置维度: 输出维度: 其中: 2。5池化层Pooling 图10。Maxpooling示意图 在卷积神经网络中,除了使用卷积层外,还使用池化层来缩减模型大小,提高计算速度。池化层分为最大池化层(maxpooling)与平均池化层(averagepooling)。池化层中的maxpooling是求每个过滤器滑动区域内的最大值;averagepooling是求每个过滤器滑动区域内的平均值。经过padding后的输出: 一般情况下padding0,输出表示: 2。6简单卷积神经网络示例LeNet5 LeNet(LeNet5)由两个卷积层和三个全连接层构成。这两卷积层的卷积核均为55,第一个卷积层的输出通道为6,第二卷积层的输出通道为16。每个池化层窗口的大小为22,步长为2。三个全连接层分别有120、84和10个输出。3算法实现 (1)下载数据集importtorchimporttorch。nnasnnimporttorch。nn。functionalasFimporttorch。optimasoptimfromtorchvisionimportdatasets,transformsimporttimefrommatplotlibimportpyplotaspltpiplinetraintransforms。Compose(〔随机旋转图片transforms。RandomHorizontalFlip(),將圖片尺寸resize到32x32transforms。Resize((32,32)),將圖片轉化為Tensor格式transforms。ToTensor(),正則化(當模型出現過擬合的情況時,用來降低模型的複雜度)transforms。Normalize((0。1307,),(0。3081,))〕)piplinetesttransforms。Compose(〔將圖片尺寸resize到32x32transforms。Resize((32,32)),transforms。ToTensor(),transforms。Normalize((0。1307,),(0。3081,))〕)下載数据集trainsetdatasets。MNIST(root。data,trainTrue,downloadTrue,transformpiplinetrain)testsetdatasets。MNIST(root。data,trainFalse,downloadTrue,transformpiplinetest)載入数据集trainloadertorch。utils。data。DataLoader(trainset,batchsize64,shuffleTrue)testloadertorch。utils。data。DataLoader(testset,batchsize32,shuffleFalse) (2)搭建LeNet5网络结构,并确定前向传递过程classLeNet(nn。Module):definit(self):super(LeNet,self)。init()self。conv1nn。Conv2d(1,6,5)self。relunn。ReLU()self。maxpool1nn。MaxPool2d(2,2)self。conv2nn。Conv2d(6,16,5)self。maxpool2nn。MaxPool2d(2,2)self。fc1nn。Linear(1655,120)self。fc2nn。Linear(120,84)self。fc3nn。Linear(84,10)defforward(self,x):xself。conv1(x)xself。relu(x)xself。maxpool1(x)xself。conv2(x)xself。maxpool2(x)xx。view(1,1655)xF。relu(self。fc1(x))xF。relu(self。fc2(x))xself。fc3(x)outputF。logsoftmax(x,dim1)returnoutput (3)将定义好的网络结构部署至CPUGPU上,并定义优化器建立模型,部署gpu或cpudevicetorch。device(cudaiftorch。cuda。isavailable()elsecpu)modelLeNet()。to(device)定义优化器optimizeroptim。Adam(model。parameters(),lr0。001) (4)定义训练过程deftrainrunner(model,device,trainloader,optimizer,epoch):訓練模型,啟用BatchNormalization和Dropout,將BatchNormalization和Dropout置為Truemodel。train()total0correct0。0enumerate迭代已載入的数据集fori,datainenumerate(trainloader,0):inputs,labelsdata把模型部署到device上inputs,labelsinputs。to(device),labels。to(device)初始化梯度optimizer。zerograd()儲存訓練結果outputsmodel(inputs)計算損失和多分類情況通常使用crossentropy(交叉熵損失函式),而對於二分類問題,通常使用sigmoidlossF。crossentropy(outputs,labels)获取最大概率的預測結果dim1表示返回每一行的最大值對應的列下標predictoutputs。argmax(dim1)totallabels。size(0)correct(predictlabels)。sum()。item()反向傳播loss。backward()更新参数optimizer。step()ifi10000:loss。item()表示當前loss的數值print(TrainEpoch{}Loss:{:。6f},accuracy:{:。6f}。format(epoch,loss。item(),100(correcttotal)))Loss。append(loss。item())Accuracy。append(correcttotal)returnloss。item(),correcttotal (5)定义测试过程deftestrunner(model,device,testloader):模型驗證,必須要寫,否則只要有輸入数据,即使不訓練,它也會改變權值model。eval()統計模型正確率,設定初始值correct0。0testloss0。0total0torch。nograd將不會計算梯度,也不會進行反向傳播withtorch。nograd():fordata,labelintestloader:data,labeldata。to(device),label。to(device)outputmodel(data)testlossF。crossentropy(output,label)。item()predictoutput。argmax(dim1)計算正確數量totallabel。size(0)correct(predictlabel)。sum()。item()計算損失值print(testavarageloss:{:。6f},accuracy:{:。6f}。format(testlosstotal,100(correcttotal))) (6)执行训练与测试ifnamemain:epoch5Loss〔〕Accuracy〔〕forepochinrange(1,epoch1):print(starttime,time。strftime(YmdH:M:S,time。localtime(time。time())))loss,acctrainrunner(model,device,trainloader,optimizer,epoch)Loss。append(loss)Accuracy。append(acc)testrunner(model,device,testloader)print(endtime:,time。strftime(YmdH:M:S,time。localtime(time。time())),)print(model)torch。save(model,。modelsmodelmnist。pth)儲存模型print(FinishedTraining)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,。modelsmodelmnist。pth)儲存模型