网站开发技术文档 范本,wordpress怎么改后台,布朗新风 中企动力提供网站建设,wordpress菜单的作用https://www.toutiao.com/i6644852565341110791/ 利用深度学习来预测股票价格变动#xff08;长文#xff0c;建议收藏#xff09; 原创 不靠谱的猫 2019-01-10 21:01:39完整架构概述 在这篇文章中#xff0c;我将创建一个预测股票价格变动的完整过程。我们将使用生成对抗网…https://www.toutiao.com/i6644852565341110791/ 利用深度学习来预测股票价格变动长文建议收藏 原创 不靠谱的猫 2019-01-10 21:01:39 完整架构概述 在这篇文章中我将创建一个预测股票价格变动的完整过程。我们将使用生成对抗网络GAN与LSTM一种循环神经网络作为生成器使用卷积神经网络CNN作为鉴别器。我们使用LSTM的原因很明显我们正在尝试预测时间序列数据。为什么我们使用GAN特别是卷积神经网络CNN作为鉴别器呢这是一个很好的问题稍后会有特别的部分。 当然我们将详细介绍每个步骤但最困难的部分是GAN成功训练GAN的非常棘手的部分是获得正确的超参数集。出于这个原因我们将使用贝叶斯优化还有高斯过程和深度强化学习DRL来决定何时以及如何改变GAN的超参数。在创建强化学习时我将使用该领域的最新进展例如Rainbow和PPO。 我们将使用许多不同类型的输入数据。除了股票的历史交易数据和技术指标我们将使用NLP的最新进展使用“BERT对NLP进行迁移学习来创建情感分析作为基本面分析的来源 用于提取整体趋势方向的傅里叶变换用于识别其他高级特征的栈式自动编码器用于查找相关资产的特征投资组合差分整合移动平均自回归模型ARIMA对于股票函数近似以便捕获尽可能多的关于股票的信息模式依赖关系等。我们都知道数据越多越好。预测股价走势是一项极其复杂的任务所以我们对股票(从不同的角度)了解得越多我们的系统就会越好。 为了创建所有神经网络我们将使用MXNet和它的高级API - Gluon并在多个GPU上训练它们。 注:尽管我试图深入探讨数学和几乎所有算法和技术背后的机制但本文并没有明确地解释机器/深度学习或股票市场是如何运作的。其目的是展示我们如何使用不同的技术和算法来准确预测股票价格的变动并给出每一步使用每种技术的原因和有用性背后的理论基础。 1.简介 准确预测股票市场是一项复杂的任务因为有数百万种情况会影响它。因此我们需要能够尽可能多地捕获这些前置条件。我们还需要做出几个重要的假设1市场不是100随机2历史重复3市场遵循人们的理性行为4市场是“ 完美的 ”。 我们将尝试预测高盛(NYSE: GS)的价格走势。为此我们将使用2010年1月1日至2018年12月31日的每日收盘价(七年数据用于训练两年数据用于验证)。我们将交替使用“高盛”和“GS”这两个术语。 2.数据 我们需要尽可能多地合并信息从不同方面和角度描绘股票。我们将使用每日数据1585天来训练各种算法(我们有70%的数据)并预测接下来的680天(测试数据)然后我们将把预测结果与测试数据进行比较。每种类型的数据(我们将其称为特征将在后面的章节中进行更详细的解释但是作为一个高层次的概述我们将使用的特征是: 相关资产 - 这些是其他资产任何类型不一定是股票如商品外汇指数甚至固定收益证券。像高盛这样的大公司显然不会“生活”在一个孤立的世界中 - 它依赖于许多外部因素并与之相互作用包括竞争对手客户全球经济地缘政治形势财政和货币政策获得资金等。详情将在后面列出。技术指标 - 很多投资者都遵循技术指标。我们将最受欢迎的指标作为独立特征。如 - 7和21天移动平均线指数移动平均线momentum布林线MACD。一个非常重要的特征表明股票可能上涨或下跌。基本面分析有两个特征:1)使用10-K和10-Q报告分析公司业绩分析ROE和P/E等(我们不会使用这个);我们将为高盛(Goldman Sachs)和阅读每日新闻提取总情绪是否对高盛(Goldman Sachs)在那一天是正面的还是负面的(如得分从0到1)。由于许多投资者会仔细阅读新闻并根据新闻(当然是部分依据新闻)做出投资决策因此如果高盛(Goldman Sachs)今天的消息非常正面那么该股明天将大幅上涨的可能性在一定程度上是很高的。关键的一点是我们将在以后对每个特征(包括这个特征)执行特征重要性(意思是它对GS波动的指示性)并决定是否使用它。我们将使用BERT - 谷歌最近公布的NLP方法用于情感分类股票新闻情绪提取的迁移学习。傅里叶变换 - 除了每日收盘价我们还将创建傅里叶变换以概括多个长期和短期趋势。使用这些变换我们将消除大量噪声随机游走并创建真实股票移动的近似值。趋势近似可以帮助LSTM网络更准确地选择其预测趋势。自回归整合移动平均线ARIMA - 这是预测时间序列数据未来值的最流行的技术之一在pre-neural网络时代。让我们添加它看看它是否是一个重要的预测特征。栈式自动编码器 - 上述大部分特征(基础分析、技术分析等)都是人们经过几十年的研究发现的。也许有一些隐藏的相关性人们无法理解因为有大量的数据点、事件、资产、图表等。通过栈式自动编码器(神经网络的类型)我们可以利用计算机的力量可能会发现影响股票走势的新类型的特征。即使我们无法理解人类语言中的这些特征我们也将在GAN中使用它们。期权定价中异常检测的深度无监督学习。我们将再使用一项功能 - 每天我们都会为高盛股票增加90天看涨期权的价格。期权定价本身结合了大量数据。期权合约的价格取决于股票的未来值分析师也试图预测价格以便为看涨期权提供最准确的价格。使用深度无监督机器学习Self-organized Maps我们将尝试发现每天定价中的异常情况。异常(例如价格的剧烈变化)可能表明一个事件可能对LSTM了解整个股票模式有用。接下来有这么多特征我们需要执行几个重要步骤: 对数据的“质量”进行统计检查。如果我们创建的数据有缺陷那么无论我们的算法多么复杂结果都不会是正确的。检查包括确保数据不受异方差、多重共线性或序列相关性的影响。创建特征的重要性。如果一个特征(如另一只股票或一个技术指标)对我们想预测的股票没有解释力那么我们就没有必要在神经网络的训练中使用它。我们将使用XGBoost(eXtreme Gradient boost)一种boosted 树回归算法。作为数据准备的最后一步我们还将使用主成分分析PCA创建Eigen投资组合以减少自动编码器创建的特征的维数。 from utils import *import timeimport numpy as npfrom mxnet import nd, autograd, gluonfrom mxnet.gluon import nn, rnnimport mxnet as mximport datetimeimport seaborn as snsimport matplotlib.pyplot as plt%matplotlib inlinefrom sklearn.decomposition import PCAimport mathfrom sklearn.preprocessing import MinMaxScalerfrom sklearn.metrics import mean_squared_errorfrom sklearn.preprocessing import StandardScalerimport xgboost as xgbfrom sklearn.metrics import accuracy_scoreimport warningswarnings.filterwarnings(ignore)context mx.cpu(); model_ctxmx.cpu()mx.random.seed(1719)def parser(x): return datetime.datetime.strptime(x,%Y-%m-%d)dataset_ex_df pd.read_csv(data/panel_data_close.csv, header0, parse_dates[0], date_parserparser)dataset_ex_df[[Date, GS]].head(3) print(There are {} number of days in the dataset..format(dataset_ex_df.shape[0])) output There are 2265 number of days in the dataset. 让我们想象一下过去九年的股票。垂直虚线表示训练和测试数据之间的分离。 plt.figure(figsize(14, 5), dpi100)plt.plot(dataset_ex_df[Date], dataset_ex_df[GS], labelGoldman Sachs stock)plt.vlines(datetime.date(2016,4, 20), 0, 270, linestyles--, colorsgray, labelTrain/Test data cut-off)plt.xlabel(Date)plt.ylabel(USD)plt.title(Figure 2: Goldman Sachs stock price)plt.legend()plt.show() num_training_days int(dataset_ex_df.shape[0]*.7)print(Number of training days: {}. Number of test days: {}..format(num_training_days, dataset_ex_df.shape[0]-num_training_days)) Number of training days: 1585. Number of test days: 680. 2.1、相关资产 如前所述我们将使用其他资产作为特征而不仅仅是GS。 那么还有哪些资产会影响高盛的股价走势呢?对公司、业务线、竞争环境、依赖关系、供应商和客户类型等的良好理解对于选择正确的相关资产集非常重要: 首先是类似于GS的公司。我们将把摩根大通(JPMorgan Chase)和摩根士丹利(Morgan Stanley)等公司加入数据集。作为一家投资银行高盛Goldman Sachs依赖于全球经济。经济不景气或不稳定意味着没有并购或IPO也可能是有限的自营交易收益。这就是为什么我们将包括全球经济指数。此外我们将包括LIBOR美元和英镑计价利率因为分析师可能会考虑经济的冲击来设定这些利率以及其他FI证券。每日波动率指数VIX - 由于前一点所述的原因。综合指数 - 例如纳斯达克和纽约证券交易所(美国)、FTSE100指数(英国)、Nikkei225指数(日本)、恒生指数和BSE Sensex指数(亚太)。货币 - 全球贸易多次反映货币如何变动因此我们将使用一篮子货币如美元兑日元英镑兑美元等作为特征。总的来说我们在数据集中有72个其他资产 - 每个资产的每日价格。 2.2、技术指标 我们已经讨论了什么是技术指标以及为什么使用它们现在让我们直接跳到Python代码。我们将只为GS创建技术指标。 def get_technical_indicators(dataset): # Create 7 and 21 days Moving Average dataset[ma7] dataset[price].rolling(window7).mean() dataset[ma21] dataset[price].rolling(window21).mean() # Create MACD dataset[26ema] pd.ewma(dataset[price], span26) dataset[12ema] pd.ewma(dataset[price], span12) dataset[MACD] (dataset[12ema]-dataset[26ema]) # Create Bollinger Bands dataset[20sd] pd.stats.moments.rolling_std(dataset[price],20) dataset[upper_band] dataset[ma21] (dataset[20sd]*2) dataset[lower_band] dataset[ma21] - (dataset[20sd]*2) # Create Exponential moving average dataset[ema] dataset[price].ewm(com0.5).mean() # Create Momentum dataset[momentum] dataset[price]-1 return datasetdataset_TI_df get_technical_indicators(dataset_ex_df[[GS]])dataset_TI_df.head() 所以我们有每个交易日的技术指标(包括MACD、Bollinger bands等)。我们总共有12项技术指标。 让我们想象一下这些指标的最后400天。 def plot_technical_indicators(dataset, last_days): plt.figure(figsize(16, 10), dpi100) shape_0 dataset.shape[0] xmacd_ shape_0-last_days dataset dataset.iloc[-last_days:, :] x_ range(3, dataset.shape[0]) x_ list(dataset.index) # Plot first subplot plt.subplot(2, 1, 1) plt.plot(dataset[ma7],labelMA 7, colorg,linestyle--) plt.plot(dataset[price],labelClosing Price, colorb) plt.plot(dataset[ma21],labelMA 21, colorr,linestyle--) plt.plot(dataset[upper_band],labelUpper Band, colorc) plt.plot(dataset[lower_band],labelLower Band, colorc) plt.fill_between(x_, dataset[lower_band], dataset[upper_band], alpha0.35) plt.title(Technical indicators for Goldman Sachs - last {} days..format(last_days)) plt.ylabel(USD) plt.legend() # Plot second subplot plt.subplot(2, 1, 2) plt.title(MACD) plt.plot(dataset[MACD],labelMACD, linestyle-.) plt.hlines(15, xmacd_, shape_0, colorsg, linestyles--) plt.hlines(-15, xmacd_, shape_0, colorsg, linestyles--) plt.plot(dataset[log_momentum],labelMomentum, colorb,linestyle-) plt.legend() plt.show()plot_technical_indicators(dataset_TI_df, 400) 2.3、基本面分析 对于基本面分析我们将对所有关于GS的每日新闻进行情感分析。最后使用sigmoid结果将在0和1之间。分数越接近0 - 新闻越负面接近1表示正面情感。对于每一天我们将创建平均每日得分作为0到1之间的数字并将其添加为特征。 2.3.1、 BERT 为了将新闻分类为正面或负面或中性我们将使用BERT这是一种预训练的语言表示。 已经在MXNet / Gluon中提供预训练的BERT模型。我们只需要实例化它们并添加两个任意数量Dense层softmax - 得分从0到1。 # just import bertimport bert 2.4、傅立叶变换用于趋势分析 傅立叶变换采用函数并创建一系列正弦波具有不同的幅度和帧。组合时这些正弦波接近原始函数。从数学上讲变换看起来像这样 我们将使用傅里叶变换来提取GS股票的整体和局部趋势并对其进行降噪。我们来看看它是如何工作的。 data_FT dataset_ex_df[[Date, GS]]close_fft np.fft.fft(np.asarray(data_FT[GS].tolist()))fft_df pd.DataFrame({fft:close_fft})fft_df[absolute] fft_df[fft].apply(lambda x: np.abs(x))fft_df[angle] fft_df[fft].apply(lambda x: np.angle(x))plt.figure(figsize(14, 7), dpi100)fft_list np.asarray(fft_df[fft].tolist())for num_ in [3, 6, 9, 100]: fft_list_m10 np.copy(fft_list); fft_list_m10[num_:-num_]0 plt.plot(np.fft.ifft(fft_list_m10), labelFourier transform with {} components.format(num_))plt.plot(data_FT[GS], labelReal)plt.xlabel(Days)plt.ylabel(USD)plt.title(Figure 3: Goldman Sachs (close) stock prices Fourier transforms)plt.legend()plt.show() 正如您在图3中看到的我们使用傅里叶变换的成分越多逼近函数越接近实际股票价格100个成分变换几乎与原始函数相同 - 红色和紫色线几乎重叠。我们使用傅立叶变换来提取长期和短期趋势因此我们将使用具有3,6和9个成分的变换。您可以推断出具有3个成分的转换是长期趋势。 用于去噪数据的另一种技术是调用小波。小波和傅里叶变换给出了类似的结果因此我们只使用傅里叶变换。 from collections import dequeitems deque(np.asarray(fft_df[absolute].tolist()))items.rotate(int(np.floor(len(fft_df)/2)))plt.figure(figsize(10, 7), dpi80)plt.stem(items)plt.title(Figure 4: Components of Fourier transforms)plt.show() 2.5、ARIMA作为一项特征值 ARIMA是一种预测时间序列数据的技术。我们将展示如何使用它虽然ARIMA不能作为我们的最终预测但我们将使用它作为一种技术来稍微降低噪声并(可能)提取一些新的模式或特征。 from statsmodels.tsa.arima_model import ARIMAfrom pandas import DataFramefrom pandas import datetimeseries data_FT[GS]model ARIMA(series, order(5, 1, 0))model_fit model.fit(disp0)print(model_fit.summary()) from pandas.tools.plotting import autocorrelation_plotautocorrelation_plot(series)plt.figure(figsize(10, 7), dpi80)plt.show() from pandas import read_csvfrom pandas import datetimefrom statsmodels.tsa.arima_model import ARIMAfrom sklearn.metrics import mean_squared_errorX series.valuessize int(len(X) * 0.66)train, test X[0:size], X[size:len(X)]history [x for x in train]predictions list()for t in range(len(test)): model ARIMA(history, order(5,1,0)) model_fit model.fit(disp0) output model_fit.forecast() yhat output[0] predictions.append(yhat) obs test[t] history.append(obs)error mean_squared_error(test, predictions)print(Test MSE: %.3f % error) Test MSE: 10.151 # Plot the predicted (from ARIMA) and real pricesplt.figure(figsize(12, 6), dpi100)plt.plot(test, labelReal)plt.plot(predictions, colorred, labelPredicted)plt.xlabel(Days)plt.ylabel(USD)plt.title(Figure 5: ARIMA model on GS stock)plt.legend()plt.show() 从图5中可以看出ARIMA给出了一个非常接近实际股价的结果。我们将通过ARIMA使用预测价格作为LSTM的输入特征因为正如我们前面提到的我们希望尽可能多地捕获关于高盛的特征和模式。我们测试MSE(均方误差)为10.151这本身并不是一个坏结果(考虑到我们有很多测试数据)但是我们仍然只将其作为LSTM中的一个特征。 2.6、统计检查 确保数据具有良好的质量对于机器学习模型非常重要。为了确保我们的数据拟合我们将执行几个简单的检查以确保我们实现和观察到的结果是真实的而不是因为底层数据分布存在基本错误而受到损害。 2.6.1、异方差性多重共线性序列相关性 条件异方差发生在误差项(通过回归得到的预测值与实际值之间的差)依赖于数据时——例如误差项随着数据点(沿x轴)的增长而增长。多重共线性是指误差项也称为残差相互依赖的时间。序列相关性是指一个数据(特征)是另一个特征的公式(或完全不相关)。我们不会在这里进入代码因为它很简单我们的重点更多地放在深度学习部分但数据是定性的。 2.7、特征工程 print(Total dataset has {} samples, and {} features..format(dataset_total_df.shape[0], dataset_total_df.shape[1])) Total dataset has 2265 samples, and 112 features. 因此在添加了所有类型的数据(相关资产、技术指标、基础分析、傅立叶和Arima)之后我们在这2,265天中总共有112个特征(如前所述训练数据只有1,585天)。 我们还将从自动编码器生成更多特征。 2.7.1、XGBoost的重要性 有这么多的特征我们必须要考虑它们是否真的代表了走势。例如我们在机器学习数据集中包含了以美元计价的LIBOR利率因为我们认为LIBOR的变化可能表明经济的变化而经济的变化又可能表明GS的股票行为的变化。但我们需要测试。测试特征重要性的方法有很多但是我们将使用XGBoost因为它在分类和回归问题中都给出了最好的结果之一。 由于特征数据集非常大出于演示目的我们将仅使用技术指标。在真实特征重要性测试期间所有选定的特征都证明有些重要因此我们在训练GAN时不会排除任何内容。 def get_feature_importance_data(data_income): data data_income.copy() y data[price] X data.iloc[:, 1:] train_samples int(X.shape[0] * 0.65) X_train X.iloc[:train_samples] X_test X.iloc[train_samples:] y_train y.iloc[:train_samples] y_test y.iloc[train_samples:] return (X_train, y_train), (X_test, y_test)# Get training and test data(X_train_FI, y_train_FI), (X_test_FI, y_test_FI) get_feature_importance_data(dataset_TI_df)regressor xgb.XGBRegressor(gamma0.0,n_estimators150,base_score0.7,colsample_bytree1,learning_rate0.05)xgbModel regressor.fit(X_train_FI,y_train_FI, eval_set [(X_train_FI, y_train_FI), (X_test_FI, y_test_FI)], verboseFalse)eval_result regressor.evals_result()training_rounds range(len(eval_result[validation_0][rmse])) 让我们绘制训练和验证误差的曲线图以便观察训练和检查过拟合(欠拟合)。 plt.scatter(xtraining_rounds,yeval_result[validation_0][rmse],labelTraining Error)plt.scatter(xtraining_rounds,yeval_result[validation_1][rmse],labelValidation Error)plt.xlabel(Iterations)plt.ylabel(RMSE)plt.title(Training Vs Validation Error)plt.legend()plt.show() fig plt.figure(figsize(8,8))plt.xticks(rotationvertical)plt.bar([i for i in range(len(xgbModel.feature_importances_))], xgbModel.feature_importances_.tolist(), tick_labelX_test_FI.columns)plt.title(Figure 6: Feature importance of the technical indicators.)plt.show() 毫不奇怪MA7MACD和BB是其中的重要特征。 我遵循相同的逻辑来对整个数据集执行特征重要性 - 与仅少数几个特征相比训练花费的时间更长结果更难以阅读。 2.8、使用Stacked Autoencoders提取高级特征 在我们进入自动编码器之前我们将探索激活函数。 2.8.1、激活功函数- GELU高斯误差 最近提出了GELU - Gaussian Error Linear Unites - link。在论文中作者展示了使用ReLU作为激活的使用GELU的神经网络优于网络的几个实例。gelu也用于BERT我们用于新闻情绪分析的NLP方法。 我们将使用GELU作为自动编码器。 注:下面的单元格展示了GELU数学背后的逻辑。它不是作为激活函数的实际实现。我必须在MXNet中实现GELU。如果您按照代码将act_typerelu更改为act_typegelu那么它将不起作用除非您更改MXNet的实现。对整个项目发出pull请求以访问GELU的MXNet实现。 def gelu(x): return 0.5 * x * (1 math.tanh(math.sqrt(2 / math.pi) * (x 0.044715 * math.pow(x, 3))))def relu(x): return max(x, 0)def lrelu(x): return max(0.01*x, x) 让我们来看看GELU、ReLU和LeakyReLU(最后一个主要用于GAN)。 plt.figure(figsize(15, 5))plt.subplots_adjust(leftNone, bottomNone, rightNone, topNone, wspace.5, hspaceNone)ranges_ (-10, 3, .25)plt.subplot(1, 2, 1)plt.plot([i for i in np.arange(*ranges_)], [relu(i) for i in np.arange(*ranges_)], labelReLU, marker.)plt.plot([i for i in np.arange(*ranges_)], [gelu(i) for i in np.arange(*ranges_)], labelGELU)plt.hlines(0, -10, 3, colorsgray, linestyles--, label0)plt.title(Figure 7: GELU as an activation function for autoencoders)plt.ylabel(f(x) for GELU and ReLU)plt.xlabel(x)plt.legend()plt.subplot(1, 2, 2)plt.plot([i for i in np.arange(*ranges_)], [lrelu(i) for i in np.arange(*ranges_)], labelLeaky ReLU)plt.hlines(0, -10, 3, colorsgray, linestyles--, label0)plt.ylabel(f(x) for Leaky ReLU)plt.xlabel(x)plt.title(Figure 8: LeakyReLU)plt.legend()plt.show() 好的回到自动编码器如下所示图像只是原理图它不代表实际的层数units等 注意通常在自动编码器中编码器的数量解码器的数量。但是我们希望提取更高级别的特征而不是创建相同的输入因此我们可以跳过解码器中的最后一层。我们实现了这一点在训练期间创建了具有相同层数的编码器和解码器。 batch_size 64n_batches VAE_data.shape[0]/batch_sizeVAE_data VAE_data.valuestrain_iter mx.io.NDArrayIter(data{data: VAE_data[:num_training_days,:-1]}, label{label: VAE_data[:num_training_days, -1]}, batch_size batch_size)test_iter mx.io.NDArrayIter(data{data: VAE_data[num_training_days:,:-1]}, label{label: VAE_data[num_training_days:,-1]}, batch_size batch_size)model_ctx mx.cpu()class VAE(gluon.HybridBlock): def __init__(self, n_hidden400, n_latent2, n_layers1, n_output784, batch_size100, act_typerelu, **kwargs): self.soft_zero 1e-10 self.n_latent n_latent self.batch_size batch_size self.output None self.mu None super(VAE, self).__init__(**kwargs) with self.name_scope(): self.encoder nn.HybridSequential(prefixencoder) for i in range(n_layers): self.encoder.add(nn.Dense(n_hidden, activationact_type)) self.encoder.add(nn.Dense(n_latent*2, activationNone)) self.decoder nn.HybridSequential(prefixdecoder) for i in range(n_layers): self.decoder.add(nn.Dense(n_hidden, activationact_type)) self.decoder.add(nn.Dense(n_output, activationsigmoid)) def hybrid_forward(self, F, x): h self.encoder(x) #print(h) mu_lv F.split(h, axis1, num_outputs2) mu mu_lv[0] lv mu_lv[1] self.mu mu eps F.random_normal(loc0, scale1, shape(self.batch_size, self.n_latent), ctxmodel_ctx) z mu F.exp(0.5*lv)*eps y self.decoder(z) self.output y KL 0.5*F.sum(1lv-mu*mu-F.exp(lv),axis1) logloss F.sum(x*F.log(yself.soft_zero) (1-x)*F.log(1-yself.soft_zero), axis1) loss -logloss-KL return lossn_hidden400 # neurons in each layern_latent2 n_layers3 # num of dense layers in encoder and decoder respectivelyn_outputVAE_data.shape[1]-1 net VAE(n_hiddenn_hidden, n_latentn_latent, n_layersn_layers, n_outputn_output, batch_sizebatch_size, act_typegelu)net.collect_params().initialize(mx.init.Xavier(), ctxmx.cpu())net.hybridize()trainer gluon.Trainer(net.collect_params(), adam, {learning_rate: .01})print(net) 所以我们在编码器和解码器中都有3层(每个层有400个神经元)。 n_epoch 150print_period n_epoch // 10start time.time()training_loss []validation_loss []for epoch in range(n_epoch): epoch_loss 0 epoch_val_loss 0 train_iter.reset() test_iter.reset() n_batch_train 0 for batch in train_iter: n_batch_train 1 data batch.data[0].as_in_context(mx.cpu()) with autograd.record(): loss net(data) loss.backward() trainer.step(data.shape[0]) epoch_loss nd.mean(loss).asscalar() n_batch_val 0 for batch in test_iter: n_batch_val 1 data batch.data[0].as_in_context(mx.cpu()) loss net(data) epoch_val_loss nd.mean(loss).asscalar() epoch_loss / n_batch_train epoch_val_loss / n_batch_val training_loss.append(epoch_loss) validation_loss.append(epoch_val_loss) if epoch % max(print_period, 1) 0: print(Epoch {}, Training loss {:.2f}, Validation loss {:.2f}. format(epoch, epoch_loss, epoch_val_loss))end time.time()print(Training completed in {} seconds..format(int(end-start))) Training completed in 62 seconds. dataset_total_df[Date] dataset_ex_df[Date]vae_added_df mx.nd.array(dataset_total_df.iloc[:, :-1].values)print(The shape of the newly created (from the autoencoder) features is {}..format(vae_added_df.shape)) The shape of the newly created (from the autoencoder) features is (2265, 112). 我们从自动编码器中创建了112个更多特征。由于我们只想拥有高级特征整体模式我们将使用主成分分析PCA在新创建的112个特征上创建特征投资组合。这将减少数据的维度列数。Eigen组合的描述能力将与原始的112个特征相同。 注意再一次这纯粹是实验性的。我并非100确定描述的逻辑将成立。作为人工智能和深度学习的其他一切这是艺术和需要实验。 2.8.2、主成分分析的特征组合 # We want the PCA to create the new components to explain 80% of the variancepca PCA(n_components.8)x_pca StandardScaler().fit_transform(vae_added_df)principalComponents pca.fit_transform(x_pca)principalComponents.n_components_ 84 所以为了解释80%的方差我们需要84个(112个)特征。这仍然是一个很大的问题。因此目前我们不包括自动编码器创建的特征。我将致力于创建autoencoder架构在该架构中我们从中间层(而不是最后一层)获得输出并使用30个神经元将其连接到另一个Dense 层。因此我们将1)只提取更高级别的特征2)提供更少的列数。 3.生成性对抗网络GAN GAN的体系结构 GAN如何运作 如前所述本文的目的不是详细解释深度学习背后的数学而是展示其应用。当然在我看来彻底和非常坚实的理解从基础到最小的细节是极其必要的。因此我们将尝试平衡并给出一个关于GAN如何工作的高级概述以便读者充分理解使用GAN预测股价走势背后的原理。 GAN网络由两个模型组成 - GeneratorG和Discriminator D。训练GAN的步骤如下 使用随机数据噪声表示为zGenerator试图“生成”与真实数据无法区分或非常接近的数据。它的目的是学习真实数据的分布。将随机、真实或生成的数据拟合到Discriminator 中Discriminator 作为分类器试图了解数据是来自Generator还是真实数据。然后G和D的损失被组合并通过Generator传播回来。因此Generator的损失取决于Generator和Discriminator 。这是帮助Generator了解真实数据分布的步骤。如果Generator在生成真实数据具有相同分布方面做得不好则Discriminator的工作将很容易区分生成与实际数据集。因此Discriminator 的损失将非常小。小的Discriminator 损失将导致更大的Generator损耗参见下面的等式 LDG。这使得创建Discriminator 有点棘手因为Discriminator 太好会导致巨大的Generator损失使得Generator无法学习。该过程一直持续到Discriminator无法再将生成数据与实际数据区分开来。当组合在一起时D和G作为一种playing minmax游戏Generator试图欺骗Discriminator使其增加假例子的概率即最小化z〜pzz[log1-DG z]。Discriminator想要通过最大化x~prx[logDx]来分离来自发生器DGz的数据。但是具有分离的损失函数它是不清楚两者如何汇合在一起这就是为什么我们使用普通GAN的一些进步例如Wasserstein GAN。总的来说组合损失函数看起来像 3.1、为什么GAN用于股市预测 生成对抗网络GAN最近主要用于创建逼真的图像绘画和视频剪辑。在我们的案例中没有多少GAN用于预测时间序列数据。然而主要想法应该是相同的 - 我们希望预测未来的股票变动。在未来GS股票的模式和行为应该或多或少相同除非它开始以完全不同的方式运作或者经济急剧变化。因此我们希望“生成”未来的数据这些数据将具有与我们已有的相似当然不完全相同的分布 - 历史交易数据。所以从理论上讲它应该有效。 在我们的例子中我们将使用LSTM作为时间序列生成器并使用CNN作为Discriminator。 3.2、Metropolis-Hastings GAN和Wasserstein GAN I. Metropolis-Hastings GAN Uber的工程团队最近对传统GAN进行了改进称为Metropolis-Hastings GANMHGAN。优步的方法背后的想法正如他们所述与谷歌和加州大学伯克利分校创建的另一种方法有点类似称为DRS。基本上当我们训练GAN时我们使用DiscriminatorD的唯一目的是更好地训练GeneratorG。通常在训练GAN之后我们不再使用D. 然而MHGAN和DRS尝试使用D来选择由G生成的接近实际数据分布的样本MHGAN之间的微小差异是使用马尔可夫链蒙特卡罗MCMC进行采样。 MHGAN采用从G生成的K个样本从独立的噪声输入到下图中的G-z0到zK。然后它依次运行K个输出x0到xK并遵循接受规则从Discriminator创建决定是接受当前样本还是保留最后接受的样本。最后保留的输出是被认为是G的实际输出的输出。 注意MHGAN最初由优步在pytorch中实现。我只把它转移到MXNet / Gluon。 图10MHGAN的视觉表示 II。Wasserstein GAN 训练GAN非常困难。模型可能永远不会收敛模式崩溃很容易发生。我们将使用名为Wasserstein GAN - WGAN的GAN修改。 最值得注意的要点是 我们知道GAN背后的主要目标是让Generator开始将随机噪声转换为我们想要模仿的某些给定数据。因此在GAN中比较两个分布之间的相似性的想法是非常必要的。这两个最广泛使用的指标是KL散度Kullback-Leibler - DKLp‖q∫xpxlogpxqxdx。当px等于qx时DKL为零JS DivergenceJensen-Shannon。JS散度以0和1为界与KL散度不同它是对称的更平滑。当损失从KL转移到JS散度时GAN训练取得了显著的成功。WGAN使用 Wasserstein距离WPRPG1Ksup‖f‖L≤Kx~pr[FX] - x~pg[FX]其中supsup代表supremum作为损失函数。与KL和JS的差异相比Wasserstein度量提供了一个平滑的度量没有突然的跳跃。这使得它更适合在梯度下降期间创建稳定的学习过程。此外与KL和JS相比Wasserstein距离几乎无处不在。众所周知在反向传播过程中我们会区分损失函数以创建梯度从而更新权重。因此具有可微分的损失函数是非常重要的。3.4、Generator - One layer RNN 3.4.1、LSTM或GRU 如前所述Generator是LSTM网络的一种循环神经网络RNN。RNN用于时间序列数据因为它们跟踪所有先前的数据点并且可以捕获随时间发展的模式。由于它们的性质RNN很多时候都会受到梯度消失的影响 - 也就是说在训练期间权重变化的变化变得如此之小以至于它们不会改变使得网络无法收敛到最小的损失相反的问题也可以有时会观察到 - 当梯度变得太大时。这称为梯度爆炸但解决方法很简单。两个方法解决了这个问题 - 门控循环单元GRU和长短期记忆LSTM。两者之间最大的区别是1GRU有2个门update 和resetLSTM有4个update, input, forget, 和output2LSTM保持内部存储器状态而GRU没有 3LSTM在输出门之前应用非线性sigmoidGRU不应用。 在大多数情况下LSTM和GRU在准确性方面给出了类似的结果但GRU的计算密集程度要低得多因为GRU的可训练参数更少。然而LSTM使用得更多。 严格地说LSTM cell gates背后的数学是 LSTM cell背后的数学 其中⊙是一个逐元素的乘法运算符并且对于所有x [x1x2...xk]⊤∈R^ k激活函数 3.4.2LSTM架构 LSTM架构非常简单 - LSTM一层有112个输入单元因为我们在数据集中有112个特征和500个隐藏单元Dense层有1个输出 - 每天的价格。初始化器是Xavier我们将使用L1损失这是L1正则化的平均绝对误差损失。 注意 - 在Python代码中您可以看到我们使用Adam使用learning rate.01作为优化器。 gan_num_features dataset_total_df.shape[1]sequence_length 17class RNNModel(gluon.Block): def __init__(self, num_embed, num_hidden, num_layers, bidirectionalFalse, sequence_lengthsequence_length, **kwargs): super(RNNModel, self).__init__(**kwargs) self.num_hidden num_hidden with self.name_scope(): self.rnn rnn.LSTM(num_hidden, num_layers, input_sizenum_embed, bidirectionalbidirectional, layoutTNC) self.decoder nn.Dense(1, in_unitsnum_hidden) def forward(self, inputs, hidden): output, hidden self.rnn(inputs, hidden) decoded self.decoder(output.reshape((-1, self.num_hidden))) return decoded, hidden def begin_state(self, *args, **kwargs): return self.rnn.begin_state(*args, **kwargs)lstm_model RNNModel(num_embedgan_num_features, num_hidden500, num_layers1)lstm_model.collect_params().initialize(mx.init.Xavier(), ctxmx.cpu())trainer gluon.Trainer(lstm_model.collect_params(), adam, {learning_rate: .01})loss gluon.loss.L1Loss() 我们将在LSTM层中使用500个神经元并使用Xavier初始化。为了正则化我们将使用L1。让我们来看看LSTMMXNet打印的内容。 print(lstm_model) 正如我们所看到的LSTM的输入是112个特征dataset_total_df.shape[1]然后进入LSTM层中的500个神经元然后转换为单个输出 - 股票价格值。 LSTM背后的逻辑是:我们取17天(sequence_length)的数据(同样这些数据是GS股票每天的股价当天的所有其他特性——相关资产、情绪等)并尝试预测第18天。 3.4.3、学习率调度程序 最重要的超参数之一是学习率。在训练神经网络时为几乎所有优化器例如SGDAdam或RMSProp设置学习速率至关重要因为它既能控制收敛速度又能控制网络的最终性能。最简单的学习率策略之一是在整个训练过程中具有固定的学习率。选择较小的学习速率可以使优化器找到好的解决方案但这是以限制初始收敛速度为代价的。随着时间的推移改变学习率可以克服这种权衡。 让我们绘制我们将在每个epoch使用的学习率。 class TriangularSchedule(): def __init__(self, min_lr, max_lr, cycle_length, inc_fraction0.5): self.min_lr min_lr self.max_lr max_lr self.cycle_length cycle_length self.inc_fraction inc_fraction def __call__(self, iteration): if iteration self.cycle_length*self.inc_fraction: unit_cycle iteration * 1 / (self.cycle_length * self.inc_fraction) elif iteration self.cycle_length: unit_cycle (self.cycle_length - iteration) * 1 / (self.cycle_length * (1 - self.inc_fraction)) else: unit_cycle 0 adjusted_cycle (unit_cycle * (self.max_lr - self.min_lr)) self.min_lr return adjusted_cycleclass CyclicalSchedule(): def __init__(self, schedule_class, cycle_length, cycle_length_decay1, cycle_magnitude_decay1, **kwargs): self.schedule_class schedule_class self.length cycle_length self.length_decay cycle_length_decay self.magnitude_decay cycle_magnitude_decay self.kwargs kwargs def __call__(self, iteration): cycle_idx 0 cycle_length self.length idx self.length while idx iteration: cycle_length math.ceil(cycle_length * self.length_decay) cycle_idx 1 idx cycle_length cycle_offset iteration - idx cycle_length schedule self.schedule_class(cycle_lengthcycle_length, **self.kwargs) return schedule(cycle_offset) * self.magnitude_decay**cycle_idxschedule CyclicalSchedule(TriangularSchedule, min_lr0.5, max_lr2, cycle_length500)iterations1500plt.plot([i1 for i in range(iterations)],[schedule(i) for i in range(iterations)])plt.title(Learning rate for each epoch)plt.xlabel(Epoch)plt.ylabel(Learning Rate)plt.show() 3.4.4、如何防止过度拟合和偏差 - 方差权衡 有很多特征和神经网络我们需要确保我们避免过拟合并注意总损失。 我们使用几种技术来防止过度拟合不仅在LSTM中并且在CNN和自动编码器中 确保数据质量。我们已经进行了统计检查确保数据不受多重共线性或序列自相关的影响。进一步我们对每个特征执行了特征重要性检查。最后利用一些有关股票市场运作机制的领域知识进行了初始特征选择(例如选择相关资产、技术指标等)。正规化或权重惩罚。两种最广泛使用的正则化技术是LASSOL1和RidgeL2。L1增加了平均绝对误差L2增加了平均误差。在没有涉及太多数学细节的情况下基本区别是LASSO回归L1同时进行变量选择和参数收缩而Ridge归仅进行参数收缩并最终包括模型中的所有系数。在存在相关变量的情况下岭回归可能是首选。此外岭回归在最小二乘估计具有较高方差的情况下效果最佳。因此这取决于我们的模型目标。两种类型的正则化的影响是完全不同的。虽然它们都会对大权重进行惩罚但L1正则化会导致零不可微函数。L2正则化有利于较小的权重但L1正则化有利于权重变为零。所以使用L1正则化您可以得到一个稀疏模型 - 一个参数较少的模型。在这两种情况下L1和L2正则化模型的参数“收缩”但在L1正则化的情况下收缩直接影响模型的复杂性参数的数量。Dropout。Dropout层随机删除隐藏图层中的节点。ense-sparse-dense trainingEarly stopping.构建复杂神经网络时的另一个重要考虑因素是偏差 - 方差权衡。基本上我们在训练网络时得到的误差是偏差方差和不可减少误差的函数 - σ由噪声和随机性引起的误差。权衡的最简单公式是Errorbias^2varianceσ. 偏见。偏差衡量训练的训练数据集算法在看不见的数据上的表现。高偏差欠拟合意味着模型不能很好地处理看不见的数据。方差。方差衡量模型对数据集变化的敏感性。高度差异是过度拟合。3.5、Discriminator 为什么CNN作为Discriminator 我们通常将CNN用于与图像相关的工作分类上下文提取等。例如在狗的图像中第一个卷积层将检测边缘第二个将开始检测圆圈第三个将检测到鼻子。在我们的例子中数据点形成小趋势小趋势形成更大趋势形成模式。CNN检测特征的能力可用于提取有关GS股票价格变动模式的信息。 使用CNN的另一个原因是CNN在空间数据上运行良好 - 这意味着彼此更接近的数据点彼此之间的相关性更高而不是数据点。对于时间序列数据这应该适用。在我们的例子中每个数据点对于每个特征是连续的每一天。很自然地假设彼此距离越近彼此之间的相关性就越大。需要考虑的一件事虽然没有涉及这项工作是季节性以及它如何改变如果有的话CNN的工作。 3.5.1。CNN架构 建议的CNN模型的架构 GAN中CNN的Python代码是这样的: num_fc 512# ... other parts of the GANcnn_net gluon.nn.Sequential()with net.name_scope(): # Add the 1D Convolutional layers cnn_net.add(gluon.nn.Conv1D(32, kernel_size5, strides2)) cnn_net.add(nn.LeakyReLU(0.01)) cnn_net.add(gluon.nn.Conv1D(64, kernel_size5, strides2)) cnn_net.add(nn.LeakyReLU(0.01)) cnn_net.add(nn.BatchNorm()) cnn_net.add(gluon.nn.Conv1D(128, kernel_size5, strides2)) cnn_net.add(nn.LeakyReLU(0.01)) cnn_net.add(nn.BatchNorm()) # Add the two Fully Connected layers cnn_net.add(nn.Dense(220, use_biasFalse), nn.BatchNorm(), nn.LeakyReLU(0.01)) cnn_net.add(nn.Dense(220, use_biasFalse), nn.Activation(activationrelu)) cnn_net.add(nn.Dense(1))# ... other parts of the GAN 让我们打印CNN。 print(cnn_net) 3.6、超参数 我们将跟踪和优化的超参数是 batch_size LSTM和CNN的批量大小cnn_lrCNN的学习率stridesCNN中的strideslrelu_alphaGAN中LeakyReLU的alphabatchnorm_momentummomentum for the batch normalisation in the CNNpaddingCNN中的paddingkernel_size:1CNN中的核大小dropoutLSTM中的dropoutfilters初始filters数量我们将训练超过200个epochs。 4.超参数优化 在200个epochs的GAN训练之后它将记录MAE这是LSTM中的误差函数GG并将其作为奖励值传递给强化学习该学习决定是否改变继续训练的超参数。 如果RL决定它将更新超参数它将调用贝叶斯优化下面讨论库它将提供下一个最佳预期的超级参数集 4.1、超参数优化的强化学习 为什么我们在超参数优化中使用强化学习股市一直在变化。即使我们设法训练我们的GAN和LSTM以创建非常准确的结果结果可能仅在一段时间内有效。意思是我们需要不断优化整个过程。为了优化流程我们可以 添加或删除特征例如添加可能相关的新股票或货币改善我们的深度学习模式。改进机器学习模型的最重要方法之一是通过超参数。一旦找到了一组超参数我们就需要决定何时更改它们以及何时使用已知的集合探索与利用。此外股市代表一个连续的空间取决于数百万参数。4.1.1、强化学习理论 在不解释RL的基础知识的情况下我们将详细介绍我们在此实现的具体方法。我们将使用model-free RL算法原因很明显我们不了解整个环境因此没有明确的环境工作模型 - 如果我们不需要预测股票价格变动 - 它们只会按照该模型。我们将使用model-free RL的两个细分 - 策略优化和Q学习。 Q-learning - 在Q-learning中我们学习从一个给定的状态采取动作的价值。Q值是采取动作后的预期回报。我们将使用Rainbow它是七种Q学习算法的组合。策略优化 - 在策略优化中我们学习从给定状态采取的动作。如果我们使用像Actor / Critic这样的方法我们也会学习处于给定状态的值。我们将使用近端策略优化。构建RL算法的一个关键方面是准确设置奖励。它必须捕获环境的所有方面以及代理与环境的交互。我们将奖励R定义为 Reward2∗lossGlossDaccuracyG, 其中lossGaccuracyG和lossD分别是Generator的损失和准确性以及Discriminator的损失。环境是GAN和LSTM训练的结果。不同代理可以采取的动作是如何更改GAN的D和G网络的超参数。 4.1.1.1、Rainbow Rainbowlink是一种基于Q学习的非策略深度强化学习算法它将七种算法结合在一起 DQN。DQN是Q学习算法的扩展其使用神经网络来表示Q值。与监督深度学习类似在DQN中我们训练神经网络并尝试最小化损失函数。我们通过随机抽样转换(状态、动作、奖励)来训练网络。例如这些层不仅可以是全连接层而且可以是卷积层。Double Q学习。Double QL处理Q学习中的一个大问题即高估偏差。Prioritized replay。在vanilla DQN中所有转换都存储在replay 缓冲区中并均匀地对此缓冲区进行采样。然而并非所有transitions在学习阶段都同样有益这也使得学习效率低因为需要更多的episodes 。Prioritized experience replay 不是均匀采样而是使用分布该分布为先前迭代中具有较高Q损失的样本提供更高的概率。Dueling networks。Dueling networks通过使用两个单独的流即具有两个不同的微型神经网络来稍微改变Q学习架构。一个流用于值一个用于优势。它们都共享一个卷积编码器。棘手的部分是流的合并 - 它使用了一个特殊的聚合器Wang et al.2016。Multi-step learning。Multi-step learning背后的巨大差异在于它使用N步返回计算Q值不仅是下一步的返回这自然应该更准确。分布式RL。Q学习使用平均估计的Q值作为目标值。但是在许多情况下Q值在不同情况下可能不同。分布式RL可以直接学习或近似Q值的分布而不是对它们求平均值。再次数学比这复杂得多但对我们来说好处是Q值的更准确的采样。Noisy Nets。基本DQN实现了一个简单的ε-贪婪机制来进行探索。这种探索方法有时效率低下。Noisy Nets解决这个问题的方法是添加一个有噪声的线性层。随着时间的推移网络将学习如何忽略噪声添加为嘈杂的流。但是这种学习在空间的不同部分以不同的速度进行允许进行状态探索。4.1.1.2、PPO 近端策略优化PPO是一种无策略优化模型的强化学习。实现其他算法要简单得多效果非常好。 我们为什么要使用PPOPPO的一个优点是它直接学习策略而不是间接地通过值Q学习使用Q值来学习策略的方式。它可以在连续动作空间中很好地工作这在我们的使用案例中是合适的并且可以通过平均值和标准偏差学习分布概率如果将softmax作为输出添加。 政策梯度方法的问题在于它们对步长选择极其敏感 - 如果它很小则进度需要太长时间很可能主要是由于需要二阶导数矩阵; 如果它很大会有很多噪音会显著降低性能。由于政策的变化以及奖励和观察变化的分布输入数据是非平稳的。与监督学习相比选择不当的步骤可能会更具破坏性因为它会影响下次访问的整个分布。PPO可以解决这些问题。更重要的是与其他一些方法相比PPO 例如与ACER(它需要额外的代码来保持策略外相关性和replay 缓冲区)或TRPO(它对代理目标函数施加了约束即新旧策略之间的KL差异)相比前者要简单得多。这个约束用于控制过多更改的策略——这可能会造成不稳定。PPO减少了计算(由约束)利用 clipped 之间([1 -1 ])替代目标函数和修改目标函数用惩罚有太大的更新。与TRPO相比它与在值和策略函数或辅助损失之间共享参数的算法兼容尽管PPO也具有信任区域PO的增益。注意出于练习的目的我们不会过多地研究和优化RL方法PPO和其他方法。相反我们将采用可用的方法并尝试适应我们的GANLSTM和CNN模型的超参数优化过程。我们将重用和自定义的Python代码由OpenAI创建可在此处获得https://github.com/openai/baselines。 4.1.2、进一步加强学习的工作 进一步探索强化学习的一些想法 我接下来要介绍的第一件事是使用增强随机搜索https://arxiv.org/pdf/1803.07055.pdf作为替代算法。该算法的作者在UCBerkeley之外已经设法获得与其他最先进的方法如PPO类似的奖励结果但平均快15倍。选择奖励函数非常重要。使用好奇心作为探索政策。根据伯克利的AI研究团队BAIR提出的建立多代理体系结构 - 链接https://bair.berkeley.edu/blog/2018/12/12/rllib/。4.2、贝叶斯优化 我们将使用贝叶斯优化来代替网格搜索这可能需要花费大量时间来找到超参数的最佳组合。我们将使用的库已经实现 - 链接https://github.com/fmfn/BayesianOptimization。 4.2.1、高斯过程 # Initialize the optimizerfrom bayes_opt import BayesianOptimizationfrom bayes_opt import UtilityFunctionutility UtilityFunction(kinducb, kappa2.5, xi0.0) 4.2.2、高斯过程结果 5.结果 最后当在过程的不同阶段之后将看不见的测试数据用作输入时我们将比较LSTM的输出。 在第一个epoch之后绘制图像。from utils import plot_predictionplot_prediction(Predicted and Real price - after first epoch.) 2.绘制50个epochs后的图像。 plot_prediction(Predicted and Real price - after first 50 epochs.) plot_prediction(Predicted and Real price - after first 200 epochs.) RL运行en episodes(我们将eposide定义为200个epochs上的一个完整GAN训练)。 plot_prediction(Final result.) 转载于:https://www.cnblogs.com/changbaishan/p/10266395.html