-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

Машинное обучение и анализ данных: решаем практические задачи с победителями индустриального хакатона ЛК

Понедельник, 05 Июня 2017 г. 21:18 + в цитатник


Как вычислить замыслы киберпреступников, атакующих промышленный объект и распознать слабые сигналы SOS, которые периодически подает индустриальная АСУ ТП на фоне “нормального” поведения, – об этом и многом другом поговорим уже в ближайшую среду, 7 июня, на встрече CoLaboratory: Deep Learning в центральном офисе “Лаборатории Касперского”. Всех неравнодушных к теме промышленной безопасности ждет захватывающее погружение в мир машинного обучения и анализа данных под руководством победителей весеннего индустриального хакатона ЛК и экспертов нашей компании.

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

Так, победители хакатона расскажут о том, как:

  • превратить нахождение аномального поведения в логах заводских приборов в задачу обычной классификации;
  • как за короткое время сгенерировать легкие, но полезные фичи, характеризующие временной ряд целиком;
  • как разделить работу внутри команды;
  • как «на коленке» смешивать разные модели на одном наборе признаков;
  • как поднять AUC с помощью kNN – вишенки на торте Competitive Data Science.

Небольшой спойлер: подход наших героев базируется на «трех китах»:

  1. ключевом решении на основе подсчета статистик временных рядов в качестве признаков;
  2. кодировании подряд идущих разреженных состояний системы вектором признаков;
  3. выявлении аномалий с помощью LSTM, идеям по их модификациям.

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

Словом, 7 июня в 19.00 ждем всех желающих попрактиковаться в Deep Machine Learning в штаб-квартире ЛК. Зарегистрироваться на мероприятие можно тут.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330282/


Метки:  

Доигрались: как компании используют геймификацию в рекрутменте

Понедельник, 05 Июня 2017 г. 20:55 + в цитатник
Геймификация в рекрутменте или, проще говоря, применение игровых технологий в процессе привлечения, отклика и прохождения отбора — звучит всегда очень круто и дорого-богато, но мы привыкли всё оценивать скептически.

Так что в этот раз разберемся, как же геймификация может положительно или отрицательно сказываться на поиске и отборе, ну и соберем разные кейсы и стартапы (без ИИ не обошлось!).



Западные источники утверждают, что первые попытки именно с точки зрения технологий геймифицировать процесс шли от американской армии в 1999 году. Да, почти 20 лет назад это произошло. Игру-симулятор использовали для тренировки и обучения солдат. Вот слова полковника Касея Уардински, профессора Военной академии Соединенных Штатов о том, зачем была создана America’s Army: «нужно использовать компьютерные игровые технологии для предоставления виртуального солдатского опыта, который помог бы вовлечь, проинформировать и развлечь аудиторию». Никто не ожидал, что игра разрастется до таких масштабов — в неё рубятся до сих пор и даже придумали отдельный термин – militainment. Последняя версия вышла в 2013 году.

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


// Почему геймификация — это хорошо?


Наиграл
Среднестатистический западный миллениал (окей, подросток) к 21 году уже наиграет на 10,000 часов. Глупо не использовать это, чтобы обучить молодых специалистов чему-нибудь или продвигать среди них бренд работодателя.

Пирамида Маслоу уже не та
Есть исследование Cisco System (аж 2011 года), в котором отмечается, что для поколения Y интернет приравнивается к наличию кислорода, воды, еды и крыши над головой.

Кинь в меня вызов вызов
Геймификация хороша, когда как-то резонирует с интересами вашей аудитории. Но при этом и позволяет вам заинтересовать потенциальных кандидатов в той сфере, которая кажется им совершенно непривлекательной.

Отличный пример умения привлечь свою ЦА — попробуйте последить за активностью General Electric в последнее время — они используют разные вирусные видео, запускают подкасты и вовлекают через разные каналы аудиторию, меняя представление о работодателе и его миссии.

Цена вопроса
Геймификация — это не обязательно очень дорого и пафосно. Подобные технологии (с меньшим размахом, чем у US Army) могут стоить не очень дорого, к тому же можно воспользоваться услугами разных платформ. Также вполне можно попробовать внедрять геймификацию постепенно без затрат: делиться с аудиторией кейсами компании и поощрять тех, кто успешно справляется.


// А почему это может не работать в ряде случаев?


Сложность в том, что нужно сделать так, чтобы пользователь был вовлечен.
Геймификация — это всё же история не про игры. Это история про интерес и креативный подход, который помогает вам тестировать навыки кандидатов, а также влиять на восприятие бренда работодателя. Так что первым крупным косяком мы бы смело назвали — неумение ставить цели и понимать задачи геймификации. А также желание назвать геймификацией любого рода тестик. Нет, это всё не то!

1. Геймификация может быть нужна для тестирования профессиональных навыков кандидата. Это сложные задания, замороченные ассессмент-тесты.
2. Геймификация может быть нужна для вовлечения в процесс отбора и укрепления бренда работодателя.

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

Ещё один пункт не в пользу геймификации: сложно измерить ROI. Вообще с играми и аналитикой есть отдельные вопросы. Распределение средств и усилий на создание такого рода этапа в отборе должно быть осмыслено, разложено по полочкам и вы вместе с командой должны очень точно понимать: ЗАЧЕМ ВАМ ЭТО НННАДА.


// Окей, какие кейсы вы приведете как примеры?


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

Linkedin как самый простой пример
Сейчас (может быть) мы вас удивим — вы тоже участвуете в геймификации. Если зарегистрированы на Linkedin. И если вы думаете, что элемент игры — это попытка обойти блокировку, то нет! На Linkedin вы, набирая контакты, условно перемещаетесь по уровням. То есть чем больше ваш “нетворк”, тем круче вы в системе.

Работает ли?
Да, потому что сейчас модно и круто иметь кучу друзей на FB и в Linkedin. Это как новый способ измерять влиятельность человека. Впрочем, довольно странный.

Goodby Silverstein & Partners в поисках секретаря
Один из самых интересных кейсов на нашей памяти — work4rich от компании Goodby Silverstein & Partners. В общем, искала компания своему боссу ассистента.

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



15,000 человек прошли тест (!), потом они показали резюме кандидатов психиатру — да, всё в этой рекрутинговой кампании было странновато — а та отобрала самую крепкую, стрессоустойчивую и очень красивую девушку Грейс, которая до сих пор работает ассистентом руководителя. И если вам кажется, что это бестолковая история, то задумайтесь о том, что компания Goodby Silverstein & Partners вдруг прошла шумной волной по всему свету. Честь их бренду работодателя и хвала!

Сработало ли?
Конечно! Очевидно, были потрачены силы и средства на разработку лендинга, а также на разбор резюме, зато как много людей узнали о компании и о том, какие сумасшедшие ребята там работают. Второму отклику на вакансию от Goodby Silverstein & Partners быть без сомнений.

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

И тут слово предоставляется Uber. В 2016 году они запустили сервис тестинга потенциальных кандидатов в рамках приложения. Вы садитесь в такси, и тут ваш же смартфон предлагает за три минуты решить три задачи. Если всё получается, то сразу появляется ссылка на вакансию. Задания появляются только в городах, где много it-компаний. Uber использует данные о маршрутах и предлагает сыграть только тем, кто часто бывает у офисов, сотрудники которой в основном занимаются разработкой. Игра была доступна только в Сиэттле, Денвере, Остине, Бостоне, Портланде — это действительно города в США, где концентрация it-специалистов высока.

Сработало ли?
Не сказать, что игра была воспринята с энтузиазмом. Во-первых, не очень круто ощущать, что твои поездки с такой регулярностью и тщательностью анализируют. Большой брат и всё такое. Во-вторых, как сказал один из участников этого эксперимента: “Я не уверен, что проходить шестидесяти секундные тесты Uber при водителе, который всё время пытается с тобой о чём-то поговорить, это хорошая идея”. И он прав.

Питчингуй пока молодой
Ещё один кейс, о котором обязательно нужно упомянуть — Saatchi&Saatchi и их игра для мобильных устройств. В компании разработали приложение, с помощью которого можно было отправить свою идею: записать или сфотографировать. И получить фидбек. Так они искали нового креативного директора, который предложит что-то совершенно уникальное потенциальным коллегам.



Сработало ли?
Да, о кампании написали крупнейшие издания. Шум — уже успех для HR-бренда. Ну и позицию они успешно закрыли.

Шпион на шпионе
“Окей,” — думаете вы, — “всё это крупные компании с кучей денег!”
Но это не так. Вот GCHQ (Government Communications Headquarters) в Великобритании, что тоже самое что АНБ в США или ФСБ у нас, запустили целую кампанию для поиска юных талантов, способных защитить Англию от кибератак. Сложный пазл для любителей поломать голову, систему и код — только 1% кандидатов решили поставленную перед ними задачу. А кандидатов было 400,000 человек. С тех пор GCHQ ищут только тех, чьи таланты открываются на деле. Только секретном.

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

Для любителей пазлов. Алгоритмических.
Google Code Jam — хороший пример. Тем, кто хочет попробовать свои силы, а ещё получить место в Google, предлагают пройти несколько раундов, решая алгоритмические пазлы. Звучит сложно, а на деле ещё сложнее. Огромное количество участников, разные технологии и языки, соревнование по количеству набранных баллов, в общем, настоящая геймификация и ЦА идеальна. У IBM и Miscrosoft тоже похожие состязания есть.

Работает ли?
Да. Но, конечно, это особенная ниша — крупные и классные компании могут позволить себе вовлекать потенциальных кандидатов в решение подобных задач, а также в работу над разными кейсами. Сейчас это модно, как и хакатоны.

Гордона Рамзи на вас нет
Есть у нас и ещё один кейс — My Marriot Game. Это игра, в которую можно было играть прямо на Facebook от Marriot. Сейчас она уже закрыта, так что можем только скриншотом поделиться:



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

Работает ли?
Работает и выполняет свою функцию. Наверняка Marriot ещё очень грамотно собирали данные об игроках. Не нашли точных цифр о том, сколько людей устроились благодаря игре, но раз до сих пор о ней столько говорят, Marriot'у ставим пять :)

Поделись задачею своей
Ещё есть платформы кейсов наподобие Hackertrail — ресурс, на котором работодатели и кандидаты находят друг друга. Кандидат откликается на позицию, решает какой-нибудь кейс, например, взламывая сайт (бугага!). И вот он уже сидит на собеседовании в компании своей мечты и он молодец. Платформа простейшая. И даже как таковая не нужна — вы же можете выкладывать имеющиеся у вас задачи для потенциальных кандидатов где угодно. На своей карьерной странице или даже в социальных сетях. Нужно только придумать кейс.

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


// Понятно, что все такие молодцы! А теперь расскажите про стартапы в сфере геймификации рекрутмента.


Knack — приложение, в котором вы (если вы кандидат) сначала отвечаете на простейшие вопросы, а потом вам предлагают установить какие-нибудь игру. Затем все ваши показатели активности, ловкости и так далее анализируются.

Работодателям предлагают использовать эту площадку вообще в любых целях: карьерное консультирование, рекрутмент, вовлечение персонала, работа в команде. Компании нужно только зарегистрироваться и выбрать (или создать игры), соответствующую её потребностям.

Подобная платформа — Arctic Shores. Game-Based Assessments — снова несколько игр, в которые можно поиграть, чтобы потом ваши навыки оценили и куда-нибудь может позвали.

Ещё мы уже как-то рассказывали про Pymetrics и Pomello — сервисы, которые точно знают, какая позиция подойдёт кандидату и в какой компании. Если вы кандидат, то получить доступ к сервису можно бесплатно и начать играть – с помощью простых головоломок и задач сервис оценивает мотивацию, способность удерживать внимание и подсказывает, какая должность подходит кандидату больше всего.

Pomello — компании на этой платформе предлагают кандидатам пройти тесты и принять участие в играх (на 5-10 минут), которые позволяют понять, насколько он и компания-работодатель совместимы. Все «упражнения», как и в Pymetrics, сформированы на основе исследований, проведённых конкретно в каждой компании, на основе изучения особенностей работы команды.

И это работает?
Здесь нужно много раз и прям жирным шрифтом написать AI / ИИ / Искусственный интеллект. И трижды подчеркнуть. Как это сделано на лендингах всех этих стартапов.

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

Тем более в России, где на рынке HR-технологий пока только отстаивают свою позицию ATS, говорить о такого рода инструментах рано. Сложные и реально дорогие платформы геймификации только начинают внедрять модные западные компании, которые готовы к экспериментам и к встряске бюджета.

Уже сейчас на западе постепенно появляются также simulation-based assessments. И здесь уже речь идет о виртуальной реальности, носимых технологиях, технологиях анализирующих движения и язык тела — вот это всё и много больше.


// К какому выводу мы пришли?


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

И в этом процессе вот вообще далеко не всегда нужен искусственный интеллект (хотя мы ждем прорывных инструментов, что уж тут!). Начать можно с нетривиального технологического подхода к отбору. Как это сделали компании, о которых мы рассказали выше.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330280/


Unity: система сохранения для любого проекта

Понедельник, 05 Июня 2017 г. 19:48 + в цитатник
Игры надо сохранять. Сохраняемых сущностей может быть великое множество. Например, в последних выпусках TES и Fallout игра помнит расположение каждой закатившейся склянки. Необходимо решение, чтобы:
1) Написал один раз и используй в любом проекте для любых сущностей. Ну, насколько возможно.
2) Создал сущность — и она сохраняется сама собою, с минимумом дополнительных усилий.

Решение пришло из стана синглтонов. Не надоело ли вам писать один и тот же синглтон-код? А меж тем есть generic singleton. Вот как он выглядит для MonoBehaviour
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GenericSingleton : MonoBehaviour {
    static GenericSingleton instance;
    public static GenericSingleton Instance { get { return instance; } }

	void Awake () {
		if (instance && instance != this)
        {
            Destroy(this);
            return;
        }
        instance = this;
	}
}


public class TestSingletoneA : GenericSingleton {

	// Use this for initialization
	void Start () {
        Debug.Log("A");
	}
}


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

Вот код интерфейса модели. Он примечателен тем, что метод SetValues примет в качестве аргумента только модель такого же (или производного) типа. Не чудо ли?

/// 
/// Voloshin Game Framework: basic scripts supposed to be reusable
/// 
namespace VGF
{    
    //[System.Serializable]
    public interface AbstractModel where T : AbstractModel, new()
    {
        /// 
        /// Copy fields from target
        /// 
        /// Source model
        void SetValues(T model);            
    }

    public static class AbstratModelMethods
    {
        /// 
        /// Initialize model with source, even if model is null
        /// 
        /// 
        /// Target model, can be null
        /// Source model
        public static void InitializeWith(this T model, T source) where T: AbstractModel, new ()
        {
            //model = new T();
            if (source == null)
                return;
            model.SetValues(source);
        }
    }
}


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

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

А что делать с сохранением и загрузкой? Ведь нужно сохранять и загружать сразу всё. А писать для каждой новой сущности код для сохранения и загрузки в каком-нибудь SaveLoadManager — утомительно и легкозабываемо.

И тут на помощь приходят статики.
1) Абстрактный класс с protected функциями сохранения и загрузки
2) У него — статичная коллекция All, куда каждый экземпляр класса-потомка добавляется при инициализации
3) И статичные публичные функции сохранения и загрузки, внутри которых перебираются все экземпляры из All и вызываются конкретные методы сохранения и загрузки.

И вот какой код получается в результате.
using System.Collections.Generic;
using UnityEngine;

namespace VGF
{
    /* Why abstract class instead of interface?
     * 1) Incapsulate all save, load, init, loadinit functions inside class, make them protected, mnot public
     * 2) Create static ALL collection and static ALL methods
     * */
     //TODO: create a similar abstract class for non-mono classes. For example, PlayerController needs not to be a MonoBehaviour
    /// 
    /// Abstract class for all MonoBehaiour classes that support save and load
    /// 
    public abstract class SaveLoadBehaviour : CachedBehaviour
    {
        /// 
        /// Collection that stores all SaveLoad classes in purpose of providing auto registration and collective save and load
        /// 
        static List AllSaveLoadObjects = new List();

        protected override void Awake()
        {
            base.Awake();
            Add(this);
        }

        static void Add(SaveLoadBehaviour item)
        {
            if (AllSaveLoadObjects.Contains(item))
            {
                Debug.LogError(item + "  element is already in All list");
            }
            else
                AllSaveLoadObjects.Add(item);
        }

        public static void LoadAll()
        {
            foreach (var item in AllSaveLoadObjects)
            {
                if (item == null)
                {
                    Debug.LogError("empty element in All list");
                    continue;
                }
                else
                    item.Load();
            }
        }

        public static void SaveAll()
        {
            Debug.Log(AllSaveLoadObjects.Count);
            foreach (var item in AllSaveLoadObjects)
            {
                if (item == null)
                {
                    Debug.LogError("empty element in All list");
                    continue;
                }
                else
                    item.Save();
            }
        }

        public static void LoadInitAll()
        {
            foreach (var item in AllSaveLoadObjects)
            {
                if (item == null)
                {
                    Debug.LogError("empty element in All list");
                    continue;
                }
                else
                    item.LoadInit();
            }
        }

        protected abstract void Save();

        protected abstract void Load();

        protected abstract void Init();

        protected abstract void LoadInit();
    }
}

using UnityEngine;

namespace VGF
{
    /// 
    /// Controller for abstract models, providing save, load, reset model
    /// 
    /// AbstractModel child type
    public class GenericModelBehaviour : SaveLoadBehaviour where T: AbstractModel, new()
    {
        [SerializeField]
        protected T InitModel;
        //[SerializeField]
        protected T CurrentModel, SavedModel;

        protected override void Awake()
        {
            base.Awake();
            //Init();
        }

        void Start()
        {
            Init();
        }

        protected override void Init()
        {
            //Debug.Log(InitModel);
            if (InitModel == null)
                return;
            //Debug.Log(gameObject.name + " : Init current model");
            if (CurrentModel == null)
                CurrentModel = new T();
            CurrentModel.InitializeWith(InitModel);
            //Debug.Log(CurrentModel);
            //Debug.Log("Init saved model");
            SavedModel = new T();
            SavedModel.InitializeWith(InitModel);
        }

        protected override void Load()
        {
            //Debug.Log(gameObject.name + "   saved");
            LoadFrom(SavedModel);
        }

        protected override void LoadInit()
        {
            LoadFrom(InitModel);
        }

        void LoadFrom(T source)
        {
            if (source == null)
                return;
            CurrentModel.SetValues(source);
        }

        protected override void Save()
        {
            //Debug.Log(gameObject.name + "   saved");
            if (CurrentModel == null)
                return;
            if (SavedModel == null)
                SavedModel.InitializeWith(CurrentModel);
            else
                SavedModel.SetValues(CurrentModel);
        }
    }
}


Примеры унаследованных конкретных классов:
public abstract class AbstractAliveController : GenericModelBehaviour, IAlive
    {
        //TODO: create separate unity implementation where put all the [SerializeField] attributes
        [SerializeField]
        bool Immortal;
        static Dictionary All = new Dictionary();
        public static bool GetAliveControllerForTransform(Transform tr, out AbstractAliveController aliveController)
        {
            return All.TryGetValue(tr, out aliveController);
        }

        DamageableController[] BodyParts;
        
        public bool IsAlive { get { return Immortal || CurrentModel.HealthCurrent > 0; } }
        public bool IsAvailable { get { return IsAlive && myGO.activeSelf; } }
        public virtual Vector3 Position { get { return myTransform.position; } }

        public static event Action OnDead;
        /// 
        /// Sends the current health of this alive controller
        /// 
        public event Action OnDamaged;

        //TODO: create 2 inits
        protected override void Awake()
        {
            base.Awake();
            All.Add(myTransform, this);            
        }

        protected override void Init()
        {
            InitModel.Position = myTransform.position;
            InitModel.Rotation = myTransform.rotation;
            base.Init();
            
            BodyParts = GetComponentsInChildren();
            foreach (var bp in BodyParts)
                bp.OnDamageTaken += TakeDamage;
        }

        protected override void Save()
        {
            CurrentModel.Position = myTransform.position;
            CurrentModel.Rotation = myTransform.rotation;
            base.Save();
        }

        protected override void Load()
        {
            base.Load();
            LoadTransform();
        }

        protected override void LoadInit()
        {
            base.LoadInit();
            LoadTransform();
        }

        void LoadTransform()
        {
            myTransform.position = CurrentModel.Position;
            myTransform.rotation = CurrentModel.Rotation;
            myGO.SetActive(true);
        }

        public void Respawn()
        {
            LoadInit();
        }

        public void TakeDamage(int damage)
        {
            if (Immortal)
                return;
            CurrentModel.HealthCurrent -= damage;
            OnDamaged.CallEventIfNotNull(CurrentModel.HealthCurrent);
            if (CurrentModel.HealthCurrent <= 0)
            {
                OnDead.CallEventIfNotNull(this);
                Die();
            }
        }

        public int CurrentHealth
        {
            get { return CurrentModel == null? InitModel.HealthCurrent: CurrentModel.HealthCurrent; }
        }

        protected abstract void Die();

    }


namespace VGF.Action3d
{
    [System.Serializable]
    public class AliveModelTransform : AliveModelBasic, AbstractModel
    {
        [HideInInspector]
        public Vector3 Position;
        [HideInInspector]
        public Quaternion Rotation;

        public void SetValues(AliveModelTransform model)
        {
            Position = model.Position;
            Rotation = model.Rotation;
            base.SetValues(model);
        }
    }
}


Недостатки решения и способы их исправления.
1) Сохраняется (перезаписывается) всё. Даже то, что не было изменено.
Возможное решение: проверять перед сохранением равенство полей у исходной и текущей моделей и сохранять только при необходимости.
2) Загрузка из файла. Из json, например. Вот есть список моделей. Как загрузчику узнать, какой класс надо создать для этого json-текста?
Возможное решение: сделать словарь где регистрировать типы хардкодом. При загрузке из json берется строковой идентификатор типа и инстанцируется объект нужного класса. При сохранении объект проверяет, есть ли в словаре ключ его типа, и выдает сообщение/ошибку/исключение. Это позволит стороннему программисту не забыть добавить новый тип в словарь.

Посмотреть мой код с этим и другими хорошими решениями можно здесь (проекты в начальной стадии):
FPSProject
Невероятные космические похождения изворотливых котосминогов

Замечания, улучшения, советы — приветствуются.
Предложения помощи и совместного творчества приветствуются.
Предложения о работе крайне приветствуются.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330278/


Метки:  

Анонс RamblerElixir #3

Понедельник, 05 Июня 2017 г. 18:55 + в цитатник
image

Приглашаем разработчиков, тимлидов и всех, кто так или иначе связан с разработкой на Elixir, принять участие в RamblerElixir Meetup, который состоится 14 июня 19:00, в среду, на уютной мансарде Rambler&Co.

В программе встречи три доклада:

1. Лингвистическая относительность – Станислав Герман (Rambler&Co)
У каждого языка есть принципы, на которых он основан. Для Erlang — «Let it crash and let someone else deal with it», для Ruby — «less astonishing» и «programmer happiness», в Python такие принципы как «Explicit is better than implicit», сформулированы в «Zen of Python». В то время, как одни взгляды нам близки, а другие мы не принимаем, они формируют не только дизайн языка и архитектуру библиотек и систем, которые разрабатываются на этом языке, но и сообщество разработчиков вокруг этого языка. В своем докладе я рассмотрю какие принципы заложены в Elixir, чем знание этих принципов полезно разработчику, и как они влияют на код, который мы пишем и используем в своей работе.

2. Трюки с ETS – Алексей Никитин (Bookmate)
У меня сложилось впечатление, что в Elixir-сообществе незаслуженно подзабыли про такую штуку как ETS. Про этот мощный инструмент, который предоставляет платформа. Я расскажу несколько хитростей, которые сделают работу с ETS более удобной и как решить некоторые проблемы, которые возникают при работе с ним. Доклад будет интересен в основном новичкам. Но, возможно, и опытные разработчики смогут узнать что-то новое.

3. Фреймворк для работы с нейронными сетями на Elixir – Алексей Овчинников (Sirin)
Нейронные сети набирают всё большую популярность в ИТ-индустрии, во многом благодаря применению графических сопроцессоров. Существует множество библиотек для реализации нейронных сетей, большинство из которых написано на Python или C. Вместе с тем платформа Erlang OTP позволяет без лишних усилий реализовать отказоустойчивое приложение с высокой степенью распараллеливания вычислений, и, на наш взгляд, идеально подходит для реализации высоко интегрированного решения для обработки потокового видео с помощью нейронных сетей. Также в библиотеке реализован медиа-сервер для приёма потокового видео. Таким образом пользователь может сосредоточиться на конфигурации нейронной сети, не отвлекаясь на технические детали обработки видео.

Регистрация здесь — rambler-co-e-org.timepad.ru/event/505943

Сбор гостей в 18:30.
Начало первого доклада в 19:00.
Место проведения – Мансарда Rambler&Co, которая расположена по адресу ул. Варшавское шоссе д.9 стр.1

Приходите, будет интересно!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330256/


Метки:  

Анонс RamblerElixir #3

Понедельник, 05 Июня 2017 г. 18:51 + в цитатник
image

Приглашаем разработчиков, тимлидов и всех, кто так или иначе связан с разработкой на Elixir, принять участие в RamblerElixir Meetup, который состоится 14 июня 19:00, в среду, на уютной мансарде Rambler&Co.

В программе встречи три доклада:

1. Лингвистическая относительность – Станислав Герман (Rambler&Co)
У каждого языка есть принципы, на которых он основан. Для Erlang — «Let it crash and let someone else deal with it», для Ruby — «less astonishing» и «programmer happiness», в Python такие принципы как «Explicit is better than implicit», сформулированы в «Zen of Python». В то время, как одни взгляды нам близки, а другие мы не принимаем, они формируют не только дизайн языка и архитектуру библиотек и систем, которые разрабатываются на этом языке, но и сообщество разработчиков вокруг этого языка. В своем докладе я рассмотрю какие принципы заложены в Elixir, чем знание этих принципов полезно разработчику, и как они влияют на код, который мы пишем и используем в своей работе.

2. Трюки с ETS – Алексей Никитин (Bookmate)
У меня сложилось впечатление, что в Elixir-сообществе незаслуженно подзабыли про такую штуку как ETS. Про этот мощный инструмент, который предоставляет платформа. Я расскажу несколько хитростей, которые сделают работу с ETS более удобной и как решить некоторые проблемы, которые возникают при работе с ним. Доклад будет интересен в основном новичкам. Но, возможно, и опытные разработчики смогут узнать что-то новое.

3. Фреймворк для работы с нейронными сетями на Elixir – Алексей Овчинников (Sirin)
Нейронные сети набирают всё большую популярность в ИТ-индустрии, во многом благодаря применению графических сопроцессоров. Существует множество библиотек для реализации нейронных сетей, большинство из которых написано на Python или C. Вместе с тем платформа Erlang OTP позволяет без лишних усилий реализовать отказоустойчивое приложение с высокой степенью распараллеливания вычислений, и, на наш взгляд, идеально подходит для реализации высоко интегрированного решения для обработки потокового видео с помощью нейронных сетей. Также в библиотеке реализован медиа-сервер для приёма потокового видео. Таким образом пользователь может сосредоточиться на конфигурации нейронной сети, не отвлекаясь на технические детали обработки видео.

Регистрация здесь — rambler-co-e-org.timepad.ru/event/505943

Сбор гостей в 18:30.
Начало первого доклада в 19:00.
Место проведения – Мансарда Rambler&Co, которая расположена по адресу ул. Варшавское шоссе д.9 стр.1

Приходите, будет интересно!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330268/


Метки:  

Анонс RamblerElixir #3

Понедельник, 05 Июня 2017 г. 18:50 + в цитатник
image

Приглашаем разработчиков, тимлидов и всех, кто так или иначе связан с разработкой на Elixir, принять участие в RamblerElixir Meetup, который состоится 14 июня 19:00, в среду, на уютной мансарде Rambler&Co.

В программе встречи три доклада:

1. Лингвистическая относительность – Станислав Герман (Rambler&Co)
У каждого языка есть принципы, на которых он основан. Для Erlang — «Let it crash and let someone else deal with it», для Ruby — «less astonishing» и «programmer happiness», в Python такие принципы как «Explicit is better than implicit», сформулированы в «Zen of Python». В то время, как одни взгляды нам близки, а другие мы не принимаем, они формируют не только дизайн языка и архитектуру библиотек и систем, которые разрабатываются на этом языке, но и сообщество разработчиков вокруг этого языка. В своем докладе я рассмотрю какие принципы заложены в Elixir, чем знание этих принципов полезно разработчику, и как они влияют на код, который мы пишем и используем в своей работе.

2. Трюки с ETS – Алексей Никитин (Bookmate)
У меня сложилось впечатление, что в Elixir-сообществе незаслуженно подзабыли про такую штуку как ETS. Про этот мощный инструмент, который предоставляет платформа. Я расскажу несколько хитростей, которые сделают работу с ETS более удобной и как решить некоторые проблемы, которые возникают при работе с ним. Доклад будет интересен в основном новичкам. Но, возможно, и опытные разработчики смогут узнать что-то новое.

3. Фреймворк для работы с нейронными сетями на Elixir – Алексей Овчинников (Sirin)
Нейронные сети набирают всё большую популярность в ИТ-индустрии, во многом благодаря применению графических сопроцессоров. Существует множество библиотек для реализации нейронных сетей, большинство из которых написано на Python или C. Вместе с тем платформа Erlang OTP позволяет без лишних усилий реализовать отказоустойчивое приложение с высокой степенью распараллеливания вычислений, и, на наш взгляд, идеально подходит для реализации высоко интегрированного решения для обработки потокового видео с помощью нейронных сетей. Также в библиотеке реализован медиа-сервер для приёма потокового видео. Таким образом пользователь может сосредоточиться на конфигурации нейронной сети, не отвлекаясь на технические детали обработки видео.

Регистрация здесь — rambler-co-e-org.timepad.ru/event/505943

Сбор гостей в 18:30.
Начало первого доклада в 19:00.
Место проведения – Мансарда Rambler&Co, которая расположена по адресу ул. Варшавское шоссе д.9 стр.1

Приходите, будет интересно!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330264/


Метки:  

Анонс RamblerElixir #3

Понедельник, 05 Июня 2017 г. 18:49 + в цитатник
image

Приглашаем разработчиков, тимлидов и всех, кто так или иначе связан с разработкой на Elixir, принять участие в RamblerElixir Meetup, который состоится 14 июня 19:00, в среду, на уютной мансарде Rambler&Co.

В программе встречи три доклада:

1. Лингвистическая относительность – Станислав Герман (Rambler&Co)
У каждого языка есть принципы, на которых он основан. Для Erlang — «Let it crash and let someone else deal with it», для Ruby — «less astonishing» и «programmer happiness», в Python такие принципы как «Explicit is better than implicit», сформулированы в «Zen of Python». В то время, как одни взгляды нам близки, а другие мы не принимаем, они формируют не только дизайн языка и архитектуру библиотек и систем, которые разрабатываются на этом языке, но и сообщество разработчиков вокруг этого языка. В своем докладе я рассмотрю какие принципы заложены в Elixir, чем знание этих принципов полезно разработчику, и как они влияют на код, который мы пишем и используем в своей работе.

2. Трюки с ETS – Алексей Никитин (Bookmate)
У меня сложилось впечатление, что в Elixir-сообществе незаслуженно подзабыли про такую штуку как ETS. Про этот мощный инструмент, который предоставляет платформа. Я расскажу несколько хитростей, которые сделают работу с ETS более удобной и как решить некоторые проблемы, которые возникают при работе с ним. Доклад будет интересен в основном новичкам. Но, возможно, и опытные разработчики смогут узнать что-то новое.

3. Фреймворк для работы с нейронными сетями на Elixir – Алексей Овчинников (Sirin)
Нейронные сети набирают всё большую популярность в ИТ-индустрии, во многом благодаря применению графических сопроцессоров. Существует множество библиотек для реализации нейронных сетей, большинство из которых написано на Python или C. Вместе с тем платформа Erlang OTP позволяет без лишних усилий реализовать отказоустойчивое приложение с высокой степенью распараллеливания вычислений, и, на наш взгляд, идеально подходит для реализации высоко интегрированного решения для обработки потокового видео с помощью нейронных сетей. Также в библиотеке реализован медиа-сервер для приёма потокового видео. Таким образом пользователь может сосредоточиться на конфигурации нейронной сети, не отвлекаясь на технические детали обработки видео.

Регистрация здесь — rambler-co-e-org.timepad.ru/event/505943

Сбор гостей в 18:30.
Начало первого доклада в 19:00.
Место проведения – Мансарда Rambler&Co, которая расположена по адресу ул. Варшавское шоссе д.9 стр.1

Приходите, будет интересно!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330272/


Метки:  

IBM Watson и кибербезопасность: служба быстрого реагирования, которая работает круглосуточно

Понедельник, 05 Июня 2017 г. 18:21 + в цитатник


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

Но, несмотря на принимаемые меры, проблемы зачастую возникают даже у самых, казалось бы, защищенных компаний и организаций. Недавно стало известно, например, что из-за массового распространения в сети вируса WCry в некоторых областях России даже пришлось отменить выдачу водительских прав. Этот вирус скомпрометировал многие компьютеры, которые без разблокировки практически невозможно использовать. Что будет, если сети достаточно крупной коммерческой компании заблокирует вирус? Такая компания потерпит многомиллионные, а то и миллиардные убытки. Так оно и есть, сейчас остановить эпидемию WannaCry удалось только чудом, а убытки еще никто не подсчитывал.

Стандартные инструменты защиты не всегда справляются с угрозой, но когнитивная система может значительно все упростить, управляя кибербезопасностью предприятия. У корпорации IBM есть такой продукт, это сервис Watson for Cyber Security. Подробнее об этом — ниже.




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

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





На основе сервиса Watson for Cyber Security работает один из продуктов платформы IBM QRadar — IBM Qradar Advisor with Watson.

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

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

Работа IBM Qradar Advisor with Watson ведется в режиме 24/7/365. Она состоит из четырех ключевых элементов:
1. Обнаружение инцидента и выявление его причины. При этом когнитивная система активно работает с данными мониторинга работы сети, накопленных Qradar;
2. Далее идет поиск по базе данных самой когнитивной системы, для обнаружения информации, которая относится к обнаруженной аномалии или происшествию;
3. Далее Qradar Advisor отправляет информацию о проблеме в Watson for Cyber Security, для фиксирования этих данных и изучения проблемы;
4. Идет идентификация угрозы и поиск подходящей стратегии борьбы с ней.


Кстати, в 2015 году институт Понемона проводил изучение особенностей работы различных компаний с QRadar. В рамках этого исследования был проведен опрос. Представителей компаний, которые согласились принять участие в опросе, спросили, работали ли они с дополнительными сервисами по сетевой безопасности после внедрения Qradar. 70% опрошенных ответили, что нет, причем 62% заявили, что если бы хотели, без проблем сменили бы продукт, но такого желания не возникало. 43% опрошенных заявили, что почувствовали эффект от работы с сервисом уже через несколько дней, для 27% этот эффект проявился в течение недели.

В целом, достоинства QRadar, включая и когнитивный сервис, можно выделить в следующие пункты:
• Единая архитектура для анализа журналов, сетевых потоков, пакетов, уязвимостей, данных о пользователях и ресурсах
• Анализ корреляции в реальном времени с использованием Sense Analytics для выявления наиболее серьезных угроз, атак и уязвимостей
• Расстановка приоритетов и выделение важнейших инцидентов среди миллиардов единиц данных, получаемых ежедневно
• Прогнозный анализ имеющихся рисков, вызванных некорректной настройкой устройств и известными уязвимостями
• Автоматическое реагирование на инциденты
• Автоматическое выполнение нормативных требований за счет возможностей сбора данных, определения их корреляции и составления отчетности



По оценке института Понемона, обычная компания тратит более 20 000 в год на работу с сетевыми угрозами, как внешними, так и внутренними. Это огромное количество времени, и его можно сэкономить, если автоматизировать все процессы мониторинга.

Watson for Cyber Security оперирует в своей работы данными из 100 000 задокументированных уязвимостей ПО, содержащихся в базе данных IBM X-Force Exchange. Также в распоряжении когнитивной системы более 10 000 различных документов и 700 000 записей в блогах специалистов по информационной безопасности, публикуемых каждый год. В случае необходимости все эти данные можно быстро структурировать и получить необходимую информацию по определенной тематике. Структурированные данные, сформированные Watson for CyberSecurity, поступают в сервис IBM QRadar, о чем уже говорилось выше. Если говорить об эффективности работы такой системы, то она может анализировать тысячи инцидентов в день, сортируя ложные срабатывания и актуальные проблемы с безопасностью.

Вскоре Watson for Cyber Security станет частью новой платформы Cognitive Security Operations Centre (SOC), которая окончательно объединит когнитивные технологии и операции в сфере сетевой безопасности. Ключевым элементом платформы является IBM BigFix Detect. Это решение, позволяющее отследить атаку, использует своеобразную «машину времени» для обнаружения начальной точки, где все началось. Для конечного пользователя это означает возможность быстро, очень быстро отвечать на возникающие угрозы, включая локальные сети и «облака». Другиекомпоненты SOC — это IBM Security, X-Force Exchange и i2. Доступ к этой унифицированной платформе IBM планирует предоставлять в качестве сервиса, который так и будет называться SOC-as-a-Service.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330250/


Метки:  

В качестве приветствия

Понедельник, 05 Июня 2017 г. 17:39 + в цитатник
Всем привет.

С сегодняшнего дня Райффайзенбанк начинает свое вещание на Хабрахабр. За последние 2-3 года мы многое сделали с точки зрения внутренних преобразований, запуска и реализации ИТ-проектов. И нам есть, о чем рассказать.

Для начала пара слов об истории.
Райффайзенбанк работает на российском рынке уже более 20 лет, на сегодняшний день мы крупнейшая иностранная банковская группа в России. В 2016 году банк начал активно поддерживать стартапы в области финтеха. Мы работаем, в том числе с фондами Сколково и РВК. Райффайзенбанк стал партнером федерального стартап-акселератора GenerationS (проводится РВК) и учредил специальную номинацию Raiffeisen Award для участников, разрабатывающих продукты и решения для финансовой отрасли. В этом году мы провели первый хакатон GetApp. Как это было, вы можете посмотреть в фотоальбоме и отчетном видео.

Для увеличения надежности и доступности банковских сервисов, а также для развития банковских систем, продуктов и сервисов мы используем широкий спектр технологий. Например, разработчики работают на платформах: Tomcat/RHEL7, Scala stack, IBM WAS/AIX, IIS/Win2012, RPG(for AS400), IBM BPM/WPS, IBM ESB и пишут на Python, Kotlin, Swift, Java, C/C++ и С#. Мы используем несколько платформ для хранения и обработки данных большого объема и различной структуры: DB2, Oracle, SQL-server, PosgreSQL. Для задач, связанных с Big Data, используем Hadoop. Также работаем на крупных промышленных платформах, таких как Oracle Siebel, Mysis, BankFusion, MIDAS SAS, TSYS Prime. Ну, и в этом году внедряем у себя Jira/Confluence/Bamboo/Bitbucket/SonarCube.

Мы верим в культуру и философию Agile, активно применяем практики SCRUM, Kanban, TDD, для развития наших команд ежемесячно проводим внутренние митапы по Java и .Net, а для расширения кругозора ИТ и банка в целом – регулярно информируем о развитии рынка, новых технологиях и подходах к внедрению решений

Подробнее обо всем этом обязательно расскажем в следующих постах.

Читайте наш блог!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330248/


Метки:  

Взнос за публикацию игры через Steam Direct установлен в размере $100

Понедельник, 05 Июня 2017 г. 16:20 + в цитатник


Компания Valve приняла решение о размере взноса для разработчиков, которые планируют публиковать свои игры в каталоге Steam. Эти взносы будут взиматься после того, как текущая система Greenlight будет заменена на Steam Direct. Взнос будет возвращаемым и в принципе, для разработчиков почти ничего не меняется, поскольку в текущей системе размер возвращаемой оплаты за публикацию игры такой же.

По словам представителей Valve, решение было не таким уж и простым, члены команды предлагали изменить размер взносов, причем рассматривались суммы от $100 до $5000 (нет, ноль здесь не лишний). После обсуждения было решено остановиться на $500, но мнение самого сообщества было решающим — в Valve прислушались к нему и решили не изменять размер взноса, чтобы позволить разработчикам без проблем публиковать свои игры.

«Наше внутреннее обсуждение привело к мысли о поднятии суммы взноса до $500, но разговоры в сообществе привели к тому, что мы решили сделать взнос минимально возможным», — говорится в обращении.

Систему Steam Greenlight было решено заменить еще в феврале, для того, чтобы у разработчиков был более прямой и простой путь публиковать свои игры. Собственно, намерения Valve отражены в самом названии нового сервиса — Steam Direct. Для работы нужно зарегистрироваться, после чего внести деньги, о чем и говорилось выше. «Мы попросим новых разработчиков поработать с документами, пройдя процедуру верификации и добавив налоговую информацию, все это похоже на открытие счета в банке», — объясняли в Valve в феврале.

После того, как пройдены все формальности и внесена оплата за игру (при публикации нового тайтла взнос снова придется заплатить) разработчик может начинать процедуру добавления. Правда, пока что нет информации о том, каким образом разработчик может получить свой возвращаемый взнос обратно, хотя представители Valve сообщили одному из интернет-СМИ о том, что деньги разработчик получит по достижению уровня продаж в $1000. В блоге компании пока этой информации нет.

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

Теперь пользователи могут принимать участие в выделении игр в Steam, а игроки — искать среди них тех, кто будет представлять их интересы. В Valve также решили ограничить полномочия кураторов: «Мы решили оставить деятельность кураторов функцией строго по выбору: комментарии кураторов могут повлиять на работу алгоритма только тех игроков, которые открыто заявили о желании подписаться на этих кураторов».

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

image

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

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

В дальнейшем Valve обещает постепенно дорабатывать и улучшать Steam Direct. Новая система упрощает задачу поиска игр, показывая те из них, которые должны понравиться конкретному игроку. Больше значения придается индивидуальным интересам. Создатели Steam Direct просят сразу сообщать о проблемах, как только они появляются, чтобы была возможность их исправить.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330244/


Метки:  

[Перевод] Обзор исследований в области глубокого обучения: обработка естественных языков

Понедельник, 05 Июня 2017 г. 15:54 + в цитатник


Это третья статья из серии “Обзор исследований в области глубокого обучения” (Deep Learning Research Review) студента Калифорнийского университета в Лос-Анджелесе Адита Дешпанда (Adit Deshpande). Каждые две недели Адит публикует обзор и толкование исследований в определенной области глубинного обучения. В этот раз он сосредоточил свое внимание на применении глубокого обучения для обработки текстов на естественном языке.

Введение в обработку естественных языков


Введение


Под обработкой естественных языков (Natural Language Processing, NLP) понимается создание систем, обрабатывающих или “понимающих” язык с целью выполнения определенных задач. Эти задачи могут включать:

  • Формирование ответов на вопросы (Question Answering) (то, что делают Siri, Alexa и Cortana)
  • Анализ эмоциональной окраски высказываний (Sentiment Analysis) (определение, имеет ли высказывание положительную или отрицательную коннотацию)
  • Нахождение текста, соответствующего изображению (Image to Text Mappings) (генерация подписи к входному изображению)
  • Машинный перевод (Machine Translation) (перевод абзаца текста с одного языка на другой)
  • Распознавание речи (Speech Recognition)
  • Морфологическая разметка (Part of Speech Tagging) (определение частей речи в предложении и их аннотирование)
  • Извлечение сущностей (Name Entity Recognition)

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

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


Мы понимаем, что приставка (prefix) un- означает отрицание, и знаем, что -ed может означать время, к которому относится данное слово (в данном случае – прошедшее время). Распознав значение однокоренного слова interest, мы легко можем сделать вывод о значении и эмоциональной окраске всего слова. Вроде бы просто. Тем не менее, если принять во внимание все многообразие приставок и суффиксов английского языка, понадобится очень умелый лингвист, чтобы понять все возможные комбинации и их значения.


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

Как использовать глубокое обучение


В основе глубокого обучения лежит обучение представлениям. Например, сверточные нейронные сети (Convolutional Neural Network, CNN) включают в себя объединение различных фильтров, предназначенных для классификации объектов по категориям. Здесь мы попытаемся применить похожий подход, создавая представления слов в больших наборах данных.

Структура статьи


Эта статья организована таким образом, чтобы мы могли пройти по основным элементам, из которых можно строить глубокие сети для NLP, а затем перейти к обсуждению некоторых приложений, которых касаются недавние научные работы. Ничего страшного, если вы не будете точно знать, почему, например, мы используем RNN, или чем полезен LSTM, но, надеюсь, изучив эти работы, вы поймете, почему глубокое обучение так важно для NLP.

Векторы слов


Так как глубокое обучение не может жить без математики, представим каждое слово в виде d-мерного вектора. Возьмем d=6.

Теперь подумаем, как заполнить значения. Мы хотим, чтобы вектор был заполнен таким образом, чтобы он каким-то образом представлял слово и его контекст, значение или семантику. Один из способов – построить матрицу совместной встречаемости (cooccurrence matrix). Рассмотрим следующее предложение:

Мы хотим получить векторное представление для каждого слова.


Матрица совместной встречаемости содержит количество раз, которое каждое слово встретилось в корпусе (обучающем наборе) после каждого другого слова этого корпуса.



Строки этой матрицы могут служить в качестве векторных представлений наших слов.

Обратите внимание, что даже из этой простой матрицы мы можем почерпнуть довольно важные сведения. Например, заметим, что векторы слов “love” и “like” содержат единицы в ячейках, отвечающих за их соседство с существительными (“NLP” и “dogs”). У них также стоит “1” там, где они соседствуют с “I”, показывая, что это слово, скорее всего, глагол. Можете себе представить, насколько проще выявлять подобные схожие черты, когда набор данных больше, чем одно предложение: в этом случае векторы таких глаголов, как “love”, “like” и других синонимов, будут похожи, так как эти слова будут использоваться в схожих контекстах.

Хорошо для начала, но здесь мы обращаем внимание, что размерность вектора каждого слова будет линейно возрастать в зависимости от размера корпуса. В случае миллиона слов (что немного для стандартных задач NLP), мы получили бы матрицу размерности миллион на миллион, которая, к тому же, была бы очень разреженной (с большим количеством нулей). Это определенно не лучший вариант с точки зрения эффективности хранения данных. В вопросе нахождения оптимального векторного представления слов было сделано несколько серьезных подвижек. Самая известная из них – Word2Vec.

Word2Vec


Главная цель всех методов инициализации вектора слова – хранить в этом векторе как можно больше информации, сохраняя разумную размерность (в идеале, от 25 до 1000). В основе Word2Vec лежит идея научиться прогнозировать окружающие слова для каждого слова. Рассмотрим предложение из предыдущего примера: “I love NLP and I like dogs”. Сейчас нас интересуют только три первые слова. Пусть размер нашего окна и будет равен трем.

Теперь мы хотим взять центральное слово “love” и предсказать слова, идущие до и после него. Как же мы это осуществим? Конечно же, с помощью максимизации и оптимизации функции! Формально наша функция пытается максимизировать логарифмическую вероятность каждого слова-контекста для текущего центрального слова.

Изучим вышеприведенную формулу подробнее. Из нее следует, что мы будем складывать логарифмическую вероятность совместной встречаемости как “I” и “love”, так и “NLP” и “love” (в обоих случаях “love” – центральное слово). Переменная T означает количество обучающих (training) предложений. Рассмотрим логарифмическую вероятность поближе.

$V_c$ – векторное представление центрального слова. У каждого слова есть два векторных представления: $U_o$ и $U_w$, одно для случая, когда слово занимает центральную позицию, другое для случая, когда это слово – “внешнее”. Векторы обучаются методом стохастического градиентного спуска. Это определенно одно из самых трудных для понимания уравнений, так что если вам все еще трудно представить себе, что происходит, можно найти дополнительную информацию здесь и здесь.

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

(Дополнительно: дальше авторы работы подробно рассказывают о том, как с помощью негативного семплирования (negative sampling) и взятия подвыборок (subsampling) можно получить более точные векторы слов).

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

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

Бонус: еще один классный метод инициализации векторов слов – GloVe (Global Vector for Word Representation) (сочетает идеи матрицы совместной встречаемости с Word2Vec).

Рекуррентные нейронные сети (Recurrent Neural Networks, RNN)


Теперь посмотрим, как с нашими векторами будет работать рекуррентная нейронная сеть. RNN – палочка-выручалочка для большинства современных задач обработки естественного языка. Главное преимущество RNN в том, что они могут эффективно использовать данные с предыдущих шагов. Вот так выглядит маленький кусочек RNN:

Внизу изображены векторы слов ($x_t, x_{t-1}, x_{t+1}$). У каждого вектора на каждом шаге есть скрытый вектор состояния (hidden state vector) ($h_t, h_{t-1}, h_{t+1}$). Будем называть эту пару модулем (module).

Скрытое состояние в каждом модуле RNN – это функция от вектора слова и вектора скрытого состояния с прошлого шага.

Если мы приглядимся к верхним индексам, то увидим, что здесь есть матрица весов $W^{hx}$, которую мы умножаем на входное значение, и есть рекуррентная матрица весов $W^{hh}$, которая умножается на вектор скрытого состояния с предыдущего шага. Имейте в виду, что эти рекуррентные матрицы весов на каждом шаге одинаковы. Это ключевой момент RNN. Если тщательно обдумать, то этот подход значительно отличается от, скажем, традиционных двухслойных нейронных сетей. В этом случае у нас обычно выбирается отдельная матрица W для каждого слоя: $W_1$ и $W_2$. Здесь же рекуррентная матрица весов одна и та же для всей сети.

Для получения выходных значений каждого модуля (Yhat) служит еще одна матрица весов – $W^s$, умноженная на h.


Теперь давайте посмотрим со стороны и поймем, в чем состоят преимущества RNN. Наиболее явное отличие RNN от традиционной нейронной сети в том, что RNN принимает на вход последовательность входных данных (в нашем случае слов). Этим они отличаются, например, от типичных CNN, на вход которым подается целое изображение. Для RNN же входными данными может служить как короткое предложение, так и сочинение из пяти абзацев. Кроме того, порядок, в котором подаются данные, может влиять на то, как в процессе обучения меняются матрицы весов и векторы скрытых состояний. К концу обучения в векторах скрытых состояний должна накопиться информация из прошлых шагов.

Управляемые рекуррентные нейроны (Gated recurrent units, GRU)


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

В обычных RNN вектор скрытых состояний вычисляется по следующей формуле:

Метод GRU позволяет вычислять h(t) иначе. Вычисления разбиваются на три блока: фильтр обновления (update gate), фильтр сброса состояния (reset gate) и новый контейнер памяти (memory container). Обы фильтра – функции от входного векторного представления слова и скрытого состояния на предыдущем шаге.

Главное отличие состоит в том, что для каждого фильтра используются свои веса. Это обозначено разными верхними индексами. Фильтр обновления использует $W^z$ и $U^z$, а фильтр сброса состояния – $W^r$ и $U^r$.
Теперь рассчитаем контейнер памяти:

(пустой кружок здесь обозначает произведение Адамара).

Теперь, если присмотреться к формуле, то можно заметить, что если множитель фильтра сброса состояния близок к нулю, то и все произведение также приблизится к нулю, и таким образом, информация из предыдущего шага $h_{t-1}$ не будет учтена. В этом случай нейрон – всего лишь функция от нового вектора слова $x_{t-1}$.

Окончательную формулу h(t) можно записать как

$h_t$ – функция от всех трех компонентов: фильтра обновления, фильтра сброса состояния и контейнера памяти. Можно лучше понять это, визуализируя, что происходит с формулой, когда $z_t$ близится к 1 и когда $z_t$ близко к 0. В первом случай вектор скрытого состояния $h_t$ в большей степени зависит от предыдущего скрытого состояния, а текущий контейнер памяти не принимается во внимание, так как (1 – $z_t$) стремится к 0. Когда же $z_t$ близится к 1, новый вектор скрытого состояния $h_t$, наоборот, зависит в основном от контейнера памяти, а предыдущее скрытое состояние не учитывается. Итак, наши три компонента можно интуитивно описать следующим образом:

  • Фильтр обновления
    • Если $z_t$ ~ 1, то $h_t$ не принимает во внимание текущий вектор слова и просто копирует предыдущее скрытое состояние.
    • Если $z_t$ ~ 0, то $h_t$ не учитывает предыдущее скрытое состояние и зависит только от контейнера памяти.
    • Этот фильтр позволяет модели контролировать, как много информации от предыдущего скрытого состояния должно влиять на текущее скрытое состояние.
  • Фильтр сброса состояния
    • Если $r_t$ ~ 1, то контейнер памяти сохраняет информацию с предыдущего скрытого состояния.
    • Если $r_t$ ~ 0, то контейнер памяти не учитывает предыдущее скрытое состояние.
    • Этот фильтр позволяет отбросить часть информации, если в будущем она не будет нас интересовать.
  • Контейнер памяти: зависит от фильтра сброса состояния.

Приведем пример, иллюстрирующий работу GRU. Допустим, у нас есть следующие несколько предложений:

и вопрос: “Чему равна сумма двух чисел?” Так как предложение посередине никак не влияет на ответ, фильтры сброса и обновления позволят модели “забыть” это предложение и понять, что изменять скрытое состояние может только определенная информация (в данном случае, числа).

Нейроны с длительной кратковременной памятью (Long short-term memory, LSTM)


Если вы разобрались с GRU, то LSTM не составит для вас трудности. LSTM также состоит из последовательности фильтров.

LSTM определенно принимает на вход больше информации. Так как ее можно считать расширением GRU, я не буду разбирать ее подробно, а чтобы получить детальное описание каждого фильтра и каждого шага вычислений, вы можете обратиться к прекрасно написанному блог-посту Криса Олаха (Chris Olah). На текущий момент это самый популярный тьюториал по LSTM, и точно поможет тем из вас, кто ищет понятное и интуитивное объяснение работы этого метода.

Сравнение LSTM и GRU


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

Различие между двумя методами состоит в количестве фильтров (GRU – 2, LSTM – 3). Это влияет на количество нелинейностей, которое приходит от входных данных и в конечном итоге влияет на процесс вычислений. Кроме того, в GRU отсутствует ячейка памяти $c_t$, как в LSTM.

Перед тем, как углубиться в статьи


Хотел бы сделать небольшое замечание. Если и другие модели глубокого обучения, полезные в NLP. На практике иногда используются рекурсивные и сверточные нейронные сети, хотя они не так распространены, как RNN, которые лежат в основе большинства NLP-систем глубокого обучения.

Теперь, когда мы начали хорошо разбираться в рекуррентных нейронных сетях применительно к NLP, давайте ознакомимся с некоторыми работами в этой области. Так как NLP включает в себя несколько различных областей задач (от машинного перевода до формирования ответов на вопросы), мы могли бы рассмотреть довольно много работ, но я выбрал те три, которые нашел особенно информативными. В 2016 году случился ряд серьезных продвижений в области NLP, но начнем с одной работы 2015 года.

Нейронные сети с памятью (Memory Networks)


Введение


Первая работа, которую мы обсудим, оказала большое влияние на развитие области формирования ответов на вопросы. В этой публикации авторства Джейсона Вестона (Jason Weston), Сумита Чопры (Sumit Chopra) и Антуана Бордеса (Antoine Bordes) был впервые описан класс моделей под названием ”сети с памятью”.

Интуитивно-понятная идея состоит в следующем: для того, чтобы точно ответить на вопрос, относящийся к фрагменту текста, необходимо каким-то образом хранить предоставленную нам исходную информацию. Если бы я спросил вас: “Что означает аббревиатура RNN?”, вы смогли бы ответить мне, потому что информация, которую вы усвоили, читая первую часть статьи, сохранилась где-то в вашей памяти. Вам понадобилось бы лишь несколько секунд, чтобы найти эту информацию и озвучить ее. Я понятия не имею, как это получается у мозга, но мысль о том, что необходимо пространство для хранения этой информации, остается неизменной.

