机器学习-面部表情识别
一、选题背景
面部表情识别的深度学习技术具有广泛的应用前景和社会价值。从社会层面来看,它可以应用于公共安全、金融支付、智能交通等领域,提高社会治理效率和安全性;从经济层面来看,它可以为企业提供更加智能化的服务,提高企业竞争力和盈利能力;从技术层面来看,深度学习技术在图像处理、模式识别等方面具有优势,可以不断提高面部识别的准确性和可靠性;从数据来源方面来看,随着智能手机、摄像头等设备的普及,人们在日常生活中产生的大量数据为面部识别提供了丰富的训练样本。
二、机器学习设计方案
本项目是基于卷积神经网络模型开展表情识别的研究,为了尽可能的提高最终表情识别的准确性,需要大量的样本图片训练,优化,所以采用了 一个大量的图片数据集用来训练、测试,划分一下训练集和验证集。
本次训练的数据来源于: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()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理