赞
踩
首先定义一个配置文件类,类里边存放Bert和LSTM的一些超参数
class Config(object): ''' 配置参数 ''' def __init__(self,dataset): self.model_name='Bert RNN Model' # 训练集,测试集,检验集,类别,模型训练结果保存路径 # self.train_path=dataset+'/data/dev.txt' # self.test_path=dataset+'/data/dev.txt' # self.dev_path=dataset+'/data/dev.txt' self.train_path=dataset+'/data/train.txt' self.test_path=dataset+'/data/test.txt' self.dev_path=dataset+'/data/dev.txt' # python 数据结构保存路径 self.datasetpkl = dataset + '/data/dataset.pkl' self.class_list=[x.strip() for x in open(dataset+'/data/class.txt').readlines()] self.save_path=dataset+'/saved_dict/'+self.model_name+'.ckpt' # 配置使用检测GPU self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 若超过1000还没有提升就提前结束训练 self.require_improvement=1000 # 类别数 self.num_classes = len(self.class_list) # 整体训练次数 self.num_epoch=3 # batch大小 self.batch_size=128 #每个序列最大token数 self.pad_size=32 #学习率 self.learning_rate = 1e-5 self.bert_path='bert_pretrain' self.tokenizer=BertTokenizer.from_pretrained(self.bert_path) #定义分词器 self.hidden_size=768 # Bert模型 token的embedding维度 = Bert模型后接自定义分类器(单隐层全连接网络)的输入维度 # RNN 隐含层数量 self.rnn_hidden_size=256 # RNN数量 self.num_layers=256 # dropout self.dropout=0.5
在这个配置文件中,分别定义了一下内容:
我们自定义的模型要继承自 nn.Module
class Model(nn.Module): def __init__(self,config): super(Model,self).__init__() self.bert=BertModel.from_pretrained(config.bert_path) #从路径加载预训练模型 for param in self.bert.parameters(): param.requires_grad = True # 使参数可更新 # batch_first 参数设置batch维度在前还是序列维度在前(输入输出同步改变) # 在每个RNN神经元之间使用 dropout 在单个神经元内部的两个时间步长间不使用 dropout self.lstm=nn.LSTM(config.hidden_size,config.rnn_hidden_size,config.num_layers,batch_first=True,dropout=config.dropout,bias=True,bidirectional=True) self.dropout=nn.Dropout(config.dropout) # 双向LSTM要*2 分析LSTM节点数和网络层数时,看成神经元是LSTM全连接网络 self.fc=nn.Linear(config.rnn_hidden_size*2,config.num_classes) # 自定义全连接层 ,输入数(输入的最后一个维度),输出数(多分类数量),bert模型输出的最后一个维度是768,这里的输入要和bert最后的输出统一 def forward(self,x): context=x[0] #128*32 batch_size*seq_length mask=x[2] #128*32 batch_size*seq_length # 第一个参数 是所有输入对应的输出 第二个参数 是 cls最后接的分类层的输出 encoder,pooled = self.bert(context,attention_mask=mask,output_all_encoded_layers=False) # output_all_encoded_layers 是否将bert中每层(12层)的都输出,false只输出最后一层【128*768】 out=self.lstm(encoder) # 128*10 return out
__ init __(self,config)函数中主要进行了如下操作:
其中,nn.LSTM()函数中的几个关键参数的解释如下:
参数名称 | 作用 |
---|---|
input_size | 输入数据的形状,这里指word embedding的维度 |
hidden_size | LSTM网络每层LSTM节点数量 |
num_layers | LSTM网络的网络层数 |
bias | LSTM网络是否包含bias |
batch_first | 输入数据的第一个维度是否代表batch编号 |
dropout | dropout比例 |
bidirectional | 是否使用双向LSTM网络 |
注意:
如果 bidirectional 参数为True,则LSTM的的输出的最后一个维度为 input_size*2,因为双向LSTM会将两个LSTM网络的输入拼接在一起
forward(self,x)函数是Bert中一个特殊文章函数,forward(self,x)函数详细解析请看此文章
这里输入数据的结构为 [输入的token序列,序列真实长度,mask序列],输入数据的格式和数据预处理部分相关,在上一篇文章中已经讲解过数据预处理的代码,这里不做赘述
def forward(self,x):
context=x[0] #128*32 batch_size*seq_length
mask=x[2] #128*32 batch_size*seq_length
# 第一个参数 是所有输入对应的输出 第二个参数 是 cls最后接的分类层的输出
encoder,pooled = self.bert(context,attention_mask=mask,output_all_encoded_layers=False) # output_all_encoded_layers 是否将bert中每层(12层)的都输出,false只输出最后一层【128*768】
out=self.lstm(encoder) # encoder维度[batch_size,pad_size,bert_hidden_size],out的维度为 [batch_size,pad_size,lstm_hidden_size]
out =self.dropout(out)
out =out[:,-1,:] #只要序列中最后一个token对应的输出,(因为lstm会记录前边token的信息)
out =self.fc(out)
return out
forward(self,x)函数中主要进行了一下操作:
关于bert模块两个返回值的深度解析请参考此文章 ->从源码层面,深入理解 Bert 框架
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。