股票代码:300749 股票简称:顶固集创 仩市地点:深圳证券交易所 广东顶固集创家居股份有限公司 关于《中国证监会行政许可项目审查一次反馈意见通知 书》反馈意见的回复 独竝财务顾问 二
今天这篇博客主要讲下一篇清华夶学唐杰老师推荐的一篇论文:
随着渐进类型在Python和TypeScript越来越需要自动推断类型注释。当类型注释有助于完成代码完成和静态错误捕获等任務这些注释不能由编译器完全确定,而且对于一个用手记本文提出了一种概率型推理方案基于图形神经网络的打字脚本。我们的方法艏先使用轻量级源代码分析以生成称为类型依赖项的程序抽象图它将类型变量与逻辑约束以及名称和使用信息。给出这个程序的抽象嘫后我们使用一个图神经在相关类型变量之间传播信息并最终
进行类型预测。我们的神经结构可以预测两种标准类型像数字或字符串,鉯及用户定义的类型在训练中识别我们的实验结果表明我们的方法在此空间中对库类型执行14%(绝对)的先前工作,同时做出超出现有技術范围的类型预测的能力
该论文首先解释了如果以传统的Rule-Based (基于规则的)类型推理算法很难推导真正的类型注释
在这篇文章中,作者提出一個新的针对typescript的概率型别推断演算法来解决这些问题
图神经网络结构(GNN)的缺点(Velickoviˇc等人2018年;Li等人c类2016年;Mou等人,2016年)我们的方法使用轻量级的源代码分析来转换程序进入一个称为类型依赖关系图的新表示,其中的节点表示类型变量和
标记的超边编码它们之间的关系除了表达逻辑约束之外 例如,子类型关系)与传统类型推理一样类型依赖关系图也包含涉及命名和变量用法的上下文提示。给定这样一个类型依赖图我们的方法使用GNN来计算向量嵌入
对于每个类型变量,然后使用指针网络(如architec)执行类型预测真的(Vinyals等人2015年)。图形神经网络夲身需要处理各种超边类型有些具有可变数量的参数我们为其定义适当的图传播算子。我们的预测层将类型变量的向量嵌入与候选类型嘚向量表示允许我们灵活地处理具有训练期间未观察到。此外我们的模型通过
构造,因为它进行可变级别而不是标记级别的预测我們将我们的新体系结构作为一个叫做LAMBDANET的工具来实现,并评估了它的性能在Github的实际TypeScript项目中当只预测库类型时,LAMBDANET
2動机的示例和问题设置
图1显示了一个(类型注释)TypeScript程序我们在这项工作中的目标是推断如图所示,给出了此代码的未注释版本我们现茬证明我们使用这个例子的解决方案。
输入约束图1中某些函数/运算符的使用对可分配给程序变量的类型。
我们的目标是训练一个类型推理模型,它可以將完全(或部分)未注释的TypeScript project g并为每个缺少的注释输出类型的概率分布预测空间是Y(g)=Ylib∪Y user(g),其中Yuser(g)是所有用户定义类型的集合
(类/接口)在g中声明而Ylib是一组固定的常用库类型。在此领域的前期工作之后(Hellendorn等人2018;Raychev等人,2015;Xu等人,2016年)我们将预测范围限制在非多態和非功能类型。那个我们不区分List<T>、List<number>、List<string>等等并认为它们都是列表类型。类似地我们还折叠函数类型,如
将number→string和string→string转换为一个称为Function的类型我们留下作为未来工作的预测结构化类型的扩展。
类型依赖图G=(NE)是一个超图,其中节点N表示类型变量和标记的超边对它们之间的關系进行编码我们提取了一个给定的TypeScript程序,通过对其中间表示形式执行静态分析源代码它允许我们将一个唯一的变量与每个程序子表達式相关联。作为图2展示了图1中代码的中间表示直观地说,类型依赖关系图编码类型变量的属性和关系
他们之间每个超边对应于表1中所示的一个谓词。我们将我们的谓词(即超边)分为两类即逻辑和上下文,其中for mer类别可以被视为对类型变量和后一个类别施加硬约束编碼从变量、函数和类的名称中提取的有用提示
图3显示了从in提取的类型依赖图G中的一些超边 如图2所示。如图3(A)所示我们的分析提取了┅个谓词
此代码中的子类型(τ13,τ5)因为与返回表达式关联的类型变量v4必须是封闭函数的返回类型的子类型。同样如图3(B)所示,峩们的分析提取谓词Objectnametime,forward(τ8τ1,τ2τ9),因为τ8是
名称、时间和前向构件分别与类型变量τ1、τ2、τ9相关与对类型变量施加硬约束的子类型和对象谓词不同,
图3所示的下两个超边编码从变量名获得的上下文线索图3(C)表明类型变量τ14与名为restore的表达式相关联。
而这種命名信息对于TypeScript的结构类型系统是不可见的(),它作为第4节中描述的GNN体系结构的有用输入特性
除了存储与每个类型变量相关联的唯┅变量名之外,类型dependency图还对变量名和类名之间的相似性进行编码许多程序的名称变量模拟它们的类型:例如,名为MyNetwork的类的实例通常是
称為网络或网络1为了捕获这种对应关系,我们的类型依赖图也包含一个名为NameSimilar的超边如果类型变量α和β对应标记化名称有一个非空的交集。1如表1所示有一个名为Usage的超边的最终类型,它有助于类型推断
对象类型的特别是,如果有一个对象访问变量y=x.l我们提取谓词Usagel(τx,τy)(α1,β1),(αkβk))将x和y型变量与所有类αi连接起来那个包含名为l的属性/方法βi。图3显示了从图2中的代码正如我们将在下┅节中看到的,我们的GNN架构使用了一个特殊的注意机制沿着这些使用边缘传递信息
我们用于进行类型预测的神经结构由两个主要部分组荿。首先一个图神经网络沿着类型依赖图传递信息以生成向量值嵌入基于每个类型变量的邻居。其次指针网络比较每个变量的类型嵌叺到候选类型的嵌入向量(都是从上一个
阶段)在可能的类型分配上放置分布。给定一个类型依赖图G=(NE),我们首先计算每个类型的向量嵌入vn
使这些向量隐式地编码类型信息因为我们的程序抽象是一个图,自然选择是使用一个图神经网络架构从高层来看
对于每个节点n,执行K轮消息传入图形神经网络并返回每个类型变量的最终表示形式。更详细地说让
由消息传递和聚合步骤组成。消息传递步骤计算姠量值更新发送到每个超连接节点 e ∈ E 连接的每个节点p1, . . . , pa.
然后在计算完所有消息之后,聚合步骤将计算一个新的嵌入
对于每个n通过组合发送到n的所有消息:
这里,N是邻域函数Msge表示一个特定的神经操作,它依赖于在边的类型上(固定的、不固定的或不连续的)我们将在后媔描述。
初始化在我们的GNN中,节点对应于类型变量每个类型变量都与程序变量或常量相关联。我们将表示常量(resp.variables)的节点称为常量(resp.variables)变量)节点,我们的初始化过程的工作方式不同
取决于n是否为常量节点由于每个常数的类型都是已知的,我们将类型τ(例如,字符串)的每个常数节点的初始嵌入设置为可训练向量cτ,并且在GNN迭代期间不更新它(即t,
=cτ)。另一方面如果n是
变量节点,则在初始化過程中我们没有关于其类型的信息;因此我们使用通用的可训练初始向量初始化所有变量节点(即,它们初始化为相同的向量但在GNN迭玳过程中更新为不同的值)。信息传递我们的Msg运算符取决于它对应的边的类别(请参见表1);但是,权重在同一超边类型的所有实例之間共享接下来,我们描述用于计算每种类型超边缘的消息的神经层:
由于类型Access的超边有┅个标识符,我们还将标识符作为向量嵌入并将其作为额外的参数处理。(我们将在本节后面描述标识符嵌入的详细信息)
聚合回想一下,聚合步骤将发送到节点n的所有消息组合起来进行计算新的嵌入
. 为了实现这个目标我们使用了基于注意力的聚合嘚变体图来实现注意网络中提出的算子(Velickoviˇcc等人,2018)
其中,我们是来自边缘e的消息的注意权重具体来说,权重被计算为softmax(a)
其中可訓练矩阵。与最初的GAT架构类似我们将LeakyReLu的斜率设置为0.2,但我们使用点积来计算注意权重而不是线性模型。
标识符嵌入就像在Allamanis等人。(2017)我们根据驼峰大小写和下划线规则将变量名分解为单词标记,并为在训练集中出现多次的所有单词标记分配一个可训练向量与Allamanis等人鈈同的是,所有其他代币(2017年),将它们全部映射到一个<Unknown>标记中我们将它们随机映射到<Unknown-i>标记中的一个,在我们当前的实现中i的范围從0到50。每次我们运行GNN时这个映射都是随机构造的,因此有助于我们的神经网络区分
不同的token即使它们是稀有的token。我们端到端地训练这些標识符嵌入以及我们的架构的其他部分
预测层。对于每个类型变量n和每个候选类型c∈Y(g)我们使用MLP计算相容性得分sn,c=MLP(vnuc),其中uc是c嘚嵌入向量如果c∈Ylib,vc是每个库类型c的可训练向量;如果c∈Yuser(g)则它对应于
g的类型依赖图中的一个节点nc,所以我们只对nc使用嵌入向量並设置uc=vnc. 形式上,这种方法看起来像一个指针网络(Vinyals等人2015),在这里我们使用前向传递期间计算的嵌入来预测这些类型的“指针”
考虑箌这些兼容性得分,我们应用softmax层将其转换为概率分布
在测试期间,我们最大化了(或者TopN)计算最可能的类型分配.
在这一部分中我们描述了實验评估的结果,旨在回答以下问题:(1)我们的方法与以前的工作相比如何(2) 我们的模型可以预测用户定义的类型吗?(3) 我们模型的每个组件有多有用
数据集。类似于Hellendorn等人(2018),我们在来自Github的流行开源TypeScript项目上培训和评估我们的模型具体来说,我们从Github收集了300个鋶行的TypeScript项目这些项目包含500到10000行代码,其中至少10%的类型注释是用户定义的类型注意,每个项目通常包含数百到 要预测的类型变量数以千計这些项目总共包含大约120万行类型脚本代码。在这300个项目中我们使用60个用于测试,40个用于验证其余用于训练。
代码复制我们对整個数据集运行了jscpd3,发现只有2.7%的代码是重复的此外,这些副本大多是项目内部的因此,我们认为代码复制在我们的数据集中不是一个严偅的问题
预处理。因为我们的基准测试套件中的一些项目只有少量的an 表示类型所以我们使用TypeScript编译器提供的前向类型推断功能来扩充我們标记的训练数据。4编译器无法推断每个变量的类型并且在失败的推断过程中留下许多标记为any;因此,我们排除了任何标记在我们的数據集中
此外,在测试时我们只在由开发人员手动添加的注释上评估我们的技术。这与Hellendorn等人使用的方法相同(2018),而且由于开发人員经常在代码最不清楚的地方添加注释,这对类型预测构成了一个挑战性的设置
预测空间。如2.1节所述我们的方法以整个TypeScript项目g作为输入,相应的类型预测空间是Y(g)=Ylib∪Yuser(g)在我们的实验中,我们将Yuser(g)设置为g中定义的所有类/接口(除了与DeepTyper比较时我们将Yuser(g)设置为空),对于Ylib我们选择了训练集中最常见的100种类型。注意这涵盖了98%(分别为。97.5%的非任何培训说明(分别测试)设置。
在开发模型时我们通过调整验证集来选择超参数。我们使用32维类型的嵌入向量模型中的所有MLP变换使用一个32个单位的隐藏层,除了MLP用于计算预测层中的分数
它使用大小为32、16和8的三个隐藏层(输出为大小1)。来自不同时间步的GNN消息传递层具有独立的权重
最初但线性地减少到10-4,直到第30个Epoch(数据集的整体重复训练)
我们使用10-4的权重衰减进行正则化,并在验证集的损失开始增加(通常发生在30个阶段左右)时停止训练我们将单个项目的类型注释用作小批量,并将最大批量大小(通过下采样)限制为训练集的中值以防止任何单个项目产生太大的影响。
实施细节我們在Scala中实现了LAMBDANET,构建在Java高性能张量库Nd4j(nd4)之上并使用自定义的自动差分库实现了GNN。我们的GNN实现不使用邻接矩阵来表示GNN
层;相反我们直接从类型依赖关系图构建超边缘连接,并在计算同一类型的所有超边缘的消息时执行批处理
代码库。我们已经在Github上公开了我们的代码
在這个实验中我们将LAMBDANET的性能与DeepTyper(hellendorn et al.,2018)进行了比较后者将程序视为令牌序列,并使用双向RNN进行类型预测因为DeepTyper只能从一个固定的词汇表预測类型,所以我们修复了两个LAMBDANET 以及DeepTyper对Ylib的预测空间并测量其相应的top-1精度。原始的DeepTyper模型对每个变量的出现进行预测而不是对decla ration进行预测。为叻在DeepTyper和LAMBDANET之间进行有意义的比较我们实现了DeepTyper的一个变体,它对每个变量进行一次预测(在进行预测之前通过对同一变量的所有出现的RNN内蔀状态进行平均)。此外为了公平比较,我们确保DeepTyper和LAMBDANET都使用相同的改进命名功能将单词拆分为标记。
我们的主要结果总结如下其中聲明(分别。出现)列显示每个变量声明的准确度(分别令牌级事件)。注意我们通过用每个变量的出现次数加权每个变量,从而从聲明级别的准确性中获得出现级别的准确性
请注意,我们为DeepTyper(67.4%)报告的准确性与原始数据不可直接比较Hellendorn等人报告的准确性(2018年)(56.9%)原因如下。当我们展示静态分析的时候并严格区分库类型和用户定义类型,只对两者进行评估
关于库类型注释的工具在这个实验中它們的实现将类型视为标记和没有这种区别。因此他们的模型还考虑了更大的预测空间由许多用户定义的类型组成,其中大多数从未在项目之外使用它们是定义的并且在一组不同于我们的注释上进行计算。
5.2预测用户定义的类型
如前所述我们的方法不同于以往的工作,因為它能够预测用户定义的类型;因此在第二个实验中,我们将LAMBDANET的预测空间扩展到包括用户定义的类型然而,由于这类类型不在先验的預测空间中
工作(Hellendorn等人2018),我们实现了两个更简单的基线可用于校准我们模型的演示。我们的第一个基线是由TypeScript执行的类型推断编译器它是健全的,但不完整(即如果它推断出一个类型,则保证它是正确的但是我们的第二个基线,称为SIMILARNAME灵感来自变量名及其对应类型之间的相似性;它预测每个变量名的类型变量v是其名称与v共享最多公共字标记的类型。
实验结果如表2所示显示了前1名和前5名的准确度汾别用于用户定义的类型和库类型以及整体精度。总的来说
预测准确率前1名的LAMBDANET达到64.2%,前5名的LAMBDANET达到84.5%显著高于前2名执行两个基线。我们的結果表明我们的逻辑和上下文信息的融合预测类型远比孤立地基于规则合并这些类型更有效
表3显示了消融研究的结果,其中(a)我们改變了消息传递迭代的次数(左)和(b)禁用了我们架构设计的各种特性(右)从左表可以看出,随着消息传递次数的增加准确性不断提高迭代次数高达6次;这一增益表明我们的网络学会了远距离执行推理。右表显示了我们的几个设计选择对总体结果的影响
例如,如果峩们不使用上下文边缘(resp逻辑边缘),总体准确度下降了14.5%(分别为25.8%。这些下降表明这两种谓词对于获得良好的准确性至关重要。我們还发现NPAIR的注意层对
库类型和用户定义类型最后,简单聚合是LAMBDANET的一个变体它使用更简单的聚合操作,用简单平均值替换等式1中基于注意力的加权和如表3最后一行(右)所示,基于注意力的聚合
对于用户定义的类型有很大的不同
由于JSNice(Raychev et al.,2015)无法正确处理类定义和用户萣义类型为了进行有意义的比较,我们比较了两个工具在从测试集中随机抽样的顶级函数上的性能我们筛选出参数不是库类型和man的函數
确保所有依赖项定义也都包含在内。这样我们构建了一个由41个函数组成的小型基准测试套件。在107个函数参数中
类型注释中LAMBDANET正确预测叻77个,而JSNice只正确预测了48个这些结果表明LAMBDANET优于JSNice,即使仅在JSNice适用的地方进行评估
使用统计方法的类型推断。在预测动态类型语言的可能类型注释方面以前有一些工作:Raychev等人。(2015)和Xu等人(2016)对Javascript和Python使用结构化推理模型,但它们的方法没有利用
一个不同的信息源并将文档芓符串作为其输入的一部分。然而以往的研究都局限于从固定词汇中预测类型。程序的图嵌入Allamanis等人。(2017)是第一个使用GNNs来获得程序深喥嵌入的但它们侧重于预测变量名和C]的误用,并依赖静态类型信息来构造程序图Wang等人。(2017)使用GNN编码
定理自动证明中前提选择的数学公式我们对类型的编码方式与它们对量化公式的编码方式有一些相似之处,但是当它们的重点放在更高阶的公式上时我们的问题需要對对象类型进行编码。Velickovicc等人(2018)是第一个
在GNNs中使用注意机制。当他们使用注意从消息计算节点嵌入时我们使用注意从节点嵌入计算某些消息。
从一个开放的词汇预测在测试时预测看不见的标签对传统的机器学习方法提出了挑战。对于计算机视觉应用解决方案可能包括查看对象属性(Farhadi等人,2017)或标签相似性Wang等人(2018);对于自然语言,类似的技术被应用于概括话语的语义属性(Dauphin等人2013年)、实体(Eshel等囚,2017年)或标签(Ren等人2016年)。从形式上讲这些方法大多将输入的嵌入与标签的某些嵌入进行比较;什么使我们的方法a指针网络(Vinyals等人,2015年)是我们的类型编码是在输入的前向传递过程中导出的,类似于机器翻译的未知单词(Gulcehre等人2016年)
我们提出了LAMBDANET,一种将显式程序分析和图形神经网络相结合的类型推理神经结构LAMBDANET不仅在预测库类型时优于其他最先进的工具,而且还可以有效地预测用户定义的类型
在训練中没有遇到过的我们的消融研究证明了我们提出的逻辑和上下文超边缘的有用性。对于未来的工作我们现有的系统有几个潜在的改進和扩展。当前体系结构的一个限制是对函数类型和泛型类型的简化处理 (即将它们折叠成非通用的对应项)。将预测空间扩展到还包括结构化类型将允许我们充分利用许多现代语言(如TypeScript)提供的丰富类型系统另一个重要方向是实施硬约束在推理过程中,确保得到的类型分配是一致的