定义

PyTorch 高级索引:

从笛卡尔积到点对点映射在基础索引(Basic Indexing,如 tensor[0:5, :])中,我们定义的是子空间切片。

而高级索引(Advanced Indexing)是指使用整型张量或布尔张量作为索引。

数学描述:设目标张量为

当我们提供 个索引张量 时,这组索引张量必须是可广播(Broadcastable的。

设它们广播后的共同形状为
那么高级索引的操作本质上是定义了一个映射

核心逻辑:输出张量 在位置 的值,是由 个索引张量在相同位置提供的坐标共同指向的。

案例分析

        edge_index = self.connect_edges(res_X, batch)  # 2, E
        n_nodes = res_H.shape[0]
        n_edges = edge_index.shape[1]
        n_channels = res_X.shape[1]
        n_heads = self.n_heads
        row, col = edge_index
 
        R_ij = torch.cdist(res_X[row], res_X[col], p=2)  # (E, 14, 3) , (E, 14, 3) -> (E, 14, 14)
        dist_rep = self.distance_expansion(R_ij).view(
            row.shape[0], res_X.shape[1], res_X.shape[1], -1
        )  # E, 14, 14, d_edge
        Q = self.W_Q(res_H).view([n_nodes, n_channels, self.n_heads, self.d])  # (N, 14, n_heads, d)
        K = self.W_K(res_H).view([n_nodes, n_channels, self.n_heads, self.d])
        V = self.W_V(res_H).view([n_nodes, n_channels, self.n_heads, self.d])
 
        attend_logits = torch.matmul(Q[row].transpose(1, 2), K[col].permute(0, 2, 3, 1)).view(
            [n_edges, self.n_heads, n_channels, n_channels]
        )  # (E, n_heads, 14, d) @ (E, n_heads, d, 14) -> (E, n_heads, 14, 14)
        attend_logits /= np.sqrt(self.d)  # n_edges, n_heads, 14, 14
        attend_logits = attend_logits + self.sigma_D(dist_rep).permute(0, 3, 1, 2)  # distance bias
        attend_mask = torch.ones(n_edges, self.n_heads, n_channels, n_channels, device=res_H.device, dtype=torch.bool)
 
        # sparse attention, only keep top k=3
        attend_logits[torch.logical_not(attend_mask)] = -1e5  # do not sellect from entries not attend
        _, top_indices = torch.topk(attend_logits, self.sparse_k, dim=-1, largest=True)  # (E, n_heads, 14, 3)
        sparse_mask = torch.zeros_like(attend_logits, dtype=torch.bool)  # (E, n_heads, 14, 14)
        rows = torch.arange(n_edges).view(-1, 1, 1, 1).expand(-1, n_heads, n_channels, self.sparse_k) # (E, n_heads, 14, 3)
        depth = torch.arange(n_heads).view(1, -1, 1, 1).expand(n_edges, -1, n_channels, self.sparse_k) # (E, n_heads, 14, 3)
        height = torch.arange(n_channels).view(1, 1, -1, 1).expand(n_edges, n_heads, -1, self.sparse_k) # (E, n_heads, 14, 3)
        sparse_mask[rows, depth, height, top_indices] = True
        attend_logits = attend_logits * sparse_mask
        attend_logits = attend_logits * attend_mask  # (E, n_heads, 14, 14)

上述代码本质为坐标映射的构造. 当执行 sparse_mask[rows, depth, height, top_indices] = True 时,实际上是在手动构建这个 维坐标映射。

  • 维度对齐为了对形状为 的四维张量进行精确赋值,需要提供 4 个坐标分量:

    • Row 轴 ():
    • Depth 轴 ():
    • Height 轴 ():
    • Width 轴 ():
  • 广播机制的数学实现使用的 torch.arange(…).view(…).expand(…) 本质上是在构造基矢量张量:

    • rows: 形状 ,满足
    • depth: 形状 ,满足
    • height: 形状 ,满足
    • 这样,当它们与形状为 的 top*indices 结合时,对于任意确定的索引位置 ,其指向的目标坐标就是:(e, \ h, \ c, \ \text{top_indices}*{e,h,c,k})