乐趣区

关于java:是不是有一天想象着让代码自动补全今天他来了

作者:熊唯,黄飞,腾讯 PCG/QQ 研发核心 /CV 利用研究组

AI 如果真的能够写代码了,程序员将何去何从?近几年,NLP 畛域的生成式工作有显著的晋升,那通过 AI
咱们能够让代码主动实现后续补全吗?本文次要介绍了如何应用 GPT2 框架实现代码主动补全的性能。

如果 AI 真的能够本人写代码了,程序员将何去何从?

我去年做过一个代码补全的小性能,打包为 androidStudio 插件,应用成果如下:

代码补全模型预测出的后果有时确实会惊吓到我,这也能学到~? 那如果给它见识了全世界的优良代码,再给足够量级参数和优良的模型框架,真的能够实现需求作为输出,间接输入代码吗?

“ 我的需要讲完了,你的代码呢?” 心愿能够看到这一天。

代码补齐性能有其余优良插件也已实现,比方 tabnine,Kite 和国产的 aixcoder。本文次要介绍下代码补全性能须要实现的整套流程。次要包含数据,算法和工程。

数据

家喻户晓,算法工程师大部分工夫都在解决数据。

深度学习是应用大数据训练模型的一个过程,数据是很重要的一个模块。人是会累的,劳动不好还导致记忆不好。AI 是你给多少数据它就能存储接管多少数据,学不到信息那是人的错,给的数据不好或者算法设计不好。所以咱们先尽可能多的筹备好训练数据。

1、数据采集
本文的目标是代码补全,训练数据就是代码段。思考到每种语言格调和语法都不统一,所以单个模型只针对一种代码语言。

我应用的训练数据次要来源于 GitHub,编写了一个简略的爬虫代码,指定语言后依据 stars 的排序下载工程。

Github 的 search API 官网地址:

https://developer.github.com/…

2、数据清理
间接下载的数据必定是不能间接用的,咱们还须要对数据进行清理。

首先,咱们的训练数据只须要工程中的代码文件,以 java 工程为例,咱们只保留.java 结尾的文件,其余文件可剔除。

其次,我的代码补全指标是代码段,不针对正文性能。而且对于代码补全训练时,咱们是会给定肯定范畴的上文,如果存在正文段会占用无效代码信息。另外正文除英文外其余字符不在我的训练 vocab 范畴内,所以须要对代码中正文和日志进行清理。

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

2. 删除日志行

3. 删除正文行,次要针对以下格局

/ 正文文本/

/**
正文段落
*/

// 正文文本

code // 正文
通过以上数据清理后,失去纯代码数据。

3、数据编码
失去了训练数据后还须要把代码文本进行编码。本文应用的是 bpe(byte pair encoder)字节对编码,次要为了数据压缩。bpe 简略了解为将一个单词再拆分为多个字母组合,比方 tencent 拆分为 ten-cent,这些组合形式则是依据大量数据,统计频率失去。因为咱们期待的代码补全性能是在行首输出几个字母,依据上文预期出本行内容。

假如 tensorflow 这个 token 被编码对应到一个 id,那我心愿输出 ten 就输入 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,提出 attention is all you need。2018 年 openAI 采纳了 transformers 构造在 18 年公布了 GPT。同年 google AI Language 公布了 bert 论文,提出的 BERT 模型在 11 个 NLP 工作上刷新了记录。2019 年 openAI 又推出了 GPT-2 模型。。

BERT(Bidirectional Encoder Representation from Transformers)是基于 transformers 框架的 encoder 局部,自编码语言模型,适宜 N-1(比方句子分类),N-N(比方词性标注)的工作,然而它并不适宜做生成工作。

GPT(Generative Pre-Training)基于 transformers 的 decoder 局部,自回归语言模型,适宜生成式工作。

代码补全性能就是基于 GPT2 框架,OPenAI 官网提供了多套 GPT2 预训练模型:

作为一个常常要把模型部署到挪动端的 CVer,看到这个参数级别,我抉择最小的模型进行 finetune。

对于 GPT 算法,上面这篇文章讲的很好,感兴趣同学能够看看:

https://zhuanlan.zhihu.com/p/…

本文在训练中应用 512 个上文,预测到回车符为终止。模型网络应用超参:12 个层,768 个暗藏节点,12 个 heads,采纳了 uber 的 Horovod 分布式框架进行训练。

infer 阶段采纳 beam-search 会导致整个预测过程特地耗时,所以参考了 https://arxiv.org/abs/1904.09…,采纳 top-k sampling,每次预测 top3 的后果再通过概率阈值过滤后作为最终候选输入。

最终 infer 成果:

输出一段代码,预测出后续代码,以回车符截止。

工程

家喻户晓,算法工程师大部分工夫都在做工程。

训练出模型后,还要把模型利用起来,所以还须要一些工程工作须要实现。代码补全性能,最合适的利用场景就是上 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 前向计算逻辑,从申请中获取 data 字段作为输出,infer 预测的后果列表作为输入返回给调用方。

通过下面的工作,咱们曾经提供了一个服务接口,返回咱们代码补全的预测后果。

插件编写

最初一步就是如何在 IDE 上应用性能了。咱们要开发 AS 的插件,须要应用 IntelliJ,首先须要在本机装置配置 IntelliJ IDEA

下载地址:

https://www.jetbrains.com/ide…

社区版源码:

https://github.com/JetBrains/…

好用的插件能够节俭程序员很多工夫,在插件实现时,我还增加了一个小的 git-blame 性能,实时查看指定行的 git 提交人,对于手 Q 这种多人单干的工作,比拟实用。大家也能够通过 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] https://arxiv.org/abs/1706.03762

[2] https://arxiv.org/abs/1810.04805

[3] https://github.com/openai/gpt-2

[4] https://arxiv.org/abs/1904.09751

退出移动版