1. 项目概述

人脸表情识别是人机交互、情感计算、智能监控、心理分析领域的核心基础任务。相较于物体分类、缺陷检测,表情识别高度依赖细微面部纹理、眉眼嘴局部特征变化,类间差异极小、极易混淆,对模型特征提取能力与细节感知能力要求更高。本文基于PyTorch从零搭建专属CNN网络,针对人脸灰度表情特征优化结构,完整实现FER2013数据集七类表情分类全流程。项目包含精细化人脸数据预处理、针对性数据增强、表情专用CNN模型、完整训练验证、Loss/Acc曲线可视化、样本预测可视化、错例分析,可视化维度为五套项目中最丰富,可直接用于课程大作业、毕设、竞赛 baseline。

2. 数据集介绍

FER2013(Facial Expression Recognition 2013)是业界通用的人脸表情识别标准数据集,广泛用于表情算法评测、情感计算模型训练,数据来源于真实网络人脸图像,包含光照变化、轻微侧脸、遮挡、肤色差异等真实场景干扰,贴合实际落地场景。

数据集核心参数:

  • 样本总量:35887张人脸图像

  • 图像尺寸:统一48×48单通道灰度人脸图

  • 分类类别:7类基础人脸表情

  • 具体标签:愤怒、厌恶、恐惧、开心、平淡、悲伤、惊讶

  • 数据特点:部分类别样本不均衡、恐惧与悲伤、平淡与悲伤类间相似度极高,识别难度大

  • 任务属性:细粒度情感分类,依赖人脸微特征提取

3. 环境依赖

通用深度学习环境,支持全平台CPU/GPU训练,兼容前面所有项目环境。

pip install torch torchvision matplotlib numpy -i https://pypi.tuna.tsinghua.edu.cn/simple

4. 数据集预处理与专属数据增强

针对人脸表情识别特性设计增强策略:人脸无固定朝向、轻微平移缩放不改变表情类别,因此采用轻微旋转、平移、水平翻转;摒弃剧烈变换,避免破坏五官相对位置导致表情语义失真。训练集强增强提升泛化,测试集纯净标准化,保证评测公平性。

import torch
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np

# FER2013标准化参数
mean = (0.5,)
std = (0.5,)

# 人脸专属增强:不破坏五官结构
train_transform = transforms.Compose([
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomRotation(10),
    transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
    transforms.ToTensor(),
    transforms.Normalize(mean, std)
])

test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean, std)
])

# 加载数据集
train_dataset = torchvision.datasets.FER2013(
    root="./fer2013", split="train", download=True, transform=train_transform
)
test_dataset = torchvision.datasets.FER2013(
    root="./fer2013", split="test", download=True, transform=test_transform
)

train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=0)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=0)

# 表情标签对应
emotion_labels = ["愤怒", "厌恶", "恐惧", "开心", "平淡", "悲伤", "惊讶"]
print(f"训练集样本数:{len(train_dataset)}")
print(f"测试集样本数:{len(test_dataset)}")
print("表情类别:", emotion_labels)

# 人脸样本可视化
def show_fer_sample():
    data_iter = iter(train_loader)
    images, labels = next(data_iter)
    img = torchvision.utils.make_grid(images[:8])
    img = img / 2 + 0.5
    np_img = img.numpy().transpose(1, 2, 0)
    plt.figure(figsize=(12,6))
    plt.imshow(np_img, cmap="gray")
    plt.title("FER2013人脸表情样本")
    plt.axis("off")
    plt.show()

show_fer_sample()

5. 人脸表情专用CNN网络模型

针对48×48小尺寸灰度人脸、细微表情特征设计专用深层CNN结构。多层卷积逐级提取五官边缘、纹理、五官相对位置特征,搭配BN层加速收敛、Dropout抑制过拟合,适配表情细粒度区分难点。网络参数量适中,兼顾精度与训练速度,专门适配人脸表情分类任务。

import torch.nn as nn
import torch.nn.functional as F

class FER2013_CNN(nn.Module):
    def __init__(self, num_classes=7):
        super(FER2013_CNN, self).__init__()
        # 特征提取模块1
        self.conv1 = nn.Conv2d(1, 64, kernel_size=3, padding=1)
        self.bn1 = nn.BatchNorm2d(64)
        self.conv2 = nn.Conv2d(64, 64, kernel_size=3, padding=1)
        self.bn2 = nn.BatchNorm2d(64)
        self.pool1 = nn.MaxPool2d(2, 2)
        self.drop1 = nn.Dropout(0.3)

        # 特征提取模块2
        self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
        self.bn3 = nn.BatchNorm2d(128)
        self.conv4 = nn.Conv2d(128, 128, kernel_size=3, padding=1)
        self.bn4 = nn.BatchNorm2d(128)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.drop2 = nn.Dropout(0.4)

        # 特征提取模块3
        self.conv5 = nn.Conv2d(128, 256, kernel_size=3, padding=1)
        self.bn5 = nn.BatchNorm2d(256)
        self.conv6 = nn.Conv2d(256, 256, kernel_size=3, padding=1)
        self.bn6 = nn.BatchNorm2d(256)
        self.pool3 = nn.MaxPool2d(2, 2)
        self.drop3 = nn.Dropout(0.5)

        # 分类头
        self.fc1 = nn.Linear(256 * 6 * 6, 1024)
        self.drop4 = nn.Dropout(0.5)
        self.fc2 = nn.Linear(1024, num_classes)

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = F.relu(self.bn2(self.conv2(x)))
        x = self.pool1(x)
        x = self.drop1(x)

        x = F.relu(self.bn3(self.conv3(x)))
        x = F.relu(self.bn4(self.conv4(x)))
        x = self.pool2(x)
        x = self.drop2(x)

        x = F.relu(self.bn5(self.conv5(x)))
        x = F.relu(self.bn6(self.conv6(x)))
        x = self.pool3(x)
        x = self.drop3(x)

        x = x.view(-1, 256 * 6 * 6)
        x = F.relu(self.fc1(x))
        x = self.drop4(x)
        x = self.fc2(x)
        return x

# 设备适配
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = FER2013_CNN().to(device)
print("人脸表情识别模型初始化完成")
print(model)

6. 模型训练与验证

针对表情数据集样本不均衡、难样本多的特点,采用交叉熵损失函数,Adam优化器配合适中学习率,避免步长过大跳过最优解。严格区分训练、测试模式,全程记录损失与精度数据,保证实验可复现。

import torch.optim as optim

# 超参数
EPOCHS = 50
LR = 0.0009

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=LR)

# 记录指标
train_loss_list = []
train_acc_list = []
test_loss_list = []
test_acc_list = []

for epoch in range(EPOCHS):
    # 训练
    model.train()
    train_loss = 0.0
    train_correct = 0
    total_train = 0

    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()

        outputs = model(images)
        loss = criterion(outputs, labels)

        loss.backward()
        optimizer.step()

        train_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total_train += labels.size(0)
        train_correct += (predicted == labels).sum().item()

    avg_train_loss = train_loss / len(train_loader)
    train_acc = 100 * train_correct / total_train

    # 测试
    model.eval()
    test_loss = 0.0
    test_correct = 0
    total_test = 0

    with torch.no_grad():
        for images, labels in test_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)

            test_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total_test += labels.size(0)
            test_correct += (predicted == labels).sum().item()

    avg_test_loss = test_loss / len(test_loader)
    test_acc = 100 * test_correct / total_test

    train_loss_list.append(avg_train_loss)
    train_acc_list.append(train_acc)
    test_loss_list.append(avg_test_loss)
    test_acc_list.append(test_acc)

    print(f"第{epoch+1:2d}轮 | 训练损失:{avg_train_loss:.4f} | 训练精度:{train_acc:.2f}% | 测试损失:{avg_test_loss:.4f} | 测试精度:{test_acc:.2f}%")

# 保存模型
torch.save(model.state_dict(), "fer2013_cnn_best.pth")
print("人脸表情识别模型保存成功")

7. 超强可视化模块(本项目特色)

包含训练曲线可视化 + 批量预测结果可视化 + 真实标签VS预测标签对比,是五套项目中可视化效果最丰富、报告展示效果最好的一套。

7.1 损失与精度曲线可视化

plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(train_loss_list, label="训练损失", color="#e74c3c")
plt.plot(test_loss_list, label="测试损失", color="#3498db")
plt.title("表情识别损失变化曲线")
plt.xlabel("迭代轮数")
plt.ylabel("损失值")
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(train_acc_list, label="训练精度", color="#e74c3c")
plt.plot(test_acc_list, label="测试精度", color="#3498db")
plt.title("表情识别准确率变化曲线")
plt.xlabel("迭代轮数")
plt.ylabel("准确率(%)")
plt.legend()

plt.tight_layout()
plt.show()

7.2 人脸表情预测结果对比可视化

def visualize_prediction():
    model.eval()
    data_iter = iter(test_loader)
    images, labels = next(data_iter)
    images = images.to(device)

    with torch.no_grad():
        outputs = model(images)
        _, preds = torch.max(outputs, 1)

    # 反归一化
    images = images.cpu() * 0.5 + 0.5
    images = images.numpy().transpose(0, 2, 3, 1)

    # 展示前12张预测结果
    plt.figure(figsize=(14, 8))
    for i in range(12):
        plt.subplot(3, 4, i+1)
        plt.imshow(images[i], cmap="gray")
        true_label = emotion_labels[labels[i]]
        pred_label = emotion_labels[preds[i]]
        color = "green" if true_label == pred_label else "red"
        plt.title(f"真实:{true_label}\n预测:{pred_label}", color=color)
        plt.axis("off")
    plt.tight_layout()
    plt.show()

visualize_prediction()

8. 创新优化与拓展方向

  • 引入注意力机制,聚焦眉眼嘴关键表情区域,抑制人脸背景无效信息

  • 添加混淆矩阵,精准统计各类表情错分比例,针对性优化难样本

  • 使用加权交叉熵损失,解决FER2013样本不均衡问题

  • 引入学习率衰减,提升模型后期收敛精度

  • 替换ResNet、VGG等经典网络,对比不同骨干网络表情识别性能

  • 结合摄像头实时推理,实现动态实时人脸表情识别系统

9. 常见问题解决

  • 表情识别精度偏低:该数据集本身难度高,属于正常现象,可通过注意力机制、迁移学习提升

  • 训练过拟合:提升Dropout系数、加强数据增强、降低学习率

  • 数据集下载失败:手动下载放入对应目录,关闭网络代理重试

  • 预测图标签乱码:代码内置中文字体适配,直接运行即可正常显示

 

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