Keras實例:人臉識別

1、 olivettifaces人臉數據庫簡介

Olivetti Faces是紐約大學的一個比較小的人臉庫,由40個人的400張圖片構成,即每個人的人臉圖片為10張。每張圖片的灰度級為8位,每個像素的灰度大小位於0-255之間,每張圖片大小為64×64。如下圖,這個圖片大小是1190*942,一共有20*20張人臉,故每張人臉大小是(1190/20)*(942/20)即57*47=2679:

Keras實例:人臉識別


本文所用的訓練數據就是這張圖片,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、數據的讀取;標籤的劃分

<code># 讀取整張圖片的數據,並設置對應標籤
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
/<code>

依照圖片的path地址,讀取圖片的主要信息,根據上面的註釋大家都能理解每一步都是做什麼的,主要介紹以下幾個地方:

  1. 設置圖片標籤:每10張圖片設置一個相同的標籤,
  2. 分割數據集:每個人10張照片中,前8張用做訓練,第9張用做內測驗證,第10張用做外測,也是按照像素索引進行劃分;
  3. 函數的返回值就是3個元組,分別是訓練集、內測驗證集、外測測試集;

2、CNN網絡的搭建

<code># 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
/<code>

Kreas框架是不是看起來很簡潔,它的語法主體就包含在以下聲明中:

<code>from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.optimizers import SGD # 梯度下降的優化器
/<code>
  • Sequential:模型初始化
  • Dense:全連接層
  • Flatten:合併拉伸函數
  • SGD:optimizers,優化器
  • Dropout、Activation、Conv2D、MaxPooling2D就不介紹了,各自的參數含義也請參考卷積神經網絡(CNN)原理

3、訓練過程,保存參數

<code># 訓練過程,保存參數
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
/<code>

4、測試過程,調用參數

<code># 測試過程,調用參數
def get_test_model(model,X,Y):
model.load_weights('model_weights.h5')
score = model.evaluate(X, Y, verbose=0)
return score /<code>

三、Kreas源碼及結果展示

<code># -*- 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/<code>


分享到:


相關文章: