【NLP实战】基于Bert和双向LSTM的情感分类【中篇】( 二 )


输出:
55223('我妈大早上去买了好几盒维C维E,说专家说了每天吃能预防肺炎还要去买金银花,一本正经的听专家讲座,还拿个小本子记笔记也是很认真了?', 1.0)
step2:装载,定义批处理函数
这个批处理函数主要做的事情是:使用bert-base-对字典将我们的text进行编码,详细不展开拓展,请花时间去大致了解bert都做了些什么,bert如何使用 。简单来说,bert每个模型自己有一个字典,我们映射text也是映射到它的字典上去 。
如果字典上没有的字符,会映射成[UNK] 。所以之前我们数据清洗时没有去除特殊字符 。
其他的解释都在代码的注释里 。
from transformers import BertTokenizer, BertModel# todo:自定义数据集class MydataSet(Dataset):...# todo: 定义批处理函数def collate_fn(data):sents = [i[0] for i in data]labels = [i[1] for i in data]# 分词并编码data = http://www.kingceram.com/post/token.batch_encode_plus(batch_text_or_text_pairs=sents,# 单个句子参与编码truncation=True,# 当句子长度大于max_length时,截断padding='max_length',# 一律补pad到max_length长度max_length=200,return_tensors='pt',# 以pytorch的形式返回,可取值tf,pt,np,默认为返回listreturn_length=True,)# input_ids:编码之后的数字# attention_mask:是补零的位置是0,其他位置是1input_ids = data['input_ids']# input_ids 就是编码后的词attention_mask = data['attention_mask']# pad的位置是0,其他位置是1token_type_ids = data['token_type_ids']# (如果是一对句子)第一个句子和特殊符号的位置是0,第二个句子的位置是1labels = torch.LongTensor(labels)# 该批次的labels# print(data['length'], data['length'].max())return input_ids, attention_mask, token_type_ids, labelsif __name__ == '__main__':# 定义超参数batch_size = 128epochs = 30# load train data...# todo: 加载字典和分词工具token = BertTokenizer.from_pretrained('bert-base-chinese')# 装载训练集train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, collate_fn=collate_fn, shuffle=True,drop_last=True)# 检查一个批次是否编码成功for i, (input_ids, attention_mask, token_type_ids,labels) in enumerate(train_loader):# print(len(train_loader))print(input_ids[0])# 第一句话分词后在bert-base-chinese字典中的word_to_idprint(token.decode(input_ids[0]))# 检查第一句话的id_to_wordprint(input_ids.shape)# 一个批次32句话,每句话被word_to_id成500维# print(attention_mask.shape)# 对于使用者而言,不是重要的 。含义上面有说明,感兴趣可以做实验测试# print(token_type_ids.shape)# 对于使用者而言,不是重要的 。含义上面有说明,感兴趣可以做实验测试print(labels)# 该批次的labelsbreak
因为没有指定随机种子,每次结果可能不一样 。需要注意的是.shape需要与批处理函数中的规则对应 。
step3:生成层–预训练模块,测试word
这里就直接上代码了,基础部分不讲解 。其他地方都在代码里有说明 。
注意我们冻结了上游参数,这样bert层就不会更新参数了 。当然你也可以试试微调 。
# todo:自定义数据集class MydataSet(Dataset):...# todo: 定义批处理函数def collate_fn(data):...# todo: 定义模型,上游使用bert预训练,下游任务选择双向LSTM模型,最后加一个全连接层class Model(nn.Module):def __init__(self):super(Model, self).__init__()# todo: 加载bert中文模型self.bert = BertModel.from_pretrained('bert-base-chinese')# 冻结上游模型参数(不进行预训练模型参数学习)for param in self.bert.parameters():param.requires_grad_(False)def forward(self, input_ids, attention_mask, token_type_ids):# bert预训练embedding = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)# print(embedding)# print("===============")# print(embedding[0])# print(embedding[0].shape)embedding = embedding.last_hidden_state# 第0维才是我们需要的embedding,embedding.last_hidden_state 和 embedding[0] 效果是一样的 。# print("--------------------------------")print(embedding)print(embedding.shape)if __name__ == '__main__':# 定义超参数...# 检查一个批次是否编码成功...model = Model()# 测试for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_loader):model.forward(input_ids, attention_mask, token_type_ids)break