Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Tensorflow implementation #1665

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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