【精选】​​深度学习:构建卷积神经网络的表情识别系统(源码&教程)

1.研究背景与意义

随着社交媒体和在线通信的普及,人们越来越多地使用表情符号来表达情感和情绪。表情识别系统的发展成为一个重要的研究领域,旨在通过计算机自动识别和理解人类的表情,从而提高人机交互的效果和用户体验。

传统的表情识别方法主要基于手工设计的特征提取和分类算法,这些方法通常需要大量的人工参与和专业知识,且对于复杂的表情模式识别效果有限。然而,随着深度学习的兴起,特别是卷积神经网络(CNN)的发展,基于深度学习的表情识别系统取得了显著的进展。

深度学习是一种模仿人脑神经网络结构和工作原理的机器学习方法。卷积神经网络是深度学习中最常用的模型之一,其通过多层卷积和池化操作,可以有效地从原始图像数据中提取特征,并进行分类和识别。在表情识别领域,卷积神经网络可以自动学习和提取图像中的表情特征,从而实现准确的表情分类和识别。

基于深度学习卷积神经网络的表情识别系统具有以下几个重要的意义:

  1. 提高表情识别的准确性:传统的表情识别方法往往依赖于人工设计的特征和分类算法,其准确性受限。而基于深度学习的表情识别系统可以自动学习和提取图像中的表情特征,从而实现更准确的表情分类和识别。

  2. 降低人工成本和专业知识要求:传统的表情识别方法需要大量的人工参与和专业知识,而基于深度学习的表情识别系统可以自动学习和提取特征,减少了人工成本和专业知识要求。

  3. 提高人机交互的效果和用户体验:表情识别系统可以应用于人机交互领域,例如智能机器人、虚拟现实和增强现实等。基于深度学习的表情识别系统可以更准确地理解用户的情感和情绪,从而提高人机交互的效果和用户体验。

  4. 推动深度学习和人工智能的发展:基于深度学习的表情识别系统是深度学习和人工智能领域的重要应用之一。通过研究和开发基于深度学习的表情识别系统,可以推动深度学习和人工智能的发展,为其他领域的研究和应用提供借鉴和启示。

综上所述,基于深度学习卷积神经网络的表情识别系统在提高表情识别准确性、降低人工成本和专业知识要求、提高人机交互效果和用户体验以及推动深度学习和人工智能的发展等方面具有重要的研究背景和意义。随着深度学习技术的不断发展和应用,基于深度学习的表情识别系统有望在实际应用中发挥更大的作用,并为人们提供更好的用户体验和服务。

2.图片演示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.视频演示

基于深度学习卷积神经网络的表情识别系统_哔哩哔哩_bilibili

4.人脸表情识别流程

传统的人脸表情识别一般包括人脸检测、表情特征提取、表情分类三个主要步
骤。基于传统方法的表情识别流程如图 a)所示。
在这里插入图片描述

构建一个传统的表情识别系统,第一步通过人脸检测算法对输入的静态图像或图像视频序列进行人脸检测,第二步对检测到的人脸图像进行特征提取,去除一些与表情无关的信息,最后对测试样本进行表情分类。
基于卷积神经网络的人脸表情识别将表情特征提取过程与表情分类过程整合到一起,通过训练表情数据集来提取不同的表情特征,基于卷积神经网络的表情识别流程如图b)所示。人脸检测是表情识别中较为重要的一步,常用的人脸检测算法如下。
在这里插入图片描述

5.核心代码讲解

5.1 CK.py


class CK(data.Dataset):
    def __init__(self, split='Training', fold = 1, transform=None):
        self.transform = transform
        self.split = split  # training set or test set
        self.fold = fold # the k-fold cross validation
        self.data = h5py.File('./data/CK_data.h5', 'r', driver='core')

        number = len(self.data['data_label']) #981
        sum_number = [0,135,312,387,594,678,927,981] # the sum of class number
        test_number = [12,18,9,21,9,24,6] # the number of each class

        test_index = []
        train_index = []

        for j in range(len(test_number)):
            for k in range(test_number[j]):
                if self.fold != 10: #the last fold start from the last element
                    test_index.append(sum_number[j]+(self.fold-1)*test_number[j]+k)
                else:
                    test_index.append(sum_number[j+1]-1-k)

        for i in range(number):
            if i not in test_index:
                train_index.append(i)

        print(len(train_index),len(test_index))

        # now load the picked numpy arrays
        if self.split == 'Training':
            self.train_data = []
            self.train_labels = []
            for ind in range(len(train_index)):
                self.train_data.append(self.data['data_pixel'][train_index[ind]])
                self.train_labels.append(self.data['data_label'][train_index[ind]])

        elif self.split == 'Testing':
            self.test_data = []
            self.test_labels = []
            for ind in range(len(test_index)):
                self.test_data.append(self.data['data_pixel'][test_index[ind]])
                self.test_labels.append(self.data['data_label'][test_index[ind]])

    def __getitem__(self, index):
        if self.split == 'Training':
            img, target = self.train_data[index], self.train_labels[index]
        elif self.split == 'Testing':
            img, target = self.test_data[index], self.test_labels[index]
        img = img[:, :, np.newaxis]
        img = np.concatenate((img, img, img), axis=2)
        img = Image.fromarray(img)
        if self.transform is not None:
            img = self.transform(img)
        return img, target

    def __len__(self):
        if self.split == 'Training':
            return len(self.train_data)
        elif self.split == 'Testing':
            return len(self.test_data)

该程序文件名为CK.py,主要是一个名为CK的数据集类。该类继承自torch.utils.data.Dataset类,用于加载CK+数据集。

该类的构造函数有三个参数:split、fold和transform。split参数用于指定数据集是训练集还是测试集,默认为训练集。fold参数用于指定k-fold交叉验证的折数,默认为1。transform参数是一个可调用的函数/变换,用于对图像进行变换,默认为None。

在构造函数中,首先通过h5py库打开名为CK_data.h5的HDF5文件,该文件包含了CK+数据集的图像和标签数据。然后根据split参数选择加载训练集数据还是测试集数据。

加载训练集数据时,根据fold参数确定训练集的索引。根据数据集的类别数和每个类别的图像数量,计算出每个类别在训练集中的图像数量。根据fold参数和类别数量,确定每个类别在训练集中的起始索引和结束索引。根据起始索引和结束索引,将训练集的图像数据和标签数据加载到self.train_data和self.train_labels中。

加载测试集数据时,根据fold参数确定测试集的索引。根据数据集的类别数和每个类别的图像数量,计算出每个类别在测试集中的图像数量。根据fold参数和类别数量,确定每个类别在测试集中的起始索引和结束索引。根据起始索引和结束索引,将测试集的图像数据和标签数据加载到self.test_data和self.test_labels中。

该类还实现了__getitem__和__len__方法。__getitem__方法用于获取指定索引的图像和标签数据,并进行一些预处理操作,如将图像数据转换为PIL图像、进行图像通道的拼接和应用指定的变换。__len__方法用于返回训练集或测试集的长度。

总结:该程序文件定义了一个名为CK的数据集类,用于加载CK+数据集的图像和标签数据。可以根据split参数指定加载训练集或测试集的数据,并根据fold参数指定k-fold交叉验证的折数。加载的数据可以通过__getitem__方法获取,并可以应用指定的变换进行预处理。

5.2 fer.py


class FER2013(data.Dataset):
    def __init__(self, split='Training', transform=None):
        self.transform = transform
        self.split = split  # training set or test set
        self.data = h5py.File('./data/data.h5', 'r', driver='core')
        # now load the picked numpy arrays
        if self.split == 'Training':
            self.train_data = self.data['Training_pixel']
            self.train_labels = self.data['Training_label']
            self.train_data = np.asarray(self.train_data)
            self.train_data = self.train_data.reshape((28709, 48, 48))

        elif self.split == 'PublicTest':
            self.PublicTest_data = self.data['PublicTest_pixel']
            self.PublicTest_labels = self.data['PublicTest_label']
            self.PublicTest_data = np.asarray(self.PublicTest_data)
            self.PublicTest_data = self.PublicTest_data.reshape((3589, 48, 48))

        else:
            self.PrivateTest_data = self.data['PrivateTest_pixel']
            self.PrivateTest_labels = self.data['PrivateTest_label']
            self.PrivateTest_data = np.asarray(self.PrivateTest_data)
            self.PrivateTest_data = self.PrivateTest_data.reshape((3589, 48, 48))

    def __getitem__(self, index):
        if self.split == 'Training':
            img, target = self.train_data[index], self.train_labels[index]
        elif self.split == 'PublicTest':
            img, target = self.PublicTest_data[index], self.PublicTest_labels[index]
        else:
            img, target = self.PrivateTest_data[index], self.PrivateTest_labels[index]

        img = img[:, :, np.newaxis]
        img = np.concatenate((img, img, img), axis=2)
        img = Image.fromarray(img)
        if self.transform is not None:
            img = self.transform(img)
        return img, target

    def __len__(self):
        if self.split == 'Training':
            return len(self.train_data)
        elif self.split == 'PublicTest':
            return len(self.PublicTest_data)
        else:
            return len(self.PrivateTest_data)

这个程序文件是用来处理FER2013数据集的。它定义了一个名为FER2013的类,继承自torch.utils.data.Dataset类,用于创建FER2013数据集。

FER2013数据集包含训练集、测试集和验证集。在FER2013类的构造函数中,可以通过传入参数split来选择创建训练集、测试集还是验证集的数据集对象。同时可以传入一个可选的transform参数,用于对图像进行预处理。

在构造函数中,程序会加载一个名为data.h5的HDF5文件,该文件包含了FER2013数据集的像素数据和标签数据。根据split参数的不同,程序会将数据集分别存储在train_data、train_labels、PublicTest_data、PublicTest_labels、PrivateTest_data和PrivateTest_labels这些成员变量中。

在__getitem__方法中,根据split参数的不同,程序会返回对应数据集中的图像和标签。图像会经过一系列的处理,包括将图像转换为PIL图像对象、进行预处理等。

在__len__方法中,根据split参数的不同,程序会返回对应数据集的长度。

总之,这个程序文件是用来创建FER2013数据集对象的,可以根据需要选择训练集、测试集或验证集,并对图像进行预处理。

5.3 k_fold_train.py

class TrainModel:
    def __init__(self):
        pass
    
    def train(self):
        for i in range(10):
            cmd = 'python mainpro_CK+.py --model VGG19 --bs 32 --lr 0.01 --fold %d' %(i+1)
            os.system(cmd)
        print("Train VGG19 ok!")

这个程序文件名为k_fold_train.py,它的功能是使用k折交叉验证训练一个VGG19模型。程序首先导入了os模块。然后,通过一个循环,执行了10次以下操作:构建一个命令字符串cmd,其中包含了调用mainpro_CK+.py脚本的命令以及一些参数,其中fold参数的值在每次循环中递增1。然后,使用os.system函数执行这个命令。最后,打印出"Train VGG19 ok!"的提示信息。

5.4 plot_CK+_confusion_matrix.py

class ConfusionMatrixPlotter:
    def __init__(self, dataset, model):
        self.dataset = dataset
        self.model = model
        self.class_names = ['Angry', 'Disgust', 'Fear', 'Happy', 'Sad', 'Surprise', 'Contempt']
        self.cut_size = 44
        self.transform_test = transforms.Compose([
            transforms.TenCrop(self.cut_size),
            transforms.Lambda(lambda crops: torch.stack([transforms.ToTensor()(crop) for crop in crops])),
        ])
        self.net = self._get_model()
        self.all_targets = None
        self.all_predicted = None

    def _get_model(self):
        if self.model == 'VGG19':
            return VGG('VGG19')
        elif self.model == 'Resnet18':
            return ResNet18()

    def _load_checkpoint(self, fold):
        path = os.path.join(self.dataset + '_' + self.model,  '%d' %(fold+1))
        checkpoint = torch.load(os.path.join(path, 'Test_model.t7'))
        self.net.load_state_dict(checkpoint['net'])
        self.net.cuda()
        self.net.eval()

    def _get_testloader(self, fold):
        testset = CK(split='Testing', fold=fold+1, transform=self.transform_test)
        return torch.utils.data.DataLoader(testset, batch_size=5, shuffle=False, num_workers=1)

    def _compute_accuracy(self, targets, predicted):
        correct = predicted.eq(targets.data).cpu().sum()
        total = targets.size(0)
        acc = 100. * correct / total
        return acc

    def _concatenate_predictions(self, predicted, targets):
        if self.all_predicted is None and self.all_targets is None:
            self.all_predicted = predicted
            self.all_targets = targets
        else:
            self.all_predicted = torch.cat((self.all_predicted, predicted), 0)
            self.all_targets = torch.cat((self.all_targets, targets), 0)

    def _plot_confusion_matrix(self, cm, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues):
        if normalize:
            cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
            print("Normalized confusion matrix")
        else:
            print('Confusion matrix, without normalization')

        print(cm)

        plt.imshow(cm, interpolation='nearest', cmap=cmap)
        plt.title(title, fontsize=16)
        plt.colorbar()
        tick_marks = np.arange(len(self.class_names))
        plt.xticks(tick_marks, self.class_names, rotation=45)
        plt.yticks(tick_marks, self.class_names)

        fmt = '.2f' if normalize else 'd'
        thresh = cm.max() / 2.
        for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
            plt.text(j, i, format(cm[i, j], fmt),
                     horizontalalignment="center",
                     color="white" if cm[i, j] > thresh else "black")

        plt.ylabel('True label', fontsize=18)
        plt.xlabel('Predicted label', fontsize=18)
        plt.tight_layout()

    def plot(self):
        for i in range(10):
            print("%d fold" % (i+1))
            self._load_checkpoint(i)
            testloader = self._get_testloader(i)

            for batch_idx, (inputs, targets) in enumerate(testloader):
                bs, ncrops, c, h, w = np.shape(inputs)
                inputs = inputs.view(-1, c, h, w)
                inputs, targets = inputs.cuda(), targets.cuda()
                inputs, targets = Variable(inputs, volatile=True), Variable(targets)
                outputs = self.net(inputs)
                outputs_avg = outputs.view(bs, ncrops, -1).mean(1)  # avg over crops
                _, predicted = torch.max(outputs_avg.data, 1)
                acc = self._compute_accuracy(targets, predicted)
                self._concatenate_predictions(predicted, targets)
                print("accuracy: %0.3f" % acc)

        matrix = confusion_matrix(self.all_targets.data.cpu().numpy(), self.all_predicted.cpu().numpy())
        np.set_printoptions(precision=2)

        plt.figure(figsize=(10, 8))
        self._plot_confusion_matrix(matrix, classes=self.class_names, normalize=False,
                                    title='Confusion Matrix (Accuracy: %0.3f%%)' % acc)
        plt.savefig(os.path.join(self.dataset + '_' + self.model, 'Confusion Matrix.png'))
        plt.close()

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='PyTorch CK+ CNN Training')
    parser.add_argument('--dataset', type=str, default='CK+', help='CNN architecture')
    parser.add_argument('--model', type=str, default='VGG19', help='CNN architecture')
    opt = parser.parse_args()

    plotter = ConfusionMatrixPlotter(opt.dataset, opt.model)
    plotter.plot()

这个程序文件的作用是绘制CK+数据集的混淆矩阵。它使用了PyTorch框架和一些相关的库来加载模型、进行预测和计算混淆矩阵。程序首先定义了一些必要的参数和函数,包括数据集的类别、模型的选择、绘制混淆矩阵的函数等。然后,程序加载模型并对测试集进行预测,计算出预测准确率,并将预测结果和真实标签保存起来。最后,程序根据预测结果和真实标签计算混淆矩阵,并将其绘制出来保存为图片文件。

5.5 plot_fer2013_confusion_matrix.py


class ConfusionMatrixPlotter:
    def __init__(self, model, dataset, split):
        self.model = model
        self.dataset = dataset
        self.split = split
        self.class_names = ['Angry', 'Disgust', 'Fear', 'Happy', 'Sad', 'Surprise', 'Neutral']
        self.cut_size = 44
        self.transform_test = transforms.Compose([
            transforms.TenCrop(self.cut_size),
            transforms.Lambda(lambda crops: torch.stack([transforms.ToTensor()(crop) for crop in crops])),
        ])

    def plot_confusion_matrix(self, cm, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues):
        if normalize:
            cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
            print("Normalized confusion matrix")
        else:
            print('Confusion matrix, without normalization')

        print(cm)

        plt.imshow(cm, interpolation='nearest', cmap=cmap)
        plt.title(title, fontsize=16)
        plt.colorbar()
        tick_marks = np.arange(len(self.class_names))
        plt.xticks(tick_marks, self.class_names, rotation=45)
        plt.yticks(tick_marks, self.class_names)

        fmt = '.2f' if normalize else 'd'
        thresh = cm.max() / 2.
        for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
            plt.text(j, i, format(cm[i, j], fmt),
                     horizontalalignment="center",
                     color="white" if cm[i, j] > thresh else "black")

        plt.ylabel('True label', fontsize=18)
        plt.xlabel('Predicted label', fontsize=18)
        plt.tight_layout()

    def plot(self):
        if self.model == 'VGG19':
            net = VGG('VGG19')
        elif self.model == 'Resnet18':
            net = ResNet18()

        path = os.path.join(self.dataset + '_' + self.model)
        checkpoint = torch.load(os.path.join(path, self.split + '_model.t7'))

        net.load_state_dict(checkpoint['net'])
        net.cuda()
        net.eval()
        Testset = FER2013(split=self.split, transform=self.transform_test)
        Testloader = torch.utils.data.DataLoader(Testset, batch_size=64, shuffle=False)
        correct = 0
        total = 0
        all_target = []
        for batch_idx, (inputs, targets) in enumerate(Testloader):
            bs, ncrops, c, h, w = np.shape(inputs)
            inputs = inputs.view(-1, c, h, w)
            inputs, targets = inputs.cuda(), targets.cuda()
            inputs, targets = Variable(inputs, volatile=True), Variable(targets)
            outputs = net(inputs)

            outputs_avg = outputs.view(bs, ncrops, -1).mean(1)  # avg over crops
            _, predicted = torch.max(outputs_avg.data, 1)

            total += targets.size(0)
            correct += predicted.eq(targets.data).cpu().sum()
            if batch_idx == 0:
                all_predicted = predicted
                all_targets = targets
            else:
                all_predicted = torch.cat((all_predicted, predicted), 0)
                all_targets = torch.cat((all_targets, targets), 0)

        acc = 100. * correct / total
        print("accuracy: %0.3f" % acc)

        matrix = confusion_matrix(all_targets.data.cpu().numpy(), all_predicted.cpu().numpy())
        np.set_printoptions(precision=2)

        plt.figure(figsize=(10, 8))
        self.plot_confusion_matrix(matrix, classes=self.class_names, normalize=True,
                                   title=self.split + ' Confusion Matrix (Accuracy: %0.3f%%)' % acc)
        plt.savefig(os.path.join(path, self.split + '_cm.png'))
        plt.close()


这个程序文件的目的是绘制FER2013数据集中PublicTest和PrivateTest的混淆矩阵。它首先导入所需的库和模块,然后定义了一个用于绘制混淆矩阵的函数plot_confusion_matrix。接下来,它解析命令行参数,包括模型名称、数据集名称和划分名称。然后,它加载指定模型的权重,并将模型移动到GPU上进行评估。然后,它加载测试数据集,并使用模型对数据进行预测。最后,它计算准确率、绘制混淆矩阵,并将结果保存为图片文件。

5.6 preprocess_CK+.py


class CKData:
    def __init__(self, ck_path):
        self.ck_path = ck_path
        self.data_x = []
        self.data_y = []

    def load_data(self):
        anger_path = os.path.join(self.ck_path, 'anger')
        disgust_path = os.path.join(self.ck_path, 'disgust')
        fear_path = os.path.join(self.ck_path, 'fear')
        happy_path = os.path.join(self.ck_path, 'happy')
        sadness_path = os.path.join(self.ck_path, 'sadness')
        surprise_path = os.path.join(self.ck_path, 'surprise')
        contempt_path = os.path.join(self.ck_path, 'contempt')

        files = os.listdir(anger_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(anger_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(0)

        files = os.listdir(disgust_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(disgust_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(1)

        files = os.listdir(fear_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(fear_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(2)

        files = os.listdir(happy_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(happy_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(3)

        files = os.listdir(sadness_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(sadness_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(4)

        files = os.listdir(surprise_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(surprise_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(5)

        files = os.listdir(contempt_path)
        files.sort()
        for filename in files:
            I = skimage.io.imread(os.path.join(contempt_path,filename))
            self.data_x.append(I.tolist())
            self.data_y.append(6)

    def save_data(self, datapath):
        datafile = h5py.File(datapath, 'w')
        datafile.create_dataset("data_pixel", dtype = 'uint8', data=self.data_x)
        datafile.create_dataset("data_label", dtype = 'int64', data=self.data_y)
        datafile.close()

        print("Save data finish!!!")


这个程序文件的目的是为CK+数据集创建数据和标签。CK+数据集包含了7种情绪(愤怒、厌恶、恐惧、快乐、悲伤、惊讶、蔑视),每种情绪都有不同数量的图像。

程序首先定义了CK+数据集的路径,包括每种情绪的文件夹路径。

然后,程序创建了两个空列表data_xdata_y,用于存储数据和标签信息。

接下来,程序遍历每个情绪文件夹中的图像文件,并将图像读取为numpy数组,并将其添加到data_x列表中。同时,根据情绪的顺序,将相应的标签(0到6)添加到data_y列表中。

最后,程序将data_xdata_y保存为HDF5文件(CK_data.h5),并打印保存完成的消息。

总结起来,这个程序文件的功能是将CK+数据集的图像数据和标签保存为HDF5文件。

6.系统整体结构

整体功能和构架概述:

该表情识别系统基于深度学习卷积神经网络,用于识别人脸表情。系统包含多个程序文件,每个文件负责不同的功能,如数据预处理、模型训练、模型评估和可视化等。以下是每个文件的功能概述:

文件名功能
CK.py加载CK+数据集的图像和标签数据
fer.py加载FER2013数据集的图像和标签数据
k_fold_train.py执行k-fold交叉验证的训练过程
mainpro_FER.py主程序,用于训练和测试FER2013数据集
plot_CK+_confusion_matrix.py绘制CK+数据集的混淆矩阵
plot_fer2013_confusion_matrix.py绘制FER2013数据集的混淆矩阵
preprocess_CK+.py将CK+数据集的图像和标签保存为HDF5文件
preprocess_fer2013.py将FER2013数据集的图像和标签保存为HDF5文件
train.py训练模型的主程序
transformer.py数据预处理的变换函数
ui.py用户界面模块,用于交互式操作
utils.py包含一些辅助函数和工具函数
visualize.py可视化工具,用于绘制图像和结果
models\resnet.pyResNet模型的定义和实现
models\vgg.pyVGG模型的定义和实现
models_init_.py模型初始化文件
transforms\functional.py数据预处理的函数实现
transforms\transforms.py数据预处理的变换类实现
transforms_init_.py数据预处理初始化文件

这些文件共同构成了一个完整的表情识别系统,包括数据集加载、数据预处理、模型训练、模型评估和结果可视化等功能。

7.深度学习常识

卷积神经网络结构

1962年日本科学家通过对猫视觉皮层的研究发现,人们对外界的认知是从局部到整体的,并提出了感受野的概念。卷积神经网络是一种用来处理相似结构的数据的神经网络,如时间序列数据和图像数据。

(1)卷积层

卷积计算是卷积神经网络的核心操作,也是一种特殊的线性运算。卷积层经过多个卷积核的计算后形成多个特征图(feature map),图2-4为一个卷积计算的示例,其中输入图像的尺寸为3×3,红色方块表示一个尺寸为2×2的卷积核,滑动步长为1,生成特征图的尺寸为2×2,虚线框内表示卷积计算过程。

在这里插入图片描述

设输入图片的尺寸为W×H,卷积核尺寸为K×K,滑动步长为S,填充(Padding)为Р,输出特征图的尺寸为W×H,,则W与H的计算公式如下:

输出特征图的通道数等于卷积核的个数,填充的具体操作是在输入图像的外围补0,补一层,Padding记为1。填充的作用是防止图像边缘信息的丢失。

(2)池化层

池化层又称下采样层,可以减少输入数据的空间大小。最大池化和平均池化是两种使用频率最高的池化方法。最大池化方法是保留池化区域元素的最大值,平均池化方法是保留池化区域元素的平均值。最大池化和平均池化的示意图如图2-5所示,图中,两种池化方法的尺寸均为2x2,步长均为2。根据步长的大小可以将池化分为重叠池化和非重叠池化,池化层可以降低特征图的维度,减少计算量,对图像变换和噪声的鲁棒性更强。
在这里插入图片描述

(3)全连接层

全连接层通常放在卷积神经网络的最后几层,在网络中起到“分类器”的作用。图2-6表示一个四层的前馈神经网络,该网络采用的就是全连接方式。从中可以看出,前馈神经网络每一层的神经元都与其上一层的每个神经元相连。卷积神经网络的参数量主要集中在全连接层,占比高达80%,容易造成参数冗余。

在这里插入图片描述

(4)激活函数层

激活函数层通常放在卷积层之后,池化层之前。激活函数层可以增加卷积神经网络的非线性,让卷积神经网络可以解决更复杂的问题。如果没有激活函数层,无论有多少卷积层,都是输入和输出的线性组合,无法形成更复杂的表达空间。Sigmoid、Tanh、Relu是三种常用的激活函数。Sigmoid函数的数学计算式为o(z)=1/1+e",导数的数学计算式为o(z)=o(z)(1-o(z)),其函数图像和导数图像

在这里插入图片描述

8.卷积神经网络

Resnet

Resnet的出现为卷积神经网络向更多层数的发展提供了可能,具有里程碑意义。Resnet 通过捷径连接来解决过多层数引起的梯度消失及网络误差增大的问题,该网络由多个残差块组成,1.2.2小节中叙述了残差块的结构,实际的做法是在残差块中加入1x1卷积结构,起到改变输出通道的作用,新的模块被称为 Bottleneck结构,Bottleneck 的示意如所示。
在这里插入图片描述

常见的Rsenet的版本有 Resnet18、Resnet34、Resnet50、Resnet101等更多层的结构,每个版本的 Resnet 的前两层均为一个卷积层和一个最大池化层,最后两层均为一个全局平均池化层和一个全连接层。其中卷积核的尺寸为7×7,步长为2,最大池化层的尺寸为3×3,步长为2。中间有四个模块,每个模块含有不同个数的残差块或Bottleneck 结构。Resnet的输入尺寸为224×224。

VGG网络

VGG是最经典的卷积神经网络之一, VGG网络分析了网络层数与网络识别效果的关系,使用多个3×3的小尺寸卷积核与2×2的最大池化层构建了多个不同深度的卷积神经网络。VGG卷积神经网络简单灵活、拓展性强,在多个图像识别任务上取得了很好的效果。与Alexnet 相比,VGG 网络在不同数据集上的泛化能力较为出色,与Inception 网络和 Resnet 网络相比,VGG 网络进行改进时更为灵活。然而传统的VGG卷积神经网络含有庞大的参数量和计算量,需要大量的内存空间和计算资源,实际应用时较为不便。
在这里插入图片描述

VGG网络的核心思想是通过叠堆小尺度卷积核来构造更深的卷积神经网络,以此来提升模型表现力,对比Alexnet网络,VGG 网络主要做了以下改进:
(1)移除原有的11×11,5x5卷积核,全部采用3×3的小尺度卷积核。两个串联的3x3卷积核与一个5x5的卷积核具有相同的感受野,三个串联的3x3的卷积核与一个7×7的卷积核具有相同的感受野。多次卷积可以增加网络的非线性,
并且参数量更少。假设输出的通道数为M﹐若经过一个5x5卷积,则对应的参数量为25M ,若经过两个3×3卷积,则对应的参数量为18M,由此可以看出,两个3×3卷积带来的参数量更少。
(2)移除原有的局部响应归一化层(LRN),通过实验对比发现,加入LRN层后,不但没有提升网络性能,反而会额外增加运行内存和计算时间。
(3)将最大池化层的尺寸由3x3变成2×2,相比3×3的最大池化层,2×2的最大池化层可以保留更多的特征信息。

传统的VGG网络包含六个版本,包括VGG11和带有LRN层的VGG11、VGG13、VGG16和带有1×1卷积的 VGG16、以及 VGG19。VGG网络的输入图像尺寸为224×224,每个版本具有相同的最大池化层和全连接层,都包含5个卷积模块。最大池化层在每个卷积模块之后,尺寸为2×2,全连接层都包含4096个神经元。VGG11的前两个卷积模块只有一个卷积层,后三个卷积模块都包含两个卷积层,卷积模块中卷积核的个数依次为64、128、256、512、512。带有LRN层的VGG11在第一个卷积层后加入LRN。VGG13的每个卷积模块都包含两个卷积层,其余结构与VGG11相同。VGG16与 VGG13相比,后三个卷积模块均含有三个卷积层,其余结构与VGG13相同。VGG16的另一个版本引入了lx1卷积,1×1卷积可以在不影响卷积层感受野的情况下增加网络的非线性。VGG19 与 VGG16相比,后三个卷积模块都包含四个卷积层,其余结构与VGG16相同。常用的三个版本为VGG13、VGG16、VGG19。
在这里插入图片描述

增加注意力机制

Transformer机制最初是由Bengio团队提出的,用于机器翻译,并在许多NLP任务中建立了最先进的技术。为了使Transformer也适用于计算机视觉任务,已经进行了一些修改。只在每个查询像素的局部邻域中应用自注意,而不是全局应用。本文提出了稀疏Transformer,它采用可扩展的近似来实现全局自注意。最近,视觉Transformer 通过直接将具有全局自关注的Transformer直接应用于全尺寸图像,实现了最先进的图像网分类技术。我们采用Transformer的核心思想,利用注意机制设计了Transformer层模块。Transformer层的结构如图1-3所示。变形器层包含多头注意结构。多头注意力模块的结构如图所示。

在这里插入图片描述

多头自注意力模块是设计特征融合网络的基本组成部分。计算公式(1)如下:
在这里插入图片描述

将注意机制扩展到多个头部,使该机制能够考虑不同的注意分布,并使模型关注信息的不同方面。多头注意机制见下公式(2):
在这里插入图片描述

在模型通过迁移学习之后,微表达样本被馈送到微调模型并被分类为识别。VGG比传统CNN效率更高、延迟更小的主要原因是提出了可分离卷积。可分离卷积具有足够的通用性,可以在大多数视觉任务中取代网络模型。易于培训,有效减少网络模型的大小,减少延迟。然而,问题是可分离卷积仍然局限于空间,并且难以提取全局特征。Vit在作为变压器的计算机视觉平面的图像分类问题中获得了良好的结果。与Transformer的情况一样,每个块具有位置编码,该编码允许一维学习来保存空间信息。组件作为接头嵌入编码器中。vit插入可检索的类别标识符,该标识符使用Transformer编码器的输出状态作为性能分类标准。此外,二维放置方法补充了预训练的位置编码,以保持与任意分辨率图像的输入一致的块顺序,并获得全局特征。然而,没有引入图像中固有的感应偏差,并且当训练数据不足时不可能将其概括。

9.数据集的处理和训练

CK+数据集

CK+数据集是使用最广泛的实验室型数据集,由CK数据集扩展而来,包含123个不同种族的参与者。CK+数据集共有593个图片序列,这些序列的持续时间为10帧到60帧中的任意值,变化方式为从中性到其他表情峰值,实验中常使用每个序列的最后三帧作为样本图片。与JAFFE 数据集相同的是,CK+数据集同样包含七种面部表情,与JAFFE数据集不同的是,CK+数据集中没有中性表情,新增加了蔑视表情,同时年龄、种族、性别组成更为丰富。原始CK+数据集有很多与表情无关的信息﹐需要使用人脸检测并统一剪裁成大小为48×48的图片,图为处理后的CK+数据集的样本示例。
在这里插入图片描述

FER2013Plus 数据集

FER2013数据集是从互联网收集而来,每张图片剪裁后的大小为48×48,共有35887张图片,包含愤怒、厌恶、害怕、开心、悲伤、惊讶、中性七种表情。FFER2013同样属于非受控条件数据集,但是其标注准确率不高。为此 Barsoum等人l51重新标注了FER2013数据集,提出了FER2013Plus数据集。每张图片采用十名标注者进行标注,最后投票决定是哪一种表情。研究人员人发现,标注者人数越多,标注一致性越高。FER2013Plus数据集新增了蔑视表情、“未知表情”、“不是人脸”三个类别。由于“未知表情”和“不是人脸”对实际的研究没有意义,通常FER2013Plus数据集包含八种面部表情。图3-4是FER2013Plus和FER2013的标签对比图,黑色标签为FER2013数据集的样本标注,红色标签为FER2013Plus数据集重新标注后的样本标签,可以看出FER2013数据集出现了明显的标注错误,FER2013Plus的标注更为准确。
在这里插入图片描述

10.训练结果分析

表3-3和表3-4表示改进的VGGG13在两个数据集上的混淆矩阵,混淆矩阵可以显示模型在进行预测时会对哪一类别产生混淆,横轴为预测标签,纵轴为真实标签,可以更好的呈现算法的性能,图表示两个数据集上每类表情的识别率。
在这里插入图片描述

由表3-3可知,在RAF-DB数据集上,M-VUG13 在L心、T、心了040种表情的识别率超过了84%,其中开心表情取得了最高的识别率,达到了94.6%。而厌恶和害怕两种表情的识别率明显低于其他五种表情,仅为55%和56.76%。厌恶表情有13.13%的概率被认为是中性表情,有12.5%的概率被认为成悲伤表情。害怕表情有12.16%的概率被认为是惊讶表情。
在这里插入图片描述

由表3-4可知,在FER2013Plus 数据集上,M-VGG13在开心和中性两种表情的识别率达到了93%以上,而在蔑视和厌恶两种表情的识别率仅为31.25%和44.44%,这是因为FER2013Plus训练集中两种表情样本数量较少。有些表情很容易被识别错误,例如厌恶表情有38.89%的概率被认为足愤芯衣情,舌"旧衣月1月./由万穴员认为是惊讶表情。这种高度的混淆在现买生活中是很典型的,即使定八’较区分这些表情。由图3-10可以发现,通常开心、中性、惊讶三种表情的识别率较高,其中开心表情是所有表情中识别率最高的,一方面由于开心表情的样本数量最多,另一方面说明与其他表情特征相比,开心表情的特征更容易识别。而蔑视、厌恶、害怕三种表情的识别率较低,其中蔑视表情是所有表情中识别率最低的,这是因为蔑视表情的样本数量最少。
在这里插入图片描述

11.系统整合

下图完整源码&数据集&环境部署视频教程&自定义UI界面

在这里插入图片描述

参考博客《基于深度学习卷积神经网络的表情识别系统》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/182300.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java之《ATM自动取款机》(面向对象)

《JAVA编程基础》项目说明 一、项目名称: 基于JAVA控制台版本银行自动取款机 项目要求: 实现银行自动取款机的以下基本操作功能:读卡、取款、查询。(自动取款机中转账、修改密码不作要求) 具体要求: 读卡…

github批量仓库克隆,git clone某个用户的所有仓库

利用github的api工具, 首先拿到用户名为kevin的所有仓库的url: curl "https://api.github.com/users/kevin/repos?per_page100&&page1" | grep -w clone_url >clone.txt过滤一下: grep -o https://[^"]* clone…

Vue学习笔记-Object.defineproperty函数

文章目录 前文提要Object.defineProperty作用Object.defineProperty参数使用例图getter,也就是get函数setter,也就是set函数 前文提要 本人仅做个人学习记录,如有错误,请多包涵 Object.defineProperty作用 当在js中声明了一个变…

老鸟总结,软件测试工程师职业发展规划路线,入门到冲击大厂...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、测试工程师发展…

1999-2021年地级市城镇居民人均消费性支出数据

1999-2021年地级市城镇居民人均消费性支出数据 1、时间:1999-2021年 2、指标:城镇居民人均消费性支出 3、范围:290个地级市 4、来源:城市年鉴、地级市统计公报 5、指标解释: 城镇居民人均消费性支出:指…

JavaScript基础—引入方式、注释和结束符、输入和输出、变量、常量、数据类型、检测数据类型、类型转换、综合案例—用户订单信息

版本说明 当前版本号[20231123]。 版本修改说明20231123初版 目录 文章目录 版本说明目录JavaScript 基础 - 第1天介绍引入方式内部方式外部形式 注释和结束符单行注释多行注释 结束符输入和输出输出输入 变量声明赋值变量初始化更新变量 关键字变量名命名规则 常量数据类型…

java--权限修饰符

1.什么是权限修饰符 就是是用来限制类中的成员(成员变量、成员方法、构造器、代码块...)能够被访问的范围。 2.权限修饰符有几种&#xff1f;各自的作用是什么&#xff1f; private<缺省<protected<public(范围由小到大)

【成功案例】7日ROI超65%!注册率超85%!雷霆网络 联手 NetMarvel 实现效果翻倍增长!

雷霆网络旗下多款角色扮演手游在国内长期霸占买量榜前列&#xff0c;而这股“买量大户”的风依旧吹到了海外&#xff0c;其中《地下城堡3》依靠买量在境外业务收入上增长明显&#xff0c;目前市场潜力巨大。 然而&#xff0c;面对竞争激烈的PRG游戏出海局面&#xff0c;打开市…

PyTorch包

进入PyTorch的官网&#xff1a; pytorch GitHub 点击GitHub&#xff1a; 进入PyTorch的主目录&#xff1a; 进入Vision reference&#xff1a; detection&#xff1a; 这就是我们在训练过程中会使用到的文件了&#xff1a;

如何下载OpenJDK及其源码

如果想下载 OpenJDK&#xff0c;存在以下几种办法&#xff1a; 最简单的办法是去 OpenJDK 官网&#xff0c;这里能下载 JDK9 及其以上的版本&#xff0c;还有 JDK 源码所在的 github 地址。 第二种方法是使用 IDEA 下载&#xff0c;位置在 File->Project Structure->SD…

R数据分析:集成学习方法之随机生存森林的原理和做法,实例解析

很久很久以前给大家写过决策树&#xff0c;非常简单明了的算法。今天给大家写随机&#xff08;生存&#xff09;森林&#xff0c;随机森林是集成了很多个决策数的集成模型。像随机森林这样将很多个基本学习器集合起来形成一个更加强大的学习器的这么一种集成思想还是非常好的。…

XUbuntu22.04之解决gpg keyserver receive failed no data(一百九十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

nginx 模块相关配置及结构理解

文章目录 模块配置结构模块配置指令先看一下 ngx_command_t 结构一个模块配置的demo简单模块配置的案例演示 模块上下文结构模块的定义 模块配置结构 Nginx中每个模块都会提供一些指令&#xff0c;以便于用户通过配置去控制该模块的行为。 Nginx的配置信息分成了几个作用域(sc…

LeetCode算法心得——爬楼梯(记忆化搜索+dp)

大家好&#xff0c;我是晴天学长&#xff0c;第二个记忆化搜索练习&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1&#xff09;爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或…

9.2 Windows驱动开发:内核解析PE结构导出表

在笔者的上一篇文章《内核特征码扫描PE代码段》中LyShark带大家通过封装好的LySharkToolsUtilKernelBase函数实现了动态获取内核模块基址&#xff0c;并通过ntimage.h头文件中提供的系列函数解析了指定内核模块的PE节表参数&#xff0c;本章将继续延申这个话题&#xff0c;实现…

Springboot集成swagger之knife4j

knife4j的最终效果&#xff1a; 支持直观的入参介绍、在线调试及离线各种API文档下载。 1 引入pom <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</ver…

创建maven的web项目

&#xff08;一&#xff09;创建maven的web项目 Step1、创建一个普通的maven项目 &#xff08;1&#xff09;新建一个empty project&#xff0c;命名为SSM2。 点击项目名&#xff0c;右键new&#xff0c;选择Module&#xff0c;左侧选择“Maven archetype”&#xff0c;可以给…

Py之PyMuPDF:PyMuPDF的简介、安装、使用方法之详细攻略

Py之PyMuPDF&#xff1a;PyMuPDF的简介、安装、使用方法之详细攻略 目录 PyMuPDF的简介 PyMuPDF的安装 PyMuPDF的使用方法 1、基础用法 PyMuPDF的简介 PyMuPDF是一个高性能的Python库&#xff0c;用于PDF(和其他)文档的数据提取&#xff0c;分析&#xff0c;转换和操作。 …

Matrix

Matrix 如下是四种变换对应的控制参数&#xff1a; Rect 常用的一个“绘画相关的工具类”&#xff0c;常用来描述长方形/正方形&#xff0c;他只有4个属性&#xff1a; public int left; public int top; public int right; public int bottom; 这4个属性描述着这一个“方块…