1

我正在尝试在 CSharp 中读取一个大的 .npy 文件。为了做到这一点,我正在尝试使用NumSharp nuget。

该文件是 7GB 锯齿状浮点数组 (float[][])。它有大约 100 万个向量,每个向量都是 960 维。

注意: 更具体地说,我使用的数据是来自以下链接Approximate Nearest Neighbors Large datasets的 GIST 。

以下是我用来加载数据的方法,但它失败并出现异常:

    private static void ReadNpyVectorsFromFile(string pathPrefix, out List<float[]> candidates)
    {
        var npyFilename = @$"{pathPrefix}.npy";
        
        var v = np.load(npyFilename);//NDArray
        
        candidates = v
            .astype(np.float32)
            .ToJaggedArray<float>()
            .OfType<float[]>()
            .Select(a =>
            {
                return a.OfType<float>().ToArray();
            })
            .ToList();
    }

例外是:

引发异常:NumSharp.dll 中的“System.OverflowException” NumSharp.dll 中发生“System.OverflowException”类型的未处理异常算术运算导致溢出。

我该如何解决这个问题?


更新

如果文件太大,NumSharp 包有限制。阅读下面的评论/答案以获得更多解释。我添加了一个带有解决方法建议的答案

但是,一个好的选择是将数据保存为 .npz(请参阅:numpy.savez()),然后以下包可以完成这项工作:

https://github.com/matajoh/libnpy

代码示例:

        NPZInputStream npz = new NPZInputStream(npyFilename);
        var keys = npz.Keys();
        //var header = npz.Peek(keys[0]);
        var t = npz.ReadFloat32(keys[0]);

        Debug.Assert(t.DataType == DataType.FLOAT32);
4

2 回答 2

1

我看到您已经找到了解决方法。万一您现在想知道问题的原因,那是因为Array.NET 中的类的限制。

np.load(string path)方法在此处定义,然后依次调用np.load(Stream stream).

int bytes;
Type type;
int[] shape;
if (!parseReader(reader, out bytes, out type, out shape))
    throw new FormatException();

Array array = Arrays.Create(type, shape.Aggregate((dims, dim) => dims * dim));

var result = new NDArray(readValueMatrix(reader, array, bytes, type, shape));
return result.reshape(shape);

bytes是您的日期类型的大小。因为你使用的是float,所以这个值为4shape是向量的数量和它们的形状。

接下来,我们来看看readValueMatrix 方法

int total = 1;
for (int i = 0; i < shape.Length; i++)
    total *= shape[i];
var buffer = new byte[bytes * total];
// omitted

NumSharp 试图创建一个byte大小为 equals 的一维数组bytes * total。这里,bytes是 4,total是向量的数量乘以所有维度的大小。

但是,在 .NET 中,byte数组的任何给定维度中的最大索引是0X7FFFFFC7,即2147483591,如此所述。我还没有下载你的数据,但我猜它足够大了bytes * total > 2147483591

请注意,如果您想使用 NumSharp 将数据写回 npy 文件,那么您将在writeValueMatrix 方法中遇到同样的问题。

于 2021-11-15T16:55:41.013 回答
0

问题是 NumSharp 数据结构是一个沉重的 RAM 消耗者,而且 CSharp GC 似乎不知道 NumSharp 正在分配什么,因此它很快就达到了 RAM 限制。

因此,为了克服这个问题,我拆分了输入 npy 文件,以便每个部分消耗的内存不应超过 C# (2147483591) 中允许的最大内存分配。就我而言,我分成 5 个不同的文件(每个文件 200k 向量)。

python部分拆分大.npy文件:

 infile = r'C:\temp\input\GIST.1m.npy'
 data = np.load(infile)

 # create 5 files
 incr = int(data.shape[0] / 5) 

 # the +1 is to handle any leftovers
 r = range(0,  int(size/incr + 1)) 

 for i in r:
    print(i)

    start = i * incr
    stop = min(start + incr, size)

    if(start >= len(data)):
        break

    np.save(infile.replace('.npy', f'.{i}.npy'), data[start:stop])

现在在 CSharp 中,代码如下所示:

    private static void ReadNpyVectorsFromFile(string pathPrefix, out List<float[]> candidates)
    {
        candidates = new List<float[]>();

        // TODO: 
        // For now I am assuming there are 10 files maximum... 
        // this can be improved by scanning the input folder and 
        // collecting all the relevant files.
        foreach (var i in Enumerable.Range(-1, 10))
        {
            var npyFilename = @$"{pathPrefix}.{i}.npy";
            Console.WriteLine(npyFilename);

            if (!File.Exists(npyFilename))
                continue;

            var v = np.load(npyFilename); //NDArray

            var tempList = v
                .astype(np.float32)
                .ToJaggedArray<float>()
                .OfType<float[]>()
                .Select(a => { return a.OfType<float>().ToArray(); })
                .ToList();

            candidates.AddRange(tempList);
        }
    }
于 2021-11-11T06:57:23.367 回答