ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Transformers的NLP实战项目:从文本分类到机器翻译

基于Transformers的NLP实战项目:从文本分类到机器翻译 简介一份面向Python期末大作业与课程设计场景的Transformers基础应用及机器翻译实现项目源码附带详细注释与文档适合NLP方向学生快速搭建完整项目。压缩包共16个文件约1.86MB以9个Jupyter Notebook为核心分别演示预训练模型加载、Tokenizer分词、Pipeline任务、特征提取、NER命名实体识别、面向模型的数据预处理等关键环节另有翻译脚本、可视化UI界面文件、说明文档与期末报告jupyter版便于对照实验过程、梳理答辩思路与直接部署。已有137人学习下载。代码注释细致新手也能看懂从模型调用到结果展示的部署逻辑各Notebook按功能独立组织既可逐模块学习也可组合成完整机器翻译链路覆盖数据处理、模型应用、翻译交互与界面呈现等环节适合作为期末大作业、课程设计或二次开发的基础参考整体实用价值高。1. 期末大作业选了 Transformers这份源码把基础应用和机器翻译串成了一条线NLP 课程的期末大作业最怕的不是算法难而是「代码跑不通、答辩没得演示」。这份基于 transformers 的 Python 期末大作业源码把两件事装进了同一条 pipeline先用文本分类、命名实体识别、文本生成这些基础应用把 Hugging Face 的用法跑熟再基于同一套 tokenizer model 架构完成英译中的机器翻译实现最后用 BLEU 给翻译质量打分。源码是全 Python 实现带文档说明模型走预训练权重不需要从零训练重点放在「会加载、会用、会调参、会评估」。这份资源适合三类人正在做 NLP 课程设计的学生想快速把 transformers 用起来做验证性实验的开发者以及需要一份可解释、可演示、带文档的期末项目源码来应付答辩的同学。代码不难但每一步都踩得到真实运行的逻辑。2. 环境与选型为什么 PyTorch Hugging Face 是复现成本最低的组合2.1 为什么期末作业选 Transformers PyTorch而不是自己写 Seq2Seq期末作业时间有限从零手写一个 Transformer 的代价非常高。多头注意力、位置编码、层归一化、mask 机制这几个模块里任何一个细节写错跑出来的 loss 曲线都像玄学光排查就要花掉大半周。而 Hugging Face Transformers 把预训练权重、分词器、训练器和推理封装成了统一的接口我只需要关注「加载哪个模型、传什么参数、拿什么结果」。选 PyTorch 而不是 TensorFlow是因为 transformers 生态里 PyTorch 的示例和 issue 讨论最多出问题搜答案快。PyTorch 的动态图对调试也更友好比如在翻译模型里打印某一层的 logits直接打断点就行。TensorFlow 的 Keras API 做部署没得说但做期末实验PyTorch 的学习曲线更平缓。另外要注意的是transformers 库本身是框架无关的backend 可以是 PyTorch 也可以是 TensorFlow。你不必纠结于「我选的是 transformers 还是 PyTorch」可以理解为 transformers 提供模型和接口PyTorch 提供张量运算和自动求导两份代码在工程上是配合关系。源码包里也固定用 PyTorch backend避免两套框架混用导致环境冲突。2.2 环境搭建三步走虚拟环境、torch 与 transformers 安装、首次加载验证我习惯先建虚拟环境避免把系统 Python 搞乱。下面这套在 Linux 和 macOS 上直接用Windows 用户把激活命令换一下即可。python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install torch --index-url https://download.pytorch.org/whl/cu118 pip install transformers datasets evaluate sacrebleu gradio这里有三个细节要说明。torch 的下载地址指定了 cu118表示 CUDA 11.8 版本的预编译包如果你的机器没有 Nvidia 显卡把地址换成https://download.pytorch.org/whl/cpu即可。transformers 我这里没锁版本号默认装 4.x 最新版源码里的 README 会记录打包时的版本。最后两个库 datasets 和 evaluate 是数据加载和评估用的sacrebleu 是算 BLEU 的gradio 是最后做演示界面的如果不想装模块可以只装前两个。装完之后跑一个最小验证确认库能正常加载并完成推理from transformers import pipeline # 第一次运行会下载权重需要联网之后会走本地缓存 classifier pipeline( sentiment-analysis, modeldistilbert-base-uncased-finetuned-sst-2-english ) print(classifier(This final project works!))pipeline 是 transformers 提供的高层封装第一个参数是任务名第二个参数指定模型。这里选 distilbert 的小模型参数量只有 6700 万左右CPU 上也能秒出结果。输出是一条带 label 和 score 的列表score 接近 1 表示模型对这个判断很有信心。如果这一步能跑通环境基本没问题。2.3 权重本地化答辩现场断网也不翻车的缓存策略transformers 默认把模型权重下载到本地缓存Linux 和 macOS 在~/.cache/huggingface/hubWindows 在C:\Users\用户名\.cache\huggingface\hub。实际答辩时现场网络状况谁也说不准所以我在项目里强制要求「先把所有要用的权重下载到本地再考虑跑实验」。路径作用hub存放所有下载的模型权重hub/models--Helsinki-NLP--opus-mt-en-zh某个具体模型的缓存目录hub/models--Helsinki-NLP--opus-mt-en-zh/snapshots/commit某个版本快照的实际权重下载阶段如果直连模型仓库经常中断可以在下载前设置镜像端点export HF_ENDPOINThttps://hf-mirror.com这个环境变量只影响下载阶段下载完成后代码逻辑不依赖它。之后把整个流程改成离线模式答辩时就算断网也不影响演示# 先联网把所有用到的模型各加载一次权重会进入本地缓存 python - EOF from transformers import AutoModelForSeq2SeqLM, AutoTokenizer AutoTokenizer.from_pretrained(Helsinki-NLP/opus-mt-en-zh) AutoModelForSeq2SeqLM.from_pretrained(Helsinki-NLP/opus-mt-en-zh) EOF # 跑正式实验前强制离线 export HF_HUB_OFFLINE1 export TRANSFORMERS_OFFLINE1 python translation_demo.py这里有个容易忽略的点transformers 加载模型时除了权重还要读 config 和 tokenizer 文件这几类文件都在同一个缓存目录里。只下载了权重而 config 没下载完离线模式会报「connection error」而不是「file not found」。所以预下载那一步一定要用真实代码把模型完整加载一遍而不是手动去 curl 文件。3. 基础应用三件套情感分析、命名实体识别与文本生成的调用姿势3.1 pipeline 三行代码先跑通把黑匣子留到后面拆transformers 的 pipeline 是上手最快的入口。我把期末项目里的三个基础应用都先用 pipeline 各跑了一遍确认结果合理再往下拆。from transformers import pipeline # 1. 情感分析 sent pipeline(sentiment-analysis, modeldistilbert-base-uncased-finetuned-sst-2-english) print(sent(I really enjoy this NLP course.)) # 2. 命名实体识别 ner pipeline(ner, modeldbmdz/bert-large-cased-finetuned-conll03-english) print(ner(Alice works at Google in Paris.)) # 3. 文本生成 gen pipeline(text-generation, modelgpt2, max_length40, num_return_sequences1) print(gen(In this project, we implement))三行代码背后分别封装了不同的流程情感分析是 tokenizer 编码 - 模型前向 - softmax 取概率NER 是对每个 token 预测实体类别并做对齐文本生成则是自回归循环每步生成一个 token 再拼回去。pipeline 适合验收和演示但对期末作业来说如果只写 pipeline 会被老师问倒。所以项目里每个应用都补齐了下一节的手动链路。3.2 手动链路tokenizer → model → output每一步输出什么期末项目里我保留了一套不依赖 pipeline 的手写推理代码用于展示理解深度。以情感分析为例完整链路是分词、编码、前向、取 logits、映射标签。from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch model_id distilbert-base-uncased-finetuned-sst-2-english tokenizer AutoTokenizer.from_pretrained(model_id) model AutoModelForSequenceClassification.from_pretrained(model_id) texts [I love this project., I hate waiting for grading.] # padding 保证 batch 内等长truncation 截断超长文本 enc tokenizer( texts, paddingTrue, truncationTrue, max_length64, return_tensorspt ) print(enc[input_ids]) # token 索引shape [batch, seq_len] print(enc[attention_mask]) # 1 表示真实 token0 表示 padding with torch.no_grad(): logits model(**enc).logits preds logits.argmax(dim-1) print(preds.tolist()) # [1, 0] 表示积极和消极这里值得解释的是 attention_mask 的作用和 no_grad 的意义。attention_mask 告诉模型哪些位置是实际 token、哪些是 padding 补出来的模型计算注意力时会忽略 padding 位置避免补位影响语义。torch.no_grad()是推理时的固定动作因为这里不需要反向传播没必要让 PyTorch 为每个中间张量保存计算图省内存也提速。输出层 logits 的 shape 是[batch, num_labels]情感分析模型有两个标签消极、积极所以第二维是 2。argmax(dim-1)取的是最后一个维度上概率最大的索引1 代表积极0 代表消极。3.3 批处理参数max_length、truncation、padding 与长文本切窗调用 tokenizer 时几个参数直接影响性能和结果我把项目里的参数选择整理成了表方便答辩时讲思路。参数作用项目里的设定max_length截断或填充的目标长度分类任务用 64翻译任务用 128truncation是否截断超长文本设为 True防止长文本撑爆显存padding是否补齐到 batch 内等长单条推理用 False批量推理用 Truestride切窗时的重叠步长长文档分类时设为 64长文本切窗是容易被忽略的场景。比如课程论文摘要超过 512 token直接截断会丢掉结尾信息我一般用stride做滑窗把长文本切成多个片段分别预测再对结果做投票或取平均。long_text This is a very long review. * 200 inputs tokenizer( [long_text], return_tensorspt, truncationTrue, max_length128, stride64, return_overflowing_tokensTrue, ) print(len(inputs[input_ids])) # 输出片段数量return_overflowing_tokensTrue会返回所有切出来的窗口每个窗口长度为 max_length相邻窗口重叠 64 token。这样做的好处是信息不丢缺点是推理次数变多。项目里对这个函数的计算量做了注释一段 4000 token 的长文本会切成大约 32 个窗口每个窗口独立过模型期末数据量下完全可以接受。注意stride 只在truncationTrue且return_overflowing_tokensTrue时生效单独设 stride 不会触发切窗。4. 机器翻译实现从预训练权重到 BLEU 评估的完整链路4.1 翻译模型选型对比opus-mt / M2M100 / mBART-50 期末场景下怎么选机器翻译是这份期末作业的主轴。选什么模型直接决定答辩演示的稳定性。我把三个候选模型做了对比测试最终项目里用的是 Helsinki-NLP 的 opus-mt 系列。模型参数量语言方向CPU 推理期末推荐度Helsinki-NLP/opus-mt-en-zh约 3 亿英译中可用高facebook/m2m100_418M4.18 亿多语种较慢中facebook/mbart-large-50-many-to-many-mmt6.8 亿多语种很慢低选 opus-mt 的原因有三个第一单语言方向相对专注英译中质量在这个体量下足够支撑课程展示第二CPU 上能跑不是所有答辩机器都有独显第三权重和缓存体积小下载快离线预演不容易出幺蛾子。M2M100 更适合「一个模型翻译多种语言」的加分方向如果项目要求展示多语种可以把它作为扩展项写进文档。4.2 翻译链路实现加载、分词、beam search 生成、解码的完整代码翻译链路的核心代码在源码包里是独立文件translation_demo.py核心逻辑如下from transformers import AutoTokenizer, AutoModelForSeq2SeqLM model_id Helsinki-NLP/opus-mt-en-zh tokenizer AutoTokenizer.from_pretrained(model_id) model AutoModelForSeq2SeqLM.from_pretrained(model_id) source [ Transformers make machine translation easy., The final project includes both basics and translation. ] # 编码翻译任务同样需要 padding 和 truncation enc tokenizer( source, return_tensorspt, paddingTrue, truncationTrue, max_length128 ) # 生成beam search 加长度惩罚 outs model.generate( **enc, num_beams4, max_new_tokens128, length_penalty0.6, no_repeat_ngram_size3, early_stoppingTrue ) # 解码并保存结果 for i, out in enumerate(outs): trans tokenizer.decode(out, skip_special_tokensTrue) print(f{source[i]}\n {trans}\n) with open(translations.txt, w, encodingutf-8) as f: for i, out in enumerate(outs): f.write(f{source[i]}\t{tokenizer.decode(out, skip_special_tokensTrue)}\n)生成参数是这段代码里最有答辩价值的部分。num_beams4表示 beam search 每步保留 4 个最优候选比贪心解码质量高但时间复杂度近似翻倍length_penalty0.6小于 1会对长句做惩罚让模型倾向输出更紧凑的译文no_repeat_ngram_size3禁止任意 3-gram 重复出现专门治「的的的」这种复读early_stoppingTrue表示所有 beam 都到结束符就停止不硬顶到 max_new_tokens。保存结果时用\t分隔原文和译文方便后面导入 Excel 或 pandas 做对照表。这里要提醒一句直接用tokenizer.decode(out)会带上pad和eos这些特殊 token影响阅读也不算 BLEU务必加skip_special_tokensTrue。4.3 BLEU 评估用 sacrebleu 打分时最容易错的两个点翻译质量不能光靠肉眼判断项目里用 sacrebleu 做了量化评估。sacrebleu 比手写 BLEU 脚本更规范内部处理了 n-gram 匹配和长度惩罚答辩老师一般认这个。import sacrebleu # references 必须是 list of list外层是每个句子的多组参考译文 refs [ [Transformers 让机器翻译变得简单。], [这个期末项目包含了基础应用和机器翻译。], ] hyps [ Transformer 让机器翻译变得简单。, 本项目包含基础应用和翻译。, ] bleu sacrebleu.corpus_bleu(hyps, refs) print(fBLEU: {bleu.score:.2f}) print(fn-gram 精确率: {bleu.precisions}) print(f长度惩罚因子: {bleu.bp})这里有两个常见错误。第一个refs传成 list of stringsacrebleu 会直接报错或得出错误结果它要求的结构是「每个句子对应一个参考译文列表」所以是两层嵌套第二个只拿 10 句话算 BLEU 没有任何区分度一个 n-gram 不匹配就掉好几分项目里建议至少准备 50 句对齐数据做评估。BLEU 分数本身也有解释成本两个句子如果措辞不同但语义一致n-gram 匹配不上分数就很低。所以期末项目里我额外保留了人工评估环节每句译文打印出原文、译文和参考译文三个字段答辩时可以解释「BLEU 偏低不代表译文错只是用词差异」。5. 答辩前必看的避坑清单缓存冲突、显存溢出与低质量译文5.1 现象config 报 already used 冲突 → 原因进程内注册表残留 → 解决换 kernel 且每次只跑一个模型有次我在 Jupyter Notebook 里连续加载四个模型做对比实验跑到第三个时报了一行看起来莫名其妙的错误xxx is already used by a transformers config, pick another name.。第一次遇到时我一度以为是模型权重下载坏了重新下了一遍还是同样的问题。原因是 transformers 在同一个 Python 进程内有全局配置注册表它以模型名作为键。你在一个 kernel 里反复实例化不同名字的 AutoConfig某些情况下旧配置没被释放新配置再注册时发现键名已被占用就从报错里透出这个「pick another name」。解决方式很简单换一个干净的 kernel 重新运行或者每个模型单独用一个脚本文件跑。源码包里每个 demo 都是独立脚本目的之一就是避开这个坑。5.2 现象长句翻译 CUDA out of memory → 原因beam search 展开量过大 → 解决按顺序降 batch、beams、max length机器翻译的显存翻车和分类不一样。分类任务 OOM 多是 batch 太大翻译任务还有个叠加因素beam search 的中间状态也要常驻显存。我遇到过 batch_size8、num_beams4、max_new_tokens128 三件套叠加直接把一张 8G 显卡打满报CUDA out of memory。显存占用的量级估算可以用这个公式beam search 峰值显存约等于batch_size * num_beams * max_new_tokens * hidden_size。beam 从 1 提到 4显存直接翻四倍这还没算输入编码的中间张量。遇到 OOM 我按固定顺序排查排查步骤操作效果第一步num_beams降到 1显存降到原来的四分之一先把流程跑通第二步batch_size降到 1再降一个数量级适合长句第三步max_new_tokens从 128 降到 64限制生成长度截断尾部计算第四步换更小的模型CPU 可跑的小模型副作用是翻译质量下降如果做完前三步还爆显存基本就是显存只有 2G 的机器直接换opus-mt的 mini 版本或用 CPU。期末场景不丢人跑通比跑快重要。5.3 现象译文重复、出现unk、BLEU 得 0 → 原因解码参数与评估格式 → 解决逐一修这组问题我会放在一起讲因为它们经常同时出现而且三个原因彼此独立。译文重复的特征是先正常输出一小段后面整个循环复读。原因是贪心解码或 beam 数量太少时模型陷入了 n-gram 循环而不自知。解决方法是代码里必须带no_repeat_ngram_size3配合early_stoppingTrue防止模型在结尾处来回打转。译文出现unk通常是分词器加载错了模型对应的 tokenizer或者模型权重和 tokenizer 版本不匹配。我踩过的一次是手动下载了词表文件覆盖到缓存目录结果词表对齐不上。解决方法是删掉对应模型的缓存目录重新用AutoTokenizer.from_pretrained(model_id)完整加载一遍。BLEU 得 0 要分两种情况看一种是评估脚本的 references 结构错了sacrebleu 需要 list of list另一种是译文和参考译文使用的语体差异太大字面完全没对上。前者改数据结构后者需要换更贴合的参考译文或者减少 n-gram 权重。遇到 BLEU0 先打印一行bleu.precisions如果前几项都是 0基本就是格式问题而不是模型问题。注意离线模式下如果报「尝试连接模型仓库」优先检查缓存目录里是否有完整快照而不是急着改网络配置。6. 用 Gradio 给翻译加一个演示界面答辩展示多两分钟6.1 三行 Gradio 代码把翻译函数变成可演示的 Web 页面文本代码只能展示结果交互界面能让你在答辩现场现场输入句子看实时翻译。Gradio 封装得很薄把翻译函数包一层就能本地起服务。import gradio as gr from transformers import pipeline translator pipeline(translation, modelHelsinki-NLP/opus-mt-en-zh) def translate_en2zh(text: str) - str: result translator(text, max_length128)[0] return result[translation_text] gr.Interface( fntranslate_en2zh, inputsgr.Textbox(lines3, placeholder输入英文句子), outputsgr.Textbox(label中文译文), title英译中 Demo, ).launch()这里 pipeline 的返回结果是列表每项是{translation_text: ...}。启动后浏览器打开本地地址即可访问。Gradio 的 share 参数走的是公网中转服务答辩现场网络不可控不建议依赖它本地演示最稳。6.2 更专业的展示方式对照表输出英中双语结果界面给观众看对照表给老师看。我把翻译结果和原文做成了对齐表格截图放进答辩 PPT 里比一堆文本输出直观得多import pandas as pd pairs list(zip(source, [tokenizer.decode(o, skip_special_tokensTrue) for o in outs])) df pd.DataFrame(pairs, columns[英文原文, 中文译文]) print(df.to_markdown()) df.to_csv(translation_table.csv, indexFalse, encodingutf-8-sig)encodingutf-8-sig是为了让生成的 CSV 用 Excel 打开不乱码这个细节踩过坑的人都知道。有对照表、有 BLEU 分数、有可交互的 Web 界面期末答辩的演示部分基本就完整了。源码包里已经按这套顺序排好了目录环境配置、基础应用、翻译实现、评估脚本README 里记录了每一步的命令和参数。我之前做演示型项目吃过「现场才发现权重没下载全」的亏从那以后每次交作业前都会强制走一遍断网预演先断网跑完全部脚本确认权重和缓存都在本地再考虑做界面。希望帮到你。本文还有配套的精品资源点击获取
返回列表