0 前言
?? 這兩年開(kāi)始畢業(yè)設(shè)計(jì)和畢業(yè)答辯的要求和難度不斷提升,傳統(tǒng)的畢設(shè)題目缺少創(chuàng)新和亮點(diǎn),往往達(dá)不到畢業(yè)答辯的要求,這兩年不斷有學(xué)弟學(xué)妹告訴學(xué)長(zhǎng)自己做的項(xiàng)目系統(tǒng)達(dá)不到老師的要求。
為了大家能夠順利以及最少的精力通過(guò)畢設(shè),學(xué)長(zhǎng)分享優(yōu)質(zhì)畢業(yè)設(shè)計(jì)項(xiàng)目,今天要分享的是
?? **基于深度學(xué)習(xí)的植物識(shí)別算法 **
??學(xué)長(zhǎng)這里給一個(gè)題目綜合評(píng)分(每項(xiàng)滿分5分)
- 難度系數(shù):3分
- 工作量:4分
- 創(chuàng)新點(diǎn):4分
1 課題背景
植物在地球上是一種非常廣泛的生命形式,直接關(guān)系到人類的生活環(huán)境,目前,植物識(shí)別主要依靠相關(guān)行業(yè)從業(yè)人員及有經(jīng)驗(yàn)專家實(shí)踐經(jīng)驗(yàn),工作量大、效率低。近年來(lái),隨著社會(huì)科技及經(jīng)濟(jì)發(fā)展越來(lái)越快,計(jì)算機(jī)硬件進(jìn)一步更新,性能也日漸提高,數(shù)字圖像采集設(shè)備應(yīng)用廣泛,設(shè)備存儲(chǔ)空間不斷增大,這樣大量植物信息可被數(shù)字化。同時(shí),基于視頻的目標(biāo)檢測(cè)在模式識(shí)別、機(jī)器學(xué)習(xí)等領(lǐng)域得到快速發(fā)展,進(jìn)而基于圖像集分類方法研究得到發(fā)展。
本項(xiàng)目基于深度學(xué)習(xí)實(shí)現(xiàn)圖像植物識(shí)別。
2 具體實(shí)現(xiàn)
3 數(shù)據(jù)收集和處理
數(shù)據(jù)是深度學(xué)習(xí)的基石
數(shù)據(jù)的主要來(lái)源有: 百度圖片, 必應(yīng)圖片, 新浪微博, 百度貼吧, 新浪博客和一些專業(yè)的植物網(wǎng)站等
爬蟲爬取的圖像的質(zhì)量參差不齊, 標(biāo)簽可能有誤, 且存在重復(fù)文件, 因此必須清洗。清洗方法包括自動(dòng)化清洗, 半自動(dòng)化清洗和手工清洗。
自動(dòng)化清洗包括:
- 濾除小尺寸圖像.
- 濾除寬高比很大或很小的圖像.
- 濾除灰度圖像.
- 圖像去重: 根據(jù)圖像感知哈希.
半自動(dòng)化清洗包括:
- 圖像級(jí)別的清洗: 利用預(yù)先訓(xùn)練的植物/非植物圖像分類器對(duì)圖像文件進(jìn)行打分, 非植物圖像應(yīng)該有較低的得分; 利用前一階段的植物分類器對(duì)圖像文件 (每個(gè)文件都有一個(gè)預(yù)標(biāo)類別) 進(jìn)行預(yù)測(cè), 取預(yù)標(biāo)類別的概率值為得分, 不屬于原預(yù)標(biāo)類別的圖像應(yīng)該有較低的得分. 可以設(shè)置閾值, 濾除很低得分的文件; 另外利用得分對(duì)圖像文件進(jìn)行重命名, 并在資源管理器選擇按文件名排序, 以便于后續(xù)手工清洗掉非植物圖像和不是預(yù)標(biāo)類別的圖像.
- 類級(jí)別的清洗
手工清洗: 人工判斷文件夾下圖像是否屬于文件夾名所標(biāo)稱的物種, 這需要相關(guān)的植物學(xué)專業(yè)知識(shí), 是最耗時(shí)且枯燥的環(huán)節(jié), 但也憑此認(rèn)識(shí)了不少的植物.
3 MobileNetV2網(wǎng)絡(luò)
簡(jiǎn)介
MobileNet網(wǎng)絡(luò)是Google最近提出的一種小巧而高效的CNN模型,其在accuracy和latency之間做了折中。
主要改進(jìn)點(diǎn)
相對(duì)于MobileNetV1,MobileNetV2 主要改進(jìn)點(diǎn):
- 引入倒殘差結(jié)構(gòu),先升維再降維,增強(qiáng)梯度的傳播,顯著減少推理期間所需的內(nèi)存占用(Inverted Residuals)
- 去掉 Narrow layer(low dimension or depth) 后的 ReLU,保留特征多樣性,增強(qiáng)網(wǎng)絡(luò)的表達(dá)能力(Linear Bottlenecks)
- 網(wǎng)絡(luò)為全卷積,使得模型可以適應(yīng)不同尺寸的圖像;使用 RELU6(最高輸出為 6)激活函數(shù),使得模型在低精度計(jì)算下具有更強(qiáng)的魯棒性
- MobileNetV2 Inverted residual block 如下所示,若需要下采樣,可在 DW 時(shí)采用步長(zhǎng)為 2 的卷積
- 小網(wǎng)絡(luò)使用小的擴(kuò)張系數(shù)(expansion factor),大網(wǎng)絡(luò)使用大一點(diǎn)的擴(kuò)張系數(shù)(expansion factor),推薦是5~10,論文中 t = 6 t = 6t=6
倒殘差結(jié)構(gòu)(Inverted residual block)
ResNet的Bottleneck結(jié)構(gòu)是降維->卷積->升維,是兩邊細(xì)中間粗
而MobileNetV2是先升維(6倍)-> 卷積 -> 降維,是沙漏形。區(qū)別于MobileNetV1, MobileNetV2的卷積結(jié)構(gòu)如下:
因?yàn)镈W卷積不改變通道數(shù),所以如果上一層的通道數(shù)很低時(shí),DW只能在低維空間提取特征,效果不好。所以V2版本在DW前面加了一層PW用來(lái)升維。
同時(shí)V2去除了第二個(gè)PW的激活函數(shù)改用線性激活,因?yàn)榧せ詈瘮?shù)在高維空間能夠有效地增加非線性,但在低維空間時(shí)會(huì)破壞特征。由于第二個(gè)PW主要的功能是降維,所以不宜再加ReLU6。
tensorflow相關(guān)實(shí)現(xiàn)代碼
import tensorflow as tf
import numpy as np
from tensorflow.keras import layers, Sequential, Model
class ConvBNReLU(layers.Layer):
def __init__(self, out_channel, kernel_size=3, strides=1, **kwargs):
super(ConvBNReLU, self).__init__(**kwargs)
self.conv = layers.Conv2D(filters=out_channel,
kernel_size=kernel_size,
strides=strides,
padding='SAME',
use_bias=False,
name='Conv2d')
self.bn = layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name='BatchNorm')
self.activation = layers.ReLU(max_value=6.0) # ReLU6
def call(self, inputs, training=False, **kargs):
x = self.conv(inputs)
x = self.bn(x, training=training)
x = self.activation(x)
return x
class 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_ratio
self.use_shortcut = (strides == 1) and (in_channel == out_channel)
layer_list = []
# first bottleneck does not need 1*1 conv
if expand_ratio != 1:
# 1x1 pointwise conv
layer_list.append(ConvBNReLU(out_channel=self.hidden_channel, kernel_size=1, name='expand'))
layer_list.extend([
# 3x3 depthwise conv
layers.DepthwiseConv2D(kernel_size=3, padding='SAME', strides=strides, use_bias=False, name='depthwise'),
layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name='depthwise/BatchNorm'),
layers.ReLU(max_value=6.0),
#1x1 pointwise conv(linear)
# linear activation y = x -> no activation function
layers.Conv2D(filters=out_channel, kernel_size=1, strides=1, padding='SAME', use_bias=False, name='project'),
layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name='project/BatchNorm')
])
self.main_branch = Sequential(layer_list, name='expanded_conv')
def call(self, inputs, **kargs):
if self.use_shortcut:
return inputs + self.main_branch(inputs)
else:
return self.main_branch(inputs)
4 損失函數(shù)softmax 交叉熵
4.1 softmax函數(shù)
Softmax函數(shù)由下列公式定義
softmax 的作用是把 一個(gè)序列,變成概率。
softmax用于多分類過(guò)程中,它將多個(gè)神經(jīng)元的輸出,映射到(0,1)區(qū)間內(nèi),所有概率的和將等于1。
python實(shí)現(xiàn)
def softmax(x):
shift_x = x - np.max(x) # 防止輸入增大時(shí)輸出為nan
exp_x = np.exp(shift_x)
return exp_x / np.sum(exp_x)
PyTorch封裝的Softmax()函數(shù)
dim參數(shù):
- dim為0時(shí),對(duì)所有數(shù)據(jù)進(jìn)行softmax計(jì)算
- dim為1時(shí),對(duì)某一個(gè)維度的列進(jìn)行softmax計(jì)算
- dim為-1 或者2 時(shí),對(duì)某一個(gè)維度的行進(jìn)行softmax計(jì)算
import torch
x = torch.tensor([2.0,1.0,0.1])
x.cuda()
outputs = torch.softmax(x,dim=0)
print("輸入:",x)
print("輸出:",outputs)
print("輸出之和:",outputs.sum())
4.2 交叉熵?fù)p失函數(shù)
定義如下:
python實(shí)現(xiàn)
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), axis=1))
PyTorch實(shí)現(xiàn)
交叉熵函數(shù)分為二分類(torch.nn.BCELoss())和多分類函數(shù)(torch.nn.CrossEntropyLoss()
# 二分類 損失函數(shù)
loss = torch.nn.BCELoss()
l = loss(pred,real)
# 多分類損失函數(shù)
loss = torch.nn.CrossEntropyLoss()
5 優(yōu)化器SGD
簡(jiǎn)介
SGD全稱Stochastic Gradient Descent,隨機(jī)梯度下降,1847年提出。每次選擇一個(gè)mini-batch,而不是全部樣本,使用梯度下降來(lái)更新模型參數(shù)。它解決了隨機(jī)小批量樣本的問(wèn)題,但仍然有自適應(yīng)學(xué)習(xí)率、容易卡在梯度較小點(diǎn)等問(wèn)題。
pytorch調(diào)用方法:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-681994.html
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
相關(guān)代碼:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-681994.html
def step(self, closure=None):
"""Performs a single optimization step.
Arguments:
closure (callable, optional): A closure that reevaluates the model
and returns the loss.
"""
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
weight_decay = group['weight_decay'] # 權(quán)重衰減系數(shù)
momentum = group['momentum'] # 動(dòng)量因子,0.9或0.8
dampening = group['dampening'] # 梯度抑制因子
nesterov = group['nesterov'] # 是否使用nesterov動(dòng)量
for p in group['params']:
if p.grad is None:
continue
d_p = p.grad.data
if weight_decay != 0: # 進(jìn)行正則化
# add_表示原處改變,d_p = d_p + weight_decay*p.data
d_p.add_(weight_decay, p.data)
if momentum != 0:
param_state = self.state[p] # 之前的累計(jì)的數(shù)據(jù),v(t-1)
# 進(jìn)行動(dòng)量累計(jì)計(jì)算
if 'momentum_buffer' not in param_state:
buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
else:
# 之前的動(dòng)量
buf = param_state['momentum_buffer']
# buf= buf*momentum + (1-dampening)*d_p
buf.mul_(momentum).add_(1 - dampening, d_p)
if nesterov: # 使用neterov動(dòng)量
# d_p= d_p + momentum*buf
d_p = d_p.add(momentum, buf)
else:
d_p = buf
# p = p - lr*d_p
p.data.add_(-group['lr'], d_p)
return loss
6 最后
到了這里,關(guān)于計(jì)算機(jī)畢設(shè) 基于深度學(xué)習(xí)的植物識(shí)別算法 - cnn opencv python的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!