rdkit.ML.Neural.Network

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

# Source Code for Module rdkit.ML.Neural.Network

      1  ## Automatically adapted for numpy.oldnumeric Jun 27, 2008 by -c
      2
      3  #
      4  #  Copyright (C) 2000-2008  greg Landrum
      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 rdkit.ML.Neural import NetNode, ActFuncs
     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 -  def ConstructRandomWeights(self,minWeight=-1,maxWeight=1):
     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
     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 = 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
     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 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
    107 -  def GetInputNodeList(self):
    108      """ returns a list of input node indices
    109      """
    110      return self.layerIndices[0]
    111 -  def GetOutputNodeList(self):
    112      """ returns a list of output node indices
    113      """
    114      return self.layerIndices[-1]
    115 -  def GetHiddenLayerNodeList(self,which):
    116      """ returns a list of hidden nodes in the specified layer
    117      """
    118      return self.layerIndices[which+1]
    119
    120 -  def GetNumNodes(self):
    121      """ returns the total number of nodes
    122      """
    123      return sum(self.nodeCounts)
    124
    125 -  def GetNumHidden(self):
    126      """ returns the number of hidden layers
    127      """
    128      return self.numHiddenLayers
    129
    130 -  def GetNode(self,which):
    131      """ returns a particular node
    132      """
    133      return self.nodeList[which]
    134 -  def GetAllNodes(self):
    135      """ returns a list of all nodes
    136      """
    137      return self.nodeList
    138
    139 -  def ClassifyExample(self,example,appendExamples=0):
    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
    171 -  def GetLastOutputs(self):
    172      """ returns the complete list of output layer values from the last time this node classified anything"""
    173      return self.lastResults
    174
    175 -  def __str__(self):
    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