本文目录导读:

我来为你详细介绍如何使用Scikit-learn进行多输出(多标签)分类的完整案例。
什么是多输出分类?
多输出分类是指模型需要同时预测多个目标变量。
- 预测一篇文章属于多个主题类别
- 预测一张图片中是否包含多种物体
- 预测一个人具备哪些技能
基础示例:简单多输出分类
import numpy as np
from sklearn.datasets import make_multilabel_classification
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
# 1. 生成示例数据
X, y = make_multilabel_classification(
n_samples=1000, # 样本数量
n_features=10, # 特征数量
n_classes=5, # 类别数量(输出维度)
n_labels=2, # 每个样本的平均标签数
random_state=42
)
print(f"特征矩阵形状: {X.shape}")
print(f"标签矩阵形状: {y.shape}")
print(f"前3个样本的标签:\n{y[:3]}")
# 2. 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 3. 创建多输出分类器
base_classifier = RandomForestClassifier(n_estimators=100, random_state=42)
multi_output_classifier = MultiOutputClassifier(base_classifier)
# 4. 训练模型
multi_output_classifier.fit(X_train, y_train)
# 5. 预测
y_pred = multi_output_classifier.predict(X_test)
# 6. 评估
print("\n每类的准确率:")
for i in range(y.shape[1]):
accuracy = accuracy_score(y_test[:, i], y_pred[:, i])
print(f"类别 {i}: {accuracy:.3f}")
# 总体准确率(所有类别都正确)
total_accuracy = accuracy_score(y_test, y_pred)
print(f"\n总体准确率: {total_accuracy:.3f}")
真实场景案例:文本主题分类
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.metrics import hamming_loss, f1_score
# 创建示例数据:新闻文本及其多标签
data = {
'text': [
"Apple releases new iPhone with AI features",
"Climate change affects global agriculture production",
"New AI model beats chess grandmaster",
"Stock market reaches new heights in technology sector",
"Renewable energy helps reduce carbon emissions",
"Machine learning improves healthcare diagnostics",
"Weather forecast predicts heavy rainfall",
"International trade agreement signed between countries"
],
'categories': [
['technology', 'business'],
['environment', 'science'],
['technology', 'science'],
['business', 'technology'],
['environment', 'science'],
['technology', 'health'],
['environment', 'science'],
['business', 'politics']
]
}
df = pd.DataFrame(data)
# 1. 文本特征提取
vectorizer = TfidfVectorizer(max_features=100, stop_words='english')
X = vectorizer.fit_transform(df['text'])
# 2. 标签编码
mlb = MultiLabelBinarizer()
y = mlb.fit_transform(df['categories'])
print("所有类别:", mlb.classes_)
print(f"X形状: {X.shape}")
print(f"y形状: {y.shape}")
# 3. 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
# 4. 创建多输出分类模型
base_estimator = LogisticRegression(max_iter=1000, random_state=42)
multi_label_model = MultiOutputClassifier(base_estimator)
# 5. 训练模型
multi_label_model.fit(X_train, y_train)
# 6. 预测
y_pred = multi_label_model.predict(X_test)
# 7. 评估模型
print("\n模型评估结果:")
print(f"Hamming Loss: {hamming_loss(y_test, y_pred):.3f}")
# 计算每个类别的F1分数
print("\n各类别F1分数:")
for i, label in enumerate(mlb.classes_):
f1 = f1_score(y_test[:, i], y_pred[:, i], average='binary')
print(f"{label}: {f1:.3f}")
# 示例预测
new_texts = ["AI and technology advances in healthcare",
"Environmental impacts of renewable energy"]
X_new = vectorizer.transform(new_texts)
y_new_pred = multi_label_model.predict(X_new)
print("\n预测结果:")
for text, pred in zip(new_texts, y_new_pred):
predicted_categories = mlb.inverse_transform([pred])[0]
print(f"文本: {text}")
print(f"预测类别: {predicted_categories}\n")
高级案例:图像多标签分类
import numpy as np
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.multioutput import ClassifierChain
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, jaccard_score
# 模拟图像多标签分类
# 使用digits数据集,每个数字对应多个特征标签
# 加载数据集
digits = load_digits()
X = digits.data
print(f"原始特征形状: {X.shape}")
# 创建多标签:是否为偶数、是否大于5、是否质数
def create_multilabels(y):
"""创建多标签目标"""
n_samples = len(y)
labels = np.zeros((n_samples, 3), dtype=int)
# 标签1: 是否为偶数
labels[:, 0] = (y % 2 == 0).astype(int)
# 标签2: 是否大于5
labels[:, 1] = (y > 5).astype(int)
# 标签3: 是否为质数
primes = [2, 3, 5, 7]
labels[:, 2] = np.isin(y, primes).astype(int)
return labels
y = create_multilabels(digits.target)
print(f"标签形状: {y.shape}")
print(f"标签说明: [是否偶数, 是否>5, 是否质数]")
print(f"前5个样本标签:\n{y[:5]}")
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 方法1: 使用ClassifierChain
print("\n=== ClassifierChain 方法 ===")
base_clf = LogisticRegression(max_iter=1000, random_state=42)
chain = ClassifierChain(base_clf, order='random', random_state=42)
chain.fit(X_train, y_train)
y_pred_chain = chain.predict(X_test)
print(f"Accuracy Score: {accuracy_score(y_test, y_pred_chain):.3f}")
print(f"Jaccard Score: {jaccard_score(y_test, y_pred_chain, average='samples'):.3f}")
# 方法2: 使用MultiOutputClassifier
print("\n=== MultiOutputClassifier 方法 ===")
from sklearn.multioutput import MultiOutputClassifier
multi_clf = MultiOutputClassifier(base_clf)
multi_clf.fit(X_train, y_train)
y_pred_multi = multi_clf.predict(X_test)
print(f"Accuracy Score: {accuracy_score(y_test, y_pred_multi):.3f}")
print(f"Jaccard Score: {jaccard_score(y_test, y_pred_multi, average='samples'):.3f}")
# 详细评估
print("\n详细类别评估:")
label_names = ['偶数', '大于5', '质数']
for i, name in enumerate(label_names):
acc = accuracy_score(y_test[:, i], y_pred_multi[:, i])
print(f"{name}: 准确率 {acc:.3f}")
实际应用:代码问题多标签分类
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, hamming_loss, f1_score
from sklearn.preprocessing import MultiLabelBinarizer
# 模拟代码问题数据
data = {
'code_snippet': [
"def add(a, b): return a + b",
"for i in range(10): print(i)",
"if x > 5: print('x is large')",
"import pandas as pd; df = pd.DataFrame()",
"class MyClass: def __init__(self): pass",
"while True: break",
"try: x = 1/0 except: pass",
"lambda x: x * 2"
],
'tags': [
['function', 'math'],
['loop', 'basic'],
['conditional', 'basic'],
['import', 'data-structure'],
['class', 'oop'],
['loop', 'control-flow'],
['error-handling', 'control-flow'],
['function', 'advanced']
]
}
df = pd.DataFrame(data)
# 特征提取
vectorizer = TfidfVectorizer(max_features=50)
X = vectorizer.fit_transform(df['code_snippet'])
# 标签编码
mlb = MultiLabelBinarizer()
y = mlb.fit_transform(df['tags'])
print("代码标签类别:", mlb.classes_)
print(f"特征矩阵形状: {X.shape}")
print(f"标签矩阵形状: {y.shape}")
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
# 创建模型
rf = RandomForestClassifier(n_estimators=50, random_state=42)
model = MultiOutputClassifier(rf)
# 训练
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 评估
print("\n模型评估:")
print(f"Hamming Loss: {hamming_loss(y_test, y_pred):.3f}")
# 预测新代码
new_codes = [
"def multiply(a, b): return a * b",
"if condition: do_something()"
]
X_new = vectorizer.transform(new_codes)
predictions = model.predict(X_new)
print("\n新代码预测结果:")
for code, pred in zip(new_codes, predictions):
tags = mlb.inverse_transform([pred])[0]
print(f"代码: {code}")
print(f"预测标签: {tags}\n")
关键技术点总结
常用模型选择:
- MultiOutputClassifier: 为每个输出构建独立的分类器
- ClassifierChain: 链式分类器,考虑输出之间的依赖关系
- Label Powerset: 将多标签问题转化为多类问题
评估指标:
- Hamming Loss: 衡量错误标签比例
- Accuracy: 所有标签完全正确才算对
- F1 Score: 为每个标签单独计算
- Jaccard Score: 衡量预测集合的重叠程度
最佳实践建议:
- 根据输出之间的关系选择合适的方法
- 对于大规模多标签问题,考虑使用Label Powerset
- 进行特征选择和降维,避免维度灾难
- 使用交叉验证选择最佳参数
希望这些案例能帮助你掌握Scikit-learn多输出分类的处理方法!