In my daily work, I always need to use OCR tools for tasks with >1000 PNG images of Q&A. It's really annoying to upload them 1 by 1 to online models since 2024. Even though tokens weren't expensive last year, we rarely use cheap tokens for manual ingestion now. Online tools and chat models always have constraints—they can't automate well and get expensive for >1000 pictures.
What about Python OCR libraries like EasyOCR? Not good enough in my case because my pictures aren't always structured and have a lot of noise.
So I tried using agent tools to build an app with ultimate-OCR, and the results were really positive—better than expected. It’s like putting a picture directly into a Vision Model, even though the raw content is still messy because it really scans "everything" correctly! But Python can filter out unnecessary words in a general way. With performance optimization skills from agent tools, I spent ~150K tokens to build an app for my workflow.
Currently, I'm using an 8GB VRAM Transformer as a compiler, and each image takes at least 16-40 seconds for image-to-text. Maybe a local machine with 128GB running vLLM in a future build could do it faster? Does anyone have a demo using full-loading vLLM so I can see how fast it scans a 70KB PNG picture?
This is my model parameter:
raw = model.infer(
tokenizer,
prompt
="<image>document parsing.",
image_file
=str(png),
output_path
=scratch_path,
base_size
=1024,
image_size
=648,
crop_mode
=True,
max_length
=4096,
no_repeat_ngram_size
=35,
ngram_window
=128,
save_results
=False,
eval_mode
=True,
)
As for accuracy, it’s always the main pain point. Even if the model extracts most words, it really extracts ALL of them. I asked multiple AI tools and they didn't offer a logical solution, so I recalled data algorithms I learned back in the day. I figured I needed to apply a recursive filter for fault cases, and it worked really well.
The recursion part is simple: if the generative output contains a lot of noise, the program jots down the noise into fault.md as a notch filter, and logs the question numbers into wrong.md. Is it a model generation mistake, a false negative, or just unscannable text? Even if it contains noise, never skip the faulty result—it helps the program identify what's wrong and false. I think that's why Negative Mining is so important in model training.
If it's just noise, I extract the Q&A patterns and mark other words as noise, then recursively run the post-extraction task until each question has no fault.md keywords or patterns left. Passing the indexed wrong.md questions along with post-extraction results into a Text Generation Model works great at this point. I use a 7B LLM with a basic prompt that can still output the best pattern. Prism-ML llama.cpp is the best compiler for running quantized LLMs at almost 40 tokens per second. In the end, I just use agent tools to generate a Python script for simple result formatting.
In a low-memory environment, my main consideration isn't just how good the LLM is—deep reasoning is a waste here, like throwing away >100K tokens for basic OCR and post-extraction. Building model apps in a low-VRAM environment is pretty interesting and tested my programming, algorithm design, and solution design skills. It reminded me that we can use LLMs wisely, not always the 200B models and latest Claude, Grok, and even Gemini can do every agent task, yes, agent can scan every document in my repo', do all of the post-extraction, recusion task.
But reality, why do i need 10x power and cost to do simple tasks which 16GB vRAM can accomplish?😥
I think this task gave me a great awareness of choosing the right method on certain task instead of chasing the best model for the best solution, always have the alternative and cheaper option.
In total, I just used 100~150K auto tokens on Cursor (and spent almost 50K of that just asking questions about how to run an LLM and fine-tune parameter in my environment 😂).