基于pytorch的人脸情绪识别

大二寒假刚自学完李沐老师的《动手学深度学习》,简单的做个应用,水平有限。

一.数据获取

网络爬虫获得数据集

使用github上开源项目 使用 爬虫工具image downloader

使用前 根据requirements.txt配置环境

相关python环境 anaconda

#激活环境
activate 你的环境名字
#进入Image-Downloader根目录
cd Image-Downloader-master
#安装环境
pip3 install -r requirements.txt

 运行image_downloader_gui.py文件后就可以弹出以下界面

得到的图片

数据预处理

保留有效数据

去除损坏图片(testdetect.py)

格式归一化(根目录下reformat_images.py)

尺寸归一化

命名归一化

opencv人脸检测,Dlib关键点检测

cascade为人脸检测器,predictor关键点检测器,传统算法,效果一般般,,只是用来练手,在训练模型的时候还需要检查一遍数据,可以使用卷积+nin提取出特征

通过关键点检测提取出图片中的人脸特征,输入网络

使用torchvision包读取数据,数据增强,不过数据增强只使用随机缩放裁剪,随机翻转,有兴趣还可以,提高曝光度,改变色温,数据增强可以提高模型的泛化能力(比如模拟不同自然光照射下的的场景)。

## 优化目标使用交叉熵,优化方法使用带动量项的SGD,学习率迭代策略为step,每隔100个epoch,变为原来的0.1倍
 image_size = 64 ##图像统一缩放大小
    crop_size = 48 ##图像裁剪大小,即训练输入大小
    nclass = 4 ##分类类别数
    model = simpleconv3(nclass) ##创建模型
    data_dir = './data' ##数据目录
    
    ## 模型缓存接口
    if not os.path.exists('models'):
        os.mkdir('models')

    ## 检查GPU是否可用,如果是使用GPU,否使用CPU
    use_gpu = torch.cuda.is_available()
    if use_gpu:
        model = model.cuda()
    print(model)

    ## 创建数据预处理函数,训练预处理包括随机裁剪缩放、随机翻转、归一化,验证预处理包括中心裁剪,归一化
    data_transforms = {
        'train': transforms.Compose([
            transforms.RandomResizedCrop(48),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
        ]),
        'val': transforms.Compose([
            transforms.Resize(image_size),
            transforms.CenterCrop(crop_size),
            transforms.ToTensor(),
            transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])
        ]),
    }

    ## 使用torchvision的dataset ImageFolder接口读取数据
    image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                              data_transforms[x]) for x in ['train', 'val']}

    ## 创建数据指针,设置batch大小,shuffle,多进程数量
    dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x],
                                                 batch_size=64,
                                                 shuffle=True,
                                                 num_workers=4) for x in ['train', 'val']}
    ## 获得数据集大小
    dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}

    ## 优化目标使用交叉熵,优化方法使用带动量项的SGD,学习率迭代策略为step,每隔100个epoch,变为原来的0.1倍
    criterion = nn.CrossEntropyLoss()
    optimizer_ft = optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
    step_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=100, gamma=0.1)##每隔step_size步,学习率变为gamma倍

模型搭建与训练

可以多尝试几个模型,自定义模型或者vgg,resnet,nin,Googlenet等多个经典模型比较并尝试解释

自定义模型

class simpleconv3(nn.Module):
    ## 初始化函数
    def __init__(self,nclass):
        super(simpleconv3,self).__init__()
        self.conv1 = nn.Conv2d(3, 12, 3, 2) #输入图片大小为3*48*48,输出特征图大小为12*23*23,卷积核大小为3*3,步长为2
        self.bn1 = nn.BatchNorm2d(12)
        self.conv2 = nn.Conv2d(12, 24, 3, 2) #输入图片大小为12*23*23,输出特征图大小为24*11*11,卷积核大小为3*3,步长为2
        self.bn2 = nn.BatchNorm2d(24)
        self.conv3 = nn.Conv2d(24, 48, 3, 2) #输入图片大小为24*11*11,输出特征图大小为48*5*5,卷积核大小为3*3,步长为2
        self.bn3 = nn.BatchNorm2d(48)
        self.fc1 = nn.Linear(48 * 5 * 5 , 1200) #输入向量长为48*5*5=1200,输出向量长为1200
        self.fc2 = nn.Linear(1200 , 128) #输入向量长为1200,输出向量长为128
        self.fc3 = nn.Linear(128 , nclass) #输入向量长为128,输出向量长为nclass,等于类别数

    ## 前向函数
    def forward(self, x):
        ## relu函数,不需要进行实例化,直接进行调用
        ## conv,fc层需要调用nn.Module进行实例化
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = F.relu(self.bn3(self.conv3(x)))
        x = x.view(-1 , 48 * 5 * 5) 
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

