Partecipa al simposio Women in ML il 7 dicembre Registrati ora

Procedura dettagliata per la formazione del modello

Visualizza su TensorFlow.org Visualizza l'origine su GitHub

Questa guida introduce Swift per TensorFlow creando un modello di apprendimento automatico che classifica i fiori di iris in base alla specie. Utilizza Swift per TensorFlow per:

  1. Costruisci un modello,
  2. Addestra questo modello su dati di esempio e
  3. Utilizzare il modello per fare previsioni su dati sconosciuti.

Programmazione TensorFlow

Questa guida utilizza questi concetti Swift for TensorFlow di alto livello:

  • Importa i dati con l'API Epochs.
  • Costruisci modelli usando le astrazioni Swift.
  • Usa le librerie Python usando l'interoperabilità Python di Swift quando le librerie Swift pure non sono disponibili.

Questo tutorial è strutturato come molti programmi TensorFlow:

  1. Importa e analizza i set di dati.
  2. Seleziona il tipo di modello.
  3. Allena il modello.
  4. Valuta l'efficacia del modello.
  5. Usa il modello addestrato per fare previsioni.

Programma di installazione

Configura le importazioni

Importa TensorFlow e alcuni utili moduli Python.

import TensorFlow
import PythonKit
// This cell is here to display the plots in a Jupyter Notebook.
// Do not copy it into another environment.
%include "EnableIPythonDisplay.swift"
print(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)
}

Il problema della classificazione dell'iride

Immagina di essere un botanico alla ricerca di un modo automatizzato per classificare ogni fiore di iris che trovi. L'apprendimento automatico fornisce molti algoritmi per classificare statisticamente i fiori. Ad esempio, un sofisticato programma di apprendimento automatico potrebbe classificare i fiori in base a fotografie. Le nostre ambizioni sono più modeste: classificheremo i fiori di iris in base alle misure di lunghezza e larghezza dei loro sepali e petali .

Il genere Iris comprende circa 300 specie, ma il nostro programma classificherà solo le tre seguenti:

  • Iris setosa
  • Iris virginica
  • Iris versicolor
Geometria del petalo confrontata per tre specie di iris: Iris setosa, Iris virginica e Iris versicolor
Figura 1. Iris setosa (di Radomil , CC BY-SA 3.0), Iris versicolor (di Dlanglois , CC BY-SA 3.0) e Iris virginica (di Frank Mayfield , CC BY-SA 2.0).

Fortunatamente qualcuno ha già creato un set di dati di 120 fiori di iris con le misure del sepalo e del petalo. Questo è un set di dati classico che è popolare per i problemi di classificazione dell'apprendimento automatico per principianti.

Importa e analizza il set di dati di addestramento

Scarica il file del set di dati e convertilo in una struttura che può essere utilizzata da questo programma Swift.

Scarica il dataset

Scarica il file del set di dati di allenamento da 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)

Ispeziona i dati

Questo set di dati, iris_training.csv , è un file di testo normale che memorizza dati tabulari formattati come valori separati da virgole (CSV). Diamo un'occhiata alle prime 5 voci.

let f = Python.open(trainDataFilename)
for _ in 0..<5 {
    print(Python.next(f).strip())
}
print(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

Da questa visualizzazione del set di dati, notare quanto segue:

  1. La prima riga è un'intestazione contenente informazioni sul set di dati:
    • Ci sono 120 esempi totali. Ogni esempio ha quattro caratteristiche e uno dei tre possibili nomi di etichetta.
  2. Le righe successive sono record di dati, un esempio per riga, dove:
    • I primi quattro campi sono caratteristiche : queste sono le caratteristiche di un esempio. Qui, i campi contengono numeri float che rappresentano le misure dei fiori.
    • L'ultima colonna è l' etichetta : questo è il valore che vogliamo prevedere. Per questo set di dati, è un valore intero di 0, 1 o 2 che corrisponde al nome di un fiore.

Scriviamolo nel codice:

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

Ogni etichetta è associata al nome della stringa (ad esempio, "setosa"), ma l'apprendimento automatico si basa in genere su valori numerici. I numeri di etichetta vengono mappati su una rappresentazione denominata, ad esempio:

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

Per ulteriori informazioni su funzionalità ed etichette, vedere la sezione sulla terminologia ML del corso di arresto anomalo di Machine Learning .

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

Crea un set di dati utilizzando l'API di Epochs

L'API Epochs di Swift for TensorFlow è un'API di alto livello per leggere i dati e trasformarli in un modulo utilizzato per l'addestramento.

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})
    }
}

