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

歡迎訪問 生活随笔!

生活随笔

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

pytorch

基于ResNet迁移学习的LFW人脸识别分类

發(fā)布時(shí)間:2023/12/20 pytorch 71 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于ResNet迁移学习的LFW人脸识别分类 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

基于ResNet遷移學(xué)習(xí)的LFW人臉識(shí)別分類

LFW數(shù)據(jù)集(Labeled Faces in the Wild)是馬薩諸塞大學(xué)阿姆斯特分校計(jì)算機(jī)視覺研究所整理制作的一個(gè)非限制環(huán)境下人臉數(shù)據(jù)集,包含5749人合計(jì)13233張圖片,圖片大小都是250x250

本代碼背景是一份CNN的人臉分類報(bào)告,僅需要完成簡單的人臉分類即可,不需要完成人臉識(shí)別,因此就當(dāng)作是人臉識(shí)別的簡單入門,之后的話可能會(huì)根據(jù)自己的興趣做一個(gè)人臉識(shí)別檢測(cè)的demo程序用在樹莓派上面

PS. 基于pytorch-gpu 1.5.1實(shí)現(xiàn),但是為了通用性所以改成了cpu版本,需要使用gpu的同學(xué)請(qǐng)自行添加相應(yīng)代碼

數(shù)據(jù)集準(zhǔn)備

下載數(shù)據(jù)集

可以到LFW官網(wǎng)上下載數(shù)據(jù)集,下載之后會(huì)有好幾個(gè)壓縮包,我們只需要其中的lfw.tgz文件,解壓之后就得到了包含所有圖片的文件夾

也可以直接拿我下好的數(shù)據(jù)集,下面是度娘鏈接

鏈接:https://pan.baidu.com/s/152iVUmPoMDQN_B94hJWETA
提取碼:7a6h
復(fù)制這段內(nèi)容后打開百度網(wǎng)盤手機(jī)App,操作更方便哦–來自百度網(wǎng)盤超級(jí)會(huì)員V5的分享(炫耀下我的v5的(~ ̄▽ ̄)~)

制作DataSet

考慮到LFW原始數(shù)據(jù)集中有很多人只有一張照片,也有部分名人,像布什這種一個(gè)人就有上百張照片,一方面為了保持每個(gè)人對(duì)應(yīng)的人臉照片量合適,另一方面盡量減少需要分類的人的個(gè)數(shù)以減小網(wǎng)絡(luò)大小方便訓(xùn)練,因此需要從LFW數(shù)據(jù)集中挑選一部分照片用于本次實(shí)驗(yàn)。這里最終挑選的是擁有30-100張照片的這部分人,共有29人,也就是說最終的CNN需要分類的個(gè)數(shù)為29類,對(duì)于小實(shí)驗(yàn)而言可以接受了

