Argilla 集成指南:用 Unstructured 采集非结构化网页文本,构建并微调摘要模型训练数据
【免费下载链接】argillaArgilla is a collaboration tool for AI engineers and domain experts to build high-quality datasets项目地址: https://gitcode.com/GitHub_Trending/ar/argilla
本篇技术指南围绕 Argilla 生态中与 Unstructured 库的官方集成教程展开:先由unstructured负责把 HTML、PDF 等非结构化文档切分为结构化元素,再通过stage_for_argilla将数据暂存为 Argilla 的 Text2Text 数据集,在 Argilla UI 中完成摘要标签的核验与修正,最后用 Hugging Facetransformers微调一个自定义摘要模型。读完本文,你将掌握"文档采集 → 标签校验 → 模型训练"的完整闭环,并能直接复现基于 ISW(Institute for the Study of War)每日战况报告训练摘要模型的实战案例。
一、集成概览:为什么用 Unstructured 收集非结构化数据
在构建自然语言处理训练集时,最耗时的一步往往不是训练本身,而是把散落在 HTML 页面、PDF、Word 文档中的非结构化内容清洗成可供标注和训练的结构化文本。过去通常需要借助lxml或beautifulsoup手写大量 HTML 解析逻辑,逐个定位目标区域,费时且脆弱。
Unstructured 正是为解决这一问题而生的文档解析库,也是 Argilla 官方推荐的、用于为数据集采集非结构化格式(如 HTML 文档和 PDF)的配套方案。它把一篇原始文档自动"分区"(partition)成带类型的语义元素(如Title、NarrativeText、ListItem),并提供了直接对接 Argilla 的暂存工具。结合 Argilla 的人工校验能力和transformers的微调能力,一个原本可能需要一周甚至更久的数据科学项目可以在几小时内完成。
官方教程将整个流程划分为三个紧密衔接的阶段,本文沿用该结构:
- Section 1:数据采集与暂存(Data Collection and Staging with
unstructured)——批量抓取网页,切分元素,抽取正文与摘要目标文本; - Section 2:标签校验(Label Verification with Argilla)——将数据暂存为 Argilla 数据集,上传后在 UI 中人工核验与修正;
- Section 3:模型训练(Model Training with
transformers)——把 Argilla 数据导出为datasets.Dataset,微调 T5 摘要模型并用于推理。
完整可运行的 Notebook 位于 training-textgeneration-unstructured.ipynb,本文的所有代码均直接取自该 Notebook。
二、环境准备:安装依赖并启动 Argilla
2.1 安装 Python 依赖
在 Notebook 中依次安装三个核心库。注意 Unstructured 在本教程中锁定了版本0.4.4,以保证教程代码与该版本 API 兼容:
%pip install argilla %pip install "unstructured==0.4.4" -qqq %pip install transformers datasets此外还需要requests、tqdm、nltk等辅助库。nltk需要额外下载两个模型资源(词性标注与分句模型),Unstructured 的元素切分依赖它们:
import calendar from datetime import datetime import re import time import requests from transformers import pipeline import tqdm from unstructured.partition.html import partition_html from unstructured.documents.elements import NarrativeText, ListItem from unstructured.staging.argilla import stage_for_argilla import nltk nltk.download('averaged_perceptron_tagger') nltk.download('punkt')2.2 启动 Argilla 服务
教程要求本地有一个正在运行的 Argilla 服务,官方提供两种主流部署方式:
- Hugging Face Spaces 一键部署:适合使用 Google Colab 等外部 Notebook 的场景,在 Spaces 上选择 Argilla 官方模板即可创建;
- Docker 快速启动镜像:适合在本地运行教程(注意此方式无法配合外部 Notebook 服务使用),详见 quickstart 指南。
2.3 初始化 Argilla 客户端
使用 Docker 快速启动镜像或 Hugging Face Spaces 部署时,需要通过rg.init显式传入服务地址与 API Key:
# Replace api_url with the url to your HF Spaces URL if using Spaces # Replace api_key if you configured a custom API key # Replace workspace with the name of your workspace rg.init( api_url="http://localhost:6900", api_key="owner.apikey", workspace="admin" )如果使用的是私有Hugging Face Space,还需要额外设置HF_TOKEN环境变量,并在初始化时通过extra_headers注入 Authorization 头:
# # Set the HF_TOKEN environment variable # import os # os.environ['HF_TOKEN'] = "your-hf-token" # # Replace api_url with the url to your HF Spaces URL # rg.init( # api_url="https://[your-owner-name]-[your_space_name].hf.space", # api_key="owner.apikey", # workspace="admin", # extra_headers={"Authorization": f"Bearer {os.environ['HF_TOKEN']}"}, # )rg.init与后续用到的rg.log、rg.load均来自 Argilla v1 时代的 Python SDK;在本仓库中,对应的数据集实现位于 argilla-v1/src/argilla_v1/client/datasets.py,客户端入口位于 argilla-v1/src/argilla_v1/client/client.py。
三、Section 1:用 Unstructured 完成数据采集与文本切分
3.1 构造报告 URL 列表
案例的数据源是 ISW 每日发布的俄罗斯进攻战役评估报告。利用 Python 标准库datetime与calendar,可以按日期批量生成报告 URL:
ISW_BASE_URL = "https://www.understandingwar.org/backgrounder/russian-offensive-campaign-assessment" def datetime_to_url(dt): month = dt.strftime("%B").lower() return f"{ISW_BASE_URL}-{month}-{dt.day}"urls = [] year = 2022 for month in range(3, 13): _, last_day = calendar.monthrange(year, month) for day in range(1, last_day + 1): dt = datetime(year, month, day) urls.append(datetime_to_url(dt))上述代码会生成 2022 年 3 月至 12 月每一天对应的报告 URL 列表。实际抓取时会遇到部分日期没有对应报告的情况,因此在批量采集阶段需要做健壮性判断。
3.2 用partition_html切分网页元素
拿到 URL 后,用requests拉取网页 HTML。关键在于:不需要任何手写解析器,只要调用一次partition_html,就能把整篇网页切分成带有语义类型的元素列表:
def url_to_elements(url): r = requests.get(url) if r.status_code != 200: return None elements = partition_html(text=r.text) return elementspartition_html是 Unstructured 的核心 API,其产物是若干类型的元素对象,教程中主要用到以下两类:
| 元素类型 | 语义 | 本案例中的作用 |
|---|---|---|
Title | 章节/文档标题 | 用于定位 "Key Takeaways" 小节 |
NarrativeText | 叙事性正文段落 | 作为摘要模型的输入文本 |
ListItem | 列表项 | 构成 "Key Takeaways" 的每一条要点 |
3.3 抽取目标摘要:get_key_takeaways
ISW 报告正文开头有一个固定的Key Takeaways小节,其中的要点列表正是摘要模型的目标文本(label)。借助 Unstructured 的元素分类,定位逻辑非常简单:先找到文本恰好为"Key Takeaways"的标题元素,然后持续收集其后的ListItem元素,直到列表结束:
def _find_key_takeaways_idx(elements): for idx, element in enumerate(elements): if element.text == "Key Takeaways": return idx def get_key_takeaways(elements): key_takeaways_idx = _find_key_takeaways_idx(elements) if not key_takeaways_idx: return None takeaways = [] for element in elements[key_takeaways_idx + 1:]: if not isinstance(element, ListItem): break takeaways.append(element) takeaway_text = " ".join([el.text for el in takeaways]) return NarrativeText(text=takeaway_text)以列表中的第 200 个 URL 为例,抽取结果是一段完整的要点摘要:
elements = url_to_elements(urls[200]) print(get_key_takeaways(elements))输出示例(节选):
Russian forces continue to prioritize strategically meaningless offensive operations around Donetsk City and Bakhmut over defending against continued Ukrainian counter-offensive operations in Kharkiv Oblast. Ukrainian forces liberated a settlement southwest of Lyman ...ISW 报告中 Key Takeaways 小节的实际形态如下图所示,它是摘要模型要学习生成的输出格式:
3.4 抽取正文输入:get_narrative
接下来抽取报告正文作为模型的输入文本。同样基于元素类型过滤:只保留长度超过 500 字符的NarrativeText段落,并顺手用正则清除文中的引用标记(形如[3]的角标,它们不是自然语言,会干扰摘要质量):
def get_narrative(elements): narrative_text = "" for element in elements: if isinstance(element, NarrativeText) and len(element.text) > 500: # NOTE: Removes citations like [3] from the text element_text = re.sub("\[\d{1,3}\]", "", element.text) narrative_text += f"\n\n{element_text}" return NarrativeText(text=narrative_text.strip())长度阈值500是教程中的经验值,作用是过滤掉过短的碎片段落,保证进入训练集的是信息完整的正文段落。
3.5 批量采集全部报告
将以上函数组合起来,遍历 URL 列表完成全量采集。这里刻意加入了time.sleep(1)限速,避免对目标站点造成过大的请求压力:
inputs = [] annotations = [] for url in tqdm.tqdm(urls): elements = url_to_elements(url) if url is None or not elements: continue text = get_narrative(elements) annotation = get_key_takeaways(elements) if text and annotation: inputs.append(text) annotations.append(annotation.text) # NOTE: Sleeping to reduce the volume of requests to ISW time.sleep(1)循环结束时,inputs与annotations两个列表即为"正文 → 摘要"的训练样本对,其中摘要由网页自带结构自动抽取获得,还需要人工校验。
四、Section 2:用 Argilla 完成标签校验与数据集管理
4.1 通过stage_for_argilla暂存数据
自动抽取的摘要可能存在噪声(如抓取到不完整的列表、包含非正文内容),因此在训练前需要人工把关。Unstructured 为此提供了专门的暂存组件stage_for_argilla,它会把元素列表自动转换为 Argilla 的DatasetForText2Text对象,并将输入文本与标注一一对应:
dataset = stage_for_argilla(inputs, "text2text", annotation=annotations)从源码结构看,stage_for_argilla生成的DatasetForText2Text正是 Argilla v1 SDK 中用于文本生成任务的数据集容器,其类定义与to_pandas、to_datasets等导出方法都集中在 argilla-v1/src/argilla_v1/client/datasets.py。
转换后的数据集可以直接以 DataFrame 形式预览,每一行包含text(正文输入)、annotation(自动抽取的摘要)、id、status(此时为Validated)等字段:
dataset.to_pandas().head()预览输出(节选):
| text | annotation | status | event_timestamp |
|---|---|---|---|
| Russian forces are completing the reinforcemen... | Russian forces are setting conditions to envel... | Validated | 2023-01-31 11:19:52.784880 |
| Russian forces resumed offensive operations in... | Russian forces resumed offensive operations ag... | Validated | 2023-01-31 11:19:52.784941 |
4.2 上传到 Argilla 服务端
确认数据无误后,调用rg.log把数据集上传到 Argilla 服务端。这一步要求 Argilla Server 正在后台运行:
rg.log(dataset, name="isw-summarization")上传成功后,Argilla UI 中会出现名为isw-summarization、任务类型为Text2Text的数据集,如下图所示:
4.3 在 UI 中核验与修正摘要
接下来进入人工校验环节:在 Argilla UI 中逐条打开记录,核验 Unstructured 自动抽取的摘要是否正确,必要时手动调整,随后标记为已验证(Validated)或丢弃。这种"机器预标注 + 人工复核"的协作模式,正是 Argilla 作为 AI 工程师与领域专家协作工具的核心价值所在——领域专家无需写代码即可参与训练数据质量把控:
五、Section 3:用 Transformers 微调摘要模型
5.1 导出训练数据
人工校验完成后,从 Argilla 加载数据集并导出为 Hugging Facedatasets.Dataset格式——这正是transformers的Trainer所要求的输入格式。to_datasets的实现位于 argilla-v1/src/argilla_v1/client/datasets.py:
training_data = rg.load("isw-summarization").to_datasets()5.2 选择模型并完成 Tokenization
教程选用t5-small作为基线模型,以控制 Notebook 的运行时长;实际使用中可以替换为更大的 T5 系列模型以追求更高质量的摘要效果:
model_checkpoint = "t5-small"from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)为序列到序列任务设置输入与输出的最大长度,并编写预处理函数:输入正文截断到 1024 token,目标摘要截断到 128 token,标签使用tokenizer.as_target_tokenizer()上下文进行目标侧编码:
max_input_length = 1024 max_target_length = 128 def preprocess_function(examples): inputs = [doc for doc in examples["text"]] model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True) # Set up the tokenizer for targets with tokenizer.as_target_tokenizer(): labels = tokenizer(examples["annotation"], max_length=max_target_length, truncation=True) model_inputs["labels"] = labels["input_ids"] return model_inputstokenized_datasets = training_data.map(preprocess_function, batched=True)5.3 配置训练参数并启动微调
加载序列到序列模型,配置Seq2SeqTrainingArguments。各关键参数的含义与取值如下:
| 参数 | 教程取值 | 含义 |
|---|---|---|
evaluation_strategy | "epoch" | 每个 epoch 结束时评估一次 |
learning_rate | 2e-5 | 学习率 |
per_device_train_batch_size | 16 | 每个设备上的训练批大小 |
per_device_eval_batch_size | 16 | 每个设备上的评估批大小 |
weight_decay | 0.01 | 权重衰减,用于正则化 |
save_total_limit | 3 | 最多保留 3 个检查点 |
num_train_epochs | 1 | 训练轮数 |
predict_with_generate | True | 评估时使用生成式解码(自回归生成摘要) |
fp16 | False | 关闭半精度训练 |
push_to_hub | False | 不推送到 Hub |
from transformers import AutoModelForSeq2SeqLM, DataCollatorForSeq2Seq, Seq2SeqTrainingArguments, Seq2SeqTrainer model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)batch_size = 16 model_name = model_checkpoint.split("/")[-1] args = Seq2SeqTrainingArguments( "t5-small-isw-summaries", evaluation_strategy = "epoch", learning_rate=2e-5, per_device_train_batch_size=batch_size, per_device_eval_batch_size=batch_size, weight_decay=0.01, save_total_limit=3, num_train_epochs=1, predict_with_generate=True, fp16=False, push_to_hub=False, )构造数据整理器(DataCollator)与Seq2SeqTrainer,然后启动训练:
data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)trainer = Seq2SeqTrainer( model, args, train_dataset=tokenized_datasets, eval_dataset=tokenized_datasets, data_collator=data_collator, tokenizer=tokenizer, )trainer.train()训练完成后保存模型:
trainer.save_model("t5-small-isw-summaries")5.4 推理:对未来的报告自动生成摘要
微调后的模型可以直接通过pipeline加载,并复用本文第三节的 Unstructured 辅助函数对未来的报告进行推断——数据采集管道与模型形成完整闭环:
summarization_model = pipeline( task="summarization", model="./t5-small-isw-summaries", )elements = url_to_elements(urls[200]) narrative_text = get_narrative(elements) results = summarization_model(str(narrative_text), max_length=100) print(results[0]["summary_text"])模型对示例报告的生成结果(节选):
Russian forces continue to attack Bakhmut and various villages near Donetsk City . the Russians are apparently directing some of the very limited reserves available in Ukraine to these efforts rather than to the vulnerable Russian defensive lines hastily thrown up . Russian sources claimed that Russian forces are repelled a Ukrainian ground attack on Pravdyne .六、流程回顾与工程化要点
把三个阶段串起来看,整条流水线是:
- 采集:
requests拉取 HTML →partition_html切分为带类型的语义元素; - 抽取:按
Title/ListItem定位 Key Takeaways 作为标签,按NarrativeText提取正文作为输入; - 暂存与上传:
stage_for_argilla转换为DatasetForText2Text→rg.log上传; - 人工校验:在 Argilla UI 中核验、修正或丢弃自动抽取的摘要,保证训练标签质量;
- 训练:
rg.load(...).to_datasets()导出 →Seq2SeqTrainer微调 T5; - 推理:复用 Unstructured 管道抓取新文档,交给微调模型生成摘要。
几个值得注意的工程要点:
- 版本兼容:教程将
unstructured锁定在0.4.4,新版本 API 可能有变化,复现时建议先按该版本运行,再评估升级; - 抓取礼貌性:批量采集务必加入限速(如
time.sleep(1)),并做好非 200 状态码、元素缺失等异常分支的兜底; - 数据质量优先:自动抽取的标签必然存在噪声,Argilla UI 的人工复核环节是训练集质量的关键保障,不应跳过;
- 可扩展方向:
partition_html只是 Unstructured 分区 API 之一,同一套暂存与标注思路可以平滑迁移到 PDF、DOCX 等其他非结构化格式的采集场景。
本教程对应的完整 Notebook 可在 training-textgeneration-unstructured.ipynb 中查看与运行;Unstructured 在 Argilla 文档库中的入口页面见 unstructured.md。若需深入理解 Argilla 数据集容器与导出方法的底层实现,可查阅 argilla-v1/src/argilla_v1/client/datasets.py。
【免费下载链接】argillaArgilla is a collaboration tool for AI engineers and domain experts to build high-quality datasets项目地址: https://gitcode.com/GitHub_Trending/ar/argilla
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考