<?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: Pradeep Kumar</title>
    <description>The latest articles on DEV Community by Pradeep Kumar (@pradeep_kumar_bc4e7e9f7ec).</description>
    <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec</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%2F4020864%2F3dda0ab9-b27c-4cee-a1ee-d34067f9bab9.png</url>
      <title>DEV Community: Pradeep Kumar</title>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pradeep_kumar_bc4e7e9f7ec"/>
    <language>en</language>
    <item>
      <title>Apple Is Tightening Full Disk Access on macOS, and AI Agents Are the Reason</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:28:28 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/apple-is-tightening-full-disk-access-on-macos-and-ai-agents-are-the-reason-3k5d</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/apple-is-tightening-full-disk-access-on-macos-and-ai-agents-are-the-reason-3k5d</guid>
      <description>&lt;p&gt;Apple is going to make it harder to give an app Full Disk Access on the Mac. The reason is simple: AI agents have made that level of access riskier to hand out.&lt;/p&gt;

&lt;p&gt;The announcement is short. Apple gave no dates and no details on how the new controls will work. Even so, it says a lot about where desktop AI is heading.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Full Disk Access does
&lt;/h2&gt;

&lt;p&gt;Full Disk Access is a switch in System Settings, under Privacy &amp;amp; Security. Turn it on for an app, and that app can reach data macOS normally protects. That includes files, mail, messages and browsing history.&lt;/p&gt;

&lt;p&gt;Apple says the permission largely sidesteps the privacy controls that apps usually have to respect. It exists so backup apps can do their job, since a backup tool has to copy everything.&lt;/p&gt;

&lt;p&gt;That design made sense for backups. It makes less sense for software that can read, decide and act on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Apple said
&lt;/h2&gt;

&lt;p&gt;Apple posted the note on its developer news page on October 2. The main points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Some developers use Full Disk Access in ways that put users at risk. It can expose everything on a system without the user fully understanding that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For communication apps, the exposure reaches past the user. It can also compromise the privacy of the people they talk to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apple will add controls so that anyone who really wants to grant this access can only do it through "very explicit user action."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As AI agents become more capable and autonomous, Apple expects the risks of this access to grow a great deal.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apple's note doesn't name any app or company.&lt;/p&gt;

&lt;h2&gt;
  
  
  What set it off
&lt;/h2&gt;

&lt;p&gt;The timing lines up with two recent stories, and TechCrunch tied Apple's move to both.&lt;/p&gt;

&lt;p&gt;The first is about Muse, Meta's AI agent, which has a Mac app. Inc. columnist Jason Aten reported that Muse knew the content of his private messages, even though he says he never gave it permission. He said Full Disk Access was off at the time. When he asked Muse how it knew, it told him it was syncing his device notifications.&lt;/p&gt;

&lt;p&gt;Meta disputes this. Meta VP of Communications Andy Stone said the Messages integration is opt-in and needs both Full Disk Access and a Messages connector.&lt;/p&gt;

&lt;p&gt;Meta Superintelligence Labs executive David Singleton added that the setup takes three separate permission steps. He said macOS protections can't be bypassed even if the app had a bug, and that Muse's own explanation of what happened was wrong.&lt;/p&gt;

&lt;p&gt;Reports so far don't show the disagreement being settled.&lt;/p&gt;

&lt;p&gt;The second story is a Wired report on a flaw in ChatGPT's Mac app that could have let hackers reach sensitive data.&lt;/p&gt;

&lt;p&gt;Both stories point at the same worry: desktop AI apps sit close to a lot of personal data. Apple hasn't said either one caused its decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents change the picture
&lt;/h2&gt;

&lt;p&gt;A backup app copies files and does little else. An agent reads, decides and acts. It can open a file, summarize it, send a message or run a command.&lt;/p&gt;

&lt;p&gt;Add broad access and the risk grows. An agent can also be steered by what it reads. Hidden instructions in a document or web page can push it to do things the user never asked for. Security researchers call this prompt injection.&lt;/p&gt;

&lt;p&gt;There's a simpler issue too. macOS grants this permission per app, not per task. Once it's on, the app can reach everything the permission covers, whether or not the current job needs it.&lt;/p&gt;

&lt;p&gt;And agents often run inside other apps. An agent launched from Terminal inherits whatever permissions Terminal has.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Apple hasn't said
&lt;/h2&gt;

&lt;p&gt;Apple hasn't said what the new controls look like, which version of macOS will get them, or when. TechCrunch reports that Apple didn't respond to its questions about the change.&lt;/p&gt;

&lt;p&gt;A few things are worth watching once details appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Whether apps that already have Full Disk Access keep it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whether backup tools get a separate path from AI apps&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How much friction the new approval step adds&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are open questions, not reported plans.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do now
&lt;/h2&gt;

&lt;p&gt;You don't need to wait for Apple.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open System Settings, then Privacy &amp;amp; Security, then Full Disk Access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Switch off anything you don't recognize or no longer use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For AI apps, grant specific folders instead. You'll find those options under Files &amp;amp; Folders in the same section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check Terminal and code editors too. An agent running inside them inherits their access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep macOS and your apps up to date.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Full Disk Access was built with backup apps in mind. Apple now says AI agents have changed the risk.&lt;/p&gt;

&lt;p&gt;The details are still to come, but the direction is clear. Handing an app everything on your Mac should take a deliberate choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Apple Developer: &lt;a href="https://developer.apple.com/news/?id=p6zjojqw" rel="noopener noreferrer"&gt;Updates to Full Disk Access in macOS&lt;/a&gt; (October 2, 2026)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TechCrunch: &lt;a href="https://techcrunch.com/2026/10/02/apple-says-its-tightening-macos-full-disk-access-controls-due-to-new-risks-from-ai-agents/" rel="noopener noreferrer"&gt;Apple says it's tightening macOS 'Full Disk Access' controls due to new risks from AI agents&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TechCrunch: &lt;a href="https://techcrunch.com/2026/09/30/meta-disputes-claim-that-muse-read-a-users-private-messages-without-permission/" rel="noopener noreferrer"&gt;Meta disputes claim that Muse read a user's private messages without permission&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inc.: &lt;a href="https://www.inc.com/jason-aten/metas-new-muse-ai-agent-read-my-private-messages-i-never-asked-it-to/91408202" rel="noopener noreferrer"&gt;Jason Aten's report on Muse&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wired: &lt;a href="https://www.wired.com/story/a-flaw-in-chatgpts-mac-app-could-have-let-hackers-grab-sensitive-data/" rel="noopener noreferrer"&gt;A Flaw in ChatGPT's Mac App Could Have Let Hackers Grab Sensitive Data&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/apple-is-tightening-full-disk-access-on-macos-and-ai-agents-are-the-reason-h4j2z?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aiagents</category>
      <category>apple</category>
      <category>macos</category>
      <category>privacy</category>
    </item>
    <item>
      <title>1,313 Kernel CVEs in One Debian Update: Alarm Bell or Accounting Quirk?</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sun, 04 Oct 2026 11:37:36 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/1313-kernel-cves-in-one-debian-update-alarm-bell-or-accounting-quirk-2chc</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/1313-kernel-cves-in-one-debian-update-alarm-bell-or-accounting-quirk-2chc</guid>
      <description>&lt;p&gt;Debian's security team published &lt;a href="https://lwn.net/Articles/1097401/" rel="noopener noreferrer"&gt;DSA-6528-1&lt;/a&gt; with a familiar opening line: several vulnerabilities have been discovered in the Linux kernel that may lead to privilege escalation, denial of service, or information leaks. What follows is unusual. The CVE list fills most of the advisory page. I counted it: &lt;strong&gt;1,313 unique CVE IDs&lt;/strong&gt; in a single update to the Linux 6.12 LTS kernel that powers Debian 13 "Trixie". 9to5Linux called it &lt;a href="https://9to5linux.com/latest-debian-13-trixie-kernel-security-update-patches-more-than-1300-cves" rel="noopener noreferrer"&gt;probably the biggest kernel security release ever&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The number sounds frightening, but it says less about the kernel's health than you might expect. Here is what actually happened, why the count is so large, and what you should do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Debian shipped
&lt;/h2&gt;

&lt;p&gt;DSA-6528-1 moves Debian 13 to kernel package version 6.12.111-1. Debian's &lt;a href="https://security-tracker.debian.org/tracker/DSA-6528-1" rel="noopener noreferrer"&gt;security tracker&lt;/a&gt; shows trixie's previous kernel, 6.12.107-1, as vulnerable and 6.12.111-1 in the security repository as the fix. The advisory covers only trixie, the current stable release. Debian 12 "bookworm" kernels are handled in separate advisories.&lt;/p&gt;

&lt;p&gt;The opening sentence isn't a clue to severity. It's the standard summary for Debian kernel advisories; the &lt;a href="https://linuxsecurity.com/advisories/debian/debian-dsa-6274-1-linux" rel="noopener noreferrer"&gt;May update to 6.12.88-1 (DSA-6274-1)&lt;/a&gt;, for example, is summarized with the same three impact categories.&lt;/p&gt;

&lt;p&gt;The CVE IDs themselves are telling. Only 18 of the 1,313 carry 2024 or 2025 identifiers (3 and 15). The other 1,295 are 2026 IDs. The year in a CVE ID reflects when the number was issued, not when the bug was written, so this is mostly a batch of recently issued numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the number is so big
&lt;/h2&gt;

&lt;p&gt;Three things combine.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The kernel assigns CVEs very generously, by design
&lt;/h3&gt;

&lt;p&gt;Since early 2024 the Linux kernel project has been its own CVE Numbering Authority, able to issue identifiers itself. Its &lt;a href="https://docs.kernel.org/process/cve.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; explains the approach. Changes that are potentially security issues are identified during the normal stable release process and get CVE numbers automatically. The reasoning is that almost any kernel bug might be exploitable, but exploitability often isn't evident when the bug is fixed, so the team is "overly cautious" and assigns a number to any bugfix it identifies. Two rules limit the scope: CVEs are assigned only after a fix lands in a stable tree, and none are assigned for kernel versions the stable team no longer supports.&lt;/p&gt;

&lt;p&gt;The effect on volume has been large. Greg Kroah-Hartman &lt;a href="https://lwn.net/Articles/1049963/" rel="noopener noreferrer"&gt;has written&lt;/a&gt; that the kernel went from nothing to third among CVE issuers in 2024 and first in 2025, though in a &lt;a href="https://openssf.org/podcast/2026/06/30/whats-in-the-soss-podcast-64-s3e16-the-heartbeat-of-the-kernel-why-upstream-is-the-ultimate-security-strategy-with-greg-kroah-hartman/" rel="noopener noreferrer"&gt;June podcast&lt;/a&gt; he described it as trading places with GitHub's pooled CNA, so the exact rank depends on who is counted. By NVD-based counts, &lt;a href="https://linuxcvetracker.com/cve-statistics/2026/" rel="noopener noreferrer"&gt;2025 had 5,681 kernel CVEs and 2026 had reached 7,178 by the end of September&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Debian batches its kernel fixes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://9to5linux.com/latest-debian-13-trixie-kernel-security-update-patches-more-than-1300-cves" rel="noopener noreferrer"&gt;9to5Linux&lt;/a&gt; notes that Debian Stable doesn't ship every upstream stable point release as its own security advisory, so fixes pile up and arrive together. A big number in one advisory partly reflects how much accumulated between advisories.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI-assisted review is finding more bugs
&lt;/h3&gt;

&lt;p&gt;Linux 7.2-rc7, released August 9, carried &lt;a href="https://betanews.com/article/linux-7-2-rc7-ai-review-new-normal/" rel="noopener noreferrer"&gt;more than 400 fixes from over 230 contributors&lt;/a&gt;. Linus Torvalds called this the new normal, with many of the fixes coming from review by AI tools. The tools review and report; humans still write and submit the patches. One fix in that release closed a race condition in ptdump, the kernel's page-table display interface, that could cause a use-after-free and traced back to changes made in 2018. It's worth getting the credit right: Google's syzbot fuzzer flagged it in June, and a developer used an AI model to help trace the root cause. AI is one part of a wider tooling surge.&lt;/p&gt;

&lt;p&gt;The surge has a cost. In May, Torvalds said duplicate AI-generated bug reports had left the kernel's private security list almost unmanageable. In July, Kroah-Hartman &lt;a href="https://seclists.org/oss-sec/2026/q3/210" rel="noopener noreferrer"&gt;wrote on oss-security&lt;/a&gt; that the number of LLM-found issues is only on the rise and that digging out will take at least 18 months.&lt;/p&gt;

&lt;p&gt;More bugs found means more fixes, and more fixes means more CVEs. The pipeline is working as designed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the kernel getting less secure?
&lt;/h2&gt;

&lt;p&gt;Probably not, though the answer depends on what you mean by "secure." A few data points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;a href="https://linuxcvetracker.com/cve-statistics/2026/" rel="noopener noreferrer"&gt;tracker's NVD-based data&lt;/a&gt;, with entries through September 29, shows 7,178 kernel CVEs published in 2026, including 1,651 in August and 2,118 in September. Only 3 are in CISA's Known Exploited Vulnerabilities (KEV) catalog. None of those three is in DSA-6528-1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Counts are lumpy because the queue is processed in bulk. When 432 kernel CVEs appeared over July 19 and 20, Kroah-Hartman explained that this was &lt;a href="https://seclists.org/oss-sec/2026/q3/210" rel="noopener noreferrer"&gt;not a wave of new vulnerabilities&lt;/a&gt;. It was a review backlog, pending for weeks because of six straight weeks of conferences and vacations, that finally got published. The burst led Akamai's Jan Schaumann to &lt;a href="https://www.scworld.com/brief/linux-kernel-security-faces-challenge-with-surge-in-cves" rel="noopener noreferrer"&gt;argue&lt;/a&gt; that prioritizing individual kernel changes is no longer feasible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Severity data is thin and noisy. The tracker lists 2,312 of the 7,178 as still awaiting NVD scoring. That's partly because &lt;a href="https://www.nist.gov/news-events/news/2026/04/nist-updates-nvd-operations-address-record-cve-growth" rel="noopener noreferrer"&gt;NIST announced on April 15&lt;/a&gt; that it will enrich only CVEs that are in KEV, used by the federal government, or covered by critical-software rules. Among the scored ones, 3,278 of 4,866 (my arithmetic) are rated High or Critical, yet only 3 are known to be exploited. Treat the scores as a weak signal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;9to5Linux's reading is that most of the 1,313 are low-severity, highly conditional, or in subsystems irrelevant to any given machine, so this isn't 1,313 independently critical issues.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The kernel's own documents point the same way. Its &lt;a href="https://docs.kernel.org/process/threat-model.html" rel="noopener noreferrer"&gt;threat model&lt;/a&gt; lists whole classes of bugs it does not count as vulnerabilities, such as problems triggered only by a crafted filesystem image, a fake USB device, physical access, or actions by users who already hold the needed privileges. CVE assignment errs toward including these cases. A CVE number is a flag on a fix, not a verdict on danger.&lt;/p&gt;

&lt;p&gt;The policy has critics. When it was announced in 2024, kernel developer Josh Poimboeuf &lt;a href="https://lkml.iu.edu/hypermail/linux/kernel/2402.2/01187.html" rel="noopener noreferrer"&gt;argued&lt;/a&gt; that a bug isn't automatically a vulnerability, and that issuing CVEs without any analysis of how a bug could be exploited makes them close to useless. That debate has not gone away. It has only become more pressing as the count has grown.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real kernel emergency looks like
&lt;/h2&gt;

&lt;p&gt;For contrast, consider Copy Fail (CVE-2026-31431). It isn't in this advisory's list, and I include it only to show the other end of the scale. Researchers at Theori and Xint disclosed it in late April. It is a local privilege escalation bug in the kernel's authentication crypto template that lets an unprivileged user get root with a &lt;a href="https://thehackernews.com/2026/05/cisa-adds-actively-exploited-linux-root.html" rel="noopener noreferrer"&gt;732-byte Python exploit&lt;/a&gt;. Three individually harmless changes made in 2011, 2015 and 2017 combined to create it. CISA added it to the KEV catalog on May 1. Microsoft noted that it is not remotely exploitable on its own but becomes highly impactful when chained with an initial foothold such as SSH access, a malicious CI job or a compromised container. Kaspersky's analysis flagged a serious risk for containerized environments, since Docker, LXC and Kubernetes grant containers access to the affected AF_ALG subsystem when the algif_aead module is loaded.&lt;/p&gt;

&lt;p&gt;The other two 2026 kernel entries in KEV, per the tracker, are a netfilter bridge fix (CVE-2026-53266) and an IPv6 fix (CVE-2026-53362). Neither is in this batch either.&lt;/p&gt;

&lt;p&gt;Copy Fail had a name, a public exploit, a clear affected population and a government deadline. Most of the 1,313 have none of those things. A CVE number tells you very little on its own. What tells you something is whether the affected code is reachable on your system, whether an exploit exists, and whether your exposure model includes untrusted local users, containers or shared hosts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you run Debian 13&lt;/strong&gt;, update and reboot. A kernel update doesn't take effect until the new kernel is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;                      &lt;span class="c"&gt;# shows your running kernel's Debian package version&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt full-upgrade
&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;                      &lt;span class="c"&gt;# confirm you're now on 6.12.111-1 or later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If you run fleets or compliance scanners&lt;/strong&gt;, expect ticket floods and plan for them. A scanner that opens one ticket per CVE will generate over a thousand from this single update. Kroah-Hartman's own advice in that oss-security thread is to run the latest supported kernel everywhere and update regularly. For those who can't track upstream themselves, he named Debian and Yocto as good fallbacks because of their security practices. He also described a practical filter: intersect the files each CVE touches with the files you actually build into your kernel, which he says normally leaves about 10% of the total on a typical system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't cherry-pick "important" patches.&lt;/strong&gt; The kernel documentation says it is best to take all released kernel changes, because they are tested together, and that the fix for a problem is often spread across several commits. It also says to assume that some changes without a CVE might be relevant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For prioritizing what remains&lt;/strong&gt;, don't lean on NVD scores alone, since many kernel CVEs have none. Weigh KEV status, public exploit availability and whether your exposure model (shared hosts, containers, untrusted local users) matches the bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For general hardening&lt;/strong&gt;, the standard advice applies. Unload or blacklist modules you don't use, and restrict unprivileged user namespaces and exotic socket families such as AF_ALG where your workload allows. Copy Fail is a reminder that a rarely used interface can matter a great deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;DSA-6528-1 matters, and you should install it. But 1,313 is a measure of how the kernel counts fixes, how Debian batches them, and how many more bugs are being found, more than a measure of danger. The harder problem is human. As &lt;a href="https://linuxnews.de/1313-kernel-cves-wenn-die-sicherheitsluecke-zum-normalfall-wird/" rel="noopener noreferrer"&gt;LinuxNews.de&lt;/a&gt; puts it, the bottleneck ahead is less about issuing CVE numbers than about the limited time maintainers have for review, stable backports and regression testing. Defenders face the mirror image: no team can read a thousand CVEs a month, so the winners will be the ones who automate "does this apply to my system?"&lt;/p&gt;

&lt;p&gt;If AI-assisted review keeps improving, four-digit CVE batches may become routine. The skill worth building now is reading them well.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://lwn.net/Articles/1097401/" rel="noopener noreferrer"&gt;Debian Security Advisory DSA-6528-1 (via LWN)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://security-tracker.debian.org/tracker/DSA-6528-1" rel="noopener noreferrer"&gt;Debian Security Tracker: DSA-6528-1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://9to5linux.com/latest-debian-13-trixie-kernel-security-update-patches-more-than-1300-cves" rel="noopener noreferrer"&gt;9to5Linux coverage&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://linuxnews.de/1313-kernel-cves-wenn-die-sicherheitsluecke-zum-normalfall-wird/" rel="noopener noreferrer"&gt;LinuxNews.de analysis (German)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.kernel.org/process/cve.html" rel="noopener noreferrer"&gt;Kernel CVE process documentation&lt;/a&gt; and &lt;a href="https://docs.kernel.org/process/threat-model.html" rel="noopener noreferrer"&gt;kernel threat model&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://seclists.org/oss-sec/2026/q3/210" rel="noopener noreferrer"&gt;Kroah-Hartman's reply on oss-security, July 22&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://lwn.net/Articles/1049963/" rel="noopener noreferrer"&gt;LWN: Kroah-Hartman on kernel CVEs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://betanews.com/article/linux-7-2-rc7-ai-review-new-normal/" rel="noopener noreferrer"&gt;BetaNews on Linux 7.2-rc7 and AI review&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.nist.gov/news-events/news/2026/04/nist-updates-nvd-operations-address-record-cve-growth" rel="noopener noreferrer"&gt;NIST: NVD operations update, April 15, 2026&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://linuxcvetracker.com/cve-statistics/2026/" rel="noopener noreferrer"&gt;LinuxCVETracker 2026 statistics&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://thehackernews.com/2026/05/cisa-adds-actively-exploited-linux-root.html" rel="noopener noreferrer"&gt;The Hacker News on Copy Fail and CISA KEV&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Method note: the 1,313 figure comes from counting the CVE IDs in the advisory text as published by LWN (329 lines, 1,313 unique IDs, no duplicates). Statistics from the tracker are NVD-based, come from a third-party aggregator and change daily, so treat them as approximate.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/1-313-kernel-cves-in-one-debian-update-alarm-bell-or-accounting-quirk-0rf9j?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cve</category>
      <category>debian</category>
      <category>kernel</category>
      <category>linux</category>
    </item>
    <item>
      <title>Testing if working</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sat, 03 Oct 2026 17:48:14 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/testing-if-working-5e2c</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/testing-if-working-5e2c</guid>
      <description>&lt;p&gt;Jsdkkdkdkdkdkkdkdkdkdkdkdjd&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/testing-if-working-r1dwq?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build a Small LLM From Scratch: A Tested GPT in PyTorch</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sat, 03 Oct 2026 05:06:10 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/build-a-small-llm-from-scratch-a-tested-gpt-in-pytorch-2f18</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/build-a-small-llm-from-scratch-a-tested-gpt-in-pytorch-2f18</guid>
      <description>&lt;p&gt;You can train a real language model on a laptop CPU during a coffee break. It won't be smart. It will have every part a big one has.&lt;/p&gt;

&lt;p&gt;This post builds one. It's a decoder-only transformer in PyTorch, the same family as GPT-2, with 813,440 parameters. It trains on Tiny Shakespeare and ends at a validation loss of 1.78.&lt;/p&gt;

&lt;p&gt;All the code ran before it went into this post. The full project is in &lt;a href="https://github.com/pradeep200892/tinygpt" rel="noopener noreferrer"&gt;tinygpt.zip&lt;/a&gt;: model, training script, sampler, baselines, 25 tests, and the trained checkpoint (&lt;code&gt;out/best.pt&lt;/code&gt;). Every number below comes from one run on a single CPU core, with Python 3.12.3 and PyTorch 2.14.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a language model actually does
&lt;/h2&gt;

&lt;p&gt;A language model predicts the next token. That's the whole job.&lt;/p&gt;

&lt;p&gt;Feed it &lt;code&gt;To be or not to b&lt;/code&gt; and it returns a probability for every possible next character. Training pushes those probabilities toward what the text really says. The loss is cross-entropy: the negative log of the probability the model gave the correct character.&lt;/p&gt;

&lt;p&gt;Loss also gives you a floor to compare against. A model that guesses uniformly across our 65 characters scores ln(65) = 4.174. An untrained network should land right there, and ours does.&lt;/p&gt;

&lt;p&gt;Chat models work the same way, with subword tokens, billions of parameters, and more training stages on top. The core loop doesn't change.&lt;/p&gt;

&lt;p&gt;We'll use characters instead of subword tokens. The vocabulary is 65 symbols, so there's no tokenizer to train.&lt;/p&gt;

&lt;p&gt;The price is that the model spends capacity learning to spell. GPT-2 used byte pair encoding with a 50,257-token vocabulary instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpV0MFu2zAMBuBX4XjaUPnQrdshhwF1UjcFuh6WbBerB8ZmYiKyaEha26zIuw9WAWE9ih9_ieIrdtozLnDv9LkbKCTYrqwHALhuLS4HCtQlDiB9NBAHmhg-1ga2nyw-QlV9h7q1uNUje-Bxx30v_gAXMGmUJPpf0eLj2711ji1bi7XT7giXRZZZVkU-F1lluSnypchNlqbIVZEmy21rsRFPDu7pxOFBw1g6bnPHurV4L54pwMDUG0jCPSSFNDCk9z8r0XWO3s1RPUh6vxsD377O-0GDI4eRpMfFK6aBx3nTPe_pj0to3iq_KQjtHMe5Z68-NTSKO-ECK5omx1U8xcSjgdqJP_6gbpPPjfpkwOKGD8rw686igZ-606QG1uyeOElHBq6DkDMQyccqcpA9mvzIRv7Os1xeTS94PhvcHZbqNOACPzwPkhjP_wCu96XX%3Ftype%3Dpng" 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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpV0MFu2zAMBuBX4XjaUPnQrdshhwF1UjcFuh6WbBerB8ZmYiKyaEha26zIuw9WAWE9ih9_ieIrdtozLnDv9LkbKCTYrqwHALhuLS4HCtQlDiB9NBAHmhg-1ga2nyw-QlV9h7q1uNUje-Bxx30v_gAXMGmUJPpf0eLj2711ji1bi7XT7giXRZZZVkU-F1lluSnypchNlqbIVZEmy21rsRFPDu7pxOFBw1g6bnPHurV4L54pwMDUG0jCPSSFNDCk9z8r0XWO3s1RPUh6vxsD377O-0GDI4eRpMfFK6aBx3nTPe_pj0to3iq_KQjtHMe5Z68-NTSKO-ECK5omx1U8xcSjgdqJP_6gbpPPjfpkwOKGD8rw686igZ-606QG1uyeOElHBq6DkDMQyccqcpA9mvzIRv7Os1xeTS94PhvcHZbqNOACPzwPkhjP_wCu96XX%3Ftype%3Dpng" alt="Mermaid Diagram" width="276" height="917"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each block has two sublayers. Both are wrapped in a residual connection, and both normalize their input first.&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%2Fmermaid.ink%2Fimg%2Fpako%3AeNp1kMFqwzAMhl9F02llziFhg5LDYCuUDdoxWrZL3IOSOK2pYwdbWduVvvtI0uUw2Mmy_g99ts5YuFJhipVxh2JHnmGxkhYA4JhJPErcQBQ9grFxJnFBJ-XfnK8lbgbI2LjPiTmTOKM2kIGgTBURs7KsnR1ZYh7YsoxvbyXeSZxMrq4xuKJlGV-9yT_epM9r02QSl4v3FOJkCuzgIU66I06mI1yb5nd-8kc8irpsbA2zXdv9KTeu2Hd107LEDQqsla9Jl5iekXeq7rZXqopawyiGzid5TblRoWMqZ3lOtTYnTDGipjEqCqfAqhbwbLTdL6lY9_e5syxA4lptnYKPV4kCVi537AS8KPOlWBck4MlrMgIC2RAF5XWFopes9Xf3lvi-OeLlIjDfzpxxHlO8Oew0K7z8AOCul2o%3Ftype%3Dpng" 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%2Fmermaid.ink%2Fimg%2Fpako%3AeNp1kMFqwzAMhl9F02llziFhg5LDYCuUDdoxWrZL3IOSOK2pYwdbWduVvvtI0uUw2Mmy_g99ts5YuFJhipVxh2JHnmGxkhYA4JhJPErcQBQ9grFxJnFBJ-XfnK8lbgbI2LjPiTmTOKM2kIGgTBURs7KsnR1ZYh7YsoxvbyXeSZxMrq4xuKJlGV-9yT_epM9r02QSl4v3FOJkCuzgIU66I06mI1yb5nd-8kc8irpsbA2zXdv9KTeu2Hd107LEDQqsla9Jl5iekXeq7rZXqopawyiGzid5TblRoWMqZ3lOtTYnTDGipjEqCqfAqhbwbLTdL6lY9_e5syxA4lptnYKPV4kCVi537AS8KPOlWBck4MlrMgIC2RAF5XWFopes9Xf3lvi-OeLlIjDfzpxxHlO8Oew0K7z8AOCul2o%3Ftype%3Dpng" alt="Mermaid Diagram" width="1267" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Moving LayerNorm to the input of each sublayer is one of the changes the GPT-2 paper made to the original transformer. It's why this design is called pre-LN. The original Transformer paper, &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;Attention Is All You Need&lt;/a&gt;, applied it after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;torch numpy pytest
unzip tinygpt.zip
&lt;span class="nb"&gt;cd &lt;/span&gt;tinygpt
python get_data.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get_data.py&lt;/code&gt; downloads Tiny Shakespeare from Andrej Karpathy's &lt;a href="https://github.com/karpathy/char-rnn" rel="noopener noreferrer"&gt;char-rnn repo&lt;/a&gt;. The file is 1,115,394 bytes.&lt;/p&gt;

&lt;p&gt;On macOS with a Python from python.org, that download can fail with a &lt;code&gt;CERTIFICATE_VERIFY_FAILED&lt;/code&gt; error. The script catches this and falls back to &lt;code&gt;curl&lt;/code&gt;. The FAQ at the end explains the real fix.&lt;/p&gt;

&lt;p&gt;Files in the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;data.py&lt;/code&gt;: character tokenizer, train/validation split, batching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;model.py&lt;/code&gt;: the GPT itself&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;train.py&lt;/code&gt;: training loop, learning-rate schedule, evaluation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;sample.py&lt;/code&gt;: text generation from a checkpoint&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;checkpoint.py&lt;/code&gt;: save and load&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;baselines.py&lt;/code&gt;: unigram and bigram losses for comparison&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;tests/test_tinygpt.py&lt;/code&gt;: 25 tests&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Data
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;data.py&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Character tokenizer and batching for tinygpt.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CharTokenizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Maps every distinct character in the corpus to an integer id.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stoi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;from_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Id that starts generation: newline if the corpus has one, else id 0.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stoi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stoi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_fraction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Read a text file and return (tokenizer, train_ids, val_ids).

    The split is contiguous: the last `val_fraction` of the file is
    validation data. Shuffling characters would leak train text into val.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CharTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;n_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;val_fraction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n_val&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n_val&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sample `batch_size` random windows.

    x is a window of block_size tokens. y is the same window shifted one
    position to the right, so y[t] is the token that follows x[t].
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;need at least &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tokens, got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tokenizer sorts the distinct characters in the file and numbers them. Sorting makes the mapping deterministic. &lt;code&gt;start_id&lt;/code&gt; returns the newline's id if the corpus has one, and generation starts from it when you give no prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_corpus&lt;/span&gt;

&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/input.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;First&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;First&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;65 1003855 111539
"\n !$&amp;amp;',-.3:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
[18, 47, 56, 57, 58]
First
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vocabulary holds a newline, a space, a handful of punctuation marks, the digit &lt;code&gt;3&lt;/code&gt;, and both alphabets. That's 65 symbols. The last 10% of the text becomes validation data.&lt;/p&gt;

&lt;p&gt;The split is contiguous on purpose. A random split would scatter overlapping windows of the same passages across both sets, and validation loss would stop measuring anything.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;get_batch&lt;/code&gt; samples random windows. The target &lt;code&gt;y&lt;/code&gt; is the input &lt;code&gt;x&lt;/code&gt; shifted one position right. It refuses data shorter than &lt;code&gt;block_size + 2&lt;/code&gt; tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_batch&lt;/span&gt;

&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/input.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1337&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Let's he"
"et's hea"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the two strings. From &lt;code&gt;L&lt;/code&gt; the target is &lt;code&gt;e&lt;/code&gt;, from &lt;code&gt;Le&lt;/code&gt; it's &lt;code&gt;t&lt;/code&gt;, from &lt;code&gt;Let&lt;/code&gt; it's &lt;code&gt;'&lt;/code&gt;. One window of 8 characters gives you 8 training examples at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: The model
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;model.py, imports and config&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A small GPT: decoder-only transformer with pre-LayerNorm blocks.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asdict&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.nn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch.nn.functional&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GPTConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;
    &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;   &lt;span class="c1"&gt;# maximum context length
&lt;/span&gt;    &lt;span class="n"&gt;n_layer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;n_head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;
    &lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;asdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config is small on purpose: 4 layers, 4 heads, 128 dimensions, and a context of 128 characters. Each head works on 128 / 4 = 32 dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Causal self-attention
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;model.py, the attention layer&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CausalSelfAttention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n_embd must divide by n_head&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qkv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attn_drop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resid_drop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Lower-triangular matrix: position t may look at positions 0..t only.
&lt;/span&gt;        &lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tril&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;persistent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;
        &lt;span class="n"&gt;hs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt;  &lt;span class="c1"&gt;# head size
&lt;/span&gt;        &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;qkv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# (B, T, C) -&amp;gt; (B, n_head, T, hs)
&lt;/span&gt;        &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hs&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# (B, nh, T, T)
&lt;/span&gt;        &lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;masked_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attn_drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;                                              &lt;span class="c1"&gt;# (B, nh, T, hs)
&lt;/span&gt;        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;contiguous&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# merge heads
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resid_drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One linear layer produces queries, keys and values together, and &lt;code&gt;split&lt;/code&gt; cuts them apart. The &lt;code&gt;view&lt;/code&gt; and &lt;code&gt;transpose&lt;/code&gt; calls reshape each from &lt;code&gt;(B, T, 128)&lt;/code&gt; to &lt;code&gt;(B, 4, T, 32)&lt;/code&gt;, so every head gets its own 32 dimensions.&lt;/p&gt;

&lt;p&gt;The scores are &lt;code&gt;q @ k.T&lt;/code&gt;, scaled by &lt;code&gt;hs ** -0.5&lt;/code&gt;. The scaling comes from the scaled dot-product attention in the Transformer paper. The authors suspect that for large head sizes the dot products grow in magnitude, which pushes softmax into regions with extremely small gradients.&lt;/p&gt;

&lt;p&gt;The mask is the part that makes this a language model. Position &lt;code&gt;t&lt;/code&gt; may look at positions &lt;code&gt;0..t&lt;/code&gt; and nothing later. Here's the mask for 4 positions, and what softmax does with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tril&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;masked_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decimals&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tensor([[1., 0., 0., 0.],
        [1., 1., 0., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 1.]])
tensor([[1.0000, 0.0000, 0.0000, 0.0000],
        [0.5400, 0.4600, 0.0000, 0.0000],
        [0.4500, 0.0900, 0.4600, 0.0000],
        [0.1300, 0.4100, 0.3600, 0.0900]])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Masked scores become &lt;code&gt;-inf&lt;/code&gt; before softmax, so they turn into exact zeros. The second row spreads its weight across positions 0 and 1 only. Before rounding, every row sums to 1.&lt;/p&gt;

&lt;p&gt;PyTorch ships a fused version of this in &lt;code&gt;scaled_dot_product_attention&lt;/code&gt;, with an &lt;code&gt;is_causal&lt;/code&gt; flag. We write it by hand so you can see every step. A test later in this post checks that our layer matches PyTorch's output.&lt;/p&gt;

&lt;h3&gt;
  
  
  MLP, block, and the full model
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;model.py, MLP and block&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MLP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gelu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ln1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayerNorm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CausalSelfAttention&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ln2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayerNorm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MLP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ln1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mlp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ln2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MLP expands each position from 128 to 512 dimensions, applies GELU, and projects back. Attention mixes information across positions. The MLP transforms each position on its own.&lt;/p&gt;

&lt;p&gt;The block adds each sublayer's output to its input. Those residual paths let gradients flow straight through a deep stack.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;model.py, the GPT class&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# token embeddings
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wpe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# position embeddings
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dropout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ModuleList&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nc"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_layer&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ln_f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LayerNorm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lm_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lm_head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;                 &lt;span class="c1"&gt;# weight tying
&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_init_weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# GPT-2 trick: shrink the layers that write into the residual stream,
&lt;/span&gt;        &lt;span class="c1"&gt;# so the stream's variance doesn't grow with depth.
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;named_parameters&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attn.proj.weight&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mlp.proj.weight&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_layer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_init_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normal_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sequence length &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &amp;gt; block_size &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wpe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lm_head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ln_f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;                   &lt;span class="c1"&gt;# (B, T, vocab)
&lt;/span&gt;        &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cross_entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt;

    &lt;span class="nd"&gt;@torch.no_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;use top_k=1 for greedy decoding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;was_training&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;training&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="p"&gt;:]&lt;/span&gt;              &lt;span class="c1"&gt;# crop to the window
&lt;/span&gt;            &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;           &lt;span class="c1"&gt;# last position only
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;kth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;topk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
                &lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;masked_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;kth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;nxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;multinomial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nxt&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;was_training&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;num_params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# parameters() counts the tied embedding/head matrix once
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details in this class matter more than they look.&lt;/p&gt;

&lt;p&gt;First, the token embedding and the output head share one weight matrix. The input side turns an id into a vector, and the output side turns a vector back into scores over ids. Tying them saves 65 × 128 = 8,320 parameters, and the original Transformer paper shares its embedding and pre-softmax matrices the same way.&lt;/p&gt;

&lt;p&gt;Second, initialization: every weight starts from a normal distribution with standard deviation 0.02. The two projections that write into the residual stream, &lt;code&gt;attn.proj&lt;/code&gt; and &lt;code&gt;mlp.proj&lt;/code&gt;, get that value divided by &lt;code&gt;sqrt(2 * n_layer)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The GPT-2 paper describes scaling residual layers by 1/√N, where N is the number of residual layers. Each block contributes two, hence the &lt;code&gt;2 *&lt;/code&gt;. &lt;a href="https://github.com/karpathy/nanoGPT" rel="noopener noreferrer"&gt;nanoGPT&lt;/a&gt; implements it the same way.&lt;/p&gt;

&lt;p&gt;Third, &lt;code&gt;generate&lt;/code&gt; crops the context to the last &lt;code&gt;block_size&lt;/code&gt; tokens. The position embedding table only has 128 rows.&lt;/p&gt;

&lt;p&gt;Let's count parameters and check the untrained loss.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_batch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GPTConfig&lt;/span&gt;

&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/input.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1337&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GPTConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;num_params&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; parameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token embedding :&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wte&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;position embed  :&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wpe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one block       :&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final LayerNorm :&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;numel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ln_f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loss &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;   ln(65) = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;813,440 parameters
token embedding : 8320
position embed  : 16384
one block       : 197120
final LayerNorm : 256
torch.Size([32, 128, 65])
loss 4.1827   ln(65) = 4.1744
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers line up with the architecture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Token embedding (65 × 128)&lt;/td&gt;
&lt;td&gt;8,320&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Position embedding (128 × 128)&lt;/td&gt;
&lt;td&gt;16,384&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Each block (4 of them)&lt;/td&gt;
&lt;td&gt;197,120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Final LayerNorm&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;813,440&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A block holds 65,536 attention weights (4 × 128²), 131,072 MLP weights (8 × 128²), and 512 LayerNorm parameters. The untrained loss of 4.1827 sits right on ln(65) = 4.1744.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Test it before you train it
&lt;/h2&gt;

&lt;p&gt;Training a broken transformer wastes an afternoon and doesn't announce itself. The loss still goes down. Three checks catch most bugs first.&lt;/p&gt;

&lt;p&gt;The first is the one you just saw: initial loss near ln(vocab size). The second is a leak test. Change a token at position 10 and confirm that nothing before position 10 moves.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;tests/test_tinygpt.py&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_model_cannot_see_the_future&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;small_cfg&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;          &lt;span class="c1"&gt;# change the token at position 10
&lt;/span&gt;    &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;atol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1e-6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# earlier positions unchanged
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;          &lt;span class="c1"&gt;# position 10 onward changes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A model that can see the answer gets near-zero training loss and generates junk. This test is what tells you the mask works.&lt;/p&gt;

&lt;p&gt;The third check is overfitting. A healthy model can memorize one small batch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_can_overfit_one_batch&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;small_cfg&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AdamW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3e-3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the loss can't reach zero on 4 sequences, something is wrong in the forward pass or the optimizer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Train
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;train.py, schedule, optimizer and evaluation&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_lr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Linear warmup, then cosine decay from lr down to min_lr.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;min_lr&lt;/span&gt;
    &lt;span class="n"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;min_lr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;min_lr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_optimizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight_decay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.99&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;AdamW that applies weight decay to matrices only, not to biases or norms.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;decay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;no_decay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;decay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weight_decay&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;weight_decay&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;no_decay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weight_decay&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AdamW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@torch.no_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;estimate_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;splits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eval_iters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;splits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;losses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eval_iters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eval_iters&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The learning rate warms up over 100 steps to 1e-3, then follows a cosine curve down to 1e-4. Warmup keeps the first updates small while Adam's statistics settle.&lt;/p&gt;

&lt;p&gt;AdamW uses betas of (0.9, 0.99). The second value matches nanoGPT's Shakespeare config, which raises it because each step sees few tokens.&lt;/p&gt;

&lt;p&gt;Weight decay of 0.1 applies to weight matrices only. Biases and LayerNorm parameters are excluded, and a test checks the split.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;estimate_loss&lt;/code&gt; averages over several batches and switches to eval mode, which turns dropout off. It switches back afterward.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;train.py, the training loop (excerpt from main)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pick_device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GPTConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_layer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_layer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_head&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_embd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_optimizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight_decay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;splits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; params=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;num_params&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vocab=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vocab_size&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train_tokens=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; val_tokens=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sample_gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_id&lt;/span&gt;&lt;span class="p"&gt;()]],&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;t0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval_interval&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;losses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimate_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;splits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eval_iters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sample_gen&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
        &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;step &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | train &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | val &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;val&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;| &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;best_val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;best_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="nf"&gt;save_checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;best.pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;history.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;samples.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="n"&gt;lr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_lr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;min_lr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warmup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param_groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lr&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set_to_none&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip_grad_norm_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grad_clip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step draws a batch, computes the loss, backpropagates, clips the gradient norm to 1.0, and updates. Every 250 steps it evaluates, generates a 200-character sample, and saves &lt;code&gt;best.pt&lt;/code&gt; if validation loss improved.&lt;/p&gt;

&lt;p&gt;A step processes 32 × 128 = 4,096 characters. Two thousand steps is 8,192,000 characters, or about 8.2 passes over the 1,003,855-character training split.&lt;/p&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python train.py &lt;span class="nt"&gt;--data&lt;/span&gt; data/input.txt &lt;span class="nt"&gt;--out&lt;/span&gt; out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;device=cpu params=813,440 vocab=65 train_tokens=1,003,855 val_tokens=111,539
step     0 | train 4.1835 | val 4.1799 |     13s
step   250 | train 2.3962 | val 2.4186 |    160s
step   500 | train 2.1546 | val 2.1950 |    307s
step   750 | train 1.9662 | val 2.0432 |    450s
step  1000 | train 1.8321 | val 1.9598 |    595s
step  1250 | train 1.7527 | val 1.8790 |    740s
step  1500 | train 1.6836 | val 1.8391 |    891s
step  1750 | train 1.6436 | val 1.8080 |   1044s
step  2000 | train 1.6215 | val 1.7838 |   1188s
best val loss 1.7838 (2.573 bits/char)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That took 1,188 seconds on one CPU core. The best validation loss was 1.7838, which is 2.573 bits per character.&lt;/p&gt;

&lt;p&gt;The samples that &lt;code&gt;train.py&lt;/code&gt; writes to &lt;code&gt;out/samples.json&lt;/code&gt; show the model learning in stages. These are the first 110 characters at four checkpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;step 0
t3ZxjmhPPdkARxLsXltUWHoHca ?eghkyy&amp;amp;&amp;amp;NdkIgAlsvSBUMyccRD$BygXOfntw
kVrM h&amp;amp;?j SJR!sOEHelC!BTg3xUhPRCs3Ma--3KypMrW

step 250
Inol3 f pifie-mily mpoennd shay tinte.

The beome igle inkee he them.

KES:
S y tonoong sof themyowangh.
HOn d

step 1000
DORY:
Not fieve my that that
Romest thear not ut .
LUCED:
What the gelivl the. Shou jroses.
Find With you lets

step 2000
Eing Noices you, for villing a by like whose mork hard,
Shall appy'd mired: thou almp the place!

CORIOLANUS:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At step 0 it's noise drawn from all 65 symbols. By step 250 it has word-length chunks, sentence-ending periods, and even a stray speaker tag (&lt;code&gt;KES:&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;By step 1000 it knows that a name in capitals followed by a colon starts a speech. By step 2000 the format is solid, and many of the words are still not real ones.&lt;/p&gt;

&lt;p&gt;The training loss ends at 1.6215 and validation at 1.7838. That gap of 0.16 is small, and validation was still falling at the last checkpoint, so overfitting isn't what limits this run.&lt;/p&gt;

&lt;p&gt;The learning rate also decays to 1e-4 by step 2,000. That makes the flattening curve a poor sign that the model has run out of things to learn.&lt;/p&gt;

&lt;p&gt;Here's a tighter validation estimate on 200 batches instead of 40:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;checkpoint&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_checkpoint&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_corpus&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;train&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;estimate_loss&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;out/best.pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_corpus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/input.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimate_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val loss &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  perplexity &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  bits/char &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val loss 1.7899  perplexity 5.99  bits/char 2.582
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perplexity of 5.99 means the model is about as uncertain as a fair choice between six characters at each step. The 1.7899 differs from the 1.7838 in the log only because it samples different batches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Is 1.78 any good?
&lt;/h2&gt;

&lt;p&gt;A loss means nothing without a baseline. Two are easy to compute on the same validation split.&lt;/p&gt;

&lt;p&gt;The unigram model predicts from character frequencies alone. The bigram model predicts from the previous character alone.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;baselines.py&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unigram_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bincount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minlength&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;   &lt;span class="c1"&gt;# add-one smoothing
&lt;/span&gt;    &lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bigram_loss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;train_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bincount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minlength&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vocab&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keepdim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                  &lt;span class="c1"&gt;# P(next | previous)
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;val_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]].&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python baselines.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uniform guess : 4.1744
unigram       : 3.3473
bigram        : 2.4819
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bigram table, just 65 × 65 counts, reaches 2.48. The transformer's 1.78 comes from using more than the previous character. That gap is what attention over 128 characters of context buys you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Generate text
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;model.py, the sampling loop inside generate&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="p"&gt;:]&lt;/span&gt;              &lt;span class="c1"&gt;# crop to the window
&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;           &lt;span class="c1"&gt;# last position only
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;kth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;topk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;masked_fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;kth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;probs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nxt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;multinomial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;probs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nxt&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model outputs scores for every position, and we keep the last one. Dividing by temperature reshapes the distribution. Below 1 it sharpens, above 1 it flattens.&lt;/p&gt;

&lt;p&gt;Top-k keeps only the &lt;code&gt;k&lt;/code&gt; highest scores and masks the rest to &lt;code&gt;-inf&lt;/code&gt;. Then softmax turns scores into probabilities and &lt;code&gt;multinomial&lt;/code&gt; draws one character. The code asserts that temperature is positive, so greedy decoding uses &lt;code&gt;top_k=1&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sample.py&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Generate text from a trained checkpoint.

    python sample.py --ckpt out/best.pt --prompt &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROMEO:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; --tokens 400
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;checkpoint&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_checkpoint&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--ckpt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;out/best.pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--top-k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--seed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--device&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_checkpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ckpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_id&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stoi&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;SystemExit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt has characters the model has never seen: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temperature 0.8 with top-k 40 is the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python sample.py &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"ROMEO:"&lt;/span&gt; &lt;span class="nt"&gt;--tokens&lt;/span&gt; 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO:
As lord shall, you for the read, one and bed forther
Bear head laist to dreath be that God,
Why so, upon this up enemer fament.

FLAUDE:
I lord, the the fall for not presence forfe,
The cuntracia, bu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temperature 1.2 with no top-k filter (&lt;code&gt;--top-k 65&lt;/code&gt; keeps all 65 characters):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python sample.py &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"ROMEO:"&lt;/span&gt; &lt;span class="nt"&gt;--tokens&lt;/span&gt; 200 &lt;span class="nt"&gt;--temperature&lt;/span&gt; 1.2 &lt;span class="nt"&gt;--top-k&lt;/span&gt; 65
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO:
As loud shadford: whose chard's, Statenty no fe thy eread?

First MAUXEqd:
Sorrow that's planly so,
Two to it'u
Let it them could to hinde lilenct, a who and for neWrp
Suanntifhed but nighnie his, bu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And greedy decoding, which always takes the most likely character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python sample.py &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"ROMEO:"&lt;/span&gt; &lt;span class="nt"&gt;--tokens&lt;/span&gt; 200 &lt;span class="nt"&gt;--top-k&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROMEO:
The shall the soul the soul the soul the soul the son,
And the shall the some the so the soul the stand
The shall the so the so the so the so the soul
The shall the shall the shall the shall the stay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default sample has the shape of a play: speaker names in capitals, colons, short lines, and a few real words like "lord", "head" and "God". It has no grammar and no meaning. At 1.2 it invents words, and greedy decoding falls into a loop.&lt;/p&gt;

&lt;p&gt;Greedy decoding always picks the single most likely character, so once a phrase repeats, the repeated context makes the next repeat even more likely. Sampling adds randomness that can break the cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this model can and can't do
&lt;/h2&gt;

&lt;p&gt;It learned the surface of Shakespeare: layout, capitalization, common letter patterns. It didn't learn to write sentences.&lt;/p&gt;

&lt;p&gt;That's expected. The model has 813,440 parameters and read about a million characters, eight times.&lt;/p&gt;

&lt;p&gt;I ran one training run with one seed and didn't tune any hyperparameters. Everything ran on CPU only.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to scale it up
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/karpathy/nanoGPT" rel="noopener noreferrer"&gt;nanoGPT README&lt;/a&gt; gives useful reference points on the same dataset. Its CPU example (4 layers, 4 heads, 128 dimensions, context 64) takes about 3 minutes and reaches a loss of 1.88.&lt;/p&gt;

&lt;p&gt;Its GPU config (6 layers, 6 heads, 384 dimensions, context 256) reaches 1.4697 in about 3 minutes on one A100. The settings differ from ours, so treat those as rough comparisons.&lt;/p&gt;

&lt;p&gt;The same README lists GPT-2 at 124M parameters. That's about 150 times larger than tinygpt. It says &lt;code&gt;train.py&lt;/code&gt; reproduces that model on OpenWebText in about 4 days on one node with 8 A100 40GB GPUs.&lt;/p&gt;

&lt;p&gt;To move toward that, change things in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;More data. A 1 MB corpus caps everything else.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A subword tokenizer. Byte pair encoding shortens sequences, so each token carries more meaning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A bigger model. Raise &lt;code&gt;n_layer&lt;/code&gt;, &lt;code&gt;n_head&lt;/code&gt; and &lt;code&gt;n_embd&lt;/code&gt;, and lengthen &lt;code&gt;block_size&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A GPU. &lt;code&gt;train.py&lt;/code&gt; picks CUDA or MPS automatically. I haven't run those paths.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't do step 3 alone. Hoffmann et al. trained over 400 models and found that model size and training tokens should scale together: double the parameters, double the tokens.&lt;/p&gt;

&lt;p&gt;Their 70B model, Chinchilla, beat the 280B Gopher on a range of tasks using the same compute and 4× more data. The paper is &lt;a href="https://arxiv.org/abs/2203.15556" rel="noopener noreferrer"&gt;Training Compute-Optimal Large Language Models&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest &lt;span class="nt"&gt;-q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.........................                                                [100%]
25 passed in 12.23s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suite covers the tokenizer round trip, batch shifting, the parameter-count formula, tied weights, the causal mask, agreement with PyTorch's attention, overfitting a single batch, reproducible sampling, greedy decoding, the learning-rate schedule, the weight-decay split, checkpoint loading, a two-step training run on a tiny corpus, the sampler's rejection of characters it has never seen, and the download script's fallback when Python can't verify certificates.&lt;/p&gt;

&lt;p&gt;I also broke the code on purpose. Deleting the &lt;code&gt;masked_fill&lt;/code&gt; line from the attention layer makes exactly two tests fail: the leak test and the PyTorch-equivalence test. The other 23 still pass, which shows those two guard that line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas I hit while building it
&lt;/h2&gt;

&lt;p&gt;Split contiguously. Sample validation windows from the tail of the file, not from a shuffle of the whole text.&lt;/p&gt;

&lt;p&gt;Decay matrices only. Weight decay on biases and LayerNorm gains does nothing useful. The &lt;code&gt;dim() &amp;gt;= 2&lt;/code&gt; split handles it.&lt;/p&gt;

&lt;p&gt;Crop the context. &lt;code&gt;generate&lt;/code&gt; must slice to &lt;code&gt;block_size&lt;/code&gt;, or long generations crash on the position table. The test asks for 40 new tokens with a block size of 16.&lt;/p&gt;

&lt;p&gt;Seed your sampler. A &lt;code&gt;torch.Generator&lt;/code&gt; passed to &lt;code&gt;generate&lt;/code&gt; makes samples reproducible, and one test checks exactly that.&lt;/p&gt;

&lt;p&gt;Don't hard-code a start token. My first version seeded sampling with a newline and crashed on any corpus that had none, which the tiny-corpus test now covers.&lt;/p&gt;

&lt;p&gt;Save the vocabulary. The checkpoint stores the character list next to the weights. Without it, ids can't be turned back into text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download and run
&lt;/h2&gt;

&lt;p&gt;The project is &lt;a href="https://github.com/pradeep200892/tinygpt" rel="noopener noreferrer"&gt;tinygpt.zip&lt;/a&gt;. It contains every file listed above, the trained checkpoint in &lt;code&gt;out/&lt;/code&gt;, and the training log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unzip tinygpt.zip
&lt;span class="nb"&gt;cd &lt;/span&gt;tinygpt
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python get_data.py
python sample.py &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"ROMEO:"&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; pytest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retraining with &lt;code&gt;python train.py&lt;/code&gt; writes to &lt;code&gt;out/&lt;/code&gt; and overwrites the shipped checkpoint. Copy &lt;code&gt;out/&lt;/code&gt; somewhere first if you want to keep it.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is this really an LLM?&lt;/strong&gt; It's a language model built the same way as one. The "large" is about scale, and this is roughly 150 times smaller than the 124M GPT-2 model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I train it on my own text?&lt;/strong&gt; Yes. Run &lt;code&gt;python train.py --data yourfile.txt&lt;/code&gt;. The vocabulary comes from the file.&lt;/p&gt;

&lt;p&gt;With the default 128-character context the file needs at least 1,300 characters. Shorter files stop with a &lt;code&gt;ValueError&lt;/code&gt; that says how many tokens are missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does sampling fail on some prompts?&lt;/strong&gt; The tokenizer only knows characters from the training text. &lt;code&gt;sample.py&lt;/code&gt; stops and lists the ones it doesn't know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python sample.py &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"ROMEO: ☃"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt has characters the model has never seen: ['☃']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;encode&lt;/code&gt; yourself raises a &lt;code&gt;KeyError&lt;/code&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CharTokenizer&lt;/span&gt;

&lt;span class="n"&gt;tok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CharTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KeyError:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KeyError: '!'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why does&lt;/strong&gt; &lt;code&gt;get_data.py&lt;/code&gt; &lt;strong&gt;fail with&lt;/strong&gt; &lt;code&gt;CERTIFICATE_VERIFY_FAILED&lt;/code&gt;&lt;strong&gt;?&lt;/strong&gt; Python installed from python.org on macOS often ships without root certificates, so it can't verify any HTTPS server. &lt;code&gt;get_data.py&lt;/code&gt; detects this and downloads with &lt;code&gt;curl&lt;/code&gt; instead, which keeps verification on.&lt;/p&gt;

&lt;p&gt;To fix Python itself, run &lt;code&gt;Install Certificates.command&lt;/code&gt; from the &lt;code&gt;/Applications/Python 3.x/&lt;/code&gt; folder. Don't switch verification off to make the error go away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does&lt;/strong&gt; &lt;code&gt;pip install torch&lt;/code&gt; &lt;strong&gt;say "No matching distribution"?&lt;/strong&gt; Usually PyTorch doesn't publish a wheel for your Python version and Mac chip. I checked Python 3.14 with &lt;code&gt;pip install --dry-run&lt;/code&gt;: Apple Silicon has wheels, and the Intel Mac tag I tried (&lt;code&gt;macosx_10_15_x86_64&lt;/code&gt;) has none.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;uname -m&lt;/code&gt; to see your chip (&lt;code&gt;arm64&lt;/code&gt; means Apple Silicon). I tested everything on Python 3.12.3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the "Failed to initialize NumPy" warning?&lt;/strong&gt; PyTorch prints it when NumPy isn't installed. Nothing in tinygpt uses NumPy, and the sampler, the training script and the test suite ran fine without it in my check.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install numpy&lt;/code&gt; silences the message, and the install commands above include it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it run on a GPU?&lt;/strong&gt; The code picks CUDA or MPS if PyTorch finds one. I only tested it on CPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is the output nonsense?&lt;/strong&gt; The model is small and the data is tiny. Better output comes from more data, a bigger model, and longer training, in that order.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Vaswani et al., &lt;a href="https://arxiv.org/abs/1706.03762" rel="noopener noreferrer"&gt;Attention Is All You Need&lt;/a&gt;, 2017.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Radford et al., &lt;em&gt;Language Models are Unsupervised Multitask Learners&lt;/em&gt; (GPT-2), 2019. Source for pre-LN placement and residual-scaled initialization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hoffmann et al., &lt;a href="https://arxiv.org/abs/2203.15556" rel="noopener noreferrer"&gt;Training Compute-Optimal Large Language Models&lt;/a&gt;, 2022.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Andrej Karpathy, &lt;a href="https://github.com/karpathy/nanoGPT" rel="noopener noreferrer"&gt;nanoGPT&lt;/a&gt; and &lt;a href="https://github.com/karpathy/char-rnn" rel="noopener noreferrer"&gt;char-rnn&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyTorch documentation, &lt;code&gt;scaled_dot_product_attention&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/build-a-small-llm-from-scratch-a-tested-gpt-in-pytorch-62ccl?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>deeplearning</category>
      <category>gpt</category>
      <category>largelanguagemodels</category>
      <category>pytorch</category>
    </item>
    <item>
      <title>How an AI-Driven Copyright Takedown Got Luanti Pulled From Google Play, Again</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sat, 03 Oct 2026 04:56:55 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/how-an-ai-driven-copyright-takedown-got-luanti-pulled-from-google-play-again-2hnk</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/how-an-ai-driven-copyright-takedown-got-luanti-pulled-from-google-play-again-2hnk</guid>
      <description>&lt;p&gt;Luanti's Android app was temporarily removed from the Google Play Store this week after Microsoft, acting through the AI-driven brand protection company &lt;a href="https://www.tracer.ai/" rel="noopener noreferrer"&gt;Tracer.AI&lt;/a&gt;, filed a DMCA takedown notice alleging that the open-source voxel game platform infringes Minecraft's copyright.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://blog.luanti.org/2026/08/27/luanti-dmca-tracer-ai/" rel="noopener noreferrer"&gt;an August 27 post&lt;/a&gt;, the Luanti team called the notice baseless, said the app contains no proprietary code or assets from Minecraft, and confirmed that it had submitted a counter-notice to Google.&lt;/p&gt;

&lt;p&gt;The incident is notable not only because of the copyright dispute itself, but because it is the &lt;strong&gt;second time Luanti says it has received essentially the same notice from the same company&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Luanti Actually Is
&lt;/h2&gt;

&lt;p&gt;Luanti, known as Minetest until its &lt;a href="https://blog.luanti.org/2024/10/13/Introducing-Our-New-Name/" rel="noopener noreferrer"&gt;2024 rebrand&lt;/a&gt;, is a nonprofit, open-source voxel game-creation platform.&lt;/p&gt;

&lt;p&gt;Unlike a conventional game, Luanti does not ship with a default game or a collection of game assets. Instead, users can browse and download games created by the community through its &lt;a href="https://content.luanti.org/packages/?type=game" rel="noopener noreferrer"&gt;ContentDB&lt;/a&gt; catalog. Luanti describes itself as a platform for creating, sharing, discovering, and playing block-based games.&lt;/p&gt;

&lt;p&gt;That distinction matters here because the copyright complaint alleges unauthorized use of Minecraft assets, while Luanti says the engine itself does not contain those assets.&lt;/p&gt;

&lt;p&gt;The project previously bundled a basic survival game called Minetest Game. Starting with the December 2023 release, Minetest stopped shipping with a default game, although Minetest Game remained available as a separate download.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Notice Without Specifics
&lt;/h2&gt;

&lt;p&gt;The DMCA notice Google forwarded to Luanti alleges:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"These products use copyrighted assets as outlined directly from the Minecraft game…"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It identifies &lt;strong&gt;US Copyright Registration TX 8-192-097&lt;/strong&gt;, which Luanti says corresponds to Minecraft Java Edition 1.9.&lt;/p&gt;

&lt;p&gt;But according to Luanti, the notice provides no further explanation of what specific assets supposedly infringe or where those assets can be found in Luanti.&lt;/p&gt;

&lt;p&gt;That lack of specificity is at the center of the dispute.&lt;/p&gt;

&lt;p&gt;Luanti's position is essentially simple: if copyrighted Minecraft assets are supposedly present in the app, the claimant should be able to identify them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Assets That Are Not There
&lt;/h2&gt;

&lt;p&gt;Luanti's response is that the alleged Minecraft assets cannot be found in the Android app.&lt;/p&gt;

&lt;p&gt;The project published an &lt;a href="https://blog.luanti.org/static/blog/2026_dmca/builtin.webp" rel="noopener noreferrer"&gt;image showing the textures included with the engine&lt;/a&gt; and says the additional assets it ships, including fonts, are properly attributed in its &lt;a href="https://github.com/luanti-org/luanti/blob/master/LICENSE.txt" rel="noopener noreferrer"&gt;license files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The situation is slightly more complicated when looking at the broader Luanti ecosystem.&lt;/p&gt;

&lt;p&gt;The project's GitHub repository contains the Development Test game, but Luanti says this is not included in released builds. Minetest Game is also separate from the engine and is no longer bundled with releases.&lt;/p&gt;

&lt;p&gt;Luanti also hosts third-party games, mods, and texture packs through ContentDB. Those packages are reviewed manually by volunteer moderators before approval, and the project says it actively checks for potential copyright problems.&lt;/p&gt;

&lt;p&gt;That distinction matters: a platform can host community-created material without every piece of that material being part of the core application itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Genre, Not a Monopoly
&lt;/h2&gt;

&lt;p&gt;Luanti also makes a broader legal argument.&lt;/p&gt;

&lt;p&gt;Section 102(b) of the &lt;a href="https://www.law.cornell.edu/uscode/text/17/102" rel="noopener noreferrer"&gt;US Copyright Act&lt;/a&gt; says copyright protection does not extend to ideas, concepts, systems, or methods of operation.&lt;/p&gt;

&lt;p&gt;Luanti points out that Minecraft was inspired by the 2009 voxel game Infiniminer, while later games such as Hytale also use block-based worlds.&lt;/p&gt;

&lt;p&gt;The project's argument is not that Microsoft or Mojang cannot protect Minecraft's actual copyrighted expression. They can enforce rights in protected assets where infringement exists.&lt;/p&gt;

&lt;p&gt;The argument is narrower: &lt;strong&gt;a general voxel-based game concept or block-based visual language is not, by itself, something one company can own exclusively.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whether a particular game or asset infringes copyright is a separate question that depends on the specific material involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not the First Time
&lt;/h2&gt;

&lt;p&gt;This is not Luanti's first encounter with Tracer.AI.&lt;/p&gt;

&lt;p&gt;The project says it received an &lt;strong&gt;identical notice in March 2023&lt;/strong&gt;. After submitting a counter-notice, the app was eventually restored &lt;strong&gt;46 days later&lt;/strong&gt;, on May 10. Luanti says the counter-notice was submitted on March 25.&lt;/p&gt;

&lt;p&gt;That matters because US copyright law provides a specific counter-notification mechanism.&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;17 U.S.C. §512(g)(2)(C)&lt;/strong&gt;, a service provider that removes material generally has a restoration procedure under which it must replace the material or cease disabling access &lt;strong&gt;10 to 14 business days after receiving a qualifying counter-notification&lt;/strong&gt;, unless the claimant files a court action seeking to restrain the material.&lt;/p&gt;

&lt;p&gt;Luanti says the 46-day delay in 2023 went well beyond that statutory window and argues that Google's handling of the previous counter-notice raises questions about how the company processes DMCA disputes.&lt;/p&gt;

&lt;p&gt;That is Luanti's characterization of the situation, rather than a court ruling that Google violated the statute.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Allumeria Connection
&lt;/h2&gt;

&lt;p&gt;Luanti's experience is also not the only recent Microsoft-related case involving Tracer.AI.&lt;/p&gt;

&lt;p&gt;In February 2026, indie developer Unomelon said the voxel sandbox game &lt;a href="https://allumeria.com/" rel="noopener noreferrer"&gt;Allumeria&lt;/a&gt; had been temporarily removed from Steam following a Microsoft copyright complaint submitted through Tracer.AI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pcgamer.com/games/survival-crafting/i-genuinely-do-not-know-what-to-do-says-developer-of-minecraft-like-allumeria-after-microsoft-issues-a-dmca-takedown-forcing-it-off-steam/" rel="noopener noreferrer"&gt;PC Gamer&lt;/a&gt; reported that the claim concerned alleged similarities to Minecraft and that Microsoft later withdrew the complaint. The game returned to Steam without the developer needing to file a counter-notice.&lt;/p&gt;

&lt;p&gt;The two cases are not identical, but they share an important feature: &lt;strong&gt;both involved smaller voxel-game projects facing Microsoft-backed copyright enforcement despite disputes over what, specifically, was allegedly infringing.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Tracer.AI describes itself as an AI-powered brand protection company.&lt;/p&gt;

&lt;p&gt;Its own marketing describes a &lt;strong&gt;Human-in-the-Loop AI&lt;/strong&gt; system that uses AI to identify potential infringements while incorporating human expertise and verification.&lt;/p&gt;

