Платформа вывода глубокого обучения 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 можно получить следующими способами:
- Модели ONNX можно экспортировать из OpenMMLab/PyTorch:model-convert-guide.md
- Получите модель из модельного зоопарка ONNX:github.com/onnx/models
Версия 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