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:
| Issue | Impact |
|---|---|
| Model trained on only ~100 images | Insufficient data for reliable generalization |
| No documentation on the labeling process | Could not understand label quality or methodology |
| No training job configuration shared | Could not reproduce or iterate on the model |
| Limited species coverage | Model missed species common in Oregon habitats |
| High false positive rate | Detected 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
| Concept | Training Dataset | Validation Dataset |
|---|---|---|
| Purpose | Images the model learns from | Images used to test how well the model is learning |
| During training | Model adjusts its parameters using these | Model is evaluated against these (never trains on them) |
| Analogy | Studying from a textbook | Taking 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
| Scenario | Training Images (per class) | Validation Images (per class) | Expected Outcome |
|---|---|---|---|
| Proof of concept | 10 | 3–5 | Low accuracy (~60–70%), useful for pipeline testing |
| Moderate accuracy | 50–100 | 15–30 | Reasonable accuracy (~80%) |
| Production-ready | 200+ | 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:
| Type | Contains | When Used |
|---|---|---|
| Input manifest | Only image paths (S3 locations) | To tell Ground Truth which images to label |
| Output manifest | Image paths + bounding box annotations | To 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/ --recursiveCreate 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
- Navigate to Amazon SageMaker in the AWS Console
- In the left menu, click Ground Truth → Labeling jobs
- Click Create labeling job
3.2 Configure Job Details
| Field | What to Enter |
|---|---|
| Job name | A descriptive name (e.g., bird-detection-train). This name becomes the annotation attribute name — remember it for later. |
| Input dataset location | Select "Automated data setup" or point to your input.manifest file |
| S3 location for input datasets | s3://your-bucket/training-data/images/ |
| S3 location for output datasets | s3://your-bucket/labeling-output/ |
| Data type | Image |
| IAM Role | Select 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
| Option | When to Use |
|---|---|
| Private | Your own team labels the images (recommended for domain expertise) |
| Amazon Mechanical Turk | Crowdsourced 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
- Task category: Select "Image"
- Task type: Select "Bounding box"
- Add label categories: Enter each species name (e.g., "Sparrow", "Robin", "Black Pigeon")
- The number of labels must match the
num_classeshyperparameter you'll use later
- The number of labels must match the
- Provide instructions: Write clear guidelines for annotators (what to label, what to skip, how tight the boxes should be)
- Click Create
3.5 Complete Labeling and Locate Output
- If you are the labeler: go to Ground Truth → Labeling workforces → Private, click the portal link, and draw bounding boxes on each image
- Wait for the job status to show Complete
- Your output manifest (with annotations) is at:
1
s3://your-bucket/labeling-output/<job-name>/manifests/output/output.manifestWhat 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.json4.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.params | model_algo_1 |
model-0000.params | model |
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.jsonUnderstanding the shape values:
1= batch size (always 1)3= color channels (RGB)300, 300= height and width (must match theimage_shapehyperparameter 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 usetar -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
- Navigate to Amazon SageMaker → Training → Training jobs
- Click Create training job
5.2 Select the Algorithm
| Field | Value |
|---|---|
| Algorithm source | Amazon SageMaker built-in algorithm |
| Algorithm | Object Detection |
⚠️ Common mistake: Selecting "Image Classification" instead of "Object Detection." These are completely different architectures and are not interchangeable. If unsure, checkhyperparams.jsonin your model — if it hasnms_thresholdandoverlap_threshold, it's Object Detection.
5.3 Configure Resources
| Field | Value |
|---|---|
| Instance type | ml.p3.2xlarge or ml.g4dn.xlarge (GPU required) |
| Instance count | 1 |
| Volume size | 50 GB |
5.4 Set Hyperparameters
| Parameter | Value | Notes |
|---|---|---|
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_network | resnet-50 | Must match the original model |
image_shape | 300 | Must match the shapes file (300 or 512) |
mini_batch_size | 2–32 | Must be ≤ your smallest dataset (training or validation) |
epochs | 20–30 | More epochs = longer training; 20 is usually enough for fine-tuning |
learning_rate | 0.0005 | Keep small for fine-tuning existing models |
optimizer | adam | Good default for fine-tuning |
use_pretrained_model | 0 | Set to 0 when retraining from your own model |
overlap_threshold | 0.5 | IoU threshold for detection |
nms_threshold | 0.45 | Non-max suppression threshold |
💡 Key rule:mini_batch_sizecannot exceed the number of images in your smallest dataset. If you have 10 training images and 3 validation images, setmini_batch_sizeto 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:
| Field | Value |
|---|---|
| Channel name | train (must be exactly this) |
| S3 data type | AugmentedManifestFile |
| S3 URI | s3://your-bucket/labeling-output/<train-job-name>/manifests/output/output.manifest |
| Attribute names | source-ref, <train-labeling-job-name> |
| Input mode | Pipe |
Channel 2 — validation:
| Field | Value |
|---|---|
| Channel name | validation (must be exactly this) |
| S3 data type | AugmentedManifestFile |
| S3 URI | s3://your-bucket/labeling-output/<val-job-name>/manifests/output/output.manifest |
| Attribute names | source-ref, <validation-labeling-job-name> |
| Input mode | Pipe |
Channel 3 — model:
| Field | Value |
|---|---|
| Channel name | model (must be exactly this) |
| S3 data type | S3Prefix |
| S3 URI | s3://your-bucket/models/prepared/model.tar.gz |
| Content type | application/x-sagemaker-model |
| Input mode | File (must be File, not Pipe) |
⚠️ The #1 mistake: The Attribute names field must include TWO values — bothsource-refAND the labeling job name. If you only entersource-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.toolLook for the key containing
"annotations" — that's your attribute name (ignore keys ending in -metadata).5.6 Configure Output
| Field | Value |
|---|---|
| S3 output path | s3://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.gzYou 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 text6.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.gz6.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.gz6.4 Test and Validate
After deployment:
- Upload test images through your application
- Verify bounding boxes appear in correct locations
- Confirm class labels and confidence scores are reasonable
- Test with images the model has never seen (the real test of generalization)
| Result | Action |
|---|---|
| Good accuracy | ✅ Done! Model is live. |
| Some detections missing | Add more training images of missed cases, retrain |
| False positives | Add more diverse images, consider increasing epochs |
| Very poor results | Roll 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.gzTroubleshooting Common Errors
"Model shapes file cannot be loaded"
| Possible Cause | Fix |
|---|---|
| Shapes file is missing | Create <prefix>-shapes.json (see Step 4) |
| Wrong filename prefix | Prefix must match model files (e.g., model_algo_1-shapes.json, not model-shapes.json) |
| Files nested in a subfolder | Repackage with tar -czf model.tar.gz * from inside the directory |
| Wrong algorithm selected | Verify you chose "Object Detection" not "Image Classification" |
"Invalid JSON string found"
| Possible Cause | Fix |
|---|---|
| Using input manifest instead of output manifest | Use the output manifest at .../manifests/output/output.manifest |
| Missing annotation attribute name | Add the labeling job name to Attribute names: source-ref, <job-name> |
"Number of input images must be >= mini_batch_size"
| Cause | Fix |
|---|---|
mini_batch_size exceeds your smallest dataset | Set mini_batch_size ≤ smallest dataset. If validation has 3 images, set batch size to 3 or less. |
"Model shape does not match with input"
| Cause | Fix |
|---|---|
Shape values don't match image_shape hyperparameter | For image_shape: 300, shapes file must be [1, 3, 300, 300]. For 512, use [1, 3, 512, 512]. |
Complete Hyperparameter Reference
| Parameter | Default | Description |
|---|---|---|
base_network | vgg-16 | Base 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_model | 0 | Use ImageNet pretrained weights (1=yes, 0=no) |
mini_batch_size | 32 | Training batch size (must be ≤ smallest dataset) |
epochs | 30 | Number of training epochs |
learning_rate | 0.001 | Initial learning rate |
lr_scheduler_step | "" | Epochs at which to reduce LR (e.g., "30,60") |
lr_scheduler_factor | 0.1 | Factor to reduce learning rate by |
optimizer | sgd | Optimizer: sgd or adam |
momentum | 0.9 | SGD momentum |
weight_decay | 0.0005 | L2 regularization |
overlap_threshold | 0.5 | IoU threshold for positive detection |
nms_threshold | 0.45 | Non-max suppression threshold |
image_shape | 300 | Input image size (300 or 512) |
label_width | 350 | Maximum annotations per image |
nms_topk | 400 | Top K detections after NMS |
early_stopping | false | Enable early stopping |
early_stopping_patience | 5 | Epochs without improvement before stopping |
early_stopping_tolerance | 0.0 | Minimum 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.gzcontains<prefix>-shapes.jsonwith the correct prefix - [ ] Shape values match the
image_shapehyperparameter - [ ] Files in
model.tar.gzare at the root level (not nested in a subfolder) - [ ]
num_classesmatches the number of label categories - [ ]
num_training_samplesmatches 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-refAND 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
| Action | Priority | Expected Impact |
|---|---|---|
| Label 200+ images per species | High | Significant accuracy improvement |
| Include varied conditions (lighting, angles, distances) | High | Better real-world generalization |
| Add negative examples (images with no birds) | Medium | Reduce false positives |
Increase image_shape to 512 | Medium | Better detection of small/distant birds |
| Implement automated retraining with new uploads | Low | Continuous 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
- OSU Bird Image Analysis Solution (GitHub)
- Amazon SageMaker Object Detection Algorithm
- Amazon SageMaker Ground Truth Developer Guide
- Object Detection Hyperparameters Reference
- SageMaker Built-in Algorithm Common Parameters
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.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article