基于Niu等人方法的序数回归MLP模型实现——以水泥强度数据集为例

基于Niu等人方法的序数回归MLP模型实现——以水泥强度数据集为例

deeplearning-models A collection of various deep learning architectures, models, and tips deeplearning-models 项目地址: https://gitcode.com/gh_mirrors/de/deeplearning-models

序言

在机器学习领域,序数回归(Ordinal Regression)是一种特殊的分类任务,其类别标签具有明确的顺序关系。本文将详细介绍如何使用Niu等人在2016年提出的二元扩展方法,构建一个多层感知机(MLP)模型来解决水泥强度预测这一序数回归问题。

1. 理解序数回归问题

序数回归与常规分类问题的主要区别在于其类别标签具有内在的顺序性。以水泥强度为例,强度等级1-5代表了从低到高的强度水平,这种顺序关系在建模过程中需要特别考虑。

Niu等人提出的方法核心思想是将K类序数回归问题转化为K-1个二元分类子问题,每个子问题预测目标是否大于某个阈值。这种方法能够有效利用类别间的顺序信息。

2. 数据准备与预处理

2.1 数据集介绍

我们使用的水泥强度数据集包含998个样本,每个样本有8个特征变量和1个序数标签(强度等级1-5)。首先需要对数据进行预处理:

import pandas as pd
import numpy as np

# 加载数据并调整标签范围(从0开始)
data_df = pd.read_csv("水泥强度数据集路径")
data_df["response"] = data_df["response"]-1 

# 分离特征和标签
data_labels = data_df["response"]
data_features = data_df.loc[:, ["V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8"]]

2.2 数据标准化与分割

为了提升模型性能,我们对特征进行标准化处理,并将数据集划分为训练集和测试集:

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 数据集分割
X_train, X_test, y_train, y_test = train_test_split(
    data_features.values,
    data_labels.values,
    test_size=0.2,
    random_state=1,
    stratify=data_labels.values)

# 特征标准化
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)

3. 构建PyTorch数据管道

为了高效地训练模型,我们需要创建PyTorch的数据加载器:

from torch.utils.data import Dataset, DataLoader
import torch

class MyDataset(Dataset):
    def __init__(self, feature_array, label_array):
        self.features = feature_array.astype(np.float32)
        self.labels = label_array
        
    def __getitem__(self, index):
        return self.features[index], self.labels[index]
        
    def __len__(self):
        return self.labels.shape[0]

# 创建数据集和数据加载器
train_dataset = MyDataset(X_train_std, y_train)
test_dataset = MyDataset(X_test_std, y_test)

train_loader = DataLoader(dataset=train_dataset, batch_size=128, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=128, shuffle=False)

4. 模型架构设计

4.1 网络结构

我们构建一个包含两个隐藏层的MLP,关键点在于输出层的特殊设计:

class MLP(torch.nn.Module):
    def __init__(self, in_features, num_classes, num_hidden_1=300, num_hidden_2=300):
        super().__init__()
        self.num_classes = num_classes
        
        # 隐藏层
        self.my_network = torch.nn.Sequential(
            torch.nn.Linear(in_features, num_hidden_1, bias=False),
            torch.nn.LeakyReLU(),
            torch.nn.Dropout(0.2),
            torch.nn.BatchNorm1d(num_hidden_1),
            
            torch.nn.Linear(num_hidden_1, num_hidden_2, bias=False),
            torch.nn.LeakyReLU(),
            torch.nn.Dropout(0.2),
            torch.nn.BatchNorm1d(num_hidden_2)
        )
        
        # 特殊设计的输出层
        self.fc = torch.nn.Linear(num_hidden_2, (num_classes-1)*2)
        
    def forward(self, x):
        x = self.my_network(x)
        logits = self.fc(x)
        # 重塑输出以适应Niu方法
        logits = logits.view(-1, (self.num_classes-1), 2)
        return logits

4.2 输出层解析

输出层设计是Niu方法的核心:

  • 对于K类序数问题,输出层产生K-1个二元分类结果
  • 每个二元分类对应一个阈值比较问题
  • 最终输出维度为(batch_size, K-1, 2),表示每个阈值比较的二元分类logits

5. 训练过程实现

5.1 损失函数

Niu方法的专用损失函数实现如下:

import torch.nn.functional as F
from coral_pytorch.dataset import levels_from_labelbatch

def niu_et_al_loss(logits, levels):
    val = (-torch.sum((F.log_softmax(logits, dim=2)[:, :, 1]*levels
                  + F.log_softmax(logits, dim=2)[:, :, 0]*(1-levels)), dim=1))
    return torch.mean(val)

5.2 训练循环

完整的训练过程包括:

  1. 将序数标签转换为扩展二元形式
  2. 前向传播计算logits
  3. 使用专用损失函数计算损失
  4. 反向传播更新参数
# 初始化模型和优化器
model = MLP(in_features=8, num_classes=5)
optimizer = torch.optim.Adam(model.parameters(), lr=0.05)

for epoch in range(50):
    model.train()
    for batch_idx, (features, class_labels) in enumerate(train_loader):
        # 转换标签格式
        levels = levels_from_labelbatch(class_labels, num_classes=5)
        
        features = features.to(device)
        levels = levels.to(device)
        
        # 前向传播
        logits = model(features)
        
        # 计算损失
        loss = niu_et_al_loss(logits, levels)
        
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

6. 模型评估

6.1 预测结果转换

我们需要将模型的输出转换为原始序数标签:

def niu_logits_to_labels(logits):
    probas = F.softmax(logits, dim=2)[:, :, 1]
    predict_levels = probas > 0.5
    return torch.sum(predict_levels, dim=1)

6.2 评估指标计算

使用平均绝对误差(MAE)和均方误差(MSE)评估模型性能:

def compute_mae_and_mse(model, data_loader):
    mae, mse, num_examples = 0., 0., 0
    
    with torch.no_grad():
        for features, targets in data_loader:
            features = features.to(device)
            targets = targets.float().to(device)
            
            logits = model(features)
            predicted_labels = niu_logits_to_labels(logits)
            
            num_examples += targets.size(0)
            mae += torch.sum(torch.abs(predicted_labels - targets))
            mse += torch.sum((predicted_labels - targets)**2)
    
    return mae/num_examples, mse/num_examples

train_mae, train_mse = compute_mae_and_mse(model, train_loader)
test_mae, test_mse = compute_mae_and_mse(model, test_loader)

7. 结果分析与总结

在我们的实验中,模型在水泥强度数据集上表现良好:

  • 训练集MAE: 0.29
  • 测试集MAE: 0.31
  • 训练集MSE: 0.32
  • 测试集MSE: 0.36

Niu等人的二元扩展方法通过将序数回归问题转化为一系列二元分类问题,有效地利用了类别间的顺序信息。相比直接使用常规分类方法,这种方法通常能获得更好的序数预测性能。

8. 扩展思考

  1. 模型改进:可以尝试更深的网络结构或不同的激活函数
  2. 损失函数优化:考虑加入正则化项防止过拟合
  3. 应用场景:该方法可广泛应用于其他序数回归问题,如产品评级、疾病严重程度评估等

通过本教程,我们完整实现了基于Niu方法的序数回归MLP模型,为处理类似问题提供了可复用的框架。

deeplearning-models A collection of various deep learning architectures, models, and tips deeplearning-models 项目地址: https://gitcode.com/gh_mirrors/de/deeplearning-models

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

缪阔孝Ruler

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

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

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

打赏作者

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

抵扣说明:

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

余额充值