Welcome to bert4torch’s documentation!

bert4torch使用教程

0. 下载安装

安装稳定版

pip install bert4torch

安装最新版

pip install git+https://www.github.com/Tongjilibo/bert4torch.git
  • 注意事项:pip包的发布慢于git上的开发版本,git clone注意引用路径,注意权重是否需要转换

  • 测试用例git clone https://github.com/Tongjilibo/bert4torch,修改example中的预训练模型文件路径和数据路径即可启动脚本

  • 自行训练:针对自己的数据,修改相应的数据处理代码块

  • 开发环境:使用torch==1.10版本进行开发,如其他版本遇到不适配,欢迎反馈

1. 建模流程示例

from bert4torch.tokenizers import Tokenizer
from bert4torch.models import build_transformer_model, BaseModel
from bert4torch.snippets import Callback, Logger, Tensorboard, ListDataset, AdversarialTraining
import torch.nn as nn
import torch
import torch.optim as optim
from torch.utils.data import DataLoader

# 建立分词器
tokenizer = Tokenizer(dict_path, do_lower_case=True)

# 加载数据集,可以自己继承Dataset来定义
class MyDataset(ListDataset):
    @staticmethod
    def load_data(filenames):
        """读取文本文件,整理成需要的格式
        """
        D = []
        return D

def collate_fn(batch):
    '''处理上述load_data得到的batch数据,整理成对应device上的Tensor
    注意:返回值分为feature和label, feature可整理成list或tuple
    '''
    batch_token_ids, batch_segment_ids, batch_labels = [], [], []
    return [batch_token_ids, batch_segment_ids], batch_labels.flatten()

# 加载数据集
train_dataloader = DataLoader(MyDataset('file_path'), batch_size=batch_size, shuffle=True, collate_fn=collate_fn) 

# 定义bert上的模型结构,以文本二分类为例
class Model(BaseModel):
    def __init__(self) -> None:
        super().__init__()
        self.bert = build_transformer_model(config_path, checkpoint_path, with_pool=True)
        self.dropout = nn.Dropout(0.1)
        self.dense = nn.Linear(768, 2)

    def forward(self, token_ids, segment_ids):
        # build_transformer_model得到的模型仅接受list/tuple传参,因此入参只有一个时候包装成[token_ids]
        hidden_states, pooled_output = self.bert([token_ids, segment_ids])
        output = self.dropout(pooled_output)
        output = self.dense(output)
        return output
model = Model().to(device)

# 定义使用的loss和optimizer,这里支持自定义
model.compile(
    loss=nn.CrossEntropyLoss(), # 可以自定义Loss
    optimizer=optim.Adam(model.parameters(), lr=2e-5),  # 可以自定义优化器
    scheduler=None, # 可以自定义scheduler
    clip_gram_norm=1.0,  # 梯度裁剪
    grad_accumulation_steps=2,  # 梯度累积
    metrics=['accuracy']  # 可以自定义回调函数
)

# 定义评价函数
def evaluate(data):
    total, right = 0., 0.
    for x_true, y_true in data:
        y_pred = model.predict(x_true).argmax(axis=1)
        total += len(y_true)
        right += (y_true == y_pred).sum().item()
    return right / total

class Evaluator(Callback):
    """评估与保存,这里定义仅在epoch结束后调用
    """
    def __init__(self):
        self.best_val_acc = 0.

    def on_epoch_end(self, global_step, epoch, logs=None):
        val_acc = evaluate(valid_dataloader)
        if val_acc > self.best_val_acc:
            self.best_val_acc = val_acc
            model.save_weights('best_model.pt')
        print(f'val_acc: {val_acc:.5f}, best_val_acc: {self.best_val_acc:.5f}\n')


if __name__ == '__main__':
    # 指定训练的epochs,每轮的steps_per_epoch(不设置或者设置为None表示自动计算)
    # 使用默认Logger和Tensorboard
    # 使用对抗学习
    model.fit(train_dataloader, epochs=20, steps_per_epoch=100,
              callbacks=[Evaluator(), AdversarialTraining('fgm'), Logger('./test/test.log'), Tensorboard('./test/')])

2. 主要模块讲解

1) 数据处理部分

