Package rdkit :: Package ML :: Package Neural :: Module Network
[hide private]
[frames] | no frames]

Source Code for Module rdkit.ML.Neural.Network

  1  # 
  2  #  Copyright (C) 2000-2008  greg Landrum 
  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 __future__ import print_function 
 23  import numpy 
 24  import random 
 25   
 26  from rdkit.ML.Neural import NetNode, ActFuncs 
 27   
 28   
 29  # FIX: this class has not been updated to new-style classes 
 30  # (RD Issue380) because that would break all of our legacy pickled 
 31  # data. Until a solution is found for this breakage, an update is 
 32  # impossible. 
33 -class Network:
34 """ a neural network 35 36 """ 37
38 - def ConstructRandomWeights(self, minWeight=-1, maxWeight=1):
39 """initialize all the weights in the network to random numbers 40 41 **Arguments** 42 43 - minWeight: the minimum value a weight can take 44 45 - maxWeight: the maximum value a weight can take 46 47 """ 48 for node in self.nodeList: 49 inputs = node.GetInputs() 50 if inputs: 51 weights = [random.uniform(minWeight, maxWeight) for _ in range(len(inputs))] 52 node.SetWeights(weights)
53
54 - def FullyConnectNodes(self):
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 = list(range(self.numInputNodes)) 63 nConnections = 0 64 for layer in range(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
75 - def ConstructNodes(self, nodeCounts, actFunc, actFuncParms):
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 range(self.numHiddenLayers): 91 self.numInHidden[i] = nodeCounts[i + 1] 92 93 numNodes = sum(self.nodeCounts) 94 self.nodeList = [None] * (numNodes) 95 for i in range(numNodes): 96 self.nodeList[i] = NetNode.NetNode(i, self.nodeList, actFunc=actFunc, 97 actFuncParms=actFuncParms) 98 99 self.layerIndices = [None] * len(nodeCounts) 100 start = 0 101 for i in range(len(nodeCounts)): 102 end = start + nodeCounts[i] 103 self.layerIndices[i] = list(range(start, end)) 104 start = end
105
106 - def GetInputNodeList(self):
107 """ returns a list of input node indices 108 """ 109 return self.layerIndices[0]
110
111 - def GetOutputNodeList(self):
112 """ returns a list of output node indices 113 """ 114 return self.layerIndices[-1]
115
116 - def GetHiddenLayerNodeList(self, which):
117 """ returns a list of hidden nodes in the specified layer 118 """ 119 return self.layerIndices[which + 1]
120
121 - def GetNumNodes(self):
122 """ returns the total number of nodes 123 """ 124 return sum(self.nodeCounts)
125
126 - def GetNumHidden(self):
127 """ returns the number of hidden layers 128 """ 129 return self.numHiddenLayers
130
131 - def GetNode(self, which):
132 """ returns a particular node 133 """ 134 return self.nodeList[which]
135
136 - def GetAllNodes(self):
137 """ returns a list of all nodes 138 """ 139 return self.nodeList
140
141 - def ClassifyExample(self, example, appendExamples=0):
142 """ classifies a given example and returns the results of the output layer. 143 144 **Arguments** 145 146 - example: the example to be classified 147 148 **NOTE:** 149 150 if the output layer is only one element long, 151 a scalar (not a list) will be returned. This is why a lot of the other 152 network code claims to only support single valued outputs. 153 154 """ 155 if len(example) > self.numInputNodes: 156 if len(example) - self.numInputNodes > self.numOutputNodes: 157 example = example[1:-self.numOutputNodes] 158 else: 159 example = example[:-self.numOutputNodes] 160 assert len(example) == self.numInputNodes 161 totNumNodes = sum(self.nodeCounts) 162 results = numpy.zeros(totNumNodes, numpy.float64) 163 for i in range(self.numInputNodes): 164 results[i] = example[i] 165 for i in range(self.numInputNodes, totNumNodes): 166 self.nodeList[i].Eval(results) 167 self.lastResults = results[:] 168 if self.numOutputNodes == 1: 169 return results[-1] 170 else: 171 return results
172
173 - def GetLastOutputs(self):
174 """ returns the complete list of output layer values from the last time this node 175 classified anything""" 176 return self.lastResults
177
178 - def __str__(self):
179 """ provides a string representation of the network """ 180 outStr = 'Network:\n' 181 for i in range(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, actFunc=ActFuncs.Sigmoid, actFuncParms=(), 190 weightBounds=1):
191 """ Constructor 192 193 This constructs and initializes the network based upon the specified 194 node counts. 195 196 A fully connected network with random weights is constructed. 197 198 **Arguments** 199 200 - nodeCounts: a list containing the number of nodes to be in each layer. 201 the ordering is: 202 (nInput,nHidden1,nHidden2, ... , nHiddenN, nOutput) 203 204 - nodeConnections: I don't know why this is here, but it's optional. ;-) 205 206 - actFunc: the activation function to be used here. Must support the API 207 of _ActFuncs.ActFunc_. 208 209 - actFuncParms: a tuple of extra arguments to be passed to the activation function 210 constructor. 211 212 - weightBounds: a float which provides the boundary on the random initial weights 213 214 """ 215 self.ConstructNodes(nodeCounts, actFunc, actFuncParms) 216 self.FullyConnectNodes() 217 self.ConstructRandomWeights(minWeight=-weightBounds, maxWeight=weightBounds) 218 self.lastResults = []
219 220 221 if __name__ == '__main__': # pragma: nocover 222 223 print('[2,2,2]') 224 net = Network([2, 2, 2]) 225 print(net) 226 227 print('[2,4,1]') 228 net = Network([2, 4, 1]) 229 print(net) 230 231 print('[2,2]') 232 net = Network([2, 2]) 233 print(net) 234 inp = [1, 0] 235 res = net.ClassifyExample(inp) 236 print(inp, '->', res) 237 inp = [0, 1] 238 res = net.ClassifyExample(inp) 239 print(inp, '->', res) 240 inp = [.5, .5] 241 res = net.ClassifyExample(inp) 242 print(inp, '->', res) 243