Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I implement the Adaline perceptron in DJL #3197

Open
MohamedLEGH opened this issue May 15, 2024 · 0 comments
Open

How can I implement the Adaline perceptron in DJL #3197

MohamedLEGH opened this issue May 15, 2024 · 0 comments

Comments

@MohamedLEGH
Copy link

Hello,
The Adaline perceptron is a model used for binary classification. It's a very simple model, based on a linear regression (with MSE loss) and a gradient descend.
The only difference with a linear regression is that to predict a new input, we take the sign of the output of the linear regression. If the sign is negative, the predicted class is "-1", if the sign is positive, the predicted class is "1".
This is the equivalent code in Python:

import random
import numpy as np
import pandas as pd
import torch
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, LabelEncoder

class Adaline():
    def __init__(self, n_inputs, n_outputs):
        self.W = torch.zeros(n_inputs)
        self.b = torch.zeros(n_outputs)

    def linear(self, X):
        return torch.dot(self.W,X)+self.b

    def predict(self, X):
        return 1 if self.linear(X) > 0 else -1

    def forward(self, X, y, learning_rate):
        y_pred = self.linear(X)
        # m.w ← m.w + η(y − hm.w, xi)x
        residual = y-y_pred
        self.W += learning_rate*residual*X
        self.b += learning_rate*residual

    def __repr__(self):
        return "Weights:"+str(self.W)+", \nbias: "+str(self.b)

# accuracy of scikit-learn
def accuracy(model, X, y):
    acc_val = 0
    for index,x in enumerate(X):
        y_pred = model.predict(x)
        if(y_pred == y[index]):
            acc_val+=1
    return acc_val/len(X)


if __name__ == "__main__":
    df = pd.read_csv("spambase.csv")
    data = df.to_numpy()
    y = LabelEncoder().fit_transform(data[:, 57])
    X = np.delete(data, [57], axis=1).astype('float64')
    X = StandardScaler().fit_transform(X)
    X = torch.tensor(X).float()
    y = torch.tensor(y).float().reshape(-1, 1)
    y = 2*y - 1 #convert 0/1 labels to -1/1
    X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, shuffle=True)

    n_inputs = 57
    n_outputs = 1

    n_iter = 1000

    learning_rate = 0.001

    model = Adaline(n_inputs, n_outputs)

    for _ in range(n_iter):
        index = random.randrange(len(X_train))
        X = X_train[index]
        y = y_train[index]
        model.forward(X, y, learning_rate)

    print(model)
    acc = accuracy(model, X_test, y_test)
    print("Model accuracy: %.2f" % acc) 

I want to do the same this in Java, here my code below:

package machine_learning;

import java.io.IOException;
import java.util.Random;

import tech.tablesaw.api.Table;
import ai.djl.Model;
import ai.djl.ndarray.NDManager;
import ai.djl.ndarray.NDArray;
import ai.djl.ndarray.NDList;
import ai.djl.ndarray.types.DataType;
import ai.djl.nn.SequentialBlock;
import ai.djl.nn.core.Linear;
import ai.djl.nn.Parameter;
import ai.djl.nn.Activation;
import ai.djl.training.dataset.ArrayDataset;
import ai.djl.training.dataset.RandomAccessDataset;
import ai.djl.training.loss.Loss;
import ai.djl.training.loss.L2Loss;
import ai.djl.training.tracker.Tracker;
import ai.djl.training.optimizer.Optimizer;
import ai.djl.training.TrainingConfig;
import ai.djl.training.DefaultTrainingConfig;
import ai.djl.training.Trainer;
import ai.djl.training.evaluator.Accuracy;
import ai.djl.training.evaluator.BinaryAccuracy;
import ai.djl.training.TrainingResult;
import ai.djl.training.listener.TrainingListener;
import ai.djl.training.dataset.Batch;
import ai.djl.training.EasyTrain;
import ai.djl.training.initializer.ConstantInitializer;
import ai.djl.metric.Metrics;
import ai.djl.ndarray.types.Shape;
import ai.djl.translate.TranslateException;
import ai.djl.translate.Translator;
import ai.djl.translate.NoopTranslator;
import ai.djl.inference.Predictor;
import ai.djl.util.Pair;

public class Adaline {

    public static void main(String[] args) throws IOException, TranslateException {
        Table spambase = Table.read().csv("spambase.csv");
        Table inputs = spambase.copy().removeColumns("is_spam");
        Table outputs = spambase.copy().retainColumns("is_spam");
        NDManager manager = NDManager.newBaseManager();
        NDArray x = manager.create(inputs.as().floatMatrix());
        NDArray scaled_x = Utils.normalize(x);

        NDArray y = manager.create(outputs.as().intMatrix());
        y.muli(2).addi(-1); // y = 2*y - 1 : convert 0/1 labels to -1/1
        int batchSize = inputs.rowCount();
        ArrayDataset dataset = Utils.loadArray(scaled_x, y, batchSize, true);
        RandomAccessDataset[] datasets_split = dataset.randomSplit(80, 20);
        ArrayDataset trainingSet = (ArrayDataset) datasets_split[0];
        ArrayDataset testingSet = (ArrayDataset) datasets_split[1];

        Model model = Model.newInstance("adaline");
        SequentialBlock net = new SequentialBlock();
        Linear linearBlock = Linear.builder().optBias(true).setUnits(1).build();
        net.add(linearBlock);
        net.setInitializer(new ConstantInitializer(0), Parameter.Type.WEIGHT);
        net.initialize(manager, DataType.FLOAT32, x.getShape());

        model.setBlock(net);
        Loss loss = new L2Loss();

        float lr = 0.01f;

        Tracker lrt = Tracker.fixed(lr);
        Optimizer sgd = Optimizer.sgd().setLearningRateTracker(lrt).build();

        TrainingConfig config = new DefaultTrainingConfig(loss)
            .optOptimizer(sgd) // Optimizer
            .optDevices(manager.getEngine().getDevices(0)) // CPU
            .addEvaluator(new BinaryAccuracy()) // Model Accuracy
            .addTrainingListeners(TrainingListener.Defaults.logging()); // Logging

        Trainer trainer = model.newTrainer(config);

        trainer.initialize(new Shape(1, inputs.columnCount())); 
        Metrics metrics = new Metrics();
        trainer.setMetrics(metrics);

        int numEpochs = 10000;
        EasyTrain.fit(trainer, numEpochs, trainingSet, testingSet);
    }
}

The only thing missing is changing the way the model is evaluated, to compute the sign of the output of the model, instead of just the output of the linear block. Any idea how to do it ?

Thanks,
Mohamed Amine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant