×

Why Candidate Generation Comes Before Ranking in This Investor Graph

Abstract illustration of a graph neural network recommender

The dual GATv2 design is the part people ask about first. The part that made the system usable was earlier in the pipeline: candidate generation.

At Doriot, the investor-startup graph was large enough that running attention over every pair was not a ranking problem. It was a latency and cost problem. Attention is expensive. Most pairs are irrelevant. So the system needed a cheap stage that throws away most of the graph before anything heavy runs.

Why Not Rank the Full Graph

GATv2 with multi-head attention is a good fit for bipartite matching once the candidate set is small. It is a bad default for “score every investor against every startup.” The compute grows with edges you do not need. Quality does not improve when most candidates were never plausible.

Recommendation systems already know this pattern: retrieve first, rank second. The graph version needs the same split. Retrieval owns coverage. Ranking owns discrimination.

GraphSAGE as the Retrieval Stage

GraphSAGE fits retrieval because it is inductive and samples neighborhoods instead of materializing the full graph for every query. Combined with k-nearest-neighbor clustering on startup similarity, it produces a shortlist of investors worth scoring carefully.

That shortlist is the contract between stages. GraphSAGE’s job is not perfect ordering. Its job is high recall of plausible matches with low enough cost that the product can run often. If a good investor never enters the shortlist, GATv2 never gets to save it.

What Ranking Sees After Retrieval

Once candidates exist, dual GATv2 models specialize for VC and angel dynamics. That separation only works if retrieval already put the right kinds of investors in the pool. A retrieval stage that collapses both investor types into one noisy blob forces ranking to waste capacity on cleanup.

So candidate generation is not a preprocessing detail. It decides which failure modes ranking has to solve.

Re-ranking and Rivalry After the GNN

Raw GATv2 scores still miss deal context. Bayesian likelihood re-ranking adjusts the list using observed investment patterns. Jaccard-based rivalry detection finds investors who compete for the same deals or cluster as co-investors. That signal changes order even when the graph embedding looked similar.

Why after ranking, not inside GraphSAGE: rivalry and Bayesian adjustments need a small ranked set. Running them over the full graph is the same mistake as running attention over the full graph.

The Layering Lesson

Cheap graph retrieval → specialized attention ranking → local re-ranking with rivalry. Each stage owns a different error. Mixing them into one model hides where the system failed and makes every change expensive.

If your graph recommender feels slow or noisy, check whether ranking is doing retrieval’s job.

Learn More

Related: Why this investor recommender uses two GATv2 models, not one.

Project: Doriot on Projects. Open source: Investor Recommender on GitHub.

Building something with AI?

I design and ship production systems across ML, deep learning, GenAI, LLMs, and RAG. Happy to talk through what you're working on.

Book a Free Call