基于bert-base-chinese的微博情感分析微调实战与LoRA优化
简介这份资源是面向高校学生与NLP入门者的中文情感分析实战项目基于WeiboSenti100k数据集对bert-base-chinese进行微调可用于毕业设计、课程设计或软件工程实践帮助读者掌握预训练模型在文本分类任务中的完整落地流程。压缩包共5个文件包含2个Python脚本分别负责训练与推理1个CSV数据集文件、1个依赖清单和1份项目说明文档整体约9.73MB结构精简便于快速跑通。项目覆盖数据清洗与分词、加载预训练模型并构建分类层、定义损失函数与优化器、在验证集上评估准确率与F1值以及对新微博文本进行情感极性预测等环节读者可据此理解微调机制、超参数调整与工程组织方式。目前已有196人学习下载适合希望把深度学习理论转化为可运行代码、积累NLP项目经验的学习者参考。1. 从一份微博情感分析源码说起bert-base-chinese 微调到底能跑出什么如果你手头正好有一份 WeiboSenti100k 数据集加 bert-base-chinese 微调的源码包大概率会先冒出三个问题这份代码能不能直接跑通、微调出来的模型准不准、以及它跟现在满屏的 LoRA 微调、adapter 微调到底差在哪。我拿到这个压缩包的第一反应也是先看目录结构——train.py、inference.py、requirements.txt、README.md外加一个weibo_senti_100k.csv典型的课程设计和毕业设计交付形态麻雀虽小但五脏俱全。它解决的核心问题很明确用一份十万量级的中文微博二分类情感语料把预训练好的中文 BERT 在下游分类任务上做全参数微调最终得到一个能判断一句话是正面还是负面的模型。适合谁正在做 NLP 方向课程设计、毕业设计的学生以及想跑通一遍「预训练模型微调」完整链路但不想从零搭数据管道的工程师。它不追求 SOTA追求的是链路完整、可复现、能讲清楚每一步在干什么。2. 数据管道与标签分布weibo_senti_100k.csv 怎么读才不翻车2.1 先搞清楚这份 CSV 的真实结构WeiboSenti100k 这个数据集在圈子里流传的版本不止一个字段名和分隔符都有差异。常见的有两列label,review也有带id的三列版本分隔符有的是逗号有的是制表符。直接pd.read_csv一把梭遇到文本里本身带逗号的情况就会列错位。我一般会先做一次结构探测而不是上来就写 Dataset 类。import pandas as pd # 先只读前 5 行不指定列名看看真实分隔情况 raw pd.read_csv(weibo_senti_100k.csv, nrows5, headerNone) print(raw.shape) print(raw.head()) # 再读全量显式指定列名和编码避免中文乱码 df pd.read_csv( weibo_senti_100k.csv, encodingutf-8, names[label, text] if raw.shape[1] 2 else [id, label, text], header0 ) print(df[label].value_counts()) print(df[text].str.len().describe())这段代码的逻辑是先用nrows5和headerNone探测真实列数再决定列名映射。参数上encoding优先试utf-8如果报UnicodeDecodeError就换gb18030这是中文 CSV 最常见的两个编码。value_counts()用来确认标签是不是 0/1 二分类str.len().describe()看文本长度分布这一步很关键——BERT 的max_length设多少直接由这个分布决定拍脑袋设 128 很可能截掉大量有效信息。2.2 标签映射与文本清洗的边界这份数据集通常是二分类label 为 0 和 1对应负面和正面。但有些衍生版本会混入中性样本或把标签写成 -1/1。训练前必须把标签统一成 0/1 连续整数否则CrossEntropyLoss会直接报 index 越界。文本清洗这块微博文本的典型噪声是 用户、话题标签 #、URL 和表情符号。我的做法是保留表情的中文描述如果数据集已经转好只去掉 URL 和 提及因为过度清洗会把情感信号一起洗掉。import re def clean_text(s): s re.sub(rhttp\S|www\.\S, , s) # 去 URL s re.sub(r[\w\u4e00-\u9fa5], , s) # 去 提及 s re.sub(r#(.?)#, r\1, s) # 话题标签保留内容去井号 s re.sub(r\s, , s).strip() # 合并空白 return s df[text] df[text].astype(str).map(clean_text) df df[df[text].str.len() 1] # 丢掉清洗后为空的样本 df[label] df[label].astype(int)这里每个正则都有明确目的URL 和 提及是纯噪声话题标签的井号去掉但保留里面的词因为话题词往往带情感倾向。最后过滤掉长度小于等于 1 的样本避免空文本进 tokenizer 后只剩特殊标记。注意astype(str)要放在清洗前防止 NaN 让正则报错。2.3 划分训练验证集时的分层采样直接train_test_split不设stratify是新手常踩的坑尤其当正负样本比例不是严格 1:1 时验证集可能严重偏斜导致评估指标虚高或虚低。正确做法是按 label 分层。from sklearn.model_selection import train_test_split train_df, val_df train_test_split( df, test_size0.1, random_state42, stratifydf[label] ) print(train_df[label].mean(), val_df[label].mean())stratifydf[label]保证训练集和验证集的标签比例一致random_state固定后结果可复现这在毕业设计答辩时很重要——评委让你重跑一遍你得能跑出同样的数。3. bert-base-chinese 微调train.py 里的关键参数怎么定3.1 模型加载与分类头改造bert-base-chinese是 12 层、768 隐藏维度、12 个注意力头的标准 BERT-base 结构词表约 2.1 万。用 transformers 加载时AutoModelForSequenceClassification会自动在 [CLS] 向量上接一个全连接分类头num_labels2对应二分类。这里有个容易忽略的点bert-base-chinese的输出是 768 维分类头是768 - 2的线性层参数量很小真正被微调的是整个 BERT 编码器的约 1.02 亿参数。这也是为什么全参数微调显存吃紧而 LoRA 微调只需要训练极少量低秩矩阵就能接近全量微调效果。import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification MODEL_NAME bert-base-chinese tokenizer AutoTokenizer.from_pretrained(MODEL_NAME) model AutoModelForSequenceClassification.from_pretrained( MODEL_NAME, num_labels2 ) device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device)num_labels2必须和标签种类数一致改成 3 就是三分类。device判断这块如果没有 GPUCPU 上跑 10 万条数据全参数微调基本不现实一个 epoch 可能要几个小时这是硬件门槛不是代码问题。3.2 数据集封装与动态 padding把 DataFrame 转成 torch Dataset 时核心是 tokenizer 的调用方式。固定max_length128加paddingmax_length会浪费大量计算在 padding token 上更优的做法是paddingmax_length配合truncationTrue或者用 DataCollator 做动态 padding。我一般先用长度分布决定一个上限比如覆盖 95% 样本的长度。from torch.utils.data import Dataset class WeiboDataset(Dataset): def __init__(self, texts, labels, tokenizer, max_len128): self.texts texts self.labels labels self.tokenizer tokenizer self.max_len max_len def __len__(self): return len(self.texts) def __getitem__(self, idx): enc self.tokenizer( self.texts[idx], max_lengthself.max_len, paddingmax_length, truncationTrue, return_tensorspt ) return { input_ids: enc[input_ids].squeeze(0), attention_mask: enc[attention_mask].squeeze(0), labels: torch.tensor(self.labels[idx], dtypetorch.long) }truncationTrue保证超长文本被截断而不是报错return_tensorspt直接返回 PyTorch 张量squeeze(0)去掉 batch 维度。max_len设 128 是中文短文本的常见选择微博文本大多在 100 字以内128 个 token 基本够用。如果你的长度分布 P95 超过 128就调到 160 或 192但注意显存占用会随之上升。3.3 训练循环与超参数设置train.py里最值得盯的是学习率、batch size 和 epoch 数。BERT 微调的经典学习率是 2e-5 到 5e-5太大容易灾难性遗忘太小收敛慢。batch size 在显存允许下尽量大16 或 32 是常见值。epoch 数一般 3 到 5再多容易过拟合尤其数据量只有 10 万条时。from transformers import AdamW, get_linear_schedule_with_warmup from torch.utils.data import DataLoader BATCH_SIZE 32 EPOCHS 3 LR 2e-5 train_loader DataLoader( WeiboDataset(train_df[text].tolist(), train_df[label].tolist(), tokenizer), batch_sizeBATCH_SIZE, shuffleTrue ) optimizer AdamW(model.parameters(), lrLR, weight_decay0.01) total_steps len(train_loader) * EPOCHS scheduler get_linear_schedule_with_warmup( optimizer, num_warmup_stepsint(0.1 * total_steps), num_training_stepstotal_steps ) model.train() for epoch in range(EPOCHS): for batch in train_loader: batch {k: v.to(device) for k, v in batch.items()} outputs model(**batch) loss outputs.loss loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() scheduler.step() optimizer.zero_grad() print(fepoch {epoch} done)AdamW的weight_decay0.01是 BERT 微调的标准配置get_linear_schedule_with_warmup让学习率在前 10% 步数线性升温再线性衰减这个策略对 Transformer 类模型很关键能避免训练初期梯度震荡。clip_grad_norm_设 1.0 是防止梯度爆炸的保险丝中文短文本任务里梯度爆炸不常见但加上不亏。注意optimizer.zero_grad()放在step()之后顺序反了会导致梯度累积错误。3.4 验证集评估与指标选择二分类情感分析不能只看准确率尤其当正负样本不均衡时。精确率、召回率和 F1 都要看classification_report一行搞定。from sklearn.metrics import classification_report, accuracy_score model.eval() preds, trues [], [] with torch.no_grad(): for batch in DataLoader( WeiboDataset(val_df[text].tolist(), val_df[label].tolist(), tokenizer), batch_size64 ): batch {k: v.to(device) for k, v in batch.items()} logits model(**batch).logits preds.extend(torch.argmax(logits, dim-1).cpu().numpy()) trues.extend(batch[labels].cpu().numpy()) print(accuracy_score(trues, preds)) print(classification_report(trues, preds, digits4))torch.no_grad()关闭梯度计算省显存也加速。argmax取 logits 最大值对应的类别。classification_report会输出每个类别的 precision、recall、f1-score重点看 macro avg 和 weighted avg 的差异差异大说明类别不均衡影响了整体表现。4. 推理与部署inference.py 怎么把模型用起来4.1 单条文本预测的完整流程inference.py的核心是把训练好的权重加载回来对任意中文文本输出情感极性。这里最容易翻车的地方是 tokenizer 必须和训练时完全一致包括max_length和 padding 策略。换一个 tokenizer 或者改了max_length预测结果可能完全不对。import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification MODEL_DIR ./saved_model tokenizer AutoTokenizer.from_pretrained(MODEL_DIR) model AutoModelForSequenceClassification.from_pretrained(MODEL_DIR) model.eval() model.to(cuda if torch.cuda.is_available() else cpu) def predict(text): enc tokenizer( text, max_length128, paddingmax_length, truncationTrue, return_tensorspt ) enc {k: v.to(model.device) for k, v in enc.items()} with torch.no_grad(): logits model(**enc).logits prob torch.softmax(logits, dim-1) pred torch.argmax(prob, dim-1).item() return {label: pred, confidence: prob[0][pred].item()} print(predict(这家店的服务态度真的太好了下次还来)) print(predict(快递太慢了等了一个星期才到))softmax把 logits 转成概率confidence给出模型对当前预测的置信度。实际用的时候置信度低于 0.6 的样本建议人工复核尤其是做舆情监控场景误判成本高。4.2 批量推理与显存控制生产环境不会一条一条预测批量推理能显著提升吞吐。但 batch size 设太大容易 OOM需要根据显存动态调整。def batch_predict(texts, batch_size64): results [] for i in range(0, len(texts), batch_size): batch_texts texts[i:i batch_size] enc tokenizer( batch_texts, max_length128, paddingTrue, truncationTrue, return_tensorspt ) enc {k: v.to(model.device) for k, v in enc.items()} with torch.no_grad(): logits model(**enc).logits probs torch.softmax(logits, dim-1) preds torch.argmax(probs, dim-1).cpu().numpy() results.extend(preds.tolist()) return results批量推理时paddingTrue会按 batch 内最长序列动态补齐比max_length固定补齐省显存。batch_size从 64 起步OOM 就减半直到能跑通。这个函数返回的是标签列表如果需要置信度把probs也收集起来即可。4.3 模型保存与加载的目录结构训练完保存模型时save_pretrained会生成config.json、pytorch_model.bin和 tokenizer 相关文件。加载时指向这个目录就行不要只保存state_dict然后手动重建模型结构那样容易因为配置不一致导致加载失败。# 训练结束后保存 model.save_pretrained(./saved_model) tokenizer.save_pretrained(./saved_model) # 推理时加载 model AutoModelForSequenceClassification.from_pretrained(./saved_model) tokenizer AutoTokenizer.from_pretrained(./saved_model)save_pretrained保存的是完整配置加权重from_pretrained自动读取配置重建模型这是 transformers 的标准做法比手动torch.save省心得多。5. 避坑与排查这份源码跑不通时先看这几条5.1 现象训练 loss 不下降准确率卡在 0.5 左右原因通常是标签没对齐或者学习率过大。先检查df[label].unique()是不是只有 0 和 1如果出现 -1 或 2CrossEntropyLoss虽然不报错但学不到东西。学习率方面2e-5 是安全值如果你手改成 1e-3loss 会震荡甚至发散。解决方法是把学习率调回 2e-5 到 5e-5 区间并确认标签映射正确。5.2 现象CUDA out of memory原因无非三个batch size 太大、max_length太长、或者没有用torch.no_grad()做验证。解决顺序是先降 batch size 到 16 甚至 8再把max_length从 128 降到 96验证阶段务必包在torch.no_grad()里。如果还 OOM考虑用梯度累积模拟大 batch或者上混合精度训练。5.3 现象推理结果和训练时验证集表现差距大原因通常是推理时的 tokenizer 配置和训练时不一致比如训练用了max_length128推理用了 64长文本被截断后语义丢失。解决方法是把 tokenizer 的配置和训练脚本对齐最好直接加载保存下来的 tokenizer 目录而不是重新从bert-base-chinese加载。5.4 现象中文乱码或 tokenizer 报错原因多半是 CSV 编码问题。pd.read_csv默认 utf-8但有些 Windows 环境下生成的 CSV 是 gb18030。解决方法是显式指定encodinggb18030试一次如果还不行就用chardet探测编码。tokenizer 报错则可能是文本里有 None 或 NaN清洗前先astype(str)。5.5 现象训练完保存的模型加载时报配置缺失原因是只保存了state_dict而没有保存config.json。解决方法是统一用save_pretrained和from_pretrained不要混用torch.save和from_pretrained。如果已经只存了 state_dict需要手动构造BertConfig再加载权重麻烦且容易出错。6. 进阶技巧用 LoRA 微调把显存门槛打下来全参数微调bert-base-chinese在 10 万条数据上单卡 8GB 显存基本是极限batch size 只能开到 8 左右。如果你手头只有消费级显卡或者想同时跑多组超参数实验LoRA 微调是更实际的选择。它的思路是在 BERT 的注意力层里注入低秩矩阵只训练这些新增的小矩阵原始 BERT 权重冻结。这样可训练参数量从 1 亿降到几十万显存占用大幅下降训练速度也快很多。from peft import LoraConfig, get_peft_model, TaskType lora_config LoraConfig( task_typeTaskType.SEQ_CLS, r8, lora_alpha32, lora_dropout0.1, target_modules[query, value] ) model AutoModelForSequenceClassification.from_pretrained( bert-base-chinese, num_labels2 ) model get_peft_model(model, lora_config) model.print_trainable_parameters()r8是低秩矩阵的秩越大可训练参数越多8 或 16 是常见值。lora_alpha32控制缩放一般设为r的 2 到 4 倍。target_modules指定注入位置BERT 里通常是query和value两个投影矩阵。print_trainable_parameters()会打印可训练参数占比你会看到只有不到 1% 的参数需要更新。训练循环和全参数微调完全一样只是优化器只更新 LoRA 参数。推理时可以用model.merge_and_unload()把 LoRA 权重合并回原模型得到一个和全参数微调结构一致的模型方便部署。我自己的习惯是先用全参数微调跑一个 baseline确认数据和链路没问题再切 LoRA 做超参数搜索。这样既保证了效果上限又控制了实验成本。从那以后我每次拿到新的文本分类任务都会先跑一遍全参数微调确认数据管道无误再决定要不要上 LoRA 省显存。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

JavaWeb连接MySQL实战:JDBC+连接池+DAO实现用户增删改查

JavaWeb连接MySQL实战:JDBC+连接池+DAO实现用户增删改查

这天的内容我在实际做项目时回头再看,觉得是整个JavaWeb阶段里性价比最高的一课。Day10的主题正好卡在“能跑”和“能扛”之间的分水岭上——Servlet和JSP你已经会用了,但是项目一旦要让用户真正用起来、数据要存得住,就必须把数据库接进来&a…

2026/10/9 3:20:05 阅读更多 →
Go实现出现频率最低数字:边界处理与易错点全解析

Go实现出现频率最低数字:边界处理与易错点全解析

刷题群里看到“出现频率最低的数字”这道题时,我第一反应是:这不就是计数题换个问法吗?但真正用 Go 写完整再跑测试,才发现里面至少有三个坑:负数怎么处理、没出现过的数字算不算“0次”、并列时怎么保证返回最小的那一…

2026/10/9 3:20:05 阅读更多 →
SpringBoot+Vue+MySQL图书管理系统:从架构到答辩的全链路实战

SpringBoot+Vue+MySQL图书管理系统:从架构到答辩的全链路实战

SpringBootVueMySQL 的图书管理系统,放在毕业设计这个场景里,是出现频率最高的一类全栈项目。说它简单,因为核心业务就是图书、读者、借还、统计,本质上是标准 CRUD;说它不简单,是因为前后端分离、数据库设…

