Skip to content

Commit 3e57e00

Browse files
lucasmouranorvig
authored andcommitted
Refactor backpropagation (aimacode#437)
* Create function to initialize random weights * Add sigmoid derivative function
1 parent b96f01b commit 3e57e00

File tree

4 files changed

+45
-13
lines changed

4 files changed

+45
-13
lines changed

learning.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from utils import (
44
removeall, unique, product, mode, argmax, argmax_random_tie, isclose, gaussian,
55
dotproduct, vector_add, scalar_vector_product, weighted_sample_with_replacement,
6-
weighted_sampler, num_or_str, normalize, clip, sigmoid, print_table, DataFile
6+
weighted_sampler, num_or_str, normalize, clip, sigmoid, print_table,
7+
DataFile, sigmoid_derivative
78
)
89

910
import copy
@@ -567,13 +568,17 @@ def predict(example):
567568
return predict
568569

569570

571+
def random_weights(min_value, max_value, num_weights):
572+
return [random.uniform(min_value, max_value) for i in range(num_weights)]
573+
574+
570575
def BackPropagationLearner(dataset, net, learning_rate, epochs):
571576
"""[Figure 18.23] The back-propagation algorithm for multilayer network"""
572577
# Initialise weights
573578
for layer in net:
574579
for node in layer:
575-
node.weights = [random.uniform(-0.5, 0.5)
576-
for i in range(len(node.weights))]
580+
node.weights = random_weights(min_value=-0.5, max_value=0.5,
581+
num_weights=len(node.weights))
577582

578583
examples = dataset.examples
579584
'''
@@ -611,10 +616,11 @@ def BackPropagationLearner(dataset, net, learning_rate, epochs):
611616
delta = [[] for i in range(n_layers)]
612617

613618
# Compute outer layer delta
614-
err = [t_val[i] - o_nodes[i].value
615-
for i in range(o_units)]
616-
delta[-1] = [(o_nodes[i].value) * (1 - o_nodes[i].value) *
617-
(err[i]) for i in range(o_units)]
619+
620+
# Error for the MSE cost function
621+
err = [t_val[i] - o_nodes[i].value for i in range(o_units)]
622+
# The activation function used is the sigmoid function
623+
delta[-1] = [sigmoid_derivative(o_nodes[i].value) * err[i] for i in range(o_units)]
618624

619625
# Backward pass
620626
h_layers = n_layers - 2
@@ -623,11 +629,9 @@ def BackPropagationLearner(dataset, net, learning_rate, epochs):
623629
h_units = len(layer)
624630
nx_layer = net[i+1]
625631
# weights from each ith layer node to each i + 1th layer node
626-
w = [[node.weights[k] for node in nx_layer]
627-
for k in range(h_units)]
632+
w = [[node.weights[k] for node in nx_layer] for k in range(h_units)]
628633

629-
delta[i] = [(layer[j].value) * (1 - layer[j].value) *
630-
dotproduct(w[j], delta[i+1])
634+
delta[i] = [sigmoid_derivative(layer[j].value) * dotproduct(w[j], delta[i+1])
631635
for j in range(h_units)]
632636

633637
# Update weights
@@ -744,7 +748,8 @@ def LinearLearner(dataset, learning_rate=0.01, epochs=100):
744748
X_col = [ones] + X_col
745749

746750
# Initialize random weigts
747-
w = [random.uniform(-0.5, 0.5) for _ in range(len(idx_i) + 1)]
751+
num_weights = len(idx_i) + 1
752+
w = random_weights(min_value=-0.5, max_value=0.5, num_weights=num_weights)
748753

749754
for epoch in range(epochs):
750755
err = []

tests/test_learning.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from learning import parse_csv, weighted_mode, weighted_replicate, DataSet, \
22
PluralityLearner, NaiveBayesLearner, NearestNeighborLearner, \
33
NeuralNetLearner, PerceptronLearner, DecisionTreeLearner, \
4-
euclidean_distance, grade_learner, err_ratio
4+
euclidean_distance, grade_learner, err_ratio, random_weights
55
from utils import DataFile
66

77

@@ -124,3 +124,18 @@ def test_perceptron():
124124

125125
assert grade_learner(perceptron, tests) > 1/2
126126
assert err_ratio(perceptron, iris) < 0.4
127+
128+
129+
def test_random_weights():
130+
min_value = -0.5
131+
max_value = 0.5
132+
num_weights = 10
133+
134+
test_weights = random_weights(min_value, max_value, num_weights)
135+
136+
assert len(test_weights) == num_weights
137+
138+
for weight in test_weights:
139+
assert weight >= min_value and weight <= max_value
140+
141+

tests/test_utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ def test_gaussian():
154154
assert gaussian(3,1,3) == 0.3989422804014327
155155

156156

157+
def test_sigmoid_derivative():
158+
value = 1
159+
assert sigmoid_derivative(value) == 0
160+
161+
value = 3
162+
assert sigmoid_derivative(value) == -6
163+
164+
157165
def test_step():
158166
assert step(1) == step(0.5) == 1
159167
assert step(0) == 1

utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ def clip(x, lowest, highest):
250250
return max(lowest, min(x, highest))
251251

252252

253+
def sigmoid_derivative(value):
254+
return value * (1 - value)
255+
256+
253257
def sigmoid(x):
254258
"""Return activation value of x with sigmoid function"""
255259
return 1/(1 + math.exp(-x))

0 commit comments

Comments
 (0)