Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Przewodnik po szkoleniu modelu

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub

Ten przewodnik wprowadza Swift for TensorFlow, tworząc model uczenia maszynowego, który kategoryzuje kwiaty tęczówki według gatunków. Wykorzystuje Swift for TensorFlow do:

  1. Zbudować model,
  2. Wytrenuj ten model na przykładowych danych i
  3. Użyj modelu, aby przewidzieć nieznane dane.

Programowanie TensorFlow

W tym przewodniku zastosowano następujące koncepcje wysokiego poziomu języka Swift for TensorFlow:

  • Importuj dane za pomocą interfejsu Epochs API.
  • Twórz modele przy użyciu abstrakcji Swift.
  • Użyj bibliotek Python, korzystając ze współdziałania języka Swift w języku Python, gdy czyste biblioteki Swift nie są dostępne.

Ten samouczek ma strukturę wielu programów TensorFlow:

  1. Importuj i analizuj zestawy danych.
  2. Wybierz typ modelu.
  3. Wytrenuj model.
  4. Oceń skuteczność modelu.
  5. Użyj wytrenowanego modelu do tworzenia prognoz.

Program instalacyjny

Skonfiguruj import

Importuj TensorFlow i kilka przydatnych modułów Pythona.

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

Problem klasyfikacji tęczówki

Wyobraź sobie, że jesteś botanikiem szukającym automatycznego sposobu kategoryzowania każdego znalezionego kwiatu irysa. Uczenie maszynowe zapewnia wiele algorytmów do statystycznej klasyfikacji kwiatów. Na przykład zaawansowany program do uczenia maszynowego może klasyfikować kwiaty na podstawie fotografii. Nasze ambicje są skromniejsze - będziemy klasyfikować kwiaty tęczówki na podstawie pomiarów długości i szerokości ich działek i płatków .

Rodzaj Iris obejmuje około 300 gatunków, ale nasz program sklasyfikuje tylko następujące trzy:

  • Iris setosa
  • Iris virginica
  • Iris versicolor
Porównanie geometrii płatków dla trzech gatunków tęczówki: Iris setosa, Iris virginica i Iris versicolor
Ryc. 1. Iris setosa (autorstwa Radomil , CC BY-SA 3,0), Iris versicolor (autorstwa Dlanglois , CC BY-SA 3,0) i Iris virginica (autorstwa Franka Mayfielda , CC BY-SA 2,0).

Na szczęście ktoś już stworzył zestaw danych 120 kwiatów tęczówki z wymiarami działek i płatków. Jest to klasyczny zbiór danych popularny w przypadku początkujących problemów z klasyfikacją systemów uczących się.

Importuj i analizuj zestaw danych szkoleniowych

Pobierz plik zestawu danych i przekonwertuj go na strukturę, której może używać ten program Swift.

Pobierz zestaw danych

Pobierz plik zestawu danych szkoleniowych z 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)

Sprawdź dane

Ten zbiór danych, iris_training.csv , jest zwykłym plikiem tekstowym, w którym są przechowywane dane tabelaryczne sformatowane jako wartości rozdzielane przecinkami (CSV). Spójrzmy na pierwsze 5 wpisów.

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

Z tego widoku zbioru danych zwróć uwagę na następujące kwestie:

  1. Pierwsza linia to nagłówek zawierający informacje o zbiorze danych:
    • Łącznie jest 120 przykładów. Każdy przykład ma cztery funkcje i jedną z trzech możliwych nazw etykiet.
  2. Kolejne wiersze to rekordy danych, jeden przykład na wiersz, gdzie:
    • Pierwsze cztery pola to cechy : są to cechy przykładu. Tutaj pola zawierają liczby zmiennoprzecinkowe reprezentujące pomiary kwiatów.
    • Ostatnia kolumna to etykieta : to wartość, którą chcemy przewidzieć. W przypadku tego zbioru danych jest to liczba całkowita równa 0, 1 lub 2, która odpowiada nazwie kwiatu.

Napiszmy to w kodzie:

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

Każda etykieta jest powiązana z nazwą ciągu (na przykład „setosa”), ale systemy uczące się zwykle opierają się na wartościach liczbowych. Numery etykiet są mapowane na nazwaną reprezentację, taką jak:

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

Aby uzyskać więcej informacji o funkcjach i etykietach, zobacz sekcję Terminologia ML kursu Machine Learning Crash Course .

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

Utwórz zestaw danych za pomocą interfejsu Epochs API

Swift for TensorFlow's Epochs API to interfejs API wysokiego poziomu do odczytywania danych i przekształcania ich w formę używaną do uczenia.

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

Ponieważ pobrane przez nas zbiory danych są w formacie CSV, napiszmy funkcję do załadowania danych jako listę obiektów 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)}

    }

Możemy teraz użyć funkcji ładowania CSV, aby załadować zestaw danych szkoleniowych i utworzyć obiekt TrainingEpochs

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

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

Obiekt TrainingEpochs to nieskończona sekwencja epok. Każda epoka zawiera IrisBatch . Spójrzmy na pierwszy element pierwszej epoki.

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.0, 2.3, 3.3, 1.0],
 [5.6, 2.9, 3.6, 1.3],
 [5.7, 2.8, 4.1, 1.3],
 [4.4, 2.9, 1.4, 0.2],
 [5.0, 3.2, 1.2, 0.2],
 [4.9, 3.1, 1.5, 0.1],
 [4.9, 3.1, 1.5, 0.1],
 [5.8, 2.6, 4.0, 1.2],
 [5.8, 2.7, 5.1, 1.9],
 [5.0, 3.5, 1.3, 0.3],
 [6.3, 2.5, 5.0, 1.9],
 [4.6, 3.1, 1.5, 0.2],
 [5.9, 3.2, 4.8, 1.8],
 [6.9, 3.2, 5.7, 2.3],
 [5.7, 4.4, 1.5, 0.4],
 [5.8, 2.8, 5.1, 2.4],
 [5.4, 3.0, 4.5, 1.5],
 [6.3, 3.4, 5.6, 2.4],
 [5.9, 3.0, 5.1, 1.8],
 [4.6, 3.4, 1.4, 0.3],
 [5.0, 3.4, 1.6, 0.4],
 [6.3, 2.3, 4.4, 1.3],
 [4.4, 3.0, 1.3, 0.2],
 [5.5, 2.6, 4.4, 1.2],
 [5.4, 3.7, 1.5, 0.2],
 [5.0, 3.4, 1.5, 0.2],
 [6.8, 3.0, 5.5, 2.1],
 [6.2, 2.8, 4.8, 1.8],
 [5.4, 3.9, 1.7, 0.4],
 [7.7, 2.6, 6.9, 2.3],
 [4.9, 3.1, 1.5, 0.1],
 [5.8, 2.7, 4.1, 1.0]]
firstTrainFeatures.shape: [32, 4]
First batch of labels: [1, 1, 1, 0, 0, 0, 0, 1, 2, 0, 2, 0, 1, 2, 0, 2, 1, 2, 2, 0, 0, 1, 0, 1, 0, 0, 2, 2, 0, 2, 0, 1]
firstTrainLabels.shape: [32]

Należy zauważyć, że te funkcje w pierwszych batchSize przykładami są grupowane (lub dozowane) do firstTrainFeatures i że etykiety pierwszych batchSize przykładami są dozowane do firstTrainLabels .

Możesz zacząć widzieć kilka klastrów, wykreślając kilka funkcji z partii, używając matplotlib Pythona:

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

Wybierz typ modelu

Dlaczego modelować?

Model to relacja między elementami a etykietą. W przypadku problemu klasyfikacji tęczówki model definiuje zależność między wymiarami działki i płatka a przewidywanymi gatunkami tęczówki. Niektóre proste modele można opisać kilkoma liniami algebry, ale złożone modele uczenia maszynowego mają dużą liczbę parametrów, które są trudne do podsumowania.

Czy możesz określić związek między czterema cechami a gatunkiem tęczówki bez korzystania z uczenia maszynowego? To znaczy, czy mógłbyś użyć tradycyjnych technik programowania (na przykład wielu instrukcji warunkowych) do stworzenia modelu? Być może - jeśli przeanalizowałeś zbiór danych wystarczająco długo, aby określić relacje między pomiarami płatków i działek działek a konkretnym gatunkiem. A to staje się trudne - może niemożliwe - w przypadku bardziej skomplikowanych zbiorów danych. Dobre podejście do uczenia maszynowego określa model za Ciebie . Jeśli podasz wystarczającą liczbę reprezentatywnych przykładów do odpowiedniego typu modelu uczenia maszynowego, program wykryje relacje za Ciebie.

Wybierz model

Musimy wybrać rodzaj modelu do trenowania. Istnieje wiele rodzajów modeli, a wybór dobrego wymaga doświadczenia. Ten samouczek wykorzystuje sieć neuronową do rozwiązania problemu klasyfikacji tęczówki. Sieci neuronowe mogą znajdować złożone relacje między cechami a etykietą. Jest to wysoce ustrukturyzowany wykres, podzielony na jedną lub więcej ukrytych warstw . Każda ukryta warstwa składa się z jednego lub więcej neuronów . Istnieje kilka kategorii sieci neuronowych, a ten program wykorzystuje gęstą lub w pełni połączoną sieć neuronową : neurony w jednej warstwie otrzymują połączenia wejściowe z każdego neuronu w warstwie poprzedniej. Na przykład rysunek 2 ilustruje gęstą sieć neuronową składającą się z warstwy wejściowej, dwóch warstw ukrytych i warstwy wyjściowej:

Schemat architektury sieci: wejścia, 2 ukryte warstwy i wyjścia
Rysunek 2. Sieć neuronowa z funkcjami, ukrytymi warstwami i prognozami.

Kiedy model z rysunku 2 jest trenowany i karmiony nieznakowanym przykładem, daje on trzy prognozy: prawdopodobieństwo, że ten kwiat to dany gatunek tęczówki. Ta prognoza nazywa się wnioskiem . W tym przykładzie suma prognoz wyjściowych wynosi 1,0. Na rycinie 2 to przewidywanie rozkłada się jako: 0.02 dla Iris setosa , 0.95 dla Iris versicolor i 0.03 dla Iris virginica . Oznacza to, że model przewiduje - z 95% prawdopodobieństwem - że przykładowy kwiat bez etykiety to Iris versicolor .

Utwórz model za pomocą biblioteki Swift for TensorFlow Deep Learning

Biblioteka Swift for TensorFlow Deep Learning definiuje prymitywne warstwy i konwencje łączenia ich razem, co ułatwia tworzenie modeli i eksperymentowanie.

Model jest struct zgodną z Layer , co oznacza, że ​​definiuje callAsFunction(_:) , która odwzorowuje wejściowe Tensor na wyjściowe Tensor . Metoda callAsFunction(_:) często po prostu sekwencjonuje dane wejściowe przez podwarstwy. Zdefiniujmy IrisModel który sekwencjonuje dane wejściowe przez trzy Dense podwarstwy.

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

Funkcja aktywacji określa wyjściowy kształt każdego węzła w warstwie. Te nieliniowości są ważne - bez nich model byłby równoważny pojedynczej warstwie. Dostępnych jest wiele aktywacji, ale ReLU jest wspólna dla warstw ukrytych.

Idealna liczba ukrytych warstw i neuronów zależy od problemu i zbioru danych. Podobnie jak w przypadku wielu aspektów uczenia maszynowego, wybór najlepszego kształtu sieci neuronowej wymaga połączenia wiedzy i eksperymentów. Z reguły zwiększenie liczby ukrytych warstw i neuronów zazwyczaj tworzy mocniejszy model, który wymaga więcej danych, aby efektywnie trenować.

Korzystanie z modelu

Rzućmy okiem na to, co ten model robi z zestawem funkcji:

// Apply the model to a batch of features.
let firstTrainPredictions = model(firstTrainFeatures)
firstTrainPredictions[0..<5]
[[ -0.8188498,   1.5179185,  -0.3823631],
 [ -0.8939362,   1.8654141,   -0.431251],
 [  -0.877989,   1.6670536, -0.37928653],
 [-0.62468827,   1.7252572,  -0.3766331],
 [-0.68565977,   2.0301576, -0.44697276]]

Tutaj każdy przykład zwraca logit dla każdej klasy.

Aby przekonwertować te logity na prawdopodobieństwo dla każdej klasy, użyj funkcji softmax :

softmax(firstTrainPredictions[0..<5])
[[ 0.07754943,   0.8024614, 0.119989246],
 [ 0.05441314,   0.8591607,   0.0864262],
 [ 0.06497577,   0.8280362,  0.10698802],
 [ 0.07832983,   0.8212881, 0.100382075],
 [0.057515744,   0.8694633,   0.0730209]]

Biorąc argmax między klasami, otrzymujemy przewidywany indeks klas. Ale model nie został jeszcze wytrenowany, więc nie są to dobre prognozy.

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

Wytrenuj model

