面部表情识别的多层感知器实现
1. 多层感知器(MLP)类的构建
1.1 分类器基类
首先,我们定义一个抽象基类
Classifier
,它包含训练和测试的抽象方法:
from abc import ABCMeta, abstractmethod
class Classifier:
"""Abstract base class for all classifiers"""
__metaclass__ = ABCMeta
@abstractmethod
def fit(self, X_train, y_train):
pass
@abstractmethod
def evaluate(self, X_test, y_test, visualize=False):
pass
这里,
X_train
和
X_test
分别对应训练数据和测试数据,每一行代表一个样本,每一列是该样本的一个特征值。
y_train
和
y_test
分别是训练标签和测试标签向量。
1.2 多层感知器类
接着,我们定义一个新的类
MultiLayerPerceptron
,它继承自
Classifier
基类:
class MultiLayerPerceptron(Classifier):
def __init__(self, layer_sizes, class_labels, params=None):
self.num_features = layer_sizes[0]
self.num_classes = layer_sizes[-1]
self.class_labels = class_labels
self.params = params or dict()
self.model = cv2.ANN_MLP()
self.model.create(layer_sizes)
构造函数接受
layer_sizes
数组,用于指定网络每层的神经元数量,以及
class_labels
数组,列出所有可用的类别标签。
1.3 标签转换方法
为了方便用户操作,
MultiLayerPerceptron
类提供了字符串标签和整数标签之间的转换方法:
def _labels_str_to_num(self, labels):
""" convert string labels to their corresponding ints """
return np.array([int(np.where(self.class_labels == l)[0])
for l in labels])
def _labels_num_to_str(self, labels):
"""Convert integer labels to their corresponding string
names """
return self.class_labels[labels]
1.4 加载和保存方法
提供简单的包装器,用于加载和保存预训练的 MLP:
def load(self, file):
""" load a pre-trained MLP from file """
self.model.load(file)
def save(self, file):
""" save a trained MLP to file """
self.model.save(file)
2. MLP 的训练
2.1 训练方法
根据
Classifier
基类的要求,我们在
fit
方法中进行训练:
def fit(self, X_train, y_train, params=None):
""" fit model to data """
if params is None:
params = self.params
y_train = self._labels_str_to_num(y_train)
y_train = self._one_hot(y_train).reshape(-1,
self.num_classes)
self.model.train(X_train, y_train, None, params=params)
其中,
params
是一个可选的字典,包含训练相关的选项,如终止条件和学习算法。例如,使用反向传播并在 300 次迭代或损失小于 0.01 时终止训练:
params = dict(
term_crit = (cv2.TERM_CRITERIA_COUNT, 300, 0.01),
train_method = cv2.ANN_MLP_TRAIN_PARAMS_BACKPROP)
2.2 独热编码方法
由于
cv2.ANN_MLP
模块的
train
方法不允许整数值的类别标签,我们需要将
y_train
转换为独热编码:
def _one_hot(self, y_train):
"""Convert a list of labels into one-hot code """
num_samples = len(y_train)
new_responses = np.zeros(num_samples * self.num_classes,
np.float32)
resp_idx = np.int32(y_train +
np.arange(num_samples) * self.num_classes)
new_responses[resp_idx] = 1
return new_responses
3. MLP 的测试
3.1 评估方法
根据
Classifier
基类的要求,我们在
evaluate
方法中评估模型性能:
def evaluate(self, X_test, y_test, visualize=False):
""" evaluate model performance """
ret, Y_vote = self.model.predict(X_test)
y_test = self._labels_str_to_num(y_test)
accuracy = self._accuracy(y_test, Y_vote)
precision = self._precision(y_test, Y_vote)
recall = self._recall(y_test, Y_vote)
return (accuracy, precision, recall)
这里,我们根据输出层神经元的活动来构建投票矩阵,然后计算准确率、精确率和召回率。
3.2 预测方法
还提供了
predict
方法,用于预测单个数据样本的标签:
def predict(self, X_test):
""" predict the labels of test data """
ret, Y_vote = self.model.predict(X_test)
# find the most active cell in the output layer
y_hat = np.argmax(Y_vote, 1)
# return string labels
return self._labels_num_to_str(y_hat)
4. 运行脚本
4.1 数据加载和预处理
使用
train_test_mlp.py
脚本训练和测试 MLP。脚本首先解析自制数据集并提取所有类别标签:
import cv2
import numpy as np
from datasets import homebrew
from classifiers import MultiLayerPerceptron
def main():
# load training data
(X_train, y_train),(X_test, y_test) =
homebrew.load_data("datasets/faces_training.pkl",
num_components=50, test_split=0.2,
save_to_file="datasets/faces_preprocessed.pkl",
seed=42)
if len(X_train) == 0 or len(X_test) == 0:
print "Empty data"
raise SystemExit
# convert to numpy
X_train = np.squeeze(np.array(X_train)).astype(np.float32)
y_train = np.array(y_train)
X_test = np.squeeze(np.array(X_test)).astype(np.float32)
y_test = np.array(y_test)
# find all class labels
labels = np.unique(np.hstack((y_train, y_test)))
4.2 超参数调整
通常,神经网络的最佳大小事先未知,需要调整超参数。我们在循环中运行不同大小的 MLP,并将最佳结果保存到文件中:
params = dict( term_crit = (cv2.TERM_CRITERIA_COUNT, 300,
0.01), train_method=cv2.ANN_MLP_TRAIN_PARAMS_BACKPROP,
bp_dw_scale=0.001, bp_moment_scale=0.9 )
save_file = 'params/mlp.xml'
num_features = len(X_train[0])
num_classes = len(labels)
# find best MLP configuration
print "1-hidden layer networks"
best_acc = 0.0 # keep track of best accuracy
for l1 in xrange(10):
# gradually increase the hidden-layer size
layer_sizes = np.int32([num_features,
(l1 + 1) * num_features / 5,
num_classes])
MLP = MultiLayerPerceptron(layer_sizes, labels)
print layer_sizes
MLP.fit(X_train, y_train, params=params)
(acc, _, _) = MLP.evaluate(X_train, y_train)
print " - train acc = ", acc
(acc, _, _) = MLP.evaluate(X_test, y_test)
print " - test acc = ", acc
if acc > best_acc:
# save best MLP configuration to file
MLP.save(save_file)
best_acc = acc
4.3 流程图
graph LR
A[加载数据] --> B[数据预处理]
B --> C[超参数调整]
C --> D[训练MLP]
D --> E[评估MLP]
E --> F{是否最佳?}
F -- 是 --> G[保存模型]
F -- 否 --> C
4.4 表格:评估指标
| 指标 | 含义 |
|---|---|
| 准确率 | 模型正确预测的样本数占总样本数的比例 |
| 精确率 | 模型预测为正类的样本中,实际为正类的比例 |
| 召回率 | 实际为正类的样本中,被模型正确预测为正类的比例 |
5. 应用到实时分类
5.1 主 GUI 应用
保存的
params/mlp.xml
文件包含网络配置和学习到的权重,可以通过将
loadMLP='params/mlp.xml'
传递给
FaceLayout
类的
init_algorithm
方法加载到主 GUI 应用中。在主函数例程(
chapter7.py
)中,会加载预训练的级联分类器和多层感知器,并将它们应用到网络摄像头的实时流的每一帧上。
5.2 测试模式
在应用中选择“Test”单选按钮,会触发
EVT_RADIOBUTTON
事件,绑定到
FaceLayout._on_testing
,禁用所有与训练相关的按钮,将应用切换到测试模式。在测试模式下,预训练的 MLP 分类器会应用到实时流的每一帧,尝试预测当前的面部表情。
5.3 处理帧方法
FaceLayout._process_frame
方法用于检测面部并在测试模式下预测面部标签:
def _process_frame(self, frame):
""" detects face, predicts face label in testing mode """
success, frame, self.head = self.faces.detect(frame)
if success and self.testing.GetValue():
success, head = self.faces.align_head(self.head)
if success:
X, _, _ = homebrew.extract_features(
[head.flatten()], self.pca_V, self.pca_m)
label = self.MLP.predict(np.array(X))[0]
cv2.putText(frame, str(label), (x,y-20),
cv2.FONT_HERSHEY_COMPLEX, 1, (0,255,0), 2)
return frame
该方法的具体步骤如下:
1. 检测当前帧的降采样灰度版本中的面部。
2. 如果检测到面部且处于测试模式,对齐头部区域。
3. 使用预加载的主成分分析(PCA)基向量和均值对当前帧进行相同的预处理。
4. 提取特征并使用预训练的 MLP 预测当前帧的类别标签。
5. 在当前帧的边界框上方显示预测的标签。
5.4 流程图
graph LR
A[开始] --> B[检测面部]
B -- 成功 --> C{是否测试模式?}
C -- 是 --> D[对齐头部区域]
D -- 成功 --> E[提取特征]
E --> F[预测标签]
F --> G[显示标签]
G --> H[结束]
C -- 否 --> H
B -- 失败 --> H
D -- 失败 --> H
6. 结果与总结
6.1 结果
尽管分类器仅在大约 100 个训练样本上进行了训练,但它能够在实时流的每一帧中可靠地检测各种面部表情,无论当时面部看起来多么扭曲。这表明学习到的神经网络既没有欠拟合也没有过拟合数据,因为它能够为新的数据样本预测正确的类别标签。
6.2 总结
通过构建多层感知器(MLP)分类器并将其应用于面部表情识别,我们结合了目标检测和目标识别的多种技能,实现了一个端到端的应用。具体过程包括:
1. 定义分类器基类和多层感知器类。
2. 实现 MLP 的训练和测试方法。
3. 运行脚本进行数据加载、预处理和超参数调整。
4. 将预训练的 MLP 应用到实时分类中。
6.3 拓展与展望
在机器学习领域,还有许多其他的库和算法可供探索,例如
pylearn
、
scikit - learn
和
pycaffe
等。深度学习爱好者可以研究
Theano
或
Torch
。如果在应用算法时缺乏数据集,可以访问 UC Irvine 机器学习库。
6.4 表格:相关库与资源
| 资源 | 描述 | 链接 |
|---|---|---|
| pylearn | 机器学习库 | https://github.com/lisa - lab/pylearn2 |
| scikit - learn | 机器学习库 | http://scikit - learn.org |
| pycaffe | 深度学习库 | http://caffe.berkeleyvision.org |
| Theano | 深度学习库 | http://deeplearning.net/software/theano |
| Torch | 深度学习库 | http://torch.ch |
| UC Irvine 机器学习库 | 数据集资源 | http://archive.ics.uci.edu/ml |
通过不断学习和实践,我们可以进一步改进和拓展这个面部表情识别应用,使其在更多场景中发挥作用。
超级会员免费看
21

被折叠的 条评论
为什么被折叠?



