holehouse.org Blog Machine learning notes

09: Neural Networks - Learning

Neural network cost function

Types of classification problems with NNs

yK e.g.[1000],[0100],[0010],[0001] Left to right: pedestrian, car, motorcycle, truck.

Cost function for neural networks

J(θ)=1m[i=1my(i)loghθ(x(i))+(1y(i))log(1hθ(x(i)))]+λ2mj=1nθj2
hΘ(x)K(hΘ(x))i=ithoutput J(Θ)=1m[i=1mk=1Kyk(i)log(hΘ(x(i)))k+(1yk(i))log(1(hΘ(x(i)))k)]+λ2ml=1L1i=1slj=1sl+1(Θji(l))2

Woah there - lets take a second to try and understand this!

First half

1m[i=1mk=1Kyk(i)log(hΘ(x(i)))k+(1yk(i))log(1(hΘ(x(i)))k)]

Second half

λ2ml=1L1i=1slj=1sl+1(Θji(l))2

Summary of what's about to go down

The following section is, I think, the most complicated thing in the course, so I'm going to take a second to explain the general idea of what we're going to do;

Back propagation algorithm

J(Θ)=1m[i=1mk=1Kyk(i)log(hΘ(x(i)))k+(1yk(i))log(1(hΘ(x(i)))k)]+λ2ml=1L1i=1slj=1sl+1(Θji(l))2
a(1)=x z(2)=Θ(1)a(1) a(2)=g(z(2)) z(3)=Θ(2)a(2) a(3)=g(z(3)) z(4)=Θ(3)a(3) a(4)=hΘ(x)=g(z(4)) The bias unit a0 is added to a(2) and a(3) after each activation.

What is back propagation?

Analyzing the mathematics

Why do we do this?

Putting it all together to get the partial derivatives!

Back propagation intuition

Forward propagation with pictures!

Back propagation

J(Θ)=1m[i=1my(i)log(hΘ(x(i)))+(1y(i))log(1(hΘ(x(i))))]+λ2ml=1L1i=1slj=1sl+1(Θji(l))2
cost(i)=y(i)loghΘ(x(i))+(1y(i))log(1hΘ(x(i))) The original slide writes the second logarithm as log h(x(i)), dropping the 1 − ; it is corrected here to match the same expression elsewhere in this chapter.

Implementation notes - unrolling parameters (matrices)

def cost_function(theta):
    ...
    return jval, gradient

res = minimize(cost_function, initial_theta, jac=True)
opt_theta = res.x
Neural Network (L = 4): Θ(1),Θ(2),Θ(3)— matrices D(1),D(2),D(3)— matrices In the code these are Theta1, Theta2, Theta3 and D1, D2, D3.

Example

s1=10,s2=10,s3=1 Θ(1)10×11,Θ(2)10×11,Θ(3)1×11 D(1)10×11,D(2)10×11,D(3)1×11
Theta1 = np.ones((10, 11))
Theta2 = np.ones((10, 11))
Theta3 = np.ones((1, 11))

theta_vec = np.concatenate([Theta1.ravel(), Theta2.ravel(), Theta3.ravel()])
theta_vec.shape   # (231,) = 110 + 110 + 11, one long vector

np.array_equal(theta_vec[0:110].reshape(10, 11), Theta1)   # True - the round trip

Unroll and reshape for the example network above: three matrices flatten into one 231-element vector and come back out unchanged.

Gradient checking

θ1J(θ)J(θ1+ϵ,θ2,θ3,)J(θ1ϵ,θ2,θ3,)2ϵ θ2J(θ)J(θ1,θ2+ϵ,θ3,)J(θ1,θ2ϵ,θ3,)2ϵ θnJ(θ)J(θ1,θ2,θ3,,θn+ϵ)J(θ1,θ2,θ3,,θnϵ)2ϵ Each partial derivative is checked by nudging one parameter and leaving the rest alone.
def J(theta):                  # a simple stand-in cost: J = sum of squares
    return (theta ** 2).sum()

theta = np.array([1.0, -2.0, 3.0])
EPSILON = 1e-4

grad_approx = np.zeros(3)
for i in range(3):
    theta_plus  = theta.copy(); theta_plus[i]  += EPSILON
    theta_minus = theta.copy(); theta_minus[i] -= EPSILON
    grad_approx[i] = (J(theta_plus) - J(theta_minus)) / (2 * EPSILON)

grad_approx   # [2., -4., 6.]  - the analytic gradient of sum-of-squares is 2*theta

Gradient checking on a cost simple enough to differentiate by hand: the two-sided differences land on 2θ to ten decimal places.

for i in range(n):
    theta_plus = theta.copy()
    theta_plus[i] += EPSILON
    theta_minus = theta.copy()
    theta_minus[i] -= EPSILON
    grad_approx[i] = (J(theta_plus) - J(theta_minus)) \
                     / (2 * EPSILON)

Random initialization

Putting it all together

for i in range(m):
    forward propagation on (xi, yi)   # get activation (a) terms
    back propagation on (xi, yi)      # get delta (δ) terms
    compute Δl := Δl + δl+1(al)T

With this done compute the partial derivative terms