Tensorflow - 包含批量数据的输入矩阵的{mm

时间:2016-07-06 23:43:32

标签: python tensorflow

我有一些input_x代表的数据。它是一个未知大小的张量(应该是批量输入),每个项目的大小为ninput_x经历了tf.nn.embedding_lookup,因此embed现在具有维度[?, n, m],其中m是嵌入大小,?是指未知的批量大小。

这里描述:

input_x = tf.placeholder(tf.int32, [None, n], name="input_x") 
embed = tf.nn.embedding_lookup(W, input_x)

我现在正试图将我的输入数据中的每个样本(现在通过嵌入维度扩展)乘以矩阵变量U,我似乎无法知道如何做到这一点。 / p>

我首先尝试使用tf.matmul,但由于形状不匹配而导致错误。然后我通过扩展U的维度并应用batch_matmul来尝试以下内容(我也尝试了tf.nn.math_ops.中的函数,结果是相同的):

U = tf.Variable( ... )    
U1 = tf.expand_dims(U,0)
h=tf.batch_matmul(embed, U1)

这会传递初始编译,但是当应用实际数据时,我会收到以下错误:

In[0].dim(0) and In[1].dim(0) must be the same: [64,58,128] vs [1,128,128]

我也知道为什么会发生这种情况 - 我复制了U的维度,现在是1,但是小批量大小64不合适。

如何正确地对张量矩阵输入进行矩阵乘法(对于未知的批量大小)?

4 个答案:

答案 0 :(得分:21)

1。我想将一批矩阵与一批相同长度的成对矩阵相乘

M = tf.random_normal((batch_size, n, m))
N = tf.random_normal((batch_size, m, p))

# python >= 3.5
MN = M @ N
# or the old way,
MN = tf.matmul(M, N)
# MN has shape (batch_size, n, p)

2。我想将一批矩阵与一批相同长度的向量成对相乘

我们通过向v添加和删除维度来回到情况1。

M = tf.random_normal((batch_size, n, m))
v = tf.random_normal((batch_size, m))

Mv = (M @ v[..., None])[..., 0]
# Mv has shape (batch_size, n)

3。我想将单个矩阵与一批矩阵相乘

在这种情况下,我们不能简单地将1的批量维度添加到单个矩阵中,因为tf.matmul不会以批量维度进行广播。

3.1。单个矩阵在右侧

在这种情况下,我们可以使用简单的整形将矩阵批处理视为单个大矩阵。

M = tf.random_normal((batch_size, n, m))
N = tf.random_normal((m, p))

MN = tf.reshape(tf.reshape(M, [-1, m]) @ N, [-1, n, p])
# MN has shape (batch_size, n, p)

3.2。单个矩阵在左侧

这种情况更加复杂。通过转置矩阵,我们可以回到情况3.1。

MT = tf.matrix_transpose(M)
NT = tf.matrix_transpose(N)
NTMT = tf.reshape(tf.reshape(NT, [-1, m]) @ MT, [-1, p, n])
MN = tf.matrix_transpose(NTMT)

但是,换位可能是一项代价高昂的操作,在此情况下,它在整批矩阵上执行两次。最好简单地复制M以匹配批次尺寸:

MN = tf.tile(M[None], [batch_size, 1, 1]) @ N

分析将告诉您哪个选项对给定的问题/硬件组合更有效。

4。我想将一个矩阵与一批向量相乘

这看起来与情况3.2类似,因为单个矩阵位于左侧,但实际上更简单,因为转置向量本质上是无操作的。我们最终以

M = tf.random_normal((n, m))
v = tf.random_normal((batch_size, m))

MT = tf.matrix_transpose(M)
Mv = v @ MT

einsum呢?

以前所有的乘法都可以用tf.einsum瑞士军刀来写。例如,第一个3.2的解决方案可以简单地写为

MN = tf.einsum('nm,bmp->bnp', M, N)

但是,请注意,einsum最终是relying on tranpose and matmul进行计算。

因此,即使einsum是编写矩阵乘法的一种非常方便的方法,但它掩盖了其下面运算的复杂性-例如,猜测einsum表达式将转置多少次并不容易您的数据,以及操作的成本。同样,它可能掩盖了一个事实,即同一操作可能有多种选择(请参阅案例3.2),而不一定选择更好的选择。

由于这个原因,我个人将使用上述明确的公式来更好地传达它们各自的复杂性。尽管如果您知道自己在做什么,并且喜欢einsum语法的简单性,那么一定要这么做。

答案 1 :(得分:17)

matmul operation仅适用于矩阵(2D张量)。以下是执行此操作的两种主要方法,均假设U是2D张量。

  1. embed切片到2D张量中,并将每个张量分别与U相乘。这可能是最容易使用这样的tf.scan()

    h = tf.scan(lambda a, x: tf.matmul(x, U), embed)
    
  2. 另一方面,如果效率很重要,最好将embed重塑为2D张量,这样可以使用单个matmul进行乘法运算:

    embed = tf.reshape(embed, [-1, m])
    h = tf.matmul(embed, U)
    h = tf.reshape(h, [-1, n, c])
    

    其中cU中的列数。最后一次重塑将确保h是一个3D张量,其中第0维对应于批次,就像原始x_inputembed一样。

答案 2 :(得分:4)

正如@Stryke所说,有两种方法可以达到这个目的:1。扫描,和2.重塑

  1. tf.scan需要lambda函数,通常用于递归操作。这里有一些例子:https://rdipietro.github.io/tensorflow-scan-examples/

  2. 我个人更喜欢重塑,因为它更直观。如果你试图通过2D张量矩阵(如Cijl = Aijk * Bkl)将3D张量中的每个矩阵进行矩阵乘法,则可以通过简单的重塑来实现。

    A' = tf.reshape(Aijk,[i*j,k])
    C' = tf.matmul(A',Bkl)
    C = tf.reshape(C',[i,j,l])
    

答案 3 :(得分:0)

在TensorFlow 1.11.0中,tf.matmul的{​​{3}}似乎错误地认为它适用于等级> = 2。

相反,我发现最好的替代方法是使用tf.tensordot(a, b, (-1, 0))docs)。

此函数以常规形式a获取数组b的任何轴和数组tf.tensordot(a, b, axis)的任何轴的点积。将axis设置为(-1, 0)可获得两个数组的标准点积。