基于卷积神经网络对男女性眼睛进行识别


  依靠人工智能,往往能识别单靠肉眼是难以分辨的东西。例如,耳生物识别已成为一个热门的研究课题,能够依靠耳垂来进行性别的识别,而人工智能单从耳部图像分辨性别准确率惊人。

  因此从单一部位图像进行性别识别的角度而言,本文选择对男女性眼睛进行识别,探究单从眼睛图像分辨性别是否有良好效果。

男女性眼睛数据集

数据集介绍

  男女眼睛性数据集共有11525张图像,并且各个类别的图像数量如下表所示:

类别图片数量
男性6323
女性5202

  各个类别的数据集展示如下:

表1. 男性眼睛数据集展示

男性眼睛数据集

表2. 女性眼睛数据集展示

女性眼睛数据集

数据预处理

  将图片的像素作为模型的特征,将图像的类别作为模型的标签,但是因为图像的像素为0~255的整型,数量级差别较大。所以需要对特征进行标准化,将原来0~255的特征值映射到[0,1]区间。并且因为标签为数字编码,所以在计算时,需要把离散的数字编码转化为独热编码。最后对数据集进行拆分,选择数据集中的80%作为训练集,10%作为测试集,10%作为验证集。

使用CNN网络对男女性眼睛数据集进行分类

  使用CNN网络对男女性眼睛数据集进行分类,首先展示CNN的网络结构图如下:
CNN网络结构图
一个完整的CNN包含如下的结构:

  输入层: 网络的输入,以图像为例,一般代表一张图片的像素矩阵。

  卷积层: CNN中最为重要的部分。通过卷积操作获取图像的局部区域信息。

  池化层: 对数据进行降采样,缩小数据规模,收集关键数据,同时提高计算速度。

  全连接层: 将学到的特征表示映射到类标签空间,起到分类器的作用。

输入层

  表示整个神经网络的输入。在本文中,将以处理图像数据为例。在处理图像时,输入层一般代表一张图像的三维像素矩阵,大小通常为 w × h × 3 w\times h\times 3 w×h×3或者 w × h × 1 w\times h\times 1 w×h×1的矩阵,其中这三个维度分别图像的宽度、长度、深度。深度也称为通道数,在彩色图像中有R、G、B三种色彩通道,而黑白图像只有一种色彩通道。
在这里插入图片描述

卷积层

  卷积层是CNN结构中最重要的部分。它通过卷积操作获取图像的局部区域信息。

  卷积操作通过使用过滤器filter,将当前层神经网络上的子节点矩阵转化为下一层神经网络上的一个节点矩阵,得到特征图。卷积操作就是filter矩阵跟filter覆盖的图片局部区域矩阵对应的每个元素相乘后累加求和。

池化层

  池化层可以缩小特征图,保留有用信息,得到一个更小的子图来表征原图。池化操作本质上是对图片进行降采样,可以认为是将一张分辨率高的图片转化为分辨率较低的子图,保留的子图不会对图片内容理解产生太大影响。

全连接层在经过多层的卷积层和池化层操作后,一般会有1-2个全连接层,给出最后的分类结果。全连接层在整个卷积神经网络中起到“分类器”的作用,它将学到的特征表示映射到类标签空间。

  在实际中,全连接层可由卷积操作实现:对前层是全连接的全连接层可以转化为卷积核为 1 × 1 1\times 1 1×1的卷积;而前层是卷积层的全连接层可以转化为卷积核为的全局卷积, h × w h\times w h×w分别为前层卷积输出结果的高和宽。

  全连接层对前层输出的特征进行加权求和,并把结果输入到激活函数,最终完成目标的分类。加权求和计算公式如下:

y w , b ( x ) = f ( ∑ i = 1 n w i x i + b i ) y_{w,b}(x)=f(\sum_{i=1}^{n}w_ix_i+b_i ) yw,b(x)=f(i=1nwixi+bi)

  其中,wi 是全连接层中的权重系数,xi 是上一层第 i 个神经元的值,bi 是全连接层的偏置量。 全连接层的结构如下图所示。

