Logo elodees  elodees

A caring AI for a better world













Only alphabetic characters accented or not as well as the space are accepted

Logo IA




Universal approximation





No account yet ?

Sign up to access all content




In artificial neural networks, universal approximation theorems are results that establish the density of an algorithmically generated class of functions in a given functional space of interest.

The universal approximation theorem tells us that neural networks have a kind of universality.

Universal approximation theorems imply that neural networks can represent a wide variety of interesting functions when given appropriate weights.



Tested in Anaconda and Python 3.7

# Downloading dependencies:
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
 
### Setting function to approximate: *******************   #
 
# Actual Function here, Relationship: y = x^2
x = np.linspace(-30,30,100)
y = x**2
 
# Feel free to change Actual Function whatever u want to try: Ex: y=sin(x)
#x = np.linspace(0,180,200)
#y = np.sin(np.deg2rad(x))
 
 
#   ****************************************************   #
 
### Setting up the feedfoward neural network  **********   #
 
# Hyperparamters to tune: Number of Neurons and Hidden Layers, Learning Rate and Epochs.
n_neurons = 20  # number of neurons/nodes
learning_rate = 5e-3 # learning rate
 
 
model = nn.Sequential(     
          nn.Linear(1, n_neurons),
          nn.ReLU(),
          #nn.Linear(n_neurons,n_neurons),
          #nn.ReLU(),        
          nn.Linear(n_neurons,1),
          nn.ReLU()
          )
 
 
# Set up  : Input (1 Node) -> Hidden (10 nodes) -> Output (1 Node) 
# Set up 2: Input (1 Node) -> Hidden (10 nodes) -> Hidden (10 nodes) -> Output (1 Node)
 
# Important Note: If you increase the number of neurons or use a harder function to approximate, try tuning the learning rate.
#                 Tuning the learning rate is vital to properly train the network.
 
optimizer = optim.RMSprop(model.parameters(), lr=learning_rate) # define optimizer
#optimizer = optim.SGD(model.parameters(), lr=learning_rate)
 
criterion = nn.MSELoss() # define loss function
 
# ******************************************************  #
 
### Training: ******************************************  #
 
# Convert to tensor form with batch for PyTorch model.
inputs = torch.tensor(x).view(-1,1)
labels = torch.tensor(y).view(-1,1)
 
# Important Note 2: Change epochs
epochs = 20000
for epoch in range(epochs):  # loop over the data multiple times
 
    # zero the parameter gradients
    optimizer.zero_grad()
 
    # forward + backward + optimize
    outputs = model(inputs.float())
    loss = criterion(outputs, labels.float())
    loss.backward()
    optimizer.step()
 
# ******************************************************  #
 
### Running Inference over the trained model ***********  #
with torch.no_grad():
    test_inputs = torch.tensor(x).view(len(x),-1).float()
    y_hat = model(test_inputs)
    y_hat = y_hat.detach().numpy()
 
# ******************************************************  #
 
### Plot results: Actual vs Model Prediction ***********  #
plt.scatter(x,y,label='Actual Function')
plt.scatter(x,y_hat,label="Predicted Function")
plt.title(f'Number of neurons : {n_neurons} - Number of epoch : {epochs}')
plt.xlabel('Input Variable (x)')
plt.ylabel('Output Variable (y)')
plt.legend()
plt.show()
# ******************************************************  #
 


UAT - GitHub



Y = x2



Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com




Tested in Anaconda and Python 3.7

# Universal Approximation
 
import numpy as np
import math
import matplotlib.pyplot as plt
import random
import torch
 
from tqdm import tqdm
 
dtype = torch.float
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print('Using device:', device)
print()
 
#While working with GPUs
if device.type == 'cuda':
    print(torch.cuda.get_device_name(0))
    print('Memory Usage:')
    print('Allocated:', round(torch.cuda.memory_allocated(0)/1024**3,1), 'GB')
    print('Cached:   ', round(torch.cuda.memory_cached(0)/1024**3,1), 'GB')
 
# Model
 
def parameter_initialize(hidden): 
    '''Initialize parameters W and b'''
    #Input Layer to Hidden 
    W_1 = torch.randn(hidden,1, dtype = dtype, device=device) - 0.5
    b_1 = torch.randn(hidden,1, dtype = dtype, device=device) - 0.5
 
    #Hidden Layer to output
    W_2 = torch.randn(1,hidden, dtype = dtype, device=device) - 0.5
    b_2 = torch.randn(1,1, dtype = dtype, device=device) - 0.5
    return W_1, b_1, W_2, b_2
 
