用Java处理大型矩阵

时间:2011-12-30 09:20:02

标签: java matrix

我目前需要使用大小为48K x 50K的矩阵进行奇异值分解。

我尝试了JAMA,但它只适用于行>列。 我尝试了PCOLT,JBLAS,但是当行*列>时它们返回错误MAX_INT

有什么建议我应该做什么?

很抱歉,如果我在上述行中犯了任何错误。

提前多多感谢!

3 个答案:

答案 0 :(得分:6)

我在执行SVD计算时遇到了类似的问题,我的经验是:不要在Java中这样做。有一些工具可以更有效地完成这项工作。如果您确实需要Java,可以考虑构建一个从代码中调用该工具的接口。我最终使用了R。我通过将矩阵存储在一个文件中手动使用它,该文件可以由R作为矩阵读取。

顺便说一下,如果矩阵是sparse,那么可以进行各种优化,这样可以减少内存使用量和输出文件的大小(如果你选择使用一个)。

否则,请查看此主题以查看是否有帮助:Handle large data structure in Java

答案 1 :(得分:4)

对于非常大的内存块,我倾向于建议使用内存映射文件(也许这就是R为你做的)你可以用Java在一些样板代码中做到这一点。遗憾的是,Java并不直接支持一次超过2 GB的映射,因此您必须将其分成几个部分。

import sun.misc.Cleaner;
import sun.nio.ch.DirectBuffer;

import java.io.Closeable;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

public class LargeDoubleMatrix implements Closeable {
    private static final int MAPPING_SIZE = 1 << 30;
    private final RandomAccessFile raf;
    private final int width;
    private final int height;
    private final List<MappedByteBuffer> mappings = new ArrayList<MappedByteBuffer>();

    public LargeDoubleMatrix(String filename, int width, int height) throws IOException {
        this.raf = new RandomAccessFile(filename, "rw");
        try {
            this.width = width;
            this.height = height;
            long size = 8L * width * height;
            for (long offset = 0; offset < size; offset += MAPPING_SIZE) {
                long size2 = Math.min(size - offset, MAPPING_SIZE);
                mappings.add(raf.getChannel().map(FileChannel.MapMode.READ_WRITE, offset, size2));
            }
        } catch (IOException e) {
            raf.close();
            throw e;
        }
    }

    protected long position(int x, int y) {
        return (long) y * width + x;
    }

    public int width() {
        return width;
    }

    public int height() {
        return height;
    }

    public double get(int x, int y) {
        assert x >= 0 && x < width;
        assert y >= 0 && y < height;
        long p = position(x, y) * 8;
        int mapN = (int) (p / MAPPING_SIZE);
        int offN = (int) (p % MAPPING_SIZE);
        return mappings.get(mapN).getDouble(offN);
    }

    public void set(int x, int y, double d) {
        assert x >= 0 && x < width;
        assert y >= 0 && y < height;
        long p = position(x, y) * 8;
        int mapN = (int) (p / MAPPING_SIZE);
        int offN = (int) (p % MAPPING_SIZE);
        mappings.get(mapN).putDouble(offN, d);
    }

    public void close() throws IOException {
        for (MappedByteBuffer mapping : mappings)
            clean(mapping);
        raf.close();
    }

    private void clean(MappedByteBuffer mapping) {
        if (mapping == null) return;
        Cleaner cleaner = ((DirectBuffer) mapping).cleaner();
        if (cleaner != null) cleaner.clean();
    }
}

有此测试设置对角线值。

@Test
public  void getSetMatrix() throws IOException {
    long start = System.nanoTime();
    final long used0 = usedMemory();
    LargeDoubleMatrix matrix = new LargeDoubleMatrix("/tmp/ldm.test", 48*1000, 50*1000);
    for(int i=0;i<matrix.width();i++)
        matrix.set(i,i,i);
    for(int i=0;i<matrix.width();i++)
        assertEquals(i, matrix.get(i,i), 0.0);
    long time = System.nanoTime() - start;
    final long used = usedMemory() - used0;
    if (used==0)
        System.err.println("You need to use -XX:-UsedTLAB to see small changes in memory usage.");
    System.out.printf("Setting the diagonal took %,d ms, Heap used is %,d KB%n", time/1000/1000, used/1024);
    matrix.close();
}

private long usedMemory() {
    return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}

打印(使用-XX:-UseTLAB运行时)

Setting the diagonal took 60 ms, Heap used is 55 KB

仅创建实际使用的页面。文件看起来非常大,但分配的空间基于使用。

$ ls -lh /tmp/ldm.test 
-rw-rw-r-- 1 peter peter 18G 2011-12-30 10:18 /tmp/ldm.test
$ du -sh /tmp/ldm.test 
222M    /tmp/ldm.test

答案 2 :(得分:1)

步骤1.使用数据库保存它。
步骤2.使用多正面/并行算法。

This paper调查大型SVD的SOTA方法。 3个处理器上的Lanzcos算法在32k X 32k矩阵上花费了超过10分钟,但仅用于最小的奇异值。它可能会缩小,并重新提取连续的奇异值 - 我总是发现Power Iteration与deflate有利于此。

简而言之,使M X M_T和M_T X M并取特征向量和特征值来重建SVD矩阵。

如果您准备接受近似值,this other paper只是处理近似算法的众多问题之一。许多都是基于列的某种下采样,或者是最佳代表性的子矩阵,在这些子矩阵中,您可以利用立方体较小的片段的优势,以及并行性。

显然这些都有一些失真,但也许你可以为你的结果平滑。

最后,您确实需要使用Strassen的方法进行乘法运算。

相关问题