从朴素贝叶斯到N-gram:理清语言模型核心原理与Python实现
很多人学NLP的时候会把朴素贝叶斯和N-gram语言模型当成两个八竿子打不着的东西一个做分类一个给句子打分或者生成文本。实际上它们背后是同一个核心问题——怎么计算一个词序列出现的概率。朴素贝叶斯用“词与词独立”把这个问题简化到极致而N-gram语言模型则是把独立性假设逐步放开考虑词和前面若干词的关系。搞懂这条线你才算真正理解了为什么语言模型叫“模型”。这一篇我按自己折腾这条路时的顺序复盘从朴素贝叶斯做文本分类时的概率拆解到bigram/trigram的参数估计再到平滑和困惑度最后给一套可以直接跑的Python实现。适合刚看完朴素贝叶斯、想往语言模型走的人也适合那些会在分类任务里用ngram特征但没搞懂原理的同学。1. 为什么把朴素贝叶斯和语言模型放在一起讲1.1 朴素贝叶斯做文本分类时其实在估计一个“类别条件下的词序列概率”先回到朴素贝叶斯的核心公式。对一个文档 D 和一个类别 C我们想算后验概率 P(C|D)用贝叶斯定理展开P(C|D) ∝ P(C) * P(D|C)这里的 P(D|C) 是关键。如果我们把文档 D 看作由词 w1, w2, ..., wm 组成的序列那么 P(D|C) 就是在类别 C 下“生成”出这个完整词序列的概率。朴素贝叶斯做了一件事强行假设这些词之间互相独立于是概率拆成每个词概率的连乘P(D|C) P(w1|C) * P(w2|C) * ... * P(wm|C)分类的时候模型为每个类别算一份这样的连乘再乘上先验 P(C)最后比较大小。哪个类别的后验概率高文档就分到哪个类别。注意这里有个容易被忽略的点P(w_i|C) 的全体其实就是类别 C 对应的一元词分布。换句话说每个类别都有一份自己的“词概率表”也就是一个最简单的 unigram 语言模型。朴素贝叶斯分类器在做的事情本质上是“用每个类别的语言模型分别给文档打分谁的分数高就选谁”。这就是它和语言模型天然绑定的原因。很多资料只把朴素贝叶斯讲成条件概率和独立假设的练习题没点破这一层。你一旦意识到文本分类里的似然项就是类别语言模型后面理解N-gram就顺了。1.2 一旦放开“词与词独立”你就在向语言模型靠近独立假设最大的硬伤是完全没有语序。我举一个最夸张的例子“我喜欢她”和“她喜欢我”在朴素贝叶斯看来概率完全相同因为两个句子包含的词集合一样只是顺序不同。可在情感分类、意图识别这些任务里语序往往直接决定结果。“我不喜欢”和“我喜欢不”已经不是同一个意思了。如果我们不再假设词与词独立而是把 P(D|C) 用链式法则原原本本展开就会得到P(w1, w2, ..., wm|C) P(w1|C) * P(w2|w1, C) * P(w3|w1, w2, C) * ...这个式子本身没有做任何近似条件概率越往后依赖的历史越长。问题是这种完整模型参数太多任何语料都撑不起来。于是需要引入马尔可夫假设一个词的概率只依赖前 n-1 个词。当 n2 时P(w_i | w_{i-1})这就是 bigram当 n1 时退化成 P(w_i)和朴素贝叶斯完全一致。所以从朴素贝叶斯到N-gram并不是换了一个毫不相关的新知识。它只是把朴素贝叶斯扔掉的信息一点点捡了回来。打个比方把句子看成一条铁链朴素贝叶斯把每个链环单独扔在桌上数重量N-gram则开始看相邻两个环是怎么扣上的。扣得越紧能捕捉的语言规律就越多。2. N-gram语言模型的数学定义与参数估计2.1 从链式法则到马尔可夫假设一个词序列 w1 到 wm 的联合概率用链式法则可以精确写成条件概率连乘。但完整条件概率 P(w_i | w1...w_{i-1}) 的每一项都依赖前面所有词参数数量随句子长度指数增长根本没法估计。马尔可夫假设就是在这里介入的假设当前词只和前面 n-1 个词有关更早的信息可以忽略。通用的N-gram公式可以写成P(w_i | w_{i-n1}, ..., w_{i-1})n1unigramP(w_i)词之间完全独立。n2bigramP(w_i | w_{i-1})只看前一个词。n3trigramP(w_i | w_{i-2}, w_{i-1})看前两个词。这里的“n”指一个词加上它看的上下文词总数。bigram其实是“2-gram”上下文长度为1trigram上下文长度为2。很多人刚学时容易把bigram理解成“两个词一组”这个说法没错但在概率公式里要清楚它估计的是“已知前一个词下一个词是什么”的条件概率。N-gram语言模型没有花哨的神经网络结构它纯粹靠计数。给定一个训练语料统计每个n-gram出现的次数然后归一化成概率。这种方式简单、可解释性强到今天依然是很多任务里可靠的概率估计基准。2.2 最大似然估计与一个具体的计数例子参数估计最常用的是最大似然估计。对于bigram条件概率的估计公式是P(w_i | w_{i-1}) count(w_{i-1}, w_i) / count(w_{i-1})就是看前一个词 w_{i-1} 总共出现了多少次其中后面跟着 w_i 出现了多少次。除法得到的就是“从 w_{i-1} 转移到 w_i”的频率。我拿一个极小的语料走一遍这样后面实现代码时心里踏实。假设语料是两个句子我 喜欢 学习我 喜欢 运动先把句子加上开始符s和结束符/s得到我 喜欢 学习我 喜欢 运动统计 unigram 和 bigram 次数count(我) 2count(喜欢) 2count(学习) 1count(运动) 1count() 2count( ) 2bigram 计数count(, 我) 2count(我, 喜欢) 2count(喜欢, 学习) 1count(学习, ) 1count(喜欢, 运动) 1count(运动, ) 1然后算条件概率P(喜欢 | 我) 2 / 2 1P(学习 | 喜欢) 1 / 2 0.5P(运动 | 喜欢) 1 / 2 0.5P(运动 | 我) 0 / 2 0这里已经能看到问题如果测试句子里出现“我 运动”bigram概率是0整个句子概率直接被清零。这个现象叫数据稀疏是N-gram语言模型最核心的痛点。后面平滑方法解决的就是它。2.3 和朴素贝叶斯的关系一个是特例一个是扩展刚才例子里的 unigram其实就是朴素贝叶斯里那个 P(D|C) 的估计方式。如果每个类别下各自做一个bigram模型那就是“bigram朴素贝叶斯”文本分类时也把语序信息用上了。我整理了一张表方便对照模型概率分解核心假设典型应用unigram / 朴素贝叶斯∏ P(w_i)词间独立文本分类、垃圾邮件过滤bigram∏ P(w_i | w_{i-1})一阶马尔可夫句子打分、中文分词、拼写纠错trigram∏ P(w_i | w_{i-2}, w_{i-1})二阶马尔可夫语音识别、机器翻译候选排序从表中能看出朴素贝叶斯不是孤立存在的小技巧它是语言模型家族里假设最强、参数最少的一份子。反过来N-gram语言模型也不是只能做生成或评分它完全可以替代朴素贝叶斯里的独立假设变成更强大的分类器。很多开源工具里的N-gram文本分类器本质就是在每个类别下分别训练N-gram模型然后用贝叶斯定理选择后验最高的类别。理解了这个对应关系你就同时掌握了两类算法。3. 从零实现bigram语言模型训练、平滑与困惑度3.1 预处理与ngram计数理论说多了容易飘直接动手写一个。我用中文短句作为语料以空格分词。真实项目里中文要先用分词工具切好但原理是一样的。import math from collections import defaultdict corpus [ 我 喜欢 学习 自然语言处理, 我 喜欢 运动, 学习 自然语言处理 需要 耐心, 运动 让 我 快乐 ]预处理这一步很容易被忽略。必须给每个句子加上s和/s否则开头的词没有上下文结尾的词也没有后文模型会少学很多边界模式。比如s后面接“我”的频率直接决定了句子以什么词开头的概率。统计时要区分 unigram 和 bigram 的键。bigram 用二元组 (w_prev, w) 做键unigram 用词本身做键。def build_ngram_counts(sentences): unigram_counts defaultdict(int) bigram_counts defaultdict(int) for sent in sentences: tokens sent.split() tokens [s] tokens [/s] for i, tok in enumerate(tokens): unigram_counts[tok] 1 if i 1: bigram_counts[(tokens[i-1], tok)] 1 return unigram_counts, bigram_counts这个函数返回两张计数字典。后面所有概率都从这两张表算不需要再碰原始语料。3.2 加入平滑的完整代码如果不做平滑测试时遇到一个未出现过的bigram概率就是0。为了演示效果我直接实现带拉普拉斯平滑的bigram模型。公式是P(w_i | w_{i-1}) (count(w_{i-1}, w_i) α) / (count(w_{i-1}) α * V)其中 V 是词表大小α 是平滑参数。α1 就是经典的 add-one smoothing。之所以加 α*V是为了保证分母里所有可能的后继词都有概率质量总和为1。class BigramModel: def __init__(self, alpha1.0): self.alpha alpha self.unigram_counts None self.bigram_counts None self.vocab_size 0 def train(self, sentences): self.unigram_counts, self.bigram_counts build_ngram_counts(sentences) self.vocab_size len(self.unigram_counts) def prob(self, w_prev, w): numerator self.bigram_counts[(w_prev, w)] self.alpha denominator self.unigram_counts[w_prev] self.alpha * self.vocab_size return numerator / denominator def sentence_logprob(self, sentence): tokens sentence.split() tokens [s] tokens [/s] log_prob 0.0 for i in range(1, len(tokens)): p self.prob(tokens[i-1], tokens[i]) log_prob math.log(p) return log_prob def perplexity(self, sentence): tokens sentence.split() tokens [s] tokens [/s] log_prob 0.0 n 0 for i in range(1, len(tokens)): p self.prob(tokens[i-1], tokens[i]) log_prob math.log(p) n 1 return math.exp(-log_prob / n)注意 sentence_logprob 返回的是 log 概率之和不是概率本身。原因很简单一句话的bigram概率是很多小于1的小数连乘句子稍长就下溢成0。在 log 域里把它们变成负数相加既稳定又方便比较。3.3 用困惑度评估模型先解释困惑度的概念。公式是perplexity exp(- (1/N) * Σ log P(w_i | history))N 是句子中预测次数也就是bigram数量。困惑度可以理解为模型平均每次预测下一个词时面临的有效候选数量。困惑度越低说明模型对这个句子的把握越大。我用刚才的语料训练然后测试三个句子model BigramModel(alpha1.0) model.train(corpus) test_sents [ 我 喜欢 运动, 运动 我 喜欢, 学习 需要 耐心 ] for s in test_sents: ppl model.perplexity(s) print(f{s} - 困惑度 {ppl:.2f})输出会是类似这样的结果实际数字依赖随机细微差别“我 喜欢 运动” 困惑度最低因为“我 喜欢”“喜欢 运动”都出现在训练语料里。“运动 我 喜欢” 困惑度高“运动 我”几乎没出现过。“学习 需要 耐心” 中等bigram有的见过有的没见过。这就是语言模型“给句子打分”的能力。困惑度既能用来评估模型好坏也能用来比较两个候选句子哪个更通顺。4. 平滑方法、OOV处理和n值选择的实战心得4.1 拉普拉斯平滑选α的窍门拉普拉斯平滑的 α 不是固定不变的。α 越大分配给零计数事件的概率越多会让所有预测变得更平均通顺句和乱句的差距变小。α 越小模型越依赖实际统计但没见过的事件概率会非常低测试句子里一旦出现生僻组合就爆掉。我自己的经验是小规模Demo用 α1 图省事真实任务里最好在验证集上扫一遍。比如 α 从 0.01、0.05、0.1、0.5、1 各跑一次比较困惑度。选困惑度最低的那个。注意不要拿训练集困惑度调参那只会得到 α0 的过拟合结果。有一个取巧的办法如果语料大、覆盖广α 可以调小一点如果语料小、长尾词多α 适当调大。本质上是在“相信数据”和“兜底未知”之间找平衡。4.2 未登录词处理是绕不过去的坎不管加了多平滑的公式OOVout-of-vocabulary未登录词都会让词表外的新词变成0。因为如果 w_prev 本身没在词表里count(w_prev) 为0分母直接出问题即使有 unigram count一个没见过的新词也可能让后续bigram全部失效。常规做法是引入一个特殊的UNK标记。训练时先统计词频把出现次数低于阈值比如2次的词全部替换成UNK然后在这个替换后的语料上统计n-gram。测试时遇到训练词表以外的词也用UNK替换这样模型至少能给出一个概率。这个处理必须在训练和测试时用同一套词表不然训练时学到的UNK分布和测试根本不匹配。我踩过的坑是训练时用了词频阈值测试时忘了替换新词结果测试句子里一个生僻词就让整句概率变成0。4.3 选bigram还是trigram数据量说了算n 越大模型能看到的上下文越远但参数数量和稀疏程度也越严重。bigram 大致有 V^2 个潜在二元组trigram 有 V^3 个。想象一个词表有 5000 个词trigram 就是 1250 亿个可能组合任何语料都不可能填满。所以选 n 实际是一个数据量的权衡。我自己的粗略标准几千到几万句的小语料用 bigram 最稳。百万句以上可以试 trigram。超过5-gram的模型没有海量数据基本是自欺欺人。判断标准也很简单在固定验证集上比较困惑度或者直接看下游任务效果。不要只看生成文本是否流畅那是另一个维度。5. 常见问题与排查实录5.1 明明统计了概率还是一堆0大概率是你用了未平滑的MLE。确认一下模型里有没有加平滑项。如果你加了平滑概率不该是0。还有一种情况是测试句子里的某个词在训练词表里完全不存在又没有UNK机制那么 unigram 计数为0分母就崩了。调试时可以写一个打印函数把每个bigram键、计数、概率都列出来。看到底是哪一步变成0而不是靠猜。我曾经排查了一下午最后发现是没有给训练语料加/s导致句尾词的下文概率全漏了。5.2 计算句子概率时下溢怎么办一句话的bigram概率是十几个小数连乘很快跌破浮点数下限。如果直接用浮点乘法长句子概率会变成0比较“哪个句子更通顺”时就全乱了。 解决方法是无条件使用 log 概率。所有乘法变成 log 相加最后再 exp 回来。要注意 math.log(0) 会报错所以概率必须经过平滑永远不为0。5.3 训练集和测试集处理不一致导致效果崩掉这是最隐蔽的坑。训练时中文分词用了词表A测试时用了词表B或者训练时把标点删干净测试时留了标点甚至训练时句子加s测试时没加都会让统计对不上号。语言模型是一个极其依赖预处理一致性的模型前后不一致等于直接换了一份语料。把预处理代码封装成函数训练和测试都调用同一份逻辑。尤其是中文分词尽量用同一个版本的分词工具和同一份词表否则一字之差ngram就完全错位。字符级n-gram可以绕过分词问题在小规模短文本任务上值得尝试。5.4 生成文本很怪是不是模型错了用 bigram 随机生成文本本身就是一件很“短视”的事。因为生成每个词时只看上一个词基本只能产出两三个词的连贯片段稍微长一点就会跳到一个无关的词。这不一定说明模型错了只是 n 太小、数据太少。语言模型更适合用来打分而不是生成。比如输入法给的候选排序、搜索引擎的查询纠错、机器翻译候选重排都是给一组候选句子打困惑度分选最低的。如果你确实想拿它生成可以试试 trigram再加一个随机采样参数 temperature。在 temperature 很低时模型倾向于选高概率词文本会更稳定。但别期待一个两万句的语料能训练出写文章的语言模型那是信息量不够不是代码问题。我在自己练习时是先写了一个很简陋的bigram分类器把朴素贝叶斯的unigram换成bigram去跑情感分类准确率涨了好几个点。那一次我才真正意识到所谓语言模型并不是从天上掉下来的知识它就是把朴素贝叶斯强行忽略掉的顺序信息一点点加回来。后来再做平滑、OOV、困惑度每一步都有了具体的感知。建议你也找个十几行语料的小分类任务亲手把unigram升级成bigram调一遍α观察概率和困惑度怎么变。踩过这几个坑后面看神经网络语言模型都会轻松很多。

相关新闻

华为MetaERP 按你要求的三个层次——业务对象 BO(Business Object)/ 逻辑对象 LE(Logical Entity)/ 物理实体 PE(Physical Entity)——

华为MetaERP 按你要求的三个层次——业务对象 BO(Business Object)/ 逻辑对象 LE(Logical Entity)/ 物理实体 PE(Physical Entity)——

按你要求的三个层次——业务对象 BO(Business Object)/ 逻辑对象 LE(Logical Entity)/ 物理实体 PE(Physical Entity)——把 Oracle EBS R12 OM(ONT)​ 与 Oracle Fusion Cloud Orde…

2026/10/14 0:59:02 阅读更多 →
NeRF三维重建实战:从手机拍摄到mesh导出的完整指南

NeRF三维重建实战:从手机拍摄到mesh导出的完整指南

简介:基于NeRF与手机拍摄物体图片的三维重建高分项目,面向具备一定Python基础的计算机视觉学习者和毕业设计开发者。资源内含完整可运行的Python源码、配套数据集与文档说明,覆盖数据预处理、COLMAP位姿估计、NeRF模型训练、渲染路径生成与结…

2026/10/14 0:58:02 阅读更多 →
数据库实验报告高分指南:从SQL设计到MySQL避坑

数据库实验报告高分指南:从SQL设计到MySQL避坑

简介:一份面向太原理工大学软件工程专业《数据库概论》课程的实验报告,适合正在学习SQL Server 2016交互式SQL、数据完整性与用户控制的在校生参考。报告按实验目的、平台、主要内容和结果展开,完整记录了基本表的创建/修改/删除、索引与视图…

2026/10/14 0:58:02 阅读更多 →

最新新闻

风电水电联合调度优化算法与工程实践

风电水电联合调度优化算法与工程实践

1. 项目背景与核心价值去年参与某省级电网清洁能源消纳项目时,我第一次接触到风-水联合调度这个课题。当时面对风电出力的强随机性和水电调节的滞后性,团队在制定优化运行方案时遇到了不少挑战。这个经历让我深刻认识到:在双碳目标下&#xf…

2026/10/14 2:21:24 阅读更多 →
从PowerDesigner迁移到PDMan:开源数据库建模与Git版本管理实践

从PowerDesigner迁移到PDMan:开源数据库建模与Git版本管理实践

1. 为什么我最终把数据库建模从商业软件迁到了PDMan做后端开发时间长了,数据库建模这件事几乎绕不开。早些年团队里清一色用PowerDesigner,画物理模型、生成DDL、导出字段文档,一套流程走下来确实顺手。但问题也很明显:正版授权费…

2026/10/14 2:21:24 阅读更多 →
微信小程序仿智行火车票开发实战:云开发+数据离线化方案

微信小程序仿智行火车票开发实战:云开发+数据离线化方案

简介:本资源是一套完整的微信小程序仿智行火车票12306系统实战项目,面向Java后端与小程序前端初学者及全栈学习者,聚焦铁路订票核心业务场景,涵盖用户查询、车次管理、订单生成等全流程功能实现。项目采用SSM(SpringSp…

2026/10/14 2:21:23 阅读更多 →
RTMP Flash播放插件替代方案:flv.js迁移实战与参数调优

RTMP Flash播放插件替代方案:flv.js迁移实战与参数调优

简介:这份资源是面向需要在网页中播放RTMP流媒体的开发者与运维人员的一套Flash播放插件实现方案,核心解决旧系统或特定场景下通过Flash技术播放RTMP直播、点播流的问题。压缩包共5个文件,约1.15MB,包含2个swf播放器与安装检测组件…

2026/10/14 2:21:23 阅读更多 →
AI编程助手代码质量救星:TypeScript静态类型约束实战

AI编程助手代码质量救星:TypeScript静态类型约束实战

AI 编程助手越帮越乱,这个话题我最近真的是感同身受。我们团队接入 AI 编程助手之后,合并请求里的代码量倒是上去了,可编译期错误非但没减少,反而冒出来一批"语法完全合法、类型对不上、一跑就崩"的代码。后来大家坐下来…

2026/10/14 2:21:23 阅读更多 →
LaTeX 2025环境配置:texlive + TeXstudio

LaTeX 2025环境配置:texlive + TeXstudio

文章目录Latex2025环境配置一、LaTeX 简介1.1 什么是 LaTeX?1.2 为什么使用 LaTeX?1.3 如何开始使用 LaTeX?二、texlive下载安装2.1 texlive下载2.2 texlive安装2.3 常见问题2.3.1 用管理员权限打开install-tl-windows.bat 报错2.3.2 Latex安…

2026/10/14 2:20:23 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →