用BERT实现中文语句分类

BERT以Transformer的Encoder为架构,已MLM为模型,在很多领域取得历史性的的突破。这里以Transformers上基于中文语料库上训练的预训练模型bert-base-chinese为模型,以BertForSequenceClassification为下游任务模型,在一个中文数据集上进行语句分类。具体包括如下内容:
 使用BERT的分词库Tokenizer
 可视化BERT注意力权重
 用BERT预训练模型微调下游任务
 训练模型

1.1 背景说明

本章用到预训练模型库Transformers,Transformers为自然语言理解(NLU)和自然语言生成(NLG)提供了最先进的通用架构(BERT、GPT、GPT-2、Transformer-XL、XLNET、XLM、T5等等),其中有超过32个100多种语言的预训练模型并同时支持TensorFlow 2.0和Pythorch1.0两大深度学习框架。可用pip安装Transformers。

pip install transformers

Transformers的官网:https://github.com/huggingface
这章使用BERT模型中汉语版本:BERT-Base, Chinese: 包括简体和繁体汉字,共12层,768个隐单元,12个Attention head,110M参数。中文 BERT 的字典大小约有 2.1 万个标识符(tokens),这些预训练模型可以从Transformers官网下载。
使用了可视化工具BertViz,它的安装步骤如下:
1)下载bertviz:
https://github.com/jessevig/bertviz
2)解压到jupyter notebook当前目录下
bertviz-master

1.1.1 查看中文BERT字典里的一些信息

1)导入需要的库
指定使用预训练模型bert-base-chinese。

import torch
from transformers import BertTokenizer
from IPython.display import clear_output
 
# 指定繁简中文 BERT-BASE预训练模型
PRETRAINED_MODEL_NAME = "bert-base-chinese"  
# 获取预测模型所使用的tokenizer
tokenizer = BertTokenizer.from_pretrained(PRETRAINED_MODEL_NAME)

2)查看tokenizer的信息

vocab = tokenizer.vocab
print("字典大小:", len(vocab))

运行结果:
字典大小: 21128
3)查看分词的一些信息

import random
random_tokens = random.sample(list(vocab), 5)
random_ids = [vocab[t] for t in random_tokens]
 
print("{0:20}{1:15}".format("token", "index"))
print("-" * 30)
for t, id in zip(random_tokens, random_ids):
    print("{0:15}{1:10}".format(t, id))

运行结果:
token index
------------------------------
##san 10978
王 4374
##and 9369
蚀 6008
60 8183

BERT 使用当初 Google NMT 提出的 WordPiece Tokenization ,将本来的 words 拆成更小粒度的 wordpieces,有效处理不在字典里头的词汇 。中文的话大致上就像是 character-level tokenization,而有 ## 前缀的 tokens 即为 wordpieces。
除了一般的wordpieces以外,BERT还有5个特殊tokens:
 [CLS]:在做分类任务时其最后一层的表示.会被视为整个输入序列的表示;
 [SEP]:有两个句子的文本会被串接成一个输入序列,并在两句之间插入这个token作为分割;
 [UNK]:没出现在BERT字典里头的字会被这个token取代;
 [PAD]:zero padding掩码,将长度不一的输入序列补齐方便做batch运算;
 [MASK]:未知掩码,仅在预训练阶段会用到。

1.1.2 使用Tokenizer分割中文语句

让我们利用中文BERT的tokenizer将一个中文句子断词。

text = "[CLS] 他移开这[MASK]桌子,就看到他的手表了。"
tokens = tokenizer.tokenize(text)
ids = tokenizer.convert_tokens_to_ids(tokens)
 
print(text)
print(tokens[:10], '...')
print(ids[:10], '...')

运行结果:
[CLS] 他移开这[MASK]桌子,就看到他的手表了。
['[CLS]', '他', '移', '开', '这', '[MASK]', '桌', '子', ',', '就'] ...
[101, 800, 4919, 2458, 6821, 103, 3430, 2094, 8024, 2218] ...

1.2 可视化BERT注意力权重

现在马上让我们看看给定上面有 [MASK] 的句子,BERT会填入什么字。

1.2.1 BERT对MAKS字的预测

"""
导入已训练好的masked语言模型并对有[MASK]的句子做预测
"""
from transformers import BertForMaskedLM
 
# 除了tokens 以外我们还需要辨別句子的segment ids
tokens_tensor = torch.tensor([ids])  # (1, seq_len)
segments_tensors = torch.zeros_like(tokens_tensor)  # (1, seq_len)
maskedLM_model = BertForMaskedLM.from_pretrained(PRETRAINED_MODEL_NAME)
clear_output()
 
# 使用masked LM 估计[MASK]位置所代表的实际标识符(token)
maskedLM_model.eval()
with torch.no_grad():
    outputs = maskedLM_model(tokens_tensor, segments_tensors)
    predictions = outputs[0]
    # (1, seq_len, num_hidden_units)
del maskedLM_model
 
