无预定义段数的单变量时间序列无监督分段方法求助
问题描述
我有单变量时间序列数据,需要自动识别出明显的低分值分段(示例数据中存在一个长时段的低分分段),且预先不知道潜在状态的数量。
已尝试的方法:
- 使用hmmlearn的Gaussian HMM:拟合后的模型经常快速跳离低状态,导致连续低分分段的识别结果不稳定(表现为低状态被频繁打断,无法覆盖完整的低分时段)
- 临时解决方案:设置较大的
n_components值(如10),拟合HMM后合并所有均值小于0.3的状态。该方法能得到正确结果,但缺乏严谨性
我了解过Sticky HMM,但不确定哪种方法最优(非HMM路线是否更合适)。需要一种严谨、可复现的方法,优先尝试简单方案(复杂方法缺乏维护完善的教程和代码示例),希望能提供针对该示例的可运行代码片段。
示例信号生成代码
import matplotlib.pyplot as plt import numpy as np x = np.arange(5000) # 生成模拟信号 y = 0.12 + 0.02*np.random.randn(5000) # 基线噪声 y += 0.6*np.exp(-((x-400)**2)/(2*180**2)) # 左侧大峰值 y += 0.20*np.exp(-((x-1150)**2)/(2*90**2)) # 中部小凸起 y += 0.03*np.sin(x/35) # 小幅波动 y += 0.06*np.sin(x/7) * ((x>3400) & (x<4200)) # 振荡区域 y += 0.75*np.exp(-((x-4700)**2)/(2*120**2)) # 右侧大峰值 # 绘制原始信号 plt.figure(figsize=(14,4)) plt.plot(x, y, linewidth=1.3) plt.xlim(0, 5000) plt.ylim(0, 0.85) plt.tight_layout() plt.show()
解决方案
方案一:统计阈值+滑动窗口(简单、严谨)
核心思路:基于数据的统计特征设定低分阈值,再用滑动窗口过滤孤立噪声点,保留连续的低分分段,无需假设潜在状态数量。
代码实现
import pandas as pd # 1. 设定低分阈值:用基线区域的均值+2倍标准差适配噪声 baseline_mask = (y < 0.2) # 粗略筛选基线区域 baseline_mean = y[baseline_mask].mean() baseline_std = y[baseline_mask].std() low_threshold = baseline_mean + 2 * baseline_std # 阈值可根据需求调整 # 2. 标记初始低分点 is_low = y < low_threshold # 3. 滑动窗口过滤:仅保留连续N个点以上的低分区域 window_size = 50 # 可根据信号时间尺度调整 is_low_series = pd.Series(is_low) # 计算窗口内低分点的占比 rolling_ratio = is_low_series.rolling(window=window_size, center=True).mean() # 保留占比>0.8的窗口区域(认为是连续低分) filtered_low = (rolling_ratio > 0.8).fillna(False) # 4. 可视化结果 plt.figure(figsize=(14,4)) plt.plot(x, y, linewidth=1.3, label='原始信号') plt.fill_between(x, 0, 0.85, where=filtered_low, color='red', alpha=0.2, label='识别的低分分段') plt.xlim(0,5000) plt.ylim(0,0.85) plt.legend() plt.tight_layout() plt.show()
优势:
- 无需复杂模型,完全基于数据统计特征,可复现性强
- 手动调整参数少,阈值和窗口大小可根据业务需求或数据分布量化确定
- 能有效过滤噪声导致的孤立低分点,保留连续的目标分段
方案二:改进的HMM模型(解决状态跳转问题)
针对普通Gaussian HMM容易跳离低状态的问题,通过约束转移矩阵增强状态的持续性,实现类似Sticky HMM的效果,同时可自动确定最优状态数量。
代码实现
from hmmlearn import hmm from sklearn.model_selection import GridSearchCV # 1. 准备数据:hmmlearn要求输入为2D数组 X = y.reshape(-1, 1) # 2. 网格搜索最优状态数量(解决预先不知道状态数的问题) param_grid = {'n_components': [2,3,4,5]} model = hmm.GaussianHMM(covariance_type='diag', random_state=42) grid_search = GridSearchCV(model, param_grid, cv=3, scoring='neg_log_likelihood') grid_search.fit(X) best_n = grid_search.best_params_['n_components'] # 3. 构建带自转移约束的HMM model = hmm.GaussianHMM(n_components=best_n, covariance_type='diag', random_state=42) # 设置转移矩阵:增加每个状态的自转移概率(Sticky效果) trans_matrix = np.eye(best_n) * 0.95 # 自转移概率0.95 # 剩余概率均匀分配给其他状态 trans_matrix += (1 - 0.95)/(best_n - 1) * (1 - np.eye(best_n)) model.transmat_ = trans_matrix # 拟合模型 model.fit(X) # 4. 预测状态并合并低均值状态 states = model.predict(X) state_means = model.means_.flatten() low_state_ids = np.where(state_means < low_threshold)[0] # 用方案一的阈值筛选低状态 is_low_hmm = np.isin(states, low_state_ids) # 5. 可视化结果 plt.figure(figsize=(14,4)) plt.plot(x, y, linewidth=1.3, label='原始信号') plt.fill_between(x, 0, 0.85, where=is_low_hmm, color='blue', alpha=0.2, label='HMM识别的低分分段') plt.xlim(0,5000) plt.ylim(0,0.85) plt.legend() plt.tight_layout() plt.show()
优势:
- 通过网格搜索自动确定最优状态数量,解决“预先不知道状态数”的问题
- 约束转移矩阵增强状态持续性,避免普通HMM的频繁跳转问题
- 结合统计阈值合并低均值状态,识别结果更稳定
内容的提问来源于stack exchange,提问作者youtube
相关产品推荐
相关产品推荐

