时间序列预测模型实战案例(一)深度学习华为MTS-Mixers模型

时间序列预测 — 时间序列预测研究综述 指对某种事物发展变化过程进行观测并按照一定频率采集得出的一组随机变量。就是从众多数据中挖掘出其蕴含的核心规律并且依据已知的因素对未来的数据做出准确的估计。由于大量物联网数据采集设备的接入、多维数据的爆炸增长和对预测精度的要求愈发苛刻,经典的参数模型以及传统机器学习算法难以满足预测任务的高效率和高精度需求。近年来,以卷积神经网络、循环神经网络和 Transformer模型为代表的深度学习算法在时间序列预测任务中取得了丰硕的成果。 阅读详情
AI摘要

本文介绍了时间序列预测模型,包括单元和多元时间序列预测及其常见模型。给出MTS - Mixers代码地址,详细讲解运行文件参数,修复代码中的bug。完成配置后可训练和预测,结果保存为npy文件。还提及后续将讲解其他时间序列预测模型。

目录

引言

下面来介绍一下时间序列预测模型

代码地址

代码讲解

第一部分:

第二部分

开始训练

开始预测


 

 

引言

上来不多说先上预测精度图,所有的预测都是基于未知的数据进行预测并不是训练预测!MAE的误差大概在0.4左右,数据是基于过去一年的进行模型的训练,输入的数据有许多列后面我也会进行讲解如何使用该模型进行预测你们自己的数据。

f131118d007140209096cc2f6c879d5e.png

 

 

介绍一下时间序列预测模型

首先我们要对时间序列概念有一个基本的了解

时间序列预测大致分为两种一种是单元时间序列预测另一种是多元时间序列预测

单元时间序列预测是指只考虑一个时间序列的预测模型。它通常用于预测单一变量的未来值,例如股票价格、销售量等。在单元时间序列预测中,我们需要对历史数据进行分析,确定趋势、季节性和周期性等因素,并使用这些因素来预测未来的值。

常见的单元时间序列预测模型有

  1. 移动平均模型(MA)
  2. 自回归模型(AR)
  3. 自回归移动平均模型(ARMA)
  4. 差分自回归移动平均模型(ARIMA)
  5. 季节性自回归移动平均模型(SARIMA)
  6. 季节性分解模型(STL)
  7. 傅里叶分析模型(FFT)

多元时间序列预测是指考虑多个时间序列的预测模型。它通常用于预测多个相关变量的未来值,例如股票价格和市场指数等。在多元时间序列预测中,我们需要对每个变量的历史数据进行分析,并确定它们之间的关系。然后,我们可以使用这些关系来预测未来的值。

常见的多元时间序列预测模型有

  1. 向量自回归模型(VAR)
  2. 向量自回归移动平均模型(VARMA)
  3. 向量误差修正模型(VEC)
  4. 动态因子模型(DFM)
  5. 多变量时间序列模型(MTS)
  6. 状态空间模型(SSM)

总的来说,单元时间序列预测和多元时间序列预测都是非常有用的预测模型,可以帮助我们更好地了解和预测未来的趋势。

但是如今多元时间序列预测在各种场景中越来越受欢迎,如电力预测、天气预测和交通流量估计。随着计算资源和模型架构的扩展,深度学习技术,包括基于rnn的模型和基于cnn的模型,已经取得了比传统统计方法更好的预测性能。由于捕获长期依赖关系,Transformer最近被用于捕获时间序列预测中的长期时间相关性,并显示出有希望的结果。

 

代码地址

MTS-Mixers的代码包含许多个模块和许多个部分组成,所以我下面的讲解只会讲解其中的关键部分也会修改其中的一些BUG地方,代码地址推荐大家到Github上面进行下载,下面附上超链接地址点击即可跳转

点击此处

(提示:加载进去以后点击右上角的code下面的download zip 即可下载到本地)

如果点击不开多点击几遍即可如果实在点击不开我后期会发布百度网盘版本下载链接方式(我自己已经调试好bug之后的代码点击即可运行结果)

提供给大家优快云的下载地址

时间序列预测模型实战案例深度学习华为MTS-Mixers模型资源-优快云文库

 

代码讲解

首先先对其运行文件(程序的入口)进行一个讲解包括其中的参数含义,如果你想要提高预测精度就要对这些参数要有一个具体的理解,否则光运用模型而不去调节其中的超参数效果不一定会好!    

parser.add_argument('--is_training', type=int,  default=1, help='status')
parser.add_argument('--model', type=str,  default='Transformer',
                    help='model name, options: [Transformer, Linear, NLinear, DLinear, SCINet, ConvFC, MTSMixer, MTSMatrix, FNet]')

# data loader
parser.add_argument('--data', type=str,  default='custom', help='dataset type')
parser.add_argument('--root_path', type=str, default='./', help='root path of the data file')
parser.add_argument('--data_path', type=str, default='sum14.csv', help='data file')
parser.add_argument('--features', type=str, default='MS',
                    help='forecasting task, options:[M, S, MS]; M:multivariate predict multivariate, S:univariate predict univariate, MS:multivariate predict univariate')
parser.add_argument('--target', type=str, default='sl', help='target feature in S or MS task')
parser.add_argument('--freq', type=str, default='t',
                    help='freq for time features encoding, options:[s:secondly, t:minutely, h:hourly, d:daily, b:business days, w:weekly, m:monthly], you can also use more detailed freq like 15min or 3h')
parser.add_argument('--checkpoints', type=str, default='./checkpoints/', help='location of model checkpoints')

# forecasting task
parser.add_argument('--seq_len', type=int, default=128, help='input sequence length')
parser.add_argument('--label_len', type=int, default=64, help='start token length')
parser.add_argument('--pred_len', type=int, default=4, help='prediction sequence length')

parser.add_argument('--individual', action='store_true', default=False,
                    help='DLinear: a linear layer for each variate(channel) individually')
parser.add_argument('--seg', type=int, default=20, help='prediction plot segments')
parser.add_argument('--rev', action='store_true', default=False, help='whether to apply RevIN')
parser.add_argument('--norm', action='store_false', default=True, help='whether to apply LayerNorm')
parser.add_argument('--fac_T', action='store_true', default=False,
                    help='whether to apply factorized temporal interaction')
parser.add_argument('--sampling', type=int, default=2,
                    help='the number of downsampling in factorized temporal interaction')
parser.add_argument('--fac_C', action='store_true', default=False,
                    help='whether to apply factorized channel interaction')
parser.add_argument('--refine', action='store_true', default=False, help='whether to refine the linear prediction')
parser.add_argument('--mat', type=int, default=0, help='option: [0-random, 1-identity]')

# model 
parser.add_argument('--embed_type', type=int, default=0,
                    help='0: default 1: value embedding + temporal embedding + positional embedding 2: value embedding + positional embedding')
parser.add_argument('--enc_in', type=int, default=5, help='encoder input size')
parser.add_argument('--dec_in', type=int, default=5, help='decoder input size')
parser.add_argument('--c_out', type=int, default=1, help='output size')
parser.add_argument('--d_model', type=int, default=512, help='dimension of model')
parser.add_argument('--n_heads', type=int, default=1, help='num of heads')
parser.add_argument('--e_layers', type=int, default=2, help='num of encoder layers')
parser.add_argument('--d_layers', type=int, default=1, help='num of decoder layers')
parser.add_argument('--d_ff', type=int, default=2048, help='dimension of fcn')
parser.add_argument('--moving_avg', type=int, default=24, help='window size of moving average')
parser.add_argument('--factor', type=int, default=1, help='attn factor')
parser.add_argument('--dropout', type=float, default=0.05, help='dropout')
parser.add_argument('--embed', type=str, default='timeF',
                    help='time features encoding, options:[timeF, fixed, learned]')
parser.add_argument('--activation', type=str, default='gelu', help='activation')
parser.add_argument('--output_attention', action='store_true', help='whether to output attention in ecoder')
parser.add_argument('--do_predict', action='store_true',default='True', help='whether to predict unseen future data')

# optimization
parser.add_argument('--num_workers', type=int, default=0, help='data loader num workers')
parser.add_argument('--itr', type=int, default=1, help='experiments times')
parser.add_argument('--train_epochs', type=int, default=10, help='train epochs')
parser.add_argument('--batch_size', type=int, default=16, help='batch size of train input data')
parser.add_argument('--patience', type=int, default=3, help='early stopping patience')
parser.add_argument('--learning_rate', type=float, default=0.001, help='optimizer learning rate')
parser.add_argument('--loss', type=str, default='mse', help='loss function')
parser.add_argument('--lradj', type=str, default='type1', help='adjust learning rate')
parser.add_argument('--use_amp', action='store_true', help='use automatic mixed precision training', default=False)

# GPU
parser.add_argument('--use_gpu', type=bool, default=True, help='use gpu')
parser.add_argument('--gpu', type=int, default=0, help='gpu')
parser.add_argument('--use_multi_gpu', action='store_true', help='use multiple gpus', default=False)
parser.add_argument('--devices', type=str, default='0,1,2,3', help='device ids of multile gpus')
parser.add_argument('--test_flop', action='store_true', default=False, help='See utils/tools for usage')

首先其中的参数部分分为几个部分分别为

(dataloader,forecasting task,model,optimization, gpu)

分为以上五个部分,我会选择其中的一些关键参数进行讲解

 

 

第一部分:

parser.add_argument('--is_training', type=int,  default=1, help='status')

这就是决定是否训练,输入的类型必须是整数类型,则会进行训练!

parser.add_argument('--model', type=str,  default='Transformer',
                    help='model name, options: [Transformer, Linear, NLinear, DLinear, SCINet, ConvFC, MTSMixer, MTSMatrix, FNet]')

