djl
djl copied to clipboard
How can I implement the Adaline perceptron in DJL
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