Configure EuroSAT Field Scout submission
#1
by yava-code - opened
- .gitattributes +5 -0
- .hfignore +11 -0
- FIELD_NOTES.md +46 -0
- README.md +113 -6
- app.py +80 -4
- examples/annualcrop_sample.jpg +0 -0
- examples/forest_sample.jpg +0 -0
- examples/highway_sample.jpg +0 -0
- examples/industrial_sample.jpg +0 -0
- examples/residential_sample.jpg +0 -0
- examples/sealake_sample.jpg +0 -0
- model.py +64 -0
- requirements.txt +5 -0
- weights/simple_net_v1.part00 +3 -0
- weights/simple_net_v1.part01 +3 -0
- weights/simple_net_v1.part02 +3 -0
- weights/simple_net_v1.part03 +3 -0
- weights/simple_net_v1.part04 +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
weights/simple_net_v1.part00 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
weights/simple_net_v1.part01 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
weights/simple_net_v1.part02 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
weights/simple_net_v1.part03 filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
weights/simple_net_v1.part04 filter=lfs diff=lfs merge=lfs -text
|
.hfignore
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git/
|
| 2 |
+
.cache/
|
| 3 |
+
.hf-inspect/
|
| 4 |
+
output/
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.ipynb
|
| 7 |
+
full_model.pth
|
| 8 |
+
simple_net_v1.pth
|
| 9 |
+
simple_net_v1.weights
|
| 10 |
+
demo.mp4
|
| 11 |
+
2026-06-13 19-57-34.mp4
|
FIELD_NOTES.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EuroSAT Field Scout - Field Notes
|
| 2 |
+
|
| 3 |
+
## What I Built
|
| 4 |
+
|
| 5 |
+
EuroSAT Field Scout is a small Gradio app that classifies Sentinel-style land
|
| 6 |
+
tiles into 10 EuroSAT scene classes. The goal is not to replace a GIS workflow.
|
| 7 |
+
It is a quick first-pass helper for students, mapping volunteers, or anyone who
|
| 8 |
+
wants to sort a handful of tiles before deeper review.
|
| 9 |
+
|
| 10 |
+
## Why It Fits Build Small
|
| 11 |
+
|
| 12 |
+
The deployed model is a custom PyTorch CNN with 2,492,170 parameters. It is far
|
| 13 |
+
below the 32B parameter cap and runs on CPU inside the Space. There are no cloud
|
| 14 |
+
inference calls and no hidden model API. The repo contains the weights as small
|
| 15 |
+
`weights/simple_net_v1.part*` chunks, and `app.py` reconstructs them at startup.
|
| 16 |
+
|
| 17 |
+
## Implementation Notes
|
| 18 |
+
|
| 19 |
+
- `model.py` defines `SimpleNet`, a four-block convolutional classifier.
|
| 20 |
+
- `app.py` handles image upload, preprocessing, softmax inference, and Gradio UI.
|
| 21 |
+
- `examples/` contains small RGB EuroSAT samples from `torchgeo/eurosat`.
|
| 22 |
+
- The LinkedIn social post contains the short demo video for the hackathon submission.
|
| 23 |
+
|
| 24 |
+
One deployment issue was the original checkpoint format. The model was first
|
| 25 |
+
stored as a full pickled Python object, which is fragile across PyTorch versions.
|
| 26 |
+
For the Space, I converted it to a plain `state_dict`. To avoid Git LFS upload
|
| 27 |
+
issues in the hackathon org, the deployed file stores floating tensors in
|
| 28 |
+
float16 and splits the bytes into small `weights/simple_net_v1.part*` chunks.
|
| 29 |
+
The app joins the chunks in memory and casts tensors back to float32 before
|
| 30 |
+
loading. The app now instantiates the architecture explicitly and loads only
|
| 31 |
+
tensor weights.
|
| 32 |
+
|
| 33 |
+
## Runtime Path
|
| 34 |
+
|
| 35 |
+
1. User uploads an image or clicks a sample.
|
| 36 |
+
2. The image is converted to RGB and resized to 64 x 64.
|
| 37 |
+
3. The tensor is normalized with ImageNet-style mean and std values.
|
| 38 |
+
4. `SimpleNet` runs on CPU.
|
| 39 |
+
5. The app returns the top EuroSAT class probabilities.
|
| 40 |
+
|
| 41 |
+
## Limits
|
| 42 |
+
|
| 43 |
+
The model was built for EuroSAT-style RGB tiles. It is useful for a quick demo
|
| 44 |
+
or educational triage, but it should not be used for production remote-sensing
|
| 45 |
+
decisions without calibration, validation on the target geography, and a clearer
|
| 46 |
+
uncertainty policy.
|
README.md
CHANGED
|
@@ -1,13 +1,120 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
python_version:
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: EuroSAT Field Scout
|
| 3 |
+
emoji: 🛰️
|
| 4 |
+
colorFrom: green
|
| 5 |
colorTo: blue
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.34.0
|
| 8 |
+
python_version: 3.11
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
+
license: mit
|
| 12 |
+
short_description: Local EuroSAT land-use classifier for map triage.
|
| 13 |
+
tags:
|
| 14 |
+
- gradio
|
| 15 |
+
- build-small-hackathon
|
| 16 |
+
- backyard-ai
|
| 17 |
+
- track:backyard
|
| 18 |
+
- small-models
|
| 19 |
+
- tiny-model
|
| 20 |
+
- computer-vision
|
| 21 |
+
- satellite-imagery
|
| 22 |
+
- pytorch
|
| 23 |
+
- eurosat
|
| 24 |
+
- local-first
|
| 25 |
+
- achievement:offgrid
|
| 26 |
+
- achievement:sharing
|
| 27 |
+
- achievement:fieldnotes
|
| 28 |
+
datasets:
|
| 29 |
+
- torchgeo/eurosat
|
| 30 |
---
|
| 31 |
|
| 32 |
+
<div align="center">
|
| 33 |
+
|
| 34 |
+
# EuroSAT Field Scout
|
| 35 |
+
|
| 36 |
+
Small local-first land-use triage for satellite tiles.
|
| 37 |
+
|
| 38 |
+
**Try it:** [Live Space](https://huggingface.co/spaces/build-small-hackathon/EuroSATFieldScout) · [Demo video + social post](https://www.linkedin.com/posts/varfolomiy-yasenoviy-a0209a2a3_gradio-huggingface-hackaton-share-7471607916270080000-W9RZ/) · [Field notes](FIELD_NOTES.md)
|
| 39 |
+
|
| 40 |
+
</div>
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Judges Quick Read
|
| 45 |
+
|
| 46 |
+
- **Track:** Backyard AI
|
| 47 |
+
- **Model cap:** 2.49M-parameter PyTorch CNN, far below the 32B hackathon cap
|
| 48 |
+
- **Runtime:** Gradio Space on CPU
|
| 49 |
+
- **Off the Grid:** no cloud inference API; the app reconstructs local weights from `weights/simple_net_v1.part*`
|
| 50 |
+
- **Use case:** quick first-pass sorting of Sentinel-style land tiles for students, mapping volunteers, and geo demos
|
| 51 |
+
|
| 52 |
+
Upload a satellite or aerial land image and the app predicts the closest EuroSAT
|
| 53 |
+
land-use class:
|
| 54 |
+
|
| 55 |
+
- AnnualCrop
|
| 56 |
+
- Forest
|
| 57 |
+
- HerbaceousVegetation
|
| 58 |
+
- Highway
|
| 59 |
+
- Industrial
|
| 60 |
+
- Pasture
|
| 61 |
+
- PermanentCrop
|
| 62 |
+
- Residential
|
| 63 |
+
- River
|
| 64 |
+
- SeaLake
|
| 65 |
+
|
| 66 |
+
## Demo
|
| 67 |
+
|
| 68 |
+
The short demo video is attached to the LinkedIn social post:
|
| 69 |
+
|
| 70 |
+
https://www.linkedin.com/posts/varfolomiy-yasenoviy-a0209a2a3_gradio-huggingface-hackaton-share-7471607916270080000-W9RZ/
|
| 71 |
+
|
| 72 |
+
## Model
|
| 73 |
+
|
| 74 |
+
`SimpleNet` is a lightweight convolutional neural network trained on EuroSAT.
|
| 75 |
+
The Space reconstructs the local weights from `weights/simple_net_v1.part*` and
|
| 76 |
+
runs inference on CPU.
|
| 77 |
+
|
| 78 |
+
| Component | Details |
|
| 79 |
+
| --- | --- |
|
| 80 |
+
| Architecture | Four Conv-BN-ReLU-Pool blocks plus a dense classifier |
|
| 81 |
+
| Parameters | 2,492,170 |
|
| 82 |
+
| Input | RGB image resized to 64 x 64 |
|
| 83 |
+
| Output | 10 EuroSAT land-use classes |
|
| 84 |
+
| Inference | Local PyTorch CPU inference inside the Space |
|
| 85 |
+
|
| 86 |
+
## Build Notes
|
| 87 |
+
|
| 88 |
+
The checkpoint originally existed as a pickled full model object. For a safer Space
|
| 89 |
+
deploy, it was converted to a plain PyTorch `state_dict` in `simple_net_v1.pth`.
|
| 90 |
+
That avoids PyTorch pickle compatibility issues and makes the app startup path
|
| 91 |
+
simple: instantiate `SimpleNet`, load weights, run inference.
|
| 92 |
+
|
| 93 |
+
For the hackathon Space upload, the state dict is stored as float16 tensors and
|
| 94 |
+
split into small `weights/simple_net_v1.part*` chunks so the submission can be
|
| 95 |
+
reviewed without Git LFS write permissions. The app rebuilds the bytes in memory
|
| 96 |
+
and casts floating tensors back to float32 before loading them into the model.
|
| 97 |
+
|
| 98 |
+
## Badges
|
| 99 |
+
|
| 100 |
+
| Badge | Status | Why it fits |
|
| 101 |
+
| --- | --- | --- |
|
| 102 |
+
| Backyard AI | Submitted | Helps with a practical local mapping workflow |
|
| 103 |
+
| Off the Grid | Submitted | No hosted LLM or remote inference service |
|
| 104 |
+
| Sharing is Caring | Submitted | LinkedIn post and reusable Space source are public |
|
| 105 |
+
| Field Notes | Submitted | `FIELD_NOTES.md` documents the build and deployment choices |
|
| 106 |
+
|
| 107 |
+
## Running Locally
|
| 108 |
+
|
| 109 |
+
```bash
|
| 110 |
+
pip install -r requirements.txt
|
| 111 |
+
python app.py
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
## Submission Links
|
| 115 |
+
|
| 116 |
+
| Item | Link |
|
| 117 |
+
| --- | --- |
|
| 118 |
+
| Live Space | https://huggingface.co/spaces/build-small-hackathon/EuroSATFieldScout |
|
| 119 |
+
| Demo video + social post | https://www.linkedin.com/posts/varfolomiy-yasenoviy-a0209a2a3_gradio-huggingface-hackaton-share-7471607916270080000-W9RZ/ |
|
| 120 |
+
| Field notes | [`FIELD_NOTES.md`](FIELD_NOTES.md) |
|
app.py
CHANGED
|
@@ -1,7 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
| 7 |
-
demo.launch()
|
|
|
|
| 1 |
+
from io import BytesIO
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
import gradio as gr
|
| 6 |
+
from torchvision import transforms
|
| 7 |
+
from PIL import Image
|
| 8 |
+
|
| 9 |
+
from model import SimpleNet, CLASS_NAMES
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
WEIGHTS_DIR = Path("weights")
|
| 13 |
+
EXAMPLES = [
|
| 14 |
+
["examples/annualcrop_sample.jpg"],
|
| 15 |
+
["examples/forest_sample.jpg"],
|
| 16 |
+
["examples/highway_sample.jpg"],
|
| 17 |
+
["examples/industrial_sample.jpg"],
|
| 18 |
+
["examples/residential_sample.jpg"],
|
| 19 |
+
["examples/sealake_sample.jpg"],
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def load_model():
|
| 24 |
+
model = SimpleNet(num_classes=10)
|
| 25 |
+
weight_bytes = b"".join(
|
| 26 |
+
path.read_bytes() for path in sorted(WEIGHTS_DIR.glob("simple_net_v1.part*"))
|
| 27 |
+
)
|
| 28 |
+
state_dict = torch.load(BytesIO(weight_bytes), map_location="cpu")
|
| 29 |
+
state_dict = {
|
| 30 |
+
name: tensor.float() if torch.is_floating_point(tensor) else tensor
|
| 31 |
+
for name, tensor in state_dict.items()
|
| 32 |
+
}
|
| 33 |
+
model.load_state_dict(state_dict)
|
| 34 |
+
model.eval()
|
| 35 |
+
return model
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
model = load_model()
|
| 39 |
+
|
| 40 |
+
preprocess = transforms.Compose([
|
| 41 |
+
transforms.Resize((64, 64)),
|
| 42 |
+
transforms.ToTensor(),
|
| 43 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406],
|
| 44 |
+
std=[0.229, 0.224, 0.225]),
|
| 45 |
+
])
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def predict(image: Image.Image) -> dict[str, float]:
|
| 49 |
+
if image is None:
|
| 50 |
+
return {}
|
| 51 |
+
|
| 52 |
+
image = image.convert("RGB")
|
| 53 |
+
tensor = preprocess(image).unsqueeze(0) # [1, 3, 64, 64]
|
| 54 |
+
|
| 55 |
+
with torch.no_grad():
|
| 56 |
+
logits = model(tensor)
|
| 57 |
+
probs = torch.nn.functional.softmax(logits, dim=1)[0]
|
| 58 |
+
|
| 59 |
+
return {CLASS_NAMES[i]: float(probs[i]) for i in range(len(CLASS_NAMES))}
|
| 60 |
+
|
| 61 |
|
| 62 |
+
demo = gr.Interface(
|
| 63 |
+
fn=predict,
|
| 64 |
+
inputs=gr.Image(type="pil", label="Upload a Sentinel-style land image"),
|
| 65 |
+
outputs=gr.Label(num_top_classes=5, label="Top land-use guesses"),
|
| 66 |
+
title="EuroSAT Field Scout",
|
| 67 |
+
description=(
|
| 68 |
+
"A small local-first Gradio classifier for quick land-use triage. "
|
| 69 |
+
"It runs a custom PyTorch CNN trained on EuroSAT and returns the closest scene class."
|
| 70 |
+
),
|
| 71 |
+
article=(
|
| 72 |
+
"Built for the Build Small Hackathon Backyard AI track. "
|
| 73 |
+
"No cloud inference API, no giant model: the Space loads local weights "
|
| 74 |
+
"and runs CPU inference inside the app."
|
| 75 |
+
),
|
| 76 |
+
examples=EXAMPLES,
|
| 77 |
+
cache_examples=False,
|
| 78 |
+
allow_flagging="never",
|
| 79 |
+
theme=gr.themes.Soft(),
|
| 80 |
+
)
|
| 81 |
|
| 82 |
+
if __name__ == "__main__":
|
| 83 |
+
demo.queue(default_concurrency_limit=2).launch()
|
examples/annualcrop_sample.jpg
ADDED
|
examples/forest_sample.jpg
ADDED
|
examples/highway_sample.jpg
ADDED
|
examples/industrial_sample.jpg
ADDED
|
examples/residential_sample.jpg
ADDED
|
examples/sealake_sample.jpg
ADDED
|
model.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
SimpleNet — lightweight CNN for EuroSAT satellite image classification.
|
| 3 |
+
4 convolutional blocks (double-and-halve pattern) + FC classifier.
|
| 4 |
+
Input: 3×64×64 RGB | Output: 10 land-use classes | ~2.49M parameters
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
CLASS_NAMES = [
|
| 12 |
+
"AnnualCrop", "Forest", "HerbaceousVegetation", "Highway",
|
| 13 |
+
"Industrial", "Pasture", "PermanentCrop", "Residential",
|
| 14 |
+
"River", "SeaLake"
|
| 15 |
+
]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class SimpleNet(nn.Module):
|
| 19 |
+
def __init__(self, num_classes: int = 10):
|
| 20 |
+
super().__init__()
|
| 21 |
+
|
| 22 |
+
# 64×64 → 32×32
|
| 23 |
+
self.block1 = nn.Sequential(
|
| 24 |
+
nn.Conv2d(3, 32, kernel_size=3, padding=1),
|
| 25 |
+
nn.BatchNorm2d(32),
|
| 26 |
+
nn.ReLU(),
|
| 27 |
+
nn.MaxPool2d(2),
|
| 28 |
+
)
|
| 29 |
+
# 32×32 → 16×16
|
| 30 |
+
self.block2 = nn.Sequential(
|
| 31 |
+
nn.Conv2d(32, 64, kernel_size=3, padding=1),
|
| 32 |
+
nn.BatchNorm2d(64),
|
| 33 |
+
nn.ReLU(),
|
| 34 |
+
nn.MaxPool2d(2),
|
| 35 |
+
)
|
| 36 |
+
# 16×16 → 8×8
|
| 37 |
+
self.block3 = nn.Sequential(
|
| 38 |
+
nn.Conv2d(64, 128, kernel_size=3, padding=1),
|
| 39 |
+
nn.BatchNorm2d(128),
|
| 40 |
+
nn.ReLU(),
|
| 41 |
+
nn.MaxPool2d(2),
|
| 42 |
+
)
|
| 43 |
+
# 8×8 → 4×4
|
| 44 |
+
self.block4 = nn.Sequential(
|
| 45 |
+
nn.Conv2d(128, 256, kernel_size=3, padding=1),
|
| 46 |
+
nn.BatchNorm2d(256),
|
| 47 |
+
nn.ReLU(),
|
| 48 |
+
nn.MaxPool2d(2),
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
self.classifier = nn.Sequential(
|
| 52 |
+
nn.Flatten(),
|
| 53 |
+
nn.Linear(256 * 4 * 4, 512),
|
| 54 |
+
nn.ReLU(),
|
| 55 |
+
nn.Dropout(0.3),
|
| 56 |
+
nn.Linear(512, num_classes),
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 60 |
+
x = self.block1(x)
|
| 61 |
+
x = self.block2(x)
|
| 62 |
+
x = self.block3(x)
|
| 63 |
+
x = self.block4(x)
|
| 64 |
+
return self.classifier(x)
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch>=2.2,<3
|
| 2 |
+
torchvision>=0.17,<1
|
| 3 |
+
gradio>=5.34,<6
|
| 4 |
+
huggingface_hub>=0.25,<1
|
| 5 |
+
Pillow>=10
|
weights/simple_net_v1.part00
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8681a2e1cb7d4188af217f57fdda104d07664455a779cdc241dd39008c102304
|
| 3 |
+
size 1000000
|
weights/simple_net_v1.part01
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d139af73bfe778f069f0fe9c373a81b401a93f60103b90228bef271345077b46
|
| 3 |
+
size 1000000
|
weights/simple_net_v1.part02
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8a8e3bd5e1863a6e2976bacfd504ff972d2640f198c49fade23e78232ea464af
|
| 3 |
+
size 1000000
|
weights/simple_net_v1.part03
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:280199fe980e2ee5845ce5cbe9e7ee56dd5493384b69c970cbf1d9367d2f5d63
|
| 3 |
+
size 1000000
|
weights/simple_net_v1.part04
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8c08cfae02a27254d8aac9d9b5ff7e861010ee8de995aac3471c89205ed576f3
|
| 3 |
+
size 996327
|