机器学习-面部表情识别

一、选题背景

  面部表情识别的深度学习技术具有广泛的应用前景和社会价值。从社会层面来看,它可以应用于公共安全、金融支付、智能交通等领域,提高社会治理效率和安全性;从经济层面来看,它可以为企业提供更加智能化的服务,提高企业竞争力和盈利能力;从技术层面来看,深度学习技术在图像处理、模式识别等方面具有优势,可以不断提高面部识别的准确性和可靠性;从数据来源方面来看,随着智能手机、摄像头等设备的普及,人们在日常生活中产生的大量数据为面部识别提供了丰富的训练样本。

二、机器学习设计方案

  本项目是基于卷积神经网络模型开展表情识别的研究,为了尽可能的提高最终表情识别的准确性,需要大量的样本图片训练,优化,所以采用了 一个大量的图片数据集用来训练、测试,划分一下训练集和验证集。

  本次训练的数据来源于:https://www.kaggle.com/c/challenges-in-representation-learning-facial-expression-recognition-challenge/data

  数据集包含人类面部表情的图片的label和feature。在这里,面部表情识别相当于一个分类问题,共有7个类别。

其中label包括7种类型表情:

  面部表情识别面临着一些技术难点,如:光照变化的影响,不同的光照条件可能会影响面部表情的识别准确率,特别是在光线较暗或者过度曝光的情况下;面部姿态的多样性,面部表情不仅受到肌肉运动的影响,还与头部姿态、面部朝向等因素有关,这增加了表情识别的复杂性。对这两个我们可以对光照进行调整和补偿:可以通过图像预处理技术对光照变化进行补偿,如使用直方图均衡化或自适应直方图均衡化技术来改善图像对比度。姿态估计,引入姿态估计模型来辅助表情识别,使用卷积神经网络(CNN)来预测头部姿态,从而校正姿态造成的误差。

框架描述:
1. 数据收集:收集大量的面部表情图像数据,这些数据通常来自于不同的场景、光照条件和角度。为了保证模型的泛化能力,数据集应包含多样化的表情和个体差异。
2. 数据预处理:对收集到的数据进行预处理,以提高模型训练的效率和准确性。预处理步骤可能包括图像大小标准化、归一化、数据增强(如旋转、缩放、裁剪)等。
3. 特征提取:从预处理后的图像中提取有助于表情识别的特征。这可能包括面部关键点(如眼睛、鼻子、嘴巴的位置)、面部姿态、以及用于描述面部表情的局部二值模式(LBP)或深度学习特征(如卷积神经网络的特征)。
4. 模型选择与训练:选择合适的机器学习模型来训练表情识别任务。常用的模型包括支持向量机(SVM)、随机森林、卷积神经网络(CNN)等。使用训练数据集对模型进行训练,调整模型参数以最小化分类错误。
5. 模型评估:使用验证集或测试集对训练好的模型进行评估,以确定其性能。评估指标可能包括准确率、召回率、F1分数等。
6. 模型优化:根据评估结果对模型进行调整和优化,以提高其泛化能力。这可能涉及超参数调优、模型融合、特征选择等。
7. 模型部署:将训练好的模型部署到实际应用中,如应用程序、网站或嵌入式设备。部署过程中需要考虑模型的实时性能、资源消耗和兼容性问题。
8. 持续学习与更新:随着更多数据的积累和变化,定期更新和优化模型,以适应新的表情趋势和个体差异。

三、机器学习的实现步骤

  面部表情识别框架

  面部表情识别通常可以划分为四个进程。包括图像获取,面部检测,图像预处理和表情分类。其中,面部检测,脸部特征提取和面部表情分类是面部表情识别的三个关键环节面部表情识别的基本框架如下图所示。

 

  首先是获取图像并执行面部检测,然后提取仅具有面部的图像部分。所提取的面部表情在比例和灰度上不均匀,因此有必要对面部特征区域进行分割和归一化,其中执行归一化主要是对面部光照和位置进行统一处理,将图像统一重塑为标准大小,如 48×48 像素的图片,即图像预处理。然后对脸部图像提取面部表情特征值,并进行分类。采用卷积神经网络(CNN)来完成特征提取和分类的任务,因为 CNN 是模仿人脑工作并建立卷积神经网络结构模型的著名模型,所以选择卷积神经网络作为构建模型体系结构的基础,最后不断训练,优化,最后达到较准确识别出面部表情的结果。

  具体步骤如下:

   先对图像进行预处理:

(1)在原文件中,emotion和pixels人脸像素数据是集中在一起的。为了方便操作,决定利用pandas库进行数据分离,即将所有emotion读出后,写入新创建的文件emotion.csv;将所有的像素数据读出后,写入新创建的文件pixels.csv。

 

复制代码
 1 # 将emotion和pixels像素数据分离
 2 import pandas as pd
 3 
 4 # 注意修改train.csv为你电脑上文件所在的相对或绝对路劲地址。
 5 path = 'dataset/train.csv'
 6 # 读取数据
 7 df = pd.read_csv(path)
 8 # 提取emotion数据
 9 df_y = df[['emotion']]
10 # 提取pixels数据
11 df_x = df[['pixels']]
12 # 将emotion写入emotion.csv
13 df_y.to_csv('dataset/emotion.csv', index=False, header=False)
14 # 将pixels数据写入pixels.csv
15 df_x.to_csv('dataset/pixels.csv', index=False, header=False)
复制代码

 (2)给定的数据集是csv格式的,所以我先将其全部可视化,还原为图片文件再送进模型进行处理。

复制代码
 1 # face_view.py
 2 
 3 import cv2
 4 import numpy as np
 5 
 6 # 放图片的路径
 7 path = '../images'
 8 # 读取像素数据
 9 data = np.loadtxt('../datasets/cnn_data.csv')
10 
11 # 按行取数据并写图
12 for i in range(data.shape[0]):
13     face_array = data[i, :].reshape((48, 48))  # reshape
14     cv2.imwrite(path + '//' + '{}.jpg'.format(i), face_array)  # 写图
复制代码

  (3)Step1:划分一下训练集和验证集。一共有28709张图片,取了前24000张图片作为训练集,其他图片作为验证集。新建文件夹cnn_train和cnn_val,将0.jpg到23999.jpg放进文件夹cnn_train,将其他图片放进文件夹cnn_val.

复制代码
 1 # cnn_picture_label.py
 2 # ###三、表情图片和类别标注,
 3 # 1.取前24000张图片作为训练集放入cnn_train,其他图片作为验证集放入cnn_val
 4 # 2.对每张图片标记属于哪一个类别,存放在dataset.csv中,分别在刚刚训练集和测试集执行标记任务。
 5 
 6 # #因为cpu训练太慢,我只取前2000张做训练,400张做测试!!,手动删除两个文件夹重dataset.csv的多余行数据
 7 import os
 8 import pandas as pd
 9 
10 
11 def data_label(path):
12     # 读取label文件
13     df_label = pd.read_csv('../datasets/cnn_label.csv', header=None)
14     # 查看该文件夹下所有文件
15     files_dir = os.listdir(path)
16     # 存放文件名和标签的列表
17     path_list = []
18     label_list = []
19     # 遍历所有文件,取出文件名和对应的标签分别放入path_list和label_list列表
20     for file_dir in files_dir:
21         if os.path.splitext(file_dir)[1] == '.jpg':
22             path_list.append(file_dir)
23             index = int(os.path.splitext(file_dir)[0])
24             label_list.append(df_label.iat[index, 0])
25     # 将两个列表写进dataset.csv文件
26     path_s = pd.Series(path_list)
27     label_s = pd.Series(label_list)
28     df = pd.DataFrame()
29     df['path'] = path_s
30     df['label'] = label_s
31     df.to_csv(path + '\\dataset.csv', index=False, header=False)
32 
33 
34 def main():
35     # 指定文件夹路径
36     train_path = '../datasets/cnn_train'
37     val_path = '../datasets/cnn_val'
38     data_label(train_path)
39     data_label(val_path)
40 
41 
42 if __name__ == '__main__':
43     main()
复制代码

  (3)然后重写Dataset类,这是Pytorch中图像数据集加载的一个基类,需要重写类来实现加载上面的图像数据集

 

复制代码
 1 # rewrite_dataset.py
 2 # ###四、重写类来实现加载上面的图像数据集。
 3 import bisect
 4 import warnings
 5 
 6 import cv2
 7 import numpy as np
 8 import pandas as pd
 9 import torch
10 import torch.utils.data as data
11 
12 
13 class FaceDataset(data.Dataset):
14     # 初始化
15     def __init__(self, root):
16         super(FaceDataset, self).__init__()
17         self.root = root
18         df_path = pd.read_csv(root + '\\dataset.csv', header=None, usecols=[0])
19         df_label = pd.read_csv(root + '\\dataset.csv', header=None, usecols=[1])
20         self.path = np.array(df_path)[:, 0]
21         self.label = np.array(df_label)[:, 0]
22 
23     # 读取某幅图片,item为索引号
24     def __getitem__(self, item):
25         # 图像数据用于训练,需为tensor类型,label用numpy或list均可
26         face = cv2.imread(self.root + '\\' + self.path[item])
27         # 读取单通道灰度图
28         face_gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
29         # 直方图均衡化
30         face_hist = cv2.equalizeHist(face_gray)
31 
32         """
33         像素值标准化
34         读出的数据是48X48的,而后续卷积神经网络中nn.Conv2d() 
35         API所接受的数据格式是(batch_size, channel, width, height),
36         本次图片通道为1,因此我们要将48X48 reshape为1X48X48。
37         """
38         face_normalized = face_hist.reshape(1, 48, 48) / 255.0
39         face_tensor = torch.from_numpy(face_normalized)
40         face_tensor = face_tensor.type('torch.FloatTensor')
41         # face_tensor = face_tensor.type('torch.cuda.FloatTensor')
42         label = self.label[item]
43         return face_tensor, label
44 
45     # 获取数据集样本个数
46     def __len__(self):
47         return self.path.shape[0]
复制代码

 

然后开始搭建模型:

  卷积神经网络(CNN)是一种前馈神经网络,它包括卷积计算并具有较深的结构,因此是深度学习的代表性算法之一。随着科技的不断进步,人们在研究人脑组织时受启发创立了神经网络。神经网络由很多相互联系的神经元组成,并且可以在不同的神经元之间通过调整传递彼此之间联系的权重系数 x 来增强或抑制信号。标准卷积神经网络通常由输入层、卷积层、池化层、全连接层和输出层组成,如下图所示

 

  上图中第一层为输入层,大小为 28×28,然后通过 20×24×24 的卷积层,得到的结果再输入池化层中,最后再通过图中第四层既全连接层,直到最后输出。

  下图为CNN常见的网络模型。其中包括 4 个卷积层,3 个池化层,池化层的大小为 3×3,最终再通过两个全连接层到达输出层。网络模型中的输入层一般是一个矩阵,卷积层,池化层和全连接层可以当作隐藏层,这些层通常具有不同的计算方法,需要学习权重以找到最佳值。

复制代码
 1 # CNN_face.py
 2 # 定义一个CNN模型
 3 """
 4 inputs(48*48*1) ->
 5 conv(24*24*64) -> conv(12*12*128) -> conv(6*6*256) ->
 6 Dropout -> fc(4096) -> Dropout -> fc(1024) ->
 7 outputs(7)
 8 """
 9 
10 import torch.nn as nn
11 
12 
13 # 参数初始化
14 def gaussian_weights_init(m):
15     classname = m.__class__.__name__
16     # 字符串查找find,找不到返回-1,不等-1即字符串中含有该字符
17     if classname.find('Conv') != -1:
18         m.weight.data.normal_(0.0, 0.04)
19 
20 
21 class FaceCNN(nn.Module):
22     # 初始化网络结构
23     def __init__(self):
24         super(FaceCNN, self).__init__()
25 
26         # layer1(conv + relu + pool)
27         # input:(bitch_size, 1, 48, 48), output(bitch_size, 64, 24, 24)
28         self.conv1 = nn.Sequential(
29             nn.Conv2d(1, 64, 3, 1, 1),
30             nn.BatchNorm2d(num_features=64),
31             nn.RReLU(inplace=True),
32             nn.MaxPool2d(kernel_size=2, stride=2)
33         )
34         # layer2(conv + relu + pool)
35         # input:(bitch_size, 64, 24, 24), output(bitch_size, 128, 12, 12)
36         self.conv2 = nn.Sequential(
37             nn.Conv2d(64, 128, 3, 1, 1),
38             nn.BatchNorm2d(num_features=128),
39             nn.RReLU(inplace=True),
40             nn.MaxPool2d(kernel_size=2, stride=2)
41         )
42         # layer3(conv + relu + pool)
43         # input: (bitch_size, 128, 12, 12), output: (bitch_size, 256, 6, 6)
44         self.conv3 = nn.Sequential(
45             nn.Conv2d(128, 256, 3, 1, 1),
46             nn.BatchNorm2d(num_features=256),
47             nn.RReLU(inplace=True),
48             nn.MaxPool2d(kernel_size=2, stride=2)
49         )
50 
51         # 参数初始化
52         self.conv1.apply(gaussian_weights_init)
53         self.conv2.apply(gaussian_weights_init)
54         self.conv3.apply(gaussian_weights_init)
55 
56         # 全连接层
57         self.fc = nn.Sequential(
58             nn.Dropout(p=0.2),
59             nn.Linear(256*6*6, 4096),
60             nn.RReLU(inplace=True),
61 
62             nn.Dropout(p=0.5),
63             nn.Linear(4096, 1024),
64             nn.RReLU(inplace=True),
65 
66             nn.Linear(1024, 256),
67             nn.RReLU(inplace=True),
68             nn.Linear(256, 7)
69         )
70 
71     # 向前传播
72     def forward(self, x):
73         x = self.conv1(x)
74         x = self.conv2(x)
75         x = self.conv3(x)
76         x = x.view(x.shape[0], -1)  # 数据扁平化
77         y = self.fc(x)
78 
79         return y
复制代码

 

 

之后就是训练模型:

  损失函数使用交叉熵,优化器是随机梯度下降SGD,其中weight_decay为正则项系数,每轮训练打印损失值,每10轮训练打印准确率。

 

 

 

复制代码
 1 # train.py
 2 # 定义训练轮
 3 import torch
 4 import torch.utils.data as data
 5 import torch.nn as nn
 6 import numpy as np
 7 from torch import optim
 8 
 9 from models import CNN_face
10 from dataloader import rewrite_dataset
11 
12 
13 def train(train_dataset, val_dataset, batch_size, epochs, learning_rate, wt_decay, print_cost=True, isPlot=True):
14     device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15     # 加载数据集并分割batch
16     train_loader = data.DataLoader(train_dataset, batch_size)
17     # 构建模型
18     model = CNN_face.FaceCNN()
19     model.to(device)
20     # 损失函数和优化器
21     compute_loss = nn.CrossEntropyLoss()
22     optimizer = optim.SGD(model.parameters(), lr=learning_rate, weight_decay=wt_decay)
23     # 学习率衰减
24     # scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.8)
25 
26     for epoch in range(epochs):
27         loss = 0
28         model.train()
29         for images, labels in train_loader:
30             optimizer.zero_grad()
31             outputs = model.forward(images)
32             loss = compute_loss(outputs, labels)
33             loss.backward()
34             optimizer.step()
35 
36         # 打印损失值
37         if print_cost:
38             print('epoch{}: train_loss:'.format(epoch + 1), loss.item())
39 
40         # 评估模型准确率
41         if epoch % 10 == 9:
42             model.eval()
43             acc_train = validate(model, train_dataset, batch_size)
44             acc_val = validate(model, val_dataset, batch_size)
45             print('acc_train: %.1f %%' % (acc_train * 100))
46             print('acc_val: %.1f %%' % (acc_val * 100))
47 
48     return model
49 
50 
51 # 验证模型在验证集上的正确率
52 def validate(model, dataset, batch_size):
53     val_loader = data.DataLoader(dataset, batch_size)
54     result, total = 0.0, 0
55     for images, labels in val_loader:
56         pred = model.forward(images)
57         pred_tmp = pred.cuda().data.cpu().numpy()
58         pred = np.argmax(pred_tmp.data.numpy(), axis=1)
59         labels = labels.data.numpy()
60         result += np.sum((pred == labels))
61         total += len(images)
62     acc = result / total
63     return acc
64 
65 
66 def main():
67     train_dataset = rewrite_dataset.FaceDataset(root=r'D:\01 Desktop\JUST_YAN\05 DeepLearning\Facial-expression_Reg\datasets\cnn_train')
68     val_dataset = rewrite_dataset.FaceDataset(root=r'D:\01 Desktop\JUST_YAN\05 DeepLearning\Facial-expression_Reg\datasets\cnn_val')
69     model = train(train_dataset, val_dataset, batch_size=128, epochs=100, learning_rate=0.01,
70                   wt_decay=0, print_cost=True, isPlot=True)
71     torch.save(model, 'model_net.pkl')  # 保存模型
72 
73 
74 if __name__ == '__main__':
75     main()
复制代码

最后是 项目结果:

在超参数为:batch_size=128, epochs=100, learning_rate=0.01, wt_decay=0,的情况下跑得最终结果如下:

 总结

  在本次机器学习过程实现中我明白了数据预处理是关键。在面部表情识别中,数据预处理包括图像去噪、裁剪、对齐等操作,这些操作能够提高模型的准确性和鲁棒性。特征提取是重要的一步。在面部表情识别中,常用的特征提取方法有基于几何特征的方法、基于纹理特征的方法和基于深度学习的方法等。其中,基于深度学习的方法具有更好的性能表现。在本次的机器学习过程实现中我加深了对面部表情识别算法的理解,和培养自己解决问题的能力,提高了机器学习技能和知识水平,以及获得了系统化的思维。

总代码如下:

复制代码
  1 # cnn_feature_label.py
  2 # ###一、将原始数据的label和feature(像素)数据分离
  3 import pandas as pd
  4 
  5 # 源数据路径
  6 path = '../datasets/originalData/cnn_train.csv'
  7 # 读取数据
  8 df = pd.read_csv(path)
  9 # 提取feature(像素)数据 和 label数据
 10 df_x = df[['feature']]
 11 df_y = df[['label']]
 12 # 将feature和label数据分别写入两个数据集
 13 df_x.to_csv('../datasets/cnn_data.csv', index=False, header=False)
 14 df_y.to_csv('../datasets/cnn_label.csv', index=False, header=False)
 15 
 16 
 17 
 18 # face_view.py
 19 # ###二、数据可视化,将每个数据行的2304个像素值合成每张48*48的表情图。
 20 import cv2
 21 import numpy as np
 22 
 23 # 放图片的路径
 24 path = '../images'
 25 # 读取像素数据
 26 data = np.loadtxt('../datasets/cnn_data.csv')
 27 
 28 # 按行取数据并写图
 29 for i in range(data.shape[0]):
 30     face_array = data[i, :].reshape((48, 48))  # reshape
 31     cv2.imwrite(path + '//' + '{}.jpg'.format(i), face_array)  # 写图片
 32 
 33 
 34 
 35 # cnn_picture_label.py
 36 # ###三、表情图片和类别标注,
 37 # 1.取前24000张图片作为训练集放入cnn_train,其他图片作为验证集放入cnn_val
 38 # 2.对每张图片标记属于哪一个类别,存放在dataset.csv中,分别在刚刚训练集和测试集执行标记任务。
 39 
 40 # #因为cpu训练太慢,我只取前2000张做训练,400张做测试!!,手动删除两个文件夹重dataset.csv的多余行数据
 41 import os
 42 import pandas as pd
 43 
 44 
 45 def data_label(path):
 46     # 读取label文件
 47     df_label = pd.read_csv('../datasets/cnn_label.csv', header=None)
 48     # 查看该文件夹下所有文件
 49     files_dir = os.listdir(path)
 50     # 存放文件名和标签的列表
 51     path_list = []
 52     label_list = []
 53     # 遍历所有文件,取出文件名和对应的标签分别放入path_list和label_list列表
 54     for file_dir in files_dir:
 55         if os.path.splitext(file_dir)[1] == '.jpg':
 56             path_list.append(file_dir)
 57             index = int(os.path.splitext(file_dir)[0])
 58             label_list.append(df_label.iat[index, 0])
 59 
 60     # 将两个列表写进dataset.csv文件
 61     path_s = pd.Series(path_list)
 62     label_s = pd.Series(label_list)
 63     df = pd.DataFrame()
 64     df['path'] = path_s
 65     df['label'] = label_s
 66     df.to_csv(path + '\\dataset.csv', index=False, header=False)
 67 
 68 
 69 def main():
 70     # 指定文件夹路径
 71     train_path = '../datasets/cnn_train'
 72     val_path = '../datasets/cnn_val'
 73     data_label(train_path)
 74     data_label(val_path)
 75 
 76 
 77 if __name__ == '__main__':
 78     main()
 79 
 80 
 81 
 82 # rewrite_dataset.py
 83 # ###四、重写类来实现加载上面的图像数据集。
 84 import bisect
 85 import warnings
 86 
 87 import cv2
 88 import numpy as np
 89 import pandas as pd
 90 import torch
 91 import torch.utils.data as data
 92 
 93 
 94 class FaceDataset(data.Dataset):
 95     # 初始化
 96     def __init__(self, root):
 97         super(FaceDataset, self).__init__()
 98         self.root = root
 99         df_path = pd.read_csv(root + '\\dataset.csv', header=None, usecols=[0])
