Back to "Hur Neural Machine översättning faktiskt fungerar: En utvecklare guide"

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

Hur Neural Machine översättning faktiskt fungerar: En utvecklare guide

Sunday, 09 November 2025

Inledning

Om du har följt den här bloggen, vet du att jag är lite besatt av maskinöversättning.Jag har skrivit om, användning av EasyNMTTjänster avseende översättning av byggnaders bakgrund, och ävenförbättra EasyNMT**Men jag insåg att jag aldrig riktigt har förklarat

hur

Neural maskinöversättning fungerar under huven.

OBS: Detta är en del av mina experiment med AI / ett sätt att spendera $ 1000 Calude Code Web krediter.

  • Jag har matat detta en BUNCH av papper, min förståelse, frågor jag var tvungen att generera denna artikel.
  • Det är kul och fyller en lucka jag inte har sett fyllas någon annanstans.
  • Så låt oss fixa det!
  • Detta inlägg tar dig från "översättning är magi" till "översättning är smart matematik" genom att förklara de viktigaste begreppen bakom neural maskin översättning på klar engelska (med massor av diagram och några C#-kod för att göra det konkret).
  • I slutet av detta inlägg, kommer du att förstå:

Vad artificiella neurala nätverk är och hur de lär sig

Hur ord blir tal (inbäddningar)

Vad "attention" betyder i AI (spoiler: det handlar inte om att vara uppmärksam)

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

Hur encoder-dekoder arkitekturen fungerar

Varför transformatorer tog över världen

Oroa dig inte om du inte är en matte person - Jag ska hålla detta så praktiskt och visuellt som möjligt.

Låt oss dyka in!

Kärnan Idé: Översättning som nummer Crunching

  1. Här är det första tankebändande konceptet: neural maskinöversättning behandlar översättning som en rent matematisk process.
  2. Du matar i en mening, systemet konverterar det till siffror, utför miljontals matematiska operationer, och ut pops en översättning.
  3. Men vänta - hur konverterar man ord till siffror?
  4. Och hur kan matte "förstå" språket?

Vi börjar med byggstenarna.

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

Konstgjorda neurala nätverk: Stiftelsen

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);
    }
}

Innan vi kan förstå neurala maskinöversättningar måste vi förstå artificiella neurala nätverk.Math.TanhTrots det tjusiga namnet är de faktiskt ganska enkla i sin kärna.

Vad är en konstgjord neuron?

En artificiell neuron är en matematisk funktion som:

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

Tar flera ingångar

  • Multiplicerar varje insats med en vikt (hur viktig är denna insats?)
  • Lägger till dem alla
  • Gäller en "aktiveringsfunktion" för att producera en utgång

Här är en visuell representation:

Låt oss göra det här konkret med lite C#-kod:

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

Aktiveringsfunktionen (i detta fall)**) är vad som gör neurala nätverk intressant.**Det introducerar icke-linjäritet, vilket gör det möjligt för nätverket att lära sig komplexa mönster.

Utan det, oavsett hur många neuroner ni staplade ihop, skulle ni bara ha en tjusig linjär 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);
            }
        }
    }
}

Från neuroner till nätverk**Magin händer när du kopplar ihop tusentals eller miljoner av dessa nervceller i lager:**Varje lager "raffinaderier" representationen av inmatningen, extraherar alltmer abstrakta mönster.

I översättning:

Tidiga lager kan upptäcka "detta är ett verb" eller "detta ord är förflutet spänd"

Mellanlager kan upptäcka "detta är en fråga" eller "detta handlar om väder"

Senare lager kombinerar dessa till "översätt detta som en artig fråga om morgondagens väder"

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

Hur nätverk lär sig: Utbildningsprocessen

Här är den smarta delen: vi ställer inte in alla vikter manuellt.

Nätverket lär sig dem av exempel!Denna process kallaslutning nedstigning

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 varje fel svar, nätverket räknar ut "vilka vikter var mest ansvariga för detta fel?" och justerar dem en liten bit.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"
}

Efter att ha sett miljontals exempel slår sig vikterna ner på värderingar som fungerar bra.

Här är en förenklad version i 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, ...]"]

Riktiga neurala nätverk använder en mer sofistikerad algoritm som kallas

ryggförökning

som effektivt beräknar hur man justerar vikter i hela nätet, men principen är densamma: lära av misstag.

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

Ordinslag: Omvandla språk till matematikNu slår vi till mot vår första stora utmaning: hur kan vi föra in ord i ett neuralt nätverk?

Vi måste omvandla text till siffror.

Problemet med en het kodning

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;
    }
}

Den naiva metoden är "one-hot kodning" - tilldela varje ord ett unikt nummer:

  1. Men detta har ett enormt problem: talen är godtyckliga.
  2. Nätverket kan inte säga att "katt" och "hund" är mer lika än "katt" och "queen".
  3. Siffrorna 1 och 2 betyder ingenting.
  4. Lösningen: Ordinslag

I stället representerar vi varje ord som en

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

av siffror, vanligtvis 300-1000 dimensioner.

Liknande ord får liknande vektorer:

Här är vad som gör inbäddningar magiska - de fångar semantiska relationer:**Det berömda exemplet:**Hur lär man sig sängkläder?

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

Inbäddningar lärs genom att träna ett neuralt nätverk på en enkel uppgift: "prediktera de omgivande orden".

