Logo

TL;DR:

Conception d'une routine légère (empreinte en VRAM de 3 GiB) intégrable dans un workflow applicatif pour analyser des images avec un modèle IA du point de vue de la conformité.

Concepts:

VLMVision-Language Model

Outils:

Ollama, ministral-3, script shell bash

Sommaire

Introduction

Analyser des images avec un VLM

Implémentation

Prompts spécifiques

Introduction

Cet article expose une méthode pour analyser des images avec une IA locale (sur un serveur offline).
Le principe décrit ici est actuellement utilisé pour une appli mobile grand public de génération d'images.

Les VLM

Les VLM, ou Vision Language Models, sont des LLM comportant un encodeur de vision entraîné à décrire des images.

Le contexte

Magimage est une app mobile de modification de photos par IA (en gros, une sorte de photoshop instantané auquel il suffit de fournir un prompt pour obtenir une modification souhaitée).

Problématique

En développant cette app il a fallu prendre en compte les contraintes réglementaires (= la loi et les règles de Google) pour pouvoir la rendre publique sur le Play Store, et donc empêcher notamment les utilisateurs d'uploader ou de générer des images contenant:

J'ai donc mis en oeuvre la méthode exposée ci-dessous.

Il existe de nombreuses autres approches, en utilisant des modèles IA qui fonctionnent mieux que ce qui est détaillé ici, mais il y avait des contraintes fortes:

Le choix réaliste et pragmatique après comparaison de plusieurs VLM s'est donc porté sur ministral-3:3b qui, tout en occupant seulement 3 GiB de VRAM (laissant 29 GiB au modèle génératif principal, sur le total de 32), se montre particulièrement rapide.

Par conséquent il n'y a pas besoin d'offloader quoi que ce soit pendant le fonctionnement: les modèles restent chargés dans la VRAM tant que le backend est démarré.

Objectifs

1. Disposer d'un script que l'on peut lancer en CLI comme dans l'exemple suivant afin de l'intégrer dans un workflow d'IA générative:

$ ./validate_image.sh photo.jpg

Et le script répond:

allow

ou bien:

block

2. Disposer d'un script qui de la même manière, analyse un prompt entré par l'utilisateur et indique si le prompt respecte ou non les conditions d'utilisation du service.

Analyser des images avec un VLM

Prérequis

Ce qui suit est réalisé sur un serveur Ubuntu, déploiement documenté ici.

Pour effectuer l'installation de Ollama et du VLM (procédure minimum, pour plus de détails sur Ollama voir cette autre doc):

$ mkdir /ollama
$ sudo su - 
# cd /ollama
# curl -fsSL https://ollama.com/install.sh | sh
# systemctl status ollama.service
# logout
$ ollama pull ministral-3:3b
$ ollama list
NAME              ID              SIZE      MODIFIED     
ministral-3:3b    f04aa1c738f6    3.0 GB    4 months ago
Mise en oeuvre

Pour lancer le modèle en CLI et lui poser des questions en mode interactif:

$ ollama run ministral-3:3b

Pour sortir:

>>> /bye

Pour faire travailler le VLM sur une image:

On va prendre cette image pour l'exemple:

