top of page

Koi identifier Pt.2: LLM training

  • Writer: David Turner
    David Turner
  • Mar 22
  • 11 min read

Updated: Aug 10


Screenshot of an AI image recognition tool for identifying and cataloguing Koi carp
AI image recognition and cataloguing system

Recap of the project so far

In Part 1 of this project, I'd used Claude to vibe-code an app that identified koi varieties and individual fish sightings from uploaded photographs. However it became clear that there were limitations to using a general purpose LLM. Analysis of the same photograph twice would result in different results. So the answer was to train a model specifically for the task, and see if that performed any better.

Summary

  • Purpose: Training a model specifically on images of koi

  • Stack: Google Colab with Nvidia T4, Imagenet ResNet50, TensorFlow

  • Results: 20.8% Val Accuracy, model overfitting

  • Outcome: Not satisfactory fro use; need a much larger and varied data set to train on if I ever wanted to take this to Prod.


Requirements for training the learning model

The basic requirements for this phase of the project were to train a specialised deep-learning model that:


  • Recognises koi breed characteristics

  • Works locally (no API costs after training)

  • Learns from my specific photos

  • Improves with feedback

  • Gives consistent results every time


Design approach for visual recognition model training

The design concept here centred around building a Convolutional Neural Network (CNN) trained on a set of labeled koi photos from my portfolio.


In simplistic terms it would work as follows:


Koi photo

Pre-trained model (ImageNet features)

Fine-tuning layer (learns breed-specific patterns)

Classification layer (outputs breed probability)

Result: 98% confident → e.g. "Kohaku"


In the context of my project, there were two design options to consider.


Option A: Transfer Learning

  • Use pre-trained model (trained on millions of images)

  • Fine-tune on my koi data

  • Requires: 50-200 labeled photos per breed

  • Time: 1-2 weeks

  • Accuracy: 90-95%

  • Difficulty: Intermediate


Option B: Full Training

  • Train from scratch on koi images

  • Requires: 1000+ labeled photos per breed

  • Time: 4-8 weeks

  • Accuracy: 96-99%

  • Difficulty: Advanced


The choice was pretty obvious here - Option A. No way I was going to pull together 1000+ pictures across 12 varieties (plus another 1000 or so 'unclassified' examples).


The datasets

Training a model means you need training data. And for me this meant lots of pictures of koi. A minimum of 50 pictures per variety was required to reach a minimum-acceptable level of accuracy, with 200-300 pictures delivering better results.


There are a couple of ways to do this, and in reality they're all very tedious. However I submitted to the sunken-cost fallacy and decided that since I'd started, I might as well stick out the boredom and complete the task.


Method 1: Run through all my photos, sort them into varieties, and drop them into corresponding folders. I discounted this as (i) the whole purpose of building this project was because I didn't know what varieties they were and (ii) I may have had gaps where photo's didn't exist of certain varieties.


Method 2: Suggested by Claude, was to visit inaturalist.org and search each variety of koi, then download the license-free pictures. It even offered to write a script for me to automate that. One flaw in the plan though is that iNaturalist records sightings of wild animals, not captive ones like koi. So this method was discounted too.


Method 3: Google Images - search for each variety with the correct license settings configured in the search tools. Tedious, but viable and legal.


Method 4: Search forums, community pages and YouTube thumbnails for images. Request permission for use of the pictures for training. Frankly I don't have time to write to hundreds of people asking for permission to use their pics, and the YouTube option didn't sound particularly ethical, so this was out too.


That left Method 3 as the only viable and legally compliant option, and so I commenced with one of the most boring few hours of my life. Not something I want to repeat anytime soon, and not something I'd recommend where your sanity is concerned, or if you'd like to avoid a case of RSI.


BTW: If you ever wondered why review sites ask you to name the things in the pictures you've uploaded, it's because they also don't want to go through this the same tedium (albeit on a much larger scale) so have outsourced the labelling work to you. Enjoy.


Fast forward a couple of hours and I'd got my set of 600+ pics ready across 12 varieties. The experience made worse by listening to Liverpool get beaten by Brighton at the same time. Like I said, not an afternoon to remember.


A quite specific note here on koi as the subject matter; the variety of pictures available is really poor. Most tend to be identical in style - the fish in a collector's or breeder's blue artificial pond or bucket, photographed front-on or top-down. Same lighting, composition, angles, colours, and zero variety. These probably account for around 80-90% of all the pics out there. If I was doing such a project for real, I think the first step would be to take my camera out to Japan and visit every breeder, dealer and koi pond in the country. Probably a nice excuse for a trip, but not practical in the least.

Nevertheless I got them labelled by dropping each variety into it's own separate folder, and moved on.


The AI model training environment

There were a couple of options for how to execute the training:


Option 1: Google Colab

Free Google service, runs in browser, no installation needed.


Pros:

  • Free GPU access

  • No setup required

  • Very intuitive to use


Cons:

  • Session times out after 12 hours

  • Limited free GPU time


Option 2: Local Setup


  • Install locally on machine.


Pros:

  • Best for ongoing use.

  • Uses Python 3.8+


Cons:

  • NVIDIA GPU (recommended, as CPU will run much slower)

  • 10GB free disk space


Since I don't have a spare GPU handy, and aren't prepared to give up 10GB that could be filled with more photos, it was clearly Option 1.


The AI model selected for training

After some back and forth with Claude, we settled on ResNet50 Transfer Learning, based upon the following rationale:


  • Pre-trained on ImageNet (1.2M images, 1000 classes)

  • Learns general visual features (edges, textures, shapes) applicable to any image classification task

  • Moderate size: faster training than ResNet101/152, more capacity than ResNet18

  • Well-documented, battle-tested in production systems

  • Strong performance on image classification benchmarks


Architecture wise this gave us:


Input (224×224×3)

ResNet50 base (pre-trained, frozen)

Global Average Pooling

Dense(256, ReLU)

Dropout(0.5)

Dense(128, ReLU)

Dropout(0.3)

Dense(12, Softmax)

Output (breed probabilities)


Transfer Learning Strategy:

  • Freeze early/middle layers (layer1, layer2) to preserve general features

  • Unfreeze final 20 layers for breed-specific learning

  • Separate optimiser for unfrozen layers (lower learning rate: 0.0001)


Worth mentioning TensorFlow at this juncture; chosen over PyTorch due its robustness with image formats, suitability for transfer learning (by virtue of Keras Sequential) and a generally better error tolerance. Keras itself selected over Functional API due to its simpler architecture and readability.


I've dropped a more comprehensive .MD file into the accompanying repo for those interested.


A note on metrics

Before jumping into the training and results. some explainers on the metrics we're looking for:


Accuracy (%): How many training images the model got right. Usually higher because the model has already seen them.

Validation accuracy (%): How many new, unseen images it classifies correctly. This is the real test.

Loss: How wrong the training predictions are. Lower is better. Should decrease as training progresses.

Speed (s/step): Seconds per batch processed. CPU is slow; GPU is ~35× faster.


In terms of accuracy versus validation accuracy, we can use the exam analogy to illustrate the relative importance:


Accuracy: You score 95% on practice tests you've already studied. The questions look familiar.

Validation accuracy: You score 70% on the real exam with questions you've never seen before. This tells you what you actually learned.


Validation accuracy is what matters. Accuracy can lie to you, whereas validation accuracy tells the truth about real-world performance.


Also worth mentioning epochs at this stage: being a relatively small dataset, a good guideline is to use 10 epochs. During training we'll adjust up or down based upon which of three different patterns we see in the results:


Screenshot of AI training 'still improving' results graph
Screenshot of AI training Plateau results graph
Screenshot of AI training Overfitting results graph

The right number of epochs is however many it takes for validation accuracy to stop improving. If we see over-fitting start to occur (when the model performs much better on training data than validation data - it's memorised the training set instead of learning patterns that work on new data.) then we need to stop early and address the data size, quality and model complexity.


In terms of end results, we're looking for final validation accuracy of:


Metric thresholds for levels of LLM training results

Training: teaching the model to recognise images

Into Google Colab, which is incredibly intuitive for simple projects like this. Even faster and easier when using Claude to generate all the scripts too. First we set up the data path to the zip folder of training data on my desktop, and got that loaded:


Screenshot of the Data path set used to train the LLM in Google Colab
Data path set

Screenshot during the LLM training process confirming that Data was loaded successfully
Data loaded

A couple of minor syntax errors and bugs in the script, and we were making progress.


Now at this stage I have an argument with Claude due to an error we keep getting - Google Colab doesn't like .webp images. Right at the start of the Google Image downloading extravaganza, I explicitly asked Claude if .webp files were ok - it said yes, no problem, we can handle those along with .png and .jpg. I double checked this and it confirmed again 'no worries, in the worst case I'll just write a script to convert them, it's easy'.


So once we get the errors I remind Claude of this, and it does it's 'oh yeh I was wrong, I should have checked' routine. Not much help at this stage, so I told it to write the conversion script. Which didn't work. Over at least 5 attempts. It's best advice after the last failure was 'well we'll just have to use the smaller set of non-webp files then'.


Screenshot of webp issues in Google Colab during the data loading process
Colab doesn't like .webp

Screenshot of AI image recognition Training data set
So we're training with 130 images instead of 648

So I'd wasted most of my afternoon collecting over 600 images and listening to another shambolic Liverpool performance, to be left with less than 200 usable files. Thanks Claude.


A number of other corrupted images were discounted from this set (due to attribute errors in the JPEGImageFile setting) further reducing the dataset. TensorFlow would skip through the corrupted images and use only the good ones.


The training loop itself consisted of 10 epochs: Epoch duration: 21–23 seconds per epoch (GPU)

Total training time: ~35 minutes for 10 epochs

Hardware: Google Colab T4 GPU

Learning rate schedule: StepLR (decay by 0.1 every 3 epochs, not applied in final run)


Results: did the models train successfully?

Undeterred and with the end in sight, I ran the training on those files regardless across 10 epochs. The final results came in as follows:


Screenshot of AI Image recognition model training results
The results

Dataset Summary

```

Breeds: 6 (asagi, bekko, kawarimono, kinginrin, matsuba, metallic)

Total Images: 243 (after automatic validation by TensorFlow)

Training Set: 195 images (80%)

Validation Set: 48 images (20%)

Training Batches: 7 (batch size 32)

Validation Batches: 2

Images per Breed: ~40 average

```


Epoch-by-Epoch Performance

Epoch

Train Accuracy

Train Loss

Val Accuracy

Val Loss

Observation

1.

15.9%

2.190

20.8%

1.852

Random chance initialization

2.

38.5%

1.575

18.8%

1.829

Training improving, val noisy

3.

44.1%

1.430

14.6%

1.818

Divergence beginning

4.

55.4%

1.180

20.8%

1.918

Widening gap

5.

69.2%

0.994

20.8%

2.450

Major divergence

6.

70.3%

0.947

20.8%

4.282

Val loss exploding

7.

73.3%

0.791

20.8%

4.487

Severe overfitting

8.

74.4%

0.731

20.8%

3.071

Memorization complete

9.

80.5%

0.602

20.8%

2.745

Model perfecting on training

10.

83.1%

0.503

20.8%

2.812

Final: Extreme gap (62.3%)


Key Metrics

Final Training Accuracy: 83.08%

Final Validation Accuracy: 20.83%

Accuracy Gap: 62.25 percentage points

Training Loss Trend: Smooth decrease (good)

Validation Loss Trend: Volatile, then increasing (bad)

Validation Loss Ratio: Starts at 1.85, ends at 2.81 (+52% increase)


What the Numbers Mean

Interpretation:

  • The model achieved 83% accuracy on images it had already seen during training

  • On images it had never seen (validation set), accuracy remained at 20.8%—essentially **random guessing for 5 classes** (20% = 1 in 5)

  • Validation loss increased by 52% while training loss decreased, classic overfitting signature

  • The 62-point gap is severe; healthy models show gap < 15 points


Root Cause: With only ~33 training images per breed, the model memorised specific patterns rather than learning generalisable breed characteristics.


Overfitting Analysis


What Happened Epoch-by-Epoch

Epochs 1–3: Learning Phase
  • Training accuracy climbs from 15.9% → 44.1%

  • Validation accuracy fluctuates (20.8% → 18.8% → 14.6%)

  • Model is learning some patterns, but validation is too small to show reliable progress

  • Validation batch size = only 8 images per breed (high variance)


Epochs 4–5: Divergence Begins
  • Training accuracy reaches 69.2%

  • Validation accuracy drops to 14.6%, then returns to 20.8%

  • Gap widens to ~50 points

  • Validation loss rises from 1.8 → 2.45 (35% increase)

  • Model starts memorising training set details


Epochs 6–10: Severe Memorisation
  • Training accuracy continues climbing (70.3% → 83.1%)

  • Validation accuracy stuck at 20.8%

  • Validation loss rises further (2.45 → 4.49 at peak)

  • Model has completely memorised training data

  • Zero transfer learning benefit


Why Early Stopping Would Have Helped:

Best validation accuracy: 20.83% (occurred at epoch 1–3)

Optimal stopping point: Epoch 3

What actually happened: Trained all 10 epochs


If we had used early stopping (patience=3), training would have halted at epoch 3, potentially avoiding the severe memorisation. Even at epoch 3 with 14.6% accuracy, we would have at least not gotten worse.


Diagnosis: Why 20.8% Validation Accuracy?


Factor 1: Insufficient Training Data

The Maths:

  • 6 breeds × ~33 training images = 198 images total

  • Batch size: 32 images

  • Training batches: 7

  • Classes: 6

  • Random chance: 16.7% (1/6)

  • Achieved: 20.8%


The model barely outperforms random guessing. This strongly indicates it learned nothing generalisable.


Benchmark comparison:

  • ImageNet transfer learning: Requires ≥100 images per class

  • Fine-tuning ResNet50: Industry standard minimum

  • My dataset: 33 images per class (1/3 of minimum)


Factor 2: Image Quality & Diversity

While TensorFlow successfully loaded 243 images, quality issues remained:


Potential problems:

  • Variant angles (fish shot from side vs. top vs. front)

  • Lighting variation (sunlit pond, indoor aquarium, murky water)

  • Partial visibility (fish at edge of frame, partly obscured)

  • Breed confusion (WebP conversion may have lost color fidelity; Kohaku's red loses saturation)


ResNet50 is powerful, but with only 33 images per breed, it can't learn robust features across all these variations.


Factor 3: Validation Set Too Small

Challenge:

  • Validation set: 48 images (8 per breed)

  • With 8-image samples, random fluctuation is large

  • Val accuracy could fluctuate wildly between epochs due to chance


Example:

  • Epoch 2: Val accuracy 18.8% (might have gotten fewer breeds correct by chance)

  • Epoch 3: Val accuracy 14.6% (all 8 validation images were hard cases)

  • This noise masks any real learning signal


What would help:

  • Validation set of ≥50 per breed (300+ total)

  • More stable, reliable accuracy measurements

  • Clearer signal about whether model is actually learning


Factor 4: Class Imbalance

While breed distribution is relatively balanced (49–55 images per breed), with only 33 per breed, any slight imbalance matters more.


Conclusion: you need a lot of data to train an AI model

As expected, the training set let me down here. Too many similar pictures, the majority of which were rendered useless once it became apparent all the .webp files were to be discarded.


To make an improvement, I'd be looking in the region of 1000+ images of greater variety, running a quality audit on them prior to data loading (can be automated via Python script).


With the addition of early stopping too, Claude estimates potential results of:


  • Training accuracy: 85–95%

  • Validation accuracy: 75–85%

  • Gap: 10–20 points (healthy)

  • Model size: ~95MB (manageable)


Frankly there's more chance of Elvis riding past on Shergar than me going through the tedium of creating an expanded dataset, never mind training it again and then integrating to the frontend. If I was a tech company and had ways to get hold of huge datasets to create a monetisable product, then sure. But for a personal project it's a bridge too far - I'm happy enough with the Anthropic AI. And I never want to see a picture of a koi for a very long time.


The actual learning experience here was of great value though, and that's what really matters. To get hands-on with training and see how important the variety, quantity and quality of image data is to the end result, watching the epochs churn through on one of Google's Nvidia T4 GPUs brings a huge part of AI to life. Seeing how those results developed through the epochs and being able to spot patterns and identify issues was invaluable in getting further under the skin of AI and model training, and demystifying the topic into more practical, tangible outcomes.


And once again this was thanks to the brilliance of the tools (Claude) existing in the first place. Twelve to eighteen months ago I wouldn't have bothered attempting such a project, expecting to fully get bogged down in the tedium of bug fixes, writing code (which I have nowhere near enough patience for and rage-quit several years ago), generating all the accompanying docs, deciding on tech stack and toolchain, and summarising the whole thing in markdown files for my repos. This was all done on the free tier of Claude, with less than 5 USD of API credit (which also covered me for Claude Code training certifications).


It's not the purest way to develop projects, but frankly I don't care - I have no truck for purists or dogmas in any field. What's important is how easy it is to create, to learn and do it fast. If you've got a product or design idea and enough knowledge of regulations and security not to do anything stupid (and an aversion to writing proper code) then this is the way.

David Turner is the founder of Kói, an independent strategic consultancy advising senior leaders and investors on high-value decisions across technology and adjacent creative fields.

You can reach him at: enquiries@dkoi.design


© Kói Holdings Ltd 2026. All Rights Reserved.




    Kói Holdings Ltd

    71-75 Shelton Street,

    Covent Garden,

    London

    WC2H 9JQ

    enquiries@dkoi.design

    UK Registered Company: 17312304

    Kói is a member of Manchester Digital

    © Copyright D. Turner 2026.

    Images and articles here are the original work of David Turner (except where explicitly stated), protected under international copyright law. Reproducing, scraping, or using them for AI training without permission is both a legal infringement and an ethical one. We pursue both.

    Content and images on this site are protected by copyright law and actively monitored via automated IP tracking and digital fingerprinting tools.

    Infringements are immediately met with legal action and DMCA takedown notices issued directly to hosting providers, which can result in site suspension and search engine de-listing.

    'Kói' logos are trademarks of Kói Holdings Ltd.

    bottom of page