Пошаговое руководство по обучению модели

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub

В этом руководстве представлен Swift для TensorFlow путем создания модели машинного обучения, которая классифицирует цветы ириса по видам. Он использует Swift для TensorFlow для:

  1. Построить модель,
  2. Обучите эту модель на примере данных и
  3. Используйте модель, чтобы делать прогнозы о неизвестных данных.

Программирование TensorFlow

В этом руководстве используются эти высокоуровневые концепции Swift для TensorFlow:

  • Импортируйте данные с помощью Epochs API.
  • Создавайте модели, используя абстракции Swift.
  • Используйте библиотеки Python, используя совместимость Swift с Python, когда чистые библиотеки Swift недоступны.

Этот учебник структурирован так же, как и многие программы TensorFlow:

  1. Импортируйте и проанализируйте наборы данных.
  2. Выберите тип модели.
  3. Обучите модель.
  4. Оцените эффективность модели.
  5. Используйте обученную модель, чтобы делать прогнозы.

Программа установки

Настроить импорт

Импортируйте TensorFlow и некоторые полезные модули 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)
}

Проблема классификации радужной оболочки

Представьте, что вы ботаник, ищущий автоматизированный способ классификации каждого найденного вами цветка ириса. Машинное обучение предоставляет множество алгоритмов для статистической классификации цветов. Например, сложная программа машинного обучения может классифицировать цветы на основе фотографий. Наши амбиции более скромны — мы собираемся классифицировать цветы ириса на основе измерений длины и ширины чашелистиков и лепестков .

Род Iris включает около 300 видов, но наша программа классифицирует только следующие три:

  • Ирис сетоса
  • Ирис виргинский
  • Ирис разноцветный
Сравнение геометрии лепестков трех видов ирисов: Iris setosa, Iris virginica и Iris versicolor.
Рисунок 1. Iris setosa (автор Radomil , CC BY-SA 3.0), Iris versicolor (автор Dlanglois , CC BY-SA 3.0) и Iris virginica (автор Frank Mayfield , CC BY-SA 2.0).

К счастью, кто-то уже создал набор данных из 120 цветков ириса с размерами чашелистиков и лепестков. Это классический набор данных, который популярен для начинающих задач классификации машинного обучения.

Импорт и анализ обучающего набора данных

Загрузите файл набора данных и преобразуйте его в структуру, которую может использовать эта программа Swift.

Скачать набор данных

Загрузите файл набора обучающих данных с 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)

Проверить данные

Этот набор данных, iris_training.csv , представляет собой обычный текстовый файл, в котором хранятся табличные данные, отформатированные как значения, разделенные запятыми (CSV). Давайте посмотрим на первые 5 записей.

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

В этом представлении набора данных обратите внимание на следующее:

  1. Первая строка — это заголовок, содержащий информацию о наборе данных:
    • Всего 120 примеров. Каждый пример имеет четыре функции и одно из трех возможных имен меток.
  2. Последующие строки представляют собой записи данных, по одному примеру на строку, где:
    • Первые четыре поля — это характеристики : это характеристики примера. Здесь поля содержат числа с плавающей запятой, представляющие размеры цветов.
    • Последний столбец — это метка : это значение, которое мы хотим предсказать. Для этого набора данных это целочисленное значение 0, 1 или 2, которое соответствует названию цветка.

Давайте запишем это в коде:

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

Каждая метка связана с именем строки (например, «setosa»), но машинное обучение обычно использует числовые значения. Номера меток сопоставляются с именованным представлением, например:

  • 0 : Ирис щетинковидный
  • 1 : Ирис разноцветный
  • 2 : Ирис виргинский

Дополнительные сведения о функциях и метках см. в разделе «Терминология машинного обучения» ускоренного курса по машинному обучению .

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

Создайте набор данных с помощью Epochs API.

Swift для Epochs API TensorFlow — это высокоуровневый API для чтения данных и преобразования их в форму, используемую для обучения.

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

Поскольку наборы данных, которые мы загрузили, имеют формат CSV, давайте напишем функцию для загрузки данных в виде списка объектов 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)}

    }

Теперь мы можем использовать функцию загрузки CSV для загрузки набора обучающих данных и создания объекта TrainingEpochs .

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

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

Объект TrainingEpochs представляет собой бесконечную последовательность эпох. Каждая эпоха содержит IrisBatch es. Давайте посмотрим на первый элемент первой эпохи.

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]

Обратите внимание, что функции для первых примеров batchSize группируются (или firstTrainFeatures , а метки для первых примеров batchSize группируются в firstTrainLabels .

Вы можете начать видеть некоторые кластеры, нарисовав несколько функций из пакета, используя Python matplotlib:

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.

Выберите тип модели

Почему модель?

Модель — это связь между функциями и меткой. Для задачи классификации радужной оболочки модель определяет взаимосвязь между размерами чашелистиков и лепестков и предсказанными видами радужной оболочки. Некоторые простые модели можно описать с помощью нескольких строк алгебры, но сложные модели машинного обучения имеют большое количество параметров, которые сложно обобщить.

Могли бы вы определить взаимосвязь между четырьмя признаками и видом ириса без использования машинного обучения? То есть, могли бы вы использовать традиционные приемы программирования (например, множество условных операторов) для создания модели? Возможно, если вы проанализируете набор данных достаточно долго, чтобы определить отношения между размерами лепестков и чашелистиков к конкретному виду. И это становится трудным, а может быть, и невозможным, для более сложных наборов данных. Хороший подход к машинному обучению определяет модель для вас . Если вы добавите достаточно репрезентативных примеров в правильный тип модели машинного обучения, программа выяснит взаимосвязи за вас.

Выберите модель

Нам нужно выбрать тип модели для обучения. Моделей много, и чтобы выбрать хорошую, нужен опыт. В этом руководстве нейронная сеть используется для решения задачи классификации радужной оболочки. Нейронные сети могут находить сложные отношения между функциями и меткой. Это высокоструктурированный график, организованный в один или несколько скрытых слоев . Каждый скрытый слой состоит из одного или нескольких нейронов . Существует несколько категорий нейронных сетей, и в этой программе используется плотная или полносвязная нейронная сеть : нейроны в одном слое получают входные соединения от каждого нейрона в предыдущем слое. Например, на рисунке 2 показана плотная нейронная сеть, состоящая из входного слоя, двух скрытых слоев и выходного слоя:

Схема сетевой архитектуры: входы, 2 скрытых слоя и выходы.
Рис. 2. Нейронная сеть с признаками, скрытыми слоями и прогнозами.

Когда модель на рис. 2 обучена и получает немаркированный пример, она дает три прогноза: вероятность того, что этот цветок является данным видом ириса. Это предсказание называется выводом . В этом примере сумма выходных прогнозов равна 1,0. На Рисунке 2 этот прогноз представлен следующим образом: 0.02 для Iris setosa , 0.95 для Iris versicolor и 0.03 для Iris virginica . Это означает, что модель предсказывает — с вероятностью 95 % — что немаркированный пример цветка — это Iris versicolor .

Создайте модель с помощью библиотеки глубокого обучения Swift для TensorFlow.

Библиотека глубокого обучения Swift для TensorFlow определяет примитивные слои и правила их объединения, что упрощает создание моделей и экспериментирование.

Модель — это struct , соответствующая Layer , что означает, что она определяет callAsFunction(_:) , который сопоставляет входные Tensor s с выходными Tensor s. Метод callAsFunction(_:) часто просто упорядочивает входные данные через подслои. Давайте определим IrisModel , который упорядочивает ввод через три подслоя 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()

Функция активации определяет выходную форму каждого узла в слое. Эти нелинейности важны — без них модель была бы эквивалентна одному слою. Есть много доступных активаций, но ReLU является общим для скрытых слоев.

Идеальное количество скрытых слоев и нейронов зависит от задачи и набора данных. Как и многие аспекты машинного обучения, выбор наилучшей формы нейронной сети требует сочетания знаний и экспериментов. Как правило, увеличение количества скрытых слоев и нейронов обычно создает более мощную модель, для эффективного обучения которой требуется больше данных.

Использование модели

Давайте быстро посмотрим, что эта модель делает с пакетом функций:

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

Здесь каждый пример возвращает логит для каждого класса.

Чтобы преобразовать эти логиты в вероятность для каждого класса, используйте функцию 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]]

