中文NER词汇信息融合:SoftLexicon+BERT+CRF实战与避坑
简介本资源面向中文自然语言处理学习者与课程设计开发者提供一套基于词汇信息融合的中文NER模型完整实现。项目以LEBERT为核心将词汇信息引入BERT并对比Bert-Softmax、Bert-Crf、LEBert-Softmax、LEBert-Crf四种组合在Resume、Ontonote、Msra、Weibo四个中文数据集上的表现适合用于模型复现、对比实验与毕业设计参考。压缩包共44个文件约12.97MB包含16个Python源码文件、设计报告Word文档、6张F1对比图、训练脚本、依赖清单与说明文档源码覆盖模型定义、CRF层、损失函数、数据处理与评估指标等模块并附带ner_data.zip数据集。已有493人学习下载。读者可据此获得从数据预处理、模型搭建到多数据集评测的完整流程理解词汇增强对NER效果的影响并借助报告与结果图快速完成实验复现与结果分析。1. 词汇信息融合做中文 NER为什么纯字符模型总在边界上翻车中文命名实体识别NER是很多 NLP 落地项目的第一道坎而「词汇信息融合」正是这几年在中文 NER 上被反复验证有效的一条路线。你如果只用字符级 BERT 做中文 NER大概率会遇到这样的场景模型把「北京大学第三医院」切成「北京 / 大学 / 第三 / 医院」实体边界飘忽不定或者把「长江大桥」里的「长江」单独识别成地名丢掉「大桥」这个后缀。这类错误不是数据量不够而是字符序列本身丢掉了「词」这一层信息——中文没有天然空格字符之间的组合边界对模型来说是个黑匣子。词汇信息融合要解决的就是这件事把分词工具或词典里已有的词边界信息注入到字符级序列标注模型里让模型在预测每个字符的标签时能「看到」这个字符属于哪些候选词、这些词在词典里的位置和权重。它适合谁适合已经跑通 BERTCRF 基线、但 F1 卡在 85 左右上不去的从业者也适合刚入门中文 NER、想直接学一条工业界主流方案的新手。下面从原理到代码把这条路线拆开讲清楚。2. 词汇信息融合的三种主流方案与选型理由2.1 为什么是「字符 词」双通道而不是直接分词后做词级 NER直接对分词结果做词级 NER 看起来更自然但实际落地会踩两个坑。第一分词错误会直接传导到 NER且无法回退——「南京市长江大桥」如果被切成「南京市 / 长江 / 大桥」词级模型就再也看不到「市长」这个跨词实体。第二中文 NER 的标注体系BIO/BIOES本身是按字符定义的词级模型需要额外做标签对齐工程复杂度高。字符级 词汇信息融合的思路是主干仍然是字符序列保证标注体系不变额外引入一个「词信息通道」把每个字符匹配到的候选词编码成向量再和字符向量融合。这样即使分词错了字符主干仍能兜底词信息只是增强而非替代。常见做法是 Lattice LSTM、SoftLexicon、以及基于注意力融合的 FLAT 三类下面用表格对比。方案核心机制优点代价Lattice LSTM在字符 LSTM 上并联词级 LSTM用门控融合词信息利用充分无法并行训练慢SoftLexicon把每个字符的候选词按 B/M/E/S 分组池化可并行易接入 BERT丢失词序信息FLAT把词转成 span用位置注意力融合保留词边界位置实现复杂度高我一般会推荐 SoftLexicon 作为第一版落地因为它对 BERT 友好、代码量小、调参少F1 通常能比纯字符 BERT 提升 1.53 个点。下面就以 SoftLexicon BERT CRF 为主线展开。2.2 SoftLexicon 的核心把候选词按位置分组池化SoftLexicon 的关键洞察是对每个字符它在词典中可能出现在词的开始B、中间M、结尾E或单独成词S。把这四类候选词分别收集起来各自做平均池化得到四个向量再拼接到字符向量上。这样模型既知道「这个字符能作为哪些词的开头」也知道「它能作为哪些词的结尾」边界信息就被显式编码了。具体流程分四步。第一步用分词工具或词典对训练语料做词匹配构建一个词表。第二步对每个字符扫描词表找出所有包含它的词按该字符在词中的位置归入 B/M/E/S 四组。第三步对每组内的词向量取平均得到四个池化向量。第四步把四个向量与字符向量拼接送入 BERT 编码。这里有个容易忽略的点词向量从哪来常见做法是用同一个 BERT 对词做编码取词首字符的向量作为词向量这样不需要额外训练词嵌入也避免了词表和 BERT 词表不一致的问题。2.3 用 Python 构建词表与 B/M/E/S 分组的最小实现下面这段代码是词表构建和分组逻辑的核心可以直接抄作业。假设你已经有一份分词后的语料每行是「字符 标签」格式分词结果单独存一份。# build_lexicon.py # 功能从分词结果构建词表并为每个字符生成 B/M/E/S 四组候选词索引 from collections import defaultdict def build_lexicon(word_list, max_word_len6): word_list: 分词后的词列表如 [南京, 市, 长江, 大桥] max_word_len: 过滤超长词避免噪声 返回: lexicon 字典key 是字符value 是包含该字符的词集合 lexicon defaultdict(set) for word in word_list: if len(word) max_word_len or len(word) 1: continue for ch in word: lexicon[ch].add(word) return lexicon def group_by_position(char, lexicon): 对单个字符把它匹配到的词按 B/M/E/S 分组 返回: dictkey 是 B/M/E/Svalue 是词列表 groups {B: [], M: [], E: [], S: []} for word in lexicon.get(char, []): idx word.index(char) if len(word) 1: groups[S].append(word) elif idx 0: groups[B].append(word) elif idx len(word) - 1: groups[E].append(word) else: groups[M].append(word) return groups # 示例 word_list [南京, 市, 长江, 大桥, 南京市, 长江大桥] lex build_lexicon(word_list) print(group_by_position(市, lex)) # 输出: {B: [], M: [], E: [南京市], S: [市]}逻辑说明build_lexicon把每个词拆成字符反向建立「字符 → 包含它的词」索引这是后续快速匹配的基础。group_by_position用word.index(char)判断字符在词中的位置注意这里假设字符在词中只出现一次中文 NER 场景下这个假设基本成立如果遇到「哈哈」这类叠词index会返回第一个位置需要额外处理。参数说明max_word_len建议设 6中文实体很少超过 6 个字过长的词往往是分词错误或噪声。如果你的领域有长实体如医学名词可以放宽到 8但要观察验证集 F1 是否下降。3. 把词汇信息接进 BERTCRF训练脚本与参数配置3.1 模型结构字符向量、词池化向量、CRF 解码的拼接方式模型整体分三层。第一层是 BERT 编码器输入是字符序列输出每个字符的上下文向量。第二层是词信息融合层把 B/M/E/S 四组池化向量拼到 BERT 输出上再过一个线性层降维。第三层是 CRF 解码层输出标签序列。这里有个工程细节BERT 的输出维度通常是 768四个池化向量如果也用 768 维拼接后是 768×53840 维直接送 CRF 会参数量爆炸。常见做法是先过一个Linear(3840, 768)降维再接Linear(768, num_tags)最后送 CRF。降维层可以用 ReLU 激活也可以不加我一般不加因为 CRF 本身会学转移约束。词向量的获取方式对每个候选词用 BERT 对词做一次前向取[CLS]位置的向量作为词向量。但这样每个词都要单独跑一次 BERT训练时开销大。更高效的做法是预先用 BERT 对所有词表里的词做一次编码存成word2vec.npy训练时直接查表。词表通常几千到几万一次编码几分钟就能完成。3.2 训练脚本数据加载、模型定义与 CRF 损失下面给出模型定义和训练循环的核心代码基于 PyTorch 和pytorch-crf。# model.py import torch import torch.nn as nn from transformers import BertModel from torchcrf import CRF class LexiconBertCRF(nn.Module): def __init__(self, bert_path, num_tags, word_emb_dim768, dropout0.1): super().__init__() self.bert BertModel.from_pretrained(bert_path) self.dropout nn.Dropout(dropout) # 字符向量 768 四组词向量 768*4 3840 self.fusion nn.Linear(768 * 5, 768) self.classifier nn.Linear(768, num_tags) self.crf CRF(num_tags, batch_firstTrue) def forward(self, input_ids, attention_mask, lexicon_vecs, tagsNone): input_ids: (B, L) 字符 id lexicon_vecs: (B, L, 4, 768) 每个字符的 B/M/E/S 池化向量 tags: (B, L) 标签训练时传入 outputs self.bert(input_ids, attention_maskattention_mask) char_vec outputs.last_hidden_state # (B, L, 768) # 拼接词信息 lex_flat lexicon_vecs.view(*lexicon_vecs.shape[:2], -1) # (B, L, 3072) fused torch.cat([char_vec, lex_flat], dim-1) # (B, L, 3840) fused self.fusion(fused) fused self.dropout(fused) emissions self.classifier(fused) # (B, L, num_tags) if tags is not None: loss -self.crf(emissions, tags, maskattention_mask.bool()) return loss else: return self.crf.decode(emissions, maskattention_mask.bool())逻辑说明lexicon_vecs的形状是(B, L, 4, 768)四个维度对应 B/M/E/S。view操作把它压成(B, L, 3072)再和字符向量拼接。CRF 的mask参数必须传attention_mask否则 padding 位置会参与转移计算导致 loss 异常。decode返回的是列表每个元素是序列的标签 id 列表需要自己转回标签名。参数说明dropout设 0.1 是常规起点如果训练集小于 5000 条可以提到 0.3。fusion层不加激活函数因为 CRF 前的 emissions 需要保留线性可分性。学习率建议 BERT 部分用 2e-5新增层用 1e-3用param_groups分开设置。3.3 数据预处理把 B/M/E/S 向量对齐到字符序列训练前需要把每个字符的 B/M/E/S 池化向量预先算好存成和input_ids同形状的张量。下面是对齐脚本。# prepare_data.py import numpy as np import torch def align_lexicon_vecs(chars, lexicon, word2vec, max_len128): chars: 字符列表如 [南, 京, 市] lexicon: build_lexicon 的输出 word2vec: dict词 - 768 维 numpy 向量 max_len: 序列最大长度 返回: (max_len, 4, 768) 的 numpy 数组 vecs np.zeros((max_len, 4, 768), dtypenp.float32) for i, ch in enumerate(chars[:max_len]): groups group_by_position(ch, lexicon) for j, key in enumerate([B, M, E, S]): words groups[key] if words: # 取组内词向量的平均 vecs[i, j] np.mean([word2vec[w] for w in words], axis0) # 如果组为空保持全零模型会学到「无候选词」的信号 return vecs逻辑说明对每个字符分别计算 B/M/E/S 四组的平均词向量。如果某组为空保持全零向量这本身是一个有效信号——模型会学到「这个字符没有作为词首的候选词」。max_len要和 BERT 的max_length一致超出部分截断不足部分补零。参数说明word2vec的 key 必须和lexicon里的词完全一致否则会 KeyError。建议在构建word2vec时用set(lexicon.values())展平后去重确保覆盖所有候选词。4. 避坑与排查词汇信息融合 NER 的 5 个血泪教训4.1 现象加了词信息后 F1 反而下降原因词表里混入了大量噪声词比如分词工具把「的」「了」这类停用词也切出来导致 B/M/E/S 分组里全是无意义词池化向量变成噪声。解决构建词表时过滤掉长度小于 2 的词除非是单字实体并统计每个词在语料中的频次去掉频次低于 3 的长尾词。我一般会保留一个min_freq3的阈值F1 能回升 1 个点左右。4.2 现象训练 loss 正常下降但验证集 F1 卡在 0.1 不动原因CRF 的mask传错了。如果attention_mask没有转成 bool或者传的是input_ids的 mask 而不是attention_maskCRF 会把 padding 位置也纳入转移路径导致解码时标签全乱。解决检查self.crf(emissions, tags, maskattention_mask.bool())这一行确保mask的形状是(B, L)且 dtype 是 bool。另外pytorch-crf的batch_first参数必须和输入一致否则维度对不上。4.3 现象词向量查表时 KeyError提示某个词不在 word2vec 里原因lexicon里的词和word2vec的 key 不一致。常见于分词工具输出的词带有空格或特殊符号而word2vec构建时做了清洗。解决在align_lexicon_vecs里加一层兜底word2vec.get(w, np.zeros(768))同时打印缺失词列表人工检查是否需要补充编码。更彻底的做法是构建word2vec时直接用lexicon里的词集合保证一一对应。4.4 现象显存溢出batch_size 只能设到 4原因lexicon_vecs的形状是(B, L, 4, 768)当L128、B16时这个张量本身就有 16×128×4×768×4 字节 ≈ 25MB加上 BERT 的激活值显存占用翻倍。解决把lexicon_vecs存成float16在模型里再转float32或者把 B/M/E/S 四组向量先降维到 256 维再拼接。我一般会在fusion层前加一个Linear(768, 256)对每组词向量单独降维显存能省 60%。4.5 现象推理时速度慢单条 128 字符要 200ms原因每次推理都要实时计算 B/M/E/S 池化向量而词向量查表和平均池化是 Python 循环无法并行。解决把词向量查表和池化逻辑用 NumPy 向量化或者预先对所有训练集和测试集的字符算好lexicon_vecs存成文件推理时直接加载。如果词典不大也可以把整个lexicon和word2vec转成 PyTorch 张量用torch.index_select做批量查表。5. 进阶技巧用对抗验证挑出真正有用的词表阈值词汇信息融合的效果高度依赖词表质量而词表质量又取决于分词工具和过滤阈值。与其拍脑袋设min_freq3不如用对抗验证Adversarial Validation来挑阈值。具体做法是把训练集和验证集的字符序列拼在一起训练一个二分类器判断每条样本来自训练集还是验证集。如果某个阈值下分类器很难区分AUC 接近 0.5说明这个词表让训练集和验证集的分布更一致泛化更好。下面是一个简化的对抗验证脚本用来对比不同min_freq下的词表质量。# adversarial_validation.py import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_auc_score def adversarial_score(train_feats, val_feats): train_feats, val_feats: (N, D) 特征矩阵这里用词表覆盖率作为特征 返回: AUC越接近 0.5 说明分布越一致 X np.vstack([train_feats, val_feats]) y np.array([0] * len(train_feats) [1] * len(val_feats)) clf LogisticRegression(max_iter1000) clf.fit(X, y) pred clf.predict_proba(X)[:, 1] return roc_auc_score(y, pred) # 示例用词表覆盖率作为特征 def coverage_feature(chars, lexicon): 计算每个字符在词表中的覆盖率 return np.array([len(lexicon.get(ch, [])) for ch in chars]).reshape(-1, 1) # 对比 min_freq1 和 min_freq3 for min_freq in [1, 3, 5]: lex build_lexicon_with_freq(word_list, min_freq) train_feat coverage_feature(train_chars, lex) val_feat coverage_feature(val_chars, lex) auc adversarial_score(train_feat, val_feat) print(fmin_freq{min_freq}, AUC{auc:.4f})逻辑说明coverage_feature把每个字符在词表中的候选词数量作为特征这个特征能反映词表的覆盖情况。adversarial_score用逻辑回归做二分类AUC 越接近 0.5说明训练集和验证集在这个特征上越难区分词表泛化性越好。实际使用时可以加入更多特征比如 B/M/E/S 各组的平均词频、词向量范数等。参数说明min_freq的候选值建议从 1 到 10 遍历步长 1。如果 AUC 在某个阈值后不再下降说明继续过滤会丢失有效信息。我一般会选 AUC 最低的那个阈值同时观察验证集 F1两者结合决定。这个技巧在数据量小、领域差异大的场景下特别有用能避免词表过拟合训练集。最后说个我自己的习惯每次改词表或融合方式一定先跑一遍对抗验证再看 F1。因为 F1 的波动有时候是随机种子导致的而对抗验证的 AUC 更稳定能帮你判断改动是否真的让数据分布更一致。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

