v2x-risk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- v2x_risk-0.1.0/LICENSE +21 -0
- v2x_risk-0.1.0/PKG-INFO +209 -0
- v2x_risk-0.1.0/README.md +177 -0
- v2x_risk-0.1.0/pyproject.toml +67 -0
- v2x_risk-0.1.0/setup.cfg +4 -0
- v2x_risk-0.1.0/src/v2x_risk/__init__.py +3 -0
- v2x_risk-0.1.0/src/v2x_risk/config.py +59 -0
- v2x_risk-0.1.0/src/v2x_risk/data.py +656 -0
- v2x_risk-0.1.0/src/v2x_risk/evaluate.py +78 -0
- v2x_risk-0.1.0/src/v2x_risk/metrics.py +47 -0
- v2x_risk-0.1.0/src/v2x_risk/model.py +216 -0
- v2x_risk-0.1.0/src/v2x_risk/preprocess.py +102 -0
- v2x_risk-0.1.0/src/v2x_risk/report.py +199 -0
- v2x_risk-0.1.0/src/v2x_risk/reproducibility.py +40 -0
- v2x_risk-0.1.0/src/v2x_risk/smoke.py +93 -0
- v2x_risk-0.1.0/src/v2x_risk/train.py +23 -0
- v2x_risk-0.1.0/src/v2x_risk/training.py +220 -0
- v2x_risk-0.1.0/src/v2x_risk.egg-info/PKG-INFO +209 -0
- v2x_risk-0.1.0/src/v2x_risk.egg-info/SOURCES.txt +26 -0
- v2x_risk-0.1.0/src/v2x_risk.egg-info/dependency_links.txt +1 -0
- v2x_risk-0.1.0/src/v2x_risk.egg-info/entry_points.txt +6 -0
- v2x_risk-0.1.0/src/v2x_risk.egg-info/requires.txt +10 -0
- v2x_risk-0.1.0/src/v2x_risk.egg-info/top_level.txt +1 -0
- v2x_risk-0.1.0/tests/test_config.py +20 -0
- v2x_risk-0.1.0/tests/test_data.py +133 -0
- v2x_risk-0.1.0/tests/test_model.py +37 -0
- v2x_risk-0.1.0/tests/test_report.py +57 -0
- v2x_risk-0.1.0/tests/test_smoke_pipeline.py +17 -0
v2x_risk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benjamin Quito
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
v2x_risk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: v2x-risk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reproducible LSTM-GAT model for V2X traffic anomaly and collision-risk classification
|
|
5
|
+
Author: Benjamin Quito
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/benjaminquito/V2X
|
|
8
|
+
Project-URL: Repository, https://github.com/benjaminquito/V2X
|
|
9
|
+
Project-URL: Paper, https://doi.org/10.4236/ojsst.2026.163010
|
|
10
|
+
Project-URL: Issues, https://github.com/benjaminquito/V2X/issues
|
|
11
|
+
Keywords: v2x,traffic-anomaly-detection,collision-risk,lstm,graph-attention-network,reproducible-research
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy<3,>=1.26
|
|
23
|
+
Requires-Dist: openpyxl<4,>=3.1
|
|
24
|
+
Requires-Dist: pandas<3,>=2.1
|
|
25
|
+
Requires-Dist: PyYAML<7,>=6
|
|
26
|
+
Requires-Dist: scikit-learn<2,>=1.3
|
|
27
|
+
Requires-Dist: torch<3,>=2.2
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest<9,>=8; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff<1,>=0.9; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# V2X Traffic Anomaly and Collision-Risk Model
|
|
34
|
+
|
|
35
|
+
[](https://github.com/benjaminquito/V2X/actions/workflows/ci.yml)
|
|
36
|
+
[](https://pypi.org/project/v2x-risk/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
|
|
39
|
+
This repository is a reproducible implementation of the revised model associated with
|
|
40
|
+
*Predicting Traffic Anomalies and Collision Risks in V2X Systems: A Deep Learning Approach
|
|
41
|
+
Using LSTM and GNN*. It implements the later reviewer-response design:
|
|
42
|
+
|
|
43
|
+
- a two-layer LSTM for 15-step vehicle histories;
|
|
44
|
+
- a two-layer, multi-head Graph Attention Network (GAT) for per-frame vehicle interactions;
|
|
45
|
+
- directed proximity edges between vehicles less than 20 m apart;
|
|
46
|
+
- a trainable softmax attention module that fuses temporal and spatial representations; and
|
|
47
|
+
- three-class prediction: Low (0), Medium (1), and High (2) risk.
|
|
48
|
+
|
|
49
|
+
No trained weights or real-data results are claimed by this repository. The NGSIM data is not
|
|
50
|
+
redistributed. Figures stated in the manuscript or later development chat are historical claims and
|
|
51
|
+
must not be treated as results reproduced by this code.
|
|
52
|
+
|
|
53
|
+
## Why this implementation differs from the uploaded manuscript
|
|
54
|
+
|
|
55
|
+
The uploaded manuscript describes an earlier model in several places: 50-step, three-feature
|
|
56
|
+
sequences; two GCN layers; continuous risk scores; and fixed 0.6/0.4 fusion. The later revision
|
|
57
|
+
changed these to 15-step, four-feature inputs; GAT; three-class outputs; and learned attention
|
|
58
|
+
fusion. This repository implements the later design requested for the reviewer response while
|
|
59
|
+
preserving the original claims as historical metadata only. See
|
|
60
|
+
[`docs/METHODOLOGY.md`](docs/METHODOLOGY.md) for the complete reconciliation.
|
|
61
|
+
|
|
62
|
+
The earlier chat evaluation paired LSTM samples and graphs by truncating both collections to the
|
|
63
|
+
same length. That does not establish that the two inputs describe the same vehicle and frame. Here,
|
|
64
|
+
each graph node is built only when that exact vehicle has a valid history ending at the graph's
|
|
65
|
+
frame. Both branches therefore operate on an explicitly aligned sample.
|
|
66
|
+
|
|
67
|
+
The later chat's 99.90% accuracy and 1.00 weighted-F1 claim is not a repository baseline: the shared
|
|
68
|
+
evaluation snippet did not demonstrate trained fusion weights or key-based modality alignment. It
|
|
69
|
+
is mentioned only to prevent accidental reuse as a regenerated result.
|
|
70
|
+
|
|
71
|
+
## Repository layout
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
configs/ Full-data and smoke-test configurations
|
|
75
|
+
data/ Raw/processed placeholders (generated data is ignored)
|
|
76
|
+
docs/ Data, methodology, and reproducibility notes
|
|
77
|
+
results/ Historical claims and regenerated-output placeholder
|
|
78
|
+
src/v2x_risk/ Preprocessing, model, training, and evaluation code
|
|
79
|
+
tests/ Unit and end-to-end smoke tests
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
Python 3.10 or newer is required.
|
|
85
|
+
|
|
86
|
+
Install the published package:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
python -m pip install v2x-risk
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For repository development, create an isolated environment and install the editable package:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python3 -m venv .venv
|
|
96
|
+
source .venv/bin/activate
|
|
97
|
+
python -m pip install --upgrade pip
|
|
98
|
+
python -m pip install -e ".[dev]"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To reproduce the exact dependency versions used for the repository smoke test, install
|
|
102
|
+
`requirements-lock.txt` before the editable package:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python -m pip install -r requirements-lock.txt
|
|
106
|
+
python -m pip install --no-deps -e .
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
PyTorch is the only deep-learning dependency. The GAT layer uses native PyTorch scatter operations,
|
|
110
|
+
so no separately compiled graph library is required.
|
|
111
|
+
|
|
112
|
+
## Quick execution check
|
|
113
|
+
|
|
114
|
+
The smoke run creates a small synthetic, NGSIM-shaped dataset, preprocesses it, trains one epoch,
|
|
115
|
+
evaluates all three branches, and writes a visual report:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
python -m v2x_risk.smoke --config configs/smoke.yaml
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Smoke metrics only verify that the pipeline executes. They are not research results and must not be
|
|
122
|
+
compared with the paper.
|
|
123
|
+
|
|
124
|
+
Open `results/regenerated/smoke/run_report.html` after the command completes to inspect the visual
|
|
125
|
+
pipeline summary.
|
|
126
|
+
|
|
127
|
+
## Reproduce with NGSIM
|
|
128
|
+
|
|
129
|
+
1. Obtain an NGSIM trajectory CSV from the
|
|
130
|
+
[U.S. DOT NGSIM Open Data portal](https://data.transportation.gov/stories/s/Next-Generation-Simulation-NGSIM-Open-Data/i5zb-xe34/)
|
|
131
|
+
and place it at `data/raw/NGSIM.csv`.
|
|
132
|
+
2. Review [`configs/default.yaml`](configs/default.yaml), especially the coordinate conversion and
|
|
133
|
+
risk-label assumptions.
|
|
134
|
+
3. Run the complete pipeline:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
python -m v2x_risk.preprocess --config configs/default.yaml
|
|
138
|
+
python -m v2x_risk.train --config configs/default.yaml
|
|
139
|
+
python -m v2x_risk.evaluate --config configs/default.yaml --split test
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Or use:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
./scripts/reproduce.sh configs/default.yaml
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The loader detects CSV and Excel OOXML content from the file signature. This means an uploaded
|
|
149
|
+
workbook can still be read if it was accidentally named `NGSIM.csv`, although using the correct
|
|
150
|
+
`.xlsx` suffix is recommended.
|
|
151
|
+
|
|
152
|
+
Generated files include:
|
|
153
|
+
|
|
154
|
+
- `data/processed/ngsim_aligned_graph_windows.npz`: aligned graph/history samples;
|
|
155
|
+
- `data/processed/scaler.json`: train-only min-max parameters;
|
|
156
|
+
- `data/processed/manifest.json`: source hash, counts, split sizes, and preprocessing metadata;
|
|
157
|
+
- `results/regenerated/best_model.pt`: best checkpoint by validation macro F1;
|
|
158
|
+
- `results/regenerated/metrics.json`: training history and branch-level results;
|
|
159
|
+
- `results/regenerated/test_metrics.json`: independently regenerated test metrics; and
|
|
160
|
+
- `results/regenerated/test_predictions.csv`: per-frame predictions and learned fusion weights;
|
|
161
|
+
- `results/regenerated/run_summary.txt`: a plain-language completion summary; and
|
|
162
|
+
- `results/regenerated/run_report.html`: a visual completion and results dashboard.
|
|
163
|
+
|
|
164
|
+
At the end of `scripts/reproduce.sh`, the terminal prints `V2X RUN COMPLETED SUCCESSFULLY` followed
|
|
165
|
+
by the main dataset counts, held-out metrics, and output locations. Open `run_report.html` in a web
|
|
166
|
+
browser to verify the completed run visually.
|
|
167
|
+
|
|
168
|
+
The default configuration follows the paper's 70/15/15 proportions and five training epochs. The
|
|
169
|
+
split is chronological and inserts a 14-frame gap at boundaries to reduce leakage from overlapping
|
|
170
|
+
15-step windows. Min-max scaling is fit on the training split only.
|
|
171
|
+
|
|
172
|
+
## Required CSV columns
|
|
173
|
+
|
|
174
|
+
Core inputs are `Vehicle_ID`, `Frame_ID`, `Local_X`, and `Local_Y`. The preferred input also has
|
|
175
|
+
`v_Vel` and `v_Acc`. When either kinematic field is absent, preprocessing derives it from
|
|
176
|
+
longitudinal position and elapsed time using the explicit settings under `data.kinematics`.
|
|
177
|
+
Existing kinematic columns are never replaced. If the CSV already contains `Risk_Class`, it is used
|
|
178
|
+
after validation. Otherwise, `Time_Headway` and `Space_Headway` are also required and labels are
|
|
179
|
+
generated from the explicit thresholds in the configuration. The manuscript did not state the
|
|
180
|
+
numerical labeling thresholds, so the supplied values are transparent repository assumptions that
|
|
181
|
+
researchers should review or replace.
|
|
182
|
+
|
|
183
|
+
Combined files should include `Location`. Preprocessing keeps locations separate, segments reused
|
|
184
|
+
vehicle IDs into contiguous frame runs, and keys graph snapshots by location and timestamp. This
|
|
185
|
+
prevents observations from separate roads or recording sessions from entering the same trajectory
|
|
186
|
+
or proximity graph.
|
|
187
|
+
|
|
188
|
+
Derived speed and acceleration are a transparent compatibility fallback. Report their use and do
|
|
189
|
+
not treat results based on derived kinematics as directly comparable to experiments that used the
|
|
190
|
+
original NGSIM `v_Vel` and `v_Acc` measurements.
|
|
191
|
+
|
|
192
|
+
See [`docs/DATA.md`](docs/DATA.md) before combining NGSIM sites or changing units.
|
|
193
|
+
|
|
194
|
+
## Tests
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
pytest
|
|
198
|
+
ruff check .
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The test suite checks class encoding, radius-graph construction, split gaps, tensor shapes, fusion
|
|
202
|
+
weight normalization, and an end-to-end synthetic run.
|
|
203
|
+
|
|
204
|
+
## Result-reporting rule
|
|
205
|
+
|
|
206
|
+
Only files produced under `results/regenerated/` by a completed run may be described as regenerated
|
|
207
|
+
results. Report the raw-data SHA-256 hash, configuration, seed, split policy, and checkpoint with
|
|
208
|
+
every result. Do not copy the 99.90% accuracy or 1.00 F1 claim into a new report unless this aligned
|
|
209
|
+
pipeline independently produces it on the intended held-out data.
|
v2x_risk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# V2X Traffic Anomaly and Collision-Risk Model
|
|
2
|
+
|
|
3
|
+
[](https://github.com/benjaminquito/V2X/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/v2x-risk/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
This repository is a reproducible implementation of the revised model associated with
|
|
8
|
+
*Predicting Traffic Anomalies and Collision Risks in V2X Systems: A Deep Learning Approach
|
|
9
|
+
Using LSTM and GNN*. It implements the later reviewer-response design:
|
|
10
|
+
|
|
11
|
+
- a two-layer LSTM for 15-step vehicle histories;
|
|
12
|
+
- a two-layer, multi-head Graph Attention Network (GAT) for per-frame vehicle interactions;
|
|
13
|
+
- directed proximity edges between vehicles less than 20 m apart;
|
|
14
|
+
- a trainable softmax attention module that fuses temporal and spatial representations; and
|
|
15
|
+
- three-class prediction: Low (0), Medium (1), and High (2) risk.
|
|
16
|
+
|
|
17
|
+
No trained weights or real-data results are claimed by this repository. The NGSIM data is not
|
|
18
|
+
redistributed. Figures stated in the manuscript or later development chat are historical claims and
|
|
19
|
+
must not be treated as results reproduced by this code.
|
|
20
|
+
|
|
21
|
+
## Why this implementation differs from the uploaded manuscript
|
|
22
|
+
|
|
23
|
+
The uploaded manuscript describes an earlier model in several places: 50-step, three-feature
|
|
24
|
+
sequences; two GCN layers; continuous risk scores; and fixed 0.6/0.4 fusion. The later revision
|
|
25
|
+
changed these to 15-step, four-feature inputs; GAT; three-class outputs; and learned attention
|
|
26
|
+
fusion. This repository implements the later design requested for the reviewer response while
|
|
27
|
+
preserving the original claims as historical metadata only. See
|
|
28
|
+
[`docs/METHODOLOGY.md`](docs/METHODOLOGY.md) for the complete reconciliation.
|
|
29
|
+
|
|
30
|
+
The earlier chat evaluation paired LSTM samples and graphs by truncating both collections to the
|
|
31
|
+
same length. That does not establish that the two inputs describe the same vehicle and frame. Here,
|
|
32
|
+
each graph node is built only when that exact vehicle has a valid history ending at the graph's
|
|
33
|
+
frame. Both branches therefore operate on an explicitly aligned sample.
|
|
34
|
+
|
|
35
|
+
The later chat's 99.90% accuracy and 1.00 weighted-F1 claim is not a repository baseline: the shared
|
|
36
|
+
evaluation snippet did not demonstrate trained fusion weights or key-based modality alignment. It
|
|
37
|
+
is mentioned only to prevent accidental reuse as a regenerated result.
|
|
38
|
+
|
|
39
|
+
## Repository layout
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
configs/ Full-data and smoke-test configurations
|
|
43
|
+
data/ Raw/processed placeholders (generated data is ignored)
|
|
44
|
+
docs/ Data, methodology, and reproducibility notes
|
|
45
|
+
results/ Historical claims and regenerated-output placeholder
|
|
46
|
+
src/v2x_risk/ Preprocessing, model, training, and evaluation code
|
|
47
|
+
tests/ Unit and end-to-end smoke tests
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
Python 3.10 or newer is required.
|
|
53
|
+
|
|
54
|
+
Install the published package:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python -m pip install v2x-risk
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For repository development, create an isolated environment and install the editable package:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python3 -m venv .venv
|
|
64
|
+
source .venv/bin/activate
|
|
65
|
+
python -m pip install --upgrade pip
|
|
66
|
+
python -m pip install -e ".[dev]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To reproduce the exact dependency versions used for the repository smoke test, install
|
|
70
|
+
`requirements-lock.txt` before the editable package:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python -m pip install -r requirements-lock.txt
|
|
74
|
+
python -m pip install --no-deps -e .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
PyTorch is the only deep-learning dependency. The GAT layer uses native PyTorch scatter operations,
|
|
78
|
+
so no separately compiled graph library is required.
|
|
79
|
+
|
|
80
|
+
## Quick execution check
|
|
81
|
+
|
|
82
|
+
The smoke run creates a small synthetic, NGSIM-shaped dataset, preprocesses it, trains one epoch,
|
|
83
|
+
evaluates all three branches, and writes a visual report:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
python -m v2x_risk.smoke --config configs/smoke.yaml
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Smoke metrics only verify that the pipeline executes. They are not research results and must not be
|
|
90
|
+
compared with the paper.
|
|
91
|
+
|
|
92
|
+
Open `results/regenerated/smoke/run_report.html` after the command completes to inspect the visual
|
|
93
|
+
pipeline summary.
|
|
94
|
+
|
|
95
|
+
## Reproduce with NGSIM
|
|
96
|
+
|
|
97
|
+
1. Obtain an NGSIM trajectory CSV from the
|
|
98
|
+
[U.S. DOT NGSIM Open Data portal](https://data.transportation.gov/stories/s/Next-Generation-Simulation-NGSIM-Open-Data/i5zb-xe34/)
|
|
99
|
+
and place it at `data/raw/NGSIM.csv`.
|
|
100
|
+
2. Review [`configs/default.yaml`](configs/default.yaml), especially the coordinate conversion and
|
|
101
|
+
risk-label assumptions.
|
|
102
|
+
3. Run the complete pipeline:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python -m v2x_risk.preprocess --config configs/default.yaml
|
|
106
|
+
python -m v2x_risk.train --config configs/default.yaml
|
|
107
|
+
python -m v2x_risk.evaluate --config configs/default.yaml --split test
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Or use:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
./scripts/reproduce.sh configs/default.yaml
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The loader detects CSV and Excel OOXML content from the file signature. This means an uploaded
|
|
117
|
+
workbook can still be read if it was accidentally named `NGSIM.csv`, although using the correct
|
|
118
|
+
`.xlsx` suffix is recommended.
|
|
119
|
+
|
|
120
|
+
Generated files include:
|
|
121
|
+
|
|
122
|
+
- `data/processed/ngsim_aligned_graph_windows.npz`: aligned graph/history samples;
|
|
123
|
+
- `data/processed/scaler.json`: train-only min-max parameters;
|
|
124
|
+
- `data/processed/manifest.json`: source hash, counts, split sizes, and preprocessing metadata;
|
|
125
|
+
- `results/regenerated/best_model.pt`: best checkpoint by validation macro F1;
|
|
126
|
+
- `results/regenerated/metrics.json`: training history and branch-level results;
|
|
127
|
+
- `results/regenerated/test_metrics.json`: independently regenerated test metrics; and
|
|
128
|
+
- `results/regenerated/test_predictions.csv`: per-frame predictions and learned fusion weights;
|
|
129
|
+
- `results/regenerated/run_summary.txt`: a plain-language completion summary; and
|
|
130
|
+
- `results/regenerated/run_report.html`: a visual completion and results dashboard.
|
|
131
|
+
|
|
132
|
+
At the end of `scripts/reproduce.sh`, the terminal prints `V2X RUN COMPLETED SUCCESSFULLY` followed
|
|
133
|
+
by the main dataset counts, held-out metrics, and output locations. Open `run_report.html` in a web
|
|
134
|
+
browser to verify the completed run visually.
|
|
135
|
+
|
|
136
|
+
The default configuration follows the paper's 70/15/15 proportions and five training epochs. The
|
|
137
|
+
split is chronological and inserts a 14-frame gap at boundaries to reduce leakage from overlapping
|
|
138
|
+
15-step windows. Min-max scaling is fit on the training split only.
|
|
139
|
+
|
|
140
|
+
## Required CSV columns
|
|
141
|
+
|
|
142
|
+
Core inputs are `Vehicle_ID`, `Frame_ID`, `Local_X`, and `Local_Y`. The preferred input also has
|
|
143
|
+
`v_Vel` and `v_Acc`. When either kinematic field is absent, preprocessing derives it from
|
|
144
|
+
longitudinal position and elapsed time using the explicit settings under `data.kinematics`.
|
|
145
|
+
Existing kinematic columns are never replaced. If the CSV already contains `Risk_Class`, it is used
|
|
146
|
+
after validation. Otherwise, `Time_Headway` and `Space_Headway` are also required and labels are
|
|
147
|
+
generated from the explicit thresholds in the configuration. The manuscript did not state the
|
|
148
|
+
numerical labeling thresholds, so the supplied values are transparent repository assumptions that
|
|
149
|
+
researchers should review or replace.
|
|
150
|
+
|
|
151
|
+
Combined files should include `Location`. Preprocessing keeps locations separate, segments reused
|
|
152
|
+
vehicle IDs into contiguous frame runs, and keys graph snapshots by location and timestamp. This
|
|
153
|
+
prevents observations from separate roads or recording sessions from entering the same trajectory
|
|
154
|
+
or proximity graph.
|
|
155
|
+
|
|
156
|
+
Derived speed and acceleration are a transparent compatibility fallback. Report their use and do
|
|
157
|
+
not treat results based on derived kinematics as directly comparable to experiments that used the
|
|
158
|
+
original NGSIM `v_Vel` and `v_Acc` measurements.
|
|
159
|
+
|
|
160
|
+
See [`docs/DATA.md`](docs/DATA.md) before combining NGSIM sites or changing units.
|
|
161
|
+
|
|
162
|
+
## Tests
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pytest
|
|
166
|
+
ruff check .
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The test suite checks class encoding, radius-graph construction, split gaps, tensor shapes, fusion
|
|
170
|
+
weight normalization, and an end-to-end synthetic run.
|
|
171
|
+
|
|
172
|
+
## Result-reporting rule
|
|
173
|
+
|
|
174
|
+
Only files produced under `results/regenerated/` by a completed run may be described as regenerated
|
|
175
|
+
results. Report the raw-data SHA-256 hash, configuration, seed, split policy, and checkpoint with
|
|
176
|
+
every result. Do not copy the 99.90% accuracy or 1.00 F1 claim into a new report unless this aligned
|
|
177
|
+
pipeline independently produces it on the intended held-out data.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "v2x-risk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Reproducible LSTM-GAT model for V2X traffic anomaly and collision-risk classification"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{name = "Benjamin Quito"}]
|
|
12
|
+
license = "MIT"
|
|
13
|
+
keywords = [
|
|
14
|
+
"v2x",
|
|
15
|
+
"traffic-anomaly-detection",
|
|
16
|
+
"collision-risk",
|
|
17
|
+
"lstm",
|
|
18
|
+
"graph-attention-network",
|
|
19
|
+
"reproducible-research",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"Intended Audience :: Education",
|
|
24
|
+
"Intended Audience :: Science/Research",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"numpy>=1.26,<3",
|
|
32
|
+
"openpyxl>=3.1,<4",
|
|
33
|
+
"pandas>=2.1,<3",
|
|
34
|
+
"PyYAML>=6,<7",
|
|
35
|
+
"scikit-learn>=1.3,<2",
|
|
36
|
+
"torch>=2.2,<3",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=8,<9",
|
|
42
|
+
"ruff>=0.9,<1",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
v2x-preprocess = "v2x_risk.preprocess:main"
|
|
47
|
+
v2x-train = "v2x_risk.train:main"
|
|
48
|
+
v2x-evaluate = "v2x_risk.evaluate:main"
|
|
49
|
+
v2x-report = "v2x_risk.report:main"
|
|
50
|
+
v2x-smoke = "v2x_risk.smoke:main"
|
|
51
|
+
|
|
52
|
+
[project.urls]
|
|
53
|
+
Homepage = "https://github.com/benjaminquito/V2X"
|
|
54
|
+
Repository = "https://github.com/benjaminquito/V2X"
|
|
55
|
+
Paper = "https://doi.org/10.4236/ojsst.2026.163010"
|
|
56
|
+
Issues = "https://github.com/benjaminquito/V2X/issues"
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.packages.find]
|
|
59
|
+
where = ["src"]
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
addopts = "-q"
|
|
63
|
+
testpaths = ["tests"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 100
|
|
67
|
+
target-version = "py310"
|
v2x_risk-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigError(ValueError):
|
|
11
|
+
"""Raised when a configuration is incomplete or internally inconsistent."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_config(path: str | Path) -> dict[str, Any]:
|
|
15
|
+
config_path = Path(path)
|
|
16
|
+
with config_path.open("r", encoding="utf-8") as handle:
|
|
17
|
+
config = yaml.safe_load(handle)
|
|
18
|
+
if not isinstance(config, dict):
|
|
19
|
+
raise ConfigError(f"Configuration must be a mapping: {config_path}")
|
|
20
|
+
validate_config(config)
|
|
21
|
+
return config
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def validate_config(config: dict[str, Any]) -> None:
|
|
25
|
+
for section in ("data", "model", "training"):
|
|
26
|
+
if section not in config:
|
|
27
|
+
raise ConfigError(f"Missing required configuration section: {section}")
|
|
28
|
+
|
|
29
|
+
data = config["data"]
|
|
30
|
+
model = config["model"]
|
|
31
|
+
split = data.get("split", {})
|
|
32
|
+
ratios = [float(split.get(name, -1)) for name in ("train", "validation", "test")]
|
|
33
|
+
if any(value <= 0 for value in ratios) or abs(sum(ratios) - 1.0) > 1e-9:
|
|
34
|
+
raise ConfigError("data.split train/validation/test values must be positive and sum to 1")
|
|
35
|
+
if int(data.get("sequence_length", 0)) < 2:
|
|
36
|
+
raise ConfigError("data.sequence_length must be at least 2")
|
|
37
|
+
if float(data.get("graph_radius_m", 0)) <= 0:
|
|
38
|
+
raise ConfigError("data.graph_radius_m must be positive")
|
|
39
|
+
if len(data.get("feature_columns", [])) != int(model.get("input_dim", -1)):
|
|
40
|
+
raise ConfigError("model.input_dim must match the number of data.feature_columns")
|
|
41
|
+
if int(model.get("num_classes", 0)) != 3:
|
|
42
|
+
raise ConfigError("This implementation requires exactly three risk classes")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def with_overrides(config: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]:
|
|
46
|
+
"""Return a deep copy with recursively merged overrides."""
|
|
47
|
+
|
|
48
|
+
merged = deepcopy(config)
|
|
49
|
+
|
|
50
|
+
def merge(target: dict[str, Any], source: dict[str, Any]) -> None:
|
|
51
|
+
for key, value in source.items():
|
|
52
|
+
if isinstance(value, dict) and isinstance(target.get(key), dict):
|
|
53
|
+
merge(target[key], value)
|
|
54
|
+
else:
|
|
55
|
+
target[key] = value
|
|
56
|
+
|
|
57
|
+
merge(merged, overrides)
|
|
58
|
+
validate_config(merged)
|
|
59
|
+
return merged
|