学长安排的更新一下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 Type | Tesla |
Product Series | V-Series |
Product | Tesla V100 |
Operating System | Linux 64-bit |
CUDA Toolkit | 10.2 |
下载安装CUDA
接下来内容就比较简单了,从NVIDIA官网下载CUDA-10.2(选择这个版本是因为这个版本能配上最新的pytorch),如何安装都在网页中写了,这里就不复制粘贴了。
安装完成后需要重启
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)
- 这部分有一个没有搞明白的,如下图
- 这部分有一个没有搞明白的,如下图
- 再将data信息按照
conf.json
文件中的配置信息_batch_size
截取成合适的batch - 将总inputs以及总outputs中,按照batch_size进行分割得到batches
- 即一个batch中batch_size个fragment的序列
- 先分别将训练集与测试集转换为data(见函数arr2data)
模型
/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 权重衰减(weight decay)与学习率衰减(learning rate decay)
机器学习工具
Python
PyTorch
pytorch小知识点(二)——-CrossEntropyLoss(reduction参数)
PyTorch官方文档 关于softmax接口的dim参数内容