时间序列预测实战(二十四)PyTorch实现RNN进行多元和单元预测(附代码+数据集+完整解析)

     


一、本文介绍

本篇文章给大家带来的是利用我个人编写的架构进行RNN时间序列卷积进行时间序列建模(专门为了时间序列领域新人编写的架构,简单且不同于市面上大家用GPT写的代码),包括结果可视化、支持单元预测、多元预测、模型拟合效果检测、预测未知数据、以及滚动长期预测功能该结构是一个通用架构任何模型嵌入其中都可运行。下面来介绍一下RNN:循环神经网络(RNN)是深度学习中用于处理序列数据的一种基本网络结构。RNN的核心原理是它拥有一个循环结构这使得网络能够保持一个内部的状态,从而捕捉到数据中随时间演变的信息。在RNN中,每个节点(或称为单元)在处理当前输入的同时,也会考虑前一时间步的隐藏状态。这种结构让RNN能够在时间序列分析、语言模型、文本生成等任务中建模数据中的时序依赖性,所以它可以用于时序预测,但是因为其存在导致梯度爆炸或者梯度消失的问题所以后来引出了LSTM,GRU等RNN模型。

  专栏目录:时间序列预测目录:深度学习、机器学习、融合模型、创新模型实战案例

专栏: 时间序列预测专栏:基础知识+数据分析+机器学习+深度学习+Transformer+创新模型

预测功能效果展示(不是测试集是预测未知数据,所以图中没有对比的数据)->

损失截图(损失这里我先展示一个训练过程中的后面会自动生成损失图像)-> 

根据损失来看模型的拟合效果还是很好的,但后面还是做了检验模型拟合效果的功能让大家真正的评估模型的效果。

测试集状况->

(从下面的图片可以看出模型在测试集的效果还可以,毕竟只是单一的RNN结构,为啥发单一因为我发现发高端或者融合的根本没人看) 

目录

一、本文介绍

二、RNN的框架原理

三、数据集介绍

四、参数讲解 

五、完整代码

六、训练模型 

七、预测结果

7.1 预测未知数据效果图

7.2 测试集效果图 

7.3 CSV文件生成效果图 

7.4 检验模型拟合效果图

八、全文总结


二、RNN的框架原理

循环神经网络(RNN)是一种专门用于处理序列数据的神经网络。它的主要思想是在处理序列的每个元素时,网络不仅考虑当前输入,还会考虑之前的信息。这使得RNN非常适合处理时间序列数据、语言模型等任务。

RNN的工作机制主要包括以下几点:

  1. 隐藏状态: RNN维护一个隐藏状态,它捕获到目前为止处理过的信息。
  2. 序列处理: 在处理序列的每个时间步时,RNN会更新其隐藏状态。
  3. 权重共享: 在不同时间步,RNN使用相同的权重,这减少了模型的复杂性并提高了训练效率。
  4. 输出: 根据任务的不同,RNN可以在每个时间步产生输出,或者仅在最后一个时间步产生输出。

总结来说,RNN之所以强大,是因为它能够利用序列数据的时间依赖性,通过维护一个在时间步之间传递的隐藏状态来实现这一点。这种结构使得RNN在处理诸如文本、语音等序列数据方面表现出色。然而,RNN也存在一定的局限性,比如难以处理长序列中的长期依赖问题,这通常通过引入LSTM或GRU等变体来解决。

下面的链接里包含了RNN系列的完整发展流程大家有兴趣的可以看看->

RNN发展流程:点击即可跳转

下面分享给大家两个LSTM和GRU的原理图吧,RNN实在没啥好说的,主要不发这些经典的文章还没人看。 

LSTM结构图-> 

GRU结构图-> 


三、数据集介绍

本文是实战讲解文章,上面主要是简单讲解了一下网络结构比较具体的流程还是很复杂的涉及到很多的数学计算,下面我们来讲一讲模型的实战内容,第一部分是我利用的数据集。

