diff --git a/global.json b/global.json
index 058794f5d..6a0767248 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
 {
-  "sdk": {
+  "sdk": {    
     "version": "5.0.402",
     "allowPrerelease": true,
     "rollForward": "minor"
diff --git a/src/TorchSharp/DataLoader.cs b/src/TorchSharp/DataLoader.cs
new file mode 100644
index 000000000..8ed439c74
--- /dev/null
+++ b/src/TorchSharp/DataLoader.cs
@@ -0,0 +1,165 @@
+// Copyright (c) .NET Foundation and Contributors.  All Rights Reserved.  See LICENSE in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using TorchSharp.Utils;
+
+namespace TorchSharp
+{
+    public static partial class torch
+    {
+        public static partial class utils
+        {
+            public static partial class data
+            {
+                /// <summary>
+                /// This class makes easier to create batch. Data set must implement Dataset interface
+                /// </summary>
+                public class DataLoader : IEnumerable<Dictionary<string, Tensor>>, IDisposable
+                {
+                    private Dataset dataset;
+                    private int batchSize;
+                    private bool shuffle;
+                    private Device device;
+                    private IEnumerable<long> shuffler;
+
+                    /// <summary>
+                    /// Pytorch style dataloader
+                    /// </summary>
+                    /// <param name="dataset">Dataset for create batch</param>
+                    /// <param name="batchSize">Size of batch</param>
+                    /// <param name="device">device for output tensor</param>
+                    /// <param name="shuffler">Shuffler for dataloader</param>
+                    public DataLoader(Dataset dataset, int batchSize, IEnumerable<long> shuffler, Device device = null)
+                    {
+                        this.dataset = dataset;
+                        this.batchSize = batchSize;
+                        this.shuffle = true;
+                        this.device = device ?? CPU;
+                        this.shuffler = shuffler;
+                    }
+
+                    /// <summary>
+                    /// Pytorch style dataloader
+                    /// </summary>
+                    /// <param name="dataset">Dataset for create batch</param>
+                    /// <param name="batchSize">Size of batch</param>
+                    /// <param name="shuffle">true if shuffle dataset, false for not</param>
+                    /// <param name="device">device for output tensor</param>
+                    /// <param name="seed">Seed for generating shuffle</param>
+                    public DataLoader(Dataset dataset, int batchSize, bool shuffle = false, Device device = null, int? seed = null)
+                    {
+                        this.dataset = dataset;
+                        this.batchSize = batchSize;
+                        this.shuffle = shuffle;
+                        this.device = device ?? CPU;
+                        this.shuffler = seed is null ? new FisherYatesShuffler(dataset.Count) : new FisherYatesShuffler(dataset.Count, seed);
+                    }
+
+                    /// <summary>
+                    /// Generate enumerator
+                    /// </summary>
+                    /// <returns>Enumerator for batch</returns>
+                    public IEnumerator<Dictionary<string, Tensor>> GetEnumerator() =>
+                        new DataLoaderEnumerator(dataset, batchSize, shuffle, device, shuffler);
+
+                    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+                    /// <summary>
+                    /// Size of batch
+                    /// </summary>
+                    public long Count => (dataset.Count - 1) / batchSize + 1;
+
+                    private class DataLoaderEnumerator : IEnumerator<Dictionary<string, Tensor>>
+                    {
+                        private Dataset dataset;
+                        private int batchSize;
+                        private Device device;
+                        private bool shuffle;
+                        private IEnumerable<long> shuffleEnumerable;
+                        private IEnumerator<long> shuffler;
+                        private long currentVal = 0;
+                        public DataLoaderEnumerator(Dataset dataset, int batchSize, bool shuffle, Device device, IEnumerable<long> shuffleEnumerable)
+                        {
+                            this.dataset = dataset;
+                            this.batchSize = batchSize;
+                            this.device = device;
+                            this.shuffle = shuffle;
+                            this.shuffleEnumerable = shuffleEnumerable;
+                            Reset();
+                        }
+                        private bool MoveNextValue()
+                        {
+                            if (shuffle) {
+                                if (!shuffler.MoveNext()) return false;
+                                currentVal = shuffler.Current;
+                                return true;
+                            }
+                            else {
+                                currentVal++;
+                                return currentVal < dataset.Count;
+                            }
+                        }
+
+                        /// <summary>
+                        /// Get next batch
+                        /// </summary>
+                        /// <returns>true if batch created, false if batch has finished</returns>
+                        public bool MoveNext()
+                        {
+                            DisposeCurrent();
+                            if (!MoveNextValue()) return false;
+                            List<Dictionary<string, Tensor>> dic = new();
+                            dic.Add(dataset.GetTensor(currentVal));
+                            for (var i = 1; i < batchSize; i++) {
+                                if (!MoveNextValue()) break;
+                                dic.Add(dataset.GetTensor(currentVal));
+                            }
+
+                            Current = new();
+                            foreach (var x in dic[0].Keys)
+                                Current[x] = cat(dic.Select(k => k[x].unsqueeze(0)).ToArray(), 0).to(device);
+                            return true;
+                        }
+
+                        /// <summary>
+                        /// Reset enumerator
+                        /// </summary>
+                        public void Reset()
+                        {
+                            DisposeCurrent();
+                            if(shuffle) shuffler = shuffleEnumerable.GetEnumerator();
+                            currentVal = -1;
+                        }
+
+                        /// <summary>
+                        /// Current tensor
+                        /// </summary>
+                        public Dictionary<string, Tensor> Current { get; private set; }
+
+                        object IEnumerator.Current => Current;
+
+                        public void Dispose()
+                        {
+                            DisposeCurrent();
+                        }
+
+                        private void DisposeCurrent()
+                        {
+                            if (Current is null) return;
+                            foreach(var x in Current.Values)
+                                x.Dispose();
+                        }
+                    }
+
+                    public void Dispose()
+                    {
+                        dataset.Dispose();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/TorchSharp/Dataset.cs b/src/TorchSharp/Dataset.cs
new file mode 100644
index 000000000..85eca42af
--- /dev/null
+++ b/src/TorchSharp/Dataset.cs
@@ -0,0 +1,37 @@
+// Copyright (c) .NET Foundation and Contributors.  All Rights Reserved.  See LICENSE in the project root for license information.
+using System;
+using System.Collections.Generic;
+
+namespace TorchSharp
+{
+    public static partial class torch
+    {
+        public static partial class utils
+        {
+            public static partial class data
+            {
+                /// <summary>
+                /// Interface for Dataloader
+                /// </summary>
+                public abstract class Dataset : IDisposable
+                {
+                    public virtual void Dispose()
+                    {
+                    }
+
+                    /// <summary>
+                    /// Size of dataset
+                    /// </summary>
+                    public abstract long Count { get; }
+
+                    /// <summary>
+                    /// Get tensor via index
+                    /// </summary>
+                    /// <param name="index">Index for tensor</param>
+                    /// <returns>Tensor for index. You should return dictionary for catenate random amount of tensors.</returns>
+                    public abstract Dictionary<string, Tensor> GetTensor(long index);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/TorchSharp/Utils/FastShuffler.cs b/src/TorchSharp/Utils/FastShuffler.cs
new file mode 100644
index 000000000..7b2b7d7b2
--- /dev/null
+++ b/src/TorchSharp/Utils/FastShuffler.cs
@@ -0,0 +1,124 @@
+// Copyright (c) .NET Foundation and Contributors.  All Rights Reserved.  See LICENSE in the project root for license information.
+
+//Algorithm from https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/2017/09/18_2/VisitInDisorder.java
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace TorchSharp.Utils
+{
+    class FastShuffler : IEnumerable<long>
+    {
+        private long size;
+        private int? seed;
+
+        public FastShuffler(long size, int? seed = null)
+        {
+            this.size = size;
+            this.seed = seed;
+        }
+
+        private class ShuffleEnumerator : IEnumerator<long>
+        {
+            long maxrange;
+            long prime;
+            long index;
+            long offset;
+            long runningvalue;
+            private Random rand;
+            private int? seed;
+            private long size;
+
+            public ShuffleEnumerator(long size, int? seed = null)
+            {
+                this.seed = seed;
+                this.size = size;
+                Reset();
+            }
+
+            private const long MAX_COUNT = long.MaxValue;
+
+            long SelectCoPrimeResev(long min, long target)
+            {
+                var count = 0L;
+                var selected = 0L;
+                for (var val = min; val < target; ++val) {
+                    if (Coprime(val, target)) {
+                        count += 1;
+                        if ((count == 1) || (NextLong(rand, count) < 1)) {
+                            selected = val;
+                        }
+                    }
+
+                    if (count == MAX_COUNT) return val;
+                }
+
+                return selected;
+            }
+
+            static bool Coprime(long u, long v) => Gcd(u, v) == 1;
+
+            private static long Gcd(long a, long b)
+            {
+                while (a != 0 && b != 0)
+                    if (a > b) a %= b;
+                    else b %= a;
+
+                return a | b;
+            }
+
+            private static long NextLong(Random r, long l)
+            {
+                var bytebuffer = new byte[8];
+                r.NextBytes(bytebuffer);
+                var t = 0L;
+                foreach (var b in bytebuffer) {
+                    t <<= 8;
+                    t += b;
+                }
+
+                return t % l;
+            }
+
+            public bool MoveNext()
+            {
+
+                if (index >= maxrange) return false;
+                runningvalue += prime;
+                if (runningvalue >= maxrange) runningvalue -= maxrange;
+                index++;
+                return true;
+            }
+
+            public void Reset()
+            {
+                rand = seed is null ? new Random() : new Random(seed.Value);
+                var min = size / 2;
+                maxrange = size;
+                prime = SelectCoPrimeResev(min, size);
+                offset = NextLong(rand, size);
+                index = 0;
+                runningvalue = offset;
+            }
+
+            object IEnumerator.Current => Current;
+
+            public long Current => runningvalue;
+
+            public void Dispose()
+            {
+
+            }
+        }
+
+        public IEnumerator<long> GetEnumerator()
+        {
+            return new ShuffleEnumerator(size, seed);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/TorchSharp/Utils/FisherYatesShuffler.cs b/src/TorchSharp/Utils/FisherYatesShuffler.cs
new file mode 100644
index 000000000..4134ccc68
--- /dev/null
+++ b/src/TorchSharp/Utils/FisherYatesShuffler.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace TorchSharp.Utils
+{
+    public class FisherYatesShuffler : IEnumerable<long>
+    {
+        private long size;
+        private int? seed;
+
+        public FisherYatesShuffler(long size, int? seed = null)
+        {
+            this.size = size;
+            this.seed = seed;
+        }
+
+        public IEnumerator<long> GetEnumerator()
+            => seed == null
+                ? new FisherYatesShufflerEnumerable(size)
+                : new FisherYatesShufflerEnumerable(size, seed.Value);
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        private class FisherYatesShufflerEnumerable : IEnumerator<long>
+        {
+            public FisherYatesShufflerEnumerable(long size)
+            {
+                this.size = size;
+                this.seed = null;
+                Reset();
+            }
+
+            public FisherYatesShufflerEnumerable(long size, int seed)
+            {
+                this.size = size;
+                this.seed = seed;
+                Reset();
+            }
+
+            private int? seed;
+            private long size;
+            private Dictionary<long, long> indexs = new();
+            private Random r;
+            private long current = -1;
+
+            public bool MoveNext()
+            {
+                current++;
+                return current < size;
+            }
+
+            public void Reset()
+            {
+                r = seed == null ? new Random() : new Random(seed.Value);
+                current = -1;
+                indexs.Clear();
+                for (var i = 0L; i < size; i++) {
+                    var rndidx = GetRandomLong(i);
+                    if (rndidx == i)
+                        indexs[i] = i;
+                    else {
+                        indexs[i] = indexs[rndidx];
+                        indexs[rndidx] = i;
+                    }
+                }
+            }
+
+            public long Current => indexs[current];
+
+            object IEnumerator.Current => Current;
+
+            public void Dispose()
+            {
+                /* Ignore */
+            }
+
+            private long GetRandomLong(long l)
+            {
+                unchecked {
+                    return (((long) r.Next() << 32) + (uint) r.Next()) % (l + 1);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/TorchSharpTest/TestDataLoader.cs b/test/TorchSharpTest/TestDataLoader.cs
new file mode 100644
index 000000000..7054f4f12
--- /dev/null
+++ b/test/TorchSharpTest/TestDataLoader.cs
@@ -0,0 +1,73 @@
+// Copyright (c) .NET Foundation and Contributors.  All Rights Reserved.  See LICENSE in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+
+namespace TorchSharp
+{
+    public class TestDataLoader
+    {
+        private class TestDataset : torch.utils.data.Dataset
+        {
+            public override long Count { get; } = 10;
+            public override Dictionary<string, torch.Tensor> GetTensor(long index)
+            {
+                return new() {{"data", torch.tensor(1)}, {"label", torch.tensor(13)}, {"index", torch.tensor(index)}};
+            }
+        }
+
+        [Fact]
+        public void DatasetTest()
+        {
+            using var dataset = new TestDataset();
+            var d = dataset.GetTensor(0);
+            Assert.True(d.ContainsKey("data"));
+            Assert.True(d.ContainsKey("index"));
+            Assert.True(d.ContainsKey("label"));
+
+            Assert.Equal(d["data"], torch.tensor(1));
+            Assert.Equal(d["label"], torch.tensor(13));
+            Assert.Equal(d["index"], torch.tensor(0L));
+        }
+
+        [Fact]
+        public void DataLoaderTest1()
+        {
+            using var dataset = new TestDataset();
+            using var dataloader = new torch.utils.data.DataLoader(dataset, 2, false, torch.CPU);
+            var iterator = dataloader.GetEnumerator();
+            Assert.True(iterator.MoveNext());
+            Assert.Equal(iterator.Current["data"], torch.tensor(rawArray: new[]{1L, 1L}, dimensions: new[]{2L}, dtype: torch.ScalarType.Int32));
+            Assert.Equal(iterator.Current["label"], torch.tensor(rawArray: new[]{13L, 13L}, dimensions: new[]{2L}, dtype: torch.ScalarType.Int32));
+            Assert.Equal(iterator.Current["index"].ToString(true), torch.tensor(rawArray: new[]{0L, 1L}, dimensions: new[]{2L}, dtype: torch.ScalarType.Int64).ToString(true));
+            iterator.Dispose();
+        }
+
+        [Fact]
+        public void DataLoaderTest2()
+        {
+            using var dataset = new TestDataset();
+            using var dataloader = new torch.utils.data.DataLoader(dataset, 2, false, torch.CPU);
+            long idx = 0;
+            foreach (var x in dataloader) {
+                Assert.Equal(x["data"], torch.tensor(new[]{1, 1}, new[]{2L}));
+                Assert.Equal(x["index"], torch.tensor(new[]{idx++, idx++}, new[]{2L}));
+            }
+        }
+
+        [Fact]
+        public void CustomSeedTest()
+        {
+            using var dataset = new TestDataset();
+            using var dataloader = new torch.utils.data.DataLoader(dataset, 2, true, seed: 1);
+            using var dataloader2 = new torch.utils.data.DataLoader(dataset, 2, true, seed: 1);
+            var iterator = dataloader.GetEnumerator();
+            var iterator2 = dataloader2.GetEnumerator();
+            iterator.MoveNext();
+            iterator2.MoveNext();
+            Assert.Equal(iterator.Current, iterator2.Current);
+        }
+    }
+}
\ No newline at end of file
