Track:正则化(防止过拟合)

现在是代码详解:

class CovidDataset(Dataset):
    def __init__(self, file_path, mode):
        with open(file_path,'r') as f:
            ori_data = list(csv.reader(f))
            csv_data = np.array(ori_data)[1:, 1:].astype(float)

            if mode == 'train':
                indices = [i for i in range(len(csv_data)) if i % 5 != 0]

            elif mode == 'val':
                indices = [i for i in range(len(csv_data)) if i % 5 == 0]

            elif mode == 'test':
                indices = [i for i in range(len(csv_data))]

            x = torch.tensor(csv_data[indices, :93])
            if mode != "test":
                self.y = torch.tensor(csv_data[indices, -1])

            self.x = (x - x.mean(dim=0,keepdim=True)) / x.std(dim=0,keepdim=True)
            self.mode = mode
        pass

    def __getitem__(self,item):
        if self.mode == 'test':
            return self.x[item].float()
        return self.x[item].float(),self.y[item].float()

    def __len__(self):
        return len(self.x)

        这个类的作用是数据加载与处理,数据加载csv.reader(f),csv.data保存的数据是清洗过的,因为零行零列是没有价值的,同时进行数据转换,将字符串通过astype转为浮点数。

        数据集的划分:一般可以将训练集分为训练集和验证集两部分,这里是4:1的比例(通过indices存储),注意数据要是随机的,也就是shuffle为True。特征提取和标签提取也很简单,该数据集中前93列是特征,最后一列是标签。

        X的计算公式也很简单:(x - 均值) / 标准差,构造出一个正态分布。

        getitem函数的作用:区别训练/验证模式与测试模式

        len函数的作用:返回数据集的大小

class myModel(torch.nn.Module):
    def __init__(self, inDim):
        super(myModel, self).__init__()
        self.fc1 = torch.nn.Linear(inDim, 128)
        self.relu1 = torch.nn.ReLU()
        self.fc2 = torch.nn.Linear(128, 1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.fc2(x)

        if len(x.size()) > 1:
            x = x.squeeze(1)
        return x

        自主定义的简易神经网络模型。第一步要调用父类初始化,和JAVA的继承很像。第二步定义网络层,这是一个全连接网络,输入维度为indim,输出维度为128,激活函数采用的是relu。前向传播方法就是如此:输入通过、激活函数、输入再通过、激活函数……维度调整、返回输出。

def train_val(model, train_loader, val_loader, lr, optimizer, device, epochs, save_path):
    model = model.to(device)
    plt_train_loss = []
    plt_val_loss = []
    min_val_loss = np.inf
    
    # 创建保存模型的目录
    save_dir = os.path.dirname(save_path)
    if save_dir and not os.path.exists(save_dir):
        os.makedirs(save_dir)
    
    # 初始化绘图
    plt.ion()  # 开启交互模式
    fig, ax = plt.subplots()

    for epoch in range(epochs):
        model.train()
        start_time = time.time()
        train_loss = 0.0
        for x, y in train_loader:
            x, y = x.to(device), y.to(device)
            y_pred = model(x)
            batch_loss = loss(y_pred, y)
            batch_loss.backward()
            optimizer.step()
            optimizer.zero_grad()
            train_loss += batch_loss.cpu().item()
        plt_train_loss.append(train_loss/train_loader.__len__())

        model.eval()
        val_loss = 0.0
        with torch.no_grad():
            for val_x, val_y in val_loader:
                val_x, val_y = val_x.to(device), val_y.to(device)
                val_pred_y = model(val_x)
                val_batch_loss = loss(val_pred_y, val_y)
                val_loss += val_batch_loss.cpu().item()
        plt_val_loss.append(val_loss / val_loader.__len__())

        if val_loss < min_val_loss:
            min_val_loss = val_loss
            torch.save(model, save_path)

        print("[%03d/%03d] %2.2f sec(s) train_loss: %6f val_loss: %06f" %
              (epoch, epochs, time.time()- start_time, plt_train_loss[-1], plt_val_loss[-1]))

        # 更新绘图
        ax.clear()  # 清除当前轴
        ax.plot(plt_train_loss)
        ax.plot(plt_val_loss)
        ax.set_title("loss")
        ax.legend(["train", "val"])
        plt.pause(0.1)  # 暂停一小段时间以更新图形
    
    plt.ioff()  # 关闭交互模式
    plt.show()  # 显示最终图形

        这是训练与可视化模块。

        先介绍运用的参数:model、data、hyperparam※

        model:待训练的模型、train_loader:训练数据加载器、val_loader:验证数据加载器、lr:学习率、optimizer:优化器、device:运行设备(CPU或GPU)、epochs:训练轮次、save_path:模型存储路径

        第一步:初始化操作。model = model.to(device)将模型迁移到指定设备,能用GPU就用,不能用就CPU。损失记录:plt_train_loss和plt_val_loss记录了训练和验证的损失。min_val_loss用于记录最小验证损失。

        第二步:开始训练:model.train(),并且记录训练时间start_time=time.time()

  1. 遍历训练数据加载器
  2. 将数据移至指定设备
  3. 前向传播:pred_y = model(x)
  4. 计算损失:batch_loss = loss(pred_y, y)
  5. 反向传播:batch_loss.backward()
  6. 更新参数:optimizer.step()
  7. 清零梯度:optimizer.zero_grad()
  8. 累加损失:train_loss += batch_loss.cpu().item()
  9. 记录损失:plt_train_loss.append(train_loss/train_loader.__len__())

        第三步:开始验证。设置评估模式model.eval(),关闭梯度计算with torch.no_grad()并开始。

  1. 遍历验证数据加载器
  2. 将数据移至指定设备
  3. 前向传播:val_y_pred = model(val_x)
  4. 计算损失:val_batch_loss = loss(val_y_pred, val_y)
  5. 累加损失:val_loss += val_batch_loss.cpu().item()
  6. 记录验证损失:plt_val_loss.append(val_loss / val_loader.__len__())

        如果当前验证损失小于最小验证损失,则保存模型,也就是目前我们得到的最优模型。清除当前轴,绘制训练和验证损失曲线,设置标题和图例,暂停一小段时间以更新图形。关闭 Matplotlib 交互模式,显示最终图形。

train_file = r"covid.train.csv"
test_file = r"covid.test.csv"

train_set = CovidDataset(train_file, 'train')
val_set = CovidDataset(train_file, 'val')
test_set = CovidDataset(test_file, 'test')

batch_size = 16

train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_set, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_set, batch_size=1, shuffle=False)

loss = torch.nn.MSELoss()
epochs = 20
lr = 0.001
device = "cuda" if torch.cuda.is_available() else "cpu"
print(device)

data_dim = 93
model = myModel(data_dim).to(device)
save_path = "model_save1/bestmodel.pth"
rel_path = "model_save1/pred.csv"

optimizer = optim.SGD(params=model.parameters(), lr=lr, momentum=0.9)

train_val(model, train_loader, val_loader, lr, optimizer, device, epochs, save_path)
evaluate(save_path, test_loader, rel_path, device)

        这是主程序,也就是所有超参的体现和函数调用。

        最值得讲的部分:

        优化器初始化:optimizer = optim.SGD(params=model.parameters(), lr=lr, momentum=0.9

使用随机梯度下降(SGD)优化器,学习率为 lr,动量为 0.9。

动量:

加速收敛 :通过累积梯度方向,加快模型训练速度
避免局部最小值 :利用惯性帮助参数冲过小的能量壁垒
减少震荡 :平滑参数更新过程,提高训练稳定性

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