unif icon indicating copy to clipboard operation
unif copied to clipboard

对抗训练

Open yupeijei1997 opened this issue 3 years ago • 28 comments

作者您好,

with tf.control_dependencies([init_op]): # fix perturbation # Scale randomly initialized permutation, to make sure norm # of r is smaller than epsilon. shape = tf.cast(np.prod(init_r.shape.as_list()), tf.float32) r = tf.divide(init_r, tf.sqrt(shape)) r = tf.IndexedSlices(values=r, indices=grad.indices, dense_shape=grad.dense_shape) attack_op = param.assign(param + r)

with tf.control_dependencies([init_op]): # fix perturbation # Scale randomly initialized permutation, to make sure norm # of r is smaller than epsilon. r = tf.divide(init_r, tf.norm(init_r, np.inf)) r = tf.IndexedSlices(values=r, indices=grad.indices, dense_shape=grad.dense_shape) attack_op = param.assign(param + r)

关于对抗训练的FreeLB模式中初始扰动r,上述代码两个部分对r的计算似乎不太一样,实际用的时候是否第二种方式会直接覆盖掉第一种,需要注释第一种吗?这里的逻辑有点不太明白 另,有关对抗训练中dropout mask在每次扰动计算中保持一致的方式希望可以实现~

yupeijei1997 avatar Mar 22 '21 06:03 yupeijei1997

你好~

有快一年没有碰这段代码了。我看了一下,应该是当时写的时候上面那段忘了删了,不过因为下面的r会覆盖上面的r,所以删不删其实表现上没有影响。具体来说,上下两段的区别是,一个用shape进行固定取值的缩放,一个直接使全局除以最大值进行归一化,都是为了避免随机扰动过于极端,给训练带来噪声,实际使用中效果其实差不多。删掉上面那段就可以了。

对于dropout mask,之前有所了解,是使用额外的随机参数记录dropout,叠加扰动的时候dropout不变。实现起来不难,但需要时间。最近比较忙,可能要过段时间才能考虑了。谢谢你的建议

geyingli avatar Mar 23 '21 00:03 geyingli

谢谢您的解答~

另外关于FreeLB里的这段代码,我做了点修改,加了一个判断,当扰动的范数超过epsilon ball的范围时,再进行缩放。 似乎论文里是这么说的,您看下是否正确~ cur_r = (acc_r + tmp_r) * tf.divide(ARGS.epsilon, norm) -> cur_r = tf.cond(norm > ARGS.epsilon, lambda: (acc_r + tmp_r) * tf.divide(ARGS.epsilon, norm), lambda: (acc_r + tmp_r))

yupeijei1997 avatar Mar 25 '21 05:03 yupeijei1997

当然是可以的,对抗式训练是实践科学,改了代码以后可以试试表现如何。表现的好自然是不错的,表现的不好也是十分正常的事情。可以多尝试做做变通,没必要一定和论文保持一致,毕竟换一个数据集可能就是另一番景象。代码没问题~

geyingli avatar Mar 25 '21 06:03 geyingli

感谢您的解答~ 还有这几天在自己尝试实现dropout mask,阅读了一下tf.nn.Dropout的源码,发现其mask是由如下两行代码(来源于贴出的tensorflow中的Dropout实现)实现的; random_tensor = random_ops.random_uniform( noise_shape, seed=seed, dtype=x.dtype) keep_mask = random_tensor >= rate 但是其只是返回了ret结果,如果我想要获得mask,可能需要修改tensorflow的源码,在dropout_v2参数中增加keep_mask参数,并在返回值中增加keep_mask,请问这个思路正确吗? ret = x * scale * math_ops.cast(keep_mask, x.dtype) 我还发现dropout_v2()函数有一个参数是seed,但是查阅API后发现他只是让多次会话时,可以生成的keep mask可以保持一致,但是对于某一次会话,不同step之间生成的keep mask还是会不同。 想请问您说的使用额外的随机参数记录dropout,具体该怎么做呢~

tensorflow中的Dropout实现: Dropout

yupeijei1997 avatar Mar 27 '21 13:03 yupeijei1997

小意思~

实现mask固定其实就是设置一个和tensor X=[batch_size, seq_length, hidden_size] 一模一样大小的随机变量mask=tf.get_variable('mask', shape=X.shape, initializer=tf.random_uniform_initializer(0, 1)) 然后通过tf.greater(mask, 0.1)*X实现手动dropout,取代tf.nn.dropout。 那么如何保证每次叠加扰动的时候mask不变呢,那么就是用tf.control_dependencies([init_op]),其中init_op就是随机初始化一次mask,更具体怎么写,相信你明白~我就不多说了,没有那么复杂

另外为了保证项目代码的可迁移性,比如换了新设备能迅速跑起来,不太建议修改tensorflow源码。修改源码还有可能会造成不可预知的错误,你需要保证tensorflow内部所有引用了tf.nn.dropout的代码不会因为这一项修改而出错

geyingli avatar Mar 28 '21 01:03 geyingli

您好~谢谢您的回复

按照您的思路,我尝试写了如下代码,x1、x2想模拟正常样本和对抗样本,但是发现对x1、x2,mask以后只有x1_mask和keep_mask保持一致,而x2_mask则没有。不知道问题出在了哪里~ 也许是因为我对 init_op = tf.variables_initializer([random_tensor]) with tf.control_dependencies([init_op]): 这两行的操作理解的还不够深。

在您的freelb的代码中的init_r于此类似,我仔细调试您的代码后了解到,init_r会在sess.run(self._train_op)前被初始化一次,利用 local_init_op = tf.variables_initializer(variables) module.sess.run(local_init_op)来实现,在https://github.com/geyingli/unif/blob/33280b5826a610427fdf62fa6185d5f3dfa03851/uf/processing.py#L71此处。

我感觉这行代码 init_op = tf.variables_initializer([random_tensor]) 是对random_tensor再次随机初始化一次, 然后通过with tf.control_dependencies([init_op]) 控制依赖,让之后的keep_mask依赖这次初始化的random_tensor。 但是我的结果中只有x1_mask依赖成功了,而x2_mask失败了,想请教您这是什么导致的呢~

