基于keras-bert的序列标注实战:从数据预处理到模型调优
1. 先说清楚序列标注任务是什么为什么选BERT1.1 序列标注的任务定义与典型场景序列标注是NLP里非常经典的一类任务。简单说就是给一个句子里的每一个字或词打上一个标签把所有标签连起来就形成了一个对整句的结构化理解。最常见的三个应用就是命名实体识别、词性标注和中文分词。这三个任务本质上是同一套数学模型区别只在于标签体系不同。词性标注的标签集是名词、动词、形容词这些中文分词的标签集是B、M、E、S命名实体识别的标签集是人物、地点、组织等实体类型加上BIO前后缀。我做这个项目时业务需求就是命名实体识别从一段客户咨询文本中抽取出人名、机构名和地名。刚开始用的是BiLSTM-CRF效果也能跑但在长句上明显乏力尤其是那种连续出现多个实体、中间还夹杂修饰成分的句子模型经常会漏掉后半段的实体。后来把底层换成BERT之后效果提升非常直观F1值涨了将近6个点。这正是BERT这类预训练模型的优势所在它在海量语料上学出来的上下文表示比随机初始化的词向量加BiLSTM要强太多。1.2 序列标注为什么需要预处理和标签对齐很多人第一次做序列标注时容易忽略一个问题BERT的输入是经过WordPiece切分的token序列而你的标注是基于“字”或“词”的。中文场景还好说BERT的tokenizer本身就是按字切分的字和token基本是一一对应但英文场景中一个单词可能被切分成多个subword比如playing会被切成play和##ing这时候你的标注标签就需要跟着token一起变。如果一个词被拆成两个token那么它的实体标签就要复制到两个token上。处理不好这一环后面训练出来的模型预测结果会错位得很离谱而且很难排查。另外序列标注模型和文本分类模型在输出层上也不同。文本分类在BERT的[CLS]向量后面接一个全连接层做多分类就行序列标注则是要在每个token的输出向量上都接一个全连接层预测每个token对应的标签。所以你的标签序列长度必须和BERT的输入序列长度完全一致padding的同时也要给标签做padding并且要在计算loss时把这些padding位置忽略掉。这些都是实现细节但往往决定了一个模型能不能真正训练起来。1.3 为什么选择keras-bert这个库在BERT刚开源那段时间生态不像现在这么成熟。当年的transformers库还不是主流选择很多人用pytorch版本很顺畅但习惯了Keras这一套的开发者就很尴尬。keras-bert是一个把BERT移植到Keras生态的第三方库它做的事情就是把BERT的预训练权重转成Keras模型能直接加载的结构同时暴露了tokenizer和模型接口让人可以很自然地把BERT当作一个特征提取层接到自己的任务模型里。我选择keras-bert的理由有三个。第一它是纯Keras实现训练流程完全沿用Keras的compile和fit不需要切换心智模型。第二它保留了BERT的原始checkpoint权重格式加载预训练权重时不用做太多转换操作下载好官方权重就能直接跑。第三模型的输出可以很方便地接自定义层比如CRF层、全连接层、多头注意力等扩展性很强。当然这个库也存在一定局限性比如更新频率不高、对新版TensorFlow的兼容性一般这也是很多人在实际项目中后来迁移到transformers的原因。但对于以学习和快速验证为目的的序列标注任务keras-bert依然是一条非常简洁的路径。2. 数据准备从原始语料到模型输入2.1 BIO标注体系与数据集示例序列标注项目的第一步永远是数据。数据质量直接决定模型上限模型只是努力去逼近这个上限。命名实体识别最常用的标注体系是BIOB表示实体的开始I表示实体的中间或延续O表示非实体。如果你要识别三种实体类型比如人名、地名、机构名那么标签集合就是B-PER、I-PER、B-LOC、I-LOC、B-ORG、I-ORG、O总共7个标签。我之前在项目中用的数据格式是类CoNLL的三列式每行一个字空行隔开句子第二列是这个字在原始句子中的位置第三列是标签。示例长这样张 B B-PER 三 I I-PER 在 O O 北 B B-LOC 京 I I-LOC 工 O O 作 O O这里第一列是字第三列是标签第三列每一行的标签和字的顺序是一一对应的。训练前要把这些数据读进来按空行切分成句子然后把每个句子的字序列转成BERT input_ids标签序列转成整数序列。2.2 token对齐与padding细节BERT处理输入时要加上[CLS]和[SEP]这两个特殊token。所以在构造input_ids的时候字序列前要插入101后要插入102。相应的标签序列前面也要插入一个特殊标签值后面插入一个特殊标签值。我在项目中用0表示padding位置用-1来表示这些非实体token对应的位置比如[CLS]、[SEP]这样在计算loss时直接跳过这些位置。具体流程是先把字序列用keras_bert的tokenizer转成token_ids和segment_ids再把字级别的标签序列映射到token级别并做长度对齐。中文场景几乎是一比一的但最好还是在代码里做一个通用处理如果tokenizer把某个字切成了多个subword就把该字对应的标签复制到所有subword上如果遇到未知字符统一给O标签。padding的策略要统一。模型训练时通常固定一个max_len比如128或者256。对于不足max_len的句子token_ids、segment_ids、mask都要补0对应的标签用-1补齐。在自定义loss函数中把标签等于-1的位置从loss中排除掉只计算有效token的交叉熵。这一步如果忘了padding位置的标签都会变成某个数字模型就会被大量无意义的padding项干扰训练出来的效果会非常差。2.3 踩过的坑标签错位问题这个坑我印象非常深。第一次用keras-bert做命名实体识别时在数据预处理环节只处理了token_ids没有把标签序列同步加上[CLS]和[SEP]对应的值。当时训练loss下降很顺利验证集上的准确率看起来也很高但一打印预测结果就傻眼了模型预测出来的第一个位置的标签永远是错的而且所有序列的预测结果整体向后移了一位。原因很简单标签序列比input_ids少了两个特殊token的位置导致模型在每个位置上学习的对应关系整体错位。排查这个问题的思路也值得分享一下别一上来就怀疑模型结构先用一个训练好的模型去预测几条手动标注的数据逐字对比。一旦发现所有预测结果都错位八九不离十是标签对齐出了问题。把预处理逻辑打印出来检查一遍很快就能定位。这个问题后来我加了一段代码做防御性检查在构造训练集时强制断言len(input_ids)等于len(label_ids)不相等就直接抛异常。3. 代码实战构建并训练BERT序列标注模型3.1 环境准备先说明一下环境版本这个库有一个要特别注意的地方keras-bert依赖的是老版本的TensorFlow接口所以最好在TensorFlow 2.x早期版本下运行我当时用的是TensorFlow 2.6和Python 3.8。如果你装了新版TensorFlow 2.10以上很可能会遇到tf.contrib相关的兼容报错解决方案是锁定环境版本或者用我后面会讲到的替代方案。安装依赖pip install keras-bert pip install tensorflow2.6下载BERT中文预训练权重时需要拿到三个文件bert_config.json、bert_model.ckpt和vocab.txt。这三个文件放好之后代码里指定路径即可。3.2 数据集加载与Input构造看代码最直接。我按照大家在序列标注任务中常用的流程来写import numpy as np from keras_bert import Tokenizer, load_vocabulary, load_trained_model_from_checkpoint # 载入词典 token_dict load_vocabulary(vocab_path) tokenizer Tokenizer(token_dict) # 标签映射 label2id {O: 0, B-PER: 1, I-PER: 2, B-LOC: 3, I-LOC: 4, B-ORG: 5, I-ORG: 6} id2label {v: k for k, v in label2id.items()} max_len 128 def encode_text(text, labels): # text: 字符串例如 张三在北京工作 # labels: 和text中的每个字一一对应的标签列表 tokens list(text) input_ids [tokenizer._token_dict.get(token, tokenizer._token_dict[[UNK]]) for token in tokens] # 插入[CLS]和[SEP] input_ids [tokenizer._token_dict[[CLS]]] input_ids [tokenizer._token_dict[[SEP]]] segment_ids [0] * len(input_ids) # 标签对齐 label_ids [-1] [label2id[label] for label in labels] [-1] # padding padding_len max_len - len(input_ids) if padding_len 0: input_ids input_ids [0] * padding_len segment_ids segment_ids [0] * padding_len label_ids label_ids [-1] * padding_len mask [1 if i len(tokens) 2 else 0 for i in range(max_len)] else: print(句子太长需要截断) return np.array(input_ids), np.array(segment_ids), np.array(mask), np.array(label_ids)这段代码有几个点要提醒。segment_ids在单句场景下全部为0不需要修改mask表示哪些位置是有效tokenpadding位置为0后面计算loss时会用到。这里的tokenizer._token_dict是内部对象实际用tokenizer.encode也可以但不方便和标签对位所以我直接用了词典映射。3.3 模型搭建与训练keras-bert的加载方式是load_trained_model_from_checkpoint加载出来的模型直接就是一个完整的Keras结构。在加载时把output_layer设置为Encoder-12就能拿到最后一个transformer层的输出。接下来在这个输出之上接全连接层对每个token预测标签。from keras.layers import Dense, Dropout from keras.models import Model from keras.losses import sparse_categorical_crossentropy from keras.optimizers import Adam bert_model load_trained_model_from_checkpoint( config_path, checkpoint_path, seq_lenmax_len, output_layerEncoder-12, trainingFalse ) # 让BERT层可训练 for layer in bert_model.layers: layer.trainable True input_ids bert_model.inputs[0] segment_ids bert_model.inputs[1] mask bert_model.inputs[2] bert_output bert_model.output # shape: (batch_size, max_len, hidden_size) dropout_layer Dropout(0.1)(bert_output) output Dense(len(label2id), activationsoftmax)(dropout_layer) model Model(inputs[input_ids, segment_ids, mask], outputsoutput) model.compile( optimizerAdam(learning_rate2e-5), losssparse_categorical_crossentropy, metrics[accuracy] )这里有三个关键选择。第一BERT层是整体可训练还是冻结。刚开始做实验时我建议让BERT层整体可训练因为序列标注任务毕竟和预训练任务有一定差异底层表示需要针对性的微调。不过如果你数据量很小或者显存很紧可以先把BERT层冻结只训练上面的分类层先跑通流程再说。第二学习率用2e-5是BERT微调时的常见经验值太大容易破坏预训练权重太小又会在有限的数据量下训练太慢。第三这里的mask没有直接参与loss计算我需要自定义loss来忽略padding位置的标签下一小节会说明。训练数据准备好之后直接model.fit跑起来就行。batch size建议在8到32之间序列标注任务对显存的消耗比文本分类大不少尤其是max_len设得比较大的时候。3.4 预测解码与效果验证预测阶段要把模型输出的概率序列转换成实体列表。核心逻辑是遍历每个token的predicted_id根据id2label还原标签。遇到B开头的标签就开启一个新实体记录实体起始位置遇到I开头的标签就判断当前是否在实体内部如果在则延续否则跳过遇到O标签就把当前实体结束。最后再根据实体起止位置还原出原始文本片段。def decode_entities(text, pred_ids): tokens list(text) entities [] current_entity None for idx, label_id in enumerate(pred_ids): label id2label[label_id] if label.startswith(B-): if current_entity: entities.append(current_entity) current_entity {type: label[2:], start: idx, text: tokens[idx]} elif label.startswith(I-) and current_entity and current_entity[type] label[2:]: current_entity[text] tokens[idx] elif label O: if current_entity: entities.append(current_entity) current_entity None if current_entity: entities.append(current_entity) return entities这个解码逻辑在项目中是直接可用的。要注意的是标签对齐的时候已经在前后各插入了一个特殊位置所以预测结果里的第一个和最后一个位置对应的标签本来就是-1或者无效标签用的时候直接跳过即可。4. 效果评估与调参经验4.1 指标怎么算才靠谱序列标注任务的评估不能只看整体准确率因为大部分token的标签是O如果模型把所有位置都预测成O准确率可能高达90%以上然而这对实体识别毫无意义。业界通用的做法是分别计算每个实体类别的精确率、召回率和F1值实体级别的判断标准是类型和位置都完全正确才算一个正确预测的实体。我建议用seqeval这个库来做评估它专门处理序列标注指标。使用方法也很简单from seqeval.metrics import classification_report, f1_score true_labels [...] # 真实标签列表的列表 pred_labels [...] # 预测标签列表的列表 print(classification_report(true_labels, pred_labels))如果不想引入额外依赖自己实现实体级评估也不难。先把标签序列按实体位置合并成实体三元组集合再计算集合之间的交并比就行。但用现成的库更省心也避免了自己写评估逻辑时引入bug的可能性。4.2 我试过的调参路径第一版模型我用max_len128、batch_size16、learning_rate2e-5跑了20个epoch观察验证集F1的曲线是缓慢上升的到第12个epoch左右开始出现轻微过拟合。后面加了EarlyStopping问题就解决了。Keras的EarlyStopping回调在最简单的配置下非常实用from keras.callbacks import EarlyStopping, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience3, restore_best_weightsTrue), ModelCheckpoint(best_model.h5, monitorval_loss, save_best_onlyTrue) ]调参方面的一个经验是序列标注任务对max_len非常敏感。如果业务场景中实体经常出现在长句的后半部分max_len设得太短会直接截断实体设太长又会增加显存占用和训练时间。我当时的做法是先去统计训练集中句子的长度分布发现95%的句子长度小于120个字所以才把max_len定为128。这样既不浪费也能覆盖绝大多数情况。关于学习率我试过1e-5、2e-5、5e-5三档结论是2e-5到5e-5之间效果差异并不大但1e-5时收敛速度明显变慢需要多跑好几个epoch才能追上。batch size方面从8调到16之后训练速度有明显提升效果基本持平再往上到32时显存就撑不住了。4.3 效果提升的两条进阶路线第一条路线是在BERT输出之上再接CRF层。BERT加全连接softmax有一个已知的缺点它没有建模标签之间的转移约束。比如B-PER后面不能直接接I-LOC这种约束在softmax输出下是无法体现的。CRF层可以自动学习这些转移规则整体上通常能再提升一到两个点的F1值。keras-bert这个方案要加CRF层也不复杂用keras-contrib的CRF层接在Dense输出后面配合稀疏标签训练即可。第二条路线是模型集成把不同随机种子训练出来的多个BERT模型做投票。这个方法见效快但测试时需要跑多个模型推理时间会成倍增加。我实际项目中用的还是单模型加CRF的组合性价比最高。另外还有一个小技巧在微调时先冻结BERT层只训练分类层几个epoch再解冻所有层一起训练有时候能避免初始阶段的剧烈波动训练过程更稳定。5. 常见问题速查与避坑指南5.1 经典报错与排查对照表我把实际使用keras-bert时遇到的问题整理成了一张速查表遇到类似问题可以直接对照。报错信息原因分析解决办法加载checkpoint时提示Not found权重路径错误或者下载的权重不匹配确认bert_model.ckpt和config.json是同一版本路径不要写错维度不匹配Dense层输入shape报错seq_len和加载模型时的seq_len不一致加载模型时指定seq_len和训练数据max_len保持一致显存溢出OOMbatch_size或max_len过大减小batch_size或降低max_len也可以用混合精度训练训练时loss为nan学习率过大或数据中存在非法字符降低学习率检查input_ids中是否有[UNK]意外的异常值fit时报Input shape不匹配数据集的shape和模型输入不一致确认input_ids、segment_ids、mask三个输入都传入了model.fit预测结果全部是O训练数据类不平衡或模型还未收敛检查label统计分布增加实体样本或延长训练epoch5.2 训练不收敛的几个原因训练不收敛这个事大多数时候不是模型本身的问题而是数据处理出了问题。最常见的一种是标签序列没有对齐我在2.3节说过的错位问题就表现为训练loss下降但验证集F1非常糟糕。另一种是padding位置的标签没有忽略掉模型学了一堆padding位置的“规律”导致验证集表现奇差。还有一种情况是数据集中存在空句子空句子进入BERT之后只有[CLS]和[SEP]对应的标签全是无效标签如果处理不当就会报维度错误或者产生奇怪的loss值。如果loss在下降但预测效果很差建议画一下每个标签的混淆矩阵看看是整体混乱还是某一个类别完全没学会。实体类别如果本身样本很少模型很容易把这类实体全部预测成O。解决思路是收集更多该实体类型的样本或者对这类实体的样本做上采样又或者在loss计算时给少数类加权重。5.3 经验提点最后分享几点我自己做序列标注项目时的体会。第一个就是一定先做小规模跑通再进行完整训练。先用100条数据把整个流程从头到尾跑一遍确认没有问题之后再上全量数据。别一上来就投460万条数据折腾几个小时才发现数据预处理有bug那种挫败感非常难受。第二个是保存模型时建议把预处理参数一起保存包括max_len、label2id、vocab路径等。序列标注项目的模型效果严重依赖预处理逻辑的一致性训练和预测必须用完全相同的预处理代码。第三个是不要盲目追求复杂模型先跑通基线再逐步加CRF、加数据增强。基线模型能拿到60分的话后面每一步优化都会让你看得清清楚楚。

相关新闻

Cocos Creator 3.0.0 实现 web-mobile 透明背景的完整指南

Cocos Creator 3.0.0 实现 web-mobile 透明背景的完整指南

1. 为什么要在 Cocos Creator 3.0.0 里折腾透明背景做过 H5 小游戏或者互动营销页的朋友大概率都遇到过这种需求:游戏画面不是全屏铺满,而是作为一个"图层"叠在网页已有的背景之上,或者嵌在某个卡片、弹窗、直播间挂件里。这时候如…

2026/10/17 16:51:51 阅读更多 →
Zemax单模光纤耦合仿真:POP与光线追迹的差异及避坑指南

Zemax单模光纤耦合仿真:POP与光线追迹的差异及避坑指南

简介:这是一份面向光学设计与光纤耦合方向学习者的Zemax实操范例,围绕单模光纤光束耦合系统的建模、分析与优化展开,适合具备一定Zemax基础、希望深入理解光纤耦合效率计算的工程师与学生参考。资源包内含1个doc文档,压缩包约504K…

2026/10/17 16:51:51 阅读更多 →
AI论文工具应用指南:本科毕业论文从选题到答辩实战拆解

AI论文工具应用指南:本科毕业论文从选题到答辩实战拆解

先泼一盆冷水:打开任何一个AI论文工具的详情页,你看到的可能是“一键生成万字初稿”“自动降重”“文献自动引用”这种话术。但真的把本科毕业论文丢给AI,绝大多数人会在第三章卡住——不是工具不行,而是不知道每个工具该在哪个环…

