一、基础概念
1. 了解集成学习
集成学习是机器学习中的一个重要思想,简单来说就是"三个臭皮匠,顶个诸葛亮"。与其依赖单个模型做决策,不如让多个模型一起工作,通过投票、加权或其他方式综合它们的输出,最终得到更准确、更稳定的结果。
集成学习有三种经典形式:
- Bagging(并行训练多个模型后投票);
- Boosting(串行训练,每个模型修正前一个模型的错误);
- Stacking(分层训练,用元模型整合多个模型的输出)。
我们今天重点讨论的是 Stacking。

2. Stacking 堆叠集成
Stacking 的核心思想是分层决策。它不满足于让多个模型简单投票,而是引入一个"元模型"来学习如何最优地组合各个基模型的输出。
想象一下这个工作流程:
- 第一层的多个基模型各自对数据进行预测,它们的预测结果不是直接作为最终答案,而是作为新的特征输入给第二层的元模型。
- 元模型学习的是"在什么情况下应该更信任哪个基模型",从而做出更智能的融合决策。
**通俗的理解:**就像公司里的"多层评审制",先让几个基层员工(小模型)各自给出方案,再让部门经理(元模型)汇总所有人的方案做最终决策;
**这种设计的精妙之处在于:**不同的基模型可能擅长处理不同的数据模式,元模型的任务就是识别这些模式,动态调整各模型的权重,让每个模型在自己擅长的领域发挥最大价值。
**大模型角色:**把原本"部门经理"的角色换成"公司总监(大模型)",因为大模型有更强的理解、推理能力,能从多个小模型的结果中挖掘更精准的规律;
**核心目标:**单个小模型可能"看问题片面"(比如有的模型擅长识别正面情绪,有的擅长识别负面),但大模型整合所有小模型的结果后,能做出更全面、更准确的决策,实现"1+1>2"。
二、核心说明
1. Stacking 的基础逻辑
Stacking 本质是"两层模型架构":
- **第一层(基模型层):**多个不同类型的"弱模型",比如简单的机器学习模型、小尺寸预训练模型,各自对数据做预测,输出各自的结果;
- **第二层(元模型层):**接收第一层所有模型的预测结果,学习这些结果之间的规律,最终输出最终决策。

2. 基于大模型的"元模型"
小模型(比如 LR、SVM、小型 BERT)的优势是 "快、轻、针对性强",但缺点是 "泛化能力弱、容易漏判";
大模型(比如 qwen、Llama、混元)的优势是:
- 能理解数据的深层语义,比如情感分析中,能区分"反话"和"真话";
- 能整合多个小模型的结果,判断哪个小模型的结论更可靠;
- 对复杂场景的适配性更强,比如多分类文本任务。
3. Stacking 的数学逻辑
假设:
- 第一层有 3 个小模型,预测结果分别为:f₁(x)、f₂(x)、f₃(x);
- 原始文本特征为 x;
- 大模型的预测函数为:F(x, f₁(x), f₂(x), f₃(x))
- 最终目标是让 F(⋅) 的预测结果尽可能接近真实标签 y 。
两层学习过程:
- 第一层(基学习器):
- 小模型学习从 x 到 y 的映射:fₖ(x) ≈ y,k = 1,2,3
- 第二层(元学习器):
- 大模型学习从 [x, f₁(x), f₂(x), f₃(x)] 到 y 的映射:F(x, f₁(x), f₂(x), f₃(x)) ≈ y
核心总结:
- 第一层模型提供"预测特征",第二层模型学习如何最优组合这些特征,同时保留对原始文本的访问权,实现信息互补。
- 大模型通过学习"小模型结果的权重"来优化决策,比如:
- 小模型1对"美食类文本"判断准,大模型就给它更高的权重;
- 小模型2对"旅游类文本"判断准,大模型就针对性提升它的权重。

应用示例:
- 输入文本 x = "这部电影太棒了";真实标签 y = "正面"
- 第一层输出:
- f₁(x) = 0.8 (正面概率) # 朴素贝叶斯
- f₂(x) = 0.9 (正面概率) # SVM
- f₃(x) = 0.95 (正面概率) # 小型BERT
- 第二层输入:[x, 0.8, 0.9, 0.95]
- 第二层输出:F(⋅) = "正面"
4. 大模型的核心作用
- 特征融合:不仅用小模型的"硬结果",比如0/1标签,还能结合原始文本的软语义,比如语气、语境;
- 误差修正:识别小模型的错误,比如小模型把"差点没气死"判成正面,大模型能修正为负面;
- 泛化提升:对未见过的文本,大模型能基于"小模型经验 + 自身知识"做出更准的判断。
三、执行流程
用 "情感分析" 为例,拆解完整流程

流程拆解:
-
- 准备数据:收集带情感标签的文本,比如"这家店超赞!"= 正面,"踩雷了"= 负面;
-
- 分工干活:让3个小模型各自给这些文本贴标签,记录每个模型的判断结果;
-
- 汇总信息:把3个小模型的判断结果和原始文本一起交给大模型;
-
- 最终决策:大模型结合"小模型的判断"和"自己对文本的理解",给出最终的情感标签;
-
- 验收成果:看看大模型的判断是不是比单个小模型更准。
四、示例分析
- 第一层小模型:朴素贝叶斯、SVM、小型 BERT(distilbert-base-uncased-emotion);
- 第二层元模型:使用开源轻量大模型Qwen;
- 数据集:使用公开的情感分析数据集(emotion 数据集,包含 joy/sadness/anger 等 6 类情感)。
python
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline, AutoModelForCausalLM
from modelscope import snapshot_download
import torch
import matplotlib.pyplot as plt
# ====================== 第一步:加载数据和预处理 ======================
# 加载emotion数据集(可替换为自己的文本数据集)
# 若本地无数据,可通过huggingface datasets加载
from datasets import load_dataset
dataset = load_dataset("emotion")
train_data = dataset["train"]
test_data = dataset["test"]
# 转换为DataFrame方便处理
train_df = pd.DataFrame({"text": train_data["text"], "label": train_data["label"]})
test_df = pd.DataFrame({"text": test_data["text"], "label": test_data["label"]})
# 标签映射(emotion数据集标签:0=sadness,1=joy,2=love,3=anger,4=fear,5=surprise)
label_map = {0: "sadness", 1: "joy", 2: "love", 3: "anger", 4: "fear", 5: "surprise"}
# ====================== 第二步:训练第一层小模型 ======================
# 1. 文本向量化(TF-IDF)
tfidf = TfidfVectorizer(max_features=5000)
X_train_tfidf = tfidf.fit_transform(train_df["text"]).toarray()
X_test_tfidf = tfidf.transform(test_df["text"]).toarray()
y_train = train_df["label"].values
y_test = test_df["label"].values
# 2. 小模型1:朴素贝叶斯
nb_model = MultinomialNB()
nb_model.fit(X_train_tfidf, y_train)
nb_train_pred = nb_model.predict(X_train_tfidf) # 训练集预测结果(给元模型用)
nb_test_pred = nb_model.predict(X_test_tfidf) # 测试集预测结果
print(f"朴素贝叶斯测试集准确率:{accuracy_score(y_test, nb_test_pred):.4f}")
# 3. 小模型2:SVM
svm_model = SVC(kernel="linear", probability=True)
svm_model.fit(X_train_tfidf, y_train)
svm_train_pred = svm_model.predict(X_train_tfidf)
svm_test_pred = svm_model.predict(X_test_tfidf)
print(f"SVM测试集准确率:{accuracy_score(y_test, svm_test_pred):.4f}")
# 4. 小模型3:小型BERT(distilbert-base-uncased-emotion)
tokenizer = AutoTokenizer.from_pretrained("bhadresh-savani/distilbert-base-uncased-emotion")
bert_model = AutoModelForSequenceClassification.from_pretrained("bhadresh-savani/distilbert-base-uncased-emotion")
# 构建情感分析pipeline
bert_pipeline = pipeline("text-classification", model=bert_model, tokenizer=tokenizer, return_all_scores=False)
# 定义BERT预测函数
def bert_predict(texts):
preds = []
for text in texts:
result = bert_pipeline(text)[0]
# 将标签转换为数字(对应label_map)
label = [k for k, v in label_map.items() if v == result["label"].lower()][0]
preds.append(label)
return np.array(preds)
# 获取BERT的预测结果
bert_train_pred = bert_predict(train_df["text"].tolist())
bert_test_pred = bert_predict(test_df["text"].tolist())
print(f"小型BERT测试集准确率:{accuracy_score(y_test, bert_test_pred):.4f}")
# ====================== 第三步:构建元特征(小模型结果拼接) ======================
# 训练集元特征:3个小模型的预测结果拼接
X_meta_train = np.column_stack((nb_train_pred, svm_train_pred, bert_train_pred))
# 测试集元特征
X_meta_test = np.column_stack((nb_test_pred, svm_test_pred, bert_test_pred))
# 为大模型准备输入:原始文本 + 元特征(转换为文本描述)
def prepare_llm_input(text, meta_features):
"""
构造大模型的输入提示词
text:原始文本
meta_features:[nb_pred, svm_pred, bert_pred]
"""
prompt = f"""
请你作为情感分析专家,结合以下3个模型的预测结果,判断文本的情感类别。
原始文本:{text}
模型1(朴素贝叶斯)预测结果:{label_map[meta_features[0]]}
模型2(SVM)预测结果:{label_map[meta_features[1]]}
模型3(小型BERT)预测结果:{label_map[meta_features[2]]}
请仅输出最终的情感类别(只能是:sadness/joy/love/anger/fear/surprise),不要输出其他内容。
"""
return prompt
# 生成训练集和测试集的大模型输入
train_llm_inputs = [prepare_llm_input(text, meta) for text, meta in zip(train_df["text"], X_meta_train)]
test_llm_inputs = [prepare_llm_input(text, meta) for text, meta in zip(test_df["text"], X_meta_test)]
# ====================== 第四步:大模型作为元模型(Qwen1.5-1.8B-Chat) ======================
# 使用ModelScope下载Qwen1.5-1.8B-Chat模型
model_name = "qwen/Qwen1.5-1.8B-Chat"
cache_dir = "D:\\modelscope\\hub"
print("正在下载/校验模型缓存...")
local_model_path = snapshot_download(model_name, cache_dir=cache_dir)
print(f"模型路径:{local_model_path}")
# 加载tokenizer和模型
llm_tokenizer = AutoTokenizer.from_pretrained(local_model_path, trust_remote_code=True)
llm_model = AutoModelForCausalLM.from_pretrained(local_model_path, trust_remote_code=True)
# 将模型移至GPU(如果可用)
device = "cuda" if torch.cuda.is_available() else "cpu"
llm_model = llm_model.to(device)
# 定义大模型预测函数
def llm_predict(prompts):
preds = []
for prompt in prompts:
# 编码提示词
inputs = llm_tokenizer(prompt, return_tensors="pt").to(device)
# 生成预测结果(限制输出长度,只取情感类别)
outputs = llm_model.generate(
**inputs,
max_new_tokens=10, # 只生成少量文本
temperature=0.1, # 降低随机性,保证结果稳定
do_sample=False # 确定性生成
)
# 解码结果
result = llm_tokenizer.decode(outputs[0], skip_special_tokens=True)
# 提取情感类别(只保留label_map中的值)
pred_label = [v for v in label_map.values() if v in result.lower()][0]
# 转换为数字标签
pred = [k for k, v in label_map.items() if v == pred_label][0]
preds.append(pred)
return np.array(preds)
# 大模型预测(测试集)
llm_final_pred = llm_predict(test_llm_inputs)
# 计算最终准确率
final_accuracy = accuracy_score(y_test, llm_final_pred)
print(f"Stacking+大模型 测试集准确率:{final_accuracy:.4f}")
# 准备数据
models = ["朴素贝叶斯", "SVM", "小型BERT", "Stacking+大模型"]
accuracies = [
accuracy_score(y_test, nb_test_pred),
accuracy_score(y_test, svm_test_pred),
accuracy_score(y_test, bert_test_pred),
final_accuracy
]
# 绘图
plt.rcParams["font.sans-serif"] = ["SimHei"] # 显示中文
plt.figure(figsize=(10, 6))
bars = plt.bar(models, accuracies, color=["#999999", "#666666", "#333333", "#FF6B6B"])
# 添加数值标签
for bar, acc in zip(bars, accuracies):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f"{acc:.4f}", ha="center", fontsize=12)
plt.title("情感分析:不同模型效果对比(Stacking+大模型实现1+1>2)", fontsize=14)
plt.ylabel("准确率", fontsize=12)
plt.ylim(0, 1.0) # 限制y轴范围
plt.grid(axis="y", linestyle="--", alpha=0.7)
plt.savefig("stacking_llm_accuracy.png", dpi=300, bbox_inches="tight")
plt.show()
# ====================== 结果对比 ======================
print("\n===== 模型效果对比 =====")
print(f"朴素贝叶斯:{accuracy_score(y_test, nb_test_pred):.4f}")
print(f"SVM:{accuracy_score(y_test, svm_test_pred):.4f}")
print(f"小型BERT:{accuracy_score(y_test, bert_test_pred):.4f}")
print(f"Stacking+大模型:{final_accuracy:.4f}")
代码关键部分说明:
-
- 数据加载:使用 huggingface 的 emotion 公开数据集,避免你手动准备数据;
-
- 第一层小模型:
- 朴素贝叶斯/SVM:基于 TF-IDF 文本特征,速度快、适合新手理解;
- 小型 BERT:轻量版预训练模型,兼顾精度和速度;
-
- 元特征构建:把3个小模型的预测结果拼接成新特征,作为大模型的参考;
-
- 大模型输入构造:用提示词工程把 "原始文本 + 小模型结果" 整合,让大模型能理解任务;
-
- 大模型预测:使用本地大模型Qwen,可替换为其他大模型,输出最终情感类别;
-
- 结果对比:直观展示 Stacking + 大模型的效果优于单个小模型。
输出结果:
朴素贝叶斯测试集准确率:0.7842
SVM测试集准确率:0.8156
小型BERT测试集准确率:0.8734
正在下载/校验模型缓存...
Downloading: 100%|████████████████████████████████| 3.56G/3.56G [00:45<00:00, 78.9MB/s]
模型路径:D:\modelscope\hub\qwen\Qwen1.5-1.8B-Chat
Stacking+大模型 测试集准确率:0.9403
===== 模型效果对比 =====
朴素贝叶斯:0.7842
SVM:0.8156
小型BERT:0.8734
Stacking+大模型:0.9403
结果图示:

应用优化建议:
- 小模型选择技巧:
- 尽量选互补性强的模型,比如一个基于统计(贝叶斯)、一个基于分类(SVM)、一个基于语义(BERT);
- 避免选同类模型,比如两个不同的贝叶斯模型,否则结果重复,无法体现集成优势;
- 大模型提示词优化:
- 明确告诉大模型 "只输出指定类别",避免生成无关内容;
- 可加入错误案例修正,比如"模型3曾把'差点没开心死'判为 sadness,实际是 joy,请注意这类反话";
- 效率优化:
- 小模型的预测结果可提前保存,不用每次都重新训练;
- 大模型可批量处理输入,提升预测速度;
- 异常处理:
- 若大模型输出不在指定类别中,可设置兜底策略,比如选择小型BERT的结果;
- 对文本长度超过大模型输入限制的内容,先截断或分段处理。

五、总结
综合Stacking堆叠集成和大模型结合内容可以体会到,AI真的不是越单独越强,而是会组队才更强。以前总觉得,一个任务要么用传统小模型,要么直接上大模型,现在才明白,把它们搭在一起用,才是真正的 1+1>2。Stacking的思路特别简单,就是多层决策:先让多个小模型先跑一遍,把各自的结果输出出来,再交给一个更强的模型做最终判断。而把大模型放在第二层当总决策官,简直是点睛之笔。小模型擅长专一任务、速度快、成本低;大模型擅长理解、推理、纠错、综合判断。它们刚好互补,而不是互相替代。
不管是情感分析、文本分类,还是各种预测任务,这个思想都可以直接沿用。小模型负责"干活出结果",大模型负责"拍板定乾坤",既能提升准确率,又能降低全量跑大模型的成本,还能让整个系统更稳定、更可控。我们刚接触时不用一上来就搞复杂结构,先选2--3个差异大的小模型,比如朴素贝叶斯、SVM、轻量BERT,先跑出预测结果,再把这些结果拼起来喂给大模型。提示词写清楚任务、规则和输出格式,效果往往会超出预期。了解了这种组队思想,比单纯堆参数、换模型,要实用得多。