Zur Entwicklung von Artificial Intelligence

antaris

Registriertes Mitglied
Es ist ein bisschen wie die Frage, inwiefern sich ein F1-Rennwagen vom Ford-T-Modell unterscheiden: vom Prinzip her gar nicht, nur in Details.

Dann fallen unter dieses „Prinzip“ aber auch Seifenkiste, Dreirad, Tretauto, Fahrrad, selbst ein Einrad und in gewissem Sinne sogar ein rollender Stein. ;)

Die Frage ist also, auf welcher Abstraktionsebene man das „Prinzip“ definiert. Wenn es nur noch „etwas bewegt sich rollend fort“ lautet, verschwinden gerade die Unterschiede, die erklären, warum ein F1-Wagen etwas völlig anderes leisten kann als ein Ford T.

Ähnlich sehe ich „im Prinzip nur Matrizenalgebra“ beim LLM: Auf der Ebene der elementaren Rechenoperationen stimmt das näherungsweise. Aber die interessante Frage ist doch gerade, welche Strukturen und Fähigkeiten aus der konkreten Komposition dieser Operationen entstehen. Sonst erklärt die Reduktion zwar, womit gerechnet wird, aber kaum noch, warum das Gesamtsystem kann, was es kann.


Interessanterweise hatte ich genau zu diesem Problem der gewählten Beschreibungsebene schon vor einiger Zeit etwas geschrieben, siehe #8:


Dort hatte ich ausgerechnet Fortbewegungsmittel als Beispiel für Approximationen verwendet. Je nachdem, welche Eigenschaften ich als wesentlich auswähle und welche ich herausfiltere, können völlig unterschiedliche Dinge dieselbe Approximation erfüllen.

Wenn ich weit genug abstrahiere, sind Ford T und F1 natürlich „vom Prinzip her“ gleich. Aber ich kann die Approximation noch weiter reduzieren und lande irgendwann bei „etwas bewegt sich von A nach B“. Dann ist plötzlich auch der rollende Stein vom selben Prinzip.

Das ist nicht falsch. Die Frage ist nur, welchen Erklärungswert die Aussage auf der jeweiligen Approximationsebene noch besitzt.

Beim LLM sehe ich dasselbe Problem mit der Reduktion auf Matrizenalgebra. Natürlich kann man die elementaren Rechenoperationen darauf zurückführen. Aber damit habe ich gerade Architektur, Nichtlinearitäten, gelernte Parameterstrukturen, Attention, Kontextabhängigkeit usw. aus der Betrachtung herausgefiltert. Und möglicherweise liegen gerade in der Komposition dieser vermeintlichen „Details“ die Eigenschaften, über die wir hier diskutieren.

Das passt eigentlich ziemlich genau zu meinem damaligen Punkt: Eine Approximation ist nicht deshalb falsch, weil sie grob ist. Man muss nur aufpassen, dass man nicht genau die Eigenschaften wegfiltert, deren Wirkung man anschließend erklären möchte.

Insofern finde ich dein F1/Ford-T-Beispiel sogar ziemlich passend – nur ziehe ich daraus fast die umgekehrte Schlussfolgerung. :)


Ein "Fortbewegungsmittel-Coarse-Graining": https://chatgpt.com/s/t_6a76d073a1308191821656eaa186acbe
Wird der Unterbau aller Fortbewegungsmittel "abgeschnitten", also z.B. alles was zum Antrieb gehört, dann erhält man ein effektives Beschreibungssystem mit emergent auftretende Konstanten.
 
Zuletzt bearbeitet:

antaris

Registriertes Mitglied
Der gleiche Weg führt zurück zum LLM und den Trainingsdaten. Die Trainingsdaten bzw. im daraus erlernten ist gerade nicht die Provenienz der ursprünglichen Daten gespeichert. Ein LLM kann nicht aus den Trainingsdaten nachvollziehen, woher Aussage X eigentlich stammt.

Das ist bereits ein coarse-grained und damit effektiver Anfangszustand, bei dem der eigentliche Unterbau gar nicht mehr verfügbar ist aber die Informationen daraus sozusagen "anonymisiert" dennoch verfügbar gemacht wurden: https://chatgpt.com/s/t_6a76e2ca54ec8191b2accb2fe405a8eb
 
Zuletzt bearbeitet:

Jakito

Registriertes Mitglied
Ich würde mir mal Python mit Libraries plus ein kleines LLM auf den Rechner ziehen und damit rumspielen. Vom Prinzip her reduziert sich das auf nämlich auf Algebra, …
AI Coding Agents: A Reductive Account for Physicists
Dies kommt der Art von „im Prinzip“ Erklärung typischer LLMs nahe, die TomS vorzuschweben scheint (inklusive Hands-on Demonstrationen). Für die Meinungen des Autors zu den real existierenden praktischen Fähigkeiten der LLMs von OpenAI und Anthropic sind Tobias J. Osborne's research notes aufschlussreicher.

@Tom, @pauli,

Euer Tipp mit dem „Hands-on“-Ansatz war Gold wert! Nur Theorie zu lesen war mir zu trocken, aber es direkt auf dem eigenen Rechner aufzuziehen macht richtig Spaß und bringt den echten Erkenntnisgewinn
Gut zu hören, dass TomS hier hilfreich war. Ich hatte mich etwas schwer getan, seine Meinungen und/oder seinen Wissenstand zu den LLMs von OpenAI und Anthropic einzuordnen. Was meint er z.B. mit: „Deduktion ist logisches Beweisen, das können sie tatsächlich im Kern nicht“, wenn selbst die besten Experten zugehörige zentrale empirische Beobachtungen noch nicht sauber verstehen, geschweige denn erklären können:

Klar ist mir hingegen, dass LLMs gerade in der Komplexitätsklasse liegen (TC), wo die Forscher keine nützlichen Schranken der Fähigkeiten mehr beweisen können, obwohl alle (ich auch) von der Beschränktheit der Fähigkeiten felsenfest überzeugt sind.
 

Jakito

Registriertes Mitglied
Deduktion ist logisches Beweisen, das können sie tatsächlich im Kern nicht, ihre Mustererkennung imitiert jedoch bei genügend guten Trainingsdaten Deduktion, d.h. ihre Ausgaben sehen so aus, als ob sie logisch schlussfolgern würden.
Der „failure mode“ in der Praxis ist, dass ohne entsprechenden „harness“ irgendwann wichtige Informationen hinten aus den Kontextfenster rausfallen. So geschehen z.B. bei dem Apple-Paper, welches LLMs diese Fähigkeit absprach. Bei Claude Code kann der Anwender schön sehen, wie ein entsprechender „harness“ diese Problem entschärfen kann: Wichtige Informationen werden „menschenlesbar“ in speziellen Dateien abgelegt. (Dadurch verbrauchen die Coding Agents zwar im Laufe der Zeit immer mehr Tokens, wenn man diese Dateien nicht ab und zu ausmisten läßt, aber dafür arbeiten sie schön zuverlässig.)
 