`import tensorflow as tf

x1 = tf.constant([[[1., 2.], [3., 4.]], [[5., 6.], [7., 8.]]], dtype=tf.float32) x2 = tf.constant([[[10., 20.], [30., 40.]], [[50., 60.], [70., 80.]]], dtype=tf.float32) random_tensor = tf.get_variable('random_tensor', shape=[2, 2, 2], initializer=tf.random_uniform_initializer(minval=0, maxval=1)) keep_mask = tf.cast(tf.greater(random_tensor, 0.5), tf.float32)

init_op = tf.variables_initializer([random_tensor])

with tf.control_dependencies([init_op]): x1_mask = x1 * keep_mask x2_mask = x2 * keep_mask

global_variables = tf.get_default_graph()._collections.get('variables', []) local_init_op = tf.variables_initializer(global_variables)

sess = tf.Session()

sess.run(local_init_op) print("keep mask") print(sess.run(keep_mask)) print("x1") print(sess.run(x1)) print("x1 mask") print(sess.run(x1_mask)) print("x2") print(sess.run(x2)) print("x2 mask") print(sess.run(x2_mask))

sess.close()`

output: keep mask [[[1. 1.] [1. 1.]]

[[0. 0.] [0. 1.]]] x1 [[[1. 2.] [3. 4.]]

[[5. 6.] [7. 8.]]] x1 mask [[[1. 2.] [3. 4.]]

[[0. 0.] [0. 8.]]] x2 [[[10. 20.] [30. 40.]]

[[50. 60.] [70. 80.]]] x2 mask [[[10. 0.] [30. 40.]]

yupeijei1997 avatar Mar 28 '21 06:03 yupeijei1997

是这样的,按照上面的代码: print(sess.run(keep_mask)) 调用的时候,random_tensor随机初始化了一次 print(sess.run(x1_mask))调用的时候,random_tensor又初始化了一次 随后调用print(sess.run(x2_mask))的时候,再一次初始化 细心观察一下,你会发现再跑一次,你用上面的代码跑的,keep_mask和x1_mask也不一样

因此正确的写法是: a, b, c = sess.run([keey_mask, x1_mask, x2_mask]) print(a) print(b) print(c)

试一试~

geyingli avatar Mar 28 '21 08:03 geyingli

谢谢您的指点,目前已经跑通了dropout mask的这种方式~

通过您的讲解,我了解到我出错的原因应该是因为每次sess.run(op)的时候,都会重新对计算图执行一遍,所以每次都会执行一次init_op,因此random_tensor初始化了多次,加深了理解,感恩~

另我又多跑了几遍,结果keep_mask和x1_mask,在mask位置上的结果是一致的 比如我的keep_mask是上面的 [[[1. 1.] [1. 1.]]

[[0. 0.] [0. 1.]]] 其x1_mask,在对应位置上是按keep_mask进行mask的(我想表达的是,比如mask矩阵(2 * 2 * 2)的第一个2*2矩阵中,四个元素均为1,而x1_mask,在第一个矩阵上的结果也都和原先的值相同,说明其对应元素是和mask中的四个1对应相乘了,即有按keep_mask进行mask) [[[1. 2.] [3. 4.]]

[[0. 0.] [0. 8.]]]

对此我感到疑惑,当我sess.run(keep_mask)时,它的值依赖于random_tensor,而random_tensor的值又依赖于sess.run(local_init_op),第一次初始化全部变量值时所赋予的; 当我sess.run(x1_mask)时,random_tensor的值理应会因为init_op的初始化而发生变化,从而使得keep_mask发生变化,但结果却没有,这是为什么呢,还请您指点一下~

如下代码表达了我的想法: `import tensorflow as tf

x1 = tf.constant([[[1., 2., 2.], [3., 4., 2.]], [[5., 6., 2.], [7., 8., 2.]]], dtype=tf.float32) random_tensor = tf.get_variable('random_tensor', shape=[2, 2, 3], initializer=tf.random_uniform_initializer(minval=0, maxval=1)) keep_mask = tf.cast(tf.greater(random_tensor, 0.5), tf.float32)

init_op = tf.variables_initializer([random_tensor])

with tf.control_dependencies([init_op]): x1_mask = x1 * keep_mask

global_variables = tf.get_default_graph()._collections.get('variables', []) local_init_op = tf.variables_initializer(global_variables)

sess = tf.Session()

sess.run(local_init_op) print("keep mask") print(sess.run(keep_mask))

keep_mas, X1_mask = sess.run([keep_mask, x1_mask]) print("x1 mask's keep mask") print(keep_mas)

sess.close()`

output:可见两个keep mask相同 keep mask [[[0. 1. 1.] [0. 1. 0.]]

[[1. 1. 1.] [1. 0. 0.]]] x1 mask's keep mask [[[0. 1. 1.] [0. 1. 0.]]

[[1. 1. 1.] [1. 0. 0.]]]

yupeijei1997 avatar Mar 28 '21 10:03 yupeijei1997

好的,太客气了~

上面的代码我跑了几次,两个keep mask都是不一样的,可否再确认一下呢

geyingli avatar Mar 28 '21 11:03 geyingli

确认了~ 我分别使用1.12(windows)和1.13(Linux)版本进行了测试,结果是一样的 难道是版本问题吗

yupeijei1997 avatar Mar 28 '21 11:03 yupeijei1997

时而相同,时而不同。我猜这个取决于tensorflow递归调用节点,keep_mask和x1_mask哪个更先触及random_tensor,后者先触及,就会先触发random_tensor的重新初始化,导致最终两者的mask相同;前者先触及,那么最终mask不同。我想我们可以先不纠结这个点,按规范写代码就行,正确写dropout mask,不会面临这个随机性的问题。你说呢

geyingli avatar Mar 28 '21 12:03 geyingli

那看来是我测试的次数还是太少了~ 您分析的很有道理,不纠结啦~

