算法 自动采集列表(如何使用GPT2框架实现代码自动补全的功能?(上))

优采云 发布时间: 2021-09-10 21:13

  算法 自动采集列表(如何使用GPT2框架实现代码自动补全的功能?(上))

  作者:魏雄、黄飞、腾讯PCG/QQ研发中心/简历应用研究组

  如果AI真的会写代码,程序员会去哪里?近年来,NLP领域的生成任务有了明显的提升。能否通过AI让代码自动完成后续补全?本文主要介绍如何使用GPT2框架实现代码补全功能。

  如果人工智能真的可以自己写代码,程序员会去哪里?

  我去年做了一个小代码补全功能,打包成androidStudio插件,效果如下:

  

  代码补全模型预测的结果有时候确实能吓到我,这也能学~?那么如果给它洞察世界上优秀的代码,再给它足够的幅度参数和优秀的模型框架,真的可以实现需求作为输入直接输出代码吗?

  “我的要求已经结束,你的代码呢?”我希望看到这一天。

  代码补全功能也已经被其他优秀的插件实现了,比如tabnine、Kite和国内的aixcoder。本文主要介绍代码补全功能需要实现的全过程。主要包括数据、算法和工程。

  数据

  众所周知,算法工程师大部分时间都花在处理数据上。

  深度学习是一个利用大数据训练模型的过程,数据是一个非常重要的模块。人累了,休息不好会导致记忆力差。 AI 意味着它可以存储和接收尽可能多的数据。如果你没有学习这些信息,那是人为的错。给定的数据不好或算法设计不好。所以我们首先准备尽可能多的训练数据。

  1、数据采集

  本文的目的是代码补全,训练数据是代码段。考虑到每种语言风格和语法不一致,单一模型只针对一种代码语言。

  我使用的训练数据主要来自GitHub。我写了一个简单的爬虫代码。指定语言后,我按照星星的顺序下载项目。

  Github 搜索 API 官方地址:/v3/search/

  2、数据清理

  直接下载的数据不能直接使用。我们还需要清理数据。

  首先,我们的训练数据只需要项目中的代码文件。以java项目为例,我们只保留.java结尾的文件,其他文件可以去掉。

  其次,我的代码补全的目标是代码段,而不是注释功能。而对于代码补全训练,我们会给出一定范围的上述内容,如果有注释部分,会占用有效的代码信息。另*敏*感*词*内,所以代码中的注释和日志需要清理。

  1.删除代码行中除符号和英文以外的字符的存在

  2.删除日志行

  3.删除注释行,主要针对以下格式

  /* 注释文本*/

/**

注释段落

*/

// 注释文本

code //注释

  经过以上数据清洗,得到纯代码数据。

  3、数据编码

  得到训练数据后,需要对代码文本进行编码。本文使用bpe(byte pair encoding)字节对编码,主要用于数据压缩。 Bpe 简单理解为将一个单词拆分为多个字母组合。例如,腾讯拆分为十美分。这些组合基于大量数据和统计频率。由于我们期望的代码补全功能是在行首输入几个字母,所以这一行的内容是按照上面的来期望的。

  假设token tensorflow被编码为对应一个id,那么我希望输入十,输出tensorflow是不可能的。所以在训练过程中,我会随机打断token,比如打断tensorflow到t-en-sor-flow进行编码。打断的原则是被分割的部分必须在词汇表中。数据编码后,将编码的每个token编码成1~N个id。模型预测的id可以反向编码为token。回车符被认为是预测的终止符。经过上面的处理,我们已经准备好了训练数据,接下来就可以进入算法部分了。

  模型算法

  众所周知,算法工程师大部分时间都在研究算法。

  在腾讯文档的错别字和纠错要求中,我们使用了基于LSTM的seq2seq和facebook的基于CNN的seq2seq,可以得到很好的纠错效果。直到NLP出现了“网红”-BERT,采用后准确率直接提升了8分左右,而且是google。下面简单介绍一下bert和gpt2。

  BERT 和 GPT2

  2017 年年中,google 提出了 Transformer 结构。不需要rnn,不需要cnn,引起关注就是你所需要的。 2018年openAI采用transformers结构,2018年发布GPT。同年google AI Language发布bert论文,提出的BERT模型在11个NLP任务上创下新纪录。 2019 年,openAI 推出了 GPT-2 模型。 .

  BERT(Bidirectional Encoder Representation from Transformers)基于transformers框架的encoder部分,自编码语言模型,适用于N-1(如句子分类)、NN(如词性标注)任务,但不适合生成Task。

  GPT(Generative Pre-Training)基于transformers的*敏*感*词*部分,一种自回归语言模型,适用于生成任务。

  

  

  代码补全功能基于GPT2框架。 OPenAI官方提供多套GPT2预训练模型:

  

  作为经常将模型部署到移动端的CVer,看到这个参数级别,我选择了最小的模型进行finetune。

  对于GPT算法,/p/137350403这个文章很不错,有兴趣的同学可以看看。

  

  本文在训练中使用了512个文本,预测回车符结束。模型网络使用超参数:12 层、768 个隐藏节点和 12 个头。它使用 uber 的 Horovod 分布式框架进行训练。

  infer阶段使用beam-search会导致整个预测过程特别耗时,所以参考/abs/1904.09751论文,使用top-k采样,并且每次将top3预测的结果通过概率阈值过滤作为最终候选输出。

  最终推断效果:

  

  输入代码,预测后续代码,以回车结束。

  工程

  众所周知,算法工程师大部分时间都花在工程上。

  模型训练好后,需要应用模型,所以需要实现一些工程工作。代码补全功能最适合的应用场景是使用IDE。 nlp模型不适合本地部署,最后选择在GPU机器上部署模型,然后终端通过http请求获得预测文本显示的解决方案。

  后台部署

  Flask 是一个 Web 应用程序框架,灵活、轻便且易于使用。本文简单介绍了如何使用flask启动一个web服务,以及如何访问和调用我们的功能接口。首先我们创建一个 conda 环境:

  conda create -n flask python=3.6

source activate flask

pip install flask

  在代码中添加一个接口函数:

  from flask import Flask

from flask import request

app = Flask()

# route把一个函数绑定到对应的 url 上

@app.route("/plugin",methods=['GET',])

def send():

data = request.args.get('data')

# 模型预测逻辑

out = model_infer(data)

return out

if __name__ == '__main__':

app.run(host='0.0.0.0',port=8080, debug=False)

  执行run.py代码,后台服务启动并运行:

  

  客户请求:

  url = http://ip:8080/plugin?data="输入"

  model_infer函数需要实现模型的infer前向计算逻辑。从请求中获取数据字段作为输入,将infer预测的结果列表作为输出返回给调用者。

  经过上述工作,我们提供了一个服务接口,用于返回我们代码完成的预测结​​果。

  插件编写

  最后一步是如何使用IDE上的功能。如果我们要开发AS插件,需要用到IntelliJ,首先我们需要在本机上安装配置IntelliJ IDEA

  下载地址:/idea/download/

  社区版源码:/JetBrains/intellij-community

  一个简单易用的插件可以为程序员节省大量时间。插件实现的时候,我还加了一个小的git-blame功能,可以实时查看指定行的git committer。对于手机QQ等多人协同工作,更实用。也可以通过 IntelliJ 自己开发一些常用功能。

  

  gitBlame 的主要代码:

  public class GitBlame extends AnAction {

private void showPopupBalloon(final Editor editor, final String result) {

ApplicationManager.getApplication().invokeLater(new Runnable() {

public void run() {

JBPopupFactory factory = JBPopupFactory.getInstance();

factory.createHtmlTextBalloonBuilder(result, null, new JBColor(new Color(186, 238, 186), new Color(73, 117, 73)), null)

.setFadeoutTime(5000)

.createBalloon()

.show(factory.guessBestPopupLocation(editor), Balloon.Position.below);

}

});

}

@Override

public void actionPerformed(AnActionEvent e) {

// TODO: insert action logic here

//获得当前本地代码根目录

String base_path = e.getProject().getBasePath();

String file_path = e.getProject().getProjectFilePath();

//获取编辑mEditor

final Editor mEditor = e.getData(PlatformDataKeys.EDITOR);

if (null == mEditor) {

return;

}

SelectionModel model = mEditor.getSelectionModel();

final String selectedText = model.getSelectedText();

if (TextUtils.isEmpty(selectedText)) {

return;

}

//获取当前编辑文档的目录

PsiFile mPsifile = e.getData(PlatformDataKeys.PSI_FILE);

VirtualFile file = mPsifile.getContainingFile().getOriginalFile().getVirtualFile();

if (file != null && file.isInLocalFileSystem()) {

file_path = file.getCanonicalPath();

}

//gitkit工具

JGitUtil gitKit = new JGitUtil();

String filename = file_path.replace(base_path+"/","");

//得到blame信息

int line_index = mEditor.getSelectionModel().getSelectionStartPosition().getLine();

String blame_log = gitKit.git_blame(base_path,filename,line_index);

//展示

if (!blame_log.isEmpty()){

showPopupBalloon(mEditor, blame_log);

}

}

}

  本文代码补全插件的主要代码逻辑是调用上一步后台部署的请求。

  // 请求url格式(和flask接口一致)

String baseUrl = "http://ip:8080/plugin?data=";

// 获取当前编辑位置文本

PsiFile str = position.getContainingFile();

// 根据模型上文限制获取代码端

String data = getContentCode();

String url = baseUrl+data;

// 发送请求

String result = HttpUtils.doGet(url);

// 后处理逻辑,在提示框显示预测结果

show()

  最终呈现形式:

  

  可以看出模型的预测结果还是不错的~

  以上就是代码补全功能的实现和应用,可以看作是AI自动编写代码的一小步。

  AI自己写代码有可能在嫌疑人追踪上达到TM的水平吗?我不敢说不可能,但以我目前的认知,是不可能实现的。毕竟,是程序员编写代码并将数据提供给算法。是程序员,算法设计师是程序员,AI连帮助人类解决bug的功能都没有! \

  参考资料:

  [1] /abs/1706.03762

  [2] /abs/1810.04805

  [3] /openai/gpt-2

  [4] /abs/1904.09751

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线