这一部分是模型的选择,首先这个模型是由一遍论文发布出来的,所以他肯定要有一些对比的模型来衬托自己模型的优点,所以这一部分是进行一个模型的选择,其中Transformer就是本文所提到的模型MTS-Mixers,后面的模型分别为

  1. Linear模型:是一个线性模型,适用于简单的线性回归问题。

  2. NLinear模型:是一个非线性模型,可以处理非线性问题,例如多项式回归。

  3. DLinear模型:是一个动态线性模型,可以处理随时间变化的线性关系。

  4. SCINet模型:是一个基于自注意力机制的神经网络模型,可以用于时间序列预测和其他相关任务。

  5. ConvFC模型:是一个卷积神经网络和全连接神经网络的结合体,可以处理时间序列数据。

  6. MTSMixer模型:是一个基于多头自注意力机制的神经网络模型,可以用于时间序列预测和其他相关任务。

  7. MTSMatrix模型:是一个基于矩阵分解的神经网络模型,可以处理多变量时间序列数据。

  8. FNet模型:是一个基于傅里叶变换的神经网络模型,可以用于时间序列预测和其他相关任务。

注意这些模型呢其实都各有各自的优点如果本文的模型并没有给你一个好的精度对于你的数据你可以换着其它的模型进行尝试,总之时间序列预测并不是越复杂的模型越好,所有的选择都要基于你的数据进行选择。 

parser.add_argument('--data', type=str,  default='custom', help='dataset type')

这一部分是重点

如果你想要运行你自己的数据进行预测,这里需要根据你自己的数据进行输入! 

首先我们来看下面的图!

8f222f17e492449f9525086670ae3921.png

如果你想要预测自己的数据则需要在参数部分的default位置后面输入custom,其中的ETTh1 ETTh2 ETTm1 ETTm2 是官方的数据集进行预测的选项,当大家没有数据的时候可以用来进行预测,我也会把官方数据提供给大家进行预测,

所以如果你用的是ETTh1数据集就可以填写ETTh1即可,如果你用的是自己的数据集进行预测就填入custom即可!!

这里我放一下官方的数据集标题给大家先看一下

6f59ad8ec0d9472bbcf13f7ef8a45bac.png

官方的数据集就是这个形式是用前七列数据进行预测第八列数据OT,当然也可以将每一列的结果全部预测出来!

 

parser.add_argument('--root_path', type=str, default='./', help='root path of the data file')
parser.add_argument('--data_path', type=str, default='sum14.csv', help='data file')

这两个参数没什么好讲的就是你文件的目录所在,你的输入必须是csv格式的文件,如果你的是excel文件格式可以用Pandas转一下或者用Excel自带的功能转存一下, 像我的文件就存在和当前运行文件的同级目录下面!

 

parser.add_argument('--features', type=str, default='MS',
                    help='forecasting task, options:[M, S, MS]; )

重点参数!

这一个参数代表着你预测的方式,其中选项有三个分别是[M, S, MS],

情况一:假如你是做单变量预测,就是你的时间序列数据只包含两列一列是时间一列是你预测的数据, 那就填写S!

情况二:假如你的数据是多元时间序列预测,那么就可以有两个选项一个是M另一个是MS

我先介绍一下英文就是Multi-single和Multi-Multi,其中M和MS就是他们的首字母,所以大家应该理解是什么意思了,M就是多元变量预测多元变量,拿官方的数据举例,假设有7个变量你想要预测其中的几列数据就是多元预测多元,

情况三:MS就是多元预测单元数据,假设你输入的数据是7列输出的数据是一列数据就是多元预测单元也就是MS(Multi-single)

 

6f59ad8ec0d9472bbcf13f7ef8a45bac.png

举例:我将以上图片所看到的数据全部输入的模型内部,得到OT一列的未来情况就填写MS!

 

parser.add_argument('--target', type=str, default='OT', help='target feature in S or MS task')

这一参数,就是代表你在上一个参数所讲的时候填写的是MS或者M就需要填写,我们这里还拿官方的数据举例,类似于假设你输入的是MS在上一个features参数的位置,那么这一参数就需要填写你数据当中需要被预测出来的列明标题,类似于官方的数据我们想要预测的是OT这一列这里我们就填写OT,假设你想要预测HUFL这一列数据就填写HUFL即可!

 

parser.add_argument('--freq', type=str, default='t',
                    help='freq for time features encoding, options:[s:secondly, t:minutely, h:hourly, d:daily, b:business days, w:weekly, m:monthly], 

这一参数就代表你时间信息那一列,因为大家做的是时间序列模型如果没有时间的一列怎么能叫时间序列预测呢?所以这一列可选的参数有[s:secondly, t:minutely, h:hourly, d:daily, b:business days, w:weekly, m:monthly]

其中的含义就像英文所述的那样我就不在赘述了,下面来讲一下怎么填写

假设你的两列数据当中的间隔是分钟你就填写(t)如果是小时你就填写(h)以此类推,假设你的间隔不是一分钟或者一小时你也可以在他们前面加上系数,类似于15t这种,2h这种

 

parser.add_argument('--seq_len', type=int, default=128, help='input sequence length')
parser.add_argument('--label_len', type=int, default=64, help='start token length')
parser.add_argument('--pred_len', type=int, default=4, help='prediction sequence length')

提高精度的重点!

这三个参数拿到一起来讲,其中seq_len,label_len,pred_len是相互之间有影响的,

其中seq_len就是你输入到模型内部一个长度,类似于你用过去128条数据去预测未来一个数据,那么这里就填写128!

当然这个数字不能够随便填写,往往和你模型的周期性和季节性有一定的关系,类似于sin或者cos函数一样有周期性,我们如果想预测未来的一个数据当然最好选够一个周期的长度数据来预测,这样就能包含所有的特征预测的往往也就更准,

当然如果你不知道怎么看你数据的周期性对数据进行一个建模的操作去分析可以在评论区留言后期我会教大家用EXCEL或者Matplotlib或者PoewBi进行一些简单的建模操作从而分析其中的周期性季节性等因素!

label_len就代表一个预测的预热性质你也可以理解为权重更大的部分这样更好的理解一点,可以这样想对于一个时间序列来说,肯定越新的数据越有价值,所以这个数据可以理解为更注重那一部分的数据(这只是为了让大家更好的理解这么解释),如果不理解可以看一下下面的图。 

12282564a357499cad7fa278cec4d200.png

pred_len就好理解了你想预测未来多少个时间段的数据就填写几即可,

 在我的数据当中我填写的是seq_len=128,label_len=64,pred_len=4,可以供大家参考,其中每一个参数都要相互影响如果大家想得到一个好的精度就要多做实验多尝试!

Model参数部分

parser.add_argument('--enc_in', type=int, default=7, help='encoder input size')
parser.add_argument('--dec_in', type=int, default=7, help='decoder input size')
parser.add_argument('--c_out', type=int, default=1, help='output size')

这三个参数就代表着编码器和解码器的部分,

6f59ad8ec0d9472bbcf13f7ef8a45bac.png

我们拿MS来举例,假设你输入的数据就是官方的数据,其中OT是你需要预测的列数,那么你的特征就是其余的7列数据,目标列就是OT列,那么enc_in和dec_in就填写7,c_out就填写1 

如果你填写的是M那么就输入到少列数据就填写多少数字即可,S的话就都是1即可

参数部分我就讲到这里,因为参数实在是太多了,用文字的形式很难全部叙述出来如果大家有需要想更深入的了解,可以评论区留言,我也会进行回复解答!

这里修复一个小BUG(算是吧)

parser.add_argument('--do_predict', action='store_true', help='whether to predict unseen future data')

 这一个参数就是进行是否进行预测的选项,需要在action后面增加一个参数,让其进行预测否则光训练模型在验证集和测试集评估没有意义,default='True'在action参数后面添加即可预测未知的数据!

 

 

第二部分

 

这个官方模型最大的问题在于他输入的数据进行了归一化处理,而对于预测出来的数据并没有归一化处理,假设我们输入的数据是1000,那经过StandardScaler()处理以后,变为一个深度学习模型比较喜欢的数字范围(-1,-1)之间,那么他在预测输出的时候也是(-1,-1)之间的一个数字,并没有反转化为我们1000附近的数据,所以这算一个很大的bug,类似于下图。

b762b16f6bad4306a5aee1371daf8beb.png

如何修复这一个bug呢,

在data_provider文件目录下面的data_loader文件下面我们需要修改一部分内容,

736045ff53b942d187eceae07c22c77f.png

在Class类名为class Dataset_Pred(Dataset):下面将下面这段代码复制粘贴替代他后面的所有代码,(注意只替换方法read_data以后的代码) 

    def __read_data__(self):
        self.scaler = StandardScaler()
        self.y_scaler = StandardScaler()
        df_raw = pd.read_csv(os.path.join(self.root_path,
                                          self.data_path))  # 因为进行预测的时候代码会自动帮我们取最后的数据我们只需做好拼接即可

        '''
        df_raw.columns: ['date', ...(other features), target feature]
        '''
        if self.cols:
            cols = self.cols.copy()
            cols.remove(self.target)
        else:
            cols = list(df_raw.columns)
            cols.remove(self.target)
            cols.remove('date')
        df_raw = df_raw[['date'] + cols + [self.target]]
        border1 = len(df_raw) - self.seq_len
        border2 = len(df_raw)

        if self.features == 'M' or self.features == 'MS':
            cols_data = df_raw.columns[1:]
            df_data = df_raw[cols_data]
        elif self.features == 'S':
            df_data = df_raw[[self.target]]

        if self.scale:
            self.scaler.fit(df_data.values[:, :4])
            self.y_scaler.fit(df_data.values[:, 4:5])
            data1 = self.scaler.transform(df_data.values[:, :4])
            data2 = self.y_scaler.transform(df_data.values[:, 4:5])
            data = np.concatenate((data1,data2), axis=1)

        else:
            data = df_data.values

        tmp_stamp = df_raw[['date']][border1:border2]
        tmp_stamp['date'] = pd.to_datetime(tmp_stamp.date)
        pred_dates = pd.date_range(tmp_stamp.date.values[-1], periods=self.pred_len + 1, freq=self.freq)

        df_stamp = pd.DataFrame(columns=['date'])
        df_stamp.date = list(tmp_stamp.date.values) + list(pred_dates[1:])
        if self.timeenc == 0:
            df_stamp['month'] = df_stamp.date.apply(lambda row: row.month, 1)
            df_stamp['day'] = df_stamp.date.apply(lambda row: row.day, 1)
            df_stamp['weekday'] = df_stamp.date.apply(lambda row: row.weekday(), 1)
            df_stamp['hour'] = df_stamp.date.apply(lambda row: row.hour, 1)
            df_stamp['minute'] = df_stamp.date.apply(lambda row: row.minute, 1)
            df_stamp['minute'] = df_stamp.minute.map(lambda x: x // 15)
            data_stamp = df_stamp.drop(['date'], 1).values
        elif self.timeenc == 1:
            data_stamp = time_features(pd.to_datetime(df_stamp['date'].values), freq=self.freq)
            data_stamp = data_stamp.transpose(1, 0)

        self.data_x = data[border1:border2]
        if self.inverse:
            self.data_y = df_data.values[border1:border2]
        else:
            self.data_y = data[border1:border2]
        self.data_stamp = data_stamp

    def __getitem__(self, index):
        s_begin = index
        s_end = s_begin + self.seq_len
        r_begin = s_end - self.label_len
        r_end = r_begin + self.label_len + self.pred_len

        seq_x = self.data_x[s_begin:s_end]
        if self.inverse:
            seq_y = self.data_x[r_begin:r_begin + self.label_len]
        else:
            seq_y = self.data_y[r_begin:r_begin + self.label_len]
        seq_x_mark = self.data_stamp[s_begin:s_end]
        seq_y_mark = self.data_stamp[r_begin:r_end]

        return seq_x, seq_y, seq_x_mark, seq_y_mark

    def __len__(self):
        return len(self.data_x) - self.seq_len + 1

    def inverse_transform(self, data):
        return self.y_scaler.inverse_transform(data.detach().cpu().numpy())

 在上面的代码部分主要我将两部分的标准化分开处理了,

ea39e59b67af4507b9b08964b7233bdc.png

        if self.scale:
            self.scaler.fit(df_data.values[:, :4])
            self.y_scaler.fit(df_data.values[:, 4:5])
            data1 = self.scaler.transform(df_data.values[:, :4])
            data2 = self.y_scaler.transform(df_data.values[:, 4:5])
            data = np.concatenate((data1,data2), axis=1)

其中主要修改了这两部分,因为假设前面的features部分输入的是MS这里就可以这么修改,

其中的4就是你输入数据的特征数我们还拿官方的数据举例,

6f59ad8ec0d9472bbcf13f7ef8a45bac.png

其中前七列是特征数,OT列是要预测的列,那么在

             

           self.scaler.fit(df_data.values[:, :4])
           self.y_scaler.fit(df_data.values[:, 4:5])

其中fit将数字替换为6,后面的替换为6:7即可,因为我们的索引数字从0开始所以这么替换,所以这里其实就是和你前面的编码器和解码器部分的数字是相关的,前面在

        data1 = self.scaler.transform(df_data.values[:, :4])
        data2 = self.y_scaler.transform(df_data.values[:, 4:5])

同理按上面进行替换, 即可,

修改完上面之后我们就只需修改最后一部分即可

8139fb58382748f59a91efb386f9d0ef.png

 

在exp_main.py文件当中我们添加一行代码即可 

我们在最后一个方法predict下面,大约在280行左右进行一个替换

                    outputs = outputs[0]
                    outputs = data_set.inverse_transform(outputs)
                    preds.append(outputs)
                    print(preds)
            preds = np.array(preds)
            preds = preds.reshape(-1, preds.shape[-2], preds.shape[-1])

将这段代码进行一个替换即可替换之后的代码长下面这个样子

                with torch.no_grad():
                for _, (batch_x, batch_y, batch_x_mark, batch_y_mark) in enumerate(pred_loader):
                    batch_x = batch_x.float().to(self.device)
                    batch_y = batch_y.float()
                    batch_x_mark = batch_x_mark.float().to(self.device)
                    batch_y_mark = batch_y_mark.float().to(self.device)

                    dec_inp = torch.zeros([batch_y.shape[0], self.args.pred_len, batch_y.shape[2]]).float().to(batch_y.device)
                    dec_inp = torch.cat([batch_y[:, :self.args.label_len, :], dec_inp], dim=1).float().to(self.device)

                    if self.args.use_amp:
                        with torch.cuda.amp.autocast():
                            if self.args.model in non_transformer:
                                outputs = self.model(batch_x)
                            else:
                                outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)[0] if self.args.output_attention else \
                                    self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)
                    else:
                        if self.args.model in non_transformer:
                            outputs = self.model(batch_x)
                        else:
                            outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)[0] if self.args.output_attention else \
                                self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)
                    outputs = outputs[0]
                    outputs = data_set.inverse_transform(outputs)  
                    preds.append(outputs)
                    print(preds)
                    results.append(outputs[0][0])
            preds = np.array(preds)
            preds = preds.reshape(-1, preds.shape[-2], preds.shape[-1])

            # result save
            folder_path = './results/' + setting + '/'
            if not os.path.exists(folder_path):
                os.makedirs(folder_path)

            np.save(folder_path + 'real_prediction.npy', preds)

 

 

