Diffusers documentation

FLUX 3 Image Klein

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.42.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

FLUX 3 Image Klein

FLUX 3 Image Klein est un modèle de génération d’images de 5,6 Md de paramètres de Black Forest Labs : un seul transformeur (Flux3Transformer2DModel) génère des images à partir d’un prompt texte, seul ou accompagné d’une ou plusieurs images de référence. Le conditionnement par le texte provient de Qwen3-VL, et les images utilisent l’autoencodeur compact FLUX.2 (AutoencoderKLFlux2).

FLUX 3 Image Klein est livré sous forme de pipeline modulaire. Commencez par une génération d'image à partir de texte :

import torch
from diffusers import ModularPipeline

pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein-base")
pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16})
pipe.to("cuda")

image = pipe(
    prompt="A red fox in a snow-covered forest clearing at golden hour",
    generator=torch.Generator().manual_seed(0),
    output="images",
)[0]
image.save("fox.png")

prompt est la seule entrée obligatoire : le pipeline applique les paramètres d’échantillonnage recommandés (50 étapes, échelle de guidage 3,5, décalage du planificateur 6,93) et génère par défaut des images de 1024 x 1024 pixels. Il accepte un seul prompt par appel ; utilisez num_images_per_prompt pour obtenir plusieurs images à partir du même prompt.

Comme vous pouvez le voir dans l’exemple ci-dessus, le chargement et l’exécution diffèrent légèrement d’un DiffusionPipeline. Le chargement est différé : from_pretrained() ne construit que le pipeline, et vous appelez load_components() pour charger les poids. Un appel renvoie l’état du pipeline, et vous demandez ce dont vous avez besoin avec output : output="images" ici, ou output=["images", "prompt"] pour obtenir également la légende écrite par l’amplificateur de prompt.

Si vous travaillez avec ModularPipeline pour la première fois, lisez le tutoriel de démarrage rapide, et Passage de DiffusionPipeline si vous connaissez les pipelines standard. Outre le chargement et les sorties, quelques autres éléments diffèrent : guidance, callbacks et offloading.

Échantillonnage supérieur des prompts

Prompting matters: FLUX 3 Image Klein takes full scene captions that describe the subject, composition, lighting, and atmosphere, not short prompts; a request sent as typed gives generic output. That is why the pipeline runs the prompt upsampler first on every request, text-to-image and editing alike. It is a separate 9B model from Black Forest Labs (black-forest-labs/flux-3-image-klein-pup) that expands prompt into such a caption before the text encoder, and a short prompt is all it takes.

L’upsampler de prompt transforme une demande brève, accompagnée des images de référence lors d’une édition, en la légende complète de la scène que le modèle reçoit : médium et style, scène, éclairage et caméra, chaque sujet avec ses couleurs, ses matériaux et sa position dans le cadre. Un seul modèle couvre le texte-vers-image, l’édition avec images de référence et la mise en page avec des boîtes. La légende qu’il génère est renvoyée en tant que sortie prompt. Dans l’exemple ci-dessus, demandez-la en même temps que les images pour voir ce que le modèle d’image a reçu :

out = pipe(
    prompt="A red fox in a snow-covered forest clearing at golden hour",
    generator=torch.Generator().manual_seed(0),
    output=["images", "prompt"],
)
print(out["prompt"])
Full-color outdoor wildlife photograph taken from a low eye-level perspective in a snow-covered forest clearing at golden hour. In the center foreground, a red fox stands in profile facing left, its body turned toward the viewer and its head raised as it looks across the clearing. The fox has dense rust-red winter fur, a white throat and chest, black lower legs, pointed ears with dark tips, and a long bushy tail ending in white. ...

La légende complète compte environ 240 mots ; max_new_tokens limite sa longueur (3584 par défaut).

Vous pouvez afficher le doc de l'étape pour voir ce qu'elle prend et ce qu'elle écrit :

