长时间序列模型DLinear(代码解析)

news2025/7/20 13:03:53

前言

  • 今年时间序列SOTA,DLinear模型,论文下载链接,也可以看我写的论文解析当然最好是读原文。
  • DlinearNLinear模型Github项目地址,下载项目文件
  • 这里提供我写过注释的项目文件,下载地址

参数设定模块(run_longExp)

  • 首先打开run_longExp.py文件保证在不修改任何参数的情况下,代码可以跑通,这里windows系统需要将代码中--is_training--model_id--model--data参数中required=True选项删除,否则会报错。--num_workers参数需要置为0。
  • 其次需要在项目文件夹下新建子文件夹data用来存放训练数据,可以使用ETTh1数据,这里提供下载地址
  • 运行run_longExp.py训练完成不报错就成功了

参数含义

  • 下面是各参数含义(注释)
parser = argparse.ArgumentParser(description='Autoformer & Transformer family for Time Series Forecasting')

# 是否进行训练
parser.add_argument('--is_training', type=int, default=1, help='status')
# 模型前缀
parser.add_argument('--model_id', type=str, default='test', help='model id')
# 选择模型(可选模型有Autoformer, Informer, Transformer,DLinear,NLinear)
parser.add_argument('--model', type=str, default='DLinear',
                    help='model name, options: [Autoformer, Informer, Transformer]')

# 数据选择
parser.add_argument('--data', type=str, default='ETTh1', help='dataset type')
# 数据存放路径
parser.add_argument('--root_path', type=str, default='./data/', help='root path of the data file')
# 数据完整名称
parser.add_argument('--data_path', type=str, default='ETTh1.csv', help='data file')
# 预测类型(多变量预测、单变量预测、多元预测单变量)
parser.add_argument('--features', type=str, default='M',
                    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='OT', 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')

# 时间窗口长度
parser.add_argument('--seq_len', type=int, default=96, help='input sequence length')
# 先验序列长度
parser.add_argument('--label_len', type=int, default=48, help='start token length')
# 要预测的序列长度
parser.add_argument('--pred_len', type=int, default=96, help='prediction sequence length')


# 针对DLinear是否为每个变量(通道)单独建立一个线性层
parser.add_argument('--individual', action='store_true', default=False, help='DLinear: a linear layer for each variate(channel) individually')

# 嵌入策略选择
parser.add_argument('--embed_type', type=int, default=0, help='0: default 1: value embedding + temporal embedding + positional embedding 2: value embedding + temporal embedding 3: value embedding + positional embedding 4: value embedding')
# 编码器default参数为特征列数
parser.add_argument('--enc_in', type=int, default=7, help='encoder input size') # DLinear with --individual, use this hyperparameter as the number of channels
# 解码器default参数与编码器相同
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')
# 模型宽度
parser.add_argument('--d_model', type=int, default=512, help='dimension of model')
# 多头注意力机制头数
parser.add_argument('--n_heads', type=int, default=8, help='num of heads')
# 模型中encoder层数
parser.add_argument('--e_layers', type=int, default=2, help='num of encoder layers')
# 模型中decoder层数
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=25, help='window size of moving average')
# 采样因子数
parser.add_argument('--factor', type=int, default=1, help='attn factor')
# 是否需要序列长度衰减
parser.add_argument('--distil', action='store_false',
                    help='whether to use distilling in encoder, using this argument means not using distilling',
                    default=True)
# drop_out率
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')
# 是否输出attention
parser.add_argument('--output_attention', action='store_true', help='whether to output attention in ecoder')
# 是否进行预测
parser.add_argument('--do_predict', action='store_false', help='whether to predict unseen future data')

# 多线程
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=100, help='train epochs')
# batch_size大小
parser.add_argument('--batch_size', type=int, default=32, help='batch size of train input data')
# early stopping检测间隔
parser.add_argument('--patience', type=int, default=3, help='early stopping patience')
# 学习率
parser.add_argument('--learning_rate', type=float, default=0.0001, help='optimizer learning rate')
parser.add_argument('--des', type=str, default='test', help='exp description')
# loss函数
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')
# GPU分布式训练
parser.add_argument('--use_multi_gpu', action='store_true', help='use multiple gpus', default=False)
# 多GPU训练
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')

# 取参数表
args = parser.parse_args()
# 获取GPU
args.use_gpu = True if torch.cuda.is_available() and args.use_gpu else False

我们在exp.train(setting)行打上断点跳到训练主函数exp_main.py

数据处理模块

  • _get_data中找到数据处理函数data_provider.py点击进入,可以看到各标准数据集处理方法:
data_dict = {
    'ETTh1': Dataset_ETT_hour,
    'ETTh2': Dataset_ETT_hour,
    'ETTm1': Dataset_ETT_minute,
    'ETTm2': Dataset_ETT_minute,
    'Custom': Dataset_Custom,
}
  • 由于我们的数据集是ETTh1,那么数据处理的方式为Dataset_ETT_hour,我们进入data_loader.py文件,找到Dataset_ETT_hour
  • __init__主要用于传各类参数,这里不过多赘述,主要对__read_data__进行说明
     def __read_data__(self):
        # 数据标准化实例
        self.scaler = StandardScaler()
        # 读取数据
        df_raw = pd.read_csv(os.path.join(self.root_path,
                                          self.data_path))
        # 计算数据起始点
        border1s = [0, 12 * 30 * 24 - self.seq_len, 12 * 30 * 24 + 4 * 30 * 24 - self.seq_len]
        border2s = [12 * 30 * 24, 12 * 30 * 24 + 4 * 30 * 24, 12 * 30 * 24 + 8 * 30 * 24]
        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]
        # 若预测类型为S(单特征预测单特征)
        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]
        # 利用pandas将数据转换为日期格式
        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
  • 需要注意的是time_features函数,用来提取日期特征,比如't':['month','day','weekday','hour','minute'],表示提取月,天,周,小时,分钟。可以打开timefeatures.py文件进行查阅,同样后期也可以加一些日期编码进去。
  • 同样的,对__getitem__进行说明
    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

网络架构

  • 我们回到exp_main.py文件中的train函数。
 def train(self, setting):
        train_data, train_loader = self._get_data(flag='train')
        vali_data, vali_loader = self._get_data(flag='val')
        test_data, test_loader = self._get_data(flag='test')

        path = os.path.join(self.args.checkpoints, setting)
        if not os.path.exists(path):
            os.makedirs(path)
        # 记录时间
        time_now = time.time()

        # 训练steps
        train_steps = len(train_loader)
        # 早停策略
        early_stopping = EarlyStopping(patience=self.args.patience, verbose=True)

        # 优化器
        model_optim = self._select_optimizer()
        # 损失函数(MSE)
        criterion = self._select_criterion()

        # 分布式训练(windows一般不推荐)
        if self.args.use_amp:
            scaler = torch.cuda.amp.GradScaler()

        # 训练次数
        for epoch in range(self.args.train_epochs):
            iter_count = 0
            train_loss = []

            self.model.train()
            epoch_time = time.time()
            for i, (batch_x, batch_y, batch_x_mark, batch_y_mark) in enumerate(train_loader):
                iter_count += 1
                # 梯度归零
                model_optim.zero_grad()
                # 取训练数据
                batch_x = batch_x.float().to(self.device)
                batch_y = batch_y.float().to(self.device)
                batch_x_mark = batch_x_mark.float().to(self.device)
                batch_y_mark = batch_y_mark.float().to(self.device)

                # decoder输入
                dec_inp = torch.zeros_like(batch_y[:, -self.args.pred_len:, :]).float()
                dec_inp = torch.cat([batch_y[:, :self.args.label_len, :], dec_inp], dim=1).float().to(self.device)

                # encoder - decoder
                if self.args.use_amp:
                    with torch.cuda.amp.autocast():
                        if 'Linear' in self.args.model:
                            outputs = self.model(batch_x)
                        else:
                            if self.args.output_attention:
                                outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)[0]
                            else:
                                outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)

                        f_dim = -1 if self.args.features == 'MS' else 0
                        outputs = outputs[:, -self.args.pred_len:, f_dim:]
                        batch_y = batch_y[:, -self.args.pred_len:, f_dim:].to(self.device)
                        loss = criterion(outputs, batch_y)
                        train_loss.append(loss.item())
                else:
                    # 如果模型是Linear系列
                    if 'Linear' in self.args.model:
                            # 得到输出
                            outputs = self.model(batch_x)
                    else:
                        if self.args.output_attention:
                            outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)[0]
                            
                        else:
                            outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark, batch_y)
                    # print(outputs.shape,batch_y.shape)
                    # 如果预测方式为MS,取最后1列否则取第1列
                    f_dim = -1 if self.args.features == 'MS' else 0
                    outputs = outputs[:, -self.args.pred_len:, f_dim:]
                    batch_y = batch_y[:, -self.args.pred_len:, f_dim:].to(self.device)
                    # 计算损失
                    loss = criterion(outputs, batch_y)
                    # 将损失放入train_loss列表中
                    train_loss.append(loss.item())
                # 记录训练过程
                if (i + 1) % 500 == 0:
                    print("\titers: {0}, epoch: {1} | loss: {2:.7f}".format(i + 1, epoch + 1, loss.item()))
                    speed = (time.time() - time_now) / iter_count
                    left_time = speed * ((self.args.train_epochs - epoch) * train_steps - i)
                    print('\tspeed: {:.4f}s/iter; left time: {:.4f}s'.format(speed, left_time))
                    iter_count = 0
                    time_now = time.time()

                if self.args.use_amp:
                    scaler.scale(loss).backward()
                    scaler.step(model_optim)
                    scaler.update()
                else:
                    # 反向传播
                    loss.backward()
                    # 更新梯度
                    model_optim.step()

            print("Epoch: {} cost time: {}".format(epoch + 1, time.time() - epoch_time))
            train_loss = np.average(train_loss)
            vali_loss = self.vali(vali_data, vali_loader, criterion)
            test_loss = self.vali(test_data, test_loader, criterion)

            print("Epoch: {0}, Steps: {1} | Train Loss: {2:.7f} Vali Loss: {3:.7f} Test Loss: {4:.7f}".format(
                epoch + 1, train_steps, train_loss, vali_loss, test_loss))
            early_stopping(vali_loss, self.model, path)
            if early_stopping.early_stop:
                print("Early stopping")
                break
            # 更新学习率
            adjust_learning_rate(model_optim, epoch + 1, self.args)
        # 保存模型
        best_model_path = path + '/' + 'checkpoint.pth'
        self.model.load_state_dict(torch.load(best_model_path))
  • 注意模型训练outputs = self.model(batch_x, batch_x_mark, dec_inp, batch_y_mark)model中包含DLinear的核心架构(也是最重要的部分),打开项目文件夹下models文件夹,找到DLinear.py文件,打开后找到Model类。直接看forward
    def forward(self, x):
        # x: [Batch, Input length, Channel]
        # 季节与时间趋势性分解
        seasonal_init, trend_init = self.decompsition(x)
        # 将维度索引2与维度索引1交换
        seasonal_init, trend_init = seasonal_init.permute(0,2,1), trend_init.permute(0,2,1)
        if self.individual:
            seasonal_output = torch.zeros([seasonal_init.size(0),seasonal_init.size(1),self.pred_len],dtype=seasonal_init.dtype).to(seasonal_init.device)
            trend_output = torch.zeros([trend_init.size(0),trend_init.size(1),self.pred_len],dtype=trend_init.dtype).to(trend_init.device)
            for i in range(self.channels):
                # 使用全连接层得到季节性
                seasonal_output[:,i,:] = self.Linear_Seasonal[i](seasonal_init[:,i,:])
                # 使用全连接层得到趋势性
                trend_output[:,i,:] = self.Linear_Trend[i](trend_init[:,i,:])
                # 两者共享所有权重
        else:
            seasonal_output = self.Linear_Seasonal(seasonal_init)
            trend_output = self.Linear_Trend(trend_init)
        # 将季节性与趋势性相加
        x = seasonal_output + trend_output
        # 交换维度位置
        return x.permute(0,2,1) # to [Batch, Output length, Channel]
  • 季节趋势性分解,跳转到series_decomp