def forward(a_0, W_1, W_2, b_1, b_2): 
    '''Forward Propagation Function'''
    n_1 = W_1.mm(a_0) + b_1
    a_1 = []
 
    for i in range(n_1.shape[0]):
          a_1.append(float(1)/(1+math.exp(-n_1[i])))
 
    a_1 = torch.tensor(a_1, device=device).float().reshape(-1,1)
 
    n_2 = W_2.mm(a_1) + b_2
    a_2 = n_2
 
    return a_2,n_2,a_1,n_1
 
def backward(error, a_1, W_2): 
    '''Backpropagation Function'''
    s_2 = -2*1*error
    F_1 = []
    for i in range(a_1.shape[0]):
        F_1.append((1-a_1[i][0])*a_1[i][0])
    F_1 = torch.tensor(F_1, device=device).float().reshape(-1)
    F_1 = torch.diag(F_1)
 
    s_1 = F_1.mm(W_2.t())
    s_1 = s_1.mm(s_2)
    return s_1, s_2
 
def Update(W_1, W_2, b_1, b_2, lr, s_1, s_2, a_1, a_0 ): 
    '''Weight and Bias Updation'''
    W_2_new = W_2 - lr*s_2.mm(a_1.t())
    b_2_new = b_2 - lr*s_2
    W_1_new = W_1 - lr*s_1.mm(a_0.t())
    b_1_new = b_1 - lr*s_1
    return W_2_new, W_1_new, b_2_new, b_1_new
 
# Plotting Functions
 
def plot_error(lr,s,epoch,error):
    plt.figure(figsize = (10,5))
    plt.plot(epoch, error, c='blue', label=str(lr), alpha = 0.3)
    plt.xlabel('Epoch')
    plt.ylabel('Mean squared error')
    plt.legend(loc='best')
    plt.title('Mean Squared Error / {} neurons'.format(s))
    plt.show()
 
def plot_function(input,output):
    plt.figure(figsize = (10,5))
    input = input.numpy()
    plt.plot(input,output, c='green', label='function')
    plt.xlabel('data')
    plt.ylabel('Output')
    plt.legend(loc='best')
 
def plot_network(W_2, W_1, b_2, b_1, input, epochs):
    output_list = []
    for x in input:
        x = x.reshape(1,1)
        output, n_2, a_1, n_1 = forward(x,W_1,W_2,b_1,b_2)
        output = output[0]
        output_list.append(output)
    input = input.numpy()
    plt.plot(input,output_list,c='orange',label='Network')
    plt.title('Function vs Network after {} epochs'.format(epochs))
    plt.grid(True)
    plt.legend(loc='best')
    plt.show()
 
# Function to approximate
# 1. Cosine Function
 
data = torch.linspace(-3,3,100,dtype = dtype, device=device).reshape(100,1)
 
def approximate_function_1(a): 
    return math.cos(a)
 
var = []
for x in data.reshape(-1):
    var.append(approximate_function_1(x))
 
plot_function(data,var)
plt.savefig('images/cosine.jpeg')
 
# 2. Sine Function
 
def approximate_function_2(a): 
    return math.sin(a)
 
var_1 = []
for x in data.reshape(-1):
    var_1.append(approximate_function_2(x))
 
plot_function(data,var_1)
plt.savefig('images/sine.jpeg')
 
# Training
 
epochs=1000
s = 100
lr = 0.01
 
# 1. Cosine Function Approximation
 
error_list = []
epoch_list = []
 
W_1, b_1, W_2, b_2 = parameter_initialize(s)  # Parameter initialize
 
for epoch in tqdm(range(epochs)):
    epoch_list.append(epoch)
    p_1 = torch.linspace(-3,3,100,dtype = dtype, device=device).reshape(100,1)
    sum = 0
    for iter in range(p_1.shape[0]):
 
        a_0 = p_1[iter][0].reshape(1, 1)
 
        output, n_2, a_1, n_1 = forward(a_0, W_1, W_2, b_1, b_2)
        target = approximate_function_1(a_0)
        error = target - output
        square_error = (error.reshape(-1)[0]) * (error.reshape(-1)[0])
        sum += square_error
        s_1, s_2 = backward(error, a_1, W_2)
        W_2, W_1, b_2, b_1 = Update(W_1, W_2, b_1, b_2, lr, s_1, s_2, a_1, a_0)
    mean_square_error = sum/p_1.shape[0]
    #print("Epoch {} --> MSE: {}".format(epoch + 1,mean_square_error))
    error_list.append(mean_square_error)
 
