偏差与漂移监控

概览

TrustyAI Service (TAS) 会收集模型推理数据(请求输入和响应输出),将其持久化,并组织为用于分析的数据集,包括针对参考流量和线上流量的漂移检测与偏差评估。

主要功能包括:

  • 捕获并存储推理记录。
  • model_namedata_tag 组织记录。
  • 提供元数据 API,用于查看已收集的内容。
  • 为已记录字段配置人类可读的列名,以便简化下游分析。
  • 针对参考子集和当前生产数据运行漂移检测(例如 KS test、mean shift)。
  • 在受保护属性和结果上运行偏差指标(例如 SPD 和 DIR)。

本页介绍如何通过 POST /data/upload 摄取参考数据、通过 POST /consumer/kserve/v2 摄取线上推理数据,通过 HTTP API 注册漂移和偏差指标,以及如何将时间序列暴露给 Prometheus 以用于监控仪表板。

前提条件

  • 在你要监控的模型所在命名空间中,存在一个处于 Ready 状态的 TrustyAIService。请参阅 部署 TrustyAI Service

访问 TAS API

Service 概览与身份验证

在 TAS Deployment 中,kube-rbac-proxy 作为 sidecar 运行以提供身份验证。
operator 会在同一命名空间中创建两个 Service:

  • <tas-name>:直接将流量路由到 TAS 容器(原始 Service)。
  • <tas-name>-tls:将流量路由到 kube-rbac-proxy sidecar;这是需要 Authorization: Bearer <token> 的已认证端点。

获取 token

在与 TrustyAIService 相同的命名空间中创建一个 ServiceAccount、一个 Role(对 services/proxy 具有 getcreatedelete 权限)以及一个 RoleBinding;然后为该 ServiceAccount 创建 token:

# Replace <your-namespace> and optionally the ServiceAccount name (for example, `tas-client`)
kubectl create serviceaccount -n <your-namespace> tas-client
kubectl create role -n <your-namespace> tas-client --verb=get,create,delete --resource=services/proxy
kubectl create rolebinding -n <your-namespace> tas-client --role=tas-client --serviceaccount=<your-namespace>:tas-client
kubectl create token -n <your-namespace> tas-client

也可以选择设置 token 的有效期,例如使用 --duration=8760h 表示一年。最后一条命令会输出 token;将其设置为 Authorization: Bearer <token> 请求头的值。

Bearer token 和 base URL

在访问受保护的 TAS API 时,将上一小节中的 token 作为每个请求的 Authorization: Bearer <token> 请求头。

为调用选择 base URL(host)

  • 直接 Service(无 proxy 身份验证):https://<tas-name>.<your-namespace>.svc.cluster.local
  • 已认证端点(kube-rbac-proxy):https://<tas-name>-tls.<your-namespace>.svc.cluster.local —— 当 API 需要 Authorization: Bearer <token> 时使用此 host(通常是 -tls Service)。

例如,GET /info 请求可以写成:

curl -k -H "Authorization: Bearer $TOKEN" \
  "https://<tas-host>/info"

当需要该请求头时,请将 <tas-host> 替换为已认证的 host(通常是 -tls URL)。

数据摄取

