How Reddit Scans 1M+ Images a Day to Flag NSFW Content Using Deep Learning
A deep dive into the ML pipeline that cut moderation latency from seconds to milliseconds without blowing the budget.
Fellow Data Tinkerers!
Today we will look at how Reddit handles NSFW content at scale using deep learning
But before that, I wanted to thank those who shared Data Tinkerer with others and share an example of what you can unlock if you share Data Tinkerer with just 2 other people.
There are 100+ more cheat sheets covering everything from Python, R, SQL, Spark to Power BI, Tableau, Git and many more. So if you know other people who like staying up to date on all things data, please share Data Tinkerer with them!
Now, with that out of the way, let’s get to Reddit’s automated content moderation
TL;DR
Situation
Reddit allows NSFW content but needs to ensure it only shows up where users opt in. Their old moderation models weren’t cutting anymore because of limited in scalability and accuracy.
Task
Build a high-performing, scalable deep learning model to detect sexually explicit images at upload time and efficient enough to scan over 1 million images daily with low latency.
Action
The Reddit team:
Chose EfficientNet after testing CNNs, ViTs, and CLIP for the best performance vs. cost tradeoff.
Trained it using Ray for distributed compute and hyperparameter tuning.
Deployed it with Ray Serve, using CPU for I/O and GPU for inference to optimize speed and cost.
Parallelized workloads with Ray Actors and made image downloads asynchronous.
Result
After migrating to GPU-based serving and optimizing CPU/GPU split, they achieved up to 11x faster inference, improved throughput and laid the groundwork for future multimodal moderation using generative AI.
Use Cases
Content moderation, spam image detection
Tech Stack/Framework
EfficientNet, Ray Tune, Ray Serve, TensorFlow, Ray Actors
Explained Further
Background
Reddit deals with a lot of content. Some of them great, some of them weird and some of them … well, let’s say a bit “spicy”. Sexually explicit material is allowed under their content policy but only in places that are clearly marked and only for users who opt in.
The problem is, not everyone plays by the rules. Sometimes, explicit images show up in the wrong places and when that happens, moderation teams scramble and user trust erodes. To prevent that, Reddit needed a system that could automatically flag explicit content before it even goes live.
They’d already built some early detection tools. These relied on shallow models using subreddit-level metadata, basic visual signals and a bit of post context. It worked well enough for low-scale moderation but it didn’t generalize, wasn’t great with edge cases and didn’t scale with the volume Reddit now sees.

So they built something better. A deep learning image model, served in real time that classifies NSFW content as it’s uploaded. Let’s have a look at how they did it and what challenges they needed to overcome.
From metadata to pixels
The old system was reliant on downstream heuristics. It leaned on subreddit tags and post-level signals to guess whether content was explicit. That helped at the margins but it missed the actual point: what’s in the image?
Reddit’s ML team wanted to move to a pixel-first approach, processing the raw content directly. No more indirect signals. No more shortcut logic. Just straight-up image classification.
That sounds obvious but doing it at Reddit scale isn’t trivial. The model would need to:
Scan over 1 million images a day
Deliver sub-second inference (ideally sub-100ms)
Be lightweight enough to deploy on existing infra
Remain accurate across a constantly changing image landscape
This set up a tricky tension. Going deep improves accuracy. Going light keeps latency down. The right model had to do both.
Model selection: the case for EfficientNet
They evaluated a bunch of pretrained open-source models. Transformers looked good on paper but they were too heavy for real-time inference. CLIP (Contrastive Language-Image Pre-Training) and other vision-language models had strong generalization but required additional complexity that wasn’t needed for this use case.
EfficientNet stood out. It may not be cutting-edge anymore but it remains one of the best trade-offs in image classification: compact, performant and relatively easy to fine-tune. It also had the added bonus of fitting cleanly into Reddit’s serving stack without major changes.
After benchmarking different variants on internal datasets, EfficientNet gave them the balance they needed. It wasn’t the most sophisticated model they could’ve picked but it was the one that worked in prod.
Training the model at scale (without burning through infra)
Once the model architecture was locked in, training became the next challenge. They built a distributed training pipeline using Ray. We covered Ray’s implementation at Uber in a previous article:
Back to Reddit, Ray let them scale workloads across CPUs and GPUs without rewriting everything from scratch. Ray gave them a flexible training environment that worked across both local and cloud-based clusters. Task scheduling was handled for them. They didn’t need to build custom job orchestration logic or spin up a new training cluster for every experiment.
For hyperparameter tuning, they used Ray Tune to run parallel sweeps across architectural variants. This included depth and block configuration, batch sizes, activation functions and regularization strategies. Freezing and unfreezing layers was part of the mix too depending on the base checkpoint.
The key takeaway here isn’t that they found some magical setting. it’s that the system let them explore quickly, test multiple ideas in parallel and move fast without getting bogged down by infrastructure.
Active learning: keeping the model from going stale
Once the model was trained and running, the team didn’t stop there. Static models go stale fast, especially in high-volume, user-generated environments where the definition of NSFW shifts constantly.
They built in support for active learning, which meant the model could flag low-confidence predictions and ambiguous examples. These samples were reviewed and if useful, incorporated into future training runs.
This feedback loop allowed the model to improve continuously, without full re-training from scratch. It also gave them a safety net: when the model started to drift or new content types emerged, they had a system in place to adapt.
This isn’t groundbreaking technically but it’s crucial operationally. Reddit’s image landscape changes frequently and having a model that works within a shifting environment is important.
Serving the model in real-time (and fast enough to matter)
Training a performant model is one thing. Serving it in real-time during image uploads is a different problem entirely.
At Reddit’s scale, this model couldn’t be treated like a batch job. It had to run in the critical path. That meant tight latency guarantees, high availability and horizontal scalability.
The media classification pipeline works like this:
A user uploads an image
The image enters an input queue
Reddit’s internal GIS (Gazette Inference Service) pulls it down, runs preprocessing and performs inference
The result is sent downstream to mod tooling or tagging systems

To benchmark latency, the team started with a CPU-only deployment. This gave them a baseline of how long requests were taking, looking specifically at the median (p50), slower (p90), and worst-case (p99) response times.
The results weren’t surprising: the main delays came from downloading images (which is limited by network and disk speed) and running model inference on CPUs (which just isn’t fast enough for deep learning). Time to bring in GPUs.
Moving to GPU inference (and doing it right)
Reddit was already using Ray Serve to run models on GPUs so the team moved the NSFW classifier over to that setup. The migration went smoothly; they kept the logic exactly the same as the CPU version but swapped in GPU support using TensorFlow.
Just doing that shaved off a good chunk of latency. But they ran into another issue: the GPU was doing everything; from downloading images and preprocessing to running inference. That meant GPU resources were tied up on tasks that didn’t need them.
To fix that, the team split deployments:
1- One Ray deployment handled CPU workloads like request handling, image downloads and preprocessing.
2- A separate Ray deployment handled inference only. Ray’s resource isolation made this easy to configure and enforce.
With the split in place, GPU utilization improved dramatically. Inference was faster and the whole pipeline became more predictable under load.
Tuning the CPU side to handle spikes
Even with GPU offloading, the CPU side needed to be tuned for throughput, especially during traffic spikes.
The team used Ray’s Actor model to run multiple replicas per pod. Each Actor handled requests independently and they could scale the number of actors via a simple parameter (num_replicas
). This increased concurrency and helped the system stay responsive under load.
They also optimized image downloads. Originally, images were downloaded one at a time, synchronously. That became a bottleneck, especially when traffic spiked. They rewrote the download logic using asynchronous APIs, allowing multiple concurrent downloads per process.
So what were the results for all of this work?
The impact of optimizations
After all the infra work mentioned before: GPU migration, CPU/GPU workload separation, parallelization and async downloads—the gains were clear:
p50 (median) latency improved by 11x
p90 (slower) and p99 (worst-case) latency improved by 4x
The model now runs real-time during uploads. It flags explicit content in real time and supports Reddit’s broader moderation infrastructure without slowing down the user experience.
Lessons Learned
Accuracy is secondary to deployability. A slightly less accurate model that runs fast and scales is the real win.
Infra tuning matters more than model tweaks. Latency gains came from serving optimizations, not fancy architectures.
Active learning keeps models relevant. Static classifiers decay fast when user content changes frequently.
Simplicity scales. A well-scoped model plus clean infra beats complexity every time.
The Full Scoop
To learn more about the implementation, check Reddit posts on this topic here and here
If you liked this article, please give it a like and share it with others (and I will pray to the data gods for your health :)
Keep learning
How Walmart Automated 400+ Forecasts and Cut Runtime by Half
Learn how their Autotuning Framework slashed errors, halved processing time and scaled across hundreds of time series without manual tuning.
Inside the Mind of an LLM
If you ever wondered what goes on inside the "mind" of an LLM, this article is for you.
We look at how "Claude" thinks and why sometimes it wants to add chocolate to soups!