Poiché i dataset che abbiamo scaricato sono in formato CSV, scriviamo una funzione per caricare i dati come elenco di oggetti IrisBatch

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

        let featuresNp = np.loadtxt(
            contentsOf,
            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 featuresNp 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(
            contentsOf,
            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 labelsNp 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)}

    }

È ora possibile utilizzare la funzione di caricamento CSV per caricare il set di dati di addestramento e creare un oggetto TrainingEpochs

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

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

L'oggetto TrainingEpochs è una sequenza infinita di epoche. Ogni epoca contiene IrisBatch es. Diamo un'occhiata al primo elemento della prima epoca.

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

print("First batch of features: \(firstTrainFeatures)")
print("firstTrainFeatures.shape: \(firstTrainFeatures.shape)")
print("First batch of labels: \(firstTrainLabels)")
print("firstTrainLabels.shape: \(firstTrainLabels.shape)")
First batch of features: [[5.1, 2.5, 3.0, 1.1],
 [6.4, 3.2, 4.5, 1.5],
 [4.9, 3.1, 1.5, 0.1],
 [5.0, 2.0, 3.5, 1.0],
 [6.3, 2.5, 5.0, 1.9],
 [6.7, 3.1, 5.6, 2.4],
 [4.9, 3.1, 1.5, 0.1],
 [7.7, 2.8, 6.7, 2.0],
 [6.7, 3.0, 5.0, 1.7],
 [7.2, 3.6, 6.1, 2.5],
 [4.8, 3.0, 1.4, 0.1],
 [5.2, 3.4, 1.4, 0.2],
 [5.0, 3.5, 1.3, 0.3],
 [4.9, 3.1, 1.5, 0.1],
 [5.0, 3.5, 1.6, 0.6],
 [6.7, 3.3, 5.7, 2.1],
 [7.7, 3.8, 6.7, 2.2],
 [6.2, 3.4, 5.4, 2.3],
 [4.8, 3.4, 1.6, 0.2],
 [6.0, 2.9, 4.5, 1.5],
 [5.0, 3.0, 1.6, 0.2],
 [6.3, 3.4, 5.6, 2.4],
 [5.1, 3.8, 1.9, 0.4],
 [4.8, 3.1, 1.6, 0.2],
 [7.6, 3.0, 6.6, 2.1],
 [5.7, 3.0, 4.2, 1.2],
 [6.3, 3.3, 6.0, 2.5],
 [5.6, 2.5, 3.9, 1.1],
 [5.0, 3.4, 1.6, 0.4],
 [6.1, 3.0, 4.9, 1.8],
 [5.0, 3.3, 1.4, 0.2],
 [6.3, 3.3, 4.7, 1.6]]
firstTrainFeatures.shape: [32, 4]
First batch of labels: [1, 1, 0, 1, 2, 2, 0, 2, 1, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 1, 0, 2, 0, 0, 2, 1, 2, 1, 0, 2, 0, 1]
firstTrainLabels.shape: [32]

Si noti che le funzionalità per i primi esempi batchSize sono raggruppate (o raggruppate in batch ) in firstTrainFeatures e che le etichette per i primi esempi batchSize sono raggruppate in firstTrainLabels .

Puoi iniziare a vedere alcuni cluster tracciando alcune funzionalità dal batch, usando matplotlib di 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

Use `print()` to show values.

Seleziona il tipo di modello

Perché modellare?

Un modello è una relazione tra le caratteristiche e l'etichetta. Per il problema della classificazione dell'iride, il modello definisce la relazione tra le misurazioni del sepalo e del petalo e le specie previste dell'iride. Alcuni modelli semplici possono essere descritti con poche righe di algebra, ma i modelli complessi di machine learning hanno un gran numero di parametri difficili da riassumere.

Potresti determinare la relazione tra le quattro caratteristiche e le specie di iris senza utilizzare l'apprendimento automatico? Cioè, potresti usare le tecniche di programmazione tradizionali (ad esempio, molte istruzioni condizionali) per creare un modello? Forse, se hai analizzato il set di dati abbastanza a lungo da determinare le relazioni tra le misurazioni dei petali e dei sepali in una specie particolare. E questo diventa difficile, forse impossibile, su set di dati più complicati. Un buon approccio di apprendimento automatico determina il modello per te . Se inserisci un numero sufficiente di esempi rappresentativi nel giusto tipo di modello di apprendimento automatico, il programma scoprirà le relazioni per te.

Seleziona il modello

Dobbiamo selezionare il tipo di modello da addestrare. Esistono molti tipi di modelli e sceglierne uno buono richiede esperienza. Questo tutorial utilizza una rete neurale per risolvere il problema di classificazione dell'iride. Le reti neurali possono trovare relazioni complesse tra le caratteristiche e l'etichetta. È un grafico altamente strutturato, organizzato in uno o più livelli nascosti . Ogni strato nascosto è costituito da uno o più neuroni . Esistono diverse categorie di reti neurali e questo programma utilizza una rete neurale densa o completamente connessa : i neuroni in uno strato ricevono connessioni di input da ogni neurone nello strato precedente. Ad esempio, la Figura 2 illustra una fitta rete neurale costituita da un livello di input, due livelli nascosti e un livello di output:

Un diagramma dell'architettura di rete: ingressi, 2 livelli nascosti e uscite
Figura 2. Una rete neurale con funzionalità, livelli nascosti e previsioni.

Quando il modello della Figura 2 viene addestrato e alimentato con un esempio senza etichetta, fornisce tre previsioni: la probabilità che questo fiore sia la specie di iris indicata. Questa previsione è chiamata inferenza . Per questo esempio, la somma delle previsioni di output è 1,0. Nella Figura 2, questa previsione è suddivisa come: 0.02 per Iris setosa , 0.95 per Iris versicolor e 0.03 per Iris virginica . Ciò significa che il modello prevede, con una probabilità del 95%, che un fiore di esempio senza etichetta sia un Iris versicolor .

Crea un modello utilizzando Swift for TensorFlow Deep Learning Library

La libreria Swift for TensorFlow Deep Learning definisce i livelli primitivi e le convenzioni per collegarli insieme, il che semplifica la creazione di modelli e gli esperimenti.

Un modello è una struct conforme a Layer , il che significa che definisce un callAsFunction(_:) che mappa l'input Tensor s in output Tensor s. Il callAsFunction(_:) spesso semplicemente mette in sequenza l'input attraverso sottolivelli. Definiamo un IrisModel che sequenzia l'input attraverso tre sottolivelli 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()

La funzione di attivazione determina la forma di output di ciascun nodo nel livello. Queste non linearità sono importanti: senza di esse il modello sarebbe equivalente a un singolo livello. Ci sono molte attivazioni disponibili, ma ReLU è comune per i livelli nascosti.

Il numero ideale di strati e neuroni nascosti dipende dal problema e dal set di dati. Come molti aspetti dell'apprendimento automatico, scegliere la forma migliore della rete neurale richiede un mix di conoscenza e sperimentazione. Come regola generale, l'aumento del numero di strati e neuroni nascosti crea in genere un modello più potente, che richiede più dati per allenarsi in modo efficace.

Usando il modello

Diamo una rapida occhiata a ciò che questo modello fa a una serie di funzionalità:

// Apply the model to a batch of features.
let firstTrainPredictions = model(firstTrainFeatures)
print(firstTrainPredictions[0..<5])
[[  1.1514063,  -0.7520321,  -0.6730235],
 [  1.4915676,  -0.9158071,  -0.9957161],
 [  1.0549936,  -0.7799266,   -0.410466],
 [  1.1725322, -0.69009197,  -0.8345413],
 [  1.4870572,  -0.8644099,  -1.0958937]]

Qui, ogni esempio restituisce un logit per ogni classe.

Per convertire questi logit in una probabilità per ciascuna classe, utilizzare la funzione softmax :

print(softmax(firstTrainPredictions[0..<5]))
[[  0.7631462,  0.11375094, 0.123102814],
 [  0.8523791, 0.076757915,  0.07086295],
 [  0.7191151,  0.11478964,  0.16609532],
 [ 0.77540654,  0.12039323,  0.10420021],
 [  0.8541314,  0.08133837, 0.064530246]]

Prendendo l' argmax tra le classi si ottiene l'indice di classe previsto. Ma il modello non è stato ancora addestrato, quindi queste non sono buone previsioni.

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: [1, 1, 0, 1, 2, 2, 0, 2, 1, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 1, 0, 2, 0, 0, 2, 1, 2, 1, 0, 2, 0, 1]

Allena il modello

La formazione è la fase dell'apprendimento automatico in cui il modello viene gradualmente ottimizzato o il modello apprende il set di dati. L'obiettivo è imparare abbastanza sulla struttura del set di dati di addestramento per fare previsioni sui dati invisibili. Se impari troppo sul set di dati di addestramento, le previsioni funzionano solo per i dati che ha visto e non saranno generalizzabili. Questo problema è chiamato overfitting : è come memorizzare le risposte invece di capire come risolvere un problema.

Il problema della classificazione dell'iride è un esempio di apprendimento automatico supervisionato : il modello viene addestrato da esempi che contengono etichette. Nell'apprendimento automatico non supervisionato , gli esempi non contengono etichette. Al contrario, il modello in genere trova modelli tra le funzionalità.

Scegli una funzione di perdita

Sia la fase di formazione che quella di valutazione devono calcolare la perdita del modello. Questo misura quanto le previsioni di un modello si discostano dall'etichetta desiderata, in altre parole, quanto male sta funzionando il modello. Vogliamo ridurre al minimo o ottimizzare questo valore.

Il nostro modello calcolerà la sua perdita utilizzando la softmaxCrossEntropy(logits:labels:) che prende le previsioni di probabilità della classe del modello e l'etichetta desiderata e restituisce la perdita media negli esempi.

Calcoliamo la perdita per l'attuale modello non addestrato:

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

Crea un ottimizzatore

Un ottimizzatore applica i gradienti calcolati alle variabili del modello per ridurre al minimo la funzione di loss . Puoi pensare alla funzione di perdita come a una superficie curva (vedi Figura 3) e vogliamo trovare il suo punto più basso camminando. Le pendenze puntano nella direzione della salita più ripida, quindi viaggeremo nella direzione opposta e ci sposteremo giù per la collina. Calcolando in modo iterativo la perdita e il gradiente per ogni batch, regoleremo il modello durante l'allenamento. A poco a poco, il modello troverà la migliore combinazione di pesi e bias per ridurre al minimo la perdita. E minore è la perdita, migliori sono le previsioni del modello.

Algoritmi di ottimizzazione visualizzati nel tempo nello spazio 3D.
Figura 3. Algoritmi di ottimizzazione visualizzati nel tempo nello spazio 3D.
(Fonte: Stanford classe CS231n , licenza MIT, credito immagine: Alec Radford )

Swift for TensorFlow ha molti algoritmi di ottimizzazione disponibili per l'allenamento. Questo modello utilizza l'ottimizzatore SGD che implementa l'algoritmo di discesa del gradiente stocastico (SGD). Il learningRate imposta la dimensione del passo da prendere per ogni iterazione in discesa. Questo è un iperparametro che regolerai comunemente per ottenere risultati migliori.

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

Usiamo l' optimizer per eseguire un singolo passaggio di discesa del gradiente. Innanzitutto, calcoliamo il gradiente della perdita rispetto al modello:

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.7598655

Successivamente, passiamo il gradiente che abbiamo appena calcolato all'ottimizzatore, che aggiorna di conseguenza le variabili differenziabili del modello:

optimizer.update(&model, along: grads)

Se calcoliamo nuovamente la perdita, dovrebbe essere inferiore, perché i gradini di discesa del gradiente (di solito) riducono la perdita:

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

Ciclo di formazione

Con tutti i pezzi a posto, il modello è pronto per l'allenamento! Un ciclo di addestramento alimenta gli esempi di set di dati nel modello per aiutarlo a fare previsioni migliori. Il blocco di codice seguente imposta questi passaggi di formazione:

  1. Iterare su ogni epoca . Un'epoca è un passaggio attraverso il set di dati.
  2. All'interno di un'epoca, scorrere ogni batch nell'epoca di addestramento
  3. Fascicola il batch e prendi le sue caratteristiche ( x ) e l' etichetta ( y ).
  4. Utilizzando le funzionalità del batch fascicolato, fai una previsione e confrontala con l'etichetta. Misurare l'imprecisione della previsione e utilizzarla per calcolare la perdita e i gradienti del modello.
  5. Utilizzare la discesa del gradiente per aggiornare le variabili del modello.
  6. Tieni traccia di alcune statistiche per la visualizzazione.
  7. Ripetere per ogni epoca.

La variabile epochCount è il numero di volte in cui eseguire il ciclo della raccolta del set di dati. Controintuitivamente, addestrare un modello più a lungo non garantisce un modello migliore. epochCount è un iperparametro che puoi regolare. La scelta del numero giusto di solito richiede sia esperienza che sperimentazione.

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.475254, Accuracy: 0.34375
Epoch 50: Loss: 0.91668004, Accuracy: 0.6458333
Epoch 100: Loss: 0.68662673, Accuracy: 0.6979167
Epoch 150: Loss: 0.540665, Accuracy: 0.6979167
Epoch 200: Loss: 0.46283028, Accuracy: 0.6979167
Epoch 250: Loss: 0.4134724, Accuracy: 0.8229167
Epoch 300: Loss: 0.35054502, Accuracy: 0.8958333
Epoch 350: Loss: 0.2731444, Accuracy: 0.9375
Epoch 400: Loss: 0.23622067, Accuracy: 0.96875
Epoch 450: Loss: 0.18956228, Accuracy: 0.96875

Visualizza la funzione di perdita nel tempo

Sebbene sia utile stampare l'avanzamento dell'addestramento del modello, spesso è più utile vedere questo progresso. Possiamo creare grafici di base usando il modulo matplotlib di Python.

L'interpretazione di questi grafici richiede un po' di esperienza, ma vuoi davvero vedere la perdita diminuire e la precisione aumentare.

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

Use `print()` to show values.

Si noti che gli assi y dei grafici non sono a base zero.

Valuta l'efficacia del modello

Ora che il modello è stato addestrato, possiamo ottenere alcune statistiche sulle sue prestazioni.

Valutare significa determinare l'efficacia con cui il modello effettua le previsioni. Per determinare l'efficacia del modello nella classificazione dell'iride, passare alcune misurazioni del sepalo e del petalo al modello e chiedere al modello di prevedere quali specie di iris rappresentano. Quindi confrontare la previsione del modello con l'etichetta effettiva. Ad esempio, un modello che ha selezionato la specie corretta su metà degli esempi di input ha un'accuratezza di 0.5 . La figura 4 mostra un modello leggermente più efficace, che ottiene 4 previsioni su 5 corrette con una precisione dell'80%:

Caratteristiche di esempio Etichetta Previsione del modello
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
6.0 3.4 4.5 1.6 1 2
5.5 2.5 4.0 1.3 1 1
Figura 4. Un classificatore dell'iride accurato all'80%.

Imposta il set di dati di prova

La valutazione del modello è simile all'addestramento del modello. La differenza più grande è che gli esempi provengono da un set di test separato piuttosto che dal set di addestramento. Per valutare equamente l'efficacia di un modello, gli esempi utilizzati per valutare un modello devono essere diversi dagli esempi utilizzati per addestrare il modello.

La configurazione per il set di dati di test è simile a quella per il set di dati di addestramento. Scarica il set di test da 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)

Ora caricalo in un array di IrisBatch es:

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

Valutare il modello sul set di dati di test

A differenza della fase di addestramento, il modello valuta solo una singola epoca dei dati del test. Nella cella di codice seguente, ripetiamo ogni esempio nel set di test e confrontiamo la previsione del modello con l'etichetta effettiva. Viene utilizzato per misurare l'accuratezza del modello nell'intero set di test.

// 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

Possiamo vedere sul primo lotto, ad esempio, il modello è solitamente corretto:

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]

Usa il modello addestrato per fare previsioni

Abbiamo addestrato un modello e dimostrato che è buono, ma non perfetto, per classificare le specie di iris. Ora usiamo il modello addestrato per fare alcune previsioni su esempi senza etichetta ; ovvero su esempi che contengono caratteristiche ma non un'etichetta.

Nella vita reale, gli esempi senza etichetta potrebbero provenire da molte fonti diverse tra cui app, file CSV e feed di dati. Per ora, forniremo manualmente tre esempi senza etichetta per prevedere le loro etichette. Ricordiamo, i numeri di etichetta sono mappati su una rappresentazione denominata come:

  • 0 : Iris setosa
  • 1 : Iris versicolor
  • 2 : Iris 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.98731947,   0.012679046, 1.4035809e-06])
Example 1 prediction: Iris versicolor ([0.005065103,  0.85957265,  0.13536224])
Example 2 prediction: Iris virginica ([2.9613977e-05,     0.2637373,    0.73623306])