Сеть с памятью, описанная в данной работе, уникальна, так как у нее есть ассоциативная память, в которую она может писать и из которой она может читать. Интересно заметить, что подобную память не используют ни CNN, ни Q-Network (для обучения с подкреплением (reinforcement learning), ни традиционные нейронные сети. Это отчасти связано с тем, что задача формирования ответов на вопросы в большой степени полагается на способность моделировать или прослеживать отдаленные зависимости, например, следить за героями истории или запоминать последовательность событий. В CNN или Q-Networks память как бы встроена в веса системы, так как она обучается различным фильтрам или картам соответствий состояний и действий. На первый взгляд, можно было бы использовать RNN или LSTM, но обычно они не способны запоминать входные данные из прошлого (что является критичным для задач формирования ответов на вопросы).

Архитектура сети


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

Следующий шаг – взять представление в пространстве признаков I(x) и считать в память новую порцию входных данных x.

Память m можно рассматривать как подобие массива, составленного из отдельных блоков памяти $m_i$. Каждый такой блок $m_i$ может быть функцией от всей памяти m, представления в пространстве признаков I(x) и/или самого себя. Функция G может просто хранить все представление I(x) в блоке памяти mi. Функцию G можно изменить так, чтобы она обновляла память о прошлом на основе новых входных данных. Третий и четвертый шаги включают в себя чтение из памяти с учетом вопроса, чтобы найти представление признаков o, и его декодирование, чтобы получить окончательный ответ r.

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

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

Мы находим аргумент максимизации (argmax) оценочной функции, чтобы найти представление, наиболее соответствующее вопросу (можно также выбрать несколько блоков с самыми высокими оценками, не обязательно ровно один). Оценочная функция вычисляет матричное произведение между различными векторными представлениями вопроса и выбранным блоком (или блоками) памяти (подробности вы найдете в самой работе). Можете представить этот процесс как перемножение двух векторов из двух слов, чтобы определить, равны ли они. Выходное представление о затем передается RNN, LSTM или другой оценочной функции, которая вернет человекочитаемый ответ.

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

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


Tree-LSTM для анализа эмоциональной окраски высказываний


Введение


Следующая работа рассказывает о прогрессе в области анализа эмоциональной окраски – задачи определения, имеет высказывание положительную или отрицательную коннотацию (значение). Формально эмоциональную окраску можно определить как “взгляд на ситуацию или событие или отношение к ним”. На тот момент наиболее распространенным инструментом для задач распознавания эмоциональной окраски были LSTM. Работа авторства Кай Шенг Тай (Kai Sheng Tai), Ричарда Сочера (Richard Socher) и Кристофера Маннинга (Christopher Manning) вводит принципиально новый способ объединения нескольких LSTM-нейронов в нелинейную структуру.

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

Архитектура сети


Одно из отличий Tree-LSTM от обычного LSTM состоит в том, что во последнем скрытое состояние – функция от текущих входных данных и и скрытого состояния на предыдущем шаге. В Tree-LSTM скрытое состояние – функция от текущих входных данных и скрытых состояний его дочерних нейронов.

Вместе с новой структурой – деревом – вводятся также некоторые изменения в математике сети, например, у дочерних нейронов теперь есть фильтры забывания. С подробностями можно познакомится в самой работе. А я хотел бы уделить внимание объяснению, почему такие сети работают лучше линейных LSTM.

В Tree-LSTM каждый нейрон может вмещать в себя скрытые состояния всех его дочерних узлов. Это интересный момент, так как нейрон может оценивать каждый свой дочерний узел по-разному. Во время обучения сеть может осознать, что определенное слово (например, слово “не” или “очень”) чрезвычайно важно для определения эмоциональной окраски всего предложения. Возможность выше оценить соответствующий узел обеспечивает большую гибкость сети и может улучшить ее производительность.

Нейронный машинный перевод (Neural Machine Translation, NMT)


Введение


Последняя работа, которую мы сегодня рассмотрим, описывает подход к решению задачи машинного перевода. Авторы этой работы – специалисты Google по машинному обучению Джефф Дин (Jeff Dean), Грег Коррадо (Greg Corrado), Ориал Виньялс (Orial Vinyals) и другие – представляют систему машинного обучения, которая лежит в основе широко известного сервиса Google Translate. С введением этой системы количество ошибок перевода сократилось в среднем на 60% по сравнению с прежней системой, используемой Google.

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

Архитектура сети


Авторы этой статьи описывают глубокую сеть LSTM, которая может быть от обучена с помощью восьми слоем энкодеров и декодеров. Мы можем разделить систему на три компонента: энкодер RNN, декодер RNN и модуль “внимания” (attention module). Энкодер работает над задачей преобразования входного предложения в векторное представление, декодер возвращает выходное представление, затем модуль внимания сообщает декодеру, на чем следует заострить внимание во время операции декодирования (здесь вступает идея использования всего контекста предложения).

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

Заключение


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

О, а приходите к нам работать? :)
wunderfund.io — молодой фонд, который занимается высокочастотной алготорговлей. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.

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

Присоединяйтесь к нашей команде: wunderfund.io
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330194/


Как работает лазерная рулетка: реверс-инжиниринг

Понедельник, 05 Июня 2017 г. 15:32 + в цитатник
image
Ранее в своей статье я рассказывал о том, как устроены фазовые лазерные дальномеры. Теперь пришло время разобраться с тем, как работают бытовые лазерные рулетки. Разобраться — это не просто заглянуть, что же там внутри, а полностью восстановить всю схему и написать собственную программу для микроконтроллера.


Принцип работы лазерных рулеток


Большинство лазерных рулеток используют фазовый, а не импульсный (времяпролетный, TOF) метод измерения расстояния.
Для целостности этой статьи процитирую часть теории из своей предыдущей статьи:
image
В фазовом методе, в отличие от импульсного, лазер работает постоянно, но его излучение амплитудно модулируется сигналом определенной частоты (обычно это частоты меньше 500МГц). Отмечу, что длина волны лазера при этом остается неизменной (она находится в пределах 500 — 1100 нм).
Отраженное от объекта излучение принимается фотоприемником, и его фаза сравнивается с фазой опорного сигнала — от лазера. Наличие задержки при распространении волны создает сдвиг фаз, который и измеряется дальномером.
Расстояние определяется по формуле:

$D=\frac{c}{2f}\cdot \frac{\varphi }{2\pi }$


Где с — скорость света, f — частота модуляции лазера, фи — фазовый сдвиг.
Эта формула справедлива только в том случае, если расстояние до объекта меньше половины длины волны модулирующего сигнала, которая равна с / 2f.
Если частота модуляции равна 10 МГц, то измеряемое расстояние может доходить до 15 метров, и при изменении расстояния от 0 до 15 метров разность фаз будет меняться от 0 до 360 градусов. Изменение сдвига фаз на 1 градус в таком случае соответствует перемещению объекта примерно на 4 см.
При превышении этого расстояния возникает неоднозначность— невозможно определить, сколько периодов волны укладывается в измеряемом расстоянии. Для разрешения неоднозначности частоту модуляции лазера переключают, после чего решают получившуюся систему уравнений.
Самый простой случай — использование двух частот, на низкой приблизительно определяют расстояние до объекта (но максимальное расстояние все равно ограничено), на высокой определяют расстояние с нужной точностью — при одинаковой точности измерения фазового сдвига, при использовании высокой частоты точность измерения расстояния будет заметно выше.

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

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

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

Отдельно стоит упомянуть, что в большинстве лазерных дальномеров в качестве фотоприемников используются лавинные фотодиоды (APD). Они обладают собственным внутренним усилением сигнала, что уменьшает требования к усилительным узлам дальномера. Коэффициент усиления таких фотодиодов нелинейно зависит от питающего напряжения. Таким образом, если модулировать напряжение питания APD сигналом гетеродина, то смешивание (перемножение) сигналов происходит прямо в самом фотодиоде. Это позволяет упростить конструкцию дальномера, и уменьшить влияние шумов.
В тоже время, у лавинных фотодиодов много недостатков. К ним можно отнести:
  • Напряжение питания должно быть достаточно высоким — сотня вольт и выше.
  • Сильная зависимость параметров от температуры.
  • Достаточно высокая стоимость (по сравнению с другими фотодиодами).


Реверс-инжиниринг лазерной рулетки


image
В качестве подопытного образца я использовал набор «50M DIY Rangefinder», найденный на просторах Aliexpress (справа приведена фотография включенной рулетки). Насколько я понял, этот набор — внутренности лазерной рулетки «X-40» (сейчас ее можно найти в продаже за 20$). Этот набор я выбрал только потому, что на его фотографиях было видно электронику устройства. По имеющейся у меня информации, схемотехника этой рулетки очень близка к схемотехнике рулетки U-NIT UT390B+, и другим китайским лазерным рулеткам и модулям лазерных дальномеров.

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

Что же представляет из себя конструкция такой рулетки?
image
Как видно из фотографий, она достаточно проста. Конструктивно рулетка состоит из блока лазерного дальномера, индикатора и платы с кнопками. Очевидно, что самое интересное — это блок дальномера. Вот так он выглядит вблизи:
image
С верхней стороны платы расположены две основные микросхемы дальномера — микроконтроллер STM32F100C8T6 и сдвоенный PLL генератор Si5351. Эта микросхема способна формировать два сигнала с частотами до 200 МГц. Именно она формирует сигнал для модуляции лазера и сигнал гетеродина. Также на этой стороне платы расположен смеситель и фильтр опорного (REF) сигнала и часть деталей узла высоковольтного источника напряжения для APD (вверху фотографии).

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

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

image

image

В одном из китайских интернет-магазинов мне удалось найти картинку с изображением печатной платы модуля лазерного дальномера (версия 511F), которая была очень близка по конструкции с моей платой (версия 512A). Разрешение картинки довольно низкое, зато на ней видно расположение проводников и переходных отверстий под микросхемами. В дальнейшем я подписал на ней номера компонентов и выделил проводники:
image

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

В результате исследования у меня получилась вот такая структурная схема рулетки:
image

Электрическую схему я разбил на несколько листов:
image
Схема 1. Микроконтроллер, узел питания и некоторое простые цепи.
Здесь все достаточно просто — тут показаны микроконтроллер STM32, некоторые элементы его обвязки, динамик, клавиатура, некоторые ФНЧ фильтры. Здесь же показан повышающий DC-DC преобразователь напряжения (микросхема DA1), формирующий напряжение питания рулетки. Рулетка рассчитана на работу от 2 батареек, напряжение которых может меняться в процессе работы. Указанный преобразователь формирует из входного напряжения VBAT постоянное напряжение 3.5 В (несколько необычное значение). Для включения и выключения питания рулетки используется узел, собранный на транзисторной сборке DA2. При нажатии кнопки S1 он включает DC-DC, после чего микроконтроллер сигналом по линии «MCU_power» начинает удерживать DC-DC включенным.
Во время одного из измерений я случайно сжег микросхему этого DC-DC преобразователя (щуп мультиметра соскочил, и замкнул ее ножки). Так как я не смог определить название микросхемы, мне пришлось выпаять ее, и подавать на рулетку напряжение 3.5 В от внешнего источника напряжения.
Снизу на краю платы есть 8 прямоугольных площадок, которые могут использоваться как отладочные или тестовые. Я отметил их на схеме «PMx». Из схемы видно, что все они подключены к выводам микроконтроллера. Среди них есть линии UART. Родная прошивка не ведет никакой активности на этих линиях, линия TX, судя по осциллографу, сконфигурирована на вход.
Также на краю платы есть 6 отверстий-контактов. На схеме они отмечены «Px». На них выведены линии питания рулетки и линии программирования STM32.

image
Схема 2. Узел PLL генератора, и узел управления лазерным диодом.
Микросхема PLL генератора Si5351 формирует прямоугольный сигнал, поэтому, чтобы убрать лишние гармоники, сигналы с выхода PLL подаются на два одинаковых полосовых фильтра. Тут же показан смеситель сигналов, собранный на диоде D1 — сигнал с него используется в качестве опорного при измерении разности фаз.
Как можно видеть из схемы, один из сигналов c PLL («LASER_signal») выводится на лазерный диод D3 без каких-либо преобразований. С другой стороны, яркость лазера (которая определяется величиной тока, текущим через него) стабилизируется при помощи аналогового узла, собранного на микросхеме DA3 и окружающих ее компонентах. Реальный уровень яркости лазера этот узел получает от встроенного в лазер фотодиода (он не показан на схеме). При помощи линии «laser_power» микроконтроллер может полностью отключить лазер, а при помощи линии «line10», соединенной с ЦАП микроконтроллера — регулировать яркость лазера. Исследование осциллографом показало, что рулетка постоянно удерживает на этой линии значение 1.4 В, и оно не меняется ни при каких условиях.

image
Схема 3. Узел питания APD и усилитель сигнала с APD.
Слева здесь показан линейный источник напряжения, формирующий питающее напряжение для усилителя фотодиода (DA5). Эта микросхема формирует напряжение 3.3 В, так что напряжение на ее входе должно быть выше 3.3 В. Насколько я понимаю, именно это служит причиной того, что остальная часть схемы питается от 3.5 В.
Ниже показан повышающий DC-DC преобразователь, собранный на микросхеме DA4, формирующий высокое напряжение (> 80 В) для лавинного фотодиода. Микроконтроллер может изменять величину этого напряжения при помощи линии «MCU_APD_CTRL», соединенной с ЦАП контроллера. Название микросхемы DA4 мне не удалось установить, так что пришлось экспериментально определять, как зависит напряжение на APD от уровня управляющего сигнала. Эта зависимость получается какая-то странная, с ростом величины управляющего сигнала, выходное напряжение падает. В дальнейших экспериментах я использовал несколько константных значений ЦАП, для которых я знал соответствующие им выходные напряжения.

Справа на схеме 3 показана схема маленькой печатной платы. Линиями M1-M8 показаны контактные площадки, соединяющие обе платы. Диод D6 — это лавинный фотодиод (APD). Он никак не промаркирован, так что определить его название и характеристики невозможно. Могу лишь сказать, что он имеет корпус LCC3.
На катод APD по линии M8 подается высокое постоянное напряжение. Также можно видеть, что через конденсатор C41 по линии «APD_modul» к нему подмешивается высокочастотный сигнал от PLL. Таким образом, на APD смешиваются оптический сигнал и сигнал «APD_modul», имеющие разные частоты. В результате этого на выходе APD появляется низкочастотный сигнал, который выделяется полосовым фильтром (компоненты C55, R41, R42, R44, C58, C59).
Далее низкочастотный сигнал усиливается операционным усилителем DA6B (SGM8542). Сигнал с выхода DA6B передается на АЦП микроконтроллера по линии M2. Также этот сигнал дополнительно усиливается транзистором T6 и передается на микроконтроллер по линии M1.
Такое ступенчатое усиление нужно из-за того, что уровень входного сигнала меняется в очень широких пределах.
Кроме того, рядом с APD установлен терморезистор R58, позволяющий определить температуру APD. Как я уже говорил, параметры APD сильно зависят от температуры, и терморезистор нужен для программной компенсации этой зависимости. В процессе работы APD нагревается, и даже это изменяет его характеристики. К примеру, при комнатной температуре из-за собственного нагрева усиление фотодиода падает более чем в 2 раза.

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

На фотографиях платы видно, что на ней есть контрольные площадки. Я отметил их на схеме и плате: «TPx». Среди них можно выделить:
  • TP3, TP4 — низкочастотный сигнал с усилителя фотодиода. Именно этот сигнал несет информацию о расстоянии до объекта. При помощи осциллографа можно увидеть, что сигнал имеет частоту 5 кГц, и содержит постоянную составляющую.
  • TP1 — опорный сигнал. Также имеет частоту 5 кГц и содержит постоянную составляющую. Амплитуда этого сигнала довольно мала — около 100 мВ.
  • TP5 — высокое напряжение питания лавинного фотодиода.


Программирование


Прежде чем пытаться сделать что-то с родной прошивкой контроллера, я решил снять логическим анализатором обмен между STM32 и PLL, который происходит по I2C шине. Для этого я припаял провода к подтягивающим резисторам шины:
image
Мне без проблем удалось перехватить обмен между упомянутыми микросхемами и декодировать данные в передаваемых посылках:

Анализ результатов показал, что контроллер всегда только записывает информацию в PLL, и ничего не считывает. При хорошем уровне сигнала один цикл измерений занимает около 0.4 секунд, при плохом уровне сигнала измерения идут значительно дольше.
Видно, что микроконтроллер передает в PLL достаточно крупные посылки с периодом около 5 мс.
Поскольку данных было много, для их анализа я написал специальную программу на Python. Программа определяла и подсчитывала посылки, определяла размер посылок, время между ними. Кроме того, программа выводила названия регистров PLL, в которые производится запись передаваемых байтов.
Как оказалось, каждые 5 мс STM32 полностью перезаписывает основные регистры PLL (длина пакета 51 байт), в результате чего PLL меняет обе частоты. Никакой инициализации PLL рулетка не проводит — то есть пакеты передаваемых данных несут полную конфигурацию PLL. При хорошем уровне сигнала цикл измерений состоит из 64 передач данных.

Далее я добавил в программу расчет частоты по данным, передаваемым в пакетах. Выяснилось, что в процессе измерений рулетка использует четыре частоты модуляции лазера:
  • 162.0 MHz
  • 189.0 MHz
  • 192.75 MHz
  • 193.5 MHz

Частота гетеродина (второй выход PLL) при этом всегда имеет частоту, на 5 кГц меньшую, чем частота модуляции лазера.
Судя по всему, 4 цикла переключения частот (по 5 мс каждый) позволяют обеспечить однократное определение расстояния. Таким образом, проведя 64 цикла, рулетка выполняет 16 измерений расстояния, после чего усредняет и фильтрует результаты, за счет чего повышается точность измерения.

Далее я приступил к написанию своей программы для микроконтроллера рулетки.
После подключения программатора к рулетке компьютер не обнаружил ее микроконтроллер. Насколько я понимаю, это значит, что в родной прошивке интерфейс SWD отключен программно. Эту проблему я обошел, подключив к рулетке линию программатора NRST и выбрав в настройках ST-LINK Utility режим «Connect under reset». После этого компьютер обнаружил контроллер, но, как и ожидалось, родная прошивка была защищена от чтения. Для того, чтобы записать в контроллер свою программу, Flash-память контроллера пришлось стереть.

Первым делом в своей программе я реализовал включение питания аналоговой части дальномера, включение лазера и установку его тока, включение напряжения питания APD. После того, как я убедился, что все напряжения в норме, можно было экспериментировать с PLL. Для теста я просто реализовал запись в PLL тех данных, которые я ранее получил с рулетки.
В результате после запуска своей программы я обнаружил, что на контрольных точках появился сигнал с частотой 5 кГц, амплитуда которого явно зависела от типа объекта, на которые светил лазер. Это значило, что вся аналоговая электроника работает правильно.
После этого я добавил в программу захват аналогового сигнала при помощи АЦП. Стоит отметить, что для измерения разности фаз сигналов микроконтроллер должен захватывать уровни основного и опорного сигналов одновременно или с постоянной задержкой. В STM32F100 последний вариант можно реализовать, используя режим сканирования АЦП. Данные от АЦП при этом логично захватывать в память при помощи DMA, а для того, чтобы данные захватывались с заданной частотой дискретизации, запуск преобразования АЦП должен производиться по сигналу от одного из таймеров.
В результате экспериментов я остановился на следующих параметрах захвата:
Частота дискретизации АЦП — 50 кГц,
Количество выборок — 250.
Суммарное время захвата сигнала — 5 мс.
Захваченные данные программа контроллера передает на ПК по UART.

Для обработки захваченных данных я написал на C# небольшую программу:
image
График синего цвета — принятый сигнал, график оранжевого цвета — опорный сигнал (его амплитуда на этом графике увеличена в 20 раз).
На графике снизу показан результат FFT преобразования принятого сигнала.
Используя FFT, можно определить фазу сигнала — нужно рассчитать фазовый спектр сигнала, и выбрать из него значение фазы в точке, соответствующей 5кГц. Отмечу, что я пробовал выводить фазовый спектр на экран, но он выглядит шумоподобным, так что я от этого отказался.
В то же время в действительности на микроконтроллер поступают два сигнала — основной и опорный. Это значит, что нужно вычислить при помощи FFT фазу каждого из сигналов на частоте 5 кГц, а затем вычесть из одного результата другой. Результат — искомая разность фаз, которая и используется для расчета расстояния. Моя программа выводит это значение под графиком спектра.
Очевидно, что использование FFT — не самый подходящий метод определения фазы сигнала на единственной частоте. Вместо его я решил использовать алгоритм Гёрцеля. Процитирую Википедию:
Алгоритм Гёрцеля (англ. Goertzel algorithm) — это специальная реализация дискретного преобразования Фурье (ДПФ) в форме рекурсивного фильтра.… В отличие от быстрого преобразования Фурье, вычисляющего все частотные компоненты ДПФ, алгоритм Гёрцеля позволяет эффективно вычислить значение одного частотного компонента.

Этот алгоритм очень прост в реализации. Как и FFT, он может возвращать комплексный результат, благодаря чему можно рассчитать фазу сигнала. В случае использования этого алгоритма также нужно рассчитать фазы основного и опорного сигналов, после чего вычислить их разность.
Эта же программа для ПК позволяет вычислять разность фаз и амплитуду сигнала при помощи алгоритма Герцеля. Результаты экспериментов показали, что при хорошем уровне сигнала точность измерения разности фаз может доходить до 0.4 градусов (СКЗ по 20 измерениям).

На следующем этапе я написал программу для микроконтроллера, которая сама рассчитывала разность фаз сигналов для трех разных частот модуляции (при помощи алгоритма Герцеля), и передавала результат на ПК. Почему использовались именно три частоты — я объясню позднее. За счет того, что расчеты производятся на самом микроконтроллере, нет необходимости передавать большой объем данных по UART, что значительно увеличивает скорость измерений.
Для ПК была написана программа, которая позволяла захватывать принимаемые данные и логировать их.

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

Такого оборудования у меня не было, так что пришлось ограничится более простыми средствами. Я проводил исследования работы дальномера только при двух рабочих напряжениях лавинного фотодиода (Uapd) в 82 В и 98 В. Все исследования шли при частоте модуляции лазера 160 МГц.
В своих исследованиях я считал, что изменения амплитуды светового сигнала и температуры независимо друг от друга влияют на результаты измерения разности фаз.

Для изменения амплитуды принимаемого светового сигнала я использовал специальный подвижный столик с прикрепленной заслонкой, которая могла перекрывать линзу-объектив фотодиода:


С изменением температуры все было сложней. В первую очередь, как я уже упоминал ранее, у APD был заметный эффект саморазогрева, который хорошо отслеживался термодатчиком. Для охлаждения рулетки я накрыл ее коробом из пенопласта с установленным в нем вентилятором, и установил сверху емкость с холодной водой. Кроме того, я пробовал охлаждать рулетку на балконе (там было около 10 °C). Судя по уровню сигнала с термодатчика, оба метода давали примерно одинаковую температуру APD. С нагревом все проще — я нагревал рулетку потоком горячего воздуха. Для этого я использовал резистор, прикрепленный к кулеру — так можно было регулировать температуру воздуха.
У меня не было никакой информации об установленном в рулетке терморезисторе, так что я нигде не пересчитывал результаты преобразования АЦП в градусы. При увеличении температуры уровень напряжения на АЦП падал.

В результате получились такие результаты:
  • При увеличении Uapd (то есть с ростом усиления) заметно возрастает чувствительность APD к изменениям температуры и изменению уровня сигнала.
  • При уменьшении амплитуды светового сигнала появляется небольшой сдвиг фазы — примерно +2 градуса при изменении амплитуды от максимальной до минимальной.
  • При охлаждении APD появляется положительный сдвиг фазы.

Для напряжения 98 В получилась такая зависимость фазового сдвига от температуры (в единицах АЦП):

Можно видеть, что при изменении температуры (примерно от 15 до 40 градусов) разность фаз изменяется более чем на 30 градусов.
Для напряжения 82 В эта зависимость получилась практически линейной (по крайней мере, в том диапазоне температур, где я проводил измерения).
В результате, я получил два графика для двух Uapd, которые показывали связь между температурой и фазовым сдвигом. По этим графикам я определил две математические функции, которые использовал в микроконтроллере для коррекции значения разности фаз. Таким образом, я смог избавиться от влияния изменения внешних факторов на правильность измерений.

Следующий этап — определение расстояния до объекта по трем полученным разностям фаз. Для начала, я решил сделать это на ПК.
В чем тут проблема? Как я уже упоминал ранее, если частота модуляции достаточно высокая, то на определенном расстоянии от дальномера при попытке определить расстояние возникает неоднозначность. В таком случае для точного определения расстояния до объекта нужно знать не только разность фаз, но и число целых фаз сигнала (N), которые укладываются в этом расстоянии.
Расстояние в результате определяется формулой:

$D=\frac{1}{2}\left ( N\cdot \lambda + \frac{\varphi\cdot\lambda }{2\pi} \right )$


Из анализа работы заводской программы рулетки видно, что частоты модуляции лежат в диапазоне 160-195 МГц. Вполне вероятно, что схемотехника рулетки не позволит модулировать излучение лазера с меньшей частотой (я это не проверял). Это значит, что метод определения расстояния до объекта по разности фаз в рулетке должен быть сложнее, чем простое переключение между высокой и низкой частотами модуляции.
Стоит заметить, что из-за того, что частоты модуляции разные, то число целых фаз сигнала в одних случаях может иметь общее значение N, а в других — нет (N1, N2 ...).

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

Второй вариант — использование эффекта биений сигналов, имеющих близкие частоты модуляции.
Пусть в дальномере используются две частоты модуляции сигнала с длинами волн $\lambda_{1}$ и $\lambda_{2}$, имеющие достаточно близкие значения.
Можно предположить, что на дистанции до объекта количество целых периодов N1 и N2 равны между собой и равны некому значению N.
В таком случае получается такая система уравнений:

$\left\{\begin{matrix}D=\frac{1}{2}\left ( N\cdot \lambda_{1} + \frac{\varphi_{1}\cdot\lambda_{1} }{2\pi} \right ) \\ D=\frac{1}{2}\left ( N\cdot \lambda_{2} + \frac{\varphi_{2}\cdot\lambda_{2} }{2\pi} \right ) \end{matrix}\right.$


Из нее можно вывести значение N:

$N=\frac{\lambda_{2}\cdot \frac{\varphi_{2}}{2\pi}+\lambda_{1}\cdot \frac{\varphi_{1}}{2\pi}}{\lambda_{1}-\lambda_{2}}$


Получив значение N, можно вычислить расстояние до объекта.
Максимальное расстояние, на котором выполняется вышеупомянутое утверждение, определяется формулой:

$D_{max}=\frac{1}{2}\cdot\frac{\lambda_{1}\cdot \lambda_{2}} {\left |\lambda_{2} - \lambda_{1}\right |}$


Из этой формулы видно, что чем ближе друг к другу длины волн сигналов, тем больше максимальное расстояние.
В то же время, даже на указанной дистанции в некоторых случаях это утверждение (N1=N2) выполнятся не будет.
Приведу простой пример.
Пусть $\lambda_{1}= 1.55 м$ и $\lambda_{2}= 1.5 м$.
В таком случае $D_{max}=11.6 м$.
Но если при этом путь, который проходит свет, равен 1.53м, то получается что для первой длины волны N1 = 0, а для второй N2 = 1.
В результате расчета величина N получается отрицательной.
Бороться c этим эффектом можно, используя знание, что $\lambda_{1}>\lambda_{2}$.
В таком случае можно модифицировать систему уравнений:

$\left\{\begin{matrix}D=\frac{1}{2}\left ( N1\cdot \lambda_{1} + \frac{\varphi_{1}\cdot\lambda_{1} }{2\pi} \right ) \\ D=\frac{1}{2}\left ((N1+1)\cdot \lambda_{2} + \frac{\varphi_{2}\cdot\lambda_{2} }{2\pi} \right ) \end{matrix}\right.$


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

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

В своем алгоритме определения расстояния я решил использовать три частоты модуляции: 162.5 МГц, 191.5 МГц, 193.5 МГц — по результатам экспериментов, это было наиболее количество.
Мой алгоритм определения расстояния состоит из трех этапов:
  1. Проверка, не попали ли разности фаз в зону «нулевого» расстояния. В области, близкой к нулю калибровки, из-за ошибок измерения значение разности фаз может «прыгать» — от 0 градусов до 359 градусов, что приводит к большим ошибками при измерении расстояния. Поэтому, при обнаружении, что все три разности фаз одновременно получились близкими к нулю, можно считать, что измеряемое расстояние близко к нулевому значению, и за счет этого отказаться от вычисления величин N.
  2. Предварительное вычисление расстояния по биениям сигналов с частотами 191.5 МГц и 193.5 МГц. Эти частоты выбраны близкими, за счет чего зона определенности получается достаточно большой: $D_{max}=37.5 м$, но и результат вычислений сильно подвержен влиянию ошибок измерений. При низком уровне принимаемого сигнала ошибка может составлять несколько метров (несколько длин волн).
  3. Вычисление расстояния методом перебора по разностям фаз сигналов с частотами 162.5 МГц и 191.5 МГц.
    Поскольку на предыдущем этапе уже определено приблизительное расстояние, то диапазон перебираемых значений N можно ограничить. За счет этого уменьшается сложность перебора и отбрасываются возможные ошибочные результаты.

В результате у меня получилась вот такая программа для ПК:

Эта программа позволяет отображать данные, передаваемые рулеткой — амплитуду сигнала, напряжение APD, температуру в единицах АЦП, значения разности фаз сигналов для трех частот и вычисленное по ним расстояние до объекта.
Калибровка нуля производится в самой программе при нажатии кнопки «ZERO».

Для автономно работающего лазерного дальномера важно, чтобы усиление сигнала можно было менять, так как при изменении расстояния и коэффициента отражения уровень сигнала может очень сильно меняться. У себя в программе микроконтроллера я реализовал изменение усиления за счет переключения между двумя напряжениями питания APD — 82 В и 98 В. При переключении напряжения уровень усиления менялся примерно в 10 раз.
Я не стал реализовывать переключение между двумя каналами АЦП — «MCU_signal_high», «MCU_signal_low» — программа микроконтроллера всегда использует сигнал только с канала «MCU_signal_high».

Следующий этап — окончательный, заключается в переносе алгоритма расчета расстояния на микроконтроллер. Благодаря тому, что алгоритм был уже проверен на ПК, это не составило особого труда. Кроме того, в программу микроконтроллера пришлось добавить возможность производить калибровку нуля. Данные этой калибровки микроконтроллер сохраняет во Flash памяти.
Я реализовал два различных варианта прошивки микроконтроллера, отличающихся принципом захвата сигналов. В одной из них, более простой, микроконтроллер во время захвата данных от АЦП ничего не делает. Вторая прошивка — более сложная, в ней данные от АЦП одновременно записываются в один из массивов при помощи DMA, и в то же время при помощи алгоритма Герцеля обрабатываются уже захваченные ранее данные. За счет этого скорость измерений повышается практически в 2 раза по сравнению с простой версией прошивки.

Результат вычислений микроконтроллер отправляет по UART на компьютер.
Для удобства анализа результатов я написал еще одну маленькую программу для ПК:


Результаты


В результате мне удалось точно выяснить, как устроена электроника лазерной рулетки, и написать собственную Open source прошивку для нее.
Для меня в процессе написания прошивки наиболее важным было добиться максимальной скорости измерений. К сожалению, повышение скорости измерений заметно сказывается на точности измерений, так что требуется искать компромисс. К примеру, код, приведенный в конце
этой статьи, обеспечивает 60 измерений в секунду, и точность при этом составляет около 5-10 мм. Если уменьшить количество захватываемых значений сигнала, можно повысить скорость измерений. Я получал и 100 измерений в секунду, но при этом влияние шумов значительно увеличивалось.

Конечно же, внешние условия, такие как расстояние до объекта и коэффициент отражения поверхности сильно влияют на отношение сигнал-шум, а следовательно, и на точность измерений. К сожалению, при слишком низком уровне светового сигнал даже увеличение усиления APD не сильно помогает — с ростом усиления растет и уровень шумов.
В ходе экспериментов я заметил, что внешняя засветка лавинного фотодиода тоже значительно увеличивает уровень помех. В модуле, который был у меня, вся электроника открыта, так что для уменьшения помех его приходится накрывать чем-нибудь непрозрачным.
Еще одна замеченная особенность — из-за того, что оптические оси лазера и объектива фотодиода не совпадают, на близких расстояниях (<0.7 м) уровень сигнала значительно падает.

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

Видео, показывающее работу рулетки:




Напоследок: какие рулетки еще можно встретить?


Здесь я хочу рассказать о конструкциях других лазерных рулеток, о которых можно найти информацию в сети.
  • В первую очередь стоит отменить проект реверс-инжиниринга лазерной рулетки BOSCH DLE50.
    Особенность этой рулетки — в ней в качестве PLL генератора используется заказная микросхема CF325, на которую в интернете нет никакой документации, что заметно усложняет процесс реверс-инжиниринга. Эта ситуация (заказные микросхемы без документации) очень часто встречается в лазерных рулетках, но, похоже, сейчас ситуация начинает меняться — заказные микросхемы начинают заменятся «универсальными».
    Используемый в этой рулетке микроконтроллер — ATmega169P.
    Еще одна особенность этой рулетки — использование механического узла, управляемого электромагнитом, который позволяет создавать «оптическое короткое замыкание», то есть перенаправляет свет от лазера к фотодиоду по известному пути. За счет того, что длина пути света и коэффициент отражения при этом известны, микроконтроллер может производить различные калибровки (по амплитуде и фазе). Во время работы этого узла лазерная рулетка достаточно громко щелкает.
    Вот здесь можно посмотреть фотографии электроники этой рулетки.
  • Достаточно много что известно про лазерную рулетку UT390B.
    Некий энтузиаст смог произвести реверс-инжиниринг протокола отладочного UART интерфейса этой рулетки, и научился управлять ее работой. Есть даже библиотека для Arduino.
    На русском про устройство этой рулетки можно почитать здесь.
    Как видно из фотографий, электроника этой рулетки достаточно проста, и похожа на ту, что описана в этой статье.
    Используемый в этой рулетке микроконтроллер — STM32F103C8. Микросхема PLL: CKEL925 (на нее есть документация).
  • А вот протокол новой версии рулетки UT390B+ никто пока выяснить не смог.
    Схемотехника этой рулетки отличается от ее старой версии.
    Она еще ближе к схемотехнике моей рулетки — здесь используется микроконтроллер STM32F030CBT6 и PLL Si5351.
    Если приглядеться к фотографиям, можно заметить, что в рулетке установлены два лазера.
    Судя по всему, два лазера в рулетке сейчас — не редкость. Вот в этом описании устройства еще одной рулетки упоминается, что один из лазеров имеет видимое излучение, и служит только для «целеуказания», а второй лазер — инфракрасный, и используется для измерения расстояния. Интересно, что при этом и лазер, и фотодиод используют одну линзу.
  • Еще одна рулетка с неизвестным протоколом — BOSCH PLR 15.
    Энтузиасты уже пытались разобраться с ее протоколом, но пока в этом никто не преуспел.
    Раньше я тоже пробовал выяснить, как работает эта рулетка, и даже частично восстановил схему этой рулетки.
    Используемый в этой рулетке микроконтроллер — STM32F051R6. А вот других микросхем высокой степени интеграции в ней просто нет!
    Зато фотоприемник здесь использован очень необычный, я никогда не встречал даже упоминаний таких устройств:
    image
    Судя по всему, он представляет собой систему на кристалле, и содержит два фотодиода (измерительный и опорный каналы), усилители фотодиодов, цифровую управляющую электронику и АЦП. Сигнал модуляции лазера идет тоже с него. Сам фотоприемник соединен с микроконтроллером через SPI.
    Я пробовал перехватывать данные, которые идут по SPI — там присутствуют команды от контроллера датчику и пакеты информации от датчика контроллеру.
    Если обработать эти пакеты в Excel — то явно видны синусоиды (то есть используется фазовый способ измерения расстояния). Это значит, что обработкой сигнала в этой рулетке занимается микроконтроллер.
    Однако информации по SPI идет очень много, частоты, на которых идут измерения, установить не удалось, так что даже считать с рулетки расстояние — достаточно проблематичная задача.
  • Различные китайские модули.
    В последнее время в китайских интернет-магазинах появилось большое количество модулей лазерных дальномеров (из можно найти по запросу «laser ranging module» и аналогичных ему).
    Среди них можно найти и модули, которые выглядят абсолютно так же, как и мой, но продаются они в два раза дороже (40$). Похоже, что это все те же внутренности лазерных рулеток, но с модифицированной прошивкой. Интересно, что среди различных конструкций мне несколько раз попадались дальномеры с двумя одинаковыми микросхемами PLL (судя по всему, эти микросхемы — не заказные).


Файлы проекта: github.com/iliasam/Laser_tape_reverse_engineering
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/327642/


Метки:  

[recovery mode] Kokoc Group настаивает на email-маркетинге

Понедельник, 05 Июня 2017 г. 15:26 + в цитатник
Kokoc Group представляет новое направление – комплексный email-маркетинг на основе бизнес-аналитики. Департамент возглавил признанный эксперт в области онлайн-маркетинга и интернет-торговли Антон Шаманаев. С помощью новой команды группа компаний планирует сделать рассылки популярным и эффективным инструментом на рынке e-commerce.

image

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

— У 90% интернет-магазинов email-маркетинг не развит, поэтому на рынке существует стереотип, что инструмент не работает. На самом деле он эффективно дополняет основные каналы, к примеру, SEO и контекст. При высоком ROI (окупаемость инвестиций) инструмент имеет низкую стоимость контакта. Мы намерены помочь интернет-магазинам грамотно настроить email-маркетинг с нуля или исправить ошибки в текущих системах тем, кто в этом нуждается, — говорит Антон Шаманаев.

image

Новое направление ориентировано, прежде всего, на интернет-магазины и B2С-проекты. Вне зависимости от объема бизнеса для каждого клиента будет проводиться детальная аналитика поведения пользователей на сайте. В результате заказчик получит уникальную стратегию email-маркетинга, разработанную с учетом специфики его продукта и покупательских предпочтений.
В первую очередь инструмент будет полезен крупным e-commerce сайтам, которые ежедневно посещают сотни тысяч человек. Внушительный объем базы подписчиков можно быстро монетизировать: получить дополнительный приток продаж и улучшить результативность других каналов рекламы.

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

Молодым интернет-магазинам с низкой посещаемостью или тем, кто пока не делал рассылок вообще, email-маркетинг поможет повысить активность текущих подписчиков и собрать новых. Учитывая ограниченность в бюджете на продвижение, достаточно использовать разовый пакет услуг, который включает в себя настройку email-платформы и разработку основных триггеров (рассылок). Последние автоматически отправляются пользователям, к примеру, не завершившим заказ или давно не делавшим покупки.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330242/


Метки:  

Создание дизайн-экосистемы для десятков связанных ИТ-решений: слово дизайнерам

Понедельник, 05 Июня 2017 г. 14:38 + в цитатник
Сегодня расскажем, как пришли к необходимости создания единой UI/UX-системы для разных приложений одного из наших заказчиков. О том, какие принципы в нее заложили и как технологично дизайн-экосистему упаковали.

image

Надеемся, наш опыт будет интересен UI/UX дизайнерам, фронтэндерам, а также бренд-менеджерам, которые на собственном опыте сталкиваются с подобными задачами.


Когда нужна экосистема. Наш опыт


Много лет для одного заказчика мы делаем большое множество ИТ-проектов различной степени сложности и направленности (публичные/внутренние). На данный момент проектов более 30. В процессе происходили значительные изменения в дизайне, вплоть до смены основных корпоративных цветов. И по факту до некоторого момента дизайн и разработка велись “стихийно”, то есть просто брали и делали каждый проект “с чистого листа”.

Последствия для нас — разработчиков – вы и сами легко можете спрогнозировать:
Вас неизбежно ждет неоднородный и постоянно требующийся новый дизайн для новых модулей.
  • Если меняется фирменный стиль, то нужно бежать и менять дизайн всего. Но более реалистично – у вас будет зоопарк. (Приоритеты, ресурсы, бюджеты – вам его обязательно обеспечат).
  • Дизайнерам нужно будет постоянно контролировать фронтендеров. А им придется писать много повторяемого кода (но это отдельная большая история, и о ней мы вам расскажем отдельно)


Для заказчика такой несистемный подход тоже плачевен:
  • Пользователи испытывают неудобство в работе. Когда одному и тому же человеку приходится использовать несколько систем с неоднородным UX и в каждой из них элементы управления ведут себя немного по-разному, неизбежно возникают “трения” (frictions), количество которых и характеризует UX как хороший или плохой. Такого рода “трения” воспринимаются пользователем очень болезненно, потому что не дают работать “в потоке”.
    Так как у нас бизнес-системы, пользователи вынужденно их используют. Тем не менее “производительность” труда явно не на максимуме.

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

Язык дизайна. Этапы создания


Осознание бесперспективности текущего пути в один прекрасный момент привело нас к мысли о том, что нужна система. И вот как мы ее последовательно строили.

Этап 1. Определяем основные принципы


1. Единство.
Язык дизайна — это система. И он должен работать как система — понял принципы один раз, и дальше пользуешься ими на автомате.
  • Единая сетка и расположение ключевых элементов управления.
  • Единые обозначения для типовых данных. Деньги, даты, статусы, текст, время, периоды…
  • Единые акценты, которые подсказывают правильную последовательность действий в любом из приложений системы.


image

image

2. Простота и функциональность.
Наш дизайн-язык — для деловой коммуникации. Это не декоративный жанр. Поэтому:
  • Избавляем интерфейсы от всех нефункциональных “красивостей”, которые не несут полезной функции.
  • Используем минимум иконок. Как правило они не облегчают пользователю жизнь, а заставляют играть в «угадайку».

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

image

3. Проактивность.
Сокращаем трудозатраты пользователя для получения результата за счет того, что UI “ведет” пользователя по нужному пути?
  • Используем интуитивно-понятные ассоциации.
    Не каждый сразу вспомнит, что такое CVC-код, но всем понятно, что такое «три цифры на обороте карты», поэтому на страничках онлайн-оплаты размещают изображение банковской карты.
    В нашем случае, для сотрудников авиакомпании, которые занимаются анализом проданных перевозок, отображение данных было сверстано по стандартам составления маршрутных квитанций (по-простому – билета).

image

  • Учитываем восприятие цветов человеком при выборе цветовых решений в интерфейсах.
    К примеру, в финансах принято красным обозначать расходы. Поэтому, когда мы делаем финансовую систему, в которой будут работать бухгалтера, мы будем «раскрашивать» для них деньги так, как им привычно. И так нужно действовать с каждой аудиторией.

image

4. Гибкость.
Система — это организм. Она способна перестроиться под новые реалии, не разрушаясь.
  • Поддерживает развитие функционала.
  • Поддерживает разные платформы — веб, мобильность.
  • Оставляет пространство для “творчества”.
    Можно сделать специальное оформление к праздникам, или маркетинговой акции.


image

image

image

Этап 2. Собираем UI Kit


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

Фактически UI Kit – это продолжение бренд-бука заказчика, только для цифровых носителей. В нем собраны все типовые страницы, элементы управления и их возможные состояния, а также полный сет необходимых иконок. Он позволяет выполнять те же задачи, что и обычный бренд-бук: повышать узнаваемость бренда, создавать определенный образ компании — только в “цифре”.

image

image

Дальнейшие проекты дизайнеры строят на основе данного каталога, продумывая лишь уникальные для конкретного проекта вещи. Как следствие, дизайнер теперь больше времени тратит именно на проектирование приложения, проработку вопросов эргономики и usability, а не занимается стайлингом.

От дизайна к разработке


Дальнейшее развитие проекта в большей степени ложится на плечи разработчиков. Дизайнеры выступают в роли заказчиков, цензоров и контролеров технологической оболочки UI Kit, которую делают уже фронтэндеры. Так как эта статья у нас дизайн-ориентированная, сейчас мы обозначим следующие шаги, а детальный технический рассказ мы вынесем в отдельную статью. Продолжение: “Дизайн-экосистема: слово фронтэндерам” — уже уже на подходе.

Этап 3. Технологично упаковываем


Жирный плюс в кассу дизайн-экосистемы — возможность переиспользовать код. Конечно же мы хотим написать код один раз, сэкономив время на внедрение.

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

Помимо изменений в дизайне возможны и ошибки в css, воплощающем спроектированный дизайн. И эти ошибки хорошо бы делать и исправлять в одном месте, а не в каждой системе.
Решение данной задачи достаточно очевидное — создаем библиотеку, реализующую концепции разработанного дизайна. Физически это выглядит как тема для bootstrap, упакованная в приватный npm-пакет. Разработчики устанавливают ее “поверх” bootstrap и получают нужный им дизайн.



Этап 4. Создаем «документацию»


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



Этап 5. Обеспечиваем версионность


Тотальную и безболезненную. Но этому будет посвящена уже наша следующая история. Передаем эстафетную палочку фронтэндерам.

Итого в результате дизайн-части проекта:


  • Мы научились создавать собственный «язык» дизайна, адаптированный под специфику экосистемы ИТ-сервисов заказчика.
  • На опыте ИТ-сервисов конкретного заказчика превратили зоопарк в единую экосистему. Разработали единую концепцию UX, что позволит минимизировать “трение” при работе с нашими системами.
  • Создали UI Kit, который позволяет заказчику поддерживать и развивать все свои системы в едином стиле, а для наших дизайнеров сокращает временные затраты при работе над дизайном.
  • Реализовали распространение обновлений библиотеки в виде npm-пакета, что в сочетании с грамотным версионированием помогает исправлять огрехи и актуализировать дизайн безболезненно и изолированно на каждом проекте.
  • Исчерпывающе проиллюстрировали работу библиотеки через демо-приложение, что позволяет разработчикам нередко обходиться не только без дизайнеров, но даже без верстальщиков.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330206/


Метки:  

Разбираемся с новыми архитектурными компонентами в Android

Понедельник, 05 Июня 2017 г. 14:37 + в цитатник
Гостевая статья от участника Google I\O 2017 и одного из лидеров GDG Kazan — Артура Василова (@Arturka).

19 мая завершилась конференция Google I/O. По поему мнению, несмотря на то, что новых продуктов в этом году было представлено не так много, с технической точки зрения конференция получилась интересной.

Самым большим и интересным техническим обновлением лично для меня стали новые Architecture Components (не Android O, в котором мало чего интересного, и уж точно не Kotlin). Google сделал то, что должен был сделать уже очень давно — разработать стандарты архитектуры и предоставить их разработчикам. Что же, лучше поздно, чем никогда, и давайте разбираться с тем, насколько полезной может быть архитектура от Google.



Первое впечатление


Лично меня название докладов не заинтересовало настолько, что я даже не посещал их, так как не верил, что Google в этой области может сделать что-то интересное, а не просто повторить Mosby/Moxy/<ваш собственный велосипед>. Такой же скепсис сохранился и после быстрого просмотра разработанных библиотек. Однако через некоторое время пришлось поменять свое мнение, так как:
  • Google действительно сделали хорошую библиотеку с удобным API;
  • Google не стал закрываться, а учел все современные наработки в области архитектуры Android-приложений, взяв лучшее из всего;
  • У новичков наконец-то есть хорошая документация, которая поможет им начать разрабатывать приложения уже с неплохой архитектурой, без необходимости детально вникать в смысл MVP/MVVM и т.д.

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

Хорошая архитектура


Для начала рассмотрим, что вообще подразумевается под хорошей архитектурой приложения. Чтобы не уходить в философские утверждения о том, зачем вообще нужна архитектура и так далее, сразу обозначим требования для хорошей архитектуры приложения:
  • Позволяет декомпозировать логику (получение данных/обработка/взаимодействие с пользователем и т.д.);
  • Масштабируемость. Слово, которое должно быть в любой статье про архитектуру. В Android с этим все достаточно просто, так как любое приложение можно разбить на экраны, между которыми будут весьма прозрачные связи, а архитектуру отдельного экрана сделать масштабируемой не представляется сложным;
  • Позволяет писать тесты для бизнес-логики приложения.

Все это общие требования, но кроме них можно выделить и вполне конкретные задачи, которые должны без проблем решаться в рамках конкретной архитектуры:
  1. Обработка пересоздания Activity. Вероятно, самая многострадальная тема при построении архитектуры приложения, которая не нуждается в комментариях;
  2. Выполнение HTTP запросов (как на получение данных, так и на отправку);
  3. Поддержка потоков данных, например, при работе с сокетами/сенсорами;
  4. Кэширование данных;
  5. Обработка ошибок.

Разумеется, пункты 2-5 должны выполняться с учетом первого пункта.

Достаточно долго Android-разработчики самостоятельно решали все эти проблемы, но постепенно накопившийся опыт привел к появлению Clean Architecture. Подходы Clean Architecture очень хороши, за исключением того, что часто в приложении неуместно разбиение логики на UI и бизнес (так как один из слоев может почти не содержать кода), поэтому многие разработчики используют архитектуру, которую можно представить следующей схемой (немного переставив блоки из Clean Architecture):


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

Архитектура от Google


Сразу посмотрим на схему от Google:

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

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

Одной из больших проблем в Android является необходимость постоянно подписываться/отписываться от каких-то объектов при вызове методов жизненного цикла. И поскольку методы жизненного цикла есть только в Activity/Fragment все такие объекты (например, GoogleApiClient, LocationManager, SensorManager и другие) должны быть расположены только в Activity/Fragment, что приводит к большому количеству строк кода в этих файлах.

Для решения этой и других проблем Google предложил использовать класс LiveData — класс, который тесно связан с жизненным циклом и который реализует паттерн Observer. По большому счету это очень простой класс, инкапсулирующий в себе работу с одним объектом, который может изменяться и за изменением которого можно следить. И это опять-таки достаточно стандартный подход.

Рассмотрим, как мы можем использовать LiveData, например, для того, чтобы следить за изменением местоположения пользователя. Создадим следующий класс:
public class LocationLiveData extends LiveData {
 
   private LocationManager locationManager;
 
   private LocationListener listener = new LocationListener();
 
   private LocationLiveData(@NonNull Context context) {
       locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
   }
 
   @Override
   protected void onActive() {
       locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
   }
 
   @Override
   protected void onInactive() {
       locationManager.removeUpdates(listener);
   }
}

И теперь мы можем написать следующий код в Activity:
public class LocationActivity extends LifecycleActivity {
 
   private LocationLiveData locationLiveData;
  
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_movies);
      
       //...
 
       locationLiveData = new LocationLiveData(this);
       locationLiveData.observe(this, location -> {
           // update your UI
       });
   }
 
   //...
}

Все нужные методы жизненного цикла у LiveData будут вызываться за счет первого параметра в методе observe — LifecycleOwner. Это позволяет нам знать, когда нужно подписываться и отписываться от различных объектов (например, с помощью методов onActive и onInactive).

LifecycleActivity — это класс из библиотеки, который реализует интерфейс LifecycleOwner. К сожалению, он почему-то унаследован от FragmentActivity, а не от AppCompatActivity, и, если вы хотите все же наследоваться от AppCompatActivity, придется написать некоторый код самому:
public class BaseLifecycleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
 
   @NonNull
   private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
 
   @MainThread
   @NonNull
   @Override
   public LifecycleRegistry getLifecycle() {
       return lifecycleRegistry;
   }
}

Кроме методов onActive и onInactive в LiveData вы можете получать коллбэки на любой метод жизненного цикла.

Точно такую же модель можно использовать и для реализации серверных запросов. То есть мы никаким образом не меняем способ получения данных (все также через Repository), но делегируем данные в LiveData, которая по сути является биндингом для View.

И теперь встает главный вопрос — как бороться с проблемой жизненного цикла? Разумеется, Google об этом подумал и дал разработчикам компонент, который переживает пересоздание Activity — ViewModel.

Пару слов про ViewModel:
  1. Все экземпляры ViewModel хранятся в retain Fragment (HolderFragment). Это весьма стандартное решение, которым также пользовались многие разработчики.
  2. Класс ViewModel должен хранить все экземпляры LiveData, чтобы они переживали пересоздание Activity.
  3. Связь ViewModel с View осуществляется через LiveData, то есть ViewModel не управляет View явным образом, и это можно считать некоторой вариацией паттерна MVVM.

Разберем, как мы можем реализовать серверный запрос для какого-либо экрана. Создадим ViewModel, которая будет управлять логикой экрана:
public class MoviesViewModel extends ViewModel {
  
   @NonNull
   private final MoviesRepository moviesRepository;
 
   @Nullable
   private MutableLiveData> moviesLiveData;
 
   public MoviesViewModel(@NonNull MoviesRepository moviesRepository) {
       this.moviesRepository = moviesRepository;
   }
 
   @MainThread
   @NonNull
   LiveData> getMoviesList() {
       if (moviesLiveData == null) {
           moviesLiveData = new MutableLiveData<>();
           moviesRepository.popularMovies()
                   .subscribeOn(Schedulers.io())
                   .observeOn(AndroidSchedulers.mainThread())
                   .subscribe(moviesLiveData::setValue);
       }
       return moviesLiveData;
   }
}

Тогда View сможет использовать эту ViewModel следующим образом:
viewModel.getMoviesList().observe(this, movies -> {
   if (movies != null) {
       adapter.setNewValues(movies);
   }
});

Поскольку ViewModel переживает пересоздание Activity, LiveData будет создана только один раз и серверный запрос будет выполнен только один раз, то есть эти проблемы решены. Остается вопрос создания ViewModel.

Для доступа к ViewModel используется класс ViewModelProviders:
MoviesViewModel viewModel = ViewModelProviders.of(this).get(MoviesViewModel.class);

Однако здесь есть небольшая проблема. Когда мы используем не конструктор по умолчанию (а в примере выше мы передавали в конструктор объект Repository), нам придется писать свою фабрику для создания ViewModel:
class MoviesViewModelProviderFactory extends BaseViewModelFactory {
 
   @NonNull
   private final MoviesRepository repository;
 
   MoviesViewModelProviderFactory(@NonNull MoviesService moviesService) {
       this.repository = new MoviesRepository(moviesService);
   }
 
   @NonNull
   @Override
   public  T create(@NonNull Class modelClass) {
       if (modelClass.equals(MoviesViewModel.class)) {
           //noinspection unchecked
           return (T) new MoviesViewModel(repository);
       }
       return super.create(modelClass);
   }
}

И теперь мы можем создать ViewModel следующим образом:
ViewModelProvider.Factory factory = new MoviesViewModelProviderFactory(service);
return ViewModelProviders.of(this, factory).get(MoviesViewModel.class);

В данном примере мы вручную передаем в Factory все нужные объекты для создания ViewModel, однако единственным нормальным подходом для создания ViewModel при росте числа параметров является Dagger. Здесь Google фактически вынуждает использовать его. Хорошо это или плохо, решать вам.

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

Показ прогресса и обработка ошибок


Когда я только начал разбираться с новыми архитектурными компонентами, у меня сразу возник вопрос относительно того, как с помощью LiveData обрабатывать ошибки или получать изменение прогресса. Ведь все, что у нас есть — это экземпляр одного класса, который мы должны передавать в Activity/Fragment. Аналогичный вопрос возникает и с отображением прогресса. Такая проблема немного похожа на проблему с лоадерами, однако здесь есть два варианта, как ее решить.

Во-первых, мы можем создать некоторый класс, объект которого будет инкапсулировать в себе статус запроса, сообщение об ошибке и сами данные, и передавать этот объект в Activity/Fragment. Этот способ рекомендуется в документации. Такой класс может выглядеть, например, следующим образом (пока что разберем только обработку ошибок):
public class Response {
 
   @Nullable
   private final T data;
 
   @Nullable
   private final Throwable error;
 
   private Response(@Nullable T data, @Nullable Throwable error) {
       this.data = data;
       this.error = error;
   }
 
   @NonNull
   public static  Response success(@NonNull T data) {
       return new Response(data, null);
   }
 
   @NonNull
   public static  Response error(@NonNull Throwable error) {
       return new Response(null, error);
   }
 
   @Nullable
   public T getData() {
       return data;
   }
 
   @Nullable
   public Throwable getError() {
       return error;
   }
}

Тогда при каждом изменении мы передаем во View объект этого класса:
public class MoviesViewModel extends ViewModel {
 
   @Nullable
   private MutableLiveData>> moviesLiveData;
 
   //...
 
   @MainThread
   @NonNull
   LiveData>> getMoviesList() {
       if (moviesLiveData == null) {
           moviesLiveData = new MutableLiveData<>();
           moviesRepository.popularMovies()
                   //...
                   .subscribe(
                           movies -> moviesLiveData.setValue(Response.success(movies)),
                           throwable -> moviesLiveData.setValue(Response.error(throwable))
                   );
       }
       return moviesLiveData;
   }
}

И View обрабатывает его соответствующим образом:
viewModel.getMoviesList().observe(this, moviesResponse -> {
   if (moviesResponse != null && moviesResponse.getData() != null) {
       adapter.setNewValues(moviesResponse.getData());
   } else if (moviesResponse != null && moviesResponse.getError() != null) {
       // show error
   }
});

И во-вторых есть способ, который на мой взгляд является более лаконичным. В конце концов, статус запроса и ошибки — это тоже некоторые изменяющиеся данные. Тогда почему бы не поместить их в LiveData и не подписываться на их изменения? Для этих целей мы можем написать следующий код (а в этой ситуации уже рассмотрим обработку статуса загрузки):
@NonNull
private final MutableLiveData loadingLiveData = new MutableLiveData<>();
 
@NonNull
LiveData isLoading() {
   return loadingLiveData;
}
 
@MainThread
@NonNull
LiveData>> getMoviesList() {
   if (moviesLiveData == null) {
       moviesLiveData = new MutableLiveData<>();
       moviesRepository.popularMovies()
               //...
               .doOnSubscribe(disposable -> loadingLiveData.setValue(true))
               .doAfterTerminate(() -> loadingLiveData.setValue(false))
               .subscribe();
   }
   return moviesLiveData;
}

И теперь View будет подписываться не на одну LiveData, а на несколько. Это может быть очень удобно, если мы пишем общий обработчик отображения прогресса или ошибок):
viewModel.isLoading().observe(this, isLoading -> {
   if (isLoading != null && isLoading) {
       progressDialog.show(getSupportFragmentManager());
   } else {
       progressDialog.cancel();
   }
});

В итоге можно сказать, что обработка ошибок с помощью LiveData почти так же хороша, как и с помощью привычной RxJava.

Тестирование


Как уже было сказано выше, хорошая архитектура должна позволять тестировать приложение. Поскольку принципиальных изменений архитектура от Google не несет, то тестирование можно рассматривать только на уровне ViewModel (так как мы знаем, как тестировать Repository/UI).

Тестирование ViewModel всегда чуть сложнее, чем тестирование Presenter-а из паттерна MVP, так как в MVP явная связь между View и Presenter позволяет легко проверять, что были вызваны нужные методы у View. В случае MVVM нам приходится проверять работу биндингов или же LiveData.

Создадим тестовый класс, в котором в методе setUp создадим ViewModel:
@RunWith(JUnit4.class)
public class MoviesViewModelTest {
 
