news 2026/9/18 11:38:44

Argilla 基础技术实战:团队标注分配、预训练模型微调与 spaCy NER 探索

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Argilla 基础技术实战:团队标注分配、预训练模型微调与 spaCy NER 探索

Argilla 基础技术实战:团队标注分配、预训练模型微调与 spaCy NER 探索

【免费下载链接】argillaArgilla is a collaboration tool for AI engineers and domain experts to build high-quality datasets项目地址: https://gitcode.com/GitHub_Trending/ar/argilla

本篇文章围绕 Argilla 官方教程体系中的「Basics(基础)技术」三件套展开:如何为标注团队分配记录、如何用 Hugging Face 预训练模型"先预测、后标注、再微调"地构建领域分类器,以及如何把 spaCy NER 管道的预测灌入 Argilla 进行探索与对比。读完你可以掌握三条可立即复用的数据标注与模型迭代工作流,并理解其底层客户端 API(rg.logrg.loadrg.init)的实际调用方式。

本文对应的入口文档为 1_basics.md,三份可直接运行的 Jupyter Notebook 均位于 docs/_source/tutorials/notebooks/ 目录下,其中用到的 Argilla v1 客户端实现可在 argilla-v1/src/argilla_v1/client 中查阅源码。

这三门基础课解决什么问题

在 Argilla 的教程体系中,「Basics」被定位为帮助用户迈出第一步的入门技术。入口页通过三张卡片(详见各教程目录下的modal.md)概括了它们各自覆盖的 MLOps 环节、NLP 任务与技术栈:

教程MLOps 环节NLP 任务依赖库核心技巧
给标注团队分配记录Labelling(标注)TextClassification / TokenClassification / Text2TextArgilla团队协作与任务分配
用 Hugging Face 微调情感分类器Labelling + Training(标注与训练)TextClassification(sentiment)transformers预测-标注-微调闭环
探索与分析 spaCy NER 管道Labelling(标注)TokenClassification(NER)spaCy模型预测的探索与对比

三个教程的卡片文件分别位于 docs/_source/_static/tutorials/labelling-tokenclassification-basics/modal.md、docs/_source/_static/tutorials/training-textclassification-transformers-pretrained/modal.md 与 docs/_source/_static/tutorials/labelling-tokenclassification-spacy-pretrained/modal.md。

环境准备:先跑起一个 Argilla 服务

三个教程都假定你本地已经有一个可用的 Argilla 服务,官方提供两种主流的部署方式:

  • 部署到 Hugging Face Spaces:如果你希望在 Google Colab 等外部 Notebook 环境中运行教程,且有 Hugging Face 账号,可以在 Spaces 上一键部署(适合外部 Notebook 场景)。
  • 使用 Argilla 官方 quickstart Docker 镜像:如果你希望本地运行教程,推荐这种方式。注意它只支持在本地机器上运行,无法配合外部 Notebook 服务使用。镜像与部署配置可在 argilla-server/docker/ 目录下找到,更完整的部署选项参见 distribution.md。

三份 Notebook 都可以通过页面上方的 "Open in Colab" 按钮直接运行,或下载.ipynb文件在本地 Jupyter 环境中执行;涉及模型训练与推理的环节建议切换 GPU 运行时。

安装依赖并初始化客户端

以"分配记录"教程为例,先安装 Argilla 客户端及必要的第三方库:

%pip install argilla datasets==2.10.1 httpx==0.23.3 -qqq

随后导入并初始化客户端。若使用 Docker quickstart 镜像或 Hugging Face Spaces,需要通过URLAPI_KEY完成初始化:

import argilla as rg # 使用 Spaces 时,把 api_url 替换为你的 HF Spaces 地址 # 若配置了自定义 API key,同步替换 api_key rg.init( api_url="http://localhost:6900", api_key="admin.apikey" )

如果是私有的 Hugging Face Space,还需要先设置HF_TOKEN环境变量,并在初始化时携带额外的鉴权头:

import os os.environ['HF_TOKEN'] = "your-hf-token" rg.init( api_url="https://[your-owner-name]-[your_space_name].hf.space", api_key="admin.apikey", extra_headers={"Authorization": f"Bearer {os.environ['HF_TOKEN']}"}, )

「微调分类器」与「spaCy NER」两个教程的初始化参数略有不同:它们使用owner.apikey作为默认密钥,并显式指定默认工作区,例如rg.init(api_url="http://localhost:6900", api_key="owner.apikey", workspace="admin")

