Mit proprietären Daten trainierte KI-Chat-Anwendung

07.12.2023 | B1 Systems in howto

Vektorspeicher

Ein Vektorspeicher ist ein spezialisiertes Datenspeichersystem, das für die effiziente Speicherung und den Abruf von hochdimensionalen numerischen Vektoren konzipiert ist. Diese Vektoren können verschiedene Arten von Daten repräsentieren, z. B. Textdokumente, Bilder oder Benutzerpräferenzen. Vektorspeicher werden häufig in Anwendungen wie Empfehlungssystemen, Informationsrückgewinnung und maschinellem Lernen verwendet, wo Ähnlichkeitssuche oder andere vektorbasierte Operationen von entscheidender Bedeutung sind. Sie verwenden in der Regel Indizierungstechniken und Ähnlichkeitsmetriken, um eine schnelle und genaue Suche und einen Vergleich von Vektoren zu ermöglichen.

Vektorspeicher können große Sprachmodelle ergänzen, in dem sie Informationen speichern, auf die ein Modell sehr schnell und effizient zugreifen kann. Damit erhält ein Modell mehr Wissen. Außerdem wird damit die Wahrscheinlichkeit von fehlerhaften Antworten reduziert. Es können große Datenmengen in einer Vektordatenbank abgelegt werden, z. B. Dokumentensammlungen.

Wie funktioniert der Vektorspeicher?

FAISS, die Abkürzung für Facebook AI Similarity Search, ist eine hochmoderne Bibliothek, mit der wir die Herausforderungen der effizienten Suche und des Abrufs hochdimensionaler Vektoren bewältigen können. Diese Technologie ist von zentraler Bedeutung bei der Arbeit mit umfangreichen Sprachmodellen wie LLama2 oder GPT-3.

Stellen Sie sich vor, Sie haben einen riesigen Datensatz mit Textdarstellungen, wobei jedes Textdokument in einen hochdimensionalen numerischen Vektor umgewandelt wird. Diese Vektoren kodieren die semantische Bedeutung und die Beziehungen innerhalb des Textes. Ziel ist es, die Vektoren so zu speichern, dass bei einer Abfrage schnell der ähnlichste Kontext gefunden werden kann. Genau hier kommen FAISS und sein Vektorspeicher ins Spiel.

Schauen wir uns an, wie FAISS-Vektorspeicher funktionieren:

  1. Einlesen der Daten: Wir beginnen mit der Einspeisung unserer Vektoren in den FAISS-Vektorspeicher. Diese Vektoren sind in der Regel Einbettungen, die von unserem Sprachmodell (LLama2 oder GPT-3) aus den Textdaten erzeugt werden. Diese Einbettungen sind dichte, hochdimensionale Vektoren, wobei jede Dimension spezifische Merkmale des Textes kodiert.
  2. Indizierung: FAISS baut einen intelligenten Index auf, der diese Vektoren effizient organisiert. Diese Indexstruktur wurde sorgfältig entworfen, um ein schnelles Auffinden der Vektoren zu ermöglichen, die einer bestimmten Abfrage am ähnlichsten sind. Anstatt den gesamten Datensatz zu durchsuchen, können wir mit FAISS schnell auf potenzielle Übereinstimmungen zoomen.
  3. Ähnlichkeitssuche: Dies ist der Kern der Operation. Wenn wir einen neuen Kontext oder eine neue Frage haben, wandeln wir sie in eine Vektordarstellung um und verwenden dabei dasselbe Modell, das die ursprünglichen Einbettungen erzeugt hat. Anschließend suchen wir mit FAISS nach den ähnlichsten Vektoren im Index. Diese ähnlichen Vektoren entsprechen Kontexten in unserem Datensatz, die unserer Anfrage semantisch ähnlich sind.
  4. Bewertung und Ranking: FAISS kann Ähnlichkeitsbewertungen zwischen dem Abfragevektor und den gespeicherten Vektoren unter Verwendung verschiedener Metriken wie Cosinus-Ähnlichkeit oder euklidischer Distanz berechnen. Diese Werte helfen, die Ergebnisse nach Relevanz zu ordnen. Die ähnlichsten Kontexte stehen an der Spitze, was die Auswahl des für unsere Aufgabe am besten geeigneten Kontexts erleichtert.
  5. Effiziente Speicherung: FAISS nutzt Techniken wie Quantisierung und Komprimierung, um Vektoren effizient zu speichern. Dadurch werden die Speicheranforderungen minimiert, ohne die Suchgenauigkeit zu beeinträchtigen.
  6. Skalierung: Der Umgang mit großen Datensätzen ist eine Herausforderung, aber FAISS ist dieser Aufgabe gewachsen. Es ist auf Skalierbarkeit ausgelegt und nutzt verteilte Systeme und parallele Verarbeitung, um auch bei der Verarbeitung umfangreicher Daten Antworten mit geringer Latenz zu liefern.

Sobald wir mit FAISS den relevantesten Kontext identifiziert haben, können wir ihn zusammen mit unserer Frage einem großen Sprachmodell (LLama2 oder GPT-3) vorlegen. Dieser zweistufige Prozess – zunächst die Ermittlung des Kontexts mit Hilfe des Vektorspeichers und dann die Verwendung eines Sprachmodells zur Erstellung einer Antwort – ermöglicht es uns, die Leistungsfähigkeit von Sprachmodellen für die Generierung von Antworten zu nutzen, die kontextuell relevant und kohärent sind.

Zusammenfassend lässt sich sagen, dass FAISS und sein Vektorspeicher eine robuste Grundlage für die Verwaltung hochdimensionaler Einbettungen im Zusammenhang mit großen Sprachmodellen bieten. Es zeigt, wie fortschrittliche Technologie die Arbeit mit Sprachdaten verbessern und effizienter sowie effektiver machen kann. Durch die Nutzung von FAISS und die Kopplung mit hochentwickelten Sprachmodellen können wir neue Möglichkeiten für das Verständnis und die Erzeugung natürlicher Sprache erschließen.

Große Sprachmodelle

Große Sprachmodelle im Detail

Wie eingangs bereits erwähnt, sind große Sprachmodelle, oder auch Large Language Models (LLMs) mit Daten trainierte neuronale Netzwerke, welche in eine besondere Architektur eingebettet sind. Als solche wird zumeist die Transformer-Architektur (oder eine Abwandlung) gewählt, die von Google-Wissenschaftlern im Jahr 2017 erstmals veröffentlicht wurde. Seitdem hat die Transformer-Architektur eine beispiellose Erfolgsgeschichte erlebt, die mit vielen weiteren technischen Verbesserungen sowie zu den berühmten großen Sprachmodellen, wie GPT-4 (ChatGPT) geführt hat.

Training großer Sprachmodelle

Große Sprachmodelle nutzen die Fähigkeit neuronaler Netze, Eingabedaten mit einem erlernten Modell der Welt zu assozieren und darauf basierend sinnvolle Ausgbeinformationen zu generieren. Dafür werden die Modelle mit gigantischen Textmengen trainiert und bekommen dabei die Aufgabe, die in den Trainingsdaten enthaltenen Texte Wort für Wort zu vervollständigen. Das heißt, sie treffen stets eine Vorhersage für das nächste Wort. Der Clou dabei ist, dass man über semantische Beziehungen (Embeddings) ausrechnen kann, wie weit ein vorhergesagtes nächstes Wort vom tatsächlich gesuchten Wort entfernt liegt. Dieser Fehlerwert wird dann dazu genutzt, die vielen Milliarden Gewichtungen innerhalb des künstlichen neuronalen Netzwerks des Modells etwas anzupassen, so dass beim nächsten Versuch das Modell mit einer kleinen Wahrscheinlichkeit ein etwas besseres Ergebnis liefert.

Wenn man diesen Prozess nun mit den gigantischen Textmengen des Internets und vielen weiteren großen Textdatenbanken wiederholt, lernt das Modell auf diese Weise Sprache, Zusammenhänge und ein Verständnis der Welt. Damit ist es dann irgendwann in der Lage, auf natürlich sprachliche Eingaben sinnvolle natürlich sprachliche Ausgaben zu liefern, die noch dazu kontextbezogen und in passenden Zusammenhängen erfolgen.

In diesem Stadium wird ein Modell auch Basismodell oder foundational model genannt. Es ist noch nicht besonders gut in der Lage, auf Fragen zu antworten oder Dialoge zu führen. Vielmehr versucht das Modell immer einen Text weiter zu führen, den es als Eingabe erhält.

Um aus einem Basismodell einen Chatbot zu machen, sind noch weitere Trainingsschritte notwendig. In diesem Nachtraining wird dem Modell beigebracht, wie man Dialoge führt, auf Fragen antwortet und viele andere Dinge, die in der Interaktion mit Nutzern notwendig sind. Für das Nachtraining kommt meistens die Trainingsmethode RHLF (Reinforcement Learning with human feedback) zum Einsatz. Dabei bekommt das Modell das Ziel, Belohnungen zu maximieren, die es wiederum von einem eigenständigen Bewertungsmodell erhält. Das Bewertungsmodell bewertet dabei automatisch die Ausgaben des zukünftigen Chatbots, also beispielsweise wie gut Nutzerdialoge geführt werden. Dabei kommt zusätzlich menschliches Feedback zum Zug: Menschliche Nutzer bewerten die Ausgaben des Modells und geben dazu Feedback. Die Bewertungen und das Feedback werden wiederum als Grundlage für das Bewertungsmodell und damit für das finale Training des künftigen Chatbots verwendet. Nachdem das Nachtraining abgeschlossen ist, verhält sich das Modell wie die uns bekannten Chatbots. Sie können natürlichsprachliche Dialoge mit menschlichen Nutzern führen und dabei auf ihr erlerntes Wissen und ggf. weitere (emergente) Fähigkeiten zurückgreifen.

Sprachmodelle und “Intelligenz”

Eine verbreitete Meinung ist, dass diese Modelle nur bessere Auto-Textvervollständiger wären, die mittels Statistik bzw. Stochastik nächste Worte eines Satzes “erraten”. In gewisser Weise stimmt das auch. Denn die Textgenerierung funktioniert genau nach diesem Prinzip: Wörter mit einer bestimmten Wahrscheinlichkeit auf Basis statistischer Berechnungen vorherzusagen. Dennoch erlauben Assoziationen der Eingabedaten mit erlernten tieferen Konzepten im Modell ein maschinelles Verständnis der Anfrage. Daher sind die erzeugten Ausgaben auch wieder sinnvolle Antworten zu den Eingaben, beispielsweise zu einer Frage. Dabei gilt: Je besser das Modell, desto besser die Antworten.

Ob es sich dabei bereits um “Intelligenz” im eigentlich Sinne handelt, wird lebhaft diskutiert. Ein wichtiges Argument dafür, den großen Sprachmodellen eine Intelligenz abzusprechen ist das weit verbreitete Problem der “Halluzinationen”. Damit ist gemeint, dass Sprachmodelle dazu tendieren, eine plausibel klingende Antwort zu generieren, auch wenn sie etwas nicht wissen. Aber nicht nur das: Die Antwort wird dann gerne sehr plausibel formuliert und umfangreich ausgeführt, so dass es für einen Nutzer schwierig sein kann, eine korrekte von einer fehlerhaften Antwort zu unterscheiden. Allerdings haben Forschung und Entwicklung diesbezüglich in den letzten Monaten große Fortschritte gemacht, so dass die Zahl der “Halluzinationen” der neuesten LLM-Generationen bereits deutlich reduziert werden konnte. In OpenAI’s GPT-4 Modell kommen Halluzinationen beispielsweise nur noch selten vor. Es ist davon auszugehen, dass dieses Problem mit künftigen LLM-Generationen eine immer geringere Rolle spielen wird.

Wie man ein Modell auswählt

Im Bereich der künstlichen Intelligenz und des maschinellen Lernens ist die Auswahl des richtigen Modells für Ihre Aufgabe vergleichbar mit der Auswahl des perfekten Werkzeugs. Der Auswahlprozess umfasst eine sorgfältige Analyse verschiedener Faktoren, von der Art Ihrer Anwendung bis hin zur zugrunde liegenden Architektur und den Optimierungsmethoden.

Grundsätzlich muss zwischen propriäteren und Open Source Modellen unterschieden werden. Proprietäre Modelle laufen normalerweise in einer Cloud und nicht lokal innerhalb einer Organisation. Die bekannten Modelle sind die GPT-Modelle von OpenAI. Aber auch Firmen wie Anthropic, Claude oder die deutsche Firma Aleph Alpha seien hier beispielhaft erwähnt. Im Gegensatz dazu können Open Source Modelle auch lokal innerhalb der eigenen Organisation ausgeführt werden. Selbstverständlich ist auch ein Betrieb in einer Cloud möglich. Ein weiterer Vorteil der Open Source Modelle ist die Quelloffenheit. Die Gewichtungen der Modelle können heruntergeladen und z. B. mittels Fine-Tuning oder generischem Modelltraining verändert bzw. mit einem Vektor-Store verküpft werden.

