transformers加载roberta实现序列标注任务 
最近在断断续续的学习hugging face的transformers,主要是运用transformers加载各种预训练模型实现序列标注。本次博客的主要内容是争对加载roberta预训练模型做序列标注任务。大致内容如下:  (1)roberta 模型  (2)transformers实现序列标注  
roberta模型 
争对bert模型,有很多改进版本的模型,roberta模型与bert模型有以下几个不同的地方,其中roberta的全称为Robustly optimized BERT approach。  (1)roberta的训练语料增大,模型效果比bert好  (2)roberta使用动态的mask,这个与bert的mask不一样,bert在模型训练的时候,每一个epoch的数据mask都一样,而roberta改进了这个maks的方式,采用动态的mask,即每一个epoch的输入的数据的mask不一样  (3)roberta采用Byte-Pair Encoding的编码方式,即采用character- and word-level representations  (4)roberta取消了nsp,论文中实验证明nsp损失下游任务     (5)roberta的优化器对adam的参数进行了修改  (6)roberta训练的时候,可以采取更大的batch size  
transformers实现序列标注 
这里介绍transformers加载roberta预训练模型实现序列标注,采用的是哈工大的roberta-wwm模型。  (1)tokenizer  
from tokenizers import BertWordPieceTokenizer
def get_tokenizer(model_path):
    vocab_file = os.path.join(model_path, "vocab.txt")
    tokenizer = BertWordPieceTokenizer(vocab_file,
                                       lowercase=True)
    return tokenizer
  
(2)模型输入特征话  模型的输入和bert的输入保持一致  
def convert_example_to_feature(context, context_tags, tokenizer):
    code = tokenizer.encode(context)
    new_tags = []
    for offset in code.offsets:
        if offset != (0, 0):
            start_index, end_index = offset
            new_tags.append(context_tags[start_index])
    assert len(code.ids) == len(code.type_ids) == len(code.attention_mask)
    return code.ids, code.type_ids, code.attention_mask, new_tags
def create_inputs_targets_roberta(sentences, tags, tag2id, max_len, tokenizer):
    tokenizer.enable_padding(length=max_len)
    tokenizer.enable_truncation(max_length=max_len)
    dataset_dict = {
        "input_ids": [],
        "token_type_ids": [],
        "attention_mask": [],
        "tags": []
    }
    for sentence, tag in zip(sentences, tags):
        sentence = ''.join(sentence)
        input_ids, token_type_ids, attention_mask, \
        post_tags = convert_example_to_feature(sentence, tag, tokenizer)
        dataset_dict["input_ids"].append(input_ids)
        dataset_dict["token_type_ids"].append(token_type_ids)
        dataset_dict["attention_mask"].append(attention_mask)
        if len(post_tags) < max_len - 2:
            pad_bio_tags = post_tags + [tag2id['O']] * (max_len - 2 - len(post_tags))
        else:
            pad_bio_tags = post_tags[:max_len - 2]
        dataset_dict["tags"].append([tag2id['O']] + pad_bio_tags + [tag2id['O']])
    for key in dataset_dict:
        dataset_dict[key] = np.array(dataset_dict[key])
    x = [
        dataset_dict["input_ids"],
        dataset_dict["token_type_ids"],
        dataset_dict["attention_mask"],
    ]
    y = dataset_dict["tags"]
    return x, y
  
(3)模型  在transformers加载roberta模型进行fine-tuning,中文序列标注使用的是TFBertForTokenClassification。模型的代码如下:  
	model = TFBertForTokenClassification.from_pretrained(args["pretrain_model_path"],
                                                         from_pt=True,
                                                         num_labels=len(list(tag2id.keys())))
  
    optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5, epsilon=1e-08)
    
    loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
    metric = tf.keras.metrics.SparseCategoricalAccuracy('accuracy')
    model.compile(optimizer=optimizer, loss=loss, metrics=[metric])
    model.summary()
    model.fit(train_x,
              train_y,
              epochs=epoch,
              verbose=1,
              batch_size=batch_size,
              validation_data=(dev_x, dev_y),
              validation_batch_size=batch_size
             )   
    
    model_file = os.path.join(args["output_path"], "ner_model.h5")
    model.save_weights(model_file, overwrite=True)
    
    tf.keras.models.save_model(model, args["pb_path"], save_format="tf")
  
(4)模型评价  
id2tag = {value: key for key, value in tag2id.items()}
pred_logits = model.predict(data, batch_size=batch_size)[0]
preds = np.argmax(pred_logits, axis=2).tolist()
assert len(preds) == len(seq_len_list)
predict_label = []
target_label = []
for i in range(len(preds)):
    pred = preds[i][1:]
    temp = []
    true_label = label[i][:min(seq_len_list[i], len(pred))]
    for j in range(min(seq_len_list[i], len(pred))):
        temp.append(id2tag[pred[j]])
    assert len(temp) == len(true_label)
    target_label.append(true_label)
    predict_label.append(temp)
    
precision = precision_score(target_label, predict_label, average="macro", zero_division=0)
recall = recall_score(target_label, predict_label, average="macro", zero_division=0)
f1 = f1_score(target_label, predict_label, average="macro",
              zero_division=0)
logger.info(classification_report(target_label, predict_label))
  
后起补充在不同数据集上的结果,如有错误,欢迎大家指证。 
                
                
                
        
    
 
 |