Commit
·
628e9ae
1
Parent(s):
9a730b6
Add application file, models, support files
Browse files- .DS_Store +0 -0
- .vscode/settings.json +6 -0
- README.md +83 -11
- app/.DS_Store +0 -0
- app/app.py +75 -0
- app/bird_species.py +99 -0
- models/birds_learner.pkl +3 -0
- models/birds_vocab.pkl +3 -0
- models/freezed_model_summary.pkl +3 -0
- models/unfreezed_model_summary.pkl +3 -0
- requirements.txt +3 -0
.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
.vscode/settings.json
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"[python]": {
|
3 |
+
"editor.defaultFormatter": "ms-python.autopep8"
|
4 |
+
},
|
5 |
+
"python.formatting.provider": "none"
|
6 |
+
}
|
README.md
CHANGED
@@ -1,13 +1,85 @@
|
|
|
|
1 |
---
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
# Birds 525 Species Detector
|
2 |
---
|
3 |
+
## 1. Load Data
|
4 |
+
[**BIRDS 525 SPECIES IMAGE CLASSIFICATION DATASET**](https://www.kaggle.com/datasets/gpiosenka/100-bird-species)
|
5 |
+
- **Dataset Size:** 525 bird species.
|
6 |
+
- **Images:** 84,635 training images, 2,625 test images (5 per species), and 2,625 validation images (5 per species).
|
7 |
+
- **Image Dimensions:** 224x224x3 JPG format.
|
8 |
+
- **Data Structure:** Convenient subdirectories for each species.
|
9 |
+
- **Additional File:** `birds.csv` contains file paths, class labels, and more.
|
10 |
+
|
11 |
+
**Dataset Quality:**
|
12 |
+
- Cleaned to remove duplicate and low-quality images.
|
13 |
+
- Each image features a single bird taking up at least 50% of pixels.
|
14 |
+
- Original images, no augmentation.
|
15 |
+
|
16 |
+
**File Naming:**
|
17 |
+
- Sequential numbering, e.g., "001.jpg," "002.jpg" for training.
|
18 |
+
- Test and validation images: "1.jpg" to "5.jpg."
|
19 |
+
|
20 |
+
**Note:** Training set not balanced but has ≥130 images per species.
|
21 |
+
|
22 |
+
## 2. Data Preparation
|
23 |
+
**Augmentation Transforms:**
|
24 |
+
- Imported augmentation transforms to enhance dataset diversity.
|
25 |
+
|
26 |
+
**Item Transforms:**
|
27 |
+
- Images resized to a consistent width of 460 pixels.
|
28 |
+
|
29 |
+
**Batch Transforms:**
|
30 |
+
- Images resized to 224x224 pixels with a minimum scaling of 75%.
|
31 |
+
- Additional augmentations applied to batches for robustness.
|
32 |
+
|
33 |
+
These transformations ensure data consistency and introduce variability, preparing the dataset for model training.
|
34 |
+
|
35 |
+
## 3. Create DataLoader
|
36 |
+
**DataBlock Configuration:**
|
37 |
+
- Utilized Fastai's DataBlock to define data processing.
|
38 |
+
- Blocks specified as (ImageBlock, CategoryBlock) for image and category data.
|
39 |
+
- `get_items` set to retrieve image files.
|
40 |
+
- Splitter configured using `GrandparentSplitter` to separate train and test sets based on directory names.
|
41 |
+
- `get_y` method defined to extract parent label as category.
|
42 |
+
- Item and batch transforms (`item_tfms` and `batch_tfms`) applied as configured.
|
43 |
+
|
44 |
+
**DataLoaders Creation:**
|
45 |
+
- Fastai DataLoaders created using the DataBlock and dataset path.
|
46 |
+
- Batch size set to 64 for efficient data loading.
|
47 |
+
|
48 |
+
These steps establish the DataLoaders required for training and validation, making the data ready for model ingestion.
|
49 |
+
|
50 |
+
## 4. Create Learner (Model) & Find Learning Rate
|
51 |
+
**Pre-trained Model Configuration:**
|
52 |
+
- Imported pre-trained ResNet models (resnet34 and resnet50) from torchvision.
|
53 |
+
- Created a vision learner using the Fastai library with the ResNet-50 architecture.
|
54 |
+
- Enabled the use of pretrained weights and specified evaluation metrics, including accuracy and error rate.
|
55 |
+
- Enabled mixed-precision training using `to_fp16()` for enhanced training efficiency.
|
56 |
+
|
57 |
+
**Finding Learning Rate:**
|
58 |
+
- Utilized the `lr_find()` method to determine an optimal learning rate.
|
59 |
+
- Discovered a suitable learning rate range (`lr`) for model training, set as `slice(1e-4, 5e-3)`.
|
60 |
+
|
61 |
+
This section highlights the model configuration, including the choice of architecture, pretrained weights, and learning rate discovery, ensuring an effective setup for model training and evaluation.
|
62 |
+
|
63 |
+
## 5. Train & Save Model
|
64 |
+
**Model Performance with Resnet50 (Freezed Layers)**
|
65 |
+
|
66 |
+
| Epoch | Train Loss | Valid Loss | Accuracy | Error Rate | Time |
|
67 |
+
|-------|------------|------------|----------|------------|--------|
|
68 |
+
| 0 | 1.2802 | 0.4474 | 0.8705 | 0.1295 | 13:50 |
|
69 |
+
| 1 | 0.7865 | 0.1838 | 0.9482 | 0.0518 | 11:06 |
|
70 |
+
| 2 | 0.4920 | 0.1074 | 0.9695 | 0.0305 | 11:17 |
|
71 |
+
| 3 | 0.3435 | 0.0671 | 0.9844 | 0.0156 | 10:44 |
|
72 |
+
| 4 | 0.2979 | 0.0590 | 0.9859 | 0.0141 | 11:12 |
|
73 |
+
|
74 |
+
**Model Training:**
|
75 |
+
- The model was trained for 5 epochs using a one-cycle learning rate policy with the specified learning rate range.
|
76 |
+
- The training results demonstrate the model's impressive performance, achieving high accuracy and low error rates.
|
77 |
+
|
78 |
+
**Model Preservation:**
|
79 |
+
- Saved the trained model with the name 'model1_freezed' for future use.
|
80 |
+
- This step ensures that the model's architecture and learned weights are preserved and can be easily loaded and deployed for various tasks.
|
81 |
+
|
82 |
+
This section provides an overview of the model's training performance, including training and validation losses, accuracy, and error rates. It also emphasizes the importance of preserving the trained model for future use.
|
83 |
+
|
84 |
+
|
85 |
|
|
app/.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
app/app.py
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Import streamlit
|
2 |
+
import streamlit as st
|
3 |
+
from streamlit_option_menu import option_menu
|
4 |
+
from fastai.vision.all import *
|
5 |
+
from fastai.learner import load_learner
|
6 |
+
import pickle
|
7 |
+
from bird_species import model_info
|
8 |
+
from bird_species import capture_photo
|
9 |
+
from bird_species import upload_photo
|
10 |
+
|
11 |
+
|
12 |
+
def app():
|
13 |
+
#######################################################################################################################
|
14 |
+
|
15 |
+
# Set the page config
|
16 |
+
st.set_page_config(
|
17 |
+
page_title="Bird 525 Species Detector", # The title of the web page
|
18 |
+
page_icon="🕊️", # The icon of the web page, can be an emoji or a file path
|
19 |
+
initial_sidebar_state="collapsed"
|
20 |
+
)
|
21 |
+
|
22 |
+
#######################################################################################################################
|
23 |
+
|
24 |
+
st.markdown("<h1 style='text-align: center;'>🕊️Birds 525 Species Detector🕊️</h1>",
|
25 |
+
unsafe_allow_html=True)
|
26 |
+
|
27 |
+
#######################################################################################################################
|
28 |
+
|
29 |
+
# Options Menu at the top of the homepage
|
30 |
+
selected = option_menu(None, ["Upload", "Capture", "Model"],
|
31 |
+
icons=["cloud upload", "camera", "gear"],
|
32 |
+
menu_icon="cast", default_index=0, orientation="horizontal")
|
33 |
+
|
34 |
+
#######################################################################################################################
|
35 |
+
|
36 |
+
# Load model and model class labels (vocab)
|
37 |
+
model = load_learner(fname="models/birds_learner.pkl")
|
38 |
+
|
39 |
+
with open("models/birds_vocab.pkl", "rb") as f:
|
40 |
+
vocab = pickle.load(f)
|
41 |
+
# Sorting
|
42 |
+
vocab = sorted(vocab)
|
43 |
+
|
44 |
+
with open("models/freezed_model_summary.pkl", "rb") as f:
|
45 |
+
freezed_arch_summary = pickle.load(f)
|
46 |
+
|
47 |
+
with open("models/unfreezed_model_summary.pkl", "rb") as f:
|
48 |
+
unfreezed_arch_summary = pickle.load(f)
|
49 |
+
|
50 |
+
#######################################################################################################################
|
51 |
+
|
52 |
+
if selected == "Upload":
|
53 |
+
st.caption("""Our project utilizes FastAI Vision with the ResNet50 architecture to classify
|
54 |
+
525 bird species. Our dataset comprises 84,635 training images, 2,625 test images and 2,625 validation
|
55 |
+
images, all standardized to 224x224x3 pixels. Initial training yields 96.6% accuracy, improved to 98% post
|
56 |
+
fine-tuning. Despite gender imbalances, it's a valuable resource for accurate bird species classification.""")
|
57 |
+
|
58 |
+
upload_photo(model=model, vocab=vocab, key="upload photo")
|
59 |
+
|
60 |
+
#######################################################################################################################
|
61 |
+
|
62 |
+
if selected == "Capture":
|
63 |
+
capture_photo(model=model, vocab=None, key="capture photo")
|
64 |
+
|
65 |
+
if selected == "Model":
|
66 |
+
model_info()
|
67 |
+
st.subheader("FastAi Model Summary (Freezed)")
|
68 |
+
st.code(freezed_arch_summary)
|
69 |
+
st.subheader("FastAi Model Summary (Unfreezed)")
|
70 |
+
st.code(unfreezed_arch_summary)
|
71 |
+
|
72 |
+
|
73 |
+
#######################################################################################################################
|
74 |
+
if __name__ == "__main__":
|
75 |
+
app()
|
app/bird_species.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Import streamlit
|
2 |
+
import streamlit as st
|
3 |
+
from streamlit_option_menu import option_menu
|
4 |
+
from PIL import Image
|
5 |
+
import pandas as pd
|
6 |
+
from fastai.vision.all import *
|
7 |
+
from fastai.learner import load_learner
|
8 |
+
|
9 |
+
#######################################################################################################################
|
10 |
+
|
11 |
+
def upload_photo(model=None, vocab=None, key=None):
|
12 |
+
options = st.multiselect("**Find & select multiple names, then perform Web Search, Download, Upload & Detect...**",
|
13 |
+
vocab,
|
14 |
+
vocab[100:108],
|
15 |
+
key="birds_detector 1")
|
16 |
+
st.text(f"Your selection: {options}")
|
17 |
+
|
18 |
+
uploaded_image = st.file_uploader(
|
19 |
+
"**Upload an image**", type=["jpg", "png", "jpeg"], key=key)
|
20 |
+
st.divider()
|
21 |
+
|
22 |
+
if uploaded_image:
|
23 |
+
image = Image.open(uploaded_image)
|
24 |
+
st.image(image, use_column_width=True)
|
25 |
+
|
26 |
+
if st.button("**Detect**", type="primary"):
|
27 |
+
output = model.predict(image)
|
28 |
+
st.markdown(f"""
|
29 |
+
<div style="text-align:center;">
|
30 |
+
<h1>{output[0]}</h1>
|
31 |
+
</div>""",
|
32 |
+
unsafe_allow_html=True)
|
33 |
+
|
34 |
+
st.image(
|
35 |
+
image, caption=f'{output[0]} {max(output[2]).item() * 100:.2f}%', use_column_width=True)
|
36 |
+
|
37 |
+
#######################################################################################################################
|
38 |
+
|
39 |
+
def capture_photo(model=None, vocab=None, key=None):
|
40 |
+
capture_toggle = st.toggle(
|
41 |
+
label="**`Enable Camera`**", key="birds_capture_photo")
|
42 |
+
|
43 |
+
if capture_toggle:
|
44 |
+
# Check if the cancel checkbox is not selected
|
45 |
+
img_file_buffer = st.camera_input(
|
46 |
+
label="Take a picture (`try to keep the subject at the center`)", key=key)
|
47 |
+
|
48 |
+
if img_file_buffer:
|
49 |
+
# To read image file buffer as a PIL Image:
|
50 |
+
image = Image.open(img_file_buffer)
|
51 |
+
|
52 |
+
st.image(image, use_column_width=True)
|
53 |
+
|
54 |
+
if st.button(label="Detect", key="pets_capture_detect"):
|
55 |
+
|
56 |
+
output = model.predict(image)
|
57 |
+
st.markdown(f"""<div style="text-align:center;">
|
58 |
+
<h1>{output[0]}</h1>
|
59 |
+
</div>""",
|
60 |
+
unsafe_allow_html=True)
|
61 |
+
|
62 |
+
st.image(
|
63 |
+
image, caption=f'{output[0]} {max(output[2]).item() * 100:.2f}%', use_column_width=True)
|
64 |
+
|
65 |
+
#######################################################################################################################
|
66 |
+
|
67 |
+
def model_info():
|
68 |
+
# Model performance on Freezed Layers
|
69 |
+
st.subheader("Model performance with Resnet50 (freezed layers)")
|
70 |
+
freezed_data = {
|
71 |
+
'epoch': [0, 1, 2, 3, 4],
|
72 |
+
'train_loss': [1.280186, 0.786523, 0.491951, 0.343532, 0.297919],
|
73 |
+
'valid_loss': [0.447442, 0.183758, 0.107396, 0.067139, 0.058960],
|
74 |
+
'accuracy': [0.870476, 0.948190, 0.969524, 0.984381, 0.985905],
|
75 |
+
'error_rate': [0.129524, 0.051810, 0.030476, 0.015619, 0.014095],
|
76 |
+
'time': ['13:50', '11:06', '11:17', '10:44', '11:12']
|
77 |
+
}
|
78 |
+
|
79 |
+
df = pd.DataFrame(freezed_data)
|
80 |
+
st.table(df)
|
81 |
+
|
82 |
+
# Model performance on Unfreezed Layers
|
83 |
+
st.subheader("Model performance with Resnet50 (unfreezed layers)")
|
84 |
+
# Create a DataFrame with the provided data
|
85 |
+
unfreezed_data = {
|
86 |
+
'epoch': [0, 1, 2, 3, 4],
|
87 |
+
'train_loss': [0.989004, 0.699652, 0.447756, 0.258508, 0.160852],
|
88 |
+
'valid_loss': [0.391414, 0.176702, 0.089814, 0.037954, 0.029704],
|
89 |
+
'accuracy': [0.896381, 0.949714, 0.975238, 0.991238, 0.992762],
|
90 |
+
'error_rate': [0.103619, 0.050286, 0.024762, 0.008762, 0.007238],
|
91 |
+
'time': ['17:05', '12:41', '12:12', '14:43', '12:30']
|
92 |
+
}
|
93 |
+
|
94 |
+
df = pd.DataFrame(unfreezed_data)
|
95 |
+
|
96 |
+
# Create a Streamlit table
|
97 |
+
st.table(df)
|
98 |
+
|
99 |
+
#######################################################################################################################
|
models/birds_learner.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5c1e6951eb889c8ccc74ce374ad895dbc75729528cb9391869906db7f940281c
|
3 |
+
size 105634905
|
models/birds_vocab.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ba7936fc583eab66f5c0007492366a946ac7d19a411214f04122e97027214238
|
3 |
+
size 12695
|
models/freezed_model_summary.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:bdca1fa49df71b40420b42e95803dbc66eb1ddc4bbf304b837099c74e2f7370c
|
3 |
+
size 13999
|
models/unfreezed_model_summary.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e7cf8743aa71a4cbb2a86a7cdf5447b58f5e438dd1438343b00bd5312ac3b79e
|
3 |
+
size 13968
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
fastai
|
2 |
+
streamlit
|
3 |
+
streamlit-option-menu
|