转载自:9012年,该用bert打比赛了
从下载模型权重开始
1 2 3 4 5 | # 切换到你的anaconda gpu 环境 # source activate 你的conda环境名称 ? # 安装加载预训练模型&权重的包 pip install pytorch-pretrained-bert |
接着就是下载模型权重文件了,pytorch-pretrained-bert官方下载地址太慢了…,推荐去kaggle下载L-12_H-768-A-12 uncase版本,下载地址在这里,里面有两个文件,都下载下来,并把模型参数权重的文件bert-base-uncased解压出来,然后放在你熟悉的硬盘下即可。
加载模型试试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from pytorch_pretrained_bert import BertModel, BertTokenizer import numpy as np import torch # 加载bert的分词器 tokenizer = BertTokenizer.from_pretrained('E:/Projects/bert-pytorch/bert-base-uncased-vocab.txt') # 加载bert模型,这个路径文件夹下有bert_config.json配置文件和model.bin模型权重文件 bert = BertModel.from_pretrained('E:/Projects/bert-pytorch/bert-base-uncased/') s = "I'm not sure, this can work, lol -.-" tokens = tokenizer.tokenize(s) print("\".join(tokens)) # "i\'\\m\\not\\sure\\,\\this\\can\\work\\,\\lo\\##l\\-\\.\\-" # 是否需要这样做? # tokens = ["[CLS]"] + tokens + ["[SEP]"] ids = torch.tensor([tokenizer.convert_tokens_to_ids(tokens)]) print(ids.shape) # torch.Size([1, 15]) result = bert(ids, output_all_encoded_layers=True) print(result) |
没问题,那么bert返回给我们了什么呢?
1 2 3 4 | result = ( [encoder_0_output, encoder_1_output, ..., encoder_11_output], pool_output ) |
- 因为我选择了参数
output_all_encoded_layers=True ,12层Transformer的结果全返回了,存在第一个列表中,每个encoder_output的大小为[batch_size, sequence_length, hidden_size] ; - pool_out大小为
[batch_size, hidden_size] ,pooler层的输出在论文中描述为:
which is the output of a classifier pretrained on top of the hidden state associated to the first character of the input (CLS ) to train on the Next-Sentence task (see BERT’s paper).
也就是说,取了最后一层Transformer的输出结果的第一个单词[cls]的hidden states,其已经蕴含了整个input句子的信息了。 - 如果你用不上所有encoder层的输出,output_all_encoded_layers参数设置为Fasle,那么result中的第一个元素就不是列表了,只是encoder_11_output,大小为
[batch_size, sequence_length, hidden_size] 的张量,可以看作bert对于这句话的表示。
用bert微调我们的模型
将bert嵌入我们的模型即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class CustomModel(nn.Module): def __init__(self, bert_path, n_other_features, n_hidden): super().__init__() # 加载并冻结bert模型参数 self.bert = BertModel.from_pretrained(bert_path) for param in self.bert.parameters(): param.requires_grad = False self.output = nn.Sequential( nn.Dropout(0.2), nn.Linear(768 + n_other_features, n_hidden), nn.ReLU(), nn.Linear(n_hidden, 1) ) def forward(self, seqs, features): _, pooled = self.bert(seqs, output_all_encoded_layers=False) concat = torch.cat([pooled, features], dim=1) logits = self.output(concat) return logits |
测试:
1 2 3 4 5 6 7 8 9 10 11 12 | s = "I'm not sure, this can work, lol -.-" ? tokens = tokenizer.tokenize(s) ids = torch.tensor([tokenizer.convert_tokens_to_ids(tokens)]) # print(ids) # tensor([[1045, 1005, 1049, 2025, 2469, 1010, 2023, 2064, 2147, 1010, 8840, 2140, # 1011, 1012, 1011]]) ? model = CustomModel('你的路径/bert-base-uncased/',10, 512) outputs = model(ids, torch.rand(1, 10)) # print(outputs) # tensor([[0.1127]], grad_fn=<AddmmBackward>) |