训练和参考数据(POST /data/upload

POST /data/upload 是用于训练和参考批次的 JSON 上传路径:每次调用都会携带一条记录的完整 request 和 response,其中 data_tag 可使用诸如 TRAINING 之类的值来表示参考子集。线上生产推理由 POST /consumer/kserve/v2 单独摄取(见下文)。

TAS 可以存储由模型运行产生的推理记录。一条记录包含:

  • request:发送给模型的输入特征
  • response:模型返回的输出

数据集由 model_namedata_tag 定义。

准备一个数据集子集

  1. 选择一个 model_name 用于对上传记录进行分组。
  2. 为参考子集选择一个 data_tag。对于训练/参考数据,使用 data_tag: TRAINING
  3. 对于每次推理,上传一条合并后的 request + response 载荷。

请求体字段

请求体必须包含:

  • model_name:用于对已收集记录分组的模型标识符
  • data_tag:数据集子集标签
  • is_ground_truth:上传的输出是否为 ground truth(当载荷记录的是模型输出而不是已验证标签时,设为 false
  • request
    • id:该记录的 inference id
    • inputs:输入 tensor 列表
  • response
    • model_name:必须与 model_name 一致
    • id:必须与 request.id 一致
    • outputs:输出 tensor 列表

单条记录的语义: TAS 不会训练模型;它仅存储用于监控的行数据。对于 data_tag: TRAINING,每次上传都代表一个 参考 样本。request 携带会发送给模型的输入特征(相同的 id 用于关联这对数据)。response 携带该推理观测到的输出——通常是模型预测 tensor,以及用于公平性分析的任何结果字段(例如批准分数和决策)。漂移与公平性指标会将这个参考子集与通过 /consumer/kserve/v2 收集到的实时数据进行比较。

下面的示例使用一个小型信用评分风格的特征集:数值输入和一个分组字段(gender),以及名为 predict-0approved 的输出。

{
  "model_name": "demo-model",
  "data_tag": "TRAINING",
  "is_ground_truth": false,
  "request": {
    "id": "training-1",
    "inputs": [
      { "name": "credit_inputs-0", "shape": [1, 1], "datatype": "FP32", "data": [21.0] },
      { "name": "credit_inputs-1", "shape": [1, 1], "datatype": "FP32", "data": [605.0] },
      { "name": "credit_inputs-2", "shape": [1, 1], "datatype": "FP32", "data": [12.0] },
      { "name": "credit_inputs-3", "shape": [1, 1], "datatype": "FP32", "data": [5.0] },
      { "name": "gender", "shape": [1, 1], "datatype": "INT64", "data": [0] }
    ]
  },
  "response": {
    "model_name": "demo-model",
    "id": "training-1",
    "outputs": [
      { "name": "predict-0", "shape": [1, 1], "datatype": "FP32", "data": [0.301] },
      { "name": "approved", "shape": [1, 1], "datatype": "INT64", "data": [0] }
    ]
  }
}

使用 curl 上传

curl -k -X POST "https://<tas-host>/data/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @<training-data.json>

线上推理数据(POST /consumer/kserve/v2

推理时的数据通过 KServe v2 consumer 端点发送到 TAS。每次逻辑推理都使用相同 correlation id两次 POST 调用:先发送模型输入(kind: "request"),再发送模型输出(kind: "response")。请求体为 JSON;tensor 载荷是 Base64 编码ModelInferRequest / ModelInferResponse protobuf 消息,如 KServe prediction API v2 (grpc_predict_v2.proto) 中所定义,而不是 /data/upload 使用的扁平 tensor JSON。

JSON 字段:

字段描述
id关联 request/response 对(例如一个 prediction id)。
kind"request""response"
modelid用于对已存储行分组的模型标识符;指标请求使用相同的 modelId。protobuf blob 可能省略 model_name;TrustyAI 会使用这个 JSON modelid 进行持久化。
datakind 对应的 protobuf 消息的 Base64 编码。

示例序列(Base64 blob 为占位符):

# Request half of one inference
curl -k -X POST "https://<tas-host>/consumer/kserve/v2" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id":"<prediction-id>","kind":"request","modelid":"<modelId>","data":"<base64-ModelInferRequest>"}'

# Response half (same id)
curl -k -X POST "https://<tas-host>/consumer/kserve/v2" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id":"<prediction-id>","kind":"response","modelid":"<modelId>","data":"<base64-ModelInferResponse>"}'

referenceTag 设为 TRAINING 的漂移指标会将该参考子集(来自 /data/upload)与通过此 consumer 路径收集到的自然流量行进行比较。GET /info/tags 可以列出诸如 TRAINING 之类的标签,以及线上流量对应的未标记/自然流量一侧。

列名映射(POST /info/names

通过 /data/upload 摄取训练/参考数据并通过 /consumer/kserve/v2 摄取线上推理数据后,TAS 可以报告已记录的内容,并使用 POST /info/names 将记录的输入/输出列名映射为人类可读名称。

示例:

curl -k -X POST "https://<tas-host>/info/names" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "modelId": "demo-model",
  "inputMapping": {
    "credit_inputs-0": "Age",
    "credit_inputs-1": "Credit Score",
    "credit_inputs-2": "Education",
    "credit_inputs-3": "Employment",
    "gender": "Gender"
  },
  "outputMapping": {
    "predict-0-0": "Acceptance Probability",
    "approved-0": "Approved"
  }
}'

数据漂移指标

数据漂移指标会将一个 参考 子集(例如 POST /data/upload 中标记为 TRAINING 的行)与 当前 生产数据(通常通过 POST /consumer/kserve/v2 摄取)进行比较。注册、列出和删除遵循与其他计划指标相同的请求/响应模式。

漂移指标类型

指标角色
KSTest对参考数据与当前数据在所选列上的经验分布进行 Kolmogorov–Smirnov 风格比较。
MeanShift比较参考数据与当前数据在所选列上的均值(及相关统计量)。
ApproxKSTest带有可调精度参数(epsilonthresholdDelta)的近似 KS 风格漂移检测。
FourierMMD使用随机 Fourier 特征(gammaparameters)通过 maximum mean discrepancy 进行漂移检测。

GET /metrics/drift/<name>/definition 会为每个指标返回人类可读的文档说明。

注册计划漂移指标

使用带有 JSON 请求体的 POST /metrics/drift/<metricName>/request。常见字段包括:

  • modelId:数据集 id(必须与上传内容 / consumer 中的 modelid 一致)。
  • requestName:此计划任务的唯一名称。
  • metricName:必须与路径段一致(kstestmeanshiftapproxkstestfouriermmd)。
  • batchSize:每次计算运行中包含的推理行数。
  • referenceTag:参考子集的标签(通常为 TRAINING)。
  • fitColumns:要评估的输入列名(已记录字段名,例如在 POST /info/names 映射之前的 tensor 名称)。

KSTest — 示例:

curl -k -X POST "https://<tas-host>/metrics/drift/kstest/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "modelId": "<modelId>",
  "requestName": "<requestName>",
  "metricName": "kstest",
  "batchSize": 20,
  "referenceTag": "TRAINING",
  "fitColumns": ["credit_inputs-0", "credit_inputs-1"]
}'

MeanShift — 示例:

curl -k -X POST "https://<tas-host>/metrics/drift/meanshift/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "modelId": "<modelId>",
  "requestName": "<requestName>",
  "metricName": "meanshift",
  "batchSize": 20,
  "referenceTag": "TRAINING",
  "fitColumns": ["credit_inputs-0", "credit_inputs-1", "credit_inputs-2", "credit_inputs-3"]
}'

ApproxKSTest — 额外包含 thresholdDeltaepsilon(语义请参见 GET /metrics/drift/approxkstest/definition):

curl -k -X POST "https://<tas-host>/metrics/drift/approxkstest/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "modelId": "<modelId>",
  "requestName": "<requestName>",
  "metricName": "approxkstest",
  "batchSize": 20,
  "thresholdDelta": 0.05,
  "referenceTag": "TRAINING",
  "fitColumns": ["credit_inputs-0", "credit_inputs-1"],
  "epsilon": 0.01
}'

FourierMMD — 额外包含 thresholdDeltagammaparameters 对象(请参见 GET /metrics/drift/fouriermmd/definition):

curl -k -X POST "https://<tas-host>/metrics/drift/fouriermmd/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "modelId": "<modelId>",
  "requestName": "<requestName>",
  "metricName": "fouriermmd",
  "batchSize": 20,
  "thresholdDelta": 0.05,
  "referenceTag": "TRAINING",
  "fitColumns": ["credit_inputs-0", "credit_inputs-1"],
  "gamma": 1.0,
  "parameters": { "nWindow": 10, "nTest": 10, "nMode": 50, "randomSeed": 0, "sig": 1.0, "deltaStat": false, "epsilon": 0.01 }
}'

一次性漂移请求

对于一次按需运行(非计划任务),POST /metrics/drift/kstestPOST /metrics/drift/meanshift 接受与计划注册相同的 JSON 请求体结构,但不带 /request 后缀路径。

列出和删除计划漂移任务

列出计划任务:

curl -k -H "Authorization: Bearer $TOKEN" \
  "https://<tas-host>/metrics/drift/kstest/requests"

根据列表响应中的 requestId 停止任务:

curl -k -X DELETE "https://<tas-host>/metrics/drift/kstest/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "<requestId-uuid>"
}'

