Day46

DAY 46 Inception网络及其思考

知识点回顾:

  1. 传统计算机视觉发展史:LeNet-->AlexNet-->VGGNet-->nceptionNet-->ResNet

> 之所以说传统,是因为现在主要是针对backbone-neck-head这样的范式做文章

  1. inception模块和网络

  2. 特征融合方法阶段性总结:逐元素相加、逐元素相乘、concat通道数增加等

  3. 感受野与卷积核变体:深入理解不同模块和类的设计初衷

作业:

(一次稍微有点学术感觉的作业:)

  1. 对inception网络在cifar10上观察精度

  2. 消融实验:引入残差机制和cbam模块分别进行消融

1.对inception网络在cifar10上观察精度:

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

# 设备配置:优先使用GPU,没有则用CPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")

# ---------------------- 1. 修正后的Inception模块和InceptionNet ----------------------
class Inception(nn.Module):
    def __init__(self, in_channels):
        """
        Inception模块初始化,实现多尺度特征并行提取与融合
        
        参数:
            in_channels: 输入特征图的通道数
        """
        super(Inception, self).__init__()
        
        # 1x1卷积分支:降维并提取通道间特征关系
        self.branch1x1 = nn.Sequential(
            nn.Conv2d(in_channels, 64, kernel_size=1),
            nn.ReLU()
        )
        
        # 3x3卷积分支:先1x1降维,再3x3提取中等尺度特征
        self.branch3x3 = nn.Sequential(
            nn.Conv2d(in_channels, 96, kernel_size=1),
            nn.ReLU(),
            nn.Conv2d(96, 128, kernel_size=3, padding=1),  # padding=1保持尺寸不变
            nn.ReLU()
        )
        
        # 5x5卷积分支:先1x1降维,再5x5提取大尺度特征
        self.branch5x5 = nn.Sequential(
            nn.Conv2d(in_channels, 16, kernel_size=1),
            nn.ReLU(),
            nn.Conv2d(16, 32, kernel_size=5, padding=2),  # padding=2保持尺寸不变
            nn.ReLU()
        )
        
        # 池化分支:最大池化+1x1卷积降维
        self.branch_pool = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),  # 保持尺寸不变
            nn.Conv2d(in_channels, 32, kernel_size=1),
            nn.ReLU()
        )

    def forward(self, x):
        """前向传播:四个分支并行计算,通道维度拼接"""
        branch1x1 = self.branch1x1(x)
        branch3x3 = self.branch3x3(x)
        branch5x5 = self.branch5x5(x)
        branch_pool = self.branch_pool(x)
        
        # 通道维度拼接(dim=1),总通道数64+128+32+32=256
        return torch.cat([branch1x1, branch3x3, branch5x5, branch_pool], dim=1)

class InceptionNet(nn.Module):
    def __init__(self, num_classes=10):
        super(InceptionNet, self).__init__()
        # 修正conv1:适配CIFAR10的32×32输入(原7x7 stride=2会导致尺寸非整数)
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),  # 3x3卷积,保持32×32
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)  # 池化后变为16×16
        )

        self.inception1 = Inception(64)   # 输入64通道,输出256通道
        self.inception2 = Inception(256)  # 输入256通道,输出256通道

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # 自适应池化到1×1,适配任意尺寸
        self.fc = nn.Linear(256, num_classes)        # 全连接层分类

    def forward(self, x):
        x = self.conv1(x)       # [batch,3,32,32] → [batch,64,16,16]
        x = self.inception1(x)  # → [batch,256,16,16]
        x = self.inception2(x)  # → [batch,256,16,16]
        x = self.avgpool(x)     # → [batch,256,1,1]
        x = torch.flatten(x, 1) # → [batch,256]
        x = self.fc(x)          # → [batch,10]
        return x

# ---------------------- 2. 数据加载(CIFAR10) ----------------------
# 数据预处理:归一化(CIFAR10的均值和标准差)
transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),  # 随机水平翻转,数据增强
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

# 加载训练集和测试集
train_dataset = datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform
)
test_dataset = datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform
)

# 数据加载器
batch_size = 64
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)

# ---------------------- 3. 训练和测试函数 ----------------------
def train(model, train_loader, criterion, optimizer, epoch):
    """训练一个epoch,返回训练损失"""
    model.train()  # 切换到训练模式
    total_loss = 0.0
    for i, (images, labels) in enumerate(train_loader):
        # 数据移到指定设备
        images = images.to(device)
        labels = labels.to(device)
        
        # 前向传播
        outputs = model(images)
        loss = criterion(outputs, labels)
        
        # 反向传播+优化
        optimizer.zero_grad()  # 清空梯度
        loss.backward()        # 反向传播
        optimizer.step()       # 更新参数
        
        total_loss += loss.item()
        
        # 打印训练进度
        if (i+1) % 100 == 0:
            print(f'Epoch [{epoch+1}], Step [{i+1}/{len(train_loader)}], Loss: {loss.item():.4f}')
    
    avg_loss = total_loss / len(train_loader)
    return avg_loss

def test(model, test_loader):
    """测试模型,返回测试精度"""
    model.eval()  # 切换到评估模式(禁用Dropout/BatchNorm的训练行为)
    correct = 0
    total = 0
    with torch.no_grad():  # 禁用梯度计算,节省内存
        for images, labels in test_loader:
            images = images.to(device)
            labels = labels.to(device)
            outputs = model(images)
            # 获取预测结果(最大概率的类别)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    
    accuracy = 100 * correct / total
    print(f'Test Accuracy of the model on the 10000 test images: {accuracy:.2f} %')
    return accuracy

# ---------------------- 4. 主训练流程 ----------------------
# 初始化模型、损失函数、优化器
model = InceptionNet(num_classes=10).to(device)
criterion = nn.CrossEntropyLoss()  # 分类任务用交叉熵损失
optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam优化器

# 训练参数
num_epochs = 30
train_losses = []
test_accs = []

# 开始训练
print("开始训练InceptionNet on CIFAR10...")
for epoch in range(num_epochs):
    # 训练
    train_loss = train(model, train_loader, criterion, optimizer, epoch)
    train_losses.append(train_loss)
    # 测试
    test_acc = test(model, test_loader)
    test_accs.append(test_acc)

# ---------------------- 5. 结果可视化 ----------------------
plt.figure(figsize=(12, 4))
# 训练损失曲线
plt.subplot(1, 2, 1)
plt.plot(range(1, num_epochs+1), train_losses, 'b-', label='Train Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.legend()

# 测试精度曲线
plt.subplot(1, 2, 2)
plt.plot(range(1, num_epochs+1), test_accs, 'r-', label='Test Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.title('Test Accuracy')
plt.legend()

plt.tight_layout()
plt.savefig('inception_cifar10_result.png')
plt.show()

# 保存模型
torch.save(model.state_dict(), 'inception_cifar10.pth')
print("训练完成,模型已保存为 inception_cifar10.pth")
  1. 消融实验:引入残差机制和cbam模块分别进行消融
python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy as np

# 设备配置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")

# ---------------------- 1. 定义核心模块(CBAM + 残差Inception) ----------------------
# CBAM注意力模块(通道注意力+空间注意力)
class CBAM(nn.Module):
    def __init__(self, channels, reduction=16):
        super(CBAM, self).__init__()
        # 通道注意力:压缩空间维度,关注重要通道
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(channels, channels // reduction),
            nn.ReLU(),
            nn.Linear(channels // reduction, channels)
        )
        # 空间注意力:压缩通道维度,关注重要空间位置
        self.spatial = nn.Sequential(
            nn.Conv2d(2, 1, kernel_size=3, padding=1, bias=False),
            nn.Sigmoid()
        )
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 通道注意力计算
        b, c, h, w = x.size()
        avg_out = self.fc(self.avg_pool(x).view(b, c)).view(b, c, 1, 1)
        max_out = self.fc(self.max_pool(x).view(b, c)).view(b, c, 1, 1)
        channel_att = self.sigmoid(avg_out + max_out)
        x = x * channel_att  # 通道维度加权
        
        # 空间注意力计算
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        spatial_att = self.spatial(torch.cat([avg_out, max_out], dim=1))
        x = x * spatial_att  # 空间维度加权
        return x

# 基础Inception模块(基线)
class Inception(nn.Module):
    def __init__(self, in_channels):
        super(Inception, self).__init__()
        self.branch1x1 = nn.Sequential(
            nn.Conv2d(in_channels, 64, kernel_size=1),
            nn.ReLU()
        )
        self.branch3x3 = nn.Sequential(
            nn.Conv2d(in_channels, 96, kernel_size=1),
            nn.ReLU(),
            nn.Conv2d(96, 128, kernel_size=3, padding=1),
            nn.ReLU()
        )
        self.branch5x5 = nn.Sequential(
            nn.Conv2d(in_channels, 16, kernel_size=1),
            nn.ReLU(),
            nn.Conv2d(16, 32, kernel_size=5, padding=2),
            nn.ReLU()
        )
        self.branch_pool = nn.Sequential(
            nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
            nn.Conv2d(in_channels, 32, kernel_size=1),
            nn.ReLU()
        )

    def forward(self, x):
        branch1x1 = self.branch1x1(x)
        branch3x3 = self.branch3x3(x)
        branch5x5 = self.branch5x5(x)
        branch_pool = self.branch_pool(x)
        return torch.cat([branch1x1, branch3x3, branch5x5, branch_pool], dim=1)

# 带残差的Inception模块(消融组1)
class ResInception(nn.Module):
    def __init__(self, in_channels):
        super(ResInception, self).__init__()
        # 基础Inception分支
        self.inception = Inception(in_channels)
        # 残差捷径:如果输入通道≠输出通道(256),用1x1卷积匹配维度
        self.shortcut = nn.Sequential()
        if in_channels != 256:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, 256, kernel_size=1),
                nn.ReLU()
            )

    def forward(self, x):
        # 残差连接:Inception输出 + 捷径输出(逐元素相加)
        inception_out = self.inception(x)
        shortcut_out = self.shortcut(x)
        return nn.ReLU()(inception_out + shortcut_out)

# ---------------------- 2. 构建3个消融实验模型 ----------------------
# 模型1:基线模型(基础InceptionNet)
class BaselineInceptionNet(nn.Module):
    def __init__(self, num_classes=10):
        super(BaselineInceptionNet, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )
        self.inception1 = Inception(64)
        self.inception2 = Inception(256)
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(256, num_classes)

    def forward(self, x):
        x = self.conv1(x)
        x = self.inception1(x)
        x = self.inception2(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

# 模型2:InceptionNet + 残差(消融组1)
class ResInceptionNet(nn.Module):
    def __init__(self, num_classes=10):
        super(ResInceptionNet, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )
        self.inception1 = ResInception(64)  # 替换为带残差的Inception
        self.inception2 = ResInception(256)
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(256, num_classes)

    def forward(self, x):
        x = self.conv1(x)
        x = self.inception1(x)
        x = self.inception2(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

# 模型3:InceptionNet + CBAM(消融组2)
class CBAMInceptionNet(nn.Module):
    def __init__(self, num_classes=10):
        super(CBAMInceptionNet, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        )
        self.inception1 = Inception(64)
        self.cbam1 = CBAM(256)  # Inception1后加CBAM
        self.inception2 = Inception(256)
        self.cbam2 = CBAM(256)  # Inception2后加CBAM
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(256, num_classes)

    def forward(self, x):
        x = self.conv1(x)
        x = self.inception1(x)
        x = self.cbam1(x)       # CBAM增强特征
        x = self.inception2(x)
        x = self.cbam2(x)       # CBAM增强特征
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

# ---------------------- 3. 数据加载(与基线一致,保证变量唯一) ----------------------
transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
])

train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)

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

