Back to "Cómo funciona realmente la traducción automática neural: Guía de un desarrollador"

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

Cómo funciona realmente la traducción automática neural: Guía de un desarrollador

Sunday, 09 November 2025

Introducción

Si has estado siguiendo este blog, sabes que estoy un poco obsesionado con la traducción automática.He escrito sobre, Uso de EasyNMTcreación de servicios de traducción de antecedentes, e inclusomejorar EasyNMT**Pero me di cuenta de que en realidad nunca he explicado

¿Cómo?

traducción automática neuronal trabaja bajo el capó.

NOTA: Esto es parte de mis experimentos con IA / una manera de gastar $1000 Créditos Web Código Calude.

  • He alimentado esto un montón de papeles, mi comprensión, preguntas que tuve que generar este artículo.
  • Es divertido y llena un hueco que no he visto en ningún otro lugar.
  • Así que vamos a arreglar eso!
  • Este post te llevará de "traducción es magia" a "traducción es matemática inteligente" explicando los conceptos clave detrás de la traducción automática neuronal en inglés (con un montón de diagramas y un poco de código C# para hacerlo concreto).
  • Al final de este post, entenderás:

Qué son las redes neuronales artificiales y cómo aprenden

Cómo las palabras se convierten en números (embeddings)

Lo que significa "atención" en IA (spoiler: no se trata de prestar atención)

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

Cómo funciona la arquitectura codificadora-decodificadora

¿Por qué los transformadores se apoderaron del mundo?

No te preocupes si no eres una persona de matemáticas - voy a mantener esto lo más práctico y visual posible.

¡Vamos a zambullirnos!

La idea central: la traducción como el recorte de números

  1. Aquí está el primer concepto de dominio de la mente: la traducción automática neuronal trata la traducción como un proceso puramente matemático.
  2. Se alimenta en una frase, el sistema lo convierte en números, realiza millones de operaciones matemáticas, y sale una traducción.
  3. Pero espera - ¿cómo convertir palabras a números?
  4. ¿Y cómo "entiende" el lenguaje matemático?

Empecemos con los bloques de construcción.

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

Redes Neural Artificiales: La Fundación

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

Antes de que podamos entender la traducción automática neuronal, necesitamos entender las redes neuronales artificiales.Math.TanhA pesar del nombre elegante, en realidad son bastante simples en su núcleo.

¿Qué es una neurona artificial?

Una neurona artificial es una función matemática que:

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

Toma múltiples entradas

  • Multiplica cada entrada por un peso (¿cuán importante es esta entrada?)
  • Los suma a todos.
  • Aplica una "función de activación" para producir una salida

Aquí hay una representación visual:

Hagamos esto concreto con un poco de código C#:

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

La función de activación (en este caso**) es lo que hace interesantes las redes neuronales.**Introduce la no linealidad, permitiendo que la red aprenda patrones complejos.

Sin ella, no importa cuántas neuronas apilaste, tendrías una función lineal de lujo.

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

De las neuronas a las redes**La magia ocurre cuando conectas miles o millones de estas neuronas en capas:**Cada capa "refine" la representación de la entrada, extrayendo patrones cada vez más abstractos.

En traducción:

Las primeras capas podrían detectar "esto es un verbo" o "esta palabra es tiempo pasado"

Capas medias podrían detectar "esto es una pregunta" o "esto es sobre el tiempo"

Las capas posteriores las combinan en "traducir esto como una pregunta educada sobre el clima de mañana"

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

Cómo aprenden las redes: El proceso de formación

Aquí está la parte inteligente: no establecemos manualmente todos esos pesos.

¡La red los aprende de ejemplos!Este proceso se llamadescenso de gradiente

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

Para cada respuesta incorrecta, la red averigua "¿qué pesos fueron los más responsables de este error?" y los retoca un poco.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"
}

Después de ver millones de ejemplos, los pesos se establecen en valores que funcionan bien.

Aquí hay una versión simplificada en 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, ...]"]

Las redes neuronales reales utilizan un algoritmo más sofisticado llamado

retropropagación

que calcula eficientemente cómo ajustar los pesos en toda la red, pero el principio es el mismo: aprender de los errores.

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

Incrustaciones de palabras: Convirtiendo el lenguaje en matemáticasAhora nos enfrentamos a nuestro primer gran desafío: ¿cómo alimentamos las palabras en una red neuronal?

Tenemos que convertir el texto en números.

El problema con la codificación de un solo calor

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

El enfoque ingenuo es "codificación única" - asignar a cada palabra un número único:

  1. Pero esto tiene un gran problema: los números son arbitrarios.
  2. La red no puede decir que "gato" y "perro" son más similares que "gato" y "queen".
  3. Los números 1 y 2 no significan nada.
  4. La solución: La palabra encaja

En su lugar, representamos cada palabra como un

vector

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

de números, típicamente 300-1000 dimensiones.

Palabras similares consiguen vectores similares:

Esto es lo que hace mágicas las incrustaciones - capturan las relaciones semánticas:El famoso ejemplo:¿Cómo se aprenden las incrustaciones?

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

Las incrustaciones se aprenden mediante el entrenamiento de una red neuronal en una tarea simple: "predecir las palabras circundantes".

La idea es que las palabras que aparecen en contextos similares deben tener significados similares.

