В этом посте мы расскажем об использовании библиотеки ускорения аналитики данных Intel Data Analytics Acceleration Library (Intel DAAL) с языком программирования Go для пакетной, интерактивной и распределенной обработки.
На основе Go построены самые современные инфраструктурные проекты, в том числе Kubernetes, Docker, Consul, etcd и многие другие. Go становится предпочитаемым языком для DevOps, веб-серверов и микрослужб. Этот язык прост в изучении, удобен в развертывании, он очень быстрый, для него есть прекрасный набор инструментов разработки.
Обработка и анализ данных используются в бизнесе все чаще, поэтому приходится внедрять ресурсоемкие вычислительные алгоритмы на всех уровнях инфраструктуры компаний, в том числе и на тех уровнях, где используется язык Go. Возникает закономерный вопрос: как интегрировать такие решения как машинное обучение, распределенное преобразование данных и интерактивный анализ данных в системы на основе Go?
Один из способов надежной, быстрой и масштабируемой обработки данных в Go состоит в использовании библиотеки Intel Data Analytics Acceleration Library (Intel DAAL) в программах Go. Эта библиотека предоставляет алгоритмы пакетной, интерактивной и распределенной обработки для целого ряда полезных задач.
Поскольку Go прекрасно взаимодействует с C/C++, можно реализовать эту функциональность в программах Go без особых затруднений. При этом мы существенно выиграем по скорости: эти библиотеки уже оптимизированы для архитектуры Intel. Как показано здесь, в определенных операциях, например при анализе основных компонентов, Intel DAAL может работать в семь раз быстрее, чем Spark с MLlib. Это очень здорово! Было бы очень кстати задействовать такую мощь в приложениях Go.
Установка Intel DAAL
Библиотека Intel DAAL доступна в виде открытого исходного кода, для ее установки выполните следующие инструкции. На моем компьютере с Linux это было невероятно просто.
- Загрузка исходного кода.
- Запуск сценария установки.
- Настройка необходимых переменных среды (для этого также можно использовать предоставляемый сценарий shell).
Перед интеграцией Intel DAAL в любую программу Go имеет смысл убедиться, что все правильно работает. Для этого можно воспользоваться различными руководствами по началу работы в документации Intel DAAL. В частности, в этих руководствах предоставляется пример приложения Intel DAAL для алгоритма разложения Холецкого. Ниже мы попробуем создать его на языке Go. Исходный пример алгоритма разложения Холецкого на языке C++ выглядит так.
/****************************************************************************
! Copyright(C) 2014-2017 Intel Corporation. All Rights Reserved.
!
! The source code, information and material ("Material") contained herein is
! owned by Intel Corporation or its suppliers or licensors, and title to such
! Material remains with Intel Corporation or its suppliers or licensors. The
! Material contains proprietary information of Intel or its suppliers and
! licensors. The Material is protected by worldwide copyright laws and treaty
! provisions. No part of the Material may be used, copied, reproduced,
! modified, published, uploaded, posted, transmitted, distributed or disclosed
! in any way without Intel's prior express written permission. No license
! under any patent, copyright or other intellectual property rights in the
! Material is granted to or conferred upon you, either expressly, by
! implication, inducement, estoppel or otherwise. Any license under such
! intellectual property rights must be express and approved by Intel in
! writing.
!
! *Third Party trademarks are the property of their respective owners.
!
! Unless otherwise agreed by Intel in writing, you may not remove or alter
! this notice or any other notice embedded in Materials by Intel or Intel's
! suppliers or licensors in any way.
!
!****************************************************************************
! Content:
! Cholesky decomposition sample program.
!***************************************************************************/
#include "daal.h"
#include <iostream>
using namespace daal;
using namespace daal::algorithms;
using namespace daal::data_management;
using namespace daal::services;
const size_t dimension = 3;
double inputArray[dimension *dimension] =
{
1.0, 2.0, 4.0,
2.0, 13.0, 23.0,
4.0, 23.0, 77.0
};
int main(int argc, char *argv[])
{
/* Create input numeric table from array */
SharedPtr inputData = SharedPtr(new Matrix(dimension, dimension, inputArray));
/* Create the algorithm object for computation of the Cholesky decomposition using the default method */
cholesky::Batch<> algorithm;
/* Set input for the algorithm */
algorithm.input.set(cholesky::data, inputData);
/* Compute Cholesky decomposition */
algorithm.compute();
/* Get pointer to Cholesky factor */
SharedPtr<Matrix > factor =
staticPointerCast<Matrix, NumericTable>(algorithm.getResult()->get(cholesky::choleskyFactor));
/* Print the first element of the Cholesky factor */
std::cout << "The first element of the Cholesky factor: "
<< (*factor)[0][0];
return 0;
}
Попробуйте скомпилировать и запустить этот код, чтобы убедиться в успешной установке Intel DAAL. Кроме того, так вы получите представление о том, что мы будем делать на языке Go. Любые вопросы и проблемы, связанные с установкой Intel DAAL, можно обсудить на форуме Intel DAAL (лично для меня этот форум оказался исключительно полезным ресурсом, когда я начал пробовать работать с Intel DAAL).
Использование Intel DAAL в программах на языке Go
Если речь идет об использовании библиотеки Intel DAAL в программах на Go, у нас есть несколько возможных вариантов.
- Непосредственный вызов библиотеки Intel DAAL из программы Go через функцию-оболочку.
- Создание многократно используемой библиотеки с определенной функциональностью Intel DAAL.
Ниже я демонстрирую оба этих подхода. Весь исходный код доступен здесь. Это лишь один пример. Было бы здорово, если бы со временем удалось добавить и другие примеры программ Go с Intel DAAL в это хранилище. При экспериментах, пожалуйста, отправляйте запросы. Мне было бы очень интересно увидеть, что вы создадите.
Если вы раньше не использовали Go, то перед продолжением работы с этой статьей рекомендую получше ознакомиться с этим языком. Отметим, что Go даже не нужно устанавливать на локальный компьютер, чтобы начать его изучать. Можно воспользоваться ознакомлением с Go в Интернете и сайтом Go Playground, а уже потом, когда будете готовы, можно установить Go на локальный компьютер.
Вызов библиотеки Intel DAAL непосредственно из Go
Go предоставляет инструмент под названием cgo, который дает возможность создавать пакеты Go, вызывающие код C. В этом случае мы будем использовать cgo для организации взаимодействия нашей программы Go с библиотекой Intel DAAL.
Кстати, использование cgo с программами Go сопряжено с определенными ограничениями, которые достаточно подробно обсуждаются в Интернете (в частности, см. обсуждение Дейва Чени (Dave Cheney) или эту статью компании Cockroach Labs). Принимая решение об использовании cgo, всегда принимайте во внимания эти ограничения или хотя бы просто помните о них. В данном случае мы готовы примириться с ограничениями cgo, чтобы воспользоваться преимуществами оптимизированной распределенной библиотеки Intel DAAL: эти ограничения с лихвой окупятся повышением производительности в определенных случаях с высокой вычислительной нагрузкой или с большими объемами данных.
Для интеграции алгоритма разложения Холецкого из Intel DAAL в программу Go потребуется создать следующую структуру папок (в директории $GOPATH).
cholesky`
├── cholesky.go`
├── cholesky.hxx`
└── cholesky.cxx`
Файл cholesky.go — это наша программа Go, которая будет использовать алгоритм разложения Холецкого из библиотеки Intel DAAL. Файлы cholesky.cxx и cholesky.hxx — это определения/объявления C++, включающие Intel DAAL и указывающие компилятору cgo, какую функциональность Intel DAAL мы будем использовать. Рассмотрим каждый из них.
Сначала файл *.cxx.
#include "cholesky.hxx"
#include "daal.h"
#include <iostream>
using namespace daal;
using namespace daal::algorithms;
using namespace daal::data_management;
using namespace daal::services;
int choleskyDecompose(int dimension, double inputArray[]) {
/* Create input numeric table from array */
SharedPtr inputData = SharedPtr(new Matrix(dimension, dimension, inputArray));
/* Create the algorithm object for computation of the Cholesky decomposition using the default method */
cholesky::Batch<> algorithm;
/* Set input for the algorithm */
algorithm.input.set(cholesky::data, inputData);
/* Compute Cholesky decomposition */
algorithm.compute();
/* Get pointer to Cholesky factor */
SharedPtr<Matrix > factor =
staticPointerCast<Matrix, NumericTable>(algorithm.getResult()->get(cholesky::choleskyFactor));
/* Return the first element of the Cholesky factor */
return (*factor)[0][0];
}
Теперь файл *.hxx.
#ifndef CHOLESKY_H
#define CHOLESKY_H
// __cplusplus gets defined when a C++ compiler processes the file.
// extern "C" is needed so the C++ compiler exports the symbols w/out name issues.
#ifdef __cplusplus
extern "C" {
#endif
int choleskyDecompose(int dimension, double inputArray[]);
#ifdef __cplusplus
}
#endif
#endif
Эти файлы определяют функцию-оболочку choleskyDecompose на C++, использующую алгоритм разложения Холецкого Intel DAAL для разложения входной матрицы и вывода первого элемента множителя Холецкого (как в примере, приведенном в руководстве по началу работы с Intel DAAL). Обратите внимание, что в этом случае наши входные данные — это массив длины размерности матрицы (т. е. матрица 3 х 3 соответствует входному массиву длиной 9). Нужно включить extern “C” в файл *.hxx. В этом случае компилятор C++ будет «знать», что нужно экспортировать соответствующие имена, определенные в наших файлах C++.
После определения функции-оболочки разложения Холецкого в файлах *.cxx и *.hxx можно вызвать эту функцию напрямую из Go. cholesky.go выглядит так.
package main
// #cgo CXXFLAGS: -I$DAALINCLUDE
// #cgo LDFLAGS: -L$DAALLIB -ldaal_core -ldaal_sequential -lpthread -lm
// #include "cholesky.hxx"
import "C"
import (
"fmt"
"unsafe"
)
func main() {
// Define the input matrix as an array.
inputArray := [9]float64{
1.0, 2.0, 4.0,
2.0, 13.0, 23.0,
4.0, 23.0, 77.0,
}
// Get the first Cholesky decomposition factor.
data := (*C.double)(unsafe.Pointer(&inputArray[0]))
factor := C.choleskyDecompose(3, data)
// Output the first Cholesky dcomposition factor to stdout.
fmt.Printf("The first Cholesky decomp. factor is: %dn", factor)
}
Давайте разберем этот процесс поэтапно, чтобы понять, что же тут происходит. Сначала нужно сообщить программе Go, что нужно использовать cgo при компиляции программы, а также нужно компилировать с определенными флагами.
// #cgo CXXFLAGS: -I$DAALINCLUDE
// #cgo LDFLAGS: -L$DAALLIB -ldaal_core -ldaal_sequential -lpthread -lm
// #include "cholesky.hxx"
import "C"
Чтобы использовать, требуется import “C”: это псевдоупаковка, сообщающая об использовании cgo. Если непосредственно перед командой импорта «C» стоит комментарий, то этот комментарий (он называется преамбулой) будет использован в качестве заголовка при компиляции компонентов C++ этого пакета.
С помощью CXXFLAGS и LDFLAGS можно указать флаги компиляции и компоновки, которые cgo должен использовать при компиляции, а нашу функцию C++ можно добавить, используя // #include «cholesky.hxx”. Для компиляции этого примера я использовал Linux и gcc, что и указано выше с помощью соответствующих флагов. Впрочем, можно следовать этому руководству, чтобы определить, как скомпоновать приложение с Intel DAAL.
После этого можно писать код Go таким же образом, как для любой другой программы, и обращаться к нашей функции-оболочке как C.choleskyDecompose().
// Define the input matrix as an array.
inputArray := [9]float64{
1.0, 2.0, 4.0,
2.0, 13.0, 23.0,
4.0, 23.0, 77.0,
}
// Get the first Cholesky decomposition factor.
data := (*C.double)(unsafe.Pointer(&inputArray[0]))
factor := C.choleskyDecompose(3, data)
// Output the first Cholesky dcomposition factor to stdout.
fmt.Printf("The first Cholesky decomp. factor is: %dn", factor)
Уникальная особенность в этом случае (она обусловлена использованием cgo) состоит в том, что нужно преобразовать указатель на первый элемент нашего среза float64 в небезопасный указатель, который затем можно будет явным образом преобразовать в указатель *C.double (совместимый с C++) на нашу функцию choleskyDecompose. Упаковка в небезопасный указатель позволяет нам обойти ограничения безопасности типов, действующие в программах Go.
Отлично! Итак, у нас есть программа Go, которая вызвала алгоритм разложения Холецкого из библиотеки Intel DAAL. Теперь пора собрать и запустить эту программу. Это можно сделать обычным образом с помощью go build.
$ ls
cholesky.cxx cholesky.go cholesky.hxx
$ go build
$ ls
cholesky cholesky.cxx cholesky.go cholesky.hxx
$ ./cholesky
The first Cholesky decomp. factor is: 1
$
И результат готов! Разумеется, первый множитель разложения Холецкого равен 1. Мы успешно воспользовались библиотекой Intel DAAL непосредственно из Go. Впрочем, наша программа на Go выглядит достаточно странно с небезопасными указателями и фрагментами кода C. Кроме того, это одноразовое решение. Теперь попробуем реализовать эту же функциональность в виде многократно используемого пакета Go, который можно будет импортировать точно так же, как и любой другой пакет Go.
Создание многократно используемого пакета Go с Intel DAAL
Чтобы создать пакет Go, содержащий функциональность Intel DAAL, мы воспользуемся программой SWIG. В Go, помимо использования cgo, можно вызывать SWIG при сборке, чтобы компилировать пакеты Go, реализующие функциональность C/C++. Для такой сборки потребуется создать следующую структуру папок.
choleskylib
├── cholesky.go
├── cholesky.hxx
├── cholesky.cxx
└── cholesky.swigcxx
При этом файлы оболочек *.cxx и *.hxx могут остаться такими же. Но теперь нужно добавить файл *.swigcxx. Этот файл выглядит так.
%{
#include "cholesky.hxx"
%}
%include "cholesky.hxx"
Теперь программа SWIG создает код-оболочку для функции разложения Холецкого, что позволит использовать этот код в качестве пакета Go.
Кроме того, мы создаем пакет Go, пригодный для многократного использования (а не отдельное приложение), поэтому файл *.go может не включать package main или function main. Он должен просто определять имя нашего пакета. В данном случае назовем его cholesky. Теперь cholesky.go будет выглядеть так.
package cholesky
// #cgo CXXFLAGS: -I$DAALINCLUDE
// #cgo LDFLAGS: -L$DAALLIB -ldaal_core -ldaal_sequential -lpthread -lm
import "C"
(Снова указываем файлы в заголовке.)
Теперь можно собрать пакет и установить его локально.
$ ls
cholesky.cxx cholesky.go cholesky.hxx cholesky.swigcxx
$ go install
$
Эта команда компилирует все необходимые двоичные файлы и библиотеки, к которым обращается программа Go, использующая этот пакет. Go «видит», что в нашей папке есть файл *.swigcxx, и автоматически использует SWIG для сборки пакета.
Великолепно! Теперь у нас есть пакет Go, использующий Intel DAAL. Посмотрим, как сработают импорт и использование пакета.
package main
import (
"fmt"
"github.com/dwhitena/daal-go/choleskylib"
)
func main() {
// Define the input matrix as an array.
inputArray := [9]float64{
1.0, 2.0, 4.0,
2.0, 13.0, 23.0,
4.0, 23.0, 77.0,
}
// Get the first Cholesky decomposition factor.
factor := cholesky.CholeskyDecompose(3, &inputArray[0])
// Output the first Cholesky dcomposition factor to stdout.
fmt.Printf("The first Cholesky decomp. factor is: %dn", factor)
}
Класс! Такой код намного чище по сравнению с прямым использованием Intel DAAL. Можно импортировать пакет алгоритма Холецкого, как и любой другой пакет Go, и вызвать заключенную в оболочку функцию как cholesky.CholeskyDecompose(...). Кроме того, в SWIG были автоматически обработаны и все небезопасные компоненты. Теперь можно просто передать адрес первого элемента нашего исходного среза float64 в cholesky.CholeskyDecompose(...).
Эту программу, как и любую другую программу на языке Go, можно скомпилировать и запустить командой go build:
$ ls
main.go
$ go build
$ ls
example main.go
$ ./example
The first Cholesky decomp. factor is: 1
$
Ура! Все правильно. Теперь можно использовать этот пакет в других программах Go, если нам потребуется алгоритм разложения Холецкого.
Выводы и ресурсы
С помощью Intel DAAL, cgo и SWIG нам удалось встроить оптимизированный алгоритм разложения Холецкого в программы на языке Go. Разумеется, возможности не ограничиваются только этим алгоритмом. Аналогичным образом можно создавать программы и пакеты на языке Go, использующие любые алгоритмы, реализованные в Intel DAAL. Можно создавать нейросети с пакетной, интерактивной и распределенной обработкой, кластеризацией, ускорением, совместной фильтрацией и другими возможностями непосредственно в приложениях Go.
Весь использованный выше код доступен здесь.
Ресурсы по языку программирования Go
- Присоединяйтесь к Gophers on Slack и поговорите с другими участниками канала #data-science, которые занимаются большими данными, анализом данных, машинным обучением и прочими аналогичными решениями с помощью Go.
- Посетите сайт организации GopherData, где пользователи взаимодействуют с разработчиками средств управления данными, обработки, анализа, машинного обучения и визуализации данных на языке Go.
- Отслеживайте GopherData в Twitter.
- Используйте (и пополняйте) растущий перечень инструментов для Go.
Ресурсы по DAAL
- Ознакомьтесь с документацией по Intel DAAL.
- Воспользуйтесь учебными материалами.
- Станьте участником форума Intel DAAL.
Об авторе
Дэниэл (@dwhitena) — доктор наук, опытный исследователь данных, он работает в компании Pachyderm (@pachydermIO). Он занимается разработкой современных распределенных конвейеров данных, включающих предсказательные модели, визуализацию данных, статистический анализ и другие возможности. Он выступал на конференциях в разных странах мира (ODSC, Spark Summit, Datapalooza, DevFest Siberia, GopherCon b lheubt), преподает исследование и анализ данных в компании Ardan Labs (@ardanlabs), поддерживает ядро Go для Jupyter и активно участвует в развитии различных проектов по интеллектуальному исследованию данных с открытым исходным кодом.
Автор: Виктор Гурылев