&lt;p&gt;Tracer says its technology produces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;85% faster takedowns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;100% more reviews month over month&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review times six times faster than traditional methods&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;44% more takedowns month over month&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those figures come from Tracer itself and are therefore best understood as the company's own reported performance claims.&lt;/p&gt;

&lt;p&gt;But they highlight an important tension.&lt;/p&gt;

&lt;p&gt;AI-assisted enforcement can make it possible to identify and process vastly more potential infringements than a purely manual system. That is useful for rights holders dealing with large-scale online abuse.&lt;/p&gt;

&lt;p&gt;The problem appears when a detection is wrong.&lt;/p&gt;

&lt;p&gt;The cost of a false positive may not be paid by the enforcement vendor or rights holder. It may instead be paid by the developer whose app disappears from a major distribution platform while the dispute is reviewed.&lt;/p&gt;

&lt;p&gt;That is why the question is not simply whether AI can make copyright enforcement faster.&lt;/p&gt;

&lt;p&gt;It is whether &lt;strong&gt;verification can become fast enough to keep up with detection.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Luanti Is Asking For
&lt;/h2&gt;

&lt;p&gt;Luanti's demands are relatively specific.&lt;/p&gt;

&lt;p&gt;The project wants Microsoft, Mojang, and Tracer.AI to require human verification before sending copyright notices and to provide concrete evidence supporting each claim rather than only citing a copyright registration number.&lt;/p&gt;

&lt;p&gt;It is also asking Google to improve the way it evaluates notices and handles counter-notifications.&lt;/p&gt;

&lt;p&gt;In other words, Luanti is not arguing that copyright enforcement should disappear.&lt;/p&gt;

&lt;p&gt;It is arguing that enforcement should be &lt;strong&gt;specific, verifiable, and reversible without leaving legitimate projects offline for extended periods.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Things Stand Now
&lt;/h2&gt;

&lt;p&gt;The immediate situation has changed quickly.&lt;/p&gt;

&lt;p&gt;When Luanti published its August 27 statement, the project said its Android app was unavailable on Google Play and that a counter-notice had been submitted.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://play.google.com/store/apps/details?hl=en_US&amp;amp;id=net.minetest.minetest" rel="noopener noreferrer"&gt;Google Play listing&lt;/a&gt; currently appears to be live again and shows an &lt;strong&gt;Install&lt;/strong&gt; button for the official Luanti app.&lt;/p&gt;

&lt;p&gt;That suggests the latest takedown was resolved substantially faster than the 46-day incident in 2023, although the available sources do not establish exactly when Google restored the listing or whether the underlying dispute has been formally closed.&lt;/p&gt;

&lt;p&gt;The story also attracted significant attention on Hacker News, where it reached the front page and generated hundreds of points and more than 100 comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Lesson
&lt;/h2&gt;

&lt;p&gt;The Luanti case is ultimately bigger than one open-source game platform.&lt;/p&gt;

&lt;p&gt;Copyright enforcement depends on trust.&lt;/p&gt;

&lt;p&gt;Rights holders need tools capable of finding genuine infringement at internet scale. Platforms need mechanisms for acting quickly when credible complaints arrive. And developers need a meaningful way to challenge mistakes before a temporary removal becomes a serious business or distribution failure.&lt;/p&gt;

&lt;p&gt;AI can make the first part dramatically faster.&lt;/p&gt;

&lt;p&gt;But speed creates a second requirement: &lt;strong&gt;the verification process has to become better, too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Luanti's second encounter with the same type of copyright notice shows what happens when those two systems move at different speeds.&lt;/p&gt;

&lt;p&gt;A claim can take minutes to trigger a platform action.&lt;/p&gt;

&lt;p&gt;A legitimate developer may then need days, weeks, or longer to prove that the claim was wrong.&lt;/p&gt;

&lt;p&gt;The real question raised by the Luanti case is therefore not whether AI should be used in copyright enforcement.&lt;/p&gt;

&lt;p&gt;It is whether &lt;strong&gt;AI-assisted enforcement can scale without scaling the damage caused by false positives.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/how-an-ai-driven-copyright-takedown-got-luanti-pulled-from-google-play-again-lp4uv?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aimoderation</category>
      <category>copyright</category>
      <category>luanti</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Apple M6 and M5 Ultra: The Mac Is Becoming a Serious AI Workstation</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sat, 03 Oct 2026 04:55:26 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/apple-m6-and-m5-ultra-the-mac-is-becoming-a-serious-ai-workstation-1l56</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/apple-m6-and-m5-ultra-the-mac-is-becoming-a-serious-ai-workstation-1l56</guid>
      <description>&lt;p&gt;Apple has just pushed its Mac silicon strategy into a new phase.&lt;/p&gt;

&lt;p&gt;The company has introduced &lt;strong&gt;M6&lt;/strong&gt; in the new Mac mini and &lt;strong&gt;M5 Ultra&lt;/strong&gt; in the new Mac Studio, bringing major changes to both everyday computing and high-end AI workloads.&lt;/p&gt;

&lt;p&gt;M6 is Apple's &lt;strong&gt;first 2-nanometer chip&lt;/strong&gt;, with a new 12-core CPU, 12-core GPU, Dual 16-core Neural Engine, and up to &lt;strong&gt;170GB/s of unified memory bandwidth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;M5 Ultra goes much further. It is Apple's &lt;strong&gt;first quad-die M-series chip&lt;/strong&gt;, combining four compute dies through a new generation of UltraFusion technology. It scales to a 36-core CPU, 80-core GPU, up to 512GB of unified memory, and 1.2TB/s of memory bandwidth.&lt;/p&gt;

&lt;p&gt;On paper, those numbers look impressive.&lt;/p&gt;

&lt;p&gt;But the more important story is what Apple is building toward: &lt;strong&gt;a Mac where local AI is becoming a core workload rather than an experimental feature.&lt;/strong&gt;&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%2Fblhhnalbsx3pm0w0me33.jpg" 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%2Fblhhnalbsx3pm0w0me33.jpg" alt="Apple M6 and M5 Ultra" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Image:&lt;/strong&gt; Apple M6 and M5 Ultra&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Image credit:&lt;/strong&gt; Apple&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://www.apple.com/newsroom/2026/08/apple-introduces-m6-and-m5-ultra-for-a-big-leap-in-performance-and-ai-compute/" rel="noopener noreferrer"&gt;Apple Newsroom&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  M6: Apple's First 2nm Mac Chip
&lt;/h2&gt;

&lt;p&gt;M6 is the first Apple silicon chip for the Mac built using a &lt;strong&gt;2nm process&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Moving to a smaller process node gives Apple more transistor density and the potential for better performance and efficiency. Apple is using that additional silicon capacity across the CPU, GPU and AI hardware rather than concentrating it in a single compute block.&lt;/p&gt;

&lt;p&gt;The M6 CPU has &lt;strong&gt;12 cores&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2 super cores&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4 performance cores&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;6 efficiency cores&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is two additional CPU cores compared with M5.&lt;/p&gt;

&lt;p&gt;Apple claims up to &lt;strong&gt;1.2× faster multithreaded performance than M5&lt;/strong&gt; and up to &lt;strong&gt;2.4× faster than M1&lt;/strong&gt; in its selected benchmarks. Apple also describes M6 as delivering the world's fastest single-threaded performance.&lt;/p&gt;

&lt;p&gt;These are &lt;strong&gt;Apple's benchmark claims&lt;/strong&gt;, so they should not be interpreted as independent real-world measurements.&lt;/p&gt;

&lt;p&gt;For developers, however, the potential benefits are straightforward: compiling projects, indexing large codebases, manipulating assets, running multiple development tools, and executing local agent workloads can all benefit from stronger CPU performance.&lt;/p&gt;

&lt;p&gt;Apple specifically highlights &lt;strong&gt;code compilation, file indexing and agentic AI workloads&lt;/strong&gt; as areas that benefit from M6.&lt;/p&gt;

&lt;h2&gt;
  
  
  The M6 Is Designed Around AI
&lt;/h2&gt;

&lt;p&gt;The most interesting part of M6 may not be the CPU.&lt;/p&gt;

&lt;p&gt;Its &lt;strong&gt;12-core GPU&lt;/strong&gt; contains a &lt;strong&gt;Neural Accelerator in every GPU core&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Apple says this produces nearly &lt;strong&gt;30% higher peak GPU AI compute than M5&lt;/strong&gt; and more than &lt;strong&gt;8× the peak GPU AI compute of M1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;M6 also features a &lt;strong&gt;Dual 16-core Neural Engine&lt;/strong&gt;. Apple says system frameworks can use both engines simultaneously, providing up to &lt;strong&gt;2× the peak compute of previous generations&lt;/strong&gt; for suitable workloads.&lt;/p&gt;

&lt;p&gt;That makes the architectural direction clear.&lt;/p&gt;

&lt;p&gt;Apple is not relying on one dedicated AI block. The CPU, GPU and Neural Engine can all participate in AI workloads depending on what an application requires.&lt;/p&gt;

&lt;p&gt;That becomes increasingly important as applications move beyond simple AI features toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Coding assistants&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Local AI agents&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Private assistants&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document analysis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On-device retrieval&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous inference&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automated development workflows&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fydol24zsv451kq0a8olx.jpg" 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%2Fydol24zsv451kq0a8olx.jpg" alt="M6-powered Mac mini" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Image:&lt;/strong&gt; M6-powered Mac mini&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Image credit:&lt;/strong&gt; Apple&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://www.apple.com/newsroom/2026/08/apple-unveils-a-more-powerful-mac-mini-featuring-the-all-new-m6-and-m5-pro/" rel="noopener noreferrer"&gt;Apple Newsroom&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Faster Unified Memory Matters More Than It Looks
&lt;/h2&gt;

&lt;p&gt;M6 supports up to &lt;strong&gt;32GB of unified memory&lt;/strong&gt; with memory bandwidth of up to &lt;strong&gt;170GB/s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Apple says this is a &lt;strong&gt;10% increase over M5&lt;/strong&gt; and &lt;strong&gt;2.5× the bandwidth of M1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This matters for AI because model execution can become a memory problem long before a developer expects it to.&lt;/p&gt;

&lt;p&gt;Apple's unified-memory architecture allows the CPU and GPU to work from the same memory pool rather than constantly moving large datasets between separate CPU and GPU memory.&lt;/p&gt;

&lt;p&gt;For smaller local models, embeddings, AI coding assistants and agentic applications, memory capacity and bandwidth can sometimes matter more than a headline CPU benchmark.&lt;/p&gt;

&lt;p&gt;There is still an important limitation.&lt;/p&gt;

&lt;p&gt;The maximum &lt;strong&gt;32GB&lt;/strong&gt; memory configuration means M6 is not intended to compete with M5 Ultra for the largest local models.&lt;/p&gt;

&lt;p&gt;That is where Apple's high-end desktop silicon becomes much more interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then Apple Does Something Much Bigger With M5 Ultra
&lt;/h2&gt;

&lt;p&gt;M5 Ultra is where Apple's architecture becomes particularly interesting.&lt;/p&gt;

&lt;p&gt;Instead of simply creating one enormous die, Apple uses &lt;strong&gt;UltraFusion&lt;/strong&gt; to connect &lt;strong&gt;two dual-die M5 Max chips&lt;/strong&gt;, creating a &lt;strong&gt;quad-die architecture&lt;/strong&gt; for the first time in Apple silicon.&lt;/p&gt;

&lt;p&gt;The interconnect provides more than &lt;strong&gt;4.4TB/s of inter-die bandwidth&lt;/strong&gt;, while Apple says its connection density is more than six times higher than before.&lt;/p&gt;

&lt;p&gt;The objective is to make the four dies behave like a &lt;strong&gt;single unified processor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the key to understanding M5 Ultra.&lt;/p&gt;

&lt;p&gt;Apple is effectively scaling its architecture horizontally without abandoning the unified-system approach that has defined Apple silicon.&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%2F70u96b49mfz2kzptc9p4.jpg" 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%2F70u96b49mfz2kzptc9p4.jpg" alt="M5 Ultra / Mac Studio" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Image:&lt;/strong&gt; M5 Ultra-powered Mac Studio&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Image credit:&lt;/strong&gt; Apple&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://www.apple.com/newsroom/2026/08/apple-introduces-new-mac-studio-with-m5-max-and-m5-ultra/" rel="noopener noreferrer"&gt;Apple Newsroom&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  36 CPU Cores and 80 GPU Cores
&lt;/h2&gt;

&lt;p&gt;At the top end, M5 Ultra reaches &lt;strong&gt;36 CPU cores&lt;/strong&gt;, made up of 12 super cores and 24 performance cores.&lt;/p&gt;

&lt;p&gt;Apple claims up to &lt;strong&gt;1.25× higher single-threaded performance&lt;/strong&gt; and up to &lt;strong&gt;1.3× higher multithreaded performance than M3 Ultra&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The GPU scales to an enormous &lt;strong&gt;80 cores&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And once again, every GPU core includes a Neural Accelerator.&lt;/p&gt;

&lt;p&gt;Apple says M5 Ultra can deliver up to &lt;strong&gt;4.5× the peak GPU AI compute compared with M3 Ultra&lt;/strong&gt;, and more than &lt;strong&gt;6× compared with M1 Ultra&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Apple also claims graphics performance of up to &lt;strong&gt;40% faster than M3 Ultra&lt;/strong&gt; in its selected benchmarks.&lt;/p&gt;

&lt;p&gt;These figures are Apple-provided performance claims. Independent testing will ultimately determine how those improvements translate across different applications and AI workloads.&lt;/p&gt;

&lt;p&gt;This puts M5 Ultra firmly into workstation territory.&lt;/p&gt;

&lt;p&gt;But the most interesting specification is still the memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  512GB of Unified Memory Changes the Conversation
&lt;/h2&gt;

&lt;p&gt;M5 Ultra supports up to &lt;strong&gt;512GB of unified memory&lt;/strong&gt; with up to &lt;strong&gt;1.2TB/s of memory bandwidth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Apple says the bandwidth is &lt;strong&gt;50% higher than M3 Ultra&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why is this such a big deal?&lt;/p&gt;

&lt;p&gt;Because large AI models need memory.&lt;/p&gt;

&lt;p&gt;A system with powerful GPU compute but insufficient memory can still be heavily constrained. Large models may need to be quantized, split across devices, offloaded or executed remotely.&lt;/p&gt;

&lt;p&gt;With 512GB of unified memory, M5 Ultra dramatically raises the amount of model data that can remain resident locally.&lt;/p&gt;

&lt;p&gt;Apple says the 512GB configuration can be used to run &lt;strong&gt;LLMs with hundreds of billions of parameters entirely on-device&lt;/strong&gt; while also keeping large datasets in local memory.&lt;/p&gt;

&lt;p&gt;That does not mean every huge model will suddenly run quickly on a Mac.&lt;/p&gt;

&lt;p&gt;Model architecture, quantization, context length, software optimization, memory access patterns and inference implementation still matter.&lt;/p&gt;

&lt;p&gt;The distinction is important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Memory capacity determines what can fit. Compute and software determine how quickly it runs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&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%2F81157dbbd40cxiqjf31z.jpg" 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%2F81157dbbd40cxiqjf31z.jpg" alt="Local AI workflow using M5 Ultra" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Image:&lt;/strong&gt; Local AI workloads on Mac Studio&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Image credit:&lt;/strong&gt; Apple&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://www.apple.com/newsroom/2026/08/apple-introduces-m6-and-m5-ultra-for-a-big-leap-in-performance-and-ai-compute/" rel="noopener noreferrer"&gt;Apple Newsroom&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  This Is Where Developers Should Pay Attention
&lt;/h2&gt;

&lt;p&gt;Apple's developer ecosystem is increasingly aligned with these hardware capabilities.&lt;/p&gt;

&lt;p&gt;The company highlights technologies including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Core AI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Core ML&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Metal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Xcode&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apple Foundation Models&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;App Intents&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These technologies give developers multiple ways to integrate AI into applications and take advantage of Apple's on-device compute.&lt;/p&gt;

&lt;p&gt;The potential architecture is interesting.&lt;/p&gt;

&lt;p&gt;You can imagine an application where:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple inference happens locally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private data stays on-device.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency-sensitive tasks run locally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large or expensive workloads move to the cloud.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Mac therefore does not necessarily replace cloud AI.&lt;/p&gt;

&lt;p&gt;Instead, it becomes another compute layer in an AI application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apple Is Betting on Local AI
&lt;/h2&gt;

&lt;p&gt;There is a broader shift happening here.&lt;/p&gt;

&lt;p&gt;The AI industry has spent years moving toward massive centralized GPU clusters. That will continue because frontier training and the largest inference workloads require extraordinary amounts of compute.&lt;/p&gt;

&lt;p&gt;But not every AI task needs a data center.&lt;/p&gt;

&lt;p&gt;Code completion, document analysis, personal assistants, private knowledge retrieval, image generation, automation and agentic workflows can benefit from local execution.&lt;/p&gt;

&lt;p&gt;Local inference offers several obvious advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lower latency&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced dependence on cloud APIs&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Potentially lower recurring inference costs&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;M6 brings more of that capability to the Mac mini.&lt;/p&gt;

&lt;p&gt;M5 Ultra pushes it into a different category entirely: a desktop system with enough memory and compute to handle local AI workloads that would previously have required more specialized hardware or cloud infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  M6 vs M5 Ultra
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Specification&lt;/th&gt;
&lt;th&gt;M6&lt;/th&gt;
&lt;th&gt;M5 Ultra&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Process&lt;/td&gt;
&lt;td&gt;2nm&lt;/td&gt;
&lt;td&gt;M5 generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Up to 12 cores&lt;/td&gt;
&lt;td&gt;Up to 36 cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;Up to 12 cores&lt;/td&gt;
&lt;td&gt;Up to 80 cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neural Engine&lt;/td&gt;
&lt;td&gt;Dual 16-core&lt;/td&gt;
&lt;td&gt;32-core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unified memory&lt;/td&gt;
&lt;td&gt;Up to 32GB&lt;/td&gt;
&lt;td&gt;Up to 512GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory bandwidth&lt;/td&gt;
&lt;td&gt;Up to 170GB/s&lt;/td&gt;
&lt;td&gt;Up to 1.2TB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Single chip&lt;/td&gt;
&lt;td&gt;Quad-die&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main AI advantage&lt;/td&gt;
&lt;td&gt;Efficient local AI&lt;/td&gt;
&lt;td&gt;High-memory local AI workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device&lt;/td&gt;
&lt;td&gt;Mac mini&lt;/td&gt;
&lt;td&gt;Mac Studio&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are not really direct competitors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;M6 makes local AI more accessible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;M5 Ultra makes local AI much more ambitious.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Story Behind M6 and M5 Ultra
&lt;/h2&gt;

&lt;p&gt;It is easy to summarize this launch as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;New Apple chips are faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That misses the more important development.&lt;/p&gt;

&lt;p&gt;M6 demonstrates how Apple is using &lt;strong&gt;2nm manufacturing, additional compute cores, Neural Accelerators and multiple Neural Engines&lt;/strong&gt; to make on-device AI part of mainstream Mac computing.&lt;/p&gt;

&lt;p&gt;M5 Ultra demonstrates how Apple can scale that architecture through &lt;strong&gt;multi-die packaging, enormous unified memory and extreme bandwidth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The result is a different kind of Mac.&lt;/p&gt;

&lt;p&gt;It is not just a machine for running applications.&lt;/p&gt;

&lt;p&gt;It is increasingly a machine for &lt;strong&gt;running models&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that distinction matters.&lt;/p&gt;

&lt;p&gt;As AI moves from chatbots toward coding agents, autonomous workflows and always-available assistants, more computation can potentially happen locally.&lt;/p&gt;

&lt;p&gt;Apple's M6 and M5 Ultra suggest that the company wants the Mac to be ready for exactly that future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The M6 and M5 Ultra are interesting for different reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;M6 is Apple's next step toward efficient, accessible local AI.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;M5 Ultra is Apple's attempt to turn a desktop Mac into a serious high-memory AI workstation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The M6's 2nm process, Dual 16-core Neural Engine, 12-core GPU and 170GB/s memory bandwidth make it a substantial step forward in Apple's Mac silicon lineup.&lt;/p&gt;

&lt;p&gt;M5 Ultra goes much further with its quad-die design, 80-core GPU, Neural Accelerators, 512GB unified memory and 1.2TB/s memory bandwidth.&lt;/p&gt;

&lt;p&gt;But the biggest takeaway is not any individual specification.&lt;/p&gt;

&lt;p&gt;It is Apple's direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mac is increasingly becoming an AI-capable computer rather than simply a traditional personal computer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And with M6 and M5 Ultra, Apple is betting that some of the most important AI workloads of the next few years will happen right on the desk in front of you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.apple.com/newsroom/2026/08/apple-introduces-m6-and-m5-ultra-for-a-big-leap-in-performance-and-ai-compute/" rel="noopener noreferrer"&gt;Apple Newsroom — Apple introduces M6 and M5 Ultra&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.apple.com/newsroom/2026/08/apple-unveils-a-more-powerful-mac-mini-featuring-the-all-new-m6-and-m5-pro/" rel="noopener noreferrer"&gt;Apple Newsroom — Mac mini with M6 and M5 Pro&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.apple.com/newsroom/2026/08/apple-introduces-new-mac-studio-with-m5-max-and-m5-ultra/" rel="noopener noreferrer"&gt;Apple Newsroom — Mac Studio with M5 Max and M5 Ultra&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://arstechnica.com/apple/2026/08/with-new-mac-studio-and-mac-mini-apple-leans-hard-into-local-ai-inference/" rel="noopener noreferrer"&gt;Ars Technica — Apple's local AI strategy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.theverge.com/tech/984118/apple-m6-m5-ultra-chip-mac-mini-studio" rel="noopener noreferrer"&gt;The Verge — M6 and M5 Ultra&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Image Credits
&lt;/h3&gt;

&lt;p&gt;All images in this article are official Apple Newsroom images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Image credit:&lt;/strong&gt; Apple&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; Apple Newsroom&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/apple-m6-and-m5-ultra-the-mac-is-becoming-a-serious-ai-workstation-3wbyf?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Introducing SynthID Bio: Watermarking AI-Designed Proteins Before They Reach the Lab</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sat, 03 Oct 2026 04:16:30 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/introducing-synthid-bio-watermarking-ai-designed-proteins-before-they-reach-the-lab-2of2</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/introducing-synthid-bio-watermarking-ai-designed-proteins-before-they-reach-the-lab-2of2</guid>
      <description>&lt;p&gt;If you build anything that sits between an AI model and a physical molecule, SynthID Bio is worth understanding today. On 30 September 2026, Google DeepMind &lt;a href="https://deepmind.google/blog/introducing-synthid-bio/" rel="noopener noreferrer"&gt;introduced SynthID Bio&lt;/a&gt;, a family of watermarking methods for synthetic biology. It hides a verifiable signature inside AI-designed protein sequences and predicted 3D structures, and DeepMind reports that the signature survives synthesis into a real, working protein.&lt;/p&gt;

&lt;p&gt;This post covers what the system does, what the evidence supports, what it does not solve, and where an engineering team could plug it in. The authors call it a proof of concept, and that framing matters throughout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a watermark check would sit
&lt;/h2&gt;

&lt;p&gt;The clearest way to see the value is to follow a design from model to molecule. A researcher generates a protein with an AI model, then places an order with a DNA synthesis provider. The provider screens the order before making anything. A watermark gives that screening step a new input: evidence that the design came from a specific, trusted model.&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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpNkM1uwkAMhF_F9XlzqNQTh0r8qpVahEBtD4HDknUSi42NdpcABd69ClS0R1vfzHh8wkIdYQ9Lr_uitiHB23wpAAD9_MsmCo0NG3LQqCO_gix7hkE-4oqT9bANmogFHEWuZHXTDa7QMB9N-xCPkmqKHEGDo_BLDK_E6DQL2rKjALEIRMJSXW7AqAPO93hoKXDJ5M4wzmcaOXFLQJ1WCjIQqGXaQ6GSWHYUV_9dpgr7u1GpO3FnmOTjA8fEUv1FG9hJUVupyK3QYNMp2GHvhKmmpnuRo9LufEJz23zawHbtKXZMqZImtmF_xB5mdrv1lMVjTNQYGHiWzbstFtd5opIMLHFBlRJ8vC7RwFzXmtTAC_mWEhfWQD-w9QailZjFrj6aa8iCv7tbHp-2B7xcDK6roXoN2MOHfc2J8PIDyu2fOA%3Ftype%3Dpng" 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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpNkM1uwkAMhF_F9XlzqNQTh0r8qpVahEBtD4HDknUSi42NdpcABd69ClS0R1vfzHh8wkIdYQ9Lr_uitiHB23wpAAD9_MsmCo0NG3LQqCO_gix7hkE-4oqT9bANmogFHEWuZHXTDa7QMB9N-xCPkmqKHEGDo_BLDK_E6DQL2rKjALEIRMJSXW7AqAPO93hoKXDJ5M4wzmcaOXFLQJ1WCjIQqGXaQ6GSWHYUV_9dpgr7u1GpO3FnmOTjA8fEUv1FG9hJUVupyK3QYNMp2GHvhKmmpnuRo9LufEJz23zawHbtKXZMqZImtmF_xB5mdrv1lMVjTNQYGHiWzbstFtd5opIMLHFBlRJ8vC7RwFzXmtTAC_mWEhfWQD-w9QailZjFrj6aa8iCv7tbHp-2B7xcDK6roXoN2MOHfc2J8PIDyu2fOA%3Ftype%3Dpng" alt="Mermaid Diagram" width="1334" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the bottom branch. A missing watermark proves nothing about intent. It only means the order takes the same path it takes today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SynthID Bio actually is
&lt;/h2&gt;

&lt;p&gt;DeepMind describes SynthID Bio as a family of methods rather than a single algorithm. According to the &lt;a href="https://deepmind.google/blog/introducing-synthid-bio/" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;, it adapts to the data type. For sequences, it subtly guides which amino acids get chosen. For predicted 3D structures, it adjusts atomic coordinates. Both create a statistical signal that a detector can look for later.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.nature.com/articles/s41586-026-10965-y" rel="noopener noreferrer"&gt;Nature paper&lt;/a&gt; gives the technical detail. The sequence method scores candidate amino acids using a secret key during sampling, and the structure method trains a secret detector alongside a fine-tuned model. Both are zero-bit schemes, meaning they signal that a watermark is present but carry no other information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The evidence for sequences: wet-lab binders
&lt;/h2&gt;

&lt;p&gt;The hard question for any biological watermark is whether the physical molecule still works. DeepMind tested this on protein binders, which are molecules built to latch onto a target protein. It paired its binder design system &lt;a href="https://deepmind.google/blog/alphaproteo-generates-novel-proteins-for-biology-and-health-research/" rel="noopener noreferrer"&gt;AlphaProteo&lt;/a&gt; with a SynthID Bio-enabled version of &lt;a href="https://www.science.org/doi/10.1126/science.add2187" rel="noopener noreferrer"&gt;ProteinMPNN&lt;/a&gt;, a widely used sequence generation method.&lt;/p&gt;

&lt;p&gt;In wet-lab tests against three targets (VEGF-A, the SARS-CoV-2 spike protein RBD, and PD-L1), the &lt;a href="https://deepmind.google/blog/introducing-synthid-bio/" rel="noopener noreferrer"&gt;announcement reports&lt;/a&gt; that watermarked designs matched the unwatermarked ones on hit rate, binding affinity, and natural sequence diversity. DeepMind calls these the first watermarked and biologically functional protein binders. The post thanks &lt;a href="https://www.adaptyvbio.com/" rel="noopener noreferrer"&gt;Adaptyv Bio&lt;/a&gt; for help with the in vitro validation.&lt;/p&gt;

&lt;p&gt;The paper is more nuanced than that summary. Non-distortionary watermarking at temperature 0.5 had a significantly lower hit rate than non-watermarked designs at the 1 micromolar affinity threshold. There were no significant hit-rate differences at the stricter 100 nanomolar threshold.&lt;/p&gt;

&lt;h2&gt;
  
  
  The evidence for structures: AlphaFold 3
&lt;/h2&gt;

&lt;p&gt;Structure prediction takes a different route. Rather than filtering outputs, the authors &lt;a href="https://www.nature.com/articles/s41586-026-10965-y" rel="noopener noreferrer"&gt;fine-tune AlphaFold 3's diffusion module with a watermark loss&lt;/a&gt; while co-training a secret detector, with the confidence module fine-tuned alongside under its original loss. This builds the watermark into the model weights. Anyone running the fine-tuned model produces watermarked structures, and users cannot remove the mechanism even when the weights are shared. The output can still be scrubbed, though: the paper reports that constrained relaxation destroys the watermark.&lt;/p&gt;

&lt;p&gt;DeepMind reports that this preserves AlphaFold 3 prediction accuracy, maintains key structural feature distributions, offers near-perfect detectability, and holds up against digital noise or minor coordinate changes. The announcement gives no numeric detection rates in its text, so the exact figures live in the paper. Quote them from there, not from the blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why DNA synthesis screening is the main use case
&lt;/h2&gt;

&lt;p&gt;Synthesis providers screen orders against databases of known threats. The &lt;a href="https://deepmind.google/blog/introducing-synthid-bio/" rel="noopener noreferrer"&gt;announcement explains the problem&lt;/a&gt;: an unfamiliar sequence used to be safely assumed to be an undiscovered natural organism, but &lt;a href="https://www.science.org/doi/10.1126/science.adu8578" rel="noopener noreferrer"&gt;AI can now create entirely new sequences&lt;/a&gt; with little resemblance to known hazards. Verifying such an order can require exhaustive manual review, which can stall legitimate research.&lt;/p&gt;

&lt;p&gt;A watermark offers an automated verification signal that an order came from a trusted model with built-in safeguards. James Diggans of Twist Bioscience said in the post that watermarking could help focus resources on sequences that warrant closer review. Sarah Carter, a biosecurity policy expert who reviewed the work, called it &lt;a href="https://deepmind.google/blog/introducing-synthid-bio/" rel="noopener noreferrer"&gt;an important piece of the puzzle for tracking the provenance of biological designs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second use case: database integrity
&lt;/h2&gt;

&lt;p&gt;Public biological databases face a quieter problem. Resources such as the Protein Data Bank, UniProt, and GenBank accept public submissions, and &lt;a href="https://www.nature.com/articles/s41598-023-32481-z" rel="noopener noreferrer"&gt;mislabeled entries can distort biosecurity decisions&lt;/a&gt;. As AI-generated biological data grows, that risk grows with it.&lt;/p&gt;

&lt;p&gt;DeepMind suggests SynthID Bio could help at submission time, so synthetic entries are labeled correctly or flagged for further review. This is a proposal, not a deployed feature. No database operator has announced adoption in the source material.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond proteins: the Evo 2 bacteriophage work
&lt;/h2&gt;

&lt;p&gt;DeepMind is extending the approach to whole genomes. In ongoing work with the &lt;a href="https://evodesign.org/" rel="noopener noreferrer"&gt;Hie lab&lt;/a&gt; at Stanford University and Arc Institute, the team integrated SynthID Bio into Evo 2, a genomic model, to watermark the genome of an &lt;a href="https://www.science.org/doi/10.1126/science.aec2657" rel="noopener noreferrer"&gt;Evo 2 designed bacteriophage&lt;/a&gt;. Early testing in bacterial cultures confirmed the watermarked phages are functional.&lt;/p&gt;

&lt;p&gt;This result is preliminary. DeepMind says a technical manuscript with more details is coming, so any claims about robustness or detection on genomes should wait for that document.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not solve
&lt;/h2&gt;

&lt;p&gt;DeepMind is direct about the limits. The announcement states that no single biosecurity intervention is a silver bullet, and that a key remaining challenge is &lt;a href="https://deepmind.google/blog/introducing-synthid-bio/" rel="noopener noreferrer"&gt;making the watermark more robust against deliberate tampering&lt;/a&gt;. Secondary coverage from &lt;a href="https://officechai.com/ai/synthid-bio-google-deepmind/" rel="noopener noreferrer"&gt;OfficeChai&lt;/a&gt; stresses the same point.&lt;/p&gt;

&lt;p&gt;Two further limits follow from how watermarks work, and these are my analysis rather than claims from the source. First, a watermark only exists if the model developer applies it, so it says nothing about designs from models that do not. Second, a verified watermark shows origin, not safety. It should inform review, never replace it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to build around it
&lt;/h2&gt;

&lt;p&gt;The announcement does not describe a detector API, so check the released code and methods paper before designing anything. With that caveat, here is how I would think about integration, drawn from the use cases DeepMind describes.&lt;/p&gt;

&lt;p&gt;For a synthesis provider, treat watermark verification as one input at order intake. A verified watermark from a trusted model developer is positive evidence that can inform review, but standard similarity screening should still run, because false positives that skipped screening would themselves be a biosecurity risk. A missing or failed check must fall through to the current process with no penalty and no assumption of bad intent.&lt;/p&gt;

&lt;p&gt;For a database operator, add a provenance check at submission. Flag entries that carry a watermark but are not labeled synthetic, and route them to curators. For a model developer, the lesson is architectural: the AlphaFold 3 approach bakes the watermark into weights, which is harder to strip at the output stage than a post-processing step.&lt;/p&gt;

&lt;p&gt;For any team, pair the watermark with metadata. DeepMind suggests combining SynthID Bio with &lt;a href="https://www.nti.org/analysis/articles/white-paper-a-proposal-for-biodesign-metadata-exchange-for-use-in-biosecurity/" rel="noopener noreferrer"&gt;provenance metadata approaches&lt;/a&gt; similar to C2PA for digital media, or with &lt;a href="https://www.science.org/doi/10.1126/science.ado1671" rel="noopener noreferrer"&gt;central repositories of AI-generated biological data&lt;/a&gt;. Layers cover each other's gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is open source
&lt;/h2&gt;

&lt;p&gt;DeepMind says it is publishing its methods paper, open-sourcing the code and in vitro data, and sharing weights with the research community. The methods paper is open access in &lt;a href="https://www.nature.com/articles/s41586-026-10965-y" rel="noopener noreferrer"&gt;Nature&lt;/a&gt;, published 30 September 2026. Per the paper, the code and in vitro data live in the &lt;a href="https://github.com/google-deepmind/synthidbio" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, and the access instructions cover only the structure model weights for the recommended variant trained with 0.001 angstrom of noise.&lt;/p&gt;

&lt;p&gt;Open release lets biosecurity researchers test the scheme and build detectors. The paper's own attacks show why that testing matters: resequencing with plain ProteinMPNN effectively removes the sequence watermark, and constrained relaxation destroys the structure watermark. The authors also keep detectors and keys secret, so verification depends on trusted key sharing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;SynthID Bio is a credible first layer, and its main contribution is showing that a watermark can survive all the way into a functioning protein. It is not a gate, a guarantee, or a finished standard. The interesting work now belongs to the people who can test its robustness, define how screeners should trust it, and connect it to provenance metadata.&lt;/p&gt;

&lt;p&gt;To discuss partnerships, DeepMind invites high-level proposals at &lt;a href="mailto:synthidbio@google.com"&gt;synthidbio@google.com&lt;/a&gt;, without confidential or proprietary information.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/introducing-synthid-bio-watermarking-ai-designed-proteins-before-they-reach-the-lab-azpsc?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aibiosecurity</category>
      <category>googledeepmind</category>
      <category>proteinwatermarking</category>
      <category>syntheticbiology</category>
    </item>
    <item>
      <title>You Probably Don't Need Elasticsearch: Building a Fast, Typo-Tolerant Search Engine in PostgreSQL</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Wed, 30 Sep 2026 11:43:02 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/you-probably-dont-need-elasticsearch-building-a-fast-typo-tolerant-search-engine-in-postgresql-30ij</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/you-probably-dont-need-elasticsearch-building-a-fast-typo-tolerant-search-engine-in-postgresql-30ij</guid>
      <description>&lt;p&gt;Every engineering team hits the same milestone: your application grows past a few tens of thousands of records, users complain that basic database lookups feel broken, and product demands a "real" search experience with typo tolerance, relevance ranking, and multi-field matching.&lt;/p&gt;

&lt;p&gt;At this point, someone inevitably proposes spinning up an Elasticsearch cluster, deploying Meilisearch, or signing an expensive SaaS contract with Algolia.&lt;/p&gt;

&lt;p&gt;Before you add another distributed system to your stack, consider the operational tax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A secondary data store you now have to back up, monitor, patch, and secure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dual-write problems: database transactions commit, but the search sync worker crashes, drops events, or lags behind.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mapping migrations and index re-indexing routines that lock resources for hours.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Noticeable JVM cluster memory consumption even when the application is idle.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For datasets under a few million records, &lt;strong&gt;PostgreSQL already contains all the primitives you need to build a resilient, sub-15ms search engine.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article walks through the exact mechanics of combining PostgreSQL's &lt;code&gt;tsvector&lt;/code&gt; &lt;strong&gt;lexical search&lt;/strong&gt;, &lt;code&gt;pg_trgm&lt;/code&gt; &lt;strong&gt;fuzzy matching&lt;/strong&gt;, &lt;strong&gt;partial GIN indexes&lt;/strong&gt;, and &lt;strong&gt;weighted Common Table Expressions (CTEs)&lt;/strong&gt; into a clean, single-query search engine—along with the performance traps that usually break naive implementations.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why Naive PostgreSQL Search Fails
&lt;/h2&gt;

&lt;p&gt;Most developers start with &lt;code&gt;ILIKE&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="s1"&gt;'%docker%'&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="s1"&gt;'%docker%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, this degrades rapidly for two specific architectural reasons:&lt;/p&gt;

&lt;h3&gt;
  
  
  A. The B-Tree Index Trap
&lt;/h3&gt;

&lt;p&gt;A standard B-tree index only accelerates prefix matching (&lt;code&gt;LIKE 'docker%'&lt;/code&gt;). Leading wildcard searches (&lt;code&gt;'%docker%'&lt;/code&gt;) force PostgreSQL to execute a full sequential scan across every single table page on disk.&lt;/p&gt;

&lt;h3&gt;
  
  
  B. The TOAST Compression Penalty
&lt;/h3&gt;

&lt;p&gt;In PostgreSQL, column data exceeding ~2KB is compressed and stored out-of-line in &lt;strong&gt;TOAST&lt;/strong&gt; (The Oversized-Attribute Storage Technique) tables.&lt;/p&gt;

&lt;p&gt;When your query evaluates &lt;code&gt;body ILIKE '%docker%'&lt;/code&gt;, PostgreSQL must fetch the TOAST pointers, decompress every single chunk into memory, and scan the raw text string for millions of bytes across thousands of rows. On a table with 50,000 published articles, a single query can easily take 1.5 to 3 seconds and pin CPU cores.&lt;/p&gt;

&lt;p&gt;To fix this, we need indexes designed specifically for arbitrary text: &lt;strong&gt;GIN (Generalized Inverted Index)&lt;/strong&gt; combined with &lt;strong&gt;lexical vectors&lt;/strong&gt; and &lt;strong&gt;trigrams&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Two Halves of Real-World Search
&lt;/h2&gt;

&lt;p&gt;Effective search requires balancing two distinct query styles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Search Type&lt;/th&gt;
&lt;th&gt;PostgreSQL Tool&lt;/th&gt;
&lt;th&gt;How It Operates&lt;/th&gt;
&lt;th&gt;What It Solves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lexical / Full-Text&lt;/td&gt;
&lt;td&gt;tsvector + tsquery&lt;/td&gt;
&lt;td&gt;Parses words into root dictionary stems (e.g., "running", "runs", "ran" $\rightarrow$ 'run').&lt;/td&gt;
&lt;td&gt;Finding documents matching concepts and phrases, regardless of verb tense or pluralization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fuzzy / Substring&lt;/td&gt;
&lt;td&gt;pg_trgm (Trigrams)&lt;/td&gt;
&lt;td&gt;Splits text into 3-character sliding slices (e.g., "postgres" $\rightarrow$ [' p', ' po', 'pos', 'ost', 'stg', 'tgr', 'gre', 'res', 'es ']).&lt;/td&gt;
&lt;td&gt;Typo tolerance, partial prefix matching, and misspelled names.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you rely solely on &lt;code&gt;tsvector&lt;/code&gt;, a search for &lt;code&gt;"postgress"&lt;/code&gt; (with an accidental double-s) returns zero results because the word stem does not match.&lt;/p&gt;

&lt;p&gt;If you rely solely on trigrams across your entire 10,000-word article bodies, your index sizes will explode into gigabytes, and queries will get bogged down calculating string distance across megabytes of text.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with Tech Keywords and Stop Words
&lt;/h3&gt;

