ELAI S.r.l.

AI 报警变多了:会不会是传感器变了?

重训 AI 前先区分真实变化与测量误差:用合成数据完整解释校准、独立检查和适用限制。

AI 报警变多了:会不会是传感器变了?

更换 AI 之前,先检查它测到了什么

系统报告房间比平时更热。负责人发现警报增多,想重新训练模型。但变化可能来自房间、传感器,或转换读数的软件。如何在把时间花到错误问题之前分辨原因?我们将通过完整数值算例,理解什么时候应先检查传感器,再考虑更新 AI,以及决策需要哪些信息。

案例完全合成:温度和参考值都是作者选择的数字,不是仪器读数。未测试微控制器、传感器、AI 模型或报警系统。实际执行的代码估计参考值与读数间的简单关系,求逆,并检查另外四个值。它不是认证程序,也不证明设备性能。所有结果都会保留计算与测量的区别。

单次读数不能独自说明原因

假设读数为 22 °C。在世界 A,真实温度是 20 °C,仪器加上恒定的 2 °C 误差。在世界 B,真实温度确为 22 °C,仪器没有额外偏差。观测数字完全相同。只收到这个数字的算法,在没有其他假设或信息时无法区分两个世界。这不是训练缺陷,而是信息不足,即原因不可识别。

observed = true_value + offset 22 = 20 + 2 = 22 + 0

独立参考可以打破歧义,但必须在相关条件一致时测量同一量。不同位置的探头可能读到不同温度,却都没有故障。同步、位置和稳定时间都是比较的一部分。独立不等于绝对正确;参考也有不确定性,可能与仪器共享误差。理想算例假定参考值已知,是为了隔离机制,并非声称现场存在这种完美条件。

两种不同的偏差方式

传感器可能总是增加同一偏移,也可能放大变化。为区分二者,采用具有截距 a 和斜率 b 的直线。若参考值和读数均以摄氏度表示,a 的单位为 °C,b 无单位。误差 e 是直线未解释的部分。NIST 将这类模型列为校准模型之一;我们选择仿射情况只是教学假设,并非所有传感器的规律。

y = a + b*x + e

代入数值:a=2 °C、b=1.1,在加上小误差 e 之前把 20 °C 变成 24 °C;40 °C 则读为 46 °C。只减两度可修正截距,却留下比例误差:40 度时仍得到 44。只在一个温度点比较,不能充分区分斜率与截距。需要多个水平,并检查该关系在使用范围内是否成立。

构造数据并估计修正

采用五个合成参考值:0、10、20、30、40 °C。每个值按 2+1.1x 生成两次读数,分别加 −0.2 和 +0.2 °C。十个确定性点具有刻意构造的对称性,不是随机样本,也不是重复性试验。代码保留全部点,不删除不方便的观测。这种对称性使参数恢复容易理解,但远比真实校准简单。

选择直线前,先定义“拟合这些点”的含义。最小化竖直偏差平方和,会更强地惩罚大偏差。J 写成 [yᵢ−a−bxᵢ]² 的总和。该准则把 x 当成已知;若参考的不确定性不可忽略,这种选择可能不适当。方法简单,不等于可以忽略实验条件。

J(a,b) = sum_i (y_i-a-b*x_i)^2 b_hat = sum_i((x_i-x_mean)*(y_i-y_mean)) / sum_i((x_i-x_mean)^2) a_hat = y_mean - b_hat*x_mean

后两个表达式来自令 J 对 a、b 的导数为零:一个要求平均残差为零,另一个要求残差与中心化 x 不相关。分母衡量参考值的分散;若参考全相同,分母为零,斜率不可识别。本例均值为 20、24 °C,分子为 2200 °C²,分母为 2000 °C²,得到 b=1.1、a=2 °C,并由程序检查。

从读数到修正值:把问题反过来

此前根据参考值预测仪器读数;实际使用时问的恰好相反:收到的读数对应什么值?对 x 解方程,先减截距,再除斜率。x̂ 表示估计,不是真实值。求逆要求 b 非零。如果 b 很小,除法会放大微小读数误差;漂亮公式不会消除这个问题。

x_hat = (y-a_hat)/b_hat y = 7.61 degC => x_hat = (7.61-2)/1.1 = 5.1 degC

