Fermer

mai 20, 2025

Recherche de vecteur avec EF 9

Recherche de vecteur avec EF 9


Entity Framework Core 9 a introduit une nouvelle fonctionnalité pour la recherche de vecteur, qui permet des recherches basées sur la similitude vectorielle, permettant aux développeurs d’incorporer ces avantages directement dans les bases de données. Consultez cet article sur la façon d’implémenter la recherche vectorielle dans ASP.NET Core en utilisant cette nouvelle fonctionnalité dans EF 9.

La recherche vectorielle est une approche qui permet de trouver un élément de données spécifique d’une manière différente des méthodes traditionnelles, car elle est basée sur la similitude vectorielle. C’est-à-dire des vecteurs qui sont sémantiquement ou visuellement similaires les uns aux autres.

Entity Framework Core 9 introduit, quoique expérimentalement, la possibilité de créer et de rechercher des données vectorielles dans la base de données Azure Cosmos DB. Dans cet article, nous verrons une introduction à la recherche vectorielle, quels sont ses avantages et comment implémenter la recherche vectorielle dans ASP.NET Core en utilisant les fonctionnalités expérimentales d’EF 9.

La recherche vectorielle est une technique de recherche utilisée pour trouver des données qui ont une certaine similitude les unes avec les autres. Les vecteurs déterminent généralement cette similitude.

Les vecteurs, ou intégres, sont des caractères numériques qui représentent des mots, des documents, des images ou des vidéos. Les vecteurs capturent les relations sémantiques entre les données. Cette technique identifie des éléments similaires en fonction de leur proximité dans un espace vectoriel multidimensionnel, plutôt que de s’appuyer uniquement sur des correspondances de texte exactes, comme dans les approches de recherche traditionnelles.

La recherche traditionnelle utilise une approche basée sur les mots clés pour récupérer des informations. De cette façon, la recherche est effectuée par une correspondance exacte ou approximative des termes, sans interpréter le contexte ou la relation sémantique entre les termes.

En revanche, la recherche vectorielle transforme les données en représentations numériques multidimensionnelles (vecteurs), permettant d’identifier les similitudes sémantiques et d’élargir la puissance de la recherche.

Bien que l’approche traditionnelle soit efficace pour les recherches exactes et structurées, la recherche vectorielle excelle dans les scénarios où la compréhension du contexte et la relation entre les termes est essentielle, comme dans les recherches sémantiques, les chatbots et les systèmes de recommandation.

Imaginez le scénario suivant: si vous recherchez un «bon film fantastique» dans un moteur de recherche traditionnel, il peut retourner des pages qui contiennent exactement ces mots, mais il ne comprend pas nécessairement que le «grand film d’aventure» peut avoir le même sens. Cependant, la recherche de vecteurs, par le biais d’incorporation, peut percevoir cette relation et renvoyer des résultats plus pertinents, même si les mots ne sont pas identiques.

En d’autres termes, la recherche traditionnelle est basée sur la correspondance des personnages et la fréquence des mots, tandis que la recherche de vecteur utilise l’apprentissage automatique pour capturer le sens et les relations entre les termes.

Remarque dans l’exemple ci-dessous où les principales différences entre la recherche traditionnelle et la recherche vectorielle sont démontrées.

Recherche traditionnelle vs recherche vectorielle

Quand la recherche vectorielle est-elle la plus utile?

La recherche est utile dans des situations où il est nécessaire de rechercher des informations en gros volumes de données non structurées, en particulier lorsque la recherche exacte de mots clés n’est pas très pertinente, auquel cas la recherche de similitude dans la recherche de vecteurs se distingue.

Dans ce contexte, nous pouvons mettre en évidence certains scénarios:

Recherche sémantique dans les textes – Contrairement aux recherches traditionnelles basées sur les mots clés, la recherche vectorielle peut trouver des fichiers tels que des documents qui ont des significations similaires, même s’ils utilisent des mots différents. Par exemple, les réseaux sociaux et le commerce électronique.

Recherche d’image et de vidéo – La recherche de vecteur vous permet de trouver des images similaires sans compter sur les métadonnées, analysant directement les caractéristiques visuelles des fichiers.

Recherche de données multimodales – La recherche de vecteurs peut saisir des relations complexes entre différents types de données, tels que le texte et les images, et les combiner pour un résultat plus approfondi.

Chatbots et assistants virtuels – La recherche vectorielle peut être utilisée pour formuler des réponses plus pertinentes pour l’utilisateur, en fonction du sens (sentiment) et pas seulement des mots exacts.