# 将[MASK]位置的概率分布取前k个最有可能的标识符出来
masked_index = 5
k = 3
probs, indices = torch.topk(torch.softmax(predictions[0, masked_index], -1), k)
predicted_tokens = tokenizer.convert_ids_to_tokens(indices.tolist())
 
# 显示前k个最可能的字。一般取第一个作为预测值
print("輸入 tokens :", tokens[:10], '...')
print('-' * 50)
for i, (t, p) in enumerate(zip(predicted_tokens, probs), 1):
    tokens[masked_index] = t
    print("Top {} ({:2}%):{}".format(i, int(p.item() * 100), tokens[:10]), '...')

 运行结果:
輸入 tokens : ['[CLS]', '他', '移', '开', '这', '[MASK]', '桌', '子', ',', '就'] ...
--------------------------------------------------
Top 1 (83%):['[CLS]', '他', '移', '开', '这', '张', '桌', '子', ',', '就'] ...
Top 2 ( 7%):['[CLS]', '他', '移', '开', '这', '个', '桌', '子', ',', '就'] ...
Top 3 ( 0%):['[CLS]', '他', '移', '开', '这', '间', '桌', '子', ',', '就'] ...

BERT透过关注这桌这两个字,从2万多个wordpieces的可能性中选出"张"作为这个情境下[MASK] token的预测值,效果还是不错的。

1.2.2 导入可视化需要的库

1)导入需要的库

from transformers import BertTokenizer, BertModel
from bertv_master.bertviz import head_view

 2)创建可视化使用html配置函数

# 在 jupyter notebook 显示visualzation 
def call_html():
  import IPython
  display(IPython.core.display.HTML('''<script src="/static/components/requirejs/require.js"></script><script>// <![CDATA[
requirejs.config({ paths: { base: '/static/base', "d3": "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.8/d3.min", jquery: '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min', }, });
// ]]></script>'''))

1.2.3 可视化 

# 記得我们是使用中文 BERT
model_version = 'bert-base-chinese'
model = BertModel.from_pretrained(model_version, output_attentions=True)
tokenizer = BertTokenizer.from_pretrained(model_version)
 
# 情境 1 的句子
sentence_a = "老爸叫小宏去买酱油,"
sentence_b = "回来慢了就骂他。"
 
# 得到tokens后输入BERT模型获取注意力权重(attention)
inputs = tokenizer.encode_plus(sentence_a,sentence_b,return_tensors='pt', add_special_tokens=True)
token_type_ids = inputs['token_type_ids']
input_ids = inputs['input_ids']
attention = model(input_ids, token_type_ids=token_type_ids)[-1]
input_id_list = input_ids[0].tolist() # Batch index 0
tokens = tokenizer.convert_ids_to_tokens(input_id_list)
call_html()
 
# 用BertViz可视化
head_view(attention, tokens)

 运行结果:

 图1-1 某词对其它词注意力权重示意图
这是BERT第 10 层 Encoder block 其中一个 head 的注意力结果,从改图可以看出,左边的这个他对右边的“宏”字关注度较高。

  • 13
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用BERT实现中文文本分类的代码,包括数据预处理、模型构建和训练等步骤: 1. 数据预处理 ```python import pandas as pd import numpy as np import tensorflow as tf import os from sklearn.model_selection import train_test_split from transformers import BertTokenizer # 加载数据 df = pd.read_csv('data.csv') # 标签映射 labels = df['label'].unique() label2id = {label: index for index, label in enumerate(labels)} id2label = {index: label for index, label in enumerate(labels)} # 文本处理 tokenizer = BertTokenizer.from_pretrained('bert-base-chinese') max_length = 128 def create_inputs_targets(data): input_ids = [] attention_masks = [] targets = [] for _, row in data.iterrows(): text = row['text'] label = row['label'] # 分词 encoded_dict = tokenizer.encode_plus(text, add_special_tokens=True, max_length=max_length, pad_to_max_length=True, return_attention_mask=True, return_tensors='tf') input_ids.append(encoded_dict['input_ids']) attention_masks.append(encoded_dict['attention_mask']) targets.append(label2id[label]) return np.array(input_ids), np.array(attention_masks), np.array(targets) # 划分数据集 train_data, test_data = train_test_split(df, test_size=0.2, random_state=42) train_inputs, train_masks, train_targets = create_inputs_targets(train_data) test_inputs, test_masks, test_targets = create_inputs_targets(test_data) ``` 2. 模型构建 ```python from transformers import TFBertForSequenceClassification # 加载预训练模型 model = TFBertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=len(labels)) # 定义优化器和损失函数 optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5) loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) # 编译模型 model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy']) ``` 3. 模型训练 ```python batch_size = 32 epochs = 3 # 训练模型 history = model.fit([train_inputs, train_masks], train_targets, batch_size=batch_size, epochs=epochs, validation_split=0.1) ``` 4. 模型评估 ```python test_loss, test_accuracy = model.evaluate([test_inputs, test_masks], test_targets, batch_size=batch_size) print('Test Loss:', test_loss) print('Test Accuracy:', test_accuracy) ``` 以上是用BERT实现中文文本分类的完整代码,其中数据集需自行准备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值