制作過程分為以下幾步:

  • 讀取文件夾,獲取圖片及人名
  • 挑選其中符合要求的人臉圖片并將人名轉(zhuǎn)換為整數(shù)標(biāo)簽
  • 對(duì)人臉圖片進(jìn)行變換后和人名標(biāo)簽一起存入DataSet
  • 定義DataLoader用于后續(xù)訓(xùn)練
  • PS. 在圖像處理的時(shí)候,因?yàn)镽esNet的圖片輸入大小是224x224,因此做了一個(gè)中心裁剪

    class MyDataSet(Dataset):'''定義數(shù)據(jù)集,用于將讀取到的圖片數(shù)據(jù)轉(zhuǎn)換并處理成CNN神經(jīng)網(wǎng)絡(luò)需要的格式'''def __init__(self, DataArray, LabelArray):super(MyDataSet, self).__init__()self.data = DataArrayself.label = LabelArraydef __getitem__(self, index):# 對(duì)圖片的預(yù)處理步驟# 1. 中心縮放至224(ResNet的輸入大小)# 2. 隨機(jī)旋轉(zhuǎn)0-30°# 3. 對(duì)圖片進(jìn)行歸一化,參數(shù)來源為pytorch官方文檔im_trans = transforms.Compose([transforms.ToPILImage(),transforms.CenterCrop(size=224),transforms.RandomRotation((0, 30)),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])])return im_trans(self.data[index]), t.tensor(self.label[index], dtype=t.long)def __len__(self):return self.label.shape[0]# 讀取LFW數(shù)據(jù)集,將圖片數(shù)據(jù)讀入數(shù)組并將名字轉(zhuǎn)換為標(biāo)簽 path = r'face+\lfw' pathlist = map(lambda x: '\\'.join([path, x]), os.listdir(path)) namedict = {} data, label = [], [] idx = 0 for item in pathlist:dirlist = os.listdir(item)# 選取擁有30-100張照片的人作為數(shù)據(jù)來源# 太少網(wǎng)絡(luò)不容易學(xué)習(xí)到其人臉特征,太多的話則容易過擬合if not (30<= len(dirlist) <= 100):continue# data: 存儲(chǔ)人像照片的三通道數(shù)據(jù)# label: 存儲(chǔ)人像的對(duì)應(yīng)標(biāo)簽(整數(shù))# namedict: 記錄label中整數(shù)與人名的對(duì)應(yīng)關(guān)系for picpath in dirlist:data.append(image.imread(item + '\\' + picpath))label.append(idx)namedict[str(idx)] = item.split('\\')[-1]idx += 1# 隨機(jī)打亂數(shù)據(jù),重新排序并按照8:2的比例分割訓(xùn)練集和測(cè)試集 data, label = np.stack(data), np.array(label) idx = np.random.permutation(data.shape[0]) data, label = data[idx], label[idx] train_X, test_X, train_Y, test_Y = train_test_split(data, label, test_size=0.2)# 將分割好的訓(xùn)練集和測(cè)試集處理為pytorch所需的格式 TrainSet = MyDataSet(train_X, train_Y) TestSet = MyDataSet(test_X, test_Y) TrainLoader = DataLoader(TrainSet, batch_size=32, shuffle=True, drop_last=True) TestLoader = DataLoader(TestSet, batch_size=32, shuffle=True, drop_last=True)

    調(diào)用ResNet18

    pytorch官方提供了很多CNN網(wǎng)絡(luò)的現(xiàn)成版本可以直接調(diào)用,就不用自己費(fèi)力去寫了。而且官方提供的網(wǎng)絡(luò)都有預(yù)訓(xùn)練版本,可以直接拿在ImageNet訓(xùn)練過的CNN網(wǎng)絡(luò)在我們的簡易LFW數(shù)據(jù)集上稍微訓(xùn)練微調(diào),從而實(shí)現(xiàn)遷移學(xué)習(xí),效果一般都會(huì)比較好。

    考慮到我們簡易LFW數(shù)據(jù)集的規(guī)模,用ResNet18就可以了,把pretrained屬性設(shè)置為True使用預(yù)訓(xùn)練版本,初始使用的話會(huì)自動(dòng)下載網(wǎng)絡(luò)參數(shù),需要等一會(huì)。ResNet18模型沒辦法直接運(yùn)用在我們的數(shù)據(jù)集上,需要做如下三點(diǎn)變換

  • 將輸入圖片的大小轉(zhuǎn)為N x C x 224 x 244
  • ResNet18網(wǎng)絡(luò)中的requires_grad置為False,使其后續(xù)不參與訓(xùn)練更新(可設(shè)置也可以不設(shè)置,看哪個(gè)效果好而定,不過不更新ResNet網(wǎng)絡(luò)參數(shù)的話訓(xùn)練更新會(huì)更快,但是通常效果會(huì)差一些)
  • ResNet18網(wǎng)絡(luò)的fc分類頭改為適合我們數(shù)據(jù)集的大小
  • # 調(diào)用預(yù)訓(xùn)練的resnet18進(jìn)行遷移學(xué)習(xí) # resnet50參數(shù)量過多,訓(xùn)練效果不太好 resnet = models.resnet18(pretrained=True) for param in resnet.parameters():param.requires_grad = False# 將resnet的輸出fc(全連接層)替換為本任務(wù)所需的格式 # 1000-->256-->relu-->dropout-->29-->softmax fc_inputs = resnet.fc.in_features resnet.fc = nn.Sequential(nn.Linear(fc_inputs, 256),nn.ReLU(),nn.Dropout(),nn.Linear(256, 29) )

    進(jìn)行遷移學(xué)習(xí)

    之后的步驟就跟通常的CNN訓(xùn)練沒有區(qū)別了,設(shè)置好參數(shù)按照模板進(jìn)行訓(xùn)練即可,由于遷移學(xué)習(xí)的效果比較好,因此這里也不需要特別設(shè)置網(wǎng)絡(luò)訓(xùn)練的參數(shù),保持默認(rèn)即可

    # 定義交叉熵?fù)p失函數(shù)和Adam優(yōu)化器(學(xué)習(xí)率,權(quán)重衰減使用默認(rèn)值) loss = nn.CrossEntropyLoss() optimizer = t.optim.Adam(resnet.parameters())def train(net, dataloader, testdataloader, optimizer, criterion, epocs=20):# 以下四個(gè)參數(shù)分別用于存儲(chǔ)訓(xùn)練和測(cè)試的損失函數(shù)值以及分類準(zhǔn)確率train_loss_arr, train_acc_arr, test_loss_arr, test_acc_arr = [], [], [], []for epoc in range(epocs):net.train()TrainLoss, TrainAcc = 0, 0for BatchIdx, (InputData, Labels) in enumerate(dataloader):Outputs = net(InputData)optimizer.zero_grad()loss = criterion(Outputs.squeeze(), Labels)loss.backward()optimizer.step()TrainLoss += loss.item()_, pred = t.max(Outputs.data, 1)TrainAcc += t.mean(pred.eq(Labels.data.view_as(pred)).type(t.FloatTensor)).item() * len(InputData)if BatchIdx % 10 == 0 and BatchIdx > 0:print('Bathch: {}/{}\tLoss: {}\tAcc: {}%'.format(BatchIdx, len(dataloader), round(TrainLoss, 2), round(100*TrainAcc/((BatchIdx+1) * InputData.shape[0]), 2)))train_acc_arr.append(100*TrainAcc/(len(dataloader)*32))train_loss_arr.append(TrainLoss)TestLoss, TestAcc = 0, 0with t.no_grad():net.eval()for BatchIdx, (InputData, Labels) in enumerate(testdataloader):Outputs = net(InputData)loss = criterion(Outputs.squeeze(), Labels)TestLoss += loss.item()_, pred = t.max(Outputs.data, 1)TestAcc += t.mean(pred.eq(Labels.data.view_as(pred)).type(t.FloatTensor)).item() * len(InputData)print('Loss: {}\tAcc: {}%'.format(round(TrainLoss, 2),round(100*TestAcc/(len(testdataloader) * 32), 2)))print('-'*60) test_acc_arr.append(100*TestAcc/(len(testdataloader)*32))test_loss_arr.append(TestLoss)return train_loss_arr, train_acc_arr, test_loss_arr, test_acc_arr# 進(jìn)行訓(xùn)練并繪制訓(xùn)練曲線 train_loss_arr, train_acc_arr, test_loss_arr, test_acc_arr = train(resnet, TrainLoader, TestLoader, optimizer, loss) fig = plt.figure() ax1 = fig.add_subplot(121) ax1.plot(train_loss_arr, label='train loss') ax1.plot(test_loss_arr, label='test loss') ax1.legend() ax1.set_title('Loss Curve') ax1.set_xlabel('epocs') ax1.set_ylabel('loss') ax2 = fig.add_subplot(122) ax2.plot(train_acc_arr, label='train acc') ax2.plot(test_acc_arr, label='test acc') ax2.legend() ax2.set_title('Accuracy Curve') ax2.set_xlabel('epocs') ax2.set_ylabel('loss') plt.show()# 打印測(cè)試集的真實(shí)/預(yù)測(cè)結(jié)果 for InputData, Labels in enumerate(TestSet):Outputs = resnet(Labels[0].unsqueeze(0))_, pred = t.max(Outputs.data, 1)pred_name = namedict[str(pred.item())]real_name = namedict[str(Labels[1].item())]print('real name: {}\t\t\t\tpredict name: {}'.format(real_name, pred_name)) t.save(resnet, r'face+\resnet.pth')

    模型分類結(jié)果

    訓(xùn)練完成后模型的分類準(zhǔn)確率訓(xùn)練集上差不多99%,測(cè)試集上最高可以到90%,還是比較符合預(yù)期了,畢竟整個(gè)網(wǎng)絡(luò)其實(shí)沒有進(jìn)行太多的調(diào)整

    lfw_test中的8張人臉照片進(jìn)行測(cè)試,其中6張正確,2張錯(cuò)誤,看了下分類錯(cuò)誤的兩張之一

    左邊是Jean Chretien(加拿大前總理),右邊是大名鼎鼎的貝克漢姆,網(wǎng)絡(luò)把總理的人臉照片錯(cuò)誤識(shí)別成了貝克漢姆。講道理,有一說一,我覺得沒啥毛病,總理也挺帥的😎😎😎

    有興趣的同學(xué)也可以了解下總理的故事,還挺勵(lì)志的。

    完整代碼GitHub地址:https://github.com/Staaaying/record-repo/tree/main/face-classfication/resnet

    總結(jié)

    以上是生活随笔為你收集整理的基于ResNet迁移学习的LFW人脸识别分类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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