时间序列预测实战(十五)PyTorch实现GRU模型长期预测并可视化结果

news2024/11/16 19:51:45

往期回顾:时间序列预测专栏——包含上百种时间序列模型带你从入门到精通时间序列预测

一、本文介绍

本文讲解的实战内容是GRU(门控循环单元),本文的实战内容通过时间序列领域最经典的数据集——电力负荷数据集为例,深入的了解GRU的基本原理和框架,GRU是时间序列领域最常见的Cell之一,其相对于LSTM需要的参数量更少结构也更加简单,经常用于复杂的模型的过度单元,本文的讲解内容包括详细的代码讲解,带你一行一行的理解整个项目的流程,从而对整个项目有一个深入的了解,如果你是时间序列领域的新人,这篇文章可以带你入门时间序列领域并对时间序列的流程有一个详细的了解。

预测类型->单元预测、多元预测、长期预测

代码地址->文末提供复制粘贴即可运行的代码块

二、框架原理介绍

1.GRU的基本原理

GRU(门控循环单元)是一种循环神经网络(RNN)的变体,主要用于处理序列数据,它的基本原理可以概括如下:

  1. 门控机制:GRU的核心是门控机制,包括更新门(update gate)和重置门(reset gate)。这些门控制着信息的流动,即决定哪些信息应该被保留,哪些应该被遗忘。

  2. 更新门:更新门帮助模型决定过去的信息有多少需要保留到当前状态。它是通过当前输入和前一个隐状态计算得出的,用于调节隐状态的更新程度。

  3. 重置门:重置门决定了多少过去的信息需要被忘记。它同样依赖于当前输入和前一个隐状态的信息。当重置门接近0时,模型会“忘记”过去的隐状态,只依赖于当前输入。

  4. 当前隐状态的计算:利用更新门和重置门的输出,结合前一隐状态和当前输入,GRU计算出当前的隐状态。这个隐状态包含了序列到目前为止的重要信息。

  5. 输出:GRU的最终输出通常是在序列的每个时间步上产生的,或者在序列的最后一个时间步产生,取决于具体的应用场景。

总结:GRU相较于传统的RNN,其优势在于能够更有效地处理长序列数据,减轻了梯度消失的问题。同时,它通常比LSTM(长短期记忆网络)更简单,因为它有更少的参数。

1.1GRU的基本框架

​​

上面的图片为一个GRU的基本结构图,解释如下->

  • 更新门(z) 在决定是否用新的隐藏状态更新当前隐藏状态时扮演重要角色。
  • 重置门(r) 决定是否忽略之前的隐藏状态。

这些部分是GRU的核心组成,它们共同决定了网络如何在序列数据中传递和更新信息,这对于时间序列分析至关重要。

总结:这个 GRU真的是结构太简单了,没什么好讲解的,如果你是时间序列预测的新手这篇文章能够帮助你很好的入门时间序列并且能够对时间序列的整体流程有一个完整的了解如果你是大神这边文章可能并不能给你带来太多的帮助。

三、数据集介绍

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

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

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

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

​​

四、项目的全部代码

import time
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from torch.utils.data import DataLoader
import torch
from torch.utils.data import Dataset

# 随机数种子
np.random.seed(0)


class TimeSeriesDataset(Dataset):
    def __init__(self, sequences):
        self.sequences = sequences

    def __len__(self):
        return len(self.sequences)

    def __getitem__(self, index):
        sequence, label = self.sequences[index]
        return torch.Tensor(sequence), torch.Tensor(label)


def calculate_mae(y_true, y_pred):
    # 平均绝对误差
    mae = np.mean(np.abs(y_true - y_pred))
    return mae

"""
数据定义部分
"""
true_data = pd.read_csv('ETTh1.csv')  # 填你自己的数据地址,自动选取你最后一列数据为特征列


target = 'OT'  # 添加你想要预测的特征列
test_size = 0.15  # 训练集和测试集的尺寸划分
train_size = 0.85  # 训练集和测试集的尺寸划分
pre_len = 4  # 预测未来数据的长度
train_window = 32  # 观测窗口

# 这里加一些数据的预处理, 最后需要的格式是pd.series
true_data = np.array(true_data[target])

# 定义标准化优化器
scaler_train = MinMaxScaler(feature_range=(0, 1))
scaler_test = MinMaxScaler(feature_range=(0, 1))

# 训练集和测试集划分
train_data = true_data[:int(train_size * len(true_data))]
test_data = true_data[-int(test_size * len(true_data)):]
print("训练集尺寸:", len(train_data))
print("测试集尺寸:", len(test_data))

# 进行标准化处理
train_data_normalized = scaler_train.fit_transform(train_data.reshape(-1, 1))
test_data_normalized = scaler_test.fit_transform(test_data.reshape(-1, 1))

# 转化为深度学习模型需要的类型Tensor
train_data_normalized = torch.FloatTensor(train_data_normalized)
test_data_normalized = torch.FloatTensor(test_data_normalized)


def create_inout_sequences(input_data, tw, pre_len):
    # 创建时间序列数据专用的数据分割器
    inout_seq = []
    L = len(input_data)
    for i in range(L - tw):
        train_seq = input_data[i:i + tw]
        if (i + tw + 4) > len(input_data):
            break
        train_label = input_data[i + tw:i + tw + pre_len]
        inout_seq.append((train_seq, train_label))
    return inout_seq


# 定义训练器的的输入
train_inout_seq = create_inout_sequences(train_data_normalized, train_window, pre_len)
test_inout_seq = create_inout_sequences(test_data_normalized, train_window, pre_len)

# 创建数据集
train_dataset = TimeSeriesDataset(train_inout_seq)
test_dataset = TimeSeriesDataset(test_inout_seq)

# 创建 DataLoader
batch_size = 32  # 你可以根据需要调整批量大小
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, drop_last=True)


class GRU(nn.Module):
    def __init__(self, input_dim=1, hidden_dim=32, num_layers=1, output_dim=1, pre_len= 4):
        super(GRU, self).__init__()
        self.pre_len = pre_len
        self.num_layers = num_layers
        self.hidden_dim = hidden_dim
        # 替换 LSTM 为 GRU
        self.gru = nn.GRU(input_dim, hidden_dim,num_layers=num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.1)
    def forward(self, x):
        h0_gru = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).to(x.device)

        out, _ = self.gru(x, h0_gru)

        out = self.dropout(out)

        # 取最后 pre_len 时间步的输出
        out = out[:, -self.pre_len:, :]

        out = self.fc(out)
        out = self.relu(out)
        return out


lstm_model = GRU(input_dim=1, output_dim=1, num_layers=2, hidden_dim=train_window, pre_len=pre_len)
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(lstm_model.parameters(), lr=0.005)
epochs = 20
Train = True  # 训练还是预测

if Train:
    losss = []
    lstm_model.train()  # 训练模式
    for i in range(epochs):
        start_time = time.time()  # 计算起始时间
        for seq, labels in train_loader:
            lstm_model.train()
            optimizer.zero_grad()

            y_pred = lstm_model(seq)

            single_loss = loss_function(y_pred, labels)

            single_loss.backward()
            optimizer.step()
            print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
        losss.append(single_loss.detach().numpy())
    torch.save(lstm_model.state_dict(), 'save_model.pth')
    print(f"模型已保存,用时:{(time.time() - start_time) / 60:.4f} min")


else:
    # 加载模型进行预测
    lstm_model.load_state_dict(torch.load('save_model.pth'))
    lstm_model.eval()  # 评估模式
    results = []
    reals = []
    losss = []

    for seq, labels in test_loader:
        pred = lstm_model(seq)
        mae = calculate_mae(pred.detach().numpy(), np.array(labels))  # MAE误差计算绝对值(预测值  - 真实值)
        losss.append(mae)
        for j in range(batch_size):
            for i in range(pre_len):
                reals.append(labels[j][i][0].detach().numpy())
                results.append(pred[j][i][0].detach().numpy())

    reals = scaler_test.inverse_transform(np.array(reals).reshape(1, -1))[0]
    results = scaler_test.inverse_transform(np.array(results).reshape(1, -1))[0]
    print("模型预测结果:", results)
    print("预测误差MAE:", losss)

    plt.figure()
    plt.style.use('ggplot')

    # 创建折线图
    plt.plot(reals, label='real', color='blue')  # 实际值
    plt.plot(results, label='forecast', color='red', linestyle='--')  # 预测值

    # 增强视觉效果
    plt.grid(True)
    plt.title('real vs forecast')
    plt.xlabel('time')
    plt.ylabel('value')
    plt.legend()
    plt.savefig('test——results.png')

五、模型代码的详细讲解

整个代码的流程我会从模型的入口参数定义开始进行讲解, 然后顺序讲解在直到模型的结束。

true_data = pd.read_csv('ETTh1.csv')  # 填你自己的数据地址,自动选取你最后一列数据为特征列

这一步就是读取你的数据了~不给大家讲了主要是csv的格式数据。 

target = 'OT'  # 添加你想要预测的特征列
test_size = 0.15  # 训练集和测试集的尺寸划分
train_size = 0.85  # 训练集和测试集的尺寸划分
pre_len = 4  # 预测未来数据的长度
train_window = 32  # 观测窗口

这一步就是参数定义的部分,讲解我已经再代码里标注了出来,需要说说的就是,pre_len和train_window这两个参数,

其中pre_len就是你预测未来数据的长度,假设你有一百条数据你想知道未来多少条数据的信息就填多少。 

train_window是数据的观测窗口,就是你利用多少条数据去预测你定义的pre_len长度。

# 这里加一些数据的预处理, 最后需要的格式是pd.series
true_data = np.array(true_data[target])

这是提取出特征列,根据前面你定义的target。 

# 定义标准化优化器
scaler_train = MinMaxScaler(feature_range=(0, 1))
scaler_test = MinMaxScaler(feature_range=(0, 1))

# 训练集和测试集划分
train_data = true_data[:int(train_size * len(true_data))]
test_data = true_data[-int(test_size * len(true_data)):]
print("训练集尺寸:", len(train_data))
print("测试集尺寸:", len(test_data))

# 进行标准化处理
train_data_normalized = scaler_train.fit_transform(train_data.reshape(-1, 1))
test_data_normalized = scaler_test.fit_transform(test_data.reshape(-1, 1))

# 转化为深度学习模型需要的类型Tensor
train_data_normalized = torch.FloatTensor(train_data_normalized)
test_data_normalized = torch.FloatTensor(test_data_normalized)

这部分是定义优化器,我们的深度学习模型输入一般都是-1到1(虽然这不是必须的,但是如果你不进行标准化处理效果真是天差地别),然后是测试集和训练集的划分,和根据数据进行标准化处理的操作,并且将数据转化为tensor的格式(tensor是我们深度学习特有的数据格式)。

# 定义训练器的的输入
train_inout_seq = create_inout_sequences(train_data_normalized, train_window, pre_len)
test_inout_seq = create_inout_sequences(test_data_normalized, train_window, pre_len)

这一部分是重点!!!!!

时间序列的数据和其他领域的不一样他需要滑窗的数据形式,假设我有100条数据,前面定义的滑窗大小是32预测未来数据的长度是4那么他就会用32和4去滑动数据,

所以我们的到数据是多少呢就是100 - 32 - 4 =54条数据(每条数据包含32条观测数据和4个标签数据),这里必须理解大家这是时间序列的基础,他是不能够直接用Dataloader进行数据加载的。

# 创建数据集
train_dataset = TimeSeriesDataset(train_inout_seq)
test_dataset = TimeSeriesDataset(test_inout_seq)

# 创建 DataLoader
batch_size = 32  # 你可以根据需要调整批量大小
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, drop_last=True)

这部分是创建数据集和Dataloader数据加载器,利用Dataloader的好处是可以避免内存爆炸,但是我们时间序列的数据一般都不大不会有这种情况。

class GRU(nn.Module):
    def __init__(self, input_dim=1, hidden_dim=32, num_layers=1, output_dim=1, pre_len= 4):
        super(GRU, self).__init__()
        self.pre_len = pre_len
        self.num_layers = num_layers
        self.hidden_dim = hidden_dim
        # 替换 LSTM 为 GRU
        self.gru = nn.GRU(input_dim, hidden_dim,num_layers=num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.1)
    def forward(self, x):
        h0_gru = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).to(x.device)

        out, _ = self.gru(x, h0_gru)

        out = self.dropout(out)

        # 取最后 pre_len 时间步的输出
        out = out[:, -self.pre_len:, :]

        out = self.fc(out)
        out = self.relu(out)
        return out

这是模型的内部,就是一个简单的gru模型,我来说一下其中的通道数情况,我们输入的X是三维的分别是[batch_size, train_window, target数量], 这是我们输入x的情况,经过gru进行处理我们添加了一个dropout避免过拟合,然后取出了你想要预测长度的步长数据,最后经过全连接层进行一个结果输出,大家有兴趣建议还是debug一下我这么讲你是不能理解的,最好还是实际动手debug看一下其中的通道数变化情况。

lstm_model = GRU(input_dim=1, output_dim=1, num_layers=2, hidden_dim=train_window, pre_len=pre_len)
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(lstm_model.parameters(), lr=0.005)
epochs = 20
Train = True  # 训练还是预测

这里实例化了我们的模型,定义了MSE损失函数,和优化器Adam和训练轮次,其中的Train是来判断是否进行训练。

if Train:
    losss = []
    lstm_model.train()  # 训练模式
    for i in range(epochs):
        start_time = time.time()  # 计算起始时间
        for seq, labels in train_loader:
            lstm_model.train()
            optimizer.zero_grad()

            y_pred = lstm_model(seq)

            single_loss = loss_function(y_pred, labels)

            single_loss.backward()
            optimizer.step()
            print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
        losss.append(single_loss.detach().numpy())
    torch.save(lstm_model.state_dict(), 'save_model.pth')
    print(f"模型已保存,用时:{(time.time() - start_time) / 60:.4f} min")

 如果Train为True则开始训练执行上面的代码,这是一个标准pytorch框架下的训练过程就不给大家 说了,如果不能理解的话大家可以去补补基础,或者评论区问我我在给大家讲讲。

else:
    # 加载模型进行预测
    lstm_model.load_state_dict(torch.load('save_model.pth'))
    lstm_model.eval()  # 评估模式
    results = []
    reals = []
    losss = []

    for seq, labels in test_loader:
        pred = lstm_model(seq)
        mae = calculate_mae(pred.detach().numpy(), np.array(labels))  # MAE误差计算绝对值(预测值  - 真实值)
        losss.append(mae)
        for j in range(batch_size):
            for i in range(pre_len):
                reals.append(labels[j][i][0].detach().numpy())
                results.append(pred[j][i][0].detach().numpy())

如果Train为False时候则开始进行评估模式我们利用test的数据集进行测试评估训练模型,

 reals = scaler_test.inverse_transform(np.array(reals).reshape(1, -1))[0]
    results = scaler_test.inverse_transform(np.array(results).reshape(1, -1))[0]
    print("模型预测结果:", results)
    print("预测误差MAE:", losss)

    plt.figure()
    plt.style.use('ggplot')

    # 创建折线图
    plt.plot(reals, label='real', color='blue')  # 实际值
    plt.plot(results, label='forecast', color='red', linestyle='--')  # 预测值

    # 增强视觉效果
    plt.grid(True)
    plt.title('real vs forecast')
    plt.xlabel('time')
    plt.ylabel('value')
    plt.legend()
    plt.savefig('test——results.png')

 这一部分是我们预测值和真实值之间的对比,来确定我们预测的好坏,后面的结果分析会有展示。

六、模型的训练和预测

上面我把大多数的代码都讲了一便大家应该对整个过程有一个大致的了解下面来大家进行训练看看模型的结果。

6.1模型的训练

我们将我前面提供的全部代码块复制粘贴到随便一个.py的文件内然后将数据集和特征数填写进去,就可以开始训练模型了。

训练的过程中控制台会输出训练结果和损失,可以看到刚开始我们的损失非常的大,到训练结束之后我们的损失如下会变的非常小。

​​

 可以看到我们的模型损失只有0.010.5901一个批次下可以说模型的拟合效果是非常的好,我们下面来看一下模型的损失图像,可以看到我们模型拟合速度比较一般在20个epoch左右在完全拟合。

6.2模型的评估

经过训练之后我们可以开始进行模型的评估了。

6.2.1结果展示

下面的图片是模型的评估结果,其中评估数据大概有800条左右,评估了大概八百条数据,结果只能说太一般了。

6.2.2结果分析

这个模型结果只能说在意料之中,大家看其中的图像可以看到明显的数据滞后性,这一问题我在前面利用过ARIMA-LSTM进行解决进行了完美的解决,大家有兴趣可以去回去评估一下,这单个GRU模型结果在这样只能说是正常的情况。

全文总结

到此本文已经全部讲解完成了,希望能够帮助到大家,在这里也给大家推荐一些我其它的博客的时间序列实战案例讲解,其中有数据分析的讲解就是我前面提到的如何设置参数的分析博客,最后希望大家订阅我的专栏,本专栏均分文章均分98,并且免费阅读。

概念理解 

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

数据分析

时间序列预测中的数据分析->周期性、相关性、滞后性、趋势性、离群值等特性的分析方法

机器学习——难度等级(⭐⭐)

时间序列预测实战(四)(Xgboost)(Python)(机器学习)图解机制原理实现时间序列预测和分类(附一键运行代码资源下载和代码讲解)

深度学习——难度等级(⭐⭐⭐⭐)

时间序列预测实战(五)基于Bi-LSTM横向搭配LSTM进行回归问题解决

时间序列预测实战(七)(TPA-LSTM)结合TPA注意力机制的LSTM实现多元预测

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

时间序列预测实战(十一)用SCINet实现滚动预测功能(附代码+数据集+原理介绍)

时间序列预测实战(十二)DLinear模型实现滚动长期预测并可视化预测结果

Transformer——难度等级(⭐⭐⭐⭐)

时间序列预测模型实战案例(八)(Informer)个人数据集、详细参数、代码实战讲解

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

时间序列预测实战(十三)定制化数据集FNet模型实现滚动长期预测并可视化结果

时间序列预测实战(十四)Transformer模型实现长期预测并可视化结果(附代码+数据集+原理介绍)

个人创新模型——难度等级(⭐⭐⭐⭐⭐)

时间序列预测实战(十)(CNN-GRU-LSTM)通过堆叠CNN、GRU、LSTM实现多元预测和单元预测

传统的时间序列预测模型(⭐⭐)

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

时间序列预测实战(六)深入理解ARIMA包括差分和相关性分析

融合模型——难度等级(⭐⭐⭐)

时间序列预测实战(九)PyTorch实现融合移动平均和LSTM-ARIMA进行长期预测

​​​

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

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

相关文章

信驰达科技加入车联网联盟(CCC),推进数字钥匙发展与应用

CCC)的会员。 图 1 深圳信驰达正式成为车联网联盟(CCC)会员 车联网联盟(CCC)是一个跨行业组织,致力于推动智能手机与汽车连接解决方案的技术发展。CCC涵盖了全球汽车和智能手机行业的大部分企业,拥有150多家成员公司。CCC成员公司包括智能手机和汽车制造…

Elasticsearch docker-compose 使用 Logstash 从 JSON 文件中预加载数据

在我们创建 Elasticsearch 进行开发时,最简单的办法就是在本地使用 docker-compose 来一键部署一个 Elasticsearch 集群。有时,特别是在准备测试环境时,开发人员希望从一开始就创建包含一些测试数据的数据库容器。我们可以使用 Logstash 来很…

nodejs+vue+python+PHP+微信小程序-安卓-房产中介管理信息系统的设计与实现-计算机毕业设计

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

Day29力扣打卡

打卡记录 美丽塔 II(前后缀分解 单调栈) 链接 大佬的题解 class Solution:def maximumSumOfHeights(self, a: List[int]) -> int:n len(a)suf [0] * (n 1)st [n] # 哨兵s 0for i in range(n - 1, -1, -1):x a[i]while len(st) > 1 and …

OSG查看版本信息和32or64位

使用osgversiond命令; -h,显示帮助; osg使用了OpenThreads库,也可以查看OpenThreads的版本号; -r 或 -read,读取贡献者名单文件;没看到啥; 然后进入VS开发人员命令提示;…

一行JavaScrip可以做什么?

说在前面 JavaScript 提供了许多方便的方法和操作符来简化常见的任务,使得编程变得更加高效和便捷。无论是数学计算、字符串处理还是数据操作,JavaScript 都能帮助我们以简洁的方式实现所需功能。 代码 1、生成指定范围内的随机整数 const randomInt …

【CCF BDCI 2023】多模态多方对话场景下的发言人识别 Baseline

模型简介 本基线模型共分为三个部分: 基于CNN的判断每张人脸是否是说话人的模型;基于Transformer-Encoder的判断同一段对话中不同轮次的说话人关系的模型;和使用上述两个预测结果求解二次型优化问题的说话人识别求解器。 基于CNN的判断每…

OpenAI调查ChatGPT故障;向量搜索的优势与局限

🦉 AI新闻 🚀 OpenAI调查ChatGPT故障,发布新AI产品GPTs和GPT-4 Turbo 摘要:OpenAI的ChatGPT和其他服务出现故障,经过调查后发现是由于DDoS攻击导致的异常流量模式。OpenAI在首届开发者大会上发布了新的AI产品GPTs&am…

