...

четверг, 20 июля 2017 г.

VAX — инструмент для визуального программирования, или как написать SQL мышкой

Я хочу рассказать про созданный мною web редактор для «визуального программирования» и его историю создания.

Случилось как-то мне столкнуться с одной очень надоедливой задачей на работе в компании PimPay, в которой я занимаю должность технического директора. Есть у нас отдел, занимающийся претензионной деятельностью с Почтой России. Ребята ищут потерянные/не оплаченные/не доставленные отправления. В сухом остатке для разработчиков эта задача сводилась к написанию очень большого количества разнообразных и огромных (>150 строк) SQL запросов на диалекте PostgreSQL, которые ОЧЕНЬ часто менялись и дополнялись в силу активного появления новых гипотез, а также поступления новых сведений о заказах.

Естественно, нам, как программистам, это дело очень быстро надоело, и сразу захотелось предложить отстать от нас какой-то визуальный инструмент конструирования фильтров, которые в итоге транслируются в SQL. Сначала захотели налепить просто много формочек с кучей «инпутов», но идея быстро провалилась, т.к. было понятно, что все фильтры надо уметь как-то «композиционировать» (compose): объединять в группы, пересекать с разными условиями И, ИЛИ, параметризовать и прочее. И даже простейший реально используемый фильтр начинал выглядеть ужасно. Плюс сбоку закрадывалась мысль, что в принципе данные фильтры являются частным вариантом просто общего сборщика SQL, и вообще, раз уж дошло дело до «визуального программирования», то можно постараться полностью абстрагироваться от предметной области.

И тут, как в фильме «Проблеск гениальности» («Flash of Genius»), у меня перед глазами всплыла картина визуального редактора схем (blueprints) из UE4 (Unreal Engine 4), с помощью которых персонажи запускали файрболы в своих врагов:
image

Прибежав в тот же вечер домой, я взял первую попавшуюся JavaScript библиотеку, умеющую рисовать красивые прямоугольники и сложные линии — ей оказалась Raphaël от нашего соотечественника DmitryBaranovskiy. Нарисовав пару прямоугольников и подёргав их с помощью библиотечных drag-and-drop, я сразу написал автору библиотеки с вопросом поддерживает ли он её. И не дождавшись ответа (до сих пор), я в ту же ночь наплодил более 1000 строк кода на JavaScript, и моя мечта на глазах почти стала явью! Но предстояло ещё много работы.

В итоге, что же захотелось сделать:

  • Красивый и удобный редактор в вебе, который предоставляет средства для манипуляции и связывания «узлов» разных типов, которые описываются в доменной схеме, передающейся редактору при инициализации. Тем самым сделать редактор независимым от предметной области.
  • Возможность сериализации ацикличного графа пользователя в древовидную структуру, которую очень легко разбирать (parse) (например JSON) и интерпретировать на любом языке.
  • Предоставить удобный формат для описания типов и компонентов предметной области.
  • Придумать богатую систему типов и ограничений, которая поможет пользователю создавать графы, корректные с точки зрения предметной области.
  • Дать возможность пользователю создавать свои компоненты из комбинаций существующих.

В итоге вот что получилось: image

Видно, что чертёж (blueprint) состоит из узлов (nodes), которые являются конкретными экземплярами компонентов (component), описанных в схеме предметной области (schema). Узлы соединяют проводами (wires). Провод всегда идёт от выхода (output) одного узла к входу (input) другого (и наоборот). Из одного выхода может идти много проводов, но на вход можно привязать только один.

Все входы и выходы имеют тип, и соответственно накладывают ограничения на возможные связи. Например, возьмём следующую систему типов:

types:
    # Всегда есть тип Any, который супер-родитель всех типов
    Scalar:
    Numeric:
       extends: Scalar
    String:
       extends: Scalar
    List:
       typeParams: [A]


Таким образом можно выход типа Numeric связать с входом типа Scalar, но не наоборот. Для параметризованных типов вроде List подразумевается ковариативность, т.е. List[String] можно передать в List[Scalar], но не наоборот. Плюс всегда присутствует супер тип Any, наследником которого являются все остальные типы.

У узлов также могу присутствовать атрибуты, которые не настраиваются с помощью проводов, а пользователь сам вводит в них значения. Для этого существуют концепция valuePicker-ов, с помощью которых можно задавать свой интерфейс ввода значений атрибутов. Из коробки есть просто текстовый инпут и возможность выбора из заранее определённого набора констант.

Также узлы бывают параметризованы по типам. Например, дан компонент:

IfThenElse:
  typeParams: [T]
  in:
    C: Boolean  
  out:
    onTrue: @T
    onFalse: @T


При создании узла на базе компонента IfThenElse редактор попросит нас указать тип T и подставит его во все места с T:
image

Типы входов и выходов также помогают пользователю при проектировании. Если вы потянете проводок из выхода с типом Numeric и отпустите мышку, то вылезет окно создания компонентов, отфильтрованных таким образом, что там останутся только те, вход которых совместим (conforms) с типом Numeric. И даже автоматически привяжется проводок.

Заняло всё это примерно чистых тройку человеко-недель, растянутых на добрых 5-6 месяцев. И ещё спустя полгода появились силы что-то задокументировать и заявить об этом миру.

Итак, господа, настало время творить! Возьмём самый нереальный случай, когда вам надо предоставить «не техническому» пользователю возможность визуально программировать процесс складывания чисел. Мы пониманием, что нам нужен всего лишь один тип Numeric и пару компонентов: возможность задать число (Literal) и возможность сложить два таких числа (Plus). Далее приведён пример схемы данной предметной области: (все примеры схем описаны в формате YAML для наглядности, в реальности же вам надо будет передавать нативные javascript объекты):

types:
    # Всегда есть тип Any, который супер-родитель всех типов
    Numeric:
        color: "#fff"
      
components:
  Literal: # Название компонента
    attrs: # Атрибуты
      V: Numeric 
    out: # Исходящие сокеты
      O: Numeric

  Plus:
    in: # Входящие сокеты
      A: Numeric
      B: Numeric
    out:
      O: Numeric


Пример собранного редактора с данной схемой и простым графом можно посмотреть тут.

Поиграйтесь! Нажмите X для создания нового элемента, удалите элемент двойным кликом. Соедините узлы проводками, выделите их все и скопируйте и вставьте через Ctrl+C и Ctrl+V. Потом выделите все Ctrl+A и удалите с помощью Delete. Ведь всегда можно сделать Undo, прибегнув к Ctrl+Z!

Теперь допустим, наш нехитрый пользователь собрал следующий граф:
image

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

[
  {
    "id": 8,
    "c": "Plus",
    "links": {
      "A": {
        "id": 2,
        "c": "Literal",
        "a": {
          "V": "2"
        },
        "links": {},
        "out": "O"
      },
      "B": {
        "id": 5,
        "c": "Literal",
        "a": {
          "V": "2"
        },
        "links": {},
        "out": "O"
      }
    }
  }
]
Как видим нам тут пришло дерево, которое очень легко рекурсивно обойти и получить какой-то результат. Допустим, языком нашего бэкэнда является тоже JavaScript (хотя может быть любой). Пишем тривиальный код:
function walk(node) {
    switch (node.c) {
        case 'Literal':
            return parseFloat(node.a.V);

        case 'Plus':
            return walk(node.links.A) + walk(node.links.B);

        default:
            throw new Error("Unsupported node component: " + node.component);
    }
}

walk(tree);
Если мы прогуляемся такой функцией по вышеуказанному дереву, то получим 2+2=4. Вуаля!

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

Например, даже имея такую скудную доменную область, где можно просто складывать числа, пользователь может определить свой компонент, который будет умножать заданное число на три:

Теперь у нас появилась пользовательская функция x3:

Которой можно воспользоваться, как новым компонентом:

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

Вернёмся же к нашему первому примитивному примеру складывания чисел. Не смотря на то, что процессор это интегральное устройство, которое только и делает, что складывает — визуально программировать конечному пользователю на уровне сложения чисел будет не очень весело. Нужны более экспрессивные и богатые конструкции!

Возьмём к примеру замечательный язык SQL. Если присмотреться, то любой SQL запрос на самом деле очень легко раскладывается в дерево (этим и занимается первым делом БД, когда получает ваш запрос). Понаписав достаточное количество типов и компонентов можно получить нечто уже более устрашающее:
image

P.S. Если какой-то из примеров не открывается...
Возможно это связано с тем, что вы уже попытались сохранить пользовательскую функцию для одной из схем. А так как по умолчанию (но можно и нужно определять свои обработчики) все пользовательские функции хранятся в localStorage, то может возникнуть ситуация, когда редактор попытается загрузить компоненты или типы, не описанные в текущей схеме.
Для этого просто очистите текущий localStorage с помощью:
localStorage.clear()

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

SELECT 
     COUNT(o.id) AS cnt
   , (o.created_at)::DATE AS "Дата" 
FROM tbl_order AS o 
WHERE o.created_at BETWEEN '2017-1-1'::DATE AND CURRENT_DATE 
GROUP BY (o.created_at)::DATE 
HAVING ( COUNT(o.id) ) > ( 100 ) 
ORDER BY (o.created_at)::DATE ASC

Который сразу же исполняется и отдаёт готовый отчёт в формате Excel. Переведя данный SQL на человечий, получаем:
Показать количество загруженных заказов за каждую день в хронологическом порядке, начинания с 1 января 2017 года, выкидывания дни, где загрузили меньше чем 100 заказов. Вполне себе реальный отчёт для бизнеса!

Схему в формате JSON для данного примера можно посмотреть тут.

Я не буду в статье приводить полное описание системы типов и компонентов, за этим отправляю в соответствующий раздел документации. Но для подогрева интереса лишь немного «вброшу», что можно писать вот так (немного косячит подсветка вычурного синтаксиса YAML):

Plus:
        typeParams: [T]
        typeBounds: {T: {<: Expr}} # Параметризованный тип 'T' ограничен сверху типом 'Expr',
                                   # что означает, что нам надо сюда передать наследник типа 'Expr'
        in:
            A: @T
            B: @T
        out:
            O: @T


Это как если бы вы в Scala объявили функцию:
def Plus[T <: Expr](A: T, B: T): T = A + B

В итоге, подготовив достаточное количество компонентов, и придумав хорошую систему типов (и написав много немного backend кода для обхода деревьев) мы сбагрили дали возможность пользователю составлять свои ad-hoc отчёты на базе SQL. Немного расширив доменную область, мы сильно упростили исходную задачу с фильтрами для поиска проблемных заказов, описанную в начале статьи. А самое главное дали бизнесу возможность самостоятельно тестировать свои гипотезы без привлечения разработчиков. Теперь правда, приходится обучать и писать новые компоненты, но это куда более приятное занятие! Надеюсь, что на SQL и фильтрах дело не остановится, и мы воспользуемся этим инструментом в другим областях проекта.

Самое главное, что я с радостью передаю этот инструмент в общественное достояние на GitHub под лицензией MIT.

Кому интересно, есть идеи/задачи по дальнейшему развитию инструмента:
  • Более удобная навигация по компонентам в схеме + их документация для пользователя
  • Сокеты-атрибуты (как в UE4)
  • Возможность определять атрибуты в пользовательских функциях.
  • Режим read-only для отображения схем
  • Узлы кастомной формы (как в UE4), а не только прямоугольники
  • Ускорение и оптимизация для работы с большим количеством элементов
  • Интернационализация
  • Экспорт картинки в SVG/PNG
  • You name it!

Будет круто, если кому-то захочется воспользоваться этим инструментом на практике и поделиться своим опытом.

P.S. Ещё круче будет, если кто-то присоединится к разработке инструмента!

P.P.S. Я проводил презентацию инструмента в компании с помощью данного документа.

Комментарии (0)

    Let's block ads! (Why?)

    Как перейти от КЛАДР к ФИАС и ничего себе не сломать

    ГНИВЦ ФНС сообщает, что с начала 2018 года КЛАДР перестанет существовать и скачать его будет нельзя.
    Дисклеймер:
    Если вы совсем не поняли, что означают эти наборы букв, ничего страшного. Ниже мы расскажем о реалиях работы с адресами в России. Если вам это неинтересно, почитайте про топографические каламбуры.

    Правильные адреса нужны компаниям, которые любят своих клиентов. Знакомые банки, страховые и интернет-магазины, которые сейчас используют справочник КЛАДР, спрашивают нас, что же делать дальше. Поэтому мы запарились и написали пошаговое руководство по переходу с одних букв на другие от КЛАДР к ФИАС.



    Зачем нужны классификаторы адресов


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

    Как вы думаете, какой государственный орган наиболее заинтересован в том, чтобы вы получали от него письма? Правильно! Налоговая. Поэтому ФНС России много лет разрабатывает и поддерживает ведомственные общероссийские классификаторы адресов. И так как более полных справочников нет, их стали использовать повсеместно.

    Сначала был КЛАДР, и было в нем 7 уровней:

    Регион → Район → Город → Населенный пункт → Улица → Дом со строениями и корпусами → Квартира.

    С 2012 года в эксплуатацию ввели новый классификатор — ФИАС. Про основные отличия мы писали три года назад в статье «ФИАС или КЛАДР: выбираем справочник адресов». В ФНС к разработке нового классификатора подошли основательнее и постарались учесть все грабли минусы КЛАДР. Из интересного добавили дату начала и окончания записи, ввели фиксированный ID для каждого дома (предполагается, что он не будет меняться).

    До сих пор продолжают обновляться оба классификатора, но поддерживать совместимость становится сложнее. В прошлом году в ФИАС начали добавлять новые уровни. Например, планировочные структуры — это всякие дачные товарищества и микрорайоны, в классификаторе их уже больше 81 000. В конечном итоге в ФНС приняли логичное решение прекратить поддержку КЛАДР и удалить его в конце 2017 года.

    Как адреса хранятся в ФИАС и КЛАДР


    Мы видели много способов хранения адресов в структуре КЛАДР. В основном они сводятся к двум вариантам:
    1. Код КЛАДР до улицы + домовая часть вида «дом 1 строение 3 квартира 33», так как части домов в классификаторе хранятся как диапазоны
    2. Адрес в виде текста — одним полем или разбитый по структуре КЛАДР.

    Так как в ФИАС нет части домов, мы рекомендуем хранить адрес как:
    1. Код ФИАС до улицы + дом + квартира для идентификации адреса (подробные рекомендации).
    2. Адрес одной строкой в формате ФИАС. При этом лучше сохранять исходный адрес. ФИАС — это все-таки справочник налоговой, и там есть не всё.

    То есть задача миграции сводится к тому, чтобы перевести код или текстовый адрес в формате КЛАДР в код ФИАС.

    Классификационные коды выглядят так:
    • КЛАДР: СС+РРР+ГГГ+ППП+УУУУ+ДДДД;
    • ФИАС: СС+РРР+ГГГ+ППП+СССС+УУУУ+ДДДД (или ЗЗЗЗ)+ОООО.

    Смотрится страшно, рассмотрим пример: Ленинградская обл, Всеволожский р-н, деревня Кудрово, мкр Новый Оккервиль. Адрес реально существует, вот он на Яндекс.Картах. Классификационный код КЛАДР и ФИАС на первый взгляд для него выглядит одинаково: 470050000550023. Разложим его на составляющие.
    Уровень Пример код ФИАС код КЛАДР
    1 Регион Ленинградская область СС: 47 СС: 47
    2 Автономный округ
    3 Район Всеволожский р-н РРР: 005 РРР: 005
    4 Город ГГГ: 000 ГГГ: 000
    5 Внутригородская территория
    6 Населенный пункт деревня Кудрово ППП: 055 ППП: 055
    65 Планировочная структура мкр Новый Оккервиль СССС: 0023
    7 Улица УУУУ УУУУ: 0023
    75 Земельный участок ЗЗЗЗ
    8 Здание, сооружение, объект незавершенного строительства ДДДД ДДДД
    9 Помещение в пределах здания, сооружения ОООО
    90 Дополнительная территория
    91 Подчинённые дополнительных территорий
    Раскладываем адрес по уровням ФИАС и КЛАДР

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

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

    Чтобы избавиться от этого, в ФИАС добавили еще идентификационный код ФИАС. Это глобальный идентификатор для каждого объекта: города, улицы, района и т. д. Выглядит он как набор букв и цифр, который формируется по стандарту GUID (Globally Unique Identifier). Типичный GUID в ФИАС выглядит так: f77948dc-7bc8-42cb-979e-2c958d162d63.

    Код КЛАДР → идентификационный код ФИАС


    Если адрес у вас хранится как код КЛАДР улицы + домовая часть («дом 1 строение 3 квартира 44»), то для него все относительно просто.


    Таблица ADDROBJ хранит все адресные объекты с их идентификаторами

    Для работы понадобится таблица ADDROBJ из выгрузки ФИАС в формате xml или dbf. В ней хранятся все объекты (города, улицы и т. д.) подряд с уникальными идентификаторами GUID.

    Будем использовать следующие колонки:
    • код КЛАДР (PlainCode);
    • наименование и тип текстом (FormalName и ShortName);
    • актуальность объекта (CurrStatus);
    • почтовый индекс (PostalCode);
    • собственный и родительский идентификатор ФИАС (AoGuid и ParentGuid);
    • уровень (AoLevel).

    Пример: Москва, ул Александра Солженицына. Код КЛАДР: 77000000000151900.

    Шаг 1. Выделяем из кода КЛАДР код до улицы, то есть берем первые 15 цифр: 77000000000151900.

    • Если КЛАДР меньше 15 цифр, берем 11 и дополняем нулями — это код до населённого пункта: 770000000000000;
    • Меньше 11 цифр — берем 8: 77000000000000000;
    • Меньше 8 — берем 5: 77000000000000000;
    • Меньше 5 — берем 2 цифры кода региона: 77000000000000000.

    Шаг 2. Ищем код КЛАДР в поле PlainCode. Если нашлась одна запись, то сохраняем значение поля AoGuid и пропускаем следующий пункт. Но по нашему коду 770000000001519 находится три записи, нужна дополнительная проверка.

    Шаг 3. Находим актуальную запись. Значение поля CurrStatus = 0 означает, что запись актуальна. Выбираем ее и сохраняем значение поля AoGuid. Идентификационный код ФИАС найден!

    • Александра Солженицына, CurrStatus = 2;
    • Коммунистическая Б., CurrStatus = 1;
    • Александра Солженицына, CurrStatus = 0, AuGuid = f77948dc-7bc8-42cb-979e-2c958d162d63.

    В качестве бонуса соберем текстовый адрес по найденному AoGuid:
    • берем PostalCode — это почтовый индекс: 109004
    • добавляем ShortName и FormalName: 109004, ул. Александра Солженицына
    • если объект является регионом (поле AoLevel = 1), то адрес готов. Но в нашем случаем AoLevel = 7 — это улица. Поэтому ищем родительский элемент по полю ParentGuid и повторяем предыдущий пункт;
    • для улицы Александра Солженицына родительская запись сразу имеет уровень региона — это Москва. Сохраняем ShortName и FormalName и собираем адрес: 109004, г. Москва, ул. Александра Солженицына

    Но можно поступить еще проще и воспользоваться готовым сервисом. DaData.ru умеет подсказывать адреса в конкретных регионах, районах, городах и населенных пунктах. Понимает названия («Петергоф»), коды КЛАДР («7800000800000») и ФИАС («8f238984-812b-4bb1-850b-49749fb5c56d»).


    Строчный адрес КЛАДР → код ФИАС


    Самый простой вариант — воспользоваться DaData.ru. Сервис сделает все за вас автоматически. Но можно и развлекаться самостоятельно.

    Адреса одной строкой


    Если адреса у вас хранятся одной строкой, вроде этой:
    г Москва, улица Большая Коммунистическая, дом 3,то поздравляем, это самая интересная задача. Нужно писать свой адресный парсер, который будет разделять строку в формате КЛАДР на части, искать каждый ее компонент в ФИАС с учетом опечаток, сокращений, исторических названий и определять по ним ФИАС-код. Легче это сделать уже готовым адресным парсером. Как выбрать алгоритм для адресного фильтра, мы рассказывали раньше.

    Адреса по КЛАДР


    Если адреса у вас хранятся разложенными по КЛАДР, то задача немного проще.
    Тип региона Название региона Тип улицы Название улицы Тип дома Номер дома
    г Москва улица Коммунистическая Б. дом 3
    Примерно так выгядит адрес, разложенный по КЛАДР

    Код ФИАС можно собрать, используя всё ту же таблицу ADDROBJ. Но в этом случае двигаться по уровням нужно от большего к меньшему.

    Шаг 1. Берем название региона и ищем его в поле FormalName таблицы ADDROBJ.

    FormalName = Москва
    AoGuid = 0c5b2444-70a0-4932-980c-b4dc0d3f02b5


    Шаг 2. Идем дальше по уровням вниз и ищем по FormalName с фиксированным родителем — найденным AoGuid на предыдущем шаге. В нашем случае уровни «город» и «населенный пункт» пустые, а следующий непустой уровень — улица.

    ParentGuid = 0c5b2444-70a0-4932-980c-b4dc0d3f02b5,
    FormalName = Коммунистическая Б. → AoGuid=f77948dc-7bc8-42cb-979e-2c958d162d63


    Шаг 3. Если дошли до улицы, то можно найти и дом. Для этого в таблице HOUSE ищем номер дома с фиксированным AoGuid улицы. ФИАС не полон домами, поэтому не расстраивайтесь, если нужный номер не найдется.

    AoGuid = f77948dc-7bc8-42cb-979e-2c958d162d63,
    дом номер 3
    HouseGuid = bce8be1f-f2f7-4cce-836e-08daac0b931e


    Подводные камни


    Может ли что-то пойти не так? Конечно. Например, по названию может легко найтись несколько объектов. «Москва, Тверская» — это про площадь или про улицу? Тогда нужно сравнивать типы, но не все так просто.
    LEVEL SCNAME SOCRNAME KOD_T_ST
    7 тракт Тракт 727
    7 туп Тупик 728
    7 ул Улица 729
    7 уч-к Участок 730
    7 ф/х Фермерское хозяйство 789
    7 ферма Ферма 769
    7 х Хутор 758
    7 ш Шоссе 731
    Таблица SOCRBASE хранит полные и сокращенные типы объектов

    Тип по КЛАДР может быть сокращенным и полным: «ул» — «Улица», «х» — «Хутор». В ФИАС в явном виде хранится только сокращенный тип (в поле ShortName). Полный тип в сокращенный можно превратить с помощью таблицы SOCRBASE, в ней для каждого уровня хранится соответствие сокращенный и полных типов.

    § § §

    И напоследок хорошая новость для тех, кому лень запариваться всем перечисленным. В ближайшее время мы планируем выпустить ФИАС в формате КЛАДР для тех, кто не успеет перейти на формат ФИАС до конца года. Следите за новостями :-)

    P.S. Благодарю за неоценимую помощь в создании этой статьи моих коллег по HFLabs Михаила Березина, Елену Расторгуеву и Антона Жиянова.

    Комментарии (0)

      Let's block ads! (Why?)

      Сортировка пузырьком в коде Qualcomm

      A!Hack Summer — хакатон Альфа-Банка 5 и 6 августа 2017

      Привет, Хабр! 5 и 6 августа мы будем проводить внешний летний хакатон “A!Hack Summer”.

      Если вам интересны такие вещи как Artificial Intelligence, Machine Learning, AR/VR, Data Science, вы умеете с ними работать и подумывали сделать что-то свое или знаете, как на базе этого сделать продукт для клиентов банка — заходите в Deworkacy (Москва, Берсеневская наб. 6 стр. 3) 5 и 6 августа.

      А о том, для чего мы все это делаем, расскажет Владимир Урбанский, руководитель Альфа-Лаборатории.

      Первый хакатон мы делали в ноябре 2013-го, тогда еще само слово «Хакатон» не вошло в обиход и многим казалось смешным. В банковской среде практически не было культуры хакатонов, которую мы можем наблюдать сейчас.

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

      В лабе мы хотели это поменять. Мы делали кросс-функциональные команды, где продакт работает вместе с командой — с разработчиками и аналитиками, разработчики понимают цели и задачи, стоящие перед бизнесом. А если они сами понимают эти задачи – они могут принести бизнесу существенно больше пользы.

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

      Большую часть времени разработчики заняты тем, что для них определил product owner в бэклоге. Но при этом у них есть свои идеи, и если дать им даже короткий промежуток времени на то, чтобы пойти и запилить то, что им действительно хочется – это может дать очень интересный результат.

      Для нас это была такая культурная история с точки зрения того, чтобы дать людям некое ощущение свободы, энергетически подзаряжающий глоток воздуха, возможность запилить что-то интересное. То, что им хотелось сделать, а не только то, что решили сделать product owner-ы.

      Поэтому мы стали делать хакатоны. Тут изначально не было цели сделать продукты, которые заработают миллионы долларов, это история про людей и культуру, чтобы какие-то идеи не просто оставались на уровне идей, а находили реализацию. Я, например, не очень верю в банки идей, в стандартный idea management в организациях. Такие подходы работают в некоторых кейсах, когда человек видит какую-то проблему, но она находится совсем вне зоны его влияния. Тогда он может ее кому-то передать, и сотрудник, отвечающий за эту область, возможно, ее реализует. Но так работает далеко не всегда.

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

      Мы делали пост-анализ первых хакатонов, оказалось, что в том или ином виде много чего было реализовано, просто не всегда в виде той первоначальной идеи, которая была. Часть из этих продуктов потом была закрыта. Один из первых продуктов – счет-активити, когда мы связывали банковскую деятельность с фитнес-браслетами. Классная и резонансная была штука, получила свою аудиторию, но была достаточно нишевой. Дальше развивать мы это не хотели, не видели глобальных перспектив, поэтому решили не размывать фокус.

      Критерии успешности хакатона


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

      В этом плане у нас для внутренних хакатонов не было хард-метрик, по которым бы мы оценивали успешность. Конечно, мы всегда были рады, если какое-то завершение хакатона доходило до боя, и мы очень старались, чтобы придуманные решения реализовывались.

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

      Оценка идей


      Для того чтобы команда на хакатоне сложилась, она должна загореться какой-то идеей, и уже на этом этапе и происходит минимальная оценка этой идеи. Если идея плохая – человек просто не сможет захантить людей в команду, люди скажут «Я не верю и не пойду это делать».

      Хакатон, который будет проводиться 5 и 6 августа – внешний.
      Цель хакатона — найти идеи и людей, способных их реализовать.

      Нам интересны идеи и предложения сервисов, которые улучшат жизнь наших клиентов. Если мы сочтем какие-то идеи интересными для наших клиентов, это уже хорошо. Но если есть идея + команда, готовая делать это с банком – это намного интереснее.

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

      Комментарии (0)

        Let's block ads! (Why?)

        [unable to retrieve full-text content]

        [Из песочницы] Пусть интернет прогнётся под нас

        Особенности национальной SMS-авторизации

        Выбранный UI-фреймворк – вред. Архитектурные требования – профит

        Google планирует представить облачный сервис для квантовых вычислений

        Google собирается открыть онлайн-доступ к своим квантовым машинам для ученых и исследователей проблем искусственного интеллекта. Этим ИТ-гигант хочет «подстегнуть» разработку новых инструментов для своей технологии. Со временем компания планирует создать мощный облачный сервис с использованием квантовых компьютеров.

        / фото Spiros VathisCC

        По данным Bloomberg, ИТ-гигант сформировал новую лабораторию Embryonic Quantum Data Center, а также готовит проект ProjectQ — open source проект по написанию программного обеспечения для квантовых компьютеров. Иными словами, Google уже рассматривает возможность практического применения квантовых вычислений.

        «В компании довольно прямо говорят о создании квантового «железа» и запуске сервиса облачных вычислений на его основе», — отмечает Питер МакМахон (Peter McMahon), специалист по квантовым вычислениям из университета Стэнфорда.

        По словам экспертов, квантовые вычисления в будущем способны революционизировать ИТ-отрасль, поскольку могут решать задачи [пока только определенный их спектр] гораздо быстрее, чем на то способны современные машины. Квантовые компьютеры хранят информацию в так называемых кубитах, кодирующих данные как 0 и 1 одновременно. Это позволяет оперировать множеством состояний сразу.

        Ученые надеются, что полнофункциональные квантовые компьютеры найдут применение в самых разных областях. Технический сотрудник Sandia National Laboratories Робин Блюм-Коут (Robin Blume-Kohout) считает, что квантовые вычисления помогут в улучшении работы солнечных панелей, создании медикаментов и даже удобрений.

        Впервые в Google задумались о квантовых вычислениях еще в 2013 году, когда компания купила один из ранних квантовых компьютеров D-Wave. А в 2014 ИТ-гигант уже заявил о начале разработки собственных квантовых машин. По заявлениям разработчиков, система сможет доказать свое превосходство над классическими компьютерами уже к концу этого года.

        Однако Google не единственная компания, занимающаяся созданием квантовых компьютеров. IBM уже предлагает доступ к специализированной платформе квантовых вычислений, выведя 16-кубитный квантовый компьютер онлайн. Также компания намерена за следующие 5 лет создать 50-кубитную квантовую систему, которая должна будет «обойти» все современные классические компьютеры. Еще IBM ведет работу над созданием универсального квантового компьютера для корпоративного сегмента. В этой сфере свой интерес имеет и компания Microsoft.

        Профессор Сет Ллойд (Seth Lloyd) из Массачусетского технологического института отмечает, что сегодня квантовые стартапы «растут как грибы». Один из них — Rigetti Computing, собравший более $69 млн на создание оборудования и программного обеспечения для квантового компьютера. Его основатель Чад Ригетти (Chad Rigetti) видит, что технология становится такой же горячей, как и ИИ, однако убежден — все еще только начинается.

        «Несмотря ни на что эта отрасль еще находится в зачаточном состоянии, — сказал он. — Пока что никто не создал квантовый компьютер, который действительно работает».

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

        P.S. Еще несколько материалов из нашего блога:

        Комментарии (0)

          Let's block ads! (Why?)

          Когда деревья были большими: как маленький дата-центр ураган пережил


          Лето 2017 года выдалось богатым на ураганы. А вот у нас тоже был случай. Ровно 7 лет назад наш первый дата-центр на Боровой пережил ураган, который похоронил чиллеры под слоем 10 тонн железа, прилетевшего с соседней крыши. Душещипательные фотографии искореженных чиллеров разошлись по интернету уже давно, а история про восстановление ЦОДа, оставшегося без холода, никогда не публиковалась. Решил поднять архивы и восполнить пробел.


          В 2010 году DataLine был начинающим оператором дата-центров. На площадке OST успели запустить только три зала на 360 стоек, на севере Москвы (NORD) был один корпус с одним залом на 147 стоек.



          Вот как изменились масштабы нашей инфраструктуры с 2010 года.


          Хотя мы сами проектировали и строили, тогда у нас не было выделенной службы эксплуатации. Не было, как сейчас, отдельных специалистов по ДГУ, кондиционерам, электриков – отдавали все по максимуму подрядчикам. Объемы инфраструктуры были небольшие, как, впрочем, и наш опыт. За инженерку тогда отвечали директор по производству, главный энергетик и я, технический директор. На подхвате были еще дежурные инженеры (трое в смене), но они занимались клиентскими запросами и мониторингом.



          Один из первых залов дата-центра OST-1 в конце 2009 года.



          Так выглядят новые залы в OST сегодня.


          Три зала на Боровой были заполнены только наполовину. Клиентов было немного, для пересчета хватило бы пальцев одной руки.
          На площадке работала чиллерная схема на этиленгликоле с тремя чиллерами Emicon в схеме резервирования 2+1. Надо сказать, эти чиллеры так и не вышли на заявленную производителем мощность, но, поскольку нагрузка была небольшая, одного чиллера почти хватало на все три зала.


          День первый


          20 июля стояла жара за тридцать. В такую погоду чиллерам было плохо, поэтому, когда ближе к концу рабочего дня пошел дождь, я обрадовался, надеясь, что чиллерам полегчает. Вместе с дождем поднялся сильный ветер, и вот я из окна своего кабинета вижу, как мимо пролетают листы металла. Вышел на улицу, а на другой стороне дороги валяются куски крыши. Удивительно, но ни одна из машин сотрудников, запаркованных рядом с ЦОД, сильно не пострадала.



          Кровельное железо свисало с проводов, как белье.


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


          По записи системы видеонаблюдения видно, что все железо прилетело за один мощный порыв ветра. Вот что мы потом увидели c записи одной из камер:



          На видео часы отстают. Когда все произошло, было уже 18.18.


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



          Поврежденная рама и теплообменник фрикулинга первого чиллера. Теплообменник представляет собой “бутерброд”: снаружи рубашка фрикулинга, внутри, с зазором сантиметров пять, такой же на вид теплообменник фреонового конденсатора.



          Искореженные вентиляторы одного из чиллеров.


          Из пробитых теплообменников фрикулинга хлестал гликоль. Давление в системе холодоснабжения резко упало. Насосы остановились по защите от сухого хода, вырубился последний рабочий чиллер, и вся система холодоснабжения встала (времени было 18:32, две минуты как закончился рабочий день). Несколько секунд мы пребывали в ступоре и не знали, что делать. Потом позвонили подрядчику по холодоснабжению и вызвали аварийную бригаду. По телефону подрядчик посоветовал перекрыть внешний контур, объяснил, где находятся нужные вентили и краны системы подпитки. Мы перекрыли вентили, питающие внешние теплообменники, гликоль перестал течь.


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


          В 18:51 начали дозаправлять гликолевые контуры водопроводной водой и постепенно довели давление в системе до рабочего.
          В 19.45 приехала аварийная бригада.
          В 19.53 насосы запустились, но заработал только один чиллер из трех. У другого были повреждены вентиляторы, а у третьего – еще и фреоновый контур.
          Пока мы проделывали все эти упражнения, температура гликоля успела вырасти с рабочих значений (7–12° С) до 20 градусов. Один живой чиллер работал с перегрузкой, и периодически один из двух его контуров останавливался по ошибке. После этого нужно было вручную сбросить ошибку на пульте, и через пять минут (защитный интервал) компрессор запускался. Или не запускался. Тогда помогало полное обесточивание чиллера с перезагрузкой.


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



          Директор по капитальному строительству тогда сорвал спину, скидывая стальные балки с чиллеров.


          С чиллера с пробитыми фреоновыми трубками сняли вентиляторы. Не обошлось без пауэрлифтинга – каждый вентилятор весит под 30 кг.
          К 23:00 худо-бедно собрали и запустили второй чиллер, и температура в залах начала медленно опускаться.


          К тому времени стемнело, но самое интересное только начиналось. Чиллеры стало вышибать по защите из-за перегрева компрессоров: температура гликоля по-прежнему была высокой несмотря на отключение большей части клиентов.
          Начальник производства съездил и купил керхеры, шланги и налобные фонари, чтобы можно было работать ночью. Мы поливали компрессоры чиллеров холодной водой, но это не очень помогало, так как компрессор – это кусок железа весом больше тонны и быстро его не охладить. Теперь, когда чиллер останавливался по ошибке, вместо пяти минут приходилось ждать несколько десятков минут, пока компрессор остынет и ошибка Compressor Overload пропадет.



          Сообщение об ошибке, которое нам по очереди показывали то один, то другой чиллер.


          Глубокой ночью случилось то, чего мы боялись: по аварии одновременно остановились оба чиллера и завести их вместе больше не удавалось. Из четырех юнитов первого и второго чиллеров работало один-два, остальные по очереди пребывали в коматозном состоянии по случаю перегрузки. Температура в залах остановилась на уровне около 30 градусов. Все двери в машинные залы были открыты. Это позволяло хоть как-то избавиться от накопившегося тепла.
          Мы вместе с подрядчиками пошли изучать схемы чиллеров. После долгих и тяжких раздумий они предложили под нашу ответственность сделать то, чего делать нельзя: обойти защиту, поставив перемычки, т.е. накоротко замкнуть реле тепловой защиты. Это был прямой путь к тому, чтобы окончательно убить компрессоры, но других вариантов не было. В три часа ночи чиллеры завелись и больше не останавливались. Температура в холодных коридорах начала приходить в соответствие с SLA.



          Изменение температуры в холодных коридорах с начала аварии и до ее устранения.
          1 — время первой остановки всех чиллеров; 2 — время запуска первого чиллера; 3 — время запуска второго чиллера; 4 — повторная остановка чиллеров; 5 — запуск чиллеров с отключенной тепловой защитой.


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


          День второй


          Утро следующего дня застало нас за монтажом самодельной системы орошения: на крышу подвели трубы с водой и прокололи в садовом шланге дырки.
          Гидрометцентр не обманул: снова пекло под 30° С. Из этой собранной на коленке системы и керхеров мы практически без остановки поливали чиллеры, которые продолжали работать с отключенной тепловой защитой.



          А вот исторический кадр: чиллеры спасает дежурный сетевой инженер Григорий Атрепьев, ныне руководитель отдела комплексных проектов.


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



          Замена вентиляторов на третьем чиллере. Чиллер Emicon RAH1252F с опцией фрикулинга (свободного охлаждения) состоит из двух модулей, в каждом из которых стоит 8 осевых вентиляторов и компрессор Bitzer.



          Заправка фреоном.



          Вид на задний двор на следующий день. Еще долго вывозили металлолом.


          Что было дальше


          Чиллеры. Повреждения были серьезными, и мы потратили еще какое-то время на ремонт. Компрессоры после пережитого издевательства прослужили около года, после чего начали выходить из строя: для двух чиллеров сказалась работа без защиты, в третьем чиллере мы, похоже, поторопились с заправкой фреонового контура (недостаточно хорошо вакуумировали, оставив следы влаги). Пробы масла, взятые из еще живых фреоновых контуров, показывали высокий уровень кислотности, предвещавший скорый конец обмотки электродвигателя. В течение второго года после аварии мы заменили почти все компрессоры пострадавших машин. Пробовали починить один из компрессоров, отдав его в перемотку, но после ремонта он протянул считанные месяцы и снова сгорел, так что мы сочли за благо в дальнейшем покупать новые.


          Вода, которой мы дозаправили гликолевый контур, не повлияла на морозоустойчивость системы. Замеры показали, что концентрация этиленгликоля осталась на достаточном уровне.


          Поскольку чиллеры так и не выдавали заявленной холодильной мощности (а ИТ-нагрузка росла по мере заполнения ЦОДа), приходилось и дальше поливать их в жару. Теплообменники не пережили водных процедур: с годами они обросли известковыми отложениями, а в зазор между теплообменником фрикулинга и фреоновым конденсатором набилась всякая грязь, удалить которую конструкция не позволяла. Через несколько лет мы планово заменили два из трех чиллеров (про это тоже будет увлекательная история, на этот раз без жертв), а на оставшемся срезали теплообменники фрикулинга. Сейчас на площадке OST работает 4 чиллера: два Stulz, Hiref (добавился, когда дата-центр подрос) и один старый Emicon.



          Чиллеры на площадке OST в 2017 году.


          Клиенты. Несмотря на этот кошмар эксплуататора, клиенты отнеслись с нашей беде с пониманием и даже никто от нас не съехал.


          Запомнилось, что для получения страховки на чиллеры и для отчета перед пострадавшими клиентами долго добывали у Гидрометцентра справку о локальному урагане.


          Оргвыводы


          К таким форс-мажорам сложно подготовиться заранее, но важно из любой аварии сделать правильные выводы. Наши, добытые потом и кровью, были такими:


          1. В Москве тоже бывают ураганы. Это сейчас что ни день, то штормовое предупреждение, а тогда это было в новинку. После той аварии при выборе площадки или готового здания под ЦОД особенно тщательно смотрим, нет ли в опасной близости условных сараев и прочих хлипких строений. Конечно, крышу, которая прилетела на наши чиллеры, соседи перекрывали уже под нашим чутким контролем.


          2. Мы стали сами закупать ЗИП (вентиляторы, компрессоры, запас фреона и пр.) и хранить его у себя. Восстановление прошло бы быстрее, если бы у нас на площадке были хотя бы запасные вентиляторы. В тот раз поставку нужного количества пришлось ждать несколько недель.


          3. Волей-неволей разобрались в устройстве чиллеров, они перестали быть для нас “черными ящиками”. Это нам пригодилось впоследствии, потому что замечательные холодильные машины не перестали ломаться.


          4. Провели на крышу воду. Для новых дата-центров делаем это по умолчанию. Вода пригодится для промывки чиллеров или внешних блоков от грязи, скопившейся за осень-зиму, поможет отбиться от тополиного пуха летом и облегчит жизнь системе холодоснабжения в условиях аномальной жары.


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


          6. Настроили удаленное управление чиллерами из центра мониторинга.


          7. Синхронизировали часы на всех системах, чтобы при разборах аварий иметь понятную картину развития событий.


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

          Еще мы с особым тщанием занялись проработкой всех ключевых процессов, задокументировали и сопроводили схемами все, до чего смогли дотянуться, и ввели регулярные боевые учения. И если завтра случится какой-нибудь армагеддон, наши ЦОДы будут спасать не 3,5 человека в жанре импровизации, а большая и опытная служба эксплуатации с четкими, отработанными инструкциями. Это позволяет нам не только управлять постоянно растущей сетью из семи дата-центров, но и успешно проходить аудиты и сертификации самых уважаемых и строгих организаций вроде Uptime institute.


          А какие стихийные бедствия пришлось пережить вашей серверной/дата-центру и какие полезные выводы вы для себя сделали?

          Комментарии (0)

            Let's block ads! (Why?)

            Ядро автоматизации тестирования в микросервисной архитектуре

            Привет, Хабр! Меня зовут Дмитрий Химион, я руковожу отделом обеспечения качества в Avito. Cегодня я хочу рассказать про автоматизацию тестирования в рамках работы с микросервисной архитектурой. Что мы можем предложить разработке для того, чтобы облегчить контроль качества? Читайте под катом.



            Вместо вступления


            “An implementation should be conservative in its sending behavior, and liberal in its receiving behavior”.
            Jonathan Bruce Postel, computer scientist

            Что такое микросервисная архитектура?


            Чтобы мой рассказ был полным, начнем с основ. Если упростить, микросервисная архитектура — это способ организации сервера приложений. Как он работает? По сути, это просто ответ сервис-ориентированной архитектуры на появление такой практики, как DevOps. Если в SOA не регламентированы размеры сервисов и то, что именно они должны делать, то в рамках микросервисной архитектуры есть некоторые умозрительные ограничения. Микросервис — это некоторая сущность, которая заключает в себе одну небольшую функциональность, которой она заведует и предоставляет внешним сервисам какие-то данные.

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

            В чем здесь могут возникать проблемы? Сервисов быстро становится много, повышается их связность. Это осложняет тестирование: проверка изменения одного микросервиса влечет за собой необходимость поднять довольно толстый слой инфраструктуры из множества сервисов, с которыми взаимодействует изменяемый микросервис. В определенный момент развития проекта количество связей и зависимостей сильно вырастает и выполнять быстрое и изолированное тестирование становится затруднительно. Мы начинаем работать медленнее. Разработка замедляется. Эти и некоторые другие проблемы можно решить с помощью автоматизации тестирования.

            Переход к микросервисам


            Для начала рассмотрим переход от монолита к микросервисной архитектуре. В случае с монолитом для того, чтобы заменить какой-либо кусочек в этой системе, мы не можем выкатить его отдельно. Нужно делать сборку заново и полностью обновлять бэк-энд. Это не всегда рационально и удобно. Что происходит при переходе на микросервисную архитектуру? Мы берем бэк-энд и делим его на составляющие компоненты, разделяя их по функциональности. Определяем взаимодействия между ними, и получается новая система с тем же фронт-эндом и теми же базами данных. Микросервисы взаимодействуют между собой и обеспечивают всё те же бизнес-процессы. Для пользователя и системного тестирования всё осталось таким, как и прежде, изменилась внутренняя организация.

            Что это дает? Можно сделать изменение в микросервисе X, которое реализует какую-то его функциональность, и сразу выкатить его в работу. Всё это звучит очень хорошо. Но, как и всегда, есть нюанс: нужно проверять то, как микросервисы взаимодействуют друг с другом.

            Контракты для микросервисной архитектуры


            Взаимодействия микросервисов между собой реализуются посредством контрактов. Что это значит для сервис-ориентированной архитектуры? Контракт — это некоторое соглашение, которое разработчики сервиса создают для взаимодействия между собой внешних пользователей. Разработчики сервиса сами решают, что вы можете запросить у них “X”, а они вам предоставят “Y”. Сервис может предоставлять внешним пользователям яблоки, помидоры, плутоний, продавать детскую одежду или телевизоры — ему не декларируется чёткий фокус на функционал. Внутреннее наполнение регламентируется только на уровне здравого смысла. И соответственно, это сервис диктует внешним пользователям то, что они будут получать, и как к нему необходимо обращаться.

            Если увеличивать комплексность этой задачи, когда сервисов будут не десятки (10-20-50), а сотни (200, 400…2000), то традиционные, “классические” контракты перестают работать в функциональном смысле. И тогда уже возникает необходимость модернизации контрактов под микросервисную архитектуру. Разработан шаблон работы для разработчиков, в котором они и “конечные пользователи” микросервиса меняются местами — этот подход называется consumer driven contacts. Теперь запросы делают внешние пользователи. Это можно представить как такого рода беседу:

            Пользователь 1: “Я слышал, вы поставляете яблоки. Мне нужны маленькие и зелёные”.
            Пользователь 2: “А мне нужны огромные красные яблоки”.
            Пользователь N: “А мне нужно, чтобы вы привезли три тонны яблок”.

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

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

            Предикаты для CDC-testing (Consumer Driven Contracts)


            Без чего нельзя начать работать по принципам CDC-testing? Первое. У нас не получится помочь, если мы видим что у нас в разработке не соблюдается процесс работы по контрактам. Вторая вещь более техническая: это система post-commit (post-PR, если хотите) hook-ов, которая обрабатывает этот поток общения между девелоперами с помощью контрактов и сигнализирует нашей системе тестирования об их обновлении, удалении, появлении новых. Соответственно, заводятся соответствующие таски в Jira, чтобы автоматизаторы успели это все “переварить”. К этому базовому процессу можно добавить еще все что угодно — дополнительные проверки, процессные примочки, но без контроля изменения контрактов взаимодействия между микросервисами жить будет достаточно сложно. Выполнив в каком-то виде эти два пункта, мы можем приступить к имплементации.

            Имплементация автоматизации под CDC


            В основу нашей системы автоматизации тестирования ляжет такое решение, как PACT-фреймворки. Что нужно о них знать? Это протокольное взаимодействие с API: JSON over HTTP, в этом нет ничего сложного. Эти решения взаимодействуют с нашими микросервисами и дают некоторый дополнительный функционал для изоляции и организации тестирования. Что сказать еще? Я видел, как это реализовано в том или ином виде на семи языках программирования (Java, Javascript, Ruby, Python, Go, .NET, Swift). Но если вашего нет в этом списке, не пугайтесь: можно взять базовую библиотеку и сделать свой велосипед, или написать что-то подобное тому, что уже реализовано.

            Что ещё нас ждёт почти в 100% случаев? Первое — это мокирование внешних сервисов. Проблема в том, что сложно поддерживать релевантность того, как себя ведет внешний сервис, и поддерживать это во всех заглушках. Как решать эту проблему? Это зависит от вас. Скорее всего, потребуется направить на это больше ресурсов или ограничить покрытие тестами. И второе: сэмплированные базы данных. Понадобится нарезать боевые базы данных в облегченном виде так, чтобы в них были все необходимые данные для тестирования в рамках нашей системы. Тогда получится наглядный релевантный результат.

            Идем далее. Первое, что я сразу захотел сделать в этом проекте — это информативное логирование. Всегда необходима репрезентативность итогов тестирования. Простая истина: если результаты невозможно прочитать и понять — их никто не будет смотреть. Надо сказать, что базовое логирование в PACT-фреймворках реализовано очень убого.

            Я предпочел бы вынести его в отдельный модуль, сделать обёртку и навертеть там то, что нам нужно. В первую очередь сделать разделение ошибок по источникам их появления. Далее — всё, что вы можете реализовать, например, решения наподобие Allure (решение от компании Яндекс для повышения простоты анализа результатов тестирования). Делать можно что угодно, но необходимость информативных логов нужно учесть.

            Следующий, возможно, “капитанский” момент — это Config Reader. Но в рамках тестирования микросервисной архитектуры это может быть немного заковыристой вещью. Для Config Reader у нас есть два источника. Первый — это PACT-файлы, а второй — State.

            Что это такое PACT-файл? Это не какой-то шифрованный бинарник, а обычный JSON-файл, который имеет специфичную структуру. В нем выделяется consumer, provider (т.е. кто именно и с кем взаимодействует в рамках этого контракта и в какой роли). Далее описываются взаимодействия (это делают разработчики): я — consumer, и хочу от этого сервиса (provider), чтобы он отдавал мне “маленькие зеленые яблоки”; жду такой-то код ответа, статус, header, body и так далее. Есть поле description — это просто описание, повод напомнить разработчикам о чём шла речь в контракте, какой смысл в него вкладывался.

            И самое интересное — это State Provider. Что это? По сути, это состояние, в котором должен пребывать тестируемый микросервис на момент обращений к нему по конкретной итерации тестирования, по конкретному запросу. В States могут описываться как SQL-запросы (или другие механизмы приведения сервиса в некоторое состояние), так и создание каких-то данных в нашей сэмплированной базе. States — это сложный модуль, который может содержать в себе всякого рода сущности, приводящие наш сервис в нужное надлежащее состояние.

            Важно отметить, что здесь появляется Suite runner (см. схему ниже). Это — та сущность, которая будет отвечать за запуск и конфигурирование тестов в удобном для разработчика/тестировщика виде. Её можно и не писать, но я бы все-таки выделил этот момент, так как сложно предвидеть какие тесты необходимо будет прогонять в тот или иной момент времени на проекте. В итоге у нас получается вот такое ядро автоматизации тестирования под микросервисную архитектуру:

            Теперь самое важное — внедрение. Мы должны предоставить эту схему с требованиями по наличию описанных контрактов разработчикам. Что мы получаем после?

            У нас есть информация по каждому сервису: о том, что он выдает и то, что он запрашивает у своих “соседей”. Соответственно, с помощью нашей системы автоматизации тестирования, с помощью PACT-файлов мы изолируем наш микросервис для обеспечения изоляции его тестирования вне зависимости от внешних сервисов, с которыми он интегрирован. И предоставляем states через моки, заглушки, сэмплированные базы данных, или напрямую как-то изменяем сервис. Получаем, соответственно, изолированное тестирование. Вуаля: у нас есть ответ на вопрос, что делать с автоматизацией тестирования во время перехода от монолита к микросервисной архитектуре.

            Что учесть при имплементации?


            Что же нужно учесть при имплементации? Первое — это использование контейнеризации и виртуализации в процессе сборки/деплоя микросервиса. И наша система автоматизации тестирования точно так же закручивается в контейнер. Как будете обеспечивать взаимодействие микросервиса и системы автоматизации тестирования — это уже не столь важно: как удобнее, так и делайте.

            Второе — актуализация файлов требований по контрактам. Есть такая проблема, что требования консьюмеров к провайдеру начинают копиться, а разработчики занимаются только тем, что им насущно необходимо сейчас. Здесь нужно на уровне тест-менеджмента или управления продуктом ставить задачу разработчикам: например, час в неделю работать с этим “хвостом”, чтобы он не разрастался. Конечно, эти тесты быстрые, они могут проходить за несколько секунд. Но если их количество будет измеряться тысячами, это будет осложнять актуализацию тестирования. А мы со своей стороны, через систему commit hook-ов будем получать информацию и убирать ненужные автотесты. По сути, CDC — это documentation-driven development, если так можно выразиться.

            Третье: нужно ввести обязательно ввести в эту работу процесс актуализации states и data suites. Что такое data suites? Девелоперы в процессе разработки пишут некоторые базовые сценарии взаимодействия. К примеру: “мне нужен такой запрос и такой ответ”. А обо всем остальном — в каких рамках он должен существовать, какие значения параметров возможны, а какие — нет, забывают. Это необходимо проверять. И сюда мы должны входить с нашим data-driven подходом к тестированию, реализуя это в нашем конфигураторе: позитивные, пограничные и негативные тесты, для того, чтобы обеспечить отказоустойчивость работы нашей микросервисной архитектуры на продакшене. Без этого она вряд ли будет работать, любой шаг вправо/влево — и ошибка взаимодействия.

            Соответственно при появлении новых pact-файлов, через hook мы получаем таски и наполняем новые/изменённые взаимодействия большим количеством наборов данных в interactions, что равно целому ряду проведенных тестов. Мы можем наполнять и редактировать interactions вручную, а можем делать генераторы, которые будут параметризировать их и гнать тесты.
            Что касается States: если брать автоматизацию тестирования веба, то это preconditions, setUps, FixTure; для микросервисов есть проблема — готового механизма нет: надо продумать то, как вы будете сопрягать изменение названия States в PACT-файлах в этом модуле. Самый простой из механизмов — использовать Aliases, по сути KDT (keyword driven testing), говоря на языке автоматизации тестирования. Рассказать сейчас про более красивое и элегантное решение не готов: пока что его не придумал.

            Итоги


            1. То, что касается post-commit-hooks, автоматизации мониторинга, появления и изменения, удаления контрактов между сервисами — это все необходимо автоматизировать, это часть системы. Я бы сказал, что это кусочек ядра, который стоит на уровне репозиториев.
            2. CDC-паттерн для разработчиков обязателен. Это фундамент, без него ничего работать не будет.
            3. Вы заметите снижение склонности системы к появлению дефектов. По сути, у нас все её части окутаны изолирующим тестированием. Мы быстро локализуем дефекты, их быстро исправляют, мы располагаем высоким тестовым покрытием. Если будут появляться дефекты на уровне системного тестирования, они, скорее всего, будут связаны с фронт-эндом или какими-то внешними сервисами.
            4. Вы имеете возможность уйти от ручного тестирования изменений. Ключевые моменты: быстрое и изолированное тестирование. Тогда мы получим профит от того, что мы можем хорошо локализовать проблему или дефект.

            P.S.


            Если вы больше любите не читать, а смотреть видео, то эта информация доступна в формате доклада по ссылке. А в конце августа у нас в Avito пройдет тематический митап для профессионалов в области тестирования. Поговорим о векторах развития систем автоматизации в целом, обсудим применение прикладных инструментов и их влияние на изменение инфраструктуры тестирования. Если интересно — следите за обновлениями на нашей странице в FB, или напишите в комментариях: дадим знать, когда будут подробности о мероприятии.

            Комментарии (0)

              Let's block ads! (Why?)

              Deep Learning, теперь и в OpenCV

              Данная статья является кратким обзором возможностей dnn — модуля OpenCV, предназначенного для работы с нейросетями. Если вам интересно, что это такое, что оно умеет и как быстро работает, добро пожаловать под кат.

              Пожалуй, многие согласятся, что OpenCV является наиболее известной библиотекой компьютерного зрения. За долгое время своего существования она приобрела обширную аудиторию пользователей и стала, де-факто, стандартом в области компьютерного зрения. Множество алгоритмов, работающих «из коробки», открытость исходного кода, замечательная поддержка, большое сообщество пользователей и разработчиков, возможность пользоваться библиотекой на языках C, C++, Python (а также Matlab, C#, Java) под различными операционными системами — это далеко не полный список того, что позволяет OpenCV оставаться востребованной. Но OpenCV не стоит на месте — постоянно добавляется функционал. И сегодня я хочу рассказать о новых возможностях OpenCV в области Deep Learning. Загрузка и получение результатов (предсказаний) с помощью моделей, созданных в любом из трех популярных фреймворков (Caffe, TensorFlow, Torch), быстрая работа на CPU, поддержка основных слоев нейронных сетей и, как всегда, кроссплатформенность, открытость исходного кода и поддержка — об этом я собираюсь рассказать в данной статье.

              Прежде всего, хотелось бы представиться. Меня зовут Рыбников Александр. Я являюсь инженером компании Intel и занимаюсь реализацией функциональности Deep Learning в библиотеке OpenCV.

              Пару слов о том, как устроена OpenCV. Эта библиотека представляет собой набор модулей, каждый из которых связан с определенной областью компьютерного зрения. Существует стандартный набор модулей — так сказать, «must have» для любой задачи компьютерного зрения. Реализуя известные алгоритмы, данные модули хорошо проработаны и протестированы. Все они представлены в основном репозитории OpenCV. Также существует репозиторий с дополнительными модулями, реализующими экспериментальную или новую функциональность. Требования к экспериментальным модулям, по понятным причинам, мягче. И, как правило, когда какой-то из таких модулей становится достаточно развитым, сформировавшимся и востребованным, он может быть перенесен в основной репозиторий.

              Данная статья связана с одним из модулей, совсем недавно занявшим почетное место в основном репозитории — с модулем dnn (далее просто dnn).

              (N+1)-й фреймворк для deep learning, это вообще зачем?


              Зачем вообще понадобился Deep Learning в OpenCV? В последние годы во многих областях глубокое обучение (в некоторых источниках глубинное обучение) показывает результаты, значительно превосходящие аналогичные у классических алгоритмов. Это касается и области компьютерного зрения, где масса задач решается с применением нейронных сетей. В свете данного факта кажется логичным дать пользователям OpenCV возможность работы с нейросетями.

              Почему был выбран путь написания чего-то своего вместо использования уже существующих реализаций? Этому есть несколько причин.

              Во-первых, так можно добиться легковесности решения. Оставляя только возможность выполнения прямого прохода (forward pass) по сети, можно упростить код, ускорить процесс установки и сборки.

              Во-вторых, имея свою реализацию, можно свести внешние зависимости к минимуму. Это упростит распространение приложений, использующих dnn. И, если ранее в проекте использовалась библиотека OpenCV, не составит труда добавить в такой проект поддержку глубоких сетей.

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

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

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

              Основные возможности


              Основная возможность dnn заключается, конечно же, в загрузке и запуске нейронных сетей (inference). При этом модель может быть создана в любом из трех фреймворков глубокого обучения — Caffe, TensorFlow или Torch; способ ее загрузки и использования сохраняется независимо от того, где она была создана.

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

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

              Поддерживаются все основные слои: начиная от базовых (Convolution и Fully connected) и заканчивая более специализированными — всего более 30.

              Список поддерживаемых слоев
              AbsVal
              AveragePooling
              BatchNormalization
              Concatenation
              Convolution (with dilation)
              Crop
              DetectionOutput
              Dropout
              Eltwise
              Flatten
              FullConvolution
              FullyConnected
              LRN
              LSTM
              MaxPooling
              MaxUnpooling
              MVN
              NormalizeBBox
              Padding
              Permute
              Power
              PReLU
              PriorBox
              ReLU
              RNN
              Scale
              Shift
              Sigmoid
              Slice
              Softmax
              Split
              TanH

              Если вы не обнаружили в этом списке слой, который требуется именно вам, не стоит отчаиваться. Вы можете создать запрос на добавление поддержки интересующего вас слоя (и наша команда постарается помочь вам в ближайшее время), либо реализовать все самостоятельно и подать pull request.


              Кроме поддержки отдельных слоев, важна также и поддержка конкретных архитектур нейронных сетей. Модуль содержит примеры для классификации (AlexNet, GoogLeNet, ResNet, SqueezeNet), сегментации (FCN, ENet), детектирования объектов (SSD); многие из указанных моделей проверены на исходных датасетах, но об этом позднее.

              Сборка


              Если вы — опытный пользователь OpenCV, то можете смело пропустить этот раздел. Если нет, то я постараюсь максимально кратко рассказать о том, как же получить работающие примеры из исходного кода для Linux или Windows.
              Краткая инструкция по сборке
              Предварительно потребуется установить git (либо Git Bash для Windows), [cmake](http://cmake.org) и компилятор С++ (Visual Studio под Windows, Xcode на Mac, clang либо gcc под Linux). Если вы собираетесь использовать OpenCV из Python, то нужно также установить сам Python (подойдут последние версии 2.7.x или 3.x) и соответствующую ему версию numpy.

              Начнем с клонирования репозитория:

              mkdir git && cd git
              git clone http://ift.tt/2bzpqye
              

              На Windows клонирование репозиториев также можно выполнить, например, с помощью TortoiseGit или SmartGit. Далее, приступим к генерации файлов для сборки:

              cd ..
              mkdir build && cd build
              cmake ../git/opencv -DBUILD_EXAMPLES=ON
              

              (для Windows здесь и далее нужно заменить cmake на полный путь до запускаемого файла cmake, например на «C:\Program Files\CMake\bin\cmake.exe» или использовать cmake GUI)

              Теперь непосредственно сборка:

              make -j5 (Linux)
              cmake --build . --config Release -- /m:5 (Windows)
              

              После этого dnn готов к использованию.
              Приведенная выше инструкция достаточно краткая, поэтому приведу также ссылки на пошаговые инструкции по установке OpenCV на Windows и Linux.

              Примеры использования


              По хорошей традиции, каждый модуль OpenCV включает в себя примеры использования. dnn — не исключение, примеры на С++ и Python доступны в поддиректории samples в репозитории с исходным кодом. В примерах присутствуют комментарии, да и в целом все достаточно просто.
              Приведу здесь краткий пример, выполняющий классификацию изображений с помощью модели GoogLeNet. На языке Python наш пример будет выглядеть следующим образом:
              import numpy as np
              import cv2 as cv
              
              # read names of classes
              with open('synset_words.txt') as f:
                  classes = [x[x.find(' ') + 1:] for x in f]
              
              image = cv.imread('space_shuttle.jpg')
              # create tensor with 224x224 spatial size and subtract mean values (104, 117, 123) 
              # from corresponding channels (R, G, B)
              input = cv.dnn.blobFromImage(image, 1, (224, 224), (104, 117, 123))
              
              # load model from caffe
              net = cv.dnn.readNetFromCaffe('bvlc_googlenet.prototxt', 'bvlc_googlenet.caffemodel')
              # feed input tensor to the model
              net.setInput(input)
              # perform inference and get output
              out = net.forward() 
              # get indices with the highest probability
              indexes = np.argsort(out[0])[-5:] 
              for i in reversed(indexes):
                  print('class:', classes[i], ' probability:', out[0][i])
              

              Данный код загружает картинку, проводит небольшую предобработку и получает для изображения выход сети. Предобработка заключается в масштабировании изображения таким образом, чтобы наименьшая из сторон стала равной 224, вырезании центральной части и вычитании среднего значения из элементов каждого канала. Данные операции необходимы, так как модель была натренирована на изображениях заданного размера (224 x 224) с именно такой предобработкой.

              Выходной тензор интерпретируется как вектор вероятностей принадлежности изображения к тому или иному классу и имена для 5 классов с наибольшими вероятностями выводятся в консоль.

              Выглядит несложно, не так ли? Если записать то же самое на C++, код получится немного более длинным. Однако, самое главное — имена функций и логика работы с модулем — останутся одними и теми же.

              Точность


              Как понять, что одна натренированная модель лучше другой? Необходимо сравнить метрики качества для обеих моделей. Очень часто борьба на вершине рейтинга лучших моделей идет за доли процентов качества. Поскольку dnn читает и преобразует модели из различных фреймворков в свое внутреннее представление, возникают вопросы сохранения качества после преобразования модели: не «испортилась» ли модель после загрузки? Без ответов на эти вопросы, а значит без проверки сложно говорить о полноценном использовании dnn.
              Я провел тестирование моделей из имеющихся примеров для различных фреймворков и различных задач: AlexNet (Caffe), GoogLeNet (Caffe), GoogLeNet (TensorFlow), ResNet-50 (Caffe), SqueezeNet v1.1 (Caffe) для задачи классификации объектов; FCN (Caffe), ENet (Torch) для задачи семантической сегментации. Результаты приведены в Таблицах 1 и 2.
              Модель (исходный фреймворк)
              Опубликованное значение acc@top-5
              Измеренное значение acc@top-5 в исходном фреймворке
              Измеренное значение acc@top-5 в dnn
              Средняя разница на элемент между выходными тензорами фреймворка и dnn
              Максимальная разница между выходными тензорами фреймворка и dnn
              AlexNet (Caffe)
              80.2%
              79.1%
              79.1%
              6.5E-10
              3.01E-06
              GoogLeNet (Caffe)
              88.9%
              88.5%
              88.5%
              1.18E-09
              1.33E-05
              GoogLeNet (TensorFlow)
              89.4%
              89.4%
              1.84E-09
              1.47E-05
              ResNet-50
              (Caffe)
              92.2%
              91.8%
              91.8%
              8.73E-10
              4.29E-06
              SqueezeNet v1.1
              (Caffe)
              80.3%
              80.4%
              80.4%
              1.91E-09
              6.77E-06
              Таблица 1. Результаты оценки качества для задачи классификации. Измерения проводились на валидационном наборе ImageNet 2012 (ILSVRC2012 val, 50000 примеров).
              Модель (фреймворк)
              Опубликованное значение mean IOU
              Измеренное значение mean IOU в исходном фреймворке
              Измеренное значение mean IOU в dnn
              Средняя разница на элемент между выходными тензорами фреймворка и dnn
              Максимальная разница между выходными тензорами фреймворка и dnn
              FCN (Caffe)
              65.5%
              60.402874%
              60.402879%
              3.1E-7
              1.53E-5
              ENet (Torch)
              58.3%
              59.1368%
              59.1369%
              3.2E-5
              1.20
              Таблица 2. Результаты оценки качества для задачи семантической сегментации. Объяснение большой максимальной разницы для ENet далее в тексте.

              Результаты для FCN вычислены для валидационного набора сегментационной части PASCAL VOC 2012 (736 примеров). Результаты для ENet вычислены на валидационном наборе Cityscapes (500 примеров).

              Следует сказать несколько слов о том, какой смысл имеют указанные выше числа. Для задач классификации общепринятой метрикой качества моделей является точность для топ-5 ответов сети (accuracy@top-5, [1]): если правильный ответ имеется среди 5 ответов сети с максимальными показателями уверенности (confidence), то данный ответ сети засчитывается как верный. Соответственно, точность — это отношение числа верных ответов к числу примеров. Данный способ измерения позволяет учесть не всегда корректную разметку данных, когда, например, отмечается объект, занимающий далеко не центральное положение на кадре.

              Для задач семантической сегментации используются несколько метрик — попиксельная точность (pixel accuracy) и среднее по классам отношение пересечения к объединению (mean intersection over union, mean IOU) [5]. Попиксельная точность — это отношение количества правильно классифицированных пикселей к количеству всех пикселей. mean IOU — более сложная характеристика: это усредненное по классам отношение правильно отмеченных пикселей к сумме числа пикселей данного класса и числа пикселей, отмеченных как данный класс.

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

              Разницу между опубликованными и полученными в экспериментах числами можно объяснить тем, что авторы моделей проводят все вычисления с использованием GPU, в то время как я использовал CPU-реализации. Также было замечено, что различные библиотеки могут по-разному декодировать формат jpeg. Это могло сказаться на результатах для FCN, так как датасет PASCAL VOC 2012 содержит изображения именно данного формата, а модели для семантической сегментации оказываются достаточно чувствительны к изменению распределения входных данных.

              Как вы заметили, в Таблице 2 присутствует аномально большая максимальная разница выходов dnn и Torch для модели ENet. Меня также заинтересовал данный факт и далее я кратко расскажу о причинах его возникновения.

              Почему возникает большое различие между dnn и Torch для ENet?
              Модель ENet использует несколько операций MaxPooling. Данная операция выбирает максимальный элемент в окрестности каждой позиции и записывает в выходной тензор это максимальное значение, а также передает далее индексы выбранных максимальных элементов. Эти индексы далее используются операцией, в некотором смысле обратной данной — MaxUnpooling. Эта операция записывает элементы входного тензора в позиции выходного, соответствующие тем самым индексам. В этом месте и возникает большая ошибка: в определенной окрестности операция MaxPooling выбирает элемент с неправильным индексом; при этом разница между правильным выходом Torch и выходом dnn для данного слоя лежит в пределах вычислительной погрешности (10E-7), а разница в индексах соответствует соседним элементам окрестности. То есть, в результате небольшой флуктуации соседний элемент стал несколько больше, чем элемент с правильным индексом. Результат операции MaxUnpooling, при этом, зависит не только от выхода предыдущего слоя, но и от индексов соответствующей операции MaxPooling, которая располагается намного раньше (в начале вычислительного графа модели). Таким образом, MaxUnpooling записывает элемент с правильным значением в неверную позицию. В результате, накапливается ошибка.

              К сожалению, устранить данную ошибку не представляется возможным, так как первопричины ее появления связаны, скорее всего, с немного различающимися реализациями алгоритмов, использованных при тренировке и при inference и не связаны с наличием ошибки в реализации.
              Однако, справедливо отметить, что средняя ошибка на элемент выходного тензора остается низкой (см. Таблицу 2) — то есть ошибки в индексах возникают достаточно редко. Более того, наличие данной ошибки не приводит к ухудшению качества работы модели, о чем свидетельствуют числа в той же Таблице 2.

              Производительность


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

              Я провел замеры времени работы для различных моделей при их использовании — результаты в Таблице 3.

              Модель (исходный фреймворк)
              Разрешение изображения
              Производительность исходного фреймворка, CPU (библиотека акселерации); потребление памяти
              Производительность dnn, CPU (ускорение относительно исходного фреймворка); потребление памяти
              AlexNet (Caffe)
              227x227
              23.7 мс (MKL); 945 МБ
              14.7 мс (1.6x); 713 МБ
              GoogLeNet (Caffe)
              224x224
              44.6 мс (MKL); 197 МБ
              20.1 мс (2.2x); 172 МБ
              ResNet-50 (Caffe)
              224x224
              70.2 мс (MKL); 386 МБ
              58.8 мс (1.2x); 224 МБ
              SqueezeNet v1.1
              (Caffe)
              227x227
              12.4 мс (MKL); 113 МБ
              5.3 мс (2.3x); 38 МБ
              GoogLeNet (TensorFlow)
              224x224
              17.9 мс (Eigen); 310 МБ
              21.1 мс (0.8x); 135 МБ
              FCN (Caffe)
              различное (500x350 в среднем)
              3873.6 мс (MKL);
              4453 МБ
              1229.8 мс (3.1x);
              1332 МБ
              ENet (Torch)
              1024x512
              1105.0 мс; 828 МБ
              218.7 мс (5.1x); 190 МБ
              Таблица 3. Результаты замеров времени работы различных моделей. Эксперименты проводились с использованием Intel Core i7-6700k.

              Замеры времени производились с усреднением по 50-ти запускам и выполнялись следующим образом: для dnn использовался встроенный в OpenCV таймер; для Caffe использовалась утилита caffe time; для Torch и TensorFlow использовались существующие функции замера времени.

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

              Дальнейшие планы


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

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

              Что касается производительности, то несмотря на многие выполненные оптимизации, у нас все еще есть идеи, как улучшить результаты. Одна из таких идей — уменьшить разрядность вычислений. Данная процедура носит название квантизации. Грубо говоря, выкинуть часть разрядов у входа и весов слоя перед вычислением сверток (fp32→fp16), либо вычислить масштабирующие коэффициенты, переводящие диапазон входных чисел в диапазон int или short. При этом возрастет скорость (за счет использования более быстрых операций с целыми числами), но, возможно, немного пострадает точность. Однако публикации и эксперименты в этой области показывают, что даже достаточно сильная квантизация в определенных случаях не приводит к заметному падению качества.

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

              Сейчас к релизу готовится нечто достаточно интересное. Думаю, немногие слышали о языке программирования Halide. Он не является Тьюринг-полным — некоторые конструкции реализовать на нем не получится; возможно поэтому он и не пользуется популярностью. Однако указанный недостаток является одновременно и его преимуществом — написанный на нем исходный код может быть автоматически превращен в высокооптимизированный под разные «железки»: CPU, GPU, DSP. При этом нет нужды быть гуру оптимизации — специальный компилятор все сделает за вас. Уже сейчас Halide позволяет получить ускорение некоторых моделей — и, например, семантическая сегментация с моделью ENet работает 25 fps для разрешения 512x256 на Intel Core i7-6700k (против 22 fps у dnn без Halide). И, что самое приятное, без переписывания кода можно задействовать интегрированную в процессор GPU, получив дополнительно еще пару кадров в секунду.

              В действительности, мы возлагаем большие надежды на Halide. Благодаря своим уникальным характеристикам он позволит получать ускорение работы, не требуя от пользователя дополнительных манипуляций. Мы стремимся к тому, чтобы для использования Halide вместе с OpenCV у пользователя не возникало необходимости в установке дополнительного программного обеспечения для использования Halide — принцип работы «из коробки» должен сохраняться. И, как показывают наши эксперименты, у нас есть все шансы реализовать это.

              Заключение


              Уже сейчас dnn имеет все, чтобы оказаться полезным. И с каждым днем все большее число пользователей открывает для себя его возможности. Тем не менее, нам еще есть над чем трудиться. Я продолжу свою работу над модулем, расширяя его возможности и совершенствуя функционал. Надеюсь, что данная статья оказалась для вас интересной и полезной.

              Если у вас есть вопросы, появились предложения, возникли проблемы или вы хотите внести свой вклад путем подачи pull request — добро пожаловать в github-репозиторий, а также на наш форум, где я и мои коллеги постараемся вам помочь. Если ни один из указанных способов не подошел, на нашем сайте можно найти дополнительные пути коммуникации. Я всегда буду рад сотрудничеству, конструктивным замечаниям и предложениям. Спасибо за внимание!

              P.S. Выражаю огромную благодарность моим коллегам за помощь в работе и написании данной статьи.

              Ссылки


              1. ImageNet Classification with Deep Convolutional Neural Networks
              2. Going deeper with convolutions
              3. Deep Residual Learning for Image Recognition
              4. SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size
              5. Fully Convolutional Networks for Semantic Segmentation
              6. ENet: A Deep Neural Network Architecture for Real-Time Semantic Segmentation
              7. SSD: Single Shot MultiBox Detector
              8. Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding
              9. OpenCV github
              10. Официальный сайт OpenCV
              11. Форум OpenCV
              12. Halide
              13. Caffe
              14. TensorFlow
              15. Torch

              Комментарии (0)

                Let's block ads! (Why?)