print(pipe.blocks.sub_blocks["prompt_upsampler"].doc)
  Inputs:
      prompt (`str`):
          The edit instruction to expand into the full caption (one per call).
      image (`Image | list`, *optional*):
          The reference image(s) of the edit, at most 10; the same ones the pipeline receives.
      boxes (`list`, *optional*):
          Layout boxes, each {'id', 'desc', 'from', 'src_bbox', 'tgt_bbox'} with the boxes as [y_min, x_min, y_max, x_max] in
          0-1: ...
      max_new_tokens (`int`, *optional*, defaults to 3584):
          The most tokens the upsampler may generate for the caption.

  Outputs:
      prompt (`str`):
          The full caption, replacing the request.

Comme les autres blocs du pipeline, le bloc prompt_upsampler s'exécute également de manière autonome. La méthode recommandée consiste à l'extraire des blocs : le bloc extrait devient un upsampler autonome, et les blocs restants forment un pipeline qui prend la légende étendue en tant que prompt :

blocks = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein-base").blocks
pup_block = blocks.sub_blocks.pop("prompt_upsampler")

pup = pup_block.init_pipeline("black-forest-labs/flux-3-image-klein-base")
pup.load_components(dtype=torch.bfloat16)
pup.to("cuda")
caption = pup(prompt="A red fox in a snow-covered forest clearing at golden hour", output="prompt")

pipe = blocks.init_pipeline("black-forest-labs/flux-3-image-klein-base")
pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16})
pipe.to("cuda")
image = pipe(prompt=caption, generator=torch.Generator().manual_seed(0), output="images")[0]

De cette manière, il peut être servi comme une étape distincte, sur son propre GPU ou derrière son propre endpoint, et une interface utilisateur peut afficher la légende qu’il a rédigée et permettre à l’utilisateur de la modifier avant la génération. Tout autre LLM peut remplacer l’upsampler, avec les mêmes prompts système : fournissez-lui system_prompts["t2i"] du dépôt de l’upsampler (ou "i2i", avec les références en premier dans le message utilisateur) en tant que prompt système et la requête en tant que message utilisateur, puis transmettez la légende qu’il renvoie en tant que prompt à pipe.

Image vers image

Passez une ou plusieurs images de référence avec image. Une référence unique est conservée entière : elle est réduite uniquement au-delà de 4 mégapixels, rembourrée sur la grille de 16 pixels, et la génération adopte par défaut sa taille, de sorte que l’édition est restituée à la taille de l’entrée. Plusieurs références sont chacune ajustées sous 1 mégapixel et recadrées sur la grille, et la génération adopte par défaut la taille de la première. Une height/width donnée prime sur la valeur par défaut. Décrivez l’image cible comme une légende complète de la référence intégrant votre modification, plutôt que comme une instruction en une ligne, ou laissez le prompt upsampler la rédiger à partir de l’instruction et des références :

night = pipe(
    prompt="the same scene at night under a full moon",
    image=image,
    generator=torch.Generator().manual_seed(0),
    output=["images", "prompt"],
)

Lorsque plusieurs références sont fournies, désignez-les comme la première image, la deuxième image, … dans l’ordre où elles sont transmises à image ; la légende les nomme <ref_image_0>, <ref_image_1>, … pour le modèle d’images :

combined = pipe(
    prompt="the fox from the first image sitting on the wooden bench from the second image",
    image=[image, bench],
    height=768,
    width=1360,
    output="images",
)[0]

Passez height et/ou width pour remplacer la taille dérivée.

Contrôle de la mise en page

Pour placer explicitement le contenu, passez boxes : pour chaque élément, un id, une courte desc et un bbox de [y_min, x_min, y_max, x_max] dans l'intervalle 0-1 (origine en haut à gauche), afin d'être indépendant de la taille de sortie. L'upsampler écrit la légende autour d'eux, en citant chaque élément sous la forme <id>, et ajoute le tableau des boîtes, les vôtres étant conservées à l'identique :

boxes = [
    {"id": "balloon_1", "desc": "a red and yellow striped hot-air balloon", "bbox": [0.08, 0.06, 0.48, 0.38]},
    {"id": "balloon_2", "desc": "a small blue hot-air balloon", "bbox": [0.20, 0.62, 0.42, 0.80]},
]
out = pipe(prompt="hot-air balloons over a green valley at sunrise", boxes=boxes, output=["images", "prompt"])

Lors de l’édition, une boîte indique sa source avec from : "target" avec un tgt_bbox ajoute un nouvel élément à cet endroit, "ref_image_0" avec un src_bbox et un tgt_bbox déplace un élément de cette référence (des boîtes identiques le laissent en place) :

boxes = [{"id": "cactus", "desc": "a small potted cactus", "from": "target", "src_bbox": None, "tgt_bbox": [0.55, 0.62, 0.85, 0.85]}]
out = pipe(prompt="add a small potted cactus in the marked spot", image=mug, boxes=boxes, output=["images", "prompt"])

boxes = [{"id": "knight", "desc": "the knight figure", "from": "ref_image_0", "src_bbox": [0.50, 0.15, 0.85, 0.35], "tgt_bbox": [0.19, 0.06, 0.54, 0.26]}]
out = pipe(prompt="move the knight up and to the left, keep the rest unchanged", image=knight, boxes=boxes, output=["images", "prompt"])

La légende que le modèle d’image lit contient les éléments balisés <id> dans le texte et le tableau ajouté en JSON, avec les boîtes sur un canevas de 0 à 1000 ; affichez la sortie de prompt pour la voir. Une légende écrite à la main dans ce format est transmise au pipeline sans cette étape, comme dans Prompt upsampling.

Échelle de guidage

Le pipeline applique le guidage sans classifieur via son composant guider et n'accepte pas d'argument guidance_scale lors de l'appel. Le guider par défaut est ClassifierFreeGuidance avec guidance_scale=3.5, le paramétrage recommandé pour les deux flux de travail. Pour le modifier, créez une copie mise à jour du guider :

pipe.guider = pipe.guider.new(guidance_scale=5.0)

Consultez le guide des guides pour passer à une autre méthode de guidage.

Modèle distillé par étapes

Le point de contrôle distillé par étapes génère en 16 étapes sans guidage sans classifieur. Son transformeur est une carte de flux : à chaque étape, il reçoit l'instantané temporel actuel et celui vers lequel l'étape saute. Il se charge comme le modèle de base (le modular_model_index.json du dépôt se résout en Flux3DistilledModularPipeline et Flux3DistilledAutoBlocks) ; le pipeline n'a pas de guider et n'accepte pas negative_prompt :

pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein")
pipe.load_components(dtype=torch.bfloat16)
pipe.to("cuda")

image = pipe(prompt=prompt, generator=torch.Generator().manual_seed(0), output="images")[0]

Il prend en charge les deux mêmes flux de travail que le modèle de base. num_inference_steps est défini sur 16 par défaut ; le modèle est entraîné pour 4 à 16 étapes.

Génération reproductible

Un generator fixe le bruit que le pipeline tire (voir Reproductibilité). Pour réutiliser un bruit tiré ailleurs, passez-le en tant que latents avec la forme (batch_size, 128, height // 16, width // 16). Par exemple, l'implémentation BFL tire son bruit sous forme de jetons d'image sur le CPU et le convertit en bf16 ; cela reproduit son échantillon pour une graine donnée :

height, width = 1024, 1024
h, w = height // 16, width // 16

noise = torch.randn((h * w, 128), generator=torch.Generator().manual_seed(0)).to(torch.bfloat16)
latents = noise.T.reshape(1, 128, h, w)

image = pipe(prompt=prompt, latents=latents, height=height, width=width, output="images")[0]

Flux de travail

Le pipeline sert à deux flux de travail, text2image et image_conditioned ; celui qui s’exécute est sélectionné par les entrées (image sélectionne image-to-image). from_pretrained() peut également construire un pipeline pour un seul flux de travail avec l’argument workflow, qui n’attend alors que les composants nécessaires à ce flux de travail et ne charge que ceux-ci :

from diffusers.modular_pipelines import Flux3AutoBlocks

print(Flux3AutoBlocks().available_workflows)
# ["text2image", "image_conditioned"]

t2i = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein-base", workflow="text2image")
print(t2i.blocks.doc)

Callbacks par étape

Pour utiliser une fonction de rappel existante, insérez un bloc dans la boucle de débruitage. Le guide des rappels propose un exemple simple avec le bloc CallbackOnStepEnd et l'assistant walk_denoise_loops :

blocks = Flux3AutoBlocks()  # or Flux3DistilledAutoBlocks
for loop in walk_denoise_loops(blocks):
    loop.sub_blocks["callback"] = CallbackOnStepEnd()
pipe = blocks.init_pipeline("black-forest-labs/flux-3-image-klein-base")

image = pipe(prompt=caption, callback_on_step_end=my_callback, output="images")[0]

Pour arrêter prématurément, déclenchez une exception depuis le rappel ; il n'existe pas de drapeau interrupt.

Mémoire

Charger l’ensemble en bf16 représente environ 39 Go de poids : le transformer compte 5,5 Md de paramètres, l’encodeur de texte Qwen3-VL-4B en compte 4 Md et le prompt upsampler 9 Md (environ 19 Go à lui seul), tandis que le VAE d’images est en fp32. Un pipeline construit sans l’étape de l’upsampler (voir là) charge environ 20 Go. Pour les GPU disposant de moins de mémoire, activez le déchargement des composants ; le gestionnaire déplace les composants entre le CPU et le GPU au fur et à mesure des besoins de chaque étape, l’upsampler inclus :

from diffusers import ComponentsManager, ModularPipeline

manager = ComponentsManager()
pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein-base", components_manager=manager)
pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16})
manager.enable_auto_cpu_offload(device="cuda")