# ---------------------- 4. 训练/测试函数(通用,适配所有模型) ----------------------
def train_model(model, train_loader, criterion, optimizer, num_epochs=10):
    """训练模型,返回训练损失和测试精度列表"""
    model.to(device)
    train_losses = []
    test_accs = []
    
    for epoch in range(num_epochs):
        # 训练阶段
        model.train()
        total_loss = 0.0
        for i, (images, labels) in enumerate(train_loader):
            images, labels = images.to(device), labels.to(device)
            
            outputs = model(images)
            loss = criterion(outputs, labels)
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            
            total_loss += loss.item()
            
            if (i+1) % 100 == 0:
                print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {loss.item():.4f}')
        
        # 记录训练损失
        avg_loss = total_loss / len(train_loader)
        train_losses.append(avg_loss)
        
        # 测试阶段
        model.eval()
        correct = 0
        total = 0
        with torch.no_grad():
            for images, labels in test_loader:
                images, labels = images.to(device), labels.to(device)
                outputs = model(images)
                _, predicted = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += (predicted == labels).sum().item()
        
        acc = 100 * correct / total
        test_accs.append(acc)
        print(f'Epoch [{epoch+1}], Test Accuracy: {acc:.2f} %')
    
    return train_losses, test_accs

# ---------------------- 5. 执行消融实验 ----------------------
# 超参数(所有模型保持一致)
num_epochs = 10
lr = 0.001
criterion = nn.CrossEntropyLoss()

# 实验1:训练基线模型
print("\n========== 训练基线模型(BaselineInceptionNet) ==========")
baseline_model = BaselineInceptionNet()
baseline_optimizer = optim.Adam(baseline_model.parameters(), lr=lr)
baseline_losses, baseline_accs = train_model(
    baseline_model, train_loader, criterion, baseline_optimizer, num_epochs
)

# 实验2:训练残差版模型
print("\n========== 训练残差版模型(ResInceptionNet) ==========")
res_model = ResInceptionNet()
res_optimizer = optim.Adam(res_model.parameters(), lr=lr)
res_losses, res_accs = train_model(
    res_model, train_loader, criterion, res_optimizer, num_epochs
)

# 实验3:训练CBAM版模型
print("\n========== 训练CBAM版模型(CBAMInceptionNet) ==========")
cbam_model = CBAMInceptionNet()
cbam_optimizer = optim.Adam(cbam_model.parameters(), lr=lr)
cbam_losses, cbam_accs = train_model(
    cbam_model, train_loader, criterion, cbam_optimizer, num_epochs
)

# ---------------------- 6. 消融实验结果可视化与对比 ----------------------
plt.figure(figsize=(15, 6))

# 子图1:训练损失对比
plt.subplot(1, 2, 1)
plt.plot(range(1, num_epochs+1), baseline_losses, 'b-', label='Baseline')
plt.plot(range(1, num_epochs+1), res_losses, 'r-', label='Inception + Residual')
plt.plot(range(1, num_epochs+1), cbam_losses, 'g-', label='Inception + CBAM')
plt.xlabel('Epoch')
plt.ylabel('Training Loss')
plt.title('Ablation Experiment: Training Loss')
plt.legend()
plt.grid(alpha=0.3)

# 子图2:测试精度对比
plt.subplot(1, 2, 2)
plt.plot(range(1, num_epochs+1), baseline_accs, 'b-', label='Baseline')
plt.plot(range(1, num_epochs+1), res_accs, 'r-', label='Inception + Residual')
plt.plot(range(1, num_epochs+1), cbam_accs, 'g-', label='Inception + CBAM')
plt.xlabel('Epoch')
plt.ylabel('Test Accuracy (%)')
plt.title('Ablation Experiment: Test Accuracy')
plt.legend()
plt.grid(alpha=0.3)

plt.tight_layout()
plt.savefig('ablation_experiment_result.png')
plt.show()

# 打印最终精度对比
print("\n========== 消融实验最终结果 ==========")
print(f"基线模型最终精度: {baseline_accs[-1]:.2f} %")
print(f"残差版模型最终精度: {res_accs[-1]:.2f} % (提升: {res_accs[-1]-baseline_accs[-1]:.2f} %)")
print(f"CBAM版模型最终精度: {cbam_accs[-1]:.2f} % (提升: {cbam_accs[-1]-baseline_accs[-1]:.2f} %)")

