Execução de teste

Classificação e previsão usando redes neurais

James McCaffrey

Baixar o código de exemplo

James McCaffrey

Na coluna deste mês, explico como usar redes neurais para solucionar problemas de classificação e previsão. O objetivo da classificação é mais bem explicado por meio de exemplos. Suponha que você tenha alguns dados históricos sobre as flores íris semelhantes a:

    5.1 3.5 1.4 0.2 Setosa
    7.0 3.2 4.7 1.4 Versicolor
    6.3 3.3 6.0 2.5 Virginica
    6.4 3.2 4.5 1.5 Versicolor
    ...

Cada linha de dados delimitados por espaço tem cinco campos. Os quatro primeiros campos numéricos são comprimento da sépala (a parte verde que cobre o botão), largura da sépala, comprimento da pétala (a parte colorida da flor) e largura da pétala. O quinto campo é a espécie: Setosa, versicolor ou virginica. O objetivo da classificação é determinar uma equação ou conjunto de regras que preveja a que espécie ou classe uma íris pertence. Então, o conjunto de regras pode ser usado para prever a classe de uma nova íris com base em seus valores de comprimento e largura de sépala e pétala. Esses dados da planta íris são um exemplo clássico usado pela primeira vez por R. A. Fisher em 1936. É possível que você não fique empolgado, mas a classificação é extremamente importante. Os exemplos incluem classificar a avaliação de crédito de um candidato com base em variáveis como renda e despesas mensais (ou de maneira equivalente, prevendo sua capacidade financeira para obter crédito), e classificar se o paciente de um hospital tem câncer baseado nos valores de um exame de sangue.

Há muitas abordagens para classificar dados, incluindo o uso de redes neurais. Uma maneira de pensar a respeito de redes neurais é que elas são dispositivos de entrada-saída virtuais que aceitam qualquer número de entradas numéricas e produzem qualquer número de saídas numéricas. A melhor forma de você perceber onde quero chegar é examinando a captura de tela da Figura 1 e a imagem da Figure 2. A Figura 1 mostra a classificação de rede neural em ação. Para manter limpos os conceitos de classificação usando redes neurais, não usei dados da vida real. Em vez disso, usei dados artificiais em que os valores x de entrada são quatro valores numéricos arbitrários sem nenhum significado. A variável y de saída da classificação é cor, e pode assumir um de três valores categóricos: vermelho, verde ou azul. O programa mostrado na Figura 1 começa gerando um arquivo de texto com 100 linhas de dados artificiais (por exemplo, “8.0 5.0 9.0 5.0 green”) e, então, exibe as quatro primeiras linhas desses dados. Em seguida, o programa lê o arquivo de dados brutos na memória como uma matriz de treinamento com 80 linhas de dados e uma matriz de teste com 20 linhas. Observe que duas transformações são aplicadas aos dados brutos. Os dados de entrada numéricos brutos são normalizados de modo que todos os valores estejam entre -1,00 e +1,00, e os dados de saída brutos (como “red”) são codificados em um vetor com três valores (“1,0 0,0 0,0”).


Figura 1 Classificação de rede neural em ação

Neural Network Structure
Figura 2 Estrutura da rede neural

Depois de criar as matrizes de treinamento e teste, o programa de demonstração cria uma rede neural sem realimentação totalmente conectada com três neurônios de entrada, cinco neurônios ocultos para computação e três neurônios de saída. Resulta que uma rede neural totalmente conectada 4-5-3 requer 43 pesos e tendências. Em seguida, o programa de classificação analisa os dados de treinamento para descobrir os melhores 43 pesos e tendências (os que minimizam o erro de classificação total). O programa usa otimização por nuvem de partículas junto com erro de entropia cruzada para estimar os melhores valores dos pesos e das tendências.

O programa de classificação então carrega os melhores pesos e tendências na rede neural e avalia a precisão preditiva do modelo nas 20 linhas de dados da matriz de teste. Observe que a saída da rede neural foi projetada para que a soma dos três valores de saída seja 1,0. Nesse exemplo, o modelo prevê corretamente 17 dos 20 vetores de teste A imagem da Figura 2 ilustra a rede neural aceitando entrada de (-1.00, 1.00, 0.25, -0.50) e gerando uma saída prevista de (0.9, 0.1, 0.0), que corresponde a vermelho.

O programa de exemplo indica que há cinco decisões principais a serem tomadas ao usar redes neurais para classificação em que os dados de entrada são numéricos e os dados de saída são categóricos:

  1. Como normalizar dados de entrada numéricos
  2. Como codificar dados de saída categóricos
  3. Como gerar saída neural no intervalo [0,0, 1,0]
  4. Como medir erro durante o treinamento
  5. Como medir precisão durante o teste

Nas seções a seguir explicarei que as escolhas feitas no programa de exemplo são:

  1. Executar uma transformação linear em dados de entrada numéricos
  2. Usar codificação 1-de-N para dados de saída categóricos
  3. Usar uma função de ativação Softmax para a camada de saída
  4. Usar erro de entropia cruzada para determinar os melhores pesos
  5. Usar uma abordagem o vencedor leva tudo para determinar a precisão

O código do programa que gerou a captura de tela da Figura 1 é um pouco longo demais para apresentar neste artigo, irei me concentrar então nos algoritmos usados. O código-fonte completo do programa está disponível no site de download de código do MSDN em archive.msdn.microsoft.com/mag201207TestRun. Este artigo pressupõe que você tenha habilidades de programação avançada e um entendimento básico de redes neurais. Eu explico os fundamentos das redes neurais na edição de maio de 2012 da MSDN Magazine (msdn.microsoft.com/magazine/hh975375).

Estrutura geral do programa

A Figura 3 lista a estrutura do programa do exemplo mostrado em execução na Figura 1. Usei o Visual Studio 2010 para criar um único aplicativo de console em C# chamado NeuralClassification. Na janela do Solution Explorer, renomeei o arquivo Program.cs para o mais descritivo NeuralClassificationProgram.cs, que automaticamente renomeou a classe que contém Main. Removi instruções using desnecessárias que foram geradas pelo modelo do Visual Studio e adicionei um referência ao namespace System.IO.

Figura 3 Estrutura do programa de classificação neural

using System;
using System.IO;
namespace NeuralClassification
{
  class NeuralClassificationProgram
  {
    static Random rnd = null;
    static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("\nBegin Neural network classification\n");
        rnd = new Random(159); // 159 makes a nice example
        string dataFile = "..\\..\\colors.txt";
        MakeData(dataFile, 100);
        double[][] trainMatrix = null;
        double[][] testMatrix = null;
        MakeTrainAndTest(dataFile, out trainMatrix, out testMatrix);
        NeuralNetwork nn = new NeuralNetwork(4, 5, 3);
        double[] bestWeights = nn.Train(trainMatrix);
        nn.SetWeights(bestWeights);
        double accuracy = nn.Test(testMatrix);
        Console.WriteLine("\nEnd neural network classification\n");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Fatal: " + ex.Message);
      }
    } // Main()
    static void MakeData(string dataFile, int numLines) { ... }
    static void MakeTrainAndTest(string file, out double[][] trainMatrix,
      out double[][] testMatrix) { ... }
  }
  class NeuralNetwork
  {
    // Class member fields here
    public NeuralNetwork(int numInput, int numHidden,
      int numOutput) { ... }
    public void SetWeights(double[] weights) { ... }
    public double[] ComputeOutputs(double[] currInputs) { ... }
    private static double SigmoidFunction(double x) { ... }
    private static double[] Softmax(double[] hoSums) { ... }
    public double[] Train(double[][] trainMatrix) { ... }
    private double CrossEntropy(double[][] trainData,
      double[] weights) { ... }
    public double Test(double[][] testMatrix) { ... }
  }
  public class Helpers
  {
    static Random rnd = new Random(0);
    public static double[][] MakeMatrix(int rows, int cols) { ... }
    public static void ShuffleRows(double[][] matrix) { ... }
    public static int IndexOfLargest(double[] vector) { ... }
    public static void ShowVector(double[] vector, int decimals,
      bool newLine) { ... }
    public static void ShowMatrix(double[][] matrix, int numRows) { ... }
    public static void ShowTextFile(string textFile, int numLines) { ... }
  }
  public class Particle
  {
    // Class member fields here
    public Particle(double[] position, double fitness,
      double[] velocity, double[] bestPosition,
     double bestFitness) { ... }
    public override string ToString() { ... }
  }
} // ns

