一条反馈可以同时属于“物流”“包装”“售后”,这类任务不能强迫每条样本只选一个类别。用 MultiOutputClassifier,可以把每个标签看成一个独立目标,为它各训练一个分类器;输出仍是“样本行 × 标签列”的矩阵。下面用人工生成的三标签教学数据,演示训练、列名对应、逐标签概率和验证口径。
先区分多分类与多标签
| 任务 | 一条样本的答案 | 常见标签形状 |
|---|---|---|
| 多分类 | 几个互斥类别中的一个 | (n_samples,) |
| 本文的多标签 | 多个标签各自为 0 或 1 | (n_samples, n_labels) |
例如按固定顺序 [物流, 包装, 售后],答案 [1, 1, 0] 表示前两个标签成立、第三个不成立,不表示一个编号为 110 的类别。官方多分类与多输出指南说明这种指示矩阵表示方式;MultiOutputClassifier API说明它会为每个目标拟合一个分类器。该方法分别学习各列,不能自动建模标签之间的关系。

示例于 2026 年 10 月 1 日在 Windows、Python 3.11.15、scikit-learn 1.7.2、NumPy 2.4.6、SciPy 1.17.1 运行。标签 A/B/C 没有业务含义,500 条特征由 make_multilabel_classification 人工生成。它只是检验接口和输出对应关系的实验,未在真实反馈上验证。
python -m venv .venv
.venv\Scripts\python -m pip install "scikit-learn==1.7.2" "numpy==2.4.6" "scipy==1.17.1"
固定标签列顺序后训练
将代码保存为 multilabel_model.py,运行 .venv\Scripts\python multilabel_model.py。官方合成多标签数据接口定义 n_classes、n_labels 和 allow_unlabeled;此处 n_classes=3,n_labels=2 是生成过程的平均标签数参数,不要求每条样本恰好两个标签。
import numpy as np
from sklearn.datasets import make_multilabel_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, hamming_loss
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
label_names = ["标签A", "标签B", "标签C"]
X, Y = make_multilabel_classification(
n_samples=500, n_features=12, n_classes=3, n_labels=2,
allow_unlabeled=False, random_state=7)
X_train, X_test, Y_train, Y_test = train_test_split(
X, Y, test_size=0.3, random_state=7)
if Y.ndim != 2 or Y.shape[1] != len(label_names):
raise ValueError("标签矩阵列数与标签名不一致")
if not set(np.unique(Y)).issubset({0, 1}):
raise ValueError("本例只接受0/1标签指示矩阵")
model = Pipeline([
("scale", StandardScaler()),
("multi", MultiOutputClassifier(LogisticRegression(max_iter=500),
n_jobs=1))
])
model.fit(X_train, Y_train)
prediction = model.predict(X_test)
assert prediction.shape == Y_test.shape
multi = model.named_steps["multi"]
print("输入形状:", X_train.shape, "标签形状:", Y_train.shape)
print("独立目标分类器数量:", len(multi.estimators_))
print("整行标签全对的比例:", f"{accuracy_score(Y_test, prediction):.4f}")
print("逐标签错误比例:", f"{hamming_loss(Y_test, prediction):.4f}")
for i, name in enumerate(label_names):
print(name, "准确率:", f"{accuracy_score(Y_test[:, i], prediction[:, i]):.4f}")
for row in range(3):
print("样本", row,
"真实", dict(zip(label_names, Y_test[row].tolist())),
"预测", dict(zip(label_names, prediction[row].tolist())))
probabilities = model.predict_proba(X_test[:3])
for name, estimator, proba in zip(label_names, multi.estimators_, probabilities):
assert proba.shape == (3, len(estimator.classes_))
pos_column = np.flatnonzero(estimator.classes_ == 1)
if len(pos_column) != 1:
raise ValueError(f"{name}没有明确的正类1,请检查训练标签")
print(name, "正类1概率:", np.round(proba[:, pos_column[0]], 4).tolist())
先划分训练与测试,标准化只在训练集学习。三个目标分类器使用同一份特征,但每个只学习 Y 的一列。n_jobs=1 方便在普通电脑核对;并行不保证小模型更快,多个目标也会增加训练和模型保存成本。
验证矩阵、标签含义与概率列
实际运行得到:
输入形状: (350, 12) 标签形状: (350, 3)
独立目标分类器数量: 3
整行标签全对的比例: 0.5867
逐标签错误比例: 0.1556
标签A 准确率: 0.7933
标签B 准确率: 0.8800
标签C 准确率: 0.8600
样本 0 真实 {'标签A': 1, '标签B': 1, '标签C': 1} 预测 {'标签A': 1, '标签B': 1, '标签C': 1}
样本 1 真实 {'标签A': 0, '标签B': 1, '标签C': 1} 预测 {'标签A': 0, '标签B': 1, '标签C': 1}
样本 2 真实 {'标签A': 0, '标签B': 1, '标签C': 0} 预测 {'标签A': 0, '标签B': 1, '标签C': 0}
标签A 正类1概率: [0.5322, 0.0374, 0.0241]
标签B 正类1概率: [0.6341, 0.9868, 1.0]
标签C 正类1概率: [0.9647, 0.9149, 0.2046]
训练 X 是 350 行、12 列,Y 是 350 行、3 列,目标分类器数量为 3。预测与测试标签必须同形状;每列名称始终取自固定的 label_names,不按每次出现频率重新排序。打印的前三条样本能逐项核对真实和预测的 A/B/C 值。
predict_proba() 对多个目标返回数组列表,并非一个共享三类别概率矩阵。第 j 个数组属于第 j 个目标,概率列按该目标分类器的 classes_ 排列。代码显式寻找 classes_ 中的正类 1,再读对应列,避免假定所有目标的第 2 列永远存在。若某标签在训练中只有单一取值,逻辑回归会训练失败;先检查每列的 0/1 分布和划分,不能靠补一条虚构答案修复真实数据。
为什么逐标签分数较高,整行全对比例仍可能不高
官方模型评估指南定义多标签 accuracy_score 为 subset accuracy:一条样本的所有标签都对,才记作整行正确。hamming_loss 则是全部标签单元格中出错的比例。一次只错一个标签,也会让整行准确率扣一整条样本。
本次整行全对比例为 0.5867,逐标签错误比例为 0.1556,各标签准确率为 0.7933、0.8800、0.8600。它们不是相互冲突的数字,也不是分类器在真实反馈上的成绩。你可按业务选择同时报告整行完整正确、逐标签错误,以及重要标签的漏判/误判,不只看一个总体数值。
换成真实数据前的检查
- 把人工标签转成固定 0/1 矩阵,保留标签字典版本;缺失标签代表“尚未判断”,不能直接当成否定 0。
- 有同一用户、同一文档来源或时间顺序时,采用适合的分组或时间划分;此教学随机划分不保证真实业务无泄漏。
- MultiOutputClassifier 只包裹分类器,不负责把中文文本变成特征。文本任务应另加入向量化,并确保只在训练部分学习词表。
- 三个独立模型可能给出不符合业务约束的组合,例如互斥标签同时为 1。保留原预测,再按经过确认的约束标记待复核,不悄悄改成看起来合理的结果。
下一步可先取一批有完整人工标签的真实样本,核对每列含义和样本编号,训练后导出逐行预测矩阵,让业务人员查看错误组合。正式任务还需处理标签不均衡、阈值和误判代价,本教程未替具体业务决定这些规则。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30812.html