use rand::seq::SliceRandom;
use super::Dataset;
use super::Batcher;
pub struct DataLoader<D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
dataset: D,
batcher: B,
batch_size: usize,
shuffle: bool,
}
#[derive(Debug, thiserror::Error)]
pub enum DataLoaderError {
#[error("dataset error: {0}")]
Dataset(String),
#[error("batcher error: {0}")]
Batcher(String),
}
pub struct DataLoaderIter<'a, D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
loader: &'a DataLoader<D, B>,
cursor: usize,
indices: Vec<usize>,
}
pub struct DataLoaderIntoIter<D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
loader: DataLoader<D, B>,
cursor: usize,
indices: Vec<usize>,
}
impl<D, B> DataLoader<D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
pub fn new(dataset: D, batcher: B, batch_size: usize, shuffle: bool) -> Self {
Self {
dataset,
batcher,
batch_size,
shuffle,
}
}
pub fn dataset_len(&self) -> usize {
self.dataset.len()
}
pub fn batch_count(&self) -> usize {
self.dataset.len() / self.batch_size
}
pub fn batch_size(&self) -> usize {
self.batch_size
}
pub fn iter<'a>(&'a self) -> DataLoaderIter<'a, D, B> {
let indices = self.get_iter_indices();
DataLoaderIter { loader: self, cursor: 0, indices }
}
fn get_iter_indices(&self) -> Vec<usize> {
let len = self.dataset.len();
let mut indices: Vec<usize> = (0..len).collect();
if self.shuffle {
let mut rng = rand::rng();
indices.shuffle(&mut rng);
}
indices
}
}
impl<D, B> DataLoader<D, B>
where
D: Dataset,
B: Batcher<Item = D::Item> + Default
{
pub fn from_dataset(dataset: D, batch_size: usize, shuffle: bool) -> Self {
Self::new(dataset, B::default(), batch_size, shuffle)
}
}
impl<D, B> Iterator for DataLoaderIntoIter<D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
type Item = Result<B::Output, DataLoaderError>;
fn next(&mut self) -> Option<Self::Item> {
iter_next(&self.loader, &mut self.cursor, &self.indices)
}
}
impl<'a, D, B> Iterator for DataLoaderIter<'a, D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
type Item = Result<B::Output, DataLoaderError>;
fn next(&mut self) -> Option<Self::Item> {
iter_next(&self.loader, &mut self.cursor, &self.indices)
}
}
impl<D, B> IntoIterator for DataLoader<D, B>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
type IntoIter = DataLoaderIntoIter<D, B>;
type Item = Result<B::Output, DataLoaderError>;
fn into_iter(self) -> Self::IntoIter {
let indices = self.get_iter_indices();
DataLoaderIntoIter {
loader: self,
cursor: 0,
indices,
}
}
}
fn iter_next<D, B>(loader: &DataLoader<D, B>, cursor: &mut usize, indices: &[usize]) -> Option<Result<B::Output, DataLoaderError>>
where
D: Dataset,
B: Batcher<Item = D::Item>
{
let begin = *cursor;
if begin >= loader.dataset.len() {
None
} else {
let mut f = || -> Result<B::Output, DataLoaderError> {
let end = *cursor + loader.batch_size;
let end = end.min(loader.dataset.len());
let mut items = vec![];
for index in begin..end {
let index = indices[index];
let item = loader.dataset
.get(index)
.map_err(|e| DataLoaderError::Dataset(e.to_string()))?
.expect("must valid item");
items.push(item);
}
*cursor += items.len();
let batch = loader.batcher
.batch(items)
.map_err(|e| DataLoaderError::Batcher(e.to_string()))?;
Ok(batch)
};
Some(f())
}
}