Além da classe que contém o método Main, o programa tem três outras classes. A classe NeuralNetwork encapsula uma rede neural sem realimentação totalmente conectada. Toda a lógica do programa principal está contida nessa classe. A classe Helpers contém seis rotinas de utilitário. A classe Particle define um objeto de partícula que é usado pela algoritmo da otimização por nuvem de partículas no método Train da classe NeuralNetwork. Um característica de programas de classificação é que há muitas estruturas de programa possíveis; a organização apresentada aqui é apenas uma possibilidade.

Gerando o arquivo de dados brutos

Na maioria dos cenários de classificação você sempre terá um conjunto de dados brutos, mas para este artigo eu criei dados brutos fictícios usando o método MakeData. Veja o processo em pseudocódigo:

create 43 arbitrary weights between -2.0 and +2.0
create a 4-5-3 neural network
load weights into the neural network
open a result file for writing
loop 100 times
  generate four random inputs x0, x1, x2, x3 between 1.0 and 9.0
  compute the y0, y1, y2 neural outputs for the input values
  determine largest of y0, y1, y2
  if y0 is largest write x0, x1, x2, x3, red
  else if y1 is largest write x0, x1, x2, x3, green
  else if y2 is largest write x0, x1, x2, x3, blue
end loop
close result file

O objetivo aqui é obter alguns dados que são definitivamente classificáveis com precisão de 100%, em vez de dados aleatórios em que não está claro qual seria a efetividade de qualquer abordagem de classificação. Em outras palavras, uso uma rede neural para criar dados brutos e, então, começo tudo de novo e uso uma rede neural para tentar classificar esses dados.

Criando as matrizes de treinamento e teste

Ao executar a análise de classificação com um conjunto de dados existentes, uma abordagem comum, chamada validação holdout, é dividir os dados em um conjunto de dados maior (frequentemente 80%) para treinar a rede neural e um conjunto de dados menor (20%) para testar o modelo. Treinar significa encontrar os pesos e tendências da rede neural que minimizam algum valor de erro. Testar significa avaliar a rede neural com os melhores pesos encontrados durante o treinamento, usando alguma medida de precisão. Aqui, o método MakeTrainAndTest cria as matrizes de treinamento e teste e também normaliza os dados de entrada numéricos e codifica os dados de saída categóricos. Em pseudocódigo, o método funciona da seguinte maneira:

determine how many rows to split data into
create a matrix to hold all data
loop
  read a line of data
  parse each field
  foreach numeric input
    normalize input to between -1.0 and +1.0
  end foreach
  encode categorical output using 1-of-N
  place normalized and encoded data in matrix
end loop
shuffle matrix
create train and test matrices
transfer first 80% rows of matrix to train, remaining rows to test

A assinatura do método é:

static void MakeTrainAndTest(string file, out double[][] trainMatrix,
  out double[][] testMatrix)

O parâmetro chamado file é o nome do arquivo de dados brutos a ser criado. Os parâmetros trainMatrix e testMatrix são parâmetros de saída nos quais os resultados são colocados. O método inicia com:

int numLines = 0;
FileStream ifs = new FileStream(file, FileMode.Open);
StreamReader sr = new StreamReader(ifs);
while (sr.ReadLine() != null)
  ++numLines;
sr.Close(); ifs.Close();
int numTrain = (int)(0.80 * numLines);
int numTest = numLines - numTrain;

Esse código conta o número de linhas do arquivo de dados brutos e então calcula quantas linhas constituem 80% e 20% dos dados. Aqui, as porcentagens são inseridas no código; convém parametrizá-las. A seguir, a matriz que irá conter todos os dados é alocada:

double[][] allData = new double[numLines][];
  for (int i = 0; i < allData.Length; ++i)
    allData[i] = new double[7];

