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  from __future__ import print_function
     25  import numpy
     26  import random
     27
     28  from rdkit.six.moves import xrange
     29  from rdkit.ML.Neural import NetNode, ActFuncs
     30
     31  # FIX: this class has not been updated to new-style classes
     32  # (RD Issue380) because that would break all of our legacy pickled
     33  # data. Until a solution is found for this breakage, an update is
     34  # impossible.
     35 -class Network:
     36    """ a neural network
     37
     38    """
     39 -  def ConstructRandomWeights(self,minWeight=-1,maxWeight=1):
     40      """initialize all the weights in the network to random numbers
     41
     42        **Arguments**
     43
     44          - minWeight: the minimum value a weight can take
     45
     46          - maxWeight: the maximum value a weight can take
     47
     48      """
     49      for node in self.nodeList:
     50        inputs = node.GetInputs()
     51        if inputs:
     52          weights = [random.uniform(minWeight,maxWeight) for x in range(len(inputs))]
     53          node.SetWeights(weights)
     54
     55
     56 -  def FullyConnectNodes(self):
     57      """ Fully connects each layer in the network to the one above it
     58
     59
     60       **Note**
     61         this sets the connections, but does not assign weights
     62
     63      """
     64      nodeList = range(self.numInputNodes)
     65      nConnections = 0
     66      for layer in xrange(self.numHiddenLayers):
     67        for i in self.layerIndices[layer+1]:
     68          self.nodeList[i].SetInputs(nodeList)
     69          nConnections = nConnections + len(nodeList)
     70        nodeList = self.layerIndices[layer+1]
     71
     72      for i in self.layerIndices[-1]:
     73        self.nodeList[i].SetInputs(nodeList)
     74        nConnections = nConnections + len(nodeList)
     75      self.nConnections = nConnections
     76
     77 -  def ConstructNodes(self,nodeCounts,actFunc,actFuncParms):
     78      """ build an unconnected network and set node counts
     79
     80        **Arguments**
     81
     82          - nodeCounts: a list containing the number of nodes to be in each layer.
     83             the ordering is:
     84              (nInput,nHidden1,nHidden2, ... , nHiddenN, nOutput)
     85
     86      """
     87      self.nodeCounts = nodeCounts
     88      self.numInputNodes = nodeCounts[0]
     89      self.numOutputNodes = nodeCounts[-1]
     90      self.numHiddenLayers = len(nodeCounts)-2
     91      self.numInHidden = [None]*self.numHiddenLayers
     92      for i in xrange(self.numHiddenLayers):
     93        self.numInHidden[i] = nodeCounts[i+1]
     94
     95      numNodes = sum(self.nodeCounts)
     96      self.nodeList = [None]*(numNodes)
     97      for i in xrange(numNodes):
     98        self.nodeList[i] = NetNode.NetNode(i,self.nodeList,
     99                                           actFunc=actFunc,
    100                                           actFuncParms=actFuncParms)
    101
    102      self.layerIndices = [None]*len(nodeCounts)
    103      start = 0
    104      for i in xrange(len(nodeCounts)):
    105        end = start + nodeCounts[i]
    106        self.layerIndices[i] = range(start,end)
    107        start = end
    108
    109 -  def GetInputNodeList(self):
    110      """ returns a list of input node indices
    111      """
    112      return self.layerIndices[0]
    113 -  def GetOutputNodeList(self):
    114      """ returns a list of output node indices
    115      """
    116      return self.layerIndices[-1]
    117 -  def GetHiddenLayerNodeList(self,which):
    118      """ returns a list of hidden nodes in the specified layer
    119      """
    120      return self.layerIndices[which+1]
    121
    122 -  def GetNumNodes(self):
    123      """ returns the total number of nodes
    124      """
    125      return sum(self.nodeCounts)
    126
    127 -  def GetNumHidden(self):
    128      """ returns the number of hidden layers
    129      """
    130      return self.numHiddenLayers
    131
    132 -  def GetNode(self,which):
    133      """ returns a particular node
    134      """
    135      return self.nodeList[which]
    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 xrange(self.numInputNodes):
    164        results[i] = example[i]
    165      for i in xrange(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 classified anything"""
    175      return self.lastResults
    176
    177 -  def __str__(self):
    178      """ provides a string representation of the network """
    179      outStr = 'Network:\n'
    180      for i in xrange(len(self.nodeList)):
    181        outStr = outStr + '\tnode(% 3d):\n'%i
    182        outStr = outStr + '\t\tinputs:  %s\n'%(str(self.nodeList[i].GetInputs()))
    183        outStr = outStr + '\t\tweights: %s\n'%(str(self.nodeList[i].GetWeights()))
    184
    185      outStr = outStr + 'Total Number of Connections: % 4d'%self.nConnections
    186      return outStr
    187
    188 -  def __init__(self,nodeCounts,nodeConnections=None,
    189                 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
    216      """
    217      self.ConstructNodes(nodeCounts,actFunc,actFuncParms)
    218      self.FullyConnectNodes()
    219      self.ConstructRandomWeights(minWeight=-weightBounds,maxWeight=weightBounds)
    220      self.lastResults = []
    221
    222  if __name__ == '__main__':
    223
    224    print('[2,2,2]')
    225    net = Network([2,2,2])
    226    print(net)
    227
    228    print('[2,4,1]')
    229    net = Network([2,4,1])
    230    print(net)
    231
    232    print('[2,2]')
    233    net = Network([2,2])
    234    print(net)
    235    input = [1,0]
    236    res = net.ClassifyExample(input)
    237    print(input,'->',res)
    238    input = [0,1]
    239    res = net.ClassifyExample(input)
    240    print(input,'->',res)
    241    input = [.5,.5]
    242    res = net.ClassifyExample(input)
    243    print(input,'->',res)
    244