用 RNN构建一个 AI programmer (2)

在上一篇文章中,我们使用一个简单的1层LSTM神经网络构建了一个基本的人工智能AI程序员。AI程序员生成的代码没有多大意义。在本文中,我们将使用令牌 (tokens) 而不是单个字符序列来训练模型。

1. 获取原始训练数据

我使用的源代码与上一篇文章相同。网址:https://blog.csdn.net/qq_15192373/article/details/89214333。这一次,每个.java文件都被扫描、标记,然后聚合成一个名为 “. / jdk- tokens.txt ” 的文件。不保留换行符。你不需要下载JDK源代码。为了你的方便,我在这个项目的GitHub存储库中包含了聚合文件。你可以在这篇文章的结尾找到这个链接。

下面的代码从jdk-tokens.txt文件中读取令牌,并对其进行切片,以适应桌面的硬件功能。在我的例子中,我只使用了代码中显示的20%的代码。

path = "./jdk-tokens.txt"
filetext = open(path).read().lower()  #修改
 
# slice the whole string to overcome memory limitation
slice = len(filetext)/5  
slice = int (slice)
filetext = filetext[:slice]  #修改
 
tokenized = filetext.split()  #修改
 
print('# of tokens:', len(tokenized))

2. 建立索引以寻址令牌

LSTM输入只理解数字。将令牌转换为数字的一种方法是为每个令牌分配一个唯一的整数。例如,如果代码中有1000个唯一的令牌,我们可以为1000个令牌中的每一个分配一个唯一的数字。下面的代码用类似于 [ “public” : 0 ] [ “static” : 1 ], ... ].  的条目构建字典。还生成了用于解码 LSTM 输出的反向字典。

# 基本上和上一篇类似
uniqueTokens = sorted(list(set(tokenized)))
print('total # of unique tokens:', len(uniqueTokens))
token_indices = dict((c, i) for i, c in enumerate(uniqueTokens))
indices_token = dict((i, c) for i, c in enumerate(uniqueTokens))

3. 用标签准备训练序列

这里我们将文本剪切成10个令牌的半冗余序列。每个序列是一个训练样本,每个令牌序列的标签是下一个令牌。

NUM_INPUT_TOKENS = 10
step = 3
sequences = []
next_token = []
 
for i in range(0, len(tokenized) - NUM_INPUT_TOKENS, step):  # text 改成 tokenized
    sequences.append(tokenized[i: i + NUM_INPUT_TOKENS])
    next_token.append(tokenized[i + NUM_INPUT_TOKENS])
 
print('nb sequences:', len(sequences))

4. 矢量化训练数据

我们首先创建两个矩阵,然后为每个矩阵赋值。一个用于功能,一个用于标签。len(sequences)是训练样本的总数。

X = np.zeros((len(sequences), NUM_INPUT_TOKENS, len(uniqueTokens)), \
             dtype=np.bool)
y = np.zeros((len(sequences), len(uniqueTokens)), dtype=np.bool)
for i, sentence in enumerate(sequences):
    for t, char in enumerate(sentence):
        X[i, t, token_indices[char]] = 1
    y[i, token_indices[next_token[i]]] = 1

5. 构建单层LSTM模型

我们建立的网络如下:

此外,如下面的注释代码所示,堆叠两个LSTM层非常简单。下面的代码定义了神经网络的结构。网络包含一个具有128个隐藏单元的LSTM层。

input_shape:指定每次的输入序列长度和输入尺寸。

Dense() :执行 output = activation(dot(input, kernel) + bias) . 这里的输入是 lstm 层的输出。

激活功能:由线性激活(“SoftMax”)指定。

优化器:是优化功能。你可能熟悉逻辑回归中常用的方法,即随机梯度下降法。

成本函数:最后一行指定成本函数。在这种情况下,我们使用“分类交叉熵”。你可以看看这篇 文章 理解为什么 交叉熵 比 均方误差(mse)更好。

model = Sequential()
 
# 1-layer LSTM
#model.add(LSTM(128, input_shape=(NUM_INPUT_TOKENS, len(uniqueTokens))))
 
