GA-LSTM预测代码调参实战:遗传算法优化超参数与PyTorch实现
简介这份资源是面向时间序列预测学习者与算法实践者的GA-LSTM预测模型Python实现包针对传统LSTM易过拟合、欠拟合及参数调优困难的问题用遗传算法对LSTM权重进行全局寻优可应用于股票价格、天气等复杂序列预测场景。压缩包共5个文件约9KB以2个py源码文件为核心分别承载遗传算法优化与LSTM网络构建逻辑另含2个pyc编译缓存与1个txt说明文档结构精简、便于快速阅读与二次修改。目前已有390人学习下载。代码覆盖数据预处理、模型定义、遗传算法适应度评估与选择交叉变异、训练迭代及预测评估等环节读者可据此理解GA与LSTM结合的完整流程掌握用MSE、MAE等指标衡量预测性能的方法并在此基础上迁移到自己的序列建模任务中。1. GA-LSTM 预测代码到底在优化什么从一组跑崩的超参说起很多人第一次拿到 GA-LSTM 遗传算法优化的 LSTM 预测代码会以为它是个更高级的模型结构其实不是。它优化的是 LSTM 的超参数——时间窗长度、隐藏层神经元数、学习率、批大小、训练轮数。这些参数手动调要么靠经验要么靠网格搜索参数一多组合就爆炸。遗传算法做的事就是把这些超参数编码成一条“染色体”用选择、交叉、变异去迭代搜索让验证集上的预测误差最小。适合谁手上有时间序列预测任务、已经跑通过基础 LSTM、但被调参折磨到怀疑人生的从业者。这篇笔记就按“先讲清 GA 和 LSTM 怎么接、再给可复现的 Python 实现、最后说清踩坑点”的顺序展开代码可以直接抄参数可以按你的数据改。2. GA 和 LSTM 怎么接染色体编码与适应度函数的设计2.1 为什么不是所有超参都值得丢给遗传算法遗传算法的搜索开销和染色体长度直接相关。染色体越长搜索空间越大收敛越慢。我一般只把对预测精度影响最大的 4 个参数放进染色体时间窗长度window_size、LSTM 隐藏层单元数hidden_size、学习率lr、批大小batch_size。训练轮数epochs不放进搜索因为每评估一次染色体都要完整训练一轮轮数再翻倍时间成本直接失控。常见做法是固定一个偏大的epochs配合早停策略让模型自己决定什么时候停。染色体编码用实数编码每个基因对应一个超参范围自己定。比如window_size取 5 到 60 的整数hidden_size取 16 到 256 的整数lr取 1e-4 到 1e-2 的对数均匀分布batch_size取 16 到 128 的 2 的幂次。编码方式决定了搜索的粒度粒度太细遗传算法退化成随机搜索粒度太粗容易错过好解。适应度函数是 GA 和 LSTM 之间的桥梁。每次评估一条染色体就用它解码出的超参构建一个 LSTM在训练集上训练在验证集上算一个误差指标比如 RMSE 或 MAE。误差越小适应度越高。这里有个血泪经验验证集必须和训练集在时间上隔开不能随机打乱否则时间序列的时序依赖被破坏适应度会虚高最后选出来的参数在真实预测时翻车。2.2 用 DEAP 搭一个最小可跑的 GA 搜索框架DEAP 是 Python 里比较轻量的遗传算法库不用自己手写选择、交叉、变异。下面这段代码搭了一个最小框架染色体是 4 个浮点数适应度函数先用一个模拟的 LSTM 评估函数占位后面再替换成真实训练。import random from deap import base, creator, tools, algorithms # 定义适应度最大化和个体类型 creator.create(FitnessMax, base.Fitness, weights(1.0,)) creator.create(Individual, list, fitnesscreator.FitnessMax) # 超参范围window_size, hidden_size, lr, batch_size BOUNDS [(5, 60), (16, 256), (1e-4, 1e-2), (16, 128)] def decode(individual): 把染色体解码成可用的超参 window int(round(individual[0])) hidden int(round(individual[1])) lr float(individual[2]) batch int(2 ** round(individual[3] ** 0.5)) # 映射到2的幂次附近 batch max(16, min(128, batch)) return window, hidden, lr, batch def evaluate(individual): 适应度函数这里先用模拟值后面替换成真实LSTM训练 window, hidden, lr, batch decode(individual) # 模拟假设存在一个最优组合越接近越好 score -(abs(window - 30) / 30 abs(hidden - 128) / 128 abs(lr - 1e-3) / 1e-3 abs(batch - 64) / 64) return (score,) toolbox base.Toolbox() toolbox.register(attr_float, random.uniform, 0, 1) toolbox.register(individual, tools.initRepeat, creator.Individual, toolbox.attr_float, n4) toolbox.register(population, tools.initRepeat, list, toolbox.individual) toolbox.register(evaluate, evaluate) toolbox.register(mate, tools.cxBlend, alpha0.5) toolbox.register(mutate, tools.mutGaussian, mu0, sigma0.2, indpb0.2) toolbox.register(select, tools.selTournament, tournsize3) def main(): pop toolbox.population(n20) hof tools.HallOfFame(1) stats tools.Statistics(lambda ind: ind.fitness.values) stats.register(max, max) algorithms.eaSimple(pop, toolbox, cxpb0.6, mutpb0.3, ngen10, statsstats, halloffamehof, verboseTrue) best hof[0] print(最优染色体:, best) print(解码超参:, decode(best)) if __name__ __main__: main()逻辑说明decode函数把 0 到 1 之间的浮点基因映射到实际超参范围其中batch_size做了幂次映射因为批大小通常取 2 的幂。evaluate是占位函数真实使用时要把 LSTM 训练和验证误差算进去。cxBlend是混合交叉mutGaussian是高斯变异selTournament是锦标赛选择这三个算子是时间序列超参搜索里比较稳的组合。参数说明种群大小n20适合快速验证实际跑建议 30 到 50交叉概率cxpb0.6、变异概率mutpb0.3是常用起点变异概率别超过 0.4否则搜索退化成随机游走。提示DEAP 的eaSimple每代都会打印统计信息第一次跑先把ngen设小一点比如 5 到 10确认流程通了再加大。3. 把 LSTM 训练塞进适应度函数PyTorch 实现与数据窗口构造3.1 时间序列滑窗构造与归一化顺序LSTM 预测代码里最容易翻车的地方不是模型结构而是数据窗口的构造顺序。时间序列要先做差分或去趋势再做归一化最后滑窗。如果先滑窗再归一化训练集和验证集的统计量会混在一起造成信息泄漏。我一般用MinMaxScaler在训练集上拟合然后 transform 验证集和测试集绝不 fit 验证集。滑窗函数要保证输入X和标签y的时序对齐。假设原始序列是series窗口长度是window那么第i个样本的输入是series[i:iwindow]标签是series[iwindow]。下面这个函数返回三维数组形状是(样本数, 窗口长度, 特征数)可以直接喂给 LSTM。import numpy as np from sklearn.preprocessing import MinMaxScaler def make_windows(series, window): 把一维序列转成LSTM需要的滑窗样本 X, y [], [] for i in range(len(series) - window): X.append(series[i:iwindow]) y.append(series[iwindow]) X np.array(X).reshape(-1, window, 1) y np.array(y) return X, y def prepare_data(series, window, train_ratio0.8): 先划分训练/验证再分别归一化和滑窗 n len(series) train_end int(n * train_ratio) train_series series[:train_end] val_series series[train_end - window:] # 验证集要包含窗口回溯部分 scaler MinMaxScaler() train_scaled scaler.fit_transform(train_series.reshape(-1, 1)).flatten() val_scaled scaler.transform(val_series.reshape(-1, 1)).flatten() X_train, y_train make_windows(train_scaled, window) X_val, y_val make_windows(val_scaled, window) return X_train, y_train, X_val, y_val, scaler逻辑说明prepare_data先按时间顺序切分训练集和验证集验证集往前多取window个点保证第一个验证样本的输入窗口完整。归一化只在训练集上 fit验证集用同一个 scaler transform。参数说明train_ratio0.8是常见起点如果数据量少可以调到 0.7window由 GA 搜索决定这里作为入参传入。3.2 用 PyTorch 写一个可被 GA 调用的 LSTM 训练函数下面这个函数接收 GA 解码出的超参完成一次完整的 LSTM 训练和验证返回验证集 RMSE。它会被注册到 DEAP 的toolbox.evaluate里替换掉之前的模拟函数。import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset import numpy as np class LSTMPredictor(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers1): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, _ self.lstm(x) out self.fc(out[:, -1, :]) # 取最后一个时间步 return out.squeeze(-1) def train_and_eval(X_train, y_train, X_val, y_val, hidden_size, lr, batch_size, epochs50, patience5): 训练LSTM并返回验证集RMSE device torch.device(cuda if torch.cuda.is_available() else cpu) model LSTMPredictor(hidden_sizehidden_size).to(device) optimizer torch.optim.Adam(model.parameters(), lrlr) criterion nn.MSELoss() train_ds TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train)) train_loader DataLoader(train_ds, batch_sizebatch_size, shuffleFalse) val_X torch.FloatTensor(X_val).to(device) val_y torch.FloatTensor(y_val).to(device) best_rmse float(inf) wait 0 for epoch in range(epochs): model.train() for xb, yb in train_loader: xb, yb xb.to(device), yb.to(device) optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() optimizer.step() model.eval() with torch.no_grad(): pred_val model(val_X) rmse torch.sqrt(criterion(pred_val, val_y)).item() if rmse best_rmse: best_rmse rmse wait 0 else: wait 1 if wait patience: break # 早停 return best_rmse逻辑说明LSTMPredictor只取 LSTM 最后一个时间步的输出接全连接层适合单步预测。train_and_eval里shuffleFalse是故意的时间序列训练集内部也不建议打乱否则相邻 batch 之间的时序连续性被破坏。早停patience5防止过拟合同时控制 GA 评估的时间成本。参数说明epochs50配合早停实际训练轮数通常远小于 50num_layers1是默认值如果欠拟合可以加到 2但 GA 搜索里不建议把层数也放进去搜索空间会太大。3.3 把真实评估函数接回 GA 主循环现在把train_and_eval包进evaluateGA 每评估一条染色体就完整跑一次 LSTM 训练。注意数据准备只做一次放在 GA 外面避免每条染色体都重复归一化和滑窗。# 假设 series 是你的原始时间序列 # X_train, y_train, X_val, y_val, scaler prepare_data(series, window30) def evaluate_real(individual): window, hidden, lr, batch decode(individual) # 注意window 变了滑窗数据也要重新构造 X_tr, y_tr, X_va, y_va, _ prepare_data(series, window) rmse train_and_eval(X_tr, y_tr, X_va, y_va, hidden_sizehidden, lrlr, batch_sizebatch) return (-rmse,) # DEAP 最大化适应度所以取负 toolbox.register(evaluate, evaluate_real)逻辑说明因为window是 GA 搜索的一部分每条染色体解码出的窗口长度不同滑窗数据必须重新构造。这是 GA-LSTM 比固定窗口 LSTM 慢的主要原因。参数说明如果数据量很大可以在 GA 外面预先算好几个候选窗口的滑窗数据用字典缓存evaluate_real里按window查表能省不少时间。4. 避坑与排查GA-LSTM 预测代码最常见的 5 个翻车点4.1 现象GA 收敛到一组超参但测试集误差远大于验证集原因验证集和测试集的时间划分有问题或者归一化时用了全量数据的统计量。时间序列里验证集和测试集必须严格在训练集之后不能有重叠。归一化 scaler 只能在训练集上 fit。解决检查prepare_data里的train_ratio切分点确认验证集和测试集没有用到未来信息。把 scaler 的fit和transform分开写验证集和测试集只 transform。4.2 现象GA 每代最优适应度几乎不变搜索像在随机游走原因变异概率太高或者染色体编码范围太宽导致好基因被破坏。另一个常见原因是适应度函数噪声太大LSTM 每次训练结果波动大GA 分不清哪条染色体真的好。解决把mutpb降到 0.1 到 0.2cxpb保持在 0.5 到 0.7。适应度噪声大的话每条染色体评估 2 到 3 次取平均或者固定随机种子。PyTorch 里可以用torch.manual_seed和np.random.seed固定。4.3 现象训练 loss 正常下降但验证 RMSE 一直很大原因LSTM 隐藏层单元数太多模型过拟合或者学习率太大验证集上的预测值震荡。GA 搜索出的hidden_size和lr组合可能偏向训练集。解决在适应度函数里加正则项比如对hidden_size做惩罚防止 GA 一味追求大模型。学习率搜索范围上限别超过 1e-2下限别低于 1e-5。4.4 现象GA 跑了几代就报内存不足原因每条染色体都重新构造滑窗数据X_train数组反复分配释放Python 垃圾回收跟不上。数据量大时尤其明显。解决用字典缓存不同window对应的滑窗数据evaluate_real里先查缓存。另外把batch_size的下限设大一点比如 32减少 DataLoader 的迭代次数。4.5 现象最优超参在训练集上表现很好换一批数据就崩原因GA 过拟合了验证集。遗传算法本身也是一种搜索搜索次数多了验证集就被“记住”了。这是 GA-LSTM 最隐蔽的坑。解决留一个独立的测试集GA 搜索结束后只在测试集上跑一次不参与任何选择。如果测试集误差和验证集差很多说明搜索过拟合减少 GA 代数或增大种群多样性。5. 让 GA-LSTM 真正可用的两个进阶技巧5.1 用并行评估把 GA 搜索时间压下来GA-LSTM 最大的成本是每条染色体都要训练一次 LSTM。种群 30、代数 20就是 600 次训练。单次训练 10 秒总共 100 分钟。DEAP 支持并行评估用multiprocessing把种群评估分发到多个进程。关键是把toolbox.register(map, pool.map)加进去注意 LSTM 训练函数里不要有全局状态数据用只读方式传入。import multiprocessing from deap import base pool multiprocessing.Pool(processes4) toolbox.register(map, pool.map) # 后续 eaSimple 会自动用 pool.map 并行评估逻辑说明pool.map会把种群里的个体分发到 4 个进程每个进程独立跑evaluate_real。参数说明进程数别超过 CPU 核心数GPU 训练的话每个进程都会占显存进程数要按显存大小调。如果显存不够改成 2 个进程或者用 CPU 训练。5.2 用验证集误差曲线判断 GA 是否真的在收敛不要只看 GA 打印的最优适应度那个值受随机性影响大。我一般把每代的最优验证 RMSE 和平均验证 RMSE 都存下来画一条曲线。如果最优曲线震荡但平均曲线在下降说明种群在探索还有希望如果两条曲线都平了说明收敛了再跑下去只是浪费时间。下面这段代码把统计信息存成列表跑完画图。import matplotlib.pyplot as plt history {gen: [], best: [], avg: []} def record(stats): gen len(history[gen]) history[gen].append(gen) history[best].append(stats[max]) history[avg].append(stats[avg]) # 在 eaSimple 的 stats 里注册 record或者手动在每代后调用 # 跑完后画图 plt.plot(history[gen], history[best], labelbest) plt.plot(history[gen], history[avg], labelavg) plt.xlabel(generation) plt.ylabel(fitness (negative RMSE)) plt.legend() plt.savefig(ga_convergence.png)逻辑说明best曲线反映当前找到的最好解avg曲线反映种群整体水平。两条曲线都平了就可以停。参数说明如果best还在降但avg平了说明种群多样性不够可以适当提高变异概率。如果avg比best差很多说明种群分化大可以增加选择压力。我自己跑 GA-LSTM 的习惯是先用小种群、少代数跑通流程确认数据窗口和归一化没问题再加大搜索规模。每次改数据或者改预测目标都要重新检查验证集划分别偷懒复用上一次的切分。这个方向值不值得做如果你的 LSTM 调参时间超过两天GA 搜索半天到一天能给出一个稳定可用的超参组合那就值得。如果数据量小到手动试几组就能找到好参数GA 反而是过度工程。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

客户流失预测实战:生存分析+随机森林+Flask部署全链路拆解

客户流失预测实战:生存分析+随机森林+Flask部署全链路拆解

简介:这份资源是面向计算机、人工智能及数据科学相关专业学生与从业者的客户留存分析与流失预测完整项目,适用于电信运营商、保险公司等需要提升客户留存率的业务场景,也可作为毕业设计或课程作业的参考方案。压缩包共62个文件,约…

2026/10/9 3:41:17 阅读更多 →
Java跨平台原理深度解析:字节码、JVM 与一次编译到处运行的真相

Java跨平台原理深度解析:字节码、JVM 与一次编译到处运行的真相

1. 跨平台问题的本质:你踩中的“认知陷阱”在哪1.1 跨平台到底在跨什么“平台”先把这个词拆开看。平台这个概念,多数时候指的是操作系统与底层硬件架构的组合,比如 Windows x86、Linux ARM、macOS Apple Silicon。任何一个程序最终都要通过操…

2026/10/9 3:40:17 阅读更多 →
MySQL JOIN优化实战:从EXPLAIN看懂执行计划,彻底告别慢查询

MySQL JOIN优化实战:从EXPLAIN看懂执行计划,彻底告别慢查询

前阵子接到一个线上慢查询,三张表JOIN,接口一到晚高峰就超时,DBA把EXPLAIN丢给我,我看到一整排的ALL和Extra列里的“Using join buffer”,就知道这条SQL又没吃到JOIN算法的红利,纯靠嵌套循环硬扛。后来花了…

2026/10/9 3:40:17 阅读更多 →

最新新闻

AI广告生成技术原理与实时ROI预测应用

AI广告生成技术原理与实时ROI预测应用

我无法基于当前输入生成符合要求的博文。原因如下:项目标题“MiniMax 将参展纽约广告周”属于企业公关/市场活动类信息,本质是一条新闻通稿式短讯,不含任何可拆解的技术点、实操路径、原理机制、工具链或用户可复现的动作;项目正文…

2026/10/9 4:14:37 阅读更多 →
AI模型微调实战:LoRA与QLoRA技术解析

AI模型微调实战:LoRA与QLoRA技术解析

我无法基于当前输入生成符合要求的博文。原因如下:项目标题“Mistral CEO 发文表兴奋”缺乏明确的技术领域、具体事件背景、可操作内容或实际问题指向;项目正文为空,无任何原始描述可供理解上下文;关键词与摘要描述均为空&#xf…

2026/10/9 4:14:37 阅读更多 →
后端进阶实战:事务、缓存、并发、部署的关键设计避坑指南

后端进阶实战:事务、缓存、并发、部署的关键设计避坑指南

写这个系列写到第三篇,我明显感觉沉淀下来的东西越来越偏"实务"了。前两篇聊的多是语法和框架层面的零散记忆,这篇我想换个角度,把这些年真正在项目里反复用到、也反复踩过坑的概念重新梳理一遍。说是知识点总结,其实就…

2026/10/9 4:14:37 阅读更多 →
基于SpringBoot的船舶维保管理系统设计与实践

基于SpringBoot的船舶维保管理系统设计与实践

船舶维保管理系统这个题目,这几年在毕业设计里出现频率相当高。用的人多,说明这个方向确实能打:业务场景明确、流程闭环完整、前后端都有足够的发挥空间,而且跟真实的工业信息化场景贴合得很紧。我前后帮朋友公司搭过类似的船队维…

2026/10/9 4:14:37 阅读更多 →
写Prompt总翻车?把任务、对象、依据、交付说清楚

写Prompt总翻车?把任务、对象、依据、交付说清楚

搭 AI 对话工具到现在,我前前后后写了不下几百条 prompt,从最开始只会丢一句"帮我写个文案",到后来能稳定产出我要的东西,中间踩过的坑真的能写一本小册子。今天不聊那些花里胡哨的"万能公式",就聊…

2026/10/9 4:14:37 阅读更多 →
9点标定+Halcon+C#:产线视觉引导链路实战拆解

9点标定+Halcon+C#:产线视觉引导链路实战拆解

简介:本资源面向工业机器人与机器视觉方向的开发者,提供一套完整的9点标定Halcon程序及C#联合Halcon的源码实现,可用于机器人手眼标定、相机坐标与机器人坐标映射等实际项目场景。包内共577个文件,以385个ico图标、73个ssk皮肤资源…

2026/10/9 4:13:37 阅读更多 →

日新闻

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 阅读更多 →