您好,登錄后才能下訂單哦!
使用卷積神經網絡怎么實現人臉識別?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
卷積神經網絡(CNN)的前級包含了卷積和池化操作,可以實現圖片的特征提取和降維,最近幾年由于計算機算力的提升,很多人都開始轉向這個方向,所以我這次打算使用它來試試效果。
老規矩,先配置下編程的環境:
系統: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
上次文章有位讀者評論說:
所以,為了照顧初學者,這里簡單介紹下Anaconda的安裝方法,Anaconda是一個開源的Python發行版本,其包含了Conda、Python等180多個科學包及其依賴項。因為包含了大量的科學包,Anaconda 的下載文件比較大,所以有python包安裝基礎的人還是建議通過pip來安裝所需的依賴。
首先進入Anaconda下載頁(https://www.anaconda.com/download/):
這里根據自己的電腦系統來選擇相應的系統選項,至于是64位還是32位要根據自己電腦的內存大小和系統位數來選擇,python版本選擇3.6。
下載完成安裝,打開程序,切換左側菜單到Environment,選擇all,輸入想要安裝的模塊并搜索,選中后點擊右下角的Apply就開始安裝了。
基本思路:
我的設計思路是這樣的,先用上節講到的人臉檢測方法來檢測出人臉位置,然后根據返回的坐標、尺寸把臉用數組切片的方法截取下來,然后把截取的小圖片送進訓練好的卷積神經網絡模型,得出人臉的分類結果,最后在原圖片上打上包圍框并且把結果寫在包圍框的上端:
原諒我拙劣的繪畫技巧
當然了,實現這一步驟的前提就是要有一個訓練好的可以做人臉識別的模型,所以本文的主要內容都會放在訓練上面。
深度學習框架的選擇:
卷積神經網絡是深度學習在圖像方面的應用,所以最高效的方法就是選擇合適的深度學習框架來實現它,現在市面上有很多深度學習框架可供選擇, 比如基于 C++ 的 Caffe 、基于 Python 的TensorFlow、Pytorch、Theano、CNTK 以及前兩天一個好友提到的她正在用來做推薦算法的 MXNET 。
這些都是搭建深度學習框架不錯的選擇,不過搭建的步驟會比較繁瑣,會讓很多初學者瞬間放棄,還好世界上出現了Keras,它可以使用TensorFlow、Theano、CNTK作為后端運算引擎,提供了高層的,更易于使用的函數,可以讓不太了解深度學習原理的人也能快速上手,用通俗的話說就是:“ Keras是為人類而不是天頂星人設計的API ”。
本文所使用后端運算引擎為TensorFlow,簡稱 TF (掏糞)。
人臉收集:
我的目的是希望在很多人中可以識別出自己的臉,所以對這個系統的要求是:
不能把別人識別成我
要能在我出現的時候識別出我
于是我需要自己的一些圖照片,來教會神經網絡,這個就是我,以及一堆其他人的照片來告訴它,這些不是我,或者說這些人分別是誰。
現在需要去采集一些其他人的圖片,這些數據集可以自己用相機照、或者寫個爬蟲腳本去網上爬,不過由于人臉識別早在幾十年前就一直有前輩在研究,很多大學和研究機構都采集并公布了一些人臉數據集專門用作圖像識別算法的研究和驗證用,像耶魯大學的Yale人臉庫,劍橋大學的ORL人臉庫以及美國國防部的FERET人臉庫等,我在這里用了耶魯大學的Yale人臉庫,里面包含15個人,每人11張照片,主要包括光照條件的變化,表情的變化,接下來我會把自己的幾張照片混進去,看看訓練過后能不能被神經網絡良好的識別。
頭像提取:
提取自己照片使用的是上篇文章提到的方法:
獲取文件夾下所有圖片文件 -> 檢測人臉位置 -> 根據人臉位置及尺寸剪裁出人臉 -> 保存。
這是我的目錄結構:
代碼:
# _*_ 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
得到了還蠻不錯的效果:
尺寸變換:
現在有了所有的圖片,可以開始訓練了,不過Yale人臉庫里面所有照片都是100*100的尺寸,所以將要構建的卷積神經網絡的輸入就是100*100,而我新生成的圖片樣本形狀都是不規則的,為了使它可以順利進入卷積層,第一步就要對圖片做尺寸變換,當然不能暴力的resize成100*100,否則會引起圖片的變形,所以這里采用了一種數字圖像處理中常用的手段,就是將較短的一側涂黑,使它變成和目標圖像相同的比例,然后再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
調用了該函數出現了下面的效果:
下面是讀取照片的函數,可以傳入尺寸,默認尺寸是100*100,返回了兩個列表,第一個列表中每一個元素都是一張圖片,第二個列表中則對應存儲了圖片的標簽,這里用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
訓練:
接下來就是最重要的一步了,訓練卷積神經網絡,訓練的好壞會直接影響識別的準確度。
引進卷積和池化層,卷積類似于圖像處理中的特征提取操作,池化則很類似于降維,常用的有最大池化和平均池化:
from keras.layers import Conv2D, MaxPooling2D
引入全連接層、Dropout、Flatten。
全連接層就是經典的神經網絡全連接。
Dropout用來在訓練時按一定概率隨機丟棄一些神經元,以獲得更高的訓練速度以及防止過擬合。
Flatten用于卷積層與全連接層之間,把卷積輸出的多維數據拍扁成一維數據送進全連接層(類似shape方法):
from keras.layers import Dense, Dropout, Flatten
引入SGD(梯度下降優化器)來使損失函數最小化,常用的優化器還有Adam:
from keras.optimizers import SGD
讀入所有圖像及標簽:
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) #把圖像轉換為float類型,方便歸一化
神經網絡需要數值進行計算,需要對字符型類別標簽進行編碼,最容易想到的就是把他們編碼成1、2、3.......這種,但是這樣也就出現了強行給它們定義了大小的問題,因為如果一個類別是2,一個是4,他們之間就會有兩倍的關系,但是實際上他們之間并沒有直接的倍數關系,所以這里使用one-hot編碼規則,做到所有標簽的平等化。on-hot編碼:
from keras.utils import np_utils ont_hot_labels = np_utils.to_categorical(raw_labels)
在所有讀入的圖像和標簽中,需要劃分一部分用來訓練,一部分用來測試,這里使用了sklearn中的train_test_split方法,不僅可以分割數據,還可以把數據打亂,訓練集 :測試集 = 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)
數據歸一化,圖像數據只需要每個像素除以255就可以:
train_input /= 255.0 valid_input /= 255.0
構建卷積神經網絡的每一層:
添加卷積層,32個卷積核,每個卷積核是3 * 3,邊緣不補充,卷積步長向右、向下都為1, 后端運算使用 tf , 圖片輸入尺寸是(100,100, 3),使用relu作為激活函數,也可以用sigmoid函數等,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())
全連接層, 經典的神經網絡結構,512個神經元:
face_recognition_model.add(Dense(512, activation = 'relu'))
face_recognition_model.add(Dropout(0.4))
輸出層,神經元數是標簽種類數,使用sigmoid激活函數,輸出最終結果:
face_recognition_model.add(Dense(len(ont_hot_labels[0]), activation = 'sigmoid'))
有點不放心,把神經網絡結構打印出來看一下:
face_recognition_model.summary()
看起來沒什么問題。
使用SGD作為反向傳播的優化器,來使損失函數最小化,學習率(learning_rate)是0.01,學習率衰減因子(decay)用來隨著迭代次數不斷減小學習率,防止出現震蕩。引入沖量(momentum),不僅可以在學習率較小的時候加速學習,又可以在學習率較大的時候減速,使用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)
編譯模型,損失函數使用交叉熵,交叉熵函數隨著輸出和期望的差距越來越大,輸出曲線會越來越陡峭,對權值的懲罰力度也會增大,如果其他的損失函數,如均方差可以可以的,各有優劣:
face_recognition_model.compile(loss = 'categorical_crossentropy', optimizer = sgd_optimizer, metrics = ['accuracy'])
開始訓練,訓練100次(epochs),每次訓練分幾個批次,每批(batch_size)20個,shuffle用來打亂樣本順序:
batch_size = 20 #每批訓練數據量的大小 epochs = 100 face_recognition_model.fit(train_input, train_output, epochs = epochs, batch_size = batch_size, shuffle = True, validation_data = (valid_input, valid_output))
現在離開座位,找一個西瓜,慢慢吃,一定要慢,因為訓練的時間著實太長,配上薯片會更好。
訓練完成后在測試集上評估結果并保存模型供以后加載使用:
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
加載級聯分類器模型:
CASE_PATH = "haarcascade_frontalface_default.xml" face_cascade = cv2.CascadeClassifier(CASE_PATH)
加載卷積神經網絡模型:
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),)
根據檢測到的坐標及尺寸裁剪、無形變resize、并送入模型運算,得到結果后在人臉上打上矩形框并在矩形框上方寫上識別結果:
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)
看效果:
當然了,識別的效果還是取決于訓練好的模型的質量,我差不多用了吃2/3個西瓜的時間來訓練,還是有一些誤識別的情況出現:
看完上述內容,你們掌握使用卷積神經網絡怎么實現人臉識別的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。