<?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: Nathan Maine</title>
    <description>The latest articles on DEV Community by Nathan Maine (@dentity007).</description>
    <link>https://dev.to/dentity007</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3858306%2Fe600f034-eccc-4bb7-a775-189eb2753fe8.png</url>
      <title>DEV Community: Nathan Maine</title>
      <link>https://dev.to/dentity007</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dentity007"/>
    <language>en</language>
    <item>
      <title>You Can't Verify Intent. Can You Verify Output?</title>
      <dc:creator>Nathan Maine</dc:creator>
      <pubDate>Thu, 09 Apr 2026 20:01:07 +0000</pubDate>
      <link>https://dev.to/dentity007/you-cant-verify-intent-can-you-verify-output-37l9</link>
      <guid>https://dev.to/dentity007/you-cant-verify-intent-can-you-verify-output-37l9</guid>
      <description>&lt;h2&gt;
  
  
  The Zero Trust Paradox at the Frontier of Autonomous AI Agents
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;By Nathan Maine&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;There's a question I can't stop thinking about. It sits at the intersection of two ideas that the AI industry treats as compatible but aren't.&lt;/p&gt;

&lt;p&gt;The first idea is zero trust architecture. Every action is verified explicitly. No implicit trust. No assumption that because a system was authorized to do something five minutes ago, it's still authorized now. This is the foundation of modern enterprise security and it works well for systems that behave predictably.&lt;/p&gt;

&lt;p&gt;The second idea is Level 3 autonomous AI agents. These are systems that explore their environment freely - browsing the web, reading emails, querying databases, executing multi-step plans, running for hours or days without human intervention. They don't follow a predetermined path. They decide their own path at runtime based on what they encounter. And increasingly, they write and execute their own code.&lt;/p&gt;

&lt;p&gt;Here's the paradox: zero trust demands that you verify every action explicitly. But how do you explicitly verify the intent of a system that is constantly rewriting its own internal logic?&lt;/p&gt;

&lt;p&gt;I don't think anyone has a complete answer yet. But I think we're asking the wrong question, and I want to walk through why.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Works: Levels 0 Through 2
&lt;/h2&gt;

&lt;p&gt;Before we get to the hard part, it's worth acknowledging that the AI security community has built real solutions for simpler agent architectures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 0&lt;/strong&gt; is a single inference call. User asks a question, model answers. The security model here is straightforward: scan the input, scan the output, block anything malicious. Tools like NVIDIA's garak vulnerability scanner do this well. I contribute adversarial probes to garak - one tests whether models fabricate regulatory citations when asked compliance questions (PR 1658), another tests whether attackers can bypass safety filters using Unicode character substitution (PR 1660). At Level 0, these probes catch the failures before deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 1&lt;/strong&gt; is a chain of deterministic tool calls. The agent follows a predetermined sequence: retrieve data, process it, format the output. The security model adds dataflow tracing - you can manually map every possible path and block untrusted data from reaching sensitive tools. It's tedious but tractable because the paths are enumerable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 2&lt;/strong&gt; introduces weak autonomy. The agent chooses which tools to call based on context. Now you need runtime guardrails (like NeMo Guardrails filtering input and output in real time), sandboxing (like NVIDIA's OpenShell isolating each agent at the kernel level using Linux Landlock), and manual approval gates for sensitive actions. The attack surface is larger but still bounded because the agent's autonomy is constrained.&lt;/p&gt;

&lt;p&gt;These are real, shipping solutions. They work. The industry should be proud of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Everything Breaks: Level 3
&lt;/h2&gt;

&lt;p&gt;Level 3 is where the security model collapses.&lt;/p&gt;

&lt;p&gt;A fully autonomous agent doesn't follow a predetermined path. It explores. It reads a document, decides it needs more context, searches the web, finds a relevant page, summarizes it, realizes the summary contradicts the original document, queries a database to resolve the contradiction, writes a script to analyze the results, executes the script, and uses the output to update its plan. All without a human in the loop.&lt;/p&gt;

&lt;p&gt;The threat vector at Level 3 is no longer the user. It's the environment. A compromised web page. A poisoned database entry. A malicious instruction embedded in white text on a PDF that the agent reads as a system command. The agent didn't start malicious. The environment made it malicious, mid-session, through data it ingested autonomously.&lt;/p&gt;

&lt;p&gt;The standard defense for this is taint tracing - tagging every piece of data from an untrusted source as "tainted" and blocking any tainted data from reaching high-privilege tools. In theory, this works. In practice, it creates a cascading problem.&lt;/p&gt;

&lt;p&gt;When a Level 3 agent enters a reasoning loop - which it will, because that's the whole point of autonomy - every piece of data it processes after touching a tainted source becomes tainted itself. The agent summarizes a tainted web page. The summary is now tainted. The agent uses that summary to formulate a new query. The query is tainted. The query returns results that get incorporated into the agent's reasoning. All tainted. Within minutes, the entire context window is what I'd call "permanently pink."&lt;/p&gt;

&lt;p&gt;If you enforce strict taint tracing policies at this point, you trigger a denial of service against your own application. The policy engine flags every subsequent tool call. The human operator drowns in approval requests. The agent's autonomy collapses back to Level 0. You've spent millions of dollars building a system that's functionally equivalent to a chatbot with extra steps.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Deeper Problem: Agents Building Agents
&lt;/h2&gt;

&lt;p&gt;It gets worse. Jensen Huang described this at GTC 2026 as the next industrial revolution in knowledge work - employees "supercharged by teams of frontier, specialized, and custom-built agents they deploy and manage." But the current trajectory isn't just autonomous agents executing tasks. It's autonomous agents writing and deploying code for other autonomous agents to execute. The orchestrator agent identifies a problem, spins up a temporary worker agent in a sandboxed environment, feeds it data, evaluates the output, and terminates the worker when the job is done.&lt;/p&gt;

&lt;p&gt;In this architecture, the fundamental boundary between code and data dissolves. The prompt IS the code. The generated code IS the data for the next agent. A malicious instruction injected into one agent's data stream becomes executable code in the next agent's runtime. Traditional security assumes you can distinguish between what the system is told to do (code) and what the system processes (data). When agents write code for other agents, that distinction ceases to exist.&lt;/p&gt;

&lt;p&gt;Zero trust says: verify explicitly. But verify WHAT? The agent's intent changes with every reasoning step. Its logic rewrites itself continuously. The verification target is a moving target that moves faster than any verification system can evaluate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Wrong Question and the Right One
&lt;/h2&gt;

&lt;p&gt;I spent months trying to figure out how to verify the intent of a self-modifying autonomous system. I couldn't. And I eventually realized I was asking the wrong question.&lt;/p&gt;

&lt;p&gt;You can't verify intent. Intent in a Level 3 system is non-deterministic by definition. The agent's "intent" is an emergent property of its current context window, its model weights, the data it has ingested, and the tools available to it. It changes with every token generated. Trying to verify it is like trying to verify the intent of weather. You can observe it. You can model it. You can't verify it.&lt;/p&gt;

&lt;p&gt;But you can verify output.&lt;/p&gt;

&lt;p&gt;The agent produces something. A recommendation. A generated document. A code commit. An API call. Whatever it produces, it produces specific bytes. And those bytes can be cryptographically attested before they leave the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  An Output-Centric Framework
&lt;/h2&gt;

&lt;p&gt;I've been building toward a framework that shifts the security question from "did the agent mean well?" to "can we prove what the agent actually produced?"&lt;/p&gt;

&lt;p&gt;The approach has three layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Canonical byte-binding.&lt;/strong&gt; When the agent generates output, the exact bytes are canonicalized to a deterministic sequence and bound to a cryptographic signature chain before the output leaves the system. Any modification downstream - whether by a compromised intermediary, a network man-in-the-middle, or a post-processing step that introduces errors - is detectable because the signature no longer matches the canonical form. You can prove to an auditor that the output the user received is byte-for-byte identical to what the model produced. I have a patent pending on this approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Tamper-evident audit trails.&lt;/strong&gt; Every step of the agent's execution is logged in an append-only chain where each entry is cryptographically linked to the previous one. This isn't standard logging - standard logs can be modified by anyone with admin access. A cryptographically linked chain means even the system administrator can't alter a historical entry without breaking the hash chain. For regulated industries deploying autonomous agents (healthcare under HIPAA, defense under CMMC, finance under SOX), this level of auditability will eventually be table stakes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3: Steganographic channel prevention.&lt;/strong&gt; Even if the output passes through guardrails and sandbox restrictions, data can be exfiltrated through the authorized output channel itself. An agent can embed hidden information in Unicode characters that look identical to humans but carry different byte values - a Latin "a" swapped for a Cyrillic "a" passes visual inspection but encodes a different signal. Canonicalizing the output to strip these channels before attestation closes this exfiltration vector.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Doesn't Solve
&lt;/h2&gt;

