org.apache.hama.ml.perception.TestSmallMultiLayerPerceptron.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hama.ml.perception.TestSmallMultiLayerPerceptron.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.perception;

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

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.apache.commons.lang.SerializationUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
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.hadoop.io.WritableUtils;
import org.apache.hama.commons.io.MatrixWritable;
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.ml.util.DefaultFeatureTransformer;
import org.apache.hama.ml.util.FeatureTransformer;
import org.junit.Test;
import org.mortbay.log.Log;

public class TestSmallMultiLayerPerceptron {

    /**
     * Write and read the parameters of MLP.
     */
    @Test
    public void testWriteReadMLP() {
        String modelPath = "/tmp/sampleModel-testWriteReadMLP.data";
        double learningRate = 0.3;
        double regularization = 0.0; // no regularization
        double momentum = 0; // no momentum
        String squashingFunctionName = "Sigmoid";
        String costFunctionName = "SquaredError";
        int[] layerSizeArray = new int[] { 3, 2, 2, 3 };
        MultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);
        FeatureTransformer transformer = new DefaultFeatureTransformer();
        mlp.setFeatureTransformer(transformer);
        try {
            mlp.writeModelToFile(modelPath);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            // read the meta-data
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(conf);
            mlp = new SmallMultiLayerPerceptron(modelPath);
            assertEquals(mlp.getClass().getName(), mlp.getMLPType());
            assertEquals(learningRate, mlp.getLearningRate(), 0.001);
            assertEquals(regularization, mlp.isRegularization(), 0.001);
            assertEquals(layerSizeArray.length, mlp.getNumberOfLayers());
            assertEquals(momentum, mlp.getMomentum(), 0.001);
            assertEquals(squashingFunctionName, mlp.getSquashingFunctionName());
            assertEquals(costFunctionName, mlp.getCostFunctionName());
            assertArrayEquals(layerSizeArray, mlp.getLayerSizeArray());
            assertEquals(transformer.getClass().getName(), mlp.getFeatureTransformer().getClass().getName());
            // delete test file
            fs.delete(new Path(modelPath), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Test the output of an example MLP.
     */
    @Test
    public void testOutput() {
        // write the MLP meta-data manually
        String modelPath = "/tmp/sampleModel-testOutput.data";
        Configuration conf = new Configuration();
        try {
            FileSystem fs = FileSystem.get(conf);
            FSDataOutputStream output = fs.create(new Path(modelPath), true);

            String MLPType = SmallMultiLayerPerceptron.class.getName();
            double learningRate = 0.5;
            double regularization = 0.0;
            double momentum = 0.1;
            String squashingFunctionName = "Sigmoid";
            String costFunctionName = "SquaredError";
            int[] layerSizeArray = new int[] { 3, 2, 3, 3 };
            int numberOfLayers = layerSizeArray.length;

            WritableUtils.writeString(output, MLPType);
            output.writeDouble(learningRate);
            output.writeDouble(regularization);
            output.writeDouble(momentum);
            output.writeInt(numberOfLayers);
            WritableUtils.writeString(output, squashingFunctionName);
            WritableUtils.writeString(output, costFunctionName);

            // write the number of neurons for each layer
            for (int i = 0; i < numberOfLayers; ++i) {
                output.writeInt(layerSizeArray[i]);
            }

            double[][] matrix01 = { // 4 by 2
                    { 0.5, 0.2 }, { 0.1, 0.1 }, { 0.2, 0.5 }, { 0.1, 0.5 } };

            double[][] matrix12 = { // 3 by 3
                    { 0.1, 0.2, 0.5 }, { 0.2, 0.5, 0.2 }, { 0.5, 0.5, 0.1 } };

            double[][] matrix23 = { // 4 by 3
                    { 0.2, 0.5, 0.2 }, { 0.5, 0.1, 0.5 }, { 0.1, 0.2, 0.1 }, { 0.1, 0.2, 0.5 } };

            DoubleMatrix[] matrices = { new DenseDoubleMatrix(matrix01), new DenseDoubleMatrix(matrix12),
                    new DenseDoubleMatrix(matrix23) };
            for (DoubleMatrix mat : matrices) {
                MatrixWritable.write(mat, output);
            }

            // serialize the feature transformer
            FeatureTransformer transformer = new DefaultFeatureTransformer();
            Class<? extends FeatureTransformer> featureTransformerCls = transformer.getClass();
            byte[] featureTransformerBytes = SerializationUtils.serialize(featureTransformerCls);
            output.writeInt(featureTransformerBytes.length);
            output.write(featureTransformerBytes);

            output.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        // initial the mlp with existing model meta-data and get the output
        MultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(modelPath);
        DoubleVector input = new DenseDoubleVector(new double[] { 1, 2, 3 });
        try {
            DoubleVector result = mlp.output(input);
            assertArrayEquals(new double[] { 0.6636557, 0.7009963, 0.7213835 }, result.toArray(), 0.0001);
        } catch (Exception e1) {
            e1.printStackTrace();
        }

        // delete meta-data
        try {
            FileSystem fs = FileSystem.get(conf);
            fs.delete(new Path(modelPath), true);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * Test training with squared error on the XOR problem.
     */
    @Test
    public void testTrainWithSquaredError() {
        // generate training data
        DoubleVector[] trainingData = new DenseDoubleVector[] { new DenseDoubleVector(new double[] { 0, 0, 0 }),
                new DenseDoubleVector(new double[] { 0, 1, 1 }), new DenseDoubleVector(new double[] { 1, 0, 1 }),
                new DenseDoubleVector(new double[] { 1, 1, 0 }) };

        // set parameters
        double learningRate = 0.3;
        double regularization = 0.02; // no regularization
        double momentum = 0; // no momentum
        String squashingFunctionName = "Sigmoid";
        String costFunctionName = "SquaredError";
        int[] layerSizeArray = new int[] { 2, 5, 1 };
        SmallMultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);

        try {
            // train by multiple instances
            Random rnd = new Random();
            for (int i = 0; i < 100000; ++i) {
                DenseDoubleMatrix[] weightUpdates = mlp.trainByInstance(trainingData[rnd.nextInt(4)]);
                mlp.updateWeightMatrices(weightUpdates);
            }

            // System.out.printf("Weight matrices: %s\n",
            // mlp.weightsToString(mlp.getWeightMatrices()));
            for (int i = 0; i < trainingData.length; ++i) {
                DenseDoubleVector testVec = (DenseDoubleVector) trainingData[i].slice(2);
                double expected = trainingData[i].toArray()[2];
                double actual = mlp.output(testVec).toArray()[0];
                if (expected < 0.5 && actual >= 0.5 || expected >= 0.5 && actual < 0.5) {
                    Log.info("Neural network failes to lear the XOR.");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Test training with cross entropy on the XOR problem.
     */
    @Test
    public void testTrainWithCrossEntropy() {
        // generate training data
        DoubleVector[] trainingData = new DenseDoubleVector[] { new DenseDoubleVector(new double[] { 0, 0, 0 }),
                new DenseDoubleVector(new double[] { 0, 1, 1 }), new DenseDoubleVector(new double[] { 1, 0, 1 }),
                new DenseDoubleVector(new double[] { 1, 1, 0 }) };

        // set parameters
        double learningRate = 0.3;
        double regularization = 0.0; // no regularization
        double momentum = 0; // no momentum
        String squashingFunctionName = "Sigmoid";
        String costFunctionName = "CrossEntropy";
        int[] layerSizeArray = new int[] { 2, 7, 1 };
        SmallMultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);

        try {
            // train by multiple instances
            Random rnd = new Random();
            for (int i = 0; i < 50000; ++i) {
                DenseDoubleMatrix[] weightUpdates = mlp.trainByInstance(trainingData[rnd.nextInt(4)]);
                mlp.updateWeightMatrices(weightUpdates);
            }

            // System.out.printf("Weight matrices: %s\n",
            // mlp.weightsToString(mlp.getWeightMatrices()));
            for (int i = 0; i < trainingData.length; ++i) {
                DenseDoubleVector testVec = (DenseDoubleVector) trainingData[i].slice(2);
                double expected = trainingData[i].toArray()[2];
                double actual = mlp.output(testVec).toArray()[0];
                if (expected < 0.5 && actual >= 0.5 || expected >= 0.5 && actual < 0.5) {
                    Log.info("Neural network failes to lear the XOR.");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Test training with regularizatiion.
     */
    @Test
    public void testWithRegularization() {
        // generate training data
        DoubleVector[] trainingData = new DenseDoubleVector[] { new DenseDoubleVector(new double[] { 0, 0, 0 }),
                new DenseDoubleVector(new double[] { 0, 1, 1 }), new DenseDoubleVector(new double[] { 1, 0, 1 }),
                new DenseDoubleVector(new double[] { 1, 1, 0 }) };

        // set parameters
        double learningRate = 0.3;
        double regularization = 0.02; // regularization should be a tiny number
        double momentum = 0; // no momentum
        String squashingFunctionName = "Sigmoid";
        String costFunctionName = "CrossEntropy";
        int[] layerSizeArray = new int[] { 2, 7, 1 };
        SmallMultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);

        try {
            // train by multiple instances
            Random rnd = new Random();
            for (int i = 0; i < 20000; ++i) {
                DenseDoubleMatrix[] weightUpdates = mlp.trainByInstance(trainingData[rnd.nextInt(4)]);
                mlp.updateWeightMatrices(weightUpdates);
            }

            // System.out.printf("Weight matrices: %s\n",
            // mlp.weightsToString(mlp.getWeightMatrices()));
            for (int i = 0; i < trainingData.length; ++i) {
                DenseDoubleVector testVec = (DenseDoubleVector) trainingData[i].slice(2);
                double expected = trainingData[i].toArray()[2];
                double actual = mlp.output(testVec).toArray()[0];
                if (expected < 0.5 && actual >= 0.5 || expected >= 0.5 && actual < 0.5) {
                    Log.info("Neural network failes to lear the XOR.");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Test training with momentum. The MLP can converge faster.
     */
    @Test
    public void testWithMomentum() {
        // generate training data
        DoubleVector[] trainingData = new DenseDoubleVector[] { new DenseDoubleVector(new double[] { 0, 0, 0 }),
                new DenseDoubleVector(new double[] { 0, 1, 1 }), new DenseDoubleVector(new double[] { 1, 0, 1 }),
                new DenseDoubleVector(new double[] { 1, 1, 0 }) };

        // set parameters
        double learningRate = 0.3;
        double regularization = 0.02; // regularization should be a tiny number
        double momentum = 0.5; // no momentum
        String squashingFunctionName = "Sigmoid";
        String costFunctionName = "CrossEntropy";
        int[] layerSizeArray = new int[] { 2, 7, 1 };
        SmallMultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);

        try {
            // train by multiple instances
            Random rnd = new Random();
            for (int i = 0; i < 5000; ++i) {
                DenseDoubleMatrix[] weightUpdates = mlp.trainByInstance(trainingData[rnd.nextInt(4)]);
                mlp.updateWeightMatrices(weightUpdates);
            }

            // System.out.printf("Weight matrices: %s\n",
            // mlp.weightsToString(mlp.getWeightMatrices()));
            for (int i = 0; i < trainingData.length; ++i) {
                DenseDoubleVector testVec = (DenseDoubleVector) trainingData[i].slice(2);
                double expected = trainingData[i].toArray()[2];
                double actual = mlp.output(testVec).toArray()[0];
                if (expected < 0.5 && actual >= 0.5 || expected >= 0.5 && actual < 0.5) {
                    Log.info("Neural network failes to lear the XOR.");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testByRunningJobs() {
        this.testTrainingByXOR();
        this.testFeatureTransformer();
    }

    /**
     * Test the XOR problem.
     */
    public void testTrainingByXOR() {
        // write in some training instances
        Configuration conf = new Configuration();
        String strDataPath = "/tmp/xor-training-by-xor";
        Path dataPath = new Path(strDataPath);

        // generate training data
        DoubleVector[] trainingData = new DenseDoubleVector[] { new DenseDoubleVector(new double[] { 0, 0, 0 }),
                new DenseDoubleVector(new double[] { 0, 1, 1 }), new DenseDoubleVector(new double[] { 1, 0, 1 }),
                new DenseDoubleVector(new double[] { 1, 1, 0 }) };

        try {
            URI uri = new URI(strDataPath);
            FileSystem fs = FileSystem.get(uri, conf);
            fs.delete(dataPath, true);
            if (!fs.exists(dataPath)) {
                fs.createNewFile(dataPath);
                SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, dataPath, LongWritable.class,
                        VectorWritable.class);

                for (int i = 0; i < 1000; ++i) {
                    VectorWritable vecWritable = new VectorWritable(trainingData[i % 4]);
                    writer.append(new LongWritable(i), vecWritable);
                }
                writer.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        // begin training
        String modelPath = "/tmp/xorModel-training-by-xor.data";
        double learningRate = 0.6;
        double regularization = 0.02; // no regularization
        double momentum = 0.3; // no momentum
        String squashingFunctionName = "Tanh";
        String costFunctionName = "SquaredError";
        int[] layerSizeArray = new int[] { 2, 5, 1 };
        SmallMultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);

        Map<String, String> trainingParams = new HashMap<String, String>();
        trainingParams.put("training.iteration", "2000");
        trainingParams.put("training.mode", "minibatch.gradient.descent");
        trainingParams.put("training.batch.size", "100");
        trainingParams.put("tasks", "3");
        trainingParams.put("modelPath", modelPath);

        try {
            mlp.train(dataPath, trainingParams);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // test the model
        for (int i = 0; i < trainingData.length; ++i) {
            DenseDoubleVector testVec = (DenseDoubleVector) trainingData[i].slice(2);
            try {
                double expected = trainingData[i].toArray()[2];
                double actual = mlp.output(testVec).toArray()[0];
                if (expected < 0.5 && actual >= 0.5 || expected >= 0.5 && actual < 0.5) {
                    Log.info("Neural network failes to lear the XOR.");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Use transformer to extract the first half features of the original features.
     */
    public void testFeatureTransformer() {
        // write in some training instances
        Configuration conf = new Configuration();
        String strDataPath = "/tmp/xor-training-by-xor";
        Path dataPath = new Path(strDataPath);

        // generate training data
        DoubleVector[] trainingData = new DenseDoubleVector[] { new DenseDoubleVector(new double[] { 0, 0, 0 }),
                new DenseDoubleVector(new double[] { 0, 1, 1 }), new DenseDoubleVector(new double[] { 1, 0, 1 }),
                new DenseDoubleVector(new double[] { 1, 1, 0 }) };

        try {
            URI uri = new URI(strDataPath);
            FileSystem fs = FileSystem.get(uri, conf);
            fs.delete(dataPath, true);
            if (!fs.exists(dataPath)) {
                fs.createNewFile(dataPath);
                SequenceFile.Writer writer = new SequenceFile.Writer(fs, conf, dataPath, LongWritable.class,
                        VectorWritable.class);

                for (int i = 0; i < 1000; ++i) {
                    VectorWritable vecWritable = new VectorWritable(trainingData[i % 4]);
                    writer.append(new LongWritable(i), vecWritable);
                }
                writer.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        // begin training
        String modelPath = "/tmp/xorModel-training-by-xor.data";
        double learningRate = 0.6;
        double regularization = 0.02; // no regularization
        double momentum = 0.3; // no momentum
        String squashingFunctionName = "Tanh";
        String costFunctionName = "SquaredError";
        int[] layerSizeArray = new int[] { 1, 5, 1 };
        SmallMultiLayerPerceptron mlp = new SmallMultiLayerPerceptron(learningRate, regularization, momentum,
                squashingFunctionName, costFunctionName, layerSizeArray);

        mlp.setFeatureTransformer(new FeatureTransformer() {

            @Override
            public DoubleVector transform(DoubleVector originalFeatures) {
                return originalFeatures.sliceUnsafe(originalFeatures.getDimension() / 2);
            }

        });

        Map<String, String> trainingParams = new HashMap<String, String>();
        trainingParams.put("training.iteration", "2000");
        trainingParams.put("training.mode", "minibatch.gradient.descent");
        trainingParams.put("training.batch.size", "100");
        trainingParams.put("tasks", "3");
        trainingParams.put("modelPath", modelPath);

        try {
            mlp.train(dataPath, trainingParams);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}