ROC曲线及facenet中的使用

ROC(Receiver Operating Characteristic)曲线,以及AUC(Area Under Curve),常用来评价一个二值分类器的优劣,ROC的横轴为false positive rate,FPR,也就是误判为正确的比例,纵轴是true positive rate,也就是正确的判断为正确的比例,定义如下:
这里写图片描述
接下来我们考虑ROC曲线图中的四个点和一条线。第一个点,(0,1),即FPR=0, TPR=1,这意味着FN(false negative)=0,并且FP(false positive)=0。Wow,这是一个完美的分类器,它将所有的样本都正确分类。第二个点,(1,0),即FPR=1,TPR=0,类似地分析可以发现这是一个最糟糕的分类器,因为它成功避开了所有的正确答案。第三个点,(0,0),即FPR=TPR=0,即FP(false positive)=TP(true positive)=0,可以发现该分类器预测所有的样本都为负样本(negative)。类似的,第四个点(1,1),分类器实际上预测所有的样本都为正样本。经过以上的分析,我们可以断言,ROC曲线越接近左上角,该分类器的性能越好。
这里写图片描述
下面考虑ROC曲线图中的虚线y=x上的点。这条对角线上的点其实表示的是一个采用随机猜测策略的分类器的结果,例如(0.5,0.5),表示该分类器随机对于一半的样本猜测其为正样本,另外一半的样本为负样本。

如何画ROC曲线

对于一个特定的分类器和测试数据集,显然只能得到一个分类结果,即一组FPR和TPR结果,而要得到一个曲线,我们实际上需要一系列FPR和TPR的值,这又是如何得到的呢?我们先来看一下Wikipedia上对ROC曲线的定义:

In signal detection theory, a receiver operating characteristic (ROC), or simply ROC curve, is a graphical plot which illustrates the performance of a binary classifier system as its discrimination threshold is varied.

问题在于“as its discrimination threashold is varied”。如何理解这里的“discrimination threashold”呢?我们忽略了分类器的一个重要功能“概率输出”,即表示分类器认为某个样本具有多大的概率属于正样本(或负样本)。通过更深入地了解各个分类器的内部机理,我们总能想办法得到一种概率输出。通常来说,是将一个实数范围通过某个变换映射到(0,1)区间[^3]。
假如我们已经得到了所有样本的概率输出(属于正样本的概率),现在的问题是如何改变“discrimination threashold”?我们根据每个测试样本属于正样本的概率值从大到小排序。每次选取一个不同的threshold,我们就可以得到一组FPR和TPR,即ROC曲线上的一点。
当我们将threshold设置为1和0时,分别可以得到ROC曲线上的(0,0)和(1,1)两个点。将这些(FPR,TPR)对连接起来,就得到了ROC曲线。当threshold取值越多,ROC曲线越平滑。

其实,我们并不一定要得到每个测试样本是正样本的概率值,只要得到这个分类器对该测试样本的“评分值”即可(评分值并不一定在(0,1)区间)。评分越高,表示分类器越肯定地认为这个测试样本是正样本,而且同时使用各个评分值作为threshold。我认为将评分值转化为概率更易于理解一些。

AUC值的计算

AUC(Area Under Curve)被定义为ROC曲线下的面积,显然这个面积的数值不会大于1。又由于ROC曲线一般都处于y=x这条直线的上方,所以AUC的取值范围在0.5和1之间。使用AUC值作为评价标准是因为很多时候ROC曲线并不能清晰的说明哪个分类器的效果更好,而作为一个数值,对应AUC更大的分类器效果更好。

为什么使用ROC曲线

既然已经这么多评价标准,为什么还要使用ROC和AUC呢?因为ROC曲线有个很好的特性:当测试集中的正负样本的分布变化的时候,ROC曲线能够保持不变。在实际的数据集中经常会出现类不平衡(class imbalance)现象,即负样本比正样本多很多(或者相反),而且测试数据中的正负样本的分布也可能随着时间变化。下图是ROC曲线和Precision-Recall曲线[^5]的对比:
这里写图片描述
在上图中,(a)和(c)为ROC曲线,(b)和(d)为Precision-Recall曲线。(a)和(b)展示的是分类其在原始测试集(正负样本分布平衡)的结果,(c)和(d)是将测试集中负样本的数量增加到原来的10倍后,分类器的结果。可以明显的看出,ROC曲线基本保持原貌,而Precision-Recall曲线则变化较大

下面给出facenet中的计算roc代码:

#embeddings:输入端的特征向量,由于这份代码是针对lfw数据的,因此特征是每两个特征之间是一对
#actual_issame,实际上是不是同一类,或者说是不是同一个人
#nrof_folds,交叉验证的fold数
#下面有个thresholds,是从0开始,间隔0.01,一直到4,总共400个阈值,这里就对应了前面提到的,多个阈值下,得到多个TPR和FPR值,从而可以画出一个ROC曲线
#返回的tpr,fpr,即可用于绘制ROC曲线
def evaluate(embeddings, actual_issame, nrof_folds=10):
    # 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)
    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)
    return tpr, fpr, accuracy, val, val_std, far



def calculate_roc(thresholds, embeddings1, embeddings2, actual_issame, nrof_folds=10):
    assert(embeddings1.shape[0] == embeddings2.shape[0])
    assert(embeddings1.shape[1] == embeddings2.shape[1])
    #lfw中点对的个数
    nrof_pairs = min(len(actual_issame), embeddings1.shape[0])
    #这里thresholds是一个长度为400的数组,从0开始,间隔0.01
    nrof_thresholds = len(thresholds)
    k_fold = KFold(n_splits=nrof_folds, shuffle=False)
    #tprs是一个10x400的数组
    tprs = np.zeros((nrof_folds,nrof_thresholds))
    fprs = np.zeros((nrof_folds,nrof_thresholds))
    accuracy = np.zeros((nrof_folds))

    #求点对之间的差值
    diff = np.subtract(embeddings1, embeddings2)
    #求点对之间的差值的平方和,再相加,相当于是求完欧式距离
    dist = np.sum(np.square(diff),1)
    indices = np.arange(nrof_pairs)

    #将数据分成10份,一份是测试,9份是训练
    for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)):

        # Find the best threshold for the fold
        acc_train = np.zeros((nrof_thresholds))
        #在0到4,每间隔0.01的数字作为阈值,然后再选择其中值最大的作为阈值
        for threshold_idx, threshold in enumerate(thresholds):
            #在当前阈值下,求训练数据的准确度
            _, _, acc_train[threshold_idx] = calculate_accuracy(threshold, dist[train_set], actual_issame[train_set])
        #获取0-4之间阈值下的最佳值
        best_threshold_index = np.argmax(acc_train)
        #获取0-4之间阈值下的测试集的TPR和FPR
        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])
        #由于还有交叉验证,因此还需要求10次tpr和fpr均值
        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

图片及部分文字来源:
http://alexkong.net/2013/06/introduction-to-auc-and-roc/

