日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人工智能 > pytorch >内容正文

pytorch

聪明的人脸识别3——Pytorch 搭建自己的Facenet人脸识别平台

發(fā)布時間:2023/12/8 pytorch 76 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聪明的人脸识别3——Pytorch 搭建自己的Facenet人脸识别平台 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

聰明的人臉識別3——Pytorch 搭建自己的Facenet人臉識別平臺

  • 學(xué)習(xí)前言
  • 什么是Facenet
  • 源碼下載
  • Facenet的實(shí)現(xiàn)思路
    • 一、預(yù)測部分
      • 1、主干網(wǎng)絡(luò)介紹
      • 2、根據(jù)初步特征獲得長度為128的特征向量
      • 3、l2標(biāo)準(zhǔn)化
      • 4、構(gòu)建分類器(用于輔助Triplet Loss的收斂)
    • 二、訓(xùn)練部分
      • 1、數(shù)據(jù)集介紹
      • 2、LOSS組成
  • 訓(xùn)練自己的Facenet人臉識別算法

學(xué)習(xí)前言

好的還有Pytorch版。

什么是Facenet

谷歌人臉識別算法,發(fā)表于 CVPR 2015,利用相同人臉在不同角度等姿態(tài)的照片下有高內(nèi)聚性,不同人臉有低耦合性,提出使用 cnn + triplet mining 方法,在 LFW 數(shù)據(jù)集上準(zhǔn)確度達(dá)到 99.63%。

通過 CNN 將人臉映射到歐式空間的特征向量上,實(shí)質(zhì)上:不同圖片人臉特征的距離較大;通過相同個體的人臉的距離,總是小于不同個體的人臉這一先驗(yàn)知識訓(xùn)練網(wǎng)絡(luò)。

測試時只需要計(jì)算人臉特征EMBEDDING,然后計(jì)算距離使用閾值即可判定兩張人臉照片是否屬于相同的個體。

簡單來講,在使用階段,facenet即是:
1、輸入一張人臉圖片
2、通過深度卷積網(wǎng)絡(luò)提取特征
3、L2標(biāo)準(zhǔn)化
4、得到一個長度為128特征向量。

源碼下載

https://github.com/bubbliiiing/facenet-pytorch

Facenet的實(shí)現(xiàn)思路

一、預(yù)測部分

1、主干網(wǎng)絡(luò)介紹


facenet的主干網(wǎng)絡(luò)起到提取特征的作用,原版的facenet以Inception-ResNetV1為主干特征提取網(wǎng)絡(luò)。

本文一共提供了兩個網(wǎng)絡(luò)作為主干特征提取網(wǎng)絡(luò),分別是mobilenetv1和Inception-ResNetV1,二者都起到特征提取的作用,為了方便理解,本博文中會使用mobilenetv1作為主干特征提取網(wǎng)絡(luò)。

MobilenetV1模型是Google針對手機(jī)等嵌入式設(shè)備提出的一種輕量級的深層神經(jīng)網(wǎng)絡(luò),其使用的核心思想便是depthwise separable convolution(深度可分離卷積塊)。

深度可分離卷積塊由兩個部分組成,分別是深度可分離卷積和1x1普通卷積,深度可分離卷積的卷積核大小一般是3x3的,便于理解的話我們可以把它當(dāng)作是特征提取,1x1的普通卷積可以完成通道數(shù)的調(diào)整。

下圖為深度可分離卷積塊的結(jié)構(gòu)示意圖:

深度可分離卷積塊的目的是使用更少的參數(shù)來代替普通的3x3卷積。

我們可以進(jìn)行一下普通卷積和深度可分離卷積塊的對比:

對于普通卷積而言,假設(shè)有一個3×3大小的卷積層,其輸入通道為16、輸出通道為32。具體為,32個3×3大小的卷積核會遍歷16個通道中的每個數(shù)據(jù),最后可得到所需的32個輸出通道,所需參數(shù)為16×32×3×3=4608個。

對于深度可分離卷積結(jié)構(gòu)塊而言,假設(shè)有一個深度可分離卷積結(jié)構(gòu)塊,其輸入通道為16、輸出通道為32,其會用16個3×3大小的卷積核分別遍歷16通道的數(shù)據(jù),得到了16個特征圖譜。在融合操作之前,接著用32個1×1大小的卷積核遍歷這16個特征圖譜,所需參數(shù)為16×3×3+16×32×1×1=656個。

