溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

發(fā)布時間:2021-05-07 15:18:39 來源:億速云 閱讀:244 作者:Leah 欄目:開發(fā)技術(shù)

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別?相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

卷積神經(jīng)網(wǎng)絡(luò)(CNN)的前級包含了卷積和池化操作,可以實現(xiàn)圖片的特征提取和降維,最近幾年由于計算機算力的提升,很多人都開始轉(zhuǎn)向這個方向,所以我這次打算使用它來試試效果。

老規(guī)矩,先配置下編程的環(huán)境:

  • 系統(tǒng):windows / linux

  • 解釋器:python 3.6

  • 依賴庫:numpy、opencv-python 3、tensorflow、keras、scikit-learn

pip3 install numpy
pip3 install opencv-python
pip3 install keras
pip3 install scikit-learn
pip3 install tensorflow

如果手中有一塊支持Cuda加速的GPU建議安裝GPU版本:

pip3 install tensorflow-gpu

上次文章有位讀者評論說:

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

所以,為了照顧初學(xué)者,這里簡單介紹下Anaconda的安裝方法,Anaconda是一個開源的Python發(fā)行版本,其包含了Conda、Python等180多個科學(xué)包及其依賴項。因為包含了大量的科學(xué)包,Anaconda 的下載文件比較大,所以有python包安裝基礎(chǔ)的人還是建議通過pip來安裝所需的依賴。

