Первый опыт глубокого фреймворка MXNet/Gluon

глубокое обучение Python алгоритм сервер
Первый опыт глубокого фреймворка MXNet/Gluon

MXNet: A flexible and efficient library for deep learning.

Это МХНетОфициальный сайтВведение: «MXNet — это гибкая и эффективная библиотека глубокого обучения».

MXNet — одна из трех основных сред глубокого обучения:

  • TensorFlow: поддерживается Google, его упрощенная версияKeras;
  • PyTorch: поддержка Facebook, его промышленная версияCaffe2;
  • MXNet: нейтральный проект-инкубатор Apache, также выбранный AWS в качестве официальной платформы DL;

Преимущество MXNet в том, что один из его разработчиков, Ли Му, является китайцем ??, и имеет языковые преимущества (китайский) в продвижении MXNet, что способствует обучению отечественных разработчиков. В то же время рекомендуется, чтобы Ли Му записалУчебное видео,очень хороший.

Высокоуровневый интерфейс MXNet — Gluon. Gluon поддерживает как гибкие динамические графики, так и эффективные статические графики. Он не только сохраняет простоту использования динамических графиков, но также обладает высокой производительностью статических графиков. Это также представлено на официальном Веб-сайт.flexibleиefficientисточник. В то же время MXNet также имеет большое количество передовых алгоритмов в академических кругах, что удобно для переноса в промышленность. Я надеюсь, что команда MXNet продолжит усердно работать и будет в авангарде конкурса фреймворков глубокого обучения.

Поэтому мастерMXNet/Gluonнеобходимый.

В этой статье используется глубокое обучениеМногослойный персептрон(Многослойные персептроны) является основой алгоритма, а набор данных выбираетсяMNIST, в котором представлены технические детали MXNet.

этой статьиисходный код: https://GitHub.com/spike king/gluon-tutorial


набор данных

В виртуальной среде (Virtual Env) просто используйте pip для установки MXNet:

pip install mxnet

Если скорость загрузки низкая, рекомендуется использоватьАли КлаудИсточник пипи:

-i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com

MNISTЭто известная библиотека распознавания рукописных цифр, которая содержит рукописные символы из 10 цифр, например от 0 до 9, а размер изображения представляет собой изображение в градациях серого 28 * 28. Цель состоит в том, чтобы определить правильные цифры в соответствии с изображением.

Библиотека MNIST упакована в MXNet какМНИСТ класс, данные хранятся в.mxnet/datasets/mnistсередина. Если загрузка данных MNIST идет медленно, вы можетеMNISTСкачайте его с официального сайта и поместите в папку mnist. В классе МНИСТ:

  • параметрtrain: Является ли это обучающими данными, где true — это обучающие данные, а false — тестовые данные;
  • параметрtransform: функции преобразования данных, лямбда-выражения, преобразование данных и меток в указанные типы данных;

Исходный код:

# 参数train
if self._train:
    data, label = self._train_data, self._train_label
else:
    data, label = self._test_data, self._test_label
    
# 参数transform
if self._transform is not None:
    return self._transform(self._data[idx], self._label[idx])
return self._data[idx], self._label[idx]

В MXNet класс загрузки данных инкапсулирован какКласс DataLoader, режим итератора, итеративно выводит тот же набор выборок, что и количество пакетов. В DataLoader,

  • параметрdataset: источник данных, например MNIST;
  • параметрbatch_size: количество пакетов в процессе обучения и указанное количество образцов выводятся в итерациях;
  • параметрshuffle: Перетасовать ли карты, то есть перетасовать данные, эта операция вообще требуется во время обучения.

Тест итератора, количество выходных выборок (1-е измерение) каждый раз совпадает с указанным количеством пакетов:

for data, label in train_data:
    print(data.shape)  # (64L, 28L, 28L, 1L)
    print(label.shape)  # (64L,)
    break

существуетload_data()В методе выводятся обучающие и тестовые данные, тип данных — число с плавающей запятой от 0 до 1 (значение серого, деленное на 255), а тип метки — также число с плавающей запятой.

Выполнение:

def load_data(self):
    def transform(data, label):
        return data.astype(np.float32) / 255., label.astype(np.float32)
    train_data = DataLoader(MNIST(train=True, transform=transform),
                            self.batch_size, shuffle=True)
    test_data = DataLoader(MNIST(train=False, transform=transform),
                           self.batch_size, shuffle=False)
    return train_data, test_data

Модель

Сетевая модель использует стиль Gluon в MXNet:

  1. СоздайтеSequential()Sequence, Sequential — это контейнер всех операционных единиц;
  2. Добавьте полностью подключенный блок Dense, единицы параметра — это количество выходных единиц, а активация параметра — функция активации;
  3. Параметры инициализации:
    • init — источник данных, класс Normal — нормальное распределение, а sigma — стандартное отклонение нормального распределения;
    • ctx — это контекст, который означает, что обновление параметра в обучении использует CPU или GPU, например, mx.cpu();

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

выполнить:

def model(self):
    num_hidden = 64
    net = gluon.nn.Sequential()
    with net.name_scope():
        net.add(gluon.nn.Dense(units=num_hidden, activation="relu"))
        net.add(gluon.nn.Dense(units=num_hidden, activation="relu"))
        net.add(gluon.nn.Dense(units=self.num_outputs))

    net.collect_params().initialize(init=mx.init.Normal(sigma=.1), ctx=self.model_ctx)
    print(net)  # 展示模型
    return net

в,net.name_scope()Автоматически добавляйте имена к единицам действия в Sequential.

Визуализация модели

Используйте print() напрямую для печати структуры модели, напримерprint(net):

Sequential(
  (0): Dense(None -> 64, Activation(relu))
  (1): Dense(None -> 64, Activation(relu))
  (2): Dense(None -> 10, linear)
)

или, используя немного более сложныйjupyterЧтобы нарисовать модель, установите пакет jupyter (Python 2.x):

pip install ipython==5.3.0
pip install jupyter==1.0.0

Запустите службу jupyter и получите доступhttp://localhost:8888/:

jupyter notebook

новыйPython 2файл для написания кода для рисования сети. Стиль кода состоит в том, чтобы добавить «логику рисования» после существующей модели, вызываяplot_network()рисовать. Если вы замените класс Sequential классом HybridSequential, эффективность рисования можно повысить, а эффект рисования не изменится без замены.

Сетевая модель и логика рисования:

import mxnet as mx
from mxnet import gluon

num_hidden = 64
net = gluon.nn.HybridSequential()
with net.name_scope():
    net.add(gluon.nn.Dense(num_hidden, activation="relu"))
    net.add(gluon.nn.Dense(num_hidden, activation="relu"))
    net.add(gluon.nn.Dense(10))

# 绘制逻辑
net.hybridize()
net.collect_params().initialize()
x = mx.sym.var('data')
sym = net(x)
mx.viz.plot_network(sym)

Изображение эффекта:

模型


тренироваться

Перед обучением загрузите данные и создайте сеть.

train_data, test_data = self.load_data()  # 训练和测试数据
net = self.model()  # 模型

Далее создадим интерфейс для кросс-энтропииsoftmax_cross_entropy, создать трейнерtrainer.

К параметрам трейнера относятся: параметры в сети, оптимизатор, параметры оптимизатора и т.д.

epochs = 10
smoothing_constant = .01
num_examples = 60000

softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()  # 交叉熵
trainer = gluon.Trainer(params=net.collect_params(),
                        optimizer='sgd',
                        optimizer_params={'learning_rate': smoothing_constant})

Эпоха цикла для обучения сетевой модели:

  1. от итератораtrain_dataисточник, получить пакетные данные и метки:
  2. Среда выполнения ctx указанных данных и метки — ЦП или ГП, а расширенные данные — 1 строка;
  3. Автоматический расчет градиентаautograd.record(), сеть прогнозирует данные, выводит результат и вычисляет потерю перекрестной энтропии;
  4. Для получения обратного распространения потерь шагами настройки тренера являются количество пакетов;
  5. существуетcumulative_loss, потери каждой партии накапливаются, и рассчитывается общая потеря;
  6. После обучения эпохи рассчитайте точность тестовых и обучающих данных;

Продолжайте цикл, пока выполнение не завершит все эпохи.

Осуществление обучения:

for e in range(epochs):
    cumulative_loss = 0  # 累积的
    for i, (data, label) in enumerate(train_data):
        data = data.as_in_context(self.model_ctx).reshape((-1, 784))  # 数据
        label = label.as_in_context(self.model_ctx)  # 标签

        with autograd.record():  # 梯度
            output = net(data)  # 输出
            loss = softmax_cross_entropy(output, label)  # 输入和输出计算loss

        loss.backward()  # 反向传播
        trainer.step(data.shape[0])  # 设置trainer的step
        cumulative_loss += nd.sum(loss).asscalar()  # 计算全部损失

    test_accuracy = self.__evaluate_accuracy(test_data, net)
    train_accuracy = self.__evaluate_accuracy(train_data, net)
    print("Epoch %s. Loss: %s, Train_acc %s, Test_acc %s" %
          (e, cumulative_loss / num_examples, train_accuracy, test_accuracy))