Zuletzt bearbeitet:

TomS

Registriertes Mitglied
Ähnlich sehe ich „im Prinzip nur Matrizenalgebra“ beim LLM: Auf der Ebene der elementaren Rechenoperationen stimmt das näherungsweise.
Gut.

Aber die interessante Frage ist doch gerade, welche Strukturen und Fähigkeiten aus der konkreten Komposition dieser Operationen entstehen.
Das ist tatsächlich die interessante Frage.

Sonst erklärt die Reduktion zwar, womit gerechnet wird, aber kaum noch, warum das Gesamtsystem kann, was es kann.
Das interessante ist, dass diese Frage offen ist. Es kann aber auch niemand erklären, warum das menschliche Gehirn kann, was es kann.

Strukturen, Fähigkeiten usw. sind im Bauplan des LLMs nicht wirklich angelegt. Wir verstehen, dass sie etwas können, insofern wir den Output mit unserer Erwartungshaltung vergleichen. Wir verstehen die fundamentale Algebra, denn wir haben sie programmiert. Aber die Ebene dazwischen, warum genau sie etwas können, verstehen wir nicht wirklich.

Das sagen auch führende KI-Forscher.

Beim LLM sehe ich dasselbe Problem mit der Reduktion auf Matrizenalgebra. Natürlich kann man die elementaren Rechenoperationen darauf zurückführen. Aber damit habe ich gerade Architektur, Nichtlinearitäten, gelernte Parameterstrukturen, Attention, Kontextabhängigkeit usw. aus der Betrachtung herausgefiltert.
Die kannst du gerne mitgetrachten, deswegen verstehen wir immer nicht mehr.

Und möglicherweise liegen gerade in der Komposition dieser vermeintlichen „Details“ die Eigenschaften, über die wir hier diskutieren.
Natürlich sind diese Details wichtig.

Aber:
  • für einen Einstieg – und das war die Frage von @pauli – sind sie irrelevant; würdest du einem Menschen aus dem Mittelalter ein Auto erklären, fängst du vermutlich nicht mit dem Automatikgetriebe an
  • die Gesamtheit der Algorithmen führt auch für die Experten offenbar nicht zum Gesamtverständnis ihrer Schöpfung; das ist ggw. Konsens
 
Zuletzt bearbeitet:

TomS

Registriertes Mitglied
Der „failure mode“ in der Praxis ist, dass ohne entsprechenden „harness“ irgendwann wichtige Informationen hinten aus den Kontextfenster rausfallen. So geschehen z.B. bei dem Apple-Paper, welches LLMs diese Fähigkeit absprach. Bei Claude Code kann der Anwender schön sehen, wie ein entsprechender „harness“ diese Problem entschärfen kann: Wichtige Informationen werden „menschenlesbar“ in speziellen Dateien abgelegt. (Dadurch verbrauchen die Coding Agents zwar im Laufe der Zeit immer mehr Tokens, wenn man diese Dateien nicht ab und zu ausmisten läßt, aber dafür arbeiten sie schön zuverlässig.)
Das bedeutet nur, dass der Algorithmus des LLMs Deduktion und logisches Schließen besser imitiert, nicht dass er wirklich logisch schließt.

Evtl. muss ich das nochmal erklären:
  • ein LLM enthält keinen Automated Theorem Prover; es führt algebraische Berechnungen durch und übersetzt diese in Zeichen, die für uns so aussehen wie ein Beweis; das bedeutet jedoch nicht, dass da tatsächlich ein Beweis ist
  • an der Stelle muss ich natürlich hinzufügen, dass es sich beim menschlichen Gehirn genauso verhält: auch hier gibt es auf der Ebene der Neuronen keinen ATP; Mathematiker sind aber von ihrem Gehirn der Meinung, dieses könne so arbeiten;
  • bei LLMs muss ich meine Aussage dahingehend einschränken, dass es ähnlich sein könnte wie bei menschlichen Mathematikern, wir das jedoch nicht wissen können
 

antaris

Registriertes Mitglied
Das interessante ist, dass diese Frage offen ist. Es kann aber auch niemand erklären, warum das menschliche Gehirn kann, was es kann.

Strukturen, Fähigkeiten usw. sind im Bauplan des LLMs nicht wirklich angelegt. Wir verstehen, dass sie etwas können, insofern wir den Output mit unserer Erwartungshaltung vergleichen. Wir verstehen die fundamentale Algebra, denn wir haben sie programmiert. Aber die Ebene dazwischen, warum genau sie etwas können, verstehen wir nicht wirklich.

Das sagen auch führende KI-Forscher.
Das streite ich gar nicht ab.

Beim LLM sehe ich dasselbe Problem mit der Reduktion auf Matrizenalgebra. Natürlich kann man die elementaren Rechenoperationen darauf zurückführen. Aber damit habe ich gerade Architektur, Nichtlinearitäten, gelernte Parameterstrukturen, Attention, Kontextabhängigkeit usw. aus der Betrachtung herausgefiltert.
Die kannst du gerne mitbetrachten, deswegen verstehen wir immer noch nicht mehr.
Das mag sein. Aber durch Ausklammern der Details verstehen wir erst recht nicht alles. Das ist eigentlich mein Punkt.

für einen Einstieg – und das war die Frage von @pauli – sind sie irrelevant; würdest du einem Menschen aus dem Mittelalter ein Auto erklären, fängst du vermutlich nicht mit dem Automatikgetriebe an
Schon klar, aber das war meinerseits ja auch keine Antwort an Pauli.

die Gesamtheit der Algorithmen führt auch für die Experten offenbar nicht zum Gesamtverständnis ihrer Schöpfung; das ist ggw. Konsens
Der Witz ist für mich gerade, dass dieses „Nichtverstehen“ strukturell sehr ähnlich zu vielen anderen Fällen unseres Nichtverstehens ist.

Wir kennen beim LLM eine mikroskopischere Beschreibungsebene ziemlich genau: Architektur, Parameter, elementare Operationen usw. Und wir kennen eine makroskopische Ebene, weil wir das Verhalten beobachten: Das Modell übersetzt, programmiert, bildet Analogien, findet mathematische Zusammenhänge usw.

Was uns fehlt, ist die belastbare Abbildung dazwischen:

\text{Mikrostruktur}\longrightarrow ? \longrightarrow \text{beobachtete F}\ddot{a}\text{higkeit}.


