1
\$\begingroup\$

I wrote a Java program implementing Resenblatt’s perceptron Single Layer Network, Least Mean Square algorithm for Single Layer Network and Back-propagation algorithm (MLP) Network. I'm trying to write efficient and clean code.

NeuralNetwork class

package fcis.asu.neural;

import java.util.Map;

import lombok.Getter;
import lombok.NonNull;

public abstract class NeuralNetwork {
    @Getter
    protected double learningRate;
    @Getter
    protected ActivationType activationType;
    @Getter
    protected double bias;
    protected Layer inputLayer;
    protected Layer outputLayer;
    @Getter
    protected int nNeuralClasses;
    @Getter
    protected int nFeaturs;
    @Getter
    protected double testAccuracy;
    @Getter
    protected double learnAccuracy;

    public NeuralNetwork(int nNeuralClasses, int nFeatures, double learningRate, @NonNull ActivationType activationType,
            double bias) {
        if (nNeuralClasses <= 0)
            throw new IllegalArgumentException("number of neural class must be more than 0");
        if (nFeatures <= 0)
            throw new IllegalArgumentException("nFeatures must be more than 0");

        this.learningRate = learningRate;
        this.activationType = activationType;
        this.bias = bias;
        this.nNeuralClasses = nNeuralClasses;
        this.outputLayer = Layer.outputLayer(nNeuralClasses, activationType);
        this.testAccuracy = 0.0;
        this.learnAccuracy = 0.0;
    }

    public void run(Map<double[], ClassType> learnSamples, Map<double[], ClassType> testSamles) {
        if (learnSamples == null || testSamles == null)
            throw new IllegalArgumentException("samples must be not null");
        learn(learnSamples);
        test(testSamles);
    }

    protected abstract void learn(Map<double[], ClassType> learnSamples);

    protected abstract void test(Map<double[], ClassType> testSamles);

}

Neural class

package fcis.asu.neural;

import fcis.asu.utilities.Utilities;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Neural {

    private double[] weights;
    private double error;
    private double output;
    private ActivationType activationType;

    public Neural(int nNeuralNextLaye, ActivationType activationType) {

        this.weights = nNeuralNextLaye > 0 ? Utilities.generateRandomDoubleArr(nNeuralNextLaye) : null;
        this.activationType = activationType;
    }

    public double calcOutput(double[] inputs, double[] inputWeights, ActivationType activationType, double slope) {
        double net = Utilities.dotProduct(inputs, inputWeights);
        return this.output = activation(activationType, net, slope);
    }

    public double activation(ActivationType activation, double... args) {
        switch (activation) {
        case Sigmoid: {
            return Activation.sigmoid(args[0], args[1]);
        }
        case Tangent: {
            return Activation.tangentSigmoid(args[0], args[1]);
        }
        case Signum: {
            return Activation.signum(args[0]);
        }
        case Linear: {
            return args[0];
        }
        default:
            throw new IllegalArgumentException();

        }
    }

    public double calcHiddenNeuralError(double[] errors, double slope) {
        double sumError = Utilities.dotProduct(weights, errors);
        switch (activationType) {
        case Sigmoid:
            return this.error = slope * output * (1 - output) * sumError;
        case Tangent:
            throw new UnsupportedOperationException("Tangent not implemented yet");
        default:
            throw new UnsupportedOperationException("default not implemented yet");
        }

    }

    /**
     * @param yk
     *            result of activation function of last layer
     * @param dk
     *            desire output of this neural
     * @return error
     */
    public double calcOutputNeuralError(double dk, double slope) {
        switch (activationType) {
        case Sigmoid:
            return this.error = (double) slope * (dk - output) * output * (1 - output);
        case Tangent:
            throw new UnsupportedOperationException("Tangent not implemented yet");
        case Signum:
            return this.error = dk - output;
        case Linear:
            return this.error = dk - output;
        default:
            throw new UnsupportedOperationException("default not implemented yet");
        }

    }

    public void updateWeight(double[] nextLayerError, double learningRate) {
        int nWeights = this.weights.length;
        for (int i = 0; i < nWeights; i++) {
            weights[i] = weights[i] + (learningRate * nextLayerError[i] * output);
        }

    }

    public double getWeight(int index) {
        return this.weights[index];
    }

}

Layer class

