4651 words
23 minutes
Transformer
2025-09-19
2026-09-20

Transformers are the foundational technology behind most of the modern AI tools we use every day. The most prominent real-life applications of Transformer models in Natural Language Processing (NLP), for example, include

  • Machine Translation

    This was the original task the Transformer was designed for. Services like Google Translate use Transformer models to read an entire sentence in one language, understand its full context, and then generate a high-quality translation in another language.

  • AI Chatbots and Generative AI

    This is the most famous application today. Tools like ChatGPT, Gemini, and Claude are built on large Transformer models (specifically Decoder-only models like GPT). They are trained to predict the next logical word in a response, allowing them to hold conversations, answer complex questions, write essays, and much more.

    The model takes user-typed question (such as “What is the capital of France?”) as an initial sequence of text, converts it into numerical tokens, and feeds it through transformer, which then “understands” the full context of the question, also called prompt. The model’s response is in the form of a prediction for the very first word of its answer (e.g., “The”).

    In the “Generation” stage, Gemini, for example, employees autoregressive “loop” to build the response one word (or token) at a time, with each new word depending on all the words that came before it:

    1. To get Word #1 (“The”):

      • Input: “What is the capital of France?”
      • Output: “The”
    2. To get Word #2 (“capital”):

      • Input: “What is the capital of France? The”
      • Output: “capital”
    3. To get Word #3 (“of”):

      • Input: “What is the capital of France? The capital”
      • Output: “of”
    4. To get Word #4 (“France”):

      • Input: “What is the capital of France? The capital of”
      • Output: “France”

    …and so on. The model appends its own previously generated word to the sequence and feeds that new, longer sequence back into itself to predict the very next word.

  • Code Generation (AI Assistants)

    This is a specialized form of text generation where the “language” is code. Tools like GitHub Copilot and other AI-powered IDEs use Transformer models trained on billions of lines of public code. They can read our existing code and comments to suggest auto-completions, write entire functions, or even help us debug.

With this sense of practicality in mind, let’s learn what’s under the hood of transformer.

Introduction#

The Transformer was introduced in the 2017 paper “Attention Is All You Need” by researchers at Google. Its creation was a direct response to the fundamental limitations of the dominant sequence-to-sequence (seq2seq) models of the time: Recurrent Neural Networks (RNNs), including LSTMs and GRUs.

Transformers are everywhere and its models are used to solve all kinds of tasks across different modalities, including natural language processing (NLP), computer vision, audio processing, and more. The 🤗 Transformers library by Hugging Face provides the functionality to create and use those shared models. The Model Hub contains millions of pretrained models that anyone can download and use. We can also upload our own models to the Hub

huggingface
/
transformers
Waiting for api.github.com...
00K
0K
0K
Waiting...

Before diving into how Transformer models work under the hood, let’s look at an example of how they can be used to solve some interesting NLP problems to give us some intuitive feels.

The most basic object in the 🤗 Transformers library is the pipeline() function. It connects a model with its necessary preprocessing and postprocessing steps, allowing us to directly input any text and get an intelligible answer. Let’s take named entity recognition as an example

TIP

Named entity recognition (NER) is a task where the model has to find which parts of the input text correspond to entities such as persons, locations, or organizations.

from transformers import pipeline
ner = pipeline("ner", grouped_entities=True)
ner("My name is Jiaqi and I work at OpenML in Shenzhen.")
Terminal window
[
{'entity_group': 'PER', 'score': 0.99816, 'word': 'Jiaqi'},
{'entity_group': 'ORG', 'score': 0.97960, 'word': 'OpenML'},
{'entity_group': 'LOC', 'score': 0.99321, 'word': 'Shenzhen'}
]

Here the model correctly identified that Jiaqi is a person (PER), OpenML an organization (ORG), and Shenzhen a location (LOC).

There are three main steps involved when we pass some text to the pipeline above:

  1. The text is preprocessed into a format the model can understand.
  2. The preprocessed inputs are passed to the model.
  3. The predictions of the model are post-processed, so we can make sense of them.

The pipeline() function supports multiple modalities allowing us to work with not just text, but also images, audio, and even multimodal tasks. In this post we’ll focus on text tasks

All the Transformer models have been trained as large language models and is primarily composed of 2 blocks:

  1. Encoder (left): The encoder receives an input and builds a representation of it (its features). This means that the model is optimized to acquire understanding from the input.
  2. Decoder (right): The decoder uses the encoder’s representation (features) along with other inputs to generate a target sequence. This means that the model is optimized for generating outputs.