本文我们用到的数据集是ETTh1.csv该数据集是一个用于时间序列预测的电力负荷数据集,它是 ETTh 数据集系列中的一个。ETTh 数据集系列通常用于测试和评估时间序列预测模型。以下是 ETTh1.csv 数据集的一些内容:

数据内容:该数据集通常包含有关电力系统的多种变量,如电力负荷、价格、天气情况等。这些变量可以用于预测未来的电力需求或价格。

时间范围和分辨率数据通常按小时或天记录,涵盖了数月或数年的时间跨度。具体的时间范围和分辨率可能会根据数据集的版本而异。 

以下是该数据集的部分截图->


四、参数讲解 

parser.add_argument('-model', type=str, default='RNN', help="模型持续更新")parser.add_argument('-window_size', type=int, default=126, help="时间窗口大小, window_size > pre_len")parser.add_argument('-pre_len', type=int, default=24, help="预测未来数据长度")# dataparser.add_argument('-shuffle', action='store_true', default=True, help="是否打乱数据加载器中的数据顺序")parser.add_argument('-data_path', type=str, default='ETTh1-Test.csv', help="你的数据数据地址")parser.add_argument('-target', type=str, default='OT', help='你需要预测的特征列,这个值会最后保存在csv文件里')parser.add_argument('-input_size', type=int, default=7, help='你的特征个数不算时间那一列')parser.add_argument('-feature', type=str, default='M', help='[M, S, MS],多元预测多元,单元预测单元,多元预测单元')# learningparser.add_argument('-lr', type=float, default=0.001, help="学习率")parser.add_argument('-drop_out', type=float, default=0.05, help="随机丢弃概率,防止过拟合")parser.add_argument('-epochs', type=int, default=20, help="训练轮次")parser.add_argument('-batch_size', type=int, default=16, help="批次大小")parser.add_argument('-save_path', type=str, default='models')# modelparser.add_argument('-hidden_size', type=int, default=64, help="隐藏层单元数")parser.add_argument('-kernel_sizes', type=int, default=3)parser.add_argument('-laryer_num', type=int, default=2)# deviceparser.add_argument('-use_gpu', type=bool, default=True)parser.add_argument('-device', type=int, default=0, help="只设置最多支持单个gpu训练")# optionparser.add_argument('-train', type=bool, default=True)parser.add_argument('-test', type=bool, default=True)parser.add_argument('-predict', type=bool, default=True)parser.add_argument('-inspect_fit', type=bool, default=True)parser.add_argument('-lr-scheduler', type=bool, default=True)

为了大家方便理解,文章中的参数设置我都用的中文,所以大家应该能够更好的理解。下面我在进行一遍讲解。 

参数名称参数类型参数讲解
1modelstr模型名称
2window_sizeint时间窗口大小,用多少条数据去预测未来的数据

3

pre_lenint预测多少条未来的数据
4shufflestore_true是否打乱输入dataloader中的数据,不是数据的顺序

5

data_pathstr你输入数据的地址
6targetstr你想要预测的特征列

7

input_sizeint输入的特征数不包含时间那一列!!!

8

featurestr[M, S, MS],多元预测多元,单元预测单元,多元预测单元
9lrfloat学习率大小

10

drop_out

float丢弃概率
11epochsint训练轮次

12

batch_sizeint批次大小
13svae_pathstr模型的保存路径

14

hidden_sizeint隐藏层大小
15kernel_sizeint卷积核大小

16

layer_numintlstm层数
17use_gpubool是否使用GPU

18

deviceintGPU编号
19trainbool是否进行训练

20

predictbool是否进行预测

21

inspect_fitbool是否进行检验模型
22lr_schdulerbool是否使用学习率计划


五、完整代码

复制粘贴到一个文件下并且按照上面的从参数讲解配置好参数即可运行~(极其适合新手和刚入门的读者)

import argparse
import timeimport numpy as np
import pandas as pd
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
from torch.nn.utils import weight_norm
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
from tqdm import tqdm# 随机数种子
np.random.seed(0)class StandardScaler():def __init__(self):self.mean = 0.self.std = 1.def fit(self, data):self.mean = data.mean(0)self.std = data.std(0)def transform(self, data):mean = torch.from_numpy(self.mean).type_as(data).to(data.device) if torch.is_tensor(data) else self.meanstd = torch.from_numpy(self.std).type_as(data).to(data.device) if torch.is_tensor(data) else self.stdreturn (data - mean) / stddef inverse_transform(self, data):mean = torch.from_numpy(self.mean).type_as(data).to(data.device) if torch.is_tensor(data) else self.meanstd = torch.from_numpy(self.std).type_as(data).to(data.device) if torch.is_tensor(data) else self.stdif data.shape[-1] != mean.shape[-1]:mean = mean[-1:]std = std[-1:]return (data * std) + meandef plot_loss_data(data):# 使用Matplotlib绘制线图plt.figure()plt.figure(figsize=(10, 5))plt.plot(data, marker='o')# 添加标题plt.title("loss results Plot")# 显示图例plt.legend(["Loss"])plt.show()class TimeSeriesDataset(Dataset):def __init__(self, sequences):self.sequences = sequencesdef __len__(self):return len(self.sequences)def __getitem__(self, index):sequence, label = self.sequences[index]return torch.Tensor(sequence), torch.Tensor(label)def create_inout_sequences(input_data, tw, pre_len, config):# 创建时间序列数据专用的数据分割器inout_seq = []L = len(input_data)for i in range(L - tw):train_seq = input_data[i:i + tw]if (i + tw + pre_len) > len(input_data):breakif config.feature == 'MS':train_label = input_data[:, -1:][i + tw:i + tw + pre_len]else:train_label = input_data[i + tw:i + tw + pre_len]inout_seq.append((train_seq, train_label))return inout_seqdef calculate_mae(y_true, y_pred):# 平均绝对误差mae = np.mean(np.abs(y_true - y_pred))return maedef create_dataloader(config, device):print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>创建数据加载器<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<")df = pd.read_csv(config.data_path)  # 填你自己的数据地址,自动选取你最后一列数据为特征列 # 添加你想要预测的特征列pre_len = config.pre_len  # 预测未来数据的长度train_window = config.window_size  # 观测窗口# 将特征列移到末尾target_data = df[[config.target]]df = df.drop(config.target, axis=1)df = pd.concat((df, target_data), axis=1)cols_data = df.columns[1:]df_data = df[cols_data]# 这里加一些数据的预处理, 最后需要的格式是pd.seriestrue_data = df_data.values# 定义标准化优化器# 定义标准化优化器scaler = StandardScaler()scaler.fit(true_data)train_data = true_data[int(0.3 * len(true_data)):]valid_data = true_data[int(0.15 * len(true_data)):int(0.30 * len(true_data))]test_data = true_data[:int(0.15 * len(true_data))]print("训练集尺寸:", len(train_data), "测试集尺寸:", len(test_data), "验证集尺寸:", len(valid_data))# 进行标准化处理train_data_normalized = scaler.transform(train_data)test_data_normalized = scaler.transform(test_data)valid_data_normalized = scaler.transform(valid_data)# 转化为深度学习模型需要的类型Tensortrain_data_normalized = torch.FloatTensor(train_data_normalized).to(device)test_data_normalized = torch.FloatTensor(test_data_normalized).to(device)valid_data_normalized = torch.FloatTensor(valid_data_normalized).to(device)# 定义训练器的的输入train_inout_seq = create_inout_sequences(train_data_normalized, train_window, pre_len, config)test_inout_seq = create_inout_sequences(test_data_normalized, train_window, pre_len, config)valid_inout_seq = create_inout_sequences(valid_data_normalized, train_window, pre_len, config)# 创建数据集train_dataset = TimeSeriesDataset(train_inout_seq)test_dataset = TimeSeriesDataset(test_inout_seq)valid_dataset = TimeSeriesDataset(valid_inout_seq)# 创建 DataLoadertrain_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True, drop_last=True)test_loader = DataLoader(test_dataset, batch_size=args.batch_size, shuffle=False, drop_last=True)valid_loader = DataLoader(valid_dataset, batch_size=args.batch_size, shuffle=False, drop_last=True)print("通过滑动窗口共有训练集数据:", len(train_inout_seq), "转化为批次数据:", len(train_loader))print("通过滑动窗口共有测试集数据:", len(test_inout_seq), "转化为批次数据:", len(test_loader))print("通过滑动窗口共有验证集数据:", len(valid_inout_seq), "转化为批次数据:", len(valid_loader))print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>创建数据加载器完成<<<<<<<<<<<<<<<<<<<<<<<<<<<")return train_loader, test_loader, valid_loader, scalerclass RNNs(nn.Module):"""Model class to declare an rnn and define a forward pass of the model."""def __init__(self, input_size, output_size, hidden_size, num_layers, pred_len):"""'rnn'"""# inherit the nn.Module class via 'super'super(RNNs, self).__init__()# store stuff in the classself.pre_len = pred_lenself.n_layers = num_layersself.hidden_size = hidden_sizeself.hidden = nn.Linear(input_size, self.hidden_size)self.relu = nn.ReLU()self.rnn = nn.RNN(self.hidden_size, self.hidden_size, num_layers, bias=True, batch_first=True)  # output (batch_size, obs_len, hidden_size)self.linear = nn.Linear(self.hidden_size, output_size)def forward(self, x):# rnn module expects data of shape [seq, batch_size, input_size]batch_size, obs_len, features_size = x.shape  # (batch_size, obs_len, features_size)xconcat = self.hidden(x)  # (batch_size, obs_len, hidden_size)H = torch.zeros(batch_size, obs_len - 1, self.hidden_size).to(device)  # (batch_size, obs_len-1, hidden_size)ht = torch.zeros(self.n_layers, batch_size, self.hidden_size).to(device)  # (num_layers, batch_size, hidden_size)for t in range(obs_len):xt = xconcat[:, t, :].view(batch_size, 1, -1)  # (batch_size, 1, hidden_size)out, ht = self.rnn(xt, ht)  # ht size (num_layers, batch_size, hidden_size)htt = ht[-1, :, :]  # (batch_size, hidden_size)if t != obs_len - 1:H[:, t, :] = httH = self.relu(H)  # (batch_size, obs_len-1, hidden_size)x = self.linear(H)return x[:, -self.pre_len:, :]def train(model, args, scaler, device):start_time = time.time()  # 计算起始时间model = modelloss_function = nn.MSELoss()optimizer = torch.optim.Adam(model.parameters(), lr=0.005)epochs = args.epochsmodel.train()  # 训练模式results_loss = []for i in tqdm(range(epochs)):losss = []for seq, labels in train_loader:optimizer.zero_grad()y_pred = model(seq)single_loss = loss_function(y_pred, labels)single_loss.backward()optimizer.step()losss.append(single_loss.detach().cpu().numpy())tqdm.write(f"\t Epoch {i + 1} / {epochs}, Loss: {sum(losss) / len(losss)}")results_loss.append(sum(losss) / len(losss))torch.save(model.state_dict(), 'save_model.pth')time.sleep(0.1)# valid_loss = valid(model, args, scaler, valid_loader)# 尚未引入学习率计划后期补上# 保存模型print(f">>>>>>>>>>>>>>>>>>>>>>模型已保存,用时:{(time.time() - start_time) / 60:.4f} min<<<<<<<<<<<<<<<<<<")plot_loss_data(results_loss)def valid(model, args, scaler, valid_loader):lstm_model = model# 加载模型进行预测lstm_model.load_state_dict(torch.load('save_model.pth'))lstm_model.eval()  # 评估模式losss = []for seq, labels in valid_loader:pred = lstm_model(seq)mae = calculate_mae(pred.detach().numpy().cpu(), np.array(labels.detach().cpu()))  # MAE误差计算绝对值(预测值  - 真实值)losss.append(mae)print("验证集误差MAE:", losss)return sum(losss) / len(losss)def test(model, args, test_loader, scaler):# 加载模型进行预测losss = []model = modelmodel.load_state_dict(torch.load('save_model.pth'))model.eval()  # 评估模式results = []labels = []for seq, label in test_loader:pred = model(seq)mae = calculate_mae(pred.detach().cpu().numpy(),np.array(label.detach().cpu()))  # MAE误差计算绝对值(预测值  - 真实值)losss.append(mae)pred = pred[:, 0, :]label = label[:, 0, :]pred = scaler.inverse_transform(pred.detach().cpu().numpy())label = scaler.inverse_transform(label.detach().cpu().numpy())for i in range(len(pred)):results.append(pred[i][-1])labels.append(label[i][-1])plt.figure(figsize=(10, 5))print("测试集误差MAE:", losss)# 绘制历史数据plt.plot(labels, label='TrueValue')# 绘制预测数据# 注意这里预测数据的起始x坐标是历史数据的最后一个点的x坐标plt.plot(results, label='Prediction')# 添加标题和图例plt.title("test state")plt.legend()plt.show()# 检验模型拟合情况
def inspect_model_fit(model, args, train_loader, scaler):model = modelmodel.load_state_dict(torch.load('save_model.pth'))model.eval()  # 评估模式results = []labels = []for seq, label in train_loader:pred = model(seq)[:, 0, :]label = label[:, 0, :]pred = scaler.inverse_transform(pred.detach().cpu().numpy())label = scaler.inverse_transform(label.detach().cpu().numpy())for i in range(len(pred)):results.append(pred[i][-1])labels.append(label[i][-1])plt.figure(figsize=(10, 5))# 绘制历史数据plt.plot(labels, label='History')# 绘制预测数据# 注意这里预测数据的起始x坐标是历史数据的最后一个点的x坐标plt.plot(results, label='Prediction')# 添加标题和图例plt.title("inspect model fit state")plt.legend()plt.show()def predict(model, args, device, scaler):# 预测未知数据的功能df = pd.read_csv(args.data_path)df = df.iloc[:, 1:][-args.window_size:].values  # 转换为nadarrypre_data = scaler.transform(df)tensor_pred = torch.FloatTensor(pre_data).to(device)tensor_pred = tensor_pred.unsqueeze(0)  # 单次预测 , 滚动预测功能暂未开发后期补上model = modelmodel.load_state_dict(torch.load('save_model.pth'))model.eval()  # 评估模式pred = model(tensor_pred)[0]pred = scaler.inverse_transform(pred.detach().cpu().numpy())# 假设 df 和 pred 是你的历史和预测数据# 计算历史数据的长度history_length = len(df[:, -1])# 为历史数据生成x轴坐标history_x = range(history_length)plt.figure(figsize=(10, 5))# 为预测数据生成x轴坐标# 开始于历史数据的最后一个点的x坐标prediction_x = range(history_length - 1, history_length + len(pred[:, -1]) - 1)# 绘制历史数据plt.plot(history_x, df[:, -1], label='History')# 绘制预测数据# 注意这里预测数据的起始x坐标是历史数据的最后一个点的x坐标plt.plot(prediction_x, pred[:, -1], marker='o', label='Prediction')plt.axvline(history_length - 1, color='red')  # 在图像的x位置处画一条红色竖线# 添加标题和图例plt.title("History and Prediction")plt.legend()if __name__ == '__main__':parser = argparse.ArgumentParser(description='Time Series forecast')parser.add_argument('-model', type=str, default='RNN', help="模型持续更新")parser.add_argument('-window_size', type=int, default=126, help="时间窗口大小, window_size > pre_len")parser.add_argument('-pre_len', type=int, default=24, help="预测未来数据长度")# dataparser.add_argument('-shuffle', action='store_true', default=True, help="是否打乱数据加载器中的数据顺序")parser.add_argument('-data_path', type=str, default='ETTh1-Test.csv', help="你的数据数据地址")parser.add_argument('-target', type=str, default='OT', help='你需要预测的特征列,这个值会最后保存在csv文件里')parser.add_argument('-input_size', type=int, default=7, help='你的特征个数不算时间那一列')parser.add_argument('-feature', type=str, default='MS', help='[M, S, MS],多元预测多元,单元预测单元,多元预测单元')parser.add_argument('-model_dim', type=list, default=[64, 128, 256], help='这个地方是这个TCN卷积的关键部分,它代表了TCN的层数我这里输''入list中包含三个元素那么我的TCN就是三层,这个根据你的数据复杂度来设置''层数越多对应数据越复杂但是不要超过5层')# learningparser.add_argument('-lr', type=float, default=0.001, help="学习率")parser.add_argument('-drop_out', type=float, default=0.05, help="随机丢弃概率,防止过拟合")parser.add_argument('-epochs', type=int, default=20, help="训练轮次")parser.add_argument('-batch_size', type=int, default=16, help="批次大小")parser.add_argument('-save_path', type=str, default='models')# modelparser.add_argument('-hidden_size', type=int, default=128, help="隐藏层单元数")parser.add_argument('-kernel_sizes', type=int, default=3)parser.add_argument('-laryer_num', type=int, default=2)# deviceparser.add_argument('-use_gpu', type=bool, default=True)parser.add_argument('-device', type=int, default=0, help="只设置最多支持单个gpu训练")# optionparser.add_argument('-train', type=bool, default=True)parser.add_argument('-test', type=bool, default=True)parser.add_argument('-predict', type=bool, default=True)parser.add_argument('-inspect_fit', type=bool, default=True)parser.add_argument('-lr-scheduler', type=bool, default=True)args = parser.parse_args()if isinstance(args.device, int) and args.use_gpu:device = torch.device("cuda:" + f'{args.device}')else:device = torch.device("cpu")print("使用设备:", device)train_loader, test_loader, valid_loader, scaler = create_dataloader(args, device)if args.feature == 'MS' or args.feature == 'S':args.output_size = 1else:args.output_size = args.input_size# 实例化模型try:print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始初始化{args.model}模型<<<<<<<<<<<<<<<<<<<<<<<<<<<")model = RNNs(args.input_size, args.output_size, args.hidden_size, args.laryer_num, args.pre_len).to(device)print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始初始化{args.model}模型成功<<<<<<<<<<<<<<<<<<<<<<<<<<<")except:print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始初始化{args.model}模型失败<<<<<<<<<<<<<<<<<<<<<<<<<<<")# 训练模型if args.train:print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始{args.model}模型训练<<<<<<<<<<<<<<<<<<<<<<<<<<<")train(model, args, scaler, device)if args.test:print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始{args.model}模型测试<<<<<<<<<<<<<<<<<<<<<<<<<<<")test(model, args, test_loader, scaler)if args.inspect_fit:print(f">>>>>>>>>>>>>>>>>>>>>>>>>开始检验{args.model}模型拟合情况<<<<<<<<<<<<<<<<<<<<<<<<<<<")inspect_model_fit(model, args, train_loader, scaler)if args.predict:print(f">>>>>>>>>>>>>>>>>>>>>>>>>预测未来{args.pre_len}条数据<<<<<<<<<<<<<<<<<<<<<<<<<<<")predict(model, args, device, scaler)plt.show()


六、训练模型 

我们配置好所有参数之后就可以开始训练模型了,根据我前面讲解的参数部分进行配置,不懂得可以评论区留言。


七、预测结果

7.1 预测未知数据效果图

RNN的预测效果图(这里我只预测了未来24个时间段的值为未来一天的预测值)->


7.2 测试集效果图 

测试集上的表现->


7.3 CSV文件生成效果图 

同时我也可以将输出结果用csv文件保存,但是功能还没有做,我在另一篇informer的文章里实习了这个功能大家如果有需要可以评论区留言,有时间我会移植过来,最近一直在搞图像领域的文章因为时间序列看的人还是太少了。

另一篇文章链接->时间序列预测实战(十九)魔改Informer模型进行滚动长期预测(科研版本,结果可视化)

将滚动预测结果生成了csv文件方便大家对比和评估,以下是我生成的csv文件可以说是非常的直观。

 我们可以利用其进行画图从而评估结果-> 


7.4 检验模型拟合效果图

检验模型拟合情况->

(从下面的图片可以看出模型拟合的情况很好,估计是我发这么多里面拟合效果最好的了) 


八、全文总结

 到此本文的正式分享内容就结束了,在这里给大家推荐我的时间序列专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的模型进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~)如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

 专栏回顾: 时间序列预测专栏——持续复习各种顶会内容——科研必备

如果大家有不懂的也可以评论区留言一些报错什么的大家可以讨论讨论看到我也会给大家解答如何解决!最后希望大家工作顺利学业有成!

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

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

相关文章

【Java代码接口自动化测试】REST Assured接口测试 HTTPClient接口测试

近几年接口自动化变得越来越热门&#xff0c;相对比于UI自动化&#xff0c;接口自动化有一些优势 1.运行比UI更稳定&#xff0c;让BUG更容易定位 2.UI自动化维护成本太高&#xff0c;接口相对低一些 接口测试其实有很多方式&#xff0c;主要有两种&#xff0c;一个是工具&am…

JM中ref_pic_list_modification bug记录

问题描述 今天在用JM对YUV420p编码时,发现编出的码流用ffplay播放花屏,报如下错误: JM的版本时19.1,没有使能B帧,PicOrderCntType设置为2,其它都是encoder.cfg中的默认配置。我用一些码流分析工具播放H264码流正常,用一些播放器播放也都存在花屏,不过大多数播放器都是…

k8s集群部分使用gpu资源的pod出现UnexpectedAdmissionError问题

记录一次排查UnexpectedAdmissionError问题的过程 1. 问题 环境 3master节点N个GPU节点 kubelet版本&#xff1a;v1.19.4 kubernetes版本&#xff1a;v1.19.4 生产环境K8S集群&#xff0c;莫名其妙的出现大量UnexpectedAdmissionError状态的Pod&#xff0c;导致部分任务执…

12.07

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口设置//去掉表头this->setWindowFlags(Qt::FramelessWindowHint);//重新设置大小this->resize(800,420);//设置背景颜色this->setStyleSheet("background-color:whi…

【推荐系统】了解推荐系统的生态(重点:推荐算法的主要分类)

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍推荐系统的关键元素和思维模式、推荐算法的主要分类、推荐系统常见的问题、推荐系统效果评测。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一…

javaee实验:Spring Boot 整合 Mybatis

目录 MybatisMyBatis 框架简介Mybatis 框架执行流程图映射器 实验目的实验内容实验过程数据库准备项目结构代码实现 实验结果 Mybatis MyBatis 框架简介 Mybatis 的前身是 Apache 的开源框架 iBatis&#xff0c;与 Hibernate 一样是一个 Java 持久层的框 架。Mybatis 的优势在…

使用Python实现的Excel像素画

简介&#xff1a;本项目主要使用python语言&#xff0c;将图片转为 Excel&#xff0c;图片中的每一个像素转化为 Excel 中的每一个单元格。主要使用pillow和xlsxwriter这两个模块。项目使用一个python文件即可。 一&#xff1a;项目功能和流程介绍 项目的主要功能&#xff1a…

Python-封装配置文件

Code [url] baidu http://www.baidu.com[value] send_value 百度[server] ip 220.181.111.188封装的格式可以套用 # 封装,类似函数调用 import configparserclass ReadConfigIni():def __init__(self,filename):self.cf configparser.ConfigParser()self.cf.read(filenam…

