Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 12 Min.

Anpassen oder erweitern?

Um spezifischere Anforderungen zu erfüllen, brauchen Sprachmodelle etwas „Nachhilfe“. Zwei Methoden hierfür sind Fine-Tuning und Retrieval-Augmented Generation (RAG).
Die Entwicklung von künstlicher Intelligenz (KI) hat mit dem Aufkommen von Large Language Models (LLMs) ­einen neuen Meilenstein erreicht [1]. Das betrifft auch die Möglichkeiten, ein solches aufwendig vortrainiertes Modell auf sehr spezialisierte Aufgaben oder Bereiche abzustimmen: Mit Techniken wie Fine-Tuning [2] und Retrieval-Augmented Generation (RAG) [3] lassen sich bestehende Modelle schnell und gezielt anpassen beziehungsweise erweitern, ohne dass das Modell von Grund auf neu trainiert werden muss.Das traditionelle Training nutzt einen festen Datensatz, um das Modell zu trainieren, was zeit- und ressourcenintensiv ist. Heutige Methoden erlauben es, vortrainierte Modelle auf spezifische Anforderungen zuzuschneiden (via Fine-Tuning) oder externe Wissensquellen dynamisch zu integrieren ((via Retrieval-Augmented Genera­tion, RAG). Dies eröffnet eine größere Flexibilität, ohne die Vorteile des vorherigen Trainings zu verlieren.Je nach Anwendungsfall ist die eine oder die andere Vorgehensart mehr oder weniger komplex und sinnvoll. Der Artikel stellt beide Varianten vor, versucht sich an einer praxis­tauglichen Abgrenzung und zeigt mittels Codebeispielen, wie ein RAG-System mit Semantic Kernel funktioniert. Das ist eine gute Fingerübung und eine passende Vorbereitung für eine der künftigen Ausgaben dieser Serie, wenn ein RAG-System mit Azure und Microsofts Copilot umgesetzt werden soll – zwei Varianten, die im Unternehmenskontext erfahrungsgemäß häufig angefragt und implementiert sind.

Fine-Tuning als Modellanpassung

Fine-Tuning ermöglicht es, die Leistung und Genauigkeit eines KI-Modells für spezifische Anwendungsfälle zu optimieren, ohne es komplett neu trainieren zu müssen. Large Language Models (LLMs) wie GPT sind bereits mit enormen Datenmengen trainiert, um eine breite Sprachkompetenz zu entwickeln. Durch Fine-Tuning können sie gezielt mit einem kleineren, domänenspezifischen Datensatz nachtrainiert werden. Ziel ist es, das Modell so anzupassen, dass es präzise und relevant für bestimmte Aufgaben oder Branchen bleibt, ohne die allgemeinen Sprachfähigkeiten zu verlieren.Die Methodik des Fine-Tuning erfordert eine Kombination aus geeigneten Daten, Ressourcen und Tools. Für erfolgreiches Fine-Tuning sind qualitativ hochwertige und repräsentative Datensätze notwendig. Diese sollten gut kuratiert, ausreichend groß und auf den Anwendungsbereich zugeschnitten sein. Das Nachtrainieren von Modellen ist rechenintensiv und erfordert leistungsfähige Hardware, wie GPUs oder TPUs, um die Rechenzeit zu minimieren. Plattformen wie Microsoft Azure, OpenAI oder Hugging Face bieten spezialisierte APIs und Frameworks, die den Fine-Tuning-Prozess erleichtern. Python-Bibliotheken wie PyTorch oder TensorFlow unterstützen die technische Implementierung.Der Fine-Tuning-Prozess umfasst in der Praxis mehrere Schritte:
  • Laden eines vortrainierten Modells: Als Ausgangspunkt wird ein bereits auf großen Datensätzen trainiertes Modell verwendet, wie beispielsweise GPT-4 von OpenAI.
  • Modellanpassung: Das vortrainierte Modell wird nach dem Laden mit einem spezifischen, kleineren Datensatz weiter trainiert. Dabei werden nur bestimmte Gewichte des Modells angepasst, um eine Überanpassung zu vermeiden.
  • Hyperparameter-Optimierung: Wichtige Parameter wie Lernrate und Losgröße werden optimiert, um die besten Ergebnisse zu erzielen.
Die Lernrate bestimmt, wie groß die Schritte sind, die der Optimierungsalgorithmus (zum Beispiel Stochastic Gradient Descent, Adam) bei jedem Iterationsschritt macht, um die Modellparameter zu aktualisieren. Sie ist ein Skalierungsfaktor, der den Grad der Anpassung an den Fehler signalisiert. Oft kommt beim Fine-Tuning eine kleinere Lernrate zum Einsatz als beim vollständigen Training, um bereits gelernte Gewichte nicht drastisch zu ändern und Overfitting zu vermeiden. Die Losgröße (Batch Size) gibt an, wie viele Datenproben gleichzeitig durch das Modell laufen, bevor die Parameter basierend auf dem aggregierten Fehler aktualisiert werden. Beim Fine-Tuning kommen oft mittlere Losgrößen zum Einsatz, da sehr kleine Losgrößen beim Update stärker mit Rauschen belastete Ergebnisse verursachen können und große Losgrößen viel Rechenleistung und Speicher erfordern.Fine-Tuning eröffnet vielfältige Möglichkeiten in unterschiedlichen Bereichen. Unternehmen können Modelle für branchenspezifische Anforderungen wie juristische Texte, medizinische Berichte oder technische Dokumentationen anpassen. Modelle lassen sich so trainieren, dass sie den spezifischen Sprachstil, die Terminologie und die Prozesse eines Unternehmens verstehen. Das führt zu personalisierten und effizienteren Interaktionen mit Kunden. In der Forschung lassen sich LLMs durch Fine-Tuning zum Beispiel auf Fachliteratur anpassen, um gezielte Unterstützung bei der Analyse oder Erstellung wissenschaftlicher Texte zu bieten.

Fine-Tuning am Beispiel

Nehmen wir als konkreten Anwendungsfall ein Technologieunternehmen, das ein Kundensupport-System verbessern möchte. Das Ziel ist es, eingehende Support-Anfragen automatisch in verschiedene Kategorien einzuordnen, um sie effizienter an die zuständigen Abteilungen weiterzuleiten. Für dieses Beispiel dient Googles Gemini-Modell [4] als Ausgangspunkt. Der Datensatz besteht aus einer Sammlung von technischen Support-Anfragen, die in Kategorien wie „Hardware-Probleme“, „Software-Probleme“ und „Kontoprobleme“ eingeteilt sind. Für das Fine-Tuning ist zunächst der Datensatz vorzubereiten. Die Datenstruktur ist in Listing 1 zu sehen. Die Daten sind zu bereinigen und in Token zu überführen, was Listing 2 zeigt. Nun lässt sich das Modell initialisieren und das Fine-Tuning starten, was in Listing 3 abgebildet ist. Ist der Fine-Tuning-Prozess abgeschlossen, kann das Modell für Vorhersagen genutzt werden (siehe Listing 4).
Listing 1: Datenstruktur für den zu trainierenden Datensatz
support_data = [<br/>  {<span class="hljs-string">"query"</span>: <span class="hljs-string">"Mein Laptop startet nicht mehr"</span>, <span class="hljs-string">"category"</span>: <br/>    <span class="hljs-string">"Hardware-Probleme"</span>},<br/>  {<span class="hljs-string">"query"</span>: <span class="hljs-string">"Ich kann mich nicht in mein Konto einloggen"</span>, <span class="hljs-string">"category"</span>: <br/>    <span class="hljs-string">"Kontoprobleme"</span>},<br/>  {<span class="hljs-string">"query"</span>: <span class="hljs-string">"Die Software stürzt ständig ab"</span>, <span class="hljs-string">"category"</span>: <br/>    <span class="hljs-string">"Software-Probleme"</span>},<br/>  # ... weitere Einträge<br/>] 
Listing 4: Nutzung der Daten für eine Vorhersage
def predict_category(query):<br/>  inputs = tokenizer(query, return_tensors="pt")<br/>  outputs = model(**inputs)<br/>  predicted_class = torch.argmax(<br/>    outputs.logits, dim=1).item()<br/>  return list(set([item["category"] for item in <br/>    support_data]))[predicted_class]<br/># Beispielanfrage<br/>test_query = "Meine E-Mails werden nicht <br/>  synchronisiert"<br/>predicted_category = predict_category(test_query)<br/>print(<br/>  f"Vorhergesagte Kategorie: {predicted_category}") 
Listing 3: Der Fine-Tuning-Prozess
from transformers import <br/>  AutoModelForSequenceClassification, Trainer, <br/>  TrainingArguments<br/>import torch<br/>num_labels = len(set([item["category"] for item in <br/>  support_data]))<br/>model = AutoModelForSequenceClassification.from_<br/>  pretrained("google/gemini-base", <br/>  num_labels=num_labels)<br/>training_args = TrainingArguments(<br/>  output_dir="./results",<br/>  num_train_epochs=3,<br/>  per_device_train_batch_size=8,<br/>  per_device_eval_batch_size=8,<br/>  warmup_steps=500,<br/>  weight_decay=0.01,<br/>  logging_dir="./logs",<br/>)<br/>trainer = Trainer(<br/>  model=model,<br/>  args=training_args,<br/>  train_dataset=inputs,<br/>  eval_dataset=inputs,  # In der Praxis würden Sie hier<br/>    # einen separaten Evaluierungsdatensatz verwenden<br/>)<br/>trainer.train() 
Listing 2: Umwandlung der Daten zu Tokens
from transformers import AutoTokenizer<br/>tokenizer = AutoTokenizer.from_pretrained(<br/>  "google/gemini-base")<br/>def preprocess_data(data):<br/>  queries = [item["query"].lower() for item in data]<br/>  categories = [item["category"] for item in data]<br/>  <br/>  tokenized_inputs = tokenizer(queries, padding=True, <br/>    truncation=True, return_tensors="pt")<br/>  return tokenized_inputs, categories<br/>inputs, labels = preprocess_data(support_data) 
Dieses Beispiel zeigt, wie sich ein vortrainiertes Gemini-Modell für die spezifische Aufgabe der Kategorisierung von Support-Anfragen feinabstimmen lässt. In der Praxis kommt verständlicherweise ein deutlich umfangreicherer Datensatz zum Einsatz, als an dieser Stelle in einem Artikel abbildbar ist. Und sehr wahrscheinlich kommen weitere Optimierungen zum Tragen, um die Leistung des Modells und der Kategorisierung zu verbessern.

RAG-Systeme als Modellerweiterung

Retrieval-Augmented Generation (RAG) ist ein Konzept, das die Leistungsfähigkeit von großen Sprachmodellen (LLMs) mit externen Datenquellen kombiniert. Ziel ist es, die Qualität und Relevanz von Antworten durch den Zugriff auf aktuelle und spezifische Informationen zu erhöhen. LLMs sind zwar auf umfangreichen Datensätzen trainiert, können ­ohne zusätzliche Informationen aber veraltet oder unzureichend auf kontextspezifische Anfragen reagieren. RAG löst dieses Problem, indem es zunächst relevante Informationen aus externen Wissensdatenbanken, Dokumenten oder APIs abruft, was der „Retrieval“-Teil ist, und diese dynamisch in die Antwortgenerierung, den „Generation“-Teil, integriert. Dies erfolgt durch eine zweistufige Pipeline: Im ersten Schritt filtert ein Retrieval-Modell relevante Inhalte heraus, die anschließend vom LLM verarbeitet werden. Auf diese Weise bleiben die Modelle flexibel und lassen sich ohne kostspieliges Nachtraining für spezifische Anwendungen nutzen.Ein zentraler Vorteil von RAG besteht darin, dass keine permanente Modelländerung erforderlich ist. Statt ein Modell ständig mit neuen Daten nachzutrainieren, setzt RAG auf die dynamische Einbindung externer Quellen. Dies führt zu einer deutlichen Zeit- und Kosteneinsparung. Darüber hinaus bleibt das System durch die Einbindung aktueller Datenquellen stets auf dem neuesten Stand, was insbesondere in schnelllebigen Branchen von großer Bedeutung ist. Kontextspezifische Antworten können durch den direkten Zugriff auf spezifische Dokumente oder Datenbanken präziser und relevanter gestaltet werden. Zudem verbessert RAG die Skalierbarkeit, da bestehende Modelle für unterschiedliche Anwendungsfälle wiederverwendet werden können, ohne individuelle Anpassungen vornehmen zu müssen.Die Anwendungsbereiche von RAG sind vielfältig. In Wissensdatenbanken, wie sie etwa in technischen Supportsystemen oder internen Unternehmenslösungen genutzt werden, erlaubt RAG den Zugriff auf umfassende und aktuelle Informationen. Unternehmen können so Mitarbeitenden oder Kunden präzise Antworten auf komplexe Fragen bieten.Ein weiteres Einsatzgebiet liegt in der Echtzeit-Dokumentation, beispielsweise in der medizinischen Forschung oder juristischen Analyse, wo stets die neuesten Erkenntnisse berücksichtigt werden müssen.Darüber hinaus wird RAG in Unternehmensanwendungen eingesetzt, etwa bei der Automatisierung von Kundenserviceleistungen oder bei der Erstellung von personalisierten Berichten und Analysen. Auch für die Entwicklung smarter Assistenzsysteme, die kontextspezifisches Wissen benötigen, eignet sich RAG hervorragend.

Fine-Tuning versus RAG-System

Häufig werden Fine-Tuning und RAG-Systeme „in einen Topf geworfen“ beziehungsweise verwechselt. Anhand einiger anschaulicher Vergleiche soll daher der Unterschied zwischen den Methoden aufgezeigt werden, um eine passende Auswahl im Projektkontext zu ermöglichen. Die Vergleiche verdeutlichen, dass Fine-Tuning eine tiefgreifende Anpassung des Modells selbst darstellt, während RAG eine flexiblere Methode ist, um das Modell mit externen Informationen anzureichern, ohne seine Grundstruktur zu verändern.So lässt sich Fine-Tuning beispielsweise mit einem Auswendiglernen von Informationen vergleichen, bei dem das Modell spezifisches Wissen internalisiert. RAG dagegen ermöglicht dem Modell, bei Bedarf auf externe Informationen zuzugreifen, ähnlich dem Nachschlagen in einem Buch. Auch die intensive Schulung eines Mitarbeiters, um diesem neues Wissen zu vermitteln, kann als Analogie zum Fine-Tuning dienen. RAG würde dem Mitarbeiter dagegen Arbeitsanweisungen und Nachschlagewerke zur Verfügung stellen, die er bei Bedarf konsultieren kann. Und noch ein Beispiel aus einem ganz anderen Bereich: Man kann sich Fine-Tuning auch wie das Maßschneidern eines Anzugs vorstellen, während RAG eher Accessoires zu einem Standardanzug hinzufügt, um ihn für bestimmte Anlässe zu optimieren.

Ein erstes RAG-System mit Semantic Kernel

Das nachfolgende Beispiel demonstriert die Implementierung eines RAG-Systems mit Semantic Kernel in C#. Dieses System lässt sich leicht mit neuen Informationen aktualisieren, ohne das gesamte Modell neu trainieren zu müssen. Das System kann zudem stets auf die neuesten Informationen zugreifen, die in der Vektordatenbank gespeichert sind. Die Implementierung ermöglicht es darüber hinaus, die Quellen der verwendeten Informationen nachzuvollziehen, was bei Fine-Tuning schwieriger ist. Die RAG-Implementierung erfordert weniger Rechenressourcen als das kontinuierliche Fine-Tuning großer Sprachmodelle. RAG kann leicht an spezifische Domänen oder Anwendungsfälle angepasst werden, indem relevante Dokumente in die Wissensbasis aufgenommen werden.Das vorgestellte Beispiel zeigt, wie Dokumente in Chunks aufgeteilt, als Vektoren gespeichert und bei Abfragen effizient abgerufen werden können, um kontextbezogene und präzise Antworten zu generieren. Zunächst initialisieren wir den Semantic Kernel und die benötigten Dienste, was in Listing 5 zu sehen ist. Dieser Code-Abschnitt erstellt den Kernel inklusive der notwendigen Konfiguration und den notwendigen Diensten. Zum Einsatz kommt OpenAI für die Textvervollständigung und die Generierung der Einbettungen (Text Embeddings). Außerdem initialisiert der Code einen Qdrant-Speicher [5] als Vektordatenbank für das RAG-System.
Listing 5: Initialisieren des Kernels in C# für Semantic Kernel
using Microsoft.SemanticKernel;<br/>using Microsoft.SemanticKernel.Connectors.Memory.Qdrant;<br/>using Microsoft.SemanticKernel.Memory;<br/>// Initialisierung des Kernels<br/>IKernel kernel = new KernelBuilder()<br/>  .WithLoggerFactory(ConsoleLogger.LoggerFactory)<br/>  .WithOpenAIChatCompletionService(<br/>    "gpt-3.5-turbo",<br/>    "YOUR_OPENAI_API_KEY")<br/>  .WithOpenAITextEmbeddingGenerationService(<br/>    "text-embedding-ada-002",<br/>    "YOUR_OPENAI_API_KEY")<br/>  .Build();<br/>// Initialisierung des Qdrant-Speichers<br/>const string qdrantEndpoint = "http://localhost:6333";<br/>const string vectorSize = "1536"; // Für OpenAI Ada-002<br/>var qdrantMemoryStore = <br/>  new QdrantMemoryStore(qdrantEndpoint, vectorSize);<br/>// Hinzufügen des Speichers zum Kernel<br/>kernel.UseMemory(qdrantMemoryStore); 
Als Nächstes ist eine Funktion zum Import und Speichern der Dokumente notwendig. Listing 6 zeigt den erforderlichen C#-Code. Diese Funktion teilt ein Dokument in Chunks auf und speichert diese im Qdrant-Speicher. Wir verwenden eine maximalen Chunk-Size von 1000 Zeichen, um die Effi­zienz der Einbettungen zu gewährleisten.
Listing 6: Importieren der Dokumente als Chunks in den Vektorstore
public async Task ImportDocumentAsync(<br/>    string documentText, string documentId)<br/>{<br/>  const string memoryCollectionName = <br/>    "DocumentCollection";<br/>  const int maxChunkSize = 1000;<br/>  var lines = documentText.Split('\n');<br/>  var currentChunk = new StringBuilder();<br/>  var chunkIndex = 0;<br/>  foreach (var line in lines)<br/>  {<br/>    if (currentChunk.Length + line.Length > <br/>        maxChunkSize)<br/>    {<br/>      await kernel.Memory.SaveInformationAsync(<br/>        memoryCollectionName,<br/>        currentChunk.ToString(),<br/>        $"{documentId}-chunk-{chunkIndex}");<br/>      currentChunk.Clear();<br/>      chunkIndex++;<br/>    }<br/>    currentChunk.AppendLine(line);<br/>  }<br/>  if (currentChunk.Length > 0)<br/>  {<br/>    await kernel.Memory.SaveInformationAsync(<br/>      memoryCollectionName,<br/>      currentChunk.ToString(),<br/>      $"{documentId}-chunk-{chunkIndex}");<br/>  }<br/>} 
Chunks sind ein zentraler Bestandteil von RAG-Systemen, da sie Effizienz und Genauigkeit steigern. Große Dokumente lassen sich besser durchsuchen, wenn sie in kleinere Teile (Chunks) zerlegt werden, wodurch spezifische Informationen präziser gefunden werden können. Sprachmodelle haben begrenzte Kontextgrößen, und Chunks ermöglichen eine effiziente Verarbeitung innerhalb dieser Grenzen, was die Qualität des Abgleichs zwischen Anfrage und Dokument verbessert. Sie erhöhen die Relevanz, indem sie irrelevante Teile großer Dokumente ausblenden und sich auf die wesentlichen Abschnitte konzentrieren. Zudem reduzieren sie die Belastung der Systemressourcen, da kleinere Einheiten die Verarbeitung beschleunigen.Die optimale Chunk-Größe in einem RAG-System hängt von einer Balance zwischen Informationsgehalt und Modellgrenzen ab. Die Kontextgröße des Modells bestimmt die maximale Länge eines Chunks, beispielsweise 2048 Token bei GPT-3. Chunks sollten etwa 50 bis 70 Prozent dieser Kapazität nutzen, um Platz für Anfragen und Überlappungen zu lassen. Überlappende Chunks, etwa durch ein Sliding-Window-Verfahren, helfen, wichtige Informationen an Chunk-Grenzen nicht zu verlieren. Dabei sollten Chunks thematisch oder inhaltlich zusammenhängend sein, um eigenständige Antworten zu ermöglichen. Die ideale Größe variiert je nach Anwendungsfall und erfordert Tests, bei denen Metriken wie Relevanz und Genauigkeit berücksichtigt werden. Als Heuristik gelten 200 bis 300 Wörter für allgemeine Texte oder 100 bis 150 Wörter für stark strukturierte Inhalte als sinnvoll.Damit sind die initialen Vorbereitungen abgeschlossen. Zeit, um die primäre Funktion für die RAG-Abfrage zu implementieren. Dazu werfen wir einen Blick auf den Code in Listing 7. Diese Funktion führt verschiedene Schritte aus. Zunächst sucht der Code nach relevanten Dokumenten-Chunks basierend auf der Abfrage. Aus diesen relevanten Chunks lässt sich dann ein Kontext erstellen. Unter Verwendung dieses Kontexts und der ursprünglichen Abfrage lässt sich anschließend eine Antwort erzeugen, die das Sprachmodell berechnet.
Listing 7: Die primäre RAG-Funktion für eine RAG-Abfrage
public async Task<string> QueryDocumentsAsync(<br/>    string query)<br/>{<br/>  const string memoryCollectionName = <br/>    "DocumentCollection";<br/>  const int maxRelevantChunks = 5;<br/>  var relevantMemories = await kernel.Memory.SearchAsync(<br/>    memoryCollectionName, <br/>    query, <br/>    limit: maxRelevantChunks, <br/>    minRelevanceScore: 0.7);<br/>  var context = new StringBuilder();<br/>  context.AppendLine("Relevant information:");<br/>  foreach (var memory in relevantMemories)<br/>  {<br/>    context.AppendLine(memory.Metadata.Text);<br/>  }<br/>  context.AppendLine("Query: " + query);<br/>  var promptTemplate = @"<br/>    Based on the following information and query, <br/>      provide a concise and accurate answer:<br/>    {context}<br/>    Answer:";<br/>  var prompt = promptTemplate.Replace(<br/>      "{context}", context.ToString());<br/>  var result = <br/>    await kernel.InvokeSemanticFunctionAsync(prompt);<br/>  return result.ToString();<br/>} 
Listing 8 zeigt die Nutzung des RAG-Systems unter der Annahme, dass sich der zuvor gezeigte Code in der Klasse RAGSystem befindet. Zunächst lassen sich die notwendigen Dokumente importieren, was in einem realen System nur einmal ini­tial passiert und dann lediglich bei Änderungen. Anschließend ist eine Abfrage möglich, um Informationen aus dem RAG-System zu beziehen. Diese Informationen basieren auf der Anfrage und dem ermittelten Kontext der relevanten Dokumente.
Listing 8: Nutzung des RAG-Systems in C#
class Program<br/>{<br/>  static async Task Main(string[] args)<br/>  {<br/>    var ragSystem = new RAGSystem(); // Angenommen, <br/>      // die obigen Methoden sind in dieser Klasse<br/>    // Dokumente importieren<br/>    await ragSystem.ImportDocumentAsync(<br/>      "Inhalt von Dokument 1...", "doc1");<br/>    await ragSystem.ImportDocumentAsync(<br/>      "Inhalt von Dokument 2...", "doc2");<br/>    // RAG-Abfrage durchführen<br/>    var query = "Was ist die Hauptaussage der <br/>      importierten Dokumente?";<br/>    var answer = <br/>      await ragSystem.QueryDocumentsAsync(query);<br/>    Console.WriteLine($"Frage: {query}");<br/>    Console.WriteLine($"Antwort: {answer}");<br/>  }<br/>} 

Relevante Chunks finden

Das Finden relevanter Chunks in einem Vektorstore basiert auf der semantischen Ähnlichkeit zwischen der Benutzeranfrage (Query) und den gespeicherten Chunks. Dazu wird zunächst jeder Chunk eines Dokuments mit einem Embedding-Modell, wie OpenAI Embeddings [6] oder Sentence-BERT [7], in einen Vektor umgewandelt, der dessen semantische Bedeutung repräsentiert. Diese Vektoren werden zusammen mit Metadaten wie Chunk-ID oder Dokumentquelle im Vektorstore gespeichert.Zur Laufzeit transformiert der Code die Benutzeranfrage ebenfalls in einen Vektor, wobei dasselbe Embedding-Modell zum Einsatz kommt, um die Anfrage in den gleichen semantischen Vektorraum zu projizieren. Anschließend vergleicht der Vektorstore den Query-Vektor mit den gespeicherten Vektoren, typischerweise anhand von Ähnlichkeitsmaßen wie Kosinusabstand, Euklidischer Distanz oder Dot-Produkt. Um die Suche bei großen Datenmengen effizient zu gestalten, kommen Algorithmen für Approximate Nearest Neighbor (ANN) wie FAISS, Milvus oder Pinecone zum Einsatz.Die gefundenen Chunks lassen sich anhand ihrer Ähnlichkeit zum Query-Vektor sortieren, wobei die höchstbewerteten als die relevantesten gelten. Optional sind Filter nützlich, um nur Chunks mit bestimmten Metadaten, etwa zu einer spezifischen Kategorie oder einem Zeitrahmen, einzubeziehen. Schließlich gibt der Vektorstore die relevantesten Chunks zurück, die entweder direkt als Antwort verwendet oder für weitere Verarbeitungsschritte wie Zusammenfassung oder Antwortgenerierung durch ein Sprachmodell genutzt werden.Beispielsweise könnte eine Anfrage wie „Was ist die Funktion von Mitochondrien?“ in einen Vektor umgewandelt werden. Der Vektorstore durchsucht daraufhin seine gespeicherten Vektoren nach ähnlichen und gibt relevante Chunks mit Informationen über Mitochondrien zurück. Dieser Ansatz ermöglicht es RAG-Systemen, präzise und effizient relevante Informationen abzurufen.

Frameworks

Mit einer Vielzahl von verfügbaren Frameworks wie Semantic Kernel, LangChain oder PromptFlow stehen Entwicklern leistungsstarke Tools zur Verfügung, um die jeweiligen Stärken von Fine-Tuning und RAG optimal auszuschöpfen.Semantic Kernel ist ein Open-Source-Framework von Microsoft, das modulare KI-Funktionalitäten wie Sprachmodelle oder APIs als „Skills“ integriert. Es zeichnet sich durch Gedächtnisfunktionen und Plug-in-basierte Architekturen aus, wodurch personalisierte und wiederverwendbare Anwendungen möglich sind. Das Framework ist ideal für die Orchestrierung verschiedener KI-Modelle in Chatbots, digitalen Assistenten und Workflow-Automatisierung.PromptFlow optimiert Prompts für Sprachmodelle (LLMs) und bietet eine visuelle Benutzeroberfläche zur Workflow-Erstellung und -Testung. Dank der Integration in Azure-AI-Services unterstützt es Unternehmen mit Tools wie A/B-Tests zur systematischen Verbesserung von Modellausgaben. PromptFlow ist wie gemacht für Teams, die KI-generierte Flows optimieren möchten.LangChain ist ein Open-Source-Framework zur Entwicklung kontextspezifischer KI-Anwendungen. Es kombiniert LLMs mit externen Datenquellen wie Datenbanken und APIs und ist bekannt für Retrieval-Augmented Generation (RAG), das dynamische und aktuelle Antworten ermöglicht. Das Framework ist besonders geeignet für datenintensive Anwendungen wie Wissensmanagement, Echtzeit-Analysen oder personalisierte Assistenten. Tabelle 1 fasst die Eigenschaften und Unterschiede in den Ansätzen zusammen.

Tabelle 1: Vergleich der Ansätze von Semantic Kernel, PromptFlow und LangChain

Aspekt OllamaSharp LLamaSharp
Abhängigkeit Ollama API erforderlich Direkte Nutzung von Modellen
Installation Einfach über NuGet Mehr manuelle Einrichtung
Unterstützte Modelle Modelle des Ollama-Ökosystems Flexible Unterstützung für viele Modelle
Multimodale Fähigkeiten Optimiert für Bild- und Textverarbeitung Textfokussiert, Anpassung für Vision-Modelle möglich
Performance Abhängig von der Ollama-Serverkonfiguration Optimierbar durch direkte Modellverwaltung

Fazit und Ausblick

Fine-Tuning und Retrieval-Augmented Generation sind leistungsstarke Ansätze zur Anpassung und Erweiterung großer Sprachmodelle. Fine-Tuning erlaubt eine gezielte Spezialisierung von Modellen auf spezifische Anforderungen, während RAG durch die dynamische Integration externer Wissensquellen flexible und aktuelle Anwendungen ermöglicht.Beide Ansätze haben klare Stärken: Fine-Tuning glänzt bei hochspezifischen und wiederkehrenden Aufgaben, bei denen maximale Präzision erforderlich ist, während RAG-Systeme eine effiziente und skalierbare Lösung für kontextabhängige und dynamische Anforderungen bieten.Der Artikel zeigt nicht nur die technischen Grundlagen, sondern auch praxisnahe Umsetzungen und verdeutlicht die Einsatzmöglichkeiten in verschiedenen Branchen. Das dient als gute Vorbereitung für eine nächste Ausgabe, in der eine konkrete RAG-Umsetzung für ein Unternehmen im Fokus steht. Dieses Mal mit Microsofts Copilot als vergleichende Implementierung.

Fussnoten

  1. Fabian Deitelhoff, Über Sprache lernen, dotnetpro 12/2024, Seite 74 ff., http://www.dotnetpro.de/A2412KIundData
  2. Weitere Informationen zu Fine-Tuning, https://www.it-p.de/lexikon/fine-tuning/
  3. Weitere Informationen zu RAG, http://www.dotnetpro.de/SL2502KIundData1
  4. Googles Gemini-Modell, https://gemini.google.com/
  5. Qdrant Vektordatenbank, https://qdrant.tech
  6. OpenAI Embeddings, http://www.dotnetpro.de/SL2502KIundData2
  7. SBERT, https://sbert.net

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige