PharmAfiyahProducts — doctobert fine-tuné pour le matching de produits pharmaceutiques
But du modèle
Ce modèle est un bi-encoder de similarité textuelle, fine-tuné à partir de doctolib-lab/doctobert-fr-base pour la recherche de produits pharmaceutiques et parapharmaceutiques par similarité : à partir d'une désignation de produit (souvent abrégée, écrite de façon incohérente d'un système à l'autre), il permet de retrouver dans un référentiel les produits qui désignent le même produit et la même présentation (même marque, principe actif, dosage, forme galénique, volume, conditionnement), avec un score de similarité cosinus.
Il est conçu pour une étape de retrieval rapide (embeddings + recherche par similarité, ex. via un index FAISS) : à partir d'un produit donné, le modèle retrouve les candidats les plus proches dans un référentiel de produits, à utiliser ensuite pour une déduplication de catalogue, un matching produit, ou une recherche assistée.
Ce n'est pas un modèle de similarité sémantique générale — il est spécialisé sur des noms de produits courts, très abrégés, avec un fort enjeu à distinguer des textes presque identiques qui ne diffèrent que par un dosage, un volume ou une marque (ex. "AMCARD CPR 10MG BT14" vs "AMCARD CPR 5MG BT14" doivent être écartés, pas rapprochés).
Métriques (classification binaire par similarité cosinus, jeu de validation)
| Métrique | Valeur |
|---|---|
| Cosine AP (Average Precision) | 0.9685 |
| Cosine F1 | 0.8976 |
| Cosine Precision | 0.9195 |
| Cosine Recall | 0.8752 |
| Cosine Accuracy | 0.8976 |
| Cosine MCC | 0.7963 |
Données d'entraînement
- Base :
doctolib-lab/doctobert-fr-base - Paires labellisées (1 = même produit/présentation, 0 = différent), issues de comparaisons de désignations de produits pharmaceutiques, labellisées par un LLM puis vérifiées
- 8481 paires d'entraînement / 1387 de validation, labels globalement équilibrés (train : 4252 négatifs / 4229 positifs)
- Loss :
OnlineContrastiveLoss(similarité cosinus)
This is a sentence-transformers model finetuned from doctolib-lab/doctobert-fr-base. It maps sentences & paragraphs to a 768-dimensional dense vector space and can be used for semantic textual similarity, semantic search, paraphrase mining, text classification, clustering, and more.
Model Details
Model Description
- Model Type: Sentence Transformer
- Base model: doctolib-lab/doctobert-fr-base
- Maximum Sequence Length: 64 tokens
- Output Dimensionality: 768 dimensions
- Similarity Function: Cosine Similarity
Model Sources
- Documentation: Sentence Transformers Documentation
- Repository: Sentence Transformers on GitHub
- Hugging Face: Sentence Transformers on Hugging Face
Full Model Architecture
SentenceTransformer(
(0): Transformer({'max_seq_length': 64, 'do_lower_case': False, 'architecture': 'RobertaModel'})
(1): Pooling({'word_embedding_dimension': 768, 'pooling_mode_cls_token': False, 'pooling_mode_mean_tokens': True, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
)
Usage
Direct Usage (Sentence Transformers)
First install the Sentence Transformers library:
pip install -U sentence-transformers
Then you can load this model and run inference.
from sentence_transformers import SentenceTransformer
# Download from the 🤗 Hub
model = SentenceTransformer("sentence_transformers_model_id")
# Run inference
sentences = [
'ACILOC 25MG/ML AMP/2ML BT5 AZANTAC',
'AZANTAC ACILOC 25MG/ML AMP/2ML B/5',
'NATURALIA CR SPF20 DEPIG 50 ML',
]
embeddings = model.encode(sentences)
print(embeddings.shape)
# [3, 768]
# Get the similarity scores for the embeddings
similarities = model.similarity(embeddings, embeddings)
print(similarities)
# tensor([[1.0000, 0.9727, 0.3115],
# [0.9727, 1.0000, 0.2771],
# [0.3115, 0.2771, 1.0000]])
Evaluation
Metrics
Binary Classification
- Dataset:
validation - Evaluated with BinaryClassificationEvaluator
| Metric | Value |
|---|---|
| cosine_accuracy | 0.8976 |
| cosine_accuracy_threshold | 0.8936 |
| cosine_f1 | 0.8968 |
| cosine_f1_threshold | 0.8881 |
| cosine_precision | 0.9195 |
| cosine_recall | 0.8752 |
| cosine_ap | 0.9685 |
| cosine_mcc | 0.7963 |
Training Details
Training Dataset
Unnamed Dataset
- Size: 8,481 training samples
- Columns: name_left, name_right, and label
- Approximate statistics based on the first 1000 samples:
name_left name_right label type string string float details min: 3 tokensmean: 16.28 tokensmax: 34 tokens min: 4 tokensmean: 16.81 tokensmax: 32 tokens min: 0.0mean: 0.49max: 1.0 - Samples:
name_left name_right label EFFACLAR DUO+ 40ML M6940501 EFFACLAR DUO+ 40 ML M6940501 1.0 AVENE CLEAN SPOT SOIN LOCALIS AVENE CLEAN SPOT SOIN LOCALISE 1.0 Canpol Tétine Biberon Standart Croix 2pcs 6M+ Canpol Tétine Biberon Standart Slow 3M+ 2pcs 0.0 - Loss: OnlineContrastiveLoss
Training Hyperparameters
Non-Default Hyperparameters
eval_strategy: stepsgradient_accumulation_steps: 4learning_rate: 2e-05num_train_epochs: 8warmup_ratio: 0.1data_seed: 42fp16: Trueload_best_model_at_end: Trueauto_find_batch_size: True
All Hyperparameters
overwrite_output_dir: Falsedo_predict: Falseeval_strategy: stepsprediction_loss_only: Trueper_device_train_batch_size: 8per_device_eval_batch_size: 8per_gpu_train_batch_size: Noneper_gpu_eval_batch_size: Nonegradient_accumulation_steps: 4eval_accumulation_steps: Nonetorch_empty_cache_steps: Nonelearning_rate: 2e-05weight_decay: 0.0adam_beta1: 0.9adam_beta2: 0.999adam_epsilon: 1e-08max_grad_norm: 1.0num_train_epochs: 8max_steps: -1lr_scheduler_type: linearlr_scheduler_kwargs: Nonewarmup_ratio: 0.1warmup_steps: 0log_level: passivelog_level_replica: warninglog_on_each_node: Truelogging_nan_inf_filter: Truesave_safetensors: Truesave_on_each_node: Falsesave_only_model: Falserestore_callback_states_from_checkpoint: Falseno_cuda: Falseuse_cpu: Falseuse_mps_device: Falseseed: 42data_seed: 42jit_mode_eval: Falsebf16: Falsefp16: Truefp16_opt_level: O1half_precision_backend: autobf16_full_eval: Falsefp16_full_eval: Falsetf32: Nonelocal_rank: 0ddp_backend: Nonetpu_num_cores: Nonetpu_metrics_debug: Falsedebug: []dataloader_drop_last: Falsedataloader_num_workers: 0dataloader_prefetch_factor: Nonepast_index: -1disable_tqdm: Falseremove_unused_columns: Truelabel_names: Noneload_best_model_at_end: Trueignore_data_skip: Falsefsdp: []fsdp_min_num_params: 0fsdp_config: {'min_num_params': 0, 'xla': False, 'xla_fsdp_v2': False, 'xla_fsdp_grad_ckpt': False}fsdp_transformer_layer_cls_to_wrap: Noneaccelerator_config: {'split_batches': False, 'dispatch_batches': None, 'even_batches': True, 'use_seedable_sampler': True, 'non_blocking': False, 'gradient_accumulation_kwargs': None}parallelism_config: Nonedeepspeed: Nonelabel_smoothing_factor: 0.0optim: adamw_torch_fusedoptim_args: Noneadafactor: Falsegroup_by_length: Falselength_column_name: lengthproject: huggingfacetrackio_space_id: trackioddp_find_unused_parameters: Noneddp_bucket_cap_mb: Noneddp_broadcast_buffers: Falsedataloader_pin_memory: Truedataloader_persistent_workers: Falseskip_memory_metrics: Trueuse_legacy_prediction_loop: Falsepush_to_hub: Falseresume_from_checkpoint: Nonehub_model_id: Nonehub_strategy: every_savehub_private_repo: Nonehub_always_push: Falsehub_revision: Nonegradient_checkpointing: Falsegradient_checkpointing_kwargs: Noneinclude_inputs_for_metrics: Falseinclude_for_metrics: []eval_do_concat_batches: Truefp16_backend: autopush_to_hub_model_id: Nonepush_to_hub_organization: Nonemp_parameters:auto_find_batch_size: Truefull_determinism: Falsetorchdynamo: Noneray_scope: lastddp_timeout: 1800torch_compile: Falsetorch_compile_backend: Nonetorch_compile_mode: Noneinclude_tokens_per_second: Falseinclude_num_input_tokens_seen: noneftune_noise_alpha: Noneoptim_target_modules: Nonebatch_eval_metrics: Falseeval_on_start: Falseuse_liger_kernel: Falseliger_kernel_config: Noneeval_use_gather_object: Falseaverage_tokens_across_devices: Trueprompts: Nonebatch_sampler: batch_samplermulti_dataset_batch_sampler: proportionalrouter_mapping: {}learning_rate_mapping: {}
Training Logs
| Epoch | Step | Training Loss | validation_cosine_ap |
|---|---|---|---|
| 0.5014 | 133 | 0.3404 | 0.8725 |
| 1.0 | 266 | 0.1281 | 0.9267 |
| 1.5014 | 399 | 0.0943 | 0.9419 |
| 2.0 | 532 | 0.077 | 0.9508 |
| 2.5014 | 665 | 0.0488 | 0.9504 |
| 3.0 | 798 | 0.0544 | 0.9544 |
| 3.5014 | 931 | 0.033 | 0.9566 |
| 4.0 | 1064 | 0.0394 | 0.9571 |
| 4.5014 | 1197 | 0.0308 | 0.9590 |
| 5.0 | 1330 | 0.0282 | 0.9662 |
| 5.5014 | 1463 | 0.0252 | 0.9654 |
| 6.0 | 1596 | 0.0255 | 0.9656 |
| 6.5014 | 1729 | 0.0192 | 0.9685 |
| 7.0 | 1862 | 0.0149 | 0.9685 |
| 7.5014 | 1995 | 0.0185 | 0.9686 |
| 8.0 | 2128 | 0.0152 | 0.9685 |
- The bold row denotes the saved checkpoint.
Framework Versions
- Python: 3.13.11
- Sentence Transformers: 5.2.2
- Transformers: 4.57.6
- PyTorch: 2.10.0+cu128
- Accelerate: 1.12.0
- Datasets: 4.5.0
- Tokenizers: 0.22.2
Citation
BibTeX
Sentence Transformers
@inproceedings{reimers-2019-sentence-bert,
title = "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks",
author = "Reimers, Nils and Gurevych, Iryna",
booktitle = "Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing",
month = "11",
year = "2019",
publisher = "Association for Computational Linguistics",
url = "https://arxiv.org/abs/1908.10084",
}