开始训练

经过上面的处理之后我们的模型已经可以正常的开始训练和预测和测试了,

我们将所有的文件配置好之后就可以运行run.py文件

80e1776b911145129dbce67179276b39.png

d44c1ba686e14c07b76e13a7b2ab80ab.png

运行之后稍等控制台就可以看到模型已经开始训练了,等待时间跟你数据有关,其中有一部分代码变更部分我并没有讲因为涉及到公司的业务逻辑!但是并不影响你们进行正常的预测。

 

 

 

开始预测

当训练完成之后模型就会自动进行预测,控制台会打印输出结果,并且预测结果会保存到同级目录下面

9505dad574d8431f926494b3823b431a.png

在同级目录result下面会保存每次预测的结果,因为其保存的结果是npy的格式文件需要numpy进行一个转化才可以读取其内容,可以另建一个py文件输入如下代码即可打印结果!

import numpy as np

forecast = np.load('results/Transformer_sum14_ftMS_sl64_ll32_pl4_dm512_nh1_el2_dl1_df2048_fc1_ebtimeF_0/real_prediction.npy')

print(forecast)

 其中的地址要换成你自己的即可,其实也可以将结果转化为外部的excel或者csv格式通过Dataframe格式然后进行转化即可,有需求可以评论区留言我也会进行更新,

我是通过转到csv文件通过excel作了一些数据可视化的工作,下面展示一下结果

预测值和真实值折线图

1a9bb768833d4b21aecd273e5ef0db1b.png

 

这是我做得两个预测不同的目标的预测结果大家可以参考一下,

MAE损失的截图

9c15a66e65094916b6bf513958b6ff8e.png

后期我也会讲一些最新的预测模型包括Informer,TPA-LSTM,ARIMA,XGBOOST,Holt-winter,移动平均法等等一系列关于时间序列预测的模型,包括深度学习和机器学习方向的模型我都会讲,你可以根据需求选取适合你自己的模型进行预测,如果有需要可以+个关注,包括本次模型我自己的代码大家有需要我也会放出百度网盘下载链接!!

其它时间序列预测模型讲解

----------------------------------------------------------Holt-Winters------------------------------------------------------

时间序列预测模型实战案例(二)(Holt-Winter)(Python)结合K-折交叉验证进行时间序列预测实现企业级预测精度(包括运行代码以及代码讲解)

----------------------------------------------------------LSTM---------------------------------------------------------------

时间序列预测模型实战案例(三)(LSTM)(Python)(深度学习)时间序列预测(包括运行代码以及代码讲解)

如果大家有不懂的也可以评论区留言一些报错什么的大家可以讨论讨论看到我也会给大家解答如何解决!

 

 

 

一文搞懂Python时间序列预测(步骤,模板,python代码) 预测包括,数值拟合,线性回归,多元回归,时间序列,神经网络等等对于单变量的时间序列预测:模型有AR,MA,ARMA,ARIMA,综合来说用ARIMA即可表示全部。 阅读详情

相关推荐

时间序列预测模型实战案例深度学习华为MTS-Mixers模型

首先我们要对时间序列概念有一个基本的了解时间序列预测大致分为两种一种是单元时间序列预测另一种是多元时间序列预测单元时间序列预测是指只考虑一个时间序列的预测模型。它通常用于预测单一变量的未来值,例如股票价格、销售量等。在单元时间序列预测中,我们需要对历史数据进行分析,确定趋势、季节性和周期性等因素,并使用这些因素来预测未来的值。常见的单元时间序列预测模型有移动平均模型(MA)自回归模型(AR)自回归移动平均模型(ARMA)差分自回归移动平均模型(ARIMA)后期我也会讲一些最新的预测模型包括Informer,TPA-LSTM,ARIMA,XGBOOST,Holt-winter,移动平均法等等一系列关于时间序列预测的模型,包括深度学习和机器学习方向的模型我都会讲,你可以根据需求选取适合你自己的模型进行预测,如果有需要可以+个关注。

MTS-Mixers(含代码)

MTS-Mixers 是一种用于多元时间序列预测的新颖且灵活的框架,通过因子化时间和通道混合来捕捉数据中的复杂依赖关系。整个框架包括多种实现,利用注意力机制、随机矩阵操作和因子化 MLP 来有效地处理时间和通道依赖关系,同时降低计算复杂性。本研究在十个基准测试上进行了实验,并在预测任务中取得了令人满意的性能。

332295505的博客 2935

深度学习-时间序列预测模型实战案例MTS-Mixers模型python代码.zip

深度学习-时间序列预测模型实战案例MTS-Mixers模型python代码.zip

15种时间序列预测方法总结(包含多种方法代码实现)

本文提供时间序列分析从基本概念到模型的讲解,利用python和pytroch实现代码讲解,文章内既包括简单的代码示例又包括复杂的代码讲解部分,也有精美的图片的形式帮助读者更加快速的理解时间序列,本文涉及到的内容有机器学习和深度学习部分也包括一部分强化学习内容,文章内还含有运行结果的展示,同时提供给大家数据集进行操作,可以说时间序列类的任务你只看这一篇文章就足够了,总之无论你是数据科学新手,还是寻求进一步提升你的时间序列预测技能的专业人士,这篇博客都将为你提供宝贵的知识和经验。

Snu77的博客 11万+

基于PyTorch的MTS-Mixers代码

这是MTS-Mixers的官方实现:通过因子化时间和通道混合进行多元时间序列预测。 使用: 1. 安装 Python 版本不低于 3.6,PyTorch 版本不低于 1.5.0。 2. 运行 pip install -r requirements.txt 3. 下载数据并将 .csv 文件放在 ./dataset 文件夹中。您可以从 Google Drive 获取所有基准测试数据。所有数据集都经过了良好的预处理,可以轻松使用。 4. 训练模型。我们在 script.md 中提供了一个运行所有基准测试脚本的示例。如果需要,您可以更改任何超参数。请查看 run.py 以了解有关超参数配置的更多详细信息。 引用:Li Z, Rao Z, Pan L, et al. MTS-Mixers: Multivariate Time Series Forecasting via Factorized Temporal and Channel Mixing[J]. arXiv preprint arXiv:2302.04501, 2023.

C# Log4Net不输出日志的解决方法

log4net是一个流行的 .NET 日志框架,用于记录应用程序的日志信息。如果你在使用log4net。

精通搬砖,资深技术砖家,在软件开发,人工智能,设备开发等领域都有研究。商业合作&交流学习可私信联系。 1263

【论文阅读】2023 MTS-Mixers

论文提出了MTS混合器,它使用两个因子化模块来捕获时间和信道相关性。在多个真实数据集上的实验结果表明,MTS Mixer以更高的效率优于现有的基Transformer的模型。

mataolc的博客 2779

Vector CANape的使用记录

开门见山,今天想和大家分享下CANape使用记录,分享原因是近日在分析一个客户现场软件问题时,需要用到CANape来实时的监测ECU中的变量,因为之前对CANape的认知比较浅薄,觉得标定工程师使用他就足够了,但是这次的经历让我觉得需要写点东西来记录自己学习并使用他的过程。 相信大家能够在网上找到很多关于如何使用CANape的文章,从新建Project到后面的数据的观测、保存以及回放等等。我个人也是这么学过来的,所以我不打算从那些方面着手去描述如何使用她,而是根据我的真实使用过程以及问题的抛出到如何解决来

2万+

推荐开源项目:MTS-Mixers——多变量时间序列预测的因子化时空混合器

推荐开源项目:MTS-Mixers——多变量时间序列预测的因子化时空混合器 去发现同类优质开源项目:https://gitcode.com/ 在现代数据科学中,时间序列预测是不可或缺的一部分,尤其在诸如能源消耗预测、股票市场趋势分析和天气预报等领域。今天我们要向您推荐一个创新的开源项目——MTS-Mixers。这个项目基于《MTS-Mixers: 多变量时间序列预测通过因子化时空混合》这篇论文的官...

gitblog_00092的博客 1068

matlab 图像合成_用MATLAB做图像合成加雾的处理

众所周知,现在有很多算法来对图像进行去雾处理。但反之,对图像增加雾的代码却少之又少,本代码就是基于Matlab实现了对图像的加雾处理。该方法可用做雾天图像的合成数据集。代码公开如下:clear;clc; % close all; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%i...

weixin_35719260的博客 2060

Arxml文件可视化:当汽车软件架构遇见交互式设计工具

本文探讨了Arxml文件在汽车电子系统开发中的可视化技术,结合AUTOSAR架构标准,介绍了如何利用现代Web技术构建交互式设计工具。通过三维组件关系图、动态数据流展示和版本对比等功能,显著提升开发效率和架构评审质量,适用于MBD和ASPICE开发流程。

weixin_29050271的博客 429

华为2023年多元时间序列预测模型(MTS-Mixers)

向AI转型的程序员都关注了这个号????????????华为在这2023年2月9日发布了一篇关于多元时间序列预测的文章,借鉴了NLP中前一阵比较热的Mixer模型,取代了Attention结构,不仅实现了效果上的提升,而且还实现了效率上的提高。1. Transformer的探讨Transformer在时间序列预测中的作用最近得到非常广泛地探讨。下图为经典Transformer时间序列预测模型图。Transforme...

datayx的文章 1280

Simulink中搭建一个包含PID控制的压电陶瓷快反镜模型

这个模型只是一个基本框架,你需要根据实际的系统参数和要求进行调整和优化。希望这些步骤能帮助你在Simulink中搭建所需的模型。

AI的内部世界的博客 474

时间序列预测模型怎么选?从ARIMA到华为MTS-Mixers,我帮你做了份横向评测

本文全面评测了从传统ARIMA到华为MTS-Mixers等时间序列预测模型,构建统一评测框架对比预测精度、训练效率和资源消耗。华为MTS-Mixers在多元预测任务中表现卓越,展现出混合编码架构和轻量设计的优势。针对金融、工业和零售等不同场景,提供了具体的选型建议和优化技巧,帮助技术决策者做出明智选择。

weixin_30328063的博客 297

在 Linux 64 位上遭遇 Signal 7 与 ST22 SYSTEM_CORE_DUMPED:从 es/implementation=std 到 /dev/shm 的一套落地排查法

摘要: Linux 64位SAP系统出现SYSTEM_CORE_DUMPED和Signal 7(对应SIGBUS)错误时,核心问题是进程因共享内存资源异常被操作系统强制终止。主要诱因包括:1)/dev/shm的tmpfs默认容量不足(通常仅占物理内存50%);2)SAP内存管理参数es/implementation=map在64位场景下的敏感性。解决方案应双管齐下:在实例profile设置es/implementation=std以降低对tmpfs的依赖,同时按(RAM+Swap)*75%重新配置/dev/

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 164

31、贝叶斯认知建模与隐马尔可夫模型:理论与应用

本文探讨了贝叶斯认知建模与隐马尔可夫模型(HMM)的理论基础及其在认知科学和神经科学中的应用。重点介绍了贝叶斯定理和HMM的基本原理,并结合实际案例,如情绪识别系统和脑机接口(BCI),展示了它们在处理复杂认知任务中的强大能力。文章还讨论了贝叶斯-HMM模型的技术细节、优势与局限性,以及未来的发展前景。

meat5的博客 141

华为2023年提出的多元时间序列预测模型(MTS-Mixers)

华为在这2023年2月9日发布了一篇的多元时间序列预测的文章,借鉴了NLP中前一阵比较热的Mixer模型,取代了Attention结构,不仅实现了效果上的提升,而且还实现了效率上的提高。

332295505的博客 1万+

深度学习部署工程师--8(docker+TensorRT部署到推理+yolo人体检测)下

本文介绍了YOLOv5模型通过TensorRT加速部署的流程。首先需要检查磁盘空间,确保有30GB可用。文章对比了不同推理方式的性能:int8量化+TensorRT可达200FPS,纯TensorRT为166FPS,CUDA为147FPS,CPU为93FPS。重点讲解了将YOLOv5模型导出为ONNX格式的详细步骤,包括修改Detect类的forward方法以移除decode运算,使用git patch批量修改代码,安装必要的依赖库,以及通过onnx-graphsurgeon工具修改ONNX图结构。最终目标

FJN110的博客 495

时间序列预测模型(非常详细),零基础入门到精通,看这一篇就够了

时间序列预测模型(非常详细),零基础入门到精通,看这一篇就够了

Java癫疯的博客 9999

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

时间序列预测方法概述

时间序列预测是数据分析的一个重要领域,涉及对未来事件的预测,基于过去的数据点。以下是几种常用的时间序列预测方法,包括其原理、优缺点。

m0_59257547的博客 4441

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

时间序列预测:8种最有效的方法

1.背景介绍 时间序列分析是一种处理和分析随时间变化的数据的方法。时间序列预测是一种利用过去数据预测未来数据的方法。在现实生活中,时间序列预测应用非常广泛,例如财务预测、商业预测、天气预报、电力负荷预测等。 在本文中,我们将介绍8种最有效的时间序列预测方法,并详细讲解它们的算法原理、数学模型和实例代码。 2.核心概念与联系 在进入具体的方法之前,我们需要了解一些核心概念: 时间序列:是...

AI天才研究院 3984

【时间序列预测】—全教程

时间序列预测

weixin_47082769的博客 5163

时间序列的7种预测模型

背景 时间序列问题比较常见,比如股市,工业生产指标等。 1 朴素估计 使用最后一个时间点的值估测后面一段时间段的值。 2 简单平均 4 滑动窗平均 使用之前一定大小时间段的平均值作为这个时间点的值。 或者使用加权的滑动窗平均: y_hat_avg = test.copy() y_hat_avg['moving_avg_forecast'] = train['Count'].rollin...

rosefun96的博客 19万+

时间序列预测

简单来说,时间序列是按照时间顺序,按照一定的时间间隔取得的一系列观测值,比如我们上边提到的国内生产总值,消费者物价指数,利率,汇率,股票价格等等。时间间隔可以是日,周,月,季度,年等。那么如何进行时间序列分析分析呢?通常来说我们尝试找出序列值在过去所呈现的特征模式,假定这种模式在未来能够持续,进而对未来进行预测。。

weixin_54385104的博客 3666

时间序列-预测(Forcasting):时间序列预测算法总结

绝大部分行业场景,尤其是互联网、量化行业,每天都会产生大量的数据。金融领域股票价格随时间的走势;电商行业每日的销售额;旅游行业随着节假日周期变化的机票酒店价格等;我们称这种不同时间收到的,描述一个或多种特征随着时间发生变化的数据,为时间序列数据(Time Series Data)。而时间序列预测做的就是通过多种维度的数据本身内在与时间的关联特性,利用历史的数据预测未来这么一件事情。

u013250861的博客 7574
上一篇: 超详细教程YoloV8官方推荐免费数据集网站Roboflow一键导出Voc、COCO、Yolo、Csv等格式
下一篇: 时间序列预测模型实战案例(二)(Holt-Winter)(Python)结合K-折交叉验证进行时间序列预测实现企业级预测精度(包括运行代码以及代码讲解)
Snu77
Snu77 优质创作者: 人工智能技术领域 优质创作者: 人工智能技术领域
博客等级 码龄5年 11万+粉丝 934原创
评论 130
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Snu77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值