可选的遥测开关

教程还提供一段可选的匿名遥测代码(自 Argilla 1.20.0 起引入),用于帮助官方了解教程的使用情况;你完全可以选择跳过:

try: from argilla.utils.telemetry import tutorial_running tutorial_running() except ImportError: print("Telemetry is introduced in Argilla 1.20.0 and not found in the current installation. Skipping telemetry.")

遥测的完整机制参见 telemetry.md,服务端遥测实现位于 argilla-server/src/argilla_server/telemetry/。

技巧一:把记录分配给标注团队

实际项目中,标注通常由多人分担工作量。为了避免标注重叠(或反过来,为了刻意控制重叠以评估标注一致性),Argilla(v1.6.0 起)提供了两种策略:

  1. 单一数据集 + 元数据分配:所有成员共享同一个数据集,但每人被指派其中的一部分记录;
  2. 多数据集 + 个人工作区:把记录拆分到不同数据集,分别写入各标注者的个人工作区。

选择策略 1 的典型场景是:希望团队所有人都能看到完整数据集做探索,但在标注时聚焦于自己被分配的那部分——例如标注者还会基于全团队的标注结果编写弱标签规则。策略 2 更适合希望成员独立工作、互不可见他人记录的场景,例如事后需要计算标注者一致性(annotator agreement)。

注意:该教程仅适用于 Text2Text、Token Classification 与 Text Classification 三类数据集;Feedback 数据集的记录分配请参考 assign_records.md。

加载数据集

可以加载 Argilla 中已有的数据集,并通过查询过滤出指定状态(例如只取尚未标注、状态为Default的记录):

# 从数据集加载 status 为 "Default" 的记录 ds = rg.load("gutenberg_spacy-ner", query="status:Default")

也可以从头创建新数据集——下面用datasets库从 Hugging Face Hub 拉取同一份数据,再包装成 Argilla 的 Token Classification 数据集对象:

from datasets import load_dataset ds = rg.DatasetForTokenClassification.from_datasets( dataset=load_dataset('argilla/gutenberg_spacy-ner', split='train') )

数据集创建与加载的更多细节参见 create_dataset.md。

随机分配记录

首先获取将参与标注的用户列表。自 Argilla 1.11.0 起可直接使用rg.User.list(),并可按角色过滤出annotator

# 获取数据集所在(或即将所在)的工作区 ws = rg.Workspace.from_name("my_workspace") # 获取有权限访问该工作区的用户 # 注意:所有参与标注的用户都必须有该工作区的访问权限 # 可选:只保留 annotator 角色的用户 users = [u for u in rg.User.list() if u.role == "annotator"]

如果版本早于 1.11.0,则需要直接调用 API 获取用户列表,此时返回的是字典而非对象,取值方式相应变为users['username']

import httpx rg_client = rg.active_client().client auth_headers = {"X-Argilla-API-Key": rg_client.token} http = httpx.Client(base_url=rg_client.base_url, headers=auth_headers) # 使用 Argilla Client 发起请求 users = http.get("/api/users").json() # 可选:只保留 annotator 角色的用户 users = [u for u in users if u['role'] == 'annotator']

接下来(可选)打乱数据集以保证分配随机,然后把记录按"标注者数量"切成等长块,轮转分发给每位用户,存进一个username -> records的字典:

import random from collections import defaultdict # 不需要打乱时跳过本行 random.shuffle(ds) # 构建 username -> 记录列表 的分配字典 assignments = defaultdict(list) # 按用户数切成等长块,依次分配给每位用户 n = len(users) chunked_records = [ds[i:i + n] for i in range(0, len(ds), n)] for chunk in chunked_records: for idx, record in enumerate(chunk): assignments[users[idx].username].append(record)

方式一:把分配结果写入记录元数据

如果采用"单一数据集"策略,需要把分配结果保存为记录的元数据字段,再统一写入一个所有参与者都有权限的工作区:

assigned_records = [] for user, records in assignments.items(): for record in records: record.metadata['user'] = user assigned_records.append(record) # 将记录写入 Argilla rg.log( records=assigned_records, workspace='recognai', name='gutenberg_spacy-ner', tags={'with assignments': True} )

rg.log是 v1 客户端最核心的写入入口,其实现位于 argilla-v1/src/argilla_v1/client 下的 API 封装中,负责把记录批量提交到服务端。

方式二:按用户拆分到个人工作区

如果采用"多数据集"策略,则为每位用户在其个人工作区中单独写入一份数据集:

for user, records in assignments.items(): rg.log( records=records, workspace=user, name='gutenberg_spacy-ner' )

这种方式下,标注者应使用annotator角色(无法访问彼此的工作区),项目负责人则使用admin角色以便管理项目下所有数据集。用户角色与权限管理参见 user.md 以及 users.md。

技巧:如果计划让多个用户标注同一条记录,建议在拆分数据集之前为每条记录加上唯一 ID。这样后处理阶段可以通过 ID 关联同一记录的多份标注。

在 UI 中按分配结果标注

方式一的使用体验:标注者打开数据集后,在Metadata标签页选择自己的用户名并点击Filter按钮,即可自动过滤出分配给自己的记录:

方式二则更简单:标注者直接打开自己工作区中的数据集开始标注即可。元数据过滤的更多用法参见 filter_dataset.md。

技巧二:用预训练模型引导微调——零标注数据起步的情感分类器

第二个基础教程演示了完整的predict → log → label → fine-tune闭环:从一个开箱即用的预训练情感分类器出发,先让模型"预标注",再由人工修正,最后用标注结果微调出面向自身领域的分类器。

为什么需要"面向领域"的微调

大多数微调教程都假设你已经拥有一份训练集(例如 IMDB 情感分析)。但在实际项目中,你真正想要的是针对自己用例的模型——NLP 模型在"域外数据"上性能通常会明显下降:在电影评论(IMDB)上预训练的情感分类器,处理银行用户请求时的表现并不理想。

因此本教程以银行业务场景为例:数据来自 Banking 77 数据集(银行在线客服查询及其意图标注),起点模型则是 Hugging Face Hub 上非常流行的distilbert-base-uncased-finetuned-sst-2-english(基于 SST-2 情感基准微调的 DistilBERT)。整个工作流如下图所示:

流程共两轮迭代:第一轮用预训练模型预测并标注出首批训练集 → 微调出领域模型;第二轮用微调后的模型继续预测剩余数据,人工修正后再扩充训练集、再次微调。

加载数据与模型,并定义标注策略

先把 Banking 77 数据集一分为二,一半用于首轮标注,另一半留到后续迭代:

from datasets import load_dataset, load_metric, concatenate_datasets import numpy as np banking_ds = load_dataset("PolyAI/banking77") to_label1, to_label2 = ( banking_ds["train"].train_test_split(test_size=0.5, seed=42).values() )

再加载预训练情感分类器,并在一条例子上做一次试运行:

from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification from transformers import TrainingArguments, Trainer sentiment_classifier = pipeline( model="distilbert-base-uncased-finetuned-sst-2-english", task="sentiment-analysis", top_k=None, ) to_label1[3]["text"], sentiment_classifier(to_label1[3]["text"])

教程中该模型把示例"I have contacted the seller for a refund..."判为NEGATIVE(得分约 0.993),但按照教程定义的标注策略,这类"一般性咨询问题"应标为POSITIVE——这正是后续要教会模型的差异。

教程还特别强调了两点标注设计原则:

  • 标签体系要与任务对齐:情感分析是 NLP 中最主观的任务之一,可建模为连续值或 2 个以上离散标签(含不同程度)。本教程沿用预训练模型的原始方案,仅使用POSITIVE/NEGATIVE两个标签(也可加入NEUTRAL)。
  • 必须事先定义标注指南(annotation guidelines):明确"什么样本该标什么标签",例如"一般性咨询问题标 POSITIVE,表达问题/投诉的标 NEGATIVE",这是保证标注质量的前提。

第 1 步:跑模型、写日志(predict + log)

datasets库的map对整份原始数据做批量预测(想快速试验可先加.select(range(10))只取 10 条):

def predict(examples): return {"predictions": sentiment_classifier(examples["text"], truncation=True)} # 小样本快速测试可改为:to_label1.select(range(10)).map(...) to_label1 = to_label1.map(predict, batched=True, batch_size=4)

如果不想自己跑预测,也可以直接从 Hub 加载已带预测结果的记录:load_dataset("argilla/sentiment-banking", split="train")

接着把每条样本包装成TextClassificationRecord——文本、意图元数据、模型预测及prediction_agent(记录预测来源,便于后续在 UI 中按模型筛选)——并写入 Argilla:

records = [] for example in to_label1.shuffle(): record = rg.TextClassificationRecord( text=example["text"], metadata={ "category": example["label"] }, # 记录意图类别,便于按意图探索 prediction=[(pred["label"], pred["score"]) for pred in example["predictions"]], prediction_agent="distilbert-base-uncased-finetuned-sst-2-english", ) records.append(record) rg.log(name="labeling_with_pretrained", records=records)

第 2 步:探索与标注(label)

打开 Argilla 标注 UI,先评估预训练模型在本数据上的表现。教程中的观察是:预训练分类器把 5001 条记录中的 4835 条判为NEGATIVE,明显失衡;用Predictions / Predicted as:过滤器筛出被预测为POSITIVE的样本,会发现不少与标注策略相悖的例子。

Argilla 提供搜索驱动的标注 UI,支持全文检索搜索过滤器以及Elasticsearch 查询 DSL做高级查询,特别适合稀疏数据集、多标签任务或类别不均衡场景。官方推荐的标注节奏是:

  1. 先顺序标注:不使用搜索功能,从头到尾逐条标注,这样首批样本的分布与数据集整体一致;
  2. 再定向标注:对数据有了感觉后,用过滤器和搜索针对特定标签补齐样本——例如先修正所有被判为POSITIVE的记录,再挑若干NEGATIVE样本。

按此流程标注约 5% 的数据(200 余条),即可进入第一次微调。

第 3 步:用 Trainer 微调

query="status:Validated"从 Argilla 拉回已标注记录(Validated即已标注状态),转成训练集并做 tokenize:

rb_dataset = rg.load(name="labeling_with_pretrained", query="status:Validated") rb_dataset.to_pandas().head(3) # 生成带数值标签的 Hugging Face 数据集 train_ds = rb_dataset.prepare_for_training() tokenizer = AutoTokenizer.from_pretrained( "distilbert-base-uncased-finetuned-sst-2-english" ) def tokenize_function(examples): return tokenizer(examples["text"], padding="max_length", truncation=True) tokenized_train_ds = train_ds.map(tokenize_function, batched=True) # 按 8:2 划分训练集与评估集 train_dataset, eval_dataset = tokenized_train_ds.train_test_split( test_size=0.2, seed=42 ).values()

加载模型并配置Trainer(每个 epoch 评估一次,每 30 步记录一次日志,以 accuracy 作为指标):

model = AutoModelForSequenceClassification.from_pretrained( "distilbert-base-uncased-finetuned-sst-2-english" ) training_args = TrainingArguments( "distilbert-base-uncased-sentiment-banking", evaluation_strategy="epoch", logging_steps=30, ) metric = load_metric("accuracy") def compute_metrics(eval_pred): logits, labels = eval_pred predictions = np.argmax(logits, axis=-1) return metric.compute(predictions=predictions, references=labels) trainer = Trainer( args=training_args, model=model, train_dataset=train_dataset, eval_dataset=eval_dataset, compute_metrics=compute_metrics, ) trainer.train()

prepare_for_training是 Argilla 记录集对象提供的能力,负责把 Argilla 记录规整为transformers/datasets可消费的训练格式。

第 4 步:对比新旧模型

用微调后的模型建一条新 pipeline,与预训练模型在同一条样本上对比:

finetuned_sentiment_classifier = pipeline( model=model.to("cpu"), tokenizer=tokenizer, task="sentiment-analysis", return_all_scores=True, ) finetuned_sentiment_classifier( "I need to deposit my virtual card, how do i do that." ), sentiment_classifier("I need to deposit my virtual card, how do i do that.")

可以看到:微调后的模型把这类一般性问题判为POSITIVE,而预训练模型仍判为NEGATIVE;而对于真正的问题类样本(如"Why is my payment still pending?"),两个模型都能正确判定——说明领域微调确实修正了偏差。

第 5~7 步:第二轮迭代

第二轮开始:用query="status:Default"加载剩余未标注记录,改用微调后的模型批量预测,并写入新的数据集labeling_with_finetuned

rb_dataset = rg.load(name="labeling_with_pretrained", query="status:Default") def predict(examples): texts = [example["text"] for example in examples["inputs"]] return { "prediction": finetuned_sentiment_classifier(texts), "prediction_agent": ["distilbert-base-uncased-banking77-sentiment"] * len(texts), } ds_dataset = rb_dataset.to_datasets().map(predict, batched=True, batch_size=8) records = rg.read_datasets(ds_dataset, task="TextClassification") rg.log(records=records, name="labeling_with_finetuned")

回到 UI 探索第二轮预测:微调模型的预测已更贴合标注策略。接下来用预测分数过滤不确定性高的样本、并分别修正被预测为POSITIVE/NEGATIVE的样本,补充约 80 条高信息量标注(约占原始数据 2%)。

最后把新标注并入原训练集,重新从预训练权重出发微调并保存模型:

rb_dataset = rg.load("labeling_with_finetuned") train_ds = rb_dataset.prepare_for_training() tokenized_train_ds = train_ds.map(tokenize_function, batched=True) train_dataset = concatenate_datasets([train_dataset, tokenized_train_ds]) model = AutoModelForSequenceClassification.from_pretrained( "distilbert-base-uncased-finetuned-sst-2-english" ) trainer = Trainer( args=training_args, model=model, train_dataset=train_dataset, eval_dataset=eval_dataset, compute_metrics=compute_metrics, ) trainer.train() model.save_pretrained("distilbert-base-uncased-sentiment-banking")

整个教程的核心可概括为两轮predict → log → label循环:先靠预训练模型降低冷启动标注成本,再用"模型预测 + 人工修正"的方式持续扩充高质量训练集。除手工标注外,还可以与弱监督、主动学习等方法组合(例如教程末尾提到的 ModAL 主动学习、基于 Sentence Transformers 的弱监督等,均在 notebooks 目录 下可找到对应教程)。

技巧三:探索与分析 spaCy NER 管道

第三个基础教程把视角转向序列标注:把 spaCy 命名实体识别(NER)管道的输出灌入 Argilla,用于评估预训练模型、定位高频错误(开发期与生产期皆可)、在 Argilla 标注模式下持续改进管线,以及借助 Kibana 集成监控模型预测。

准备数据与模型

教程使用Gutenberg Time数据集(从 Project Gutenberg 全文小说中提取的显式时间引用,共 52183 部小说的抽取片段),流式加载前几条样本:

from datasets import load_dataset import pandas as pd import spacy from tqdm.auto import tqdm dataset = load_dataset("gutenberg_time", split="train", streaming=True) # 查看 train 集合的前 5 条样本 pd.DataFrame(dataset.take(5))

安装并下载两个 spaCy 模型——一个基于 transformer 的高精度模型和一个轻量模型,用于后续对比:

%pip install argilla -qqq %pip install torch -qqq %pip install datasets "spacy[transformers]~=3.0" protobuf -qqq !python -m spacy download en_core_web_trf !python -m spacy download en_core_web_sm

把 NER 预测灌入 Argilla

对前 50 条样本运行en_core_web_trf,提取 token 与实体,包装成TokenClassificationRecord(实体格式为(label, start_char, end_char)),最后rg.log写入数据集:

nlp = spacy.load("en_core_web_trf") # 创建空记录列表用于存放所有记录 records = [] # 遍历 Gutenberg 数据集的前 50 条样本 for record in tqdm(list(dataset.take(50))): # 只需要每条样本的文本 text = record["tok_context"] # 创建 spaCy Doc doc = nlp(text) # 实体标注 entities = [(ent.label_, ent.start_char, ent.end_char) for ent in doc.ents] # 预分词的输入文本 tokens = [token.text for token in doc] # Argilla TokenClassificationRecord 列表 records.append( rg.TokenClassificationRecord( text=text, tokens=tokens, prediction=entities, prediction_agent="en_core_web_trf", ) ) rg.log(records=records, name="gutenberg_spacy_ner")

打开 Argilla 中的gutenberg_spacy_ner数据集,可以对该模型的预测做以下操作:

  • 按实体类型过滤记录;
  • 查看每种实体最常见的表层形式(mentions)——例如时长(duration)实体的 mention 可能是 "1 month",这对错误分析非常有用,能快速暴露问题实体类型;
  • 全文检索定位包含特定词的记录;
  • 校验、采纳或拒绝具体实体标注,从而构建新训练集。

想跳过 spaCy 计算阶段?可以直接从 Hub 加载教程预生成的 Argilla 记录,例如records = rg.read_datasets(load_dataset("argilla/gutenberg_spacy_ner", split="train"), task="TokenClassification"),再rg.log进 web app 继续后续步骤。

用轻量模型对比,并在更难的域外数据上验证

en_core_web_sm重复同一流程,这次跑 10000 条样本,并写入同一个数据集gutenberg_spacy_ner(标注prediction_agent="en_core_web_sm"):

nlp = spacy.load("en_core_web_sm") records = [] for record in tqdm(list(dataset.take(10000))): text = record["tok_context"] doc = nlp(text) entities = [(ent.label_, ent.start_char, ent.end_char) for ent in doc.ents] tokens = [token.text for token in doc] records.append( rg.TokenClassificationRecord( text=text, tokens=tokens, prediction=entities, prediction_agent="en_core_web_sm", ) ) rg.log(records=records, name="gutenberg_spacy_ner")

回到 UI,可以通过predicted by过滤器(来源于prediction_agent参数)只看某个模型的预测,从而在同一数据集内直接对比两个模型的表现:

接下来用更"域外"的 IMDB 影评数据集做压力测试——它与 spaCy 预训练数据分布差异更大,更容易暴露问题。教程对 IMDB test 集前 5000 条运行en_core_web_sm并写入imdb_spacy_ner

imdb = load_dataset("imdb", split="test") records = [] for record in tqdm(imdb.select(range(5000))): text = record["text"] doc = nlp(text) entities = [(ent.label_, ent.start_char, ent.end_char) for ent in doc.ents] tokens = [token.text for token in doc] records.append( rg.TokenClassificationRecord( text=text, tokens=tokens, prediction=entities, prediction_agent="en_core_web_sm" ) ) rg.log(records=records, name="imdb_spacy_ner")

在 IMDB 上探索发现:Person 实体最常见的 mention 中出现了gore(电影类型)与Oscar(奖项)这类典型误判,直观印证了针对特定领域进行微调的必要性——利用过滤器和搜索框可以逐条核查这些错误。

附:把数据集回推 Hub 做版本管理

教程附录演示了如何把 Argilla 数据集一键推送回 Hugging Face Hub,从而对数据集进行版本化:

records = rg.load("gutenberg_spacy_ner") records.to_datasets().push_to_hub("<name of the dataset on the HF Hub>")

这一能力对应 v1 客户端的数据集 IO 封装,相关实现可参考 argilla-v1/src/argilla_v1/client 中的to_datasets/read_datasets逻辑。

小结:三条可复用、可组合的基础工作流

回到 1_basics.md 的定位——"Basics" 为初学者铺开三张最小可用的地图:

  • 标注管理:通过metadata['user']或拆分工作区两种方式分配记录,配合 UI 的元数据过滤,让团队协作标注有序、可控、可度量;
  • 模型引导标注:用pipeline预测 +rg.TextClassificationRecord(prediction=..., prediction_agent=...)+rg.log建出"带预测的训练集",人工修正后prepare_for_training()直接接上 Hugging FaceTrainer,并支持多轮迭代扩充;
  • 预测探索与对比:把 spaCy 等第三方模型的预测统一灌入 Argilla,借助实体类型过滤、mentions 统计与predicted by过滤器做错误分析、模型对比与领域微调决策。

三者覆盖了数据标注生命周期中最常见的三个入口:分工冷启动评估。理解这三条工作流后,再前往 techniques 目录 下的其他进阶教程(弱监督、主动学习、可解释性等)时,就能在同一个数据底座上自由组合——因为无论使用哪种技巧,数据进出的方式始终是rg.logrg.load这对基础原语。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 11:38:33

Visual Studio 2022安装到非C盘全指南:从路径设置到符号链接

相信很多人跟我一样&#xff0c;电脑买回来C盘就只分了100G&#xff0c;Windows系统更新加上各种软件的默认缓存&#xff0c;三下五除二就给塞满了。这时候再装Visual Studio 2022&#xff0c;点击安装界面那个“更改”按钮&#xff0c;你会发现C盘还是咔咔往下掉几十个G。原因…

作者头像 李华
网站建设 2026/9/18 11:34:12

WRF-CMAQ从零配置到跑通测试案例:环境搭建与编译运行全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 11:34:03

Python 基础语法(2)

顺序语句 默认情况下, Python 的代码执行顺序是按照从上到下的顺序, 依次执行的. print("1") print("2") print("3")执行结果一定为 “123”, 而不会出现 “321” 或者 “132” 等. 这种按照顺序执行的代码, 我们称为 顺序语句.这个顺序是很关键…

作者头像 李华
网站建设 2026/9/18 11:33:02

M2DGR多模态SLAM数据集:地面机器人退化场景评测与实战

去年年底接了个室内外混合场景的巡检机器人项目&#xff0c;客户要求方案在电梯、走廊、地下车库、园区道路这几类环境里都能稳定定位&#xff0c;我第一反应是找数据集做离线验证——毕竟真车跑一遍的成本太高&#xff0c;出了问题还复现不了。翻了一圈发现自己手里的存货都不…

作者头像 李华