Qwen2.5-Coder技术报告阅读
这项工作推出了Qwen2.5-Coder,这是Qwen系列的最新成员。基于顶级开源LLM(大型语言模型)Qwen2.5,Qwen2.5-Coder通过对Qwen2.5-1.5B和Qwen2.5-7B在大规模数据集上的广泛预训练和后训练进行开发。为了保证预训练数据的质量,我们通过收集公共代码数据,从网络文本中提取高质量的代码相关内容,并使用高级分类器过滤低质量数据,汇编了一个数据集(20 Techn
Qwen2.5-Coder基于Qwen2.5架构,并在超过5.5万亿个令牌的大量语料库上继续进行预训练。
通过细致的数据清理、可扩展的合成数据生成和平衡的数据混合,Qwen2.5-Coder展现出卓越的代码生成能力,同时保持了通用性。
该模型已在各种与代码相关的任务上进行了评估,在包括代码生成、完成、推理和修复在内的10多个基准测试中达到最先进的(SOTA)性能,持续超越同等规模的更大模型。
预训练
数据工作
数据清理、可扩展的合成数据生成、平衡的数据混合
特殊标记
<|endoftext>| 表示文本或者序列的结束
<|fim_prefix|>, <|fim_middle|>, <|fim_suffix|> 用于实现Fill in the Middle(FIM)技术(预测代码缺失部分)
<|fim_pad|> 在FIM操作中用于 填充
<|repo_name|> 用于标识仓库名称
<|file_step|> 用作文件分隔符
Qwen2.5-Coder-Data的 dataset
5种关键数据类型: 源代码数据、文本-代码关联数据、合成数据、数学数据和文本数据
(1)源代码
2024.2之前的github公开仓库,包括92种编程语言。
类似 StarCoder2、 DS-Coder,我们应用了一系列基于规则的过滤方法。
我们还收集了Pull Requests、Commits、Jupyter笔记本和Kaggle数据集的数据,所有这些数据都经过了类似的基于规则的清理技术处理。
(2)文本代码关联数据 ⭐
我们从Common Crawl中编译了一个大规模、高质量的文本-代码混合数据集,其中包含了与代码相关的文档、教程、博客等。我们没有采用传统的基于URL的多阶段召回方法,而是开发了一种从粗到细的层次化过滤方法来处理原始数据。这种方法有两个主要优点:
1.这允许对每个过滤器的责任进行精确控制,确保每个维度的全面处理。
2.它自然地为数据集分配质量分数,最后阶段保留的数据质量更高,为质量驱动的数据混合提供有价值的见解。
我们为文本代码关联数据设计了一条清洗流程,其中每个过滤层级都是用较小的模型(如fastText)构建的。尽管我们也尝试了更大的模型,但它们并没有带来显著的好处。一个可能的解释是,较小的模型更专注于表面特征,避免了不必要的语义复杂性。
在Qwen2.5-Coder中,我们迭代地应用了这一过程。如图1所示,每次迭代都带来了提升。经过四阶段过滤,与基线相比,HumanEval和MBPP的平均分数从41.6%提高到46.8%,这证明了高质量文本代码关联数据对代码生成的价值。
(3)合成数据
合成数据为解决预期的训练数据稀缺问题提供了一种有希望的方法。
我们使用Qwen2.5-Coder的前体CodeQwen1.5生成大规模的合成数据集。为了减少在这个过程中出现虚幻生成的风险,我们引入了一个验证器,确保只保留可执行的代码。
(4)数学数据
为了提升Qwen2.5-Coder的数学能力,我们将Qwen2.5-Math的预训练语料库整合到了Qwen2.5-Coder的数据集中。
重要的是,引入数学数据并未对模型在代码任务上的性能产生负面影响。有关收集和清洗过程的更多详细信息,请参阅Qwen2.5-Math的技术报告。
(5)文本数据
与数学数据相似,我们从Qwen2.5模型的预训练语料库中包含了高质量的一般自然语言数据,以保留Qwen2.5-Coder的一般能力。为了防止与代码数据重叠,从一般文本数据中移除了所有代码段,确保不同数据源的独立性。
数据混合
平衡代码、数学和文本数据对于构建强大的基础模型至关重要。
虽然研究社区之前已经探索过这种平衡,但关于其在大型数据集上的可扩展性的证据有限。
为了解决这个问题,我们进行了不同的代码、数学和文本数据比例的实证实验,设计了多项实验以快速确定最佳组合。
具体来说,如表3所示,我们比较了三种不同的代码:文本比例——100:0:0、85:10:5和70:20:10。
有趣的是,我们发现7:2:1的比例甚至超过了那些代码比例更高的组,表现更优。
一个可能的解释是,数学和文本数据可能对代码性能有正向影响,但前提是要达到特定的浓度阈值。
在后续的工作中,我们计划探索更有效的比例机制,并研究这一现象的深层原因。
最终,我们选择了70%的代码、20%的文本和10%的数学的最终混合比例。
最终的训练数据集包含5.2万亿个令牌。
训练策略
3个步骤:file-level、 repo-level、instruction tuning
(1)file-level
文件级预训练侧重于从单个代码文件中学习。
在这个阶段,最大训练序列长度设置为8,192个令牌,涵盖了5.2TB的高质量数据。
训练目标包括下一个令牌预测和中间填空(FIM)(Bavarian等人,2022年)。
具体的FIM格式如图3所示。
(2)Repo-level
在文件级别的预训练之后,我们转向仓库级别的预训练,旨在增强模型处理长上下文的能力。
在这个阶段,上下文长度从8,192个令牌扩展到32,768个令牌,RoPE的基础频率从10,000调整到1,000,000。
为了进一步利用模型的外推潜力,我们应用了YARN机制(Peng et al., 2023),使模型能够处理长达131,072(132K)个令牌的序列。
在这个阶段,我们使用了大量的高质量、长代码数据(约300B)并将文件级别的FIM扩展到了仓库级别的FIM,遵循Lozhkov et al. (2024)中描述的方法,具体格式如图4所示。
post training
指令数据菜单
(1)多语言编程代码识别
我们对CodeBERT进行微调,以执行语言识别模型,将文档分类为近100种编程语言。我们保留主流编程语言的指令数据,并随机丢弃长尾语言的一部分指令数据。如果给定的样本包含很少的代码数据,甚至没有代码片段,那么该样本可能会被归类到“无编程语言”标签。我们删除了大部分没有代码片段的样本,以保持我们的指令模型的代码生成能力。
(2)从GitHub生成指令合成
对于许多网站(如GitHub)上大量存在的无监督数据(代码片段),我们尝试构建监督指令数据集。
具体来说,我们利用LLM从不超过1024个令牌的代码片段生成指令,然后使用代码LLM生成回复(Sun et al.,2024年)。
最后,我们使用LLM评估器过滤掉质量较低的样本,得到最终的配对。
面对不同编程语言的代码片段,我们从这些片段构建一个指令数据集。
为了增加指令数据集的多样性,我们首先从代码生成答案,然后使用LLM评估器过滤掉低质量的样本,得到最终的三元组。
同样,给出不同编程语言的代码片段,我们可以从这些片段构建一个具有通用代码的指令数据集。
为了充分释放我们所提方法的潜力,我们还在种子指令数据集中包含了开源指令数据集。
最后,我们将三个部分的指令数据集合并用于监督微调。
(3)多语言代码指导数据
为弥合不同编程语言之间的鸿沟,我们提出了一种多语言多代理协作框架,用于合成多语言指令语料库。我们引入了特定语言的代理,其中创建了一组专业代理,每个代理都专注于特定的编程语言。这些代理通过从有限的现有多语言指令语料库中派生的特定语言指令数据进行初始化。
多语言数据生成过程可以分为以下步骤:
(1) 语言特定智能代理:我们创建一组专业代理,每个代理都专注于特定的编程语言。这些代理使用从精选的代码片段中派生的特定语言指令数据进行初始化。
(2) 协作讨论协议:多个特定语言的代理参与结构化的对话,以制定新的指令和解决方案。这个过程可以增强现有语言的能力或为新型编程语言生成指令。
(3) 自适应记忆系统:每个代理维护一个动态的存储器库,用于存储其生成历史,以避免生成相似的样本。
(4) 跨语言讨论:我们实现了一种新颖的知识蒸馏技术,允许代理跨语言边界分享见解和模式,促进对编程概念的更全面理解。
(5) 协同评估指标:我们开发了一种新指标,用于量化模型中不同编程语言之间的知识共享和协同程度。
(6) 自适应指令生成:框架包括一种机制,可根据语言之间的知识空白动态生成新的指令。
(4)基于清单的指令数据评分 ⭐
为了全面评估生成的指令对的质量,我们为每个样本引入了多个评分点:(1) 问题与答案一致性:问题和答案是否一致且正确,适用于微调。 (2) 问题与答案相关性:问题和答案是否与计算机领域相关。 (3) 问题与答案难度:问题和答案是否具有足够的挑战性。 (4) 代码存在:问题或答案中是否提供了代码。 (5) 代码正确性:评估提供的代码是否没有语法错误和逻辑缺陷。 (6) 考虑适当的变量命名、代码缩进以及遵循最佳实践等因素。 (7) 代码清晰度:评估代码的清晰度和可理解性。评估其是否使用有意义的变量名、适当的注释,并遵循一致的编码风格。 (8) 代码注释:评估注释的存在及其在解释代码功能方面的有用性。 (9) 易于学习:确定其对目标是学习基本编码概念的学生的教育价值。收集所有分数(s1, …, sn)后,我们可以使用公式 s = w1s1 + … + wnsn 计算最终分数,其中(w1, …, wn)是一组预定义的权重。
(5)一个用于代码验证的多语言沙箱
为了进一步验证代码语法的正确性,我们对提取的编程语言代码片段(如Python、Java和C++)进行了代码静态检查。我们将代码片段解析为抽象语法树,并过滤掉那些在代码片段中解析出错的节点。我们创建了一个多语言沙箱,以支持主要编程语言的代码静态检查。此外,多语言沙箱是一个综合平台,旨在跨多个编程语言验证代码片段。它根据特定语言的样例自动生成相关的单元测试,并评估提供的代码片段是否能成功通过这些测试。特别是,只有自包含的(如算法问题)代码片段才会被输入到多语言验证沙箱中。多语言验证沙箱主要由五个部分组成:
a.多语言支持模块:• 实现对多种语言的支持(如Python、Java、C++、JavaScript)• 维护特定语言的解析和执行环境• 处理每种支持语言的语法和语义分析
b.样例代码库: • 为每种支持的语言存储各种各样的代码示例 • 根据语言、难度级别和编程概念对示例进行分类 • 由语言专家定期更新和精选
c.单元测试生成器: • 分析样例代码以识别主要功能和边界情况 • 根据预期行为自动生成单元测试 • 生成覆盖多种输入场景和预期输出的测试用例
d.代码执行引擎:• 提供安全执行代码片段的隔离环境 • 支持多个测试用例的并行执行 • 处理资源分配和超时机制
e.结果分析器:• 比较代码片段的输出与单元测试的预期结果 • 生成关于测试用例成功和失败的详细报告 • 根据失败的测试用例提供改进建议
后训练的训练策略
(1)从粗到细的微调
我们首先合成了数千万条低质量但多样的指令样本来微调基础模型。
在第二阶段,我们采用数百万条高质量的指令样本,通过拒绝采样和监督微调来提升指令模型的性能。
对于相同的查询,我们利用LLM生成多个候选结果,然后使用LLM对这些候选进行评分,以进行监督微调,选择最佳的一个。
(2)混合训练
由于大多数指令数据长度较短,我们使用FIM格式构建指令对,以保持基础模型的长上下文能力。
受到编程语言语法规则和实际场景中用户习惯的启发,我们利用tree-sitter-languages1解析代码片段并提取基本逻辑块作为中间代码进行填充。
例如,抽象语法树(AST)以树的形式表示Python代码的结构,其中树的每个节点都代表源代码中的一个构造。
树的层次结构反映了代码中构造的语法嵌套,包括表达式、语句和函数等各种元素。通过遍历和操作AST,我们可以随机提取多个级别的节点,并利用同一文件的代码上下文来揭示被遮掩的节点。
最后,我们使用大部分标准SFT数据和一小部分FIM指令样本来优化指令模型。
在基础模型上的代码相关任务评估
代码生成、完成、推理和修复在内的10多个基准测试
代码生成:HumanEval+、 MBPP+
HumanEval 包含164个手工编写的编程任务,每个任务都为模型提供了Python函数签名和docstring作为输入。而MBPP则由众包贡献者创建了974个编程问题。每个问题包括问题描述(即一个docstring)、函数签名和三个测试用例。
BigCodeBench-Complete是一个近期更具挑战性的代码生成基准,主要专注于评估工具使用和复杂指令遵循的能力。
MultiPL-E基准:主流的8种语言进行评估(python、c++、java、php、js、c#、bash、typescript)
代码补全:HumanEval Infilling【FIM训练策略 ⭐】
代码推理: CRUXEval
数学推理:MATH、GSM8K、MMLU-STEM、TheoremQA
通用自然语言:MMLU、MMLURedux、ARC-Challenge、TruthfulQA、WinoGrande、HellaSwag
长文本评估 ⭐:128K个token输入长度
在指令模型下的评估
六个核心领域:代码生成、代码推理、代码编辑、文本到SQL、数学推理和一般自然语言理解。
总结
这项工作推出了Qwen2.5-Coder,这是Qwen系列的最新成员。基于顶级开源LLM(大型语言模型)Qwen2.5,Qwen2.5-Coder通过对Qwen2.5-1.5B和Qwen2.5-7B在大规模数据集上的广泛预训练和后训练进行开发。为了保证预训练数据的质量,我们通过收集公共代码数据,从网络文本中提取高质量的代码相关内容,并使用高级分类器过滤低质量数据,汇编了一个数据集(20 Technical Report表示这一步骤)。此外,我们构建了一个精心设计的指令微调数据集,将基础代码LLM转化为强大的编码助手。未来,我们的研究将集中在探索在数据规模和模型规模两方面扩大代码LLM的影响。我们还会继续提升这些模型的推理能力,旨在突破代码LLM的潜力极限。
主要竞品
DeepSeek-Coder、CodeStral
如何衡量同一个工程的两个文件之间是否存在间接关系
如果建图的成本过高,以下几种方法可以作为替代方案,帮助你高效地判断代码文件之间的间接关系:
基于文本特征的相似度计算:通过 TF-IDF 或代码嵌入方法提取文件特征,并计算文件之间的相似度。
基于代码结构的分析:提取文件间的函数调用链、类继承等结构信息,进行关系分析。(PyLint、 ESLint)
基于元数据的简单分析:使用 Git 提交历史或文件修改记录分析文件之间的间接关系。(Git提交推断)
基于机器学习的特征工程:将文件特征作为输入,使用传统机器学习或深度学习方法进行分类任务。(类型、函数数量、代码行数、复杂度等)
更多推荐
所有评论(0)