1.研究背景與意義
小麥是世界上最重要的糧食作物之一,但由于病害的侵襲,小麥產量和質量受到了嚴重的威脅。因此,開發一種高效準確的小麥病害識別分類防治系統對于保障糧食安全和農業可持續發展具有重要意義。
傳統的小麥病害識別分類方法主要依賴于人工觀察和經驗判斷,這種方法存在著識別效率低、準確性不高的問題。而隨著計算機視覺和深度學習技術的快速發展,基于圖像處理和機器學習的小麥病害識別分類方法逐漸成為研究熱點。
近年來,深度學習在圖像識別領域取得了顯著的成果。然而,傳統的深度卷積神經網絡(CNN)在處理小麥病害圖像時面臨著一些挑戰。首先,小麥病害圖像通常具有復雜的紋理和形狀特征,傳統的CNN模型往往難以捕捉到這些細節信息。其次,小麥病害圖像中的病害部分往往只占整個圖像的一小部分,而傳統的CNN模型往往會將整個圖像作為輸入,導致對病害部分的關注不足。
為了解決上述問題,本研究提出了一種基于通道注意力LW-ResNet的小麥病害識別分類防治系統。該系統主要包括兩個關鍵部分:通道注意力機制和LW-ResNet模型。
通道注意力機制是一種自適應的注意力機制,它能夠根據圖像的內容自動調整各個通道的權重,從而使網絡更加關注重要的特征。在小麥病害識別分類任務中,通道注意力機制可以幫助網絡更好地捕捉到病害圖像中的細節信息,提高識別準確性。
LW-ResNet模型是一種改進的殘差網絡模型,它結合了局部窗口和全局窗口的特征信息。在小麥病害識別分類任務中,LW-ResNet模型可以通過局部窗口和全局窗口的特征融合,提高網絡對病害部分的關注度,進一步提高識別準確性。
該研究的意義主要體現在以下幾個方面:
首先,基于通道注意力LW-ResNet的小麥病害識別分類防治系統可以提高小麥病害的識別準確性和分類效果,幫助農民及時采取相應的防治措施,減少病害對小麥產量和質量的影響。
其次,該系統可以提高小麥病害的識別效率,減輕人工觀察和經驗判斷的負擔,提高工作效率和決策準確性。
此外,該研究還可以為其他農作物的病害識別分類提供借鑒和參考,促進農業科技的發展和應用。
綜上所述,基于通道注意力LW-ResNet的小麥病害識別分類防治系統具有重要的研究意義和應用價值,對于保障糧食安全、提高農業生產效益具有重要的推動作用。
2.圖片演示
3.視頻演示
基于全局注意力的改進YOLOv7-AC的水下場景目標檢測系統_嗶哩嗶哩_bilibili
4.LW-ResNet 網絡模型
針對自然場景下蘋果病害的特點以及移動設備存儲空間和計算資源有限的應用需求基于ResNet18構建了改進的殘差網絡模型(LW-ResNet),結構如圖所示。
該網絡在ResNet18的基礎上,將每個Stage減少至1個殘差模塊,并且在殘差模塊中添加了多種感受野尺寸,降低模型的參數量與計算量并獲取多種局部特征。設計池化層與卷積層串聯的方式實現恒等映射,減少信息損失,加強病斑細節特征的表達。在殘差模塊間引入輕量注意力模塊ECANet,抑制由復雜背景產生的環境噪聲在模型學習過程中的傳遞。
5.核心代碼講解
5.1 model.py
class ECANet(nn.Module):def __init__(self, channels):super(ECANet, self).__init__()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.conv = nn.Conv1d(1, 1, kernel_size=3, padding=1, bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):y = self.avg_pool(x)y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)y = self.sigmoid(y)return x * y.expand_as(x)class ResidualBlock(nn.Module):def __init__(self, in_channels, out_channels, stride=1):super(ResidualBlock, self).__init__()G = 16 # group countmid_channels = out_channels // 4self.conv1 = nn.Conv2d(in_channels, mid_channels, kernel_size=1, stride=stride, bias=False)self.bn1 = nn.BatchNorm2d(mid_channels)self.gconv1 = nn.Conv2d(mid_channels, mid_channels, kernel_size=1, groups=G, bias=False)self.gconv3 = nn.Conv2d(mid_channels, mid_channels, kernel_size=3, groups=G, padding=1, bias=False)self.gconv5 = nn.Conv2d(mid_channels, mid_channels, kernel_size=5, groups=G, padding=2, bias=False)self.gconv7 = nn.Conv2d(mid_channels, mid_channels, kernel_size=7, groups=G, padding=3, bias=False)self.conv2 = nn.Conv2d(mid_channels*4, out_channels, kernel_size=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channels)self.relu = nn.ReLU(inplace=True)self.downsample = nn.Sequential(nn.MaxPool2d(3, stride=2, padding=1),nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False)) if stride != 1 or in_channels != out_channels else Nonedef forward(self, x):identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)g1 = self.gconv1(out)g3 = self.gconv3(out)g5 = self.gconv5(out)g7 = self.gconv7(out)out = torch.cat([g1, g3, g5, g7], dim=1)out = self.conv2(out)out = self.bn2(out)if self.downsample:identity = self.downsample(identity)out += identityout = self.relu(out)return outclass LWResNet(nn.Module):def __init__(self, num_classes=2): # Assuming Binary Classification (Disease/Not Disease)super(LWResNet, self).__init__()self.conv = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)self.bn = nn.BatchNorm2d(16)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.stage1 = self._make_layer(16, 16, 1)self.stage2 = self._make_layer(16, 32, 2)self.stage3 = self._make_layer(32, 64, 2)self.stage4 = self._make_layer(64, 128, 2)self.ecanet1 = ECANet(16)self.ecanet2 = ECANet(32)self.ecanet3 = ECANet(64)self.ecanet4 = ECANet(128)self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(128, num_classes)self.softmax = nn.Softmax(dim=1)def _make_layer(self, in_channels, out_channels, stride):return ResidualBlock(in_channels, out_channels, stride)def forward(self, x):x = self.conv(x)x = self.bn(x)x = self.relu(x)x = self.maxpool(x)x = self.stage1(x)x = self.ecanet1(x)x = self.stage2(x)x = self.ecanet2(x)x = self.stage3(x)x = self.ecanet3(x)......
-
ECANet類:
- 初始化函數中,定義了avg_pool、conv和sigmoid層。
- forward函數中,首先對輸入進行平均池化操作,然后將結果經過一系列卷積操作,并使用sigmoid函數進行激活。最后將輸入與激活后的結果相乘并返回。
-
ResidualBlock類:
- 初始化函數中,定義了conv1、bn1、gconv1、gconv3、gconv5、gconv7、conv2、bn2、relu和downsample層。
- forward函數中,首先將輸入保存為identity,然后經過一系列卷積和激活操作,其中gconv1、gconv3、gconv5和gconv7是分組卷積操作。接著將這些操作的結果進行拼接,并經過一次卷積操作。如果downsample不為None,則對identity進行下采樣操作。最后將identity與輸出相加,并經過激活函數后返回。
-
LWResNet類:
- 初始化函數中,定義了conv、bn、relu、maxpool、stage1、stage2、stage3、stage4、ecanet1、ecanet2、ecanet3、ecanet4、avgpool、fc和softmax層。
- _make_layer函數用于生成ResidualBlock層。
- forward函數中,首先經過一次卷積、批歸一化和激活操作,然后進行最大池化。接著經過stage1、ecanet1、stage2、ecanet2、stage3、ecanet3、stage4和ecanet4的一系列操作。最后進行自適應平均池化、展平、全連接和softmax操作,并返回結果。
以上是對給定代碼的逐文件分析,核心部分是ECANet、ResidualBlock和LWResNet類的定義和實現。
該程序文件名為model.py,主要包含了三個類:ECANet、ResidualBlock和LWResNet。
ECANet類是一個自定義的模塊,包含了一個自適應平均池化層、一個一維卷積層和一個Sigmoid激活函數。該類的forward方法實現了特征圖的通道注意力機制。
ResidualBlock類是一個殘差塊,包含了多個卷積層和批歸一化層。該類的forward方法實現了殘差連接和特征圖的分組卷積。
LWResNet類是一個輕量級的ResNet模型,包含了多個卷積層、批歸一化層、殘差塊和ECANet模塊。該類的forward方法實現了模型的前向傳播過程,包括卷積、池化、殘差塊、通道注意力和全連接層等操作。
該程序文件主要實現了一個輕量級的ResNet模型,并使用了通道注意力機制來提升模型的性能。
5.2 train.py
class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)self.bn1 = nn.BatchNorm2d(64)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = nn.Sequential(RestNetBasicBlock(64, 64, 1),RestNetBasicBlock(64, 64, 1))self.ca = ChannelAttention(64)self.sa = SpatialAttention()self.layer2 = nn.Sequential(RestNetDownBlock(64, 128, [2, 1]),RestNetBasicBlock(128, 128, 1))self.layer3 = nn.Sequential(RestNetDownBlock(128, 256, [2, 1]),RestNetBasicBlock(256, 256, 1))self.layer4 = nn.Sequential(RestNetDownBlock(256, 512, [2, 1]),RestNetBasicBlock(512, 512, 1))self.spp = SPP()self.conv2 = nn.Conv2d(2048, 512, kernel_size=1)self.avgpool = nn.AdaptiveAvgPool2d(output_size=(1, 1))self.fc = nn.Linear(512, 6)def forward(self, x):out = self.conv1(x)out = self.layer1(out)out = self.ca(out) * outout = self.sa(out) * outout = self.layer2(out)out = self.layer3(out)out = self.layer4(out)out = self.spp(out)out = self.conv2(out)out = self.avgpool(out)out = out.reshape(x.shape[0], -1)out = self.fc(out)return out......
這個程序文件名為train.py,主要功能是訓練一個卷積神經網絡模型來對圖像進行分類。程序的主要步驟如下:
- 導入所需的庫和模塊,包括numpy、pandas、torch、sklearn、matplotlib、torchvision等。
- 定義數據集的路徑和類別,包括訓練集和測試集的路徑。
- 將原始圖像按照類別分別復制到訓練集和測試集的文件夾中。
- 進行數據預處理,包括圖像的縮放、裁剪、翻轉、旋轉、顏色調整等操作,并進行歸一化處理。
- 創建訓練集和測試集的數據加載器,用于批量加載數據。
- 定義卷積神經網絡模型,包括基本的卷積塊、下采樣塊、SPP模塊、通道注意力模塊和空間注意力模塊等。
- 將模型移動到GPU上(如果可用)。
- 定義優化器、損失函數和學習率衰減策略。
- 進行模型訓練,包括多個epoch的循環,每個epoch中進行前向傳播、計算損失、反向傳播和參數更新。
- 保存訓練好的模型。
- 繪制訓練過程中的損失和準確率曲線。
總體來說,這個程序文件實現了一個卷積神經網絡模型的訓練過程,用于對圖像進行分類。
5.3 ui.py
class RestNetBasicBlock(nn.Module):def __init__(self, in_channels, out_channels, stride):super(RestNetBasicBlock, self).__init__()self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)self.bn1 = nn.BatchNorm2d(out_channels)self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride, padding=1)self.bn2 = nn.BatchNorm2d(out_channels)def forward(self, x):output = self.conv1(x)output = F.relu(self.bn1(output))output = self.conv2(output)output = self.bn2(output)return F.relu(x + output)class RestNetDownBlock(nn.Module):def __init__(self, in_channels, out_channels, stride):super(RestNetDownBlock, self).__init__()self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride[0], padding=1)self.bn1 = nn.BatchNorm2d(out_channels)self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride[1], padding=1)self.bn2 = nn.BatchNorm2d(out_channels)self.extra = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride[0], padding=0),nn.BatchNorm2d(out_channels))def forward(self, x):extra_x = self.extra(x)output = self.conv1(x)out = F.relu(self.bn1(output))out = self.conv2(out)out = self.bn2(out)return F.relu(extra_x + out)class SPP(nn.Module):def __init__(self):super(SPP, self).__init__()self.pool1 = nn.MaxPool2d(kernel_size=5,stride=1,padding=5 // 2)self.pool2 = nn.MaxPool2d(kernel_size=7, stride=1, padding=7 // 2)self.pool3 = nn.MaxPool2d(kernel_size=13, stride=1, padding=13 // 2)def forward(self,x):x1 = self.pool1(x)x2 = self.pool2(x)x3 = self.pool3(x)return torch.cat([x,x1,x2,x3],dim=1)class ChannelAttention(nn.Module):def __init__(self, in_planes, ratio=16):super(ChannelAttention, self).__init__()self.avg_pool = nn.AdaptiveAvgPool2d(1)self.max_pool = nn.AdaptiveMaxPool2d(1)self.fc1 = nn.Conv2d(in_planes, in_planes // 16, 1, bias=False)self.relu1 = nn.ReLU()self.fc2 = nn.Conv2d(in_planes // 16, in_planes, 1, bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))out = avg_out + max_outreturn self.sigmoid(out)class SpatialAttention(nn.Module):def __init__(self, kernel_size=7):super(SpatialAttention, self).__init__()assert kernel_size in (3, 7), 'kernel size must be 3 or 7'padding = 3 if kernel_size == 7 else 1self.conv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):avg_out = torch.mean(x, dim=1, keepdim=True)max_out, _ = torch.max(x, dim=1, keepdim=True)x = torch.cat([avg_out, max_out], dim=1)x = self.conv1(x)return self.sigmoid(x)......
ui.py是一個用于圖像分類的程序文件。它使用了PyTorch庫來構建一個卷積神經網絡模型,并使用訓練好的模型權重進行圖像分類。程序文件中還包含了一些輔助函數和類,用于數據預處理、圖像顯示和結果輸出等功能。程序通過讀取輸入的圖像文件路徑,將圖像進行預處理后輸入到模型中進行分類,并將分類結果輸出到界面上。
6.系統整體結構
該工程是一個圖像分類的項目,主要包含三個程序文件:model.py、train.py和ui.py。
model.py文件定義了卷積神經網絡模型的結構,包括ECANet、ResidualBlock和LWResNet三個類。ECANet類實現了通道注意力機制,ResidualBlock類實現了殘差連接,LWResNet類是一個輕量級的ResNet模型。
train.py文件用于訓練模型,包括數據預處理、模型構建、模型訓練和保存等功能。它使用了訓練集和測試集的圖像數據進行訓練,并保存訓練好的模型權重。
ui.py文件是一個圖像分類的界面程序,它使用了訓練好的模型權重進行圖像分類。它通過讀取輸入的圖像文件路徑,將圖像進行預處理后輸入到模型中進行分類,并將分類結果輸出到界面上。
下面是每個文件的功能整理:
文件名 | 功能 |
---|---|
model.py | 定義卷積神經網絡模型的結構,包括ECANet、ResidualBlock和LWResNet三個類 |
train.py | 進行模型的訓練,包括數據預處理、模型構建、模型訓練和保存等功能 |
ui.py | 圖像分類的界面程序,使用訓練好的模型權重進行圖像分類,包括圖像預處理、模型加載和分類結果輸出等功能 |
7.多尺度特征提取層
小麥病害識別的難點之一是病斑大小不同,尤其是識別小病斑。由于病斑細節特征的豐富性與其所占像素數成正比[6],微小病斑特征不易提取,從而導致模型整體識別精度的下降。對于ResNet18來說,殘差模塊中只有尺寸為3x3的卷積層,單一尺度的卷積核感受野是固定的,提取到的特征有限,無法滿足識別不同尺寸病斑特別是微小病斑所需的特征信息,還需聯合多個尺度的特征進行鑒別。
然而使用傳統的卷積核獲取多尺度特征的同時也導致了網絡的參數需求大大增加增加了模型訓練與部署的難度。所以AAAI提出的模型選擇參數量與復雜度較低的群卷積構建多尺度特征提取層。將ResNet18的殘差模塊中的第二層卷積替換為4種不同尺度(1×1、3×3、5×5、7×7)的群卷積,實現不同尺度特征的提取[5]。
群卷積操作如圖所示,群卷積將尺寸為HxWxM的特征圖分為G組,每組的輸入通道數為MIG,每組的輸出通道數為NG,卷積核總數為N。
以LW-ResNet的Stagel中的殘差模塊為例,多尺度特征提取層中每個卷積(1×1,3×3,5×5,7x7)的輸入通道為16,輸出通道為16,特征圖輸入和輸出的尺寸為56×56。
傳統卷積構造的多尺度特征提取層的參數量為12×16×16+32×16×16+52×16×16+72×16×16=21504。
組數為16 ( G=16 )的群卷積構建的多尺度特征提取層的參數數量為12×16×16×1/16+32×16×16×1/16+52×16×16×1/16+7×16×16×1/16=1344,與傳統卷積相比參數數量減少了93.75%。
同理,由公式可以計算出,傳統卷積構造的多尺度特征提取層的FLOPs為6.74E+07,而群卷積構造的多尺度特征提取層的FLOPs為4.21E+06。
8.輕量高效的通道注意力模塊
由于AAAI提供的數據集使用的圖像為田間環境下的小麥葉部病害圖像,背景復雜,存在諸多環境干擾因素。干擾因素產生的噪聲會在模型學習過程中傳遞,隨著網絡的加深,噪聲信息在特征圖的權重也不斷增加[6],最終對模型的學習效果產生負面影響。通道注意力機制削弱代表背景特征的通道并減少它們的權重[8],從而降低噪聲等干擾因素對模型識別任務的負面影響。
而大多數通道注意力模塊不能同時考慮低復雜性和高性能[9]。例如,經典的SENet模塊t0I使用兩個全連接層在通道之間交換信息和降低特征維數。全連接層的使用使SENet模塊不具備輕量的特點,降維操作使得通道與其預測值沒有直接關系,這對 SENet的整體性能有所影響。
ECANet利用一維卷積結合相鄰通道獲得加權的病斑特征,彌補特征降維帶來的缺陷,實現局部交互,更好地增強病斑區域的特征。此外,ECANet的參數量和復雜度較低,不會過多增加網絡模型的存儲開銷和計算開銷。ECANet適合引入到輕量級網絡結構中以提高模型在復雜環境下關注有效特征的能力,并保持模型的輕量和快速的特性。因此,本章將低復雜度、高效率的ECANet放置在每個殘差模塊后,通過對殘差模塊產生的通道特征重新校準,提高模型的表達能力,最終達到提高模型識別性能的目標。
ECANet結構如圖所示,其中W為特征圖的寬度,H為特征圖的高度,C為通道數量,U代表原始,GAP代表全局平均池化操作,K既代表一維卷積的卷積核大小,也代表局部跨通道交流的范圍,ü為加權后特征。
9.系統整合
下圖[完整源碼&環境部署視頻教程&自定義UI界面
參考博客《基于通道注意力LW-ResNet的小麥病害識別分類防治系統》