<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 TensorFlow
References
Step1:  Computational Graph
TensorFlow programs consist of 2 discrete sections
Step2:  Session
To actually evaluate nodes, the computational graph must be run in a session. The session encapsulates the control and state of the TensorFlow runtime. Below, we create a Session object and invoke its run method to evaluate node1 and node2.
Step3:  More complicated computations can be performed by combining Tensor nodes with Operation nodes. Use the tf.add node to mathematically add node1 and node2
Step4:  Placeholders
TensorBoard will show an uninteresting, static graph at this point. By adding external inputs (Placeholders) a dynamic value can be added later
Step5:  Make the graph more complex by adding another operation
Step6:  Variables
To make the model trainable, add Variables for trainable parameters
Step7:  Initialize all variables with a special operation. Until this point, they are uninitialized
Step8:  Because x is a placeholder, linear_model can be evaluated for several x values simultaneously
Step9:  Loss Function
The loss function measures how far apart the current model is from the actual data. Use a sum of squared error function to see how far off 'y' is from what is produced from 'linear_model=W * x + b' run with x=[1,2,3,4]
Step10:  The values of W and b need to be updated in order to get a perfect fit. We can manually figure out what they should be in order to get the right y output (with a loss of 0)
Step11:  tf.train API
TensorFlow optimizers will modify each variable to automatically minimize the loss function. Gradient descent is the most simple optimizer. It modifies each variable by the magnitude of the derivative of less w.r.t. that variable
Step12:  The above values are the final model parameters which minimize the loss function!
Complete Program
Everything done above is compiled below
Step13:  TensorBoard Graphs
The following produces a simple TensorBoard graph. It must be run from the containing directory and then can be viewed at the local web browser address below
* Reference
<ASSISTANT_TASK:>
Python Code:
import tensorflow as tf
Explanation: TensorFlow
References:
* TensorFlow Getting Started
* Tensor Ranks, Shapes, and Types
Overview
TensorFlow has multiple APIs:
* TensorFlow Core: lowest level, complete control, fine tuning capabilities
* Higher Level APIs: easier to learn, abstracted. (example: tf.estimator helps manage data sets, estimators, training, and inference)
Tensors
The Tensor is the central unit of data consisting of a set of values shaped into an array of any number of dimensions (rank)  
  
TensorFlow Core
End of explanation
node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node2)
Explanation: Computational Graph
TensorFlow programs consist of 2 discrete sections:
 1. Building the computational graph
 2. Running the computational graph
The computational graph is a series of TF operations arranged into a graph of nodes. Each node takes zero or more tensors as inputs and produces a tensor as an output.
Constants are a type of node which takes no inputs and will output an internally stored value. Values are initialized with tf.constant and can never change. Printing the nodes gives tensor node metadata, not the values of the nodes.
End of explanation
sess = tf.Session()
print(sess.run([node1, node2]))
Explanation: Session
To actually evaluate nodes, the computational graph must be run in a session. The session encapsulates the control and state of the TensorFlow runtime. Below, we create a Session object and invoke its run method to evaluate node1 and node2.
End of explanation
node3 = tf.add(node1, node2)
print(node3)
print(sess.run(node3))
Explanation: More complicated computations can be performed by combining Tensor nodes with Operation nodes. Use the tf.add node to mathematically add node1 and node2:
End of explanation
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b  # + provides a shortcut for tf.add(a, b)
print(sess.run(adder_node, {a: 3, b: 4.5}))
print(sess.run(adder_node, {a: [1, 3], b: [2, 4]}))
Explanation: Placeholders
TensorBoard will show an uninteresting, static graph at this point. By adding external inputs (Placeholders) a dynamic value can be added later:
End of explanation
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b: 4.5}))
Explanation: Make the graph more complex by adding another operation:
End of explanation
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
Explanation: Variables
To make the model trainable, add Variables for trainable parameters:
End of explanation
init = tf.global_variables_initializer()
sess.run(init)
Explanation: Initialize all variables with a special operation. Until this point, they are uninitialized:
End of explanation
Sessionprint(sess.run(linear_model, {x: [1, 2, 3, 4]}))
Explanation: Because x is a placeholder, linear_model can be evaluated for several x values simultaneously:
End of explanation
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))
Explanation: Loss Function
The loss function measures how far apart the current model is from the actual data. Use a sum of squared error function to see how far off 'y' is from what is produced from 'linear_model=W * x + b' run with x=[1,2,3,4]
End of explanation
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))
Explanation: The values of W and b need to be updated in order to get a perfect fit. We can manually figure out what they should be in order to get the right y output (with a loss of 0):
End of explanation
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
  sess.run(train, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]})
print(sess.run([W, b]))
Explanation: tf.train API
TensorFlow optimizers will modify each variable to automatically minimize the loss function. Gradient descent is the most simple optimizer. It modifies each variable by the magnitude of the derivative of less w.r.t. that variable
End of explanation
# Model parameters
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1, 2, 3, 4]
y_train = [0, -1, -2, -3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
  sess.run(train, {x: x_train, y: y_train})
# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
Explanation: The above values are the final model parameters which minimize the loss function!
Complete Program
Everything done above is compiled below:
End of explanation
g = tf.Graph()
with g.as_default():
    a = tf.placeholder(tf.float32, name="node1")
    b = tf.placeholder(tf.float32, name="node2")
    c = a + b
tf.summary.FileWriter("logs", g).close()
#from this notebook's directory run > tensorboard --logdir=logs
#then open TensorBoard at: http://localhost:6006/#graphs
Explanation: TensorBoard Graphs
The following produces a simple TensorBoard graph. It must be run from the containing directory and then can be viewed at the local web browser address below
* Reference: Viewing TensorFlow Graphs in Jupyter Notebooks
End of explanation
<END_TASK>