Después del entrenamiento en miles de millones de oraciones, palabras que aparecen en contextos similares terminan con incrustaciones similares. "Cat" y "dog" ambos aparecen cerca de "pet", "feed", "lindo", por lo que sus incrustaciones terminan juntas.Atención: El cambiador de juegoHe aquí una visión crítica: al traducir "El gato se sentó en la estera" al francés, diferentes palabras de origen importan para diferentes palabras objetivo:

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

Atención

es un mecanismo que permite a la red "enfocarse" en diferentes partes de la entrada al generar cada palabra de salida.

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

Cómo funciona la atención

  • Piense en la atención como preguntar: "Para traducir esta palabra, ¿a qué palabras de origen debo prestar atención?"
  • El mecanismo de atención:
  • Compara el estado de destino actual con cada palabra fuente

Calcula una "puntuación de relevancia" para cada palabra fuente

Convierte las puntuaciones en probabilidades (suman a 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;
    }
}

Crea un promedio ponderado de palabras de origen basado en estas probabilidades

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"

Visualizar la atención

Al traducir "El Acuerdo sobre el Espacio Económico Europeo se firmó en agosto de 1992" al alemán, la atención se ve así:

  1. **Al generar "Abkommen" (acuerdo), la red presta 90% de atención a "acuerdo", 5% a "el", y 5% distribuido entre otras palabras.**Autoatención: Atenderse a Sí Mismo
  2. Uso de transformadores modernosautoatención
  3. **: las palabras en la misma frase se atienden mutuamente para construir mejores representaciones.**En "El pez nadó cerca de la orilla del río", la palabra "banco" atiende fuertemente a "río", "pescado" y "agua", ayudando a la red a entender que significa "banco fluvial" no "institución financiera".
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
    }
}

La arquitectura codificadora-decodificadora

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

¡Ahora podemos juntarlo todo!

  • Traducción automática neural utiliza unencoder-decoder
  • **arquitectura:**El Codificador: Entendiendo la Fuente
  • **El trabajo del codificador es leer la frase fuente y construir representaciones ricas:**Después de codificar, cada palabra fuente tiene una representación que captura:

Su significado (de la incrustación)

Su posición en la oración (de codificación posicional)

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


Su relación con otras palabras (desde la autoatención)

El Decodificador: Generando la Traducción

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

El decodificador genera la traducción de una palabra a la vez:

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

El proceso completo de traducción:

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

Formación de un modelo de traducción

var decoderState = decoder.InitialState();

La formación de un modelo de traducción requiere tres cosas:

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

Cuerpo paralelo

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

: Millones de pares de oraciones en ambos idiomas

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

Función de pérdida

: ¿Cómo de "equivocado" fue nuestra predicción?

  1. Optimización: Ajustar los pesos para reducir la pérdida
  2. **El entrenamiento requiere enormes cantidades de datos y computación:**Para un modelo de vanguardia:
  3. Datos de capacitación: 10-100 millones de pares de frases
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


Tiempo de capacitación

: 1-4 semanas en GPU de gama alta

Tamaño del modelo

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 a 1B+ parámetros (pesos)

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

Reunirlo todo: un ejemplo completo

Vamos a rastrear a través de la traducción "El gato se sentó en la estera" al francés:

Proceso paso a paso:

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

Paso 1: Codifique "El"

Paso 2: Codifique "gato"

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

Paso 3: Autoatención

Paso 4: Decodificador comienza con

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

Paso 5: Generar "Le"

Paso 6: Generar "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

Paso 7: Continuar hasta

  • Por qué ganaron los transformadoresLa arquitectura del transformador (la "T" en ChatGPT!) se convirtió en dominante porque:
  • Paralelización: A diferencia de los modelos recurrentes más antiguos, todas las palabras se pueden procesar simultáneamente
  • Dependencias de largo alcance: La atención puede conectar cualquier dos palabras, no importa cuán lejos

Escalabilidad: Más datos + modelo más grande = mejores resultados (hasta un punto)

**Las redes recurrentes procesan secuencialmente (¡lento!), mientras que los transformadores procesan todas las palabras a la vez (¡rápido!).**Uso de NMT en C#: Un ejemplo práctico

Ahora que entiendes cómo funciona, así es como lo usarías en una aplicación .NET:

Uso:

  1. Desafíos y soluciones comunes1.
  2. Manejo de textos largosLos modelos NMT tienen límites de longitud de entrada (normalmente 512-1024 tokens).
  3. **Solución: trocear!**2.
  4. Preservar formatoMarkdown, HTML y otros tipos de marcado pueden confundir los modelos NMT:
  5. **3.**Procesamiento por lotes para la eficiencia

Traducir una frase a la vez es lento.

¡Cógelos!

  • Características del rendimientoComprender los costos computacionales le ayuda a diseñar mejores soluciones:
  • **Ejecución típica (por frase en GPU):**Modelos pequeños
  • (params 100M): 50-100msModelos medios
  • (params 500M): 200-500msModelos grandes

(1B+ params): 1-3 segundos

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

CPU vs GPU

: GPU es 2-10x más rápido para NMT

  • Procesamiento por lotes: Puede lograr más de 100 oraciones/segundo con el loteado
  • ConclusiónLa traducción automática neural puede parecer mágica, pero en realidad es una elegante combinación de varias ideas inteligentes:
  • Incrustaciones: Representar palabras como vectores que capturan significado
  • Redes neurales: Aprender patrones de millones de ejemplos
logo

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