Szkolenie to etap uczenia maszynowego, na którym model jest stopniowo optymalizowany lub model uczy się zestawu danych. Celem jest zdobycie wystarczającej wiedzy o strukturze zbioru danych szkoleniowych, aby móc przewidywać niewidoczne dane. Jeśli dowiesz się zbyt wiele o zbiorze danych szkoleniowych, prognozy będą działać tylko dla danych, które widział i nie będzie można ich uogólniać. Ten problem nazywa się overfittingiem - to jak zapamiętywanie odpowiedzi zamiast zrozumienia, jak rozwiązać problem.

Problem klasyfikacji tęczówki jest przykładem nadzorowanego uczenia maszynowego : model jest uczony na podstawie przykładów zawierających etykiety. W uczeniu maszynowym bez nadzoru przykłady nie zawierają etykiet. Zamiast tego model zazwyczaj znajduje wzorce wśród funkcji.

Wybierz funkcję straty

Zarówno etap szkolenia, jak i oceny wymagają obliczenia utraty modelu. Mierzy to, jak odbiegają od prognoz modelu od żądanej etykiety, innymi słowy, jak źle działa model. Chcemy zminimalizować lub zoptymalizować tę wartość.

Nasz model obliczy stratę za pomocą funkcji softmaxCrossEntropy(logits:labels:) która pobiera prognozy prawdopodobieństwa klasy modelu i żądaną etykietę i zwraca średnią stratę w przykładach.

Obliczmy stratę dla obecnego nietrenowanego modelu:

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

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

Użyjmy optimizer aby wykonać pojedynczy krok zejścia gradientu. Najpierw obliczamy gradient straty w odniesieniu do modelu:

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

Następnie przekazujemy gradient, który właśnie obliczyliśmy, do optymalizatora, który odpowiednio aktualizuje zmienne różniczkowalne modelu:

optimizer.update(&model, along: grads)

Jeśli ponownie obliczymy stratę, powinna być mniejsza, ponieważ stopniowe opadanie gradientu (zwykle) zmniejsza stratę:

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

Pętla treningowa

Gdy wszystkie elementy są na miejscu, model jest gotowy do treningu! Pętla szkoleniowa dostarcza przykłady zestawów danych do modelu, aby pomóc mu w tworzeniu lepszych prognoz. Poniższy blok kodu konfiguruje te kroki szkoleniowe:

  1. Powtarzaj po każdej epoce . Epoka to jedno przejście przez zbiór danych.
  2. W ramach epoki iteruj po każdej partii w epoce uczenia
  3. Zbierz pakiet i chwyć jego cechy ( x ) i oznacz ( y ).
  4. Korzystając z funkcji zestawionej partii, wykonaj prognozę i porównaj ją z etykietą. Zmierz niedokładność prognozy i użyj jej do obliczenia strat i gradientów modelu.
  5. Użyj spadku gradientu, aby zaktualizować zmienne modelu.
  6. Śledź niektóre statystyki do wizualizacji.
  7. Powtórz dla każdej epoki.

Zmienna epochCount określa liczbę epochCount pętli w kolekcji zestawu danych. Wbrew intuicji, dłuższe trenowanie modelu nie gwarantuje lepszego modelu. epochCount to hiperparametr , który można dostroić. Wybór odpowiedniej liczby zwykle wymaga zarówno doświadczenia, jak i eksperymentów.

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.2097169, Accuracy: 0.4375
Epoch 50: Loss: 0.52431935, Accuracy: 0.875
Epoch 100: Loss: 0.3710712, Accuracy: 0.8958333
Epoch 150: Loss: 0.2160871, Accuracy: 0.9583333
Epoch 200: Loss: 0.14524944, Accuracy: 0.9791667
Epoch 250: Loss: 0.12487585, Accuracy: 0.9583333
Epoch 300: Loss: 0.122261345, Accuracy: 0.9791667
Epoch 350: Loss: 0.10571604, Accuracy: 0.9895833
Epoch 400: Loss: 0.09489065, Accuracy: 0.9791667
Epoch 450: Loss: 0.09057075, Accuracy: 0.9895833

Wizualizuj funkcję strat w czasie

Chociaż pomocne jest wydrukowanie postępów treningu modelu, często bardziej pomocne jest zobaczenie tego postępu. Możemy tworzyć podstawowe wykresy za pomocą modułu matplotlib Python.

Interpretacja tych wykresów wymaga pewnego doświadczenia, ale naprawdę chcesz zobaczyć, jak strata spada, a dokładność rośnie.

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

Zauważ, że osie Y wykresów nie są od zera.

Oceń skuteczność modelu

Teraz, gdy model jest wytrenowany, możemy uzyskać statystyki dotyczące jego wydajności.

Ocenianie oznacza określenie, jak skutecznie model dokonuje prognoz. Aby określić skuteczność modelu w klasyfikacji tęczówki, prześlij do modelu kilka pomiarów działek i płatków i poproś model, aby przewidział, jakie gatunki tęczówki reprezentują. Następnie porównaj przewidywania modelu z rzeczywistą etykietą. Na przykład model, który wybrał właściwy gatunek z połowy przykładów wejściowych, ma dokładność 0.5 . Rysunek 4 przedstawia nieco bardziej efektywny model, w którym 4 z 5 prognoz są poprawne z 80% dokładnością:

Przykładowe funkcje Etykieta Przewidywanie modelu
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
Rysunek 4. Klasyfikator tęczówki, który jest dokładny w 80%.

Skonfiguruj testowy zestaw danych

Ocena modelu jest podobna do uczenia modelu. Największą różnicą jest to, że przykłady pochodzą z oddzielnego zestawu testowego, a nie zestawu uczącego. Aby rzetelnie ocenić skuteczność modelu, przykłady użyte do oceny modelu muszą różnić się od przykładów użytych do trenowania modelu.

Konfiguracja zestawu danych testowych jest podobna do konfiguracji zestawu danych uczących. Pobierz zestaw testowy z 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)

Teraz załaduj go do tablicy es IrisBatch :

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

Oceń model w testowym zestawie danych

W przeciwieństwie do etapu uczenia model ocenia tylko jedną epokę danych testowych. W poniższej komórce kodu iterujemy po każdym przykładzie w zestawie testowym i porównujemy przewidywania modelu z rzeczywistą etykietą. Służy do pomiaru dokładności modelu w całym zestawie testowym.

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

Widzimy na przykład na pierwszej partii, że model jest zwykle poprawny:

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]

Użyj wytrenowanego modelu do tworzenia prognoz

Wytrenowaliśmy model i wykazaliśmy, że dobrze - ale nie doskonały - klasyfikuje gatunki tęczówki. Teraz użyjmy wytrenowanego modelu, aby dokonać pewnych prognoz na nieoznaczonych przykładach ; to znaczy na przykładach, które zawierają funkcje, ale nie zawierają etykiety.

W rzeczywistości nieoznakowane przykłady mogą pochodzić z wielu różnych źródeł, w tym aplikacji, plików CSV i źródeł danych. Na razie mamy zamiar ręcznie podać trzy nieoznakowane przykłady, aby przewidzieć ich etykiety. Przypomnijmy, numery etykiet są mapowane do nazwanej reprezentacji jako:

  • 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.9872727,  0.0124736205, 0.00025379486])
Example 1 prediction: Iris versicolor ([0.0026452888,     0.973102,  0.024252668])
Example 2 prediction: Iris virginica ([9.066205e-05,    0.0736953,    0.9262141])

Utwórz optymalizator

Optymalizator stosuje obliczone gradienty do zmiennych modelu, aby zminimalizować funkcję loss . Możesz myśleć o funkcji straty jako zakrzywionej powierzchni (patrz Rysunek 3) i chcemy znaleźć jej najniższy punkt, spacerując po okolicy. Gradienty wskazują kierunek najbardziej stromego wzniesienia - więc pojedziemy w przeciwnym kierunku i zejdziemy w dół. Poprzez iteracyjne obliczanie strat i gradientu dla każdej partii dostosujemy model podczas uczenia. Stopniowo model znajdzie najlepszą kombinację wag i odchylenia, aby zminimalizować straty. Im mniejsza strata, tym lepsze przewidywania modelu.

Algorytmy optymalizacji wizualizowane w czasie w przestrzeni 3D.
Rysunek 3. Algorytmy optymalizacji wizualizowane w czasie w przestrzeni 3D.
(Źródło: Stanford class CS231n , licencja MIT, zdjęcie: Alec Radford )

Swift for TensorFlow ma wiele algorytmów optymalizacji dostępnych do uczenia. Ten model wykorzystuje optymalizator SGD, który implementuje algorytm stochastycznego zejścia gradientowego (SGD). learningRate ustawia rozmiar kroku do wykonania dla każdej iteracji w dół wzgórza. Jest to hiperparametr , który zwykle dostosowujesz, aby osiągnąć lepsze wyniki.