Initial Commit
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +12 -0
- 3D-SEMMN/.DS_Store +0 -0
- 3D-SEMMN/README.md +105 -0
- 3D-SEMMN/__pycache__/train.cpython-312.pyc +0 -0
- 3D-SEMMN/__pycache__/visualize.cpython-312.pyc +0 -0
- 3D-SEMMN/config/default.yaml +51 -0
- 3D-SEMMN/config/light.yaml +17 -0
- 3D-SEMMN/data/MNIST/raw/t10k-images-idx3-ubyte +3 -0
- 3D-SEMMN/data/MNIST/raw/t10k-images-idx3-ubyte.gz +3 -0
- 3D-SEMMN/data/MNIST/raw/t10k-labels-idx1-ubyte +0 -0
- 3D-SEMMN/data/MNIST/raw/t10k-labels-idx1-ubyte.gz +3 -0
- 3D-SEMMN/data/MNIST/raw/train-images-idx3-ubyte +3 -0
- 3D-SEMMN/data/MNIST/raw/train-images-idx3-ubyte.gz +3 -0
- 3D-SEMMN/data/MNIST/raw/train-labels-idx1-ubyte +0 -0
- 3D-SEMMN/data/MNIST/raw/train-labels-idx1-ubyte.gz +3 -0
- 3D-SEMMN/outputs/.DS_Store +0 -0
- 3D-SEMMN/outputs/best.pt +3 -0
- 3D-SEMMN/outputs/figures/manifold_pca.png +3 -0
- 3D-SEMMN/outputs/figures/metrics.png +0 -0
- 3D-SEMMN/outputs/figures/neuron_clusters_3d.png +3 -0
- 3D-SEMMN/outputs/figures/neuron_grid_interactive.html +0 -0
- 3D-SEMMN/outputs/figures/neuron_module_audio.png +3 -0
- 3D-SEMMN/outputs/figures/neuron_module_hub.png +3 -0
- 3D-SEMMN/outputs/figures/neuron_module_other.png +3 -0
- 3D-SEMMN/outputs/figures/neuron_module_vision.png +3 -0
- 3D-SEMMN/outputs/figures/neuron_modules_all.png +3 -0
- 3D-SEMMN/outputs/figures/route_edges.csv +13 -0
- 3D-SEMMN/outputs/figures/route_map_audio_2d.png +3 -0
- 3D-SEMMN/outputs/figures/route_map_interactive.html +7 -0
- 3D-SEMMN/outputs/figures/route_map_vision_2d.png +3 -0
- 3D-SEMMN/outputs/figures/route_nodes.csv +241 -0
- 3D-SEMMN/outputs/history.json +37 -0
- 3D-SEMMN/requirements.txt +10 -0
- 3D-SEMMN/semmn/__init__.py +6 -0
- 3D-SEMMN/semmn/__pycache__/__init__.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/columns.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/config.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/connectivity.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/dataset.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/encoders.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/grid.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/hub.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/losses.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/manifold.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/__pycache__/model.cpython-312.pyc +0 -0
- 3D-SEMMN/semmn/columns.py +33 -0
- 3D-SEMMN/semmn/config.py +121 -0
- 3D-SEMMN/semmn/connectivity.py +98 -0
- 3D-SEMMN/semmn/dataset.py +135 -0
- 3D-SEMMN/semmn/encoders.py +66 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,15 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
3D_SEMMN_final.pdf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
3D-SEMMN/data/MNIST/raw/t10k-images-idx3-ubyte filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
3D-SEMMN/data/MNIST/raw/train-images-idx3-ubyte filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
3D-SEMMN/outputs/figures/manifold_pca.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
3D-SEMMN/outputs/figures/neuron_clusters_3d.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
3D-SEMMN/outputs/figures/neuron_module_audio.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
3D-SEMMN/outputs/figures/neuron_module_hub.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
3D-SEMMN/outputs/figures/neuron_module_other.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
3D-SEMMN/outputs/figures/neuron_module_vision.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
3D-SEMMN/outputs/figures/neuron_modules_all.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
3D-SEMMN/outputs/figures/route_map_audio_2d.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
3D-SEMMN/outputs/figures/route_map_vision_2d.png filter=lfs diff=lfs merge=lfs -text
|
3D-SEMMN/.DS_Store
ADDED
|
Binary file (8.2 kB). View file
|
|
|
3D-SEMMN/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 3D-SEMMN (Prototype)
|
| 2 |
+
|
| 3 |
+
Minimal but fully functional PyTorch 2.3+ prototype of a **3D Spatially-Embedded Multimodal Manifold Network** with:
|
| 4 |
+
|
| 5 |
+
- fixed 3D neuron coordinates (`100 x 100 x 50` in `config/default.yaml`)
|
| 6 |
+
- 100 columnar modules (dense intra-column, sparse inter-column)
|
| 7 |
+
- exponential distance-penalized sparse connectivity (`torch.sparse`)
|
| 8 |
+
- Hebbian/STDP update every 100 steps
|
| 9 |
+
- shared perceptual manifold hub with cross-modal attention
|
| 10 |
+
- manifold regularization (latent `d=8`) + geometric twist layer (Cayley rotation)
|
| 11 |
+
- multimodal losses: classification + contrastive + reconstruction + imagination cycle
|
| 12 |
+
- ablations for spatial penalty and manifold loss
|
| 13 |
+
|
| 14 |
+
This code is designed for readability and modular experimentation rather than full biological fidelity.
|
| 15 |
+
|
| 16 |
+
## Biological Inspirations
|
| 17 |
+
|
| 18 |
+
- **seRNN (Achterberg et al., 2023)**: spatial embedding + wiring-cost constraints for modular sparse topology.
|
| 19 |
+
- **MSeNN-style multimodal ideas (2021)**: cross-modal coupling and biologically motivated integration.
|
| 20 |
+
- **Gallego et al. (2017)**: low-dimensional population manifolds.
|
| 21 |
+
|
| 22 |
+
## Project Layout
|
| 23 |
+
|
| 24 |
+
- `semmn/config.py`: dataclass config + YAML loader
|
| 25 |
+
- `semmn/grid.py`: 3D coordinates, lobe masks, column assignment
|
| 26 |
+
- `semmn/connectivity.py`: sparse distance-decay edges + STDP + energy proxy
|
| 27 |
+
- `semmn/columns.py`: low-rank dense intra-column dynamics
|
| 28 |
+
- `semmn/encoders.py`: vision CNN and audio 1D-conv encoder
|
| 29 |
+
- `semmn/manifold.py`: manifold autoencoder and geometric twist
|
| 30 |
+
- `semmn/hub.py`: shared manifold hub + attention + imagination decoders
|
| 31 |
+
- `semmn/losses.py`: full training objective
|
| 32 |
+
- `semmn/model.py`: full SEMMN forward pass
|
| 33 |
+
- `semmn/dataset.py`: AV-MNIST loading with synthetic fallback
|
| 34 |
+
- `train.py`: training/eval/checkpointing
|
| 35 |
+
- `visualize.py`: 3D grid, manifold PCA, metrics plots
|
| 36 |
+
|
| 37 |
+
## Quick Start
|
| 38 |
+
|
| 39 |
+
```bash
|
| 40 |
+
python -m venv .venv
|
| 41 |
+
source .venv/bin/activate
|
| 42 |
+
pip install -r requirements.txt
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
Train (light config, recommended first):
|
| 46 |
+
|
| 47 |
+
```bash
|
| 48 |
+
python train.py --config config/light.yaml --device cuda
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
Train (full 500K-neuron spatial grid metadata):
|
| 52 |
+
|
| 53 |
+
```bash
|
| 54 |
+
python train.py --config config/default.yaml --device cuda
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
Generate figures:
|
| 58 |
+
|
| 59 |
+
```bash
|
| 60 |
+
python visualize.py \
|
| 61 |
+
--config config/light.yaml \
|
| 62 |
+
--checkpoint outputs/best.pt \
|
| 63 |
+
--history outputs/history.json \
|
| 64 |
+
--device cuda
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
## Config Notes
|
| 68 |
+
|
| 69 |
+
- `config/default.yaml`: full spatial grid (`100x100x50`)
|
| 70 |
+
- `config/light.yaml`: faster profile (`100x100x10`) for rapid iteration
|
| 71 |
+
- Ablations:
|
| 72 |
+
- `loss.use_spatial_penalty: false`
|
| 73 |
+
- `loss.use_manifold_loss: false`
|
| 74 |
+
|
| 75 |
+
## Reported Metrics
|
| 76 |
+
|
| 77 |
+
`train.py` logs:
|
| 78 |
+
|
| 79 |
+
- classification loss and total loss
|
| 80 |
+
- validation classification accuracy
|
| 81 |
+
- cross-modal retrieval accuracy (vision->audio nearest match in batch)
|
| 82 |
+
- active sparse parameters (`nnz` proxy)
|
| 83 |
+
- energy proxy (`mean(|w| * distance)`)
|
| 84 |
+
|
| 85 |
+
## Extension Notes
|
| 86 |
+
|
| 87 |
+
### Full spiking variant (Brian2 / snnTorch)
|
| 88 |
+
|
| 89 |
+
1. Replace rate-based state updates (`tanh`) with LIF/AdEx neuron dynamics.
|
| 90 |
+
2. Convert STDP update from rate Hebbian correlation to spike-timing windows.
|
| 91 |
+
3. Keep 3D coordinates and sparse wiring graph unchanged for fair ablations.
|
| 92 |
+
|
| 93 |
+
### Neuromorphic export direction
|
| 94 |
+
|
| 95 |
+
1. Map columns to compute cores (Loihi/SpiNNaker style partitioning).
|
| 96 |
+
2. Convert sparse inter-column edges to routing tables.
|
| 97 |
+
3. Quantize state variables and weights (8-bit or mixed precision).
|
| 98 |
+
4. Use event-driven updates for power-efficient deployment.
|
| 99 |
+
|
| 100 |
+
## Practical Runtime Guidance
|
| 101 |
+
|
| 102 |
+
- Start with `config/light.yaml` to confirm setup and debug quickly.
|
| 103 |
+
- Increase `training.batch_size` and `model.recurrent_steps` gradually.
|
| 104 |
+
- Keep STDP interval at `100` to limit plasticity overhead.
|
| 105 |
+
- Use mixed precision (`training.amp: true`) on CUDA devices.
|
3D-SEMMN/__pycache__/train.cpython-312.pyc
ADDED
|
Binary file (8.79 kB). View file
|
|
|
3D-SEMMN/__pycache__/visualize.cpython-312.pyc
ADDED
|
Binary file (28 kB). View file
|
|
|
3D-SEMMN/config/default.yaml
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
seed: 42
|
| 2 |
+
grid:
|
| 3 |
+
dims: [100, 100, 50]
|
| 4 |
+
n_columns_xy: [10, 10]
|
| 5 |
+
vision_y_ratio: 0.30
|
| 6 |
+
audio_y_ratio: 0.70
|
| 7 |
+
hub_y_range: [0.40, 0.60]
|
| 8 |
+
hub_z_range: [0.30, 0.70]
|
| 9 |
+
|
| 10 |
+
model:
|
| 11 |
+
state_dim: 32
|
| 12 |
+
latent_dim: 8
|
| 13 |
+
vision_feature_dim: 256
|
| 14 |
+
audio_feature_dim: 256
|
| 15 |
+
recurrent_steps: 6
|
| 16 |
+
attention_heads: 4
|
| 17 |
+
|
| 18 |
+
connectivity:
|
| 19 |
+
lambda_decay: 5.0
|
| 20 |
+
inter_density: 0.08
|
| 21 |
+
intra_density_boost: 2.0
|
| 22 |
+
min_weight: 0.0
|
| 23 |
+
max_weight: 1.5
|
| 24 |
+
init_weight_scale: 0.15
|
| 25 |
+
stdp_interval: 100
|
| 26 |
+
stdp_lr: 0.01
|
| 27 |
+
anti_hebbian: 0.05
|
| 28 |
+
|
| 29 |
+
loss:
|
| 30 |
+
contrastive_weight: 1.0
|
| 31 |
+
reconstruction_weight: 0.5
|
| 32 |
+
imagination_weight: 0.3
|
| 33 |
+
manifold_weight: 0.1
|
| 34 |
+
spatial_penalty_weight: 0.0005
|
| 35 |
+
temperature: 0.1
|
| 36 |
+
use_spatial_penalty: true
|
| 37 |
+
use_manifold_loss: true
|
| 38 |
+
|
| 39 |
+
training:
|
| 40 |
+
dataset_source: synthetic
|
| 41 |
+
batch_size: 64
|
| 42 |
+
epochs: 10
|
| 43 |
+
lr: 0.001
|
| 44 |
+
weight_decay: 0.0001
|
| 45 |
+
num_workers: 4
|
| 46 |
+
log_interval: 50
|
| 47 |
+
val_batches: 100
|
| 48 |
+
amp: true
|
| 49 |
+
|
| 50 |
+
paths:
|
| 51 |
+
output_dir: outputs
|
3D-SEMMN/config/light.yaml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
seed: 42
|
| 2 |
+
grid:
|
| 3 |
+
dims: [100, 100, 10]
|
| 4 |
+
n_columns_xy: [10, 10]
|
| 5 |
+
|
| 6 |
+
model:
|
| 7 |
+
recurrent_steps: 4
|
| 8 |
+
state_dim: 24
|
| 9 |
+
|
| 10 |
+
connectivity:
|
| 11 |
+
inter_density: 0.10
|
| 12 |
+
|
| 13 |
+
training:
|
| 14 |
+
batch_size: 128
|
| 15 |
+
epochs: 10
|
| 16 |
+
num_workers: 2
|
| 17 |
+
log_interval: 20
|
3D-SEMMN/data/MNIST/raw/t10k-images-idx3-ubyte
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0fa7898d509279e482958e8ce81c8e77db3f2f8254e26661ceb7762c4d494ce7
|
| 3 |
+
size 7840016
|
3D-SEMMN/data/MNIST/raw/t10k-images-idx3-ubyte.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8d422c7b0a1c1c79245a5bcf07fe86e33eeafee792b84584aec276f5a2dbc4e6
|
| 3 |
+
size 1648877
|
3D-SEMMN/data/MNIST/raw/t10k-labels-idx1-ubyte
ADDED
|
Binary file (10 kB). View file
|
|
|
3D-SEMMN/data/MNIST/raw/t10k-labels-idx1-ubyte.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f7ae60f92e00ec6debd23a6088c31dbd2371eca3ffa0defaefb259924204aec6
|
| 3 |
+
size 4542
|
3D-SEMMN/data/MNIST/raw/train-images-idx3-ubyte
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ba891046e6505d7aadcbbe25680a0738ad16aec93bde7f9b65e87a2fc25776db
|
| 3 |
+
size 47040016
|
3D-SEMMN/data/MNIST/raw/train-images-idx3-ubyte.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:440fcabf73cc546fa21475e81ea370265605f56be210a4024d2ca8f203523609
|
| 3 |
+
size 9912422
|
3D-SEMMN/data/MNIST/raw/train-labels-idx1-ubyte
ADDED
|
Binary file (60 kB). View file
|
|
|
3D-SEMMN/data/MNIST/raw/train-labels-idx1-ubyte.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3552534a0a558bbed6aed32b30c495cca23d567ec52cac8be1a0730e8010255c
|
| 3 |
+
size 28881
|
3D-SEMMN/outputs/.DS_Store
ADDED
|
Binary file (8.2 kB). View file
|
|
|
3D-SEMMN/outputs/best.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:66d1a8bbb97532ad3ea4005ad99e6dbda5b5cced507c3d5c6961fb52b0b1b1a2
|
| 3 |
+
size 7554761
|
3D-SEMMN/outputs/figures/manifold_pca.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/metrics.png
ADDED
|
3D-SEMMN/outputs/figures/neuron_clusters_3d.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/neuron_grid_interactive.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
3D-SEMMN/outputs/figures/neuron_module_audio.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/neuron_module_hub.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/neuron_module_other.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/neuron_module_vision.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/neuron_modules_all.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/route_edges.csv
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
step,source,target,strength,modality
|
| 2 |
+
0.0,80.0,90.0,0.014531210064888,vision
|
| 3 |
+
0.0,10.0,0.0,0.0044663818553090096,vision
|
| 4 |
+
0.0,22.0,11.0,0.003179906401783228,vision
|
| 5 |
+
1.0,80.0,90.0,0.013347183354198933,vision
|
| 6 |
+
1.0,10.0,0.0,0.004102454055100679,vision
|
| 7 |
+
1.0,22.0,11.0,0.002920802216976881,vision
|
| 8 |
+
2.0,80.0,90.0,0.012341569177806377,vision
|
| 9 |
+
2.0,10.0,0.0,0.0037933634594082832,vision
|
| 10 |
+
2.0,22.0,11.0,0.0027007409371435642,vision
|
| 11 |
+
3.0,80.0,90.0,0.011476870626211166,vision
|
| 12 |
+
3.0,10.0,0.0,0.0035275856498628855,vision
|
| 13 |
+
3.0,22.0,11.0,0.0025115162134170532,vision
|
3D-SEMMN/outputs/figures/route_map_audio_2d.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/route_map_interactive.html
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<html>
|
| 2 |
+
<head><meta charset="utf-8" /></head>
|
| 3 |
+
<body>
|
| 4 |
+
<div> <script type="text/javascript">window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
|
| 5 |
+
<script charset="utf-8" src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script> <div id="8c11a467-ec1f-422e-a480-88b89e6970e8" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script type="text/javascript"> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("8c11a467-ec1f-422e-a480-88b89e6970e8")) { Plotly.newPlot( "8c11a467-ec1f-422e-a480-88b89e6970e8", [{"hovertemplate":"column=%{text}\u003cextra\u003e\u003c\u002fextra\u003e","marker":{"color":"#D0D0D0","opacity":0.35,"size":7},"mode":"markers","name":"all_columns","text":["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","91","92","93","94","95","96","97","98","99"],"x":[4.5,4.5,4.5,4.5,4.5,4.5,4.5,4.5,4.5,4.5,14.5,14.5,14.5,14.5,14.5,14.5,14.5,14.5,14.5,14.5,24.5,24.5,24.5,24.5,24.5,24.5,24.5,24.5,24.5,24.5,34.5,34.5,34.5,34.5,34.5,34.5,34.5,34.5,34.5,34.5,44.5,44.5,44.5,44.5,44.5,44.5,44.5,44.5,44.5,44.5,54.5,54.5,54.5,54.5,54.5,54.5,54.5,54.5,54.5,54.5,64.5,64.5,64.5,64.5,64.5,64.5,64.5,64.5,64.5,64.5,74.5,74.5,74.5,74.5,74.5,74.5,74.5,74.5,74.5,74.5,84.5,84.5,84.5,84.5,84.5,84.5,84.5,84.5,84.5,84.5,94.5,94.5,94.5,94.5,94.5,94.5,94.5,94.5,94.5,94.5],"y":[4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5,4.5,14.5,24.5,34.5,44.5,54.5,64.5,74.5,84.5,94.5],"type":"scatter"},{"hoverinfo":"skip","line":{"color":"#1f77b4","width":2},"mode":"lines","name":"vision_route","opacity":0.75,"x":[84.5,94.5,null,14.5,4.5,null,24.5,14.5,null,84.5,94.5,null,14.5,4.5,null,24.5,14.5,null,84.5,94.5,null,14.5,4.5,null,24.5,14.5,null,84.5,94.5,null,14.5,4.5,null,24.5,14.5,null],"y":[4.5,4.5,null,4.5,4.5,null,24.5,14.5,null,4.5,4.5,null,4.5,4.5,null,24.5,14.5,null,4.5,4.5,null,4.5,4.5,null,24.5,14.5,null,4.5,4.5,null,4.5,4.5,null,24.5,14.5,null],"type":"scatter"},{"hoverinfo":"skip","line":{"color":"#ff7f0e","width":2},"mode":"lines","name":"audio_route","opacity":0.75,"x":[],"y":[],"type":"scatter"}], {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"legend":{"orientation":"h"},"title":{"text":"2D Route Simulation Across Column Graph (digit 7)"},"xaxis":{"title":{"text":"Column X"}},"yaxis":{"title":{"text":"Column Y"}},"updatemenus":[{"buttons":[{"args":[{"visible":[true,true,true]}],"label":"Show both","method":"update"},{"args":[{"visible":[true,true,false]}],"label":"Vision route","method":"update"},{"args":[{"visible":[true,false,true]}],"label":"Audio route","method":"update"}],"type":"dropdown","x":0.01,"y":1.12}]}, {"responsive": true} ) }; </script> </div>
|
| 6 |
+
</body>
|
| 7 |
+
</html>
|
3D-SEMMN/outputs/figures/route_map_vision_2d.png
ADDED
|
Git LFS Details
|
3D-SEMMN/outputs/figures/route_nodes.csv
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
step,column,activity,modality
|
| 2 |
+
0.0,0.0,0.02956058643758297,vision
|
| 3 |
+
0.0,1.0,0.0409482941031456,vision
|
| 4 |
+
0.0,2.0,0.026564355939626694,vision
|
| 5 |
+
0.0,10.0,0.040411435067653656,vision
|
| 6 |
+
0.0,11.0,0.02947550266981125,vision
|
| 7 |
+
0.0,12.0,0.0309763066470623,vision
|
| 8 |
+
0.0,20.0,0.027739018201828003,vision
|
| 9 |
+
0.0,21.0,0.023138199001550674,vision
|
| 10 |
+
0.0,22.0,0.03138161823153496,vision
|
| 11 |
+
0.0,30.0,0.03375103697180748,vision
|
| 12 |
+
0.0,31.0,0.034890495240688324,vision
|
| 13 |
+
0.0,32.0,0.025857770815491676,vision
|
| 14 |
+
0.0,40.0,0.03491085767745972,vision
|
| 15 |
+
0.0,41.0,0.04038066044449806,vision
|
| 16 |
+
0.0,42.0,0.030116962268948555,vision
|
| 17 |
+
0.0,50.0,0.029867637902498245,vision
|
| 18 |
+
0.0,51.0,0.036975305527448654,vision
|
| 19 |
+
0.0,52.0,0.04041629657149315,vision
|
| 20 |
+
0.0,60.0,0.03777403011918068,vision
|
| 21 |
+
0.0,61.0,0.02775333821773529,vision
|
| 22 |
+
0.0,62.0,0.03777413070201874,vision
|
| 23 |
+
0.0,70.0,0.029612775892019272,vision
|
| 24 |
+
0.0,71.0,0.03889508917927742,vision
|
| 25 |
+
0.0,72.0,0.03445589914917946,vision
|
| 26 |
+
0.0,80.0,0.03937682509422302,vision
|
| 27 |
+
0.0,81.0,0.027864739298820496,vision
|
| 28 |
+
0.0,82.0,0.027492109686136246,vision
|
| 29 |
+
0.0,90.0,0.036627497524023056,vision
|
| 30 |
+
0.0,91.0,0.03462231159210205,vision
|
| 31 |
+
0.0,92.0,0.04038887843489647,vision
|
| 32 |
+
1.0,0.0,0.04356175288558006,vision
|
| 33 |
+
1.0,1.0,0.03761175647377968,vision
|
| 34 |
+
1.0,2.0,0.02439984865486622,vision
|
| 35 |
+
1.0,10.0,0.03711864724755287,vision
|
| 36 |
+
1.0,11.0,0.038757000118494034,vision
|
| 37 |
+
1.0,12.0,0.028452306985855103,vision
|
| 38 |
+
1.0,20.0,0.025478797033429146,vision
|
| 39 |
+
1.0,21.0,0.021252859383821487,vision
|
| 40 |
+
1.0,22.0,0.028824592009186745,vision
|
| 41 |
+
1.0,30.0,0.03100094571709633,vision
|
| 42 |
+
1.0,31.0,0.03204755857586861,vision
|
| 43 |
+
1.0,32.0,0.02375083602964878,vision
|
| 44 |
+
1.0,40.0,0.032066263258457184,vision
|
| 45 |
+
1.0,41.0,0.037090376019477844,vision
|
| 46 |
+
1.0,42.0,0.027662981301546097,vision
|
| 47 |
+
1.0,50.0,0.02743397280573845,vision
|
| 48 |
+
1.0,51.0,0.033962495625019073,vision
|
| 49 |
+
1.0,52.0,0.03712311014533043,vision
|
| 50 |
+
1.0,60.0,0.03469613939523697,vision
|
| 51 |
+
1.0,61.0,0.025491949170827866,vision
|
| 52 |
+
1.0,62.0,0.03469623252749443,vision
|
| 53 |
+
1.0,70.0,0.0271998792886734,vision
|
| 54 |
+
1.0,71.0,0.035725854337215424,vision
|
| 55 |
+
1.0,72.0,0.03164837509393692,vision
|
| 56 |
+
1.0,80.0,0.03616833686828613,vision
|
| 57 |
+
1.0,81.0,0.025594275444746017,vision
|
| 58 |
+
1.0,82.0,0.025252006947994232,vision
|
| 59 |
+
1.0,90.0,0.0870317593216896,vision
|
| 60 |
+
1.0,91.0,0.03180122748017311,vision
|
| 61 |
+
1.0,92.0,0.03709792718291283,vision
|
| 62 |
+
2.0,0.0,0.055453140288591385,vision
|
| 63 |
+
2.0,1.0,0.03477798402309418,vision
|
| 64 |
+
2.0,2.0,0.02256149612367153,vision
|
| 65 |
+
2.0,10.0,0.03432202339172363,vision
|
| 66 |
+
2.0,11.0,0.04663990065455437,vision
|
| 67 |
+
2.0,12.0,0.026308631524443626,vision
|
| 68 |
+
2.0,20.0,0.023559153079986572,vision
|
| 69 |
+
2.0,21.0,0.019651610404253006,vision
|
| 70 |
+
2.0,22.0,0.026652866974473,vision
|
| 71 |
+
2.0,30.0,0.028665248304605484,vision
|
| 72 |
+
2.0,31.0,0.02963300608098507,vision
|
| 73 |
+
2.0,32.0,0.021961383521556854,vision
|
| 74 |
+
2.0,40.0,0.02965030074119568,vision
|
| 75 |
+
2.0,41.0,0.034295883029699326,vision
|
| 76 |
+
2.0,42.0,0.025578774511814117,vision
|
| 77 |
+
2.0,50.0,0.025367021560668945,vision
|
| 78 |
+
2.0,51.0,0.031403668224811554,vision
|
| 79 |
+
2.0,52.0,0.03432615101337433,vision
|
| 80 |
+
2.0,60.0,0.03208203613758087,vision
|
| 81 |
+
2.0,61.0,0.0235713142901659,vision
|
| 82 |
+
2.0,62.0,0.032082121819257736,vision
|
| 83 |
+
2.0,70.0,0.025150563567876816,vision
|
| 84 |
+
2.0,71.0,0.03303416818380356,vision
|
| 85 |
+
2.0,72.0,0.029263898730278015,vision
|
| 86 |
+
2.0,80.0,0.03344331309199333,vision
|
| 87 |
+
2.0,81.0,0.023665931075811386,vision
|
| 88 |
+
2.0,82.0,0.023349450901150703,vision
|
| 89 |
+
2.0,90.0,0.12984082102775574,vision
|
| 90 |
+
2.0,91.0,0.02940523438155651,vision
|
| 91 |
+
2.0,92.0,0.03430286794900894,vision
|
| 92 |
+
3.0,0.0,0.06567821651697159,vision
|
| 93 |
+
3.0,1.0,0.03234130144119263,vision
|
| 94 |
+
3.0,2.0,0.020980749279260635,vision
|
| 95 |
+
3.0,10.0,0.03191728889942169,vision
|
| 96 |
+
3.0,11.0,0.05341819301247597,vision
|
| 97 |
+
3.0,12.0,0.024465344846248627,vision
|
| 98 |
+
3.0,20.0,0.021908504888415337,vision
|
| 99 |
+
3.0,21.0,0.018274743109941483,vision
|
| 100 |
+
3.0,22.0,0.024785460904240608,vision
|
| 101 |
+
3.0,30.0,0.026656849309802055,vision
|
| 102 |
+
3.0,31.0,0.027556801214814186,vision
|
| 103 |
+
3.0,32.0,0.020422684028744698,vision
|
| 104 |
+
3.0,40.0,0.027572883293032646,vision
|
| 105 |
+
3.0,41.0,0.03189297765493393,vision
|
| 106 |
+
3.0,42.0,0.023786624893546104,vision
|
| 107 |
+
3.0,50.0,0.023589707911014557,vision
|
| 108 |
+
3.0,51.0,0.029203403741121292,vision
|
| 109 |
+
3.0,52.0,0.03192112594842911,vision
|
| 110 |
+
3.0,60.0,0.029834242537617683,vision
|
| 111 |
+
3.0,61.0,0.021919816732406616,vision
|
| 112 |
+
3.0,62.0,0.0298343226313591,vision
|
| 113 |
+
3.0,70.0,0.023388417437672615,vision
|
| 114 |
+
3.0,71.0,0.030719663947820663,vision
|
| 115 |
+
3.0,72.0,0.027213554829359055,vision
|
| 116 |
+
3.0,80.0,0.031100144609808922,vision
|
| 117 |
+
3.0,81.0,0.02200780250132084,vision
|
| 118 |
+
3.0,82.0,0.02171349711716175,vision
|
| 119 |
+
3.0,90.0,0.1666511446237564,vision
|
| 120 |
+
3.0,91.0,0.02734498865902424,vision
|
| 121 |
+
3.0,92.0,0.03189947456121445,vision
|
| 122 |
+
0.0,7.0,0.03473106026649475,audio
|
| 123 |
+
0.0,8.0,0.02794949896633625,audio
|
| 124 |
+
0.0,9.0,0.027408087626099586,audio
|
| 125 |
+
0.0,17.0,0.038603682070970535,audio
|
| 126 |
+
0.0,18.0,0.036946672946214676,audio
|
| 127 |
+
0.0,19.0,0.02548990212380886,audio
|
| 128 |
+
0.0,27.0,0.03835974261164665,audio
|
| 129 |
+
0.0,28.0,0.04123900458216667,audio
|
| 130 |
+
0.0,29.0,0.030051397159695625,audio
|
| 131 |
+
0.0,37.0,0.03510997071862221,audio
|
| 132 |
+
0.0,38.0,0.03655286878347397,audio
|
| 133 |
+
0.0,39.0,0.024867547675967216,audio
|
| 134 |
+
0.0,47.0,0.039415426552295685,audio
|
| 135 |
+
0.0,48.0,0.0356949046254158,audio
|
| 136 |
+
0.0,49.0,0.04190101474523544,audio
|
| 137 |
+
0.0,57.0,0.033966340124607086,audio
|
| 138 |
+
0.0,58.0,0.03714350610971451,audio
|
| 139 |
+
0.0,59.0,0.03321348875761032,audio
|
| 140 |
+
0.0,67.0,0.030020426958799362,audio
|
| 141 |
+
0.0,68.0,0.034492406994104385,audio
|
| 142 |
+
0.0,69.0,0.02616014890372753,audio
|
| 143 |
+
0.0,77.0,0.03146708384156227,audio
|
| 144 |
+
0.0,78.0,0.04283866658806801,audio
|
| 145 |
+
0.0,79.0,0.02919699251651764,audio
|
| 146 |
+
0.0,87.0,0.03697360306978226,audio
|
| 147 |
+
0.0,88.0,0.03308973088860512,audio
|
| 148 |
+
0.0,89.0,0.02546405978500843,audio
|
| 149 |
+
0.0,97.0,0.03400813415646553,audio
|
| 150 |
+
0.0,98.0,0.02903173305094242,audio
|
| 151 |
+
0.0,99.0,0.028612902387976646,audio
|
| 152 |
+
1.0,7.0,0.03473105654120445,audio
|
| 153 |
+
1.0,8.0,0.0279494971036911,audio
|
| 154 |
+
1.0,9.0,0.027408085763454437,audio
|
| 155 |
+
1.0,17.0,0.03860367834568024,audio
|
| 156 |
+
1.0,18.0,0.03694666922092438,audio
|
| 157 |
+
1.0,19.0,0.02548990026116371,audio
|
| 158 |
+
1.0,27.0,0.038359738886356354,audio
|
| 159 |
+
1.0,28.0,0.04123900085687637,audio
|
| 160 |
+
1.0,29.0,0.030051395297050476,audio
|
| 161 |
+
1.0,37.0,0.03510996699333191,audio
|
| 162 |
+
1.0,38.0,0.03655286505818367,audio
|
| 163 |
+
1.0,39.0,0.024867547675967216,audio
|
| 164 |
+
1.0,47.0,0.039415426552295685,audio
|
| 165 |
+
1.0,48.0,0.035694900900125504,audio
|
| 166 |
+
1.0,49.0,0.04190101474523544,audio
|
| 167 |
+
1.0,57.0,0.03396633639931679,audio
|
| 168 |
+
1.0,58.0,0.03714350238442421,audio
|
| 169 |
+
1.0,59.0,0.03321348503232002,audio
|
| 170 |
+
1.0,67.0,0.030020423233509064,audio
|
| 171 |
+
1.0,68.0,0.03449240326881409,audio
|
| 172 |
+
1.0,69.0,0.026160147041082382,audio
|
| 173 |
+
1.0,77.0,0.03146708011627197,audio
|
| 174 |
+
1.0,78.0,0.04283866286277771,audio
|
| 175 |
+
1.0,79.0,0.02919699065387249,audio
|
| 176 |
+
1.0,87.0,0.03697359934449196,audio
|
| 177 |
+
1.0,88.0,0.03308972716331482,audio
|
| 178 |
+
1.0,89.0,0.02546405978500843,audio
|
| 179 |
+
1.0,97.0,0.03400813415646553,audio
|
| 180 |
+
1.0,98.0,0.02903173118829727,audio
|
| 181 |
+
1.0,99.0,0.028612900525331497,audio
|
| 182 |
+
2.0,7.0,0.03473105654120445,audio
|
| 183 |
+
2.0,8.0,0.02794949896633625,audio
|
| 184 |
+
2.0,9.0,0.027408087626099586,audio
|
| 185 |
+
2.0,17.0,0.038603682070970535,audio
|
| 186 |
+
2.0,18.0,0.036946672946214676,audio
|
| 187 |
+
2.0,19.0,0.02548990212380886,audio
|
| 188 |
+
2.0,27.0,0.03835974261164665,audio
|
| 189 |
+
2.0,28.0,0.04123900458216667,audio
|
| 190 |
+
2.0,29.0,0.030051397159695625,audio
|
| 191 |
+
2.0,37.0,0.03510997071862221,audio
|
| 192 |
+
2.0,38.0,0.03655286878347397,audio
|
| 193 |
+
2.0,39.0,0.024867551401257515,audio
|
| 194 |
+
2.0,47.0,0.03941543027758598,audio
|
| 195 |
+
2.0,48.0,0.035694900900125504,audio
|
| 196 |
+
2.0,49.0,0.04190101847052574,audio
|
| 197 |
+
2.0,57.0,0.033966340124607086,audio
|
| 198 |
+
2.0,58.0,0.03714350610971451,audio
|
| 199 |
+
2.0,59.0,0.03321348503232002,audio
|
| 200 |
+
2.0,67.0,0.030020425096154213,audio
|
| 201 |
+
2.0,68.0,0.034492406994104385,audio
|
| 202 |
+
2.0,69.0,0.02616014890372753,audio
|
| 203 |
+
2.0,77.0,0.03146708384156227,audio
|
| 204 |
+
2.0,78.0,0.04283866658806801,audio
|
| 205 |
+
2.0,79.0,0.02919699251651764,audio
|
| 206 |
+
2.0,87.0,0.03697360306978226,audio
|
| 207 |
+
2.0,88.0,0.03308973088860512,audio
|
| 208 |
+
2.0,89.0,0.02546406351029873,audio
|
| 209 |
+
2.0,97.0,0.03400813788175583,audio
|
| 210 |
+
2.0,98.0,0.02903173305094242,audio
|
| 211 |
+
2.0,99.0,0.028612902387976646,audio
|
| 212 |
+
3.0,7.0,0.03473105654120445,audio
|
| 213 |
+
3.0,8.0,0.0279495008289814,audio
|
| 214 |
+
3.0,9.0,0.027408087626099586,audio
|
| 215 |
+
3.0,17.0,0.038603682070970535,audio
|
| 216 |
+
3.0,18.0,0.036946672946214676,audio
|
| 217 |
+
3.0,19.0,0.02548990212380886,audio
|
| 218 |
+
3.0,27.0,0.03835974261164665,audio
|
| 219 |
+
3.0,28.0,0.04123900458216667,audio
|
| 220 |
+
3.0,29.0,0.030051397159695625,audio
|
| 221 |
+
3.0,37.0,0.03510997071862221,audio
|
| 222 |
+
3.0,38.0,0.03655286878347397,audio
|
| 223 |
+
3.0,39.0,0.024867551401257515,audio
|
| 224 |
+
3.0,47.0,0.03941543027758598,audio
|
| 225 |
+
3.0,48.0,0.035694900900125504,audio
|
| 226 |
+
3.0,49.0,0.04190101847052574,audio
|
| 227 |
+
3.0,57.0,0.033966340124607086,audio
|
| 228 |
+
3.0,58.0,0.03714350610971451,audio
|
| 229 |
+
3.0,59.0,0.03321348503232002,audio
|
| 230 |
+
3.0,67.0,0.030020426958799362,audio
|
| 231 |
+
3.0,68.0,0.034492406994104385,audio
|
| 232 |
+
3.0,69.0,0.02616015076637268,audio
|
| 233 |
+
3.0,77.0,0.03146708384156227,audio
|
| 234 |
+
3.0,78.0,0.04283866658806801,audio
|
| 235 |
+
3.0,79.0,0.02919699251651764,audio
|
| 236 |
+
3.0,87.0,0.03697360306978226,audio
|
| 237 |
+
3.0,88.0,0.03308973088860512,audio
|
| 238 |
+
3.0,89.0,0.02546406351029873,audio
|
| 239 |
+
3.0,97.0,0.03400813788175583,audio
|
| 240 |
+
3.0,98.0,0.02903173491358757,audio
|
| 241 |
+
3.0,99.0,0.028612902387976646,audio
|
3D-SEMMN/outputs/history.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"epoch": 0,
|
| 4 |
+
"step": 0,
|
| 5 |
+
"loss_total": 6.665867805480957,
|
| 6 |
+
"loss_cls": 2.322234630584717,
|
| 7 |
+
"loss_contrastive": 4.330072402954102,
|
| 8 |
+
"loss_manifold": 0.007405777927488089,
|
| 9 |
+
"active_params": 2.0,
|
| 10 |
+
"energy_proxy": 0.17007668316364288
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"epoch": 0,
|
| 14 |
+
"step": 10,
|
| 15 |
+
"loss_total": 5.909451484680176,
|
| 16 |
+
"loss_cls": 2.2938742637634277,
|
| 17 |
+
"loss_contrastive": 3.5930726528167725,
|
| 18 |
+
"loss_manifold": 0.001445187022909522,
|
| 19 |
+
"active_params": 6.0,
|
| 20 |
+
"energy_proxy": 0.12415368109941483
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"epoch": 0,
|
| 24 |
+
"step": 20,
|
| 25 |
+
"loss_total": 5.335961818695068,
|
| 26 |
+
"loss_cls": 2.2907979488372803,
|
| 27 |
+
"loss_contrastive": 3.0338499546051025,
|
| 28 |
+
"loss_manifold": 0.0009116176515817642,
|
| 29 |
+
"active_params": 6.0,
|
| 30 |
+
"energy_proxy": 0.07187842577695847
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"epoch": 0,
|
| 34 |
+
"val_accuracy": 0.0,
|
| 35 |
+
"val_cross_modal_retrieval": 0.0
|
| 36 |
+
}
|
| 37 |
+
]
|
3D-SEMMN/requirements.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch>=2.3
|
| 2 |
+
torchvision>=0.18
|
| 3 |
+
numpy>=1.25
|
| 4 |
+
pandas>=2.2
|
| 5 |
+
PyYAML>=6.0
|
| 6 |
+
matplotlib>=3.8
|
| 7 |
+
plotly>=5.22
|
| 8 |
+
scikit-learn>=1.4
|
| 9 |
+
tqdm>=4.66
|
| 10 |
+
datasets>=2.20
|
3D-SEMMN/semmn/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""3D-SEMMN package."""
|
| 2 |
+
|
| 3 |
+
from semmn.config import SEMMNConfig
|
| 4 |
+
from semmn.model import SEMMNModel
|
| 5 |
+
|
| 6 |
+
__all__ = ["SEMMNConfig", "SEMMNModel"]
|
3D-SEMMN/semmn/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (314 Bytes). View file
|
|
|
3D-SEMMN/semmn/__pycache__/columns.cpython-312.pyc
ADDED
|
Binary file (2.38 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (7.13 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/connectivity.cpython-312.pyc
ADDED
|
Binary file (7.7 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/dataset.cpython-312.pyc
ADDED
|
Binary file (8.56 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/encoders.cpython-312.pyc
ADDED
|
Binary file (4.64 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/grid.cpython-312.pyc
ADDED
|
Binary file (5.31 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/hub.cpython-312.pyc
ADDED
|
Binary file (4.54 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/losses.cpython-312.pyc
ADDED
|
Binary file (3.45 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/manifold.cpython-312.pyc
ADDED
|
Binary file (4.13 kB). View file
|
|
|
3D-SEMMN/semmn/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (6.95 kB). View file
|
|
|
3D-SEMMN/semmn/columns.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Columnar dynamics modules."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ColumnarDynamics(nn.Module):
|
| 10 |
+
"""Dense intra-column dynamics with low-rank factors."""
|
| 11 |
+
|
| 12 |
+
def __init__(self, num_columns: int, state_dim: int, rank: int = 8) -> None:
|
| 13 |
+
super().__init__()
|
| 14 |
+
self.num_columns = num_columns
|
| 15 |
+
self.state_dim = state_dim
|
| 16 |
+
self.rank = rank
|
| 17 |
+
|
| 18 |
+
self.u = nn.Parameter(torch.randn(num_columns, state_dim, rank) * 0.05)
|
| 19 |
+
self.v = nn.Parameter(torch.randn(num_columns, rank, state_dim) * 0.05)
|
| 20 |
+
self.bias = nn.Parameter(torch.zeros(num_columns, state_dim))
|
| 21 |
+
self.norm = nn.LayerNorm(state_dim)
|
| 22 |
+
|
| 23 |
+
def forward(self, states: torch.Tensor, inter_messages: torch.Tensor) -> torch.Tensor:
|
| 24 |
+
# W_col = U @ V in factored form to keep parameters compact.
|
| 25 |
+
w = torch.matmul(self.u, self.v) # (C, D, D)
|
| 26 |
+
intra = torch.einsum("bcd,cde->bce", states, w)
|
| 27 |
+
updated = intra + inter_messages + self.bias.unsqueeze(0)
|
| 28 |
+
return torch.tanh(self.norm(updated))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def pool_columns(states: torch.Tensor) -> torch.Tensor:
|
| 32 |
+
"""Convert `(B, C, D)` into `(B, C)` pooled activity."""
|
| 33 |
+
return states.mean(dim=-1)
|
3D-SEMMN/semmn/config.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configuration handling for 3D-SEMMN."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass, field
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Any
|
| 8 |
+
|
| 9 |
+
import yaml
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass
|
| 13 |
+
class GridConfig:
|
| 14 |
+
dims: tuple[int, int, int] = (100, 100, 50)
|
| 15 |
+
n_columns_xy: tuple[int, int] = (10, 10)
|
| 16 |
+
vision_y_ratio: float = 0.30
|
| 17 |
+
audio_y_ratio: float = 0.70
|
| 18 |
+
hub_y_range: tuple[float, float] = (0.40, 0.60)
|
| 19 |
+
hub_z_range: tuple[float, float] = (0.30, 0.70)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass
|
| 23 |
+
class ModelConfig:
|
| 24 |
+
state_dim: int = 32
|
| 25 |
+
latent_dim: int = 8
|
| 26 |
+
vision_feature_dim: int = 256
|
| 27 |
+
audio_feature_dim: int = 256
|
| 28 |
+
recurrent_steps: int = 6
|
| 29 |
+
attention_heads: int = 4
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@dataclass
|
| 33 |
+
class ConnectivityConfig:
|
| 34 |
+
lambda_decay: float = 5.0
|
| 35 |
+
inter_density: float = 0.08
|
| 36 |
+
intra_density_boost: float = 2.0
|
| 37 |
+
min_weight: float = 0.0
|
| 38 |
+
max_weight: float = 1.5
|
| 39 |
+
init_weight_scale: float = 0.15
|
| 40 |
+
stdp_interval: int = 100
|
| 41 |
+
stdp_lr: float = 0.01
|
| 42 |
+
anti_hebbian: float = 0.05
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@dataclass
|
| 46 |
+
class LossConfig:
|
| 47 |
+
contrastive_weight: float = 1.0
|
| 48 |
+
reconstruction_weight: float = 0.5
|
| 49 |
+
imagination_weight: float = 0.3
|
| 50 |
+
manifold_weight: float = 0.1
|
| 51 |
+
spatial_penalty_weight: float = 5e-4
|
| 52 |
+
temperature: float = 0.1
|
| 53 |
+
use_spatial_penalty: bool = True
|
| 54 |
+
use_manifold_loss: bool = True
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclass
|
| 58 |
+
class TrainingConfig:
|
| 59 |
+
dataset_source: str = "synthetic"
|
| 60 |
+
batch_size: int = 64
|
| 61 |
+
epochs: int = 10
|
| 62 |
+
lr: float = 1e-3
|
| 63 |
+
weight_decay: float = 1e-4
|
| 64 |
+
num_workers: int = 4
|
| 65 |
+
log_interval: int = 50
|
| 66 |
+
val_batches: int = 100
|
| 67 |
+
amp: bool = True
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
@dataclass
|
| 71 |
+
class PathsConfig:
|
| 72 |
+
output_dir: str = "outputs"
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
@dataclass
|
| 76 |
+
class SEMMNConfig:
|
| 77 |
+
seed: int = 42
|
| 78 |
+
grid: GridConfig = field(default_factory=GridConfig)
|
| 79 |
+
model: ModelConfig = field(default_factory=ModelConfig)
|
| 80 |
+
connectivity: ConnectivityConfig = field(default_factory=ConnectivityConfig)
|
| 81 |
+
loss: LossConfig = field(default_factory=LossConfig)
|
| 82 |
+
training: TrainingConfig = field(default_factory=TrainingConfig)
|
| 83 |
+
paths: PathsConfig = field(default_factory=PathsConfig)
|
| 84 |
+
|
| 85 |
+
@property
|
| 86 |
+
def n_columns(self) -> int:
|
| 87 |
+
return self.grid.n_columns_xy[0] * self.grid.n_columns_xy[1]
|
| 88 |
+
|
| 89 |
+
@property
|
| 90 |
+
def n_neurons(self) -> int:
|
| 91 |
+
return self.grid.dims[0] * self.grid.dims[1] * self.grid.dims[2]
|
| 92 |
+
|
| 93 |
+
@classmethod
|
| 94 |
+
def from_yaml(cls, path: str | Path) -> "SEMMNConfig":
|
| 95 |
+
with Path(path).open("r", encoding="utf-8") as f:
|
| 96 |
+
raw = yaml.safe_load(f) or {}
|
| 97 |
+
return cls.from_dict(raw)
|
| 98 |
+
|
| 99 |
+
@classmethod
|
| 100 |
+
def from_dict(cls, raw: dict[str, Any]) -> "SEMMNConfig":
|
| 101 |
+
return cls(
|
| 102 |
+
seed=raw.get("seed", 42),
|
| 103 |
+
grid=_merge_dataclass(GridConfig(), raw.get("grid", {})),
|
| 104 |
+
model=_merge_dataclass(ModelConfig(), raw.get("model", {})),
|
| 105 |
+
connectivity=_merge_dataclass(
|
| 106 |
+
ConnectivityConfig(), raw.get("connectivity", {})
|
| 107 |
+
),
|
| 108 |
+
loss=_merge_dataclass(LossConfig(), raw.get("loss", {})),
|
| 109 |
+
training=_merge_dataclass(TrainingConfig(), raw.get("training", {})),
|
| 110 |
+
paths=_merge_dataclass(PathsConfig(), raw.get("paths", {})),
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def _merge_dataclass(default_obj: Any, overrides: dict[str, Any]) -> Any:
|
| 115 |
+
current = default_obj.__dict__.copy()
|
| 116 |
+
for key, value in overrides.items():
|
| 117 |
+
if isinstance(current.get(key), tuple) and isinstance(value, list):
|
| 118 |
+
current[key] = tuple(value)
|
| 119 |
+
else:
|
| 120 |
+
current[key] = value
|
| 121 |
+
return type(default_obj)(**current)
|
3D-SEMMN/semmn/connectivity.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Sparse distance-penalized connectivity and STDP rules."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
|
| 8 |
+
from semmn.config import ConnectivityConfig
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class SparseInterColumnConnectivity(nn.Module):
|
| 12 |
+
"""Sparse inter-column graph with distance-decay sampling.
|
| 13 |
+
|
| 14 |
+
Inspired by seRNN wiring constraints: long-range links carry a larger cost,
|
| 15 |
+
so we initialize and regularize them with an exponential distance penalty.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def __init__(
|
| 19 |
+
self,
|
| 20 |
+
column_centers: torch.Tensor,
|
| 21 |
+
state_dim: int,
|
| 22 |
+
config: ConnectivityConfig,
|
| 23 |
+
) -> None:
|
| 24 |
+
super().__init__()
|
| 25 |
+
self.state_dim = state_dim
|
| 26 |
+
self.config = config
|
| 27 |
+
self.num_columns = int(column_centers.shape[0])
|
| 28 |
+
self.register_buffer("column_centers", column_centers.clone())
|
| 29 |
+
|
| 30 |
+
row_idx, col_idx, distances, weights = self._build_sparse_edges()
|
| 31 |
+
self.register_buffer("row_idx", row_idx)
|
| 32 |
+
self.register_buffer("col_idx", col_idx)
|
| 33 |
+
self.register_buffer("edge_distances", distances)
|
| 34 |
+
self.edge_weights = nn.Parameter(weights)
|
| 35 |
+
self.inter_gain = nn.Parameter(torch.ones(state_dim))
|
| 36 |
+
|
| 37 |
+
def _build_sparse_edges(self) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 38 |
+
centers = self.column_centers
|
| 39 |
+
c = centers.shape[0]
|
| 40 |
+
dist = torch.cdist(centers, centers, p=2)
|
| 41 |
+
decay = torch.exp(-dist / max(self.config.lambda_decay, 1e-6))
|
| 42 |
+
eye_mask = ~torch.eye(c, dtype=torch.bool, device=centers.device)
|
| 43 |
+
prob = (decay * self.config.inter_density).clamp(0.0, 1.0)
|
| 44 |
+
sampled = (torch.rand_like(prob) < prob) & eye_mask
|
| 45 |
+
row_idx, col_idx = torch.where(sampled)
|
| 46 |
+
|
| 47 |
+
if row_idx.numel() == 0:
|
| 48 |
+
row_idx = torch.arange(0, c - 1, device=centers.device)
|
| 49 |
+
col_idx = torch.arange(1, c, device=centers.device)
|
| 50 |
+
|
| 51 |
+
distances = dist[row_idx, col_idx]
|
| 52 |
+
weights = torch.randn(row_idx.numel(), device=centers.device) * self.config.init_weight_scale
|
| 53 |
+
weights = weights.clamp(self.config.min_weight, self.config.max_weight)
|
| 54 |
+
return row_idx.long(), col_idx.long(), distances.float(), weights.float()
|
| 55 |
+
|
| 56 |
+
def sparse_matrix(self) -> torch.Tensor:
|
| 57 |
+
indices = torch.stack([self.row_idx, self.col_idx], dim=0)
|
| 58 |
+
return torch.sparse_coo_tensor(
|
| 59 |
+
indices=indices,
|
| 60 |
+
values=self.edge_weights,
|
| 61 |
+
size=(self.num_columns, self.num_columns),
|
| 62 |
+
device=self.edge_weights.device,
|
| 63 |
+
).coalesce()
|
| 64 |
+
|
| 65 |
+
def forward(self, states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 66 |
+
"""Apply sparse inter-column message passing.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
states: `(B, C, D)` column states.
|
| 70 |
+
Returns:
|
| 71 |
+
inter_messages: `(B, C, D)` sparse messages.
|
| 72 |
+
scalar_activity: `(B, C)` pooled activity used for STDP.
|
| 73 |
+
"""
|
| 74 |
+
scalar_activity = states.mean(dim=-1) # (B, C)
|
| 75 |
+
sparse_w = self.sparse_matrix()
|
| 76 |
+
propagated = torch.sparse.mm(sparse_w, scalar_activity.transpose(0, 1)).transpose(0, 1)
|
| 77 |
+
inter_messages = propagated.unsqueeze(-1) * self.inter_gain.view(1, 1, -1)
|
| 78 |
+
return inter_messages, scalar_activity
|
| 79 |
+
|
| 80 |
+
@torch.no_grad()
|
| 81 |
+
def stdp_update(self, pre_activity: torch.Tensor, post_activity: torch.Tensor) -> None:
|
| 82 |
+
"""Hebbian/STDP-like update on sparse edge values."""
|
| 83 |
+
if pre_activity.numel() == 0:
|
| 84 |
+
return
|
| 85 |
+
pre = pre_activity.mean(dim=0) # (C,)
|
| 86 |
+
post = post_activity.mean(dim=0) # (C,)
|
| 87 |
+
corr = torch.outer(post, pre)
|
| 88 |
+
edge_corr = corr[self.row_idx, self.col_idx]
|
| 89 |
+
anti = self.config.anti_hebbian * self.edge_weights.abs()
|
| 90 |
+
delta = self.config.stdp_lr * (edge_corr - anti)
|
| 91 |
+
new_w = self.edge_weights + delta
|
| 92 |
+
self.edge_weights.copy_(new_w.clamp(self.config.min_weight, self.config.max_weight))
|
| 93 |
+
|
| 94 |
+
def spatial_wiring_cost(self) -> torch.Tensor:
|
| 95 |
+
return (self.edge_weights.abs() * self.edge_distances).mean()
|
| 96 |
+
|
| 97 |
+
def active_parameters(self) -> int:
|
| 98 |
+
return int((self.edge_weights.abs() > 1e-8).sum().item())
|
3D-SEMMN/semmn/dataset.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Multimodal AV-MNIST dataset helpers."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
from torch.utils.data import DataLoader, Dataset
|
| 10 |
+
from torchvision import datasets, transforms
|
| 11 |
+
|
| 12 |
+
from semmn.config import SEMMNConfig
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@dataclass
|
| 16 |
+
class Batch:
|
| 17 |
+
vision: torch.Tensor
|
| 18 |
+
audio: torch.Tensor
|
| 19 |
+
label: torch.Tensor
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class SyntheticAVMNIST(Dataset):
|
| 23 |
+
"""Pair MNIST vision with synthetic spectrogram-like audio."""
|
| 24 |
+
|
| 25 |
+
def __init__(self, root: str, train: bool = True) -> None:
|
| 26 |
+
tfm = transforms.ToTensor()
|
| 27 |
+
self.mnist = datasets.MNIST(root=root, train=train, download=True, transform=tfm)
|
| 28 |
+
|
| 29 |
+
def __len__(self) -> int:
|
| 30 |
+
return len(self.mnist)
|
| 31 |
+
|
| 32 |
+
def __getitem__(self, idx: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 33 |
+
vision, label = self.mnist[idx]
|
| 34 |
+
audio = self._label_to_spectrogram(int(label), idx)
|
| 35 |
+
return vision, audio, torch.tensor(label, dtype=torch.long)
|
| 36 |
+
|
| 37 |
+
def _label_to_spectrogram(self, label: int, seed: int) -> torch.Tensor:
|
| 38 |
+
g = torch.Generator().manual_seed(seed)
|
| 39 |
+
t = torch.linspace(0, 1, steps=224)
|
| 40 |
+
base_freq = 110.0 + 22.0 * float(label)
|
| 41 |
+
sine = (
|
| 42 |
+
torch.sin(2.0 * torch.pi * base_freq * t)
|
| 43 |
+
+ 0.3 * torch.sin(2.0 * torch.pi * 2.0 * base_freq * t)
|
| 44 |
+
+ 0.2 * torch.randn(224, generator=g)
|
| 45 |
+
)
|
| 46 |
+
spec = torch.stft(
|
| 47 |
+
sine,
|
| 48 |
+
n_fft=64,
|
| 49 |
+
hop_length=16,
|
| 50 |
+
win_length=64,
|
| 51 |
+
return_complex=True,
|
| 52 |
+
).abs()
|
| 53 |
+
spec = spec.unsqueeze(0) # (1, F, T)
|
| 54 |
+
spec = F.interpolate(spec.unsqueeze(0), size=(112, 112), mode="bilinear", align_corners=False)
|
| 55 |
+
spec = spec.squeeze(0)
|
| 56 |
+
spec = spec / (spec.max().clamp_min(1e-6))
|
| 57 |
+
return spec
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class HuggingFaceAVMNIST(Dataset):
|
| 61 |
+
"""Attempt to read BLOSSOM-framework/AV-MNIST with robust key fallback."""
|
| 62 |
+
|
| 63 |
+
def __init__(self, split: str = "train") -> None:
|
| 64 |
+
from datasets import load_dataset # lazy import
|
| 65 |
+
|
| 66 |
+
self.ds = load_dataset("BLOSSOM-framework/AV-MNIST", split=split)
|
| 67 |
+
|
| 68 |
+
def __len__(self) -> int:
|
| 69 |
+
return len(self.ds)
|
| 70 |
+
|
| 71 |
+
def __getitem__(self, idx: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 72 |
+
row = self.ds[idx]
|
| 73 |
+
vision = _to_tensor_image(row.get("image") or row.get("vision") or row.get("mnist"))
|
| 74 |
+
audio = _to_tensor_audio(row.get("audio") or row.get("spectrogram") or row.get("sound"))
|
| 75 |
+
label = int(row.get("label") or row.get("digit") or row.get("class"))
|
| 76 |
+
return vision, audio, torch.tensor(label, dtype=torch.long)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _to_tensor_image(value) -> torch.Tensor:
|
| 80 |
+
if isinstance(value, torch.Tensor):
|
| 81 |
+
x = value.float()
|
| 82 |
+
else:
|
| 83 |
+
x = torch.tensor(value, dtype=torch.float32)
|
| 84 |
+
if x.ndim == 2:
|
| 85 |
+
x = x.unsqueeze(0)
|
| 86 |
+
x = F.interpolate(x.unsqueeze(0), size=(28, 28), mode="bilinear", align_corners=False).squeeze(0)
|
| 87 |
+
return x.clamp(0.0, 1.0)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _to_tensor_audio(value) -> torch.Tensor:
|
| 91 |
+
if isinstance(value, dict) and "array" in value:
|
| 92 |
+
arr = torch.tensor(value["array"], dtype=torch.float32)
|
| 93 |
+
spec = torch.stft(arr, n_fft=128, hop_length=32, return_complex=True).abs()
|
| 94 |
+
x = spec.unsqueeze(0)
|
| 95 |
+
elif isinstance(value, torch.Tensor):
|
| 96 |
+
x = value.float()
|
| 97 |
+
else:
|
| 98 |
+
x = torch.tensor(value, dtype=torch.float32)
|
| 99 |
+
while x.ndim < 3:
|
| 100 |
+
x = x.unsqueeze(0)
|
| 101 |
+
if x.shape[0] != 1:
|
| 102 |
+
x = x[:1]
|
| 103 |
+
x = F.interpolate(x.unsqueeze(0), size=(112, 112), mode="bilinear", align_corners=False).squeeze(0)
|
| 104 |
+
x = x / (x.max().clamp_min(1e-6))
|
| 105 |
+
return x
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def build_dataloaders(config: SEMMNConfig) -> tuple[DataLoader, DataLoader]:
|
| 109 |
+
root = "./data"
|
| 110 |
+
if config.training.dataset_source == "hf":
|
| 111 |
+
try:
|
| 112 |
+
train_ds = HuggingFaceAVMNIST(split="train")
|
| 113 |
+
val_ds = HuggingFaceAVMNIST(split="test")
|
| 114 |
+
except Exception:
|
| 115 |
+
train_ds = SyntheticAVMNIST(root=root, train=True)
|
| 116 |
+
val_ds = SyntheticAVMNIST(root=root, train=False)
|
| 117 |
+
else:
|
| 118 |
+
train_ds = SyntheticAVMNIST(root=root, train=True)
|
| 119 |
+
val_ds = SyntheticAVMNIST(root=root, train=False)
|
| 120 |
+
|
| 121 |
+
train_loader = DataLoader(
|
| 122 |
+
train_ds,
|
| 123 |
+
batch_size=config.training.batch_size,
|
| 124 |
+
shuffle=True,
|
| 125 |
+
num_workers=config.training.num_workers,
|
| 126 |
+
pin_memory=True,
|
| 127 |
+
)
|
| 128 |
+
val_loader = DataLoader(
|
| 129 |
+
val_ds,
|
| 130 |
+
batch_size=config.training.batch_size,
|
| 131 |
+
shuffle=False,
|
| 132 |
+
num_workers=max(1, config.training.num_workers // 2),
|
| 133 |
+
pin_memory=True,
|
| 134 |
+
)
|
| 135 |
+
return train_loader, val_loader
|
3D-SEMMN/semmn/encoders.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Vision/audio encoders and lobe injection."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class VisionEncoder(nn.Module):
|
| 10 |
+
def __init__(self, out_dim: int = 256) -> None:
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.net = nn.Sequential(
|
| 13 |
+
nn.Conv2d(1, 32, kernel_size=3, padding=1),
|
| 14 |
+
nn.ReLU(inplace=True),
|
| 15 |
+
nn.MaxPool2d(2),
|
| 16 |
+
nn.Conv2d(32, 64, kernel_size=3, padding=1),
|
| 17 |
+
nn.ReLU(inplace=True),
|
| 18 |
+
nn.MaxPool2d(2),
|
| 19 |
+
nn.Flatten(),
|
| 20 |
+
nn.Linear(64 * 7 * 7, out_dim),
|
| 21 |
+
nn.ReLU(inplace=True),
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 25 |
+
return self.net(x)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class AudioEncoder1D(nn.Module):
|
| 29 |
+
"""1D Conv encoder over spectrogram time sequences."""
|
| 30 |
+
|
| 31 |
+
def __init__(self, out_dim: int = 256, freq_bins: int = 112) -> None:
|
| 32 |
+
super().__init__()
|
| 33 |
+
self.net = nn.Sequential(
|
| 34 |
+
nn.Conv1d(freq_bins, 128, kernel_size=5, padding=2),
|
| 35 |
+
nn.ReLU(inplace=True),
|
| 36 |
+
nn.Conv1d(128, 128, kernel_size=5, padding=2),
|
| 37 |
+
nn.ReLU(inplace=True),
|
| 38 |
+
nn.AdaptiveAvgPool1d(1),
|
| 39 |
+
nn.Flatten(),
|
| 40 |
+
nn.Linear(128, out_dim),
|
| 41 |
+
nn.ReLU(inplace=True),
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
def forward(self, spec: torch.Tensor) -> torch.Tensor:
|
| 45 |
+
# Input expected `(B, 1, F, T)`; use `(B, F, T)` for Conv1d.
|
| 46 |
+
x = spec.squeeze(1)
|
| 47 |
+
return self.net(x)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class LobeInjector(nn.Module):
|
| 51 |
+
"""Project modality features into selected columns."""
|
| 52 |
+
|
| 53 |
+
def __init__(self, in_dim: int, num_columns: int, state_dim: int, target_columns: torch.Tensor) -> None:
|
| 54 |
+
super().__init__()
|
| 55 |
+
self.num_columns = num_columns
|
| 56 |
+
self.state_dim = state_dim
|
| 57 |
+
self.register_buffer("target_columns", target_columns.long())
|
| 58 |
+
out_dim = max(1, self.target_columns.numel()) * state_dim
|
| 59 |
+
self.project = nn.Linear(in_dim, out_dim)
|
| 60 |
+
|
| 61 |
+
def forward(self, features: torch.Tensor) -> torch.Tensor:
|
| 62 |
+
b = features.shape[0]
|
| 63 |
+
injected = features.new_zeros(b, self.num_columns, self.state_dim)
|
| 64 |
+
projected = self.project(features).view(b, -1, self.state_dim)
|
| 65 |
+
injected[:, self.target_columns, :] = projected[:, : self.target_columns.numel(), :]
|
| 66 |
+
return injected
|