Tous les produits
Search
Centre de documentation

Tair (Redis® OSS-Compatible):Construire un système de recherche image-texte multimodal avec TairVector

Dernière mise à jour :Aug 08, 2026

Ce tutoriel explique comment construire un système de recherche multimodale à l'aide de TairVector et de Chinese-CLIP. Une fois ce tutoriel terminé, votre système pourra accepter une requête textuelle pour renvoyer les images correspondantes, ou recevoir une image pour retourner les descriptions textuelles associées.

Fonctionnement

La recherche multimodale est possible car CLIP encode à la fois les images et le texte dans un même espace vectoriel. Étant donné que les embeddings partagent un système de coordonnées commun, il est possible de mesurer directement la distance entre une requête textuelle et n'importe quelle image, sans avoir à traduire une modalité vers l'autre. Une requête telle que « un chien qui court » se situera à proximité des images de chiens en mouvement, même si ces images n'ont jamais été étiquetées avec cette expression exacte.

Chinese-CLIP est un modèle CLIP open source développé par DAMO Academy, basé sur les architectures Text Transformer et ResNet. Ce tutoriel utilise la variante RN50, qui génère des embeddings de 1 024 dimensions.

TairVector stocke ces embeddings et récupère les voisins les plus proches grâce à un algorithme de recherche des plus proches voisins approximatifs (ANN), soutenu par l'indexation HNSW. Pour en savoir plus sur TairVector, consultez la rubrique Vector.

Prérequis

Avant de commencer, assurez-vous de disposer des éléments suivants :

  • Une instance Tair opérationnelle, avec son endpoint et son mot de passe

  • Python 3.8 ou version ultérieure

  • Un GPU compatible CUDA

  • Les packages Python suivants installés :

    Chinese-CLIP s'installe depuis GitHub plutôt que depuis PyPI. Exécutez les deux commandes ci-dessus.
    Package Objectif
    tair Client Python Tair pour les opérations TairVector
    torch Environnement d'exécution PyTorch requis par Chinese-CLIP
    Pillow Chargement et traitement des images
    matplotlib Affichage des images résultantes
    Chinese-CLIP Modèle d'embedding pour les images et le texte
    pip3 install tair torch Pillow matplotlib
    pip3 install git+https://github.com/OFA-Sys/Chinese-CLIP.git
  • Des exemples de données :

    • Images : plus de 7 000 images d'animaux de compagnie issues du jeu de données Extreme Mart, téléchargées dans un répertoire local (par exemple, /home/CLIP_Demo)

    • Texte : trois exemples de descriptions — « a dog », « a white dog » et « a running white dog »

Étape 1 : Se connecter à Tair

Importez toutes les dépendances et définissez une fonction de connexion. Remplacez les valeurs de host et de password par celles de votre instance Tair. Pour les comptes personnalisés, définissez password au format username:password.

# -*- coding: utf-8 -*-
from tair import Tair
from tair.tairvector import DistanceMetric
from tair import ResponseError

from typing import List
import torch
from PIL import Image
import pylab
from matplotlib import pyplot as plt
import os
import cn_clip.clip as clip
from cn_clip.clip import available_models

def get_tair() -> Tair:
    """Connect to a Tair instance.
    * host: the endpoint of your Tair instance.
    * port: the port number. Default: 6379.
    * password: the password. For custom accounts, use the username:password format.
    """
    tair: Tair = Tair(
        host="r-8vbehg90y9rlk9****pd.redis.rds.aliyuncs.com",
        port=6379,
        db=0,
        password="D******3",
        decode_responses=True
    )
    return tair

Étape 2 : Créer les index vectoriels

Créez deux index HNSW : l'un pour les embeddings d'images et l'autre pour les embeddings de texte. Tous deux utilisent le produit intérieur (IP) comme métrique de distance et une dimension de 1 024 afin de correspondre à la sortie du modèle Chinese-CLIP RN50.

def create_index():
    """Create vector indexes for image and text embeddings.
    * Index names: index_images, index_texts
    * Vector dimension: 1024 (matches Chinese-CLIP RN50 output)
    * Distance metric: IP (inner product)
    * Index algorithm: HNSW
    """
    ret = tair.tvs_get_index("index_images")
    if ret is None:
        tair.tvs_create_index("index_images", 1024, distance_type="IP",
                              index_type="HNSW")
    ret = tair.tvs_get_index("index_texts")
    if ret is None:
        tair.tvs_create_index("index_texts", 1024, distance_type="IP",
                              index_type="HNSW")

Étape 3 : Générer et stocker les embeddings

Utilisez Chinese-CLIP pour encoder les images et le texte, puis stockez chaque embedding dans TairVector à l'aide de la commande TVS.HSET.

Embeddings d'images — le chemin d'accès à chaque fichier image devient la clé, et son vecteur de 1 024 dimensions devient la valeur :

