如果要評選2017三大流行金酸梅獎,毫無疑問,獲獎的肯定是指尖陀螺、人工智慧以及加密貨幣。加密貨幣是一項顛覆性的技術,它背後的原理引人注目,我十分看好它未來的發展。
實際上,我並沒有持有任何加密貨幣,但說起憑藉深度學習、機器學習以及人工智慧成功預測加密貨幣的價格,我覺得自己還算是個老司機。
一開始,我認為把深度學習和加密貨幣結合在一起研究是個非常新穎獨特的想法,但是當我在準備這篇文章時,我發現了一篇類似的文章。那篇文章只談到比特幣。我在這篇文章中還會討論到以太幣(它還有一些別名:ether、eth或者lambo-money)。
類似文章連結:
http://www.jakob-aungiers.com/articles/a/Multidimensional-LSTM-Networks-to-Predict-Bitcoin-Price
我們將使用一個長短時記憶(LSTM)模型,它是深度學習中一個非常適合分析時間序列資料的特定模型(或者任何時間/空間/結構序列資料,例如電影、語句等)。
如果你真的想了解其中的基礎理論,那麼我推薦你閱讀這三篇文章:《理解LSTM網路》、《探究LSTM》、原始白皮書。出於私心,我主要是想吸引更多的非專業機器學習愛好者,所以我會盡量減少程式碼的篇幅。如果你想自己使用這些資料或者建立自己的模型,本篇文章同樣提供了Jupyter (Python) 筆記供參考。那麼,我們開始吧!
理解LSTM網路
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
探究LSTM
http://blog.echen.me/2017/05/30/exploring-lstms/
原始白皮書
http://www.bioinf.jku.at/publications/older/2604.pdf
Jupyter (Python) 筆記https://raw.githubusercontent.com/dashee87/blogScripts/master/Jupyter/2017-11-20-predicting-cryptocurrency-prices-with-deep-learning.ipynb
資料
在建立模型之前,我們需要獲取相應的資料。在Kaggle上有過去幾年比特幣詳細到每分鐘的價格資料(以及其他一些相關的特徵,可以在另外一篇部落格中看到)。但是如果採用這個時間顆粒度,其中的噪音可能會掩蓋真正的訊號,所以我們以天為顆粒度。
另外一篇部落格:
http://www.jakob-aungiers.com/articles/a/Multidimensional-LSTM-Networks-to-Predict-Bitcoin-Price
但這樣的話,我們會面臨資料不夠的問題(我們的資料量只能達到幾百行,而不是上千或者上百萬行)。在深度學習中,沒有模型能夠解決資料過少的問題。我也不想依賴於靜態檔案來建立模型,因為這會使未來注入新資料更新模型的流程變得很複雜。取而代之,我們來試試從網站和API上爬取資料。
因為我們需要在一個模型中使用多種加密貨幣,也許從同一個資料來源中爬取資料是個不錯方法。我們將使用網站coinmarketcap.com。
截至目前,我們只考慮比特幣和以太幣,但是用同樣的渠道獲取最近火爆的Altcoin相關資料也不難。在我們匯入資料之前,我們必須載入一些python包,它們會讓分析過程便捷很多。
import pandas as pd import time import seaborn as sns import matplotlib.pyplot as plt import datetime import numpy as np # get market info for bitcoin from the start of 2016 to the current day bitcoin_market_info = pd.read_html("https://coinmarketcap.com/currencies/bitcoin/historical-data/?start=20130428&end="+time.strftime("%Y%m%d"))[0] # convert the date string to the correct date format bitcoin_market_info = bitcoin_market_info.assign(Date=pd.to_datetime(bitcoin_market_info['Date'])) # when Volume is equal to '-' convert it to 0 bitcoin_market_info.loc[bitcoin_market_info['Volume']=="-",'Volume']=0 # convert to int bitcoin_market_info['Volume'] = bitcoin_market_info['Volume'].astype('int64') # look at the first few rows bitcoin_market_info.head()
解釋一下剛才發生了什麼,我們載入了一些python包,並匯入了在這個網站(連結見下)上看到的表格。經過簡單的資料清理,我們得到了上面的這張表。透過簡單地把URL地址(此處程式碼忽略)中的“bitcoin”換成“ethereum”,就可以相應的獲得以太幣的資料。
網站連結:
https://coinmarketcap.com/currencies/bitcoin/historical-data/
為了驗證資料的準確性,我們可以作出兩種貨幣的價格和成交量時間線圖。
圖注:上半部分-收盤價; 下半部分-成交量
圖注:上半部分-收盤價; 下半部分-成交量
訓練,測試以及隨機遊走
我們有了資料,現在可以開始建立模型了。在深度學習領域中,資料一般分為訓練資料和測試資料,用訓練資料集建立模型,然後用訓練樣本之外的測試資料集進行評估。
在時間序列模型中,一般我們用一段時間的資料訓練,然後使用另一段時間的資料測試。我比較隨意的把時間節點設為2017年6月1日(也就是說模型將用6月1日之前的資料訓練,用其後的資料進行評估)。
圖注:紫色線-訓練集; 藍色線-測試集
上半部分-比特幣價格($); 下半部分-以太幣價格($)
你可以觀察到,訓練集的資料大多處於貨幣價格較低的時候,因此,訓練資料的分佈也許並不能很好地代表測試資料的分佈,這將削弱模型推廣到樣本外資料的能力(你可以參照這個網站把資料變換成平穩的時間序列)。
網站連結:
https://dashee87.github.io/data%2520science/general/A-Road-Incident-Model-Analysis/
但是為什麼要讓不如人意的現實干擾我們的分析呢?在我們帶著深度人工智慧機器模型起飛前,討論一個更簡單的模型是很有必要的。最簡單的模型就是假設明天的價格相等於今天的價格,我們簡單粗暴地稱之為遞延模型。下面我們用數學語言來定義這個模型:
圖:簡單遞延模型
上半部分-比特幣價格($); 下半部分-以太幣價格($)
稍稍擴充一下這個簡單的模型,一般人們認為股票的價格是隨機漫步的,用數學模型表示為:
我們將從訓練資料集中獲取μ和σ的取值,然後在比特幣和以太幣的測試資料集上應用隨機漫步模型。
圖:單點隨機遊走模型(測試資料)
上半部分-比特幣價格($); 下半部分-以太幣價格($)
哈哈,看看這些預測線,除了一些彎折,它基本追隨了每個貨幣的實際收盤價格,它甚至預測了以太幣在六月中旬及八月下旬的漲勢(以及隨後的跌勢)。
那些僅僅只預測未來某個點的模型展現出來的準確性都很誤導人,因為誤差並不會延續到後續的預測中。無論上一個值有多大的誤差,由於每個時間點的輸入都是真實值,誤差都會被重置。
比特幣隨機漫步模型尤其具有欺騙性,因為y軸的範圍很大,讓這個預測曲線看上去非常平滑。
不幸的是,單點預測在時間序列模型的評估中十分常見(比如文章一、文章二)。更好的做法是用多點預測來評估它的準確性,用這種方法,之前的誤差不會被重置,而是被納入之後的預測中。越是預測能力差的模型受到的限制也越嚴重。數學模型如下:
文章一連結:
https://medium.com/@binsumi/neural-networks-and-bitcoin-d452bfd7757e
文章二連結:
https://blog.statsbot.co/time-series-prediction-using-recurrent-neural-networks-lstms-807fa6ca7f
我們基於所有的測試資料集得到了隨機漫步模型,並對收盤價格進行了預測。
圖:完全區間隨機模型
上半部分-比特幣價格($); 下半部分-以太幣價格($)
模型預測對隨機種子的選取極度敏感,我已經選擇了一個預測以太幣結果較好的完全區間隨機漫步模型。在相應的Jupyter筆記中,你可以透過互動介面嘗試下面動圖中的種子值,看看隨機漫步模型表現差的情況。
圖:單點漫步模型/完全區間隨機模型對比
縱座標-以太幣價格($)
需要注意的是,單點隨機漫步總是看上去非常準確,即使其背後沒有任何含義。希望你能夠帶著懷疑的眼光看任何宣稱能夠準確預測價格的文章。但也許我不需要擔心,加密貨幣的愛好者們似乎並不會被虛有其表的廣告語所誘惑。
長短期記憶(LSTM)
就像我之前所說的,如果你對LSTM的原理感興趣,可以閱讀:《理解LSTM網路》、《探究LSTM》、原始白皮書。(連結見上文)
幸運的是,我們不需要從頭開始建立網路(甚至不需要理解它),我們可以運用一些包含多種深度學習演算法標準實現的函式包(例如TensorFlow、 Keras,、PyTorch等等)。我將採用Keras,因為我發現對於非專業的愛好者來說,它是最直觀的。如果你對Keras不熟悉,那麼可以看看我之前推出的教程。
TensorFlow
https://www.tensorflow.org/get_started/get_started
Keras
https://keras.io/#keras-the-python-deep-learning-library
PyTorch
http://pytorch.org/
之前的教程
https://dashee87.github.io/data%2520science/deep%2520learning/python/another-keras-tutorial-for-neural-network-beginners/
model_data.head()
我建好了一個新資料表格model_data,移除了部分列(開盤價,當日的價最高價、當日最低價),重新安排了新的列:close_off_high代表當天收盤價格和最高價格的差值,-1和1的值分別代表收盤價格與每日最低或者最高價格相等。
volatility列就是最高價和最低價的差值除以開盤價。你可能還會注意到model_data資料集是按照時間由古至今排列的。實際上模型輸入不包括Date,所以我們不再需要這一列了。
我們的LSTM模型將會使用以往資料(比特幣和以太幣均有)來預測某一特定貨幣第二天的收盤價格。我們需要決定在模型中使用以往多少天的資料。
同樣的,我又隨意地決定選擇使用之前10天的資料,因為10是一個很好的整數。我們用連續10天的資料(稱之為視窗)建立了多個小資料表格,第一個視窗將由訓練資料集中的第0-9行組成(Python從0開始計數),下一個視窗由1-10行組成,以此類推。
選擇較小的視窗規模意味著我們能在模型中使用更多的視窗,不利之處在於這個模型沒有充足的資訊以預測複雜長期行為(如果能夠預測的話)。
深度學習並不喜歡變化範圍大的輸入值。觀察一下這些列,有些值在-1到1之間,其他的值則達到了上百萬。我們需要進行資料標準化,保證我們的輸入值的變化範圍是一致的。
一般-1到1的值是最理想的,off_high列和 volatility列的值是符合要求的,但是對於其他的列,我們需要把輸入值按照視窗的第一行值進行標準化。
表格展示了LSTM模型的輸入的一部分(實際上會有幾百個相似的表格)。我們對一些列進行了標準化處理,使它們在第一個時間點的值為0,以便預測相較此時間點而言價格的變動。
現在我們準備構建LSTM模型,實際上使用Keras來構建會非常簡單,你只需將幾個模組堆疊在一起。
更好的解釋請戳這裡:
https://dashee87.github.io/data%2520science/deep%2520learning/python/another-keras-tutorial-for-neural-network-beginners/
程式碼如下:
# import the relevant Keras modules from keras.models import Sequential from keras.layers import Activation, Dense from keras.layers import LSTM from keras.layers import Dropout def build_model(inputs, output_size, neurons, activ_func = "linear", dropout =0.25, loss="mae", optimizer="adam"): model = Sequential() model.add(LSTM(neurons, input_shape=(inputs.shape[1], inputs.shape[2]))) model.add(Dropout(dropout)) model.add(Dense(units=output_size)) model.add(Activation(activ_func)) model.compile(loss=loss, optimizer=optimizer) return model
不出所料,build_model 函式建立了一個空模型,名字為model(即這行程式碼model= Sequential),LSTM層已加在模型中,大小與輸入相匹配(n * m的表格,n和m分別代表時間點/行和列)。
函式也包含了更通用的神經網路特徵,例如 dropout和activation functions。現在我們只需確定放置到LSTM層中的神經元個數(我選擇了20個以便保證合理的執行時間)和建立模型的訓練資料。
程式碼如下:
# random seed for reproducibility np.random.seed(202) # initialise model architecture eth_model = build_model(LSTM_training_inputs, output_size=1, neurons = 20) # model output is next price normalised to 10th previous closing price LSTM_training_outputs = (training_set['eth_Close'][window_len:].values/training_set['eth_Close'][:-window_len].values)-1 # train model on data # note: eth_history contains information on the training error per epoch eth_history = eth_model.fit(LSTM_training_inputs, LSTM_training_outputs, epochs=50, batch_size=1, verbose=2, shuffle=True) #eth_preds = np.loadtxt('eth_preds.txt') 結果: Epoch 50/50 6s - loss: 0.0625
我們建好了一個LSTM模型,可以預測以太幣明日的收盤價。讓我們來看看模型表現如何。首先檢驗模型在訓練集上的表現情況(2017年6月前的資料)。程式碼下面的數字是對訓練集進行50次訓練迭代(或週期)後,模型的平均絕對誤差(mae)。我們可將模型的輸出結果視為每日的收盤價,而不是相對的變化。
訓練集:單時間點預測
藍色線-實際價格;綠色線-預測價格
縱座標:以太幣價格($)
平均絕對誤差:0.0583
正如我們所期待的,準確性看起來很高。訓練過程中,模型可以瞭解其誤差來源並相應地做出調整。
實際上,訓練誤差達到幾乎為零不會很難,我們只需用上幾百個神經元並且訓練數千個週期(這就是過度擬合,實際上是在預測噪音,我在build_model 函式中加入了Dropout() , 可以為我們相對小的模型降低過度擬合的風險)。
我們應該更關注模型在測試集上的表現,因為可以看到模型處理全新資料的表現。
測試集:單時間點預測
藍色線-實際價格;綠色線-預測價格
縱座標:以太幣價格($)
平均絕對誤差:0.0531
撇開單點預測誤導性的侷限,LSTM模型似乎在測試集中表現良好。但它最明顯的缺陷是以太幣的價格在暴增後(例如六月中旬和十月)不可避免的下降,模型無法探測出來。
實際上這個問題一直存在,只是在這些劇烈變化的時間點更加明顯。預測價格曲線幾乎是實際價格曲線向未來平移一天的結果(例如七月中旬的下跌)。此外,模型似乎整體高估了以太幣的未來價值(我們也是~),預測曲線總是高於實際曲線。
我懷疑這是由於訓練集所屬的時間範圍內,以太幣的價格以天文數字增長,因此模型推斷這種趨勢仍會持續(我們也是~)。我們也建立了一個相似的LSTM模型用來預測比特幣,測試集的預測圖如下
完整程式碼的Jupyter notebook連結:
https://github.com/dashee87/blogScripts/blob/master/Jupyter/2017-11-20-predicting-cryptocurrency-prices-with-deep-learning.ipynb
測試集:單時間點預測
藍色線-實際價格;綠色線-預測價格
縱座標:比特幣價格($)
平均絕對誤差:0.0392
正如我之前所說的,單點預測會有欺騙作用。如果仔細觀察你會注意到,預測值通常會反映出先前的值(例如十月)。深度學習模型LSTM已經部分推匯出了p元自迴歸模型(autoregression model,AR),未來的值僅僅是先前p個值的加權和。AR模型的數學公式如下:
好的方面是,AR模型常運用於時間序列中 ,因此LSTM模型似乎有了合理的用武之地。壞訊息是,這是對LSTM能力的浪費,我們可以建立更加簡單的AR模型,花費時間更少,可能會得到相似的結果。
測試集:5個時間點的預測
藍色連續線:實際價格
其他顏色線:預測價格
上半部分:比特幣價格($);下半部分:以太幣價格($)
這個預測結果顯然沒有單點預測的結果吸引眼球。然而,我很開心這個模型輸出有些微妙的狀況(例如以太幣的第二條線);它並沒有簡單的預測價格會朝著一個方向統一移動,這是個好現象。
回過頭來看一下單點預測,深度機器人工神經模型執行的還可以,但隨機漫步模型也不差。和隨機漫步模型一樣,LSTM模型對隨機種子的選擇也很敏感(模型的權重最初是隨機分配的)。
因此,如果我們想去比較這兩個模型,就需要多次執行(大約25次)之後獲取模型誤差的估計值,測試集中實際和預測的收盤價之間差值的絕對值記為誤差。
左圖:比特幣測試集(執行25次)縱座標:平均絕對誤差橫座標:LSTM模型,隨機遊走模型
右圖:以太幣測試集(執行25次)縱座標:平均絕對誤差橫座標:LSTM模型,隨機遊走模型
也許AI還是值得廣而告之的,上圖顯示了對每個模型進行25次初始化後測試集的誤差。LSTM模型在比特幣和以太幣上價格的平均誤差分別是0.04和0.05,這個結果完勝隨機漫步模型。
戰勝隨機漫步模型是很低的標準,將LSTM與更合適的時間序列模型做比較會更有趣(比如加權平均,AR,ARIMA 或Facebook的 Prophet algorithm)。另一方面,我相信改進LSTM模型並不難(嘗試加入更多層和/或神經元,改變批次的大小,學習率等)。
也就是說,希望你已經發現了我對應用深度學習預測加密貨幣的價格變化的疑慮。這是因為我們忽視了最好的框架:人類智慧。顯然,預測加密貨幣的完美模型*應是:
(譯者注:如果在時過境遷之後,加密貨幣的價格接近月球的高度,那麼所有不在OmiseGo區塊鏈中的加密貨幣會一直升值)
本篇文章不涉及財務建議,也不應該做財務建議使用。儘管加密貨幣的投資在長時間的範圍看肯定會增值,但它們也可能會貶值。
總結
我們收集了一些加密貨幣資料,並將其輸入到酷炫的深度智慧機器學習LSTM模型中,不幸的是,預測值與先前的輸入值並無太大差別。那麼問題來了,如何使模型學習更復雜的行為?
改變損失函式:平均絕對誤差(MAE)使模型中規中矩,得不到“出格”的結果。例如,如果採用均方誤差(MSE),LSTM模型會被迫更加重視檢測高峰值/低谷值。 許多定製交易的損失函式也會使模型朝著沒那麼保守的方向演化。
限制保守的AR類模型:這會激勵深度學習演算法來探索更具風險/有趣的模型。不過說起來容易做起來難啊。
獲取更多且/或更好的資料:如果過去的價格已經足以預測較為準確的未來價格,那麼我們需要引入其他具有相當預測能力的特徵。這樣LSTM模型不會過度依賴過去的價格,也許會解鎖更復雜的行為,這可能是最可靠同時也是最難完成的解決方案。
如果以上是積極的一面,那接下來的負面訊息是,有可能加密貨幣的價格變化模式根本找不出來;沒有任何模型(無論多麼深)可以將訊號與噪音分開(這與利用深度學習預測地震類似),即使出現了某種模式也會很快消失 。
想想看2016年和2017年末狂熱的比特幣差別多麼大,任何建立在2016年資料的模型肯定難以復刻2017年空前的變化。以上討論就是在建議你,不妨節省些時間,還是堅持研究AR模型吧。
但我相信他們最終會為深度學習找到用武之地,與此同時,你可以透過下載 Python 程式碼建立自己的LSTM模型。
Python 程式碼:
https://github.com/dashee87/blogScripts/blob/master/Jupyter/2017-11-20-predicting-cryptocurrency-prices-with-deep-learning.ipynb