如何将Casadi MX变量用于PyTorch nn.Conv1d卷积操作?
解决方案:Casadi集成含Conv1d的神经网络做MPC
方案1:手动在Casadi中实现Conv1d逻辑
卷积操作的本质是滑动窗口内的加权求和加偏置,可以直接用Casadi的原生操作复现,步骤如下:
- 从PyTorch的Conv1d层中提取训练好的权重和偏置:
import torch import casadi as cs # 假设你的Conv1d层是conv_layer conv_layer = torch.nn.Conv1d(in_channels=3, out_channels=2, kernel_size=2, stride=1, padding=0) # 提取权重(形状:out_channels × in_channels × kernel_size) weight_np = conv_layer.weight.detach().numpy() # 提取偏置(形状:out_channels) bias_np = conv_layer.bias.detach().numpy() - 将参数转为Casadi的MX常量:
weight_cs = cs.MX(weight_np) bias_cs = cs.MX(bias_np) - 实现卷积计算逻辑(以输入为
x_cs,形状为(in_channels, seq_len)为例):def casadi_conv1d(x, weight, bias, stride=1): in_channels, seq_len = x.shape out_channels, _, kernel_size = weight.shape out_seq_len = (seq_len - kernel_size) // stride + 1 output = [] for out_idx in range(out_channels): channel_output = [] for pos in range(0, seq_len - kernel_size + 1, stride): # 取当前窗口的输入 window = x[:, pos:pos+kernel_size] # 加权求和 val = cs.sum1(weight[out_idx, :, :] * window) + bias[out_idx] channel_output.append(val) output.append(cs.vertcat(*channel_output)) return cs.horzcat(*output) - 替换原Sequential中的Conv1d层为上述Casadi实现即可,确保整个模型的计算都基于Casadi MX变量。
方案2:将Conv1d等价转换为全连接层(输入序列长度固定时适用)
如果MPC中输入的状态序列长度是固定值,可以把卷积层展开成等价的全连接层,直接用l4casadi处理:
- 假设输入序列长度为
L,Conv1d的kernel_size=K,stride=1,padding=0,每个输出神经元对应输入中连续的K个元素。 - 把Conv1d的权重矩阵
(out_channels, in_channels, K)重排为全连接层的权重(out_channels*(L-K+1), in_channels*L),对应每个输出位置的窗口权重。 - 代码示例:
import torch.nn as nn import numpy as np def conv_to_linear(conv_layer, input_seq_len): out_channels, in_channels, kernel_size = conv_layer.weight.shape out_seq_len = input_seq_len - kernel_size + 1 # 初始化全连接层 linear_layer = nn.Linear(in_channels * input_seq_len, out_channels * out_seq_len) # 重排权重 weight_np = conv_layer.weight.detach().numpy() new_weight = np.zeros((out_channels * out_seq_len, in_channels * input_seq_len)) for out_c in range(out_channels): for seq_pos in range(out_seq_len): row_idx = out_c * out_seq_len + seq_pos for in_c in range(in_channels): col_start = in_c * input_seq_len + seq_pos new_weight[row_idx, col_start:col_start+kernel_size] = weight_np[out_c, in_c, :] # 赋值权重和偏置 linear_layer.weight.data = torch.tensor(new_weight, dtype=torch.float32) linear_layer.bias.data = conv_layer.bias.repeat(out_seq_len) return linear_layer - 转换后,用l4casadi将全连接层转为Casadi兼容的函数,注意输入要先展平为一维向量。
方案3:Casadi自定义操作封装(仅前向预测场景适用)
如果MPC只需要神经网络的前向预测,不需要对网络参数求导,可以用Casadi的自定义操作封装PyTorch的前向传播:
def torch_forward(x_np): x_tensor = torch.tensor(x_np, dtype=torch.float32).unsqueeze(0) # 增加batch维度 with torch.no_grad(): return your_sequential_model(x_tensor).squeeze(0).numpy() # 封装为Casadi函数 casadi_nn = cs.Function('nn', [cs.MX.sym('x', in_channels*seq_len)], [cs.MX(torch_forward(cs.MX.sym('x', in_channels*seq_len)))])
- 注意:该方法无法自动计算关于输入的梯度,若MPC需要对神经网络的输入(状态/控制量)求导,此方法不适用。
内容的提问来源于stack exchange,提问作者ryan xuan
相关产品推荐
相关产品推荐