Das ist strukturell kein exotisches Problem der Ai. Genau solche Lücken entstehen überall dort, wo sehr viele Freiheitsgrade zu einer effektiven Beschreibung zusammengefasst werden. Man kann die mikroskopischen Regeln kennen und trotzdem nicht unmittelbar verstehen, warum auf einer anderen Beschreibungsebene gerade diese makroskopischen Strukturen auftreten.
Das ist bereits ein coarse-grained und damit effektiver Anfangszustand, bei dem der eigentliche Unterbau gar nicht mehr verfügbar ist

Deshalb finde ich das Coarse-Graining-Beispiel mit den Fortbewegungsmitteln passend. Je nachdem, welche Details ich wegprojiziere, erhalte ich eine andere effektive Beschreibung. Die Projektion kann für eine bestimmte Fragestellung völlig sinnvoll sein – sie erklärt aber natürlich nicht mehr die Eigenschaften, die ich dabei gerade eliminiert habe.

Beim LLM kommt noch etwas Interessantes hinzu: Schon das Training selbst komprimiert eine gewaltige Menge an Information. Die Trainingsdaten und insbesondere ihre Provenienz liegen dem fertigen Modell nicht einfach als expliziter Wissensgraph vor. Ihre Wirkung steckt verteilt in der gelernten Parameterstruktur. Anschließend beobachten wir Fähigkeiten auf einer nochmals anderen Ebene.

Vielleicht ist daher gerade die „Ebene dazwischen“ interessant: Welche effektiven Strukturen ersetzen beim Übergang von der bekannten Mikrostruktur zur beobachteten Makrostruktur die enorme Menge eliminierter Details?

Das muss nicht bedeuten, dass es dafür eine einfache Erklärung oder überhaupt eine eindeutige Zwischenebene gibt. Aber das Nichtverstehen dieser Abbildung ist für mich gerade kein Grund, die Details als irrelevant anzusehen. Es ist vielmehr die eigentliche Forschungsfrage.
 

TomS

Registriertes Mitglied
Vielleicht ist daher gerade die „Ebene dazwischen“ interessant: Welche effektiven Strukturen ersetzen beim Übergang von der bekannten Mikrostruktur zur beobachteten Makrostruktur die enorme Menge eliminierter Details?
Mit "effektiven Strukturen" meinst du vermutlich Strukturen in den im Zuge des Trainings erlernten Parametern, richtig?
 

antaris

Registriertes Mitglied
Mit "effektiven Strukturen" meinst du vermutlich Strukturen in den im Zuge des Trainings erlernten Parametern, richtig?
Nein das meine ich nicht. Was liegt mathematisch zwischen den einzelnen Gewichten und der von uns beobachteten Fähigkeit?

Die einzelnen Gewichte selbst dürften dafür die falsche Beschreibungsebene sein. Verschiedene konkrete Parametrisierungen können funktional sehr ähnliche Strukturen realisieren. Interessanter sind daher eher die Relationen und Repräsentationen, die durch die Gewichte erzeugt werden, und darüber wiederum funktionale Strukturen, die gegenüber Änderungen auf der niedrigeren Ebene weitgehend invariant bleiben.

Sehr schematisch vielleicht:

\text{Gewichte}\rightarrow \text{Repr}\ddot{a}\text{sentationen}\rightarrow \text{relationale Strukturen/Circuits}\rightarrow \text{funktionale Invarianten}\rightarrow \text{beobachtete F}\ddot{a}\text{higkeit}.


Das wäre dann gewissermaßen ein Coarse-Graining der Beschreibung. Ich muss nicht jedes einzelne Gewicht kennen, wenn verschiedene mikroskopische Realisierungen bezüglich einer bestimmten Funktion äquivalent sind.

Man könnte das sogar als Äquivalenzrelation auffassen:

\theta_1\sim\theta_2,


wenn unterschiedliche Parameterkonfigurationen hinsichtlich einer untersuchten Funktion denselben effektiven Mechanismus realisieren. Die interessante Beschreibung läge dann nicht mehr in jedem einzelnen
\theta
, sondern in den Äquivalenzklassen beziehungsweise den darin invarianten relationalen Strukturen.

Genau solche Zwischenstrukturen versucht die mechanistische Interpretierbarkeit ja tatsächlich zu finden: Features, Repräsentationen, Circuits und deren kausalen Beitrag zum Output.

Insofern würde ich „wir verstehen die Ebene dazwischen nicht“ etwas präzisieren: Wir wissen durchaus, dass dort solche Strukturen existieren und können einzelne davon bereits rekonstruieren. Was uns fehlt, ist eine hinreichend vollständige Theorie dafür, wie aus Milliarden Parametern und deren Dynamik genau die funktionalen Strukturen entstehen, die schließlich die beobachteten Fähigkeiten tragen.

Und das finde ich strukturell so interessant, weil dasselbe Erkenntnisproblem an vielen Stellen wiederkehrt:

\text{bekannter Unterbau}\rightarrow \text{unvollst}\ddot{a}\text{ndig verstandene effektive Zwischenstruktur}\rightarrow \text{bekanntes makroskopisches Verhalten}.


Das „Nichtverstehen“ liegt dann möglicherweise weniger darin, dass zwischen Mikro- und Makroebene einfach ein schwarzes Loch wäre, sondern darin, dass wir die richtigen effektiven Variablen, Relationen und Invarianten dieser Zwischenebene noch nicht vollständig kennen.

Und genau deshalb würde ich die Details nicht aus prinzipiellen Gründen ausklammern. Einige davon werden beim Coarse-Graining irrelevant sein, andere könnten gerade diejenigen Relationen erzeugen, die auf der nächsthöheren Ebene invariant und damit erklärungsrelevant werden.


Die Thermodynamik ist fast das perfekte Gegenbeispiel gegen die Vorstellung, man müsse entweder die Mikroebene vollständig verstehen oder die Makroebene bleibe mysteriös.

Bei einem Gas kennen wir grob beide Enden:

\text{Mikrozust}\ddot{a}\text{nde}\quad\longrightarrow\quad \text{Temperatur,Druck,Entropie}.


Aber die interessante Erklärung liegt gerade in der Zwischenebene, die als Pfeil "codiert" ist: Welche mikroskopischen Details werden irrelevant, welche statistischen Relationen bleiben erhalten und welche makroskopischen Größen werden dadurch wohldefiniert?

Temperatur ist beispielsweise keine Eigenschaft, die man sinnvoll einem einzelnen klassischen Gasmolekül in derselben thermodynamischen Bedeutung zuschreibt. Trotzdem ist sie keine Illusion. Sie ist eine robuste effektive Größe eines Ensembles.

Das trifft unseren Punkt beim LLM ziemlich genau:

\text{einzelne Gewichte}\not\equiv \text{F}\ddot{a}\text{higkeit}.


Gesucht sind vielmehr Größen analog zu den thermodynamischen Zustandsgrößen:

\text{Gewichte}\rightarrow \boxed{\text{?}}\rightarrow \text{Reasoning /}\ddot{U}\text{bersetzung / Abstraktion / etc.}


Und die „Blackbox“
\boxed{?}
könnte aus Repräsentationen, relationalen Strukturen, Circuits und funktionalen Invarianten bestehen.

Der Vergleich macht außerdem klar, warum du mit

„Wir verstehen die Ebene dazwischen nicht“

recht haben kannst, ohne dass daraus folgt, diese Ebene sei irgendwie strukturlos oder grundsätzlich unerklärbar.


Historisch war ja auch

PV=nRT


eine funktionierende makroskopische Beschreibung, bevor die statistische Mechanik erklärte, warum solche makroskopischen Größen aus mikroskopischer Dynamik hervorgehen. Die Ebene „zwischen Mikro und Makro“, welche der Pfeil repräsentiert, wurde nachträglich „geschlossen“.
 
Zuletzt bearbeitet:

TomS

Registriertes Mitglied
Was liegt mathematisch zwischen den einzelnen Gewichten und der von uns beobachteten Fähigkeit?
Das Zusammenspiel von Algorithmus und Parametern.

Die einzelnen Gewichte selbst dürften dafür die falsche Beschreibungsebene sein. Verschiedene konkrete Parametrisierungen können funktional sehr ähnliche Strukturen realisieren. Interessanter sind daher eher die Relationen und Repräsentationen, die durch die Gewichte erzeugt werden, und darüber wiederum funktionale Strukturen, die gegenüber Änderungen auf der niedrigeren Ebene weitgehend invariant bleiben.
Erinnerst du dich an die Szene im Film "Die Matrix", wo der Programmierer der Simulation Code liest und darin direkt Frauen sieht?

Insofern würde ich „wir verstehen die Ebene dazwischen nicht“ etwas präzisieren: Wir wissen durchaus, dass dort solche Strukturen existieren und können einzelne davon bereits rekonstruieren. Was uns fehlt, ist eine hinreichend vollständige Theorie dafür, wie aus Milliarden Parametern und deren Dynamik genau die funktionalen Strukturen entstehen, die schließlich die beobachteten Fähigkeiten tragen.
Ja.

… ohne dass daraus folgt, diese Ebene sei irgendwie strukturlos oder grundsätzlich unerklärbar.
Das war auch keine prinzipielle Aussage.

Möglicherweise verstehen wir es irgenwann, möglicherweise auch nicht. Vermutlich letzteres, wenn wir weiter AIs mittels AIs optimieren.
 

TomS

Registriertes Mitglied
Hier ist ein lesenswerter kurzer Überblick zur Mathematik:

The Simple Mathematics of Large Language Models
Ich habe nur das mal von einer KI in Python generi lassen. Noch nicht geprüf!

Python:
"""
Implementation of Chapters 5-9 of:
"The Simple Mathematics of Large Language Models" (Breeden, 2026)

Chapter 5 - The Core Mechanism: Learned Weighted Averaging
Chapter 6 - Multiple Relations
Chapter 7 - Depth: Stacking Multiple Layers
Chapter 8 - Position Information
Chapter 9 - From Representations to Probabilities

Variable names are kept identical to the document wherever possible:
WA, WB, WC, WO, ai, bj, cj, rij, alpha (influence weight), hi, h,
W1, W2, b1, b2, pi, uv, zv, k, d, H, L.
"""

import numpy as np


# ---------------------------------------------------------------------------
# Chapter 5: The Core Mechanism - Learned Weighted Averaging
# ---------------------------------------------------------------------------

def project(e, W):
    """W e in R^k -- 'a set of learned features extracted from the
    original representation.'"""
    return W @ e


def influence_score(ai, bj, k):
    """rij = ai . bj -- 'how well what position j offers aligns with
    what position i is receptive to.' Scaled by sqrt(k) to prevent the
    dot products from becoming very large."""
    return (ai @ bj) / np.sqrt(k)


def influence_weights(rij_vector):
    """alpha_ij = e^rij / sum_m e^rim -- the inverse multinomial logit.
    'Larger inputs receive exponentially more weight.'"""
    r = rij_vector - np.max(rij_vector)  # numerical stability, does not change ratios
    exp_r = np.exp(r)
    return exp_r / np.sum(exp_r)


def core_mechanism(h_prev, WA, WB, WC, i):
    """The Complete Operation (Section 5.8):
    1. ai = WA ei for the single position i being updated
    2. bj = WB ej, cj = WC ej for each context position j
    3. rij = ai . bj / sqrt(k)
    4. alpha_ij = softmax(rij)
    5. hi = sum_j alpha_ij cj  -- 'a new representation for position i
       that incorporates information from the entire context.'
    """
    n = h_prev.shape[0]
    k = WA.shape[0]

    ai = project(h_prev[i], WA)                      # what position i is receptive to
    bj = np.stack([project(h_prev[j], WB) for j in range(n)])  # what position j offers
    cj = np.stack([project(h_prev[j], WC) for j in range(n)])  # content to aggregate from j

    rij = np.array([influence_score(ai, bj[j], k) for j in range(n)])
    alpha = influence_weights(rij)                    # influence weight

    hi = np.sum(alpha[:, None] * cj, axis=0)          # weighted average
    return hi, alpha


# ---------------------------------------------------------------------------
# Chapter 6: Multiple Relations
# ---------------------------------------------------------------------------

def multiple_relations(h_prev, WA_h, WB_h, WC_h, WO, i):
    """'Rather than learning a single set of transformation matrices,
    learn H sets in parallel... These are concatenated and projected
    back to the original dimension.'
    WA_h, WB_h, WC_h: lists of length H (one matrix set per relation).
    """
    H = len(WA_h)
    o_list = []
    for h in range(H):
        o_i_h, _ = core_mechanism(h_prev, WA_h[h], WB_h[h], WC_h[h], i)
        o_list.append(o_i_h)                          # o^(h)_i

    concatenated = np.concatenate(o_list)              # [o^(1)_i ; ... ; o^(H)_i]
    hi = WO @ concatenated
    return hi


# ---------------------------------------------------------------------------
# Chapter 7: Depth - Stacking Multiple Layers
# ---------------------------------------------------------------------------

def relu(x):
    """phi(x) = max(0, x) -- 'simply replace negative values with zero.'"""
    return np.maximum(0, x)


def nonlinear_transformation(hi, W1, b1, W2, b2, phi=relu):
    """hi <- W2 . phi(W1 hi + b1) + b2 -- a nonlinear function applied
    element-wise, position by position."""
    return W2 @ phi(W1 @ hi + b1) + b2


def residual_connection(hi, f_hi):
    """hi <- hi + f(hi) -- 'each layer makes an incremental additive
    adjustment to the representation.'"""
    return hi + f_hi


def normalize(hi, eps=1e-8):
    """Normalize each vector to have mean 0 and variance 1 across its
    coordinates -- 'a form of standardization that keeps the numbers
    in a well-behaved range.'"""
    mean = np.mean(hi)
    var = np.var(hi)
    return (hi - mean) / np.sqrt(var + eps)


def transformer_layer(h_prev, WA_h, WB_h, WC_h, WO, W1, b1, W2, b2):
    """One full layer: multi-relation aggregation + feedforward,
    each wrapped in a residual connection, followed by normalization.
    h^(l)_i = Normalize(h^(l-1)_i + h~_i + W2 . max(0, W1(h^(l-1)_i + h~_i)))
    """
    n = h_prev.shape[0]
    h_out = np.zeros_like(h_prev)

    h_tilde = np.stack([multiple_relations(h_prev, WA_h, WB_h, WC_h, WO, i)
                         for i in range(n)])
    h_mid = h_prev + h_tilde                            # residual connection

    for i in range(n):
        f_hi = nonlinear_transformation(h_mid[i], W1, b1, W2, b2)
        h_res = residual_connection(h_mid[i], f_hi)      # incremental refinement
        h_out[i] = normalize(h_res)

    return h_out


def stacked_layers(h0, layer_params):
    """h^(0)_i = e_i (initial word vectors)
    h^(l)_i = Layer_l(h^(l-1)_1, ..., h^(l-1)_n)_i
    layer_params: list of dicts, one per layer, each with keys
    WA_h, WB_h, WC_h, WO, W1, b1, W2, b2.
    """
    h = h0
    for params in layer_params:
        h = transformer_layer(h, **params)
    return h


# ---------------------------------------------------------------------------
# Chapter 8: Position Information
# ---------------------------------------------------------------------------

def add_absolute_position(ei, pi):
    """ei <- ei + pi -- early approaches added a position vector
    directly to each word embedding."""
    return ei + pi


def sinusoidal_position_encoding(position, d):
    """pi defined using mathematical functions such as sines and
    cosines at different frequencies (Vaswani et al., 2017)."""
    pi_vec = np.zeros(d)
    for idx in range(0, d, 2):
        freq = 1.0 / (10000 ** (idx / d))
        pi_vec[idx] = np.sin(position * freq)
        if idx + 1 < d:
            pi_vec[idx + 1] = np.cos(position * freq)
    return pi_vec


def rope_rotate(x, position, base=10000.0):
    """Rotary Position Embedding: 'rotates the vectors ai and bj by
    angles proportional to their positions,' so that the dot product
    ai . bj depends on the difference i - j, not on i and j individually."""
    d = x.shape[0]
    x_rot = np.zeros_like(x)
    for idx in range(0, d, 2):
        theta = position / (base ** (idx / d))
        cos_t, sin_t = np.cos(theta), np.sin(theta)
        x0, x1 = x[idx], x[idx + 1] if idx + 1 < d else 0.0
        x_rot[idx] = x0 * cos_t - x1 * sin_t
        if idx + 1 < d:
            x_rot[idx + 1] = x0 * sin_t + x1 * cos_t
    return x_rot


def alibi_bias(rij, i, j, slope):
    """ALiBi: 'adds a penalty to influence scores that increases
    linearly with distance between positions.'"""
    return rij - slope * abs(i - j)


# ---------------------------------------------------------------------------
# Chapter 9: From Representations to Probabilities
# ---------------------------------------------------------------------------

def token_score(uv, h_L_n):
    """zv = uv . h^(L)_n -- a score for each token v in the vocabulary."""
    return uv @ h_L_n


def next_token_probabilities(U, h_L_n):
    """P(w_{n+1} = v | w1, ..., wn) = e^zv / sum_v' e^zv'
    -- the inverse multinomial logit applied to vocabulary scores.
    U: array of shape (|V|, d), one uv row per vocabulary token.
    """
    zv = U @ h_L_n                       # score for each token v in the vocabulary
    zv = zv - np.max(zv)                 # numerical stability
    exp_zv = np.exp(zv)
    return exp_zv / np.sum(exp_zv)


# ---------------------------------------------------------------------------
# Minimal smoke test
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    rng = np.random.default_rng(0)

    n, d, k, H, L, vocab_size = 5, 16, 8, 4, 2, 20

    h0 = rng.normal(size=(n, d))
    for pos in range(n):
        h0[pos] = add_absolute_position(h0[pos], sinusoidal_position_encoding(pos, d))

    layer_params = []
    for _ in range(L):
        WA_h = [rng.normal(size=(k, d)) * 0.1 for _ in range(H)]
        WB_h = [rng.normal(size=(k, d)) * 0.1 for _ in range(H)]
        WC_h = [rng.normal(size=(k, d)) * 0.1 for _ in range(H)]
        WO = rng.normal(size=(d, k * H)) * 0.1
        W1 = rng.normal(size=(d, d)) * 0.1
        b1 = np.zeros(d)
        W2 = rng.normal(size=(d, d)) * 0.1
        b2 = np.zeros(d)
        layer_params.append(dict(WA_h=WA_h, WB_h=WB_h, WC_h=WC_h, WO=WO,
                                  W1=W1, b1=b1, W2=W2, b2=b2))

    h_L = stacked_layers(h0, layer_params)   # h^(L)_i for all positions

    U = rng.normal(size=(vocab_size, d)) * 0.1
    probs = next_token_probabilities(U, h_L[-1])   # P(w_{n+1} = v | ...)

    print("influence weights sum to 1:", np.isclose(probs.sum(), 1.0))
    print("next-token probability distribution:\n", probs)
 

antaris

Registriertes Mitglied
Das Zusammenspiel von Algorithmus und Parametern.

Ja, aber das ist für mich noch ungefähr so, als würde man auf die Frage, woher die Temperatur kommt, antworten: „aus dem Zusammenspiel der Moleküle und ihrer Dynamik“. Das ist richtig, aber die eigentlich interessante Erklärung liefert erst die statistische Mechanik mit der Abbildung von sehr vielen mikroskopischen Freiheitsgraden auf wenige effektive Zustandsgrößen.

Erinnerst du dich an die Szene im Film "Die Matrix", wo der Programmierer der Simulation Code liest und darin direkt Frauen sieht?

Ja, und eigentlich ist das ein ziemlich gutes Bild dafür. Der sieht offenbar nicht mehr einzelne Zeichen, sondern erkennt darin eine höherstufige Struktur. Genau nach so einer Beschreibungsebene frage ich.

Beim LLM wäre also nicht jedes einzelne Gewicht interessant, sondern vielleicht eher, welche Repräsentationen, Relationen, Circuits oder noch allgemeineren funktionalen Invarianten gegenüber unterschiedlichen mikroskopischen Realisierungen erhalten bleiben.

