TensorFlow中的batch_matmul是如何工作的?

12

Tensorflow有一个名为batch_matmul的函数,可以将高维张量相乘。但我很难理解它是如何运作的,部分原因可能是我很难想象它。

enter image description here

我的目标是将矩阵与3D张量的每个切片相乘,但我不太了解张量a的形状。 z是最内层的维度吗? 以下哪个是正确的?

enter image description here

我更希望第一个是正确的——对我来说最直观且易于在 .eval() 输出中查看。但我怀疑第二个是正确的。

Tensorflow表示batch_matmul执行以下操作:

out[..., :, :] = matrix(x[..., :, :]) * matrix(y[..., :, :])

那是什么意思?在我的例子中,这又意味着什么?有什么正在与什么相乘?为什么我没有按照预期得到一个3D张量?


1
tf.batch_matmul不再可用。 - Salvador Dali
6个回答

23

您可以将其想象为对批处理中的每个训练样例执行矩阵乘法。

例如,如果您有以下维度的两个张量:

a.shape = [100, 2, 5]
b.shape = [100, 5, 2]

如果你执行批量操作tf.matmul(a, b),你的输出将具有形状[100, 2, 2]

这里的100是批次大小,其他两个维度则代表你的数据维度。


我感觉你只是部分回答了这个问题。具体来说,为什么你的例子中b的第一维必须是100?如果我有一个张量a,其中包含一批样本,并且我想在每个样本上应用相同的操作,即我想将它们每个都乘以[5, 2]的张量b,这只能用tf.tile完成吗?如果不是,batch_matmul的输出如何定义? - Alex Lenail
1
@AlexLenail:我有完全相同的问题 - 我想将一个3D张量乘以2D张量,而不需要显式平铺2D张量。你找到答案了吗? - ahmadh
使用matmul支持的广播机制。 - Andrzej Pronobis

18

首先,tf.batch_matmul() 已被移除并不再可用。现在你需要使用tf.matmul():

输入必须是矩阵(或秩 > 2 的张量,表示矩阵批次),具有匹配的内部维度,可能需要转置。

因此,让我们假设您有以下代码:

import tensorflow as tf
batch_size, n, m, k = 10, 3, 5, 2
A = tf.Variable(tf.random_normal(shape=(batch_size, n, m)))
B = tf.Variable(tf.random_normal(shape=(batch_size, m, k)))
tf.matmul(A, B)
现在你将收到一个形状为(batch_size, n, k)的张量。这里发生了什么事情。假设你有batch_size个矩阵nxmbatch_size个矩阵mxk。现在对于每一对它们,你计算nxm X mxk,这给你一个nxk的矩阵。你会有batch_size个。

请注意,类似以下内容也是有效的:

A = tf.Variable(tf.random_normal(shape=(a, b, n, m)))
B = tf.Variable(tf.random_normal(shape=(a, b, m, k)))
tf.matmul(A, B)

并且将给你一个形状(a, b, n, k)


4

您现在可以使用tf.einsum,在TensorFlow0.11.0rc0及更高版本中实现此操作。

例如,

M1 = tf.Variable(tf.random_normal([2,3,4]))
M2 = tf.Variable(tf.random_normal([5,4]))  
N = tf.einsum('ijk,lk->ijl',M1,M2)       

它将矩阵 M2 与 M1 中每个批次(2 批次)中的每个帧(3 帧)相乘。

输出为:

[array([[[ 0.80474716, -1.38590837, -0.3379252 , -1.24965811],
        [ 2.57852983,  0.05492432,  0.23039417, -0.74263287],
        [-2.42627382,  1.70774114,  1.19503212,  0.43006262]],

       [[-1.04652011, -0.32753903, -1.26430523,  0.8810069 ],
        [-0.48935518,  0.12831448, -1.30816901, -0.01271309],
        [ 2.33260512, -1.22395933, -0.92082584,  0.48991606]]], dtype=float32),
array([[ 1.71076882, 0.79229093, -0.58058828, -0.23246667],
       [ 0.20446332,  1.30742455, -0.07969904,  0.9247328 ],
       [-0.32047141,  0.66072595, -1.12330854,  0.80426538],
       [-0.02781649, -0.29672042,  2.17819595, -0.73862702],
       [-0.99663496,  1.3840003 , -1.39621222,  0.77119476]], dtype=float32), 
array([[[ 0.76539308, 2.77609682, -1.79906654,  0.57580602, -3.21205115],
        [ 4.49365759, -0.10607499, -1.64613271,  0.96234947, -3.38823152],
        [-3.59156275,  2.03910899,  0.90939498,  1.84612727,  3.44476724]],

       [[-1.52062428,  0.27325237,  2.24773455, -3.27834225,  3.03435063],
        [ 0.02695178,  0.16020992,  1.70085776, -2.8645196 ,  2.48197317],
        [ 3.44154787, -0.59687197, -0.12784094, -2.06931567, -2.35522676]]], dtype=float32)]

我已经验证过,算法是正确的。


只是一个小疑问,相对于其他方法如batch_matmul(),matmul()等,tf.einsum()是快还是慢? 我想在tensorflow中实现张量点积,但只有einsum()方法似乎支持它,而其他方法需要一些重塑和再次变换的过程,因此我想知道是否使用einsum()是有效的。 - pikachuchameleon
它应该取决于tensorflow的实现,这在不同版本中会有所不同。 - xuancong84
@pikachuchameleon 它应该是相同的。einsum 会简化为 matmul 和 transpose。如果需要转置,则方程式会变慢,因为这需要进行深度复制。 - Roy

2

tf.tensordot可以解决这个问题。它支持批量操作,例如,如果您想将一个2D张量与具有批处理维度的3D张量收缩,则可使用该函数。

如果a的形状为[n,m],b的形状为[?,m,l],则

y = tf.tensordot(b,a,axes = [1,1])将生成一个形状为[?,n,l]的张量

https://www.tensorflow.org/api_docs/python/tf/tensordot


-1
这个特定问题的答案是使用tf.scan函数。
如果a = [5,3,2] #每个batch有3X2矩阵,并且共有5个batch的维度 并且b = [2,3] #要与每个样本相乘的常数矩阵
那么let def fn(a,x): return tf.matmul(x,b)
initializer = tf.Variable(tf.random_number(3,3))
h = tf.scan(fn,outputs,initializer)
这个h将存储所有输出。

-1

这很简单,就像分别在第一维上进行拆分,然后进行乘法和连接。如果你想将三维变成二维,可以进行reshape、乘法,再reshape回去。例如:[100, 2, 5] -> [200, 5] -> [200, 2] -> [100, 2, 2]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接