yupeijei1997 avatar Mar 28 '21 12:03 yupeijei1997

哈哈,恩。其实就是把keep_mask,和x1_mask一样,给同样写到tf.control_depencies下面,这样两个变量的mask就完全一致了

最近忙别的项目,没有再更新UNIF了,感谢你的支持,祝你顺利~

geyingli avatar Mar 28 '21 13:03 geyingli

是滴,哈哈,多向您学习 祝您也顺利~

yupeijei1997 avatar Mar 28 '21 14:03 yupeijei1997

还想向您请教一下,我自己实现dropout以后,虽然代码可以跑,但是loss下降却有问题,使用tf.nn.dropout()在200个step时,我的loss可以下降到9左右,而用自己实现的dropout却为30左右。

以下是我代码中关于dropout的这部分,关键代码用(##关键代码)表示。其中version 1. 版本为使用tf.nn.dropout()进行dropout,此方式测试过loss是正常的,我尝试用此方式测试其余代码的正确性。

使用version 2. 进行dropout时,就出现了loss问题, 我进行了如下步骤(分类任务):

  1. 定义了变量random_tensor
  2. 对其进行初始化,得到init_dp_op
  3. 依赖init_dp_op,model.build()进行前向传播,得到文章最后的向量表示actual_h_pool_flat
  4. 把dropout_keep转换为tensor
  5. 计算scale,用于dropout后的非零元素的放大
  6. 计算keep_mask
  7. 将scale、keep_mask与文章表示向量actual_h_pool_flat相乘,得到dropout后的文章表示向量actual_h_dropout
  8. 过最后一层全连接层,得到用于分类的scores向量
  9. 计算loss
  10. 计算梯度,得到扰动
  11. 生成对抗样本
  12. 对抗样本进行前向传播,model.build()后得到对抗样本最后的向量表示attack_h_pool_flat
  13. 对attack_h_pool_flat进行dropout,使用前面得到的keep_mask与scale,对应元素相乘后得到最后的对抗样本表示attack_h_dropout
  14. 计算对抗样本的梯度
  15. 正常样本 + 对抗样本的梯度进行平均后更新参数

似乎没有问题,但是却出现了loss不正常下降的问题,百思不得其解,您有空的时候可以帮我看下吗~ (使用tensorflow1.12 Linux)

    dropout_keep = tf.placeholder(tf.float32)
    model = TextCNN(DATA_HELPER_OBJ.embedding_dim,
                                  vocab_size,
                                  ARGS.sentence_len,
                                  label_list,
                                  pretrained_embedding,
                                 ARGS.filter_num,
                                 ARGS.max_save_model,
                                 SUMMARIES_DIR,
                                 init_lr=ARGS.init_lr,
                                 lr_decay_step=ARGS.lr_decay_step,
                                 lr_decay_rate=ARGS.lr_decay_rate)

    with tf.variable_scope(tf.get_variable_scope(), reuse=tf.AUTO_REUSE):
        learning_rate = tf.get_variable(initializer=tf.constant(1e-3), trainable=False, name='lr')
        global_step = tf.get_variable(initializer=tf.constant(0), dtype=tf.int32, name='global_step', trainable=False)
        lr = tf.train.exponential_decay(learning_rate, global_step, ARGS.lr_decay_step, ARGS.lr_decay_rate, staircase=True)
        optimizer = tf.train.AdamOptimizer(lr)

        # [batch_size, hidden_size] 最后的文章向量表示
        actual_h_pool_flat = model.build(x_batch, y_batch)

        # version 1. 使用tf.nn.dropout(), 对actual_h_pool_flat进行dropout
        # actual_h_dropout = tf.nn.dropout(actual_h_pool_flat, dropout_keep)  ##关键代码

        # version 2. 自己实现dropout, 对actual_h_pool_flat进行dropout
        random_tensor = tf.get_variable('random_tensor', shape=actual_h_pool_flat.shape[1:], initializer=tf.random_uniform_initializer(minval=0, maxval=1), trainable=False)  ##关键代码
        init_dp_op = tf.variables_initializer([random_tensor])  ##关键代码
        with tf.control_dependencies([init_dp_op]):  ##关键代码
            dropout_keep = tf.convert_to_tensor(dropout_keep, dtype=actual_h_pool_flat.dtype)  ##关键代码
            scale = 1 / dropout_keep  ##关键代码
            keep_mask = tf.cast(tf.greater(random_tensor, 1 - dropout_keep), tf.float32)  ##关键代码
            # dropout
            actual_h_dropout = actual_h_pool_flat * scale * keep_mask  ##关键代码

            # 计算scores: actual_h_dropout * weight = scores [batch_size, label_num]
            model.add_prediction_op(actual_h_dropout)  ##关键代码
            # 计算loss: losses = tf.nn.softmax_cross_entropy_with_logits_v2(logits=scores, labels=input_y)
            model.add_loss_op()  ##关键代码

            # attack
            actual_loss, actual_accuracy, actual_predictions, actual_input_y = model.loss, model.accuracy, model.predictions, model.input_y
            trainable_variables = tf.get_default_graph()._collections.get('trainable_variables', [])
            actual_grads = tf.gradients(actual_loss, trainable_variables)
            grad, param = get_grad_and_param(trainable_variables, actual_grads, 'embeddings_table:0')
            r = tf.multiply(ARGS.epsilon, grad / (tf.norm(grad) + 1e-9))
            attack_op = param.assign(param + r)

            # restore
            with tf.control_dependencies([attack_op]):
                attack_h_pool_flat = model.build(x_batch, y_batch)

                # version 1. 使用tf.nn.dropout(), 对attack_h_dropout进行dropout
                # attack_h_dropout = tf.nn.dropout(attack_h_pool_flat, dropout_keep)  ##关键代码

                # version 2. 自己实现dropout, 对actual_h_pool_flat进行dropout
                attack_h_dropout = attack_h_pool_flat * scale * keep_mask  ##关键代码
                model.add_prediction_op(attack_h_dropout)  ##关键代码
                model.add_loss_op()  ##关键代码
                attack_loss = model.loss
                attack_grads = tf.gradients(attack_loss, trainable_variables)
                restore_op = param.assign(param - r)

            # sum up
            with tf.control_dependencies([restore_op]):
                grads = [average_n_grads([actual_grad, attack_grad])
                        for (actual_grad, attack_grad) in zip(actual_grads, attack_grads)]

            update_params_op = update_global_params(trainable_variables, global_step, optimizer, grads)
            # update_step_op = global_step.assign(global_step + 1)
            train_op = tf.group([update_params_op, global_step])

model部分

   def add_prediction_op(self, h_dropout):
       with tf.name_scope('output'):
           weight = tf.get_variable('W_projection',
                                    shape=[self.num_filters_total, self.classes_num],
                                    initializer=self.norm_initializer, trainable=True)
           bias = tf.get_variable(initializer=tf.constant_initializer(0.1), shape=[self.classes_num], name='b', trainable=True)
           self.l2_loss = tf.add_n([tf.nn.l2_loss(weight), tf.nn.l2_loss(bias)])
           self.scores = tf.nn.xw_plus_b(h_dropout, weight, bias, name='scores')  ##关键代码
           self.predictions = tf.nn.softmax(self.scores)
           self.labels_all = tf.tile(self.labels, [tf.shape(self.predictions)[0], 1])

   def add_loss_op(self):
       with tf.name_scope('loss'):
           losses = tf.nn.softmax_cross_entropy_with_logits_v2(
               logits=self.scores, labels=self.input_y)
           self.loss = tf.reduce_mean(losses) + 0.001 * self.l2_loss
           tf.summary.scalar('loss', self.loss)

       with tf.name_scope('accuracy'):
           self.truth = tf.argmax(self.input_y, 1)
           self.pred = tf.argmax(self.predictions, 1)
           correct_preds = tf.cast(tf.equal(self.pred, self.truth), 'float')
           self.accuracy = tf.reduce_mean(correct_preds, name='accuracy')
           tf.summary.scalar('accuracy', self.accuracy)

yupeijei1997 avatar Mar 29 '21 11:03 yupeijei1997

单从代码看,有一个地方不太可取:random_tensor = tf.get_variable('random_tensor', shape=actual_h_pool_flat.shape[1:]...这里,random_tensor的形状不应该忽视第一维的batch_size,因为tf.nn.dropout是所有维度都包括在内。建议可以把这里修改了以后再尝试一下效果。

如果成绩还是不好,可以试试把with tf.control_dependencies([init_dp_op]):注释掉,让random_tensor每次跌价扰动时都重新初始化,对比tf.nn.dropout,排查一下问题根源。

geyingli avatar Mar 30 '21 07:03 geyingli

以下结果均注释了with tf.control_dependencies([init_dp_op])(加上后经测试,结论也和以下实验一样)

如下为使用tf.nn.dropout()的正常结果(每1000个batch进行一次验证,如下为第一次验证): [INFO] [20210327 20:22:56]: epoch:0, gstep:995, b-loss:6.2543, b-acc:0.1484, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:996, b-loss:6.1792, b-acc:0.2188, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:997, b-loss:6.0566, b-acc:0.2266, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:998, b-loss:5.9870, b-acc:0.2188, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:999, b-loss:6.0397, b-acc:0.1719, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:1000, b-loss:6.2203, b-acc:0.1797, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:23:23]: Evaluate on "test" data, loss: 5.6079, acc: 0.2745 [INFO] [20210327 20:23:39]: Evaluate on "dev" data, loss: 5.8003, acc: 0.2473 ...

我尝试将random_tensor改为如下代码,使用actual_h_pool_flat的形状作为random_tensor的形状 random_tensor = tf.get_variable('random_tensor', shape=actual_h_pool_flat.shape... random_tensor = tf.get_variable('random_tensor', shape=actual_h_pool_flat.get_shape().as_list()...,两者都会报错 错误信息:ValueError: You can only pass an initializer function that expects no arguments to its callable when the shape is not fully defined. The given initializer function expects the following args ['self', 'shape', 'dtype', 'partition_info']

我感觉是无法获取batch_size的维度导致的错误,因为feed数据之前,这一维是未知的,然后又尝试改为,使用tf.shape()来获取形状 random_tensor = tf.get_variable('random_tensor', shape=tf.shape(actual_h_pool_flat)...,但仍会报错,错误信息有点看不懂 错误信息:TypeError: Tensor objects are only iterable when eager execution is enabled. To iterate over this tensor use tf.map_fn.

最后我把shape的形状写死,768即最后的文章表示向量actual_h_pool_flat的维度 random_tensor = tf.get_variable('random_tensor', shape=[batch_size, 768]...

loss相比之前不取batch_size,下降许多,但仍比不上正常结果 [INFO] [20210330 19:55:17]: epoch:0, gstep:995, b-loss:6.5370, b-acc:0.1641, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:996, b-loss:6.8437, b-acc:0.1719, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:997, b-loss:6.4046, b-acc:0.2188, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:998, b-loss:6.6196, b-acc:0.1406, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:999, b-loss:6.5291, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:1000, b-loss:6.7619, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:44]: Evaluate on "test" data, loss: 6.0914, acc: 0.2483 [INFO] [20210330 19:55:59]: Evaluate on "dev" data, loss: 6.2881, acc: 0.2230

因为我使用的版本是tensorflow1.12,因此我又修改了dropout的代码和1.12版本中的实现对齐,将dropout部分的代码改为

        actual_h_pool_flat = model.build(x_batch, y_batch)
        # actual_h_dropout = tf.nn.dropout(actual_h_pool_flat, dropout_keep)
        random_tensor = tf.get_variable('random_tensor', shape=[ARGS.batch_size, 768], initializer=tf.random_uniform_initializer(minval=0, maxval=1), trainable=False)
        # init_dp_op = tf.variables_initializer([random_tensor])
        # with tf.control_dependencies([init_dp_op]):
        dropout_keep = tf.convert_to_tensor(dropout_keep, dtype=actual_h_pool_flat.dtype)

        # uniform [keep_prob, 1.0 + keep_prob)
        random_tensor += dropout_keep  ## 修改部分代码
        # 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob)
        binary_tensor = tf.floor(random_tensor)  ## 修改部分代码
        actual_h_dropout = tf.div(actual_h_pool_flat, dropout_keep) * binary_tensor  ## 修改部分代码

        model.add_prediction_op(actual_h_dropout)
        model.add_loss_op()

        # attack
        actual_loss, actual_accuracy, actual_predictions, actual_input_y = model.loss, model.accuracy, model.predictions, model.input_y
        trainable_variables = tf.get_default_graph()._collections.get('trainable_variables', [])
        actual_grads = tf.gradients(actual_loss, trainable_variables)
        grad, param = get_grad_and_param(trainable_variables, actual_grads, 'embeddings_table:0')
        r = tf.multiply(ARGS.epsilon, grad / (tf.norm(grad) + 1e-9))
        attack_op = param.assign(param + r)

        # restore
        with tf.control_dependencies([attack_op]):
            attack_h_pool_flat = model.build(x_batch, y_batch)
            # attack_h_dropout = tf.nn.dropout(attack_h_pool_flat, dropout_keep)
            # 此行加不加,不影响结果,加的原因是dropout实现中对传入的x都做了转为tensor的操作
            # 不影响的原因,我感觉是因为attack_h_pool_flat 本身就是一个tensor了,属于重复操作,所以去掉了
            # attack_h_pool_flat = tf.convert_to_tensor(attack_h_pool_flat, name="attack_h_pool_flat") 
            attack_h_dropout = tf.div(attack_h_pool_flat, dropout_keep) * binary_tensor  ## 修改部分代码

            model.add_prediction_op(attack_h_dropout)
            model.add_loss_op()
            attack_loss = model.loss
            attack_grads = tf.gradients(attack_loss, trainable_variables)
            restore_op = param.assign(param - r)

acc相比上一版本差不太多,以上实验均跑了5次,使用自己写的dropout的第一次在测试集上的验证,基本都在24%左右 且之后几次的验证相比使用tf.nn.dropout()在相同的step时,也有所降低 [INFO] [20210330 19:41:30]: epoch:0, gstep:995, b-loss:6.6033, b-acc:0.1484, lr:0.001000, step_time:0.13s. [INFO] [20210330 19:41:30]: epoch:0, gstep:996, b-loss:6.6954, b-acc:0.1641, lr:0.001000, step_time:0.12s. [INFO] [20210330 19:41:30]: epoch:0, gstep:997, b-loss:6.5559, b-acc:0.1797, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:30]: epoch:0, gstep:998, b-loss:6.3113, b-acc:0.1797, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:30]: epoch:0, gstep:999, b-loss:6.3899, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:30]: epoch:0, gstep:1000, b-loss:6.7224, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:57]: Evaluate on "test" data, loss: 6.1011, acc: 0.2438 [INFO] [20210330 19:42:13]: Evaluate on "dev" data, loss: 6.2961, acc: 0.2202