2026/10/9 3:19:05 阅读更多 →

最新新闻

项目合同管理实战避坑指南:从订立、变更到结算的全流程经验

项目合同管理实战避坑指南:从订立、变更到结算的全流程经验

刚看到项目管理教材目录里"16.6 项目合同管理"这一节时,很多人第一反应是把它当成选择题考点:背背合同类型、记记索赔流程、知道几个关键词,考试能蒙对就行。但实际上,项目经理真正的工作里,合同管理是最不像…

2026/10/9 3:56:27 阅读更多 →
关键字新闻爬虫:百度新闻与今日头条采集入库完整方案

关键字新闻爬虫:百度新闻与今日头条采集入库完整方案

简介:针对百度新闻与今日头条的Java新闻爬虫项目资源,围绕“按关键词抓取新闻并落库”的真实场景设计,面向Java初、中级开发者,可帮助学习网络数据采集与存储的完整链路。压缩包共20个文件,其中16个为Java源码&#xf…

2026/10/9 3:56:27 阅读更多 →
pstack-claude:用Linux栈跟踪诊断Claude/Codex本地代理故障

pstack-claude:用Linux栈跟踪诊断Claude/Codex本地代理故障

1. 项目概述:pstack-claude 是什么,它解决的是哪类开发者的真实痛点?pstack-claude 这个名字乍看像一个工具组合词,但拆开来看——“pstack”是 Linux 系统中用于打印进程栈跟踪(process stack trace)的经典…

2026/10/9 3:56:27 阅读更多 →
C++多态深度解析:从虚函数表到工程实践

C++多态深度解析:从虚函数表到工程实践

先问你一个问题:如果有一天你接手了一个老项目,handler 数组里挂了十几个回调函数,每个回调背后都是一套完全不同的对象行为,但它们的注册方式却完全一样——你第一反应是什么?八成会感慨一句:“这就是多态…

2026/10/9 3:56:27 阅读更多 →
Agent触达层实战:从Function Calling到MCP构建可靠工具调用

Agent触达层实战:从Function Calling到MCP构建可靠工具调用

1. 项目解析:Agent-Reach到底在解决什么问题圈子里的朋友看到Agent-Reach这个名字,第一反应多半是:这不就是给Agent装了一双“手”吗?确实,我拿到这个项目标题时的理解也是这样——Agent是智能体,Reach是触…

2026/10/9 3:56:27 阅读更多 →
论文降重工具怎么选?从查重原理到实战流程全解析

论文降重工具怎么选?从查重原理到实战流程全解析

经常有师弟师妹拿着大篇幅标红的查重报告来找我,第一句话就是:“师兄,用什么工具能一夜之间把重复率降下去?”说实话,每次听到这种问题我都想把“一夜之间”四个字划掉。降重不是体力活,也不是无脑堆工具就…

2026/10/9 3:55: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/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 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/7 13:34:55 阅读更多 →