# 保存所有模型
torch.save(baseline_model.state_dict(), 'baseline_inception.pth')
torch.save(res_model.state_dict(), 'res_inception.pth')
torch.save(cbam_model.state_dict(), 'cbam_inception.pth')
print("\n所有模型已保存,消融实验完成!")

DAY 46 序列预测任务介绍

知识点回顾

  1. 序列预测介绍

a. 单步预测

b. 多步预测的2种方式

  1. 序列数据的处理:滑动窗口

  2. 多输入多输出任务的思路

  3. 经典机器学习在序列任务上的劣势;以随机森林为例

作业:手动构造类似的数据集(如cosx数据),观察不同的机器学习模型的差异

@浙大疏锦行

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from xgboost import XGBRegressor
from sklearn.metrics import mean_squared_error

# =============================================================
# ===== 步骤1:构造cosx为核心的合成时间序列数据集 =====
# =============================================================
# 生成cosx基础的时间序列(加入趋势项和噪声,模拟真实时序数据)
x = np.linspace(0, 100, 1000)  # 1000个时间步,范围0-100
# 核心信号:cos(x) + 线性趋势 + 随机噪声
y = np.cos(x) + 0.05 * x + np.random.normal(0, 0.3, 1000)

# 定义关键参数(与原代码一致,保证可比性)
train_size = int(len(y) * 0.8)  # 80%训练集,20%测试集
seq_length = 30  # 滑动窗口长度:用前30个时间步预测下1个

# =============================================================
# ===== 步骤2:数据预处理(标准化 + 滑动窗口构造序列) =====
# =============================================================
# 标准化(仅基于训练集拟合,避免数据泄露)
train_data_raw = y[:train_size]
scaler = MinMaxScaler(feature_range=(0, 1))
scaler.fit(train_data_raw.reshape(-1, 1))  # 必须reshape为二维(样本数, 特征数)
scaled_y = scaler.transform(y.reshape(-1, 1)).flatten()

# 滑动窗口构造时序数据集(前seq_length个值预测下1个值)
def create_sequences(data, seq_length):
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:i+seq_length])  # 输入:前30个时间步
        y.append(data[i+seq_length])    # 输出:第31个时间步
    return np.array(X), np.array(y)

# 对标准化后的数据构造序列
all_X, all_y = create_sequences(scaled_y, seq_length)

# 划分训练/测试集(注意滑动窗口后的索引对齐)
split_idx = train_size - seq_length
X_train = all_X[:split_idx]
y_train = all_y[:split_idx]
X_test = all_X[split_idx:]
y_test = all_y[split_idx:]

# 重塑输入形状:ML模型需要二维输入(样本数, 特征数),而非RNN的三维
X_train_ml = X_train.reshape(X_train.shape[0], -1)  # (770, 30)
X_test_ml = X_test.reshape(X_test.shape[0], -1)    # (200, 30)

# =============================================================
# ===== 步骤3:训练多个机器学习模型并评估 =====
# =============================================================
# 定义要对比的模型(随机森林、线性回归、XGBoost)
models = {
    "线性回归": LinearRegression(n_jobs=-1),
    "随机森林": RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1),
    "XGBoost": XGBRegressor(n_estimators=100, random_state=42, n_jobs=-1)
}

# 存储各模型的预测结果和评估指标
predictions = {}
rmse_results = {}

print("===== 各模型训练与评估结果 =====")
for model_name, model in models.items():
    # 训练模型
    model.fit(X_train_ml, y_train)
    
    # 预测
    train_pred = model.predict(X_train_ml)
    test_pred = model.predict(X_test_ml)
    
    # 反标准化(还原到原始数据尺度)
    train_pred = scaler.inverse_transform(train_pred.reshape(-1, 1))
    test_pred = scaler.inverse_transform(test_pred.reshape(-1, 1))
    y_train_orig = scaler.inverse_transform(y_train.reshape(-1, 1))
    y_test_orig = scaler.inverse_transform(y_test.reshape(-1, 1))
    
    # 计算RMSE(均方根误差,越小表示预测越准)
    train_rmse = np.sqrt(mean_squared_error(y_train_orig, train_pred))
    test_rmse = np.sqrt(mean_squared_error(y_test_orig, test_pred))
    
    # 存储结果
    predictions[model_name] = (train_pred, test_pred)
    rmse_results[model_name] = (train_rmse, test_rmse)
    
    # 打印评估结果
    print(f"\n{model_name}:")
    print(f"  训练集RMSE: {train_rmse:.4f}")
    print(f"  测试集RMSE: {test_rmse:.4f}")