Chargement de fichier unique

Flux3Transformer2DModel charge le transformer directement depuis un package de modèle Black Forest Labs avec from_single_file(). Le fichier config.json du package n’est pas lu ; passez la configuration diffusers via config et subfolder.

import torch
from diffusers import Flux3Transformer2DModel, ModularPipeline

transformer = Flux3Transformer2DModel.from_single_file(
    "https://huggingface.co/black-forest-labs/flux-3-image-klein-base/blob/main/model.safetensors",
    config="black-forest-labs/flux-3-image-klein-base",
    subfolder="transformer",
    dtype=torch.bfloat16,
)

pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein-base")
pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16})
pipe.update_components(transformer=transformer)
pipe.to("cuda")

Chargez d'abord les autres composants par leur nom : load_components() sans names chargerait également le transformateur depuis le dépôt du pipeline.

Le chargement des points de contrôle monofichier FP8r et NVFP4 est également pris en charge. Les deux se chargent via le même appel avec une BFLQuantizationConfig. Consultez le guide de quantification BFL pour connaître les exigences de noyau de chaque schéma.

from diffusers import BFLQuantizationConfig

transformer = Flux3Transformer2DModel.from_single_file(
    "https://huggingface.co/black-forest-labs/flux-3-image-klein-base/blob/main/variants/nvfp4/model.safetensors",
    config="black-forest-labs/flux-3-image-klein-base",
    subfolder="transformer",
    quantization_config=BFLQuantizationConfig(compute_dtype=torch.bfloat16),
    dtype=torch.bfloat16,
)

Lors du chargement des points de contrôle distillés par étape, nous avons besoin de leurs configurations (flow_map=True) :

transformer = Flux3Transformer2DModel.from_single_file(
    "https://huggingface.co/black-forest-labs/flux-3-image-klein/blob/main/variants/nvfp4/model.safetensors",
    config="black-forest-labs/flux-3-image-klein",
    subfolder="transformer",
    quantization_config=BFLQuantizationConfig(compute_dtype=torch.bfloat16),
    dtype=torch.bfloat16,
)

Flux3ModularPipeline

classe diffusers.Flux3ModularPipeline

< >

( blocks: diffusers.modular_pipelines.modular_pipeline.ModularPipelineBlocks | None = Nonepretrained_model_name_or_path: str | os.PathLike | None = Nonecomponents_manager: diffusers.modular_pipelines.components_manager.ComponentsManager | None = Nonecollection: str | None = Noneworkflow: str | None = Nonemodular_config_dict: dict[str, typing.Any] | None = Noneconfig_dict: dict[str, typing.Any] | None = None**kwargs )

Un ModularPipeline pour FLUX.3.

Flux3AutoBlocks

classe diffusers.Flux3AutoBlocks

< >

