August 7, 2026

Possible Causes Of AI Hallucinations (Draft)

Over the past few years, AI has become more and more prevalent in our day to day lives. There are lots of ethical reasons to have issues with AI, but I want to focus on one of the main problems with generative AI: hallucinations. I am going to define a "hallucination" by AI as a response that is factually incorrect or inappropriate for the context that it is in. My hypothesis is that there are three reasons for these, but let me establish some baselines first. 

For the sake of argument, I'd like us to consider current generative AI models as compressed forms of knowledge. Individual tokens can exist in multiple locations within this compressed knowledge. The token "ABC," for example, could live in the portion of the cloud that knows about Sesame Street, the alphabet, Jackson Five lyrics, etc. LLMs condense their training set into probabilistic domains. The number of parameters for a model roughly corresponds to the amount of knowledge compressed into that model. For example, you can get a 12 billion parameter version of Google's Gemma4 model in Ollama. This model is roughly 7.4 gigabytes in size. This is actually a pretty decent model, but when you look at this model, you see that 7.4 gigabytes is really small for what you can do with it

I am also going to recommend the book "Why Machines Learn: The Elegant Math Behind Modern AI" by Anil Ananthaswamy (sponsored link) for a more technical underpinning than I'm going to go into in this post.

The four main root causes that I propose are causing AI hallucinations are the input set, the compression mechanism, the precision of floating point models on computers, and the steps taken to quantize these models. 

First, the input set. Current generative AI models predict what the next likely token is going to be in an output set based on the input that was trained into it. The "temperature" that you pass into a model will give lower likelihood tokens a higher chance of being chosen. A low temperature (e.g. less than 0.3) will result in the highest likelihood of an appropriate response, while a high temperature (e.g. greater than 0.8) will give lower likelihood tokens a chance at coming out. The Gemma4 model linked earlier has a default temperature of 1.0. Since some level of randomness is going to be coming back for any temperature greater than 0.0, the data that the model was trained on will have a massive impact on the potential output set for tokens at any one time. Models that are purpose trained (e.g. coding models) are more likely to have appropriate responses, but they're still going to be limited by the training data. If you train a coding model on nothing but the results of the final projects from a weekend game jam, chances are that the output won't necessarily be high quality. Most code that you find on the internet (mine included) will be of average quality at best, so it's no surprise that you'll find most generated code is middling-to-average at best. The more specific you are in your request, the more likely that the model will end up with higher quality output, but if any amount of "bad code" makes it into the model, there's a chance that "bad code" can still come out. If your input data contains any invalid or malicious input tokens, there's still a chance that can come out, and the more questionable data you train your model on, the more likely that you'll get either invalid data that matches your query or inappropriate data that reinforces bad ideas that you have. 

Second, the compression mechanism. Let's try to look at a bit of an analogue for compression: images. When you are trying to get an image to take up less space on disk or in memory, there are usually only a couple of ways of doing it. You can reduce the resolution (e.g. reduce the resolution from 3840x2160 to 1280x720), or you can use a lossy compression algorithm (e.g. compress a RAW image as a JPEG). In the end, the image gets compressed heavily, but you lose fine detail. The same thing happens when you compress knowledge into a model. Google's Gemma3 model compressed 14 trillion input tokens into a 27 billion parameter model roughly 17GB in size. Assuming an average input token size of 3 bytes, that's still compressing the knowledge contained in those input tokens down to roughly 1% the size of the original input data.  Take a selfie and compress it down to 1% the size of the original image.  You'll still be able to tell that it's you, but a ton of information is lost.

PNG showing compression differences.  The left side is a headshot from a JPEG at 95% quality, while the right side is a JPEG at 1% quality.

While information was lost, you can still tell what the original source was.  The training mechanism used to backpropagate the knowledge is the same.  You are still working with the knowledge, but the fine details can get lost depending on how the training occurs.

