深度学习方法识别恶意的HTTP请求
一、相关工作
1.LSTM
? LSTM由Hochreiter&Schmidhuber(1997)[1]引入,并在以后的工作中被许多人提炼和推广。它们在各种与序列相关的问题上运作得非常好,现在被广泛使用。
? 总体来说:为了让梯度无损传播,LSTM中使用了c(t)=c(t-1)这个朴素的梯度传播模型,于是称c为“长时记忆单元”。为了把新信息平稳安全可靠的装入长时记忆单元,引入了“输入门”。为了解决新信息装载次数过多带来的激活函数饱和的问题,引入了“遗忘门”。为了让网络能够选择合适的记忆进行输出,引入了“输出门”。为了将神经网络的简单反馈结构升级成模糊历史记忆的结构,引入了隐单元h,并且发现h中存储的模糊历史记忆是短时的,于是记h为短时记忆单元。该网络既具备长时记忆,又具备短时记忆,因此被称为长短时记忆神经网络(Long Short Term Memory Neural Networks,简称LSTM)。
2.Embedding Layer
? 在传统机器学习方法中,将文本转换为向量可以使用one-hot向量编码,把每一系列的文本整合成一个稀疏矩阵。但one-hot没有给出任何词汇之间的内在关系概念,失去了内在相似性,不能得出词汇与模型之间的关联。而经过embedding layer之后,一个稀疏的one-hot向量被映射为稠密向量,并且这个稠密向量的每一个特征可以认为是有实际意义的,具有相似特性和意义的词语往往会具有相似的稠密vector表示。
? 使用Embedding layer时,通常使用其他网络(如word2vec) 训练好的现成的词向量, 作为初始化参数。如果输入数据不需要词的语义特征语义,简单使用Embedding layer就可以得到一个对应的词向量矩阵。本文中使用了keras中自带的embedding层。
二、实验过程
1.数据读取和预处理
? 所使用的数据来自GitHub[2]
? 原数据是json格式,其中一个示例如下:
{
"timestamp": 1502135820943,
"method": "get",
"query": {
"query": "Lawn & Garden Buying Guides"
},
"path": "/search",
"statusCode": 200,
"source": {
"remoteAddress": "22.73.58.187",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
},
"route": "/search",
"headers": {
"host": "localhost:8002",
"connection": "keep-alive",
"cache-control": "no-cache",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
"accept": "*/*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.8,es;q=0.6"
},
"requestPayload": null,
"responsePayload": "SEARCH"
}
? 首先调用json.loads()将json文本字符串转为python中的list,再删去其中对于分析其是否恶意作用不大的timestamp、headers、source、route、responsePayload字段,得到的X如下所示:
2.tokenize
? 使用文本的第一步就是将其拆分为单词。在深度学习中单词称为标记(token),将文本拆分为标记的过程称为标记化(tokenization),而标记化用到的模型或工具称为tokenizer。Keras提供了Tokenizer类,用于为深度学习文本文档的预处理。
? 首先利用fit_on_texts()学习出文本字典,文本字典记录了每个词在所有文档中出现的次数、每个词出现的文档数量等特征。学习出文本字典后,可以通过texts_to_sequences()将文本序列转换成整数的索引序列。完成分词和编码后通过padding把所有词向量补成同样长度,便于送入之后的网络模型。标记化后的X如下所示:
3.划分样本集
? 数据集划分的方法有:留出法、交叉验证法、k折交叉验证、自助法等。“留出法”是其中最简便的方法,直接将数据集划分为两个互斥的集合,一个为训练集S ,一个为测试集T。在S上进行模型学习,然后用T来评估其测试误差,作为对泛化误差的估计。这里为了简便,使用了留出法。
4.搭建模型
? 首先使用了keras中自带的embedding层进行向量化,考虑到数据集比较小,为了防止过拟合,模型参数不易过多,经过了几次试验后,发现一个LSTM隐藏层已经可以达到较好效果。此外,Dropout可以比较有效的缓解过拟合的发生,在一定程度上达到正则化的效果,因此在隐藏层的前后都做了dropout。最后使用一个全连接层输出二分类结果,使用了sigmoid作为激活函数。
? 损失函数使用二分类的交叉熵损失函数,优化器使用Adam,Adam对梯度的一阶矩估计和二阶矩估计进行综合考虑,计算出更新步长。在很多情况下Adam是工作性能比较优秀的优化器。
5.训练与验证
? 使用前面按照1:3划分出的测试集与训练集,进行训练。测试得到模型的准确率为98.01%
三、不足
- 由于条件限制,模型简单、数据集小
- 数据集的划分使用单次使用留出法,得到的结果往往不够稳定可靠。
- 测试数据集可能和训练数据集风格过于一致,显得精确度很高,在实际的应用场景中效果未知。
四、参考文献
[1] Sepp Hochreiter; Jürgen Schmidhuber(1997). “Long short-term memory”. Neural Computation. 9 (8): 1735–1780.
[2] https://github.com/adamkusey/securitai-lstm-model
[3]Mikolov T , Chen K , Corrado G , et al. Efficient Estimation of Word Representations in Vector Space[J]. Computer Science, 2013.
[4]https://www.cnblogs.com/DjangoBlog/p/9224780.html
CODE
import sys
import os
import json
import pandas
import numpy as np
import optparse
from keras.callbacks import TensorBoard
from keras.models import Sequential
from keras.layers import LSTM, Dense, Dropout
from keras.layers.embeddings import Embedding
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer
from collections import OrderedDict
import tensorflow as tf
dataframe = pandas.read_csv('./dev-access.csv', engine='python', quotechar='|', header=None)
dataset = dataframe.sample(frac=1).values
X = dataset[:,0]
Y = dataset[:,1]
for index, item in enumerate(X):
reqJson = json.loads(item, object_pairs_hook=OrderedDict)
del reqJson['timestamp']
del reqJson['headers']
del reqJson['source']
del reqJson['route']
del reqJson['responsePayload']
X[index] = json.dumps(reqJson, separators=(',', ':'))
tokenizer = Tokenizer(filters='\t\n', char_level=True)
tokenizer.fit_on_texts(X)
num_words = len(tokenizer.word_index)+1
X = tokenizer.texts_to_sequences(X)
max_log_length = 1024
train_size = int(len(dataset) * .75)
X_processed = sequence.pad_sequences(X, maxlen=max_log_length)
X_train, X_test = X_processed[0:train_size], X_processed[train_size:len(X_processed)]
Y_train, Y_test = Y[0:train_size], Y[train_size:len(Y)]
tb_callback = TensorBoard(log_dir='./logs', embeddings_freq=1)
from keras.layers import Activation, Dense,advanced_activations
model = Sequential()
model.add(Embedding(num_words, 32, input_length=max_log_length))
model.add(Dropout(0.5))
model.add(LSTM(64, recurrent_dropout=0.5))
model.add(Dropout(0.5))
model.add(Dense(1,activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
model.summary()
X_train = np.asarray(X_train).astype(np.int64)
Y_train = np.asarray(Y_train).astype(np.int64)
model.fit(X_train, Y_train, validation_split=0.25, epochs=3, batch_size=32, callbacks=[tb_callback])
score, acc = model.evaluate(X_test, Y_test, verbose=1, batch_size=64)
print("Model Accuracy: {:0.2f}%".format(acc * 100))
欢迎指正
|