lib/tensorflow/session.rb
# A class for running TensorFlow operations.
# A Session object encapsulates the environment in which Operation objects are executed, and Tensor objects are evaluated.
# A Session instance lets a caller drive a TensorFlow graph computation.
# When a Session is created with a given target, a new Session object is bound to the
# universe of resources specified by that target. Those resources are available to this
# session to perform computation described in the GraphDef. After extending the session
# with a graph, the caller uses the Run() API to perform the computation and potentially
# fetch outputs as Tensors. Protocol buffer exposes various configuration options for a session. The Op definations are stored in ops.pb file.
# Official documentation of {session}[https://www.tensorflow.org/api_docs/python/client/session_management#Session] and {Operation}[https://www.tensorflow.org/api_docs/python/framework/core_graph_data_structures#Operation].
class Tensorflow::Session
attr_accessor :status, :ops, :session, :graph, :c
# @!attribute dimensions
# Create a success status.
# @!attribute ops
# Nodes in the graph are called ops (short for operations). An op takes zero or more Tensors, performs some computation, and produces zero or more Tensors.
# @!attribute graph
# A TensorFlow graph is a description of computations. To compute anything, a graph must be launched in a Session. A Session places the graph ops and provides methods to execute them.
def initialize(graph, c_options = nil)
c_options = Tensorflow::Session_options.new if c_options.nil?
self.status = Tensorflow::Status.new
cOpt = c_options.c
c_session = Tensorflow::TF_NewSession(graph.c, cOpt, status.c)
Tensorflow::TF_DeleteSessionOptions(cOpt)
raise 'Error in session initialization.' if status.code != 0
self.c = c_session
end
# Run the graph with the associated session starting with the supplied feeds
# to compute the value of the requested fetches. Runs, but does not return
# Tensors for operations specified in targets.
#
# On success, returns the fetched Tensors in the same order as supplied in
# the fetches argument. If fetches is set to nil, the returned Tensor fetches
# is empty.
#
# Note that the caller maintains responsibility for the input tensors, so
# the caller should still call tensor.delete() on each input tensor
def run(inputs, outputs, targets)
inputPorts = Tensorflow::TF_Output_vector.new
inputValues = Tensorflow::Tensor_Vector.new
inputs.each do |port, tensor|
inputPorts.push(port.c)
inputValues.push(tensor.tensor)
end
outputPorts = Tensorflow::TF_Output_vector.new
outputs.each do |output|
outputPorts.push(output.c)
end
cTargets = Tensorflow::TF_Operation_vector.new
targets.each do |targe|
cTargets.push(targe.c)
end
outputValues = Tensorflow::Session_run(c, inputPorts, inputValues, outputPorts, cTargets)
output_array = []
outputValues.each do |value|
converted_value = convert_value_for_output_array(value)
output_array.push(converted_value)
# Since we're returning the results as an array, there's no point keeping
# the output tensor, so we delete it.
Tensorflow::TF_DeleteTensor(value)
end
output_array
end
def close
status = Tensorflow::Status.new
Tensorflow::TF_CloseSession(c, status.c)
raise 'Error in closing session.' if status.code != 0
Tensorflow::TF_DeleteSession(c, status.c)
raise 'Error in deleting session.' if status.code != 0
self.c = nil
end
private
def initialize_inputs(inputs)
input_names = Tensorflow::String_Vector.new
input_values = Tensorflow::Tensor_Vector.new
unless inputs.nil?
inputs.each do |key, value|
input_values.push(value)
input_names.push(key)
end
end
[input_names, input_values]
end
def initialize_outputs(outputs)
output_names = Tensorflow::String_Vector.new
unless outputs.nil?
outputs.each do |name|
output_names.push(name)
end
end
output_values = Tensorflow::Tensor_Vector.new
[output_names, output_values]
end
def initialize_targets(targets)
target_names = Tensorflow::String_Vector.new
unless targets.nil?
targets.each do |name|
target_names.push(name)
end
end
target_names
end
def convert_value_for_output_array(value)
type = Tensorflow::TF_TensorType(value)
if type == Tensorflow::TF_STRING
return Tensorflow::String_decoder(value)
end
size = Tensorflow.tensor_size(value)
c_array = construct_c_array(value, size)
length_by_dimension = length_by_dimension(value)
arrange_into_dimensions(c_array, size, length_by_dimension)
end
# Returns an array containing the length of the tensor in each dimension
def length_by_dimension(value)
num_dimensions = Tensorflow::TF_NumDims(value)
(0..num_dimensions - 1).each_with_object([]) do |dimension, array|
array.push(Tensorflow::TF_Dim(value, dimension))
end
end
def construct_c_array(value, size)
type = Tensorflow::TF_TensorType(value)
case type
when Tensorflow::TF_FLOAT
c_array = Tensorflow::Float.new(size)
Tensorflow.float_reader(value, c_array, size)
when Tensorflow::TF_DOUBLE
c_array = Tensorflow::Double.new(size)
Tensorflow.double_reader(value, c_array, size)
when Tensorflow::TF_INT32
c_array = Tensorflow::Int.new(size)
Tensorflow.int_reader(value, c_array, size)
when Tensorflow::TF_INT64
c_array = Tensorflow::Long_long.new(size)
Tensorflow.long_long_reader(value, c_array, size)
when Tensorflow::TF_COMPLEX128
c_array = Tensorflow.complex_reader(value)
# Add support for bool, uint and other data types.
else
raise 'Data type not supported.'
end
c_array
end
# Arrange a flat array into an array of arrays organized by tensor dimensions
def arrange_into_dimensions(c_array, size, length_by_dimension)
output = []
(0..size - 1).each do |j|
output.push(c_array[j])
end
length_by_dimension.reverse!
(0..length_by_dimension.length - 2).each do |dim|
all_dimensions = []
one_dimension = []
output.each do |val|
one_dimension.push(val)
if one_dimension.length == length_by_dimension[dim]
all_dimensions.push(one_dimension)
one_dimension = []
end
end
output = all_dimensions
end
output
end
#
# Converts a give ruby array to C array (using SWIG) by detecting the data type automatically.
# Design decision needs to be made regarding this so the all the data types are supported.
# Currently Integer(Ruby) is converted to long long(C) and Float(Ruby) is converted double(C).
#
# * *Returns* :
# - A c array.
#
def graph_def_to_c_array(array)
c_array = Tensorflow::Character.new(array.length)
(0..array.length - 1).each do |i|
c_array[i] = array[i]
end
c_array
end
end