Cette page a été traduite par l'API Cloud Translation.
Switch to English

Procédure pas à pas pour la formation du modèle

Voir sur TensorFlow.org Exécuter dans Google Colab Afficher la source sur GitHub

Ce guide présente Swift pour TensorFlow en créant un modèle d'apprentissage automatique qui catégorise les fleurs d'iris par espèce. Il utilise Swift pour TensorFlow pour:

  1. Construire un modèle,
  2. Former ce modèle sur des données d'exemple, et
  3. Utilisez le modèle pour faire des prédictions sur des données inconnues.

Programmation TensorFlow

Ce guide utilise ces concepts Swift for TensorFlow de haut niveau:

  • Importez des données avec l'API Epochs.
  • Créez des modèles à l'aide d'abstractions Swift.
  • Utilisez les bibliothèques Python en utilisant l'interopérabilité Python de Swift lorsque les bibliothèques Swift pures ne sont pas disponibles.

Ce tutoriel est structuré comme de nombreux programmes TensorFlow:

  1. Importez et analysez les ensembles de données.
  2. Sélectionnez le type de modèle.
  3. Entraînez le modèle.
  4. Évaluez l'efficacité du modèle.
  5. Utilisez le modèle entraîné pour faire des prédictions.

Programme d'installation

Configurer les importations

Importez TensorFlow et quelques modules Python utiles.

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

Le problème de la classification de l'iris

Imaginez que vous êtes un botaniste à la recherche d'un moyen automatisé de catégoriser chaque fleur d'iris que vous trouvez. L'apprentissage automatique fournit de nombreux algorithmes pour classer les fleurs statistiquement. Par exemple, un programme d'apprentissage automatique sophistiqué pourrait classer les fleurs à partir de photographies. Nos ambitions sont plus modestes - nous allons classer les fleurs d'iris en fonction des mesures de longueur et de largeur de leurs sépales et pétales .

Le genre Iris comprend environ 300 espèces, mais notre programme ne classera que les trois suivantes:

  • Iris Setosa
  • Iris virginica
  • Iris versicolor
Géométrie des pétales comparée pour trois espèces d'iris: Iris setosa, Iris virginica et Iris versicolor
Figure 1. Iris setosa (par Radomil , CC BY-SA 3.0), Iris versicolor (par Dlanglois , CC BY-SA 3.0) et Iris virginica (par Frank Mayfield , CC BY-SA 2.0).

Heureusement, quelqu'un a déjà créé un ensemble de données de 120 fleurs d'iris avec les mesures des sépales et des pétales. Il s'agit d'un jeu de données classique populaire pour les problèmes de classification d'apprentissage automatique des débutants.

Importez et analysez l'ensemble de données d'entraînement

Téléchargez le fichier de jeu de données et convertissez-le en une structure qui peut être utilisée par ce programme Swift.

Téléchargez le jeu de données

Téléchargez le fichier de l'ensemble de données de formation sur 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)

Inspectez les données

Cet ensemble de données, iris_training.csv , est un fichier texte brut qui stocke des données tabulaires formatées sous forme de valeurs séparées par des virgules (CSV). Regardons les 5 premières entrées.

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

À partir de cette vue de l'ensemble de données, notez ce qui suit:

  1. La première ligne est un en-tête contenant des informations sur l'ensemble de données:
    • Il y a 120 exemples au total. Chaque exemple a quatre fonctionnalités et l'un des trois noms d'étiquettes possibles.
  2. Les lignes suivantes sont des enregistrements de données, un exemple par ligne, où:
    • Les quatre premiers champs sont des caractéristiques : ce sont des caractéristiques d'un exemple. Ici, les champs contiennent des nombres flottants représentant les mesures des fleurs.
    • La dernière colonne est l' étiquette : c'est la valeur que nous voulons prédire. Pour cet ensemble de données, il s'agit d'une valeur entière de 0, 1 ou 2 qui correspond à un nom de fleur.

Écrivons cela dans le code:

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

Chaque étiquette est associée au nom de la chaîne (par exemple, "setosa"), mais l'apprentissage automatique repose généralement sur des valeurs numériques. Les numéros d'étiquette sont mappés à une représentation nommée, telle que:

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

Pour plus d'informations sur les fonctionnalités et les étiquettes, consultez la section Terminologie ML du cours d'initiation au Machine Learning .

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

Créer un ensemble de données à l'aide de l'API Epochs

L'API Epochs de Swift for TensorFlow est une API de haut niveau permettant de lire des données et de les transformer en une forme utilisée pour la formation.

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

Puisque les jeux de données que nous avons téléchargés sont au format CSV, écrivons une fonction à charger dans les données sous forme de liste d'objets 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)}

    }

Nous pouvons maintenant utiliser la fonction de chargement CSV pour charger le jeu de données d'entraînement et créer un objet 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'objet TrainingEpochs est une séquence infinie d'époques. Chaque époque contient des IrisBatch es. Regardons le premier élément de la première époque.

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]

Notez que les fonctionnalités des premiers exemples batchSize sont regroupées (ou par lots ) dans firstTrainFeatures , et que les étiquettes des premiers exemples batchSize sont regroupées dans firstTrainLabels .

Vous pouvez commencer à voir certains clusters en traçant quelques fonctionnalités du lot, en utilisant matplotlib de 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.

Sélectionnez le type de modèle

Pourquoi modéliser?

Un modèle est une relation entre les fonctions et l'étiquette. Pour le problème de classification de l'iris, le modèle définit la relation entre les mesures des sépales et des pétales et les espèces d'iris prédites. Certains modèles simples peuvent être décrits avec quelques lignes d'algèbre, mais les modèles d'apprentissage automatique complexes ont un grand nombre de paramètres difficiles à résumer.

Pourriez-vous déterminer la relation entre les quatre caractéristiques et les espèces d'iris sans utiliser l'apprentissage automatique? Autrement dit, pourriez-vous utiliser des techniques de programmation traditionnelles (par exemple, beaucoup d'instructions conditionnelles) pour créer un modèle? Peut-être - si vous avez analysé le jeu de données suffisamment longtemps pour déterminer les relations entre les mesures des pétales et des sépales pour une espèce particulière. Et cela devient difficile, voire impossible, sur des ensembles de données plus complexes. Une bonne approche d'apprentissage automatique détermine le modèle pour vous . Si vous insérez suffisamment d'exemples représentatifs dans le bon type de modèle d'apprentissage automatique, le programme déterminera les relations pour vous.

Sélectionnez le modèle

Nous devons sélectionner le type de modèle à former. Il existe de nombreux types de modèles et choisir un bon demande de l'expérience. Ce didacticiel utilise un réseau de neurones pour résoudre le problème de classification de l'iris. Les réseaux de neurones peuvent trouver des relations complexes entre les entités et l'étiquette. Il s'agit d'un graphe hautement structuré, organisé en une ou plusieurs couches cachées . Chaque couche cachée est constituée d'un ou plusieurs neurones . Il existe plusieurs catégories de réseaux de neurones et ce programme utilise un réseau de neurones dense ou entièrement connecté : les neurones d'une couche reçoivent des connexions d'entrée de chaque neurone de la couche précédente. Par exemple, la figure 2 illustre un réseau neuronal dense composé d'une couche d'entrée, de deux couches cachées et d'une couche de sortie:

Un diagramme de l'architecture du réseau: entrées, 2 couches cachées et sorties
Figure 2. Un réseau neuronal avec des fonctionnalités, des couches cachées et des prédictions.

Lorsque le modèle de la figure 2 est formé et alimenté par un exemple non étiqueté, il donne trois prédictions: la probabilité que cette fleur soit l'espèce d'iris donnée. Cette prédiction est appelée inférence . Pour cet exemple, la somme des prédictions de sortie est de 1,0. Dans la figure 2, cette prédiction se décompose comme 0.02 : 0.02 pour Iris setosa , 0.95 pour Iris versicolor et 0.03 pour Iris virginica . Cela signifie que le modèle prédit - avec une probabilité de 95% - qu'une fleur d'exemple non étiquetée est un Iris versicolor .

Créer un modèle à l'aide de la bibliothèque d'apprentissage en profondeur Swift pour TensorFlow

La bibliothèque d'apprentissage profond Swift pour TensorFlow définit des couches primitives et des conventions pour les relier, ce qui facilite la création de modèles et l'expérimentation.

Un modèle est une struct qui se conforme à la Layer , ce qui signifie qu ' il définit un callAsFunction(_:) méthode qui fait correspondre l' entrée Tensor s à la sortie Tensor s. La callAsFunction(_:) séquence souvent simplement l'entrée à travers des sous-couches. Définissons un IrisModel qui séquence l'entrée à travers trois sous-couches 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 fonction d'activation détermine la forme de sortie de chaque nœud du calque. Ces non-linéarités sont importantes - sans elles, le modèle équivaudrait à une seule couche. Il existe de nombreuses activations disponibles, mais ReLU est courant pour les couches cachées.

Le nombre idéal de couches et de neurones cachés dépend du problème et du jeu de données. Comme de nombreux aspects de l'apprentissage automatique, choisir la meilleure forme de réseau neuronal nécessite un mélange de connaissances et d'expérimentation. En règle générale, l'augmentation du nombre de couches et de neurones cachés crée généralement un modèle plus puissant, qui nécessite plus de données pour s'entraîner efficacement.

Utilisation du modèle

Jetons un coup d'œil à ce que ce modèle fait à un lot de fonctionnalités:

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

Ici, chaque exemple renvoie un logit pour chaque classe.

Pour convertir ces logits en probabilité pour chaque classe, utilisez la fonction 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]]

Prendre l' argmax travers les classes nous donne l'indice de classe prévu. Mais le modèle n'a pas encore été formé, ce ne sont donc pas de bonnes prédictions.

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]

Former le modèle

La formation est l'étape de l'apprentissage automatique lorsque le modèle est progressivement optimisé ou lorsque le modèle apprend l'ensemble de données. L'objectif est d'en apprendre suffisamment sur la structure de l'ensemble de données d'entraînement pour faire des prédictions sur les données invisibles. Si vous en apprenez trop sur l'ensemble de données d'entraînement, les prédictions ne fonctionnent que pour les données vues et ne seront pas généralisables. Ce problème s'appelle le surajustement - c'est comme mémoriser les réponses au lieu de comprendre comment résoudre un problème.

Le problème de classification de l'iris est un exemple d' apprentissage automatique supervisé : le modèle est formé à partir d'exemples contenant des étiquettes. Dans l'apprentissage automatique non supervisé , les exemples ne contiennent pas d'étiquettes. Au lieu de cela, le modèle trouve généralement des modèles parmi les fonctionnalités.

Choisissez une fonction de perte

Les étapes de formation et d'évaluation doivent calculer la perte du modèle. Cela mesure à quel point les prédictions d'un modèle sont éloignées de l'étiquette souhaitée, en d'autres termes, les performances du modèle. Nous voulons minimiser ou optimiser cette valeur.

Notre modèle calculera sa perte en utilisant la fonction softmaxCrossEntropy(logits:labels:) qui prend les prédictions de probabilité de classe du modèle et l'étiquette souhaitée, et renvoie la perte moyenne à travers les exemples.

Calculons la perte pour le modèle non entraîné actuel:

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

Créer un optimiseur

Un optimiseur applique les gradients calculés aux variables du modèle pour minimiser la fonction de loss . Vous pouvez considérer la fonction de perte comme une surface courbe (voir figure 3) et nous voulons trouver son point le plus bas en marchant. Les gradients pointent dans la direction de la montée la plus raide - nous allons donc voyager dans le sens opposé et descendre la colline. En calculant de manière itérative la perte et le gradient pour chaque lot, nous ajusterons le modèle pendant l'entraînement. Progressivement, le modèle trouvera la meilleure combinaison de poids et de biais pour minimiser les pertes. Et plus la perte est faible, meilleures sont les prévisions du modèle.

Algorithmes d'optimisation visualisés au fil du temps dans l'espace 3D.
Figure 3. Algorithmes d'optimisation visualisés au fil du temps dans l'espace 3D.
(Source: Stanford class CS231n , licence MIT, crédit d'image: Alec Radford )

Swift pour TensorFlow dispose de nombreux algorithmes d'optimisation disponibles pour la formation. Ce modèle utilise l'optimiseur SGD qui implémente l'algorithme dedescente de gradient stochastique (SGD). Le learningRate définit la taille du pas à prendre pour chaque itération en bas de la colline. Il s'agit d'un hyperparamètre que vous ajusterez généralement pour obtenir de meilleurs résultats.

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

Utilisons l' optimizer pour effectuer une seule étape de descente de gradient. Tout d'abord, nous calculons le gradient de la perte par rapport au modèle:

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

Ensuite, nous transmettons le gradient que nous venons de calculer à l'optimiseur, qui met à jour les variables différentiables du modèle en conséquence:

optimizer.update(&model, along: grads)

Si nous calculons à nouveau la perte, elle devrait être plus petite, car les étapes de descente de gradient diminuent (généralement) la perte:

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

Boucle d'entraînement

Avec toutes les pièces en place, le modèle est prêt pour l'entraînement! Une boucle d'apprentissage alimente les exemples de jeux de données dans le modèle pour l'aider à faire de meilleures prédictions. Le bloc de code suivant configure ces étapes de formation:

  1. Itérer sur chaque époque . Une époque est un passage dans l'ensemble de données.
  2. Dans une époque, itérer sur chaque lot de l'époque d'apprentissage
  3. Rassemblez le lot et saisissez ses caractéristiques ( x ) et son étiquette ( y ).
  4. À l'aide des fonctionnalités du lot assemblé, faites une prédiction et comparez-la avec l'étiquette. Mesurez l'inexactitude de la prédiction et utilisez-la pour calculer la perte et les gradients du modèle.
  5. Utilisez la descente de gradient pour mettre à jour les variables du modèle.
  6. Gardez une trace de certaines statistiques pour la visualisation.
  7. Répétez pour chaque époque.

La variable epochCount est le nombre de boucles sur la collection de epochCount données. Contre-intuitivement, former un modèle plus longtemps ne garantit pas un meilleur modèle. epochCount est un hyperparamètre que vous pouvez régler. Choisir le bon nombre nécessite généralement à la fois de l'expérience et de l'expérimentation.

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

Visualisez la fonction de perte au fil du temps

Bien qu'il soit utile d'imprimer la progression de la formation du modèle, il est souvent plus utile de voir cette progression. Nous pouvons créer des graphiques de base en utilisant le module matplotlib de Python.

L'interprétation de ces graphiques demande une certaine expérience, mais vous voulez vraiment voir la perte diminuer et la précision augmenter.

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.

Notez que les axes y des graphiques ne sont pas basés sur zéro.

Évaluer l'efficacité du modèle

Maintenant que le modèle est formé, nous pouvons obtenir des statistiques sur ses performances.

Évaluer signifie déterminer avec quelle efficacité le modèle fait des prédictions. Pour déterminer l'efficacité du modèle à la classification de l'iris, transmettez quelques mesures de sépales et de pétales au modèle et demandez au modèle de prédire quelles espèces d'iris ils représentent. Ensuite, comparez la prédiction du modèle à l'étiquette réelle. Par exemple, un modèle qui a sélectionné les bonnes espèces sur la moitié des exemples d'entrée a une précision de 0.5 . La figure 4 montre un modèle légèrement plus efficace, obtenant 4 prédictions sur 5 correctes avec une précision de 80%:

Exemples de fonctionnalités Étiquette Prédiction de modèle
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
Figure 4. Un classificateur d'iris précis à 80%.

Configurer le jeu de données de test

L'évaluation du modèle est similaire à l'entraînement du modèle. La plus grande différence est que les exemples proviennent d'un ensemble de tests distinct plutôt que de l'ensemble d'apprentissage. Pour évaluer équitablement l'efficacité d'un modèle, les exemples utilisés pour évaluer un modèle doivent être différents des exemples utilisés pour entraîner le modèle.

La configuration de l'ensemble de données de test est similaire à la configuration de l'ensemble de données d'entraînement. Téléchargez l'ensemble de test depuis 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)

Maintenant, chargez-le dans un tableau d' IrisBatch es:

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

Évaluer le modèle sur l'ensemble de données de test

Contrairement à la phase d'apprentissage, le modèle n'évalue qu'une seule époque des données de test. Dans la cellule de code suivante, nous parcourons chaque exemple de l'ensemble de test et comparons la prédiction du modèle à l'étiquette réelle. Ceci est utilisé pour mesurer la précision du modèle sur l'ensemble de l'ensemble de 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

On peut voir sur le premier lot, par exemple, le modèle est généralement correct:

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]

Utilisez le modèle entraîné pour faire des prédictions

Nous avons formé un modèle et démontré qu'il est bon - mais pas parfait - pour classer les espèces d'iris. Utilisons maintenant le modèle entraîné pour faire des prédictions sur des exemples non étiquetés ; c'est-à-dire sur des exemples qui contiennent des fonctionnalités mais pas une étiquette.

Dans la vie réelle, les exemples non étiquetés peuvent provenir de nombreuses sources différentes, notamment des applications, des fichiers CSV et des flux de données. Pour l'instant, nous allons fournir manuellement trois exemples non étiquetés pour prédire leurs étiquettes. Rappelez-vous, les numéros d'étiquette sont mappés à une représentation nommée comme:

  • 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])