有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用Java处理大型矩阵

我现在需要用48K x 50K的矩阵进行奇异值分解

我试过JAMA,但它只适用于行>;柱。 我尝试过PCOLT和JBLAS,但当rows*columns>;最大值

有什么建议我该怎么做

对不起,如果我在上面几行中犯了错误

提前多谢


共 (3) 个答案

  1. # 1 楼答案

    我在执行SVD计算时遇到了类似的问题,我的经验是:不要在Java中这样做。有一些工具可以更有效地实现这一点。如果你真的需要java,你可以考虑建立一个接口,从代码内部调用这个工具。我最终使用了R。我手动使用它,将矩阵存储在一个文件中,R可以将其作为矩阵读取

    顺便说一句,如果矩阵是sparse,则可能会有各种优化,这些优化将减少内存使用和输出文件的大小(如果您选择使用)

    否则,请检查此线程以查看这是否有帮助:Handle large data structure in Java

  2. # 2 楼答案

    对于非常大的内存块,我倾向于建议使用内存映射文件(也许这就是R为您所做的),您可以在Java中使用一些锅炉板代码来实现这一点。不幸的是,Java不直接支持每次超过2GB的映射,所以您必须将其划分为多个部分

    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
    
  3. # 3 楼答案

    第一步。使用数据库保存它
    第二步。使用多正面/并行算法

    This paper调查大型SVD的SOTA方法。3个处理器上的Lanzcos算法在32k X 32k矩阵上只需10分钟,但仅用于最小奇异值。也许可以进行deflate,然后重新提取连续的奇异值,我一直认为使用deflate的幂次迭代很好

    简而言之,将M X M_T和M_T X M取特征向量和特征值来重构奇异值分解矩阵

    如果您准备接受近似,那么this other paper只是处理近似算法的众多方法之一。很多都是基于某种列的下采样,或者基于最有代表性的子矩阵,在这种情况下,您可以利用立方体更小的块的优点,再加上并行性

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

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