相似论文LSTM模型部分学习

学长安排的更新一下Montage模型的学习进度

大致流程

准备运行环境

Montage运行需要Linux环境以及CUDA(也就是说需要一张显卡…)

而本菜菜之前从来没有用过有独显的电脑,所以学习CUDA以及显卡驱动之间的关系耽误了一些时间,主要遇到的问题的解决思路是:

更新显卡驱动

先查看已有的显卡驱动的版本(上方会有显示,比如服务器原本的版本是330.***),如果足够高那就不用更新了

nvidia-msi

如果版本较低,需要先卸载掉已有的版本

sudo apt purge nvidia*

然后可以在 NVIDIA官网 上查询与显卡型号对应的最新的显卡驱动,然后手动下载安装(wget下来然后sh运行run文件),网上也有通过apt安装的方法,但是我捣鼓了一个小时发现总会有获取不到信息的问题,这个没能解决,参考内容 CSDN 更新显卡驱动

本次更新的显卡驱动版本信息
Version:440.118.02
Release Date:2020.9.30
Operating System:Linux 64-bit
CUDA Toolkit:10.2
Language:English (US)
File Size:137.71 MB
服务器显卡信息
Product TypeTesla
Product SeriesV-Series
ProductTesla V100
Operating SystemLinux 64-bit
CUDA Toolkit10.2

下载安装CUDA

接下来内容就比较简单了,从NVIDIA官网下载CUDA-10.2(选择这个版本是因为这个版本能配上最新的pytorch),如何安装都在网页中写了,这里就不复制粘贴了。

https://developer.nvidia.com/cuda-10.2-download-archive?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1804&target_type=runfilelocal

安装完成后需要重启

sudo reboot

下载安装PyTorch

PyTorch官网安装指导 https://pytorch.org/get-started/locally/

Linux系统上,配套CUDA-10.2的安装比较方便

pip install torch torchvision

检测安装是否成功

安装完成后,可以进行以下操作进行检验

cd /usr/local/cuda-10.2/samples/1_Utilities/deviceQuery
sudo make
./deviceQuery

cd ../bandwidthTest/
sudo make
./bandwidthTest

如果这两个运行结果都是PASS的话,那就安装成功了,如果不成功,emmm,我也不知道,建议直接Stack Overflow

载入配置

配置信息保存在了conf/conf.json文件中,与语言模型相关的属性都在model内容中

  • bug_dir: 保存发现的bug的文件夹,需要填写绝对路径
  • data_dir: 保存预处理后的数据,需要填写绝对路径
  • eng_name: 待测试的 JS 引擎的名字 (“chakra”, “v8”, “moz”, “jsc”).
  • eng_path: 保存 JS 引擎的文件夹,需要填绝对路径
  • max_ins: 生成过程中,新增的 fragment 的数量上限
  • model_path: 保存学习到的语言模型的文件夹,需要填写绝对路径
  • batch_size: 训练模型的时候的 batch 大小
  • emb_size: 将 fragment 嵌入成向量时,嵌入向量的维度
  • epoch: 训练的最大轮数
  • gamma: 学习过程中,学习速率需要不断减小,该量为每次减小的倍数
  • lr: 初始学习速率,即SGD算法中的初始梯度下降速度
  • momentum: 这里使用的优化器方法为使用Momentum的SGD(一种梯度下降算法),该参数给出了该算法的一个惯性参数,具体解释参见参考资料
  • split_size: The size of each split. Montage splits each sequence into multiple sequences for training efficiency.
  • weight_decay: 用于减少模型过拟合问题,对神经网络中的新增的权重会有权重衰减,该项也称为L2正则化权重衰减(详细内容可参见参考资料),该参数给出的是L2正则化中的正则化系数
  • num_gpu: 用于该项目的GPU的个数
  • num_proc: 用于该项目的CPU的个数
  • opt: 运行 JS 引擎的其他指令内容
  • seed_dir: 用于保存 JS 种子文件的文件夹,需要填写绝对路径
  • timeout: 编译器编译运行 JS 代码的时间上限,如果超过则视为编译失败
  • top_k: 对应论文中的k_suggestions,即每次编译时提供的最可能的代码片段的数量

载入数据与预处理

从第一部分处理好的数据中载入数据,并进行数据的预处理。其实预处理也没啥内容了,毕竟真正的预处理内容已经在第一部分做过了。

主要执行这些功能的函数是/src/train.py中的:

  • load_data(self)
    • 将第一阶段生成的已经序列化的数据反序列化得到信息
    • 并将数据集的属性信息记录在ModelTrainer类的内部变量中
    • 最后将纯粹的数据集信息按照1:9的比例划分测试集与数据集
  • process_data(self, train, test)
    • 先分别将训练集与测试集转换为data(见函数arr2data)
      • 这部分有一个没有搞明白的,如下图image-20201005211103847
    • 再将data信息按照conf.json文件中的配置信息_batch_size截取成合适的batch
    • 将总inputs以及总outputs中,按照batch_size进行分割得到batches
    • 即一个batch中batch_size个fragment的序列

模型

/src/train.py中的主要内容

def init_model(self):
    self.print_config()

    type_mask = self.build_type_mask()
    # 交叉熵评定函数,对每个数据都计算交叉熵
    loss = CrossEntropyLoss(reduction='none')
    # 语言模型的字典大小
    vocab_size = len(self._oov_frag_list)
    # 每个GPU所需要计算的batch数量
    batch_per_gpu = int(self._emb_size / self._num_gpu)
    # 初始化训练模型
    model = LSTM(vocab_size, self._emb_size,
                 type_mask, loss, batch_per_gpu)
    # 实现从CPU到GPU的内存信息转移
    model.cuda()

    # 使用SGD(一种梯度下降算法变种)对模型进行优化
    # 给出初始梯度下降步长lr,以及初始惯性系数mometum,以及权重衰减系数
    optimizer = SGD(model.parameters(),
                    lr=self._lr,
                    momentum=self._momentum,
                    weight_decay=self._weight_decay)
    # 按照指数衰减调整学习率,调整公式为lr = lr * gamma ** epoch
    scheduler = ExponentialLR(optimizer,
                              gamma=self._gamma)

    return model, optimizer, scheduler

该函数主要功能有:

  • 定义交叉熵损失函数
  • 确定语言模型的字典大小
  • 初始化训练模型(根据配置信息与数据信息)
  • 设定模型的优化器以及学习速率调整器

分层内容

关于模型的主要代码

def __init__(self, vocab_size, embedding_dim,
             type_mask, loss_function, batch_per_gpu):
    super(LSTM, self).__init__()
    # 输入层
    self.embeddings = nn.Embedding(vocab_size, embedding_dim)

    # 隐藏层
    self.lstm = nn.LSTM(input_size=embedding_dim,
                        hidden_size=embedding_dim)

    # 输出层
    self.out_dim = embedding_dim * 2 + 1
    # 最后一层是线性回归函数
    self.fc = nn.Linear(self.out_dim, vocab_size)

    # 语言模型词典规模
    self.vocab_size = vocab_size
    self.type_mask = type_mask
    # softmax仅对结果tensor的第1维使用(维度从0开始计算)
    # 其他信息均为从train.py转移到model.py的参数信息
    self.softmax = nn.Softmax(dim=1)
    self.loss_function = loss_function
    self.embedding_dim = embedding_dim
    self.batch_per_gpu = batch_per_gpu

依旧是导入数据以及配置信息

训练模型

以下为本次一轮epoch中的训练函数,其中包括了对数据的pad、投入模型进行训练以及统计准确度与损失

def run_epoch(self, model, batches, epoch,
              optimizer=None, scheduler=None, mode=None):
    # 将上一轮的训练信息清零
    total_cross_entropy = 0.0
    total_diff = 0.0
    total_acc = 0.0
    num_val = 0
    is_train = (optimizer != None)
    if is_train:
        # 进度条
        batch_iter = tqdm(batches)
        model.train()
    else:
        batch_iter = batches
        model.eval()

    # 对一组batches中的每一个batch进行pad然后投入训练
    for batch in batch_iter:
        # 对batch中的所有信息进行pad
        padded_batch = pad_input(batch)
        # 将pad后的input/pfrag/type/output信息,切分成chunk
        (input_frag_chunks,
         pfrag_chunks, type_chunks,
         output_chunks) = map(self.split_batch, padded_batch[:4])
        # 等到后面用完再切分
        seq_len_chunks = padded_batch[4]
        # num_val统计目前已经训练过多少个fragment
        num_val += sum(seq_len_chunks)

        hidden = None
        seq_len_chunks = self.split_length(seq_len_chunks)
        data_chunks = zip(input_frag_chunks,
                          pfrag_chunks, output_chunks,
                          seq_len_chunks, type_chunks)

        for data_chunk in data_chunks:
            # Zero out grads
            # 清除上一轮训练的梯度信息
            model.zero_grad()
            # 将需要训练的数据转换为张量tensor
            (input_frag_chunk,
             pfrag_chunk, output_chunk,
             seq_len_chunk) = map(data2tensor, data_chunk[:4])
            type_chunk = data2tensor(data_chunk[4],
                                     tensor_type='Float')

            # Forward pass
            # 开始LSTM模型的向前传播,这里调用的是model中LSTM类下的forward函数
            res = model(input_frag_chunk,
                        pfrag_chunk, type_chunk,
                        hidden, output_chunk, seq_len_chunk)

            # 计算本次训练的损失值
            hidden, pred, cross_entropy_loss, top_k_loss = res
            hidden = repackage_hidden(hidden)
            if is_train:
                # 反向传播修正
                loss = top_k_loss + cross_entropy_loss
                self.backward_pass(loss, optimizer)
            # 总偏差、交叉熵以及准确度
            total_diff += float(torch.sum(top_k_loss))
            total_cross_entropy += float(torch.sum(cross_entropy_loss))
            total_acc += float(torch.sum(pred))

    if is_train:
        # 修改学习步长
        scheduler.step()

    total_loss = (total_diff + total_cross_entropy) / num_val
    pplx = np.exp(total_cross_entropy / num_val)
    acc = total_acc / num_val
    total_diff = total_diff / num_val

    # 输出本轮训练的结果信息
    self.print_metrics(mode, epoch,
                       total_loss, pplx, total_diff, acc)
    return pplx

参考资料

机器学习算法

CSDN 机器学习优化器算法Optimizer详解

知乎 mask矩阵在深度学习中有哪些应用场景?

简书 SGD+Mometum中的weight decay

CSDN 权重衰减(weight decay)与学习率衰减(learning rate decay)

机器学习工具

Python

Python zip() 函数

Python pickle 对象序列化

PyTorch

pytorch小知识点(二)——-CrossEntropyLoss(reduction参数)

PyTorch官方文档 关于softmax接口的dim参数内容

知乎 torch.cat与torch.chunk的使用

LSTM细节分析理解(pytorch版)

StackOverflow 为什么PyTorch中要主动清除上一轮训练的梯度信息

Pytorch中的RNN之pack_padded_sequence()和pad_packed_sequence()