File size: 2,275 Bytes
d3fd67f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"""Cross-encoder stage: MS MARCO MiniLM-L6-v2 logits (max_length 256) for every (query, pool doc), top-DEPTH.
Resumable: out/<tag>.ce.npz holds logits computed so far in pool order.
Usage: python ce_stage.py CE_DIR DEPTH
"""
import json
import os
import sys
import time

import numpy as np
import torch
from sentence_transformers import CrossEncoder

from common import OUT, load_corpus, load_queries

torch.set_num_threads(os.cpu_count())
TAGS = ["scifact.test", "nfcorpus.test", "trec-covid.test", "scifact.train", "nfcorpus.dev", "fiqa.dev",
        "fiqa.test", "scidocs.test"]


def main():
    cdir, depth = sys.argv[1], int(sys.argv[2])
    ce = CrossEncoder(cdir, max_length=256, device="cpu")
    cache = {}
    for tag in TAGS:
        name = tag.split(".")[0]
        if name not in cache:
            cache.clear()
            ids, titles, texts = load_corpus(name)
            idx = {d: i for i, d in enumerate(ids)}
            cache[name] = (idx, titles, texts, load_queries(name))
        idx, titles, texts, queries = cache[name]
        pools = json.load(open(os.path.join(OUT, f"{tag}.pools.json")))
        pairs = [(queries[q], (titles[idx[d]] + ". " if titles[idx[d]] else "") + texts[idx[d]])
                 for q in sorted(pools) for d in pools[q]["ids"][:depth]]
        path = os.path.join(OUT, f"{tag}.ce.npz")
        done = np.load(path)["logits"] if os.path.exists(path) else np.zeros(0, np.float32)
        if len(done) == len(pairs):
            print(f"{tag}: complete", flush=True)
            continue
        t0, start = time.time(), len(done)
        for s in range(start, len(pairs), 2048):
            chunk = pairs[s:s + 2048]
            o = np.argsort([len(a) + len(b) for a, b in chunk], kind="stable")  # length-sort: less padding
            out = np.asarray(ce.predict([chunk[i] for i in o], batch_size=32, show_progress_bar=False),
                             np.float32).reshape(-1)
            res = np.empty_like(out)
            res[o] = out
            done = np.concatenate([done, res])
            np.savez(path, logits=done)
            rate = (len(done) - start) / (time.time() - t0)
            print(f"{tag}: {len(done)}/{len(pairs)} pairs, {rate:.0f} pairs/s", flush=True)


if __name__ == "__main__":
    main()