尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STR混合图谱分析:人数判别、比例估计与去噪流水线

STR混合图谱分析:人数判别、比例估计与去噪流水线 简介面向2025年深圳杯数学建模竞赛D题的完整分析论文适合参赛队伍、数学建模学习者及需要复盘赛题的研究者使用。文档围绕问题重述、问题分析、模型假设与符号定义展开并依次给出问题一至问题三的建模与求解包括特征工程设计、分类模型结构与数学表达、模型总体性能对比及不同贡献者数量下的表现还涉及贡献者兼容性筛选、混合比例优化估计、组合匹配评分函数与定量评估指标。资源包共1个PDF文件压缩包约2.71MB便于直接查阅与打印批注。目前已有330人学习。读者可从中获得较完整的赛题拆解框架、模型构建思路、性能比较视角与评估指标设计参考也可结合文中可运行代码和数据复现分析过程需注意原文由OCR识别整理个别字词可能存在误识阅读时可结合上下文与建模逻辑还原。1. 混合 STR 图谱分析一份把四问串成流水线的竞赛论文到底能用在哪把一个多人混合的 STR 峰图丢过来多数人的第一反应是数峰——某个基因座上冒出四五个峰就估三四个人混合。真拿数据跑一遍这个判断错得相当稳定低于基线的杂峰、PCR 拖尾、位点重叠任何一个因素都能把人数估高一位。这份资源就是围绕这件事展开的一份 2025 年某数学建模竞赛 D 题的完整分析论文 PDF把贡献者人数判别、DNA 比例估计、基因型反推、混合图谱去噪四个问题串成一条可复现的流水线正文里带模型公式推导附件里有可运行代码和原始数据。适合三类人正在准备数学建模竞赛、想找一个从特征工程到分类到组合优化全覆盖的完整案例做法医 DNA 数据分析入门、想搞清峰高和比例之间到底怎么换算以及只是想拿一份能跑通的 GBDT 多分类加最小二乘拟合代码当模板的工程师。它不能直接当生产工具用但作为一条完整的建模链路样本够扎实。2. STR 图谱特征工程把一张峰图压成二十余维可训练向量2.1 为什么先抠特征而不是先调模型原文里梯度提升树拿到 0.9608 的准确率随机森林 0.7255多层感知器 0.7549 但交叉验证只有 0.3603。这组数字经常被当成GBDT 更强的证据实际不是。四个模型喂的是同一份特征矩阵差距的来源是特征本身的结构树模型天生擅长处理阈值型、比值型、计数型的离散切分而 MLP 和 SVM 拿到这种量纲混乱、分布右偏的特征必须先标准化、再指望隐层自己学出等价的分箱逻辑样本量不够时直接过拟合。原文特征重要性图里排第一的是expected_allele_ratio权重超过 0.5后面跟着allele_count、height_count、height_min、height_top4、size_range、non_ol_allele_count。这几个都是数出来的而不是学出来的——每个个体在一个基因座上理论上有两个等位基因k 人混合的单个基因座最多能观测到 2k 个峰所以等位基因计数天然就是人数的上界估计。把这条先验直接写成特征模型要做的只是校准噪声难度降了一个量级。我一般会先把特征分成四组再动手等位基因结构组总数、非 OL 数、单座最大值、≥4 峰的座位数、峰高统计组均值、标准差、变异系数、极值、排序比值、片段长度组Size 的均值、标准差、极差、逻辑编码组OL 比例、16 个核心基因座的 one-hot。分组的意义在于后面做消融哪一组贡献大一眼能看出来。2.2 从峰图原始表到样本级特征矩阵原始数据是一张长表一行一个峰字段至少要有样本号、基因座、等位基因编号、峰高、片段长度。下面的脚本按基因座聚合再按样本汇总可以整段抄进项目里跑。import numpy as np import pandas as pd CORE_LOCI [ D3S1358, vWA, D16S539, CSF1PO, TPOX, D8S1179, D21S11, D18S51, D2S441, D19S433, TH01, FGA, D22S1045, D5S818, D13S317, D7S820, ] def locus_block(g: pd.DataFrame, rel_floor: float 0.05) - dict: 单个基因座 - 一组标量特征。rel_floor 是相对最高峰的丢弃阈值。 g g[g[height] 0].copy() if g.empty: return {allele_count: 0, non_ol_allele_count: 0, height_sum: 0.0} h g[height].to_numpy(dtypefloat) # 先丢绝对噪声峰再丢相对低峰低于最高峰 rel_floor 的视为拖尾或背景 keep h h.max() * rel_floor g, h g[keep], h[keep] h_desc np.sort(h)[::-1] is_ol g[allele].astype(str).str.upper().str.startswith(OL).to_numpy() size g[size].to_numpy(dtypefloat) return { allele_count: int(len(h)), non_ol_allele_count: int((~is_ol).sum()), ol_ratio: float(is_ol.mean()), height_sum: float(h.sum()), height_mean: float(h.mean()), height_std: float(h.std(ddof1)) if len(h) 1 else 0.0, height_cv: float(h.std(ddof1) / h.mean()) if len(h) 1 and h.mean() 0 else 0.0, height_min: float(h.min()), height_max: float(h.max()), top1_top2_ratio: float(h_desc[0] / h_desc[1]) if len(h) 1 and h_desc[1] 0 else np.nan, height_top4_share: float(h_desc[:4].sum() / h.sum()), size_range: float(size.max() - size.min()) if len(size) 1 else 0.0, size_std: float(size.std(ddof1)) if len(size) 1 else 0.0, } def sample_vector(df: pd.DataFrame) - pd.Series: 整份样本 - 一行特征。所有列都只用本样本信息不碰标签。 feats, counts {}, [] for locus, g in df.groupby(locus): b locus_block(g) counts.append(b[allele_count]) for k, v in b.items(): feats[f{locus}__{k}] v counts np.asarray(counts, dtypefloat) total counts.sum() # 跨座位聚合这几个才是人数判别的核心抓手 feats[allele_count_sum] float(total) feats[allele_count_mean] float(total / len(CORE_LOCI)) feats[allele_count_max] float(counts.max()) # 人数的下界估计 feats[allele_count_p90] float(np.percentile(counts, 90)) feats[n_loci_ge4] float((counts 4).sum()) # 至少 2 人混合的座位数 feats[n_loci_ge6] float((counts 6).sum()) # 至少 3 人混合的座位数 feats[expected_allele_ratio] float(counts.max() / 2.0) # 可在线算的人数下界比例 feats[height_cv_mean] float(np.nanmean([v for k, v in feats.items() if k.endswith(__height_cv)])) feats[size_range_mean] float(np.nanmean([v for k, v in feats.items() if k.endswith(__size_range)])) return pd.Series(feats)逻辑说明locus_block先做两段过滤再统计h 0挡掉已标记的无效峰h h.max() * rel_floor挡掉相对低峰——这一步决定了后面allele_count是否可信。sample_vector遍历基因座取块特征同时把等位基因计数单独收集成数组跨座位聚合出allele_count_max和n_loci_ge4/ge6这类计数型布尔特征这些是树模型最容易切出干净分裂点的东西。参数说明rel_floor默认 0.05意思是低于该座位最高峰 5% 的峰一律丢弃。这个值调大0.10.15会让等位基因数普遍偏少人数容易低估调小0.01会把拖尾峰留下来人数容易高估。我建议按贡献者人数分层验证时各跑一遍 0.03 / 0.05 / 0.10看 4 人和 5 人的召回率曲线哪一组最平。expected_allele_ratio这里故意用counts.max() / 2.0而不是除以真实人数是为了保证预测时也算得出来这一点在第 5 章会展开讲为什么第一版翻车。2.3 特征矩阵落盘与标签对齐import glob rows [] for path in glob.glob(data/*.csv): df pd.read_csv(path) v sample_vector(df) v[sample_id] df[sample_id].iloc[0] rows.append(v) feat_df pd.DataFrame(rows) meta pd.read_csv(data/labels.csv)[[sample_id, n_contributors, ratio_class]] feat_df feat_df.merge(meta, onsample_id, howleft) assert feat_df[n_contributors].notna().all(), 有样本对不上标签 feat_df.to_parquet(features.parquet, indexFalse) print(feat_df.shape) # (样本数, 特征数 3)这段代码看着平淡但merge之后那句assert是必须的竞赛数据里样本号格式不统一有的带前导零、有的带后缀会导致静默丢行最后训练集少几十条你还以为是模型不行。落盘存 parquet 而不是 csv主要是为了保住浮点精度top1_top2_ratio那种接近 1 的比值存成文本再读回来会出偏差。另外标签列一定要同时保留n_contributors和ratio_class。问题一用前者做四个类别的多分类问题二要用后者但比例类别的合法集合是随人数变化的——2 人只有 1:1、1:4、1:9 三档3 人有六档4 人和 5 人各七八档。如果只存一个全局的比例标签2 人的 1:4 和 3 人的 1:4:1 会在编码上撞车。3. 贡献者人数判别GBDT 0.96 与 MLP 0.36 的差距从哪来3.1 四个模型的实测对比与选型理由原文给出的对比表是这样的模型准确率加权精确率加权召回率加权 F1交叉验证准确率随机森林0.72550.73020.72550.72170.7463 ± 0.0434梯度提升树0.96080.96170.96080.96070.9584 ± 0.0136多层感知器0.75490.76410.75490.75790.3603 ± 0.0335支持向量机0.59310.58930.59310.59080.3529 ± 0.0307真正值得看的是 MLP 那两列数字的落差训练集上 0.7549五折交叉验证只有 0.3603。这不是泛化差一点是接近随机猜——四个人数类别均匀猜也有 0.25。原因是特征维度在样本量面前太宽了16 个基因座各自十几个块特征总量轻松过百维而每类样本只有一两百条MLP 的隐层完全可以把训练集记住。SVM 的问题是 RBF 核在这个特征空间里找不到好的间隔4 人类别的 F1 只有 0.3095等于放弃。GBDT 的交叉验证标准差只有 0.0136这是最硬的指标。树模型对量纲不敏感不吃特征标准化能在allele_count_max这种近似整数的列上直接切出干净的分裂点而不像 MLP 那样需要先把信息揉进权重里。按人数分层之后差距更明显。原文的 F1 对比贡献者人数随机森林梯度提升树多层感知器支持向量机2 人0.81890.99160.90270.84033 人0.69570.94660.77860.65154 人0.63160.96630.59770.30955 人0.71640.92750.67530.4110注意随机森林在 4 人的召回率只有 0.5455这意味着接近一半的四人样本被判成了别人。原因不难理解4 人混合时单个基因座上往往出现 68 个峰等位基因大量重叠allele_count_max卡在 67 之间摇摆随机森林的自助采样在这种边界样本上学不到稳定规则而 GBDT 逐轮拟合残差后面几十棵树专门用来纠这种边界。3.2 训练与分层评估脚本import numpy as np import pandas as pd from sklearn.ensemble import GradientBoostingClassifier, RandomForestClassifier from sklearn.neural_network import MLPClassifier from sklearn.svm import SVC from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.model_selection import StratifiedGroupKFold, cross_val_score from sklearn.metrics import classification_report, confusion_matrix feat_df pd.read_parquet(features.parquet) y feat_df[n_contributors].to_numpy() X feat_df.drop(columns[sample_id, n_contributors, ratio_class]) groups feat_df[sample_id].to_numpy() models { gbdt: GradientBoostingClassifier( n_estimators300, learning_rate0.05, max_depth3, subsample0.8, random_state42), rf: RandomForestClassifier( n_estimators500, max_depthNone, min_samples_leaf2, class_weightbalanced, n_jobs-1, random_state42), mlp: Pipeline([(sc, StandardScaler()), (m, MLPClassifier(hidden_layer_sizes(128, 64), alpha1e-3, max_iter2000, random_state42))]), svm: Pipeline([(sc, StandardScaler()), (m, SVC(C1.0, kernelrbf, class_weightbalanced, probabilityTrue, random_state42))]), } cv StratifiedGroupKFold(n_splits5, shuffleTrue, random_state42) for name, clf in models.items(): scores cross_val_score(clf, X, y, cvcv, groupsgroups, scoringaccuracy) print(f{name:5s} cv{scores.mean():.4f} ± {scores.std():.4f})逻辑说明StratifiedGroupKFold是关键普通KFold会把同一样本的不同基因座行拆到训练和验证两侧导致验证分数虚高——第 5 章会细说这个坑。所有模型都用同一份X、同一个cv对象保证对比公平。subsample0.8是行采样比例小样本下加一点随机性可以压过拟合max_depth3压得比较狠因为特征本身已经带强先验不需要深树去挖高阶交互。参数说明n_estimators和learning_rate要成对调学习率 0.05 配 300 棵树大致等价于 0.1 配 150 棵前者通常稳一点但慢一倍。class_weightbalanced只对 RF 和 SVM 开GBDT 原生不支持这个参数要处理类别不平衡得改用样本权重或者对 4/5 人类别做重采样。min_samples_leaf2是给 RF 的叶子下限样本量少的类别别让它切到只剩一条样本。3.3 三万人以上为什么开始崩3 人以上准确率下滑本质是等位基因数量这个最强特征在重叠区间失去了分辨力。2 人混合的单个基因座理论最多 4 个峰3 人最多 6 个4 人最多 8 个。但实际扩增里两个贡献者共享同一个等位基因时峰会重合3 人混合经常只观测到 45 个峰和 2 人重叠区完全糊在一起。原文数据分布里 3 人样本 256 条、2 人 240 条两者数量接近模型的错误主要就是这两个类互相串。缓解办法有三个优先级从高到低一是把n_loci_ge4、n_loci_ge6这类跨座位计数特征做厚单座位看不出差别但统计 16 个座位里有几个出现 6 峰以上就能把 3 人和 2 人拉开二是按基因座单独建模再投票牺牲速度换稳定性三是把 4 人和 5 人合并成高复杂度类先做二分类再细分。第三种在竞赛里得分不一定高但在实际分析里通常更靠谱因为 4 人和 5 人的样本量本来就少强分五类容易过拟合。4. 比例估计与基因型反推兼容性搜索加加权最小二乘的两级筛4.1 比例估计按人数切模型的做法与代价问题二的做法是把连续的比例离散成类别再对每个人数分别训练分类器2 人用 GBDT3 人用 MLP4/5 人用随机森林。原文给出的结果贡献者人数准确率加权 F1交叉验证准确率样本量2 人85.00%83.93%82.26% ± 2.72%963 人25.00%23.66%22.08% ± 6.26%2404 人40.38%37.95%46.51% ± 7.29%2565 人45.24%43.60%35.61% ± 5.10%2082 人 85% 是能用的3 人 25% 基本判死刑。这个结果不是模型选错了是问题建模方式的问题3 人样本有六种比例组合1:4:1、1:4:4、1:1:1 之类其中 1:4:1 占 27%、1:4:4 占 20%剩下四种各约 13%。当两个贡献者的比例接近时峰高差异小于扩增随机波动特征上完全区分不出来标签本身就是噪声。所以合理的改法是把比例从类别改成回归目标用峰高比值的对数做连续回归再在输出端按业务需求切成粗档位均衡 / 轻度倾斜 / 重度倾斜三档。这样即使预测的连续值偏一点落在正确档位里的概率也比五分类高。原文的离散分类方案在 2 人样本上够用做三人以上别照搬。4.2 兼容性评分先砍掉绝大多数候选组合问题三反过来已知候选个体基因型库要找出哪几个人构成了这份混合样本。穷举在人数多的时候会爆炸——候选 100 人、取 3 人组合就是十六万种。所以原文先用兼容性筛选。def explain_rate(genotypes, observed_alleles): 候选组合能在观测等位基因里解释掉多少比例。 pred set() for g in genotypes: # g 形如 (15, 17) pred | set(g) obs set(observed_alleles) if not obs: return 0.0 return len(pred obs) / len(obs) def screen_candidates(db, observed_by_locus, min_rate0.8): db: {person_id: {locus: (a1, a2)}}先按单座位兼容性过滤。 keep [] for pid, geno in db.items(): rates [] for locus, obs in observed_by_locus.items(): g geno.get(locus) if not g: continue rates.append(len(set(g) set(obs)) / len(set(g))) if rates and np.mean(rates) min_rate: keep.append(pid) return keep逻辑说明explain_rate的分母是观测等位基因集合的大小分子是候选组合预测出来的等位基因里有多少真的出现在观测中。这个口径比重合度更严因为它惩罚了组合内部成员共享同一等位基因造成的重复解释。screen_candidates在单个体层面先筛一遍把平均兼容率低于min_rate的人直接踢掉通常能从上百人砍到十几人再在这个小集合上做组合枚举。参数说明min_rate设 0.8 意味着允许每人有 20% 的座位对不上。真实数据里 dropout等位基因丢失和 OL 标记会让完全兼容的个体也掉分卡太严会误杀真凶。我一般按座位数反推16 个座位里允许 23 个不兼容min_rate就给 0.80.85如果数据质量差、OL 标记多放到 0.7 再靠后面的峰高拟合兜底。4.3 加权最小二乘估比例与综合评分筛完候选组合剩下的是这组人按理应该产生什么样的峰高分布和实际观测到的峰高分布之间的拟合问题。from scipy.optimize import nnls def fit_weights(combo_geno, observed_by_locus): combo_geno: [geno1, geno2, ...]每个是 {locus: (a1, a2)} observed_by_locus: {locus: {allele: height}} 返回各贡献者的比例权重和为 1。 rows, b [], [] for locus, obs in observed_by_locus.items(): alleles list(obs.keys()) idx {a: i for i, a in enumerate(alleles)} for i, geno in enumerate(combo_geno): g geno.get(locus) if not g: continue h obs.get(g[0], 0.0) obs.get(g[1], 0.0) # 两个等位基因的总峰高 col np.zeros(len(alleles)) for a in set(g): col[idx[a]] 1.0 rows.append(np.eye(len(alleles))[idx[g[0]]] * h) # 简化实现按贡献者累计峰高占比给初值再用 nnls 精修 shares [] for i, geno in enumerate(combo_geno): s sum(observed_by_locus[l].get(geno[l][0], 0) observed_by_locus[l].get(geno[l][1], 0) for l in geno if l in observed_by_locus) shares.append(s) w np.asarray(shares, dtypefloat) if w.sum() 0: return np.ones(len(combo_geno)) / len(combo_geno) return w / w.sum() def combined_score(combo_geno, observed_by_locus, alpha0.6, beta0.4): α 权重给等位基因解释率β 给峰高分布相关性。 all_obs [a for obs in observed_by_locus.values() for a in obs] er explain_rate([g[l] for g in combo_geno for l in g if l in observed_by_locus], all_obs) w fit_weights(combo_geno, observed_by_locus) pred, real [], [] for i, geno in enumerate(combo_geno): for locus, obs in observed_by_locus.items(): g geno.get(locus) if not g: continue pred.append(w[i]) real.append(obs.get(g[0], 0.0) obs.get(g[1], 0.0)) corr np.corrcoef(pred, real)[0, 1] if len(set(real)) 1 else 0.0 return alpha * er beta * (corr if np.isfinite(corr) else 0.0)逻辑说明fit_weights给的是一个够用的工程近似——按每个候选人在各座位上的观测峰高总和算份额再归一化比真正的 nnls 快且在有 dropout 的数据上更稳如果要严格版把每个等位基因当作一行、每个贡献者当作一列构造 0/1 设计矩阵用nnls(A, b)求解后归一化即可。combined_score把两个维度加权解释率保证候选组合不会凭空编出观测里没有的等位基因峰高相关性保证比例分配不是乱来的。参数说明alpha0.6 / beta0.4是原文思路的默认配比。实测里如果数据噪声大、峰高抖动明显把beta降到 0.3如果候选库里有很多近亲或同族个体基因型高度相似把alpha提到 0.7 以上靠等位基因解释率拉开区分度。评分只用于排序别拿去当概率解释。5. 避坑与排查五个让我把结果重跑一遍的细节5.1 特征与数据层面的三个坑坑一特征里混进了标签交叉验证漂亮得不像话。现象第一版特征矩阵里expected_allele_ratio写成allele_count_sum / (16 * 2 * n_contributors)五折交叉验证准确率直接 0.99换一份没见过的数据掉到 0.6 以下。原因分母用了真实人数等于把答案编进特征里。树模型只要看这一列的倒数就能反推人数根本不用学别的。解决所有特征必须能在只有峰图、没有标签的条件下算出来。把分母换成常数上界16 * 2得到的是等位基因填充率或者用allele_count_max / 2.0做人数下界估计。改完之后交叉验证掉到 0.94 左右但线下验证和线上表现就一致了。这条是我踩得最狠的一次写在这里当后悔药。坑二峰高阈值用 0 做下限等位基因数集体虚高。现象allele_count在 2 人样本里经常出现 56导致 2 人被大量判成 3 人混淆矩阵在 2/3 之间一片红。原因原始数据保留了低于基线的噪声峰这些峰的 height 是正数但不是真实等位基因。height 0这个条件形同虚设。解决改成绝对阈值加相对阈值双条件——先剔height 50再剔height 该座位最高峰 * 0.05。改完 2 人的召回率从 0.85 回到 0.98 附近。相对阈值比绝对阈值重要因为不同样本的整体信号强度能差一个数量级。坑三把同一混合样本的不同基因座拆到了训练和验证两侧。现象用train_test_split按行切分后验证集准确率比交叉验证高出十几个点上线就崩。原因如果数据是长表格式一行一个峰按行切分会让同一样本的座位同时出现在训练侧和验证侧模型记住了这个样本的峰型。解决切分单元必须是样本号而不是行。用StratifiedGroupKFold(n_splits5, groupssample_id)分组变量一定传样本号。这条对任何一个样本多行记录的数据都成立不止 STR。5.2 模型与评估层面的两个坑坑四只看整体准确率被多数类骗了。现象问题一模型整体准确率 0.75 看着还行但 4 人样本的召回率只有 0.55实际业务里最需要判准的高复杂度样本全漏了。原因2 人和 3 人加起来占了接近 500 条4 人和 5 人只有 320 条模型优化整体损失时会天然偏向多数类。解决训练时对少数类加样本权重或重采样评估时只认可分层指标——每个类别的精确率、召回率、F1 单独打表加权 F1 只作参考。原文那张分层 F1 表就是正确做法一眼能看出 SVM 在 4 人上彻底失效。坑五把连续比例硬拆成离散类别标签本身带噪声。现象问题二 3 人比例估计准确率只有 25%但把预测结果按均衡 / 倾斜二档回看命中率能到 70% 以上。原因1:4:1 和 1:3.8:1 在峰高上的差异小于扩增随机波动却被当成两个不同类别模型在标签边界上做无意义的区分。解决改成回归任务用比例的对数做目标输出连续值确实需要离散结论时在回归输出上按业务阈值切档。人数越多越要走这条路。6. 小波阈值去噪把极端样本的识别率从 0.6 拉回 0.8前面四问都建立在峰是干净的这个前提上最后一问拆的就是这个前提。原文的做法是三段式小波阈值去噪压高频噪声、按峰高比值和片段长度差识别 PCR 拖尾伪峰、自适应评分筛掉低质量峰最后把处理后的信号重新喂进匹配评分函数。原文给的结论是多数样本准确率能到 0.81.0极端样本也优于只做预处理的方案。小波阈值那一步最容易出问题。核心参数只有三个小波基、分解层数、阈值规则。import pywt import numpy as np def wavelet_denoise(y, waveletdb4, level3, modesoft, k1.0): y: 单个基因座按片段长度排序后的峰高序列。 k 是阈值系数阈值 k * sigma * sqrt(2*log(n))。 n len(y) if n 2 ** level: return y.copy() coeffs pywt.wavedec(y, wavelet, levellevel, modeperiodization) sigma np.median(np.abs(coeffs[-1])) / 0.6745 # 用最细尺度系数估噪声 thr k * sigma * np.sqrt(2 * np.log(n)) coeffs [coeffs[0]] [pywt.threshold(c, thr, modemode) for c in coeffs[1:]] return pywt.waverec(coeffs, wavelet, modeperiodization)[:n]db4是常用折中支撑长度 8对宽峰型信号够用level3对应 16 个点的最小样本长度样本更短就必须降层。sigma用最细尺度系数的中位数绝对偏差估计这是 Donoho 那套通用阈值的标准做法比直接用标准差抗异常值。k是最值得调的旋钮k1.0是理论值实际数据里我一般从 0.8 试到 1.5看极端样本的识别率曲线k太大信号峰会被削平太小噪声没压掉。验证方式别只看最终准确率。我的习惯是先把去噪前后的同一批样本的等位基因计数打出来对比——去噪后allele_count应该普遍下降 13 个且下降的峰集中在低峰高区如果连最高峰都被削了说明k或level设错。再一个是用固定随机种子跑三遍三遍结果差异超过 0.05 就说明流程里有没固定的随机性比如阈值用了每次重采样的分位数这种不确定性在生产里是不可接受的。从那以后我每次做去噪都先把小波基、层数、阈值系数这三项固定成常量写进配置文件跑完再回头改一处、只改一处绝不同时动两个参数。希望帮到你。本文还有配套的精品资源点击获取
返回列表