a. 精简词表,并建立分词器
token_dict, keep_tokens = load_vocab(
    dict_path=dict_path,  # 词典文件路径
    simplified=True,  # 过滤冗余部分token,如[unused1]
    startswith=['[PAD]', '[UNK]', '[CLS]', '[SEP]'],  # 指定起始的token,如[UNK]从bert默认的103位置调整到1
)
tokenizer = Tokenizer(token_dict, do_lower_case=True)  # 若无需精简,仅使用当前行定义tokenizer即可
b. 好用的小函数
  • text_segmentate(): 截断总长度至不超过maxlen, 接受多个sequence输入,每次截断最长的句子,indices表示删除的token位置

  • tokenizer.encode(): 把text转成token_ids,默认句首添加[CLS],句尾添加[SEP],返回token_ids和segment_ids,相当于同时调用tokenizer.tokenize()tokenizer.tokens_to_ids()

  • tokenizer.decode(): 把token_ids转成text,默认会删除[CLS], [SEP], [UNK]等特殊字符,相当于调用tokenizer.ids_to_tokens()并做了一些后处理

  • sequence_padding: 将序列padding到同一长度, 传入一个元素为list, ndarray, tensor的list,返回ndarry或tensor

  • parallel_apply(): 多进程或多线程地将func应用到iterable的每个元素中

  • get_pool_emb(): 根据参数设置,多种方式获取句向量

  • seed_everything(): 固定全局seed

2) 模型定义部分

  • 模型创建

'''
调用模型后,若设置with_pool, with_nsp, with_mlm,则返回值依次为[hidden_states, pool_emb/nsp_emb, mlm_scores],否则只返回hidden_states
'''
build_transformer_model(
    config_path=config_path, # 模型的config文件地址
    checkpoint_path=checkpoint_path, # 模型文件地址,默认值None表示不加载预训练模型
    model='bert', # 加载的模型结构,这里Model也可以基于nn.Module自定义后传入
    application='encoder',  # 模型应用,支持encoder,lm和unilm格式
    segment_vocab_size=2,  # type_token_ids数量,默认为2,如不传入segment_ids则需设置为0
    with_pool=False,  # 是否包含Pool部分
    with_nsp=False,  # 是否包含NSP部分
    with_mlm=False,  # 是否包含MLM部分
    return_model_config=False,  # 是否返回模型配置参数
    output_all_encoded_layers=False,  # 是否返回所有hidden_state层
    layer_add_embs=nn.Embedding(2, 768),  # 自定义额外的embedding输入
)
  • 定义loss,optimizer,scheduler, metrics等

'''
定义使用的loss、optimizer和metrics,这里支持自定义
'''
def eval(y_pred, y_true):
    # 仅做示意
    return {'rouge-1': random.random(), 'rouge-2': random.random(), 'rouge-l': random.random(), 'bleu': random.random()}

def f1(y_pred, y_true):
    # 仅做示意
    return random.random()

model.compile(
    loss=nn.CrossEntropyLoss(), # 可以自定义Loss
    optimizer=optim.Adam(model.parameters(), lr=2e-5),  # 可以自定义优化器
    scheduler=None, # 可以自定义scheduler
    metrics=['accuracy', eval, {'f1': f1}]  # loss等默认打印的字段无需设置,可多种方式自定义回调函数
)
  • 自定义模型

'''
基于bert上层的各类魔改,如last2layer_average, token_first_last_average
'''
class Model(BaseModel):
    # 需要继承BaseModel
    def __init__(self):
        super().__init__()
        self.bert = build_transformer_model(config_path, checkpoint_path)
    def forward(self):
        pass
'''
自定义fit过程,适用于自带fit()不满足需求时
'''
class Model(BaseModel):
    def fit(self, train_dataloader, steps_per_epoch, epochs):   
        train_dataloader = cycle(train_dataloader)
        self.train()
        for epoch in range(epochs):
            for bti in range(steps_per_epoch):
                train_X, train_y = next(train_dataloader)
                output = self.forward(*train_X)
                loss = self.criterion(output, train_y)
                loss.backward()
                self.optimizer.step()
                self.optimizer.zero_grad()
  • 模型保存和加载

'''
mapping: 是否以原始的key来保存,如word_embedding原始key为bert.embeddings.word_embeddings.weight
可传入mapping来按照预设的key保存,一般会设置为model.variable_mapping()
'''
# ====仅进行保存和加载====
model.save_weights(save_path, mapping={})  # 保存模型权重
model.load_weights(save_path)  # 加载模型权重

