...
суббота, 22 июля 2017 г.
Neural conversational models: как научить нейронную сеть светской беседе. Лекция в Яндексе
Под катом — расшифровка и основная часть слайдов.
Спасибо, что пришли. Меня зовут Боря Янгель, я в Яндексе занимаюсь применением deep learning к текстам на естественном языке и диалоговыми системами. Я сегодня вам хочу рассказать про Neural Conversational Models. Это сравнительно новая область исследований в deep learning, задача которой — научиться разрабатывать нейронные сети, которые с собеседником разговаривают на некоторые общие темы, то есть ведут то, что можно условно назвать светской беседой. Говорят «Привет», обсуждают, как у тебя дела, ужасную погоду или фильм, который ты недавно посмотрел. И сегодня я хочу рассказать, что в этой области уже было сделано, что можно делать на практике, пользуясь результатами, и какие остались проблемы, которые только предстоит решить.
Мой доклад будет устроен примерно следующим образом. Сперва мы немножко поговорим о том, зачем может понадобиться учить нейронные сети светской беседе, какие данные и нейросетевые архитектуры нам для этого будут нужны, и как мы будем обучать, чтобы решить эту задачу. В конце немного поговорим о том, как оценить, что у нас в результате получилось, то есть о метриках.
Зачем учить сети разговаривать? Кто-то может подумать, что мы учим, чтобы сделать искусственный интеллект, который кого-нибудь когда-нибудь поработит.
Но таких амбициозных задач мы перед собой не ставим. Более того, я сильно сомневаюсь, что методы, о которых я сегодня буду рассказывать, нам помогут сильно приблизиться к созданию настоящего искусственного интеллекта.
Вместо этого мы ставим перед собой цель делать более интересные голосовые и диалоговые продукты. Есть такой класс продуктов, который называется, например, голосовые ассистенты. Это такие приложения, которые в формате диалога вам помогают решить какие-то насущные задачи. Например, узнать, какая погода сейчас, или вызвать такси или узнать, где находится ближайшая аптека. Про то, как делаются такие продукты, вы узнаете на втором докладе моего коллеги Жени Волкова, а меня сейчас интересует вот какой момент. Хочется, чтобы в этих продуктах, если пользователю ничего сейчас не нужно, он мог с системой о чем-нибудь поболтать. И продуктовая гипотеза состоит в том, что если с нашей системой можно будет иногда поболтать, причем эти диалоги будут хорошими, интересными, уникальными, не повторяющимися — то к такому продукту пользователь будет возвращаться чаще. Такие продукты хочется делать.
Как их можно делать?
Есть путь, которым пошли, например, создатели Siri — можно взять и заготовить много реплик ответов, какие-то реплики, которые часто говорит пользователь. И когда вы произносите одну из этих реплик и получаете созданный редакторами ответ — все замечательно, это здорово выглядит, пользователям нравится. Проблема в том, что стоит вам сделать шажок в сторону от этого сценария, и вы тут же видите, что Siri — это не более чем глупая программа, которая может в одной реплике употребить какую-то фразу, а в следующей же реплике сказать, что ей неизвестен смысл этой фразы — что, по меньшей мере, странно.
Вот пример похожего по структуре диалога с ботом, который я сделал с помощью методов, о которых я вам сегодня буду рассказывать. Он, может быть, никогда не отвечает так же интересно и витиевато, как Siri, но зато ни в какой момент времени он не производит впечатление совсем уж глупой программы. И кажется, что это может быть лучше в каких-то продуктах. А если это совместить с подходом, который используется в Siri и отвечает редакторскими репликами, когда вы можете иначе делать fallback на такую модель, — кажется, что получится еще лучше. Наша цель — делать такие системы.
Какие данные нам понадобятся? Давайте я чуть забегу вперед и сперва скажу, с какой постановкой задачи мы будем работать, потому что это важно для обсуждения нашего вопроса. Мы хотим по репликам в диалоге до текущего момента, а также, возможно, какой-то другой контекстной информации о диалоге — например, где и когда этот диалог происходит — предсказать, какой должна быть следующая реплика. То есть — предсказать ответ.
Чтобы решать такую задачу с помощью deep learning, нам хорошо бы иметь корпус с диалогами. Этот корпус лучше бы был большим, потому что deep learning с маленькими текстовыми корпусами — сами, наверное, знаете, как он работает. Хорошо бы, чтобы диалоги были на нужные нам темы. То есть если мы хотим сделать бота, который будет обсуждать с вами ваши чувства или говорить о погоде, то такие диалоги должны быть в диалоговом корпусе. Поэтому корпус диалогов со службой поддержки интернет-провайдера нам в решении проблемы вряд ли подойдет.
Хорошо бы в корпусе знать автора каждой реплики хотя бы на уровне уникального идентификатора. Это поможет нам как-то моделировать тот факт, что, например, разные спикеры пользуются разной лексикой или вообще обладают разными свойствами: их по-разному зовут, они в разных местах живут и по-разному отвечают на одни вопросы. Соответственно, если у нас есть какие-то метаданные о спикерах — пол, возраст, место проживания и так далее — то это нам еще лучше поможет смоделировать их особенности.
Наконец, какие-то метаданные о диалогах — время или место, если это диалоги в реальном мире, — тоже полезны. Дело в том, что два человека могут вести совсем разные диалоги в зависимости от пространственно-временного контекста.
В литературе, то есть в статьях про Neural Conversational Models, очень любят два датасета.
Первый из них — Open Subtitles. Это просто субтитры из огромного числа американских фильмов и сериалов. Какие плюсы этого датасета? В нем очень много жизненных диалогов, прямо таких, которые нам нужны, потому что это фильмы, сериалы, там люди часто говорят друг другу: «Привет! Как дела?», обсуждают какие-то жизненные вопросы. Но поскольку это фильмы и сериалы, то здесь же кроется и минус датасета. Там много фантастики, много фэнтези, которое аккуратно нужно вычищать, и много довольно своеобразных диалогов. Я помню, первая модель, которую мы обучили на Open Subtitles, она к месту и не к месту очень много про вампиров почему-то говорила. На вопрос «Откуда ты?» иногда отвечала: «Я, мать твою, из ФБР». Кажется, что не каждый захочет, чтобы его диалоговый продукт вел себя таким образом.
Это не единственная проблема датасета с субтитрами. Он как сформирован? Надеюсь, многие из вас знают, что такое srt-файлы. Фактически авторы датасета просто взяли srt-файлы этих фильмов и сериалов, все реплики оттуда и записали в огромный текстовый файл. Вообще говоря, в srt-файлах ничего не понятно о том, кто какую реплику говорит и где заканчивается один диалог и начинается другой. Можно пользоваться разными эвристиками: например, предполагать, что две последовательные реплики всегда говорят разные спикеры, или, например, что если между репликами прошло больше 10 секунд, то это разные диалоги. Но подобные предположения выполняются в 70% случаев, и это создает много шума в датасете.
Есть работы, в которых авторы пытаются, например, опираясь на лексику спикеров, сегментировать все реплики в субтитрах на то, кто что говорит и где кончается один диалог и начинается другой. Но никаких прямо очень хороших результатов достигнуть пока не получилось. Кажется, что если использовать дополнительную информацию — например, видео или звуковую дорожку, — то можно сделать лучше. Но я пока ни одной такой работы не знаю.
Мораль какая? С субтитрами нужно быть осторожным. На них, наверное, можно предобучать модели, но учить до конца с учетом всех этих минусов я не советую.
Следующий датасет, который очень любят в научной литературе — это Твиттер. В Твиттере про каждый твит известно, корневой ли он или же является ответом на какой-то другой твит. Корневой в том смысле, что он не написан как ответ. Соответственно, это дает нам точную разбивку на диалоги. Каждый твит образует дерево, в котором путь от корня, то есть от корневого твита до листа, — какой-то диалог, часто довольно осмысленный. В Твиттере известен автор и время каждой реплики, можно получить дополнительную информацию о пользователях, то есть что-то там написано прямо в профиле пользователя Твиттера. Профиль можно поматчить с профилями в других социальных сетях и еще что-то узнать.
Какие минусы у Твиттера? В первую очередь он, очевидно, смещен в сторону размещения и обсуждения ссылок. Но оказывается, что если убрать все диалоги, в которых корневой твит содержит ссылку, то оставшееся — оно во многом, не всегда, но часто напоминает ту самую светскую беседу, которую мы пытаемся смоделировать. Однако также оказывается, что диалоги на светские темы, по крайней мере в русском Твиттере —за английский ручаться не буду — ведутся в основном школьниками.
Мы это выяснили следующим образом. Мы обучили какую-то модель на Твиттере в первый раз и задали ей несколько простых вопросов типа «Ты где?» и «Сколько тебе лет?».
В общем, на вопрос «Ты где?» единственный цензурный ответ был «В школе», а все остальные отличались разве что знаками препинания. Но ответ на вопрос «Сколько тебе лет?» окончательно расставил все на свои места. Поэтому тут мораль какая? Если хотите учить диалоговые системы на этом датасете, то проблему школьников как-то надо решить. Например, надо пофильтровать датасет. Ваша модель будет разговаривать как часть спикеров — нужно оставить только нужную часть или воспользоваться одним из методов кластеризации спикеров, про которую я чуть дальше поговорю.
Эти два датасета любят в научной литературе. А если вы собираетесь делать что-то на практике, то вы во многом ограничены разве что своей фантазией и названием компании, на которую вы работаете. Например, если вы Facebook, то вам повезло иметь свой мессенджер, где огромное количество диалогов как раз на те темы, которые нас интересуют. Если вы не Facebook, у вас все еще есть какие-то возможности. Например, можно достать данные из публичных чатов в Telegram, в Slack, в каких-то IRC-каналах, можно распарсить какие-то форумы, поскрепить какие-то комментарии в социальных сетях. Можно скачать сценарии фильмов, которые на самом деле следуют некоторому формату, который в принципе можно распарсить автоматически — и даже понять, где там кончается одна сцена, где кончается другая и кто автор конкретной реплики. Наконец, можно какие-то транскрипты телепередач найти в интернете, и я на самом деле уверен, что я перечислил только малую часть всевозможных источников для диалогового корпуса.
Мы поговорили про данные. Теперь давайте к самой главной части перейдем. Какие же нам нейронные сети надо на этих данных учить, чтобы у нас получилось что-то, что может разговаривать? Я напомню вам постановку задачи. Мы хотим по предыдущим репликам, которые были сказаны до текущего момента в диалоге, предсказать, какой же должна быть следующая реплика. И все подходы, которые решают эту задачу, можно условно разделить на два. Я их называю «порождающий» и «ранжирующий». В порождающем подходе мы моделируем условное распределение на ответ при фиксированном контексте. Если у нас такое распределение есть, то, чтобы отвечать, мы берем его моду, допустим, или просто сэмплируем из этого распределения. А ранжирующий подход — это когда мы обучаем некоторую функцию уместности ответа при условии контекста, который необязательно имеет вероятностную природу. Но, в принципе, это условное распределение из порождающего подхода тоже может быть с этой функцией уместности. А потом берем некоторый пул кандидатов ответов и выбираем из него лучший ответ для заданного контекста с помощью нашей функции уместности.
Сперва поговорим про первый подход — порождающий.
Зедсь нам нужно знать, что такое рекуррентные сети. Я, честно говоря, надеюсь, что если вы пришли на доклад, где в названии есть нейросети, то вы знаете, что такое рекуррентные сети — потому что из моего сбивчивого минутного объяснения вы все равно вряд ли поймете, что это такое. Но правила такие, что я должен о них рассказать.
Итак, рекуррентные сети — это такая нейросетевая архитектура для работы с последовательностями произвольной длины. Работает она следующим образом.
У рекуррентной сети есть некоторое внутреннее состояние, которое она обновляет, проходясь по всем элементам последовательности. Условно можно считать, что проходит она слева направо. И так же опционально рекуррентная сеть на каждом шаге может сгенерировать какой-то выход, который идет куда-то дальше в вашей многослойной нейронной сети. И в классических нейронных сетях под названием vanilla RNN функция обновления внутреннего состояния — это просто некоторая нелинейность поверх линейного преобразования входа и предыдущего состояния, а выход — тоже нелинейность поверх линейного преобразования внутреннего состояния. Все любят рисовать вот так, или еще разворачивать по последовательностям. Мы в дальнейшем будем пользоваться второй нотацией.
На самом деле такими формулами обновления никто не пользуется, потому что если обучать такие нейронные сети, возникает очень много неприятных проблем. Пользуются более продвинутыми архитектурами. Например, LSTM (Long short-term memory) и GRU (Gated recurrent units). Дальше, когда мы будем говорить «рекуррентная сеть», мы будем предполагать что-то более продвинутое, чем простые рекуррентные сети.
Порождающий подход. О нашей задаче генерации реплики в диалоге по контексту можно думать как о задаче генерации строки по строке. То есть представим, что мы возьмем весь контекст, все предыдущие сказанные реплики, и просто сконкатенируем их, разделяя реплики разных спикеров некоторым специальным символом. Получается задача генерации строки по строке, а такие задачи неплохо изучены в машинном обучении, в частности — в машинном переводе. И стандартная архитектура в машинном переводе — так называемая sequence-to-sequence. И state of the art в машинном переводе — это все еще модификация подхода sequence-to-sequence. Он был предложен Суцкевером в 2014 году, а позже как раз адаптирован его соавторами для нашей задачи, Neural Conversational Models.
Что такое sequence-to-sequence? Это рекуррентная архитектура encoder-decoder, то есть это две рекуррентных сети: encoder и decoder. Encoder прочитывает исходную строку и генерирует некоторое ее конденсированное представление. Это конденсированное представление подается на вход декодеру, который уже должен сгенерировать выходную строку или для каждой выходной строки сказать, какая же у нее вероятность в этом условном распределении, которое мы пытаемся смоделировать.
Выглядит это следующим образом. Желтенькое — сеть encoder. Допустим, у нас есть диалог двух спикеров из двух реплик «Привет» и «Здаров», для которого мы хотим сгенерировать ответ. Реплики спикеров мы разделим специальным символом end-of-sentense, eos. На самом деле не всегда разделяют предложение, но исторически его называют именно так. Каждое слово мы сперва погрузим в некоторое векторное пространство, сделаем то, что называется vector embedding. Затем этот вектор для каждого слова мы подадим на вход сети encoder, и последнее состояние сети encoder после того, как она обработает последнее слово, как раз и будет нашим конденсированным представлением контекста, которое мы подадим на вход в decoder. Мы можем, например, инициализировать первое скрытое состояние сети decoder этим вектором или, в качестве альтернативы, например, подать его на каждый timestamp вместе со словами. Сеть decoder на каждом шаге генерирует очередное слово реплики и на вход получает предыдущее слово, которое она сгенерировала. Это позволяет действительно лучше моделировать условное распределение. Почему? Я не хочу вдаваться сейчас в детали.
Генерирует decoder все до тех пор, пока не сгенерирует токен end-of-sentence. Это значит, что «Все, хватит». А на вход на первом шаге decoder, как правило, тоже получает токен end-of-sentence. И непонятно, что ему на вход нужно подать.
Обычно такие архитектуры обучаются с помощью обучения maximum likelihood. То есть мы берем условное распределение на ответы при известных нам контекстах в обучающей выборке и пытаемся сделать известные нам ответы как можно более вероятными. То есть максимизируем, допустим, логарифм такой вероятности по параметрам нейронной сети. А когда нам нужно сгенерировать реплику, у нас параметры нейронной сети уже известны, потому что мы их обучили и зафиксировали. И мы просто максимизируем условное распределение по ответу или сэмплируем из него. На самом деле точно его промаксимизировать нельзя, поэтому приходится пользоваться некоторыми приближенными методами. Например, есть метод стохастического поиска максимума в таких архитектурах encoder-decoder. Называется beam search. Что это такое, я тоже сейчас рассказать не успею, но ответ на данный вопрос легко найти в интернете.
Все модификации этой архитектуры, которые были придуманы для машинного перевода, можно попробовать применить и для Neural Conversational Models. Например, encoder и decoder, как правило, многослойные. Они работают лучше, чем однослойная архитектура. Как я уже сказал, это, скорее всего, LSTM- или GRU-сети, а не обычные RNN.
Encoder, как правило, двунаправленный. То есть на самом деле это две рекуррентные сети, которые проходятся по последовательности слева направо и справа налево. Практика показывает, что если идти только с одного направления, то пока вы дойдете до конца, сеть уже забудет, что там было сначала. А если идти с двух сторон, то у вас есть информация и слева, и справа в каждый момент. Это работает лучше.
Потом в машинном переводе есть такой трюк, прием, который называется attention. Его идея примерно в следующем. Каждый раз, когда ваш декодер генерирует очередное слово, вы можете еще дополнительно посмотреть на все слова или на скрытое представление на каждом timestamp в encoder и как-то их взвесить согласно тому, что вам сейчас нужно. Например, для генерации очередного слова вам нужно найти какой-нибудь следующий предлог во входной последовательности или понять, какая именованная сущность там определялась. Механизм attention помогает это сделать, и он немного помогает в Neural Conversational Models, но на самом деле намного меньше, чем в машинном переводе. Кажется, так происходит потому, что в машинном переводе в большинстве случаев для перевода очередного слова нужно посмотреть на одно слово в исходной последовательности. А при генерации реплики нужно посмотреть на много слов. И возможно, здесь будут работать лучше какие-то приемы, аналогичные тем, которые используются в memory networks. Типа multi-hole potential.
На самом деле того, что я вам сейчас рассказал, уже достаточно для создания некой Neural Conversational Model — при условии, что у вас есть данные. И она как-то будет разговаривать. Не могу сказать, что прямо очень плохо, но если вы будете с ней говорить, вы неизбежно столкнетесь с рядом проблем.
Первая проблема, которую вы увидите, — так называемая проблема слишком «общих» реплик. Это известная проблема модели encoder-decoder sequence-to-sequence, которая заключается в следующем. Такие модели склонны генерировать некие очень общие короткие по длине фразы, которые подходят к большому числу контекстов. Например, «Я не знаю», «Окей», «Я не могу сказать» и т. д. Почему так происходит? Можно, например, почитать статью, где авторы попытались формализовать некоторым образом это явление и показали, что в таких архитектурах оно будет происходить неизбежно.
В литературе про Neural Conversational Models предложен ряд решений или, я бы сказал, «костылей» для решения этой проблемы. Все они основаны на том, что мы по-прежнему обучаем модели в режиме максимизации правдоподобия, но когда генерируем реплику, то максимизируем не правдоподобие, а некоторый модифицированный функционал, который в себе это правдоподобие содержит.
Первая идея, которая появилась в литературе, — вместо правдоподобия максимизировать взаимную информацию между ответом и контекстом.
Что это значит на практике? Вот было такое выражение, которое мы максимизировали по ответу. А теперь давайте добавим к нему такой член. Это некий коэффициент, умноженный на априорную вероятность ответа. На самом деле это некое обобщение взаимной информации между ответом и контекстом. Такой коэффициент равен единице — получается как раз взаимная информация. Если он равен нулю, то получается исходный функционал. Но он может принимать этот параметр и промежуточные значения, чтобы вам можно было что-нибудь настроить в своем методе.
Какой у этого выражения смысл? Если мы его будем максимизировать, то теперь мы ищем не только уместный ответ при условии контекста, но и пенализируем ответы с большой априорной вероятностью. То есть, грубо говоря, пенализируем те ответы, которые часто встречаются в обучающем корпусе и которые можно сказать по поводу и без повода — как раз эти «Привет», «Как дела» и т. д.
Чтобы этим методом воспользоваться, вам теперь нужно не только обучить модель sequence-to-sequence, которая выдает указанную вероятность, но и обучить некоторую языковую модель на всевозможных ответах — чтобы получить эту вероятность. То есть возникает минус — нужно две модели.
Есть альтернативный способ переписать этот функционал, а точнее — записать другой функционал, который равен предыдущему с точностью до константы. Он факторизован немножко по-другому. Здесь все еще есть наша условная вероятность ответа при условии контекста, а еще есть вероятность контекста при условии ответа. Это можно проинтерпретировать следующим образом. Мы хотим не только ответы, уместные в данном контексте, но и такие ответы, по которым легко восстановить исходный контекст. То есть если ответ слишком общий, «Окей» или «Я не знаю», то совершенно непонятно, в каком контексте это было сказано. И такие ответы мы хотим штрафовать. Чтобы таким приемом воспользоваться, вам нужна и модель sequence-to-sequence, которая ответ генерирует по контексту, и модель sequence-to-sequence, которая контекст генерирует по ответу. То есть вам всё ещё нужны две модели.
Сравнительно недавно в статье, которую подали на ICLR, был предложен метод, в котором нужна всего одна модель. Тут идея такая. Мы при генерации реплики случайно выбираем сколько-то контекстов из нашего пула — допустим, из обучающей выборки. Затем наш функционал меняется следующим образом. Мы вычитаем из него такую пронормированную вероятность ответа при условии случайного контекста. Тут идея примерно такая же, как и на предыдущем слайде. Если наш ответ уместен для какого-то значительного числа случайных контекстов — это плохо, это значит, что он слишком общий. И на самом деле, если посмотреть на это формально, то перед нами всего лишь оценка Monte Carlo для MMI, который был записан на предыдущем слайде. Но ее прелесть в том, что дополнительная модель вам не нужна, и эмпирически почему-то это работает даже лучше, чем честный MMI.
Например, у честного MMI есть такое неприятное свойство, что этот член штрафует не только слишком общие ответы, но и грамматически корректные ответы, потому что грамматически корректные ответы более вероятны, чем грамматически некорректные. В результате, если неаккуратно подстроить коэффициент А, то сеть начинает разговаривать совершенно некогерентно. Это плохо.
Следующая проблема, с которой вы столкнетесь, — проблема консистентности ответов. Заключается она в следующем. Сеть на одни и те же вопросы, сформулированные по-разному или заданные в разных контекстах, будет давать разные ответы. Почему? Потому что сеть обучали на всем датасете в режиме максимизации правдоподобия, то есть она обучилась отвечать правильно в среднем по датасету. Если какой-то ответ часто встречается в датасете, значит, так можно отвечать. У сети нет никакого понятия о собственной личности и о том, что все ее ответы должны быть когерентными.
Если весь ваш датасет состоит из ответов одного и того же спикера, это никаких проблем не создаст, но вряд ли у вас есть датасет, в котором миллионы или десятки миллионов таких ответов. Поэтому проблему как-то надо решать.
Вот одно из решений, которое было предложено в литературе, в статье «A Persona-Based Neural Conversation Model»: давайте мы каждому спикеру дополнительно сопоставим вектор в некотором латентном пространстве спикеров. Так же, как мы слова погружаем в латентное пространство, мы и этот вектор будем подавать на вход декодеру в надежде, что при обучении мы туда запишем какую-то информацию, которая нужна, чтобы генерировать ответы от имени данного спикера. То есть, грубо говоря, запишем туда его пол, возраст, какие-то лексические особенности и т. д. И тут же у нас появится некий инструмент контроля поведения модели. Другими словами, мы компоненты этого вектора потом как-то сможем настраивать и, возможно, добиваться от сети желаемого поведения.
Но выглядит это в архитектуре sequence-to-sequence примерно следующим образом: все как раньше, только тут добавляется еще один вектор, который подается на каждый timestamp декодера. Например — конкатинируется с этим embedding-вектором слова.
В моделях с латентными переменными обычно есть проблема: тот факт, что мы хотим, чтобы в этот вектор записалась какая-то информация о спикере, еще не означает, что так действительно произойдет при обучении. В общем, нейронная сеть вправе распорядиться вектором как угодно. Ее представления необязательно совпадают с нашими. Но если обучить такую модель, а потом нарисовать, допустим, это пространство спикеров на плоскости с помощью алгоритма t-SNE или чего-нибудь подобного и поискать в нем какую-нибудь структуру, окажется, что она есть.
Например, можно нарисовать это пространство и отметить на нем возраст спикеров. Здесь светлые точки — это, грубо говоря, школьники, а красные точки — люди, которым больше 30 лет, если я не ошибаюсь. То есть видно, что это пространство слоистое, и сверху там находятся в основном школьники. Дальше идут студенты, потом молодые профессионалы и, наконец, люди, которым больше 30 или скольки-то лет. Другими словами, какая-то структура есть. Хорошо.
Можно сделать еще так. Я для некоторого числа пользователей Твиттера посмотрел, фолловят ли они некоторые аккаунты либеральных политиков или не фолловят, и это тоже нарисовал в указанном пространстве. Те, кто фоловят, оказались в основном в правом нижнем углу пространства. Это еще одно свидетельство, что там присутствует некоторая структура.
Сами авторы в статье приводят такую табличку, которая иллюстрирует, что их сеть научилась отвечать на вопросы консистентно. Тут ей задается ряд вопросов про ее домашний город, про то, откуда она, из какой страны, чем она занималась в колледже и т. д. И вроде как она консистентно отвечает. И вообще они там приводят, скажем, сравнение log-likelihood у моделей, в которых есть информация о спикере и в которых нет. Утверждается, что log-likelihood у моделей, которые знают про спикера, лучше.
Но они дальше говорят: «Наша цель не была полностью достигнута, потому что так же легко можно найти диалог, где это свойство не выполняется и где модель вроде как бы и уверенно, но периодически сбивается и отвечает в среднем по датасету». То есть проблема окончательно не решена, нужно работать. Это все, что я про порождающие модели хотел рассказать. Давайте теперь немного поговорим про ранжирующие.
Тут идея такая: вместо того, чтобы генерировать ответ с помощью какого-то вероятностного распределения, мы будем ранжировать ответы из некоторого пула согласно функции уместности ответа при условии контекста, которую мы обучим. Какие плюсы у такого подхода? Вы полностью контролируете пул ответов. Вы можете исключить грамматически некорректные ответы или ответы с обсценной лексикой, например. Тогда вы их никогда не сгенерируете, и вы меньше рискуете, чем при использовании порождающей модели, о которой я говорил раньше. Обучение таких архитектур происходит на порядки быстрее, и меньше проявляется проблема общих ответов — потому что она, скорее, свойственна архитектурам sequence-to-sequence encoder-decoder.
А минус, очевидно, такой: множество реплик, которые вы можете сказать, ограничено. И там, скорее всего, окажется реплика не на каждую ситуацию. Как только вам понадобится нечто не совсем тривиальное, скорее всего, его в вашем пуле не окажется.
Как обычно устроены ранжирующие модели? Примерно следующим образом. Есть две сети, которые тут уже называются — и та сеть, и другая — encoder. Задача одной сети — получить некоторое конденсированное векторное представление контекста, другой — векторное представление ответа. Дальше уместность контекста при условии ответа считается с помощью некоторой функции сравнения двух векторов, но и получается в итоге некоторое число, которое говорит об уместности. Такая архитектура стала популярной после статьи Microsoft Research про DSSM, Deep Structure Semantic Models, в 2013 году. И впоследствии указанная архитектура тоже была не раз адаптирована во множестве разных статей для Neural Conversational Models.
Сети encoder, в принципе, могут быть любыми, если они по набору слов могут получить вектор. Например, это тоже могут быть рекуррентные сети — как и в архитектурах sequence-to-sequence. Или можно пойти более простым путем: это могут быть полносвязанные сети поверх усредненных эмбединговых слов. Тоже на удивление неплохо работает.
Как правило, функция уместности ответа в контексте — что-то простое, ведь нам нужно просто сравнить два вектора, скалярное произведение или косинусное расстояние, например.
Как обучаются такие модели? Поскольку они не порождающие, то тут уже положительных примеров недостаточно, нужны отрицательные. Если вы хотите, чтобы что-то отранжировалось высоко согласно вашей функции, вам нужно сказать, что должно отранжироваться низко.
Где брать отрицательные примеры? Классический подход — random sampling, когда вы просто берете случайные реплики в вашем датасете, говорите, что с большой вероятностью они неуместны и на это предположение опираетесь. Есть чуть более нетривиальный подход, который называется hard negative mining. Там идея такая: вы выбираете случайные реплики, но потом из случайных выбираете те, на которых сейчас модель сильнее всего ошибается.
С недавних пор в веб-ранжировании Яндекса существует алгоритм «Палех». Он во многом опирается на аналогичную архитектуру, и в статье на Хабрахабре написано, как этот hard negative mining может работать.
Теперь у вас есть положительные и отрицательные примеры. Что со всем этим делать? Нужна какая-то функция штрафа. Как правило, поступают очень просто: берут выходы этой функции Sim, которая является скалярным произведением или косинусным расстоянием, прогоняют через softmax и получают вероятностное распределение на ваш положительный пример и сколько-то отрицательных примеров, которые вы нагенерировали. А потом, как и в порождающих моделях, просто пользуются кроссэнтропийным лоссом, то есть хотят, чтобы вероятность правильного ответа была большой по сравнению с вероятностью неправильных. Есть всякие модификации на основе tripletloss. Это что-то типа подходов max margin, когда вы хотите, чтобы уместность вашего ответа при условии контакта была больше, чем уместность случайного ответа при условии контекста на некоторый margin, как в SVN. Про это тоже в интернете можно много всего интересного найти.
Как узнать, какая модель лучше? Как в машинном обучении обычно решают этот вопрос? У вас есть тестовая выборка, и вы по ней считаете какую-то метрику. Проблема в том, что здесь это не сработает, поскольку если ответ вашей модели не похож на ответ из тестовой выборки, это не говорит вообще ни о чем. Другими словами, даже на банальное «Привет» можно придумать десятки ответов, которые уместны, но у которых с ответом в тестовой выборке пара общих букв, не более того. Поэтому изначально все пытались использовать метрики из машинного перевода для решения этой задачи, которая сравнивает как-то ваш ответ с тем, что у вас было написано в тестовой выборке. Но все эти попытки провалились. Есть даже такая статья, где считается корреляция метрик, используемых в машинном переводе, с воспринимаемой людьми уместностью ответов. Корреляцию можно посчитать по тестовой выборке. И оказывается, что корреляции практически нет. Значит, этим способом лучше не пользоваться.
Каким же способом пользоваться тогда? Сейчас State of the art, если можно так сказать, подход — использовать краудсорсинг, то есть брать условный mechanical turk, и спрашивать у тёркеров: «Уместен ли данный ответ в данном контексте? Оцените по шкале от 0 до 5». Или: «Какой из данных ответов более уместен в этом контексте?». Если вы посмотрите литературу, в конечном итоге всем приходится сравнивать модели именно так.
А что лучше: порождающие или ранжирующие модели? Вот мы взяли некоторую модель, которую обучили сами на Твиттере sequence-to-sequence, взяли ранжирующую DSSM-подобную модель и дальше на нашей краудсорсинговой платформе попросили работников оценить уместность каждого ответа при данном контексте, поставить одну из трех меток: bad, neutral или good. Bad значит, что ответ синтаксически некорректен, абсолютно неуместен или, например, содержит обсценную лексику. Neutral значит, что он уместен, но является общим и неинтересным. А good — что это синтаксически корректный и уместный ответ. И еще мы попросили людей сгенерировать сколько-то ответов, чтобы у нас был некий baseline, к которому можно стремиться. Вот какие получились цифры.
Что интересно, у людей есть 10% плохих ответов. Почему так происходит? Оказывается, в большинстве случаев люди пытались пошутить, но работники на краудсорсинговой платформе их шутку не поняли. Там, по-моему, в пуле был вопрос: «Какой главный ответ на все?». Ответ был «42», и видимо, никто не понял, что это значит. Там 9 из 10 — bad.
Что тут можно видеть? Очевидно, до людей еще далеко. Ранжирующие модели работают лучше — хотя бы потому, что в пуле много более интересных ответов и такой моделью проще ответ сгенерировать. А модели sequence-to-sequence работают хуже, но не то чтобы сильно хуже. Зато, как вы помните, они могут генерировать ответ в любой ситуации, так что, возможно, моделями sequence-to-sequence надо пользоваться. Или нужно комбинировать sequence-to-sequence и ранжирующие модели в виде какого-то ансамбля.
В заключение повторю основные поинты своего доклада. В последние пару лет Neural Conversational Models — по-настоящему горячая область исследований в deep learning, одна из таких областей. Ей очень много занимаются, в том числе крупные компании: Facebook, Google. Там происходит много интересного. В принципе, какими-то ее плодами можно пользоваться уже сейчас, но не следует ожидать, что у вас сразу получится искусственный интеллект. Осталось очень-очень много проблем, которые предстоит решать. И если у вас есть значительный опыт работы с текстами на естественном языке, опыт работы с диалоговыми системами или с deep learning в этой области — скорее всего, мы найдем что вам предложить.
Если вам интересно, вы можете, например, мне написать. Вот мои контакты. Только, пожалуйста, не звоните ночью. У меня все. Спасибо.
Определяем номера с помощью CallKit
Когда в CRM 57000 контактов, людям совсем не хочется записывать их в айфон вручную. Надо найти решение поизящней, которое позволит не просто искать контакты в отдельном приложении, но и отображать имя человека при входящем звонке. Мы долго гуглили, а потом вспомнили про анонс фреймворка CallKit с WWDC. Информации по этой теме оказалось не так много: немногословная документация, статья на Хабре и ни одного пошагового руководства. Хочу восполнить этот пробел. На примере создания простого приложения покажу, как научить CallKit определять тысячи номеров.
Определяем один номер
Для начала попробуем определить один единственный номер.
Начнем с пустого проекта. Создадим Single View Application с именем TouchInApp.
Добавим extension для определения номеров. В меню Xcode выберите File > New > Target… В разделе Application Extension выберите Call Directory Extension, нажмите Next.
В поле Product Name введите TouchInCallExtension, нажмите Finish. В появившемся алерте нажмите Cancel.
Надеюсь, вы уже подготовили тестовый телефон, с которого будете звонить. Если нет, то сейчас самое время.
В Project navigator раскройте TouchInCallExtension и откройте CallDirectoryHandler.swift. Найдите функцию addIdentificationPhoneNumbers
. Там вы увидите массивы phoneNumbers
и labels
. Удалите номера из phoneNumbers
, впишите туда тестовый номер. Удалите содержимое массива labels
, впишите туда «Test number».
У вас получится что-то вроде этого:
private func addIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws {
let phoneNumbers: [CXCallDirectoryPhoneNumber] = [ 79214203692 ]
let labels = [ "Test number" ]
for (phoneNumber, label) in zip(phoneNumbers, labels) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
}
CXCallDirectoryPhoneNumber — просто
typealias
для Int64
. Номер должен быть в формате 7XXXXXXXXXX, то есть сначала код страны (country calling code), потом сам номер. Код России +7, поэтому в данном случае пишем 7.
Поставьте приложение на устройство и тут же закройте. В нем пока нечего делать. Зайдите в настройки телефона > Phone > Call Blocking & Identification. Найдите там приложение TouchInApp и позвольте ему определять и блокировать вызовы. Бывает, что приложение не сразу появляется в списке. В таком случае закройте настройки, откройте и закройте еще раз приложение и попробуйте снова.
Когда вы переводите Switch в состояние On, вызывается
addIdentificationPhoneNumbers
из ранее добавленного расширения и считывает оттуда контакты.
Позвоните с тестового номера на ваше устройство. Номер должен определиться.
Определяем тысячи номеров
Все это, конечно, здорово, но это всего лишь один номер. А в начале статьи речь шла о тысячах контактов. Очевидно, что мы не будем их все вручную переписывать в массивы
phoneNumbers
и labels
.
Итак, контакты мы должны добавлять в расширении. Из приложения мы это сделать не можем. Мы можем лишь вызвать функцию reloadExtension
, вызов которой приведет к вызову addIdentificationPhoneNumbers
. О ней я расскажу чуть позже.
Так или иначе, приложение будет иметь доступ к контактам. Либо они сразу будут с ним поставляться в определенном формате, либо мы будем получать их по запросу к API, либо как-то еще — неважно. Важно, что расширение должно каким-то образом получить эти контакты.
Давайте на секунду отвлечемся и проведем небольшую аналогию. Представьте, что у вас есть кот. Если есть, можете не представлять. Вы просыпаетесь утром и собираетесь его покормить. Как вы будете это делать? По всей вероятности, насыпете корм в миску. А уже из нее кот покушает.
А теперь представьте, что Call Directory Extension — это кот, а вы — приложение. И вы хотите накормить контактами Call Directory Extension. Что в нашем случае будет исполнять роль миски, которую мы должны наполнить контактами и из которой extension впоследствии будет их потреблять? К сожалению, вариантов у нас не так много. Мы не можем использовать Core Data или SQLite, так как очень сильно ограничены в ресурсах во время работы расширения.
Когда вы редактировали функцию
addIdentificationPhoneNumbers
, вы наверняка заметили комментарии. Там говорится о том, что «Numbers must be provided in numerically ascending order.». Сортировка выборки из базы слишком ресурсоемка для расширения. Поэтому решение, использующее БД, нам не подходит.
Все, что нам остается — использовать файл. Для простоты реализации будем использовать текстовый файл следующего формата:
Использование этого формата не ведет к оптимальной производительности. Но это позволит сделать акцент на основных моментах, вместо того, чтобы погружаться в работу с бинарными файлами.
Увы, мы не можем просто так взять и получить доступ к одному файлу как из приложения, так и из расширения. Однако, если воспользоваться App Groups, это становится возможным.
Делимся контактами с помощью App Groups
App Group позволяет приложению и расширению получать доступ к общим данным. Более подробно написано в документации Apple. Если вы никогда с этим не работали — не страшно, сейчас я расскажу, как это настроить.
В Project navigator кликните по вашему проекту. Выберите target приложения, перейдите на вкладку Capabilities, включите App Groups. Добавьте группу «group.ru.touchin.TouchInApp». Логика тут та же, что и с bundle identifier. Просто добавьте префикс group. У меня bundle identifier — «ru.touchin.TouchInApp», соответственно, группа — «group.ru.touchin.TouchInApp».
Перейдите к target'у расширения, перейдите на вкладку Capabilities, включите App Groups. Там должна появиться группа, которую вы вводили ранее. Поставьте на ней галочку.
Если мы используем опцию «Automatically manage signing», App Groups настраиваются достаточно легко. Как видите, я уложился в пару абзацев. Благодаря этому я могу не превращать статью о CallKit в статью об App Groups. Но если вы используете профайлы из аккаунта разработчика, то нужно в аккаунте добавить App Group и включить ее в App ID приложения и расширения.
Записываем контакты в файл
После включения App Group можем получить доступ к контейнеру, в котором будет храниться наш файл. Делается это следующим образом:
let container = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")
«group.ru.touchin.TouchInApp» — это наша App Group, которую мы только что добавили.
Назовем наш файл «contacts» и сформируем для него URL
:
guard let fileUrl = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")?
.appendingPathComponent("contacts") else { return }
Немного позже вы увидите полный код, сейчас я просто хочу пояснить некоторые моменты.
Теперь нужно записать в него номера и имена. Предполагается, что они у вас уже подготовлены в следующем виде:
let numbers = ["79214203692",
"79640982354",
"79982434663"]
let labels = ["Иванов Петр Петрович",
"Сергеев Иван Николаевич",
"Николаев Андрей Михайлович"]
Напомню, что номера должны быть с правильным кодом страны и отсортированы в порядке возрастания.
Теперь сформируем из контактов будущее содержимое файла:
var string = ""
for (number, label) in zip(numbers, labels) {
string += "\(number),\(label)\n"
}
Каждую пару номер-имя записываем в одну строку, разделяя запятой. Завершаем символом перевода строки.
Записываем все это дело в файл:
try? string.write(to: fileUrl, atomically: true, encoding: .utf8)
А теперь самое интересное. Нужно сообщить расширению, что миска наполнена и пора подкрепиться. Для этого вызовем следующую функцию:
CXCallDirectoryManager.sharedInstance.reloadExtension(
withIdentifier: "ru.touchin.TouchInApp.TouchInCallExtension")
Параметр функции — bundle identifier расширения.
Полный код:
@IBAction func addContacts(_ sender: Any) {
let numbers = ["79214203692",
"79640982354",
"79982434663"]
let labels = ["Иванов Петр Петрович",
"Сергеев Иван Николаевич",
"Николаев Андрей Михайлович"]
writeFileForCallDirectory(numbers: numbers, labels: labels)
}
private func writeFileForCallDirectory(numbers: [String], labels: [String]) {
guard let fileUrl = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")?
.appendingPathComponent("contacts") else { return }
var string = ""
for (number, label) in zip(numbers, labels) {
string += "\(number),\(label)\n"
}
try? string.write(to: fileUrl, atomically: true, encoding: .utf8)
CXCallDirectoryManager.sharedInstance.reloadExtension(
withIdentifier: "ru.touchin.TouchInApp.TouchInCallExtension")
}
Читаем контакты из файла
Но это еще не все. Мы не подготовили расширение к тому, чтобы оно могло этот файл прочесть. Попросим его читать файл по одной строчке, вычленять из строки номер и имя. Дальше поступаем так же, как с тестовым номером.
Увы, iOS не предоставляет возможность читать текстовые файлы построчно. Воспользуемся подходом, предложенным пользователем StackOverflow. Скопируйте к себе класс LineReader
вместе с расширением.
Вернемся к файлу CallDirectoryHandler.swift и внесем изменения. Сначала получим URL нашего файла. Делается это точно так же, как и в приложении. Затем инициализируем LineReader
путем к файлу. Читаем файл построчно и добавляем контакт за контактом.
Код обновленной функции addIdentificationPhoneNumbers
:
private func addIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws {
guard let fileUrl = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")?
.appendingPathComponent("contacts") else { return }
guard let reader = LineReader(path: fileUrl.path) else { return }
for line in reader {
autoreleasepool {
// удаляем перевод строки в конце
let line = line.trimmingCharacters(in: .whitespacesAndNewlines)
// отделяем номер от имени
var components = line.components(separatedBy: ",")
// приводим номер к Int64
guard let phone = Int64(components[0]) else { return }
let name = components[1]
context.addIdentificationEntry(withNextSequentialPhoneNumber: phone, label: name)
}
}
}
Функция должна использовать минимум ресурсов, поэтому заверните итерацию цикла в
autoreleasepool
. Это позволит освобождать временные объекты и использовать меньше памяти.
Все. Теперь после вызова функции addContacts
телефон будет способен определять номера из массива numbers
.
Окончательную версию проекта можете скачать в репозитории на GitHub.
Что дальше?
Это лишь один из вариантов решения задачи. Его можно улучшить, используя бинарный файл вместо текстового, как сделал 2GIS. Это позволит быстрей записывать и читать данные. Соответственно, придется продумать структуру файла, а также переписать функции для записи и чтения.
Когда у вас есть представление о том, как это работает, все в ваших руках.
Комментарии (0)
пятница, 21 июля 2017 г.
RubyMine 2017.2: Docker Compose, автокоррекции RuboCop в редакторе, улучшенный VCS
- Docker Compose
- Отладка приложений в Docker Compose
- Автокоррекции RuboCop
- “Хлебные крошки” для Ruby
- Улучшения поддержки JavaScript
- Новое в VCS
- Пользовательский интерфейс
- Другие улучшения
А теперь по порядку:
Docker Compose
В прошлом релизе мы анонсировали поддержку Docker. Однако пользователям значительно не хватало поддержки Docker Compose, которую мы успешно добавили в новую версию. Откройте Docker проект в RubyMine, и в настройках установите Docker Compose в качестве удаленной SDK (Preferences / Settings | Languages & Frameworks | Ruby SDK and Gems | New remote | Docker Compose). Теперь можно работать с приложениями в контейнерах, используя всю функциональность IDE от автодополнения кода до отладки. Подробнее об установке в блоге (англ.)
Отладка приложений в Docker Compose
Отладка заслуживает отдельного анонса, так как ее очень ждали. Отладчик RubyMine отныне можно использовать для приложений в контейнерах через Docker и Docker Compose. Для этого после настройки Docker/Compose в
Gemfile
нужно добавить гемы ruby-debug-ide
и debase
и установить их через команду docker-compose build
, запускаемую прямо из редактора Gemfile
вместо bundle install
. Об этом также подробнее в блоге.
Автокоррекции RuboCop
В предыдущей версии RubyMine научился анализировать код с помощью RuboCop. Мы расширили эту функциональность, добавив возможность быстро исправить код через RuboCop прямо в редакторе. Это довольно удобно:
Находим код, подсвеченный инспекцией rubocop как ошибка, нажимаем Alt+Enter, и, вуаля, ошибки исправлены во всем файле.
“Хлебные крошки” для Ruby
Мы добавили “хлебные крошки” для Ruby. Небольшая, но довольно полезная деталь. Показывает текущий контекст типа модулей, классов, методов, блоков, а в файлах RSpec — названий групп и примеров.
Кстати, вы также обнаружите “хлебные крошки” и в файлах JavaScript. А для HTML, XML и YAML мы обновили их дизайн.
Улучшения поддержки JavaScript
Среди улучшений для JavaScript стоит выделить следующие:
- Новый рефакторинг Move symbol позволяет аккуратно перенести классы, глобальные функции и переменные в модулях ES6 из одного файла JavaScript/TypeScript в другой.
- Автодополнение и навигация в JavaScript-коде теперь учитывают конфигурацию проекта (например, aliases), описанную в
webpack.config.js
. - Код внутри классов в JavaScript- и TypeScript-файлах теперь легко упорядочить с помощью нового действия Rearrange code и настроек Code Style — Arrangement и Blank lines.
- Если вы используете ESLint для проверки форматирования кода, RubyMine предложит импортировать некоторые правила из
.eslintrc
в настройки форматирования в IDE и будет применять их автоматически при форматировании. - Для селекторов Sass и SCSS, созданных через амперсанд (&), теперь работает автодополнение в файлах HTML и навигация к самому селектору.
Новое в VCS
В Git-логе появилось сразу два новых действия: Revert и Reword.
Revert — имплементация git revert
в IDE, позволяющая сделать Revert выбранных комитов.
Reword же просто позволяет переименовать любые коммиты, для которых еще не был сделан Push (не только последние).
Мы доработали настройки диалога Commit и перенесли их в отдельную вкладку Commit Dialog (Preferences / Settings | Version Control | Commit Dialog). Появилась возможность контролировать разделение заголовка и содержимого коммита пустой строкой, а также указывать максимальную длину строки.
Если вы пользуетесь IDE от JetBrains, то знаете о возможности временно откладывать текущие изменения, Shelve. Это полезно, когда, к примеру, нужно срочно переключиться на другую задачу, а текущие изменения положить “на полочку”, чтобы они не мешали выполнению срочной задачи.
В этом релизе во вкладке Shelf появился предварительный просмотр, Preview, отображающий внесенные изменения в выбранный файл, а также возможность сравнения “отложенной” версии с текущей.
Пользовательский интерфейс
В прошлый раз мы добавили предварительный просмотр в Find in Path. Теперь там же находится и Gutter — панель слева от редактора, содержащая номера строк, навигационные иконки, и индикаторы внесенных в файл изменений.
Кстати, мы также убрали дубликаты строк из результатов поиска. Теперь все повторяющиеся подходящие результаты выделяются сразу на одной строке.
Другие улучшения
- Улучшения в инструментах работы с базами данных.
- Анализ кода и автокоррекции RuboCop в scratch-файлах.
- Ряд исправлений для повышения производительности IDE.
- Возможность создавать патчи в буфер обмена.
Скачать новую версию можно со страницы What’s new. Для новых пользователей действует 30-дневный бесплатный пробный период. Для компаний мы также готовы предоставить расширенный пробный период (90 дней).
Делитесь вашими мыслями с нами в комментариях, докладывайте о багах в трекер и присоединяйтесь к нам в Slack!
Комментарии (0)
Отчет с Science Slam Digital 7 июля
7 июля Science Slam Digital собрал в нашем офисе более 600 зрителей, а число просмотров трансляции в соцсетях Одноклассники и ВКонтакте превысило 420 тысяч. Формат Science Slam зародился в Германии семь лет назад для популяризации научных достижений среди простых обывателей. Он состоит из серии научных лекций, которые читают молодые ученые. Доклад участника должен быть коротким (10 минут), доступным и информативным. Победителя слема определяют с помощью определения громкости аплодисментов зрителей шумометром.
Нам очень понравился этот формат, и мы захотели провести свой Science Slam, только цифровой, чтобы рассказать о технологиях просто и понятно. О том, что происходит внутри компании и чем занимаются сотрудники. Шесть разработчиков рассказали гостям и зрителям трансляции, что можно определить по почте, не открывая самих писем; как выяснить возраст человека в социальных сетях, даже если он не указан; какие тренды в медиапотреблении можно выделить уже сейчас и как они влияют на восприятие информации; как модифицировать социальную сеть, которой пользуются 100 миллионов человек, чтобы у них ничего не сломалось. Как это у нас получилось, вы можете посмотреть по нашим докладам.
Ян Романихин (руководитель команды машинного обучения Почты Mail.Ru): «Когда умрет электронная почта? Не сегодня»
Борис Ребров (руководитель разработки клиентской части в группе frontend-разработки медиапроектов): «Как технологии меняют медиа»
Вячеслав Шебанов (старший разработчик ВКонтакте): «Как менять сервис, которым пользуются 100 миллионов человек»
Виталий Худобахшов (разработчик отдела дата-майнинга Одноклассников): «Как узнать возраст человека в социальной сети, даже если он не указан»
Дмитрий Суконовалов (руководитель направления аналитики):«Как вернуть ушедшего пользователя»
Алексей Петров (директор по качеству в отделе тестирования Почты Mail.Ru): «Баги есть? А если найду?»
Записи видеотрансляций доступны во ВКонтакте и Одноклассниках. А здесь можно найти фотоотчет с мероприятия и видеоотчет. В ноябре у нас состоится новый Science Slam Digital, но на этот раз он будет межкорпоративным. Встретимся осенью!
Комментарии (0)
Жизнь разработчика на Кипре
И вот тут я, находясь на волшебном солнечном острове уже почти год, я поймал себя на мысли, что было бы здорово вернуться назад, в родной туманный и пасмурный Питер. Не будучи хоть сколько-нибудь профессиональным психиатром, вряд ли я смогу дать точную оценку своему когнитивному расстройству, однако попробую выразить собственные ощущения и полученный жизненный опыт в виде нескольких слабосвязанных между собой наблюдений и баек касаемо жизни и работы.
Итак, знакомьтесь, Кипр. Солнечный остров в составе Евросоюза, обладающий рекордным количеством чистейших пляжей на квадратный метр площади. Остров, где практически всегда лето, где буквально на каждой улице растут апельсиновые, гранатовые, абрикосовые и оливковые деревья. Остров, где местные жители беззаботны, открыты и дружелюбны. Где компания, в которой я тружусь, предоставляет бесплатное жилье в 10 минутах пешком от офиса плюс своевременно выплачивает денежку.
Что, спрашивается, может пойти не так? Да, в общем-то, всё что угодно.
Климат
Давайте начнем с климата. Сейчас в июне, когда я пишу эти строки, термометр показывает +31C. В июле будет примерно +40С. Сколько будет в августе — не знает никто, т.к. к этому времени ласковое солнышко выдавливает оставшихся в живых представителей относительно разумной фауны куда-нибудь в горы или вообще за пределы острова. В связи с этим могу поделиться тремя занимательными фактами о лете.
Факт первый. Иногда воздух на улице бывает настолько теплый, что при ветре бывает жарче, чем просто в штиль. Не верите? Встаньте под фен! А теперь экстраполируйте этот фен на территорию небольшого государства.
Факт второй. Кондиционер в автомобиле не всегда способен помочь. Он дует, старается, но охлажденный воздух не способен достаточно охладить кожу, на которую падают солнечные лучи сквозь тонированные стекла местных авто.
Факт третий. Летом забываешь, какой кран отвечает за горячую воду, а какой за холодную. Ибо из обоих течет водичка «комнатной» (за бортом +40С, напомню) температуры. Очень приятно и освежающе.
Наверное, я сгущаю краски и вот уж зимой-то точно все хорошо? Конечно, хорошо. Температура существенно опускается, но, тем не менее, остается положительной. И вот тут начинают играть роль представления киприотов о домах — с тонкими стенами, большими окнами и не менее большими щелями везде, где только можно. В результате героическое поддержание хотя бы +18С в небольшой квартире-студии обходится примерно в 100 евро/мес. Еще никогда в жизни мои кошки не были такими пушистыми! Ну а необходимость самостоятельно греть воду чтобы помыться так и вовсе выбивает скупую мужскую слезу в память о централизованном горячем водоснабжении.
Природа и города
В общем, фиг с ними — с особенностями местного быта. Но ведь на Кипре должно быть красиво? Красота — понятие субъективное, но я, пожалуй, соглашусь, что вид солнца, окунающегося в Средиземное море на закате, не лишен некоторого очарования…
Волшебство заканчивается, стоит только перевести взгляд с побережья вглубь территории острова. Страна оказывается удивительно уныла и уродлива. К середине мая вся зеленая травка подсыхает и обугливается, поэтому до октября все пейзажи выглядят немного по-марсиански. Деревья, конечно, продолжают стоять и плодоносить, но глобальной картины не улучшают, и, скорее, подчеркивают некоторую экзистенциальную безысходность всего сущего.
Но, разумеется, на Кипре есть парки. Любая огороженная территория этак 10x10 метров голой земли, из которой растет пара десятков деревьев уже признается парком, в котором, теоретически, можно попробовать провести время. Есть парки и покрупнее. Прямо на их асфальтированную территорию заезжают киприоты на авто, выходят, гуляют минут 5 и возвращаются в машину с чувством выполненного долга.
Любителям красивой и/или старинной архитектуры на Кипре тоже ничего не светит — всю власть в стране захватили стандартные 2-4-этажные белые коробки. Церкви, пожалуй, выделяются кирпичного цвета черепицей, но выглядят они настолько одинаково, что начинаешь верить, будто в их строительстве применялся единственный метод, а именно Ctrl+C, Ctrl+V.
Более опытные коллеги рассказывали, что все эти печали архитектурного свойства проистекают от глинистой почвы, на которой здания дольше лет сорока не стоят. Только вот построенная венецианцами крепостная стена 17-го века, отлично сохранившаяся до наших дней, легонько так и недвусмысленно намекает на другую причину. Но, пожалуй, отложим исследование анатомии произрастания рук у киприотов, и констатируем очевидный плюс всего вышесказанного.
С Кипра чертовски приятно улетать! Любая Тьмутаракань начинает блистать архитектурой, каждое пятиэтажное здание превращается в небоскреб, а от обилия зелени — да даже в центре Москвы — просто захватывает дух.
Аэропорт
Правда, возможность улететь с Кипра, как оказалось, надо еще выстрадать. Первый раз об этом мне намекнуло мироздание прошлой осенью, когда наивный я встал в очередь на регистрацию в аэропорту Ларнаки, намереваясь получить посадочный талон и сдать багаж. Очередь упорно не желала двигаться и спустя какое-то время объявили, что по причине технических неполадок работает только self-checkin регистрация через киоски и, соответственно, без багажа. Спустя еще час проблема была героически решена и я даже успел на рейс.
Уже в начале этого года умудренный опытом я заблаговременно зарегистрировался онлайн и решил обойтись только ручной кладью без багажа. Аэропорт вызов с честью принял, поэтому в этот раз информационные системы решили отказать всерьез и надолго. Не столь догадливым попутчикам посадочные талоны выписывались от руки на чистых листах бумаги, все сопутствующие операции сотрудники аэропорта, как могли, пытались проводить вручную… В общем, вылетели мы на 2.5 часа позже, как и множество других рейсов.
Что любопытно, когда я написал гневное письмо по этому поводу, в ответ меня попросили уточнить дату, когда это все произошло, дабы разобраться в сложившейся ситуации. На минуточку: в аэропорту случился глобальный коллапс в обслуживании отправлений, но руководство об этом ничего не знало.
Этот случай неплохо иллюстрирует взаимоотношения Кипра и ИТ, но для более полной картины расскажу еще одну историю. Про налоги.
Налоги
Каждое физлицо на Кипре обязано самостоятельно отчитываться по полученным доходам и уплаченным налогам, несмотря на то, что налоги за него платит работодатель. Отчитаться можно через интернет, для этого нужно получить логин/пароль в личный кабинет, подав заявление. Пару месяцев назад это заявление написал, отдал нашему бухгалтеру. Прошел месяц — ответа нет, поэтому начали разбираться.
Оказалось, что в тот день, когда бухгалтер отправлял мое заявление в налоговую, там не работал факс, вот они ничего и не получили. Ну в самом деле — а что такого? Для киприота не работающее что угодно, за исключением аппарата для взбивания нескафе, не представляет собой ничего страшного. Особенно какой-то там факс.
Итак, трудности преодолены, логины и пароли получены, заполняем декларацию. В общем и целом она не сильно отличается от российской 3-НДФЛ, где указываются полученные доходы и уплаченные с них налоги, в результате чего подбивается баланс (в идеале — нулевой).
Все просто, но киприоты не были бы киприотами, если бы не накосячили и здесь. Причем со вкусом: половина полей анкеты принимала вещественные числа, а другая половина — только целые, причем в совершенно бессистемно. Результат очевиден — дебит с кредитом не сошлись на пару десятков центов.
Решение проблемы пришло от бухгалтера (ВНИМАНИЕ: если вы связаны с российской бухгалтерией то, пожалуйста, не читайте следующее предложение, ибо оно способно нанести непоправимый вред вашему психическому здоровью). Несколькими легкими движениями она просто подогнала цифры, манипулируя вещественными полями декларации. Когда же я рассказал об этом одному коллеге, он просто посоветовал не париться, ибо «в лучшем случае до проверки твоей декларации дойдут лет через семь».
Ну и вишенка на торте. Как вы думаете, в чем контора, в которой я тружусь, считает зарплату своих ста сотрудников? Праааавильно, в Excel. Кстати, бухгалтер, если верить профилю Linkedin, получил MBA — здесь же, на Кипре.
Банки
В целом же, говоря о деньгах, нельзя обойти вниманием и другую сторону, а именно, банки. Заведения эти замечательны хотя бы тем, что работают с 8:00 до примерно 14:30. По будням, естественно.
Можно долго рассказывать об особенностях банковского обслуживания, или о том, что заказанная пластиковая карточка вместе с пин-кодом в обязательном порядке отправляется письмом на домашний адрес (во имя безопасности — в разных конвертах и с разницей в 1 день).
Во всей красе кипрские банки проявили себя, когда мне надо было обменять евро на фунты для поездки в Британию. 2000 евро сумма не то, чтобы астрономически большая, но просто так ее с собой таскать не хочется, поэтому для начала зашел в свое банковское отделение с вопросом, можно ли произвести обмен. «Конечно, можно! За свои евро получите вот столько вот фунтов,» — ответили мне.
Отлично, на следующее утро беру деньги, возвращаюсь в банк. «Нууу,» — говорит тот же сотрудник — «Это как-то слишком дофига денег, у нас столько нету, надо заказывать. Давайте мы закажем, фунты привезут, мы вам позвоним, а вы потом придете». Соглашаюсь, чего еще делать.
Зная, что рядом с моим отделением расположены еще 3 конкурирующие организации, захожу к ним. Во всех трех объясняют, что поскольку я не являюсь их клиентом, обмен наличности они осуществить вот прям никак не могут.
Наступает следующий день. Банк предсказуемо молчит. Не беда, иду туда ножками. «О, вы пришли 2000 евро обменять?» — кричит мне через весь зал ставший уже родным кассир — «А деньги только приехали, подходите.»
Полиция
Подобные увлекательные квесты поджидают счастливых обитателей острова буквально на каждом шагу. Понадобилось мне однажды получить кипрскую справку о несудимости (Criminal records certificate). Делаться справка должна в полиции, а вот где конкретно — вопрос. Разумеется, в полицию можно позвонить и всё узнать. Заходим на сайт кипрской полиции и лицезреем список полезных телефонов:
Окей, подойдем к задаче с другой стороны — спросим у более опытных в криминальных делах коллег. Более криминальные коллеги подсказывают адрес, по которому я даже успешно приезжаю. И тут, естественно, оказывается, что иностранцам эту справку делают не здесь, а в специально отведенном месте посредством специально обученных людей. На мой резонный вопрос, а где же, собственно, это специальное место расположено, доблестные сотрудники полиции минут 5 роются к компе, потом еще минут 5 куда-то звонят. В конечном итоге показывают место на карте. Вооот где-то здесь:
«Вооот где-то там» по факту оказался расположен целый квартал принадлежащих полиции зданий, без номеров домов, без конкретных адресов.
Такие топографические особенности характерны для всего острова, поэтому адрес любого объекта на Кипре выглядит примерно так: «едешь в Никосию по хайвею. Как только светофор увидите первый — смотрите направо — там будет здание с синими ставнями. но повернуть туда сразу не получится. надо чуть дальше проехать и развернуться. когда развернулись вам надо будет к этому зданию проехать повернув соответственно налево.»
Транспорт
Местные таксисты при всем при этом — так же совершенно прекрасные и чудесные люди, делящиеся на несколько категорий:
Те, которые не пользуются навигатором категорически. Сначала долго расспрашивают, куда везти и как ехать, затем встают на полпути и углубляются в бумажные карты острова. Для этих доморощенных Флинтов сие — вполне разумная стратегия, ибо деньги по счетчику исправно продолжают капать во время такой вот «технической остановки».
Те, у кого навигатор есть, но валяется он в бардачке в качестве крутого и модного аксессуара. Если бумажная карта по какой-то причине не помогла, киприот-таксист начинает тыкать в кнопочки навигатора, силясь разобраться, как же эта шайтан-штука устроена. Иногда даже получается. Но чаще всего — нет, т.к. отсутствие половины дорог и зданий на электронных картах — вполне себе норма. Равно как и наличие трех-четырех одноименных улиц с нужным названием, расположенных в различных частях города. Так что даже если в навигаторе удалось найти адрес, расслабляться рано — такси вполне может поехать совсем не туда, куда нужно.
Те, у кого есть навигатор с актуальными картами, и кто без лишних расспросов просто довезет из пункта А в пункт Б. Таких товарищей меньше всего, и с ними определенно стоит завязывать крепкую дружбу.
Вместо такси, в принципе, можно рискнуть воспользоваться общественным транспортом. Ходит он по расписанию с интервалом движения минут в 40. Само расписание можно посмотреть через официальное приложение. В нем же, теоретически, можно и проложить маршрут, но поскольку написано оно профессиональными индусами, пользоваться этой функцией совершенно невозможно — проще построить маршрут по 2ГИС, а затем уже в приложении свериться с расписанием.
Заканчивается движение автобусов чертовски поздно — примерно в пять-шесть часов вечера. При этом перевозят они за день совершенно феноменальное количество людей — зачастую единственными пассажирами автобусов на различных рейсах оказываемся только мы с супругой.
Подавляющая же масса местных предпочитает личный транспорт, что совсем не удивительно, учитывая все вышесказанное. Обратная сторона любви к железным коням выливается в то, что пешком киприоты не ходят вообще, и пешеходную инфраструктуру не проектируют. Поэтому редкие тротуары, как правило, засажены деревьями, и ходить по ним получается не особо. Примерно недели две спустя привыкаешь прогуливаться до работы и магазинов просто по проезжей части, благо машины в спальных районах ездят нечасто.
Такая вот проза жизни. И раз эта проза опубликована на техническом, по большей части, ресурсе, расскажу немного и о самой программистской работе.
Работа в IT
Контора, где я тружусь, позиционирует себя в качестве международной компании с офисами по всему миру. Головной офис и центр разработки здесь — на Кипре. Владелец заявляет, что эта локация выбрана исключительно по причине невероятной привлекательности острова во всех отношениях, хотя на мой циничный взгляд все упирается исключительно в бабло — расходы на сотрудников за счет лояльной налоговой системы крайне низки.
Впрочем, не могу исключать и того, что кому-то здесь в самом деле может нравиться. По крайней мере, в конторе добровольно работают и англичане, и немцы, и французы и выходцы из стран СНГ (из коих практически полностью и состоит отдел разработки).
В целом, есть ощущение, что контора только что перешла от небольшого «домашнего» предприятия к стадии зрелости, и до сих пор не осознала этого факта. Организационная структура отдела разработки, несмотря на растущее количество работников, представляет собой звезду с проект-менеджером в центре.
Причем сам ПМ звездит не по-детски: требует проводить через него абсолютно все технические решения и все коммуникации между разработчиками, при этом требуя самостоятельности и командной работы. И нащупать вот эту границу между «зачем ты пришел ко мне с этой ерундой» и «а почему ты не пришел — я бы сразу сказал, как сделать правильно» не представляется никакой возможности.
Поэтому оптимальная стратегия — самостоятельно разрабатывать максимально гибкие решения возникающих задач, молясь, чтобы ПМ никогда не задался вопросом «а как это все у тебя работает» и не начал давать очень умные советы. Воистину лучше просить прощения, чем разрешения.
При всем этом руководство прислушивается к опыту разработчиков и не опасается рисковать и пробовать новые подходы и технологии. Так, например, компания начала внедрение .NET Core в продукт и набивание шишек на этом пути практически сразу после его появления.
С другой стороны, подобный подход при отсутствии целостного видения руководством бизнес-процессов приводит к тому, что в фокусе оказываются только задачи, интересные проактивным разработчикам, и не все из этих задач доходят до финальной «вылизанной» реализации.
Так, например, для работы с пакетами используется полусамописный nuget-сервер, который чуть ли ежедневно нужно реанимировать руками, а запуск очередной сборки продукта триггерится исключительно вручную исключительно уполномоченными на то людьми. Такой вот continuous integration.
Выводы
Какие, в целом, выводы можно сделать из этой длинной и не совсем связной истории?
Во-первых, Кипр — очень и очень специфическое место. И рекомендовать его кому бы то ни было помимо влюблённых в море и негуманный климат мазохистов я бы не стал.
Во-вторых, жизнь здесь очень и очень сильно снижает все возможные планки ожиданий от окружающей реальности. С одной стороны, это несколько закаляет характер, и позволяет более спокойно и по-философски смотреть на возникающие жизненные неурядицы. С другой стороны, возрождает истинный смысл слов благодарности: если что-то происходит (почти) вовремя и (практически) согласно договоренности — то мое «спасибо» будет совершенно искренним проявлением радости оттого, что все получилось. Даже если это всего лишь доставка пиццы: и адрес нашли, и в заказе ничего не перепутали, и она горячая еще, а курьер и сдачу нашел!
В-третьих, все это оказалось очень интересным языковым опытом: где еще найти такое разнообразие акцентов и вариаций английского языка? А для тех, кому английского мало — всегда могут пойти на практически бесплатные курсы греческого. Хотя, с моей точки зрения, в отличие от английского, этот навык абсолютно бесполезен для любой практической цели.
Комментарии (0)
Как мы добавили RAM в серверы HPE
Качество и надежность DRAM сейчас важнее, чем когда-либо, в основном из-за растущего использования виртуализации серверов. Конечно, стоит отметить что модули RAM, по мнению многих IT-специалистов, являются одним из самых надёжных элементов сервера и выходят из строя последними. Виртуализация имеет много преимуществ, но она значительно увеличивает количество необходимой памяти в сервере для обеспечения оптимальной производительности максимального числа виртуальных машин. По данным HP за 5 лет с 2007 до 2011 средняя память, установленная на всех серверах HP ProLiant, выросла более чем на 500% — от 4 ГБ до более чем 30 ГБ на сервер.
В настоящее время как облачный провайдер мы используем blade-серверы HP ProLiant BL460c Gen8 на шасси HPE BLADESYSTEM C7000 ENCLOSURE. Полностью QuickSpecs тут, обозначим лишь спецификацию RAM.
Standard (Preconfigured Models)
64GB (8 x 8GB) DDR3 1600MHz RDIMMs at 1.5V
64GB (4 x 16GB) DDR3 1866MHz RDIMMs at 1.5V
32GB (4 x 8GB) DDR3 1600MHz RDIMMs at 1.5V
32GB (2 x 16GB) DDR3 1866MHz RDIMMs at 1.5V
32GB (4 x 8GB) DDR3 1333MHz RDIMMs at 1.35V
32GB (2 x 16GB) DDR3 1600MHz RDIMMs at 1.35V
16GB (2 x 8GB) DDR3 1866MHz RDIMMs at 1.5V
16GB (4 x 4GB) DDR3 1333MHz RDIMMs at 1.35V
16GB (2 x 8GB) DDR3 1600MHz RDIMMs at 1.35V
Maximum
(LRDIMM) 512GB (16 x 32GB) up to 1333MHz at 1.35V
(RDIMM) 256GB (16 x 16GB) up to 1866MHz at 1.5V
(RDIMM) 384GB (16 x 24GB) up to 1333MHz at 1.35V
(UDIMM) 128GB (16 x 8GB) up to 1600MHz at 1.5v
Если требуется помощь, чтобы разобраться в обозначениях типов памяти и их различиях, рекомендуем полезную статью на Хабре.
Отметим, что в случае с использованием в оборудовании для облака, то есть в системах, требующих масштабируемости и отказоустойчивости в ущерб дешевизне, используется регистровая память c функцией коррекции ошибок (ECC RDIMM).
Чтобы повысить производительность нашей облачной среды в соответствии с растущими потребностями клиентов, мы приняли решение увеличить количество оперативной памяти в серверах до максимально возможных значений. Возник закономерный вопрос какую именно память купить? Начать подбор логично было со знакомства с предложением производителя сервера, что и было сделано.
В поисках лаконичного ответа был найден White paper от HP, точнее даже два, один 2011 года, а второй 2017. Что мы выделили для себя:
- HP не является производителем модулей RAM.
- Когда вы видите бренд HP Qualified Memory, это означает, что DRAM прошла серьёзную проверку качества и тестирование с используем проприетарных диагностических инструментов и специализированных диагностических тестов, чтобы обеспечить высокий уровень производительности и доступности серверов HP ProLiant.
- До того, как модуль DRAM получит бренд HP Qualified Memory (наклейку), он проходит интенсивный четырехфазный процесс проверки качества. HP задолго до разработки продукта сообщает производителям модулей памяти (Tier 1 memory suppliers) требования к DRAM. Группа HP Memory Core получает модули DIMM непосредственно от поставщика-производителя. На диаграмме процесса HPMQ (HP Memory Qualification) даны краткие описания четырех фаз процесса проверки качества. На наш взгляд, важно обратить внимание на то, что не описано какая доля полученных на проверку модулей отсеивается.
В результате HPMQ не меняется расположение микросхем, кажется, дизайнер ошибся - Память проверяется так, чтобы она не становилась слишком горячей и по этой причине не забыла ваши данные.
HP-validated memory is tested so that it does not get too hot and forget your data. HP-validated memory is tested so that it does not cause the system to shut-down due to power overload.
- HP Qualified Memory обладает такой же полной гарантией, что и серверы ProLiant.
- Если будет установлено, что отказ сервера вызван RAM под другим брендом (не-hp) или в любое время будет обнаружено, что сторонняя часть наносит ущерб компонентам или системам HP, стоимость ремонта, включая поврежденные детали HP, оплату труда и проезда, не будет покрываться по стандартной гарантии HP. Но установка сторонней памяти не снимает сервера ProLiant с гарантии HP. Подобное заблуждение часто приводится пользователями IT-форумов как аргумент «за OEM» в ходе обсуждения подбора памяти для сервера.
Сколько стоит HPMQ и сервис HP для покупателя? Для наглядности и понимания об уровне цен приложены скриншоты с Яндекс.Маркета. Мы не предлагаем расценивать это как полноценное исследование рынка, особенно по причине минимальных цен на HP RAM от неизвестных нам продавцов, которые на 60% ниже средней рыночной.
Почему для сравнения мы выбрали оперативную память от Kingston?
- Каждый серверный модуль Kingston для конкретной системы должен пройти испытания на принудительный отказ. Эта уникальная процедура позволяет отбраковывать потенциально дефектные модули, не допуская их попадания в отгружаемые партии продукции. В том числе, присутствует проверка надёжности при разогреве. Полный список проверок качества здесь.
- Являясь долговременным членом Совета директоров JEDEC, Kingston Technology способствует созданию отраслевых стандартов для технологий изготовления памяти. Все модули памяти, выпускаемые компанией Kingston, отвечают требованиям JEDEC и основным техническим условиям, применяемым ведущими изготовителями полупроводниковых устройств.
- Техническая поддержка и тест-драйв. Есть официальное представительство в РФ (в отличии от Crucial, например), по запросу всегда есть необходимое количество плашек для отгрузки. Серверную память можно взять на тест-драйв и посмотреть, как она будет работать в реальных условиях на конкретном сервере.
- «Пожизненная» гарантия. Если модуль памяти откажет, его заменят по гарантии.
У Kingston есть большое количество различных вариантов модулей RAM. Подходящий можно подобрать на их сайте. Потенциальные проблемы с несовместимостью должны отсутствовать, так как по заявлениям Kingston, модули были протестированы на всех соответствующих вариантах серверов. В нашем случае, мы выбирали для HP BL460C G8, проблем в работе не возникло.
Что дала нам покупка модулей памяти Kingston? При значительном объёме закупки удалось серьёзно сократить бюджет на модернизацию. Нам не удалось выявить значимых отличий в проверке качества RAM от Kingston и HP. Не найдя оснований для того, чтобы оплатить почти в два раза больший счёт, мы выбрали модули Kingston. Это также позволило не перекладывать дополнительные издержки на потребителей наших услуг.
В этой статье мы не пытаемся обратить внимание на дороговизну комплектующих от HP. Более того, мы довольны использованием серверов HP ProLiant и HP BladeSystem. Для поддержки большого количества виртуальных машин на одном физическом сервере требуется больше памяти, больше подключений для хранилищ данных и больше сетевых подключений, поэтому, по нашему мнению, серверы HP, сертифицированные для VMware, являются одними из лучших платформ для виртуализации, так как они были построены «with virtualization in mind».
Сертификация VMware даёт нам возможность стабильного использования всеми опциями платформы виртуализации VMware, которые значительно повышают эффективность и надёжность работы всего облака:
- VMware VMotion;
- VMware DRS;
- VMware HA;
- и прочие.
Выбранное оборудование для облачного провайдера является основой для построения всей бизнес-системы, которая в конечном счёте должна обеспечить клиентов услугами с высоким уровнем доступности и качества (SLA от 99,95%) по конкурентной цене. Это является причиной поиска оптимальных решений.
Комментарии (0)