cover_image

前端数据中心使用LLM训练错误信息

陈泽伟 Goodme前端团队
2024年11月25日 00:00

背景

之前分享过一篇 古茗是如何做前端数据中心的 ,其中我就有一个错误监控模块

在错误模块中,我们收集了 APP、小程序(微信、支付宝、钉钉、抖音等等)、浏览器、webview等等平台的错误

小程序我们使用 taro 编写,但是 taro 的错误是一个字符串,他把 message 和 stack 整合在一起了、

然后我们就会得到各种各样的报错信息:

MiniProgramError
Module build failed (from xxx.js):
SyntaxError: xxx.tsx: Unexpected token (180:12)

[0m [90m 178 |[39m             {
 [90m 179 |[39m               isReady [33m&&[39m indexFallback[33m?[39m[33m.[39menableActivity [33m&&[39m
[31m[1m>[22m[39m[90m 180 |[39m             }
 [90m     |[39m             [31m[1m^[22m[39m
 [90m 181 |[39m
 [90m 182 |[39m             [33m<[39m[33mTaskSection[39m position[33m=[39m[32m"index"[39m [33m/[39m[33m>[39m
 [90m 183 |[39m[0m
    at toParseError (xxx.ts:74:19)
    at TypeScriptParserMixin.raise (xxx.ts:1490:19)
   
USER_PAGE_NOT_FOUND_ERROR: Page[xxx] not found. May be caused by: 1. Forgot to add page route in app.json. 2. Invoking Page() in async task. construct@[native code]
t@file:xxx.js:2:43094
t@file:xxx.js:2:55180
t@file:xxx.js:2:55373
@file:xxx.js:2:524958
OC@file:xxx.js:2:525081

等等。。。

如果用正则去匹配,完全无法穷尽,只会得到各种各样奇怪的内容

如何拆分

因为我们错误告警等等需要针对错误聚合后的信息进行告警、忽略、流程处理等等,所以我们需要尽量收敛同一个类型的错误不要因为 message 扩散。

针对一些规范的错误、例如 TypeError、Error 等等常见的普通错误,我们直接在 SDK 使用正则拆分

但是针对一些各种平台自定义的小程序错误、甚至自定义错误我们采用AI方式去识别

技术方案

其实很多开源模型都可以通过微调来实现,例如 Hugging Face、spaCy 等等

这里我们通过 spaCy 来进行微调模型

spaCy 提供了一个预训练小型英语 NLP 模型: en_core_web_sm, 这个模型体积小、加载快、资源消耗低

微调模型

准备预训练数据集

微调模型最重要的就是如何准备训练数据

因为本来实在有点懒、所以准备使用chatgpt来准备训练数据,人工进行数据校验

首先我们直接导出数据库中几十万条之前上报的错误信息(我是通过数据库工具直接导出csv文件)

然后将文件直接喂给 chatgpt,告诉 chatgpt 如何进行数据修改

图片

然后我们再对数据进行查看,如果有一些格式的错误无法被正确分类,就告诉 chatgpt 如何进行分类

最后告诉chatgpt导出我们需要的预训练数据集

图片

训练模型

直接跑训练代码(简要)

import spacy
import random
from spacy.training.example import Example
import json

# 加载 spaCy 格式的训练数据
with open("xxx.json""r", encoding="utf-8"as f:
    train_data = json.load(f)

# 加载或初始化 spaCy 模型
nlp = spacy.blank("en")  # 创建一个空的 spaCy 模型
if "ner" not in nlp.pipe_names:
    ner = nlp.add_pipe("ner", last=True)
else:
    ner = nlp.get_pipe("ner")

# 添加标签
for _, annotations in train_data:
    for ent in annotations["entities"]:
        ner.add_label(ent[2])  # 添加实体标签,例如 "MESSAGE" 或 "STACK"

# 准备训练数据
train_examples = []
for text, annotations in train_data:
    example = Example.from_dict(nlp.make_doc(text), annotations)
    train_examples.append(example)

# 开始训练
optimizer = nlp.initialize()

# 设置训练参数
n_iter = 10  # 训练迭代次数
for epoch in range(n_iter):
    random.shuffle(train_examples)
    losses = {}
    
    # 使用批量训练数据更新模型
    for batch in spacy.util.minibatch(train_examples, size=8):
        nlp.update(batch, sgd=optimizer, drop=0.35, losses=losses)
    
    print(f"Epoch {epoch+1}/{n_iter} - Losses: {losses}")

# 保存模型
nlp.to_disk("/mnt/data/error_log_model")
print("模型已保存至 '/mnt/data/error_log_model'")

当训练日志显示,模型的损失在逐渐下降,说明训练过程是正常的,我们可以继续训练更多的 Epoch,直到损失不再显著下降,或达到一个理想的损失范围。若损失值降至较低水平,表明模型已学到足够的特征信息,可以停止训练。

测试模型

import spacy

# 加载训练好的模型
MODEL_PATH = "error_log_model"  # 替换为模型的实际路径
nlp = spacy.load(MODEL_PATH)

# 然后通过我们测试的数据进行测试,测试成功之后就可以部署我们的模型了

后续

部署后为了提高并发性能,我们可以采用特征缓存等等

识别出现错误后,我们可以通过平台直接进行人工数据标记,然后将标记数据纳入下一次训练的数据集,长期微调模型


继续滑动看下一个
Goodme前端团队
向上滑动看下一个