в интерфейсе предсказанияevaluate_accuracy()середина:

  1. Создайте класс Accuracy для статистической точности;
  2. Итеративно выводить пакеты данных и меток;
  3. Прогнозировать вероятность различных категорий данных и выбирать максимальную вероятность (argmax) в качестве категории;
  4. пройти черезacc.update()точность обновления;

Наконец, верните значение точности, которое является вторым измерением accacc[1], а первая размерность соотв.acc[0]это название акк.

def __evaluate_accuracy(self, data_itertor, net):
    acc = mx.metric.Accuracy()  # 准确率
    for i, (data, label) in enumerate(data_iterator):
        data = data.as_in_context(self.model_ctx).reshape((-1, 784))
        label = label.as_in_context(self.model_ctx)
        output = net(data)  # 预测结果
        predictions = nd.argmax(output, axis=1)  # 类别
        acc.update(preds=predictions, labels=label)  # 更新概率和标签
    return acc.get()[1]  # 第1维是数据名称,第2维是概率

Эффект:

Epoch 0. Loss: 1.2743850797812144, Train_acc 0.846283333333, Test_acc 0.8509
Epoch 1. Loss: 0.46071574948628746, Train_acc 0.884366666667, Test_acc 0.8892
Epoch 2. Loss: 0.37149955205917357, Train_acc 0.896466666667, Test_acc 0.9008
Epoch 3. Loss: 0.3313815038919449, Train_acc 0.908366666667, Test_acc 0.9099
Epoch 4. Loss: 0.30456133014361064, Train_acc 0.915966666667, Test_acc 0.9172
Epoch 5. Loss: 0.2827877395868301, Train_acc 0.919466666667, Test_acc 0.9214
Epoch 6. Loss: 0.2653073514064153, Train_acc 0.925433333333, Test_acc 0.9289
Epoch 7. Loss: 0.25018166739145914, Train_acc 0.92965, Test_acc 0.9313
Epoch 8. Loss: 0.23669789231618246, Train_acc 0.933816666667, Test_acc 0.9358
Epoch 9. Loss: 0.22473177655935286, Train_acc 0.934716666667, Test_acc 0.9337

GPU

Для глубокого обучения использование графического процессора может ускорить процесс обучения сети, а MXNet также поддерживает использование графического процессора для обучения сети.

Чтобы проверить Cuda-версию сервера, команда:nvcc --version, который используется для определения версии графического процессора для загрузки MXNet.

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2016 NVIDIA Corporation
Built on Sun_Sep__4_22:14:01_CDT_2016
Cuda compilation tools, release 8.0, V8.0.44

Тогда текущая версия Cuda сервера — 8.0.

Преобразование MXNet из версии ЦП вверсия графического процессора, удалитьmxnet,Установитьmxnet-cu80.

pip uninstall mxnet
pip install mxnet-cu80

После установки версии GPU выполните следующий код в консоли Python, чтобы убедиться, что можно использовать библиотеку GPU MXNet.

>>> import mxnet as mx
>>> a = mx.nd.ones((2, 3), mx.gpu())
>>> b = a * 2 + 1
>>> b.asnumpy()
array([[ 3.,  3.,  3.],
       [ 3.,  3.,  3.]], dtype=float32)

Чтобы проверить количество графических процессоров, команда:nvidia-smi:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.26                 Driver Version: 375.26                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  TITAN X (Pascal)    Off  | 0000:02:00.0     Off |                  N/A |
| 28%   49C    P2    84W / 250W |  12126MiB / 12189MiB |     25%      Default |
+-------------------------------+----------------------+----------------------+
|   1  TITAN X (Pascal)    Off  | 0000:03:00.0     Off |                  N/A |
| 24%   39C    P2    57W / 250W |  12126MiB / 12189MiB |     33%      Default |
+-------------------------------+----------------------+----------------------+
|   2  TITAN X (Pascal)    Off  | 0000:83:00.0     Off |                  N/A |
| 25%   41C    P2    58W / 250W |  12126MiB / 12189MiB |     37%      Default |
+-------------------------------+----------------------+----------------------+
|   3  TITAN X (Pascal)    Off  | 0000:84:00.0     Off |                  N/A |
| 23%   31C    P2    53W / 250W |  11952MiB / 12189MiB |      2%      Default |
+-------------------------------+----------------------+----------------------+

Тогда количество графических процессоров на текущем сервере равно 4.

Установите параметр environment ctx в список графических процессоров, т.е.[mx.gpu(0), mx.gpu(1), ...].

GPU_COUNT = 4
ctx = [mx.gpu(i) for i in range(GPU_COUNT)]

Используйте графический процессор для инициализации параметров параметра initialize() в сети, а затем создайте тренера.

net = self.model()  # 模型
net.collect_params().initialize(init=mx.init.Normal(sigma=.1), ctx=ctx)

smoothing_constant = .01
trainer = gluon.Trainer(params=net.collect_params(),
                        optimizer='sgd',
                        optimizer_params={'learning_rate': smoothing_constant})

Пройдите через 10 эпох, чтобы обучить модель,train_dataиvalid_data— это итератор, который каждый раз выводит пакетный набор образцов. существуетtrain_batch(), передать пакет пакетных данных, список среды графического процессора ctx, сетевую сеть и тренер по очереди; вvalid_batch(), аналогично обучению, за исключением того, что трейнер не проходит.

epochs = 10
for e in range(epochs):
    start = time()
    for batch in train_data:
        self.train_batch(batch, ctx, net, trainer)
    nd.waitall()  # 等待所有异步的任务都终止
    print('Epoch %d, training time = %.1f sec' % (e, time() - start))
    correct, num = 0.0, 0.0
    for batch in valid_data:
        correct += self.valid_batch(batch, ctx, net)
        num += batch[0].shape[0]
    print('\tvalidation accuracy = %.4f' % (correct / num))

Подробный анализ методов пакетного обученияtrain_batch():

  1. Входной пакет представляет собой набор данных и меток с индексом 0 для данных и индексом 1 для меток.
  2. В зависимости от количества графических процессоров, разделенных данных данных и меток меток каждый графический процессор соответствует разным данным;
  3. Для каждой группы данных и меток обратное распространение назад() соответственно обновляет параметры сети сети;
  4. Установите шаг тренера трейнера как количество партийbatch_size;

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

Реализация выглядит следующим образом:

@staticmethod
def train_batch(batch, ctx, net, trainer):
    # split the data batch and load them on GPUs
    data = gluon.utils.split_and_load(batch[0], ctx)  # 列表
    label = gluon.utils.split_and_load(batch[1], ctx)  # 列表
    # compute gradient
    GluonFirst.forward_backward(net, data, label)
    # update parameters
    trainer.step(batch[0].shape[0])
    
@staticmethod
def forward_backward(net, data, label):
    loss = gluon.loss.SoftmaxCrossEntropyLoss()
    with autograd.record():
        losses = [loss(net(X), Y) for X, Y in zip(data, label)]  # loss列表
    for l in losses:  # 每个loss反向传播
        l.backward()

Метод проверки конкретной аналитической партииvalid_batch():

  1. Запустите все данные проверки в одном графическом процессоре, а именно в ctx[0];
  2. Сетевая сеть прогнозирует вероятность категории данных данных, а затем преобразует ее в определенную категорию argmax();
  3. Объедините все правильно предсказанные выборки, чтобы получить общее количество правильных выборок;

Реализация выглядит следующим образом:

@staticmethod
def valid_batch(batch, ctx, net):
    data = batch[0].as_in_context(ctx[0])
    pred = nd.argmax(net(data), axis=1)
    return nd.sum(pred == batch[1].as_in_context(ctx[0])).asscalar()

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

Чтобы обучить модель графического процессора, вам необходимо подключиться к удаленному серверу и загрузить проект. Рекомендуется, если транспорт Git недоступенRsyncOSX, очень удобный инструмент синхронизации файлов:

RsyncOSX

На удаленном сервере установите библиотеку зависимостей проекта в виртуальную среду, обратите внимание, что вам нужно использовать версию MXNet для графического процессора.mxnet-cu80, а затем выполните обучение модели.

Вот вывод модели для версии GPU:

Epoch 5, training time = 13.7 sec
	validation accuracy = 0.9277
Epoch 6, training time = 13.9 sec
	validation accuracy = 0.9284
Epoch 7, training time = 13.8 sec
	validation accuracy = 0.9335
Epoch 8, training time = 13.7 sec
	validation accuracy = 0.9379
Epoch 9, training time = 14.4 sec
	validation accuracy = 0.9402

При появлении следующего предупреждения ⚠️:

only 4 out of 12 GPU pairs are enabled direct access. 
It may affect the performance. You can set MXNET_ENABLE_GPU_P2P=0 to turn it off

закрытиеMXNET_ENABLE_GPU_P2PТо есть на нормальный тренировочный процесс это никак не влияет.

export MXNET_ENABLE_GPU_P2P=0

ужеMXNet/GluonЗавершен инженерный проект MXNet. Детали реализации MXNet анализируются по четырем частям набора данных, модели, обучения и графического процессора. Каждая ссылка MXNet очень продумана, похожа на другие структуры и проста в использовании. Хотя пример небольшой, он «укомплектован пятью внутренними органами» и играет роль привлечения других к продолжению изучения фреймворка MXNet.

OK, that's all! Enjoy it!