基于粒子群优化的联邦学习电力消耗预测模型

本文介绍了一种基于粒子群优化的联邦学习电力消耗预测模型。该模型利用 LSTM 网络进行预测,并使用 PSO 算法对模型参数进行优化。

代码示例:

import os
import random
import numpy as np
import pandas as pd

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data as Data

from sklearn.model_selection import train_test_split
from tqdm import tqdm

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 加载数据集
df = pd.read_csv('Electric_Power_Consumption.csv', delimiter=';',
                 parse_dates={'dt': ['Date', 'Time']}, infer_datetime_format=True,
                 na_values=['nan', '?'], index_col='dt')

# 填充缺失值
df.fillna(method='ffill', inplace=True)

# 重采样为每小时数据
df = df.resample('H').mean()

# 数据归一化
df = (df - df.mean()) / df.std()

# 将数据分为训练集和测试集
train_df, test_df = train_test_split(df, test_size=0.2, shuffle=False)

# 将数据转换为长度为 seq_len+timestep_pred 的序列
seq_len = 24 * 7  # 一周
timestep_pred = 24  # 预测接下来的 24 小时
train_data = []
for i in range(seq_len, len(train_df) - timestep_pred):
    train_data.append(train_df.iloc[i - seq_len:i].values.tolist())

train_data = np.array(train_data)
train_labels = train_df.iloc[seq_len + timestep_pred:].values

test_data = []
for i in range(seq_len, len(test_df) - timestep_pred):
    test_data.append(test_df.iloc[i - seq_len:i].values.tolist())

test_data = np.array(test_data)
test_labels = test_df.iloc[seq_len + timestep_pred:].values

print('训练集形状:train_data: %s 和 train_labels: %s' % (train_data.shape, train_labels.shape))
print('测试集形状:test_data: %s 和 test_labels: %s' % (test_data.shape, test_labels.shape))

train_y_ts = torch.from_numpy(train_labels).float().to(device)
train_X_ts = torch.from_numpy(train_data).float().to(device)

test_y_ts = torch.from_numpy(test_labels).float().to(device)
test_X_ts = torch.from_numpy(test_data).float().to(device)

train_set = Data.TensorDataset(train_X_ts, train_y_ts)
test_set = Data.TensorDataset(test_X_ts, test_y_ts)

num_clients = 10
num_selected = 10
num_rounds = 29
epochs = 1
batch_size = 64

traindata_split = torch.utils.data.random_split(train_set, [int(train_labels.shape[0] / num_clients)] * (num_clients - 1) + [train_labels.shape[0] - int(train_labels.shape[0] / num_clients) * (num_clients - 1)])

train_loader = [torch.utils.data.DataLoader(x, batch_size=batch_size, shuffle=True) for x in traindata_split]
test_loader = torch.utils.data.DataLoader(test_set, batch_size=batch_size, shuffle=True)


# 定义 LSTM 模型
class LSTM(nn.Module):
    def __init__(self, input_dim, hidden_dim, seq_len, num_layers=2):
        super(LSTM, self).__init__()
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.seq_len = seq_len
        self.num_layers = num_layers

        self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim * seq_len, 1)

    def forward(self, x):
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).to(device)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_dim).to(device)

        out, (hn, cn) = self.lstm(x, (h0, c0))
        out = out.reshape(out.size(0), -1)
        out = self.fc(out)

        return out


# 定义粒子
class Particle:
    def __init__(self, model):
        # self.position = model.get_params().detach().clone()
        self.position = model.parameters().__next__().data.detach().clone()
        self.velocity = torch.zeros_like(self.position)
        self.best_position = self.position.detach().clone()
        self.best_loss = float('inf')


# 定义 PSO 算法
class PSO:
    def __init__(self, model, criterion, lr=0.01, momentum=0.8, weight_decay=0.1):
        self.model = model
        self.criterion = criterion
        self.lr = lr
        self.momentum = momentum
        self.weight_decay = weight_decay
        self.particles = [Particle(model) for _ in range(num_selected)]

    def step(self):
        for i in range(num_selected):
            # 更新速度
            self.particles[i].velocity = self.momentum * self.particles[i].velocity \
                                         + 2 * torch.rand_like(self.particles[i].position) * (
                                                     self.particles[i].best_position - self.particles[i].position) \
                                         + 2 * torch.rand_like(self.particles[i].position) * (
                                                     global_best_position - self.particles[i].position)

            # 限制速度
            self.particles[i].velocity.clamp_(-1, 1)

            # 更新位置
            self.particles[i].position += self.lr * self.particles[i].velocity

            # 限制位置
            self.particles[i].position.clamp_(-1, 1)

            # 评估新位置的适应度
            self.model.set_params(self.particles[i].position)
            # loss = self.evaluate_fitness()
            # model_param = iter(self.model.parameters())
            # for param in model_param:
            #     param.data.copy_(selected_particles[i].position)
            loss = self.evaluate_fitness()

            # 更新个人最佳
            if loss < self.particles[i].best_loss:
                self.particles[i].best_position = self.particles[i].position.detach().clone()
                self.particles[i].best_loss = loss

    def evaluate_fitness(self):
        total_loss = 0.0
        with torch.no_grad():
            for data, target in test_loader:
                output = self.model(data)
                loss = self.criterion(output, target)
                total_loss += loss.item() * len(data)

        return total_loss / len(test_set)

    def get_best(self):
        best_particle = min(self.particles, key=lambda x: x.best_loss)
        return best_particle.best_position, best_particle.best_loss