2026/10/17 16:50:51 阅读更多 →

最新新闻

Kepware OPC DA Client配置实战:三层结构与QC质量码诊断

Kepware OPC DA Client配置实战:三层结构与QC质量码诊断

简介:本资源是一份面向工业物联网(IIoT)开发者与自动化工程师的Kepware OPC DA Client实战入门教程,聚焦于使用KEPServerEX构建稳定、高效的数据采集通道。内容从零开始详解项目(Project)创建、Channel配置…

2026/10/17 21:00:06 阅读更多 →
蓝桥杯省赛“直线”试题解析:整点去重与精确计数

蓝桥杯省赛“直线”试题解析:整点去重与精确计数

第十二届蓝桥杯省赛的试题C“直线”,是我印象里非常典型的“看着简单,做起来全是坑”的结果填空题。题目只有一句话:平面上有2021个整点,横坐标范围是0到19,纵坐标范围是0到20,问这些点一共能确定多少条不同…

2026/10/17 21:00:06 阅读更多 →
RTSP日志实战:从协议交互到问题定位的完整指南

RTSP日志实战:从协议交互到问题定位的完整指南

1. 从"rtsp 日志"这四个字里能挖出什么搞流媒体的人看到"rtsp 日志"这个标题,第一反应多半是又爱又恨。爱的是,RTSP协议本身并不复杂,抓包加日志基本能把问题定位到八九不离十;恨的是,真正到了生产…