# 2-layer LSTM # 修改
model.add(LSTM(128,return_sequences=True, \
               input_shape=(NUM_INPUT_TOKENS, len(uniqueTokens))))
model.add(LSTM(128)) # 修改
 
model.add(Dense(len(uniqueTokens)))
model.add(Activation('softmax'))
 
optimizer = RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)
print(model.summary())

以上代码还包含了用于堆叠另一层 LSTM 并使其成为两层 LSTM RNN的代码。

 

6. 训练模型与Java代码生成

样本函数用于从一个概率数组中抽取索引。例如,给定preds=[0.5,0.2,0.3]和默认参数,函数将以0.5的概率返回索引0、以0.2的概率返回索引1或以0.3的概率返回索引2。它用于避免反复生成相同的序列。这样可以看 AI 程序员可以编码的不同的代码序列。

def sample(preds, temperature=1.0):
    # helper function to sample an index from a probability array
    preds = np.asarray(preds).astype('float64')
    preds = np.log(preds) / temperature
    exp_preds = np.exp(preds)
    preds = exp_preds / np.sum(exp_preds)
    probas = np.random.multinomial(1, preds, 1)
    return np.argmax(probas)
 
# train the model, output generated code after each iteration
for iteration in range(1, 60):
    print()
    print('-' * 50)
    print('Iteration', iteration)
    model.fit(X, y, batch_size=128, epochs=1)
 
    start_index = random.randint(0, len(tokenized) - NUM_INPUT_TOKENS - 1)
 
    for diversity in [0.2, 0.5, 1.0, 1.2]:
        print()
        print('----- diversity:', diversity)
 
        generated = [] #''
        sequence = tokenized[start_index: start_index + NUM_INPUT_TOKENS]
 
        generated=list(sequence)
 
        print('----- Generating with seed: "' + ' '.join(sequence) + '"-------')
        sys.stdout.write(' '.join(generated))
 
        for i in range(100):
            x = np.zeros((1, NUM_INPUT_TOKENS, len(uniqueTokens)))
            for t, char in enumerate(sequence):
                x[0, t, token_indices[char]] = 1.
 
            preds = model.predict(x, verbose=0)[0]
            next_index = sample(preds, diversity)
            next_pred_token = indices_token[next_index]
 
            generated.append(next_pred_token)
            sequence = sequence[1:]
            sequence.append(next_pred_token)
 
            sys.stdout.write(next_pred_token+" ")
            sys.stdout.flush()
        print()

7. 结果

训练这个模型需要几个小时。我在第40次迭代时停止了,生成的代码如下所示:

----- Generating with seed: "true ) ; } else { boolean result = definesequals"-------
true ) ; } else { boolean result = definesequals
( ) . substring ( 1 , gradients . get ( p ) ; } 
if ( val . null || ( npoints == null ) ? new void . bitlength ( ) + prefixlength ) ; 
for ( int i = 0 ; i < num ; i ++ ) } break ; } 
if ( radix result = != other . off ) ; 
int endoff = b . append ( buf , 0 , len + 1 ) ; digits ++ ] ; 

代码生成看起来比以前基于字符的方法生成的代码要好得多。

为了便于阅读,这里添加了换行符。可以看到 lstm很好地捕获了循环和条件,代码开始变得更有意义。例如,for(int i=0;i<num;i++)是完全正确的Java 循环语句。

如果调整参数(如num_input_chars和step)并训练更长时间,您可能会获得更好的结果。下一个博客将会展示更好的方法。

还可以查看在早期迭代中生成的代码。它们没那么有意义。

8. 下一步是什么?

本文使用令牌序列作为输入来训练模型,模型预测令牌序列。如果一切正常,它应该比基于字符的方法工作得更好。

此外,我们还可以使用不同的网络结构。我们将在下一篇文章中探讨这些内容。

源代码

1)本帖的源代码为 lstm_ai_coder_tokens.py,位于 https://github.com/ryanlr/rnn-ai-programmer。

相关博客:

利用循环神经网络构建人工智能程序员(1)
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦dancing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值