Tensorflow 通过嵌套tf.map_fn反向传播梯度

Tensorflow 通过嵌套tf.map_fn反向传播梯度,tensorflow,nested,gradient,backpropagation,map-function,Tensorflow,Nested,Gradient,Backpropagation,Map Function,我想在每个向量上映射一个TensorFlow函数,该向量对应于矩阵中每个像素的深度通道,维度[批次大小,H,W,n\u通道] 换句话说,对于批次中每个大小为H x W的图像: 我提取了一些具有相同大小hxw的特征映射F_k(其数量为n_通道)(因此,所有特征映射都是形状[H,W,n_通道]的张量) 然后,我希望对向量v_ij应用一个自定义函数,该向量与每个特征映射F_k的第I行和第j列相关联,但探索整个深度通道(例如,v具有维度[1 x 1 x n_通道])。理想情况下,所有这一切都将并行发生

我想在每个向量上映射一个TensorFlow函数,该向量对应于矩阵中每个像素的深度通道,维度[批次大小,H,W,n\u通道]

换句话说,对于批次中每个大小为H x W的图像:

  • 我提取了一些具有相同大小hxw的特征映射F_k(其数量为n_通道)(因此,所有特征映射都是形状[H,W,n_通道]的张量)
  • 然后,我希望对向量v_ij应用一个自定义函数,该向量与每个特征映射F_k的第I行和第j列相关联,但探索整个深度通道(例如,v具有维度[1 x 1 x n_通道])。理想情况下,所有这一切都将并行发生
  • 下面可以找到一幅解释该过程的图片。该图片的唯一区别在于输入和输出“感受野”的大小均为1x1(将该函数独立应用于每个像素)

    这类似于对矩阵应用1x1卷积;但是,我需要在深度通道上应用更通用的函数,而不是简单的求和操作

    我认为
    tf.map\u fn()
    可能是一个选项,我尝试了以下解决方案,其中我递归地使用
    tf.map\u fn()
    来访问与每个像素相关的功能。然而,这类功能似乎是次优的,最重要的是在尝试反向传播梯度时会出错

    您是否知道发生这种情况的原因,以及我应该如何构造代码以避免错误

    这是我当前对该功能的实现:

    import tensorflow as tf
    from tensorflow import layers
    
    
    def apply_function_on_pixel_features(incoming):
        # at first the input is [None, W, H, n_channels]
        if len(incoming.get_shape()) > 1:
            return tf.map_fn(lambda x: apply_function_on_pixel_features(x), incoming)
        else:
            # here the input is [n_channels]
            # apply some function that applies a transfomration and returns a vetor of the same size
            output = my_custom_fun(incoming) # my_custom_fun() doesn't change the shape
            return output
    
    以及我的代码主体:

    H = 128
    W = 132
    n_channels = 8
    
    x1 = tf.placeholder(tf.float32, [None, H, W, 1])
    x2 = layers.conv2d(x1, filters=n_channels, kernel_size=3, padding='same')
    
    # now apply a function to the features vector associated to each pixel
    x3 = apply_function_on_pixel_features(x2)  
    x4 = tf.nn.softmax(x3)
    
    loss = cross_entropy(x4, labels)
    optimizer = tf.train.AdamOptimizer(lr)
    train_op = optimizer.minimize(loss)  # <--- ERROR HERE!
    
    可以找到整个错误堆栈和代码。 谢谢你的帮助

    G


    更新:


    根据@thushv89的建议,我为这个问题添加了一个可能的解决方案。我仍然不知道我以前的代码为什么不起作用。对此的任何见解都将非常感谢。

    根据@thushv89的建议,我重塑了数组,应用了函数,然后又重塑了它(为了避免
    tf.map\u fn
    递归)。我仍然不知道以前的代码为什么不起作用,但是当前的实现允许将渐变传播回以前的层。我将在下面留下它,可能对谁感兴趣:

    def apply_function_on_pixel_features(incoming, batch_size):
    
        # get input shape:
        _, W, H, C = incoming.get_shape().as_list()
        incoming_flat = tf.reshape(incoming, shape=[batch_size * W * H, C])
    
        # apply function on every vector of shape [1, C]
        out_matrix = my_custom_fun(incoming_flat)  # dimension remains unchanged
    
        # go back to the input shape shape [None, W, H, C]
        out_shape = tf.convert_to_tensor([batch_size, W, H, C])
        out_matrix = tf.reshape(out_matrix, shape=out_shape)
    
        return out_matrix
    
    请注意,现在我需要给出批量大小来正确地重塑张量,因为如果我没有给出任何或-1作为维度,TensorFlow会抱怨


    如果您对上述代码有任何意见和见解,我们将不胜感激。

    @gabriele关于必须依赖于批大小的问题,您是否尝试过以下方法?此函数不依赖于批大小。您可以用任何您喜欢的方法替换
    映射fn

    def apply_function_on_pixel_features(incoming):
    
        # get input shape:
        _, W, H, C = incoming.get_shape().as_list()
        incoming_flat = tf.reshape(incoming, shape=[-1, C])
    
        # apply function on every vector of shape [1, C]
        out_matrix = tf.map_fn(lambda x: x+1, incoming_flat)  # dimension remains unchanged
    
        # go back to the input shape shape [None, W, H, C]
        out_matrix = tf.reshape(out_matrix, shape=[-1, W, H, C])
    
        return out_matrix
    
    我测试的完整代码如下所示

    import numpy as np
    import tensorflow as tf
    from tensorflow.keras.losses import categorical_crossentropy
    
    def apply_function_on_pixel_features(incoming):
    
        # get input shape:
        _, W, H, C = incoming.get_shape().as_list()
        incoming_flat = tf.reshape(incoming, shape=[-1])
    
        # apply function on every vector of shape [1, C]
        out_matrix = tf.map_fn(lambda x: x+1, incoming_flat)  # dimension remains unchanged
    
        # go back to the input shape shape [None, W, H, C]
        out_matrix = tf.reshape(out_matrix, shape=[-1, W, H, C])
    
        return out_matrix
    
    H = 32
    W = 32
    x1 = tf.placeholder(tf.float32, [None, H, W, 1])
    labels = tf.placeholder(tf.float32, [None, 10])
    x2 = tf.layers.conv2d(x1, filters=1, kernel_size=3, padding='same')
    
    # now apply a function to the features vector associated to each pixel
    x3 = apply_function_on_pixel_features(x2)  
    x4 = tf.layers.flatten(x3)
    x4 = tf.layers.dense(x4, units=10, activation='softmax')
    
    loss = categorical_crossentropy(labels, x4)
    optimizer = tf.train.AdamOptimizer(0.001)
    train_op = optimizer.minimize(loss)
    
    
    x = np.zeros(shape=(10, H, W, 1))
    y = np.random.choice([0,1], size=(10, 10))
    
    
    with tf.Session() as sess:
      tf.global_variables_initializer().run()
      sess.run(train_op, feed_dict={x1: x, labels:y})
    

    请参阅@geometrical谢谢你的回答。我恐怕我没有很好地解释这个问题。我更新了这个问题,所以可能它更清楚。如果你仍然认为广播是最好的选择,请你更好地解释一下如何在我的情况下使用它,好吗?(我不明白)我用我当前的代码和问题更新了这个问题。我认为错误与if语句和应用函数中的递归有关。你能与你共享要应用的确切函数吗?我认为广播可以用于基础数学,其他一些tensorflow函数采用轴参数。我不确定如果可以应用任何函数。@gabriele,看这幅图像,你似乎试图在特征映射中的每个像素上应用某个自定义函数?是否正确,如果正确,为什么需要递归?只需进行一次整形,进行映射,然后再进行一次整形,恢复到原始形状?您好@thushv89,谢谢您的建议。但是,对于wh根据你的建议,我将重塑张量,使其具有形状[-1],而不是[batch_size*W*H,C](这是我需要将函数一致地应用于每个像素的所有特征)。此外,我认为重塑为[-1,C],然后[-1,W,H,C]给了我一个错误。TensorFlow似乎在抱怨,因为它无法将形状未知的对象转换为tensor。@gabriele,实际上对我来说工作得很好。你有错误吗?嗨@thushv89,很抱歉回复太晚,但我以前无法测试。我再次尝试用-1替换批次大小,现在它似乎工作了。可能是我出了问题。谢谢您的帮助!:)您应该使用
    shape=[-1,C]
    更新行
    incoming\u flat=tf.reformate(incoming,shape=[-1])
    ,这是我想要获得的,然后我将为您分配bounty@gabriele,很高兴听到这个消息。更新了我的答案。:)
    import numpy as np
    import tensorflow as tf
    from tensorflow.keras.losses import categorical_crossentropy
    
    def apply_function_on_pixel_features(incoming):
    
        # get input shape:
        _, W, H, C = incoming.get_shape().as_list()
        incoming_flat = tf.reshape(incoming, shape=[-1])
    
        # apply function on every vector of shape [1, C]
        out_matrix = tf.map_fn(lambda x: x+1, incoming_flat)  # dimension remains unchanged
    
        # go back to the input shape shape [None, W, H, C]
        out_matrix = tf.reshape(out_matrix, shape=[-1, W, H, C])
    
        return out_matrix
    
    H = 32
    W = 32
    x1 = tf.placeholder(tf.float32, [None, H, W, 1])
    labels = tf.placeholder(tf.float32, [None, 10])
    x2 = tf.layers.conv2d(x1, filters=1, kernel_size=3, padding='same')
    
    # now apply a function to the features vector associated to each pixel
    x3 = apply_function_on_pixel_features(x2)  
    x4 = tf.layers.flatten(x3)
    x4 = tf.layers.dense(x4, units=10, activation='softmax')
    
    loss = categorical_crossentropy(labels, x4)
    optimizer = tf.train.AdamOptimizer(0.001)
    train_op = optimizer.minimize(loss)
    
    
    x = np.zeros(shape=(10, H, W, 1))
    y = np.random.choice([0,1], size=(10, 10))
    
    
    with tf.Session() as sess:
      tf.global_variables_initializer().run()
      sess.run(train_op, feed_dict={x1: x, labels:y})