系列目录
《Web安全之机器学习入门》笔记:第十五章 16.2 识别验证码
《Web安全之机器学习入门》笔记:第十六章 16.3 恶意评论识别(一)
《Web安全之机器学习入门》笔记:第十六章 16.3 恶意评论识别(二)
《Web安全之机器学习入门》笔记:第十六章 16.4 生成城市名称
《Web安全之机器学习入门》笔记:第十六章 16.5 识别WebShell
《Web安全之机器学习入门》笔记:第十六章 16.6 生成常用密码
《Web安全之机器学习入门》笔记:第十六章 16.7 识别异常操作
目录
人们在设置密码时候,总是倾向于好记的密码,于是常见的密码有一定的关联性。RNN(循环神经网络)是一类用于处理序列数据的神经网络。它通过隐藏层的循环结构,能捕捉序列中的时间依赖信息。可用于自然语言处理、语音识别等领域。不过,传统 RNN 存在梯度消失或爆炸问题,后续衍生出 LSTM、GRU 等改进模型。我们可以尝试让RNN学习常见的密码,摸索其中的规律,然后自动生成密码。本小节示例RNN生成常用密码。
1、数据集
使用WVS自带的密码字典作为训练集。约定密码长度不超过10,逐行读取密码文件中的每行密码,并将其序列化。具体代码如下所示。
path = "../data/wvs-pass.txt"
maxlen = 10
file_lines = open(path, "r", encoding='utf-8').read()
X, Y, char_idx = \
string_to_semi_redundant_sequences(file_lines, seq_maxlen=maxlen, redun_step=3)
2、构建RNN模型
接下来构建一个基于 LSTM 的循环神经网络模型,用于处理序列数据的分类问题。代码用 tflearn 构建 LSTM 模型。输入层接收形状为 [任意批次,maxlen, 字符特征维度] 的数据;含两个 LSTM 层(各 512 隐藏单元,首层返回全序列),每层后接 Dropout 层(丢弃率 0.5)防止过拟合;全连接层用 softmax 激活输出分类概率;回归层配置 Adam 优化器、交叉熵损失函数(学习率 0.001).
- 输入层定义:指定输入数据形状,支持任意批次、固定长度和特征维度
- 首 LSTM 层:512 隐藏单元,返回全序列输出供后续处理
- 首 Dropout 层:以 0.5 概率丢弃神经元,减少过拟合风险
- 次 LSTM 层:512 隐藏单元,仅返回最后时间步输出
- 次 Dropout 层:继续以 0.5 概率丢弃,增强泛化能力
- 全连接层:输出维度为字符类别数,softmax 激活获概率分布
- 回归层配置:Adam 优化器,交叉熵损失,学习率 0.001
# 定义输入层
# shape 参数指定输入数据的形状,[None, maxlen, len(char_idx)] 表示输入数据的批次大小可以是任意的(None),
# 每个样本的长度为 maxlen,每个时间步的特征维度为 len(char_idx)
g = tflearn.input_data(shape=[None, maxlen, len(char_idx)])
# 第一个 LSTM 层
# 这里使用了 512 个隐藏单元,return_seq=True 表示返回整个序列的输出,而不仅仅是最后一个时间步的输出
g = tflearn.lstm(g, 512, return_seq=True)
# Dropout 层,用于防止过拟合
# 以 0.5 的概率随机丢弃一些神经元的输出,这样可以减少神经元之间的依赖关系,增强模型的泛化能力
g = tflearn.dropout(g, 0.5)
# 第二个 LSTM 层
# 同样使用 512 个隐藏单元,这里没有设置 return_seq=True,意味着只返回最后一个时间步的输出
g = tflearn.lstm(g, 512)
# 第二个 Dropout 层,继续防止过拟合
g = tflearn.dropout(g, 0.5)
# 全连接层
# 将上一层的输出连接到一个具有 len(char_idx) 个神经元的全连接层,使用 softmax 激活函数,
# 通常用于多分类问题,输出每个类别的概率分布
g = tflearn.fully_connected(g, len(char_idx), activation='softmax')
# 回归层,定义优化器、损失函数和学习率
# 使用 Adam 优化器,它结合了 AdaGrad 和 RMSProp 的优点,能够自适应地调整每个参数的学习率
# 损失函数使用分类交叉熵损失,适用于多分类问题
# 学习率设置为 0.001,控制模型参数更新的步长
g = tflearn.regression(g, optimizer='adam', loss='categorical_crossentropy',
learning_rate=0.001)
对应的模型如下图所示。
3、实例化序列器
代码用 tflearn 的 SequenceGenerator 类创建序列生成器 m,基于模型 g 生成序列。指定字符与索引映射字典(char_idx)以转换索引为字符,设最大序列长度为 maxlen,梯度裁剪阈值 5.0 防止梯度爆炸,将检查点文件保存至 'wvs_passwd' 路径,便于后续恢复训练或预测,适用于序列生成任务。
# 创建一个序列生成器对象 m
# 该对象基于之前定义的模型 g 来生成序列
# 这里使用了 tflearn 库中的 SequenceGenerator 类
m = tflearn.SequenceGenerator(g,
# 指定字典 dictionary,它是一个字符到索引的映射(char_idx)
# 用于将生成的索引转换为对应的字符
dictionary=char_idx,
# 设定生成序列的最大长度为 maxlen
# 这意味着每次生成的序列长度不会超过这个值
seq_maxlen=maxlen,
# 梯度裁剪的阈值设置为 5.0
# 在训练过程中,当梯度的范数超过这个阈值时,会对梯度进行裁剪
# 这样可以防止梯度爆炸的问题,使训练过程更加稳定
clip_gradients=5.0,
# 指定检查点文件的保存路径为 'wvs_passwd'
# 检查点文件会保存模型在训练过程中的参数
# 可以在后续恢复训练或者进行预测时使用这些参数
checkpoint_path='wvs_passwd')
4、完整代码
本小节通过数据处理、创建模型、训练模型以及测试密码生成四个步骤来实现生成密码的功能。完整的流程与代码如下所示。
-
数据准备:读取密码文本文件,转换为字符级序列数据(X为输入序列,Y为下一个字符的one-hot标签)
-
模型架构:双层LSTM网络,每层512个单元,使用Dropout防止过拟合
-
训练过程:采用Adam优化器,40次迭代,每次迭代训练1个epoch
-
密码生成:通过调整temperature参数控制生成结果的随机性:
-
高temperature(1.2):生成更随机/创造性的密码
-
标准temperature(1.0):平衡随机性和准确性
-
低temperature(0.5):生成更保守/可预测的密码
-
import os
from six import moves # Python 2/3兼容工具
import ssl # 安全套接字层
import tflearn # 基于TensorFlow的高级深度学习库
from tflearn.data_utils import * # 数据预处理工具
# 数据文件路径和参数设置
path = "../data/wvs-pass.txt" # 密码数据集路径
maxlen = 10 # 序列最大长度(字符级)
# 读取原始数据文件
file_lines = open(path, "r", encoding='utf-8').read()
# 将文本转换为序列数据(输入X和标签Y)
# char_idx: 字符到索引的映射字典
X, Y, char_idx = string_to_semi_redundant_sequences(
file_lines,
seq_maxlen=maxlen, # 序列长度
redun_step=3 # 滑动窗口步长
)
# 构建LSTM网络架构
g = tflearn.input_data(shape=[None, maxlen, len(char_idx)]) # 输入层(None表示可变batch_size)
g = tflearn.lstm(g, 512, return_seq=True) # 第一层LSTM(512单元,返回序列)
g = tflearn.dropout(g, 0.5) # 50%的dropout防止过拟合
g = tflearn.lstm(g, 512) # 第二层LSTM
g = tflearn.dropout(g, 0.5) # 第二层dropout
g = tflearn.fully_connected(g, len(char_idx), activation='softmax') # 全连接输出层
g = tflearn.regression( # 定义回归任务
g,
optimizer='adam', # 使用Adam优化器
loss='categorical_crossentropy', # 分类交叉熵损失
learning_rate=0.001 # 学习率
)
# 创建序列生成模型
m = tflearn.SequenceGenerator(
g,
dictionary=char_idx, # 字符索引字典
seq_maxlen=maxlen, # 序列长度
clip_gradients=5.0, # 梯度裁剪阈值
checkpoint_path='wvs_passwd' # 模型保存路径
)
# 训练和生成过程
for i in range(40): # 40次迭代
# 从原始文本随机选择种子序列
seed = random_sequence_from_string(file_lines, maxlen)
# 训练模型(1个epoch)
m.fit(
X, Y,
validation_set=0.1, # 10%验证集
batch_size=128, # 批量大小
n_epoch=1, # 每次迭代训练1个epoch
run_id='password' # 实验标识
)
# 测试不同温度参数下的生成效果
print("-- TESTING...")
print("-- Test with temperature of 1.2 --") # 高温度(更多随机性)
print(m.generate(30, temperature=1.2, seq_seed=seed))
print("-- Test with temperature of 1.0 --") # 标准温度
print(m.generate(30, temperature=1.0, seq_seed=seed))
print("-- Test with temperature of 0.5 --") # 低温度(更保守)
print(m.generate(30, temperature=0.5, seq_seed=seed))
5、运行结果
Temperature定义为新颖程度,Temperature越小,自动生成的城市名称越接近样本中的城市名称,Temperature越大,自动生成的城市名称与样本中的城市名称差别越大。具体运行结果如下所示。
......
Training Step: 13528 | total loss: 0.58454 | time: 316.845s
| Adam | epoch: 033 | loss: 0.58454 -- iter: 52224/52452
Training Step: 13529 | total loss: 0.58885 | time: 317.170s
| Adam | epoch: 033 | loss: 0.58885 -- iter: 52352/52452
Training Step: 13530 | total loss: 0.58233 | time: 334.266s
| Adam | epoch: 033 | loss: 0.58233 | val_loss: 0.36391 -- iter: 52452/52452
--
-- TESTING...
-- Test with temperature of 1.2 --
om
doidc.com
xiao
haining
jhidc
6588888
-- Test with temperature of 1.0 --
om
doidc.com
tama_.com
tomiin@367894321$
-- Test with temperature of 0.5 --
om
doidc.com
xiidc
idc02
idc0000
0000000