系列目录
《Web安全之机器学习入门》笔记:第十五章 15.4 TensorFlow识别验证码(一)
《Web安全之机器学习入门》笔记:第十五章 15.5 TensorFlow多层感知机识别验证码(二)
《Web安全之机器学习入门》笔记:第十五章 15.6 TensorFlow DNN识别验证码(三)
《Web安全之机器学习入门》笔记:第十五章 15.7与15.8 TensorFlow识别垃圾邮件
目录
本章节根据《Web安全之机器学习入门》第15章神经网络来完成笔记,本小节通过DNN识别验证码(MNIST数据集)。
一、DNN
深度神经网络(DNN)是深度学习领域的重要模型。它由多个隐藏层组成,相比浅层神经网络,能学习到数据更复杂、抽象的特征表示。在结构上,包含输入层接收原始数据,多个隐藏层进行特征提取与变换,输出层给出最终结果。训练时,借助反向传播算法,根据预测值与真实值的误差调整各层权重,优化模型。在图像识别中,可提取图像的纹理、形状等深层特征实现精准分类;在语音识别里,能对语音信号进行特征建模以准确转写文字;在自然语言处理方面,可处理文本语义理解等任务。
二、数据集
MNIST数据集是机器学习领域的经典基准数据集,主要用于手写数字识别任务。该数据集由美国国家标准与技术研究院(NIST)整理,包含70,000张28×28像素的灰度图像,涵盖0-9共10个类别,其中60,000张为训练样本,10,000张为测试样本
三、代码
1、DNN原理图
2、定义DNN
模型是一个含三层隐藏层的多层感知机(MLP),专为 MNIST 手写数字分类设计,整体结构呈 “输入层→隐藏层 1→隐藏层 2→隐藏层 3→输出层” 的链式传递,各层参数与运算逻辑如下所示。
(1)输入层
- 输入维度:784 维(对应 MNIST 图像 28×28 像素展平后的特征向量)
- 占位符定义:
x = tf.placeholder("float", [None, 784])
,None
表示支持任意批量的样本输入,784 固定为特征维度。
(2)隐藏层(共 3 层,均为全连接层)
每层均由 “线性变换 + ReLU 激活” 组成,通过全连接方式传递特征,逐步提取高层抽象特征:
-
隐藏层 1
- 神经元数量:300 个
- 线性变换:
layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
- 权重
weights['h1']
:维度[784, 300]
,连接输入层与隐藏层 1 - 偏置
biases['b1']
:维度[300]
,为每个神经元添加偏置
- 权重
- 激活函数:ReLU(
tf.nn.relu(layer_1)
),引入非线性,增强特征表达能力
-
隐藏层 2
- 神经元数量:200 个
- 线性变换:
layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
- 权重
weights['h2']
:维度[300, 200]
,连接隐藏层 1 与隐藏层 2 - 偏置
biases['b2']
:维度[200]
- 权重
- 激活函数:ReLU,进一步提取非线性特征
-
隐藏层 3
- 神经元数量:100 个
- 线性变换:
layer_3 = tf.add(tf.matmul(layer_2, weights['h3']), biases['b3'])
- 权重
weights['h3']
:维度[200, 100]
,连接隐藏层 2 与隐藏层 3 - 偏置
biases['b3']
:维度[100]
- 权重
- 激活函数:ReLU,继续压缩特征维度,提炼核心模式
(3)输出层
- 输出维度:10 维(对应 0-9 共 10 个数字类别)
- 线性变换:
out_layer = tf.matmul(layer_3, weights['out']) + biases['out']
- 权重
weights['out']
:维度[100, 10]
,连接隐藏层 3 与输出层 - 偏置
biases['out']
:维度[10]
,为每个类别添加偏置
- 权重
- 无激活函数:输出为原始 logits(未归一化的得分),需后续结合
softmax
计算概率(代码中通过tf.nn.softmax_cross_entropy_with_logits
隐式处理)。
(4)参数初始化
- 权重(weights):所有权重均用
tf.random_normal
初始化(服从正态分布的随机值),维度随层间连接关系动态调整(如[784, 300]
、[300, 200]
等)。 - 偏置(biases):所有偏置同样用
tf.random_normal
初始化,维度与对应层神经元数量一致(如隐藏层 1 偏置为[300]
)。
n_hidden_1 = 300
n_hidden_2 = 200
n_hidden_3 = 100
n_input = 784
n_classes = 10
x = tf.placeholder("float",[None,784])
y = tf.placeholder("float",[None,n_classes])
def multilayer_perceptron(x,weights,biases):
layer_1 = tf.add(tf.matmul(x,weights['h1']),biases['b1'])
layer_1 = tf.nn.relu(layer_1)
layer_2 = tf.add(tf.matmul(layer_1,weights['h2']),biases['b2'])
layer_2 = tf.nn.relu(layer_2)
layer_3 = tf.add(tf.matmul(layer_2,weights['h3']),biases['b3'])
layer_3 = tf.nn.relu(layer_3)
out_layer = tf.matmul(layer_3,weights['out']) + biases['out']
#out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
return out_layer
weigths = {
'h1': tf.Variable(tf.random_normal([n_input,n_hidden_1])),
'h2': tf.Variable(tf.random_normal([n_hidden_1,n_hidden_2])),
'h3': tf.Variable(tf.random_normal([n_hidden_2,n_hidden_3])),
'out': tf.Variable(tf.random_normal([n_hidden_3,n_classes]))
#'out': tf.Variable(tf.random_normal([n_hidden_2,n_classes]))
}
biases = {
'b1': tf.Variable(tf.random_normal([n_hidden_1])),
'b2': tf.Variable(tf.random_normal([n_hidden_2])),
'b3': tf.Variable(tf.random_normal([n_hidden_3])),
'out': tf.Variable(tf.random_normal([n_classes]))
}
pred = multilayer_perceptron(x,weigths,biases)
衰减函数则是使用交叉熵,使用Adagrad自使用调节
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred,labels=y))
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
3、完整代码
本文使用 TensorFlow 构建三层隐藏层的多层感知机(MLP)识别 MNIST 数字。加载数据并设超参数,网络含 3 个隐藏层(300、200、100 神经元,均用 ReLU 激活)和输出层,随机初始化权重偏置;以交叉熵为损失,梯度下降(学习率 0.001)训练 10 轮(每批 100 样本),输出每轮平均损失,最终测试准确率,实现较浅层模型更强的特征提取能力。主要处理逻辑如下所示。
-
数据准备:使用TensorFlow内置的MNIST数据加载器,自动下载和管理数据集。
-
网络结构:构建了一个4层神经网络(3个隐藏层+1个输出层),每层使用ReLU激活函数。
-
训练过程:采用小批量梯度下降法,每轮遍历所有批次数据,计算并优化损失。
-
评估指标:使用分类准确率作为模型性能评估标准。
原书配套源码修改为可运行后,基于python3的完整源码如下所示:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow.compat.v1 as tf # 使用TensorFlow 1.x的API
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 屏蔽TensorFlow的非错误警告信息
# 加载MNIST数据集,one_hot表示标签是否采用独热编码格式
mnist = input_data.read_data_sets("../data/mnist", one_hot=True)
# 定义超参数
learning_rate = 0.001 # 学习率
training_epochs = 10 # 训练轮数
batch_size = 100 # 每批数据量
display_step = 1 # 每隔多少轮显示一次训练信息
# 网络结构参数
n_hidden_1 = 300 # 第一隐藏层神经元数量
n_hidden_2 = 200 # 第二隐藏层神经元数量
n_hidden_3 = 100 # 第三隐藏层神经元数量
n_input = 784 # MNIST数据输入维度(28x28=784)
n_classes = 10 # MNIST分类类别数(0-9共10类)
# 定义占位符
x = tf.placeholder("float", [None, 784]) # 输入数据占位符
y = tf.placeholder("float", [None, n_classes]) # 标签占位符
# 定义多层感知机模型
def multilayer_perceptron(x, weights, biases):
# 第一隐藏层:矩阵乘法 + 偏置 + ReLU激活
layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
layer_1 = tf.nn.relu(layer_1)
# 第二隐藏层
layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
layer_2 = tf.nn.relu(layer_2)
# 第三隐藏层
layer_3 = tf.add(tf.matmul(layer_2, weights['h3']), biases['b3'])
layer_3 = tf.nn.relu(layer_3)
# 输出层(无激活函数)
out_layer = tf.matmul(layer_3, weights['out']) + biases['out']
return out_layer
# 定义权重和偏置变量
weights = {
'h1': tf.Variable(tf.random_normal([n_input, n_hidden_1])), # 输入层到第一隐藏层
'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])), # 第一到第二隐藏层
'h3': tf.Variable(tf.random_normal([n_hidden_2, n_hidden_3])), # 第二到第三隐藏层
'out': tf.Variable(tf.random_normal([n_hidden_3, n_classes])) # 第三隐藏层到输出层
}
biases = {
'b1': tf.Variable(tf.random_normal([n_hidden_1])), # 第一隐藏层偏置
'b2': tf.Variable(tf.random_normal([n_hidden_2])), # 第二隐藏层偏置
'b3': tf.Variable(tf.random_normal([n_hidden_3])), # 第三隐藏层偏置
'out': tf.Variable(tf.random_normal([n_classes])) # 输出层偏置
}
# 构建模型
pred = multilayer_perceptron(x, weights, biases)
# 定义损失函数和优化器
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) # 交叉熵损失
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) # 梯度下降优化器
# 初始化所有变量
init = tf.global_variables_initializer()
# 开始训练
with tf.Session() as sess:
sess.run(init) # 初始化变量
# 按轮次训练
for epoch in range(training_epochs):
avg_cost = 0. # 平均损失
total_batch = int(mnist.train.num_examples / batch_size) # 计算总批次数
# 遍历所有批次
for i in range(total_batch):
batch_x, batch_y = mnist.train.next_batch(batch_size) # 获取下一批数据
# 运行优化器和损失计算
_, c = sess.run([train_step, cost], feed_dict={x: batch_x, y: batch_y})
avg_cost += c / total_batch # 计算平均损失
# 显示训练信息
if epoch % display_step == 0:
print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(avg_cost))
# 测试模型准确率
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) # 比较预测和真实标签
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) # 计算准确率
print("Accuracy:", accuracy.eval({x: mnist.test.images, y: mnist.test.labels})) # 评估测试集
4、运行结果
运行结果如下所示,很明显0.2左右的准确率显示模型训练效果极差,存在明显问题,。
Epoch: 0001 cost= 160.255845963
Epoch: 0002 cost= 3.702655527
Epoch: 0003 cost= 2.998886849
Epoch: 0004 cost= 2.768014714
Epoch: 0005 cost= 2.632289400
Epoch: 0006 cost= 2.541885174
Epoch: 0007 cost= 2.471330396
Epoch: 0008 cost= 2.419655328
Epoch: 0009 cost= 2.382263158
Epoch: 0010 cost= 2.350417513
Accuracy: 0.1641
5、程序优化
(1)参照书中内容,将学习率由代码中的0.0001改为0.3,效果无明显增长
再将epoch轮数有10改为100,运行结果如下
test_accuracy= 0.8123
(2)更改train_step参数,同时修改学习率
train_step = tf.train.AdamOptimizer(1e-4).minimize(cost)
设置10轮效果如下所示。
test_accuracy= 0.8571
设置100轮效果如下所示。
test_accuracy= 0.9289
这里之所以要简单改一下,是因为运行作者配套源码时出现了准确率过低的问题,实际上就是神经网络中学习率以及损失函数等优化问题,大家可以自己调一调,作者这里选择了最简单的入门级的MNIST图片数据集,命名为验证码,也只是举个应用实例。