Skip to main content
La classe de dictionnaire naive_bayes (NAIVE_BAYES) classe le texte à l’aide d’un modèle Naive Bayes multinomial, le modèle d’événements standard pour le texte : elle attribue un score à chaque classe selon la fréquence d’apparition des n-grammes dans l’entrée. Vous lui fournissez une table de comptages de n-grammes par classe, qu’elle compile en modèle une seule fois, au moment du chargement, puis utilise pour classer tout texte que vous lui passez. Elle convient à une classification de texte rapide et légère, comme l’analyse des sentiments, l’étiquetage thématique ou anti-spam, ainsi que la détection de langue ou d’écriture. Vous interrogez le dictionnaire avec l’une des trois fonctions : Un simple dictGet permet aussi de classer (voir les Remarques). Une autre fonction, naiveBayesNgrams, ne classe pas — elle découpe le texte en n-grammes de la même manière que le dictionnaire, afin que vous puissiez créer les données d’entraînement à partir de texte brut (voir Créer des données d’entraînement à partir de texte brut).

Démarrage rapide

Nous allons construire un modèle unigramme (n = 1) en mode token pour l’analyse des sentiments. 1. Créez une table source contenant les décomptes de n-grammes par classe :
2. Insérez les données d’entraînement — des mots uniques (unigrammes) et leur fréquence d’apparition respective dans les classes positive (1) et négative (0) :
3. Créez le dictionnaire avec le layout NAIVE_BAYES :
PRIMARY KEY ngram fait de la colonne ngram la clé — mais pour un dictionnaire NAIVE_BAYES, cette « clé » est le texte que vous soumettez à la classification, et non une valeur stockée que vous recherchez (voir Structure du dictionnaire). Le LAYOUT configure le modèle : class_attribute 'class_id' désigne class_id comme l’étiquette de classe (l’autre attribut, count, correspondant alors au nombre d’occurrences par classe), n 1 utilise des unigrammes, et mode 'token' découpe le texte en mots séparés par des espaces (voir Paramètres du layout). 4. ClassifiernaiveBayesClassifier renvoie l’identifiant de classe :
1 correspond à la classe positive, d’après les données d’entraînement que nous avons insérées à l’étape 2.
De même, 0 correspond à la classe négative. On obtient le même résultat avec dictGet :
Obtenez la probabilité de la prédiction ou de chaque classe :
La prédiction correspond à la classe 0 (négative) avec une probabilité de 0.64.
naiveBayesClassifierWithAllProbs renvoie toutes les classes, triées de la plus probable à la moins probable, avec des probabilités dont la somme est égale à 1.0 — ici 0.64 pour la classe négative et 0.36 pour la classe positive.

Fonctionnement

Entraînement (au chargement). Chaque ligne source est une observation (n-gram, class, count). Lorsque le dictionnaire se charge, les lignes sont compilées une seule fois dans le modèle. Les lignes (n-gram, class) en double sont additionnées, et les lignes avec count = 0 sont ignorées. Classification (au moment de la requête). Pour classer une chaîne, le modèle :
  1. La découpe en n-grams selon mode et n (voir Modes de tokenisation).
  2. Attribue un score à chaque classe en combinant l’a priori de la classe avec la fréquence d’apparition des n-grams de l’entrée dans cette classe.
  3. Classe les classes par score. La classe ayant le score le plus élevé est la prédiction renvoyée par naiveBayesClassifier ; naiveBayesClassifierWithProb et naiveBayesClassifierWithAllProbs renvoient aussi des probabilités — pour cette classe ou pour l’ensemble des classes.