def insert_images(image_dir):
    """Generate image embeddings with CLIP and store them in TairVector.
    Each image is stored as:
    * Index: index_images
    * Key: image file path (e.g., test/images/boxer_18.jpg)
    * Value: 1024-dimensional embedding vector
    """
    file_names = [f for f in os.listdir(image_dir) if (f.endswith('.jpg') or f.endswith('.jpeg'))]
    for file_name in file_names:
        image_feature = extract_image_features(image_dir + "/" + file_name)
        tair.tvs_hset("index_images", image_dir + "/" + file_name, image_feature)

def extract_image_features(img_name):
    """Extract a 1024-dimensional embedding from an image using CLIP."""
    image_data = Image.open(img_name).convert("RGB")
    infer_data = preprocess(image_data)
    infer_data = infer_data.unsqueeze(0).to("cuda")
    with torch.no_grad():
        image_features = model.encode_image(infer_data)
    image_features /= image_features.norm(dim=-1, keepdim=True)
    return image_features.cpu().numpy()[0]  # [1, 1024]

Embeddings de texte — chaque chaîne de texte devient la clé :

def upsert_text(text):
    """Generate a text embedding with CLIP and store it in TairVector.
    Each text is stored as:
    * Index: index_texts
    * Key: the text content (e.g., "a running dog")
    * Value: 1024-dimensional embedding vector
    """
    text_features = extract_text_features(text)
    tair.tvs_hset("index_texts", text, text_features)

def extract_text_features(text):
    """Extract a 1024-dimensional embedding from a text string using CLIP."""
    text_data = clip.tokenize([text]).to("cuda")
    with torch.no_grad():
        text_features = model.encode_text(text_data)
    text_features /= text_features.norm(dim=-1, keepdim=True)
    return text_features.cpu().numpy()[0]  # [1, 1024]
Il n'est pas nécessaire de stocker le texte ou l'image de la requête dans TairVector. Seuls les embeddings indexés doivent être stockés au préalable.

Étape 4 : Exécuter la recherche multimodale

Utilisez le paramètre topK pour contrôler le nombre de résultats à renvoyer. Une valeur de distance plus faible indique une similarité plus élevée.

Recherche texte vers image — convertit une requête textuelle en embedding, puis recherche dans l'index des images :

def query_images_by_text(text, topK):
    """Text-to-image search: find images that match a text description.
    Returns image keys and distance scores. Lower distance = higher similarity.
    """
    text_feature = extract_text_features(text)
    result = tair.tvs_knnsearch("index_images", topK, text_feature)
    for k, s in result:
        print(f'key : {k}, distance : {s}')
        img = Image.open(k.decode('utf-8'))
        plt.imshow(img)
        pylab.show()

Recherche image vers texte — convertit une image de requête en embedding, puis recherche dans l'index des textes :

def query_texts_by_image(image_path, topK=3):
    """Image-to-text search: find text descriptions that match an image.
    Returns text keys and distance scores. Lower distance = higher similarity.
    """
    image_feature = extract_image_features(image_path)
    result = tair.tvs_knnsearch("index_texts", topK, image_feature)
    for k, s in result:
        print(f'text : {k}, distance : {s}')

Exécuter le flux de travail complet

if __name__ == "__main__":
    # Connect to Tair and create vector indexes for images and texts.
    tair = get_tair()
    create_index()
    # Load the Chinese-CLIP model (RN50 variant).
    model, preprocess = clip.load_from_name("RN50", device="cuda", download_root="./")
    model.eval()

    # Store image embeddings. Replace the path with your dataset directory.
    insert_images("/home/CLIP_Demo")
    # Store text embeddings.
    upsert_text("a dog")
    upsert_text("a white dog")
    upsert_text("a running white dog")

    # Text-to-image search: find the top 3 images matching "a running dog".
    query_images_by_text("a running dog", 3)
    # Image-to-text search: find text descriptions matching the specified image.
    query_texts_by_image("/home/CLIP_Demo/boxer_18.jpg", 3)

Résultats

Recherche texte vers image — requête « a running dog », 3 premiers résultats :

Text-to-image search results showing three running dog images

Recherche image vers texte — image saisie :

Query image: a running dog

Résultats classés par similarité (distance la plus faible = similarité la plus élevée) :

{
  "results": [
    {
      "text": "a running white dog",
      "distance": "0.4052203893661499"
    },
    {
      "text": "a white dog",
      "distance": "0.44666868448257446"
    },
    {
      "text": "a dog",
      "distance": "0.4553511142730713"
    }
  ]
}

Étapes suivantes

Tair est une base de données en mémoire dotée d'une indexation HNSW intégrée, ce qui la rend particulièrement adaptée à la récupération de données en temps réel. Appliquez ce schéma à des cas d'utilisation tels que :

  • Recommandation de produits : acceptez une description textuelle et renvoyez les images de produits correspondantes

  • Légendage d'images : acceptez une image de produit et renvoyez des descriptions candidates

  • Modalités étendues : remplacez Chinese-CLIP par un autre modèle d'embedding pour prendre en charge la recherche vidéo-texte ou audio-texte