Parent Directory
|
Revision Log
Lisa's changes, and updates for 64-bit: lights in pyro simulator
| 1 | """ |
| 2 | ---------------------------------------------------- |
| 3 | An Artificial Neural Network System Implementing |
| 4 | Backprop. Part of the Pyrobot Robotics Project. |
| 5 | Provided under the GNU General Public License. |
| 6 | ---------------------------------------------------- |
| 7 | (c) 2001-2006, Developmental Robotics Research Group |
| 8 | ---------------------------------------------------- |
| 9 | |
| 10 | This file implements the major classes and functions for |
| 11 | making artificial neural networks in Python. |
| 12 | """ |
| 13 | |
| 14 | __author__ = "Douglas Blank <dblank@brynmawr.edu>" |
| 15 | __version__ = "$Revision$" |
| 16 | |
| 17 | # Includes newConx version 4/24/2009 |
| 18 | # Jim Marshall |
| 19 | |
| 20 | import Numeric, math, random, time, sys, operator |
| 21 | try: |
| 22 | import pyrobot.system.share as share |
| 23 | except: |
| 24 | class share: debug = 0 |
| 25 | if not share.debug: |
| 26 | try: |
| 27 | import psyco; psyco.full() |
| 28 | print "Conx, version %s (psyco enabled)" % __version__.split()[1] |
| 29 | except: |
| 30 | print "Conx, version %s (regular speed)" % __version__.split()[1] |
| 31 | else: |
| 32 | print "Conx, version %s (debug; regular speed)" % __version__.split()[1] |
| 33 | |
| 34 | def reverse(lyst): |
| 35 | """ Returns a reversed list. """ |
| 36 | return [lyst[p] for p in range(len(lyst) - 1, -1, -1)] |
| 37 | |
| 38 | def pad(s, n, p = " ", sep = "|", align = "left"): |
| 39 | """ |
| 40 | Returns a padded string. |
| 41 | s = string to pad |
| 42 | n = width of string to return |
| 43 | sep = separator (on end of string) |
| 44 | align = text alignment, "left", "center", or "right" |
| 45 | """ |
| 46 | if align == "left": |
| 47 | return (s + (p * n))[:n] + sep |
| 48 | elif align == "center": |
| 49 | pos = n + len(s)/2 - n/2 |
| 50 | return ((p * n) + s + (p * n))[pos:pos + n] + sep |
| 51 | elif align == "right": |
| 52 | return ((p * n) + s)[-n:] + sep |
| 53 | |
| 54 | def sumMerge(dict1, dict2): |
| 55 | """ |
| 56 | Adds two dictionaries together, and merges into the first, dict1. |
| 57 | Returns first dict. |
| 58 | """ |
| 59 | for key in dict2: |
| 60 | dict1[key] = map(lambda a,b: a + b, dict1.get(key, [0,0,0,0]), dict2[key]) |
| 61 | return dict1 # and also returns it, in case you want to do something to it |
| 62 | |
| 63 | def loadNetwork(filename, mode = 'pickle'): |
| 64 | """ |
| 65 | Loads network from a file using pickle. See Network.saveNetwork() |
| 66 | """ |
| 67 | return loadNetworkFromFile(filename, mode) |
| 68 | |
| 69 | def loadNetworkFromFile(filename, mode = 'pickle'): |
| 70 | """ |
| 71 | Deprecated. Use loadNetwork instead. |
| 72 | """ |
| 73 | if mode == 'pickle': |
| 74 | import pickle |
| 75 | fp = open(filename) |
| 76 | network = pickle.load(fp) |
| 77 | fp.close() |
| 78 | return network |
| 79 | elif mode in ['plain', 'conx']: |
| 80 | fp = open(filename, "r") |
| 81 | line = fp.readline() |
| 82 | network = None |
| 83 | while line: |
| 84 | if line.startswith("layer,"): |
| 85 | # layer, name, size |
| 86 | temp, name, sizeStr = line.split(",") |
| 87 | name = name.strip() |
| 88 | size = int(sizeStr) |
| 89 | network.addLayer(name, size) |
| 90 | line = fp.readline() |
| 91 | weights = [float(f) for f in line.split()] |
| 92 | for i in range(network[name].size): |
| 93 | network[name].weight[i] = weights[i] |
| 94 | elif line.startswith("connection,"): |
| 95 | # connection, fromLayer, toLayer |
| 96 | temp, nameFrom, nameTo = line.split(",") |
| 97 | nameFrom, nameTo = nameFrom.strip(), nameTo.strip() |
| 98 | network.connect(nameFrom, nameTo) |
| 99 | for i in range(network[nameFrom].size): |
| 100 | line = fp.readline() |
| 101 | weights = [float(f) for f in line.split()] |
| 102 | for j in range(network[nameTo].size): |
| 103 | network[nameFrom, nameTo].weight[i][j] = weights[j] |
| 104 | elif line.startswith("parameter,"): |
| 105 | temp, exp = line.split(",") |
| 106 | exec(exp) # network is the neural network object |
| 107 | elif line.startswith("network,"): |
| 108 | temp, netType = line.split(",") |
| 109 | netType = netType.strip().lower() |
| 110 | if netType == "cascornetwork": |
| 111 | from pyrobot.brain.cascor import CascorNetwork |
| 112 | network = CascorNetwork() |
| 113 | elif netType == "network": |
| 114 | network = Network() |
| 115 | elif netType == "srn": |
| 116 | network = SRN() |
| 117 | else: |
| 118 | raise AttributeError, "unknown network type: '%s'" % netType |
| 119 | line = fp.readline() |
| 120 | return network |
| 121 | def ndim(n, *args): |
| 122 | """ |
| 123 | Makes a multi-dimensional array of random floats. (Replaces RandomArray). |
| 124 | """ |
| 125 | if not args: |
| 126 | return [random.random() for i in xrange(n)] |
| 127 | A = [] |
| 128 | for i in range(n): |
| 129 | A.append( ndim(*args) ) |
| 130 | return A |
| 131 | def ndim2(n, *args): |
| 132 | """ |
| 133 | Makes a multi-dimensional array of random floats. (Replaces RandomArray). |
| 134 | This version generates random values from a probability distribution more appropriate for a Tanh activation function. |
| 135 | """ |
| 136 | if not args: |
| 137 | return [random.gauss(0, 1) for i in xrange(n)] |
| 138 | A = [] |
| 139 | for i in range(n): |
| 140 | A.append( ndim(*args) ) |
| 141 | return A |
| 142 | def randomArray2(size, bound): |
| 143 | """ |
| 144 | Returns an array initialized to random values between -max and max. |
| 145 | """ |
| 146 | if type(size) == type(1): |
| 147 | size = (size,) |
| 148 | temp = Numeric.array( ndim(*size) ) * (2.0 * bound) |
| 149 | return temp - bound |
| 150 | def randomArray(size, bound): |
| 151 | """ |
| 152 | Returns an array initialized to random values between -max and max. |
| 153 | """ |
| 154 | if type(size) == type(1): |
| 155 | size = (size,) |
| 156 | temp = Numeric.array( ndim(*size) ) * (2.0 * bound) |
| 157 | return temp - bound |
| 158 | |
| 159 | def displayArray(name, a, width = 0): |
| 160 | """ |
| 161 | Prints an array (any sequence of floats, really) to the screen. |
| 162 | """ |
| 163 | print name + ": ", |
| 164 | cnt = 0 |
| 165 | for i in a: |
| 166 | print "%4.2f" % i, |
| 167 | if width > 0 and (cnt + 1) % width == 0: |
| 168 | print '' |
| 169 | cnt += 1 |
| 170 | |
| 171 | def toStringArray(name, a, width = 0): |
| 172 | """ |
| 173 | Returns an array (any sequence of floats, really) as a string. |
| 174 | """ |
| 175 | string = name + ": " |
| 176 | cnt = 0 |
| 177 | for i in a: |
| 178 | string += "%4.2f " % i |
| 179 | if width > 0 and (cnt + 1) % width == 0: |
| 180 | string += '\n' |
| 181 | cnt += 1 |
| 182 | return string |
| 183 | |
| 184 | def writeArray(fp, a, delim = " ", nl = 1): |
| 185 | """ |
| 186 | Writes a sequence a of floats to file pointed to by file pointer. |
| 187 | """ |
| 188 | for i in a: |
| 189 | fp.write("%f%s" % (i, delim)) |
| 190 | if nl: |
| 191 | fp.write("\n") |
| 192 | |
| 193 | class LayerError(AttributeError): |
| 194 | """ |
| 195 | Used to indicate that a layer has some improper attribute (size, |
| 196 | type, etc.). |
| 197 | """ |
| 198 | |
| 199 | class NetworkError(AttributeError): |
| 200 | """ |
| 201 | Used to indicate that a network has some improper attribute (no |
| 202 | layers, no connections, etc.). |
| 203 | """ |
| 204 | |
| 205 | class SRNError(NetworkError): |
| 206 | """ |
| 207 | Used to indicate that SRN specific attributes are improper. |
| 208 | """ |
| 209 | |
| 210 | class Node: |
| 211 | """ |
| 212 | A temp place to hold values for reference. If given a layer and position, then |
| 213 | the node can be used to set values, and can update itself. |
| 214 | """ |
| 215 | def __init__(self, layer = None, position = None, **keywords): |
| 216 | self.__dict__["_layer"] = layer |
| 217 | self.__dict__["_position"] = position |
| 218 | self.__dict__["_attributes"] = keywords.keys() |
| 219 | for key in keywords: |
| 220 | self.__dict__[key] = keywords[key] |
| 221 | def __str__(self): |
| 222 | retval = "Node:" |
| 223 | if self._layer != None: |
| 224 | retval += "\n layer = '%s'" % self._layer.name |
| 225 | retval += "\n position = %d" % self._position |
| 226 | for key in self._attributes: |
| 227 | retval += "\n %s = %s" % (key, self.__dict__[key]) |
| 228 | return retval |
| 229 | def update(self): |
| 230 | if self._layer != None: |
| 231 | for key in self._attributes: |
| 232 | self.__dict__[key] = self._layer.__dict__[key][self._position] |
| 233 | else: |
| 234 | raise AttributeError, "node is read-only" |
| 235 | def __setattr__(self, attribute, value): |
| 236 | if self._layer != None: |
| 237 | if attribute in self._layer.__dict__: |
| 238 | self._layer.__dict__[attribute][self._position] = value |
| 239 | else: |
| 240 | raise AttributeError, "no such property '%s'" % attribute |
| 241 | else: |
| 242 | raise AttributeError, ("node is read-only: use net['layerName'].%s[NUM] = %s" % (attribute, value)) |
| 243 | |
| 244 | class Layer: |
| 245 | """ |
| 246 | Class which contains arrays of node elements (ie, activation, |
| 247 | error, bias, etc). |
| 248 | """ |
| 249 | # constructor |
| 250 | def __init__(self, name, size, maxRandom = 0.1): |
| 251 | """ |
| 252 | Constructor for Layer class. A name and the number of nodes |
| 253 | for the instance are passed as arguments. |
| 254 | """ |
| 255 | if size <= 0: |
| 256 | raise LayerError, ('Layer was initialized with size zero.' , size) |
| 257 | self.patternReport = 0 # flag to determine if layer should be in report |
| 258 | # patternReport is set to 1 automatically for output layers (in .connect()) |
| 259 | self.name = name |
| 260 | self.size = size |
| 261 | self.displayWidth = size |
| 262 | self.type = 'Undefined' # determined later by connectivity |
| 263 | self.kind = 'Undefined' # mirrors self.type but will include 'Context' |
| 264 | self._verbosity = 0 |
| 265 | self.log = 0 |
| 266 | self.logFile = '' |
| 267 | self._logPtr = 0 |
| 268 | self.active = 1 # takes layer out of the processing |
| 269 | self.frozen = 0 # freezes weights (dbiases), if layer still active |
| 270 | self._maxRandom = maxRandom |
| 271 | self.initialize() |
| 272 | self.verify = 1 |
| 273 | # layer report of stats: |
| 274 | self.pcorrect = 0 |
| 275 | self.ptotal = 0 |
| 276 | self.correct = 0 |
| 277 | # misc: |
| 278 | self.minTarget = 0.0 |
| 279 | self.maxTarget = 1.0 |
| 280 | self.minActivation = 0.0 |
| 281 | self.maxActivation = 1.0 |
| 282 | |
| 283 | def initialize(self): |
| 284 | """ |
| 285 | Initializes important node values to zero for each node in the |
| 286 | layer (target, error, activation, dbias, delta, netinput, bed). |
| 287 | """ |
| 288 | self.randomize() |
| 289 | self.dweight = Numeric.zeros(self.size, 'f') |
| 290 | self.delta = Numeric.zeros(self.size, 'f') |
| 291 | self.wed = Numeric.zeros(self.size, 'f') |
| 292 | self.wedLast = Numeric.zeros(self.size, 'f') |
| 293 | self.target = Numeric.zeros(self.size, 'f') |
| 294 | self.error = Numeric.zeros(self.size, 'f') |
| 295 | self.activation = Numeric.zeros(self.size, 'f') |
| 296 | self.netinput = Numeric.zeros(self.size, 'f') |
| 297 | self.targetSet = 0 |
| 298 | self.activationSet = 0 |
| 299 | def randomize(self, force = 0): |
| 300 | """ |
| 301 | Initialize node biases to random values in the range [-max, max]. |
| 302 | """ |
| 303 | if force or not self.frozen: |
| 304 | self.weight = randomArray(self.size, self._maxRandom) |
| 305 | |
| 306 | # general methods |
| 307 | def __len__(self): |
| 308 | """ |
| 309 | Returns the number of nodes in the layer. |
| 310 | """ |
| 311 | return self.size |
| 312 | def __str__(self): |
| 313 | return self.toString() |
| 314 | def __getitem__(self, i): |
| 315 | """ Return a temp object with some properties set """ |
| 316 | if type(i) == int: |
| 317 | return Node( |
| 318 | layer = self, |
| 319 | position = i, |
| 320 | activation = self.activation[i], |
| 321 | error = self.error[i], |
| 322 | target = self.target[i], |
| 323 | netinput = self.netinput[i], |
| 324 | weight = self.weight[i] |
| 325 | ) |
| 326 | else: |
| 327 | raise AttributeError, "expected integer instead of '%s'" % i |
| 328 | # layer methods |
| 329 | def setActive(self, value): |
| 330 | """ |
| 331 | Sets layer to active or inactive. Layers must be active to propagate activations. |
| 332 | """ |
| 333 | self.active = value |
| 334 | def getActive(self): |
| 335 | """ |
| 336 | Used to determine if a layer is active or inactive. |
| 337 | """ |
| 338 | return self.active |
| 339 | def changeSize(self, newsize): |
| 340 | """ |
| 341 | Changes the size of the layer. Should only be called through |
| 342 | Network.changeLayerSize(). |
| 343 | """ |
| 344 | # overwrites current data |
| 345 | if newsize <= 0: |
| 346 | raise LayerError, ('Layer size changed to zero.', newsize) |
| 347 | minSize = min(self.size, newsize) |
| 348 | bias = randomArray(newsize, self._maxRandom) |
| 349 | Numeric.put(bias, Numeric.arange(minSize), self.weight) |
| 350 | self.weight = bias |
| 351 | self.size = newsize |
| 352 | self.displayWidth = newsize |
| 353 | self.targetSet = 0 |
| 354 | self.activationSet = 0 |
| 355 | self.target = Numeric.zeros(self.size, 'f') |
| 356 | self.error = Numeric.zeros(self.size, 'f') |
| 357 | self.activation = Numeric.zeros(self.size, 'f') |
| 358 | self.dweight = Numeric.zeros(self.size, 'f') |
| 359 | self.delta = Numeric.zeros(self.size, 'f') |
| 360 | self.netinput = Numeric.zeros(self.size, 'f') |
| 361 | self.wed = Numeric.zeros(self.size, 'f') |
| 362 | self.wedLast = Numeric.zeros(self.size, 'f') |
| 363 | |
| 364 | # error and report methods |
| 365 | def TSSError(self): |
| 366 | """ |
| 367 | Returns Total Sum Squared Error for this layer's pattern. |
| 368 | """ |
| 369 | return Numeric.add.reduce((self.target - self.activation) ** 2) |
| 370 | def RMSError(self): |
| 371 | """ |
| 372 | Returns Root Mean Squared Error for this layer's pattern. |
| 373 | """ |
| 374 | tss = self.TSSError() |
| 375 | return math.sqrt(tss / self.size) |
| 376 | def getCorrect(self, tolerance): |
| 377 | """ |
| 378 | Returns the number of nodes within tolerance of the target. |
| 379 | """ |
| 380 | return Numeric.add.reduce(Numeric.fabs(self.target - self.activation) < tolerance) |
| 381 | def getWinner(self, type = 'activation'): |
| 382 | """ |
| 383 | Returns the winner of the type specified {'activation' or |
| 384 | 'target'}. |
| 385 | """ |
| 386 | maxvalue = -10000 |
| 387 | maxpos = -1 |
| 388 | ttlvalue = 0 |
| 389 | if type == 'activation': |
| 390 | ttlvalue = Numeric.add.reduce(self.activation) |
| 391 | maxpos = Numeric.argmax(self.activation) |
| 392 | maxvalue = self.activation[maxpos] |
| 393 | elif type == 'target': |
| 394 | # note that backprop() resets self.targetSet flag |
| 395 | if self.verify and self.targetSet == 0: |
| 396 | raise LayerError, \ |
| 397 | ('getWinner() called with \'target\' but target has not been set.', \ |
| 398 | self.targetSet) |
| 399 | ttlvalue = Numeric.add.reduce(self.target) |
| 400 | maxpos = Numeric.argmax(self.target) |
| 401 | maxvalue = self.target[maxpos] |
| 402 | else: |
| 403 | raise LayerError, ('getWinner() called with unknown layer attribute.', \ |
| 404 | type) |
| 405 | if self.size > 0: |
| 406 | avgvalue = ttlvalue / float(self.size) |
| 407 | else: |
| 408 | raise LayerError, ('getWinner() called for layer of size zero.', \ |
| 409 | self.size) |
| 410 | return maxpos, maxvalue, avgvalue |
| 411 | |
| 412 | # used so pickle will work with log file pointer |
| 413 | def __getstate__(self): |
| 414 | odict = self.__dict__.copy() |
| 415 | del odict['_logPtr'] |
| 416 | return odict |
| 417 | def __setstate__(self,dict): |
| 418 | if dict['log']: |
| 419 | self._logPtr = open(dict['logFile'], 'a') |
| 420 | else: |
| 421 | self._logPtr = 0 |
| 422 | self.__dict__.update(dict) |
| 423 | |
| 424 | # log methods |
| 425 | def setLog(self, fileName): |
| 426 | """ |
| 427 | Opens a log file with name fileName. |
| 428 | """ |
| 429 | self.log = 1 |
| 430 | self.logFile = fileName |
| 431 | self._logPtr = open(fileName, "w") |
| 432 | def logMsg(self, msg): |
| 433 | """ |
| 434 | Logs a message. |
| 435 | """ |
| 436 | self._logPtr.write(msg) |
| 437 | def closeLog(self): |
| 438 | """ |
| 439 | Closes the log file. |
| 440 | """ |
| 441 | self._logPtr.close() |
| 442 | self.log = 0 |
| 443 | def writeLog(self): |
| 444 | """ |
| 445 | Writes to the log file. |
| 446 | """ |
| 447 | if self.log: |
| 448 | writeArray(self._logPtr, self.activation) |
| 449 | |
| 450 | # string and display methods |
| 451 | def setDisplayWidth(self, val): |
| 452 | """ |
| 453 | Sets self.displayWidth the the argument value. |
| 454 | """ |
| 455 | self.displayWidth = val |
| 456 | def toString(self): |
| 457 | """ |
| 458 | Returns a string representation of Layer instance. |
| 459 | """ |
| 460 | string = "Layer '%s': (Kind: %s, Size: %d, Active: %d, Frozen: %d)\n" % ( |
| 461 | self.name, self.kind, self.size, self.active, self.frozen) |
| 462 | if (self.type == 'Output'): |
| 463 | string += toStringArray('Target ', self.target, self.displayWidth) |
| 464 | string += toStringArray('Activation', self.activation, self.displayWidth) |
| 465 | if (self.type != 'Input' and self._verbosity > 1): |
| 466 | string += toStringArray('Error ', self.error, self.displayWidth) |
| 467 | if (self._verbosity > 4 and self.type != 'Input'): |
| 468 | string += toStringArray('weight ', self.weight, self.displayWidth) |
| 469 | string += toStringArray('dweight ', self.dweight, self.displayWidth) |
| 470 | string += toStringArray('delta ', self.delta, self.displayWidth) |
| 471 | string += toStringArray('netinput ', self.netinput, self.displayWidth) |
| 472 | string += toStringArray('wed ', self.wed, self.displayWidth) |
| 473 | return string |
| 474 | def display(self): |
| 475 | """ |
| 476 | Displays the Layer instance to the screen. |
| 477 | """ |
| 478 | if self.displayWidth == 0: return |
| 479 | print "=============================" |
| 480 | print "Layer '%s': (Kind: %s, Size: %d, Active: %d, Frozen: %d)" % ( |
| 481 | self.name, self.kind, self.size, self.active, self.frozen) |
| 482 | if (self.type == 'Output'): |
| 483 | displayArray('Target ', self.target, self.displayWidth) |
| 484 | displayArray('Activation', self.activation, self.displayWidth) |
| 485 | if (self.type != 'Input' and self._verbosity > 1): |
| 486 | displayArray('Error ', self.error, self.displayWidth) |
| 487 | if (self._verbosity > 4 and self.type != 'Input'): |
| 488 | print " ", ; displayArray('weight', self.weight) |
| 489 | print " ", ; displayArray('dweight', self.dweight) |
| 490 | print " ", ; displayArray('delta', self.delta) |
| 491 | print " ", ; displayArray('netinput', self.netinput) |
| 492 | print " ", ; displayArray('wed', self.wed) |
| 493 | |
| 494 | |
| 495 | # activation methods |
| 496 | def getActivationsList(self): |
| 497 | """ |
| 498 | Returns node activations in list (copy) form. |
| 499 | """ |
| 500 | return self.activation.copy() |
| 501 | def getActivations(self): |
| 502 | """ |
| 503 | Returns node activations in (Numeric) array (pointer) form. |
| 504 | """ |
| 505 | return self.activation |
| 506 | def setActivations(self, value): |
| 507 | """ |
| 508 | Sets all activations to the value of the argument. Value should be in the range [0,1]. |
| 509 | """ |
| 510 | #if self.verify and not self.activationSet == 0: |
| 511 | # raise LayerError, \ |
| 512 | # ('Activation flag not reset. Activations may have been set multiple times without any intervening call to propagate().', self.activationSet) |
| 513 | Numeric.put(self.activation, Numeric.arange(len(self.activation)), value) |
| 514 | self.activationSet = 1 |
| 515 | def copyActivations(self, arr, reckless = 0): |
| 516 | """ |
| 517 | Copies activations from the argument array into |
| 518 | layer activations. |
| 519 | """ |
| 520 | array = Numeric.array(arr) |
| 521 | if not len(array) == self.size: |
| 522 | raise LayerError, \ |
| 523 | ('Mismatched activation size and layer size in call to copyActivations()', \ |
| 524 | (len(array), self.size)) |
| 525 | if self.verify and not self.activationSet == 0: |
| 526 | if not reckless: |
| 527 | raise LayerError, \ |
| 528 | ('Activation flag not reset before call to copyActivations()', \ |
| 529 | self.activationSet) |
| 530 | self.activation = array |
| 531 | self.activationSet = 1 |
| 532 | |
| 533 | # target methods |
| 534 | def getTargetsList(self): |
| 535 | """ |
| 536 | Returns targets in list form. |
| 537 | """ |
| 538 | return self.target.copy() |
| 539 | def getTargets(self): |
| 540 | """ |
| 541 | Return targets in (Numeric) array form. |
| 542 | """ |
| 543 | return self.target |
| 544 | def setTargets(self, value): |
| 545 | """ |
| 546 | Sets all targets the the value of the argument. This value must be in the range [min,max]. |
| 547 | """ |
| 548 | # Removed this because both propagate and backprop (via compute_error) set targets |
| 549 | #if self.verify and not self.targetSet == 0: |
| 550 | # if not self.warningIssued: |
| 551 | # print 'Warning! Targets have already been set and no intervening backprop() was called.', \ |
| 552 | # (self.name, self.targetSet) |
| 553 | # print "(Warning will not be issued again)" |
| 554 | # self.warningIssued = 1 |
| 555 | if value > self.maxActivation or value < self.minActivation: |
| 556 | raise LayerError, ('Targets for this layer are out of the proper interval.', (self.name, value)) |
| 557 | Numeric.put(self.target, Numeric.arange(len(self.target)), value) |
| 558 | self.targetSet = 1 |
| 559 | def copyTargets(self, arr): |
| 560 | """ |
| 561 | Copies the targets of the argument array into the self.target attribute. |
| 562 | """ |
| 563 | array = Numeric.array(arr) |
| 564 | if not len(array) == self.size: |
| 565 | raise LayerError, \ |
| 566 | ('Mismatched target size and layer size in call to copyTargets()', \ |
| 567 | (len(array), self.size)) |
| 568 | # Removed this because both propagate and backprop (via compute_error) set targets |
| 569 | #if self.verify and not self.targetSet == 0: |
| 570 | # if not self.warningIssued: |
| 571 | # print 'Warning! Targets have already been set and no intervening backprop() was called.', \ |
| 572 | # (self.name, self.targetSet) |
| 573 | # print "(Warning will not be issued again)" |
| 574 | # self.warningIssued = 1 |
| 575 | if Numeric.add.reduce(array < self.minTarget) or Numeric.add.reduce(array > self.maxTarget): |
| 576 | print self.name, self.minTarget, self.maxTarget |
| 577 | raise LayerError, ('Targets for this layer are out of range.', (self.name, array)) |
| 578 | self.target = array |
| 579 | self.targetSet = 1 |
| 580 | |
| 581 | # flag methods |
| 582 | def resetFlags(self): |
| 583 | """ |
| 584 | Resets self.targetSet and self.activationSet flags. |
| 585 | """ |
| 586 | self.targetSet = 0 |
| 587 | self.activationSet = 0 |
| 588 | def resetTargetFlag(self): |
| 589 | """ |
| 590 | Resets self.targetSet flag. |
| 591 | """ |
| 592 | self.targetSet = 0 |
| 593 | def resetActivationFlag(self): |
| 594 | """ |
| 595 | Resets self.activationSet flag. |
| 596 | """ |
| 597 | self.activationSet = 0 |
| 598 | |
| 599 | class Connection: |
| 600 | """ |
| 601 | Class which contains references to two layers (from and to) and the |
| 602 | weights between them. |
| 603 | """ |
| 604 | # constructor and initialization methods |
| 605 | def __init__(self, fromLayer, toLayer): |
| 606 | """ |
| 607 | Constructor for Connection class. Takes instances of source and |
| 608 | destination layers as arguments. |
| 609 | """ |
| 610 | self.active = 1 # propagates and backrops if active |
| 611 | self.frozen = 0 # freezes weights |
| 612 | self.fromLayer = fromLayer |
| 613 | self.toLayer = toLayer |
| 614 | self.initialize() |
| 615 | def __getitem__(self, i): |
| 616 | return self.weight[i] |
| 617 | def initialize(self): |
| 618 | """ |
| 619 | Initializes self.dweight and self.wed to zero matrices. |
| 620 | """ |
| 621 | self.randomize() |
| 622 | self.dweight = Numeric.zeros((self.fromLayer.size, \ |
| 623 | self.toLayer.size), 'f') |
| 624 | self.wed = Numeric.zeros((self.fromLayer.size, \ |
| 625 | self.toLayer.size), 'f') |
| 626 | self.wedLast = Numeric.zeros((self.fromLayer.size, \ |
| 627 | self.toLayer.size), 'f') |
| 628 | def randomize(self, force = 0): |
| 629 | """ |
| 630 | Sets weights to initial random values in the range [-max, max]. |
| 631 | """ |
| 632 | if force or not self.frozen: |
| 633 | self.weight = randomArray((self.fromLayer.size, \ |
| 634 | self.toLayer.size), |
| 635 | self.toLayer._maxRandom) |
| 636 | def __str__(self): |
| 637 | return self.toString() |
| 638 | |
| 639 | # connection modification methods |
| 640 | def changeSize(self, fromLayerSize, toLayerSize): |
| 641 | """ |
| 642 | Changes the size of the connection depending on the size |
| 643 | change of either source or destination layer. Should only be |
| 644 | called through Network.changeLayerSize(). |
| 645 | """ |
| 646 | if toLayerSize <= 0 or fromLayerSize <= 0: |
| 647 | raise LayerError, ('changeSize() called with invalid layer size.', \ |
| 648 | (fromLayerSize, toLayerSize)) |
| 649 | dweight = Numeric.zeros((fromLayerSize, toLayerSize), 'f') |
| 650 | wed = Numeric.zeros((fromLayerSize, toLayerSize), 'f') |
| 651 | wedLast = Numeric.zeros((fromLayerSize, toLayerSize), 'f') |
| 652 | weight = randomArray((fromLayerSize, toLayerSize), |
| 653 | self.toLayer._maxRandom) |
| 654 | # copy from old to new, considering one is smaller |
| 655 | minFromLayerSize = min( fromLayerSize, self.fromLayer.size) |
| 656 | minToLayerSize = min( toLayerSize, self.toLayer.size) |
| 657 | for i in range(minFromLayerSize): |
| 658 | for j in range(minToLayerSize): |
| 659 | wed[i][j] = self.wed[i][j] |
| 660 | wedLast[i][j] = self.wedLast[i][j] |
| 661 | dweight[i][j] = self.dweight[i][j] |
| 662 | weight[i][j] = self.weight[i][j] |
| 663 | self.dweight = dweight |
| 664 | self.wed = wed |
| 665 | self.wedLast = wedLast |
| 666 | self.weight = weight |
| 667 | |
| 668 | # display methods |
| 669 | def display(self): |
| 670 | """ |
| 671 | Displays connection information to the screen. |
| 672 | """ |
| 673 | if self.toLayer._verbosity > 4: |
| 674 | print "wed: from '" + self.fromLayer.name + "' to '" + self.toLayer.name +"'" |
| 675 | for j in range(self.toLayer.size): |
| 676 | print self.toLayer.name, "[", j, "]", |
| 677 | print '' |
| 678 | for i in range(self.fromLayer.size): |
| 679 | print self.fromLayer.name, "[", i, "]", ": ", |
| 680 | for j in range(self.toLayer.size): |
| 681 | print self.wed[i][j], |
| 682 | print '' |
| 683 | print '' |
| 684 | print "dweight: from '" + self.fromLayer.name + "' to '" + self.toLayer.name +"'" |
| 685 | for j in range(self.toLayer.size): |
| 686 | print self.toLayer.name, "[", j, "]", |
| 687 | print '' |
| 688 | for i in range(self.fromLayer.size): |
| 689 | print self.fromLayer.name, "[", i, "]", ": ", |
| 690 | for j in range(self.toLayer.size): |
| 691 | print self.dweight[i][j], |
| 692 | print '' |
| 693 | print '' |
| 694 | if self.toLayer._verbosity > 2: |
| 695 | print "Weights: from '" + self.fromLayer.name + "' to '" + self.toLayer.name +"'" |
| 696 | print " ", |
| 697 | for j in range(self.toLayer.size): |
| 698 | print self.toLayer.name, "[", j, "]", |
| 699 | print '' |
| 700 | for i in range(self.fromLayer.size): |
| 701 | print self.fromLayer.name, "[", i, "]", ": ", |
| 702 | for j in range(self.toLayer.size): |
| 703 | print self.weight[i][j], |
| 704 | print '' |
| 705 | print '' |
| 706 | # string method |
| 707 | def toString(self): |
| 708 | """ |
| 709 | Connection information as a string. |
| 710 | """ |
| 711 | string = "" |
| 712 | if self.toLayer._verbosity > 4: |
| 713 | string += "wed: from '" + self.fromLayer.name + "' to '" + self.toLayer.name +"'\n" |
| 714 | string += " " |
| 715 | for j in range(self.toLayer.size): |
| 716 | string += " " + self.toLayer.name + "[" + str(j) + "]" |
| 717 | string += '\n' |
| 718 | for i in range(self.fromLayer.size): |
| 719 | string += self.fromLayer.name+ "["+ str(i)+ "]"+ ": " |
| 720 | for j in range(self.toLayer.size): |
| 721 | string += " " + str(self.wed[i][j]) |
| 722 | string += '\n' |
| 723 | string += '\n' |
| 724 | string += "dweight: from '" + self.fromLayer.name + "' to '" + self.toLayer.name +"'\n" |
| 725 | string += " " |
| 726 | for j in range(self.toLayer.size): |
| 727 | string += " " + self.toLayer.name+ "["+ str(j)+ "]" |
| 728 | string += '\n' |
| 729 | for i in range(self.fromLayer.size): |
| 730 | string += self.fromLayer.name+ "["+ str(i)+ "]"+ ": " |
| 731 | for j in range(self.toLayer.size): |
| 732 | string += " " + str(self.dweight[i][j]) |
| 733 | string += '\n' |
| 734 | string += '\n' |
| 735 | if self.toLayer._verbosity > 2: |
| 736 | string += "Weights: from '" + self.fromLayer.name + "' to '" + self.toLayer.name +"'\n" |
| 737 | string += " " |
| 738 | for j in range(self.toLayer.size): |
| 739 | string += " " + self.toLayer.name+ "["+ str(j)+ "]" |
| 740 | string += '\n' |
| 741 | for i in range(self.fromLayer.size): |
| 742 | string += self.fromLayer.name+ "["+ str(i)+ "]"+ ": " |
| 743 | for j in range(self.toLayer.size): |
| 744 | string += " " + str(self.weight[i][j]) |
| 745 | string += '\n' |
| 746 | string += '\n' |
| 747 | return string |
| 748 | |
| 749 | class Network(object): |
| 750 | """ |
| 751 | Class which contains all of the parameters and methods needed to |
| 752 | run a neural network. |
| 753 | """ |
| 754 | # constructor |
| 755 | def __init__(self, name = 'Backprop Network', verbosity = 0, |
| 756 | seed = None): |
| 757 | """ |
| 758 | Constructor for the Network class. Takes optional name and |
| 759 | verbosity arguments. |
| 760 | """ |
| 761 | if seed == None: |
| 762 | x = random.random() * 100000 + time.time() |
| 763 | else: |
| 764 | x = seed |
| 765 | self.layers = [] |
| 766 | self.verbosity = verbosity |
| 767 | self.setSeed(x) |
| 768 | self.complete = 0 |
| 769 | self.name = name |
| 770 | self.layersByName = {} |
| 771 | self.connections = [] |
| 772 | self.inputMap = [] |
| 773 | self.targetMap = [] |
| 774 | self.association = [] |
| 775 | self.inputs = [] |
| 776 | self.targets = [] |
| 777 | self.orderedInputs = 0 |
| 778 | self.loadOrder = [] |
| 779 | self.learning = 1 |
| 780 | self.momentum = 0.9 |
| 781 | self.resetEpoch = 5000 |
| 782 | self.resetCount = 1 |
| 783 | self.resetLimit = 1 |
| 784 | self.batch = 0 |
| 785 | self.epoch = 0 |
| 786 | self.totalEpoch = 0 |
| 787 | self.count = 0 # number of times propagate is called |
| 788 | self.stopPercent = 1.0 |
| 789 | self.sigmoid_prime_offset = 0.1 |
| 790 | self.tolerance = 0.4 |
| 791 | self.interactive = 0 |
| 792 | self.epsilon = 0.1 |
| 793 | self.reportRate = 25 |
| 794 | self.sweepReportRate = None #1000 |
| 795 | self.crossValidationCorpus = () |
| 796 | self.crossValidationReportLayers = [] |
| 797 | self.crossValidationSampleRate = 0 |
| 798 | self.crossValidationSampleFile = "sample.cv" |
| 799 | self.patterns = {} |
| 800 | self.patterned = 0 # used for file IO with inputs and targets |
| 801 | self.sharedWeights = 0 |
| 802 | self.useCrossValidationToStop = 0 |
| 803 | self.saveResults = 0 # will save error, correct, total in sweep() |
| 804 | self.results = [] |
| 805 | self.autoCrossValidation = 0 |
| 806 | self.autoSaveWeightsFile = None |
| 807 | self.autoSaveWeightsFileFormat = "conx" |
| 808 | self.lastAutoSaveWeightsFilename = None |
| 809 | self.autoSaveNetworkFile = None |
| 810 | self.autoSaveNetworkFileFormat = "conx" |
| 811 | self.lastAutoSaveNetworkFilename = None |
| 812 | self.lastLowestTSSError = sys.maxint # some maximum value (not all pythons have Infinity) |
| 813 | self._cv = False # set true when in cross validation |
| 814 | self._sweeping = 0 # flag set when sweeping through corpus (as apposed to just stepping) |
| 815 | self._maxRandom = 0.1 |
| 816 | self.currentSweepCount = None |
| 817 | self.log = None # a pointer to a file-like object, like a Log object |
| 818 | self.echo = False # if going to a log file, echo it too, if true |
| 819 | self.hyperbolicError = 0 # exaggerate error? |
| 820 | # Quickprop settings: |
| 821 | self._quickprop = 0 |
| 822 | self.mu = 1.75 # maximum growth factor |
| 823 | self.splitEpsilon = 0 |
| 824 | self.decay = 0.0000 |
| 825 | self.cacheConnections = [] |
| 826 | self.cacheLayers = [] |
| 827 | self.setup() |
| 828 | def setCache(self, val = 1): |
| 829 | """ Sets cache on (or updates), or turns off """ |
| 830 | # first clear the old cached values |
| 831 | self.cacheConnections = [] |
| 832 | self.cacheLayers = [] |
| 833 | if val: |
| 834 | for layer in self.layers: |
| 835 | if layer.active and not layer.frozen: |
| 836 | self.cacheLayers.append( layer ) |
| 837 | for connection in self.connections: |
| 838 | if connection.active and not connection.frozen: |
| 839 | self.cacheConnections.append( connection ) |
| 840 | def setQuickprop(self, value): |
| 841 | if value: |
| 842 | self.batch = 1 |
| 843 | self._quickprop = 1 |
| 844 | self.mu = 1.75 # maximum growth factor |
| 845 | self.splitEpsilon = 1 |
| 846 | self.decay = -0.0001 |
| 847 | self.epsilon = 4.0 |
| 848 | #set different activation function here? |
| 849 | self.name = "Quickprop Network" |
| 850 | else: |
| 851 | self._quickprop = 0 |
| 852 | self.splitEpsilon = 0 |
| 853 | self.decay = 0.0000 |
| 854 | self.epsilon = 0.1 |
| 855 | def getQuickprop(self): return self._quickprop |
| 856 | def setup(self): |
| 857 | pass |
| 858 | # general methods |
| 859 | def path(self, startLayer, endLayer): |
| 860 | """ |
| 861 | Used in error checking with verifyArchitecture() and in prop_from(). |
| 862 | """ |
| 863 | next = {startLayer.name : startLayer} |
| 864 | visited = {} |
| 865 | while next != {}: |
| 866 | for item in next.items(): |
| 867 | # item[0] : name, item[1] : layer reference |
| 868 | # add layer to visited dict and del from next |
| 869 | visited[item[0]] = item[1] |
| 870 | del next[item[0]] |
| 871 | for connection in self.connections: |
| 872 | if connection.fromLayer.name == item[0]: |
| 873 | if connection.toLayer.name == endLayer.name: |
| 874 | return 1 # a path! |
| 875 | elif next.has_key(connection.toLayer.name): |
| 876 | pass # already in the list to be traversed |
| 877 | elif visited.has_key(connection.toLayer.name): |
| 878 | pass # already been there |
| 879 | else: |
| 880 | # add to next |
| 881 | next[connection.toLayer.name] = connection.toLayer |
| 882 | return 0 # didn't find it and ran out of places to go |
| 883 | def __str__(self): |
| 884 | """ |
| 885 | Returns string representation of network. |
| 886 | """ |
| 887 | return self.toString() |
| 888 | def __iter__(self): |
| 889 | for layer in self.layers: |
| 890 | yield layer |
| 891 | def __getitem__(self, name): |
| 892 | """ |
| 893 | Returns the layer specified by name. |
| 894 | """ |
| 895 | if type(name) == str: |
| 896 | return self.layersByName[name] |
| 897 | else: |
| 898 | fromName, toName = name |
| 899 | return self.getConnection(fromName, toName) |
| 900 | def __len__(self): |
| 901 | """ |
| 902 | Returns the number of layers in the network. |
| 903 | """ |
| 904 | return len(self.layers) |
| 905 | def getLayerIndex(self, layer): |
| 906 | """ |
| 907 | Given a reference to a layer, returns the index of that layer in |
| 908 | self.layers. |
| 909 | """ |
| 910 | for i in range(len(self.layers)): |
| 911 | if layer == self.layers[i]: # shallow cmp |
| 912 | return i |
| 913 | return -1 # not in list |
| 914 | def addLayer(self, name, size, verbosity = 0, position = None): |
| 915 | assert type(name) == str, "first parameter (name) must be a string" |
| 916 | assert type(size) == int, "second parameter (size) must be an integer" |
| 917 | assert (name not in self.layersByName), ("duplicate layer name '%s' is not allowed" % name) |
| 918 | layer = Layer(name, size, maxRandom=self._maxRandom) |
| 919 | Network.add(self, layer, verbosity, position) |
| 920 | # methods for constructing and modifying a network |
| 921 | def add(self, layer, verbosity = 0, position = None): |
| 922 | """ |
| 923 | Adds a layer. Layer verbosity is optional (default 0). |
| 924 | """ |
| 925 | layer._verbosity = verbosity |
| 926 | layer._maxRandom = self._maxRandom |
| 927 | layer.minTarget = 0.0 |
| 928 | layer.maxTarget = 1.0 |
| 929 | layer.minActivation = 0.0 |
| 930 | layer.maxActivation = 1.0 |
| 931 | if position == None: |
| 932 | self.layers.append(layer) |
| 933 | else: |
| 934 | self.layers.insert(position, layer) |
| 935 | self.layersByName[layer.name] = layer |
| 936 | def isConnected(self, fromName, toName): |
| 937 | """ Are these two layers connected this way? """ |
| 938 | for c in self.connections: |
| 939 | if (c.fromLayer.name == fromName and |
| 940 | c.toLayer.name == toName): |
| 941 | return 1 |
| 942 | return 0 |
| 943 | def connect(self, *names): |
| 944 | """ |
| 945 | Connects a list of names, one to the next. |
| 946 | """ |
| 947 | fromName, toName, rest = names[0], names[1], names[2:] |
| 948 | self.connectAt(fromName, toName) |
| 949 | if len(rest) != 0: |
| 950 | self.connect(toName, *rest) |
| 951 | def connectAt(self, fromName, toName, position = None): |
| 952 | """ |
| 953 | Connects two layers by instantiating an instance of Connection |
| 954 | class. Allows a position number, indicating the ordering of |
| 955 | the connection. |
| 956 | """ |
| 957 | fromLayer = self.getLayer(fromName) |
| 958 | toLayer = self.getLayer(toName) |
| 959 | if self.getLayerIndex(fromLayer) >= self.getLayerIndex(toLayer): |
| 960 | raise NetworkError, ('Layers out of order.', (fromLayer.name, toLayer.name)) |
| 961 | if (fromLayer.type == 'Output'): |
| 962 | fromLayer.type = 'Hidden' |
| 963 | fromLayer.patternReport = 0 # automatically turned off for hidden layers |
| 964 | if fromLayer.kind == 'Output': |
| 965 | fromLayer.kind = 'Hidden' |
| 966 | elif (fromLayer.type == 'Undefined'): |
| 967 | fromLayer.type = 'Input' |
| 968 | fromLayer.patternReport = 0 # automatically turned off for input layers |
| 969 | if fromLayer.kind == 'Undefined': |
| 970 | fromLayer.kind = 'Input' |
| 971 | if (toLayer.type == 'Input'): |
| 972 | raise NetworkError, ('Connections out of order', (fromLayer.name, toLayer.name)) |
| 973 | elif (toLayer.type == 'Undefined'): |
| 974 | toLayer.type = 'Output' |
| 975 | toLayer.patternReport = 1 # automatically turned on for output layers |
| 976 | if toLayer.kind == 'Undefined': |
| 977 | toLayer.kind = 'Output' |
| 978 | if position == None: |
| 979 | self.connections.append(Connection(fromLayer, toLayer)) |
| 980 | else: |
| 981 | self.connections.insert(position, Connection(fromLayer, toLayer)) |
| 982 | def addThreeLayers(self, inc, hidc, outc): |
| 983 | """ |
| 984 | Creates a three layer network with 'input', 'hidden', and |
| 985 | 'output' layers. |
| 986 | """ |
| 987 | self.addLayer('input', inc) |
| 988 | self.addLayer('hidden', hidc) |
| 989 | self.addLayer('output', outc) |
| 990 | self.connect('input', 'hidden') |
| 991 | self.connect('hidden', 'output') |
| 992 | def addLayers(self, *arg, **kw): |
| 993 | """ |
| 994 | Creates an N layer network with 'input', 'hidden1', 'hidden2',... |
| 995 | and 'output' layers. Keyword type indicates "parallel" or |
| 996 | "serial". If only one hidden layer, it is called "hidden". |
| 997 | """ |
| 998 | netType = "serial" |
| 999 | if "type" in kw: |
| 1000 | netType = kw["type"] |
| 1001 | self.addLayer('input', arg[0]) |
| 1002 | hiddens = [] |
| 1003 | if len(arg) > 3: |
| 1004 | hcount = 0 |
| 1005 | for hidc in arg[1:-1]: |
| 1006 | name = 'hidden%d' % hcount |
| 1007 | self.addLayer(name, hidc) |
| 1008 | hiddens.append(name) |
| 1009 | hcount += 1 |
| 1010 | elif len(arg) == 3: |
| 1011 | name = 'hidden' |
| 1012 | self.addLayer(name, arg[1]) |
| 1013 | hiddens.append(name) |
| 1014 | elif len(arg) == 2: |
| 1015 | pass |
| 1016 | else: |
| 1017 | raise AttributeError, "not enough layers! need >= 2" |
| 1018 | self.addLayer('output', arg[-1]) |
| 1019 | lastName = "input" |
| 1020 | for name in hiddens: |
| 1021 | if netType == "parallel": |
| 1022 | self.connect('input', name) |
| 1023 | self.connect(name, 'output') |
| 1024 | else: # serial |
| 1025 | self.connect(lastName, name) |
| 1026 | lastName = name |
| 1027 | if netType == "serial" or lastName == "input": |
| 1028 | self.connect(lastName, "output") |
| 1029 | def setLayerVerification(self, value): |
| 1030 | for layer in self.layers: |
| 1031 | layer.verify = value |
| 1032 | def deleteLayerNode(self, layername, nodeNum): |
| 1033 | """ |
| 1034 | Removes a particular unit/node from a layer. |
| 1035 | """ |
| 1036 | # first, construct an array of all of the weights |
| 1037 | # that won't be deleted: |
| 1038 | gene = [] |
| 1039 | for layer in self.layers: |
| 1040 | if layer.type != 'Input': |
| 1041 | for i in range(layer.size): |
| 1042 | if layer.name == layername and i == nodeNum: |
| 1043 | pass # skip it |
| 1044 | else: |
| 1045 | gene.append(layer.weight[i]) |
| 1046 | for connection in self.connections: |
| 1047 | for i in range(connection.fromLayer.size): |
| 1048 | for j in range(connection.toLayer.size): |
| 1049 | if ((connection.fromLayer.name == layername and i == nodeNum) or |
| 1050 | (connection.toLayer.name == layername and j == nodeNum)): |
| 1051 | pass # skip weights from/to nodeNum |
| 1052 | else: |
| 1053 | gene.append(connection.weight[i][j]) |
| 1054 | # now, change the size (removes rightmost node): |
| 1055 | self.changeLayerSize(layername, self[layername].size - 1) |
| 1056 | # and put the good weights where they go: |
| 1057 | self.unArrayify(gene) |
| 1058 | def addLayerNode(self, layerName, bias = None, weights = {}): |
| 1059 | """ |
| 1060 | Adds a new node to a layer, and puts in new weights. Adds node on the end. |
| 1061 | Weights will be random, unless specified. |
| 1062 | |
| 1063 | bias = the new node's bias weight |
| 1064 | weights = dict of {connectedLayerName: [weights], ...} |
| 1065 | |
| 1066 | Example: |
| 1067 | >>> net.addLayers(2, 5, 1) |
| 1068 | >>> net.addLayerNode("hidden", bias = -0.12, weights = {"input": [1, 0], "output": [0]}) |
| 1069 | """ |
| 1070 | self.changeLayerSize(layerName, self[layerName].size + 1) |
| 1071 | if bias != None: |
| 1072 | self[layerName].weight[-1] = bias |
| 1073 | for name in weights.keys(): |
| 1074 | for c in self.connections: |
| 1075 | if c.fromLayer.name == name and c.toLayer.name == layerName: |
| 1076 | for i in range(self[name].size): |
| 1077 | self[name, layerName].weight[i][-1] = weights[name][i] |
| 1078 | elif c.toLayer.name == name and c.fromLayer.name == layerName: |
| 1079 | for j in range(self[name].size): |
| 1080 | self[layerName, name].weight[-1][j] = weights[name][j] |
| 1081 | def changeLayerSize(self, layername, newsize): |
| 1082 | """ |
| 1083 | Changes layer size. Newsize must be greater than zero. |
| 1084 | """ |
| 1085 | # for all connection from to this layer, change matrix: |
| 1086 | if self.sharedWeights: |
| 1087 | raise AttributeError, "shared weights broken" |
| 1088 | for connection in self.connections: |
| 1089 | if connection.fromLayer.name == layername: |
| 1090 | connection.changeSize( newsize, connection.toLayer.size ) |
| 1091 | if connection.toLayer.name == layername: |
| 1092 | connection.changeSize( connection.fromLayer.size, newsize ) |
| 1093 | # then, change the actual layer size: |
| 1094 | self.getLayer(layername).changeSize(newsize) |
| 1095 | # reset and intialization |
| 1096 | def reset(self): |
| 1097 | """ |
| 1098 | Resets seed values. |
| 1099 | """ |
| 1100 | random.seed(self.seed) |
| 1101 | self.initialize() |
| 1102 | def initialize(self): |
| 1103 | """ |
| 1104 | Initializes network by calling Connection.initialize() and |
| 1105 | Layer.initialize(). self.count is set to zero. |
| 1106 | """ |
| 1107 | print >> sys.stderr, "Initializing '%s' weights..." % self.name |
| 1108 | if self.sharedWeights: |
| 1109 | raise AttributeError, "shared weights broken" |
| 1110 | self.count = 0 |
| 1111 | for connection in self.connections: |
| 1112 | connection.initialize() |
| 1113 | for layer in self.layers: |
| 1114 | layer.initialize() |
| 1115 | def resetFlags(self): |
| 1116 | """ |
| 1117 | Resets layer flags for activation and target. |
| 1118 | """ |
| 1119 | for layer in self.layers: |
| 1120 | layer.resetFlags() |
| 1121 | |
| 1122 | # set and get methods for attributes |
| 1123 | def putActivations(self, dict): |
| 1124 | """ |
| 1125 | Puts a dict of name: activations into their respective layers. |
| 1126 | """ |
| 1127 | for name in dict: |
| 1128 | self.layersByName[name].copyActivations( dict[name] ) |
| 1129 | def getActivationsDict(self, nameList): |
| 1130 | """ |
| 1131 | Returns a dictionary of layer names that map to a list of activations. |
| 1132 | """ |
| 1133 | retval = {} |
| 1134 | for name in nameList: |
| 1135 | retval[name] = self.layersByName[name].getActivationsList() |
| 1136 | return retval |
| 1137 | def getLayer(self, name): |
| 1138 | """ |
| 1139 | Returns the layer with the argument (string) name. |
| 1140 | """ |
| 1141 | return self.layersByName[name] |
| 1142 | def setAutoCrossValidation(self, value): |
| 1143 | self.autoCrossValidation = value |
| 1144 | def setAutoSaveWeightsFile(self, filename, format = "conx"): |
| 1145 | self.autoSaveWeightsFile = filename |
| 1146 | self.autoSaveWeightsFileFormat = format |
| 1147 | def setAutoSaveNetworkFile(self, filename, format = "conx"): |
| 1148 | self.autoSaveNetworkFile = filename |
| 1149 | self.autoSaveNetworkFileFormat = format |
| 1150 | def setPatterned(self, value): |
| 1151 | """ |
| 1152 | Sets the network to use patterns for inputs and targets. |
| 1153 | """ |
| 1154 | self.patterned = value |
| 1155 | def setEpsilon(self, value): self.epsilon = value |
| 1156 | def setInteractive(self, value): |
| 1157 | """ |
| 1158 | Sets interactive to value. Specifies if an interactive prompt |
| 1159 | should accompany sweep() or step(). |
| 1160 | """ |
| 1161 | self.interactive = value |
| 1162 | def setTolerance(self, value): |
| 1163 | """ |
| 1164 | Sets tolerance to value. This specifies how close acceptable |
| 1165 | outputs must be to targets. |
| 1166 | """ |
| 1167 | self.tolerance = value |
| 1168 | def setActive(self, layerName, value |