【RustyML入门】2.8. DBSCAN
2.8. DBSCANDBSCANDensity-Based Spatial Clustering of Applications with Noise把落在稠密区域的点聚成簇其余的点标记为噪声。和 KMeans 不同DBSCAN 不需要你预先给出簇的数量。它能找出任意形状的簇而不只是圆滚滚的团块还会明确标出离群点。作为交换你要设置eps和min_samples这 2 个密度参数而不是k。本页讲解 RustyML 实现的密度模型、挑选eps的方法、predict到底做了什么以及 O(n^2) 算法的开销。2.8.1. 密度模型核心点、边界点与噪声点DBSCAN 给每个训练点分配 3 种角色之一eps邻域半径和min_samples密度阈值这 2 个参数共同决定角色。一个点的邻域是所有落在它eps范围内的点。边界取闭区间距离恰好等于eps的点也算邻居。RustyML 的邻域查询还会把点自身算进去因为它到自己的距离是 0而 0 永远 eps。这一点决定了min_samples的含义角色本实现中的定义核心点邻域内至少有min_samples个点含自身。也就是说至少有min_samples - 1个其他点落在eps内。边界点本身不是核心点但落在某个核心点的eps范围内因此加入该核心点所在的簇。噪声点既非核心点也非边界点。标记为-1。所以min_samples把查询点自身也算在内这和 scikit-learn 的算法一致。如果你把“邻居”理解成只算别的点、不含自身那就要把min_samples减去 1。当min_samples 2时只要eps内还有另外 1 个点这个点就成为核心点。簇靠密度连通形成。fit从一个未访问的核心点出发认领它再沿着核心点的邻域做洪泛式扩散把每个能到达的点都收进来。边界点会被收进簇里却不会让洪泛继续蔓延因为fit不会展开边界点的邻域。洪泛始终碰不到的点就留在-1。由此可以得出 2 点结论。第一fit里没有任何随机数生成器因此这里的聚类完全确定。fit按行号升序处理各点每份邻居列表也按下标排序返回所以相同输入永远给出相同的标签和相同的簇 id。第二当一个边界点同时落在 2 个簇的触及范围内时哪个簇的洪泛先扩散到它它就归哪个簇。由于fit按行号顺序处理那就是 id 较小的那个簇。这样就以确定的方式解决了经典 DBSCAN 里的边界归属二义性而不是把它留作未定义。2.8.2. 构造与配置估计器构造函数接收这 2 个密度参数并对它们做校验pub fn new(eps: f64, min_samples: usize) - ResultSelf, Error pub fn with_metric(self, metric: DistanceCalculationMetric) - ResultSelf, Errornew在eps非正或非有限、或者min_samples为0时返回Error::InvalidParameter。距离度量默认为欧几里得可以用with_metric改掉它这个方法同样返回Result因为它要校验闵可夫斯基阶数见 2.8.4 节。Default::default()给出eps 0.5、min_samples 5、欧几里得。这些数字只是占位符并不是适配你数据的好默认值。参数类型含义校验epsf64邻域半径单位取决于所用度量必须为正且有限min_samplesusize核心点的邻域规模含自身必须 0metricDistanceCalculationMetric距离函数闵可夫斯基p必须 1且有限构造完成后getter 会暴露存下来的状态get_epsilon、get_min_samples、get_metric。训练完成后还会暴露get_labels() - OptionArray1isize和get_core_sample_indices() - OptionArray1usize。各错误变体如何对应到真实的失败见错误处理。2.8.3. 训练并读取标签fit跑完聚类并把结果存下来。fit_predict做同样的事同时还会把标签数组返回。get_labels则用来事后读取存下的标签。标签的类型是Array1isize簇 id 按发现顺序取0, 1, 2, ...-1标记噪声。正是这个带符号的isize类型才让噪声能和簇 id 存在同一个数组里不需要额外的掩码。userustyml::machine_learning::DBSCAN;usendarray::Array2;fnmain(){// 两个紧凑的点团外加一个孤立点。letdataArray2::from_shape_vec((9,2),vec![0.0,0.0,0.1,0.0,0.0,0.1,0.1,0.1,// 原点附近的点团 A10.0,10.0,10.1,10.0,10.0,10.1,10.1,10.1,// (10, 10) 附近的点团 B5.0,5.0,// 噪声离两个点团都很远],).unwrap();letmutdbscanDBSCAN::new(0.5,2).unwrap();letlabelsdbscan.fit_predict(data).unwrap();letn_clusterslabels.iter().filter(|l|l0).map(|l|l).max().map_or(0,|m|m1);letn_noiselabels.iter().filter(|l|l-1).count();println!(labels {:?},labels);println!(clusters {},n_clusters);println!(noise pts {},n_noise);// core_sample_indices 只保存达到核心点标准的行按升序排列。letcoresdbscan.get_core_sample_indices().unwrap();println!(core rows {:?},cores);}点团 A 成为簇0因为fit最先发现它。点团 B 成为簇1。孤零零落在(5, 5)的那个点留在-1。min_samples 2、每个点团 4 个点因此每个点团里的点都是核心点core_sample_indices就是[0,1,2,3,4,5,6,7]噪声那一行不在其列。labels [0, 0, 0, 0, 1, 1, 1, 1, -1], shape[9], strides[1], layoutCFcf (0xf), const ndim1 clusters 2 noise pts 1 core rows [0, 1, 2, 3, 4, 5, 6, 7], shape[8], strides[1], layoutCFcf (0xf), const ndim1fit在动手之前先校验输入零行矩阵会给出Error::EmptyInput数据里任何NaN或无穷值都会给出Error::NonFinite。这体现了职责的划分像非有限的eps这样糟糕的超参数在构造时就报InvalidParameter而数据里糟糕的取值则在训练时报NonFinite。2.8.4. 距离度量with_metric接受DistanceCalculationMetric的 3 个变体Euclidean默认L2、ManhattanL1和Minkowski(p)广义的 Lp 范数。Minkowski(2.0)得到的标签和Euclidean完全一样Minkowski(1.0)得到的标签和Manhattan完全一样。要用 L1 或 L2就直接用有名字的那两个变体。把Minkowski留给真正的分数阶或更高阶。userustyml::machine_learning::{DBSCAN,DistanceCalculationMetric};usendarray::array;fnmain(){letdataarray![[0.0,0.0],[0.1,0.0],[0.0,0.1],[5.0,5.0],[5.1,5.0],[5.0,5.1],];letmutdbscanDBSCAN::new(0.5,2).unwrap().with_metric(DistanceCalculationMetric::Manhattan).unwrap();letlabelsdbscan.fit_predict(data).unwrap();println!({:?},labels);// 两个簇没有噪声}构造函数会拒绝低于1包括0.5或非有限的闵可夫斯基阶数这样的阶数会破坏三角不等式进而破坏邻域判定。换一种度量改变的是eps的单位而不仅仅是它的含义。同一组点在曼哈顿距离下的跨度比欧几里得距离更大因此为某个度量调好的eps换到另一个度量就不对了。每次换度量都要重新调eps。各度量的定义及其取舍见距离度量。2.8.5. 选择 epsk 距离启发式eps是最常被调错的参数。本节给出一套具体的方法来选它而不必靠试错。对每个点量出它到第 k 近邻的距离。取k min_samples并把点自身也算上于是下标0就是距离为0的它自己。把所有这些 k 距离按升序排好再看这条曲线。稠密簇内部的点 k 距离很小噪声点的 k 距离很大。曲线在成簇的点上一直平缓走低随后在开始碰到离群点时在“拐点”处陡然上扬。拐点处的 k 距离就是个不错的eps取值大到足以连起真正的簇又小到能把离群点晾在外面。这个排序步骤和一次 KNN 查询返回的结果一致。你可以直接用公开的度量调度器把这些 k 距离算出来userustyml::machine_learning::DistanceCalculationMetric;usendarray::array;fnmain(){// 5 个点组成的密集簇外加 2 个散落的离群点。letdataarray![[0.0,0.0],[0.2,0.1],[0.1,0.2],[0.3,0.0],[0.0,0.3],[4.0,4.0],[8.0,1.0],];letmetricDistanceCalculationMetric::Euclidean;letmin_samples3usize;// k 距离图所用的 kletndata.nrows();// 每个点的 k 距离到它第 k 近邻的距离含自身。letmutk_dists:Vecf64(0..n).map(|i|{letmutd:Vecf64(0..n).map(|j|metric.distance(data.row(i),data.row(j))).collect();d.sort_by(|a,b|a.partial_cmp(b).unwrap());d[min_samples-1]// 下标 0 是自身距离 0}).collect();k_dists.sort_by(|a,b|a.partial_cmp(b).unwrap());// 从左到右看这条曲线末尾陡然上扬处就是拐点。println!(sorted k-distances: {:?},k_dists);}打印出来的曲线里那段平缓的前缀对应的就是成簇的点。在曲线开始上扬的地方挑eps。min_samples则留给密度下限常见的起点是2 * n_features。数据噪声大就调高它因为要求的邻居越多噪声剔除就越严格。数据干净、维度又低就往n_features 1调低。因为 RustyML 把点自身也算进去min_samples 1会让每个点都成为核心点于是产出 0 个噪声点这多半不是你想要的结果。2.8.6. predict 到底做了什么以及它为何不是经典 DBSCAN教科书里的 DBSCAN 没有面向新点的predict方法这个缺失是根本性的而不是疏漏。DBSCAN 里的簇归属是直推式transductive的一个点的标签取决于整个邻域的密度。往数据里加入一个新点可能把它变成核心点也可能把两个原本分开的簇合并成一个或者挪动某条边界的落点。想给新点正确地打上标签就少不了在合并后的整个集合上重新做一次密度分析。RustyML 依然提供了predict方法。它做的是一件比重新做密度分析更窄、更省的事。本节说清楚它到底做了什么userustyml::machine_learning::DBSCAN;usendarray::array;fnmain(){lettrainarray![[0.0,0.0],[0.1,0.0],[0.0,0.1],[0.1,0.1],[10.0,10.0],[10.1,10.0],[10.0,10.1],[10.1,10.1],];letmutdbscanDBSCAN::new(0.5,2).unwrap();dbscan.fit(train).unwrap();// 把每个新点归到它最近核心点所属的簇前提是落在 eps 之内。letqueriesarray![[0.05,0.05],// 位于点团 A 内 - 0[10.05,10.05],// 位于点团 B 内 - 1[5.0,5.0],// 远离所有核心点 - 噪声 (-1)];letpredsdbscan.predict(queries).unwrap();println!({:?},preds);// preds 依次是 [0, 1, -1]对应每个查询点。}predict会为每个查询点找到它最近的核心点。它只在fit期间存下的核心点里搜索而不是整个训练集。如果查询点落在eps之内就返回那个核心点的簇标签否则返回-1。predict只挑最近的那一个核心点不会去检查eps内的每一个核心点。eps这道闸取闭区间。predict从不创建新簇从不把查询点提拔为核心点也从不重新跑一遍密度洪泛。所以predict(x)的结果并不等于把x加进训练数据后再调用一次fit。把predict当成一种快速、近似的手段用来把留出的点分派到fit已经找到的那些簇里。如果想要真正的 DBSCAN 语义就在扩大后的数据集上重新调用fit。在fit之前调用predict会得到Error::NotFitted。特征数不匹配会得到Error::DimensionMismatch。查询里出现非有限值会得到Error::NonFinite。空输入则返回空数组。2.8.7. KMeans 失手的双环选 DBSCAN 而不是 KMeans主要理由在于形状。KMeans 用直线边界围着k个质心切分空间因此只能划出凸的、大致圆形的区域。DBSCAN 顺着密度走能勾勒任意形状。两个同心环把这种差别体现得很清楚两环绕着共同的圆心并非线性可分KMeans 会一刀直接切穿两者而 DBSCAN 会把每个环当作一条连通的链走下来。userustyml::machine_learning::{DBSCAN,KMeans};usendarray::Array2;fnmain(){// 造 2 个同心环内环半径 1外环半径 4。letmutcoords:Vecf64Vec::new();letinner12usize;forkin0..inner{lettkasf64/innerasf64*std::f64::consts::TAU;coords.push(t.cos());coords.push(t.sin());}letouter28usize;forkin0..outer{lettkasf64/outerasf64*std::f64::consts::TAU;coords.push(4.0*t.cos());coords.push(4.0*t.sin());}letdataArray2::from_shape_vec((innerouter,2),coords).unwrap();// eps 覆盖得住环上相邻点的间距却覆盖不了两环之间 3 个单位的空隙。letmutdbscanDBSCAN::new(1.2,2).unwrap();letdbdbscan.fit_predict(data).unwrap();letdb_clustersdb.iter().filter(|l|l0).map(|l|l).max().map_or(0,|m|m1);letdb_noisedb.iter().filter(|l|l-1).count();// KMeansk 2固定种子以保证可复现。letmutkmKMeans::new(2,100,1e-4).unwrap().with_random_state(0);letkm_labelskm.fit_predict(data).unwrap();// km_inner0 和 km_outer0 统计 KMeans 簇 0 里内环点和外环点各有多少个。letkm_inner0(0..inner).filter(|i|km_labels[i]0).count();letkm_outer0(inner..innerouter).filter(|i|km_labels[i]0).count();println!(DBSCAN: {} clusters, {} noise,db_clusters,db_noise);println!(KMeans cluster 0: {} inner-ring {} outer-ring points,km_inner0,km_outer0);}DBSCAN 把这 2 个环完整还原找到 2 个簇、0 个噪声点内环是一个标签外环是另一个。KMeans 用一条过原点的直线把平面劈开因此它的 2 个簇都混着内环和外环的点。KMeans 根本无法表示一个环。DBSCAN: 2 clusters, 0 noise KMeans cluster 0: 6 inner-ring 14 outer-ring points想用真实标签或内在指标给这类结果打分见聚类指标。2.8.8. 开销、索引与并行朴素的 DBSCAN 运行时间是 O(n^2)每个点都要跑一次区域查询而暴力的区域查询要扫过全部 n 个点。RustyML 用 kd 树压低这个常数。fit期间RustyML 会在数据上建一棵 kd 树把每次区域查询的平均耗时压到大约 O(log n)。这只在数据至多有 8 个特征时才成立DBSCAN_KD_TREE_MAX_DIMS。维度一旦超过 8kd 树就剪不好枝了原因是维度灾难几乎每个点看起来都离其他点“很远”。于是fit会退回暴力扫描。这条路径上聚类结果依旧正确只是更慢。在使用高维数据之前先用 PCA 或 t-SNE 做降维。这样既能给索引提速也是因为不管用什么索引欧几里得邻域在高维里都会失去意义。暴力区域查询会借助 rayon 在邻居间并行。触发条件是扫描工作量n_samples * n_features越过一道标定好的元素闸默认262_144。簇扩张那个循环本身是串行的因为它是一次洪泛填充。所以并行帮到的是单次区域扫描而不是整体的控制流。predict会在n_queries * n_core_points * n_features越过同一道闸时按查询点并行。你可以通过crate::tuning门面调节这些闸无需重新编译。怎么调见性能调优与并行闸的机制见并行归约。还有一道护栏如果某个病态数据集产出了isize::MAX个簇fit会返回Error::Computation而不是让标签计数器溢出。DBSCAN 换给你的是任意形状的簇、自动的离群点检测以及无需去猜的k。作为代价它在最坏情况下要花二次方时间。kd 树能在低维里降低这个开销却无法把它去掉。当各簇密度不同时DBSCAN 还对eps很敏感因为单一的全局eps没法同时贴合一个稠密簇和一个稀疏簇。2.8.9. 持久化训练好的DBSCAN用save_to_path和load_from_path序列化。这两个方法都使用紧凑的 postcard 二进制格式。保存下来的数据既包含超参数也包含predict需要的训练态存下的核心点及其标签。重新加载的模型无需再见到原始训练集就能给出同样的预测。userustyml::machine_learning::DBSCAN;usendarray::array;fnmain(){letdataarray![[0.0,0.0],[0.1,0.0],[0.0,0.1],[0.1,0.1],[10.0,10.0],[10.1,10.0],[10.0,10.1],[10.1,10.1],];letmutdbscanDBSCAN::new(0.5,2).unwrap();dbscan.fit(data).unwrap();letpathdbscan_model.bin;dbscan.save_to_path(path).unwrap();letloadedDBSCAN::load_from_path(path).unwrap();letpredsloaded.predict(array![[0.05,0.05],[10.05,10.05],[5.0,5.0]]).unwrap();println!({:?},preds);// preds 仍是 [0, 1, -1]和 fit 给出的结果一致。std::fs::remove_file(path).unwrap();}关于格式细节、版本兼容的注意事项以及持久化如何与 crate 其余部分交互见深入模型持久化。

相关新闻

开发APP应用时应有的策划、开发、思路

开发APP应用时应有的策划、开发、思路

随着移动互联网的发展,使得APP应用市场呈现出更火热的发展趋势,同时也使长沙APP开发行业迎来了春天。许多企业都争先恐后去开发APP应用,如此就使得APP应用之间的竞争越发的激烈。长沙APP制作创研移动认为,在这样的情况下&#xff…

2026/10/18 18:55:31 阅读更多 →
C++多态机制:从虚函数表到现代实现

C++多态机制:从虚函数表到现代实现

1. C多态的本质与实现原理 多态是面向对象编程的三大特性之一(封装、继承、多态),它允许不同类的对象对同一消息做出不同响应。在C中,多态主要通过虚函数机制实现,其核心原理可以概括为: 静态多态&#xf…

2026/10/18 18:55:24 阅读更多 →
【RustyML入门】2.7. KMeans聚类

【RustyML入门】2.7. KMeans聚类

2.7. KMeans聚类 KMeans 把样本划分到固定数量的簇里。它交替执行两步:先把每个点分配给最近的质心,再把每个质心重新计算为其成员的均值。RustyML 的实现是一套并行的 Lloyd 算法,配上 k-means 初始化和「重启若干次、取最优」的策略。如果…

2026/10/18 9:14:44 阅读更多 →

最新新闻

HC32系列J-Flash烧录指南:算法配置与量产脚本全攻略

HC32系列J-Flash烧录指南:算法配置与量产脚本全攻略

简介:面向华大HC32系列单片机开发者的J-Link补丁包,专门解决J-Flash软件无法识别华大芯片、无法下载Hex文件的问题。安装补丁后,开发者可在J-Flash软件中直接看到并选择HC32L110、HC32F003、HC32F19X等常见型号,一键完成程序烧录&…

2026/10/18 18:55:04 阅读更多 →
基于SSM的连锁咖啡店后台管理系统:从库存到订单的全链路设计

基于SSM的连锁咖啡店后台管理系统:从库存到订单的全链路设计

简介:这套基于Java开发的连锁咖啡店后台管理系统源码,主要面向计算机相关专业的学生、教师及行业开发者,可满足毕业设计、课程设计、项目演示或初期立项的多重需求。压缩包共25个文件,以13个Java源文件为主体,配合9个X…

2026/10/18 18:55:04 阅读更多 →
验算点法本质是坐标重定向而非找点

验算点法本质是坐标重定向而非找点

简介:本资源是面向结构工程专业学生、青年工程师及可靠性分析初学者的MATLAB实践工具包,聚焦结构可靠性分析核心方法——验算点法(MPP/HL)与直接抽样法,解决结构在地震、风荷载等不确定性作用下的失效概率评估与安全裕…

2026/10/18 18:55:04 阅读更多 →
LSTM空气质量预测系统:Django全栈部署实战指南

LSTM空气质量预测系统:Django全栈部署实战指南

简介:这是一套面向计算机专业本科生的高分毕业设计级空气质量监测与预测系统源码,融合LSTM时序建模能力与Django Web工程实践,适用于毕业设计、课程设计及期末大作业场景,兼顾算法理解与全栈开发训练。资源共260个文件&#xff0c…

2026/10/18 18:55:04 阅读更多 →
高性能运动控制器实时性对比:PMAC/ISEL/ACS在半导体精密控制中的硬核分水岭

高性能运动控制器实时性对比:PMAC/ISEL/ACS在半导体精密控制中的硬核分水岭

简介:本资源是一份面向工业自动化与精密制造领域工程师、设备选型人员及技术决策者的高性能运动控制器深度对比研究报告,聚焦PMAC(欧姆龙)、ISEL和ACS三大国际厂商在半导体、数控机床、机器人及激光加工等高要求场景下的技术差异与…

2026/10/18 18:55:04 阅读更多 →
集成学习回归实战:从评估到调参的完整指南

集成学习回归实战:从评估到调参的完整指南

1. 集成学习在回归场景的整体设计思路1.1 单模型容易“翻车”,问题到底出在哪先说一个我经常见到的现象:很多初学者拿着一棵决策树直接做回归,训练集上R高得离谱,一到测试集就崩。换成线性回归,预测值甚至可能出现负数…

2026/10/18 18:54:04 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/18 12:16:16 阅读更多 →