Package rdkit :: Package ML :: Package Neural :: Module Trainers

[hide private]
[frames] | no frames]

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

      1  ## Automatically adapted for numpy.oldnumeric Jun 27, 2008 by -c
      2
      3  #
      4  #  Copyright (C) 2000-2008  greg Landrum
      5  #
      6  """ Training algorithms for feed-forward neural nets
      7
      8    Unless noted otherwise, algorithms and notation are taken from:
      9    "Artificial Neural Networks: Theory and Applications",
     10      Dan W. Patterson, Prentice Hall, 1996
     11
     12  """
     13  from __future__ import print_function
     14  import numpy
     15
     16  from rdkit.six.moves import xrange
     17
     18 -class Trainer(object):
     19    """ "virtual base class" for network trainers
     20
     21    """
     22    pass
     23
     24 -class BackProp(Trainer):
     25    """implement back propagation (algorithm on pp 153-154 of Patterson)
     26
     27     I don't *think* that I've made any assumptions about the connectivity of
     28       the net (i.e. full connectivity between layers is not required).
     29
     30     **NOTE:** this code is currently making the assumption that the activation
     31       functions on the nodes in the network are capable of calculating their
     32       derivatives using only their values (i.e. a DerivFromVal method should
     33       exist).  This shouldn't be too hard to change.
     34
     35    """
     36 -  def StepUpdate(self,example,net,resVect=None):
     37      """ does a BackProp step based upon the example
     38
     39        **Arguments**
     40
     41          - example: a 2-tuple:
     42             1) a list of variable values values
     43             2) a list of result values (targets)
     44
     45          - net: a _Network_ (or something supporting the same API)
     46
     47          - resVect: if this is nonzero, then the network is not required to
     48            classify the _example_
     49
     50        **Returns**
     51
     52          the backprop error from _network_ **before the update**
     53
     54        **Note**
     55
     56          In case it wasn't blindingly obvious, the weights in _network_ are modified
     57          in the course of taking a backprop step.
     58
     59      """
     60      totNumNodes = net.GetNumNodes()
     61      if self.oldDeltaW is None:
     62        self.oldDeltaW = numpy.zeros(totNumNodes,numpy.float64)
     63      outputNodeList = net.GetOutputNodeList()
     64      nOutput = len(outputNodeList)
     65      targetVect = numpy.array(example[-nOutput:],numpy.float64)
     66      trainVect = example[:-nOutput]
     67      if resVect is None:
     68        # classify the example
     69        net.ClassifyExample(trainVect)
     70        resVect = net.GetLastOutputs()
     71      outputs = numpy.take(resVect,outputNodeList)
     72      errVect = targetVect - outputs
     73
     74      delta = numpy.zeros(totNumNodes,numpy.float64)
     75      # start with the output layer
     76      for i in xrange(len(outputNodeList)):
     77        idx = outputNodeList[i]
     78        node = net.GetNode(idx)
     79        # the deltas here are easy
     80        delta[idx] = errVect[i]*node.actFunc.DerivFromVal(resVect[idx])
     81        # use these results to start working on the deltas of the preceding layer
     82        inputs = node.GetInputs()
     83        weights = delta[idx]*node.GetWeights()
     84        for j in xrange(len(inputs)):
     85          idx2 = inputs[j]
     86          delta[idx2] = delta[idx2] + weights[j]
     87
     88      # now propagate the deltas backwards
     89      for layer in xrange(net.GetNumHidden()-1,-1,-1):
     90        nodesInLayer = net.GetHiddenLayerNodeList(layer)
     91        for idx in nodesInLayer:
     92          node = net.GetNode(idx)
     93          # start by finishing off the error term for this guy
     94          delta[idx] = delta[idx]*node.actFunc.DerivFromVal(resVect[idx])
     95
     96          # and then propagate our errors to the preceding layer
     97          if layer != 0:
     98            inputs = node.GetInputs()
     99            weights = delta[idx]*node.GetWeights()
    100            for i in xrange(len(inputs)):
    101              idx2 = inputs[i]
    102              delta[idx2] = delta[idx2] + weights[i]
    103
    104      # okey dokey... we've now got the deltas for each node, use those
    105      #  to update the weights (whew!)
    106      nHidden = net.GetNumHidden()
    107      for layer in xrange(0,nHidden+1):
    108        if layer == nHidden:
    109          idxList = net.GetOutputNodeList()
    110        else:
    111          idxList = net.GetHiddenLayerNodeList(layer)
    112        for idx in idxList:
    113          node = net.GetNode(idx)
    114          dW = self.speed * delta[idx] * numpy.take(resVect,node.GetInputs())
    115          newWeights = node.GetWeights() + dW
    116          node.SetWeights(newWeights)
    117
    118      # return the RMS error from the OLD network
    119      return numpy.sqrt(errVect*errVect)[0]
    120
    121
    122 -  def TrainOnLine(self,examples,net,maxIts=5000,errTol=0.1,useAvgErr=1,
    123                    silent=0):
    124      """ carries out online training of a neural net
    125
    126        The definition of online training is that the network is updated after
    127          each example is presented.
    128
    129        **Arguments**
    130
    131          - examples: a list of 2-tuple:
    132             1) a list of variable values values
    133             2) a list of result values (targets)
    134
    135          - net: a _Network_ (or something supporting the same API)
    136
    137          - maxIts: the maximum number of *training epochs* (see below for definition) to be
    138            run
    139
    140          - errTol: the tolerance for convergence
    141
    142          - useAvgErr: if this toggle is nonzero, then the error at each step will be
    143            divided by the number of training examples for the purposes of checking
    144            convergence.
    145
    146          - silent: controls the amount of visual noise produced as this runs.
    147
    148
    149        **Note**
    150
    151           a *training epoch* is one complete pass through all the training examples
    152
    153      """
    154      nExamples = len(examples)
    155      converged = 0
    156      cycle = 0
    157
    158      while (not converged) and (cycle < maxIts):
    159        maxErr = 0
    160        newErr = 0
    161        #print('bp: ',cycle)
    162        for example in examples:
    163          localErr = self.StepUpdate(example,net)
    164          newErr += localErr
    165          if localErr > maxErr:
    166            maxErr = localErr
    167        if useAvgErr == 1:
    168          newErr = newErr / nExamples
    169        else:
    170          newErr = maxErr
    171        #print('\t',newErr,errTol)
    172
    173        if newErr <= errTol:
    174          converged = 1
    175
    176  #      if cycle % 10 == 0 and not silent:
    177        if not silent:
    178          print('epoch %d, error: % 6.4f'%(cycle,newErr))
    179
    180        cycle = cycle + 1
    181      if not silent:
    182        if converged:
    183          print('Converged after %d epochs.'%cycle)
    184        else:
    185          print('NOT Converged after %d epochs.'%cycle)
    186        print('final error: % 6.4f'%newErr)
    187
    188 -  def __init__(self,speed=0.5,momentum=0.7):
    189      """ Constructor
    190
    191        **Arguments**
    192
    193          - speed: the speed parameter for back prop training
    194
    195          - momentum: the momentum term for back prop training
    196            *Not currently used*
    197
    198      """
    199      self.speed = speed
    200      self.momentum = momentum
    201      self.oldDeltaW = None
    202
    203
    204
    205  if __name__ == '__main__':
    206    from rdkit.ML.Neural import Network
    207
    208 -  def testAnd():
    209      examples = [
    210            [[0,0,1], [0.1]],
    211            [[0,1,1], [.1]],
    212            [[1,0,1], [.1]],
    213            [[1,1,1], [.9]]
    214        ]
    215      net = Network.Network([3,1])
    216      t = BackProp()
    217      t.TrainOnLine(examples,net)
    218      return net
    219
    220 -  def testOr():
    221      examples = [
    222            [[0,0,1], [0.1]],
    223            [[0,1,1], [.9]],
    224            [[1,0,1], [.9]],
    225            [[1,1,1], [.9]]
    226        ]
    227      net = Network.Network([3,1])
    228      t = BackProp()
    229      t.TrainOnLine(examples,net,maxIts=1000,useAvgErr=0)
    230      print('classifications:')
    231      for example in examples:
    232        res = net.ClassifyExample(example[0])
    233        print('%f -> %f'%(example[1][0],res))
    234
    235      return net
    236
    237 -  def testXor():
    238      examples = [
    239            [[0,0,1], [.1]],
    240            [[0,1,1], [.9]],
    241            [[1,0,1], [.9]],
    242            [[1,1,1], [.1]]
    243        ]
    244      net = Network.Network([3,3,1])
    245
    246      t = BackProp(speed=.8)
    247      t.TrainOnLine(examples,net,errTol=0.2)
    248      return net
    249
    250
    251 -  def testLinear():
    252      examples = [
    253            [.1,.1],
    254            [.2,.2],
    255            [.3,.3],
    256            [.4,.4],
    257            [.8,.8],
    258        ]
    259      net = Network.Network([1,2,1])
    260      t = BackProp(speed=.8)
    261      t.TrainOnLine(examples,net,errTol=0.1,useAvgErr=0)
    262      print('classifications:')
    263      for example in examples:
    264        res = net.ClassifyExample(example[:-1])
    265        print('%f -> %f'%(example[-1],res))
    266
    267      return net
    268
    269 -  def runProfile(command):
    270      import random
    271      random.seed(23)
    272      import profile,pstats
    273      datFile = '%s.prof.dat'%(command)
    274      profile.run('%s()'%command,datFile)
    275      stats = pstats.Stats(datFile)
    276      stats.strip_dirs()
    277      stats.sort_stats('time').print_stats()
    278
    279    if 0:
    280      net = testXor()
    281      print('Xor:', net)
    282      from rdkit.six.moves import cPickle
    283      outF = open('xornet.pkl','wb+')
    284      cPickle.dump(net,outF)
    285      outF.close()
    286    else:
    287      #runProfile('testLinear')
    288      net = testLinear()
    289      #net = testOr()
    290