   private MoviesViewModel viewModel;
 
   private MoviesRepository repository;
 
   @Before
   public void setUp() throws Exception {
       repository = Mockito.mock(MoviesRepository.class);
       Mockito.when(repository.popularMovies()).thenReturn(Observable.just(new ArrayList<>()));
 
       viewModel = new MoviesViewModel(repository);
   }
}

Чтобы протестировать корректность ViewModel, мы должны убедиться, что она выполняет запрос к Repository и передает полученные данные в LiveData:
@Test
public void testLoadingMovies() throws Exception {
   Observer observer = Mockito.mock(Observer.class);
   viewModel.getMoviesList().observeForever(observer);
 
   Mockito.verify(repository).popularMovies();
   Mockito.verify(observer).onChanged(any(Response.class));
}

Здесь опустим лишние детали, но при необходимости можно использовать возможности Mockito, чтобы удостовериться, что в LiveData пришли именно те данные, которые нужно.
Однако такой тест не сработает, так как метод setValue в классе LiveData проверяет, что он вызывается в главном потоке приложения (разумеется, с помощью Looper):
@MainThread
protected void setValue(T value) {
   assertMainThread("setValue");
   mVersion++;
   mData = value;
   dispatchingValue(null);
}

Как мы прекрасно знаем, тесты на JUnit этого не любят и падают. Но разработчики Google предусмотрели и это. Для этого нужно подключить дополнительную библиотеку для тестов и добавить правило к тесту:
testCompile ("android.arch.core:core-testing:$architectureVersion", {
   exclude group: 'com.android.support', module: 'support-compat'
   exclude group: 'com.android.support', module: 'support-annotations'
   exclude group: 'com.android.support', module: 'support-core-utils'
})
 
@Rule
public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();

Аналогичным образом мы можем протестировать показ прогресса во время загрузки данных:
@Test
public void testProgressUpdated() throws Exception {
   Observer observer = Mockito.mock(Observer.class);
   viewModel.isLoading().observeForever(observer);
   viewModel.getMoviesList();
 
   Mockito.verify(observer).onChanged(true);
   Mockito.verify(observer).onChanged(false);
}


Таким образом, мы можем без особых проблем писать тесты на все нужные компоненты.

Кэширование данных


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

Все прекрасно знают про существующий огромный зоопарк библиотек для баз данных в Android. Это Realm, greenDao, ObjectBox, DBFlow, SQLBrite и много других. Поэтому многие разработчики не знают, что выбрать для своего проекта, и здесь Room является идеальным вариантом, как минимум потому, что она от Google.

Room является достаточно простой библиотекой, которая работает поверх SQLite, использует аннотации для генерации boilerplate кода и имеет достаточно привычный и удобный API. Однако я не считаю, что смогу рассказать про Room что-то более интересное, чем было в сессии на Google I/O и чем есть в документации. Поэтому здесь мы закончим рассмотрение архитектурных новинок.

Заключение


В общем и целом Google делает правильные вещи, и их архитектура действительно весьма продумана и удобна. Так что можно начинать ее использовать уже сейчас, по крайней мере, стоит попробовать в новых проектах. Небольшой пример, по которому писалась статья и который, возможно, поможет вам попробовать новую архитектуру от Google, доступен на Github. Также, конечно, стоит посмотреть примеры от Google.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330208/


Метки:  

Методы работы с «тяжёлыми» XML

Понедельник, 05 Июня 2017 г. 14:34 + в цитатник
На работе попросили провести исследование какими средствами лучше разбирать объёмный XML файл (более 100Mb). Предлагаю сообществу ознакомиться с результатами.

Рассмотрим основные методы работы с XML:
1. Simple XML (documentation)
2. DOM (documentation)
3. xml_parser (SAX) (documentation)
4. XMLReader (documentation)


Simple XML


Минусы: работает очень медленно, собирает весь файл в память, дерево составляется в отдельных массив.
Плюсы: простота работы, работа «из коробки» (требует библиотеки libxml которая включена практически на всех серверах)

Пример использования Simple XML
$xml = simplexml_load_file("price.xml");
echo "\n";

foreach ($xml->xpath('/DocumentElement/price') as $producs) { ?> 
    

https://habrahabr.ru/post/330240/


Метки:  

Почему не работает Tree Shaking и как с этим жить

Понедельник, 05 Июня 2017 г. 14:34 + в цитатник

В нашей предыдущей статье про голосовых ботов для Рокетбанка хабраюзеры возмутились, что в 2017 году примеры JavaScript для облака Voximplant написаны на ES5. У нас в облаке сильно модифицированный SpiderMonkey, специально обученный не течь и не падать. Тысячи одновременных звонков с параллельно выполняемым JavaScript как бы намекают, что нода – для нас не вариант. Тем не менее, никто не мешает использовать транспайлеры, компилировать ES2017/TypeScript/Elm/Whatever в старый добрый JavaScript и загружать результаты компиляции с помощью Continuous Integration. При таком раскладе возникает соблазн использовать все последние достижения из npmjs, собирая весь код в один ES5 бандл. И вот тут нас ждет засада: даже один метод из lodash дает на выходе бандл размером в полмегабайта. И не похоже, чтобы рекламируемый последние пару лет tree shaking работал.


Кто трясет деревья?


Огромные JavaScript бандлы — это не очень хорошо. В мире браузеров они увеличивают время загрузки страницы: сначала такой бандл надо скачать, потом распарсить, потом выполнить. В мире backend и скриптов-в-облаке тоже свои нюансы. Чтобы выполнять тысячи звонков в секунду, контролируемых через JavaScript, наш SpiderMonkey ограничивает память JavaScript сессии 16-ю мегабайтами. Это на все: исходный код, ast, структуры данных. Архитектура нашей платформы подразумевает, что в облаке выполняется код, который должен работать в реальном времени. А все «тяжелые» вещи можно перенести на свой backend и делать к нему HTTP запросы прямо во время звонка. Беда в том, что пара методов из lodash выглядят как замечательная идея для легковесного кода в облаке. Хоп — и плюс полмегабайта к результирующему JavaScript.


Сообщество JavaScript разработчиков знает об этой проблеме давно, и кроме «давайте выкинем все пробелы и переименуем что не страшно в однобуквенные варианты» (uglify до dead code elimination) активно разрабатывает «Tree Shaking». В идеале, «Tree Shaking» должно убирать весь неиспользуемый код: импорты, вызовы методов, глобальные переменные. И для нашего кода мы должны получить несколько функций lodash, наш код – и всё. А вместо этого получаем lodash целиком. WTF?

Webpack, Rollup и Uglify


Поддержка tree shaking считается сильной стороной rollup, заявлена в последних версиях webpack и уже давно присутствует в UglifyJS в виде «dead code elimination». О разнице между «dead code elimination» два года назад очень хорошо написал автор Rollup: если dead code elimination получает на вход скомпилированный бандл и пытается выкинуть из него неиспользуемый код, то tree shaking работает с AST кода во время компиляции и пытается включить только тот код, который используется. Кстати, Webpack рассчитан на комбинированный подход: вначале tree shaking во время сборки бандла, а затем dead code elimination с помощью UglifyJS плагина.

Только в реальном мире Tree Shaking не работает.

По словам самих авторов, определить используемый код в слабо типизированным языке – задача нетривиальная. И, чтобы ничего не сломать, в непонятных ситуациях код всегда включается. К несчастью, примерами таких «непонятных» ситуаций являются самые популярные библиотеки общего назначения: lodash, underscore, – все вот эти ребята.

Что делать?


Можно, конечно, подождать еще пару лет. Вывод типов становится лучше, ведутся работы над поддержкой tree shaking для типизированных диалектов вроде TypeScript. Но писать ES2017 код с библиотеками хочется сейчас. Без многомегабайтных бандлов.

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


Конечно, это демонстрация «в лоб» с пустыми конфигами webpack/rollup. Можно докрутить и до более впечатляющих цифр, но основная идея в том, что не стоит огорчаться тысячам зависимостей, которые ставит yarn. Минимально допиленный напильником стек позволяет выкинуть большую часть неиспользуемого кода и получить вполне читаемый бандл для загрузки в Voximplant или любую другую платформу, которые программируется на JavaScript.

Картинка до ката из статьи про tree shaking в webpack 2
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330148/


Метки:  

Что такое диалоговые системы, или Кое-что об Элизе

Понедельник, 05 Июня 2017 г. 14:26 + в цитатник

Диалоговые системы давно и прочно вошли в нашу жизнь. В заглавии упомянута и на картинке представлена ELIZA — диалоговая система-психоаналитик (сейчас, ее назвали бы чат-бот), родом из 60-ых годов. Если вам интересно, как человек дошел до общения с ботами-психоаналитиками и что еще есть интересного в диалоговых системах, добро пожаловать под кат.


image

В самом деле сейчас диалоговые системы можно встретить где угодно: если вы звоните в банк, вы общаетесь (по крайней мере сначала) с диалоговой системой, когда вы делаете заказ или пытаетесь задать маршрут в навигаторе — тоже, может быть, вы пользуетесь Siri от Apple или Cortana от Microsoft, и это тоже они.


Чем привлекательны диалоговые интерфейсы? Тем, что это естественный для человека способ получения информации. (Собственно, поэтому робот, с которым вы разговариваете, когда куда-то звоните, называет “автоинформатор”.)


Классификация


Диалоговые системы можно охарактеризовать по следующим признакам: General — Task-oriented (общего назначения — задачеориентированный) и Open Domain — Closed Domain (способный говорить на любую тему или только на строго определенную). В каждой из пар первый компонент существенно сложнее второго. Давайте рассмотрим несколько примеров:



Начнем с самого простого — с автоинформаторов, они совершенно точно task-oriented и closed domain.
Вышеупомянутая ELIZA — closed domain (она умеет говорить только на тему психоанализа), но при этом general — у нее нет четко поставленной задачи, с ней можно “поболтать”.
Еще один пример бота, c которым можно поболтать, — CharRNN от Андрея Карпаты (Andrej Karpathy). Сама CharRNN — это просто нейросетевая модель, которая умеет продолжать данную ей строку, если натренировать ее, например, на субтитрах к фильмам и сериалам, то она научится “отвечать” на ваши реплики. В этом смысле она является general — у нее нет четко выраженной цели — и open domain — потенциально может говорить на любую тему. Проблема заключается только в том, что эта модель исключительно простая, она просто продолжает данную ей строку, не имея никакого понятия о диалоге, фразах и даже отдельных словах.


Из примеров остались два: ConvAI — это наше соревнование, о котором ниже, и true AI. Почему я отдельно выделил, что AI здесь “настоящий”? Потому что сейчас пошла мода называть AI все, что угодно, вплоть до автоинформаторов. Я же хотел подчеркнуть, что это полноценный искусственный интеллект, способный говорить на любую тему. И — самое важное — он способен вести беседу, то есть имеет представление о диалоге.


ConvAI


Теперь пришло время рассказать о нашем соревновании, чем оно отличается от приведенного выше и зачем мы решили его делать.
ConvAI — это Conversational Intelligence Challenge, соревнование разговорного искусственного интеллекта. А раз это соревнование, то нужно как-то участников сравнивать. И тут оказывается, что стандартные метрики сравнения текстов, известные по машинному переводу BLEU, ROUGE, etc. здесь не работают.



Получается, что машинные метрики, которые мы знаем, не коррелируют с человеческими суждениями (в то время как человеческие оценки между собой прекрасно коррелируют — верхняя правая картинка). Источник [1].


Из этого следует, что сравнивать системы общего назначения между собой пока не представляется возможным — мы просто не знаем, как это делать. Но зато мы совершенно точно можем сравнивать задачеориентированные системы с помощью простейшей метрики Task Completion Rate (TCR).



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



Стоит еще упомянуть, что наш challenge прошел жесткий отбор и был выбран, как NIPS Live Competition этого года. А также то, что соорганизаторами соревнования являются Йошуа Бенжио (Yoshua Bengio), который не нуждается в представлении, а также Александр Рудницкий (Alexander Rudnicky) и Алан Блэк (Alan W. Black) из университета Карнеги-Мэллон (Carnegie-Mellon University).
Всю информацию про соревнование, включая правила, API нашего сервера и другое, можно найти на его сайте convai.io.


Датасеты


Отдельно стоит сказать про доступные датасеты для исследований в области разговорного интеллекта. Существует несколько общедоступных датасетов для диалоговых систем [2]. Прежде всего, стоит выделить Dialog State Tracking Challenge, в этом году он, кстати, будет проводиться уже в шестой раз. Он рассчитан на системы, которые могут вести диалог, отслеживая его состояние (state), то есть является, пожалуй, самым близким к цели нашего соревнования. Но у этого датасета есть важная особенность — он является closed domain, то есть исключительно рассматривает одну конкретную тему. Open domain и task-oriented общедоступного датасета не существует, и мы рассчитываем, что после нашего соревнования будет сформирован новый датасет, состоящий из разговоров добровольцев и участников команд с ботами, который будет выложен в общий доступ для всех исследователей.


Заключение


Надеемся, что у вас сформировалось некоторое представление о диалоговых системах. Более того, если вы хотите попробовать себя в их создании, то мы будем рады вас видеть в качестве участника или добровольца (нам нужны люди, которые будут разговаривать с ботами наших участников). Если вы не представляете, с чего начать, то мы специально для вас сделали базовое решение. Оно требует немного пререквизитов для запуска — по факту, один только Docker. Так что — дерзайте!


P.S. Если вам интересно поучаствовать в создании искусственного интеллекта не только во время соревнования, но и на постоянной основе, у нас есть открытые позиции. Информацию можно почерпнуть на сайте ipavlov.ai.


Литература


  1. Chia-Wei Liu et al. How NOT To Evaluate Your Dialogue System: An Empirical Study of Unsupervised Evaluation Metrics for Dialogue Response Generation. arxiv:1603.08023
  2. Iulian Vlad Serban et al. A Survey of Available Corpora for Building Data-Driven Dialogue Systems. arxiv:1512.05742
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330228/


Метки:  

Как сделать context switch на STM32

Понедельник, 05 Июня 2017 г. 14:24 + в цитатник

Добрый день!
Потоки… Переключение контекстов… Базовая сущность ОС. И конечно, при разработке библиотек и приложений мы всегда полагаемся на то, что реализация потоков безошибочна. Поэтому было неожиданно найти грубую ошибку в переключении потоков для STM32 на ОСРВ Embox, когда уже продолжительное время работали и сеть, и файловая система и многие сторонние библиотеки. И мы даже успели похвастаться о своих достижениях на хабре.

Я бы хотел рассказать про то, как мы делали переключение потоков для Cortex-M, и тестировали на STM32. Кроме того, постараюсь рассказать о том как это сделано в других ОС — NuttX и FreeRTOS.

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

Я сел за отладку. Оказалось, что в потоках просто отключены все прерывания! Вы скажете, а как вообще могло тогда что-то работать? Все просто — много где есть sleep(), mutex_lock() и прочие “wait”, и за счет них потоки естественным образом и переключались. Проблема была, очевидно, связана с переключением контекстов для STM32F4, на которой я ее и обнаружил.

Давайте детальнее разберем проблему. Переключение контекстов потоков происходит в том числе по таймеру, то есть по прерываниям. Схематично обработку прерывания в Embox можно представить так:
void irq_handler(pt_regs_t *regs) {
        ...
    int irq = get_irq_number(regs);
    {
        ipl_enable();
        irq_dispatch(irq);
        ipl_disable();
    }
    irqctrl_eoi(irq);
        ...
    critical_dispatch_pending();
}

Вся суть в том, что сначала вызывается обработчик прерывания irq_dispatch, после этого “заканчивается” обработка прерывания, и контекст переключается на другой поток, если планировщик этого требует внутри critical_dispatch_pending. И тут очень важно, что состояние процессора в данном потоке должно быть такое же как и до того как его прервали, включая разрешение или запрещение прерываний. За разрешение прерываний отвечает бит в xPSR, который укладывается на стек самим процессором во время входа в прерывание, при выходе из прерываний он достается со стека. Проблема заключается в том, что так как мы имеем вытесняющую многозадачность, мы можем, войдя в прерывании на одном потоке, захотеть выйти на стеке другого потока, в котором конечно нет сохраненного xPSR. Более того, как и большинство ОС, мы имеем примитивы синхронизации, например, pthread_mutex_lock(), которые могут привести к переключению контекста не из прерывания. Мы вообще стали сомневаться, можно ли на cortex-m организовать вытесняющую многозадачность, ведь эта архитектура хорошо оптимизирована под небольшие задачи. Но стоп! А как же тогда работают другие операционки?

Обработка прерываний на Cortex-M


Давайте для начала разберемся как устроена обработка прерываний на Cortex-M.

На картинке показаны стеки в двух режимах — с плавающей точкой и без нее. Когда происходит прерывание, процессор сохраняет на стек соответствующие регистры, а в регистр LR помещает одно из следующих значений приведенных в таблице ниже. То есть, если прерывание вложенное, то там будет 0xFFFFFFF1.

Далее вызывается обработчик прерывания ОС, в конце которого обычно выполняется “bx lr” (напомним, что в LR находится 0xFFFFFFXX). После этого восстанавливаются автоматически сохраненные регистры, и исполнение программы продолжается.

Теперь рассмотрим, как же происходит переключение контекстов в разных ОС.

FreeRTOS


Давайте начнем с FreeRTOS. Для этого заглянем в portable/GCC/ARM_CM4F/port.c. Ниже представлен код функции xPortSysTickHandler:
xPortSysTickHandler
void xPortSysTickHandler( void )
{
    /* The SysTick runs at the lowest interrupt priority, so when this interrupt
    executes all interrupts must be unmasked.  There is therefore no need to
    save and then restore the interrupt mask value as its value is already
    known. */
    portDISABLE_INTERRUPTS();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* A context switch is required.  Context switching is performed in
            the PendSV interrupt.  Pend the PendSV interrupt. */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portENABLE_INTERRUPTS();
}


Это обработчик аппаратного таймера. Здесь мы видим, что если нужно сделать переключение контекстов, то инициируется некое прерывание PendSV. Как говорит документация — “PendSV is an interrupt-driven request for system-level service. In an OS environment, use PendSV for context switching when no other exception is active.” Внутри обработчика прерывания xPortPendSVHandler непосредственно и происходит переключение контекстов:
xPortPendSVHandler
void xPortPendSVHandler( void )
{
    /* This is a naked function. */
 
    __asm volatile
    (
    "   mrs r0, psp                         \n"
    "   isb                                 \n"
    "                                       \n"
    "   ldr r3, pxCurrentTCBConst           \n" /* Get the location of the current TCB. */
    "   ldr r2, [r3]                        \n"
    "                                       \n"
    "   tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, push high vfp registers. */
    "   it eq                               \n"
    "   vstmdbeq r0!, {s16-s31}             \n"
    "                                       \n"
    "   stmdb r0!, {r4-r11, r14}            \n" /* Save the core registers. */
    "                                       \n"
    "   str r0, [r2]                        \n" /* Save the new top of stack into the first member of the TCB. */
    "                                       \n"
    "   stmdb sp!, {r3}                     \n"
    "   mov r0, %0                          \n"
    "   msr basepri, r0                     \n"
    "   dsb                                 \n"
    "   isb                                 \n"
    "   bl vTaskSwitchContext               \n"
    "   mov r0, #0                          \n"
    "   msr basepri, r0                     \n"
    "   ldmia sp!, {r3}                     \n"
    "                                       \n"
    "   ldr r1, [r3]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */
    "   ldr r0, [r1]                        \n"
    "                                       \n"
    "   ldmia r0!, {r4-r11, r14}            \n" /* Pop the core registers. */
    "                                       \n"
    "   tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
 
    "   it eq                               \n"
    "   vstmdbeq r0!, {s16-s31}             \n"
    "                                       \n"
    "   stmdb r0!, {r4-r11, r14}            \n" /* Save the core registers. */
    "                                       \n"
    "   str r0, [r2]                        \n" /* Save the new top of stack into the first member of the TCB. */
    "                                       \n"
    "   stmdb sp!, {r3}                     \n"
    "   mov r0, %0                          \n"
    "   msr basepri, r0                     \n"
    "   dsb                                 \n"
    "   isb                                 \n"
    "   bl vTaskSwitchContext               \n"
    "   mov r0, #0                          \n"
    "   msr basepri, r0                     \n"
    "   ldmia sp!, {r3}                     \n"
    "                                       \n"
    "   ldr r1, [r3]                        \n" /* The first item in pxCurrentTCB is the task top of stack. */
    "   ldr r0, [r1]                        \n"
    "                                       \n"
    "   ldmia r0!, {r4-r11, r14}            \n" /* Pop the core registers. */
    "                                       \n"
    "   tst r14, #0x10                      \n" /* Is the task using the FPU context?  If so, pop the high vfp registers too. */
    "   it eq                               \n"
    "   vldmiaeq r0!, {s16-s31}             \n"
    "                                       \n"
    "   msr psp, r0                         \n"
    "   isb                                 \n"
    "                                       \n"
    #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */
        #if WORKAROUND_PMU_CM001 == 1
    "           push { r14 }                \n"
    "           pop { pc }                  \n"
        #endif
    #endif
    "                                       \n"
    "   bx r14                              \n"
    "                                       \n"
    "   .align 4                            \n"
    "pxCurrentTCBConst: .word pxCurrentTCB  \n"
    ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
    );
}


Но теперь давайте представим, что мы переключаемся на новый поток, который будет исполнять, скажем, некую функцию fn. То есть если мы просто поместим в PC адрес функции fn, то сразу же попадем в правильное место, но с неправильным контекстом — из прерывания же мы не вышли! FreeRTOS предлагает следующее решение. Давайте изначально проинициализируем создаваемый поток так, как если бы мы выходили из прерывания — /* Simulate the stack frame as it would be created by a context switch interrupt. */. В таком случае мы сначала “по-честному” выйдем из обработчика xPortPendSVHandler, то есть окажемся в правильном контексте, после чего, следуя по подготовленному стеку, попадем в fn. Ниже приведен код такой подготовки потока:
pxPortInitialiseStack
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    /* Simulate the stack frame as it would be created by a context switch
    interrupt. */
 
    /* Offset added to account for the way the MCU uses the stack on entry/exit
    of interrupts, and to ensure alignment. */
    pxTopOfStack--;
 
    *pxTopOfStack = portINITIAL_XPSR;   /* xPSR */
    pxTopOfStack--;
    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;    /* PC */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS;    /* LR */
 
    /* Save code space by skipping register initialisation. */
    pxTopOfStack -= 5;  /* R12, R3, R2 and R1. */
    *pxTopOfStack = ( StackType_t ) pvParameters;   /* R0 */
 
    /* A save method is being used that requires each task to maintain its
    own exec return value. */
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_EXEC_RETURN;
 
    pxTopOfStack -= 8;  /* R11, R10, R9, R8, R7, R6, R5 and R4. */
 
    return pxTopOfStack;
}


