Série de tests

Classification naïve bayésienne avec C#

James McCaffrey

Télécharger l'exemple de code

 

James McCaffreyLa classification naïve bayésienne est une technique d'apprentissage automatique qui peut être utilisée pour prédire à quelle catégorie appartient un cas de données spécifique. Dans cet article, j'explique le fonctionnement de la classification naïve bayésienne en m'appuyant sur un exemple codé avec le langage C#.

Il existe de nombreux outils autonomes qui sont en mesure de réaliser la classification naïve bayésienne. Néanmoins, ces outils peuvent être difficiles, voire impossibles à intégrer directement dans votre application, et compliqués à personnaliser pour répondre à des besoins spécifiques. Ils peuvent de plus présenter des problèmes de copyright cachés. Cet article vous fournira de solides bases pour l'ajout de fonctionnalités de classification naïve bayésienne à une application .NET, sans devoir vous appuyer sur des dépendances externes.

Pour mieux comprendre ce qu'est la classification naïve bayésienne et voir où je veux en venir dans cet article, observez la capture d'écran d'un programme de démonstration de la figure 1. Ce programme de démonstration commence par générer 40 lignes de données qui seront utilisées pour former le classificateur. Dans la plupart des cas, vous utiliserez une source de données existante, mais j'ai généré des données fictives pour que la démonstration reste simple. La première ligne de données est « administrative,right,72.0,female ». Le premier champ concerne la profession, le deuxième concerne la prédominance main gauche/main droite, le troisième, la taille en pouces et le quatrième, le sexe. Ce classificateur a pour objectif de prédire le sexe à partir d'un ensemble donné de valeurs concernant la profession, la prédominance main gauche/main droite et la taille. Du fait que la variable dépendante sexe peut accepter deux valeurs, il s'agit d'un exemple de classification binaire.

Naive Bayes Classification Demo
Figure 1 Démonstration de classification naïve bayésienne

Une fois des données brutes générées, le programme de démonstration convertit chaque champ numérique de taille en catégorie (petite, moyenne ou grande) en classifiant la taille. Comme je l'expliquerai par la suite, la classification de données numériques en données catégoriques est une approche qui présente des avantages et des inconvénients. Une fois les données d'apprentissage classifiées, le programme de démonstration analyse les 40 lignes de données catégoriques et calcule des résultats communs. Par exemple, le nombre de cas de données pour lesquels la personne occupe une profession administrative et est de sexe masculin est de 2. De plus, les nombres totaux de chaque valeur dépendante (l'attribut à prédire, masculin ou féminin dans cet exemple) sont calculés. Vous pouvez voir que les données d'apprentissage comptent 24 hommes et 16 femmes.

Le programme de démonstration dispose alors de toutes les informations nécessaires pour classifier le sexe d'un nouveau cas de données pour lequel les champs de profession, prédominance et taille correspondent respectivement aux valeurs suivantes : enseignement, main droite et grande. Il s'avère, dans cet exemple, que la démonstration a déterminé que la probabilité pour que le cas de données soit un homme est de 0,3855 et la probabilité pour que le cas corresponde à une femme est de 0,6145. Le système conclut donc que le cas de données est très certainement une femme.

Dans les sections suivantes, j'expliquerai tout d'abord le fonctionnement exact de la classification naïve bayésienne, je détaillerai le code dans le programme de démonstration, puis je décrirai de quelle manière modifier la démonstration pour satisfaire vos propres besoins. Cet article suppose que vous avez des compétences au moins de débutant en programmation avec un langage de la famille C, mais ne suppose pas que vous avez des connaissances en matière de classification naïve bayésienne. Le code du programme de démonstration est un peu trop long pour être présenté ici dans son intégralité, mais la source complète est disponible sur le site de téléchargement MSDN, à l'adresse suivante : archive.msdn.microsoft.com/mag201302TestRun.

Fonctionnement de la classification naïve bayésienne

En s'appuyant sur l'exemple présenté à la figure 1, l'objectif consiste à prédire le sexe (masculin ou féminin) d'une personne occupant un poste d'enseignant, droitière et de grande taille (supérieure ou égale à 71 pouces (1,80 m)). Nous pouvons pour ce faire calculer la probabilité pour que cette personne soit un homme en fonction de ces informations, et celle pour que cette personne soit une femme en fonction de ces informations, puis prédire le sexe avec la plus grande probabilité. Exprimé symboliquement, nous voulons connaître la valeur correspondant à P(male | X), généralement lue comme suit : « la probabilité qu'il s'agisse d'un homme étant données des valeurs de variable indépendante X ») et P(female | X), où X est (enseignement, droite, grande). Le terme « naïve » dans l'expression naïve bayésienne signifie que tous les attributs X sont supposés être mathématiquement indépendants, ce qui simplifie grandement la classification. Vous pouvez trouver de nombreuses références en ligne expliquant le raisonnement mathématique plutôt intéressant qui se cache derrière la classification naïve bayésienne, mais le résultat est assez simple. Symboliquement :

P(male | X) =
  [ P(education | male) * P(right | male) * P(tall | male) * P(male) ] /
    [ PP(male | X) + PP(female | X) ]

Notez que l'équation est une fraction. Le numérateur, parfois librement qualifié de probabilité partielle, se compose de quatre termes multipliés entre eux. Dans cet article, j'utilise la notation non standard de PP pour un terme de probabilité partielle. Le dénominateur correspond à la somme de deux termes, dont l'un est le numérateur. Le premier élément à calculer est P(education | male), ou la probabilité pour que la profession d'une personne soit dans l'enseignement, étant donné qu'il s'agit d'un homme. Cela, en définitive, peut être estimé en fonction du nombre de cas d'apprentissage pour lesquels la profession est l'enseignement et le sexe masculin, divisé par le nombre de cas étant des hommes (quelle que soit la profession). Ainsi :

P(education | male ) = count(education & male) / count(male) = 2/24 = 0.0833

Suivant la même logique :

P(right | male) = count(right & male) / count(male) = 17/24 = 0.7083
P(tall | male) = count(tall & male) / count(male) = 4/24 = 0.1667

La pièce suivante du puzzle est P(male). Dans la terminologie naïve bayésienne, cela s'appelle un antécédent. La meilleure manière de calculer des antécédents soulève des discussions. Nous pouvons d'un côté supposer qu'il n'y a aucune raison de penser que la présence des hommes revête une probabilité supérieure ou inférieure à celle des femmes et affecter en conséquence 0,5 à P(male). D'un autre côté, nous pouvons utiliser le fait que les données d'apprentissage comptent 24 hommes et 16 femmes et estimer une probabilité de 24/40 = 0,6000 pour P(male). Je préfère l'approche consistant à évaluer les antécédents à l'aide de données d'apprentissage.

Si vous vous référez à présent à l'équation précédente pour P(male | X), vous remarquerez qu'elle contient la valeur PP(female | X). La somme du bas, PP(male | X) + PP(female | X), est parfois appelée la preuve. Les éléments de PP(female | X) sont calculés comme suit :

P(education | female) = count(education & female) / count(female) = 4/16 = 0.2500
P(right | female) = count(right & female) / count(female) = 14/16 = 0.8750
P(tall | female) = count(tall & female) / count(female) = 2/16 = 0.1250
P(female) = 16/40 = 0.4000

Ainsi, le numérateur de probabilité partielle pour P(male | X) est le suivant :

PP(male | X) = 0.0833 * 0.7083 * 0.1667 * 0.6000 = 0.005903

Si l'on utilise la même logique, la probabilité partielle pour une femme si l'on a X = (enseignement, droite, grande) est la suivante :

PP(female | X) = 0.2500 * 0.8750 * 0.1250 * 0.4000 = 0.010938

Et, enfin, les probabilités globales pour hommes et femmes sont les suivantes :

P(male | X) = 0.005903 / (0.005903 + 0.010938) = 0.3505
P(female | X) = 0.010938 / (0.005903 + 0.010938) = 0.6495

Ces probabilités globales sont parfois appelées les valeurs postérieures. Du fait que la valeur P(female | X) est supérieure à P(male | X), le système conclut que l'inconnue est de sexe féminin. Mais attendez une minute. Ces deux probabilités, 0,3505 et 0,6495, sont proches mais certainement pas identiques aux deux probabilités 0,3855 et 0,6145 de la figure 1. La raison de cette différence repose sur le fait que le programme de démonstration utilise une modification optionnelle importante de la naïve bayésienne appelée lissage laplacien.

Lissage laplacien

Si vous consultez la figure 1, vous verrez que le nombre de cas d'apprentissage pour lesquels la profession de l'individu = construction et le sexe = féminin est de 0. Dans la démonstration, les valeurs X sont (enseignement, droite, grande), ce qui n'inclut pas la construction. Mais supposons que X ait été (construction, droite, grande). Dans le calcul de PP(female | X), il serait nécessaire de calculer P(construction | female) = count(construction & female) / count(female), dont le total serait de 0, ce qui, à son tour, annulerait toute la probabilité partielle. En résumé, un résultat commun de 0 n'est pas favorable. La technique la plus courante pour éviter cette situation consiste simplement à ajouter 1 à tous les résultats communs. Cela peut sembler intrusif mais, en réalité, cela s'appuie sur une solide base mathématique. La technique s'appelle le lissage d'incrément par un, qui est un type spécifique de lissage laplacien.

Avec le lissage laplacien, si X = (enseignement, droite, grande) comme dans la section précédente, les valeurs P(male | X) et P(female | X) sont calculées comme suit :

P(education | male ) =
count(education & male) + 1 / count(male) + 3 = 3/27 = 0.1111
P(right | male) =
count(right & male) + 1 / count(male) + 3 = 18/27 = 0.6667
P(tall | male) =
count(tall & male) + 1 / count(male) + 3 = 5/27 = 0.1852
P(male) = 24/40 = 0.6000
P(education | female) =
count(education & female) + 1 / count(female) + 3 = 5/19 = 0.2632
P(right | female) =
count(right & female) + 1 / count(female) + 3 = 15/19 = 0.7895
P(tall | female) =
count(tall & female) + 1 / count(female) + 3 = 3/19 = 0.1579
P(female) = 16/40 = 0.4000

Les probabilités partielles sont les suivantes :

PP(male | X) = 0.1111 * 0.6667 * 0.1852 * 0.6000 = 0.008230
PP(female | X) = 0.2632 * 0.7895 * 0.1579 * 0.4000 = 0.013121

Et de ce fait, les deux dernières probabilités sont les suivantes :

P(male | X) = 0.008230 / (0.008230 + 0.013121) = 0.3855
P(female | X) = 0.013121 / (0.008230 + 0.013121) = 0.6145

Voici les valeurs présentées dans la capture d'écran de la figure 1. Vous noterez que 1 est ajouté à chaque total commun mais que 3 est ajouté aux dénominateurs count(male) et count(female). Ce 3 est dans une certaine mesure arbitraire en cela que le lissage laplacien ne précise aucune valeur spécifique à utiliser. Dans ce cas, c'est le nombre d'attributs X (profession, prédominance, taille). Il s'agit de la valeur la plus couramment ajoutée aux dénominateurs de probabilités partielles dans un lissage laplacien, mais vous pouvez tester d'autres valeurs. La valeur ajoutée au dénominateur se voit souvent affecter le symbole k dans la documentation mathématique relative à la naïve bayésienne. Vous noterez également que les antécédents, P(male) et P(female), ne sont généralement pas modifiés dans le lissage laplacien de naïve bayésienne.

Structure générale du programme

Le programme de démonstration en cours d'exécution est illustré à la figure 1 dans une application console C#unique. La méthode principale, avec quelques déclarations WriteLine supprimées, est illustrée dans la figure 2.

Figure 2 Structure du programme de naïve bayésienne

using System;
namespace NaiveBayes
{
  class Program
  {
    static Random ran = new Random(25); // Arbitrary
    static void Main(string[] args)
    {
      try
      {
        string[] attributes = new string[] { "occupation", "dominance",
          "height", "sex"};
        string[][] attributeValues = new string[attributes.Length][];
        attributeValues[0] = new string[] { "administrative",
          "construction", "education", "technology" };
        attributeValues[1] = new string[] { "left", "right" };
        attributeValues[2] = new string[] { "short", "medium", "tall" };
        attributeValues[3] = new string[] { "male", "female" };
        double[][] numericAttributeBorders = new double[1][];
        numericAttributeBorders[0] = new double[] { 64.0, 71.0 };
        string[] data = MakeData(40);
        for (int i = 0; i < 4; ++i)
          Console.WriteLine(data[i]);
        string[] binnedData = BinData(data, attributeValues,
          numericAttributeBorders);
        for (int i = 0; i < 4; ++i)
          Console.WriteLine(binnedData[i]);
        int[][][] jointCounts = MakeJointCounts(binnedData, attributes,
          attributeValues);
        int[] dependentCounts = MakeDependentCounts(jointCounts, 2);
        Console.WriteLine("Total male = " + dependentCounts[0]);
        Console.WriteLine("Total female = " + dependentCounts[1]);
        ShowJointCounts(jointCounts, attributeValues);
        string occupation = "education";
        string dominance = "right";
        string height = "tall";
        bool withLaplacian = true;
        Console.WriteLine(" occupation = " + occupation);
        Console.WriteLine(" dominance = " + dominance);
        Console.WriteLine(" height = " + height);
        int c = Classify(occupation, dominance, height, jointCounts,
          dependentCounts, withLaplacian, 3);
        if (c == 0)
          Console.WriteLine("\nData case is most likely male");
        else if (c == 1)
          Console.WriteLine("\nData case is most likely female");
        Console.WriteLine("\nEnd demo\n");
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
    } // End Main
    // Methods to create data
    // Method to bin data
    // Method to compute joint counts
    // Helper method to compute partial probabilities
    // Method to classify a data case
  } // End class Program
}

Ce programme débute par la configuration des attributs codés en dur X de profession, prédominance et taille et de l'attribut dépendant de sexe. Dans certains cas de figure, vous allez préférer analyser vos données existantes pour déterminer les attributs, et spécialement lorsque la source est un fichier de données avec des en-têtes ou une table SQL avec des noms de colonnes. Le programme de démonstration précise également les neuf valeurs d'attributs X catégoriques : (administration, construction, enseignement, technologie) pour la profession ; (gauche, droite) pour la prédominance de main et (petite, moyenne, grande) pour la taille. Dans cet exemple, il y a deux valeurs d'attributs de variable dépendants : (masculin, féminin) pour le sexe. Là encore, vous voudrez peut-être déterminer par programmation les valeurs d'attributs en analysant vos données.

La démonstration définit des valeurs limites codées en dur de 64 et 71 pour classifier les valeurs de taille numériques afin que celles inférieures ou égales à 64 (1,60 m) soient dans classées comme petites ; les tailles comprises entre 64 et 71 (1,60 et 1,80 m) sont moyennes et les tailles supérieures ou égales à 71 (1,80 m) sont grandes. Lors de la classification de données numériques pour la naïve bayésienne, le nombre de valeurs limites sera inférieur de un à celui de catégories. Dans cet exemple, les valeurs 64 et 71 ont été déterminées en analysant les données d'apprentissage pour des valeurs de taille minimum et maximum (57 et 78 (1,45 et 2 m), en calculant la différence, 21 (0,55 m), puis en calculant la taille de l'intervalle en divisant par le nombre de catégories de tailles, 3, soit 7. Dans la plupart des situations, vous voudrez déterminer par programmation et non manuellement des valeurs limites pour des attributs X numériques.

Le programme de démonstration appelle une méthode d'assistance MakeData pour générer des données d'apprentissage quelque peu aléatoires. MakeData appelle des programmes d'assistance MakeSex, MakeOccupation, MakeDominance et MakeHeight. Par exemple, ces programmes d'assistance génèrent des données afin que la probabilité soit plus forte pour que : les professions masculines s'orientent vers la construction et la technologie, la prédominance masculine soit à droite et la taille masculine soit comprise entre 66 et 72 pouces (1,65 et 1,80 m).

Les méthodes clés appelées dans Main sont BinData pour classifier des données de taille ; MakeJointCounts pour analyser des données classifiées et calculer les résultats communs ; MakeDependentCounts pour calculer le nombre total d'hommes et de femmes et Classify, qui utilise des résultats communs et des résultats dépendants pour réaliser une classification naïve bayésienne.

Données de classification

La méthode BinData est présentée dans la figure 3. Cette méthode accepte toute une gamme de chaînes délimitées par des virgules où chaque chaîne est sur ce modèle « education,left,67.5,male ». Dans de nombreux cas de figure, vous lirez des données d'apprentissage à partir d'un fichier texte où chaque ligne est une chaîne. Cette méthode utilise String.Split pour convertir chaque chaîne en jetons. Token[2] correspond à la taille. Il est converti depuis une chaîne en type Double en utilisant la méthode double.Parse. La taille numérique est comparée aux valeurs limites jusqu'à ce que l'intervalle de taille soit identifié. Ensuite, la catégorie de taille correspondante en tant que chaîne est déterminée. Une chaîne de résultat est combinée à l'aide des anciens jetons, des virgules de délimitation et la nouvelle chaîne de catégorie de hauteur calculée.

Figure 3 Méthode BinData pour classification de taille

static string[] BinData(string[] data, string[][] attributeValues,
  double[][] numericAttributeBorders)
{
  string[] result = new string[data.Length];
  string[] tokens;
  double heightAsDouble;
  string heightAsBinnedString;
  for (int i = 0; i < data.Length; ++i)
  {
    tokens = data[i].Split(',');
    heightAsDouble = double.Parse(tokens[2]);
    if (heightAsDouble <= numericAttributeBorders[0][0]) // Short
      heightAsBinnedString = attributeValues[2][0];
    else if (heightAsDouble >= numericAttributeBorders[0][1]) // Tall
      heightAsBinnedString = attributeValues[2][2];
    else
      heightAsBinnedString = attributeValues[2][1]; // Medium
    string s = tokens[0] + "," + tokens[1] + "," + heightAsBinnedString +
      "," + tokens[3];
    result[i] = s;
  }
  return result;
}

Il n'est pas obligatoire de classifier des données numériques au cours d'une classification naïve bayésienne. La naïve bayésienne peut traiter des données numériques directement, mais ces techniques vont au-delà de l'objet de cet article. La classification des données présente l'avantage de la simplicité ainsi que d'éviter de devoir émettre quelque hypothèse explicite que ce soit concernant la distribution mathématique (par exemple de Gauss ou de Poisson) des données. La classification perd essentiellement des informations et exige effectivement de votre part que vous déterminiez et précisiez en combien de catégories il faut diviser les données.

Détermination des résultats communs

La clé de la classification naïve bayésienne consiste à calculer des résultats communs. Dans l'exemple de la démonstration, il y a un total de neuf valeurs d'attributs X indépendants (administration, construction, … grande) et deux valeurs d'attributs dépendants (masculin, féminin). Ainsi c'est un total de 9 * 2 = 18 résultats communs qui doit être calculé et stocké. Mon approche préférée consiste à stocker des résultats communs dans un tableau en trois dimensions dans jointCounts. Le premier index indique l'attribut X indépendant ; le deuxième indique la valeur d'attribut X indépendant et le troisième indique la valeur d'attribut dépendant. Par exemple, jointCounts[0][3][1] signifie un attribut 0 (profession), une valeur d'attribut 3 (technologie) et sexe 1 (féminin), soit, en d'autres termes, la valeur jointCounts[0][3][1] correspond au total de cas d'apprentissage occupant une profession dans le secteur de la technologie et de sexe féminin. La méthode MakeJointCounts est présentée dans la figure 4.

Figure 4 Méthode MakeJointCounts

static int[][][] MakeJointCounts(string[] binnedData, string[] attributes,
  string[][] attributeValues)
{
  int[][][] jointCounts = new int[attributes.Length - 1][][]; // -1 (no sex)
  jointCounts[0] = new int[4][]; // 4 occupations
  jointCounts[1] = new int[2][]; // 2 dominances
  jointCounts[2] = new int[3][]; // 3 heights
  jointCounts[0][0] = new int[2]; // 2 sexes for administrative
  jointCounts[0][1] = new int[2]; // construction
  jointCounts[0][2] = new int[2]; // education
  jointCounts[0][3] = new int[2]; // technology
  jointCounts[1][0] = new int[2]; // left
  jointCounts[1][1] = new int[2]; // right
  jointCounts[2][0] = new int[2]; // short
  jointCounts[2][1] = new int[2]; // medium
  jointCounts[2][2] = new int[2]; // tall
  for (int i = 0; i < binnedData.Length; ++i)
  {
    string[] tokens = binnedData[i].Split(',');
    int occupationIndex = AttributeValueToIndex(0, tokens[0]);
    int dominanceIndex = AttributeValueToIndex(1, tokens[1]);
    int heightIndex = AttributeValueToIndex(2, tokens[2]);
    int sexIndex = AttributeValueToIndex(3, tokens[3]);
    ++jointCounts[0][occupationIndex][sexIndex];
    ++jointCounts[1][dominanceIndex][sexIndex];
    ++jointCounts[2][heightIndex][sexIndex];
  }
  return jointCounts;
}

L'implémentation est soumise à de nombreuses valeurs codées en dur qui facilitent la compréhension. Par exemple, ces trois instructions pourraient être remplacées par une seule pour une boucle affectant de l'espace en utilisant les propriétés Length dans le tableau attributeValues:

jointCounts[0] = new int[4][]; // 4 occupations
jointCounts[1] = new int[2][]; // 2 dominances
jointCounts[2] = new int[3][]; // 3 heights

La fonction Helper AttributeValueToIndex accepte un index d'attribut et une chaîne de valeur d'attribut et renvoie l'index qui convient. Par exemple, AttributeValueToIndex(2, “medium”) renvoie l'index « medium » (moyenne) pour l'attribut de taille, qui est de 1.

Le programme de démonstration utilise une méthode MakeDependentCounts afin de déterminer le nombre de cas de données masculins et féminins. Il y a plusieurs façons de procéder. Si vous vous reportez à la figure 1, vous verrez qu'une approche consiste à ajouter le nombre de résultats communs de n'importe lequel des trois attributs. Par exemple, le nombre d'hommes correspond à la somme de count(administrative & male), count(construction & male), count(education & male) et count(technology & male) :

static int[] MakeDependentCounts(int[][][] jointCounts,
  int numDependents)
{
  int[] result = new int[numDependents];
  for (int k = 0; k < numDependents; ++k) 
  // Male then female
    for (int j = 0; j < jointCounts[0].Length; ++j)
    // Scanning attribute 0
      result[k] += jointCounts[0][j][k];
  return result;
}

Classification d'un cas de données

La méthode Classify, présentée à la figure 5, est courte car elle s'appuie sur des méthodes d'assistance.

Figure 5 Méthode Classify

static int Classify(string occupation, string dominance, string height,
  int[][][] jointCounts, int[] dependentCounts, bool withSmoothing,
  int xClasses)
{
  double partProbMale = PartialProbability("male", occupation, dominance,
    height, jointCounts, dependentCounts, withSmoothing, xClasses);
  double partProbFemale = PartialProbability("female", occupation, dominance,
    height, jointCounts, dependentCounts, withSmoothing, xClasses);
  double evidence = partProbMale + partProbFemale;
  double probMale = partProbMale / evidence;
  double probFemale = partProbFemale / evidence;
  if (probMale > probFemale) return 0;
  else return 1;
}

La méthode Classify accepte les tableaux jointCounts et dependentCounts ; un champ booléen pour indiquer s'il convient ou non d'utiliser un lissage laplacien et le paramètre xClasses, qui dans cet exemple sera de 3 parce qu'il y a trois variables indépendantes (profession, prédominance, taille). Ce paramètre pourrait également être déduit du paramètre jointCounts.

La méthode Classify renvoie un int qui représente l'index de la variable dépendante prévue. Vous pouvez à la place souhaiter renvoyer un tableau de probabilités pour chaque variable dépendante. Veuillez noter que la classification se base sur les valeurs probMale et probFemale qui sont toutes deux le résultat de la division de probabilités partielles par la valeur de preuve. Vous voudrez peut-être simplement ignorer le terme de preuve pour uniquement comparer les valeurs des probabilités partielles en elles-mêmes.

La méthode Classify renvoie l'index de la variable dépendante dont la probabilité est la plus forte. Une alternative consiste à fournir une valeur seuil. Supposons par exemple que probMale est de 0,5001 et probFemale de 0,4999. Vous pouvez choisir de considérer ces valeurs comme trop proches pour les appeler et renvoyer une valeur de classification représentant « indéterminé ».

La méthode PartialProbability effectue la majeure partie du travail pour Classify et est répertoriée à la figure 6.

Figure 6 Méthode PartialProbability

static double PartialProbability(string sex, string occupation, string dominance,
  string height, int[][][] jointCounts, int[] dependentCounts,
  bool withSmoothing, int xClasses)
{
  int sexIndex = AttributeValueToIndex(3, sex);
  int occupationIndex = AttributeValueToIndex(0, occupation);
  int dominanceIndex = AttributeValueToIndex(1, dominance);
  int heightIndex = AttributeValueToIndex(2, height);
  int totalMale = dependentCounts[0];
  int totalFemale = dependentCounts[1];
  int totalCases = totalMale + totalFemale;
  int totalToUse = 0;
  if (sex == "male") totalToUse = totalMale;
  else if (sex == "female") totalToUse = totalFemale;
  double p0 = (totalToUse * 1.0) / (totalCases); // Prob male or female
  double p1 = 0.0;
  double p2 = 0.0;
  double p3 = 0.0;
  if (withSmoothing == false)
  {
    p1 = (jointCounts[0][occupationIndex][sexIndex] * 1.0) / totalToUse
    p2 = (jointCounts[1][dominanceIndex][sexIndex] * 1.0) / totalToUse;  
    p3 = (jointCounts[2][heightIndex][sexIndex] * 1.0) / totalToUse;     
  }
  else if (withSmoothing == true)
  {
    p1 = (jointCounts[0][occupationIndex][sexIndex] + 1) /
     ((totalToUse + xClasses) * 1.0); 
    p2 = (jointCounts[1][dominanceIndex][sexIndex] + 1) /
     ((totalToUse + xClasses) * 1.0 ;
    p3 = (jointCounts[2][heightIndex][sexIndex] + 1) /
     ((totalToUse + xClasses) * 1.0);
  }
  //return p0 * p1 * p2 * p3; // Risky if any very small values
  return Math.Exp(Math.Log(p0) + Math.Log(p1) + Math.Log(p2) + Math.Log(p3));
}

Pour plus de clarté, la méthode PartialProbability est principalement codée en dur. Par exemple, il y a quatre éléments de probabilité : p0, p1, p2 et p3. Vous pouvez rendre plus générale la valeur PartialProbability en utilisant un tableau de probabilités dans lequel la taille du tableau est déterminée à partir du tableau jointCounts.

Veuillez noter qu'au lieu de renvoyer le produit des quatre éléments de probabilité, la méthode renvoie la valeur Exp équivalente de la somme du Log de chaque élément. L'utilisation de probabilités Log est une technique standard des algorithmes d'apprentissage automatique qui est utilisée pour empêcher les erreurs numériques pouvant se produire avec de très petites valeurs numériques réelles.

Pour résumer

L'exemple présenté ici devrait vous fournir de solides bases pour l'ajout de fonctionnalités de classification naïve bayésienne à vos applications .NET. La classification naïve bayésienne est une technique assez rudimentaire. Néanmoins, elle présente plusieurs avantages par rapport à des alternatives plus sophistiquées telles qu'une classification réseau neuronale, une classification de régression logistique et la prise en charge d'une classification de machine vectrice. La naïve bayésienne est simple, relativement facile à mettre en place et s'adapte bien à de très grands jeux de données. De plus, la naïve bayésienne s'étend facilement à des problèmes de classification multinomiaux, ceux comportant au moins trois variables dépendantes.

Le Dr. James McCaffrey travaille pour Volt Information Sciences Inc., où il gère la formation technique d'ingénieurs logiciels travaillant sur le Campus Microsoft à Redmond (État de Washington). Il a collaboré à plusieurs produits Microsoft, comme Internet Explorer et MSN Search. Il est l'auteur de « NET Test Automation Recipes » (Apress, 2006) et vous pouvez le contacter à l'adresse jammc@microsoft.com.

Merci à l'expert technique Microsoft suivant d'avoir relu cet article : Rich Caruana