SolidWorks Videos

Showing posts with label learning. Show all posts
Showing posts with label learning. Show all posts

Tuesday, April 15, 2025

Muon: An optimizer for the hidden layers of neural networks (XPU version)

     With help from [1], the Muon optimizer now works on Intel XPUs. As of now, tested on Intel Iris Xe that comes bundled with Core i7 1185G7. The purpose of this post is to help hobbyists such as yours truly to be able to use Muon on whatever hardware resources they have available. The version posted on [2] has been hardcoded for CUDA, it seems.

     My interest in this optimizer is to use it to try to solve pressing problems in engineering mechanics such as vortex shedding and fracture of materials, for solid mechanics refer to here and for fluid dynamics, please read this post.

     Muon optimizer also fails to predict vortex shedding. As of writing, SOAP is the best optimizer for training physics informed neural networks. With SOAP, lid-driven cavity (Re = 3200), flow past airfoils, squares and circles (Re = 50), flow past backwards-facing step (Re = 400) have been validated in the field of fluid dynamics. As far as solid mechanics is concerned, the neural network has successfully learnt 3-point bending test, tensile test and compression test on specimens with tremendous success. In a sea of optimizers that claim to perform better that ADAM; at least for PINN training, SOAP works best. It is to be noted that, all my PINNs are without any kind of training data, this is important as finite difference method, read free code here, and finite element methods do not require data and work with boundary conditions only.

     The complex case of vortex shedding is still a mystery. May be a new kind of activation function is required. Constant / learnable sinusoidal and polynomial based activation functions have been used with SOAP by yours truly to no success in predicting vortex shedding, again without training data. With training data, even ADAM is enough.

Code

import torch

from torch import Tensor


def zeropower_via_newtonschulz5(G: Tensor, steps: int) -> Tensor:

    assert G.ndim >= 2


    a, b, c = (3.4445, -4.7750, 2.0315)


    # Ensure consistent type and device

    X = G.to(dtype=torch.float32, device=G.device)


    if G.size(-2) > G.size(-1):

        X = X.mT


    # Normalize

    norm = X.norm(dim=(-2, -1), keepdim=True) + 1e-7

    X = X / norm


    for _ in range(steps):

        A = X @ X.mT

        B = b * A + c * (A @ A)

        X = a * X + B @ X


    if G.size(-2) > G.size(-1):

        X = X.mT


    return X



class Muon(torch.optim.Optimizer):

    

    def __init__(self, params, lr=0.02, weight_decay=0.01, momentum=0.95, nesterov=True, ns_steps=5, rank=None, world_size=None, device=None):

        if device is None:

            device = torch.device("cpu")  # or use "xpu" if always on Intel

            self.device = device

        if (rank is None) or (world_size is None):

            raise Exception("world_size and rank params required, if you want to use this optimizer on a single GPU, pass rank=0 and world_size=1.")

        self.rank = rank

        self.world_size = world_size

        defaults = dict(lr=lr, weight_decay=weight_decay, momentum=momentum, nesterov=nesterov, ns_steps=ns_steps)

        params: list[Tensor] = [*params]

        param_groups = []

        for size in {p.numel() for p in params}:

            b = torch.empty(world_size, size, dtype=torch.bfloat16, device=self.device)

            # b = torch.empty(world_size, size, dtype=torch.bfloat16, device="cuda")

            group = dict(params=[p for p in params if p.numel() == size],

                         update_buffer=b, update_buffer_views=[b[i] for i in range(world_size)])

            param_groups.append(group)

        super().__init__(param_groups, defaults)

        

    @torch.no_grad()

    def step(self):

        for group in self.param_groups:

            # Fetch optimizer settings

            params: list[Tensor] = group["params"]

            lr = group["lr"]

            weight_decay = group["weight_decay"]

            momentum = group["momentum"]

            nesterov = group["nesterov"]

            ns_steps = group["ns_steps"]


            for p in params:

                g = p.grad

                if g is None:

                    continue

                

                # Momentum buffer

                state = self.state[p]

                if "momentum_buffer" not in state:

                    state["momentum_buffer"] = torch.zeros_like(g)

                    

                    buf: Tensor = state["momentum_buffer"]

                    buf.lerp_(g, 1 - momentum)

                    

                    g = g.lerp_(buf, momentum) if nesterov else buf

                    

                    # Handle convolutional filters

                    if g.ndim == 4:

                        g = g.view(len(g), -1)


                if g.ndim < 2:

                    # Skip Muon orthogonalization for 0D/1D (handled by AdamW)

                    continue

                

                # Orthogonalize gradient

                g = zeropower_via_newtonschulz5(g, steps=ns_steps).view_as(p)

                

                # Weight decay + update

                p.mul_(1 - lr * weight_decay)

                p.add_(g, alpha=-lr * max(1, p.size(-2) / p.size(-1))**0.5)

Implementation

     At first, define parameters of the network to be optimized by Muon and the other optimizer of choice by using the following statements. Afterwards, select the optimizer parameters.

muon_params = [p for p in model.parameters() if p.requires_grad and p.ndim >= 2] # define parameters to be updated by muon
AdamW_params = [p for p in model.parameters() if p.requires_grad and p.ndim < 2] # define parameters to be updated by AdamW
muon_optimizer = Muon(muon_params, lr = 0.02, momentum = 0.95, rank = 0, world_size = 1)
AdamW_optimizer = optim.AdamW(AdamW_params, lr = 3e-4, betas = (0.90, 0.95), weight_decay = 0.01)
optimizer = [muon_optimizer, soap_optimizer]

     After defining the optimizers and selecting the parameters to be updated using muon and AdamW or any other optimizer of choice, this is what the training loop could like, basically.