Итак, это был один из способов, предложенный во FreeRTOS.

NuttX


Давайте теперь посмотрим на другой метод, предложенный в NuttX. Это еще одна относительная известная ОС для разных мелких железок.

Главная часть обработки прерывания происходит внутри функции up_doirq, это по сути обработчик прерывания второго уровня, вызывается из ассемблерного кода. В нем происходит решение того надо ли переключаться на другой поток. Данная функция вернет необходимый контекст нового потока.
up_doirq
uint32_t *up_doirq(int irq, uint32_t *regs)
{
  board_autoled_on(LED_INIRQ);
#ifdef CONFIG_SUPPRESS_INTERRUPTS
  PANIC();
#else
  uint32_t *savestate;
 
  /* Nested interrupts are not supported in this implementation.  If you want
   * to implement nested interrupts, you would have to (1) change the way that
   * CURRENT_REGS is handled and (2) the design associated with
   * CONFIG_ARCH_INTERRUPTSTACK.  The savestate variable will not work for
   * that purpose as implemented here because only the outermost nested
   * interrupt can result in a context switch.
   */
 
  /* Current regs non-zero indicates that we are processing an interrupt;
   * CURRENT_REGS is also used to manage interrupt level context switches.
   */
 
  savestate    = (uint32_t *)CURRENT_REGS;
  CURRENT_REGS = regs;
 
  /* Acknowledge the interrupt */
 
  up_ack_irq(irq);
 
  /* Deliver the IRQ */
 
  irq_dispatch(irq, regs);
 
  /* If a context switch occurred while processing the interrupt then
   * CURRENT_REGS may have change value.  If we return any value different
   * from the input regs, then the lower level will know that a context
   * switch occurred during interrupt processing.
   */
 
  regs = (uint32_t *)CURRENT_REGS;
 
  /* Restore the previous value of CURRENT_REGS.  NULL would indicate that
   * we are no longer in an interrupt handler.  It will be non-NULL if we
   * are returning from a nested interrupt.
   */
 
  CURRENT_REGS = savestate;
#endif
  board_autoled_off(LED_INIRQ);
  return regs;
}


После возврата из функции мы снова оказываемся в обработчике первого уровня. И если нужно переключаться на новый поток, то модифицируем на стеке автоматически сохраненные при входе в прерывание регистры так, чтобы по завершении обработки прерывания попасть в нужный поток. Ниже приведен фрагмент кода.
    bl      up_doirq                /* R0=IRQ, R1=register save (msp) */
    mov     r1, r4                  /* Recover R1=main stack pointer */
 
    /* On return from up_doirq, R0 will hold a pointer to register context
     * array to use for the interrupt return.  If that return value is the same
     * as current stack pointer, then things are relatively easy.
     */
 
    cmp     r0, r1                  /* Context switch? */
    beq     l2                      /* Branch if no context switch */
	//Далее копируем регистры
…
    /* We are returning with a pending context switch.  This case is different
     * because in this case, the register save structure does not lie in the
     * stack but, rather, within a TCB structure.  We'll have to copy some
     * values to the stack.
     */
 
    add     r1, r0, #SW_XCPT_SIZE   /* R1=Address of HW save area in reg array */
    ldmia   r1, {r4-r11}            /* Fetch eight registers in HW save area */
    ldr     r1, [r0, #(4*REG_SP)]   /* R1=Value of SP before interrupt */
    stmdb   r1!, {r4-r11}           /* Store eight registers in HW save area */
#ifdef CONFIG_BUILD_PROTECTED
    ldmia   r0, {r2-r11,r14}        /* Recover R4-R11, r14 + 2 temp values */
#else
    ldmia   r0, {r2-r11}            /* Recover R4-R11 + 2 temp values */
#endif
	…

То есть в Nuttx (в отличие от FreeRTOS) уже модифицируются автоматически сохраненные на стек значения регистров. Это, пожалуй, основное отличие. Кроме того, можно заметить, что они прекрасно обходятся без PendSV (хотя ARM рекомендует :) ). Ну и последнее — само переключение контекстов у них отложенное, происходит через стек прерывания, а не по принципу — “сохранили старые значения и тут же загрузили в регистры новые”.

Embox


Наконец, про то как это сделано в Embox. Основная идея заключается в том, чтобы добавить некоторую дополнительную функцию (назовем ее __irq_trampoline), в которой сделать переключение контекстов уже “в обычном режиме”, а не в режиме обработки прерывания, и после этого уже по-настоящему выйти из обработчика прерывания. То есть, иными словами, мы постарались полностью сохранить логику, описанную в начале статьи:
void irq_handler(pt_regs_t *regs) {
          ...
    int irq = get_irq_number(regs);
    {
          ipl_enable();
          irq_dispatch(irq);
          ipl_disable();
    }
    irqctrl_eoi(irq); // Только тут теперь будет небольшая хитрость, а не прямой вызов
          ...
}

Для начала приведу рисунок, на котором представлена картина в целом. А далее объясню по частям что есть что.

Как это делается? Идея в следующем. Обработчик прерывания сначала выполняется обычным образом, как и на других платформах. Но при выходе из обработчика мы на самом деле модифицируем стек и выходим совсем в другое место — в __pending_handle! При этом происходит это так, как если бы прерывание действительно случилось на входе функции __pending_handle. Ниже приведен код, который модифицирует стек, чтобы выйти в __pending_handle. Я постарался написать к особо важным местам комменты на русском.
// Регистры сохраняемые процессором при входе в прерывание
struct cpu_saved_ctx {
    uint32_t r[5];
    uint32_t lr;
    uint32_t pc;
    uint32_t psr;
};
 
void interrupt_handle(struct context *regs) {
    uint32_t source;
    struct irq_saved_state state;
    struct cpu_saved_ctx *ctx;
 
    ... // Тут обычная обработка прерывания, пропустим
 
    state.sp = regs->sp;
    state.lr = regs->lr;
    assert(!interrupted_from_fpu_mode(state.lr));
    ctx = (struct cpu_saved_ctx*) state.sp;
    memcpy(&state.ctx, ctx, sizeof *ctx);
 
    // Ниже показано то как мы модифицируем стек
    /* It does not matter what value of psr is, just set up sime correct value.
     * This value is only used to go further, after return from interrupt_handle.
     * 0x01000000 is a default value of psr and (ctx->psr & 0xFF) is irq number if any. */
    ctx->psr = 0x01000000 | (ctx->psr & 0xFF);
    ctx->r[0] = (uint32_t) &state; // we want pass the state to __pending_handle()
    ctx->r[1] = (uint32_t) regs; // we want pass the registers to __pending_handle()
    ctx->lr = (uint32_t) __pending_handle;
    ctx->pc = ctx->lr;
 
    /* Now return from interrupt context into __pending_handle */
    __irq_trampoline(state.sp, state.lr);
}

Также приведем код функции __irq_trampoline. В комментариях к функции указано про чит с SP, но чтобы не перегружать статью я это пропускаю. Главное — это “bx r1” в конце функции. Напомню, что в регистре r1 находится второй аргумент функции __irq_trampoline. Если посмотреть код выше, то мы увидим вызов “__irq_trampoline(state.sp, state.lr)”, а это значит, что в регистре r1 находится значение state.lr, которое равно значению 0xFFFFFXX (см. Первый раздел)
__irq_trampoline
.global __irq_trampoline
__irq_trampoline:
 
    cpsid  i
    # r0 contains SP stored on interrupt handler entry. So we keep some data
    # behind SP for a while, but interrupts are disabled by 'cpsid i'
    mov    sp,  r0
    # Return from interrupt handling to usual mode
    bx     r1


Короче говоря, после выхода из функции __irq_trampoline мы раскручиваемся по стеку, выходим из прерывания и попадаем в __pending_handle. В этой функции мы делаем все оставшиеся операции (такие как context switch). При этом при выходе из этой функции нам необходимо вернуть на стек первоначально сохраненные значения регистров, после чего снова войти в прерывание и выйти из него, но уже в первоначальном месте! Для это делается следующая вещь. Мы сначала подготавливаем стек, затем инициируем прерывание PendSV, после чего оказываемся в обработчике __pendsv_handle. А далее обычным способом по-честному выходим из обработчика, но уже по первоначальному старому стеку. Код функций __pending_handle и __pendsv_handle приведен ниже:
__pending_handle и __pendsv_handle
.global __pending_handle
__pending_handle:
    // Тут выгружаем на стек “старый” контекст, чтобы выйти из прерывания
    // уже по-честному, то есть туда где нас изначально прервали.
    # Push initial saved context (state.ctx) on top of the stack
    add    r0, #32
    ldmdb  r0, {r4 - r11}
    push   {r4 - r11}
 
    // Тут восстанавливаем некоторые регистры. Но это не очень значимая деталь,
    // Для понимания эта деталь не важна, пропустим.
    ...
 
    cpsie  i
    // Вот тут переключаем контексты, если требуется
    bl     critical_dispatch_pending
    cpsid  i
    # Generate PendSV interrupt
    // Тут инициируем прерывание PendSV, обработчик приведен ниже
    bl     nvic_set_pendsv
    cpsie  i
    # DO NOT RETURN
1: b       1
 
.global __pendsv_handle
__pendsv_handle:
 
    # 32 == sizeof (struct cpu_saved_ctx)
    add    sp, #32
    # Return to the place we were interrupted at,
    # i.e. before interrupt_handle_enter
    bx     r14


В заключение скажу пару фраз о рассмотренных версиях реализации context_switch. Каждый из рассмотренных методов рабочий, имеет свои достоинства и недостатки. Нам не очень подходит вариант FreeRTOS, так как эта ОС, направлена прежде всего на микроконтроллеры, что влечет за собой некую “захардкоженность” context_switch под конкретный чип. А мы в своей ОС пытаемся предложить даже для микроконтроллеров использовать принципы “большой” ОС, со всеми вытекающими … Приблизительно такой же подход у NuttX, и может быть нам удастся либо реализовать подобный подход, либо улучшить наш с помощью идеи модификации стека. Но на данный момент наша версия вполне справляется со своими задачами, в чем можно убедиться если взять код из репозитория.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330236/


Как развернуть систему клиентской поддержки за 5 минут

Понедельник, 05 Июня 2017 г. 14:07 + в цитатник
Повышение эффективности — одна из самых актуальных и популярных тем в бизнесе. При этом всё более актуальными и востребованными становятся системы автоматизации поддержки пользователей. Это происходит прежде всего потому, что конкуренция на рынке заставляет компании бороться за лояльность буквально каждого своего клиента. Работа же службы клиентской поддержки станет значительно эффективнее, если правильно выбрать программное обеспечение. Конечно, не все системы одинаковы. Некоторые лучше подходят для малого и среднего бизнеса, а другие — для крупного. На рынке уже давно представлены продукты, которые так или иначе решают задачу автоматизации поддержки клиентов: с открытым и закрытым исходным кодом, платные и бесплатные, устанавливаемые на серверах и компьютерах пользователей, и SaaS-решения.





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

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


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

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


Шаг 1: Установка расширения


Работа с Deskun начинается с авторизации на сайте. В отличие от других сервисов, здесь не нужно регистрироваться, заполнять анкеты с информацией о компании и вводить данные сотрудников. Авторизуйтесь с помощью учетной записи Google на сайте Deskun и после этого установите расширение для браузера. Также вы можете просто установить плагин, и доступ к личному кабинету произойдет автоматически. Этот же процесс будет необходимо выполнить на всех компьютерах сотрудников, которые будут работать в сервисе. Напомним, что Deskun работает только с браузерами Google Chrome и Яндекс.Браузер, но разработчики в скором времени обещают запустить web-версию клиента.

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


Шаг 2: Создание нового проекта


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

image


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

Также на этом этапе можно сразу добавить сотрудников (агентов) компании в проект. Для этого необходимо ввести один или несколько e-mail адресов, на которые отправятся приглашения.

image


Шаг 3: Выбор сценария


После заполнения первоначальной информации сервис предложит выбрать сценарии развертывания нового проекта. На выбор доступны «Поддержка клиентов (email)» и «Управление задачами». Или же можно вручную настроить конфигурацию.

image


Для создания сервиса клиентской поддержки выбираем первый пункт. Сразу же потребуется ввести адрес электронной почты, который будет использоваться для приёма заявок клиентов. Вы можете использовать для этого почту Gmail, которую указали при регистрации в Deskun, или же любую другую. Для подтверждения доступа к внешнему ящику нужны такие данные, как SMTP-сервер, порт, логин и пароль. Как правило, SMTP-сервер и порт определяются Deskun автоматически, но можно и вручную настроить параметры.

image


Теперь остаётся только добавить агентов клиентской поддержки, если это не было сделано при создании проекта.

image


На этом первоначальная настройка Deskun заканчивается.

image


Шаг 4: Настройка очереди


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

image


Общие настройки


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

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


Агенты


Во вкладке Агенты выбирается ответственный сотрудник, на которого по умолчанию будут приходить все новые заявки от клиентов. В Deskun, как и в других сервисах, они называются «тикеты», но в нашем сервисе это, по сути, электронные письма. Для каждого тикета устанавливается исполнителя, приоритет, состояние и дедлайн. В зависимости от того, как именно вы используете Deskun, роль тикета может меняться: внешняя заявка — для службы поддержки клиентов, внутренняя задача — для системы управления проектами. Как работать с ними мы расскажем чуть ниже.

В этой же вкладке можно добавлять и удалять сотрудников, которые будут работать с конкретной очередью.

image


Почтовые настройки


В почтовых настройках изменяются параметры e-mail, который привязан к данной очереди.


Фильтры


Во вкладке «Фильтры» настраиваются сценарии, по которым письма от клиентов будут попадать в настраиваемую очередь. Например, можно создать фильтр, который будет срабатывать на заявки, отправленные с определённого e-mail, или же, наоборот, отправленные на определенный ящик. Естественно, данная почта должна быть привязана к аккаунту создателя фильтра.

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

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

  • {client_name} – имя клиента
  • {client_email} – e-mail клиента
  • {ticket_id} – id созданного тикета (если создался тикет)


image


Шаг 5: Работа с тикетами


Теперь Deskun полностью готов к работе. Потом вы сможете вернуться в Личный кабинет, чтобы настроить шаблоны сообщений. Это делается для удобства и ускорения работы сотрудников клиентской поддержки.

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

  • Для ручного создания тикета необходимо открыть письмо от клиента, нажать на иконку Deskun и выбрать пункт «Создать тикет в...». Далее выбирается очередь, к которой относится задача.
  • Если в настройках очереди вы указали ответственного сотрудника, то все тикеты будут автоматически адресоваться ему.


image


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

image


Работать с тикетами и контролировать выполнение задач можно из папки Deskun в Gmail. Там же доступна аналитика по работе вашей службы клиентской поддержки:
  1. Сводная информация.
  2. Последние действия.
  3. Информация по открытым и закрытым тикетам.



Заключение


За то время, пока вы читали этот текст, можно было полностью настроить Deskun для работы. Добавить всех сотрудников, создать фильтры и написать автоответы. При этом возможности сервиса на этом не исчерпываются. Вы сможете отправлять письма клиентам в заданное вами время и отслеживать их прочтение. Откладывать прочтение и выполнение писем-тикетов на определенное время, быстро распределять задачи между сотрудниками и гибко распределять зоны доступа и ответственности. При этом часть функционала, отвечающая за работу с электронной почтой, в Deskun полностью бесплатна. Таким образом, если вы, например, временно закрываете работу по каким-то направлениям, то достаточно удалить все проекты из сервиса и он автоматически перейдет в бесплатный режим. А вы сможете продолжать отслеживать прочтение ваших писем, пользоваться планировщиком отправки и откладывать прочтение. Платный функционал Deskun стоит всего от $0.99 в месяц.

В настоящий момент в планах у Deskun внедрить мультиканальную поддержку клиентов.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/330232/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 993 992 [991] 990 989 ..
.. 1 Календарь