对于其他漂移类型,将路径中的 kstest 替换为 meanshiftapproxkstestfouriermmdGET /metrics/all/requests 会列出跨类别的计划指标。

Prometheus 指标(漂移)

计划漂移结果会作为 Micrometer gauges 发布到 GET /q/metrics,例如:

  • KSTest: trustyai_kstest
  • MeanShift: trustyai_meanshift
  • ApproxKSTest: trustyai_approxkstest
  • FourierMMD: trustyai_fouriermmd

每条漂移序列都带有用于标识计划任务及其测量对象的标签。trustyai_kstest / trustyai_meanshift / trustyai_approxkstest / trustyai_fouriermmd 上常见的 语义 标签包括:

标签角色
model模型数据集 id(与上传 / 指标请求中的 modelId 一致)。
requestName计划漂移请求的名称(来自 POST .../kstest/request 等)。
metricName该序列暴露的指标类型(例如 KSTESTMEANSHIFT;具体大小写取决于 TAS 构建版本)。
batch_size为计划请求配置的 batch size。
subcategory样本所对应的特征或列(例如某个映射后的名称,如 Acceptance Probability,取决于指标和 fitColumns)。
request指标请求实例的内部 id(UUID),如果存在。
endpoint来自 Micrometer 的传输或抓取路径提示(例如 http)。

抓取 / target 标签(名称取决于集群和 ServiceMonitor 配置)通常包括 namespacepodservicejobinstance。部署的实际值应从 Prometheus 中读取(例如通过指标 UI 中 trustyai_kstest 的标签名称或使用 label_values()),而不是直接复制文档——环境相关的 id 和地址会因集群而异。

PromQL 示例(缩小到一个模型和一个 requestName):

trustyai_kstest{model="<modelId>", requestName="<requestName>"}

如果要过滤单个列或特征,可以在该标签存在时为 subcategory 添加匹配器。

偏差指标

本节介绍如何通过 TAS HTTP API(对 /metrics/group/fairness/... 执行 POST / GET / DELETE)进行偏差监控(组公平性指标,如 SPD 和 DIR)。

SPD 和 DIR

TAS 在相同的受保护属性和结果上暴露两个相关的 组公平性 指标:

缩写全称含义(典型用法)
SPDStatistical Parity Difference未特权组的有利结果率与特权组的有利结果率之差。数值越接近 0,表示组间越接近平衡。
DIRDisparate Impact Ratio未特权组有利结果率与特权组有利结果率的比值。数值越接近 1,表示越接近平衡(传统“四分之五”风格的检查通常会将该比值与阈值进行比较)。

两者使用相同的请求字段(protectedAttributeoutcomeNamefavorableOutcome 等)。HTTP 路径和 metricName 用于区分 SPDspd)与 DIRdir)。

注册计划偏差指标

通过以下调用,为已部署的模型数据集创建一个周期性的偏差指标请求:

  • POST /metrics/group/fairness/spd/request(Statistical Parity Difference, SPD)
  • POST /metrics/group/fairness/dir/request(Disparate Impact Ratio, DIR)

示例(SPD):

curl -k -X POST "https://<tas-host>/metrics/group/fairness/spd/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "modelId": "<modelId>",
    "requestName": "<requestName>",
    "metricName": "spd",
    "batchSize": 20,
    "protectedAttribute": "<protectedAttribute>",
    "privilegedAttribute": <privilegedValue>,
    "unprivilegedAttribute": <unprivilegedValue>,
    "outcomeName": "<outcomeName>",
    "favorableOutcome": <favorableOutcome>
  }'

SPD 请求字段:

  • modelId:要计算该指标的模型数据集 id(必须与上传中使用的数据集/模型一致)。
  • requestName:该计划指标请求的唯一名称(用于区分周期性任务)。
  • metricName:指标类型名称;对于此端点使用 spd
  • batchSize:TAS 在计算计划指标时包含的推理记录数。
  • protectedAttribute:定义被比较组的特征(如果使用了名称映射,则使用映射后的名称)。
  • privilegedAttributeprotectedAttribute 的值,代表特权组。
  • unprivilegedAttributeprotectedAttribute 的值,代表未特权组。
  • outcomeName:用于公平性评估的输出字段(例如分类结果)。
  • favorableOutcome:被视为有利结果的 outcomeName 值。

对于 DIR,使用相同的 JSON 请求体,将 "metricName" 设为 "dir",并调用 POST /metrics/group/fairness/dir/requestGET /metrics/group/fairness/dir/definition 会以文本形式描述该指标。

列出和删除计划偏差任务

要停止周期性计算,先列出任务,然后将响应中的 requestId 发送到对应的删除端点:

指标列表删除
SPDGET /metrics/group/fairness/spd/requestsDELETE /metrics/group/fairness/spd/request
DIRGET /metrics/group/fairness/dir/requestsDELETE /metrics/group/fairness/dir/request

示例(SPD):

curl -k -H "Authorization: Bearer $TOKEN" \
  "https://<tas-host>/metrics/group/fairness/spd/requests"
curl -k -X DELETE "https://<tas-host>/metrics/group/fairness/spd/request" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "<requestId-uuid>"
  }'

对于 DIR 任务,使用相同的 requestId JSON 请求体和 .../dir/request 删除 URL。

Prometheus 指标(偏差)

TAS 会在 /q/metrics 上暴露 Prometheus 指标。TrustyAI Operator 会创建一个 ServiceMonitor,以持续采集与偏差相关的指标(默认匹配 trustyai_(spd|dir).* 的序列)。

基础指标名称:

  • SPD(Statistical Parity Difference):trustyai_spd
  • DIR(Disparate Impact Ratio):trustyai_dir

每条序列都带有用于标识计划计算及其公平性配置的标签。trustyai_spd / trustyai_dir 上常见的 语义 标签包括:

标签角色
model模型数据集 id(与上传 / 指标请求中的 modelId 一致)。
requestName计划指标请求的名称(来自 POST .../spd/requestPOST .../dir/request)。
metricName该序列暴露的指标类型(例如 SPDDIR)。
protected受保护属性列(例如 Gender)。
outcome结果列(例如 Approved)。
favorable_value被视为有利结果的值。
privileged / unprivilegedprotected 上的特权组和未特权组取值。
batch_size为计划请求配置的 batch size。
request指标请求实例的内部 id(UUID),如果存在。

抓取 / target 标签(名称取决于集群和 ServiceMonitor 配置)通常包括 namespacepodservicejobinstance

示例:选择一个模型和一个计划请求名称的 SPD:

trustyai_spd{model="demo-credit-model-1774142686-91294", requestName="demo-spd-1774142686-91294"}

示例:某个时间窗口内的最新值(将范围与抓取间隔对齐):

max_over_time(trustyai_spd{model="<modelId>", requestName="<requestName>"}[15m])

当注册了 DIR 计划任务时,相同的标签过滤条件同样适用于 trustyai_dir