for epoch in range(5001):
    for opt in optimizer:
        opt.zero_grad() # clear gradients of all optimized variables
    loss_value = loss_fn(train_points) # compute prediction by passing inputs to model
    loss_value.backward() # compute gradient of loss with respect to model parameters
    for opt in optimizer:
        opt.step() # perform parameter up date
    if epoch % 100 == 0:
        print(f"{epoch} {loss_value.item()}"

References

[1] OpenAI, ChatGPT, April 15, 2025. [Online]. Available: https://chat.openai.com/

[2] https://github.com/KellerJordan/Muon

Tuesday, January 16, 2024

Feed Forward Back Propagating Neural Network (Regression) Theory

     This post is about the theory about the code shared in Feed Forward Back Propagating Neural Network (Regression)πŸ‘Ύ. As far as I understand this alien technology πŸ‘½.














     Thank you for reading! If you want to hire me as you brand new PhD student or want to collaborate on research, please reach out!

Sunday, January 14, 2024

      After successfully creating my own neural network from scratch using math and calculus only [here], I have taught myself to do the same in TensorFlow using CPU only. So one can run this without CUDA GPU. I have added comments; if the readers are like me and migrating from MATLAB; the comments will help, probably πŸ˜•. The results are shown in Fig. 1.

# import libraries

import numpy as np

import matplotlib.pyplot as plt

import tensorflow as tf

from tensorflow.keras import layers, models, optimizers, callbacks


# generate data

x = np.linspace(0, 1 * np.pi, round((1 * np.pi) / (np.pi / 16))).reshape(-1, 1)  # reshape is like x' in MATLAB, create neural network inputs from 0 to pi, with spacing of pi/16

y = x**2 * np.sin(x) # the function we want neural network to learn


# create neural network

tf.random.set_seed(42) # initialize neural network weights and biases to same values everytime to ensure reproducibility

model = models.Sequential() # create feed forward back propagating neural network

model.add(layers.Dense(5, activation='sigmoid', input_shape=(1,))) # 1st hidden layer, input_shape=(1,) means input must be column vector

model.add(layers.Dense(1)) # output layer, no activation in case of regression problems

custom_optimizer = optimizers.Adam(learning_rate=0.1) # learning rate for Adam optimizer

model.compile(optimizer=custom_optimizer, loss='mean_squared_error') # choose loss function

early_stopping = callbacks.EarlyStopping( # stopping criterion 

    monitor='loss', # monitor loss

    min_delta=0.0001, # minimum change to qualify as an improvement

    patience=100, # number of epochs with no improvement to stop training

    mode='min' # stop when the loss has stopped decreasing

)


# train the model

model.fit(x, y, epochs=1000, verbose=0, callbacks=[early_stopping]) 


# make predictions

x_predict = np.linspace(0, 1 * np.pi, round((1 * np.pi) / (np.pi / 31))).reshape(-1, 1) # create prediction data, entirely different from training data

y_predict = model.predict(x_predict) # neural network prediction


# plotting

plt.figure(dpi=300) # image resolution

plt.scatter(x, y, label='Actual Data', facecolors='none', edgecolors='red', alpha=1)

plt.plot(x_predict, y_predict, color='blue', label='Neural Network Predictions')

plt.xlabel('x')

plt.ylabel('f(x)')

plt.legend()

plt.show()


Fig. 1, Comparison of results

     Results are compared between TensorFlow and my own code [here] Fig. 2. For a simple case shown, TensorFlow takes 2.5s from pressing the run button to plotting. My own code takes 0.5 s. It's neck and neck 🀣. Obviously, learning rate, hidden layers, hidden layer neurons, epochs and optimizers are same in both cases.


Fig. 2, Own code, VS analytical VS TF

     If you want to hire me as your awesome PhD student, please reach out! Thank you very much for reading!

Friday, September 22, 2023

Machine Learning (ML) Accelerated Computational Fluid Dynamics (CFD) Attempt

     An attempt to model ML πŸ§ πŸ’» accelerated CFD 🌬. Idea was to make CFD with 10x10 grid and show results on 100x100 or even more fine grid. Needs a better ML algorithm. This uses Deep Feed Forward Back Propagating Neural Network. Can be a good topic for my little PhD. Please reach out if you want to collaborate!


%% clear and close

close all

clear

clc


%% define spatial and temporal grids

h = 1/10; % grid spacing

cfl = h*1; % cfl number

L = 1; % cavity length

D = 1; % cavity depth

Nx = round((L/h)+1); % grid points in x-axis

Ny = round((D/h)+1); % grid points in y-axis

nu = 0.000015111; % kinematic viscosity

Uinf = 0.0015111; % free stream velocity / inlet velocity  / lid velocity

dt = h*cfl/Uinf; % time step

travel = 2; % times the disturbance travels entire length of computational domain

TT = travel*L/Uinf; % total time

ns = TT/dt; % number of time steps

l_square = 1; % length of square

Re = l_square*Uinf/nu; % Reynolds number

rho = 1.2047; % Initial fluid density


%% initialize flowfield

u = zeros(Nx,Ny); % x-velocity

v = zeros(Nx,Ny); % y-velocity

p = zeros(Nx,Ny); % pressure

i = 2:Nx-1; % spatial interior nodes in x-axis

j = 2:Ny-1; % spatial interior nodes in y-axis

[X, Y] = meshgrid(0:h:L, 0:h:D); % spatial grid

maxNumCompThreads('automatic'); % select CPU cores


%% solve 2D Navier-Stokes equations

for nt = 1:ns

    p(i, j) = ((p(i+1, j) + p(i-1, j)) * h^2 + (p(i, j+1) + p(i, j-1)) * h^2) ./ (2 * (h^2 + h^2)) ...

        - h^2 * h^2 / (2 * (h^2 + h^2)) * (rho * (1 / dt * ((u(i+1, j) - u(i-1, j)) / (2 * h) + (v(i, j+1) - v(i, j-1)) / (2 * h)))); % pressure poisson

    p(1, :) = p(2, :); % dp/dx = 0 at x = 0

    p(Nx, :) = p(Nx-1, :); % dp/dx = 0 at x = L

    p(:, 1) = p(:, 2); % dp/dy = 0 at y = 0

    p(:, Ny) = 0; % p = 0 at y = D

    u(i, j) = u(i, j) - u(i, j) * dt / (2 * h) .* (u(i+1, j) - u(i-1, j)) ...

        - v(i, j) * dt / (2 * h) .* (u(i, j+1) - u(i, j-1)) - dt / (2 * rho * h) * (p(i+1, j) - p(i-1, j)) ...

        + nu * (dt / h^2 * (u(i+1, j) - 2 * u(i, j) + u(i-1, j)) + dt / h^2 * (u(i, j+1) - 2 * u(i, j) + u(i, j-1))); % x-momentum

    u(1, :) = 0; % u = 0 at x = 0

    u(Nx, :) = 0; % u = 0 at x = L

    u(:, 1) = 0; % u = 0 at y = 0

    u(:, Ny) = Uinf; % u = Uinf at y = D

    v(i, j) = v(i, j) - u(i, j) * dt / (2 * h) .* (v(i+1, j) - v(i-1, j)) ...

        - v(i, j) * dt / (2 * h) .* (v(i, j+1) - v(i, j-1)) - dt / (2  * rho * h) * (p(i, j+1) - p(i, j-1)) ...

        + nu * (dt / h^2 * (v(i+1, j) - 2 * v(i, j) + v(i-1, j)) + dt / h^2 * (v(i, j+1) - 2 * v(i, j) + v(i, j-1))); % y-momentum

    v(1, :) = 0; % v = 0 at x = 0

    v(Nx, :) = 0; % v = 0 at x = L

    v(:, 1) = 0; % v = 0 at y = 0

    v(:, Ny) = 0; % v = 0 at y = D

end


%% post-processing

velocity_magnitude = sqrt(u.^2 + v.^2); % Velocity magnitude

data = zeros(Nx*Ny, 4); % 5 columns: X, Y, U, P

counter = 0;

for ii = 1:Nx

    for jj = 1:Ny

        counter = counter + 1;

        data(counter, 1) = X(ii, jj); % x-coordinate

        data(counter, 2) = Y(ii, jj); % y-coordinate

        data(counter, 3) = velocity_magnitude(ii, jj); % velocity magnitude

        data(counter, 4) = p(ii, jj); % pressure

    end

end


%% import data to neural network


x = normalize(data(:,1:2));

y = normalize(data(:,3:4));



%% weight and bias initialize; learning rate


inputlayersize = 2; % input layers

outputlayersize = 2; % output layers

firsthiddenlayersize = 10; % hidden layers

secondhiddenlayersize = 10; % hidden layers

thirdhiddenlayersize = 10; % hidden layers


%% weight and bias initialize; learning rate


w1=zeros(inputlayersize, firsthiddenlayersize); % weights input to hidden

w2=zeros(firsthiddenlayersize,secondhiddenlayersize); % weights hidden to output

w3=zeros(secondhiddenlayersize,thirdhiddenlayersize); % weights hidden to output

w4=zeros(thirdhiddenlayersize,outputlayersize); % weights hidden to output

b1=zeros(1,firsthiddenlayersize); % bias input to hidden

b2=zeros(1,secondhiddenlayersize); % bias hidden to output

b3=zeros(1,thirdhiddenlayersize); % bias hidden to output

b4=zeros(1,outputlayersize); % bias hidden to output

lr = 0.1; % learning rate


%% training

for i=1:100000

    z2=x*w1+b1;

    a2=activation(z2); % hidden layer 1

    z3=a2*w2+b2;

    a3=activation(z3); % hidden layer 2

    z4=a3*w3+b3;

    a4=activation(z4); % hidden layer 3

    z5=a4*w4+b4;

    yhat=activation(z5); %final output

    delta5=-(y-yhat).*activationprime(z5);

    delta4=delta5*w4.'.*activationprime(z4);

    delta3=delta4*w3.'.*activationprime(z3);

    delta2=delta3*w2.'.*activationprime(z2);

    DJW4= a4.'*delta5; % error hidden3 to output

    DJW3= a3.'*delta4; % error hidden2 to hidden3

    DJW2= a2.'*delta3; % error hidden1 to hidden2

    DJW1= x.'*delta2; % error input to hidden1

    w1=w1-(lr*DJW1); % updated weights input to hidden1

    w2=w2-(lr*DJW2); % updated weights hidden1 to hidden2

    w3=w3-(lr*DJW3); % updated weights hidden2 to hidden3

    w4=w4-(lr*DJW4); % updated weights hidden3 to output

    b1=b1-(lr*mean(delta2)); % updated bias input to hidden1

    b2=b2-(lr*mean(delta3)); % updated bias hidden1 to hidden2

    b3=b3-(lr*mean(delta4)); % updated bias hidden2 to hidden3

    b4=b4-(lr*mean(delta5)); % updated bias hidden3 to output

end


%% testing and plotting

xt = x;

z2=xt*w1+b1;

a2=activation(z2); % hidden layer 1

z3=a2*w2+b2;

a3=activation(z3); % hidden layer 2

z4=a3*w3+b3;

a4=activation(z4); % hidden layer 3

z5=a4*w4+b4;

yhat=activation(z5); % final output

(mean(y)) - (mean(yhat))

hold on; grid on; box on, grid minor

set(gca,'FontSize',40)

set(gca, 'FontName', 'Times New Roman')

ylabel('Pressure [Pa]','FontSize',44)

xlabel('x','FontSize',44)

plot(x,y(:,1),'s','LineWidth',1,'MarkerSize',10,'Color', [0 0 0])

plot(xt,yhat(:,1),'o','LineWidth',1,'MarkerSize',10,'Color', [1 0 0])


%% activation and derivative of activation


function [s]=activation(z)

s = 1 ./ (1 + exp(-z));

% s = tanh(z);

% s = z .* (1 ./ (1 + exp(-z)));

% s = cos(z);

% s = tanh(z) - (1 ./ (1 + exp(-z)));

end


function [s]=activationprime(z)

s = (exp(-z)) ./ ((1 + exp(-z))).^2;

% s = (sech(z)).^2;

% s = (exp(z) .* (1 + exp(z) + z) ) ./ (1 + exp(z)).^2;

% s = -sin(z);

% s = -exp(z) ./ (1 + exp(z)).^2 + (sech(z)).^2;

end


Citation: Cite as: Fahad Butt (2023). Machine Learning (ML) Accelerated Computational Fluid Dynamics (CFD) Attempt (https://whenrobotslove.blogspot.com/2023/09/machine-learning-ml-accelerated.html), When Robots Love!, Retrieved Month Date, Year

Saturday, December 18, 2021

Deep Feed Forward Back Propagating Neural Network (Regression) from Scratch without Keras / without TensorFlow using MATLAB Syntax

      Here is a code I wrote on MATLAB using the online tutorials for machine learning for regression. In the current form, the code can predict quadratic, cubic and periodic functions with considerable accuracy! Like [Introduction and Feed Forward Back Propagating Neural Network], I tried to add as many comments as possible. The predict portion of the code will not have back propagation. I still intend to replace for with while. If ever. 😏

     This has 3 hidden layers, making it "deep" neural network. so far, I have not seen any benefit of adding more hidden layers. I also don't know how to make neural network get out of local minima.


clear; clc;

%% traing data %%

x0=[-0*pi:pi/16:2*pi].'; %input data

y0=[sin(x0)]; %expected output data

x=x0./max(x0); %normalized inputs

y=y0./max(y0); %normalized outputs

%% size of neural network %%

inputlayersize = 1; %input layers

outputlayersize = 1; %output layers

firsthiddenlayersize = 5; %hidden layers

secondhiddenlayersize = 5; %hidden layers

thirdhiddenlayersize = 5; %hidden layers

%% weight and bias initialize; learning rate %%

w1=rand(inputlayersize, firsthiddenlayersize)-0.5; %weights input to hidden

w2=rand(firsthiddenlayersize,secondhiddenlayersize)-0.5; %weights hidden to output

w3=rand(secondhiddenlayersize,thirdhiddenlayersize)-0.5; %weights hidden to output

w4=rand(thirdhiddenlayersize,outputlayersize)-0.5; %weights hidden to output

b1=rand(1,firsthiddenlayersize)-0.5; %bias input to hidden

b2=rand(1,secondhiddenlayersize)-0.5; %bias hidden to output

b3=rand(1,thirdhiddenlayersize)-0.5; %bias hidden to output

b4=rand(1,outputlayersize)-0.5; %bias hidden to output

lr = 0.1; %learning rate

%% neural network %%

for i=1:100000

    z2=x*w1+b1;

    a2=activation(z2); %hidden layer 1

    z3=a2*w2+b2;

    a3=activation(z3); %hidden layer 2

    z4=a3*w3+b3;

    a4=activation(z4); %hidden layer 3

    z5=a4*w4+b4;

    yhat=activation(z5); %final output (normalized)

    delta5=-(y-yhat).*activationprime(z5);

    delta4=delta5*w4.'.*activationprime(z4);

    delta3=delta4*w3.'.*activationprime(z3);

    delta2=delta3*w2.'.*activationprime(z2);

    DJW4= a4.'*delta5; %error hidden3 to output

    DJW3= a3.'*delta4; %error hidden2 to hidden3

    DJW2= a2.'*delta3; %error hidden1 to hidden2

    DJW1= x.'*delta2; %error input to hidden1

    w1=w1-(lr*DJW1); %updated weights input to hidden1

    w2=w2-(lr*DJW2); %updated weights hidden1 to hidden2

    w3=w3-(lr*DJW3); %updated weights hidden2 to hidden3

    w4=w4-(lr*DJW4); %updated weights hidden3 to output

    b1=b1-(lr*mean(delta2)); %updated bias input to hidden1

    b2=b2-(lr*mean(delta3)); %updated bias hidden1 to hidden2

    b3=b3-(lr*mean(delta4)); %updated bias hidden2 to hidden3

    b4=b4-(lr*mean(delta5)); %updated bias hidden3 to output

end

%% plotting %%

yhat0=yhat.*max(y0); %final outputs

hold on; grid on; box on, grid minor

set(gca,'FontSize',40)

set(gca, 'FontName', 'Times New Roman')

ylabel('Cl     Cd','FontSize',44)

xlabel('AoA [{\circ}]','FontSize',44)

%xlim([0 1])

%xlim([0 30])

plot(x0,y0(:,1),'--','color',[0 0 0],'LineWidth',2,'MarkerSize',10)

plot(x0,yhat0(:,1),'o','color',[1 0 0],'LineWidth',2,'MarkerSize',20)

set(0,'DefaultLegendAutoUpdate','off')

legend({'Training Data','Neural Network Output'},'FontSize',44,'Location','Northwest')

%plot(x0,y0(:,2),'--','color',[0 0 0],'LineWidth',2,'MarkerSize',10)

%plot(x0,yhat0(:,2),'o','color',[1 0 0],'LineWidth',2,'MarkerSize',20)

%% activation %%

function [s]=activation(z)

%s=1./(1+exp(-z));

s=tanh(z);

end

%% derivative of activation %%

function [s]=activationprime(z)

%s=(exp(-z))./((1+exp(-z))).^2;

s=(sech(z)).^2;

end

     Lets see what the future bring! If you want to collaborate on the research projects related to turbo-machinery, aerodynamics, renewable energy and well, machine learning please reach out. Thank you very much for reading!

Thursday, December 9, 2021

Feed Forward Back Propagating Neural Network (Regression) from Scratch without PyTorch / without TensorFlow using Only Calculus and Math, Update 04: ADAM + Derivatives

     Here is a code I wrote using the online tutorials for machine learning for regression. In the current form, the code can predict complex functions with considerable accuracy! Like [Introduction and Feed Forward Back Propagating Neural Network], I tried to add as many comments as possible. Update 04 has while-loop, finally 😊 . Adam optimizer is now implemented. The code can now calculate 1st and 2nd derivatives of neural network output (Update 04)! Read theory here.

Update 01:

The hyperbolic tangent activation is implemented and the code can now be trained to learn trigonometric functions!


clear; clc;


%% traing data %%

x0=[-1*pi:pi/8:1*pi].'; %input data

y0=sin(x0); %expected output data

x=x0/max(x0); %normalized inputs

y=y0/max(y0); %normalized outputs


%% size of neural network %%

inputlayersize = 1; %input layers

outputlayersize = 1; %output layers

hiddenlayersize = 8; %hidden layers


%% weight and bias initialize; learning rate %%

w1=rand(inputlayersize, hiddenlayersize)-0.5; %weights input to hidden

w2=rand(hiddenlayersize,outputlayersize)-0.5; %weights hidden to output

b1=rand(1,hiddenlayersize)-0.5; %bias input to hidden

b2=rand(1,outputlayersize)-0.5; %bias hidden to output

lr = 0.1; %learning rate


%% forward propogation and training %%

for i=1:100000

    z2=x*w1+b1;

    a2=activation(z2); %hidden layer

    z3=a2*w2+b2;

    yhat=activation(z3); %final output (normalized)

    delta3=-(y-yhat).*activationprime(z3);

    DJW2= a2.'*delta3; %error hidden to output

    delta2=delta3*w2.'.*activationprime(z2);

    DJW1=x.'*delta2; %error weights input to hidden

    w1=w1-(lr*DJW1); %updated weights input to hidden

    w2=w2-(lr*DJW2); %updated weights hidden to output

    b1=b1-(lr*mean(delta2)); %updated bias input to hidden

    b2=b2-(lr*mean(delta3)); %updated bias hidden to output

end


%% plotting %%

yhat0=yhat*max(y0); %final outputs

hold on; grid on; box on, grid minor

set(gca,'FontSize',40)

set(gca, 'FontName', 'Times New Roman')

ylabel('sin (x)','FontSize',44)

xlabel('x','FontSize',44)

plot(x0,y0,'-','LineWidth',2,'MarkerSize',10)

plot(x0,yhat0,'+','LineWidth',2,'MarkerSize',10)

set(0,'DefaultLegendAutoUpdate','off')

legend({'Training Data','Neural Network Output'},'FontSize',44,'Location','Southeast')


%% activation %%

function [s]=activation(z)

%s=1./(1+exp(-z));

s=tanh(z);

end


%% derivative of activation %%

function [s]=activationprime(z)

%s=(exp(-z))./((1+exp(-z))).^2;

s=(sech(z)).^2;

end

Update 02:

      Entirely new code, works even better than Update 01. Turns out, no activation functions are required in output layer.


%% clear and close

clear

clc

close all


%% network parameters

input_size = 1; % number of input features

hidden_size = 5; % number of neurons in the hidden layer

output_size = 1; % number of output features

learning_rate = 0.01; % learning rate

epochs = 1e7; % maximum iterations

epoch = 0; % iteration counter

loss = inf; % initial loss

loss_min = 1e-5; % minimum loss


%% generate training data

X = 0:0.05:2;

X = X';

Y = cos(X) - sin(2*X);


%% initialization

rng(1)

W1 = rand(input_size, hidden_size); % input to hidden weights

b1 = rand(1, hidden_size); % input to hidden bias

W2 = rand(hidden_size, output_size); % hidden to output weights

b2 = rand(1, output_size); % output bias


%% training

while loss >= loss_min && epoch <= epochs

    % forward pass

    hidden_input = X * W1 + b1;

    hidden_output = 1 ./ (1 + exp(-hidden_input)); % hidden layer output

    output = hidden_output * W2 + b2; % output of neural network


    % loss calculation

    loss = 0.5 * mean((output - Y).^2); % root mean square


    % backpropagation

    output_error = output - Y; % error hidden to output

    hidden_error = (output_error * W2') .* (hidden_output .* (1 - hidden_output)); % error input to hidden


    % update weights and biases

    W2 = W2 - learning_rate * (hidden_output' * output_error);

    b2 = b2 - learning_rate * sum(output_error, 1);

    W1 = W1 - learning_rate * (X' * hidden_error);

    b1 = b1 - learning_rate * sum(hidden_error, 1);

    epoch = epoch + 1; % iteration counter

end


%% testing and plotting

test_input = 0:0.025:2; % test data

test_input = test_input';

hidden_input = test_input * W1 + b1;

hidden_output = 1 ./ (1 + exp(-hidden_input));

predicted_output = hidden_output * W2 + b2; % prediction by neural network


% plotting

hold on; grid on; box on, grid minor

set(gca,'FontSize',40)

set(gca, 'FontName', 'Times New Roman')

plot(X, Y, 'k-', 'LineWidth', 1, 'DisplayName', 'Training','MarkerSize',10);

plot(test_input, predicted_output, 'ro', 'LineWidth', 1, 'DisplayName', 'Testing','MarkerSize',10);

xlim([0 max(X)])

ylim([min(Y) max(Y)])

legend('show');

xlabel('Input');

ylabel('Output');


Update 03:

%% clear and close all
close all
clear
clc

%% make and prepare traing data
x0 = 0:0.2:9.6; % input data
x0 = x0';
y0 = x0.^2 .* sin(x0) + cos(2*x0) + 0.5 * x0; % expected output
noise_level = 20; % amount of noise (prevents over fitting)
rng(1) % random seed 1 (initialization is same everytime)
noise = noise_level * rand(size(y0)); % generate random noise
y0 = y0 + noise; % add noise
x = x0 / max(x0); % neural network variable name
y = y0 / max(y0); % neural network variable name

%% size of neural network
inputlayersize = 1; % input layer neurons
outputlayersize = 1; % output layer neurons
hiddenlayersize = 5; % hidden layer neurons

%% weight, bias initialize, learning rate, initial error and epochs initialize
w1 = rand(inputlayersize, hiddenlayersize); % weights input to hidden
w2 = rand(hiddenlayersize,outputlayersize); % weights hidden to output
b1 = rand(1,hiddenlayersize); % bias input to hidden
b2 = rand(1,outputlayersize); % bias hidden to output
vdb1 = zeros(size(b1)); % bias momentum input to hidden
vdb2 = zeros(size(b2)); % bias momentum hidden to output
vdw1 = zeros(size(w1)); % weights momentum input to hidden
vdw2 = zeros(size(w2)); % weights momentum hidden to output
B = 0.9; % beta
lr = 0.01; % learning rate
J = inf; % initial error
J_min = 1e-50; % minimum loss
epochs = 2e5; % maximum iterations
epoch = 0; % iteration counter

%% training
while J > J_min && epoch < epochs

    % forward pass
    z2 = x * w1 + b1;
    a2 = activation(z2); % hidden layer output
    z3 = a2 * w2 + b2;
    yhat = z3; % neural network final output (normalized)
    % yhat = (activation(x * w1 + b1) * w2 + b2) % neural network output (expanded form)

    % error calculation
    J = 0.5 * mean((y - yhat)).^2; % loss function
    % J = 0.5 * (y - (activation(x * w1 + b1) * w2 + b2)).^2 % loss function (expanded form)

    % gradient descent with momentum
    dJb2 = - (y - yhat); % change in loss function w.r.t. b2
    DJW2 = a2.' * dJb2; % change in loss function w.r.t. w2
    dJb1 = dJb2 * w2.' .* activationprime(z2); % change in loss function w.r.t. b1
    DJW1 = x .' * dJb1; % change in loss function w.r.t. w1

    vdb2 = B * vdb2 + (1 - B) * dJb2; % momentum term for b2
    vdw2 = B * vdw2 + (1 - B) * DJW2; % momentum term for w2
    vdb1 = B * vdb1 + (1 - B) * dJb1; % momentum term for b1
    vdw1 = B * vdw1 + (1 - B) * DJW1; % momentum term for w1

    % backpropagation
    b2 = b2 - (lr * mean(vdb2)); % updated bias hidden to output
    w2 = w2 - (lr * vdw2); % updated weights hidden to output
    b1 = b1 - (lr * mean(vdb1)); % updated bias input to hidden
    w1 = w1 - (lr * vdw1); % updated weights input to hidden

    epoch = epoch + 1; % iteration counter
end

%% testing and plotting
x_test = 0:0.3:9.6; % input data
x_test = x_test';
x_test = x_test / max(x_test);
z2 = x_test * w1 + b1;
a2 = activation(z2); % hidden layer output
z3 = a2 * w2 + b2;
yhat_test = z3; % neural network final output (normalized)

hold on; grid on; box on, grid minor
set(gca,'FontSize',40)
set(gca, 'FontName', 'Times New Roman')
ylabel('f (x)','FontSize',44)
xlabel('x','FontSize',44)
plot(x,y,'o','color',[0 0 0],'LineWidth',2,'MarkerSize',10)
plot(x_test,yhat_test,'-','color',[1 0 0],'LineWidth',2,'MarkerSize',10)
set(0,'DefaultLegendAutoUpdate','off')
legend({'Training Data','Neural Network Output'},'FontSize',44,'Location','Northwest')

%% activation
function [s]=activation(z)
s = 1 ./ (1 + exp(-z));
% s = tanh(z);
end

%% derivative of activation
function [s]=activationprime(z)
s = (exp(-z)) ./ ((1 + exp(-z))) .^2;
% s = (sech(z)) .^2;
end

Update 04:

%% clear and close all
close all
clear
clc

%% make and prepare traing data
x = -1*pi:pi/32:1*pi; % input data
x = x';
y = x.^2 .* sin(x); % expected output

%% size of neural network
inputlayersize = 1; % input layer neurons
outputlayersize = 1; % output layer neurons
hiddenlayersize = 10; % hidden layer neurons

%% weight, bias initialize, learning rate, initial error and epochs initialize
w1 = rand(inputlayersize, hiddenlayersize); % weights input to hidden
w2 = rand(hiddenlayersize,outputlayersize); % weights hidden to output
b1 = rand(1,hiddenlayersize); % bias input to hidden
b2 = rand(1,outputlayersize); % bias hidden to output
vdb1 = zeros(size(b1)); % bias momentum input to hidden
vdb2 = zeros(size(b2)); % bias momentum hidden to output
vdw1 = zeros(size(w1)); % weights momentum input to hidden
vdw2 = zeros(size(w2)); % weights momentum hidden to output
sdb1 = zeros(size(b1)); % bias RMS-prop input to hidden
sdb2 = zeros(size(b2)); % bias RMS-prop hidden to output
sdw1 = zeros(size(w1)); % weights RMS-prop input to hidden
sdw2 = zeros(size(w2)); % weights RMS-prop hidden to output
B1 = 0.9; % beta momentum
B2 = 0.999; % beta RMS-prop
E = 1e-8; 
lr = 0.001; % learning rate
J = inf; % initial error
J_min = 1e-50; % minimum loss
epochs = 1e7; % maximum iterations
epoch = 0; % iteration counter

%% training
while J > J_min && epoch < epochs

    % forward pass
    z2 = x * w1 + b1;
    a2 = activation(z2); % hidden layer output
    z3 = a2 * w2 + b2;
    yhat = z3; % neural network final output

    % error calculation
    J = 0.5 * mean((y - yhat)).^2; % loss function

    % gradient descent with momentum, RMS-prop a.k.a. adam
    rng(1)
    dJb2 = - (y - yhat); % change in loss function w.r.t. b2
    DJW2 = a2.' * dJb2; % change in loss function w.r.t. w2
    dJb1 = dJb2 * w2.' .* activationprime(z2); % change in loss function w.r.t. b1
    DJW1 = x .' * dJb1; % change in loss function w.r.t. w1

    vdb2 = B1 * vdb2 + ((1 - B1) * dJb2); % momentum term for b2
    vdw2 = B1 * vdw2 + ((1 - B1) * DJW2); % momentum term for w2
    vdb1 = B1 * vdb1 + ((1 - B1) * dJb1); % momentum term for b1
    vdw1 = B1 * vdw1 + ((1 - B1) * DJW1); % momentum term for w1

    sdb2 = B2 * sdb2 + ((1 - B2) * dJb2.^2); % RMS-prop term for b2
    sdw2 = B2 * sdw2 + ((1 - B2) * DJW2.^2); % RMS-prop term for w2
    sdb1 = B2 * sdb1 + ((1 - B2) * dJb1.^2); % RMS-prop term for b1
    sdw1 = B2 * sdw1 + ((1 - B2) * DJW1.^2); % RMS-prop term for w1

    % backpropagation
    b2 = b2 - (lr * (mean(vdb2) ./ (sqrt(mean(sdb2)) + E))); % updated bias hidden to output
    w2 = w2 - (lr * (vdw2 ./ (sqrt(sdw2) + E))); % updated weights hidden to output
    b1 = b1 - (lr * (mean(vdb1) ./ (sqrt(mean(sdb1)) + E))); % updated bias input to hidden
    w1 = w1 - (lr * (vdw1 ./ (sqrt(sdw1) + E))); % updated weights input to hidden

    epoch = epoch + 1; % iteration counter
end

%% testing and plotting
x_test = -1*pi:pi/90:1*pi; % input data
x_test = x_test';
z2_test = x_test * w1 + b1;
a2_test = activation(z2_test); % hidden layer output
z3_test = a2_test * w2 + b2;
yhat_test = z3_test; % neural network final output

dyhat_dx = w2.' .* activationprime(z2_test) * w1.'; % 1st derivative of output w.r.t. input
d2yhat_dx2 = w2.' .* (activationprime(z2_test) .* (1 - 2 * a2_test)) * (w1.^2).'; % 2nd derivative of output w.r.t. input

hold on; grid on; box on, grid minor
set(gca,'FontSize',40)
set(gca, 'FontName', 'Times New Roman')
ylabel('f (x)','FontSize',44)
xlabel('x','FontSize',44)
xlim([-1*pi 1*pi])
ylim([-4.5*pi 4.5*pi])

plot(x, y,'o','color',[1 0 0],'LineWidth',2,'MarkerSize',10)
plot(x_test, yhat_test,'-','color',[1 0 0],'LineWidth',2,'MarkerSize',10)

plot(x, 2 * x .* sin(x) + x.^2 .* cos(x),'s','color',[0 1 0],'LineWidth',2,'MarkerSize',10)
plot(x_test, dyhat_dx,'-.','color',[0 1 0],'LineWidth',2,'MarkerSize',10)

plot(x, 4 * x .* cos(x) - (x.^2 - 2) .* sin(x),'x','color',[0 0 1],'LineWidth',2,'MarkerSize',10)
plot(x_test, d2yhat_dx2,'--','color',[0 0 1],'LineWidth',2,'MarkerSize',10)

set(0,'DefaultLegendAutoUpdate','off')
legend({'Training Data','Neural Network Output','First Derivative Analytical','First Derivative Neural Network Output',...
    'Second Derivative Analytical','Second Derivative Neural Network Output'},'FontSize',10,'Location','Northoutside')

%% activation
function [s]=activation(z)
s = 1 ./ (1 + exp(-z));
% s = tanh(z);
% c = 0; % adjust as needed
% beta = 0.010; % adjust as needed
% s = exp(-beta * (z - c).^2);
end

%% derivative of activation
function [s]=activationprime(z)
s = activation(z) .* (1 - activation(z));
% s = (sech(z)) .^2;
% c = 0; % adjust as needed
% beta = 0.010; % adjust as needed
% s = -2 * beta * (z - c) .* activation(z);
end


     Lets see what the future bring! If you want to collaborate on the research projects related to turbo-machinery, aerodynamics, renewable energy and well, machine learning please reach out. Thank you very much for reading.

Tuesday, November 30, 2021

Introduction and Feed Forward Back Propagating Neural Network from Scratch without Keras / without TensorFlow using MATLAB Syntax

In this blog, I would try to use machine learning to predict flow fields around wings, propellers and turbines of all sorts using machine learning!

Recently, I have managed to taught myself the basics of machine learning like I taught myself Computational Fluid Dynamics (CFD) [Fluid Dynamics using the Computer] using the resources available on the internet (ME 702), all those years ago!

The little code I wrote in MATLAB is mentioned at the end. This code has one input, output and hidden layer, respectively. In the current configuration, it can predict all the logic gates successfully. To change the gate, just change the input matrices, which are marked by comments. I tried to include as many comments as I could. I would try to write code from scratch without Keras / without TensorFlow using MATLAB Syntax.

The code can be optimized more. For instance, the for loop can be replaced by while. Which I might, might not post. The code was written after I read a certain blue colored book I found called "Make your own neural network" by Tariq Rashid.

%% clear workspace and command window %%

clear; clc;

%% initialize nodes / structure of the neural network %%

inodes = 2; %input nodes

hnodes = 4; %hidden nodes

onodes = 1; %output nodes

%% initialize weights and biases %%

wih = 2*(rand(hnodes,inodes)-0.5); %random weights input to hidden -1 to 1

who = 2*(rand(onodes,hnodes)-0.5); %random weights hidden to output -1 to 1

lr = 0.5; %learning rate

bh=2*(rand(hnodes,1)-0.5); %initialize bias for hidden nodes -1 to 1

bo=2*(rand(onodes,1)-0.5); %initialize bias for hidden nodes -1 to 1

%% inputs and targets (training data) %%

inputs = [1 0; 0 1; 0 0; 1 1].'; %training data

targets = [0; 0; 0; 1].'; %training output

%% training loop %%

for i=1:100000

    hidden_inputs = wih*inputs + bh;

    hidden_outputs = 1./(1+exp(-hidden_inputs)); %hidden layer

    final_inputs = who*hidden_outputs + bo;

    final_outputs = 1./(1+exp(-final_inputs)); %output matrix

    output_errors = targets - final_outputs; %errors output to target

    hidden_errors = who.'*output_errors; %errors input to hidden

    who = who + (lr .* ((output_errors .* final_outputs  .* (1.0 -  final_outputs)) * hidden_outputs.')); %updating of weights hidden to output

    wih = wih + (lr .* ((hidden_errors .* hidden_outputs .* (1.0 - hidden_outputs)) * inputs.')); %updating of weights input to hidden

end

%% predict code %%

inputs_p = [0 1].'; %test inputs

hidden_inputs_p = wih*inputs_p + bh;

hidden_outputs_p = 1./(1+exp(-hidden_inputs_p)); %predicted hidden layer

final_inputs_p = who*hidden_outputs_p + bo;

final_outputs_p = 1./(1+exp(-final_inputs_p)); %predicted output matrix


Lets see what the future bring! If you want to collaborate on the research projects related to turbo-machinery, aerodynamics, renewable energy and well, machine learning please reach out. Thank you very much for reading.