原创

SVM、NN等统计学算法爆内存的解决方案

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://shazhenyu.blog.csdn.net/article/details/88019843

0、推荐

无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。

1、背景

这阵子一直在研究机器学习和深度学习的算法,昨天碰到了一个棘手的问题。当使用支持向量机算法时,训练样本数量达到100000条的时候,操作系统直接用“OOM Killer”将程序杀死了。我用的是CentOs系统,通常触发 Linux 内核里的 Out of Memory (OOM) killer,是因为某时刻应用程序大量请求内存导致系统内存不足造成的。OOM killer 会杀掉某个进程以腾出内存留给系统用,不致于让系统立刻崩溃。解决这个问题最简单的办法就是增加内存,后续我会一点点讲如何用最简单的方式去解决。

2、起因

内核检测到系统内存不足、挑选并杀掉某个进程的过程,可以参考内核源代码
https://github.com/torvalds/linux/blob/master/mm/oom_kill.c

通过源码我们可以看到,当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉。如何判断并选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最“bad”的那个进程就是那个最占用内存的进程。

值得关注的核心模块:

/**
 * oom_badness - heuristic function to determine which candidate task to kill
 * @p: task struct of which task we should calculate
 * @totalpages: total present RAM allowed for page allocation
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
              const nodemask_t *nodemask, unsigned long totalpages)
{
    long points;
    long adj;

    if (oom_unkillable_task(p, memcg, nodemask))
        return 0;

    p = find_lock_task_mm(p);
    if (!p)
        return 0;

    adj = (long)p->signal->oom_score_adj;
    if (adj == OOM_SCORE_ADJ_MIN) {
        task_unlock(p);
        return 0;
    }

    /*
     * The baseline for the badness score is the proportion of RAM that each
     * task's rss, pagetable and swap space use.
     */
    points = get_mm_rss(p->mm) + p->mm->nr_ptes +
         get_mm_counter(p->mm, MM_SWAPENTS);
    task_unlock(p);

    /*
     * Root processes get 3% bonus, just like the __vm_enough_memory()
     * implementation used by LSMs.
     */
    if (has_capability_noaudit(p, CAP_SYS_ADMIN))
        adj -= 30;

    /* Normalize to oom_score_adj units */
    adj *= totalpages / 1000;
    points += adj;

    /*
     * Never return 0 for an eligible task regardless of the root bonus and
     * oom_score_adj (oom_score_adj can't be OOM_SCORE_ADJ_MIN here).
     */
    return points > 0 ? points : 1;
}

3、耗内存的原因

通常情况下,我们会把总样本2-8分成测试集和训练集,通常也是在执行训练和测试的时候耗时的。每种算法中,占用内存是不相同的,拿CNN来说吧。

作为案例研究,让我们更详细地分析VGGNet。 整个VGGNet由CONV层组成,它们用步幅1和垫1执行3x3卷积,POOL层用步长2执行2x2最大池化(并且没有填充)。 我们可以在处理的每一步写出表示的大小,并跟踪表示大小和权重总数:

INPUT: [224x224x3]        memory:  224*224*3=150K   weights: 0
CONV3-64: [224x224x64]  memory:  224*224*64=3.2M   weights: (3*3*3)*64 = 1,728
CONV3-64: [224x224x64]  memory:  224*224*64=3.2M   weights: (3*3*64)*64 = 36,864
POOL2: [112x112x64]  memory:  112*112*64=800K   weights: 0
CONV3-128: [112x112x128]  memory:  112*112*128=1.6M   weights: (3*3*64)*128 = 73,728
CONV3-128: [112x112x128]  memory:  112*112*128=1.6M   weights: (3*3*128)*128 = 147,456
POOL2: [56x56x128]  memory:  56*56*128=400K   weights: 0
CONV3-256: [56x56x256]  memory:  56*56*256=800K   weights: (3*3*128)*256 = 294,912
CONV3-256: [56x56x256]  memory:  56*56*256=800K   weights: (3*3*256)*256 = 589,824
CONV3-256: [56x56x256]  memory:  56*56*256=800K   weights: (3*3*256)*256 = 589,824
POOL2: [28x28x256]  memory:  28*28*256=200K   weights: 0
CONV3-512: [28x28x512]  memory:  28*28*512=400K   weights: (3*3*256)*512 = 1,179,648
CONV3-512: [28x28x512]  memory:  28*28*512=400K   weights: (3*3*512)*512 = 2,359,296
CONV3-512: [28x28x512]  memory:  28*28*512=400K   weights: (3*3*512)*512 = 2,359,296
POOL2: [14x14x512]  memory:  14*14*512=100K   weights: 0
CONV3-512: [14x14x512]  memory:  14*14*512=100K   weights: (3*3*512)*512 = 2,359,296
CONV3-512: [14x14x512]  memory:  14*14*512=100K   weights: (3*3*512)*512 = 2,359,296
CONV3-512: [14x14x512]  memory:  14*14*512=100K   weights: (3*3*512)*512 = 2,359,296
POOL2: [7x7x512]  memory:  7*7*512=25K  weights: 0
FC: [1x1x4096]  memory:  4096  weights: 7*7*512*4096 = 102,760,448
FC: [1x1x4096]  memory:  4096  weights: 4096*4096 = 16,777,216
FC: [1x1x1000]  memory:  1000 weights: 4096*1000 = 4,096,000

TOTAL memory: 24M * 4 bytes ~= 93MB / image (only forward! ~*2 for bwd)
TOTAL params: 138M parameters

详情请看:http://cs231n.github.io/convolutional-networks/#case

4、解决方案

目前,大多数操作系统都使用了虚拟内存。如 Windows的“虚拟内存”;Linux的“交换空间”。其实所谓的虚拟内存,说白了。就是内存条的空间不够了,为了能让应用程序认为它拥有连续可用的内存(一个连续完整的地址空间),我们匀出一部分硬盘空间来充当内存使用的。在windows上,虚拟内存在硬盘上其实就是为一个硕大无比的文件,文件名是PageFile.Sys。在Linux上,通常使用Swap分区来实现。

还记得大学的时候,给Linux硬盘分区时分的Swap,一直不知道他有什么实际用途,现在才搞明白。Swap空间的作用可简单描述为:当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap空间中,等到那些程序要运行时,再从Swap中恢复保存的数据到内存中。这样,系统总是在物理内存不够时,才进行Swap交换。 其实,Swap的调整对Linux服务器,特别是Web服务器的性能至关重要。通过调整Swap,有时可以越过系统性能瓶颈,节省系统升级费用。

我们在Linux上开发就应该直接增加Swap分区就可以了:
1、创建swap分区 :

dd if=/dev/zero of=/swapfile  bs=1M count=20000

2、激活swap分区 :

# 将swap文件设置为swap分区文件  
mkswap swapfile 
#(由于此文件也会占用磁盘空间 , 最好找一个比较大的磁盘存放)
#激活swap,启用分区交换文件
swapon swapfile
#注意:insecure permissions 0644, 0600 suggested.
chmod 600 swapfile
# 停用虚拟内存
# swapoff -v swapfile

3、设置允许开机启用swap分区 : 修改 /etc/fstab

vim /etc/fstab
#第一栏磁盘装置文件名或该磁盘的lable
#第二栏挂载点
#第三栏磁盘分区槽的文件系统
/dev/mapper/fedora-root /                       ext4    defaults        1 1
/dev/mapper/fedora-home /home                   ext4    defaults        1 2
/opt/swapfile            swap                    swap     defaults       0 0

5、效果

增加完了后,查看内存使用情况:
在这里插入图片描述
或者直接用top命令来查看:
在这里插入图片描述
增加虚拟内存后,算法可以正常使用了。

以上是linux增加虚拟内存的方案,windows的可以这样做:https://jingyan.baidu.com/article/63acb44a2c7c7461fcc17edd.html

文章最后发布于: 2019-02-28 11:35:05
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503

分享到微信朋友圈

×

扫一扫,手机浏览