手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

大數據文摘作品

編譯:修竹、笪潔瓊、夏雅薇

作者用了一種新奇的方法來訓練神經網絡。更新權重的分佈而不是順序更新靜態權重,得到了更有趣和可靠的結果。貝葉斯方法給了我們一個機會,使得我們可以不手動添加正則項的情況下對神經網絡進行正則化,理解模型的不確定性,並儘可能使用更少的數據得到更好的結果。

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

Hi!又見面啦。去年我推出了幾篇基於神經網絡的金融預測教程,我認為有些結果還是蠻有趣的,值得應用在實際交易中。

如果你讀過那些教程,你一定會注意到,當你試圖在“隨機”數據上用一些機器學習模型並且希望找到隱藏模式時,你其實正逐漸對訓練集進行過擬合。

我們使用不同的正則化方法和補充數據來解決這個問題,但是這非常耗時間並且有點盲目搜索了。

今天我想介紹一種稍微不同的方法來用於相同的算法。從概率角度講,我們可以從數據本身學習正則化方法,在我們預測中估計準確性,使用更少的數據來訓練並且在模型中加入概率依賴。

我不會深入到貝葉斯模型或變分推理的技術或者數學細節上,我將給出一些概述,同時也會更加關注如何應用。像往常一樣,你可以在下面的鏈接內查看代碼。

代碼鏈接:

https://github.com/Rachnog/Deep-Trading/tree/master/bayesian

為了更深入瞭解概率編程、貝葉斯模型及其應用,我推薦以下資源給大家:

  • 模式識別和機器學習:

    http://www.springer.com/us/book/9780387310732

  • 為黑客設計的貝葉斯方法:

    https://www.amazon.com/Bayesian-Methods-Hackers-Probabilistic-Addison-Wesley/dp/0133902838

同時推薦以下python庫:

  • PyMC3:

    https://github.com/pymc-devs/pymc3

  • Edward:

    http://edwardlib.org/

  • Pyro:

    http://pyro.ai/

概率編程

這個概率性的東西是什麼,而且我們為什麼要稱之為編程呢?首先,我們先回憶一下“正常”的神經網絡以及我們能從中獲得什麼。

我們有參數(權重),這些參數以矩陣表示,輸出通常是一些標量值或者向量(例如用於分類時)。比如說,在用SGD訓練模型之後,我們有了這些固定矩陣和網絡在相同的輸入樣本上輸出相同的向量。完全正確!

但是如果我們認為這些參數和輸出都是互相依賴的分佈呢?

神經網絡中的每個權重都是來自某個分佈的樣本,輸出也一樣,每個輸入來自整個網絡的樣本,同時這個網絡依賴參數的樣本。它給予了我們什麼?

我們從最基礎的開始講。

如果我們把網絡看成一組彼此依賴的分佈,首先定義聯合概率分佈為p(y, z|x),輸出為y,還有一些依賴輸入x 的模型“內部”、隱藏參數z(和普通神經網絡一樣)。

而我們想要找到一種神經網絡分佈,我們可以對y ~ p(y|x)採樣然後把分佈作為輸出(該分佈的樣本期望值通常就是輸出,標準差用來評估不確定性,如果分佈模型的尾部越大,我們對於輸出越沒有信心)。

這個設置或多或少已經很清楚了,我們只需要記住,現在所有的參數,不管是模型的輸入還是輸出,都是分佈。我們需要的訓練是找到這些分佈的參數以便在實際任務中獲得更高的準確率。

必須要提到的是,參數分佈的形狀是我們自己設置的(例如,所有的初始權重都是w ~ Normal(0, 1),然後我們將學習正確的均值和方差)。

初始分佈稱之為先驗分佈,使用過訓練數據擬合參數的分佈叫做後驗分佈。後者用於取樣和獲得輸出數據。

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

模型的擬合效果怎麼樣呢?一般的框架叫做變分推理。我們不去深入瞭解細節,在這裡我們需要尋找的模型是可以最大化似然函數log p_w(z|x)的, w 是模型的參數(分佈參數),z是隱藏變量(隱藏神經元輸出,從參數為 w 的分佈中取樣得到的),x是輸入數據樣本。這就是我們的模型。

在Pyro庫中我們引入了一個實例作為這個模型的指導,指導中包括一些對所有隱藏變量q_ф(z)的分佈,其中 ф叫做變分參數。這個分佈必須近似於擬合數據最好的模型參數的“真實”分佈。

訓練的目的是最小化一個指導中關於輸入數據和樣本[log(p_w(z|x)) — log(q_ф(z))] 的期望。我們這裡不討論訓練過程的細節,因為這裡麵包含好幾門大學課程,現在我們就做黑盒優化好了。

哦對了,為什麼是編程呢?因為我們通常將這種概率模型(比如神經網絡)描述為從一個變量到另一個變量的有向圖,這樣我們就可以直接表示變量的依賴性:

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

最初這種概率編程語言被用來定義這些模型並對其進行推斷。

為什麼用概率編程?

你可以將它認為是一種附加的隱藏變量,從數據中學習模型,而不是採用在模型中注入dropout或L1正則化的方法。

考慮到所有權重都是分佈,你可以從中進行N次抽樣然後得到輸出的分佈,通過標準差可以估算你的模型對於結果的準確性。

而且還有個不錯的贈禮就是我們只需要用更少的數據來訓練模型,並且我們可以在變量間靈活的增加不同的依賴關係。

為什麼不用概率編程呢?

我對於使用貝葉斯模型沒有太多經驗,但就我從Pyro和PyMC3學習中可以知道,訓練過程耗時很長而且很難定義準確的先驗分佈。此外,處理分佈的多個樣本會導致誤解和歧義。

數據展示

我獲取了以太坊每日價格。這裡麵包括典型OHLCV(高開低走)元組以及每天關於以太坊推特的數量。

https://bitinfocharts.com/

我們將使用7天的價格、交易量和推特數量變化的百分比來預測下一天變化的百分比。

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

價格、推特數量和交易量變化

上圖所示是數據的樣本——藍色表示價格變化,黃色表示推特數量變化,綠色表示交易量變化。這些值(0.1-0.2)之間有一些正相關,所以我們希望利用一些數據訓練模型。

貝葉斯線性迴歸

首先我想了解簡單線性迴歸在我們任務中的表現。

Pyro官方教程:

http://pyro.ai/examples/bayesian_regression.html

我們在PyTorch中定義了我們模型(詳細解釋請看官方教程):

class RegressionModel(nn.Module): def __init__(self, p): super(RegressionModel, self).__init__() self.linear = nn.Linear(p, 1)def forward(self, x): # x * w + b return self.linear(x)

這只是我們曾經用過的一個簡單確定性模型,但是這也是在Pyro中定義概率的方法。

def model(data): # Create unit normal priors over the parameters mu = Variable(torch.zeros(1, p)).type_as(data) sigma = Variable(torch.ones(1, p)).type_as(data) bias_mu = Variable(torch.zeros(1)).type_as(data) bias_sigma = Variable(torch.ones(1)).type_as(data) w_prior, b_prior = Normal(mu, sigma), Normal(bias_mu, bias_sigma) priors = {'linear.weight': w_prior, 'linear.bias': b_prior} lifted_module = pyro.random_module("module", regression_model, priors) lifted_reg_model = lifted_module()with pyro.iarange("map", N, subsample=data): x_data = data[:, :-1] y_data = data[:, -1] # run the regressor forward conditioned on inputs prediction_mean = lifted_reg_model(x_data).squeeze() pyro.sample("obs", Normal(prediction_mean, Variable(torch.ones(data.size(0))).type_as(data)), obs=y_data.squeeze())

上述代碼中我們為參數W和b設置了一般線性迴歸模型分佈,均為 ~Normal(0, 1)。我們稱之為先驗,創建了Pyro的隨機函數(在我們例子中,是PyTorch的迴歸模型),將先驗概率加到({‘linear.weight’: w_prior, ‘linear.bias’: b_prior})並且基於輸入數據x從模型p(y|x)中採樣。

這個模型的指導如下所示:

def guide(data): w_mu = Variable(torch.randn(1, p).type_as(data.data), requires_grad=True) w_log_sig = Variable(0.1 * torch.ones(1, p).type_as(data.data), requires_grad=True) b_mu = Variable(torch.randn(1).type_as(data.data), requires_grad=True) b_log_sig = Variable(0.1 * torch.ones(1).type_as(data.data), requires_grad=True) mw_param = pyro.param("guide_mean_weight", w_mu) sw_param = softplus(pyro.param("guide_log_sigma_weight", w_log_sig)) mb_param = pyro.param("guide_mean_bias", b_mu) sb_param = softplus(pyro.param("guide_log_sigma_bias", b_log_sig)) w_dist = Normal(mw_param, sw_param) b_dist = Normal(mb_param, sb_param) dists = {'linear.weight': w_dist, 'linear.bias': b_dist} lifted_module = pyro.random_module("module", regression_model, dists)return lifted_module()

這裡我們定義了我們想要“訓練”的分佈的變分分佈。就像你看到的,我們為W和b定義了相同形狀的分佈,但是儘量使他們更接近實際(只要我們能想到的)。在這個例子裡,我選擇讓這個分佈形狀更窄一些。

(~Normal(0, 0.1))

我們以這種方式訓練模型:

for j in range(3000): epoch_loss = 0.0 perm = torch.randperm(N) # shuffle data data = data[perm] # get indices of each batch all_batches = get_batch_indices(N, 64) for ix, batch_start in enumerate(all_batches[:-1]): batch_end = all_batches[ix + 1] batch_data = data[batch_start: batch_end] epoch_loss += svi.step(batch_data)

之後我們想從模型中取樣y。重複取樣100次然後計算每一次取樣預測的均值和標準差(標準差越大,我們對預測準確的信心越低)。

preds = []for i in range(100): sampled_reg_model = guide(X_test) pred = sampled_reg_model(X_test).data.numpy().flatten()preds.append(pred)

我們應該記得,金融預測中MSE,MAE或者MAPE等經典指標可能會讓人很困惑——相對較小的錯誤率並不意味著你的模型運行良好,所以在樣本外的數據上查看性能是非常重要的,這也是我們要做的:

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

30天的貝葉斯模型預測

如上圖所示,結果並不是很好,但是最後一條的預測形狀很好,這給了我們一點信心。讓我們繼續吧!

普通神經網絡

在這個非常簡單的模型之後,我們想試著嘗試些更有趣的東西,比如神經網絡。首先讓我們先了解一個簡單的MLP,只有一個隱藏層,包括含有25個神經元以及線性激活模型。

def get_model(input_size): main_input = Input(shape=(input_size, ), name='main_input') x = Dense(25, activation='linear')(main_input) output = Dense(1, activation = "linear", name = "out")(x) final_model = Model(inputs=[main_input], outputs=[output]) final_model.compile(optimizer='adam', loss='mse')return final_model

然後訓練100次。

model = get_model(len(X_train[0]))history = model.fit(X_train, Y_train, epochs = 100, batch_size = 64, verbose=1, validation_data=(X_test, Y_test), callbacks=[reduce_lr, checkpointer], shuffle=True)

得到以下結果:

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

30天的Keras神經網絡預測

這個結果甚至比簡單貝葉斯迴歸還糟糕,而且這個模型不能得到確定性估計,更重要的是,這個模型甚至不能正則化。

貝葉斯神經網絡

現在我想在PyTorch中定義一個和我們在Keras中訓練的相同的神經網絡。

class Net(torch.nn.Module): def __init__(self, n_feature, n_hidden): super(Net, self).__init__() self.hidden = torch.nn.Linear(n_feature, n_hidden) # hidden layer self.predict = torch.nn.Linear(n_hidden, 1) # output layerdef forward(self, x): x = self.hidden(x) x = self.predict(x) return x

與貝葉斯迴歸模型相比,我們現在有兩組參數(從輸入到隱藏層以及從隱藏層到輸出),所以我們稍微改變一下模型分佈和先驗:

priors = {'hidden.weight': w_prior, 'hidden.bias': b_prior, 'predict.weight': w_prior2, 'predict.bias': b_prior2}

以及這個指導:

dists = {'hidden.weight': w_dist, 'hidden.bias': b_dist, 'predict.weight': w_dist2, 'predict.bias': b_dist2}

不要忘記為模型中所有的分佈設置不同的名字,因為不能有任何的歧義和重複!可以在源代碼中查看更多細節。

源代碼:

https://github.com/Rachnog/Deep-Trading/tree/master/bayesian

在擬合模型和採樣後,讓我們直接看最終結果:

手把手:基於概率編程Pyro的金融預測,讓正則化結果更有趣!

30天的Pyro神經網絡預測

這個結果看上去比之前的結果都要好!

考慮下從貝葉斯模型中學到的正則化或者權重的性質,與普通神經網絡做比較,我還會看一下權重統計。下面是我在Pryo模型中如何檢查參數的:

for name in pyro.get_param_store().get_all_param_names():print name, pyro.param(name).data.numpy()

在Keras模型中我是這麼做的:

import tensorflow as tfsess = tf.Session()with sess.as_default(): tf.global_variables_initializer().run()dense_weights, out_weights = None, Nonewith sess.as_default(): for layer in model.layers: if len(layer.weights) > 0: weights = layer.get_weights() if 'dense' in layer.name: dense_weights = layer.weights[0].eval() if 'out' in layer.name: out_weights = layer.weights[0].eval()

舉個例子,對於Keras模型中,最後一層的權重平均值和標準差分別是-0.0025901748,0.30395034,對於Pyro模型分別是0.0005974418和0.0005974418,數值更小,模型性能更好!

就像許多L2或者dropout這種正則化方法做的那樣,讓參數儘可能接近0,然後我們可以用變分推理來實現!對於隱藏層權重的情況就更有趣了。

我們把一些權重向量畫出來,藍色代表Keras的權重,橙色代表Pyro的權重:

輸入和隱藏層間的一些權重

有趣的是,事實上不僅權重的均值和標準差很小,而且權重變得更加稀疏,所以基本上我們對於第一組權重用到了稀疏表示(類似L1正則),對第二組用到了類似L2正則表示,簡直不可思議!不要忘記試一下代碼哦!

結論

我們用了一種新奇的方法來訓練神經網絡。我們更新權重的分佈而不是順序更新靜態權重。所以我們得到更有趣和可靠的結果。

我想要強調的是,貝葉斯方法給了我們一個機會,使得我們可以不手動添加正則項的情況下對神經網絡進行正則化,理解模型的不確定性,並儘可能使用更少的數據得到更好的結果。歡迎繼續關注!:)

原文鏈接:

https://medium.com/@alexrachnog/financial-forecasting-with-probabilistic-programming-and-pyro-db68ab1a1dba


分享到:


相關文章: