Datawhale AI夏令营 task3学习笔记

目录

题目概述

主要思路

总结


题目概述

本文主要是对代码的解读,优化效果甚微。

这次的任务是使用 Transformer 进行深度学习。

具体思路和RNN的相同,从关键函数——训练函数 train 来理清代码的逻辑。

# 训练
def train():
    ## super param
    N = 10  # 批次数量 int / int(len(dataset) * 1)  # 或者你可以设置为数据集大小的一定比例,如 int(len(dataset) * 0.1)
    INPUT_DIM = 292 # src length,即输入数据的维度。对于嵌入层来说,这是词汇表的大小或输入序列的长度
    D_MODEL = 512 # 嵌入向量的维度。这是输入数据在嵌入后的表示大小,即模型中的特征维度
    NUM_HEADS = 4 # 多头注意力机制中的头数。多头注意力机制可以捕捉到不同子空间的关系
    FNN_DIM = 1024 # 前馈神经网络(FNN)层的隐藏层维度。它决定了前馈网络中的中间表示大小
    NUM_LAYERS = 4 # Transformer 编码器的层数。每一层包含一个多头注意力子层和一个前馈神经网络子层
    DROPOUT = 0.2 # Dropout 的比率,用于在训练过程中随机丢弃部分神经元,防止过拟合
    CLIP = 1 # CLIP value,即梯度裁剪的阈值。用于防止梯度爆炸现象,确保梯度在反向传播过程中不会过大
    N_EPOCHS = 40 # 训练的轮数
    LR = 1e-4 # 学习率
    
    start_time = time.time()  # 开始计时
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    # device = 'cpu'
    data = read_data("../dataset/round1_train_data.csv")
    dataset = ReactionDataset(data)
    subset_indices = list(range(N))
    subset_dataset = Subset(dataset, subset_indices)
    train_loader = DataLoader(dataset, batch_size=128, shuffle=True, collate_fn=collate_fn)

    model = TransformerEncoderModel(INPUT_DIM, D_MODEL, NUM_HEADS, FNN_DIM, NUM_LAYERS, DROPOUT)
    model = model.to(device)
    model.train()
    
    optimizer = optim.AdamW(model.parameters(), lr=LR, weight_decay=0.01)
    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10)
    # 这里默认采用MSE损失函数
    criterion = nn.MSELoss()

    best_valid_loss = 10
    for epoch in range(N_EPOCHS):
        epoch_loss = 0
        # adjust_learning_rate(optimizer, epoch, LR) # 动态调整学习率
        for i, (src, y) in enumerate(train_loader):
            src, y = src.to(device), y.to(device)
            optimizer.zero_grad()
            output = model(src)
            loss = criterion(output, y)
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), CLIP)
            optimizer.step()
            epoch_loss += loss.detach().item()
            
            if i % 50 == 0:
                print(f'Step: {i} | Train Loss: {epoch_loss:.4f}')
                
        loss_in_a_epoch = epoch_loss / len(train_loader)
        scheduler.step(loss_in_a_epoch)
        print(f'Epoch: {epoch+1:02} | Train Loss: {loss_in_a_epoch:.3f}')
        if loss_in_a_epoch < best_valid_loss:
            best_valid_loss = loss_in_a_epoch
            # 在训练循环结束后保存模型
            torch.save(model.state_dict(), '../model/transformer.pth')
    end_time = time.time()  # 结束计时
    # 计算并打印运行时间
    elapsed_time_minute = (end_time - start_time)/60
    print(f"Total running time: {elapsed_time_minute:.2f} minutes")

可以看到,整体的思路和 RNN 模型大同小异,区别主要在于模型的转变和相应参数的变化,更改为使用 TransformerEncoderModel 训练。

下面是一些功能函数/类的介绍。

  • adjust_learning_rate:动态调整学习率

    # 每 3 个 epoch 将学习率衰减 10 倍,从而控制学习率的变化
    def adjust_learning_rate(optimizer, epoch, start_lr):
        """Sets the learning rate to the initial LR decayed by 10 every 3 epochs"""
        # 根据当前的 epoch 计算新的学习率
        lr = start_lr * (0.1 ** (epoch // 3))
        # 遍历优化器的参数组,并将每个参数组的学习率设置为新计算的学习率 lr
        for param_group in optimizer.param_groups:
            param_group['lr'] = lr
  • TransformerEncoderModel:transformer模型 
    # 模型
    '''
    直接采用一个transformer encoder model就好了
    '''
    class TransformerEncoderModel(nn.Module):
        # 初始化模型
        def __init__(self, input_dim, d_model, num_heads, fnn_dim, num_layers, dropout):
            super().__init__()
            # 将输入的词嵌入到 d_model 维的向量空间
            self.embedding = nn.Embedding(input_dim, d_model)
            # Layer Normalization 层,标准化嵌入向量
            self.layerNorm = nn.LayerNorm(d_model)
            # 单个 Transformer 编码器层,包括多头注意力机制和前馈神经网络
            self.encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, 
                                                            nhead=num_heads, 
                                                            dim_feedforward=fnn_dim,
                                                            dropout=dropout,
                                                            batch_first=True,
                                                            norm_first=True # pre-layernorm
                                                            )
            # 堆叠多个 Transformer 编码器层
            self.transformer_encoder = nn.TransformerEncoder(self.encoder_layer, 
                                                             num_layers=num_layers,
                                                             norm=self.layerNorm)
            # Dropout 层,防止过拟合
            self.dropout = nn.Dropout(dropout)
            # 多层感知机(MLP),用于将编码器的输出变换为最终的输出
            self.lc = nn.Sequential(nn.Linear(d_model, 256),
                                    nn.Sigmoid(),
                                    nn.Linear(256, 96),
                                    nn.Sigmoid(),
                                    nn.Linear(96, 1))
    
        # 前向传播,主要作用是通过模型计算输入数据的预测结果,包括接收输入数据、嵌入层处理、通过编码器层、生成输出几个步骤
        def forward(self, src):
            # src shape: [batch_size, src_len]
            # 对嵌入向量进行 Dropout
            embedded = self.dropout(self.embedding(src))
            # embedded shape: [batch_size, src_len, d_model]
            # 通过 Transformer 编码器层处理嵌入向量
            outputs = self.transformer_encoder(embedded)
            # outputs shape: [batch_size, src_len, d_model]
    
            # fisrt
            # 取出每个序列的第一个标记(通常是特殊的分类标记)的输出
            z = outputs[:,0,:]
            # z = torch.sum(outputs, dim=1)
            # print(z)
            # z shape: [bs, d_model]
            # 通过全连接层(MLP)处理第一个标记的输出,得到最终的标量输出
            outputs = self.lc(z)
            # print(outputs)
            # outputs shape: [bs, 1]
            # 返回形状为 [batch_size] 的输出
            return outputs.squeeze(-1)

至于其他函数,大体可以参考 task2 文章中的介绍。

主要思路

其实优化的主要思路和 RNN 也是类似的,包括调参、修改模型。在进行了一系列调整之后,模型只有0.15左右的表现,并不理想。

总结

第三期夏令营的必做任务基本已经完成,最好的成绩还是 task1 的0.35(后面又有一些优化),排名也就是几十名吧,成绩不算太好,尤其对于深度学习的优化效果不佳。作为软件专业的学生,人工智能在之后都可能没有机会专门学习了,但是我很乐意将它作为自己的兴趣,继续探究下去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值