Esta página foi traduzida pela API Cloud Translation.
Switch to English

Passo a passo de treinamento de modelo

Ver em TensorFlow.org Executar no Google Colab Ver fonte no GitHub

Este guia apresenta o Swift for TensorFlow criando um modelo de aprendizado de máquina que categoriza as flores da íris por espécie. Ele usa o Swift for TensorFlow para:

  1. Construir um modelo,
  2. Treine esse modelo em dados de exemplo e
  3. Use o modelo para fazer previsões sobre dados desconhecidos.

Programação TensorFlow

Este guia usa os conceitos de alto nível do Swift for TensorFlow:

  • Importe dados com a API Epochs.
  • Crie modelos usando abstrações do Swift.
  • Use bibliotecas Python usando a interoperabilidade Python do Swift quando as bibliotecas Swift puras não estiverem disponíveis.

Este tutorial está estruturado como muitos programas TensorFlow:

  1. Importe e analise os conjuntos de dados.
  2. Selecione o tipo de modelo.
  3. Treine o modelo.
  4. Avalie a eficácia do modelo.
  5. Use o modelo treinado para fazer previsões.

Programa de instalação

Configurar importações

Importe o TensorFlow e alguns módulos úteis do Python.

 import TensorFlow
#if canImport(PythonKit)
    import PythonKit
#else
    import Python
#endif
 
 // This cell is here to display the plots in a Jupyter Notebook.
// Do not copy it into another environment.
%include "EnableIPythonDisplay.swift"
IPythonDisplay.shell.enable_matplotlib("inline")
 
('inline', 'module://ipykernel.pylab.backend_inline')

 let plt = Python.import("matplotlib.pyplot")
 
 import Foundation
import FoundationNetworking
func download(from sourceString: String, to destinationString: String) {
    let source = URL(string: sourceString)!
    let destination = URL(fileURLWithPath: destinationString)
    let data = try! Data.init(contentsOf: source)
    try! data.write(to: destination)
}
 

O problema de classificação da íris

Imagine que você é um botânico que procura uma maneira automatizada de categorizar cada flor de íris que encontrar. O aprendizado de máquina fornece muitos algoritmos para classificar flores estatisticamente. Por exemplo, um sofisticado programa de aprendizado de máquina pode classificar flores com base em fotografias. Nossas ambições são mais modestas - vamos classificar as flores de íris com base nas medidas de comprimento e largura de suas sépalas e pétalas .

O gênero Iris envolve cerca de 300 espécies, mas nosso programa classificará apenas os três seguintes:

  • Iris setosa
  • Iris virginica
  • Iris versicolor
Geometria das pétalas comparada para três espécies de íris: Iris setosa, Iris virginica e Iris versicolor
Figura 1. Iris setosa (por Radomil , CC BY-SA 3.0), Iris versicolor (por Dlanglois , CC BY-SA 3.0) e Iris virginica (por Frank Mayfield , CC BY-SA 2.0).

Felizmente, alguém já criou um conjunto de dados de 120 flores de íris com as medições de sépalas e pétalas. Este é um conjunto de dados clássico popular para problemas de classificação de aprendizado de máquina para iniciantes.

Importar e analisar o conjunto de dados de treinamento

Faça o download do arquivo do conjunto de dados e converta-o em uma estrutura que pode ser usada por este programa Swift.

Faça o download do conjunto de dados

Faça o download do arquivo do conjunto de dados de treinamento em http://download.tensorflow.org/data/iris_training.csv

 let trainDataFilename = "iris_training.csv"
download(from: "http://download.tensorflow.org/data/iris_training.csv", to: trainDataFilename)
 

Inspecione os dados

Este conjunto de dados, iris_training.csv , é um arquivo de texto sem formatação que armazena dados tabulares formatados como valores separados por vírgula (CSV). Vejamos as 5 primeiras entradas.

 let f = Python.open(trainDataFilename)
for _ in 0..<5 {
    print(Python.next(f).strip())
}
f.close()
 
120,4,setosa,versicolor,virginica
6.4,2.8,5.6,2.2,2
5.0,2.3,3.3,1.0,1
4.9,2.5,4.5,1.7,2
4.9,3.1,1.5,0.1,0

None

Nesta visão do conjunto de dados, observe o seguinte:

  1. A primeira linha é um cabeçalho que contém informações sobre o conjunto de dados:
    • Existem 120 exemplos no total. Cada exemplo possui quatro recursos e um dos três nomes de rótulos possíveis.
  2. Linhas subsequentes são registros de dados, um exemplo por linha, em que:
    • Os quatro primeiros campos são recursos : são características de um exemplo. Aqui, os campos contêm números de bóia que representam as medidas das flores.
    • A última coluna é o rótulo : este é o valor que queremos prever. Para esse conjunto de dados, é um valor inteiro 0, 1 ou 2 que corresponde ao nome da flor.

Vamos escrever isso no código:

 let featureNames = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
let labelName = "species"
let columnNames = featureNames + [labelName]

print("Features: \(featureNames)")
print("Label: \(labelName)")
 
Features: ["sepal_length", "sepal_width", "petal_length", "petal_width"]
Label: species

Cada rótulo está associado ao nome da string (por exemplo, "setosa"), mas o aprendizado de máquina geralmente depende de valores numéricos. Os números das etiquetas são mapeados para uma representação nomeada, como:

  • 0 : Iris setosa
  • 1 : íris versicolor
  • 2 : íris virginica

Para obter mais informações sobre recursos e etiquetas, consulte a seção Terminologia do ML do Curso de colisão de aprendizado de máquina .

 let classNames = ["Iris setosa", "Iris versicolor", "Iris virginica"]
 

Crie um conjunto de dados usando a API Epochs

A API Swift for Epochs do TensorFlow é uma API de alto nível para a leitura de dados e a sua transformação em um formulário usado para treinamento.

 let batchSize = 32

/// A batch of examples from the iris dataset.
struct IrisBatch {
    /// [batchSize, featureCount] tensor of features.
    let features: Tensor<Float>

    /// [batchSize] tensor of labels.
    let labels: Tensor<Int32>
}

/// Conform `IrisBatch` to `Collatable` so that we can load it into a `TrainingEpoch`.
extension IrisBatch: Collatable {
    public init<BatchSamples: Collection>(collating samples: BatchSamples)
        where BatchSamples.Element == Self {
        /// `IrisBatch`es are collated by stacking their feature and label tensors
        /// along the batch axis to produce a single feature and label tensor
        features = Tensor<Float>(stacking: samples.map{$0.features})
        labels = Tensor<Int32>(stacking: samples.map{$0.labels})
    }
}
 

Como os conjuntos de dados que baixamos estão no formato CSV, vamos escrever uma função para carregar os dados como uma lista de objetos IrisBatch

 /// Initialize an `IrisBatch` dataset from a CSV file.
func loadIrisDatasetFromCSV(
        contentsOfCSVFile: String, hasHeader: Bool, featureColumns: [Int], labelColumns: [Int]) -> [IrisBatch] {
        let np = Python.import("numpy")

        let featuresNp = np.loadtxt(
            contentsOfCSVFile,
            delimiter: ",",
            skiprows: hasHeader ? 1 : 0,
            usecols: featureColumns,
            dtype: Float.numpyScalarTypes.first!)
        guard let featuresTensor = Tensor<Float>(numpy: featuresNp) else {
            // This should never happen, because we construct numpy in such a
            // way that it should be convertible to tensor.
            fatalError("np.loadtxt result can't be converted to Tensor")
        }

        let labelsNp = np.loadtxt(
            contentsOfCSVFile,
            delimiter: ",",
            skiprows: hasHeader ? 1 : 0,
            usecols: labelColumns,
            dtype: Int32.numpyScalarTypes.first!)
        guard let labelsTensor = Tensor<Int32>(numpy: labelsNp) else {
            // This should never happen, because we construct numpy in such a
            // way that it should be convertible to tensor.
            fatalError("np.loadtxt result can't be converted to Tensor")
        }

        return zip(featuresTensor.unstacked(), labelsTensor.unstacked()).map{IrisBatch(features: $0.0, labels: $0.1)}

    }
 