可以看出來深度可分離卷積結(jié)構(gòu)塊可以減少模型的參數(shù)。

如下就是MobileNet的結(jié)構(gòu),其中Conv dw就是分層卷積,在其之后都會接一個1x1的卷積進(jìn)行通道處理,

import torch.nn as nndef conv_bn(inp, oup, stride = 1):return nn.Sequential(nn.Conv2d(inp, oup, 3, stride, 1, bias=False),nn.BatchNorm2d(oup),nn.ReLU6())def conv_dw(inp, oup, stride = 1):return nn.Sequential(nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),nn.BatchNorm2d(inp),nn.ReLU6(),nn.Conv2d(inp, oup, 1, 1, 0, bias=False),nn.BatchNorm2d(oup),nn.ReLU6(),)class MobileNetV1(nn.Module):def __init__(self):super(MobileNetV1, self).__init__()self.stage1 = nn.Sequential(# 160,160,3 -> 80,80,32conv_bn(3, 32, 2), # 80,80,32 -> 80,80,64conv_dw(32, 64, 1), # 80,80,64 -> 40,40,128conv_dw(64, 128, 2),conv_dw(128, 128, 1),# 40,40,128 -> 20,20,256conv_dw(128, 256, 2),conv_dw(256, 256, 1),)self.stage2 = nn.Sequential(# 20,20,256 -> 10,10,512conv_dw(256, 512, 2),conv_dw(512, 512, 1),conv_dw(512, 512, 1),conv_dw(512, 512, 1),conv_dw(512, 512, 1),conv_dw(512, 512, 1),)self.stage3 = nn.Sequential(# 10,10,512 -> 5,5,1024conv_dw(512, 1024, 2),conv_dw(1024, 1024, 1),)self.avg = nn.AdaptiveAvgPool2d((1,1))self.fc = nn.Linear(1024, 1000)def forward(self, x):x = self.stage1(x)x = self.stage2(x)x = self.stage3(x)x = self.avg(x)# x = self.model(x)x = x.view(-1, 1024)x = self.fc(x)return x

2、根據(jù)初步特征獲得長度為128的特征向量


利用主干特征提取網(wǎng)絡(luò)我們可以獲得一個特征層,它的shape為(batch_size, h, w, channels),我們可以將其取全局平均池化,方便后續(xù)的處理(batch_size, channels)。

我們可以將平鋪后的特征層進(jìn)行一個神經(jīng)元個數(shù)為128的全連接。此時我們相當(dāng)于利用了一個長度為128的特征向量代替輸入進(jìn)來的圖片。這個長度為128的特征向量就是輸入圖片的特征濃縮。

class Facenet(nn.Module):def __init__(self, backbone="mobilenet", dropout_keep_prob=0.5, embedding_size=128, num_classes=None, mode="train"):super(Facenet, self).__init__()if backbone == "mobilenet":self.backbone = mobilenet()flat_shape = 1024elif backbone == "inception_resnetv1":self.backbone = inception_resnet()flat_shape = 1792else:raise ValueError('Unsupported backbone - `{}`, Use mobilenet, inception_resnetv1.'.format(backbone))self.avg = nn.AdaptiveAvgPool2d((1,1))self.Dropout = nn.Dropout(1 - dropout_keep_prob)self.Bottleneck = nn.Linear(flat_shape, embedding_size,bias=False)self.last_bn = nn.BatchNorm1d(embedding_size, eps=0.001, momentum=0.1, affine=True)if mode == "train":self.classifier = nn.Linear(embedding_size, num_classes)def forward(self, x):x = self.backbone(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.Dropout(x)x = self.Bottleneck(x)x = self.last_bn(x)x = F.normalize(x, p=2, dim=1)return xdef forward_feature(self, x):x = self.backbone(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.Dropout(x)x = self.Bottleneck(x)before_normalize = self.last_bn(x)x = F.normalize(before_normalize, p=2, dim=1)return before_normalize, xdef forward_classifier(self, x):x = self.classifier(x)return x

3、l2標(biāo)準(zhǔn)化


在獲得一個長度為128的特征向量后,我們還需要進(jìn)行l(wèi)2標(biāo)準(zhǔn)化的處理。

這個L2標(biāo)準(zhǔn)化是為了使得不同人臉的特征向量可以屬于同一數(shù)量級,方便比較。

在進(jìn)行l(wèi)2標(biāo)準(zhǔn)化前需要首先計(jì)算2-范數(shù):
∣∣x∣∣2=∑i=1Nxi2||\textbf{x}||_2 =\sqrt{\sum_{i=1}^Nx_i^2}x2?=i=1N?xi2??,也就是歐幾里得范數(shù),即向量元素絕對值的平方和再開方。

L2標(biāo)準(zhǔn)化就是每個元素/L2范數(shù);

在pytorch代碼中,只需要一行就可以實(shí)現(xiàn)l2標(biāo)準(zhǔn)化的層。

class Facenet(nn.Module):def __init__(self, backbone="mobilenet", dropout_keep_prob=0.5, embedding_size=128, num_classes=None, mode="train"):super(Facenet, self).__init__()if backbone == "mobilenet":self.backbone = mobilenet()flat_shape = 1024elif backbone == "inception_resnetv1":self.backbone = inception_resnet()flat_shape = 1792else:raise ValueError('Unsupported backbone - `{}`, Use mobilenet, inception_resnetv1.'.format(backbone))self.avg = nn.AdaptiveAvgPool2d((1,1))self.Dropout = nn.Dropout(1 - dropout_keep_prob)self.Bottleneck = nn.Linear(flat_shape, embedding_size,bias=False)self.last_bn = nn.BatchNorm1d(embedding_size, eps=0.001, momentum=0.1, affine=True)if mode == "train":self.classifier = nn.Linear(embedding_size, num_classes)def forward(self, x):x = self.backbone(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.Dropout(x)x = self.Bottleneck(x)x = self.last_bn(x)x = F.normalize(x, p=2, dim=1)return xdef forward_feature(self, x):x = self.backbone(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.Dropout(x)x = self.Bottleneck(x)before_normalize = self.last_bn(x)x = F.normalize(before_normalize, p=2, dim=1)return before_normalize, xdef forward_classifier(self, x):x = self.classifier(x)return x

到這里,我們輸入進(jìn)來的圖片,已經(jīng)變成了一個經(jīng)過l2標(biāo)準(zhǔn)化的長度為128的特征向量了!

4、構(gòu)建分類器(用于輔助Triplet Loss的收斂)

當(dāng)我們完成第三步后,我們已經(jīng)可以利用這個預(yù)測結(jié)果進(jìn)行訓(xùn)練和預(yù)測了。

但是由于僅僅只是用Triplet Loss會使得整個網(wǎng)絡(luò)難以收斂,本文結(jié)合Cross-Entropy Loss和Triplet Loss作為總體loss。

Triplet Loss用于進(jìn)行不同人的人臉特征向量歐幾里得距離的擴(kuò)張,同一個人的不同狀態(tài)的人臉特征向量歐幾里得距離的縮小。
Cross-Entropy Loss用于人臉分類,具體作用是輔助Triplet Loss收斂。

想要利用Cross-Entropy Loss進(jìn)行訓(xùn)練需要構(gòu)建分類器,因此對第三步獲得的結(jié)果再次進(jìn)行一個全連接用于分類。

構(gòu)建代碼如下,當(dāng)我們在進(jìn)行網(wǎng)絡(luò)的訓(xùn)練的時候,可使用分類器輔助訓(xùn)練,在預(yù)測的時候,分類器是不需要的:

class Facenet(nn.Module):def __init__(self, backbone="mobilenet", dropout_keep_prob=0.5, embedding_size=128, num_classes=None, mode="train"):super(Facenet, self).__init__()if backbone == "mobilenet":self.backbone = mobilenet()flat_shape = 1024elif backbone == "inception_resnetv1":self.backbone = inception_resnet()flat_shape = 1792else:raise ValueError('Unsupported backbone - `{}`, Use mobilenet, inception_resnetv1.'.format(backbone))self.avg = nn.AdaptiveAvgPool2d((1,1))self.Dropout = nn.Dropout(1 - dropout_keep_prob)self.Bottleneck = nn.Linear(flat_shape, embedding_size,bias=False)self.last_bn = nn.BatchNorm1d(embedding_size, eps=0.001, momentum=0.1, affine=True)if mode == "train":self.classifier = nn.Linear(embedding_size, num_classes)def forward(self, x):x = self.backbone(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.Dropout(x)x = self.Bottleneck(x)x = self.last_bn(x)x = F.normalize(x, p=2, dim=1)return xdef forward_feature(self, x):x = self.backbone(x)x = self.avg(x)x = x.view(x.size(0), -1)x = self.Dropout(x)x = self.Bottleneck(x)before_normalize = self.last_bn(x)x = F.normalize(before_normalize, p=2, dim=1)return before_normalize, xdef forward_classifier(self, x):x = self.classifier(x)return x

二、訓(xùn)練部分

1、數(shù)據(jù)集介紹

我們使用的數(shù)據(jù)集是CASIA-WebFace數(shù)據(jù)集,我已經(jīng)對其進(jìn)行了預(yù)處理,將其屬于同一個人的圖片放到同一個文件夾里面,并且進(jìn)行了人臉的提取和人臉的矯正。

數(shù)據(jù)集里面有很多的文件夾,每一個文件夾里面存放同一個人的不同情況下的人臉。不同文件夾存放不同人的臉。

這是\0000045文件夾里面的人臉,屬于同一個人。

這是\0000099文件夾里面的人臉,屬于同一個人。

2、LOSS組成

facenet使用Triplet Loss作為loss。
Triplet Loss的輸入是一個三元組

  • a:anchor,基準(zhǔn)圖片獲得的128維人臉特征向量
  • p:positive,與基準(zhǔn)圖片屬于同一張人臉的圖片獲得的128維人臉特征向量
  • n:negative,與基準(zhǔn)圖片不屬于同一張人臉的圖片獲得的128維人臉特征向量

我們可以將anchor和positive求歐幾里得距離,并使其盡量小。
我們可以將anchor和negative求歐幾里得距離,并使其盡量大。

我們所使用的公式為。
L=max(d(a,p)?d(a,n)+margin,0)L=max(d(a,p)?d(a,n)+margin,0) L=max(d(a,p)?d(a,n)+margin,0)
d(a,p)就是anchor和positive的歐幾里得距離。
d(a,n)就是negative和positive的歐幾里得距離。
margin是一個常數(shù)。

d(a,p)前面為正符號,所以我們期望其越來越小。
d(a,n)前面為負(fù)符號,所以我們期望其越來越大。

即我們希望,同一個人的不同狀態(tài)的人臉特征向量歐幾里得距離小。
不同人的人臉特征向量歐幾里得距離大。

但是由于僅僅只是用Triplet Loss會使得整個網(wǎng)絡(luò)難以收斂,本文結(jié)合Cross-Entropy Loss和Triplet Loss作為總體loss。

Triplet Loss用于進(jìn)行不同人的人臉特征向量歐幾里得距離的擴(kuò)張,同一個人的不同狀態(tài)的人臉特征向量歐幾里得距離的縮小。
Cross-Entropy Loss用于人臉分類,具體作用是輔助Triplet Loss收斂。

訓(xùn)練自己的Facenet人臉識別算法

博客中所使用的例子為CASIA-WebFace數(shù)據(jù)集。

下載數(shù)據(jù)集,放在根目錄下的dataset文件夾下。
運(yùn)行根目錄下的txt_annotation.py,生成訓(xùn)練所需的cls_train.txt。

cls_train.txt中每一行都存放了一張圖片和它對應(yīng)的類別(需要類別是因?yàn)橛?xùn)練時會用交叉熵?fù)p失輔助收斂。)

下載facenet_inception_resnetv1.pth或者facenet_mobilenet.pth放在model_data文件夾內(nèi)。

在train.py中指定合適的模型預(yù)訓(xùn)練權(quán)重路徑。facenet_inception_resnetv1.pth是我已經(jīng)訓(xùn)練過的基于inception_resnetv1的facenet網(wǎng)絡(luò);
facenet_mobilenet.pth是我已經(jīng)訓(xùn)練過的基于mobilenet的facenet網(wǎng)絡(luò)。

運(yùn)行train.py開始訓(xùn)練。

總結(jié)

以上是生活随笔為你收集整理的聪明的人脸识别3——Pytorch 搭建自己的Facenet人脸识别平台的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。