Recherche de vecteur dans ASP.NET Core

.NET 9 a apporté la prise en charge de la recherche de vecteurs natifs au noyau ASP.NET pour la première fois, et il est actuellement disponible dans Azure Cosmos DB, la base de données cloud de Microsoft. Si vous n’êtes pas familier avec Cosmos DB, je vous suggère de lire cet article: Travailler avec Cosmos DB dans ASP.NET Core.

Avertissement 1: Au moment de la rédaction du post, Vector Search était toujours en avant-première, qui pourrait être modifié à l’avenir.

Avertissement 2: Ce message utilise l’émulateur COSMOS DB, auquel cas aucune configuration supplémentaire n’est requise. Cependant, si vous allez utiliser la version officielle de Cosmos DB, vous devrez mettre à jour les ressources de votre compte pour prendre en charge la recherche vectorielle. Vous pouvez le faire en utilisant la CLI Azure:

az cosmosdb update --resource-group <resource-group-name> --name <account-name> --capabilities EnableNoSQLVectorSearch

Avertissement 3: Ce message utilise la génération de vecteurs aléatoires similaires. Pour générer des vecteurs à l’aide de données, il est nécessaire d’utiliser des API externes, telles que les incorporations de vecteurs OpenAi, qui ne seront pas couvertes dans ce post.

Azure Cosmos DB prend désormais en charge le stockage du vecteur, vous permettant d’exécuter toutes les requêtes dans une seule base de données, directement sur des documents contenant d’autres données en plus du vecteur. Cela élimine la nécessité de créer une solution supplémentaire pour une base de données vectorielle dédiée, qui peut simplifier considérablement l’architecture des applications.

Pour implémenter la recherche vectorielle dans la pratique, nous créerons une API simple dans ASP.NET Core, insérez quelques exemples de données et récupérerons ces données via la recherche vectorielle. La base de données utilisée pour l’insertion et la recherche sera Cosmos DB, via l’émulateur Cosmos DB, en cours d’exécution localement.

Vous pouvez accéder au code de projet complet dans ce référentiel GitHub: Code source.

Implémentation de l’application

Pour créer l’application de base, vous pouvez utiliser la commande ci-dessous dans le terminal:

dotnet new web -o VectorMovieRecommendation

Les packages NuGet utilisés dans l’application sont les suivants:

  • Microsoft.azure.cosmos
  • Microsoft.entityframeworkcore
  • Microsoft.entityframeworkcore.cosmos
  • Microsoft.entityFrameworkCore.design
  • Newtonsoft.json

Vous pouvez les installer via le terminal ou utiliser le gestionnaire de packages Visual Studio ou un autre IDE de votre choix.

Ainsi, dans l’application, créez un nouveau dossier appelé «modèles» et, à l’intérieur, créez la classe suivante:

using Newtonsoft.Json;

namespace VectorMovieRecommendation.Models;
public class Movie
{
    [JsonProperty("id")]
    public string Id { get; set; } = Guid.NewGuid().ToString();
    public float[] Vector { get; set; } = new float[1025];
    public string Title { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public int ReleaseYear { get; set; }
    public List<string> Genres { get; set; } = new List<string>();
    public double Rating { get; set; }
}

Notez que nous déclarons ici une propriété appelée Vector qui est une liste de tableaux flottants. Et nous déclarons également une valeur de 1025, qui représente le nombre d’éléments stockés dans le tableau. Cette propriété représentera le vecteur lorsque nous insérons un enregistrement dans COSMOS DB et sera utilisé pour la vérification lors de la recherche de similitude.

L’étape suivante consiste à créer la classe de contexte, qui contiendra les paramètres de noyau EF pour créer les tables. Créez un nouveau dossier appelé «données» et, à l’intérieur, ajoutez la classe ci-dessous:

using Microsoft.Azure.Cosmos;
using Microsoft.EntityFrameworkCore;
using VectorMovieRecommendation.Models;
namespace VectorMovieRecommendation.Data;
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<Movie> Movies { get; set; } = default!;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        #pragma warning disable EF9103 
        modelBuilder.Entity<Movie>()
            .Property(e => e.Vector)
            .IsVector(DistanceFunction.Cosine, 1025);
        #pragma warning restore EF9103 
    }
}

Analysons le code.

Ici, nous créons une classe de contexte pour effectuer les configurations de noyau EF par défaut, mais notez que dans le OnModelCreation Méthode Il y a une configuration supplémentaire:

