Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d00ce3b
started taking some notes on preprocessing.
Mar 4, 2025
7a58426
added and tested pytorch data module
Mar 4, 2025
5d14ac5
added source code for a minimal implementation of single session AE.
Mar 5, 2025
602a8e2
added rudimentary tests of function for all components. Can get a bab…
Mar 5, 2025
16ae258
added config params for training of ALM network.
Mar 5, 2025
f8f0ca6
have a script that should in theory train
Mar 5, 2025
38cf713
got training script to run in dev mode.
Mar 5, 2025
e7ec537
added configs for debugging and training.
Mar 5, 2025
75d36be
fixed some attributes in dataloading.
Mar 5, 2025
111a808
deleted crop configs
Mar 5, 2025
a005bf8
fixed up tests for data configs.
Mar 5, 2025
7a43ab6
added some docstring for bad stub
Mar 5, 2025
67898c3
fixed merge conflict
Mar 5, 2025
8de0168
some messaging for the dataloading.
Mar 5, 2025
fc55d3b
finishing merge
Mar 5, 2025
5c98441
updated mean image calc.
Mar 5, 2025
f799621
updated dataloading
Mar 5, 2025
dd43809
fixed argument passing
Mar 5, 2025
2ed86c5
issue with editor
Mar 5, 2025
d1e7dc1
trying to keep all predictions in memory is infeasible. save out trai…
Mar 5, 2025
7acd6f9
tested additional flags on valset
Mar 5, 2025
f15553c
eval test failing for some reason
Mar 5, 2025
08c20bb
adapted training for high frame count
Mar 5, 2025
4d8ade9
added back mean image when predicting
Mar 5, 2025
da2ccfa
added learning rate monitoring and fixed checkpointing conditions.
Mar 6, 2025
01cbca0
played with learning rate scheduling.
Mar 6, 2025
ca177ee
added chained lr scheduler
Mar 6, 2025
d2bcfb9
Merge branch 'taiga-dev' of https://github.com/druckmann-lab/VideoAna…
Mar 6, 2025
ff7e235
arg order wrong for chainedscheduler
Mar 6, 2025
f7113eb
step scheduler needs to be passed the raw functions, not dict
Mar 6, 2025
f54ffc8
was scheduling on epochs, not steps!
Mar 7, 2025
5103a09
added string matching to only create some frames.
Mar 7, 2025
658e3ce
started building stub of eval
Mar 19, 2025
8d861ae
make sure we save data path.
Mar 19, 2025
9ea6498
added sequence dataset (used for eval for now)
Mar 19, 2025
9527a36
the parameter for the trainer set up should be fit, not train
Mar 19, 2025
6ac0558
added evaluation script that saves out results per trial
Mar 20, 2025
665ddc0
moved things around so we have a preliminary version of eval loop
Mar 20, 2025
47d9b16
played with changes to training and added caching
Mar 20, 2025
91b6cde
subset of data config variables are useful.
Mar 20, 2025
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
5 changes: 0 additions & 5 deletions configs/crop_configs/alm_side.json

This file was deleted.

8 changes: 8 additions & 0 deletions configs/data_configs/alm_side.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"proportional_h_coord_top": 0.21666666666666667,
"target_h": 120,
"target_w": 112,
"extension": ".png",
"trial_pattern": "^CW44_20240522153954_side_trial_"

}
24 changes: 24 additions & 0 deletions configs/model_configs/alm_default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"num_blocks": 2,
"in_channels_0": 1,
"out_channels_0": 16,
"in_channels_1": 16,
"out_channels_1": 32,
"kernel_preconv": 3,
"stride_preconv": 1,
"pool_size_preconv_0": 2,
"pool_size_preconv_1": 4,
"use_batch_norm_preconv": false,
"kernel_residual": 3,
"stride_residual": 1,
"use_batch_norm_residual": false,
"pool_size_residual_0": null,
"pool_size_residual_1": 4,
"n_layers_residual": 3,
"out_conv": 288,
"out_linear": 128,
"embed_size": 16,
"use_batch_norm_linear": false,
"image_height": 120,
"image_width": 112
}
17 changes: 17 additions & 0 deletions configs/train_configs/alm_default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"scheduler":"linear",
"start_factor":0.1,
"end_factor":1,
"warmup_steps":10,
"max_epochs":500,
"weight_decay":0,
"l2_weight":1,
"learning_rate":0,
"batch_size":10,
"num_workers":2,
"subsample_rate":10,
"subsample_offset":0,
"max_epochs":500,
"accelerator":"gpu"
"fast_dev_run":false
}
17 changes: 17 additions & 0 deletions configs/train_configs/alm_default_dev.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"scheduler":"linear",
"start_factor":0.1,
"end_factor":1,
"warmup_steps":10,
"max_epochs":500,
"weight_decay":0,
"l2_weight":1,
"learning_rate":0,
"batch_size":10,
"num_workers":2,
"subsample_rate":10,
"subsample_offset":0,
"max_epochs":100,
"accelerator":"cpu",
"fast_dev_run":true
}
Binary file added figures/CW44_naive_default_crop_img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added figures/CW44_naive_pre_crop_img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added figures/preprocessing_difference_alias.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions notes/preprocessing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Preprocessing description

## Default cropping

[precrop](../figures/CW44_naive_pre_crop_img.png)

[crop](../figures/CW44_naive_default_crop_img.png)

## Antialiasing

Differences greater than 1e-3. Due to aliasing I'm pretty sure.

[alias](../figures/preprocessing_difference_alias.png)
14 changes: 10 additions & 4 deletions requirements_cpu.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
pip install torch # should specify a version later
pip install numpy
pip install matplotlib
pip install pytest
torch # should specify a version later
torchvision
joblib
pytorch_lightning
numpy
matplotlib
pytest
opencv-python
fire
scikit-image
98 changes: 98 additions & 0 deletions scripts/eval_single_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
Evaluate a single session autoencoder.

Assumes that we are given a path to a directory `preds/{modeltype}/date/time/`. Will use the configuration parameters stored there
"""
import os
import datetime
from tqdm import tqdm
import json
import numpy as np
import fire
import torch
import pytorch_lightning as pl
from behavioral_autoencoder.module import SingleSessionModule
from behavioral_autoencoder.dataloading import SessionFramesDataModule
from behavioral_autoencoder.dataset import CropResizeProportion

here = os.path.join(os.path.abspath(os.path.dirname(__file__)))

def eval_trialwise(model,test_dataset,mean_image,path):
"""
"""
for i, image_sequence in tqdm(enumerate(test_dataset)):
reconstructs,latents = model(image_sequence[None,:].cuda())
reconstructs_centered = reconstructs + mean_image.cuda()
folder = test_dataset.trial_folders[i]
savepath = os.path.join(path,folder)
try:
os.mkdir(savepath)
except FileExistsError:
pass
np.save(os.path.join(savepath,"reconstruct.npy"),reconstructs_centered.cpu().detach().numpy())
np.save(os.path.join(savepath,"latents.npy"),latents.cpu().detach().numpy())

def main(data_path,data_config_path,eval_config_path):
"""
get a model path, and use it to load in a given model.
"""
saved_checkpoint_path = os.path.join(".","models","single_session_autoencoder","03-07-25","18_05_06","epoch=99-step=33100.ckpt")
data_dir = os.path.join("home","ubuntu","Data","CW35","2023_12_15","Frames")
metadata_dir = os.path.join(".","preds","single_session_autoencoder","03-07-25","18_05_06")
video_fps = 400
delay_start_time = 2.5+1.3 ## pre-sample and sample time intervals.
delay_end_time = 2.5+1.3+3 ## delay is 3 seconds.
subsample = 10
eval_batch_size=1

## Load in data related stuff

with open(data_config_path,"r") as f:
data_process_config = json.load(f)

with open(eval_config_path,"r") as f:
eval_config = json.load(f)

alm_cropping = CropResizeProportion(data_config_path)
data_config = {
"data_path":data_path,
"transform":alm_cropping,
"extension":data_process_config["extension"],
"trial_pattern":data_process_config["trial_pattern"],
"frame_subset":[f"frame_{i:06d}.png" for i in np.arange(int(delay_start_time*video_fps),int(delay_end_time*video_fps),subsample)]
}

date = datetime.datetime.now().strftime("%m-%d-%y")
time = datetime.datetime.now().strftime("%H_%M_%S")
datestamp_eval = os.path.join(here,"eval",date)
timestamp_eval = os.path.join(here,"eval",date,time)
for path in [datestamp_eval,timestamp_eval]:
try:
os.mkdir(path)
except FileExistsError:
pass

sfdm = SessionFramesDataModule(
data_config,
eval_config["batch_size"],
eval_config["num_workers"],
eval_config["subsample_rate"],
eval_config["subsample_offset"],
eval_config["val_subsample_rate"],
eval_config["val_subsample_offset"]
)

model = SingleSessionModule.load_from_checkpoint(
checkpoint_path=saved_checkpoint_path
)

sfdm.setup("test")

eval_trialwise(model,sfdm.dataset,sfdm.mean_image,path)


import pdb; pdb.set_trace()

if __name__ == "__main__":
fire.Fire(main)

121 changes: 121 additions & 0 deletions scripts/train_single_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Train a single session autoencoder on provided data.

Saves checkpoints for the corresponding model, outputs to tensorboard, and finally dumps all predictions and latents into a save directory.

"""
import os
import fire
import json
import joblib
import datetime
import pytorch_lightning as pl
from behavioral_autoencoder.module import SingleSessionModule
from behavioral_autoencoder.dataset import CropResizeProportion
from behavioral_autoencoder.dataloading import SessionFramesDataModule
from behavioral_autoencoder.eval import get_all_predicts_latents,get_dl_predicts_latents
from pytorch_lightning.callbacks import ModelCheckpoint,LearningRateMonitor
from pytorch_lightning.loggers import TensorBoardLogger

here = os.path.join(os.path.abspath(os.path.dirname(__file__)))

def main(model_config_path, train_config_path, data_path, data_config_path):
"""This main function takes as input four paths. These paths indicate the model configuration parameters, training configuration parameters, path to the data directory, and cropping configuration, respectively. By default we assume that we are training a single session autoencoder.
"""
print("\n=== Starting Single Session Autoencoder Training ===")

## Model setup
print("\nLoading configurations...")
with open(model_config_path,"r") as f:
model_config = json.load(f)
with open(train_config_path,"r") as f:
train_config = json.load(f)
model_name = "single_session_autoencoder"

print(f"Model config: {model_config}")
print(f"Training config: {train_config}")

hparams = {
"model":model_name,
"model_config":model_config,
"train_config":train_config
}

print("\nInitializing model...")
ssm = SingleSessionModule(hparams)

## Data setup
with open(data_config_path,"r") as f:
data_process_config = json.load(f)
print("\nSetting up data...")
alm_cropping = CropResizeProportion(data_config_path)
data_config = {
"data_path":data_path,
"transform":alm_cropping,
"data_config_path":data_config_path,
"extension":data_process_config["extension"],
"trial_pattern":data_process_config["trial_pattern"]
}
print(f"Data config: {data_config}")

print("Initializing data module...")
sfdm = SessionFramesDataModule(
data_config,
train_config["batch_size"],
train_config["num_workers"],
train_config["subsample_rate"],
train_config["subsample_offset"],
train_config["val_subsample_rate"],
train_config["val_subsample_offset"]
)

import pdb; pdb.set_trace()
## Set up logging and trainer
print("\nSetting up logging and checkpoints...")
date=datetime.datetime.now().strftime("%m-%d-%y")
time=datetime.datetime.now().strftime("%H_%M_%S")
timestamp_model = os.path.join(here,"models",model_name,date,time)
timestamp_pred = os.path.join(here,"preds",model_name,date,time)
print(f"Model will be saved to: {timestamp_model}")
print(f"Predictions will be saved to: {timestamp_pred}")

logger = TensorBoardLogger("tb_logs",name="test_single_session_auto",log_graph=True)
checkpoint = ModelCheckpoint(monitor="mse/val", mode="min", save_last=True, dirpath=timestamp_model)
lr_monitor = LearningRateMonitor(logging_interval='epoch')

print("\nInitializing trainer...")
trainer = pl.Trainer(
fast_dev_run=train_config["fast_dev_run"],
max_epochs=train_config["max_epochs"],
accelerator=train_config["accelerator"],
enable_checkpointing=True,
callbacks=[checkpoint,lr_monitor],
log_every_n_steps=1,
logger=logger,
enable_progress_bar=True,
)

## Fit the model
print(f"\nStarting training for {train_config['max_epochs']} epochs...")
trainer.fit(ssm,sfdm)
print("Training completed!")

## Get out predictions
print("\nGenerating predictions and latents...")
preds,latents = get_dl_predicts_latents(ssm,sfdm.val_dataloader(),sfdm.mean_image,train_config["batch_size"],train_config["num_workers"])

## Save out all relevant metadata
print("\nSaving results...")
os.makedirs(timestamp_pred, exist_ok=True)
joblib.dump(preds,os.path.join(timestamp_pred,"preds"))
joblib.dump(latents,os.path.join(timestamp_pred,"latents"))
with open(os.path.join(timestamp_pred,"model_config"),"w") as f:
json.dump(hparams, f)
with open(os.path.join(timestamp_pred,"data_config"),"w") as f:
data_config["data_path"] = data_path
json.dump(data_config, f)

print("\n=== Training Complete ===")
print(f"Results saved to: {timestamp_pred}")

if __name__ == "__main__":
fire.Fire(main)
7 changes: 7 additions & 0 deletions src/behavioral_autoencoder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# README
This package is organized as follows:
- `networks.py` contains actual descriptions of network architectures which can be used as components of an autoencoder model.
- `module.py` contains the logic which specifies which combination of network architectures correspond to what kind of models.
- `metrics.py` contains custom build evaluation metrics.
- `dataloading.py` contains code for the dataloaders with preprocessing.
- `dataset.py` contains code to specify how datasets should be structured.
6 changes: 4 additions & 2 deletions src/behavioral_autoencoder/data_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def check_exists(frame_dir: Path) -> bool:
return response == "y"
return True

def main(video_dir,frame_dir,video_suffix=".avi"):
def main(video_dir,frame_dir,video_suffix=".avi",match_str=None):
"""Given a directory of videos, write to a different directory with the following structure:
1. one subdirectory per video file, as well as a metadata file `annotations.txt`.
2. within each subdirectory, pngs per individual frames.
Expand All @@ -76,12 +76,15 @@ def main(video_dir,frame_dir,video_suffix=".avi"):
video_dir: directory containing video files.
frame_dir: directory to write frames to.
video_suffix (default=".avi"): suffix of video files to consider.
match_str: string to find within the video names to write only a subset.
"""
video_dir = Path(video_dir)
frame_dir = Path(frame_dir)
# 1. Get a directory which contains video files. Store video file names.
video_files = os.listdir(video_dir)
video_files = [f for f in video_files if f.endswith(video_suffix)]
if match_str is not None:
video_files = [f for f in video_files if match_str in f]
# 2. Check that the directory we care about exists. If it doesn't create. If it does, ask user.
video_files_write = []
for video_file in video_files:
Expand All @@ -99,7 +102,6 @@ def main(video_dir,frame_dir,video_suffix=".avi"):
with open(frame_dir / "annotations.txt", "a") as f:
f.write(f"{video_dir_name} {first} {last} 0\n")

def npy_to_frames()

if __name__ == "__main__":
fire.Fire(main)
Loading