Tensorflow2.0 + Transformers 实现Bert FGM对抗训练惩罚梯度损失函数
前言变种实现Transformers中的word_embeddings代码修改实验效果总结前言
之前看了很多关于NLP中应用对抗训练的文章,测试结果都很香,所以想在自己在用的模型上试一试看看能不能提升效果,参考了一些代码找到了pytroch和keras实现,但发现对于tensorflows来说更改训练过程非常繁琐,而且容易出错,如果要配合transformers实现则更难
——transformers的bert类会被打包成一个layers,在调用tf.layers时拿不到里面的embedding_layers,如果有大神知道怎么拿还望留言告知~
变种实现
但最近看了一篇文章对抗训练浅谈:意义、方法和思考(附Keras实现)
文中讲到了FGM的效果等价转化式:
***这里有个小技巧使用了一阶泰勒展开:
并且文章也给出了keras的实现代码:
def sparse_categorical_crossentropy(y_true, y_pred):"""自定义稀疏交叉熵这主要是因为keras自带的sparse_categorical_crossentropy不支持求二阶梯度。"""y_true = K.reshape(y_true, K.shape(y_pred)[:-1])y_true = K.cast(y_true, 'int32')y_true = K.one_hot(y_true, K.shape(y_pred)[-1])return K.categorical_crossentropy(y_true, y_pred)def loss_with_gradient_penalty(y_true, y_pred, epsilon=1):"""带梯度惩罚的loss"""loss = K.mean(sparse_categorical_crossentropy(y_true, y_pred))embeddings = search_layer(y_pred, 'Embedding-Token').embeddingsgp = K.sum(K.gradients(loss, [embeddings])[0].values**2)return loss + 0.5 * epsilon * pile(loss=loss_with_gradient_penalty,optimizer=Adam(2e-5),metrics=['sparse_categorical_accuracy'],)
但可惜的是在loss_with_gradient_penalty部分仍然需要调用kerasbert的方法search_layer,但transformers中的get_input_embeddings在调用时一直报错(可能我用法不对)
Transformers中的word_embeddings
但是发现tf除了通过tf.layers追踪到相应的参数,还能通过model.variables这个方法,
调用 model.variables (model为你搭建好的模型名称)会返回一个list,里面是按顺序排列好的tensor参数,在这里通过 model.variables[0] 即可找到模型的第一层参数:word_embeddings
可以看到前三组参数分别为word_embeddings, position_embeddings, token_type_embeddings, 这里我们只要取word_embeddings(NLP中对抗训练的扰动对象)即可。
代码修改
因此我们可以简单的修改原来的代码:
def sparse_categorical_crossentropy(y_true, y_pred):y_true = tf.reshape(y_true, tf.shape(y_pred)[:-1])y_true = tf.cast(y_true, tf.int32)y_true = tf.one_hot(y_true, K.shape(y_pred)[-1])return tf.keras.losses.categorical_crossentropy(y_true, y_pred)def loss_with_gradient_penalty(model,epsilon=1):def loss_with_gradient_penalty_2(y_true, y_pred):loss = tf.math.reduce_mean(sparse_categorical_crossentropy(y_true, y_pred))embeddings = model.variables[0]gp = tf.math.reduce_sum(tf.gradients(loss, [embeddings])[0].values**2)return loss + 0.5 * epsilon * gpreturn loss_with_gradient_penalty_2
调用方法:
pile(optimizer=optimizer, loss=[loss_with_gradient_penalty(bert_ner_model,1.0)],metrics=['sparse_categorical_accuracy'])
实验效果
原sparse_cross_entropy结果:
加入惩罚项(epsilon = 1)结果:
加入惩罚项(epsilon = 0.5)结果:
总结
可以看到使用该惩罚梯度损失函数,以为要计算两次梯度,训练时间增加了2倍之多,但模型效果有了一个点左右的提升,而且不容易过拟合。所以看出,尽管无法复原FGM的方法,该用效果差不多的惩罚梯度损失函数,还是可以获得一定的提升(前提是epsilon这个超参要调好)
参考链接:/archives/7234
如果觉得《Tensorflow2.0 + Transformers 实现Bert FGM对抗训练惩罚梯度损失函数》对你有帮助,请点赞、收藏,并留下你的观点哦!