简介本资源是一套基于LSTM神经网络的日志异常检测系统完整实现面向人工智能、软件工程及自动化等专业的在校学生、教师与初级算法工程师适用于毕业设计、课程实践或工业日志分析场景的快速入门与二次开发。压缩包共74个文件含14个核心Python脚本如main.py、train.log、test.log、syslogparser.py等、20个预处理后的npy特征数据、13个CSV原始/标注日志样本、12个pkl模型与中间结果以及README.md、demo.png、requirements.txt等配套文档与可视化素材整体23.89MB结构清晰、模块解耦明确。已有585人学习下载项目源自高分毕设答辩均分94.5所有代码经实测可直接运行涵盖日志解析、序列建模、异常判别与结果可视化全流程。读者可直接复现端到端检测流程深入理解LSTM在时序日志建模中的应用逻辑并基于现有框架扩展滑动窗口策略、多源日志融合或阈值自适应机制。1. 这不是“调个LSTM跑个日志”一个能真正在服务器上守夜的异常检测系统到底长什么样你见过太多“LSTM日志异常检测”的Demo读几行Apache日志、喂进一个Keras模型、画条loss曲线、输出几个“0/1”标签——然后戛然而止。但真实运维场景里凌晨三点告警邮件炸屏你打开后台发现模型把sshd[1234]: Accepted password for root from 192.168.1.100 port 22标成异常却对sshd[5678]: Failed password for invalid user admin from 203.124.98.222 port 42311视而不见。这不是模型不准是整个pipeline断在了日志语义建模的第一公里。这个资源包Python基于LSTM的日志异常检测系统源码数据集.zip不是教学玩具它是一套完整落地的日志序列建模闭环从原始train.log/test.log的正则清洗、syslogparser.py的结构化解析、spell.py的事件模板抽象、pca.py的高维特征降维到main.py中双层LSTMAttention的时序建模最后用analyse/下的阈值自适应模块输出可解释告警。它被用于某高校数据中心真实日志流压测答辩评审94.5分所有脚本均通过python3.8torch1.12环境实测支持直接替换logdata/目录接入你的Nginx/SSH/MySQL日志。适合想把毕设做成“能上线”的学生、需要快速验证AIops方案的运维工程师以及想避开“日志文本→向量”黑匣子陷阱的算法同学——它不教你LSTM公式但会告诉你为什么train.log里必须保留pid字段、为什么data_instances.csv的滑动窗口步长设为3、为什么demo.png里的ROC曲线AUC0.923却仍要加PCA后处理。2. 日志预处理为什么不用正则就别碰LSTM从syslogparser.py到spell.py的四层清洗链日志异常检测的成败80%取决于预处理是否“懂日志”。这个项目没用任何现成日志库如Logstash而是用纯Python构建了四层清洗链原始解析 → 模板抽象 → 序列编码 → 特征压缩。每层都针对LSTM输入要求做了硬约束下面拆解关键实现。2.1syslogparser.py用有限状态机替代正则的健壮解析器很多同学直接用re.findall(r(\w\s\d\s\d:\d:\d)\s(\w)\s(\w)\[(\d)\]:\s(.*), line)匹配syslog但遇到kernel: [12345.67890] CPU0: online或CRON[1234]: (root) CMD (/usr/bin/python3 /opt/clean.py)就崩。本项目采用状态机驱动解析# syslogparser.py 核心片段 import re class SyslogParser: def __init__(self): # 预编译所有可能模式避免重复compile开销 self.patterns [ re.compile(r^(\w\s\d\s\d:\d:\d)\s(\w)\s(\w)\[(\d)\]:\s(.*)$), # 标准格式 re.compile(r^(\w\s\d\s\d:\d:\d)\s(\w)\skernel:\s\[(\d\.\d)\]\s(.*)$), # kernel日志 re.compile(r^(\w\s\d\s\d:\d:\d)\s(\w)\s(\w)\[(\d)\]:\s\((\w)\)\sCMD\s\((.*)\)$), # CRON ] def parse_line(self, line: str) - dict: for pattern in self.patterns: match pattern.match(line.strip()) if match: groups match.groups() # 统一返回结构化字典缺失字段补None return { timestamp: groups[0], hostname: groups[1], service: groups[2] if len(groups) 2 else None, pid: groups[3] if len(groups) 3 else None, message: groups[-1] if groups else line.strip() } return {raw: line.strip()} # 解析失败保留原始行提示parse_line()返回字典而非字符串为后续spell.py的模板抽象提供结构化基础。注意pid字段被显式保留——这是LSTM捕捉进程行为链的关键ID丢掉它会导致sshd[1234]和sshd[5678]被当作同一事件。2.2spell.py基于最长公共子序列LCS的动态模板生成传统方法用LogPai的Drain算法需预设深度树而本项目用轻量级LCS匹配实现无监督模板抽象。核心逻辑对同一服务如sshd的所有消息逐字符比对提取稳定token序列。# spell.py 关键函数 def lcs_template(messages: list) - str: 对messages列表生成LCS模板用*替代变动部分 if not messages: return # 取第一条作为基准 base messages[0].split() template base.copy() for msg in messages[1:]: tokens msg.split() # 计算LCS索引 lcs_indices [] i j 0 while i len(base) and j len(tokens): if base[i] tokens[j]: lcs_indices.append(i) i 1 j 1 elif i j: i 1 else: j 1 # 将非LCS位置置为* for idx in range(len(template)): if idx not in lcs_indices: template[idx] * return .join(template) # 示例输入[Accepted password for root, Accepted password for admin] → 输出 Accepted password for *参数说明lcs_template()不依赖词典或停用词表纯靠token序列匹配。*占位符数量直接反映该模板的泛化能力——sshd模板中*越少说明该事件越稳定若某模板*占比超60%则被自动过滤见index.py中MIN_TEMPLATE_STABILITY0.4阈值。这避免了LSTM学习噪声模板。2.3index.py事件ID映射与滑动窗口构造LSTM需要固定长度序列而日志是无限流。本项目用data_instances.csv存储预生成的训练样本其构造逻辑藏在index.py# index.py 片段生成滑动窗口序列 import pandas as pd from collections import defaultdict def build_sequences(log_df: pd.DataFrame, window_size: int 3, step: int 1) - list: 按service分组构建滑动窗口事件序列 sequences [] for service, group in log_df.groupby(service): # 按timestamp排序确保时序 sorted_group group.sort_values(timestamp) events sorted_group[event_id].tolist() # event_id来自spell.py映射 # 滑动窗口[e1,e2,e3], [e2,e3,e4], ... for i in range(0, len(events) - window_size 1, step): window events[i:iwindow_size] # 标签窗口内最后一个事件是否异常基于人工标注或规则 label 1 if sorted_group.iloc[iwindow_size-1][is_anomaly] else 0 sequences.append({service: service, sequence: window, label: label}) return sequences # 生成data_instances.csv的调用入口 if __name__ __main__: parsed_logs parse_all_logs(logdata/) # 调用syslogparser.py event_mapped map_to_templates(parsed_logs) # 调用spell.py seq_data build_sequences(event_mapped, window_size3, step1) pd.DataFrame(seq_data).to_csv(data_instances.csv, indexFalse)关键设计window_size3不是随意选的——实验表明少于3步无法捕获login→cmd→logout行为链大于5步则LSTM梯度消失加剧。step1保证样本密度但会增加冗余实际部署时可调为step2提速。2.4pca.py为什么LSTM前要加PCA解决日志向量维度灾难data_instances.csv中每个事件ID对应一个one-hot向量若事件模板数达2000输入维度爆炸。pca.py用PCA将1000维压缩至64维# pca.py from sklearn.decomposition import PCA import numpy as np def apply_pca(event_vectors: np.ndarray, n_components: int 64) - np.ndarray: 对事件向量矩阵做PCA降维 # 标准化日志向量稀疏需先归一化 normalized event_vectors / (np.linalg.norm(event_vectors, axis1, keepdimsTrue) 1e-8) pca PCA(n_componentsn_components) reduced pca.fit_transform(normalized) # 保存PCA模型供推理复用 import joblib joblib.dump(pca, pca_model.pkl) return reduced # 在main.py中加载使用 pca_model joblib.load(pca_model.pkl) X_reduced pca_model.transform(X_onehot)血泪经验跳过PCA直接喂LSTMtrain.log训练时loss下降极慢且震荡。PCA不仅降维更通过方差筛选pca.explained_variance_ratio_.sum()≈0.92保留了事件共性模式让LSTM专注学习时序依赖而非记忆稀疏ID。3. LSTM建模双层BiLSTMAttention为何比单层LSTM更适合日志序列LSTM层是整个系统的“心脏”但本项目没用Keras的Sequential堆叠而是用PyTorch手写双层双向LSTMAttention原因很实在日志异常往往由长距离依赖触发如login后10分钟出现rm -rf /单向LSTM难以捕捉而Attention能定位关键事件。3.1deeplog/lib/model.py可解释的Attention权重计算# deeplog/lib/model.py import torch import torch.nn as nn class DeepLogModel(nn.Module): def __init__(self, input_dim, hidden_dim, num_layers, num_classes, dropout0.3): super().__init__() self.hidden_dim hidden_dim self.num_layers num_layers # 双层BiLSTMforwardbackward双向捕获 self.lstm nn.LSTM( input_sizeinput_dim, hidden_sizehidden_dim, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0, bidirectionalTrue ) # Attention层计算每个时间步的重要性 self.attention nn.Linear(hidden_dim * 2, 1) # *2因bidirectional # 分类头 self.classifier nn.Sequential( nn.Dropout(dropout), nn.Linear(hidden_dim * 2, 64), nn.ReLU(), nn.Dropout(dropout), nn.Linear(64, num_classes) ) def forward(self, x): # x: [batch, seq_len, input_dim] lstm_out, _ self.lstm(x) # [batch, seq_len, hidden_dim*2] # Attention权重 attn_weights torch.softmax(self.attention(lstm_out), dim1) # [batch, seq_len, 1] context torch.sum(attn_weights * lstm_out, dim1) # [batch, hidden_dim*2] # 分类 output self.classifier(context) return output, attn_weights.squeeze(-1) # 返回logits和attention权重 # 在train.py中可视化Attention def visualize_attention(model, sample_seq): _, attn_weights model(sample_seq.unsqueeze(0)) # [1, seq_len] # 绘制attn_weights可定位异常触发点 plt.plot(attn_weights[0].detach().numpy()) plt.title(Attention Weights for Sequence) plt.show()参数深挖hidden_dim128是平衡效果与速度的临界点——小于64时无法捕获复杂模式大于256则显存溢出测试环境RTX3060 12G。num_layers2足够更多层反而过拟合因日志序列本身较短window_size3。3.2main.py训练循环中的三个关键控制点main.py不是简单model.train()它嵌入了日志场景特有的控制逻辑# main.py 片段 def train_epoch(model, dataloader, optimizer, criterion, device): model.train() total_loss 0 for batch_idx, (data, target) in enumerate(dataloader): data, target data.to(device), target.to(device) # 1. 梯度裁剪防止日志序列梯度爆炸 optimizer.zero_grad() output, _ model(data) loss criterion(output, target) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) # 关键 # 2. 学习率warmup前10轮线性提升避免初期震荡 if epoch 10: lr 0.001 * (epoch 1) / 10 for param_group in optimizer.param_groups: param_group[lr] lr # 3. 异常样本加权日志中异常1%用Focal Loss思想 # 这里简化为class weight实际可替换为focal_loss class_weights torch.tensor([1.0, 10.0]).to(device) # 异常类权重10倍 weighted_criterion nn.CrossEntropyLoss(weightclass_weights) loss weighted_criterion(output, target) optimizer.step() total_loss loss.item() return total_loss / len(dataloader)为什么必须梯度裁剪日志序列中存在极端事件如Segmentation fault连续爆发导致LSTM隐藏状态爆炸clip_grad_norm_是保命操作。未加此行训练第3轮即nan。3.3analyse/阈值自适应模块——让模型告别“一刀切”LSTM输出是概率但运维需要明确告警。analyse/threshold_adapt.py实现动态阈值# analyse/threshold_adapt.py import numpy as np from sklearn.metrics import precision_recall_curve def find_optimal_threshold(y_true, y_score, methodf1): 根据验证集寻找最优阈值 if method f1: precision, recall, thresholds precision_recall_curve(y_true, y_score) f1_scores 2 * (precision * recall) / (precision recall 1e-8) optimal_idx np.argmax(f1_scores) return thresholds[optimal_idx] elif method youden: # Youden指数灵敏度特异度-1 from sklearn.metrics import confusion_matrix thresholds np.arange(0.1, 0.9, 0.01) best_j -1 best_th 0.5 for th in thresholds: y_pred (y_score th).astype(int) tn, fp, fn, tp confusion_matrix(y_true, y_pred).ravel() j tp/(tpfn) tn/(tnfp) - 1 if j best_j: best_j j best_th th return best_th # 在eval.py中调用 val_scores model.predict(val_loader) # 获取概率分数 optimal_th find_optimal_threshold(val_labels, val_scores, methodf1) print(fOptimal threshold: {optimal_th:.3f})实战价值固定阈值0.5在日志场景下必然漏报。本模块让test.log评估时F1-score从0.62提升至0.87且阈值随数据漂移自动更新——这才是生产级系统该有的样子。3.4 避坑LSTM日志建模的四个血泪教训现象 → 原因 → 解决现象训练loss下降缓慢50轮后仍0.8验证acc卡在52%。原因data_instances.csv中sequence列存的是原始事件ID字符串如sshd_login_001未映射为整数索引。LSTM输入必须是int64张量字符串导致隐式转换失败。解决检查index.py中map_to_templates()函数确保event_id字段为int类型并在Dataset.__getitem__()中添加torch.tensor(seq, dtypetorch.long)强制转换。现象test.log预测全为0正常无任何异常检出。原因pca_model.pkl在训练时生成但推理时未加载main.py中误用原始one-hot向量直接输入LSTM维度不匹配导致全零输出。解决在predict.py开头添加pca_model joblib.load(pca_model.pkl)且确认X_test经pca_model.transform()处理。现象Attention可视化显示权重均匀分布所有时间步≈0.33无法定位关键事件。原因attention层输入未归一化lstm_out数值范围过大±100softmax后权重趋近相等。解决在model.py的forward中attn_weights torch.softmax(self.attention(lstm_out) / 10.0, dim1)除以温度系数10缩放logits。现象更换train.log为Nginx日志后syslogparser.py解析失败率90%。原因syslogparser.py硬编码匹配syslog格式Nginx日志格式为[error] 1234#1234: *1234 connect() failed...不在预设patterns中。解决扩展syslogparser.py的patterns列表添加Nginx专用正则re.compile(r\[(\w)\]\s(\d)#(\d):\s\*(\d)\s(.*))并修改parse_line()返回结构兼容新字段。4. 数据集实战logdata/目录如何替换成你的生产日志三步走通路logdata/是本项目的“燃料仓”但它的结构设计直指生产落地——不是扔给你一堆.log文件完事而是定义了可插拔的数据契约。替换你的日志只需三步且每步都有验证手段。4.1 目录结构契约为什么logdata/必须含train/、test/、val/子目录项目默认logdata/下有logdata/ ├── train/ │ ├── sshd.log │ ├── nginx_error.log │ └── mysql_slow.log ├── test/ │ └── mixed.log # 混合多服务日志 └── val/ └── annotated.log # 人工标注异常的验证集为什么必须分三目录train/供index.py生成data_instances.csv要求日志量10万行覆盖正常行为全谱系test/模拟线上流量用于最终评估禁止参与训练val/含人工标注is_anomaly: 0/1字段用于threshold_adapt.py找最优阈值。若你的日志只有app.log一个文件必须手动拆分为三份否则模型泛化性归零。4.2 格式适配器syslogparser.py的扩展开发指南假设你要接入Kubernetes Pod日志格式2023-10-01T08:30:45.123Z stdout F {level:error,msg:timeout}需修改syslogparser.py# 在SyslogParser.__init__()中添加 self.patterns.append( re.compile(r^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\dZ)\s(\w)\s(\w)\s(.*)$) ) # 在parse_line()中补充解析逻辑 if stdout in line or stderr in line: # 提取JSON部分 json_start line.find({) if json_start ! -1: try: import json log_json json.loads(line[json_start:]) return { timestamp: groups[0], service: k8s_pod, pid: log_json.get(pod_name, unknown), message: log_json.get(msg, ) } except: pass验证方法运行python syslogparser.py --test k8s_sample.log检查输出是否含service: k8s_pod及正确message。错误日志会进入{raw: ...}分支需单独处理。4.3data_instances.csv重生成当你的日志模板数超过2000怎么办spell.py生成的模板数过多如微服务日志达5000会导致pca.py降维失效。解决方案是模板聚类# 在spell.py末尾添加模板聚类 from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans def cluster_templates(templates: list, n_clusters: int 500): 对模板字符串做TF-IDF聚类压缩模板数 vectorizer TfidfVectorizer(max_features10000, stop_wordsenglish) tfidf_matrix vectorizer.fit_transform(templates) kmeans KMeans(n_clustersn_clusters, random_state42) labels kmeans.fit_predict(tfidf_matrix) # 为每簇选代表性模板 clustered_templates [] for i in range(n_clusters): cluster_templates [t for j, t in enumerate(templates) if labels[j] i] # 选最长模板信息最全 representative max(cluster_templates, keylen) clustered_templates.append(representative) return clustered_templates # 调用入口 all_templates load_all_templates(tmpdata/templates.txt) clustered cluster_templates(all_templates, n_clusters500) save_templates(clustered, tmpdata/clustered_templates.txt)参数选择n_clusters500是经验值对应PCA的n_components64。聚类后data_instances.csv的event_id映射表需同步更新否则index.py会报KeyError。4.4 验证你的数据集demo.png背后的三个必检指标demo.png是训练过程可视化图但真正决定数据集质量的是以下三个指标需在analyse/下手动验证检查项合格标准验证命令不合格后果模板稳定性MIN_TEMPLATE_STABILITY ≥ 0.4python analyse/check_stability.py --min 0.4模板过泛LSTM学不到有效模式序列长度分布95%序列长度 ∈ [2,5]python analyse/seq_length_dist.py过长序列导致LSTM内存溢出过短丢失时序信息异常比例0.5% ≤ 异常样本占比 ≤ 5%python analyse/anomaly_ratio.py低于0.5%模型欠学习高于5%则误报率飙升注意anomaly_ratio.py会扫描val/annotated.log中的is_anomaly字段。若你的日志无标注需先用规则引擎如grep -E fail|error|timeout生成粗标注再人工校验。5. 部署与监控如何把main.py变成守护进程在服务器上7×24小时盯日志写完模型不等于完成真正的工程落地是让它成为服务器上的“数字哨兵”。本项目虽未提供systemd脚本但main.py已预留部署接口只需三步即可转为生产服务。5.1main.py的守护模式添加--daemon参数启动修改main.py的argparse部分支持后台运行# main.py 开头添加 import argparse import os import sys import time from pathlib import Path def parse_args(): parser argparse.ArgumentParser() parser.add_argument(--mode, choices[train, eval, predict, daemon], defaulttrain) parser.add_argument(--log_dir, typestr, defaultlogdata/) parser.add_argument(--model_path, typestr, defaultmodel.pth) parser.add_argument(--interval, typeint, default300) # 监控间隔秒数 return parser.parse_args() if __name__ __main__: args parse_args() if args.mode daemon: # 守护进程主循环 print(f[INFO] Starting daemon mode, monitoring {args.log_dir} every {args.interval}s) model torch.load(args.model_path) model.eval() last_mtime 0 while True: # 检查log_dir下最新文件修改时间 latest_log max(Path(args.log_dir).rglob(*.log), keylambda f: f.stat().st_mtime, defaultNone) if latest_log and latest_log.stat().st_mtime last_mtime: last_mtime latest_log.stat().st_mtime # 加载新增日志行 new_lines read_new_lines(latest_log, last_pos) # 需实现read_new_lines if new_lines: predictions predict_batch(model, new_lines) # 写入告警日志 with open(alerts.log, a) as f: for pred in predictions: if pred[score] 0.8: # 动态阈值可替换 f.write(f{time.ctime()}: {pred[event]} anomaly score {pred[score]:.3f}\n) time.sleep(args.interval)关键点--interval 300设为5分钟避免高频IO。read_new_lines()需实现文件增量读取记录上次读取位置否则每次全量加载会拖垮服务器。5.2requirements.txt的生产精简删掉所有Jupyter依赖原requirements.txt含jupyter,matplotlib,seaborn等开发依赖生产环境必须剔除# requirements-prod.txt 替换原requirements.txt torch1.12.1 scikit-learn1.1.2 pandas1.4.4 numpy1.22.4 joblib1.1.0 regex2022.10.31验证命令pip install -r requirements-prod.txt python -c import torch, sklearn; print(OK)。若报错ModuleNotFoundError: No module named matplotlib说明精简成功。5.3 告警集成把alerts.log对接企业微信/钉钉机器人analyse/alert_sender.py提供Webhook发送模板# analyse/alert_sender.py import requests import json import time def send_wecom_alert(content: str, webhook_url: str): 发送企业微信告警 payload { msgtype: text, text: { content: f[DeepLog Alert] {time.strftime(%Y-%m-%d %H:%M:%S)} \n{content} } } response requests.post(webhook_url, jsonpayload) return response.status_code 200 # 在daemon循环中调用 if pred[score] 0.8: send_wecom_alert(fHigh-risk event: {pred[event]}, https://qyapi.weixin.qq.com/...)安全提示Webhook URL切勿硬编码在代码中应通过环境变量WECOM_WEBHOOK传入os.getenv(WECOM_WEBHOOK)。5.4 避坑生产部署的五个隐形地雷现象 → 原因 → 解决现象daemon模式运行2小时后内存占用飙升至90%。原因read_new_lines()未释放旧日志缓存predictions列表无限增长。解决在daemon循环开头添加gc.collect()且predictions只保留最近100条超限则predictions predictions[-100:]。现象企业微信收到告警但内容为空白。原因content字符串含不可见Unicode字符如\u200b微信API拒绝解析。解决发送前清洗content re.sub(r[\u200b-\u200f\u202a-\u202e], , content)。现象model.pth加载失败报RuntimeError: unexpected EOF。原因训练时模型保存在GPU而生产服务器无GPUtorch.load()默认map_locationcuda。解决torch.load(args.model_path, map_locationcpu)。现象alerts.log写入频繁磁盘IO 100%。原因每条告警独立open/write/close小文件IO开销大。解决改用缓冲写入——初始化alert_buffer []每10条或每60秒flush()一次。现象syslogparser.py解析Docker日志时timestamp字段解析为2023-10-01T08:30:45.123456789Z超出datetime.strptime精度。原因Pythonstrptime不支持纳秒。解决用dateutil.parser.parse()替代from dateutil import parser; parser.parse(timestamp)。6. 进阶技巧用test.pickle做模型蒸馏把LSTM压缩成轻量级决策树test.pickle不是测试数据而是本项目埋的“后悔药”——它存储了LSTM在test.log上每一行的原始预测概率Attention权重事件模板可用于模型蒸馏把10MB的PyTorch模型压缩成200KB的sklearn.tree.DecisionTreeClassifier部署到边缘设备。6.1test.pickle的结构解密不只是预测结果# 查看test.pickle内容 import pickle import pandas as pd with open(test.pickle, rb) as f: data pickle.load(f) print(Keys:, data.keys()) # [sequences, predictions, attention_weights, templates] print(Shape of predictions:, data[predictions].shape) # [N, 2] 二分类概率 print(Sample attention:, data[attention_weights][0]) # [3] 对应window_size3的权重关键洞察data[sequences]是原始事件ID序列如[12, 45, 88]data[predictions][:, 1]是异常概率data[attention_weights]是LSTM认为最关键的事件位置。这三者构成蒸馏的黄金三元组。6.2 蒸馏流程用LSTM的“判断依据”训练决策树# distill_to_tree.py import numpy as np import pandas as pd from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import classification_report # 1. 构造蒸馏特征序列统计 Attention加权 X_distill [] y_distill [] for i in range(len(data[sequences])): seq data[sequences][i] # [e1, e2, e3] attn data[attention_weights][i] # [w1, w2, w3] pred_prob data[predictions][i, 1] # 异常概率 # 特征工程序列统计 features [ np.mean(seq), # 平均事件ID np.std(seq), # 事件ID离散度 seq[0], # 首事件 seq[-1], # 尾事件 np.dot(seq, attn), # Attention加权事件ID np.max(attn), # 最大Attention权重 pred_prob # LSTM原始置信度软标签 ] X_distill.append(features) y_distill.append(1 if pred_prob 0.5 else 0) X_distill np.array(X_distill) y_distill np.array(y_distill) # 2. 训练轻量模型 dt DecisionTreeClassifier( p a hrefhttps://download.csdn.net/download/liufang_imei/88114761 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p