Skip to content

Commit

Permalink
Update tensorflow writing algorithm and research
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisSzeto authored and AlexCatarino committed Apr 5, 2024
1 parent 7c148e4 commit e6c7ada
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 251 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<p>This page explains how to build, train, deploy and store <code>Tensorflow</code> v1 models. To view the tutorial on Tensorflow 2, see <a href="/docs/v2/writing-algorithms/machine-learning/popular-libraries/keras">Keras</a>.</p>
<p>This page explains how to build, train, deploy and store <code>Tensorflow</code> models.</p>
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
<p>Import the <code>tensorflow</code> libraries.</p>
<p>Import the <code>tensorflow</code> and <code>sklearn</code> libraries.</p>

<div class="section-example-container">
<pre class="python">from AlgorithmImports import *
import tensorflow.compat.v1 as tf
from google.protobuf import json_format
import json5

tf.disable_v2_behavior()</pre>
</div>

<p>You need the <code>google.protobuf</code> and <code>json5</code> libraries to store and load models.</p>

<p>Disable <code>tensorflow</code> v2 behaviors in order to deploy a v1 model.</p>
import tensorflow as tf</pre>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<tbody>
<tr>
<td>Features</td>
<td>The last 5 closing prices</td>
<td>The last 5 close price differencing compared to current price</td>
</tr>
<tr>
<td>Labels</td>
<td>The following day's closing price</td>
<td>The following day's price change</td>
</tr>
</tbody>
</table>
Expand All @@ -24,58 +24,23 @@

<p>Follow these steps to create a method to build the model:</p>
<ol>
<li>Create a method to build the model for the algorithm class.</li>
<li>Set the number of layers, their number of nodes, the number of epoch and the learning rate.</li>
<div class="section-example-container">
<pre class="python">def BuildModel(self):
# Instantiate a tensorflow session
sess = tf.Session()

# Declare the number of factors and then create placeholders for the input and output layers.
num_factors = 5
X = tf.placeholder(dtype=tf.float32, shape=[None, num_factors], name='X')
Y = tf.placeholder(dtype=tf.float32, shape=[None])

# Set up the weights and bias initializers for each layer.
weight_initializer = tf.variance_scaling_initializer(mode="fan_avg", distribution="uniform", scale=1)
bias_initializer = tf.zeros_initializer()

# Create hidden layers that use the Relu activator.
num_neurons_1 = 32
num_neurons_2 = 16
num_neurons_3 = 8

W_hidden_1 = tf.Variable(weight_initializer([num_factors, num_neurons_1]))
bias_hidden_1 = tf.Variable(bias_initializer([num_neurons_1]))
hidden_1 = tf.nn.relu(tf.add(tf.matmul(X, W_hidden_1), bias_hidden_1))

W_hidden_2 = tf.Variable(weight_initializer([num_neurons_1, num_neurons_2]))
bias_hidden_2 = tf.Variable(bias_initializer([num_neurons_2]))
hidden_2 = tf.nn.relu(tf.add(tf.matmul(hidden_1, W_hidden_2), bias_hidden_2))

W_hidden_3 = tf.Variable(weight_initializer([num_neurons_2, num_neurons_3]))
bias_hidden_3 = tf.Variable(bias_initializer([num_neurons_3]))
hidden_3 = tf.nn.relu(tf.add(tf.matmul(hidden_2, W_hidden_3), bias_hidden_3))

# Create the output layer and give it a name, so it is accessible after saving and loading the model.
W_out = tf.Variable(weight_initializer([num_neurons_3, 1]))
bias_out = tf.Variable(bias_initializer([1]))
output = tf.transpose(tf.add(tf.matmul(hidden_3, W_out), bias_out), name='outer')

# Set up the loss function and optimizers for gradient descent optimization and backpropagation.
# This example uses mean-square error as the loss function because the close price is a continuous data and uses Adam as the optimizer because of its adaptive step size.
loss = tf.reduce_mean(tf.squared_difference(output, Y))
optimizer = tf.train.AdamOptimizer().minimize(loss)

return sess, X, Y, output, optimizer</pre>
</div>

<li>Instantiate the model, input layers, output layer, and optimizer and then save them as class variables.</li>
<div class="section-example-container">
<pre class="python">self.model, self.X, self.Y, self.output, self.optimizer = self.BuildModel(features, labels)</pre>
<pre class="python">num_factors = 5
num_neurons_1 = 10
num_neurons_2 = 20
num_neurons_3 = 5
self.epochs = 20
self.learning_rate = 0.0001</pre>
</div>

