在人脸识别中,我们通常采用欧氏距离和余弦距离来衡量人脸特征的相似度,判别是否为同一个人,但这样的精度通常比较低,本文将通过训练的方式提高相似度计算的精度。
一、数据集
本文采用CASIA-WebFace数据集:
如上图所示每个目录代表其中一个人
二、对人脸编码
首先需要对每一张人脸进行编码,将人脸转换为长度为128的向量,代码如下:
import face_recognition
image_path = './img.jpg'#图片路径
img = face_recognition.load_image_file(image_path)
encoding = face_recognition.face_encodings(img , num_jitters=4)
三、构建特征
我们对任意两张图片的编码向量组合,同一目录的label记为1,不同label的记为0,组合成长度为256的向量。
四、相似度计算
这里有训练数据有标签可以采样传统的机器学习模型:LR,随机森林,lightgbm等,本文将通过构建一个简单的神经网络来完成这个事情。
首先是构建Dataset,以及评估函数
import pandas as pd
import torch
from torch import nn
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score,accuracy_score
from torch.utils.data import Dataset
import random
import os
from torch.utils.data import DataLoader
import torch.optim as optim
from tqdm import tqdm
class evaluate:
def __init__(self):
pass
def set_lable(self,y_ture,y_pre):
self.y_ture = torch.concat(y_ture).flatten().cpu().numpy()
self.y_pre = torch.concat(y_pre).flatten().cpu().numpy()
def evaluation(self, outputs, labels): # 定义自己的评价函数,用分类的准确率来评价
# outputs => probability (float)
# labels => labels
outputs[outputs >= 0.5] = 1 # 大於等於0.5為有惡意
outputs[outputs < 0.5] = 0 # 小於0.5為無惡意
correct = torch.sum(torch.eq(outputs, labels)).item()
return correct
def acc(self):
return accuracy_score(self.y_ture, self.y_pre)
def auc(self):
return roc_auc_score(self.y_ture,self.y_pre)
class myDataset(Dataset):
def __init__(self,x,y):
self.x = x
self.y = y
def __getitem__(self, item):
lable = self.y[item]
feature = self.x[item]
return feature,lable
def __len__(self):
return len(self.x)
然后是module
class mymodule(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Sequential(
nn.Linear(256 ,512)
,nn.BatchNorm1d(512),
nn.ReLU(),
nn.Linear(512,256)
,nn.BatchNorm1d(256),
nn.ReLU(),
nn.Linear(256,128)
,nn.BatchNorm1d(128),
nn.ReLU(),
#nn.Dropout(p=0.5),
nn.Linear(128, 2)
)
def forward(self,input):
out = self.linear(input)
return out
最后是训练
traindataset = myDataset(x = torch.tensor(X_train.values),y = torch.tensor(y_train.values))
testdataset = myDataset(x = torch.tensor(X_test.values),y = torch.tensor(y_test.values))
batch_size = 16
train_loader = DataLoader(dataset = traindataset,batch_size=batch_size,shuffle=True)
val_loader = DataLoader(dataset = testdataset,batch_size=batch_size,shuffle=True)
model = mymodule()
e1 = evaluate()
e2 = evaluate()
criterion = nn.CrossEntropyLoss()
lr = 0.001
#optimizer = optim.Adam(model.parameters(), lr=lr)
optimizer = torch.optim.SGD(lr=lr,params=model.parameters(), momentum=0.5)
best_acc = 0.
epochs = 50
diff = []
pt = {}
for epoch in tqdm(range(epochs)):
model.train()
total_loss, total_acc = 0, 0
pre_list_train,train_label = [],[]
if epoch % 5 == 0:
optimizer.param_groups[0]['lr'] *= 0.5
for i, (inputs, labels) in enumerate(train_loader):
inputs = inputs.to(dtype=torch.float)
labels = labels.to(dtype=torch.long) # 类型为float
# 2. 清空梯度
outputs = model(inputs)
optimizer.zero_grad()
#print(outputs)
loss = criterion(outputs, labels.long())
_,pre_train = outputs.max(1)
pre_list_train.append(pre_train)
train_label.append(labels)
loss.backward()
optimizer.step()
model.eval()
total_loss, total_acc = 0, 0
pre_list_test, test_label = [],[]
for i, (inputs, labels) in enumerate(val_loader):
inputs = inputs.to(dtype=torch.float)
labels = labels.to(dtype=torch.long)
outputs = model(inputs)
_,pre_test = outputs.max(1)
loss = criterion(outputs, labels.long())
total_loss += loss.item()
pre_list_test.append(pre_test)
test_label.append(labels)
e2.set_lable(test_label,pre_list_test)
e1.set_lable(train_label, pre_list_train)
if e1.auc()>0.90 and e2.auc()>0.90:
diff.append(abs(e1.auc() - e2.auc()))
pt[epoch] = model.state_dict()
print(epoch)
print('\n','epoch:',epoch,
'train_auc:',e1.auc(),
'test_auc:',e2.auc())
# torch.save(model.state_dict(), f'{epoch}moth.pt')
可以看到第三轮的时候auc已经达到了0.96