<think>好的,我现在要帮助用户了解如何在PyTorch中使用FaceNet模型进行人脸识别。首先,我需要回忆一下FaceNet的基本原理和实现步骤。根据用户提供的引用内容,FaceNet的核心是将人脸映射到高维空间,通过比较特征向量的距离来判断是否为同一个人。那在PyTorch中实现的话,可能需要用到facenet-pytorch这个库,对吧? 用户的问题是如何用PyTorch实现基于FaceNet的人脸识别程序。我需要分步骤来解答。首先,应该安装必要的库,比如facenet-pytorch和mtcnn,因为引用里提到mtcnn用于人脸检测。然后,加载预训练的FaceNet模型,这可以通过库里的InceptionResnetV1来实现。接下来,处理输入图片,可能需要进行人脸检测和对齐,再提取特征向量。最后,计算欧氏距离并设定阈值来判断是否匹配。 我需要确保每一步的代码示例正确,比如使用MTCNN检测人脸,然后使用Resnet提取特征。同时,要解释每个步骤的作用,比如为什么需要人脸检测,特征归一化的原因等。用户可能对如何比较特征向量不太清楚,所以需要详细说明欧氏距离的计算和阈值的选择,引用中提到阈值设为0.8或根据实际情况调整,这点很重要。 另外,用户可能遇到的问题包括环境配置、库的版本兼容性,或者如何处理不同尺寸的人脸图像。需要提醒用户注意安装正确的依赖库,比如PyTorch和torchvision。同时,在代码示例中,应该包括加载图片、检测人脸、转换到模型需要的输入格式等步骤。可能还需要提到使用GPU加速,如果有的话。 还要考虑用户的实际应用场景,比如是否需要实时检测,或者批量处理图片。不过根据引用中的示例,可能先以单张图片对比为例。最后,生成的相关问题需要覆盖性能优化、模型训练、不同检测库的比较,以及部署到生产环境的方法,这些都在用户提供的引用内容中有涉及,可以据此提出相关问题。 现在需要组织这些思路,按照步骤清晰解释,并给出代码示例,同时确保LaTeX格式正确,如变量用$符号包裹,独立公式用$$。检查是否有遗漏的步骤,比如预处理中的归一化,或者特征向量的归一化处理,这在引用3中提到过,特征向量需要归一化后再计算距离,避免误差。 总结下来,回答的结构应该是:环境准备、人脸检测、特征提取、比对与识别,然后是代码示例,最后是相关问题。确保每个部分都有对应的解释和代码片段,引用相关的参考文献,比如引用2和3中的实现流程。同时,注意代码中的注释,帮助用户理解每行代码的作用,比如使用MTCNN检测人脸,调整图像尺寸,转换为Tensor等。</think>以下是基于PyTorch实现FaceNet人脸识别的分步指南: --- ### 一、环境准备 1. 安装依赖库 ```bash pip install facenet-pytorch pip install mtcnn # 用于人脸检测 pip install torchvision ``` 2. 导入核心模块 ```python from facenet_pytorch import MTCNN, InceptionResnetV1 import torch from PIL import Image ``` --- ### 二、人脸检测与对齐 使用MTCNN模型进行人脸检测和裁剪: ```python mtcnn = MTCNN(keep_all=True, device='cuda:0' if torch.cuda.is_available() else 'cpu') img = Image.open("face1.jpg") aligned_faces = mtcnn(img) # 返回(tensor, prob)列表 ``` --- ### 三、特征提取 1. 加载预训练FaceNet模型 ```python resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device) ``` 2. 生成特征向量 ```python face_tensor = aligned_faces[0].unsqueeze(0).to(device) feature_vector = resnet(face_tensor) feature_vector = feature_vector.detach().cpu().numpy() feature_vector = feature_vector / np.linalg.norm(feature_vector) # 归一化[^3] ``` --- ### 四、比对与识别 1. 计算欧氏距离 ```python def compare_faces(vec1, vec2): return np.linalg.norm(vec1 - vec2) ``` 2. 设定阈值判断 ```python threshold = 0.8 # 根据数据集调整[^3] distance = compare_faces(vec1, vec2) is_match = distance < threshold ``` --- ### 五、完整示例代码 ```python device = 'cuda:0' if torch.cuda.is_available() else 'cpu' # 初始化模型 mtcnn = MTCNN(keep_all=True, device=device) resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device) # 处理图片 def process_image(path): img = Image.open(path) faces = mtcnn(img) if faces is not None: return faces[0].unsqueeze(0).to(device) return None # 特征提取 face1 = process_image('img1.jpg') face2 = process_image('img2.jpg') vec1 = resnet(face1).detach().cpu().numpy() vec2 = resnet(face2).detach().cpu().numpy() # 归一化处理 vec1 /= np.linalg.norm(vec1) vec2 /= np.linalg.norm(vec2) # 比对结果 distance = np.linalg.norm(vec1 - vec2) print(f"欧式距离: {distance:.4f}, 是否匹配: {distance < 0.8}") ``` --- ### 关键点说明 1. **MTCNN的作用**:实现人脸检测、关键点定位和人脸对齐三合一[^4] 2. **特征归一化**:将128维特征向量归一化为单位向量,确保距离计算有效性[^3] 3. **阈值选择**:LFW数据集建议阈值为$1.0 \pm 0.2$,实际需通过ROC曲线确定最优值[^2] ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值