ВведениеБлагодаря предобучению, большие языковые модели (LLM) приобретают широкие лингвистические способности и общий «кругозор» о мире. Но постобучение — не меВведениеБлагодаря предобучению, большие языковые модели (LLM) приобретают широкие лингвистические способности и общий «кругозор» о мире. Но постобучение — не ме

[Перевод] Как в Netflix масштабируют постобучение LLM

2026/02/20 14:16
15м. чтение

Введение

Благодаря предобучению, большие языковые модели (LLM) приобретают широкие лингвистические способности и общий «кругозор» о мире. Но постобучение — не менее важный этап, на котором они как раз усваивают конкретные намерения человека, ограничения, присущие предметной области, а также требования к надёжности, предъявляемые в продакшне. В Netflix исследовали, как именно LLM могут открыть новые грани рекомендаций, персонализации и поиска. Для этого в Netflix попробовали адаптировать универсальные обобщённые модели к имеющимся условиям так, чтобы они лучше отражали содержание каталога фильмов и нюансы истории взаимодействия пользователей с сайтом. В масштабе такой компании как Netflix постобучение быстро превращается как в инженерную проблему, так и в проблему моделирования: приходится выстраивать сложные конвейеры данных и оперировать ими, координировать распределённое состояние в масштабах многоузловых кластеров GPU и оркестровать потоки задач, в рамках которых перемежаются обучение и логический вывод. В этой статье описаны архитектура и инженерная философия применяемого в Netflix фреймворка постобучения, который был разработан командой по платформе ИИ с целью скрыть сложность инфраструктуры — так, чтобы исследователи и разработчики моделей могли сосредоточиться на внедрении инноваций, а не на латании распределённых систем.

Путь разработчика моделей к постобучению

Постобучение зачастую начинается обманчиво просто: отбираются проприетарные данные предметной области, загружается модель с открытыми весами от Hugging Face, после чего данные прогоняются через неё пакет за пакетом. Если нас интересует лишь эксперимент, то он делается всего в нескольких строках кода. Но в случае тонкой настройки масштабной большой языковой модели, рассчитанной на использование в продакшне, пропасть между «выполнением скрипта» и «надёжным постобучением» оказывается усеяна нетривиальными пограничными случаями.

https://miro.medium.com/v2/resize:fit:700/1*at60qZXd0j6SphkjzdmazQ.png
Рис. 1. Простые шаги для постобучения модели с открытыми весами.

Правильно готовим данные

На бумаге постобучение выглядит просто: сначала выбор токенизатора, затем предобработка датасета и, наконец, сооружение загрузчика данных. На практике всё начинает ломаться уже на этапе подготовки данных. Высококачественное постобучение — выполнение инструкций, многоходовый диалог, цепочка рассуждений — требует точно контролировать, какие именно токены складываются в функцию потерь. Шаблоны чатов Hugging Face позволяют сериализовать беседы, но не позволяют явно указать, на каком материале обучаться, а какой игнорировать. Конвейер должен применять маскировку явных потерь, так, чтобы оптимизировались только вспомогательные токены. В противном случае модель начнёт учиться на промптах и другом нецелевом тексте, что приведёт к снижению качества.

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

Настройка модели

Сначала кажется, что загрузить опенсорсный чекпойнт не составляет труда, но только до тех пор, пока модель не перестаёт умещаться на одном GPU. Когда это произойдёт, вам понадобится выработать стратегию шардирования (напр., FSDP, TP) и придётся загружать веса частями прямо в сеть устройств. Так мы уклонимся от необходимости когда-либо материализовать полную модель на одном устройстве.

Загрузив модель, мы должны обеспечить её обучаемость: выбрать полноценную тонкую настройку или LoRA, а затем применять такие оптимизации как разреженная активация слоёв (activation checkpointing), компиляция и правильные настройки точности (особенно тонкими они бывают при обучении с подкреплением (RL), где нужно соотносить развёртывание и точность политик). Работа с большими словарями (>128 тысяч токенов) привносит ещё одну проблему с памятью: логиты здесь [batch, seq_len, vocab] и могут вызывать пиковые всплески расхода памяти. Устраняют эту проблему обычно так: ещё до проекции отбрасывают игнорируемые токены, а также вычисляют логиты/потери пофрагментно по той оси, где пролегает размерность последовательностей.

Начало обучения

Даже когда готовы модели и данные, обучение в продакшне несводимо к простому «циклу for». Система должна поддерживать все аспекты от прямого/обратного распространения преобразования Фурье до потоков задач, регулируемых политиками обучения с подкреплением. Всё это перемежается с генерацией развёртывания, логическим выводом вознаграждения/референса и обновлениями политик.

В таком масштабе как в Netflix обучение решается как распределённое задание. В работе задействуется Ray для оркестрации потоков задач через акторы и открепления логики моделирования от аппаратного уровня. Для надёжности прогонов также требуется отслеживать эксперименты (в частности, метрики качества модели, такие, как её потери, и метрики эффективности — например, MFU, утилизация доступных на вычислителе FLOPS одним проходом модели). Отказоустойчивость достигается благодаря стандартизации чекпойнтов, обеспечивающих чистое возобновление работы после отказов.

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

Фреймворк постобучения, применяемый в Netflix

Фреймворк постобучения LLM в Netflix устроен так, что разработчики моделей могут превращать в надёжные и масштабируемые задания такие идеи, как, например, показаны на рис. 1. Фреймворк помогает преодолевать все вышеописанные инженерные препятствия, а также справляться с ограничениями, специфичными для экосистемы Netflix. Имеющиеся инструменты (напр., Tinker от Thinking Machines) хорошо работают со стандартными чатами и тонкой настройкой инструкций, но сама их структура может ограничивать более глубокие эксперименты. Напротив, внутрикорпоративные практические случаи, обрабатываемые в Netflix, часто требуют варьировать архитектуру (например, кастомизировать выходные проекционные головы под цели, характерные для конкретных задач), использовать расширенные или нестандартные словари, основанные на семантических ID или специальных токенах, и даже модели-трансформеры, предобученные с нуля на предметно-ориентированных последовательностях, не являющихся естественным языком. Для поддержки такого диапазона возможностей требуется фреймворк, в котором гибкость и расширяемость расцениваются как более важные, чем приспособленная к тонкой настройке, но фиксированная парадигма.

https://miro.medium.com/v2/resize:fit:700/1*z2IFH-4iIQ5qxARTur-wQA.png
Рис. 2. Библиотека для постобучения внутри стека Netflix

На рис. 2 показан стек в сквозном разрезе: от инфраструктуры до обученных моделей. В основе лежит Mako, внутренняя вычислительная платформа Netflix для машинного обучения, на которой предоставляются графические процессоры от AWS. Поверх Mako используются надёжные опенсорсные компоненты — PyTorch, Ray и vLLM — в основном работающие «из коробки». Фреймворк для постобучения находится над всем этим базисом и реализован как библиотека: в нём предоставляются утилиты для многоразового использования и стандартизированные рецепты обучения, такие как тонкая настройка с учителем (SFT), прямая оптимизация предпочтений (DPO), обучение с подкреплением (RL) и дистилляция знаний. Пользователи обычно оформляют задания в виде конфигурационных файлов, в которых выбирается рецепт, после чего подключаются компоненты, специфичные для конкретной задачи.

https://miro.medium.com/v2/resize:fit:700/1*RzZzr3wADPwism5CQP8mYw.png
Рис. 3. Основные компоненты, разработанные для фреймворка постобучения

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

  • Данные: абстракции датасетов для SFT, моделирование вознаграждений и обучение с подкреплением; высокопроизводительная потоковая передача данных для датасета из облака на диск, если эти данные не умещаются в локальном хранилище. Также речь идёт об асинхронной упаковке последовательностей, происходящей на лету, благодаря чему можно сочетать операции упаковки, сильно нагружающие ЦП, с выполнением на GPU. Так сокращается длительность работы процессора вхолостую.

  • Модель: поддержка современных архитектур (напр., Qwen3, Gemma3) и вариантов рода «смесь экспертов» (напр., Qwen3 MoE, GPT-OSS); LoRA, интегрированная в определения моделей; а также высокоуровневые шардинговые API, через которые разработчики могут распределять большие модели по сетям устройств, и для этого не пишется низкоуровневый распределённый код.

  • Вычислительные мощности: унифицированный интерфейс для подачи заданий, который масштабируется от одного узла до сотен GPU; MFU (утилизация доступных на вычислителе FLOPS), мониторинг, продолжающий давать точные данные на материале собственных архитектур и LoRA; а также исчерпывающая разметка чекпойнтами (охватывающая состояния обученных параметров, оптимизатор, загрузчик данных, смешиватель данных, т.д.) для обеспечения точного возобновления работы после всех перерывов.

  • Поток задач: поддержка парадигм обучения, не ограничиваясь SFT, в том числе, сложного онлайнового RL. В частности, рабочие SFT-нагрузки в стиле «одна программа, множественные данные» (SPMD) дополняются так, чтобы с ними можно было использовать онлайновое обучение с подкреплением с применением гибридной модели выполнения «один контроллер + SPMD». Об этом мы поговорим ниже.

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

Чему удалось научиться, выстроив фреймворк для постобучения

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

Масштабирование от SFT к RL

Исходно библиотека проектировалась с расчётом на тонкую настройку с учителем (SFT): относительно статичный поток данных, всего один учебный цикл, а также модель выполнения «одна программа множественные данные» (SPMD). Такой курс держали до 2025 года. Но с появлением DeepSeek-R1 и всё более обширного внедрения методов обучения с подкреплением на основе политик, в частности, GRPO, SFT превратилась в начальную ставку, а не в итоговую. Чтобы держаться фронтира, требовалась такая инфраструктура, которая обеспечивала бы переход от «оффлайнового цикла обучения» к «многоэтапной оркестрации, регулируемой на основе политик».

При SFT учебный сигнал плотный и безотлагательный: для каждой позиции, занимаемой токеном, мы рассчитываем логиты в масштабах всего словаря и методом обратного распространения передаём значение дифференцированных потерь. На уровне инфраструктуры это сильно напоминает предобучение и аккуратно соотносится с SPMD — каждый рабочий GPU выполняет одну и ту же функцию шаг за шагом над разными сегментами данных, и эта работа синхронизируется при помощи распределённых примитивов PyTorch.

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

В исходной SFT-архитектуре ведущий узел целенаправленно был исполнен «тонким»: он запускал N идентичных акторов Ray, в каждом из которых инкапсулирован полный цикл обучения. При этом масштабирование сводилось к запуску дополнительных работников, идентичных уже имеющимся. При применении обучения с подкреплением эта модель ломается. Обучение с подкреплением требует диверсифицировать систему на разные роли: акторы, отвечающие за политику, за развёртывание, за модель вознаграждения, за справочную модель, т.д. — и развить ведущий узел до активного контроллера, в котором запрограммирована плоскость управления. То есть, он отвечает за то, когда генерировать развёртывания, как объединять данные в пакеты и оценивать их, когда инициировать оптимизацию, а также как управлять ресурсами кластера на разных этапах работы.

https://miro.medium.com/v2/resize:fit:700/1*5lhd3rDmexD0KGoHy78CZQ.png
Рис. 4. Архитектурные различия для вариантов фреймворка с SFT и RL

На рис. 4 подчёркнут этот переход. Чтобы добавить поддержку RL, не переизобретая при этом с нуля распределённую оркестрацию, было решено интегрировать в проект инфраструктурное ядро из опенсорсной библиотеки Verl, позволяющей управлять жизненным циклом акторов в Ray и выделением ресурсов для GPU. Активно используя бэкенд Verl, удалось сосредоточиться на «моделировании поверхностного слоя» — а именно, абстракций Данные/Модель/Вычисления и внутренних оптимизаций — но при этом не смешивая с ними процесс оркестрации. В результате получилась гибридная система: унифицированный пользовательский интерфейс, через который разработчики могли легко переключаться между потоками задач SFT и RL, не усваивая при этом совершенно иную ментальную модель или набор API.

Работа с опорой на Hugging Face

Хаб Hugging Face превратился в де-факто стандартный канал распространения больших языковых моделей с открытыми весами, а также токенизаторов и конфигураций. Фреймворк создавался с расчётом на то, чтобы держать его как можно ближе к экосистеме, а не создавать изолированный внутренний стандарт. Даже в том случае, если бы для повышения скорости работы внутреннее представление модели было оптимизировано, то загружать и сохранять чекпойнты приходилось бы в стандартных форматах Hugging Face. В результате удаётся избегать «межведомственного» трения между командами и быстро подтягивать новые архитектуры, веса и токенизаторы.

Эта философия легла и в основу истории с токенизатором. С самого начала архитектура была напрямую связана с низкоуровневыми библиотеками токенизации (такими как SentencePiece, tiktoken) для обеспечения максимальной управляемости. На практике получился режим, отказы в котором обходились очень дорого: стал медленно нарастать перекос от обучения к обслуживанию. Стек логического вывода (vLLM) по умолчанию использует Hugging Face AutoTokenizer, и из-за крошечных отличий в нормализации, особенностях обработки токенов или шаблонизации чатов границы токенов могут прокладываться по-разному. Именно такое несоответствие позже проявляется как необъяснимое снижение качества. Это удалось исправить, сделав Hugging Face AutoTokenizer единым источником истины. Затем был построен тонкий уровень совместимости (BaseHFModelTokenizer), удовлетворяющий нужды постобучения. Он расставляет токены-заполнители, внедряет маркеры генерации для поддержки маскировки потерь и управляет специальными токенами / семантическими ID — в то же время гарантируя, что путь токенизации на байтовом уровне соответствует продакшну.

Подход к реализации моделей также изменился. Вместо обучения непосредственно на материале классов трансформерной модели, Netflix поддерживал собственные унифицированные и оптимизированные определения моделей с возможностью загрузки и сохранения чекпойнтов Hugging Face. Именно благодаря этому слою можно выполнять оптимизации на уровне всего фреймворка — такие, как FlexAttention, фрагментная перекрёстная энтропия с эффективным использованием памяти, согласованный учёт MFU и однородная расширяемость LoRA — без необходимости всё это заново отдельно реализовывать для каждого семейства моделей. Благодаря унифицированному соглашению об именовании модулей также стало возможно программно находить и переставлять компоненты (внимание, MLP, выходные головы) в масштабах разных архитектур, а также обеспечить общую плоскость для тензорного параллелизма и политик FSDP.

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

В имеющемся виде такая структура означает, что Netflix может обучать только такие архитектуры, которые явно поддерживаются. Это ограничение было введено намеренно, так как оно используется и в других высокопроизводительных системах, например, vLLM, SGLang и torchtitan. Для более широкого покрытия в качестве резервного бэкенда планируется добавить Hugging Face, в соответствии с паттернами совместимости, используемыми в этих проектах. Пользователи могут вести обучение непосредственно на нативных трансформерных моделях для быстрого исследования инновационных архитектур, при понимании, что некоторые оптимизации и возможности фреймворка в этом режиме могут быть неприменимы.

Обеспечение значимой пользы

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

Во-первых, эффективность обучения оптимизируется под реальные прикладные случаи. Вот репрезентативный пример: крайняя вариабельность длины последовательностей. При обучении в стиле FSDP последовательности с длинными хвостами порождают «отстающих»: те работники, которые быстрее справляются с работой, вынуждены дожидаться отстающих, простаивая в точках синхронизации, пока не придёт самый медленный пакет. В результате ресурсы используются не так эффективно, как могли бы. Помогают стандартные подходы с распределением по корзинам, но оффлайновая работа такого рода при рассматриваемом масштабе данных значительно увеличивает задержку при предобработке. Так усложнится поддержание свежести датасетов. Вместо этого в Netflix организована упаковка последовательностей на лету, при которой образцы потоком подаются из хранилища данных и динамически упаковываются в памяти. Упаковка выполняется асинхронно, при этом работа ЦП перемежается с вычислениями на GPU. На рис. 5 показан эффект от этого: на материале самого неравновесного датасета упаковка на лету улучшает фактическую пропускную способность при обработке токенов в 4,7 раза.

https://miro.medium.com/v2/resize:fit:700/1*Z2mdtXVFJsr764NihkguIA.png
Рис. 5. Пропускная способность при обучении на двух внутрикорпоративных датасетах Netflix с использованием GPU A100 и H200

Также при расширении словаря исследователи столкнулись с менее явными обрывами производительности. В рассматриваемых рабочих нагрузках часто приходилось добавлять собственные токены и семантические ID. Оказалось, что при определённых размерах словаря языковая модель предпочитает откатиться от сильно оптимизированного ядра cuBLAS на гораздо более медленный путь CUTLASS, из-за чего время выполнения этого слоя утраивается. Теперь фреймворк автоматически дополняет размеры словарей до кратных 64, благодаря чему компилятор выбирает быстрое ядро. При этом сохраняется высокая пропускная способность, а разработчикам не требуется знать эти низкоуровневые ограничения.

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

Заключение

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

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

Благодарности

Эта работа состоялась благодаря усилиям широкого опенсорсного ML-сообщества. Авторы особенно благодарят команды и отдельных контрибьюторов, разрабатывающих Torchtune, Torchtitan и Verl, чьи справочные реализации и использованные в них паттерны проектирования помогли принять информированные решения о многих аспектах фреймворков обучения — в особенности что касается масштабируемых рецептов обучения, распределённого выполнения и оркестрации на основе обучения с подкреплением. Также благодарности заслуживают команды коллег из Netflix AI for Member Systems, с которыми авторы совместно работали, получали от них обратную связь и помощь в развёртывании фреймворка постобучения. Наконец, авторы благодарят команду Training Platform, предоставившую надёжную инфраструктуру и операционный базис, благодаря которому стало возможным такое крупномасштабное постобучение.

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу [email protected] для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Быстрое чтение

Еще

Цена Conway Research (CONWAY) в сравнении с ценой Bitcoin (BTC) дает инвесторам четкое представление о том, как этот развивающийся мемкоин соотносится с крупнейшей криптовалютой. Поскольку BTC остается эталоном крипторынка, анализ динамики цен CONWAY vs BTC выявляет относительную силу, волатильность и возможности для трейдеров, ищущих прогнозы цены Conway Research и данные для сравнения цен Bitcoin.

Сравнение цены Conway Research (CONWAY) с ценой Ethereum (ETH) предлагает ценную перспективу для трейдеров и инвесторов. Поскольку ETH является второй по величине криптовалютой по рыночной капитализации и краеугольным камнем децентрализованных финансов, анализ его производительности по сравнению с CONWAY помогает выявить как конкурентные преимущества, так и потенциальные возможности роста.