Agora podemos usar a função de carregamento CSV para carregar o conjunto de dados de treinamento e criar um objeto TrainingEpochs

 let trainingDataset: [IrisBatch] = loadIrisDatasetFromCSV(contentsOfCSVFile: trainDataFilename, 
                                                  hasHeader: true, 
                                                  featureColumns: [0, 1, 2, 3], 
                                                  labelColumns: [4])

let trainingEpochs: TrainingEpochs = TrainingEpochs(samples: trainingDataset, batchSize: batchSize)
 

O objeto TrainingEpochs é uma sequência infinita de épocas. Cada época contém IrisBatch es. Vejamos o primeiro elemento da primeira época.

 let firstTrainEpoch = trainingEpochs.next()!
let firstTrainBatch = firstTrainEpoch.first!.collated
let firstTrainFeatures = firstTrainBatch.features
let firstTrainLabels = firstTrainBatch.labels

print("First batch of features: \(firstTrainFeatures)")
print("First batch of labels: \(firstTrainLabels)")
 
First batch of features: [[6.0, 3.0, 4.8, 1.8],
 [6.3, 2.5, 5.0, 1.9],
 [5.7, 2.8, 4.5, 1.3],
 [5.6, 2.9, 3.6, 1.3],
 [6.1, 3.0, 4.9, 1.8],
 [5.5, 3.5, 1.3, 0.2],
 [7.9, 3.8, 6.4, 2.0],
 [5.1, 3.5, 1.4, 0.3],
 [6.7, 3.3, 5.7, 2.1],
 [5.1, 3.8, 1.9, 0.4],
 [5.0, 2.3, 3.3, 1.0],
 [7.0, 3.2, 4.7, 1.4],
 [6.8, 3.2, 5.9, 2.3],
 [4.6, 3.6, 1.0, 0.2],
 [6.7, 3.1, 4.4, 1.4],
 [4.9, 3.1, 1.5, 0.1],
 [5.6, 2.7, 4.2, 1.3],
 [5.8, 2.8, 5.1, 2.4],
 [4.8, 3.0, 1.4, 0.1],
 [6.1, 2.8, 4.0, 1.3],
 [6.3, 2.3, 4.4, 1.3],
 [6.3, 3.4, 5.6, 2.4],
 [4.6, 3.4, 1.4, 0.3],
 [6.6, 3.0, 4.4, 1.4],
 [6.4, 2.8, 5.6, 2.2],
 [6.4, 3.1, 5.5, 1.8],
 [4.7, 3.2, 1.3, 0.2],
 [6.1, 2.9, 4.7, 1.4],
 [5.0, 3.2, 1.2, 0.2],
 [6.3, 2.7, 4.9, 1.8],
 [5.2, 3.5, 1.5, 0.2],
 [6.4, 3.2, 4.5, 1.5]]
First batch of labels: [2, 2, 1, 1, 2, 0, 2, 0, 2, 0, 1, 1, 2, 0, 1, 0, 1, 2, 0, 1, 1, 2, 0, 1, 2, 2, 0, 1, 0, 2, 0, 1]

Observe que os recursos dos primeiros exemplos batchSize são agrupados (ou em lote ) em firstTrainFeatures e que os rótulos dos primeiros exemplos batchSize são agrupados em firstTrainLabels .

Você pode começar a ver alguns clusters plotando alguns recursos do lote, usando o matplotlib do Python:

 let firstTrainFeaturesTransposed = firstTrainFeatures.transposed()
let petalLengths = firstTrainFeaturesTransposed[2].scalars
let sepalLengths = firstTrainFeaturesTransposed[0].scalars

plt.scatter(petalLengths, sepalLengths, c: firstTrainLabels.array.scalars)
plt.xlabel("Petal length")
plt.ylabel("Sepal length")
plt.show()
 

png

None

Selecione o tipo de modelo

Por que modelar?

Um modelo é um relacionamento entre recursos e o rótulo. Para o problema de classificação da íris, o modelo define a relação entre as medidas de sépala e pétala e as espécies de íris previstas. Alguns modelos simples podem ser descritos com algumas linhas de álgebra, mas modelos complexos de aprendizado de máquina têm um grande número de parâmetros que são difíceis de resumir.

Você poderia determinar a relação entre os quatro recursos e as espécies de íris sem usar o aprendizado de máquina? Ou seja, você poderia usar técnicas tradicionais de programação (por exemplo, muitas instruções condicionais) para criar um modelo? Talvez - se você analisou o conjunto de dados por tempo suficiente para determinar as relações entre as medidas de pétalas e sépalas com uma espécie específica. E isso se torna difícil - talvez impossível - em conjuntos de dados mais complicados. Uma boa abordagem de aprendizado de máquina determina o modelo para você . Se você fornecer exemplos representativos suficientes para o tipo certo de modelo de aprendizado de máquina, o programa descobrirá os relacionamentos para você.

Selecione o modelo

Precisamos selecionar o tipo de modelo para treinar. Existem muitos tipos de modelos e escolher um bom requer experiência. Este tutorial usa uma rede neural para resolver o problema de classificação da íris. As redes neurais podem encontrar relacionamentos complexos entre os recursos e o rótulo. É um gráfico altamente estruturado, organizado em uma ou mais camadas ocultas . Cada camada oculta consiste em um ou mais neurônios . Existem várias categorias de redes neurais e este programa usa uma rede neural densa ou totalmente conectada : os neurônios em uma camada recebem conexões de entrada de todos os neurônios da camada anterior. Por exemplo, a Figura 2 ilustra uma rede neural densa que consiste em uma camada de entrada, duas camadas ocultas e uma camada de saída:

Um diagrama da arquitetura de rede: entradas, 2 camadas ocultas e saídas
Figura 2. Uma rede neural com recursos, camadas ocultas e previsões.

Quando o modelo da Figura 2 é treinado e alimentado com um exemplo não marcado, ele produz três previsões: a probabilidade de que essa flor seja a espécie de íris especificada. Essa previsão é chamada de inferência . Neste exemplo, a soma das previsões de saída é 1.0. Na Figura 2, essa previsão é dividida em: 0.02 para Iris setosa , 0.95 para Iris versicolor e 0.03 para Iris virginica . Isso significa que o modelo prevê - com 95% de probabilidade - que um exemplo de flor sem rótulo seja uma íris versicolor .

Crie um modelo usando a biblioteca de aprendizado profundo do Swift for TensorFlow

A Swift for TensorFlow Deep Learning Library define camadas e convenções primitivas para conectá- las, o que facilita a criação de modelos e a experimentação.

Um modelo é uma struct que está em conformidade com a Layer , o que significa que define um callAsFunction(_:) que mapeia o Tensor entrada s para o Tensor saída s. O callAsFunction(_:) geralmente simplesmente sequencia a entrada por subcamadas. Vamos definir um IrisModel que sequencie a entrada através de três subcamadas Dense .

 import TensorFlow

let hiddenSize: Int = 10
struct IrisModel: Layer {
    var layer1 = Dense<Float>(inputSize: 4, outputSize: hiddenSize, activation: relu)
    var layer2 = Dense<Float>(inputSize: hiddenSize, outputSize: hiddenSize, activation: relu)
    var layer3 = Dense<Float>(inputSize: hiddenSize, outputSize: 3)
    
    @differentiable
    func callAsFunction(_ input: Tensor<Float>) -> Tensor<Float> {
        return input.sequenced(through: layer1, layer2, layer3)
    }
}

var model = IrisModel()
 

A função de ativação determina a forma de saída de cada nó na camada. Essas não linearidades são importantes - sem elas o modelo seria equivalente a uma única camada. Existem muitas ativações disponíveis, mas o ReLU é comum para camadas ocultas.

O número ideal de camadas e neurônios ocultos depende do problema e do conjunto de dados. Como muitos aspectos do aprendizado de máquina, escolher a melhor forma da rede neural requer uma mistura de conhecimento e experimentação. Como regra geral, aumentar o número de camadas e neurônios ocultos geralmente cria um modelo mais poderoso, que requer mais dados para treinar com eficiência.

Usando o modelo

Vamos dar uma rápida olhada no que esse modelo faz em um lote de recursos:

 // Apply the model to a batch of features.
let firstTrainPredictions = model(firstTrainFeatures)
firstTrainPredictions[0..<5]
 
[[   1.454208,  0.24379121,  -0.9649837],
 [  1.4504895,  0.31482732, -0.88196427],
 [  1.4068713,  0.26030555,  -0.8821216],
 [    1.48215,   0.2896214,  -0.8973131],
 [  1.4721411,  0.25258976,  -0.9691639]]

Aqui, cada exemplo retorna um logit para cada classe.

Para converter esses logits em uma probabilidade para cada classe, use a função softmax :

 softmax(firstTrainPredictions[0..<5])
 
[[  0.7209459,  0.21489452, 0.064159505],
 [ 0.70508605,  0.22648023, 0.068433754],
 [ 0.70467466,   0.2238935, 0.071431816],
 [  0.7163051,  0.21736495, 0.066330045],
 [  0.7233744,  0.21365778, 0.062967695]]

Levar o argmax entre classes nos fornece o índice de classe previsto. Mas, o modelo ainda não foi treinado, portanto, essas não são boas previsões.

 print("Prediction: \(firstTrainPredictions.argmax(squeezingAxis: 1))")
print("    Labels: \(firstTrainLabels)")
 
Prediction: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    Labels: [2, 2, 1, 1, 2, 0, 2, 0, 2, 0, 1, 1, 2, 0, 1, 0, 1, 2, 0, 1, 1, 2, 0, 1, 2, 2, 0, 1, 0, 2, 0, 1]

Treine o modelo

O treinamento é o estágio do aprendizado de máquina quando o modelo é gradualmente otimizado ou o modelo aprende o conjunto de dados. O objetivo é aprender o suficiente sobre a estrutura do conjunto de dados de treinamento para fazer previsões sobre dados não vistos. Se você aprender muito sobre o conjunto de dados de treinamento, as previsões funcionarão apenas para os dados vistos e não serão generalizáveis. Esse problema é chamado de adaptação excessiva - é como memorizar as respostas em vez de entender como resolver um problema.

O problema de classificação da íris é um exemplo de aprendizado de máquina supervisionado : o modelo é treinado a partir de exemplos que contêm rótulos. No aprendizado de máquina não supervisionado , os exemplos não contêm rótulos. Em vez disso, o modelo normalmente encontra padrões entre os recursos.

Escolha uma função de perda

Os estágios de treinamento e avaliação precisam calcular a perda do modelo. Isso mede o quanto as previsões de um modelo estão do rótulo desejado, ou seja, o desempenho ruim do modelo. Queremos minimizar ou otimizar esse valor.

Nosso modelo calculará sua perda usando a função softmaxCrossEntropy(logits:labels:) , que obtém as previsões de probabilidade de classe do modelo e o rótulo desejado e retorna a perda média nos exemplos.

Vamos calcular a perda para o modelo atual não treinado:

 let untrainedLogits = model(firstTrainFeatures)
let untrainedLoss = softmaxCrossEntropy(logits: untrainedLogits, labels: firstTrainLabels)
print("Loss test: \(untrainedLoss)")
 
Loss test: 1.6138709

 let optimizer = SGD(for: model, learningRate: 0.01)
 

Vamos usar o optimizer para executar uma única etapa de descida de gradiente. Primeiro, calculamos o gradiente da perda em relação ao modelo:

 let (loss, grads) = valueWithGradient(at: model) { model -> Tensor<Float> in
    let logits = model(firstTrainFeatures)
    return softmaxCrossEntropy(logits: logits, labels: firstTrainLabels)
}
print("Current loss: \(loss)")
 
Current loss: 1.6138709

Em seguida, passamos o gradiente que acabamos de calcular para o otimizador, que atualiza as variáveis ​​diferenciáveis ​​do modelo de acordo:

 optimizer.update(&model, along: grads)
 

Se calcularmos a perda novamente, ela deverá ser menor, porque as etapas de descida do gradiente (geralmente) diminuem a perda:

 let logitsAfterOneStep = model(firstTrainFeatures)
let lossAfterOneStep = softmaxCrossEntropy(logits: logitsAfterOneStep, labels: firstTrainLabels)
print("Next loss: \(lossAfterOneStep)")
 
Next loss: 1.461563

Loop de treinamento

Com todas as peças no lugar, o modelo está pronto para o treinamento! Um loop de treinamento alimenta os exemplos de conjunto de dados no modelo para ajudá-lo a fazer melhores previsões. O seguinte bloco de código configura estas etapas de treinamento:

  1. Iterar sobre cada época . Uma época é uma passagem pelo conjunto de dados.
  2. Dentro de uma época, itere sobre cada lote na época de treinamento
  3. Agrupe o lote e pegue seus recursos ( x ) e o rótulo ( y ).
  4. Usando os recursos do lote agrupado, faça uma previsão e compare-a com o rótulo. Meça a imprecisão da previsão e use-a para calcular a perda e os gradientes do modelo.
  5. Use a descida gradiente para atualizar as variáveis ​​do modelo.
  6. Acompanhe algumas estatísticas para visualização.
  7. Repita para cada época.

A variável epochCount é o número de vezes para repetir a coleção de conjuntos de dados. Contra-intuitivamente, treinar um modelo por mais tempo não garante um modelo melhor. epochCount é um hiperparâmetro que você pode ajustar. Escolher o número certo geralmente requer experiência e experimentação.

 let epochCount = 500
var trainAccuracyResults: [Float] = []
var trainLossResults: [Float] = []
 
 func accuracy(predictions: Tensor<Int32>, truths: Tensor<Int32>) -> Float {
    return Tensor<Float>(predictions .== truths).mean().scalarized()
}

for (epochIndex, epoch) in trainingEpochs.prefix(epochCount).enumerated() {
    var epochLoss: Float = 0
    var epochAccuracy: Float = 0
    var batchCount: Int = 0
    for batchSamples in epoch {
        let batch = batchSamples.collated
        let (loss, grad) = valueWithGradient(at: model) { (model: IrisModel) -> Tensor<Float> in
            let logits = model(batch.features)
            return softmaxCrossEntropy(logits: logits, labels: batch.labels)
        }
        optimizer.update(&model, along: grad)
        
        let logits = model(batch.features)
        epochAccuracy += accuracy(predictions: logits.argmax(squeezingAxis: 1), truths: batch.labels)
        epochLoss += loss.scalarized()
        batchCount += 1
    }
    epochAccuracy /= Float(batchCount)
    epochLoss /= Float(batchCount)
    trainAccuracyResults.append(epochAccuracy)
    trainLossResults.append(epochLoss)
    if epochIndex % 50 == 0 {
        print("Epoch \(epochIndex): Loss: \(epochLoss), Accuracy: \(epochAccuracy)")
    }
}
 
Epoch 0: Loss: 1.316198, Accuracy: 0.36458334
Epoch 50: Loss: 0.5683666, Accuracy: 0.6875
Epoch 100: Loss: 0.42986333, Accuracy: 0.8541667
Epoch 150: Loss: 0.3263924, Accuracy: 0.9583333
Epoch 200: Loss: 0.24369155, Accuracy: 0.9791667
Epoch 250: Loss: 0.17126365, Accuracy: 0.9791667
Epoch 300: Loss: 0.1455363, Accuracy: 0.96875
Epoch 350: Loss: 0.121186025, Accuracy: 0.9895833
Epoch 400: Loss: 0.11745409, Accuracy: 0.9791667
Epoch 450: Loss: 0.08234135, Accuracy: 0.9895833

Visualize a função de perda ao longo do tempo

Embora seja útil imprimir o progresso do treinamento do modelo, geralmente é mais útil ver esse progresso. Podemos criar gráficos básicos usando o módulo matplotlib do Python.

A interpretação desses gráficos exige alguma experiência, mas você realmente deseja que a perda diminua e a precisão suba.

 plt.figure(figsize: [12, 8])

let accuracyAxes = plt.subplot(2, 1, 1)
accuracyAxes.set_ylabel("Accuracy")
accuracyAxes.plot(trainAccuracyResults)

let lossAxes = plt.subplot(2, 1, 2)
lossAxes.set_ylabel("Loss")
lossAxes.set_xlabel("Epoch")
lossAxes.plot(trainLossResults)

plt.show()
 

png

None

Observe que os eixos y dos gráficos não são baseados em zero.

Avalie a eficácia do modelo

Agora que o modelo foi treinado, podemos obter algumas estatísticas sobre seu desempenho.

Avaliar significa determinar com que eficácia o modelo faz previsões. Para determinar a eficácia do modelo na classificação da íris, passe algumas medições de sépalas e pétalas ao modelo e peça ao modelo para prever quais espécies de íris eles representam. Em seguida, compare a previsão do modelo com o rótulo real. Por exemplo, um modelo que selecionou as espécies corretas na metade dos exemplos de entrada tem uma precisão de 0.5 . A Figura 4 mostra um modelo um pouco mais eficaz, corrigindo 4 de 5 previsões com precisão de 80%:

Recursos de exemplo Rótulo Predição de modelo
5,9 3.0 4.3. 1.5 1 1
6,9 3.1. 5.4 2.1 2 2
5.1 3.3. 1.7 0,5 0 0 0 0
6.0 3.4. 4.5 1.6 1 2
5.5 2.5 4.0 1.3 1 1
Figura 4. Um classificador de íris com 80% de precisão.

Configurar o conjunto de dados de teste

A avaliação do modelo é semelhante ao treinamento do modelo. A maior diferença é que os exemplos vêm de um conjunto de testes separado , e não do conjunto de treinamento. Para avaliar de maneira justa a eficácia de um modelo, os exemplos usados ​​para avaliar um modelo devem ser diferentes dos exemplos usados ​​para treiná-lo.

A configuração do conjunto de dados de teste é semelhante à configuração do conjunto de dados de treinamento. Faça o download do conjunto de testes em http://download.tensorflow.org/data/iris_test.csv :

 let testDataFilename = "iris_test.csv"
download(from: "http://download.tensorflow.org/data/iris_test.csv", to: testDataFilename)
 

Agora carregue-o em uma matriz de IrisBatch es:

 let testDataset = loadIrisDatasetFromCSV(
    contentsOfCSVFile: testDataFilename, hasHeader: true,
    featureColumns: [0, 1, 2, 3], labelColumns: [4]).inBatches(of: batchSize)
 

Avalie o modelo no conjunto de dados de teste

Diferentemente da fase de treinamento, o modelo avalia apenas uma única época dos dados de teste. Na célula de código a seguir, iteramos sobre cada exemplo no conjunto de testes e comparamos a previsão do modelo com o rótulo real. Isso é usado para medir a precisão do modelo em todo o conjunto de testes.

 // NOTE: Only a single batch will run in the loop since the batchSize we're using is larger than the test set size
for batchSamples in testDataset {
    let batch = batchSamples.collated
    let logits = model(batch.features)
    let predictions = logits.argmax(squeezingAxis: 1)
    print("Test batch accuracy: \(accuracy(predictions: predictions, truths: batch.labels))")
}
 
Test batch accuracy: 0.96666664

Podemos ver no primeiro lote, por exemplo, o modelo geralmente está correto:

 let firstTestBatch = testDataset.first!.collated
let firstTestBatchLogits = model(firstTestBatch.features)
let firstTestBatchPredictions = firstTestBatchLogits.argmax(squeezingAxis: 1)

print(firstTestBatchPredictions)
print(firstTestBatch.labels)
 
[1, 2, 0, 1, 1, 1, 0, 2, 1, 2, 2, 0, 2, 1, 1, 0, 1, 0, 0, 2, 0, 1, 2, 2, 1, 1, 0, 1, 2, 1]
[1, 2, 0, 1, 1, 1, 0, 2, 1, 2, 2, 0, 2, 1, 1, 0, 1, 0, 0, 2, 0, 1, 2, 1, 1, 1, 0, 1, 2, 1]

Use o modelo treinado para fazer previsões

Nós treinamos um modelo e demonstramos que ele é bom - mas não perfeito - na classificação de espécies de íris. Agora vamos usar o modelo treinado para fazer algumas previsões em exemplos não rotulados ; isto é, em exemplos que contêm recursos, mas não um rótulo.

Na vida real, os exemplos não rotulados podem vir de várias fontes diferentes, incluindo aplicativos, arquivos CSV e feeds de dados. Por enquanto, forneceremos manualmente três exemplos não identificados para prever seus rótulos. Lembre-se, os números das etiquetas são mapeados para uma representação nomeada como:

  • 0 : Iris setosa
  • 1 : íris versicolor
  • 2 : íris virginica
 let unlabeledDataset: Tensor<Float> =
    [[5.1, 3.3, 1.7, 0.5],
     [5.9, 3.0, 4.2, 1.5],
     [6.9, 3.1, 5.4, 2.1]]

let unlabeledDatasetPredictions = model(unlabeledDataset)

for i in 0..<unlabeledDatasetPredictions.shape[0] {
    let logits = unlabeledDatasetPredictions[i]
    let classIdx = logits.argmax().scalar!
    print("Example \(i) prediction: \(classNames[Int(classIdx)]) (\(softmax(logits)))")
}
 
Example 0 prediction: Iris setosa ([   0.9853947,  0.014605234, 1.049226e-10])
Example 1 prediction: Iris versicolor ([0.0022595704,    0.9383183,  0.059422087])
Example 2 prediction: Iris virginica ([2.2416664e-06,    0.06604068,    0.93395704])

Crie um otimizador

Um otimizador aplica os gradientes computados às variáveis ​​do modelo para minimizar a função de loss . Você pode pensar na função de perda como uma superfície curva (veja a Figura 3) e queremos encontrar seu ponto mais baixo andando por aí. Os gradientes apontam na direção da subida mais íngreme - portanto, seguiremos o caminho oposto e desceremos a colina. Ao calcular iterativamente a perda e o gradiente de cada lote, ajustaremos o modelo durante o treinamento. Gradualmente, o modelo encontrará a melhor combinação de pesos e viés para minimizar a perda. E quanto menor a perda, melhores as previsões do modelo.

Algoritmos de otimização visualizados ao longo do tempo no espaço 3D.
Figura 3. Algoritmos de otimização visualizados ao longo do tempo no espaço 3D.
(Fonte: classe Stanford CS231n , licença MIT, crédito de imagem: Alec Radford )

O Swift for TensorFlow possui muitos algoritmos de otimização disponíveis para treinamento. Este modelo usa o otimizador SGD que implementa o algoritmo de descida de gradiente estocástico (SGD). O learningRate define o tamanho da etapa a ser executada para cada iteração descendo a colina. Este é um hiperparâmetro que você costuma ajustar para obter melhores resultados.