← Назад к вопросам
Как реализовано определение соответствия фотографии цензурным требованиям с точки зрения machine learning?
2.7 Senior🔥 41 комментариев
#Другое
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Определение соответствия фотографии цензурным требованиям (NSFW)
Рассмотрю методы и подходы на практике для определения NSFW (Not Safe For Work) контента.
1. Основные подходы
Есть несколько стратегий:
- Готовые API (простой способ)
- Предтренированные модели (быстро, достаточно хорошо)
- Fine-tune модели (точнее, но требует данных)
- Комбинированный подход (надёжнее)
2. Готовые API сервисы
Google Vision API
from google.cloud import vision
def check_nsfw_google(image_url):
client = vision.ImageAnnotatorClient()
image = vision.Image()
image.source.image_uri = image_url
response = client.safe_search_detection(image=image)
safe_search = response.safe_search_annotation
# Likelihood: UNKNOWN (0) -> VERY_LIKELY (5)
result = {
'adult': safe_search.adult, # Взрослый контент
'spoof': safe_search.spoof, # Подделка
'medical': safe_search.medical, # Медицинский
'violence': safe_search.violence, # Насилие
'racy': safe_search.racy # Откровенный
}
# Если adult или racy >= LIKELY (4), то NSFW
is_nsfw = (result['adult'] >= 4 or result['racy'] >= 4)
return result, is_nsfw
AWS Rekognition
import boto3
def check_nsfw_aws(image_url):
rekognition = boto3.client('rekognition')
response = rekognition.detect_moderation_labels(
Image={'S3Object': {'Bucket': 'my-bucket', 'Name': 'image.jpg'}}
)
result = []
for label in response['ModerationLabels']:
result.append({
'name': label['Name'],
'confidence': label['Confidence']
})
# Проверяем высокую уверенность для опасных категорий
is_nsfw = any(
label['confidence'] > 80
for label in result
if label['name'] in ['Explicit Nudity', 'Suggestive']
)
return result, is_nsfw
Плюсы API: простота, точность, постоянные обновления
Минусы: стоимость, задержка сети, privacy (данные отправляются на серверы)
3. Предтренированные модели локально
Yahoo NSFW Detector (ResNet-50)
# Установка
pip install nsfw_model
pip install tensorflow
import nsfw_model
import numpy as np
from PIL import Image
import requests
from io import BytesIO
def check_nsfw_local(image_url):
# Загруженная модель
model = nsfw_model.load_model("inceptionv3")
# Загрузить изображение
response = requests.get(image_url)
img = Image.open(BytesIO(response.content))
img = img.resize((224, 224))
# Предсказание
predictions = model.predict(np.array([img]))[0]
result = {
'drawings': predictions[0], # Рисунки
'hentai': predictions[1], # Хентай
'neutral': predictions[2], # Нейтральный контент
'porn': predictions[3], # Порнография
'sexy': predictions[4] # Сексуальный контент
}
# NSFW если porn > 0.5 или sexy > 0.7
is_nsfw = (result['porn'] > 0.5 or result['sexy'] > 0.7)
return result, is_nsfw
# Использование
result, is_nsfw = check_nsfw_local("https://example.com/image.jpg")
print(f"NSFW: {is_nsfw}")
print(f"Porn confidence: {result['porn']:.2%}")
print(f"Sexy confidence: {result['sexy']:.2%}")
OpenCV + MobileNet
import cv2
import numpy as np
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.preprocessing import image
def check_nsfw_mobilenet(image_path):
# Загруженная модель
model = MobileNetV2(weights='imagenet')
# Подготовка изображения
img = image.load_img(image_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = x / 255.0
# Предсказание
predictions = model.predict(x)
# Анализируем top-1 prediction
# ImageNet классы: 0-999, некоторые связаны с NSFW
# Например: класс 645-649 - топлесс женщины
return predictions
Плюсы: приватность, быстро, бесплатно
Минусы: менее точно, требует GPU для производства
4. Fine-tuning под свои требования
Если встроенные модели не подходят:
from torch import nn, optim
from torchvision import models, transforms
from torch.utils.data import DataLoader, Dataset
class NSFWDataset(Dataset):
def __init__(self, images, labels):
self.images = images
self.labels = labels
self.transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
img = Image.open(self.images[idx])
img = self.transform(img)
label = self.labels[idx]
return img, label
def train_nsfw_model(train_images, train_labels):
# Загруженная модель ResNet50
model = models.resnet50(pretrained=True)
# Заменяем последний слой на бинарный классификатор
model.fc = nn.Linear(2048, 2) # 0 - safe, 1 - nsfw
dataset = NSFWDataset(train_images, train_labels)
loader = DataLoader(dataset, batch_size=32, shuffle=True)
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
model.train()
for epoch in range(10):
for images, labels in loader:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
print(f"Epoch {epoch+1}/10, Loss: {loss.item():.4f}")
return model
def predict_nsfw(model, image_path):
model.eval()
img = Image.open(image_path)
img = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])(img)
with torch.no_grad():
output = model(img.unsqueeze(0))
probabilities = torch.softmax(output, dim=1)
is_nsfw = probabilities[0, 1].item() # Вероятность NSFW
return is_nsfw
5. Комбинированный подход (рекомендуется)
from concurrent.futures import ThreadPoolExecutor
import logging
logger = logging.getLogger(__name__)
class NSFWDetector:
def __init__(self):
self.local_model = nsfw_model.load_model("inceptionv3")
self.google_client = vision.ImageAnnotatorClient()
def detect_nsfw(self, image_url, use_api=True, threshold=0.7):
"""
Определяет NSFW контент используя несколько подходов.
Args:
image_url: URL изображения
use_api: использовать Google API для подтверждения
threshold: порог вероятности для считания NSFW
Returns:
{'is_nsfw': bool, 'confidence': float, 'details': dict}
"""
# 1. Локальная проверка (быстро)
local_result, local_nsfw = self._check_local(image_url)
# 2. Если локально неуверенно, используем API
if use_api and 0.3 < local_result.get('porn', 0) < 0.7:
api_result, api_nsfw = self._check_google_api(image_url)
# Комбинируем результаты
confidence = (local_result.get('porn', 0) +
(api_result.get('adult', 0) / 5.0)) / 2
is_nsfw = confidence > threshold
else:
confidence = local_result.get('porn', 0)
is_nsfw = confidence > threshold
return {
'is_nsfw': is_nsfw,
'confidence': round(confidence, 3),
'details': {
'local': local_result,
'api': api_result if use_api else None
}
}
def _check_local(self, image_url):
try:
response = requests.get(image_url, timeout=5)
img = Image.open(BytesIO(response.content))
img = img.resize((224, 224))
predictions = self.local_model.predict(np.array([img]))[0]
return {
'porn': float(predictions[3]),
'sexy': float(predictions[4])
}, predictions[3] > 0.5
except Exception as e:
logger.error(f"Local check failed: {e}")
return {}, False
def _check_google_api(self, image_url):
try:
image = vision.Image()
image.source.image_uri = image_url
response = self.google_client.safe_search_detection(image=image)
safe = response.safe_search_annotation
return {
'adult': safe.adult,
'racy': safe.racy
}, (safe.adult >= 4 or safe.racy >= 4)
except Exception as e:
logger.error(f"API check failed: {e}")
return {}, False
# Использование
detector = NSFWDetector()
result = detector.detect_nsfw("https://example.com/image.jpg")
if result['is_nsfw']:
print(f"Изображение содержит NSFW контент (уверенность: {result['confidence']:.1%})")
else:
print("Изображение безопасно")
6. Интеграция в веб-приложение
Django модель
from django.db import models
from django.core.validators import URLValidator
import celery
class Photo(models.Model):
url = models.URLField(validators=[URLValidator()])
is_nsfw = models.BooleanField(null=True, default=None)
nsfw_confidence = models.FloatField(null=True)
checked_at = models.DateTimeField(null=True)
class Meta:
indexes = [
models.Index(fields=['is_nsfw'])
]
# Asynchronous проверка
@celery.shared_task
def check_photo_nsfw(photo_id):
photo = Photo.objects.get(id=photo_id)
detector = NSFWDetector()
result = detector.detect_nsfw(photo.url)
photo.is_nsfw = result['is_nsfw']
photo.nsfw_confidence = result['confidence']
photo.checked_at = timezone.now()
photo.save()
7. Метрики и мониторинг
from sklearn.metrics import precision_recall_fscore_support, confusion_matrix
def evaluate_model(y_true, y_pred):
precision, recall, f1, _ = precision_recall_fscore_support(
y_true, y_pred, average='binary'
)
cm = confusion_matrix(y_true, y_pred)
return {
'precision': precision, # Из предсказанных NSFW сколько правильно
'recall': recall, # Из реальных NSFW сколько найдено
'f1': f1, # Средняя гармоническая
'confusion_matrix': cm # TP, FP, TN, FN
}
Рекомендации
- Для быстрого старта: используй Google Vision API или AWS Rekognition
- Для privacy: Yahoo NSFW model локально + fine-tune на свои данные
- Для production: комбинированный подход с асинхронной проверкой
- Ложные срабатывания: используй человеческую модерацию для граничных случаев
- Performance: кешируй результаты проверок (фото не меняется)
NSFW детекция — постоянно развивающаяся область. Регулярно обновляй модели!