# =======断点续训========
# 在Callback中的on_epoch_end()或on_batch_end()保存需要的参数
model.save_weights(save_path, mapping={})  # 保存模型权重
model.save_steps_params(save_path)  # 保存训练进度参数,当前的epoch和step,断点续训使用
torch.save(optimizer.state_dict(), save_path)  # 保存优化器,断点续训使用
# 加载前序训练保存的参数
model.load_weights(save_path)  # 加载模型权重
model.load_steps_params(save_path)  # 加载训练进度参数,断点续训使用
state_dict = torch.load(save_path, map_location='cpu')  # 加载优化器,断点续训使用
optimizer.load_state_dict(state_dict)
from transformers import AutoModelForSequenceClassification
class Model(BaseModel):
    def __init__(self):
        super().__init__()
        self.bert = AutoModelForSequenceClassification.from_pretrained("file_path", num_labels=2)
    
    def forward(self, token_ids, attention_mask, segment_ids):
        output = self.bert(input_ids=token_ids, attention_mask=attention_mask, token_type_ids=segment_ids)
        return output.logits

3) 模型评估部分

'''支持在多个位置执行
'''
class Evaluator(Callback):
    """评估与保存
    """
    def __init__(self):
        self.best_val_acc = 0.
    def on_dataloader_end():
        # 可用于重新生成dataloader
        # 比如多个数据文件时,动态读取一个文件并重新生成dataloader的情况,如预训练
        pass
    def on_train_begin(self, logs=None):  # 训练开始时候
        pass
    def on_train_end(self, logs=None):  # 训练结束时候
        pass
    def on_batch_begin(self, global_step, local_step, logs=None):  # batch开始时候
        pass
    def on_batch_end(self, global_step, local_step, logs=None):  # batch结束时候
        # 可以设置每隔多少个step,后台记录log,写tensorboard等
        # 尽量不要在batch_begin和batch_end中print,防止打断进度条功能
        pass
    def on_epoch_begin(self, global_step, epoch, logs=None):  # epoch开始时候
        pass
    def on_epoch_end(self, global_step, epoch, logs=None):  # epoch结束时候
        val_acc = evaluate(valid_dataloader)
        if val_acc > self.best_val_acc:
            self.best_val_acc = val_acc
            model.save_weights('best_model.pt')
        print(f'val_acc: {val_acc:.5f}, best_val_acc: {self.best_val_acc:.5f}\n')

3. 其他特性讲解

1) 单机多卡训练

a. 使用DataParallel
'''DP有两种方式,第一种是forward只计算logit,第二种是forward直接计算loss
建议使用第二种,可以部分缓解负载不均衡的问题
'''
from bert4torch.models import BaseModelDP

# ===========处理数据和定义model===========

model = BaseModelDP(model)  # 指定DP模式使用多gpu
model.compile(
    loss=lambda x, _: x.mean(),  # 多个gpu计算的loss的均值
    optimizer=optim.Adam(model.parameters(), lr=2e-5),
)
b. 使用DistributedDataParallel
'''DDP使用torch.distributed.launch,从命令行启动
'''
# 需要定义命令行参数
parser = argparse.ArgumentParser()
parser.add_argument("--local_rank", type=int, default=-1)
args = parser.parse_args()

torch.cuda.set_device(args.local_rank)
device = torch.device('cuda', args.local_rank)
torch.distributed.init_process_group(backend='nccl')

# ===========处理数据和定义model===========

# 指定DDP模型使用多gpu, master_rank为指定用于打印训练过程的local_rank
model = BaseModelDDP(model, master_rank=0, device_ids=[args.local_rank], output_device=args.local_rank, find_unused_parameters=False)

# 定义使用的loss和optimizer,这里支持自定义
model.compile(
    loss=lambda x, _: x,  # 直接把forward计算的loss传出来
    optimizer=optim.Adam(model.parameters(), lr=2e-5),
)

2) 日志记录

# 自行用Tensorboard记录
from tensorboardX import SummaryWriter
class Evaluator(Callback):
    """每隔多少个step评估并记录tensorboard
    """
    def on_batch_end(self, global_step, local_step, logs=None):
        if global_step % 100 == 0:
            writer.add_scalar(f"train/loss", logs['loss'], global_step)
            val_acc = evaluate(valid_dataloader)
            writer.add_scalar(f"valid/acc", val_acc, global_step)

# 使用默认的文件Logger和Tensorboard
 model.fit(train_dataloader, epochs=20, steps_per_epoch=100,
              callbacks=[evaluator, Logger('./test/test.log'), Tensorboard('./test/')])

3) 打印训练参数

from torchinfo import summary
summary(model, input_data=next(iter(train_dataloader))[0])

Examples

History

  • v0.2.5:20221127 对抗训练从compile转为使用Callback来实现,修复1.7.1版本兼容bug, uie模型内置

  • v0.2.4:20221120 删除SpTokenizer基类中的rematch, 增加deberta_v2模型

  • v0.2.3:20221023 虚拟对抗VAT在多个ouput时支持指定,把Trainer抽象到torch4keras中,修复DP和DDP出现resume_epoch不存在的bug, tokenizer的never_split去除None, transformer_xl的bug, 增加gradient_checkpoint

  • v0.2.2:20220922 修复t5的norm_mode问题,允许hidden_size不整除num_attention_heads,支持多个schedule(如同时ema+warmup)

  • v0.2.1:20220905 兼容torch<=1.7.1的torch.div无rounding_mode,增加自定义metrics,支持断点续训,增加默认Logger和Tensorboard日志

  • v0.2.0:20220823 兼容torch<1.9.0的缺失take_along_dim,修复bart中位置向量514的问题,修复Sptokenizer对符号不转换,打印Epoch开始的时间戳,增加parallel_apply

  • v0.1.9:20220808 增加mixup/manifold_mixup/temporal_ensembling策略,修复pgd策略param.grad为空的问题,修改tokenizer支持批量

  • v0.1.8:20220717 修复原来CRF训练中loss陡增的问题,修复xlnet的token_type_ids输入显存占用大的问题

  • v0.1.7:20220710 增加EarlyStop,CRF中自带转bool类型

  • v0.1.6:20220605 增加transformer_xl、xlnet、t5_pegasus模型,prompt、预训练等示例,支持增加embedding输入,EMA策略,修复tokenizer和sinusoid的bug

  • v0.1.5:20220504 增加GAU-alpha,混合梯度,梯度裁剪,单机多卡(DP、DDP)

  • v0.1.4:20220421 增加了VAT,修复了linux下apply_embedding返回项有问题的情况

  • v0.1.3:20220409 初始版本

bert4torch.tokenizers module

Tokenization classes.

class bert4torch.tokenizers.BasicTokenizer(do_lower_case=True, never_split=('[UNK]', '[SEP]', '[PAD]', '[CLS]', '[MASK]'))[source]

Runs basic tokenization (punctuation splitting, lower casing, etc.).

tokenize(text)[source]

文本切分成token

class bert4torch.tokenizers.SpTokenizer(sp_model_path, remove_space=True, keep_accents=False, do_lower_case=False, **kwargs)[source]

基于SentencePiece模型的封装,使用上跟Tokenizer基本一致。

decode(ids)[source]

转为可读文本

id_to_token(i)[source]

id转换为对应的token

preprocess_text(inputs)[source]

从transformers包的tokenization_xlnet移植过来,主要区别是对标点符号的处理

token_to_id(token)[source]

token转换为对应的id

class bert4torch.tokenizers.Tokenizer(token_dict, do_lower_case=True, do_basic_tokenize=True, do_tokenize_unk=False, **kwargs)[source]

Bert原生分词器

decode(ids, tokens=None)[source]

转为可读文本

id_to_token(id)[source]

id转为词表中的token

rematch(text, tokens)[source]

给出原始的text和tokenize后的tokens的映射关系

static stem(token)[source]

