In [1]:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
matplotlib.style.use('ggplot')
from mpl_toolkits.mplot3d import Axes3D
import IPython.html.widgets as widg
from IPython.display import clear_output
import sys
%matplotlib inline


:0: FutureWarning: IPython widgets are experimental and may change in the future.

In [2]:
class Network:
    def __init__(self, shape):
        """The base network class. This defines a simple feed-forward network with appropriate weights and biases.
        
        Arguments:
        shape (list-like): This defines the # of layers and # of neurons per layer in your network.
                           Each element of the array or list adds a new layer with the number neurons specified by the element.
        Variables:
        self.shape: see shape.
        self.weights: A list of numpy arrays containing the weights corresponding to each channel between neurons.
        self.biases: A list of numpy arrays containing the biases corresponding to each neuron.
        self.errors: A list of numpy arrays containing the error of each neurons in any iteration of the training process.
        self.eta: A float representing the learning rate.
        self.lam: A scale factor used in L2 regularization
        """
        
        self.shape = np.array(shape) #shape is array-like, i.e. (2,3,4) is a 2 input, 3 hidden node, 4 output network
        self.weights = [np.random.ranf((self.shape[i],self.shape[i-1]))*.1 for i in range(1,len(self.shape))]
        self.biases = [np.random.ranf((self.shape[i],))*.1 for i in range(1,len(self.shape))]
        self.errors = [np.random.ranf((self.shape[i],)) for i in range(1,len(self.shape))]
        self.eta = .1
        self.lam = .01
    def sigmoid(self, inputs):
        """Computes the sigmoid function of some input.
        
        Arguments:
        inputs (float or numpy array): The input or inputs to be fed through the sigmoid function.
        """
        
        return 1/(1+np.exp(-inputs))
    def feedforward(self, inputs):
        """Feeds inputs through the network and returns the output.
        
        Arguments:
        inputs (numpy array): The inputs to the network, must be the same size as the first(input) layer.
        
        Variables:
        self.activation: A list of numpy arrays corresponding to the output of each neuron in your network.
        """
        
        assert inputs.shape==self.shape[0] #inputs must feed directly into the first layer.
        self.activation = [np.zeros((self.shape[i],)) for i in range(len(self.shape))]
        self.activation[0] = inputs
        for i in range(1,len(self.shape)):
            self.activation[i]=self.sigmoid(np.dot(self.weights[i-1],self.activation[i-1])+self.biases[i-1])
        return self.activation[-1]
    def comp_error(self, answer):
        """Computes the errors of each neuron.(Typically called Back Propagation)
        
        Arguments:
        answers (numpy array): The expected output from the network.
        """
        if (self.activation[-1]-answer).any>.15:
            self.eta = .005
        else: 
            self.eta = .5
        
        assert answer.shape==self.activation[-1].shape
        self.errors[-1] = np.pi*np.tan(np.pi/2*(self.activation[-1]-answer))*1/np.cos(np.pi/2*(self.activation[-1]-answer))**2*np.exp(np.dot(self.weights[-1],self.activation[-2])+self.biases[-1])/(np.exp(np.dot(self.weights[-1],self.activation[-2])+self.biases[-1])+1)**2
        for i in range(len(self.shape)-2, 0, -1):
            self.errors[i-1] = self.weights[i].transpose().dot(self.errors[i])*np.exp(np.dot(self.weights[i-1],self.activation[i-1])+self.biases[i-1])/(np.exp(np.dot(self.weights[i-1],self.activation[i-1])+self.biases[i-1])+1)**2
    def grad_descent(self):
        """Changes each variable based on the gradient descent algorithm."""
        
        #for i in range(len(self.biases)):
         #   self.biases[i]=self.biases[i]-self.eta*self.errors[i]
        for i in range(len(self.weights)):
            self.biases[i]=self.biases[i]-self.eta*self.errors[i]
            for j in range(self.weights[i].shape[0]):
                for k in range(self.weights[i].shape[1]):
                    self.weights[i][j,k] = (1-self.eta*self.lam/1000)*self.weights[i][j,k] - self.eta*self.activation[i][k]*self.errors[i][j]
    def train(self, inputs, answer):
        """Trains the network.
        
        Arguments:
        inputs (numpy array): The inputs to the network, must be the same size as the first(input) layer.
        answers (numpy array): The expected output from the network, must be the same size as the last(output) layer.
        """
        
        self.feedforward(inputs)
        self.comp_error(answer)
        self.grad_descent()

In [3]:
n1 = Network([2,15,1])
print n1.feedforward(np.array([1,2]))
for i in range(1000):
    n1.train(np.array([1,200]), np.array([.5]))
print n1.feedforward(np.array([1,2]))


[ 0.62259247]
[ 0.51048301]

In [4]:
from sklearn.datasets import load_digits
digits = load_digits()
print(digits.data[0]*.01)