package fcis.asu.neural;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Layer {
    private Neural[] neurals;
    private double[] errors;
    private double[] outputs;
    private int nNeuralNextLayer;
    private int nNeural;

    private Layer(int nNeural, int nNeuralNextLayer, ActivationType activationType) {
        this.nNeural = nNeural;
        this.nNeuralNextLayer = nNeuralNextLayer;
        this.outputs = new double[nNeural];
        initalNeurals(nNeural, nNeuralNextLayer, activationType);
        errors = new double[nNeural];

    }

    public static Layer outputLayer(int nClasses, ActivationType activationType) {
        return new Layer(nClasses, 0, activationType);
    }

    public static Layer hiddenLayer(int nNeural, int nNeuralNextLayer, ActivationType activationType, double slope) {
        return new Layer(nNeural, nNeuralNextLayer, activationType);
    }

    public static Layer inputLayer(int nNeural, int nNeuralNextLayer, ActivationType activationType) {
        return new Layer(nNeural+1, nNeuralNextLayer, activationType);

    }

    private void initalNeurals(int nNeural, int nNeuralNextLayer, ActivationType activationType) {
        neurals = new Neural[nNeural];
        for (int i = 0; i < nNeural; i++) {
            neurals[i] = new Neural(nNeuralNextLayer, activationType);
        }
    }

    public double[][] getWeightForNextLayersSeq() {
        int nNeurals = this.neurals.length;
        double[][] weights = new double[nNeuralNextLayer][nNeurals];

        for (int i = 0; i < nNeuralNextLayer; i++) {
            for (int j = 0; j < nNeurals; j++) {
                weights[i][j] = neurals[j].getWeight(i);
            }
        }
        return weights;
    }

    public double[] calcOutputSeq(double[] input, double[][] inputWeights, double bias, ActivationType activationType,
            double slope) {
        for (int i = 0; i < nNeural; i++) {
            outputs[i] = neurals[i].calcOutput(input, inputWeights[i],activationType,slope);
        }
        return outputs;
    }

    public void setOutputForInputLayer(double[] output, double bias) {


        int lastIndex = nNeural - 1; // bias
        for (int i = 0; i < lastIndex; i++) {
            neurals[i].setOutput(output[i]);
            this.outputs[i] = output[i];
        }
        neurals[lastIndex].setOutput(bias);
        this.outputs[lastIndex] = bias;

    }

    public double[] clacOutputLayerError(int[] desire, double slope) {
        for (int i = 0; i < nNeural; i++) {
            errors[i] = neurals[i].calcOutputNeuralError(desire[i],slope);
        }
        return errors;
    }

    public double[] clacHiddenLayerError(double[] nextLayerError, double slope) {

        for (int i = 0; i < nNeural; i++) {
            errors[i] = neurals[i].calcHiddenNeuralError(nextLayerError,slope);
        }
        return errors;
    }

    public void updateWeights(double[] nextLayerError, double learningRate) {
        for (int i = 0; i < nNeural; i++) {
            neurals[i].updateWeight(nextLayerError, learningRate);
        }
    }

}

SLP class

    package fcis.asu.neural;

    import java.util.Map;

    import org.apache.log4j.Logger;

    /**
     * Resenblatt’s perceptron Single Layer Network
     * 
     * @author Eslam Ali
     *
     */
    public class SLP extends NeuralNetwork {

        final static Logger logger = Logger.getLogger(SLP.class);
        private int maxNLearnEpoch;

        public SLP(int nNeuralClasses, int nFeatures, double learningRate, double bias, int maxNEpoch) {
            super(nNeuralClasses, nFeatures, learningRate, ActivationType.Signum, bias);
            this.inputLayer = Layer.inputLayer(nFeatures, 1, activationType);
            this.maxNLearnEpoch = maxNEpoch;

        }

        @Override
        public void run(Map<double[], ClassType> learnSamples, Map<double[], ClassType> testSamles) {
            logger.info("start run SLP");
            super.run(learnSamples, testSamles);
        }

        @Override
        protected void learn(Map<double[], ClassType> learnSamples) {
            logger.info("SLP start learning");
            int nSamples = learnSamples.size();
            int correct = 0;
            for (int i = 0; i < maxNLearnEpoch; i++) {
                correct = 0;
                for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                    inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                    outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                            activationType, 1);
                    double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
                    if (error[0] == 0) {
                        correct++;
                    }
                    inputLayer.updateWeights(error, learningRate);
                } // end map samples loop
                if (nSamples == correct) {
                    break;
                }
            } // end maxNLearnEpoch loop
            learnAccuracy = ((double) correct / nSamples) * 100;
            logger.info("SLP finished learn neural with accuracy:" + learnAccuracy);

        }

        @Override
        protected void test(Map<double[], ClassType> testSamles) {
            logger.info("SLP start testing neural network");
            int correct = 0;
            for (Map.Entry<double[], ClassType> sample : testSamles.entrySet()) {
                inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                        activationType, 1);
                double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
                if (error[0] == 0)
                    correct++;
            } // end map samples loop
            this.testAccuracy = ((double) correct / testSamles.size()) * 100;
            logger.info("SLP finished test neural with accuracy:" + testAccuracy);

        }

    }

