<?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: turingrtss</title>
    <description>The latest articles on DEV Community by turingrtss (@turingrtss).</description>
    <link>https://dev.to/turingrtss</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%2F4108221%2F49ac8072-ed09-4e66-a327-4dbbf098ff0b.png</url>
      <title>DEV Community: turingrtss</title>
      <link>https://dev.to/turingrtss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/turingrtss"/>
    <language>en</language>
    <item>
      <title>Same Model, 4 Tokenizers. 30 Percentage Points of Difference.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Fri, 11 Sep 2026 14:24:27 +0000</pubDate>
      <link>https://dev.to/turingrtss/same-model-4-tokenizers-30-percentage-points-of-difference-47mj</link>
      <guid>https://dev.to/turingrtss/same-model-4-tokenizers-30-percentage-points-of-difference-47mj</guid>
      <description>&lt;p&gt;Same model, same data, 4 different ways to chop text into tokens. The accuracy spread was 30 percentage points.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Task:&lt;/strong&gt; AG News classification (World, Sports, Business, Sci/Tech)&lt;br&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Embedding (64d) + Average Pooling + 2-layer FC (128 hidden). Identical for all tokenizers.&lt;br&gt;
&lt;strong&gt;Data:&lt;/strong&gt; 15K train, 3K test&lt;br&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; 15 epochs, Adam 1e-3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tokenizers tested:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Character&lt;/strong&gt; - each character is a token (a, b, c...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Word&lt;/strong&gt; - whitespace/punctuation split (the, stock, market...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BPE&lt;/strong&gt; - learned subword merges (mark, ##et, ##ing...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Character 3-gram&lt;/strong&gt; - overlapping 3-char windows (the, he_, e_s...)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tokenizer&lt;/th&gt;
&lt;th&gt;Vocab Size&lt;/th&gt;
&lt;th&gt;Avg Seq Length&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Train Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Word&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;39 tokens&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;86.67%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9.2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Char 3-gram&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;227 tokens&lt;/td&gt;
&lt;td&gt;84.83%&lt;/td&gt;
&lt;td&gt;19.6s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BPE&lt;/td&gt;
&lt;td&gt;2,040&lt;/td&gt;
&lt;td&gt;102 tokens&lt;/td&gt;
&lt;td&gt;78.17%&lt;/td&gt;
&lt;td&gt;12.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Character&lt;/td&gt;
&lt;td&gt;83&lt;/td&gt;
&lt;td&gt;235 tokens&lt;/td&gt;
&lt;td&gt;56.67%&lt;/td&gt;
&lt;td&gt;22.0s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;30 percentage point spread from the same model on the same data. The only difference is how the text was split.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Word Wins
&lt;/h2&gt;

&lt;p&gt;Each word token carries a complete semantic unit. "stock" means something. "s", "t", "o", "c", "k" individually don't. With average pooling over the sequence, more meaning per token means more signal in the averaged representation.&lt;/p&gt;

&lt;p&gt;Word tokenization also produces the shortest sequences (39 tokens average vs 235 for characters). Shorter sequences mean less noise in the average pool and faster training.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The downside of words:&lt;/strong&gt; large vocabulary (10K), can't handle typos or unseen words. If the test set contains "cryptocurrency" and training only had "crypto", word-level misses it. Character n-grams would catch the overlap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Characters Failed
&lt;/h2&gt;

&lt;p&gt;56.67% accuracy (barely above the 25% random baseline for 4 classes). Each character carries almost zero semantic information. The model needs to compose meaning from sequences of characters, but average pooling destroys the ordering. Characters need attention or recurrence to work — you need the model to understand that "s-t-o-c-k" in sequence means something different than "k-c-o-t-s".&lt;/p&gt;

&lt;h2&gt;
  
  
  Why BPE Underperformed
&lt;/h2&gt;

&lt;p&gt;This one surprised me. BPE is the tokenizer behind GPT, Claude, and every major LLM. But my implementation only learned 2,040 merges (limited for CPU speed). Production BPE uses 32K-100K merges. With too few merges, BPE is stuck between character-level and word-level without the benefits of either.&lt;/p&gt;

&lt;p&gt;The tokenizer overhead was also brutal: 515 seconds for fit+encode vs 0.9 seconds for word-level. BPE merge learning is O(vocab * corpus_size) per merge step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Char N-grams Are Underrated
&lt;/h2&gt;

&lt;p&gt;84.83% accuracy, only 1.8% behind word-level. Character 3-grams capture subword patterns: prefixes ("pre", "un_"), suffixes ("ing", "tion"), and word fragments that generalize across related words. "trading", "traded", "trader" all share "trad" as a trigram.&lt;/p&gt;

&lt;p&gt;N-grams also handle typos, neologisms, and code-mixed text that break word-level tokenizers. For noisy real-world text (social media, logs, user input), char n-grams may outperform words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-Class Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tokenizer&lt;/th&gt;
&lt;th&gt;World&lt;/th&gt;
&lt;th&gt;Sports&lt;/th&gt;
&lt;th&gt;Business&lt;/th&gt;
&lt;th&gt;Sci/Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Word&lt;/td&gt;
&lt;td&gt;86.8%&lt;/td&gt;
&lt;td&gt;93.5%&lt;/td&gt;
&lt;td&gt;82.9%&lt;/td&gt;
&lt;td&gt;83.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Char N-gram&lt;/td&gt;
&lt;td&gt;85.7%&lt;/td&gt;
&lt;td&gt;92.6%&lt;/td&gt;
&lt;td&gt;80.1%&lt;/td&gt;
&lt;td&gt;81.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BPE&lt;/td&gt;
&lt;td&gt;77.7%&lt;/td&gt;
&lt;td&gt;88.6%&lt;/td&gt;
&lt;td&gt;72.4%&lt;/td&gt;
&lt;td&gt;74.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Character&lt;/td&gt;
&lt;td&gt;63.0%&lt;/td&gt;
&lt;td&gt;61.2%&lt;/td&gt;
&lt;td&gt;50.1%&lt;/td&gt;
&lt;td&gt;51.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sports is easiest to classify across all tokenizers (distinctive vocabulary: "goal", "championship", "quarterback"). Business and Sci/Tech are hardest (overlapping vocabulary: "market", "technology", "growth").&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tokenization is not a preprocessing detail. It is an architectural choice that affects accuracy more than most hyperparameters.&lt;/strong&gt; Switching from character to word tokenization improved accuracy by 30 percentage points with zero changes to the model.&lt;/p&gt;

&lt;p&gt;For text classification with simple models: use word-level or char n-grams. Save BPE for transformers that have the capacity to learn from subword structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>nlp</category>
      <category>research</category>
    </item>
    <item>
      <title>I Thought Spiking Networks Would Win on Temporal Tasks. They Lost.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Tue, 08 Sep 2026 01:16:14 +0000</pubDate>
      <link>https://dev.to/turingrtss/i-thought-spiking-networks-would-win-on-temporal-tasks-they-lost-1g10</link>
      <guid>https://dev.to/turingrtss/i-thought-spiking-networks-would-win-on-temporal-tasks-they-lost-1g10</guid>
      <description>&lt;p&gt;I hypothesized that spiking neural networks would outperform CNNs on temporal tasks. I was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;My previous experiment showed SNNs match CNNs on static image classification (97.4% vs 97.8% on MNIST) but are 36x slower on conventional hardware. The obvious follow-up: test tasks where temporal dynamics should give SNNs a natural advantage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tasks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Task 1: Sequential MNIST.&lt;/strong&gt; Feed image rows one at a time (28 timesteps of 28 pixels). The model must accumulate spatial information across time steps. This should favor architectures with memory (LSTM, SNN membrane potential).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task 2: Temporal spike patterns.&lt;/strong&gt; Classify synthetic spike trains by their firing pattern: regular bursts, accelerating frequency, decelerating frequency, fast-then-slow, and bimodal inter-spike intervals. Pure temporal structure, no spatial component. This is the ideal SNN task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sequential MNIST
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Params&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Train Time&lt;/th&gt;
&lt;th&gt;Inference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNN-1D&lt;/td&gt;
&lt;td&gt;11K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;93.15%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;17s&lt;/td&gt;
&lt;td&gt;0.016ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LSTM&lt;/td&gt;
&lt;td&gt;25K&lt;/td&gt;
&lt;td&gt;92.95%&lt;/td&gt;
&lt;td&gt;59s&lt;/td&gt;
&lt;td&gt;0.146ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNN&lt;/td&gt;
&lt;td&gt;2.5K&lt;/td&gt;
&lt;td&gt;42.85%&lt;/td&gt;
&lt;td&gt;45s&lt;/td&gt;
&lt;td&gt;0.146ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SNN completely failed. 42.85% is barely above random for 10 classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Temporal Spike Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Params&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Train Time&lt;/th&gt;
&lt;th&gt;Inference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNN-1D&lt;/td&gt;
&lt;td&gt;4K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99.75%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9s&lt;/td&gt;
&lt;td&gt;0.023ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNN&lt;/td&gt;
&lt;td&gt;453&lt;/td&gt;
&lt;td&gt;60.00%&lt;/td&gt;
&lt;td&gt;123s&lt;/td&gt;
&lt;td&gt;0.397ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LSTM&lt;/td&gt;
&lt;td&gt;17K&lt;/td&gt;
&lt;td&gt;51.75%&lt;/td&gt;
&lt;td&gt;163s&lt;/td&gt;
&lt;td&gt;0.889ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CNN destroyed both temporal architectures. 99.75% vs 60% and 51.75%.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Went Wrong With My Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I assumed temporal tasks need temporal architectures.&lt;/strong&gt; They don't. A 1D CNN treats a time series as a spatial signal and applies learned filters. Detecting "regular bursts every 20 steps" is a pattern detection problem, and convolutions are excellent at pattern detection regardless of whether the patterns are in space or time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The SNN had too few parameters.&lt;/strong&gt; 453 params (Task 2) vs 4,005 for CNN. The LIF neuron's linear transform plus membrane dynamics isn't expressive enough to learn complex temporal features. But adding more parameters defeats the SNN's efficiency argument.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Membrane decay forgets too fast.&lt;/strong&gt; With beta=0.9, the membrane potential decays to 12% of its value after 20 timesteps. For patterns that span 100+ timesteps, the early information is gone. Higher beta (0.99) would retain more but makes the surrogate gradient optimization harder.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Thing SNNs DID Do Better
&lt;/h2&gt;

&lt;p&gt;SNN beat LSTM on temporal patterns: 60% vs 51.75%, with 38x fewer parameters. Both are bad compared to CNN, but the SNN's LIF dynamics captured more temporal structure than LSTM's gating mechanism at this scale. The LSTM likely needs more hidden units and training time to converge on this task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revised Understanding
&lt;/h2&gt;

&lt;p&gt;After two experiments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static images:&lt;/strong&gt; SNN matches CNN on accuracy, 36x slower&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequential images:&lt;/strong&gt; SNN fails catastrophically (43% vs 93%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal patterns:&lt;/strong&gt; SNN loses to CNN-1D but beats LSTM at tiny scale&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CNNs win because convolutions are universal pattern detectors. Whether the pattern is spatial (edges in an image) or temporal (bursts in a spike train), learned convolutional filters find them efficiently. The kernel slides across space or time identically.&lt;/p&gt;

&lt;p&gt;SNNs are not better at temporal tasks on conventional hardware. Their advantage is energy efficiency on neuromorphic hardware, not architectural superiority on temporal data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publishing negative results matters. The hypothesis was reasonable, the data said otherwise, and now I know something I did not know before.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>research</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Spiking Neural Networks vs Transformers: I Tested Both. Here is What Matters.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Mon, 07 Sep 2026 21:48:14 +0000</pubDate>
      <link>https://dev.to/turingrtss/spiking-neural-networks-vs-transformers-i-tested-both-here-is-what-matters-427e</link>
      <guid>https://dev.to/turingrtss/spiking-neural-networks-vs-transformers-i-tested-both-here-is-what-matters-427e</guid>
      <description>&lt;p&gt;Can brain-inspired spiking networks compete with conventional architectures? I built both, ran them on the same task, and measured everything.&lt;/p&gt;

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

&lt;p&gt;Spiking neural networks (SNNs) process information as discrete spikes rather than continuous values. In biological neurons, spikes are energy-efficient because a neuron only consumes power when it fires. On neuromorphic hardware (Intel Loihi, IBM TrueNorth), this translates to 100-1000x energy savings over GPUs.&lt;/p&gt;

&lt;p&gt;But what happens on conventional hardware? Can SNNs match transformers and CNNs on accuracy? And is there any practical advantage when you don't have a neuromorphic chip?&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Dataset:&lt;/strong&gt; MNIST (20K train, 10K test)&lt;br&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; 15 epochs, Adam 1e-3&lt;br&gt;
&lt;strong&gt;Hardware:&lt;/strong&gt; ARM64, 2 CPU cores, 12GB RAM, no GPU&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spiking models:&lt;/strong&gt; Leaky integrate-and-fire neurons with surrogate gradient training. Rate-coded input (pixel intensity = spike probability). Tested fully-connected and convolutional variants at 10, 25, and 50 timesteps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conventional baselines:&lt;/strong&gt; MLP, CNN (LeNet), Transformer from the previous architecture comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Params&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Train Time&lt;/th&gt;
&lt;th&gt;Inference&lt;/th&gt;
&lt;th&gt;Spikes/sample&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNN (conventional)&lt;/td&gt;
&lt;td&gt;54K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;97.76%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;154s&lt;/td&gt;
&lt;td&gt;0.10ms&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNN-Conv T=25&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;11K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;97.41%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4663s&lt;/td&gt;
&lt;td&gt;3.77ms&lt;/td&gt;
&lt;td&gt;26,707&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transformer&lt;/td&gt;
&lt;td&gt;19K&lt;/td&gt;
&lt;td&gt;96.99%&lt;/td&gt;
&lt;td&gt;159s&lt;/td&gt;
&lt;td&gt;0.12ms&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNN-Conv T=10&lt;/td&gt;
&lt;td&gt;11K&lt;/td&gt;
&lt;td&gt;96.93%&lt;/td&gt;
&lt;td&gt;3227s&lt;/td&gt;
&lt;td&gt;1.57ms&lt;/td&gt;
&lt;td&gt;9,596&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MLP (conventional)&lt;/td&gt;
&lt;td&gt;55K&lt;/td&gt;
&lt;td&gt;95.20%&lt;/td&gt;
&lt;td&gt;7s&lt;/td&gt;
&lt;td&gt;0.004ms&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNN-FC T=50&lt;/td&gt;
&lt;td&gt;102K&lt;/td&gt;
&lt;td&gt;94.88%&lt;/td&gt;
&lt;td&gt;898s&lt;/td&gt;
&lt;td&gt;1.63ms&lt;/td&gt;
&lt;td&gt;1,980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNN-FC T=25&lt;/td&gt;
&lt;td&gt;102K&lt;/td&gt;
&lt;td&gt;94.61%&lt;/td&gt;
&lt;td&gt;483s&lt;/td&gt;
&lt;td&gt;0.75ms&lt;/td&gt;
&lt;td&gt;991&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNN-FC T=10&lt;/td&gt;
&lt;td&gt;102K&lt;/td&gt;
&lt;td&gt;94.19%&lt;/td&gt;
&lt;td&gt;202s&lt;/td&gt;
&lt;td&gt;0.30ms&lt;/td&gt;
&lt;td&gt;409&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Accuracy Story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SNNs match conventional networks on accuracy.&lt;/strong&gt; SNN-Conv T=25 hits 97.41%, only 0.35% behind CNN. SNN-FC T=25 hits 94.61%, matching MLP's 95.20%. The surrogate gradient method works. The spiking dynamics don't fundamentally limit what the network can learn.&lt;/p&gt;

&lt;p&gt;This is the key result: the accuracy gap between spiking and conventional architectures is negligible on this task.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Speed Story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SNNs are dramatically slower on conventional hardware.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SNN-Conv is &lt;strong&gt;36x slower&lt;/strong&gt; than CNN at inference (3.77ms vs 0.10ms)&lt;/li&gt;
&lt;li&gt;SNN-FC is &lt;strong&gt;174x slower&lt;/strong&gt; than MLP (0.75ms vs 0.004ms)&lt;/li&gt;
&lt;li&gt;SNN-Conv took &lt;strong&gt;30x longer&lt;/strong&gt; to train (4663s vs 154s)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why? Each timestep requires a full forward pass through the network. T=25 means 25 sequential passes. On a CPU, each pass costs the same as a conventional forward pass, so the SNN is roughly T times slower.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Energy Paradox
&lt;/h2&gt;

&lt;p&gt;SNNs are supposed to be energy-efficient because neurons only fire sparsely. The data shows the SNN-FC T=25 fires about 991 spikes per sample across all neurons and timesteps. That is sparse compared to a conventional network where every neuron activates every time.&lt;/p&gt;

&lt;p&gt;But on a CPU, a "spike" is not free. It is a multiply-accumulate operation just like any other. The sparsity only helps if the hardware can skip computation for non-firing neurons. CPUs can not. They execute the full membrane update and threshold check for every neuron at every timestep regardless of whether it spikes.&lt;/p&gt;

&lt;p&gt;On neuromorphic hardware, the 991 spikes would translate to 991 actual compute events. On a CPU, it translates to 128 neurons * 25 timesteps * 2 layers = 6,400 compute events. The sparsity is invisible to the hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  When SNNs Make Sense
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On neuromorphic chips:&lt;/strong&gt; The energy advantage is real. Intel Loihi processes SNNs at 100-1000x lower energy than GPUs on equivalent tasks. If your deployment target is a neuromorphic processor, SNNs are the right architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On edge devices with power constraints:&lt;/strong&gt; Even without dedicated hardware, if you can tolerate 36x slower inference in exchange for a smaller model (11K params vs 54K), the SNN-Conv might fit in memory-constrained environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For temporal data:&lt;/strong&gt; SNNs process sequences naturally. Audio, event cameras, sensor streams. The temporal dynamics of LIF neurons align with temporal data in a way that CNNs don't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not on conventional CPUs for image classification.&lt;/strong&gt; CNN wins on every practical metric: faster training, faster inference, same accuracy, simpler implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Neuromorphic vs Transformer Verdict
&lt;/h2&gt;

&lt;p&gt;Direct comparison at similar accuracy (~97%):&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;SNN-Conv T=25&lt;/th&gt;
&lt;th&gt;Transformer&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;Accuracy&lt;/td&gt;
&lt;td&gt;97.41%&lt;/td&gt;
&lt;td&gt;96.99%&lt;/td&gt;
&lt;td&gt;SNN (barely)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameters&lt;/td&gt;
&lt;td&gt;11K&lt;/td&gt;
&lt;td&gt;19K&lt;/td&gt;
&lt;td&gt;SNN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Train time&lt;/td&gt;
&lt;td&gt;4663s&lt;/td&gt;
&lt;td&gt;159s&lt;/td&gt;
&lt;td&gt;Transformer (29x)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inference&lt;/td&gt;
&lt;td&gt;3.77ms&lt;/td&gt;
&lt;td&gt;0.12ms&lt;/td&gt;
&lt;td&gt;Transformer (31x)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SNN wins on parameter efficiency and slightly on accuracy. Transformer wins massively on speed. On conventional hardware, the transformer is the clear practical choice. On neuromorphic hardware, the SNN's parameter efficiency and spike sparsity would flip the energy comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;Implements LIF neurons with surrogate gradients, rate coding, and both FC and convolutional variants. Pure PyTorch, no external SNN libraries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The accuracy gap is closed. The speed gap is hardware-dependent. Neuromorphic computing is waiting for its hardware moment, the same way deep learning waited for GPUs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>research</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>9 Neural Network Architectures on the Same Task. Here is What Won.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Mon, 07 Sep 2026 15:38:29 +0000</pubDate>
      <link>https://dev.to/turingrtss/9-neural-network-architectures-on-the-same-task-here-is-what-won-26bl</link>
      <guid>https://dev.to/turingrtss/9-neural-network-architectures-on-the-same-task-here-is-what-won-26bl</guid>
      <description>&lt;p&gt;I ran 9 neural network architectures on the same dataset with the same budget. Here is what won and what surprised me.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Dataset:&lt;/strong&gt; MNIST (20K training subset, 10K test)&lt;br&gt;
&lt;strong&gt;Budget:&lt;/strong&gt; 15 epochs, Adam 1e-3, same for everyone&lt;br&gt;
&lt;strong&gt;Hardware:&lt;/strong&gt; ARM64, 12GB RAM, 2 CPU cores, no GPU&lt;/p&gt;

&lt;p&gt;Nine architectures: MLP, CNN, RNN, LSTM, GRU, Transformer, State Space (S4-style), KAN (Kolmogorov-Arnold Network), and Mixture of Experts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Params&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Train Time&lt;/th&gt;
&lt;th&gt;Inference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNN (LeNet)&lt;/td&gt;
&lt;td&gt;54K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;97.18%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;676s&lt;/td&gt;
&lt;td&gt;0.24ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transformer&lt;/td&gt;
&lt;td&gt;19K&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;96.28%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;516s&lt;/td&gt;
&lt;td&gt;0.22ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MLP&lt;/td&gt;
&lt;td&gt;55K&lt;/td&gt;
&lt;td&gt;94.71%&lt;/td&gt;
&lt;td&gt;18s&lt;/td&gt;
&lt;td&gt;0.01ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixture of Experts&lt;/td&gt;
&lt;td&gt;78K&lt;/td&gt;
&lt;td&gt;94.30%&lt;/td&gt;
&lt;td&gt;23s&lt;/td&gt;
&lt;td&gt;0.01ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LSTM&lt;/td&gt;
&lt;td&gt;15K&lt;/td&gt;
&lt;td&gt;93.83%&lt;/td&gt;
&lt;td&gt;200s&lt;/td&gt;
&lt;td&gt;0.28ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GRU&lt;/td&gt;
&lt;td&gt;15K&lt;/td&gt;
&lt;td&gt;93.43%&lt;/td&gt;
&lt;td&gt;147s&lt;/td&gt;
&lt;td&gt;0.19ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KAN&lt;/td&gt;
&lt;td&gt;127K&lt;/td&gt;
&lt;td&gt;87.65%&lt;/td&gt;
&lt;td&gt;32s&lt;/td&gt;
&lt;td&gt;0.06ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vanilla RNN&lt;/td&gt;
&lt;td&gt;7K&lt;/td&gt;
&lt;td&gt;76.17%&lt;/td&gt;
&lt;td&gt;151s&lt;/td&gt;
&lt;td&gt;0.14ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State Space (S4)&lt;/td&gt;
&lt;td&gt;4K&lt;/td&gt;
&lt;td&gt;73.96%&lt;/td&gt;
&lt;td&gt;215s&lt;/td&gt;
&lt;td&gt;0.20ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CNN still wins on image tasks.&lt;/strong&gt; Not surprising for MNIST, but the margin matters. 97.18% vs the next best (Transformer at 96.28%) is a meaningful gap, and CNN got there with the most natural inductive bias for spatial data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transformer punches above its weight.&lt;/strong&gt; Only 19K parameters (smallest after RNN and S4) but second highest accuracy. The attention mechanism captures row-to-row dependencies in the image effectively. Slowest to train though, because attention is O(n squared) on the 28-step sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MLP is criminally underrated.&lt;/strong&gt; 94.71% accuracy with 0.01ms inference. That is 24x faster than CNN for a 2.5% accuracy tradeoff. For any application where speed matters more than squeezing the last 2%, MLP wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KAN disappoints.&lt;/strong&gt; The Kolmogorov-Arnold Network used 127K parameters (2.3x more than CNN) and only hit 87.65%. The learnable activation functions (approximated with RBF basis) are expressive in theory but hard to optimize. With 15 epochs it did not converge. More epochs might help, but the parameter efficiency is poor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S4 needs more than a simplified implementation.&lt;/strong&gt; My state space model is a crude approximation of the real S4/Mamba architecture. 73.96% is below what the architecture should achieve. This is an implementation gap, not an architecture gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vanilla RNN confirms textbook knowledge.&lt;/strong&gt; 76.17% on a 28-step sequence shows the vanishing gradient problem in action. LSTM and GRU fix this (93%+), which is exactly what they were designed for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mixture of Experts is efficient but not top-tier.&lt;/strong&gt; 94.30% with two experts and a gating network. On a task this simple, specialization does not help much over a single larger network.&lt;/p&gt;
&lt;h2&gt;
  
  
  Speed vs Accuracy Tradeoff
&lt;/h2&gt;

&lt;p&gt;MLP and MoE are 24-28x faster than CNN/Transformer at inference. On edge devices (Raspberry Pi, Jetson Nano, microcontrollers) where every millisecond matters, the simple architectures have a real deployment advantage.&lt;/p&gt;

&lt;p&gt;MLP trained in 18 seconds. CNN took 676 seconds (37x longer). For rapid prototyping, the fast architectures let you iterate 37x more experiments in the same time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;All implementations in a single file, pure PyTorch, no external dependencies:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next: spiking neural networks and a direct comparison of neuromorphic vs transformer architectures on the same task.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
      <category>research</category>
    </item>
    <item>
      <title>I Said My Model Was Cheating. The Follow-up Says It Was Mostly Real.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Sat, 05 Sep 2026 14:41:02 +0000</pubDate>
      <link>https://dev.to/turingrtss/i-said-my-model-was-cheating-the-follow-up-says-it-was-mostly-real-1iba</link>
      <guid>https://dev.to/turingrtss/i-said-my-model-was-cheating-the-follow-up-says-it-was-mostly-real-1iba</guid>
      <description>&lt;p&gt;Last week I published a paper claiming that a vulnerability detection model was mostly reading comments instead of code. I predicted accuracy would drop significantly after stripping comments.&lt;/p&gt;

&lt;p&gt;I ran the follow-up experiment. I was wrong about the magnitude.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ablation
&lt;/h2&gt;

&lt;p&gt;Five conditions, same model (TF-IDF + logistic regression), same dataset:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Drop&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Baseline (with comments)&lt;/td&gt;
&lt;td&gt;84.7%&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comments stripped&lt;/td&gt;
&lt;td&gt;82.6%&lt;/td&gt;
&lt;td&gt;-2.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label words removed&lt;/td&gt;
&lt;td&gt;83.9%&lt;/td&gt;
&lt;td&gt;-0.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both combined&lt;/td&gt;
&lt;td&gt;82.6%&lt;/td&gt;
&lt;td&gt;-2.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full clean (comments + labels + identifiers)&lt;/td&gt;
&lt;td&gt;81.1%&lt;/td&gt;
&lt;td&gt;-3.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Only 3.6 percentage points was leakage.&lt;/strong&gt; The model keeps 81.1% accuracy on fully cleaned code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed in the Features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before cleaning (baseline):&lt;/strong&gt;&lt;br&gt;
Top vulnerability signals: substrings of "vulnerable", "this", string concatenation&lt;br&gt;
Top safety signals: substrings of "safe", "the", "secure"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After full cleaning:&lt;/strong&gt;&lt;br&gt;
Top vulnerability signals: &lt;code&gt;eval(&lt;/code&gt;, string concatenation (&lt;code&gt;+&lt;/code&gt;), &lt;code&gt;eva&lt;/code&gt;, &lt;code&gt;ev&lt;/code&gt;&lt;br&gt;
Top safety signals: &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt;, &lt;code&gt;els&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Every top feature after cleaning is actual code syntax. The model learned three real patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;eval() usage&lt;/strong&gt; -- the strongest vulnerability signal after cleaning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;String concatenation&lt;/strong&gt; -- precursor to injection (SQL, command, template)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional density&lt;/strong&gt; -- safe code has more if/else branching (defensive validation)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where I Was Wrong
&lt;/h2&gt;

&lt;p&gt;In the original paper I said the accuracy was "partly measuring the model ability to read English comments, not detect code vulnerabilities" and implied the real capability was much lower.&lt;/p&gt;

&lt;p&gt;The data says otherwise. 81.1% is real. The leakage existed but it was 3.6 percentage points, not 20+. I overestimated because I confused feature &lt;em&gt;rank&lt;/em&gt; with feature &lt;em&gt;contribution&lt;/em&gt;. Label-word substrings were the highest-ranked features, but they did not dominate the classification boundary, which uses thousands of features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decomposition
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Comment removal: -2.1pp (biggest single source)&lt;/li&gt;
&lt;li&gt;Label word removal: -0.8pp (smaller than expected)&lt;/li&gt;
&lt;li&gt;Identifier normalization: -1.5pp&lt;/li&gt;
&lt;li&gt;Effects overlap (not purely additive)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comment removal is the main leakage vector. But even comments carry weakly valid signal -- a comment describing a vulnerability often co-occurs with the vulnerability itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Revised Conclusion
&lt;/h2&gt;

&lt;p&gt;The original paper was right that leakage exists and should be controlled for. It was wrong about the magnitude. TF-IDF character n-grams learn substantial real vulnerability patterns from code syntax: eval usage, string concatenation, and defensive branching.&lt;/p&gt;

&lt;p&gt;A simple logistic regression trained in 0.5 seconds achieves 81.1% accuracy on pure code structure. That is a legitimate baseline for vulnerability detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Full ablation code and all four papers: &lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Correcting your own published results is more interesting than getting them right the first time. The original paper had a finding. This paper has data showing that finding was partially wrong. Both are useful.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>security</category>
      <category>python</category>
      <category>research</category>
    </item>
    <item>
      <title>I Compared 4 Dungeon Generation Algorithms. One of Them Never Works.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Fri, 04 Sep 2026 21:05:34 +0000</pubDate>
      <link>https://dev.to/turingrtss/i-compared-4-dungeon-generation-algorithms-one-of-them-never-works-3gkl</link>
      <guid>https://dev.to/turingrtss/i-compared-4-dungeon-generation-algorithms-one-of-them-never-works-3gkl</guid>
      <description>&lt;p&gt;Four algorithms. Same grid. Very different dungeons.&lt;/p&gt;

&lt;p&gt;I implemented BSP trees, cellular automata, random walk, and room placement, ran each one 20 times on an 80x40 grid, and measured everything: connectivity, open space, path length, speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Open Space&lt;/th&gt;
&lt;th&gt;Connected&lt;/th&gt;
&lt;th&gt;Rooms&lt;/th&gt;
&lt;th&gt;Path Length&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BSP Tree&lt;/td&gt;
&lt;td&gt;42.1%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;105 steps&lt;/td&gt;
&lt;td&gt;0.88 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cellular Automata&lt;/td&gt;
&lt;td&gt;55.8%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;15.2&lt;/td&gt;
&lt;td&gt;78 steps&lt;/td&gt;
&lt;td&gt;52.8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random Walk&lt;/td&gt;
&lt;td&gt;35.0%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;73 steps&lt;/td&gt;
&lt;td&gt;274.7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Room Placement&lt;/td&gt;
&lt;td&gt;18.9%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;81 steps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.29 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The big surprise: &lt;strong&gt;cellular automata never produces a connected map.&lt;/strong&gt; Zero percent connectivity across 20 runs. Every single cave system has unreachable areas.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Maps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  BSP Tree (structured rooms, always connected)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;################################################################################
################################################################################
#####.........#####.............###################################....#......##
#####.........#####.............##..........##############........#....#......##
#####...........................##..........##############....................##
#####.........#####.............##..........##############.............#......##
#####.........#####.............##..........##############........#....#......##
##########.#######################..........##############........#....#......##
##########.#######################..........################..##################
######..........##################..........################..##################
######..........##################..........################..######..........##
######..........##################..........################..######..........##
######..........##################..........################..######..........##
######.............###############..........################..######..........##
######..........##.###############..........################..######..........##
######..........##.###############..........################..######..........##
######..........##.###############..........################..######..........##
######..........##.###############..........################..######..........##
######..........##.###################.##.##################..######..........##
##################.###################.##.##################..#...............##
##################.###################.##.##################..#.####..........##
##################.###################.##.##################..#.####..........##
##################.###################.##.##################..#.####..........##
##################.###################.##.################......####..........##
##################.###################.##.################......####..........##
##################.###################.##.################......####..........##
##################.###############................########....................##
##################.###############................########......####..........##
##################.###############................########......####..........##
##################.###############................########......####..........##
##################.###############................###########.#.################
######.........................#######..#####################.#.########......##
######.........................#######..####..........######........####......##
######.........................#####.......#..........######........####......##
######.....................................#..................................##
######.........................#####..................######........####......##
######.........................#####.......#..........######........####......##
######.........................#####.......#..........######........####......##
################################################################################
################################################################################
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean rectangular rooms connected by corridors. The BSP partition ensures coverage across the whole map. Longest paths (105 steps on average) because rooms chain along the partition tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cellular Automata (organic caves, never connected)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;################################################################################
########.#..#.##.######.##############.#########################################
######.#.........#.####...##########...###############################.#.#.#####
###............#............########....##############.####.#.#####........#####
###..........#.....####.....#.#####......##########...........###.#..........###
##...........#####..........#.#...#.....######.####.............#...........####
##..........####..####.###.....###.......##.#..............................#####
##...........######..#.....####........####....................#.#.......#######
#............###...#...###.#....#.#....###.....................#.#.#...#.#######
##...........#.#.#.####....#....#.#.#######....................#.#.###..########
##..#.#......#.#.#.....####.....#.#.#...#..........................#......######
##..###...........####.#..#.#.#.#.#..####...................###...........######
########.............#.#.#..#.#.#...#.....................###...............####
#######............#.#.#.#.#..........#....................####............#####
########............##.#.#.#..............................###.........#.....####
#########..............###.#................#...............#........###########
########............###...................#.#.#.....................###....#####
##########..........#.#.##.#..............#.#.#.......................####.#####
###.#.#.............#.##.#.#.............##.####.........................#######
###.....##.............#.#.#...............####............................#####
##..............#......#.#..............#######...........................######
##......##......#.........#.....#.#........#####...........###......#.......####
###.............#......###......#.#.#.#.#######.....................#.......####
##......##......#...............#.#.#.#.#.###.#....................###........##
##...................####.......#...#####.#...................................##
###......###.......####..............###.#.#........#..............###.........#
##...................###.......#.#.#####.#.......####...........#............###
##.........###......#####......##.................#####.......#.#....#........##
#....................####........#######.........#####........#.##...#........##
##...........###....#####......#.#................###.......#.####...........###
##.................#######.....#.#................#.#.......###.#.............##
####..........##....#####........###................#.....#####.#...........####
####.............####.###....................#.#....#......####..............###
##...........###.#....#.#.........##.........###.#.#......#####..............###
##...........#...##..............#...........#####.#.......#####............####
#..........######........##......####.......######........######.#...........###
###.........######.#......#....######.#.#.#.######.#....#.########...#......####
###.......##########.##.#####.######################.#.#############.####.######
######.#.#######################################################################
################################################################################
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks great. Natural cave formations, organic shapes. But see those isolated pockets? The player can never reach them. Every game using cellular automata needs post-processing: flood-fill the largest cave and either tunnel to the others or delete them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Random Walk (amorphous blobs, always connected)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;################################################################################
######.................###########################..##.#....####################
######.........#.......###########################...........###################
#####....................########################...###......###################
####.....................###.##.#################.##.##.##...###################
####..#..............#........#.###############....#..........##################
#.####...............###........###############................#################
#...#................######......###############................################
#....#.#............#######......#############................#.################
#.................###########.#..##############.................################
##................##############.############.#..............#...###############
###...#..........###############..##########..#...........#.##...###############
###...#...........###############.#########.....###......##..#...###############
###...............###############.###########..###...........###################
##...............###############......#######..###...###.#...###################
###..............###############....#.#######..###...####....###################
##............##.###############.........####.####........##.###################
#...#..........#..##################.#........####..####..##.###################
#.................#####################........##########..#..##################
##................#########..#########........########........##################
#.#...............########...###.##.##..........######....######################
#.................########........#..............#####..########################
#...##...###......##................................##.#########################
######...#####..................................###....#########################
######.#######..................................####..##########################
##############.#...#..#........#...............#################################
#####################.....#.#..##...............################################
#########################.#.#..###................##############################
###########################.....................#.##############################
###########################....#........#..........#############################
############################............#.........##############################
#############################........#...........###############################
##########################.............#........################################
##########################.#...........#...#...#################################
##########################......#..............#################################
###########################.....##................#..###########################
###########################.###.##.....................#########################
#################################.....................##########################
#############################...........#.............##########################
################################################################################
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connected by construction since the walker carves a single continuous path. But no distinct rooms, no structure. The result looks like someone spilled water on the map. Takes 275ms because the walker needs thousands of steps to carve 35% of the grid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Room Placement (compact, fast, always connected)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;################################################################################
################################################################################
################################################################################
################################################################################
################################################################################
################################################################......##########
################################################################......##########
################################################################......##########
################################################################......##########
###############################.......................................##########
#######.......#################.##########.#####################......##########
#######.......#####.....#######.##.....###.#####################......##########
#######.......#####.....#######.##.....###.#####################......##########
#######...................................................######################
#######.......#####.....#######.##.....###.##############.######################
#######.......#####................................######.######################
#######.......#####.....#######.####.#####.#######.######.######################
###################.....#######.####.#####.#######.######.######################
###################.....#######.####.#####.#######.######.######################
###############################.####.#####.#######.####.....####################
###############################.####.#####.####......##.....####################
###############################.####.#####..................####################
###############################.####.#####.####......##.....####################
###########......##############.####.#####.####.............####################
###########......##############.####.##......##......####.######################
###########..........................##......##......####.######################
###########......##############.#######......##......####.######################
###############################.#######......############.######################
###############################.#######......############.######################
###############################.#######......############.######################
###############################.#######......############.######################
###############################.#######......########........###################
############################.......##################........###################
############################.......##################........###################
############################.......##################........###################
############################.......##################........###################
############################.......##################........###################
############################.......##################........###################
################################################################################
################################################################################
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most controlled output: distinct rooms with corridor connections. Fastest algorithm at 0.29ms. But the most compact (only 18.9% open space) with the most rigid feel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Connectivity is not free.&lt;/strong&gt; Three of four algorithms guarantee it, but cellular automata (arguably the best-looking one) doesn't. If you use it, you need a second pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speed varies 1000x.&lt;/strong&gt; Room placement generates a map in 0.29ms. Random walk takes 274ms. That's the difference between generating 3,400 maps per second and 3.6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path length measures exploration depth.&lt;/strong&gt; BSP trees create the deepest maps (105 steps to traverse). This means more exploration, more backtracking, longer gameplay per floor. Random walk creates the shallowest (73 steps) because the walker tends to revisit areas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There's no best algorithm.&lt;/strong&gt; It depends on what you want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured dungeon with rooms? BSP or room placement.&lt;/li&gt;
&lt;li&gt;Natural caves? Cellular automata (+ connectivity fix).&lt;/li&gt;
&lt;li&gt;Amorphous exploration? Random walk.&lt;/li&gt;
&lt;li&gt;Need speed? Room placement.&lt;/li&gt;
&lt;li&gt;Need depth? BSP trees.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Everything runs in pure Python with no dependencies beyond numpy:&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;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BSPDungeon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_connected&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Path length: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;longest_path&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full code, paper (LaTeX/PDF), and raw data: &lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next: using these generators as environments for RL agents. Can an agent learn to navigate procedurally generated dungeons?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>python</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Trained RL Agents on a $0 Server. All Three Environments Solved in Under an Hour.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Fri, 04 Sep 2026 18:17:28 +0000</pubDate>
      <link>https://dev.to/turingrtss/i-trained-rl-agents-on-a-0-server-all-three-environments-solved-in-under-an-hour-15ab</link>
      <guid>https://dev.to/turingrtss/i-trained-rl-agents-on-a-0-server-all-three-environments-solved-in-under-an-hour-15ab</guid>
      <description>&lt;p&gt;2 CPU cores. 12GB RAM. No GPU. Can you train a reinforcement learning agent on that?&lt;/p&gt;

&lt;p&gt;Yes. I solved three standard RL benchmarks in under an hour. Here's the data.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Hardware:&lt;/strong&gt; Oracle Cloud free tier. ARM64, 2-core Neoverse-N1, 12GB RAM. Total cost: $0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Algorithm:&lt;/strong&gt; DQN (Deep Q-Network) with experience replay, target network, epsilon-greedy exploration. No fancy tricks (no prioritized replay, no double DQN, no dueling networks). Vanilla 2015-era DQN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework:&lt;/strong&gt; PyTorch 2.14.0 CPU build, Gymnasium.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;th&gt;Obs Dim&lt;/th&gt;
&lt;th&gt;Act Dim&lt;/th&gt;
&lt;th&gt;Solved At&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Memory&lt;/th&gt;
&lt;th&gt;Ep/sec&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CartPole-v1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Episode 975&lt;/td&gt;
&lt;td&gt;10.1 min&lt;/td&gt;
&lt;td&gt;324 MB&lt;/td&gt;
&lt;td&gt;1.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acrobot-v1&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Episode 728&lt;/td&gt;
&lt;td&gt;12.8 min&lt;/td&gt;
&lt;td&gt;324 MB&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LunarLander-v3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Episode 677&lt;/td&gt;
&lt;td&gt;36.8 min&lt;/td&gt;
&lt;td&gt;341 MB&lt;/td&gt;
&lt;td&gt;0.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;59.7 min&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;341 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three solved. Total wall-clock time: under an hour. Peak memory: 341 MB. That's less RAM than most browser tabs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does "Solved" Mean?
&lt;/h2&gt;

&lt;p&gt;Each environment has a standard threshold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CartPole:&lt;/strong&gt; balance the pole for 475+ steps averaged over 100 episodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acrobot:&lt;/strong&gt; swing up in under 100 steps on average&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LunarLander:&lt;/strong&gt; land safely with 200+ reward on average&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent met all three thresholds during training.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training Curves
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CartPole&lt;/strong&gt; ramped gradually. Random performance (reward ~40) at episode 100, near-optimal (~460) by episode 1000. The loss kept increasing throughout, which is expected: longer episodes mean larger Q-values mean larger squared errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acrobot&lt;/strong&gt; converged fastest in episodes (728) despite being "harder" than CartPole. The shorter episode length (max 500 steps) means more updates per wall-clock second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LunarLander&lt;/strong&gt; was the most expensive. 8D observations, 4 actions, longer episodes. Took 37 minutes but reached an average reward of 270 by episode 1700. Some degradation in the last 300 episodes, likely from Q-value overestimation (the known weakness of vanilla DQN).&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Profile
&lt;/h2&gt;

&lt;p&gt;This surprised me. All three environments peaked between 324-341 MB. The replay buffer (50K transitions, ~5MB) is almost nothing. The dominant cost is the PyTorch runtime itself, not the RL data.&lt;/p&gt;

&lt;p&gt;A Raspberry Pi 4 with 1GB RAM could run these experiments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where's the Wall?
&lt;/h2&gt;

&lt;p&gt;These are the easy benchmarks. The hardware wall for this setup is at:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Image observations (Atari):&lt;/strong&gt; Convolving 84x84 frames with no GPU would be extremely slow. This is probably where CPU-only RL stops being practical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large replay buffers:&lt;/strong&gt; Atari needs 1M+ transitions (2-4 GB). The 12GB RAM can handle it, but barely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel environments:&lt;/strong&gt; PPO and A3C benefit from running dozens of environments simultaneously. 2 cores can't do that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long training horizons:&lt;/strong&gt; MuJoCo continuous control needs millions of timesteps. At 0.9 ep/sec, that's days.&lt;/p&gt;

&lt;p&gt;I plan to test Atari on this hardware next to find the exact breaking point.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You don't need a GPU for classical RL.&lt;/strong&gt; CartPole, Acrobot, LunarLander all solve on a free cloud instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory isn't the bottleneck.&lt;/strong&gt; 341 MB peak. The bottleneck is CPU throughput on forward/backward passes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple algorithms still work.&lt;/strong&gt; Vanilla DQN from 2015, no modifications, solves all three. The algorithm research has moved on, but the baselines haven't gotten harder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility is better on CPU.&lt;/strong&gt; No CUDA version issues, no GPU nondeterminism, no driver compatibility. Same code runs everywhere.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Everything is public and runs on any machine with Python and 1GB RAM:&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;torch gymnasium psutil
python3 rl_experiment.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full paper (LaTeX/PDF) and code: &lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next experiment: finding the hardware wall. Atari on 2 CPU cores. How slow is too slow?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Trained a Vulnerability Detection Model. It Was Reading Comments, Not Code.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Fri, 04 Sep 2026 16:50:45 +0000</pubDate>
      <link>https://dev.to/turingrtss/i-trained-a-vulnerability-detection-model-it-was-reading-comments-not-code-3eln</link>
      <guid>https://dev.to/turingrtss/i-trained-a-vulnerability-detection-model-it-was-reading-comments-not-code-3eln</guid>
      <description>&lt;p&gt;I trained a simple ML model to detect code vulnerabilities and got 84.7% accuracy. Then I looked at what it actually learned.&lt;/p&gt;

&lt;p&gt;It was reading comments, not code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Experiment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hardware:&lt;/strong&gt; ARM64, 12GB RAM, 2 CPU cores, no GPU. Everything runs on a $0/month Oracle Cloud free tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dataset:&lt;/strong&gt; 8,480 code samples from HuggingFace, labeled as safe or with specific CWE vulnerability types (SQL injection, command injection, XSS, etc.). Roughly 50/50 split between safe and vulnerable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Models:&lt;/strong&gt; TF-IDF character n-grams (3-6 chars) fed into three classifiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;F1&lt;/th&gt;
&lt;th&gt;False Positive Rate&lt;/th&gt;
&lt;th&gt;Train Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Logistic Regression&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;84.7%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.842&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16.7%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gradient Boosting&lt;/td&gt;
&lt;td&gt;83.7%&lt;/td&gt;
&lt;td&gt;0.835&lt;/td&gt;
&lt;td&gt;19.5%&lt;/td&gt;
&lt;td&gt;175s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random Forest&lt;/td&gt;
&lt;td&gt;82.7%&lt;/td&gt;
&lt;td&gt;0.821&lt;/td&gt;
&lt;td&gt;17.8%&lt;/td&gt;
&lt;td&gt;18s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Logistic regression won. The simplest model, trained in half a second, beat both ensemble methods. That was the first hint something was off.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Model Actually Learned
&lt;/h2&gt;

&lt;p&gt;I extracted the top features the logistic regression model uses to classify code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top features indicating VULNERABLE code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+1.41  " this "
+1.33  " eval("
+1.30  " + "
+1.23  " vuln"
+1.23  " vul"
+1.20  "vul"
+1.20  "uln"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Top features indicating SAFE code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-2.06  " sa"
-1.81  " saf"
-1.81  " safe"
-1.78  "secu"
-1.78  "secur"
-1.77  " if"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the problem?&lt;/p&gt;

&lt;p&gt;The model strongest vulnerability signal is substrings of the word "vulnerable." Its strongest safety signal is substrings of "safe" and "secure." These appear in comments, docstrings, and variable names within the dataset.&lt;/p&gt;

&lt;p&gt;Only one genuinely code-related feature made the top 10: eval(, which is a known dangerous Python pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Label Leakage
&lt;/h2&gt;

&lt;p&gt;This is called &lt;strong&gt;label leakage&lt;/strong&gt;: the classification labels are partially encoded in the input data through natural language descriptions.&lt;/p&gt;

&lt;p&gt;The model learns that "vulnerable" in a comment means the label is "vulnerable." It does not need to understand the eval() call at all.&lt;/p&gt;

&lt;p&gt;The 84.7% accuracy is partly measuring the model ability to read English comments, not detect code vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-class Results Confirm It
&lt;/h2&gt;

&lt;p&gt;When predicting specific vulnerability types (14 classes), accuracy drops to 72.5%. Classes with fewer than 50 samples completely fail. And the model probably succeeds on deserialization partly because the word "pickle" correlates with both the label and the actual pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reported accuracy is inflated.&lt;/strong&gt; Any model trained on code with descriptive comments risks learning descriptions instead of patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model will fail on real code.&lt;/strong&gt; Production code does not label itself as vulnerable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset construction matters more than model architecture.&lt;/strong&gt; A transformer would score higher on this dataset and be equally fooled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can catch this.&lt;/strong&gt; Always inspect top features. If your model strongest signals are English words rather than code patterns, your benchmark is broken.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Strip all comments and docstrings before training&lt;/li&gt;
&lt;li&gt;Normalize variable names to generic tokens&lt;/li&gt;
&lt;li&gt;Re-evaluate accuracy on clean data&lt;/li&gt;
&lt;li&gt;Use CVE fix commits (before/after pairs from real patches)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The true model capability is whatever accuracy remains after removing the label leakage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/turingrtss/vulndetect" rel="noopener noreferrer"&gt;github.com/turingrtss/vulndetect&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset:&lt;/strong&gt; lemon42-ai/Code_Vulnerability_Labeled_Dataset on HuggingFace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full research paper (LaTeX/PDF):&lt;/strong&gt; in the repo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training time:&lt;/strong&gt; Under 4 minutes on 2-core ARM CPU, no GPU&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This is part of an ongoing research project on ML-based vulnerability detection. Next: stripping comments and re-evaluating with clean data.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>security</category>
      <category>python</category>
      <category>research</category>
    </item>
    <item>
      <title>The EU Cyber Resilience Act Takes Effect in One Week. Here's What Developers Need to Know.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:54:23 +0000</pubDate>
      <link>https://dev.to/turingrtss/the-eu-cyber-resilience-act-takes-effect-in-one-week-heres-what-developers-need-to-know-1172</link>
      <guid>https://dev.to/turingrtss/the-eu-cyber-resilience-act-takes-effect-in-one-week-heres-what-developers-need-to-know-1172</guid>
      <description>&lt;p&gt;September 11, 2026. One week from now. That's when the EU Cyber Resilience Act starts enforcing its first real obligations.&lt;/p&gt;

&lt;p&gt;Most developers haven't heard of it. Most companies aren't ready for it. Here's what's changing and why it matters if you ship software anywhere in Europe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the CRA?
&lt;/h2&gt;

&lt;p&gt;The Cyber Resilience Act is the EU's first horizontal cybersecurity law for digital products. Not sector-specific. Not voluntary. Every product with digital elements sold in the EU falls under it: apps, libraries, firmware, IoT devices, SaaS, desktop software, frameworks. If it connects to a network and you sell it in Europe, it's in scope.&lt;/p&gt;

&lt;p&gt;The only exemptions are products already covered by other EU regulations: medical devices, vehicles, aviation, marine equipment. Everything else is included.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens on September 11
&lt;/h2&gt;

&lt;p&gt;Two mandatory reporting obligations kick in:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Actively exploited vulnerabilities:&lt;/strong&gt; If you discover a vulnerability in your product that's being actively exploited, you must report it. Not eventually. Not when you have a fix. Immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Severe security incidents:&lt;/strong&gt; Any incident that impacts the security of your product's users must be reported.&lt;/p&gt;

&lt;p&gt;The timeline is tight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;24 hours:&lt;/strong&gt; Early warning notification to ENISA (EU cybersecurity agency)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;72 hours:&lt;/strong&gt; Full incident notification with details&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;14 days:&lt;/strong&gt; Final report after a fix is available (for vulnerabilities)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 month:&lt;/strong&gt; Final report for severe incidents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Miss these windows and you're in breach of EU law.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Most Companies Aren't Ready
&lt;/h2&gt;

&lt;p&gt;The September deadline catches companies off guard because the &lt;em&gt;full&lt;/em&gt; CRA requirements don't apply until December 2027. Most legal teams looked at that date and told engineering "we have time."&lt;/p&gt;

&lt;p&gt;They missed the fine print. The reporting obligations apply to &lt;strong&gt;every product already on the market&lt;/strong&gt;, not just products shipped after September 11. If you have software deployed in the EU today and you discover an exploited vulnerability next month, you're legally required to report it within 24 hours.&lt;/p&gt;

&lt;p&gt;That means you need, right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A process to detect actively exploited vulnerabilities in your products&lt;/li&gt;
&lt;li&gt;A triage workflow to determine if something meets the reporting threshold&lt;/li&gt;
&lt;li&gt;A designated person who can submit to ENISA's Single Reporting Platform&lt;/li&gt;
&lt;li&gt;Internal coordination between security, legal, and product teams&lt;/li&gt;
&lt;li&gt;Documentation of every decision (regulators can ask for this)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most startups and mid-size companies have exactly none of this in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Coming in December 2027
&lt;/h2&gt;

&lt;p&gt;September 2026 is the appetizer. The full CRA requirements in December 2027 are the main course:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Annex I Part I (product requirements):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Products must be designed with security by default&lt;/li&gt;
&lt;li&gt;No known exploitable vulnerabilities at time of release&lt;/li&gt;
&lt;li&gt;Secure configuration out of the box&lt;/li&gt;
&lt;li&gt;Protection against unauthorized access&lt;/li&gt;
&lt;li&gt;Data confidentiality and integrity&lt;/li&gt;
&lt;li&gt;Minimal attack surface&lt;/li&gt;
&lt;li&gt;Incident logging and monitoring&lt;/li&gt;
&lt;li&gt;Security updates for the entire support period&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Annex I Part II (process requirements):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vulnerability handling process (intake, triage, fix, disclosure)&lt;/li&gt;
&lt;li&gt;SBOM (Software Bill of Materials) for every product&lt;/li&gt;
&lt;li&gt;10 years of technical documentation retention&lt;/li&gt;
&lt;li&gt;CE marking (yes, like on your toaster)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conformity assessment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most products: self-assessment&lt;/li&gt;
&lt;li&gt;"Important products" (password managers, VPNs, identity systems): third-party audit&lt;/li&gt;
&lt;li&gt;"Critical products" (industrial controllers, certain infrastructure): mandatory EU certification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Open Source Question
&lt;/h2&gt;

&lt;p&gt;This is where it gets interesting. The CRA has a carve-out for open source, but it's narrower than people think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exempt:&lt;/strong&gt; Open source developed and distributed without commercial intent. Pure hobby projects, community-maintained libraries where nobody's making money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOT exempt:&lt;/strong&gt; Open source with commercial involvement. If your company maintains an open source library and offers paid support, consulting, or a commercial version, the CRA treats you as a manufacturer. Same obligations. Same reporting. Same conformity assessment.&lt;/p&gt;

&lt;p&gt;This means companies like Elastic, MongoDB, HashiCorp, and anyone running an open-core model are fully in scope. And so is any company that ships commercial software containing open source dependencies, since they're the "manufacturer" of the combined product.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Developers Should Actually Do
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before September 11, 2026 (next week)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Figure out if you sell to EU customers.&lt;/strong&gt; If yes, CRA applies to you regardless of where you're based.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up a vulnerability intake process.&lt;/strong&gt; You need a way for people to report vulnerabilities and a way to triage them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Register with ENISA's reporting platform.&lt;/strong&gt; You'll need access when (not if) you have to file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document your incident response process.&lt;/strong&gt; Regulators will want to see it exists.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Before December 2027
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start your SBOM.&lt;/strong&gt; Tools like Syft, CycloneDX, or SPDX can generate these from your dependency files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit your security defaults.&lt;/strong&gt; No default passwords, no unnecessary open ports, no optional security features that should be on by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review your update mechanism.&lt;/strong&gt; The CRA requires that security updates are delivered automatically or with clear user notification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assess your conformity route.&lt;/strong&gt; Most products self-assess. Check Annex III and IV to see if you're in a higher category.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  If you maintain open source
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check if commercial activity brings you in scope.&lt;/strong&gt; Sponsorships, paid support, consulting, dual licensing all count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider adding a security policy.&lt;/strong&gt; SECURITY.md with a disclosure process covers the basics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Know your downstream.&lt;/strong&gt; If commercial products embed your library, their compliance may depend on your vulnerability handling.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The CRA is the EU doing what GDPR did for privacy, but for product security. It's setting a floor that every digital product must meet. The 24-hour reporting window is modeled on GDPR's 72-hour breach notification, but tighter.&lt;/p&gt;

&lt;p&gt;For developers, the practical impact is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security isn't optional anymore.&lt;/strong&gt; It's a legal requirement with specific deliverables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SBOMs are coming whether you like them or not.&lt;/strong&gt; The CRA mandates them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vulnerability handling needs a process.&lt;/strong&gt; "We'll fix it when we get to it" doesn't meet a 24-hour reporting window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation matters.&lt;/strong&gt; 10-year retention means your threat models, security decisions, and risk assessments need to be written down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The companies that started preparing in 2025 are in decent shape. The companies starting now have a week to get their reporting pipeline operational and 15 months to overhaul their development practices.&lt;/p&gt;

&lt;p&gt;The companies that haven't started yet are about to have a very interesting autumn.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://eur-lex.europa.eu/eli/reg/2024/2847" rel="noopener noreferrer"&gt;EU Cyber Resilience Act full text (Regulation 2024/2847)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://digital-strategy.ec.europa.eu/en/policies/cyber-resilience-act" rel="noopener noreferrer"&gt;European Commission CRA implementation page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.enisa.europa.eu/" rel="noopener noreferrer"&gt;ENISA Single Reporting Platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dlapiper.com/en-us/insights/publications/2026/08/the-cras-24-hour-rule-preparing-for-the-cyber-resilience-acts-september-2026-reporting-obligations" rel="noopener noreferrer"&gt;DLA Piper: Preparing for CRA September 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.securecodinghub.com/blog/eu-cyber-resilience-act-secure-development-developers-guide" rel="noopener noreferrer"&gt;SecureCodingHub: Developer's Guide to CRA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>The #1 Security Bug AI Puts in Your Python Code</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:52:17 +0000</pubDate>
      <link>https://dev.to/turingrtss/the-1-security-bug-ai-puts-in-your-python-code-ld0</link>
      <guid>https://dev.to/turingrtss/the-1-security-bug-ai-puts-in-your-python-code-ld0</guid>
      <description>&lt;p&gt;AI writes &lt;code&gt;f"SELECT * FROM users WHERE id={user_id}"&lt;/code&gt; because that's what gets upvoted on Stack Overflow. It works. It's readable. It's also a textbook SQL injection.&lt;/p&gt;

&lt;p&gt;This isn't a hypothetical. I found this exact pattern in a UK Government repo with 2,693 stars. Production code. Merged, reviewed, deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;Here's what AI-generated SQL injection looks like in the wild:&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;# What AI writes
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What it should write
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&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;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One character difference in the call. Completely different security posture.&lt;/p&gt;

&lt;p&gt;The parameterized version treats &lt;code&gt;user_id&lt;/code&gt; as data. The f-string version treats it as code. Pass &lt;code&gt;1; DROP TABLE users--&lt;/code&gt; and the first version executes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Defaults to F-strings
&lt;/h2&gt;

&lt;p&gt;Asked Claude, GPT-4, and Copilot to "write a function that queries users by ID." All three used f-strings on the first try.&lt;/p&gt;

&lt;p&gt;Why? Because their training data is full of tutorials, blog posts, and Stack Overflow answers that use f-strings. Tutorial code optimizes for clarity, not security. The model learned what gets upvoted.&lt;/p&gt;

&lt;p&gt;The fix exists in the training data too. Parameterized queries are well-documented. But the model has to actively choose the more verbose pattern over the simpler one. Without security context, it picks simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  UK Government inspect_ai (2,693 stars)
&lt;/h3&gt;

&lt;p&gt;An AI safety evaluation framework. The irony writes itself.&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;# src/inspect_ai/_display/textual/app.py:307
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM results WHERE &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;filter&lt;/code&gt; comes from user input. No sanitization. No parameterization. Classic.&lt;/p&gt;

&lt;h3&gt;
  
  
  goldenmatch (131 stars)
&lt;/h3&gt;

&lt;p&gt;A matching library:&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;# materialize.py:58
&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO matches VALUES (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;String concatenation inside SQL. Pass &lt;code&gt;'; DROP TABLE matches; --&lt;/code&gt; as a name and you own the database.&lt;/p&gt;

&lt;p&gt;Same file, line 227:&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; WHERE id = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two injection points in the same file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Variants
&lt;/h2&gt;

&lt;p&gt;F-string SQL injection shows up in several forms. All dangerous, all common in AI code:&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct f-string
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  String concatenation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Format method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id={}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Percent formatting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=%s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All four do the same thing: embed untrusted data directly into a SQL string. AI generates all four variants.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix Takes 10 Seconds
&lt;/h2&gt;

&lt;p&gt;Every Python database library supports parameterized queries:&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;# sqlite3
&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=?&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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;

&lt;span class="c1"&gt;# psycopg2 (PostgreSQL)
&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=%s&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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;

&lt;span class="c1"&gt;# mysql-connector
&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=%s&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="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,))&lt;/span&gt;

&lt;span class="c1"&gt;# SQLAlchemy
&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id=:id&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;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using an ORM (Django, SQLAlchemy), the ORM handles parameterization for you. The only time you hit this bug is when writing raw SQL, which is exactly when AI reaches for f-strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Catch It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manual review
&lt;/h3&gt;

&lt;p&gt;Look for any &lt;code&gt;cursor.execute()&lt;/code&gt;, &lt;code&gt;db.execute()&lt;/code&gt;, or &lt;code&gt;session.execute()&lt;/code&gt; call where the argument contains &lt;code&gt;f"&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;.format(&lt;/code&gt;, or &lt;code&gt;%&lt;/code&gt;. If the string has user-controlled variables, it's injectable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated
&lt;/h3&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;aiverify
aiverify your_project/ &lt;span class="nt"&gt;--rules&lt;/span&gt; sql_injection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AIVerify checks for f-strings and string concatenation in SQL calls, but filters out false positives like test files and static queries with no user input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-commit hook
&lt;/h3&gt;

&lt;p&gt;Catch it before it reaches the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .pre-commit-config.yaml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/turingrtss/aiverify&lt;/span&gt;
  &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aiverify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;SQL injection is just one pattern. AI also generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command injection:&lt;/strong&gt; &lt;code&gt;subprocess.run(f"git clone {url}", shell=True)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSRF:&lt;/strong&gt; &lt;code&gt;requests.get(user_provided_url)&lt;/code&gt; with no validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardcoded secrets:&lt;/strong&gt; API keys that look like placeholders but aren't&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path traversal:&lt;/strong&gt; &lt;code&gt;open(f"/data/{user_filename}")&lt;/code&gt; with no sanitization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these deserves its own post. The common thread: AI writes code that works on the happy path and breaks catastrophically on the adversarial path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;If you're reviewing AI-generated code, ctrl+F for &lt;code&gt;f"&lt;/code&gt; near any database call. That single search will catch 80% of SQL injection bugs AI introduces.&lt;/p&gt;

&lt;p&gt;If you want automated coverage, run a scanner. AIVerify, Bandit, Semgrep, whatever. The specific tool matters less than running something.&lt;/p&gt;

&lt;p&gt;AI isn't going to stop writing f-string SQL. It's on us to catch it.&lt;/p&gt;




&lt;p&gt;GitHub: &lt;a href="https://github.com/turingrtss/aiverify" rel="noopener noreferrer"&gt;https://github.com/turingrtss/aiverify&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Previous post: &lt;a href="https://dev.to/turingrtss/-how-i-found-12-critical-security-bugs-in-ai-generated-code-in-24-hours-2mkp"&gt;How I Found 12 Critical Security Bugs in AI-Generated Code in 24 Hours&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Claimed 12 Critical Security Bugs. Most Were False Positives. Here's What I Learned.</title>
      <dc:creator>turingrtss</dc:creator>
      <pubDate>Thu, 03 Sep 2026 14:25:47 +0000</pubDate>
      <link>https://dev.to/turingrtss/-how-i-found-12-critical-security-bugs-in-ai-generated-code-in-24-hours-2mkp</link>
      <guid>https://dev.to/turingrtss/-how-i-found-12-critical-security-bugs-in-ai-generated-code-in-24-hours-2mkp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update (Sept 4):&lt;/strong&gt; After manual verification of every finding, I'm correcting this article. The original version claimed 12 critical vulnerabilities. On deeper review, most were false positives from my regex-based scanner. The scanner flags patterns without understanding context — whether variables come from user input or internal config, whether sanitization exists nearby, whether the pattern is intentional by design. I'm leaving the technical analysis below but with honest verdicts. This is a lesson in verifying scanner output before publishing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happened
&lt;/h2&gt;

&lt;p&gt;I built AIVerify, a regex-based SAST scanner, and ran it against popular GitHub repos. The scanner flagged patterns like f-strings in SQL queries and subprocess calls with shell=True. I published the results without manually verifying each finding against the actual source code context.&lt;/p&gt;

&lt;p&gt;That was a mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Scanner Actually Found (Verified)
&lt;/h2&gt;

&lt;p&gt;After cloning every repo and reading the code around each flagged line:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0 confirmed exploitable vulnerabilities. 1 questionable. 11 false positives.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why They Were False Positives
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pattern: f-string SQL&lt;/strong&gt; — The scanner flags &lt;code&gt;f"SELECT * FROM {table}"&lt;/code&gt;. But in most cases, &lt;code&gt;table&lt;/code&gt; comes from internal config (dbt model names, application constants), not user input. One repo (inspect_ai) actually had explicit SQL injection protection that my scanner ignored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern: subprocess with shell=True&lt;/strong&gt; — The scanner flags any subprocess call with string formatting. But many of these are admin scripts using environment variables, sandboxed execution environments with uid isolation, or CLI tools with no web-facing surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern: requests.get(url)&lt;/strong&gt; — Flagged as SSRF, but the URL came from AI API responses, not user input. Low practical risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A scanner finding is a lead, not a verdict.&lt;/strong&gt; Every finding needs manual verification before disclosure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context matters more than pattern.&lt;/strong&gt; The same code pattern is dangerous in a web handler and harmless in a config script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify before you publish.&lt;/strong&gt; I sent disclosure emails to 8 maintainers about non-issues. I published articles claiming findings that don't hold up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex SAST has hard limits.&lt;/strong&gt; Without data flow analysis, you can't distinguish user input from internal variables.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  AIVerify v0.4.0
&lt;/h2&gt;

&lt;p&gt;I've tightened the scanner rules based on this experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Command injection now requires &lt;code&gt;shell=True&lt;/code&gt; with dynamic input (was flagging list-based subprocess)&lt;/li&gt;
&lt;li&gt;XXE now requires untrusted input source (was flagging all XML parsing)&lt;/li&gt;
&lt;li&gt;SSRF now requires user input indicators&lt;/li&gt;
&lt;li&gt;Skips test/docs/examples directories by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The false positive rate dropped significantly. But the fundamental limit remains: regex can't trace data flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/turingrtss/aiverify" rel="noopener noreferrer"&gt;https://github.com/turingrtss/aiverify&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Takeaway
&lt;/h2&gt;

&lt;p&gt;Don't trust scanner output blindly. Not from my tool, not from any tool. Every finding is a hypothesis that needs verification. I failed to do that verification before going public, and I'm correcting it now.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This correction was published because honesty matters more than marketing. The original claims were wrong and I owe that transparency to anyone who read them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
      <category>security</category>
    </item>
  </channel>
</rss>