resnet

class Reshape(nn.Module):
    def __init__(self, *args):
        super(Reshape, self).__init__()

class GlobalAvgPool2d(nn.Module):
    # 全局平均池化层可通过将池化窗口形状设置成输入的高和宽实现
    def __init__(self):
        super(GlobalAvgPool2d, self).__init__()
    def forward(self, x):
        return F.avg_pool2d(x, kernel_size=x.size()[2:])

# 残差神经网络
class Residual(nn.Module): 
    def __init__(self, in_channels, out_channels, use_1x1conv=False, stride=1):
        super(Residual, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, stride=stride)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
        if use_1x1conv:
            self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)

    def forward(self, X):
        Y = F.relu(self.bn1(self.conv1(X)))
        Y = self.bn2(self.conv2(Y))
        if self.conv3:
            X = self.conv3(X)
        return F.relu(Y + X)

    
def resnet_block(in_channels, out_channels, num_residuals, first_block=False):
    if first_block:
        assert in_channels == out_channels # 第一个模块的通道数同输入通道数一致
    blk = []
    for i in range(num_residuals):
        if i == 0 and not first_block:
            blk.append(Residual(in_channels, out_channels, use_1x1conv=True, stride=2))
        else:
            blk.append(Residual(out_channels, out_channels))
    return nn.Sequential(*blk)

resnet = nn.Sequential(
    nn.Conv2d(1, 64, kernel_size=7 , stride=2, padding=3),
    nn.BatchNorm2d(64), 
    nn.ReLU(),
    nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
resnet.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
resnet.add_module("resnet_block2", resnet_block(64, 128, 2))
resnet.add_module("resnet_block3", resnet_block(128, 256, 2))
resnet.add_module("resnet_block4", resnet_block(256, 512, 2))
resnet.add_module("global_avg_pool", GlobalAvgPool2d()) # GlobalAvgPool2d的输出: (Batch, 512, 1, 1)
resnet.add_module("fc", nn.Sequential(Reshape(), nn.Linear(512, 7))) 

我一直觉得resnet很好用,他可以显著提高模型精度

李沐老师讲的有一句很好 resnet可以表达为f(x)=x+g(x)

其中x是上一次的输出,g(x)是接下来要对上一次输出x的处理的过程,会使模型越来越逼近期望结果y

训练函数

from __future__ import print_function, division

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import torchvision
from torchvision import datasets, models, transforms
import os
from net import simpleconv3

## 使用tensorboardX进行可视化
from tensorboardX import SummaryWriter
writer = SummaryWriter('logs') ## 创建一个SummaryWriter的示例,默认目录名字为runs

## 训练主函数
def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        for phase in ['train', 'val']:
            if phase == 'train':
                scheduler.step()
                model.train(True)  ## 设置为训练模式
            else:
                model.train(False)  ## 设置为验证模式

            running_loss = 0.0 ##损失变量
            running_accs = 0.0 ##精度变量
            number_batch = 0 ##

            ## 从dataloaders中获得数据
            for data in dataloaders[phase]:
                inputs, labels = data 
                if use_gpu:
                    inputs = inputs.cuda()
                    labels = labels.cuda()

                optimizer.zero_grad() ##清空梯度
                outputs = model(inputs) ##前向运行
                _, preds = torch.max(outputs.data, 1) ##使用max()函数对输出值进行操作,得到预测值索引
                loss = criterion(outputs, labels) ##计算损失
                if phase == 'train':
                    loss.backward() ##误差反向传播
                    optimizer.step() ##参数更新

                running_loss += loss.data.item()
                running_accs += torch.sum(preds == labels).item()
                number_batch += 1

            ## 得到每一个epoch的平均损失与精度
            epoch_loss = running_loss / number_batch
            epoch_acc = running_accs / dataset_sizes[phase]
            
            ## 收集精度和损失用于可视化
            if phase == 'train':
                writer.add_scalar('data/trainloss', epoch_loss, epoch)
                writer.add_scalar('data/trainacc', epoch_acc, epoch)
            else:
                writer.add_scalar('data/valloss', epoch_loss, epoch)
                writer.add_scalar('data/valacc', epoch_acc, epoch)

            print('{} Loss: {:.4f} Acc: {:.4f}'.format(
                phase, epoch_loss, epoch_acc))

    writer.close()
    return model

模型训练可视化

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值