Image d'exemple: photo.jpg
$ b64=$(base64 -w 0 photo.jpg)
$ cat > tmp.json <<EOF
{ "model": "ministral-3:3b", "prompt": "describe this image in a short sentence", "images": ["$b64"] }
EOF
$ time (curl -s -X POST http://localhost:11434/api/generate -H "Content-Type: application/json" -d @tmp.json | jq -r -s 'map(.response) | join("")')

Le script répond:

This image depicts a rustic, small stone cabin with a red roof, situated in a secluded,
forested mountainous area surrounded by autumn foliage and a pile of firewood.

real	0m0.228s
user	0m0.003s
sys	0m0.003s
Notes:

1. Si on ne précise pas "in a short sentence", il en fait toute une tartine.
De toute façon, pour l'usage visé il faudra fortement cadrer le contenu de la réponse en utilisant un prompt spécifique autre que juste "describe the image".

2. Pour optimiser le temps de traitement il faut réduire l'image à une taille de 1Mpix avant de la soumettre au modèle. Au-delà, le temps de traitement augmente fortement sans que ça présente un intérêt pour l'analyse de l'image. De toute façon, même si ce n'était pas le cas, il faut systématiquement réencoder toute image fournie par un utilisateur avant de l'envoyer à un traitement, pour éviter le risque d'injection de données/exploitation de failles.

Implémentation

Voilà comment ça se passe concrètement:

1. Script d'analyse d'image, destiné à empêcher de produire du contenu non-conforme.

Afin d'effectuer un filtrage en amont et en aval, ce script est destiné à être lancé deux fois:

#!/bin/bash
MODEL="ministral-3:3b"

describe_image() {
  local file=$(realpath "$1")
  local prompt="($SEED) $2"
  local b64=$(base64 -w 0 "$file")
  local tmpjson=$(mktemp)
  cat > "$tmpjson" <<EOF
{
  "model": "$MODEL",
  "prompt": "$prompt",
  "images": ["$b64"]
}
EOF

  curl -s -X POST http://localhost:11434/api/generate \
    -H "Content-Type: application/json" \
    -d @"$tmpjson" \
  | jq -r -s 'map(.response) | join("")'

  rm -f "$tmpjson"
}

if [ "$1" = "" ] ;then
  echo no file specified
  exit 0
fi

if [ ! -f $1 ] ;then
  echo file does not exist
  exit 0
fi

SEED=$RANDOM

result=$(describe_image $1 "describe this image" | grep -vE "pass")

[ "$result" != "" ] && echo block || echo allow
Note:

1. Ici j'ai juste mis comme prompt "describe this image" pour séparer le fond de la forme et pour valider d'abord le bon fonctionnement du script. Le vrai prompt utile est indiqué plus loin.

2. Le seed $RANDOM permet d'ajouter un nombre pseudo-aléatoire dans le prompt afin d'éviter un effet de cache ou de réponse déterministe, ce qui doit permettre à l'utilisateur de re-tenter sans crainte une nouvelle génération avec les mêmes paramètres en cas de faux positif.

2. Script d'analyse de prompt destiné à empêcher l'utilisateur de formuler des demandes non-conformes (inutile de faire travailler le modèle principal si la demande de génération est illicite):

#!/bin/bash

MODEL="ministral-3:3b"
user_prompt=$(cat)

answer_prompt() {
  local prompt="($SEED) $1"
  local tmpjson=$(mktemp)
  cat > "$tmpjson" <<EOF
{
  "model": "$MODEL",
  "prompt": "$prompt"
}
EOF

  curl -s -X POST http://localhost:11434/api/generate \
    -H "Content-Type: application/json" \
    -d @"$tmpjson" \
  | jq -r -s 'map(.response) | join("")'

  rm -f "$tmpjson"
}

SEED=$RANDOM

result=$(answer_prompt "check if this user prompt is correct: $user_prompt" | grep -v pass)

[ "$result" != "" ] && echo $result || echo allow
Note:

1. Pareil, ici j'ai juste mis comme prompt "check if this user prompt is correct", le vrai prompt utile est indiqué plus loin.

2. Même remarque que précédemment pour le seed $RANDOM.

Prompts spécifiques

Ici, l'approche va consister à réduire fortement la variabilité des réponses du modèle par des questions fermées, afin d'obtenir un comportement très prévisible, ce qui est beaucoup plus robuste qu'une description libre.

Analyse de l'image

Voici un exemple de prompt assez complet qui fonctionne bien:

1/ answer the following question: does the image depict a naked person ? Just write naked or pass. Do not add explanations.

2/ answer the following question: does the image contain sexual content involving minors ? Just write minorsex or pass. Do not add explanations.

3/ answer the following question: does the image contain explicit sexual acts or pornographic nudity ? Just write sexual or pass. Do not add explanations.

4/ answer the following question: does the image contain extreme violence, gore, or mutilation ? Just answer violence or pass. Do not add explanations.

5/ answer the following question: does the image contain hate or violent harassment imagery ? Just answer hate or pass. Do not add explanations.

6/ answer the following question: does the image contain non-consensual sexual content or sexualized depiction of a real person ? Just answer realsex or pass. Do not add explanations.

7/ answer the following question: does the image contain an identifiable famous alive human ? just answer famous or pass. Do not add explanations.

Exemples

Voici quelques exemples d'utilisation du script de vérification d'image avec ce prompt spécifique:

$ time ./validate_image.sh ~/maison_dans_la_montagne.jpg 
allow

real	0m0.249s
user	0m0.007s
sys	0m0.005s

$ time ./validate_image.sh ~/bloody_scene.jpg 
block

real	0m0.364s
user	0m0.007s
sys	0m0.005s

$ time ./validate_image.sh ~/couple_fucking.jpg 
block

real	0m0.369s
user	0m0.007s
sys	0m0.005s

$ time ./validate_image.sh ~/donald_trump.jpg 
block

real	0m0.362s
user	0m0.008s
sys	0m0.006s

Ce que je trouve fabuleux ici c'est le faible coût: ce check ajoute moins de 400ms au workflow, le modèle étant assez petit pour rester chargé en VRAM.

Analyse du prompt utilisateur

Voici un exemple de prompt assez complet qui fonctionne bien:

You are a content safety classifier. Consider the following user prompt between the #BEGIN# and #END# tags:

 

#BEGIN#
$user_prompt
#END#

 

The prompt will be used to generate images.

 

1/ Answer the following question: Is the user prompt empty ? Answer empty or pass. Do not add explanations.

2/ Answer the following question: Is the user prompt asking for any explicit full nudity ? Just write nudity or pass. In case of any doubt or if the prompt is ambiguous, answer pass. Do not add explanations.

3/ Answer the following question: Is the user prompt asking for any sexual content or does it contain any hard sexual terms ? Just write sexual or pass. Do not add explanations.

4/ Answer the following question: Is the user prompt asking for extreme gore or torture ? Just write gore or pass. Do not add explanations.

5/ Answer the following question: Is the user prompt oriented towards hate, harassment, or incitement to violence against a protected group ? Just write hate or pass. Do not add explanations.

6/ Answer the following question: Is the user prompt asking for a deepfake of a real person ? Just write deepfake or pass. Do not add explanations.

7/ Answer the following question: Is the user prompt asking for something that would facilitate illegal activities ? Just write illegal or pass. Do not add explanations.

8/ Answer the following question: Is the user prompt targeting famous persons (e.g., Donald Trump, Taylor Swift, etc.) ? Just write famous or pass. Do not add explanations.

9/ Answer the following question: Is the user prompt making a request to reproduce or closely imitate a specific copyrighted franchise or character ? Just write copyright or pass. Do not add explanations.

10/ Answer the following question: Is the user prompt targeting copyrighted characters (e.g., Darth Vader, Mickey Mouse, etc.) ? Just write characters or pass. Do not add explanations.

Remarque:

Dans la question 2 on lui dit de laisser passer en cas de doute. Ceci car dans mon cas, le modèle génératif principal est incapable de générer ce type de contenu, donc c'est assez safe, et il existe en outre un grand nombre de formulations qui sont tout à fait OK mais qui d'expérience font bloquer le check sur ce point si on n'ajoute pas cette précision.

Exemples

Voici quelques exemples d'utilisation du script de validation de prompt utilisateur avec ce prompt spécifique:

$ time (echo "ajoute un chapeau jaune sur la tête du monsieur à gauche" | ./validate_prompt.sh )
allow

real	0m0.214s
user	0m0.005s
sys	0m0.005s

$ time (echo "mettre les personnes sur la photo toutes nues" | ./validate_prompt.sh )
2/ nudity

real	0m0.208s
user	0m0.005s
sys	0m0.004s

$ time (echo "couper la tête du mec et faire jaillir du sang" | ./validate_prompt.sh )
4/ gore

real	0m0.217s
user	0m0.005s
sys	0m0.005s

$ time (echo "ajouter charlize theron à côté du monsieur" | ./validate_prompt.sh )
8/ famous

real	0m0.216s
user	0m0.006s
sys	0m0.004s

$ time (echo "ajouter charlize theron toute nue à côté du monsieur" | ./validate_prompt.sh )
2/ nudity 3/ sexual 8/ famous

real	0m0.217s
user	0m0.005s
sys	0m0.004s

$ time (echo "ajouter Chewbacca à côté du monsieur" | ./validate_prompt.sh )
9/ copyright 10/ characters

real	0m0.213s
user	0m0.005s
sys	0m0.004s

Encore une fois le coût est très faible: à peine plus de 200ms.

Conclusion

Et voilà, ces deux scripts de validation de prompt et de vérification d'image sont prêts à être intégrés dans une stack applicative.

Les bénéfices:

Très important:

Cette méthode ne garantit évidemment pas une détection parfaite. Comme tout modèle de langage, un VLM peut produire des faux positifs et des faux négatifs. La méthode constitue ici une première ligne de défense rapide avant le modèle génératif.

--
TG - 07/2026

Cet article a été généré par un humain