AWS Builder Center

Improving Bird Species Detection Accuracy: Retraining a SageMaker Object Detection Model

Learn how AWS helped Oregon State University improve bird species detection accuracy by building a repeatable retraining pipeline with Amazon SageMaker. This step-by-step guide covers creating Ground Truth labeling jobs, preparing model artifacts, configuring training jobs, and deploying retrained Object Detection models — no prior SageMaker experience required.


Summary

Oregon State University's College of Arts partnered with AWS to deploy an AI-powered bird species detection system capable of identifying and counting birds from uploaded images. While the initial deployment leveraged an existing solution from the ASU CIC REPO , the model's accuracy fell short of production requirements — it had been trained on only ~100 images with no documentation on the labeling or training methodology.
This blog walks through how we built a complete, repeatable retraining pipeline using Amazon SageMaker Ground Truth and the SageMaker built-in Object Detection algorithm. Whether you are new to SageMaker or have limited ML experience, this guide provides everything you need to label images, prepare a model, and run a training job to improve detection accuracy.

The Challenge

Project Background

Oregon State University's College of Arts needed an automated solution to detect bird species and count individuals from camera trap and field-uploaded images. The use case supports ecological research and biodiversity monitoring across Oregon's diverse habitats.
We deployed the OSU Bird Image Analysis solution , originally developed as an ASU CIC (Cloud Innovation Center) project. The solution uses Amazon SageMaker for model hosting, an Object Detection algorithm with a ResNet-50 backbone for bird localization and species classification, and a serverless architecture for image upload and processing.

What Went Wrong

Upon deployment and testing with real-world Oregon imagery, we identified significant accuracy gaps:
IssueImpact
Model trained on only ~100 imagesInsufficient data for reliable generalization
No documentation on the labeling processCould not understand label quality or methodology
No training job configuration sharedCould not reproduce or iterate on the model
Limited species coverageModel missed species common in Oregon habitats
High false positive rateDetected birds where none existed
The original team did not share any information about how images were labeled, how the training job was configured, or what hyperparameters were used. We needed to build a retraining process from scratch.

Our Solution: A Repeatable Retraining Pipeline

We designed a complete, documented pipeline that enables researchers to continuously improve model accuracy — without needing deep ML expertise. The pipeline uses two core AWS services:
  • Amazon SageMaker Ground Truth — for labeling images with bounding boxes
  • Amazon SageMaker Training Jobs — for retraining the Object Detection model with new labeled data

High-Level Workflow


Understanding Key Concepts

Before diving into the implementation, let's clarify a few concepts that are essential to a successful outcome.

Training vs. Validation Datasets

ConceptTraining DatasetValidation Dataset
PurposeImages the model learns fromImages used to test how well the model is learning
During trainingModel adjusts its parameters using theseModel is evaluated against these (never trains on them)
AnalogyStudying from a textbookTaking a practice exam
Why you need both: Without a separate validation set, you have no way to measure whether the model is actually improving or just memorizing training images (a problem called overfitting).
💡 Important: Training and validation images must be completely different images. Never reuse the same image in both sets.

Recommended Dataset Sizes

ScenarioTraining Images (per class)Validation Images (per class)Expected Outcome
Proof of concept103–5Low accuracy (~60–70%), useful for pipeline testing
Moderate accuracy50–10015–30Reasonable accuracy (~80%)
Production-ready200+50+High accuracy (~90%+)
Tips for selecting images:
  • Include varied lighting conditions (day, night, overcast)
  • Include different angles and distances
  • Include partial occlusions (objects partially hidden)
  • Aim for an 80/20 split between training and validation
  • Balance the number of examples across classes

What is a Manifest File?

SageMaker uses manifest files to know where your images are and what annotations they have. There are two types:
TypeContainsWhen Used
Input manifestOnly image paths (S3 locations)To tell Ground Truth which images to label
Output manifestImage paths + bounding box annotationsTo feed into a training job
⚠️ Common mistake: Using the input manifest for training instead of the output manifest. The training job needs annotations — the input manifest doesn't have them.

Step-by-Step Implementation

Step 1: Organize Your S3 Bucket

