淘先锋技术网

首页 1 2 3 4 5 6 7

在人脸识别中,我们通常采用欧氏距离和余弦距离来衡量人脸特征的相似度,判别是否为同一个人。

欧氏距离
欧氏距离比较简单,采用欧氏公式直接计算两个点之间的距离,如下:

代码:

diff = np.subtract(feature1, feature2)
dist = np.sqrt(np.sum(np.square(diff)))
feature1.shape 和feature2.shape 为(n, )

余弦距离


余弦距离,也称为余弦相似度,是用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量。

当两个向量直接的夹角趋向0时,两个向量越接近,差异就越小。此时 = 1,即越接近1值时,说明人脸越相似。

代码:

dot = np.sum(np.multiply(feature1, feature2), axis=1)
norm = np.linalg.norm(feature1, axis=1) * np.linalg.norm(feature2, axis=1)
dist = dot / norm
 

区别
欧氏距离计算的是空间中两个点的绝对距离,距离dist越小,特征越相似;

余弦距离衡量的是空间中两个向量的夹角,夹角越靠近0,即dist越接近1,特征越相似。

阈值选取
以下是一段摘自facenet 的代码:

def distance(embeddings1, embeddings2, distance_metric=0):
    if distance_metric==0:
        # Euclidian distance
        diff = np.subtract(embeddings1, embeddings2)
        dist = np.sum(np.square(diff),1)
    elif distance_metric==1:
        # Distance based on cosine similarity
        dot = np.sum(np.multiply(embeddings1, embeddings2), axis=1)
        norm = np.linalg.norm(embeddings1, axis=1) * np.linalg.norm(embeddings2, axis=1)
        similarity = dot / norm
        dist = np.arccos(similarity) / math.pi
    else:
        raise 'Undefined distance metric %d' % distance_metric 
        
    return dist
 
def calculate_roc(thresholds, embeddings1, embeddings2, actual_issame, nrof_folds=10, distance_metric=0, subtract_mean=False):
    assert(embeddings1.shape[0] == embeddings2.shape[0])
    assert(embeddings1.shape[1] == embeddings2.shape[1])
    nrof_pairs = min(len(actual_issame), embeddings1.shape[0])
    nrof_thresholds = len(thresholds)
    k_fold = KFold(n_splits=nrof_folds, shuffle=False)
    
    tprs = np.zeros((nrof_folds,nrof_thresholds))
    fprs = np.zeros((nrof_folds,nrof_thresholds))
    accuracy = np.zeros((nrof_folds))
    
    indices = np.arange(nrof_pairs)
    
    for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)):
        if subtract_mean:
            mean = np.mean(np.concatenate([embeddings1[train_set], embeddings2[train_set]]), axis=0)
        else:
          mean = 0.0
        dist = distance(embeddings1-mean, embeddings2-mean, distance_metric)
        
        # Find the best threshold for the fold
        acc_train = np.zeros((nrof_thresholds))
        for threshold_idx, threshold in enumerate(thresholds):
            _, _, acc_train[threshold_idx] = calculate_accuracy(threshold, dist[train_set], actual_issame[train_set])
        best_threshold_index = np.argmax(acc_train)
        for threshold_idx, threshold in enumerate(thresholds):
            tprs[fold_idx,threshold_idx], fprs[fold_idx,threshold_idx], _ = calculate_accuracy(threshold, dist[test_set], actual_issame[test_set])
        _, _, accuracy[fold_idx] = calculate_accuracy(thresholds[best_threshold_index], dist[test_set], actual_issame[test_set])
          
        tpr = np.mean(tprs,0)
        fpr = np.mean(fprs,0)
    return tpr, fpr, accuracy
 
def calculate_accuracy(threshold, dist, actual_issame):
    predict_issame = np.less(dist, threshold)
    tp = np.sum(np.logical_and(predict_issame, actual_issame))
    fp = np.sum(np.logical_and(predict_issame, np.logical_not(actual_issame)))
    tn = np.sum(np.logical_and(np.logical_not(predict_issame), np.logical_not(actual_issame)))
    fn = np.sum(np.logical_and(np.logical_not(predict_issame), actual_issame))
  
    tpr = 0 if (tp+fn==0) else float(tp) / float(tp+fn)
    fpr = 0 if (fp+tn==0) else float(fp) / float(fp+tn)
    acc = float(tp+tn)/dist.size
    return tpr, fpr, acc
def evaluate(embeddings, actual_issame, nrof_folds=10, distance_metric=0, subtract_mean=False):
    # Calculate evaluation metrics
    thresholds = np.arange(0, 4, 0.01)
    embeddings1 = embeddings[0::2]
    embeddings2 = embeddings[1::2]
    tpr, fpr, accuracy = facenet.calculate_roc(thresholds, embeddings1, embeddings2,
        np.asarray(actual_issame), nrof_folds=nrof_folds, distance_metric=distance_metric, subtract_mean=subtract_mean)
    thresholds = np.arange(0, 4, 0.001)
    val, val_std, far = facenet.calculate_val(thresholds, embeddings1, embeddings2,
        np.asarray(actual_issame), 1e-3, nrof_folds=nrof_folds, distance_metric=distance_metric, subtract_mean=subtract_mean)
    return tpr, fpr, accuracy, val, val_std, far
从上面的代码,可以看到其实我们在实际的人脸识别中,可以对标准的欧氏距离和余弦距离做适当的放大,这样在更有利于阈值的比较,更精准。那么阈值我们怎么选值呢?在上面第二段代码中可以找到答案,可以看到最佳阈值是可以算出来的,代码 thresholds = np.arange(0, 4, 0.001) ,最佳阈值在(0, 4)之间进行查找,在acc_train 最高时的threshold来当中最佳阈值。

diff = np.subtract(embeddings1, embeddings2)
dist = np.sum(np.square(diff),1)
通过跑LFW数据集测试,facenet算法下,欧氏距离经过上面两个语句变形后的最佳阈值为1.21;在arcface算法,mobilefacenet模型下自己阈值为1.45,可见在选取阈值的时候要根据自己的model来计算一下才合适。

更新:2020.11.6

关于欧氏距离怎么转换为百分比的问题
我们在百度等AI开放平台会看到人脸相似度是一个百分比,我们要怎么把欧氏转换为百分比呢,今天就说一下自己的思路。

首先我们想到的是sigmoid函数,这个函数y的范围是(0, 1),对应的百分比范围0%~100%,我们同样可以通过sigmoid函数的变形来实现映射。

通过上面的式子实现百分比的计算,我们只需要计算参数,就可以计算百分比了。

计算方法:我们在验证集里面跑一下,把欧氏距离最大的值x1记录下来,此时设定一个该值对应最大百分比(比如95%),然后在把欧氏距离最小的值x2也记录下来,对应最小百分比(比如5%),然后把x1, x2代入上式子,就可以算出, 的值。

参考:

https://blog.csdn.net/liunian920305/article/details/73456736

https://blog.csdn.net/u014657795/article/details/85850891

https://www.zhihu.com/question/312564645?rf=403423583