1
2
3
4
5
6 """ Contains the class _Network_ which is used to represent neural nets
7
8 **Network Architecture:**
9
10 A tacit assumption in all of this stuff is that we're dealing with
11 feedforward networks.
12
13 The network itself is stored as a list of _NetNode_ objects. The list
14 is ordered in the sense that nodes in earlier/later layers than a
15 given node are guaranteed to come before/after that node in the list.
16 This way we can easily generate the values of each node by moving
17 sequentially through the list, we're guaranteed that every input for a
18 node has already been filled in.
19
20 Each node stores a list (_inputNodes_) of indices of its inputs in the
21 main node list.
22
23 """
24 import numpy
25 import random
26
27 from ML.Neural import NetNode, ActFuncs
28
29
30
31
32
34 """ a neural network
35
36 """
38 """initialize all the weights in the network to random numbers
39
40 **Arguments**
41
42 - minWeight: the minimum value a weight can take
43
44 - maxWeight: the maximum value a weight can take
45
46 """
47 for node in self.nodeList:
48 inputs = node.GetInputs()
49 if inputs:
50 weights = [random.uniform(minWeight,maxWeight) for x in range(len(inputs))]
51 node.SetWeights(weights)
52
53
55 """ Fully connects each layer in the network to the one above it
56
57
58 **Note**
59 this sets the connections, but does not assign weights
60
61 """
62 nodeList = range(self.numInputNodes)
63 nConnections = 0
64 for layer in xrange(self.numHiddenLayers):
65 for i in self.layerIndices[layer+1]:
66 self.nodeList[i].SetInputs(nodeList)
67 nConnections = nConnections + len(nodeList)
68 nodeList = self.layerIndices[layer+1]
69
70 for i in self.layerIndices[-1]:
71 self.nodeList[i].SetInputs(nodeList)
72 nConnections = nConnections + len(nodeList)
73 self.nConnections = nConnections
74
76 """ build an unconnected network and set node counts
77
78 **Arguments**
79
80 - nodeCounts: a list containing the number of nodes to be in each layer.
81 the ordering is:
82 (nInput,nHidden1,nHidden2, ... , nHiddenN, nOutput)
83
84 """
85 self.nodeCounts = nodeCounts
86 self.numInputNodes = nodeCounts[0]
87 self.numOutputNodes = nodeCounts[-1]
88 self.numHiddenLayers = len(nodeCounts)-2
89 self.numInHidden = [None]*self.numHiddenLayers
90 for i in xrange(self.numHiddenLayers):
91 self.numInHidden[i] = nodeCounts[i+1]
92
93 numNodes = sum(self.nodeCounts)
94 self.nodeList = [None]*(numNodes)
95 for i in xrange(numNodes):
96 self.nodeList[i] = NetNode.NetNode(i,self.nodeList,
97 actFunc=actFunc,
98 actFuncParms=actFuncParms)
99
100 self.layerIndices = [None]*len(nodeCounts)
101 start = 0
102 for i in xrange(len(nodeCounts)):
103 end = start + nodeCounts[i]
104 self.layerIndices[i] = range(start,end)
105 start = end
106
112 """ returns a list of output node indices
113 """
114 return self.layerIndices[-1]
116 """ returns a list of hidden nodes in the specified layer
117 """
118 return self.layerIndices[which+1]
119
121 """ returns the total number of nodes
122 """
123 return sum(self.nodeCounts)
124
126 """ returns the number of hidden layers
127 """
128 return self.numHiddenLayers
129
131 """ returns a particular node
132 """
133 return self.nodeList[which]
135 """ returns a list of all nodes
136 """
137 return self.nodeList
138
140 """ classifies a given example and returns the results of the output layer.
141
142 **Arguments**
143
144 - example: the example to be classified
145
146 **NOTE:**
147
148 if the output layer is only one element long,
149 a scalar (not a list) will be returned. This is why a lot of the other
150 network code claims to only support single valued outputs.
151
152 """
153 if len(example) > self.numInputNodes:
154 if len(example)-self.numInputNodes > self.numOutputNodes:
155 example = example[1:-self.numOutputNodes]
156 else:
157 example = example[:-self.numOutputNodes]
158 assert len(example) == self.numInputNodes
159 totNumNodes = sum(self.nodeCounts)
160 results = numpy.zeros(totNumNodes,numpy.float64)
161 for i in xrange(self.numInputNodes):
162 results[i] = example[i]
163 for i in xrange(self.numInputNodes,totNumNodes):
164 self.nodeList[i].Eval(results)
165 self.lastResults = results[:]
166 if self.numOutputNodes == 1:
167 return results[-1]
168 else:
169 return results
170
172 """ returns the complete list of output layer values from the last time this node classified anything"""
173 return self.lastResults
174
176 """ provides a string representation of the network """
177 outStr = 'Network:\n'
178 for i in xrange(len(self.nodeList)):
179 outStr = outStr + '\tnode(% 3d):\n'%i
180 outStr = outStr + '\t\tinputs: %s\n'%(str(self.nodeList[i].GetInputs()))
181 outStr = outStr + '\t\tweights: %s\n'%(str(self.nodeList[i].GetWeights()))
182
183 outStr = outStr + 'Total Number of Connections: % 4d'%self.nConnections
184 return outStr
185
186 - def __init__(self,nodeCounts,nodeConnections=None,
187 actFunc=ActFuncs.Sigmoid,actFuncParms=(),
188 weightBounds=1):
189 """ Constructor
190
191 This constructs and initializes the network based upon the specified
192 node counts.
193
194 A fully connected network with random weights is constructed.
195
196 **Arguments**
197
198 - nodeCounts: a list containing the number of nodes to be in each layer.
199 the ordering is:
200 (nInput,nHidden1,nHidden2, ... , nHiddenN, nOutput)
201
202 - nodeConnections: I don't know why this is here, but it's optional. ;-)
203
204 - actFunc: the activation function to be used here. Must support the API
205 of _ActFuncs.ActFunc_.
206
207 - actFuncParms: a tuple of extra arguments to be passed to the activation function
208 constructor.
209
210 - weightBounds: a float which provides the boundary on the random initial weights
211
212
213
214 """
215 self.ConstructNodes(nodeCounts,actFunc,actFuncParms)
216 self.FullyConnectNodes()
217 self.ConstructRandomWeights(minWeight=-weightBounds,maxWeight=weightBounds)
218 self.lastResults = []
219
220 if __name__ == '__main__':
221
222 print '[2,2,2]'
223 net = Network([2,2,2])
224 print net
225
226 print '[2,4,1]'
227 net = Network([2,4,1])
228 print net
229
230 print '[2,2]'
231 net = Network([2,2])
232 print net
233 input = [1,0]
234 res = net.ClassifyExample(input)
235 print input,'->',res
236 input = [0,1]
237 res = net.ClassifyExample(input)
238 print input,'->',res
239 input = [.5,.5]
240 res = net.ClassifyExample(input)
241 print input,'->',res
242