如何使用深度學(xué)習(xí)進(jìn)行缺陷檢測(cè)
共 6112字,需瀏覽 13分鐘
·
2024-05-20 11:30
機(jī)器學(xué)習(xí)自動(dòng)檢測(cè)異常已成為一個(gè)有著高度直接影響的有趣且具有潛力的研究領(lǐng)域,特別是在視覺(jué)檢測(cè)領(lǐng)域。深度學(xué)習(xí)技術(shù)已成為此任務(wù)的最佳方法。深度學(xué)習(xí)技術(shù)可以通過(guò)訓(xùn)練圖像數(shù)據(jù)集來(lái)提供一個(gè)可以檢測(cè)表面異常的模型。
DAGM數(shù)據(jù)集中不同背景紋理上的表面缺陷
介紹
在一些工業(yè)中,通過(guò)SEM/EDX檢查表面或檢查材料中的雜質(zhì)是質(zhì)量控制的一步。通常,這個(gè)檢查過(guò)程需要質(zhì)量人員手動(dòng)檢查表面。
這需要訓(xùn)練QC檢查員識(shí)別整個(gè)復(fù)雜缺陷范圍。這是耗時(shí)、低效的,可能會(huì)導(dǎo)致生產(chǎn)等待時(shí)間,甚至偶爾錯(cuò)誤分類缺陷,導(dǎo)致客戶投訴或領(lǐng)域故障,從而導(dǎo)致產(chǎn)品召回。過(guò)去,傳統(tǒng)的圖像處理方法足以解決這些問(wèn)題(Paniagua等人,2010;Bulnes等人,2016)。然而,工業(yè)4.0范例傾向于泛化生產(chǎn)線,需要快速適應(yīng)新產(chǎn)品(Oztemel和Gursev,2018)。在本文中,我們探討了基于2D卷積神經(jīng)網(wǎng)絡(luò)的U-Net架構(gòu)來(lái)檢測(cè)缺陷。
U-Net
UNET是由Olaf Ronneberger等人為生物醫(yī)學(xué)圖像分割開(kāi)發(fā)的。該架構(gòu)包含兩條路徑。第一條路徑是收縮路徑(也稱為編碼器),用于捕獲圖像的上下文。編碼器只是傳統(tǒng)的卷積和最大池化層堆棧。第二條路徑是對(duì)稱擴(kuò)展路徑(也稱為解碼器),用于使用轉(zhuǎn)置卷積進(jìn)行精確的定位。因此,它是一個(gè)端到端的,完全卷積的網(wǎng)絡(luò)(FCN),即它只包含卷積層,不包含任何稠密層,因此它可以接受任何大小的圖像。
在原始論文中,UNET描述如下:
數(shù)據(jù)集
給大家推薦一個(gè)缺陷檢測(cè)的數(shù)據(jù)集,具體網(wǎng)址如下:https://conferences.mpi-inf.mpg.de/dagm/2007/prizes.html
這個(gè)數(shù)據(jù)集是人工生成的,但類似于真實(shí)世界的問(wèn)題。它包含多個(gè)數(shù)據(jù)集,每個(gè)數(shù)據(jù)集包含1000個(gè)圖像,顯示沒(méi)有缺陷的背景紋理和150個(gè)帶有一個(gè)標(biāo)記缺陷的背景紋理圖像。單個(gè)數(shù)據(jù)集中的圖像非常相似,但每個(gè)數(shù)據(jù)集是由不同的紋理模型和缺陷模型生成的。
并不是所有紋理偏差都是缺陷。算法將需要在訓(xùn)練階段使用提供的弱標(biāo)簽來(lái)學(xué)習(xí)表征缺陷的屬性。
Python庫(kù):
matplotlibxmltodictsklearntensorflowscipy
克隆代碼庫(kù)
將代碼庫(kù)克隆到本地文件夾:
git clone https://github.com/AdarshGouda/Surface-Defect-Detection.gitcd Surface-Defect-Detection
helper函數(shù)將有助于定位缺陷并使用位于./utils文件夾中的橢圓遮罩它。此文件夾及其內(nèi)容將根據(jù)需要在以下代碼中導(dǎo)入。
下載數(shù)據(jù)集并解壓縮:
wget https://resources.mpi-inf.mpg.de/conference/dagm/2007/Class1_def.zip
根據(jù)我們的網(wǎng)絡(luò)連接性,數(shù)據(jù)集需要幾分鐘時(shí)間才能下載。
unzip -q Class1_def.zip -d .
讓我們來(lái)看看./Class1_def文件夾中的圖像。
請(qǐng)注意第一個(gè)圖像1.png左上角的缺陷。./utils文件夾中的輔助函數(shù)將幫助定位圖像中的這些缺陷并創(chuàng)建相應(yīng)的掩模作為標(biāo)簽。
Surface-Defect-Detection.ipynb文件中的以下代碼塊將對(duì)您想要測(cè)試的任何圖像繪制分割標(biāo)簽。在這里,我已經(jīng)測(cè)試了第一個(gè)圖像1.png。
DataIO.py腳本中的load_images_masks()函數(shù)從Class1_def文件夾中獲取原始圖像文件,并返回圖像及其分割標(biāo)簽。
如上所示,共有150個(gè)大小為512 x 512且通道為1的圖像(灰度圖像而非RGB)。
接下來(lái),讓我們查看前兩個(gè)數(shù)據(jù)點(diǎn)的X和y。
如上所示,分割標(biāo)簽正確地識(shí)別了原始圖像中缺陷的位置。
訓(xùn)練測(cè)試的劃分
定義一個(gè)簡(jiǎn)化版的U-net,以簡(jiǎn)化計(jì)算和訓(xùn)練。
損失函數(shù)和平滑的Dice系數(shù):
對(duì)于圖像分割任務(wù),一個(gè)常見(jiàn)的損失函數(shù)是基于Dice系數(shù)的,它本質(zhì)上是兩個(gè)樣本之間重疊的度量。這個(gè)度量的范圍是從0到1,其中Dice系數(shù)為1表示完美且完全重疊。Dice系數(shù)最初是為二進(jìn)制數(shù)據(jù)開(kāi)發(fā)的,可以計(jì)算為:
其中|A∩B|表示集合A和B之間的公共元素,|A||A|表示集合A中元素的數(shù)量(對(duì)于集合B同樣如此)。
對(duì)于預(yù)測(cè)分割掩碼評(píng)估Dice系數(shù)的情況,我們可以將|A∩B||A∩B|近似為預(yù)測(cè)掩碼和目標(biāo)掩碼的逐元素乘積,然后對(duì)結(jié)果矩陣求和。
因?yàn)槲覀兊哪繕?biāo)掩碼是二進(jìn)制的,所以我們有效地將不在目標(biāo)掩碼中“激活”的任何像素都置為零。對(duì)于剩余的像素,我們實(shí)際上是懲罰低置信度的預(yù)測(cè);這個(gè)式子的更高值,也就是分子中的部分,會(huì)導(dǎo)致更好的Dice系數(shù)。
為了量化|A|和|B|,一些研究人員使用簡(jiǎn)單的求和,而其他研究人員更喜歡使用平方和來(lái)計(jì)算。我沒(méi)有實(shí)踐經(jīng)驗(yàn),不知道在各種任務(wù)中哪種方法表現(xiàn)更好,所以我讓你們嘗試兩種方法,看哪種效果更好。
如果你好奇,那么在計(jì)算Dice系數(shù)的分子中有一個(gè)2,是因?yàn)槲覀兊姆帜浮半p重計(jì)算”了兩個(gè)集合之間的公共元素。為了制定可以最小化的損失函數(shù),我們將使用1?Dice。這個(gè)損失函數(shù)被稱為平滑的Dice損失,因?yàn)槲覀冎苯邮褂昧祟A(yù)測(cè)概率,而不是將它們閾值化并將它們轉(zhuǎn)換為二進(jìn)制掩碼。
關(guān)于神經(jīng)網(wǎng)絡(luò)輸出,分子關(guān)注于我們的預(yù)測(cè)和目標(biāo)掩碼之間的公共激活,而分母關(guān)注于每個(gè)掩碼中的激活數(shù)量。這就有了一個(gè)根據(jù)目標(biāo)掩碼的大小來(lái)歸一化損失的效果,以便平滑的Dice損失不會(huì)在圖像中具有較少空間表示的類中學(xué)習(xí)。
讓我們定義smooth_dice_coeff()函數(shù)來(lái)計(jì)算損失并編譯模型:
訓(xùn)練
在接下來(lái)的部分,我們將看到作者在這個(gè)項(xiàng)目中的訓(xùn)練結(jié)果和測(cè)試效果。作者選擇了批量大小為10和60個(gè)epochs。批量大小為10有助于在RTX3070(筆記本電腦)上運(yùn)行訓(xùn)練。學(xué)習(xí)曲線表現(xiàn)不錯(cuò),沒(méi)有欠擬合或過(guò)擬合的跡象,這是一個(gè)好兆頭。
測(cè)試
接下來(lái)我們使用 predict_evaulation() 函數(shù)來(lái)檢查測(cè)試集上的結(jié)果。
結(jié)論
這個(gè)項(xiàng)目更多是一個(gè)概念的證明,訓(xùn)練圖像是人工生成的。在現(xiàn)實(shí)世界中,從相機(jī)或數(shù)字顯微鏡獲得的圖像可能具有不同的對(duì)比度或亮度值,這可能會(huì)使缺陷檢測(cè)變得困難。在訓(xùn)練過(guò)程中使用數(shù)據(jù)增強(qiáng)技術(shù)可能有助于為真實(shí)的工業(yè)應(yīng)用程序準(zhǔn)備訓(xùn)練模型。在這篇文章中,結(jié)果比作者預(yù)想的要好。
參考文獻(xiàn):
U-Net: 用于生物醫(yī)學(xué)圖像分割的卷積網(wǎng)絡(luò) https://arxiv.org/abs/1505.04597
Tabernik,D.,?ela,S.,Skvar?,J.等?;诜指畹纳疃葘W(xué)習(xí)方法用于表面缺陷檢測(cè)。J Intell Manuf 31,759-776(2020)。https://doi.org/10.1007/s10845-019-01476-x
NVIDIA端到端深度學(xué)習(xí)平臺(tái)
The One Hundred Layers Tiramisu:全卷積密集網(wǎng)絡(luò)用于語(yǔ)義分割 https://arxiv.org/abs/1611.09326
智能制造中的邊緣人工智能:缺陷檢測(cè)及其它應(yīng)用
下面附上UNet網(wǎng)絡(luò)的代碼
def conv2d_block(input_tensor, n_filters, kernel_size = 3, batchnorm = True):to add 2 convolutional layers with the parameters passed to it"""# first layerx = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\kernel_initializer = 'he_normal', padding = 'same')(input_tensor)if batchnorm:x = BatchNormalization()(x)x = Activation('relu')(x)# second layerx = Conv2D(filters = n_filters, kernel_size = (kernel_size, kernel_size),\kernel_initializer = 'he_normal', padding = 'same')(input_tensor)if batchnorm:x = BatchNormalization()(x)x = Activation('relu')(x)return xdef get_unet(input_img, n_filters = 16, dropout = 0.1, batchnorm = True):# Contracting Pathc1 = conv2d_block(input_img, n_filters * 1, kernel_size = 3, batchnorm = batchnorm)p1 = MaxPooling2D((2, 2))(c1)p1 = Dropout(dropout)(p1)c2 = conv2d_block(p1, n_filters * 2, kernel_size = 3, batchnorm = batchnorm)p2 = MaxPooling2D((2, 2))(c2)p2 = Dropout(dropout)(p2)c3 = conv2d_block(p2, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)p3 = MaxPooling2D((2, 2))(c3)p3 = Dropout(dropout)(p3)c4 = conv2d_block(p3, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)p4 = MaxPooling2D((2, 2))(c4)p4 = Dropout(dropout)(p4)c5 = conv2d_block(p4, n_filters = n_filters * 16, kernel_size = 3, batchnorm = batchnorm)# Expansive Pathu6 = Conv2DTranspose(n_filters * 8, (3, 3), strides = (2, 2), padding = 'same')(c5)u6 = concatenate([u6, c4])u6 = Dropout(dropout)(u6)c6 = conv2d_block(u6, n_filters * 8, kernel_size = 3, batchnorm = batchnorm)u7 = Conv2DTranspose(n_filters * 4, (3, 3), strides = (2, 2), padding = 'same')(c6)u7 = concatenate([u7, c3])u7 = Dropout(dropout)(u7)c7 = conv2d_block(u7, n_filters * 4, kernel_size = 3, batchnorm = batchnorm)u8 = Conv2DTranspose(n_filters * 2, (3, 3), strides = (2, 2), padding = 'same')(c7)u8 = concatenate([u8, c2])u8 = Dropout(dropout)(u8)c8 = conv2d_block(u8, n_filters * 2, kernel_size = 3, batchnorm = batchnorm)u9 = Conv2DTranspose(n_filters * 1, (3, 3), strides = (2, 2), padding = 'same')(c8)u9 = concatenate([u9, c1])u9 = Dropout(dropout)(u9)c9 = conv2d_block(u9, n_filters * 1, kernel_size = 3, batchnorm = batchnorm)outputs = Conv2D(1, (1, 1), activation='sigmoid')(c9)model = Model(inputs=[input_img], outputs=[outputs])return model
