郑州网站推广公司电话,马鞍山住房和城乡建设局网站,知名企业网站建设,wordpress area53文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数#xff1a;2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 4 MobileNetV2网络5 损失函数softmax 交叉熵5.1 softmax函数5.2 交叉熵损失函数 6 优化器SGD7 学… 文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 4 MobileNetV2网络5 损失函数softmax 交叉熵5.1 softmax函数5.2 交叉熵损失函数 6 优化器SGD7 学习率衰减策略6 最后 0 前言 优质竞赛项目系列今天要分享的是 **基于深度学习的昆虫识别算法研究与实现 **
该项目较为新颖适合作为竞赛课题方向学长非常推荐
学长这里给一个题目综合评分(每项满分5分)
难度系数3分工作量4分创新点4分 更多资料, 项目分享
https://gitee.com/dancheng-senior/postgraduate
1 课题背景
中国是农业大国在传统的农业生产中经常会受到病虫害问题的困扰。在解决病虫害问题时第一步是识别昆虫。在传统的昆虫识别方法中昆虫专家根据专业知识观察昆虫的外部特征并对照相关的昆虫图鉴进行识别费时费力。如今传统的昆虫识别方法逐渐被昆虫图像识别技术代替。目前常用的昆虫识别技术有图像识别法、微波雷达检测法、生物光子检测法、取样检测法、近红外及高光谱法、声测法等。近年来随着人工智能的迅速发展深度学习技术在处理自然语言、机器视觉等方面取得了很多成果随着深度学习的发展已经有研究人员开始将深度学习技术应用于昆虫的图像识别。文章旨在利用基于深度学习的图像识别技术解决昆虫识别问题希望能给现实生活中的病虫害识别问题提供新的解决问题的思路。
2 具体实现 3 数据收集和处理
数据是深度学习的基石 数据的主要来源有: 百度图片, 必应图片, 新浪微博, 百度贴吧, 新浪博客和一些专业的昆虫网站等 爬虫爬取的图像的质量参差不齐, 标签可能有误, 且存在重复文件, 因此必须清洗。清洗方法包括自动化清洗, 半自动化清洗和手工清洗。 自动化清洗包括:
滤除小尺寸图像.滤除宽高比很大或很小的图像.滤除灰度图像.图像去重: 根据图像感知哈希.
半自动化清洗包括:
图像级别的清洗: 利用预先训练的昆虫/非昆虫图像分类器对图像文件进行打分, 非昆虫图像应该有较低的得分; 利用前一阶段的昆虫分类器对图像文件 (每个文件都有一个预标类别) 进行预测, 取预标类别的概率值为得分, 不属于原预标类别的图像应该有较低的得分. 可以设置阈值, 滤除很低得分的文件; 另外利用得分对图像文件进行重命名, 并在资源管理器选择按文件名排序, 以便于后续手工清洗掉非昆虫图像和不是预标类别的图像.类级别的清洗
手工清洗: 人工判断文件夹下图像是否属于文件夹名所标称的物种, 这需要相关的昆虫学专业知识, 是最耗时且枯燥的环节。
3 卷积神经网络
卷积神经网络Convolutional Neural NetwoekCNN是一种前馈神经网络它的人工神经元可以局部响应周围的神经元每个神经元都接收一些输入并做一些点积计算。它通常包含卷积层、激活层、池化层、全连接层。
2.1卷积层
卷积核相当于一个滑动窗口示意图中x大小的卷积核依次划过x大小的输入数据中的对应区域并与卷积核滑过区域做矩阵点乘将所得结果依次填入对应位置即可得到右侧x尺寸的卷积特征图例如划到右上角x所圈区域时将进行xxxxxxxxxx的计算操作并将得到的数值填充到卷积特征的右上角。 2.2 池化层
池化操作又称为降采样提取网络主要特征可以在达到空间不变性的效果同时有效地减少网络参数因而简化网络计算复杂度防止过拟合现象的出现。在实际操作中经常使用最大池化或平均池化两种方式如下图所示。虽然池化操作可以有效的降低参数数量但过度池化也会导致一些图片细节的丢失因此在搭建网络时要根据实际情况来调整池化操作。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img- UTsB7AhE-1658995487680)
2.3 激活函数
激活函数大致分为两种在卷积神经网络的发展前期使用较为传统的饱和激活函数主要包括sigmoid函数、tanh函数等随着神经网络的发展研宄者们发现了饱和激活函数的弱点并针对其存在的潜在问题研宄了非饱和激活函数其主要含有ReLU函数及其函数变体
2.4 全连接层
在整个网络结构中起到“分类器”的作用经过前面卷积层、池化层、激活函数层之后网络己经对输入图片的原始数据进行特征提取并将其映射到隐藏特征空间全连接层将负责将学习到的特征从隐藏特征空间映射到样本标记空间一般包括提取到的特征在图片上的位置信息以及特征所属类别概率等。将隐藏特征空间的信息具象化也是图像处理当中的重要一环。
2.5 使用tensorflow中keras模块实现卷积神经网络
class CNN(tf.keras.Model):def __init__(self):super().__init__()self.conv1 tf.keras.layers.Conv2D(filters32, # 卷积层神经元卷积核数目kernel_size[5, 5], # 感受野大小paddingsame, # padding策略vaild 或 sameactivationtf.nn.relu # 激活函数)self.pool1 tf.keras.layers.MaxPool2D(pool_size[2, 2], strides2)self.conv2 tf.keras.layers.Conv2D(filters64,kernel_size[5, 5],paddingsame,activationtf.nn.relu)self.pool2 tf.keras.layers.MaxPool2D(pool_size[2, 2], strides2)self.flatten tf.keras.layers.Reshape(target_shape(7 * 7 * 64,))self.dense1 tf.keras.layers.Dense(units1024, activationtf.nn.relu)self.dense2 tf.keras.layers.Dense(units10)def call(self, inputs):x self.conv1(inputs) # [batch_size, 28, 28, 32]x self.pool1(x) # [batch_size, 14, 14, 32]x self.conv2(x) # [batch_size, 14, 14, 64]x self.pool2(x) # [batch_size, 7, 7, 64]x self.flatten(x) # [batch_size, 7 * 7 * 64]x self.dense1(x) # [batch_size, 1024]x self.dense2(x) # [batch_size, 10]output tf.nn.softmax(x)return output4 MobileNetV2网络
简介
MobileNet网络是Google最近提出的一种小巧而高效的CNN模型其在accuracy和latency之间做了折中。
主要改进点
相对于MobileNetV1MobileNetV2 主要改进点
引入倒残差结构先升维再降维增强梯度的传播显著减少推理期间所需的内存占用Inverted Residuals去掉 Narrow layer(low dimension or depth) 后的 ReLU保留特征多样性增强网络的表达能力Linear Bottlenecks网络为全卷积使得模型可以适应不同尺寸的图像使用 RELU6最高输出为 6激活函数使得模型在低精度计算下具有更强的鲁棒性MobileNetV2 Inverted residual block 如下所示若需要下采样可在 DW 时采用步长为 2 的卷积小网络使用小的扩张系数expansion factor大网络使用大一点的扩张系数expansion factor推荐是5~10论文中 t 6 t 6t6
倒残差结构Inverted residual block
ResNet的Bottleneck结构是降维-卷积-升维是两边细中间粗
而MobileNetV2是先升维6倍- 卷积 - 降维是沙漏形。 区别于MobileNetV1, MobileNetV2的卷积结构如下 因为DW卷积不改变通道数所以如果上一层的通道数很低时DW只能在低维空间提取特征效果不好。所以V2版本在DW前面加了一层PW用来升维。
同时V2去除了第二个PW的激活函数改用线性激活因为激活函数在高维空间能够有效地增加非线性但在低维空间时会破坏特征。由于第二个PW主要的功能是降维所以不宜再加ReLU6。 tensorflow相关实现代码
import tensorflow as tfimport numpy as npfrom tensorflow.keras import layers, Sequential, Modelclass ConvBNReLU(layers.Layer):def __init__(self, out_channel, kernel_size3, strides1, **kwargs):super(ConvBNReLU, self).__init__(**kwargs)self.conv layers.Conv2D(filtersout_channel, kernel_sizekernel_size, stridesstrides, paddingSAME, use_biasFalse,nameConv2d)self.bn layers.BatchNormalization(momentum0.9, epsilon1e-5, nameBatchNorm)self.activation layers.ReLU(max_value6.0) # ReLU6def call(self, inputs, trainingFalse, **kargs):x self.conv(inputs)x self.bn(x, trainingtraining)x self.activation(x)return xclass InvertedResidualBlock(layers.Layer):def __init__(self, in_channel, out_channel, strides, expand_ratio, **kwargs):super(InvertedResidualBlock, self).__init__(**kwargs)self.hidden_channel in_channel * expand_ratioself.use_shortcut (strides 1) and (in_channel out_channel)layer_list []# first bottleneck does not need 1*1 convif expand_ratio ! 1:# 1x1 pointwise convlayer_list.append(ConvBNReLU(out_channelself.hidden_channel, kernel_size1, nameexpand))layer_list.extend([# 3x3 depthwise conv layers.DepthwiseConv2D(kernel_size3, paddingSAME, stridesstrides, use_biasFalse, namedepthwise),layers.BatchNormalization(momentum0.9, epsilon1e-5, namedepthwise/BatchNorm),layers.ReLU(max_value6.0),#1x1 pointwise conv(linear) # linear activation y x - no activation functionlayers.Conv2D(filtersout_channel, kernel_size1, strides1, paddingSAME, use_biasFalse, nameproject),layers.BatchNormalization(momentum0.9, epsilon1e-5, nameproject/BatchNorm)])self.main_branch Sequential(layer_list, nameexpanded_conv)def call(self, inputs, **kargs):if self.use_shortcut:return inputs self.main_branch(inputs)else:return self.main_branch(inputs)
5 损失函数softmax 交叉熵
5.1 softmax函数
Softmax函数由下列公式定义 softmax 的作用是把 一个序列变成概率。 softmax用于多分类过程中它将多个神经元的输出映射到0,1区间内所有概率的和将等于1。
python实现
def softmax(x):shift_x x - np.max(x) # 防止输入增大时输出为nanexp_x np.exp(shift_x)return exp_x / np.sum(exp_x)PyTorch封装的Softmax()函数
dim参数 dim为0时对所有数据进行softmax计算 dim为1时对某一个维度的列进行softmax计算 dim为-1 或者2 时对某一个维度的行进行softmax计算 import torch
x torch.tensor([2.0,1.0,0.1])
x.cuda()
outputs torch.softmax(x,dim0)
print(输入,x)
print(输出,outputs)
print(输出之和,outputs.sum())5.2 交叉熵损失函数
定义如下: python实现
def cross_entropy(a, y):return np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))# tensorflow version
loss tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y), reduction_indices[1]))# numpy version
loss np.mean(-np.sum(y_*np.log(y), axis1))PyTorch实现 交叉熵函数分为二分类(torch.nn.BCELoss())和多分类函数(torch.nn.CrossEntropyLoss()
# 二分类 损失函数loss torch.nn.BCELoss()l loss(predreal)# 多分类损失函数loss torch.nn.CrossEntropyLoss()
6 优化器SGD
简介 SGD全称Stochastic Gradient Descent随机梯度下降1847年提出。每次选择一个mini- batch而不是全部样本使用梯度下降来更新模型参数。它解决了随机小批量样本的问题但仍然有自适应学习率、容易卡在梯度较小点等问题。 pytorch调用方法
torch.optim.SGD(params, lrrequired parameter, momentum0, dampening0, weight_decay0, nesterovFalse)相关代码
def step(self, closureNone):Performs a single optimization step.Arguments:closure (callable, optional): A closure that reevaluates the modeland returns the loss.loss Noneif closure is not None:loss closure()for group in self.param_groups:weight_decay group[weight_decay] # 权重衰减系数momentum group[momentum] # 动量因子0.9或0.8dampening group[dampening] # 梯度抑制因子nesterov group[nesterov] # 是否使用nesterov动量for p in group[params]:if p.grad is None:continued_p p.grad.dataif weight_decay ! 0: # 进行正则化# add_表示原处改变d_p d_p weight_decay*p.datad_p.add_(weight_decay, p.data)if momentum ! 0:param_state self.state[p] # 之前的累计的数据v(t-1)# 进行动量累计计算if momentum_buffer not in param_state:buf param_state[momentum_buffer] torch.clone(d_p).detach()else:# 之前的动量buf param_state[momentum_buffer]# buf buf*momentum 1-dampening*d_pbuf.mul_(momentum).add_(1 - dampening, d_p)if nesterov: # 使用neterov动量# d_p d_p momentum*bufd_p d_p.add(momentum, buf)else:d_p buf# p p - lr*d_pp.data.add_(-group[lr], d_p)return loss
7 学习率衰减策略
余弦退火衰减 这可以理解为是一种带重启的随机梯度下降算法。在网络模型更新时由于存在很多局部最优解这就导致模型会陷入局部最优解即优化函数存在多个峰值。这就要求当模型陷入局部最优解时能够跳出去并且继续寻找下一个最优解直到找到全局最优解。要使得模型跳出局部最优解就需
多周期的余弦退火衰减示意图如下 相关代码实现
# ----------------------------------------------------------------------- ## 多周期余弦退火衰减# ----------------------------------------------------------------------- ## eager模式防止graph报错tf.config.experimental_run_functions_eagerly(True)# ------------------------------------------------ #import math# 继承自定义学习率的类class CosineWarmupDecay(keras.optimizers.schedules.LearningRateSchedule):initial_lr: 初始的学习率min_lr: 学习率的最小值max_lr: 学习率的最大值warmup_step: 线性上升部分需要的steptotal_step: 第一个余弦退火周期需要对总stepmulti: 下个周期相比于上个周期调整的倍率print_step: 多少个step并打印一次学习率# 初始化def __init__(self, initial_lr, min_lr, warmup_step, total_step, multi, print_step):# 继承父类的初始化方法super(CosineWarmupDecay, self).__init__()# 属性分配self.initial_lr tf.cast(initial_lr, dtypetf.float32)self.min_lr tf.cast(min_lr, dtypetf.float32)self.warmup_step warmup_step # 初始为第一个周期的线性段的stepself.total_step total_step # 初始为第一个周期的总stepself.multi multiself.print_step print_step# 保存每一个step的学习率self.learning_rate_list []# 当前步长self.step 0# 前向传播, 训练时传入当前step但是上面已经定义了一个这个step用不上def __call__(self, step):# 如果当前step达到了当前周期末端就调整if self.stepself.total_step:# 乘上倍率因子后会有小数这里要注意# 调整一个周期中线性部分的step长度self.warmup_step self.warmup_step * (1 self.multi)# 调整一个周期的总step长度self.total_step self.total_step * (1 self.multi)# 重置step从线性部分重新开始self.step 0# 余弦部分的计算公式decayed_learning_rate self.min_lr 0.5 * (self.initial_lr - self.min_lr) * \(1 tf.math.cos(math.pi * (self.step-self.warmup_step) / \(self.total_step-self.warmup_step)))# 计算线性上升部分的增长系数kk (self.initial_lr - self.min_lr) / self.warmup_step # 线性增长线段 ykxbwarmup k * self.step self.min_lr# 以学习率峰值点横坐标为界左侧是线性上升右侧是余弦下降decayed_learning_rate tf.where(self.stepself.warmup_step, warmup, decayed_learning_rate)# 每个epoch打印一次学习率if step % self.print_step 0:# 打印当前step的学习率print(learning_rate has changed to: , decayed_learning_rate.numpy().item())# 每个step保存一次学习率self.learning_rate_list.append(decayed_learning_rate.numpy().item())# 计算完当前学习率后step加一用于下一次self.step self.step 1# 返回调整后的学习率return decayed_learning_rate
6 最后 更多资料, 项目分享
https://gitee.com/dancheng-senior/postgraduate