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