使用神经网络拟合6项参数

1. 数据预处理

1.1 添加参数解析

为了方便管理模型和训练等参数,统一用参数解析。

def parse_args():
    """
    解析命令行参数并返回参数对象。

    返回:
    - args (argparse.Namespace): 解析后的参数对象
    """
    parser = argparse.ArgumentParser(description='命令行参数解析示例')

    # 添加参数
    parser.add_argument('--input', type=str, default='input.csv', help='输入文件的路径')
    parser.add_argument('--output', type=str, default='output.csv', help='输出文件的路径')
    parser.add_argument('--data_group', type=int, default=401, help='数据隔多少行划分一组')
    parser.add_argument('--test_size', type=float, default=0.1, help='测试集划分比例')
    # parser.add_argument('--batch_size', type=int, default=32, help='每批次的样本数量,默认值是 32')
    parser.add_argument('--batch_size', type=int, default=64, help='每批次的样本数量,默认值是 32')
    parser.add_argument('--epochs', type=int, default=50, help='训练的轮数,默认值是 10')
    parser.add_argument('--learning_rate', type=float, default=0.001, help='学习率,默认值是 0.001')
    parser.add_argument('--model_name', type=str, default='CNN_LSTM', help='选择模型,eg:LSTM、CNN_LSTM、Transformer')
    parser.add_argument('--predict_para', type=str, default='AAdl1', help='m1, AAdl1, AAdl2, PPa0, nn, ccd1, ccd2')

    # 解析参数
    args = parser.parse_args()

    return args

1.2 数据预处理逻辑

最近一个小项目需要搭建神经网络根据输入数据的特征去拟合对应的6项参数
输入是input.csv,一共是876987x2的两列数据,分别代表特征1:Var1,特征2:Var2;使用pandas库读取,并提取出实部(如果存在的话),然后将它转换为浮点数。
在这里插入图片描述
6项参数对应的真实值如下,维度为:2187x7,每一列分别对应一项参数的真实标签值。
在这里插入图片描述

def clean_complex_number(val):
    val = str(val)
    if 'i' in val:
        val = val.split('+')[0] if '+' in val else val.split('-')[0]
    return float(val)

input_df = pd.read_csv(args.input)
output_df = pd.read_csv(args.output)

#提取出实部(如果存在的话),然后将它转换为浮点数
input_df = input_df.applymap(clean_complex_number)
output_df = output_df.applymap(clean_complex_number)

接着,输入特征是876987x2,也就是876987行, 每401行分组,并将每组展平成一维数组,一共876987÷401=2187组,最后的输入特征处理维度为:2187x802(401x2)。full_df就是拼接上标签,就是维度:2187x809

#876987 x 2
num_features = input_df.shape[1]  # 特征数 2
#一共876987行, 每401行分组,并将每组展平成一维数组
#每组就是1x401x2 = 1x802
#一共876987÷401=2187组
grouped = input_df.groupby(input_df.index // args.data_group).apply(lambda x: x.values.ravel())
#2187x802
new_input_df = pd.DataFrame(grouped.tolist(), index=grouped.index)
new_input_df.columns = [f'feature_{i}' for i in range(args.data_group * num_features)]
#将 new_input_df 和 output_df 合并成一个完整的数据框 full_df。
#2187x809
full_df = pd.concat([new_input_df, output_df.reset_index(drop=True)], axis=1)

train_df, test_df = train_test_split(full_df, test_size=args.test_size, random_state=2024)

1.3 数据归一化及划分

接着,进行归一化处理:

scaler = StandardScaler()
train_features = train_df.iloc[:, :-output_df.shape[1]]
train_labels = train_df.iloc[:, -output_df.shape[1]:]

test_features = test_df.iloc[:, :-output_df.shape[1]]
test_labels = test_df.iloc[:, -output_df.shape[1]:]

X_train = scaler.fit_transform(train_features)
y_train = np.array(train_labels)
X_val = scaler.transform(test_features)
y_val = np.array(test_labels)

1.4 数据标签处理逻辑

由于第一参数的真实值只存在于400、420、430的这三种值,所以我们将其映射到0、1、2,这样更有利于模型的拟合。

def map_unique_values(data):
    """
    将数据中的唯一值映射到从0开始的整数。

    参数:
    data (numpy.ndarray): 输入的一维数据数组

    返回:
    numpy.ndarray: 映射后的数据数组
    """
    # 获取唯一值及其数量
    unique_values = np.unique(data)

    # 创建映射字典
    mapping = {val: idx for idx, val in enumerate(unique_values)}

    # 将数据映射到新的值
    mapped_data = np.array([mapping[val] for val in data])

    return mapped_data.reshape(-1, 1)
# 只对第一列数据进行映射
y_train[:, 0:1] = map_unique_values(y_train[:, 0])
y_val[:, 0:1] = map_unique_values(y_val[:, 0])

1.5 数据转torch

# 转换为 PyTorch 张量
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32)
X_val_tensor = torch.tensor(X_val, dtype=torch.float32)
y_val_tensor = torch.tensor(y_val, dtype=torch.float32)
# 创建数据加载器
if args.model_name != 'LSTM':
    X_train_tensor = X_train_tensor.unsqueeze(1)
    X_val_tensor = X_val_tensor.unsqueeze(1)
