# Système de reconnaissance d'entités nommées — Wolof
Projet M2 DSIA / M2 AI (ISI Dakar) — Groupe 4 : Rokhaya, Babou, Adama, Moustapha.
BiLSTM-CRF entraîné sur le corpus MasakhaNER
(Wolof) pour reconnaître 4 types d'entités : **PER**, **LOC**, **ORG**, **DATE**.
Le CRF (Conditional Random Field) est implémenté **from scratch** (forward algorithm
+ Viterbi decoding), sans dépendance externe type `torchcrf`.
## Structure
```
wolof_ner/
├── data/
│ ├── train.txt # 1871 phrases (format CoNLL)
│ ├── dev.txt # 267 phrases
│ └── test.txt # 539 phrases
├── src/
│ ├── data_utils.py # parsing CoNLL, vocabulaire, Dataset PyTorch
│ ├── crf.py # couche CRF from scratch (forward algo + Viterbi)
│ ├── model.py # BiLSTM-CRF
│ ├── train.py # entraînement
│ └── evaluate.py # évaluation F1 au niveau entité (seqeval)
├── app/
│ └── app.py # démo Streamlit
├── checkpoints/ # créé après entraînement (poids, vocab, config)
└── requirements.txt
```
## Installation
```bash
python -m venv venv
source venv/bin/activate # Windows (Git Bash) : source venv/Scripts/activate
pip install -r requirements.txt
```
## 1. Entraînement
```bash
cd src
python train.py --epochs 30 --batch_size 16 --embedding_dim 128 --hidden_dim 256
```
Sauvegarde dans `checkpoints/` : `best_model.pt`, `word2idx.json`, `tag2idx.json`, `config.json`.
Le modèle retenu (`best_model.pt`) est celui qui minimise la loss sur le **dev** set
(early-stopping implicite : on garde le meilleur, pas forcément le dernier).
> Un entraînement complet a été lancé pour valider le pipeline de bout en bout
> (25 epochs, CPU, quelques minutes vu la taille réduite du corpus ~1900 phrases).
> Le dev_loss est minimal dès l'epoch 4 puis remonte (overfitting) — le modèle
> retenu (`best_model.pt`) est donc celui de l'epoch 4, pas le dernier.
## 2. Évaluation
```bash
cd src
python evaluate.py --checkpoint ../checkpoints --data_dir ../data --split test …