Embeddings
Two families of vectors, both 128 dimensions, both trained in pure TypeScript inside Convex. Taste vectors say what a person engages with. Graph vectors say where a person sits in the friendship graph. Four vector indexes back them: user taste, post engagement, and two generations of graph embeddings.
Taste vectors
Taste vectors are both online and batch.
Online. A new post is seeded from the author's last 20 posts with a soft prior, or from a deterministic random vector if there is nothing to seed from. Each engagement then moves the user toward the post and the post toward the engager, as weighted running means. The signal weights: a like is 1.0, a comment or an article read is 2.5, a long dwell is 0.3.
Batch. A daily item2vec job at 04:30 retrains from a 45-day window of those signals. Positives are session-windowed: two posts a user engaged with within five positions of each other in their timeline are a positive pair. Each positive draws five negatives. Training runs for 30 epochs, and events are capped at 100 per user so that power users do not act as stop-words. User vectors are recency-weighted means of the posts they engaged with, with a 24-day half-life.
The vector index is bucketed at 2,560 users per bucket. It serves the taste-matched fan-out of up to 2,000 users per post and the viral reseeding job.
Graph vectors
Graph vectors are Node2Vec on the friendship graph: five random walks of length 15 per node, a window of 5, three negatives, two epochs. Admins are excluded from the graph because they act as super-hubs and would pull every vector toward themselves.
Two tables are kept.
- Fresh
- Retrained daily at 05:00 on the full graph. Used wherever the current graph is the point: shared-orbit suggestions, the tengslakort test, the admin visualisation.
- Frozen
- Retrained every 14 days, only on edges older than 14 days. Used by the friend ranker, so that the model never sees the friendships it is being trained to predict.
Without the frozen table, the friend ranker's graphSim feature would encode the answer: two people who just became friends are already close in a graph that contains their edge, which is label leakage. The frozen variant excludes those edges from the training signal.
Users missing from the last run, because they joined after it, get an inductive embedding: the closeness-weighted mean of their top 50 friends' vectors, materialised nightly.
Where each is used
| Vector | Used for |
|---|---|
| Taste, user and post | The tasteSim feature in the feed ranker; taste-matched fan-out of up to 2,000 users per new post; viral reseeding, taste-similar users first |
| Graph, fresh | Shared-orbit suggestions on profiles, a vector search around a 30/70 blend of the viewer's and the profile's vectors; the admin PCA and k-means visualisation of the friend graph; the tengslakort same-neighbourhood test at cosine 0.6 |
| Graph, frozen | The graphSim feature in the friend ranker |
The feed ranker reads tasteSim and graphSim as two features among 37. The friend suggestions page shows how the frozen graph similarity fits among the other eleven.