首頁>技術>

在進入神經網路世界之前,讓我們先談一談指紋?眾所周知,沒有兩個人具有相同的指紋,但是我們可以建立一個CNN模型來從指紋影象中預測性別嗎?讓我們看看……

在本文中,我們將建立一個可以根據指紋預測性別的卷積神經網路(CNN)模型。

實現步驟

• 瞭解資料集

• 重新構造資料集(以便使用keras 的Flow_from_directory函式)

• 定義一個簡單的函式提取所需的特定標籤

• 定義一個簡單的函式讀取影象、調整影象大小。

• 預處理訓練和測試資料

• 從頭開始構建簡單的CNN模型

• 訓練和測試模型

注:

如果你是CNN的新手?檢視這篇文章以對它有一個很好的理解:

https://www.freecodecamp.org/news/an-intuitive-guide-to-convolutional-neural-networks-260c2de0a050/

•這篇文章假定您具有卷積神經網路(CNN)的知識。

資料集描述

來自Kaggle的資料集,包含約55,000張人類指紋影象:

https://www.kaggle.com/ruizgara/socofing

關於資料集的介紹:

• 它有兩個主目錄-Real目錄和Altered目錄

• Real目錄包含真實人類指紋(無任何變化)

• Altered目錄包含經過綜合更改的指紋影象,包括用於遮蓋、中央旋轉和Z形切割的三種不同級別的更改。

• Altered目錄進一步分為:Altered_Easy、Altered_hard和Altered_Medium目錄

• Real影象的標籤如下所示:“ 100__M_Left_thumb_finger.BMP”

• Altered影象的標籤遵循以下格式:“ 100__M_Left_thumb_finger_CR.BMP”

格式化資料集

如果我們的資料集如上圖所示那樣構造,我們可以使用keras中的flow_from_directory()函式來載入資料集,這是從目錄載入資料的一種非常簡單的方法,它以目錄名稱作為類別。

話雖如此,資料中目錄的名稱並不是我們想要的類,因此我們將無法使用flow_from_directory函式。

另外,我們將不得不走更長的路來載入我們的資料——將影象轉換為畫素值,同時僅提取我們需要的標籤“ F”和“ M”。然後我們才能使用資料進行訓練、驗證和測試。

請記住,Altered影象的格式應類似於:

“ 100__M_Left_thumb_finger_CR.BMP”

而Real影象的格式應類似於:

“ 100__M_Left_thumb_finger.BMP”

我們將基於“ M”和“ F”的性別進行分類。

第一步:從影象標籤中提取性別。定義一個簡單的函式來完成此任務:

#import necessary librariesimport numpy as npimport pandas as pdimport seaborn as snsimport tensorflow as tfimport osimport cv2import matplotlib.pyplot as plt#Function to extract labels for both real and altered imagesdef extract_label(img_path,train = True):  filename, _ = os.path.splitext(os.path.basename(img_path))  subject_id, etc = filename.split('__')  #For Altered folder  if train:      gender, lr, finger, _, _ = etc.split('_')  #For Real folder  else:      gender, lr, finger, _ = etc.split('_')    gender = 0 if gender == 'M' else 1  lr = 0 if lr == 'Left' else 1  if finger == 'thumb':      finger = 0  elif finger == 'index':      finger = 1  elif finger == 'middle':      finger = 2  elif finger == 'ring':      finger = 3  elif finger == 'little':      finger = 4  return np.array([gender], dtype=np.uint16)

有了上面函式就可以幫助我們提取標籤。此函式遍歷分配給它的影象路徑(img_path)內的影象標籤,拆分影象標籤以獲得F和M,然後將0分配給M,將1分配給F,返回一個0和1的陣列(0代表M,1代表F)。將上述函式應用到Real目錄中的影象,設定train = False,同時對於Altered目錄中的影象設定train= True。

第二步:載入資料。建立另一個簡單的函式來幫助您:

img_size = 96#Function to iterate through all the imagesdef loading_data(path,train):    print("loading data from: ",path)    data = []    for img in os.listdir(path):        try:            array = cv2.imread(os.path.join(path, img), cv2.IMREAD_GRAYSCALE)            resize = cv2.resize(img_array, (img_size, size))            label = extract_label(os.path.join(path, img),train)            data.append([label[0], resize ])        except Exception as e:            pass    data    return data