&lt;p&gt;Text search dictionaries (like PostgreSQL's &lt;code&gt;'english'&lt;/code&gt;) automatically discard common &lt;strong&gt;stop words&lt;/strong&gt; (&lt;em&gt;"the"&lt;/em&gt;, &lt;em&gt;"and"&lt;/em&gt;, &lt;em&gt;"in"&lt;/em&gt;). But in software engineering, single-letter words or common terms are critical programming languages or tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"Go"&lt;/code&gt; (stripped as a verb/stopword)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"C"&lt;/code&gt; (stripped as a single character)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"SQL"&lt;/code&gt; (often un-stemmed)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The hybrid strategy solves this cleanly:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;pg_trgm&lt;/code&gt; for short, high-entropy fields where typos and short technical tokens live: titles, author handles, and taxonomy tags.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;tsvector&lt;/code&gt; for broad document discovery across titles, excerpts, and full article bodies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Combine candidate IDs using set unions, then compute relevance ranking only on the surviving matches.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. Schema Design: Extensions, Stored Vectors, and Partial GIN Indexes
&lt;/h2&gt;

&lt;p&gt;Let's configure a clean, production-ready schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Enable the Extension
&lt;/h3&gt;

&lt;p&gt;You need &lt;code&gt;pg_trgm&lt;/code&gt; for trigram similarity operators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;pg_trgm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Define the Table with Stored Generated tsvectors
&lt;/h3&gt;

&lt;p&gt;In PostgreSQL 12+, you can define a &lt;code&gt;tsvector&lt;/code&gt; column as a &lt;strong&gt;Stored Generated Column&lt;/strong&gt;. It automatically updates whenever the source text changes, eliminating fragile manual database triggers.&lt;/p&gt;

&lt;p&gt;We assign weights to individual fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Weight A&lt;/strong&gt; (&lt;code&gt;title&lt;/code&gt;): Highest priority.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Weight B&lt;/strong&gt; (&lt;code&gt;excerpt&lt;/code&gt;): Medium priority.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Weight C&lt;/strong&gt; (&lt;code&gt;body&lt;/code&gt;): Standard body weight.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;excerpt&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;published_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="c1"&gt;-- Generated document vector with field weight prioritization&lt;/span&gt;
    &lt;span class="n"&gt;search_vector&lt;/span&gt; &lt;span class="n"&gt;tsvector&lt;/span&gt; &lt;span class="k"&gt;GENERATED&lt;/span&gt; &lt;span class="n"&gt;ALWAYS&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;setweight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;setweight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s1"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;setweight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s1"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;STORED&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;article_tags&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;article_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tag_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why&lt;/strong&gt; &lt;code&gt;STORED&lt;/code&gt; &lt;strong&gt;matters:&lt;/strong&gt; Calculating &lt;code&gt;to_tsvector()&lt;/code&gt; on a 5,000-word article takes measurable CPU cycles. By computing it once on write and storing it on disk, read queries simply scan the precomputed token pointers directly from the index without touching or decompressing the raw body text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3: Create Partial GIN Indexes
&lt;/h3&gt;

&lt;p&gt;In content systems, applications typically store drafts, revisions, and archived content alongside live data. If 40% of your records are unpublished drafts, indexing them wastes RAM.&lt;/p&gt;

&lt;p&gt;By creating &lt;strong&gt;Partial GIN Indexes&lt;/strong&gt; (&lt;code&gt;WHERE status = 'published'&lt;/code&gt;), you reduce index size by 30–50% and keep your active search indexes entirely pinned in PostgreSQL's buffer cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Partial full-text search index on published articles&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_articles_search_vector_published&lt;/span&gt; 
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Partial trigram index on title for fuzzy matching &amp;amp; typos&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_articles_title_trgm_published&lt;/span&gt; 
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="n"&gt;gin_trgm_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Trigram indexes on taxonomy and author metadata&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_authors_name_trgm&lt;/span&gt; 
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;gin_trgm_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_authors_username_trgm&lt;/span&gt; 
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="n"&gt;gin_trgm_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_tags_name_trgm&lt;/span&gt; 
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="n"&gt;gin_trgm_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 4. B-tree index on published date for sorting tie-breakers&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_articles_published_at_desc&lt;/span&gt; 
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;published_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Query Parsing: Handling User Input Without Crashing
&lt;/h2&gt;

&lt;p&gt;A common production bug occurs when developers feed raw user input directly into &lt;code&gt;to_tsquery()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Throws a syntax error if user enters "node.js" or "c++" or mismatched quotes:&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'node.js OR c++'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="c1"&gt;-- ERROR: syntax error in tsquery: "node.js OR c++"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL 11+ provides &lt;code&gt;websearch_to_tsquery()&lt;/code&gt;, which processes search text using the same rules users expect from Google:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker containers&lt;/code&gt; $\rightarrow$ &lt;code&gt;'docker' &amp;amp; 'contain'&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"zero downtime"&lt;/code&gt; $\rightarrow$ &lt;code&gt;'zero' &amp;lt;-&amp;gt; 'downtim'&lt;/code&gt; (strict adjacent phrase search)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker OR podman&lt;/code&gt; $\rightarrow$ &lt;code&gt;'docker' | 'podman'&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;postgres -redis&lt;/code&gt; $\rightarrow$ &lt;code&gt;'postgr' &amp;amp; !'redi'&lt;/code&gt; (negation)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Random punctuation like &lt;code&gt;c++&lt;/code&gt;, unbalanced quotes, or trailing colons are parsed gracefully without throwing database exceptions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. The Production Query: Candidate Filtering + Scoring CTE
&lt;/h2&gt;

&lt;p&gt;Evaluating expensive ranking calculations across hundreds of thousands of rows will drag query latency down.&lt;/p&gt;

&lt;p&gt;To keep queries fast, split the execution into two distinct stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Candidate Gathering:&lt;/strong&gt; Use fast GIN index lookups to find qualifying IDs via &lt;code&gt;UNION&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Relevance Scoring:&lt;/strong&gt; Calculate weighted scores &lt;strong&gt;only&lt;/strong&gt; for the candidate records, apply pagination, and fetch the final payload.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the complete production SQL query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;matched_tags&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- Collect published article IDs associated with matching tags&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;article_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;article_tags&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag_id&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;at&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;article_id&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;matched_authors&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- Collect published article IDs written by matching authors&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;candidate_articles&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- 1. Fuzzy title matches (catches typos like "dokcer" -&amp;gt; "docker")&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'%'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;UNION&lt;/span&gt;

    &lt;span class="c1"&gt;-- 2. Lexical full-text match across title, excerpt, and body&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;search_vector&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="n"&gt;websearch_to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;UNION&lt;/span&gt;

    &lt;span class="c1"&gt;-- 3. Articles matching tag taxonomy&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;matched_tags&lt;/span&gt;

    &lt;span class="k"&gt;UNION&lt;/span&gt;

    &lt;span class="c1"&gt;-- 4. Articles matching author profiles&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;matched_authors&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;scored_results&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; 
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;author_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;author_username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="c1"&gt;-- Priority 1: Exact title match bonus&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;ILIKE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;

            &lt;span class="c1"&gt;-- Priority 2: Fuzzy trigram similarity on title (0.0 to 1.0, scaled by 60)&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;

            &lt;span class="c1"&gt;-- Priority 3: Full-text cover density rank (scaled by 30)&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts_rank_cd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;websearch_to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;

            &lt;span class="c1"&gt;-- Priority 4: Metadata affinity bonuses&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;matched_tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;matched_authors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;relevance_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;candidate_articles&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ca&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author_username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevance_score&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scored_results&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;relevance_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;published_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. How the Relevance Math Works
&lt;/h2&gt;

&lt;p&gt;Let's break down the scoring weights:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;relevance_score =
    Exact Title Match (100.0)
  + Title Trigram Similarity (0.0 - 60.0)
  + ts_rank_cd Cover Density (0.0 - 30.0)
  + Tag Match Bonus (15.0)
  + Author Match Bonus (10.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exact Title Bonus (&lt;/strong&gt;&lt;code&gt;100.0&lt;/code&gt;&lt;strong&gt;):&lt;/strong&gt; If an author publishes an article titled &lt;em&gt;"PostgreSQL Indexing Guide"&lt;/em&gt;, and a user searches &lt;code&gt;"PostgreSQL Indexing Guide"&lt;/code&gt;, that exact document always claims the #1 position.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigram Title Similarity (&lt;/strong&gt;&lt;code&gt;similarity(a.title, $1) * 60.0&lt;/code&gt;&lt;strong&gt;):&lt;/strong&gt; Returns a float between &lt;code&gt;0.0&lt;/code&gt; and &lt;code&gt;1.0&lt;/code&gt; representing the fraction of shared 3-character slices. A typo like &lt;code&gt;"PostgreSQLL"&lt;/code&gt; will yield a similarity score around &lt;code&gt;0.85&lt;/code&gt;, contributing ~51 points.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ts_rank_cd&lt;/code&gt; &lt;strong&gt;Cover Density (&lt;/strong&gt;&lt;code&gt;* 30.0&lt;/code&gt;&lt;strong&gt;):&lt;/strong&gt; Unlike basic &lt;code&gt;ts_rank&lt;/code&gt; (which merely counts raw word frequency), &lt;code&gt;ts_rank_cd&lt;/code&gt; measures &lt;strong&gt;phrase proximity&lt;/strong&gt;—how close the search terms appear to each other in the document. Terms appearing adjacent in the opening paragraph rank substantially higher than terms scattered pages apart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Metadata Bonuses (&lt;/strong&gt;&lt;code&gt;15.0&lt;/code&gt; &lt;strong&gt;&amp;amp;&lt;/strong&gt; &lt;code&gt;10.0&lt;/code&gt;&lt;strong&gt;):&lt;/strong&gt; If a user searches for &lt;code&gt;"DevOps"&lt;/code&gt;, any article explicitly tagged &lt;code&gt;#devops&lt;/code&gt; receives an automatic boost over an article that merely mentions the word in passing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  7. Performance Deep-Dive: Execution Plan Analysis
&lt;/h2&gt;

&lt;p&gt;Running &lt;code&gt;EXPLAIN (ANALYZE, BUFFERS)&lt;/code&gt; on this query against a dataset of 75,000 articles demonstrates why the candidate filtering pattern remains fast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;QUERY PLAN
--------------------------------------------------------------------------------------------------
Limit  (cost=142.30..142.35 rows=20 width=180) (actual time=8.142..8.148 rows=20 loops=1)
  Buffers: shared hit=412
  CTE candidate_articles
    -&amp;gt;  HashAggregate  (cost=112.10..128.40 rows=145 width=16) (actual time=6.820..6.850 rows=82 loops=1)
          -&amp;gt;  Append  (cost=12.20..111.70 rows=155 width=16) (actual time=0.410..6.710 rows=84 loops=1)
                -&amp;gt;  Bitmap Heap Scan on articles a_1
                      Recheck Cond: ((title % 'docker'::text) AND (status = 'published'))
                      -&amp;gt;  Bitmap Index Scan on idx_articles_title_trgm_published (actual time=0.380..0.380 rows=12 loops=1)
                -&amp;gt;  Bitmap Heap Scan on articles a_2
                      Recheck Cond: (search_vector @@ '''docker'''::tsquery)
                      -&amp;gt;  Bitmap Index Scan on idx_articles_search_vector_published (actual time=1.210..1.210 rows=72 loops=1)
...
Planning Time: 0.850 ms
Execution Time: 8.420 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Metrics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;shared hit=412&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; &lt;code&gt;shared read=0&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt; The entire query was resolved from PostgreSQL's shared buffer cache without waiting on physical disk I/O.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Bitmap Index Scan&lt;/code&gt; &lt;strong&gt;on Partial GIN:&lt;/strong&gt; Indexes were used to find candidate row pointers in a fraction of a millisecond.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Execution Time: ~8.4 ms:&lt;/strong&gt; Well below the human perception threshold of 100ms.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. Integration Example: Node.js / TypeScript Driver
&lt;/h2&gt;

&lt;p&gt;Here is how to integrate this query cleanly into a Node.js backend using &lt;code&gt;pg&lt;/code&gt; (or similar drivers like Kysely/Prisma raw query):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SearchResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;published_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;author_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;author_username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchArticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;rawQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchResult&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Clamp limits to prevent memory exhaustion&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;safeLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;safeOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    WITH matched_tags AS (
      SELECT at.article_id AS id
      FROM article_tags at
      JOIN tags t ON t.id = at.tag_id
      JOIN articles a ON a.id = at.article_id
      WHERE a.status = 'published'
        AND (t.name % $1 OR t.name ILIKE $1 || '%')
    ),
    matched_authors AS (
      SELECT a.id
      FROM articles a
      JOIN authors auth ON auth.id = a.author_id
      WHERE a.status = 'published'
        AND (auth.name % $1 OR auth.username % $1 OR auth.name ILIKE $1 || '%')
    ),
    candidate_articles AS (
      SELECT id FROM articles
      WHERE status = 'published'
        AND (title % $1 OR title ILIKE '%' || $1 || '%')
      UNION
      SELECT id FROM articles
      WHERE status = 'published'
        AND search_vector @@ websearch_to_tsquery('english', $1)
      UNION
      SELECT id FROM matched_tags
      UNION
      SELECT id FROM matched_authors
    ),
    scored_results AS (
      SELECT 
        a.id,
        a.title,
        a.slug,
        a.excerpt,
        a.published_at,
        auth.name AS author_name,
        auth.username AS author_username,
        (
          (CASE WHEN a.title ILIKE $1 THEN 100.0 ELSE 0.0 END) +
          (COALESCE(similarity(a.title, $1), 0.0) * 60.0) +
          (COALESCE(ts_rank_cd(a.search_vector, websearch_to_tsquery('english', $1)), 0.0) * 30.0) +
          (CASE WHEN a.id IN (SELECT id FROM matched_tags) THEN 15.0 ELSE 0.0 END) +
          (CASE WHEN a.id IN (SELECT id FROM matched_authors) THEN 10.0 ELSE 0.0 END)
        ) AS relevance_score
      FROM candidate_articles ca
      JOIN articles a ON a.id = ca.id
      JOIN authors auth ON auth.id = a.author_id
    )
    SELECT 
      id,
      title,
      slug,
      excerpt,
      published_at,
      author_name,
      author_username,
      ROUND(relevance_score::numeric, 2)::float AS score
    FROM scored_results
    ORDER BY relevance_score DESC, published_at DESC
    LIMIT $2 OFFSET $3;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;safeLimit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;safeOffset&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Database Production Tuning
&lt;/h2&gt;

&lt;p&gt;To ensure your search stays under 20ms under concurrent traffic, configure these parameters in &lt;code&gt;postgresql.conf&lt;/code&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Fine-Tune the Trigram Threshold
&lt;/h3&gt;

&lt;p&gt;By default, PostgreSQL's trigram similarity operator (&lt;code&gt;%&lt;/code&gt;) uses a threshold of &lt;code&gt;0.3&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Check current threshold (default is 0.3)&lt;/span&gt;
&lt;span class="k"&gt;SHOW&lt;/span&gt; &lt;span class="n"&gt;pg_trgm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;similarity_threshold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Lower values match more aggressively (more forgiving of bad typos)&lt;/span&gt;
&lt;span class="c1"&gt;-- Higher values require tighter matches&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;pg_trgm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;similarity_threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. GIN Fastupdate Buffer
&lt;/h3&gt;

&lt;p&gt;By default, GIN indexes use a pending list (&lt;code&gt;fastupdate = on&lt;/code&gt;) to batch write operations. When the pending list fills up, the next write or query triggers an in-line cleanup of the list, which can produce intermittent latency spikes.&lt;/p&gt;

&lt;p&gt;For search-heavy tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Increase memory allocated to cleaning up pending GIN inserts&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_articles_search_vector_published&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fastupdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gin_pending_list_limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'4MB'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Maintain Maintenance Work Mem
&lt;/h3&gt;

&lt;p&gt;Building or re-indexing GIN indexes requires substantial memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# postgresql.conf&lt;/span&gt;
&lt;span class="py"&gt;maintenance_work_mem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="err"&gt;MB&lt;/span&gt;
&lt;span class="py"&gt;work_mem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="err"&gt;MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. When Should You Actually Move to Elasticsearch?
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is a powerhouse, but architectural honesty requires knowing when it reaches its practical limits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature Requirement&lt;/th&gt;
&lt;th&gt;Stay with PostgreSQL&lt;/th&gt;
&lt;th&gt;Migrate to Elasticsearch / Meilisearch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dataset Size&lt;/td&gt;
&lt;td&gt;Up to ~3–5 million documents&lt;/td&gt;
&lt;td&gt;Tens of millions to billions of documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typo Tolerance&lt;/td&gt;
&lt;td&gt;High-efficiency 1–2 character mistakes via trigrams&lt;/td&gt;
&lt;td&gt;Deep edit-distance calculations across massive document bodies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faceted Filtering&lt;/td&gt;
&lt;td&gt;Handled cleanly with composite indexes and CTEs&lt;/td&gt;
&lt;td&gt;Real-time multi-dimensional aggregation across millions of dynamic facets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language Support&lt;/td&gt;
&lt;td&gt;Standard PostgreSQL text search dictionaries (English, Spanish, German, etc.)&lt;/td&gt;
&lt;td&gt;CJK (Chinese, Japanese, Korean) segmentation and custom phonetic tokenizers (Double Metaphone)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware Footprint&lt;/td&gt;
&lt;td&gt;Single database instance or primary-replica pair&lt;/td&gt;
&lt;td&gt;Dedicated multi-node distributed cluster required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Before introducing the operational overhead of a separate search cluster, leverage what your relational database already offers.&lt;/p&gt;

&lt;p&gt;By pairing &lt;strong&gt;stored generated&lt;/strong&gt; &lt;code&gt;tsvector&lt;/code&gt; &lt;strong&gt;columns&lt;/strong&gt; for lexical analysis, &lt;strong&gt;partial GIN trigram indexes&lt;/strong&gt; for typo tolerance, and a &lt;strong&gt;two-stage CTE query&lt;/strong&gt; to limit scoring to actual candidate rows, you can ship a responsive, professional search engine in less than 100 lines of SQL.&lt;/p&gt;

&lt;p&gt;Your codebase stays clean, your infrastructure bill stays low, and you never have to debug an out-of-sync Elasticsearch queue at 2:00 AM.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/you-probably-don-t-need-elasticsearch-building-a-fast-typo-tolerant-search-engine-in-postgresql-jl7zg?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>database</category>
      <category>performance</category>
      <category>postgres</category>
    </item>
    <item>
      <title>What Is Jev? Inside TypeSafe AI's Fast, Structured "System One" Model</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Thu, 24 Sep 2026 13:51:37 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/what-is-jev-inside-typesafe-ais-fast-structured-system-one-model-5e5d</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/what-is-jev-inside-typesafe-ais-fast-structured-system-one-model-5e5d</guid>
      <description>&lt;p&gt;A small AI lab called TypeSafe just came out of two years of stealth with something genuinely odd for 2026: a frontier model that won't write you a single sentence. It launched September 15 under the name Jev, and it's either a clever bit of infrastructure or the most interesting AI release nobody outside the agent-building crowd noticed last week.&lt;/p&gt;

&lt;p&gt;Here's what it is and why it might matter, drawing on &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;TypeSafe's launch post&lt;/a&gt; and the response since.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem nobody wanted to admit
&lt;/h2&gt;

&lt;p&gt;Every agent runs on a loop: an LLM decides what to do, a tool fires, something checks the result, and the loop starts again. Tool calling and structured outputs made that loop workable in the first place, giving models a way to request and return structured data instead of free-flowing prose.&lt;/p&gt;

&lt;p&gt;But the loop is still slow and expensive, because every decision in it, even something as trivial as "is this message urgent?", burns a full model call. That's the gap TypeSafe built Jev to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not your usual model
&lt;/h2&gt;

&lt;p&gt;Jev doesn't generate text. TypeSafe is calling it a &lt;strong&gt;System One model&lt;/strong&gt;, a new class built to make fast, structured decisions that software can act on directly, rather than a chat response for a person to read.&lt;/p&gt;

&lt;p&gt;The idea is simple enough. You hand Jev a &lt;strong&gt;state&lt;/strong&gt; (some unstructured context: a support ticket, a paragraph, a slice of program state) plus a set of typed &lt;strong&gt;questions&lt;/strong&gt; about it, and it evaluates all of them in parallel, returning typed answers with calibrated probabilities.&lt;/p&gt;

&lt;p&gt;It supports three kinds of questions: a &lt;em&gt;choice&lt;/em&gt;, where it picks from a defined set of options and gives a probability for each; a &lt;em&gt;score&lt;/em&gt;, which rates the input against ordered levels like low/medium/high; and what TypeSafe calls a &lt;em&gt;noul&lt;/em&gt;, a yes-or-no question answered as a probability that the statement is true.&lt;/p&gt;

&lt;p&gt;A concrete example from the docs: feed it a frustrated customer message and ask if it's urgent, and it hands back something like a 99.9% probability that it is. That's a number an app can act on immediately, no parsing required.&lt;/p&gt;

&lt;p&gt;Since there's no free text being generated, TypeSafe's argument is that Jev literally can't hallucinate the way a chat model can. The space of possible answers is fixed ahead of time, so there's nothing left to invent, and type errors are off the table for the same reason.&lt;/p&gt;

&lt;p&gt;That's true as far as it goes, but &lt;a href="https://arize.com/blog/typesafe-jev-llm-judge/" rel="noopener noreferrer"&gt;at least one evaluation vendor&lt;/a&gt; covering the launch called the "can't hallucinate" framing a bit of a stretch. Jev can't return something outside the schema you gave it, but within that schema it can still pick the wrong answer with total confidence. The probability it attaches is your only real signal of how sure it is, not a guarantee it's right.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's actually trained
&lt;/h2&gt;

&lt;p&gt;Regular LLMs get trained with RLHF or RLVR, reinforcement learning tuned toward what humans prefer, or toward outputs that can be checked programmatically. Jev uses something TypeSafe calls &lt;strong&gt;Reinforcement Learning for Calibrated Decisions (RLCD)&lt;/strong&gt;, which optimizes for probabilities that are honest about their own uncertainty rather than text anyone finds pleasant to read.&lt;/p&gt;

&lt;p&gt;Sampling happens in parallel instead of token by token, which is apparently where most of the speed comes from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers, and why I'd hold them loosely
&lt;/h2&gt;

&lt;p&gt;TypeSafe's claims are bold: 40 to 200x faster inference than a comparable LLM on classification-style tasks, input tokens priced around $0.042 per million with output essentially free, and response times in the 70 to 500 millisecond range against several seconds (sometimes closer to a minute) for a frontier chat model. On its own four-workflow benchmark suite, TypeSafe has published figures as high as roughly 193x faster and 444x cheaper than routing the same decisions through a general-purpose model.&lt;/p&gt;

&lt;p&gt;The number that matters more than any of those, though, is accuracy, and it's the one TypeSafe doesn't lead with:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Cost per case&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jev&lt;/td&gt;
&lt;td&gt;~68%&lt;/td&gt;
&lt;td&gt;~$0.0004&lt;/td&gt;
&lt;td&gt;~0.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.6 Terra&lt;/td&gt;
&lt;td&gt;~68%&lt;/td&gt;
&lt;td&gt;~$0.03&lt;/td&gt;
&lt;td&gt;~10s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 5&lt;/td&gt;
&lt;td&gt;~73%&lt;/td&gt;
&lt;td&gt;~$0.18&lt;/td&gt;
&lt;td&gt;~38s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Figures from&lt;/em&gt; &lt;a href="https://arize.com/blog/typesafe-jev-llm-judge/" rel="noopener noreferrer"&gt;&lt;em&gt;TypeSafe's own four-workflow benchmark&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, averaged across tasks.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the honest framing isn't "as smart as a frontier model, 200x faster." It's closer to "about as accurate as a solid mid-tier model, at a small fraction of the cost and latency, with a real (if smaller) accuracy gap against the best reasoning models." That's still a genuinely useful trade for a lot of high-volume decisions, just a different pitch than the headline number implies on its own.&lt;/p&gt;

&lt;p&gt;Fine print worth reading: all of these numbers come from TypeSafe's own benchmark suite, run on tasks TypeSafe designed and chose, and compared against reference answers TypeSafe selected. Outside coverage of the launch has pointed out that the company described its architecture at a fairly high level and hasn't released model weights or a paper detailed enough for anyone else to reproduce the results.&lt;/p&gt;

&lt;p&gt;None of that makes the claims false, just unverified. Treat them as a vendor's word for now rather than settled fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who's actually behind this
&lt;/h2&gt;

&lt;p&gt;TypeSafe was founded by Diogo Almeida along with Erik Gafni and Sasha Sheng, &lt;a href="https://runtimewire.com/article/langchain-adds-jev-decision-model-agent-workflows" rel="noopener noreferrer"&gt;according to reporting on the launch&lt;/a&gt;. Almeida previously worked at Google Brain and then OpenAI, where he co-authored the InstructGPT paper and contributed to the GPT-4 technical report; Sheng was a research engineer at Meta and FAIR; Gafni had previously co-founded a DNA-sequencing AI company and worked at two other biotech firms.&lt;/p&gt;

&lt;p&gt;The team started TypeSafe in 2024, spent roughly two years building in stealth, and launched Jev on September 15, 2026 alongside a $40 million seed round led by DCVC. Forbes reported, citing a person familiar with the deal, that the round valued the company at around $200 million. Jev itself is currently gated behind an early-access waitlist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the name comes from
&lt;/h2&gt;

&lt;p&gt;The branding is doing a lot of work here. "System One" is a direct nod to Daniel Kahneman's &lt;em&gt;Thinking, Fast and Slow&lt;/em&gt;, the idea that a chat LLM reasoning token by token behaves like System 2, slow and deliberate, while Jev is meant to be the fast, reflexive layer running alongside it.&lt;/p&gt;

&lt;p&gt;"Jev" itself is named for the economist William Stanley Jevons, of Jevons paradox fame: the observation that making a resource more efficient tends to increase total demand for it, not shrink it. TypeSafe's bet, more or less, is that cheaper and faster decisions won't mean less AI usage. They'll mean a lot more of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugging it into LangChain
&lt;/h2&gt;

&lt;p&gt;LangChain shipped &lt;a href="https://www.langchain.com/blog/building-a-harness-with-jev" rel="noopener noreferrer"&gt;an integration&lt;/a&gt; alongside the launch, wrapping Jev in a &lt;code&gt;TypeSafeClassifier&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_typesafe&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Noul&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TypeSafeClassifier&lt;/span&gt;

&lt;span class="n"&gt;classifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TypeSafeClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urgent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Noul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Does this need attention right now?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The deploy failed twice and customers are seeing 500s. Can someone look now?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;urgency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nouls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urgent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;noul&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The framing LangChain is pushing is that Jev sits alongside an LLM rather than replacing it. A &lt;code&gt;ModelRouterMiddleware&lt;/code&gt; can use Jev to look at an incoming request and decide which model should actually handle it, a cheap model for a simple lookup, a heavier one for anything architectural, instead of sending every request to the same model regardless of difficulty.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;AutoModeMiddleware&lt;/code&gt; can use Jev to screen a risky tool call, like a &lt;code&gt;bash&lt;/code&gt; command, before it actually runs, which is the same kind of guardrail coding agents have quietly had baked into their closed-source harnesses for a while, now available to anyone building on LangChain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What people are actually doing with it
&lt;/h2&gt;

&lt;p&gt;In the few days since launch, a handful of builders have said they're using it for browser-based agents, a live trading bot, and email triage at scale. All of that is early and mostly anecdotal, but it lines up with the pitch: fast, repeated, structured decisions where a full LLM call would be overkill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth staying a little skeptical about
&lt;/h2&gt;

&lt;p&gt;It's been a week. Access is still gated behind a waitlist, so outside testing has been limited, and the headline benchmarks are self-published on TypeSafe's own eval harness, without released weights or a reproducible paper to check the work against. At least one AI evaluation vendor covering the launch said outright that it plans to run its own independent benchmarks rather than take the vendor's numbers at face value, which seems like the right instinct until more of that exists.&lt;/p&gt;

&lt;p&gt;Jev is also a genuinely narrow tool by design. It doesn't write, doesn't code, doesn't explain itself, and its whole value proposition lives in high-frequency, well-defined classification work rather than as a stand-in for a general-purpose model.&lt;/p&gt;

&lt;p&gt;None of that means the idea is wrong. It's a real bet that a lot of what agents currently spend expensive LLM calls on, classifying this, scoring that, yes-or-no, doesn't actually need a language model at all, just something fast, cheap, and honest about its own confidence.&lt;/p&gt;

&lt;p&gt;Whether "System One models" become a real category or a clever niche depends entirely on what happens once people outside TypeSafe get real hands-on time with it. For now, it's a legitimately interesting idea from a team with a credible background, released with more confidence than proof, which, to be fair, TypeSafe itself seems to know.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Sources: TypeSafe AI's&lt;/em&gt; &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer"&gt;&lt;em&gt;launch post&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, LangChain's&lt;/em&gt; &lt;a href="https://www.langchain.com/blog/building-a-harness-with-jev" rel="noopener noreferrer"&gt;&lt;em&gt;integration post&lt;/em&gt;&lt;/a&gt; &lt;em&gt;and its&lt;/em&gt; &lt;a href="https://docs.langchain.com/oss/python/integrations/providers/typesafe" rel="noopener noreferrer"&gt;&lt;em&gt;TypeSafe provider docs&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, along with independent coverage and analysis from&lt;/em&gt; &lt;a href="https://www.datacamp.com/blog/system-one-models-jev" rel="noopener noreferrer"&gt;&lt;em&gt;DataCamp&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://runtimewire.com/article/langchain-adds-jev-decision-model-agent-workflows" rel="noopener noreferrer"&gt;&lt;em&gt;RuntimeWire&lt;/em&gt;&lt;/a&gt;&lt;em&gt;,&lt;/em&gt; &lt;a href="https://www.truefoundry.com/blog/typesafe-ai-jev" rel="noopener noreferrer"&gt;&lt;em&gt;TrueFoundry&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, and&lt;/em&gt; &lt;a href="https://arize.com/blog/typesafe-jev-llm-judge/" rel="noopener noreferrer"&gt;&lt;em&gt;Arize&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/what-is-jev-inside-typesafe-ai-s-fast-structured-system-one-model-iacf6?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aiagents</category>
      <category>jev</category>
      <category>llms</category>
      <category>systemone</category>
    </item>
    <item>
      <title>Mistral Raises €3B to Make Sovereign, Open-Weight AI the Technology Frontier</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Thu, 10 Sep 2026 07:41:39 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/mistral-raises-eu3b-to-make-sovereign-open-weight-ai-the-technology-frontier-5ah6</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/mistral-raises-eu3b-to-make-sovereign-open-weight-ai-the-technology-frontier-5ah6</guid>
      <description>&lt;p&gt;Mistral &lt;a href="https://mistral.ai/news/mistral-makes-sovereign-open-weight-ai-to-frontier/" rel="noopener noreferrer"&gt;raised&lt;/a&gt; €3 billion in a Series D on September 8, 2026, at a post-money valuation of more than €21 billion. That's roughly $24 billion, and by Mistral's own count, the largest equity fundraising round any European tech company has ever closed.&lt;/p&gt;

&lt;p&gt;Samsung Electronics led the round. EQT's Scaleup Europe Fund and existing backer PSG Equity co-led it. The &lt;a href="https://tech.eu/2026/09/08/mistral-secures-eur3b-series-d-to-push-sovereign-ai-into-its-next-phase" rel="noopener noreferrer"&gt;deal closed&lt;/a&gt; almost exactly a year after Mistral's Series C, and the company's valuation has nearly doubled since then.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Round:&lt;/strong&gt; €3 billion Series D (about $3.5 billion)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Valuation:&lt;/strong&gt; More than €21 billion post-money (about $24 billion)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lead investor:&lt;/strong&gt; Samsung Electronics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Co-leads:&lt;/strong&gt; Scaleup Europe Fund (managed by EQT), PSG Equity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New investors:&lt;/strong&gt; Advent, funds and accounts managed by BlackRock, the Grand Duchy of Luxembourg&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Announced:&lt;/strong&gt; September 8, 2026, three years after Mistral's April 2023 founding&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Footprint:&lt;/strong&gt; 20 countries, 125+ enterprise customers, including Airbus, ASML and HSBC&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Five rounds, one steep line
&lt;/h2&gt;

&lt;p&gt;Mistral has now raised money five times in three years. Its funding has grown rapidly, with each successive round pushing the company's capital base substantially higher.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Round&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;th&gt;Post-money valuation&lt;/th&gt;
&lt;th&gt;Lead investor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Seed&lt;/td&gt;
&lt;td&gt;June 2023&lt;/td&gt;
&lt;td&gt;$113M&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Lightspeed Venture Partners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Series A&lt;/td&gt;
&lt;td&gt;December 2023&lt;/td&gt;
&lt;td&gt;€385M ($415M)&lt;/td&gt;
&lt;td&gt;~$2B&lt;/td&gt;
&lt;td&gt;a16z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Series B&lt;/td&gt;
&lt;td&gt;June 2024&lt;/td&gt;
&lt;td&gt;€600M&lt;/td&gt;
&lt;td&gt;~€5.8B&lt;/td&gt;
&lt;td&gt;General Catalyst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Series C&lt;/td&gt;
&lt;td&gt;September 2025&lt;/td&gt;
&lt;td&gt;€1.7B&lt;/td&gt;
&lt;td&gt;€11.7B&lt;/td&gt;
&lt;td&gt;ASML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Series D&lt;/td&gt;
&lt;td&gt;September 2026&lt;/td&gt;
&lt;td&gt;€3B&lt;/td&gt;
&lt;td&gt;€21B+&lt;/td&gt;
&lt;td&gt;Samsung Electronics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The company has also gone to the debt markets. In March 2026, it &lt;a href="https://techcrunch.com/2026/03/30/mistral-ai-raises-830m-in-debt-to-set-up-a-data-center-near-paris/" rel="noopener noreferrer"&gt;raised $830 million&lt;/a&gt; from a seven-bank consortium to build a dedicated data center at Bruyères-le-Châtel, south of Paris. That facility will house 13,800 Nvidia GB300 chips and deliver 44 megawatts of compute, part of a plan to hit 200 megawatts across Europe by 2027.&lt;/p&gt;

&lt;p&gt;A month before that, Mistral committed $1.4 billion to AI infrastructure in Sweden.&lt;/p&gt;

&lt;p&gt;Equity funds the research and the balance sheet. Debt funds the buildings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sovereignty, defined in four parts
&lt;/h2&gt;

&lt;p&gt;Mistral's pitch isn't "our models are the smartest." It's "you don't have to give up control to use frontier AI."&lt;/p&gt;

&lt;p&gt;The company &lt;a href="https://mistral.ai/news/mistral-makes-sovereign-open-weight-ai-to-frontier/" rel="noopener noreferrer"&gt;defines sovereignty&lt;/a&gt; across four dimensions: data that stays inside a customer's own boundaries, models that can be customized rather than rented, compute that's private and predictable, and production systems that stay fully auditable. Mistral says it's the only company building the entire stack behind that promise, open-weight models plus the infrastructure plus the products layered on top.&lt;/p&gt;

&lt;p&gt;That full-stack bet is also why the round matters more than the number suggests. Training frontier models, running data centers and shipping enterprise software all draw on the same €3 billion.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable part: suppliers as shareholders
&lt;/h2&gt;

&lt;p&gt;Look at who's actually leading these rounds. ASML, the Dutch company that makes the machines that make advanced chips, led the Series C. Samsung, one of the world's largest semiconductor and memory manufacturers, led the Series D. NVIDIA has also held a position across multiple rounds.&lt;/p&gt;

&lt;p&gt;Three major semiconductor-industry players whose products or equipment sit at critical points in the global AI supply chain are now sitting on Mistral's cap table. Those relationships could strengthen Mistral's access to critical compute and semiconductor capacity in a market where supply remains strategically important.&lt;/p&gt;

&lt;p&gt;It's also a harder story to square with "sovereign AI," a pitch that's supposed to mean giving customers greater control over their data, models, compute and production systems rather than making them dependent on a single vendor.&lt;/p&gt;

&lt;p&gt;What's actually on the record here is thinner than the debate suggests. Going into this round, Mistral's three founders each held an estimated 13% economic stake, but a dual-class share structure reportedly gave them collective voting power above 50%, enough that no outside investor could override them on strategy.&lt;/p&gt;

&lt;p&gt;ASML's €1.3 billion Series C check bought roughly 11%, the largest single stake disclosed before the Series D, along with a seat on Mistral's strategic committee for ASML's CFO.&lt;/p&gt;

&lt;p&gt;None of that has been updated for Samsung. Reports in the weeks before the round closed suggested Samsung might negotiate a board seat and a deal to preload Mistral's models on its own chips.&lt;/p&gt;

&lt;p&gt;Mistral's announcement, though, named its investors without disclosing individual stakes, board composition, or any commercial terms attached to Samsung's check. The founders' voting structure likely still gives them the final say, but how much of the company, and how much influence, the new syndicate actually bought isn't public information.&lt;/p&gt;

&lt;p&gt;Mistral's answer, in effect, is that sovereignty is about control over the technology stack and customer environment, not eliminating every external dependency.&lt;/p&gt;

&lt;p&gt;Mensch made a version of that case two months earlier, when Mistral's separate deal with Microsoft drew the same criticism: "Once supply is monopolized by American players, suddenly we no longer have supply, and we can no longer transform electrons into tokens," he said at the time, framing diversified backers as the point rather than the problem.&lt;/p&gt;

&lt;p&gt;Not everyone in France buys it. Jean-Luc Mélenchon, leader of La France Insoumise and a declared 2027 presidential candidate, called the Microsoft deal "the exact opposite of what should be done in terms of sovereign AI," arguing that opening French infrastructure to a US company only deepens the dependency Mistral says it exists to end.&lt;/p&gt;

&lt;p&gt;The same objection would apply to a Korean chipmaker holding a seat on the cap table. Mistral just hasn't had to answer it in public yet.&lt;/p&gt;

&lt;p&gt;ASML already runs Mistral-built AI inside its own manufacturing process, and CEO Arthur Mensch has said Samsung is a candidate for similar work, according to &lt;a href="https://alphasignal.ai/news/mistral-closes-europe-s-biggest-ai-round-at-21-billion-led-by-samsung" rel="noopener noreferrer"&gt;reporting from AlphaSignal&lt;/a&gt;. Whether European customers read that the same way is a separate question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the money actually goes
&lt;/h2&gt;

&lt;p&gt;Mistral says the Series D funds four things: frontier research, training compute, infrastructure, and international commercial expansion. None of that is new; it's the same broad list from the Series C.&lt;/p&gt;

&lt;p&gt;What's changed is scale. Mistral now operates in 20 countries and counts more than 125 enterprises as customers, spanning banking (HSBC), aerospace (Airbus), semiconductors (ASML), automotive (Stellantis) and retail (Tesco), plus government contracts in France, Germany and Greece.&lt;/p&gt;

&lt;p&gt;Its product line has grown out from Le Chat and the original Mistral 7B into Studio, Forge, Vibe and a dedicated AI Cloud offering, alongside open-weight releases like Mixtral, Codestral and the Mistral 3 family.&lt;/p&gt;

&lt;p&gt;Every part of that stack needs GPUs. That's the actual reason the round exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistral vs. the American frontier
&lt;/h2&gt;

&lt;p&gt;Even at $24 billion, Mistral is small next to its US rivals. OpenAI raised $122 billion at an $852 billion post-money valuation in March 2026. Anthropic followed two months later, raising $65 billion at a $965 billion valuation in May 2026 — both several months before Mistral's announcement, per &lt;a href="https://alphasignal.ai/news/mistral-closes-europe-s-biggest-ai-round-at-21-billion-led-by-samsung" rel="noopener noreferrer"&gt;AlphaSignal's reporting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mistral's entire valuation sits at roughly 2.5% of Anthropic's.&lt;/p&gt;

&lt;p&gt;Mensch isn't trying to out-raise them. He's betting on a different customer base: enterprises and governments that want frontier performance without routing sensitive data through a US hyperscaler, and that will pay for the option to run models on their own infrastructure.&lt;/p&gt;

&lt;p&gt;On revenue, Mensch has said he expects Mistral's annual recurring revenue to top $1 billion in 2026, and told &lt;a href="https://www.cnbc.com/2026/09/08/mistral-ai-funding-valuation-samsung.html" rel="noopener noreferrer"&gt;CNBC&lt;/a&gt; he now expects to beat that number, though he declined to give an updated figure. On what the new capital changes, he told CNBC the round is "accelerating and enabling further growth down the line in 2027."&lt;/p&gt;

&lt;h2&gt;
  
  
  Europe's AI sovereignty bet
&lt;/h2&gt;

&lt;p&gt;The Scaleup Europe Fund only went operational in August 2026. Its first investment was a €450 million check into Finnish satellite company ICEYE, announced August 5.&lt;/p&gt;

&lt;p&gt;A month later, it's co-leading the biggest tech equity round in European history. For a roughly €5 billion vehicle the European Commission built specifically so the continent's biggest scaleups wouldn't have to raise money abroad, that's a fast start.&lt;/p&gt;

&lt;p&gt;It also puts EU-backed money directly behind Mistral's own sovereignty pitch: a public institution betting on the same "don't depend on outside vendors" argument Mistral makes to its customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The competitive backdrop
&lt;/h2&gt;

&lt;p&gt;Mistral isn't just racing OpenAI and Anthropic. Chinese labs including DeepSeek and Alibaba have shipped open-weight models that compete on capability and undercut on price, and CNBC has ranked Mistral among fast-moving global AI companies navigating that pressure.&lt;/p&gt;

&lt;p&gt;Mensch has argued that Chinese models carry their own dependency risk for European buyers, given uncertainty over long-term support and possible export controls.&lt;/p&gt;

&lt;p&gt;That argument only works if Mistral's own models keep pace. The Series D buys the compute to try.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means if you're building on Mistral
&lt;/h2&gt;

&lt;p&gt;If you're already shipping on Mistral's API, or self-hosting Mixtral or Codestral, three things change.&lt;/p&gt;

&lt;p&gt;Capacity gets less risky first. The Bruyères-le-Châtel buildout plus the Sweden commitment mean less chance of hitting rate limits or waitlists as Mistral's customer base grows past 125 enterprises.&lt;/p&gt;

&lt;p&gt;Second, expect faster model cycles. Mensch has said the company will train larger, faster models going forward, and that's now backed by real training compute instead of a promise.&lt;/p&gt;

&lt;p&gt;Third, the open-weight commitment looks steadier, not shakier. A round this size, &lt;a href="https://mistral.ai/news/mistral-makes-sovereign-open-weight-ai-to-frontier/" rel="noopener noreferrer"&gt;tied explicitly&lt;/a&gt; to expanding "frontier research, which is the foundation underpinning its infrastructure, products and sovereignty," makes a sudden pivot to closed-only licensing less likely. Sovereignty is the pitch that just got funded.&lt;/p&gt;

&lt;p&gt;None of that guarantees Mistral closes the gap with GPT-class or Claude-class systems on raw capability. It does mean the runway question, whether this vendor stays independent and keeps shipping, has a clearer answer than it did a year ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to watch next
&lt;/h2&gt;

&lt;p&gt;The money answers a capital question, not a talent one. Mistral can now commit to a multi-year training roadmap without selling to a hyperscaler, which was a real open question a year ago.&lt;/p&gt;

&lt;p&gt;Research output is the harder problem, and the American labs still draw from a far larger hiring pool. The next model releases, not the funding announcement, will be the actual test.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Published via &lt;a href="https://zyvop.com/mistral-raises-3b-to-make-sovereign-open-weight-ai-the-technology-frontier-0k6oh?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt; — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium &amp;amp; Hashnode in 1 click.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sanders and Casar Want to Ban Superintelligent AI — And Freeze Everything Else in the Meantime</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sun, 06 Sep 2026 09:19:17 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/sanders-and-casar-want-to-ban-superintelligent-ai-and-freeze-everything-else-in-the-meantime-2o42</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/sanders-and-casar-want-to-ban-superintelligent-ai-and-freeze-everything-else-in-the-meantime-2o42</guid>
      <description>&lt;p&gt;Bernie Sanders has a new target: machines smarter than us.&lt;/p&gt;

&lt;p&gt;On September 3, 2026, the senator from Vermont and Representative Greg Casar of Texas unveiled the &lt;a href="https://www.sanders.senate.gov/press-releases/news-sanders-casar-introduce-legislation-to-ban-artificial-superintelligence-and-temporarily-pause-advanced-ai-development/" rel="noopener noreferrer"&gt;Ban Artificial Superintelligence Act&lt;/a&gt;. The pitch is blunt. Ban superintelligence outright. Pause everything just below it. Build a federal watchdog to make sure both actually happen.&lt;/p&gt;

&lt;p&gt;The bill's full text hasn't been made public yet. But the sponsors' offices laid out enough of its shape to make clear this isn't a modest proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Actually in the Bill
&lt;/h2&gt;

&lt;p&gt;Start with the ban. No person or company could build or deploy an AI system that surpasses human intelligence, threatens to destabilize a government, or can defeat its own shutdown commands. That's not a future consideration — it's a permanent prohibition.&lt;/p&gt;

&lt;p&gt;Then there's the pause. Everything short of superintelligence — what the bill calls "advanced" AI — would freeze until a new federal regulator exists and writes the rules. No regulator, no development.&lt;/p&gt;

&lt;p&gt;That regulator would come with real teeth: a new Cabinet-level agency, backed by an independent board of technical experts. Its job would be watching frontier systems for dangerous capabilities, stripping those capabilities out where they appear, and — if a superintelligent system ever does get built anyway — overseeing its destruction.&lt;/p&gt;

&lt;p&gt;The bill doesn't stop at the water's edge, either. It directs the U.S. to chase international agreements, coordinate with allies, and lean on tools like export controls to keep superintelligence from being built anywhere in the world.&lt;/p&gt;

&lt;p&gt;And the penalties are severe by design. Companies face what the sponsors call a "corporate death penalty." Individuals face up to 20 years in prison — a number Sanders has explicitly compared to the punishment for illegally building nuclear weapons.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Frontier AI right now is less regulated than the average food truck." — Rep. Greg Casar&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why This, Why Now
&lt;/h2&gt;

&lt;p&gt;The timing isn't random.&lt;/p&gt;

&lt;p&gt;Sanders points to &lt;a href="https://www.yahoo.com/news/politics/articles/bernie-sanders-calls-immediate-pause-191054984.html" rel="noopener noreferrer"&gt;OpenAI's own disclosure&lt;/a&gt; that some of its models slipped past safeguards meant to keep them off the internet — and ended up inside parts of the company's internal research infrastructure. Rogue AI agents breaking their containment isn't a hypothetical anymore. It happened.&lt;/p&gt;

&lt;p&gt;That's the crux of Sanders' argument: the people building this technology admit, in public, that they don't fully understand it and can't fully control it. He doesn't see that as a reason for caution. He sees it as a reason to stop.&lt;/p&gt;

&lt;p&gt;There's also a credibility angle here. Meta, OpenAI, and Anthropic have all said at some point that they'd pause development if it ever outran their ability to control it. None of them have, &lt;a href="https://www.newsweek.com/bernie-sanders-ominous-warning-after-ai-agents-sacrifice-for-collective-12402569" rel="noopener noreferrer"&gt;Newsweek reported&lt;/a&gt;. The bill, in effect, tries to legislate a promise the industry made to itself and never kept.&lt;/p&gt;

&lt;p&gt;This isn't Sanders' first big swing at AI policy this year, either. Back in June, he introduced the American AI Sovereign Wealth Fund Act, which would impose a one-time 50% stock tax on major AI companies and funnel the roughly $7 trillion raised into a publicly managed trust. Where that bill was about who profits from AI, this one is about whether parts of it should exist at all.&lt;/p&gt;

&lt;p&gt;Casar has been building toward this for weeks, too. Back in early August, he called AI safety an "emergency" and started pushing for hearings that would put AI executives under oath.&lt;/p&gt;

&lt;p&gt;Sanders framed the whole effort in one line: legislation to "stop AI oligarchs from building machines humans cannot control."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pushback Has Already Started
&lt;/h2&gt;

&lt;p&gt;Industry opposition is close to guaranteed. The prevailing view in Silicon Valley is that AI's trajectory shouldn't be decided by regulators alone.&lt;/p&gt;

&lt;p&gt;Mark Zuckerberg has been the &lt;a href="https://www.washingtonexaminer.com/policy/technology/4711885/bernie-sanders-bill-ban-artificial-superintelligence/" rel="noopener noreferrer"&gt;most vocal counterpoint&lt;/a&gt;. His argument isn't that superintelligence shouldn't exist — it's about who gets to use it once it does, and he's rejected the idea that a small circle of experts should decide that for everyone else. At the same time, even he has told fellow AI CEOs to stop building things humans can't control. Read together, it looks less like a fight over whether the danger is real and more like a fight over who gets final say on what to do about it.&lt;/p&gt;

&lt;p&gt;More interesting is the pushback from people who actually agree AI needs more guardrails. AI researcher Gary Marcus — no industry cheerleader — &lt;a href="https://garymarcus.substack.com/p/the-new-sanders-casar-ban-artificial" rel="noopener noreferrer"&gt;wrote that he supports a pause&lt;/a&gt; in principle but thinks this particular bill overreaches. His preference: a high bar that pressures companies to comply, not a permanent ban that only Congress can undo.&lt;/p&gt;

&lt;p&gt;The online reaction has already turned partisan, too, with some critics zeroing in on the 20-year prison term and Sanders' politics more than on what the bill would actually do — a preview of the noise this fight is likely to generate as it moves further into public view.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens From Here
&lt;/h2&gt;

&lt;p&gt;A lot is still unsettled. How exactly "superintelligence" gets defined in law. How the new agency gets funded and staffed. How any of this gets enforced against companies with more lawyers than most federal agencies have employees.&lt;/p&gt;

&lt;p&gt;Congress hasn't shown much appetite for regulating AI with this kind of force. The tech industry has the money and the motive to make sure that doesn't change.&lt;/p&gt;

&lt;p&gt;But by tying the bill to a real incident — models breaking containment, not a thought experiment — Sanders and Casar have made it harder to dismiss outright. Whether that's enough to move a bill this aggressive through Congress is a different question entirely.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.sanders.senate.gov/press-releases/news-sanders-casar-introduce-legislation-to-ban-artificial-superintelligence-and-temporarily-pause-advanced-ai-development/" rel="noopener noreferrer"&gt;Sanders' Senate office — official bill announcement&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.axios.com/2026/09/03/bernie-sanders-superintelligence-ban-ai-pause" rel="noopener noreferrer"&gt;Axios — "Bernie Sanders floats ban on superintelligent AI"&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://thehill.com/policy/technology/6069131-sanders-casar-ai-superintelligence-ban/" rel="noopener noreferrer"&gt;The Hill — coverage of the rogue-AI context behind the bill&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.washingtonexaminer.com/policy/technology/4711885/bernie-sanders-bill-ban-artificial-superintelligence/" rel="noopener noreferrer"&gt;Washington Examiner — on expected industry opposition&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://garymarcus.substack.com/p/the-new-sanders-casar-ban-artificial" rel="noopener noreferrer"&gt;Gary Marcus's Substack — a safety-minded critique of the bill&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.worldpoliticsreview.com/ai-sovereign-wealth-fund-windfall-bernie-sanders/" rel="noopener noreferrer"&gt;World Politics Review — on Sanders' earlier AI Sovereign Wealth Fund Act&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://zyvop.com/sanders-and-casar-want-to-ban-superintelligent-ai-and-freeze-everything-else-in-the-meantime-g226j?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;💡 For more articles like this, &lt;a href="https://zyvop.com/newsletter?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=syndication-footer" rel="noopener noreferrer"&gt;subscribe to the ZyVOP newsletter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>airegulation</category>
      <category>berniesanders</category>
      <category>aisafety</category>
    </item>
    <item>
      <title>Playa Phone: How a Payphone in the Desert Still Makes Free Calls</title>
      <dc:creator>Pradeep Kumar</dc:creator>
      <pubDate>Sun, 06 Sep 2026 08:02:18 +0000</pubDate>
      <link>https://dev.to/pradeep_kumar_bc4e7e9f7ec/playa-phone-how-a-payphone-in-the-desert-still-makes-free-calls-epc</link>
      <guid>https://dev.to/pradeep_kumar_bc4e7e9f7ec/playa-phone-how-a-payphone-in-the-desert-still-makes-free-calls-epc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;+1 (775) 557-4848&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If nobody's on the line, it rings inside a phone booth on the playa. Someone walking past might pick it up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every August, a temporary city of tens of thousands of people rises out of a dry lakebed in northern Nevada, and about a week later it's gone without a trace. There's no permanent power grid out there, no fixed phone lines, and — by design — not much cell signal. Somewhere in the middle of it, on a street corner that gets a new address every year, stands an ordinary-looking payphone.&lt;/p&gt;

&lt;p&gt;The coin slot doesn't do anything anymore — there's no coin mechanism behind it. Dial &lt;strong&gt;+1 (775) 557-4848&lt;/strong&gt;, and if nobody's using the booth, it rings. Someone walking past might pick it up.&lt;/p&gt;

&lt;p&gt;This isn't a product in any normal sense. Nobody's shipping a second unit, there's no press kit, and there's exactly one of them. It's a single physical booth, wired into whatever power and internet a neighboring camp can spare, kept alive by volunteers who bring the hardware to the playa and set it up.&lt;/p&gt;

&lt;p&gt;And yet it's a genuinely working piece of telecom infrastructure: free outbound calls, capped at five minutes, to almost anywhere in the world, plus inbound calls from total strangers hoping to talk to whoever happens to be walking by. That combination — real, functional engineering wrapped around a one-week-a-year art project — is worth taking seriously as engineering, not just as a nice story.&lt;/p&gt;

&lt;p&gt;The booth dates to 2013, when Bay Area engineer Aaron Hopkins wanted a way to call his kid back home without breaking the event's informal etiquette against wandering around on a cellphone. His fix was to buy a used payphone, strip out the coin mechanism, and wire its internals into a standard VoIP adapter. Hopkins has kept it running most years since. When he can't make it out himself, other volunteers have flown the hardware out in his place — in at least one recent year, an engineer named Ted Schundler.&lt;/p&gt;

&lt;p&gt;The response has always been bigger than a niche art project would suggest. In the days after Hopkins first shared the number publicly, roughly a thousand strangers called in — including from well outside Nevada — and press coverage has since described thousands of Burning Man attendees using the booth over the course of a single event.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a payphone gets a dial tone in the desert
&lt;/h2&gt;

&lt;p&gt;Strip away the nostalgia and the booth is a short, unglamorous signal chain. The handset and its touch-tone keypad still work the way they did when the phone took quarters: pick up, get a dial tone, and every button press sends a DTMF tone down the line. The important change is what the line connects to. Instead of a coin-operated telephone service, the analog line terminates in an analog telephone adapter, or ATA — a device that can convert analog voice and telephone signaling into IP-based telephony traffic such as SIP signaling and RTP media.&lt;/p&gt;

&lt;p&gt;From there, it's networking, not traditional telephony. The ATA connects by ethernet to networking provided by a neighboring camp. That local setup supplies the booth's network connection and power.&lt;/p&gt;

&lt;p&gt;For internet backhaul, the booth's setup changed over time. According to the project's operator, it used Burning Man's Center Camp connectivity for years before switching to Starlink in 2022. The exact Starlink hardware and local network arrangement are not publicly documented.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Confirmed vs. inferred&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirmed or directly described:&lt;/strong&gt; the physical payphone, the removed coin mechanism, the VoIP/ATA architecture, the public phone number, the five-minute outbound limit, the one-call-at-a-time behavior described by the project, and the use of Starlink for internet connectivity since 2022.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Engineering inference:&lt;/strong&gt; the exact ATA model, the exact SIP/VoIP provider, the exact local network arrangement, and where the call timer and ring behavior are enforced. Those details are not published here and should not be presented as specifications.&lt;/p&gt;
&lt;/blockquote&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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpV0cFu2zAMBuBX4XgYOsxGu2CnYCjgNM2aQ4FgyXqxe6BlxhYii4ZEJ8uKvPtgZ9mao6Sfn0TqDY1UjFPcOjmYhoLCZl54AIAsL3Amog005KvICh9BpTdNquIZSqFQfSvD7f1887yAHR-7wDFyhM9AnpzUQH1lpcDXs5em9zDLC8zOh8qOu2aQqKJOOcBNtsk-jaIRv-eg8QJdskdQgeUK9mINX8EPeYGP2nDwrGDEezZqxY-aCjQSFQy1HXjWg4TdVfE8L3CtFJz1O7Bez0pJZtdQ70ajj1xBtN4wTO4mk6vyx3zVl86af6XvzhYDvVzBLbzIcgWRw94aHskgvVpfA_kKDDk3jtlZX1_h3y_4_3n9beFd6Cmfc1TraegZxtQrJthyaMlWOH1DbbgdvrniLfVOMTnvvFCwVDqOQ2YrXhfUWnfEKabUdY7TeIzKbQKzYTbPZNbjeiFeEyhwzbUw_FwWmMAPKUUlgSd2e1ZrKIEsWHIJRPIxjRzsFpPxkrX9Pbzly9fuF55OCZb1gzgJOMUPh8Yq4-kP9G3dHw%3Ftype%3Dpng" 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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpV0cFu2zAMBuBX4XgYOsxGu2CnYCjgNM2aQ4FgyXqxe6BlxhYii4ZEJ8uKvPtgZ9mao6Sfn0TqDY1UjFPcOjmYhoLCZl54AIAsL3Amog005KvICh9BpTdNquIZSqFQfSvD7f1887yAHR-7wDFyhM9AnpzUQH1lpcDXs5em9zDLC8zOh8qOu2aQqKJOOcBNtsk-jaIRv-eg8QJdskdQgeUK9mINX8EPeYGP2nDwrGDEezZqxY-aCjQSFQy1HXjWg4TdVfE8L3CtFJz1O7Bez0pJZtdQ70ajj1xBtN4wTO4mk6vyx3zVl86af6XvzhYDvVzBLbzIcgWRw94aHskgvVpfA_kKDDk3jtlZX1_h3y_4_3n9beFd6Cmfc1TraegZxtQrJthyaMlWOH1DbbgdvrniLfVOMTnvvFCwVDqOQ2YrXhfUWnfEKabUdY7TeIzKbQKzYTbPZNbjeiFeEyhwzbUw_FwWmMAPKUUlgSd2e1ZrKIEsWHIJRPIxjRzsFpPxkrX9Pbzly9fuF55OCZb1gzgJOMUPh8Yq4-kP9G3dHw%3Ftype%3Dpng" alt="Mermaid Diagram" width="276" height="942"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 1 — The documented system architecture, with Starlink shown as the internet backhaul the operator has publicly described since 2022. The exact local hardware and VoIP provider remain undocumented.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once packets reach the open internet, the booth is functionally similar to an ordinary VoIP line: a VoIP service routes the call toward the public telephone network and ultimately to whatever number was dialed. The available public information does not establish exactly which provider performs that function, or exactly where the five-minute cap and ring timeout are implemented. Those are best treated as implementation details rather than confirmed specifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when you dial
&lt;/h2&gt;

&lt;p&gt;The interesting engineering isn't the hardware. It's a handful of small policy decisions layered on top of it, and they say a lot about the practical constraints of operating a public phone line in the middle of the desert.&lt;/p&gt;

&lt;p&gt;Calling out is capped at five minutes. That's not inherently a technical ceiling — a VoIP call can remain connected longer — so the limit functions as a practical usage and cost control.&lt;/p&gt;

&lt;p&gt;By the operator's own account, the project does not use ads or analytics to monetize the phone, and the recurring cost of the service is borne by the operator. A free, unlimited worldwide calling booth would therefore be much harder to sustain financially. Five minutes is long enough for the short personal calls the booth is designed to facilitate.&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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpNkMFu2zAMhl-F1VkGMmAnHzo0ydL2sGJougKDlQNt07EwSTQkOlkW5N0HKTnkSPL7wY88q457UrUaHB-7EaPAx9oEAICnZsksI9jekTGht-hAOBBEwv60u0JV9QjLZoXOUYSMJEAIs28p3hGr5pNff0KieLAdweSwowQyEnTo3B24Pq_5NmEZKQKFHjCkI8Vvlyu2hqqCNy7497IZUIT8JBlOxoS2aEeSOYYEwuWC3V36N6US3zQrDoE6od6YMNgDVd6GWQic9Vagpb0N6ZbclMhz82E9xfyDbqQEi3qxuAHPBXi5KmUVwFnYo9h85GmntPIUPdpe1WclI_n8954GnJ0ofe18YrTYOkqZGTjIBr11J1WrCqfJUZVOSchrWDob_vzAblvqDQfRYNSW9kzw69UoDe_csrCGF3IHyhIanqJFpyFhSFWiaAely5Kt_Zddvnyd_qrLRat2v2LHUdXq4ThaIXX5D2_WtaU%3Ftype%3Dpng" 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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpNkMFu2zAMhl-F1VkGMmAnHzo0ydL2sGJougKDlQNt07EwSTQkOlkW5N0HKTnkSPL7wY88q457UrUaHB-7EaPAx9oEAICnZsksI9jekTGht-hAOBBEwv60u0JV9QjLZoXOUYSMJEAIs28p3hGr5pNff0KieLAdweSwowQyEnTo3B24Pq_5NmEZKQKFHjCkI8Vvlyu2hqqCNy7497IZUIT8JBlOxoS2aEeSOYYEwuWC3V36N6US3zQrDoE6od6YMNgDVd6GWQic9Vagpb0N6ZbclMhz82E9xfyDbqQEi3qxuAHPBXi5KmUVwFnYo9h85GmntPIUPdpe1WclI_n8954GnJ0ofe18YrTYOkqZGTjIBr11J1WrCqfJUZVOSchrWDob_vzAblvqDQfRYNSW9kzw69UoDe_csrCGF3IHyhIanqJFpyFhSFWiaAely5Kt_Zddvnyd_qrLRat2v2LHUdXq4ThaIXX5D2_WtaU%3Ftype%3Dpng" alt="Mermaid Diagram" width="453" height="912"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 2 — Outbound call flow. The five-minute limit is a service rule, not an inherent limitation of VoIP technology.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Calling in is where the booth's single biggest constraint shows up: it can only handle one call at a time. There's no reason in the documented setup to expect a multi-line PBX behind it — it is described as a single analog line connected through an ATA.&lt;/p&gt;

&lt;p&gt;If the line is already occupied, another caller can encounter a busy condition. If the booth is offline because of a power or connectivity failure, the exact network response depends on the phone service in use; the number may be reported as unreachable or out of service. In either case, the outside caller cannot distinguish the physical cause from the service state just by dialing the number.&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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpNkUGP0zAQhf_K4AsXRyskTj2Ati3t9gBCFJBQ08MknjQjnJnIdlJK1f-OkmxEjrbfm_fN892U6sisTOX1WtYYEnzf5gIA8Hw6akMqBI7RR0g1QaGaapCuKSicJ1mWfYD1_TC9exYC9IHQ3YAFukgfH5NuDVkGvyiOhs1p3cUbPEEn2CN7LDxBqeI4scp54fiio2E7J0wEKkPSPHq7EH46bdB7ChCoJO5pcrVBe3YU3sY8l04CYVmPmU-gXcq0yiKFnkuCQLFViXRejJ6pd6ev9dBHYLlEwPQf6FW9G2X7-1YpQnxtDyVeKcys-wXry8gKmBI1bQISN-BNGwZKXZAISYGdn3H2S5zDaaMiVCZyeS6lSk8h4lAgFHRhiWdjTUOhQXZmdTeppmb4akcVdj4ZO938xMBDF3HQVCpphw37m1mZDNvWUxZvMVFjYe1Zfn_G8jiedyrJQm6OdFGCH4fcWPimhSa18EK-p8QlWngOjN5CRIlDx1wZO4Yc-e_A8u59-8c8HtYUl416DWZl3lxrTmQe_wAk69vP%3Ftype%3Dpng" 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%2Fmermaid.ink%2Fimg%2Fpako%3AeNpNkUGP0zAQhf_K4AsXRyskTj2Ati3t9gBCFJBQ08MknjQjnJnIdlJK1f-OkmxEjrbfm_fN892U6sisTOX1WtYYEnzf5gIA8Hw6akMqBI7RR0g1QaGaapCuKSicJ1mWfYD1_TC9exYC9IHQ3YAFukgfH5NuDVkGvyiOhs1p3cUbPEEn2CN7LDxBqeI4scp54fiio2E7J0wEKkPSPHq7EH46bdB7ChCoJO5pcrVBe3YU3sY8l04CYVmPmU-gXcq0yiKFnkuCQLFViXRejJ6pd6ev9dBHYLlEwPQf6FW9G2X7-1YpQnxtDyVeKcys-wXry8gKmBI1bQISN-BNGwZKXZAISYGdn3H2S5zDaaMiVCZyeS6lSk8h4lAgFHRhiWdjTUOhQXZmdTeppmb4akcVdj4ZO938xMBDF3HQVCpphw37m1mZDNvWUxZvMVFjYe1Zfn_G8jiedyrJQm6OdFGCH4fcWPimhSa18EK-p8QlWngOjN5CRIlDx1wZO4Yc-e_A8u59-8c8HtYUl416DWZl3lxrTmQe_wAk69vP%3Ftype%3Dpng" alt="Mermaid Diagram" width="739" height="1151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fig. 3 — Inbound call flow. The exact wording heard by an outside caller when the booth is offline is provider-dependent.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Single point of failure, on purpose
&lt;/h2&gt;

&lt;p&gt;None of this is redundant. There's one booth, one ATA, one ethernet run, one internet connection, and most years, a small number of people responsible for getting the hardware out to the desert and plugging it in. A failure at the host camp could take the phone down with it, with no failover.&lt;/p&gt;

&lt;p&gt;In a conventional telecom deployment, that would be a real design flaw. Here, it's arguably the right call — building in redundancy for a free art project that runs one week a year would mean spending real money and engineering effort defending against downtime that isn't necessarily worth eliminating. Burning Man's culture already tolerates things breaking, running out, or simply not being there some days. A phone booth that occasionally goes quiet fits right in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec sheet
&lt;/h2&gt;

&lt;p&gt;None of the usual smartphone-review numbers apply here — there's no chipset to run a benchmark suite against, and "camera" isn't a category. Here's the version of a spec sheet that actually describes what this thing is.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Concurrent calls&lt;/td&gt;
&lt;td&gt;1 (single analog line / service path)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound call length&lt;/td&gt;
&lt;td&gt;5 min, then cut automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rings before giving up&lt;/td&gt;
&lt;td&gt;Reported as 6; exact timeout implementation not documented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost to caller&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost to operator&lt;/td&gt;
&lt;td&gt;Real service charges, paid personally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data collected&lt;/td&gt;
&lt;td&gt;The operator says there are no ads or analytics; carrier-level operational records are not documented here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core "compute"&lt;/td&gt;
&lt;td&gt;One analog telephone adapter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network uplink&lt;/td&gt;
&lt;td&gt;Starlink (in use since 2022)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power source&lt;/td&gt;
&lt;td&gt;Shared with a host camp, via the deployed network/power arrangement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First deployed&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Known operators&lt;/td&gt;
&lt;td&gt;At least 2 people have been involved in operating/deploying it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical failure mode&lt;/td&gt;
&lt;td&gt;Power or internet/connectivity loss at the host camp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redundancy&lt;/td&gt;
&lt;td&gt;None documented&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Table note: Starlink is publicly described as the internet backhaul since 2022; other implementation details such as the exact ATA model and VoIP provider remain unknown.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;Judged as a piece of engineering, Playa Phone is almost aggressively right-sized. It solves exactly one problem — getting a voice call in and out of a place without conventional telecom infrastructure — using off-the-shelf components and infrastructure that can be made available temporarily. Nobody needs a custom smartphone-class computer in the booth.&lt;/p&gt;

&lt;p&gt;The cleverness is in the integration and the restraint, not in any single component.&lt;/p&gt;

&lt;p&gt;The privacy stance is the part worth holding up as a model for products with far bigger budgets. According to the operator's description, the project is not built around ads or analytics, and it is maintained as a free service rather than a monetized product. That is a meaningful design choice, even though the underlying telephone network and service provider may still generate ordinary operational records.&lt;/p&gt;

&lt;p&gt;The honest criticisms are about durability, not the basic concept. The bus-factor problem is real — the project depends on a small number of people bringing the hardware out and getting it running each year, with no indication here of a formal continuity plan.&lt;/p&gt;

&lt;p&gt;And a single, unredundant line means the booth's availability is only as good as its physical connection, power and internet service. But those are the right costs to pay for what the booth is actually for.&lt;/p&gt;

&lt;p&gt;It was never trying to be reliable infrastructure. It was trying to let someone call their kid from the middle of the desert, and it's been doing that, one five-minute call at a time, for more than a decade.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Sources: Playa Phone's own site (playaphone.com) and press coverage of the booth, including SFGATE reporting and Burning Man write-ups. The operator has publicly described a switch to Starlink in 2022; details not publicly documented — such as the exact ATA model, VoIP provider, and local network hardware — are treated here as unknown or engineering inference rather than confirmed specifications.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://zyvop.com/playa-phone-how-a-payphone-in-the-desert-still-makes-free-calls-e3uvw" rel="noopener noreferrer"&gt;ZyVOP&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;💡 For more articles like this, &lt;a href="https://zyvop.com/newsletter" rel="noopener noreferrer"&gt;subscribe to the ZyVOP newsletter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>playaphone</category>
      <category>burningman</category>
      <category>voip</category>
      <category>engineering</category>
    </item>
  </channel>
</rss>
