Google · Senior Software Engineer (L5), AI & Data · 2022-2023
Reddit needed every post classified into the IAB content taxonomy, the standard advertisers buy against, so ads could be targeted by what a post is actually about. Two tiers of categories, 37 at the top level. The corpus: 350 million posts, most with images. No labeled training data at that scale; a 715K-post silver set, part of it tagged by hand.
The engagement was originally scoped at roughly a fifth of what it became. As the pipeline design proved out, the scope grew about 5x.
Stage 1: source. BigQuery, 350M posts, partitioned into views by row number so the backfill could be sharded and resumed.
Stage 2: Vision backfill. A Dataflow/Beam pipeline (bq_to_vision_backfill) on n1-standard-2 workers: store image → compress anything over 20 MiB → batch 16 to Cloud Vision → write labels back to BigQuery, with a side errors table. 350M images through the Vision API in about two weeks. It was the largest single pipeline the Vision API had processed; Google provisioned dedicated capacity for the load, and we tuned the pipeline's throughput to what they gave us.
Stage 3: the taxonomy bridge.
This is the part that was interesting. Cloud Vision has its own label vocabulary (~20K labels; 7,732 observed in our corpus). IAB has a different one. The obvious move is to map Vision labels onto IAB categories directly. I built a four-rung matching ladder, each rung looser than the last:

No rung on the ladder is usable; every step trades accuracy for coverage and there's no point where both are acceptable. So nothing gets mapped. Instead: embed all 7,732 labels with Universal Sentence Encoder (512d), cluster with KMeans at k=100, and feed the 100 cluster one-hots as features. The forests learn the mapping from clusters to IAB categories; I stop pretending I can write it by hand.
Stage 4: features. 2,151-dimensional vector: 3 structured features, four USE embeddings (title, body, subreddit name, subreddit metadata; 2,048 dims), 100 cluster one-hots.
Stage 5: the model. Tier 1: 37 binary random forests, one per top-level IAB category. Each has its own decision threshold, chosen to maximize that category's F1 by sweeping 70 candidates from 0.10 to 0.80; the categories that never reach a usable F1 are dropped rather than shipped. Tier 2: one model per Tier 1 parent for the subcategories.
Stage 6: serving. Vertex AI endpoint.
The engagement was sold to consume credits, not to solve a problem. That is upstream of everything else here. There was commitment to burn and a product to push, so the design question was never "what is the least machinery that answers this," it was "what runs on our stack." Every expensive decision below follows from that, and I was inside the frame rather than pushing back on it. The engineer I would want on this today asks what the customer actually needs before anyone designs a pipeline for 350 million images.
The framing was wrong, and it is what I would fix first. A post classified as "pets" does not mean the person reading it wants an ad for pets. Nobody chooses what shows up in front of them on a given subreddit, so a category attached to a post is a weak proxy for intent, and most of the value leaks out at exactly that step. The classification is a reasonable signal. It is a bad answer. What I would build now is a ranker rather than a classifier: a feature map of the image, a feature map of the person, a feature map of the subreddit, and an attention mechanism over the three that orders candidate ads by likelihood of a click, or by usefulness. The IAB label becomes one input to that system instead of being the output of the whole thing.
What I would build today. No taxonomy bridge at all: fine-tune a convolutional network or a vision transformer with a dense classification head on top. There was enough data for it, and it deletes the clustering, the matching ladder and the per-category threshold sweep in one move, and it still serves on Vertex. Spark rather than Dataflow. Java or Kotlin rather than Python. And if the Vision API were non-negotiable, then not Dataflow either: nearly all of that work is waiting on a remote call, which makes it a workflow problem, so Temporal or Airflow, or an ELT job that never leaves BigQuery.
The highest-signal feature was the subreddit name. Free text, already in the metadata, zero pipeline cost. Most of the engineering budget (the 350M-image Vision backfill, the taxonomy bridge, the clustering) went to the image path, and the feature that moved the model most was the one that was there before I started. The right sequence was: exhaust the cheap structured and text signal first, measure the residual, and only then decide whether vision was worth a backfill of that size. I built the expensive thing because it was the interesting thing.
BLOOM for splitting conjoined subreddit names (December 2022: r/wallstreetbets→ "wall street bets"). It worked badly, and I kept the failure table. Rule-based segmentation against a dictionary would have been better, cheaper and debuggable. I keep the table because of what it is now: one of the first times I reached for a language model to do something useful, at the exact moment it could not yet do it. A 7B model today would solve that task without effort, and so would a 4B one. The instinct was right about three years early, which is its own lesson about when to reach for a model and when to reach for a dictionary.
Not every category is learnable, and I treated them as if they were. Reddit already had an internal method, partly validated, and the real exercise was ours measured against theirs. What that surfaced is that the ceiling is per category, not global. "Pets" is trivial: a dog or a cat is in the frame or it isn't. "Education" is close to impossible from an image alone, unless the photo happens to contain a recognizable institution, and a human labeler fails on it too. So the per-forest threshold sweep and dropping the categories that never reach a usable F1 is the right response. I got there by measuring rather than by asking, before any of the pipeline was built, which categories the available signal could support at all.
Embedded with Fortune 500 customers on model best practices and production ML deployments. Migrated thousands of tables from Teradata to BigQuery for Mercado Libre. Designed the distributed data gathering and training path for McDonald's, with a local model in every restaurant. Trained a T5 translation model for a government intelligence agency inside an air-gapped environment: the same deployment shape as Metro, from the other side of the table. The last two are written up with the rest of the edge work.
