facenet lfw训练
https://blog.csdn.net/oYeZhou/article/details/88942598
近期,做人臉識別項目,用到了facenet這個開源框架,并使用LFW人臉數據集進行了測試。現將該過程總結如下:
1 facenet簡介
GitHub地址:https://github.com/davidsandberg/facenet.git
facenet的原理就是基于同一人臉總是比不同人臉更相似這一先驗知識,然后利用傳統卷積神經網絡特征提取,利用三元損失函數進行訓練。最終,將人臉映射到特征空間后,同一身份的人臉距離較近,不同身份的人臉距離較遠。模型的輸出是一個512維的向量(原來是128維)。
算法詳情可參考其論文:https://arxiv.org/pdf/1503.03832.pdf。
2 LFW數據集簡介
網盤鏈接: https://pan.baidu.com/s/1qOrFv_8RhIhUJvAmwE8p0g 提取碼: kfwh
LFW數據集是對5000多人在自然場景下采集的共13000多張圖像。lfw_funneled文件夾中每個子文件夾代表一個人,其中包含其若干張同一身份不同場景下的照片,有的只有一張,有的有多張。
lfw_funneled中還包含了幾個txt文檔,這里面記錄了這些人臉的不同組合,我們使用其中的pairs.txt中的組合進行人臉比對測試。
pairs.txt里面包含了6000對人臉,3000對同一身份,3000對不同身份。文檔第一行的10? 300代表正負樣本以300的數量依次羅列,重復10次,因此共10*(300對正樣本+300對負樣本)= 6000對人臉。
3? 測試過程
3.1 圖像路徑提取
首先,我們根據pairs.txt進行圖片路徑的提取:
??? def get_img_pairs_list(pairs_txt_path,img_path):
??????? """ 指定圖片組合及其所在文件,返回各圖片對的絕對路徑
??????????? Args:
??????????????? pairs_txt_path:圖片pairs文件,里面是6000對圖片名字的組合
??????????????? img_path:圖片所在文件夾
??????????? return:
??????????????? img_pairs_list:深度為2的list,每一個二級list存放的是一對圖片的絕對路徑
??????? """
??????? file = open(pairs_txt_path)
??????? img_pairs_list,labels = [],[]
??????? while 1:
??????????? img_pairs = []
??????????? line = file.readline().replace('\n','')
??????????? if line == '':
??????????????? break
??????????? line_list = line.split('\t')
??????????? if len(line_list) == 3:
??????????????? # 圖片路徑示例:
??????????????? # 'C:\Users\thinkpad1\Desktop\image_set\lfw_funneled\Tina_Fey\Tina_Fey_0001.jpg'
??????????????? img_pairs.append(img_path+'\\'+line_list[0]+'\\'+line_list[0]+'_'+('000'+line_list[1])[-4:]+'.jpg')
??????????????? img_pairs.append(img_path+'\\'+line_list[0]+'\\'+line_list[0]+'_'+('000'+line_list[2])[-4:]+'.jpg')
??????????????? labels.append(1)
??????????? elif len(line_list) == 4:
??????????????? img_pairs.append(img_path+'\\'+line_list[0]+'\\'+line_list[0]+'_'+('000'+line_list[1])[-4:]+'.jpg')
??????????????? img_pairs.append(img_path+'\\'+line_list[2]+'\\'+line_list[2]+'_'+('000'+line_list[3])[-4:]+'.jpg')
??????????????? labels.append(0)
??????????? else:
??????????????? continue
?????????? ?
??????????? img_pairs_list.append(img_pairs)
??????? return img_pairs_list,labels
利用上述代碼,即可提取所有人類對的絕對路徑,返回一個路徑list及其標簽(1或0)。
3.2 人臉檢測、對比
獲取到人臉對的圖片路徑及標簽之后,在使用facenet將其轉化為512維的向量之前,需要先對圖像進行人臉提取,即截取其中的人臉區域。這里用到了MTCNN模型,用于檢測出人臉并將人臉區域單獨提出來,然后就可以利用facenet進行人臉特征向量的轉化了。得到這對人臉的特征向量之后,求其歐氏距離,即可根據該距離判斷其是否為同一身份了。提取及比對過程如下(其中模型model是MTCNN的參數,在facenet的GitHub項目的“facenet/src/models/”路徑下已有;model_facenet模型因為比較大,需要單獨下載,點擊下載:鏈接: https://pan.baidu.com/s/1ty7NfBYIretHhnZwwl2dTg 提取碼: g3jy):
??? def face_verification(img_pairs_list):
??????? model = './model/'
??????? model_facenet = r'XXX\XXX\20180402-114759.pb' # 模型在你電腦中的路徑
??????? # mtcnn相關參數
??????? minsize=40
??????? threshold=[0.4,0.5,0.6] # pnet、rnet、onet三個網絡輸出人臉的閾值,大于閾值則保留,小于閾值則丟棄
??????? factor = 0.709? # scale factor
?????? ?
??????? # 創建mtcnn網絡
??????? with tf.Graph().as_default():
??????????? sess=tf.Session()
??????????? with sess.as_default():
??????????????? pnet,rnet,onet=detect_face.create_mtcnn(sess, model)
?????? ?
??????? margin = 44
??????? image_size = 160
?????? ?
??????? with tf.Graph().as_default():
?????????? ?
??????????? with tf.Session() as sess:
?????????????? ?
??????????????? # 根據模型文件載入模型
??????????????? facenet.load_model(model_facenet)
??????????????? # 得到輸入、輸出等張量
??????????????? images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
??????????????? embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
??????????????? phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
?????????????? ?
??????????????? # 設置可視化進度條相關參數
??????????????? jd = '\r?? %2d%%\t [%s%s]'
??????????????? bar_num_total = 50?? ?
??????????????? total_num = len(img_pairs_list)
??????????????? result, dist = [],[]
?????????????? ?
??????????????? for i in range(len(img_pairs_list)):
?????????????????? ?
??????????????????? # 畫進度條
??????????????????? if i%round(total_num/bar_num_total) == 0 or i == total_num-1:
??????????????????????? bar_num_alright = round(bar_num_total*i/total_num)
??????????????????????? alright = '#'*bar_num_alright
??????????????????????? not_alright = '□'*(bar_num_total-bar_num_alright)
??????????????????????? percent = (bar_num_alright/bar_num_total)*100
??????????????????????? print(jd % (percent,alright,not_alright),end='')
?????????????????? ?
??????????????????? # 讀取一對人臉圖像
??????????????????? img_pairs = img_pairs_list[i]
??????????????????? img_list = []
??????????????????? img1 = cv2.imread(img_pairs[0])
??????????????????? img2 = cv2.imread(img_pairs[1])
?????????????????? ?
??????????????????? img_size1 = np.asarray(img1.shape)[0:2]
??????????????????? img_size2 = np.asarray(img2.shape)[0:2]
?????????????????? ?
??????????????????? # 檢測該對圖像中的人臉
??????????????????? bounding_box1,_1=detect_face.detect_face(img1,minsize,pnet,rnet,onet,threshold,factor)
??????????????????? bounding_box2,_2=detect_face.detect_face(img2,minsize,pnet,rnet,onet,threshold,factor)
?????????????????? ?
??????????????????? # 未檢測到人臉,則將結果標為-1,后續計算準確率時排除
??????????????????? if len(bounding_box1)<1 or len(bounding_box2)<1:
??????????????????????? result.append(-1)
??????????????????????? dist.append(-1)
??????????????????????? continue
?????????????????? ?
??????????????????? # 將圖片1加入img_list
??????????????????? det = np.squeeze(bounding_box1[0,0:4])
??????????????????? bb = np.zeros(4, dtype=np.int32)
??????????????????? bb[0] = np.maximum(det[0]-margin/2, 0)
??????????????????? bb[1] = np.maximum(det[1]-margin/2, 0)
??????????????????? bb[2] = np.minimum(det[2]+margin/2, img_size1[1])
??????????????????? bb[3] = np.minimum(det[3]+margin/2, img_size1[0])
??????????????????? cropped = img1[bb[1]:bb[3],bb[0]:bb[2],:]
??????????????????? aligned = cv2.resize(cropped, (image_size, image_size))
??????????????????? prewhitened = facenet.prewhiten(aligned)
??????????????????? img_list.append(prewhitened)
?????????????????? ?
??????????????????? # 將圖片2加入img_list
??????????????????? det = np.squeeze(bounding_box2[0,0:4])
??????????????????? bb = np.zeros(4, dtype=np.int32)
??????????????????? bb[0] = np.maximum(det[0]-margin/2, 0)
??????????????????? bb[1] = np.maximum(det[1]-margin/2, 0)
??????????????????? bb[2] = np.minimum(det[2]+margin/2, img_size2[1])
??????????????????? bb[3] = np.minimum(det[3]+margin/2, img_size2[0])
??????????????????? cropped = img2[bb[1]:bb[3],bb[0]:bb[2],:]
??????????????????? aligned = cv2.resize(cropped, (image_size, image_size))
??????????????????? prewhitened = facenet.prewhiten(aligned)
??????????????????? img_list.append(prewhitened)
?????????????????? ?
??????????????????? images = np.stack(img_list)
?????????????????? ?
??????????????????? # 將兩個人臉轉化為512維的向量
??????????????????? feed_dict = { images_placeholder: images, phase_train_placeholder:False }
??????????????????? emb = sess.run(embeddings, feed_dict=feed_dict)
?????????????????? ?
??????????????????? # 計算兩個人臉向量的距離
??????????????????? ed = np.sqrt( np.sum( np.square( np.subtract(emb[0], emb[1]) ) ) )
??????????????????? dist.append(ed)
??????????????????? # 根據得出的人臉間的距離,判斷是否屬于同一個人
??????????????????? if ed<=1.1:
??????????????????????? result.append(1)
??????????????????? else:
??????????????????????? result.append(0)
??????? return result,dist
上述代碼可以實現在某一指定閾值下,進行人臉比對,得出對比結果存于result中,用于后續計算準確率;同時,為了畫出ROC曲線,這里還返回了,所有人臉對的歐氏距離,存于dist中。
實際上,上述result是dist在某一個閾值下的截面數據,通過設置不同閾值,即可根據dist得出不同的result,下面正是利用這個原理畫出的ROC曲線。
3.3 ROC曲線
根據3.2得出的每對人臉的歐氏距離,還有3.1得出的各對人臉樣本的標簽,即可畫出計算出ROC曲線所需指標:TPR、FPR。
代碼如下:
??? def roc(dist,labels):
??????? TP_list,TN_list,FP_list,FN_list,TPR,FPR = [],[],[],[],[],[]
??????? for t in range(180):
??????????? threh = 0.1+t*0.01
??? ?
??????????? TP,TN,FP,FN = 0,0,0,0
??????????? for i in range(len(dist)):
??????????????? if labels[i]==1 and dist[i]!=-1:
??????????????????? if dist[i]<threh:
??????????????????????? TP += 1
??????????????????? else:
??????????????????????? FN += 1
??????????????? elif labels[i]==0 and dist[i]!=-1:
??????????????????? if dist[i]>=threh:
??????????????????????? TN += 1
??????????????????? else:
??????????????????????? FP += 1
??????????? TP_list.append(TP)
??????????? TN_list.append(TN)
??????????? FP_list.append(FP)
??????????? FN_list.append(FN)
??????????? TPR.append(TP/(TP+FN))
??????????? FPR.append(FP/(FP+TN))
??????? return TP_list,TN_list,FP_list,FN_list,TPR,FPR
?4 完整代碼
??? # -*- coding: utf-8 -*-
??? """
??? Created on Fri Mar 22 09:59:41 2019
??? @author: Leon
??? 內容:
??? 人臉驗證準確率測試
??? 樣本:LFW人臉集,共6000對人臉,中3000對同一身份、3000對不同身份。
??? """
??? import numpy as np
??? import cv2
??? import tensorflow as tf
??? import matplotlib.pyplot as plt
??? # facenet 和 detect_face 均在facenet項目文件中,這里是直接將其放到測試腳本同一路徑下了,也可以安裝facenet,然后調用之
??? import facenet
??? import align.detect_face as detect_face
??? ?
??? def face_verification(img_pairs_list):
??????? model = './model/'
??????? model_facenet = r'XXX\XXX\20180402-114759.pb'
??????? # mtcnn相關參數
??????? minsize=40
??????? threshold=[0.4,0.5,0.6] # pnet、rnet、onet三個網絡輸出人臉的閾值,大于閾值則保留,小于閾值則丟棄
??????? factor = 0.709? # scale factor
?????? ?
??????? # 創建mtcnn網絡
??????? with tf.Graph().as_default():
??????????? sess=tf.Session()
??????????? with sess.as_default():
??????????????? pnet,rnet,onet=detect_face.create_mtcnn(sess, model)
?????? ?
??????? margin = 44
??????? image_size = 160
?????? ?
??????? with tf.Graph().as_default():
?????????? ?
??????????? with tf.Session() as sess:
?????????????? ?
??????????????? # 根據模型文件載入模型
??????????????? facenet.load_model(model_facenet)
??????????????? # 得到輸入、輸出等張量
??????????????? images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
??????????????? embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
??????????????? phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
?????????????? ?
??????????????? # 設置可視化進度條相關參數
??????????????? jd = '\r?? %2d%%\t [%s%s]'
??????????????? bar_num_total = 50?? ?
??????????????? total_num = len(img_pairs_list)
??????????????? result, dist = [],[]
?????????????? ?
??????????????? for i in range(len(img_pairs_list)):
?????????????????? ?
??????????????????? # 畫進度條
??????????????????? if i%round(total_num/bar_num_total) == 0 or i == total_num-1:
??????????????????????? bar_num_alright = round(bar_num_total*i/total_num)
??????????????????????? alright = '#'*bar_num_alright
??????????????????????? not_alright = '□'*(bar_num_total-bar_num_alright)
??????????????????????? percent = (bar_num_alright/bar_num_total)*100
??????????????????????? print(jd % (percent,alright,not_alright),end='')
?????????????????? ?
??????????????????? # 讀取一對人臉圖像
??????????????????? img_pairs = img_pairs_list[i]
??????????????????? img_list = []
??????????????????? img1 = cv2.imread(img_pairs[0])
??????????????????? img2 = cv2.imread(img_pairs[1])
?????????????????? ?
??????????????????? img_size1 = np.asarray(img1.shape)[0:2]
??????????????????? img_size2 = np.asarray(img2.shape)[0:2]
?????????????????? ?
??????????????????? # 檢測該對圖像中的人臉
??????????????????? bounding_box1,_1=detect_face.detect_face(img1,minsize,pnet,rnet,onet,threshold,factor)
??????????????????? bounding_box2,_2=detect_face.detect_face(img2,minsize,pnet,rnet,onet,threshold,factor)
?????????????????? ?
??????????????????? # 未檢測到人臉,則將結果標為-1,后續計算準確率時排除
??????????????????? if len(bounding_box1)<1 or len(bounding_box2)<1:
??????????????????????? result.append(-1)
??????????????????????? dist.append(-1)
??????????????????????? continue
?????????????????? ?
??????????????????? # 將圖片1加入img_list
??????????????????? det = np.squeeze(bounding_box1[0,0:4])
??????????????????? bb = np.zeros(4, dtype=np.int32)
??????????????????? bb[0] = np.maximum(det[0]-margin/2, 0)
??????????????????? bb[1] = np.maximum(det[1]-margin/2, 0)
??????????????????? bb[2] = np.minimum(det[2]+margin/2, img_size1[1])
??????????????????? bb[3] = np.minimum(det[3]+margin/2, img_size1[0])
??????????????????? cropped = img1[bb[1]:bb[3],bb[0]:bb[2],:]
??????????????????? aligned = cv2.resize(cropped, (image_size, image_size))
??????????????????? prewhitened = facenet.prewhiten(aligned)
??????????????????? img_list.append(prewhitened)
?????????????????? ?
??????????????????? # 將圖片2加入img_list
??????????????????? det = np.squeeze(bounding_box2[0,0:4])
??????????????????? bb = np.zeros(4, dtype=np.int32)
??????????????????? bb[0] = np.maximum(det[0]-margin/2, 0)
??????????????????? bb[1] = np.maximum(det[1]-margin/2, 0)
??????????????????? bb[2] = np.minimum(det[2]+margin/2, img_size2[1])
??????????????????? bb[3] = np.minimum(det[3]+margin/2, img_size2[0])
??????????????????? cropped = img2[bb[1]:bb[3],bb[0]:bb[2],:]
??????????????????? aligned = cv2.resize(cropped, (image_size, image_size))
??????????????????? prewhitened = facenet.prewhiten(aligned)
??????????????????? img_list.append(prewhitened)
?????????????????? ?
??????????????????? images = np.stack(img_list)
?????????????????? ?
??????????????????? # 將兩個人臉轉化為512維的向量
??????????????????? feed_dict = { images_placeholder: images, phase_train_placeholder:False }
??????????????????? emb = sess.run(embeddings, feed_dict=feed_dict)
?????????????????? ?
??????????????????? # 計算兩個人臉向量的距離
??????????????????? ed = np.sqrt( np.sum( np.square( np.subtract(emb[0], emb[1]) ) ) )
??????????????????? dist.append(ed)
??????????????????? # 根據得出的人臉間的距離,判斷是否屬于同一個人
??????????????????? if ed<=1.1:
??????????????????????? result.append(1)
??????????????????? else:
??????????????????????? result.append(0)
??????? return result,dist
??? ?
??? def get_img_pairs_list(pairs_txt_path,img_path):
??????? """ 指定圖片組合及其所在文件,返回各圖片對的絕對路徑
??????????? Args:
??????????????? pairs_txt_path:圖片pairs文件,里面是6000對圖片名字的組合
??????????????? img_path:圖片所在文件夾
??????????? return:
??????????????? img_pairs_list:深度為2的list,每一個二級list存放的是一對圖片的絕對路徑
??????? """
??????? file = open(pairs_txt_path)
??????? img_pairs_list,labels = [],[]
??????? while 1:
??????????? img_pairs = []
??????????? line = file.readline().replace('\n','')
??????????? if line == '':
??????????????? break
??????????? line_list = line.split('\t')
??????????? if len(line_list) == 3:
??????????????? # 圖片路徑示例:
??????????????? # 'C:\Users\thinkpad1\Desktop\image_set\lfw_funneled\Tina_Fey\Tina_Fey_0001.jpg'
??????????????? img_pairs.append(img_path+'\\'+line_list[0]+'\\'+line_list[0]+'_'+('000'+line_list[1])[-4:]+'.jpg')
??????????????? img_pairs.append(img_path+'\\'+line_list[0]+'\\'+line_list[0]+'_'+('000'+line_list[2])[-4:]+'.jpg')
??????????????? labels.append(1)
??????????? elif len(line_list) == 4:
??????????????? img_pairs.append(img_path+'\\'+line_list[0]+'\\'+line_list[0]+'_'+('000'+line_list[1])[-4:]+'.jpg')
??????????????? img_pairs.append(img_path+'\\'+line_list[2]+'\\'+line_list[2]+'_'+('000'+line_list[3])[-4:]+'.jpg')
??????????????? labels.append(0)
??????????? else:
??????????????? continue
?????????? ?
??????????? img_pairs_list.append(img_pairs)
??????? return img_pairs_list,labels
??? ?
??? def roc(dist,labels):
??????? TP_list,TN_list,FP_list,FN_list,TPR,FPR = [],[],[],[],[],[]
??????? for t in range(180):
??????????? threh = 0.1+t*0.01
??? ?
??????????? TP,TN,FP,FN = 0,0,0,0
??????????? for i in range(len(dist)):
??????????????? if labels[i]==1 and dist[i]!=-1:
??????????????????? if dist[i]<threh:
??????????????????????? TP += 1
??????????????????? else:
??????????????????????? FN += 1
??????????????? elif labels[i]==0 and dist[i]!=-1:
??????????????????? if dist[i]>=threh:
??????????????????????? TN += 1
??????????????????? else:
??????????????????????? FP += 1
??????????? TP_list.append(TP)
??????????? TN_list.append(TN)
??????????? FP_list.append(FP)
??????????? FN_list.append(FN)
??????????? TPR.append(TP/(TP+FN))
??????????? FPR.append(FP/(FP+TN))
??????? return TP_list,TN_list,FP_list,FN_list,TPR,FPR
??? ?
??? if __name__ == '__main__':
??????? pairs_txt_path = 'C:/Users/thinkpad1/Desktop/image_set/lfw_funneled/pairs.txt'
??????? img_path = 'C:/Users/thinkpad1/Desktop/image_set/lfw_funneled'
??????? img_pairs_list,labels = get_img_pairs_list(pairs_txt_path,img_path)
?????? ?
??????? result,dist = face_verification(img_pairs_list)
?????? ?
??????? num_right, num_total = 0, 0
??????? num_total = len([r for r in result if r != -1])
??????? num_right = len([result[i] for i in range(len(result)) if result[i] == labels[i]])
?????? ?
??????? print("人臉驗證測試完畢")
??????? print("閾值為1.1,共%d對人臉,準確率%2.4f%%"%(num_total, round(100*num_right/num_total,4)))
?????? ?
??????? TP_list,TN_list,FP_list,FN_list,TPR,FPR = roc(dist,labels)
??????? plt.plot(FPR,TPR,label='Roc')
??????? plt.plot([0, 1], [0, 1], '--', color=(0.6, 0.6, 0.6), label='Luck')
??????? plt.xlabel('FPR')
??????? plt.ylabel('TPR')
??????? plt.legend()
?????? ?
??????? plt.plot(np.linspace(0.1,1.89,180),TP_list,label='TP')
??????? plt.plot(np.linspace(0.1,1.89,180),TN_list,label='TN')
??????? plt.plot(np.linspace(0.1,1.89,180),FP_list,label='FP')
??????? plt.plot(np.linspace(0.1,1.89,180),FN_list,label='FN')
??????? plt.legend()
5 測試結果
在閾值1.1下測試準確率為93.48%,這里沒有達到其宣稱的99%+的準確率。
利用每對人臉距離,通過設置不同距離閾值,畫出ROC曲線,如下圖(左),將TP,TN,FP,FN的曲線也畫出來,可以佐證閾值在1.1時,達到最好的分類效果(TP、TN最大,FP、FN最小)。
?
https://blog.csdn.net/oYeZhou/article/details/88942598
總結
以上是生活随笔為你收集整理的facenet lfw训练的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【机器学习实战之一】:C++实现K-近邻
- 下一篇: c++面试常考的知识点汇总