1. 程式人生 > 其它 >自動機器學習:利用遺傳演算法優化遞迴神經網路

自動機器學習:利用遺傳演算法優化遞迴神經網路

最近,在自動機器學習方面有很多工作,從選擇合適的演算法到特徵選擇和超引數調優。有幾種可用的工具(例如:AutoML和TPOT),可以幫助使用者高效地執行數百個實驗。同樣,深層神經網路結構通常由專家設計;通過試驗和錯誤的方法。通過這種方法,在幾個領域研發出了最先進的模型,但是這種方法非常耗時。最近,由於可用計算能力的增加,研究人員正在使用強化學習和進化演算法來自動化搜尋最優的神經結構。

在本文中,我們將學習如何應用遺傳演算法(GA)來尋找一個最優的視窗大小和一些基於遞迴神經網路(RNN)的長短期記憶(LSTM)單元。為此,我們將使用Keras來訓練和評估時間序列預測問題的模型。對於遺傳演算法,將使用名為DEAP的python包。本文的主要目的是讓讀者熟悉遺傳演算法,以找到最優設定;因此,本文只研究兩個引數。此外,本文假定讀者對RNN有所認識(理論和應用)。

  • DEAP地址:https://github.com/DEAP/deap

具有完整程式碼的ipython netbook可以在以下連結中找到。

  • 程式碼地址:https://github.com/aqibsaeed/Genetic-Algorithm-RNN

遺傳演算法

遺傳演算法是一種啟發式搜尋,是一種基於自然選擇過程的優化方法。它們被廣泛應用於在較大的引數空間尋找近似最優解的優化問題。物種進化的過程(例子中的解決方法)是模仿的,依賴於生物啟發的部分,例如交叉。此外,由於它不考慮輔助資訊(例如導數),它可以用於離散和連續優化。

對於遺傳演算法,必須滿足兩個先決條件,a)解決方案表示或定義一個染色體,b)適應度函式來評估產生的解決方案。在我們的例子中,二進位制陣列是解決方案的遺傳表示(參見圖1),模型在驗證集上的均方根誤差(RMSE)將成為一個適應度值。此外,構成遺傳演算法的三種基本操作如下:

1. 選擇:它定義了為進一步的複製而保留的解決方案。例如賭輪選擇。

2. 交叉:它描述瞭如何從現有的解決方案建立新的解決方案。例如n點交叉。

3. 突變:它的目的是通過隨機交換或關閉解決方案,將多樣性和新奇性引入到解決方案池(solution pool)中。例如二進位制突變。

圖1

有時,一種被稱為“Elitism”的技術也被使用,它只保留少數最好的解決方案,並傳遞給下一代。圖2描述了一個完整的遺傳演算法,其中,初始解(種群)是隨機生成的。接下來,根據適應度函式和選擇進行評估,然後進行交叉和變異。這個過程重複定義迭代的次數中重複。最後,選擇一個具有最高適應度分數的解決方案作為最佳解決方案。

圖2

實現

現在,我們對遺傳演算法是什麼以及它如何工作有一個很好的理解。接下來,讓我們開始編碼。

我們將使用風力發電預測資料。它由7個風力發電場的標準(0-1)風能度量組成。為了簡單起見,我們將使用第一個風力發電場(名為wp1),但我鼓勵讀者嘗試並擴充套件程式碼,以預測所有7個風力發電場的能源。

  • 風能預測資料地址:https://www.kaggle.com/c/GEF2012-wind-forecasting/data

匯入所需的包,載入資料集並定義兩個輔助函式。第一個方法prepare_dataset將資料分割成塊,為模型訓練建立XY對。X是過去(例1到t-1)的風電價值(wind power value),Y將在t時刻為未來值(future value)。第二種方法train_evaluate執行三件事,1)解碼遺傳演算法解決方案以獲得視窗大小和單元數。2)使用GA找到的視窗大小來準備資料集,並將其劃分為訓練和驗證集,3)訓練LSTM模型,在驗證集上計算RMSE,並返回該值將其作為當前遺傳演算法解決方案的適應度值。

import numpy as np
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split as split

from keras.layers import LSTM, Input, Dense
from keras.models import Model

from deap import base, creator, tools, algorithms
from scipy.stats import bernoulli
from bitstring import BitArray

np.random.seed(1120)
data = pd.read_csv('train.csv')
data = np.reshape(np.array(data['wp1']),(len(data['wp1']),1))

# Use first 17,257 points as training/validation and rest of the 1500 points 
as test set.
train_data = data[0:17257]
test_data = data[17257:]
def prepare_dataset(data, window_size):
    X, Y = np.empty((0,window_size)), np.empty((0))
    for i in range(len(data)-window_size-1):
        X = np.vstack([X,data[i:(i + window_size),0]])
        Y = np.append(Y,data[i + window_size,0])   
    X = np.reshape(X,(len(X),window_size,1))
    Y = np.reshape(Y,(len(Y),1))
    return X, Y

def train_evaluate(ga_individual_solution):   
    # Decode GA solution to integer for window_size and num_units
    window_size_bits = BitArray(ga_individual_solution[0:6])
    num_units_bits = BitArray(ga_individual_solution[6:]) 
    window_size = window_size_bits.uint
    num_units = num_units_bits.uint
    print('nWindow Size: ', window_size, ', Num of Units: ', num_units)
    
    # Return fitness score of 100 if window_size or num_unit is zero
    if window_size == 0 or num_units == 0:
        return 100, 
    
    # Segment the train_data based on new window_size; split into train and 
validation (80/20)
    X,Y = prepare_dataset(train_data,window_size)
    X_train, X_val, y_train, y_val = split(X, Y, test_size = 0.20, 
random_state = 1120)
    
    # Train LSTM model and predict on validation set
    inputs = Input(shape=(window_size,1))
    x = LSTM(num_units, input_shape=(window_size,1))(inputs)
    predictions = Dense(1, activation='linear')(x)
    model = Model(inputs=inputs, outputs=predictions)
    model.compile(optimizer='adam',loss='mean_squared_error')
    model.fit(X_train, y_train, epochs=5, batch_size=10,shuffle=True)
    y_pred = model.predict(X_val)
    
    # Calculate the RMSE score as fitness score for GA
    rmse = np.sqrt(mean_squared_error(y_val, y_pred))
    print('Validation RMSE: ', rmse,'n')
    
    return rmse,

接下來,使用DEAP包來定義執行遺傳演算法的東西。對於長度為10的解,我們將使用二進位制表示。它將使用伯努利分佈隨機初始化。同樣,使用了有序交叉、隨機突變和賭輪選擇。遺傳演算法引數值被任意初始化;我建議你在不同的設定下嘗試。

population_size = 4
num_generations = 4
gene_length = 10

# As we are trying to minimize the RMSE score, that's why using -1.0. 
# In case, when you want to maximize accuracy for instance, use 1.0
creator.create('FitnessMax', base.Fitness, weights = (-1.0,))
creator.create('Individual', list , fitness = creator.FitnessMax)

toolbox = base.Toolbox()
toolbox.register('binary', bernoulli.rvs, 0.5)
toolbox.register('individual', tools.initRepeat, creator.Individual, toolbox.
binary, 
n = gene_length)
toolbox.register('population', tools.initRepeat, list , toolbox.individual)

toolbox.register('mate', tools.cxOrdered)
toolbox.register('mutate', tools.mutShuffleIndexes, indpb = 0.6)
toolbox.register('select', tools.selRoulette)
toolbox.register('evaluate', train_evaluate)

population = toolbox.population(n = population_size)
r = algorithms.eaSimple(population, toolbox, cxpb = 0.4, mutpb = 0.1, 
ngen = num_generations, verbose = False)

通過使用 tools.selBest(population,k = 1),可以很容易地通過遺傳演算法找到的K最佳解決方案。之後,最優配置可以用來訓練完整的訓練集,並在holdout測試集上進行測試。

# Print top N solutions - (1st only, for now)
best_individuals = tools.selBest(population,k = 1)
best_window_size = None
best_num_units = None

for bi in best_individuals:
    window_size_bits = BitArray(bi[0:6])
    num_units_bits = BitArray(bi[6:]) 
    best_window_size = window_size_bits.uint
    best_num_units = num_units_bits.uint
    print('nWindow Size: ', best_window_size, ', Num of Units: ', 
best_num_units)
# Train the model using best configuration on complete training set 
#and make predictions on the test set
X_train,y_train = prepare_dataset(train_data,best_window_size)
X_test, y_test = prepare_dataset(test_data,best_window_size)

inputs = Input(shape=(best_window_size,1))
x = LSTM(best_num_units, input_shape=(best_window_size,1))(inputs)
predictions = Dense(1, activation='linear')(x)
model = Model(inputs = inputs, outputs = predictions)
model.compile(optimizer='adam',loss='mean_squared_error')
model.fit(X_train, y_train, epochs=5, batch_size=10,shuffle=True)
y_pred = model.predict(X_test)

rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print('Test RMSE: ', rmse)

在本文中,我們瞭解瞭如何使用遺傳演算法自動找到最佳視窗大小以及在RNN中使用的一些單元。為了進一步學習,我建議嘗試使用不同的遺傳演算法引數配置,擴充套件遺傳表示式,以包含更多的引數。