Files
rdkit/Python/ML/Neural/Network.py
Greg Landrum 75a79b6327 initial import
2006-05-06 22:20:08 +00:00

245 lines
7.4 KiB
Python
Executable File

#
# Copyright (C) 2000 greg Landrum
#
""" Contains the class _Network_ which is used to represent neural nets
**Network Architecture:**
A tacit assumption in all of this stuff is that we're dealing with
feedforward networks.
The network itself is stored as a list of _NetNode_ objects. The list
is ordered in the sense that nodes in earlier/later layers than a
given node are guaranteed to come before/after that node in the list.
This way we can easily generate the values of each node by moving
sequentially through the list, we're guaranteed that every input for a
node has already been filled in.
Each node stores a list (_inputNodes_) of indices of its inputs in the
main node list.
"""
from Numeric import *
import RandomArray
from ML.Neural import NetNode, ActFuncs
# FIX: this class has not been updated to new-style classes
# (RD Issue380) because that would break all of our legacy pickled
# data. Until a solution is found for this breakage, an update is
# impossible.
class Network:
""" a neural network
"""
def ConstructRandomWeights(self,minWeight=-1,maxWeight=1):
"""initialize all the weights in the network to random numbers
**Arguments**
- minWeight: the minimum value a weight can take
- maxWeight: the maximum value a weight can take
**Note**
random numbers are assigned using _Numeric_'s _RandomArray_ module, so
if you want to be seeding the generators, be sure to hit that one too.
"""
for node in self.nodeList:
inputs = node.GetInputs()
if inputs:
weights = RandomArray.uniform(minWeight,maxWeight,
shape=[len(inputs)])
node.SetWeights(weights)
def FullyConnectNodes(self):
""" Fully connects each layer in the network to the one above it
**Note**
this sets the connections, but does not assign weights
"""
nodeList = range(self.numInputNodes)
nConnections = 0
for layer in xrange(self.numHiddenLayers):
for i in self.layerIndices[layer+1]:
self.nodeList[i].SetInputs(nodeList)
nConnections = nConnections + len(nodeList)
nodeList = self.layerIndices[layer+1]
for i in self.layerIndices[-1]:
self.nodeList[i].SetInputs(nodeList)
nConnections = nConnections + len(nodeList)
self.nConnections = nConnections
def ConstructNodes(self,nodeCounts,actFunc,actFuncParms):
""" build an unconnected network and set node counts
**Arguments**
- nodeCounts: a list containing the number of nodes to be in each layer.
the ordering is:
(nInput,nHidden1,nHidden2, ... , nHiddenN, nOutput)
"""
self.nodeCounts = nodeCounts
self.numInputNodes = nodeCounts[0]
self.numOutputNodes = nodeCounts[-1]
self.numHiddenLayers = len(nodeCounts)-2
self.numInHidden = [None]*self.numHiddenLayers
for i in xrange(self.numHiddenLayers):
self.numInHidden[i] = nodeCounts[i+1]
numNodes = sum(self.nodeCounts)
self.nodeList = [None]*(numNodes)
for i in xrange(numNodes):
self.nodeList[i] = NetNode.NetNode(i,self.nodeList,
actFunc=actFunc,
actFuncParms=actFuncParms)
self.layerIndices = [None]*len(nodeCounts)
start = 0
for i in xrange(len(nodeCounts)):
end = start + nodeCounts[i]
self.layerIndices[i] = range(start,end)
start = end
def GetInputNodeList(self):
""" returns a list of input node indices
"""
return self.layerIndices[0]
def GetOutputNodeList(self):
""" returns a list of output node indices
"""
return self.layerIndices[-1]
def GetHiddenLayerNodeList(self,which):
""" returns a list of hidden nodes in the specified layer
"""
return self.layerIndices[which+1]
def GetNumNodes(self):
""" returns the total number of nodes
"""
return sum(self.nodeCounts)
def GetNumHidden(self):
""" returns the number of hidden layers
"""
return self.numHiddenLayers
def GetNode(self,which):
""" returns a particular node
"""
return self.nodeList[which]
def GetAllNodes(self):
""" returns a list of all nodes
"""
return self.nodeList
def ClassifyExample(self,example,appendExamples=0):
""" classifies a given example and returns the results of the output layer.
**Arguments**
- example: the example to be classified
**NOTE:**
if the output layer is only one element long,
a scalar (not a list) will be returned. This is why a lot of the other
network code claims to only support single valued outputs.
"""
if len(example) > self.numInputNodes:
if len(example)-self.numInputNodes > self.numOutputNodes:
example = example[1:-self.numOutputNodes]
else:
example = example[:-self.numOutputNodes]
assert len(example) == self.numInputNodes
totNumNodes = sum(self.nodeCounts)
results = zeros(totNumNodes,Float64)
for i in xrange(self.numInputNodes):
results[i] = example[i]
for i in xrange(self.numInputNodes,totNumNodes):
self.nodeList[i].Eval(results)
self.lastResults = results[:]
if self.numOutputNodes == 1:
return results[-1]
else:
return results
def GetLastOutputs(self):
""" returns the complete list of output layer values from the last time this node classified anything"""
return self.lastResults
def __str__(self):
""" provides a string representation of the network """
outStr = 'Network:\n'
for i in xrange(len(self.nodeList)):
outStr = outStr + '\tnode(% 3d):\n'%i
outStr = outStr + '\t\tinputs: %s\n'%(str(self.nodeList[i].GetInputs()))
outStr = outStr + '\t\tweights: %s\n'%(str(self.nodeList[i].GetWeights()))
outStr = outStr + 'Total Number of Connections: % 4d'%self.nConnections
return outStr
def __init__(self,nodeCounts,nodeConnections=None,
actFunc=ActFuncs.Sigmoid,actFuncParms=(),
weightBounds=1):
""" Constructor
This constructs and initializes the network based upon the specified
node counts.
A fully connected network with random weights is constructed.
**Arguments**
- nodeCounts: a list containing the number of nodes to be in each layer.
the ordering is:
(nInput,nHidden1,nHidden2, ... , nHiddenN, nOutput)
- nodeConnections: I don't know why this is here, but it's optional. ;-)
- actFunc: the activation function to be used here. Must support the API
of _ActFuncs.ActFunc_.
- actFuncParms: a tuple of extra arguments to be passed to the activation function
constructor.
- weightBounds: a float which provides the boundary on the random initial weights
"""
self.ConstructNodes(nodeCounts,actFunc,actFuncParms)
self.FullyConnectNodes()
self.ConstructRandomWeights(minWeight=-weightBounds,maxWeight=weightBounds)
self.lastResults = []
if __name__ == '__main__':
print '[2,2,2]'
net = Network([2,2,2])
print net
print '[2,4,1]'
net = Network([2,4,1])
print net
print '[2,2]'
net = Network([2,2])
print net
input = [1,0]
res = net.ClassifyExample(input)
print input,'->',res
input = [0,1]
res = net.ClassifyExample(input)
print input,'->',res
input = [.5,.5]
res = net.ClassifyExample(input)
print input,'->',res