( )

Auto des blocs qui effectuent la génération texte-vers-image et image-vers-image à l'aide de FLUX.3. prompt passe d'abord par l'amplificateur de prompt ; supprimez l'étape prompt_upsampler pour l'encoder tel quel.

Workflows pris en charge :

  • text2image : nécessite prompt
  • image_conditioned : nécessite prompt, image

Composants : prompt_upsampler (Qwen3_5ForConditionalGeneration) : le prompt upsampler. prompt_upsampler_processor (AutoProcessor) : son processeur. text_encoder (Qwen3VLForConditionalGeneration) tokenizer (AutoProcessor) image_processor (Flux3ImageProcessor) vae (AutoencoderKLFlux2) transformer (Flux3Transformer2DModel) scheduler (FlowMatchEulerDiscreteScheduler) guider (ClassifierFreeGuidance)

Entrées : prompt (str) : L’instruction de modification à développer en légende complète (une par appel). image (Image | list, facultatif) : L’image de référence de la modification, au maximum 10 ; les mêmes que celles reçues par le pipeline. boxes (list, facultatif) : Boîtes de mise en page, chacune {‘id’, ‘desc’, ‘from’, ‘src_bbox’, ‘tgt_bbox’} avec les boîtes sous la forme [y_min, x_min, y_max, x_max] dans l’intervalle 0-1 : ‘from’ : ‘target’ avec tgt_bbox ajoute un élément à cet endroit, ‘from’ : ‘ref_image_0’, … avec src_bbox et tgt_bbox en déplace un depuis une image de référence. max_new_tokens (int, facultatif, par défaut 3584) : Le nombre maximal de jetons que l’upsampler peut générer pour la légende. negative_prompt (str, facultatif) : Le prompt à ne pas utiliser pour guider la génération max_sequence_length (int, facultatif, par défaut 8192) : Nombre maximal de jetons du prompt ; les prompts plus longs sont tronqués. text_encoder_out_layers (tuple, facultatif, par défaut (4, 8, 12, 16, 20, 24, 28, 32)) : Indices des couches cachées du codeur de texte dont les états sont empilés pour former les embeddings de texte num_images_per_prompt (int, facultatif, par défaut 1) : Le nombre d’images à générer par prompt. image_cond_latents (list, facultatif) : Latents normalisés et empilés des images de référence, un tenseur de forme (B, C, height_i//16, width_i//16) par image height (int, facultatif, par défaut None ou 1024, selon le flux de travail) : La hauteur en pixels de l’image générée ; par défaut, la hauteur propre de la première image de référence width (int, facultatif, par défaut None ou 1024, selon le flux de travail) : La largeur en pixels de l’image générée ; par défaut, la largeur propre de la première image de référence reference_crop (list | NoneType, facultatif) : Pour chaque référence, la boîte de la référence à l’intérieur de sa toile rembourrée ou None, issue de l’étape de prétraitement latents (Tensor) : Latents bruités pré-générés pour la génération d’images. generator (Generator, facultatif) : Générateur Torch pour une génération déterministe. num_inference_steps (int) : Le nombre d’étapes de débruitage. timesteps (Tensor) : Pas de temps pour le processus de débruitage. (remplace num_inference_steps s’il est fourni) sigmas (list, facultatif) : Sigmas personnalisés pour le processus de débruitage. (remplace num_inference_steps s’il est fourni) *denoiser_input_fields (None,facultatif) : entrées conditionnelles du modèle pour le débruiteur : par exemple prompt_embeds, negative_prompt_embeds, etc. attention_kwargs (dict,facultatif) : Kwargs supplémentaires pour les processeurs d’attention. output_type (str,facultatif*, par défaut pil) : Format de sortie : ‘pil’, ‘np’, ‘pt’.

Sorties : images (list) : Images générées. latents (Tensor) : Latents débruités.

Flux3DistilledAutoBlocks

classe diffusers.Flux3DistilledAutoBlocks

< >

( )

Auto qui effectuent la génération texte-vers-image et image-vers-image à l'aide du modèle FLUX.3 distillé par étapes (16 étapes, sans guidance sans classifieur). prompt passe d'abord par l'amplificateur de prompt ; supprimez l'étape prompt_upsampler pour l'encoder tel quel.

Workflows pris en charge :

  • text2image : nécessite prompt
  • image_conditioned : nécessite prompt, image

Composants : prompt_upsampler (Qwen3_5ForConditionalGeneration) : le prompt upsampler. prompt_upsampler_processor (AutoProcessor) : son processeur. text_encoder (Qwen3VLForConditionalGeneration) tokenizer (AutoProcessor) image_processor (Flux3ImageProcessor) vae (AutoencoderKLFlux2) transformer (Flux3Transformer2DModel) scheduler (FlowMatchEulerDiscreteScheduler)

Entrées : prompt (str) : L’instruction de modification à développer en légende complète (une par appel). image (Image | list, facultatif) : L’image de référence de la modification, au maximum 10 ; les mêmes que celles reçues par le pipeline. boxes (list, facultatif) : Boîtes de mise en page, chacune {‘id’, ‘desc’, ‘from’, ‘src_bbox’, ‘tgt_bbox’} avec les boîtes sous la forme [y_min, x_min, y_max, x_max] dans l’intervalle 0-1 : ‘from’ : ‘target’ avec tgt_bbox ajoute un élément à cet endroit, ‘from’ : ‘ref_image_0’, … avec src_bbox et tgt_bbox en déplace un depuis une image de référence. max_new_tokens (int, facultatif, par défaut 3584) : Le nombre maximal de jetons que l’upsampler peut générer pour la légende. max_sequence_length (int, facultatif, par défaut 8192) : Nombre maximal de jetons du prompt ; les prompts plus longs sont tronqués. text_encoder_out_layers (tuple, facultatif, par défaut (4, 8, 12, 16, 20, 24, 28, 32)) : Indices des couches cachées du text-encoder dont les états sont empilés pour former les embeddings de texte num_images_per_prompt (int, facultatif, par défaut 1) : Le nombre d’images à générer par prompt. image_cond_latents (list, facultatif) : Latents normalisés et empilés des images de référence, un tenseur de forme (B, C, height_i//16, width_i//16) par image height (int, facultatif, par défaut None ou 1024, selon le workflow) : La hauteur en pixels de l’image générée ; par défaut, la hauteur propre de la première image de référence width (int, facultatif, par défaut None ou 1024, selon le workflow) : La largeur en pixels de l’image générée ; par défaut, la largeur propre de la première image de référence reference_crop (list | NoneType, facultatif) : Pour chaque référence, la boîte de la référence à l’intérieur de sa toile rembourrée ou None, issue de l’étape de prétraitement latents (Tensor) : Latents bruités pré-générés pour la génération d’image. generator (Generator, facultatif) : Générateur Torch pour une génération déterministe. num_inference_steps (int) : Le nombre d’étapes de débruitage. timesteps (Tensor) : Timesteps pour le processus de débruitage. (remplace num_inference_steps s’il est fourni) sigmas (list, facultatif) : Sigmas personnalisés pour le processus de débruitage. (remplace num_inference_steps s’il est fourni) *denoiser_input_fields (None,facultatif) : entrées conditionnelles du modèle pour le débruiteur : par exemple prompt_embeds, negative_prompt_embeds, etc. output_type (str,facultatif*, par défaut pil) : Format de sortie : ‘pil’, ‘np’, ‘pt’.

Sorties : images (list) : Images générées. latents (Tensor) : Latents débruités.

Flux3DistilledModularPipeline

classe diffusers.Flux3DistilledModularPipeline

< >

( blocks: diffusers.modular_pipelines.modular_pipeline.ModularPipelineBlocks | None = Nonepretrained_model_name_or_path: str | os.PathLike | None = Nonecomponents_manager: diffusers.modular_pipelines.components_manager.ComponentsManager | None = Nonecollection: str | None = Noneworkflow: str | None = Nonemodular_config_dict: dict[str, typing.Any] | None = Noneconfig_dict: dict[str, typing.Any] | None = None**kwargs )

Un ModularPipeline pour le modèle FLUX.3 distillé par étapes (16 étapes, sans guidance sans classifieur).

Update on GitHub