App下載

Python:使用循環(huán)神經(jīng)網(wǎng)絡(luò)構(gòu)建 AI 程序員 (1)

夢(mèng)里花 2021-09-08 17:22:53 瀏覽數(shù) (2367)
反饋

近年來(lái),循環(huán)神經(jīng)網(wǎng)絡(luò) (RNN) 受到了廣泛關(guān)注,因?yàn)樗谠S多自然語(yǔ)言處理任務(wù)中顯示出了巨大的前景。 盡管它們很受歡迎,但解釋如何使用最先進(jìn)的工具實(shí)現(xiàn)簡(jiǎn)單而有趣的應(yīng)用程序的教程數(shù)量有限。在本系列中,我們將使用循環(huán)神經(jīng)網(wǎng)絡(luò)來(lái)訓(xùn)練 AI 程序員,該程序員可以像真正的程序員一樣編寫(xiě) Java 代碼(希望如此)。將涵蓋以下內(nèi)容:

1. 構(gòu)建一個(gè)簡(jiǎn)單的 AI 程序員(這篇文章)
2. 改進(jìn) AI 程序員 - 使用令牌
3. 改進(jìn) AI 程序員 - 使用不同的網(wǎng)絡(luò)結(jié)構(gòu)

這篇文章展示了構(gòu)建 LSTM 神經(jīng)網(wǎng)絡(luò)并使用它生成 Java 代碼的步驟。如果你按照帖子進(jìn)行操作,只需單擊一下即可運(yùn)行代碼。(但作為第一步,你需要設(shè)置深度學(xué)習(xí)的開(kāi)發(fā)環(huán)境。你可以按照這篇文章(http://m.hgci.cn/article/23984127.html) 展示了設(shè)置工作環(huán)境的最佳和最簡(jiǎn)單的方法。

本系列的目標(biāo)是為深度學(xué)習(xí)提供一個(gè)切入點(diǎn)。構(gòu)建深度學(xué)習(xí)模型就像畫(huà)一幅油畫(huà)。你可以在開(kāi)始后立即改進(jìn)模型并讓你的第一個(gè)模型工作。

1. 獲取訓(xùn)練原始數(shù)據(jù)

我使用 JDK 的源代碼作為訓(xùn)練數(shù)據(jù)???a >在此處獲得。我們正在構(gòu)建一個(gè)序列到序列的預(yù)測(cè)模型,輸入序列是字符序列。每個(gè) .java 文件都會(huì)被掃描并聚合到一個(gè)名為?“jdk-chars.txt”?的文件中。此外,注釋被忽略,因?yàn)槲覀兿M?AI 程序員學(xué)習(xí)如何編碼。注釋會(huì)使數(shù)據(jù)變得嘈雜。(查看這篇文章以了解如何刪除評(píng)論。)為方便起見(jiàn),聚合文件包含在此項(xiàng)目的 GitHub 存儲(chǔ)庫(kù)中。你可以在這篇文章的末尾找到鏈接。

以下代碼讀取 ?jdk-chars.txt? 并將其切片以適合我的桌面的硬件功能。就我而言,我只使用了代碼中顯示的代碼的 20%。

path = "./jdk-chars.txt"
text = open(path).read()
slice = len(text)/5
slice = int(slice)
 
# slice the text to make training faster
text = text[:slice]
 
print('# of characters in file:', len(text))

2. 建立索引以尋址字符

LSTM 輸入只能理解數(shù)字,所以首先我們需要為每個(gè)字符分配一個(gè)唯一的整數(shù)。

例如,如果代碼中有 65 個(gè)唯一字符,我們?yōu)?65 個(gè)字符中的每個(gè)字符分配一個(gè)數(shù)字。下面的代碼用 ?[“{” : 0 ] [ “a” : 1 ], ... ] ?這樣的條目構(gòu)建了一個(gè)字典。還生成反向字典用于解碼 LSTM 的輸出。

chars = sorted(list(set(text)))
print('# of unique chars:', len(chars))
char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))

3. 準(zhǔn)備帶標(biāo)簽的訓(xùn)練序列

接下來(lái),我們需要準(zhǔn)備帶有標(biāo)簽的訓(xùn)練數(shù)據(jù)。?X? 是我們定義的特定長(zhǎng)度的序列(在我的例子中是 ?40?),?y? 是序列的下一個(gè)字符。例如,從以下行:

int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1; ... ... 

?X? 的樣本是

int weekOfYear = isSet(WEEK_OF_YEAR) ? fi

?y? 是下一個(gè)字符

e

在這里,我們將文本剪切為 ?40 ?個(gè)字符的冗余序列。

NUM_INPUT_CHARS = 40
STEP = 3
sequences = []
next_chars = []
 
for i in range(0, len(text) - NUM_INPUT_CHARS, STEP):
    sequences.append(text[i: i + NUM_INPUT_CHARS])
    next_chars.append(text[i + NUM_INPUT_CHARS])
 
print('# of training samples:', len(sequences))

我們正在嘗試構(gòu)建一個(gè)具有如下結(jié)構(gòu)的網(wǎng)絡(luò):

lstm

4. 向量化訓(xùn)練數(shù)據(jù)

