深度学习项目,使用python进行表情识别,pytorch应用


前言

这个项目是以前课设用到的功能之一,参考了其他人的人的博客,自己做了一下整理
需要用到的库有:opencv,pandas,pytorch,numpy,dlib
本项目基于深度学习


一、深度学习是什么?

深度学习(DL, Deep Learning)是机器学习(ML, Machine Learning)领域中一个新的研究方向,它被引入机器学习使其更接近于最初的目标——人工智能(AI, Artificial Intelligence)。

形象来说,深度学习就是构建一个类似于人类大脑的人工神经网络,通过已有的数据不断学习,最终使得每个神经元的参数趋于完美,使其能够解决实际生活中的抽象问题。


二、数据的预处理

1.数据分类

我们所用到的数据都存放在train.csv文件里面,文件的结构如下:

在这里插入图片描述


其中,lable是单张图片的标签,不同的值对应不同的表情:

valueemotion
0angry
1disgust
2fear
3happy
4sad
5surprise
6neutral
feature是单张图片的字符串编码,每一串编码由28 * 28 = 784组字符串组成,每组字符串对应一个像素的灰度图。

在预处理阶段,我们需要将train.scv文件中的所有图像提取出来,分为train_data和val_data,分别作为训练集与验证集,并存放到各自的文件夹。
同时,建立train_data.csv与val_data.csv存放文件名到该文件标签的映射


2.代码

"""
该文件将原数据‘train.csv’分离
图片分离到train_data, val_data两个文件夹中
并创建train_data_set.csv与val_data_set.csv来标注数据
"""
import pandas as pd
import cv2 as cv
import numpy as np
import os


def classify():
    """
    将数据分为训练集与数据集并储存
    :return:
    """
    # 数据预处理
    # 将label与人脸数据作拆分
    path = './train.csv'
    all_data = pd.read_csv(path)
    # 将百分之70的数据作为训练集
    train_num = int(all_data.shape[0] * 0.7)
    train_data = all_data.loc[0: train_num]
    val_data = all_data.loc[train_num:]
    print(train_data)
    save_img_and_label(train_data, './train_data', './train_data_set.csv')
    save_img_and_label(val_data, './val_data', './val_data_set.csv')


def save_img_and_label(data: pd.DataFrame, img_save_path, csv_save_path):
    """
    保存数据中的图片并对其标注
    """
    if not os.path.exists(img_save_path):
        os.mkdir(img_save_path)

    img_name_list = []
    img_label_list = []
    for i in range(len(data)):
        # 生成文件名
        img_name = '{}.jpg'.format(i)
        # 读取csv文件中字符串类型的图片
        img_str = data[['feature']].values[i][0]
        img_label = str(data[['label']].values[i][0])
        # 分隔字符串,得到每个像素点的灰度
        img_list = img_str.split(' ')
        # 转化为darray数组并保存
        img = np.array(img_list, dtype=np.int32).reshape((48, 48))
        cv.imwrite(img_save_path + '/' + img_name, img)
        img_name_list.append(img_name)
        img_label_list.append(img_label)

    # 保存数据
    save_data = pd.DataFrame(img_label_list, index=img_name_list)
    save_data.to_csv(csv_save_path, header=False)

# 调用执行
classify()

运行完成后结果如下
在这里插入图片描述


train_data里面的东西
在这里插入图片描述


train_data_set.csv里面的东西
在这里插入图片描述


三、构建模型与训练

1.模型与代码

使用的模型参考了如下A模型
在这里插入图片描述


代码如下
文件名train_model.py

"""
训练模型
"""
import torch
import torch.utils.data as data
from torch.utils.data.dataset import T_co
import pandas as pd
import numpy as np
import cv2 as cv
import torch.nn as nn
from torch import optim


class FaceDateset(data.Dataset):
    """
    加载数据的类
    """

    def __init__(self, img_dir, data_set_path):
        self.img_dir = img_dir
        self.label_path = data_set_path
        # 读取csv
        img_names = pd.read_csv(data_set_path, header=None, usecols=[0])
        img_labels = pd.read_csv(data_set_path, header=None, usecols=[1])
        # 转化为一维np数组
        self.img_names = np.array(img_names)[:, 0]
        self.img_labels = np.array(img_labels)[:, 0]

    def __getitem__(self, index) -> T_co:
        # 记载图片路径
        img_path = self.img_dir + '/' + self.img_names[index]
        # 读取图片
        img = cv.imread(img_path)
        # 转化为灰度
        img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        # 直方图均衡
        img = cv.equalizeHist(img)
        # 标准化输入并进行归一化
        img = img.reshape((1, 48, 48)) / 255.0
        # 转化为torch图片
        img_torch = torch.from_numpy(img).type('torch.FloatTensor')
        label = self.img_labels[index]

        return img_torch, label

    def __len__(self):
        return self.img_names.shape[0]


def gaussian_weights_init(m):
    """参数初始化,采用高斯分布"""
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        m.weight.data.normal_(0.0, 0.04)


def validate(model, dataset, batch_size):
    """验证模型在验证集上的正确率"""
    val_loader = data.DataLoader(dataset, batch_size)
    result, num = 0.0, 0
    for images, labels in val_loader:
        pred = model.forward(images)
        pred = np.argmax(pred.data.numpy(), axis=1)
        labels = labels.data.numpy()
        result += np.sum((pred == labels))
        num += len(images)
    acc = result / num
    return acc


class FaceCNN(nn.Module):
    """CNN神经网络"""

    def __init__(self):
        super().__init__()
        # 第一层卷积、池化
        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=1, padding=1),  # 卷积层
            nn.BatchNorm2d(num_features=64),  # 归一化
            nn.RReLU(inplace=True),  # 激活函数
            nn.MaxPool2d(kernel_size=2, stride=2),  # 最大值池化
        )
        # 第二层卷积、池化
        self.conv2 = nn.Sequential(
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(num_features=128),
            nn.RReLU(inplace=True),
            # output:(bitch_size, 128, 12 ,12)
            nn.MaxPool2d(kernel_size=2, stride=2),
        )

        # 第三层卷积、池化
        self.conv3 = nn.Sequential(
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
            nn.BatchNorm2d(num_features=256),
            nn.RReLU(inplace=True),
            # output:(bitch_size, 256, 6 ,6)
            nn.MaxPool2d(kernel_size=2, stride=2),
        )

        # 参数初始化
        self.conv1.apply(gaussian_weights_init)
        self.conv2.apply(gaussian_weights_init)
        self.conv3.apply(gaussian_weights_init)

        # 全连接层
        self.fc = nn.Sequential(
            nn.Dropout(p=0.2),
            nn.Linear(in_features=256 * 6 * 6, out_features=4096),
            nn.RReLU(inplace=True),
            nn.Dropout(p=0.5),
            nn.Linear(in_features=4096, out_features=1024),
            nn.RReLU(inplace=True),
            nn.Linear(in_features=1024, out_features=256),
            nn.RReLU(inplace=True),
            nn.Linear(in_features=256, out_features=7),
        )

    def forward(self, x):
        """前向传播"""
        # print(x.shape)
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        # 数据扁平化
        x = x.view(x.shape[0], -1)
        y = self.fc(x)
        return y


def train(lr=0.01, weight_decay=0, batch_size=128, epochs=100):
    """训练模型"""
    # 加载数据
    train_data_set = FaceDateset('train_data', 'train_data_set.csv')
    val_data_set = FaceDateset('val_data', 'val_data_set.csv')
    train_loader = data.DataLoader(train_data_set, batch_size)
    # 构建神经网络
    model = FaceCNN()
    # 损失函数
    loss_fun = nn.CrossEntropyLoss()
    # 优化器
    optimizer = optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay)
	
	# 记录损失值与正确率
    loss_log = open('loss_log.txt', 'w')
    acc_log = open('acc_log.txt', 'w')
    
    for epoch in range(epochs):
        # 损失函数值
        print('第{}轮训练开始'.format(epoch))
        loss_rate = 0
        model.train()
        for images, labels in train_loader:
            # 梯度清零
            optimizer.zero_grad()
            # 前向转播
            out_put = model.forward(images)
            # 计算误差值
            loss_rate = loss_fun(out_put, labels)
            # 反向传播
            loss_rate.backward()
            optimizer.step()

            # 保存损失值
            loss_log.write('{}\n'.format(loss_rate.item()))
            loss_log.flush()

        print('第{}轮损失值为:{}'.format(epoch, loss_rate.item()))

        # 计算并保存正确率,保存模型
        if epoch % 5 == 0:
            acc = validate(model, val_data_set, batch_size)
            print('第{}轮正确率为:{}'.format(epoch, acc))
            acc_log.write('{}, {}\n'.format(epoch, acc))
            acc_log.flush()
            torch.save(model, './models/train_{}.pkl'.format(epoch))

    loss_log.close()
    acc_log.close()


if __name__ == '__main__':
    train()


文件名show_log.py

"""
可视化结果
"""
import matplotlib.pyplot as plt
import numpy as np


def cvt_file2np(log_file_path):
    """读取log文件并转化为np数组"""
    log = np.genfromtxt(log_file_path, delimiter=',')
    if log.ndim != 1:
        x = np.array(log[:, 0])
        y = np.array(log[:, 1])
    else:
        length = log.shape[0]
        x = np.arange(0, length)
        y = log

    return [x, y]


acc_data = cvt_file2np('acc_log.txt')
loss_data = cvt_file2np('loss_log.txt')
loss_data[1] = loss_data[1] + 0.0000001
loss_data[1] = np.log10(loss_data[1])
        

plt.subplot(121)
plt.plot(acc_data[0], acc_data[1])
plt.title('acc')
plt.xlabel('epoch')
plt.ylabel('acc')

plt.subplot(122)
plt.plot(loss_data[0], loss_data[1])
plt.title('log10(loss)')
plt.xlabel('iteration')
plt.ylabel('log10(loss)')
plt.show()


2.使用方法

首先运行train_model.py文件训练模型,这里采用的是cup运算,非常花费时间。
运行完成后模型会保存到根目录的models文件夹下,每训练5个epoch保存一次。
训练完成后,运行show_log.py将训练结果可视化,例如:
在这里插入图片描述

不难看出,训练到第35轮时正确率最高,之后数据发生了过拟合,正确率开始下降。


四、实时识别

使用opencv来打开摄像头,实现对图像中人脸的实时识别
(由于课设中不需要使用gui显示图像,所以设置了一个变量gui_open来控制是否显示gui)
代码如下:
文件名:my_face.py

"""
表情识别
"""
import cv2 as cv
import dlib
import numpy as np
import torch
from train_model import FaceCNN
from my_utils import get_face_target, labels, soft_max, cvt_R2N
import warnings
warnings.filterwarnings("ignore")


class EmotionIdentify:
    """
    用于表情识别的类
    """

    def __init__(self, gui_open=False):
        # 使用特征提取器get_frontal_face_detector
        self.detector = dlib.get_frontal_face_detector()
        # 加载CNN模型
        self.model = FaceCNN()
        self.model.load_state_dict(torch.load('./models/train_35.pkl').state_dict())
        # 建cv2摄像头对象,这里使用电脑自带摄像头,如果接了外部摄像头,则自动切换到外部摄像头
        self.cap = cv.VideoCapture(0)
        # 设置视频参数,propId设置的视频参数,value设置的参数值
        self.cap.set(3, 480)
        # 截图screenshot的计数器
        self.img = None
        self.gui_open = gui_open

    def get_img_and_predict(self):
        """
        表情识别
        :return:(img, emotion)
        """
        gray_img, left_top, right_bottom, result = None, None, None, None
        if self.cap.isOpened():
            # 从摄像头读取图像
            flag, img = self.cap.read()
            self.img = img
            img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
            # 使用人脸检测器检测每一帧图像中的人脸。并返回人脸数rects
            faces = self.detector(img_gray, 0)

            if len(faces) > 0:
            	# 只取第一张脸
                face = faces[0]
                left_top = (face.left(), face.top())
                right_bottom = (face.right(), face.bottom())
                # 转化为灰度图片并提取目标
                gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
                gray_img = get_face_target(gray_img, face)
                # 将目标大小重置为神经网络的输入大小
                gray_img = cv.resize(gray_img, (48, 48))
                target_img = gray_img.reshape((1, 1, 48, 48)) / 255.0
                img_torch = torch.from_numpy(target_img).type('torch.FloatTensor')
                result = self.model(img_torch).detach().numpy()
                result = soft_max(result)
        if self.gui_open:
            self.show(gray_img, left_top, right_bottom, result)
            return self.img, result

        raise Exception('No camera')

    def show(self, gray_img, left_top, right_bottom, result):
        """显示图像"""
        show_img = self.img
        if gray_img is not None:
            cv.imshow('gray_img', gray_img)
            color = (255, 34, 78)
            cv.rectangle(show_img, left_top, right_bottom, color, 2)
            emotion = labels[np.argmax(result)]
            cv.putText(show_img, emotion, left_top, 0, 2, color, 2)
        cv.imshow('cv', show_img)
        cv.waitKey(20)


def my_test():
    ed = EmotionIdentify(gui_open=True)
    flag = True
    while flag is not None:
        flag = ed.get_img_and_predict()
        cv.waitKey(10)
        result = flag[1]
        print(cvt_R2N(result))


if __name__ == '__main__':
    my_test()


依赖工具my_utils.py
"""
使用的工具
"""
import numpy as np

labels = {0: 'angry', 1: 'disgust', 2: 'fear', 3: 'happy',
                4: 'sad', 5: 'surprise', 6: 'neutral'}


def get_face_target(gray_img: np.ndarray, face):
    """
    从gray_img中提取面部图像
    :param gray_img:
    :param face:
    :return:
    """
    img_size = gray_img.shape
    # print(img_size)
    x1, y1, x2, y2 = face.left(), face.top(), face.right(), face.bottom()

    def reset_point(point, max_size):
        """
        重置点的位置
        :param point: 当前点的坐标
        :param max_size: 点在坐标轴的上的最大位置
        :return:
        """
        if point < 0:
            point = 0
        if point > max_size:
            point = max_size - 1
        return point

    x1 = reset_point(x1, img_size[0])
    x2 = reset_point(x2, img_size[0])
    y1 = reset_point(y1, img_size[1])
    y2 = reset_point(y2, img_size[1])
    return gray_img[y1:y2, x1:x2]


def soft_max(x):
    """classify"""
    exp_x = np.exp(x)
    sum_x = np.sum(exp_x)
    y = exp_x / sum_x
    return y


def cvt_R2N(result):
    """将神经网络预测的结果转化为0-100的数值"""
    if result is None:
        return 80
    cvt_M = [70, 20, 60, 100, 10, 90, 80]
    cvt_M = np.array(cvt_M)
    level = np.sum(cvt_M * result)
    return level

结果如下:

注:cvt_R2N()函数该项目用不到


总结

该项目的项目树如下

  • data_classfy.py
  • my_face.py
  • my_utils.py
  • show_log.py
  • train.csv
  • train_model.py
  • 10
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
Python基于PyTorch深度学习的图像识别可以通过搭建一个卷积神经网络(Convolutional Neural Network,CNN)来实现。以下是一个简单的实现步骤: 1. 准备数据集:收集有标签的图像数据集,并将其拆分为训练集和测试集。 2. 定义网络模型:使用PyTorch定义卷积神经网络模型,包括卷积层、池化层和全连接层。 3. 训练模型:使用训练集对模型进行训练,通过反向传播算法来更新模型的权重。 4. 测试模型:使用测试集对模型进行测试,计算模型的准确率和误差。 5. 优化模型:根据测试结果对模型进行调整和优化,例如增加或减少网络层数、调整学习率等。 以下是一个简单的示例代码,展示了如何使用PyTorch实现一个基于CNN的图像分类器: ```python import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms # 定义CNN网络模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(torch.relu(self.conv1(x))) x = self.pool(torch.relu(self.conv2(x))) x = x.view(-1, 16 * 5 * 5) x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x # 载入数据集 transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') # 定义损失函数和优化器 net = Net() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) # 训练网络模型 for epoch in range(2): # 遍历数据集两次 running_loss = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 2000 == 1999: # 每2000个小批量数据打印一次损失值 print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0 print('Finished Training') # 测试网络模型 correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the network on the 10000 test images: %d %%' % ( 100 * correct / total)) ``` 在这个示例代码中,我们构建了一个带有两个卷积层和三个全连接层的CNN模型,并使用CIFAR10数据集进行训练和测试。训练过程中使用了随机梯度下降(SGD)优化器和交叉熵损失函数。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值