首先進入Anaconda下載頁(https://www.anaconda.com/download/):

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

這里根據(jù)自己的電腦系統(tǒng)來選擇相應(yīng)的系統(tǒng)選項,至于是64位還是32位要根據(jù)自己電腦的內(nèi)存大小和系統(tǒng)位數(shù)來選擇,python版本選擇3.6。

下載完成安裝,打開程序,切換左側(cè)菜單到Environment,選擇all,輸入想要安裝的模塊并搜索,選中后點擊右下角的Apply就開始安裝了。

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

基本思路:

我的設(shè)計思路是這樣的,先用上節(jié)講到的人臉檢測方法來檢測出人臉位置,然后根據(jù)返回的坐標(biāo)、尺寸把臉用數(shù)組切片的方法截取下來,然后把截取的小圖片送進訓(xùn)練好的卷積神經(jīng)網(wǎng)絡(luò)模型,得出人臉的分類結(jié)果,最后在原圖片上打上包圍框并且把結(jié)果寫在包圍框的上端:

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

原諒我拙劣的繪畫技巧

當(dāng)然了,實現(xiàn)這一步驟的前提就是要有一個訓(xùn)練好的可以做人臉識別的模型,所以本文的主要內(nèi)容都會放在訓(xùn)練上面。

深度學(xué)習(xí)框架的選擇:

卷積神經(jīng)網(wǎng)絡(luò)是深度學(xué)習(xí)在圖像方面的應(yīng)用,所以最高效的方法就是選擇合適的深度學(xué)習(xí)框架來實現(xiàn)它,現(xiàn)在市面上有很多深度學(xué)習(xí)框架可供選擇, 比如基于 C++ 的 Caffe 、基于 Python 的TensorFlow、Pytorch、Theano、CNTK 以及前兩天一個好友提到的她正在用來做推薦算法的 MXNET 。

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

這些都是搭建深度學(xué)習(xí)框架不錯的選擇,不過搭建的步驟會比較繁瑣,會讓很多初學(xué)者瞬間放棄,還好世界上出現(xiàn)了Keras,它可以使用TensorFlow、Theano、CNTK作為后端運算引擎,提供了高層的,更易于使用的函數(shù),可以讓不太了解深度學(xué)習(xí)原理的人也能快速上手,用通俗的話說就是:“ Keras是為人類而不是天頂星人設(shè)計的API ”。

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

本文所使用后端運算引擎為TensorFlow,簡稱 TF (掏糞)。

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

人臉收集:

我的目的是希望在很多人中可以識別出自己的臉,所以對這個系統(tǒng)的要求是:

  • 不能把別人識別成我

  • 要能在我出現(xiàn)的時候識別出我

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

 于是我需要自己的一些圖照片,來教會神經(jīng)網(wǎng)絡(luò),這個就是我,以及一堆其他人的照片來告訴它,這些不是我,或者說這些人分別是誰。

現(xiàn)在需要去采集一些其他人的圖片,這些數(shù)據(jù)集可以自己用相機照、或者寫個爬蟲腳本去網(wǎng)上爬,不過由于人臉識別早在幾十年前就一直有前輩在研究,很多大學(xué)和研究機構(gòu)都采集并公布了一些人臉數(shù)據(jù)集專門用作圖像識別算法的研究和驗證用,像耶魯大學(xué)的Yale人臉庫,劍橋大學(xué)的ORL人臉庫以及美國國防部的FERET人臉庫等,我在這里用了耶魯大學(xué)的Yale人臉庫,里面包含15個人,每人11張照片,主要包括光照條件的變化,表情的變化,接下來我會把自己的幾張照片混進去,看看訓(xùn)練過后能不能被神經(jīng)網(wǎng)絡(luò)良好的識別。

頭像提?。?/strong>

提取自己照片使用的是上篇文章提到的方法:

獲取文件夾下所有圖片文件 -> 檢測人臉位置 -> 根據(jù)人臉位置及尺寸剪裁出人臉 -> 保存。

這是我的目錄結(jié)構(gòu):

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

代碼:

# _*_ coding:utf-8 _*_
import cv2
import os

CASE_PATH = "haarcascade_frontalface_default.xml"
RAW_IMAGE_DIR = 'me/'
DATASET_DIR = 'jm/'

face_cascade = cv2.CascadeClassifier(CASE_PATH)

def save_feces(img, name,x, y, width, height):
    image = img[y:y+height, x:x+width]
    cv2.imwrite(name, image)

image_list = os.listdir(RAW_IMAGE_DIR) #列出文件夾下所有的目錄與文件
count = 166
for image_path in image_list:
    image = cv2.imread(RAW_IMAGE_DIR + image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray,
                                         scaleFactor=1.2,
                                         minNeighbors=5,
                                         minSize=(5, 5), )
    for (x, y, width, height) in faces:
        save_feces(image, '%ss%d.bmp' % (DATASET_DIR, count), x, y - 30, width, height+30)
    count += 1

得到了還蠻不錯的效果:

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

尺寸變換:

現(xiàn)在有了所有的圖片,可以開始訓(xùn)練了,不過Yale人臉庫里面所有照片都是100*100的尺寸,所以將要構(gòu)建的卷積神經(jīng)網(wǎng)絡(luò)的輸入就是100*100,而我新生成的圖片樣本形狀都是不規(guī)則的,為了使它可以順利進入卷積層,第一步就要對圖片做尺寸變換,當(dāng)然不能暴力的resize成100*100,否則會引起圖片的變形,所以這里采用了一種數(shù)字圖像處理中常用的手段,就是將較短的一側(cè)涂黑,使它變成和目標(biāo)圖像相同的比例,然后再resize,這樣既可以保留原圖的人臉信息,又可以防止圖像形變:

def resize_without_deformation(image, size = (100, 100)):
    height, width, _ = image.shape
    longest_edge = max(height, width)
    top, bottom, left, right = 0, 0, 0, 0
    if height < longest_edge:
        height_diff = longest_edge - height
        top = int(height_diff / 2)
        bottom = height_diff - top
    elif width < longest_edge:
        width_diff = longest_edge - width
        left = int(width_diff / 2)
        right = width_diff - left

    image_with_border = cv2.copyMakeBorder(image, top , bottom, left, right, cv2.BORDER_CONSTANT, value = [0, 0, 0])

    resized_image = cv2.resize(image_with_border, size)

    return resized_image

調(diào)用了該函數(shù)出現(xiàn)了下面的效果:

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

下面是讀取照片的函數(shù),可以傳入尺寸,默認(rèn)尺寸是100*100,返回了兩個列表,第一個列表中每一個元素都是一張圖片,第二個列表中則對應(yīng)存儲了圖片的標(biāo)簽,這里用1、2、3.......來指代,因為我根本不知道這些人的名字是什么:

def read_image(size = None):
    data_x, data_y = [], []
    for i in range(1,177):
        try:
            im = cv2.imread('jm/s%s.bmp' % str(i))
            #im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
            if size is None:
                size = (100, 100)
            im = resize_without_deformation(im, size)
            data_x.append(np.asarray(im, dtype = np.int8))
            data_y.append(str(int((i-1)/11.0)))
        except IOError as e:
           print(e)
        except:
            print('Unknown Error!')

    return data_x, data_y

訓(xùn)練:

接下來就是最重要的一步了,訓(xùn)練卷積神經(jīng)網(wǎng)絡(luò),訓(xùn)練的好壞會直接影響識別的準(zhǔn)確度。

引進卷積和池化層,卷積類似于圖像處理中的特征提取操作,池化則很類似于降維,常用的有最大池化和平均池化:

from keras.layers import Conv2D, MaxPooling2D

引入全連接層、Dropout、Flatten。

全連接層就是經(jīng)典的神經(jīng)網(wǎng)絡(luò)全連接。

Dropout用來在訓(xùn)練時按一定概率隨機丟棄一些神經(jīng)元,以獲得更高的訓(xùn)練速度以及防止過擬合。

Flatten用于卷積層與全連接層之間,把卷積輸出的多維數(shù)據(jù)拍扁成一維數(shù)據(jù)送進全連接層(類似shape方法):

from keras.layers import Dense, Dropout, Flatten

引入SGD(梯度下降優(yōu)化器)來使損失函數(shù)最小化,常用的優(yōu)化器還有Adam:

from keras.optimizers import SGD

讀入所有圖像及標(biāo)簽:

IMAGE_SIZE = 100
raw_images, raw_labels = read_image(size=(IMAGE_SIZE, IMAGE_SIZE))
raw_images, raw_labels = np.asarray(raw_images, dtype = np.float32), np.asarray(raw_labels, dtype = np.int32) #把圖像轉(zhuǎn)換為float類型,方便歸一化

神經(jīng)網(wǎng)絡(luò)需要數(shù)值進行計算,需要對字符型類別標(biāo)簽進行編碼,最容易想到的就是把他們編碼成1、2、3.......這種,但是這樣也就出現(xiàn)了強行給它們定義了大小的問題,因為如果一個類別是2,一個是4,他們之間就會有兩倍的關(guān)系,但是實際上他們之間并沒有直接的倍數(shù)關(guān)系,所以這里使用one-hot編碼規(guī)則,做到所有標(biāo)簽的平等化。on-hot編碼:

from keras.utils import np_utils
ont_hot_labels = np_utils.to_categorical(raw_labels)

在所有讀入的圖像和標(biāo)簽中,需要劃分一部分用來訓(xùn)練,一部分用來測試,這里使用了sklearn中的train_test_split方法,不僅可以分割數(shù)據(jù),還可以把數(shù)據(jù)打亂,訓(xùn)練集 :測試集 = 7 : 3  :

from sklearn.model_selection import  train_test_split
train_input, valid_input, train_output, valid_output =train_test_split(raw_images, 
                  ont_hot_labels,
                  test_size = 0.3)

數(shù)據(jù)歸一化,圖像數(shù)據(jù)只需要每個像素除以255就可以:

train_input /= 255.0
valid_input /= 255.0

構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)的每一層:

