Архитектура Qwen2.5-Coder основана на архитектуре Qwen2.5. В таблице 1 представлены гиперпараметры архитектуры модели Qwen2.5-Coder для шести различных размеров: 0.5B, 1.5B, 3B, 7B, 14B и 32B параметров.
Хотя все модели имеют одинаковый размер голов (head size), они различаются по ряду других ключевых параметров. Например, модель 1.5B имеет увеличенный промежуточный размер (intermediate size), а модель 3B — больше слоёв. В целом, большинство параметров увеличиваются по мере роста размера модели. Для сравнения, модель 7B имеет скрытый размер (hidden size) 3 584, тогда как модель 32B — 5 120. Модель 7B использует 28 голов запросов (query heads) и 4 головы ключ-значение (key-value heads), в то время как модель 32B — 40 голов запросов и 8 голов ключ-значение, что отражает её повышенную ёмкость.
Промежуточный размер также масштабируется с увеличением модели: 18 944 для модели 7B и 27 648 для модели 32B.Кроме того, более мелкие модели используют связывание эмбеддингов (embedding tying), в то время как более крупные — нет. Обе модели имеют размер словаря 151 646 токенов и обучаются на 5,5 триллионах токенов.
Configuration | 0.5B | 1.5B | 3B | 7B | 14B | 32B |
---|---|---|---|---|---|---|
Hidden Size | 896 | 1,536 | 2048 | 3,584 | 5120 | 5120 |
# Layers | 24 | 28 | 36 | 28 | 48 | 64 |
# Query Heads | 14 | 12 | 16 | 28 | 40 | 40 |
# KV Heads | 2 | 2 | 2 | 4 | 8 | 8 |
Head Size | 128 | 128 | 128 | 128 | 128 | 128 |
Intermediate Size | 4,864 | 8,960 | 4,864 | 18,944 | 13824 | 27648 |
Embedding Tying | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
Vocabulary Size | 151,646 | 151,646 | 151,646 | 151,646 | 151,646 | 151,646 |
# Trained Tokens | 5.5T | 5.5T | 5.5T | 5.5T | 5.5T | 5.5T |
Таблица 1.Гиперпараметры архитектуры моделей
Embedding tying в больших языковых моделях (LLM) означает совместное использование параметров между слоем входных эмбеддингов (word embedding) и выходным слоем модели (часто называемым LM Head или классификатором). Идея заключается в том, что преобразование, используемое для представления входного токена в виде вектора (эмбеддинга), и преобразование, используемое для предсказания следующего токена из последнего скрытого состояния, разделяют одни и те же веса. Почему? А почему бы и нет! Попробовали - хуже не стало, а параметров в модели стало меньше.
В архитектуре языковых моделей обычно используются две матрицы:
Авторы работы отмечают, что к этим матрицам предъявляются схожие требования¹:
Кроме того, матрицы U и V имеют одинаковый размер. Это наводит на интуитивный вопрос: можно ли использовать одну и ту же матрицу весов для входного и выходного эмбеддингов?
Ответ: да. И вот пример как легко это сделать.
in_features, hidden_dim = 3, 4
U = nn.Embedding(in_features, hidden_dim)
V = nn.Linear(hidden_dim, in_features, bias=False)
# Связать матрицы можно простым присваиванием:
U.weight = V.weight
Преимущества использования одной матрицы для разных слоев очевидны:
Важно понимать, что даже если веса эмбеддингов и LM Head совпадают, промежуточные слои трансформера модифицируют представление, поэтому выход зависит не только от входа, но и от контекста и полученные на выходе эмбеддинги последнего слоя перед LM-head отличаются от входных эмбеддингов.
Qwen2.5-Coder наследует словарь от Qwen2.5, но добавляет несколько специальных токенов, которые помогают модели лучше понимать код. В Таблице 2 представлен обзор специальных токенов, добавленных в процессе обучения для более эффективного захвата различных форм данных кода. Эти токены выполняют специфические функции в конвейере обработки кода.
Например:
Другие токены включают:
Эти токены играют ключевую роль, помогая модели обучаться на разнообразных структурах кода и обрабатывать более длинные и сложные контексты как на уровне файлов, так и на уровне репозиториев во время предобучения.
Токен | Token ID | Описание |
---|---|---|
<|endoftext|> | 151643 | Конец текста/последовательности |
<|fim_prefix|> | 151659 | Префикс FIM |
<|fim_middle|> | 151660 | Средняя часть FIM |
<|fim_suffix|> | 151661 | Суффикс FIM |
<|fim_pad|> | 151662 | Заполнение FIM |
<|repo_name|> | 151663 | Название репозитория |
<|file_sep|> | 151664 | Разделитель файлов |
Таблица 2. Спец токены Qwen2.5-Coder
Масштабные, высококачественные и разнообразные данные являются основой для предобучения моделей. С этой целью был создан датасет Qwen2.5-Coder-Data. Этот датасет включает пять ключевых типов данных:
Исходный код
Собрали публичные репозитории с GitHub, созданные до февраля 2024 года, охватывающие 92 языка программирования. Аналогично StarCoder2 (Lozhkov et al., 2024) и DS-Coder (Guo et al., 2024a), применили ряд методов фильтрации на основе правил. Помимо исходного кода, также собрали данные из Pull Requests, коммитов, Jupyter Notebooks и датасетов Kaggle, все из которых прошли аналогичные процедуры очистки на основе правил.
Данные для связывания текста и кода (Text-Code Grounding Data)
Отобрали крупномасштабный и высококачественный датасет, сочетающий текст и код, из Common Crawl. В него вошли документация, руководства, блоги и другие материалы, связанные с кодом. Вместо традиционного многоэтапного метода отбора на основе URL разработали иерархический подход фильтрации "от грубого к точному".
Этот метод фильтрации имеет два ключевых преимущества:
Для очистки данных Text-Code Grounding разработали конвейер, где каждый уровень фильтрации строится на основе небольших моделей, таких как fastText. Хотя исследователи экспериментировали с более крупными моделями, они не показали значительных преимуществ. Вероятно, это связано с тем, что небольшие модели сосредоточены на поверхностных признаках, избегая излишней семантической сложности.
В Qwen2.5-Coder применили этот процесс итеративно. Как показано на Рисунке 1, каждая итерация улучшала показатели модели Qwen2.5-Coder-1.5B. После 4-этапной фильтрации средние результаты на HumanEval и MBPP выросли с 41.6% до 46.8% по сравнению с базовой моделью, что подтверждает ценность высококачественных данных Text-Code Grounding для генерации кода.
Синтетические данные (Synthetic Data)
Синтетические данные предлагают перспективный способ решения проблемы нехватки обучающих данных. Исследователи использовали CodeQwen1.5 — предшественника Qwen2.5-Coder — для генерации крупномасштабных синтетических датасетов. Чтобы снизить риск "галлюцинаций" (некорректных генераций), внедрили исполнитель (executor) для валидации, гарантируя, что сохраняется только исполняемый код.
Математические данные (Math Data)
Для улучшения математических способностей Qwen2.5-Coder интегрировали предобучающий корпус из Qwen2.5-Math в датасет Qwen2.5-Coder. Важно отметить, что включение математических данных не ухудшило производительность модели на задачах, связанных с кодом. Подробнее о процессе сбора и очистки можно узнать в техническом отчёте Qwen2.5-Math.
Текстовые данные (Text Data)
Аналогично математическим данным, включили высококачественные общие текстовые данные из предобучающего корпуса Qwen2.5, чтобы сохранить общие возможности Qwen2.5-Coder. Эти данные уже прошли строгий контроль качества на этапе очистки датасета Qwen2.5, поэтому дополнительная обработка не требовалась. Однако все фрагменты кода были удалены из общих текстовых данных, чтобы избежать дублирования с данными по коду и обеспечить независимость источников.
Баланс между кодом, математическими и текстовыми данными имеет решающее значение для создания базовой модели. Хотя исследовательское сообщество уже изучало этот вопрос, доказательств масштабируемости таких подходов на крупных датасетах недостаточно. Чтобы решить эту проблему, исследователи провели эмпирические эксперименты с разными пропорциями кода, математических и текстовых данных, разработав несколько тестов для быстрого определения оптимального сочетания.
В частности, как показано в Таблице 3, сравнили три различных соотношения для Qwen2.5-Coder-7B:
Интересно, что соотношение 7:2:1 (70% кода, 20% текста, 10% математики) показало лучшие результаты, чем другие варианты, превзойдя даже группы с большей долей кода. Возможное объяснение заключается в том, что математические и текстовые данные могут положительно влиять на производительность модели в задачах, связанных с кодом, но только при достижении определённого порога концентрации.
В будущих исследованиях планируется изучить более эффективные механизмы подбора пропорций и глубже исследовать причины этого явления. В итоге выбрали финальное соотношение: 70% кода, 20% текста и 10% математики. Финальный обучающий датасет включает 5,2 триллиона токенов.
Token Ratio (Код:Текст:Математика) | Coding | Math | General | Средний показатель | ||||
---|---|---|---|---|---|---|---|---|
Common | BCB | MATH | GSM8K | MMLU | CEval | HellaSwag | ||
100:0:0 | 49.8 | 40.3 | 10.3 | 23.8 | 42.8 | 35.9 | 58.3 | 31.3 |
85:15:5 | 43.3 | 36.2 | 26.1 | 52.5 | 56.8 | 57.1 | 70.0 | 48.9 |
70:20:10 | 48.3 | 38.3 | 33.2 | 64.5 | 62.9 | 64.0 | 73.5 | 55.0 |
Таблица 3. Эффективность обучения Qwen2.5-Coder при различных вариантах смешивания данных
Как показано на рисунке 2, использовали трёхэтапный подход к обучению модели Qwen2.5-Coder, который включает:
Предобучение на уровне файлов (File-Level Pretraining)
На этом этапе модель обучается на индивидуальных файлах с кодом. Максимальная длина обучающей последовательности составляет 8 192 токена, а объём высококачественных данных — 5,2 триллиона токенов. Основные задачи обучения:
Формат FIM на уровне файлов представлен на Рисунке 3.
Предобучение на уровне репозиториев (Repo-Level Pretraining)
После предобучения на уровне файлов перешли к предобучению на уровне репозиториев, целью которого является улучшение способности модели работать с длинными контекстами. На этом этапе:
На этом этапе используется большой объём высококачественных данных с длинным контекстом (≈ 300 млрд токенов). Метод FIM расширяется с уровня файлов до уровня репозиториев в соответствии с подходами, описанными в работе Lozhkov et al. (2024). Формат FIM на уровне репозиториев представлен на Рисунке 4.
Идентификация мультиязычного программного кода
Дообучили модель CodeBERT (Feng et al., 2020) для классификации документов по почти 100 языкам программирования. Исследователи сохранили инструкционные данные для основных языков программирования и случайным образом отбрасывали часть данных для редких языков. Если образец содержит очень мало кода или не содержит фрагментов кода вообще, он может быть классифицирован как "Без языка программирования". Поскольку слишком большое количество инструкционных образцов без фрагментов кода ухудшает производительность модели на задачах генерации кода (например, MultiPL-E, McEval, MdEval), удалили большинство таких образцов, чтобы сохранить способность модели генерировать код.
Синтез инструкций из GitHub
Для неразмеченных данных (фрагментов кода), массово представленных на многих сайтах (например, GitHub), пытались создать размеченный инструкционный датасет с помощью LLM. Конкретно:
На основе фрагментов кода на разных языках программирования создаём инструкционный датасет. Чтобы полностью раскрыть потенциал такого метода, также включаем открытые инструкционные датасеты (например, McEval-Instruct для массовой мультиязычной генерации и отладки кода) в начальный набор инструкций. В итоге объединяем инструкционные данные из фрагментов кода GitHub и открытых источников для контролируемого дообучения SFT.
Мультиязычные инструкционные данные по коду
Для преодоления разрыва между разными языками программирования предлагатся использовать мультиязычную мультиагентную коллаборативную платформу для синтеза мультиязычных инструкционных корпусов. Исследователи ввели агентов, специализирующихся на языках программирования, где каждый агент отвечает за определённый язык. Эти агенты инициализируются данными, специфичными для языка, полученными из ограниченных существующих мультиязычных инструкционных корпусов.
Процесс генерации мультиязычных данных можно разделить на следующие этапы:
Оценка качества инструкционных данных на основе чек-листа
Для полной оценки качества созданной пары инструкций вводится несколько критериев оценки для каждого образца:
После получения всех оценок (s₁, ..., sₙ) финальная оценка вычисляется как: s = w₁s₁ + ... + wₙsₙ, где (w₁, ..., wₙ) — заранее определённые веса.
Мультиязыковая пессочница для проверки кода
Для дополнительной проверки корректности синтаксиса кода используем статическую проверку кода для всех извлечённых фрагментов на разных языках программирования (например, Python, Java, C++) преобразуем фрагменты кода в дерево абстрактного синтаксиса (AST) и отфильтровываем те фрагменты, в которых обнаружены ошибки разбора.
Была создана мультиязыковая пессочница, поддерживающая статическую проверку кода для основных языков программирования. Кроме того, мультиязыковая пессочница — это комплексная платформа, предназначенная для валидации фрагментов кода на нескольких языках программирования. Она автоматизирует процесс генерации релевантных юнит-тестов на основе языково-специфичных образцов и оценивает, могут ли предоставленные фрагменты кода успешно пройти эти тесты. Особенно важно, что в пессочницу поступают только самодостаточные фрагменты кода (например, задачи по алгоритмам).
Мультиязыковая пессочница для проверки состоит из пяти основных частей:
Постепенное уточнение (Coarse-to-fine Fine-tuning)
На первом этапе синтезировали десятки миллионов низкокачественных, но разнообразных инструкционных образцов для дообучения базовой модели. На втором этапе используем миллионы высококачественных инструкционных образцов, чтобы улучшить производительность модели с помощью метода отбора (rejection sampling) и контролируемого дообучения (supervised fine-tuning). Для одного и того же запроса используем LLM для генерации нескольких кандидатов, а затем применяем LLM для оценки и выбора лучшего из них для контролируемого дообучения.
Смешанное обучение (Mixed Tuning)
Поскольку большинство инструкционных данных имеют короткую длину, формируем пары инструкций в формате FIM (Fill-in-the-Middle), чтобы сохранить способность базовой модели работать с длинными контекстами. Вдохновившись правилами синтаксиса языков программирования и привычками пользователей в реальных сценариях, была использована библиотека tree-sitter-languages для разбора фрагментов кода и извлечения базовых логических блоков, которые затем используются в качестве центрального кода для заполнения.
Например, дерево абстрактного синтаксиса (AST) представляет структуру кода на Python в виде дерева, где каждый узел отражает конструкцию исходного кода. Иерархическая природа дерева отражает синтаксическую вложенность конструкций в коде и включает различные элементы, такие как выражения, операторы и функции. Путем обхода и манипуляции с AST мы можем случайным образом извлекать узлы разных уровней и использовать контекст кода из того же файла, чтобы восстановить скрытые узлы.
В итоге мы оптимизируем инструкционную модель, используя большинство стандартных данных SFT и небольшую часть инструкционных образцов в формате FIM.
После получения модели SFT дополнительно выравниваем Qwen2.5-Coder с помощью автономной оптимизации предпочтений (DPO) (Rafailov et al., 2023). Учитывая, что обратная связь от человека требует значительных трудозатрат, используется мультиязыковая песочница для кода, чтобы предоставлять обратную связь на основе исполнения кода, а LLM — для оценки качества кода с точки зрения человека.
Для алгоритмических и самодостаточных фрагментов кода генерируются тестовые случаи, чтобы проверить корректность кода и использовать результаты исполнения в качестве обратной связи. Это касается таких языков, как Python, Java и других. Для более сложных фрагментов кода используется подход "LLM как судья" (Zheng et al., 2023), чтобы определить, какой фрагмент кода лучше.
Далее объединяются данные DPO для кода и общие данные для автономного обучения с использованием DPO.
Абстрактное синтаксическое дерево (AST, Abstract Syntax Tree) — это древовидная структура данных, представляющая синтаксическую структуру исходного кода программы без деталей, несущественных для семантики. Оно строится на этапе синтаксического анализа (парсинга) и используется компиляторами, интерпретаторами и инструментами для анализа и преобразования кода. В отличие от полного синтаксического дерева (parse tree), AST упрощает структуру, убирая вспомогательные элементы (например, скобки, точки с запятой), оставляя только логические конструкции языка.
Деконтаминация данных в Qwen Coder
Чтобы гарантировать, что Qwen2.5-Coder не показывает завышенные результаты из-за утечки данных тестового набора, провели деконтаминацию всех данных, включая как наборы для предварительного обучения, так и для дообучения. Исследователи удалили ключевые наборы данных, такие как HumanEval, MBPP, GSM8K и MATH. Фильтрация осуществлялась с использованием метода перекрытия 10-грамм: любые обучающие данные с перекрытием на уровне 10 слов с тестовыми данными были удалены.
Как строится AST?
Лексический анализ (токенизация)
Исходный код разбивается на токены (лексемы): ключевые слова, операторы, идентификаторы, литералы.
Пример: Для кода if (x > 0) { y = 1; } токены: if, (, x, >, 0, ), {, y, =, 1, ;, }.
Синтаксический анализ (парсинг)
Токены объединяются в структуру согласно грамматике языка. На этом этапе формируется синтаксическое дерево (parse tree), содержащее все детали грамматики (включая скобки, разделители).
Преобразование в AST
Парсер упрощает parse tree, удаляя «шум» (например, скобки вокруг условия if), и оставляет только семантически значимые узлы. Это и есть AST.
Примеры AST
Код:
2 + 3 * 4
Синтаксическое дерево (parse tree):
program
|
expression
|
binary_expression
/ | \
operand '+' operand
| |
2 binary_expression
/ | \
operand '*' operand
| |
3 4
AST:
BinaryExpression (+)
/ \
Literal(2) BinaryExpression (*)
/ \
Literal(3) Literal(4)
Пояснение:
Код:
function add(a, b) {
return a + b;
}
AST (упрощённо):
FunctionDeclaration (name: "add")
|
├── Parameters
│ ├── Identifier (name: "a")
│ └── Identifier (name: "b")
│
└── Body (BlockStatement)
└── ReturnStatement
└── BinaryExpression (+)
├── Identifier (name: "a")
└── Identifier (name: "b")
Пояснение:
Код:
if x > 0:
y = 1
else:
y = -1
AST:
IfStatement
|
├── Condition: BinaryExpression (>)
│ ├── Identifier (x)
│ └── Literal (0)
│
├── ThenBlock: Assignment (y = 1)
│ ├── Identifier (y)
│ └── Literal (1)
│
└── ElseBlock: Assignment (y = -1)
├── Identifier (y)
└── Literal (-1)
Пояснение:
Отличие AST от синтаксического дерева (Parse Tree)
Критерий | Parse Tree | AST |
---|---|---|
Детализация | Содержит все элементы грамматики (скобки, точки с запятой). | Удаляет «шум», оставляя только логические конструкции. |
Размер | Больше из-за технических узлов. | Компактнее. |
Цель | Проверка синтаксической корректности. | Анализ и преобразование кода. |
Пример для a + b | Узел expression → term → a + b. | Прямой узел BinaryExpression (+). |
AST — это «каркас» программы, отражающий её логику без синтаксического «мусора». Он позволяет инструментам понимать код так, как это делает компилятор, и является ключевым элементом современных систем обработки программ. Понимание AST необходимо для разработки компиляторов, линтеров, форматтеров и других инструментов, работающих с исходным кодом.
Artifact | Public link |
---|---|
Qwen2.5-Coder-0.5B | https://hf.co/Qwen/Qwen2.5-Coder-0.5B |
Qwen2.5-Coder-1.5B | https://hf.co/Qwen/Qwen2.5-Coder-1.5B |
Qwen2.5-Coder-3B | https://hf.co/Qwen/Qwen2.5-Coder-3B |
Qwen2.5-Coder-7B | https://hf.co/Qwen/Qwen2.5-Coder-7B |
Qwen2.5-Coder-14B | https://hf.co/Qwen/Qwen2.5-Coder-14B |
Qwen2.5-Coder-32B | https://hf.co/Qwen/Qwen2.5-Coder-32B |
CodeQwen1.5-7B | https://hf.co/Qwen/CodeQwen1.5-7B |
StarCoder2-3B | https://hf.co/bigcode/starcoder2-3b |
StarCoder2-7B | https://hf.co/bigcode/starcoder2-7b |
StarCoder2-15B | https://hf.co/bigcode/starcoder2-15b |
DS-Coder-1.3B-Base | https://hf.co/deepseek-ai/deepseek-coder-1.3b-base |
DS-Coder-6.7B-Base | https://hf.co/deepseek-ai/deepseek-coder-6.7b-base |
DS-Coder-33B-Base | https://hf.co/deepseek-ai/deepseek-coder-33b-base |
DS-Coder-V2-Lite-Base | https://hf.co/deepseek-ai/DeepSeek-Coder-V2-Lite-Base |
DS-Coder-V2-Base | https://hf.co/deepseek-ai/DeepSeek-Coder-V2-Base |
Детальные метрики моделей на бенчмарках написания кода и остальные технические детали вы можете посмотреть в оригинальной статье https://arxiv.org/html/2409.12186v3
Хорошего вам кода!