For two 2D tensors a and b (of size [b,n] and [b,m] respectively),
a[:, :, None] @ b[:, None, :] (of size [b,n,m]) gives the outer product operated on each item in the batch.
It’s easy to extend this to higher dimensions, for example, for two 3D tensors a and b (of size [b,n,m] and [b,n,k]),
a[:, :, :, None] @ b[:,:, None, :] gives the desired outer product of size [b,n,m,k]
Hope this helps!