模型选型
根据下游任务的领域选择合适的模型,另外区分好语言。如果算力足够的话优先选用large模型。
中文:hfl/chinese-roberta-wwm-ext以及hfl/chinese-macbert-base,这两个在中文上的效果都非常不错
英文:microsoft/deberta-v3-base
预训练
领域内预训练,就是在自己的训练集文本(或相似领域的训练集文本)上执行预训练,训练结束后模型便更贴合自己的任务了,在训练的时候收敛速度加快,效果一般来说也能带来百分位点的提升。
训练任务:
-
mlm预训练,简单来说完形填空。
-
simcse预训练,对比学习的预训练。(适用场景:主要考量embedding嵌入的相似度)
对抗训练
对抗训练的方式是对输入的embedding进行攻击,这样的好处便是不用自己再人为制定策略来对数据进行增强(对某个词进行删除替换等),而可以在训练过程中让攻击和防守自然而然的发生,具体的做法是这样的:
-
攻击:在输入的embedding上进行梯度上升,使得loss变大
-
防守:在参数上进行梯度下降,使得loss减少
这一过程会贯穿每一个训练step,因此便使得对抗的过程自然而然的发生,模型也会训练的越来越强。如FGM:将因子*参数的梯度作为原有模型参数以外的参数用于学习,从而增大了学习难度。
class FGM():
def __init__(self, model):
self.model = model
self.backup = {}
def attack(self, epsilon=1., emb_name='emb.'):
# emb_name这个参数要换成你模型中embedding的参数名
for name, param in self.model.named_parameters():
if param.requires_grad and emb_name in name:
self.backup[name] = param.data.clone()
norm = torch.norm(param.grad)
if norm != 0 and not torch.isnan(norm):
r_at = epsilon * param.grad / norm
param.data.add_(r_at)
def restore(self, emb_name='emb.'):
# emb_name这个参数要换成你模型中embedding的参数名
for name, param in self.model.named_parameters():
if param.requires_grad and emb_name in name:
assert name in self.backup
param.data = self.backup[name]
self.backup = {}
# 初始化
fgm = FGM(model)
for batch_input, batch_label in data:
# 正常训练
loss = model(batch_input, batch_label)
loss.backward() # 反向传播,得到正常的grad
# 对抗训练
fgm.attack() # 在embedding上添加对抗扰动
loss_adv = model(batch_input, batch_label)
loss_adv.backward() # 反向传播,并在正常的grad基础上,累加对抗训练的梯度
fgm.restore() # 恢复embedding参数
# 梯度下降,更新参数
optimizer.step()
model.zero_grad()
但是这样增加扰动的方式往往需要大量的训练数据,而且增加的点大概只有两三点,在打比赛的时候更适合。
Rdrop
dropout在训练和推理的时候存在不一致性,训练的时候存在drop,推理的时候不存在。因此,存在影响模型效果的可能性。为了减小dropout带来的扰动,除了调整系数外,还可以将两次输出的logits过KL散度的计算。(感觉划不来
可以直接在bert微调的时候去掉dropout,因为dropout本质上是一种缓解过拟合的方法,可能并不适合微调的场景,如果在bert的每一层都增加了扰动,最终会引发相对较大的不稳定。
在文本分类的微调中,做了实验,发现去掉dropout能有所提升。
学习率
bert微调学习率大概都是e-5。bert顶层是task specific的,因此从顶到底可以实现学习率的递减。对不同层采用不一样的学习率策略。
长文本处理
-
选取文本,标题、第一段内容、最后一段内容包含大量有用信息。
-
拆分句子多次过bert,最后pooling。
Pooling方式
除了max_pooling和mean_pooling,还有attention_pooling
class AttentionPooling(nn.Module):
def __init__(self, in_dim):
super().__init__()
self.attention = nn.Sequential(
nn.Linear(in_dim, in_dim),
nn.LayerNorm(in_dim),
nn.GELU(),
nn.Linear(in_dim, 1),
)
def forward(self, last_hidden_state, attention_mask):
w = self.attention(last_hidden_state).float()
w[attention_mask==0]=float('-inf')
w = torch.softmax(w,1)
attention_embeddings = torch.sum(w * last_hidden_state, dim=1)
return attention_embeddings