Files
radiacode/TUTORIEL.md
Jacquin Antoine 27ef0727e8 Ajout du tutoriel detaille
Explication etape par etape du pipeline complet :
- Durees et raisons (24h background, 12-24h spectres synthetiques)
- Bruit Poissonnien et rapport signal/bruit
- Physique des spectres synthetiques (Compton, FWHM, efficacite)
- Architecture VegaModel et resultats d'entrainement
- Interpretation des rapports et isotopes de reference
- Guide de transfer vers Raspberry Pi 4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-19 12:32:56 +02:00

416 lines
16 KiB
Markdown

# Tutoriel : Radiacode 103 — Du debut a la detection d'isotopes
Ce tutoriel explique chaque etape du pipeline, pourquoi les durees sont ce qu'elles sont, et comment interpreter les resultats.
---
## 1. Pre-requis materiels
| Element | Detail |
|---------|--------|
| Radiacode 103 | Spectrometre gamma CsI(Tl), 1024 canaux, USB |
| Machine GPU | NVIDIA avec drivers + nvidia-container-toolkit |
| Docker | Version 20.10+ avec Docker Compose V2 (`docker compose`) |
| Cable USB | Pour connecter le Radiacode a la machine |
Verifier que le Radiacode est bien detecte :
```bash
lsusb | grep -i radiacode
# Doit afficher : ID 0483:f123 RadiaCode RadiaCode-103
```
Verifier le GPU :
```bash
nvidia-smi
# Doit afficher votre carte NVIDIA
```
---
## 2. Builder les images Docker (~5 min)
```bash
docker compose build
```
Deux images sont construites :
- `radiacode_103-train` : PyTorch 2.7.0 + CUDA 12.8 (pour l'entrainement sur GPU)
- `radiacode_103-detect` : Python 3.11 + radiacode + PyTorch CPU (pour la detection)
**Pourquoi PyTorch 2.7.0 ?** Les cartes RTX 50-series (Blackwell, compute capability sm_120) ne sont pas supportees par PyTorch < 2.7. Si vous avez une carte plus ancienne (RTX 30/40-series), PyTorch 2.4.0 suffit.
---
## 3. Entrainer le modele (~45 min sur RTX 5060 Ti)
```bash
docker compose run --rm train
```
Cette commande lance deux phases automatiquement :
### Phase 1 : Generation des spectres synthetiques (~10 min)
Le generateur cree 50 000 spectres 1D simulant la physique reelle :
| Type | Nombre | Description |
|------|--------|-------------|
| Mono-isotope | 20 000 | Un seul isotope + background |
| Bi-isotope | 15 000 | Deux isotopes + background |
| Multi-isotope | 10 000 | 3-5 isotopes + background |
| Background seul | 5 000 | Background environnemental uniquement |
**Pourquoi des durees de 12-24h ?**
Les spectres synthetiques simulent des acquisitions de 12 a 24 heures (43200-86400 secondes). C'est volontaire :
1. **Bruit Poissonnien** : La statistique de comptage suit une loi de Poisson. Pour un canal avec N comptages, l'ecart-type est sqrt(N). Le rapport signal/bruit s'ameliore en sqrt(N), donc en sqrt(duree).
- A 6.5 CPS/canal moyen et 1024 canaux, apres 1h on a ~23 comptages/canal (SNR ~5)
- Apres 24h on a ~560 comptages/canal (SNR ~23)
- Apres 1 semaine ~3900 comptages/canal (SNR ~63)
2. **Conditions reelles** : Le detecteur sera utilise en continu pendant des jours. Le modele doit voir des spectres avec le meme niveau de bruit que les mesures reelles.
3. **Sensibilite** : Les isotopes a faible activite (1-5 Bq) ne sont detectables qu'apres plusieurs heures d'accumulation. Le modele doit apprendre cette relation.
**Ce que le generateur simule physiquement :**
- **Pics photoelectriques** : Gaussiennes dont la largeur (FWHM) depend de l'energie. Pour le CsI(Tl) du Radiacode 103, FWHM = 8.4% a 662 keV, avec la relation FWHM(E) = FWHM_662 * sqrt(E/662).
- **Continuum Compton** : Distribution de Klein-Nishina simplifiee sous chaque pic, simulant la diffusion des photons gamma.
- **Bruit Poissonnien** : Chaque canal est echantillonne depuis une loi Poisson(lambda), ce qui reproduit exactement les fluctuations statistiques reelles.
- **Background environnemental** : Continuum exponentiel + pics de K-40 (1460 keV), Pb-214 (295, 352 keV), Bi-214 (609, 1120, 1764 keV), Ac-228 (911 keV), Pb-212 (239 keV), Tl-208 (583, 2614 keV), avec des activites aleatoires realistes.
- **Efficacite du detecteur** : Modele phenomenologique tenant compte de l'absorption basse energie et de la penetration haute energie du scintillateur CsI(Tl) de 1 cm3.
### Phase 2 : Entrainement du VegaModel (~35 min sur RTX 5060 Ti)
Le modele VegaModel est un CNN-FCNN multi-tache :
```
Entree : spectre 1D (1023 canaux, 20-3000 keV)
├── Bloc CNN 1 : Conv1d(1, 64, 7) → BN → ReLU → MaxPool
├── Bloc CNN 2 : Conv1d(64, 128, 5) → BN → ReLU → MaxPool
├── Bloc CNN 3 : Conv1d(128, 256, 3) → BN → ReLU → MaxPool
├── Tete classification : FC(256→512→256→82) → Sigmoid
│ 82 isotopes, probabilite de presence [0, 1]
└── Tete regression : FC(256→512→256→82)
Activite estimee en Bq pour chaque isotope
```
- **34 493 156 parametres** au total
- **Fonction de perte** : BCE (classification) + MSE ponderee (regression sur isotopes presents uniquement)
- **Mixed precision (AMP)** : Acceleration sur GPU via float16
- **Early stopping** : Patience de 10 epochs sans amelioration
**Resultats typiques :**
| Metrique | Valeur |
|----------|--------|
| Val accuracy | 99.89% |
| AUC macro | 0.995 |
| Precision | 99.8% |
| Recall | 95.6% |
| Exact match | 93.5% |
| Best val loss | 0.0051 |
**Fichiers generes dans `models/` :**
| Fichier | Taille | Description |
|---------|--------|-------------|
| `vega_best.pt` | 395 Mo | Meilleur modele (checkpoint avec val loss minimale) |
| `vega_final.pt` | 395 Mo | Modele final (epoch 100) |
| `vega_isotope_index.txt` | 546 o | Liste des 82 isotopes detectables |
| `vega_history.json` | 75 Ko | Metriques d'entrainement par epoch |
> **Note** : Seul `vega_best.pt` et `vega_isotope_index.txt` sont necessaires pour la detection. Les checkpoints par epoch (`vega_epoch_*.pt`) peuvent etre supprimes pour gagner ~39 Go.
---
## 4. Capturer le bruit de fond (24h)
Cette etape est **critique** et ne doit pas etre ignoree. Placez le detecteur dans un endroit sans source radioactive connue.
```bash
docker compose run --rm -d --name radiacode-bg \
-e TARGET_DURATION=86400 -e SAMPLE_INTERVAL=60 \
detect python capture_background.py
```
### Pourquoi 24h ?
Le background environnemental n'est pas constant. Il varie selon :
1. **Le radon** : Concentration dans l'air qui varie avec la ventilation, la meteo, l'heure du jour (cycle diurne)
2. **Le thorium** : Present dans les materiaux de construction (beton, brique), relativement stable mais varie lentement
3. **Le potassium-40** : Partout dans la nature (aliments, corps humain), assez stable
4. **Les rayons cosmiques** : Varient avec l'altitude et l'activite solaire
Un echantillonnage de 24h capture un cycle complet de ces variations, donnant un background de reference representative.
**Pourquoi pas moins ?**
| Duree | CPS/canal | SNR/canal | Pics visibles |
|-------|-----------|-----------|---------------|
| 1h | ~23 | ~5 | Aucun pic clair |
| 4h | ~94 | ~10 | K-40 commence a emerger |
| 12h | ~280 | ~17 | K-40, Bi-214 visibles |
| **24h** | **~560** | **~24** | **Tous les pics de background nets** |
| 48h | ~1120 | ~33 | Pics tres nets, variations capturees |
A 24h, les principaux pics du background sont clairement identifiables :
- **K-40 a 1460 keV** : Toujours present (~5-15 Bq dans l'environnement)
- **Bi-214 a 609 keV** : Fille du radon
- **Pb-214 a 295 et 352 keV** : Fille du radon
- **Tl-208 a 2614 keV** : Fille du thorium
### Suivre la progression
```bash
# Voir les logs
docker logs -f radiacode-bg
# Lire le snapshot JSON
cat data/background_snapshot.json | python3 -m json.tool | head -20
```
Le snapshot montre en temps reel les pics detectes, le CPS moyen et la duree ecoulee.
### Le script gere les deconnexions
Si le Radiacode est debranche pendant la capture, le script se reconnecte automatiquement et reprend l'accumulation. Les donnees deja collectees ne sont pas perdues.
### A la fin (24h plus tard)
Le fichier `data/background_24h.npy` est genere avec :
```python
{
"counts": np.array, # 1024 canaux, comptages cumules
"duration": float, # live time en secondes
"timestamp": float # timestamp Unix
}
```
---
## 5. Lancer la detection continue
```bash
docker compose up detect
```
### Comment ca marche
Toutes les 60 secondes, le moniteur :
```
┌──────────────────────────────────────────────────────────────┐
│ 1. Echantillonnage │
│ Radiacode.spectrum() → 1024 canaux + duree │
│ cumulated_counts += counts │
│ cumulated_live_time += duration.total_seconds() │
│ Radiacode.spectrum_reset() │
│ │
│ 2. A 00h00 chaque jour : Rapport │
│ Si live_time > 1h : │
│ rate = cumulated_counts / cumulated_live_time │
│ bg_rate = bg_counts / bg_live_time │
│ net_rate = clip(rate - bg_rate, 0, None) │
│ normalized = net_rate / net_rate.max() │
│ logits, activities = model(normalized) │
│ probs = sigmoid(logits) │
│ Pour chaque isotope avec prob > 0.5 : │
│ rapport[name, prob%, activite_bq] │
│ Sauvegarder dans logs/report_YYYY-MM-DD.json │
│ Reset cumulateurs │
│ │
│ 3. Si detecteur debranche : │
│ Attendre 60s, retenter la connexion │
│ Les donnees cumulees sont conservees │
└──────────────────────────────────────────────────────────────┘
```
### Pourquoi soustraire le background ?
Le modele est entraite avec du background synthetique, mais le background reel varie selon l'emplacement. La soustraction reelle ameliore la detection :
- **Sans soustraction** : Le modele voit K-40 a 1460 keV et peut le signaler comme isotope detecte, meme si c'est juste du background
- **Avec soustraction** : Le pic de K-40 du background est elimine, seul un signal supplementaire est analyse
- **Resultat** : Moins de faux positifs, meilleure sensibilite pour les isotopes faibles
### Debranchement et rebranchement
Le moniteur gere les deconnexions USB proprement :
- Si le Radiacode est debranche, `try_connect()` echoue et retourne `None`
- Le moniteur attend 60 secondes et retente
- Les compteurs cumules ne sont pas reinitialises
- Quand le detecteur est rebranche, l'accumulation reprend
Cela permet de prendre le detecteur avec soi pendant la journee sans perdre les donnees de la nuit.
---
## 6. Interpreter les rapports
Chaque jour a `REPORT_HOUR` (00h00 par defaut), un rapport JSON est genere dans `logs/` :
```json
{
"date": "2026-05-20T00:00:00",
"live_time_hours": 18.5,
"total_counts": 434500,
"cps_mean": 6.52,
"background_subtracted": true,
"isotopes_detected": [
{
"isotope": "Cs-137",
"probability": 0.97,
"activity_bq": 12.3
},
{
"isotope": "K-40",
"probability": 0.85,
"activity_bq": 8.1
}
]
}
```
### Champs du rapport
| Champ | Description |
|-------|-------------|
| `live_time_hours` | Temps effectif d'acquisition (sans les trous de deconnexion) |
| `total_counts` | Nombre total de coups cumules |
| `cps_mean` | Comptage par seconde moyen |
| `background_subtracted` | `true` si le background a ete soustrait |
| `isotopes_detected` | Liste des isotopes avec probabilite > seuil (0.5 par defaut) |
### Interpretation des resultats
- **Probabilite > 95%** : Detection certaine, l'isotope est present
- **Probabilite 70-95%** : Detection probable, a confirmer avec plus de donnees
- **Probabilite 50-70%** : Detection incertaine, probablement du bruit ou une confusion spectrale
- **Probabilite < 50%** : Non detecte (en dessous du seuil)
**Isotopes de background frequents** (normaux, pas une alerte) :
- K-40 : Toujours present dans l'environnement (~5-15 Bq)
- Bi-214 / Pb-214 : Filles du radon, varient selon la ventilation
- Ac-228 / Pb-212 / Tl-208 : Chaine du thorium, presents dans les materiaux de construction
**Isotopes d'interet** (a surveiller) :
- Cs-137 : Marqueur de contamination (accident nucleaire, sources industrielles)
- Co-60 : Source medicale/industrielle
- I-131 : Marqueur medical (scintigraphie thyroidienne)
- Am-241 : Source de calibration, detecteurs de fumee
- Ir-192 : Source industrielle (radiographie gamma)
---
## 7. Tester avec une source connue
Si vous avez une source radioactive connue (par ex. Cs-137) :
1. Placez la source pres du detecteur
2. Lancez le moniteur
3. Attendez au moins 1 heure d'acquisition (MIN_LIVE_TIME=3600)
4. Forcez un rapport manuel :
```bash
docker compose exec detect python -c "
from radiacode_monitor import RadiacodeMonitor
m = RadiacodeMonitor()
m.generate_report()
"
```
Pour un Cs-137 (662 keV, ~10 uSv/h a 10 cm), le resultat attendu est :
- Cs-137 detecte avec probabilite > 90%
- Activite estimee approximative (la calibration exacte necessite un etalonnage)
---
## 8. Nettoyer les checkpoints (~39 Go a recuperer)
Apres l'entrainement, seuls `vega_best.pt` et `vega_isotope_index.txt` sont necessaires :
```bash
# Supprimer les checkpoints intermediaires
rm models/vega_epoch_*.pt models/vega_final.pt
# Verifier
ls -lh models/
# vega_best.pt vega_history.json vega_isotope_index.txt
```
---
## 9. Transfer vers Raspberry Pi 4 (production)
Quand le pipeline fonctionne sur la machine GPU :
1. Copier les fichiers du modele :
```bash
scp models/vega_best.pt models/vega_isotope_index.txt pi@raspberrypi:~/radiacode_103/models/
```
2. Capturer le background sur le Pi (emplacement final) :
```bash
docker compose run --rm -d --name radiacode-bg \
-e TARGET_DURATION=86400 -e SAMPLE_INTERVAL=60 \
detect python capture_background.py
```
3. Adapter `docker-compose.yml` sur le Pi :
- Retirer `deploy.resources.reservations.devices` (pas de GPU)
- Mettre `VEGA_DEVICE=cpu`
- Le detect/Dockerfile fonctionne sur ARM64 (PyTorch a des wheels ARM)
4. Lancer :
```bash
docker compose up detect
```
L'inference CPU sur Pi 4 prend environ 0.5-1 seconde par spectre, ce qui est suffisant pour un rapport quotidien.
---
## 10. Variables d'environnement de reference
### Entrainement (`docker-compose.yml` - service `train`)
| Variable | Defaut | Description |
|----------|--------|-------------|
| `NUM_SAMPLES` | `50000` | Nombre de spectres synthetiques |
| `EPOCHS` | `100` | Epochs max d'entrainement |
| `BATCH_SIZE` | `64` | Taille de batch |
| `LEARNING_RATE` | `0.001` | Taux d'apprentissage initial |
| `DETECTOR` | `radiacode_103` | Config du detecteur |
| `MIN_DURATION` | `43200` | Duree min des spectres (12h en secondes) |
| `MAX_DURATION` | `86400` | Duree max des spectres (24h en secondes) |
| `NVIDIA_VISIBLE_DEVICES` | `1` | GPU a utiliser (0 ou 1) |
| `CUDA_VISIBLE_DEVICES` | `1` | GPU visible par CUDA |
### Detection (`docker-compose.yml` - service `detect`)
| Variable | Defaut | Description |
|----------|--------|-------------|
| `MODEL_PATH` | `/models/vega_best.pt` | Chemin du modele |
| `ISOTOPE_INDEX_PATH` | `/models/vega_isotope_index.txt` | Index des isotopes |
| `BACKGROUND_PATH` | `/data/background_24h.npy` | Fichier background |
| `VEGA_DEVICE` | `cpu` | Device PyTorch |
| `THRESHOLD` | `0.5` | Seuil de probabilite |
| `SAMPLE_INTERVAL` | `60` | Intervalle d'echantillonnage (s) |
| `REPORT_HOUR` | `0` | Heure du rapport quotidien |
| `MIN_LIVE_TIME` | `3600` | Live time min pour rapport (s) |
### Capture de background
| Variable | Defaut | Description |
|----------|--------|-------------|
| `TARGET_DURATION` | `86400` | Duree de capture (24h en secondes) |
| `SAMPLE_INTERVAL` | `60` | Intervalle entre echantillons (s) |
| `BACKGROUND_PATH` | `/data/background_24h.npy` | Fichier de sortie |