<?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: Khadim Hussain</title>
    <description>The latest articles on DEV Community by Khadim Hussain (@khadimhussain).</description>
    <link>https://dev.to/khadimhussain</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%2F4177831%2F1b58be95-f5d8-48f8-a7ed-1f0b3aaa6a31.png</url>
      <title>DEV Community: Khadim Hussain</title>
      <link>https://dev.to/khadimhussain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khadimhussain"/>
    <language>en</language>
    <item>
      <title>Is SynthID reliable? I tested Google's AI image check on screenshots, stripped files and real photos</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Mon, 12 Oct 2026 18:40:48 +0000</pubDate>
      <link>https://dev.to/khadimhussain/is-synthid-reliable-i-tested-googles-ai-image-check-on-screenshots-stripped-files-and-real-photos-297o</link>
      <guid>https://dev.to/khadimhussain/is-synthid-reliable-i-tested-googles-ai-image-check-on-screenshots-stripped-files-and-real-photos-297o</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/is-synthid-reliable" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; Mostly, for what it is built to do. In my tests on October 12, 2026, Google's SynthID check in the Gemini app flagged a Gemini-generated image after its metadata was removed and again after a screenshot, and found nothing on a real photo. It missed the same AI image once it was pasted small into a real photo, and the new SynthID Detector website failed on every upload that day. A clean result only means no watermark from Google or its partners was found.&lt;/p&gt;

&lt;p&gt;Google opened the &lt;a href="https://synthid.com" rel="noopener noreferrer"&gt;SynthID Detector&lt;/a&gt; to everyone on October 7, 2026. Most of the coverage so far repeats the launch post, so I generated some images and tested the questions people search for. Does it survive a screenshot? Does it need the file's metadata? Will it flag a real photo?&lt;/p&gt;

&lt;h2&gt;
  
  
  What SynthID Detector is
&lt;/h2&gt;

&lt;p&gt;SynthID is Google DeepMind's invisible watermark for AI-generated images, video and audio. &lt;a href="https://blog.google/innovation-and-ai/models-and-research/google-deepmind/synth-id-ai-content/" rel="noopener noreferrer"&gt;Google's launch post&lt;/a&gt; says it has watermarked over 180 billion images and videos and 240,000 years of audio. The Detector reads watermarks from Google and its partners: OpenAI, NVIDIA, Kakao and, soon, Apple. You sign in with a Google, Apple or OpenAI account, then upload a file.&lt;/p&gt;

&lt;p&gt;It is not a general AI detector. It answers one question: does this file carry a SynthID watermark? The same check is built into Search, Chrome and the Gemini app, which together handle over a million verification requests a day, according to Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I tested it
&lt;/h2&gt;

&lt;p&gt;I generated two photorealistic images with Gemini 3 Pro Image through the Gemini API (2400 × 1792 px). Google's &lt;a href="https://ai.google.dev/gemini-api/docs/image-generation" rel="noopener noreferrer"&gt;image generation docs&lt;/a&gt; say "All generated images include a SynthID watermark." From one of them I made these test files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the original file, untouched&lt;/li&gt;
&lt;li&gt;the same pixels with the metadata removed (a pixel diff against the original is exactly zero)&lt;/li&gt;
&lt;li&gt;a real screenshot: the image shown in Chrome, captured from the screen and saved as a 2048 × 1523 PNG&lt;/li&gt;
&lt;li&gt;a real photo from NASA's public image library, taken at Kennedy Space Center in 2023&lt;/li&gt;
&lt;li&gt;a second NASA photo with only the sky changed by Gemini&lt;/li&gt;
&lt;li&gt;the AI image pasted small into the corner of the real photo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Detector was the plan, but on October 12 all four of my uploads, between 4:36 and 5:33 pm UTC, ended in "Something went wrong! Please wait and try again later." That included a 166 KB file, so it wasn't file size. So I used the SynthID check built into the Gemini app: a fresh chat for each file, my personal account, and the question "Is this AI-generated?"&lt;/p&gt;