添加卷積層,32個卷積核,每個卷積核是3 * 3,邊緣不補充,卷積步長向右、向下都為1, 后端運算使用 tf , 圖片輸入尺寸是(100,100, 3),使用relu作為激活函數(shù),也可以用sigmoid函數(shù)等,relu收斂速度比較快:

face_recognition_model = keras.Sequential()
 
face_recognition_model.add(Conv2D(32, 3, 3, border_mode='valid',
                                  subsample = (1, 1),
                                  dim_ordering = 'tf',
                                  input_shape = (IMAGE_SIZE, IMAGE_SIZE, 3),
                                  activation='relu'))
 
face_recognition_model.add(Conv2D(32, 3, 3,border_mode='valid',
                                  subsample = (1, 1),
                                  dim_ordering = 'tf',
                                  activation = 'relu'))

池化層,過濾器尺寸是2 * 2:

face_recognition_model.add(MaxPooling2D(pool_size=(2, 2)))

Dropout層:

face_recognition_model.add(Dropout(0.2))
face_recognition_model.add(Conv2D(64, 3, 3, border_mode='valid',
                                  subsample = (1, 1),
                                  dim_ordering = 'tf',
                                  activation = 'relu'))
 
face_recognition_model.add(Conv2D(64, 3, 3, border_mode='valid',
                                  subsample = (1, 1),
                                  dim_ordering = 'tf',
                                  activation = 'relu'))
 
face_recognition_model.add(MaxPooling2D(pool_size=(2, 2)))
face_recognition_model.add(Dropout(0.2))

Flatten層,處于卷積層與Dense(全連層)之間,將圖片的卷積輸出壓扁成一個一維向量:

face_recognition_model.add(Flatten())

全連接層,  經(jīng)典的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),512個神經(jīng)元:

face_recognition_model.add(Dense(512, activation = 'relu'))
face_recognition_model.add(Dropout(0.4))

輸出層,神經(jīng)元數(shù)是標(biāo)簽種類數(shù),使用sigmoid激活函數(shù),輸出最終結(jié)果:

face_recognition_model.add(Dense(len(ont_hot_labels[0]), activation = 'sigmoid'))

有點不放心,把神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)打印出來看一下:

face_recognition_model.summary()

看起來沒什么問題。

使用SGD作為反向傳播的優(yōu)化器,來使損失函數(shù)最小化,學(xué)習(xí)率(learning_rate)是0.01,學(xué)習(xí)率衰減因子(decay)用來隨著迭代次數(shù)不斷減小學(xué)習(xí)率,防止出現(xiàn)震蕩。引入沖量(momentum),不僅可以在學(xué)習(xí)率較小的時候加速學(xué)習(xí),又可以在學(xué)習(xí)率較大的時候減速,使用nesterov:

learning_rate = 0.01
decay = 1e-6
momentum = 0.8
nesterov = True
sgd_optimizer = SGD(lr = learning_rate, decay = decay,
                    momentum = momentum, nesterov = nesterov)

編譯模型,損失函數(shù)使用交叉熵,交叉熵函數(shù)隨著輸出和期望的差距越來越大,輸出曲線會越來越陡峭,對權(quán)值的懲罰力度也會增大,如果其他的損失函數(shù),如均方差可以可以的,各有優(yōu)劣:

face_recognition_model.compile(loss = 'categorical_crossentropy',
                               optimizer = sgd_optimizer,
                               metrics = ['accuracy'])

開始訓(xùn)練,訓(xùn)練100次(epochs),每次訓(xùn)練分幾個批次,每批(batch_size)20個,shuffle用來打亂樣本順序:

batch_size = 20 #每批訓(xùn)練數(shù)據(jù)量的大小
epochs = 100
face_recognition_model.fit(train_input, train_output,
                           epochs = epochs,
                           batch_size = batch_size, 
                           shuffle = True,
                           validation_data = (valid_input, valid_output))

現(xiàn)在離開座位,找一個西瓜,慢慢吃,一定要慢,因為訓(xùn)練的時間著實太長,配上薯片會更好。

訓(xùn)練完成后在測試集上評估結(jié)果并保存模型供以后加載使用:

print(face_recognition_model.evaluate(valid_input, valid_output, verbose=0))
MODEL_PATH = 'face_model.h6'
face_recognition_model.save(MODEL_PATH)

識別:

要開始寫在識別時正式運行的程序了:

import cv2
import numpy as np
import keras
from keras.models import load_model

加載級聯(lián)分類器模型:

CASE_PATH = "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(CASE_PATH)

加載卷積神經(jīng)網(wǎng)絡(luò)模型:

face_recognition_model = keras.Sequential()
MODEL_PATH = 'face_model.h6'
face_recognition_model = load_model(MODEL_PATH)

打開攝像頭,獲取圖片并灰度化:

cap = cv2.VideoCapture(0) 
ret, image = cap.read()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

人臉檢測:

faces = faceCascade.detectMultiScale(gray, scaleFactor=1.2,
                minNeighbors=5, minSize=(30, 30),)

根據(jù)檢測到的坐標(biāo)及尺寸裁剪、無形變resize、并送入模型運算,得到結(jié)果后在人臉上打上矩形框并在矩形框上方寫上識別結(jié)果:

for (x, y, width, height) in faces:
    img = image[y:y+height, x:x+width]
    img = resize_without_deformation(img)
 
    img = img.reshape((1, 100, 100, 3))
    img = np.asarray(img, dtype = np.float32)
    img /= 255.0
 
    result = face_recognition_model.predict_classes(img)
 
    cv2.rectangle(image, (x, y), (x + width, y + height), (0, 255, 0), 2)
    font = cv2.FONT_HERSHEY_SIMPLEX
    if result[0] == 15:
        cv2.putText(image, 'kangChi', (x, y-2), font, 0.7, (0, 255, 0), 2)
    else:
        cv2.putText(image, 'No.%d' % result[0], (x, y-2), font, 0.7, (0, 255, 0), 2)
        
cv2.imshow('', image)
cv2.waitKey(0)

看效果:

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

當(dāng)然了,識別的效果還是取決于訓(xùn)練好的模型的質(zhì)量,我差不多用了吃2/3個西瓜的時間來訓(xùn)練,還是有一些誤識別的情況出現(xiàn):

使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別

看完上述內(nèi)容,你們掌握使用卷積神經(jīng)網(wǎng)絡(luò)怎么實現(xiàn)人臉識別的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI