Cogito-V1-Preview-Llama-3B 基于Transformer的文本分类实战:从数据准备到模型评估

张开发
2026/4/15 13:28:41 15 分钟阅读

分享文章

Cogito-V1-Preview-Llama-3B 基于Transformer的文本分类实战:从数据准备到模型评估
Cogito-V1-Preview-Llama-3B 基于Transformer的文本分类实战从数据准备到模型评估最近在尝试一些新的开源模型发现了一个挺有意思的选手——Cogito-V1-Preview-Llama-3B。别看它只有30亿参数但在一些中文任务上的表现还真有点让人刮目相看。正好手头有个中文情感分类的项目我就用它完整跑了一遍从数据准备到模型评估整个过程下来感觉挺有收获的。这篇文章我就想跟你分享一下这次实战的全过程。我们不谈那些复杂的理论就看看怎么把一个公开的中文数据集用这个模型跑起来效果到底怎么样跟咱们熟悉的一些传统方法比又有什么不同。整个过程我会尽量讲得明白代码也会给出来你完全可以跟着做一遍。1. 项目背景与模型初印象这次我们用的模型是 Cogito-V1-Preview-Llama-3B。它本质上是一个基于 Transformer 架构的大语言模型经过了指令微调所以能够理解我们的指令并给出回答。我们要做的文本分类任务其实就是把“分类”这个指令通过合适的 Prompt提示词告诉它让它来执行。我选择的任务是中文情感二分类也就是判断一条评论是正面还是负面。这个任务很经典也很有实际价值比如电商平台自动分析用户评价。为了公平和可复现我选用了一个公开的中文情感分析数据集。在开始之前我们先快速了解一下这个模型。它的“身材”是30亿参数在开源模型里属于中等体量对计算资源的要求相对友好。它支持中英文并且针对指令跟随做了优化这意味着我们不用进行复杂的微调通过设计好的 Prompt 就能让它完成特定任务这种方式通常被称为“零样本”或“少样本”学习。2. 环境搭建与数据准备工欲善其事必先利其器。第一步咱们先把环境和数据准备好。2.1 快速搭建运行环境这个模型可以通过 Hugging Face 的transformers库来调用这是目前最主流的方式。你需要确保你的 Python 环境里安装了这个库以及一些必要的依赖。# 安装核心库 pip install transformers torch datasets scikit-learn pandas如果你的机器有 GPU并且安装了 CUDA那么 PyTorch 会自动利用 GPU 来加速这会大大减少模型推理的等待时间。没有 GPU 也能跑只是会慢一些。2.2 获取与探索数据集我这次用的是 ChnSentiCorp 数据集的一个子集这是一个广泛使用的中文情感分析数据集。我们通过datasets库可以很方便地加载它。from datasets import load_dataset # 加载ChnSentiCorp数据集 dataset load_dataset(seamew/ChnSentiCorp) # 查看数据集结构 print(f数据集结构: {dataset}) print(f训练集样本数: {len(dataset[train])}) print(f验证集样本数: {len(dataset[validation])}) print(f测试集样本数: {len(dataset[test])}) # 看看前几条数据长什么样 for i in range(3): sample dataset[train][i] print(f\n样本 {i1}:) print(f 文本: {sample[text]}) print(f 标签: {sample[label]} (0: 负面, 1: 正面))运行上面的代码你会看到数据的基本情况一共有多少条数据每条数据包含一段中文文本和一个标签0代表负面1代表正面。随便看几条你会发现文本都是真实的用户评论比如“房间很干净服务人员态度很好”之类的。在正式使用前我们通常需要简单看看数据的分布比如正负面评论的比例是不是均衡的这有助于我们理解任务难度。import pandas as pd # 将训练集转为Pandas DataFrame方便分析 train_df pd.DataFrame(dataset[train]) label_counts train_df[label].value_counts() print(训练集标签分布:) print(label_counts) print(f\n正面评论占比: {label_counts[1] / len(train_df):.2%})理想情况下我们希望正负样本数量差不多这样模型不会偏向某一类。从结果看这个数据集大致是平衡的我们可以直接使用。3. 核心步骤Prompt工程与模型调用这是整个项目的核心环节。我们要解决两个关键问题第一怎么把分类任务“说”给模型听第二怎么高效地调用模型得到结果。3.1 设计有效的分类Prompt大模型不像传统的分类器那样直接接收文本和标签。它需要你通过自然语言给它下达指令。设计一个好的 Prompt提示词至关重要它直接决定了模型能不能正确理解你要它做什么。经过几次尝试我找到了一个比较有效的 Prompt 模板请判断以下文本的情感倾向是正面还是负面。只输出“正面”或“负面”不要输出其他任何内容。 文本{这里放需要分类的句子} 情感倾向这个 Prompt 有几个设计要点指令清晰开头明确告诉模型任务是什么——“判断情感倾向”。格式约束严格要求模型“只输出‘正面’或‘负面’”这能极大减少它输出无关内容或长篇大论的概率方便我们后续解析结果。示例分隔用“文本”和“情感倾向”清晰地分隔了指令和输入以及输入和输出位置。我们来写一个函数专门负责构造这个 Prompt。def build_classification_prompt(text): 构建用于情感分类的Prompt prompt_template 请判断以下文本的情感倾向是正面还是负面。只输出“正面”或“负面”不要输出其他任何内容。 文本{text} 情感倾向 return prompt_template.format(texttext) # 测试一下Prompt构造 test_text 这部电影的剧情太糟糕了看得我昏昏欲睡。 print(构造的Prompt示例) print(build_classification_prompt(test_text))3.2 加载模型与批量推理接下来我们加载 Cogito-V1-Preview-Llama-3B 模型并编写一个函数来处理批量文本。from transformers import AutoTokenizer, AutoModelForCausalLM import torch # 指定模型名称 model_name CogitoLab/Cogito-V1-Preview-Llama-3B print(f正在加载模型: {model_name}...) tokenizer AutoTokenizer.from_pretrained(model_name, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained(model_name, trust_remote_codeTrue, torch_dtypetorch.float16, # 使用半精度节省显存 device_mapauto) # 自动分配设备CPU/GPU print(模型加载完毕) def classify_text(text, model, tokenizer, max_new_tokens10): 对单条文本进行情感分类 prompt build_classification_prompt(text) inputs tokenizer(prompt, return_tensorspt).to(model.device) # 生成回答 with torch.no_grad(): outputs model.generate(**inputs, max_new_tokensmax_new_tokens, do_sampleFalse, # 为了结果确定性关闭随机采样 pad_token_idtokenizer.eos_token_id) # 解码生成的内容并只提取新生成的部分即答案 full_output tokenizer.decode(outputs[0], skip_special_tokensTrue) answer full_output.split(情感倾向)[-1].strip() # 提取“情感倾向”后面的内容 return answer # 测试单条分类 test_result classify_text(test_text, model, tokenizer) print(f\n测试文本: {test_text}) print(f模型输出: {test_result})运行后你应该能看到模型对测试句子输出了“负面”。这证明我们的流程跑通了。但是对几千条数据一条条调用太慢了。我们需要一个批量处理的函数并且要考虑到模型生成可能出现的意外输出比如它没听话输出了别的词增加一些简单的后处理逻辑。def batch_classify(texts, model, tokenizer, batch_size4): 批量分类文本 predictions [] for i in range(0, len(texts), batch_size): batch_texts texts[i:ibatch_size] batch_prompts [build_classification_prompt(t) for t in batch_texts] inputs tokenizer(batch_prompts, return_tensorspt, paddingTrue, truncationTrue).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens10, do_sampleFalse, pad_token_idtokenizer.eos_token_id) # 解码每个样本的输出 for idx in range(len(batch_texts)): # 获取单个样本的输出ids并跳过输入部分 output_ids outputs[idx][len(inputs[input_ids][idx]):] answer tokenizer.decode(output_ids, skip_special_tokensTrue).strip() # 后处理将模型输出映射为数字标签 if 正面 in answer: pred_label 1 elif 负面 in answer: pred_label 0 else: # 如果模型输出不符合预期可以记录或赋予一个默认值如-1 # print(f非常规输出: {answer} for text: {batch_texts[idx][:50]}...) pred_label -1 # 标记为未知 predictions.append(pred_label) if (i // batch_size 1) % 10 0: print(f已处理 {ibatch_size}/{len(texts)} 条样本...) return predictions4. 模型效果评估与对比现在让我们在测试集上看看这个基于 Transformer 的大模型到底表现如何。4.1 在测试集上运行并评估我们用写好的批量函数来处理整个测试集。# 准备测试集文本和真实标签 test_texts dataset[test][text] true_labels dataset[test][label] print(f开始对测试集共{len(test_texts)}条样本进行分类预测...) predicted_labels batch_classify(test_texts, model, tokenizer, batch_size4) print(预测完成) # 评估性能 from sklearn.metrics import accuracy_score, f1_score, classification_report # 过滤掉模型未能识别标记为-1的样本 valid_indices [i for i, pred in enumerate(predicted_labels) if pred ! -1] filtered_true [true_labels[i] for i in valid_indices] filtered_pred [predicted_labels[i] for i in valid_indices] print(f有效预测样本数: {len(filtered_pred)} / {len(true_labels)}) print(f模型未能识别的样本数: {len(true_labels) - len(filtered_pred)}) if len(filtered_pred) 0: accuracy accuracy_score(filtered_true, filtered_pred) f1 f1_score(filtered_true, filtered_pred, averagemacro) # 宏平均F1考虑类别平衡 print(\n Cogito-V1-Preview-Llama-3B 评估结果 ) print(f准确率 (Accuracy): {accuracy:.4f}) print(f宏平均F1分数 (Macro-F1): {f1:.4f}) print(\n详细分类报告:) print(classification_report(filtered_true, filtered_pred, target_names[负面, 正面]))运行这段代码需要一些时间取决于你的硬件。完成后你会看到一组评估指标。以我这次运行的结果为例准确率大概在 0.85-0.90 之间F1分数也差不多在这个范围。这意味着模型在近九成的样本上都判断正确了对于零样本学习即没有用这个数据集的任何标签进行训练来说这个成绩相当不错。4.2 与传统机器学习方法对比为了有个更直观的感受我们用一个非常经典的传统方法——基于 TF-IDF 特征和逻辑回归Logistic Regression分类器在同一个数据集上做一个对比。我们会在训练集上训练这个传统模型然后在测试集上评估。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline # 准备训练数据 train_texts dataset[train][text][:2000] # 为了快速演示取部分训练数据 train_labels dataset[train][label][:2000] # 构建并训练传统机器学习管道 print(训练TF-IDF 逻辑回归模型...) traditional_model make_pipeline( TfidfVectorizer(max_features5000), LogisticRegression(max_iter1000) ) traditional_model.fit(train_texts, train_labels) print(传统模型训练完成。) # 在测试集上预测和评估 traditional_preds traditional_model.predict(test_texts) traditional_accuracy accuracy_score(true_labels, traditional_preds) traditional_f1 f1_score(true_labels, traditional_preds, averagemacro) print(\n 传统方法 (TF-IDF LR) 评估结果 ) print(f准确率 (Accuracy): {traditional_accuracy:.4f}) print(f宏平均F1分数 (Macro-F1): {traditional_f1:.4f})运行这个对比你会发现传统方法在这个特定数据集上的准确率可能更高有时能达到 0.92 甚至更高。这引出了一个有趣的观察。4.3 效果分析与观察把两个结果放在一起看我们能得到一些启发传统方法在“封闭任务”上可能更精准对于情感分类这种定义清晰、特征相对明确的“封闭任务”专门针对该任务训练的传统模型即使很简单往往能取得非常高的分数。因为它从大量标注数据中学到了非常具体的词语与情感的关联模式。大模型展现了强大的泛化与指令理解能力Cogito-V1-Preview-Llama-3B 并没有用这个数据集的任何一条评论训练过。它纯粹是依靠从海量文本中学到的语言知识和我们给的指令来完成任务。能达到接近90%的准确率充分证明了其强大的零样本泛化能力和对自然语言指令的理解能力。适用场景不同传统方法需要标注数据训练且通常难以直接迁移到其他任务比如从情感分类换到主题分类就要重新提取特征和训练。而大模型通过更换 Prompt理论上可以应对无数种任务灵活性高得多。成本与效率传统方法训练和推理速度通常极快资源消耗低。大模型推理则需要更多的计算资源GPU和时间。但在没有标注数据的新任务上大模型的“零样本”能力可以省去昂贵的数据标注成本。简单来说如果你有一个标注充足、任务固定的场景传统方法可能更高效、更精准。但如果你面对的是快速变化、缺乏标注数据、或者需要模型理解复杂指令的多种任务那么大模型这种“通过说话来指挥”的方式就显示出其独特的优势了。5. 总结走完这一整套流程从数据准备、Prompt设计、模型调用到效果评估我感觉对如何应用这类开源大模型做具体任务思路清晰了不少。Cogito-V1-Preview-Llama-3B 在这个中文情感分类任务上的表现验证了即使参数量不是特别巨大基于 Transformer 的指令微调模型也能通过恰当的 Prompt 完成不错的零样本学习。整个过程里Prompt 的设计是关键一步直接影响了模型输出是否规整、是否易于处理。另外如何处理模型的“非预期输出”也是一个实际的工程问题。对比传统方法更能让我们看清两种技术路线的特点和适用边界一个像“专业工具”在特定领域打磨得极其锋利一个像“瑞士军刀”功能多样适应性强但可能需要一些技巧才能用好。如果你想自己试试完全可以用这个代码作为起点换成你自己的文本数据或者尝试其他分类任务比如新闻分类、意图识别等只需要修改 Prompt 模板和标签映射逻辑就行了。大模型的世界很多时候就是“大胆假设小心 Prompt”多试试总会有惊喜。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章