分享一下我的第一题的实现,是用爱因斯坦求和约定写的(不然感觉还挺麻烦的写起来)
class TesnorReduceLayer(nn.Module):
def __init__(self, in_features1, in_features2, out_features):
super().__init__()
self.weight = nn.Parameter(
torch.randn(in_features1, in_features2, out_features)
)
def forward(self, X):
# X: (b, x, y, any)
# Xi: (b, x, y, any) => (b, m, y, any)
# Xj: (b, y, x, any) => (b, n, x, any)
# W: (x, y, h)
Xi = X
Xj = X.transpose(1, 2)
out_withdim_ij = torch.einsum('xyk, bmyc, bnxc->bmnkc', self.weight, Xi, Xj)
out = out_withdim_ij.sum(axis=[1, 2])
return out
X = torch.rand(100, 42, 67, 5)
layer = TesnorReduceLayer(42, 67, 6)
layer(X).shape
然后是通用版本:
class TesnorReduceLayer_General(nn.Module):
def __init__(self, out_features):
super().__init__()
# 权重参数
self.weight = None
# 输出维度
self.out_features = out_features
# 额外维度的einsum表达式记录
self.einsum_c_str = "xyk, bmyc, bnxc->bmnkc"
self.einsum_str_lis = "cdefghijlopqrstuvwz" # 预留了 x, y, k, m, n, b
def initParam(self, X):
# X: (b, x, y, any)
assert 0 <= len(X.shape) - 3 <= len(self.einsum_str_lis)
x = X.shape[1]
y = X.shape[2]
self.weight = nn.Parameter(
torch.randn(x, y, self.out_features)
)
self.einsum_c_str = self.einsum_c_str.replace('c', self.einsum_str_lis[:len(X.shape) - 3])
def forward(self, X):
# X: (b, x, y, any)
# Xi: (b, x, y, any) => (b, m, y, any)
# Xj: (b, y, x, any) => (b, n, x, any)
# W: (x, y, k)
if self.weight is None:
self.initParam(X)
Xi = X
Xj = X.transpose(1, 2)
out_withdim_ij = torch.einsum(self.einsum_c_str, self.weight, Xi, Xj)
out = out_withdim_ij.sum(axis=[1, 2])
return out
X = torch.rand(100, 42, 67, 5)
layer = TesnorReduceLayer_General(6)
print(layer(X).shape)
Y = torch.rand(64, 21, 13, 2, 3, 4, 5, 6)
layer = TesnorReduceLayer_General(6)
print(layer(Y).shape)
Z = torch.rand(32, 12, 11, 2, 3, 4, 5, 6)
layer = TesnorReduceLayer_General(6)
print(layer(Z).shape)