Seaborn数据可视化综合应用Basemap和Seaborn在线闯关_头歌实践教学平台

Seaborn数据可视化综合应用Basemap和Seaborn 第1关 Seaborn第2关 Seaborn图形介绍第3关 Basemap 第1关 Seaborn 任务描述 本关任务:编写一个绘制每个月销售总额的折线图。 编程要求 本关的编程任务是补全右侧上部代码编辑区内的相应代码,根据输入文件路…

Maven 插件统一修改聚合工程项目版本号

目录 引言直接修改 pom.xml 的版本号的问题Maven 插件修改版本号开源项目微服务商城项目前后端分离项目 引言 在Maven项目中,我们通常有两种常见的方式来修改版本号:直接在pom.xml文件中手动编辑和利用Maven插件进行版本号调整。 本文将比较这两种修改…

如何用Python实现图像拼接画(把一堆小图拼成大图)

诸神缄默不语-个人CSDN博文目录 在这里的图像拼接画指的是一张大图由很多小图组成,效果就像这样: 原理:将大图拆成很多小块,每一块计算平均颜色,用平均颜色最相近的小图来替代,就可以。 直接遍历就可以&…

No198.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

雷达波形及MATLAB仿真

文章目录 前言一、雷达波形二、Matlab 仿真1、SFW 的距离分辨率和距离模糊①、MATLAB 源码②、仿真结果 三、资源自取 前言 本文对雷达波形的内容以思维导图的形式呈现,有关仿真部分进行了讲解实现。 一、雷达波形 思维导图如下图所示,如有需求请到文章…

C#中.NET 6.0控制台应用通过EF访问已建数据库

目录 一、新建.NET 6.0控制台应用并建立数据库连接 二、下载并安装EF程序包 三、自动生成EF模型和上下文 1.Blog类模型 2.Post类模型 3.数据库上下文 四、设计自己的应用 VS2022的.NET6.0、.NET7.0框架下默认支持EF7(版本号7.0.13),除…

《QT从基础到进阶·二十五》界面假死处理

假如有这样一种情况,我们在主线程写了一个死循环,当程序运行到主线程的死循环代码后界面便卡死点了没有反应,这里提供几种方法处理界面假死的情况,保证比如主线程在执行死循环没有退出的时候点击界面不会卡死能继续执行其他功能。…

【c++】——类和对象(中)——实现完整的日期类

作者:chlorine 专栏:c专栏 我的花一定会开。 【学习目标】 拷贝复制——赋值运算符重载 目录 &#x1f393;运算符重载(-><...) &#x1f393;日期&天数 &#x1f393;前置和后置重载 我们完成了赋值运算符重载章节的学习&#xff0c;对operator关键字的使用有…

wpf devexpress项目中添加GridControl绑定数据

本教程讲解了如何添加GridControl到wpf项目中并且绑定数据 原文地址Lesson 1 - Add a GridControl to a Project and Bind it to Data | WPF Controls | DevExpress Documentation 1、使用 DevExpress Template Gallery创建一个新的空白mvvm应用程序&#xff0c;这个项目包括了…

算法笔记-第七章-队列

算法笔记-第七章-队列 队列的相关知识点c中队列queue用法队列的操作序列求和队列约瑟夫环-队列匹配队列 队列的相关知识点 大佬的讲解 c中队列queue用法 一&#xff1a;queue是一种容器转换器模板&#xff0c;调用#include< queue>即可使用队列类 二&#xff1a;使用q…

Python--集合----无序,去重,空集合只能用set()方法

集合&#xff08;set&#xff09;是一个无序的不重复元素序列。 特点&#xff1a;天生去重 无序 集合定义&#xff1a;在Python中&#xff0c;我们可以使用一对花括号 {} 或者 set()方法 来定义集合&#xff0c; 但是如果你 定义的集合是一个 空集合&#xf…

SpringCloud Alibaba(上):注册中心-nacos、负载均衡-ribbon、远程调用-feign

Nacos 概念&#xff1a;Nacos是阿里巴巴推出的一款新开源项目&#xff0c;它是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos致力于帮助用户发现、配置和管理微服务&#xff0c;它提供了一组简单易用的特性集&#xff0c;包括动态服务发现、服务配置…