class series_decomp(nn.Module):
    """
    Series decomposition block
    """
    def __init__(self, kernel_size):
        super(series_decomp, self).__init__()
        self.moving_avg = moving_avg(kernel_size, stride=1)

    def forward(self, x):
        # 滑动平均
        moving_mean = self.moving_avg(x)
        # 季节趋势性
        res = x - moving_mean
        return res, moving_mean
  • 季节性和趋势性使用同一全连接神经网络,共享所有权重。使用nn.Linear函数构建了全连接神经网络

结果展示

  • 训练DLinear模型是非常快的,因为丢弃了很多transformer中复杂的计算块,跑一遍ETTh1数据只需要大约1分钟,我用的笔记本上的CPU。
Args in experiment:
Namespace(is_training=1, model_id='test', model='DLinear', data='ETTh1', root_path='./data/', data_path='ETTh1.csv', features='M', target='OT', freq='h', checkpoints='./checkpoints/', seq_len=96, label_len=48, pred_len=96, individual=False, embed_type=0, enc_in=7, dec_in=7, c_out=7, d_model=512, n_heads=8, e_layers=2, d_layers=1, d_ff=2048, moving_avg=25, factor=1, distil=True, dropout=0.05, embed='timeF', activation='gelu', output_attention=False, do_predict=True, num_workers=0, itr=1, train_epochs=100, batch_size=32, patience=3, learning_rate=0.0001, des='test', loss='mse', lradj='type1', use_amp=False, use_gpu=False, gpu=0, use_multi_gpu=False, devices='0,1,2,3', test_flop=False)
Use CPU
>>>>>>>start training : DLinear_rate 0.0001>>>>>>>>>>>>>>>>>>>>>>>>>>
train 8449
val 2785
test 2785
Epoch: 1 cost time: 1.5147898197174072
Epoch: 1, Steps: 264 | Train Loss: 0.6620889 Vali Loss: 0.8593202 Test Loss: 0.5310578
Validation loss decreased (inf --> 0.859320).  Saving model ...
Updating learning rate to 0.0001
Epoch: 2 cost time: 1.49473237991333
Epoch: 2, Steps: 264 | Train Loss: 0.4363616 Vali Loss: 0.7708484 Test Loss: 0.4540242
Validation loss decreased (0.859320 --> 0.770848).  Saving model ...
Updating learning rate to 5e-05
Epoch: 3 cost time: 1.2200875282287598
Epoch: 3, Steps: 264 | Train Loss: 0.4081523 Vali Loss: 0.7452631 Test Loss: 0.4380584
Validation loss decreased (0.770848 --> 0.745263).  Saving model ...
Updating learning rate to 2.5e-05
Epoch: 4 cost time: 1.2776997089385986
Epoch: 4, Steps: 264 | Train Loss: 0.3990288 Vali Loss: 0.7355505 Test Loss: 0.4318272
Validation loss decreased (0.745263 --> 0.735550).  Saving model ...
Updating learning rate to 1.25e-05
Epoch: 5 cost time: 1.2430932521820068
Epoch: 5, Steps: 264 | Train Loss: 0.3950030 Vali Loss: 0.7301292 Test Loss: 0.4291500
Validation loss decreased (0.735550 --> 0.730129).  Saving model ...
Updating learning rate to 6.25e-06
Epoch: 6 cost time: 1.260094165802002
Epoch: 6, Steps: 264 | Train Loss: 0.3931120 Vali Loss: 0.7285364 Test Loss: 0.4276760
Validation loss decreased (0.730129 --> 0.728536).  Saving model ...
Updating learning rate to 3.125e-06
Epoch: 7 cost time: 1.2400920391082764
Epoch: 7, Steps: 264 | Train Loss: 0.3921362 Vali Loss: 0.7272122 Test Loss: 0.4269841
Validation loss decreased (0.728536 --> 0.727212).  Saving model ...
Updating learning rate to 1.5625e-06
Epoch: 8 cost time: 1.2691984176635742
Epoch: 8, Steps: 264 | Train Loss: 0.3916254 Vali Loss: 0.7265375 Test Loss: 0.4266387
Validation loss decreased (0.727212 --> 0.726538).  Saving model ...
Updating learning rate to 7.8125e-07
Epoch: 9 cost time: 1.31856369972229
Epoch: 9, Steps: 264 | Train Loss: 0.3913689 Vali Loss: 0.7263398 Test Loss: 0.4264523
Validation loss decreased (0.726538 --> 0.726340).  Saving model ...
Updating learning rate to 3.90625e-07
Epoch: 10 cost time: 1.3412230014801025
Epoch: 10, Steps: 264 | Train Loss: 0.3912611 Vali Loss: 0.7261187 Test Loss: 0.4263628
Validation loss decreased (0.726340 --> 0.726119).  Saving model ...
Updating learning rate to 1.953125e-07
Epoch: 11 cost time: 1.246096134185791
Epoch: 11, Steps: 264 | Train Loss: 0.3911887 Vali Loss: 0.7262567 Test Loss: 0.4263154
EarlyStopping counter: 1 out of 3
Updating learning rate to 9.765625e-08
Epoch: 12 cost time: 1.2540950775146484
Epoch: 12, Steps: 264 | Train Loss: 0.3911324 Vali Loss: 0.7254719 Test Loss: 0.4262920
Validation loss decreased (0.726119 --> 0.725472).  Saving model ...
Updating learning rate to 4.8828125e-08
Epoch: 13 cost time: 1.284095287322998
Epoch: 13, Steps: 264 | Train Loss: 0.3911295 Vali Loss: 0.7261668 Test Loss: 0.4262800
EarlyStopping counter: 1 out of 3
Updating learning rate to 2.44140625e-08
Epoch: 14 cost time: 1.3260986804962158
Epoch: 14, Steps: 264 | Train Loss: 0.3911082 Vali Loss: 0.7258070 Test Loss: 0.4262740
EarlyStopping counter: 2 out of 3
Updating learning rate to 1.220703125e-08
Epoch: 15 cost time: 1.2486040592193604
Epoch: 15, Steps: 264 | Train Loss: 0.3911197 Vali Loss: 0.7261318 Test Loss: 0.4262710
EarlyStopping counter: 3 out of 3
Early stopping
>>>>>>>testing : DLinear_rate 0.0001<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
test 2785
mse:0.42629215121269226, mae:0.4337235391139984
>>>>>>>predicting : DLinear_rate 0.0001<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
pred 1
  • 模型运行完以后会在test_results文件夹下生成,模型在测试集上表现情况:
    请添加图片描述
  • 如果想要生成季节性、趋势性图,可以打开项目文件夹下的weight_plot.py文件,将save_root = 'weights_plot/%s'%root.split('/')[1]改成save_root = './weights_plot/',然后运行。那么在weights_plot文件夹下就能看见季节性与趋势性热力图。
季节性趋势

趋势性

总体趋势

后面如果有时间我会继续写如何使用DLinear定义自己的项目。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图神经网络之预训练大模型结合:ERNIESage在链接预测任务应用

1.ERNIESage运行实例介绍(1.8x版本) 本项目原链接&#xff1a;https://aistudio.baidu.com/aistudio/projectdetail/5097085?contributionType1 本项目主要是为了直接提供一个可以运行ERNIESage模型的环境&#xff0c; https://github.com/PaddlePaddle/PGL/blob/develop/e…

笔记redis

redis特点: 1.这些数据类型都支持 push/pop. add/remove 及取交集并集和差集及更丰富的操作 2.Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中进行持久化 3.单线路IO多路复用 4.redis操作是原子性操作 redis 单线程模型&#xff1a; Redis 内…

chrome插件开发(manifest_version版本V3 + Ant Design Vue)

1.什么是 Chrome 插件 谷歌浏览器插件是一种小型的定制浏览器体验的程序&#xff0c;通过插件可以自定义浏览器的一些行为来适合个人的需求&#xff0c;例如上面的查看服务器状态插件。 在应用商店中下载下来的插件基本上都是以.crx 为文件后缀&#xff0c;该文件其实就是一个…

Word2Vec

Word2Vec 在自然语言发展的早期阶段&#xff0c;词的表示经历了不断地发展和改进&#xff0c;直到后来有一种word vector的思想被提出以及后续的实现&#xff0c;才极大地促进了NLP的发展。 word vector的核心思想&#xff1a; 为每个单词构建一个密集向量&#xff0c;选择后…

傻白入门芯片设计,先进封装技术(五)

集成电路芯片与封装之间是不可分割的整体。没有一个芯片可以不用封装就能正常工作&#xff0c;封装对芯片来说是必不可少的&#xff0c;随着IC生产技术的进步&#xff0c;封装技术也不断更新换代&#xff0c;每一代IC都与新一代的IC封装技术紧密相连。 一、什么是封装&#xf…

什么蓝牙耳机颜值高音质好?颜值高音质好的蓝牙耳机推荐

朋友让我推荐蓝牙耳机的时候&#xff0c;总是喜欢问哪款蓝牙耳机的性能更强&#xff0c;想要直接入手那款性能更强的蓝牙耳机&#xff0c;以此节省对比的时间。但是用户自行进行对比的步骤&#xff0c;显然是不能省的&#xff0c;所以今天我给四款蓝牙耳机做了横向对比&#xf…

C语言tips-NULL指针和void指针

0.写在最前 最近因为工作需要开始重新学c语言&#xff0c;越学越发现c语言深不可测&#xff0c;当初用python轻轻松松处理的一些数据&#xff0c;但是c语言写起来却异常的复杂&#xff0c;这个板块就记录一下我的c语言复习之路 1. void指针 1.1 解释 void 用在函数定义中可以表…

身份安全风险分析

摘要 从勒索软件到 APT&#xff0c;身份风险是重要的攻击向量。 管理 Active Directory 的复杂性&#xff0c;导致所有组织都存在1/6的可利用的特权身份风险。 这些身份风险包括使用过时密码的本地管理员、具有不必要权限的错误配置用户、在终端上暴露的缓存凭据等。 当攻击者…

让学前端不再害怕英语单词(四)

|| 欢迎关注csdn前端领域博主: 前端小王hs || email: 337674757qq.com || 前端交流群&#xff1a; 598778642前三章直通车↓↓↓ 让学前端不再害怕英语单词&#xff08;一&#xff09; 让学前端不再害怕英语单词&#xff08;二&#xff09; 让学前端不再害怕英语单词&#xff0…

单目标应用:最有价值球员算法(Most Valuable Player Algorithm,MVPA)求解旅行商问题TSP

一、最有价值球员算法 最有价值球员算法&#xff08;Most Valuable Player Algorithm&#xff0c;MVPA&#xff09;由Bouchekara 等人于2017年提出&#xff0c;该算法受到体育比赛的启发&#xff0c;球员们为了赢得冠军而组成队伍进行队伍竞争&#xff0c;他们也为了赢得最有价…

使用VMware安装系统Window、Linux操作系统

使用VMware安装系统Window、Linux操作系统 下载镜像文件打开VMware 下载镜像文件地址或链接&#xff1a; Windows全家桶镜像文件下载网站&#xff1a;msdn.itellyou.cnWindows 10 种子文件 ed2k://|file|cn_windows_10_business_editions_version_1803_updated_aug_2019_x64_dv…

Js逆向教程-14反调试

Js逆向教程-14反调试 一、检测是否在调试 键盘监听&#xff08;F12&#xff09;检测浏览器的高度插值检测开发者人员工具变量是否为true利用console.log调用次数利用代码运行的时间差利用toString检测非浏览器 二、显性 2.1 debugger&#xff1a; function xx() {debugger;…

盘点re:Invent历年重磅创新:今年亚马逊云科技又将有哪些重磅发布?

“云计算春晚”re&#xff1a;Invent即将开幕&#xff0c;这一次亚马逊云科技会带来哪些重磅发布&#xff1f; 一年一度的亚马逊云科技re:Invent大会即将在下周开幕。作为云计算行业的顶级盛事&#xff0c;亚马逊云科技re:Invent大会看出云计算领域创新的风向标&#xff0c;也…

QQ小程序——无法正常创建项目与uniapp联动问题

目录 一、使用QQ小程序开发者工具无法创建项目 情境引入 检查原因 解决方法 总结&#xff1a; 二、QQ小程序开发者工具无法与uniapp联动 情境导入 问题排错 解决方法 三、QQ小程序开发者工具无法正常上传 情境导入 错误分析 解决方法 心得分享 分清工作和学习的区别…

运动耳机什么牌子的好,推荐几款排行靠前的耳机

骨传导耳机近些年在耳机界迅速燃起&#xff0c;其设计原理是由贝多芬含棍棒发声所启发&#xff0c;而佩戴骨传导耳机主要是为了防止细菌在耳道内的滋生&#xff0c;除此之外骨传导耳机的佩戴方式也是直接将耳机套在头骨处即可完成佩戴&#xff0c;不用进入耳道&#xff0c;能够…

【torch-sparse及pytorch-geometric 安装】

torch-sparse及pytorch-geometric 安装 pip安装完pytorch-geometric之后&#xff0c;报错No module named torch_sparse 然后安装pip安装torch-sparse&#xff0c;结果报错&#xff0c;百度一下怎么安装&#xff0c;无果&#xff0c;转战官方文档&#xff0c;成功&#xff01; …

实验讲解-线程池停止执行任务 Executor.execute shutdown awaitTermination shutdownNow()

1 Executor.execute public interface Executor { 在将来的某个时间执行给定的可运行的任务。该可运行的任务可以在新线程、池线程或调用线程中执行&#xff0c;由Executor实现决定。 参数&#xff1a; command–可运行的任务 投掷&#xff1a; RejectedExecutionException–…

Kafka生产者之分区

一、分区好处 &#xff08;1&#xff09;便于合理使用存储资源&#xff0c;每个Partition在一个Broker上存储&#xff0c;可以把海量的数据按照分区切割成一块一块数据存储在多台Broker上。合理控制分区的任务&#xff0c;可以实现负载均衡的效果&#xff1b; &#xff08;2&…

【畅购商城】购物车模块之查看购物车

目录 分析 接口 后端实现 前端实现&#xff1a;显示页面 前端实现&#xff1a;显示购物车信息 分析 用户如果没有登录&#xff0c;购物车存放在浏览器端的localStorage处&#xff0c;且以数组的方式进行存储。用户如果登录了&#xff0c;购物车存放在redis中&#xff0c…

项目实战——对战回放和排行榜

目录 一、天梯积分更新 二、实现对局列表页面 三、前端测试 四、实现查看录像功能 五、实现分页功能 六、后端实现查询排行耪 七、前端展示 八、限制Bot数量 一、天梯积分更新 可以自己定义一下规则 存之前算一下两名玩家的天梯积分 实现更新&#xff0c;实现后重启看一…