if args.model_name == 'Transformer':
    X_train_tensor = X_train_tensor.permute(0, 2, 1)  # 重新排列维度以适应 Transformer 输入
    X_val_tensor = X_val_tensor.permute(0, 2, 1)  # 重新排列维度以适应 Transformer 输入
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=args.batch_size, shuffle=True)
input_size = X_train.shape[1]  # 802
# 选择推理设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
if args.model_name == 'Transformer':
    model = TransformerModel().to(device)
elif args.model_name == 'CNN_LSTM':
    hidden_size = 100
    output_size = 7
    model = CNN_LSTM_Model(input_size, hidden_size, output_size).to(device)

2. 定义model

2.1 CNN_LSTM

这里我定义了1个CNN和LSTM的hybird结构。

class CNN_LSTM_Model(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(CNN_LSTM_Model, self).__init__()

        # CNN 部分
        self.conv1 = nn.Conv1d(1, 16, kernel_size=5, stride=1, padding=2)
        self.relu = nn.ReLU()
        self.maxpool = nn.MaxPool1d(kernel_size=2)
        self.conv2 = nn.Conv1d(16, 32, kernel_size=5, stride=1, padding=2)

        # 计算 CNN 输出的特征维度
        self.cnn_output_size = 32 * (input_size // 4)  # 输入大小 / 2^2

        # LSTM 部分
        self.lstm = nn.LSTM(self.cnn_output_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # CNN 部分
        x = self.relu(self.conv1(x))
        x = self.maxpool(x)
        x = self.relu(self.conv2(x))
        x = self.maxpool(x)

        # 展平 CNN 输出
        x = x.permute(0, 2, 1)  # (batch_size, seq_len, feature_size) 这里的 seq_len 是时间步长

        # LSTM 部分
        x = x.flatten(1)
        h_lstm, _ = self.lstm(x)
        out = self.fc(h_lstm)  # 只取最后一个时间步的输出

        return out

2.2 Transformer

class TransformerModel(nn.Module):
    def __init__(self):
        super(TransformerModel, self).__init__()
        self.input_embed = nn.Linear(1, 32)  # 嵌入到更高维度
        self.pos_encoder = nn.Parameter(torch.randn(802, 32))  # 位置编码
        encoder_layers = nn.TransformerEncoderLayer(d_model=32, nhead=4)
        self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers=1)
        self.fc = nn.Linear(32, 7)  # 输出层

    def forward(self, x):
        x = self.input_embed(x) + self.pos_encoder[:x.size(1), :]  # 加入位置编码
        x = x.permute(1, 0, 2)  # 调整维度以匹配 PyTorch Transformer 输入需求
        x = self.transformer_encoder(x)
        x = x.mean(dim=0)  # 池化层
        x = self.fc(x)
        return x

3. 定义train脚本

3.1 loss和optimizer

criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=args.learning_rate)

3.2 train

for epoch in range(args.epochs):
    model.train()
    for batch_X, batch_y in train_loader:
        outputs = model(batch_X.to(device))
        loss = criterion(outputs, batch_y.to(device))
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{args.epochs}], Loss: {loss.item():.4f}')


model.eval()
with torch.no_grad():
    y_pred_tensor = model(X_val_tensor.to(device))
    y_pred = y_pred_tensor.cpu().numpy()
    for i in range(y_pred.shape[1]):
        y_pred[:, i:i+1] = map_to_discrete_values(y_pred[:, i], np.unique(y_val[:, i]))

3.3 predict

x_axis = range(len(y_pred))
figure_list = ['m1', 'AAdl1', 'AAdl2', 'PPa0', 'nn', 'ccd1', 'ccd2']
for i, param in enumerate(figure_list):
    plt.figure(figsize=(10, 6))
    plt.scatter(x_axis, y_pred[:, i], color='blue', alpha=0.5, label='Predicted')
    plt.scatter(x_axis, y_val[:, i], color='red', alpha=0.5, label='True Label')
    plt.xlabel('item')
    plt.ylabel(figure_list[i])
    plt.title(f'{param} - True vs Predicted')

    # 计算均方误差(MSE)
    mse = np.mean((y_val[:, i] - y_pred[:, i]) ** 2)
    # 打印 MSE 值
    print(f"{args.model_name}--MSE of {figure_list[i]}: {mse:.4f}")
    plt.legend()
    plt.show()

其中,可视化的第一个参数拟合结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Andrew_Xzw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值