Im nächsten Kapitel werden wir die wichtigsten Überlegungen zur Auswahl eines geeigneten Modells untersuchen. Wir werden uns mit Modelltypen und architektonischen Details befassen und sogar Modellnamen entschlüsseln, um ein tieferes Verständnis ihrer Fähigkeiten zu erlangen.

Modelltypen: Chat, QA, etc.

Der erste Schritt bei der Modellauswahl besteht darin, die Art des Modells zu bestimmen, die Ihrer Aufgabe entspricht. Modelle können für verschiedene Zwecke spezialisiert sein, wie z. B. Chatbots (für die Teilnahme an Unterhaltungen), Frage-Antwort-Systeme (zum Abrufen bestimmter Informationen aus Text) und mehr. Das Verständnis des Anwendungsfalls ist von entscheidender Bedeutung, da jede Art von Modell für bestimmte Aufgaben abgestimmt und optimiert ist.

Betrachten wir zum Beispiel die folgenden Modelltypen:

Chat-Modelle: Diese Modelle sind für natürliche und ansprechende Unterhaltungen konzipiert. Sie zeichnen sich dadurch aus, dass sie während interaktiver Chats kohärente und kontextbezogene Antworten generieren


Dies sind einige Open Source Chat-Modell Beispiele:
https://huggingface.co/meta-llama/Llama-2-70b-chat-hf
https://huggingface.co/meta-llama/Llama-2-13b-chat-hf
https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML

QA Modelle: Question-answering Modelle sind darauf zugeschnitten, Antworten aus einem gegebenen Kontext oder Dokument als Antwort auf Benutzeranfragen zu extrahieren. Sie sind besonders nützlich für das Beschaffen von Informationen.


Dies sind QA Modell Beispiele:
https://huggingface.co/dave-does-data/llama-2-7B-qa-fine-tune
https://huggingface.co/Ayushnangia/Llama-2-7b-QA-GPTQ

Llama2 & Vector Store

img.png

In diesem Schema wird die “Benutzerfrage” in “Einbettungen” umgewandelt, die dann zur Kontext-Suche im “FAISS-Vektorspeicher” verwendet werden. Der ähnlichste Kontext wird an “LLama2”, ein großes Sprachmodell, weitergeleitet, um eine kontextbezogene Antwort zu erhalten. Dieser Prozess kombiniert Sprachverständnis, Vektormathematik und künstliche Intelligenz, um aufschlussreiche Antworten zu liefern und die Lücke zwischen menschlichen Anfragen und maschinengenerierten Antworten zu schließen.

Wir sind in der Lage, das Modell z. B. mit Daten für die Linux-Verwaltung zu füttern. Bemerkenswert ist die Vielseitigkeit des Modells. Egal, ob es mit der Wissensbasis eines Unternehmens oder anderen relevanten Informationen versorgt wird, besitzt es die Fähigkeit, aussagekräftige Antworten und Erkenntnisse zu liefern.

img.png

Es ist auch möglich, benutzerdefinierte Funktionen zu schreiben, die es erlauben, Ihren speziellen Datentyp für das Training zu verwenden.

Hier sind einige Beispiele für .txt (Textdateien) und .eml (Organisations-E-Mails):

  • .txt files
from langchain.docstore.document import Document

# converting text file to langchain Document format
def txt_to_doc(file):
    with open(file, 'r') as txt_file:
        content = txt_file.read()
        doc = Document(page_content=content, metadata={"source": file})
    return doc
  • .eml files
from langchain.docstore.document import Document
import email

# converting email file to langchain Document format
def email_to_doc(file):
    with open(file) as email_file:
        email_message = email.message_from_file(email_file)
        payload = email_message.get_payload()
        doc = Document(page_content=payload, metadata={"source": file})
    return doc

Chatgpt (gpt 3.5 / gpt 4) & Vector Store

Die Modelle GPT-3.5 und GPT-4.0 der Firma OpenAI sind zwar nicht quelloffen, bieten aber in Kombination mit einem Vektorspeicher wie FAISS eine faszinierende Möglichkeit für spezielle Anwendungen. Die Methodik ähnelt stark der von LLama2, allerdings mit einem kleinen Unterschied: Anstatt das Modell lokal einzusetzen, können wir einen API-Aufruf absetzen, um Antworten auf der Grundlage des bereitgestellten Kontexts aus dem GPT-Modell zu erhalten.

Es ist jedoch wichtig zu wissen, dass bei diesem Ansatz Daten zur Verarbeitung an US-Server übertragen werden, was wiederum, je nach Anwendungsfall, mit dem Datenschutz nach DSGVO kollidieren könnte.

Training

Auf unserem Weg, ein Sprachmodell zu trainieren, verwenden wir das Sharded Llama2-Modell, welches von der Firma Meta unter einer Open Source Lizenz veröffentlicht wurde. Dieses Modell kann lokal betrieben werden und eignet sich besonders bei der Bewältigung umfangreicher Sprachaufgaben. Es ist jedoch wichtig, die richtigen Voraussetzungen zu schaffen, und dazu müssen wir das Wesen von Sharding und sharded data parallelism verstehen.

Die Grundlage für unseren Ansatz ist die Aufteilung einer riesigen Datenbank in überschaubare Fragmente, das so genannte Sharding. Dieses Konzept ist vergleichbar mit der Aufteilung eines Problems in kleinere, besser verdauliche Komponenten. Wenn wir nun von sharded data parallelism sprechen, meinen wir damit eine ausgeklügelte Technik.

Stellen Sie sich vor, Sie stellen ein Team zusammen, um ein komplexes Rätsel zu lösen. Jedes Mitglied hat einen bestimmten Teil, auf den es sich konzentrieren muss. In ähnlicher Weise werden bei der sharded data parallelism die wichtigsten Komponenten des Modells – Parameter, Gradienten und Optimierungszustände – auf mehrere GPUs aufgeteilt. Dieser Ansatz optimiert nicht nur die Speichernutzung, sondern steigert auch die Leistung und Skalierbarkeit des Modells, was für die Bewältigung umfangreicher Sprachaufgaben unerlässlich ist.

Wenn Sie sich also das Sharding zu eigen machen und sich die Möglichkeiten des sharded data parallelism zunutze machen, sind Sie bestens gerüstet, um Ihr eigenes Sprachmodell zu trainieren.

Wie man ein geeignetes Llama2-Modell auswählt

Für diese spezielle Aufgabe empfehlen wir die Auswahl des Sharded Llama2-Modells, z. B. die Variante Llama-2-7B-bf16-sharded. Es ist wichtig zu beachten, dass dieses Modell umso effektiver arbeitet, je mehr Parameter es hat. Sie können dieses Modell bequem über den https://huggingface.co/TinyPixel/Llama-2-7B-bf16-sharded herunterladen.

Ein wichtiger Aspekt ist das Format Ihrer persönlichen Unternehmensdaten, die Sie für das Training des Modells verwenden möchten. Sie müssen im Parquet-Format vorliegen. Parquet ist ein spaltenförmiges Speicherformat, d.h. es speichert die Daten spaltenweise und nicht zeilenweise. Dieses Format bietet mehrere Vorteile, insbesondere im Hinblick auf die Komprimierung.

Es stimmt zwar, dass CSV-Dateien ebenfalls komprimiert werden können, aber aufgrund der unterschiedlichen Art der Datenspeicherung können sie nicht den gleichen Komprimierungsgrad wie Parquet erreichen. Um Ihren Trainingsprozess zu optimieren und das Sharded Llama2-Modell optimal zu nutzen, sollten Sie daher sicherstellen, dass Ihre Daten im Parquet-Format vorbereitet sind. Dies wird erheblich zur Effizienz und Effektivität des Sprachmodell-Trainings beitragen.

Es ist auch wichtig zu erwähnen, dass Sie möglicherweise ein anderes Modell auswählen möchten, je nachdem, in welcher Sprache Sie das Modell verwenden möchten. Während Llama2 in der Lage ist, andere Sprachen zu verstehen, ist seine Leistung in der englischen Sprache deutlich besser. Es gibt viele vorgefertigte Modelle auf huggingface, die sich auf bestimmte Sprachen spezialisiert haben.

Dataset Generation

1. Daten-Struktur

