LabGym opsætning og brug på AI-LAB
Generel guide: Sådan kører du LabGym på AI-LAB
Denne guide viser, hvordan du sætter et generelt LabGym-projekt op på AI-LAB, så det kan køre headless (uden GUI) og som SLURM array jobs på GPU. Guiden er skrevet, så den ikke kun passer til ét bestemt dyr eller ét bestemt specialeprojekt.
Undervejs vil der stå DITBRUGERNAVN. Det betyder dit grimme AAU-brugernavn uden @student.aau.dk.
Hvad denne guide antager
Denne guide antager, at du allerede har:
- en trænet detector eksporteret fra LabGym
- en trænet categorizer eksporteret fra LabGym
- videoer, som enten allerede er i
.mp4, eller som du kan konvertere til.mp4 - har adgang til AI-LAB gennem AAU
- få adgang gennem denne hjemmeside og følg deres guide til opsætning
- basal viden fra AI-LAB guiden
- et ønske om at analysere mange videoer via array jobs
Hvis du mangler modeller, skal de først trænes i LabGym lokalt.
Vigtige placeholders du selv skal ændre
Du skal selv ændre disse steder i guiden og de forskellige scripts:
DITBRUGERNAVN- din grimme mail uden @student.aau.dklabgym_projecteller et andet projektnavn efter eget valg- stier til din detector
- stier til din categorizer
--animal-number--animal-kinds
Ressourcer på AI-LAB: vælg kun det du har brug for
Værdier som --mem, --cpus-per-task, --time, antal samtidige array-jobs og brug af GPU skal optimeres til det konkrete projekt.
Brug ikke flere ressourcer end du har behov for.
Det afhænger blandt andet af:
- videoernes længde
- videoernes opløsning
- antal dyr per video
- om du bruger behavior categorization og hvilken kompleksitet
- om du genererer annoterede videoer
- hvor tung din detector/categorizer er
Start med små testvideoer og justér derefter.
Praktisk tommelfingerregel
- korte testvideoer: start lavt
- lange videoer / høj opløsning / mange dyr: øg gradvist
- hvis jobbet fejler på memory: prøv mere RAM
- hvis GPU står ubrugt og jobbet er CPU-tungt: prøv flere CPU'er
- hvis jobbet allerede kører fint: lad være med at anmode om mere med mindre det gør analysen hurtigere
Eksempel: --mem=80G og --cpus-per-task=8 er kun et eksempel, ikke et universelt krav.
Opsætning
TRIN 1 — Log ind på AI-LAB
Åbn PowerShell (Windows) eller Terminal (macOS) og skriv:
ssh ailab-1
eller
ssh ailab-2
alt efter hvad der virkede under opsætning af AI-LAB
TRIN 2 — Opret mapper
Når du er logget ind:
mkdir -p ~/labgym_project/{code,data/videos,data/models,results,logs,tmp}
labgym_project kan ændres til et mere relevant navn for dit projekt. Men HUSK at ændre det i resten af guiden også!
Mappestrukturen i dit projekt bliver:
~/labgym_project/
├── code/ # scripts
├── data/
│ ├── videos/
│ └── models/ # detector og categorizer
├── results/ # output
└── logs/ # SLURM logs
└── tmp/ # midlertidige filer
Læg derefter:
- test videoer i
~/labgym_project/data/videos/- brug WinSCP som beskrevet af AI-LAB guiden
- start her med en kort video til test af opsætningen af LabGym før du tilføjer de videoer du vil analysere
- detector-mappen og categorizer-mappen i `~/labgym_project/data/models/
- model mapperne kan findes ved at aktivere LabGym i dit almindelige python miljø på din computer og skriv
pip show labgym. Placeringen er ofte noget lignende:c:\users\bruger\miniconda3\envs\labgym\lib\site-packages
- model mapperne kan findes ved at aktivere LabGym i dit almindelige python miljø på din computer og skriv
TRIN 3 — Opret virtual environment i Python-containeren
Kør:
srun --mem=8G --cpus-per-task=2 \
singularity exec /ceph/container/python/python_3.10.sif \
python -m venv --system-site-packages ~/labgym_project/venv
Dette laver et virtual python environment, som bruges sammen med AI-LABs Python-container.
TRIN 4 — Hent LabGym-koden
Gå til kode mappen:
cd ~/labgym_project/code
Hent LabGym source code fra GitHub:
git clone https://github.com/umyelab/LabGym.git
TRIN 5 — Fjern GUI-afhængigheden
LabGym er normalt lavet til GUI-brug. På AI-LAB vil vi køre headless (uden GUI), så wxPython skal fjernes fra dependencies.
Åben pyproject.toml i LabGym:
nano ~/labgym_project/code/LabGym/pyproject.toml
Find wxPython i dependencies og fjern den.
Gem (Ctrl+s) og luk (Ctrl+x)
TRIN 6 — Installér LabGym i venv inde i containeren
Start interaktivt job:
srun --mem=24G --cpus-per-task=8 --time=02:00:00 --pty bash
Kør derefter installationen:
singularity exec \
-B ~/labgym_project:/scratch/labgym_project \
/ceph/container/python/python_3.10.sif \
/bin/bash -c "
source /scratch/labgym_project/venv/bin/activate
pip uninstall -y labgym tensorflow numpy opencv-python opencv-contrib-python opencv-python-headless
pip install --no-cache-dir numpy==1.26.4
pip install --no-cache-dir tensorflow==2.15.1
pip install --no-cache-dir opencv-python-headless==4.10.0.84
cd /scratch/labgym_project/code/LabGym
pip install -e . --no-deps
"
Bemærk
Der VIL komme warnings og errors når vi installere, tester og bruger LabGym, da LabGym ikke er lavet til at blive brugt på denne måde.
TRIN 7 — Test at installationen virker
Kør:
singularity exec --nv \
-B ~/labgym_project:/scratch/labgym_project \
/ceph/container/python/python_3.10.sif \
/bin/bash -c "
source /scratch/labgym_project/venv/bin/activate
python - <<'PY'
import numpy
print('numpy:', numpy.__version__)
import cv2
print('cv2 works')
import torch
print('torch:', torch.__version__)
print('torch cuda available:', torch.cuda.is_available())
from LabGym.analyzebehavior_dt import AnalyzeAnimalDetector
print('OK - LabGym detector API works')
PY
"
Hvis der til sidst står:
OK - LabGym detector API works
så virker installationen selv om der kommer mangle advarsler og fejl.
Når du er færdig med det interaktive job, kan du lukke det med:
scancel JOBID
Job-id kan findes med denne kode hvis du ikke kan huske den fra da du startede det interaktive job:
squeue --me
TRIN 8 — Opret Python-wrapper-script
Formålet med wrapper-scriptet er at gøre LabGym nemmere at kalde fra SLURM scripts og samtidig gøre det muligt at styre parametre fra kommandolinjen.
Lav python filen run_labgym_detector.py ved brug af koden:
nano ~/labgym_project/code/run_labgym_detector.py
Indsæt dette i filen:
import argparse
import ast
import csv
from pathlib import Path
from LabGym.analyzebehavior_dt import AnalyzeAnimalDetector
def parse_animal_number(raw_value, animal_kinds):
raw_value = str(raw_value).strip()
if raw_value.isdigit():
return int(raw_value)
parsed = ast.literal_eval(raw_value)
if isinstance(parsed, int):
return parsed
if isinstance(parsed, dict):
return {str(k): int(v) for k, v in parsed.items()}
if isinstance(parsed, (list, tuple)):
if len(parsed) != len(animal_kinds):
raise ValueError(
f"--animal-number as a list must have the same length as --animal-kinds. "
f"Got {len(parsed)} values but {len(animal_kinds)} classes."
)
return {animal_kinds[i]: int(parsed[i]) for i in range(len(animal_kinds))}
raise ValueError("--animal-number must be an integer, dict, or list.")
def read_categorizer_table(categorizer_dir):
mp = Path(categorizer_dir) / "model_parameters.txt"
if not mp.exists():
raise FileNotFoundError(f"Could not find model_parameters.txt in {categorizer_dir}")
with mp.open("r", encoding="utf-8", errors="ignore", newline="") as f:
reader = csv.DictReader(f)
rows = list(reader)
if not rows:
raise ValueError("model_parameters.txt exists but contains no rows.")
return rows
def build_names_and_colors(behavior_names):
default_pairs = [
["#ffffff", "#ff00ff"],
["#ffffff", "#00ffff"],
["#ffffff", "#00ff00"],
["#ffffff", "#ff9900"],
["#ffffff", "#ff0000"],
["#ffffff", "#0000ff"],
["#ffffff", "#9900ff"],
["#ffffff", "#999999"],
]
names_and_colors = {}
for i, name in enumerate(behavior_names):
names_and_colors[name] = default_pairs[i % len(default_pairs)]
return names_and_colors
def build_id_colors(animal_number):
default_colors = [
(255, 255, 255),
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(255, 255, 0),
(255, 0, 255),
(0, 255, 255),
(255, 128, 0),
(128, 0, 255),
(128, 128, 128),
]
if isinstance(animal_number, int):
total = animal_number
elif isinstance(animal_number, dict):
total = sum(animal_number.values())
else:
raise ValueError("animal_number must be int or dict")
return [default_colors[i % len(default_colors)] for i in range(total)]
def first_int(rows, key, default):
value = rows[0].get(key, default)
try:
return int(value)
except Exception:
return default
def main():
parser = argparse.ArgumentParser()
# Krævede argumenter
parser.add_argument("--video", required=True)
parser.add_argument("--detector", required=True)
parser.add_argument("--categorizer", required=True)
parser.add_argument("--results", required=True)
parser.add_argument("--animal-number", required=True)
parser.add_argument("--animal-kinds", nargs="+", required=True)
# Analyseindstillinger
parser.add_argument("--behavior-mode", type=int, default=None)
parser.add_argument("--framewidth", type=int, default=0)
parser.add_argument("--dim-tconv", type=int, default=None)
parser.add_argument("--dim-conv", type=int, default=None)
parser.add_argument("--channel", type=int, default=None)
parser.add_argument("--include-bodyparts", action="store_true")
parser.add_argument("--std", type=int, default=None)
parser.add_argument("--start-time", type=float, default=0)
parser.add_argument("--duration", type=float, default=0)
parser.add_argument("--length", type=int, default=None)
parser.add_argument("--social-distance", type=int, default=None)
# Performance / inputbehandling
parser.add_argument("--batch-size", type=int, default=1)
parser.add_argument("--background-free", action="store_true")
# Behavior categorization
parser.add_argument("--uncertain", type=int, default=0)
parser.add_argument("--min-behavior-length", type=int, default=None)
# Annoteret video
parser.add_argument("--skip-annotated-video", action="store_true")
parser.add_argument("--hide-legend", action="store_true")
# Resultateksport
parser.add_argument("--no-normalize-distance", action="store_true")
parser.add_argument(
"--parameters",
nargs="+",
default=["count", "duration"],
help="Fx count duration latency speed distance intensity area ..."
)
args = parser.parse_args()
uncertain_value = args.uncertain / 100.0
results = Path(args.results)
results.mkdir(parents=True, exist_ok=True)
animal_number = parse_animal_number(args.animal_number, args.animal_kinds)
rows = read_categorizer_table(args.categorizer)
behavior_names = [row["classnames"] for row in rows if row.get("classnames")]
if not behavior_names:
raise ValueError("Could not extract behavior names from the 'classnames' column.")
dim_tconv = args.dim_tconv if args.dim_tconv is not None else first_int(rows, "dim_tconv", 8)
dim_conv = args.dim_conv if args.dim_conv is not None else first_int(rows, "dim_conv", 8)
channel = args.channel if args.channel is not None else first_int(rows, "channel", 1)
length = args.length if args.length is not None else first_int(rows, "time_step", 15)
std = args.std if args.std is not None else first_int(rows, "std", 0)
behavior_mode = args.behavior_mode if args.behavior_mode is not None else first_int(rows, "behavior_kind", 0)
social_distance = args.social_distance if args.social_distance is not None else first_int(rows, "social_distance", 0)
network = first_int(rows, "network", 1)
animation_analyzer = network != 0
names_and_colors = build_names_and_colors(behavior_names)
id_colors = build_id_colors(animal_number)
print("Parsed animal_number:", animal_number)
print("Animal kinds:", args.animal_kinds)
print("Behavior names:", behavior_names)
print("dim_tconv:", dim_tconv)
print("dim_conv:", dim_conv)
print("channel:", channel)
print("length:", length)
print("behavior_mode:", behavior_mode)
print("social_distance:", social_distance)
print("ID colors:", id_colors)
aad = AnalyzeAnimalDetector()
aad.prepare_analysis(
args.detector,
args.video,
args.results,
animal_number,
args.animal_kinds,
behavior_mode,
names_and_colors=names_and_colors,
framewidth=None if args.framewidth == 0 else args.framewidth,
dim_tconv=dim_tconv,
dim_conv=dim_conv,
channel=channel,
include_bodyparts=args.include_bodyparts,
std=std,
categorize_behavior=True,
animation_analyzer=animation_analyzer,
t=args.start_time,
duration=args.duration,
length=length,
social_distance=social_distance,
)
aad.acquire_information(
batch_size=args.batch_size,
background_free=args.background_free
)
if behavior_mode != 1:
aad.craft_data()
aad.categorize_behaviors(
args.categorizer,
uncertain=args.uncertain,
min_length=args.min_behavior_length
)
if not args.skip_annotated_video:
aad.annotate_video(
ID_colors=id_colors,
animal_to_include=args.animal_kinds,
behavior_to_include=behavior_names,
show_legend=not args.hide_legend
)
aad.export_results(
normalize_distance=not args.no_normalize_distance,
parameter_to_analyze=args.parameters
)
print("Done.")
if __name__ == "__main__":
main()
Gem (Ctrl+s) og luk (Ctrl+x)
TRIN 9 — Lav array job script
Videoerne til test analyse skal nu ligge inde i mappen til videoer. Til at starte med kan man have et par meget korte test videoer.
Lav en array script:
nano ~/labgym_project/code/run_labgym_array.sh
Indsæt:
#!/bin/bash
#SBATCH --job-name=labgym_project
#SBATCH --output=/ceph/home/student.aau.dk/DITBRUGERNAVN/labgym_project/logs/labgym_%A_%a.out
#SBATCH --error=/ceph/home/student.aau.dk/DITBRUGERNAVN/labgym_project/logs/labgym_%A_%a.err
# Justér disse ressourcer til dit projekt
#SBATCH --mem=80G
#SBATCH --cpus-per-task=8
#SBATCH --gres=gpu:1
#SBATCH --time=08:00:00
set -euo pipefail
BASE=/ceph/home/student.aau.dk/DITBRUGERNAVN/labgym_project
VIDEO_LIST=${BASE}/code/video_list.txt
FILE=$(sed -n "$((SLURM_ARRAY_TASK_ID + 1))p" "${VIDEO_LIST}")
if [ -z "${FILE}" ]; then
echo "No video found for task ${SLURM_ARRAY_TASK_ID}"
exit 1
fi
BASENAME=$(basename "${FILE}")
BASENAME_NOEXT="${BASENAME%.*}"
RESULTS_DIR=/scratch/labgym_project/results/${BASENAME_NOEXT}
singularity exec --nv \
-B ${BASE}:/scratch/labgym_project \
/ceph/container/python/python_3.10.sif \
/bin/bash -c "
source /scratch/labgym_project/venv/bin/activate
export TMPDIR=/scratch/labgym_project/tmp
export TEMP=/scratch/labgym_project/tmp
export TMP=/scratch/labgym_project/tmp
mkdir -p /scratch/labgym_project/tmp
mkdir -p '${RESULTS_DIR}'
python /scratch/labgym_project/code/run_labgym_detector.py \
--video '${FILE}' \
--detector /scratch/labgym_project/data/models/DETector_MAPPE \
--categorizer /scratch/labgym_project/data/models/CATEGORIZER_MAPPE \
--results '${RESULTS_DIR}' \
--animal-number '{\"Animal\": 1}' \
--animal-kinds Animal \
--batch-size 1 \
--duration 0 \
--parameters count duration
# ============================
# Mulige ekstra indstillinger
# Fjern # foran dem du vil bruge
# ============================
# --behavior-mode 0 \
# --framewidth 0 \
# --dim-tconv 8 \
# --dim-conv 8 \
# --channel 1 \
# --include-bodyparts \
# --std 0 \
# --start-time 0 \
# --duration 60 \
# --length 15 \
# --social-distance 0 \
# --batch-size 2 \
# --background-free \
# --uncertain 20 \
# --min-behavior-length 10 \
# --hide-legend \
# --no-normalize-distance \
# --skip-annotated-video \
"
Ting du altid skal ændre for at passe til dit projekt
DITBRUGERNAVNDETector_MAPPECATEGORIZER_MAPPE--animal-number--animal-kinds--parameters
Eksempler på --animal-number
Én klasse:
--animal-number '{"Animal": 3}' \
--animal-kinds Animal
Flere klasser:
--animal-number '{"Male": 1, "Female": 2}' \
--animal-kinds Male Female
Hvis du bruger flere klasser, skal navnene i --animal-number og --animal-kinds passe sammen.
Forklaring af LabGym-argumenterne i array-scriptet
Krævede argumenter
-
--video
sti til videoen der skal analyseres -
--detector
sti til detector-mappen -
--categorizer
sti til categorizer-mappen -
--results
outputmappe for den aktuelle video -
--animal-number
antal individer per klasse eller totalt antal dyr -
--animal-kinds
navnene på de klasser, som modellen skal finde
Analyseindstillinger
Dette er de indstillinger der skal passe med den trænet model
-
--behavior-mode
LabGyms behavior mode. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--framewidth
sæt en bestemt framebredde.0betyder: brug default / lad LabGym håndtere det. dette er det som LabGym kalder resizing -
--dim-tconv
temporal convolution dimension. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--dim-conv
convolution dimension. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--channel
antal kanaler, typisk 1 (gråskala) eller 3 (farver) afhængigt af model og input. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--include-bodyparts
inkluder bodyparts hvis modellen/workflowet bruger det. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--std
std-parameter fra modelopsætningen. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--start-time
starttid i sekunder inde i videoen -
--duration
hvor mange sekunder der skal analyseres.0betyder typisk hele videoen -
--length
længde / time steps brugt i behavior analyzeren. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt. -
--social-distance
social distance parameter for sociale setups. Hvis du ikke angiver den, forsøger wrapperen at læse den framodel_parameters.txt.
Performance / inputbehandling
-
--batch-size
hvor mange frames/dataenheder der behandles ad gangen. Højere værdi kan være hurtigere, men bruger typisk mere memory. Dette skal optimeres til det enkelte projekt -
--background-free
bruges hvis dit workflow kræver background-free analyse
Behavior categorization
-
--uncertain
tærskel for usikkerhed i behavior categorization -
--min-behavior-length
minimumslængde for adfærdsepisoder. Hvis længden af en adfærd ikke opfylder kravet bliver det sat til NA
Annoteret video
-
--skip-annotated-video
spring generering af annoteret video over. God instilling hvis man kun er interesseret i excel output -
--hide-legend
skjul legend i annoteret video
Resultateksport
-
--no-normalize-distance
slå distance-normalisering fra -
--parameters
hvilke outputparametre der eksporteres. Standard i wrapperen er:--parameters count duration
Hvis du vil eksportere andre eller flere mål, kan du skrive fx:
--parameters count duration latency speed distance
Hvilke parametre der giver mening, afhænger af dit LabGym-setup og hvad
export_resultsforventer i den version af LabGym, du bruger.
TRIN 10 — Opret submit script
Lav filen:
nano ~/labgym_project/code/submit_array.sh
Indsæt:
#!/bin/bash
PROJECT_DIR=$HOME/labgym_project
INPUT_DIR=/ceph/home/student.aau.dk/DITBRUGERNAVN/video_processing/data_out
FILE_LIST=$PROJECT_DIR/code/video_list.txt
JOB_SCRIPT=$PROJECT_DIR/code/run_labgym_array.sh
mkdir -p "$PROJECT_DIR/logs"
mkdir -p "$PROJECT_DIR/results"
mkdir -p "$PROJECT_DIR/tmp"
find "$INPUT_DIR" -maxdepth 1 -type f -name "*.mp4" | sort > "$FILE_LIST"
NUM_FILES=$(wc -l < "$FILE_LIST")
if [ "$NUM_FILES" -eq 0 ]; then
echo "No MP4 files found in $INPUT_DIR"
exit 1
fi
MAX_INDEX=$((NUM_FILES - 1))
echo "Found $NUM_FILES videos"
echo "Submitting jobs"
# %8 betyder maks 8 samtidige array tasks
sbatch --array=0-"$MAX_INDEX"%8 "$JOB_SCRIPT"
Vigtigt
- ændr
DITBRUGERNAVN - justér
%8til det antal samtidige jobs, der passer til dit projekt og til den belastning, du vil lægge på systemet. Du kan max have 8 job på samme tid i AI-LAB
TRIN 11 — Gør scripts kørbare
chmod +x ~/labgym_project/code/run_labgym_array.sh
chmod +x ~/labgym_project/code/submit_array.sh
Brug LabGym
Sådan kører man array-jobbet
Sådan sender du array-jobbet
sbatch ~/labgym_project/code/submit_array.sh
Sådan tjekker du køen
squeue --me
Hvor finder du output?
Hver video får sin egen resultatmappe:
~/labgym_project/results/video_navn/
hver array-task får sin egen log:
~/labgym_project/logs/labgym_JOBID_TASKID.out
~/labgym_project/logs/labgym_JOBID_TASKID.err
Få videoer over i .mp4 med ffmpeg
Hvis dine videoer ikke allerede er i .mp4, kan du konvertere dem med ffmpeg.
Simpel konvertering til MP4 i python terminal
Hvis du bare vil lave en ny MP4-fil:
ffmpeg -i "input.avi" "output.mp4"
Erstat input.avi med stien til den video du gerne vil ændre til mp4.
Erstat output.mp4 med stien og det nye navn til videoen
For eksempel:
ffmpeg -i "E:\1_01_H_20251226000000.avi" "E:\1_01_H_20251226000000.mp4"
ffmpeg har mange indstillinger og jeg vil anbefale at spørge ChatGPT (eller anden chatbot) om yderligere anbefalinger og koder til konvertering af videoer til dit projekt
Fejlsøgning
Job fejler med memory error
- sænk
--batch-size - prøv mere
--mem - slå annoteret video fra
Job kører meget langsomt
- prøv lidt flere CPU'er
- undgå unødvendig annoteret video
Ingen outputfiler
- tjek
.outog.errilogs/ - tjek at detector- og categorizer-stierne er rigtige
- tjek at
video_list.txtfaktisk indeholder filer - tjek at input er
.mp4