Deux éléments influencent le score de chaque classe. Le premier est alpha, utilisé pour le lissage. Le lissage empêche le modèle d’attribuer à une classe un score nul simplement parce qu’un n-gram n’est pas apparu dans cette classe pendant l’entraînement. Un alpha plus faible fait davantage reposer le modèle sur les données d’entraînement, si bien qu’une classe peut obtenir un score beaucoup plus élevé que les autres, mais cela peut aussi rendre le modèle trop sensible lorsque les données d’entraînement sont peu nombreuses ou déséquilibrées. Un alpha plus élevé réduit l’importance des décomptes de n-grams, de sorte que les scores des différentes classes se rapprochent. Si alpha est très élevé, les informations apportées par les n-grams comptent à peine, et le score dépend surtout de l’a priori de la classe (décrit ci-après). Le second est l’a priori de la classe — ce que le modèle suppose quant à la probabilité de chaque classe avant d’examiner le texte. Il agit comme un score initial attribué à chaque classe avant que les n-grams ne soient pris en compte, si bien qu’un a priori plus élevé rend une classe plus susceptible d’être prédite. Son paramétrage dépend de priors_mode. Par défaut (proportional), une classe dont le nombre total de n-grams est plus élevé dans les données d’entraînement commence avec un score plus élevé. Avec uniform, toutes les classes démarrent à égalité, si bien que seuls les n-grams font la différence. Avec explicit, vous définissez vous-même le point de départ de chaque classe. Voir Modes d’a priori. Un n-gram qui n’a jamais été vu dans les données d’entraînement est ignoré : il ne fait pas partie du vocabulaire du modèle et n’aide ni ne pénalise donc aucune classe. L’algorithme suit le modèle multinomial de Naive Bayes pour la classification de texte ; voir Manning, Raghavan & Schütze, Introduction to Information Retrieval, ch. 13 (Text Classification and Naive Bayes).

Structure du dictionnaire

Un dictionnaire NAIVE_BAYES a une structure fixe :
  • La PRIMARY KEY est une unique colonne String — le n-gram. Au moment de la requête, cette “clé” est le texte que vous fournissez pour la classification, et non une clé de lookup stockée.
  • À ses côtés, déclarez exactement deux attributs de type entier non signé : l’étiquette de classe et le nombre d’occurrences. Les identifiants de classe utilisent toujours UInt32 en interne ; une étiquette de classe doit donc tenir dans UInt32 (au maximum 4294967295), même si vous déclarez son attribut en UInt64. Une valeur plus grande est rejetée lors du chargement du dictionnaire, et non lors de sa création. Il en va de même pour les types déclarés : si un identifiant de classe source ou un nombre ne tient pas dans le type d’attribut déclaré, le chargement échoue au lieu d’être tronqué silencieusement.
  • Le paramètre class_attribute du layout désigne l’attribut correspondant à l’étiquette de classe ; l’autre est automatiquement interprété comme le nombre. Les deux attributs peuvent être déclarés dans n’importe quel ordre.
La table source contient des nombres pré-agrégés : une ligne par (n-gram, class) avec le nombre de fois où ce n-gram est apparu dans cette classe. Vous produisez ces nombres en tokenisant votre corpus et en groupant le résultat, soit dans votre propre pipeline d’entraînement, soit dans ClickHouse à partir de texte brut labellisé (voir Build training data from raw text). Le dictionnaire se contente de les consommer. Mise à jour du modèle. Comme le modèle est un dictionnaire adossé à une table, réentraînez-le en mettant à jour la table puis en le rechargeant :

Paramètres de layout

Vous pouvez définir le dictionnaire avec la DDL CREATE DICTIONARY (comme dans le guide de démarrage rapide ci-dessus) ou dans un fichier de configuration XML ; voir layouts de dictionnaire pour savoir où placer ce fichier. L’exemple ci-dessous définit toutes les options de layout afin que vous puissiez toutes les voir — seuls class_attribute, n et mode sont obligatoires, et le tableau ci-dessus donne les valeurs par défaut des autres. Dans un fichier de configuration, les probabilités a priori s’écrivent sous forme d’éléments prior répétés (un par classe, comme illustré ci-dessous), les tokens de padding pour byte et codepoint sont des nombres (la config ne peut pas contenir d’octets bruts), et un littéral token est échappé en XML si nécessaire ; ainsi, <s> devient &lt;s&gt;.

Modes de tokenisation

mode détermine ce qu’est un « token » et, par conséquent, la forme des n-grammes. Les n-grammes source doivent avoir été produits avec le même mode et le même n.
  • byte — chaque token est un octet unique ; aucune hypothèse n’est faite sur l’UTF-8. Avec n = 2, 'abc' produit les bigrammes d’octets 'ab', 'bc'. Adapté à la détection de la langue ou de l’encodage sur des séquences d’octets arbitraires, ainsi qu’à toute donnée où un signal sous-caractère est important. Généralement utilisé avec n >= 2.
  • codepoint — chaque token correspond à un point de code Unicode ; l’entrée est interprétée comme de l’UTF-8. Avec n = 1, 'café' produit les points de code 'c', 'a', 'f', 'é'. Adapté à la détection du système d’écriture et de la langue, ainsi qu’aux textes courts ou en CJK, où les séparations entre mots par des espaces sont peu fiables. (Les n-grammes source doivent être en UTF-8 valide ; l’entrée de la requête est décodée de façon permissive — voir les Notes.)
  • token — chaque token est un mot délimité par des espaces ASCII (espace, tabulation, saut de ligne, retour chariot, saut de page, tabulation verticale ; les séquences consécutives sont réduites à un seul séparateur). Les espaces Unicode non ASCII, tels que U+00A0 (espace insécable) ou U+2003 (espace cadratin), ne sont pas des séparateurs et restent à l’intérieur d’un token. Les espaces sont la seule chose qui segmente — rien n’est mis en minuscules ni supprimé — ainsi, 'Hello, World!' devient les tokens 'Hello,' et 'World!' (la virgule, le ! et les majuscules sont tous conservés), et avec n = 2 ils forment l’unique bigramme 'Hello, World!'. Adapté à la classification au niveau des mots pour les langues séparées par des espaces — sentiment, thème, spam, langue d’une phrase.

Modes de probabilité a priori

La probabilité a priori correspond à ce que le modèle suppose pour chaque classe avant d’examiner le texte. priors_mode détermine comment elle est définie.
  • proportional (par défaut) — la probabilité a priori de chaque classe est proportionnelle à son nombre total de n-grammes dans les données d’entraînement — c’est-à-dire la somme de la colonne count pour cette classe, et non son nombre de lignes ou de documents d’entraînement — de sorte que les classes observées plus souvent sont initialement plus probables. Choisissez ce mode lorsque les proportions des classes dans l’entraînement (par nombre total de n-grammes) correspondent aux fréquences attendues au moment de la requête. Rien à fournir — elle est dérivée des comptes de la source.
  • uniform — au départ, toutes les classes sont équiprobables, donc aucune ne part avec un avantage et la prédiction repose entièrement sur les n-grammes de l’entrée. Choisissez ce mode lorsque les classes sont équilibrées, ou lorsque les fréquences d’entraînement ne reflètent pas la fréquence d’apparition de chaque classe au moment de la requête. Rien à fournir.
  • explicit — vous fournissez les probabilités a priori avec priors [(0, 0.6), (1, 0.4)] : une paire (classe, probabilité) par classe, chaque probabilité étant strictement supérieure à 0 et inférieure ou égale à 1, le tout devant totaliser 1.0. Choisissez ce mode lorsque vous connaissez les taux de base réels et qu’ils diffèrent de ceux de l’entraînement — par exemple, si seulement 1 % du trafic de production est du spam alors que l’ensemble d’entraînement était équilibré. Calculez-les à partir de la proportion réelle attendue pour chaque classe.

Tokens de délimitation (padding)

Le padding est désactivé par défaut. Il n’a d’intérêt que pour n > 1, où il peut améliorer la précision en permettant au modèle d’utiliser des signaux au début et à la fin du texte. Pourquoi c’est utile. Avec n > 1, les n-grams au milieu du texte bénéficient d’un contexte complet à gauche et à droite, mais ce n’est pas le cas des premiers et derniers tokens. L’ajout de tokens de délimitation crée des n-grams qui marquent le « début du texte » et la « fin du texte », afin que le modèle puisse apprendre des motifs liés à la position — par exemple, un mot distinctif lorsqu’il ouvre un message, ou un caractère typique à la fin d’un mot. Ce que vous devez faire :
  1. Décidez pour chaque côté. start_token et end_token sont indépendants — définissez l’un, les deux ou aucun des deux. Une valeur vide signifie qu’aucun padding n’est appliqué de ce côté.
  2. Choisissez des valeurs rares qui n’entreront pas en collision avec des données réelles, par exemple 0x01 / 0xFF pour byte, U+10FFFE / U+10FFFF pour codepoint, ou <s> / </s> pour token.
  3. Générez les n-grams d’entraînement avec le même padding. Le dictionnaire applique le padding à l’entrée de la requête, mais jamais à votre source ; les tokens de délimitation doivent donc déjà être intégrés aux n-grams que vous chargez. Le moyen le plus simple de garantir la correspondance consiste à construire la source avec naiveBayesNgrams, en lui passant les mêmes start_token et end_token (ainsi que n et mode) que pour le layout — la fonction émet exactement les n-grams paddés que le dictionnaire produit au moment de la requête.
Le format du token de padding dépend du mode :
  • byte — un nombre pour la valeur de l’octet, en décimal ou en hexadécimal 0x (ainsi, '1' et '0x01' sont identiques) :
  • codepoint — un nombre pour le point de code UTF-8, en décimal ou en hexadécimal 0x (ainsi, '1114110' et '0x10FFFE' sont identiques) :
  • token — la chaîne littérale du token :

Créer des données d’entraînement à partir de texte brut

Si vous partez de texte brut étiqueté plutôt que de décomptes préagrégés, utilisez la fonction naiveBayesNgrams pour le découper en n-grammes. Donnez-lui les mêmes valeurs n, mode, start_token et end_token que votre layout, et elle produira exactement les n-grammes attendus par le dictionnaire, afin que les données d’entraînement correspondent à ce que le modèle voit au moment de la requête. À partir d’une table de lignes (class_id, text), construisez la source (ngram, class_id, count) avec un seul GROUP BY :
training_data est désormais une source valide pour un dictionnaire NAIVE_BAYES (ici, des unigrammes de tokens ; modifiez les arguments n et mode pour qu’ils correspondent à votre layout). Le dictionnaire tokenize l’entrée de la requête exactement telle qu’elle est fournie. Ainsi, si le texte d’entraînement est en minuscules mais pas le texte de requête, leurs n-grams ne correspondront pas et la précision du modèle s’en ressentira.
A priori et nombre de documentsL’a priori proportional (par défaut) est pondéré par le nombre total de n-grams de chaque classe, et non par son nombre de documents. Si vous voulez l’a priori classique basé sur la fréquence des documents (documents_in_class / total_documents), calculez-le à partir de la table brute docs et transmettez-le avec priors_mode 'explicit' :
Ensuite, créez le dictionnaire à partir de training_data, en transmettant l’a priori explicite calculé ci-dessus, puis classez les nouveaux avis :
La classe 1 est positive et 0 est négative, donc les deux avis sont bien classés.

Autres exemples

Mode octet — bigrammes d’octets (n = 2, mode 'byte' ; classe 0 = chaînes de lettres ad, classe 1 = lettres xz) :
Mode point de code — détection de l’écriture caractère par caractère (n = 1, mode 'codepoint'; class 0 = latin, 1 = cyrillique) :
Relisez les données d’entraînement à l’aide de store_source :
Détection de la langue à partir de texte brut — mots courts avec padding aux limites (n = 2, mode 'codepoint' ; classe 0 = anglais, 1 = espagnol). Les n-grammes d’entraînement sont construits à partir de mots bruts avec naiveBayesNgrams, et les tokens de délimitation — transmis à la fois à la fonction et au layout — permettent au modèle d’utiliser la première et la dernière lettre de chaque mot :

Remarques

  • Sémantique d’un dictionnaire de calcul. Il s’agit d’un dictionnaire de calcul : dictGet(dict, '<class_attribute>', text) classe text (la clé est une entrée à classer, et non une clé stockée), l’attribut de décompte ne peut pas être interrogé, et dictHas renvoie toujours 1.
  • Validation de la source au chargement. Chaque n-gramme source doit correspondre aux valeurs configurées de n et de mode (en mode codepoint, il doit aussi être en UTF-8 valide) ; à la moindre divergence, le chargement échoue. Comme les lignes dont le décompte est nul sont ignorées (voir Fonctionnement), une source vide ou ne contenant que des décomptes nuls n’offre aucune donnée d’entraînement et ne peut pas être chargée.
  • La tokenisation au moment de la requête est tolérante. Contrairement à la validation de la source, l’entrée de la requête n’est jamais rejetée. En mode codepoint, les octets qui ne sont pas en UTF-8 valide sont décodés au mieux au lieu de faire échouer la requête ; en mode token, seuls les espaces ASCII séparent les mots (les espaces Unicode comme U+00A0 restent à l’intérieur d’un token). Une entrée mal formée est tout de même classée — généralement d’après les probabilités a priori, puisque ses n-grammes ne correspondront pas à ceux appris.
Dernière modification le 23 juillet 2026