chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ /* ******************************************************************************
~ *
~ *
~ * This program and the accompanying materials are made available under the
~ * terms of the Apache License, Version 2.0 which is available at
~ * https://www.apache.org/licenses/LICENSE-2.0.
~ *
~ * See the NOTICE file distributed with this work for additional
~ * information regarding copyright ownership.
~ * 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.
~ *
~ * SPDX-License-Identifier: Apache-2.0
~ ******************************************************************************/
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>deeplearning4j-data</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>deeplearning4j-datavec-iterators</artifactId>
<packaging>jar</packaging>
<name>deeplearning4j-datavec-iterators</name>
<properties>
<module.name>deeplearning4j.datavec.iterators</module.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>datavec-api</artifactId>
<version>${datavec.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>nd4j-api</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,580 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.datasets.datavec;
import lombok.Getter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.datavec.api.io.WritableConverter;
import org.datavec.api.io.converters.SelfWritableConverter;
import org.datavec.api.records.Record;
import org.datavec.api.records.metadata.RecordMetaData;
import org.datavec.api.records.metadata.RecordMetaDataComposableMap;
import org.datavec.api.records.reader.RecordReader;
import org.datavec.api.records.reader.SequenceRecordReader;
import org.datavec.api.records.reader.impl.ConcatenatingRecordReader;
import org.datavec.api.records.reader.impl.collection.CollectionRecordReader;
import org.datavec.api.writable.Writable;
import org.nd4j.common.base.Preconditions;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.DataSet;
import org.nd4j.linalg.dataset.api.DataSetPreProcessor;
import org.nd4j.linalg.dataset.api.MultiDataSet;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.factory.Nd4j;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@Slf4j
public class RecordReaderDataSetIterator implements DataSetIterator {
private static final String READER_KEY = "reader";
@Getter
protected RecordReader recordReader;
protected WritableConverter converter;
protected int batchSize = 10;
protected int maxNumBatches = -1;
protected int batchNum = 0;
protected int labelIndex = -1;
protected int labelIndexTo = -1;
protected int numPossibleLabels = -1;
protected Iterator<List<Writable>> sequenceIter;
protected DataSet last;
protected boolean useCurrent = false;
protected boolean regression = false;
@Getter
protected DataSetPreProcessor preProcessor;
@Getter
private boolean collectMetaData = false;
private RecordReaderMultiDataSetIterator underlying;
private boolean underlyingIsDisjoint;
/**
* Constructor for classification, where:<br>
* (a) the label index is assumed to be the very last Writable/column, and<br>
* (b) the number of classes is inferred from RecordReader.getLabels()<br>
* Note that if RecordReader.getLabels() returns null, no output labels will be produced
*
* @param recordReader Record reader to use as the source of data
* @param batchSize Minibatch size, for each call of .next()
*/
public RecordReaderDataSetIterator(RecordReader recordReader, int batchSize) {
this(recordReader, new SelfWritableConverter(), batchSize, -1, -1,
recordReader.getLabels() == null ? -1 : recordReader.getLabels().size(), -1, false);
}
/**
* Main constructor for classification. This will convert the input class index (at position labelIndex, with integer
* values 0 to numPossibleLabels-1 inclusive) to the appropriate one-hot output/labels representation.
*
* @param recordReader RecordReader: provides the source of the data
* @param batchSize Batch size (number of examples) for the output DataSet objects
* @param labelIndex Index of the label Writable (usually an IntWritable), as obtained by recordReader.next()
* @param numPossibleLabels Number of classes (possible labels) for classification
*/
public RecordReaderDataSetIterator(RecordReader recordReader, int batchSize, int labelIndex,
int numPossibleLabels) {
this(recordReader, new SelfWritableConverter(), batchSize, labelIndex, labelIndex, numPossibleLabels, -1, false);
}
/**
* Constructor for classification, where the maximum number of returned batches is limited to the specified value
*
* @param recordReader the recordreader to use
* @param labelIndex the index/column of the label (for classification)
* @param numPossibleLabels the number of possible labels for classification. Not used if regression == true
* @param maxNumBatches The maximum number of batches to return between resets. Set to -1 to return all available data
*/
public RecordReaderDataSetIterator(RecordReader recordReader, int batchSize, int labelIndex, int numPossibleLabels,
int maxNumBatches) {
this(recordReader, new SelfWritableConverter(), batchSize, labelIndex, labelIndex, numPossibleLabels, maxNumBatches, false);
}
/**
* Main constructor for multi-label regression (i.e., regression with multiple outputs). Can also be used for single
* output regression with labelIndexFrom == labelIndexTo
*
* @param recordReader RecordReader to get data from
* @param labelIndexFrom Index of the first regression target
* @param labelIndexTo Index of the last regression target, inclusive
* @param batchSize Minibatch size
* @param regression Require regression = true. Mainly included to avoid clashing with other constructors previously defined :/
*/
public RecordReaderDataSetIterator(RecordReader recordReader, int batchSize, int labelIndexFrom, int labelIndexTo,
boolean regression) {
this(recordReader, new SelfWritableConverter(), batchSize, labelIndexFrom, labelIndexTo, -1, -1, regression);
if (!regression) {
throw new IllegalArgumentException("This constructor is only for creating regression iterators. " +
"If you're doing classification you need to use another constructor that " +
"(implicitly) specifies numPossibleLabels");
}
}
/**
* Main constructor
*
* @param recordReader the recordreader to use
* @param converter Converter. May be null.
* @param batchSize Minibatch size - number of examples returned for each call of .next()
* @param labelIndexFrom the index of the label (for classification), or the first index of the labels for multi-output regression
* @param labelIndexTo only used if regression == true. The last index <i>inclusive</i> of the multi-output regression
* @param numPossibleLabels the number of possible labels for classification. Not used if regression == true
* @param maxNumBatches Maximum number of batches to return
* @param regression if true: regression. If false: classification (assume labelIndexFrom is the class it belongs to)
*/
public RecordReaderDataSetIterator(RecordReader recordReader, WritableConverter converter, int batchSize,
int labelIndexFrom, int labelIndexTo, int numPossibleLabels, int maxNumBatches,
boolean regression) {
this.recordReader = recordReader;
this.converter = converter;
this.batchSize = batchSize;
this.maxNumBatches = maxNumBatches;
this.labelIndex = labelIndexFrom;
this.labelIndexTo = labelIndexTo;
this.numPossibleLabels = numPossibleLabels;
this.regression = regression;
}
protected RecordReaderDataSetIterator(Builder b){
this.recordReader = b.recordReader;
this.converter = b.converter;
this.batchSize = b.batchSize;
this.maxNumBatches = b.maxNumBatches;
this.labelIndex = b.labelIndex;
this.labelIndexTo = b.labelIndexTo;
this.numPossibleLabels = b.numPossibleLabels;
this.regression = b.regression;
this.preProcessor = b.preProcessor;
this.collectMetaData = b.collectMetaData;
}
/**
* When set to true: metadata for the current examples will be present in the returned DataSet.
* Disabled by default.
*
* @param collectMetaData Whether to collect metadata or not
*/
public void setCollectMetaData(boolean collectMetaData) {
if (underlying != null) {
underlying.setCollectMetaData(collectMetaData);
}
this.collectMetaData = collectMetaData;
}
private void initializeUnderlying() {
if (underlying == null) {
Record next = recordReader.nextRecord();
initializeUnderlying(next);
}
}
private void initializeUnderlying(Record next) {
int totalSize = next.getRecord().size();
//allow people to specify label index as -1 and infer the last possible label
if (numPossibleLabels >= 1 && labelIndex < 0) {
labelIndex = totalSize - 1;
labelIndexTo = labelIndex;
}
if(recordReader.resetSupported()) {
recordReader.reset();
} else {
//Hack around the fact that we need the first record to initialize the underlying RRMDSI, but can't reset
// the original reader
recordReader = new ConcatenatingRecordReader(
new CollectionRecordReader(Collections.singletonList(next.getRecord())),
recordReader);
}
RecordReaderMultiDataSetIterator.Builder builder = new RecordReaderMultiDataSetIterator.Builder(batchSize);
if (recordReader instanceof SequenceRecordReader) {
builder.addSequenceReader(READER_KEY, (SequenceRecordReader) recordReader);
} else {
builder.addReader(READER_KEY, recordReader);
}
if (regression) {
builder.addOutput(READER_KEY, labelIndex, labelIndexTo);
} else if (numPossibleLabels >= 1) {
builder.addOutputOneHot(READER_KEY, labelIndex, numPossibleLabels);
}
//Inputs: assume to be all the other writables
//In general: can't assume label indices are all at the start or end (event though 99% of the time they are)
//If they are: easy. If not: use 2 inputs in the underlying as a workaround, and concat them
if (labelIndex >= 0 && (labelIndex == 0 || labelIndexTo == totalSize - 1)) {
//Labels are first or last -> one input in underlying
int inputFrom;
int inputTo;
if (labelIndex < 0) {
//No label
inputFrom = 0;
inputTo = totalSize - 1;
} else if (labelIndex == 0) {
inputFrom = labelIndexTo + 1;
inputTo = totalSize - 1;
} else {
inputFrom = 0;
inputTo = labelIndex - 1;
}
builder.addInput(READER_KEY, inputFrom, inputTo);
underlyingIsDisjoint = false;
} else if (labelIndex >= 0) {
Preconditions.checkState(labelIndex < next.getRecord().size(),
"Invalid label (from) index: index must be in range 0 to first record size of (0 to %s inclusive), got %s", next.getRecord().size()-1, labelIndex);
Preconditions.checkState(labelIndexTo < next.getRecord().size(),
"Invalid label (to) index: index must be in range 0 to first record size of (0 to %s inclusive), got %s", next.getRecord().size()-1, labelIndexTo);
//Multiple inputs
int firstFrom = 0;
int firstTo = labelIndex - 1;
int secondFrom = labelIndexTo + 1;
int secondTo = totalSize - 1;
builder.addInput(READER_KEY, firstFrom, firstTo);
builder.addInput(READER_KEY, secondFrom, secondTo);
underlyingIsDisjoint = true;
} else {
//No labels - only features
builder.addInput(READER_KEY);
underlyingIsDisjoint = false;
}
underlying = builder.build();
if (collectMetaData) {
underlying.setCollectMetaData(true);
}
}
private DataSet mdsToDataSet(MultiDataSet mds) {
INDArray f;
INDArray fm;
if (underlyingIsDisjoint) {
//Rare case: 2 input arrays -> concat
INDArray f1 = getOrNull(mds.getFeatures(), 0);
INDArray f2 = getOrNull(mds.getFeatures(), 1);
fm = getOrNull(mds.getFeaturesMaskArrays(), 0); //Per-example masking only on the input -> same for both
//Can assume 2d features here
f = Nd4j.hstack(f1, f2);
} else {
//Standard case
f = getOrNull(mds.getFeatures(), 0);
fm = getOrNull(mds.getFeaturesMaskArrays(), 0);
}
INDArray l = getOrNull(mds.getLabels(), 0);
INDArray lm = getOrNull(mds.getLabelsMaskArrays(), 0);
DataSet ds = new DataSet(f, l, fm, lm);
if (collectMetaData) {
List<Serializable> temp = mds.getExampleMetaData();
List<Serializable> temp2 = new ArrayList<>(temp.size());
for (Serializable s : temp) {
RecordMetaDataComposableMap m = (RecordMetaDataComposableMap) s;
temp2.add(m.getMeta().get(READER_KEY));
}
ds.setExampleMetaData(temp2);
}
//Edge case, for backward compatibility:
//If labelIdx == -1 && numPossibleLabels == -1 -> no labels -> set labels array to features array
if (labelIndex == -1 && numPossibleLabels == -1 && ds.getLabels() == null) {
ds.setLabels(ds.getFeatures());
}
if (preProcessor != null) {
preProcessor.preProcess(ds);
}
return ds;
}
@Override
public DataSet next(int num) {
if (useCurrent) {
useCurrent = false;
if (preProcessor != null)
preProcessor.preProcess(last);
return last;
}
if (underlying == null) {
initializeUnderlying();
}
batchNum++;
return mdsToDataSet(underlying.next(num));
}
//Package private
static INDArray getOrNull(INDArray[] arr, int idx) {
if (arr == null || arr.length == 0) {
return null;
}
return arr[idx];
}
@Override
public int inputColumns() {
if (last == null) {
DataSet next = next();
last = next;
useCurrent = true;
return next.numInputs();
} else
return last.numInputs();
}
@Override
public int totalOutcomes() {
if (last == null) {
DataSet next = next();
last = next;
useCurrent = true;
return next.numOutcomes();
} else
return last.numOutcomes();
}
@Override
public boolean resetSupported() {
if(underlying == null){
initializeUnderlying();
}
return underlying.resetSupported();
}
@Override
public boolean asyncSupported() {
return true;
}
@Override
public void reset() {
batchNum = 0;
if (underlying != null) {
underlying.reset();
}
last = null;
useCurrent = false;
}
@Override
public int batch() {
return batchSize;
}
@Override
public void setPreProcessor(org.nd4j.linalg.dataset.api.DataSetPreProcessor preProcessor) {
this.preProcessor = preProcessor;
}
@Override
public boolean hasNext() {
return (((sequenceIter != null && sequenceIter.hasNext()) || recordReader.hasNext())
&& (maxNumBatches < 0 || batchNum < maxNumBatches));
}
@Override
public DataSet next() {
return next(batchSize);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public List<String> getLabels() {
return recordReader.getLabels();
}
/**
* Load a single example to a DataSet, using the provided RecordMetaData.
* Note that it is more efficient to load multiple instances at once, using {@link #loadFromMetaData(List)}
*
* @param recordMetaData RecordMetaData to load from. Should have been produced by the given record reader
* @return DataSet with the specified example
* @throws IOException If an error occurs during loading of the data
*/
public DataSet loadFromMetaData(RecordMetaData recordMetaData) throws IOException {
return loadFromMetaData(Collections.singletonList(recordMetaData));
}
/**
* Load a multiple examples to a DataSet, using the provided RecordMetaData instances.
*
* @param list List of RecordMetaData instances to load from. Should have been produced by the record reader provided
* to the RecordReaderDataSetIterator constructor
* @return DataSet with the specified examples
* @throws IOException If an error occurs during loading of the data
*/
public DataSet loadFromMetaData(List<RecordMetaData> list) throws IOException {
if (underlying == null) {
Record r = recordReader.loadFromMetaData(list.get(0));
initializeUnderlying(r);
}
//Convert back to composable:
List<RecordMetaData> l = new ArrayList<>(list.size());
for (RecordMetaData m : list) {
l.add(new RecordMetaDataComposableMap(Collections.singletonMap(READER_KEY, m)));
}
MultiDataSet m = underlying.loadFromMetaData(l);
return mdsToDataSet(m);
}
/**
* Builder class for RecordReaderDataSetIterator
*/
public static class Builder {
protected RecordReader recordReader;
protected WritableConverter converter;
protected int batchSize;
protected int maxNumBatches = -1;
protected int labelIndex = -1;
protected int labelIndexTo = -1;
protected int numPossibleLabels = -1;
protected boolean regression = false;
protected DataSetPreProcessor preProcessor;
private boolean collectMetaData = false;
private boolean clOrRegCalled = false;
/**
*
* @param rr Underlying record reader to source data from
* @param batchSize Batch size to use
*/
public Builder(@NonNull RecordReader rr, int batchSize){
this.recordReader = rr;
this.batchSize = batchSize;
}
public Builder writableConverter(WritableConverter converter){
this.converter = converter;
return this;
}
/**
* Optional argument, usually not used. If set, can be used to limit the maximum number of minibatches that
* will be returned (between resets). If not set, will always return as many minibatches as there is data
* available.
*
* @param maxNumBatches Maximum number of minibatches per epoch / reset
*/
public Builder maxNumBatches(int maxNumBatches){
this.maxNumBatches = maxNumBatches;
return this;
}
/**
* Use this for single output regression (i.e., 1 output/regression target)
*
* @param labelIndex Column index that contains the regression target (indexes start at 0)
*/
public Builder regression(int labelIndex){
return regression(labelIndex, labelIndex);
}
/**
* Use this for multiple output regression (1 or more output/regression targets). Note that all regression
* targets must be contiguous (i.e., positions x to y, without gaps)
*
* @param labelIndexFrom Column index of the first regression target (indexes start at 0)
* @param labelIndexTo Column index of the last regression target (inclusive)
*/
public Builder regression(int labelIndexFrom, int labelIndexTo){
this.labelIndex = labelIndexFrom;
this.labelIndexTo = labelIndexTo;
this.regression = true;
clOrRegCalled = true;
return this;
}
/**
* Use this for classification
*
* @param labelIndex Index that contains the label index. Column (indexes start from 0) be an integer value,
* and contain values 0 to numClasses-1
* @param numClasses Number of label classes (i.e., number of categories/classes in the dataset)
*/
public Builder classification(int labelIndex, int numClasses){
this.labelIndex = labelIndex;
this.labelIndexTo = labelIndex;
this.numPossibleLabels = numClasses;
this.regression = false;
clOrRegCalled = true;
return this;
}
/**
* Optional arg. Allows the preprocessor to be set
* @param preProcessor Preprocessor to use
*/
public Builder preProcessor(DataSetPreProcessor preProcessor){
this.preProcessor = preProcessor;
return this;
}
/**
* When set to true: metadata for the current examples will be present in the returned DataSet.
* Disabled by default.
*
* @param collectMetaData Whether metadata should be collected or not
*/
public Builder collectMetaData(boolean collectMetaData){
this.collectMetaData = collectMetaData;
return this;
}
public RecordReaderDataSetIterator build(){
return new RecordReaderDataSetIterator(this);
}
}
}
@@ -0,0 +1,480 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.datasets.datavec;
import lombok.Getter;
import lombok.Setter;
import org.datavec.api.records.SequenceRecord;
import org.datavec.api.records.metadata.RecordMetaData;
import org.datavec.api.records.metadata.RecordMetaDataComposable;
import org.datavec.api.records.metadata.RecordMetaDataComposableMap;
import org.datavec.api.records.reader.SequenceRecordReader;
import org.deeplearning4j.datasets.datavec.exception.ZeroLengthSequenceException;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.DataSet;
import org.nd4j.linalg.dataset.api.DataSetPreProcessor;
import org.nd4j.linalg.dataset.api.MultiDataSet;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.linalg.indexing.INDArrayIndex;
import org.nd4j.linalg.indexing.NDArrayIndex;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
public class SequenceRecordReaderDataSetIterator implements DataSetIterator {
/**Alignment mode for dealing with input/labels of differing lengths (for example, one-to-many and many-to-one type situations).
* For example, might have 10 time steps total but only one label at end for sequence classification.<br>
* Currently supported modes:<br>
* <b>EQUAL_LENGTH</b>: Default. Assume that label and input time series are of equal length, and all examples are of
* the same length<br>
* <b>ALIGN_START</b>: Align the label/input time series at the first time step, and zero pad either the labels or
* the input at the end<br>
* <b>ALIGN_END</b>: Align the label/input at the last time step, zero padding either the input or the labels as required<br>
*
* Note 1: When the time series for each example are of different lengths, the shorter time series will be padded to
* the length of the longest time series.<br>
* Note 2: When ALIGN_START or ALIGN_END are used, the DataSet masking functionality is used. Thus, the returned DataSets
* will have the input and mask arrays set. These mask arrays identify whether an input/label is actually present,
* or whether the value is merely masked.<br>
*/
public enum AlignmentMode {
EQUAL_LENGTH, ALIGN_START, ALIGN_END
}
private static final String READER_KEY = "reader";
private static final String READER_KEY_LABEL = "reader_labels";
private SequenceRecordReader recordReader;
private SequenceRecordReader labelsReader;
private int miniBatchSize = 10;
private final boolean regression;
private int labelIndex = -1;
private final int numPossibleLabels;
private int cursor = 0;
private int inputColumns = -1;
private int totalOutcomes = -1;
private boolean useStored = false;
private DataSet stored = null;
@Getter
private DataSetPreProcessor preProcessor;
private AlignmentMode alignmentMode;
private final boolean singleSequenceReaderMode;
@Getter
@Setter
private boolean collectMetaData = false;
private RecordReaderMultiDataSetIterator underlying;
private boolean underlyingIsDisjoint;
/**
* Constructor where features and labels come from different RecordReaders (for example, different files),
* and labels are for classification.
*
* @param featuresReader SequenceRecordReader for the features
* @param labels Labels: assume single value per time step, where values are integers in the range 0 to numPossibleLables-1
* @param miniBatchSize Minibatch size for each call of next()
* @param numPossibleLabels Number of classes for the labels
*/
public SequenceRecordReaderDataSetIterator(SequenceRecordReader featuresReader, SequenceRecordReader labels,
int miniBatchSize, int numPossibleLabels) {
this(featuresReader, labels, miniBatchSize, numPossibleLabels, false);
}
/**
* Constructor where features and labels come from different RecordReaders (for example, different files)
*/
public SequenceRecordReaderDataSetIterator(SequenceRecordReader featuresReader, SequenceRecordReader labels,
int miniBatchSize, int numPossibleLabels, boolean regression) {
this(featuresReader, labels, miniBatchSize, numPossibleLabels, regression, AlignmentMode.EQUAL_LENGTH);
}
/**
* Constructor where features and labels come from different RecordReaders (for example, different files)
*/
public SequenceRecordReaderDataSetIterator(SequenceRecordReader featuresReader, SequenceRecordReader labels,
int miniBatchSize, int numPossibleLabels, boolean regression, AlignmentMode alignmentMode) {
this.recordReader = featuresReader;
this.labelsReader = labels;
this.miniBatchSize = miniBatchSize;
this.numPossibleLabels = numPossibleLabels;
this.regression = regression;
this.alignmentMode = alignmentMode;
this.singleSequenceReaderMode = false;
}
/** Constructor where features and labels come from the SAME RecordReader (i.e., target/label is a column in the
* same data as the features). Defaults to regression = false - i.e., for classification
* @param reader SequenceRecordReader with data
* @param miniBatchSize size of each minibatch
* @param numPossibleLabels number of labels/classes for classification
* @param labelIndex index in input of the label index. If in regression mode and numPossibleLabels > 1, labelIndex denotes the
* first index for labels. Everything before that index will be treated as input(s) and
* everything from that index (inclusive) to the end will be treated as output(s)
*/
public SequenceRecordReaderDataSetIterator(SequenceRecordReader reader, int miniBatchSize, int numPossibleLabels,
int labelIndex) {
this(reader, miniBatchSize, numPossibleLabels, labelIndex, false);
}
/** Constructor where features and labels come from the SAME RecordReader (i.e., target/label is a column in the
* same data as the features)
* @param reader SequenceRecordReader with data
* @param miniBatchSize size of each minibatch
* @param numPossibleLabels number of labels/classes for classification
* @param labelIndex index in input of the label index. If in regression mode and numPossibleLabels > 1, labelIndex denotes the
* first index for labels. Everything before that index will be treated as input(s) and
* everything from that index (inclusive) to the end will be treated as output(s)
* @param regression Whether output is for regression or classification
*/
public SequenceRecordReaderDataSetIterator(SequenceRecordReader reader, int miniBatchSize, int numPossibleLabels,
int labelIndex, boolean regression) {
this.recordReader = reader;
this.labelsReader = null;
this.miniBatchSize = miniBatchSize;
this.regression = regression;
this.labelIndex = labelIndex;
this.numPossibleLabels = numPossibleLabels;
this.singleSequenceReaderMode = true;
}
private void initializeUnderlyingFromReader() {
initializeUnderlying(recordReader.nextSequence());
underlying.reset();
}
private void initializeUnderlying(SequenceRecord nextF) {
if (nextF.getSequenceRecord().isEmpty()) {
throw new ZeroLengthSequenceException();
}
int totalSizeF = nextF.getSequenceRecord().get(0).size();
//allow people to specify label index as -1 and infer the last possible label
if (singleSequenceReaderMode && numPossibleLabels >= 1 && labelIndex < 0) {
labelIndex = totalSizeF - 1;
} else if (!singleSequenceReaderMode && numPossibleLabels >= 1 && labelIndex < 0) {
labelIndex = 0;
}
recordReader.reset();
//Add readers
RecordReaderMultiDataSetIterator.Builder builder = new RecordReaderMultiDataSetIterator.Builder(miniBatchSize);
builder.addSequenceReader(READER_KEY, recordReader);
if (labelsReader != null) {
builder.addSequenceReader(READER_KEY_LABEL, labelsReader);
}
//Add outputs
if (singleSequenceReaderMode) {
if (labelIndex < 0 && numPossibleLabels < 0) {
//No labels - all values -> features array
builder.addInput(READER_KEY);
} else if (labelIndex == 0 || labelIndex == totalSizeF - 1) { //Features: subset of columns
//Labels are first or last -> one input in underlying
int inputFrom;
int inputTo;
if (labelIndex < 0) {
//No label
inputFrom = 0;
inputTo = totalSizeF - 1;
} else if (labelIndex == 0) {
inputFrom = 1;
inputTo = totalSizeF - 1;
} else {
inputFrom = 0;
inputTo = labelIndex - 1;
}
builder.addInput(READER_KEY, inputFrom, inputTo);
underlyingIsDisjoint = false;
} else if (regression && numPossibleLabels > 1){
//Multiple inputs and multiple outputs
int inputFrom = 0;
int inputTo = labelIndex - 1;
int outputFrom = labelIndex;
int outputTo = totalSizeF - 1;
builder.addInput(READER_KEY, inputFrom, inputTo);
builder.addOutput(READER_KEY, outputFrom, outputTo);
underlyingIsDisjoint = false;
} else {
//Multiple inputs (disjoint features case)
int firstFrom = 0;
int firstTo = labelIndex - 1;
int secondFrom = labelIndex + 1;
int secondTo = totalSizeF - 1;
builder.addInput(READER_KEY, firstFrom, firstTo);
builder.addInput(READER_KEY, secondFrom, secondTo);
underlyingIsDisjoint = true;
}
if(!(labelIndex < 0 && numPossibleLabels < 0)) {
if (regression && numPossibleLabels <= 1) {
//Multiple output regression already handled
builder.addOutput(READER_KEY, labelIndex, labelIndex);
} else if (!regression) {
builder.addOutputOneHot(READER_KEY, labelIndex, numPossibleLabels);
}
}
} else {
//Features: entire reader
builder.addInput(READER_KEY);
underlyingIsDisjoint = false;
if (regression) {
builder.addOutput(READER_KEY_LABEL);
} else {
builder.addOutputOneHot(READER_KEY_LABEL, 0, numPossibleLabels);
}
}
if (alignmentMode != null) {
switch (alignmentMode) {
case EQUAL_LENGTH:
builder.sequenceAlignmentMode(RecordReaderMultiDataSetIterator.AlignmentMode.EQUAL_LENGTH);
break;
case ALIGN_START:
builder.sequenceAlignmentMode(RecordReaderMultiDataSetIterator.AlignmentMode.ALIGN_START);
break;
case ALIGN_END:
builder.sequenceAlignmentMode(RecordReaderMultiDataSetIterator.AlignmentMode.ALIGN_END);
break;
}
}
underlying = builder.build();
if (collectMetaData) {
underlying.setCollectMetaData(true);
}
}
private DataSet mdsToDataSet(MultiDataSet mds) {
INDArray f;
INDArray fm;
if (underlyingIsDisjoint) {
//Rare case: 2 input arrays -> concat
INDArray f1 = RecordReaderDataSetIterator.getOrNull(mds.getFeatures(), 0);
INDArray f2 = RecordReaderDataSetIterator.getOrNull(mds.getFeatures(), 1);
fm = RecordReaderDataSetIterator.getOrNull(mds.getFeaturesMaskArrays(), 0); //Per-example masking only on the input -> same for both
//Can assume 3d features here
f = Nd4j.createUninitialized(new long[] {f1.size(0), f1.size(1) + f2.size(1), f1.size(2)});
f.put(new INDArrayIndex[] {NDArrayIndex.all(), NDArrayIndex.interval(0, f1.size(1)), NDArrayIndex.all()},
f1);
f.put(new INDArrayIndex[] {NDArrayIndex.all(), NDArrayIndex.interval(f1.size(1), f1.size(1) + f2.size(1)),
NDArrayIndex.all()}, f2);
} else {
//Standard case
f = RecordReaderDataSetIterator.getOrNull(mds.getFeatures(), 0);
fm = RecordReaderDataSetIterator.getOrNull(mds.getFeaturesMaskArrays(), 0);
}
INDArray l = RecordReaderDataSetIterator.getOrNull(mds.getLabels(), 0);
INDArray lm = RecordReaderDataSetIterator.getOrNull(mds.getLabelsMaskArrays(), 0);
DataSet ds = new DataSet(f, l, fm, lm);
if (collectMetaData) {
List<Serializable> temp = mds.getExampleMetaData();
List<Serializable> temp2 = new ArrayList<>(temp.size());
for (Serializable s : temp) {
RecordMetaDataComposableMap m = (RecordMetaDataComposableMap) s;
if (singleSequenceReaderMode) {
temp2.add(m.getMeta().get(READER_KEY));
} else {
RecordMetaDataComposable c = new RecordMetaDataComposable(m.getMeta().get(READER_KEY),
m.getMeta().get(READER_KEY_LABEL));
temp2.add(c);
}
}
ds.setExampleMetaData(temp2);
}
if (preProcessor != null) {
preProcessor.preProcess(ds);
}
return ds;
}
@Override
public boolean hasNext() {
if (underlying == null) {
initializeUnderlyingFromReader();
}
return underlying.hasNext();
}
@Override
public DataSet next() {
return next(miniBatchSize);
}
@Override
public DataSet next(int num) {
if (useStored) {
useStored = false;
DataSet temp = stored;
stored = null;
if (preProcessor != null)
preProcessor.preProcess(temp);
return temp;
}
if (!hasNext())
throw new NoSuchElementException();
if (underlying == null) {
initializeUnderlyingFromReader();
}
MultiDataSet mds = underlying.next(num);
DataSet ds = mdsToDataSet(mds);
if (totalOutcomes == -1) {
inputColumns = (int) ds.getFeatures().size(1);
totalOutcomes = ds.getLabels() == null ? -1 : (int) ds.getLabels().size(1);
}
return ds;
}
@Override
public int inputColumns() {
if (inputColumns != -1)
return inputColumns;
preLoad();
return inputColumns;
}
@Override
public int totalOutcomes() {
if (totalOutcomes != -1)
return totalOutcomes;
preLoad();
return totalOutcomes;
}
private void preLoad() {
stored = next();
useStored = true;
inputColumns = (int) stored.getFeatures().size(1);
totalOutcomes = (int) stored.getLabels().size(1);
}
@Override
public boolean resetSupported() {
return true;
}
@Override
public boolean asyncSupported() {
return true;
}
@Override
public void reset() {
if (underlying != null)
underlying.reset();
cursor = 0;
stored = null;
useStored = false;
}
@Override
public int batch() {
return miniBatchSize;
}
@Override
public void setPreProcessor(DataSetPreProcessor preProcessor) {
this.preProcessor = preProcessor;
}
@Override
public List<String> getLabels() {
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported for this iterator");
}
/**
* Load a single sequence example to a DataSet, using the provided RecordMetaData.
* Note that it is more efficient to load multiple instances at once, using {@link #loadFromMetaData(List)}
*
* @param recordMetaData RecordMetaData to load from. Should have been produced by the given record reader
* @return DataSet with the specified example
* @throws IOException If an error occurs during loading of the data
*/
public DataSet loadFromMetaData(RecordMetaData recordMetaData) throws IOException {
return loadFromMetaData(Collections.singletonList(recordMetaData));
}
/**
* Load a multiple sequence examples to a DataSet, using the provided RecordMetaData instances.
*
* @param list List of RecordMetaData instances to load from. Should have been produced by the record reader provided
* to the SequenceRecordReaderDataSetIterator constructor
* @return DataSet with the specified examples
* @throws IOException If an error occurs during loading of the data
*/
public DataSet loadFromMetaData(List<RecordMetaData> list) throws IOException {
if (underlying == null) {
SequenceRecord r = recordReader.loadSequenceFromMetaData(list.get(0));
initializeUnderlying(r);
}
//Two cases: single vs. multiple reader...
List<RecordMetaData> l = new ArrayList<>(list.size());
if (singleSequenceReaderMode) {
for (RecordMetaData m : list) {
l.add(new RecordMetaDataComposableMap(Collections.singletonMap(READER_KEY, m)));
}
} else {
for (RecordMetaData m : list) {
RecordMetaDataComposable rmdc = (RecordMetaDataComposable) m;
Map<String, RecordMetaData> map = new HashMap<>(2);
map.put(READER_KEY, rmdc.getMeta()[0]);
map.put(READER_KEY_LABEL, rmdc.getMeta()[1]);
l.add(new RecordMetaDataComposableMap(map));
}
}
return mdsToDataSet(underlying.loadFromMetaData(l));
}
}
@@ -0,0 +1,31 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * 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.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.deeplearning4j.datasets.datavec.exception;
public class ZeroLengthSequenceException extends RuntimeException {
public ZeroLengthSequenceException() {
this("");
}
public ZeroLengthSequenceException(String type) {
super(String.format("Encountered zero-length %ssequence", type.equals("") ? "" : type + " "));
}
}
@@ -0,0 +1,9 @@
open module deeplearning4j.datavec.iterators {
requires nd4j.common;
requires org.apache.commons.lang3;
requires slf4j.api;
requires datavec.api;
requires nd4j.api;
exports org.deeplearning4j.datasets.datavec;
exports org.deeplearning4j.datasets.datavec.exception;
}