在这里插入图片描述

  全连接层神经元全部互相连接,其参数量占整个网络模型的 80%以上。全连接层中的各个神经元输出的特征信息基本都是重复的,这使得网络在训练过程中容易过拟合,往往通过在全连接层后添加 Dropout 的方法来避免。Dropout 层在网络训练时可以随机使部分神经元失活,这样就可以降低神经元之间的相关性,避免网络过拟合,提高网络的泛化能力。Dropout 原理如下图所示:
在这里插入图片描述

激活函数

  由于本文所研究的问题属于二分类问题,所以需要使用Sigmoid作为网络输出层的激活函数,公式如下:
S i g m o i d ( x ) = 1 1 + e − x Sigmoid(x)=\frac{1}{1+e^{-x}} Sigmoid(x)=1+ex1
  Sigmoid函数不仅可以将输出值映射到[0,1]区间,还满足所有输出值之和为1的特性。输出层经过Sigmoid函数计算后,得到的值代表了当前样本属于每个类别的概率,且概率之和为1。 通过Sigmoid函数可以将输出层转译为类别概率,在二分类问题中使用较多。

   并且使用二值交叉熵函数作为损失函数,二值交叉熵可以很好地衡量两个分布之间的‘距离’,公式如下:
L o s s ( y , y ^ = 1 1 N ∑ N i = 1 [ y i l o g y i ^ + ( 1 − y i ) l o g ( 1 − y i ^ ) ] ) Loss(y,\hat{y} =1\frac{1}{N}\sum_{N}^{i=1}[y_ilog\hat{y_{i}}+(1-y_i)log(1-\hat{y_i} ) ] ) Loss(y,y^=1N1Ni=1[yilogyi^+(1yi)log(1yi^)])
  其中, N N N 为样本容量, y y y 为标签的真实值, y i ^ \hat{y_{i}} yi^为标签的预测值。

  当对应类别上的概率为 1时,损失函数取得最小值 0,此时网络输出的标签预测值与标签的真实值完全一致,神经网络取得最优的状态。

模型训练

  首先设置模型训练的参数如下表所示,从而对模型进行训练。
在这里插入图片描述
  其中,Imagesize 为输入图像的尺寸; Optimizer 为优化器,这里选择 Adam 作为CNN 网络的优化器,其是一种基于梯度随机的目标函数优化的算法,默认学习率是 0.001。 其中 Learning rate 是学习率,Weight decay 是权重衰减率,是每次更新时的学习率下降的大小, Epoch 为模型的训练次数。
  由于本文选用的数据集量不大,且是二分类,因此训练次数不用很多,本文设为50次进行模型的训练; Batch size 为批次训练大小,即一次并行计算 32个样本的数据; Validation size 为验证集在数据集中比例,选择训练集中的 80% 作为训练集,10% 作为验证集,即训练集的样本容量是9220,验证集样本容量是1152。

模型评估

  训练过程中损失值和准确率的变化进行可视化
在这里插入图片描述
  可以看出整个的训练过程中损失值一直在降低,准确率一直在提高,可见训练效果不错。测试集测试到一定程度时,损失值有所提高,而准确率在一个范围内进行波动,最终模型准确率较高,可得模型具有一定参考意义。

TensorBoard可视化

将模型在TensorBoard上进行可视化。

TensorBoard Distributions and Histograms 分布和直方图

  这组图10显示了构成模型的张量。
在这里插入图片描述
  在每个图的水平轴上显示 epoch 的个数,在垂直轴上显示了每个张量的值。 这些图表基本上显示了这些张量如何随着训练的进行而随时间变化。 较暗的区域显示值在某个区域停留了更长的时间。当模型权重在每个epoch 都没有正确更新,可以通过查看快速发现这问题。
在这里插入图片描述

  图11显示了模型中张量的不同视图。每个图都有五十个相互堆叠的直方图,代表训练过的50个epoch中的每一个。它们显示了张量权重倾向于集中在哪个区域的信息。这对于调试模型的行为,发现异常非常有用。

部署模型

  将训练好的模型部署成网页:
在这里插入图片描述
  导入测试集图片进行使用:
在这里插入图片描述

  考虑到进行检测时不一定会只裁剪成眼睛部分进行识别,很多时候会导入整张图片进行辨识,因此选择整张图片导入后裁剪成只有眼睛进行辨识。
在这里插入图片描述

总代码

# 设置工作目录,否则运行目录有问题
import os
os.chdir('D:/myPython/19DL')
print (os.getcwd())



#导入需要的模块
#导入TensorFlow模块,keras作为tensorflow的子模块:tf.keras
import time
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator



# Settings
batch_size = 32
image_size = 32
train_data_path = "E:/deel/archive/train"
test_data_path = "E:/deel/archive/test"




datagen = ImageDataGenerator(rescale=1/255,
                             validation_split=0.1)
train_db = datagen.flow_from_directory(
    train_data_path,
    target_size=(image_size, image_size),
    batch_size=batch_size,
    class_mode='categorical',
    subset='training')

val_db = datagen.flow_from_directory(
    train_data_path,
    target_size=(image_size, image_size),
    batch_size=batch_size,
    class_mode='categorical',
    subset='validation')

test_db = ImageDataGenerator(rescale=1/255).flow_from_directory(
    test_data_path,
    target_size=(image_size, image_size),
    batch_size=batch_size,
    class_mode='categorical')


# 查看数据维度
print("train data:",'images:',(len(train_db.labels), 3, image_size, image_size),
      " labels:",len(train_db.labels))
print("val  data:",'images:',(len(val_db.labels), 3, image_size, image_size),
      " labels:",len(val_db.labels))
print("test  data:",'images:',(len(test_db.labels), 3, image_size, image_size),
      " labels:",len(test_db.labels))



# 定义标签字典(将文字映射到数字)
label_dict={0:"female",1:"male"}



"""----------------------------
@@@     建立卷积神经网络模型
----------------------------"""
model = tf.keras.Sequential()

"""
建立卷积层1与池化层1
加入Dropout(0.25)的功能是每次训练迭代时,
会随机地在神经网络中放弃25%的神经元,以免过度拟合。
"""

"""+++++++++++++++
卷积层1与池化层1
filters=32 设置随机产生32个滤镜(32个)
kernel_size=(3,3) 每个滤镜大小为3x3
input_shape=(32, 32,3) 代表图像大小为32x32,3代表彩色图像,代表三色RGB值
activation='relu'代表设置的激活函数:ReLU
padding='same'设置卷积运算产生的卷积图像大小不变
"""
model.add(tf.keras.layers.Conv2D(filters=32,kernel_size=(3,3),
                 input_shape=(image_size, image_size, 3), 
                 activation='relu', 
                 padding='same'))
# 避免过度拟合
model.add(tf.keras.layers.Dropout(rate=0.25))

"""池化层1
pool_size=(2, 2)执行第一次缩减采样,将32x32的图像缩小为16x16的图像
缩小后图像任然为32个。
"""
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))


"""++++++++++++++++++
建立卷积层2与池化层2
卷积层2
执行第2次卷积运算,将原来32个图像转为64个图像,图片大小为16x16
"""
model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=(3, 3), 
                 activation='relu', padding='same'))

##避免过度拟合
model.add(tf.keras.layers.Dropout(0.25))

"""池化层2
pool_size=(2, 2)执行第一次缩减采样,将16x16的图像缩小为8x8的图像
缩小后图像为64个。
"""
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))


"""+++++++++++++++++++++++++++++++++++++
建立神经网络(平坦层、隐藏层、输出层) """
#平坦层
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dropout(rate=0.25))

#隐藏层(1024个神经元)
model.add(tf.keras.layers.Dense(1024, activation='relu'))
model.add(tf.keras.layers.Dropout(rate=0.25))

#输出层(十种分类)
model.add(tf.keras.layers.Dense(2, activation='sigmoid'))

#查看卷积神经网络摘要
print(model.summary())



"""------------------------------------------
  开始训练模型: 训练epochs=10周期 
  CPU需要17分钟
  GPU 30秒
---------------------------------------------"""

# 启动GPU
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "1"
#log_dir = "E:/deel/Tensorboard/fit/"
#tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)

model.compile(loss='categorical_crossentropy',
              optimizer='adam', metrics=['accuracy'])

t1=time.time()
train_history=model.fit_generator(train_db, epochs=30, validation_data=val_db)#,callbacks=[tensorboard_callback])
t2=time.time()
CNNfit = float(t2-t1)
print("Time taken: {} seconds".format(CNNfit))

#model.save("E:/deel/Cifar10/archive/cifarCnnModelnew.h5")
#print("Saved model to disk")



# 描绘拟合曲线
import matplotlib.pyplot as plt 
def show_train_history(train_acc,test_acc):
    plt.plot(train_history.history[train_acc])
    plt.plot(train_history.history[test_acc])
    plt.title('Train History')
    plt.ylabel(train_acc)
    plt.xlabel('Epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.show()
# show_train_history('acc','val_acc') 1.x
show_train_history('accuracy','val_accuracy') 
show_train_history('loss','val_loss')


# 评估模型的准确率(测试集): 
# epochs=2周期,GPU运行 175秒,准确率:64.74%
# epochs=10周期,GPU运行 185秒,准确率:72.10%
# epochs=30周期,GPU运行 577秒,准确率:72.79%


score = model.evaluate(train_db, verbose=0)
score[1]

scores = model.evaluate(test_db, verbose=0)
scores[1]


# 预测
prediction=model.predict_classes(test_db)
prediction[:10]

# 查看预测结果
label_dict={0:"female",1:"male"}

def plot_images_labels_prediction(images,labels,prediction,
                                  idx,num=10):
    fig = plt.gcf()
    fig.set_size_inches(8, 10)
    if num>25: num=25 
    for i in range(0, num):
        ax=plt.subplot(5,5, 1+i)
        ax.imshow(images[idx],cmap='binary')

        title=str(i)+','+label_dict[labels[i][1]]
#        title=str(i)+','+label_dict[labels[i]]
        if len(prediction)>0:
            title+='=>'+label_dict[prediction[i]]

        ax.set_title(title,fontsize=10) 
        ax.set_xticks([]);ax.set_yticks([])        
        idx+=1 
    plt.show()

plot_images_labels_prediction(test_db[0][0],test_db[0][1],
                              prediction,0,25)

test_db.image_data_generator
test_db[0][1]
# 查看预测概率
Predicted_Probability=model.predict(test_db[0][0])



def show_Predicted_Probability(y,prediction,
                               x_img,Predicted_Probability,i):
    print('label:',label_dict[y[i]],'predict:',label_dict[prediction[i]])
    plt.figure(figsize=(2,2))
    plt.imshow(np.reshape((test_db[0][0])[i],(32, 32,3)))
    plt.show()
    for j in range(2):
        print(label_dict[j]+' Probability:%1.9f'%(Predicted_Probability[i][j]))

#show_Predicted_Probability(y_label_test,prediction,
#                           x_img_test,Predicted_Probability,0)
show_Predicted_Probability(test_db.labels,prediction,
                           test_db[0][0],Predicted_Probability,13)

# 模糊矩阵 confusion matrix
prediction.shape
test_db.labels.shape
test_db.labels
test_db.labels.reshape(-1)

import pandas as pd
print(label_dict)
pd.crosstab(test_db.labels.reshape(-1),prediction,
            rownames=['label'],colnames=['predict'])

print(label_dict)
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值