Die Trainigsdaten müssen folgendermaßen strukturiert sein:

Instruction Output Text
Wie starte ich den Apache-Webserver unter Linux neu?
Sie können den Apache-Webserver unter Linux mit dem Befehl “systemctl restart apache2” neu starten. Nachfolgend finden Sie eine Anweisung, die eine Aufgabe beschreibt. Schreiben Sie eine Antwort, die die Anforderung angemessen erfüllt. ### Anweisung Wie starte ich den Apache-Webserver unter Linux neu? ### Antwort Sie können den Apache-Webserver unter Linux mit dem Befehl ‘systemctl restart apache2’ neu starten.

Dataset Generation

1. Daten-Struktur

Die Trainigsdaten müssen folgendermaßen strukturiert sein:

Instruction Output Text
Wie starte ich den Apache-Webserver unter Linux neu?
Sie können den Apache-Webserver unter Linux mit dem Befehl “systemctl restart apache2” neu starten. Nachfolgend finden Sie eine Anweisung, die eine Aufgabe beschreibt. Schreiben Sie eine Antwort, die die Anforderung angemessen erfüllt. ### Anweisung Wie starte ich den Apache-Webserver unter Linux neu? ### Antwort Sie können den Apache-Webserver unter Linux mit dem Befehl ‘systemctl restart apache2’ neu starten.

Künftige Möglichkeiten

Nach dieser Einführung in den Aufbau KI-gestützter Chat-Anwendungen für Ihre unternehmensinterne Wissensdatenbank sollten wir uns einen Moment Zeit nehmen, um einen Blick in die Zukunft zu werfen.

In den kommenden Jahren werden sich KI-Chat-Anwendungen weiterentwickeln und nahtlos in die täglichen Abläufe von Unternehmen integriert werden. Wenn Sie sich die Möglichkeiten der KI zunutze machen, können Sie Chatbots entwickeln, die nicht nur besondere Kundenerlebnisse bieten, sondern auch maßgeschneiderte Lösungen, die auf Ihre Branche und Zielgruppe zugeschnitten sind. Das Engagement für Innovation und die Integration von Daten in diese Anwendungen hat das Potential für mehr Effizienz und Kundenzufriedenheit.

Fazit

Wie im Artikel beschrieben, haben große Sprachmodelle ein enormes Potential für Privatnutzer und Unternehmen gleichermaßen. Die Modelle lassen sich für unterschiedlichste Anwendungsfälle nutzen. Auch können sie spezialisiert und individualisert werden. In Verbindung mit einem Vektor-Store können sie auch mit großen Datenmengen umgehen. Neben den proprietären Varianten, wie den GPT-Modellen von OpenAI, gibt es auch eine große Anzahl von Open Source Modellen, wie beispielsweise die Llama Modelle von Meta, die mit den großen proprietären Modellen durchaus mithalten können.

Wir von B1-Systems bieten Ihnen ein umfangreiches Dienstleistungsportfolio rund um Künstliche Intelligenz an. Dazu gehören verschiedenste Themen der KI, wie Computer Vision, Zeitreihenanalysen aber eben auch die großen Sprachmodelle. Dies umfasst die Entwicklung von KI-Systemen, Fine-Tuning von Modellen, Trainings rund um das Thema Machine Learning / Deep Learning und vieles mehr. Wenn Sie weitere Fragen haben, Informationen benötigen oder einfach ein KI-Thema mit uns besprechen möchten, zögern Sie nicht und kontaktieren Sie uns unter mailto:info@b1-systems.de

Autoren

Sergey Danilov is a Data Scientist. His areas of expertise are time series analysis and he continuously strives to advance in the ever-changing field of data science.

Markus Gürtler hat über 20 Jahre Berufserfahrung in der IT, u.a. als IT-Architekt und Manager bei Firmen im Open Source Umfeld und beschäftigt sich seit 2012 mit Künstlicher Intelligenz und Deep Learning Technologien.

B1 Systems
B1 Systems
Wir sind ein unabhängiges Unternehmen und bieten nur Lösungen an, von denen wir hundertprozentig überzeugt sind. Dabei legen wir unser Hauptaugenmerk auf Linux- und Open Source-Lösungen.

 


Haben Sie Anmerkungen oder Nachfragen? Melden Sie sich unter blog%b1-systems.de
Col 2