org.apache.hama.ml.ann.TestSmallLayeredNeuralNetwork.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hama.ml.ann.TestSmallLayeredNeuralNetwork.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hama.ml.ann;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hama.commons.io.VectorWritable;
import org.apache.hama.commons.math.DenseDoubleMatrix;
import org.apache.hama.commons.math.DenseDoubleVector;
import org.apache.hama.commons.math.DoubleMatrix;
import org.apache.hama.commons.math.DoubleVector;
import org.apache.hama.commons.math.FunctionFactory;
import org.apache.hama.ml.MLTestBase;
import org.apache.hama.ml.ann.AbstractLayeredNeuralNetwork.LearningStyle;
import org.apache.hama.ml.ann.AbstractLayeredNeuralNetwork.TrainingMethod;
import org.apache.hama.ml.util.DefaultFeatureTransformer;
import org.apache.hama.ml.util.FeatureTransformer;
import org.junit.Test;
import org.mortbay.log.Log;

/**
 * Test the functionality of SmallLayeredNeuralNetwork.
 * 
 */
public class TestSmallLayeredNeuralNetwork extends MLTestBase {

    @Test
    public void testReadWrite() {
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.addLayer(2, false, FunctionFactory.createDoubleFunction("IdentityFunction"));
        ann.addLayer(5, false, FunctionFactory.createDoubleFunction("IdentityFunction"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("IdentityFunction"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        double learningRate = 0.2;
        ann.setLearningRate(learningRate);
        double momentumWeight = 0.5;
        ann.setMomemtumWeight(momentumWeight);
        double regularizationWeight = 0.05;
        ann.setRegularizationWeight(regularizationWeight);
        // intentionally initialize all weights to 0.5
        DoubleMatrix[] matrices = new DenseDoubleMatrix[2];
        matrices[0] = new DenseDoubleMatrix(5, 3, 0.2);
        matrices[1] = new DenseDoubleMatrix(1, 6, 0.8);
        ann.setWeightMatrices(matrices);
        ann.setLearningStyle(LearningStyle.UNSUPERVISED);

        FeatureTransformer defaultFeatureTransformer = new DefaultFeatureTransformer();
        ann.setFeatureTransformer(defaultFeatureTransformer);

        // write to file
        String modelPath = "/tmp/testSmallLayeredNeuralNetworkReadWrite";
        ann.setModelPath(modelPath);
        try {
            ann.writeModelToFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // read from file
        SmallLayeredNeuralNetwork annCopy = new SmallLayeredNeuralNetwork(modelPath);
        assertEquals(annCopy.getClass().getSimpleName(), annCopy.getModelType());
        assertEquals(modelPath, annCopy.getModelPath());
        assertEquals(learningRate, annCopy.getLearningRate(), 0.000001);
        assertEquals(momentumWeight, annCopy.getMomemtumWeight(), 0.000001);
        assertEquals(regularizationWeight, annCopy.getRegularizationWeight(), 0.000001);
        assertEquals(TrainingMethod.GRADIENT_DESCENT, annCopy.getTrainingMethod());
        assertEquals(LearningStyle.UNSUPERVISED, annCopy.getLearningStyle());

        // compare weights
        DoubleMatrix[] weightsMatrices = annCopy.getWeightMatrices();
        for (int i = 0; i < weightsMatrices.length; ++i) {
            DoubleMatrix expectMat = matrices[i];
            DoubleMatrix actualMat = weightsMatrices[i];
            for (int j = 0; j < expectMat.getRowCount(); ++j) {
                for (int k = 0; k < expectMat.getColumnCount(); ++k) {
                    assertEquals(expectMat.get(j, k), actualMat.get(j, k), 0.000001);
                }
            }
        }

        FeatureTransformer copyTransformer = annCopy.getFeatureTransformer();
        assertEquals(defaultFeatureTransformer.getClass().getName(), copyTransformer.getClass().getName());
    }

    @Test
    /**
     * Test the forward functionality.
     */
    public void testOutput() {
        // first network
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.addLayer(2, false, FunctionFactory.createDoubleFunction("IdentityFunction"));
        ann.addLayer(5, false, FunctionFactory.createDoubleFunction("IdentityFunction"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("IdentityFunction"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        ann.setLearningRate(0.1);
        // intentionally initialize all weights to 0.5
        DoubleMatrix[] matrices = new DenseDoubleMatrix[2];
        matrices[0] = new DenseDoubleMatrix(5, 3, 0.5);
        matrices[1] = new DenseDoubleMatrix(1, 6, 0.5);
        ann.setWeightMatrices(matrices);

        double[] arr = new double[] { 0, 1 };
        DoubleVector training = new DenseDoubleVector(arr);
        DoubleVector result = ann.getOutput(training);
        assertEquals(1, result.getDimension());
        // assertEquals(3, result.get(0), 0.000001);

        // second network
        SmallLayeredNeuralNetwork ann2 = new SmallLayeredNeuralNetwork();
        ann2.addLayer(2, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann2.addLayer(3, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann2.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann2.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        ann2.setLearningRate(0.3);
        // intentionally initialize all weights to 0.5
        DoubleMatrix[] matrices2 = new DenseDoubleMatrix[2];
        matrices2[0] = new DenseDoubleMatrix(3, 3, 0.5);
        matrices2[1] = new DenseDoubleMatrix(1, 4, 0.5);
        ann2.setWeightMatrices(matrices2);

        double[] test = { 0, 0 };
        double[] result2 = { 0.807476 };

        DoubleVector vec = ann2.getOutput(new DenseDoubleVector(test));
        assertArrayEquals(result2, vec.toArray(), 0.000001);

        SmallLayeredNeuralNetwork ann3 = new SmallLayeredNeuralNetwork();
        ann3.addLayer(2, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann3.addLayer(3, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann3.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann3.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        ann3.setLearningRate(0.3);
        // intentionally initialize all weights to 0.5
        DoubleMatrix[] initMatrices = new DenseDoubleMatrix[2];
        initMatrices[0] = new DenseDoubleMatrix(3, 3, 0.5);
        initMatrices[1] = new DenseDoubleMatrix(1, 4, 0.5);
        ann3.setWeightMatrices(initMatrices);

        double[] instance = { 0, 1 };
        DoubleVector output = ann3.getOutput(new DenseDoubleVector(instance));
        assertEquals(0.8315410, output.get(0), 0.000001);
    }

    @Test
    public void testXORlocal() {
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.addLayer(2, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(3, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        ann.setLearningRate(0.5);
        ann.setMomemtumWeight(0.0);

        int iterations = 50000; // iteration should be set to a very large number
        double[][] instances = { { 0, 1, 1 }, { 0, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 } };
        for (int i = 0; i < iterations; ++i) {
            DoubleMatrix[] matrices = null;
            for (int j = 0; j < instances.length; ++j) {
                matrices = ann.trainByInstance(new DenseDoubleVector(instances[j % instances.length]));
                ann.updateWeightMatrices(matrices);
            }
        }

        for (int i = 0; i < instances.length; ++i) {
            DoubleVector input = new DenseDoubleVector(instances[i]).slice(2);
            // the expected output is the last element in array
            double result = instances[i][2];
            double actual = ann.getOutput(input).get(0);
            if (result < 0.5 && actual >= 0.5 || result >= 0.5 && actual < 0.5) {
                Log.info("Neural network failes to lear the XOR.");
            }
        }

        // write model into file and read out
        String modelPath = "/tmp/testSmallLayeredNeuralNetworkXORLocal";
        ann.setModelPath(modelPath);
        try {
            ann.writeModelToFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        SmallLayeredNeuralNetwork annCopy = new SmallLayeredNeuralNetwork(modelPath);
        // test on instances
        for (int i = 0; i < instances.length; ++i) {
            DoubleVector input = new DenseDoubleVector(instances[i]).slice(2);
            // the expected output is the last element in array
            double result = instances[i][2];
            double actual = annCopy.getOutput(input).get(0);
            if (result < 0.5 && actual >= 0.5 || result >= 0.5 && actual < 0.5) {
                Log.info("Neural network failes to lear the XOR.");
            }
        }
    }

    @Test
    public void testXORWithMomentum() {
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.addLayer(2, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(3, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        ann.setLearningRate(0.6);
        ann.setMomemtumWeight(0.3);

        int iterations = 2000; // iteration should be set to a very large number
        double[][] instances = { { 0, 1, 1 }, { 0, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 } };
        for (int i = 0; i < iterations; ++i) {
            for (int j = 0; j < instances.length; ++j) {
                ann.trainOnline(new DenseDoubleVector(instances[j % instances.length]));
            }
        }

        for (int i = 0; i < instances.length; ++i) {
            DoubleVector input = new DenseDoubleVector(instances[i]).slice(2);
            // the expected output is the last element in array
            double result = instances[i][2];
            double actual = ann.getOutput(input).get(0);
            if (result < 0.5 && actual >= 0.5 || result >= 0.5 && actual < 0.5) {
                Log.info("Neural network failes to lear the XOR.");
            }
        }

        // write model into file and read out
        String modelPath = "/tmp/testSmallLayeredNeuralNetworkXORLocalWithMomentum";
        ann.setModelPath(modelPath);
        try {
            ann.writeModelToFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        SmallLayeredNeuralNetwork annCopy = new SmallLayeredNeuralNetwork(modelPath);
        // test on instances
        for (int i = 0; i < instances.length; ++i) {
            DoubleVector input = new DenseDoubleVector(instances[i]).slice(2);
            // the expected output is the last element in array
            double result = instances[i][2];
            double actual = annCopy.getOutput(input).get(0);
            if (result < 0.5 && actual >= 0.5 || result >= 0.5 && actual < 0.5) {
                Log.info("Neural network failes to lear the XOR.");
            }
        }
    }

    @Test
    public void testXORLocalWithRegularization() {
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.addLayer(2, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(3, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("SquaredError"));
        ann.setLearningRate(0.7);
        ann.setMomemtumWeight(0.5);
        ann.setRegularizationWeight(0.002);

        int iterations = 5000; // iteration should be set to a very large number
        double[][] instances = { { 0, 1, 1 }, { 0, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 } };
        for (int i = 0; i < iterations; ++i) {
            for (int j = 0; j < instances.length; ++j) {
                ann.trainOnline(new DenseDoubleVector(instances[j % instances.length]));
            }
        }

        for (int i = 0; i < instances.length; ++i) {
            DoubleVector input = new DenseDoubleVector(instances[i]).slice(2);
            // the expected output is the last element in array
            double result = instances[i][2];
            double actual = ann.getOutput(input).get(0);
            if (result < 0.5 && actual >= 0.5 || result >= 0.5 && actual < 0.5) {
                Log.info("Neural network failes to lear the XOR.");
            }
        }

        // write model into file and read out
        String modelPath = "/tmp/testSmallLayeredNeuralNetworkXORLocalWithRegularization";
        ann.setModelPath(modelPath);
        try {
            ann.writeModelToFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        SmallLayeredNeuralNetwork annCopy = new SmallLayeredNeuralNetwork(modelPath);
        // test on instances
        for (int i = 0; i < instances.length; ++i) {
            DoubleVector input = new DenseDoubleVector(instances[i]).slice(2);
            // the expected output is the last element in array
            double result = instances[i][2];
            double actual = annCopy.getOutput(input).get(0);
            if (result < 0.5 && actual >= 0.5 || result >= 0.5 && actual < 0.5) {
                Log.info("Neural network failes to lear the XOR.");
            }
        }
    }

    @Test
    public void testTwoClassClassification() {
        // use logistic regression data
        String filepath = "src/test/resources/logistic_regression_data.txt";
        List<double[]> instanceList = new ArrayList<double[]>();

        try {
            BufferedReader br = new BufferedReader(new FileReader(filepath));
            String line = null;
            while ((line = br.readLine()) != null) {
                String[] tokens = line.trim().split(",");
                double[] instance = new double[tokens.length];
                for (int i = 0; i < tokens.length; ++i) {
                    instance[i] = Double.parseDouble(tokens[i]);
                }
                instanceList.add(instance);
            }
            br.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        zeroOneNormalization(instanceList, instanceList.get(0).length - 1);

        int dimension = instanceList.get(0).length - 1;

        // divide dataset into training and testing
        List<double[]> testInstances = new ArrayList<double[]>();
        testInstances.addAll(instanceList.subList(instanceList.size() - 100, instanceList.size()));
        List<double[]> trainingInstances = instanceList.subList(0, instanceList.size() - 100);

        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.setLearningRate(0.001);
        ann.setMomemtumWeight(0.1);
        ann.setRegularizationWeight(0.01);
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("CrossEntropy"));

        long start = new Date().getTime();
        int iterations = 1000;
        for (int i = 0; i < iterations; ++i) {
            for (double[] trainingInstance : trainingInstances) {
                ann.trainOnline(new DenseDoubleVector(trainingInstance));
            }
        }
        long end = new Date().getTime();
        Log.info(String.format("Training time: %fs\n", (double) (end - start) / 1000));

        double errorRate = 0;
        // calculate the error on test instance
        for (double[] testInstance : testInstances) {
            DoubleVector instance = new DenseDoubleVector(testInstance);
            double expected = instance.get(instance.getDimension() - 1);
            instance = instance.slice(instance.getDimension() - 1);
            double actual = ann.getOutput(instance).get(0);
            if (actual < 0.5 && expected >= 0.5 || actual >= 0.5 && expected < 0.5) {
                ++errorRate;
            }
        }
        errorRate /= testInstances.size();

        Log.info(String.format("Relative error: %f%%\n", errorRate * 100));
    }

    @Test
    public void testLogisticRegression() {
        this.testLogisticRegressionDistributedVersion();
        this.testLogisticRegressionDistributedVersionWithFeatureTransformer();
    }

    public void testLogisticRegressionDistributedVersion() {
        // write data into a sequence file
        String tmpStrDatasetPath = "/tmp/logistic_regression_data";
        Path tmpDatasetPath = new Path(tmpStrDatasetPath);
        String strDataPath = "src/test/resources/logistic_regression_data.txt";
        String modelPath = "/tmp/logistic-regression-distributed-model";

        Configuration conf = new Configuration();
        List<double[]> instanceList = new ArrayList<double[]>();
        List<double[]> trainingInstances = null;
        List<double[]> testInstances = null;

        try {
            FileSystem fs = FileSystem.get(new URI(tmpStrDatasetPath), conf);
            fs.delete(tmpDatasetPath, true);
            if (fs.exists(tmpDatasetPath)) {
                fs.createNewFile(tmpDatasetPath);
            }

            BufferedReader br = new BufferedReader(new FileReader(strDataPath));
            String line = null;
            int count = 0;
            while ((line = br.readLine()) != null) {
                String[] tokens = line.trim().split(",");
                double[] instance = new double[tokens.length];
                for (int i = 0; i < tokens.length; ++i) {
                    instance[i] = Double.parseDouble(tokens[i]);
                }
                instanceList.add(instance);
            }
            br.close();

            zeroOneNormalization(instanceList, instanceList.get(0).length - 1);

            // write training data to temporal sequence file
            SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, tmpDatasetPath, LongWritable.class,
                    VectorWritable.class);
            int testSize = 150;

            Collections.shuffle(instanceList);
            testInstances = new ArrayList<double[]>();
            testInstances.addAll(instanceList.subList(instanceList.size() - testSize, instanceList.size()));
            trainingInstances = instanceList.subList(0, instanceList.size() - testSize);

            for (double[] instance : trainingInstances) {
                DoubleVector vec = new DenseDoubleVector(instance);
                writer.append(new LongWritable(count++), new VectorWritable(vec));
            }
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        // create model
        int dimension = 8;
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.setLearningRate(0.7);
        ann.setMomemtumWeight(0.5);
        ann.setRegularizationWeight(0.1);
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("CrossEntropy"));
        ann.setModelPath(modelPath);

        long start = new Date().getTime();
        Map<String, String> trainingParameters = new HashMap<String, String>();
        trainingParameters.put("tasks", "5");
        trainingParameters.put("training.max.iterations", "2000");
        trainingParameters.put("training.batch.size", "300");
        trainingParameters.put("convergence.check.interval", "1000");
        ann.train(tmpDatasetPath, trainingParameters);

        long end = new Date().getTime();

        // validate results
        double errorRate = 0;
        // calculate the error on test instance
        for (double[] testInstance : testInstances) {
            DoubleVector instance = new DenseDoubleVector(testInstance);
            double expected = instance.get(instance.getDimension() - 1);
            instance = instance.slice(instance.getDimension() - 1);
            double actual = ann.getOutput(instance).get(0);
            if (actual < 0.5 && expected >= 0.5 || actual >= 0.5 && expected < 0.5) {
                ++errorRate;
            }
        }
        errorRate /= testInstances.size();

        Log.info(String.format("Training time: %fs\n", (double) (end - start) / 1000));
        Log.info(String.format("Relative error: %f%%\n", errorRate * 100));
    }

    public void testLogisticRegressionDistributedVersionWithFeatureTransformer() {
        // write data into a sequence file
        String tmpStrDatasetPath = "/tmp/logistic_regression_data_feature_transformer";
        Path tmpDatasetPath = new Path(tmpStrDatasetPath);
        String strDataPath = "src/test/resources/logistic_regression_data.txt";
        String modelPath = "/tmp/logistic-regression-distributed-model-feature-transformer";

        Configuration conf = new Configuration();
        List<double[]> instanceList = new ArrayList<double[]>();
        List<double[]> trainingInstances = null;
        List<double[]> testInstances = null;

        try {
            FileSystem fs = FileSystem.get(new URI(tmpStrDatasetPath), conf);
            fs.delete(tmpDatasetPath, true);
            if (fs.exists(tmpDatasetPath)) {
                fs.createNewFile(tmpDatasetPath);
            }

            BufferedReader br = new BufferedReader(new FileReader(strDataPath));
            String line = null;
            int count = 0;
            while ((line = br.readLine()) != null) {
                String[] tokens = line.trim().split(",");
                double[] instance = new double[tokens.length];
                for (int i = 0; i < tokens.length; ++i) {
                    instance[i] = Double.parseDouble(tokens[i]);
                }
                instanceList.add(instance);
            }
            br.close();

            zeroOneNormalization(instanceList, instanceList.get(0).length - 1);

            // write training data to temporal sequence file
            SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, tmpDatasetPath, LongWritable.class,
                    VectorWritable.class);
            int testSize = 150;

            Collections.shuffle(instanceList);
            testInstances = new ArrayList<double[]>();
            testInstances.addAll(instanceList.subList(instanceList.size() - testSize, instanceList.size()));
            trainingInstances = instanceList.subList(0, instanceList.size() - testSize);

            for (double[] instance : trainingInstances) {
                DoubleVector vec = new DenseDoubleVector(instance);
                writer.append(new LongWritable(count++), new VectorWritable(vec));
            }
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        // create model
        int dimension = 8;
        SmallLayeredNeuralNetwork ann = new SmallLayeredNeuralNetwork();
        ann.setLearningRate(0.7);
        ann.setMomemtumWeight(0.5);
        ann.setRegularizationWeight(0.1);
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(dimension, false, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.addLayer(1, true, FunctionFactory.createDoubleFunction("Sigmoid"));
        ann.setCostFunction(FunctionFactory.createDoubleDoubleFunction("CrossEntropy"));
        ann.setModelPath(modelPath);

        FeatureTransformer featureTransformer = new DefaultFeatureTransformer();

        ann.setFeatureTransformer(featureTransformer);

        long start = new Date().getTime();
        Map<String, String> trainingParameters = new HashMap<String, String>();
        trainingParameters.put("tasks", "5");
        trainingParameters.put("training.max.iterations", "2000");
        trainingParameters.put("training.batch.size", "300");
        trainingParameters.put("convergence.check.interval", "1000");
        ann.train(tmpDatasetPath, trainingParameters);

        long end = new Date().getTime();

        // validate results
        double errorRate = 0;
        // calculate the error on test instance
        for (double[] testInstance : testInstances) {
            DoubleVector instance = new DenseDoubleVector(testInstance);
            double expected = instance.get(instance.getDimension() - 1);
            instance = instance.slice(instance.getDimension() - 1);
            instance = featureTransformer.transform(instance);
            double actual = ann.getOutput(instance).get(0);
            if (actual < 0.5 && expected >= 0.5 || actual >= 0.5 && expected < 0.5) {
                ++errorRate;
            }
        }
        errorRate /= testInstances.size();

        Log.info(String.format("Training time: %fs\n", (double) (end - start) / 1000));
        Log.info(String.format("Relative error: %f%%\n", errorRate * 100));
    }

}