前言

TSlib 是一个面向深度学习研究人员的开源库,在深度时间序列分析问题上表现优越

其提供了一个简洁的代码库来评估高级深度时间序列模型或开发您的模型,其中涵盖五个主流任务:长期和短期预测、插补、异常检测和分类

GitHub地址:https://github.com/thuml/Time-Series-Library

时序?启动!

新建一个 dataset 文件夹,放入你事先准备的数据集

修改 run.py 中的文件,改为你自己的数据集信息

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

同时修改 data_provider 文件夹中的 data_loader.py 仿造其中新建一个适配自己项目的 class

一般采用复制原有 class 重命名后再更改内容的方式建立自己项目的 class

class Dataset_Custom(Dataset):
    def __init__(self, root_path, flag='train', size=None,
                 features='S', data_path='ETTh1.csv',
                 target='OT', scale=True, timeenc=0, freq='h', seasonal_patterns=None):
        # size [seq_len, label_len, pred_len]
        # info
        if size == None:
            self.seq_len = 24 * 4 * 4
            self.label_len = 24 * 4
            self.pred_len = 24 * 4
        else:
            self.seq_len = size[0]
            self.label_len = size[1]
            self.pred_len = size[2]
        # init
        assert flag in ['train', 'test', 'val']
        type_map = {'train': 0, 'val': 1, 'test': 2}
        self.set_type = type_map[flag]

        self.features = features
        self.target = target
        self.scale = scale
        self.timeenc = timeenc
        self.freq = freq

        self.root_path = root_path
        self.data_path = data_path
        self.__read_data__()

    def __read_data__(self):
        self.scaler = StandardScaler()
        df_raw = pd.read_csv(os.path.join(self.root_path,
                                          self.data_path))

        '''
        df_raw.columns: ['date', ...(other features), target feature]
        '''
        cols = list(df_raw.columns)
        cols.remove(self.target)
        cols.remove('date')
        df_raw = df_raw[['date'] + cols + [self.target]]
        num_train = int(len(df_raw) * 0.7)
        num_test = int(len(df_raw) * 0.2)
        num_vali = len(df_raw) - num_train - num_test
        border1s = [0, num_train - self.seq_len, len(df_raw) - num_test - self.seq_len]
        border2s = [num_train, num_train + num_vali, len(df_raw)]
        border1 = border1s[self.set_type]
        border2 = border2s[self.set_type]

        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:
            train_data = df_data[border1s[0]:border2s[0]]
            self.scaler.fit(train_data.values)
            data = self.scaler.transform(df_data.values)
        else:
            data = df_data.values

        df_stamp = df_raw[['date']][border1:border2]
        df_stamp['date'] = pd.to_datetime(df_stamp.date)
        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)
            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]
        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]
        seq_y = self.data_y[r_begin:r_end]
        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 - self.pred_len + 1

    def inverse_transform(self, data):
        return self.scaler.inverse_transform(data)

可能读者现在还对 features 和 target 有一些疑问,还请等待,后面都会讲到

接下来在 data_factory.py 中添加自己新创建的类

data_dict = {
    'ETTh1': Dataset_ETT_hour,
    'ETTh2': Dataset_ETT_hour,
    'ETTm1': Dataset_ETT_minute,
    'ETTm2': Dataset_ETT_minute,
    'custom': Dataset_Custom,
    'm4': Dataset_M4,
    'PSM': PSMSegLoader,
    'MSL': MSLSegLoader,
    'SMAP': SMAPSegLoader,
    'SMD': SMDSegLoader,
    'SWAT': SWATSegLoader,
    'UEA': UEAloader

}

接下来让我们回到 run.py 开始梳理一遍这些参数

    # basic config
    parser.add_argument('--task_name', type=str, required=False, default='long_term_forecast',
                        help='task name, options:[long_term_forecast, short_term_forecast, imputation, '
                             'classification, anomaly_detection]')
    parser.add_argument('--is_training', type=int, required=False, default=1, help='status')
    parser.add_argument('--model_id', type=str, required=False, default='test', help='model id')
    parser.add_argument('--model', type=str, required=False, default='TimesNet',
                        help='model name, options: [Autoformer, Transformer, TimesNet]')

task_name:从左到右依次是长期和短期预测、插补、分类和异常检测

is_training: 表示是否为训练模式,1代表训练模式

model:选择你所需的模型

parser.add_argument('--data', type=str, required=False, default='', help='dataset type')
    parser.add_argument('--root_path', type=str, default='./dataset/', help='root path of the data file')
    parser.add_argument('--data_path', type=str, default='.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='Power (MW)', help='target feature in S or MS task')
    parser.add_argument('--freq', type=str, default='h',
                        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')

data:数据名称,与 data_factory.py 中的 data_dict 自主添加的数据名称相对应

root_path:数据集根目录

data_path:数据集全名(带后缀)

features:时间特征处理方式,M:多变量预测多变量,S:单变量预测单变量,MS:多变量预测单变量

target:预测目标列列名

freq:时间采集粒度,s,t,h,d分别代表以秒,分钟,小时,与日为单位

checkpoints:模型权重保存目录

修改这些参数时,记住同时要修改你在 data_loader.py 中新建的 class

简述一下拿来即用的流程:

1.修改 run.py 将其中的数据相关的参数修改为自己的参数

2.在 data_loader.py 中新建一个适配自己项目的 class,记得也要改其中的 features ,data_path,target等,一般来说,你只需要修改你项目需要用到的这个 class

3.在 data_factory.py 的字典中,添加与新创建的 class 相对应的数据名称,记得在 run.py 中修改相应参数

4.重新运行 run.py 根据报错进一步修改,训练完成不报错的话就成功了,建议在 GPU 上进行,CPU 训练速度一言难尽

常见错误与解决方式

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x92 in position 15: invalid start byte

这段说明你的数据集并非为 utf-8 格式,出现这种错误的原因一般是你读取的数据集为 xlsx 格式的表格,改为 csv 格式即可解决问题

推荐使用 Excel 表格另存为来保存,使用 WPS 另存为很有可能损坏文件

run.py: error:the following arguments are required: --task_name --is_training --model_id --model--data

这代表在执行启动命令时需要添加额外参数,并不能使用默认参数,解决方法是将 run.py 中的相应位置的

require=True 更改为 require=False

keyError: "None of [Index(['date'], dtype='object')] are in the [columns]

这代表算法在运行过程中并没有找到名为 date 的列,出现这种情况的原因一般是你的数据集的时间列命名不为 date ,改为 date 即可解决问题

RuntimeError: Given groups=1, weight of size [512,7,3],expected input[32,12,98] to have 7 channels.but got 12 channels instead

这一类错误代表输入的数据集的通道数与你的设定不一致,同样在 run.py 中修改

以文中这个报错为例,就是因为设定通道数为 7 ,但是实际通道数为 12

    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=7, help='output size')

将参数 7 改为 12 即可解决问题

RuntimeError: CUDA out of memory. Tried to allocate 288.00 MiB

很经典的显存不足问题,可能是没有释放占用的显存的问题,也可以尝试对半降低 batch_size ,不过这个问题很有可能许多方法都解决不掉,就是得换更好的显卡