Pr项目标准化ProjectNormalizer插件|解决PR剪辑视频在Windows和Mac电脑切换打开pr项目工程文件需要重新链接媒体问题

当我们在 Windows 中打开在 Mac 上剪辑视频的 Premiere Pro 项目文件时&#xff0c;需要重新链接媒体。通常&#xff0c;如果选中“自动重新链接其他人”复选框&#xff0c;媒体将在某种程度上链接在一起。但是&#xff0c;有时这是行不通的&#xff0c;并且可能是一个非常困难…

java获取ip的工具类

java获取ip的工具类 直接上代码 package com.loit.park.common.utils;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest; import java.net.InetAddress; import java.net.UnknownHostException;/*** author hanjinqun*…

百面嵌入式专栏(岗位分析)大疆嵌入式工程师【通信/流媒体】

文章目录 一、岗位简介二、解析2.1、网络协议2.2、音视频传输算法2.3、大规模音视频会议或直播系统 三、简历 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; &#x1f4e2;本篇我们将对大疆嵌入式工程师【通信/流媒体】岗位进行分析 。 一、…

视频封面提取:精准截图,如何从指定时长中提取某一帧图片

在视频制作和分享过程中&#xff0c;一个有吸引力的封面或截图往往能吸引更多的观众点击观看。有时候要在特定的时间段内从视频中提取一帧作为封面或截图。如果每个视频都手动提取的话就会耗费很长时间&#xff0c;那么如何智化能批量提取呢&#xff1f;现在一起来看下云炫AI智…

用户案例|Milvus 助力 Credal.AI 实现 GenAI 安全与可控

AIGC 时代&#xff0c;企业流程中是否整合人工智能&#xff08;AI&#xff09;对于的企业竞争力至关重要。然而&#xff0c;随着 AI 不断发展演进&#xff0c;企业也在此过程中面临数据安全管理、访问权限、数据隐私等方面的挑战。 为了更好地解决上述问题&#xff0c;Credal.A…

0011Java程序设计-ssm药店管理系统微信小程序

文章目录 摘 要目 录系统实现5.2服务端开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;776871563 摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机…

利用eclipse导入外部java工程

利用eclipse导入外部java工程&#xff0c;打开eclipse&#xff0c;依次点击File-Import&#xff0c;…按下图依次执行…

粒子库particles.vue3在项目中的使用

一、particles.vue3是什么 particles.vue3 是一个 Vue 3 的组件库&#xff0c;用于在 Vue 3 项目中创建和管理粒子效果。它基于 tsparticles 引擎&#xff0c;提供了一系列的 Vue 组件&#xff0c;使我们能够轻松地在应用程序中添加动态的粒子效果。   如果您正在开发一个 V…

Xilinx FPGA——ISE时序约束“建立时间不满足”问题解决记录

一、现象 最近使用赛灵思的FPGA设计项目时&#xff0c;出现时序约束失效问题。 点进去发现如下&#xff1a; 一个始终约束没有生效&#xff0c;有多处报错。 二、原因 出现这个问题的原因是&#xff0c;建立时间不满足。 时序违例的主要原因是建立时间和保持时间不满足要求&a…

Spark RDD惰性计算的自主优化

原创/朱季谦 RDD&#xff08;弹性分布式数据集&#xff09;中的数据就如final定义一般&#xff0c;只可读而无法修改&#xff0c;若要对RDD进行转换或操作&#xff0c;那就需要创建一个新的RDD来保存结果。故而就需要用到转换和行动的算子。 Spark运行是惰性的&#xff0c;在…

【Python】Python仓储管理系统(源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

uni-app 微信小程序之好看的ui登录页面(二)

文章目录 1. 页面效果2. 页面样式代码 更多登录ui页面 uni-app 微信小程序之好看的ui登录页面&#xff08;一&#xff09; uni-app 微信小程序之好看的ui登录页面&#xff08;二&#xff09; uni-app 微信小程序之好看的ui登录页面&#xff08;三&#xff09; uni-app 微信小程…