準(zhǔn)備好訓(xùn)練數(shù)據(jù)后,需要將其轉(zhuǎn)換為向量。由于我們?cè)诘诙街袦?zhǔn)備了?char_indices?和?indices_char?,下面的代碼可以輕松地將我們的訓(xùn)練數(shù)據(jù)轉(zhuǎn)換為使用 ?one-hot? 編碼的向量。例如,索引為 ?11 ?的字符將是所有 ?0 ?和位置 ?11 ?處的 ?1? 的向量。

print('Vectorize training data')
X = np.zeros((len(sequences), NUM_INPUT_CHARS, len(chars)), dtype=np.bool)
y = np.zeros((len(sequences), len(chars)), dtype=np.bool)
for i, sequence in enumerate(sequences):
    for t, char in enumerate(sequence):
        X[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1

5. 構(gòu)建單層 LSTM 模型

下面的代碼定義了神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)。該網(wǎng)絡(luò)包含一層具有 ?128 ?個(gè)隱藏單元的 LSTM。所述?input_shape?參數(shù)指定輸入序列長(zhǎng)度(?NUM_INPUT_CHARS?)和輸入的每個(gè)時(shí)間(唯一的字符,即,尺寸),在尺寸。

print('Build model...')
model = Sequential()
model.add(LSTM(128, input_shape=(NUM_INPUT_CHARS, len(chars))))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))
 
optimizer = RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)
print(model.summary())

最后的 ?Dense()? 層是一個(gè)帶有 ?softmax ?激活的輸出層,允許對(duì)輸入向量進(jìn)行 ?len(chars) ?方式分類(lèi)。在訓(xùn)練期間,反向傳播時(shí)間從輸出層開(kāi)始,因此選擇優(yōu)化器 ?= rmsprop? 可以起到重要作用。注意 LSTM 是 Keras 中的輸出層。

?Optimizer ?是優(yōu)化函數(shù)。如果你不知道這個(gè)術(shù)語(yǔ),你可能熟悉邏輯回歸中常用的優(yōu)化函數(shù)——隨機(jī)梯度下降。這是類(lèi)似的事情。

最后一行指定了成本函數(shù)。在這種情況下,我們使用“?categorical_crossentropy?”。

6.訓(xùn)練模型和生成Java代碼

?sample ?函數(shù)用于從概率數(shù)組中采樣一個(gè)索引。例如,給定 ?preds=[0.5,0.2,0.3]? 和默認(rèn)溫度,該函數(shù)將以概率 ?0.5 ?返回索引 ?0?,以概率 ?0.2 ?返回索引 ?1?,或以概率 ?0.3 ?返回索引 ?2?。它用于避免一遍又一遍地生成相同的序列。我們希望看到 AI 程序員可以編寫(xiě)的一些不同的代碼序列。

# train the model, output generated text 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(text) - NUM_INPUT_CHARS - 1)
 
    for diversity in [0.2, 0.5, 1.0, 1.2]:
        print()
        print('----- diversity:', diversity)
 
        generated = ''
        sequence = text[start_index: start_index + NUM_INPUT_CHARS]
        generated += sequence
        print('----- Generating with seed: "' + sequence + '"')
        sys.stdout.write(generated)
 
        for i in range(400):
            x = np.zeros((1, NUM_INPUT_CHARS, len(chars)))
            for t, char in enumerate(sequence):
                x[0, t, char_indices[char]] = 1.
 
            preds = model.predict(x, verbose=0)[0]
            next_index = sample(preds, diversity)
            next_char = indices_char[next_index]
 
            generated += next_char
            sequence = sequence[1:] + next_char
 
            sys.stdout.write(next_char)
            sys.stdout.flush()
        print()

7. 結(jié)果

訓(xùn)練模型需要幾個(gè)小時(shí)。最后生成的代碼如下所示:

----- diversity: 1.2
----- Generating with seed: "eak positions used by next()
// and prev"

eak positions used by next()
// and previos < 
als.get[afip(lookupFDataNtIndexPesicies- > = nuls.simys);
} e.apfwn 0;
for;
rerendenus = contaroyCharset() :
Attch ;
margte.adONamel = getScale(); i  {
int exponentace = sed, off endexpVal.vilal = 0,
break;
localicIntLullAtper.sudid);
}
void fam();
;
if (offset:
b = t);
if (false;
private byte[] is(-notren} fig ist[(i = 0)
molInd);
if (end < = mame") inie = torindLotingenFiols.INFGNTR_FIELD_(ne

生成的代碼沒(méi)有多大意義,甚至沒(méi)有編譯。但是我們?nèi)匀豢梢钥吹?LSTM 捕獲了一些單詞和語(yǔ)法。例如,“?void fam();?”。你還可以查看在早期迭代中生成的代碼。他們的意義不大。

如果調(diào)整參數(shù)(如 ?NUM_INPUT_CHARS ?和 ?STEP?)并訓(xùn)練更長(zhǎng)時(shí)間,可能會(huì)得到更好的結(jié)果。隨意嘗試。我沒(méi)時(shí)間了,想發(fā)表這篇文章。更重要的是,我知道完成這項(xiàng)工作的更好方法,將在下一篇文章中展示。

8. 下一步是什么?

在這篇文章中,我使用字符序列作為輸入來(lái)訓(xùn)練模型,模型預(yù)測(cè)字符序列。除了轉(zhuǎn)換基本LSTM神經(jīng)網(wǎng)絡(luò)的參數(shù),我們還可以使用token代替字符,使用不同的網(wǎng)絡(luò)結(jié)構(gòu)。我們將在下一篇文章中探討這些。


0 人點(diǎn)贊