Third, the precision of floating point models on computers.  If you've ever worked on video games, you may have started seeing issues when game worlds got large.  The further things got from origin or the larger they got, the more likely you were to start seeing precision issues.  Fabian Sanglard did a great breakdown on the most common floating point format used on PCs.  So why does this matter?  The more knowledge you are encoding into a model, the more space you need to have between your knowledge clusters.  The larger the numbers, the more likely that some of the finer points of data could get dropped out of the window.  This won't matter much at this point given that this is all meant to define a hyperplane in n-dimensional space, but it will definitely start having an issue when we get to our final problem: quantization.

When models are stored in pure floating point land (FP32 or better), they can be very accurate, but memory hungry and slow.  Most models that we use are quantized, meaning that they are brought down to a lower precision version.  All quantized models are lossy, meaning you are losing some level of detail.  Let's look at a common quantization format: NVFP4.  If you go back to Fabian's post, rather than using 32 bits for a number, it is compressing down the entire floating point number to four bits with values [-6, -4, -3, -2, -1.5, -1.0, -0.5, -0.0, 0.0, 0.5, 1.0, 1.5, 2, 3, 4, 6].  For every group of sixteen numbers, there is an additional FP8 scalar saved as well which scales every number within that group either up or down.  There is a lot of min-maxing used in the encoding process with losses measured using RMSE, but in the end, you're still losing some data.  Even when you are able to minimize it down to 1-2%, it's already extra loss on top of a lossy compression algorithm.  It's like saving a lossy JPEG at a lower compression level.  These small errors continue to propagate through the model, through the prompt processing pipeline, though the token generation pipeline, etc.  NVFP4 is currently among the best because it also includes an additional scaling factor per tensor, but as you experiment with lower and lower quantization models, you'll continue to find worse and worse results because just so much fine detail is getting lost.  Most numbers in a model are going to be close to zero or close to the max, but "close to" and "at" are very different ideas.  The quantization algorithm is there to try to minimize data loss, but it can't eliminate it.

So what does all this have to do with hallucinations?  It's in the math.  The input set is indiscriminate in what it encodes, so any stray token could end up in its prediction set, whether appropriate or not.  The compressed knowledge set is lossy, so there's no guarantee that the proper context for that token is present or not.  The temperature of the model could easily be set to where the bad token is included.  The location of that bad token in our knowledge space could be near a precision boundary in its initial training runs.  The quantized model that we are using could shift the token closer to the valid values making it more likely to be used when it shouldn't.  Once the model starts going down an incorrect or hallucinating path, that's all there is to it.

(Note that this is a general hypothesis.  I'm writing this to try to get my thoughts out, and I'm leaving this post marked as a draft as I may come back and re-edit based on additional learnings.)

January 5, 2024

It's 2024

Long time, no typing.

Since my last post in 2022, I left Netflix.  I started at Fanatics Betting & Gaming in May 2022 as a distinguished engineer and I've had some fun projects, but I'm still in a bit of an emotional funk.

I've been working on doing some XNA optimization for RTResolution to try to get around the SpriteBatch batch size limitations.

I've got my house back to myself for the first time since the pandemic started.

I've set my profile to private on Twitter.  I'm on BlueSky as romsteady.bsky.social now.

Going to be traveling to Idaho for the next four days for a family engagement.

March 14, 2022

Silence

I haven't had a lot to say over the past bit.  My employer shifted into games, so I've had to stop working on a lot of games stuff.  Still trying to find the next focus for this blog.

Stay tuned.

Budget Planning

I was recently approached about a position which had a mixed bag for compensation.  Take-home pay would be one-third lower, but total compensation would end up 50% over my current total comp.  I used the opportunity to try to sketch out a rough budget to see what would have to go if I did have my take-home drop by a third each check.

It was a bit of an eye-opening experience.  Realistically, I'm one of the fortunate ones who would be able to withstand a hit that large per check.  There are some elements I'd have to modify about my lifestyle, but most of those involve "mad money" or disposable income.  Those elements wouldn't vanish from my lifestyle, but would be drastically curtailed.

