使用 Sktime 的签名方法#
“签名方法”是指一系列用于多模态序列数据的特征提取技术,源自受控微分方程理论。近年来,人们对签名方法提出了大量的改进,以期在某些方面进行优化。
在论文“时间序列的广义签名方法”[1]中,作者将这些改进中的绝大多数整合到了一篇文档中,并对多元 UEA 数据集进行了大规模超参数研究,以构建一个通用的签名算法,该算法有望在各种数据集上表现良好。我们在SignatureClassifier
模块中实现了该研究的最佳实践结果,将其作为超参数的默认起始值。
路径签名#
签名方法的核心是所谓的“签名变换”。
一个在\(\textit{d}\)维空间中有限长度的路径\(X\)可以通过映射\(X:[a, b]\rightarrow\mathbb{R}\) \(\!\!^d\)来描述,或者用坐标表示为\(X=(X^1_t, X^2_t, ...,X^d_t)\),其中每个坐标\(X^i_t\)是实数值,由\(t\in[a,b]\)参数化。
- 路径\(X\)的签名变换\(S\)被定义为一个无限序列的值: :nbsphinx-math:`begin{equation}
S(X)_{a, b} = (1, S(X)_{a, b}^1, S(X)_{a, b}^2, …, S(X)_{a, b}^d, S(X)_{a,b}^{1, 1}, S(X)_{a,b}^{1, 2}, …), label{eq:path_signature}
- end{equation}`其中每一项都是\(X\)的\(k\)重迭代积分,带有多重索引\(i_1,...,i_k\): :nbsphinx-math:`begin{equation}
S(X)_{a, b}^{i_1,…,i_k} = int_{a<t_k<b}…int_{a<t_1<t_2} mathrm{d}X_{t_1}^{i_1}…mathrm{d}X_{t_k}^{i_k}. label{eq:sig_moments}
end{equation}` 这定义了一个与路径相关联的有序数字序列,已知该序列可以表征路径,直至广义形式的重新参数化 [2]。可以将签名看作是一组几乎唯一确定路径的汇总统计量。此外,路径\(X\)上的任何连续函数都可以通过其签名的线性函数任意好地逼近 [3];签名揭示了无参数化路径空间上函数的非线性性质。
可视化#
为了了解签名项在物理上代表什么,我们考虑一名在 ICU 中被追踪其收缩压 (SBP) 和心率 (HR) 随时间变化的患者。这可以表示为\(\mathbb{R}^3\)中的一条路径(假设时间被包含为一个通道)。
上图草绘了这样一条路径可能出现的两种情景。这里我们假设每幅图都包含一个隐式的时间维度,使得路径沿着蓝色线从左到右遍历。
深度 1:#
深度为 1 的签名项就是每个变量在时间间隔内的变化量,图中即为\(\Delta \text{HR}\)和\(\Delta \text{SBP}\)项。注意,这些值在每种情况下都是相同的。
深度 2:#
第二层给了我们带符号的面积(阴影的橙色区域),其中最左边图的方向产生了负号的面积,而第二个则给出了正值,因此,在签名的二阶时,我们现在有了足够的信息来区分这两种情况:第一种是心率先于(或者至少最初快于)血压上升,反之亦然。
深度 > 2:#
深度大于 2 的项在图形上更难可视化,但其思想与深度 2 的情况类似,我们在深度 2 中看到签名提供了关于 HR 或 SBP 增加哪个先发生的信息,并对其发生程度进行了数值量化。在更高阶时,签名也在做类似的事情,但现在是涉及三个事件,而不是两个。签名提取出关于事件发生顺序的结构信息。
时序分析中的签名#
签名是应用于时序分析相关问题的自然工具。如上所述,它可以将多维时序数据转换为静态特征,这些特征代表了时序序列性质的信息,可以输入到标准的机器学习模型中。
- 一个关于其工作原理的简化视图如下: :nbsphinx-math:`begin{equation}
text{模型}(text{签名}(text{序列数据}))) = text{预测}
end{equation}`
考虑的签名变体#
再次,遵循 [1] 中的工作,我们将签名方法的变体概念上分为
增广 (Augmentations) - 将输入序列或时序变换为一个或多个新序列,以便签名可以返回关于路径的不同信息。
窗口 (Windows) - 窗口操作,以便签名可以在局部范围内作用。
变换 (Transforms) - 选择签名变换或对数签名变换。
重标度 (Rescalings) - 签名重标度的方法。
这可以很好地用下面的图表示,其中\(\phi\)表示增广,\(W^{i, j}\)表示窗口操作,\(S^N\)表示签名,\(\rho_{\text{pre}/\text{post}}\)表示重标度方法。
请参阅完整论文,以更全面地了解这些分组的含义。
Sktime 模块#
现在我们将介绍 sktime 接口中包含的分类和转换模块,并提供一个示例,展示如何执行高效的超参数优化,这在 [1] 中被证明能给出良好结果。
[1]:
# Some additional imports we will use
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sktime.datasets import load_gunpoint
[2]:
# Load an example dataset
train_x, train_y = load_gunpoint(split="train", return_X_y=True)
test_x, test_y = load_gunpoint(split="test", return_X_y=True)
D:\CMP Machine Learning\sktime-workshop-boss\sktime\utils\data_io.py:63: FutureWarning: This function has moved to datasets/_data_io, this version will be removed in V0.10
warn(
概述#
我们提供以下模块:
sktime.transformers.panel.signature_based.SignatureTransformer - 一个 sklearn 转换器,提供应用签名方法的功能,并可选择上述一些变体。
sktime.classification.feature_based.SignatureClassifier - 提供一个简单的接口,用于将分类器附加到 SignatureTransformer 类。
[3]:
from sktime.classification.feature_based import SignatureClassifier
from sktime.transformations.panel.signature_based import SignatureTransformer
示例 1:序列数据 -> 签名特征。#
这里我们将给出一个非常简单的示例,将形状为 [num_batch, series_length, num_features] 的序列 3D GunPoint 数据转换为形状为 [num_batch, signature_features] 的数据。
[4]:
# First build a very simple signature transform module
signature_transform = SignatureTransformer(
augmentation_list=("addtime",),
window_name="global",
window_depth=None,
window_length=None,
window_step=None,
rescaling=None,
sig_tfm="signature",
depth=3,
)
# The simply transform the stream data
print(f"Raw data shape is: {train_x.shape}")
train_signature_x = signature_transform.fit_transform(train_x)
print(f"Signature shape is: {train_signature_x.shape}")
Raw data shape is: (50, 1)
Signature shape is: (50, 14)
然后构建一个时序分类模型变得极其容易。例如:
[5]:
# Train
model = RandomForestClassifier()
model.fit(train_signature_x, train_y)
# Evaluate
test_signature_x = signature_transform.transform(test_x)
test_pred = model.predict(test_signature_x)
print(f"Accuracy: {accuracy_score(test_y, test_pred):.3f}%")
Accuracy: 0.740%
示例 2:通用模型的微调#
如前所述,在 [1] 中,作者对完整的 UEA 存档上的签名变体进行了大规模超参数搜索,以开发一种构建模型的“最佳实践”方法。这需要对以下参数进行微调,因为它们被发现与数据集高度相关:
depth
在 [1, 2, 3, 4, 5, 6] 范围内window_depth
在 [2, 3, 4] 范围内RandomForestClassifier
超参数。
这里我们展示如何使用 sktime 框架轻松完成这项工作。
[6]:
from sklearn.model_selection import RandomizedSearchCV, StratifiedKFold
# Some params
n_cv_splits = 5
n_gs_iter = 20
# Random forests found to perform very well in general
estimator = RandomForestClassifier()
# The grid to be passed to an sklearn gridsearch
signature_grid = {
# Signature params
"depth": [1, 2, 3, 4, 5],
"window_name": ["dyadic"],
"augmentation_list": [["basepoint", "addtime"]],
"window_depth": [1, 2, 3, 4],
"rescaling": ["post"],
# Classifier and classifier params
"estimator": [estimator],
"estimator__n_estimators": [50, 100, 500],
"estimator__max_depth": [2, 4, 6, 8, 12, 16, 24, 32, 45, 60],
}
# Initialise the estimator
estimator = SignatureClassifier()
# Run a random grid search and return the gs object
cv = StratifiedKFold(n_splits=n_cv_splits)
gs = RandomizedSearchCV(estimator, signature_grid, cv=n_cv_splits, n_iter=n_gs_iter)
gs.fit(train_x, train_y)
# Get the best classifier
best_classifier = gs.best_estimator_
# Evaluate
train_preds = best_classifier.predict(train_x)
test_preds = best_classifier.predict(test_x)
train_score = accuracy_score(train_y, train_preds)
test_score = accuracy_score(test_y, test_preds)
print(f"Train acc: {train_score * 100:.3f}% | Test acc: {test_score * 100:.3f}%")
Train acc: 100.000% | Test acc: 96.000%
参数的完整描述#
最后,我们将对SignatureClassifier
模块中的每个参数及其可能取值进行进一步解释。
参数#
下面我们列出每个参数及其可能取值。关于选项含义的更多细节,请参阅 [1]。
classifier 必须是任何 sklearn 估计器。默认为RandomForestClassifier()
。
augmentation_list: 字符串元组列表,在应用签名变换之前应用的增广列表。可以是以下任意组合:
'addtime' - 添加一个等间隔的时间通道。
'leadlag' - 应用 leadlag 变换。
'ir' - 执行 invisibility reset 变换。
'cumsum' - 执行累积求和变换。
'basepoint' - 在路径开头附加零以消除平移不变性。
window_name str,要应用的窗口变换名称。可以是以下任意一种:
'global' - 对所有数据应用单一窗口。
'expanding' - 从第一个数据点开始,扩展覆盖数据的多个窗口(宽度增加)。
'sliding' - 沿数据滑动(固定宽度)的多个窗口。
'dyadic' - 将数据划分成二进位窗口。
window_depth: int,二进位窗口的深度。(仅当window_name == 'dyadic']
时有效)。
window_length: int,滑动/扩展窗口的长度。(仅当window_name in ['sliding, 'expanding'].
时有效)。
window_step: int,滑动/扩展窗口的步长。(仅当window_name in ['sliding, 'expanding'].
时有效)。
rescaling: str,签名重标度的方法。可以是以下任意一种:
'pre' - 在签名变换之前对路径进行重标度。
'post' - 在签名变换之后对路径进行重标度。
None - 不进行重标度。
sig_tfm: str,指定签名变换类型的字符串。可以是以下之一:['signature', 'logsignature']。
depth: int,签名截断深度。
random_state: int,随机状态初始化。
参考文献#
[1] Morrill, James, Adeline Fermanian, Patrick Kidger, and Terry Lyons. “A Generalised Signature Method for Time Series.” arXiv preprint arXiv:2006.00873 (2020).
[2] Hambly, B., Lyons, T.: Uniqueness for the signature of a path of bounded variation and the reduced pathgroup. Annals of Mathematics171(1), 109–167 (2010). doi:10.4007/annals.2010.171.10913.
[3] Lyons, T.J.: Differential equations driven by rough signals. Revista Matem ́atica Iberoamericana14(2), 215–310(1998)