Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Esecuzione di test

Sviluppo di codice con regressione logistica con il metodo Newton-Raphson

James McCaffrey

Scarica il codice di esempio

James McCaffreyRegressione logistica (LR) è una tecnica di apprendimento automatico che può essere utilizzata per fare previsioni su dati dove la variabile dipendente essere predetto assume il valore 0 o 1. Gli esempi includono la previsione o meno un paziente morirà a causa di malattia di cuore all'interno di un certo numero di anni (0 = non die, 1 = die), in base al paziente età, sesso e livello di colesterolo e predire se o non una squadra di baseball vincerà una partita (0 = perdere, 1 = win) basata su fattori quali la squadra battuta media e partenza punti lanciatore. Regressione logistica presuppone che dati del problema si inserisce un'equazione che ha la forma p = 1.0 / (1.0 + e-z) dove z = b0 + (b1)(x1) + (b2)(x2) +. . . + (bn)(xn). Le variabili x sono i predittori e i valori di b sono costanti che devono essere determinati. Ad esempio, si supponga di che voler predire morte dalla malattia di cuore. Le variabili predittive sia x 1 = paziente età, x 2 = paziente sesso (0 = maschio, 1 = femmina) e x 3 = livello di colesterolo paziente. E supponiamo di avere in qualche modo determinato che b0 =-95.0, b1 = 0,4, b2 = -0,9 e b3 = 11.2. Se c'è un 50-anno-vecchio paziente maschio cui livello di colesterolo è 6,8, allora z =-95.0 + (0.4)(50) + (-0.9)(0) + (11.2)(6.8) = 1,16 e quindi p = 1.0 / (1.0 + exp(-1.16)) = 0.7613. Il valore di p può essere liberamente interpretata come una probabilità, quindi in questo caso aveva a concludere che il paziente ha un 0.7613 probabilità di morire entro il numero di anni specificato.

La funzione 1.0 / (1.0 + e-z) è chiamata funzione sigmoidale. Il dominio di possibili valori per z è tutti i numeri reali. Il risultato della funzione è un valore compreso tra 0.0 e 1.0, come mostrato Figura 1. Si può presupporre che i dati che si sta lavorando con possono essere modellati dalla funzione sigmoidale, ma molti insiemi di dati reali possono infatti essere accuratamente modellate dalla funzione.

The Sigmoid Function
Figura 1 la funzione sigmoidale

Quando si utilizza la regressione logistica, il problema principale è come determinare i valori di b (spesso chiamato beta) per l'equazione di LR. Nella maggior parte delle situazioni, si hanno alcuni dati storici con risultati noti e utilizzare una delle diverse tecniche per trovare i valori di beta che si adattano meglio i dati. Dopo aver determinato i valori di beta, è possibile utilizzare per fare previsioni su nuovi dati. Una delle tecniche più comuni per trovare i valori di beta per un'equazione di regressione logistica è chiamata in modo iterativo reweighted minimi quadrati (IRLS). IRLS inizia con una stima dei valori di beta e quindi calcola iterativamente una nuova, meglio impostare delle versioni beta fino a quando non vengono soddisfatte alcune condizioni di arresto. Ci sono diverse tecniche che possono essere utilizzati per determinare un nuovo, meglio impostare dei valori di beta. Uno dei più comuni è chiamato Newton-Raphson (NR), che coinvolge il derivato di calcolo di una funzione di ricerca — in questo caso la derivata della funzione sigmoidale. A causa della stretta connessione tra IRLS e Newton-Raphson in regressione logistica, i due termini sono spesso usati come sinonimi.

Anche se ci sono un sacco di risorse disponibili che descrivono la matematica complessa dietro trovando regressione logistica parametri beta utilizzando Newton-Raphson, esistono molto pochi, se del caso, guide di attuazione per i programmatori. Questo articolo spiega esattamente come logistic regression con opere di Newton-Raphson e come implementare una soluzione utilizzando il linguaggio c#. Date un'occhiata a screenshot in Figura 2 per vedere dove sono diretto.

Logistic Regression with Newton-Raphson
Figura 2 regressione logistica con Newton-Raphson

Il programma demo inizia generando due file di dati sintetici. Il primo è chiamato il file di allenamento e si compone di 80 righe di dati di età, sesso, colesterolo e morte. Il file di allenamento viene utilizzato per calcolare i valori di beta LR. Il secondo è chiamato il file di test e detiene 20 righe di dati che viene utilizzati per valutare l'accuratezza dell'equazione LR con i valori di beta calcolati da dati di training. Il programma demo carica i valori di x predittore di dati di training in una matrice e carica i valori della variabile dipendente y dei dati in un vettore. Si noti che la matrice di formazione X, spesso chiamata matrice di design, ha una colonna aggiuntiva prima che consiste di tutti i valori 1.0, e che i valori predittive sono stati convertiti in valori numerici. Successivamente, il programma demo imposta tre condizioni di arresto per l'algoritmo IRLS, indicata da jumpFactor, epsilon e variabili maxIterations. Il programma demo utilizza l'algoritmo di Newton-Raphson per stimare la b0, b1, b2 e b3 valori beta che meglio si adattano i dati di training. La demo si conclude valutando come precisa è l'equazione risultante LR con i valori di beta calcolate su dati di test. In questo esempio, i valori Y 18 di 20 (90 per cento) erano correttamente predetto.

Questo articolo presuppone che si hanno competenze di programmazione e almeno una conoscenza intermedia della terminologia e operazioni matriciali avanzati, ma non si assume che si sa nulla di regressione logistica. Il codice che ha prodotto lo screenshot in Figura 2 è troppo grande per presentare nella sua interezza qui, ma il codice sorgente completo è disponibile presso archive.msdn.microsoft.com/mag201208TestRun. A causa della complessità dell'algoritmo IRLS/NR, mi concentrerò principalmente su parti chiave dell'algoritmo, piuttosto che per il codice stesso, così sarete in grado di modificare il codice per soddisfare le proprie esigenze o rifattorizzare ad un altro linguaggio di programmazione se lo si desidera.

Struttura generale del programma

Per semplicità, tutto il codice che ha generato lo screenshot in Figura 2 è contenuta in una singola applicazione console c#. La struttura del programma e il metodo principale, con alcune istruzioni WriteLine rimosso, sono elencati nella Figura 3.

Struttura del programma Figura 3

using System;
using System.IO;
namespace LogisticRegressionNewtonRaphson
{
  class LogisticRegressionNRProgram
  {
    static void Main(string[] args)
    {
      try
      {
        string trainFile = "trainFile.txt";
        string testFile = "testFile.txt";
        MakeRawDataFile(80, 3, trainFile);
        MakeRawDataFile(20, 4, testFile);
        Console.WriteLine("First 5 lines of training data file are: \n");
        DisplayRawData(trainFile, 5);
        double[][] xTrainMatrix = LoadRawDataIntoDesignMatrix(trainFile);
        double[] yTrainVector = LoadRawDataIntoYVector(trainFile);
        double[][] xTestMatrix = LoadRawDataIntoDesignMatrix(testFile);
        double[] yTestVector = LoadRawDataIntoYVector(testFile);
        int maxIterations = 25;
        double epsilon = 0.01;
        double jumpFactor = 1000.0;
        double[] beta = ComputeBestBeta(xTrainMatrix, yTrainVector,
          maxIterations, epsilon, jumpFactor);
        Console.WriteLine("Newton-Raphson complete");
        Console.WriteLine("The beta vector is: ");
        Console.WriteLine(VectorAsString(beta, int.MaxValue, 4, 10));
        double acc = PredictiveAccuracy(xTestMatrix, yTestVector, beta);
        Console.WriteLine("The predictive accuracy of the model on the test
          data is " + acc.ToString("F2") + "%\n");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Fatal: " + ex.Message);
        Console.ReadLine();
      }
    } // Main
    static void MakeRawDataFile(int numLines, int seed, string fileName)
    static void DisplayRawData(string fileName, int numLines)
    static double[][] LoadRawDataIntoDesignMatrix(string rawDataFile)
    static double[] LoadRawDataIntoYVector(string rawDataFile)
    static double PredictiveAccuracy(double[][] xMatrix,
      double[] yVector, double[] bVector)
    static double[] ComputeBestBeta(double[][] xMatrix, double[] yVector,
      int maxNewtonIterations, double epsilon, double jumpFactor)
    static double[] ConstructNewBetaVector(double[] oldBetaVector,
      double[][] xMatrix,
      double[] yVector, double[] oldProbVector)
    static double[][] ComputeXtilde(double[] pVector, double[][] xMatrix)
    static bool NoChange(double[] oldBvector, double[] newBvector, double epsilon)
    static bool OutOfControl(double[] oldBvector, double[] newBvector,
      double jumpFactor)
    static double[] ConstructProbVector(double[][] xMatrix, double[] bVector)
    // Matrix and vector routines:
    static double MeanSquaredError(double[] pVector, double[] yVector)
    static double[][] MatrixCreate(int rows, int cols)
    static double[] VectorCreate(int rows)
    static string MatrixAsString(double[][] matrix, int numRows,
      int digits, int width)
    static double[][] MatrixDuplicate(double[][] matrix)
    static double[] VectorAddition(double[] vectorA, double[] vectorB)
    static double[] VectorSubtraction(double[] vectorA, double[] vectorB)
    static string VectorAsString(double[] vector, int count, int digits, int width)
    static double[] VectorDuplicate(double[] vector)
    static double[][] MatrixTranspose(double[][] matrix) // assumes
      matrix is not null
    static double[][] MatrixProduct(double[][] matrixA, double[][] matrixB)
    static double[] MatrixVectorProduct(double[][] matrix, double[] vector)
    static double[][] MatrixInverse(double[][] matrix)
    static double[] HelperSolve(double[][] luMatrix, double[] b) // helper
    static double[][] MatrixDecompose(double[][] matrix, out int[] perm,
      out int tog)
  } // Program
} // ns

Anche se la regressione logistica è un argomento complesso, il codice in Figura 3 non è così complicato come potrebbe sembrare in primo luogo perché la maggior parte dei metodi illustrati sono routine di supporto relativamente breve. I due metodi principali sono ComputeBestBeta e ConstructNewBetaVector.

Il metodo MakeRawData genera un file di dati di età-sesso-colesterolo-morte quasi-random. L'età è un valore integer casuali tra 35 e 80, sesso è m o f con uguale probabilità e colesterolo è un semi-casuale valore reale tra 0,1 e 9,9 che è basato sul valore attuale età. La variabile dipendente di morte viene calcolata utilizzando un'equazione di regressione logistica con valori di beta fisso di b0 =-95.0, b1 = 0,4, b2 = -0,9 e b3 = 11.2. Così MakeRawData genera dati che sicuramente possono essere modellati utilizzando la LR, in contrasto con la generazione di dati puramente casuali che probabilmente non seguono un modello di LR.

Elaborazione di un nuovo vettore di Beta

Il cuore di regressione logistica con Newton-Raphson è una routine che calcola un nuovo, presumibilmente meglio, impostare dei valori di beta dall'attuale insieme di valori. La matematica è molto profonda, ma fortunatamente il risultato non è troppo complesso. In forma di pseudo-equation, il processo di aggiornamento è dato da:

b [t] = b [t-1] + inv (X'W [t-1] X) X'(Y-p[t-1])

Ecco la nuova b [t] ("al tempo t," non array indexing) vettore beta. Sul lato destro, b [t-1] è il vettore di beta vecchio ("al tempo t-1"). La funzione inv è inversione di matrice. Maiuscola x è la matrice di design — che è, i valori delle variabili predittive aumentata con una colonna iniziale dell'italiano. Maiuscola X' è la trasposta della matrice X. Maiuscola y è il vettore dei valori di variabile dipendente (ricordiamo che ogni valore sarà 0 o 1). La rappresenta quantità p [t-1] il vettore previsto dei vecchi valori di probabilità per Y (che sarà composto da valori compresi tra 0.0 e 1.0). La quantità di w maiuscola è una matrice di pesi cosiddetto, che richiede un po' di spiegazione.

L'equazione di aggiornamento beta e la matrice w sono meglio spiegati con un esempio concreto. Supponiamo per semplicità che il set di formazione consiste solo delle prime cinque linee di dati mostrati nella Figura 1. Quindi la matrice di design x sarebbe:

1.00    48.00     1.00     4.40
1.00    60.00     0.00     7.89
1.00    51.00     0.00     3.48
1.00    66.00     0.00     8.41
1.00    40.00     1.00     3.05

Il vettore di variabile dipendente y sarebbe:

0
1
0
1
0

Supponiamo che il vecchio beta vettore di valori da aggiornare, b [t-1], è:

1.00
0.01
0.01
0.01

Con questi valori per x e beta, il vecchio vettore p, p [t-1], è:

0.8226
0.8428
0.8242
0.8512
0.8085

Si noti che se si presumono valori p < 0,5 vengono interpretati come y = 0 e p i valori > = 0,5 vengono interpretati come y = 1, i vecchi valori di beta sarebbero prevedere correttamente solo due delle cinque casi nei dati di training.

La matrice di pesi w è un m x matrice m dove m è il numero di righe di X. Tutti i valori nella matrice w sono 0.0 ad eccezione di quei valori di m che sono sulla diagonale principale. Ciascuno di questi valori è uguale al valore di p corrispondente moltiplicato per 1-p. Così per esempio, W sarebbe dimensioni 5 x 5. La cella superiore sinistra a [0,0] sarebbe (0.8226)(1-0.8226) = 0.1459. La cella a [1,1] sarebbe (0.8428)(1-0.8428) = 0.1325 e così via. La quantità (p)(1-p) rappresenta la derivata di calcolo della funzione sigmoidale.

In pratica, la matrice w non è calcolata in modo esplicito perché la sua dimensione potrebbe essere enorme. Se hai avuto 1.000 righe di dati di training, matrice w avrebbe 1.000.000 cellule. Si noti che l'equazione aggiornamento beta ha un termine W X [t-1], che significa che il prodotto di [t-1] W e X. Perché la maggior parte dei valori in W [t-1] sono zero, la maggior parte dei termini di moltiplicazione di matrice sono anche zero. Questo permette tempi di [t-1] W X essere computato direttamente da p [t-1] e X, senza costruire esplicitamente W. Molti dei riferimenti matematiche che descrivono il IRLS con l'algoritmo NR per LR utilizzare il simbolo X ~ (X tilde) per il prodotto di [t-1] W e X. ComputeXtilde nel download del codice per vedere i dettagli di implementazione.

ConstructNewBetaVector metodo accetta come parametri di input il vecchio vettore beta, matrice X, il vettore di variabile dipendente y e il vecchio vettore di probabilità. Il metodo calcola e restituisce il vettore beta aggiornato.

Il metodo viene implementato in questo modo:

double[][] Xt = MatrixTranspose(xMatrix);                // X'
double[][] A = ComputeXtilde(oldProbVector, xMatrix);    // WX
double[][] B = MatrixProduct(Xt, A);                     // X'WX
double[][] C = MatrixInverse(B);                         // inv(X'WX)
if (C == null)                                           // inverse failed!
return null;
double[][] D = MatrixProduct(C, Xt);                     // inv(X'WX)X'
double[] YP = VectorSubtraction(yVector, oldProbVector); // Y-p
double[] E = MatrixVectorProduct(D, YP);                 // inv(X'WX)X'(y-p)
double[] result = VectorAddition(oldBetaVector, E);
return result;

Con l'insieme di metodi di supporto matrice e vettore indicato Figura 3, un nuovo vettore di beta il calcolo è abbastanza semplice. Si noti che il metodo inversione di matrice. Questo è un processo che è una debolezza significativa di Newton-Raphson e può andare male in molti modi.

Continuando l'esempio, matrix Xt (la trasposta di X) sarebbe:

 1.00     1.00     1.00     1.00     1.00
48.00    60.00    51.00    66.00    40.00
 1.00     0.00     0.00     0.00     1.00
 4.40     7.89     3.48     8.41     3.05

Matrice A (X ~) dovrebbe essere calcolata dal vettore p e matrix x dal metodo helper ComputeXtilde come:

0.15     7.00     0.15     0.64
0.13     7.95     0.00     1.05
0.14     7.39     0.00     0.50
0.13     8.36     0.00     1.07
0.15     6.19     0.15     0.47

Matrice intermedia B, che rappresenta il prodotto di Xt e X ~ (che a sua volta è XtW[t-1]X) sarebbe:

 0.70    36.90     0.30     3.73
36.90  1989.62    13.20   208.46
 0.30    13.20     0.30     1.11
 3.73   208.46     1.11    23.23

Matrice intermedia c è l'inverso della matrice b e sarebbe:

 602.81   -14.43  -110.41    38.05
 -14.43     0.36     2.48    -1.02
-110.41     2.48    26.43    -5.77
  38.05    -1.02    -5.77     3.36

Matrice intermedia d è il prodotto della matrice c e matrice trasposta di x e dovrebbe essere calcolata come:

-33.00    37.01    -0.90   -29.80    31.10
  0.77    -0.96     0.30     0.66    -0.72
  9.52    -7.32    -4.17     4.54    -2.51
 -1.86     3.39    -2.24    -0.98     1.76

Vettore intermedio YP è la differenza tra vettori y e p [t-1] e sarebbe:

-0.8
 0.2
-0.8
 0.1
-0.8

Vettore intermedio e è il prodotto della matrice d e vector YP e tiene gli incrementi da aggiungere al vecchio vettoriale di beta. Vettore e sarebbe:

 4.1
-0.4
-2.8
 2.3

Il vettore finale, nuova beta viene ottenuto aggiungendo che i valori nel vettore intermedio e per i vecchi valori di beta e in questo esempio sarebbe:

 5.1
-0.3
-2.8
 2.4

Con i nuovi valori di beta, i nuovi valori per il vettore p sarebbe:

0.0240
0.9627
0.0168
0.9192
0.0154

Se questi valori di p sono interpretati come Y = 0 quando p < 0,5, poi, dopo solo una iterazione di Newton-Raphson, i valori di beta predicono correttamente tutti i cinque casi nei dati di test.

Sapere quando smettere

La tecnica di Newton-Raphson per regressione logistica iterativamente migliora i valori del vettore beta fino a quando non vengono soddisfatte alcune condizioni di arresto. È sorprendentemente difficile sapere quando smettere l'iterazione. Metodo ComputeBestBeta gestisce questa attività. Il codice è presentato in Figura 4.

Figura 4 il miglior vettore Beta di calcolo

static double[] ComputeBestBeta(double[][] xMatrix, double[] yVector,
  int maxIterations, double epsilon, double jumpFactor)
{
  int xRows = xMatrix.Length;
  int xCols = xMatrix[0].Length;
  if (xRows != yVector.Length)
    throw new Exception("xxx (error)");
  // Initialize beta values
  double[] bVector = new double[xCols];
  for (int i = 0; i < xCols; ++i) { bVector[i] = 0.0; }
  double[] bestBvector = VectorDuplicate(bVector);
  double[] pVector = ConstructProbVector(xMatrix, bVector);
  double mse = MeanSquaredError(pVector, yVector);
  int timesWorse = 0;
  for (int i = 0; i < maxIterations; ++i)
  {
    double[] newBvector = ConstructNewBetaVector(bVector, xMatrix,
      yVector, pVector);
    if (newBvector == null)
      return bestBvector;
    if (NoChange(bVector, newBvector, epsilon) == true)
      return bestBvector;
    if (OutOfControl(bVector, newBvector, jumpFactor) == true)
      return bestBvector;
    pVector = ConstructProbVector(xMatrix, newBvector);
    double newMSE = MeanSquaredError(pVector, yVector); // Smaller is better
    if (newMSE > mse) // new MSE is worse
    {
      ++timesWorse;           // Update got-worse counter
      if (timesWorse >= 4)
        return bestBvector;
      bVector = VectorDuplicate(newBvector); // Attempt escape
      for (int k = 0; k < bVector.Length; ++k)
        bVector[k] = (bVector[k] + newBvector[k]) / 2.0;
      mse = newMSE;                           
    }
    else // Improved
    {
      bVector = VectorDuplicate(newBvector);
      bestBvector = VectorDuplicate(bVector);
      mse = newMSE;
      timesWorse = 0;
    }
  } // End main iteration loop
  return bestBvector;
}

Una delle pecche più grandi di regressione logistica è l'iterazione troppe volte, risultando in un set di valori di beta che si adattano perfettamente i dati di training, ma non si adattano bene altri set di dati. Questo è chiamato over-fitting. Sapere quando fermare il processo di formazione in regressione logistica è parte arte e scienza di parte. Metodo ComputeBestBeta comincia da inizializzare il vettore beta per tutti i valori 0,0, calcolo i valori associati p e poi calcolo l'errore tra i valori y e i valori di p. Il codice in questo articolo utilizza errore quadratico medio — la media delle somme delle differenze al quadrato tra p e Y. Altre possibilità per errore di calcolo includono cross-entropy errore e deviazione assoluta Media.

Il ciclo di elaborazione principale in ComputeBestBeta ripetutamente calcola un nuovo vettore di beta e termine di errore corrispondente. La condizione di arresto primario nell'algoritmo è un parametro maxIterations. Ricordiamo che l'algoritmo di Newton-Raphson calcola un inverso della matrice, che è suscettibile di venire a mancare. In questo caso ComputeBestBeta restituisce il vettore beta migliore trovato (che, purtroppo, potrebbero essere il vettore di tutti zero iniziale). È possibile generare un'eccezione qui invece. Un'altra alternativa è di tentare una fuga modificando i valori di beta nuovo regolando li alla media dei valori precedenti e i nuovi valori.

La condizione di stop successiva controlla per vedere se il cambiamento in tutti i nuovi valori di beta è più piccolo di qualche piccolo valore Epsilon del parametro, utilizzando il metodo di supporto NoChange. Questo indica che ha eseguito la convergenza di Newton-Raphson e, infatti, c'è una buona probabilità che il tuo modello è ora over-fitted. Anziché restituire il miglior vettore beta trovato a questo punto, si potrebbe voler restituire il miglior vettore beta da un'iterazione precedente. La condizione di stop successiva controlla per vedere se uno qualsiasi dei nuovi valori di beta hanno saltato da una quantità maggiore di qualche grande valore dato dal parametro jumpFactor. Questo indica che Newton-Raphson possibilmente ha filata fuori controllo e si desidera generare un'eccezione invece di risintonizzare il miglior vettore beta trovato.

Un'altra condizione di stop in ComputeBestBeta controlla per vedere se i nuovi valori di beta producono effettivamente un errore più grande rispetto ai precedenti valori di beta ha fatto. In questo esempio, se la nuova beta produce un errore più grande quattro volte di fila, l'algoritmo termina e restituisce il miglior vettore di beta. Si potrebbe voler parametrizzare il valore per il numero massimo di aumenti consecutivi in errore. Quando viene rilevato un aumento in errore, il metodo tenta di sfuggire la situazione modificando i valori nel vettore corrente beta per la media dei valori nella beta attuale e i valori in beta appena elaborato.

Non non c'è nessun singolo set migliore di fermare le condizioni. Ogni problema di regressione logistica richiede un po ' di sperimentazione per ottimizzare l'algoritmo di Newton-Raphson.

Vantaggi vs. Svantaggi

Ci sono diverse alternative di Newton-Raphson per stimare il migliore set di valori di beta di regressione logistica. Newton-Raphson è una tecnica di ottimizzazione numerica deterministica. Si possono anche impiegare non deterministici (significato probabilistico) tecniche particle swarm optimization, gli algoritmi evolutivi e ottimizzazione di foraggiamento batterica.

Il vantaggio primario di Newton-Raphson rispetto alle alternative probabilistiche è che, nella maggior parte delle situazioni, NR è molto più veloce. Ma Newton-Raphson ha diversi svantaggi. Perché NR utilizza inversione di matrice, l'algoritmo avrà esito negativo quando incontrano una singolare matrice durante il calcolo. Semplici implementazioni di Newton-Raphson richiedono tutti i dati in memoria, il che significa che c'è un limite alla dimensione della formazione ed è possibile utilizzare i set di dati di test. Anche se è raro, alcuni set di dati potrebbe non convergono in un set di valori di beta migliori affatto usando NR.

Quando si utilizza la regressione logistica, spesso impiegano un attacco su due fronti. Io prima esperienza con un approccio di Newton-Raphson. Se tale tecnica non riesce, I fall back utilizzando particle swarm optimization per trovare il miglior set di valori di beta. È importante notare che la regressione logistica non è magia, e non tutti i dati si adatta un modello di regressione logistica. Altre tecniche di apprendimento automatico di dati del modello con una variabile dipendente binaria includono le reti neurali, support vector machines e analisi discriminante lineare.

La spiegazione del processo beta vector aggiornamento dell'algoritmo di Newton-Raphson presentato in questo articolo e il download del codice si dovrebbe ottenere installato e funzionante con regressione logistica tramite NR. Regressione logistica è un argomento affascinante, complesso e può rendere una preziosa aggiunta alla vostra insieme di abilità personali.

Dr.James McCaffrey funziona per Volt Information Sciences Inc., dove gestisce la formazione tecnica per gli ingegneri software della Microsoft di Redmond, Washington, campus. Si è occupato di diversi prodotti Microsoft, inclusi Internet Explorer e MSN Search. McCaffrey è l'autore di ".NET Test Automation Recipes" (Apress, 2006). Può essere raggiunto a jammc@microsoft.com.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Dan Liebling e Anne Loomis Thompson