[ 0.    0.    0.05  0.13  0.09  0.01  0.    0.    0.    0.    0.13  0.15
  0.1   0.15  0.05  0.    0.    0.03  0.15  0.02  0.    0.11  0.08  0.    0.
  0.04  0.12  0.    0.    0.08  0.08  0.    0.    0.05  0.08  0.    0.
  0.09  0.08  0.    0.    0.04  0.11  0.    0.01  0.12  0.07  0.    0.
  0.02  0.14  0.05  0.1   0.12  0.    0.    0.    0.    0.06  0.13  0.1   0.
  0.    0.  ]

In [11]:
num = [0,0,0,0,0,0,0]

num[0] = Network([64, 7, 10])
num[1] = Network([64, 7, 7, 10])
num[2] = Network([64, 7, 7, 7, 10])
num[3] = Network([64, 7, 7, 7, 7, 10])
num[4] = Network([64, 7, 7, 7, 7, 7, 10])
num[5] = Network([64, 7, 7, 7, 7, 7, 7,  10])
num[6] = Network([64, 7, 7, 7, 7, 7, 7, 7, 10])

In [6]:
# %timeit num.feedforward(digits.data[89]*.01)
# %timeit num.comp_error(np.eye(10)[digits.target[89]])
# %timeit num.grad_descent()

In [12]:
def Train_it(num, itera):
    iden = np.eye(10)
    acc = np.zeros((itera,))
    trainer = zip(digits.data,digits.target)
    perm = np.random.permutation(trainer)
    trains = perm[:1000]
    test = perm[1001:]
    #num = Network([64, 14, 10])
    print num.feedforward(digits.data[89]*.01)
    for i in range(itera):
        print(float(100*i/(itera*1.0)))
        for dig, ans in trains:
            num.train(dig*.01,iden[ans])
        cor = 0
        tot = 0
        for dig, ans in test:
            if num.feedforward(dig*.01).argmax()==ans:
                cor += 1
            tot += 1
        acc[i] = cor/float(tot)
    return acc

In [14]:
acc = Train_it(num[0], 100)
print(acc)


[ 0.08919598  0.08919598  0.08919598  0.08919598  0.08919598  0.08919598
  0.08919598  0.08919598  0.08919598  0.08919598  0.08919598  0.08919598
  0.13944724  0.18341709  0.22110553  0.28140704  0.38693467  0.49748744
  0.55904523  0.60050251  0.61557789  0.64070352  0.65954774  0.66582915
  0.68341709  0.70100503  0.71231156  0.72361809  0.74371859  0.75753769
  0.7638191   0.77135678  0.77889447  0.78768844  0.79899497  0.80527638
  0.80778894  0.81155779  0.81407035  0.81909548  0.8241206   0.83040201
  0.83417085  0.83417085  0.83919598  0.84296482  0.84296482  0.84170854
  0.84422111  0.84296482  0.85050251  0.85301508  0.85301508  0.85427136
  0.85929648  0.86055276  0.86055276  0.86432161  0.86809045  0.86683417
  0.86809045  0.87060302  0.87311558  0.8781407   0.88190955  0.88190955
  0.88944724  0.89572864  0.89698492  0.89824121  0.90075377  0.90201005
  0.90452261  0.90577889  0.90703518  0.9120603   0.91457286  0.91457286
  0.91582915  0.91959799  0.92085427  0.91959799  0.91959799  0.91959799
  0.92211055  0.92336683  0.92336683  0.92336683  0.92336683  0.92462312
  0.92713568  0.92713568  0.92839196  0.92839196  0.92713568  0.92839196
  0.92839196  0.92839196  0.92839196  0.92839196]

In [50]:
accu = np.zeros((7,50))
for i in range(7):
    accu[i] = Train_it(num[i], 50)
print(accu)


0.0879396984925

In [10]:
plt.figure(figsize=(15,10))
plt.plot(np.linspace(0,100,100),acc)


Out[10]:
[<matplotlib.lines.Line2D at 0x7f9b07013b10>]

In [37]:
np.savetxt("Accuracy_Data_run_8.dat", acc)

In [40]:
def plot_epochs(az_angle, eleva):
    fig = plt.figure(figsize=(15, 10))
    ax = fig.add_subplot(111, projection='3d')
    X, Y = np.meshgrid(np.linspace(0,500,500), np.linspace(8,14, 7))
    ax.plot_surface(X, Y, acc)
    ax.view_init(elev=eleva, azim=az_angle)

In [41]:
widg.interact(plot_epochs, az_angle=(0, 360, 1), eleva=(0,20,1))



In [42]:
print acc


[[ 0.10183639  0.10183639  0.10183639 ...,  0.97161937  0.97161937
   0.97161937]
 [ 0.10183639  0.10183639  0.10183639 ...,  0.97328881  0.97328881
   0.97328881]
 [ 0.10183639  0.10183639  0.10183639 ...,  0.97217585  0.97217585
   0.97217585]
 ..., 
 [ 0.10183639  0.10183639  0.10183639 ...,  0.96939343  0.96939343
   0.96939343]
 [ 0.10183639  0.10183639  0.10183639 ...,  0.97273233  0.97273233
   0.97273233]
 [ 0.10183639  0.10183639  0.10183639 ...,  0.97106288  0.97106288
   0.97106288]]

In [ ]: