groupby聚合平均值(单位:pytorch)
我有一个二维张量:groupby聚合平均值(单位:pytorch),pytorch,Pytorch,我有一个二维张量: samples = torch.Tensor([ [0.1, 0.1], #-> group / class 1 [0.2, 0.2], #-> group / class 2 [0.4, 0.4], #-> group / class 2 [0.0, 0.0] #-> group / cla
samples = torch.Tensor([
[0.1, 0.1], #-> group / class 1
[0.2, 0.2], #-> group / class 2
[0.4, 0.4], #-> group / class 2
[0.0, 0.0] #-> group / class 0
])
以及对应于类别的每个样本的标签:
labels = torch.LongTensor([1, 2, 2, 0])
因此len(样本)==len(标签)
。现在我想计算每个类/标签的平均值。由于有3类(0、1和2),最终向量应具有维度[n_类,samples.shape[1]]
,因此预期的解决方案应为:
result == torch.Tensor([
[0.1, 0.1],
[0.3, 0.3], # -> mean of [0.2, 0.2] and [0.4, 0.4]
[0.0, 0.0]
])
问题:如何在纯pytorch(即没有numpy以便我可以自动加载)和理想情况下没有for循环的情况下实现这一点?您需要做的就是形成一个mxn矩阵(m=num类,n=num样本),该矩阵将选择适当的权重,并适当缩放平均值。然后可以在新形成的矩阵和样本矩阵之间执行矩阵乘法 给定标签,矩阵应为(每行为一个类别编号,每一个类别为一个样本编号及其权重): 您可以按如下方式形成:
M = torch.zeros(labels.max()+1, len(samples))
M[labels, torch.arange(len(samples)] = 1
M = torch.nn.functional.normalize(M, p=1, dim=1)
torch.mm(M, samples)
输出:
tensor([[0.0000, 0.0000],
[0.1000, 0.1000],
[0.3000, 0.3000]])
请注意,输出方法按类顺序正确排序
为什么M[labels,torch.arange(len(samples))]=1
起作用
这是在标签和样本数之间执行广播操作。本质上,我们正在为标签中的每个元素生成一个二维索引:第一个指定它所属的m个类中的哪一个,第二个简单地指定它的索引位置(从1到N)。另一种方法是显式生成所有二维索引:
twoD_indices = []
for count, label in enumerate(labels):
twoD_indices.append((label, count))
在此转载@ptrblck_de在
由于以前的解决方案不适用于稀疏组的情况(例如,并非所有组都在数据中),我做了一个:)
好的解决方案;对于那些对
M[labels,torch.arange(4]=1
感到困惑的人来说,它本质上只是为分配的列生成索引。因此,在较高的层次上,我们在矩阵中逐列移动(跨越样本),在适当的地方放入1以表示其类别
twoD_indices = []
for count, label in enumerate(labels):
twoD_indices.append((label, count))
labels = labels.view(labels.size(0), 1).expand(-1, samples.size(1))
unique_labels, labels_count = labels.unique(dim=0, return_counts=True)
res = torch.zeros_like(unique_labels, dtype=torch.float).scatter_add_(0, labels, samples)
res = res / labels_count.float().unsqueeze(1)
def groupby_mean(value:torch.Tensor, labels:torch.LongTensor) -> (torch.Tensor, torch.LongTensor):
"""Group-wise average for (sparse) grouped tensors
Args:
value (torch.Tensor): values to average (# samples, latent dimension)
labels (torch.LongTensor): labels for embedding parameters (# samples,)
Returns:
result (torch.Tensor): (# unique labels, latent dimension)
new_labels (torch.LongTensor): (# unique labels,)
Examples:
>>> samples = torch.Tensor([
[0.15, 0.15, 0.15], #-> group / class 1
[0.2, 0.2, 0.2], #-> group / class 3
[0.4, 0.4, 0.4], #-> group / class 3
[0.0, 0.0, 0.0] #-> group / class 0
])
>>> labels = torch.LongTensor([1, 5, 5, 0])
>>> result, new_labels = groupby_mean(samples, labels)
>>> result
tensor([[0.0000, 0.0000, 0.0000],
[0.1500, 0.1500, 0.1500],
[0.3000, 0.3000, 0.3000]])
>>> new_labels
tensor([0, 1, 5])
"""
uniques = labels.unique().tolist()
labels = labels.tolist()
key_val = {key: val for key, val in zip(uniques, range(len(uniques)))}
val_key = {val: key for key, val in zip(uniques, range(len(uniques)))}
labels = torch.LongTensor(list(map(key_val.get, labels)))
labels = labels.view(labels.size(0), 1).expand(-1, value.size(1))
unique_labels, labels_count = labels.unique(dim=0, return_counts=True)
result = torch.zeros_like(unique_labels, dtype=torch.float).scatter_add_(0, labels, value)
result = result / labels_count.float().unsqueeze(1)
new_labels = torch.LongTensor(list(map(val_key.get, unique_labels[:, 0].tolist())))
return result, new_labels