**LeastMeanSquare** class

    package fcis.asu.neural;

import java.util.Map;

import org.apache.log4j.Logger;

/**
 * Least Mean Square algorithm for Single Layer Network
 * 
 * @author Eslam Ali
 *
 */
public class LeastMeanSquare extends NeuralNetwork {

    final static Logger logger = Logger.getLogger(LeastMeanSquare.class);
    double stopMse;

    public LeastMeanSquare(int nNeuralClasses, int nFeatures, double learningRate, double bias, double stopMse) {
        super(nNeuralClasses, nFeatures, learningRate, ActivationType.Linear, bias);
        this.stopMse = stopMse;
        inputLayer = Layer.inputLayer(nFeatures, 1, activationType);
    }

    @Override
    protected void learn(Map<double[], ClassType> learnSamples) {
        logger.info("LMS start learning neural");
        double mse = Double.MAX_VALUE;
        int apoch = 0;
        int nSample = learnSamples.size();
        while (mse > stopMse) {

            for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                        activationType, 1);
                double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
                inputLayer.updateWeights(error, learningRate);
            } // end map samples loop

            mse = 0;
            for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                        activationType, 1);
                double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
                mse += (error[0] * error[0]) / 2;
            }
            mse /= nSample;
            ++apoch;
        } // end while loop
            // learnAccuracy = ((double) correct / nSamples) * 100;
        logger.info("LMS finished learning final mse :" + mse + "after:" + apoch + " apoch");

    }

    @Override
    protected void test(Map<double[], ClassType> testSamles) {
        logger.info("LMS start test neural");
        int correct = 0;
        for (Map.Entry<double[], ClassType> sample : testSamles.entrySet()) {
            inputLayer.setOutputForInputLayer(sample.getKey(), bias);
            outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                    ActivationType.Signum, 1);
            double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
            if (error[0] == 0)
                correct++;

        } // end map samples loop
        testAccuracy = ((double) correct / testSamles.size()) * 100;
        logger.info("LMS finish test neural with accuracy:" + testAccuracy);

    }

}

LeastMeanSquare class

package fcis.asu.neural;

import java.util.Map;

import org.apache.log4j.Logger;

/**
 * Least Mean Square algorithm for Single Layer Network
 * 
 * @author Eslam Ali
 *
 */
public class LeastMeanSquare extends NeuralNetwork {

    final static Logger logger = Logger.getLogger(LeastMeanSquare.class);
    double stopMse;

    public LeastMeanSquare(int nNeuralClasses, int nFeatures, double learningRate, double bias, double stopMse) {
        super(nNeuralClasses, nFeatures, learningRate, ActivationType.Linear, bias);
        this.stopMse = stopMse;
        inputLayer = Layer.inputLayer(nFeatures, 1, activationType);
    }

    @Override
    protected void learn(Map<double[], ClassType> learnSamples) {
        logger.info("LMS start learning neural");
        double mse = Double.MAX_VALUE;
        int apoch = 0;
        int nSample = learnSamples.size();
        while (mse > stopMse) {

            for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                        activationType, 1);
                double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
                inputLayer.updateWeights(error, learningRate);
            } // end map samples loop

            mse = 0;
            for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                        activationType, 1);
                double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
                mse += (error[0] * error[0]) / 2;
            }
            mse /= nSample;
            ++apoch;
        } // end while loop
            // learnAccuracy = ((double) correct / nSamples) * 100;
        logger.info("LMS finished learning final mse :" + mse + "after:" + apoch + " apoch");

    }

    @Override
    protected void test(Map<double[], ClassType> testSamles) {
        logger.info("LMS start test neural");
        int correct = 0;
        for (Map.Entry<double[], ClassType> sample : testSamles.entrySet()) {
            inputLayer.setOutputForInputLayer(sample.getKey(), bias);
            outputLayer.calcOutputSeq(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), bias,
                    ActivationType.Signum, 1);
            double[] error = outputLayer.clacOutputLayerError(sample.getValue().getDesire(), 1);
            if (error[0] == 0)
                correct++;

        } // end map samples loop
        testAccuracy = ((double) correct / testSamles.size()) * 100;
        logger.info("LMS finish test neural with accuracy:" + testAccuracy);

    }

}

MLP class

package fcis.asu.neural;

import org.apache.log4j.Logger;

import lombok.Getter;

public abstract class MLP extends NeuralNetwork {

    @Getter
    protected Layer[] hiddenLayers;
    @Getter
    protected double errorThreshold;
    @Getter
    protected int nHiddenLayer;
    @Getter
    protected double slope;

    final static Logger logger = Logger.getLogger(MLP.class);

    public MLP(int nNeuralClasses, int nFeatures, int nHiddenLayer, int[] nNeuralInHiddenLayers, double learningRate,
            ActivationType activationType, double bias, double errorThreshold, double slope) {
        super(nNeuralClasses, nFeatures, learningRate, activationType, bias);
        this.errorThreshold = errorThreshold;
        this.nHiddenLayer = nHiddenLayer;
        this.slope = slope;
        inputLayer = Layer.inputLayer(nFeatures, nHiddenLayer > 0 ? nNeuralInHiddenLayers[0] : 1, activationType);
        initalHiddenLayers(nHiddenLayer, nNeuralInHiddenLayers, nNeuralClasses);
    }

    private void initalHiddenLayers(int nHidenLayer, int[] nNeuralInHidenLayers, int nClasses) {
        this.hiddenLayers = new Layer[nHidenLayer];
        // last hidden layer weights we be based on n of neural classes
        this.hiddenLayers[nHidenLayer - 1] = Layer.hiddenLayer(nNeuralInHidenLayers[nHidenLayer - 1], nClasses,
                activationType, slope);
        int tmp = nHidenLayer - 1;
        for (int i = 0; i < tmp; i++) {
            this.hiddenLayers[i] = Layer.hiddenLayer(nNeuralInHidenLayers[i], nNeuralInHidenLayers[i + 1],
                    activationType, slope);
        }
    }

}

BackPropagation class

package fcis.asu.neural;

import java.util.Map;

    /**
     * Back-propagation algorithm Multilayer Perceptron (MLP) Network
     * 
     * @author Eslam Ali
     * 
     */
    public class BackPropagation extends MLP {

        public BackPropagation(int nNeuralClasses, int nFeatures, int nHiddenLayer, int[] nNeuralInHiddenLayers,
                double learningRate, ActivationType activationType, double bias, double errorThreshold, double slope) {
            super(nNeuralClasses, nFeatures, nHiddenLayer, nNeuralInHiddenLayers, learningRate, activationType, bias,
                    errorThreshold, slope);
        }

        @Override
        public void run(Map<double[], ClassType> learnSamples, Map<double[], ClassType> testSamles) {
            logger.info("MLP start runing...");
            super.run(learnSamples, testSamles);
        }

        @Override
        protected void learn(Map<double[], ClassType> learnSamples) {
            logger.info("MLP start learning...");
            double mse = Double.MAX_VALUE;
            while (mse > errorThreshold) {
                for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                    inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                    double[] errorOfOutputLayer = forward(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(),
                            sample.getValue().getDesire());
                    backward(errorOfOutputLayer);
                    feedforward();
                }
                mse = 0;
                for (Map.Entry<double[], ClassType> sample : learnSamples.entrySet()) {
                    inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                    double[] errorOfOutputLayer = forward(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(),
                            sample.getValue().getDesire());

                    for (double err : errorOfOutputLayer)
                        mse += (err * err) / 2;

                }
                mse /= learnSamples.size();
                System.out.println("mse=" + mse);

            }
            logger.info("MLP finished learning last mse:" + mse);

        }

        @Override
        protected void test(Map<double[], ClassType> testSamles) {

            logger.info("MLP start testing...");
            int counter = 0;
            for (Map.Entry<double[], ClassType> sample : testSamles.entrySet()) {
                inputLayer.setOutputForInputLayer(sample.getKey(), bias);
                forward(inputLayer.getOutputs(), inputLayer.getWeightForNextLayersSeq(), sample.getValue().getDesire());
                double[] errorOfOutputLayer = outputLayer.getOutputs();
                int maxValueIndex = -1;
                double tmpMaxValue = Double.MIN_VALUE;
                int nClasses = errorOfOutputLayer.length;

                for (int i = 0; i < nClasses; i++) {
                    if (errorOfOutputLayer[i] > tmpMaxValue) {
                        tmpMaxValue = errorOfOutputLayer[i];
                        maxValueIndex = i;
                    }
                }

                if (sample.getValue().getDesire()[maxValueIndex] == 1) {
                    counter++;
                }
            }
            testAccuracy = ((double) counter / testSamles.size()) * 100;
            logger.info("MLP finished testing with accuracy :" + testAccuracy);
        }

        private double[] forward(double[] input, double[][] inputWeights, int[] desireClass) {
            double[] tmpInput = input;
            double[][] tmpInputWeights = inputWeights;

            for (Layer hiddenLayer : hiddenLayers) {
                tmpInput = hiddenLayer.calcOutputSeq(tmpInput, tmpInputWeights, bias, activationType, slope);
                tmpInputWeights = hiddenLayer.getWeightForNextLayersSeq();

            }
            outputLayer.calcOutputSeq(tmpInput, tmpInputWeights, bias, activationType, slope);
            if (desireClass == null)
                return null;
            return outputLayer.clacOutputLayerError(desireClass, slope);
        }

        private void backward(double[] nextLayerError) {
            int nHiddenLayer = this.nHiddenLayer - 1;
            double[] tmpNextLayerError = nextLayerError;
            for (int i = nHiddenLayer; i >= 0; i--) {
                tmpNextLayerError = hiddenLayers[i].clacHiddenLayerError(tmpNextLayerError, slope);

            }
        }

        private void feedforward() {
            double[] tmpNextLayerError = nHiddenLayer > 0 ? hiddenLayers[0].getErrors() : outputLayer.getErrors();
            inputLayer.updateWeights(tmpNextLayerError, learningRate);
            for (int i = 1; i < nHiddenLayer - 1; i++) {
                tmpNextLayerError = hiddenLayers[i].getErrors();
                hiddenLayers[i - 1].updateWeights(tmpNextLayerError, learningRate);
            }

            if (nHiddenLayer > 0)
                hiddenLayers[nHiddenLayer - 1].updateWeights(outputLayer.getErrors(), learningRate);

        }

    }

Activation class

package fcis.asu.neural;

public class Activation {

    /**
     * the neuron will has output signal only if its activation potential is
     * non-negative, a property known as all-or-none
     * 
     * @param vk
     *            is the activation potential of neuron k
     * @return Yk
     */
    public static int threshold(double vk) {
        return vk >= 0 ? 1 : 0;
    }

    /**
     * @param vk
     *            is the activation potential of neuron k
     * @return Yk
     */
    public static double piecewiseLinear(double vk) {
        if (vk >= .5)
            return 1;
        else if (-.5 < vk && vk < .5)
            return vk + .5;
        else
            return 0;
    }

    /**
     * @param vk
     *            is the activation potential of neuron k
     * @param a
     *            is the slop parameter of the sigmoid function.
     * @return Yk
     */
    public static double sigmoid(double vk, double a) {
        return ((double) 1 / (1 + Math.exp(-a * vk)));
    }

    /**
     * @param vk
     *            is the activation potential of neuron k
     * @param a
     *            is the slop parameter of the sigmoid function.
     * @return Yk
     */
    public static double tangentSigmoid(double vk, double a) {
        return ((1 - Math.exp(-a * vk)) / (1 + Math.exp(-a * vk)));
    }

    public static double signum(double y) {
        return y >= 0 ? 1 : -1;
    }

    public static double sigmoidDerivative(double yk, double dk, double slope) {
        return slope * (dk - yk) * yk * (1 - yk);
    }

    public static double tangentDerivative(double yk, double dk, double slope) {
        throw new UnsupportedOperationException();

    }
}

ActivationType enum

package fcis.asu.neural;

public enum ActivationType {
    Signum("Signum"),Sigmoid("Logistic Sigmoid"),Tangent("Hyperbolic tangent"),Linear("Linear");
    private String name;
    private ActivationType(String name) {
        this.name=name;
    }

    @Override
    public String toString() {
        return this.name;
    }


}

ClassType class

package fcis.asu.neural;

import lombok.Getter;

@Getter
public class ClassType {
    private String name;
    private int[] desire;

    public ClassType(String name, int[] desire) {
        this.name = name;
        this.desire = desire;
    }

    @Override
    public String toString() {
        return name;
    }

}

Sample class

package fcis.asu.neural;

import lombok.Getter;

@Getter
public class Sample {
    private ClassType classType;
    private double[] features;

    public Sample(ClassType classType,double[] features) {
        this.classType=classType;
        this.features=features;
    }

}

FileProperties class

package fcis.asu.utilities;

import java.util.List;

import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.Singular;

/**
 * @author Eslam Ali
 * this class to prepare configuration of
 * file path,separator between column,is has Head
 * target classes ,target features columns
 * and classType index in the file
 */
@Builder
@Getter
public class FileProperties {

    @NonNull
    private String filePath;
    @NonNull
    private String separator=",";
    private boolean hasHeader = false;

    /**
     *if you read for single layer perceptron this will make the desire 1 or -1 for the each class   
     */
    private boolean isSLP = true;
    private int classTypeIndex;

    /**
     * targetClasses = null if train and test all classes
     */
    @Singular
    private List<String> targetClasses = null;

    /**
     * targetFeatures default null if you will use all features
     */
    @Singular
    private List<Integer> targetFeatures = null;

    public int getFeatureIndex(int targetFeaturesIndex){
        if(targetFeatures == null)
            throw new IllegalArgumentException("target features is null");
        return targetFeatures.get(targetFeaturesIndex);
    }

}

ReadSamples class

package fcis.asu.utilities;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;

import fcis.asu.neural.ClassType;
import fcis.asu.neural.Sample;
import lombok.Cleanup;
import lombok.Getter;
import lombok.NonNull;

public class ReadSamples {
    @Getter private Map<double[], ClassType> testSamples;
    @Getter private Map<double[], ClassType> learnSamples;
    @Getter private int nClasses;
    @Getter public static ClassType[] classTypes;
    final static Logger logger = Logger.getLogger(ReadSamples.class);

    private ReadSamples(String rootFolderPath, double percentageSampleLearn) {
        testSamples = new HashMap<double[], ClassType>();
        learnSamples = new HashMap<double[], ClassType>();
        generateLearnAndTestSamples(rootFolderPath, percentageSampleLearn);
    }

    private ReadSamples(FileProperties properties,double percentageSampleLearn) {
        testSamples = new HashMap<double[], ClassType>();
        learnSamples = new HashMap<double[], ClassType>();
        if (properties.isSLP()) {
            classTypes = new ClassType[2];
            classTypes[0] = new ClassType(properties.getTargetClasses().get(0), new int[] { 1 });
            classTypes[1] = new ClassType(properties.getTargetClasses().get(1), new int[] { -1 });
        }
        readIRIS(properties, percentageSampleLearn);
    }

    public static ReadSamples ReadSampleImage(@NonNull String rootFolderPath, double percentageSampleLearn) {
        return new ReadSamples(rootFolderPath, percentageSampleLearn);
    }

    public static ReadSamples readIRISData(@NonNull FileProperties properties,double percentageSampleLearn) {
        return new ReadSamples(properties, percentageSampleLearn);
    }



    /**
     * this function real every subfolder of the root folder and generate
     * ClassType object for it . then loop in each class folder to set data in
     * map<String : image path ,ClassType : class of the image> learn and test
     * 
     * @param rootFolderPath
     *            this folder should contain folder for each class
     * @param percentageSampleLearn
     *            percentage of samples you to use for learn the rest will use
     *            for test
     */
    private void generateLearnAndTestSamples(String rootFolderPath, double percentageSampleLearn) {
        File file = new File(rootFolderPath);
        String[] classesNames = file.list(new FilenameFilter() {
            public boolean accept(File current, String name) {
                return new File(current, name).isDirectory();
            }
        });

        this.nClasses = classesNames.length;
        classTypes = new ClassType[nClasses];
        for (int i = 0; i < nClasses; i++) {
            int[] desire = new int[nClasses];
            desire[i] = 1;
            classTypes[i] = new ClassType(classesNames[i], desire);

            String classPath = rootFolderPath + "/" + classesNames[i];
            File Classdir = new File(classPath);
            File[] samples = Classdir.listFiles(new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.endsWith(".jpg");
                }
            });
            int nSamples = samples.length;
            int nLeanrnSample = (int) Math.round((double) nSamples * percentageSampleLearn);

            for (int j = 0; j < nLeanrnSample; j++) {
                learnSamples.put(getImgData(samples[j].getPath()), classTypes[i]);
            }

            for (int j = nLeanrnSample; j < nSamples; j++) {
                testSamples.put(getImgData(samples[j].getPath()), classTypes[i]);
            }
        }
    }

    public static double[] getImgData(@NonNull String imgPath) {
        int height, width, rgb, red, green, blue, count;
        double imgData[] = null;
        BufferedImage img = null;
        try {
            img = ImageIO.read(new File(imgPath));
            width = img.getWidth();
            height = img.getHeight();
            imgData = new double[width * height];
            count = 0;
            for (int h = 0; h < height; h++) {
                for (int w = 0; w < width; w++) {
                    rgb = img.getRGB(w, h);
                    red = (rgb >> 16) & 0x000000FF;
                    green = (rgb >> 8) & 0x000000FF;
                    blue = (rgb) & 0x000000FF;
                    double sum = (double) (red + green + blue) / 3;
                    imgData[count] = sum;
                    count++;
                }
            }
        } catch (IOException e) {
            System.out.println(e);
        }
        // System.out.println(imgPath);
        return Utilities.normalize(imgData);

    }


    private void readIRIS(FileProperties prop,double percentageSampleLearn) {
        try {
            @Cleanup
            BufferedReader in = new BufferedReader(new FileReader(prop.getFilePath()));

            String row;
            int noisyRows = 0;
            int totalRows = 0;      
            List<Integer> TargetFeatures=prop.getTargetFeatures();

            if (prop.isHasHeader()) {
                in.readLine();
            }
            int nFeatures = TargetFeatures.size();
            List<Sample> samples=new ArrayList<Sample>();
            double[] features;
            while ((row = in.readLine()) != null) {
                String[] columns = row.split(prop.getSeparator());

                try {
                    String className = columns[prop.getClassTypeIndex()];
                    ClassType type = getClassType(className);
                    if(prop.isSLP()){
                        if(type != null){
                             features = new double[nFeatures];
                             for (int i = 0; i < nFeatures; i++) {
                                    features[i] = Double.parseDouble(columns[prop.getTargetFeatures().get(i)])/10;
                            }
                             samples.add(new Sample(type, features));
                        }
                    }
                    else {
                        throw new UnsupportedOperationException("sorry not handeled yet");
                    }

                } catch (Exception e) {
                    logger.error(e);
                    noisyRows++;

                }
                totalRows++;
            }
            logger.info(
                    "Total rows: " + totalRows + " mapped to sample:" + samples.size() + " noisy rows:" + noisyRows);


            generateTestAndLearnSamplesIRIS(samples, percentageSampleLearn);

        } catch (IOException err) {
            logger.error("file read error :" + err.getMessage());
            System.exit(0);

        }

    }

    private void generateTestAndLearnSamplesIRIS(List<Sample> samples, double percentageSampleLearn) {
        List<Sample> listLearnSamples = new ArrayList<Sample>();
        List<Sample> listTestSamples = new ArrayList<Sample>();
        int nClassSamples = samples.size() / 2;

        int nLearnSamples = (int) Math.round(nClassSamples * percentageSampleLearn);
        listLearnSamples.addAll(samples.subList(0, nLearnSamples - 1));
        listLearnSamples.addAll(samples.subList(nClassSamples - 1, nClassSamples + nLearnSamples - 2));
        for(Sample sample:listLearnSamples){
            learnSamples.put( sample.getFeatures(),sample.getClassType());
        }

        listTestSamples.addAll(samples.subList(nLearnSamples, nClassSamples - 1));
        listTestSamples.addAll(samples.subList(nClassSamples + nLearnSamples - 1, samples.size() - 1));
        for(Sample sample:listTestSamples){
            testSamples.put(sample.getFeatures(),sample.getClassType());
        }
    }

    private static ClassType getClassType(String className) {
        for (ClassType type : classTypes) {
            if (type.toString().equals(className))
                return type;
        }
        return null;
    }

}

Utilities class

package fcis.asu.utilities;

import java.util.Random;

import lombok.NonNull;

public class Utilities {