Há sete colunas: uma coluna para cada uma das entradas numéricas e três colunas para o valor codificado 1-de-N da variável cor categórica. Lembre-se de que para esse exemplo o objetivo é prever a cor, que pode ser um de três valores categóricos: vermelho, verde ou azul. Codificar usando a técnica 1-de-N nessa situação significa codificar vermelho como (1,0, 0,0, 0,0), verde como (0,0, 1,0, 0,0) e azul como (0,0, 0,0, 1,0). Os dados categóricos devem ser codificados numericamente, porque as redes neurais lidam diretamente apenas com valores numéricos. Acontece que codificar cor usando uma abordagem simples como 1 para vermelho, 2 para verde e 3 para azul é uma má ideia. A explicação de por que essa é uma má ideia é um pouco extensa e fora do escopo deste artigo.

Uma exceção à diretriz de codificação 1-de-N para dados de saída categóricos é aquela quando há somente dois valores possíveis, como “masculino” ou “feminino”, você pode usar a codificação 1-de-(N-1) onde você tem um único valor de saída numérico de modo que, por exemplo, 0,0 signifique masculino e 1,0 signifique feminino.

A codificação é executada por este código:

tokens = line.Split(' ');
allData[row][0] = double.Parse(tokens[0]);
allData[row][1] = double.Parse(tokens[1]);
allData[row][2] = double.Parse(tokens[2]);
allData[row][3] = double.Parse(tokens[3]);
for (int i = 0; i < 4; ++i)
  allData[row][i] = 0.25 * allData[row][i] - 1.25;
if (tokens[4] == "red") {
  allData[row][4] = 1.0; 
  allData[row][5] = 0.0; 
  allData[row][6] = 0.0; }
else if (tokens[4] == "green") {
  allData[row][4] = 0.0; 
  allData[row][5] = 1.0; 
  allData[row][6] = 0.0; }
else if (tokens[4] == "blue") {
  allData[row][4] = 0.0; 
  allData[row][5] = 0.0; 
  allData[row][6] = 1.0; }

Lembre-se de que uma linha de dados brutos é semelhante a isto:

8.0 5.0 9.0 5.0 green

Os cinco campos são analisados usando String.Split. A experiência mostrou que na maioria das situações, melhores resultados são obtidos quando a entrada numérica é dimensionada para valores entre -1,0 e +1,0. Cada uma das primeiras quatro entradas numéricas é convertida multiplicando-se por 0,25 e subtraindo 1,25. Lembre-se de que as entradas numéricas no arquivo de dados fictícios são todas entre 1,0 e 9,0. Em um problema de classificação real, você teria de verificar os dados brutos e determinar os valores mínimo e máximo. Queremos que -1,0 corresponda a 1,0 e +1,0 corresponda a 9,0. Fazer uma transformação linear significa encontrar a inclinação (0,25 aqui) e a intercepção (-1,25). Estes valores podem ser calculados como:

slope = 2 / (max value - min value) = 2 / (9.0 - 1.0) = 0.25
intercept = 1.0 - (slope * max value) = 1 - (0.25 * 9.0) = -1.25

Há muitas alternativas para executar uma transformação linear em valores de entrada numéricos, mas a abordagem apresentada aqui é simples e um bom ponto de partida na maioria das situações.

Depois de transformados os quatro valores de entrada numéricos, o valor cor do arquivo de dados brutos é codificado usando a codificação 1-de-N. Quando todos os valores do arquivo de dados brutos forem calculados e colocados na matriz allData, essa matriz terá suas linhas rearranjadas usando o método utilitário ShuffleRows da classe Helpers. Depois de embaralhar a ordem das filas em allData, o espaço para as matrizes trainMatrix e testMatrix é alocado, então, as primeiras linhas numTrain de allData são copiadas em trainMatrix e as linhas numTest restantes de allData são copiadas em testMatrix.

Uma importante alternativa de projeto para a abordagem treinar-testar é dividir os dados brutos em três conjuntos: treinamento, validação e teste. A ideia é usar os dados de treinamento para determinar o melhor conjunto de pesos da rede neural junto com os dados de validação, que são usados para saber quando parar o treinamento. Também há outras abordagens, coletivamente chamadas de técnicas de validação cruzada.

A função de ativação Softmax

Ao executar a classificação usando uma rede neural em que a variável de saída é categórica, há um relacionamento bastante complicado entre a função de ativação de saída da rede neural, a computação de erro durante o treinamento e a computação da precisão preditiva da rede neural. Quando dados de saída categóricos (como cor com valores vermelho, verde ou azul) são codificados usando a codificação 1-de-N, por exemplo, (1,0 0,0 0,0) para vermelho, você quer que a rede neural emita três valores numéricos para que possa determinar um erro ao treinar a rede. No entanto, você não quer que três valores numéricos arbitrários sejam emitidos, pois não está completamente claro como computar uma condição de erro. Mas suponha que a rede neural emita três valores numéricos estando todos entre 0,0 e 1,0 e que a soma seja 1,0. Então, os valores emitidos podem ser interpretados como probabilidades, que, afinal, facilita a computação de uma condição de erro ao treinar e a computação da precisão ao testar. A função de ativação Softmax emite valores de saída dessa forma. A função Softmax aceita somas oculta-para-saída neurais e retorna os valores de saída neurais finais; pode ser implementada da seguinte forma:

private static double[] Softmax(double[] hoSums)
{
  double max = hoSums[0];
  for (int i = 0; i < hoSums.Length; ++i)
    if (hoSums[i] > max) max = hoSums[i];
  double scale = 0.0;
    for (int i = 0; i < hoSums.Length; ++i)
      scale += Math.Exp(hoSums[i] - max);
  double[] result = new double[hoSums.Length];
    for (int i = 0; i < hoSums.Length; ++i)
      result[i] = Math.Exp(hoSums[i] - max) / scale;
  return result;
}

Em princípio, a função Softmax calcula um fator de dimensionamento considerando o Exp de cada soma oculta-para-saída, somando-os e então dividindo o Exp de cada valor pelo fator de dimensionamento. Por exemplo, suponha que as três somas oculta-para-saída sejam (2,0, -1,0, 4,0). O fator de dimensionamento seria Exp(2,0) + Exp(-1,0) + Exp(4,0) = 7,39 + 0,37 + 54,60 = 62,36. Então, os valores de saída Softmax seriam Exp(2,0)/62,36, Exp(-1,0)/62,36, Exp(4,0)/62,36) = (0,12, 0,01, 0,87). Observe que as saídas finais estão todas entre 0,0 e 1,0 e que de fato somam 1,0, e ainda, que a maior soma oculta-para-saída (4,0) tem a maior saída/probabilidade (0,87), e o relacionamento é similar para os segundo e terceiro maiores valores.

Infelizmente, uma implementação ingênua do Softmax pode falhar com frequência, pois a função Exp torna-se muito grande muito rapidamente e pode produzir estouro aritmético. A implementação anterior usa o fato de que Exp(a - b) = Exp(a) / Exp(b) para calcular a saída de um modo que impeça o estouro. Se você rastrear a execução da implementação usando (2,0, -1,0, 4,0) como entradas, obterá as mesmas saídas (0,12, 0,01, 0,87) como explicado na seção anterior.

Erro de entropia cruzada

A essência de treinar uma rede neural é encontrar o conjunto de pesos que produza o menor erro para os dados no conjunto de treinamento. Por exemplo, suponha que uma linha de dados de treinamento normalizados e codificados seja (0,75 -0,25 0,25 -0,50 0,00 1,00 0,00). Lembre-se de que os quatro primeiros valores são as entradas normalizadas e os últimos três valores representam verde na codificação 1-de-N. Agora suponha que as entradas sejam alimentadas através da rede neural, que foi carregada com alguns conjuntos de pesos, e que a saída Softmax seja (0,20 0,70 0,10). Uma abordagem tradicional da computação de erro para esse vetor de teste é usar a soma das diferenças ao quadrado, que nesse caso seria (0,00 - 0,20)^2 + (1,00 - 0,70)^2 + (0,00 - 0,10)^2 = 0,14. Mas suponha que a saída Softmax foi (0,30 0,70 0,00). Esse vetor e o vetor anterior preveem que a saída é verde, pois a probabilidade de verde é 0,70. No entanto, a soma dos desvios ao quadrado desse segundo vetor é 0,18, que é diferente da primeira condição de erro. Embora a soma dos desvios ao quadrado possa ser usada para calcular erro de treinamento, alguns resultados de pesquisa sugerem que usar uma medida alternativa chamada de erro de entropia cruzada é preferível.

Em princípio, o erro de entropia cruzada para um determinado vetor de saída de rede neural v e um vetor de saída de teste t é calculado determinando a soma negativa do produto de cada componente do vetor v e o componente do vetor t correspondente. Como de costume, isso é mais bem entendido com um exemplo. Se um vetor de teste for (0,75 -0,25 0,25 -0,50 0,00 1,00 0,00) e a saída de rede neural Softmax correspondente for (0,20 0,70 0,10), então o erro de entropia cruzada será -1 * (0,00 * Log(0,20)) + (1,00 * Log(0,70)) + (0,00 * Log(0,10)) = -1 * (0 + -0,15 + 0) = 0,15. Observe que todos menos um termos da soma serão sempre zero quando a codificação 1-de-N for usada. O erro de entropia cruzada do conjunto de treinamento inteiro pode ser calculado como a soma da entropia cruzada de todos os vetores de teste, ou a entropia cruzada média de cada vetor de teste. Uma implementação do erro de entropia cruzada está listado na Figura 4.

Figura 4 Erro de entropia cruzada

private double CrossEntropy(double[][] trainData, 
  double[] weights)
{
  this.SetWeights(weights);
  double sce = 0.0; // sum of cross entropies
  for (int i = 0; i < trainData.Length; ++i)
  {
    double[] currInputs = new double[4];
    currInputs[0] = trainData[i][0];
    currInputs[1] = trainData[i][1];
    currInputs[2] = trainData[i][2];
    currInputs[3] = trainData[i][3];
    double[] currExpected = new double[3];
    currExpected[0] = trainData[i][4];
    currExpected[1] = trainData[i][5];
    currExpected[2] = trainData[i][6];
    double[] currOutputs = this.ComputeOutputs(currInputs);
    double currSum = 0.0;
    for (int j = 0; j < currOutputs.Length; ++j)
    {
      if (currExpected[j] != 0.0)
        currSum += currExpected[j] * Math.Log(currOutputs[j]);
    }
    sce += currSum; // accumulate
  }
  return -sce;
}

Treinando a rede neural

Há muitas maneiras de se treinar um classificador de rede neural para descobrir o conjunto de pesos que melhor corresponda aos dados de treinamento (ou, de maneira equivalente, resulte no menor erro de entropia cruzada). Em um alto nível de abstração, treinar uma rede neural é semelhante a isto:

create an empty neural network
loop
  generate a candidate set of weights
  load weights into neural network
  foreach training vector
    compute the neural output
    compute cross-entropy error
    accumulate total error
  end foreach
  if current weights are best found so far
    save current weights
  end if
until some stopping condition
return best weights found

De longe, a técnica mais comum usada para treinar redes neurais é chamada retropropagação. Essa técnica é o tema de um grande número de artigos de pesquisa. Tantos, na verdade, que se você for novo no campo de classificação de rede neural, você poderá facilmente ser levado a acreditar que a retropropagação é a única técnica usada para treinamento. Estimar o melhor conjunto de pesos para uma rede neural é um problema de minimização numérica. Duas alternativas comuns ao uso da retropropagação estão usando um algoritmo genético de valor real (também chamado algoritmo de otimização evolucionário), e usando a otimização por nuvem de partículas. Cada técnica de estimativa tem pontos fortes e pontos fracos. O programa mostrado na Figura 1 usa otimização por nuvem de partículas. Descrevi os algoritmos de otimização evolucionários na edição de junho de 2012 da MSDN Magazine (msdn.microsoft.com/magazine/jj133825) e otimização por nuvem de partículas na edição de agosto de 2011 (msdn.microsoft.com/magazine/hh335067).

Há muitas técnicas para determinar quando parar o treinamento de uma rede neural. Embora você possa simplesmente deixar uma algoritmo de treinamento ser executado até que um erro de entropia cruzada esteja muito próximo de zero, indicando um ajuste quase perfeito, o perigo é que os pesos resultantes irão exceder o ajuste dos dados de treinamento e os pesos criarão uma rede neural que faz uma classificação ruim de quaisquer dados que não estejam no conjunto de treinamento. Ainda, o treinamento até que não haja nenhuma alteração no erro de entropia cruzada pode facilmente levar a um ajuste excessivo do modelo. Uma abordagem simples, e uma usada pelo programa de exemplo, é limitar o treinamento a um número fixo de iterações. Na maioria dos casos, uma tática muito melhor para se evitar o ajuste excessivo é dividir o conjunto de dados de origem em conjuntos de treinamento-validação-teste. Normalmente, esses três conjuntos de dados usam 60%, 20% e 20% dos dados de origem, respectivamente. A técnica calcula o erro no conjunto de treinamento como descrito anteriormente, mas depois de cada iteração no loop principal a técnica calcula o erro de entropia cruzada no conjunto de dados de validação. Quando o erro de entropia cruzada no conjunto de validação começar a mostrar um aumento consistente no erro, há uma boa chance de que o algoritmo de treinamento tenha começado a exceder o ajuste dos dados e o treinamento deve parar. Há muitas outras técnicas de parada possíveis.

Avaliando o classificador de rede neural

Depois que o classificador de rede neural foi treinado e produziu um conjunto de melhores pesos e tendências, a próxima etapa é determinar o grau de precisão do modelo resultante (em que modelo significa a rede neural com o conjunto de melhores pesos) nos dados de teste. Embora medidas como a soma de desvios quadrados ou erro de entropia cruzada possam ser usadas, uma medida razoável de precisão é simplesmente a porcentagem de previsões corretas feitas pelo modelo. Novamente, há diversas abordagens para se medir a precisão, mas uma técnica simples é usar o que é chamada de abordagem o vencedor leva tudo. E, como de costume, a técnica é mais bem explicada por meio de exemplos. Suponha que um vetor de teste seja (-1,00 1,00 0,25 -0,50 1,00 0,00 0,00), como mostrado no primeiro conjunto de dados de previsão na Figura 1. Com o conjunto de melhores pesos, a rede neural gera uma saída Softmax prevista de (0,9 0,1 0,0). Se cada saída é interpretada como uma probabilidade, então, a mais alta probabilidade é 0,9 e a saída prevista pode ser considerada como (1 0 0), assim, o modelo faz a previsão correta. Colocado de outra maneira, a técnica o vencedor leva tudo determina o componente de saída da rede neural com o maior valor, torna esse componente um 1 e todos os outros componentes 0, e compara esse resultado com os dados reais do vetor de treinamento. No terceiro conjunto dos dados de análise de precisão na Figura 1, o vetor de teste é (-0,50 0,75 0,25 -0,75 0,0 0,0 1,0). Os dados de saída reais são (0,0 0,0 1,0), que corresponde a azul. A saída prevista é (0,3 0,6 0,1). O maior componente é o 0,6, portanto, a previsão do modelo é (0 1 0), que corresponde a verde. O modelo fez uma previsão incorreta.

Conclusão

A classificação com redes neurais é um tópico importante, fascinante e complexo. O exemplo apresentado aqui deve lhe dar uma base sólida para experimentar com a classificação de rede neural. No entanto, este artigo descreve apenas um cenário de classificação de rede neural específico, variáveis de entrada numéricas com uma variável de saída categórica, e é somente um ponto de partida. Outros cenários exigem técnicas um pouco diferentes. Por exemplo, se os dados de entrada contêm uma variável categórica, você pode esperar que ela seja codificada usando codificação 1-de-N, simplesmente igual a uma variável de saída categórica. No entanto, nesse caso, os dados de entrada devem ser codificados usando a técnica 1-de-(N-1). Se quiser saber mais sobre classificação usando redes neurais, recomendo o conjunto de sete Perguntas frequentes sobre redes neurais disponíveis em faqs.org. Os links dessas Perguntas frequentes tendem a mudar, mas você poderá encontrá-los com facilidade com uma pesquisa da Internet.

Dr. James McCaffrey trabalha para a Volt Information Sciences Inc., onde gerencia o treinamento técnico de engenheiros de software que trabalham no campus de Washington da Microsoft Redmond. Ele trabalhou em vários produtos da Microsoft, como o Internet Explorer e o MSN Busca. McCaffrey é autor do livro “.NET Test Automation Recipes” (Apress, 2006). É possível entrar em contato com ele pelos emails jmccaffrey@volt.com ou jammc@microsoft.com.

Agradecemos ao seguinte especialista técnico da Microsoft pela revisão deste artigo: Matthew Richardson