Sorgfältiger als ChatGPT: Embeddings mit Azure OpenAI, Qdrant und Rust

Seite 2: Ermitteln der Embeddings in Azure

Inhaltsverzeichnis

Im ersten Schritt ermittelt die Beispielanwendung die Embeddings für die Textabschnitte aus dem SQuAD-Dataset. OpenAI empfiehlt derzeit für Embeddings das LLM "text-embedding-ada-002". Daneben stellt das Unternehmen die API Create Embeddings bereit, um Embeddings automatisch zu generieren.

Die in der Beispielanwendung genutzten Azure-OpenAI-Dienste sind funktional identisch mit den Web-APIs von OpenAI. Um Erstere statt Letztere zu verwenden, ist lediglich eine Anpassung eines Teils der URL erforderlich. Die konkrete Azure-URL findet sich im Azure-Portal. In der Azure-Dokumentation beschreibt Microsoft, wie die APIs zu verwenden sind.

Auch preislich existiert bezüglich des "text-embedding-ada-002"-Modells zum Generieren von Embeddings praktisch keinen Unterschied. Der Preis pro 1000 Token lag beim Schreiben dieses Artikels bei 0,000363 Euro. Der oben gezeigte Artikelausschnitt über das chemische Element Froxium benötigt etwa 170 Token, um einen Embedding-Vektor zu erzeugen und käme somit auf 0,006171 Cent. Für eine Wissensdatenbank mit 10.000 solcher Textabschnitte lägen die Kosten zum Generieren der Embeddings bei rund 62 Cent. Das ist vernachlässigbar gering: Da die Anwendung die Embeddings in einer Vektordatenbank speichert, fallen die Kosten nur einmalig an.

Damit die OpenAI-Embeddings-API in Azure bereitstehen, ist es erforderlich, das gewünschte LLM in Azure auszurollen.

Der Screenshot zeigt exemplarisch die Model Deployments des "text-embedding-ada-002"-Modells und des Text-Completion-Modells "gpt-4" im Azure-Portal (Abb. 4).

Anschließend steht in Azure ein Playground zur Verfügung, der dem von OpenAI entspricht. Microsoft nennt ihn Azure OpenAI Studio.

Die vom "text-embedding-ada-002"-Modell erzeugten Embedding-Vektoren haben 1.536 Dimensionen. Sie sind von Haus aus normalisiert, haben also immer eine Länge von 1. Das ist für den Vergleich von Embedding-Vektoren wichtig. Die Ähnlichkeit von Vektoren ergibt sich in der Regel über die Kosinus-Ähnlichkeit (Cosine Similarity) beziehungsweise der Kosinus-Distanz (Cosine Distance). Da die vom OpenAI-LLM erzeugten Embeddings normalisiert sind, entspricht die Kosinus-Ähnlichkeit dem Standardskalarprodukt (Dot Product). Das vereinfacht und beschleunigt das Berechnen der Distanz.

Zur Veranschaulichung soll ein Codebeispiel zum Berechnen der Kosinusdistanz der Embedding-Vektoren zweier Texte (input1 und input2) in Rust dienen. Die Methode generate_embeddings dient dazu, die Embeddings mit der Azure-OpenAI-Web-API zu berechnen. Da sie nur einen simplen Web-Request durchführt, fehlt die Methode im folgenden Codeausschnitt:

async fn get_distance(input1: String, 
                      input2: String, 
                      model: String) -> Result<()> {
  // Generate the embeddings for both 
  // input strings in parallel
  let (r1, r2) = join!(
    generate_embeddings(input1, model.clone()),
    generate_embeddings(input2, model)
  );

  // Extract embedding vectors from API results
  let d1 = &r1?.data[0].embedding;
  let d2 = &r2?.data[0].embedding;

  // Definition of cosine distance - see 
  // https://en.wikipedia.org/wiki/Cosine_similarity
  // Embeddings from OpenAI are always normalized
  // to length of 1. Therefore, the cosine
  // similarity is equal to the dot product.
  let dot_prod = d1
    .into_iter()
    .enumerate()
    .map(|(ix, v)| v * d2[ix])
    .sum::<f32>();

  // Cosine distance is 1 - cosine similiarity.
  println!("{}", 1f32 - dot_prod);

  Ok(())
}

Der Code dient lediglich zur Veranschaulichung des Prinzips und nicht als Vorlage für den Praxiseinsatz. Es gibt Bibliotheken wie nalgebra, die die entsprechende Logik eingebaut haben. Sie sind bedeutend schneller als diese naive Umsetzung, da sie SIMD (Single instruction, multiple data) einsetzen. Für das Beispiel ist es allerdings nicht erforderlich, die Distanz manuell zu berechnen: Das übernimmt die Vektordatenbank Qdrant.