A well-organized S3 bucket makes the entire workflow smoother. Here is our recommended structure:
1
2
3
4
5
6
7
8
9
10
11
12
13
s3://your-bucket/
├── training-data/
│ └── images/ ← Upload training images here
├── validation-data/
│ └── images/ ← Upload validation images here (SEPARATE set)
├── labeling-output/ ← Ground Truth writes here (auto-generated)
│ ├── <train-job-name>/manifests/output/output.manifest
│ └── <val-job-name>/manifests/output/output.manifest
├── models/
│ ├── original/ ← Backup of original model (never modify)
│ ├── prepared/ ← Model with shapes file added
│ └── retrained/ ← Archive of retrained versions
└── training-output/ ← SageMaker writes retrained model here (auto-generated)
Key principles:
  • Keep training and validation images in separate folders — do not mix them
  • Never modify files in labeling-output/ — they are auto-generated
  • Always keep a backup of your original model before making any changes

Step 2: Prepare Images for Labeling

Upload your images to S3:
You can use the AWS Console (drag and drop) or the AWS CLI:
1
2
3
4
5
# Upload training images
aws s3 cp ./my-training-images/ s3://your-bucket/training-data/images/ --recursive

# Upload validation images
aws s3 cp ./my-validation-images/ s3://your-bucket/validation-data/images/ --recursive
Create an input manifest file:
This is a simple text file where each line is a JSON object pointing to one image:
1
2
3
{"source-ref": "s3://your-bucket/training-data/images/image001.jpg"}
{"source-ref": "s3://your-bucket/training-data/images/image002.jpg"}
{"source-ref": "s3://your-bucket/training-data/images/image003.jpg"}
Save this as input.manifest and upload it alongside your images. Create a separate manifest for validation images as well.
💡 Tip: You can generate the manifest automatically using the CLI: bash
aws s3 ls s3://your-bucket/training-data/images/ |
awk '{print "{"source-ref": "s3://your-bucket/training-data/images/" $4 ""}"}' \> input.manifest

Step 3: Create a Ground Truth Labeling Job

This is where you (or your team) draw bounding boxes around objects in the images. You will create two labeling jobs — one for training images and one for validation images.

3.1 Open the Ground Truth Console

  1. Navigate to Amazon SageMaker in the AWS Console
  2. In the left menu, click Ground Truth → Labeling jobs
  3. Click Create labeling job

3.2 Configure Job Details

FieldWhat to Enter
Job nameA descriptive name (e.g., bird-detection-train). This name becomes the annotation attribute name — remember it for later.
Input dataset locationSelect "Automated data setup" or point to your input.manifest file
S3 location for input datasetss3://your-bucket/training-data/images/
S3 location for output datasetss3://your-bucket/labeling-output/
Data typeImage
IAM RoleSelect or create a role with S3 access
💡 Important: The Job name you choose here becomes the annotation attribute name that you'll need later when configuring the training job. Choose something clear and memorable.

3.3 Select Workers

OptionWhen to Use
PrivateYour own team labels the images (recommended for domain expertise)
Amazon Mechanical TurkCrowdsourced labeling at scale
For specialized domains like bird species identification, we recommend using a private workforce — your research team will have the domain knowledge to label accurately.

3.4 Configure the Labeling Tool

  1. Task category: Select "Image"
  2. Task type: Select "Bounding box"
  3. Add label categories: Enter each species name (e.g., "Sparrow", "Robin", "Black Pigeon")
    • The number of labels must match the num_classes hyperparameter you'll use later
  4. Provide instructions: Write clear guidelines for annotators (what to label, what to skip, how tight the boxes should be)
  5. Click Create

3.5 Complete Labeling and Locate Output

  1. If you are the labeler: go to Ground Truth → Labeling workforces → Private, click the portal link, and draw bounding boxes on each image
  2. Wait for the job status to show Complete
  3. Your output manifest (with annotations) is at:
1
s3://your-bucket/labeling-output/<job-name>/manifests/output/output.manifest
What the output manifest looks like:
1
2
3
4
5
6
7
8
9
10
11
{
"source-ref": "s3://your-bucket/training-data/images/image001.jpg",
"bird-detection-train": {
"image_size": [{"width": 1920, "height": 1080, "depth": 3}],
"annotations": [
{"class_id": 0, "left": 100, "top": 200, "width": 50, "height": 60},
{"class_id": 1, "left": 500, "top": 300, "width": 45, "height": 55}
]
},
"bird-detection-train-metadata": {"type": "groundtruth/object-detection"}
}
Repeat this entire process for your validation images with a different job name (e.g., bird-detection-validation).

Step 4: Prepare the Model Artifact for Retraining

When retraining an existing SageMaker Object Detection model, you must add a shapes file that defines the expected input tensor dimensions. Without it, the training job will fail.

4.1 Download and Extract the Existing Model

1
2
3
4
aws s3 cp s3://your-bucket/models/original/model.tar.gz /tmp/model.tar.gz
mkdir /tmp/model_files
tar -xzf /tmp/model.tar.gz -C /tmp/model_files
ls /tmp/model_files/
You should see files like:
1
2
3
hyperparams.json
model_algo_1-0000.params
model_algo_1-symbol.json

4.2 Identify the Model Prefix

Look at the filenames — the prefix is everything before -0000.params:
If your files are named...The prefix is...
model_algo_1-0000.paramsmodel_algo_1
model-0000.paramsmodel

4.3 Create the Shapes File

Create a file named <prefix>-shapes.json with the input tensor shape:
1
printf '[{"shape": [1, 3, 300, 300], "name": "data"}]' > /tmp/model_files/model_algo_1-shapes.json
Understanding the shape values:
  • 1 = batch size (always 1)
  • 3 = color channels (RGB)
  • 300, 300 = height and width (must match the image_shape hyperparameter you'll use)
For image_shape: 512, use [1, 3, 512, 512] instead.

4.4 Verify and Repackage

1
2
3
4
5
6
7
8
9
10
11
# Verify contents
ls /tmp/model_files/
# Should show: hyperparams.json, model_algo_1-0000.params, model_algo_1-shapes.json, model_algo_1-symbol.json

# Repackage — IMPORTANT: run tar from INSIDE the directory
cd /tmp/model_files
tar -czf /tmp/model_fixed.tar.gz *
cd -

# Upload to S3
aws s3 cp /tmp/model_fixed.tar.gz s3://your-bucket/models/prepared/model.tar.gz
⚠️ Critical: Files must be at the root level of the tar.gz — not nested in a subdirectory. Always use tar -czf model.tar.gz * from inside the folder containing the files.

Step 5: Create the Training Job

With labeled data and a prepared model, you can now configure and run the training job.

5.1 Open the Training Console

  1. Navigate to Amazon SageMaker → Training → Training jobs
  2. Click Create training job

5.2 Select the Algorithm

FieldValue
Algorithm sourceAmazon SageMaker built-in algorithm
AlgorithmObject Detection
⚠️ Common mistake: Selecting "Image Classification" instead of "Object Detection." These are completely different architectures and are not interchangeable. If unsure, check hyperparams.json in your model — if it has nms_threshold and overlap_threshold, it's Object Detection.

5.3 Configure Resources

FieldValue
Instance typeml.p3.2xlarge or ml.g4dn.xlarge (GPU required)
Instance count1
Volume size50 GB

5.4 Set Hyperparameters

ParameterValueNotes
num_classes(your count)Must match the number of label categories in your labeling job
num_training_samples(exact count)Exact number of images in your training manifest
base_networkresnet-50Must match the original model
image_shape300Must match the shapes file (300 or 512)
mini_batch_size2–32Must be ≤ your smallest dataset (training or validation)
epochs20–30More epochs = longer training; 20 is usually enough for fine-tuning
learning_rate0.0005Keep small for fine-tuning existing models
optimizeradamGood default for fine-tuning
use_pretrained_model0Set to 0 when retraining from your own model
overlap_threshold0.5IoU threshold for detection
nms_threshold0.45Non-max suppression threshold
💡 Key rule: mini_batch_size cannot exceed the number of images in your smallest dataset. If you have 10 training images and 3 validation images, set mini_batch_size to 3 or less.

5.5 Configure Input Data (3 Channels)

This is the most error-prone step. You need exactly three input channels:
Channel 1 — train:
FieldValue
Channel nametrain (must be exactly this)
S3 data typeAugmentedManifestFile
S3 URIs3://your-bucket/labeling-output/<train-job-name>/manifests/output/output.manifest
Attribute namessource-ref, <train-labeling-job-name>
Input modePipe
Channel 2 — validation:
FieldValue
Channel namevalidation (must be exactly this)
S3 data typeAugmentedManifestFile
S3 URIs3://your-bucket/labeling-output/<val-job-name>/manifests/output/output.manifest
Attribute namessource-ref, <validation-labeling-job-name>
Input modePipe
Channel 3 — model:
FieldValue
Channel namemodel (must be exactly this)
S3 data typeS3Prefix
S3 URIs3://your-bucket/models/prepared/model.tar.gz
Content typeapplication/x-sagemaker-model
Input modeFile (must be File, not Pipe)
⚠️ The #1 mistake: The Attribute names field must include TWO values — both source-ref AND the labeling job name. If you only enter source-ref, the training will fail with "Invalid JSON string found."
How to find your annotation attribute name:
The attribute name always equals your Ground Truth labeling job name. To verify:
1
aws s3 cp s3://your-bucket/labeling-output/<job-name>/manifests/output/output.manifest - | head -1 | python3 -m json.tool
Look for the key containing "annotations" — that's your attribute name (ignore keys ending in -metadata).

5.6 Configure Output

FieldValue
S3 output paths3://your-bucket/training-output/
Click Create training job and wait for it to complete.

Step 6: Find and Deploy the Retrained Model

6.1 Locate the Output

After the training job shows Completed, find your retrained model at:
1
s3://your-bucket/training-output/<training-job-name>/output/model.tar.gz
You can also find this in the Console: Training jobs → click your job → scroll to Output → S3 model artifact.
Or via CLI:
1
2
3
4
aws sagemaker describe-training-job \
--training-job-name <your-job-name> \
--query 'ModelArtifacts.S3ModelArtifacts' \
--output text

6.2 Back Up the Current Production Model

Always back up before replacing:
1
2
aws s3 cp s3://your-production-bucket/models/model.tar.gz \
s3://your-production-bucket/models/backups/model-backup-$(date +%Y%m%d).tar.gz

6.3 Deploy the New Model

Copy the retrained model to your production location:
1
2
aws s3 cp s3://your-bucket/training-output/<job-name>/output/model.tar.gz \
s3://your-production-bucket/models/model.tar.gz

6.4 Test and Validate

After deployment:
  1. Upload test images through your application
  2. Verify bounding boxes appear in correct locations
  3. Confirm class labels and confidence scores are reasonable
  4. Test with images the model has never seen (the real test of generalization)
ResultAction
Good accuracy✅ Done! Model is live.
Some detections missingAdd more training images of missed cases, retrain
False positivesAdd more diverse images, consider increasing epochs
Very poor resultsRoll back to backup, investigate hyperparameters
To roll back:
1
2
aws s3 cp s3://your-production-bucket/models/backups/model-backup-YYYYMMDD.tar.gz \
s3://your-production-bucket/models/model.tar.gz

Troubleshooting Common Errors

"Model shapes file cannot be loaded"

Possible CauseFix
Shapes file is missingCreate <prefix>-shapes.json (see Step 4)
Wrong filename prefixPrefix must match model files (e.g., model_algo_1-shapes.json, not model-shapes.json)
Files nested in a subfolderRepackage with tar -czf model.tar.gz * from inside the directory
Wrong algorithm selectedVerify you chose "Object Detection" not "Image Classification"

"Invalid JSON string found"

Possible CauseFix
Using input manifest instead of output manifestUse the output manifest at .../manifests/output/output.manifest
Missing annotation attribute nameAdd the labeling job name to Attribute names: source-ref, <job-name>

"Number of input images must be >= mini_batch_size"

CauseFix
mini_batch_size exceeds your smallest datasetSet mini_batch_size ≤ smallest dataset. If validation has 3 images, set batch size to 3 or less.

"Model shape does not match with input"

CauseFix
Shape values don't match image_shape hyperparameterFor image_shape: 300, shapes file must be [1, 3, 300, 300]. For 512, use [1, 3, 512, 512].

Complete Hyperparameter Reference

ParameterDefaultDescription
base_networkvgg-16Base network architecture: resnet-50 or vgg-16
num_classes(required)Number of object classes to detect
num_training_samples(required)Exact count of training images
use_pretrained_model0Use ImageNet pretrained weights (1=yes, 0=no)
mini_batch_size32Training batch size (must be ≤ smallest dataset)
epochs30Number of training epochs
learning_rate0.001Initial learning rate
lr_scheduler_step""Epochs at which to reduce LR (e.g., "30,60")
lr_scheduler_factor0.1Factor to reduce learning rate by
optimizersgdOptimizer: sgd or adam
momentum0.9SGD momentum
weight_decay0.0005L2 regularization
overlap_threshold0.5IoU threshold for positive detection
nms_threshold0.45Non-max suppression threshold
image_shape300Input image size (300 or 512)
label_width350Maximum annotations per image
nms_topk400Top K detections after NMS
early_stoppingfalseEnable early stopping
early_stopping_patience5Epochs without improvement before stopping
early_stopping_tolerance0.0Minimum improvement to count as progress
freeze_layer_pattern""Regex pattern of layers to freeze

Pre-Training Checklist

Run through this before every training job:
  • [ ] Ground Truth labeling jobs are Complete (both training and validation)
  • [ ] Using the output manifest (with annotations), not the input manifest
  • [ ] Algorithm is Object Detection (not Image Classification)
  • [ ] model.tar.gz contains <prefix>-shapes.json with the correct prefix
  • [ ] Shape values match the image_shape hyperparameter
  • [ ] Files in model.tar.gz are at the root level (not nested in a subfolder)
  • [ ] num_classes matches the number of label categories
  • [ ] num_training_samples matches the exact line count of the training manifest
  • [ ] mini_batch_size ≤ smallest dataset (training or validation)
  • [ ] All three input channels configured: train, validation, model
  • [ ] Attribute names include BOTH source-ref AND the labeling job name
  • [ ] Model channel input mode is set to File (not Pipe)
  • [ ] Original model is backed up before any changes

Results and Next Steps

What We Delivered

  • ✅ A repeatable, fully documented retraining pipeline
  • ✅ Successfully retrained the model with new labeled data
  • ✅ Comprehensive documentation for the OSU team to operate independently
  • ✅ Clear path from proof-of-concept to production-quality accuracy

Recommendations for Continuous Improvement

ActionPriorityExpected Impact
Label 200+ images per speciesHighSignificant accuracy improvement
Include varied conditions (lighting, angles, distances)HighBetter real-world generalization
Add negative examples (images with no birds)MediumReduce false positives
Increase image_shape to 512MediumBetter detection of small/distant birds
Implement automated retraining with new uploadsLowContinuous model improvement

When to Retrain

Consider retraining when:
  • The model misses objects that are clearly visible
  • The model confuses one species for another
  • You have new species to add
  • The environment changes (new camera angle, season, lighting)
  • You accumulate 50+ new labeled images per class

Conclusion

By building a documented retraining pipeline with Amazon SageMaker Ground Truth and the built-in Object Detection algorithm, we empowered Oregon State University's College of Arts to iteratively improve their bird detection model. The key insight is that accuracy is a function of training data quality and quantity — the infrastructure to retrain is now in place, and each round of new labeled images will yield measurable improvements.
We hope this guide helps other teams facing similar challenges. Whether you're working with wildlife detection, agricultural monitoring, or any computer vision use case, the same principles apply: organize your data, label carefully, prepare your model artifact correctly, and configure your training job with the right input channels and hyperparameters.

Resources


This blog documents a real-world implementation for Oregon State University's College of Arts. The process described here is generalizable to any SageMaker Object Detection retraining workflow.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article