y = []
 
for x in p_1.reshape(-1):
    y.append(approximate_function_1(x))
 
print('Final MSE : {}'.format(mean_square_error))
 
plot_error(lr, s, epoch_list, error_list)
 
plot_function(p_1,y)
plot_network(W_2, W_1, b_2, b_1, p_1, epochs)
 
# Sine Function Approximation
 
error_list = []
epoch_list = []
 
W_1, b_1, W_2, b_2 = parameter_initialize(s)  # Parameter initialize
 
for epoch in tqdm(range(epochs)):
    epoch_list.append(epoch)
    p_2 = torch.linspace(-3,3,100,dtype = dtype, device=device).reshape(100,1)
    sum = 0
    for iter in range(p_2.shape[0]):
 
        a_0 = p_2[iter][0].reshape(1, 1)
 
        output, n_2, a_1, n_1 = forward(a_0, W_1, W_2, b_1, b_2)
        target = approximate_function_2(a_0)
        error = target - output
        square_error = (error.reshape(-1)[0]) * (error.reshape(-1)[0])
        sum += square_error
 
        s_1, s_2 = backward(error, a_1, W_2)
        W_2, W_1, b_2, b_1 = Update(W_1, W_2, b_1, b_2, lr, s_1, s_2, a_1, a_0)
    mean_square_error = sum/p_2.shape[0]
    #print("Epoch {} --> MSE: {}".format(epoch + 1,mean_square_error))
    error_list.append(mean_square_error)
 
y = []
 
for x in p_2.reshape(-1):
    y.append(approximate_function_2(x))
 
print('Final MSE : {}'.format(mean_square_error))
 
plot_error(lr, s, epoch_list, error_list)
 
plot_function(p_2,y)
plot_network(W_2, W_1, b_2, b_1, p_2, epochs)
 
# Custom Function
 
def approximate_function_3(a): 
    return 10*pow(a,2)*math.sin(a)*math.cos(a)
 
var_3 = []
for x in data.reshape(-1):
    var_3.append(approximate_function_3(x))
 
plot_function(data, var_3)
 
error_list = []
epoch_list = []
 
W_1, b_1, W_2, b_2 = parameter_initialize(s)  # Parameter initialize
 
for epoch in tqdm(range(epochs)):
    epoch_list.append(epoch)
    p_3 = torch.linspace(-3,3,100,dtype = dtype, device=device).reshape(100,1)
    sum = 0
    for iter in range(p_3.shape[0]):
 
        a_0 = p_3[iter][0].reshape(1, 1)
 
        output, n_2, a_1, n_1 = forward(a_0, W_1, W_2, b_1, b_2)
        target = approximate_function_3(a_0)
        error = target - output
        square_error = (error.reshape(-1)[0]) * (error.reshape(-1)[0])
        sum += square_error
 
        s_1, s_2 = backward(error, a_1, W_2)
        W_2, W_1, b_2, b_1 = Update(W_1, W_2, b_1, b_2, lr, s_1, s_2, a_1, a_0)
    mean_square_error = sum/p_3.shape[0]
    #print("Epoch {} --> MSE: {}".format(epoch + 1,mean_square_error))
    error_list.append(mean_square_error)
 
y = []
 
for x in p_3.reshape(-1):
    y.append(approximate_function_3(x))
 
plot_error(lr, s, epoch_list, error_list)
 
plot_function(p_3,y)
plot_network(W_2, W_1, b_2, b_1, p_3, epochs)
 


universal-approximation - GitHub



Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com

Free image provided by pexel.com




Approximation













Welcome, my name is Eric Soupet and I am the administrator of the site elodees.com. elodees.com is a state of the art of Artificial Intelligence and aims to be collaborative, you can now offer content such as articles, events, tutorials, ... so don't hesitate !

Platform images credit : Pixabay - Pixabay License | Pexels - Pexels License