65.9K
CodeProject 正在变化。 阅读更多。
Home

集成 SAP-HANA EML 库和 TensorFlow 模型服务器 (TMS) 以预测 S&P 500 指数:第 2 部分:构建和导出 TensorFlow 模型

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019 年 9 月 13 日

CPOL

18分钟阅读

viewsIcon

11564

downloadIcon

146

如何创建和导出 TensorFlow S&P 500 指数预测模型,并使用 TensorFlow 模型服务器进行服务。

引言

这是本系列的第二篇文章,我们将讨论构建和导出用于预测标普 500 指数指标值的 AI 深度学习 TensorFlow 模型的过程。

"**标普 500 指数**,或简称**标普**,是一个股票市场指数,衡量在美国股票交易所上市的 500 家大公司的股票表现。它是最常被追踪的股票指数之一,许多人认为它是美国股市的最佳代表。自 1926 年成立以来,该指数的平均年化总回报(包括股息)为 9.8%;然而,有几年指数下跌了 30% 以上。该指数 70% 的年份都实现了年度增长。" - 维基百科 - 标普 500 指数.

标普 500 指数指标值的预测是部署和测试各种执行单变量时间序列预测的 AI 深度学习模型和解决方案的一个非常常见的示例。

使用 TensorFlow 构建和导出标普 500 指数指标值的过程是本系列文章讨论的 SAP-HANA 基础解决方案开发周期中一个非常关键的阶段。

本文的读者将了解如何部署一个用于标普 500 指数预测的 AI 深度学习模型。本文的全部内容包括以下关键主题,例如数据导入、准备和归一化阶段、使用 TensorFlow 库设计模型架构及其实现、以 TensorFlow Model Server 格式导出和保存模型到磁盘。

导入和准备标普 500 指数数据集

在使用 TensorFlow 构建和导出标普 500 指数预测模型之前,让我们先看一下我们将用于模型训练的数据。为此,我们将使用 Sebastien Heinz 在 STATSWORKS 准备并在其 Medium 文章 "使用 TensorFlow 进行股票价格预测的简单深度学习模型" 中发布的数据片段。

以下以 CSV 格式存储的日内数据集包含市值占标普 500 指数前列的美国公司(即“股票代码”)的分钟级股票价格数据,这些数据与特定的标普 500 指数指标值相关。

以下数据集结构非常简单。这个微型数据集的第一列是 Unix 格式的时间戳,表示股票价格更新的时刻。第二列“SP500”包含在每个时刻(例如,每 60 秒)更新的标普 500 指数的实际值。每家标普 500 成分股公司股票的实时值存储在其余的 500 列中(例如,NASDAQ.AALNASDAQ.AAPLNASDAQ.ADBE,...),如上图所示。

显然,该数据集非常适合构建特定的深度学习模型,因为它包含了足够的数据用于深度学习模型训练,并提供高质量的预测。具体来说,整个数据集包含近 2 x 10^3 分钟的数据,从 2017 年 4 月开始。此外,其他现有数据集包含的数据要少得多,例如仅包含 20 或 50 家公司的股票价格值,而该数据集包含了标普 500 指数中每只股票(例如,N = 500)的完整股票定价值,这些将用于拟合我们的预测模型。

正如您已经注意到的,每个特定时刻的价格值几乎相同且变化缓慢。这实际上意味着我们基本上处理的是一个单变量数据集。将单变量数据集用于深度学习模型训练目的实际上需要特定的数据集归一化。

此时,让我们讨论如何准备以下数据集以用于标普 500 指数预测模型的训练和测试。为了能够使用以下数据来拟合正在创建的预测模型,我们必须将上面讨论的整个数据集拆分为两个特定数据集,一个用于训练,一个用于测试我们的模型。此外,这些数据集中的每一个都必须包含两组必要的数据,分别是模型的输入数据或输出数据。根据 CSV 格式存储的整个数据集结构,表示每家公司股票价格的所有 500 列的值将用作输入,而第二个“SP500”列中的所有值(表示当前标普 500 指数的值)将用作正在创建的学习模型的输出。

为了加载以 CSV 格式存储的数据集,我们将使用 Pandas 库,该库能够处理我们 Python 脚本中的 CSV 数据。要加载 CSV 数据文件,我们将实现以下代码。

dataset = pd.read_csv(filename).values.tolist()

以下代码行动态加载文件到 Pandas 数据帧,然后将当前数据帧转换为 Python 的通用列表。

将特定数据从 Pandas 数据帧转换为 Python 列表后,我们必须使用以下列出的代码在水平方向上对整个数据集进行切片。

首先,我们必须计算之前获得的 2D 列表的实际形状。为此,我们需要将列表转换为 Python 的 NumPy 数组,并使用以下代码获取每个维度的项数。

    # Compute the entire dataset shape:
    shape_x = np.array(dataset).shape[0]
    shape_y = np.array(dataset).shape[1]

然后,我们必须使用 Python 的数组运算符进行切片,例如:

    # Slice the dataset and extract the input and output data
    x_set = np.array(dataset)[:,2:shape_y + 2]
    y_set = np.array(dataset)[:,1]

在完成上述计算后,我们将获得两个数组 `x_set` 和 `y_set`。第一个数组 `x_set` 包含原始数据集中所有 500 列的每行值,第二个数组 `y_set` 只包含第二个“SP500”列中每行的值。显然,第一个数组 `x_set` 由用作输入数据集的数据组成,第二个数组是正在创建的模型的输出数据集。

之后,我们的目标是生成两个数据集子集,分别用于训练或测试目的。这通常使用以下 Python 代码完成。

    # Compute the training dataset starting and ending position
    train_start = 0
    train_end = train_start + int(shape_x * ratio)

    # Compute the testing dataset starting and ending position
    test_start = train_end + 1
    test_end = len(dataset) 

    # Slice the dataset vertically and obtain training dataset
    train_X = x_set[train_start:train_end:]
    train_Y = y_set[train_start:train_end:]

    # Slice the dataset vertically and obtain testing dataset
    test_X = x_set[test_start:test_end:]
    test_Y = y_set[test_start:test_end:]

下面代码所示的主要原理是我们必须首先计算 `x_set` 和 `y_set` 数组中训练或测试数据集的开始和结束位置,然后垂直切片以获得这些训练和测试数据集。根据最佳实践,我们将使用训练集大小比例值。此时,我们所要做的就是声明 `train_start` 变量并将其初始化为 `0`。在这种情况下,`train_start` 变量将分配给训练数据集的起始位置索引。类似地,我们必须声明另一个变量 `train_end`,并通过将数组中的总行数乘以某个比例值(例如,`train_ratio = 0.5`,这意味着我们希望 50% 的行存储在训练数据集中)来计算训练数据集的结束位置。之后,我们可以通过声明另外两个变量 `test_start` 和 `test_end` 来计算测试数据集的开始和结束位置。第一个变量 `test_start` 被赋值为训练数据集结束位置 `train_end` 增加 `1` 的值,第二个变量 `test_end` 被赋值为数组中最后一个行的索引值。

在计算完这些范围后,我们现在可以轻松地切片数据以分别获得 `x_set` 和 `y_set` 数组。要做到这一点,我们必须使用 Python 的数组运算符,如上面代码列表所示。

数据归一化

在使用这些输入和输出数据集之前,必须对特定数据进行适当的归一化。这通常是为了防止我们的模型出现过拟合的情况,而过拟合又会导致模型训练过程效率低下。

归一化数据的最简单方法是使用 Min-Max 缩放方法。以下方法允许将每个值缩放到区间 [0;1] 内。还有其他类似的方法,例如使用标准差值 (STD) 的标准缩放,但在本例中,Min-Max 缩放足以正确归一化用于标普 500 指数预测的数据。

为了使用 Min-Max 缩放方法归一化数据,我们将以下公式应用于每个价格值。

$\begin{aligned}X_{norm}=\frac{X - X_{min}}{X_{max} - X_{min}}\end{aligned}$

其中

$\begin{aligned}X\end{aligned}$

是未归一化的原始价格值,

$\begin{aligned}X_{min}\end{aligned}$

是当前列的最小价格值,

$\begin{aligned}X_{max}\end{aligned}$

是当前列的最大价格值,

$\begin{aligned}X_{norm}\end{aligned}$

是区间 [0;1] 内的归一化价格值;

为了反归一化这些数据,我们必须使用以下公式。

$\begin{aligned}X = X_{norm} \times (X_{max} - X_{min}) + X_{min}\end{aligned}$
其中 $\begin{aligned}X\end{aligned}$

是反归一化的价格值,

$\begin{aligned}X_{min}\end{aligned}$

是当前列的最小价格值,

$\begin{aligned}X_{max}\end{aligned}$

是当前列的最大价格值,

$\begin{aligned}X_{norm}\end{aligned}$

是区间 [0;1] 内的归一化价格值;

为了执行这种归一化/反归一化,我们必须实现这两个 Python 过程。

def normalize(dataset):
    if len(np.array(dataset).shape) > 1:
        min_vals = np.array(dataset).min(axis=0)
        max_vals = np.array(dataset).max(axis=0)
        for col in range(0, len(dataset[0])):
            for row in range(0, len(dataset)):
                if (max_vals[col] - min_vals[col]) != 0.0:
                    dataset[row][col] -=min_vals[col] 
                    dataset[row][col] /= (max_vals[col] - min_vals[col])
                else: dataset[row][col] = 1.0
        
        return (dataset, min_vals, max_vals)
    else:
        min_val = np.array(dataset).min()
        max_val = np.array(dataset).max()
        for index in range(0, len(dataset)):
            if (max_val - min_val) != 0.0:
                dataset[index] -= min_val
                dataset[index] /= (max_val - min_val)
            else: dataset[index] = 1.0
        return (dataset, [min_val], [max_val])
            
def denormalize(dataset, min_vals, max_vals):
    if len(np.array(dataset).shape) > 1:
        for col in range(0, len(dataset[0])):
            for row in range(0, len(dataset)):
                if (max_vals[col] - min_vals[col]) != 0.0:
                    dataset[row][col] *= (max_vals[col] - min_vals[col]) 
                    dataset[row][col] += min_vals[col]
                else: dataset[row][col] = min_vals[col]
    else:
        for index in range(0, len(dataset)):
            if (max_vals[0] - min_vals[0]) != 0.0:
                dataset[index] *= (max_vals[0] - min_vals[0]) 
                dataset[index] += min_vals[0]
            else: dataset[index] = min_vals[0]
            
    return dataset

第一个函数 `normalize(dataset)` 接受一个待归一化数据集作为唯一参数。在进行归一化时,该函数会找到每列的最小值和最大值,然后遍历当前列中的每个值,应用上面列出的 `Min-Max` 缩放公式。如果当前列中的所有值都相等,则将每个值替换为 1.0。

上面列出的第二个函数 `denormalize(dataset, min_vals, max_vals)` 执行几乎相同的操作,但接受三个参数,分别是数据集、列最小值数组和最大值数组。然后,它使用这些最小值和最大值通过将第二个反归一化公式应用于每个值来反归一化每列中的每个值。

根据最佳实践,我已经实现了可以归一化/反归一化 1D 和 2D 数组的归一化和反归一化函数。为了找到每列的最小值和最大值,我已经使用了 Python 的 NumPy 库中已实现的函数。以下代码说明了如何使用这些归一化函数。

    # Normalize the input and output training dataset
    (train_X, train_min_vals_X, train_max_vals_X) = normalize(train_X)
    (train_Y, train_min_vals_Y, train_max_vals_Y) = normalize(train_Y)
    # Normalize the input and output testing dataset
    (test_X, test_min_vals_X, test_max_vals_X) = normalize(test_X)
    (test_Y, test_min_vals_Y, test_max_vals_Y) = normalize(test_Y)

在执行归一化后,我们的数据集将如下所示。

    array([[0.87724435, 0.24034211, 0.2995815 , ..., 0.3976612 , 0.8962966 ,
        0.9130416 ],
       [0.8947885 , 0.24892762, 0.5907224 , ..., 0.32748637, 0.8111101 ,
        0.9130416 ],
       [0.8655475 , 0.24467744, 0.55063736, ..., 0.33333334, 0.78518426,
        0.92608786],
       ...,
       [0.8246096 , 0.18455261, 0.28270024, ..., 0.7076022 , 0.21851741,
        0.37826157],
       [0.80706316, 0.18455261, 0.29535797, ..., 0.7309945 , 0.21851741,
        0.36521864],
       [0.80706316, 0.18455261, 0.27848312, ..., 0.72514534, 0.22592641,
        0.3739128 ]], dtype=float32)