&lt;p&gt;I want to be clear about the limitations.&lt;/p&gt;

&lt;p&gt;This framework does not solve the intent verification problem. Nothing does. If a Level 3 agent decides to pursue a harmful goal through a series of individually legitimate-looking actions, output attestation won't catch the strategic intent. It will only prove that each individual output was faithfully recorded and unmodified.&lt;/p&gt;

&lt;p&gt;It also doesn't replace the existing security stack. You still need garak for pre-deployment vulnerability scanning. You still need NeMo Guardrails for runtime input/output filtering. You still need OpenShell for kernel-level sandboxing. You still need taint tracing for data provenance. Output attestation is not a replacement for any of these. It's the layer that sits on top - the proof layer that tells a regulated customer: "We can't guarantee the agent was right. But we can prove exactly what it said, when it said it, and that the record hasn't been altered."&lt;/p&gt;

&lt;p&gt;For healthcare systems deploying autonomous agents to interact with patient data, for defense contractors running agents that process classified information, for financial institutions using agents to make trading decisions - that proof layer is the difference between "we trust the AI" and "we can demonstrate to an auditor exactly what the AI did." The first is a policy statement. The second is a compliance posture.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Question That Remains
&lt;/h2&gt;

&lt;p&gt;If zero trust and Level 3 autonomy are fundamentally incompatible - and I believe they are - then the industry needs to decide what replaces explicit intent verification for self-modifying systems.&lt;/p&gt;

&lt;p&gt;My bet is on output-centric attestation. Verify what the agent produced, not what it intended. Build the cryptographic proof chain that lets regulated industries deploy autonomous agents with auditable evidence trails.&lt;/p&gt;

&lt;p&gt;But this is an open problem. The agents are getting more autonomous faster than the security frameworks are adapting. And the moment agents start building other agents - which is already happening - the verification challenge compounds exponentially.&lt;/p&gt;

&lt;p&gt;I'd love to hear how others are thinking about this. Especially if you're working on the infrastructure side at companies building these systems. The solutions will come from practitioners who are living with these constraints daily, not from theoretical frameworks written in isolation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Nathan Maine is a Technical Program Manager and AI practitioner. He contributes adversarial probes to NVIDIA's garak LLM vulnerability scanner, has trained 13 LLMs across 7 base architectures, and holds 6 pending patents on AI egress security and cryptographic attestation. He publishes models and research on HuggingFace.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Connect: &lt;a href="https://linkedin.com/in/nathanmaine" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; | &lt;a href="https://github.com/NathanMaine" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; | &lt;a href="https://huggingface.co/Nathan-Maine" rel="noopener noreferrer"&gt;HuggingFace&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agentic</category>
      <category>llm</category>
      <category>llmbuilder</category>
    </item>
    <item>
      <title>Gemma 4 After 24 Hours: What the Community Found vs What Google Promised</title>
      <dc:creator>Nathan Maine</dc:creator>
      <pubDate>Fri, 03 Apr 2026 02:31:45 +0000</pubDate>
      <link>https://dev.to/dentity007/-gemma-4-after-24-hours-what-the-community-found-vs-what-google-promised-3a2f</link>
      <guid>https://dev.to/dentity007/-gemma-4-after-24-hours-what-the-community-found-vs-what-google-promised-3a2f</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2F1o740cx5wp5k082njqhi.jpeg" 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.amazonaws.com%2Fuploads%2Farticles%2F1o740cx5wp5k082njqhi.jpeg" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google released Gemma 4 yesterday under Apache 2.0. The benchmarks looked incredible. The community went to work. Here's what we're actually seeing.&lt;/p&gt;

&lt;p&gt;I spent the last 24 hours reading through forums, running my own fine-tuning experiments, and collecting reports from dozens of early adopters. This is a summary of the real-world findings, the open questions, and where I think this model family lands.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Good News First
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Apache 2.0 is a big deal.&lt;/strong&gt; Previous Gemma releases used a custom Google license that technically allowed them to restrict usage. Apache 2.0 removes that uncertainty entirely. For anyone building commercial products on open models, this matters more than any benchmark number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multilingual quality is genuinely strong.&lt;/strong&gt; Users testing German, Arabic, Vietnamese, and French are reporting that Gemma 4 outperforms Qwen 3.5 in non-English tasks. One user called it "in a tier of its own" for translation. Another said it "makes translategemma feel outdated instantly." For global enterprise deployments, this is a significant differentiator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ELO score tells a different story than benchmarks.&lt;/strong&gt; The 31B model scored 2150 on LMArena, which puts it above GPT-OSS-120B and comparable to GPT-5-mini. But side-by-side benchmark tables show it roughly tying with Qwen 3.5 27B. The gap between ELO (human preference) and automated benchmarks suggests Gemma 4 produces responses that humans prefer even when raw accuracy is similar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The E2B model is absurd.&lt;/strong&gt; Multiple users confirmed that the 2.3B effective parameter model beats Gemma 3 27B on most benchmarks. A user running it on a basic i7 laptop with 32GB RAM reported it was "not only faster, it gives significantly better answers" than Qwen 3.5 4B for finance analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problems Nobody Warned About
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Inference Speed
&lt;/h3&gt;

&lt;p&gt;This is the elephant in the room. Multiple users are reporting that Gemma 4's MoE model (26B-A4B) runs significantly slower than Qwen 3.5's equivalent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One user: &lt;strong&gt;11 tokens/sec on Gemma 4 26B-A4B vs 60+ tokens/sec on Qwen 3.5 35B-A3B&lt;/strong&gt; on the same 5060 Ti 16GB&lt;/li&gt;
&lt;li&gt;Another confirmed higher VRAM usage for context at the same quantization level&lt;/li&gt;
&lt;li&gt;Someone running on a DGX Spark asked "why is it super slow?" with no clear answer yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the dense 31B model, users are reporting 18-25 tokens/sec on dual NVIDIA GPUs (5070 Ti + 5060 Ti), which is reasonable but not fast.&lt;/p&gt;

&lt;p&gt;The speed gap against Qwen 3.5 is concerning for production deployments where latency matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  VRAM Consumption
&lt;/h3&gt;

&lt;p&gt;Gemma models have historically been VRAM-hungry for context, and Gemma 4 appears to continue this pattern. One user noted they could only fit Gemma 3 27B Q4 with 20K context on a 5090, while Qwen 3.5 27B Q4 fit with 190K context on the same card.&lt;/p&gt;

&lt;p&gt;For the 256K context window to be useful in practice, you need significantly more VRAM than competing models at the same parameter count.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-Tuning Compatibility
&lt;/h3&gt;

&lt;p&gt;As someone who attempted QLoRA fine-tuning within hours of release, I can confirm the tooling is not ready. Three issues hit immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HuggingFace Transformers&lt;/strong&gt; didn't recognize the &lt;code&gt;gemma4&lt;/code&gt; architecture (required installing from source)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PEFT&lt;/strong&gt; couldn't handle &lt;code&gt;Gemma4ClippableLinear&lt;/code&gt;, a new layer type in the vision encoder (required a monkey-patch)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A new &lt;code&gt;mm_token_type_ids&lt;/code&gt; field&lt;/strong&gt; is required during training even for text-only data (required a custom data collator)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've filed issues on both huggingface/peft and huggingface/transformers. Both received responses within hours, and a fix for the &lt;code&gt;mm_token_type_ids&lt;/code&gt; issue is already in progress. Unsloth also has day-one support if you prefer that path.&lt;/p&gt;

&lt;p&gt;The community question "how easy is it to fine-tune compared to Gemma 3?" currently has no good answer beyond "harder, but solvable."&lt;/p&gt;

&lt;h3&gt;
  
  
  Stability Questions
&lt;/h3&gt;

&lt;p&gt;One user testing the non-quantized 31B in Google AI Studio reported "infinite loops and no possibility to read text from the image." Another found that the model jailbreaks with basic system prompts. A third reported Mac hard crashes when loading either the 31B or 26B in LM Studio.&lt;/p&gt;

&lt;p&gt;These are early reports and may be resolved with updates, but they're worth noting for anyone considering production deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Benchmark Reality
&lt;/h2&gt;

&lt;p&gt;The community quickly assembled side-by-side comparisons. Here's the consolidated picture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Gemma 4 31B&lt;/th&gt;
&lt;th&gt;Qwen 3.5 27B&lt;/th&gt;
&lt;th&gt;Winner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MMLU-Pro&lt;/td&gt;
&lt;td&gt;85.2%&lt;/td&gt;
&lt;td&gt;86.1%&lt;/td&gt;
&lt;td&gt;Qwen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPQA Diamond&lt;/td&gt;
&lt;td&gt;84.3%&lt;/td&gt;
&lt;td&gt;85.5%&lt;/td&gt;
&lt;td&gt;Qwen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LiveCodeBench v6&lt;/td&gt;
&lt;td&gt;80.0%&lt;/td&gt;
&lt;td&gt;80.7%&lt;/td&gt;
&lt;td&gt;Tie&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codeforces ELO&lt;/td&gt;
&lt;td&gt;2150&lt;/td&gt;
&lt;td&gt;1899&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Gemma&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TAU2-Bench&lt;/td&gt;
&lt;td&gt;76.9%&lt;/td&gt;
&lt;td&gt;79.0%&lt;/td&gt;
&lt;td&gt;Qwen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MMMLU&lt;/td&gt;
&lt;td&gt;88.4%&lt;/td&gt;
&lt;td&gt;85.9%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Gemma&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HLE (no tools)&lt;/td&gt;
&lt;td&gt;19.5%&lt;/td&gt;
&lt;td&gt;24.3%&lt;/td&gt;
&lt;td&gt;Qwen&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Gemma 4 wins on competitive coding (ELO) and multilingual (MMMLU). Qwen 3.5 wins on most reasoning benchmarks. Neither is a clear overall winner.&lt;/p&gt;

&lt;p&gt;The honest take from one top commenter: "Gemma 4 ties with Qwen, if not Qwen being slightly ahead. And Qwen 3.5 is more compute efficient too."&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Community Is Waiting For
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;QAT versions.&lt;/strong&gt; Gemma 3 QAT (quantization-aware training) models arrived weeks after the initial release. The community expects the same for Gemma 4, and these will likely improve quantized inference quality significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abliterated/uncensored versions.&lt;/strong&gt; At least one already exists. Multiple users are requesting more. The Apache 2.0 license makes this fully legal now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Larger models.&lt;/strong&gt; There were rumors of a 120B model that didn't materialize. Several users expressed disappointment. A 100B+ MoE from Google could be transformative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A 9-12B dense model.&lt;/strong&gt; The gap between E4B (4.5B effective) and 26B MoE leaves a hole in the lineup. Gemma 3's 12B model was popular, and there's no direct upgrade path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Leaves Us
&lt;/h2&gt;

&lt;p&gt;Gemma 4 is not the clear winner the benchmarks suggested. But it's not trying to be.&lt;/p&gt;

&lt;p&gt;The real value proposition is the combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apache 2.0&lt;/strong&gt; (fully permissive, no restrictions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multilingual excellence&lt;/strong&gt; (best in class for non-English)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base models available&lt;/strong&gt; (fine-tuning ready on day one)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size diversity&lt;/strong&gt; (2B to 31B covers edge to server)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native system prompts and function calling&lt;/strong&gt; (production-ready features)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For English-only, benchmark-optimized, speed-critical deployments, Qwen 3.5 is still the better choice. For multilingual, legally unrestricted, fine-tuning-focused use cases, Gemma 4 has a compelling argument.&lt;/p&gt;

&lt;p&gt;The speed and VRAM issues need to be addressed. The fine-tuning tooling needs a week or two to catch up. And we need QAT quantizations before the smaller models can truly compete on efficiency.&lt;/p&gt;

&lt;p&gt;But make no mistake, releasing a 31B dense model under Apache 2.0 that rivals models 4-10x its size on human preference benchmarks is a significant moment for open AI. Google is finally competing on openness, not just capability.&lt;/p&gt;

&lt;p&gt;I'll be publishing our fine-tuning results (including the day-zero bug fixes) and benchmark comparisons as the training run completes. Follow along if you're interested.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Nathan Maine builds AI systems for regulated industries. He is currently fine-tuning Gemma 4 31B for domain-specific deployment and has filed bug reports on huggingface/peft and huggingface/transformers for day-zero compatibility issues.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>google</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Fine-Tuning Gemma 4 on Day Zero: 3 Bugs We Solved in 30 Minutes</title>
      <dc:creator>Nathan Maine</dc:creator>
      <pubDate>Thu, 02 Apr 2026 20:35:03 +0000</pubDate>
      <link>https://dev.to/dentity007/fine-tuning-gemma-4-on-day-zero-3-bugs-we-solved-in-30-minutes-2ke</link>
      <guid>https://dev.to/dentity007/fine-tuning-gemma-4-on-day-zero-3-bugs-we-solved-in-30-minutes-2ke</guid>
      <description>&lt;p&gt;Google released &lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/" rel="noopener noreferrer"&gt;Gemma 4&lt;/a&gt; today under Apache 2.0 — their most capable open model family. The 31B dense model scores ~1452 on LMArena with a 256K context window.&lt;/p&gt;

&lt;p&gt;We wanted to fine-tune it immediately. QLoRA on a single NVIDIA B200. It broke three times before training started.&lt;/p&gt;

&lt;p&gt;Here's what happened and how we fixed each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bug 1: "Transformers does not recognize this architecture"
&lt;/h2&gt;

&lt;p&gt;The first error hits before the model even loads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueError: The checkpoint you are trying to load has model type `gemma4` 
but Transformers does not recognize this architecture.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The latest stable Transformers release (5.4.0) shipped before Gemma 4 existed. The &lt;code&gt;gemma4&lt;/code&gt; model type only exists in the dev branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Install from source.&lt;br&gt;
&lt;/p&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;git+https://github.com/huggingface/transformers.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets you 5.5.0.dev0 which includes the &lt;code&gt;Gemma4ForConditionalGeneration&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to fix:&lt;/strong&gt; 2 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bug 2: "Target module Gemma4ClippableLinear is not supported"
&lt;/h2&gt;

&lt;p&gt;After installing Transformers from source, the model loads fine. But when PEFT tries to apply LoRA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueError: Target module Gemma4ClippableLinear(
  (linear): Linear4bit(in_features=1152, out_features=1152, bias=False)
) is not supported.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Gemma 4 introduces a new layer type called &lt;code&gt;Gemma4ClippableLinear&lt;/code&gt; for its vision and audio encoders. It wraps &lt;code&gt;nn.Linear&lt;/code&gt; with optional input/output clamping for numerical stability. The catch: it inherits from &lt;code&gt;nn.Module&lt;/code&gt;, not &lt;code&gt;nn.Linear&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;PEFT checks the type of every target module before applying LoRA. Since &lt;code&gt;Gemma4ClippableLinear&lt;/code&gt; isn't &lt;code&gt;nn.Linear&lt;/code&gt;, PEFT rejects it — even though we only want to apply LoRA to the text decoder layers, not the vision encoder.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;exclude_modules&lt;/code&gt; parameter doesn't help either. PEFT runs the type check &lt;em&gt;before&lt;/em&gt; filtering, so excluded modules still need to be recognized types.&lt;/p&gt;

&lt;p&gt;Installing PEFT from source doesn't help either — the support simply doesn't exist yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Monkey-patch &lt;code&gt;Gemma4ClippableLinear&lt;/code&gt; to inherit from &lt;code&gt;nn.Linear&lt;/code&gt; before loading the model.&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.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;from&lt;/span&gt; &lt;span class="n"&gt;transformers.models.gemma4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;modeling_gemma4&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PatchedClippableLinear&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="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;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in_features&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_features&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="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;in_features&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_features&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;use_clipped_linears&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&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_clipped_linears&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;if&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;use_clipped_linears&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;input_min&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;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;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;input_max&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;tensor&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;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;output_min&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;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;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;output_max&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;tensor&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="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;if&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;use_clipped_linears&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;clamp&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_min&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;input_max&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;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="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;if&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;use_clipped_linears&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;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clamp&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_min&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;output_max&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;span class="n"&gt;modeling_gemma4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gemma4ClippableLinear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PatchedClippableLinear&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place this &lt;strong&gt;before&lt;/strong&gt; any &lt;code&gt;AutoModelForCausalLM.from_pretrained()&lt;/code&gt; call. PEFT now sees the vision encoder layers as standard linear layers and proceeds normally.&lt;/p&gt;

&lt;p&gt;Result: 534M trainable parameters (1.68% of 31.8B total).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to fix:&lt;/strong&gt; 15 minutes (including reading the Gemma 4 source to understand the layer).&lt;/p&gt;




&lt;h2&gt;
  
  
  Bug 3: "mm_token_type_ids is required"
&lt;/h2&gt;

&lt;p&gt;LoRA applies, data loads, training starts — and immediately crashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueError: `mm_token_type_ids` is required as a model input when training
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Gemma 3 required &lt;code&gt;token_type_ids&lt;/code&gt; during training. Gemma 4 adds a second required field: &lt;code&gt;mm_token_type_ids&lt;/code&gt; (multimodal token type IDs). The model validates their presence in the forward pass, even for text-only training. For text-only inputs, both should be all zeros.&lt;/p&gt;

&lt;p&gt;Standard tokenizers and data collators don't produce &lt;code&gt;mm_token_type_ids&lt;/code&gt;. You need a custom collator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add both fields during tokenization and build a custom data collator.&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="c1"&gt;# During tokenization
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;format_chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&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;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tokenize&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;add_generation_prompt&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;tokenized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tokenizer&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;truncation&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;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tokenized&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_type_ids&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="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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenized&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;tokenized&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mm_token_type_ids&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="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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenized&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;tokenized&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;labels&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;tokenized&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;copy&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;tokenized&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="c1"&gt;# Custom data collator
&lt;/span&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GemmaCollator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&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;features&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;max_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&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;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&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;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pad_id&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;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token_id&lt;/span&gt;
        &lt;span class="n"&gt;batch&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;input_ids&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_mask&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token_type_ids&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mm_token_type_ids&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;labels&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pad_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max_len&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;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pad_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pad_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attention_mask&lt;/span&gt;&lt;span class="sh"&gt;"&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="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="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;f&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&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="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="n"&gt;pad_len&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;batch&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_type_ids&lt;/span&gt;&lt;span class="sh"&gt;"&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="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="n"&gt;max_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mm_token_type_ids&lt;/span&gt;&lt;span class="sh"&gt;"&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="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="n"&gt;max_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;labels&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;f&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="s"&gt;labels&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_ids&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="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pad_len&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;k&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;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&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;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;batch&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: set &lt;code&gt;remove_unused_columns=False&lt;/code&gt; in your training config, or the trainer will strip &lt;code&gt;mm_token_type_ids&lt;/code&gt; before it reaches the model.&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;training_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;...,&lt;/span&gt;
    &lt;span class="n"&gt;dataset_text_field&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;remove_unused_columns&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="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTTrainer&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="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="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;training_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenized_dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;data_collator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;GemmaCollator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenizer&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;p&gt;&lt;strong&gt;Time to fix:&lt;/strong&gt; 5 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;After all three fixes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;31B model training at 4.5s/step&lt;/strong&gt; on a single NVIDIA B200 (192GB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;534M trainable parameters&lt;/strong&gt; via QLoRA (1.68% of 31.8B)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPU utilization: 89%&lt;/strong&gt;, 38GB VRAM used&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimated training time: ~7.5 hours&lt;/strong&gt; for 3 epochs on 16K examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total time from "model released" to "training steps running": &lt;strong&gt;under 4 hours&lt;/strong&gt; (including model download).&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Day-zero fine-tuning requires bleeding-edge dependencies.&lt;/strong&gt; Install Transformers and PEFT from source when working with newly released models.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multimodal models have hidden requirements for text-only training.&lt;/strong&gt; Both &lt;code&gt;token_type_ids&lt;/code&gt; and &lt;code&gt;mm_token_type_ids&lt;/code&gt; are validated even when no images or audio are involved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PEFT's type checking happens before module filtering.&lt;/strong&gt; Even if you exclude vision modules, they still need to be recognized types. Monkey-patching is a valid workaround until official support lands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;None of these are avoidable with experience.&lt;/strong&gt; They're day-zero discovery problems. The difference is how fast you solve them.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Issues filed:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;[huggingface/peft] Gemma4ClippableLinear not supported&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;[huggingface/transformers] mm_token_type_ids required for text-only fine-tuning&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Both include workarounds and suggested fixes. PRs welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
