In [1]:
"""
Fix the Sigmoid class so that it computes the sigmoid function
on the forward pass!
Scroll down to get started.
"""
import numpy as np
In [2]:
class Node(object):
def __init__(self, inbound_nodes=[]):
self.inbound_nodes = inbound_nodes
self.value = None
self.outbound_nodes = []
for node in inbound_nodes:
node.outbound_nodes.append(self)
def forward():
raise NotImplementedError
In [3]:
class Input(Node):
def __init__(self):
# An Input node has no inbound nodes,
# so no need to pass anything to the Node instantiator
Node.__init__(self)
def forward(self):
# Do nothing because nothing is calculated.
pass
In [4]:
class Linear(Node):
def __init__(self, X, W, b):
# Notice the ordering of the input nodes passed to the
# Node constructor.
Node.__init__(self, [X, W, b])
def forward(self):
X = self.inbound_nodes[0].value
W = self.inbound_nodes[1].value
b = self.inbound_nodes[2].value
self.value = np.dot(X, W) + b
In [12]:
class Sigmoid(Node):
"""
You need to fix the `_sigmoid` and `forward` methods.
"""
def __init__(self, node):
Node.__init__(self, [node])
def _sigmoid(self, x):
"""
This method is separate from `forward` because it
will be used later with `backward` as well.
`x`: A numpy array-like object.
Return the result of the sigmoid function.
Your code here!
"""
return 1.0 / (1.0 + np.exp(-x))
def forward(self):
"""
Set the value of this node to the result of the
sigmoid function, `_sigmoid`.
Your code here!
"""
# This is a dummy value to prevent numpy errors
# if you test without changing this method.
#self.value = -1
self.value = self._sigmoid(self.inbound_nodes[0].value)
In [13]:
def topological_sort(feed_dict):
"""
Sort the nodes in topological order using Kahn's Algorithm.
`feed_dict`: A dictionary where the key is a `Input` Node and the value is the respective value feed to that Node.
Returns a list of sorted nodes.
"""
input_nodes = [n for n in feed_dict.keys()]
G = {}
nodes = [n for n in input_nodes]
while len(nodes) > 0:
n = nodes.pop(0)
if n not in G:
G[n] = {'in': set(), 'out': set()}
for m in n.outbound_nodes:
if m not in G:
G[m] = {'in': set(), 'out': set()}
G[n]['out'].add(m)
G[m]['in'].add(n)
nodes.append(m)
L = []
S = set(input_nodes)
while len(S) > 0:
n = S.pop()
if isinstance(n, Input):
n.value = feed_dict[n]
L.append(n)
for m in n.outbound_nodes:
G[n]['out'].remove(m)
G[m]['in'].remove(n)
# if no other incoming edges add to S
if len(G[m]['in']) == 0:
S.add(m)
return L
def forward_pass(output_node, sorted_nodes):
"""
Performs a forward pass through a list of sorted Nodes.
Arguments:
`output_node`: A Node in the graph, should be the output node (have no outgoing edges).
`sorted_nodes`: a topologically sorted list of nodes.
Returns the output node's value
"""
for n in sorted_nodes:
n.forward()
return output_node.value
In [14]:
if __name__ == "__main__":
"""
This network feeds the output of a linear transform
to the sigmoid function.
Finish implementing the Sigmoid class in miniflow.py!
Feel free to play around with this network, too!
"""
#import numpy as np
#from miniflow import *
X, W, b = Input(), Input(), Input()
f = Linear(X, W, b)
g = Sigmoid(f)
X_ = np.array([[-1., -2.], [-1, -2]])
W_ = np.array([[2., -3], [2., -3]])
b_ = np.array([-3., -5])
feed_dict = {X: X_, W: W_, b: b_}
graph = topological_sort(feed_dict)
output = forward_pass(g, graph)
"""
Output should be:
[[ 1.23394576e-04 9.82013790e-01]
[ 1.23394576e-04 9.82013790e-01]]
"""
print(output)
In [ ]: