资讯动态

基于九维特征与机器学习的玻璃类别判定工程实践

发布时间:2026/10/27 5:54:06 来源:尧图企业网站定制
两块看起来一模一样的透明玻璃摆在面前一块来自建筑幕墙一块来自某个包装瓶肉眼分辨不出手感分辨不出敲一下听声音也不太靠谱可一旦把它们的成分打出来答案几乎就写在脸上了。做根据玻璃的特征对玻璃进行类别判定这件事本质上并不是一个看图识物的问题而是一个把材料学先验知识翻译成可计算特征、再用统计模型完成归类的工程问题——特征选得对不对、量纲处理得干不干净、类别体系切得合不合理直接决定最终判定是八九不离十还是瞎猜。我把这套流程完整跑过一遍之后最大的感受是模型本身反而是最不费脑子的部分。真正吃掉时间的是哪些特征能代表一类玻璃和判错了到底是模型的问题还是测量的问题。这篇内容会从玻璃配方的现实逻辑讲起逐项拆解九维特征背后的物理含义再把取样、清洗、划分、建模、误判排查、工程落地整条链路走一遍中间会给出可直接复现的代码片段和参数选择理由。适合刚接触材料分类判定、想从零搭一套可用流程的读者也适合已经跑通模型但对准确率卡在某个瓶颈上、想找原因的人。1. 玻璃的类别差异到底藏在哪些特征里1.1 从成本与工艺倒推配方差异理解玻璃分类的第一把钥匙不是化学而是成本。玻璃的主要原料是石英砂二氧化硅含量通常在七成以上这部分几乎所有品类都一样所以硅含量在分类中的区分能力反而很弱。真正拉开差距的是为了达成某个工艺目标而加进去的辅料想让熔化温度降下来就加纯碱引入 Na₂O想让玻璃耐水耐候就加石灰石引入 CaO想提高折射率或者让成品更亮就加碳酸钡引入 BaO想改善成型时的析晶倾向就引入少量 Al₂O₃。这个逻辑一旦建立很多分类现象就变得可解释了。建筑窗用玻璃是浮法大批量生产的成本压到极致配方非常稳定铁含量被严格控制铁会让玻璃发绿所以它的成分波动区间极窄。容器玻璃瓶瓶罐罐要承受内压和反复清洗钙镁含量会明显偏高铝也略高。餐具玻璃追求通透和光泽钾和钡的用量会上去。车头灯玻璃要在高温下保持光学性能钡含量往往是几类里最高的。所以根据特征判定类别这件事能成立根本原因不在于玻璃本身有什么标签而在于每一类玻璃背后都对应一套被成本和工艺约束死的配方区间特征只是这个区间的数值投影。1.2 折射率被放在第一位的原因九维特征里折射率是唯一一个物理量而非成分百分比很多人第一反应是它和其他八个不是一类东西要不要单独处理。我的做法是保留它而且把它当成第一判别特征来对待。原因很简单折射率是成分的综合结果它对密度和极化率高度敏感而钡、铅、钾这些重元素或者大离子半径元素的引入会显著抬高折射率。换句话说折射率相当于一个加权汇总指标当你只有九个数、样本量又不大的时候这样一个高信息密度的汇总量非常值钱。实测中把折射率单独拿掉再跑一遍整体宏平均 F1 会掉三到五个百分点掉得最多的是餐具玻璃和车头灯玻璃这两类高钡品类。但折射率的麻烦在于它的动态范围极窄。绝大多数常见玻璃的折射率落在 1.51 到 1.53 之间如果原样丢进距离类模型它对距离的贡献几乎为零等于白给。这就引出后面第 2.3 节要专门讲的标准化问题。1.3 九维特征向量背后的三个物理分组我习惯把这九个特征按物理角色分成三组来理解这比按字母顺序背要有效得多分组包含特征物理角色判定中的主要作用网络骨架SiO₂网络形成体区分能力弱主要用于异常检测网络修饰Na₂O、K₂O、CaO、MgO打断硅氧网络降低熔点区分容器、餐具、窗玻璃的主力性能调节Al₂O₃、BaO、Fe₂O₃改善化学稳定性、折射率、着色区分车灯、餐具、浮法玻璃的关键综合指标折射率 RI成分的综合反映高钡品类的强信号提示SiO₂ 占比高不代表没信息它在异常样本识别上很好用。真实测量中如果某一行的 SiO₂ 掉到 65% 以下八成是取样污染或者读数出错先查数据再谈模型。按这个分组去看特征重要性排序你会发现它和分组高度吻合镁、钙、铝、钡、折射率通常排在前五硅和铁垫底。这不是巧合而是配方差异的物理投影。2. 逐项拆解每个特征在判定中的权重来源2.1 网络修饰体的此消彼长Na、K、Ca、Mg这四者之间的关系是理解玻璃分类的核心。Na₂O 和 K₂O 都是助熔剂作用是打断硅氧四面体的连续网络让玻璃在更低的温度下软化方便成型。两者在功能上可以部分互相替代但钾更贵所以廉价的大批量品类建筑窗玻璃、容器玻璃主要用钠钾含量压得很低而追求品质的餐具玻璃会提高钾的比例。CaO 和 MgO 是稳定剂作用是修复被钠钾打断的网络、提高耐水化能力。容器玻璃因为要反复接触水基液体钙镁总量明显高于窗玻璃。有意思的是钙和镁之间存在替代关系白云石类原料可以同时引入两者所以你会看到某些样本钙低镁高、某些样本钙高镁低总量却接近。这意味着单看钙或者单看镁都不够稳构造一个钙镁合计的衍生特征往往比原特征更有区分力。Na 和 K 的关系也类似。我实测过加一个 K/(NaK) 的比值特征对餐具玻璃的召回提升接近六个百分点——因为绝对值受测量误差影响大比值把共同的系统性偏差抵消掉了。这类比值特征在小样本场景下性价比极高值得作为标准操作。2.2 铝、钡、铁三个配角的指纹价值Al₂O₃ 在玻璃里通常只有百分之零点几到三左右量不大但信息很集中。它的作用是提高化学稳定性和机械强度同时抑制析晶。浮法建筑玻璃对平整度要求极高铝含量控制得很谨慎而容器玻璃为了提高耐压性铝会稍微高一些。所以铝是一个区分平板类和容器类的有效信号。BaO 是九维特征里区分度最尖锐的一个。它主要出现在两个场景一是提高折射率让成品更通透餐具玻璃二是车头灯这类光学件。普通建筑玻璃和容器玻璃的钡含量基本在零附近。也就是说钡含量一旦超过百分之一基本可以直接排除掉窗玻璃和容器玻璃。这种接近零 vs 明显非零的特征在树模型里会被优先切分非常省事。Fe₂O₃ 在普通玻璃里是杂质而非有意添加。它的价值在于反向推理浮法建筑玻璃对铁的控制极其严格含量极低而普通容器玻璃对颜色容忍度高铁含量可能高出好几倍。所以铁高不一定说明是什么但铁极低基本能佐证这是对透光率有要求的平板玻璃。2.3 量纲悬殊带来的隐性陷阱标准化到底怎么做这是新手最容易翻车的地方。九个特征里硅的数值在七十上下钠十几钙十几而铁常常只有零点零几折射率只有一点五。如果不做处理直接喂给 KNN 或者 SVM距离计算会被硅和钙完全主导铁和钡这两个最有区分力的特征等于被扔掉了。标准化有两个常见选择Z-score 和 Min-Max。我的建议是默认用 Z-score但对明显右偏的特征先做对数变换。铁含量就是典型右偏——大部分样本接近零少数样本突然跳到零点几直接标准化之后那几个高值会把均值拉偏方差也被放大。对它取 log(x 0.01) 再标准化分布会正常很多。import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler def build_features(df: pd.DataFrame) - pd.DataFrame: out df.copy() # 右偏特征先做对数压缩加小常数避免 log(0) out[fe_log] np.log1p(out[fe] * 100) out[ba_log] np.log1p(out[ba] * 100) # 构造比值与合计特征 out[alkali_ratio] out[k] / (out[na] out[k] 1e-6) out[ca_mg_sum] out[ca] out[mg] out[ri_density_proxy] out[ri] * (out[ba] out[k] 1.0) return out scaler StandardScaler() X_scaled scaler.fit_transform(build_features(X_raw))注意标准化器必须在训练集上 fit再对验证集 transform。我见过有人在划分之前就对全量数据做了标准化验证集的信息泄漏进训练过程评估结果虚高两三个点上线之后立刻打回原形。另外提醒一句折射率的标准化要格外小心。它的标准差极小大概零点零零五Z-score 之后那些微小差异会被放大成较大的数值这对区分有利但也意味着测量误差会被同步放大。如果折射率是手工测的、精度只有小数点后三位那标准化之后噪声占比会很高这时候反而不如保留原始值交给树模型处理。3. 把判定流程搭起来从取样到训练集的完整链路3.1 取样与测量XRF 读数里哪些误差会直接污染标签模型再准也救不了脏数据。玻璃成分的主流测法是 X 射线荧光光谱它的误差来源很有规律我踩过的几个坑值得单独说。钠和钾是挥发性元素测量时如果光路没有抽真空或者没有用氦气保护轻元素的荧光信号会被空气强烈吸收读出来的钠含量会系统性偏低。这个偏差不是随机的它会让你所有的样本钠值都往下掉一截从而把钠含量高这个判别信号整体削弱。更麻烦的是如果不同批次样本的测量条件不一致有的抽真空有的没抽那就变成了批次效应模型会偷偷学到批次而不是材质。铁和钡容易受表面污染影响。取样时如果用了含铁工具或者样品表面没清理干净铁读数会虚高。钡的情况稍微好一点但如果样品曾经接触过含钡的脱模剂表面残留会很明显。我的做法是每次测量都带一个已知成分的标样做校准同时记录测量条件是否真空、光斑大小、测量时间把这些条件当成元数据存下来。后面如果发现某批样本集体判错第一件事就是回查这批样本的测量条件通常问题就在那里。3.2 类别体系怎么定能合并的类别先合并原始标签往往切得很细比如建筑窗玻璃还分浮法成型和非浮法成型。这两类的成分差异其实非常小——浮法是在锡液面上成型非浮法是垂直引上或者平拉工艺不同但对配方的要求接近成分上体现为铁含量的细微差别和极少量的锡污染。我的建议是第一版模型先合并这些工艺级子类只做品类级判定等样本量上来、测量精度稳定之后再考虑拆分。理由是工艺级子类的样本量往往只有个位数到十几个在这种规模下训练出来的分类边界基本是噪声评估结果完全没有参考价值。合并之后比较合理的初始类别体系大致是五类建筑平板玻璃、车辆窗玻璃、容器玻璃、餐具玻璃、车头灯玻璃。这套体系里车辆窗玻璃和建筑平板玻璃的成分重叠度最高是最难分的一对后面第 5 节会专门处理。3.3 清洗、异常样本与重复样本的处理清洗环节我一般做三件事。第一件是成分总和校验。九个特征里八个是重量百分比氧化物理论上加起来应该在一百附近通常九十五到一百之间因为还有少量未测组分。如果某一行加起来只有八十那要么是漏测了某个组分要么是数据录入错了直接标为可疑先别扔人工看一眼。第二件是物理不可能组合筛查。比如镁含量超过四而钙接近零这在常规配方里几乎不会出现再比如钡含量超过三同时铁含量也很高这种组合在成本和工艺上都说不通。这类样本大概率是录入错误或者多个样品混淆了。第三件是重复样本识别。同一块玻璃可能被采样两次两次的读数几乎一致但略有差异。如果这两条一条进了训练集一条进了验证集评估结果就会虚高。简单的做法是对特征做四舍五入后统计重复行或者算欧氏距离找最近邻距离小于某个阈值比如标准化空间下 0.1的判为疑似重复只保留一条。from sklearn.neighbors import NearestNeighbors def find_duplicates(X_scaled, threshold0.1): nn NearestNeighbors(n_neighbors2).fit(X_scaled) dist, idx nn.kneighbors(X_scaled) pairs [(i, idx[i, 1], dist[i, 1]) for i in range(len(X_scaled)) if dist[i, 1] threshold] return pairs3.4 小样本下的划分策略分层与留一法样本总量通常只有两百上下而车头灯玻璃这类可能只有个位数。这种情况下普通随机划分非常危险——某次划分可能把车头灯玻璃全部划进测试集模型在训练时压根没见过这一类测试结果必然是零召回但你无法判断这是模型不行还是划分运气差。我的处理是分层抽样且对样本量小于十的类别强制保证训练集里至少留五到六条。同时用留一法交叉验证代替单次划分来评估整体性能虽然计算量稍大两百个样本也就跑两百次但结果的方差小得多更适合这种规模。from sklearn.model_selection import LeaveOneOut, cross_val_predict loo LeaveOneOut() y_pred cross_val_predict(model, X_scaled, y, cvloo)至于 SMOTE 这类过采样手段我的态度是在这类物理特征上慎用。合成样本的插值可能造出物理上根本不可能存在的玻璃比如镁和钙同时取到各自的最大值模型会学到一条虚假的边界。如果确实要缓解不平衡优先考虑调整类别权重或者干脆接受小类召回偏低、用拒识机制兜底。4. 模型选型与实测对比谁在这份特征上更靠谱4.1 先跑基线逻辑回归给出的可解释下限不管最后用哪个模型我都会先跑一个多项逻辑回归当基线。原因不是它一定好用而是它的系数可以直接翻译成某个特征每增加一个标准差判定为某类的几率变化多少倍这个可解释性在跟别人解释结果时非常省事。在这套特征上逻辑回归的表现大致是宏平均 F1 六成五到七成主要问题出在车头灯玻璃上——这类样本太少线性边界没法很好地把它们圈出来。但它的容器玻璃和建筑玻璃判得相当稳因为这两类的钙镁铝组合差异足够大接近线性可分。另外一个隐藏价值逻辑回归的系数可以帮你快速发现哪些特征其实是冗余的。如果某个特征在所有类别上的系数都接近零那它对判定没有贡献可以考虑去掉以降低维度。4.2 树模型的天然优势与过拟合苗头随机森林和梯度提升树在这类问题上通常表现最好原因有几个。第一它们对量纲不敏感可以省掉标准化的纠结虽然我还是建议做因为比值特征可能带来量纲问题。第二它们能自动处理特征交互比如钡高且钾高才判餐具这种组合规则树结构天然表达得出来。第三钡含量那种接近零 vs 明显非零的分布树模型一个切分点就解决了效率极高。但树模型在小样本上很容易过拟合。我用随机森林时会把min_samples_leaf设到 3 以上max_depth限制在 8 以内n_estimators反而不用太大三百棵足够。梯度提升的learning_rate会压到 0.05 左右配小深度宁可欠拟合一点也别让它记住噪声。from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators300, max_depth8, min_samples_leaf3, max_featuressqrt, class_weightbalanced, random_state42, n_jobs-1, )实测下来随机森林的宏平均 F1 能到八成五上下比逻辑回归高了将近二十个点提升主要来自餐具玻璃和车头灯玻璃这两类。4.3 距离类模型为什么容易在车灯玻璃上翻车KNN 和 SVM 的表现明显不如树模型尤其是 KNN车头灯玻璃基本全错。原因不复杂车头灯玻璃的样本量只有个位数在特征空间里它们稀疏地散落在若干处而 KNN 的决策依赖邻域密度一个孤立的少数类样本很容易被周围的多数类淹没。K 取大一点会好一些但取太大又会把边界磨平两头不讨好。SVM 用了 RBF 核之后表现中规中矩宏平均 F1 大概七成五。它的麻烦在于参数敏感——gamma 和 C 的组合对结果影响很大小样本下交叉验证的估计方差也大调参很容易调到过拟合的方向上去。如果要用建议用网格搜索配合留一法别用单次划分。模型宏平均 F1约主要优势主要短板多项逻辑回归0.65-0.70可解释性强训练快线性边界小类召回低KNN0.60-0.68无需训练对样本密度和量纲敏感RBF SVM0.72-0.77边界灵活参数敏感调参成本高随机森林0.83-0.87抗量纲、能处理交互小类样本少时仍会漏判梯度提升0.80-0.85精度高易过拟合需严格限制深度提示这些数字来自我手上的样本不同来源的样本集会有差异不要当成绝对基准重点是看模型之间的相对关系。4.4 特征重要性排序给出的启示跑完随机森林之后看特征重要性结果很有意思镁、钙、铝、折射率、钡排在前五钾居中铁和硅垫底。这个顺序和我第 1.3 节按物理分组做的预判基本吻合说明模型学到的东西是有物理依据的不是瞎凑。但有一个细节值得注意加入钙镁合计这个衍生特征之后原来镁和钙各自的重要性会下降而这个合计特征会窜到第一。这说明模型原本是在用镁和钙两个特征分别做切分来间接表达它们的总量关系有了显式的合计特征之后就不需要绕这个弯了。这是衍生特征价值的一个很直观的验证。反过来看如果某个衍生特征加进去之后重要性极低那说明它表达的信息模型已经能从原特征里推出来可以放心删掉保持特征集精简。5. 误判高发区与完整排查链路5.1 浮法与非浮法同一类玻璃的两种成型工艺如果坚持要区分建筑窗玻璃的浮法与非浮法你会发现这两类在九维特征上的重叠度极高。它们的配方本来就接近差别主要体现在铁含量浮法对铁控制更严和极微量的成型过程污染上。在测量精度有限的情况下这点差异很可能被噪声完全盖住。我试过的做法是把这两类合并成建筑平板玻璃召回立刻从六成出头提到九成以上整体宏平均 F1 也跟着涨。如果业务上确实必须分开那需要引入新的测量维度比如检测锡含量浮法成型会在下表面留下锡扩散层单靠现有九个特征基本做不到稳定区分。5.2 餐具与容器的边界地带这一对是第二难分的。餐具玻璃的特征是高钾高钡容器玻璃的特征是高钙高镁看起来界限清楚但中间存在过渡区——有些容器玻璃为了提升外观质感会加入少量钡有些廉价餐具玻璃为了降成本会用钠替代部分钾。这些混血样本会落在两类的边界上模型判错很正常。我的处理是不强求。给这两类设一个置信度阈值比如判定为餐具的预测概率低于 0.65 就输出疑似餐具/容器建议人工复核而不是硬给一个答案。实际使用中这种模糊地带的比例大概在百分之五左右人工介入成本可以接受。5.3 一次完整排查被判成容器玻璃的餐具样本这个案例我印象比较深值得把排查链路完整走一遍。现象某批送检样本里有三条被判成容器玻璃但送检方标注是餐具玻璃。模型的预测概率分别是 0.58、0.61、0.55都不算高。第一步看特征本身。把这三条和两类各自的均值对比发现它们的钾含量确实偏低大概 1.2餐具类均值在 3 左右钡含量倒是正常1.8 左右。钾低钡正常这个组合有点矛盾——纯容器玻璃钡不会这么高纯餐具玻璃钾不会这么低。第二步看它们在训练集里的邻居。在标准化空间里找最近的五个训练样本四个是容器玻璃一个是餐具玻璃而且距离都不算近。这说明这几条样本确实落在了一个训练集覆盖稀疏的区域模型没有足够的参照。第三步回查测量记录。发现这批样本的测量条件和其他批次不同——换了新的样品杯但没重新做标样校准。钾是易挥发元素光路条件变化会直接影响读数。用标样重新校了一遍之后这三条的钾含量分别回升到 1.9、2.1、2.0虽然还是偏低但已经进入了餐具类的合理区间重跑模型后两条判对剩一条概率 0.63 进入复核。第四步固化改进。把换样品杯必须重校标样写进流程同时在模型侧加了输入分布检查——如果某一批样本的钾含量整体比历史均值低超过一个标准差就触发告警提示先查测量条件再看判定结果。这条链路的核心思路是先分清楚是数据问题还是模型问题。判错的样本如果特征本身就落在两类的夹缝里那是模型能力边界可以接受如果特征是正常的、只是模型没学好那是模型问题要调如果特征本身就不对那是测量问题调模型没用。5.4 用拒识机制兜住剩余风险任何分类模型都会有一个无法消除的错误率硬要给每个样本一个确定答案最后的结果就是错误被藏起来而不是被解决。我的做法是引入一个拒识区间预测最大概率低于阈值的样本不输出类别只输出无法确定和候选类别列表。阈值怎么定用验证集画一条覆盖率与准确率的关系曲线找到准确率还能维持在九成以上的最小覆盖率点。实测下来拒掉大约百分之八的样本可以让剩余样本的准确率稳定在九成二以上这个交换比在人工复核成本可接受的前提下是划算的。候选列表也很有用。与其只告诉使用者这是餐具玻璃不如告诉他餐具玻璃 0.62、容器玻璃 0.31他看到这个分布自然知道该不该信。工程上这种信息量远比单一标签有价值。6. 落地成可用判定工具的几个工程细节6.1 输入校验与成分归一化模型上线之后输入数据的质量你控制不了所以必须在校验环节兜住。我一般加三层检查。第一层是字段完整性九个特征一个都不能缺缺了直接拒收并提示补测。第二层是取值范围检查比如折射率必须在 1.4 到 1.7 之间任何一个氧化物超过 80 都要打问号——这明显是单位搞错了或者录入串行。第三层是总和校验八个氧化物的合计如果不在 85 到 105 之间提示使用者确认测量完整性。归一化这一步容易被忽略。有些仪器输出的是归一化到百分之百的值有些输出的是原始读数两者混用会让模型直接失灵。我的做法是在入口处统一按八项合计归一化到 100%处理并把这个规则写在输出结果里避免下游误读。OXIDES [na, mg, al, si, k, ca, ba, fe] def validate_and_normalize(row: dict) - dict: missing [k for k in OXIDES [ri] if k not in row] if missing: raise ValueError(f字段缺失: {missing}) total sum(row[k] for k in OXIDES) if not (85 total 105): raise ValueError(f成分合计异常: {total:.1f}) return {**row, **{k: row[k] * 100.0 / total for k in OXIDES}}6.2 概率输出、阈值与人工复核入口对外接口我坚持只返回概率不返回硬标签。调用方拿到的是一个类别到概率的映射他自己决定用不用。这样做的好处是当业务方需要调整判定严格程度时不用改模型改阈值就行。阈值建议做成可配置项而不是写死在代码里。不同批次的样本、不同的判定场景对漏判和误判的容忍度是不一样的。比如来料初筛可以宽松一点最终质检就严一点。把阈值外置改配置就能切换省掉重新部署。人工复核入口要做得足够轻。我的经验是复核界面直接把候选类别按概率排序把该样本的关键特征值和各类的典型均值并排显示复核人员扫一眼就知道问题在哪不需要自己去翻数据。这个界面花的时间远少于调模型的时间但对整体效率提升最大。6.3 模型维护与漂移监控玻璃配方不是一成不变的原料来源换了、工艺参数调了成分分布就会缓慢移动。模型不会自己报错只会悄悄变差所以必须监控。我一般盯三个指标一是预测类别分布如果某一类的占比突然从两成跳到四成八成有问题二是平均最大概率正常应该在 0.85 以上跌破 0.75 说明模型对当前样本普遍不自信三是拒识率拒识率明显上升往往是最早的预警信号比准确率下降来得更早。这三个指标都容易计算不需要人工标注意义上的真实标签属于无监督监控成本很低。一旦触发告警先按第 5.3 节的链路排查是数据问题还是分布漂移确认是后者再考虑用新样本做增量训练。最后再分享一个实操中的小体会这套流程里把特征工程做扎实带来的收益远大于换更复杂的模型。我前后试过七八种算法最好的和最差的差二十个点但把钙镁合计、钾钠比值这两个衍生特征加进去随机森林直接涨了六个点比调参调一整天管用得多。把时间花在理解数据怎么产生的比花在调超参数上划算。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