I've got a couple of things that would help mitigate that reduction.  My Kia is scheduled to be done being paid off by May 2022.  My HVAC loan is a large chunk of cash each month, but will be done by end of January 2023.  I've got three large expenses coming up in March, April, and June, but nothing major planned for the rest of the year.

Realistically, I'm considering trying to live with this budget for the next two months out of curiosity.  The money that I'm not going to be spending will be split in two: half going to my emergency savings account, and the other half going towards my largest bills.

June 9, 2020

RTResolution v0.4.0 Troubleshooting

RTResolution will be released to the general public on Friday after being released to patrons early today.  This post will collect the troubleshooting information from early adopters.

The game crashes on launch.
The RTResolution patcher now drops two files in your save game folder: Terraria.exe and RTRHooks.dll.  Make sure you copy both files over to your save game folder.
Update 6/10 7:24pm: Fixed in RTResolution 0.4.2.


The game won't go over 4096 in any one direction.
There are memory constraints within the Terraria process when you start going larger than this.  If you want to risk it, try the 8K experimental build.  I'm aware of at least one crash at higher resolutions due to an issue inside of Terraria with ultra-high resolutions, though.
Update 6/10 7:24pm: Fixed in RTResolution 0.4.1.

June 8, 2020

RTResolution v0.4 Released Tomorrow For Patrons, Friday For Everyone Else

I'll be releasing RTResolution v0.4 tomorrow for patrons.  This has the verified biome fix, works for Terraria v1.4.0.5, and is tested in multimonitor scenarios as well.  This also unlocks me being able to do more mods in the future the way I used to do them in RomTerraria.

The build will be released for everyone sometime on Friday.


For those of you who have been popping on to my Patreon, grabbing builds, then dropping off...I'm tracking that.

May 16, 2020

Values To Update In Terraria.Main

These are the values I need to update in each assembly for "Journey's End" v1.4.0.1 to uncap the zoom without negatively impacting the game experience:

Terraria:
Main.cs:

Update MinimumZoomComparerX to 5120
Update MinimumZoomComparerY to 5120

Update LogicCheckScreenWidth to 5120
Update LogicCheckScreenHeight to 5120

Update maxScreenW to 5120
Update maxScreenH to 5120

TerrariaServer:
Main.cs:

Update maxScreenW to 5120
Update maxScreenH to 5120

Journey's End

Long time, no typing.

Lots of stuff has happened over the last 14 months.  I moved full-time to Las Vegas, my granddaughter moved in with me so she could save cash for her future, the United States has entered an untimely death spiral from which it may never fully recover, I've started donating platelets every week, and Re-Logic released its final patch for Terraria, "Journey's End" this morning.

Here's a heads-up about where we are with RTResolution/RomTerraria and "Journey's End."

Bad side:

I haven't even looked at Terraria source code in the last eight months.  I sat down for a bit and tried to create a tModLoader mod to add in the rain from earlier versions of RTResolution for a request and failed miserably.  I donated platelets this morning and woke up after a three hour nap to a ton of requests to update RTResolution via email, Reddit chat, etc.

Good side:

I'm interested in playing Terraria again.  I got asked to play by a former coworker if I could play once "Journey's End" was released, and the changes look interesting enough to draw me in.  I finished the major work needed for a work project yesterday, and the polish work should be finished by Monday, so I'm about to have a ton of cycles available.

So this is what I'm going to do: tonight, I'm going out for a dinner to celebrate my work project being over.  Tomorrow, I'll get set back up to develop RTResolution, and start working on the mod then.

March 20, 2019

Game Mods Moving To Patreon

Starting next month, I'm going to be able to block time out for dedicated work on game mods, and starting in September, I'm going to have a dedicated room just for my game development work.

Work on new mods is going to move to my Patreon.

The Terraria mod I'm going to be working on is codenamed TSA (Terraria Server Assistant).  Basically, I'm going to be using the Mono.Cecil work that I spearheaded with the Terraria client to expose a lot more information through the server and extend its command support.

The Quake 1/2 work I'm going to be working on involves making mods easier to make in this day and age.

The Hollow Knight mod I'm working on is a rather simple tombstone mod so you'll know every place that you died during your entire run.

The Left 4 Dead 2 work I'm doing involves finishing up some of the unfinished mutations in ShackMutations.

There's no reason to join or donate unless you want early access as all of the mods will be released for free.

March 7, 2019

WTF is happening?

On RomTerraria:  The latest tModLoader should have the same scaling unlocking code that I had in RomTerraria.  At this point, consider RomTerraria discontinued unless something insane happens.

On Electric Eye:  Still working on the open source release, although we've started moving towards EyePatch at work for a more accurate result.  The patent I got screwed up the timing, and I'm still waiting to figure out what's going on with the international patent.

On my life:  Bought a house.  Will be moving first two weeks of September.  Planning on retiring in mid- to late-2030.

February 17, 2018

2020 Goal: A Studio

RomSteady Games Logo
I'm anticipating having to move next summer, so now that I'm finally debt free, I'm actively trying to save every penny that I can so that when I move, I'm going to be able to purchase wherever I move outright.

I'm also working on ensuring that I have enough savings to be able to fund three people for 18 months in a lower-cost portion of the United States to build a game that I have designed.  I may still have to work full-time elsewhere to fund this endeavor, but this is probably my last shot to see if I have what it takes to be in the games industry the way that I want to be.

Obviously, this is leading to me trying to scale back my expenses wherever possible and practical.  As a result, the current Shacknews Slow Jam will probably be the last game jam that I contribute prizes to.

February 11, 2018

Electric Eye Lite - Threading Model

I'm going to quickly go over how the threading model has changed in Electric Eye over the last two years.

Originally, Electric Eye was single-threaded.  The data flow was:

Thread #1: Get Video Frame → Extract Testable Frame → Get Audio Frame → Run Test Case → Update UI → Repeat

For our original purposes (reducing our range from 300ms to 80ms), this was fine, but it didn't scale to more complex test cases.

Our next threading model split in two: our UI/acquisition thread and our test case thread.

Thread #1: Get Video Frame → Extract Testable Frame → Get Audio Frame → Enqueue Frame → Update UI → Repeat
Thread #2: Dequeue Frame → Run Test Case → Repeat

Thread #2 used a consumer model based on a lock-free queue from C++ Concurrency In Action.  However, we started running into issues when we switched over to using UMats in our OpenCV code.  Using GPU resources on thread #2 were impacting our UI and causing frame time issues with our acquisition thread.

Our next threading model had us using three threads: acquisition, test case, and UI.

Thread #1: Get Video Frame → Extract Testable Frame → Get Audio Frame → Enqueue Frame → Repeat
Thread #2: Dequeue Frame → Run Test Case → Repeat
Thread #3: Update UI when possible

An early mistake made with this threading model was that we tried to get the video frame into a UMat at the end of thread #1 to speed up thread #2, but this led to us running into resource starvation issues (you can only have so many GPU resources allocated) and it still caused timing issues.

Our final threading model still has three threads, but we shifted where we extracted the frame.

Thread #1: Get Video Frame → Get Audio Frame → Enqueue Frame → Repeat
Thread #2: Dequeue Frame → Extract Testable Frame → Run Test Case → Repeat
Thread #3: Update UI when possible

We are actively ensuring that thread #1 is not using any GPU resources whatsoever.  We get our video frame if available (with our exposure controls, this takes us ~3-5ms per frame), grab whatever audio came in during this iteration, generate a FrameData object, and enqueue it up in thread #1.

In thread #2, we extract out the testable frame using code similar to the warpPerspective code I spoke about before with one extra perspective fix and my patented curved screen code, turn the frame into a UMat, run the test case against the extracted frame and/or audio object that we have, and if we have any changes to the UI, we signal the UI thread that it needs to update now.

Thread #3 is just a standard UI thread.  It does handle getting commands via IPC from our command-line tool as well, but it just routes them into standard UI commands.

February 4, 2018

Electric Eye Lite - Introduction

It's still going to be some time before I'm going to be able to do a full source release of Electric Eye through work.  Since Electric Eye was revealed to the world, we've done over 100 internal releases and over fifteen new releases to partners.  We've dramatically reduced the error bars in our measurements, fixed a lot of bugs, and in general have made a very stable tool.  As a result, we have a very stable but very messy codebase.

Over the next month, I'm going to be talking through a clean implementation of the non-patented parts of Electric Eye and walk through the creation of what will essentially be "Electric Eye Lite" or "EEL."

Over the next four posts, I'm going to be talking through each of the three threads inside the codebase (data acquisition, testing, UI), the lessons learned over the last two years of working on the tool, and finally bring it all together in a simple, clean codebase.

All the code will be over on Github licensed under BSD 3-clause.

Talk to you soon.

November 8, 2017

Game Jam Entry

For the first time in quite a while, I finished a game jam entry this last weekend.

It's not the most polished or the most balanced, but the important thing is that I actually released it.

I've lost count of the number of half-baked and half-finished prototypes I have lying around, so it was important to me that I actually release something this time.

If you get a chance, please try it and all of the entries to the Shacknews Jam.  So far, I'm partial to "Run Don Run."

I'm going to be out of town from Thursday through Sunday this week.

October 26, 2017

Patent

The first hurdle to releasing Electric Eye as open source has been jumped over: my curved screen correction system received a patent.

Next step is finishing up the current bugfix and getting that into partner's hands, and then I can restart the internal open source process.

September 11, 2017

Dev Machine Down

When I woke up this morning, I noticed that there had been a power outage while I slept.  When I went to turn on my main rig, all the fans started up, but I didn't even get a beep code.  The last time this happened to me, it was because the motherboard had failed.

I've had this machine for about two years, so it was time for me to upgrade anyway.  My new machine should arrive in about a week, but I'm going to be on my Mac Mini for the next week or so.

These things happen.

August 11, 2017

Minor Update

Rejoined the IGDA.

Now have a Unity Plus license for the next year.

Still working on shit.

July 13, 2017

GMTK Game Jam Tomorrow

There's a Game Maker's Toolkit Game Jam starting tomorrow and I'm going to try to livestream my attempt at participating.

I'm going to set up a simple test stream tonight and if all goes well, I'll post the stream links in a new post tomorrow night.  I'll be participating from 6pm PDT Friday through 6pm PDT on Sunday and will put my final bits up on itch.io.

June 29, 2017

RTResolution Next Steps

First, the good news.  It looks like RTResolution v0.2 properly handles all of the stock use cases.  Yay!

Now for the not so good news.  It looks like TModLoader doesn't quite like RTResolution.  If so, not that much of a surprise.  Getting mods to work well together isn't exactly easy.  Ask the Skyrim modding community...

I don't have an extended weekend coming up like most Americans, so I'm going to be spending the next couple of evenings digging into TModLoader and what's going on when it gets patched with RTResolution.  One of three things will happen as a result:
  1. I might realize that getting TModLoader and RTResolution to play nicely together will be too much of a pain in the ass and just punt on working with the mod;
  2. I might find the cause and realize that patching it my way would be a pain in the ass so I'll report the issue as a bug to the TModLoader team; or
  3. I'll find the solution and prepare RTResolution v0.3.
Stay tuned.

Update 6/30/2017 5:06pm: TModLoader moves all of the rendering code over to a method called do_Draw.  The crash is happening because I don't modify do_Draw.

Update 6/30/2017 5:17pm: Testing with TModLoader now. Still getting a crash.  It's looking for something that I don't touch.

Update 6/30/2017 5:34pm: Option #2 it is.  Issue filed with TModLoader team.

June 21, 2017

RTResolution v0.2 Available Now

Download here.

This build does only two things for Terraria 1.3.5.3:
  1. Removes the forced minimum zoom.
  2. Allows resolutions greater than 4096x4096 (memory allowing).
This build doesn't currently work with TModLoader 0.10.0.2.  The compiler version that they are using is newer than the version of Mono.Cecil that I'm using and the two don't place nicely together.  A ticket has been filed with the TModLoader team.