Взятие argmax по классам дает нам прогнозируемый индекс класса. Но модель еще не обучена, так что это не очень хорошие прогнозы.

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]

Обучите модель

Обучение — это этап машинного обучения, когда модель постепенно оптимизируется, или модель изучает набор данных. Цель состоит в том, чтобы узнать достаточно о структуре обучающего набора данных, чтобы делать прогнозы относительно невидимых данных. Если вы слишком много узнаете об обучающем наборе данных, то прогнозы будут работать только для тех данных, которые он видел, и их нельзя будет обобщить. Эта проблема называется переоснащением — это похоже на запоминание ответов вместо понимания того, как решить проблему.

Задача классификации радужной оболочки — пример машинного обучения с учителем: модель обучается на примерах, содержащих метки. В неконтролируемом машинном обучении примеры не содержат меток. Вместо этого модель обычно находит закономерности среди признаков.

Выберите функцию потерь

На этапах обучения и оценки необходимо рассчитать потери модели. Это измеряет, насколько прогнозы модели отличаются от желаемой метки, другими словами, насколько плохо работает модель. Мы хотим минимизировать или оптимизировать это значение.

Наша модель рассчитает свои потери с помощью функции softmaxCrossEntropy(logits:labels:) , которая принимает прогнозы вероятности класса модели и желаемую метку и возвращает средние потери по примерам.

Рассчитаем потери для текущей необученной модели:

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

Создать оптимизатор

Оптимизатор применяет вычисленные градиенты к переменным модели, чтобы минимизировать функцию loss . Вы можете думать о функции потерь как о изогнутой поверхности (см. рис. 3), и мы хотим найти ее самую низкую точку, прогуливаясь по ней. Градиенты указывают в направлении наибольшего подъема, поэтому мы поедем в противоположном направлении и спустимся с холма. Итеративно вычисляя потери и градиент для каждой партии, мы настроим модель во время обучения. Постепенно модель найдет наилучшее сочетание весов и смещений, чтобы минимизировать потери. И чем меньше потери, тем лучше прогнозы модели.

Алгоритмы оптимизации визуализируются во времени в трехмерном пространстве.
Рис. 3. Алгоритмы оптимизации, визуализированные во времени в трехмерном пространстве.
(Источник: Стэнфордский класс CS231n , лицензия Массачусетского технологического института, изображение предоставлено Алеком Рэдфордом )

Swift для TensorFlow имеет множество алгоритмов оптимизации, доступных для обучения. В этой модели используется оптимизатор SGD, реализующий алгоритм стохастического градиентного спуска (SGD). learningRate устанавливает размер шага для каждой итерации вниз по склону. Это гиперпараметр , который вы обычно настраиваете для достижения лучших результатов.

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

Давайте используем optimizer , чтобы сделать один шаг градиентного спуска. Во-первых, мы вычисляем градиент потерь по отношению к модели:

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

Затем мы передаем градиент, который мы только что вычислили, оптимизатору, который соответствующим образом обновляет дифференцируемые переменные модели:

optimizer.update(&model, along: grads)

Если мы снова посчитаем потери, они должны быть меньше, потому что шаги градиентного спуска (обычно) уменьшают потери:

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

Тренировочный цикл

Когда все детали на месте, модель готова к обучению! Цикл обучения передает примеры набора данных в модель, чтобы помочь ей делать более точные прогнозы. Следующий блок кода устанавливает эти шаги обучения:

  1. Итерация по каждой эпохе . Эпоха — это один проход через набор данных.
  2. В течение эпохи перебирать каждую партию в эпоху обучения.
  3. Соберите пакет и возьмите его функции ( x ) и метку ( y ).
  4. Используя функции сопоставленной партии, сделайте прогноз и сравните его с этикеткой. Измерьте неточность прогноза и используйте ее для расчета потерь и градиентов модели.
  5. Используйте градиентный спуск, чтобы обновить переменные модели.
  6. Отслеживайте некоторые статистические данные для визуализации.
  7. Повторите для каждой эпохи.

Переменная epochCount — это количество циклов по коллекции наборов данных. Вопреки здравому смыслу, более длительное обучение модели не гарантирует получение лучшей модели. epochCount — это гиперпараметр , который вы можете настроить. Выбор правильного числа обычно требует как опыта, так и экспериментов.

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

Визуализируйте функцию потерь во времени

Хотя полезно распечатать ход обучения модели, часто полезнее видеть этот прогресс. Мы можем создавать простые диаграммы, используя модуль Python matplotlib .

Интерпретация этих графиков требует некоторого опыта, но вы действительно хотите, чтобы потери уменьшались, а точность росла.

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.

Обратите внимание, что оси Y графиков не отсчитываются от нуля.

Оцените эффективность модели

Теперь, когда модель обучена, мы можем получить некоторую статистику ее производительности.

Оценка означает определение того, насколько эффективно модель делает прогнозы. Чтобы определить эффективность модели при классификации радужной оболочки, передайте в модель некоторые измерения чашелистиков и лепестков и попросите модель предсказать, какой вид радужной оболочки они представляют. Затем сравните прогноз модели с фактической меткой. Например, модель, выбравшая правильный вид в половине входных примеров, имеет точность 0.5 . На рис. 4 показана немного более эффективная модель, обеспечивающая верность 4 из 5 прогнозов с точностью 80 %:

Примеры функций Этикетка Предсказание модели
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
Рисунок 4. Классификатор радужной оболочки с точностью 80%.

Настройте тестовый набор данных

Оценка модели аналогична обучению модели. Самая большая разница в том, что примеры взяты из отдельного набора тестов, а не из обучающего набора. Чтобы справедливо оценить эффективность модели, примеры, используемые для оценки модели, должны отличаться от примеров, используемых для обучения модели.

Настройка тестового набора данных аналогична настройке обучающего набора данных. Загрузите набор тестов с 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)

Теперь загрузите его в массив IrisBatch es:

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

Оцените модель на тестовом наборе данных

В отличие от этапа обучения, модель оценивает только одну эпоху тестовых данных. В следующей ячейке кода мы перебираем каждый пример в тестовом наборе и сравниваем прогноз модели с фактической меткой. Это используется для измерения точности модели по всему набору тестов.

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

Мы можем видеть на первой партии, например, модель обычно правильная:

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]

Используйте обученную модель, чтобы делать прогнозы

Мы обучили модель и продемонстрировали, что она хорошо — но не идеально — классифицирует виды ирисов. Теперь давайте воспользуемся обученной моделью, чтобы сделать некоторые прогнозы на неразмеченных примерах ; то есть на примерах, которые содержат функции, но не метку.

В реальной жизни немаркированные примеры могут поступать из множества разных источников, включая приложения, CSV-файлы и потоки данных. Сейчас мы собираемся вручную предоставить три немаркированных примера, чтобы предсказать их метки. Напомним, номера меток сопоставляются с именованным представлением следующим образом:

  • 0 : Ирис щетинковидный
  • 1 : Ирис разноцветный
  • 2 : Ирис виргинский
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])