Tanken är att ord som förekommer i liknande sammanhang ska ha liknande betydelser.

Efter utbildning på miljarder meningar, ord som förekommer i liknande sammanhang slutar med liknande inbäddningar. "Katt" och "hund" båda visas nära "pet", "feed", "cute", så deras inbäddningar hamnar nära varandra.Uppmärksamhet: Game ChangerHär är en kritisk insikt: när man översätter "Katten satt på mattan" till franska, olika källord är viktiga för olika målord:

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

Uppmärksamhet

är en mekanism som låter nätverket "fokusera" på olika delar av indatan när man genererar varje utdataord.

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;
    }
}

Hur uppmärksamhet fungerar

  • Tänk på uppmärksamheten som frågar: "Att översätta detta ord, vilka källord ska jag uppmärksamma?"
  • Uppmärksamhetsmekanismen:
  • Jämför aktuellt måltillstånd med varje källord

Beräknar en "relevanspoäng" för varje källord

Konverterar poäng till sannolikheter (de summan till 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;
    }
}

Skapar ett vägt genomsnitt av källord baserat på dessa sannolikheter

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"

Visualisera uppmärksamhet

Vid översättningen av "Avtalet om Europeiska ekonomiska samarbetsområdet undertecknades i augusti 1992" till tyska ser uppmärksamheten ut så här:

  1. **När man skapar "Abkommen" (avtal) ägnar nätverket 90 % uppmärksamhet åt "avtalet", 5 % åt "avtalet" och 5 % fördelas bland annat.**Självuppmärksamhet: Att ta del av sig själv
  2. Användning av moderna transformatorerSjälvuppmärksamhet
  3. **: Ord i samma mening tar hand om varandra för att skapa bättre representationer.**I "Fisken simmade nära flodbanken" är ordet "bank" starkt inriktat på "älv", "fisk" och "vatten", vilket hjälper nätverket att förstå att det betyder "flodbank" inte "finansinstitut".
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
    }
}

Arkitekturen för kodare och kodare

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

Nu kan vi sätta ihop allt!

  • Neural maskin översättning använder enkodare för kodare
  • **arkitektur:**Kodaren: Förstå källan
  • **Encoderns uppgift är att läsa källmeningen och bygga upp rika representationer:**Efter kodning har varje källkodsord en representation som fångar:

Dess betydelse (från inbäddningen)

Dess position i meningen (från positionskodning)

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


Dess förhållande till andra ord (från självuppmärksamhet)

Avkodaren: Skapa översättningen

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

Dekodern genererar översättningen ett ord i taget:

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

Den fullständiga översättningsprocessen:

// "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

Utbildning av en översättningsmodell

var decoderState = decoder.InitialState();

Utbildning av en översättningsmodell kräver tre saker:

// 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";

Parallella corpus

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";

: Miljoner par meningar på båda språken

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

Förlustfunktion

: Hur "fel" var vår förutsägelse?

  1. Optimering: Justera vikter för att minska förlusten
  2. **Utbildning kräver enorma mängder data och beräkningar:**För en toppmodern modell:
  3. Utbildningsuppgifter: 10-100 miljoner par meningar
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


Utbildningstid

: 1-4 veckor på avancerade GPU:er

Förlagans storlek

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 till 1B+ parametrar (vikter)

// 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
        });
    }
}

Att sätta ihop allt: ett fullständigt exempel

Låt oss spåra genom att översätta "katten satt på mattan" till franska:

Steg-för-steg-process:

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);
}

Steg 1: Koda "The"

Steg 2: Koda "katt"

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;
}

Steg 3: Självuppmärksamhet

Steg 4: Dekoder börjar med

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;
}

Steg 5: Skapa "Le"

Steg 6: Skapa "chat"

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

Steg 7: Fortsätt tills

  • Varför transformers vannTransformatorarkitekturen (T i ChatGPT!) blev dominerande eftersom:
  • Parallellisering: Till skillnad från äldre återkommande modeller kan alla ord behandlas samtidigt
  • Långdistansberoenden: Uppmärksamhet kan ansluta två ord, oavsett hur långt ifrån varandra

Skalbarhet: Mer data + större modell = bättre resultat (upp till en punkt)

**Återkommande nätverk processa sekventiellt (slow!), medan transformatorer bearbeta alla ord på en gång (snabb!).**Använda NMT i C#: Ett praktiskt exempel

Nu när du förstår hur det fungerar, här är hur du faktiskt skulle använda det i en .NET-applikation:

Användning:

  1. Gemensamma utmaningar och lösningar1. Vad är det som händer?
  2. Hantering av långa texterNMT-modeller har indatalängdsgränser (vanligtvis 512-1024 polletter).
  3. **Lösning: bitning!**Två.
  4. Bevarande av formateringMarkering, HTML och andra markeringar kan förvirra NMT-modeller:
  5. **3. Vad är det som händer?**Partibearbetning för effektivitet

Att översätta en mening åt gången är långsamt.

Sätt på dem!

  • PrestandaegenskaperAtt förstå beräkningskostnaderna hjälper dig att skapa bättre lösningar:
  • **Typiska prestanda (per mening om GPU):**Små modeller
  • (100M-paramor): 50-100 msMedelstora modeller
  • (500M param): 200-500mStora modeller

(1B+ params): 1-3 sekunder

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

CPU mot GPU

: GPU är 2-10x snabbare för NMT

logo

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