1、 olivettifaces人脸数据库简介
Olivetti Faces是纽约大学的一个比较小的人脸库,由40个人的400张图片构成,即每个人的人脸图片为10张。每张图片的灰度级为8位,每个像素的灰度大小位于0-255之间,每张图片大小为64×64。如下图,这个图片大小是1190*942,一共有20*20张人脸,故每张人脸大小是(1190/20)*(942/20)即57*47=2679:
本文所用的训练数据就是这张图片,400个样本,40个类别
要运行CNN算法,这张图片必须先转化为数组(或者说矩阵),这个用到python的图像库PIL,几行代码就可以搞定 。
训练机器学习算法,我们一般将原始数据分成训练数据(training_set)、验证数据(validation_set)、测试数据(testing_set)。本程序将training_set、validation_set、testing_set分别设置为320、40、40个样本。它们的label为0~39,对应40个不同的人。这部分的代码如下:
2、 伪代码讲解
1、数据的读取;标签的划分
# 读取整张图片的数据,并设置对应标签
def get_load_data(dataset_path):
img = Image.open(dataset_path)
# 数据归一化。asarray是使用原内存将数据转化为np.ndarray
img_ndarray = np.asarray(img, dtype = 'float64')/255
# 400 pictures, size: 57*47 = 2679
faces_data = np.empty((400, 2679))
for row in range(20):
for column in range(20):
# flatten可将多维数组降成一维
faces_data[row*20+column] = np.ndarray.flatten(img_ndarray[row*57:(row+1)*57, column*47:(column+1)*47])
# 设置图片标签
label = np.empty(400)
for i in range(40):
label[i*10:(i+1)*10] = i
label = label.astype(np.int)
# 分割数据集:每个人前8张图片做训练,第9张做验证,第10张做测试;所以train:320,valid:40,test:40
train_data = np.empty((320, 2679))
train_label = np.empty(320)
valid_data = np.empty((40, 2679))
valid_label = np.empty(40)
test_data = np.empty((40, 2679))
test_label = np.empty(40)
for i in range(40):
train_data[i*8:i*8+8] = faces_data[i*10:i*10+8] # 训练集对应的数据
train_label[i*8:i*8+8] = label[i*10 : i*10+8] # 训练集对应的标签
valid_data[i] = faces_data[i*10+8] # 验证集对应的数据
valid_label[i] = label[i*10+8] # 验证集对应的标签
test_data[i] = faces_data[i*10+9] # 测试集对应的数据
test_label[i] = label[i*10+9] # 测试集对应的标签
train_data = train_data.astype('float32')
valid_data = valid_data.astype('float32')
test_data = test_data.astype('float32')
result = [(train_data, train_label), (valid_data, valid_label), (test_data, test_label)]
return result
依照图片的path地址,读取图片的主要信息,根据上面的注释大家都能理解每一步都是做什么的,主要介绍以下几个地方:
- 设置图片标签:每10张图片设置一个相同的标签,
- 分割数据集:每个人10张照片中,前8张用做训练,第9张用做内测验证,第10张用做外测,也是按照像素索引进行划分;
- 函数的返回值就是3个元组,分别是训练集、内测验证集、外测测试集;
2、CNN网络的搭建
# CNN主体
def get_set_model(lr=0.005,decay=1e-6,momentum=0.9):
model = Sequential()
# 卷积1+池化1
if K.image_data_format() == 'channels_first':
model.add(Conv2D(nb_filters1, kernel_size=(3, 3), input_shape = (1, img_rows, img_cols)))
else:
model.add(Conv2D(nb_filters1, kernel_size=(2, 2), input_shape = (img_rows, img_cols, 1)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 卷积2+池化2
model.add(Conv2D(nb_filters2, kernel_size=(3, 3)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 全连接层1+分类器层
model.add(Flatten())
model.add(Dense(1000)) #Full connection
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(40))
model.add(Activation('softmax'))
# 选择设置SGD优化器参数
sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd)
return model
Kreas框架是不是看起来很简洁,它的语法主体就包含在以下声明中:
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.optimizers import SGD # 梯度下降的优化器
- Sequential:模型初始化
- Dense:全连接层
- Flatten:合并拉伸函数
- SGD:optimizers,优化器
- Dropout、Activation、Conv2D、MaxPooling2D就不介绍了,各自的参数含义也请参考卷积神经网络(CNN)原理
3、训练过程,保存参数
# 训练过程,保存参数
def get_train_model(model,X_train, Y_train, X_val, Y_val):
model.fit(X_train, Y_train, batch_size = batch_size, epochs = epochs,
verbose=1, validation_data=(X_val, Y_val))
# 保存参数
model.save_weights('model_weights.h5', overwrite=True)
return model
4、测试过程,调用参数
# 测试过程,调用参数
def get_test_model(model,X,Y):
model.load_weights('model_weights.h5')
score = model.evaluate(X, Y, verbose=0)
return score
三、Kreas源码及结果展示
# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/06/05;11:41
# -*- python3.5
"""
olivetti Faces是纽约大学组建的一个比较小的人脸数据库。有40个人,每人10张图片,组成一张有400张人脸的大图片。
像素灰度范围在[0,255]。整张图片大小是1190*942,20行320列,所以每张照片大小是(1190/20)*(942/20)= 57*47
程序需配置h5py:python -m pip install h5py
博客地址:https://blog.csdn.net/zzZ_CMing,更多机器学习源码
"""
import numpy as np
from PIL import Image
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.optimizers import SGD # 梯度下降的优化器
from keras.utils import np_utils
from keras import backend as K
# 读取整张图片的数据,并设置对应标签
def get_load_data(dataset_path):
img = Image.open(dataset_path)
# 数据归一化。asarray是使用原内存将数据转化为np.ndarray
img_ndarray = np.asarray(img, dtype = 'float64')/255
# 400 pictures, size: 57*47 = 2679
faces_data = np.empty((400, 2679))
for row in range(20):
for column in range(20):
# flatten可将多维数组降成一维
faces_data[row*20+column] = np.ndarray.flatten(img_ndarray[row*57:(row+1)*57, column*47:(column+1)*47])
# 设置图片标签
label = np.empty(400)
for i in range(40):
label[i*10:(i+1)*10] = i
label = label.astype(np.int)
# 分割数据集:每个人前8张图片做训练,第9张做验证,第10张做测试;所以train:320,valid:40,test:40
train_data = np.empty((320, 2679))
train_label = np.empty(320)
valid_data = np.empty((40, 2679))
valid_label = np.empty(40)
test_data = np.empty((40, 2679))
test_label = np.empty(40)
for i in range(40):
train_data[i*8:i*8+8] = faces_data[i*10:i*10+8] # 训练集对应的数据
train_label[i*8:i*8+8] = label[i*10 : i*10+8] # 训练集对应的标签
valid_data[i] = faces_data[i*10+8] # 验证集对应的数据
valid_label[i] = label[i*10+8] # 验证集对应的标签
test_data[i] = faces_data[i*10+9] # 测试集对应的数据
test_label[i] = label[i*10+9] # 测试集对应的标签
train_data = train_data.astype('float32')
valid_data = valid_data.astype('float32')
test_data = test_data.astype('float32')
result = [(train_data, train_label), (valid_data, valid_label), (test_data, test_label)]
return result
# CNN主体
def get_set_model(lr=0.005,decay=1e-6,momentum=0.9):
model = Sequential()
# 卷积1+池化1
if K.image_data_format() == 'channels_first':
model.add(Conv2D(nb_filters1, kernel_size=(3, 3), input_shape = (1, img_rows, img_cols)))
else:
model.add(Conv2D(nb_filters1, kernel_size=(2, 2), input_shape = (img_rows, img_cols, 1)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 卷积2+池化2
model.add(Conv2D(nb_filters2, kernel_size=(3, 3)))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 全连接层1+分类器层
model.add(Flatten())
model.add(Dense(1000)) #Full connection
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(40))
model.add(Activation('softmax'))
# 选择设置SGD优化器参数
sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd)
return model
# 训练过程,保存参数
def get_train_model(model,X_train, Y_train, X_val, Y_val):
model.fit(X_train, Y_train, batch_size = batch_size, epochs = epochs,
verbose=1, validation_data=(X_val, Y_val))
# 保存参数
model.save_weights('model_weights.h5', overwrite=True)
return model
# 测试过程,调用参数
def get_test_model(model,X,Y):
model.load_weights('model_weights.h5')
score = model.evaluate(X, Y, verbose=0)
return score
# [start]
epochs = 35 # 进行多少轮训练
batch_size = 40 # 每个批次迭代训练使用40个样本,一共可训练320/40=8个网络
img_rows, img_cols = 57, 47 # 每张人脸图片的大小
nb_filters1, nb_filters2 = 20, 40 # 两层卷积核的数目(即输出的维度)
if __name__ == '__main__':
# 将每个人10张图片,按8:1:1的比例拆分为训练集、验证集、测试集数据
(X_train, y_train), (X_val, y_val),(X_test, y_test) = get_load_data('olivettifaces.gif')
if K.image_data_format() == 'channels_first': # 1为图像像素深度
X_train = X_train.reshape(X_train.shape[0],1,img_rows,img_cols)
X_val = X_val.reshape(X_val.shape[0], 1, img_rows, img_cols)
X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_val = X_val.reshape(X_val.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
print('X_train shape:', X_train.shape)
# convert class vectors to binary class matrices
Y_train = np_utils.to_categorical(y_train, 40)
Y_val = np_utils.to_categorical(y_val, 40)
Y_test = np_utils.to_categorical(y_test, 40)
# 训练过程,保存参数
model = get_set_model()
get_train_model(model, X_train, Y_train, X_val, Y_val)
score = get_test_model(model, X_test, Y_test)
# 测试过程,调用参数,得到准确率、预测输出
model.load_weights('model_weights.h5')
classes = model.predict_classes(X_test, verbose=0)
test_accuracy = np.mean(np.equal(y_test, classes))
print("last accuarcy:", test_accuracy)
for i in range(0,40):
if y_test[i] != classes[i]:
print(y_test[i], '被错误分成', classes[i]);
运行结果:
X_train shape: (320, 57, 47, 1)
Train on 320 samples, validate on 40 samples
Epoch 1/35
320/320 [==============================] - 3s 10ms/step - loss: 3.6880 - val_loss: 3.6834
Epoch 2/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6873 - val_loss: 3.6807
Epoch 3/35
320/320 [==============================] - 2s 5ms/step - loss: 3.6814 - val_loss: 3.6775
Epoch 4/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6792 - val_loss: 3.6738
Epoch 5/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6768 - val_loss: 3.6697
Epoch 6/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6682 - val_loss: 3.6649
Epoch 7/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6638 - val_loss: 3.6594
Epoch 8/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6525 - val_loss: 3.6525
Epoch 9/35
320/320 [==============================] - 2s 7ms/step - loss: 3.6468 - val_loss: 3.6438
Epoch 10/35
320/320 [==============================] - 2s 7ms/step - loss: 3.6371 - val_loss: 3.6329
Epoch 11/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6213 - val_loss: 3.6197
Epoch 12/35
320/320 [==============================] - 2s 6ms/step - loss: 3.6133 - val_loss: 3.6017
Epoch 13/35
320/320 [==============================] - 2s 6ms/step - loss: 3.5855 - val_loss: 3.5778
Epoch 14/35
320/320 [==============================] - 2s 6ms/step - loss: 3.5511 - val_loss: 3.5453
Epoch 15/35
320/320 [==============================] - 2s 6ms/step - loss: 3.5156 - val_loss: 3.5000
Epoch 16/35
320/320 [==============================] - 2s 6ms/step - loss: 3.4572 - val_loss: 3.4356
Epoch 17/35
320/320 [==============================] - 2s 5ms/step - loss: 3.3838 - val_loss: 3.3436
Epoch 18/35
320/320 [==============================] - 2s 6ms/step - loss: 3.2650 - val_loss: 3.2079
Epoch 19/35
320/320 [==============================] - 2s 5ms/step - loss: 3.0900 - val_loss: 3.0246
Epoch 20/35
320/320 [==============================] - 2s 6ms/step - loss: 2.8635 - val_loss: 2.7595
Epoch 21/35
320/320 [==============================] - 2s 7ms/step - loss: 2.5709 - val_loss: 2.4273
Epoch 22/35
320/320 [==============================] - 2s 6ms/step - loss: 2.2209 - val_loss: 2.0682
Epoch 23/35
320/320 [==============================] - 2s 5ms/step - loss: 1.8098 - val_loss: 1.7000
Epoch 24/35
320/320 [==============================] - 2s 5ms/step - loss: 1.4596 - val_loss: 1.3756
Epoch 25/35
320/320 [==============================] - 2s 5ms/step - loss: 1.1423 - val_loss: 1.1275
Epoch 26/35
320/320 [==============================] - 2s 6ms/step - loss: 0.8719 - val_loss: 0.9146
Epoch 27/35
320/320 [==============================] - 2s 6ms/step - loss: 0.7283 - val_loss: 0.7697
Epoch 28/35
320/320 [==============================] - 2s 6ms/step - loss: 0.5765 - val_loss: 0.6689
Epoch 29/35
320/320 [==============================] - 2s 6ms/step - loss: 0.4444 - val_loss: 0.5779
Epoch 30/35
320/320 [==============================] - 2s 6ms/step - loss: 0.4039 - val_loss: 0.5126
Epoch 31/35
320/320 [==============================] - 2s 5ms/step - loss: 0.3047 - val_loss: 0.4686
Epoch 32/35
320/320 [==============================] - 2s 6ms/step - loss: 0.2877 - val_loss: 0.4124
Epoch 33/35
320/320 [==============================] - 2s 6ms/step - loss: 0.2184 - val_loss: 0.3725
Epoch 34/35
320/320 [==============================] - 2s 5ms/step - loss: 0.1842 - val_loss: 0.3516
Epoch 35/35
320/320 [==============================] - 2s 5ms/step - loss: 0.1684 - val_loss: 0.3328
last accuarcy: 0.925
8.0 被错误分成 34
18.0 被错误分成 6
39.0 被错误分成 8