转座子:从跳跃基因到基因组演化与遗传工具

转座子:从跳跃基因到基因组演化与遗传工具

1. 转座子到底是什么:从“跳跃基因”说起转座子(Transposable Element,简称TE),在分子生物学和基因组学领域里,有一个更形象的名字叫“跳跃基因”。它是一段能够在基因组中从一个位置移动到另一个位置的DNA…

2026/10/9 11:14:04 阅读更多 →
DeepSeek-VL微调CT报告生成:DICOM原生处理与结构化输出实战

DeepSeek-VL微调CT报告生成:DICOM原生处理与结构化输出实战

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

2026/10/9 11:14:04 阅读更多 →
我篡改了区块链?——链上存证的脆弱真相与防篡改实战

我篡改了区块链?——链上存证的脆弱真相与防篡改实战

做了几年区块链相关项目,我经常被问到同一个问题,而且往往是以一种略带挑衅的口吻抛出来的:“你说区块链不可篡改,那我要是改了怎么办?”更直接一点的版本就是标题这句——“我篡改了区块链”。这句话在技术圈里已经快…

2026/10/9 11:14:04 阅读更多 →

最新新闻

Cherry Studio本地AI知识库:免费Embedding与RAG实战

Cherry Studio本地AI知识库:免费Embedding与RAG实战

1. 为什么我要折腾一套私人AI知识库先说结论:我搭这套东西的起因特别朴素——受够了。受够了每次查自己攒了三年的技术笔记,还得靠CtrlF在几十个 Markdown 文件里翻;受够了把公司内部文档丢给在线 AI 时那种心里发毛的感觉;更受够…

2026/10/9 12:52:26 阅读更多 →
零成本搭建本地AI知识库:Cherry Studio与免费模型实战指南

零成本搭建本地AI知识库:Cherry Studio与免费模型实战指南

1. 为什么我要折腾一套私人AI知识库先说结论:我用 Cherry Studio 配合免费模型,搭了一套完全本地化、零成本的私人知识库,日常查资料、翻文档、写东西的效率至少翻了一倍。整个过程没花一分钱,也没碰任何需要付费的API额度。事情的…

2026/10/9 12:52:26 阅读更多 →
从Codex迁移到OpenWorkBuddy:Agent工作台架构与MCP实战

从Codex迁移到OpenWorkBuddy:Agent工作台架构与MCP实战

1. 从 Codex 到 OpenWorkBuddy 的迁移背景1.1 为什么我开始重新审视 Agent 工作台最早接触 Codex CLI 的时候,我的心态其实很简单:命令行里能直接调模型写代码、跑脚本、改文件,这已经比在网页对话框里来回粘贴强太多了。那段时间我几乎把 Co…

2026/10/9 12:52:26 阅读更多 →
2026程序员梗图大赛:需求变更的100种死法拆解与创作攻略

2026程序员梗图大赛:需求变更的100种死法拆解与创作攻略

“2026程序员梗图大赛”的消息一传出,我朋友圈里写代码的朋友们就集体沸腾了。再看比赛主题——产品需求变更的100种死法,我瞬间就明白了,这个选题负责人一定是个常年被需求按在地上摩擦的老兵。需求变更这个东西,对程序员来说就像…

2026/10/9 12:52:26 阅读更多 →
JavaWeb超市管理系统毕业设计:Servlet+JSP+JDBC分层实现与部署避坑指南

JavaWeb超市管理系统毕业设计:Servlet+JSP+JDBC分层实现与部署避坑指南

简介:一套基于 JavaWeb 的超市管理系统毕业设计项目,包含可运行源码与数据库脚本,适合计算机、通信、人工智能、自动化等相关专业学生、教师或从业者,作为毕业设计、课程设计或期末大作业参考,也可作为 Java 入门者的进…

2026/10/9 12:52:26 阅读更多 →
Loop Engineering实战:用Claude Code、Codex、Cursor搭建AI编程闭环

Loop Engineering实战:用Claude Code、Codex、Cursor搭建AI编程闭环

1. 从"写提示词"到"搭回路":Loop Engineering 到底在解决什么问题 如果你最近在折腾 Claude Code、Codex、Cursor 这类 AI 编程工具,大概率经历过这样一个阶段:一开始觉得"哇,一句话就能生成代码"&…

2026/10/9 12:51:26 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/9 6:17:20 阅读更多 →