从上面的输出可以看出,归一化数据集的每个值都位于区间 [0;1] 内。此外,我决定不使用任何现有的缩放器对象,例如 Python 的 Sklearn 库中的 `MinMaxScaler` 或 StandardScaler,因为无法将这些缩放器对象保存到已构建和导出的模型中。

构建基于 RNN 的 TensorFlow 预测模型

此时,让我们看看标普 500 指数预测模型的架构及其使用 Python 的 TensorFlow Keras 库的实现。显然,我们将使用顺序模型来构建一个可用于预测标普 500 指数指标值的模型。

在设计模型之前,让我们回顾一下在训练过程中传递给模型输入和输出的数据。正如我们已经讨论过的,我们将整个数据集网格切片成一个包含输入或输出数据的 2D 数组元组。显然,每个训练样本都是一个 1D 数组,不需要重塑。

输入数据集实际上是一个 2D 数组,其每一行都是一个训练样本,包含来自 500 个股票代码的值,这些值被传递给模型输入层中每个神经元的输入。

输出数据集也是一个 2D 数组,其每一行包含一个标普 500 指数值,该值在训练过程中被传递到模型的输出。

在这种情况下,我们将使用 TensorFlow 的 Dense 层作为正在创建的模型的第一输入层和最后一输出层。

为了提高预测质量并大幅减少训练所需的数据量,我们将使用带有门控循环单元 (GRU) 单元的循环神经网络 (RNN) 作为整个模型设计的隐藏层。具体来说,我们将通过 Reshape 和 Flatten TensorFlow 层将密集输入和输出层与基于 RNN 的隐藏层互连。所设计模型的整体结构如下图所示。

根据该模型的架构,第一个密集层具有 500 个值的输入维度。这实际上意味着输入层中的每个神经元将有 500 个输入。出于预测目的,我们将设计一个包含 N1 = 100 个神经元的输入密集层,这应该足以提供所需的预测质量。接下来,该输入层与重塑层互连,在该层中,输入层的输出数据被重塑为 RNN 输入的 2D 数组。使用 RNN 的要求是输入数组必须具有以下 3D 形状:[samples, timesteps, features]。具体来说,我们将输入层的输出的扁平数组表示为一种形状,其中 samples = 10,timesteps = 10,features = 1(例如 [10, 10, 1]),并将其传递给 RNN 的输入。要实现基于 RNN 的隐藏层,我们将创建一个通用的 GRU 单元列表,每个单元包含 8 个单元并使用 ReLU 激活函数,这对于单变量时间序列预测非常有用。

最后,我们必须将 RNN 层与输出密集层互连。正如我们已经注意到的,使用的 RNN 通常具有与输入相同的输出形状,这通常与输出密集层的 [一维输入形状] 冲突。为了解决这个问题,我们必须使用 TensorFlow Flatten 层将 RNN 的输出重塑为传递给最后一个输出密集层输入的扁平 1D 数组。

由于正在设计的模型旨在预测标普 500 指数的单个值,因此输出密集层由一个神经元组成,该神经元具有 8 个输入和一个输出。

为了提供设计模型的所需预测质量,我们将使用 Adam 优化器,学习率为 `Lr = 0.002`。此外,我们将使用均方误差损失函数,该函数常用于时间序列预测。

使用 Keras 库实现上述模型的 Python 代码如下。

# Instantiate sequential model
model = tf.compat.v1.keras.Sequential()
# Add dense input layer
model.add(tf.compat.v1.keras.layers.Dense(
	tf_neurons_input_layer, input_dim=train_X.shape[1]))
# Add reshape layer
model.add(tf.compat.v1.keras.layers.Reshape((int(tf_neurons_input_layer / 10), 
	int(tf_neurons_input_layer / 10)), input_shape=(tf_neurons_input_layer,)))

# Create a generic list of GRU-cell objects
tf_gru_cells = []
for tf_gru_layer_ii in range(tf_gru_layers):
    tf_gru_cells.append(tf.compat.v1.keras.layers.GRUCell(
	tf_neurons_per_gru_layer, activation='relu'))

# Add the RNN layer with GRU-cell objects previously instantiated
model.add(tf.compat.v1.keras.layers.RNN(tf_gru_cells, 
	return_sequences=True, input_shape=(
	  int(tf_neurons_input_layer / 10), int(tf_neurons_input_layer / 10))))

# Add the flatten layer
model.add(tf.compat.v1.keras.layers.Flatten())
# Add the output dense layer
model.add(tf.compat.v1.keras.layers.Dense(1, input_dim=tf_neurons_per_gru_layer))

# Instantiate Adam optimizer object
Optimizer = tf.compat.v1.keras.optimizers.Adam(lr=0.002, beta_1=0.9, 
	beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)

# Compile the model
model.compile(optimizer=Optimizer, loss='mse')

在这段代码中,我们首先实例化顺序模型,并添加模型的第一个密集输入层以及重塑层。之后,我们创建一个 GRU 单元的通用列表,并将其作为 RNN 层的参数传递。然后,我们添加扁平层和输出密集层。此外,我们必须实例化一个优化器对象,将学习率和其他参数值传递给其构造函数。最后,我们必须通过调用特定的 `compile(optimizer=Optimizer, loss='mse')` 方法来编译正在创建的模型。

模型成功创建后,我们可以通过调用以下方法来拟合模型。

model.fit(train_X, train_Y, epochs=tf_epochs)

我们通常将归一化的输入训练数据集 `train_X` 和 `train_Y` 作为此方法的第一个和第二个参数传递。此外,我们必须将训练周期数指定为此方法的最后一个参数。

此方法在执行时提供以下输出。

Epoch 1/20
1599/1599 [==============================] - 1s 878us/sample - loss: 0.0127
Epoch 2/20
1599/1599 [==============================] - 1s 337us/sample - loss: 6.4495e-04
Epoch 3/20
1599/1599 [==============================] - 1s 336us/sample - loss: 6.0796e-04
Epoch 4/20
1599/1599 [==============================] - 1s 339us/sample - loss: 4.8388e-04
Epoch 5/20
1599/1599 [==============================] - 1s 340us/sample - loss: 5.3904e-04
Epoch 6/20
1599/1599 [==============================] - 1s 339us/sample - loss: 5.1811e-04
Epoch 7/20
1599/1599 [==============================] - 1s 340us/sample - loss: 4.5971e-04
Epoch 8/20
1599/1599 [==============================] - 1s 343us/sample - loss: 3.9597e-04
Epoch 9/20
1599/1599 [==============================] - 1s 338us/sample - loss: 4.8107e-04
Epoch 10/20
1599/1599 [==============================] - 1s 336us/sample - loss: 4.1709e-04
Epoch 11/20
1599/1599 [==============================] - 1s 340us/sample - loss: 3.7280e-04
Epoch 12/20
1599/1599 [==============================] - 1s 338us/sample - loss: 4.3241e-04
Epoch 13/20
1599/1599 [==============================] - 1s 340us/sample - loss: 3.8231e-04
Epoch 14/20
1599/1599 [==============================] - 1s 338us/sample - loss: 4.1684e-04
Epoch 15/20
1599/1599 [==============================] - 1s 337us/sample - loss: 4.0849e-04
Epoch 16/20
1599/1599 [==============================] - 1s 339us/sample - loss: 4.2306e-04
Epoch 17/20
1599/1599 [==============================] - 1s 338us/sample - loss: 4.1383e-04
Epoch 18/20
1599/1599 [==============================] - 1s 340us/sample - loss: 3.9852e-04
Epoch 19/20
1599/1599 [==============================] - 1s 338us/sample - loss: 4.0887e-04
Epoch 20/20
1599/1599 [==============================] - 1s 346us/sample - loss: 3.9221e-04   

从上面的输出可以看出,损失函数的值在每个模型拟合周期后急剧下降。

最后,我们必须使用之前创建的数据集来评估模型。为此,我们必须调用以下方法,将评估数据集 `test_X` 作为此方法的唯一参数传递。

predicted = model.predict(test_X, steps=1)

该方法返回一个预测的标普 500 指数值数组,该数组被赋给 `predicted` 变量。

之后,我们必须通过调用以下两个方法来反归一化这些预测值。

    test_Y    = denormalize(test_Y, test_min_vals_Y, test_max_vals_Y)
    predicted = denormalize(predicted, test_min_vals_Y, test_max_vals_Y)

为了检查我们的模型是否预测了正确的标普 500 指数值,让我们将预测结果与我们在数据准备阶段获得的评估数据集 `test_Y` 数组中的目标值进行可视化。为此,我们将使用 Python 的 `matplotlib.pyplot` 库并调用以下三个方法来绘制特定图表并将其保存为 jpg 格式。

    plt.plot(np.array(test_Y), color='blue')
    plt.plot(np.array(predicted), color='red')
    
    plt.savefig("5229100/sp500_result.jpg", dpi=None, facecolor='w', edgecolor='w',
        orientation='portrait', papertype=None, format=None,
        transparent=False, bbox_inches=None, pad_inches=0.1, metadata=None)

执行以下方法将创建下图所示的图表:

从上图可以看出,实际的标普 500 指数指标值和预测的标普 500 指数指标值几乎相同,这意味着我们的模型倾向于提供所需的预测质量。

导出预测模型

由于我们已经成功构建并测试了预测模型,现在是时候讨论如何导出该模型并使用 TensorFlow Model Server 进行服务了。为此,我们必须实现以下 Python 代码。

# Instantiate the session object 
sess = tf.compat.v1.Session() 

# Invoke the global variables initializer 
sess.run(tf.compat.v1.global_variables_initializer()) 

# Set the current session object 
tf.compat.v1.keras.backend.set_session(sess) 

# Set the model learning phase ‘0’ 
tf.compat.v1.keras.backend.set_learning_phase(0)

# Instantiate TensorFlow model builder
builder = tf.compat.v1.saved_model.builder.SavedModelBuilder(path)

# Create the model inputs tensor info object
tensor_info_input = tf.compat.v1.saved_model.utils.build_tensor_info(model.input)

# Create the model outputs tensor info object
tensor_info_output = tf.compat.v1.saved_model.utils.build_tensor_info(model.outputs[0])

# Create the model signature object
prediction_signature = (
  # Build the model signature definition
  tf.compat.v1.saved_model.signature_def_utils.build_signature_def(
  inputs={'TICKERS': tensor_info_input},
  outputs={'SP500INDEX': tensor_info_output},
  method_name=tf.compat.v1.saved_model.signature_constants.PREDICT_METHOD_NAME))

# Add the model’s meta-graph and tensors to the builder
builder.add_meta_graph_and_variables(
  sess, [tf.compat.v1.saved_model.tag_constants.SERVING],
  signature_def_map={
  'sp500_prediction':
  prediction_signature
  })
 
# Save the model
builder.save()

根据上面列出的代码,我们将使用 TensorFlow Model Builder 将模型导出为适当的格式。具体来说,我们要做的就是实例化模型构建器对象,将待导出的模型路径作为其构造函数的唯一参数传递。然后,我们必须为模型的输入或输出创建两个张量上下文对象,并将它们作为 `saved_model.utils.build_tensor_info(...)` 方法的唯一参数传递。我们将使用 `model.input` 和 `model.outputs[0]` 变量的值作为传递给该方法的参数。具体来说,`model.input` 和 `model.output` 变量是模型输入和输出张量对象。由于我们的模型只有一个输出,我们将使用 `model.output` 数组的第一个元素作为传递给 `build_tensor_info(...)` 方法的参数。

之后,我们必须创建模型的签名对象,作为 `build_signature_def(...)` 方法调用的结果。该方法有三个主要参数,分别是模型的输入和输出上下文对象,以及覆盖的预测方法。模型签名中的每个特定输入和输出都有其自己的唯一别名,例如 'TICKERS' 或 'SP500INDEX'。由于我们将使用该模型的默认预测方法,因此我们必须使用预定义的常量 `PREDICT_METHOD_NAME` 来指定其名称。

由于模型签名对象已经实例化,我们需要调用 `add_meta_graph_and_variables(...)` 方法,该方法有三个主要参数,包括 `session` 对象、默认服务别名以及先前创建的模型签名对象。具体来说,此方法创建一个元图,代表模型的结构上下文,并将模型的变量和张量与创建的元图一起存储到模型中。

在内存中创建模型后,我们必须最后调用 `builder.save()` 方法来保存模型。正在保存的模型具有以下目录结构。

  8889/
    variables/
      variables.data-?????-of-?????
      variables.index
    saved_model.pb

在这种情况下,模型*根*目录的名称是导出的模型版本。`variables` 目录包含两个文件,如 `variables.data` 和 `variables.index`,其中包含输入和输出模型张量以及其他变量及其值。`saved_model.pb` 文件包含保存的模型元图。

如何运行此脚本

既然我们已经编写了构建和导出模型的 Python 脚本,让我们讨论如何在开发机器上运行此脚本。首先,必须安装 Python 语言和开发工具。在这种情况下,我强烈建议从此链接下载 Anaconda 环境,并在开发机器上本地安装。

之后,我们必须打开 Anaconda3 命令提示符,并切换到解压 `sp500_retrain.tar.gz` 存档的目录。该目录包含 `sp500_retrain.py`、`utility.py` 和 `sp500.csv` 等文件。我们必须找到 `sp500_retrain.py` 脚本并使用以下命令运行它。

python sp500_retrain.py

或者,我们可以使用 Anaconda 的 Jupytter Notebook 来执行上述脚本。在这种情况下,我们要做的就是用文本编辑器打开 `sp500_retrain.py` 和 `utility.py` 脚本,并将它们粘贴到 Jupytter Notebook 中新创建的 Python 脚本中,然后单击**运行**按钮来执行脚本。

最后,将创建 `sp500_model` 目录。此外,该脚本还会生成上面提到的 JPG 图片(例如 `sp500_results.jpg`),可视化预测结果。脚本执行创建模型后,我们就可以部署该模型进行服务了。

使用 TensorFlow Model Server (TMS) 服务模型

模型成功构建并导出后,我们就可以使用 TensorFlow Model Server 来服务模型了。在上一篇文章中,我们已经讨论了如何在 SAP-HANA 虚拟服务器上安装和配置 TensorFlow Model Server。要能够服务此模型,我们首先要做的就是使用 PuTTY SSH 客户端以 `tmsadm` 用户身份登录到运行中的 SAP-HANA 服务器 bash 控制台实例。然后,我们必须通过 FTP 连接将我们的模型(例如,“`sp500_model`”目录)上传到服务器上的 `/home/tmsadm/exports/` 位置。`/home/tmsadm/exports/` 目录已经包含在服务器配置阶段之前创建的 `models.config` 文件。我们必须按如下方式修改此配置文件。

model_config_list: {
    config: {
        name: "sp500_model",
        base_path: "/tf_models/sp500_model",
        model_platform: "tensorflow"
    },
}

既然我们已经上传了 `model` 目录并修改了配置文件,我们就可以使用 `tmsadm` 用户 bash 控制台中的以下命令启动带有 TensorFlow Model Server 的 Docker 容器。

sudo docker run -it -d -p 8500:8500 -p 9000:9000 
--mount type=bind,source=/home/tmsadm/export/models.config,
target=/tf_models/config/models.config --mount type=bind,
source=/home/tmsadm/export/sp500_model/,target=/tf_models/sp500_model/ 
--entrypoint "/bin/sh" tensorflow/serving:latest -c "tensorflow_model_server 
--port=8500 --rest_api_port=9000 --model_config_file=/tf_models/config/models.config"

为了检查当前模型是否已成功服务,我们必须使用 CURL 工具发出以下 RESTFul API 请求。

curl -X GET http://192.168.0.131:9000/v1/models/sp500_model

作为对此请求的响应,我们将以 JSON 格式收到以下数据。

{
 "model_version_status": [
  {
   "version": "8889",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}

这些状态消息表明模型已正确服务,我们可以在 SAP-HANA SQL 引擎中使用它来在 SAP-HANA 服务器后端执行特定预测。

结论

在本文中,我们详细讨论了如何构建、导出和提供标普 500 指数预测模型。我们学习了如何使用各种技术来设计有效的基于 RNN 的模型架构,并使用它来做出特定的标普 500 指数指标值预测。除了模型架构,我们还了解了如何使用 Python 的 TensorFlow Keras 库来实现此模型,并使用 TensorFlow Model Server 进行导出以提供服务。

历史

  • 2019 年 9 月 13 日:首次发布版本
© . All rights reserved.