Документация Engee

Руководство. Простой многослойный перцептрон

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

Для выполнения этого примера нам понадобятся следующие пакеты.

using Flux, Statistics
using Flux.Data: DataLoader
using Flux: onehotbatch, onecold, logitcrossentropy, throttle, params
using Base.Iterators: repeated
using CUDA
using MLDatasets
if has_cuda()		# Проверка доступности CUDA
    @info "CUDA is on"
    CUDA.allowscalar(false)
end

We set default values for learning rate, batch size, epochs, and the usage of a GPU (if available) for our model:

Base.@kwdef mutable struct Args
    rate::Float64 = 3e-4    # скорость обучения
    batchsize::Int = 1024   # размер пакета
    epochs::Int = 10        # количество эпох
    device::Function = gpu  # задан как gpu, если gpu доступен
end

Если в нашей локальной системе есть GPU, Flux использует его для вычисления потерь и обновления весов и отклонений при обучении модели.

Данные

Мы создадим функцию getdata для загрузки обучающего и тестового наборов данных MNIST из MLDatasets и подготовим их к процессу обучения. Кроме того, мы создадим мини-пакеты наборов данных, загрузив их в объект DataLoader.

function getdata(args)
    ENV["DATADEPS_ALWAYS_ACCEPT"] = "true"

    # Загрузка набора данных
    xtrain, ytrain = MLDatasets.MNIST.traindata(Float32)
    xtest, ytest = MLDatasets.MNIST.testdata(Float32)

    # Изменение формы данных, чтобы свести каждое изображение в линейный массив
    xtrain = Flux.flatten(xtrain)
    xtest = Flux.flatten(xtest)

    # Прямое кодирование меток
    ytrain, ytest = onehotbatch(ytrain, 0:9), onehotbatch(ytest, 0:9)

    # Пакетирование
    train_data = DataLoader((xtrain, ytrain), batchsize=args.batchsize, shuffle=true)
    test_data = DataLoader((xtest, ytest), batchsize=args.batchsize)

    return train_data, test_data
end

getdata выполняет следующие действия.

  • Загружает набор данных MNIST. Загружает тензоры обучающего и тестового наборов. Обучающие данные имеют форму 28x28x60000, а тестовые данные — 28X28X10000.

  • Изменяет форму обучающих и тестовых данных. Использует функцию flatten для преобразования обучающего набора данных в массив 784x60000, а тестового набора данных — в массив 784x10000. Обратите внимание, что мы изменили форму данных, чтобы передать их в качестве аргументов для входного слоя модели (простой MLP ожидает вектор в качестве входных данных).

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

  • Создает пакеты данных. Создает два объекта DataLoader (для обучения и тестирования), которые обрабатывают мини-пакеты данных размером 1024 (как определено выше). Мы создаем эти два объекта, чтобы при обучении модели пропустить через функцию потерь сразу весь набор данных. Кроме того, он перемешивает точки данных во время каждой итерации (shuffle=true).

Модель

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

  • Входной. Он имеет 784 перцептрона (размер изображения MNIST составляет 28x28). Мы формируем плоскую структуру из обучающих и тестовых данных, чтобы передать их в качестве аргументов этому слою.

  • Скрытый. Он имеет 32 перцептрона, использующих функцию активации relu.

  • Выходной. Он имеет 10 перцептронов, которые выводят прогноз модели или вероятность того, что цифра имеет значение от 0 до 9.

Определим модель с помощью функции build_model.

function build_model(; imgsize=(28,28,1), nclasses=10)
    return Chain(
 	    Dense(prod(imgsize), 32, relu),
            Dense(32, nclasses))
end

Обратите внимание, что мы используем функции Dense для того, чтобы модель была плотно (или полностью) связанной, и Chain для формирования цепочки вычислений трех слоев.

Функции потерь

Теперь определим функцию loss_all. В качестве аргументов она ожидает объект DataLoader и функцию model, которую мы определили выше. Обратите внимание, что эта функция итерирует объект dataloader в мини-пакетах и использует функцию logitcrossentropy для вычисления разницы между предсказанными и фактическими значениями.

function loss_all(dataloader, model)
    l = 0f0
    for (x,y) in dataloader
        l += logitcrossentropy(model(x), y)
    end
    l/length(dataloader)
end

Кроме того, мы определим функцию (accuracy), чтобы сообщать о точности модели в процессе обучения. Чтобы вычислить точность, нужно декодировать вывод модели с помощью функции onecold.

function accuracy(data_loader, model)
    acc = 0
    for (x,y) in data_loader
        acc += sum(onecold(cpu(model(x))) .== onecold(cpu(y)))*1 / size(x,2)
    end
    acc/length(data_loader)
end

Обучение модели

Наконец, создадим функцию train, которая вызывает определенные нами функции и обучает модель.

function train(; kws...)
    # Инициализация параметров модели
    args = Args(; kws...)

    # Загрузка данных
    train_data,test_data = getdata(args)

    # Построение модели
    m = build_model()
    train_data = args.device.(train_data)
    test_data = args.device.(test_data)
    m = args.device(m)
    loss(x,y) = logitcrossentropy(m(x), y)

    ## Обучение
    evalcb = () -> @show(loss_all(train_data, m))
    opt = Adam(args.rate)

    for epoch in 1:args.epochs
        @info "Epoch $epoch"
        Flux.train!(loss, params(m), train_data, opt, cb = evalcb)
    end

    @show accuracy(train_data, m)

    @show accuracy(test_data, m)
end

train выполняет следующие действия.

  • Инициализирует параметры модели. Создает объект args, содержащий значения по умолчанию для обучения модели.

  • Загружает обучающие и тестовые данные. Вызывает функцию getdata, определенную выше.

  • Строит модель. Стоит модель, загружает обучающие и тестовые наборы данных и модель в GPU (если GPU доступен).

  • Обучает модель. Определяет функцию обратного вызова evalcb для отображения значения функции loss_all в ходе процесса обучения. Затем функция задает link:@ref Flux.Optimise.Adam[Adam] в качестве оптимизатора для обучения модели. Наконец, функция выполняет процесс обучения в течение 10 эпох (как определено в объекте args) и отображает значение accuracy для обучающих и тестовых данных.

Полную версию этого примера можно посмотреть здесь: Simple multi-layer perceptron - model-zoo.

Ресурсы

Впервые опубликовано на сайте fluxml.ai 26 февраля 2021 г. Авторы: Адарш Кумар (Adarsh Kumar), Майк Джей Иннес (Mike J Innes), Эндрю Динхобль (Andrew Dinhobl), Джерри Линг (Jerry Ling), natema, Чжан Шитянь (Zhang Shitian), Лилиана Бадильо (Liliana Badillo), Дхайрия Ганди (Dhairya Gandhi)