一、特征提取
- 日志异常检测任务一般分为日志采集、日志解析、特征提取、异常判别4个步骤
- 特征表示/提取这部分工作的主要目的在于构造机器学习模型可以处理的特征数据,借此来学习日志的正常或者异常模式。所提取特征的质量决定了后续模型检测效果所能达到的精度。
- 特征提取的输入是日志解析步骤中生成的日志事件,输出的是事件计数矩阵。
- 日志特征提取技术有:基于自然语言处理NLP,基于规则集,基于统计模型。这里我采用的是基于自然语言处理NLP
二、具体实现步骤(参考文章)
在日志解析完成后,我们已经得到系统的结构化日志,但是此时日志键还只是字符串的形式,参数列表元素也还是字符串,无法直接作为深度学习模型的输入,所以我们还需要将其特征化为数字形式的特征向量。特征提取的过程就是将字符串中转换为可量化的数字,从而构造矩阵作为特征向量,对于日志键和参数,由于其形成和表达的意义不同,我们采用了两种不同的特征化方法。
1、日志键编码(logKeyEncoding)
日志键代表日志的种类,直接采用顺序数字编号的方式来进行编码
使用pandas.factorize()函数可以实现将字符串特征转化为数字特征
factorize函数的返回值是一个tuple(元组),元组中包含两个元素。第一个元素是一个array,其中的元素是标称型元素映射为的数字;第二个元素是Index类型,其中的元素是所有标称型元素,没有重复
import pandas as pd
def logKeyEncoding(log_dataframe,outdir):
'''
:param log_dataframe: 日志文件的pandas数据帧
:outdir: csv文件的输出目录
'''
temp=pd.factorize(log_dataframe["EventId"])
log_dataframe["EventId"]=temp[0] # 第一个元素是一个array,其中的元素是标称型元素映射为的数字
log_dataframe.to_csv(os.path.join(outdir,"keyEncode.csv"),index=False) # index=False不保存行索引
2、日志参数编码(logParaEncoding)
和日志类型不同的是,参数值不由模板生成,而是在系统运行过程中根据实际发生的情况动态产生的,所以其往往具有很大的不确定性。
- 创建一个字典,存放日志参数,键为logkey,值为列表,列表中存放的是日志参数。并对字典进行初始化
logkey_list=list(set([EventId for EventId in log_dataframe["EventId"]]))
logkey_param_dict={}
# 对字典进行初始化:遍历 log_dataframe,将需要的数据依次添加到字典中
for key in logkey_list:
logkey_param_dict[key]=[]
for id in range(len(log_dataframe)):
log_key_tmp=log_dataframe["EventId"][id]
logkey_param_dict[log_key_tmp].append(log_dataframe["ParamterList"][id])
- 进行参数预处理,去除所有的标点符号和特殊字符,因为这些字符是不作为参数异常的评判标准的,所以可能会影响字符的准确性
import regex as re
new_logkey_param_dict={}
for key,value in logkey_param_dict.items():
para1=[]
# value是对于一个日志键的所有日志的参数向量组成的列表
for param in value:
para2=[]
# param是对于每个日志的参数向量泪飙
for i in param:
i=re.sub(u"([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])", '', str(i)) # 去除特殊字符,只保留汉字、字母、数字
para2.append(i)
para1.append(i)
new_logkey_param_dict[key]=para1
- 建立一个字典,存放参数的编码结果。字典的键为日志键,值为一个字典(键为字符串,值为数字)NLP知识点:Tokenizer分词器
https://juejin.cn/post/7028579355643265038
from keras.preprocessing.text import Tokenizer
token_encode_dict={}
for key,value in new_logkey_param_dict.items():
tokenizer=Tokenizer(oov_tokens='<OOV>') # oov:预留一个编号,专门用于标记超纲的词汇。
tokens=list(chain(*value)) # 去除内嵌列表
tokenizer.fit_on_texts(tokens) # 适配文本:生成一个大词典,保存在tokenizer里面
encoded_texts=tokenizer.texts_to_sequences(tokens) # 文本序列化
token_encode_dict_tmp={}
for token,encoded_text in zip(tokens,encoded_texts):
token_encode_dict_tmp[token]=encoded_text
# 去除其中为[]的,将其转化为[0]
for k,v in token_encode_dict_tmp.items():
if token_encode_dict_tmp[k]==[]:
token_encode_dict_tmp[k]=[0]
token_encode_dict[key]=token_encode_dict_tmp
- 根据编码的字典和日志参数字典,创建一个新的字典,将参数和编码一一对应
logkey_dict_num={}
for key,param in logkey_param_dict.items():
p_tmp=[]
for p in param: # p为一个日志条目中的参数向量,p为数组
p=[token_encode_dict[key][i] for i in p]
p_tmp.append(p)
logkey_dict_num[key]=p_tmp
- 将一个日志条目中的多维列表合并为一维
用之前的chain()函数去除内嵌列表
- dataframe导出到 csv文件
使用pandas.to_csv()函数即可