<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luiz Fernando Vid</title>
    <description>The latest articles on DEV Community by Luiz Fernando Vid (@luizvid).</description>
    <link>https://dev.to/luizvid</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F449250%2Faef02811-6e63-4608-908b-3ce6cdff357f.jpeg</url>
      <title>DEV Community: Luiz Fernando Vid</title>
      <link>https://dev.to/luizvid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luizvid"/>
    <language>en</language>
    <item>
      <title>Article 1 — MiniGPT in Java: Understanding a Transformer in Detail</title>
      <dc:creator>Luiz Fernando Vid</dc:creator>
      <pubDate>Sun, 09 Aug 2026 22:00:00 +0000</pubDate>
      <link>https://dev.to/luizvid/article-1-minigpt-in-java-understanding-a-transformer-in-detail-4526</link>
      <guid>https://dev.to/luizvid/article-1-minigpt-in-java-understanding-a-transformer-in-detail-4526</guid>
      <description>&lt;p&gt;Over the past few weeks, I started a personal project that, at first glance, seems impractical: implementing a GPT from scratch, in Java, without using any AI framework. No TensorFlow, no PyTorch, no DJL. Just linear algebra, a few hundred lines of code, and the willingness to understand each piece that makes a language model work.&lt;/p&gt;

&lt;p&gt;The reason is straightforward: at the pace the AI ecosystem is evolving, I felt the need to deepen my understanding of the fundamentals, starting with the piece that sustains practically everything used today in applied GenAI, the Transformer. I use this layer of applications (RAG, copilot, integrations) daily, but at a certain point I realized I wanted to understand better what supports all of this underneath, not just consume it.&lt;/p&gt;

&lt;p&gt;That frustration became MiniGPT. The idea is simple to state but nothing simple to execute, and consists of manually building each component of a Transformer Decoder (tokenization, embeddings, self-attention, backpropagation, training, text generation) to come out the other side understanding better what I use every day in applied form.&lt;/p&gt;

&lt;p&gt;This series of articles documents that journey. It will be a series of texts, one for each phase of the roadmap, from the mathematical foundation to inference of the trained model generating text. It's not a series of ready-to-copy-and-paste tutorials; it's the record of decisions, the whys, and also the mistakes, because implementing backpropagation manually, for example, is the kind of thing that doesn't work on the first try, and I think the value is precisely in showing that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is, in fact, a GPT
&lt;/h2&gt;

&lt;p&gt;It's worth pausing on the name before diving into code. GPT stands for Generative Pre-trained Transformer, and each of those three words carries a specific architectural decision.&lt;/p&gt;

&lt;p&gt;The Transformer is the architecture described by Vaswani and other Google researchers in 2017 in the paper &lt;a href="https://goo.gl/dwSBxB" rel="noopener noreferrer"&gt;"Attention Is All You Need"&lt;/a&gt;. The original proposal solved machine translation with an encoder (which reads the entire input sentence) and a decoder (which generates the output sentence, token by token). GPT uses only half of that architecture—the decoder half—which is why it's called "decoder-only". It makes sense because a language model doesn't need to "read" a source sentence before translating; it just needs to predict which is the most likely next token, given everything that came before.&lt;/p&gt;

&lt;p&gt;It's this prediction mechanism (called autoregressive modeling) that sustains both training and text generation. During training, the model sees a text snippet and learns to predict the next token; during generation, it does literally the same thing, repeatedly, token by token, feeding its own output back as input for the next prediction. It's not magic, it's technology: it's the same operation, millions of times, in a loop.&lt;/p&gt;

&lt;p&gt;The diagram below summarizes the path a sentence takes inside the model, from text input to the choice of the next token, and is also, essentially, the map of the next phases in this series.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F62fisgd6xk01qixd57a7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F62fisgd6xk01qixd57a7.png" alt="Diagram of phases where each rectangle in that diagram becomes, over the course of the series, its own phase with tests and implementation: Tokenizer (Phase 2), Embeddings and Positional Embeddings (Phases 3 and 4), Self-Attention and Multi-Head Attention (Phases 5 and 6), Feed Forward (Phase 7), and so on until text generation (Phase 13)" width="794" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each rectangle in that diagram becomes, over the course of the series, its own phase with tests and implementation: Tokenizer (Phase 2), Embeddings and Positional Embeddings (Phases 3 and 4), Self-Attention and Multi-Head Attention (Phases 5 and 6), Feed Forward (Phase 7), and so on until text generation (Phase 13).&lt;/p&gt;

&lt;p&gt;A reference I'm using as a general compass is the book &lt;a href="https://sebastianraschka.com/llms-from-scratch/" rel="noopener noreferrer"&gt;"Build a Large Language Model (From Scratch)"&lt;/a&gt; by Sebastian Raschka. It's one of the few materials that treats building a GPT manually as its own pedagogical goal, rather than treating it as a disposable academic exercise. I'll cite it again throughout the series whenever a design decision warrants deeper theoretical context than fits in an article.&lt;/p&gt;

&lt;h2&gt;
  
  
  A scope decision before tokenizer
&lt;/h2&gt;

&lt;p&gt;Before diving into Phase 2 (Tokenizer), which is the subject of the next article, it's worth recording a decision I made early on that will appear implicitly throughout the series: not everything needs to be reinvented.&lt;/p&gt;

&lt;p&gt;Phase 1 of the project is building a small math library (vectors, matrices, dot product, matrix product, transposition, basic statistics). It's the foundation everything else is built on. I even considered implementing this from scratch too, but I decided to use an external lib (EJML, in this case) for that specific layer.&lt;/p&gt;

&lt;p&gt;The criterion I used was simple: if the operation is pure math (addition, matrix product, transposition), it can come from a mature and tested library. The moment an operation carries neural network semantics (a gradient, the forward or backward of a layer) is when it needs to be mine, written and understood line by line. Reinventing matrix product teaches me nothing about Transformers; reinventing self-attention does.&lt;/p&gt;

&lt;p&gt;From there was born the &lt;code&gt;Tensor&lt;/code&gt;, the class that sustains the entire project. In practice, it's a thin wrapper over EJML matrices, representing one data item per element of a batch. It knows how to add, multiply by a factor, do matrix product, transpose, calculate mean and variance per row. Basically the minimum mathematical vocabulary that the next phases will consume without stopping. I built this entire foundation in TDD, cycle by cycle, and it's a detail that will reappear often: each component of MiniGPT (from Tensor to the trained model) is born from a test that fails before any implementation line exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next
&lt;/h2&gt;

&lt;p&gt;The next article is about Tokenizer. It's the first step where the project stops being generic math and starts to actually become a language model: the moment text becomes a number, and where the first vocabulary decisions start to shape everything the model will be able to (or not) represent afterward.&lt;/p&gt;

&lt;p&gt;I'm publishing this series over the next few weeks, one article per phase.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gpt</category>
      <category>transformer</category>
      <category>java</category>
    </item>
    <item>
      <title>Article 2 — MiniGPT in Java, Phase 2: Tokenizer</title>
      <dc:creator>Luiz Fernando Vid</dc:creator>
      <pubDate>Sun, 09 Aug 2026 22:00:00 +0000</pubDate>
      <link>https://dev.to/luizvid/article-2-minigpt-in-java-phase-2-tokenizer-54mg</link>
      <guid>https://dev.to/luizvid/article-2-minigpt-in-java-phase-2-tokenizer-54mg</guid>
      <description>&lt;p&gt;After closing Phase 1 with the Tensor structure that will support the rest of the project, it's time to solve a problem that looks simpler on the surface, but carries a design decision worth pausing to think about before jumping into coding: how to turn text into a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;A neural network doesn't process text. It processes numbers, and more specifically, it processes linear algebra operations on vectors and matrices, which means that before anything gets near an embedding layer, an attention layer, or whatever else, the text we type needs to become a sequence of integers. That's the sole responsibility of the tokenizer: to be the bridge between the language we understand and the numerical representation the model can manipulate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Ways to Build That Bridge
&lt;/h2&gt;

&lt;p&gt;There are essentially three levels of granularity for tokenization, and each one solves the problem differently, with clear trade-offs.&lt;/p&gt;

&lt;p&gt;Using the phrase "the cat sleeps" as an example, you can see how each approach tackles the problem differently.&lt;/p&gt;

&lt;p&gt;The first is &lt;strong&gt;char-level&lt;/strong&gt;, where each character (including the space) becomes a token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text:   "the cat sleeps"
Tokens: ['t', 'h', 'e', ' ', 'c', 'a', 't', ' ', 's', 'l', 'e', 'e', 'p', 's']
IDs:    [1, 0, 5, 2, 8, 1, 0, 4, 1, 7, 6, 3, 9, 4]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vocabulary here is all the distinct characters appearing in the corpus (letters, space, punctuation), which for English stays in the range of 60 to 100 symbols, and the implementation is straightforward, but in return the sequences get long, since each word becomes multiple tokens.&lt;/p&gt;

&lt;p&gt;The second is &lt;strong&gt;word-level&lt;/strong&gt;, where each word is a single token (typically separating punctuation as well):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text:   "the cat sleeps"
Tokens: ['the', 'cat', 'sleeps']
IDs:    [12, 340, 891]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vocabulary becomes all the distinct words in the corpus, which quickly turns into tens of thousands of entries, and any new word the model hasn't seen during training (a proper name, for example) simply has no ID for it—the classic OOV (out of vocabulary) problem.&lt;/p&gt;

&lt;p&gt;The third, which real production models use (GPT-2 and up, for example), is &lt;strong&gt;subword&lt;/strong&gt; via BPE (Byte Pair Encoding). The logic is to start char-level and merge the most frequent pairs of characters until forming a vocabulary of fixed size (10,000 tokens, for example), which makes common words into a single token and rare or compound words break into smaller pieces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text:   "the cat sleeping peacefully"
Tokens: ['the', 'Ġcat', 'Ġsleep', 'ing', 'Ġpeace', 'ful', 'ly']
IDs:    [12, 340, 55, 891, 203, 77, 88]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(the &lt;code&gt;Ġ&lt;/code&gt; is the GPT-2 convention to mark word start with space before). Notice that "sleeping" became two tokens ("sleep" + "ing") and "peacefully" also ("peace" + "ful" + "ly"), the algorithm learned that these fragments are frequent enough to deserve their own token, without needing an entry for every complete word in the language. It's the middle ground that solves both the long sequence problem of char-level and the OOV problem of word-level, just at the cost of a much more complex implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Char-Level, Here and Now
&lt;/h2&gt;

&lt;p&gt;For this phase of the project, I opted for char-level, not because it's the "right" approach (it's not what production uses), but because the goal here is to understand the end-to-end mechanism without the complexity of the BPE merge algorithm getting in the way of learning. Char-level closes the encode-decode cycle quickly, with an implementation that fits in a few lines, and that's what matters in a phase that exists to consolidate concepts, not to compete with production tokenizers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separating Responsibilities
&lt;/h2&gt;

&lt;p&gt;The design split into two classes: &lt;code&gt;Vocabulary&lt;/code&gt;, which is the mapping between character and ID (and vice versa), and &lt;code&gt;Tokenizer&lt;/code&gt;, which uses that vocabulary to convert text to IDs and back. This separation exists because it leaves the door open to swap tokenization strategies later (char-level for BPE, for example) without needing to touch whoever consumes the &lt;code&gt;Tokenizer&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vocabulary Consistency
&lt;/h2&gt;

&lt;p&gt;The vocabulary cannot be rebuilt with every execution. If the &lt;code&gt;Vocabulary&lt;/code&gt; is regenerated every time the program runs, the IDs assigned to each character might come out differently from one session to the next, and in that case, the model trained with one vocabulary becomes invalid if used with another, because each learned weight depends directly on the ID it represents.&lt;/p&gt;

&lt;p&gt;The vocabulary is built once, during dataset preparation, and from that point on is persisted and only loaded (never rebuilt) in all subsequent training or inference. The &lt;code&gt;encode&lt;/code&gt; and &lt;code&gt;decode&lt;/code&gt;, those are used all the time, always against the same frozen vocabulary.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Stays for Next Phase
&lt;/h2&gt;

&lt;p&gt;With the tokenizer closed, the text sequence already becomes a sequence of integers, but those integers still don't mean anything to the model, they're just indices. That's what the next phase solves: transforming each ID into a dense vector, capable of carrying some notion of meaning in vector space. That's the gateway to embeddings.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gpt</category>
      <category>transformer</category>
      <category>java</category>
    </item>
  </channel>
</rss>