如下为1.12版本dropout的实现:https://github.com/tensorflow/tensorflow/blob/v1.12.0/tensorflow/python/ops/nn_ops.py#L2321 在其中我省略了实现中的noise_shape = _get_noise_shape(x, noise_shape),有点没看懂这行代码的作用,似乎在获取x的形状,之后再作为random_tensor的形状,但具体返回的结果是似乎又不是x的形状,感觉在写死random_tensor的形状之后,也用不上它,就没管这行

不知该从哪再进行排查了,还请您有空时指点一下~

yupeijei1997 avatar Mar 30 '21 12:03 yupeijei1997

看到Github邮件过来回一下。过两天要放假,这几天太忙,不一定什么时候有时间可以帮你看,尽量先尝试着自己解决哈~

geyingli avatar Mar 31 '21 07:03 geyingli

好滴~您先忙,我肯定先自己看着啦

yupeijei1997 avatar Mar 31 '21 15:03 yupeijei1997

您好~ 我在进行一项有这1000多个label的文本分类任务,在这个特定任务上,对代码做了点修改,解决了一些可能存在的问题~想和您分享一下,哈哈

  1. 我在使用smart时,在如下代码中,由于我的类别数特别多,可能会导致tf.log()内的值可能非常接近于0或者等于0,从而使得在求log以后,该值为负无穷,导致了loss为无穷值,从而在后面计算梯度的时候,梯度值为NAN,出现梯度爆炸的情况,loss也会一直为NAN;
        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg) - tf.log(probs)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs) - tf.log(probs_breg)), axis=-1)

以及

                per_example_loss = tf.reduce_sum(
                    probs_prtb * (tf.log(probs_prtb) - tf.log(probs)), axis=-1)
                per_example_loss_prtb = tf.reduce_sum(
                    probs * (tf.log(probs) - tf.log(probs_prtb)), axis=-1)

因此我改为了如下代码,为probs_breg、probs两个概率分布,在求log时,加上了一个极小数,解决了梯度爆炸的问题;不知道有没有别的解决办法了,因为这样会让概率分布的总和大于1,感觉会引入噪声;

        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg + 1e-9) - tf.log(probs + 1e-9)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs + 1e-9) - tf.log(probs_breg + 1e-9)), axis=-1)

我猜这在label个数较少时,或者别的任务上不会出现该问题;

  1. 另一个疑惑的地方是,我在smart的实现中没找到restore_op,即恢复embedding矩阵的操作,这样不会使loss朝着上升的方向在走吗

  2. 之前的这行FreeLB中的代码,如果不加的话,在我的1000多个label的文本分类任务上会导致loss不收敛的问题; cur_r = tf.cond(norm > ARGS.epsilon, lambda: (acc_r + tmp_r) * tf.divide(ARGS.epsilon, norm), lambda: (acc_r + tmp_r))

yupeijei1997 avatar Apr 02 '21 14:04 yupeijei1997

以下结果均注释了with tf.control_dependencies([init_dp_op])(加上后经测试,结论也和以下实验一样)

如下为使用tf.nn.dropout()的正常结果(每1000个batch进行一次验证,如下为第一次验证): [INFO] [20210327 20:22:56]: epoch:0, gstep:995, b-loss:6.2543, b-acc:0.1484, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:996, b-loss:6.1792, b-acc:0.2188, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:997, b-loss:6.0566, b-acc:0.2266, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:998, b-loss:5.9870, b-acc:0.2188, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:999, b-loss:6.0397, b-acc:0.1719, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:22:56]: epoch:0, gstep:1000, b-loss:6.2203, b-acc:0.1797, lr:0.001000, step_time:0.10s. [INFO] [20210327 20:23:23]: Evaluate on "test" data, loss: 5.6079, acc: 0.2745 [INFO] [20210327 20:23:39]: Evaluate on "dev" data, loss: 5.8003, acc: 0.2473 ...

我尝试将random_tensor改为如下代码,使用actual_h_pool_flat的形状作为random_tensor的形状 random_tensor = tf.get_variable('random_tensor', shape=actual_h_pool_flat.shape... random_tensor = tf.get_variable('random_tensor', shape=actual_h_pool_flat.get_shape().as_list()...,两者都会报错 错误信息:ValueError: You can only pass an initializer function that expects no arguments to its callable when the shape is not fully defined. The given initializer function expects the following args ['self', 'shape', 'dtype', 'partition_info']

我感觉是无法获取batch_size的维度导致的错误,因为feed数据之前,这一维是未知的,然后又尝试改为,使用tf.shape()来获取形状 random_tensor = tf.get_variable('random_tensor', shape=tf.shape(actual_h_pool_flat)...,但仍会报错,错误信息有点看不懂 错误信息:TypeError: Tensor objects are only iterable when eager execution is enabled. To iterate over this tensor use tf.map_fn.

最后我把shape的形状写死,768即最后的文章表示向量actual_h_pool_flat的维度 random_tensor = tf.get_variable('random_tensor', shape=[batch_size, 768]...

loss相比之前不取batch_size,下降许多,但仍比不上正常结果 [INFO] [20210330 19:55:17]: epoch:0, gstep:995, b-loss:6.5370, b-acc:0.1641, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:996, b-loss:6.8437, b-acc:0.1719, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:997, b-loss:6.4046, b-acc:0.2188, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:998, b-loss:6.6196, b-acc:0.1406, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:999, b-loss:6.5291, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:17]: epoch:0, gstep:1000, b-loss:6.7619, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:55:44]: Evaluate on "test" data, loss: 6.0914, acc: 0.2483 [INFO] [20210330 19:55:59]: Evaluate on "dev" data, loss: 6.2881, acc: 0.2230

因为我使用的版本是tensorflow1.12,因此我又修改了dropout的代码和1.12版本中的实现对齐,将dropout部分的代码改为

        actual_h_pool_flat = model.build(x_batch, y_batch)
        # actual_h_dropout = tf.nn.dropout(actual_h_pool_flat, dropout_keep)
        random_tensor = tf.get_variable('random_tensor', shape=[ARGS.batch_size, 768], initializer=tf.random_uniform_initializer(minval=0, maxval=1), trainable=False)
        # init_dp_op = tf.variables_initializer([random_tensor])
        # with tf.control_dependencies([init_dp_op]):
        dropout_keep = tf.convert_to_tensor(dropout_keep, dtype=actual_h_pool_flat.dtype)

        # uniform [keep_prob, 1.0 + keep_prob)
        random_tensor += dropout_keep  ## 修改部分代码
        # 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob)
        binary_tensor = tf.floor(random_tensor)  ## 修改部分代码
        actual_h_dropout = tf.div(actual_h_pool_flat, dropout_keep) * binary_tensor  ## 修改部分代码

        model.add_prediction_op(actual_h_dropout)
        model.add_loss_op()

        # attack
        actual_loss, actual_accuracy, actual_predictions, actual_input_y = model.loss, model.accuracy, model.predictions, model.input_y
        trainable_variables = tf.get_default_graph()._collections.get('trainable_variables', [])
        actual_grads = tf.gradients(actual_loss, trainable_variables)
        grad, param = get_grad_and_param(trainable_variables, actual_grads, 'embeddings_table:0')
        r = tf.multiply(ARGS.epsilon, grad / (tf.norm(grad) + 1e-9))
        attack_op = param.assign(param + r)

        # restore
        with tf.control_dependencies([attack_op]):
            attack_h_pool_flat = model.build(x_batch, y_batch)
            # attack_h_dropout = tf.nn.dropout(attack_h_pool_flat, dropout_keep)
            # 此行加不加,不影响结果,加的原因是dropout实现中对传入的x都做了转为tensor的操作
            # 不影响的原因,我感觉是因为attack_h_pool_flat 本身就是一个tensor了,属于重复操作,所以去掉了
            # attack_h_pool_flat = tf.convert_to_tensor(attack_h_pool_flat, name="attack_h_pool_flat") 
            attack_h_dropout = tf.div(attack_h_pool_flat, dropout_keep) * binary_tensor  ## 修改部分代码

            model.add_prediction_op(attack_h_dropout)
            model.add_loss_op()
            attack_loss = model.loss
            attack_grads = tf.gradients(attack_loss, trainable_variables)
            restore_op = param.assign(param - r)

acc相比上一版本差不太多,以上实验均跑了5次,使用自己写的dropout的第一次在测试集上的验证,基本都在24%左右 且之后几次的验证相比使用tf.nn.dropout()在相同的step时,也有所降低 [INFO] [20210330 19:41:30]: epoch:0, gstep:995, b-loss:6.6033, b-acc:0.1484, lr:0.001000, step_time:0.13s. [INFO] [20210330 19:41:30]: epoch:0, gstep:996, b-loss:6.6954, b-acc:0.1641, lr:0.001000, step_time:0.12s. [INFO] [20210330 19:41:30]: epoch:0, gstep:997, b-loss:6.5559, b-acc:0.1797, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:30]: epoch:0, gstep:998, b-loss:6.3113, b-acc:0.1797, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:30]: epoch:0, gstep:999, b-loss:6.3899, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:30]: epoch:0, gstep:1000, b-loss:6.7224, b-acc:0.1875, lr:0.001000, step_time:0.10s. [INFO] [20210330 19:41:57]: Evaluate on "test" data, loss: 6.1011, acc: 0.2438 [INFO] [20210330 19:42:13]: Evaluate on "dev" data, loss: 6.2961, acc: 0.2202

