Welcome to transformers-keras’s documentation!¶
安装¶
transformers-keras
是使用Keras
实现的基于Transformer
模型的库,它可以加载预训练模型的权重,也实现了多个下游的NLP任务。
项目主页:transformers-keras
使用pip安装¶
transformers-keras可以直接使用pip安装:
pip install -U transformers-keras
使用pip安装的方式会自动把所需要的依赖都安装好。
如果你需要手动安装这些依赖,可以查看下面的依赖列表:
快速入门¶
本章节将会带领大家快速入门transformers-keras
的使用。
transformers-keras
功能强大,它可以:
加载不同的预训练模型权重
使用预训练模型微调下游任务
与其它类似的库相比,transformers-keras
有以下优势:
清晰简单的API,使用纯粹的
keras
构建模型,没有任何多余的封装常用任务的数据管道构建,准备好数据就可以开始训练模型,不需要担心数据处理逻辑
可以直接导出
SavedModel
格式的模型,使用 tensorflow/serving 直接部署最小依赖,除了
tensorflow
,不附带任何其它庞大的第三方库
加载预训练模型的权重¶
基于 BERT 的模型支持加载以下预训练权重:
所有使用 google-research/bert 训练的 BERT 模型
所有使用 ymcui/Chinese-BERT-wwm 训练的 BERT 和 RoBERTa 模型
基于 ALBERT 的模型支持加载以下预训练权重:
所有使用 google-research/albert 训练的 ALBERT 模型
这里是基于 BERT 的模型的使用示例。所有基于 ALBERT 的模型用法和BERT类似,所以这里只使用 BERT 为例,不再重复用ALBERT举例。
BERT特征抽取示例¶
我们这里直接加载预训练的BERT权重,来做句子特征的抽取。
from transformers_keras import Bert
# 加载预训练模型权重
model = Bert.from_pretrained('/path/to/pretrained/bert/model')
input_ids = tf.constant([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]])
segment_ids = tf.constant([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
attention_mask = tf.constant([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
sequence_output, pooled_output = model(inputs=[input_ids, segmet_ids, attention_mask], training=False)
通过上述代码,就可以抽取出模型的sequence_output
和pooled_output
特征向量。
其中:
sequence_output
是BERT模型最后的输出状态,是一个形状为(batch_size, sequence_length, hidden_size)
的张量。pooled_output
是BERT的[CLS]
位置的向量经过Dense层得到的pooling
输出。它是一个形状为(batch_size, hidden_size)
的张量。
你可以通过这两个输出,采取不同的手段,来获取输入句子的向量。例如:
使用
mean-pooling
策略计算句子向量使用
[CLS]
策略来获取句子向量(sequence_output[:, 0, )]
即为[CLS]
的向量表示)使用
pooled_output
直接作为句子的向量
另外,可以通过构造器参数 return_states=True
和 return_attention_weights=True
来获取每一层的 hidden_states
和 attention_weights
输出:
from transformers_keras import Bert
# 加载预训练模型权重
model = Bert.from_pretrained(
'/path/to/pretrained/bert/model',
return_states=True,
return_attention_weights=True)
input_ids = tf.constant([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]])
segment_ids = tf.constant([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
attention_mask = tf.constant([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
sequence_outputs, pooled_output, hidden_states, attn_weights = model(inputs=[input_ids, segment_ids, attention_mask], training=False)
其中:
hidden_states
就是每一层hidden_state
stack在一起的输出。它是一个形状为(batch_size, num_layers, sequence_length, hiddeb_size)
的张量。attn_weights
就是每一层attention_weights
stack在一起的输出。它是一个形状为(batch_size, num_layers, num_attention_heads, sequence_length, sequence_length)
的张量。
微调下游任务示例¶
这里有以下几个示例:
使用BERT微调 文本分类 任务
使用BERT微调 问答 任务
使用BERT微调文本分类任务¶
你可以使用BERT构建序列的二分类网络:
from transformers_keras import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained('/path/to/pretrained/model')
model.summary()
model.compile(
loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc']
)
可以得到下面的模型输出:
Model: "bert_for_sequence_classification"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_ids (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
segment_ids (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
attention_mask (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
bert (BertModel) ((None, None, 768), 59740416 input_ids[0][0]
segment_ids[0][0]
attention_mask[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, 2) 1538 bert[0][1]
==================================================================================================
Total params: 59,741,954
Trainable params: 59,741,954
Non-trainable params: 0
__________________________________________________________________________________________________
要训练网络,你需要准备训练数据。训练数据的格式采用JSONL
格式,即文件的每一行都是一个JSON
。例如:
{"sequence": "我喜欢自然语言处理(NLP)", "label": 1}
{"sequence": "我不喜欢自然语言处理(NLP)", "label": 0}
需要注意的是,每个JSON都需要包含两个字段:
sequence
,即文本序列label
,即文本的类别ID
然后,就可以开始构造数据集,训练模型了:
from transformers_keras import SequenceClassificationDataset
input_files = [
"filea.jsonl",
"fileb.jsonl"
]
dataset = SequenceClassificationDataset.from_jsonl_files(
input_files=input_files,
batch_size=32,
)
# 你可以查看dataset长什么样
print(next(iter(dataset)))
model.fit(
dataset,
epochs=10,
)
使用BERT微调问答任务¶
另一个例子,使用BERT来做Question Answering:
from transformers_keras import BertForQuestionAnswering
model = BertForQuestionAnswering.from_pretrained('/path/to/pretrained/model')
model.summary()
model.compile(
loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['acc']
)
可以得到下面的模型输出:
Model: "bert_for_question_answering"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_ids (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
segment_ids (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
attention_mask (InputLayer) [(None, None)] 0
__________________________________________________________________________________________________
bert (BertModel) ((None, None, 768), 59740416 input_ids[0][0]
segment_ids[0][0]
attention_mask[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, None, 2) 1538 bert[0][0]
__________________________________________________________________________________________________
head (Lambda) (None, None) 0 dense[0][0]
__________________________________________________________________________________________________
tail (Lambda) (None, None) 0 dense[0][0]
==================================================================================================
Total params: 59,741,954
Trainable params: 59,741,954
Non-trainable params: 0
__________________________________________________________________________________________________
同样的,训练数据使用JSONL
格式。每一行都是一个JSON,例如:
{"question": "距离地球最近的天体是", "context": "月亮,你非要选的话,选c好了", "answer": "月亮"}
{"question": "从月球上看地球的唯一建筑物是", "context": "从月球上看地球的唯一建筑物是:中国的万里长城", "answer": "万里长城"}
其中,每个JSON需要包含以下字段:
context
,即上下文question
,即问题answer
,即答案
准备好数据集,就可以开始训练了:
from transformers_keras import QuestionAnsweringDataset
input_files = ["filea.jsonl", "fileb.jsonl"]
dataset = QuestionAnsweringDataset.from_jsonl_files(
input_files=input_files,
batch_size=32,
)
# 你可以查看dataset长什么样
print(next(iter(dataset)))
model.fit(
dataset,
epochs=10,
)
transformers-keras
还支持其它不同的任务:
序列标注,例如NER、POS
句向量,例如SimCSE
这些任务会在下面的文档里介绍~。
下游任务模型概述¶
统一的API¶
Question Answering模型介绍¶
Sentence Embedding模型介绍¶
Sentiment Analysis模型介绍¶
细粒度的情感分析,即Aspect-based Sentiment Analysis
,通常会把它作为一个序列标注任务。也就是说,会对一个序列的 Aspect Term 和 Opinion Term进行序列标注,例如使用常见的B-I-O标注。标注过程中,可以同时对Aspect进行分类,对Opinion进行情感极性分类。
这种方式有一个缺点就是,对于类别经常变化的情况不太友好,一般来说需要重新训练模型,或者干脆对每一个领域训练一个单独的序列标注模型。总之,这种方式还是显得比较麻烦。
我本人更喜欢以下的方式:把Aspect Term Extraction、Opinion Term Extraction、Opinion Sentiment Classification三个任务,都使用Question Answering的方式来处理。
也就是说,Aspect-based Sentiment Analysis可以分成两个部分:
Aspect Term Extraction,只需要抽出Aspect Term,不需要进行分类
Opinion Term Extraction & Classification,需要抽取出Opinion Term,同时进行情感极性分类
这两个部分使用两个独立的模型来处理,但是都是使用Question Answering的方式基于BERT模型实现。
Aspect Term Extraction¶
Opinion Term Extraction and Classification¶
Sequence Classification模型介绍¶
Token Classification模型介绍¶
训练数据输入管道¶
本库提供了方便、统一、高性能的数据输入管道,只要按照一定格式准备好文件,即可构建出训练数据输入管道,立即开始模型训练。
未完待续…
统一的API¶
Question Answering模型的数据输入管道¶
Sentence Embedding模型的数据输入管道¶
Sentiment Analysis模型的数据输入管道¶
Sequence Classification模型的数据输入管道¶
Token Classification模型的输入输入管道¶
完整解决方案示例¶
统一的Pipeline:从训练到部署¶
Question Answering完整解决方案¶
Sentence Embedding完整解决方案¶
Sentiment Analysis完整解决方案¶
Sequence Classification完整解决方案¶
Token Classification完整解决方案¶
进阶使用¶
支持的高级使用方法:
加载预训练模型权重的过程中跳过一些参数的权重
加载第三方实现的模型的权重
跳过一些预训练模型的权重¶
有些情况下,你可能会在加载预训练权重的过程中,跳过一些权重的加载。这个过程很简单。
这里是一个示例:
from transformers_keras import Bert, Albert
ALBERT_MODEL_PATH = '/path/to/albert/model'
albert = Albert.from_pretrained(
ALBERT_MODEL_PATH,
# return_states=False,
# return_attention_weights=False,
skip_token_embedding=True,
skip_position_embedding=True,
skip_segment_embedding=True,
skip_pooler=True,
...
)
BERT_MODEL_PATH = '/path/to/bert/model'
bert = Bert.from_pretrained(
BERT_MODEL_PATH,
# return_states=False,
# return_attention_weights=False,
skip_token_embedding=True,
skip_position_embedding=True,
skip_segment_embedding=True,
skip_pooler=True,
...
)
所有支持跳过加载的权重如下:
skip_token_embedding
, 跳过加载ckpt的token_embedding
权重skip_position_embedding
, 跳过加载ckpt的position_embedding
权重skip_segment_embedding
, 跳过加载ckpt的token_type_emebdding
权重skip_embedding_layernorm
, 跳过加载ckpt的layer_norm
权重skip_pooler
, 跳过加载ckpt的pooler
权重
加载第三方实现的模型的权重¶
在有一些情况下,第三方实现了一些模型,它的权重的结构组织和官方的实现不太一样。对于一般的预训练加载库,实现这个功能是需要库本身修改代码来实现的。本库通过 适配器模式 提供了这种支持。用户只需要继承 AbstractAdapter 即可实现自定义的权重加载逻辑。
from transformers_keras.adapters import AbstractAdapter
from transformers_keras import Bert, Albert
# 自定义的BERT权重适配器
class MyBertAdapter(AbstractAdapter):
def adapte_config(self, config_file, **kwargs):
# 在这里把配置文件的配置项,转化成本库的BERT需要的配置
# 本库实现的BERT所需参数都在构造器里,可以简单方便得查看
pass
def adapte_weights(self, model, config, ckpt, **kwargs):
# 在这里把ckpt的权重设置到model的权重里
# 可以参考BertAdapter的实现过程
pass
# 加载预训练权重的时候,指定自己的适配器 `adapter=MyBertAdapter()`
bert = Bert.from_pretrained('/path/to/your/bert/model', adapter=MyBertAdapter())
# 自定义的ALBERT权重适配器
class MyAlbertAdapter(AbstractAdapter):
def adapte_config(self, config_file, **kwargs):
# 在这里把配置文件的配置项,转化成本库的BERT需要的配置
# 本库实现的ALBERT所需参数都在构造器里,可以简单方便得查看
pass
def adapte_weights(self, model, config, ckpt, **kwargs):
# 在这里把ckpt的权重设置到model的权重里
# 可以参考AlbertAdapter的实现过程
pass
# 加载预训练权重的时候,指定自己的适配器 `adapter=MyAlbertAdapter()`
albert = Albert.from_pretrained('/path/to/your/albert/model', adapter=MyAlbertAdapter())