# 使用 PSO 训练模型
input_dim = train_data.shape[2]
hidden_dim = 128
model = LSTM(input_dim, hidden_dim, seq_len).to(device)
criterion = nn.MSELoss()
optimizer_pso = PSO(model=model, criterion=criterion)

global_best_loss = float('inf')
global_best_position = None

for r in range(num_rounds):
    selected_particles = random.sample(optimizer_pso.particles, num_selected)

    for i in range(num_selected):
        # optimizer = optim.Adam(selected_particles[i].position, lr=0.01, weight_decay=0.1)
        optimizer = optim.Adam(model.parameters(), lr=0.01, weight_decay=0.1)
        # model.set_params(selected_particles[i].position)
        model_param = iter(model.parameters())
        for param in model_param:
            param.data.copy_(selected_particles[i].position)

        for epoch in range(epochs):
            for data, target in tqdm(train_loader[i], desc='训练轮次 %d/%d 客户端 %d/%d' % (r + 1, num_rounds, i + 1, num_clients)):
                optimizer.zero_grad()
                output = model(data)
                loss = criterion(output, target)
                loss.backward()
                # 更新个人最佳
                selected_particles[i].best_position = selected_particles[i].position.detach().clone()
                selected_particles[i].best_loss = optimizer_pso.evaluate_fitness()

                # 更新全局最佳
                if selected_particles[i].best_loss < global_best_loss:
                    global_best_loss = selected_particles[i].best_loss
                    global_best_position = selected_particles[i].best_position.detach().clone()

                optimizer.step()
                # 更新个人最佳
                selected_particles[i].best_position = selected_particles[i].position.detach().clone()
                selected_particles[i].best_loss = optimizer_pso.evaluate_fitness()

                # 更新全局最佳
                if selected_particles[i].best_loss < global_best_loss:
                    global_best_loss = selected_particles[i].best_loss
                    global_best_position = selected_particles[i].best_position.detach().clone()

            # 更新粒子群
            optimizer_pso.step()

            # 打印进度
            print('轮次 %d/%d,全局最佳损失:%.4f' % (r + 1, num_rounds, global_best_loss))

错误分析:

错误提示显示在代码的第 180 行出现了错误,即参数 tensor a 和 tensor b 的维度不匹配。具体来说,tensor a 的大小为 128,而 tensor b 的大小为 7,这意味着它们在第二个维度上不匹配。

解决方案:

需要检查第 180 行代码,找到 param.data.copy_(selected_particles[i].position) 这行代码,并检查 selected_particles[i].position 的大小是否正确,是否与 param 的大小匹配。如果不匹配,则需要调整 selected_particles[i].position 的大小,使其与 param 的大小相同。

具体操作:

  1. 检查 selected_particles[i].position 的大小,确定其第二个维度的尺寸是否为 128。
  2. 如果 selected_particles[i].position 的大小不为 128,则需要修改代码,使其大小与 param 的大小一致。例如,可以使用 torch.reshape() 函数将 selected_particles[i].position 调整为大小为 (128) 的张量。

修改后的代码:

# ...
for param in model_param:
    param.data.copy_(torch.reshape(selected_particles[i].position, (128)))
# ...

总结:

通过检查代码并修改 selected_particles[i].position 的大小,可以解决代码中的错误。

模型介绍:

该模型使用 LSTM 网络进行电力消耗预测,并使用 PSO 算法对模型参数进行优化。

  • LSTM 网络 是一种循环神经网络,能够处理时间序列数据,并在电力消耗预测方面表现出色。
  • PSO 算法 是一种优化算法,通过模拟鸟群觅食的行为来寻找最优解,可以用于优化 LSTM 模型的参数。
  • 联邦学习 是一种分布式机器学习方法,能够在多个客户端上训练模型,并保护数据隐私。

该模型将这三种技术结合起来,能够有效地提高电力消耗预测的准确性和效率。

模型优点:

  • 能够有效地处理时间序列数据,提高预测精度。
  • 使用 PSO 算法优化模型参数,能够找到更优的模型参数,提高模型性能。
  • 利用联邦学习,能够在保护数据隐私的前提下,提高模型训练效率。

应用场景:

该模型可以应用于电力系统中的多种场景,例如:

  • 电力负荷预测:预测未来一段时间内的电力负荷,为电力调度提供依据。
  • 电力市场交易:预测电力价格,为电力市场交易提供决策支持。
  • 电力设备维护:预测电力设备的运行状态,为电力设备维护提供预警。

未来方向:

未来可以研究以下方向,进一步提高模型的性能:

  • 研究更复杂的 LSTM 网络结构,提高模型的表达能力。
  • 使用其他优化算法,例如遗传算法,进一步优化模型参数。
  • 研究更先进的联邦学习方法,提高模型训练效率。

参考文献:

[1] S. Hochreiter and J. Schmidhuber. “Long short-term memory.” Neural computation 9.8 (1997): 1735-1780. [2] J. Kennedy and R. Eberhart. “Particle swarm optimization.” Proceedings of ICNN’95-International Conference on Neural Networks. Vol. 4. IEEE, 1995. [3] B. McMahan, E. Moore, D. Ramage, S. Hampson, and B. A. y Arcas. “Communication-efficient learning of deep networks from decentralized data.” Artificial intelligence and statistics. PMLR, 2017.


原文地址: https://www.cveoy.top/t/topic/nFNG 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录