如下为1.12版本dropout的实现:https://github.com/tensorflow/tensorflow/blob/v1.12.0/tensorflow/python/ops/nn_ops.py#L2321 在其中我省略了实现中的noise_shape = _get_noise_shape(x, noise_shape),有点没看懂这行代码的作用,似乎在获取x的形状,之后再作为random_tensor的形状,但具体返回的结果是似乎又不是x的形状,感觉在写死random_tensor的形状之后,也用不上它,就没管这行

不知该从哪再进行排查了,还请您有空时指点一下~

noise_shape那一行代码的确是没用的,看代码知道,它是在你指定了noise形状的时候才发挥作用,否则拿到的就是x的形状。验证集没有tf原生dropout成绩好,你看看,有没有可能是因为推理的时候,没有把dropout_rate设为0,如果是的话,原因可能就在这里。如果不是的话,可能是tensorflow底层对dropout有不一样的实现,比如它可能不是随机10%置为0,是置为负无穷大,等等

geyingli avatar Apr 06 '21 07:04 geyingli

在验证的时候是有把dropout_rate设为0的,那看来就是底层可能有不一样的实现,谢谢您的解答啦~

yupeijei1997 avatar Apr 06 '21 07:04 yupeijei1997

您好~ 我在进行一项有这1000多个label的文本分类任务,在这个特定任务上,对代码做了点修改,解决了一些可能存在的问题~想和您分享一下,哈哈

  1. 我在使用smart时,在如下代码中,由于我的类别数特别多,可能会导致tf.log()内的值可能非常接近于0或者等于0,从而使得在求log以后,该值为负无穷,导致了loss为无穷值,从而在后面计算梯度的时候,梯度值为NAN,出现梯度爆炸的情况,loss也会一直为NAN;
        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg) - tf.log(probs)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs) - tf.log(probs_breg)), axis=-1)

以及

                per_example_loss = tf.reduce_sum(
                    probs_prtb * (tf.log(probs_prtb) - tf.log(probs)), axis=-1)
                per_example_loss_prtb = tf.reduce_sum(
                    probs * (tf.log(probs) - tf.log(probs_prtb)), axis=-1)

因此我改为了如下代码,为probs_breg、probs两个概率分布,在求log时,加上了一个极小数,解决了梯度爆炸的问题;不知道有没有别的解决办法了,因为这样会让概率分布的总和大于1,感觉会引入噪声;

        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg + 1e-9) - tf.log(probs + 1e-9)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs + 1e-9) - tf.log(probs_breg + 1e-9)), axis=-1)

我猜这在label个数较少时,或者别的任务上不会出现该问题;

  1. 另一个疑惑的地方是,我在smart的实现中没找到restore_op,即恢复embedding矩阵的操作,这样不会使loss朝着上升的方向在走吗
  2. 之前的这行FreeLB中的代码,如果不加的话,在我的1000多个label的文本分类任务上会导致loss不收敛的问题; cur_r = tf.cond(norm > ARGS.epsilon, lambda: (acc_r + tmp_r) * tf.divide(ARGS.epsilon, norm), lambda: (acc_r + tmp_r))

回复:

  1. 这个做法是正确的,写SMART的时候的确没有考虑到太多分类维度的问题。实际上,SMART和其他对抗式训练算法是对抗过拟合的,分类维度越多,过拟合越难。我在我自己的项目里面用的时候,只有简单容易过拟合的任务SMART才有提升,在其他的,比如语义匹配和MRC任务上,SMART是负增益的,徒增训练时长。所以这一类算法也不是万金油,处处见效,见实际场景使用,以实际效果为准。
  2. 这个问题我没太关注,我是照着论文的伪代码写的,实现以后的确比其他一众对抗式算法FreeLB等要好,也就没有关注这个点。正所谓实践科学,效果好就行~当然你可以试着改一下代码,看看加了restore以后能不能再提升一些
  3. 既然有效,可以commit到我们的代码里,提交pull request,成为contributors之一~

geyingli avatar Apr 06 '21 07:04 geyingli

在验证的时候是有把dropout_rate设为0的,那看来就是底层可能有不一样的实现,谢谢您的解答啦~

tensorflow源码我没怎么读,过去以研究上层网络为主,不能解决你的问题很抱歉哈

geyingli avatar Apr 06 '21 07:04 geyingli

在验证的时候是有把dropout_rate设为0的,那看来就是底层可能有不一样的实现,谢谢您的解答啦~

tensorflow源码我没怎么读,过去以研究上层网络为主,不能解决你的问题很抱歉哈

哈哈没事没事,能够大致知道问题出在哪也就很有收获啦

yupeijei1997 avatar Apr 06 '21 07:04 yupeijei1997

在验证的时候是有把dropout_rate设为0的,那看来就是底层可能有不一样的实现,谢谢您的解答啦~

tensorflow源码我没怎么读,过去以研究上层网络为主,不能解决你的问题很抱歉哈

哈哈没事没事,能够大致知道问题出在哪也就很有收获啦

👍🏻👍🏻

geyingli avatar Apr 06 '21 08:04 geyingli

