# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + [markdown] id="aF3bZfc3tyv-"
# Implement neural network from scratch using python for the following datasets and predict the values for the following datasets:
# 1. Boston House prices dataset: https://www.kaggle.com/vikrishnan/boston-house-prices
#
# -

# ### Importing Libraries

import pandas as pd
import numpy as np
import io
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
# %matplotlib inline

# ### Reading the Dataset

column_names = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
df= pd.read_csv('housing.csv', header=None, delimiter=r"\s+", names=column_names)                                                              #load dataset
m=df.shape[0]               # m--> number of rows
n=df.shape[1]               # n--> number of columns
df

# +
sc = StandardScaler()
df.iloc[:,:]=sc.fit_transform(df.iloc[:,:])

x_train, x_test, y_train, y_test = [i.to_numpy() for i in train_test_split(df.iloc[:,:n-1],df.iloc[:,-1],test_size=0.2)]
# 80-20 ratio split of the data and convertion to numpy array


print("Size of train_x : {}".format(x_train.shape))
print("Size of train_y : {}".format(y_train.shape))
print("Size of test_x : {}".format(x_test.shape))
print("Size of test_y : {}".format(y_test.shape))         

# -

# ### Define Relu Function

# +
def relu(z):
    return np.maximum(0,z)

print("Relu(5) : {}".format(relu([5,-2,3,-1,4,0,-3])))
# -

# ### Plotting Relu

plt.plot(np.linspace(-50,100,5000),relu(np.linspace(-50,100,5000)),c = 'orange')


# ### Intializing Parameters of the Network

# +
def initialize_params(layer_sizes):
    params = {} #defining paramets as dictionary which contains weights and biases as W and B in the prefix
    for i in range(1, len(layer_sizes)):
        params['W' + str(i)] = np.random.randn(layer_sizes[i], layer_sizes[i-1])*0.01
        params['B' + str(i)] = np.random.randn(layer_sizes[i],1)*0.01
    return params

params_temp = initialize_params([13,0,20,10,1])
params_temp


# -

# ###  Forward Propagation

# +
def forward_propagation(X_train, params):
    layers = len(layer_sizes)-1 # Defining the number of layers
    values = {} #defining as dictionary to store the values of Z and Activations required for the backpropagations
    
    for i in range(1, layers+1):
        if i==1: #for first layer, the imput is multiplied with weights and aDDED with bias
            values['Z' + str(i)] = np.dot(params['W' + str(i)], X_train) + params['B' + str(i)]
            values['A' + str(i)] = relu(values['Z' + str(i)]) #computing activations
        else:
            values['Z' + str(i)] = np.dot(params['W' + str(i)], values['A' + str(i-1)]) + params['B' + str(i)]
            if i==layers: #for last layer, activations are computed without applying relu
                values['A' + str(i)] = values['Z' + str(i)]
            else:
                values['A' + str(i)] = relu(values['Z' + str(i)])
    return values

# val = forward_propagation(x_train.T,params_temp)


# -

# ### Compute Cost

def compute_cost(values, Y_train): #mean squared error computation
    layers = len(layer_sizes)-1
    Y_pred = values['A' + str(layers)]
    cost = 1/(2*len(Y_train)) * (np.sum(np.square(Y_pred - Y_train)))
    return cost


# ###  Back Prop

def backward_propagation(params, values, X_train, Y_train):
    layers = len(layer_sizes)-1
    m = len(Y_train)
    grads = {}
    for i in range(layers,0,-1):
        if i==layers: #for last layer dz = da as no relu non-linearity has been applied
            dA = 1/m * (values['A' + str(i)] - Y_train)
            dZ = dA
        else:
            dA = np.dot(params['W' + str(i+1)].T, dZ) #for internal layers multiply the dz of next layer with the weights
            dZ = np.multiply(dA, np.where(values['A' + str(i)]>=0, 1, 0)) # calculate dz using relu concept of backprop on da
        if i==1:
            grads['W' + str(i)] = 1/m * np.dot(dZ, X_train.T)
            grads['B' + str(i)] = 1/m * np.sum(dZ, axis=1, keepdims=True)
        else:
            grads['W' + str(i)] = 1/m * np.dot(dZ,values['A' + str(i-1)].T)
            grads['B' + str(i)] = 1/m * np.sum(dZ, axis=1, keepdims=True)
    return grads


# ###  Update Parameters with Gradients

def update_params(params, grads, learning_rate):
    layers = len(layer_sizes)-1
    params_updated = {}
    for i in range(1,layers+1):
        params_updated['W' + str(i)] = params['W' + str(i)] - learning_rate * grads['W' + str(i)]
        params_updated['B' + str(i)] = params['B' + str(i)] - learning_rate * grads['B' + str(i)]
    return params_updated


# ### Compute Test Loss

def compute_test_loss(x_test,y_test, params):
    values_train = forward_propagation(x_test.T, params)
    test_loss = np.sqrt(mean_squared_error(y_test, values_train['A' + str(len(layer_sizes)-1)].T))
    return test_loss


# ### Predicition

def predict(X, params):
    values = forward_propagation(X.T, params)
    predictions = values['A' + str(len(values)//2)].T
    return predictions


# ###  Conjuring up a Neural Net Model

# + colab={"base_uri": "https://localhost:8080/"} id="wSqY-bZG9Z8A" outputId="efdbbaaa-2549-4662-c7dc-e37b071dac29"
def model(X_train, Y_train, layer_sizes, num_iters, learning_rate):
    train_loss = []
    test_loss = []
    params = initialize_params(layer_sizes)
    
    for i in range(num_iters):
        values = forward_propagation(X_train.T, params)
        
        cost = compute_cost(values, Y_train.T)
        test_cost = compute_test_loss(x_test,y_test,params)
        
        train_loss.append(cost)
        test_loss.append(test_cost)
        
        grads = backward_propagation(params, values,X_train.T, Y_train.T)
        params = update_params(params, grads, learning_rate)
        
        if(i%100 == 0):
            print("Current Learning Rate is : {}".format(learning_rate))
            print('Cost at iteration ' + str(i+1) + ' = ' + str(cost) + '\n')
            if(i!=0):
                learning_rate = learning_rate*0.95 #learning rate scheduler
    
    fig, ax = plt.subplots(1,2, figsize = (15,8))
    
    ax[0].plot(range(num_iters),train_loss)
    ax[0].set_title("training loss trend")
    ax[1].plot(range(num_iters),test_loss, color = 'red')
    ax[1].set_title("test loss trend")
    
    return params


# +
layer_sizes = [13, 32, 64, 32, 8, 1]            #set layer sizes ; size of the first and last layer must\
                                                #be according to the features and expected output dimensions
num_iters = 5000                                 #set number of training iterations over
learning_rate = 0.1                              #set learning rate for gradient descent
params = model(x_train, y_train, layer_sizes, num_iters, learning_rate)           #train the model on the traingin data
test_rmse= compute_test_loss(x_test, y_test, params)  #get training and test accuracy

print('Root Mean Squared Error on Testing Data = ' + str(test_rmse))
# -

# ### Thank You

StarcoderdataJupyterScriptsDedupFiltered