Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
!uv.lock
!pytest.ini
!setup_tests.py
!huggingface/
!huggingface/**
!deepethogram/
!deepethogram/**
!tests/
Expand Down
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Release files must keep the exact bytes published to Hugging Face, which SHA256SUMS covers.
# Without this, Windows checkouts convert them to CRLF and the checksums stop matching.
huggingface/** -text
6 changes: 4 additions & 2 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
# Full history so the review can diff the PR against its merge base.
fetch-depth: 0

- name: Run Claude Code Review
id: claude-review
Expand All @@ -41,7 +42,8 @@ jobs:
prompt: '/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
use_sticky_comment: true
track_progress: true
claude_args: '--allowedTools "Bash(gh pr:*),Bash(gh api:*),Bash(gh diff:*)"'
# Covers the code-review plugin's own allowed-tools, plus read-only git for diffs and history.
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh search:*),Bash(gh api:*),Bash(git diff:*),Bash(git log:*),Bash(git show:*),Bash(git merge-base:*),Bash(git blame:*)"'

- name: Report Claude startup failure
if: ${{ failure() && steps.claude-review.outcome == 'failure' }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gpu-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
with:
python-version: "3.11"

- name: Run GPU tests on Modal
- name: Run GPU tests on Modal with pinned Hugging Face checkpoints
env:
# Required repository secrets:
# MODAL_TOKEN_ID
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,16 @@ jobs:
- name: Install dependencies
run: uv sync --locked --dev

- name: Setup test data
# About 1.2 GB of checkpoints, identical on every OS, so all three jobs share one cache entry.
# setup_tests.py re-hashes restored files against the checked-in checksums without contacting the Hub.
- name: Cache pinned Hugging Face checkpoints
uses: actions/cache@v6
with:
path: tests/DATA/huggingface_checkpoints
key: hf-checkpoints-${{ hashFiles('huggingface/models.json') }}
enableCrossOsArchive: true

- name: Setup video fixtures and pinned Hugging Face checkpoints
run: uv run python setup_tests.py

- name: Run CPU tests
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ deepethogram/models/*
tests/DATA/testing_deepethogram
tests/DATA/testing_deepethogram_archive
tests/DATA/testing_deepethogram_archive.zip
tests/DATA/huggingface_checkpoints
gui_logs/
.vscode/
# line profiling
Expand Down
46 changes: 39 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,33 @@ We now support docker! Docker is a way to run `deepethogram` in completely repro
with other system dependencies. [See docs/Docker for more information](docs/docker.md)

## Pretrained models
Rather than start from scratch, we will start with model weights pretrained on the Kinetics700 dataset. Go to
To download the pretrained weights, please use [this Google Drive link](https://drive.google.com/file/d/1ntIZVbOG1UAiFVlsAAuKEBEVCVevyets/view?usp=sharing).
Unzip the files in your `project/models` directory. Make sure that you don't add an extra directory when unzipping! The path should be
`your_project/models/pretrained_models/{models 1:6}`, not `your_project/models/pretrained_models/pretrained_models/{models1:6}`.

Download individual models from the [DeepEthogram Hugging Face collection](https://huggingface.co/collections/jbohnslav/deepethogram-pretrained-models-6ad61e8af1ac368c56d8deec).
Each model has its own weights, model card, configuration, license, and checksum manifest.

| Model | Role | Typical use |
| --- | --- | --- |
| [TinyMotionNet](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet) | Optical-flow generator | DEG-f flow initialization |
| [MotionNet](https://huggingface.co/jbohnslav/deepethogram-motionnet) | Optical-flow generator | DEG-m flow initialization |
| [TinyMotionNet3D](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet3d) | Optical-flow generator | 3D optical flow |
| [DEG-f](https://huggingface.co/jbohnslav/deepethogram-deg-f) | Feature extractor | Fast behavior feature-extractor initialization |
| [DEG-m](https://huggingface.co/jbohnslav/deepethogram-deg-m) | Feature extractor | Medium behavior feature-extractor initialization |
| [DEG-s](https://huggingface.co/jbohnslav/deepethogram-deg-s) | Feature extractor | Slow behavior feature-extractor initialization |

For DEG-f, install the [Hugging Face CLI](https://huggingface.co/docs/huggingface_hub/guides/cli)
and download its flow generator and feature extractor into your existing project's model directory:

```bash
hf download jbohnslav/deepethogram-tinymotionnet --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200221_115158_TinyMotionNet
hf download jbohnslav/deepethogram-deg-f --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200415_125824_hidden_two_stream_kinetics_degf
```

Run `shasum -a 256 -c SHA256SUMS` inside each downloaded run directory to verify its files.
See [pretrained model setup](docs/pretrained_models.md) for DEG-m, individual downloads, and loading details.
These weights initialize training on your own data; they do not classify your behavior labels without fine-tuning.

The [legacy bundle](https://huggingface.co/jbohnslav/deepethogram-pretrained) retains the original six-folder archive,
including the incomplete DEG-s feature extractor. It is available for historical compatibility.

## Licensing
Copyright (c) 2020 - President and Fellows of Harvard College. All rights reserved.
Expand Down Expand Up @@ -112,9 +135,18 @@ Some older ones might also be fine, like a `1080` or even `1070 Ti`/ `1070`.
## testing
Test coverage is still low, but in the future we will be expanding our unit tests.

First, download a copy of [`testing_deepethogram_archive.zip`](https://drive.google.com/file/d/1IFz4ABXppVxyuhYik8j38k9-Fl9kYKHo/view?usp=sharing)
Make a directory in tests called `DATA`. Unzip this and move it to the `deepethogram/tests/DATA`
directory, so that the path is `deepethogram/tests/DATA/testing_deepethogram_archive/{DATA,models,project_config.yaml}`.
Prepare the video fixtures and the same Hugging Face checkpoints documented above:

```bash
uv sync --locked --dev
uv run python setup_tests.py
```

Video fixtures still come from the existing test archive. All six pretrained models are downloaded
from the individual Hugging Face repos at the exact commits in [`huggingface/models.json`](huggingface/models.json)
and verified against checked-in checksums, even when the video fixtures already exist.
CPU CI and the Modal GPU test runner use this setup. The CPU suite checks checkpoint identity,
strict parameter loading, forward-pass outputs, and project discovery for all six releases.

To run tests:
```bash
Expand Down
7 changes: 5 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ CMD ["python", "-m", "deepethogram"]
FROM builder-gui AS test-gui
ENV DEG_VERSION=gui
COPY pytest.ini setup_tests.py ./
COPY huggingface ./huggingface
COPY tests ./tests
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
Expand All @@ -215,14 +216,16 @@ CMD ["sh", "-lc", "python setup_tests.py && xvfb-run --auto-servernum --server-a

FROM builder-headless AS test-headless
COPY pytest.ini setup_tests.py ./
COPY huggingface ./huggingface
COPY tests ./tests
RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \
uv sync --locked --no-editable
CMD ["pytest", "-v"]
CMD ["sh", "-c", "python setup_tests.py && pytest -v"]

FROM builder-full AS test-full
COPY pytest.ini setup_tests.py ./
COPY huggingface ./huggingface
COPY tests ./tests
RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \
uv sync --locked --no-editable
CMD ["pytest", "-v"]
CMD ["sh", "-c", "python setup_tests.py && pytest -v"]
27 changes: 15 additions & 12 deletions docker/build_and_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ verify_gpu() {

print_header "Verifying GPU access for ${image}"
echo_run docker run ${gpu_flag:+$gpu_flag} --rm "$image" nvidia-smi
docker run ${gpu_flag:+$gpu_flag} --rm "$image" nvidia-smi >/dev/null

echo_run docker run ${gpu_flag:+$gpu_flag} --rm "$image" python -c "import torch; assert torch.cuda.is_available(); print('CUDA available')"
docker run ${gpu_flag:+$gpu_flag} --rm "$image" python -c "import torch; assert torch.cuda.is_available(); print('CUDA available')" >/dev/null
}

smoke_test_runtime() {
Expand All @@ -50,11 +47,9 @@ smoke_test_runtime() {

print_header "Smoke testing $target runtime image"
echo_run docker run ${gpu_flag:+$gpu_flag} --rm "deepethogram:$target" python -c "import deepethogram"
docker run ${gpu_flag:+$gpu_flag} --rm "deepethogram:$target" python -c "import deepethogram"

if [[ "$target" == "gui" || "$target" == "full" ]]; then
echo_run docker run ${gpu_flag:+$gpu_flag} --rm "deepethogram:$target" python -c "from deepethogram.gui import main"
docker run ${gpu_flag:+$gpu_flag} --rm "deepethogram:$target" python -c "from deepethogram.gui import main"
fi
}

Expand All @@ -63,18 +58,26 @@ run_pytest_target() {
local gpu_flag=$2
local marker=$3
local image="deepethogram:$target"
local mount_args=()
local data="$PWD/tests/DATA"

if [[ -d tests/DATA ]]; then
mount_args=(-v "$PWD/tests/DATA:/app/tests/DATA:ro")
else
echo -e "${YELLOW}Skipping pytest for ${target}: tests/DATA is not present locally.${NC}"
if [[ ! -d "$data/testing_deepethogram_archive" ]]; then
echo -e "${YELLOW}Skipping pytest for ${target}: run 'uv run python setup_tests.py' first.${NC}"
return 0
fi

# The tests write into tests/DATA: they rewrite the archive's project_config.yaml and copy it into a
# project. So the archive is mounted read-only elsewhere and copied into the container. Verified checkpoint
# downloads are only read, so they are mounted in place and setup_tests.py reuses them without the Hub.
local mount_args=(-v "$data/testing_deepethogram_archive:/fixtures/testing_deepethogram_archive:ro")
if [[ -d "$data/huggingface_checkpoints" ]]; then
mount_args+=(-v "$data/huggingface_checkpoints:/app/tests/DATA/huggingface_checkpoints:ro")
fi
local command="mkdir -p tests/DATA && cp -a /fixtures/testing_deepethogram_archive tests/DATA/"
command+=" && python setup_tests.py && pytest -v -m '$marker' tests/"

print_header "Running ${marker} tests in ${target}"
echo_run docker run ${gpu_flag:+$gpu_flag} --rm "${mount_args[@]}" "$image" pytest -v -m "$marker" tests/
docker run ${gpu_flag:+$gpu_flag} --rm "${mount_args[@]}" "$image" pytest -v -m "$marker" tests/
echo_run docker run ${gpu_flag:+$gpu_flag} --rm -e QT_QPA_PLATFORM=offscreen "${mount_args[@]}" "$image" \
sh -c "$command"
}

main() {
Expand Down
19 changes: 15 additions & 4 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,21 @@ and save them to disk. This might take a few moments (or minutes for very long v
and inference.

## Download models
Rather than start from scratch, we will start with model weights pretrained on the Kinetics700 dataset. Go to
To download the pretrained weights, please use [this Google Drive link](https://drive.google.com/file/d/1ntIZVbOG1UAiFVlsAAuKEBEVCVevyets/view?usp=sharing).
Unzip the files in your `project/models` directory. The path should be
`your_project/models/pretrained/{models 1:6}`.

Choose DEG-f or DEG-m from the [pretrained model setup guide](pretrained_models.md).
Each model is published separately on Hugging Face. For the DEG-f preset, download
[TinyMotionNet](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet) and
[DEG-f](https://huggingface.co/jbohnslav/deepethogram-deg-f) using the
[Hugging Face CLI](https://huggingface.co/docs/huggingface_hub/guides/cli):

```bash
hf download jbohnslav/deepethogram-tinymotionnet --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200221_115158_TinyMotionNet
hf download jbohnslav/deepethogram-deg-f --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200415_125824_hidden_two_stream_kinetics_degf
```

Keep `checkpoint.pt` and `hyperparameters.yaml` at the root of each downloaded run folder.
Run `shasum -a 256 -c SHA256SUMS` inside each run folder to verify it.
DEG-m and DEG-s use their corresponding flow-generator/feature-extractor pairs; see the setup guide.

## Start training the flow generator
These models (see paper) estimate local motion from video frames. They are trained in a self-supervised manner, so they
Expand Down
99 changes: 99 additions & 0 deletions docs/pretrained_models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Pretrained model setup

The [DeepEthogram collection](https://huggingface.co/collections/jbohnslav/deepethogram-pretrained-models-6ad61e8af1ac368c56d8deec) groups six individual model repositories.
Download only the models your preset needs. Install DeepEthogram and the
[Hugging Face CLI](https://huggingface.co/docs/huggingface_hub/guides/cli) first.

## Choose a preset

| Preset | Optical-flow generator | Feature extractor |
| --- | --- | --- |
| `deg_f` | [TinyMotionNet](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet) | [DEG-f / ResNet18](https://huggingface.co/jbohnslav/deepethogram-deg-f) |
| `deg_m` | [MotionNet](https://huggingface.co/jbohnslav/deepethogram-motionnet) | [DEG-m / ResNet50](https://huggingface.co/jbohnslav/deepethogram-deg-m) |
| `deg_s` | [TinyMotionNet3D](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet3d) | [DEG-s / ResNet3D-34](https://huggingface.co/jbohnslav/deepethogram-deg-s) |

For the standard DeepEthogram training workflow, download both entries in your preset's row.
Full DEG-f/DEG-m/DEG-s checkpoints include embedded flow weights and can also be loaded directly
without another download; the training workflow selects flow and feature-extractor initialization separately.
These models initialize training on your own labels; a trained behavior pipeline also requires
fine-tuning and a sequence model.

## Individual downloads

Each command creates a separate run folder under your existing project's `models/pretrained_models`.
The `v1.0` release tags pin the pretrained weights.

### TinyMotionNet

[Model card](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet)

```bash
hf download jbohnslav/deepethogram-tinymotionnet --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200221_115158_TinyMotionNet
```

### MotionNet

[Model card](https://huggingface.co/jbohnslav/deepethogram-motionnet)

```bash
hf download jbohnslav/deepethogram-motionnet --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200310_174416_MotionNet_kinetics
```

### TinyMotionNet3D

[Model card](https://huggingface.co/jbohnslav/deepethogram-tinymotionnet3d)

```bash
hf download jbohnslav/deepethogram-tinymotionnet3d --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200313_125331_TinyMotionNet3D_kinetics
```

### DEG-f

[Model card](https://huggingface.co/jbohnslav/deepethogram-deg-f)

```bash
hf download jbohnslav/deepethogram-deg-f --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200415_125824_hidden_two_stream_kinetics_degf
```

### DEG-m

[Model card](https://huggingface.co/jbohnslav/deepethogram-deg-m)

```bash
hf download jbohnslav/deepethogram-deg-m --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200410_142156_hidden_two_stream_kinetics_degm
```

### DEG-s

[Model card](https://huggingface.co/jbohnslav/deepethogram-deg-s)

```bash
hf download jbohnslav/deepethogram-deg-s --revision v1.0 --local-dir /path/to/project/models/pretrained_models/200409_101808_hidden_two_stream_kinetics_degs
```

## Verify and use

From each downloaded run folder:

```bash
shasum -a 256 -c SHA256SUMS
```

DeepEthogram discovers each `checkpoint.pt` using the neighboring `hyperparameters.yaml`.
Keep the run folder names shown above. Select the appropriate preset in the GUI or CLI;
see [CLI training](using_CLI.md) for project setup and training commands.
Each model card also provides a strict-load Python example and synthetic forward-pass example.
No new package dependency or model-loader changes are required.

The historical YAML files contain original filesystem paths; they describe pretraining, rather
than serving as a portable project config. Use the new run created by fine-tuning for behavior
inference: the initialization repos do not contain the newer `config.yaml` and inference metrics.

## Legacy archive

The [original six-folder bundle](https://huggingface.co/jbohnslav/deepethogram-pretrained)
remains available with historical plots, metrics, separate stream snapshots and an older incomplete DEG-s folder.
The individual DEG-s repository provides the complete checkpoint used in the test suite.
Its structure differs: downloading that bundle into `models` creates the entire `pretrained_models` tree.
The [historical Google Drive ZIP](https://drive.google.com/file/d/1ntIZVbOG1UAiFVlsAAuKEBEVCVevyets/view)
is retained as a fallback; it was not used for these releases.
11 changes: 6 additions & 5 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ uv run pytest -m ""

### Test Data Setup

Before running tests:
`uv run python setup_tests.py` prepares everything the tests need:

1. Download [`testing_deepethogram_archive.zip`](https://drive.google.com/file/d/1IFz4ABXppVxyuhYik8j38k9-Fl9kYKHo/view?usp=sharing)
2. Create a directory called `DATA` in the tests directory
3. Unzip the archive and move its contents to `deepethogram/tests/DATA/testing_deepethogram_archive/`
4. Verify the path structure: `deepethogram/tests/DATA/testing_deepethogram_archive/{DATA,models,project_config.yaml}`
1. It downloads the video fixture archive from Google Drive into `tests/DATA/testing_deepethogram_archive/`, unless that folder already has `DATA`, `models` and `project_config.yaml`.
2. It downloads the six published checkpoints from Hugging Face at the commits pinned in `huggingface/models.json`, into `tests/DATA/huggingface_checkpoints/`. It checks each model's release tag resolves to its pinned commit, and verifies every file against the checksums in `huggingface/models/<model>/SHA256SUMS`.
3. It copies those six runs into `tests/DATA/testing_deepethogram_archive/models/pretrained_models/`, removing anything else there.

Re-run it after pulling changes to `huggingface/models.json`. Once the downloads are verified, later runs reuse them without contacting Hugging Face, so setup also works offline. Don't unzip the archive by hand: its bundled checkpoints are older snapshots without the release manifests, and `tests/test_pretrained_models.py` fails on them.

## Writing Tests

Expand Down
Loading
Loading