CIFAR-10 Dataset

Presented techniques can be utilized to classify images from the full CIFAR 10 dataset. The most significant difference is how to load images (although images representation is similar). Helper method for downloading and unpacking cifar 10 dataset is provided:

public static void downloadAndExtract() {

    if (new File("data/cifar-10-batches-bin", TEST_DATA_FILE).exists() == false) {
        try {
            if (new File("data", ARCHIVE_BINARY_FILE).exists() == false) {
                URL website = new URL("http://www.cs.toronto.edu/~kriz/" + ARCHIVE_BINARY_FILE);
                FileOutputStream fos = new FileOutputStream("data/" + ARCHIVE_BINARY_FILE);
                fos.getChannel().transferFrom(Channels.newChannel(website.openStream()), 0, Long.MAX_VALUE);
                fos.close();
            }
            TarArchiveInputStream tar =
                    new TarArchiveInputStream(
                            new GZIPInputStream(new FileInputStream("data/" + ARCHIVE_BINARY_FILE)));
            TarArchiveEntry entry = null;
            while ((entry = tar.getNextTarEntry()) != null) {
                if (entry.isDirectory()) {
                    new File("data", entry.getName()).mkdirs();
                } else {
                    byte data[] = new byte[2048];
                    int count;
                    BufferedOutputStream bos = new BufferedOutputStream(
                            new FileOutputStream(new File("data/", entry.getName())), 2048);

                    while ((count = tar.read(data, 0, 2048)) != -1) {
                        bos.write(data, 0, count);
                    }
                    bos.close();
                }
            }
            tar.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

which after checking the existence of test data file in local data folder starts to download tar.gz archive or just unpack it if already downloaded.

Unpacked files are transformed into arrays of double values, with label representing one of 10 categories at index 0 and RGB channels following them. Data augmentation can be done exactly the same way, by horizontally flipping training images:

JavaPairRDD<String, PortableDataStream> files = sc.binaryFiles("data/cifar-10-batches-bin");

JavaRDD<double[]> imagesTrain = files
    .filter(f -> ArrayUtils.contains(CifarReader.TRAIN_DATA_FILES, extractFileName.apply(f._1)))
    .flatMap(f -> CifarReader.rawDouble(f._2.open()));

JavaRDD<double[]> imagesTest = files
    .filter(f -> CifarReader.TEST_DATA_FILE.equals(extractFileName.apply(f._1)))
    .flatMap(f -> CifarReader.rawDouble(f._2.open()));

JavaRDD<DataSet> testDataset = imagesTest
    .map(i -> {
        INDArray label = FeatureUtil.toOutcomeVector(Double.valueOf(i[0]).intValue(), numLabels);
        double[] arr = Arrays.stream(ArrayUtils.remove(i, 0)).boxed().map(normalize2)
            .mapToDouble(Double::doubleValue).toArray();
        INDArray features = Nd4j.create(arr, new int[] { 1, arr.length });
        return new DataSet(features, label);
    }).cache();
log.info("Number of test images {}", testDataset.count());            

JavaPairRDD<INDArray, double[]> labelsWithDataTrain = imagesTrain.mapToPair(
    i -> {
        INDArray label = FeatureUtil.toOutcomeVector(Double.valueOf(i[0]).intValue(), numLabels);
        double[] arr = Arrays.stream(ArrayUtils.remove(i, 0)).boxed().map(normalize2).mapToDouble(Double::doubleValue).toArray();
        return new Tuple2<>(label, arr);
    });

JavaRDD<DataSet> flipped = labelsWithDataTrain
    .map(t -> {
        double[] arr = t._2;
        int idx = 0;
        double[] farr = new double[arr.length];
        for (int i = 0; i < arr.length; i += trainer.getWidth()) {
            double[] temp = Arrays.copyOfRange(arr, i, i + trainer.getWidth());
            ArrayUtils.reverse(temp);
            for (int j = 0; j < trainer.getHeight(); ++j) {
                farr[idx++] = temp[j];
            }
        }
        INDArray features = Nd4j.create(farr, new int[] { 1, farr.length });
        return new DataSet(features, t._1);
    });

JavaRDD<DataSet> trainDataset = labelsWithDataTrain
    .map(t -> {
        INDArray features = Nd4j.create(t._2, new int[] { 1, t._2.length });
        return new DataSet(features, t._1);
    }).union(flipped).cache();
log.info("Number of train images {}", trainDataset.count());

Training procedure can be also the same (as for primary dataset), but in this case every single epoch is going to take much more time.

results matching ""

    No results matching ""