My class takes functions as a parameter. There are 6 of them, along with numerous other parameters. Almost all of the fields have default values.
It also has a class method that creates an instance of the class with 2 of the fields initialized randomly. It has a subclass that cheats a bit by making 2 of the functions passed in as a parameter unnecessary.
All of this has a lot of redundant information. I have found bugs in my code caused by mistakes in the constructor calls because they were so long. I indeed found another while writing this question.
How can I improve this?
ParentClass
class NeuralNet:
def __init__(self,
weights,
biases,
learning_rate=0.01,
momentum=0.9, #Set to zero to nut use mementum
post_process=lambda i: i, #Post Process is applied to final output only (Not used when training), but is used when checkign error rate
topErrorFunc=squaredMean, d_topErrorFunc=dSquaredMean,
actFunc=sigmoid, d_actFunc=dSigmoid,
topActFunc = sigmoid, d_topActFunc = dSigmoid):
self.weights = weights
self.biases = biases
assert len(self.biases)==len(self.weights), "Must have as many bias vectors as weight matrixes"
self._prevDeltaWs = [np.zeros_like(w) for w in self.weights]
self._prevDeltaBs = [np.zeros_like(b) for b in self.biases]
self.learning_rate = learning_rate
self.momentum = momentum
self.post_process = post_process
self.topErrorFunc = topErrorFunc
self.d_topErrorFunc = d_topErrorFunc
self.actFunc = actFunc
self.d_actFunc=d_actFunc
self.topActFunc = topActFunc
self.d_topActFunc = d_topActFunc
@classmethod
def random_init(cls,
layer_sizes,
learning_rate=0.01,
momentum=0.9, #Set to zero to nut use mementum
post_process=lambda i: i, #Post Process is applied to final output only (Not used when training), but is used when checkign error rate
topErrorFunc=squaredMean, d_topErrorFunc=dSquaredMean,
actFunc=sigmoid, d_actFunc=dSigmoid,
topActFunc=sigmoid, d_topActFunc=dSigmoid):
weights = [np.random.normal(0,0.01,(szNext,szThis)) for (szThis,szNext) in pairwise(layer_sizes)]
biases = [np.random.normal(0,0.01,sz) for sz in layer_sizes[1:]]
return cls(weights, biases, learning_rate, momentum, post_process, topErrorFunc, d_topErrorFunc, actFunc, d_actFunc, topActFunc,d_topActFunc)
#... Class methods
Subclass
class FixedTopErrorSignalNeuralNet(NeuralNet):
def __init__(self,
weights,
biases,
learning_rate=0.01,
momentum=0.9, #Set to zero to nut use mementum
post_process=lambda i: i, #Post Process is applied to final output only (Not used when training), but is used when checkign error rate
actFunc=sigmoid, d_actFunc=dSigmoid,
topActFunc = softmax, d_topActFunc = dSoftmax):
NeuralNet.__init__(self,
weights=weights,
biases=biases,
momentum=momentum,
post_process=post_process,
topErrorFunc=None, d_topErrorFunc=None,
actFunc=actFunc, d_actFunc=d_actFunc,
topActFunc=topActFunc, d_topActFunc=d_topActFunc)
@classmethod
def random_init(cls,
layer_sizes,
learning_rate=0.01,
momentum=0.9, #Set to zero to nut use mementum
post_process=lambda i: i, #Post Process is applied to final output only (Not used when training), but is used when checkign error rate
actFunc=sigmoid, d_actFunc=dSigmoid,
topActFunc=softmax, d_topActFunc=dSoftmax):
weights = [np.random.normal(0,0.01,(szNext,szThis)) for (szThis,szNext) in pairwise(layer_sizes)]
biases = [np.random.normal(0,0.01,sz) for sz in layer_sizes[1:]]
return cls(weights, biases, learning_rate, momentum, post_process, actFunc, d_actFunc, topActFunc,d_topActFunc)
#... Overrides etc
What the parameters do
It has been suggested that this class is doing too many things, because of its large number of parameters. Here are what they do, which may provide insight.
The functional parameters are mostly there to avoid hard-coding them in. They are all very simple mathematical (pure) 1-2 liners.
actFunc
is a mathematical activation functiond_actFunc
is its derivativetop_actFunc
is a alternative activation function used at the top of the neural net (very common technique)d_top_actFunc
is its derivativeErrorFunc
is used to calculate the error for during trainingd_errorFunc
is its derivativepost_process
is simple mathematical transform used to get the final output, when used on nontraining data
They are all parameters of the model.
One alternative could be that rather than passing them in as a parameter, I make them abstract methods, and then for each variation I could override them. This doesn't feel right to me, though something like Java's anonymous classes might work.