1class Operation
2 attr_accessor :input_nodes, :output, :input
3
4 def initialize(*input_nodes)
5 @input_nodes = input_nodes
6 end
7
8 def compute
9 raise "Should be implemented"
10 end
11
12endThe Operation class forms the basis of all the computation in tensorflow. The compute method carries out the computation required. Let us create few classes which inherit the Operation class and we will use sigmoid as the activation function for the model
1require Matrix
2class Add < Operation
3 def initialize(*nodes)
4 @input_nodes=nodes
5 end
6
7 def compute(x,y)
8 return x.element(0,0) + y
9 end
10end
11
12class Matmul < Operation
13 def initialize(*nodes)
14 @input_nodes=nodes
15 end
16
17 def compute(x,y)
18 return Matrix[x] * Matrix[y].transpose
19 end
20end
21
22class Sigmoid < Operation
23 def initialize(*nodes)
24 @input_nodes=nodes
25 end
26
27 def compute(z)
28 1/(1+Math.exp(-z))
29 end
30endLoad the Matrix library at the begining for matrix computation. Now that we have created basic operation classes we will proceed to other components.
1class Placeholder
2 attr_accessor :value, :output
3end
4
5class Variable
6 attr_accessor :value, :output
7
8 def initialize(value)
9 @value = value
10 end
11endThe Placeholder class holds objects which are input or output of the model. These object act as constant and their values dont change during the session. The Variable holds objects whose values are either weights or bias given to the model. In the actual tensorflow model these values are updated using the optimizer(eg AdamOptimizer, Stocastic Gradient Descent etc). We wont be updating any value in our approach. We are fixing the value for weight and bias as [1,1] and -5 respectively. Now we will define the session class.
1class Session
2
3 def run(operation,feed_dict={})
4 nodes = traverse_postorder(operation)
5 nodes.each do |node|
6 if node.is_a? Placeholder
7 node.output = feed_dict[node.object_id]
8 elsif node.is_a? Variable
9 node.output = node.value
10 else
11 node.input = node.input_nodes.map { |input| input.output }
12 node.output = node.compute(*node.input)
13 end
14 end
15 operation.output
16 end
17
18 def traverse_postorder(operation)
19 node_operators = []
20 recurse(operation,node_operators)
21 end
22
23 def recurse(node,node_operators)
24 if node.is_a?(Operation)
25 node.input_nodes.each do |input_node|
26 recurse(input_node,node_operators)
27 end
28 end
29 node_operators << node
30 end
31
32endThe Session class takes care of all the executions. It first converts the set of operations to postfix order. The operations are then executed one by one. Since we have created all the components required, lets jump into action.
1W = Variable.new([1,1]) #Weight
2b = Variable.new(-5) #bias
3x = Placeholder.new() #input
4y = Matmul.new(W,x)
5z = Add.new(y,b)
6a = Sigmoid.new(z) #activation function
7sess = Session.new()
Lets see the prediction for two extreme points
result = sess.run(a,feed_dict={x.object_id=>[0,-10]})
puts(result)%3.059022269256247e-07The point (0,-10) lies towards the lower left corner of the graph hence the sigmoid value is closer to 0
result = sess.run(a,feed_dict={x.object_id=>[8,10]})
puts(result)%0.999997739675702For the point (8,10) which lies near the upper right corner of the graph the sigmoid value is closer to 1.
Hope this example gives a good understanding of basic components of tensorflow in ruby. Happy learning !!!