获取token的“词干”(如果是##开头,则自动去掉##)

token_to_id(token)[source]

token转为vocab中的id

class bert4torch.tokenizers.TokenizerBase(token_start='[CLS]', token_end='[SEP]', token_unk='[UNK]', token_pad='[PAD]', token_mask='[MASK]', add_special_tokens=None, pre_tokenize=None, token_translate=None)[source]

分词器基类

decode(ids)[source]

转为可读文本

encode(first_texts, second_texts=None, maxlen=None, pattern='S*E*E', truncate_from='right', return_offsets=False)[source]

可以处理多条或者单条

id_to_token(i)[source]

id序列为对应的token

ids_to_tokens(ids)[source]

id序列转换为对应的token序列

token_to_id(token)[source]

token转换为对应的id

tokenize(text, maxlen=None)[source]

分词函数

tokens_to_ids(tokens)[source]

token序列转换为对应的id序列

class bert4torch.tokenizers.Trie[source]

直接从transformer的tokenization_utils.py中移植, 主要是为了special_tokens分词

class bert4torch.tokenizers.WordpieceTokenizer(vocab, unk_token='[UNK]', max_input_chars_per_word=100, do_tokenize_unk=False)[source]

Runs WordPiece tokenization.

tokenize(text)[source]

Tokenizes a piece of text into its word pieces.

This uses a greedy longest-match-first algorithm to perform tokenization using the given vocabulary.

For example:

input = “unaffable” output = [“un”, “##aff”, “##able”]

Args:
text: A single token or whitespace separated tokens. This should have

already been passed through BasicTokenizer.

Returns:

A list of wordpiece tokens.

bert4torch.tokenizers.convert_to_unicode(text)[source]

Converts text to Unicode (if it’s not already), assuming utf-8 input.

bert4torch.tokenizers.load_vocab(dict_path, encoding='utf-8', simplified=False, startswith=None)[source]

加载词典文件到dict

bert4torch.tokenizers.whitespace_tokenize(text)[source]

去除文本中的空白符

bert4torch.models module

bert4torch.layers module

bert4torch.snippets module

class bert4torch.snippets.AdversarialTraining(mode, adversarial={})[source]

对抗训练Callback

Parameters
  • mode – str, 对抗训练的模式,可选{‘fgm’, ‘pgd’, ‘vat’, ‘gradient_penalty’}

  • adversarial – dict, 对抗训练的参数配置,不同模式所需参数不同

class bert4torch.snippets.AutoRegressiveDecoder(start_id, end_id, maxlen, minlen=1, device='cpu')[source]

通用自回归生成模型解码基类 包含beam search和random sample两种策略

Parameters
  • start_id – int, 解码使用的起始token_id,不同预训练模型设置可能不一样

  • end_id – int, 解码使用的结束token_id,不同预训练模型设置可能不一样

  • maxlen – int, 最大解码长度

  • minlen – int, 最小解码长度, 默认为1

  • device – str, 默认为’cpu’

beam search解码

Parameters
  • inputs_raw – tensor、array、list、tuple, 解码的输入,一般为last_hidden_state, shape=[btz, seq_len, hdsz]

  • topk – int, 这里的topk即beam size

  • states

  • temperature – 温度参数,默认为1

  • min_ends

  • add_btz_dim – bool, 是否保留btz维度, 默认为True

Returns

最优解码序列。

predict(inputs, output_ids, states=None)[source]

用户需自定义递归预测函数; 说明: 定义的时候,需要用wraps方法进行装饰,传入default_rtype和use_states,其中default_rtype为字符串logits或probas,probas时返回归一化的概率, rtype=logits时则返回softmax前的结果或者概率对数。

Returns

二元组 (得分或概率, states)

random_sample(inputs, n, topk=None, topp=None, states=None, temperature=1, min_ends=1)[source]

随机采样n个结果; 说明: 非None的topk表示每一步只从概率最高的topk个中采样;而非None的topp表示每一步只从概率最高的且概率之和刚好达到topp的若干个token中采样。

Parameters
  • inputs – tensor、array、list、tuple, 解码的输入,一般为last_hidden_state, shape=[btz, seq_len, hdsz]

  • topk – int, 这里的topk即beam size

  • topp – float, 这里的topp是token的概率阈值设置

  • states

  • temperature – 温度参数,默认为1

  • min_ends

Returns

n个解码序列组成的list。

static wraps(default_rtype='probas', use_states=False)[source]

用来进一步完善predict函数

目前包含:
  1. 设置rtype参数,并做相应处理;

  2. 确定states的使用,并做相应处理;

  3. 设置温度参数,并做相应处理。

class bert4torch.snippets.DottableDict(*args, **kwargs)[source]

支持点操作符的字典

class bert4torch.snippets.FGM(model)[source]

FGM对抗训练

class bert4torch.snippets.PGD(model)[source]

PGD对抗训练

class bert4torch.snippets.VAT(model, emb_name='word_embeddings', noise_var=1e-05, noise_gamma=1e-06, adv_step_size=0.001, adv_alpha=1, norm_type='l2', **kwargs)[source]

虚拟对抗训练 https://github.com/namisan/mt-dnn/blob/v0.2/alum/adv_masked_lm.py

static adv_project(grad, norm_type='inf', eps=1e-06)[source]

L0,L1,L2正则,对于扰动计算

static kl(inputs, targets, reduction='sum')[source]

计算kl散度

:param inputs:tensor,logits :param targets:tensor,logits

class bert4torch.snippets.WebServing(host='0.0.0.0', port=8000, server='paste')[source]

简单的Web接口,基于bottlepy简单封装,仅作为临时测试使用,不保证性能。

Example:
>>> arguments = {'text': (None, True), 'n': (int, False)}
>>> web = WebServing(port=8864)
>>> web.route('/gen_synonyms', gen_synonyms, arguments)
>>> web.start()
>>> # 然后访问 http://127.0.0.1:8864/gen_synonyms?text=你好
依赖(如果不用 server=’paste’ 的话,可以不装paste库):
>>> pip install bottle
>>> pip install paste
route(path, func, arguments, method='GET')[source]

添加接口

start()[source]

启动服务

wraps(func, arguments, method='GET')[source]

封装为接口函数

Parameters
  • func – 要转换为接口的函数,需要保证输出可以json化,即需要保证 json.dumps(func(inputs)) 能被执行成功;

  • arguments – 声明func所需参数,其中key为参数名,value[0]为对应的转换函数(接口获取到的参数值都是字符串型),value[1]为该参数是否必须;

  • method – ‘GET’或者’POST’。

bert4torch.snippets.cal_ts_num(tensor_shape)[source]

查看某个tensor在gc中的数量

bert4torch.snippets.create_position_ids_from_input_ids(input_ids, padding_idx, past_key_values_length=0)[source]

生成padding_ids, 从padding_idx+1开始。忽略填充符号

bert4torch.snippets.delete_arguments(*arguments)[source]

装饰器,为类方法删除参数(主要用于类的__init__方法)

bert4torch.snippets.get_kw(cls, kwargs)[source]

保留排除cls的入参后的kwargs

Parameters
  • cls – 类

  • kwargs – dict, 所有参数

bert4torch.snippets.get_pool_emb(hidden_state=None, pooler=None, attention_mask=None, pool_strategy='cls', custom_layer=None)[source]

获取句向量

Parameters
  • hidden_state – torch.Tensor/List(torch.Tensor),last_hidden_state/all_encoded_layers

  • pooler – torch.Tensor, bert的pool_output输出

  • attention_mask – torch.Tensor

  • pool_strategy – str, (‘cls’, ‘last-avg’, ‘mean’, ‘last-max’, ‘max’, ‘first-last-avg’, ‘custom’)

  • custom_layer – int/List[int],指定对某几层做average pooling

bert4torch.snippets.get_sinusoid_encoding_table(n_position, d_hid, padding_idx=None)[source]

sinusoid编码

Parameters
  • n_position – int, 位置长度

  • d_hid – int, 位置编码长度

  • padding_idx – padding的token_ids

Returns

[seq_len, d_hid]

bert4torch.snippets.insert_arguments(**arguments)[source]

装饰器,为类方法增加参数(主要用于类的__init__方法)

bert4torch.snippets.is_string(s)[source]

判断是否是字符串

bert4torch.snippets.lowercase_and_normalize(text, never_split=())[source]

转小写,并进行简单的标准化

bert4torch.snippets.merge_segmentate(sequences, maxlen, sep='')[source]

把m个句子合并成不超过maxlen的n个句子, 主要用途是合并碎句子

Parameters
  • sequences – List(str), 短句子列表

  • maxlen – int, 最大长度

  • sep – str, 合并使用的分隔符, 可以是,。等标点符号

bert4torch.snippets.parallel_apply(func, iterable, workers, max_queue_size, callback=None, dummy=False, random_seeds=True, unordered=True)[source]

多进程或多线程地将func应用到iterable的每个元素中(直接从bert4keras中移植过来)。 注意这个apply是异步且无序的,也就是说依次输入a,b,c,但是输出可能是func(c), func(a), func(b)。

Parameters
  • callback – 处理单个输出的回调函数;

  • dummy – False是多进程/线性,True则是多线程/线性;windows需设置dummy=True

  • random_seeds – 每个进程的随机种子;

  • unordered – 若为False,则按照输入顺序返回,仅当callback为None时生效。

bert4torch.snippets.parallel_apply_generator(func, iterable, workers, max_queue_size, dummy=False, random_seeds=True)[source]

多进程或多线程地将func应用到iterable的每个元素中(直接从bert4keras中移植过来)。 注意这个apply是异步且无序的,也就是说依次输入a,b,c,但是输出可能是func(c), func(a), func(b)。结果将作为一个 generator返回,其中每个item是输入的序号以及该输入对应的处理结果。

Parameters
  • dummy – False是多进程/线性,True则是多线程/线性;

  • random_seeds – 每个进程的随机种子。

bert4torch.snippets.sequence_padding(inputs, length=None, value=0, seq_dims=1, mode='post')[source]

将序列padding到同一长度

bert4torch.snippets.text_augmentation(texts, noise_dict=None, noise_len=0, noise_p=0.0, skip_words=None, strategy='random', allow_dup=True)[source]

简单的EDA策略, 增删改

Parameters
  • texts – 需要增强的文本/文本list

  • noise_dict – 噪音数据, 元素为str的list, tuple, set

  • noise_len – 噪音长度, 优先试用

  • noise_p – 噪音比例

  • skip_words – 跳过的短语, string/list

  • strategy – 修改的策略, 包含增insert, 删delete, 改replace, 随机random

  • allow_dup – 是否允许同一个位置多次EDA

bert4torch.snippets.text_segmentate(text, maxlen, seps='\n', strips=None, truncate=True)[source]

将文本按照标点符号划分为若干个短句

Parameters
  • text – 待划分的句子

  • maxlen – int, 截断长度

  • seps – 分隔符

  • strips – ‘’.strip()

  • truncate – True表示标点符号切分后仍然超长时, 按照maxlen硬截断分成若干个短句

Returns

List[str], 划分后的句子列表

bert4torch.snippets.truncate_sequences(maxlen, indices, *sequences)[source]

截断总长度至不超过maxlen

bert4torch.losses module

class bert4torch.losses.ContrastiveLoss(*args: Any, **kwargs: Any)[source]

对比损失:减小正例之间的距离,增大正例和反例之间的距离 公式:labels * distance_matrix.pow(2) + (1-labels)*F.relu(margin-distance_matrix).pow(2) https://www.sbert.net/docs/package_reference/losses.html

Parameters
  • margin – float, 距离参数,distance>margin时候不参加梯度回传,默认为0.5

  • size_average – bool, 是否对loss在样本维度上求均值,默认为True

  • online – bool, 是否使用OnlineContrastiveLoss, 即仅计算困难样本的loss, 默认为False

forward(distances, labels, pos_id=1, neg_id=0)[source]
Parameters
  • distances – torch.Tensor, 样本对之间的预测距离, shape=[btz]

  • labels – torch.Tensor, 样本对之间的真实距离, shape=[btz]

  • pos_id – int, 正样本的label

  • neg_id – int, 负样本的label

class bert4torch.losses.FocalLoss(*args: Any, **kwargs: Any)[source]

Multi-class Focal loss implementation

forward(input, target)[source]
Parameters
  • input – torch.Tensor, shape=[N, C]

  • target – torch.Tensor, shape=[N, ]

class bert4torch.losses.LabelSmoothingCrossEntropy(*args: Any, **kwargs: Any)[source]
forward(output, target)[source]
Parameters
  • output – torch.Tensor, shape=[N, C]

  • target – torch.Tensor, shape=[N, ]

class bert4torch.losses.MultilabelCategoricalCrossentropy(*args: Any, **kwargs: Any)[source]

多标签分类的交叉熵; 说明:y_true和y_pred的shape一致,y_true的元素非0即1, 1表示对应的类为目标类,0表示对应的类为非目标类。 警告:请保证y_pred的值域是全体实数,换言之一般情况下y_pred不用加激活函数,尤其是不能加sigmoid或者softmax!预测阶段则输出y_pred大于0的类。如有疑问,请仔细阅读并理解本文。 参考:https://kexue.fm/archives/7359

forward(y_pred, y_true)[source]
Parameters
  • y_true – torch.Tensor, […, num_classes]

  • y_pred – torch.Tensor: […, num_classes]

class bert4torch.losses.RDropLoss(*args: Any, **kwargs: Any)[source]

R-Drop的Loss实现,官方项目:https://github.com/dropreg/R-Drop

Parameters
  • alpha – float, 控制rdrop的loss的比例

  • rank – str, 指示y_pred的排列方式, 支持[‘adjacent’, ‘updown’]

forward(*args)[source]

支持两种方式: 一种是y_pred, y_true, 另一种是y_pred1, y_pred2, y_true

Parameters
  • y_pred – torch.Tensor, 第一种方式的样本预测值, shape=[btz*2, num_labels]

  • y_true – torch.Tensor, 样本真实值, 第一种方式shape=[btz*2,], 第二种方式shape=[btz,]

  • y_pred1 – torch.Tensor, 第二种方式的样本预测值, shape=[btz, num_labels]

  • y_pred2 – torch.Tensor, 第二种方式的样本预测值, shape=[btz, num_labels]

class bert4torch.losses.SparseMultilabelCategoricalCrossentropy(*args: Any, **kwargs: Any)[source]

稀疏版多标签分类的交叉熵; 请保证y_pred的值域是全体实数,换言之一般情况下y_pred不用加激活函数,尤其是不能加sigmoid或者softmax,预测阶段则输出y_pred大于0的类; 详情请看:https://kexue.fm/archives/7359

forward(y_pred, y_true)[source]
Parameters
  • y_true – shape=[…, num_positive]

  • y_pred – shape=[…, num_classes]

class bert4torch.losses.TemporalEnsemblingLoss(*args: Any, **kwargs: Any)[source]

TemporalEnsembling的实现,思路是在监督loss的基础上,增加一个mse的一致性损失loss

forward(y_pred_sup, y_pred_unsup, y_true_sup, epoch, bti)[source]
Parameters
  • y_pred_sup – torch.Tensor, 监督学习样本预测值, shape=[btz, num_labels]

  • y_pred_unsup – torch.Tensor, 无监督学习样本预测值, shape=[btz, num_labels]

  • y_true_sup – int, 监督学习样本真实值, shape=[btz,]

  • epoch – int, 当前epoch

  • bti – int, 当前batch的序号

same_batch_check(y_pred_sup, y_pred_unsup, y_true_sup, bti)[source]

检测数据的前几个batch必须是一致的, 这里写死是10个

update(hist, y_pred, epoch)[source]

更新历史logit,利用alpha门控来控制比例

class bert4torch.losses.UDALoss(*args: Any, **kwargs: Any)[source]

UDALoss,使用时候需要继承一下,因为forward需要使用到global_step和total_steps https://arxiv.org/abs/1904.12848

Parameters
  • tsa_schedule – str, tsa策略,可选[‘linear_schedule’, ‘exp_schedule’, ‘log_schedule’]

  • start_p – float, tsa生效概率下限, 默认为0

  • end_p – float, tsa生效概率上限, 默认为1

  • return_all_loss – bool, 是否返回所有的loss,默认为True

Returns

loss

forward(y_pred, y_true_sup, global_step, total_steps)[source]

y_pred由[pred_sup, true_unsup, pred_unsup]三部分组成

Parameters
  • y_pred – torch.Tensor, 样本预测值, shape=[btz_sup+btz_unsup*2, num_labels]

  • y_true_sup – torch.Tensor, 样本真实值, shape=[btz_sup,]

  • global_step – int, 当前训练步数

  • total_steps – int, 训练总步数

bert4torch.optimizers module

bert4torch.optimizers.extend_with_exponential_moving_average(model, decay=0.999)[source]

模型权重的指数滑动平均, 不参加梯度更新,只是记录滑动平均的参数,给预测使用 注意区别于类似adam一类的自适应学习率优化器, 针对一阶二阶梯度的指数滑动平均, 两者完全不同

Example:
>>> # 初始化
>>> ema = ExponentialMovingAverage(model, 0.999)
>>> # 训练过程中, 更新完参数后, 同步update ema_weights weights
>>> def train():
>>>     optimizer.step()
>>>     ema.step()
>>> # eval前, 调用apply_ema_weights(); eval之后, restore_raw_weights()恢复原来模型的参数
>>> def evaluate():
>>>     ema.apply_ema_weights()
>>>     # evaluate
>>>     # 如果想保存ema后的模型, 请在restore方法之前调用torch.save()
>>>     ema.restore_raw_weights()
bert4torch.optimizers.get_linear_schedule_with_warmup(optimizer, num_warmup_steps, num_training_steps, last_epoch=-1)[source]

带warmup的schedule, 源自transformers包optimization.py中

Parameters
  • num_warmup_steps – 需要warmup的步数, 一般为 num_training_steps * warmup_proportion(warmup的比例, 建议0.05-0.15)

  • num_training_steps – 总的训练步数, 一般为 train_batches * num_epoch

bert4torch.activations module

Indices and tables