100         df_label = pd.read_csv(root + '\\dataset.csv', header=None, usecols=[1])
101         self.path = np.array(df_path)[:, 0]
102         self.label = np.array(df_label)[:, 0]
103 
104     # 读取某幅图片,item为索引号
105     def __getitem__(self, item):
106         # 图像数据用于训练,需为tensor类型,label用numpy或list均可
107         face = cv2.imread(self.root + '\\' + self.path[item])
108 
109         # 读取单通道灰度图
110         face_gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
111 
112         # 直方图均衡化
113         face_hist = cv2.equalizeHist(face_gray)
114 
115         """
116         像素值标准化
117         读出的数据是48X48的,而后续卷积神经网络中nn.Conv2d() 
118         API所接受的数据格式是(batch_size, channel, width, height),
119         本次图片通道为1,因此我们要将48X48 reshape为1X48X48。
120         """
121         face_normalized = face_hist.reshape(1, 48, 48) / 255.0
122         face_tensor = torch.from_numpy(face_normalized)
123         face_tensor = face_tensor.type('torch.FloatTensor')
124         # face_tensor = face_tensor.type('torch.cuda.FloatTensor')
125         label = self.label[item]
126         return face_tensor, label
127 
128     # 获取数据集样本个数
129     def __len__(self):
130         return self.path.shape[0]
131 
132 
133 
134 # CNN_face.py
135 # 定义一个CNN模型
136 """
137 inputs(48*48*1) ->
138 conv(24*24*64) -> conv(12*12*128) -> conv(6*6*256) ->
139 Dropout -> fc(4096) -> Dropout -> fc(1024) ->
140 outputs(7)
141 """
142 
143 import torch.nn as nn
144 
145 
146 # 参数初始化
147 def gaussian_weights_init(m):
148     classname = m.__class__.__name__
149     # 字符串查找find,找不到返回-1,不等-1即字符串中含有该字符
150     if classname.find('Conv') != -1:
151         m.weight.data.normal_(0.0, 0.04)
152 
153 
154 class FaceCNN(nn.Module):
155     # 初始化网络结构
156     def __init__(self):
157         super(FaceCNN, self).__init__()
158 
159         # layer1(conv + relu + pool)
160         # input:(bitch_size, 1, 48, 48), output(bitch_size, 64, 24, 24)
161         self.conv1 = nn.Sequential(
162             nn.Conv2d(1, 64, 3, 1, 1),
163             nn.BatchNorm2d(num_features=64),
164             nn.RReLU(inplace=True),
165             nn.MaxPool2d(kernel_size=2, stride=2)
166         )
167         # layer2(conv + relu + pool)
168         # input:(bitch_size, 64, 24, 24), output(bitch_size, 128, 12, 12)
169         self.conv2 = nn.Sequential(
170             nn.Conv2d(64, 128, 3, 1, 1),
171             nn.BatchNorm2d(num_features=128),
172             nn.RReLU(inplace=True),
173             nn.MaxPool2d(kernel_size=2, stride=2)
174         )
175         # layer3(conv + relu + pool)
176         # input: (bitch_size, 128, 12, 12), output: (bitch_size, 256, 6, 6)
177         self.conv3 = nn.Sequential(
178             nn.Conv2d(128, 256, 3, 1, 1),
179             nn.BatchNorm2d(num_features=256),
180             nn.RReLU(inplace=True),
181             nn.MaxPool2d(kernel_size=2, stride=2)
182         )
183 
184         # 参数初始化
185         self.conv1.apply(gaussian_weights_init)
186         self.conv2.apply(gaussian_weights_init)
187         self.conv3.apply(gaussian_weights_init)
188 
189         # 全连接层
190         self.fc = nn.Sequential(
191             nn.Dropout(p=0.2),
192             nn.Linear(256*6*6, 4096),
193             nn.RReLU(inplace=True),
194 
195             nn.Dropout(p=0.5),
196             nn.Linear(4096, 1024),
197             nn.RReLU(inplace=True),
198 
199             nn.Linear(1024, 256),
200             nn.RReLU(inplace=True),
201             nn.Linear(256, 7)
202         )
203 
204     # 向前传播
205     def forward(self, x):
206         x = self.conv1(x)
207         x = self.conv2(x)
208         x = self.conv3(x)
209         x = x.view(x.shape[0], -1)  # 数据扁平化
210         y = self.fc(x)
211 
212         return y
213 
214 
215 
216 # train.py
217 # 定义训练轮
218 import torch
219 import torch.utils.data as data
220 import torch.nn as nn
221 import numpy as np
222 from torch import optim
223 
224 from models import CNN_face
225 from dataloader import rewrite_dataset
226 
227 
228 def train(train_dataset, val_dataset, batch_size, epochs, learning_rate, wt_decay, print_cost=True, isPlot=True):
229     device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
230 
231     # 加载数据集并分割batch
232     train_loader = data.DataLoader(train_dataset, batch_size)
233 
234     # 构建模型
235     model = CNN_face.FaceCNN()
236     model.to(device)
237 
238     # 损失函数和优化器
239     compute_loss = nn.CrossEntropyLoss()
240     optimizer = optim.SGD(model.parameters(), lr=learning_rate, weight_decay=wt_decay)
241 
242     # 学习率衰减
243     # scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.8)
244 
245     for epoch in range(epochs):
246         loss = 0
247         model.train()
248         for images, labels in train_loader:
249             optimizer.zero_grad()
250             outputs = model.forward(images)
251             loss = compute_loss(outputs, labels)
252             loss.backward()
253             optimizer.step()
254 
255         # 打印损失值
256         if print_cost:
257             print('epoch{}: train_loss:'.format(epoch + 1), loss.item())
258 
259         # 评估模型准确率
260         if epoch % 10 == 9:
261             model.eval()
262             acc_train = validate(model, train_dataset, batch_size)
263             acc_val = validate(model, val_dataset, batch_size)
264             print('acc_train: %.1f %%' % (acc_train * 100))
265             print('acc_val: %.1f %%' % (acc_val * 100))
266 
267     return model
268 
269 
270 # 验证模型在验证集上的正确率
271 def validate(model, dataset, batch_size):
272     val_loader = data.DataLoader(dataset, batch_size)
273     result, total = 0.0, 0
274     for images, labels in val_loader:
275         pred = model.forward(images)
276         pred_tmp = pred.cuda().data.cpu().numpy()
277         pred = np.argmax(pred_tmp.data.numpy(), axis=1)
278         labels = labels.data.numpy()
279         result += np.sum((pred == labels))
280         total += len(images)
281     acc = result / total
282     return acc
283 
284 
285 def main():
286     train_dataset = rewrite_dataset.FaceDataset(root=r'D:\01 Desktop\JUST_YAN\05 DeepLearning\Facial-expression_Reg\datasets\cnn_train')
287     val_dataset = rewrite_dataset.FaceDataset(root=r'D:\01 Desktop\JUST_YAN\05 DeepLearning\Facial-expression_Reg\datasets\cnn_val')
288     model = train(train_dataset, val_dataset, batch_size=128, epochs=100, learning_rate=0.01,
289                   wt_decay=0, print_cost=True, isPlot=True)
290     torch.save(model, 'model_net.pkl')  # 保存模型
291 
292 
293 if __name__ == '__main__':
294     main()
复制代码

 

posted @   ~May~  阅读(193)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示