AI Infrastructure Perspective: Model Selection Revision + PoC Plan + Deployment Strategy
Overall support for this proposal. From the AI infrastructure side, we’ll supplement implementation-level details and revise the model selection.
1. Model Selection: Recommend Marqo over NudeNet
I previously conducted a detailed comparison in “Cravatar Avatar NSFW Moderation: Model Selection & Deployment Recommendations”, concluding that Marqo/nsfw-image-detection-384 is preferable to NudeNet v3:
| NudeNet v3 | Falconsai | Marqo (Recommended) | |
|---|---|---|---|
| Input resolution | 320×320 | 224×224 | 384×384 |
| Architecture | YOLO-based | ViT-base-patch16-224 | ViT-base-patch16-384 |
| Primary use case | Explicit region detection (bounding box) | General NSFW binary classification | NSFW binary classification, optimized for search/classification scenarios |
| Misclassification on anime/artistic avatars | High | Moderate | Lower — better performance on ambiguous (“gray zone”) cases |
| Maintenance status | Last updated long ago | Actively maintained | Actively maintained |
Core rationale for choosing Marqo:
- The 384×384 input resolution aligns better with avatar use cases: downsampling from 512×512 incurs minimal quality loss; upscaling from small sizes (e.g., 80×80) preserves more detail.
- NudeNet is a detection model (outputs bounding boxes + explicit-region classification), whereas our use case only requires binary classification (safe/unsafe). A dedicated classifier is simpler and more appropriate.
- Marqo’s team has specifically optimized their model for image search and classification tasks — resulting in lower misclassification rates on edge cases.
- Deployment complexity is identical: just swap the model name.
Hardware requirements are also lighter: FP16 model weights ~172 MB; peak memory usage (including activations) ~500–800 MB. With ONNX Runtime instead of PyTorch, total memory can be further reduced to ~800 MB, while inference speed improves 2–3×.
2. I’ll Run the PoC
Plan:
- Sample 2,000 avatars from Cravatar’s existing cache, stratified by access frequency: high-frequency (500), medium-frequency (500), long-tail (500), plus known edge-case images (500).
- Run all three models — Marqo, Falconsai, and NudeNet v3 — on the same dataset to let data speak for itself.
- Record per-image confidence score, predicted label, and inference latency.
- Produce a comparative report covering: accuracy, recall, confidence distribution, gray-zone ratio, and average inference speed.
- Pay special attention to the categories highlighted by @wenpai: anime/2D avatars, pixel-art avatars, text-only avatars, and low-resolution blurry images.
This PoC also resolves threshold calibration: real-world confidence distributions will inform rational gray-zone boundaries (e.g., safe/unsafe thresholds), rather than arbitrary guesses like 0.3–0.95.
The ailab environment (64 GB RAM + CPU) handles this scale effortlessly — results expected within 1–2 hours. I’ll need @wenpai to either provide the sampled dataset or grant me access to the cache directory.
3. Deployment Strategy: Reuse Vector API’s Architecture
A robust FastAPI + containerized service pattern is already running stably on ailab (e.g., the Vector API has been live for months). We’ll reuse the same architecture for the NSFW detector:
nsfw-detector/
├── Dockerfile
├── app/
│ ├── main.py # FastAPI entry point
│ ├── detector.py # Model loading & inference logic
│ └── preprocessor.py # Image preprocessing pipeline
└── models/ # Model files (downloaded at build time — not committed to git)
API design:
POST /detect
Content-Type: multipart/form-data
Body: file=<image>
Response:
{
"safe": true,
"confidence": 0.97,
"model": "marqo-nsfw-384",
"inference_ms": 45,
"preprocessed_size": "384x384"
}
POST /detect/batch // For bulk scanning
Content-Type: application/json
Body: {"urls": ["https://...", ...]}
Container resource limits: 2 CPUs + 4 GB RAM. Managed via systemd --user, with auto-start on boot.
4. Image Preprocessing Pipeline
Critical to detection accuracy — fully encapsulated inside the service; callers need not handle it:
- Format normalization: Convert all inputs (WebP/JPEG/GIF) to RGB PNG.
- Upscaling: Resize to 384×384 (Marqo’s required input size) using Lanczos interpolation.
- GIF handling: Extract first/middle/last frames; if any frame is unsafe, the entire GIF is flagged unsafe.
- Alpha channel handling: For PNGs with transparency, composite onto white background before inference.
- Fallback handling: Corrupted files, zero-byte inputs, or non-image formats → flag as “review” and route to human moderation queue.
5. Deployment Location
Agree with @wenpai: avoid co-locating with the PHP origin server. Two options:
- Option A: Dedicated container in the same data center as the origin — internal network call latency < 1 ms. Ideal for synchronous checks (e.g., before writing to R2).
- Option B: Deployed on ailab — public API latency ~50–200 ms. Suitable for asynchronous checks.
We’ll initially deploy the PoC on ailab. Once model performance is validated, we’ll finalize production deployment location.
6. Additional Suggestion: Vector Similarity for NSFW Deduplication
ailab already hosts semantic search infrastructure (ChromaDB + bge-m3). We can extract embedding vectors from confirmed unsafe avatars and build an NSFW vector database. When new avatars are uploaded, perform a similarity search against this database: any match above a cosine similarity threshold (e.g., > 0.95) is automatically flagged unsafe — bypassing full model inference.
This is especially effective against repeated uploads of the same NSFW image by different users. Prioritize this after the three-layer architecture (preprocessing → inference → post-processing) is production-ready.
Summary: Switch model recommendation from NudeNet to Marqo/nsfw-image-detection-384. I’ll run the PoC — just share the stratified sample data.