Github 地址:https://github.com/geyingli/unif
有数据,想要快速实现你的想法?轻便、易使用的自然语言处理联合框架,帮你快速搭建各类常用深度学习模型 (Transformer, GPT-2, BERT, ALBERT, UniLM, XLNet, ELECTRA),同时对于 BERT 系列,支持高效用的蒸馏 (TinyBERT, FastBERT)。支持各类上下游任务 (语言模型、文本分类、文本生成、命名实体识别、机器阅读理解、机器翻译、序列标注等)。
git clone https://github.com/geyingli/unif
cd unif
python3 setup.py install --user
import uf
# 载入模型(使用 demo 配置文件进行示范)
model = uf.BERTClassifier(config_file='demo/bert_config.json', vocab_file='demo/vocab.txt')
# 定义训练样本
X, y = ['久旱逢甘露', '他乡遇故知'], [1, 0]
# 训练
model.fit(X, y)
# 推理
print(model.predict(X))
领域 | API | 简介 |
---|---|---|
语言模型 | BERTLM |
结合 MLM 和 NSP 任务,随机采样自下文及其他文档 |
RoBERTaLM |
仅 MLM 任务,采样至文档结束 | |
ALBERTLM |
结合 MLM 和 SOP,随机采样自上下文及其他文档 | |
ELECTRALM |
结合 MLM 和 RTD,生成器与判别器联合训练 | |
VAELM |
可生成语言文本负样本,也可提取向量用于聚类 | |
GPT2LM |
自回归式文本生成 | |
UniLM |
结合双向、单向及 Seq2Seq 建模的全能语言模型 | |
命名实体识别 | BERTNER |
- |
BERTCRFNER |
结合 CRF | |
BERTCRFCascadeNER |
识别与分类同时进行的级联架构 | |
机器翻译 | TransformerMT |
共享词表,标准 Seq2Seq 架构 |
机器阅读理解 | BERTMRC |
- |
RoBERTaMRC |
- | |
ALBERTMRC |
- | |
ELECTRAMRC |
- | |
SANetMRC |
引入 Sentence Attention | |
BERTVerifierMRC |
抽取 answer span 的同时判断可答性 | |
RetroReaderMRC |
抽取 answer span 的同时判断可答性 | |
单 Label 分类 | TextCNNClassifier |
小而快 |
BERTClassifier |
- | |
XLNetClassifier |
- | |
RoBERTaClassifier |
- | |
ALBERTClassifier |
- | |
ELECTRAClassifier |
- | |
WideAndDeepClassifier |
通过 Wide & Deep 架构融合句子级别特征 | |
SemBERTClassifier |
通过 SemBERT 架构融合字级别的特征 | |
PerformerClassifier |
引入 FAVOR+ 加速推理 | |
UDAClassifier |
结合一致性学习的半监督学习算法 | |
多 Label 分类 | BERTBinaryClassifier |
- |
XLNetBinaryClassifier |
- | |
RoBERTaBinaryClassifier |
- | |
ALBERTBinaryClassifier |
- | |
ELECTRABinaryClassifier |
- | |
序列标注 | BERTSeqClassifier |
- |
XLNetSeqClassifier |
- | |
RoBERTaSeqClassifier |
- | |
ALBERTSeqClassifier |
- | |
ELECTRASeqClassifier |
- | |
模型蒸馏 | TinyBERTClassifier |
大幅压缩模型参数,提速十倍以上 |
FastBERTClassifier |
动态推理,易分样本提前离开模型 |
文档目前还不完善,善用 help(XXX)
能帮你获得更多 API 的使用细节。
一步创建新模型:
model = uf.BERTClassifier(
config_file, vocab_file,
max_seq_length=128,
label_size=2,
init_checkpoint=None, # 预训练参数路径
output_dir='./output',
gpu_ids='0,1,3,5',
drop_pooler=False, # 建模时跳过 pooler 层
do_lower_case=True,
truncate_method='LIFO') # longer-FO/LIFO/FIFO
下载知名公开预训练参数:
# 查看可下载列表
uf.list_resources()
# ┌──────────────────────────┬──────────┬──────────────┬───────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────┐
# ┊ Key ┊ Backbone ┊ Organization ┊ Site ┊ URL ┊
# ├──────────────────────────┼──────────┼──────────────┼───────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────┤
# ┊ bert-base-zh ┊ BERT ┊ Google ┊ https://github.com/google-research/bert ┊ https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip ┊
# ┊ albert-tiny-zh ┊ ALBERT ┊ Google ┊ https://github.com/google-research/albert ┊ https://storage.googleapis.com/albert_zh/albert_tiny_zh_google.zip ┊
# ┊ albert-small-zh ┊ ALBERT ┊ Google ┊ https://github.com/google-research/albert ┊ https://storage.googleapis.com/albert_zh/albert_small_zh_google.zip ┊
# ┊ albert-base-zh ┊ ALBERT ┊ Brightmart ┊ https://github.com/brightmart/albert_zh ┊ https://storage.googleapis.com/albert_zh/albert_base_zh_additional_36k_steps.zip ┊
# ┊ albert-large-zh ┊ ALBERT ┊ Brightmart ┊ https://github.com/brightmart/albert_zh ┊ https://storage.googleapis.com/albert_zh/albert_large_zh.zip ┊
# ┊ albert-xlarge-zh ┊ ALBERT ┊ Brightmart ┊ https://github.com/brightmart/albert_zh ┊ https://storage.googleapis.com/albert_zh/albert_xlarge_zh_183k.zip ┊
# ┊ bert-wwm-ext-base-zh ┊ BERT ┊ HFL ┊ https://github.com/ymcui/Chinese-BERT-wwm ┊ https://drive.google.com/uc?export=download&id=1buMLEjdtrXE2c4G1rpsNGWEx7lUQ0RHi ┊
# ┊ roberta-wwm-ext-base-zh ┊ BERT ┊ HFL ┊ https://github.com/ymcui/Chinese-BERT-wwm ┊ https://drive.google.com/uc?export=download&id=1jMAKIJmPn7kADgD3yQZhpsqM-IRM1qZt ┊
# ┊ roberta-wwm-ext-large-zh ┊ BERT ┊ HFL ┊ https://github.com/ymcui/Chinese-BERT-wwm ┊ https://drive.google.com/uc?export=download&id=1dtad0FFzG11CBsawu8hvwwzU2R0FDI94 ┊
# ┊ xlnet-mid-zh ┊ XLNet ┊ HFL ┊ https://github.com/ymcui/Chinese-XLNet ┊ https://drive.google.com/uc?export=download&id=1342uBc7ZmQwV6Hm6eUIN_OnBSz1LcvfA ┊
# ┊ xlnet-base-zh ┊ XLNet ┊ HFL ┊ https://github.com/ymcui/Chinese-XLNet ┊ https://drive.google.com/uc?export=download&id=1m9t-a4gKimbkP5rqGXXsEAEPhJSZ8tvx ┊
# ┊ electra-180g-small-zh ┊ ELECTRA ┊ HFL ┊ https://github.com/ymcui/Chinese-ELECTRA ┊ https://drive.google.com/uc?export=download&id=177EVNTQpH2BRW-35-0LNLjV86MuDnEmu ┊
# ┊ electra-180g-small-ex-zh ┊ ELECTRA ┊ HFL ┊ https://github.com/ymcui/Chinese-ELECTRA ┊ https://drive.google.com/uc?export=download&id=1NYJTKH1dWzrIBi86VSUK-Ml9Dsso_kuf ┊
# ┊ electra-180g-base-zh ┊ ELECTRA ┊ HFL ┊ https://github.com/ymcui/Chinese-ELECTRA ┊ https://drive.google.com/uc?export=download&id=1RlmfBgyEwKVBFagafYvJgyCGuj7cTHfh ┊
# ┊ electra-180g-large-zh ┊ ELECTRA ┊ HFL ┊ https://github.com/ymcui/Chinese-ELECTRA ┊ https://drive.google.com/uc?export=download&id=1P9yAuW0-HR7WvZ2r2weTnx3slo6f5u9q ┊
# └──────────────────────────┴──────────┴──────────────┴───────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────┘
# 下载预训练模型包
uf.download('bert-wwm-ext-base-zh')
任务后期需要大量的训练,可以通过配置文件,方便地整理和读取模型:
# 写入配置文件
assert model.output_dir is not None # 为空的话模型就白训了
model.cache('key', cache_file='.cache')
# 从配置文件读取
model = uf.load('key', cache_file='.cache')
程序还没执行结束,内存就不够用了?试试删除模型 del model
或重置 model.reset()
。
# 训练
model.fit(
X=None, y=None, sample_weight=None,
X_tokenized=None, # 特定场景下使用,e.g. 使用你自己的分词工具
batch_size=32,
learning_rate=5e-05,
target_steps=None, # 放空代表直接训练到 `total_steps`,不中途停止;否则为本次训练暂停点
total_steps=-3, # -3 代表自动计算数据量并循环三轮
warmup_ratio=0.1,
print_per_secs=1, # 多少秒打印一次信息
save_per_steps=1000,
**kwargs) # 其他参数,下文介绍
# 推理
model.predict(
X=None, X_tokenized=None, batch_size=8)
# 评分
model.score(
X=None, y=None, sample_weight=None, X_tokenized=None,
batch_size=8)
# 常规训练流程示范
assert model.output_dir is not None # 非空才能保存模型参数
for loop_id in range(10): # 假设训练途中一共验证 10 次
model.fit(X, y, target_steps=((loop_id + 1) * -0.6), total_steps=-6) # 假设一共训练 6 轮
model.cache('dev-%d' % loop_id) # 保存一次模型
print(model.score(X_dev, y_dev)) # 查看模型表现
复用训练数据?可以尝试先存为 TFRecords,训练时读取:
# 缓存数据
model.to_tfrecords(
X=None, y=None, sample_weight=None, X_tokenized=None,
tfrecords_file='./train.tfrecords') # 一次只能存一个文件
# 边读边训
model.fit_from_tfrecords(
tfrecords_files=['./train.tfrecords-0', './.tfrecords-1'], # 同时从两个 TFRecords 文件读取
n_jobs=3, # 启动三个线程
batch_size=32, # 以下参数和 `.fit()` 中参数相同
learning_rate=5e-05,
target_steps=None,
total_steps=-3,
warmup_ratio=0.1,
print_per_secs=1,
save_per_steps=1000,
**kwargs)
训练所用的条件参数 kwargs:
# 优化器
model.fit(X, y, ..., optimizer='gd')
model.fit(X, y, ..., optimizer='adam')
model.fit(X, y, ..., optimizer='adamw') # 默认
model.fit(X, y, ..., optimizer='lamb')
# 分层学习率 (少量模型不适用)
model.fit(X, y, ..., layerwise_lr_decay_ratio=0.85) # 默认为 None
print(model._key_to_depths) # 衰减比率
# 对抗式训练
model.fit(X, y, ..., adversarial='fgm', epsilon=0.5) # FGM
model.fit(X, y, ..., adversarial='pgd', epsilon=0.05, n_loop=2) # PGD
model.fit(X, y, ..., adversarial='freelb', epsilon=0.3, n_loop=3) # FreeLB
model.fit(X, y, ..., adversarial='freeat', epsilon=0.001, n_loop=3) # FreeAT
model.fit(X, y, ..., adversarial='smart', epsilon=0.01, n_loop=2, prtb_lambda=0.5, breg_miu=0.2, tilda_beta=0.3) # SMART (仅 Classifier 可用)
# 置信度过滤 (仅 Classifier 可用)
model.fit(X, y, ..., conf_thresh=0.99) # 默认为 None
存在变量命名不同而无法加载,可通过以下步骤解决:
# 查看从 `init_checkpoint` 初始化失败的变量
assert model.init_checkpoint is not None
model.init()
print(model.uninited_vars)
# 在 `checkpoint` 中寻找对应的参数名
print(uf.list_variables(model.init_checkpoint))
# 人工添加映射关系到 `assignment_map`
model.assignment_map['var_1_in_ckpt'] = model.uninited_vars['var_1_in_model']
model.assignment_map['var_2_in_ckpt'] = model.uninited_vars['var_2_in_model']
# 重新读取预训练参数
model.reinit_from_checkpoint()
# 看看变量是否从初始化失败的名单中消失
print(model.uninited_vars)
# 保存参数及配置(避免下次载入预训练参数时,重复上述步骤)
assert model.output_dir is not None
model.cache('key')
直接给参数赋值如何?当然是可以的:
import numpy as np
# 获取参数
variable = model.trainable_variables[0]
# 赋值
model.assign(variable, value)
# 查看参数
print(model.sess.run(variable))
# 保存赋值后的参数及配置
assert model.output_dir is not None
model.cache('key')
# 导出 PB 文件到 `output_dir` 下
assert model.output_dir is not None
model.export(
export_dir, # 导出目录
rename_inputs=None, # 重命名输入
rename_outputs=None, # 重命名输出
ignore_outputs=None) # 裁剪多余输出
问:如何实现多个 segment 的输入?
答:使用 list 组合多个 segment 的输入,如 X = [['文档1句子1', '文档1句子2', '文档1句子3'], ['文档2句子1', '文档2句子2']]
,模型会自动按顺序拼接并添加分隔符。
问:如何查看切词结果?
答:通过 model.tokenizer.tokenize(text)
可查看切词结果。另外也可通过 model.convert(X)
查看切词与 ID 转换后的矩阵。
问:如何使用自己的切词工具?
答:在训练和推理时预先将传入参数 X
改为 X_tokenized
,模型将直接跳过原有的的分词步骤。需要注意的是,分词结果同样需要基于 list
承载,例如原先由 x
承载的 ['黎明与夕阳']
,由 X_tokenized
承载后需呈现 ['黎', '##明', '与', '夕', '##阳']
的形式。
问:如何实现 TinyBERT 和 FastBERT 复蒸馏?
答:TinyBERTClassifier
训练完成后使用 .to_bert()
将变量重命名保存,而后使用 FastBERTClassifier
常规读取生成的 checkpoint 和配置文件即可。
我们欢迎一切有效的 pull request,加入我们,成为 contributors 一员。 核心的代码架构如下图所示,新的模型开发仅需要在 application 下添加新的类,这些类可以由现有算法库 modeling 中的算子组合而来,也可以自行编写。
框架目前主要为我个人及团队所用,靠兴趣推动至今。如果能受到更多人,包括您的认可,我们会愿意投入更多精力进行丰富与完善,早日推出第一个正式版本。如果您喜欢,请点个 star 作为支持。如果有希望实现的 SOTA 算法,留下 issue,我们会酌情考虑,并安排时间为你编写。通常三日以内可以实现。任何需求和建议都不尽欢迎。最后,感谢你读到这里。