传统机器学习与大模型 + Prompt 的对比示例
下面两段代码分别展示了传统机器学习和大模型 + Prompt 在文本分类任务上的实现方式,帮助你直观感受两者的差异。
传统机器学习方法(使用 BERT 微调)
traditional-ml-text-classification传统机器学习文本分类实现
import torch
from torch.utils.data import Dataset, DataLoader
from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import pandas as pd
# 1. 准备数据
data = {
'text': [
"我想预订明天的机票", "查询今天的天气", "帮我设置闹钟",
"播放周杰伦的歌曲", "今天有什么新闻", "推荐几部科幻电影"
],
'label': [0, 1, 2, 3, 4, 5] # 0:订票, 1:天气, 2:闹钟, 3:音乐, 4:新闻, 5:电影
}
df = pd.DataFrame(data)
# 2. 数据集划分
train_df, val_df = train_test_split(df, test_size=0.2, random_state=42)
# 3. 创建数据集类
class TextClassificationDataset(Dataset):
def __init__(self, texts, labels, tokenizer, max_len=128):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
self.max_len = max_len
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = str(self.texts[idx])
label = self.labels[idx]
encoding = self.tokenizer(
text,
add_special_tokens=True,
max_length=self.max_len,
return_token_type_ids=False,
padding='max_length',
truncation=True,
return_attention_mask=True,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].flatten(),
'attention_mask': encoding['attention_mask'].flatten(),
'label': torch.tensor(label, dtype=torch.long)
}
# 4. 初始化tokenizer和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained(
'bert-base-chinese',
num_labels=6
)
# 5. 创建数据加载器
train_dataset = TextClassificationDataset(
train_df['text'].values,
train_df['label'].values,
tokenizer
)
val_dataset = TextClassificationDataset(
val_df['text'].values,
val_df['label'].values,
tokenizer
)
train_loader = DataLoader(train_dataset, batch_size=2, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=2)
# 6. 训练模型
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
optimizer = AdamW(model.parameters(), lr=2e-5)
epochs = 3
for epoch in range(epochs):
model.train()
train_loss = 0
for batch in train_loader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['label'].to(device)
optimizer.zero_grad()
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
train_loss += loss.item()
loss.backward()
optimizer.step()
avg_train_loss = train_loss / len(train_loader)
print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_train_loss:.4f}")
# 7. 评估模型
model.eval()
predictions = []
true_labels = []
with torch.no_grad():
for batch in val_loader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['label'].to(device)
outputs = model(input_ids, attention_mask=attention_mask)
preds = torch.argmax(outputs.logits, dim=1)
predictions.extend(preds.cpu().numpy())
true_labels.extend(labels.cpu().numpy())
accuracy = accuracy_score(true_labels, predictions)
print(f"Validation Accuracy: {accuracy:.4f}")
# 8. 保存模型
model.save_pretrained('./intent_classifier')
tokenizer.save_pretrained('./intent_classifier')
大模型 + Prompt 方法(使用 OpenAI API)
llm-prompt-text-classification大模型+Prompt文本分类实现
import openai
import json
from typing import List, Dict, Tuple
# 1. 设置API密钥(实际使用时需替换为你的密钥)
openai.api_key = "your-openai-api-key"
# 2. 定义意图分类器
class IntentClassifier:
def __init__(self, intents: Dict[str, List[str]]):
"""
初始化意图分类器
Args:
intents: 字典,键为意图名称,值为该意图的示例列表
"""
self.intents = intents
self.prompt_template = self._build_prompt_template()
def _build_prompt_template(self) -> str:
"""构建分类Prompt模板"""
intent_examples = []
for intent_name, examples in self.intents.items():
examples_str = "\n".join([f"- {ex}" for ex in examples])
intent_examples.append(f"意图: {intent_name}\n示例:\n{examples_str}\n")
intent_list = ", ".join(self.intents.keys())
prompt = f"""
你是一个文本意图分类器。请根据以下意图及其示例,对用户输入进行分类。
可能的意图包括: {intent_list}
各意图的示例:
{"".join(intent_examples)}
请对以下用户输入进行分类,只返回意图名称,不要有其他解释:
"""
return prompt.strip()
def classify(self, user_input: str) -> str:
"""
使用大模型API对用户输入进行意图分类
Args:
user_input: 用户输入的文本
Returns:
预测的意图名称
"""
full_prompt = f"{self.prompt_template}\n\n用户输入: {user_input}"
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一个精确的意图分类器,只返回意图名称。"},
{"role": "user", "content": full_prompt}
],
temperature=0.0, # 降低随机性,提高分类准确性
max_tokens=50
)
# 提取模型返回的意图
predicted_intent = response.choices[0].message.content.strip()
# 简单后处理:确保返回的是有效意图
if predicted_intent in self.intents:
return predicted_intent
else:
# 如果返回的不是有效意图,尝试映射或默认分类
for intent_name in self.intents:
if intent_name.lower() in predicted_intent.lower():
return intent_name
# 默认返回第一个意图(实际应用中应更智能地处理)
return list(self.intents.keys())[0]
except Exception as e:
print(f"API调用错误: {e}")
return list(self.intents.keys())[0] # 出错时返回默认意图
# 3. 示例:使用Few-Shot方式定义意图
intents = {
"订票": ["我想预订明天的机票", "查询从北京到上海的航班", "帮我订一张后天的火车票"],
"天气": ["今天天气怎么样", "明天会下雨吗", "查询北京的气温"],
"闹钟": ["设置早上7点的闹钟", "提醒我下午3点开会", "创建一个10分钟后的提醒"],
"音乐": ["播放周杰伦的歌曲", "我想听流行音乐", "搜索陈奕迅的歌"],
"新闻": ["今天有什么新闻", "最新的科技资讯", "查看国际新闻"],
"电影": ["推荐几部科幻电影", "我想看动作片", "最近有什么新上映的电影"]
}
# 4. 创建分类器实例
classifier = IntentClassifier(intents)
# 5. 测试分类效果
test_inputs = [
"我要预订后天去广州的飞机票",
"今天上海的天气如何?",
"帮我设置一个半小时后的提醒",
"播放一首林俊杰的歌",
"给我讲讲最近的体育新闻",
"有哪些好看的悬疑电影推荐?"
]
for input_text in test_inputs:
predicted_intent = classifier.classify(input_text)
print(f"输入: {input_text}")
print(f"预测意图: {predicted_intent}")
print("-" * 30)
# 6. 处理新意图的方式(Zero-Shot)
def add_new_intent(classifier, intent_name, examples=None):
"""
添加新意图(支持Zero-Shot或Few-Shot)
Args:
classifier: 分类器实例
intent_name: 新意图名称
examples: 新意图的示例列表(如果为None,则使用Zero-Shot)
"""
if examples:
# Few-Shot:添加示例并重建Prompt
classifier.intents[intent_name] = examples
classifier.prompt_template = classifier._build_prompt_template()
else:
# Zero-Shot:只需在使用时指定新意图
pass
return classifier
# 示例:添加一个新意图(Zero-Shot方式)
classifier = add_new_intent(classifier, "翻译", examples=["帮我把这句话翻译成英文", "中译英:我爱你", "翻译:Hello world"])
# 测试新意图
new_test = "将'人工智能很强大'翻译成英文"
print(f"输入: {new_test}")
print(f"预测意图: {classifier.classify(new_test)}")
对比分析
传统机器学习方法特点
- 数据依赖性强:需要准备标注好的训练集和验证集,示例中每个意图至少需要多个样本。
- 模型训练复杂:
- 需要定义数据集类、数据加载器
- 涉及多轮训练、优化器设置、损失计算
- 需要 GPU 加速以提高训练效率
- 模型适配成本高:新增意图需要重新训练整个模型。
- 优势:一旦训练完成,部署和推理成本较低,适合大规模生产环境。
大模型 + Prompt 方法特点
- 数据依赖性低:通过 Few-Shot 示例或 Zero-Shot 直接分类,无需大量标注数据。
- 实现简单:
- 只需构建结构化 Prompt
- 调用 API 即可完成分类
- 无需模型训练过程
- 灵活性高:
- 新增意图只需修改 Prompt,无需重新训练
- 支持 Zero-Shot 学习,可快速适配新场景
- 成本与性能:
- 单次 API 调用成本较高
- 响应速度受网络和 API 服务器影响
- 适合小规模、快速迭代的应用场景
这两种方法各有优劣,实际应用中需要根据数据规模、实时性要求、成本限制等因素选择合适的方案。