Le modèle d’intégration mis à jour offre des performances de pointe avec une fenêtre de contexte 4x plus longue. Le nouveau modèle est 90% moins cher. Les dimensions d’intégration plus petites réduisent le coût de leur stockage sur des bases de données vectorielles.
OpenAI a mis à jour en décembre 2022 le modèle Embedding pour text-embedding-ada-002
. Le nouveau modèle offre :
- Prix inférieur de 90 % à 99,8 %
- La taille des dimensions d’incorporation au 1/8e réduit les coûts de la base de données vectorielles
- Unification des terminaux pour une utilisation simplifiée
- Performances de pointe pour la recherche de texte, la recherche de code et la similarité des phrases
- La fenêtre contextuelle est passée de 2048 à 8192.
Ce didacticiel vous guide à travers le point de terminaison d’intégration avec une tâche de clustering. Je stocke et récupère ces plongements à partir d’une base de données vectorielle. Je couvre les questions liées au modèle Embedding et aux bases de données vectorielles. Pourquoi l’aspect des coûts était un problème avec la version précédente du point de terminaison Embedding ? Comment puis-je utiliser le modèle Embedding dans la pratique pour les tâches NLP ? Qu’est-ce qu’une base de données vectorielle ? Comment intégrer les incorporations de texte OpenAI dans un service de base de données vectorielle ? Comment effectuer des requêtes sur la base de données vectorielle ?
Ce didacticiel nécessite un accès à l’API OpenAI. Les jetons coûtent quelques centimes pour 300 avis. La base de données vectorielle ne nécessite qu’un compte Pinecone gratuit.
OpenAI a publié en décembre 2022 la version mise à jour du point de terminaison Embedding. Le modèle est utile pour de nombreuses tâches NLP. Il offre des performances « de pointe » pour la recherche de texte, la recherche de code et la similarité des phrases. La classification du texte est également bonne. BEIR-benchmark⁶ évalue les performances sur de telles tâches. Le modèle Embedding fonctionne bien sur cette référence, étant donné qu’il s’agit d’un produit commercial.
Je peux utiliser le modèle d’intégration pour effectuer des tâches NLP avec du texte telles que :
- recherche
- regroupement
- recommandations
- classification
- détection d’anomalies et
- mesure de la diversité.
De nombreuses tâches NLP reposent sur un concept appelé « text embeddings », qui sont des listes vectorielles de nombres à virgule flottante. Les chaînes de texte sont très liées si la distance de la liste vectorielle est petite. De même, les chaînes de texte sont très indépendantes si la distance est grande.
Les entreprises disposent d’un grand nombre de cas d’utilisation potentiels pour le NLP. Les contestations de factures et les avis sur les produits sont des exemples de chaînes de texte pouvant être converties en incorporations de texte. Ce didacticiel utilise un ensemble de données « Fine Food Reviews » accessible au public. L’ensemble de données comprend 500 000 avis au format CSV. Comme un ensemble de données aussi volumineux consomme beaucoup de jetons d’API, j’en échantillonnerai un plus petit sous-ensemble de données.
Les modèles NLP utilisent des jetons comme base de tarification. Les jetons sont des « morceaux d’un mot » avec un nombre variable de caractères :
- OpenAI réfère un mot à environ 1,3 jetons³
- Cohere API un mot est énoncé autour de 2 à 3 jetons⁴.
Plus le fichier csv de chaînes de texte à traiter est long, plus les jetons seront facturés.
C’était un facteur limitant dans le modèle d’intégration précédent. Les appels API étaient trop chers.
Examinons cela dans la pratique. Un développeur peut envisager deux projets pour dépenser le budget de l’API. Chaque projet utilise un point de terminaison OpenAI différent. Comment le développeur allouerait-il les jetons entre la complétion de texte et les modèles d’intégration ?
Commençons par le modèle de complétion de texte. Mon relevé de facturation comprend plusieurs invites avec une consommation inférieure à 1 000 tokens :
De tels appels d’API pourraient être utilisés par exemple pour résumer un texte plus court ou le traduire d’une langue à une autre. Ces appels d’API sont bon marché, tant que la longueur et la quantité des chaînes de texte sont contrôlées.
Examinons ensuite l’appel de l’API Embedding. J’ai traité un seul fichier CSV d’avis sur les produits. Il a consommé environ 33 fois plus de jetons, même si je n’ai traité qu’un petit sous-ensemble de données du fichier CSV :
Inutile de dire que ces paramètres sont utilisés à des fins différentes. La raison pour laquelle je compare les prix ici est la suivante. Le nouveau prix du modèle Embedding a été abaissé de 0,2 $ / 1K jetons à 0,0004 $ / 1K jetons. En d’autres termes, les jetons de 27k coûtaient environ 5 $. Je peux maintenant interroger la même chose avec seulement 0,01 dollar.
Le point de terminaison Embedding mis à jour est entre 90 % et 99,8 % moins cher que l’ancien point de terminaison².
La réduction de prix permet aux développeurs de créer des produits qui étaient trop coûteux dans le passé, même pour être testés avec le modèle d’intégration précédent.
Ce n’était pas un problème avec le point de terminaison d’achèvement de texte dans le passé.
OpenAI a unifié plusieurs modèles au point de terminaison Embedding en un modèle unique et plus performant. Cela a permis de baisser le prix du modèle. Je l’ai remarqué immédiatement car l’API est maintenant plus facile à utiliser.
Le nouveau point de terminaison Embedding augmente la fenêtre de contexte jusqu’à 8192 jetons. Cela permet de travailler efficacement avec des chaînes de texte 4x plus longues.
De nombreux grands modèles de langage (LLM) reposent toujours sur des fenêtres de contexte de 2048 jetons ou moins. Cela peut ne pas sembler significatif. Pourtant, de nombreuses tâches NLP fonctionnent avec de longues fenêtres contextuelles, telles que le traitement de documents ou de contrats juridiques. Je crois qu’une fenêtre de contexte plus longue permet des cas d’utilisation complètement nouveaux pour les LLM. Par exemple, je peux maintenant effectuer une recherche textuelle dans un podcast entier à l’aide de Whisper with Embedding model.
J’importe ensuite toutes les bibliothèques requises dans ce tutoriel. Pinecone n’est requis que pour la partie finale pour stocker les incorporations de mots dans la base de données vectorielle Pinecone.
!pip install plotly
!pip install -U scikit-learn
!pip install -U pinecone-client # for vector database
import os
import time # optional
import pandas as pd
import numpy as np
import openai
from openai.embeddings_utils import get_embedding, cosine_similarity
from transformers import GPT2TokenizerFast
import matplotlib
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import pinecone # for vector database
L’étape suivante consiste à ajouter vos propres clés API. Cet exemple récupère ces variables enregistrées sur Windows en tant que variables d’environnement.
openai.api_key = os.getenv("OPENAI_API_KEY")
La clé API elle-même est disponible sur le site Web d’OpenAI sous « Afficher les clés API ».
N’oubliez pas de ne pas taper la clé API directement dans le code. Ce n’est pas une pratique sécurisée.
Cet exemple utilise l’ensemble de données Amazon Fine Food Reviews publié dans Kaggle¹. L’ensemble de données comprend d’anciens avis avec des informations telles que des évaluations de produits et des avis en texte brut.
Je vais charger le jeu de données et générer l’intégration vectorielle de la révision du texte. Ensuite, je regrouperai ces plongements et les tracerai dans l’espace 2D.
input_datapath = 'Reviews.csv' # This dataset includes 500k reviews
df = pd.read_csv(input_datapath, index_col=0)
df = df[['Time', 'ProductId', 'UserId', 'Score', 'Summary', 'Text']]
df = df.dropna()
df['combined'] = "Title: " + df.Summary.str.strip() + "; Content: " + df.Text.str.strip()
df = df.sort_values('Time').tail(3_00) # Pick latest 300 reviews
df.drop('Time', axis=1, inplace=True)
Je compte ensuite un certain nombre de jetons dans la colonne de données combinées avec le tokenizer. Je filtre les avis les plus récents en dessous de 8000 jetons. La fenêtre de contexte est gérable pour le modèle et limite les révisions très longues.
J’utilise 300 avis dans ce tutoriel pour limiter les frais par l’API. Pourtant, il n’est pas limité par l’API.
J’ajoute une colonne supplémentaire au fichier CSV et l’enregistre sous un nouveau nom. Je récupère enfin à partir de l’API d’OpenAI les vecteurs de similarité et de recherche du point de terminaison Embedding.
tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
df['n_tokens'] = df.combined.apply(lambda x: len(tokenizer.encode(x))) #add number of tokens
df = df[df.n_tokens<8000].tail(3_00) # remove extra long text lines based on number of tokens
df['ada_similarity'] = df.combined.apply(lambda x: get_embedding(x, engine='text-embedding-ada-002'))
df['ada_search'] = df.combined.apply(lambda x: get_embedding(x, engine='text-embedding-ada-002'))
Je peux maintenant utiliser ces vecteurs pour regrouper les avis.
matrix = np.array(df.ada_similarity.apply(eval).to_list())
n_clusters = 7
kmeans = KMeans(n_clusters=n_clusters, init="k-means++", random_state=42, n_init='auto')
kmeans.fit(matrix)
labels = kmeans.labels_
df["Cluster"] = labels
df.groupby("Cluster").Score.mean().sort_values()
Je peux tracer les avis dans l’espace 2D en utilisant:
fig, ax = plt.subplots(figsize=(10, 7))
for category, color in enumerate(["purple", "green", "red", "blue", "black", "orange", "brown"]):
xs = np.array(x)[df.Cluster == category]
ys = np.array(y)[df.Cluster == category]
ax.scatter(xs, ys, color=color, alpha=0.3)
avg_x = xs.mean()
avg_y = ys.mean()
ax.scatter(avg_x, avg_y, marker="x", color=color, s=100)ax.set_title("Clusters of Fine Food Reviews visualized 2d with K-means", fontsize=14)
plt.show()
Le graphique résultant illustre les clusters dans les revues.
Je résume ensuite les groupes sous des thèmes communs et imprime quelques exemples de chacun.
rev_per_cluster = 3
for i in range(n_clusters):
print(f"Cluster {i} Theme:", end=" ")reviews = "\n".join(
df[df.Cluster == i]
.combined.str.replace("Title: ", "")
.str.replace("\n\nContent: ", ": ")
.sample(rev_per_cluster, random_state=42)
.values
)
response = openai.Completion.create(
engine="text-davinci-001",
prompt=f'What do the following customer reviews have in common?\n\nCustomer reviews:\n"""\n{reviews}\n"""\n\nTheme:',
temperature=0,
max_tokens=64,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
)
print(response["choices"][0]["text"].replace("\n", ""))
sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42)
for j in range(rev_per_cluster):
print(sample_cluster_rows.Score.values[j], end=", ")
print(sample_cluster_rows.Summary.values[j], end=": ")
print(sample_cluster_rows.Text.str[:70].values[j])
print("-" * 100)
Les résultats sont utiles pour trouver des thèmes, qui sont rapidement validés avec les revues qui l’accompagnent :
Cluster 0 Theme: All reviews are positive.
5, 5 Star Tea at a super price!: This tea is very good and perfect for the price....you can't go wrong
5, Delish!!: So yummy... Drinking it Black coffee or w cream this coffee is delish
5, breakfast tea: We switch to this decaf tea at night for a great cup of tea and no sle
----------------------------------------------------------------------------------------------------
Cluster 1 Theme: The reviews have in common that the customers are happy with the product and the service.
5, Perfect Gift: I got these to give out in a goodie bag for the holidays, with a coupl
5, Awesome service and great products: We sent this product as a gift to my husband's daughter (who just grad
3, its delicious...: I was surprised at how delicious these toasted chips are. They ship we
----------------------------------------------------------------------------------------------------
Cluster 2 Theme: All reviews mention the quality of the coffee bean.
5, Jamaican Blue beans: Excellent coffee bean for roasting. Our family just purchased another
5, super coffee: Great coffee and so easy to brew. This coffee has great aroma and is
1, Steer Clear-not as described: I wanted to try different K cups and didnt want to buy entire boxes of
----------------------------------------------------------------------------------------------------
Cluster 3 Theme: All three reviews mention the product's flavor.
4, Delicious, but lid is cheap: I bought this syrup after reading the glowing reviews here. It is inde
5, More Like Pepsi: There is a lot of cola syrups trying to be Coke but this one is shooti
5, Awesome!!!: This flavor is the best ever!!! It tastes better than an orange Juliu
----------------------------------------------------------------------------------------------------
Cluster 4 Theme: All reviews mention that the product is made with ingredients from another country, and that this is a negative.
1, terrible treats: My dogs love them but they are loaded with junk and come from another
1, New Recipe is Awful: These used to be my favorite animal crackers but Austin has recently c
4, Great food!: I wanted a food for a a dog with skin problems. His skin greatly impro
----------------------------------------------------------------------------------------------------
Cluster 5 Theme: All reviews mention the product's good taste.
5, Rich cocoa taste and satisfying...: I like the convenience of protein bars for an on-the-go snack that fit
4, GOOD GLUTEN FREE BREAD STCK MIX: Makes very good break sticks.. Also can be used for a pizza crust.<br
5, Red Chili Fettuccinne: This product is great - looks, cooking time and taste. Have been usin
----------------------------------------------------------------------------------------------------
Cluster 6 Theme: All reviews mention that the customer's dog loves the product.
5, Favorite chew toy!: This is the second one of these antlers that I've ordered (the first o
5, Excellent Dog Food: My dog loves it and she absorbes it well. Her breath is fresh and her
5, my dogs love the peanut butter!: First off, read the ingredients, no crazy words I can't pronounce, whi
----------------------------------------------------------------------------------------------------
Le clustering n’est pas une technique nouvelle même dans le NLP, mais je ne saurais trop insister sur l’utilité de l’API OpenAI. Le modèle est à la pointe de la technologie, ce qui supprime l’obstacle d’avoir à se contenter d’une qualité inférieure. Ensuite, j’ajoute le mot embedding-vectors à une base de données vectorielles.
Les données vectorielles peuvent être stockées de plusieurs façons, la plus simple étant peut-être un fichier CSV. Pourtant, ce n’est peut-être pas une bonne approche à long terme. Les bases de données vectorielles stockent et récupèrent les données vectorielles de manière évolutive et sécurisée sous forme de nombres à virgule flottante. La base de données vectorielle les enregistre sous la forme d’une série de bits dans le format de stockage interne de la base de données.
Les bases de données vectorielles permettent de récupérer et de stocker efficacement les vecteurs d’incorporation de texte.
Les dimensions du modèle d’intégration d’OpenAI ont été réduites de 12288 à 1536. Il s’agit du nombre de nombres à virgule flottante que contient chaque vecteur « d’intégration de texte ». Le changement réduit considérablement les coûts d’exploitation du nouveau modèle d’incorporation avec des bases de données vectorielles.
Les services de base de données vectorielles sont tarifés en fonction du nombre de vecteurs utilisés par les modèles.
- Le niveau standard de Pinecone commence à partir de 0,0960 $⁵ / pod-heure (avec s1 ou p1-pod, chaque pod s1 convient aux vecteurs 5M 768-dim). Il comprend des collections.
- Le prix de Weavite commence à partir de 0,050 $⁷ par 1 million de dimensions vectorielles.
Les dimensions du modèle Embeddings ont un impact direct sur les coûts de la base de données vectorielles. Les vecteurs de dimension inférieure sont moins chers à stocker. Cet aspect est très important car les solutions sont mises à l’échelle !
Ce didacticiel intègre les vecteurs « d’intégration de mots » d’OpenAI dans une base de données vectorielle commerciale. Peu d’options incluent Faiss, Weavite, tandis que dans ce tutoriel, j’utiliserai Pinecone. Pinecone propose un plan gratuit, suffisant pour terminer ce didacticiel.
La première étape consiste à créer un compte Pinecone et à obtenir la clé API et le nom de l’environnement à partir de votre profil Pinecone. Plus tôt, j’ai installé le paquet Pinecone- et je l’ai importé, il n’est donc plus nécessaire de le refaire. Ainsi, je peux directement définir mes paramètres de connexion :
pinecone.init(
api_key=os.getenv("PINECONE_API_KEY"),
environment="us-west1-gcp")
Je définis ensuite la dimension de mon modèle et le nom de l’index, que je souhaite utiliser pour mes vecteurs. Les dimensions du modèle sont dans ce cas 1536 — correspondant aux dimensions du modèle d’intégration. Je vérifie ensuite, au cas où j’ai déjà un index existant avec ce nom d’index, et le supprime — afin que je puisse en créer un nouveau.
index_name = 'fine-food-reviews-openai-embeddings'
dimensions = 1536
if index_name in pinecone.list_indexes():
pinecone.delete_index(index_name)
Je crée le nouvel index avec une similarité cosinus en raison de son calcul rapide :
pinecone.create_index(name=index_name, dimension=dimensions, metric="cosine")
Je peux ensuite mettre à jour les incorporations de texte d’OpenAI à partir du fichier généré précédemment df-dataframe
. Je dois définir le nom de l’index, afin que Pinecone sache quel index ajouter les données.
meta= [{'combined': line} for line in df['combined']]
vectors = list(zip(df['ProductId'], df['ada_similarity'], meta))upsert_response = index.upsert(
vectors=vectors,
namespace=index_name, values=True, include_metadata=True)
Pinecone reçoit comme résultat le score de similarité avec ada_similarity
, ProductId
et combined
Colonnes. La colonne « combiné » comprend à la fois le résumé et le texte, qui sont transmis à la base de données vectorielle en tant que métadonnées. Ce sont les seules étapes nécessaires pour stocker des données dans une base de données vectorielle !
Je peux maintenant interroger cette base de données vectorielles. Je passe un texte de requête, l’envoie à l’API OpenAI pour obtenir les intégrations de texte. Les nombres vectoriels à virgule flottante sont enregistrés en tant que variable « query_response_embeddings ».
query = "I have ordered these raisins"
response = openai.Embedding.create(input=query, model='text-embedding-ada-002')
query_response_embeddings = response['data'][0]['embedding']
J’interroge ensuite ce vecteur dans la base de données vectorielles, en précisant le nombre de réponses à fournir. Je demande également les métadonnées. J’imprime ensuite les résultats avec :
vector_database_results_matching = index.query([query_response_embeddings], top_k=5, include_metadata=True, include_Values=True,
namespace=index_name)
for match in vector_database_results_matching['matches']:
print(f"{match['score']:.2f}: {match['metadata']['combined']}")
Il en résulte les résultats de la recherche sémantique basée sur la similarité du texte, qui est interrogée directement à partir de la base de données vectorielles.
0.88: Title: Delicious!; Content: I have ordered these raisins multiple times. They are always great and arrive timely. I can't go back to store bought chocolate covered raisins now! Love this product.
0.84: Title: Just what I expected!; Content: I bought these in order to make vanilla and kahlua for Christmas gifts. They were moist, yummy-smelling beans. If these experiments work, I'll be buying again for next year!
0.82: Title: its delicious...; Content: I was surprised at how delicious these toasted chips are. They ship well with little damage. I have ordered them several times. Also they are very fresh. Not one bit was left in the bag when I got through.
0.81: Title: Perfect Gift; Content: I got these to give out in a goodie bag for the holidays, with a couple extras of course. They arrived quickly, the price was right, and they were delicious! I can't wait to give them to all my friends!
0.81: Title: a great product and deal; Content: I looked at all of the reviews and shopped around locally before buying these. Locally the best deal I could find was $6 for 1 oz!!!!! I use them on my cereal, yogurt, granola and even in baking. They are yummy and very good for you. I shared one bag with my two health conscious kids and put the other bag in the freezer so I can dip into as needed. You will not regret buying these. I tried roasting some but prefer the nibs in their natural state. I plan to grind some up and try in place of cocoa powder in recipes.
Le didacticiel a commencé par expliquer la tarification du modèle d’intégration. J’ai couvert la fenêtre contextuelle, l’unification du modèle et les dimensions plus petites. J’ai expliqué l’utilisation du point de terminaison Embedding avec une activité de clustering pratique.
J’ai alors introduit le concept de bases de données vectorielles et leur tarification définie par des dimensions vectorielles. J’ai ensuite illustré l’utilisation des bases de données vectorielles à la fois pour le stockage et la recherche textuelle.
[1] Amazon Fine Food Reviews-ensemble de données. Kagglé.
[2] Modèle d’intégration nouveau et amélioré. OpenAI.
[3] Que sont les jetons et comment les compter ?. OpenAI.
[4] Jetons. adhérer.
[5] Tarif pomme de pin. Pomme de pin.
[6] Thakur et al., 2021. BEIR : une référence hétérogène pour l’évaluation zéro des modèles de recherche d’informations OpenReview.net.