IMPORTANT

Transformers are large language models

A key feature of Transformer models is that they are built with special layers called attention layers. In fact, the title of the paper introducing the Transformer architecture was “Attention Is All You Need”! But before diving into the real business of transformers, we must learn what was before the transformer.

Attention is All You Need - Transformer#

In the previous section, we looked at attention – a ubiquitous method in modern deep learning models that helps improve the performance of neural machine translation applications. The true power of attention, and what drives the amazing abilities of large language models, was, however, first explored in the well-known Attention Is All You Need paper released in 2017. The authors proposed a network architecture called the Transformer, which was solely based on the attention mechanism and removed the recurrence network that we discussed previously. Compared to the recurrence network, the Transformer could be trained in parallel, which tremendously sped up training as we will be seeing shortly.

If transformer removed RNN, what is the point of studying RNN today?

In addition to its foundational role with respect to transformer, RNN has its dominance over efficiency and niche applications.

Transformers are not always the best tool for the job. Their core “self-attention” mechanism has a computational and memory cost that scales quadratically with the sequence length (O(n2)O(n^2)). RNNs, by contrast, scale linearly (O(n)O(n)).

This makes RNNs a better choice in several real-world scenarios:

  • Edge Computing & Mobile Devices: RNNs are much smaller and “lighter” than Transformers. For tasks on a device with limited memory and power (like a smartphone or a smart speaker), an RNN is far more efficient. Our phone likely uses a small RNN for real-time tasks like “wake word” detection (“Hey Google,” “Hey Siri”).
  • Real-Time Time-Series Data: For tasks like predicting stock prices, sensor data, or electricity demand, the data is often a continuous, never-ending stream. An RNN’s design, which processes one step at a time, is a very natural and efficient fit for this kind of streaming data.
  • Specific Small-Scale Problems: If we have a simple sequence task with short dependencies, using a massive Transformer is often overkill. A simple LSTM can train faster, use fewer resources, and perform just as well.

In this section, therefore, we will be looking at transformer – a model that uses attention to boost the speed with which these models can be trained. Transformer outperforms the Google Neural Machine Translation model in specific tasks. The biggest benefit, however, comes from how transformer lends itself to parallelization. It is in fact Google Cloud’s recommendation to use transformer as a reference model to use their Cloud TPU offering. So let’s try to break the model apart and look at how it functions.

TIP

Through the visually educational nature of this book and with over 250 custom-made figures, Hands-On Large Language Models expands this section thoroughly in its Chapter 3:

A High-Level Look#

Let’s begin by looking at the model as a single black box. In a machine translation application, it would take a sentence in one language, and output its translation in another.

Popping open that Optimus Prime goodness, we see an encoding component, a decoding component, and connections between them.

The encoding component is a stack of encoders (In its original paper Attention is All You Need, 6 encoders and decoders each (12 in total) were stacked on top of each other - there’s nothing magical about the number 6, one can definitely experiment with other arrangements). The decoding component is a stack of decoders of the same number.

The encoders are all identical in structure (yet they do not share weights). Each one is broken down into two sub-layers:

The encoder’s inputs first flow through a self-attention layer - a layer that helps the encoder look at other words in the input sentence as it encodes a specific word. We’ll look closer at self-attention later in this post.

The outputs of the self-attention layer are fed to a feed-forward neural network. The exact same feed-forward network is independently applied to each position.

The decoder has both those layers, but between them is an attention layer that helps the decoder focus on relevant parts of the input sentence (similar to what attention does in seq2seq models).

Bringing The Tensors Into The Picture#

Now that we’ve seen the major components of the model, let’s start to look at the various vectors/tensors and how they flow between these components to turn the input of a trained model into an output.

As is the case in NLP applications in general, we begin by turning each input word into a vector using embedding algorithm.

Each word is embedded into a vector of size 512. We’ll represent those vectors with these simple boxes.

The embedding only happens in the bottom-most encoder. The abstraction that is common to all the encoders is that they receive a list of vectors each of the size 512 – In the bottom encoder that would be the word embeddings, but in other encoders, it would be the output of the encoder that’s directly below. The size of this list is hyperparameter we can set – basically it would be the length of the longest sentence in our training dataset.

After embedding the words in our input sequence, each of them flows through each of the two layers of the encoder:

Here we begin to see one key property of the Transformer, which is that the word in each position flows through its own path in the encoder. There are dependencies between these paths in the self-attention layer. The feed-forward layer, however, does not have those dependencies and thus the various paths can be executed in parallel while flowing through the feed-forward layer.

Next, we’ll switch up the example to a shorter sentence and look at what happens in each sub-layer of the encoder.

Encoding#

As we’ve mentioned already, an encoder receives a list of vectors as input. It processes this list by passing these vectors into a ‘self-attention’ layer, then into a feed-forward neural network, then sends out the output upwards to the next encoder.

The word at each position passes through a self-attention process. Then, they each pass through a feed-forward neural network - the exact same network with each vector flowing through it separately.

Self-Attention at a High Level#

Let’s say the following sentence is an input sentence we want to translate:

The animal didn't cross the street because it was too tired

What does “it” in this sentence refer to? Is it referring to the street or to the animal? It’s a simple question to a human, but not as simple to an algorithm. When the model is processing the word “it”, self-attention allows it to associate “it” with “animal”, because as the model processes each word (each position in the input sequence), self-attention allows it to look at other positions in the input sequence for clues that can help lead to a better encoding for this word.

Recall that with RNNs, maintaining a hidden state allows an RNN to incorporate its representation of previous words/vectors it has processed with the current one it’s processing. Self-attention is the method the Transformer uses to bake the “understanding” of other relevant words into the one we’re currently processing.

As we are encoding the word “it” in encoder #5 (the top encoder in the stack), part of the attention mechanism was focusing on “The Animal”, and baked a part of its representation into the encoding of “it”.

Self-Attention in Detail#

Let’s first look at how to calculate self-attention using vectors in steps, then proceed to look at how it’s actually implemented – using matrices.

  1. Create 3 vectors from each of the encoder’s input vectors (in this case, the embedding of each word):

    1. a Query vector
    2. a Key vector, and
    3. a Value vector

    These vectors are created by multiplying the embedding by three matrices from the training process and are smaller in dimension than the embedding vector. Their dimensionality is 64, while the embedding and encoder input/output vectors have dimensionality of 512. They don’t have to be smaller though; this is an architecture choice to make the computation of multiheaded attention (mostly) constant.

    Multiplying X1X_1 by the WQW^Q weight matrix produces q1q_1, the “query” vector associated with that word. We end up creating a “query”, a “key”, and a “value” projection of each word in the input sentence.

    What are the “query”, “key”, and “value” vectors?

    They’re abstractions that are useful for calculating and thinking about attention. We will make them clear in the following discussions

  2. Compute scores of every word in a sentence with respect to one particular word. For instance, when we calculate the self-attention for the first word in this example, “Thinking”, we need to score each word of the input sentence against this word. The score determines how much focus to place on other parts of the input sentence as we encode a word, i.e. “Thinking”, at a certain position.

    The score is calculated by taking the dot product of the query vector with the key vectors of each word we’re scoring. For example, in our 2-word sentence, if we’re processing the self-attention for the word “Thinking”, there would be 2 scores:

    1. the dot product of q1q_1 and k1k_1: q1⋅k1q_1 \cdot k_1
    2. the dot product of q1q_1 and k2k_2: q1⋅k2q_1 \cdot k_2

  3. Divide the scores by 8 (the square root of the dimension of the key vectors used in the paper – 64. This leads to having more stable gradients. There could be other possible values, but this is the default)

  4. Pass the result through a softmax operation, which normalizes the scores so they’re all positive and add up to 1

    This softmax score determines how much each word will be expressed at this position. It would be obvious that a word at its own position has the highest softmax score (such as 0.88 for “Thinking” itself), but sometimes it’s useful to attend to another word that is relevant to the current word.

  5. Multiply each value vector by the softmax score (in preparation to sum them up). The intuition here is to keep intact the values of the word(s) we want to focus on, and drown-out irrelevant words (by multiplying them by tiny numbers like 0.001, for example).

  6. Sum up the weighted value vectors. This produces the output of the self-attention layer at this position (for the first word, “Thinking”).

That concludes the self-attention calculation. The resulting vector is one we can send along to the feed-forward neural network. In the actual implementation, however, this calculation is done in matrix form for faster processing in the following way:

To calculate the Query, Key, and Value matrices, we pack our embeddings into a matrix XX, and multiplying it by the weight matrices we’ve trained (WQW^Q, WKW^K, WVW^V):

Every row in the XX matrix corresponds to a word in the input sentence. We again see the difference in size of the embedding vector (512, or 4 boxes in the figure), and the qq/kk/vv vectors (64, or 3 boxes in the figure above)

Then we, since we are dealing with matrices, condense the rest of the vector steps into one formula to calculate the outputs of the self-attention layer:

The self-attention calculation in matrix form

”Multi-headed” Attention#

The Attention is All You Need paper further refined the self-attention layer by adding a mechanism called “multi-headed” attention. This improves the performance of the attention layer in 2 ways:

  1. It expands the model’s ability to focus on different positions. In the example above, z1z_1 from step 6 contains a little bit of every other encoding, but it could be dominated by the actual word itself. If we’re translating a sentence like “The animal didn’t cross the street because it was too tired”, it would be useful to know which word “it” refers to.

  2. It gives the attention layer multiple “representation subspaces”. As we will see shortly, with multi-headed attention we have not only one, but multiple sets of Query/Key/Value weight matrices (the Transformer uses 8 attention heads, so we end up with 8 sets for each encoder/decoder). Each of these sets is randomly initialized. After training, each set is used to project the input embeddings (or vectors from lower encoders/decoders) into a different representation subspace.

If we do the same self-attention calculation we outlined above, just 8 different times with different weight matrices, we end up with 8 different ZZ matrices

This leaves us with a bit of a challenge. The feed-forward layer is expecting a single matrix (a vector for each word), not eight. We need a way to condense these eight down into a single matrix. We do that by concatenating the matrices and multiplying them by an additional weights matrix WOW^O.

That’s pretty much all there is to multi-headed self-attention. With everything in one visual, here is what how to calculate self-attention:

Representing The Order of The Sequence Using Positional Encoding#

One thing that’s missing from the model as we have described it so far is a way to account for the order of the words in the input sequence.

To address this, the transformer adds a vector to each input embedding. These vectors follow a specific pattern that the model learns, which helps it determine the position of each word, or the distance between different words in the sequence. The intuition here is that adding these values to the embeddings provides meaningful distances between the embedding vectors once they’re projected into qq/kk/vv vectors and during dot-product attention.

To give the model a sense of the order of the words, we add positional encoding vectors — the values of which follow a specific pattern.

If we assumed the embedding has a dimensionality of 4, the actual positional encodings would look like this:

The Attention is All You Need paper generates the positional encoding patterns (formula described in section 3.5) in the following way:

import numpy as np
import matplotlib.pyplot as plt
# https://www.tensorflow.org/tutorials/text/transformer
def get_angles(pos, i, d_model):
angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
return pos * angle_rates
def positional_encoding(position, d_model):
angle_rads = get_angles(
np.arange(position)[:, np.newaxis],
np.arange(d_model)[np.newaxis, :],
d_model
)
# apply sin to even indices in the array; 2i
angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])
# apply cos to odd indices in the array; 2i+1
angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])
pos_encoding = angle_rads[np.newaxis, ...]
return pos_encoding
tokens = 10
dimensions = 64
pos_encoding = positional_encoding(tokens, dimensions)
print (pos_encoding.shape)
plt.figure(figsize=(12,8))
plt.pcolormesh(pos_encoding[0], cmap='viridis')
plt.xlabel('Embedding Dimensions')
plt.xlim((0, dimensions))
plt.ylim((tokens,0))
plt.ylabel('Token Position')
plt.colorbar()
plt.show()

TIP

There are alternative methods for positional encoding. The one presented in paper, however, does give the advantage of being able to scale to unseen lengths of sequences (e.g. if our trained model is asked to translate a sentence longer than any of those in our training set).

Here is an example of alternative in Tensor2Tensor implementation of the Transformer which, instead of interweaving the two signals, concatenates:

A real example of positional encoding for 20 words (rows) with an embedding size of 512 (columns). We can see that it appears split in half down the center. That’s because the values of the left half are generated by one function (which uses sine), and the right half is generated by another function (which uses cosine). They’re then concatenated to form each of the positional encoding vectors.

In the following figure above, each row corresponds to a positional encoding of a vector. So the first row would be the vector we’d add to the embedding of the first word in an input sequence. Each row contains 512 values – each with a value between 11 and −1-1. We’ve color-coded them so the pattern is easy to see

We can see the code for generating this positional encodings in get_timing_signal_1d().

The Residuals#

Each sub-layer (self-attention, feed-forward neural network) in each encoder has a residual connection around it, and is followed by a layer-normalization step.

If we’re to visualize the vectors and the layer-norm operation associated with self attention, it would look like this:

This goes for the sub-layers of the decoder as well. If we are to think of a Transformer of 2 stacked encoders and decoders, it would look something like this:

The Decoder Side#

Now that we’ve covered most of the concepts on the encoder side, we basically know how the components of decoders work as well. But let’s take a look at how they work together.

The encoder start by processing the input sequence. The output of the top encoder is then transformed into a set of attention vectors K and V. These are to be used by each decoder in its “encoder-decoder attention” layer which helps the decoder focus on appropriate places in the input sequence:

After finishing the encoding phase, we begin the decoding phase. Each step in the decoding phase outputs an element for the output sequence (the English translation sentence for example).

The following steps repeat the process until a special symbol is reached indicating the transformer decoder has completed its output. The output of each step is fed to the bottom decoder in the next time step, and the decoders bubble up their decoding results just like the encoders did. And just like we did with the encoder inputs, we embed and add positional encoding to those decoder inputs to indicate the position of each word.

The self-attention layers in the decoder does operate in a slightly different way than the ones in the encoder: In the decoder, the self-attention layer is only allowed to attend to earlier positions in the output sequence. This is done by masking future positions (setting them to -inf) before the softmax step in the self-attention calculation.

The Final Linear and Softmax Layer#

The decoder stack outputs a vector of floats. A final Linear layer followed by a Softmax Layer turns that into an output word.

The Linear layer is a simple fully connected neural network that projects the vector produced by the stack of decoders into a much larger vector called a logits vector.

Let’s assume that our model knows 10,000 unique English words (our model’s “output vocabulary”) that it’s learned from its training dataset. This would make the logits vector 10,000 cells wide – each cell corresponding to the score of a unique word. That is how we interpret the output of the model followed by the Linear layer.

The softmax layer then turns those scores into probabilities (all positive, all add up to 1.0). The cell with the highest probability is chosen, and the word associated with it is produced as the output for this time step.

This figure starts from the bottom with the vector produced as the output of the decoder stack. It is then turned into an output word.

Encoder-Only Transformer#

(Coming soon…)

Decoder-Only Transformer#

(Coming soon…)

Transformer in Practice#

At this point, it is not hard to imagine what transformer really does is fine-tuning a pre-trained model with private data to generate a more customized model. In this section, we would demonstrate an example to show that by fine-tuning Whisper model with German video data.

This would evolve into a robust project, but we would just be showing the minimal-code. Fine-tuning Whisper on domain-specific video data (like German media) is a perfect use case for transformers.

Preparing Hugging Face Datasets#

To fine-tune a pre-trained model with Hugging Face Transformer, we must train with data format that works with Hugging Face ecosystem. This is when Hugging Face Datasets comes into play. The essential code for using Hugging Face Datasets to generate such data is the following:

from datasets import Dataset, Audio
DATASET_PATH = Path("my/dataset/output/path")
valid_records = []
for autio_path, transcript in zip(autio_paths, transcripts):
valid_records.append({
"audio": str(audio_path),
"sentence": transcript
})
hf_dataset = Dataset.from_list(valid_records)
hf_dataset = hf_dataset.cast_column("audio", Audio(sampling_rate=16000))
hf_dataset.save_to_disk(DATASET_PATH)

We can imagine

  • autio_paths is a list of file system paths pointing to a collection of raw audio files for training
  • transcripts is another list of same size containing ordered transcripts of each raw audio files
TIP

hf_dataset.cast_column() casts the ‘audio’ column to Audio feature which handles loading and resampling on the fly

Load a Pre-trained Model#

To begin training, we need to load both the model and its corresponding processor. The processor handles 2 tasks:

  1. converting raw audio into the log-Mel spectrograms the model expects, and
  2. decoding the model’s output tokens back into human-readable German text.

and is loaded with

from transformers import WhisperProcessor
# We use the 'small' variant for a balance between performance and speed
model_id = "openai/whisper-small"
processor = WhisperProcessor.from_pretrained(model_id, language="german", task="transcribe")

Note that the processor is configured specifically for German transcription

To load pre-trained whisper model:

from transformers import WhisperForConditionalGeneration
model = WhisperForConditionalGeneration.from_pretrained(model_id)
TIP

Whisper is designed to be a “zero-shot” model, meaning it usually tries to guess what language is being spoken and whether it should translate it to English or just transcribe it. Since Whisper is a multilingual model capable of many tasks (like translating or identifying languages), there needs some settings to lock it into a specific behavior sometimes after the model is loaded. Two most frequently used settings are

# Force the model to generate German text during training and inference
model.config.forced_decoder_ids = None
model.config.suppress_tokens = []

If we are training a model for a specific task like transcription on one target language only, we would usually use

model.config.forced_decoder_ids = processor.get_decoder_prompt_ids(language="german", task="transcribe")

When we set forced_decoder_ids this way, we are forcing the model to output German text. Without this, the model might occasionally hallucinate and switch to English or another language if the audio is fuzzy. By using the processor to get these IDs, we are telling the model: “Your target language is German, and your task is transcription”. But for a general purpose fine-tuning, we would simply give it None

By default, the pre-trained Whisper model has a list of “suppressed tokens” - specific symbols or characters it is instructed never to say. This often includes special formatting tokens or certain technical markers that were restricted during its initial training to keep its output clean.

Setting suppress_tokens = [] clears that list. In the context of fine-tuning, we want the model to have full access to its entire vocabulary. We are essentially telling the model: “Do not ignore any parts of your vocabulary; you have permission to use all tokens necessary to accurately represent the German training data”.

Train#

Training involves loading a German dataset (such as Mozilla Data Collective), preprocessing the audio to 16kHz, and using the Seq2SeqTrainer. This component manages the training loop, evaluation, and checkpoint saving.

  1. Load dataset we just prepared

    from datasets import load_from_disk
    dataset = load_from_disk(DATASET_PATH)
  2. Define training parameters

    from transformers import Seq2SeqTrainingArguments
    training_args = Seq2SeqTrainingArguments(
    output_dir="./whisper-german-asr",
    per_device_train_batch_size=16,
    gradient_accumulation_steps=1,
    learning_rate=1e-5,
    warmup_steps=500,
    max_steps=4000,
    fp16=True,
    predict_with_generate=True,
    generation_max_length=225,
    save_steps=1000,
    eval_steps=1000,
    report_to=["tensorboard"],
    )
  3. Define preprocessing function to prepare audio and text labels. Initialize the trainer

    from transformers import Seq2SeqTrainer, DataCollatorForSeq2Seq
    def prepare_dataset(batch):
    audio = batch["audio"]
    batch["input_features"] = processor(audio["array"], sampling_rate=audio["sampling_rate"]).input_features[0]
    batch["labels"] = processor.tokenizer(batch["sentence"]).input_ids
    return batch
    trainer = Seq2SeqTrainer(
    args=training_args,
    model=model,
    train_dataset=dataset.map(prepare_dataset),
    data_collator=DataCollatorForSeq2Seq(processor.tokenizer, model=model, padding=True),
    tokenizer=processor.feature_extractor,
    )
  4. Start the training process

    trainer.train()

Inference#

To deploy the model to a production-ready webservice that adapts to both CPU and GPU hardware ready for inferencing, we load the model we just trained.

We first define the model path and make things work on both CPU and GPU machine:

MODEL_PATH = "./whisper-german-production"
DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu"
BATCH_SIZE = 16 if DEVICE != "cpu" else 1

Then we load the Hugging Face Pipeline for ASR:

pipe = pipeline(
"automatic-speech-recognition",
model=MODEL_PATH,
tokenizer=MODEL_PATH,
device=DEVICE,
chunk_length_s=30,
batch_size=BATCH_SIZE
)
TIP

chunk_length_s=30 enables processing of long audio files by splitting them into 30s chunks (Whisper’s native context window)

To transcribe:

result = pipe(
audio_file_path,
return_timestamps=True,
generate_kwargs={"language": "german", "task": "transcribe"}
)
transcript_text = result.get("text", "").strip()
  • The pipeline handles 16kHz resampling and normalization automatically.
  • return_timestamps=True allows detailed output if needed later.
  • We force the language to German because this is a specialized model
  • The result is usually a dict {'text': "..."} or a list of chunks. The pipeline normalizes this to a simple dictionary access for ‘text’.
Transformer
https://blogs.openml.io/posts/transformer/
Author
OpenML Blogs
Published at
2025-09-19
License
CC BY-NC-SA 4.0