卷积神经网络算法(CNN)与 PCA+SVC 算法在掌纹识别算法中的比较
- 数据库介绍
100 个手掌,每个手掌 6 个原始图像和 ROI(Region of Interest)图像。ROI表示从原始手掌图像中定位裁剪出的正方形感兴趣区域,尺寸为128×128个像素。
- 任务选择
本研究报告针对掌纹的分类识别任务,目的是将掌纹图像分为100个类别中的一类。
- 评价指标
- 精度
本研究报告使用了两种算法,其中卷积神经网络的精度为0.845,而PCA+SVC算法的精度达到1.
- 鲁棒性
- 复杂度
- 编程语言
本研究使用python编程语言在idea环境下运行
卷积神经网络算法完整源代码
import os
import tensorflow as tf
import numpy as np
from IPython.core.display_functions import clear_output
from keras.preprocessing.image import load_img, img_to_array
from tensorflow import keras
import matplotlib.pyplot as plt
train_images=[]
test_images=[]
train_labels = [i for i in range(100) for _ in range(4)]
test_labels=[i for i in range(100) for _ in range(2)]
data_dir = 'C:/Users/86137/OneDrive/桌面/机器学习/PolyU_Palmprint_600'
# 获取所有图像文件的路径
image_paths = [os.path.join(data_dir, f) for f in os.listdir(data_dir) if f.endswith('.bmp')]
for i in range(0, len(image_paths), 6):
# 获取每次循环中的6张图像路径
batch = image_paths[i:i + 6]
# 将前4张图像路径加入训练集
train_images.extend(batch[:4])
# 将后2张图像路径加入测试集
test_images.extend(batch[4:])
def load_and_process_image(image_path):
img = load_img(image_path, target_size=(224, 224)) # 加载图像并调整大小
img_array = img_to_array(img) # 将图像转换为数组
img_array = img_array / 255.0 # 归一化处理
return img_array
# 加载训练集和测试集的图像数据
train_data = np.array([load_and_process_image(image_path) for image_path in train_images])
test_data = np.array([load_and_process_image(image_path) for image_path in test_images])
train_labels = np.array(train_labels)
test_labels = np.array(test_labels)
#三个卷积层,分别包含 32、64 和 128 个滤波器(filter)。每个滤波器的大小为 (3, 3),并采用 ReLU 激活函数。第一层还需要指定输入图像的形状为 (224, 224, 3)
model = keras.models.Sequential([
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)),
tf.keras.layers.MaxPooling2D((2, 2)),
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D((2, 2)),
tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(100, activation='softmax')
])
# 输出模型的层数和参数数量
model.summary()
# 编译模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 定义一个回调函数,用于绘制训练过程中的准确率变化图表
class PlotAccuracy(tf.keras.callbacks.Callback):
def on_train_begin(self, logs={}):
self.i = 0
self.x = []
self.acc = []
self.val_acc = []
self.fig = plt.figure()
self.logs = []
def on_epoch_end(self, epoch, logs={}):
self.logs.append(logs)
self.x.append(self.i)
self.acc.append(logs.get('accuracy'))
self.val_acc.append(logs.get('val_accuracy'))
self.i += 1
clear_output(wait=True)
plt.plot(self.x, self.acc, label="accuracy")
plt.plot(self.x, self.val_acc, label="val_accuracy")
plt.title("Training Progress")
plt.xlabel("Epochs")
plt.ylabel("Accuracy")
plt.legend()
plt.show()
# 创建回调函数实例
plot_acc = PlotAccuracy()
# 训练模型,并使用回调函数绘制训练过程中的准确率变化图表
history = model.fit(train_data, train_labels, epochs=16, validation_data=(test_data, test_labels), callbacks=[plot_acc])
# 在测试集上评估模型的准确率
test_loss, test_acc = model.evaluate(test_data, test_labels)
print('Test accuracy:', test_acc)
# 使用数据增强技术来提高模型的鲁棒性
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True)
train_generator = train_datagen.flow(train_data, train_labels, batch_size=32)
model.fit(train_generator, epochs=16, validation_data=(test_data, test_labels))
# 绘制模型结构图
tf.keras.utils.plot_model(model, to_file='model.png')
# 绘制训练过程中的损失和准确率变化图表
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
PCA+SVC算法完整源代码
import os
import numpy as np
from PIL import Image
from sklearn.decomposition import PCA
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score
from sklearn.pipeline import make_pipeline
from sklearn.svm import SVC
import matplotlib.pyplot as plt
data_dir = "C:/Users/86137/OneDrive/桌面/机器学习/palmbases/"
train_images = []
train_labels = []
test_images = []
test_labels = []
param_grid = {
'svc__C': [1, 5, 10, 50,100],
'svc__gamma': [0.00005,0.0001, 0.0005, 0.001, 0.005]
}
for i in range(0, 100):
# 读取训练集,使用每个掌纹的前五张图片作为训练集
for j in range(1, 6):
file_name = os.path.join(data_dir, f'P_{i}_{j}.bmp')
image = Image.open(file_name).convert('L')
image = np.array(image)
train_images.append(image)
train_labels.append(i)
# 读取测试集,使用每个掌纹的第六张图片作为测试集
file_name = os.path.join(data_dir, f'P_{i}_6.bmp')
image = Image.open(file_name).convert('L')
image = np.array(image)
test_images.append(image)
test_labels.append(i)
train_data = np.array(train_images)
test_data = np.array(test_images)
labels_train = np.array(train_labels)
labels_test = np.array(test_labels)
train_data = train_data.reshape((500, 16384))
test_data = test_data.reshape((100, 16384))
pca = PCA(n_components=300, whiten=True, random_state=42, svd_solver='randomized')
svc = SVC(kernel='rbf', class_weight='balanced')
model = make_pipeline(pca, svc)
cv_method = StratifiedKFold(n_splits=3)
grid = GridSearchCV(model, param_grid, cv=cv_method,return_train_score=True)
grid.fit(train_data, labels_train)
# 获取最佳参数
best_params = grid.best_params_
# 将参数转换为列表
param_values = list(best_params.values())
param_names = list(best_params.keys())
# 绘制条形图
plt.bar(param_names, param_values)
plt.xlabel('Parameter Name')
plt.ylabel('Parameter Value')
plt.title('Best Parameters')
plt.show()
print(grid.best_params_)
model = grid.best_estimator_
decision_scores = grid.decision_function(test_data)
print('Decision Scores:', decision_scores)
# 选择一个样本的索引
sample_index = 0
# 获取该样本的置信度分数
sample_scores = decision_scores[sample_index]
# 获取类别标签
class_labels = np.arange(len(sample_scores))
# 绘制条形图
plt.bar(class_labels, sample_scores)
plt.xlabel('Class Label')
plt.ylabel('Confidence Score')
plt.title('Confidence Scores for Sample {}'.format(sample_index))
plt.xticks(class_labels)
plt.show()
test_out = model.predict(test_data)
accuracy = accuracy_score(labels_test, test_out)
train_accuracies = grid.cv_results_['mean_train_score']
# 绘制折线图
plt.plot(range(len(train_accuracies)), train_accuracies)
plt.xlabel('Iterations')
plt.ylabel('Train Accuracy')
plt.title('Train Accuracy during Training')
plt.show()
# 计算模型的鲁棒性
cv_results = grid.cv_results_
mean_test_scores = cv_results['mean_test_score']
std_test_scores = cv_results['std_test_score']
# 绘制鲁棒性图
plt.errorbar(range(len(mean_test_scores)), mean_test_scores, yerr=std_test_scores, fmt='-o')
plt.xlabel('Parameter Combination')
plt.ylabel('Test Accuracy')
plt.title('Robustness of the Model')
plt.xticks(range(len(mean_test_scores)), cv_results['params'], rotation=45)
plt.show()
# 计算模型的复杂度
train_accuracies = cv_results['mean_train_score']
# 绘制复杂度图
plt.plot(range(len(train_accuracies)), train_accuracies)
plt.xlabel('Iterations')
plt.ylabel('Train Accuracy')
plt.title('Complexity of the Model')
plt.show()
print("准确率:", accuracy)
- 算法原理
卷积神经网络算法
对图像(不同的数据窗口数据)和滤波矩阵(一组固定的权重:因为每个神经元的多个权重固定,所以又可以看做一个恒定的滤波器filter)做内积(逐个元素相乘再求和)的操作就是所谓的『卷积』操作,也是卷积神经网络的名字来源。在卷积神经网络(CNN)中,通常会同时使用前向传播和反向传播来训练模型。这两种传播过程是深度学习模型训练的关键组成部分。
- 首先通过前向传播计算模型的输出
- 卷积层(Convolutional Layer)
卷积层是CNN的核心组成部分,它通过使用多个滤波器(也称为卷积核)对输入数据进行卷积操作。每个滤波器在输入数据上进行滑动,计算出对应位置的卷积结果。这样可以提取出输入数据的局部特征。卷积操作的数学表示如下:
Ci,j=k=1∑ml=1∑nIi+k-1,j+l-1Kk,l
其中,I表示输入的数据,K表示卷积核,C表示卷积结果。在卷积操作时,卷积核会逐渐滑动并扫描整个输入数据,从而提取出与卷积核相关的特征信息。
- 激活函数(Activation Function)
在卷积操作后,通常会对卷积结果应用一个非线性激活函数,如ReLU(Rectified Linear Unit)。激活函数的作用是引入非线性特性,增强模型的表达能力。ReLU函数的数学表示如下:
f(z)=max(0,z)
其中,z表示输入数据。
- 池化层(Pooling Layer)
池化层用于减小特征图的空间尺寸,同时保留重要的特征信息。最常用的池化操作是最大池化(Max Pooling),它在每个区域中选择最大值作为池化结果。最大池化的数学表示如下:
Si,j=k,lmax(Ii+k-1,j+l-1)
其中,I表示输入数据,S表示池化结果。
- 全连接层(Fully Connected Layer)
在经过一系列卷积和池化操作后,通常会将特征图展平为一维向量,并通过全连接层进行分类或回归等任务的处理。全连接层中的神经元与前一层的所有神经元相连。全连接层的数学表示如下:
Y=f(XW+b)
其中,X表示输入数据,W表示权重矩阵,b表示偏置项,f表示激活函数,Y表示输出结果。
- 利用反向传播计算梯度并更新参数
反向传播是CNN中用于训练模型的关键过程,通过反向传播,模型可以根据预测结果与真实标签之间的差异来更新模型参数,以使预测结果逐渐接近真实标签。下面是反向传播的主要步骤:
- 损失函数(Loss Function)
损失函数用于衡量模型的预测结果与真实标签之间的差距。常见的损失函数包括均方误差(Mean Squared Error)和交叉熵损失(Cross Entropy Loss)等。损失函数的数学表示如下:
L(θ)=N1i=1∑Nl(f(xi;θ),yi)
其中,f表示CNN模型,xi表示输入数据,yi表示真实标签,l表示损失函数,θ表示模型参数,N表示样本数量。
- 梯度计算
通过链式法则,从输出层开始,计算损失函数对各层参数的梯度。梯度表示参数的变化对损失函数的影响程度,利用梯度可以指导参数的更新方向。具体地,梯度计算的数学表示如下:
∂L∂wj=∂L∂y∂y∂u∂u∂wj
其中,L表示损失函数,y表示输出结果,u表示中间变量,wj表示第j个参数。
- 参数更新
根据计算得到的梯度信息,使用优化算法(如随机梯度下降法)对模型参数进行更新。参数更新的目标是沿着梯度下降的方向,逐步减小损失函数的值。具体地,参数更新的数学表示如下:
wj←wj-η∂L∂wj
其中,η表示学习率。
- 反向传播
将更新后的梯度信息从输出层向输入层进行传播,以便计算更底层的参数的梯度。通过反复迭代上述过程,不断更新参数,使模型逐渐收敛于最优解。
PCA+SVC算法
PCA+SVC(Principal Component Analysis + Support Vector Classifier)算法是一种常用的机器学习方法,用于进行特征提取和分类任务。在掌纹识别中,该算法可以用于分析和识别掌纹图像。
- 数据准备:首先,需要收集一组已知标签的掌纹图像样本。每个样本都应该有一个正确的类别标签,例如"正常"或"异常"。这些图像应该经过预处理步骤,如去噪、增强,以及对比度调整等。
- 特征提取(PCA):PCA是一种常用的降维技术,用于从高维数据中提取最重要的特征。对于掌纹图像,可以将每个像素点作为一个特征,并且将图像转换为灰度图像以简化处理。然后,将所有图像样本的特征矩阵合并成一个大矩阵X。这个矩阵的大小为m×n,其中m为样本数,n为特征数。然后,通过下列公式计算主成分:
- 计算均值向量:μ=(1/m)∑xi (i=1,2,...,m)
- 计算协方差矩阵:C=(1/m)XT
- 对协方差矩阵C进行特征值分解:C=VΛVT
- 选择前k个最大的特征值,对应的特征向量为V=(v1,v2,...,vk)
- 将X映射到低维空间:Z=XTV
其中,μ为均值向量,C为协方差矩阵,V为特征向量,Λ为特征值向量,k为保留的主成分数目,Z为降维后的特征矩阵,大小为m×k。
- 训练模型(SVC):在训练阶段,使用带有已知标签的降维特征向量作为输入,训练SVC模型以学习掌纹图像的分类规则。SVC是一种监督学习算法,它通过构建一个最优的分割超平面来将样本分成不同的类别。在训练过程中,SVC会找到一个最佳的超平面,使得同一类别的样本尽可能靠近彼此,而不同类别的样本尽可能远离。这样,当有新的未知掌纹图像输入时,模型就可以根据其特征向量来预测其所属的类别。
SVC的分类函数可以表示为:
f(x)=sign(∑αiyiK(xi,x)+b)
其中,x为待分类样本,yi为第i个样本的类别标签,αi为SVC模型的系数,K(·,·)为核函数,b为偏置项。
常用的核函数有线性核、多项式核和径向基核(RBF)。其中,RBF核函数是最常用的一种核函数,其公式如下:
K(xi,xj)=exp(-γ||xi-xj||^2)
其中,γ是RBF核函数的参数,||·||表示向量的欧几里得距离。
- 特征提取和分类(测试阶段):在测试阶段,对于新的未知掌纹图像,首先将其进行与训练阶段相同的预处理,并提取与训练阶段相同的特征向量。然后,使用训练好的PCA模型将特征向量降维到与训练阶段相同的低维空间。最后,利用训练好的SVC模型对降维后的特征向量进行分类,得到该掌纹图像的类别标签。
- 程序框架
卷积神经网络算法
PCA+SVC算法
- 实验过程和结果分析
卷积神经网络算法
- 代码编写与运行结果
对于数据集的测试集与训练集的划分,我将每6张图片(即每一个人)的前4张图片作为训练集,后两张图片作为测试集,所以最终训练集有400张图片,测试集有200张图片,在我的卷积神经网络模型中,有三个卷积层,分别包含 32、64 和 128 个滤波器(filter)。每个滤波器的大小为 (3, 3),并采用 ReLU 激活函数。
- 模型参数
- 训练过程
可以看到最终训练的精确率为0.845,当训练到第16轮时精度有所下降,说明此时模型已经开始出现过拟合现象了,于是我最终截取到第15轮的训练结果
- 训练集与测试集的精度对比
从精度折线图中可以看出,从第10轮训练开始,训练集与测试集的精度都开始趋于平缓,训练集保持在1的精度,而测试集大概在0.8,但是当训练到第16轮时精度有所下降,说明此时模型已经开始出现过拟合现象了。
- 鲁棒性与复杂度分析
从结果中可以看出,模型在测试集上的准确率为1.0,表明模型具有较好的鲁棒性。此外,我们还使用数据增强技术对训练数据进行扩充,以提高模型的鲁棒性。模型的复杂度可以通过model.summary()函数输出的结果来查看,该模型共有4,532,924个可训练参数。
PCA+SVC算法
- 代码编写与运行结果
最后的训练结果显示准确率为1,说明本模型效果极好。
- 不同降维数结果对比
降维到300与100:
对比结果显示,不同降维数得到的精度变化曲线有所差异,降到300维时曲线能够快速收敛到1,而降到100维时,由于丢失了较多特征,最后的精度变低,收敛速度也变慢
- 模型最佳参数
模型最后选择的最佳参数为:'svc__C'为50,'svc__gamma'为0.0001,参数'svc__C'表示正则化参数,数值越大表示模型对误分类样本的惩罚力度越大;参数'svc__gamma'表示核函数的系数,控制数据映射到高维空间后的分布,数值越小表示映射效果越强。
- 置信度分数
这里只分析测试集中第一个样本预测后的置信度分数
可以看到这个样本在类别0上的置信度分数为几乎为100,说明它几乎有百分之100的概率为类别1,实际上它的标签确实是类别0,说明模型在测试集上的预测的置信度高,模型训练效果良好
- 模型鲁棒性分析
使用交叉验证来评估模型在不同参数组合下的性能,并计算平均测试分数和标准差。然后,使用errorbar函数绘制了鲁棒性图,显示了平均测试分数及其标准差的变化。
- 模型的复杂度分析
通过绘制训练准确率随迭代次数变化的折线图实现的。它展示了模型的复杂度随着训练的进行而变化。
- 结论和心得体会
本研究报告使用了两种算法,其中卷积神经网络的精度为0.845,而PCA+SVC算法的精度达到1.说明卷积神经网络在掌纹识别上面的效果没有PCA+SVC算法的效果好。
下一篇: 指纹识别分类-金块