предисловие
Платформа машинного обучения Yidian в основном обслуживает систему рекомендаций по информационным потокам, а обучение моделей части бизнеса осуществляется с помощью фреймворка глубокого обучения TensorFlow. На практике мы обнаружили, что нативный TensorFlow имеет некоторые недостатки и может быть оптимизирован для крупномасштабных сценариев с разреженными моделями, таких как
-
Синхронизация и онлайн-скорость крупномасштабных параметров разреженной модели медленны, и эффект обучения не может быть применен к службе логического вывода вовремя.
-
Разреженная функция Embedding занимает большой объем памяти в автономном прогнозировании, что затрудняет поддержку необходимых параметров крупномасштабной модели.
В ответ на вышеуказанные проблемы мы провели индивидуальную оптимизацию, основанную на нашем понимании платформы TensorFlow.
анализ проблемы
Параметры обучения модели сохраняются
Первоначальная схема сохранения параметров TensorFlow заключается в сохранении параметров в виде файлов и сохранении их на диске в процессе обучения для использования в прогнозировании. В рекомендательной системе имеется большое количество многомерных дискретных разреженных признаков, а параметры на этапе обучения модели имеют следующие характеристики:
Обновление рекомендательной модели является разреженным, то есть параметры, обученные и обновленные за определенный период времени, составляют лишь небольшую часть от общего числа. Для каждого параметра встраивания параметр имеет градиентные изменения только в части матрицы, и есть дублирующие данные ввода-вывода для обновления всего параметра встраивания. С учетом вышеперечисленных характеристик могут быть предприняты следующие меры по оптимизации.
При сохранении параметров модели, поскольку изменились только некоторые параметры, могут быть сохранены только параметры с изменениями градиента. По сравнению с параметрами, которые не изменились в предыдущем раунде итераций обучения, нет необходимости повторно сохранять данные при инкрементальном обновлении данных. При сохранении определенного параметра встраивания из-за разреженности матрицы параметров изменение градиента будет происходить только локально в матрице. Точно так же при сохранении инкрементной перезаписи неизмененную часть не нужно сохранять снова, необходимо обновить только часть с изменениями градиента. Благодаря постепенному сохранению данных их можно агрегировать и обрабатывать при изменении данных.В соответствии с характеристиками крупномасштабной разреженности параметров данные, подлежащие синхронизации, могут быть значительно сокращены, а уменьшение объема синхронизируемых данных может привести к повышению эффективности синхронизации данных. .
Кроме того, с точки зрения инженерной реализации для синхронизации данных используется пакетная обработка, а сокращение взаимодействия ввода-вывода может еще больше повысить эффективность синхронизации.
Модель синхронизации
Первоначальный онлайн-процесс параметров модели TensorFlow требует сохранения параметров в файл во время обучения, а затем передачи файла на сервер прогнозирования для загрузки и использования сервером прогнозирования. Этот процесс долгий, передача файлов медленная, а обновление несвоевременное.
Если параметры, сохраненные во время обучения модели, используются совместно со службой прогнозирования, затраты времени на процесс синхронизации параметров могут быть почти полностью сохранены. В частности, при передаче данных с большими параметрами повышение эффективности за счет экономии времени синхронизации становится еще больше.
Конечно, в целях изоляции данных этот метод также требует вспомогательной поддержки, такой как управление версиями параметров модели.
использование модели
В сценарии крупномасштабного вывода модели узким местом в памяти является большая проблема. Распределенный режим обучения распределяет параметры в кластере PS, что может снизить нагрузку на хранение данных параметров в памяти. Однако перед лицом высоких запросов qps сервису рассуждений не хватает хорошего решения для поддержки.Даже если будет принято то же распределенное решение, что и распределенное обучение, передача параметров между кластерами приведет к высокой нагрузке на пропускную способность сети и задержке ответа.
Если данные могут быть тонко загружены и требования к памяти, требуемой службой прогнозирования, могут быть уменьшены, проблема узких мест памяти может быть решена лучше. Из-за разреженного характера некоторых параметров можно использовать идею сегментирования данных для представления полных достоверных данных с несколькими локальными данными.
В то же время часть, фактически используемая разреженным параметром, часто является лишь частью всего параметра, поэтому можно считать, что он загружает и использует только локальные данные параметра, тем самым уменьшая требования к объему памяти для хранения.
Расширенная настройка
После приведенного выше анализа идеи проблемы и решения ясны, мы надеемся улучшить следующие функции TensorFlow.
На этапе обучения параметры могут быть сохранены в указанную систему хранения во времени.Здесь мы используем корпоративную базу данных KV Morpheus При сохранении параметров сохраняются только некоторые из измененных параметров, и каждый параметр внедрения сохраняет только некоторые из обновленных размеров. Загрузите только действительную часть данных параметра, который используется в процессе прогнозирования. Для достижения вышеуказанных целей TensorFlow необходимо настроить. Итак, как начать настройку, давайте изучим дизайн TensorFlow.
Познакомьтесь с графом вычислений TensorFlow и операциями OP
TensorFlow использует ориентированные графы «узлов» и «ребер» для описания математических вычислений. «Узел» обычно используется для обозначения прикладной математической операции, но он также может обозначать начало ввода данных/конец вывода (выталкивание) или конец чтения/записи постоянных переменных.
«Ребра» представляют отношения ввода/вывода между «узлами». Эти «ребра» данных могут передавать «динамически регулируемые» многомерные массивы данных или «тензоры». Интуитивное изображение тензоров, протекающих через граф, является причиной того, что этот инструмент называется «TensorFlow». Как только все тензоры на входе готовы, узлы назначаются различным вычислительным устройствам для выполнения операций асинхронно и параллельно.
Короче говоря, структура вычислительного графа определяется структурой алгоритма модели, а операции над данными — это операции (op). Когда структура модели определена, нашему усовершенствованию необходимо настроить операцию.
Итак, какие операции необходимо улучшить, давайте сначала объединим простой пример кода с простым графом вычислений, чтобы понять ключевой процесс процесса вычисления графа потока данных. Например
import tensorflow as tf
from tensorflow.core.protobuf import saver_pb2
from tensorflow.python.ops import variables
from numpy import random
a_matrix = random.random(size=(2,4))
b_matrix = random.random(size=(2,4))
print("a_matrix=", a_matrix)
print("b_matrix=", b_matrix)
a = tf.Variable(a_matrix, dtype=tf.float32, name="a")
b = tf.Variable(b_matrix, dtype=tf.float32, name="b")
res_a = tf.nn.embedding_lookup(a, [0, 0], name="lookup_a")
res_b = tf.nn.embedding_lookup(b, [1, 1], name="lookup_b")
y = tf.add(res_a, res_b)
saver = tf.train.Saver(variables._all_saveable_objects(), sharded=True, write_version=saver_pb2.SaverDef.V2, allow_empty=True)
meta_graph_def = saver.export_meta_graph(as_text=True, clear_devices=True, strip_default_attrs=True)
with open("./meta_graph_def.pbtxt", "w") as f:
f.write(str(meta_graph_def))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
## tensorboard --logdir=tensorboard/test0/
writer = tf.summary.FileWriter("./tensorboard/test0/", sess.graph)
print("res_a=", sess.run(res_a))
print("res_b=", sess.run(res_b))
print("y=", sess.run(y))
writer.close()
if __name__ == '__main__':
print("hello world")
Этот вычислительный граф реализует простой расчет, определяет две переменные, выполняет embedding_lookup соответственно, а затем суммирует результаты запроса. График сохраняется в коде, а также сохраняются данные, необходимые tensorboard, для дальнейшего анализа.
Используя tensorboard, чтобы открыть путь, по которому сохранен файл, мы можем увидеть интуитивно понятное представление этого вычислительного графа. Глядя на вычислительные узлы отдельно, вы можете наблюдать взаимосвязь ввода-вывода операционки.
Пользовательские OP и пользовательские вычислительные графики
Учитывая, что операции в официальной библиотеке не могут удовлетворить потребности пользователей, TensorFlow предоставляет возможность расширения пользовательских операций операций. Зная определение данных, сохранение, сбор и другие операции, вы можете вносить индивидуальные изменения. При написании пользовательской операции данные сохраняются в базе данных, которую мы указываем в разработанном формате при ее сохранении. Когда данные загружаются, они десериализуются из базы данных и собираются в необходимые параметры, минуя способ, которым платформа TensorFlow загружает параметры из файлов. Ниже перечислены некоторые из пользовательских функций проектирования операций для справки.
После настройки операции, как заменить исходную операцию в графе расчета модели? Когда TensorFlow сохранит модель, он сгенерирует файл meta_graph_def.Содержимое файла представляет собой формат, подобный json, который описывает структурные отношения вычислительного графа. Когда этот файл загружается, TensorFlow строит вычислительный граф на основе структурной информации, описанной в файле.
Вы можете изменить файл meta_graph_def, сохраненный моделью, заменить в нем операцию на нашу настроенную операцию и изменить отношение ввода и вывода каждого узла, чтобы изменить зависимости между операциями. Затем используйте модифицированный файл meta_graph_def, чтобы загрузить обратно вычислительный граф модели, что завершает модификацию исходной структуры вычислительного графа. Ниже приведен частичный пример изменения файла meta_graph_def.
node {
name: "a/read"
op: "InferenceIdentity" #### 原始op为原生的Identity,这里将其替换为了定制的op
input: "a"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
...
}
После рассмотрения планов написания пользовательских операций и изменения meta_graph_def подготовка к расширенной настройке в основном завершена.
Инкрементное обновление параметра
В процессе обучения обычно используется обратное распространение функции минимизации для обновления параметров для сходимости потерь. Внутри функция минимизации выполняется в два этапа.
На первом этапе функция calculate_gradients возвращает обновленные градиенты и параметры grads_and_vars.
На втором этапе функция apply_gradients применяет параметры и градиенты к существующим параметрам, чтобы завершить обновление параметров.
На первом шаге получается градиент и параметры, которые необходимо обновить на этот раз.Градиент параметра Embedding содержит измененный срез данных IndexedSlices в каждом тензоре. Что касается IndexedSlices, официальная документация частично описана следующим образом.
"АнIndexedSlices
is typically used to represent a subset of a larger tensor dense
of shape [LARGE0, D1, .. , DN]
where LARGE0 >> D0
.
The values in indices
являются индексами в первом измерении срезов, которые были извлечены из большего тензора».
Его можно рассматривать как идею, аналогичную SparseTensor, представляющую больший тензор с данными элемента и положением элемента. Описание состоит в том, чтобы разрезать тензор по первому измерению, тем самым представляя больший тензор формы [НАИБОЛЬШИЙ0, D1, .. , DN] как несколько меньших тензоров формы [D1, .. , DN] .
Согласно IndexedSlices, можно получить часть изменения тензора, соответствующую этому изменению градиента. При синхронизации параметров вы можете разделить слайсы в соответствии с IndexedSlices и сохранить необходимые данные слайса в базу данных. Например, тензор формы [m, n, l, k] может быть сохранен как данные m KV формы [n, l, k] в соответствии с количеством слоев, а ключ представляет собой уникальное имя комбинации tensor_name и порядковый номер m-измерения.
Разделив оптимизатор на два шага, во время каждого раунда обучающих итераций RecordIndicesOp записывает параметры обновления градиента и индексы их индексированных срезов градиента. Затем вы можете непрерывно отслеживать параметры обновления обучения и части, в которых параметры изменяются.
После нескольких раундов итераций обучения MorpheusWriteOp записывает данные сопоставления записей отслеживания в хранилище KV по индексам и синхронизирует измененные параметры за этот период с базой данных. После записи параметров ClearIndicesOp очищает записанные данные индекса, и начинается следующий цикл инкрементного обновления.
Как показано на рисунке ниже, начиная с m итераций обучения, разреженный параметр a обновляется локально, и регистратор записывает обновленные координаты первого измерения. После n итераций, в сочетании с текущим параметром a и измененными координатами первого измерения, записанными в записи, выньте часть, где параметр изменился, и запишите данные в базу данных KV. Затем очистите запись рекордера и начните следующий раунд итерации.
загрузка модели
В соответствии с предыдущим процессом мы получили настроенный график модели и данные параметров, которые были сохранены в базе данных KV. В службе логического вывода граф вычислений metra_graph загружается изначально. Загрузка раздела параметров обрабатывается вышеупомянутой пользовательской операцией, которая описана здесь.
После загрузки структуры вычислительного графа укажите фиктивный путь к файлу для загрузки данных параметров. Следует отметить, что мы модифицировали операцию RestoreV2, используемую для загрузки данных ранее, поэтому при загрузке здесь не загружаются никакие параметры, а поддельный файл загружается только для того, чтобы удовлетворить процесс фреймворка для инициализации.
Так как же загружаются данные параметров, сохраненные в базе данных KV? Граф TensorFlow будет автоматически запускать необходимые зависимости при его выполнении.Когда сеанс запускается в первый раз, данные будут проходить через пользовательскую операцию, которая инициирует операцию пользовательской части. Таким образом, после загрузки структуры диаграммы модели и файла поддельных параметров вам необходимо выполнить еще один вывод, чтобы заменить поддельный параметр. Процесс замены заключается в использовании InferenceIdentityOp для получения данных из БД и сборки нужного тензора по форме, замены предыдущего поддельного тензора и помещения его в модель. Существуют некоторые специальные оптимизации для загрузки данных встраивания, которые будут объяснены в следующем разделе.
Использование данных для служб логических выводов
Для служб прогнозирования обычно необходимо завершить загрузку моделей и данных перед выводом. Данные без встраивания, как правило, невелики и не представляют сложности для инженерной практики. Однако размер данных внедрения связан со словарем и измерениями.[vocabulary_size, Embedding_dimension] часто бывает очень большим.Для бизнеса рекомендаций параметры векторного слоя скудны. Тогда загрузка полного вектора встраивания очень расточительна с точки зрения памяти. Более того, когда словарный запас очень велик, крупномасштабные параметры трудно переносить и их невозможно загрузить в память одной машины, что создает большие инженерные проблемы.
Пользовательская обработка крупномасштабных параметров встраивания
При фактическом использовании параметр Embedding будет искать частичные данные из полного параметра в соответствии с embedding_lookup, и то же самое верно для embedding_sparse_lookup. Затем вместо загрузки всего параметра Embedding можно загрузить только некоторые части, которые необходимо использовать для полного параметра.
Схема сохранения параметров для изменений градиента представлена в главе «Пошаговое обновление параметров».Данные встраивания, как подмножество итеративных параметров в обучении, также сохраняются с той же схемой нарезки данных. Как упоминалось в главе «Пользовательская операция», в службе прогнозирования пользовательская операция InferenceGatherV2Op фактически используется для операции embedding_lookup. В следующем примере объясняется весь процесс встраивания данных от обучения до логического вывода.
Например, в обучении определите
a = tf.Variable(a_matrix, dtype=tf.float32, name="a")
использовать в выводе
b = tf.nn.embedding_lookup(a, [0, 1, 3], name="b")
Процесс потока параметра a и параметра b показан на следующем рисунке.
В процессе обучения параметр a (форма [4, 4]) претерпевает градиентное изменение.После обработки InferenceRecordOp данные сохраняются в базе данных KV после нарезки IndexedSlices в соответствии с изменением градиента. Часть параметра номер строки 0 и 2 сохраняется как два KV «a: 0» = [1, 0, 0, 0] и «a: 2» = [0, 1, 0, 0] соответственно данные , остальные строки с номерами 1 и 3 не сохраняются в базе данных.
Во время онлайн-инициализации модели параметр a не загружается, но загружаются фальшивые данные a' ( shape [1] ), чтобы обеспечить нормальную загрузку графа модели.
Когда параметр a должен быть embedded_lookup ( a, [0, 1, 3] ) во время выполнения вывода, InferenceGatherV2Op попытается загрузить ключи из базы данных KV как «a:0», «a:1», «a: 3" ” из 3 частей данных. На самом деле можно получить только данные "a:0", а затем согласно правилам сборки полученные данные собираются в данные b (форма [3,4]) для завершения операции embedding_lookup.
Видно, что весь параметр процесса а загружается не полностью в сервис предсказания, но это не влияет на использование параметра а в процессе вывода.
Оптимизация синхронизации данных
В приведенном выше проекте база данных KV используется в качестве сервера параметров в процессе прогнозирования, а в качестве базовой поддержки используется система KV компании Morpheus, что сокращает процесс синхронизации данных и повышает эффективность обучения данных в режиме онлайн. Чтобы уменьшить нагрузку на чтение и запись в базе данных KV, данные параметров, кроме Embedding, кэшируются локально в службе прогнозирования. Однако из-за наличия локального кеша клиентский запрос на извлечение данных будет заблокирован кешем, и последние параметры не могут быть получены вовремя. Поэтому в ответ на эту ситуацию клиент Morpheus использует метод подписки на очередь сообщений, чтобы обеспечить функцию мониторинга обновлений данных. Когда данные сервера Morpheus обновляются, клиент также воспринимает измененный ключ, а затем извлекает данные и обновляет их в локальном кеше, производя обновление параметров практически в реальном времени.
Резюме и перспективы
Вышеизложенное в основном основано на идее сегментирования данных и отложенной загрузки данных, попытке улучшить и настроить структуру TensorFlow с точки зрения синхронизации параметров и использования данных, а также реализовать обновление параметров обучения модели в квазиреальном времени. По сравнению с исходным обновлением параметров часовой модели показатели системы рекомендаций были значительно улучшены.
Однако с постоянным улучшением размерности данных, размера словаря, эффективности приложений и других требований к алгоритмам рекомендаций мы по-прежнему сталкиваемся со многими проблемами. Мы продолжим исследования и попытки, основанные на инженерных приложениях на основе TensorFlow. Если вы также заинтересованы в TensorFlow, вы можете оставить сообщение или дать предложения в области комментариев в конце статьи, большое спасибо.
Статья от команды платформы машинного обучения Yidianxing