diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..04085986d01c009bd103e053ffe6fe974dc29504 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +__pycache__/ +*.py[cod] +.pytest_cache/ +.venv/ +data/videos/ +data/processed/ +artifacts/models/ +artifacts/experiments/*/ +!artifacts/experiments/.gitkeep +*.mp4 + diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9147b110f69800792dfa66bfab047d248701d171 --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +--- +license: cc-by-nc-sa-4.0 +language: +- vi +library_name: pytorch +pipeline_tag: video-classification +tags: +- computer-vision +- fall-detection +- pose-estimation +- mediapipe +- temporal-convolution +--- + +# Phát hiện té ngã từ video dựa trên tư thế cơ thể + +Project code hoàn chỉnh theo pipeline: + +`Video RGB → MediaPipe (33 điểm khớp) → chuẩn hóa tư thế → cửa sổ 4 giây → Pose-TCN/Pose-GRU → làm trơn → cảnh báo` + +Project đã chạy trên bộ UR Fall Detection (URFD). Kết quả cố định với seed 42 trên tập test gồm 41 cửa sổ thuộc 13 video: + +| Mô hình | Accuracy | Precision | Recall | Specificity | F1 | ROC-AUC | +|---|---:|---:|---:|---:|---:|---:| +| Random Forest | 97,56% | 100,00% | 87,50% | 100,00% | 93,33% | 1,000 | +| Logistic Regression | 95,12% | 80,00% | 100,00% | 93,94% | 88,89% | 1,000 | +| Pose-TCN (CUDA) | 95,12% | 80,00% | 100,00% | 93,94% | 88,89% | 1,000 | +| Pose-GRU (CUDA) | 90,24% | 70,00% | 87,50% | 90,91% | 77,78% | 0,989 | + +Đây là kết quả theo **cửa sổ video**, không phải theo người. URFD không cung cấp ánh xạ danh tính đủ rõ để chia subject-independent, vì vậy không dùng bảng trên để tuyên bố khả năng tổng quát hóa lâm sàng. + +## Chạy nhanh + +Project dùng Python 3.10–3.12. Tạo môi trường và cài thư viện: + +```bash +python -m venv .venv +source .venv/bin/activate +pip install -e '.[dev]' +``` + +Để chạy Pose-GRU trên GPU NVIDIA, cài wheel PyTorch phù hợp trước. Máy đã dùng cho kết quả trong project chạy CUDA 12.8: + +```bash +pip install torch --index-url https://download.pytorch.org/whl/cu128 +python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))" +``` + +Chạy lại toàn bộ thí nghiệm URFD. Lệnh sẽ tự tải khoảng 70 video từ nguồn chính thức nếu chưa có dữ liệu: + +```bash +python scripts/run_all.py +``` + +Chạy demo trên video: + +```bash +python scripts/demo_video.py \ + --input data/videos/fall/fall-01/fall-01-cam0.mp4 \ + --model artifacts/experiments/urfd/pose_tcn/model.pt \ + --output artifacts/demo_fall01.mp4 +``` + +## Chạy từng bước + +```bash +# 1. Tải 30 video té và 40 video sinh hoạt thường ngày +python scripts/download_urfd.py + +# 2. Trích keypoint và tạo dataset .npz +python scripts/prepare_dataset.py + +# 3. Huấn luyện hai baseline +python scripts/train_baselines.py + +# 4. Huấn luyện hai mô hình sâu +python scripts/train_gru.py +python scripts/train_tcn.py + +# 5. Tổng hợp bảng và biểu đồ +python scripts/compare_models.py + +# 6. Kiểm thử mã +pytest -q +``` + +Kết quả nằm tại `artifacts/experiments/urfd/`. Mỗi mô hình có trọng số, `metrics.json`, dự đoán từng mẫu, confusion matrix và ROC. File `results.csv` và `model_comparison.png` tổng hợp một lần chạy. + +## Dữ liệu riêng + +Sắp xếp mỗi video trong một thư mục nhóm riêng. Nhóm có thể là mã người hoặc mã video; nếu có mã người, nên dùng mã người để phép chia không rò rỉ danh tính. + +```text +data/videos/ +├── fall/ +│ ├── person01/video01.mp4 +│ └── person02/video02.mp4 +└── normal/ + ├── person01/video03.mp4 + └── person02/video04.mp4 +``` + +Với dữ liệu không phải URFD và không có annotation frame, mọi cửa sổ trong `fall/` được gán nhãn té. Nên cắt video để mỗi clip té thực sự chứa sự kiện té. Sửa tham số trong [configs/default.yaml](configs/default.yaml). + +## Thiết kế thí nghiệm + +- Mỗi cửa sổ dài 40 frame ở 10 FPS, tương đương 4 giây; stride 10 frame. +- Pose được tịnh tiến về tâm hông và chia theo chiều dài thân/độ rộng vai. +- Pose-GRU dùng 140 chiều/frame: 33 × `(x, y, z, visibility)` và 8 đặc trưng hình học/chuyển động. +- Pose-TCN dùng 60 chiều/frame: 13 khớp cốt lõi × 4 kênh và 8 đặc trưng bổ sung, giúp giảm overfit trên URFD nhỏ. +- Chia train/validation/test theo group với tỷ lệ 60/20/20; các cửa sổ cùng video luôn ở cùng tập. +- Ngưỡng xác suất tối ưu F1 chỉ được chọn trên validation. Test không tham gia chọn mô hình/ngưỡng. +- GRU và TCN dùng BCE có trọng số lớp, AdamW, early stopping; hai baseline dùng cùng dữ liệu và phép chia. + +## Chạy lặp nhiều seed + +Lệnh sau chạy cả bốn mô hình với 5 seed, mỗi seed có phép chia theo video riêng. Pose-GRU và Pose-TCN tự dùng CUDA nếu có; hai mô hình scikit-learn chạy CPU. + +```bash +python scripts/run_repeated_experiments.py --seeds 13 21 42 84 123 --device cuda +``` + +Kết quả từng lần nằm trong `artifacts/experiments/urfd_repeated/seed_*/`. Bảng tổng hợp mean, standard deviation, min và max nằm tại `aggregate_results.csv`. + +Kết quả trên 5 seed đã chạy: + +| Mô hình | Accuracy mean ± std | Recall mean ± std | F1 mean ± std | ROC-AUC mean ± std | +|---|---:|---:|---:|---:| +| Random Forest | 0,968 ± 0,026 | 0,885 ± 0,114 | **0,907 ± 0,061** | 0,997 ± 0,007 | +| Logistic Regression | **0,969 ± 0,021** | **0,955 ± 0,062** | 0,904 ± 0,095 | 0,991 ± 0,015 | +| Pose-TCN (CUDA) | 0,961 ± 0,040 | 0,915 ± 0,132 | 0,901 ± 0,069 | 0,983 ± 0,035 | +| Pose-GRU (CUDA) | 0,942 ± 0,043 | 0,865 ± 0,139 | 0,842 ± 0,098 | 0,996 ± 0,006 | + +URFD có 70 chuỗi (30 fall, 40 ADL), phát hành theo CC BY-NC-SA 4.0 cho nghiên cứu phi thương mại tại [trang dữ liệu chính thức](https://fenix.ur.edu.pl/~mkepski/ds/uf.html). Khi sử dụng, trích dẫn bài của Kwolek và Kępski, DOI `10.1016/j.cmpb.2014.09.005`. + +Repository công khai chứa code, checkpoint và kết quả thí nghiệm. Video URFD gốc, pose dataset đã trích và video demo không được đóng gói cùng repository; dùng `scripts/download_urfd.py` để tải dữ liệu từ nguồn chính thức. diff --git a/artifacts/experiments/.gitkeep b/artifacts/experiments/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/artifacts/experiments/.gitkeep @@ -0,0 +1 @@ + diff --git a/artifacts/experiments/urfd/logistic_regression/confusion_matrix.png b/artifacts/experiments/urfd/logistic_regression/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..c4352a4a42b47c080ff2ecd715f07278d7f778b0 Binary files /dev/null and b/artifacts/experiments/urfd/logistic_regression/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd/logistic_regression/metrics.json b/artifacts/experiments/urfd/logistic_regression/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..2583d8ec031a2efabfdad6f6c7a76a8ac2144af0 --- /dev/null +++ b/artifacts/experiments/urfd/logistic_regression/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9512195121951219, + "precision": 0.8, + "recall": 1.0, + "specificity": 0.9393939393939394, + "f1": 0.8888888888888888, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 31, + "fp": 2, + "fn": 0, + "tp": 8, + "n_samples": 41, + "model": "logistic_regression", + "seed": 42, + "training_seconds": 0.01953271900129039, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd/logistic_regression/model.joblib b/artifacts/experiments/urfd/logistic_regression/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..5c02f5815f480167a25797f7a7c6f71114207741 --- /dev/null +++ b/artifacts/experiments/urfd/logistic_regression/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdf1c4ffd6d38bb695db15df2e5829ae12f7b3121c8a288f0e56720ec2526806 +size 21047 diff --git a/artifacts/experiments/urfd/logistic_regression/predictions.csv b/artifacts/experiments/urfd/logistic_regression/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..bb96ffe1fb35d382794a970c3f5beb0748ebc1a0 --- /dev/null +++ b/artifacts/experiments/urfd/logistic_regression/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.98377717,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9993255,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.999498,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.97410244,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.99896145,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.997311,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.9457497,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.99611413,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.0006896582,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.0023842053,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,2.3963743e-05,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,8.265512e-05,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.018153476,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.02516493,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.00035743922,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,6.653513e-07,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.00603225,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.0031787464,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.03760421,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.012593662,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.000474621,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.0028994733,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,2.2414151e-06,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.00026380236,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.51179487,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.7919934,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.0336354,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,1.8855539e-05,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.0051527205,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.042879105,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.009401498,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.00028797347,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,5.9999e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,2.6717935e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,4.8368015e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.0002776527,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.0004259999,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.0019540016,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.0005490907,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.00027555088,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.017206663,0 diff --git a/artifacts/experiments/urfd/logistic_regression/roc_curve.png b/artifacts/experiments/urfd/logistic_regression/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..792c6ceaccbde5336f2893edaf598adffc8b9f6d Binary files /dev/null and b/artifacts/experiments/urfd/logistic_regression/roc_curve.png differ diff --git a/artifacts/experiments/urfd/model_comparison.png b/artifacts/experiments/urfd/model_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..c6154356f534fd47adb37628221c74c5c5d17411 Binary files /dev/null and b/artifacts/experiments/urfd/model_comparison.png differ diff --git a/artifacts/experiments/urfd/pose_gru/confusion_matrix.png b/artifacts/experiments/urfd/pose_gru/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..63b36bf597bfd9d79957f5edc37f23d0b8d20bf8 Binary files /dev/null and b/artifacts/experiments/urfd/pose_gru/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd/pose_gru/metrics.json b/artifacts/experiments/urfd/pose_gru/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..0a9cc7c74a33f56f50130869f630e65770c67d5f --- /dev/null +++ b/artifacts/experiments/urfd/pose_gru/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9024390243902439, + "precision": 0.7, + "recall": 0.875, + "specificity": 0.9090909090909091, + "f1": 0.7777777777777778, + "roc_auc": 0.9886363636363636, + "threshold": 0.5, + "tn": 30, + "fp": 3, + "fn": 1, + "tp": 7, + "n_samples": 41, + "model": "pose_gru", + "architecture": "gru", + "seed": 42, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 30, + "best_val_loss": 0.14927974343299866, + "training_seconds": 1.3942278900067322, + "parameters": 79489, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd/pose_gru/model.pt b/artifacts/experiments/urfd/pose_gru/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..cb9cd03b4a33cea57bb4b9c6807178318d5cbbb3 --- /dev/null +++ b/artifacts/experiments/urfd/pose_gru/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:daf7d083966a9cf8ade21b4bec28123199336639c33ea561c140ae31cdc8d1bf +size 325083 diff --git a/artifacts/experiments/urfd/pose_gru/predictions.csv b/artifacts/experiments/urfd/pose_gru/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..164853bb6037dd08c32b26cc5f39708dae5206ac --- /dev/null +++ b/artifacts/experiments/urfd/pose_gru/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.36374763,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9875322,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.9928555,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.9939282,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.9983101,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.9972844,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.99895227,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.99400866,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.0007733168,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.0023768344,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.0008485273,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.0010037357,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.00556503,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.04351345,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.08348232,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,0.00067634834,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.0010444914,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.0011891089,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.0016517256,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.0052665947,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.013075952,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.032113984,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,0.002390514,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.0011807706,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.013312802,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.6862216,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.5090663,1 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,0.001116697,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.0019210341,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.015289847,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.058644745,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.0013770493,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.00230475,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.0012230183,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.0026165335,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.0022358233,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.0015868503,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.0012599203,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.0034755394,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.010827422,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.9500953,1 diff --git a/artifacts/experiments/urfd/pose_gru/roc_curve.png b/artifacts/experiments/urfd/pose_gru/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..22c481f3822952a18e4ff4b9081c9c9e7d515333 Binary files /dev/null and b/artifacts/experiments/urfd/pose_gru/roc_curve.png differ diff --git a/artifacts/experiments/urfd/pose_gru/training_curve.png b/artifacts/experiments/urfd/pose_gru/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..e998332d8cb491699f1aaa77fe74b1fdecbb74a0 Binary files /dev/null and b/artifacts/experiments/urfd/pose_gru/training_curve.png differ diff --git a/artifacts/experiments/urfd/pose_tcn/confusion_matrix.png b/artifacts/experiments/urfd/pose_tcn/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..6667c0747c32c5d4699cacfa1196e014b6d8ced0 Binary files /dev/null and b/artifacts/experiments/urfd/pose_tcn/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd/pose_tcn/metrics.json b/artifacts/experiments/urfd/pose_tcn/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..f0b723d9496a8962370234901e560b4e37c5da6e --- /dev/null +++ b/artifacts/experiments/urfd/pose_tcn/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9512195121951219, + "precision": 0.8, + "recall": 1.0, + "specificity": 0.9393939393939394, + "f1": 0.8888888888888888, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 31, + "fp": 2, + "fn": 0, + "tp": 8, + "n_samples": 41, + "model": "pose_tcn", + "architecture": "tcn", + "seed": 42, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 27, + "best_val_loss": 0.16090895235538483, + "training_seconds": 1.111777505997452, + "parameters": 12929, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd/pose_tcn/model.pt b/artifacts/experiments/urfd/pose_tcn/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..81359934f9ba89c4a7a384fcbb1c430a447e8a19 --- /dev/null +++ b/artifacts/experiments/urfd/pose_tcn/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9f02d417c3c785920bbb592f0af63fd9baa96c99bcedcc49b27c01106f98165 +size 58759 diff --git a/artifacts/experiments/urfd/pose_tcn/predictions.csv b/artifacts/experiments/urfd/pose_tcn/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..405b3817fb1d723e55856021b7b911693ca69415 --- /dev/null +++ b/artifacts/experiments/urfd/pose_tcn/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.97618514,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9786139,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.9824744,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.9479548,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.9917155,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.91648644,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.9619871,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.9757481,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.036800418,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.12765183,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.053033993,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.1157073,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.25427914,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.30693465,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.25294647,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,0.041943014,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.35644826,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.2825069,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.20812033,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.22714849,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.111267,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.13848732,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,0.054857913,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.045970306,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.5363943,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.66083694,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.24986179,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,0.025344677,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.15354195,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.12768711,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.13882609,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.05435837,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.021094386,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.023997432,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.20265935,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.22501923,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.111354865,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.06751009,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.08183312,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.100184046,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.4885858,0 diff --git a/artifacts/experiments/urfd/pose_tcn/roc_curve.png b/artifacts/experiments/urfd/pose_tcn/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..8044851a896728736769c74bdacb566a37f515ca Binary files /dev/null and b/artifacts/experiments/urfd/pose_tcn/roc_curve.png differ diff --git a/artifacts/experiments/urfd/pose_tcn/training_curve.png b/artifacts/experiments/urfd/pose_tcn/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..9150c25249fbfa5dace8379e4559ae2b01d746b8 Binary files /dev/null and b/artifacts/experiments/urfd/pose_tcn/training_curve.png differ diff --git a/artifacts/experiments/urfd/random_forest/confusion_matrix.png b/artifacts/experiments/urfd/random_forest/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..4e98ee118b55098045a5780f5b906718bd2f77b9 Binary files /dev/null and b/artifacts/experiments/urfd/random_forest/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd/random_forest/metrics.json b/artifacts/experiments/urfd/random_forest/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..47ebb2c17144ec494df352b9e57f2563256c3881 --- /dev/null +++ b/artifacts/experiments/urfd/random_forest/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.975609756097561, + "precision": 1.0, + "recall": 0.875, + "specificity": 1.0, + "f1": 0.9333333333333333, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 33, + "fp": 0, + "fn": 1, + "tp": 7, + "n_samples": 41, + "model": "random_forest", + "seed": 42, + "training_seconds": 1.2764060369954677, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd/random_forest/model.joblib b/artifacts/experiments/urfd/random_forest/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..531b827861c25a64968aa30f7e6c80d40707afa3 --- /dev/null +++ b/artifacts/experiments/urfd/random_forest/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49aac9a9a79a45fc5d59d3dd929e6fa866f16a88777834af04d8f92dcb098b6a +size 357699 diff --git a/artifacts/experiments/urfd/random_forest/predictions.csv b/artifacts/experiments/urfd/random_forest/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..5d8c3478ae455f009b0b663f4fdd6018d95fafe2 --- /dev/null +++ b/artifacts/experiments/urfd/random_forest/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.454547619047619,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.5490158730158728,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.5656825396825395,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.7903333333333331,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.7514682539682539,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.5684722222222222,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.7104444444444443,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.5993571428571427,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.021190476190476194,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.0775,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.022793650793650793,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.054722222222222214,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.12342857142857142,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.14366666666666664,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.10826190476190475,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,0.005555555555555555,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.13499999999999998,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.2251984126984127,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.32544444444444454,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.42709523809523814,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.17731746031746032,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.20001190476190472,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,0.05765873015873017,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.06333333333333334,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.13238888888888886,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.19470634920634922,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.2077857142857143,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,0.047293650793650784,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.15166666666666664,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.2036111111111111,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.2175,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.1753174603174603,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.046,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.014166666666666666,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.025611111111111112,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.020277777777777777,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.05416666666666667,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.10492857142857144,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.12884126984126984,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.20839682539682539,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.3192460317460318,0 diff --git a/artifacts/experiments/urfd/random_forest/roc_curve.png b/artifacts/experiments/urfd/random_forest/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfdc06161530ccc47edb187ded23f143f84115e Binary files /dev/null and b/artifacts/experiments/urfd/random_forest/roc_curve.png differ diff --git a/artifacts/experiments/urfd/results.csv b/artifacts/experiments/urfd/results.csv new file mode 100644 index 0000000000000000000000000000000000000000..18af8910d225c81fa4b9c6fba5f0ce38192ebb89 --- /dev/null +++ b/artifacts/experiments/urfd/results.csv @@ -0,0 +1,5 @@ +model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds +random_forest,0.975609756097561,1.0,0.875,1.0,0.9333333333333333,1.0,1.2764060369954677 +logistic_regression,0.9512195121951219,0.8,1.0,0.9393939393939394,0.8888888888888888,1.0,0.01953271900129039 +pose_tcn,0.9512195121951219,0.8,1.0,0.9393939393939394,0.8888888888888888,1.0,1.111777505997452 +pose_gru,0.9024390243902439,0.7,0.875,0.9090909090909091,0.7777777777777778,0.9886363636363636,1.3942278900067322 diff --git a/artifacts/experiments/urfd/splits.npz b/artifacts/experiments/urfd/splits.npz new file mode 100644 index 0000000000000000000000000000000000000000..75d0c9c4fbec200f7df55400199b0e5b53f3acf9 --- /dev/null +++ b/artifacts/experiments/urfd/splits.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58ae99f939839832f37f7bf92b537a1212d6f4833e0cc71ca371877e340726c3 +size 978 diff --git a/artifacts/experiments/urfd_repeated/aggregate_f1.png b/artifacts/experiments/urfd_repeated/aggregate_f1.png new file mode 100644 index 0000000000000000000000000000000000000000..8600aa2b6807a8f684d89897c693179ff9b79318 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/aggregate_f1.png differ diff --git a/artifacts/experiments/urfd_repeated/aggregate_results.csv b/artifacts/experiments/urfd_repeated/aggregate_results.csv new file mode 100644 index 0000000000000000000000000000000000000000..120d03db2426d7d777d891066b73e3d46a1515a5 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/aggregate_results.csv @@ -0,0 +1,5 @@ +model,accuracy_mean,accuracy_std,accuracy_min,accuracy_max,precision_mean,precision_std,precision_min,precision_max,recall_mean,recall_std,recall_min,recall_max,specificity_mean,specificity_std,specificity_min,specificity_max,f1_mean,f1_std,f1_min,f1_max,roc_auc_mean,roc_auc_std,roc_auc_min,roc_auc_max,training_seconds_mean,training_seconds_std,training_seconds_min,training_seconds_max +random_forest,0.9681174656574152,0.02549334076868179,0.9310344827586207,1.0,0.95,0.1118033988749895,0.75,1.0,0.885,0.1140175425099138,0.75,1.0,0.9947368421052631,0.01176877882894621,0.9736842105263158,1.0,0.9073015873015873,0.06050097396607795,0.8571428571428571,1.0,0.996875,0.006987712429686846,0.984375,1.0,1.0664501365972683,0.12809362785822495,0.9882721559988568,1.2935919419978745 +logistic_regression,0.9694245864872443,0.020663257508523332,0.9512195121951219,1.0,0.8800000000000001,0.17888543819998312,0.6,1.0,0.9550000000000001,0.06224949798994365,0.875,1.0,0.9773524720893143,0.031139304272703755,0.9393939393939394,1.0,0.9039181286549708,0.09473855676738674,0.75,1.0,0.9910197368421052,0.01490988193426192,0.965625,1.0,0.022850994201144205,0.002161931957423829,0.02111099699686747,0.025282849004724994 +pose_tcn,0.9605095318194561,0.039725647128530425,0.896551724137931,1.0,0.9099999999999999,0.12449899597988734,0.75,1.0,0.915,0.1318142632646407,0.7,1.0,0.982615629984051,0.02671391843941856,0.9393939393939394,1.0,0.900578898225957,0.06877022946810625,0.8235294117647058,1.0,0.9829605263157895,0.03471322213305365,0.9210526315789473,1.0,1.3342455781967146,0.28321537202272556,0.9513108629907947,1.74490802299988 +pose_gru,0.941708718811326,0.04285431579569855,0.896551724137931,1.0,0.86,0.19493588689617924,0.6,1.0,0.865,0.1387443692551161,0.7,1.0,0.971291866028708,0.04157451482531765,0.9090909090909091,1.0,0.8416900093370682,0.09762489706564578,0.75,1.0,0.9959728867623603,0.005589963754536537,0.9886363636363636,1.0,1.0812091261992465,0.4035546171412758,0.6123746740049683,1.667068080001627 diff --git a/artifacts/experiments/urfd_repeated/per_run_results.csv b/artifacts/experiments/urfd_repeated/per_run_results.csv new file mode 100644 index 0000000000000000000000000000000000000000..c305c8a0cb0a79b466f6a526bdd3cc5a53e84bbf --- /dev/null +++ b/artifacts/experiments/urfd_repeated/per_run_results.csv @@ -0,0 +1,21 @@ +seed,model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds,device +13,logistic_regression,0.9512195121951219,0.6,1.0,0.9473684210526315,0.75,1.0,0.02111099699686747,cpu +13,pose_gru,0.9512195121951219,0.6,1.0,0.9473684210526315,0.75,0.9912280701754386,0.6123746740049683,cuda +13,pose_tcn,0.975609756097561,0.75,1.0,0.9736842105263158,0.8571428571428571,1.0,1.268194460993982,cuda +13,random_forest,0.975609756097561,0.75,1.0,0.9736842105263158,0.8571428571428571,1.0,1.0231628899928182,cpu +21,logistic_regression,1.0,1.0,1.0,1.0,1.0,1.0,0.02136966001125984,cpu +21,pose_gru,1.0,1.0,1.0,1.0,1.0,1.0,1.667068080001627,cuda +21,pose_tcn,1.0,1.0,1.0,1.0,1.0,1.0,1.368639754000469,cuda +21,random_forest,1.0,1.0,1.0,1.0,1.0,1.0,1.0289278239943087,cpu +42,logistic_regression,0.9512195121951219,0.8,1.0,0.9393939393939394,0.8888888888888888,1.0,0.025282849004724994,cpu +42,pose_gru,0.9024390243902439,0.7,0.875,0.9090909090909091,0.7777777777777778,0.9886363636363636,1.2036171649961034,cuda +42,pose_tcn,0.9512195121951219,0.8,1.0,0.9393939393939394,0.8888888888888888,1.0,0.9513108629907947,cuda +42,random_forest,0.975609756097561,1.0,0.875,1.0,0.9333333333333333,1.0,0.9882721559988568,cpu +84,logistic_regression,0.9791666666666666,1.0,0.875,1.0,0.9333333333333333,0.965625,0.021342011998058297,cpu +84,pose_gru,0.9583333333333334,1.0,0.75,1.0,0.8571428571428571,1.0,1.1110608339949977,cuda +84,pose_tcn,0.9791666666666666,1.0,0.875,1.0,0.9333333333333333,0.9937499999999999,1.3381747899984475,cuda +84,random_forest,0.9583333333333334,1.0,0.75,1.0,0.8571428571428571,0.984375,0.9982958710024832,cpu +123,logistic_regression,0.9655172413793104,1.0,0.9,1.0,0.9473684210526315,0.9894736842105263,0.025149452994810417,cpu +123,pose_gru,0.896551724137931,1.0,0.7,1.0,0.8235294117647058,1.0,0.8119248779985355,cuda +123,pose_tcn,0.896551724137931,1.0,0.7,1.0,0.8235294117647058,0.9210526315789473,1.74490802299988,cuda +123,random_forest,0.9310344827586207,1.0,0.8,1.0,0.8888888888888888,1.0,1.2935919419978745,cpu diff --git a/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..3a89eec19ab2bcdb59ea4125daedf7ab29abfff7 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/metrics.json b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..96e7e2063f56945aecd02eff94c22d0388369b30 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9655172413793104, + "precision": 1.0, + "recall": 0.9, + "specificity": 1.0, + "f1": 0.9473684210526315, + "roc_auc": 0.9894736842105263, + "threshold": 0.5, + "tn": 19, + "fp": 0, + "fn": 1, + "tp": 9, + "n_samples": 29, + "model": "logistic_regression", + "seed": 123, + "training_seconds": 0.025149452994810417, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 134, + "groups": 38, + "normal": 114, + "fall": 20 + }, + "validation": { + "samples": 37, + "groups": 13, + "normal": 31, + "fall": 6 + }, + "test": { + "samples": 29, + "groups": 13, + "normal": 19, + "fall": 10 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/model.joblib b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..3947956f8da132cc7003eaf1032fbaf8db59a999 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd64e7183985dd7cabf9d5e76707786786a0f99e7a5953a995aa3462733387fd +size 21047 diff --git a/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/predictions.csv b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..e3ebd002800cb58c65318299f423fc978343045a --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/predictions.csv @@ -0,0 +1,30 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.8714424,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9968592,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.99814665,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=0:40,1,0.6150006,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=10:50,1,0.8142116,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=11:51,1,0.8370313,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.95890343,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.035059758,0 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.99813807,1 +data/videos/fall/fall-29/fall-29-cam0.mp4#sample=0:40,1,0.9841054,1 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.0053262985,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.058787502,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.10868171,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=10:50,0,2.9298852e-05,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=20:60,0,4.0773317e-05,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=30:70,0,0.0011008652,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=40:80,0,0.0056155613,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,6.5174994e-05,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,1.8500707e-05,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.00063501054,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.0037459473,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.002324134,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.0001870078,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,8.778199e-06,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=30:70,0,0.0023080045,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=40:80,0,0.0005286265,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=50:90,0,0.00027970385,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=60:100,0,3.850741e-05,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=70:110,0,5.7593948e-06,0 diff --git a/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..89c2fe3d90b00dce8d3552d3bb6452dd0913285f Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/logistic_regression/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/model_comparison.png b/artifacts/experiments/urfd_repeated/seed_123/model_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..10112dbb8bab8e1c0110e0afa7deb197ac81f320 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/model_comparison.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_gru/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..89a51dac5da72248fd896720bb8544fa59980693 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_gru/metrics.json b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..f31be30441f5bea7e9d291ae8f9cfb12aff87bbc --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.896551724137931, + "precision": 1.0, + "recall": 0.7, + "specificity": 1.0, + "f1": 0.8235294117647058, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 19, + "fp": 0, + "fn": 3, + "tp": 7, + "n_samples": 29, + "model": "pose_gru", + "architecture": "gru", + "seed": 123, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 17, + "best_val_loss": 0.23604731541126966, + "training_seconds": 0.8119248779985355, + "parameters": 79489, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 134, + "groups": 38, + "normal": 114, + "fall": 20 + }, + "validation": { + "samples": 37, + "groups": 13, + "normal": 31, + "fall": 6 + }, + "test": { + "samples": 29, + "groups": 13, + "normal": 19, + "fall": 10 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_gru/model.pt b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..2aacd7bc2cc79f4998e7f9d35472aa89f833d0a4 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c91fe38a28796056c69422102d51630e1bd7676c105cdc5353d94bf597985504 +size 325083 diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_gru/predictions.csv b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..7bedf3532920cc803ebb611ec73090d85ee3b25c --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/predictions.csv @@ -0,0 +1,30 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.35767898,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9803091,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.99202967,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=0:40,1,0.55568933,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=10:50,1,0.35436755,0 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=11:51,1,0.32558507,0 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.988262,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.8008202,1 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.9947249,1 +data/videos/fall/fall-29/fall-29-cam0.mp4#sample=0:40,1,0.9605884,1 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.0071825925,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.011605739,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.08605945,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=10:50,0,0.028078236,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=20:60,0,0.0039135297,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=30:70,0,0.008724526,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=40:80,0,0.222154,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.022441126,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.024359005,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.032382514,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.04610334,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.015914852,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.008139296,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.005882212,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=30:70,0,0.0040533254,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=40:80,0,0.0072238776,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=50:90,0,0.013535955,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=60:100,0,0.012939351,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=70:110,0,0.010590921,0 diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_gru/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7d058576bda3f01aa97ac89992f51f7dde55fe42 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_gru/training_curve.png b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..a6440f834516ccbf6b786d24b8b8334e9997ef11 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/pose_gru/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..4101344cead0715a7eb098a173fe085c65a52ddb Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/metrics.json b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..17eeddf1d6298ca269e70103fb4649a5b66fcd7e --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.896551724137931, + "precision": 1.0, + "recall": 0.7, + "specificity": 1.0, + "f1": 0.8235294117647058, + "roc_auc": 0.9210526315789473, + "threshold": 0.5, + "tn": 19, + "fp": 0, + "fn": 3, + "tp": 7, + "n_samples": 29, + "model": "pose_tcn", + "architecture": "tcn", + "seed": 123, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 46, + "best_val_loss": 0.1802956284955144, + "training_seconds": 1.74490802299988, + "parameters": 12929, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 134, + "groups": 38, + "normal": 114, + "fall": 20 + }, + "validation": { + "samples": 37, + "groups": 13, + "normal": 31, + "fall": 6 + }, + "test": { + "samples": 29, + "groups": 13, + "normal": 19, + "fall": 10 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/model.pt b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..229ef41f891965aad8342e3e8b1c4930dd09ceed --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f786e5f887d472b172da34dbcabe2ebd18bf26b9636ef0674d2468bb4ce0ec1 +size 58759 diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/predictions.csv b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..682f6fe52fb71a952c516f1ae0a061a14de4205f --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/predictions.csv @@ -0,0 +1,30 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.9182121,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9761614,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.97456235,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=0:40,1,0.5515729,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=10:50,1,0.019800719,0 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=11:51,1,0.013609737,0 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.90002793,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.40597087,0 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.9819664,1 +data/videos/fall/fall-29/fall-29-cam0.mp4#sample=0:40,1,0.9849961,1 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.022150403,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.058020227,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.17342727,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=10:50,0,0.008698789,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=20:60,0,0.010584317,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=30:70,0,0.0081666205,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=40:80,0,0.017216044,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.03573762,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.03528052,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.036040433,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.02276568,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.010692995,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.0033858838,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.0005006488,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=30:70,0,0.005397269,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=40:80,0,0.0040390575,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=50:90,0,0.0026508006,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=60:100,0,0.00044848185,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=70:110,0,0.0001873869,0 diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..a2fe4fac51ff2cc77577a08d48746c626fa77b05 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/training_curve.png b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..bcc4cc50d0d3be8adc4f8d233f52ae6c30c1524d Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/pose_tcn/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/random_forest/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_123/random_forest/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..aba60608d5d2739dfa5147bf715b657ceb7564f0 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/random_forest/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/random_forest/metrics.json b/artifacts/experiments/urfd_repeated/seed_123/random_forest/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..35224eee1d0fce0fe6316c44cdc947830e6377e0 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/random_forest/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9310344827586207, + "precision": 1.0, + "recall": 0.8, + "specificity": 1.0, + "f1": 0.8888888888888888, + "roc_auc": 1.0, + "threshold": 0.44000000000000006, + "tn": 19, + "fp": 0, + "fn": 2, + "tp": 8, + "n_samples": 29, + "model": "random_forest", + "seed": 123, + "training_seconds": 1.2935919419978745, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 134, + "groups": 38, + "normal": 114, + "fall": 20 + }, + "validation": { + "samples": 37, + "groups": 13, + "normal": 31, + "fall": 6 + }, + "test": { + "samples": 29, + "groups": 13, + "normal": 19, + "fall": 10 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_123/random_forest/model.joblib b/artifacts/experiments/urfd_repeated/seed_123/random_forest/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..e4fc2a07dd9b49f96c6ced9de67f185b2f201cc8 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/random_forest/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e6f7c4b7c34e1a15d745b51bef7b4e01a025447b20686ff797e9f28e536a747 +size 342195 diff --git a/artifacts/experiments/urfd_repeated/seed_123/random_forest/predictions.csv b/artifacts/experiments/urfd_repeated/seed_123/random_forest/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..1445cc4ba78a6bac48dab0095631130d015e64c7 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/random_forest/predictions.csv @@ -0,0 +1,30 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.3965158730158731,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.5286984126984129,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.5557777777777779,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=0:40,1,0.6348849206349206,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=10:50,1,0.8065978835978835,1 +data/videos/fall/fall-05/fall-05-cam0.mp4#sample=11:51,1,0.8049312169312169,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.7996666666666666,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.30144444444444446,0 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.7571111111111111,1 +data/videos/fall/fall-29/fall-29-cam0.mp4#sample=0:40,1,0.5077208994708996,1 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.005166666666666667,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.041944444444444444,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.16144444444444445,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=10:50,0,0.03177777777777777,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=20:60,0,0.012666666666666668,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=30:70,0,0.036875661375661374,0 +data/videos/normal/adl-19/adl-19-cam0.mp4#sample=40:80,0,0.07214550264550265,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.12661111111111106,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.040888888888888884,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.13785714285714284,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.21553835978835972,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.24814550264550267,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.2016587301587301,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.141,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=30:70,0,0.07480555555555557,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=40:80,0,0.08911111111111111,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=50:90,0,0.10874603174603176,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=60:100,0,0.08255555555555555,0 +data/videos/normal/adl-40/adl-40-cam0.mp4#sample=70:110,0,0.06016666666666668,0 diff --git a/artifacts/experiments/urfd_repeated/seed_123/random_forest/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_123/random_forest/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfdc06161530ccc47edb187ded23f143f84115e Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_123/random_forest/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_123/results.csv b/artifacts/experiments/urfd_repeated/seed_123/results.csv new file mode 100644 index 0000000000000000000000000000000000000000..5977f1c062c2b9a0e70efa3e9cb755db22bf8fa7 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/results.csv @@ -0,0 +1,5 @@ +model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds +logistic_regression,0.9655172413793104,1.0,0.9,1.0,0.9473684210526315,0.9894736842105263,0.025149452994810417 +random_forest,0.9310344827586207,1.0,0.8,1.0,0.8888888888888888,1.0,1.2935919419978745 +pose_gru,0.896551724137931,1.0,0.7,1.0,0.8235294117647058,1.0,0.8119248779985355 +pose_tcn,0.896551724137931,1.0,0.7,1.0,0.8235294117647058,0.9210526315789473,1.74490802299988 diff --git a/artifacts/experiments/urfd_repeated/seed_123/splits.npz b/artifacts/experiments/urfd_repeated/seed_123/splits.npz new file mode 100644 index 0000000000000000000000000000000000000000..09d33c8946d9c792469006151258f11f8e4c22e0 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_123/splits.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7463960031a328b0a425d06a66f2b907e29375bb1ae993e8981eb4047f30fd07 +size 979 diff --git a/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..71237a0d0b4e4ae73bc1bd4734154e9b04ea984c Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/metrics.json b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..45a911b76002a9252fd55114c8da0d083f50e79a --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9512195121951219, + "precision": 0.6, + "recall": 1.0, + "specificity": 0.9473684210526315, + "f1": 0.75, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 36, + "fp": 2, + "fn": 0, + "tp": 3, + "n_samples": 41, + "model": "logistic_regression", + "seed": 13, + "training_seconds": 0.02111099699686747, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 126, + "groups": 38, + "normal": 103, + "fall": 23 + }, + "validation": { + "samples": 33, + "groups": 13, + "normal": 23, + "fall": 10 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 38, + "fall": 3 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/model.joblib b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..8de36847f20da7f6f5a65cf601b448e40d66f407 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b781bcfe45e79e2335f6874d1e2fcf64567bca01f890f64cdb532ac24095b50d +size 21047 diff --git a/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/predictions.csv b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..cfcb29023e5ef3ef56bba6a263997fecd8f3f5c7 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.994507,1 +data/videos/fall/fall-17/fall-17-cam0.mp4#sample=0:40,1,0.87283736,1 +data/videos/fall/fall-26/fall-26-cam0.mp4#sample=0:40,1,0.9351381,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.00040770584,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.00096406933,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=0:40,0,0.060799465,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=10:50,0,0.31406286,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=20:60,0,0.6843436,1 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,2.057239e-05,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,1.697187e-05,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,9.296491e-05,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=10:50,0,9.1673996e-07,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=20:60,0,6.4074666e-05,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=30:70,0,0.0025378957,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=40:80,0,0.0010592887,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=50:90,0,0.0005227714,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=52:92,0,0.00062202924,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.0036722755,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.05166914,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.3987657,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.000893942,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.001740725,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.004695178,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.0018438491,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.0016835403,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,1.4482275e-05,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,1.3951938e-05,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,1.3261281e-10,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.011532649,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.0046605263,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.00042677848,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,2.3345188e-05,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,7.143413e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,8.756077e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,2.6779564e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,4.469113e-05,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.00014023065,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.00023090684,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.0003256183,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.0011855667,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.63671064,1 diff --git a/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..792c6ceaccbde5336f2893edaf598adffc8b9f6d Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/logistic_regression/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/model_comparison.png b/artifacts/experiments/urfd_repeated/seed_13/model_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..be1472e2f2de8db591e7c71bf9c7d5a693bcd300 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/model_comparison.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_gru/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..54c403c29c3e1f4a09dad7e0c93a0ea37adc1152 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_gru/metrics.json b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..15fdcac19bdd6f008f8667e6a54a98f203d14308 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9512195121951219, + "precision": 0.6, + "recall": 1.0, + "specificity": 0.9473684210526315, + "f1": 0.75, + "roc_auc": 0.9912280701754386, + "threshold": 0.36000000000000004, + "tn": 36, + "fp": 2, + "fn": 0, + "tp": 3, + "n_samples": 41, + "model": "pose_gru", + "architecture": "gru", + "seed": 13, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 18, + "best_val_loss": 0.2206486463546753, + "training_seconds": 0.6123746740049683, + "parameters": 79489, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 126, + "groups": 38, + "normal": 103, + "fall": 23 + }, + "validation": { + "samples": 33, + "groups": 13, + "normal": 23, + "fall": 10 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 38, + "fall": 3 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_gru/model.pt b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..2bc201174ea106bdcb443f230aa89095eb2ce83f --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a171c0c9b45ccefcdad18b91045b483e68cc70da70711ee24b7a2e5611d53221 +size 325083 diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_gru/predictions.csv b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..51f188dba3089b2613a72ffa9e5de93a5c7b6e80 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.9509712,1 +data/videos/fall/fall-17/fall-17-cam0.mp4#sample=0:40,1,0.82445276,1 +data/videos/fall/fall-26/fall-26-cam0.mp4#sample=0:40,1,0.9865079,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.0022780534,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.011260025,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=0:40,0,0.003008801,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=10:50,0,0.07059404,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=20:60,0,0.6897376,1 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,0.0033153663,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,0.0061660167,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,0.011214842,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=10:50,0,0.0072109075,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=20:60,0,0.0061757346,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=30:70,0,0.0076798312,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=40:80,0,0.014064031,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=50:90,0,0.011778015,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=52:92,0,0.009865224,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.0072510773,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.016588315,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.11856797,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.03365062,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.028034579,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.047279656,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.026809784,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.024114316,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,0.014409595,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,0.0069065946,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,0.0059435675,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.09777164,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.03810759,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.021393942,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.014349292,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.008168659,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.010234155,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.035865143,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.0201759,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.0060326126,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.0057999934,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.023153238,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.080989785,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.8826936,1 diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_gru/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..f29cc83c4c87a1e61042e70ee1116c9b5bf2e400 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_gru/training_curve.png b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..9b14ea84f7b158058bda2a2dd2b541b484123370 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/pose_gru/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..8126ad1eab1aea3af831862212ab961b4d062259 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/metrics.json b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..148e68a436900862679bd544f57d0daf8bd08c28 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.975609756097561, + "precision": 0.75, + "recall": 1.0, + "specificity": 0.9736842105263158, + "f1": 0.8571428571428571, + "roc_auc": 1.0, + "threshold": 0.36500000000000005, + "tn": 37, + "fp": 1, + "fn": 0, + "tp": 3, + "n_samples": 41, + "model": "pose_tcn", + "architecture": "tcn", + "seed": 13, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 45, + "best_val_loss": 0.17336134612560272, + "training_seconds": 1.268194460993982, + "parameters": 12929, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 126, + "groups": 38, + "normal": 103, + "fall": 23 + }, + "validation": { + "samples": 33, + "groups": 13, + "normal": 23, + "fall": 10 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 38, + "fall": 3 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/model.pt b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..309b6a48e815cd58e951d50175fc0a69b1213918 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:313d9ab98dd029fedfbe9399958b99ecbdee7aaf99f6acdea7008ed5415500a7 +size 58759 diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/predictions.csv b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..46bc04ba78500322f4bfcf9b53d31f618b6ff556 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.9990177,1 +data/videos/fall/fall-17/fall-17-cam0.mp4#sample=0:40,1,0.9194575,1 +data/videos/fall/fall-26/fall-26-cam0.mp4#sample=0:40,1,0.9922673,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.008707297,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.024180215,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=0:40,0,0.02398262,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=10:50,0,0.07041097,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=20:60,0,0.17998065,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,0.007769556,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,0.019232346,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,0.012456972,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=10:50,0,0.023344263,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=20:60,0,0.016221855,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=30:70,0,0.02058882,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=40:80,0,0.02687553,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=50:90,0,0.025084365,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=52:92,0,0.024726491,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.014524675,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.022763865,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.069328845,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.07700675,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.02164276,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.051635865,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.038723007,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.036426205,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,0.04207769,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,0.0003673741,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,7.558212e-11,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.06625711,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.03163815,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.014552809,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.007924037,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.0021378014,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.0025880495,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.14608024,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.06378349,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.030581927,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.018497026,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.022009714,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.023348192,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.4476926,1 diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..8044851a896728736769c74bdacb566a37f515ca Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/training_curve.png b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..ead15c1b0ec19d1a53a9c1ad1778b8bbbfd83756 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/pose_tcn/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/random_forest/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_13/random_forest/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..9c8d96efb597fdc3c9bb553d049a279b71d41023 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/random_forest/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/random_forest/metrics.json b/artifacts/experiments/urfd_repeated/seed_13/random_forest/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..3cc1b96b70feb3f849f8cce19de908a543b05783 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/random_forest/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.975609756097561, + "precision": 0.75, + "recall": 1.0, + "specificity": 0.9736842105263158, + "f1": 0.8571428571428571, + "roc_auc": 1.0, + "threshold": 0.37500000000000006, + "tn": 37, + "fp": 1, + "fn": 0, + "tp": 3, + "n_samples": 41, + "model": "random_forest", + "seed": 13, + "training_seconds": 1.0231628899928182, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 126, + "groups": 38, + "normal": 103, + "fall": 23 + }, + "validation": { + "samples": 33, + "groups": 13, + "normal": 23, + "fall": 10 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 38, + "fall": 3 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_13/random_forest/model.joblib b/artifacts/experiments/urfd_repeated/seed_13/random_forest/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..63c942900862406c67b7d1259cec635998ec0eb4 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/random_forest/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b76b7452131b2777e179946d24c3e62724bccfccf94077266a0aeadc88786b16 +size 355075 diff --git a/artifacts/experiments/urfd_repeated/seed_13/random_forest/predictions.csv b/artifacts/experiments/urfd_repeated/seed_13/random_forest/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..209fb5f24ccea142c1e7699159d34de861c9e683 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/random_forest/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.8595238095238096,1 +data/videos/fall/fall-17/fall-17-cam0.mp4#sample=0:40,1,0.3831587301587302,1 +data/videos/fall/fall-26/fall-26-cam0.mp4#sample=0:40,1,0.7573253968253968,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.03844444444444444,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.10655555555555556,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=0:40,0,0.25843253968253954,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=10:50,0,0.3574325396825397,0 +data/videos/normal/adl-05/adl-05-cam0.mp4#sample=20:60,0,0.37583730158730155,1 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,0.014888888888888889,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,0.0480436507936508,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,0.10976587301587301,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=10:50,0,0.02894444444444444,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=20:60,0,0.030055555555555554,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=30:70,0,0.08112698412698414,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=40:80,0,0.09133333333333334,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=50:90,0,0.0763888888888889,0 +data/videos/normal/adl-15/adl-15-cam0.mp4#sample=52:92,0,0.06705555555555556,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=20:60,0,0.02772222222222222,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=30:70,0,0.0915,0 +data/videos/normal/adl-16/adl-16-cam0.mp4#sample=40:80,0,0.2159444444444445,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.11442857142857144,0 +data/videos/normal/adl-28/adl-28-cam0.mp4#sample=0:40,0,0.14373809523809525,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.22384126984126984,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.12349999999999998,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.11155555555555556,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,0.1211111111111111,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,0.1365674603174603,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,0.13262301587301592,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.2475,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.21384920634920632,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.17561111111111108,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.10900000000000001,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.038992063492063495,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.0135,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.007,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.0013333333333333335,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.03716666666666667,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.12033333333333333,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.18165476190476187,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.18616666666666665,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.3250000000000001,0 diff --git a/artifacts/experiments/urfd_repeated/seed_13/random_forest/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_13/random_forest/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfdc06161530ccc47edb187ded23f143f84115e Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_13/random_forest/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_13/results.csv b/artifacts/experiments/urfd_repeated/seed_13/results.csv new file mode 100644 index 0000000000000000000000000000000000000000..bdd0c4562832a6b350739885a12950c94a55828f --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/results.csv @@ -0,0 +1,5 @@ +model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds +random_forest,0.975609756097561,0.75,1.0,0.9736842105263158,0.8571428571428571,1.0,1.0231628899928182 +pose_tcn,0.975609756097561,0.75,1.0,0.9736842105263158,0.8571428571428571,1.0,1.268194460993982 +pose_gru,0.9512195121951219,0.6,1.0,0.9473684210526315,0.75,0.9912280701754386,0.6123746740049683 +logistic_regression,0.9512195121951219,0.6,1.0,0.9473684210526315,0.75,1.0,0.02111099699686747 diff --git a/artifacts/experiments/urfd_repeated/seed_13/splits.npz b/artifacts/experiments/urfd_repeated/seed_13/splits.npz new file mode 100644 index 0000000000000000000000000000000000000000..3f805fab03b1d1a7b866ccc81f44cad4dfe9c4e7 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_13/splits.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cab314b90ee8205c1b3912c1728fea80947eb23b6c715a76f5e41aeaf6f9d82 +size 974 diff --git a/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..83ae416e676de0f8ddbf9e1512b6a3d9485ff415 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/metrics.json b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..f509449e5003d0727ae2230c13306024a0b9d3c9 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 1.0, + "precision": 1.0, + "recall": 1.0, + "specificity": 1.0, + "f1": 1.0, + "roc_auc": 1.0, + "threshold": 0.6000000000000001, + "tn": 29, + "fp": 0, + "fn": 0, + "tp": 2, + "n_samples": 31, + "model": "logistic_regression", + "seed": 21, + "training_seconds": 0.02136966001125984, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 127, + "groups": 38, + "normal": 100, + "fall": 27 + }, + "validation": { + "samples": 42, + "groups": 13, + "normal": 35, + "fall": 7 + }, + "test": { + "samples": 31, + "groups": 13, + "normal": 29, + "fall": 2 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/model.joblib b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..e0b015c31d48e8a56def0d4d0d58c4c97432d51e --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa8e3d992ba0879be514cde348e7add42278a223f2ecdcf6e34c165ec824c6a7 +size 21047 diff --git a/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/predictions.csv b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..caa05a3471293e7e75570e0bcd1595d04931e15e --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/predictions.csv @@ -0,0 +1,32 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.999813,1 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.99971837,1 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=0:40,0,0.0004982774,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=10:50,0,0.002879995,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=20:60,0,0.0035653098,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,1.9939023e-06,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,4.150401e-06,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,2.7467475e-05,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=20:60,0,3.238631e-07,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=30:70,0,5.52503e-06,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=40:80,0,1.9482564e-05,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=49:89,0,4.1039402e-05,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,1.4431541e-06,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,6.2638755e-06,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,3.2316755e-05,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,3.46592e-05,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=10:50,0,6.118577e-07,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=20:60,0,0.000456225,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=30:70,0,0.00033522418,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,3.4048523e-07,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,3.8112607e-06,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,3.833268e-05,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.0003960846,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,5.2234584e-05,0 +data/videos/normal/adl-27/adl-27-cam0.mp4#sample=0:40,0,5.9529382e-05,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=0:40,0,0.0019807052,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=2:42,0,0.00071706553,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.0074499208,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.0016041779,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,6.705834e-05,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,2.4700278e-06,0 diff --git a/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..792c6ceaccbde5336f2893edaf598adffc8b9f6d Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/logistic_regression/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/model_comparison.png b/artifacts/experiments/urfd_repeated/seed_21/model_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..857506510d0fc9a26c84e1a5f769d41b01e1943a Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/model_comparison.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_gru/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..1cb9dc8ac4f41d754a70d14795dd507bc8004b91 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_gru/metrics.json b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..079080a94a90696d8e9f53aaf7f9578d6fe2becd --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 1.0, + "precision": 1.0, + "recall": 1.0, + "specificity": 1.0, + "f1": 1.0, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 29, + "fp": 0, + "fn": 0, + "tp": 2, + "n_samples": 31, + "model": "pose_gru", + "architecture": "gru", + "seed": 21, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 50, + "best_val_loss": 0.005415067644207738, + "training_seconds": 1.667068080001627, + "parameters": 79489, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 127, + "groups": 38, + "normal": 100, + "fall": 27 + }, + "validation": { + "samples": 42, + "groups": 13, + "normal": 35, + "fall": 7 + }, + "test": { + "samples": 31, + "groups": 13, + "normal": 29, + "fall": 2 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_gru/model.pt b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..73890a0e13d40938f7cb3187f6aaabe0955aeb04 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dba692b10d50688cc7b31f17892efdc86584afb8affbc268c763e8892dc043b +size 325083 diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_gru/predictions.csv b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..3a95c78a2cffff8ffaa7ecd3084f4010bbf9a202 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/predictions.csv @@ -0,0 +1,32 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.9984951,1 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.9982735,1 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=0:40,0,0.00071111036,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=10:50,0,0.041733716,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=20:60,0,0.29806152,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,0.0002286054,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,0.00022569764,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,0.000383351,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=20:60,0,0.0006399125,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=30:70,0,0.00038798823,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=40:80,0,0.00025226249,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=49:89,0,0.00021751101,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,0.0002352982,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,0.0002479718,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,0.00028553343,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,0.00028856544,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=10:50,0,0.00021940481,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=20:60,0,0.00020251142,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=30:70,0,0.0005972904,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,0.0003501427,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,0.0002227916,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,0.0005405557,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.00062009186,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.0006271917,0 +data/videos/normal/adl-27/adl-27-cam0.mp4#sample=0:40,0,0.00077357143,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=0:40,0,0.0037347707,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=2:42,0,0.0036750068,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.0014691065,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.00062572496,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.0002789092,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.00020721322,0 diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_gru/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7d058576bda3f01aa97ac89992f51f7dde55fe42 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_gru/training_curve.png b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..e1979cf4fb0121971b0d50b002803d2f67a5db64 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/pose_gru/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..2e2677ed4135ba7d4c84d7d65f2a724404f7f450 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/metrics.json b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..c0db481b02ed88f8a958e89d58e35982e82c49dd --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 1.0, + "precision": 1.0, + "recall": 1.0, + "specificity": 1.0, + "f1": 1.0, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 29, + "fp": 0, + "fn": 0, + "tp": 2, + "n_samples": 31, + "model": "pose_tcn", + "architecture": "tcn", + "seed": 21, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 50, + "best_val_loss": 0.035785171668976545, + "training_seconds": 1.368639754000469, + "parameters": 12929, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 127, + "groups": 38, + "normal": 100, + "fall": 27 + }, + "validation": { + "samples": 42, + "groups": 13, + "normal": 35, + "fall": 7 + }, + "test": { + "samples": 31, + "groups": 13, + "normal": 29, + "fall": 2 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/model.pt b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..f8a10b0bcdd440eba826a34d17b18c4868b13fe6 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ed4ec4a38ef847ffcf23b31a54ddb80fe42cc94f68ff1973ec6fac38d7cf09d +size 58759 diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/predictions.csv b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..1d1501614948de97740fc3d6769e16cdf48c8350 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/predictions.csv @@ -0,0 +1,32 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.99476385,1 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.9942135,1 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=0:40,0,0.053165186,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=10:50,0,0.3019987,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=20:60,0,0.39571774,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,0.01240915,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,0.008674891,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,0.014480398,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=20:60,0,0.0037388112,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=30:70,0,0.009598091,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=40:80,0,0.03452105,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=49:89,0,0.014246359,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,0.009437372,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,0.009290681,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,0.020547053,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,0.0063361144,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=10:50,0,0.0019098367,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=20:60,0,0.002308625,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=30:70,0,0.002995367,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,0.0026281655,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,0.0014329808,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,0.0031739725,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.10732315,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.040824752,0 +data/videos/normal/adl-27/adl-27-cam0.mp4#sample=0:40,0,0.053443607,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=0:40,0,0.18436298,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=2:42,0,0.28004143,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.08134177,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.008471513,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.0017816339,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.00026739534,0 diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..8044851a896728736769c74bdacb566a37f515ca Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/training_curve.png b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..3d96a1512af6247da134febd53132945a15ca2d7 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/pose_tcn/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/random_forest/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_21/random_forest/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..dd471a004c6bde414e88a4e9c355b6b24333076e Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/random_forest/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/random_forest/metrics.json b/artifacts/experiments/urfd_repeated/seed_21/random_forest/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..c6f1c27319f875bde6af44fb263cafb6b7e74929 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/random_forest/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 1.0, + "precision": 1.0, + "recall": 1.0, + "specificity": 1.0, + "f1": 1.0, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 29, + "fp": 0, + "fn": 0, + "tp": 2, + "n_samples": 31, + "model": "random_forest", + "seed": 21, + "training_seconds": 1.0289278239943087, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 127, + "groups": 38, + "normal": 100, + "fall": 27 + }, + "validation": { + "samples": 42, + "groups": 13, + "normal": 35, + "fall": 7 + }, + "test": { + "samples": 31, + "groups": 13, + "normal": 29, + "fall": 2 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_21/random_forest/model.joblib b/artifacts/experiments/urfd_repeated/seed_21/random_forest/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..3a75a050effcf305f1c499e107c9e8d6b4540983 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/random_forest/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9eac715f90129549e7576497f4db451a32829a9803bbbb39d14cde00b73a43c2 +size 392371 diff --git a/artifacts/experiments/urfd_repeated/seed_21/random_forest/predictions.csv b/artifacts/experiments/urfd_repeated/seed_21/random_forest/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..8a005e1aae91cb89e712f64e81b272b74cf302ad --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/random_forest/predictions.csv @@ -0,0 +1,32 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.5881388888888888,1 +data/videos/fall/fall-16/fall-16-cam0.mp4#sample=0:40,1,0.5745277777777777,1 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=0:40,0,0.04911111111111111,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=10:50,0,0.0815,0 +data/videos/normal/adl-03/adl-03-cam0.mp4#sample=20:60,0,0.1471111111111111,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,0.006944444444444443,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,0.036555555555555556,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,0.07083333333333333,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=20:60,0,0.04385714285714286,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=30:70,0,0.03777777777777777,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=40:80,0,0.04386111111111111,0 +data/videos/normal/adl-18/adl-18-cam0.mp4#sample=49:89,0,0.06744444444444445,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,0.03907407407407408,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,0.025916666666666664,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,0.04836111111111111,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,0.09702777777777777,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=10:50,0,0.04005555555555555,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=20:60,0,0.13119444444444447,0 +data/videos/normal/adl-22/adl-22-cam0.mp4#sample=30:70,0,0.15785714285714283,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,0.023888888888888887,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,0.054305555555555544,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,0.07408333333333332,0 +data/videos/normal/adl-24/adl-24-cam0.mp4#sample=0:40,0,0.15074999999999997,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.07075,0 +data/videos/normal/adl-27/adl-27-cam0.mp4#sample=0:40,0,0.08863888888888888,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=0:40,0,0.13244444444444442,0 +data/videos/normal/adl-29/adl-29-cam0.mp4#sample=2:42,0,0.11350000000000002,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=0:40,0,0.31833333333333336,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=10:50,0,0.32666666666666666,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=20:60,0,0.2638888888888888,0 +data/videos/normal/adl-32/adl-32-cam0.mp4#sample=27:67,0,0.13472222222222222,0 diff --git a/artifacts/experiments/urfd_repeated/seed_21/random_forest/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_21/random_forest/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfdc06161530ccc47edb187ded23f143f84115e Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_21/random_forest/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_21/results.csv b/artifacts/experiments/urfd_repeated/seed_21/results.csv new file mode 100644 index 0000000000000000000000000000000000000000..9efadcec131104c3715af2a8441afb0b7865254d --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/results.csv @@ -0,0 +1,5 @@ +model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds +logistic_regression,1.0,1.0,1.0,1.0,1.0,1.0,0.02136966001125984 +pose_gru,1.0,1.0,1.0,1.0,1.0,1.0,1.667068080001627 +pose_tcn,1.0,1.0,1.0,1.0,1.0,1.0,1.368639754000469 +random_forest,1.0,1.0,1.0,1.0,1.0,1.0,1.0289278239943087 diff --git a/artifacts/experiments/urfd_repeated/seed_21/splits.npz b/artifacts/experiments/urfd_repeated/seed_21/splits.npz new file mode 100644 index 0000000000000000000000000000000000000000..3065dfa201832933fb31df51d25d2da436b0975a --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_21/splits.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f413a3a77ab3c455d3984a8d2c8856886db9e66c3aa79a39fa4c76fc42bd176 +size 977 diff --git a/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..c4352a4a42b47c080ff2ecd715f07278d7f778b0 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/metrics.json b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..2452a4d9759ee1dad0d430f0830bb3a135fe71a3 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9512195121951219, + "precision": 0.8, + "recall": 1.0, + "specificity": 0.9393939393939394, + "f1": 0.8888888888888888, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 31, + "fp": 2, + "fn": 0, + "tp": 8, + "n_samples": 41, + "model": "logistic_regression", + "seed": 42, + "training_seconds": 0.025282849004724994, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/model.joblib b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..5c02f5815f480167a25797f7a7c6f71114207741 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdf1c4ffd6d38bb695db15df2e5829ae12f7b3121c8a288f0e56720ec2526806 +size 21047 diff --git a/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/predictions.csv b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..bb96ffe1fb35d382794a970c3f5beb0748ebc1a0 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.98377717,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9993255,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.999498,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.97410244,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.99896145,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.997311,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.9457497,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.99611413,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.0006896582,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.0023842053,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,2.3963743e-05,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,8.265512e-05,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.018153476,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.02516493,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.00035743922,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,6.653513e-07,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.00603225,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.0031787464,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.03760421,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.012593662,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.000474621,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.0028994733,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,2.2414151e-06,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.00026380236,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.51179487,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.7919934,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.0336354,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,1.8855539e-05,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.0051527205,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.042879105,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.009401498,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.00028797347,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,5.9999e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,2.6717935e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,4.8368015e-06,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.0002776527,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.0004259999,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.0019540016,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.0005490907,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.00027555088,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.017206663,0 diff --git a/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..792c6ceaccbde5336f2893edaf598adffc8b9f6d Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/logistic_regression/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/model_comparison.png b/artifacts/experiments/urfd_repeated/seed_42/model_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..c6154356f534fd47adb37628221c74c5c5d17411 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/model_comparison.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_gru/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..63b36bf597bfd9d79957f5edc37f23d0b8d20bf8 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_gru/metrics.json b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..88a15f7161837ec0afb66ce4f7a31b502d9e8240 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9024390243902439, + "precision": 0.7, + "recall": 0.875, + "specificity": 0.9090909090909091, + "f1": 0.7777777777777778, + "roc_auc": 0.9886363636363636, + "threshold": 0.5, + "tn": 30, + "fp": 3, + "fn": 1, + "tp": 7, + "n_samples": 41, + "model": "pose_gru", + "architecture": "gru", + "seed": 42, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 30, + "best_val_loss": 0.14927974343299866, + "training_seconds": 1.2036171649961034, + "parameters": 79489, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_gru/model.pt b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..cb9cd03b4a33cea57bb4b9c6807178318d5cbbb3 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:daf7d083966a9cf8ade21b4bec28123199336639c33ea561c140ae31cdc8d1bf +size 325083 diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_gru/predictions.csv b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..164853bb6037dd08c32b26cc5f39708dae5206ac --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.36374763,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9875322,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.9928555,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.9939282,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.9983101,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.9972844,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.99895227,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.99400866,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.0007733168,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.0023768344,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.0008485273,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.0010037357,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.00556503,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.04351345,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.08348232,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,0.00067634834,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.0010444914,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.0011891089,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.0016517256,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.0052665947,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.013075952,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.032113984,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,0.002390514,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.0011807706,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.013312802,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.6862216,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.5090663,1 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,0.001116697,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.0019210341,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.015289847,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.058644745,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.0013770493,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.00230475,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.0012230183,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.0026165335,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.0022358233,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.0015868503,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.0012599203,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.0034755394,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.010827422,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.9500953,1 diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_gru/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..22c481f3822952a18e4ff4b9081c9c9e7d515333 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_gru/training_curve.png b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..e998332d8cb491699f1aaa77fe74b1fdecbb74a0 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/pose_gru/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..6667c0747c32c5d4699cacfa1196e014b6d8ced0 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/metrics.json b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..1c38b7a59246ef4db1344be3201b001c27d57221 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9512195121951219, + "precision": 0.8, + "recall": 1.0, + "specificity": 0.9393939393939394, + "f1": 0.8888888888888888, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 31, + "fp": 2, + "fn": 0, + "tp": 8, + "n_samples": 41, + "model": "pose_tcn", + "architecture": "tcn", + "seed": 42, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 27, + "best_val_loss": 0.16090895235538483, + "training_seconds": 0.9513108629907947, + "parameters": 12929, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/model.pt b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..81359934f9ba89c4a7a384fcbb1c430a447e8a19 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9f02d417c3c785920bbb592f0af63fd9baa96c99bcedcc49b27c01106f98165 +size 58759 diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/predictions.csv b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..405b3817fb1d723e55856021b7b911693ca69415 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.97618514,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9786139,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.9824744,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.9479548,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.9917155,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.91648644,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.9619871,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.9757481,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.036800418,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.12765183,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.053033993,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.1157073,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.25427914,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.30693465,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.25294647,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,0.041943014,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.35644826,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.2825069,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.20812033,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.22714849,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.111267,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.13848732,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,0.054857913,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.045970306,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.5363943,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.66083694,1 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.24986179,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,0.025344677,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.15354195,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.12768711,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.13882609,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.05435837,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.021094386,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.023997432,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.20265935,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.22501923,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.111354865,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.06751009,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.08183312,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.100184046,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.4885858,0 diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..8044851a896728736769c74bdacb566a37f515ca Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/training_curve.png b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..9150c25249fbfa5dace8379e4559ae2b01d746b8 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/pose_tcn/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/random_forest/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_42/random_forest/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..4e98ee118b55098045a5780f5b906718bd2f77b9 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/random_forest/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/random_forest/metrics.json b/artifacts/experiments/urfd_repeated/seed_42/random_forest/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..f1f2fcc20280d96fd95de0fdc3e32e6d1fab7a90 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/random_forest/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.975609756097561, + "precision": 1.0, + "recall": 0.875, + "specificity": 1.0, + "f1": 0.9333333333333333, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 33, + "fp": 0, + "fn": 1, + "tp": 7, + "n_samples": 41, + "model": "random_forest", + "seed": 42, + "training_seconds": 0.9882721559988568, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 132, + "groups": 38, + "normal": 111, + "fall": 21 + }, + "validation": { + "samples": 27, + "groups": 13, + "normal": 20, + "fall": 7 + }, + "test": { + "samples": 41, + "groups": 13, + "normal": 33, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_42/random_forest/model.joblib b/artifacts/experiments/urfd_repeated/seed_42/random_forest/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..531b827861c25a64968aa30f7e6c80d40707afa3 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/random_forest/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49aac9a9a79a45fc5d59d3dd929e6fa866f16a88777834af04d8f92dcb098b6a +size 357699 diff --git a/artifacts/experiments/urfd_repeated/seed_42/random_forest/predictions.csv b/artifacts/experiments/urfd_repeated/seed_42/random_forest/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..dd52d11480f64ef7dd854f915fd995b88ac82420 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/random_forest/predictions.csv @@ -0,0 +1,42 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.454547619047619,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.5490158730158728,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.5656825396825395,1 +data/videos/fall/fall-06/fall-06-cam0.mp4#sample=0:40,1,0.7903333333333331,1 +data/videos/fall/fall-14/fall-14-cam0.mp4#sample=0:40,1,0.7514682539682539,1 +data/videos/fall/fall-19/fall-19-cam0.mp4#sample=0:40,1,0.5684722222222222,1 +data/videos/fall/fall-23/fall-23-cam0.mp4#sample=0:40,1,0.7104444444444443,1 +data/videos/fall/fall-27/fall-27-cam0.mp4#sample=0:40,1,0.5993571428571427,1 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=0:40,0,0.021190476190476194,0 +data/videos/normal/adl-01/adl-01-cam0.mp4#sample=10:50,0,0.0775,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.022793650793650793,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.054722222222222214,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.12342857142857142,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.14366666666666664,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.10826190476190475,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=0:40,0,0.005555555555555555,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=10:50,0,0.13499999999999998,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=20:60,0,0.2251984126984127,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=30:70,0,0.32544444444444454,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=40:80,0,0.42709523809523814,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=50:90,0,0.1773174603174603,0 +data/videos/normal/adl-10/adl-10-cam0.mp4#sample=60:100,0,0.20001190476190472,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=10:50,0,0.05765873015873017,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=20:60,0,0.06333333333333334,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=30:70,0,0.13238888888888886,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=40:80,0,0.19470634920634922,0 +data/videos/normal/adl-13/adl-13-cam0.mp4#sample=49:89,0,0.2077857142857143,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=10:50,0,0.047293650793650784,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=20:60,0,0.15166666666666664,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=30:70,0,0.2036111111111111,0 +data/videos/normal/adl-17/adl-17-cam0.mp4#sample=37:77,0,0.2175,0 +data/videos/normal/adl-26/adl-26-cam0.mp4#sample=0:40,0,0.1753174603174603,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=0:40,0,0.046,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=10:50,0,0.014166666666666666,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=20:60,0,0.025611111111111112,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=30:70,0,0.020277777777777777,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=40:80,0,0.05416666666666667,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=50:90,0,0.10492857142857144,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=60:100,0,0.12884126984126984,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=70:110,0,0.20839682539682539,0 +data/videos/normal/adl-37/adl-37-cam0.mp4#sample=77:117,0,0.3192460317460318,0 diff --git a/artifacts/experiments/urfd_repeated/seed_42/random_forest/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_42/random_forest/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7bfdc06161530ccc47edb187ded23f143f84115e Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_42/random_forest/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_42/results.csv b/artifacts/experiments/urfd_repeated/seed_42/results.csv new file mode 100644 index 0000000000000000000000000000000000000000..60a4faa7bdc9a61de9c88cdc1894e265af79f07d --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/results.csv @@ -0,0 +1,5 @@ +model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds +random_forest,0.975609756097561,1.0,0.875,1.0,0.9333333333333333,1.0,0.9882721559988568 +logistic_regression,0.9512195121951219,0.8,1.0,0.9393939393939394,0.8888888888888888,1.0,0.025282849004724994 +pose_tcn,0.9512195121951219,0.8,1.0,0.9393939393939394,0.8888888888888888,1.0,0.9513108629907947 +pose_gru,0.9024390243902439,0.7,0.875,0.9090909090909091,0.7777777777777778,0.9886363636363636,1.2036171649961034 diff --git a/artifacts/experiments/urfd_repeated/seed_42/splits.npz b/artifacts/experiments/urfd_repeated/seed_42/splits.npz new file mode 100644 index 0000000000000000000000000000000000000000..75d0c9c4fbec200f7df55400199b0e5b53f3acf9 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_42/splits.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58ae99f939839832f37f7bf92b537a1212d6f4833e0cc71ca371877e340726c3 +size 978 diff --git a/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..2bcf8f4af9de33afea6720fdbfee48dbdb628b7a Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/metrics.json b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..9b65740a5155866e1d52bc33017c3eac569f8dd6 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9791666666666666, + "precision": 1.0, + "recall": 0.875, + "specificity": 1.0, + "f1": 0.9333333333333333, + "roc_auc": 0.965625, + "threshold": 0.5, + "tn": 40, + "fp": 0, + "fn": 1, + "tp": 7, + "n_samples": 48, + "model": "logistic_regression", + "seed": 84, + "training_seconds": 0.021342011998058297, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 107, + "groups": 38, + "normal": 84, + "fall": 23 + }, + "validation": { + "samples": 45, + "groups": 13, + "normal": 40, + "fall": 5 + }, + "test": { + "samples": 48, + "groups": 13, + "normal": 40, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/model.joblib b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..b7936476e36a19506ca119e69ad50fc1a63d5561 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a77b6cbe659a9c6a8fa362507b1848d9bd7ac83179e5af90f2b02da3fd19b96e +size 21047 diff --git a/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/predictions.csv b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..b06e323e5fc551b06c3f0e5f5e152c137d49ae82 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/predictions.csv @@ -0,0 +1,49 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.7722377,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9792789,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.98074645,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=0:40,0,0.00018083297,0 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=10:50,1,0.86309034,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=20:60,1,0.99984527,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=22:62,1,0.99995744,1 +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.99997175,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.0022619255,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,1.773275e-05,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,6.4253705e-05,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.017066583,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.020642413,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.00038715583,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,3.7289763e-05,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,2.2228518e-05,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,2.8215341e-05,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,2.6744497e-06,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,2.6106682e-06,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,1.7025804e-05,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,9.831789e-07,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,1.8706561e-06,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,4.9835267e-06,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,1.0711688e-05,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,3.0699817e-07,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,4.644224e-07,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,1.5344359e-06,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,7.224731e-05,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.005726499,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.0026233864,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.008468014,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,1.4845006e-05,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,9.632668e-10,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,1.7429064e-10,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=10:50,0,5.6884693e-07,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=20:60,0,2.7558718e-07,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=30:70,0,9.4943325e-06,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=40:80,0,0.00010273761,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=50:90,0,0.10022594,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=60:100,0,0.10010418,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=70:110,0,0.07677974,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=74:114,0,0.06183098,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=0:40,0,1.0670881e-06,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=10:50,0,0.00015453303,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=20:60,0,0.0013715285,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=30:70,0,0.0008242166,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=40:80,0,0.0045462498,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=50:90,0,0.015686234,0 diff --git a/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..16d09aa8d9c8a9779564b82f24b73ef4a8edb97f Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/logistic_regression/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/model_comparison.png b/artifacts/experiments/urfd_repeated/seed_84/model_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..08bb85c0adf66208d5d08d977cddd3a78b0e7035 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/model_comparison.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_gru/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..d015e6d6ec81e821468c83903cfd49872b2c21b5 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_gru/metrics.json b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..b07690fef5429ab2f60f6dbea92acf25df161725 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9583333333333334, + "precision": 1.0, + "recall": 0.75, + "specificity": 1.0, + "f1": 0.8571428571428571, + "roc_auc": 1.0, + "threshold": 0.5, + "tn": 40, + "fp": 0, + "fn": 2, + "tp": 6, + "n_samples": 48, + "model": "pose_gru", + "architecture": "gru", + "seed": 84, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 29, + "best_val_loss": 0.013039302080869675, + "training_seconds": 1.1110608339949977, + "parameters": 79489, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 107, + "groups": 38, + "normal": 84, + "fall": 23 + }, + "validation": { + "samples": 45, + "groups": 13, + "normal": 40, + "fall": 5 + }, + "test": { + "samples": 48, + "groups": 13, + "normal": 40, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_gru/model.pt b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..9a6717ca721d07e6d603c6ccebb2a15d563650a6 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3d9e581bf5ab88a38c3efff1e148dd32a5ff6ea2e3d10b0a520068405d83f0b +size 325083 diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_gru/predictions.csv b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..bda4f6aab6cf8b065c04ee739e46a1fda01af2d0 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/predictions.csv @@ -0,0 +1,49 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.25387654,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.97901046,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.9891305,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=0:40,0,0.05554685,0 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=10:50,1,0.8006569,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=20:60,1,0.9984884,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=22:62,1,0.99871325,1 +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.9989851,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.32012036,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.00069959747,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.0009941629,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.0051564593,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.04260116,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.05902606,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,0.0007764276,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,0.0010600404,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,0.0020516573,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,0.00068369263,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,0.00084447855,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,0.0013929297,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,0.00045979675,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,0.00050360884,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,0.0005106165,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,0.0006725409,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,0.00061690604,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,0.000424554,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,0.0006844345,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.0024590774,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.0058787605,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.0031935682,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.0026880018,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,0.0018468834,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,0.0036376938,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,0.0033970205,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=10:50,0,0.0008159202,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=20:60,0,0.0009615637,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=30:70,0,0.001399014,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=40:80,0,0.00089886045,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=50:90,0,0.002113528,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=60:100,0,0.010368914,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=70:110,0,0.038303204,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=74:114,0,0.09022932,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=0:40,0,0.0020833495,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=10:50,0,0.0012295371,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=20:60,0,0.0019861518,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=30:70,0,0.003193533,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=40:80,0,0.0037885895,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=50:90,0,0.0071395976,0 diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_gru/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..7d058576bda3f01aa97ac89992f51f7dde55fe42 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_gru/training_curve.png b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..f54b90484cbb46fed38a9ff4c4f962d55ed75fbe Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/pose_gru/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..6b0028e66180647582f7ba0c29866accf1525382 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/metrics.json b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..a4e6639a70409f4eb78ca596152f868de7fcdd8d --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/metrics.json @@ -0,0 +1,47 @@ +{ + "accuracy": 0.9791666666666666, + "precision": 1.0, + "recall": 0.875, + "specificity": 1.0, + "f1": 0.9333333333333333, + "roc_auc": 0.9937499999999999, + "threshold": 0.5, + "tn": 40, + "fp": 0, + "fn": 1, + "tp": 7, + "n_samples": 48, + "model": "pose_tcn", + "architecture": "tcn", + "seed": 84, + "device": "cuda", + "device_name": "NVIDIA GeForce RTX 4090", + "torch_version": "2.11.0+cu128", + "torch_cuda_version": "12.8", + "epochs_completed": 50, + "best_val_loss": 0.03900789935141802, + "training_seconds": 1.3381747899984475, + "parameters": 12929, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 107, + "groups": 38, + "normal": 84, + "fall": 23 + }, + "validation": { + "samples": 45, + "groups": 13, + "normal": 40, + "fall": 5 + }, + "test": { + "samples": 48, + "groups": 13, + "normal": 40, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/model.pt b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..1f708aebb36d0609854e44d58c4311705f6ab95b --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/model.pt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef26943036b986d2c751b3117edf683048bcf33289fd354d8d52456b1dbf85e0 +size 58759 diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/predictions.csv b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..19e44401881a97fa035560c38ef459405cff8101 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/predictions.csv @@ -0,0 +1,49 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.95702803,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.9637765,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.971574,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=0:40,0,0.31287956,0 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=10:50,1,0.9946144,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=20:60,1,0.99915934,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=22:62,1,0.9993709,1 +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.99957603,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.18557738,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.03345726,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.068957455,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.09088661,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.0807349,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.029492809,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,0.0066711777,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,0.037437886,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,0.031116633,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,0.016135832,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,0.032222472,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,0.034633175,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,0.019554973,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,0.05920194,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,0.07727339,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,0.070522584,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,0.012725111,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,0.0020270555,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,0.018642152,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.02794699,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.0189673,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.02153536,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.086185835,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,0.06615481,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,7.667763e-16,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,1.792633e-25,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=10:50,0,0.008451974,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=20:60,0,0.080828324,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=30:70,0,0.10312868,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=40:80,0,0.045993503,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=50:90,0,0.061115414,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=60:100,0,0.06682151,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=70:110,0,0.041023765,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=74:114,0,0.12364113,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=0:40,0,0.010540259,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=10:50,0,0.011457292,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=20:60,0,0.033112783,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=30:70,0,0.016741846,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=40:80,0,0.014043185,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=50:90,0,0.28175288,0 diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..24fe8fa6575a3ba9a60f4f1d22217dae7007afcc Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/training_curve.png b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/training_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..574603920f7e818dddb0b4d587ff8b577e9b82ad Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/pose_tcn/training_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/random_forest/confusion_matrix.png b/artifacts/experiments/urfd_repeated/seed_84/random_forest/confusion_matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..a18151bf52f28984dfabfe8bf0d96a21c0defa62 Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/random_forest/confusion_matrix.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/random_forest/metrics.json b/artifacts/experiments/urfd_repeated/seed_84/random_forest/metrics.json new file mode 100644 index 0000000000000000000000000000000000000000..ec217b8de1fd5f46794be2794077a872c3e58d31 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/random_forest/metrics.json @@ -0,0 +1,39 @@ +{ + "accuracy": 0.9583333333333334, + "precision": 1.0, + "recall": 0.75, + "specificity": 1.0, + "f1": 0.8571428571428571, + "roc_auc": 0.984375, + "threshold": 0.395, + "tn": 40, + "fp": 0, + "fn": 2, + "tp": 6, + "n_samples": 48, + "model": "random_forest", + "seed": 84, + "training_seconds": 0.9982958710024832, + "dataset": "data/processed/urfd_pose.npz", + "split": { + "train": { + "samples": 107, + "groups": 38, + "normal": 84, + "fall": 23 + }, + "validation": { + "samples": 45, + "groups": 13, + "normal": 40, + "fall": 5 + }, + "test": { + "samples": 48, + "groups": 13, + "normal": 40, + "fall": 8 + } + }, + "result_scope": "test set only" +} \ No newline at end of file diff --git a/artifacts/experiments/urfd_repeated/seed_84/random_forest/model.joblib b/artifacts/experiments/urfd_repeated/seed_84/random_forest/model.joblib new file mode 100644 index 0000000000000000000000000000000000000000..20bc165fa6921c814d1ca8963cc7aa68b0d74b87 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/random_forest/model.joblib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:929c2578e73a7543b9a43f11e3c899c7f3cf67b297a671d2db26e5fa2538db66 +size 348851 diff --git a/artifacts/experiments/urfd_repeated/seed_84/random_forest/predictions.csv b/artifacts/experiments/urfd_repeated/seed_84/random_forest/predictions.csv new file mode 100644 index 0000000000000000000000000000000000000000..59d444ea41a33a71fe228c56bfcfa25da32bb351 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/random_forest/predictions.csv @@ -0,0 +1,49 @@ +source,label,probability_fall,prediction +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=0:40,1,0.3523888888888889,0 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=10:50,1,0.5555701058201058,1 +data/videos/fall/fall-01/fall-01-cam0.mp4#sample=14:54,1,0.5900145502645502,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=0:40,0,0.10555555555555554,0 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=10:50,1,0.6591111111111111,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=20:60,1,0.9293333333333332,1 +data/videos/fall/fall-09/fall-09-cam0.mp4#sample=22:62,1,0.9431111111111109,1 +data/videos/fall/fall-12/fall-12-cam0.mp4#sample=0:40,1,0.9213333333333332,1 +data/videos/fall/fall-13/fall-13-cam0.mp4#sample=0:40,1,0.2953015873015873,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=0:40,0,0.02851851851851852,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=10:50,0,0.08544444444444445,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=20:60,0,0.12944444444444442,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=30:70,0,0.14294444444444446,0 +data/videos/normal/adl-06/adl-06-cam0.mp4#sample=37:77,0,0.13361111111111112,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=0:40,0,0.04976851851851852,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=10:50,0,0.09476851851851853,0 +data/videos/normal/adl-07/adl-07-cam0.mp4#sample=20:60,0,0.12999074074074074,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=0:40,0,0.021296296296296296,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=10:50,0,0.06332407407407406,0 +data/videos/normal/adl-08/adl-08-cam0.mp4#sample=20:60,0,0.1283240740740741,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=20:60,0,0.02687962962962963,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=30:70,0,0.02087962962962963,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=40:80,0,0.030879629629629632,0 +data/videos/normal/adl-20/adl-20-cam0.mp4#sample=50:90,0,0.07161111111111111,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=0:40,0,0.0033333333333333335,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=10:50,0,0.006666666666666667,0 +data/videos/normal/adl-23/adl-23-cam0.mp4#sample=20:60,0,0.011666666666666667,0 +data/videos/normal/adl-25/adl-25-cam0.mp4#sample=0:40,0,0.0475,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=0:40,0,0.14155555555555555,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=10:50,0,0.12399999999999999,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=20:60,0,0.12388888888888888,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=30:70,0,0.15549074074074073,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=40:80,0,0.16962566137566137,0 +data/videos/normal/adl-31/adl-31-cam0.mp4#sample=44:84,0,0.13879232804232805,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=10:50,0,0.02944444444444444,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=20:60,0,0.017222222222222222,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=30:70,0,0.04091269841269842,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=40:80,0,0.043333333333333335,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=50:90,0,0.3343333333333333,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=60:100,0,0.329,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=70:110,0,0.38761111111111113,0 +data/videos/normal/adl-36/adl-36-cam0.mp4#sample=74:114,0,0.3491111111111112,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=0:40,0,0.018333333333333333,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=10:50,0,0.036000000000000004,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=20:60,0,0.05738888888888889,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=30:70,0,0.06655555555555556,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=40:80,0,0.07661111111111112,0 +data/videos/normal/adl-39/adl-39-cam0.mp4#sample=50:90,0,0.09183333333333334,0 diff --git a/artifacts/experiments/urfd_repeated/seed_84/random_forest/roc_curve.png b/artifacts/experiments/urfd_repeated/seed_84/random_forest/roc_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..6a44d583ab7ff7ee30453d5dbb5b0afb2776a3ed Binary files /dev/null and b/artifacts/experiments/urfd_repeated/seed_84/random_forest/roc_curve.png differ diff --git a/artifacts/experiments/urfd_repeated/seed_84/results.csv b/artifacts/experiments/urfd_repeated/seed_84/results.csv new file mode 100644 index 0000000000000000000000000000000000000000..63e90ae5d88b3e267503de4401ea3dafedbe373f --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/results.csv @@ -0,0 +1,5 @@ +model,accuracy,precision,recall,specificity,f1,roc_auc,training_seconds +logistic_regression,0.9791666666666666,1.0,0.875,1.0,0.9333333333333333,0.965625,0.021342011998058297 +pose_tcn,0.9791666666666666,1.0,0.875,1.0,0.9333333333333333,0.9937499999999999,1.3381747899984475 +pose_gru,0.9583333333333334,1.0,0.75,1.0,0.8571428571428571,1.0,1.1110608339949977 +random_forest,0.9583333333333334,1.0,0.75,1.0,0.8571428571428571,0.984375,0.9982958710024832 diff --git a/artifacts/experiments/urfd_repeated/seed_84/splits.npz b/artifacts/experiments/urfd_repeated/seed_84/splits.npz new file mode 100644 index 0000000000000000000000000000000000000000..b7d580bc4a04140abb98c25703676a2b00264072 --- /dev/null +++ b/artifacts/experiments/urfd_repeated/seed_84/splits.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80ae57fd5770bd8b0fa2c18308ccb02adb7d9fd6fde77281d228ed2b7b5c5c4c +size 971 diff --git a/configs/default.yaml b/configs/default.yaml new file mode 100644 index 0000000000000000000000000000000000000000..74651d018625ba0d76fe13e94f5adbf2435e4cf0 --- /dev/null +++ b/configs/default.yaml @@ -0,0 +1,30 @@ +seed: 42 + +data: + video_dir: data/videos + dataset_path: data/processed/urfd_pose.npz + sequence_length: 40 + stride: 10 + target_fps: 10 + min_valid_pose_ratio: 0.60 + visibility_threshold: 0.35 + test_size: 0.20 + val_size: 0.20 + +model: + hidden_size: 64 + num_layers: 1 + dropout: 0.20 + tcn_channels: 32 + +training: + epochs: 50 + batch_size: 32 + learning_rate: 0.001 + weight_decay: 0.0001 + patience: 8 + threshold: auto + +demo: + smoothing_window: 5 + alert_consecutive: 3 diff --git a/data/README.md b/data/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b3c4e0df8612ecb1977bc3d0a675ff331677985f --- /dev/null +++ b/data/README.md @@ -0,0 +1,22 @@ +# Dữ liệu + +## UR Fall Detection Dataset + +Chạy `python scripts/download_urfd.py`. Script tải video RGB camera 0 và annotation từ máy chủ chính thức của University of Rzeszów. Dữ liệu có 30 chuỗi té ngã và 40 chuỗi hoạt động thường ngày. + +Video preview URFD ghép ảnh độ sâu ở nửa trái và RGB ở nửa phải. `prepare_dataset.py` nhận dạng khung hình có tỷ lệ rộng/cao trên 2,2 và chỉ dùng nửa RGB để trích pose. + +Giấy phép: Creative Commons Attribution-NonCommercial-ShareAlike 4.0, chỉ dùng phi thương mại/học thuật. Không commit hoặc phân phối lại video trong repository này. + +Nguồn: + +Trích dẫn: + +> B. Kwolek and M. Kępski, “Human fall detection on embedded platform using depth maps and wireless accelerometer,” Computer Methods and Programs in Biomedicine, vol. 117, no. 3, pp. 489–501, 2014. DOI: 10.1016/j.cmpb.2014.09.005. + +## Định dạng `.npz` + +- `poses`: `[N, T, 33, 4]`, gồm x, y, z, visibility. +- `labels`: `[N]`, 0 là bình thường, 1 là té/ngã. +- `groups`: `[N]`, mã video hoặc mã người dùng để chia tập. +- `sources`: `[N]`, đường dẫn và vị trí cửa sổ để truy vết. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..90da63dfbd61fe104323b284dc98b11829f3d9f5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[build-system] +requires = ["setuptools>=69"] +build-backend = "setuptools.build_meta" + +[project] +name = "pose-fall-detection" +version = "1.0.0" +description = "Phat hien te nga tu video dua tren chuoi diem khop co the" +requires-python = ">=3.10,<3.13" +dependencies = [ + "joblib>=1.3,<2", + "matplotlib>=3.8,<4", + "mediapipe==0.10.21", + "numpy>=1.26,<3", + "opencv-python-headless>=4.9,<5", + "pandas>=2.1,<3", + "PyYAML>=6,<7", + "scikit-learn>=1.4,<2", + "seaborn>=0.13,<1", + "torch>=2.2,<3", + "tqdm>=4.66,<5", +] + +[project.optional-dependencies] +dev = ["pytest>=8,<9"] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/scripts/compare_models.py b/scripts/compare_models.py new file mode 100644 index 0000000000000000000000000000000000000000..d54d97694c0d2e7a1b71c836e11026bceb981acd --- /dev/null +++ b/scripts/compare_models.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd + + +def main() -> None: + parser = argparse.ArgumentParser(description="Compare experiment metrics") + parser.add_argument("--input", type=Path, default=Path("artifacts/experiments/urfd")) + args = parser.parse_args() + rows = [] + for path in sorted(args.input.glob("*/metrics.json")): + with path.open(encoding="utf-8") as file: + metrics = json.load(file) + rows.append( + { + "model": metrics["model"], + "accuracy": metrics["accuracy"], + "precision": metrics["precision"], + "recall": metrics["recall"], + "specificity": metrics["specificity"], + "f1": metrics["f1"], + "roc_auc": metrics["roc_auc"], + "training_seconds": metrics["training_seconds"], + } + ) + if not rows: + raise SystemExit(f"No metrics.json files found under {args.input}") + results = pd.DataFrame(rows).sort_values("f1", ascending=False) + results.to_csv(args.input / "results.csv", index=False) + results.set_index("model")[["accuracy", "precision", "recall", "specificity", "f1"]].plot( + kind="bar", figsize=(9, 5), ylim=(0, 1.05) + ) + plt.ylabel("Score") + plt.xlabel("Model") + plt.title("So sanh ket qua tren tap kiem tra") + plt.xticks(rotation=0) + plt.legend(loc="lower right", ncol=2) + plt.tight_layout() + plt.savefig(args.input / "model_comparison.png", dpi=180) + plt.close() + print(results.to_string(index=False, float_format=lambda value: f"{value:.4f}")) + + +if __name__ == "__main__": + main() + diff --git a/scripts/demo_video.py b/scripts/demo_video.py new file mode 100644 index 0000000000000000000000000000000000000000..8105ab78083e5914724b5a953ada13f9ffbca443 --- /dev/null +++ b/scripts/demo_video.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from collections import deque +from pathlib import Path + +import cv2 +import numpy as np +import torch +from torch import nn + +from fall_detection.features import normalize_pose_sequence +from fall_detection.models import PoseGRU, PoseTCN +from fall_detection.pose_extractor import MediaPipePoseExtractor + +CONNECTIONS = [ + (11, 12), (11, 13), (13, 15), (12, 14), (14, 16), + (11, 23), (12, 24), (23, 24), (23, 25), (25, 27), + (24, 26), (26, 28), (27, 31), (28, 32), +] + + +def draw_pose(frame: np.ndarray, pose: np.ndarray | None) -> None: + if pose is None: + return + height, width = frame.shape[:2] + for first, second in CONNECTIONS: + if min(pose[first, 3], pose[second, 3]) < 0.35: + continue + p1 = (int(pose[first, 0] * width), int(pose[first, 1] * height)) + p2 = (int(pose[second, 0] * width), int(pose[second, 1] * height)) + cv2.line(frame, p1, p2, (70, 230, 70), 2) + for point in pose: + if point[3] >= 0.35: + cv2.circle(frame, (int(point[0] * width), int(point[1] * height)), 2, (0, 255, 255), -1) + + +def load_model(path: Path, device: torch.device) -> tuple[nn.Module, dict]: + checkpoint = torch.load(path, map_location=device, weights_only=False) + if checkpoint.get("architecture", "gru") == "tcn": + model = PoseTCN( + input_size=checkpoint["input_size"], + channels=checkpoint["tcn_channels"], + dropout=checkpoint["dropout"], + ).to(device) + else: + model = PoseGRU( + input_size=checkpoint["input_size"], + hidden_size=checkpoint["hidden_size"], + num_layers=checkpoint["num_layers"], + dropout=checkpoint["dropout"], + ).to(device) + model.load_state_dict(checkpoint["state_dict"]) + model.eval() + return model, checkpoint + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run fall detection on a video") + parser.add_argument("--input", type=Path, required=True) + parser.add_argument("--model", type=Path, default=Path("artifacts/experiments/urfd/pose_tcn/model.pt")) + parser.add_argument("--output", type=Path) + parser.add_argument("--target-fps", type=float, default=10.0) + parser.add_argument("--smoothing", type=int, default=5) + parser.add_argument("--alert-consecutive", type=int, default=3) + args = parser.parse_args() + output = args.output or args.input.with_name(args.input.stem + "_detected.mp4") + output.parent.mkdir(parents=True, exist_ok=True) + + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + model, checkpoint = load_model(args.model, device) + sequence_length = int(checkpoint["sequence_length"]) + feature_mean = np.asarray(checkpoint["feature_mean"], dtype=np.float32) + feature_std = np.asarray(checkpoint["feature_std"], dtype=np.float32) + threshold = float(checkpoint["threshold"]) + + capture = cv2.VideoCapture(str(args.input)) + if not capture.isOpened(): + raise SystemExit(f"Cannot open {args.input}") + source_fps = capture.get(cv2.CAP_PROP_FPS) or 25.0 + width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) + writer = cv2.VideoWriter( + str(output), cv2.VideoWriter_fourcc(*"mp4v"), source_fps, (width, height) + ) + sample_every = max(1, int(round(source_fps / args.target_fps))) + pose_window: deque[np.ndarray] = deque(maxlen=sequence_length) + probability_window: deque[float] = deque(maxlen=args.smoothing) + probability = 0.0 + consecutive = 0 + frame_index = 0 + + with MediaPipePoseExtractor() as extractor: + while True: + ok, frame = capture.read() + if not ok: + break + current_pose = None + display_pose = None + if frame_index % sample_every == 0: + is_wide_urfd = frame.shape[1] / frame.shape[0] > 2.2 + pose_frame = frame[:, frame.shape[1] // 2 :] if is_wide_urfd else frame + current_pose = extractor.process_rgb(cv2.cvtColor(pose_frame, cv2.COLOR_BGR2RGB)) + if current_pose is None: + current_pose = pose_window[-1].copy() if pose_window else np.zeros((33, 4), dtype=np.float32) + pose_window.append(current_pose) + display_pose = current_pose.copy() + if is_wide_urfd: + display_pose[:, 0] = 0.5 + 0.5 * display_pose[:, 0] + if len(pose_window) >= sequence_length: + features = normalize_pose_sequence( + np.stack(pose_window), checkpoint["visibility_threshold"] + ) + feature_indices = np.asarray( + checkpoint.get("feature_indices", np.arange(features.shape[1])), + dtype=np.int64, + ) + features = features[:, feature_indices] + features = (features - feature_mean) / feature_std + tensor = torch.from_numpy(features[None]).float().to(device) + with torch.no_grad(): + probability = float(torch.sigmoid(model(tensor)).item()) + probability_window.append(probability) + probability = float(np.mean(probability_window)) + consecutive = consecutive + 1 if probability >= threshold else 0 + + draw_pose(frame, display_pose) + alert = consecutive >= args.alert_consecutive + color = (0, 0, 255) if alert else (0, 180, 0) + status = "CANH BAO: TE NGA" if alert else "BINH THUONG" + cv2.rectangle(frame, (10, 10), (385, 78), (0, 0, 0), -1) + cv2.putText(frame, status, (20, 38), cv2.FONT_HERSHEY_SIMPLEX, 0.72, color, 2) + cv2.putText( + frame, + f"P(fall)={probability:.2f} threshold={threshold:.2f}", + (20, 66), + cv2.FONT_HERSHEY_SIMPLEX, + 0.50, + (255, 255, 255), + 1, + ) + writer.write(frame) + frame_index += 1 + + capture.release() + writer.release() + print(f"Saved annotated video to {output}") + + +if __name__ == "__main__": + main() diff --git a/scripts/download_urfd.py b/scripts/download_urfd.py new file mode 100644 index 0000000000000000000000000000000000000000..01d802ae8b7d5b6ed0c10e4f1f407b6e66d49bd2 --- /dev/null +++ b/scripts/download_urfd.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from pathlib import Path +from urllib.request import urlretrieve + +from tqdm import tqdm + +BASE_URL = "https://fenix.ur.edu.pl/~mkepski/ds/data" + + +def download(url: str, destination: Path) -> None: + destination.parent.mkdir(parents=True, exist_ok=True) + if destination.exists() and destination.stat().st_size > 0: + return + temporary = destination.with_suffix(destination.suffix + ".part") + urlretrieve(url, temporary) + temporary.replace(destination) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Download official URFD RGB videos") + parser.add_argument("--output", type=Path, default=Path("data/videos")) + args = parser.parse_args() + + items: list[tuple[str, Path]] = [] + for index in range(1, 31): + name = f"fall-{index:02d}-cam0.mp4" + items.append((f"{BASE_URL}/{name}", args.output / "fall" / f"fall-{index:02d}" / name)) + for index in range(1, 41): + name = f"adl-{index:02d}-cam0.mp4" + items.append((f"{BASE_URL}/{name}", args.output / "normal" / f"adl-{index:02d}" / name)) + items.extend( + [ + ( + f"{BASE_URL}/urfall-cam0-falls.csv", + args.output / "annotations" / "urfall-cam0-falls.csv", + ), + ( + f"{BASE_URL}/urfall-cam0-adls.csv", + args.output / "annotations" / "urfall-cam0-adls.csv", + ), + ] + ) + for url, destination in tqdm(items, desc="Downloading URFD"): + download(url, destination) + print(f"Downloaded/verified {len(items)} files in {args.output}") + print("License: CC BY-NC-SA 4.0, academic non-commercial use.") + + +if __name__ == "__main__": + main() + diff --git a/scripts/prepare_dataset.py b/scripts/prepare_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..0995eac7f9d99e56150fe101a36ff30c7d66555f --- /dev/null +++ b/scripts/prepare_dataset.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +from pathlib import Path + +import numpy as np +import pandas as pd +from tqdm import tqdm + +from fall_detection.config import load_config +from fall_detection.pose_extractor import extract_video, pad_window + +VIDEO_EXTENSIONS = {".mp4", ".avi", ".mov", ".mkv"} + + +def load_urfd_annotations(video_dir: Path) -> dict[str, dict[int, int]]: + annotations: dict[str, dict[int, int]] = {} + for path in (video_dir / "annotations").glob("*.csv"): + frame = pd.read_csv(path, header=None, usecols=[0, 1, 2]) + frame.columns = ["sequence", "frame", "posture"] + for sequence, rows in frame.groupby("sequence"): + annotations[str(sequence)] = dict( + zip(rows["frame"].astype(int), rows["posture"].astype(int)) + ) + return annotations + + +def window_label( + class_name: str, + sequence_name: str, + frame_numbers: np.ndarray, + annotations: dict[str, dict[int, int]], +) -> int: + if class_name == "normal": + return 0 + if sequence_name not in annotations: + return 1 + mapping = annotations[sequence_name] + postures = np.asarray([mapping.get(int(frame), -1) for frame in frame_numbers]) + # 0 is the falling transition and 1 is lying after the fall. + return int(np.mean(postures >= 0) >= 0.15) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Extract MediaPipe pose windows") + parser.add_argument("--config", default="configs/default.yaml") + parser.add_argument("--input", type=Path) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + + config = load_config(args.config) + data_config = config["data"] + video_dir = args.input or Path(data_config["video_dir"]) + output = args.output or Path(data_config["dataset_path"]) + length = int(data_config["sequence_length"]) + stride = int(data_config["stride"]) + min_valid_ratio = float(data_config["min_valid_pose_ratio"]) + target_fps = float(data_config["target_fps"]) + annotations = load_urfd_annotations(video_dir) + + videos = sorted( + path for path in video_dir.glob("*/*/*") if path.suffix.lower() in VIDEO_EXTENSIONS + ) + if not videos: + raise SystemExit( + f"No videos found in {video_dir}/{{fall,normal}}//. " + "Run scripts/download_urfd.py first." + ) + + poses: list[np.ndarray] = [] + labels: list[int] = [] + groups: list[str] = [] + sources: list[str] = [] + skipped = 0 + for video_path in tqdm(videos, desc="Extracting poses"): + class_name = video_path.relative_to(video_dir).parts[0] + if class_name not in {"fall", "normal"}: + continue + group = video_path.parent.name + sequence_name = group + extracted = extract_video(video_path, target_fps=target_fps) + max_start = max(len(extracted.poses) - length, 0) + starts = list(range(0, max_start + 1, stride)) or [0] + if starts[-1] != max_start: + starts.append(max_start) + + for start in starts: + stop = start + length + window = pad_window(extracted.poses[start:stop], length) + frame_numbers = extracted.frame_numbers[start:stop] + valid_ratio = float(np.mean(window[:, :, 3].max(axis=1) > 0)) + if valid_ratio < min_valid_ratio: + skipped += 1 + continue + poses.append(window) + labels.append( + window_label( + class_name, sequence_name, frame_numbers, annotations + ) + ) + groups.append(group) + sources.append(f"{video_path.as_posix()}#sample={start}:{stop}") + + if not poses: + raise SystemExit("All windows were skipped because pose detection failed") + output.parent.mkdir(parents=True, exist_ok=True) + np.savez_compressed( + output, + poses=np.stack(poses).astype(np.float32), + labels=np.asarray(labels, dtype=np.int64), + groups=np.asarray(groups), + sources=np.asarray(sources), + ) + unique, counts = np.unique(labels, return_counts=True) + distribution = dict(zip(unique.tolist(), counts.tolist())) + print(f"Saved {len(poses)} windows to {output}") + print(f"Class distribution {distribution}; skipped {skipped} low-visibility windows") + + +if __name__ == "__main__": + main() + diff --git a/scripts/run_all.py b/scripts/run_all.py new file mode 100644 index 0000000000000000000000000000000000000000..ee59f0d4a31a0567d86d6caf205dc3c22f213100 --- /dev/null +++ b/scripts/run_all.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import subprocess +import sys +from pathlib import Path + + +def run(command: list[str]) -> None: + print("\n$", " ".join(command), flush=True) + subprocess.run(command, check=True) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Run the complete experiment") + parser.add_argument("--dataset", default="data/processed/urfd_pose.npz") + parser.add_argument("--config", default="configs/default.yaml") + parser.add_argument("--output", default="artifacts/experiments/urfd") + parser.add_argument("--seed", type=int) + parser.add_argument("--device", choices=["auto", "cpu", "cuda"], default="auto") + args = parser.parse_args() + python = sys.executable + dataset = args.dataset + output = args.output + if not Path(dataset).exists(): + run([python, "scripts/download_urfd.py"]) + run([python, "scripts/prepare_dataset.py", "--config", args.config, "--output", dataset]) + common = ["--dataset", dataset, "--config", args.config, "--output", output] + if args.seed is not None: + common.extend(["--seed", str(args.seed)]) + run([python, "scripts/train_baselines.py", *common]) + run([python, "scripts/train_gru.py", *common, "--device", args.device]) + run([python, "scripts/train_tcn.py", *common, "--device", args.device]) + run([python, "scripts/compare_models.py", "--input", output]) + + +if __name__ == "__main__": + main() diff --git a/scripts/run_repeated_experiments.py b/scripts/run_repeated_experiments.py new file mode 100644 index 0000000000000000000000000000000000000000..5fdf0b968834003e4301a3c179ae805ae59648c6 --- /dev/null +++ b/scripts/run_repeated_experiments.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd + +METRICS = ["accuracy", "precision", "recall", "specificity", "f1", "roc_auc"] + + +def run(command: list[str]) -> None: + print("\n$", " ".join(command), flush=True) + subprocess.run(command, check=True) + + +def collect_results(output: Path, seeds: list[int]) -> pd.DataFrame: + rows = [] + for seed in seeds: + run_dir = output / f"seed_{seed}" + for metrics_path in sorted(run_dir.glob("*/metrics.json")): + with metrics_path.open(encoding="utf-8") as file: + metrics = json.load(file) + row = {"seed": seed, "model": metrics["model"]} + row.update({metric: metrics[metric] for metric in METRICS}) + row["training_seconds"] = metrics["training_seconds"] + row["device"] = metrics.get("device", "cpu") + rows.append(row) + return pd.DataFrame(rows) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Repeat real-data experiments over multiple split/training seeds" + ) + parser.add_argument("--dataset", default="data/processed/urfd_pose.npz") + parser.add_argument("--config", default="configs/default.yaml") + parser.add_argument( + "--output", type=Path, default=Path("artifacts/experiments/urfd_repeated") + ) + parser.add_argument("--seeds", type=int, nargs="+", default=[13, 21, 42, 84, 123]) + parser.add_argument("--device", choices=["auto", "cpu", "cuda"], default="auto") + parser.add_argument( + "--skip-existing", action="store_true", help="Reuse a seed if all four metrics files exist" + ) + args = parser.parse_args() + if not Path(args.dataset).exists(): + raise SystemExit( + f"Real dataset not found: {args.dataset}. Run scripts/download_urfd.py and " + "scripts/prepare_dataset.py first." + ) + args.output.mkdir(parents=True, exist_ok=True) + python = sys.executable + + for seed in args.seeds: + run_dir = args.output / f"seed_{seed}" + expected = [ + run_dir / model / "metrics.json" + for model in ("logistic_regression", "random_forest", "pose_gru", "pose_tcn") + ] + if args.skip_existing and all(path.exists() for path in expected): + print(f"Reusing complete run for seed {seed}") + continue + common = [ + "--dataset", args.dataset, + "--config", args.config, + "--output", str(run_dir), + "--seed", str(seed), + ] + run([python, "scripts/train_baselines.py", *common]) + run([python, "scripts/train_gru.py", *common, "--device", args.device]) + run([python, "scripts/train_tcn.py", *common, "--device", args.device]) + run([python, "scripts/compare_models.py", "--input", str(run_dir)]) + + per_run = collect_results(args.output, args.seeds) + expected_rows = len(args.seeds) * 4 + if len(per_run) != expected_rows: + raise RuntimeError(f"Expected {expected_rows} result rows, found {len(per_run)}") + per_run.to_csv(args.output / "per_run_results.csv", index=False) + + aggregate = per_run.groupby("model")[METRICS + ["training_seconds"]].agg( + ["mean", "std", "min", "max"] + ) + aggregate.columns = [f"{metric}_{stat}" for metric, stat in aggregate.columns] + aggregate = aggregate.reset_index().sort_values("f1_mean", ascending=False) + aggregate.to_csv(args.output / "aggregate_results.csv", index=False) + + plot_data = per_run.pivot(index="seed", columns="model", values="f1") + means = plot_data.mean().sort_values(ascending=False) + stds = plot_data.std().reindex(means.index) + plt.figure(figsize=(8, 5)) + plt.bar(means.index, means.values, yerr=stds.values, capsize=5) + plt.ylim(0, 1.05) + plt.ylabel("F1 (mean ± standard deviation)") + plt.xlabel("Model") + plt.title(f"URFD repeated experiment ({len(args.seeds)} seeds)") + plt.xticks(rotation=10) + plt.tight_layout() + plt.savefig(args.output / "aggregate_f1.png", dpi=180) + plt.close() + + print("\nPer-run results:") + print(per_run.to_string(index=False, float_format=lambda value: f"{value:.4f}")) + print("\nAggregate results:") + display_columns = ["model"] + [f"{metric}_{stat}" for metric in METRICS for stat in ("mean", "std")] + print(aggregate[display_columns].to_string(index=False, float_format=lambda value: f"{value:.4f}")) + + +if __name__ == "__main__": + main() diff --git a/scripts/train_baselines.py b/scripts/train_baselines.py new file mode 100644 index 0000000000000000000000000000000000000000..d9d941cbf39ad4615596da46e28c26de66163113 --- /dev/null +++ b/scripts/train_baselines.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import time +from pathlib import Path + +import joblib +import numpy as np +from sklearn.ensemble import RandomForestClassifier +from sklearn.linear_model import LogisticRegression +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import StandardScaler + +from fall_detection.experiment import prepare_experiment_data, split_summary +from fall_detection.features import summarize_sequence_features +from fall_detection.metrics import ( + choose_f1_threshold, + classification_metrics, + save_evaluation_plots, + save_predictions, +) +from fall_detection.utils import set_seed, write_json + + +def main() -> None: + parser = argparse.ArgumentParser(description="Train classical pose baselines") + parser.add_argument("--dataset", default="data/processed/urfd_pose.npz") + parser.add_argument("--config", default="configs/default.yaml") + parser.add_argument("--output", type=Path, default=Path("artifacts/experiments/urfd")) + parser.add_argument("--seed", type=int) + args = parser.parse_args() + + config, dataset, sequence_features, splits = prepare_experiment_data( + args.dataset, args.config, args.output, seed=args.seed + ) + set_seed(config["seed"]) + features = summarize_sequence_features(sequence_features) + labels = dataset["labels"] + models = { + "logistic_regression": Pipeline( + [ + ("scaler", StandardScaler()), + ( + "classifier", + LogisticRegression( + C=1.0, + max_iter=2000, + class_weight="balanced", + random_state=config["seed"], + ), + ), + ] + ), + "random_forest": RandomForestClassifier( + n_estimators=300, + max_depth=10, + min_samples_leaf=2, + class_weight="balanced", + n_jobs=-1, + random_state=config["seed"], + ), + } + + for name, model in models.items(): + output_dir = args.output / name + output_dir.mkdir(parents=True, exist_ok=True) + started = time.perf_counter() + model.fit(features[splits.train], labels[splits.train]) + training_seconds = time.perf_counter() - started + val_probabilities = model.predict_proba(features[splits.val])[:, 1] + threshold = choose_f1_threshold(labels[splits.val], val_probabilities) + test_probabilities = model.predict_proba(features[splits.test])[:, 1] + metrics = classification_metrics(labels[splits.test], test_probabilities, threshold) + metrics.update( + { + "model": name, + "seed": int(config["seed"]), + "training_seconds": training_seconds, + "dataset": str(args.dataset), + "split": split_summary(labels, dataset["groups"], splits), + "result_scope": "test set only", + } + ) + joblib.dump( + { + "model": model, + "threshold": threshold, + "sequence_length": int(sequence_features.shape[1]), + "visibility_threshold": config["data"]["visibility_threshold"], + }, + output_dir / "model.joblib", + ) + write_json(output_dir / "metrics.json", metrics) + save_predictions( + output_dir / "predictions.csv", + labels[splits.test], + test_probabilities, + dataset["sources"][splits.test], + threshold, + ) + save_evaluation_plots( + labels[splits.test], test_probabilities, threshold, output_dir, name + ) + print( + f"{name}: F1={metrics['f1']:.3f}, recall={metrics['recall']:.3f}, " + f"specificity={metrics['specificity']:.3f}, AUC={metrics['roc_auc']:.3f}" + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/train_gru.py b/scripts/train_gru.py new file mode 100644 index 0000000000000000000000000000000000000000..d978ed6f806d6aa1b32d700a995d99646cb80d0d --- /dev/null +++ b/scripts/train_gru.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import copy +import time +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import torch +from torch import nn +from torch.utils.data import DataLoader, TensorDataset + +from fall_detection.experiment import prepare_experiment_data, split_summary +from fall_detection.features import CORE_FEATURE_INDICES +from fall_detection.metrics import ( + choose_f1_threshold, + classification_metrics, + save_evaluation_plots, + save_predictions, +) +from fall_detection.models import PoseGRU, PoseTCN +from fall_detection.utils import set_seed, write_json + + +def make_loader( + features: np.ndarray, + labels: np.ndarray, + indices: np.ndarray, + batch_size: int, + shuffle: bool, +) -> DataLoader: + dataset = TensorDataset( + torch.from_numpy(features[indices]).float(), + torch.from_numpy(labels[indices]).float(), + ) + return DataLoader(dataset, batch_size=batch_size, shuffle=shuffle) + + +@torch.no_grad() +def predict(model: nn.Module, loader: DataLoader, device: torch.device) -> np.ndarray: + model.eval() + probabilities = [] + for features, _ in loader: + logits = model(features.to(device)) + probabilities.append(torch.sigmoid(logits).cpu().numpy()) + return np.concatenate(probabilities) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Train the pose GRU") + parser.add_argument("--dataset", default="data/processed/urfd_pose.npz") + parser.add_argument("--config", default="configs/default.yaml") + parser.add_argument("--output", type=Path, default=Path("artifacts/experiments/urfd")) + parser.add_argument("--device", choices=["auto", "cpu", "cuda"], default="auto") + parser.add_argument("--seed", type=int) + parser.add_argument("--architecture", choices=["gru", "tcn"], default="gru") + args = parser.parse_args() + + config, dataset, features, splits = prepare_experiment_data( + args.dataset, args.config, args.output, seed=args.seed + ) + set_seed(config["seed"]) + labels = dataset["labels"] + feature_indices = np.arange(features.shape[2], dtype=np.int64) + if args.architecture == "tcn": + feature_indices = CORE_FEATURE_INDICES + features = features[:, :, feature_indices] + train_mean = features[splits.train].mean(axis=(0, 1), keepdims=True) + train_std = features[splits.train].std(axis=(0, 1), keepdims=True) + train_std = np.maximum(train_std, 1e-5) + scaled = ((features - train_mean) / train_std).astype(np.float32) + + if args.device == "auto": + device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + else: + device = torch.device(args.device) + if device.type == "cuda" and not torch.cuda.is_available(): + raise SystemExit("CUDA was requested but torch.cuda.is_available() is false") + training = config["training"] + batch_size = int(training["batch_size"]) + train_loader = make_loader(scaled, labels, splits.train, batch_size, True) + val_loader = make_loader(scaled, labels, splits.val, batch_size, False) + test_loader = make_loader(scaled, labels, splits.test, batch_size, False) + + model_config = config["model"] + if args.architecture == "gru": + model = PoseGRU( + input_size=features.shape[2], + hidden_size=int(model_config["hidden_size"]), + num_layers=int(model_config["num_layers"]), + dropout=float(model_config["dropout"]), + ).to(device) + model_name = "pose_gru" + else: + model = PoseTCN( + input_size=features.shape[2], + channels=int(model_config["tcn_channels"]), + dropout=float(model_config["dropout"]), + ).to(device) + model_name = "pose_tcn" + train_labels = labels[splits.train] + pos_weight = float(np.sum(train_labels == 0) / max(np.sum(train_labels == 1), 1)) + criterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor(pos_weight, device=device)) + optimizer = torch.optim.AdamW( + model.parameters(), + lr=float(training["learning_rate"]), + weight_decay=float(training["weight_decay"]), + ) + + best_loss = float("inf") + best_state = None + wait = 0 + history = {"train_loss": [], "val_loss": []} + started = time.perf_counter() + for epoch in range(1, int(training["epochs"]) + 1): + model.train() + train_losses = [] + for batch_features, batch_labels in train_loader: + batch_features = batch_features.to(device) + batch_labels = batch_labels.to(device) + optimizer.zero_grad(set_to_none=True) + loss = criterion(model(batch_features), batch_labels) + loss.backward() + nn.utils.clip_grad_norm_(model.parameters(), max_norm=2.0) + optimizer.step() + train_losses.append(float(loss.item())) + + model.eval() + val_losses = [] + with torch.no_grad(): + for batch_features, batch_labels in val_loader: + batch_features = batch_features.to(device) + batch_labels = batch_labels.to(device) + val_losses.append(float(criterion(model(batch_features), batch_labels).item())) + train_loss = float(np.mean(train_losses)) + val_loss = float(np.mean(val_losses)) + history["train_loss"].append(train_loss) + history["val_loss"].append(val_loss) + print(f"Epoch {epoch:02d}: train_loss={train_loss:.4f}, val_loss={val_loss:.4f}") + + if val_loss < best_loss - 1e-4: + best_loss = val_loss + best_state = copy.deepcopy(model.state_dict()) + wait = 0 + else: + wait += 1 + if wait >= int(training["patience"]): + print("Early stopping") + break + + training_seconds = time.perf_counter() - started + if best_state is None: + raise RuntimeError("Training did not produce a checkpoint") + model.load_state_dict(best_state) + val_probabilities = predict(model, val_loader, device) + threshold = choose_f1_threshold(labels[splits.val], val_probabilities) + test_probabilities = predict(model, test_loader, device) + metrics = classification_metrics(labels[splits.test], test_probabilities, threshold) + metrics.update( + { + "model": model_name, + "architecture": args.architecture, + "seed": int(config["seed"]), + "device": str(device), + "device_name": torch.cuda.get_device_name(device) if device.type == "cuda" else "CPU", + "torch_version": torch.__version__, + "torch_cuda_version": torch.version.cuda, + "epochs_completed": len(history["train_loss"]), + "best_val_loss": best_loss, + "training_seconds": training_seconds, + "parameters": sum(parameter.numel() for parameter in model.parameters()), + "dataset": str(args.dataset), + "split": split_summary(labels, dataset["groups"], splits), + "result_scope": "test set only", + } + ) + output_dir = args.output / model_name + output_dir.mkdir(parents=True, exist_ok=True) + torch.save( + { + "state_dict": {key: value.cpu() for key, value in best_state.items()}, + "architecture": args.architecture, + "input_size": int(features.shape[2]), + "hidden_size": int(model_config["hidden_size"]), + "num_layers": int(model_config["num_layers"]), + "tcn_channels": int(model_config["tcn_channels"]), + "dropout": float(model_config["dropout"]), + "feature_indices": feature_indices, + "sequence_length": int(features.shape[1]), + "visibility_threshold": float(config["data"]["visibility_threshold"]), + "feature_mean": train_mean.squeeze().astype(np.float32), + "feature_std": train_std.squeeze().astype(np.float32), + "threshold": threshold, + }, + output_dir / "model.pt", + ) + write_json(output_dir / "metrics.json", metrics) + save_predictions( + output_dir / "predictions.csv", + labels[splits.test], + test_probabilities, + dataset["sources"][splits.test], + threshold, + ) + save_evaluation_plots( + labels[splits.test], test_probabilities, threshold, output_dir, model_name + ) + + plt.figure(figsize=(6, 4)) + plt.plot(history["train_loss"], label="Train") + plt.plot(history["val_loss"], label="Validation") + plt.xlabel("Epoch") + plt.ylabel("BCE loss") + plt.title(f"Qua trinh huan luyen {model_name}") + plt.legend() + plt.tight_layout() + plt.savefig(output_dir / "training_curve.png", dpi=180) + plt.close() + print( + f"{model_name}: F1={metrics['f1']:.3f}, recall={metrics['recall']:.3f}, " + f"specificity={metrics['specificity']:.3f}, AUC={metrics['roc_auc']:.3f}" + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/train_tcn.py b/scripts/train_tcn.py new file mode 100644 index 0000000000000000000000000000000000000000..5e2ef5125e1fb18dd990d42052d8768c4dd51cc2 --- /dev/null +++ b/scripts/train_tcn.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +"""Convenience entry point for the compact Pose-TCN.""" +from __future__ import annotations + +import sys + +from train_gru import main + + +if __name__ == "__main__": + if "--architecture" not in sys.argv: + sys.argv.extend(["--architecture", "tcn"]) + main() diff --git a/src/fall_detection/__init__.py b/src/fall_detection/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bfcd324fd3d2bde31da77877861327f76f675296 --- /dev/null +++ b/src/fall_detection/__init__.py @@ -0,0 +1,4 @@ +"""Pose-based fall detection package.""" + +__version__ = "1.0.0" + diff --git a/src/fall_detection/config.py b/src/fall_detection/config.py new file mode 100644 index 0000000000000000000000000000000000000000..76a08207062e5aa60f154c2533952f645d879873 --- /dev/null +++ b/src/fall_detection/config.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import yaml + + +def load_config(path: str | Path) -> dict[str, Any]: + """Load a YAML configuration file.""" + with Path(path).open("r", encoding="utf-8") as file: + return yaml.safe_load(file) + diff --git a/src/fall_detection/data.py b/src/fall_detection/data.py new file mode 100644 index 0000000000000000000000000000000000000000..a1f91887f7815ab08a051e9fb7bab62a1a28d320 --- /dev/null +++ b/src/fall_detection/data.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +import numpy as np +from sklearn.model_selection import GroupShuffleSplit + + +@dataclass(frozen=True) +class DatasetSplits: + train: np.ndarray + val: np.ndarray + test: np.ndarray + + +def load_pose_dataset(path: str | Path) -> dict[str, np.ndarray]: + """Load the compressed dataset produced by prepare_dataset.py.""" + with np.load(path, allow_pickle=False) as data: + required = {"poses", "labels", "groups", "sources"} + missing = required.difference(data.files) + if missing: + raise ValueError(f"Dataset is missing arrays: {sorted(missing)}") + return {key: data[key] for key in required} + + +def group_train_val_test_split( + labels: np.ndarray, + groups: np.ndarray, + test_size: float = 0.20, + val_size: float = 0.20, + seed: int = 42, +) -> DatasetSplits: + """Split without putting windows from the same source video in two sets.""" + indices = np.arange(len(labels)) + relative_val_size = val_size / (1.0 - test_size) + selected = None + for attempt in range(100): + outer = GroupShuffleSplit( + n_splits=1, test_size=test_size, random_state=seed + attempt * 2 + ) + train_val_idx, test_idx = next(outer.split(indices, labels, groups)) + inner = GroupShuffleSplit( + n_splits=1, + test_size=relative_val_size, + random_state=seed + attempt * 2 + 1, + ) + inner_train, inner_val = next( + inner.split(train_val_idx, labels[train_val_idx], groups[train_val_idx]) + ) + train_idx = train_val_idx[inner_train] + val_idx = train_val_idx[inner_val] + if all(len(np.unique(labels[idx])) == 2 for idx in (train_idx, val_idx, test_idx)): + selected = (train_idx, val_idx, test_idx) + break + if selected is None: + raise ValueError("Could not create group splits containing both classes") + train_idx, val_idx, test_idx = selected + + split_groups = [set(groups[idx].tolist()) for idx in (train_idx, val_idx, test_idx)] + if any(split_groups[i] & split_groups[j] for i in range(3) for j in range(i + 1, 3)): + raise RuntimeError("Group leakage detected") + return DatasetSplits(train=train_idx, val=val_idx, test=test_idx) + + +def save_splits(path: str | Path, splits: DatasetSplits) -> None: + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + np.savez_compressed(path, train=splits.train, val=splits.val, test=splits.test) + + +def load_splits(path: str | Path) -> DatasetSplits: + with np.load(path) as data: + return DatasetSplits(train=data["train"], val=data["val"], test=data["test"]) diff --git a/src/fall_detection/experiment.py b/src/fall_detection/experiment.py new file mode 100644 index 0000000000000000000000000000000000000000..01907dd7a9def8e9454047d4726a4098f6c9cdd4 --- /dev/null +++ b/src/fall_detection/experiment.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from pathlib import Path + +import numpy as np + +from fall_detection.config import load_config +from fall_detection.data import ( + DatasetSplits, + group_train_val_test_split, + load_pose_dataset, + load_splits, + save_splits, +) +from fall_detection.features import featurize_dataset + + +def prepare_experiment_data( + dataset_path: str | Path, + config_path: str | Path, + output_root: str | Path, + seed: int | None = None, +) -> tuple[dict, dict[str, np.ndarray], np.ndarray, DatasetSplits]: + config = load_config(config_path) + if seed is not None: + config["seed"] = int(seed) + dataset = load_pose_dataset(dataset_path) + features = featurize_dataset( + dataset["poses"], config["data"]["visibility_threshold"] + ) + split_path = Path(output_root) / "splits.npz" + if split_path.exists(): + splits = load_splits(split_path) + all_indices = np.concatenate([splits.train, splits.val, splits.test]) + if len(all_indices) != len(dataset["labels"]) or all_indices.max() >= len(dataset["labels"]): + raise ValueError( + f"Existing {split_path} does not match this dataset; remove it or use another output directory" + ) + else: + splits = group_train_val_test_split( + dataset["labels"], + dataset["groups"], + test_size=config["data"]["test_size"], + val_size=config["data"]["val_size"], + seed=config["seed"], + ) + save_splits(split_path, splits) + return config, dataset, features, splits + + +def split_summary( + labels: np.ndarray, groups: np.ndarray, splits: DatasetSplits +) -> dict[str, dict[str, int]]: + result: dict[str, dict[str, int]] = {} + for name, indices in ( + ("train", splits.train), + ("validation", splits.val), + ("test", splits.test), + ): + result[name] = { + "samples": int(len(indices)), + "groups": int(len(np.unique(groups[indices]))), + "normal": int(np.sum(labels[indices] == 0)), + "fall": int(np.sum(labels[indices] == 1)), + } + return result diff --git a/src/fall_detection/features.py b/src/fall_detection/features.py new file mode 100644 index 0000000000000000000000000000000000000000..3ed795b16424de967db99a3beb8fa0ba30bca390 --- /dev/null +++ b/src/fall_detection/features.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +import numpy as np + +# MediaPipe Pose landmark indices. +NOSE = 0 +LEFT_SHOULDER, RIGHT_SHOULDER = 11, 12 +LEFT_HIP, RIGHT_HIP = 23, 24 +LEFT_ANKLE, RIGHT_ANKLE = 27, 28 +N_LANDMARKS = 33 +N_RAW_CHANNELS = 4 # x, y, z, visibility +N_ENGINEERED = 8 +FEATURE_DIM = N_LANDMARKS * N_RAW_CHANNELS + N_ENGINEERED +CORE_LANDMARKS = [ + NOSE, + LEFT_SHOULDER, + RIGHT_SHOULDER, + 13, # left elbow + 14, # right elbow + 15, # left wrist + 16, # right wrist + LEFT_HIP, + RIGHT_HIP, + 25, # left knee + 26, # right knee + LEFT_ANKLE, + RIGHT_ANKLE, +] +CORE_FEATURE_INDICES = np.asarray( + [ + landmark * N_RAW_CHANNELS + channel + for landmark in CORE_LANDMARKS + for channel in range(N_RAW_CHANNELS) + ] + + list(range(N_LANDMARKS * N_RAW_CHANNELS, FEATURE_DIM)), + dtype=np.int64, +) + + +def _interpolate_missing(sequence: np.ndarray, visibility_threshold: float) -> np.ndarray: + """Interpolate coordinates over time while retaining original visibility.""" + result = sequence.astype(np.float32, copy=True) + timesteps = np.arange(result.shape[0]) + for landmark in range(N_LANDMARKS): + valid = result[:, landmark, 3] >= visibility_threshold + for channel in range(3): + values = result[:, landmark, channel] + if valid.any(): + result[:, landmark, channel] = np.interp( + timesteps, timesteps[valid], values[valid] + ) + else: + result[:, landmark, channel] = 0.0 + return result + + +def normalize_pose_sequence( + sequence: np.ndarray, visibility_threshold: float = 0.35 +) -> np.ndarray: + """Convert [T, 33, 4] landmarks into translation/scale-normalized features. + + Coordinates are centered at the midpoint of the hips and divided by torso + length. Eight interpretable geometry/motion features are appended per frame. + """ + sequence = np.asarray(sequence, dtype=np.float32) + if sequence.ndim != 3 or sequence.shape[1:] != (N_LANDMARKS, N_RAW_CHANNELS): + raise ValueError(f"Expected [T, 33, 4], received {sequence.shape}") + + pose = _interpolate_missing(sequence, visibility_threshold) + xyz = pose[:, :, :3] + visibility = np.clip(pose[:, :, 3:4], 0.0, 1.0) + + hip_center = (xyz[:, LEFT_HIP] + xyz[:, RIGHT_HIP]) / 2.0 + shoulder_center = (xyz[:, LEFT_SHOULDER] + xyz[:, RIGHT_SHOULDER]) / 2.0 + torso_length = np.linalg.norm(shoulder_center - hip_center, axis=1) + shoulder_width = np.linalg.norm( + xyz[:, LEFT_SHOULDER] - xyz[:, RIGHT_SHOULDER], axis=1 + ) + scale = np.maximum(torso_length, shoulder_width) + scale = np.maximum(scale, 1e-3) + + normalized_xyz = (xyz - hip_center[:, None, :]) / scale[:, None, None] + + visible_mask = visibility[:, :, 0] >= visibility_threshold + x = normalized_xyz[:, :, 0] + y = normalized_xyz[:, :, 1] + min_x = np.where(visible_mask, x, np.inf).min(axis=1) + max_x = np.where(visible_mask, x, -np.inf).max(axis=1) + min_y = np.where(visible_mask, y, np.inf).min(axis=1) + max_y = np.where(visible_mask, y, -np.inf).max(axis=1) + width = np.where(np.isfinite(min_x) & np.isfinite(max_x), max_x - min_x, 0.0) + height = np.where(np.isfinite(min_y) & np.isfinite(max_y), max_y - min_y, 0.0) + + torso_vector = shoulder_center - hip_center + torso_angle = np.arctan2(np.abs(torso_vector[:, 0]), np.abs(torso_vector[:, 1]) + 1e-6) + bbox_ratio = height / (width + 1e-3) + shoulder_slope = np.abs( + normalized_xyz[:, LEFT_SHOULDER, 1] - normalized_xyz[:, RIGHT_SHOULDER, 1] + ) + ankle_y = ( + normalized_xyz[:, LEFT_ANKLE, 1] + normalized_xyz[:, RIGHT_ANKLE, 1] + ) / 2.0 + head_to_ankle = np.abs(normalized_xyz[:, NOSE, 1] - ankle_y) + mean_visibility = visibility[:, :, 0].mean(axis=1) + + # Motion remains in image coordinates because centering removes global motion. + hip_y = hip_center[:, 1] + hip_velocity = np.gradient(hip_y) + hip_acceleration = np.gradient(hip_velocity) + nose_velocity = np.gradient(xyz[:, NOSE, 1]) + + engineered = np.column_stack( + [ + torso_angle, + bbox_ratio, + shoulder_slope, + head_to_ankle, + mean_visibility, + hip_velocity, + hip_acceleration, + nose_velocity, + ] + ).astype(np.float32) + + flattened = np.concatenate([normalized_xyz, visibility], axis=2).reshape( + sequence.shape[0], -1 + ) + return np.nan_to_num(np.concatenate([flattened, engineered], axis=1)).astype( + np.float32 + ) + + +def featurize_dataset( + poses: np.ndarray, visibility_threshold: float = 0.35 +) -> np.ndarray: + """Featurize a batch shaped [N, T, 33, 4].""" + return np.stack( + [normalize_pose_sequence(seq, visibility_threshold) for seq in poses] + ) + + +def summarize_sequence_features(features: np.ndarray) -> np.ndarray: + """Aggregate temporal features for classical ML baselines.""" + features = np.asarray(features, dtype=np.float32) + means = features.mean(axis=1) + stds = features.std(axis=1) + mins = features.min(axis=1) + maxs = features.max(axis=1) + first_last_delta = features[:, -1] - features[:, 0] + return np.concatenate([means, stds, mins, maxs, first_last_delta], axis=1) diff --git a/src/fall_detection/metrics.py b/src/fall_detection/metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..25a2cd212d4b93e2744862f855b41bddfb6b33e5 --- /dev/null +++ b/src/fall_detection/metrics.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import seaborn as sns +from sklearn.metrics import ( + accuracy_score, + confusion_matrix, + f1_score, + precision_score, + recall_score, + roc_auc_score, + roc_curve, +) + + +def choose_f1_threshold(labels: np.ndarray, probabilities: np.ndarray) -> float: + """Choose the validation threshold with the best F1 score.""" + candidates = np.linspace(0.20, 0.80, 121) + scores = np.asarray( + [f1_score(labels, probabilities >= value, zero_division=0) for value in candidates] + ) + # Several thresholds often tie on a small validation set. Choosing the one + # closest to the conventional 0.5 avoids an arbitrary low-threshold bias. + tied = np.flatnonzero(np.isclose(scores, scores.max())) + best_index = tied[np.argmin(np.abs(candidates[tied] - 0.5))] + return float(candidates[best_index]) + + +def classification_metrics( + labels: np.ndarray, probabilities: np.ndarray, threshold: float +) -> dict[str, float | int]: + predictions = (probabilities >= threshold).astype(int) + tn, fp, fn, tp = confusion_matrix(labels, predictions, labels=[0, 1]).ravel() + return { + "accuracy": float(accuracy_score(labels, predictions)), + "precision": float(precision_score(labels, predictions, zero_division=0)), + "recall": float(recall_score(labels, predictions, zero_division=0)), + "specificity": float(tn / max(tn + fp, 1)), + "f1": float(f1_score(labels, predictions, zero_division=0)), + "roc_auc": float(roc_auc_score(labels, probabilities)) + if len(np.unique(labels)) == 2 + else float("nan"), + "threshold": float(threshold), + "tn": int(tn), + "fp": int(fp), + "fn": int(fn), + "tp": int(tp), + "n_samples": int(len(labels)), + } + + +def save_evaluation_plots( + labels: np.ndarray, + probabilities: np.ndarray, + threshold: float, + output_dir: str | Path, + title: str, +) -> None: + output_dir = Path(output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + predictions = (probabilities >= threshold).astype(int) + matrix = confusion_matrix(labels, predictions, labels=[0, 1]) + + plt.figure(figsize=(5, 4)) + sns.heatmap( + matrix, + annot=True, + fmt="d", + cmap="Blues", + xticklabels=["Binh thuong", "Te nga"], + yticklabels=["Binh thuong", "Te nga"], + ) + plt.xlabel("Du doan") + plt.ylabel("Thuc te") + plt.title(f"Ma tran nham lan - {title}") + plt.tight_layout() + plt.savefig(output_dir / "confusion_matrix.png", dpi=180) + plt.close() + + if len(np.unique(labels)) == 2: + fpr, tpr, _ = roc_curve(labels, probabilities) + auc = roc_auc_score(labels, probabilities) + plt.figure(figsize=(5, 4)) + plt.plot(fpr, tpr, label=f"AUC = {auc:.3f}") + plt.plot([0, 1], [0, 1], "--", color="gray") + plt.xlabel("False Positive Rate") + plt.ylabel("True Positive Rate") + plt.title(f"ROC - {title}") + plt.legend() + plt.tight_layout() + plt.savefig(output_dir / "roc_curve.png", dpi=180) + plt.close() + + +def save_predictions( + path: str | Path, + labels: np.ndarray, + probabilities: np.ndarray, + sources: np.ndarray, + threshold: float, +) -> None: + pd.DataFrame( + { + "source": sources, + "label": labels.astype(int), + "probability_fall": probabilities, + "prediction": (probabilities >= threshold).astype(int), + } + ).to_csv(path, index=False) diff --git a/src/fall_detection/models.py b/src/fall_detection/models.py new file mode 100644 index 0000000000000000000000000000000000000000..bb08e8ddf57f22563a8a800b7c7493347178eb2f --- /dev/null +++ b/src/fall_detection/models.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import torch +from torch import nn + + +class PoseGRU(nn.Module): + """Small bidirectional GRU for binary sequence classification.""" + + def __init__( + self, + input_size: int, + hidden_size: int = 64, + num_layers: int = 1, + dropout: float = 0.2, + ) -> None: + super().__init__() + recurrent_dropout = dropout if num_layers > 1 else 0.0 + self.gru = nn.GRU( + input_size=input_size, + hidden_size=hidden_size, + num_layers=num_layers, + dropout=recurrent_dropout, + batch_first=True, + bidirectional=True, + ) + self.classifier = nn.Sequential( + nn.LayerNorm(hidden_size * 2), + nn.Dropout(dropout), + nn.Linear(hidden_size * 2, 1), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + output, _ = self.gru(x) + pooled = output.mean(dim=1) + return self.classifier(pooled).squeeze(1) + + +class PoseTCN(nn.Module): + """Compact temporal CNN with mean/max pooling for short pose sequences.""" + + def __init__(self, input_size: int, channels: int = 32, dropout: float = 0.2) -> None: + super().__init__() + self.temporal = nn.Sequential( + nn.Conv1d(input_size, channels, kernel_size=5, padding=2), + nn.BatchNorm1d(channels), + nn.ReLU(), + nn.Dropout(dropout), + nn.Conv1d(channels, channels, kernel_size=3, padding=1), + nn.BatchNorm1d(channels), + nn.ReLU(), + nn.Dropout(dropout), + ) + self.classifier = nn.Linear(channels * 2, 1) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + temporal = self.temporal(x.transpose(1, 2)) + pooled = torch.cat([temporal.mean(dim=2), temporal.amax(dim=2)], dim=1) + return self.classifier(pooled).squeeze(1) diff --git a/src/fall_detection/pose_extractor.py b/src/fall_detection/pose_extractor.py new file mode 100644 index 0000000000000000000000000000000000000000..d748edc061bb90f466fdfc3791d7b7b15f0e2c8f --- /dev/null +++ b/src/fall_detection/pose_extractor.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +import cv2 +import numpy as np + + +@dataclass +class ExtractedVideo: + poses: np.ndarray + frame_numbers: np.ndarray + source_fps: float + + +class MediaPipePoseExtractor: + """Thin wrapper so training code stays independent from MediaPipe.""" + + def __init__( + self, + model_complexity: int = 1, + min_detection_confidence: float = 0.5, + min_tracking_confidence: float = 0.5, + ) -> None: + import mediapipe as mp + + self._mp = mp + self._pose = mp.solutions.pose.Pose( + static_image_mode=False, + model_complexity=model_complexity, + smooth_landmarks=True, + enable_segmentation=False, + min_detection_confidence=min_detection_confidence, + min_tracking_confidence=min_tracking_confidence, + ) + + def process_rgb(self, rgb_frame: np.ndarray) -> np.ndarray | None: + result = self._pose.process(rgb_frame) + if result.pose_landmarks is None: + return None + return np.asarray( + [[point.x, point.y, point.z, point.visibility] for point in result.pose_landmarks.landmark], + dtype=np.float32, + ) + + def close(self) -> None: + self._pose.close() + + def __enter__(self) -> "MediaPipePoseExtractor": + return self + + def __exit__(self, *_: object) -> None: + self.close() + + +def extract_video( + path: str | Path, target_fps: float = 10.0, crop_wide_right_half: bool = True +) -> ExtractedVideo: + path = Path(path) + capture = cv2.VideoCapture(str(path)) + if not capture.isOpened(): + raise ValueError(f"Cannot open video: {path}") + + source_fps = capture.get(cv2.CAP_PROP_FPS) + if not np.isfinite(source_fps) or source_fps <= 0: + source_fps = 25.0 + sample_every = max(1, int(round(source_fps / target_fps))) + poses: list[np.ndarray] = [] + frame_numbers: list[int] = [] + + with MediaPipePoseExtractor() as extractor: + frame_index = 0 + while True: + ok, frame = capture.read() + if not ok: + break + if frame_index % sample_every == 0: + # Official URFD preview videos concatenate depth (left) and RGB + # (right) into a 640x240 frame. Cropping makes the person large + # enough for reliable landmark detection. + if crop_wide_right_half and frame.shape[1] / frame.shape[0] > 2.2: + frame = frame[:, frame.shape[1] // 2 :] + rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + pose = extractor.process_rgb(rgb) + if pose is None: + pose = np.zeros((33, 4), dtype=np.float32) + poses.append(pose) + frame_numbers.append(frame_index + 1) # URFD annotations are 1-based. + frame_index += 1 + capture.release() + + if not poses: + raise ValueError(f"No frames read from video: {path}") + return ExtractedVideo( + poses=np.stack(poses), + frame_numbers=np.asarray(frame_numbers, dtype=np.int32), + source_fps=float(source_fps), + ) + + +def pad_window(window: np.ndarray, length: int) -> np.ndarray: + if len(window) >= length: + return window[:length] + if len(window) == 0: + return np.zeros((length, 33, 4), dtype=np.float32) + padding = np.repeat(window[-1][None, ...], length - len(window), axis=0) + return np.concatenate([window, padding], axis=0) diff --git a/src/fall_detection/utils.py b/src/fall_detection/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..7c1acae4477a3ea26523f06a55aea12ebe687c11 --- /dev/null +++ b/src/fall_detection/utils.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +import json +import random +from pathlib import Path +from typing import Any + +import numpy as np +import torch + + +def set_seed(seed: int) -> None: + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + if torch.cuda.is_available(): + torch.cuda.manual_seed_all(seed) + torch.backends.cudnn.benchmark = False + torch.backends.cudnn.deterministic = True + + +def write_json(path: str | Path, value: dict[str, Any]) -> None: + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("w", encoding="utf-8") as file: + json.dump(value, file, ensure_ascii=False, indent=2) + + +def read_json(path: str | Path) -> dict[str, Any]: + with Path(path).open("r", encoding="utf-8") as file: + return json.load(file) diff --git a/tests/test_features.py b/tests/test_features.py new file mode 100644 index 0000000000000000000000000000000000000000..7468c7e2149c10f0375b8dcc22552441d2cd5e63 --- /dev/null +++ b/tests/test_features.py @@ -0,0 +1,34 @@ +import numpy as np + +from fall_detection.features import FEATURE_DIM, normalize_pose_sequence + + +def random_visible_pose(seed: int = 0) -> np.ndarray: + rng = np.random.default_rng(seed) + poses = rng.normal(size=(12, 33, 4)).astype(np.float32) + poses[:, :, :2] = poses[:, :, :2] * 0.05 + 0.5 + poses[:, :, 3] = 1.0 + return poses + + +def test_feature_shape_and_finiteness() -> None: + features = normalize_pose_sequence(random_visible_pose()) + assert features.shape == (12, FEATURE_DIM) + assert np.isfinite(features).all() + + +def test_translation_does_not_change_normalized_coordinates() -> None: + poses = random_visible_pose() + translated = poses.copy() + translated[:, :, 0] += 0.20 + translated[:, :, 1] -= 0.15 + original_features = normalize_pose_sequence(poses) + translated_features = normalize_pose_sequence(translated) + # Normalized xyz and static geometry are translation invariant. The last + # motion features may differ by tiny finite-precision gradients. + np.testing.assert_allclose( + original_features[:, : 33 * 4 + 5], + translated_features[:, : 33 * 4 + 5], + atol=2e-5, + ) + diff --git a/tests/test_split_and_model.py b/tests/test_split_and_model.py new file mode 100644 index 0000000000000000000000000000000000000000..a7fae9cf83f9991a0b94e9b75dba60fa743a90ec --- /dev/null +++ b/tests/test_split_and_model.py @@ -0,0 +1,27 @@ +import numpy as np +import torch + +from fall_detection.data import group_train_val_test_split +from fall_detection.models import PoseGRU, PoseTCN + + +def test_group_split_has_no_leakage() -> None: + groups = np.repeat(np.asarray([f"g{i}" for i in range(20)]), 4) + labels = np.tile(np.asarray([0, 1, 0, 1]), 20) + splits = group_train_val_test_split(labels, groups, seed=7) + group_sets = [set(groups[index]) for index in (splits.train, splits.val, splits.test)] + assert group_sets[0].isdisjoint(group_sets[1]) + assert group_sets[0].isdisjoint(group_sets[2]) + assert group_sets[1].isdisjoint(group_sets[2]) + + +def test_gru_returns_one_logit_per_sequence() -> None: + model = PoseGRU(input_size=140, hidden_size=16) + result = model(torch.randn(5, 40, 140)) + assert result.shape == (5,) + + +def test_tcn_returns_one_logit_per_sequence() -> None: + model = PoseTCN(input_size=60, channels=16) + result = model(torch.randn(5, 40, 60)) + assert result.shape == (5,)