Windows-GPU-UNLOCK
RTX3090 UEFI 포트, 한국어 무리셋 윈도우 부팅 가이드, 검증된 소스와 벤치마크 제공
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
SEDIC 2026 — RF/Signal Track ("Project Overwatch")
AI model that detects and classifies radio signals from raw IQ data: civilian
modulations (BPSK/QPSK/16QAM/64QAM), military/tactical signals (LFM radar,
FHSS), and hostile jamming — across clean and noisy (low-SNR) conditions, and
when more than one of these overlaps in the same window (e.g. a jammer
overlaid on top of a real signal).
Verify everything works before touching real data:
104 tests should pass.README 전문 보기 ↗SEDIC 2026 — RF/Signal Track ("Project Overwatch")
Branch: eavan-multilabel-jamming-overlay
AI model that detects and classifies radio signals from raw IQ data: civilian modulations (BPSK/QPSK/16QAM/64QAM), military/tactical signals (LFM radar, FHSS), and hostile jamming — across clean and noisy (low-SNR) conditions, and when more than one of these overlaps in the same window (e.g. a jammer overlaid on top of a victim signal).
| Doc | What it covers |
|---|---|
docs/SEDIC2026_Track1_Documentation.md | Full technical plan, team roles, 4-day timeline |
docs/TOOLS.md | Every tool, library, and dataset — with licences |
docs/pipeline/ | One doc per stage: data → generators → preprocessing → training → evaluation → submission |
docs/pipeline/09-multilabel-composite-guide.md | RadioML usage, composite generation, what to run |
docs/WORKTREES.md | Parallel branch workflow |
pip install -r requirements.txt
Verify everything works before touching real data:
pytest -q
104 tests should pass.
One model, two feature-extraction branches feeding one fused classifier —
src/models/amc_cnn.py, ~148,938 parameters, trained from scratch (no
pretrained backbone exists for raw RF IQ).
RAW IQ INPUT (2, 512) = (I, Q channels, 512 samples = 160 us)
│
┌───────┴────────────────────┐
▼ ▼
IQ BRANCH STFT BRANCH
(dilated 1D-CNN, (spectrogram + 2D-CNN,
128 ch, full resolution) 64 ch, coarser resolution)
│ │
│ upsample to IQ branch's resolution
└──────────────┬───────────────┘
▼
CONCATENATE (192 channels)
│
ENERGY-GATED ATTENTION POOLING
(learned weighted pooling over time,
also reads raw I²+Q² power directly)
│
Linear(192→256) → ReLU → Dropout(0.5)
│
Linear(256→8)
│
SIGMOID
each of the 8 classes judged independently
→ any subset can be true at once in one window
Why two branches, not one representation: raw IQ preserves phase, which civilian modulations (BPSK/QPSK/QAM) are defined by — but a spectrogram makes frequency movement (radar chirps, FHSS hops) far more explicit than raw IQ does. Neither representation alone does both jobs well, so both branches run over the same input window and get fused, rather than one branch handling "type A" and the other "type B".
Why the output can flag more than one class: the branches and fusion
produce one shared feature vector before any per-class decision is made.
Multi-label capability lives entirely in the last step — an independent
sigmoid per class, not "one winner" — not in how many branches feed into it.
A window containing a victim signal with a jammer overlaid on top correctly
reads as both classes present, e.g. {QPSK, JAMMING}.
Four sources combine into the training set (src/data/build_dataset.py):
| Source | Classes | Origin |
|---|---|---|
| RadioML 2018.01A | BPSK, QPSK, 16QAM, 64QAM | Published dataset, external (not recorded by us) |
| RadChar | LFM_RADAR (partial, radchar_fraction) | Published dataset, external (not recorded by us) |
In-house generators (src/generators/) | LFM_RADAR (remainder), FHSS, JAMMING, NOISE_FLOOR | Synthetic, our generators |
Composite overlay (src/data/composite.py) | Jammer overlaid on any of the above 6 (not NOISE_FLOOR, not JAMMING-on-JAMMING) | Combines the sources above |
No usable public dataset exists for FHSS or JAMMING — checked and confirmed,
see docs/pipeline/01-data-sources.md.
Composite generation: overlay_jamming() draws a random jammer
(barrage/tone/sweep) and mixes it onto a victim signal via the tested
apply_jamming(), at a randomized JSR (jamming.jsr_db range). Labeled with
both classes present. Additive to the dataset — standalone examples are
untouched. overlay_fraction: 0.3 sizes how many composite examples get
generated per victim class per SNR bin.
At the default config: ~6,000 standalone examples/class, ~10,800 composite
examples, ~58,800 total. Full detail on what happens to RadioML
specifically (it's loaded twice — once standalone, once as a separate
composite-victim pool) is in
docs/pipeline/09-multilabel-composite-guide.md.
Labels (y.npy) are multi-hot, shape (N, 8) — one column per class, 1 if
present. A standalone example has one 1; a composite example has two.
Git holds the recipe, not the data. Signal arrays never go in git: the
repo is public, GitHub rejects files over 100 MB, and some external datasets
(RadChar is CC BY-NC; RadioML's licence is unconfirmed) may not be
redistributed. .gitignore already blocks data/**, *.npy, *.npz and
*.h5. What goes in git:
| In git | Not in git |
|---|---|
Generator code (src/generators/, src/data/) | Generated shards (.npz) |
Config + class list (configs/default.yaml) | Downloaded external datasets |
Shard manifests (data/manifests/*.json, a few KB each) | Checkpoints (*.pt) |
Raw data stays on each person's own laptop (in data/raw/); only
processed shards go to one shared Google Drive folder, synced to each
laptop by Google Drive for desktop:
NEXA_data/
shards/
jam/ # Chua
pulse/ # Chua
spread/ # Chua
yolo/ # Chua (wideband scenes + box labels, separate from the classifier set)
radar/ # Jessy
analog/ # Eileen
multicarrier/ # Eileen
apsk/ # Eileen
fskpsk/ # Eavan (FSK, PSK, QAM)
The merged set is not on Drive: Eavan writes it locally as
data/processed/X.npy, y.npy and snr_labels.npy, the same files
src/train.py already reads.
Owners (week 1):
| Person | Classes (every one is generated) | Shard folder | External data they use |
|---|---|---|---|
| Chua (17 + YOLO) | Jamming: barrage, spot, tone, multi-tone, swept, chirp, pulsed, partial-band, reactive, DRFM deception (RGPO/VGPO). Noise: NOISE_FLOOR. Pulse comms: PPM (ADS-B), AFSK (APRS), SSR/IFF (1030 MHz). Spread: FHSS, DSSS, LoRa CSS. Plus the YOLO wideband scenes | jam/, pulse/, spread/, yolo/ | RadDet (YOLO radar scenes); Jammertest + Zenodo jamming sets for testing only |
| Jessy (18, radar only) | Week 1: plain pulse, LFM, Barker, polyphase Barker, Frank, Costas, FMCW, CW Doppler. Week 2: P1, P2, P3, P4, T1, T2, T3, T4, NLFM, stepped frequency. Later, not a CNN class: a pulse-train analyser for staggered PRI, jittered PRI and frequency-agile radars, which only show up across many pulses, far longer than one 512-sample window | radar/ | RadChar |
| Eileen (17) | Analog: AM-DSB-WC, AM-DSB-SC, AM-SSB-WC, AM-SSB-SC, WBFM, NBFM, PM. ASK: OOK, 4ASK, 8ASK. Multicarrier: OFDM, LTE-like, 5G NR-like. APSK: 16APSK, 32APSK, 64APSK, 128APSK | analog/, multicarrier/, apsk/ | RadioML 2018, HisarMod; TorchSig (OFDM generator) |
| Eavan (18 + merge + training) | FSK: 2FSK, 4FSK, 8FSK, GFSK, GMSK, MSK, CPFSK. PSK: BPSK, QPSK, OQPSK, 8PSK, 16PSK, 32PSK. QAM: 16QAM, 32QAM, 64QAM, 128QAM, 256QAM. Also: builds the mixtures, merges, trains | fskpsk/ | RadioML 2018, HisarMod, CSPB |
Each person downloads only the raw datasets in their own row, into their
own data/raw/. Raw files never go on Drive or in git; only the shards made
from them do.
Shard contract — every shard, whoever made it, must match this exactly or it cannot be merged:
| Key | dtype / shape | Meaning |
|---|---|---|
iq | complex64 (N, 512) | 3.2 MS/s windows |
y | uint8 (N, C) | Multi-hot labels, column order = dataset_classes in configs/default.yaml (60 classes) |
snr_db | float32 (N,) | SNR used to generate the window |
source | str (N,) | gen or the external dataset's name |
seed | int64 (N,) | Generator seed, so any window can be regenerated |
File name: <group>_<split>_seed<seed>_<part>.npz, at most 50,000 windows,
e.g. radar_train_seed101_003.npz. Train/val/test use different seeds and
never share a source file.
Every shard gets a manifest committed to data/manifests/ with the same
base name: shard file name, git commit hash of the generator code, seed,
split, class-list version, per-class window counts, and the file's SHA-256.
The person training checks out the commits in the manifests, reads the
manifests to know exactly what to load from Drive, and checks each SHA-256 so
a half-uploaded shard is caught before training. If a shard is lost, its
manifest (commit + seed) is enough to regenerate it.
Step by step:
fskpsk/), each for every class
in their row of the owners table: run their generators
on their own laptop with their assigned seeds, writing the .npz
shards into their folder under NEXA_data/shards/. With Google Drive for
desktop installed, that folder is a normal local folder (e.g.
G:\My Drive\NEXA_data\...) and uploads by itself. Then git push the
generator code and the matching data/manifests/*.json.git pull, wait for Drive to finish syncing, and run the merge
step locally against data/manifests/. It loads every owner's shards,
adds the composite (mixture) windows, and writes one concatenated set to
data/processed/X.npy (float32 (N, 2, 512), normalised IQ),
y.npy (float32 (N, C), multi-hot) and snr_labels.npy. The YOLO
scenes in yolo/ are a separate dataset and are not merged into it.data/processed/ files.Seeds must not overlap between people: give each person their own seed block (e.g. Chua 1000–1999, Jessy 2000–2999, Eileen 3000–3999, Eavan 4000–4999).
Shortcut: the generators are seeded, so Eavan can instead git pull and
re-run a teammate's generator locally with the seeds in their manifests,
getting the same windows without any upload (as long as the code commit and
library versions match). Use Drive when generation is slow; regenerate when
it is quick.
The merge step (scripts/merge_shards.py) is planned, not written yet.
A complete working example already exists:
scripts/make_fskpsk_shards.py (Eavan's 18
classes). Copy it as scripts/make_<group>_shards.py and change only the
class list, the generator calls, the group name and the seed block. The
pieces it uses are shared — use them, do not write your own .npz code:
| Piece | What it gives you |
|---|---|
src.data.shards.ShardWriter | Writes contract-checked shards of at most 50,000 windows and their manifests. It refuses to save a shard with a wrong dtype, shape or label column, so mistakes show up on your laptop, not at merge time |
src.data.shards.load_shard(path, manifest) | Loads a shard back and checks it against its manifest SHA-256 |
src.data.external.radioml_windows(...) / hisarmod_windows(...) | External windows for your classes, first 512 samples, rows never reused across train/val/test. RADIOML_NAMES and HISARMOD_LABELS say which dataset classes each one covers |
src.config.DATASET_CLASSES, CFG["dataset_snr_bins_db"] | The 60 class names and the 16 SNR levels. Use these names exactly |
Watch out for:
dataset_classes (configs/default.yaml),
spelled exactly (e.g. JAM_BARRAGE, AM_DSB_WC, LORA_CSS). Anything
else and ShardWriter raises. Never reorder or rename that list; tell
Eavan if a class is missing.src.data.preprocess.add_awgn).
NOISE_FLOOR is the one exception: pure noise, no SNR.generate_window(cls, snr_db, rng) -> (iq, params),
where snr_db=None returns the clean signal for tests. Take every random
number from the rng you are given, so the same seed always gives the
same window. See src/generators/digital.py.window_seed() in the example script).source="gen" for generated rows and the dataset
name ("radioml2018", "hisarmod2019", "radchar" …) otherwise.tests/test_digital.py that check each class
is built the way you say (e.g. number of tones, chirp slope, duty cycle).python scripts/make_<group>_shards.py --n-per-bin 10 --out data/interim/try --manifests data/interim/try_manifests
Full run: --out "G:/My Drive/NEXA_data/shards/<your folder>" (manifests
go to data/manifests/ by default), then commit and push the manifests
and your code. Never commit .npz files.configs/
default.yaml real run config (all tunables, with reasoning in comments)
smoke.yaml seconds-long dry run, same shape as default
src/
config.py loads YAML (SEDIC_CONFIG env var to override); multi_hot() label helper
generators/ radar.py, fhss.py, jamming.py, noise.py — synthetic signal synthesis
data/
build_dataset.py assembles X.npy/y.npy/snr_labels.npy from all 4 sources
composite.py jammer-overlaid-on-victim generation
preprocess.py windowing, AWGN, normalization, augmentation
radchar.py RadChar loader
diagnose_jamming.py, diagnose_radar.py diagnostic-only; see "Known gaps"
models/
amc_cnn.py dual-branch fusion CNN, see Architecture above
train.py training loop, stratified by (label-combination, SNR)
evaluate.py per-class recall, tier metrics, comms-vs-jamming, scorecard
infer.py runs the model over an IQ file -> per-window classification CSV
(no organiser stream exists; used for our own demo/verification files)
scripts/
train_ensemble.py N-seed ensemble (averages sigmoid outputs) — the main robustness result
measure_variance.py quantifies seed-to-seed swing, so a real improvement can be told from noise
inference_ui.py local Gradio app — upload raw IQ, see every detected component
inspect_attention.py attention-weight-vs-amplitude inspection (Section 4 limitation writeup)
validate_external_jamming.py, append_noise_floor.py
notebooks/
colab_training_multilabel.ipynb Colab training workflow for this branch
tests/ DSP correctness + pipeline wiring + metrics correctness (104 tests)
data/ raw/ interim/ processed/ — all gitignored
evals/ scorecards, plots, classification logs — gitignored
results/ model checkpoints — gitignored
docs/ technical documentation, pipeline stage docs
Nothing heavy is committed. Datasets, checkpoints, and plots are all gitignored — share those via Google Drive, not Git.
Dry run first (~15 seconds, proves the wiring, results are meaningless):
SEDIC_CONFIG=configs/smoke.yaml python -m src.data.build_dataset && SEDIC_CONFIG=configs/smoke.yaml python -m src.train && SEDIC_CONFIG=configs/smoke.yaml python -m src.evaluate
Then the real run:
python -m src.data.build_dataset
python -m src.train
python -m src.evaluate
python -m src.infer --input verification_pack/mixed_sequence.f32 --output evals/classification_log.csv
There is no organiser-provided IQ stream — confirmed with the organiser, and absent from both
official PDFs in docs/rules/. infer.py runs over our own files: the
verification_pack/ captures, or anything else in interleaved float32.
For a more robust number, run the ensemble (5 seeds averaged) and check the variance floor before trusting any single result:
python scripts/train_ensemble.py --models 5
python scripts/measure_variance.py --runs 5
Training on Google Colab: notebooks/colab_training_multilabel.ipynb. It
builds nothing itself — build locally (where RadioML/RadChar live), upload
the three small processed arrays, then train.
src/evaluate.py writes evals/scorecard.json stating plainly whether each
judged class (LFM_RADAR, FHSS, JAMMING) clears the >80% recall benchmark
the rules require.
src/infer.py writes a CSV per classified window with:
detected_classes — every class that crossed multilabel_threshold
(default 0.5), semicolon-joined; NONE if nothing didis_threat — true if any detected class is in judged_classesstatus — TRACKED / MONITOR / INVESTIGATEcivilian_present, military_present,
hostile_present, empty_present) — a tier is present if any of its
member classes is detected, so a window can show multiple tiers present
at oncescripts/inference_ui.py (local Gradio app) shows the same thing visually —
upload a raw IQ file and it renders one tier-colored badge per detected
class, so an overlapping signal shows every component it's made of.
src/evaluate.py's tier metrics (coarse_tier_metrics, comms_vs_jamming)
work the same way: presence-based per tier/class, not "which single class
was predicted" — so a civilian window that's also jammed is evaluated on
whether both were caught, not forced into an either/or bucket.
src/data/diagnose_jamming.py and diagnose_radar.py still assume
single-label argmax output. Diagnostic only (not part of the scored
benchmark), safe to skip for now.overlay_fraction (0.3) is an untested default — validate against real
composite-recall numbers before trusting it, the same way
examples_per_class_per_snr was validated (see that key's comments in
configs/default.yaml for the actual experiment that settled it).src/infer.py::load_iq_file() assumes interleaved float32. This was previously
flagged as an untested submission risk against an organiser-provided stream;
no such stream exists, so the assumption only has to hold for our own files,
which it does.The biggest risk on this track is shipping synthetic training data whose
physics is subtly wrong — the model then scores well on our own data and has
learned the generator rather than the signal. There is no organiser stream that
would eventually expose that, so nothing catches it for us: the tests below, and
the out-of-distribution check in validate_external_jamming.py, are the only
things standing between a subtle generation bug and a result that means nothing.
tests/ asserts the maths does what the class name claims: the LFM chirp
sweeps linearly at the requested rate, FHSS hops land on declared channels,
add_awgn achieves the SNR it was asked for, no generator exceeds Nyquist,
composite overlays hit their requested JSR, and the multi-label tier/comms-
vs-jamming metrics handle composite windows correctly (both components
caught, one caught, or neither).
These verify internal consistency — they cannot verify realism (whether the equations match a real-world emitter). That still needs reference literature or a signal-processing expert.
Four parallel workstreams, each on its own branch and git worktree — see
docs/WORKTREES.md. Day-by-day role split is in section
13 of the technical documentation.