Back to "Wie neurale maschinelle Übersetzung tatsächlich funktioniert: ein Entwickler-Leitfaden"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI AI-Article Deep Learning EasyNMT Machine Learning Neural Machine Translation

Wie neurale maschinelle Übersetzung tatsächlich funktioniert: ein Entwickler-Leitfaden

Sunday, 09 November 2025

Einleitung

Wenn du diesem Blog gefolgt bist, weißt du, dass ich ein bisschen besessen von maschineller Übersetzung bin.Ich habe über, Verwendung von EasyNMTÜbersetzungsdienstleistungen für den Bau von Hintergrund-Übersetzungen, und sogarVerbesserung von EasyNMT**Aber mir wurde klar, dass ich es nie wirklich erklärt habe.

Wie

neuronale maschinelle Übersetzung funktioniert unter der Haube.

HINWEIS: Dies ist Teil meiner Experimente mit KI / eine Möglichkeit, $1000 Calude Code Web Credits auszugeben.

  • Ich habe dies eine BUNCH Papiere gefüttert, mein Verständnis, Fragen, die ich diesen Artikel generieren musste.
  • Es macht Spaß und füllt eine Lücke, die ich nirgendwo sonst gefüllt habe.
  • Bringen wir das in Ordnung!
  • Dieser Beitrag wird Sie von "Übersetzung ist Magie" zu "Übersetzung ist kluge Mathematik" durch die Erklärung der wichtigsten Konzepte hinter neuronalen maschinellen Übersetzung in einfachem Englisch (mit vielen Diagrammen und einige C#-Code, um es konkret).
  • Am Ende dieses Artikels werden Sie verstehen:

Was künstliche neuronale Netzwerke sind und wie sie lernen

Wie Wörter zu Zahlen werden (Einbettungen)

Was "Aufmerksamkeit" in KI bedeutet (Spoiler: es geht nicht um Aufmerksamkeit)

graph LR
    A[Source Sentence] --> B[Convert to Numbers]
    B --> C[Neural Network Processing]
    C --> D[Convert to Words]
    D --> E[Translated Sentence]

Wie funktioniert die Encoder-Decoder-Architektur?

Warum Transformatoren die Welt eroberten

Keine Sorge, wenn du keine Mathe-Person bist - ich behalte das so praktisch und visuell wie möglich.

Lasst uns eintauchen!

Die Kernidee: Übersetzung als Zahlenkürzel

  1. Hier ist das erste Gedankenbiegen Konzept: neuronale maschinelle Übersetzung behandelt Übersetzung als rein mathematischen Prozess.
  2. Sie füttern in einem Satz, das System konvertiert es in Zahlen, führt Millionen von mathematischen Operationen, und aus einer Übersetzung.
  3. Aber warten - wie konvertiert man Wörter in Zahlen?
  4. Und wie versteht Mathe die Sprache?

Fangen wir mit den Bausteinen an.

graph LR
    I1[Input 1] -->|weight: 0.8| N[Neuron]
    I2[Input 2] -->|weight: -0.3| N
    I3[Input 3] -->|weight: 0.5| N
    N --> O[Output]

    style N stroke-width:4px

Künstliche Neuronale Netzwerke: Die Stiftung

public class Neuron
{
    private double[] weights;
    private double bias;

    public double Activate(double[] inputs)
    {
        // Step 1: Multiply each input by its weight and sum them
        double sum = bias;
        for (int i = 0; i < inputs.Length; i++)
        {
            sum += inputs[i] * weights[i];
        }

        // Step 2: Apply activation function (tanh keeps values between -1 and 1)
        return Math.Tanh(sum);
    }
}

Bevor wir neuronale maschinelle Übersetzung verstehen können, müssen wir künstliche neuronale Netzwerke verstehen.Math.TanhTrotz des Phantasienamens sind sie eigentlich ziemlich einfach in ihrem Kern.

Was ist ein künstliches Neuron?

Ein künstliches Neuron ist eine mathematische Funktion, die:

graph LR
    subgraph Input Layer
        I1[Input 1]
        I2[Input 2]
        I3[Input 3]
    end

    subgraph Hidden Layer 1
        H1[Neuron 1]
        H2[Neuron 2]
        H3[Neuron 3]
        H4[Neuron 4]
    end

    subgraph Hidden Layer 2
        H5[Neuron 5]
        H6[Neuron 6]
        H7[Neuron 7]
    end

    subgraph Output Layer
        O1[Output 1]
        O2[Output 2]
    end

    I1 --> H1 & H2 & H3 & H4
    I2 --> H1 & H2 & H3 & H4
    I3 --> H1 & H2 & H3 & H4

    H1 --> H5 & H6 & H7
    H2 --> H5 & H6 & H7
    H3 --> H5 & H6 & H7
    H4 --> H5 & H6 & H7

    H5 --> O1 & O2
    H6 --> O1 & O2
    H7 --> O1 & O2

Benötigt mehrere Eingaben

  • Vervielfacht jeden Input um ein Gewicht (wie wichtig ist dieser Input?)
  • Fügt sie alle hinzu
  • Wendet eine "Aktivierungsfunktion" an, um einen Ausgang zu erzeugen

Hier ist eine visuelle Darstellung:

Machen wir das konkret mit einem C#-Code:

graph TD
    A[Start with Random Weights] --> B[Feed in Training Example]
    B --> C[Network Makes Prediction]
    C --> D{Is Prediction Correct?}
    D -->|No| E[Calculate Error]
    E --> F[Adjust Weights Slightly]
    F --> B
    D -->|Yes| G[Try Next Example]
    G --> B

    style E stroke-width:2px
    style F stroke-width:2px

Die Aktivierungsfunktion (in diesem Fall**) ist das, was neuronale Netzwerke interessant macht.**Es führt Nichtlinearität ein, so dass das Netzwerk komplexe Muster lernen kann.

Ohne sie, egal wie viele Neuronen du zusammengestapelt hast, hättest du einfach eine ausgefallene lineare Funktion.

public class SimpleNeuralNetwork
{
    private double learningRate = 0.01;
    private Neuron[] neurons;

    public void Train(TrainingExample[] examples, int epochs)
    {
        for (int epoch = 0; epoch < epochs; epoch++)
        {
            foreach (var example in examples)
            {
                // Forward pass: make a prediction
                double prediction = Predict(example.Input);

                // Calculate error
                double error = example.ExpectedOutput - prediction;

                // Backward pass: adjust weights
                // (simplified - real networks use backpropagation)
                AdjustWeights(error * learningRate);
            }
        }
    }
}

Von Neuronen zu Netzwerken**Die Magie geschieht, wenn Sie Tausende oder Millionen dieser Neuronen in Schichten miteinander verbinden:**Jede Ebene "definiert" die Darstellung des Inputs und extrahiert zunehmend abstrakte Muster.

In der Übersetzung:

Frühe Schichten könnten erkennen, "das ist ein Verb" oder "dieses Wort ist vergangen angespannt"

Mittlere Schichten könnten erkennen, "das ist eine Frage" oder "das ist über Wetter"

Spätere Schichten kombinieren diese in "übersetzen dies als höfliche Frage über das Wetter von morgen"

// Don't do this!
var wordToNumber = new Dictionary<string, int>
{
    {"cat", 1},
    {"dog", 2},
    {"king", 3},
    {"queen", 4},
    {"man", 5},
    {"woman", 6}
};

Wie Netzwerke lernen: Der Trainingsprozess

Hier ist der clevere Teil: Wir setzen nicht manuell alle diese Gewichte.

Das Netzwerk lernt sie aus Beispielen!Dieser Prozess wird aufgerufenSteigungsabwärtsung

public class WordEmbedding
{
    // Each word is represented by a vector of floats
    private Dictionary<string, float[]> embeddings;

    public float[] GetEmbedding(string word)
    {
        return embeddings[word]; // e.g., [0.2, -0.5, 0.8, 0.1, ...]
    }

    // Calculate similarity between two words
    public double Similarity(string word1, string word2)
    {
        var vec1 = GetEmbedding(word1);
        var vec2 = GetEmbedding(word2);

        // Cosine similarity: how "aligned" are the vectors?
        return CosineSimilarity(vec1, vec2);
    }

    private double CosineSimilarity(float[] a, float[] b)
    {
        double dot = 0, magA = 0, magB = 0;
        for (int i = 0; i < a.Length; i++)
        {
            dot += a[i] * b[i];
            magA += a[i] * a[i];
            magB += b[i] * b[i];
        }
        return dot / (Math.Sqrt(magA) * Math.Sqrt(magB));
    }
}
graph TD
    subgraph "2D Projection of 300D Space"
        King[King<br/>0.8, 0.6]
        Queen[Queen<br/>0.75, 0.55]
        Man[Man<br/>0.3, 0.2]
        Woman[Woman<br/>0.25, 0.15]
        Dog[Dog<br/>-0.6, 0.4]
        Cat[Cat<br/>-0.65, 0.38]
    end

    King -.similar.-> Queen
    Man -.similar.-> Woman
    Dog -.similar.-> Cat
    King -.same vector.-> Man
    Queen -.same vector.-> Woman

Für jede falsche Antwort stellt das Netzwerk fest, "welche Gewichte für diesen Fehler am meisten verantwortlich waren?" und packt sie ein kleines bisschen.king - man + woman ≈ queen

public float[] AnalogicalReasoning(string a, string b, string c)
{
    // king - man + woman = ?
    var vecA = GetEmbedding(a); // king
    var vecB = GetEmbedding(b); // man
    var vecC = GetEmbedding(c); // woman

    var result = new float[vecA.Length];
    for (int i = 0; i < vecA.Length; i++)
    {
        result[i] = vecA[i] - vecB[i] + vecC[i];
    }

    // Find the word closest to this vector
    return FindClosestWord(result); // Should return "queen"
}

Nachdem sie Millionen von Beispielen gesehen haben, setzen sich die Gewichte in Werte ein, die gut funktionieren.

Hier ist eine vereinfachte Version in C#:

graph LR
    A["The cat sat on the mat"] --> B[Train Neural Network]
    B --> C["cat → [0.1, 0.5, -0.3, ...]"]
    B --> D["sat → [0.2, -0.1, 0.4, ...]"]
    B --> E["mat → [0.15, 0.45, -0.25, ...]"]

Reale neuronale Netzwerke verwenden einen ausgefeilteren Algorithmus namens

Rückvermehrung

Das berechnet effizient, wie man Gewichte im gesamten Netzwerk anpasst, aber das Prinzip ist das gleiche: lernen Sie aus Fehlern.

graph TD
    subgraph English
        E1[The]
        E2[cat]
        E3[sat]
        E4[on]
        E5[the]
        E6[mat]
    end

    subgraph French
        F1[Le]
        F2[chat]
        F3[s'est assis]
        F4[sur]
        F5[le]
        F6[tapis]
    end

    E2 -. focus 90% .-> F2
    E3 -. focus 70% .-> F3
    E4 -. focus 80% .-> F4
    E6 -. focus 85% .-> F6
    E1 -. focus 30% .-> F1

Wort-Embeddings: Sprache in Mathematik verwandelnJetzt haben wir unsere erste große Herausforderung: Wie speisen wir Worte in ein neuronales Netzwerk ein?

Wir müssen Text in Zahlen umwandeln.

Das Problem mit der Ein-Heiß-Enkodierung

public class AttentionMechanism
{
    // Calculate attention weights for each source word
    public double[] CalculateAttention(
        float[] currentTargetState,     // Where we are in translation
        float[][] sourceWordStates)     // All source words
    {
        int sourceLength = sourceWordStates.Length;
        double[] scores = new double[sourceLength];

        // Step 1: Calculate relevance scores
        for (int i = 0; i < sourceLength; i++)
        {
            scores[i] = DotProduct(currentTargetState, sourceWordStates[i]);
        }

        // Step 2: Convert to probabilities (softmax)
        return Softmax(scores);
    }

    public float[] ApplyAttention(
        double[] attentionWeights,
        float[][] sourceWordStates)
    {
        // Create weighted average of source words
        int dim = sourceWordStates[0].Length;
        float[] result = new float[dim];

        for (int i = 0; i < sourceWordStates.Length; i++)
        {
            for (int j = 0; j < dim; j++)
            {
                result[j] += (float)(attentionWeights[i] * sourceWordStates[i][j]);
            }
        }

        return result;
    }

    private double[] Softmax(double[] scores)
    {
        double[] result = new double[scores.Length];
        double sum = 0;

        for (int i = 0; i < scores.Length; i++)
        {
            result[i] = Math.Exp(scores[i]);
            sum += result[i];
        }

        for (int i = 0; i < scores.Length; i++)
        {
            result[i] /= sum;
        }

        return result;
    }

    private double DotProduct(float[] a, float[] b)
    {
        double sum = 0;
        for (int i = 0; i < a.Length; i++)
        {
            sum += a[i] * b[i];
        }
        return sum;
    }
}

Der naive Ansatz ist "Ein-Hot-Kodierung" - jedem Wort eine eindeutige Zahl zuweisen:

  1. Aber das hat ein großes Problem: Die Zahlen sind willkürlich.
  2. Das Netzwerk kann nicht sagen, dass "Katze" und "Hund" ähnlicher sind als "Katze" und "Queen".
  3. Die Zahlen 1 und 2 bedeuten nichts.
  4. Die Lösung: Wort-Embeddings

Stattdessen repräsentieren wir jedes Wort als

Vektor

graph TD
    subgraph "English (Source)"
        E1[The]
        E2[agreement]
        E3[on]
        E4[the]
        E5[European]
        E6[Economic]
        E7[Area]
    end

    subgraph "German (Target)"
        G1[Das]
        G2[Abkommen]
        G3[über]
        G4[den]
        G5[Europäischen]
    end

    E1 -->|0.8| G1
    E2 -->|0.9| G2
    E3 -->|0.6| G3
    E4 -->|0.3| G3
    E5 -->|0.85| G5
    E6 -->|0.7| G5

von Zahlen, in der Regel 300-1000 Abmessungen.

Ähnliche Wörter erhalten ähnliche Vektoren:

Hier ist, was Einbettungen magisch macht - sie erfassen semantische Beziehungen:**Das berühmte Beispiel:**Wie werden Einbettungen gelernt?

graph LR
    subgraph "Self-Attention for 'bank'"
        B[bank]
        R[river]
        W[water]
        F[fish]
    end

    B -.high attention.-> R
    B -.high attention.-> W
    B -.medium attention.-> F

Einbettungen werden durch die Ausbildung eines neuronalen Netzwerks auf eine einfache Aufgabe gelernt: "Prädiktieren Sie die umgebenden Wörter".

Die Idee ist, dass Wörter, die in ähnlichen Kontexten erscheinen, ähnliche Bedeutungen haben sollten.

Nach dem Training auf Milliarden von Sätzen enden Wörter, die in ähnlichen Kontexten erscheinen, mit ähnlichen Einbettungen. "Katze" und "Hund" erscheinen beide in der Nähe von "pet", "feed", "cute", so dass ihre Einbettungen in der Nähe zusammen enden.Achtung: Der Game ChangerHier ist ein kritischer Einblick: Wenn man "Die Katze saß auf der Matte" ins Französische übersetzt, sind verschiedene Ausgangswörter wichtig für verschiedene Zielwörter:

graph LR
    subgraph Encoder
        E1[Word Embeddings] --> E2[Self-Attention Layer 1]
        E2 --> E3[Self-Attention Layer 2]
        E3 --> E4[Self-Attention Layer N]
        E4 --> E5[Contextual Representations]
    end

    subgraph Decoder
        D1[Previous Translations] --> D2[Self-Attention]
        D2 --> D3[Cross-Attention to Encoder]
        D3 --> D4[Feed Forward]
        D4 --> D5[Next Word Prediction]
    end

    E5 -.provides context.-> D3

Achtung

ist ein Mechanismus, der das Netzwerk bei der Erzeugung jedes Ausgabewortes auf verschiedene Teile des Eingangs "fokussieren" lässt.

public class TransformerEncoder
{
    private WordEmbedding wordEmbedding;
    private SelfAttentionLayer[] layers;

    public float[][] Encode(string[] sourceWords)
    {
        // Step 1: Convert words to embeddings
        float[][] embeddings = sourceWords
            .Select(w => wordEmbedding.GetEmbedding(w))
            .ToArray();

        // Step 2: Add positional encoding (so network knows word order)
        float[][] withPositions = AddPositionalEncoding(embeddings);

        // Step 3: Apply multiple self-attention layers
        float[][] representations = withPositions;
        foreach (var layer in layers)
        {
            representations = layer.Forward(representations);
        }

        return representations; // Contextual representations for each word
    }

    private float[][] AddPositionalEncoding(float[][] embeddings)
    {
        // Add position-specific patterns so network knows word order
        // (transformers don't naturally understand sequence order)
        int sequenceLength = embeddings.Length;
        int embeddingDim = embeddings[0].Length;

        for (int pos = 0; pos < sequenceLength; pos++)
        {
            for (int i = 0; i < embeddingDim; i++)
            {
                double angle = pos / Math.Pow(10000, (2.0 * i) / embeddingDim);
                // Use sine for even dimensions, cosine for odd
                embeddings[pos][i] += (float)(i % 2 == 0 ? Math.Sin(angle) : Math.Cos(angle));
            }
        }

        return embeddings;
    }
}

Wie Aufmerksamkeit funktioniert

  • Denken Sie an Aufmerksamkeit als Frage: "Um dieses Wort zu übersetzen, welchen Quellwörtern sollte ich Aufmerksamkeit schenken?"
  • Der Aufmerksamkeitsmechanismus:
  • Vergleicht den aktuellen Zielzustand mit jedem Quellwort

Berechnet eine "Relevanz-Score" für jedes Quellwort

Konvertiert Partituren in Wahrscheinlichkeiten (sie summieren sich auf 1)

public class TransformerDecoder
{
    private WordEmbedding targetEmbedding;
    private SelfAttentionLayer[] selfAttentionLayers;
    private CrossAttentionLayer[] crossAttentionLayers;
    private FeedForwardLayer[] feedForwardLayers;

    public string[] Decode(float[][] encodedSource, int maxLength)
    {
        List<string> translation = new List<string>();
        translation.Add("<START>"); // Special token to begin

        while (translation.Count < maxLength)
        {
            // Get next word
            string nextWord = GenerateNextWord(encodedSource, translation.ToArray());

            if (nextWord == "<END>") break; // Stop token

            translation.Add(nextWord);
        }

        return translation.Skip(1).ToArray(); // Remove <START> token
    }

    private string GenerateNextWord(float[][] encodedSource, string[] partialTranslation)
    {
        // Step 1: Embed the partial translation
        float[][] targetEmbeddings = partialTranslation
            .Select(w => targetEmbedding.GetEmbedding(w))
            .ToArray();

        // Step 2: Self-attention on target words
        float[][] selfAttended = ApplySelfAttention(targetEmbeddings);

        // Step 3: Cross-attention to source (this is where translation happens!)
        float[][] crossAttended = ApplyCrossAttention(selfAttended, encodedSource);

        // Step 4: Feed forward
        float[] finalState = ApplyFeedForward(crossAttended[^1]); // Last position

        // Step 5: Predict next word
        return PredictWord(finalState);
    }

    private string PredictWord(float[] state)
    {
        // Convert state to probability distribution over all possible words
        Dictionary<string, double> wordProbabilities = CalculateWordProbabilities(state);

        // Return most likely word (or sample from distribution)
        return wordProbabilities.OrderByDescending(kv => kv.Value).First().Key;
    }
}

Erstellt einen gewichteten Durchschnitt von Quellwörtern basierend auf diesen Wahrscheinlichkeiten

sequenceDiagram
    participant Input as Source Sentence
    participant Encoder
    participant Decoder
    participant Output as Translation

    Input->>Encoder: "The cat sat"
    Encoder->>Encoder: Build representations
    Encoder->>Decoder: Encoded states

    Decoder->>Decoder: Generate [START]
    Decoder->>Output: Emit START token

    Decoder->>Decoder: Attend to "The" → Generate "Le"
    Decoder->>Output: "Le"

    Decoder->>Decoder: Attend to "cat" → Generate "chat"
    Decoder->>Output: "chat"

    Decoder->>Decoder: Attend to "sat" → Generate "s'est assis"
    Decoder->>Output: "s'est assis"

    Decoder->>Decoder: Generate [END]
    Output->>Output: "Le chat s'est assis"

Aufmerksamkeit visualisieren

Bei der Übersetzung von "Das Abkommen über den Europäischen Wirtschaftsraum wurde im August 1992 unterzeichnet" ins Deutsche sieht die Aufmerksamkeit folgendermaßen aus:

  1. **Bei der Generierung von "Abkommen" (Verein) schenkt das Netzwerk 90% Aufmerksamkeit auf "Verein", 5% auf "das" und 5% auf andere Wörter verteilt.**Selbstachtung: Sich selbst versorgen
  2. Einsatz moderner TransformatorenSelbstachtung
  3. **: Wörter im gleichen Satz achten aufeinander, um bessere Darstellungen zu bauen.**In "Die Fische schwammen in der Nähe des Flussufers", das Wort "Bank" kümmert sich stark um "Fluss", "Fische", und "Wasser", helfen dem Netzwerk zu verstehen, es bedeutet "Flussbank" nicht "Finanzinstitution".
public class TranslationTrainer
{
    private TransformerEncoder encoder;
    private TransformerDecoder decoder;
    private double learningRate = 0.0001;

    public void Train(ParallelCorpus corpus, int epochs)
    {
        foreach (var epoch in Enumerable.Range(0, epochs))
        {
            double totalLoss = 0;
            int batchCount = 0;

            foreach (var batch in corpus.GetBatches(batchSize: 32))
            {
                // Forward pass
                var predictions = new List<string[]>();
                var losses = new List<double>();

                foreach (var pair in batch)
                {
                    // Encode source
                    var encoded = encoder.Encode(pair.Source);

                    // Try to decode target
                    var predicted = decoder.Decode(encoded, pair.Target.Length);

                    // Calculate loss (how different is prediction from target?)
                    double loss = CalculateLoss(predicted, pair.Target);
                    losses.Add(loss);
                }

                // Backward pass: adjust weights
                double avgLoss = losses.Average();
                UpdateWeights(avgLoss);

                totalLoss += avgLoss;
                batchCount++;
            }

            Console.WriteLine($"Epoch {epoch}: Average Loss = {totalLoss / batchCount}");
        }
    }

    private double CalculateLoss(string[] predicted, string[] target)
    {
        // Cross-entropy loss: how far off were our word predictions?
        double loss = 0;

        for (int i = 0; i < Math.Min(predicted.Length, target.Length); i++)
        {
            if (predicted[i] != target[i])
            {
                loss += 1.0; // Simplified - real loss is more nuanced
            }
        }

        return loss / target.Length;
    }

    private void UpdateWeights(double loss)
    {
        // Backpropagation: adjust all weights in encoder and decoder
        // to reduce the loss (simplified here)
        // Real implementation uses automatic differentiation
    }
}

Die Encoder-Decoder-Architektur

graph TD
    A[10M Sentence Pairs] --> B[Initial Random Weights]
    B --> C[Epoch 1: Loss = 5.2]
    C --> D[Epoch 2: Loss = 3.8]
    D --> E[Epoch 3: Loss = 2.1]
    E --> F[Epoch 10: Loss = 0.8]
    F --> G[Epoch 20: Loss = 0.3]
    G --> H[Trained Model!]

    style A stroke-width:2px
    style H stroke-width:4px

Jetzt können wir alles zusammenstellen!

  • Neurale maschinelle Übersetzung verwendet eineEncoder-Decoder
  • **Architektur:**Der Encoder: Die Quelle verstehen
  • **Die Aufgabe des Encoders ist es, den Quellsatz zu lesen und reiche Darstellungen aufzubauen:**Nach der Kodierung hat jedes Quellwort eine Darstellung, die Folgendes erfasst:

Seine Bedeutung (aus der Einbettung)

Seine Position im Satz (aus der Positionskodierung)

graph TD
    subgraph "1. Encoding"
        A1[The] --> E1[emb: 0.2, -0.1, ...]
        A2[cat] --> E2[emb: 0.5, 0.3, ...]
        A3[sat] --> E3[emb: -0.1, 0.4, ...]
        A4[on] --> E4[emb: 0.1, -0.2, ...]
        A5[the] --> E5[emb: 0.2, -0.1, ...]
        A6[mat] --> E6[emb: 0.4, 0.2, ...]
    end

    subgraph "2. Self-Attention in Encoder"
        E1 & E2 & E3 & E4 & E5 & E6 --> SA[Self-Attention]
        SA --> C1[ctx: 0.3, 0.1, ...]
        SA --> C2[ctx: 0.6, 0.4, ...]
        SA --> C3[ctx: -0.2, 0.5, ...]
    end

    subgraph "3. Decoding"
        D1[START] --> G1[Le]
        C2 -.attend.-> G1

        G1 --> G2[chat]
        C2 -.attend.-> G2

        G2 --> G3[s'est assis]
        C3 -.attend.-> G3
    end


Sein Verhältnis zu anderen Worten (aus Selbstachtung)

Der Decoder: Erzeugen der Übersetzung

var theEmbedding = encoder.GetEmbedding("The");
// [0.2, -0.1, 0.3, 0.05, ..., 0.1] (300 dimensions)

Der Decoder erzeugt die Übersetzung ein Wort nach dem anderen:

var catEmbedding = encoder.GetEmbedding("cat");
// [0.5, 0.3, -0.2, 0.4, ..., 0.15] (300 dimensions)

Der vollständige Übersetzungsprozess:

// "cat" attends to other words
var catAttention = attention.CalculateAttention(catEmbedding, allWordEmbeddings);
// [0.1, 0.3, 0.2, 0.05, 0.1, 0.25]
// High attention to "sat" (0.3) and "mat" (0.25)

var catContextual = attention.ApplyAttention(catAttention, allWordEmbeddings);
// Weighted average incorporating context

Schulung eines Übersetzungsmodells

var decoderState = decoder.InitialState();

Die Ausbildung eines Übersetzungsmodells erfordert drei Dinge:

// Attend to source
var sourceAttention = crossAttention.Calculate(decoderState, encodedSource);
// [0.8, 0.05, 0.05, 0.02, 0.05, 0.03]
// Strong focus on "The" (0.8)

var nextWordProbs = decoder.PredictNextWord(decoderState, sourceAttention);
// {"Le": 0.85, "La": 0.08, "Les": 0.04, ...}

var firstWord = "Le";

Paralleler Korpus

decoderState = decoder.UpdateState(decoderState, "Le");

var sourceAttention = crossAttention.Calculate(decoderState, encodedSource);
// [0.05, 0.9, 0.02, 0.01, 0.01, 0.01]
// Strong focus on "cat" (0.9)

var nextWordProbs = decoder.PredictNextWord(decoderState, sourceAttention);
// {"chat": 0.92, "chien": 0.03, ...}

var secondWord = "chat";

: Millionen von Satzpaaren in beiden Sprachen

Final translation: "Le chat s'est assis sur le tapis"

Verlustfunktion

: Wie "falsch" war unsere Vorhersage?

  1. Optimierung: Gewichte anpassen, um den Verlust zu reduzieren
  2. **Die Ausbildung erfordert enorme Daten- und Berechnungsmengen:**Für ein state-of-the-art Modell:
  3. Ausbildungsdaten: 10-100 Millionen Satzpaare
graph LR
    subgraph "Old: Recurrent Neural Networks"
        R1[Word 1] --> R2[Word 2]
        R2 --> R3[Word 3]
        R3 --> R4[Word 4]
    end

    subgraph "New: Transformers"
        T1[Word 1] -.attend.-> T2[Word 2]
        T1 -.attend.-> T3[Word 3]
        T1 -.attend.-> T4[Word 4]
        T2 -.attend.-> T3
        T2 -.attend.-> T4
        T3 -.attend.-> T4
    end


Ausbildungszeit

: 1-4 Wochen auf High-End-GPUs

Größe des Modells

public class TranslationService
{
    private readonly HttpClient _httpClient;
    private readonly string _nmtServiceUrl;

    public TranslationService(HttpClient httpClient, IConfiguration config)
    {
        _httpClient = httpClient;
        _nmtServiceUrl = config["NMT:ServiceUrl"];
    }

    public async Task<TranslationResult> TranslateAsync(
        string text,
        string sourceLang,
        string targetLang)
    {
        var request = new TranslationRequest
        {
            Text = new[] { text },
            SourceLang = sourceLang,
            TargetLang = targetLang
        };

        var response = await _httpClient.PostAsJsonAsync(
            $"{_nmtServiceUrl}/translate",
            request);

        response.EnsureSuccessStatusCode();

        var result = await response.Content
            .ReadFromJsonAsync<TranslationResponse>();

        return new TranslationResult
        {
            Original = text,
            Translated = result.Translated[0],
            SourceLanguage = sourceLang,
            TargetLanguage = targetLang,
            TranslationTime = result.TranslationTime
        };
    }
}

public class TranslationRequest
{
    [JsonPropertyName("text")]
    public string[] Text { get; set; }

    [JsonPropertyName("source_lang")]
    public string SourceLang { get; set; }

    [JsonPropertyName("target_lang")]
    public string TargetLang { get; set; }
}

public class TranslationResponse
{
    [JsonPropertyName("translated")]
    public string[] Translated { get; set; }

    [JsonPropertyName("translation_time")]
    public double TranslationTime { get; set; }
}

: 100M bis 1B+ Parameter (Gewichte)

// In your controller or service
public class BlogPostController : ControllerBase
{
    private readonly TranslationService _translator;

    public async Task<IActionResult> TranslatePost(int postId, string targetLang)
    {
        var post = await _blogService.GetPostAsync(postId);

        var translatedTitle = await _translator.TranslateAsync(
            post.Title,
            "en",
            targetLang);

        var translatedContent = await _translator.TranslateAsync(
            post.Content,
            "en",
            targetLang);

        return Ok(new
        {
            Title = translatedTitle.Translated,
            Content = translatedContent.Translated,
            OriginalLanguage = "en",
            TargetLanguage = targetLang
        });
    }
}

Alles zusammenstellen: Ein vollständiges Beispiel

Übersetzen wir "Die Katze saß auf der Matte" ins Französische:

Schritt-für-Schritt-Prozess:

public async Task<string> TranslateLongText(string longText, string targetLang)
{
    const int maxChunkSize = 500; // characters

    // Split on paragraph boundaries
    var paragraphs = longText.Split(new[] { "\n\n", "\r\n\r\n" },
        StringSplitOptions.RemoveEmptyEntries);

    var translatedParagraphs = new List<string>();

    foreach (var paragraph in paragraphs)
    {
        if (paragraph.Length <= maxChunkSize)
        {
            var result = await _translator.TranslateAsync(paragraph, "en", targetLang);
            translatedParagraphs.Add(result.Translated);
        }
        else
        {
            // Split long paragraph into sentences
            var sentences = SplitIntoSentences(paragraph);
            var translatedSentences = new List<string>();

            foreach (var sentence in sentences)
            {
                var result = await _translator.TranslateAsync(sentence, "en", targetLang);
                translatedSentences.Add(result.Translated);
            }

            translatedParagraphs.Add(string.Join(" ", translatedSentences));
        }
    }

    return string.Join("\n\n", translatedParagraphs);
}

Schritt 1: Kodieren Sie "Das"

Schritt 2: Kodieren Sie "Katze"

public async Task<string> TranslateMarkdown(string markdown, string targetLang)
{
    // Extract text from markdown while preserving structure
    var doc = Markdig.Markdown.Parse(markdown);
    var textSegments = new List<(string text, int position)>();

    // Walk the AST and extract translatable text
    foreach (var node in doc.Descendants())
    {
        if (node is LiteralInline literal)
        {
            var text = literal.Content.ToString();
            if (!string.IsNullOrWhiteSpace(text) && !IsImagePath(text))
            {
                textSegments.Add((text, literal.Span.Start));
            }
        }
    }

    // Translate all segments
    var translations = await Task.WhenAll(
        textSegments.Select(async seg => new
        {
            seg.position,
            translated = (await _translator.TranslateAsync(seg.text, "en", targetLang)).Translated
        }));

    // Reconstruct markdown with translations
    var result = markdown;
    foreach (var translation in translations.OrderByDescending(t => t.position))
    {
        result = result.Remove(translation.position, textSegments
            .First(s => s.position == translation.position).text.Length)
            .Insert(translation.position, translation.translated);
    }

    return result;
}

Schritt 3: Selbstaufmerksamkeit

Schritt 4: Decoder beginnt mit

public async Task<Dictionary<string, string>> TranslateBatch(
    IEnumerable<string> texts,
    string targetLang)
{
    const int batchSize = 32;
    var results = new Dictionary<string, string>();

    foreach (var batch in texts.Chunk(batchSize))
    {
        var request = new TranslationRequest
        {
            Text = batch.ToArray(),
            SourceLang = "en",
            TargetLang = targetLang
        };

        var response = await _httpClient.PostAsJsonAsync(
            $"{_nmtServiceUrl}/translate",
            request);

        var result = await response.Content
            .ReadFromJsonAsync<TranslationResponse>();

        for (int i = 0; i < batch.Length; i++)
        {
            results[batch[i]] = result.Translated[i];
        }
    }

    return results;
}

Schritt 5: Generieren Sie "Le"

Schritt 6: "Chat" generieren

graph TD
    A[Translation Request] --> B{Model Size}
    B -->|Small 100M params| C[Fast: 50-100ms]
    B -->|Medium 500M params| D[Medium: 200-500ms]
    B -->|Large 1B+ params| E[Slow: 1-3 seconds]

    A --> F{Hardware}
    F -->|CPU| G[Slow: 2-5x slower]
    F -->|GPU| H[Fast: Baseline]
    F -->|TPU/Special AI chips| I[Very Fast: 2-3x faster]

    style C stroke-width:2px
    style E stroke-width:2px

Schritt 7: Weiter bis

  • Warum Transformer gewonnen habenDie Trafo-Architektur (das "T" in ChatGPT!) wurde dominant, weil:
  • Parallelisierung: Im Gegensatz zu älteren wiederkehrenden Modellen können alle Wörter gleichzeitig verarbeitet werden
  • Weiträumige Abhängigkeiten: Achtung kann zwei Wörter verbinden, egal wie weit auseinander

Skalierbarkeit: Mehr Daten + größeres Modell = bessere Ergebnisse (bis zu einem Punkt)

**Recurrent Netzwerke verarbeiten sequentiell (langsam!), während Transformatoren alle Wörter gleichzeitig (schnell!) verarbeiten.**NMT in C#: Ein praktisches Beispiel

Nun, da Sie verstehen, wie es funktioniert, hier ist, wie Sie es tatsächlich in einer .NET-Anwendung verwenden würden:

Verwendung:

  1. Gemeinsame Herausforderungen und Lösungen1.
  2. Umgang mit langen TextenNMT-Modelle haben Eingangslängengrenzwerte (typischerweise 512-1024 Token).
  3. **Lösung: Brocken!**2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  4. Formatierung erhaltenMarkdown, HTML und andere Markups können NMT-Modelle verwechseln:
  5. **3. Der Präsident. — Das Wort hat die Fraktion der Europäischen Volkspartei (Christlich-Demokratische Fraktion).**Batch-Verarbeitung für Effizienz

Einen Satz nach dem anderen zu übersetzen, ist langsam.

Los, fangt sie an!

  • LeistungsmerkmaleDas Verständnis der Rechenkosten hilft Ihnen, bessere Lösungen zu entwickeln:
  • **Typische Leistung (pro Satz auf GPU):**Kleine Modelle
  • (100M-Params): 50-100msMittlere Modelle
  • (500M-Params): 200-500msGroße Modelle

(1B+ Params): 1-3 Sekunden

Now when you hit "translate" on your blog posts, you'll know exactly what's happening under the hood! 🚀

CPU gegen GPU

: GPU ist 2-10x schneller für NMT

  • Batch-Verarbeitung: Kann 100+ Sätze/Sekunde mit Batching erreichen
  • SchlußfolgerungNeurale maschinelle Übersetzung mag mag wie Magie erscheinen, aber es ist eigentlich eine elegante Kombination aus mehreren cleveren Ideen:
  • Einbettungen: Wörter als Vektoren darstellen, die Bedeutung erfassen
  • Neurale Netze: Muster aus Millionen von Beispielen lernen
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.