机器学习练习目录
一、理解人脸图像特征提取的各种方法的特征
1.HOG
- 介绍: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。
- 主要思想: 在一副图像中,局部目标的表象和形状能够被梯度或边缘的方向密度分布很好地描述。(本质:梯度的统计信息,而梯度主要存在于边缘的地方)。
- 具体的实现方法: 首先将图像分成小的连通区域,把它叫细胞单元。然后采集细胞单元中各像素点的梯度的或边缘的方向直方图。最后把这些直方图组合起来就可以构成特征描述器。
- 步骤: 灰度图像转换、梯度计算、分网格的梯度方向直方图、快描述子、快描述子归一化、特征数据与检测窗口、匹配方法。
- 优点: ① HOG是在图像的局部方格单元上操作,它对图像几何的和光学的形变都能保持很好的不变性,这两种形变只会出现在更大的空间领域上;②在粗的空域抽样、精细的方向抽样以及较强的局部光学归一化等条件下,只要行人大体上能够保持直立的姿势,可以容许行人有一些细微的肢体动作,这些细微的动作可以被忽略而不影响检测效果。
- 综上所述,HOG特征是特别适合于做图像中的人体检测的。
参考链接:https://zhuanlan.zhihu.com/p/104670289
2.Dlib
- 介绍: Dlib是一款优秀的跨平台开源的C++工具库,该库使用C++编写,具有优异的性能。Dlib库提供的功能十分丰富,包括线性代数,图像处理,机器学习,网络,最优化算法等众多功能。同时该库也提供了Python接口。
- 核心原理: 使用了图像Hog特征来表示人脸,和其他特征提取算子相比,它对图像的几何和光学的形变都能保持很好的不变形。该特征与LBP特征,Harr特征共同作为三种经典的图像特征,该特征提取算子通常和支持向量机(SVM)算法搭配使用,用在物体检测场景。
- 该算法大致思路:
- 对正样本(即包含人脸的图像)数据集提取Hog特征,得到Hog特征描述子。
- 对负样本(即不包含人脸的图像)数据集提取Hog特征,得到Hog描述子。 其中负样本数据集中的数据量要远远大于正样本数据集中的样本数,负样本图像可以使用不含人脸的图片进行随机裁剪获取。
- 利用支持向量机算法训练正负样本,显然这是一个二分类问题,可以得到训练后的模型。
- 利用该模型进行负样本难例检测,也就是难分样本挖掘(hard-negtive mining),以便提高最终模型的分类能力。具体思路为:对训练集里的负样本不断进行缩放,直至与模板匹配位置,通过模板滑动串口搜索匹配(该过程即多尺度检测过程),如果分类器误检出非人脸区域则截取该部分图像加入到负样本中。
- 集合难例样本重新训练模型,反复如此得到最终分类模型。
- 应用最终训练出的分类器检测人脸图片,对该图片的不同尺寸进行滑动扫描,提取Hog特征,并用分类器分类。如果检测判定为人脸,则将其标定出来,经过一轮滑动扫描后必然会出现同一个人脸被多次标定的情况,这就用NMS完成收尾工作即可。
参考链接:https://zhuanlan.zhihu.com/p/92132280
3.卷积神经网络(CNN)
- 介绍: 卷积神经网络(Convolutional Neural Networks, CNN)是一类包含卷积计算且具有深度结构的前馈神经网络,是深度学习的代表算法之一 。卷积神经网络具有表征学习能力,能够按其阶层结构对输入信息进行平移不变分类,因此也被称为“平移不变人工神经网络(SIANN)"。
- CNN原理: 卷积神经网络(CNN)主要是用于图像识别领域,它指的是一类网络,而不是某一种,其包含很多不同种结构的网络。不同的网络结构通常表现会不一样。
- 所有CNN最终都是把一张图片转化为特征向量。就像上图VGG网络一样,通过多层的卷积,池化,全连接,降低图片维度,最后转化成了一个一维向量。这个向量就包含了图片的特征,当然这个特征不是肉眼上的图片特征,而是针对于神经网络的特征。
- CNN的训练过程:
- 卷积神经网络的前向传播过程
- 前向传播中的卷积操作
- 前向传播中的池化操作
- 前向传播中的全连接
- 卷积神经网络的反向传播过程
- 卷积神经网络的权值更新
- 卷积神经网络的训练过程流程图:
(不断循环这个过程,最后得到一个稳定的权值和阈值
)
- CNN的应用:
- 图片分类
- 相似图搜索
- 对抗样本(比如输入一张猫的图片,人为的修改一点图片数据,肉眼上看还是一只猫,但是你告诉神经网络这是狗。)
参考链接:https://zhuanlan.zhihu.com/p/95158245
二、卷积神经网络(CNN)笑脸数据集(genki4k)正负样本的划分、模型训练和测试的过程,输出模型训练精度和测试精度,完成对人脸微笑与否的模型训练
1.查看运行的环境
import platform
import tensorflow
import keras
print("Platform: {}".format(platform.platform()))
print("Tensorflow version: {}".format(tensorflow.__version__))
print("Keras version: {}".format(keras.__version__))
Platform: Windows-7-6.1.7601-SP1
Tensorflow version: 1.2.1
Keras version: 2.1.2
2.数据集准备
- 下载图像数据集genki4k.tar,把它解压到相应的目录(我放在了D:\mango目录下), 解压出来看见的原始数据集内容如图所示
- 手动裁剪过程
- 其中裁剪之前的图片
- 对图像进行了裁剪,提取大头照,并把一些不符合的图片进行了剔除,执行完裁剪后的显示效果
- 由于各种原因没有裁剪剔除的图片
- 导入需要的包
import keras #导入人工神经网络库
import matplotlib.pyplot as plt # matplotlib进行画图
import matplotlib.image as mpimg # # mpimg 用于读取图片
import numpy as np # numpy操作数组
from IPython.display import Image #显示图像
import os #os:操作系统接口
- 划分数据集(在当前写代码的同级目录下会产生一个mangoout的文件夹,包括 train :训练集;validation:验证集;test:测试集。)
# 原目录的路径
original_dataset_dir = 'D:\\mango\\files\\cutmango'
# 结果路径,存储较小的数据集
base_dir = 'mangoout'
os.mkdir(base_dir)
# 训练目录,验证和测试拆分
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)
# 训练微笑图片目录
train_smile_dir = os.path.join(train_dir, 'smile')
os.mkdir(train_smile_dir)
# 训练没微笑图片目录
train_unsmile_dir = os.path.join(train_dir, 'unsmile')
#s.mkdir(train_dogs_dir)
# 验证的微笑图片目录
validation_smile_dir = os.path.join(validation_dir, 'smile')
os.mkdir(validation_smile_dir)
# 验证的没微笑图片目录
validation_unsmile_dir = os.path.join(validation_dir, 'unsmile')
os.mkdir(validation_unsmile_dir)
# 测试的微笑图片目录
test_smile_dir = os.path.join(test_dir, 'smile')
os.mkdir(test_smile_dir)
# 测试的没微笑图片目录
test_unsmile_dir = os.path.join(test_dir, 'unsmile')
os.mkdir(test_unsmile_dir)
- 分配数据集,可以使用人为划分和代码划分
- 进行一次检查,计算每个分组中有多少张照片(训练/验证/测试)
print('total training smile images:', len(os.listdir(train_smile_dir)))
total training smile images: 1000
print('total testing smile images:', len(os.listdir(test_smile_dir)))
total testing smile images: 300
print('total training unsmile images:', len(os.listdir(train_unsmile_dir)))
total training unsmile images: 1000
print('total validation smile images:', len(os.listdir(validation_smile_dir)))
total validation smile images: 300
print('total testing unsmile images:', len(os.listdir(test_unsmile_dir)))
total testing unsmile images: 300
print('total validation unsmile images:', len(os.listdir(validation_unsmile_dir)))
total validation unsmile images: 300
- 有2000个训练图像,然后是600个验证图像,600个测试图像,其中每个分类都有相同数量的样本,是一个平衡的二元分类问题,意味着分类准确度将是合适的度量标准。
3.网络模型
- 卷积网络(convnets)将是一组交替的Conv2D(具有relu激活)和MaxPooling2D层。从大小150x150(有点任意选择)的输入开始,我们最终得到了尺寸为7x7的Flatten层之前的特征图。
- 注意特征图的深度在网络中逐渐增加(从32到128),而特征图的大小正在减少(从148x148到7x7)。这是一个你将在几乎所有的卷积网络(convnets)结构中会看到的模式。
- 由于我们正在处理二元分类问题,所以我们用一个神经元(一个大小为1的密集层(Dense))和一个sigmoid激活函数来结束网络。该神经元将会被用来查看图像归属于那一类或另一类的概率。
- 创建模型
from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
- 看特征图的尺寸如何随着每个连续的图层而改变,打印网络结构
model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_5 (Conv2D) (None, 148, 148, 32) 896
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 74, 74, 32) 0
_________________________________________________________________
conv2d_6 (Conv2D) (None, 72, 72, 64) 18496
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 36, 36, 64) 0
_________________________________________________________________
conv2d_7 (Conv2D) (None, 34, 34, 128) 73856
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 17, 17, 128) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 15, 15, 128) 147584
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 7, 7, 128) 0
_________________________________________________________________
flatten_2 (Flatten) (None, 6272) 0
_________________________________________________________________
dense_3 (Dense) (None, 512) 3211776
_________________________________________________________________
dense_4 (Dense) (None, 1) 513
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0
_________________________________________________________________
- 在编译步骤里,使用RMSprop优化器。由于用一个单一的神经元(Sigmoid的激活函数)结束了网络,将使用二进制交叉熵(binary crossentropy)作为损失函数
from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
4.资料预处理
- 网络的预处理步骤:
- 读入图像
- 将JPEG内容解码为RGB网格的像素
- 将其转换为浮点张量
- 将像素值(0和255之间)重新缩放到[0,1]间隔
- 数据应该先被格式化成适当的预处理浮点张量,然后才能输入到神经网络中
from keras.preprocessing.image import ImageDataGenerator
# 所有的图像将重新进行归一化处理 Rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
validation_datagen=ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
# 直接从目录读取图像数据
train_generator = train_datagen.flow_from_directory(
# 训练图像的目录
train_dir,
# 所有图像大小会被转换成150x150
target_size=(150, 150),
# 每次产生20个图像的批次
batch_size=20,
# 由于这是一个二元分类问题,y的label值也会被转换成二元的标签
class_mode='binary')
# 直接从目录读取图像数据
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
test_generator = test_datagen.flow_from_directory(test_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
Found 2000 images belonging to 2 classes.
Found 600 images belonging to 2 classes.
Found 600 images belonging to 2 classes.
- 图像张量生成器(generator)的输出,它产生150x150 RGB图像(形状"(20,150,150,3)")和二进制标签(形状"(20,)")的批次张量。20是每个批次中的样品数(批次大小)
for data_batch, labels_batch in train_generator:
print('data batch shape:', data_batch.shape)
print('labels batch shape:', labels_batch)
break
data batch shape: (20, 150, 150, 3)
labels batch shape: [ 1. 1. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 0.
0. 0.]
- 输出结果的0(笑脸)和1(非笑脸)
train_generator.class_indices
{'smile': 0, 'unsmile': 1}
5.开始训练
- 其中epochs值越大,花费的时间就越久、训练的精度更高,我电脑性能不好,运行了很久… …
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=10,
validation_data=validation_generator,
validation_steps=50)
Epoch 1/10
100/100 [==============================] - 227s 2s/step - loss: 0.6776 - acc: 0.5740 - val_loss: 0.6745 - val_acc: 0.5660
Epoch 2/10
100/100 [==============================] - 230s 2s/step - loss: 0.6422 - acc: 0.6520 - val_loss: 0.7091 - val_acc: 0.5290
Epoch 3/10
100/100 [==============================] - 222s 2s/step - loss: 0.5889 - acc: 0.7020 - val_loss: 0.5711 - val_acc: 0.7530
Epoch 4/10
100/100 [==============================] - 192s 2s/step - loss: 0.5251 - acc: 0.7575 - val_loss: 0.5592 - val_acc: 0.7330
Epoch 5/10
100/100 [==============================] - 191s 2s/step - loss: 0.4854 - acc: 0.7825 - val_loss: 0.5250 - val_acc: 0.7550
Epoch 6/10
100/100 [==============================] - 184s 2s/step - loss: 0.4503 - acc: 0.8015 - val_loss: 0.5111 - val_acc: 0.7980
Epoch 7/10
100/100 [==============================] - 183s 2s/step - loss: 0.4111 - acc: 0.8255 - val_loss: 0.5376 - val_acc: 0.7500
Epoch 8/10
100/100 [==============================] - 189s 2s/step - loss: 0.3748 - acc: 0.8380 - val_loss: 0.4906 - val_acc: 0.7850
Epoch 9/10
100/100 [==============================] - 188s 2s/step - loss: 0.3493 - acc: 0.8590 - val_loss: 0.4397 - val_acc: 0.8170
Epoch 10/10
100/100 [==============================] - 186s 2s/step - loss: 0.3177 - acc: 0.8670 - val_loss: 0.4265 - val_acc: 0.8400
- 训练完成后把模型保存下来
model.save('mangoout/smileAndUnsmile_1.h5')
- 使用图表来绘制在训练过程中模型对训练和验证数据的损失(loss)和准确性(accuracy)数据
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
- 可能由于我们提前对图片进行了裁剪筛选操作,过度拟合