Vielleicht ist deshalb auch
Vermutlich letzteres, wenn wir weiter AIs mittels AIs optimieren.
nicht zwingend. Auf der Ebene der Einzelparameter wird das Verständnis vermutlich tatsächlich immer aussichtsloser. Aber möglicherweise suchen wir dort schlicht auf der falschen Beschreibungsebene.

Bei einem Gas verstehen wir die Thermodynamik schließlich auch nicht dadurch, dass wir 10²³ Molekültrajektorien einzeln nachvollziehen, sondern indem wir herausfinden, welche Details beim Coarse-Graining irrelevant werden und welche effektiven Größen invariant bzw. robust übrigbleiben.

Vielleicht braucht es für neuronale Netze irgendwann etwas strukturell Ähnliches wie eine „statistische Mechanik der LLMs“. Nicht als bloße Analogie, sondern in dem präzisen Sinn, die Abbildung

(\text{Algorithmus},\text{Parameter})\longrightarrow \text{effektive Strukturen}\longrightarrow \text{beobachtete F higkeiten}


zu verstehen.
 

antaris

Registriertes Mitglied
Ich habe nur das mal von einer KI in Python generi lassen. Noch nicht geprüf!

Das ist als Anschauungsmodell ziemlich gut und zeigt für mich gerade schön, wo unsere offene Frage anfängt.
Der Code implementiert zunächst die Architektur bzw. die elementaren Operationen: Projektionen, gewichtete Relationen/Attention, Nichtlinearitäten, Residualpfade, Normalisierung, Positionierung und am Ende die Wahrscheinlichkeiten.

Im Smoke-Test sind die Gewichte aber noch zufällig. Damit haben wir gewissermaßen den „Bauplan“, aber noch nicht die durch Training entstandene funktionale Organisation.


Schematisch:
\text{Architektur}\rightarrow \text{Training}\rightarrow \text{gelernte Parameter}\rightarrow \boxed{?}\rightarrow \text{beobachtete F}\ddot{a}\text{higkeiten}.

Und genau das
\boxed{?}
ist interessant.

Denn selbst der verlinkte Text spricht später ausdrücklich von „Emergent Structure“: Die einfachen Operationen kodieren die höheren Beziehungen nicht explizit, aber durch ihre Komposition entstehen offenbar Strukturen auf höheren Abstraktionsebenen.

Interessant finde ich auch, dass schon die Attention dort als gerichtete Relation beschrieben wird: Der Einfluss von j auf i kann ein anderer sein als der von i auf j.

Der Python-Code erklärt also sehr schön, womit gerechnet wird. Sobald wir aber reale trainierte Gewichte einsetzen, stellt sich erst die schwierigere Frage: Welche relationalen, repräsentationalen und funktionalen Invarianten entstehen daraus, sodass am Ende Fähigkeiten wie Übersetzung, Abstraktion oder mathematische Problemlösung auftreten?


Mit zufälligen Gewichten bekommen wir die Architektur ohne Fähigkeit. Mit trainierten Gewichten bekommen wir Architektur plus Fähigkeit. Das Interessante liegt genau in der Struktur, die das Training dazwischen erzeugt.
 

TomS

Registriertes Mitglied
Das ist als Anschauungsmodell ziemlich gut und zeigt für mich gerade schön, wo unsere offene Frage anfängt.
Letzteres war nicht das Ziel.
Der Code implementiert zunächst die Architektur bzw. die elementaren Operationen: Projektionen, gewichtete Relationen/Attention, Nichtlinearitäten, Residualpfade, Normalisierung, Positionierung und am Ende die Wahrscheinlichkeiten.
Das war das Ziel: zu zeigen, dass die Mathematik dahinter kein Hexenwerk ist.
Im Smoke-Test sind die Gewichte aber noch zufällig. Damit haben wir gewissermaßen den „Bauplan“, aber noch nicht die durch Training entstandene funktionale Organisation.
Training wäre der nächste Schritt. Danach hat man dann ein gut funktionierendes LLM, jedoch immer noch kein Verständnis der funktionale Organisation. Man erkennt anhand der Ergebnisse, dass sie irgendwie existiert, mehr jedoch nicht. Letztlich so, wie heute kein Neurowissenschaftler versteht, wie das Gehirn Schach spielen kann, oder eine Oper komponieren.
Denn selbst der verlinkte Text spricht später ausdrücklich von „Emergent Structure“: Die einfachen Operationen kodieren die höheren Beziehungen nicht explizit, aber durch ihre Komposition entstehen offenbar Strukturen auf höheren Abstraktionsebenen.
"Emergent Structure" ist nur ein Begriff, der das bezeichnet, was wir nicht verstehen.
Mit zufälligen Gewichten bekommen wir die Architektur ohne Fähigkeit. Mit trainierten Gewichten bekommen wir Architektur plus Fähigkeit. Das Interessante liegt genau in der Struktur, die das Training dazwischen erzeugt.
Der Witz ist, dass man das Training im Grunde ebenfalls wieder sehr einfach implementieren kann, jedoch bei deiner Kernfrage wiederum kein Stück weiterkommt.
 

TomS

Registriertes Mitglied
Das noch ausstehende Kapitel 10:

Python:
"""
Implementation of Chapter 10 of:
"The Simple Mathematics of Large Language Models" (Breeden, 2026)

Chapter 10 - Training: Learning Parameters from Data
  10.2 The Objective: Maximum Likelihood
  10.3 A Note on Cross-Entropy
  10.4 Optimization: Gradient Descent

Variable names are kept identical to the document wherever possible:
theta, L (objective / log-likelihood), p, q, H (entropy / cross-entropy),
D_KL, eta (learning rate), grad_theta.
"""

import numpy as np


# ---------------------------------------------------------------------------
# 10.2: The Objective - Maximum Likelihood
# ---------------------------------------------------------------------------

def log_likelihood_sequence(P_theta_sequence):
    """sum_{i=1}^n log P_theta(w_i | w_1, ..., w_{i-1})
    -- the log-likelihood contribution of a single sequence.
    P_theta_sequence: array of the model's predicted probability of the
    actual next token at each position i in the sequence.
    """
    return np.sum(np.log(P_theta_sequence))


def log_likelihood_corpus(P_theta_corpus):
    """L(theta) = sum_sequences sum_i log P_theta(w_i | w_1, ..., w_{i-1})
    -- 'we adjust the parameters to maximize the probability the model
    assigns to the observed sequences.'
    P_theta_corpus: list of P_theta_sequence arrays, one per sequence.
    """
    return sum(log_likelihood_sequence(seq) for seq in P_theta_corpus)


def negative_log_likelihood(P_theta_sequence):
    """The negative log-likelihood, 'sometimes called average
    surprisal' -- equivalent to minimizing cross-entropy."""
    return -log_likelihood_sequence(P_theta_sequence)


# ---------------------------------------------------------------------------
# 10.3: A Note on Cross-Entropy
# ---------------------------------------------------------------------------

def entropy(p, eps=1e-12):
    """H(p) = - sum_i p_i log p_i -- 'measures the average surprise
    or uncertainty in a probability distribution p.'"""
    p = np.asarray(p)
    return -np.sum(p * np.log(p + eps))


def cross_entropy(p, q, eps=1e-12):
    """H(p, q) = - sum_i p_i log q_i -- 'the average surprise when
    outcomes are drawn from distribution p but you are using
    distribution q to anticipate them.'"""
    p = np.asarray(p)
    q = np.asarray(q)
    return -np.sum(p * np.log(q + eps))


def kl_divergence(p, q, eps=1e-12):
    """D_KL(p || q) -- 'a measure of how different q is from p,'
    related to cross-entropy by H(p, q) = H(p) + D_KL(p || q)."""
    p = np.asarray(p)
    q = np.asarray(q)
    return np.sum(p * (np.log(p + eps) - np.log(q + eps)))


def cross_entropy_onehot(q, actual_index):
    """For language modeling, p is one-hot (all mass on the word that
    actually occurred), so H(p, q) = -log q(actual word).
    q: predicted probability distribution over the vocabulary.
    actual_index: index of the token that actually occurred.
    """
    return -np.log(q[actual_index])


# ---------------------------------------------------------------------------
# 10.4: Optimization - Gradient Descent
# ---------------------------------------------------------------------------

def gradient_descent_step(theta, grad_theta, eta):
    """theta <- theta + eta * grad_theta L(theta)
    -- 'compute the derivative of the objective with respect to each
    parameter (the gradient), then adjust the parameters in the
    direction that improves the objective.'
    theta, grad_theta: arrays (or dicts of arrays) of matching shape.
    """
    if isinstance(theta, dict):
        return {name: theta[name] + eta * grad_theta[name] for name in theta}
    return theta + eta * grad_theta


def sample_batch(corpus, batch_size, rng):
    """'we estimate [the gradient] from a random subset (a "batch")
    of sequences.' This estimate is noisy but unbiased."""
    indices = rng.choice(len(corpus), size=batch_size, replace=False)
    return [corpus[i] for i in indices]


def stochastic_gradient_descent(theta, corpus, grad_fn, eta, batch_size,
                                 num_steps, rng):
    """Repeatedly: sample a batch, estimate the gradient of the
    objective from it, and take a gradient step.
    grad_fn(theta, batch) must return grad_theta with the same
    structure as theta (an unbiased estimate from the batch).
    """
    for _ in range(num_steps):
        batch = sample_batch(corpus, batch_size, rng)
        grad_theta = grad_fn(theta, batch)
        theta = gradient_descent_step(theta, grad_theta, eta)
    return theta


def learning_rate_schedule(step, warmup_steps, total_steps, peak_eta):
    """'Learning rates are warmed up and decayed according to specific
    protocols.' Linear warmup followed by linear decay."""
    if step < warmup_steps:
        return peak_eta * (step / warmup_steps)
    remaining = max(total_steps - step, 0)
    decay_span = max(total_steps - warmup_steps, 1)
    return peak_eta * (remaining / decay_span)


# ---------------------------------------------------------------------------
# Minimal smoke test
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    rng = np.random.default_rng(0)

    # 10.3: entropy / cross-entropy / KL relationship
    p = np.array([0.0, 1.0, 0.0, 0.0])       # one-hot "true" distribution
    q = np.array([0.1, 0.6, 0.2, 0.1])       # model's predicted distribution

    H_p = entropy(p)
    H_pq = cross_entropy(p, q)
    D_kl = kl_divergence(p, q)
    print("H(p) [one-hot, should be ~0]:", H_p)
    print("H(p,q) == H(p) + D_KL(p||q):", np.isclose(H_pq, H_p + D_kl))
    print("H(p,q) == -log q(actual word):",
          np.isclose(H_pq, cross_entropy_onehot(q, actual_index=1)))

    # 10.4: a toy gradient descent run on theta -> minimize (theta - 3)^2
    # via maximizing L(theta) = -(theta - 3)^2, so grad_theta L = -2(theta-3)
    theta = np.array([0.0])
    eta = 0.1
    for step in range(50):
        grad_theta = -2 * (theta - 3.0)      # analytic gradient of L(theta)
        theta = gradient_descent_step(theta, grad_theta, eta)
    print("theta after gradient descent (should approach 3):", theta)

    # learning rate schedule sample
    schedule = [learning_rate_schedule(s, warmup_steps=10, total_steps=100,
                                        peak_eta=1e-3) for s in [0, 5, 10, 55, 100]]
    print("learning rate schedule samples:", schedule)
 

antaris

Registriertes Mitglied
Der Witz ist, dass man das Training im Grunde ebenfalls wieder sehr einfach implementieren kann, jedoch bei deiner Kernfrage wiederum kein Stück weiterkommt.
Hmm, ich frage mich dann aber worin die eigentlichen Neuerungen der aktuellen LLM Modelle liegen? Einfach nur größere oder zahlreichere Rechenzentren?

Man darf dabei auch nicht vergessen, dass ein sehr einfaches LLM-Modell zwar irgendwie Fähigkeiten hat aber wenn ich an meinen ChatGPT Anfang mit Modell 3.5 nachdenke, dann konnte es bei weitem nicht so gut, was das jetzige kann. Schließlich war die Gefahr von Halluzinationen viel größer und das hat die nützliche Fähigkeit gute Ergebnisse zu liefern ja doch sehr eingeschränkt.

Ansonsten ist das für mich eigentlich keine wirklich wichtige Frage. Mein Hirn nutze ich schließlich auch, obwohl ich nicht weiß wie es funktioniert. Für mich sind das alles typische Blackboxen. Ob man das Unwissen über deren Inhalt emergent oder effektiv nennt, hängt wohl wirklich mit dem Verständnis zusammen. Erklären tut das Wort Emergenz wirklich gar nix...egal in welchen Bereich.
 

TomS

Registriertes Mitglied
Hmm, ich frage mich dann aber worin die eigentlichen Neuerungen der aktuellen LLM Modelle liegen? Einfach nur größere oder zahlreichere Rechenzentren?
Ja.

Varianten im der Anzahl und Größe der Schichten, der Token- und die Attention-Darstellung, die Aktivierungsfunktion … nichts fundamentales.

Ein Teil dieser Optimierungen wird heute automatisiert durchgeführt, d.h. man testet diese Varianten bei identischem Trainingsinput und vergleicht die Ergebnisse.

Man darf dabei auch nicht vergessen, dass ein sehr einfaches LLM-Modell zwar irgendwie Fähigkeiten hat aber wenn ich an meinen ChatGPT Anfang mit Modell 3.5 nachdenke, dann konnte es bei weitem nicht so gut, was das jetzige kann.
Seit der Entwicklung der Transformer-LLMs hat sich nichts fundamentales getan. Ein F1-Rennwagen ist auch nur ein Auto.
 

albertus

Registriertes Mitglied
Mein Weg zum eigenen lokalen RAG-System in Python & PyCharm (Erfahrungsbericht & Code)
Hallo zusammen, inspiriert von den Beiträgen hier im Forum habe ich mich tiefer mit den mathematischen Grundlagen von LLMs und der Funktionsweise von lokaler Vektor-Suche (RAG) beschäftigt.

Ich wollte wissen, wie man aus eigenen Textdateien auf der Festplatte ein lokales KI-Suchsystem baut, das blitzschnell passende Fakten findet und sie über ein lokales Modell (Ollama) beantwortet – ohne dass Daten ins Internet hochgeladen werden müssen.
Hier ist mein Erfahrungsbericht, wie es klappt (und welche Stolpersteine es gab):

1. Die mathematische Grundlage (Forum-Skripte nachvollzogen)​

Zuerst habe ich die Skripte zur Attention-Mechanik und zum Training (Gradientenabstieg / Cross-Entropy) in PyCharm durchrechnen lassen.
  • Das Bestätigen von influence weights sum to 1: True zeigt wunderschön, wie die Attention-Gewichte mathematisch sauber auf 100% (1.0) normiert werden.
  • Beim Gradient Descent konnte ich live beobachten, wie sich ein Parameter
    \theta
    über 50 Optimierungsschritte von 0.0 exakt an den Zielwert 2.99995... annähert. Das ist genau die Mathematik, mit der LLMs trainiert werden.

2. Der erste Praxistest: Vektor-Suche & die "Zeilen-Falle"​

Als Nächstes ging es an die Praxis: Mit sentence-transformers (paraphrase-multilingual-MiniLM-L12-v2) habe ich Ordner mit .txt-Dateien eingelesen und per Kosinus-Ähnlichkeit (cos_sim) durchsucht.

Hier kam die erste Überraschung: Wenn man große Dateien (wie Goethes Gesamtausgabe, Shakespeare, Krieg und Frieden) einfach zeilenweise einliest, entstehen aus nur 13 Dateien schlagartig über 1,67 Millionen Einzelzeilen!
  • Das hätte auf meinem Prozessor trotz i9-Power runde 6,5 Stunden Berechnungszeit bedeutet.
  • Außerdem geht der Sinnzusammenhang verloren, wenn ein Satz mitten in der Zeile abgeschnitten wird.

3. Die Lösung: Text-Chunking & Smart-Caching​

Um das Problem zu lösen, habe ich das Skript auf zwei wesentliche Techniken umgestellt:
  1. Absätze statt Zeilen (Chunking): Der Text wird in zusammenhängende Sinnblöcke von ca. 600 Zeichen zerlegt.
    • Ergebnis: Die 1,67 Millionen Zeilen schrumpften sofort auf nur noch 23.744 Blöcke zusammen!
    • Die Berechnung auf der CPU dauerte statt 6,5 Stunden nur wenige Minuten.
  2. Smart-Cache auf der Festplatte: Das Skript speichert die Vektoren und den Änderungszeitpunkt (mtime) jeder Datei in einer pickle-Datei.
    • Ergebnis ab dem 2. Start: Das Programm merkt, wenn sich Dateien nicht geändert haben, und lädt die 23.744 Vektoren in unter 3 Sekunden direkt von der Festplatte!

4. Der Ablauf im Python-Code (rag_files_chunked.py)​

Falls es jemand auf dem eigenen PC nachbauen möchte – so sieht die Architektur aus:
  1. Datei-Scan & Encoding-Fallback: Liest UTF-8 und schaltet bei älteren Dateien automatisch fehlerfrei auf cp1252 um.
  2. Chunking: erstelle_chunks(text, max_zeichen=600) fasst Absätze zusammen.
  3. Vektor-Transformation: Erzeugt 384-dimensionale Vektoren für jeden Textblock.
  4. Vektor-Suche: Berechnet die Kosinus-Ähnlichkeit zwischen der mathematischen Repräsentation der Frage und allen Textblöcken.
  5. Ollama-Anbindung: Die Top-5 Treffer werden als strikter Kontext an das lokale Sprachmodell (llama3.2) übergeben.
Mein Fazit:
Es ist faszinierend zu sehen, wie aus reiner Mathematik (Vektoren, Matrizen und Kosinus-Winkeln) ein intelligentes Suchsystem entsteht, das selbst in riesigen Textmengen in Sekundenbruchteilen die exakten Stellen findet.
 

Jakito

Registriertes Mitglied
Hmm, ich frage mich dann aber worin die eigentlichen Neuerungen der aktuellen LLM Modelle liegen? Einfach nur größere oder zahlreichere Rechenzentren?
Es gab schon ganz konkrete Neuerungen, die es erst ermöglichten, mehr Rechenzeit zu verbraten, ohne durch die Speichergröße der GPUs stark ausgebremst zu werden. Insbesondere, wenn man das von antaris angeführte Vergleichs-LLM Model beachtet:
Man darf dabei auch nicht vergessen, dass ein sehr einfaches LLM-Modell zwar irgendwie Fähigkeiten hat aber wenn ich an meinen ChatGPT Anfang mit Modell 3.5 nachdenke, dann konnte es bei weitem nicht so gut, was das jetzige kann.
Hier die zugehörigen relevanten Release-Daten:
GPT-3.5 powered the public launch of ChatGPT on November 30, 2022
The first reasoning model in ChatGPT was OpenAI o1, released in September 2024. It introduced a "chain-of-thought" process, taking time to think and solve complex problems in math, science, and coding before outputting an answer
Um es nochmals konkreter zu machen: Beim Training konnten LLMs schon immer beliebig viel Rechenzeit verbraten. Bei der Inferenz hingegen ist dies erst durch die Verwendung von „Zwischenoutput“ als zusätzlichen „Input“ ermöglicht worden. In der Komplexitätstheorie zählt diese Art von Unterschied als fundamental. (So sind auch NP und co-NP fundamental verschieden, obwohl sie aus der reduktionistischen Sicht vieler Physiker doch fundamental das Gleiche beschreiben.)

Auch die späteren Neuerungen, dass LLMs das Internet durchsuchen können, oder Werkzeuge wie python & co einsetzen, sind aus dieser Sicht fundamental, und eben keine unwichtigen Details.
 
Oben