当前位置: 首页 > news >正文

《异常检测——从经典算法到深度学习》23 TimesNet: 用于常规时间序列分析的时间二维变化模型

zz# 《异常检测——从经典算法到深度学习》

  • 0 概论
  • 1 基于隔离森林的异常检测算法
  • 2 基于LOF的异常检测算法
  • 3 基于One-Class SVM的异常检测算法
  • 4 基于高斯概率密度异常检测算法
  • 5 Opprentice——异常检测经典算法最终篇
  • 6 基于重构概率的 VAE 异常检测
  • 7 基于条件VAE异常检测
  • 8 Donut: 基于 VAE 的 Web 应用周期性 KPI 无监督异常检测
  • 9 异常检测资料汇总(持续更新&抛砖引玉)
  • 10 Bagel: 基于条件 VAE 的鲁棒无监督KPI异常检测
  • 11 ADS: 针对大量出现的KPI流快速部署异常检测模型
  • 12 Buzz: 对复杂 KPI 基于VAE对抗训练的非监督异常检测
  • 13 MAD: 基于GANs的时间序列数据多元异常检测
  • 14 对于流数据基于 RRCF 的异常检测
  • 15 通过无监督和主动学习进行实用的白盒异常检测
  • 16 基于VAE和LOF的无监督KPI异常检测算法
  • 17 基于 VAE-LSTM 混合模型的时间异常检测
  • 18 USAD:多元时间序列的无监督异常检测
  • 19 OmniAnomaly:基于随机循环网络的多元时间序列鲁棒异常检测
  • 20 HotSpot:多维特征 Additive KPI 的异常定位
  • 21 Anomaly Transformer: 基于关联差异的时间序列异常检测
  • 22 Kontrast: 通过自监督对比学习识别软件变更中的错误
  • 23TimesNet: 用于常规时间序列分析的时间二维变化模型

相关:

  • VAE 模型基本原理简单介绍
  • GAN 数学原理简单介绍以及代码实践
  • 单指标时间序列异常检测——基于重构概率的变分自编码(VAE)代码实现(详细解释)

23. TimesNet: 用于常规时间序列分析的时间二维变化模型

论文名称:TIMESNET: TEMPORAL 2D-VARIATION MODELING FOR GENERAL TIME SERIES ANALYSIS
论文发表于 International Conference on Learning Representations 2023
论文下载:openreview
源码地址:https://github.com/thuml/TimesNet

23.1 论文概述

这篇论文的主要内容是介绍了一种新的方法来处理时间序列分析中的复杂时间模式。该方法将一维时间序列转换为二维张量以便更好地发现多个周期内和周期间的变化。作者提出的TimesNet with TimesBlock 是一种通用的时间序列分析骨干(Backbone)网络,可以用于多种分析任务。该论文还介绍了一些实验结果和性能比较,以证明该方法的有效性和优越性。

该论文提出的方法主要包括以下技术:

  1. TimesBlock:可以将一维时间序列自适应地转换为一组二维张量,并通过一个参数高效的Inception块来捕获二维空间中的周期内和周期间变化的模块。TimesBlock的设计使得它可以自适应地发现多个周期内和周期间的变化,并且可以通过Inception块来捕获这些变化。

  2. TimesNet:这是一种通用的时间序列分析骨干网络,可以用于多种分析任务。TimesNet使用了TimesBlock模块来处理时间序列,并利用现有的视觉骨干网络来进一步提高性能。TimesNet的设计使得它可以自适应地发现多个周期内和周期间的变化,并且可以通过Inception块来捕获这些变化。

  3. 多周期性:该论文提出了一种新的思路,即将时间序列转换为二维张量,以便更好地发现多个周期内和周期间的变化。这种方法可以更好地捕获时间序列中的复杂模式,并且可以用于多种分析任务。

  4. 实验结果和性能比较:该论文通过实验结果和性能比较证明了TimesNet的有效性和优越性。作者在五个主流的时间序列分析任务中进行了实验,包括短期和长期预测、填充、分类和异常检测。实验结果表明,TimesNet在这些任务中均取得了一致的最先进性能。

23.2 相关技术

该论文提出的方法主要包括以下技术:

在该论文的RELATED WORK模块中,作者介绍了一些与他们的工作相关的先前研究。这些研究主要涉及时间序列分析和深度学习领域的一些关键技术和方法。以下是一些相关的研究:

  1. 时间序列分析:该部分介绍了时间序列分析的一些基本概念和方法,包括时间序列的基本特征、时间序列分解、周期性分析、自回归模型、移动平均模型等。

  2. 卷积神经网络(CNN):该部分介绍了CNN在时间序列分析中的应用。CNN可以通过卷积操作来捕获时间序列中的局部模式,并且可以通过池化操作来减少参数数量和计算量。

  3. 循环神经网络(RNN):该部分介绍了RNN在时间序列分析中的应用。RNN可以通过循环连接来处理时间序列中的时序信息,并且可以通过长短时记忆(LSTM)单元来处理长期依赖关系。

  4. 注意力机制:该部分介绍了注意力机制在时间序列分析中的应用。注意力机制可以通过对时间序列中不同部分的加权来提高模型的性能,并且可以通过自注意力机制来处理时间序列中的长期依赖关系。

  5. 时空卷积神经网络(ST-CNN):该部分介绍了ST-CNN在时间序列分析中的应用。ST-CNN可以通过卷积操作来捕获时间序列中的时空信息,并且可以通过池化操作来减少参数数量和计算量。

23.3 核心方法

23.3.1 论文结构梳理

Section内容概述
1. INTRODUCTION相关背景介绍以及对本论文的概述
2. RELATED WORK相关技术介绍
3. TIMESNET论文主题部分
4. EXPERIMENTS实验部分
5. CONCLUSION AND FUTURE WORK总结以及未来工作

读者应当重点关注第3部分,如果需要对论文复现的话考虑读一些第4部分。

请一定不要太在意论文的结果复现时与你本地测试的结果不一致的问题,因为导致结果有所差异的原因很多,我们不是审稿员,也没有必要太较真实验结果。主要还是理清楚核心过程。

23.3.2 INTRODUCTION

本文从多周期性这一新的维度对时间序列进行分析,以解决复杂的时间变化问题。首先,我们观察到现实世界的时间序列通常呈现多周期性,例如天气观测的日变化和年变化,电力消耗的周变化和季度变化。这些多个时期相互重叠和相互作用,使得变化建模变得棘手。其次,对于每个时段,我们发现每个时间点的变化不仅受其相邻区域的时间模式的影响,而且与其相邻时段的变化高度相关。为了清楚起见,我们将这两种时间变化分别命名为期内变化和期间变化。前者表示一个时期内的短期时间模式。后者可以反映连续不同时期的长期趋势。注意,对于没有明确周期性的时间序列,变化将由周期内变化主导,并且等价于具有无限周期长度的时间序列。

由于不同的周期会导致不同的周期内和周期间的变化,多周期性可以自然地衍生出一个模块化的架构,用于时间变化建模,在那里我们可以捕获由某个特定的周期在一个模块中产生的变化。此外,这种设计使复杂的时间模式被解开,有利于时间变化建模。然而,值得注意的是,一维时间序列很难同时明确地呈现两种不同类型的变化。为了解决这个障碍,我们扩展到二维空间的时间变化的分析。具体来说,如图1所示,我们可以将一维时间序列重塑为二维张量,其中每列包含一个周期内的时间点,每行包含不同周期中同一阶段的时间点。因此,通过将一维时间序列变换为二维张量,可以突破一维空间的表征能力瓶颈,成功地将二维空间的周期内和周期间变化统一起来,得到时间二维变化。

在技术上,基于上述动机,我们超越了以前的骨干,提出了TimesNet作为一个新的任务通用模型的时间序列分析。通过TimesBlock的授权,TimesNet可以发现时间序列的多周期性,并在模块化架构中捕获相应的时间变化。具体来说,TimesBlock可以根据学习的周期自适应地将一维时间序列转换为一组二维张量,并通过参数高效的初始块进一步捕获二维空间中的周期内和周期间变化。在实验中,TimesNet在五个主流分析任务中实现了一致的最先进水平,包括短期和长期预测,插补,分类和异常检测。我们的贡献可归纳为三个方面:

  • 基于多周期性和周期内、周期间复杂的相互作用,本文提出了一种时间变化建模的模块化方法。通过将一维时间序列转换到二维空间,我们可以同时呈现期内和期间的变化。
  • 我们提出了TimesNet与TimesBlock发现多个周期和捕获时间的二维变化从变换的二维张量的参数有效的起始块。
  • 作为一个通用的任务基础模型,TimesNet在五个主流的时间序列分析任务中达到了一致的先进水平。包括详细和有见地的可视化。

23.3.3 模型结构 1 —— 将一维变分变换为二维变分

图一

如图1所示,每个时间点同时涉及两种类型的时间变化,即与其相邻区域的时间变化和不同时期之间的相同相位的时间变化,即期内变化和期间变化。然而,这种原始的时间序列的一维结构只能呈现相邻时间点之间的变化。为了解决这个问题,我们探索了时间变化的二维结构,它可以显式地呈现周期内和周期之间的变化,从而在表示能力方面具有更多优势,并有利于后续的表示学习。

具体地说,对于 C C C 个长度为 T T T 的时间序列记录变量(recorded variates),其原始一维组织形式为 X 1D ∈ R T × C \mathbf{X}_\text{1D} \in \mathbb{R}^{T×C} X1DRT×C。为了表示周期间的变化,我们需要首先发现周期。从技术上讲,我们通过快速傅里叶变换(Fast Fourier Transform,FFT)在频域中分析时间序列如下:

A = Avg ( Amp  ( FFT ( X l D ) ) ) , { f 1 , ⋯ , f k } = arg Topk  ( A ) , p i = ⌈ T f i ⌉ , i ∈ { 1 , ⋯ , k } . (1) \mathbf{A}=\text{Avg}\left(\text{ Amp }\left(\text{ FFT}(\mathbf{X}_{\mathrm{lD}})\right)\right),\{f_1,\cdots,f_k\}=\text{ arg Topk }\left(\mathbf{A}\right),p_i=\left\lceil\frac{T}{f_i}\right\rceil,i\in\{1,\cdots,k\}.\tag{1} A=Avg( Amp ( FFT(XlD))),{f1,,fk}= arg Topk (A),pi=fiT,i{1,,k}.(1)

其中, FFT ( ⋅ ) \text{FFT}(\cdot) FFT() Amp ( ⋅ ) \text{Amp}(\cdot) Amp() 表示FFT和幅度值的计算。 A ∈ R T \mathbf{A}\in \mathbb{R}^T ART 表示每个频率的计算振幅,这是从 C C C 维平均 Avg ( ⋅ ) \text{Avg}(\cdot) Avg()。注意,第 j j j 个值 A j A_j Aj 表示与周期长度对应的频率 j j j 周期基函数的强度 ⌈ T j ⌉ \lceil {\frac{T}{j}} \rceil jT。考虑到频域的稀疏性和避免无意义的高频带来的噪声,我们只选择前 k k k 个幅度值 f 1 , . . . , f k f_1,...,f_k f1,...,fk 并获得最重要的频率 { A f 1 , . . . . , A f k } \{\mathbf{A}_{f_1},....,\mathbf{A}_{f_k}\} {Af1,....,Afk},其中 k k k 是超参数。这些选择的频率也对应于 k k k 个周期长度 { p i , . . . , p k } \{p_i, ..., p_k\} {pi,...,pk}。由于频域的共轭性,我们只考虑 { 1 , . . . , [ T 2 ] } \{1, ..., [\frac{T}{2}]\} {1,...,[2T]}内的频率。我们将等式1总结如下:

A , { f 1 , ⋯ , f k } , { p 1 , ⋯ , p k } = P e r i o d ( X 1 D ) (2) \mathbf{A},\{f_{1},\cdots,f_{k}\},\{p_{1},\cdots,p_{k}\}=\mathrm{Period}(\mathbf{X}_{1\text{D}} ) \tag{2} A,{f1,,fk},{p1,,pk}=Period(X1D)(2)

基于选定的频率 { f 1 , . . . , f k } \{f_1, ..., f_k\} {f1,...,fk} 和相应的周期长度 { p 1 , . . . , p k } \{p_1, ..., p_k\} {p1,...,pk},我们可以通过以下等式将一维时间序列 X 1 D ∈ R T × C \mathbf{X}_{1D} \in \mathbb{R}^{T\times C} X1DRT×C 整形为多个二维张量:

X 2 D i = Reshape ⁡ p i , f i ( Padding ⁡ ( X 1 D ) ) , i ∈ { 1 , ⋯ , k } (3) \mathbf{X}_{2 \mathrm{D}}^i=\operatorname{Reshape}_{p_i, f_i}\left(\operatorname{Padding}\left(\mathbf{X}_{1 \mathrm{D}}\right)\right), i \in\{1, \cdots, k\} \tag{3} X2Di=Reshapepi,fi(Padding(X1D)),i{1,,k}(3)

其中 Padding ( ⋅ ) \text{Padding}(\cdot) Padding() 是将时间序列沿着时间维度扩展零,以使其与 Reshape p i , f i \text{Reshape}_{p_i, f_i} Reshapepi,fi ( ⋅ ) (\cdot) () 兼容,其中 p i p_i pi f i f_i fi 分别表示变换后的二维张量的行数和列数。注意, X 2D i ∈ R p i × f i × C \mathbf{X}^i_{\text{2D}} \in \mathbb{R}^{p_i \times f_i \times C} X2DiRpi×fi×C 表示基于频率 f i f_i fi 的第 i i i 个整型时间序列,其列和行分别表示相应周期长度 p i p_i pi 下的周期内变化和周期间变化。最终,如图2 所示,基于所选择的频率和估计的周期,我们获得一组二维张量 X 2 D 1 , . . . , X 2 D k \mathbf{X}^1_{2D},...,\mathbf{X}^k_{2D} X2D1,...,X2Dk ,其指示由不同周期导出的 k k k 个不同的时间二维张量。

图2

同样值得注意的是,这种变换为变换后的2维张量带来了两种类型的局部,即相邻时间点(列,周期内变化)和相邻周期(行,周期间变化)之间的局部。因此,时间二维变换可以容易地由二维内核处理。

23.3.4 模型结构 2 —— TIMESBLOCK

图3
如图3所示,我们以残差方式组织 TimesBlock。具体地说,对于长度为 T T T 的一维输入时间序列 X 1D ∈ R T × C \mathbf{X}_{\text{1D}} \in \mathbb{R}^{T \times C} X1DRT×C,我们首先通过嵌入层 X 1D 0 = Embed ( X 1 D ) \mathbf{X}^0 _{\text{1D}} = \text{Embed}(\textbf{X}_{1\text{D}}) X1D0=Embed(X1D) 将原始输入投影到深度特征 X 1D 0 ∈ R T × d \mathbf{X}^0_{\text{1D}} \in \mathbb{R}^{T\times d} X1D0RT×d 模型中。对于 TimesNet 的第 l l l 层,输入是 X 1D l − 1 ∈ R T × d m o d e l \mathbf{X}^{l−1}_{\text{1D}} \in \mathbb{R}^{T\times d_{model}} X1Dl1RT×dmodel,该过程可以形式化为:

X 1 D l = TimesBlock  ( X 1 D l − 1 ) + X 1 D l − 1 (4) \mathbf{X}_{1 \mathrm{D}}^l=\text { TimesBlock }\left(\mathbf{X}_{1 \mathrm{D}}^{l-1}\right)+\mathbf{X}_{1 \mathrm{D}}^{l-1} \tag{4} X1Dl= TimesBlock (X1Dl1)+X1Dl1(4)

如图3所示,对于第 L L L 个TimesBlock,整个过程包括两个连续的部分:捕获时间二维变化和自适应地聚合来自不同时期的表示。

捕获时间2D变化 类似于等式1,我们可以通过 Period ( ⋅ ) \text{Period}(\cdot) Period() 估计深度特征 X 1D l − 1 \mathbf{X}^{l-1}_{\text{1D}} X1Dl1 的周期长度。基于估计的周期长度,我们可以将一维时间序列变换到二维空间,得到一组二维张量,从这组张量中我们可以得到二维空间的参数有效信息表示,并得到一组二维张量,从这组张量中我们可以方便地得到参数有效起始块的信息表示。该过程形式化如下:

A l − 1 , { f 1 , ⋯ , f k } , { p 1 , ⋯ , p k } = Period ⁡ ( X 1 D l − 1 ) X 2 D l , i = Reshape ⁡ p i , f i ( Padding ⁡ ( X 1 D l − 1 ) ) , i ∈ { 1 , ⋯ , k } X ^ 2 D l , i = Inception ⁡ ( X 2 D l , i ) , i ∈ { 1 , ⋯ , k } X ^ 1 D l , i = Trunc ⁡ ( Reshape ⁡ 1 , ( p i × f i ) ( X ^ 2 D l , i ) ) , i ∈ { 1 , ⋯ , k } , (5) \begin{aligned} \mathbf{A}^{l-1},\left\{f_1, \cdots, f_k\right\},\left\{p_1, \cdots, p_k\right\} & =\operatorname{Period}\left(\mathbf{X}_{1 \mathrm{D}}^{l-1}\right) \\ \mathbf{X}_{2 \mathrm{D}}^{l, i} & =\operatorname{Reshape}_{p_i, f_i}\left(\operatorname{Padding}\left(\mathbf{X}_{1 \mathrm{D}}^{l-1}\right)\right), i \in\{1, \cdots, k\} \\ \widehat{\mathbf{X}}_{2 \mathrm{D}}^{l, i} & =\operatorname{Inception}\left(\mathbf{X}_{2 \mathrm{D}}^{l, i}\right), i \in\{1, \cdots, k\} \\ \widehat{\mathbf{X}}_{1 \mathrm{D}}^{l, i} & =\operatorname{Trunc}\left(\operatorname{Reshape}_{1,\left(p_i \times f_i\right)}\left(\widehat{\mathbf{X}}_{2 \mathrm{D}}^{l, i}\right)\right), i \in\{1, \cdots, k\}, \tag{5} \end{aligned} Al1,{f1,,fk},{p1,,pk}X2Dl,iX 2Dl,iX 1Dl,i=Period(X1Dl1)=Reshapepi,fi(Padding(X1Dl1)),i{1,,k}=Inception(X2Dl,i),i{1,,k}=Trunc(Reshape1,(pi×fi)(X 2Dl,i)),i{1,,k},(5)

其中 X 2D l , i ∈ R p i × f i × d m o d e l \mathbf{X}_{\text{2D}}^{l,i}\in\mathbb{R}^{p_i\times f_i \times d_{model}} X2Dl,iRpi×fi×dmodel 表示第 i i i 个变换而得的二维向量。转换完成以后,我们通过参数有效的初始块将2D张量处理为初始 Inspection ( ⋅ ) \text{Inspection}(\cdot) Inspection(),该初始块涉及多维2D核(multi-scale 2D kernels),是最知名的视觉骨干之一。然后,我们将学习到的2D表示 X ^ 2 D l , i \widehat{\mathbf{X}}_{2 \mathrm{D}}^{l, i} X 2Dl,i 转换回一维空间 X ^ 1 D l , i ∈ R T × d model  \widehat{\mathbf{X}}_{1 \mathrm{D}}^{l, i} \in \mathbb{R}^{T \times d_{\text {model }}} X 1Dl,iRT×dmodel  从而用于聚合,其中我们使用 Trunc ( ⋅ ) \text{Trunc}(\cdot) Trunc() 将长度为 ( p i × f i ) (p_i \times f_i) (pi×fi) 的填充序列截断为原始长度 T T T

注意,得益于一维时间序列的转换,起始块中的2D核可以同时聚合多尺度周期内变化(列)和周期间变化(行),覆盖相邻时间点和相邻周期。此外,我们对不同的重构 2D 张量 { X 2 D l , 1 , ⋯ , X 2 D l , k } \left\{\mathbf{X}_{2 \mathrm{D}}^{l, 1}, \cdots, \mathbf{X}_{2 \mathrm{D}}^{l, k}\right\} {X2Dl,1,,X2Dl,k} 采用了共享的起始块来提高参数效率,这可以使模型大小对超参数k的选择保持不变。

自适应聚合 最后,我们需要为下一层融合 k k k 个不同的 1D 表示 { X ^ 1 D l , 1 , ⋯ , X ^ 1 D l , k } \left\{\widehat{\mathbf{X}}_{1 \mathrm{D}}^{l, 1}, \cdots, \widehat{\mathbf{X}}_{1 \mathrm{D}}^{l, k}\right\} {X 1Dl,1,,X 1Dl,k}。受自相关的启发,幅度 A A A 可以反映所选择的频率和周期的相对重要性,从而对应于每个变换的2D张量的重要性。因此,我们基于振幅聚合1D表示:

A ^ f 1 l − 1 , ⋯ , A ^ f k l − 1 = Softmax ⁡ ( A f 1 l − 1 , ⋯ , A f k l − 1 ) X 1 D l = ∑ i = 1 k A ^ f i l − 1 × X ^ 1 D l , i (6) \begin{aligned} \widehat{\mathbf{A}}_{f_1}^{l-1}, \cdots, \widehat{\mathbf{A}}_{f_k}^{l-1} & =\operatorname{Softmax}\left(\mathbf{A}_{f_1}^{l-1}, \cdots, \mathbf{A}_{f_k}^{l-1}\right) \\ \mathbf{X}_{1 \mathrm{D}}^l & =\sum_{i=1}^k \widehat{\mathbf{A}}_{f_i}^{l-1} \times \widehat{\mathbf{X}}_{1 \mathrm{D}}^{l, i} \end{aligned} \tag{6} A f1l1,,A fkl1X1Dl=Softmax(Af1l1,,Afkl1)=i=1kA fil1×X 1Dl,i(6)

由于周期内和周期之间的变化已经涉及到多个高度结构化的2D张量,TimesBlock可以同时完全捕获多尺度时间2D变化。因此,TimesNet可以实现比直接从1D时间序列更有效的表示学习。

2D视觉主干的通用性 受益于1D时间序列到时间2D变化的转换,我们可以选择各种计算机视觉骨干来代替表示学习的初始块,例如广泛使用的 ResNet 和ResNeXt,先进的 ConvNeXt 和基于注意力的模型。因此,我们的时间二维变化设计也将一维时间序列与蓬勃发展的二维视觉骨干连接起来,使时间序列分析能够利用计算机视觉社区的发展。一般来说,用于表示学习的更强大的2D主干将带来更好的性能。考虑到性能和效率(图4右),我们基于参数高效的初始块进行主要实验,如公式5所示。
在这里插入图片描述

23.4 论文实验

特此声明:如果不打算基于这篇论文的源码开发,亦或者不需要了解代码细节,完全不需要运行所有源码。主要把论文思路梳理清楚就差不多了。因为复现这些源码比较麻烦,至少需要一张还过得去的显卡。不过有条件、有时间、感兴趣的同学可以试试。

23.4.1 数据准备

前去本论文的提到的github地址可以找到对应的谷歌云盘、清华云盘(当前已经失效)以及百度云盘的下载链接,如果觉得麻烦的话,可以考虑访问我的夸克云盘,地址如下:

夸克云盘
链接:https://pan.quark.cn/s/b167f0d17234
提取码:wQKX

数据文件解压的相对地址等,将在后面介绍。

23.4.2 源码准备

前去本论文提到的github地址,把源码clone或下载到本地,然后把前面下载的数据集压缩包接下,注意相对路径,而且需要把解压后的文件夹重命名为 dataset。如图所示:
在这里插入图片描述

23.4.3 安装相关依赖

为了尽可能地减少麻烦,先保证使用的python版本与论文实验一致,即 3.8,其次确保安装好了与显卡对应的驱动,确保cuda可用,这些方面的坑太多太多,所以为了避免继续踩坑,尽可能与原论文的版本保持一致。如图所示:
在这里插入图片描述

23.4.4 执行脚本

安装依赖以后,我们不妨写几行代码,确保 cuda 可用。

import torchprint(torch.cuda.is_available())

在这里插入图片描述
接着可以直接执行作者已经给我们写好的脚本,注意,可能出现GPU不能用的情况

在这里插入图片描述
编辑这个 sh 脚本文件,并如图所示,修改使用GPU的索引为0,因为我只有一张显卡,只能使用索引为0的显卡。

在这里插入图片描述
在这里插入图片描述
其他脚本皆是如此,如果只有一张显卡,就把脚本第一行最后一个值改为 0

其他的脚本运行方法均是如此,唯一需要注意的就是 export CUDA_VISIBLE_DEVICES=0 确保是自己想用的显卡的序号。

23.5 速读源码

23.5.1 layers/Conv_Blocks.py

这段代码定义了两个不同版本的 Inception 模块(Inception_Block_V1 和 Inception_Block_V2),这些模块包含多个卷积核,用于从输入数据中提取不同尺度的特征。这些模块可以用于深度学习模型中,以提高特征提取的多样性。模块中的卷积核具有不同的大小和填充,以捕获不同尺度的信息。在前向传播中,模块对输入数据应用这些卷积核,并将它们的输出平均在一起,以生成最终的输出。

import torch
import torch.nn as nn# 定义一个名为 Inception_Block_V1 的 PyTorch 模型类
class Inception_Block_V1(nn.Module):def __init__(self, in_channels, out_channels, num_kernels=6, init_weight=True):super(Inception_Block_V1, self).__init__()# 初始化模块的参数self.in_channels = in_channelsself.out_channels = out_channelsself.num_kernels = num_kernels# 创建卷积核列表,用于构建多个不同尺寸的卷积核kernels = []for i in range(self.num_kernels):# 添加卷积层,kernel_size为2 * i + 1,padding为ikernels.append(nn.Conv2d(in_channels, out_channels, kernel_size=2 * i + 1, padding=i))# 使用 nn.ModuleList 将卷积核列表转化为模块列表self.kernels = nn.ModuleList(kernels)# 如果 init_weight 为 True,则初始化模块的权重if init_weight:self._initialize_weights()def _initialize_weights(self):# 初始化模型参数的函数for m in self.modules():if isinstance(m, nn.Conv2d):# 使用 kaiming_normal_ 初始化权重,适用于 ReLU 激活函数nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')if m.bias is not None:# 初始化偏置项为零nn.init.constant_(m.bias, 0)# 前向传播方法,接受输入 xdef forward(self, x):res_list = []# 对每个卷积核进行前向传播,并将结果添加到res_list中for i in range(self.num_kernels):res_list.append(self.kernels[i](x))# 在最后一个维度上堆叠结果,然后计算平均值res = torch.stack(res_list, dim=-1).mean(-1)return res# 定义另一个名为 Inception_Block_V2 的 PyTorch 模型类
class Inception_Block_V2(nn.Module):def __init__(self, in_channels, out_channels, num_kernels=6, init_weight=True):super(Inception_Block_V2, self).__init__()# 初始化模块的参数self.in_channels = in_channelsself.out_channels = out_channelsself.num_kernels = num_kernelskernels = []for i in range(self.num_kernels // 2):# 创建两种不同尺寸的卷积核kernels.append(nn.Conv2d(in_channels, out_channels, kernel_size=[1, 2 * i + 3], padding=[0, i + 1]))kernels.append(nn.Conv2d(in_channels, out_channels, kernel_size=[2 * i + 3, 1], padding=[i + 1, 0]))# 添加一个额外的卷积核,kernel_size为1kernels.append(nn.Conv2d(in_channels, out_channels, kernel_size=1))# 使用 nn.ModuleList 将卷积核列表转化为模块列表self.kernels = nn.ModuleList(kernels)# 如果 init_weight 为 True,则初始化模块的权重if init_weight:self._initialize_weights()def _initialize_weights(self):# 初始化模型参数的函数for m in self.modules():if isinstance(m, nn.Conv2d):# 如果当前模块是卷积层# 使用 Kaiming 初始化(适用于 ReLU 激活函数的初始化)nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')if m.bias is not None:# 如果卷积层具有偏置项,将其初始化为零nn.init.constant_(m.bias, 0)def forward(self, x):res_list = []# 对每个卷积核进行前向传播,并将结果添加到res_list中for i in range(self.num_kernels + 1):res_list.append(self.kernels[i](x))# 在最后一个维度上堆叠结果,然后计算平均值res = torch.stack(res_list, dim=-1).mean(-1)return res

23.5.2 layers/Embed.py 其中的 DataEmbedding

这段代码定义了一个 DataEmbedding 模块,用于将输入数据进行嵌入处理,包括值嵌入、时间特征嵌入和位置编码。注释解释了每个函数的功能,包括初始化模块、创建不同类型的嵌入层、前向传播的操作以及如何应用 Dropout 以防止过拟合。根据 embed_type 参数的不同,可以选择不同的嵌入类型。根据是否提供时间特征 x_mark,可以选择不同的嵌入方式。

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.utils import weight_norm
import math### 前面的代码 TimesNet用不到,这里没有复制过来,也不做解释### 以下代码是 TimesNet 用到的class DataEmbedding(nn.Module):def __init__(self, c_in, d_model, embed_type='fixed', freq='h', dropout=0.1):super(DataEmbedding, self).__init__()# 初始化 DataEmbedding 模块# c_in:输入通道数,通常是词汇表的大小# d_model:输出维度(模型的维度)# embed_type:嵌入类型,可以是 'fixed' 或 'timeF'# freq:时间频率,可以是 'h'(小时)、't'(分钟)、's'(秒)、'm'(月份)、'a'(年份)、'w'(星期)、'd'(日期)、'b'(工作日)# dropout:Dropout 概率,用于防止过拟合# 创建值嵌入层,用于将输入值(例如,词汇表中的词)映射为模型维度self.value_embedding = TokenEmbedding(c_in=c_in, d_model=d_model)# 创建位置编码层,用于表示输入序列中的位置信息self.position_embedding = PositionalEmbedding(d_model=d_model)# 创建时间特征嵌入层,用于表示时间特征(如小时、星期等)# 嵌入类型根据 embed_type 参数选择,可以是 'fixed' 或 'timeF'self.temporal_embedding = TemporalEmbedding(d_model=d_model, embed_type=embed_type, freq=freq) if embed_type != 'timeF' else TimeFeatureEmbedding(d_model=d_model, embed_type=embed_type, freq=freq)# 创建 Dropout 层,用于防止过拟合self.dropout = nn.Dropout(p=dropout)def forward(self, x, x_mark):# 前向传播函数# x:输入值张量,形状为 [batch_size, sequence_length, c_in]# x_mark:时间特征张量,形状取决于时间特征的嵌入类型if x_mark is None:# 如果没有时间特征,只使用值嵌入和位置编码x = self.value_embedding(x) + self.position_embedding(x)else:# 如果有时间特征,使用值嵌入、时间特征嵌入和位置编码x = self.value_embedding(x) + self.temporal_embedding(x_mark) + self.position_embedding(x)# 应用 Dropout 以减少过拟合return self.dropout(x)

23.5.3 models/TimesNet.py

以下这段代码定义了一个名为 TimesBlock 的 PyTorch 模型类,它实现了一种时间序列分析方法。该模块接受一个时间序列输入 x,通过快速傅里叶变换(FFT)分析其周期性,并使用卷积操作对不同周期的信号进行处理。模块的作用是对输入的时间序列进行周期性分析和特征提取,以捕捉时间序列中的周期性变化。最终输出经过自适应聚合和残差连接后的时间序列。

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.fft
from layers.Embed import DataEmbedding
from layers.Conv_Blocks import Inception_Block_V1def FFT_for_Period(x, k=2):# 使用快速傅里叶变换(FFT)分析时间序列 x 的周期性# x: 输入的时间序列 [B, T, C]xf = torch.fft.rfft(x, dim=1)# 通过振幅找到频率分量frequency_list = abs(xf).mean(0).mean(-1)frequency_list[0] = 0_, top_list = torch.topk(frequency_list, k)top_list = top_list.detach().cpu().numpy()period = x.shape[1] // top_list# 返回估计的周期和相应的频率成分return period, abs(xf).mean(-1)[:, top_list]# 定义一个名为 TimesBlock 的 PyTorch 模型类
class TimesBlock(nn.Module):def __init__(self, configs):super(TimesBlock, self).__init__()# 初始化模块的参数self.seq_len = configs.seq_len  # 输入序列的长度self.pred_len = configs.pred_len  # 预测序列的长度self.k = configs.top_k  # 周期估计的前 k 个频率成分# 创建一个卷积层序列,使用 Inception_Block_V1 模块self.conv = nn.Sequential(Inception_Block_V1(configs.d_model, configs.d_ff,num_kernels=configs.num_kernels),  # 第一个卷积层nn.GELU(),  # GELU 激活函数Inception_Block_V1(configs.d_ff, configs.d_model,num_kernels=configs.num_kernels)  # 第二个卷积层)# 前向传播方法,接受输入 xdef forward(self, x):B, T, N = x.size()  # 获取输入 x 的维度信息# 使用 FFT_for_Period 函数分析输入 x 的周期性,并获取周期列表和周期权重period_list, period_weight = FFT_for_Period(x, self.k)res = []  # 存储结果的列表for i in range(self.k):period = period_list[i]  # 获取第 i 个周期# 填充输入,使其长度能够整除周期if (self.seq_len + self.pred_len) % period != 0:length = (((self.seq_len + self.pred_len) // period) + 1) * periodpadding = 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# 重塑输入,将时间序列变成 2D 形式out = out.reshape(B, length // period, period, N).permute(0, 3, 1, 2).contiguous()# 应用卷积层,将1D变化转化为2D变化out = self.conv(out)# 重塑回原始形状out = out.permute(0, 2, 3, 1).reshape(B, -1, N)res.append(out[:, :(self.seq_len + self.pred_len), :])  # 将结果添加到列表中res = torch.stack(res, dim=-1)  # 将结果堆叠在一起,形成一个多通道的输出period_weight = F.softmax(period_weight, dim=1)  # 对周期权重进行 softmax 归一化period_weight = period_weight.unsqueeze(1).unsqueeze(1).repeat(1, T, N, 1)  # 调整周期权重的形状res = torch.sum(res * period_weight, -1)  # 对多通道输出进行加权求和,进行自适应聚合res = res + x  # 添加残差连接return res  # 返回最终输出

这段代码定义了一个 PyTorch 模型类,用于处理不同类型的时间序列数据分析任务。根据任务类型,模型采用不同的网络架构和输出层。该模型类包括任务类型如长期预测、缺失值填充、异常检测和分类。根据不同的任务类型,模型进行数据标准化、数据嵌入、网络模型的运算、投影和反标准化等步骤,以满足任务要求。前向传播方法根据任务类型返回相应的结果。

# 定义一个名为 Model 的 PyTorch 模型类
class Model(nn.Module):"""Paper link: https://openreview.net/pdf?id=ju_Uqw384Oq"""def __init__(self, configs):super(Model, self).__init__()# 初始化模型的参数self.configs = configsself.task_name = configs.task_name  # 任务名称self.seq_len = configs.seq_len  # 输入序列的长度self.label_len = configs.label_len  # 标签序列的长度self.pred_len = configs.pred_len  # 预测序列的长度# 创建一系列的 TimesBlock 模块,数量为配置中的 e_layersself.model = nn.ModuleList([TimesBlock(configs) for _ in range(configs.e_layers)])# 创建数据嵌入模块self.enc_embedding = DataEmbedding(configs.enc_in, configs.d_model, configs.embed, configs.freq,configs.dropout)self.layer = configs.e_layersself.layer_norm = nn.LayerNorm(configs.d_model)# 根据任务类型不同,初始化不同的输出层if self.task_name == 'long_term_forecast' or self.task_name == 'short_term_forecast':self.predict_linear = nn.Linear(self.seq_len, self.pred_len + self.seq_len)  # 用于预测的线性层self.projection = nn.Linear(configs.d_model, configs.c_out, bias=True)  # 投影线性层if self.task_name == 'imputation' or self.task_name == 'anomaly_detection':self.projection = nn.Linear(configs.d_model, configs.c_out, bias=True)  # 投影线性层if self.task_name == 'classification':self.act = F.gelu  # 激活函数self.dropout = nn.Dropout(configs.dropout)  # 丢弃层self.projection = nn.Linear(configs.d_model * configs.seq_len, configs.num_class)  # 分类线性层# 用于长期预测任务的方法def forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):# 标准化输入数据means = x_enc.mean(1, keepdim=True).detach()x_enc = x_enc - meansstdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)x_enc /= stdev# 数据嵌入enc_out = self.enc_embedding(x_enc, x_mark_enc)# 预测线性层enc_out = self.predict_linear(enc_out.permute(0, 2, 1)).permute(0, 2, 1)# TimesNet 模型for i in range(self.layer):enc_out = self.layer_norm(self.model[i](enc_out))# 投影线性层dec_out = self.projection(enc_out)# 反标准化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# 用于缺失值填充任务的方法def imputation(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask):# 标准化输入数据means = torch.sum(x_enc, dim=1) / torch.sum(mask == 1, dim=1)means = means.unsqueeze(1).detach()x_enc = x_enc - meansx_enc = x_enc.masked_fill(mask == 0, 0)stdev = torch.sqrt(torch.sum(x_enc * x_enc, dim=1) /torch.sum(mask == 1, dim=1) + 1e-5)stdev = stdev.unsqueeze(1).detach()x_enc /= stdev# 数据嵌入enc_out = self.enc_embedding(x_enc, x_mark_enc)# TimesNet 模型for i in range(self.layer):enc_out = self.layer_norm(self.model[i](enc_out))# 投影线性层dec_out = self.projection(enc_out)# 反标准化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# 用于异常检测任务的方法def anomaly_detection(self, x_enc):# 标准化输入数据means = x_enc.mean(1, keepdim=True).detach()x_enc = x_enc - meansstdev = torch.sqrt(torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5)x_enc /= stdev# 数据嵌入enc_out = self.enc_embedding(x_enc, None)# TimesNet 模型for i in range(self.layer):enc_out = self.layer_norm(self.model[i](enc_out))# 投影线性层dec_out = self.projection(enc_out)# 反标准化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# 用于分类任务的方法def classification(self, x_enc, x_mark_enc):# 数据嵌入enc_out = self.enc_embedding(x_enc, None)# TimesNet 模型for i in range(self.layer):enc_out = self.layer_norm(self.model[i](enc_out))# 输出output = self.act(enc_out)output = self.dropout(output)output = output * x_mark_enc.unsqueeze(-1)output = output.reshape(output.shape[0], -1)output = self.projection(output)return output# 前向传播方法def forward(self, x_enc, x_mark_enc, x_dec, x_mark_dec, mask=None):if self.task_name == 'long_term_forecast' or self.task_name == 'short_term_forecast':dec_out = self.forecast(x_enc, x_mark_enc, x_dec, x_mark_dec)return dec_out[:, -self.pred_len:, :]  # 返回预测结果if self.task_name == 'imputation':dec_out = self.imputation(x_enc, x_mark_enc, x_dec, x_mark_dec, mask)return dec_out  # 返回填充结果if self.task_name == 'anomaly_detection':dec_out = self.anomaly_detection(x_enc)return dec_out  # 返回异常检测结果if self.task_name == 'classification':dec_out = self.classification(x_enc, x_mark_enc)return dec_out  # 返回分类结果return None  # 若任务名称不匹配,则返回空值

23.6 总结

本篇论文介绍了一种新的方法,通过将一维时间序列转换为二维张量来处理时间序列分析中的复杂时间模式。作者提出的TimesNet with TimesBlock是一种通用的时间序列分析模型,可以发现时间序列中的多个周期内和周期间的变化。该模型在五个主流时间序列分析任务中表现出了很好的通用性和性能。作者还提出了未来的研究方向,包括在大规模预训练中进一步探索利用TimesNet作为骨干的方法,以及在实际应用中的潜在应用。

论文提供的源码集成了一些其他的算法,对于需要做比对实验的人来说,简直是太方便了。并且,如果你设计了某个算法,可以基于TimesNet 源码架构进行开发,然后合并到代码仓库中作为其中一个算子,简单方便直观,给作者团队点赞 ~

Smileyan
2023.10.31 22:58

相关文章:

《异常检测——从经典算法到深度学习》23 TimesNet: 用于常规时间序列分析的时间二维变化模型

zz# 《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Don…...

计算机网络(59)

1. OSI 的七层模型分别是?各自的功能是什么? 2. 为什么需要三次握手?两次不行? 3. 为什么需要四次挥手?三次不行? 4. TCP与UDP有哪些区别?各自应用场景? 5. HTTP1.0,1.1&…...

【CSS】CSS基础知识扫盲

1、 什么是CSS? CSS即层叠样式表 (Cascading Style Sheets). CSS 能够对网页中元素位置的排版进行像素级精确控制, 实现美化页面的效果. 能够做到页面的样式和结构分离 2、 CSS引入方式 CSS代码编写的时候有多种引入方式: 内部样式、外部样式、内联样…...

React中的状态管理

目录 前言 1. React中的状态管理 1.1 本地状态管理 1.2 全局状态管理 Redux React Context 2. React状态管理的优势 总结 前言 当谈到前端开发中的状态管理时,React是一个备受推崇的选择。React的状态管理机制被广泛应用于构建大型、复杂的应用程序&#xf…...

【优选算法系列】【专题九链表】第一节.链表常用技巧和操作总结(2. 两数相加)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、链表常用技巧和操作总结二、两数相加 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写总结 前言 一、链表常…...

上线Spring boot-若依项目

基础环境 所有环境皆关闭防火墙与selinux 服务器功能主机IP主机名服务名称配置前端服务器192.168.231.177nginxnginx1C2G后端服务器代码打包192.168.231.178javajava、maven、nodejs4C8G数据库/缓存192.168.231.179dbmysql、redis2C4G Nginx #配置Nginxyum源 [rootnginx ~]…...

pinia简单使用

新命令-创建vue3项目 vue create 方式使用脚手架创建项目,vue cli处理, vue3后新的脚手架工具create-vue 使用npm init vuelatest 命令创建即可。 在pinia中,将使用的组合式函数识别为状态管理内容 自动将ref 识别为stste,computed 相当于 ge…...

数据库进阶教学——数据库故障恢复(日志文件)

目录 一、日志简介 二、日志文件操作 1、查看日志状态 2、开启日志功能 3、查看日志文件 4、查看当前日志 5、查看日志中的事件 6、删除日志文件 7、查看和修改日志文件有效期 8、查看日志文件详细信息 三、删除的数据库恢复 一、日志简介 日志是记录所有数据库表结…...

Leetcode 73 矩阵置0

class Solution {//1.用矩阵的第一行和第一列来标记该行或该列是否应该为0,但是这样的话忽视了第一行或第一列为0的情况//2.用标记row0和column0来标记第一行或第一列是否该为0public void setZeroes(int[][] matrix) {int n matrix.length;int m matrix[0].length;boolean r…...

Rust学习日记(二)变量的使用--结合--温度换算/斐波那契数列--实例

前言: 这是一个系列的学习笔记,会将笔者学习Rust语言的心得记录。 当然,这并非是流水账似的记录,而是结合实际程序项目的记录,如果你也对Rust感兴趣,那么我们可以一起交流探讨,使用Rust来构建程…...

html各个标签的使用

一、标签的分类 1、单标签和双标签 1. 单标签&#xff1a;<img> img br hr 2. 双标签&#xff1a;<div></div> div span <a></a> h p a 2、按照标签属性分类 1. 块标签&#xff1a;自己独占一行 h1~h6 p div 2. 行内(内联)标签 …...

android 混淆

# 指定代码的压缩级别 0 - 7(指定代码进行迭代优化的次数&#xff0c;在Android里面默认是5&#xff0c;这条指令也只有在可以优化时起作用。) -optimizationpasses 5 # 混淆时不会产生形形色色的类名(混淆时不使用大小写混合类名) -dontusemixedcaseclassnames # 指定不去忽略…...

旋转链表(C++解法)

题目 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&#xff1a; 输入&#xff1a;head [0,1,2], k 4 输出&#xff1a;[…...

AcWing 134:双端队列

【题目来源】https://www.acwing.com/problem/content/description/136/【题目描述】 达达现在碰到了一个棘手的问题&#xff0c;有 N 个整数需要排序。 达达手头能用的工具就是若干个双端队列。 她从 1 到 N 需要依次处理这 N 个数&#xff0c;对于每个数&#xff0c;达达能做…...

Spring Cloud Gateway 重写 URL

目录 1、简介 2、Spring Cloud Gateway 快速回顾 3、基于配置的 URL 重写 4、基于 DSL 的 URL 重写 5、测试 6、总结 1、简介 Spring Cloud Gateway 的常见用例是作为一个网关&#xff0c;代理一个或多个服务&#xff0c;从而为客户端提供更简单的消费方式。 本文将带你…...

【C语法学习】10 - scanf()函数

文章目录 0 前言1 函数原型2 参数2.1 格式字符串2.1.1 转换说明 2.2 参数列表 3 返回值4 读取机制4.1 基本概念4.2 转换说明4.3 读取过程4.4 读取示例4.5 多参数 6 示例6.1 示例16.2 示例26.3 示例36.4 示例4 0 前言 scanf()函数虽然使用起来较为灵活&#xff0c;但是其读取机…...

ffmpeg mp3截取命令,视频与mp3合成带音频视频命令

从00:00:03.500开始截取往后长度到结尾的mp3音频&#xff08;这个更有用&#xff0c;测试好用&#xff09; ffmpeg -i d:/c.mp3 -ss 00:00:03.500 d:/output.mp3 将两个音频合并成一个音频&#xff08;测试好用&#xff09; ffmpeg -i "concat:d:/c.mp3|d:/output.mp3&…...

文件夹还在,里面文件没了?问题这样解决

文件夹还在但文件无故消失怎么办&#xff1f;文件的消失对于我们来说可能是个令人沮丧且困惑的问题。有时候&#xff0c;我们可能会发现文件夹依然存在&#xff0c;但其中的文件却消失了。在这篇文章中&#xff0c;我们将探讨为什么电脑文件会无故消失的原因&#xff0c;并提供…...

使用 OpenCV 和 Tesseract OCR 进行车牌识别

您将了解自动车牌识别。我们将使用 Tesseract OCR 光学字符识别引擎(OCR 引擎)来自动识别车辆牌照中的文本。 Python-tesseract: Py-tesseract 是 Python 的光学字符识别 (OCR) 工具。也就是说,它将识别并“读取”图像中嵌入的文本。Python-tesseract 是 Google 的 Tessera…...

What exactly are the practices involved in DevOps?

目录 1. Continuous Integration (CI) 2. Continuous Deployment (CD) 3. Infrastructure as Code (IAC) 4. Configuration Management 5. Monitoring and Logging 6. Automated Testing 7. Collaboration and Communication 8. Microservices Architecture 9. Conta…...

Spring底层原理(五)

Spring底层原理(五) 本章内容 介绍Aware接口与InitializingBean接口、Bean的初始化与销毁、Scope Aware接口 作用:用于注入一些与容器相关的信息 类名作用BeanNameAware注入Bean的名称BeanFactoryAware注入BeanFactory容器ApplicationContextAware注入ApplicationContext容…...

算法的基本概念(数据结构与算法)

数据结构是指数据元素之间的关系和组织方式&#xff0c;在计算机科学中被广泛应用于存储和操作数据的方法和技术。 数据元素&#xff1a; 数据元素是构成数据的基本单位&#xff0c;可以是数字、字符、记录等。 数据项&#xff1a; 数据元素中的一个部分&#xff0c;表示一个属…...

高阶数据结构学习——LRU Cache

文章目录 1、了解LRU Cache&#xff08;Least Recently Used缩写&#xff09;2、代码实现 1、了解LRU Cache&#xff08;Least Recently Used缩写&#xff09; Cache是缓存&#xff0c;在磁盘和内存之间&#xff0c;内存和寄存器之间都存在&#xff0c;CPU和内存之间存在三级缓…...

代码冲突解决

远程仓库修改 本地代码修改 接下来我们push一下 如果使用IDE 冲突内容如下&#xff1a; 我们可以使用自带的工具进行修改 我们选择接受自己改动的即可 如果使用git工具怎么去处理呢 远程分支是这样 本地是这样的 add和commit之后&#xff0c;再pull&#xff0c;最后pus…...

c/c++程序的内存开辟时 的内存情况

我们写的代码都是要存放在内存空间中的&#xff0c;我们经常说堆区&#xff0c;静态区&#xff0c;还有栈区&#xff0c;相信很多人不是很明白&#xff0c;在今天这篇博客中让大家对它们有一个粗略的认识 1.栈区&#xff08;static&#xff09; 在执行函数时&#xff0c;函数内…...

【linux常用命令+vi编辑器_2023.11.3】

芯片开发 Linux/Unix&#xff08;环境&#xff09; EDA工具TCL&#xff08;波形&#xff09; SVN/GIT&#xff08;版本控制&#xff09; Makefile&#xff08;脚本语言&#xff09; Perl/Python&#xff08;脚本语言&#xff09; Vim/Gvim&#xff08;编辑器&#xff09; 命令…...

okhttp post请求 header post参数加密遇到的两个问题

如果你对于网络请求用了https后是否还有必要对参数加密有疑问可以看我上篇的文章&#xff1a;网络安全https 记得耐心看完&#xff0c;下面说问题&#xff1a; Caused by: java.lang.IllegalArgumentException: Unexpected char 0x0a 一开始以为是okhttp框架对特殊字符做了现在…...

什么是Webpack的loader和plugin?它们的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

ESXi for ARM 最新下载地址

由于VMware决定关闭 flings.vmware.com 网站&#xff0c;内容被迁移到不同的地方&#xff0c;网站跳转到 Code Samples and PowerCLI Example Scripts | VMware - VMware {code} ESXi for ARM的下载地址迁移到了 https://customerconnect.vmware.com/downloads/get-download?…...

2. 网络之网络编程

网络编程 文章目录 网络编程1. UDP1.1 DatagramSocket1.1.1 DatagramSocket 构造方法1.1.2 DatagramSocket 方法&#xff1a; 1.2 DatagramPacket1.2.1 DatagramPacket构造方法1.2.2 DaragramPacket方法1.2.3InetSocketAddress API 1.3 UDP回显服务器1.3.1 框架结构1.3.2 读取请…...