Сквозное обучение MegEngine: позвольте ИИ понять вас и защитить вас лучше

искусственный интеллект глубокое обучение

Автор: Ленни | Стажер Megvii Technology MegEngine

Приложение для покупок часто «подсаживается», распознавание отпечатков пальцев становится все более стабильным и точным каждый раз, камера красоты делает ваше любимое изображение P одним щелчком мыши ...

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

Как в таком случае алгоритмы ИИ могут более точно понимать предпочтения пользователей и обеспечивать безопасность пользовательских данных? Интуитивная идея состоит в том, чтобы напрямую обучать модель на мобильном телефоне, что не только позволяет избежать риска утечки, вызванного передачей данных, но и постоянно повышает производительность модели.MegEngineВы можете тренироваться на графическом процессоре и делать логические выводы на мобильном устройстве. Сможете ли вы тренироваться на мобильном устройстве, если вы объедините их? Ответ положительный.

Итак, теперь давайте посмотрим, как выполнить терминальное обучение в MegEngine~

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

Оглядываясь назад на то, что мы делали при построении процесса обучения в таких фреймворках, как Pytorch и Tensorflow, мы можем обнаружить, что к основным из них относятся:

  1. построить модель;
  2. Добавить Loss и Optimizer;
  3. импортировать набор данных;
  4. Установите гиперпараметры, такие как скорость обучения, количество эпох обучения и обучение.

Построить модель

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

图1

Из структуры модели LeNet легко понять, что нам нужно вызвать 2 оператора свертки, 2 оператора объединения, 1 оператор Флаттена, 2 оператора матричного умножения и несколько четырех арифметических операторов.

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

图2

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

Поскольку код здесь довольно многословен, вот упрощенный пример кода. Видно, что на самом деле он мало чем отличается от вызова общего интерфейса Python, и даже соответствует взаимно-однозначному, напримерopr::Convolutionсоответствоватьnn.Conv2d,opr::MatrixMulсоответствоватьnn.Linear, только потому, что функции языка C++ отличаются от Python, будут некоторые различия в написании.

SymbolVar symbol_input =
           opr::Host2DeviceCopy::make(*graph, m_input); // 初始化输入数据

SymbolVar symbol_conv =
        opr::Convolution::make(symbol_input, symbol_conv_weight, conv_param); // symbol_weighs[0] 即我们提前设置好的卷积 filter 权重
symbol_conv = opr::relu(symbol_conv + symbol_conv_bias); //加偏置之后激活
SymbolVar symbol_maxpool =
        opr::Pooling::make(symbol_conv, pooling_param)
                .reshape({batchsize, fc_shape[0]}); //池化之后进行展平

SymbolVar symbol_fc =
        opr::MatrixMul::make(symbol_maxpool, symbol_fc_weight) +
        symbol_fc_bias;
symbol_fc1= opr::relu(symbol_fc); //通过矩阵乘运算构造全连接层

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

##Потеря звонков и Оптимизатор

Теперь MegEngine упаковал Loss и Optimizer на уровне C ++. Давайте возьмем в качестве примера оптимизатор кросс-энтропийных потерь и SGD в обучении набора данных Mnist.

В MegEngine все рассуждения и обучение фактически выполняются на вычислительном графе, в то время как Loss и Optimizer по существу инкапсулируют часть задачи построения вычислительных графов, чтобы пользователи могли напрямую вызывать без повторения «строительных колес». Например, среднеквадратическая ошибка, с которой мы лучше всего знакомы, на самом деле вызывает оператор вычитания один раз, а затем оператор возведения в степень.

MSE=(yy')2MSE\,\,=\,\,\слева (г-у'' \справа) ^2

Поняв это, нам нужно только перейти к предыдущему шагу и вызвать Loss API и сплайсинг после вывода нашей модели, Код очень прост и очень похож на обучение в Pytorch.

CrossEntopyLoss loss_func; // 先定义一个损失函数的实例,这里选取交叉熵损失
SymbolVar symbol_loss = loss_func(symbol_fc, symbol_label); // 将模型输出与标签作为输入,调用损失函数

В это время мы получаемsymbol_lossнаша потеря во время обучения.

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

SGD optimizer = SGD(0.01f, 5e-4f, .9f); //实例化 SGD 优化器并设置参数
 
SymbolVarArray symbol_updates =
        optimizer.make_multiple(symbol_weights, symbol_grads, graph); // 将 Optimizer 插入到计算图中

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

импортировать набор данных

Поскольку параметры модели определяются нами вручную, мы должны заметить, что проблема заключается в том, как преобразовать наш набор данных в данные, участвующие в расчете вычислительного графа?

Конечно, MegEngine подготовил способ унаследовать интерфейс и реализовать один изget_itemиsizeметод и введите экземпляр этого класса в DataLoader, после чего преобразование набора данных может быть завершено ~

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

class IDataView {
public:
    virtual DataPair get_item(int idx) = 0;
    virtual size_t size() = 0;
    virtual ~IDataView() = default;
};

О предыдущем примере много говорить нечего, здесь только как наследовать интерфейс и получить DataLoader.Если вам интересно увидеть конкретную реализацию, вы можете обратить внимание на MegEngine~

class MnistDataset : public IDataView {
public:
    MnistDataset(std::string dir_name); // 初始化数据集,指定数据集存放路径
    void load_data(Mode mode, std::string dir_name); //读取 Mnist 数据集,存到 dataset 列表中。
    DataPair get_item(int idx); // 实现接口
    size_t size(); //实现接口
 
protected:
    std::vector<DataPair> dataset;
};

// 实例化上面定义的数据集类
auto train_dataset = std::make_shared<MnistDataset>(dataset_dir);
// 用这个实例来获取对应的 DataLoader
auto train_dataloader =
        DataLoader(train_dataset, batchsize);

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

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

func = graph->compile(); // 编译计算图
 
for (int epoch = 0; epoch < epochs; epoch++) {
    for (size_t i = 0; i < train_dataloader.size(); i++) {
        data = train_dataloader.next(); // 从 DataLoader 中获取数据
 
        func->execute(); // 执行计算图
    }
}

С помощью моего собственного пробного метода (x) я обнаружил, что обучение на терминале может достигать той же точности, что и обучение с Pytorch и обучающим интерфейсом Python MegEngine~ Здесь наша проверка прошла успешно!

Увидев это, я думаю, вы уже поняли, как выполнять сквозное обучение в MegEngine, так каковы же интерфейсы Loss и Optimizer?

Пакет Loss и Optimizer

Иногда мы сталкиваемся с ситуациями, когда нам нужно инкапсулировать нужные нам Loss и Optimizer.В настоящее время более важно понимать API Loss и Optimizer.

Интерфейс Loss очень прост, и его можно обобщить следующим образом:

class ILoss {
public:
    virtual mgb::SymbolVar operator()(mgb::SymbolVar symbol_pred,
                                      mgb::SymbolVar symol_label) = 0;
    virtual ~ILoss() = default;
};

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

Интерфейс оптимизатора также очень лаконичен, его можно свести к следующему коду:

class IOptimizer {
public:
    virtual mgb::SymbolVarArray make_multiple(
            mgb::SymbolVarArray symbol_weights,
            mgb::SymbolVarArray symbol_grads,
            std::shared_ptr<mgb::cg::ComputingGraph> graph) = 0;
    virtual mgb::SymbolVar make(
            mgb::SymbolVar symbol_weight, mgb::SymbolVar symbol_grad,
            std::shared_ptr<mgb::cg::ComputingGraph> graph) = 0;
    virtual ~IOptimizer() = default;
};
 
class Optimizer : public IOptimizer {
public:
    mgb::SymbolVarArray make_multiple(
            mgb::SymbolVarArray symbol_weights,
            mgb::SymbolVarArray symbol_grads,
            std::shared_ptr<mgb::cg::ComputingGraph> graph); // 注意这里并不是纯虚函数
    virtual mgb::SymbolVar make(
            mgb::SymbolVar symbol_weight, mgb::SymbolVar symbol_grad,
            std::shared_ptr<mgb::cg::ComputingGraph> graph) = 0;
    virtual ~Optimizer() = default;
};

Подобно Loss, здесь мы также вводим вычислительный узел, а затем выводим соответствующий вычислительный узел. Стоит отметить, что Оптимизатор разделен на две части, одна представляет собой чистый интерфейсIOptimizer, другая часть — абстрактный класс, наследующий этот интерфейсOptimizer. На самом деле, во многих случаях мы привыкли использовать массив или список для хранения наших параметров и полученных градиентов.В настоящее время из-за ограничений статического языка эту ситуацию нельзя напрямую объединить в случае одного ввода, но фактическое, пока мы достигаемMakeинтерфейс, случай, когда ввод является массивом, также будет разрешен. Однако, учитывая идею о том, что интерфейс и класс должны быть разделены, здесь происходит разделение, и он становится интерфейсом, абстрактным классом, а абстрактный класс включает в себя вход массива (make_multipleинтерфейс) реализация по умолчанию.

Если вам нужно добавить пользовательский Loss или Optimizer, вам нужно только наследовать соответствующий интерфейс или абстрактный класс и реализовать его.

Например, реализация среднеквадратичной ошибки MSE:

mgb::SymbolVar MSELoss::operator()(
        mgb::SymbolVar symbol_pred, mgb::SymbolVar symol_label) {
    return opr::pow(symbol_pred - symol_label, symbol_pred.make_scalar(2));
}

Резюме и перспективы

Увидев это, вы, может быть, испытаете любопытство, может быть, вам станет противно...

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

Основные проблемы текущего обучения MegEngine и возможные улучшения на следующем этапе:

  • Процесс построения модели в настоящее время относительно примитивен и может быть дополнительно инкапсулирован для получения аналогичныхnn.moduleмодуль.
  • Иногда у вас на руках уже есть определенный файл веса с информацией о графике расчета, и вы не хотите заново строить график расчета, а непосредственно считываете существующий график расчета и вставляете его в процесс обучения, что может предоставить аналогичный API
  • Чтение данных на стороне C++ будет более проблематичным

Вы можете попробовать использовать MegEngine для создания приложения для непрерывного обучения. Вы также можете указать на недостатки текущего среднего обучения MegEngine, чтобы мы могли его улучшить. Вы также можете отправить PR для решения проблемы. вместе~

MegEngine cpp Training Example

Гитхаб:Megvii Tianyuan MegEngine

Добро пожаловать в группу технической биржи MegEngine QQ: 1029741705