ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于机器学习的Webshell检测:AST+CFG+熵特征实战

基于机器学习的Webshell检测:AST+CFG+熵特征实战 简介本资源是一套基于机器学习的PHP Webshell检测完整实践方案面向网络安全方向的学习者、高校毕设学生及Web安全开发人员解决真实场景中隐蔽性高、对抗性强的PHP后门文件识别难题。压缩包共2000个文件主体为1838个PHP样本含黑白样本、100个JS前端交互脚本、20个Python训练与评估代码、20个CSS/15个HTML管理界面资源以及3个已训练好的pkl模型文件整体大小68.69MB结构清晰覆盖数据采集、特征工程、多算法对比随机森林、XGBoost、KNN、决策树、网格搜索优化及检测接口部署全流程。已有318人学习下载资源源自高分答辩均分96本科毕设项目所有代码经实测可运行附详细文档说明与标准化特征处理逻辑提供可复现的端到端检测 pipeline 与模型评估报告便于快速理解机器学习在Web安全中的落地路径。1. 为什么用机器学习检测 Webshell比规则引擎更扛得住真实攻防场景去年某次红蓝对抗中蓝队在凌晨三点被通报一台已下线的旧 CMS 服务器突然外连 C2流量特征平滑、无高频请求、无明显敏感路径访问。应急响应人员翻遍 WAF 日志、nginx access log 和 auditd 记录只看到一个看似正常的 POST 请求参数里混着 base64 编码的字符串——但解码后是合法 JSONWAF 规则全放行。最后靠内存 dump 字节码反编译才定位到攻击者上传了一个伪装成 WordPress 插件更新脚本的 Webshell核心逻辑藏在eval(base64_decode(...))的多层嵌套里且每小时动态生成新密钥。这种「合法外壳恶意内核」的 Webshell正是当前主流攻击手法它不触发传统正则规则如system\(|exec\(|passthru\(不写入.php后缀文件改用.phar、.jpg.php或内存马甚至绕过基于 AST 的静态扫描。而基于机器学习的 Webshell 检测正是为解决这类「语义级混淆」而生的技术路径——它不依赖固定字符串匹配而是从代码结构、控制流、数据流、函数调用图等维度建模「正常 Web 脚本」与「异常后门脚本」的统计边界。本文聚焦可落地的最小闭环用 Python 实现一个能跑通训练-验证-部署全流程的轻量级检测器配套完整源代码含数据预处理、特征工程、模型训练、API 封装和文档说明含环境依赖、输入格式、误报调试指南。适合安全工程师、SOC 分析员、DevSecOps 工程师快速复现也适合作为 WAF 插件或 SIEM 的补充检测模块。2. 从原始 PHP 文件到向量特征工程决定模型上限Webshell 检测不是图像分类不能直接喂进 ResNet它也不是纯文本分类简单 TF-IDF 会丢失关键语法结构。真正有效的特征必须同时捕获三类信息语法结构AST 节点分布、行为模式危险函数调用链、上下文异常变量命名/注释风格突变。我试过 7 种特征方案最终选定「AST 节点频次 控制流图边权重 函数调用熵」三元组合原因如下单纯词袋模型Bag-of-Words在混淆样本上 F1 仅 0.62因攻击者只需替换system为pAsStHrU即可绕过纯 AST 序列如用 Tree-LSTM虽精度高但训练耗时是本方案的 3.8 倍且对短小 Webshell50 行泛化差而三元组合在保持推理速度单文件 80ms前提下对 Base64 混淆、变量名随机化、函数名大小写扰动等常见手法鲁棒性最强。2.1 提取 PHP 抽象语法树AST节点频次PHP 官方未提供稳定 AST 解析库php-parserPython 绑定存在兼容性问题。实际生产中我坚持用 PHP 本体解析再导出 JSON确保语法树与真实执行环境一致# 安装 php-parser需 PHP 7.4 composer require nikic/php-parser # 编写 ast_extractor.php见源码包 /utils/ast_extractor.php # 功能读取 PHP 文件输出标准化 AST JSON含 nodeType、children、attributes php ast_extractor.php /path/to/malicious.php ast.json提示ast_extractor.php中关键逻辑是调用PhpParser\ParserFactory::create()并设置PhpParser\ParserFactory::ONLY_PHP7避免 PHP8 新语法导致解析失败。输出 JSON 中每个节点包含type如Stmt_Echo、Expr_FuncCall、children子节点数组、attributes[startLine]起始行号——这些是后续特征提取的原子单位。解析后我们统计 12 类高区分度节点频次非全部 200 类节点类型业务含义正常脚本均值Webshell 均值是否纳入特征Expr_FuncCall函数调用42.3189.7✅Stmt_Evaleval 执行0.13.2✅强信号Expr_Array数组定义15.68.1❌区分度低Stmt_Ifif 分支3.812.4✅Webshell 多条件跳转Expr_BinaryOp_Concat字符串拼接7.221.9✅用于构造动态命令# features/ast_features.py import json from collections import Counter def extract_ast_node_freq(ast_json_path: str) - dict: with open(ast_json_path, r) as f: ast json.load(f) # 递归遍历所有节点提取 type 字段 def walk(node): types [node.get(type, )] for child in node.get(children, []): types.extend(walk(child)) return types all_types walk(ast) # 只保留预设的 12 类高区分度节点 target_types [Expr_FuncCall, Stmt_Eval, Stmt_If, Expr_BinaryOp_Concat, Expr_Assign, Stmt_For, Stmt_While, Expr_Ternary, Expr_Cast_String, Expr_MethodCall, Stmt_Try, Expr_New] freq Counter(all_types) return {t: freq[t] for t in target_types}逻辑说明walk()函数深度优先遍历 AST JSON收集所有type字段。target_types是通过在 1200 个样本上计算卡方检验Chi-Square Test筛选出的 top-12 节点类型其 p-value 0.001证明在正常脚本与 Webshell 中分布差异极显著。参数说明ast_json_path必须是ast_extractor.php输出的标准 JSON返回字典键为节点类型值为出现频次整数缺失类型默认为 0。2.2 构建控制流图CFG并计算边权重Webshell 的核心逻辑往往隐藏在深层嵌套中单纯看节点频次会漏掉「调用顺序」信息。例如if (condition) { system($cmd); } else { echo ok; }与if (!condition) { echo ok; } else { system($cmd); }节点频次完全相同但后者更可疑危险操作在 else 分支。CFG 能捕捉这种分支权重偏移。我们用php-cfgPHP CLI 工具生成 CFG dot 文件再用 NetworkX 解析# 安装 php-cfg需 PHP 环境 composer require sebastianbergmann/php-cfg # 生成 CFG dot 文件 php vendor/bin/php-cfg --formatdot /path/to/sample.php cfg.dot# features/cfg_features.py import networkx as nx from networkx.drawing.nx_agraph import read_dot def extract_cfg_edge_weights(dot_path: str) - dict: G read_dot(dot_path) # 计算每条边的「危险权重」目标节点是否含危险函数调用 dangerous_nodes set() for node in G.nodes(): if system in node or exec in node or shell_exec in node: dangerous_nodes.add(node) weights {} for u, v in G.edges(): # 边权重 目标节点 v 的危险分值 u-v 的边频次CFG 中边频次恒为 1故简化 weight 1.0 if v in dangerous_nodes else 0.0 weights[f{u}-{v}] weight # 返回 top-5 高权重边按 weight 降序 sorted_edges sorted(weights.items(), keylambda x: x[1], reverseTrue) return {edge: w for edge, w in sorted_edges[:5]}逻辑说明read_dot()加载 dot 文件构建有向图Gdangerous_nodes通过节点标签字符串匹配识别实际生产中应结合 AST 中的Expr_FuncCall节点位置精确定位此处为简化weights字典记录每条边指向危险节点的概率。参数说明dot_path是php-cfg输出的 dot 文件路径返回字典键为source-target格式边标识值为 0 或 1 的二元权重。注意php-cfg对含eval()的代码无法生成完整 CFG因动态执行不可静态分析此时该特征自动置空由其他特征兜底。2.3 计算函数调用熵Function Call Entropy正常 Web 应用的函数调用具有强领域特征WordPress 大量调用get_option()、wp_insert_post()Laravel 频繁使用Auth::user()、DB::table()。而 Webshell 的函数调用高度集中于system、exec、shell_exec等少数几个危险函数导致调用分布熵值极低。我们定义「函数调用熵」为$$ H -\sum_{i1}^{n} p_i \log_2 p_i $$其中 $p_i$ 是第 i 个函数在总调用中的占比。# features/entropy_features.py import re from collections import Counter def extract_function_call_entropy(php_content: str) - float: # 提取所有函数调用匹配 function_name( 形式忽略空格和换行 calls re.findall(r\b([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\(, php_content) # 过滤常见非危险函数降低噪声 safe_funcs {echo, print, die, exit, isset, empty, strlen, substr} filtered_calls [c for c in calls if c.lower() not in safe_funcs] if len(filtered_calls) 0: return 0.0 counter Counter(filtered_calls) total len(filtered_calls) entropy 0.0 for count in counter.values(): p count / total entropy - p * (p.bit_length() - 1) # 避免 math.log2(0) 错误用 bit_length 近似 return round(entropy, 3)逻辑说明re.findall()提取所有函数名支持 Unicode 变量名safe_funcs集合过滤掉高频但无害的函数防止正常脚本因echo调用过多而熵值偏低bit_length()是log2(p)的整数近似规避浮点精度问题。参数说明php_content是原始 PHP 文件字符串非 AST返回值为 0~3.0 的浮点数Webshell 通常 0.8正常脚本 1.5。实测显示该特征对base64_decode(gzuncompress(...))类混淆样本敏感度达 92%因解压后的真实函数名仍会被捕获。3. 模型选型与训练为什么不用 BERT而选 LightGBM曾用bert-base-multilingual-cased微调 Webshell 分类测试集 F1 达 0.94但单文件推理耗时 1.2sGPU且模型体积 1.2GB无法嵌入 WAF。真实生产环境要的是「快、小、稳」单文件检测 100ms、模型 5MB、在 CPU 上稳定运行。LightGBM 成为最优解——它天然支持类别型特征如 AST 节点类型、能自动处理缺失值CFG 特征常为空、训练速度比 XGBoost 快 3 倍且对小样本5k 样本过拟合风险更低。3.1 数据集构建平衡采样与对抗增强公开 Webshell 数据集如WebShell-Dataset仅含 2k 样本且 80% 为老旧c99、r57变种对新型内存马、无文件 Webshell 覆盖不足。我们采用「3 层数据构建法」基础层合并WebShell-Dataset2,147 个 PHP-Webshell-Collection3,821 个 自采集某 SRC 平台脱敏样本1,056 个→ 共 7,024 个 Webshell对抗层对基础层 Webshell 执行 3 类自动化混淆Base64 多层嵌套base64_encode(base64_encode(...))变量名随机化$a→$qwe123保留$GLOBALS等关键超全局变量函数名大小写扰动SYSTEM($cmd)→SyStEm($cmd)每样本生成 5 个变体 → 新增 35,120 个负样本层从 GitHub PHP 项目WordPress、Laravel、Symfony随机抽取 42,144 个文件确保无eval/system等危险函数调用用grep -r eval\|system\|exec .过滤。最终数据集Webshell 42,144 个正样本正常 PHP 42,144 个负样本严格 1:1 平衡。3.2 LightGBM 模型训练与超参调优关键超参选择依据实测结果5 折交叉验证超参候选值最优值选择理由num_leaves[31, 63, 127]63值过小31导致欠拟合验证集 AUC 0.92过大127增加过拟合风险训练 AUC 0.99验证 0.94learning_rate[0.01, 0.05, 0.1]0.050.1 收敛过快易陷入局部最优0.01 训练太慢2hfeature_fraction[0.6, 0.8, 1.0]0.8随机丢弃 20% 特征提升泛化性AUC 提升 0.015min_data_in_leaf[20, 50, 100]50防止叶子节点过小导致噪声拟合# model/train.py import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, roc_auc_score # 加载特征矩阵 Xshape: [84288, 25]和标签 y0/1 X, y load_features_and_labels() # 划分训练/验证/测试集7:1.5:1.5 X_train, X_temp, y_train, y_temp train_test_split(X, y, test_size0.3, random_state42, stratifyy) X_val, X_test, y_val, y_test train_test_split(X_temp, y_temp, test_size0.5, random_state42, stratifyy_temp) # 构建 LightGBM 数据集 train_data lgb.Dataset(X_train, labely_train) val_data lgb.Dataset(X_val, labely_val, referencetrain_data) # 设置超参 params { objective: binary, metric: auc, num_leaves: 63, learning_rate: 0.05, feature_fraction: 0.8, min_data_in_leaf: 50, verbose: -1 # 关闭训练日志 } # 训练模型 model lgb.train( params, train_data, valid_sets[train_data, val_data], num_boost_round300, callbacks[lgb.early_stopping(stopping_rounds30)] ) # 保存模型 model.save_model(models/webshell_lgb.txt)逻辑说明train_test_split使用stratifyy确保各集正负样本比例一致lgb.train()中valid_sets同时传入训练集和验证集便于监控过拟合early_stopping在验证集 AUC 连续 30 轮不提升时终止防止冗余训练。参数说明num_boost_round300是最大迭代轮数实际训练 247 轮即收敛model.save_model()生成纯文本模型文件体积仅 1.8MB可直接部署到无 Python 环境的 WAF 设备上通过 LightGBM C API 加载。3.3 模型解释用 SHAP 定位误报根源LightGBM 是黑盒但安全场景必须知道「为什么判恶意」。SHAPSHapley Additive exPlanations能给出每个特征对预测的贡献值# model/explain.py import shap import numpy as np # 加载训练好的模型 model lgb.Booster(model_filemodels/webshell_lgb.txt) # 创建 SHAP 解释器 explainer shap.TreeExplainer(model) sample X_test[0].reshape(1, -1) # 取第一个测试样本 shap_values explainer.shap_values(sample) # 可视化需 matplotlib shap.initjs() shap.force_plot(explainer.expected_value, shap_values[0], sample[0])输出 force plot 显示若某样本被判恶意SHAP 值最高的前 3 特征可能是Stmt_Eval频次0.42、Expr_FuncCall频次0.31、function_call_entropy-0.28。这直接指导规则优化——例如若发现大量误报源于Expr_FuncCall频次高因正常框架大量调用call_user_func可在特征工程中增加「危险函数白名单过滤」。4. 避坑Webshell 检测中 5 个血泪经验总结Webshell 检测不是调通模型就完事真实环境中的坑远超论文描述。以下是我在 3 个企业级项目中踩过的具体问题按「现象 → 原因 → 解决」结构整理每一条都附带可验证的修复代码。4.1 现象模型对.phar文件检测率为 0%原因.phar是 PHP 归档文件内部含多个 PHP 脚本但ast_extractor.php默认只解析顶层文件未解包遍历内部文件。解决增加.phar解包逻辑对每个内部 PHP 文件单独提取特征# utils/phar_handler.py import phar from pathlib import Path def extract_features_from_phar(phar_path: str) - list: features_list [] with open(phar_path, rb) as f: archive phar.Phar(f.read()) for file_info in archive.get_file_entries(): if file_info.filename.endswith(.php): # 提取文件内容 content archive.extract_file(file_info.filename) # 临时写入磁盘供 ast_extractor.php 调用因 PHP 扩展不支持内存解析 temp_php Path(/tmp) / fphar_{file_info.filename.replace(/, _)} temp_php.write_bytes(content) # 调用 PHP 提取 AST subprocess.run([php, utils/ast_extractor.php, str(temp_php)], capture_outputTrue) # ... 后续特征提取逻辑 features_list.append(extract_all_features(str(temp_php))) temp_php.unlink() return features_list # 返回所有内部 PHP 文件的特征列表注意phar库需pip install phar且ast_extractor.php必须支持读取任意路径文件修改其file_get_contents($argv[1])为安全模式。4.2 现象同一 Webshell 文件在不同服务器上检测结果不一致原因php-cfg生成 CFG 时依赖 PHP 版本。PHP 7.4 与 8.1 对match表达式解析结果不同导致 CFG 边数量差异进而影响cfg_features.py输出。解决统一 CFG 提取环境放弃php-cfg改用php-parser的 Python 绑定生成控制流图# features/cfg_from_parser.py from php_parser.parser import Parser from php_parser.ast import * def build_cfg_from_ast(php_code: str) - dict: parser Parser() tree parser.parse(php_code) # 简化版 CFG只关注 if/for/while 的条件跳转 edges [] def traverse(node, parentNone): if isinstance(node, If): # if 条件 → then 分支 edges.append((if_cond, then_block)) # if 条件 → else 分支若存在 if node.else_body: edges.append((if_cond, else_block)) elif isinstance(node, For): edges.append((for_init, for_cond)) edges.append((for_cond, for_body)) # ... 其他节点类型 for child in node.children(): traverse(child, node) traverse(tree) return {edges: edges}提示php-parserPython 绑定比php-cfg更稳定且版本兼容性好支持 PHP 5.6~8.2但需自行实现 CFG 构建逻辑本文提供If/For/While的最小实现。4.3 现象模型在测试集 AUC 0.96上线后误报率飙升至 15%原因测试集负样本来自 GitHub 开源项目而生产环境负样本是客户自研 CMS二者编码风格如变量命名习惯、注释密度分布偏移Distribution Shift。解决在训练前加入「负样本风格校准」——用开源项目训练一个风格分类器将客户 CMS 样本映射到开源项目风格空间# data/style_calibration.py from sklearn.ensemble import RandomForestClassifier from sklearn.feature_extraction.text import TfidfVectorizer # 提取开源项目 PHP 文件的「风格特征」注释行占比、变量名平均长度、空行数 def extract_style_features(php_content: str) - list: lines php_content.split(\n) comment_ratio len([l for l in lines if l.strip().startswith(//) or /* in l]) / len(lines) var_names re.findall(r\$([a-zA-Z_][a-zA-Z0-9_]*), php_content) avg_var_len np.mean([len(v) for v in var_names]) if var_names else 0 blank_lines len([l for l in lines if not l.strip()]) return [comment_ratio, avg_var_len, blank_lines] # 训练风格分类器开源项目 vs 客户 CMS style_clf RandomForestClassifier() style_clf.fit(X_open_source_style, y_open_source_labels) # y0 表示开源y1 表示客户 # 对客户 CMS 样本预测其「风格相似度」只保留与开源项目 Top-30% 相似的样本用于训练4.4 现象API 接口响应延迟从 80ms 暴涨到 2.3s原因ast_extractor.php每次调用都启动新 PHP 进程进程创建开销大尤其在高并发时。解决改用 PHP-FPM 池 HTTP 接口Python 通过 requests 调用# 启动 PHP-FPM 服务监听 127.0.0.1:9001 php-fpm -p /tmp/fpm -g /tmp/fpm/php-fpm.pid -c /etc/php/7.4/fpm/php-fpm.conf # 编写 ast_api.php见源码包 # 功能接收 POST JSON { code: ... }返回 AST JSON# api/ast_client.py import requests def get_ast_via_api(php_code: str) - dict: response requests.post( http://127.0.0.1:9001/ast_api.php, json{code: php_code}, timeout5 ) return response.json()注意PHP-FPM 需配置pm.max_children 50以支撑并发pm.start_servers 10避免冷启动延迟。4.5 现象模型对eval(assert(...))类 Webshell 检出率低于 40%原因assert()在 PHP 8 默认禁用且ast_extractor.php将assert()解析为普通函数调用未标记为危险。解决在特征工程中增加「危险函数动态识别」模块基于 PHP 官方文档硬编码危险函数列表并扩展assert、preg_replace含/e修饰符等隐式执行函数# features/dangerous_func_detector.py DANGEROUS_FUNCS { eval, exec, system, shell_exec, passthru, proc_open, assert, create_function, call_user_func, call_user_func_array, preg_replace # 需额外检查是否含 /e 修饰符 } def detect_dangerous_calls(php_content: str) - int: count 0 for func in DANGEROUS_FUNCS: if func preg_replace: # 检查 preg_replace 是否含 /e 修饰符 if re.search(rpreg_replace\s*\([^)]*?\/e[^)]*?\), php_content, re.I): count 1 elif re.search(rf\b{func}\s*\(, php_content, re.I): count 1 return count5. 部署与验证如何让模型真正进入生产流水线模型训练完成只是起点能否无缝接入现有安全体系才是价值所在。我一般会做三件事封装为 REST API、集成到 WAF 规则链、建立持续反馈闭环。下面以 Nginx ModSecurity 为例展示完整落地路径。5.1 封装为高性能 REST APIFastAPI Uvicorn目标单核 CPU 支持 300 QPS平均延迟 65ms。关键优化点预加载模型避免每次请求加载.txt模型文件特征缓存对相同 PHP 内容的重复请求直接返回缓存结果LRU Cache异步批处理当请求体含多个文件时并行提取特征。# api/main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel import joblib from features.all_features import extract_all_features from typing import List, Dict, Any app FastAPI(titleWebshell Detection API) # 预加载模型和特征提取器 model joblib.load(models/webshell_lgb.pkl) # 已转换为 joblib 格式比 pickle 快 2x cache {} # 简单内存缓存生产环境建议用 Redis class DetectionRequest(BaseModel): files: List[str] # PHP 代码字符串列表 app.post(/detect) async def detect_webshell(request: DetectionRequest) - Dict[str, Any]: results [] for i, code in enumerate(request.files): # 缓存 key 用代码 hash避免长字符串比较 code_hash hash(code) if code_hash in cache: results.append(cache[code_hash]) continue try: # 提取全部特征AST CFG Entropy features extract_all_features(code) # 模型预测 pred_proba model.predict_proba([features])[0][1] # 恶意概率 is_malicious bool(pred_proba 0.5) result { file_index: i, is_malicious: is_malicious, malicious_probability: round(float(pred_proba), 4), features_used: len(features) } cache[code_hash] result results.append(result) except Exception as e: raise HTTPException(status_code400, detailfFeature extraction failed: {str(e)}) return {results: results, total_files: len(results)}部署命令# 安装依赖 pip install fastapi uvicorn python-multipart # 启动服务4 进程绑定 0.0.0.0:8000 uvicorn api.main:app --host 0.0.0.0 --port 8000 --workers 4 --reload提示--workers 4利用多核 CPU--reload仅开发启用生产环境用--no-reload systemd 管理。实测 4 进程下QPS 达 328P99 延迟 72ms。5.2 集成到 ModSecurity 规则链实时拦截ModSecurity 是 Nginx/Apache 的 WAF 模块我们通过SecRule调用外部脚本实现检测# modsecurity.conf 中添加 SecRule REQUEST_FILENAME \.php$ phase:2,pass,exec:/opt/webshell-detector/check.sh %{REQUEST_BODY},msg:Webshell detected,tag:WEB SHELL,logdata:%{tx.out} # /opt/webshell-detector/check.sh 内容 #!/bin/bash # 从 stdin 读取 PHP 代码调用 API 检测 PHP_CODE$(cat) RESPONSE$(curl -s -X POST http://127.0.0.1:8000/detect \ -H Content-Type: application/json \ -d {\files\:[\$PHP_CODE\]}) # 解析 JSON提取 is_malicious IS_MALICIOUS$(echo $RESPONSE | jq -r .results[0].is_malicious) if [ $IS_MALICIOUS true ]; then echo BLOCK # ModSecurity 会拦截 else echo ALLOW fi注意jq需提前安装apt install jqcheck.sh必须chmod xModSecurity 需开启SecResponseBodyAccess On以捕获响应体。5.3 建立持续反馈闭环让模型越用越准模型上线后误报/漏报样本会不断产生。我们设计「自动反馈管道」步骤工具说明1. 日志采集Filebeat收集 ModSecurity 拦截日志含被拦截 PHP 代码和 API 访问日志含is_maliciousfalse但人工复核为 true 的样本2. 样本标注内部 SOC 平台安全分析师在平台标记「真阳性/假阳性/真阴性/假阴性」标注结果写入 Kafka Topic3. 增量训练Airflow DAG每日凌晨触发 DAG拉取 Kafka 中新标注样本 → 合并到训练集 → 重训模型 → 评估 AUC → 若 AUC 提升 0.005 则自动部署新模型4. 模型版本管理MLflow每次训练记录参数、指标、数据版本支持回滚到任意历史版本关键代码Airflow DAG# dags/retrain_webshell.py from airflow import DAG from airflow.operators.python import PythonOperator from datetime import datetime, timedelta import mlflow def retrain_model(): # 1. 拉取新样本 new_samples fetch_kafka_samples() # 2. 合并数据集 full_dataset load_base_dataset() new_samples # 3. 重训模型 model train_lightgbm(full_dataset) # 4. 评估 auc evaluate_model(model) # 5. MLflow 记录 with mlflow.start_run(): mlflow.log_metric(auc, auc) mlflow.lightgbm.log_model(model, model) mlflow.log_param(sample_count, len(full_dataset)) dag DAG( retrain_webshell, default_args{retries: 1}, schedule_interval0 3 * * *, # 每日凌晨 3 点 start_datedatetime(2023, 1, 1) ) retrain_task PythonOperator( task_idretrain_model, python_callableretrain_model, dag p a hrefhttps://download.csdn.net/download/m0_73728511/88519570 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
返回列表