Skip to content

possible data leakage #4

Description

@towardsagi

Hi authors,
Thanks for your excellent works. However i have met some troubles in reproducing the results reported in paper. I found that there are two points may cause data leakage:

1. data leakage from adjacency matrix

def compute_sign_feats(node_feats, df, start_i, num_links, root_nodes, args):
num_duplicate = len(root_nodes) // num_links
num_nodes = node_feats.shape[0]
root_inds = torch.arange(len(root_nodes)).view(num_duplicate, -1)
root_inds = [arr.flatten() for arr in root_inds.chunk(1, dim=1)]
output_feats = torch.zeros((len(root_nodes), node_feats.size(1))).to(args.device)
i = start_i
for _root_ind in root_inds:
if i == 0 or args.structure_hops == 0:
sign_feats = node_feats.clone()
else:
prev_i = max(0, i - args.structure_time_gap)
cur_df = df[prev_i: i] # get adj's row, col indices (as undirected)
src = torch.from_numpy(cur_df.src.values)
dst = torch.from_numpy(cur_df.dst.values)
edge_index = torch.stack([
torch.cat([src, dst]),
torch.cat([dst, src])
])
edge_index, edge_cnt = torch.unique(edge_index, dim=1, return_counts=True)
mask = edge_index[0]!=edge_index[1] # ignore self-loops
adj = SparseTensor(
# value = edge_cnt[mask].float(), # take number of edges into consideration
value = torch.ones_like(edge_cnt[mask]).float(),
row = edge_index[0][mask].long(),
col = edge_index[1][mask].long(),
sparse_sizes=(num_nodes, num_nodes)
)
adj_norm = row_norm(adj).to(args.device)
sign_feats = [node_feats]
for _ in range(args.structure_hops):
sign_feats.append(adj_norm@sign_feats[-1])
sign_feats = torch.sum(torch.stack(sign_feats), dim=0)
output_feats[_root_ind] = sign_feats[root_nodes[_root_ind]]
i += len(_root_ind) // num_duplicate
return output_feats

In line 239, the one-hot node feats was multiplied by the normed adjacency matrix, which makes the adj_norm becomes the input node features. However, the adj_norm contains link information that we are going to predict!
Therefore, i have tried to change line 239 to:

sign_feats.append(sign_feats[-1])

2. data leakage from binary search

in sampler.cpp, binary search has been used to accelerate the selection of recent history neighbors. However, the process of positive sampling and negative sampling should be different, for the reason that positive neighbors boundary is right on the target timestamp, and negative neighbors boundary is less than the target timestamp. But the sampler logic did not consider this issue:

GraphMixer/sampler_core.cpp

Lines 295 to 306 in bbdd992

if ((recent) || (e_search - s_search < neighs))
{
// no sampling, pick recent neighbors
for (EdgeIDType k = e_search; k > std::max(s_search, e_search - neighs); k--)
{
if (ts[k] < nts + offset - 1e-7f)
{
add_neighbor(_row[tid], _col[tid], _eid[tid], _ts[tid],
_dts[tid], _nodes[tid], k, nts, _out_node[tid]);
}
}
}

This issue can change the maximum number of history neighbors between positive and negative samples(max_num_positive + 1 = max_num_negative). It may not influence much for other algorithms, as long as the the sampled neighbors are correct. However, for Graphmixer with zero padding inputs, this difference can be easily captured by the mlp-mixer block.

Therefore, i have tried to insert the following codes to change the for-loop:

int _offset = 0;
if (ts[e_search] == nts + offset) _offset = 1;  // positive is on the boundary, so add one neighbor
// no sampling, pick recent neighbors
for (EdgeIDType k = e_search; k > std::max(s_search, e_search - neighs - _offset); k--)

After apply the above two fix, i rerun the code on WIKI and LASTFM dataset. And it turns out for WIKI the average precision(AP) can drop from 99.85 to 99.04, for LASTFM the AP can drop from 96.31 to 86.60. I spend a lot of time on reproducing this project. So i submit this issue and share my thoughts with authors and other readers to discuss whether the above possible data leakage is correct and whether there are more leakage points not discovered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions