前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NLP构建代码生成器

NLP构建代码生成器

作者头像
磐创AI
发布2021-08-05 10:10:18
1.2K0
发布2021-08-05 10:10:18
举报
文章被收录于专栏:磐创AI技术团队的专栏

NLP技术可以用来生成实际的代码吗?我们离人工智能被用来编写软件的世界还有多远?

在这个博客中,我尝试构建一个python代码生成器,可以将简单的英语问题语句转换为相应的python代码。

我们把这个问题当作一个序列对序列(Seq2Seq)的学习问题来解决。在这里,我们的英语句子将是我们的输入或SRC序列,而Python代码将是我们的输出或TRG序列。

在过去的几年里,Transformer已经成为解决Seq2Seq问题的主流架构。当今大多数sota模型(如BERT或GPT-3)都在内部使用Transformer。

Transformer,正如我们今天所知道的那样,是谷歌在其最早推出的 “Attention Is All You Need” 论文。这个是“English-to-Python” 模型也可以在该论文中找到原理。

在我们开始解决问题之前,让我们先简要回顾一下Transformer。

Transformer

Transformer可以从三个部分来理解:

  1. 将输入序列编码成状态表示向量的编码器。
  2. 一种注意机制,使我们的Transformer模型能够关注顺序输入流的正确方面。这在编码器和解码器中反复使用,以帮助它们上下文化输入数据。
  3. 对状态表示向量进行解码以生成目标输出序列的解码器。

在我以前的博客中,我已经详细解释了每一个组件中以及代码演练:https://ai.plainenglish.io/lets-pay-attention-to-transformers-a1c2dc566dbd

现在让我们看看如何将数据输入到Transformer中。

了解训练数据

我们将使用一个由人工智能学院(TSAI)策划的定制数据集来训练我们的模型。这个数据集包含大约5000个数据点,其中每个数据点包含一个英语问题语句及其相应的Python代码。你可以按照我的代码参考来理解如何解析数据。

https://github.com/divyam96/English-to-Python-Converter

示例数据点:

英文声明:“write a function that adds two numbers”

Python代码:

代码语言:javascript
复制
def add_two_numbers (num1 ,num2 ):
    sum =num1 +num2 
    return sum

在这里“英语声明” 是我们的输入,“Python代码” 是我们训练的输出或TRG序列。

标识化数据

我们的输入(SRC)和输出(TRG)序列以单个字符串的形式存在,需要进一步标识以发送到Transformer模型中。

为了对输入(SRC)序列进行标识化,我们使用了spacy。

代码语言:javascript
复制
Input = data.Field(tokenize = 'spacy',
            init_token='<sos>', 
            eos_token='<eos>', 
            lower=True)

为了标识输出(TRG)序列,我们使用基于Python源代码标识器构建自定义标识器。Python的标识器为每个标识返回几个属性。

我们只以元组的形式提取标识类型和相应的字符串属性(即,(token_type_int, token_string))作为最后的标识。

标识化输入(SRC):

代码语言:javascript
复制
SRC = [' ', 'write', 'a', 'python', 'function', 'to', 'add', 'two', 'user', 'provided', 'numbers', 'and', 'return', 'the', 'sum']

标识化输出(TRG):

代码语言:javascript
复制
TRG = [(57, 'utf-8'), (1, 'def'), (1, 'add_two_numbers'), (53, '('), (1, 'num1'), (53, ','), (1, 'num2'), (53, ')'), (53, ':'), (4, '\n'), (5, '    '), (1, 'sum'), (53, '='), (1, 'num1'), (53, '+'), (1, 'num2'), (4, '\n'), (1, 'return'), (1, 'sum'), (4, ''), (6, ''), (0, '')]

数据扩充

由于我们的数据集仅包含5000个数据点,因此我们使用数据扩充来增加数据集的大小。

在对python代码进行标识化时,我们随机屏蔽某些变量的名称(使用‘变量1,‘变量2’ 等等)以确保我们训练的模型不仅仅关注变量的命名方式,而且实际上试图理解python代码的内在逻辑和语法。

例如,考虑以下程序。

代码语言:javascript
复制
def add_two_numbers (num1 ,num2 ):
    sum =num1 +num2 
    return sum

我们可以替换上面的一些变量来创建新的数据点。以下是有效的扩充。

1

代码语言:javascript
复制
def add_two_numbers (var_1 ,num2 ):
    sum =var_1 +num2 
    return sum

2

代码语言:javascript
复制
def add_two_numbers (num1 ,var_1 ):
   sum =num1 +var_1 
   return sum 

3

代码语言:javascript
复制
def add_two_numbers (var_1 ,var_2 ):
    sum = var_1 + var_2 
    return sum

在上面的示例中,我们使用随机变量替换技术将单个数据点扩展为3个以上的数据点。

我们在生成TRG标识时实现了我们的扩充。

在随机选取变量来屏蔽时,我们避免使用关键字文字(keyword.kwlist)控件结构(如下面的skip_list所示)和对象属性。我们将所有需要跳过的文本添加到skip_list中。

代码语言:javascript
复制
import random
import io
import keyword

from tokenize import tokenize

def augment_tokenize_python_code(python_code_str, mask_factor=0.3):


    var_dict = {} # 存储被屏蔽变量的字典

    # 某些保留字不应该被当作普通变量
    # 因此需要从我们的可变屏蔽参数中跳过
    skip_list = ['range', 'enumerate', 'print', 'ord', 'int', 'float', 'zip'
                 'char', 'list', 'dict', 'tuple', 'set', 'len', 'sum', 'min', 'max']
    skip_list.extend(keyword.kwlist)

    var_counter = 1
    python_tokens = list(tokenize(io.BytesIO(python_code_str.encode('utf-8')).readline))
    tokenized_output = []

    for i in range(0, len(python_tokens)):
      if python_tokens[i].type == 1 and python_tokens[i].string not in skip_list:

        if i>0 and python_tokens[i-1].string in ['def', '.', 'import', 'raise', 'except', 'class']: # 避免屏蔽模块、函数和错误字面量
          skip_list.append(python_tokens[i].string)
          tokenized_output.append((python_tokens[i].type, python_tokens[i].string))
        elif python_tokens[i].string in var_dict:  # 变量已经被屏蔽
          tokenized_output.append((python_tokens[i].type, var_dict[python_tokens[i].string]))
        elif random.uniform(0, 1) > 1-mask_factor: # 随机屏蔽变量
          var_dict[python_tokens[i].string] = 'var_' + str(var_counter)
          var_counter+=1
          tokenized_output.append((python_tokens[i].type, var_dict[python_tokens[i].string]))
        else:
          skip_list.append(python_tokens[i].string)
          tokenized_output.append((python_tokens[i].type, python_tokens[i].string))

      else:
        tokenized_output.append((python_tokens[i].type, python_tokens[i].string))

    return tokenized_output

我们现在使用Pytorch的torchtext.data.Field字段.

代码语言:javascript
复制
Output = data.Field(tokenize = augment_tokenize_python_code,
                    init_token='<sos>', 
                    eos_token='<eos>', 
                    lower=False)

应用标识化后的标识化输出(TRG):

代码语言:javascript
复制
TRG = [(57, 'utf-8'), (1, 'def'), (1, 'add_two_numbers'), (53, '('), (1, 'num1'), (53, ','), (1, 'var_1'), (53, ')'), (53, ':'), (4, '\n'), (5, '    '), (1, 'sum'), (53, '='), (1, 'num1'), (53, '+'), (1, 'var_1'), (4, '\n'), (1, 'return'), (1, 'sum'), (4, ''), (6, ''), (0, '')]
输入数据

为了将数据输入到我们的模型中,我们首先使用Pytorch的torchtext.data.BucketIterator工具. 这样可以确保具有相似长度的输入在单个批次中保持在一起,以便于训练。

然后我们将标识化输入(SRC)批输入到编码器中,并在解码器中使用标识化输出(TRG)批。我们的目标是使用编码器的标识化英语输入(SRC)通过解码器预测标识化Python输出(TRG)。

然后,通过Python源代码标识器的untokenize函数来取消标识化预测。

损失函数

由于我们在数据集中使用了数据增强,这意味着我们的模型可以预测一个特定变量的各种值,只要预测在整个代码中是一致,那它们都是正确的。

这意味着我们的训练标签不是很确定,因此用概率1- smooth_eps来对待它们是正确的和不正确的会更有意义。这就是标签平滑所做的。

现在设置好所有组件后,我们可以使用反向传播来训练模型。我们将数据集分为训练数据和验证数据。我们的模型经过训练,直到验证损失不再改善。

值得注意的是,与不使用标签平滑的模型相比,标签平滑会导致更高的损失值。但这是意料之中的,因为我们不打算确定我们的标签预测。变量的情况尤其如此,因为只要通过目标代码序列的预测是一致的,就可以有多个正确的选项。

有关完整的训练代码,请参阅我的笔记本:https://github.com/divyam96/English-to-Python-Converter/blob/main/English_to_Python.ipynb

示例结果

输入:“program to sort a list of dictionaries by key”

输出:

代码语言:javascript
复制
var_1 ={'Nikhil':{'roll':24 ,'marks':17 },
'Akshat':{'roll':54 ,'marks':12 },
'Akash':{'roll':15 },'marks':15 }}
sort_key ='marks'
res ='marks'
res =var_2 (test_dict .items (),key =lambda x :x [1 ][sort_key ])
print ("The sorted dictionary by marks is : "+str (res ))

输入:“function to sum odd elements of list”

输出:

代码语言:javascript
复制
def sum_odd_elements (l :list ):
    return sum ([i for i in l if i %2 ==1 ])

输入:“program to reverse a string”

输出:

代码语言:javascript
复制
var_1 ='Today is bad day'
var_1 [::-1 ]

更多的例子请看我的项目仓库:https://github.com/divyam96/English-to-Python-Converter

我们已经成功地训练了一个模型,它能够将简单的问题语句(英语)转换成相应的python代码。

参考代码

https://github.com/divyam96/English-to-Python-Converter

https://github.com/bentrevett/pytorch-seq2seq

参考引用

[1] Aston Zhang and Zachary C. Lipton and Mu Li and Alexander J. Smola, Dive into Deep Learning(2020).

[2] Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Lukasz Kaiser, Illia Polosukhin, Attention Is All You Need (2017), 31st Conference on Neural Information Processing Systems (NIPS 2017), Long Beach, CA, USA

[3] Rafael Müller, Simon Kornblith, Geoffrey Hinton, When Does Label Smoothing Help?(2019), 33rd Conference on Neural Information Processing Systems (NeurIPS 2019), Vancouver, Canada

[4] Ian Goodfellow and Yoshua Bengio and Aaron Courville, Deep Learning Book(2016), MIT Press.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磐创AI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Transformer
  • 了解训练数据
  • 标识化数据
  • 数据扩充
    • 输入数据
      • 损失函数
        • 示例结果
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档