This is the second article in the series where we are going to teach a machine to find security flaws in code, all in .NET. In the first one, we saw what machine learning is: instead of writing rules, you show examples and the machine figures out the rules on its own.
Now comes a question that sounds silly, but holds up everything ahead of us: how does a machine read? Because at the end of the day, code is text. And before teaching a machine to read code, humanity had to teach machines to read, period. That took decades and went through five great eras, until it reached the transformers, the technology behind ChatGPT.
This article tells that journey. No formulas, no math, no rush. Just the idea of each era and the reason it was left behind.
Handwritten rules: the house of cards
The first attempt was the most obvious one: writing rules. Want a spam filter? Make a list: if the email contains "easy money" or "click here now", mark it as spam. Want a translator? Build a giant dictionary and some grammar rules.
Does it work? It kind of does, for a while. The problem is that language is slippery. The scammer notices the filter and swaps "prize" for "pr1ze", and your rule just died. Your aunt sends an email saying "I won some money in the raffle, click the link to see the photos!" and the filter throws the poor lady into spam. For every new rule, ten exceptions show up, and the system becomes a house of cards nobody can maintain anymore.
Bag of words: the era of counting words
The second era applied the machine learning from the previous article to text, in the most direct way possible: counting words. The technique even has a fun name, bag of words: take the text, throw all the words into a bag, count how many times each one appears, and hand that count to the machine so it can learn the patterns.
And look, counting words works surprisingly well for a lot of things. The filters that saved our inboxes in the 2000s were basically that: models that learned, on their own, that "inheritance", "prince" and "urgent" showing up together rarely end well.
But the bag of words has a birth defect: it throws away order. To it, "the client canceled the contract" and "the contract canceled the client" are the same thing, the same bag with the same words. "The movie is not good" and "the movie is good" almost identical, just one little word apart. The machine counted, but it didn't understand.
Embeddings: words become points on a map
The third era brought a clever idea: what if every word became a point on a map? Words with similar meanings sit close to each other: "king" close to "queen", "dog" close to "cat", and both far away from "spreadsheet". This map has the technical name of embeddings, but the image of the map is all you need to take from here.
And the most beautiful part: nobody drew this map. The machine built it by itself, just by observing millions of texts and noticing which words appear in similar contexts. The model that popularized this idea was called word2vec, and the map got so good you could do arithmetic with meaning: take the point for "king", walk the distance that separates "man" from "woman", and you land pretty close to... "queen". The machine never opened a dictionary. It figured that out by watching the world write.
For the first time, the machine had a notion of meaning, not just counting. But the order of the words was still unsolved.
RNN and LSTM: reading in order, word by word
The fourth era attacked order head-on with the recurrent neural networks, the RNNs, and their most famous evolution, the LSTM: networks that read the way we read, one word at a time, left to right, carrying in memory what came before. When the model reached "canceled", it remembered that the one who canceled was "the client". Order finally mattered.
But two problems showed up. The first: the memory of these networks was short. In a long text, by the time the reading reached the end, the beginning had already faded, like you at the last chapter of a book trying to remember that detail from the first one. The second: reading one word at a time is slow by nature. Each word depends on the previous one, so there is no way to split the reading among several "readers" working together. And without splitting the work, training large models took an eternity.
Transformer: everyone looks at everyone
In 2017, Google researchers published a study with one of the most confident titles in the history of science: "Attention Is All You Need". It introduced the transformer. And the title aged very well.
The central idea is the attention mechanism, and it can be explained in one sentence: instead of reading word by word, the transformer looks at the whole sentence at once, and each word decides which other words are worth paying attention to. In the sentence "the dog I adopted yesterday was hungry", when the model processes "was hungry", its attention points strongly to "dog" and weakly to "yesterday". Who was hungry? The dog. The model knows, because each word connected directly with the ones that matter to it, no matter the distance.
That solved both problems of the previous era at once. The short memory? Gone: the last word of the text sees the first one directly, without fading along the way. The slowness? Also gone: with no sequential reading, the work can be split among thousands of "readers" at the same time. And with all that speed, it became possible to feed the models absurd amounts of text, and they grew into what we see today.
From this architecture two families were born, and keep this distinction because it comes back in the series. The models of the GPT family are trained to continue a text, predicting the next word: they are the writers, great at generating content. The ones in the BERT family are trained to understand a text, reading everything at once, in both directions: they are the readers, great at interpreting and classifying. To write an essay, GPT. To read an essay and give it a grade, BERT.
And what does this have to do with code?
Everything. Because code is also a language: it has vocabulary, grammar, context, even style. And the transformer has nothing specific to Portuguese or English inside it, it learns any language you show it in sufficient quantity. They showed it encyclopedias, it learned English. They showed it millions of open source projects... it learned code.
In other words: there already are transformers that read code the way BERT reads a sentence, understanding the whole. And "reading and giving a verdict" is exactly what we will want from them.
But hold on, their time hasn't come yet. Before inviting these models into our project, we need to understand the problem they are going to solve: what exactly makes a piece of code dangerous? That is what the next article answers, from scratch, assuming nothing.
Conclusion
The whole journey in five lines:
- Handwritten rules: break at the first exception, impossible to maintain
- Bag of words: worked for spam, but threw away order and context
- Embeddings: the machine gained a notion of meaning
- RNN and LSTM: context arrived, but with short memory and slowness
- Transformer: everyone looks at everyone, long memory, speed, and the models exploded in size
In the end, the transformer's insight wasn't reading better than its predecessors, it was reading differently: stop imitating human reading and let each word see all the others at once. Sometimes the revolution isn't doing the same thing faster, it's changing the question.
Sources
- Vaswani et al., "Attention Is All You Need" (the transformer paper, 2017)
- Devlin et al., "BERT: Pre-training of Deep Bidirectional Transformers" (2018)
- Mikolov et al., "Efficient Estimation of Word Representations in Vector Space" (the word2vec paper, 2013)
- Jay Alammar, "The Illustrated Transformer" (the best visual explanation there is)
- Andrej Karpathy, "The Unreasonable Effectiveness of Recurrent Neural Networks"