• 首先設定預期的影象尺寸 size = 96。

• 迭代:對路徑(path)中的所有影象進行操作——讀取影象並將它們轉換為灰度影象(即黑白),然後將這些影象的畫素值陣列返回到img_array。

• resize包含已基於img_size調整大小的陣列值。因此所有影象將具有相同的大小(96x96)。

• 呼叫extract_labels函式來獲取標籤,label中包含標籤值。

• 所有標籤和調整大小的影象陣列新增到data列表 。

• 使用try, except塊,傳遞異常

最後一步:分配各種目錄,並在每個目錄上使用loading_data 函式

Real_path = "../input/socofing/SOCOFing/Real"Easy_path = "../input/socofing/SOCOFing/Altered/Altered-Easy"Medium_path = "../input/socofing/SOCOFing/Altered/Altered-Medium"Hard_path = "../input/socofing/SOCOFing/Altered/Altered-Hard"Easy_data = loading_data(Easy_path, train = True)Medium_data = loading_data(Medium_path, train = True)Hard_data = loading_data(Hard_path, train = True)test = loading_data(Real_path, train = False)data = np.concatenate([Easy_data, Medium_data, Hard_data], axis=0)del Easy_data, Medium_data, Hard_data

訓練集data: Easy_data包含了loading_data函式作用在Altered_Easy資料夾的結果,Medium_data、Hard_data分別對應了loading_data函式作用於Altered_Medium和Altered_hard資料夾的結果,注意函式設定設定train = True,將這些結果連線(串聯)在一起,以便所有Altered影象都在單個變數data中。

測試集test:test包含loading_data函式作用於Real資料夾的結果,需要設定train = False。

資料預處理

必須先打亂我們的資料,然後再繼續,這是為什麼呢?因為在訓練我們的模型時,如果神經網路不斷看到1型別,它將很快假設所有資料是1型別。當它看到0時將很難學習,並且使用測試資料進行測試時會表現糟糕。因此需要將資料隨機化(打亂)。

(1)隨機化訓練資料data和測試資料test陣列。並檢視data的格式

import randomrandom.shuffle(data)random.shuffle(test)

上圖顯示了data 包含的內容。對於第一個陣列,標籤值為0,然後是影象的畫素值陣列(畫素值的範圍是0到255)。

(2)分離影象陣列和標籤

img, labels = [], []for label, feature in data:    labels.append(label)    img.append(feature)train_data = np.array(img).reshape(-1, size, size, 1)train_data = train_data / 255.0from keras.utils.np_utils import to_categoricaltrain_labels = to_categorical(labels, num_classes = 2)del data

• 使用for迴圈,將影象陣列和標籤分成單獨的列表

• img包含影象陣列,labels包含標籤值

• img和 labels是列表

• img中的值在重新調整之前再次轉換為陣列

• 影象的畫素值的範圍是0到255,透過除以255.0,畫素值將按比例縮小到0到1,變為train_data。

• 標籤也從列表轉換為分類值,我們有兩個類F和M類,分配給train_labels

讓我們看看處理後的訓練影象train_data和訓練影象標籤train_labels最後的樣子

(3)最後一步,使用訓練、驗證和測試資料集來訓練模型。已經有了訓練和測試資料,我們仍然需要驗證資料(test),因此我們可以使用來自sklearn庫的train_test_splitfrom或使用keras的validation_split設定驗證資料。

總而言之,我們將在Altered指紋影象(train_data,train_labels)上訓練和驗證模型然後在Real指紋影象(test)上測試模型。

建立CNN模型

(1)構建模型網路結構

• 使用tensorflow來構建我們的模型

• 從頭開始構建一個簡單的CNN模型,在每層都有兩個卷積層之後將透過relu啟用函式新增一個max pooling層

• 之後新增一個flatten層、一個隱藏密集層,然後是一個輸出層。

• input_shape = [96,96,1](1是我們的灰度影象的結果)

• 類別數量是2