    /**
     * generate array of random numbers
     * 
     * @param nNumbers
     *            must be greater than 0
     * @return random Double arrays between 0 : 1
     */
    public static double[] generateRandomDoubleArr(int nNumbers) {
        if (nNumbers == 0)
            throw new IllegalArgumentException("nNumber must be greater than 0");

        Random rand = new Random();
        double[] randomNumbers = new double[nNumbers];
        for (int i = 0; i < nNumbers; i++) {
            randomNumbers[i] = rand.nextFloat();
        }
        return randomNumbers;
    }

    /**
     * generate 2D array of random numbers
     * 
     * @param x
     *            numbers in x dimensional
     * @param y
     *            numbers in y dimensional
     * @return random Double 2D array between 0 : 1
     */
    public static double[][] generateRandomDouble2DArr(int x, int y) {
        if (x == 0 || y == 0)
            throw new IllegalArgumentException("x and y must be greater than 0");

        Random rand = new Random();
        double[][] rand2DArr = new double[x][y];
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                rand2DArr[i][j] = rand.nextFloat();
            }

        }
        return rand2DArr;
    }

    /**
     * The dot product of two vectors a = [a1, a2, ..., an] and b = [b1, b2,...,
     * bn] is a1*b1 + a2*b2 + a3*b3 +...
     * 
     * @param a
     * @param b
     * @return dot product or scalar product
     * 
     */
    public static double dotProduct(@NonNull double[] a, @NonNull double[] b) {
        if (a.length != b.length)
            throw new IllegalArgumentException("arrays must have equal lengths");

        int arrLength = a.length;
        double sum = 0;
        for (int i = 0; i < arrLength; i++) {
            sum += a[i] * b[i];
        }
        return sum;
    }

    /**
     * Normalize double array data
     * 
     * @param arr
     * @return normalize array
     */
    public static double[] normalize(@NonNull double[] arr) {
        double sum = 0.0;
        double max = 0.0;
        double tmpNum;
        for (double num : arr) {
            sum += num;
            tmpNum = Math.abs(num);
            max = tmpNum > max ? tmpNum : max;
        }

        int lenght = arr.length;
        double mean = sum / lenght;
        double[] newArr = new double[lenght];
        for (int i = 0; i < lenght; i++) {
            double f = (arr[i] - mean) / max;
            newArr[i] = f;
        }

        return newArr;
    }
}

Test class package fcis.asu.Neural;

import fcis.asu.neural.ActivationType;
import fcis.asu.neural.BackPropagation;
import fcis.asu.neural.GHA;
import fcis.asu.neural.LeastMeanSquare;
import fcis.asu.neural.NeuralNetwork;
import fcis.asu.neural.SLP;
import fcis.asu.utilities.FileProperties;
import fcis.asu.utilities.ReadSamples;

public class Test {
    public static void main(String[] args){

        FileProperties prop = FileProperties.builder().
                filePath("src/main/resources/IrisData.txt")
                .targetClass("Iris-setosa")
                //.targetClass("Iris-versicolor")
                .targetClass("Iris-virginica")
                .targetFeature(0)
                .targetFeature(1)
                .classTypeIndex(4)
                .separator(",").isSLP(true)
                .hasHeader(true).build();

        NeuralNetwork neuralNetwork=new SLP(1, 2, .1, 1, 100);
        ReadSamples samples=ReadSamples.readIRISData(prop, .8);
        neuralNetwork.run(samples.getLearnSamples(), samples.getTestSamples());
        neuralNetwork = new LeastMeanSquare(1, 2, .2, 1, .1);
        neuralNetwork.run(samples.getLearnSamples(), samples.getTestSamples()); 
        samples=ReadSamples.ReadSampleImage("src/main/resources/data", .8);

        int[] nNeuralInHidenLayers = { 4 };
        int nHiddenLayers=1;
        int nFeatures=900;
        ActivationType activationType=ActivationType.Sigmoid;
        double stopMse=.001;
        double learningRate=.3;
        double slope=1;
        int nClasses=samples.getNClasses();
        double bias=1;

        neuralNetwork=new BackPropagation(nClasses, nFeatures, nHiddenLayers, nNeuralInHidenLayers, learningRate, activationType, bias, stopMse, slope);
        neuralNetwork.run(samples.getLearnSamples(), samples.getTestSamples());





    }

}

The complete project on GitHub contains image dataset and iris dataset.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.