modelBuilder.Entity<Movie>()
.Property(e => e.Vector)
.IsVector(DistanceFunction.Cosine, 1025);

Cette configuration indique à EF de cartographier le Vector Propriété de l’entité de film comme vecteur 1025 dimensionnel, en utilisant la fonction de distance en cosinus. Cela se fait à travers le IsVector Méthode pendant la configuration du modèle.

En définissant la propriété comme un vecteur et en spécifiant la fonction de distance en cosinus, EF peut optimiser les requêtes qui impliquent des comparaisons de similitude entre les vecteurs.

Un autre élément que vous pouvez remarquer est le #pragma warning disable EF9103 Directive qui est utilisée pour supprimer un avertissement de compilateur, identifié par le code EF9103. Cet avertissement indique qu’une caractéristique ou une API donnée est expérimentale, destinée à des fins d’évaluation uniquement, et est sujette à un changement ou à la suppression des futures mises à jour. Puisqu’il s’agit toujours d’une fonctionnalité expérimentale (au moins à la date à laquelle cet article a été écrit), si vous n’utilisez pas la directive, le compilateur rapportera une erreur.

L’étape suivante consiste à configurer les variables d’environnement. Ils sont dans le fichier AppSettings.json, ajoutez ce qui suit:

 "CosmosDb": {
    "AccountEndpoint": "https://localhost:PORT_NUMBER",
    "AccountKey": "YOUR_COSMOS_DB_ACCOUNT_KEY",
    "DatabaseName": "VectorMovieRecommendationDB",
    "ContainerName": "Movies"
  },

N’oubliez pas de remplacer PORT_NUMBER avec le port où l’émulateur Cosmos DB est en cours d’exécution et YOUR_COSMOS_DB_ACCOUNT_KEY avec la touche de configuration.

Configuration de la classe de programme

Dans la classe de programme, nous configurerons les injections de dépendance, les variables d’environnement et les points de terminaison et les méthodes. Alors, ajoutez d’abord le code suivant, juste en dessous du var builder = WebApplication.CreateBuilder(args);:

var cosmosConfig = builder.Configuration.GetSection("CosmosDb");
var connectionString = cosmosConfig["AccountEndpoint"];
var accountKey = cosmosConfig["AccountKey"];
var databaseName = cosmosConfig["DatabaseName"];

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseCosmos(connectionString!, accountKey!, databaseName!));

Ici, nous extraissons les informations des variables d’environnement créées précédemment et les transmettant à la configuration de la classe de contexte EF Core.

Maintenant, créons des points de terminaison et des méthodes pour insérer quelques exemples de données et créer la base de données et les collections pendant l’exécution du programme si elles n’existent pas déjà.

Ajouter à la classe programme.cs le code suivant:

app.MapPost("/movies/seed", async (AppDbContext db) =>
{
    await Seed(db);
    return Results.Ok();
});

using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    await dbContext.Database.EnsureCreatedAsync();
}

static async Task Seed(AppDbContext dbContext)
{
    var sampleMovies = new List<Movie>
            {
                new Movie
                {
                    Title = "Sci-Fi Adventure",
                    Description = "A thrilling space journey",
                    ReleaseYear = 2022,
                    Genres = new List<string> { "Sci-Fi", "Adventure" },
                    Rating = 8.5,
                    Vector = GenerateRandomVector()
                },
                new Movie
                {
                    Title = "Interstellar Voyage",
                    Description = "A group of explorers travel through a wormhole",
                    ReleaseYear = 2014,
                    Genres = new List<string> { "Sci-Fi", "Drama" },
                    Rating = 8.6,
                    Vector = GenerateRandomVector()
                },
                new Movie
                {
                    Title = "Futuristic Battle",
                    Description = "A war between humans and AI",
                    ReleaseYear = 2023,
                    Genres = new List<string> { "Sci-Fi", "Action" },
                    Rating = 8.2,
                    Vector = GenerateRandomVector()
                },
                new Movie
                {
                    Title = "Galactic War",
                    Description = "Intergalactic conflict between empires",
                    ReleaseYear = 2019,
                    Genres = new List<string> { "Sci-Fi", "Adventure" },
                    Rating = 8.4,
                    Vector = GenerateRandomVector()
                }
            };

    dbContext.Movies.AddRange(sampleMovies);
    await dbContext.SaveChangesAsync();
}

static float[] GenerateRandomVector()
{
    int size = 1536;
    var random = new Random();
    return Enumerable.Range(0, size)
                     .Select(_ => (float)random.NextDouble())
                     .ToArray();
}

Notez que pour générer le vecteur, nous utilisons la méthode locale GenerateRandomVector()qui définit la taille du vecteur à 1536, puis instancie un générateur de nombres aléatoires (new Random()), génère ensuite une séquence d’entiers de taille à partir de 0 (random.NextDouble()) et convertit enfin la séquence en un tableau. Cette méthode est faite génériquement pour créer des vecteurs; Si vous allez l’utiliser ailleurs, n’oubliez pas de le modifier en fonction de vos besoins.

Maintenant, toujours dans la classe programme.cs, ajoutez le point de terminaison suivant pour récupérer les enregistrements de la base de données à l’aide de la recherche vectorielle:

app.MapGet("/movies/vector-movies", async (AppDbContext db) =>
{
    var queryVector = GenerateRandomVector();

    #pragma warning disable EF9103 
    var movies = await db.Movies
    .OrderBy(m => EF.Functions.VectorDistance(m.Vector, queryVector))
    .Take(5)
    .ToListAsync();
    #pragma warning restore EF9103 
    
    return Results.Ok(movies);
});

Dans le code ci-dessus, un point de terminaison HTTP Get est défini qui renvoie une liste des cinq films les plus similaires basés sur une comparaison de vecteurs. La similitude est déterminée en calculant la distance entre un vecteur de requête généré de manière aléatoire et les vecteurs associés à chaque film de la base de données. Analysons chaque partie:

  1. Génération du vecteur de requête:
var queryVector = GenerateRandomVector();

Ici le GenerateRandomVector() La méthode est appelée pour créer un vecteur de point flottant avec 1 536 éléments, chacun contenant une valeur aléatoire entre 0,0 (inclusive) et 1,0 (exclusive). Ce vecteur sert de référence pour mesurer la similitude avec les vecteurs des films stockés.

  1. Interroger la base de données:
var movies = await db.Movies
.OrderBy(m => EF.Functions.VectorDistance(m.Vector, queryVector))
.Take(5)
.ToListAsync();

Dans cette requête, la liste des films (db.Movies) est commandé en fonction de la distance du vecteur entre le vecteur de chaque film (m.Vector) et le queryVector. Le EF.Functions.VectorDistance La fonction calcule cette distance, permettant aux films d’être commandés de la plus petite à la plus grande distance, c’est-à-dire du plus similaire au moins similaire au vecteur de requête. Ensuite, les cinq films les plus similaires sont sélectionnés avec Take(5) et converti en une liste asynchrone avec ToListAsync().

  1. Retour des résultats:
return Results.Ok(movies);

Enfin, la liste des films sélectionnés est retournée avec un statut HTTP de 200 OK.

  1. AVERTISSEMENT DE COMPILATION: La suppression d’avertissement est à nouveau requise. Les directives #pragma warning disable EF9103 et #pragma warning restore EF9103 sont utilisés pour supprimer l’avertissement EF9103 pendant la compilation. Cet avertissement indique que la fonctionnalité utilisée est en cours d’évaluation et peut changer ou être supprimé dans les futures mises à jour.

Maintenant, exécutons le get /vector-movies route pour retourner les enregistrements en utilisant l’EF 9 VectorDistance() Méthode, qui calcule la distance entre les vecteurs, permettant à la recherche de vecteurs proches ou similaires.

Donc, exécutez simplement l’application et exécutez le point de terminaison pour insérer des données: https://localhost:PORT/movies/seed. Puis accédez au point final https://localhost:PORT/movies/vector-moviespour que la liste des films soit retournée, comme vous pouvez le voir dans l’image ci-dessous, en utilisant Progress Telerik Fiddler partout:

Recherche de vecteur en cours d'exécution

Conclusion

Vector Search est une technique de recherche avancée qui a beaucoup évolué ces dernières années. Avec l’arrivée de EF 9, nous avons eu l’introduction de la prise en charge de la recherche vectorielle dans Azure Cosmos DB, ce qui facilite la mise en œuvre et l’exécution de cette fonctionnalité.

Dans cet article, nous avons créé une API simple pour insérer des données dans COSMOS DB et utiliser la nouvelle fonctionnalité pour récupérer ces enregistrements à l’aide de la recherche vectorielle. N’oubliez pas qu’il s’agit toujours d’une fonctionnalité expérimentale, mais vous pouvez commencer à l’utiliser dès maintenant et découvrir les nombreuses possibilités offertes par la recherche vectorielle.




Source link