&lt;p&gt;I kept to things a normal user would check. The Detector's terms forbid using its results to work out which transformations break the watermark, so I didn't build a grid of crops, filters and compression levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;Content Credentials (the C2PA standard) are signed metadata that record how a file was made. SynthID is the watermark in the pixels. Here is what Gemini said about each file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Content Credentials in file&lt;/th&gt;
&lt;th&gt;Gemini's answer&lt;/th&gt;
&lt;th&gt;What it used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemini image, untouched&lt;/td&gt;
&lt;td&gt;Yes, signed by Google LLC&lt;/td&gt;
&lt;td&gt;"created using artificial intelligence by Google LLC, followed by subsequent editing using AI tools"&lt;/td&gt;
&lt;td&gt;C2PA metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same pixels, metadata removed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"Most or all of this image was edited or generated with Google AI." (2 of 2 runs)&lt;/td&gt;
&lt;td&gt;SynthID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screenshot of it (PNG)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"Most or all of this image was edited or generated with Google AI." (2 of 2 runs)&lt;/td&gt;
&lt;td&gt;SynthID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real photo (NASA, 2023)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"No reliable signals were detected indicating how the content was created"&lt;/td&gt;
&lt;td&gt;Nothing found&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real photo, sky edited in Gemini&lt;/td&gt;
&lt;td&gt;Yes, signed by Google&lt;/td&gt;
&lt;td&gt;"created as a generative AI creation and subsequently edited using generative AI tools"&lt;/td&gt;
&lt;td&gt;C2PA metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI image pasted small into the real photo&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;"No reliable signals were detected"&lt;/td&gt;
&lt;td&gt;Missed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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%2Fethze9crcvtydfb2t6q9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fethze9crcvtydfb2t6q9.png" alt="Gemini answering 'Most or all of this image was edited or generated with Google AI' for an image whose metadata had been removed" width="800" height="346"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metadata removed, pixels untouched: Gemini still finds Google's watermark.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Does SynthID work on screenshots?
&lt;/h2&gt;

&lt;p&gt;It did in mine. A screenshot changes the image: it's rescaled to fit the screen, re-rendered by the browser and saved in a different format. Gemini flagged it both times. The second answer also said "No reliable C2PA content credentials were detected for this file", so the detection came from the pixels.&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%2Fu7wcba07u9qejuffxswo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7wcba07u9qejuffxswo.png" alt="Gemini detecting Google AI in a PNG screenshot of a generated image and noting that no C2PA content credentials were found" width="800" height="558"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A screenshot of the image, saved as PNG: flagged, with no C2PA credentials left in the file.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not every screenshot test agrees. On October 8, &lt;a href="https://www.digitalinformationworld.com/2026/10/google-expands-synthid-detector.html" rel="noopener noreferrer"&gt;Digital Information World&lt;/a&gt; reported that the Detector website flagged their image as a standalone file but not as a screenshot. Mine was cropped to the image and checked in Gemini, and theirs used the Detector, so the two aren't directly comparable. Either way, a screenshot is a weaker test than the original file.&lt;/p&gt;

&lt;p&gt;Google's own claim is narrower. Its &lt;a href="https://deepmind.google/models/synthid/" rel="noopener noreferrer"&gt;SynthID page&lt;/a&gt; says the image watermark is "designed to stand up to modifications like cropping, adding filters, changing frame rates, or lossy compression." Screenshots aren't on that list.&lt;/p&gt;
&lt;h2&gt;
  
  
  SynthID vs C2PA: the two labels inside a Gemini image
&lt;/h2&gt;

&lt;p&gt;Every Gemini image I made carried two separate labels, and the check reads both.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;C2PA Content Credentials&lt;/th&gt;
&lt;th&gt;SynthID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where it lives&lt;/td&gt;
&lt;td&gt;Signed metadata in the file (about 6 KB here)&lt;/td&gt;
&lt;td&gt;In the pixels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What it records&lt;/td&gt;
&lt;td&gt;Who made it, which tool, edit history&lt;/td&gt;
&lt;td&gt;Only that a Google or partner model made it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survives a re-save or screenshot&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, in my tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who can read it&lt;/td&gt;
&lt;td&gt;Anyone, with open tools&lt;/td&gt;
&lt;td&gt;Google's tools only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Removing the metadata, or re-saving the image as a 1080 px JPEG, deleted the C2PA manifest completely. The file then relies on SynthID alone. You can read the credentials yourself with &lt;a href="https://github.com/contentauth/c2pa-python" rel="noopener noreferrer"&gt;c2pa-python&lt;/a&gt; (version 0.38.0 here):&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;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;c2pa&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Reader&lt;/span&gt;

&lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini-image.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&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;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manifests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active_manifest&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;manifest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signature_info&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;issuer&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;assertion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assertions&lt;/span&gt;&lt;span class="sh"&gt;"&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;assertion&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c2pa.actions&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;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;assertion&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&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;actions&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;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;action&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;digitalSourceType&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;Google LLC
c2pa.created http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
c2pa.edited http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;trainedAlgorithmicMedia&lt;/code&gt; is the &lt;a href="https://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia" rel="noopener noreferrer"&gt;IPTC term&lt;/a&gt; for media created by a generative model. If you'd rather not run code, &lt;a href="https://contentcredentials.org/verify" rel="noopener noreferrer"&gt;contentcredentials.org/verify&lt;/a&gt; reads the same manifest in the browser.&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%2Fes913g2vo8qt2lqvxm05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fes913g2vo8qt2lqvxm05.png" alt="Gemini reporting C2PA Content Credentials showing the image was created and edited with AI by Google LLC" width="799" height="515"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;With the metadata intact, Gemini answers from the C2PA credentials and doesn't mention SynthID.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is SynthID reliable? What a result tells you
&lt;/h2&gt;

&lt;p&gt;A positive result is strong evidence. Gemini identified the untouched file from its metadata, and the stripped copy and the screenshot from the watermark, while the real photo came back clean. One photo isn't a false-positive rate, though.&lt;/p&gt;

&lt;p&gt;A negative result is weak evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It only knows its partners. An image from a generator outside the SynthID scheme will always come back clean.&lt;/li&gt;
&lt;li&gt;It missed a composite. The same AI image, pasted small into the corner of a real photo, came back "No reliable signals were detected." &lt;a href="https://www.digitalinformationworld.com/2026/10/google-expands-synthid-detector.html" rel="noopener noreferrer"&gt;Digital Information World&lt;/a&gt; saw the same on the Detector website: an AI image covering more than half of a new image came back as unlikely to be from a SynthID partner.&lt;/li&gt;
&lt;li&gt;The check has slipped before. In July, &lt;a href="https://leadstories.com/analysis/2026/07/google-gemini-synthid-detector-confuses-results-within-same-chat.html" rel="noopener noreferrer"&gt;Lead Stories found&lt;/a&gt; that within one chat, Gemini repeated its verdict on the first file when asked about a second one, for images and videos. Google said on July 16, 2026 that a fix had rolled out globally. I still used a fresh chat for every file.&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%2Fhs2rob9zhx5q8w4c03a9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhs2rob9zhx5q8w4c03a9.png" alt="Gemini answering 'No reliable signals were detected' for a real photo with a small AI-generated image pasted into its corner" width="799" height="439"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An AI image pasted small into a real photo (background: NASA, public domain): not detected.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How you ask matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first prompt was "Was this image created or edited with Google AI? Please check it for a SynthID watermark." On the metadata-stripped file, Gemini answered that it couldn't check for a watermark at all. Asked "Is this AI-generated?", which is Google's own suggested wording, it found the watermark both times. That was one run against two, but if Gemini says it can't check, ask again with the plain question in a new chat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How to check an image yourself
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the Gemini app, upload the image in a new chat and ask "Is this AI-generated?" In my tests it answered from C2PA when the file had it, and from SynthID when it didn't.&lt;/li&gt;
&lt;li&gt;On the SynthID Detector, sign in at &lt;a href="https://synthid.com" rel="noopener noreferrer"&gt;synthid.com&lt;/a&gt; and upload the file. It covers Google's partners as well, and shows which parts of the image are most likely watermarked.&lt;/li&gt;
&lt;li&gt;For Content Credentials, use &lt;a href="https://contentcredentials.org/verify" rel="noopener noreferrer"&gt;contentcredentials.org/verify&lt;/a&gt;, or the code above, to see the signed history, if the file still has it.&lt;/li&gt;
&lt;li&gt;Read a clean result as "unknown", not "real".&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What about AI-generated text?
&lt;/h2&gt;

&lt;p&gt;The image watermark is closed, but &lt;a href="https://ai.google.dev/responsible/docs/safeguards/synthid" rel="noopener noreferrer"&gt;SynthID Text&lt;/a&gt; is open source. It ships in Hugging Face Transformers 4.46 and later as a watermarking config passed to &lt;code&gt;generate()&lt;/code&gt;, with a Bayesian detector. Text detection is probabilistic, and I didn't test it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you build image pipelines
&lt;/h2&gt;

&lt;p&gt;Any step that decodes and re-encodes an image (resizing, thumbnails, format conversion) drops the C2PA manifest, as the 1080 px re-save above showed. If provenance matters to your product, carry the manifest through with the C2PA SDK rather than relying on the watermark alone. SynthID survived the re-save and the screenshot in my tests, but only Google's tools can read it.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Does SynthID work on screenshots?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes. My screenshot of a Gemini image, cropped to the image and saved as a PNG, was flagged by Gemini's check in both runs, from the watermark in the pixels. Digital Information World's screenshot test on the SynthID Detector website failed on October 8, 2026. Use the original file whenever you can get it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is SynthID the same as C2PA Content Credentials?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. C2PA Content Credentials are signed metadata stored in the file, and a normal re-save or upload removes them. SynthID is a watermark in the pixels themselves. Gemini images carry both, and Gemini's check reads whichever one it finds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can SynthID detect Midjourney or Stable Diffusion images?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only if the generator embeds SynthID. Google lists itself, OpenAI, NVIDIA and Kakao as the companies whose watermarks the Detector reads, with Apple to follow. Anything else comes back as no watermark found, which says nothing either way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does SynthID have false positives?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A real photo came back clean in my test, but that is one photo, not a false-positive rate. Treat a positive result as strong evidence the image passed through Google's or a partner's AI tools, and a negative one as unknown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is SynthID open source?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The text watermark is: SynthID Text ships in Hugging Face Transformers 4.46 and later, with a reference implementation on GitHub. Image, video and audio detection is only available through Google's tools, such as the SynthID Detector and the Gemini app.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>security</category>
      <category>google</category>
    </item>
    <item>
      <title>Fine-tuning gpt-oss-20b with Unsloth and running it in Ollama</title>
      <dc:creator>Khadim Hussain</dc:creator>
      <pubDate>Sun, 11 Oct 2026 21:08:28 +0000</pubDate>
      <link>https://dev.to/khadimhussain/fine-tuning-gpt-oss-20b-with-unsloth-and-running-it-in-ollama-1nfh</link>
      <guid>https://dev.to/khadimhussain/fine-tuning-gpt-oss-20b-with-unsloth-and-running-it-in-ollama-1nfh</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://khadim.tech/blog/fine-tune-gpt-oss-20b-unsloth-ollama" rel="noopener noreferrer"&gt;khadim.tech&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt; gpt-oss-20b fine-tunes with QLoRA on a single consumer GPU (Unsloth puts it at about 14 GB of VRAM). Shipping it is where the chat template matters: gpt-oss uses OpenAI's Harmony format, so the GGUF needs a Harmony template with &amp;lt;|return|&amp;gt; as a stop token, or Ollama gives gibberish or never stops.&lt;/p&gt;

&lt;p&gt;I fine-tuned &lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;gpt-oss-20b&lt;/a&gt; for STEM reasoning and published it three ways: a LoRA adapter, a merged 16-bit model and GGUF files. Below are the settings and versions behind it, the export code, and the Modelfile that makes the GGUF behave in Ollama.&lt;/p&gt;

&lt;h2&gt;
  
  
  QLoRA settings for gpt-oss-20b
&lt;/h2&gt;

&lt;p&gt;The published run, as recorded on the &lt;a href="https://huggingface.co/khadim-hussain/gpt-oss-20b-stem-reasoning" rel="noopener noreferrer"&gt;model card&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&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;Base model&lt;/td&gt;
&lt;td&gt;openai/gpt-oss-20b (21B parameters, mixture of experts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;QLoRA, 4-bit base weights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA rank / alpha&lt;/td&gt;
&lt;td&gt;32 / 64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning rate&lt;/td&gt;
&lt;td&gt;1e-4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch size&lt;/td&gt;
&lt;td&gt;1, with gradient accumulation 16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimizer&lt;/td&gt;
&lt;td&gt;AdamW 8-bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Epochs&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;4,260 training and 474 evaluation examples of STEM Q&amp;amp;A with chain-of-thought&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result&lt;/td&gt;
&lt;td&gt;train loss 1.087, eval loss 0.837&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Unsloth + TRL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full training config, with target modules and scheduler, is in &lt;a href="https://github.com/khadimhussain0/kllm/blob/main/configs/gpt_oss_20b.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;configs/gpt_oss_20b.yaml&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/khadimhussain0/kllm" rel="noopener noreferrer"&gt;kllm&lt;/a&gt;, the small toolkit I use for these runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much VRAM does fine-tuning gpt-oss-20b need?
&lt;/h2&gt;

&lt;p&gt;QLoRA keeps the base weights in 4-bit and trains only a small adapter, so a 20B model fits on one consumer card. &lt;a href="https://docs.unsloth.ai/new/gpt-oss-how-to-run-and-fine-tune/tutorial-how-to-fine-tune-gpt-oss" rel="noopener noreferrer"&gt;Unsloth's gpt-oss guide&lt;/a&gt; lists about 14 GB of VRAM for gpt-oss-20b with QLoRA and recommends at least 16 GB for stable runs. LoRA on unquantized BF16 weights needs about 44 GB, which is why QLoRA is the practical choice on one card.&lt;/p&gt;

&lt;p&gt;The environment kllm is verified on:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;RTX 5090 (32 GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CUDA&lt;/td&gt;
&lt;td&gt;12.8 toolkit (13.0 driver)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;3.12.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PyTorch&lt;/td&gt;
&lt;td&gt;2.9.1+cu128&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unsloth&lt;/td&gt;
&lt;td&gt;2026.1.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;transformers&lt;/td&gt;
&lt;td&gt;4.57.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM&lt;/td&gt;
&lt;td&gt;0.15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pin transformers.&lt;/strong&gt; vLLM 0.15.0 needs exactly transformers 4.57.3, and other versions fail with import errors. Install in this order, and force the pin last.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; vllm &lt;span class="nt"&gt;--torch-backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cu128   &lt;span class="c"&gt;# cu121 for RTX 40 series&lt;/span&gt;
uv pip &lt;span class="nb"&gt;install &lt;/span&gt;unsloth unsloth_zoo bitsandbytes
uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--force-reinstall&lt;/span&gt; &lt;span class="s2"&gt;"transformers==4.57.3"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Export to LoRA, merged and GGUF with Unsloth
&lt;/h2&gt;

&lt;p&gt;After training, the same model object gives all three formats. These are the calls kllm makes:&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;# 1. LoRA adapter (~61 MB): for people who already run the base model
&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;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&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="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Merged 16-bit model (~41 GB): for Transformers or vLLM, no adapter handling
&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;save_pretrained_merged&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;output_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-merged&lt;/span&gt;&lt;span class="sh"&gt;"&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;save_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merged_16bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. GGUF for llama.cpp, Ollama and LM Studio, one call per quantization
&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;save_pretrained_gguf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gguf/gpt-oss-20b-stem&lt;/span&gt;&lt;span class="sh"&gt;"&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;quantization_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q4_k_m&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;p&gt;&lt;code&gt;quantization_method&lt;/code&gt; takes &lt;code&gt;f16&lt;/code&gt;, &lt;code&gt;q8_0&lt;/code&gt;, &lt;code&gt;q4_k_m&lt;/code&gt; and the other llama.cpp types listed in &lt;a href="https://unsloth.ai/docs/basics/inference-and-deployment/saving-to-gguf" rel="noopener noreferrer"&gt;Unsloth's GGUF docs&lt;/a&gt;. The GGUF files for this model came out at:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;f16&lt;/td&gt;
&lt;td&gt;41.9 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q8_0&lt;/td&gt;
&lt;td&gt;22.3 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q4_K_M&lt;/td&gt;
&lt;td&gt;15.8 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the GGUF step runs out of memory, Unsloth's docs suggest lowering &lt;code&gt;maximum_memory_usage&lt;/code&gt; from its 0.75 default to around 0.5.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Harmony chat template is what breaks
&lt;/h2&gt;

&lt;p&gt;gpt-oss doesn't use ChatML. It was trained on OpenAI's &lt;a href="https://developers.openai.com/cookbook/articles/openai-harmony" rel="noopener noreferrer"&gt;Harmony format&lt;/a&gt;, where every message is wrapped in special tokens and the assistant writes on separate channels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;analysis&lt;/code&gt; for its chain of thought, which end users shouldn't see&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;final&lt;/code&gt; for the answer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commentary&lt;/code&gt; for tool calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A final answer ends with &lt;code&gt;&amp;lt;|return|&amp;gt;&lt;/code&gt;, which OpenAI's docs call "a valid stop token indicating that you should stop inference." Messages kept in the conversation history end with &lt;code&gt;&amp;lt;|end|&amp;gt;&lt;/code&gt; instead, while supervised training targets should end with &lt;code&gt;&amp;lt;|return|&amp;gt;&lt;/code&gt;. OpenAI's &lt;a href="https://huggingface.co/openai/gpt-oss-20b" rel="noopener noreferrer"&gt;model card&lt;/a&gt; is blunt about it: the models "should only be used with the harmony format as it will not work correctly otherwise."&lt;/p&gt;

&lt;p&gt;So the template you ship has to match the one used in training. &lt;a href="https://unsloth.ai/docs/basics/inference-and-deployment/saving-to-gguf" rel="noopener noreferrer"&gt;Unsloth's docs&lt;/a&gt; name a mismatch as the most common reason a model gives gibberish or endless output on other platforms. And &lt;code&gt;&amp;lt;|return|&amp;gt;&lt;/code&gt; has to be a stop token, or generation runs past the answer.&lt;/p&gt;

&lt;p&gt;This exact question, how to template a fine-tuned gpt-oss for Ollama, is &lt;a href="https://community.openai.com/t/guidance-needed-gpt-oss-20b-fine-tuning-with-unsloth-gguf-ollama-triton-vllm-tensorrt-llm/1373515" rel="noopener noreferrer"&gt;still open on OpenAI's forum&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working Ollama Modelfile for fine-tuned gpt-oss
&lt;/h2&gt;

&lt;p&gt;This is the Modelfile that ships with the &lt;a href="https://huggingface.co/khadim-hussain/gpt-oss-20b-stem-reasoning-GGUF" rel="noopener noreferrer"&gt;GGUF repo&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ./gpt-oss-20b-finetuned-q8_0.gguf

TEMPLATE """&amp;lt;|start|&amp;gt;system&amp;lt;|message|&amp;gt;You are a helpful assistant trained by OpenAI.

Reasoning: medium

# Valid channels: analysis, commentary, final. Channel must be included for every message.&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;user&amp;lt;|message|&amp;gt;{{ .Prompt }}&amp;lt;|end|&amp;gt;&amp;lt;|start|&amp;gt;assistant"""

PARAMETER stop "&amp;lt;|return|&amp;gt;"
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
PARAMETER num_predict 2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting reasoning effort
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Reasoning: medium&lt;/code&gt; in the system message sets how much the model thinks in its &lt;code&gt;analysis&lt;/code&gt; channel before it answers. Harmony accepts &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt; and &lt;code&gt;high&lt;/code&gt;, and medium is the default. To change it, edit that line in the TEMPLATE and run &lt;code&gt;ollama create&lt;/code&gt; again. Ollama shows the analysis channel as "Thinking...", so &lt;code&gt;low&lt;/code&gt; is the setting for short, fast answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  ollama run hf.co or a Modelfile?
&lt;/h2&gt;

&lt;p&gt;With the Modelfile, the template is the one you wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama create gpt-oss-20b-stem &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile
ollama run gpt-oss-20b-stem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ollama can also pull straight from Hugging Face:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run hf.co/khadim-hussain/gpt-oss-20b-stem-reasoning-GGUF:Q4_K_M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That route doesn't read the Modelfile in the repo. Per &lt;a href="https://huggingface.co/docs/hub/ollama" rel="noopener noreferrer"&gt;Hugging Face's Ollama docs&lt;/a&gt;, it uses the chat template stored in the GGUF's &lt;code&gt;tokenizer.chat_template&lt;/code&gt; metadata, or a file named &lt;code&gt;template&lt;/code&gt; in the repo. The quantization tag is case-insensitive, and without a tag Ollama picks Q4_K_M. If you publish a Harmony model, put a &lt;code&gt;template&lt;/code&gt; file in the repo or point people to the Modelfile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which format people actually download
&lt;/h2&gt;

&lt;p&gt;All-time Hugging Face downloads as of October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;gpt-oss-20b&lt;/th&gt;
&lt;th&gt;Qwen3-14B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GGUF&lt;/td&gt;
&lt;td&gt;513&lt;/td&gt;
&lt;td&gt;274&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merged 16-bit&lt;/td&gt;
&lt;td&gt;70&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoRA adapter&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most people want a file they can run on their own machine. If you only publish one format, make it a GGUF with a working template. The adapter is worth adding for anyone who wants to keep training.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;The failures documented in kllm's troubleshooting guide:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Training process dies with no error&lt;/td&gt;
&lt;td&gt;Out of memory. Check `dmesg \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;UnboundLocalError&lt;/code&gt; mentioning dropout&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;lora_dropout: 0.0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ImportError: cannot import name ... from 'transformers'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uv pip install --force-reinstall "transformers==4.57.3"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Python.h: No such file or directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt-get install python3.12-dev&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full project, with the Qwen3-14B run and the download numbers, is in the &lt;a href="https://khadim.tech/work/stem-reasoning-llms" rel="noopener noreferrer"&gt;STEM reasoning LLMs case study&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much VRAM do you need to fine-tune gpt-oss-20b?
&lt;/h3&gt;

&lt;p&gt;With QLoRA, Unsloth's gpt-oss guide puts gpt-oss-20b at about 14 GB of VRAM and recommends at least 16 GB for stable runs. LoRA on BF16 weights needs about 44 GB. kllm, the toolkit I trained with, is verified on an RTX 5090 with 32 GB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I run a fine-tuned gpt-oss-20b in Ollama?
&lt;/h3&gt;

&lt;p&gt;Yes. Export it to GGUF, then either run ollama create with a Modelfile that carries the Harmony template, or pull it with ollama run hf.co//:Q4_K_M. The second route uses the chat template embedded in the GGUF file, not the Modelfile in the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does my fine-tuned gpt-oss model output gibberish or never stop?
&lt;/h3&gt;

&lt;p&gt;Usually the chat template. The template at inference has to match the one used in training, and Harmony ends a final answer with &amp;lt;|return|&amp;gt;. If that token isn't a stop token, generation runs past the answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which GGUF quantization should I use for gpt-oss-20b?
&lt;/h3&gt;

&lt;p&gt;Q4_K_M (15.8 GB for this model) is the smallest file and the one Ollama picks by default. Use Q8_0 (22.3 GB) if you have the memory and want output closer to the 16-bit model.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set reasoning effort for gpt-oss in Ollama?
&lt;/h3&gt;

&lt;p&gt;Harmony reads it from the system message as Reasoning: low, medium or high, and medium is the default. In a Modelfile, change that line inside the TEMPLATE and run ollama create again.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>machinelearning</category>
      <category>ollama</category>
      <category>python</category>
    </item>
  </channel>
</rss>
