【時系列】TimesNet:一般的な2Dモデリング時系列モデル

论文: ICLR2023 | Timesnet: 一般的な時系列分析のための時間 2 次元変動モデリング [1]

著者: Wu、Haixu、Tengge Hu、Yong Liu、Hang Zhou、Jianmin Wang、Mingsheng Long

機関:清華大学

コード: https://github.com/thuml/TimesNet

引用数: 29

c9fe0b2ff58b43a885a622dcaa9ea47c.png

この論文は、時系列の多重周期性を出発点として、複雑な時間変化を複数のサイクル内および複数のサイクル間の変化に分解し、1 次元の時系列を複数のサイクルに基づいた 2 次元のテンソルのセットに変換し、2 つの-次元畳み込み 複雑な時間的変化を抽出するカーネル モデリングにより、5 つの主流の時系列分析タスク (短期および長期の予測、代入、分類、異常検出) における TimesNet のパフォーマンスが大幅に向上します。

b7eb161e088befd95adeddd8c4166723.png

レーダー チャートのソースは、さまざまなタスクにおけるさまざまなモデルのスコア ランキングです (付録を自分で参照してください)。

Q: データの複数の周期性を見つけるにはどうすればよいですか?

A: 著者は FFT を使用して元のデータをスペクトルに変換し、長さ 96 のウィンドウ シーケンスごとに上位 6 つの明白な周波数を選択します。次に、対応する周期の長さを収集し、その正規化された密度マップを描画すると、次の図に示すように、多重周期性を確認できます。電力と同様に、データセットには長さ 12 と 24 の期間が含まれています。

1f2907aa14ffb7d2d77143f0854a7129.png

モデルがデータから複数期間の変化をより適切に捕捉できるようにするために、作成者は複数期間の時系列を抽出し、次のようにデータを整理します。

43fec8e9a07fcdef2996138989842d24.png

このうち、赤がサイクル内の変化(チェーンレシオの概念と同様、昨日と今日の変化)、青がサイクル間の変化(前年比の概念と同様、昨日と今日の変化)です。先週の月曜日と今週の月曜日)。このように、2D データに対して畳み込み演算を実行し、その結果を複数のサイクルで融合すると、複数のサイクルのタイミング変化を捉えることができるのではないでしょうか? 以下に示すように:

7ac60c87fd023f8553ee8267f4fddcf9.png

この目的のために、著者は TimesNet を提案しました。その中で最も重要なものはTimesBlockモジュールです。このモジュールは次のことを行います。

- 1D 時系列を 2D 構造データに変換します。

- 2D コンボリューション カーネルが情報をキャプチャします。

- 複数のサイクルを動的にマージします。

Q: まず、1D 時系列を 2D 構造データに変換するにはどうすればよいですか?

A: まず FFT を使用して周波数領域を取得し、次に各周波数の振幅を計算します。A_j は周波数 j の振幅を表します。振幅が最も大きい topk 周波数を選択し、最後に T/j で対応する周波数の周期長を取得します。

 223b5c171d11259f7c5bf98e396db909.png

コードは以下のように表示されます。

def FFT_for_Period(x, k=2):
    # [B, T, C]
    # [32,192,16] -> [32,97,16]
    # 使用快速傅里叶变换,得到T/2+1个频率
    xf = torch.fft.rfft(x, dim=1)
    # find period by amplitudes
    # 在样本维度上求均值,得到所有样本的平均振幅
    # 在通道维度上求均值,得到所有特征的平均振幅
    # 得到的频率列表维度为 [T/2+1]
    frequency_list = abs(xf).mean(0).mean(-1)
    # 频率列表首位元素为直流分量,值较大,为避免影响后续topk选取,置为0
    # ref: https://github.com/thuml/Time-Series-Library/issues/7
    frequency_list[0] = 0
    # 从频率列表中选择振幅最高的k个元素 [k]
    # 返回两个张量,第一个是未使用的排序结果,第二个是topk的索引
    _, top_list = torch.topk(frequency_list, k)
    # 计算实际周期,即时间步数除以top_list中每个频率对应的索引值(周期长度)
    # 得到的结果维度为[32, k]
    top_list = top_list.detach().cpu().numpy()
    period = x.shape[1] // top_list # [k]
    # 返回实际周期和振幅
    # 振幅通过在最后一维上求均值得到每个频率的平均振幅 [B, k]
    return period, abs(xf).mean(-1)[:, top_list]

その後、データがインターセプトされ、さまざまなサイクル長に従って 2D にスタックされます。入力シーケンスの時間長は指定されたサイクル長で割り切れない可能性があるため、1D から 2D への変換が正常に完了できるようにするには、値 0 を埋める必要があります。

6badd06644a0b0439f8336a34e695d0a.png

92e2b48d26969df2ec868e3280c5feb8.png

コードは以下のように表示されます。

def forward(self, x):
        B, T, N = x.size()
        # period_list: 各top振幅频率j的周期长度,维度[k]
        # period_weight: 各样本下,各top振幅频率j的平均振幅,维度[B, k]
        period_list, period_weight = FFT_for_Period(x, self.k)


        res = []
        for i in range(self.k):
            # 获取第i个频率对应的周期长度
            period = period_list[i]
            # padding
            # 若周期过大,超过数据范围则需要padding
            # 为什么数据范围要考虑pred_len?
            # 因为对于预测任务来说,TimesNet的pipeline是:
            # 在embedding之后先将序列长度扩充为self.seq_len + self.pred_len,然后再不
            # 断refine预测结果。所以在中间层的TimesBlock其实在处理预测的中间结果(其长度
            # 为self.seq_len + self.pred_len)。
            if (self.seq_len + self.pred_len) % period != 0:
                # 计算调整后的序列长度,使其能够整除周期长度
                length = (
                                 ((self.seq_len + self.pred_len) // period) + 1) * period
                # 创建一个0填充张量,形状为 [B, 填充长度, N]
                padding = torch.zeros([x.shape[0], (length - (self.seq_len + self.pred_len)), x.shape[2]]).to(x.device)
                # 合并
                out = torch.cat([x, padding], dim=1)
            else:
                length = (self.seq_len + self.pred_len)
                out = x
            # reshape
            # 将输入张量进行形状变换和维度置换
            # 将长度为 length 的序列划分为 length//period 个长度为 period 的子序列
            # 将通道数特征放在第 2 维度上,将子序列放在第 3 维度上
            # 得到的结果维度为 [B, N, length//period, period]
            out = out.reshape(B, length // period, period,
                              N).permute(0, 3, 1, 2).contiguous()
            # 2D conv: from 1d Variation to 2d Variation
            out = self.conv(out)
            # reshape back
            out = out.permute(0, 2, 3, 1).reshape(B, -1, N)
            res.append(out[:, :(self.seq_len + self.pred_len), :]) # 保留前seq_len+pred_len长度的T,后面padding部分丢弃
        res = torch.stack(res, dim=-1)
        # adaptive aggregation
        # 基于每个A,softmax算权重
        period_weight = F.softmax(period_weight, dim=1)
        period_weight = period_weight.unsqueeze(
            1).unsqueeze(1).repeat(1, T, N, 1)
        # 加权融合   
        res = torch.sum(res * period_weight, -1)
        # residual connection
        res = res + x
        return res

上記のコードを読むと、TimesBlock が 2D データに対して「2D コンボリューション カーネル情報のキャプチャ」と「複数のサイクルの動的マージ」をどのように行うかがわかります。簡単に言えば、著者は情報をキャプチャするために Inception を 2D コンボリューションに使用しました。次の図に示すように、他の CV バックボーン ネットワークに置き換えることもできます。

15433a0abc7726ee2ea28536cd7b4c17.png

# parameter-efficient design
self.conv = nn.Sequential(
    Inception_Block_V1(configs.d_model, configs.d_ff,
                       num_kernels=configs.num_kernels),
    nn.GELU(),
    Inception_Block_V1(configs.d_ff, configs.d_model,
                       num_kernels=configs.num_kernels)
)

本当に何かを学ぶことができるので、次のことに集中する必要があります。

c7b55159ed07701610698dbb7221cc81.png

マルチ期間の動的マージは次のとおりです。ソフトマックス重みの周波数振幅計算に基づいて、重み付き合計によってマルチ期間の畳み込み結果がマージされます。著者は、直接加算や直接振幅加重加算などの他の結合方法も試しましたが、その効果は振幅 + ソフトマックス + 加重加算ほど良くありません。

ce493fe925898845101379345f9a9a6e.png

上記は TimesBlock の完全なコンテンツであり、TimesNet は残差によって接続された一連の TimesBlock です。以下の図に示すように、ここでは繰り返しません。

a585ebeeffe7251690995e77f193b28e.png

データ次元変換プロセスの式は次のとおりです。

著者は TimesNet を自社開発の Time-Series-Library ライブラリに組み込みました。コードは長くなく、アイデアは要点をまっすぐに示しています。興味のある友達は自分でコードを読んで理解を深めてください。データが正式に TimesNet に入力されると、データの前処理が行われます。

- 標準化については、以前の非定常変圧器を参照してください。

- 埋め込みを実行します: トークン埋め込み + 位置埋め込み + 時間埋め込み。次に、リニア層を再度フィードします。埋め込み出力は、時間次元では seq_len+pred_len であることに注意してください。その理由は、著者が Github [2] で次のように返信しているためです。「予測タスクの場合、TimesNet のパイプラインは、埋め込み後、最初にシーケンスの長さを self.seq_len + self.pred_len に拡張し、その後予測結果を改良し続けます。したがって、中間層では、TimesBlock が実際に予測の中間結果を処理します (その長さは self.seq_len + self.pred_len)。

def forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):
        # encoder输入 x_enc: (batch_size, seq_len, enc_in)
        # encoder时间戳特征 x_mark_enc: (batch_size, seq_len, ts_fnum)
        # decoder输入 x_dec: (batch_size, label_len+pred_len, dec_out)
        # decoder时间戳特征 x_mark_dec: (batch_size, label_len+pred_len, ts_fnum)


        # Normalization from Non-stationary Transformer
        # 窗口标准化
        means = x_enc.mean(1, keepdim=True).detach()
        x_enc = x_enc - means
        stdev = torch.sqrt(
            torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)
        x_enc /= stdev


        # embedding:token embedding + postion embedding + temporal embedding。
        enc_out = self.enc_embedding(x_enc, x_mark_enc)  # [B,T,d_model]
        # 用MLP在时间维度上,获取未来预测部分的序列。即[B,T,C]->[B,T+pred_len,d_model]
        enc_out = self.predict_linear(enc_out.permute(0, 2, 1)).permute(
            0, 2, 1)  # align temporal dimension
        # TimesNet
        # 经过多少个TimeBlock
        # TimeBlock -> layer_norm -> TimeBlock -> layer_norm
        for i in range(self.layer):
            enc_out = self.layer_norm(self.model[i](enc_out))
        # porject back 输出预测序列 [B,pred_len,dec_out]
        dec_out = self.projection(enc_out)


        # De-Normalization from Non-stationary Transformer
        # 逆窗口标准化
        dec_out = dec_out * \
                  (stdev[:, 0, :].unsqueeze(1).repeat(
                      1, self.pred_len + self.seq_len, 1))
        dec_out = dec_out + \
                  (means[:, 0, :].unsqueeze(1).repeat(
                      1, self.pred_len + self.seq_len, 1))
        return dec_out

さらに、TimesBlock が渡されるたびに、レイヤーの正規化が実行されます。BN であっても LN であっても、2 つの正規化はレイヤーのパラメータを安定させ、勾配の消失や爆発を避けるためのものです。具体的な違いは次のとおりです。

- バッチ正規化: サンプルのバッチの各特徴を正規化し、異なるサンプル間のサイズ関係を保持し、CV フィールドに適用します。

- レイヤー正規化: 各サンプルのすべての特徴を正規化し、サンプル内の異なる特徴間のサイズ関係を保持し、NLP フィールドを使用します。

実験の様子を見てみましょう!ここでは長期予測と短期予測のパフォーマンスのみを示しますが、論文の付録には実験的な内容が多すぎるため、自分で読むことをお勧めします。

e3cbafeddd7c8af531f866ca350dea18.png

Q: FFT 後の上位 k 振幅の周波数を決定するにはどうすればよいですか?

A: 著者は実験的な調整を行った結果、k=3 がタイミング充填、分類、および異常検出タスクに適していることを発見しました。k=5 は短期的な予測に適しています。

dd500c19474f4ee91d1c76967342d85a.png

Q: d_model を決定するにはどうすればよいですか?

A: さまざまなタスクに応じて、作成者は式に従ってさまざまな d_models を構成します。次の表を参照してください。

ab5febcd15cc0a0ce975e42bf3ec2c1e.png

最後に全文を要約すると、実際には、タイミング データには複数の周期性が存在する可能性があるため、周波数領域に基づいて、1D データを 2D に変換した後、2D コンボリューション カーネルを適用して、データ変換中およびデータ内のタイミング変化をキャプチャします。 2D サイクルと振幅に基づく、複数期間の特性評価結果の動的重み付けとマージにより、主要なタイミング タスクのパフォーマンスが大幅に向上しました。

参考文献

[1] Wu, H.、Hu, T.、Liu, Y.、Zhou, H.、Wang, J.、および Long, M. (2022)。Timesnet: 一般的な時系列分析のための時間 2D 変動モデリング。*arXiv プレプリント arXiv:2210.02186*。

[2] 2Dに変更する場合の期間選択と長さ設定、Github: https://github.com/thuml/Time-Series-Library/issues/7

推奨読書:

私の2022年のインターネットスクール募集の共有

私の2021年のまとめ

アルゴリズムポストと開発ポストの違いについての話

インターネットスクール採用・研究開発・給与概要

2022年のネット就活状況、もうすぐ金9銀10が銅9鉄10になります!

公開番号:AIカタツムリカー

謙虚さを保ち、規律を保ち、改善し続ける

8abf0943e39e02d558146bcce024bef0.jpeg

【カタツムリ】を送って「ハンズオンAIプロジェクト」(AIカタツムリカー著)を入手

[1222] を送信して、適切な leetcode ブラッシングノートを入手してください

[AI Four Classics] を送信する 4 つの古典的な AI 電子書籍を入手する

おすすめ

転載: blog.csdn.net/qq_33431368/article/details/132703129