От 0 до 1: внедрите приложение для логического вывода ИИ с помощью OpenPPL.

глубокое обучение GitHub
От 0 до 1: внедрите приложение для логического вывода ИИ с помощью OpenPPL.

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

окончательный эффект:Определите животное на картинке, загрузив фото кота (и собаки тоже) ?

жизненный опыт:OpenPPL – это механизм логического вывода, основанный на собственной высокопроизводительной библиотеке операторов. Он обеспечивает возможности развертывания моделей ИИ с несколькими серверами в облачной среде и поддерживает эффективное развертывание моделей глубокого обучения, таких как OpenMMLab.

⭐️ Приветственная звезда:GitHub.com/open PP out — универсальный...

Ниже приведен пример развертывания модели классификации изображений на платформе Linux x86.Подробно опишите процесс установки и использования OpenPPL и помогите учащимся внедрить службу рассуждений приложений искусственного интеллекта от 0 до 1.

Текст: @ Тянь Цзычень

Установить

1. Загрузите исходный код PPLNN.

git clone https://github.com/openppl-public/ppl.nn.git

2. Установите зависимости

Зависимости компиляции PPLNN следующие:

  • GCC >= 4.9 или LLVM/Clang >= 6.0
  • CMake >= 3.13
  • Git >= 2.7.0

Процедура классификации изображений, описанная в этой статье, также требует дополнительной установки OpenCV:

  • Для подходящей системы управления пакетами (например, Ubuntu/Debian):

    sudo apt install libopencv-dev

  • Для системы управления пакетами yum (например, CentOS):

    sudo yum install opencv opencv-devel

  • Или установите OpenCV из исходников

** Примечание: ** При компиляции он автоматически определяет, установлен ли OpenCV. Если он не установлен, рутина этой статьи не будет сгенерирована.

3. Скомпилируйте

cd ppl.nn
build.sh -DHPCC_USE_OPENMP=ON   # 不开启多线程的话,可以不加后面的-DHPCC_USE_OPENMP选项

После завершения компиляции в каталоге pplnn-build/samples/cpp/run_model/ будет создана процедура классификации изображений, которая может считывать файлы изображений и моделей и выводить результаты классификации.

Дополнительные описания компиляции см.building-from-source.md

Объяснение процедуры классификации изображений

Исходный код процедуры классификации изображений находится в Samples/cpp/run_model/classification.cpp, и в этом разделе объясняются его основные части.

1. Предварительная обработка изображения

Формат данных, читаемый OpenCV, — это формат BGR HWC uint8, а входной формат, требуемый моделью ONNX, — RGB NCHW fp32, и данные изображения необходимо преобразовать:

int32_t ImagePreprocess(const Mat& src_img, float* in_data) {
    const int32_t height = src_img.rows;
    const int32_t width = src_img.cols;
    const int32_t channels = src_img.channels();

    // 将颜色空间从 BGR/GRAY 转换到 RGB
    Mat rgb_img;
    if (channels == 3) {
        cvtColor(src_img, rgb_img, COLOR_BGR2RGB);
    } else if (channels == 1) {
        cvtColor(src_img, rgb_img, COLOR_GRAY2RGB);
    } else {
        fprintf(stderr, "unsupported channel num: %d\n", channels);
        return -1;
    }

    // 将 HWC 格式的三通道分开
    vector<Mat> rgb_channels(3);
    split(rgb_img, rgb_channels);

    // 这里构造 cv::Mat 时,直接用 in_data 为 cv::Mat 提供数据空间。这样当 cv::Mat 变化时,数据会直接写到 in_data 内
    Mat r_channel_fp32(height, width, CV_32FC1, in_data + 0 * height * width);
    Mat g_channel_fp32(height, width, CV_32FC1, in_data + 1 * height * width);
    Mat b_channel_fp32(height, width, CV_32FC1, in_data + 2 * height * width);
    vector<Mat> rgb_channels_fp32{r_channel_fp32, g_channel_fp32, b_channel_fp32};

    // 将 uint8 数据转换为 fp32,并减均值除标准差,y = (x - mean) / std
    const float mean[3] = {0, 0, 0}; // 根据数据集和训练参数调整均值和方差
    const float std[3] = {255.0f, 255.0f, 255.0f};
    for (uint32_t i = 0; i < rgb_channels.size(); ++i) {
        rgb_channels[i].convertTo(rgb_channels_fp32[i], CV_32FC1, 1.0f / std[i], -mean[i] / std[i]);
    }

    return 0;
}

2. Создайте построитель среды выполнения из модели ONNX.

Во-первых, вам нужно создать и зарегистрировать движок, который вы хотите использовать.Каждый движок соответствует серверной части вывода.В настоящее время поддерживаются x86 и CUDA. В этой статье используется только движок x86:

auto x86_engine = X86EngineFactory::Create(); // 创建 x86 engine

    // 注册所有想使用的 engine
    vector<unique_ptr<Engine>> engines;
    vector<Engine*> engine_ptrs;
    engines.emplace_back(unique_ptr<Engine>(x86_engine));
    engine_ptrs.emplace_back(engines[0].get());

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

auto builder = unique_ptr<ONNXRuntimeBuilder>(
        ONNXRuntimeBuilderFactory::Create(ONNX_model_path, engine_ptrs.data(), engine_ptrs.size()));

Дополнительное примечание. Уровень инфраструктуры PPLNN поддерживает смешанный вывод нескольких разнородных устройств. Можно зарегистрировать множество различных движков, и платформа автоматически разделит граф вычислений на несколько подграфов и назначит различные движки для вычислений.

3. Создайте среду выполнения

Используйте runtime_options, чтобы настроить параметры среды выполнения, например, установить для поля mm_policy значение MM_LESS_MEMORY (режим экономии памяти):

RuntimeOptions runtime_options;
    runtime_options.mm_policy = MM_LESS_MEMORY; // 使用省内存模式

Создайте экземпляр среды выполнения, сгенерированный на шаге построителя среды выполнения:

unique_ptr<Runtime> runtime;
    runtime.reset(builder->CreateRuntime(runtime_options));

Построитель среды выполнения может создавать несколько экземпляров среды выполнения. Эти экземпляры среды выполнения совместно используют постоянные данные (веса и т. д.) и сетевую топологию, что снижает нагрузку на память.

4. Установите сетевые входные данные

Сначала получите входной тензор среды выполнения через интерфейс GetInputTensor():

auto input_tensor = runtime->GetInputTensor(0); // 分类网络仅有一个输入

Измените форму входного тензора и перераспределите память тензора:

const std::vector<int64_t> input_shape{1, channels, height, width};
    input_tensor->GetShape().Reshape(input_shape); // 即使 ONNX 模型里已经将输入尺寸固定,PPLNN 仍会动态调整输入尺寸
    auto status = input_tensor->ReallocBuffer();   // 当调用了 Reshape 后,必须调用此接口重新分配内存

В отличие от ONNX Runtime, даже если размер ввода фиксирован в модели ONNX, PPLNN может динамически регулировать размер ввода сети (но убедитесь, что размер ввода является разумным).

Предварительно обработанный выше тип данных in_data — fp32, а формат — NDARRAY (четырехмерные данные NDARRAY эквивалентны NCHW), который определяет описание формата пользовательских входных данных:

TensorShape src_desc = input_tensor->GetShape();
    src_desc.SetDataType(DATATYPE_FLOAT32);
    src_desc.SetDataFormat(DATAFORMAT_NDARRAY); // 对于4维数据来说,NDARRAY 等同于 NCHW

Наконец, вызывается интерфейс ConvertFromHost() для преобразования данных in_data в формат, требуемый input_tensor для завершения заполнения данных:

status = input_tensor->ConvertFromHost(in_data, src_desc);

5. Вывод модели

status = runtime->Run(); // 执行网络推理

6. Получить выходные данные сети

Получите выходной тензор среды выполнения через интерфейс GetOutputTensor():

auto output_tensor = runtime->GetOutputTensor(0); // 分类网络仅有一个输出

Выделите пространство данных для хранения сетевого вывода:

uint64_t output_size = output_tensor->GetShape().GetElementsExcludingPadding();
    std::vector<float> output_data_(output_size);
    float* output_data = output_data_.data();

Как и в случае с входными данными, вам необходимо сначала определить желаемое описание выходного формата:

TensorShape dst_desc = output_tensor->GetShape();
    dst_desc.SetDataType(DATATYPE_FLOAT32);
    dst_desc.SetDataFormat(DATAFORMAT_NDARRAY); // 对于1维数据而言,NDARRAY 等同于 vector

Вызовите интерфейс ConvertToHost(), чтобы преобразовать данные output_tensor в формат, описанный dst_desc, чтобы получить выходные данные:

status = output_tensor->ConvertToHost(output_data, dst_desc);

7. Проанализируйте вывод

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

int32_t GetClassificationResult(const float* scores, const int32_t size) {
    vector<pair<float, int>> pairs(size);
    for (int32_t i = 0; i < size; i++) {
        pairs[i] = make_pair(scores[i], i);
    }

    auto cmp_func = [](const pair<float, int>& p0, const pair<float, int>& p1) -> bool {
        return p0.first > p1.first;
    };

    const int32_t top_k = 5;
    nth_element(pairs.begin(), pairs.begin() + top_k, pairs.end(), cmp_func); // get top K results & sort
    sort(pairs.begin(), pairs.begin() + top_k, cmp_func);

    printf("top %d results:\n", top_k);
    for (int32_t i = 0; i < top_k; ++i) {
        printf("%dth: %-10f %-10d %s\n", i + 1, pairs[i].first, pairs[i].second, imagenet_labels_tab[pairs[i].second]);
    }

    return 0;
}

бегать

1. Подготовьте модель ONNX

Мы подготовили классификационную модель mnasnet0_5.onnx под тестами/тестовыми данными, которую можно использовать для тестирования.

Дополнительные модели ONNX можно получить следующими способами:

Версия opset модели ONNX Model Zoo ниже, вы можете преобразовать opset в 11 через convert_onnx_opset_version.py в инструментах:

python convert_onnx_opset_version.py --input_model input_model.onnx --output_model output_model.onnx --output_opset 11

Для получения подробной информации о преобразовании opset см.:onnx-model-opset-convert-guide.md

2. Подготовьте тестовые изображения

Тестовые изображения могут быть любого формата. Мы подготовили cat0.png (снимок головы нашего кота) и cat1.jpg (изображение проверочного набора ImageNet) в тестах/тестовых данных:

Картинки любого размера могут работать нормально.Если вы хотите изменить размер до 224 x 224, вы можете изменить следующие переменные в программе:

const bool resize_input = false; // 想要resize的话,修改为true即可

3. Тестовый сервис логического вывода

бегать

pplnn-build/samples/cpp/run_model/classification <image_file> <onnx_model_file>

После завершения вывода будет получен следующий вывод:

image preprocess succeed!
[INFO][2021-07-23 17:29:31.341][simple_graph_partitioner.cc:107] total partition(s) of graph[torch-jit-export]: 1.
successfully create runtime builder!
successfully build runtime!
successfully set input data to tensor [input]!
successfully run network!
successfully get outputs!
top 5 results:
1th: 3.416199   284        n02123597 Siamese cat, Siamese
2th: 3.049764   285        n02124075 Egyptian cat
3th: 2.989676   606        n03584829 iron, smoothing iron
4th: 2.812310   283        n02123394 Persian cat
5th: 2.796991   749        n04033901 quill, quill pen

Нетрудно заметить, что эта программа правильно определяет, что мой владелец кошки — настоящая кошка (>^ω^

На данный момент установка OpenPPL и вывод модели классификации изображений завершены.

Кроме того, в каталоге pplnn-build/tools находится исполняемый файл pplnn, который может выполнять произвольный вывод модели, дамп выходных данных, бенчмарк и другие операции.Конкретное использование можно просмотреть с помощью параметра --help. Вы можете внести изменения на основе этого примера, чтобы лучше ознакомиться с использованием OpenPPL.

Группа связи QQ: 627853444, пароль для входа в группу OpenPPL