#Import necessary librariesfrom tensorflow.keras import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flattenfrom tensorflow.keras import layersfrom tensorflow.keras import optimizersmodel = Sequential([                    Conv2D(32, 3, padding='same', activation='relu',kernel_initializer='he_uniform', input_shape = [96, 96, 1]),                    MaxPooling2D(2),                    Conv2D(32, 3, padding='same', kernel_initializer='he_uniform', activation='relu'),                    MaxPooling2D(2),                    Flatten(),                    Dense(128, kernel_initializer='he_uniform',activation = 'relu'),                    Dense(2, activation = 'softmax'),                    ])

• 每個卷積層(Conv2D)包含32個大小為3(即3 x 3)的過濾器,僅在第一層中設定輸入形狀

• Max pooling(MaxPooling2D)的池化大小為2

• 只有一個具有128個神經單元的隱藏層(Dense),啟用函式是relu

• 分類:將使用Dense大小為2(類編號)的輸出層和softmax啟用來結束網路。

下圖是“FrançoisChollet(keras的作者)的python深度學習”一書中的圖片,詳細說明了如何選擇正確的最後一層啟用和損失函式。

模型結構總結如下:

model.compile(optimizer = optimizers.Adam(1e-3), loss = 'categorical_crossentropy', metrics = ['accuracy'])early_stopping_cb = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)

(2)編譯模型:

使用Adam、學習率為0.001的最佳化器,categorical_crossentropy作為損失函式,準確性為metrics。使用early_stopping_call回撥以防止過擬合。它會監視“val_loss”,如果10個epoch內“val_loss”沒有增加,停止訓練模型。

(3)擬合模型:

history = model.fit(train_data, train_labels, batch_size = 128, epochs = 30,           validation_split = 0.2, callbacks = [early_stopping_cb], verbose = 1)

訓練模型30個epoch(如果願意,您可以訓練更長的時間),我們設定validation_split = 0.2告訴模型將訓練資料的20%用於驗證。

如上圖所示的影象表明我們的模型正在訓練中,它給出了訓練損失和準確度的值,以及每個時期的驗證損失和準確度的值。我們成功地訓練了模型,訓練準確度為99%,val準確度為98 %。還不錯!

(4)繪製訓練和驗證資料的準確性和損失曲線:

import pandas as pdimport matplotlib.pyplot as pltpd.DataFrame(history.history).plot(figsize = (8,5))plt.grid(True)plt.gca().set_ylim(0,1)

從上圖可以看出,一切進展順利。在我們的模型訓練過程中沒有重大的過擬合,兩條損失曲線都隨著精度的提高而逐漸減小。

測試模型

訓練完模型後,想在以前未見過的資料上對其進行測試,以檢視其效能如何。如前所述,在Real指紋影象上測試模型,我們已經有了 test資料。

就像我們對訓練資料所做的一樣,我們將標籤和影象陣列分開,整形併除以255.0

test_images, test_labels = [], []for label, feature in test:    test_images.append(feature)    test_labels.append(label)    test_images = np.array(test_images).reshape(-1, size, size, 1)test_images = test_images / 255.0del testtest_labels  = to_categorical(test_labels, num_classes = 2)

我們得到了與訓練資料(train_data,train_labels)類似的結果

最後,我們透過對模型進行測試來評估測試資料,並給出準確性和損失值:

model.evaluate(test_images, test_labels)

驗證集的準確度為99.72%,損失值為0.0126。太好了,您剛剛成功建立了指紋性別分類模型!

結論

總而言之,我們從頭開始構建一個簡單的CNN,基於指紋影象來預測性別。我們提取了特定標籤,將影象轉換為陣列,預處理了我們的資料集,還預留了訓練資料供我們的模型進行訓練。在測試資料上測試了我們的模型,並達到了99%的準確性。

最後說明

只要您有足夠的影象來訓練,使用CNN就能對幾乎所有影象進行分類只是神經網路的眾多奇蹟之一。有很多東西需要學習和探索,我們只是不準備好迎接令人驚奇的事情。

參考

https://www.kaggle.com/abolarinbukola/fingerprint-gender-classification-cnn

17
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 撕書吧程式設計小白!哈佛CS50,全美最受歡迎計算機入門開放