<li>Call the <code>run</code> method with the result from the <code>global_variables_initializer</code> method.</li>
<li>Create the model using in-built Keras API.</li>
<div class="section-example-container">
<pre class="python">self.model.run(tf.global_variables_initializer())</pre>
<pre class="python">self.model = tf.keras.Sequential([
tf.keras.layers.Dense(num_neurons_1, activation=tf.nn.relu, input_shape=(num_factors,)), # input shape required
tf.keras.layers.Dense(num_neurons_2, activation=tf.nn.relu),
tf.keras.layers.Dense(num_neurons_3, activation=tf.nn.relu),
tf.keras.layers.Dense(1)
])</pre>
</div>
</ol>
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,34 @@ <h4>Warm Up Training Data</h4>
<h4>Define a Training Method</h4>
<p>To train the model, define a method that fits the model with the training data.</p>
<div class="section-example-container">
<pre class="python">def get_features_and_labels(self, n_steps=5):
close_prices = list(self.training_data)[::-1]
features = []
labels = []
for i in range(len(close_prices)-n_steps):
features.append(close_prices[i:i+n_steps])
labels.append(close_prices[i+n_steps])
features = np.array(features)
labels = np.array(labels)

return features, labels
<pre class="python">def get_features_and_labels(self, lookback=5):
lookback_series = []

data = pd.Series(list(self.training_data)[::-1])
for i in range(1, lookback + 1):
df = data.diff(i)[lookback:-1]
df.name = f"close-{i}"
lookback_series.append(df)

X = pd.concat(lookback_series, axis=1).reset_index(drop=True).dropna()
Y = data.diff(-1)[lookback:-1].reset_index(drop=True)
return X.values, Y.values

def my_training_method(self):
features, labels = self.get_features_and_labels()
self.model.run(self.optimizer, feed_dict={self.X: features, self.Y: labels})</pre>

# Define the loss function, we use MSE in this example
def loss_mse(target_y, predicted_y):
return tf.reduce_mean(tf.square(target_y - predicted_y))

# Train the model
optimizer = tf.keras.optimizers.Adam(learning_rate=self.learning_rate)
for i in range(self.epochs):
with tf.GradientTape() as t:
loss = loss_mse(labels, self.model(features))

jac = t.gradient(loss, self.model.trainable_weights)
optimizer.apply_gradients(zip(jac, self.model.trainable_weights))</pre>
</div>

<h4>Set Training Schedule</h4>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<p>To predict the labels of new data, in the <code>OnData</code> method, get the most recent set of features and then call the <code>run</code> method with new features.</p>
<div class="section-example-container">
<pre class="python">new_features, __ = self.get_features_and_labels()
prediction = self.model.run(self.output, feed_dict={self.X: new_features[-1].reshape(1, -1)})
prediction = float(prediction.flatten()[-1])</pre>
prediction = self.model(new_features)
prediction = float(prediction.numpy()[-1])</pre>
</div>

<p>You can use the label prediction to place orders.</p>
<div class="section-example-container">
<pre class="python">if prediction > slice[self.symbol].Price:
<pre class="python">if prediction > 0:
self.SetHoldings(self.symbol, 1)
else:
self.SetHoldings(self.symbol, -1)</pre>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
<p>Follow these steps to save <code>Tensorflow</code> models into the <a href="/docs/v2/writing-algorithms/object-store">Object Store</a>:</p>

<ol>
<li>Export the <code>TensorFlow</code> graph as a JSON object.</li>
<li>Set the key name of the model to be stored in the Object Store.</li>
<div class="section-example-container">
<pre class="python">graph_definition = tf.compat.v1.train.export_meta_graph()
json_graph = json_format.MessageToJson(graph_definition)</pre>
<pre class="python">model_key = "model.keras"</pre>
</div>
<p>Note that the model has to have the suffix <code>.keras</code>.</p>

<li>Export the <code>TensorFlow</code> weights as a JSON object.</li>
<li>Call the <code>GetFilePath</code> method with the key.</li>
<div class="section-example-container">
<pre class="python">weights = self.model.run(tf.compat.v1.trainable_variables())
weights = [w.tolist() for w in weights]
json_weights = json5.dumps(weights)</pre>
<pre class="python">file_name = self.ObjectStore.GetFilePath(model_key)</pre>
</div>
<p>This method returns the file path where the model will be stored.</p>

<li>Save the graph and weights to the Object Store.</li>
<li>Call the <code>save</code> method with the model and file path.</li>
<div class="section-example-container">
<pre class="python">self.ObjectStore.Save('graph', json_graph)
self.ObjectStore.Save('weights', json_weights)</pre>
<pre class="python">model.save(file_name)</pre>
</div>

<li>Save the model to the file path.</li>
<div class="section-example-container">
<pre class="python">self.ObjectStore.Save(model_key)</pre>
</div>
</ol>
Original file line number Diff line number Diff line change
@@ -1,35 +1,10 @@
<p>You can load and trade with pre-trained <code>tensorflow</code> models that you saved in the Object Store. To load a <code>tensorflow</code> model from the Object Store, in the <code>Initialize</code> method, get the file path to the saved model and then recall the graph and weights of the model.</p>
<div class="section-example-container">
<pre class="python">def Initialize(self) -&gt; None:
if self.ObjectStore.ContainsKey('graph') and self.ObjectStore.ContainsKey('weights'):
json_graph = self.ObjectStore.Read('graph')
json_weights = self.ObjectStore.Read('weights')

# Restore the tensorflow graph from JSON objects
tf.reset_default_graph()
graph_definition = json_format.Parse(json_graph, tf.MetaGraphDef())
self.model = tf.Session()
tf.train.import_meta_graph(graph_definition)

# Select the input, output tensors and optimizer
self.X = tf.get_default_graph().get_tensor_by_name('X:0')
self.Y = tf.get_default_graph().get_tensor_by_name('Y:0')
self.output = tf.get_default_graph().get_tensor_by_name('outer:0')
self.optimizer = tf.get_default_graph().get_collection('Variable/Adam')

# Restore the model weights from the JSON object.
weights = [np.asarray(x) for x in json5.loads(json_weights)]
assign_ops = []
feed_dict = {}
vs = tf.trainable_variables()
zipped_values = zip(vs, weights)
for var, value in zipped_values:
value = np.asarray(value)
assign_placeholder = tf.placeholder(var.dtype, shape=value.shape)
assign_op = var.assign(assign_placeholder)
assign_ops.append(assign_op)
feed_dict[assign_placeholder] = value
self.model.run(assign_ops, feed_dict=feed_dict)</pre>
model_key = 'model.keras'
if self.ObjectStore.ContainsKey(model_key):
file_name = self.ObjectStore.GetFilePath(model_key)
self.model = tf.keras.models.load_model(file_name)</pre>
</div>

<p>The <code>ContainsKey</code> method returns a boolean that represents if the <code>graph</code> and <code>weights</code> is in the Object Store. If the Object Store doesn't contain the keys, save the model using them before you proceed.</p>
<p>The <code>ContainsKey</code> method returns a boolean that represents if the <code>model.keras</code> is in the Object Store. If the Object Store doesn't contain the keys, save the model using them before you proceed.</p>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="qc-embed-frame" style="display: inline-block; position: relative; width: 100%; min-height: 100px; min-width: 300px;">
<div class="qc-embed-dummy" style="padding-top: 56.25%;"></div>
<div class="qc-embed-element" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<iframe class="qc-embed-backtest" height="100%" width="100%" style="border: 1px solid #ccc; padding: 0; margin: 0;" src="https://www.quantconnect.com/terminal/processCache?request=embedded_backtest_81f30fb424e8dffe1ed14741d6362dd3.html"></iframe>
<iframe class="qc-embed-backtest" height="100%" width="100%" style="border: 1px solid #ccc; padding: 0; margin: 0;" src="https://www.quantconnect.com/terminal/processCache?request=embedded_backtest_7a48d7fec6a0ba0724aeb843fc612d0e.html"></iframe>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<p>Import the <code>tensorflow</code>, <code>sklearn</code>, <code>json5</code> and <code>google.protobuf</code> libraries.</p>
<p>Import the <code>tensorflow</code>, and <code>sklearn</code> libraries.</p>

<div class="section-example-container">
<pre class="python">import tensorflow as tf
from sklearn.model_selection import train_test_split
import json5
from google.protobuf import json_format</pre>
from sklearn.model_selection import train_test_split</pre>
</div>

<p>You need the <code>sklearn</code> library to prepare the data and the <code>json5</code> and <code>google.protobuf</code> libraries to save models.</p>
<p>You need the <code>sklearn</code> library to prepare the data.</p>
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<tbody>
<tr>
<td>Features</td>
<td>The last 5 closing prices</td>
<td>The last 5 close price differencing to the current price</td>
</tr>
<tr>
<td>Labels</td>
<td>The following day's closing price</td>
<td>The following day's price change</td>
</tr>
</tbody>
</table>
Expand All @@ -28,18 +28,19 @@
<pre class="python">lookback = 5
lookback_series = []
for i in range(1, lookback + 1):
df = history['close'].shift(i)[lookback:-1]
df.name = f"close_-{i}"
df = data['close'].diff(i)[lookback:-1]
df.name = f"close-{i}"
lookback_series.append(df)
X = pd.concat(lookback_series, axis=1).reset_index(drop=True)</pre>
X = pd.concat(lookback_series, axis=1).reset_index(drop=True).dropna()
X</pre>
</div>

<p>The following image shows the format of the features DataFrame:</p>
<img class="docs-image" src="https://cdn.quantconnect.com/i/tu/tf-x.png" alt="Features and labels for training">

<li>Select the close column and then call the <code>shift</code> method to collect the labels.</li>
<div class="section-example-container">
<pre class="python">Y = history['close'].shift(-1)</pre>
<pre class="python">Y = data['close'].diff(-1)</pre>
</div>

<li>Drop the first 5 features and then call the <code>reset_index</code> method.</li>
Expand Down
Loading

0 comments on commit e6c7ada

Please sign in to comment.