另用未参与估计的 5、15、25、35 °C 四个参考检查计算。按同一关系生成读数,交替加 +0.11、−0.11 °C。首个读数为 7.61,修正为 5.1,仍有 +0.1 °C 误差。四点都在参考范围内,因此不是外推测试。它们与拟合点分开,但不是独立仪器验证。

参考 °C读数 °C修正 °C原始误差 °C修正误差 °C
57.615.12.610.1
1518.3914.93.39-0.1
2529.6125.14.610.1
3540.3934.95.39-0.1

表中各列单位均为 °C;误差定义为估计减参考。另报告平均绝对误差 MAE:绝对值相加后除以四,同时保留单独的带符号误差。原读数 MAE 为 4 °C,修正后为 0.1 °C,代码实际计算了二者。数据构造有利于改进,不能预测真实传感器改进幅度或 AI 模型准确率。

MAE = (1/n)*sum_i abs(estimate_i-reference_i) raw_MAE = 4.00 degC ; corrected_MAE = 0.10 degC
完全合成数据:左侧为十个拟合读数的偏差,右侧为四个独立检查点修正前后误差。坐标单位 °C,无硬件测量。
完全合成数据:左侧为十个拟合读数的偏差,右侧为四个独立检查点修正前后误差。坐标单位 °C,无硬件测量。

左图每点为读数减参考;偏差随温度增加,与构造的比例误差一致。右图四个检查误差从正的数度变为 ±0.1。两图纵轴尺度相同,便于比较幅度;这不会把四点变成统计分布。若只画读数,看起来几乎完全线性,反而掩盖偏差对决策的重要性。

修正偏差不等于让噪声仪器变精密

偏差是系统性偏离;精密度描述规定条件下重复测量的分散,二者不同。NIST 对校准失败的讨论指出,修正不能解决精密度不足或随时间失控的变化。本例波动由作者设定,小且对称;没有估计日漂移、滞后或湿度依赖。若拟合后物理关系变化,固定修正也会变错。

不应自动删除大偏差。异常读数可能揭示间歇连接、真实变化或参考尚未稳定。排除必须有理由并可追溯。直线本身也需质疑:弯曲残差可能提示非仿射关系。增加参数可更贴合已有点,却不保证新测量可靠。关键比较应使用未参与选择修正的条件和点。

明天偏移变了,旧修正就可能失效

直接检验稳定性假设:给四个检查读数都加 0.55 °C,不重新估计系数。旧修正会把变化也除以 1.1,因此每个估计增加 0.5 °C。误差变为 +0.6、+0.4、+0.6、+0.4 °C,MAE 升到 0.5 °C。这也是构造数据的计算,不是观测漂移。保留修正系数并不代表可以忘记参考比较。

delta_x_hat = delta_y/b_hat = 0.55/1.1 = 0.5 degC MAE_after_assumed_offset_change = 0.5 degC

求逆还揭示局部敏感度:固定系数时,读数偏离 0.2 °C,除以 1.1 后估计约偏离 0.1818 °C。这不是测量总不确定性,不包含系数、参考、探头位置或直线有效性的不确定性。把敏感度当成准确度区间,会声称本例没有测到的质量。比较板卡与算法时也应保留这个区别。

运行决策还需要由任务理由支持的容差。描述房间趋势时可接受的偏差,对其他工艺未必适用;这里不设通用阈值。接近决策边界时,应问数值还剩多少不确定性,而非软件打印几位小数。计算可以有很多小数,但物理数据不会因此同样精确。因此结果保留单位与假设,不承诺可直接部署的校准。

嵌入式设备上的决策如何变化

嵌入式表示软件运行在专用设备中,常有内存、能源和时间限制。仿射修正每样本需要一次减法和一次除法,无需重训网络。这描述运算次数,不是实测延迟;硬件上数值表示、库和采集都重要。两个系数不等于系统全部 RAM 需求。没有测量,因此不报告毫秒、瓦或焦耳。

改变修正应用位置,也会改变模型输入的意义。历史数据若已修正,再修正一次会增加错误。模型若在原读数上训练,改变输入尺度会改变决策;应以独立标签重新评价整条链。记录应附带系数版本、单位、有效范围及模型版本。替换两个数后警报减少,并不足以证明有效。

还应比较不用 AI 的方案:对正确测量物理量设置规则,可能足以满足简单需求。反之,校准不能修复温度与工艺结果关系的真实变化。如果传感器与独立参考一致,但新标注案例的预测变差,重训、增加变量或维护工艺才重新成为合理假设。没有普遍答案,证据必须区分测量故障与任务变化。

证明了什么,还缺什么

反例证明单次读数不能识别原因,随后数值检查了对独立合成值的仿射修正。没有证明真实传感器漂移,也没有证明模型需要重训。实际决策是在盲目干预前先获得独立比较。测量改善与最终任务改善是两项不同验证;改变运行链时应分别记录。

真实试验仍需覆盖实际范围、带明确不确定性的参考、跨时间重复及相关环境条件。硬件还应验证转换、数值精度、超范围处理及配置回退。本例不涉及临床决策或安全设备。EL-AI 并非在此被描述为传感器制造商或试验执行方;编辑层面对嵌入式的兴趣,不证明已有产品或安装。

附带代码用 Decimal 精确保留所选十进制输入,创建十对数据,计算均值与总和,估计两个参数,再检查四点和反例。复杂度随数据对数线性增长,保存列表也一样。它不是微控制器库,也不测量设备内存。断言只记录预期算术已经执行,不能替代计量、统计或集成试验。

NIST/SEMATECH e-Handbook — Models for instrument calibration, section 2.3.6.1.

NIST/SEMATECH e-Handbook — Data collection, section 2.3.6.2.

NIST/SEMATECH e-Handbook — What can go wrong with calibration, section 2.3.6.4.

NIST/SEMATECH e-Handbook — Data analysis and model validation, section 2.3.6.5.

NIST/SEMATECH e-Handbook — Linear Least Squares Regression, section 4.1.4.1.

from decimal import Decimal as D
from pathlib import Path
import json,sys
# Entirely synthetic temperatures and reference values, not hardware measurements.
train=[(D(x),D('1.1')*D(x)+D(2)+e) for x in [0,10,20,30,40] for e in [D('-.2'),D('.2')]]
xbar=sum(x for x,y in train)/len(train);ybar=sum(y for x,y in train)/len(train)
b=sum((x-xbar)*(y-ybar) for x,y in train)/sum((x-xbar)**2 for x,y in train)
a=ybar-b*xbar
assert a==2 and b==D('1.1')
test=[]
for x,e in zip([5,15,25,35],[D('.11'),D('-.11'),D('.11'),D('-.11')]):
 x=D(x);y=D('1.1')*x+2+e;corrected=(y-a)/b
 test.append(dict(referenceC=str(x),readingC=str(y),correctedC=str(corrected),rawErrorC=str(y-x),correctedErrorC=str(corrected-x)))
raw=sum(abs(D(t['rawErrorC'])) for t in test)/len(test)
corrected=sum(abs(D(t['correctedErrorC'])) for t in test)/len(test)
assert raw==4 and corrected==D('.1')
# One reading admits distinct physical explanations without an independent reference.
assert D(20)+2==D(22)+0
shifted=[(D(t['readingC'])+D('.55')-a)/b-D(t['referenceC']) for t in test]
shiftedMAE=sum(abs(e) for e in shifted)/len(shifted)
assert shifted==[D('.6'),D('.4'),D('.6'),D('.4')] and shiftedMAE==D('.5')
assert sum((x-xbar)**2 for x,y in train)==2000
assert sum((x-xbar)*(y-ybar) for x,y in train)==2200
r=dict(shiftedErrorsC=[str(e) for e in shifted],shiftedMAEC=str(shiftedMAE),readingSensitivityForPointTwo=str(D('.2')/b),python=sys.version.split()[0],seed=None,scope='Synthetic affine calibration arithmetic, no hardware, no AI training, no certification or model accuracy benchmark',training=[dict(referenceC=str(x),readingC=str(y)) for x,y in train],interceptC=str(a),slope=str(b),test=test,rawMAEC=str(raw),correctedMAEC=str(corrected),identifiabilityCounterexample=dict(observedC='22',worldA=dict(trueC='20',offsetC='2'),worldB=dict(trueC='22',offsetC='0')))
Path(__file__).with_name('results.json').write_text(json.dumps(r,indent=2),encoding='utf8')
print(json.dumps(r))

代码、数据与说明 · JSON. 教学计算使用 Python 3.14.0,图使用 Matplotlib 3.11.2。分析由 AI 辅助,不声称经过同行评审或人工审核。原创 ImageGen 封面仅作示意,不记录 EL-AI 人员、场所或实际安装。来源查阅于 2026 年 10 月 30 日。