# =============================================================
# ===== 步骤4:可视化对比各模型预测效果 =====
# =============================================================
plt.figure(figsize=(18, 10))

# 绘制原始数据
plt.subplot(2, 1, 1)
plt.plot(y, label='原始cosx时序数据', color='gray', alpha=0.5)
# 绘制训练集/测试集分割线
plt.axvline(x=train_size, color='black', linestyle='--', label='训练/测试分割线')

# 绘制各模型的预测结果
colors = {"线性回归": "green", "随机森林": "blue", "XGBoost": "red"}
for model_name, (train_pred, test_pred) in predictions.items():
    # 训练集预测值
    train_pred_plot = np.empty_like(y)
    train_pred_plot[:] = np.nan
    train_pred_plot[seq_length : len(train_pred) + seq_length] = train_pred.flatten()
    plt.plot(train_pred_plot, label=f'训练集预测 ({model_name})', color=colors[model_name], alpha=0.7)
    
    # 测试集预测值
    test_pred_plot = np.empty_like(y)
    test_pred_plot[:] = np.nan
    test_pred_plot[len(train_pred) + seq_length : len(y)] = test_pred.flatten()
    plt.plot(test_pred_plot, label=f'测试集预测 ({model_name})', color=colors[model_name], linestyle='--')

plt.title('cosx时间序列 - 不同机器学习模型预测结果对比')
plt.xlabel('时间步')
plt.ylabel('值')
plt.legend()
plt.grid(True)

# 绘制RMSE对比柱状图
plt.subplot(2, 1, 2)
model_names = list(rmse_results.keys())
train_rmses = [rmse_results[name][0] for name in model_names]
test_rmses = [rmse_results[name][1] for name in model_names]

x_pos = np.arange(len(model_names))
width = 0.35
plt.bar(x_pos - width/2, train_rmses, width, label='训练集RMSE', alpha=0.8)
plt.bar(x_pos + width/2, test_rmses, width, label='测试集RMSE', alpha=0.8)

plt.xlabel('模型')
plt.ylabel('RMSE')
plt.title('各模型RMSE对比(越小越好)')
plt.xticks(x_pos, model_names)
plt.legend()
plt.grid(True, axis='y')

plt.tight_layout()
plt.show()
相关推荐
是店小二呀2 小时前
在 AtomGit 昇腾 Atlas 800T上解锁 SGLang:零成本打造高性能推理服务
人工智能·pytorch·深度学习·npu
图生生2 小时前
饰品商拍提效:AI图生图实现白底图转上身图
人工智能·ai
万事可爱^2 小时前
GitCode+昇腾部署Rnj-1模型实践教程
人工智能·深度学习·语言模型·gitcode·本地部署·昇腾npu
高洁012 小时前
图神经网络初探(2)
人工智能·深度学习·算法·机器学习·transformer
njsgcs2 小时前
ai控制鼠标生成刀路系统 环境搭建尝试7 lsd识别刀路线段2
人工智能
哈__2 小时前
实测VLM:昇腾平台上的视觉语言模型测评与优化实践
人工智能·语言模型·自然语言处理·gitcode·sglang
海森大数据2 小时前
数据筛选新范式:以质胜量,揭开大模型后训练黑箱
人工智能·语言模型
PNP Robotics2 小时前
PNP机器人受邀参加英业达具身智能活动
大数据·人工智能·python·学习·机器人
智算菩萨2 小时前
【Python进阶】搭建AI工程:Python模块、包与版本控制
开发语言·人工智能·python