在我的模型中,我从神经网络获得形状 (1000, 1234) 的输出 . 我想从中计算损失 . 但是我不能用tensorflow操作来计算损失,因为计算损失涉及大量的 for 循环和条件代码(即 if() 表达式),所以我想在纯python代码中使用 tf.py_func 来做,这更灵活 .

问题是, tf.py_func 的梯度是 None ,这意味着我必须自己定义渐变函数 . 有很多解决方案,例如@harpone的gist . 但这些例子太简单而无用 . 在我的情况下,我有一个形状 (1000, 1234) 的输入张量, py_func 很复杂 .

好处是,在定制 py_funcno variable is involved . 所以我想我可以按原样返回渐变:

def _my_py_func_grad(op, grad):
    return tf.ones_like(op.inputs[0]) * grad

但是当运行代码时,神经网络似乎没有学到任何东西:在几次迭代之后,最终的损失不会减少(保持)相同 .

把它改成

def _my_py_func_grad(op, grad):
    return op.inputs[0] * grad

也没有帮助 .

我的问题在这里是什么?

如果我不能按原样返回渐变,有没有什么好的方法来计算渐变?


如果有人想看到代码:

#
    # Below is a complex function to calculate loss
    #
    def py_cal_loss(ts):
        """ ts of shape
            [-1, num_of_anchor_boxes*(5+num_of_classes) + 5*num_of_gt_bnx_per_cell]
        """
        def py_cal_loss_onecell(cell):
            """ cell of shape
                [num_of_anchor_boxes*(5+num_of_classes) + 5*num_of_gt_bnx_per_cell]
            """
            split_num = num_of_anchor_boxes*(5+num_of_classes)
            op_boxes = np.reshape(
                           cell[0:split_num],
                           [num_of_anchor_boxes, 5+num_of_classes]
                       )
            gt_boxes = np.reshape(
                           cell[split_num:],
                           [num_of_gt_bnx_per_cell, 5]
                       )
            max_oboxes = set()
            gt_op_matched_pairs = []
            for g_idx, gbox in enumerate(gt_boxes):
                if gbox == [0.0, 0.0, 0.0, 0.0, 0.0]:
                    print("all zero found")
                    continue
                o_idx = max_iou_with_op_boxes(gbox, op_boxes)
                max_oboxes.add(o_idx)
                gt_op_matched_pairs.append( (g_idx, o_idx) )
            # calculate coordinate loss & objectness confidence loss
            coor_loss = 0.0
            objectness_loss = 0.0
            for tp in gt_op_matched_pairs:
                g_idx = tp[0]
                o_idx = tp[1]
                gbox_coord = gt_boxes[g_idx][1:]
                obox_coord = op_boxes[o_idx][0:4]
                coor_loss += (math.pow(gbox_coord[0] - obox_coord[0], 2) +
                            math.pow(gbox_coord[1] - obox_coord[1], 2) +
                            math.pow(math.sqrt(gbox_coord[2]) - math.sqrt(obox_coord[2]), 2) +
                            math.pow(math.sqrt(gbox_coord[3]) - math.sqrt(obox_coord[3]), 2))
                obox_obj = op_boxes[o_idx][4]
                iou = cal_iou(gbox_coord, obox_coord)
                objectness_loss += math.pow(obox_obj-iou, 2)

            # calculate noobjectness confidence loss
            noobjectness_loss = 0.0
            for o_idx, o_box in enumerate(op_boxes):
                if o_idx in max_oboxes:
                    continue
                obox_obj = op_boxes[o_idx][4]
                noobjectness_loss += math.pow(obox_obj, 2)

            # calculate classness loss (TODO)

            return 0.5 * coor_loss + 3 * objectness_loss + noobjectness_loss
        # ----- END DEF py_cal_loss_onecell ----

        total_loss = 0.0
        for cell in ts:
            total_loss += py_cal_loss_onecell(cell)
        return total_loss
    # ---- END DEF py_cal_loss ----

    # return the grad as it is
    def _my_py_func_grad(op, grad):
        return tf.ones_like(op.inputs[0]) * grad

    # calculate loss using tf.py_func.
    #
    # The input, "op_and_gt_batch" is of shape (1000, 1234)
    #
    with ops.op_scope([op_and_gt_batch], "pyfunction", "MyLoss"):
        rnd_name = 'PyFuncGrad' + str(np.random.randint(0, 1E+8))
        tf.RegisterGradient(rnd_name)(_my_py_func_grad)
        default_graph = tf.get_default_graph()
        with default_graph.gradient_override_map({"PyFunc": rnd_name}):
            loss_out = tf.py_func(py_cal_loss, [op_and_gt_batch], [tf.float32], name="pyfunction")
    # pdb.set_trace()
    return loss_out[0]