2026/10/17 21:00:06 阅读更多 →
全网最全的AI软件测试面试题(含答案+文档):用TaoToken统一Key跑通面试题里的API测试场景

全网最全的AI软件测试面试题(含答案+文档):用TaoToken统一Key跑通面试题里的API测试场景

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

2026/10/17 21:00:06 阅读更多 →
在VS Code中用MCP集成AI修图:NanoBanana实战指南

在VS Code中用MCP集成AI修图:NanoBanana实战指南

1. 先说结论:为什么要把修图能力塞进代码编辑器1.1 写代码的人其实每天都在“修图”你可能觉得修图是设计师的事,但只要你写过项目文档、搭过个人网站、做过自动化脚本、维护过电商后台,就一定会遇到这种场景:产品图带水印、截图背…

2026/10/17 21:00:06 阅读更多 →
GEE极端降水指数计算全流程:从CHIRPS数据到R95p趋势分析

GEE极端降水指数计算全流程:从CHIRPS数据到R95p趋势分析

简介:这份资源是一套基于Google Earth Engine(GEE)与NASA GPM IMERG V07数据的极端降水分析代码包,服务于遥感、水文与灾害风险研究场景,也适合有一定GEE基础、希望直接复用降水分析脚本的开发者。代码完整覆盖从研究区…

2026/10/17 20:59:06 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →