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>
416 lines
16 KiB
Markdown
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 | |