您好~ 我在进行一项有这1000多个label的文本分类任务,在这个特定任务上,对代码做了点修改,解决了一些可能存在的问题~想和您分享一下,哈哈

  1. 我在使用smart时,在如下代码中,由于我的类别数特别多,可能会导致tf.log()内的值可能非常接近于0或者等于0,从而使得在求log以后,该值为负无穷,导致了loss为无穷值,从而在后面计算梯度的时候,梯度值为NAN,出现梯度爆炸的情况,loss也会一直为NAN;
        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg) - tf.log(probs)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs) - tf.log(probs_breg)), axis=-1)

以及

                per_example_loss = tf.reduce_sum(
                    probs_prtb * (tf.log(probs_prtb) - tf.log(probs)), axis=-1)
                per_example_loss_prtb = tf.reduce_sum(
                    probs * (tf.log(probs) - tf.log(probs_prtb)), axis=-1)

因此我改为了如下代码,为probs_breg、probs两个概率分布,在求log时,加上了一个极小数,解决了梯度爆炸的问题;不知道有没有别的解决办法了,因为这样会让概率分布的总和大于1,感觉会引入噪声;

        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg + 1e-9) - tf.log(probs + 1e-9)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs + 1e-9) - tf.log(probs_breg + 1e-9)), axis=-1)

我猜这在label个数较少时,或者别的任务上不会出现该问题;

  1. 另一个疑惑的地方是,我在smart的实现中没找到restore_op,即恢复embedding矩阵的操作,这样不会使loss朝着上升的方向在走吗
  2. 之前的这行FreeLB中的代码,如果不加的话,在我的1000多个label的文本分类任务上会导致loss不收敛的问题; cur_r = tf.cond(norm > ARGS.epsilon, lambda: (acc_r + tmp_r) * tf.divide(ARGS.epsilon, norm), lambda: (acc_r + tmp_r))

回复:

  1. 这个做法是正确的,写SMART的时候的确没有考虑到太多分类维度的问题。实际上,SMART和其他对抗式训练算法是对抗过拟合的,分类维度越多,过拟合越难。我在我自己的项目里面用的时候,只有简单容易过拟合的任务SMART才有提升,在其他的,比如语义匹配和MRC任务上,SMART是负增益的,徒增训练时长。所以这一类算法也不是万金油,处处见效,见实际场景使用,以实际效果为准。
  2. 这个问题我没太关注,我是照着论文的伪代码写的,实现以后的确比其他一众对抗式算法FreeLB等要好,也就没有关注这个点。正所谓实践科学,效果好就行~当然你可以试着改一下代码,看看加了restore以后能不能再提升一些
  3. 既然有效,可以commit到我们的代码里,提交pull request,成为contributors之一~
  1. 是的~在我的分类任务里,使用SMART方式时,将几个超参数都设置为0.001的情况下,效果才能与不加SMART时打平,其他超参组合,随着增大各种超参数,负增益也越来越明显;看原理也确实是解决使用BERT微调时的过拟合问题时使用,不太适合分类维度多且数据量大的情况。

  2. 我用的时候直接加上了restore_op,等我再去掉做个实验和您同步下结果~

  3. 哈哈非常乐意!

yupeijei1997 avatar Apr 06 '21 08:04 yupeijei1997

您好~ 我在进行一项有这1000多个label的文本分类任务,在这个特定任务上,对代码做了点修改,解决了一些可能存在的问题~想和您分享一下,哈哈

  1. 我在使用smart时,在如下代码中,由于我的类别数特别多,可能会导致tf.log()内的值可能非常接近于0或者等于0,从而使得在求log以后,该值为负无穷,导致了loss为无穷值,从而在后面计算梯度的时候,梯度值为NAN,出现梯度爆炸的情况,loss也会一直为NAN;
        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg) - tf.log(probs)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs) - tf.log(probs_breg)), axis=-1)

以及

                per_example_loss = tf.reduce_sum(
                    probs_prtb * (tf.log(probs_prtb) - tf.log(probs)), axis=-1)
                per_example_loss_prtb = tf.reduce_sum(
                    probs * (tf.log(probs) - tf.log(probs_prtb)), axis=-1)

因此我改为了如下代码,为probs_breg、probs两个概率分布,在求log时,加上了一个极小数,解决了梯度爆炸的问题;不知道有没有别的解决办法了,因为这样会让概率分布的总和大于1,感觉会引入噪声;

        per_example_loss = tf.reduce_sum(
            probs_breg * (tf.log(probs_breg + 1e-9) - tf.log(probs + 1e-9)), axis=-1)
        per_example_loss_breg = tf.reduce_sum(
            probs * (tf.log(probs + 1e-9) - tf.log(probs_breg + 1e-9)), axis=-1)

我猜这在label个数较少时,或者别的任务上不会出现该问题;

  1. 另一个疑惑的地方是,我在smart的实现中没找到restore_op,即恢复embedding矩阵的操作,这样不会使loss朝着上升的方向在走吗
  2. 之前的这行FreeLB中的代码,如果不加的话,在我的1000多个label的文本分类任务上会导致loss不收敛的问题; cur_r = tf.cond(norm > ARGS.epsilon, lambda: (acc_r + tmp_r) * tf.divide(ARGS.epsilon, norm), lambda: (acc_r + tmp_r))

回复:

  1. 这个做法是正确的,写SMART的时候的确没有考虑到太多分类维度的问题。实际上,SMART和其他对抗式训练算法是对抗过拟合的,分类维度越多,过拟合越难。我在我自己的项目里面用的时候,只有简单容易过拟合的任务SMART才有提升,在其他的,比如语义匹配和MRC任务上,SMART是负增益的,徒增训练时长。所以这一类算法也不是万金油,处处见效,见实际场景使用,以实际效果为准。
  2. 这个问题我没太关注,我是照着论文的伪代码写的,实现以后的确比其他一众对抗式算法FreeLB等要好,也就没有关注这个点。正所谓实践科学,效果好就行~当然你可以试着改一下代码,看看加了restore以后能不能再提升一些
  3. 既然有效,可以commit到我们的代码里,提交pull request,成为contributors之一~

同步~ 已测试SMART去掉restore_op后的结果了,去掉后会导致无法收敛。

yupeijei1997 avatar Apr 10 '21 15:04 yupeijei1997