王兴达 刘雪峰
1(中国科学院国家空间科学中心 北京 100190)2(中国科学院大学 北京 100049)
近年来,压缩感知(CS)理论得到了越来越多的关注。单光子成像是利用压缩感知理论的一个典型应用。原理是先将物体成像在数字微镜阵列上,利用测量矩阵的调制作用和单光子探测器的测量值,就能够在低采样率条件下利用单点探测器取得良好的成像效果[1]。在实验中起调制作用的测量矩阵是非常重要的一环,通常在理想的单光子成像实验中需要30%~50%采样率的测量矩阵才能够恢复出效果比较好的图像[2]。对于高分辨率的单光子成像实验,所需测量矩阵数量可达百万级别。普通计算机利用MATLAB等软件生成一个FHD(1 920×1 080)分辨率的全采样率测量矩阵通常需要20小时以上的时间,大大降低单光子成像实验的效率,成为单光子成像实验中的一个痛点[3]。
近几年随着以Apache Spark为代表的分布式计算技术的发展为解决这一痛点带来了曙光。Spark平台本身是一个分布式的计算框架,通常用于大规模的数据处理[4],本文通过Spark中的parallel算子生成简单RDD,然后通过对该RDD的MapPartition操作能够将测量矩阵生成和评估任务均匀分配到各个机器节点的Executor上进行计算作业,利用Spark平台调度集群的
计算性能。实验证明该方法具有良好的加速比和拓展性,满足了单光子成像实验中对于测量矩阵的频繁生成需求,大幅降低了成像实验的时间成本。
压缩感知(Compressed sensing),也被称为压缩采样(Compressive sampling)或稀疏采样(Sparse sampling),是一种寻找欠定线性系统的稀疏解的技术[5]。压缩感知理论指出:只要信号是可压缩的或在某个变换域是稀疏的[6],那么就可以用一个与变换基不相关的观测矩阵将变换所得高维信号投影到一个低维空间上[7],然后通过求解一个优化问题就可以从这些少量的投影中以高概率重构出原信号,可以证明这样的投影包含了重构信号的足够信息[8]。在过去的几年内,压缩感知作为一个新的采样理论,它可以在远小于Nyquist 采样率的条件下获取信号的离散样本,保证信号的无失真重建[9]。
单光子成像是压缩感知理论的一个典型应用。借鉴计算成像中的高通量测量的思想,如图1所示,首先用光源打在物体上,通过成像透镜将物体的像成在可调制的数字微镜阵列(DMD)上,再利用测量矩阵对DMD进行0-1的掩膜调制,每个测量矩阵都随机调制图像的部分像素点,再用一个收集透镜将这些像素点的光路聚集到一个探测点上,利用单光子探测技术对其进行测量,得到光子计数测量值[10]。最后,通过TVAL3等压缩感知恢复算法及测量矩阵和测量值就能进行图像恢复,这样就能够利用单点探测器取得良好的成像效果[11]。根据压缩感知理论的成像经验,每次成像实验大约需要30%~50%的采样率才能取得比较好的成像效果。测量矩阵的数目与采样率的关系如下:
matrix_num=width×height×sample_rate
图1 单光子成像原理图
在高分辨率的情况下,所需的测量矩阵数目往往是百万级别的,普通计算机利用MATLAB生成一次FHD分辨率的测量矩阵大约需要20小时,非常影响单光子成像实验的效率。
Spark 是一个分布式计算框架,专为大规模数据处理而设计,具有快速且通用的特点[12]。Spark原本是加州大学伯克利分校的AMP实验室开源的类似于Hadoop MapReduce的通用并行框架,它拥有Hadoop MapReduce所具有的优点,但不同于MapReduce的是Spark中间的输出结果可以保存在内存中,从而不再需要读写HDFS,因此能够获得比MapReduce更快的大数据处理速度[13]。图2展示了Spark的基本框架。用户通过Driver向Master注册申请资源,由Master通过各个worker的心跳包获取集群当前的资源状况,找到合适的资源给本任务,然后在各个worker上启动Executor进程去运行用户提交任务中的每一个task,Executor向Driver通过心跳包报告运行状态,直到作业完成。分布式计算技术的瓶颈往往在与集群的网络IO,想提高计算性能应该尽可能地避免shuffle的过程出现,而测量矩阵的生成和校验是非常独立的过程,不需要与其他的节点进行通信或数据交互[14],因此非常适合于spark这种分布式计算框架,能够非常充分地利用集群机器的计算性能快速生成和评估测量矩阵。
图2 Spark框架的工作流程
测量矩阵生成分为矩阵生成,矩阵评估和数据取回三个阶段。通过spark-submit脚本中的参数并配置本次任务的矩阵类型、分辨率和采样率等。Spark的计算框架通常用于分布式的处理数据,如果想使用该框架进行数据生成可以通过Spark中内置的parallel算子生成简单RDD,然后对RDD使用Spark中的MapPartition算子,MapPartition算子是Map算子的一个变种,虽然它们都可进行分区的并行处理,但两者的主要区别是调用的粒度不一样:Map的输入变换函数是应用于RDD中每个元素,而MapPartition的输入函数是应用于每个分区[15]。通过Spark中的调度系统将本次矩阵生成与评估任务均匀地分布在各个节点上,在每个节点根据配置的参数进行测量矩阵的生成作业。对于每个生成的测量矩阵需要在本地进行矩阵质量的评估,以随机测量矩阵为例,一般需要做计算矩阵的随机偏差,在偏差允许范围内的矩阵才会被认为是合格的矩阵而保留下来。对于不合格的矩阵,则会进行丢弃然后重新生成一个新的测量矩阵,为了进行对比实验,在进行生成与评估作业中会进行数据统计(包括生成矩阵数目、矩阵质量和矩阵保留个数等)。因为测量矩阵本身是0-1矩阵,为节省存储空间并没有将结果写入HDFS中,而是通过Spark中的collect算子将已生成好的测量矩阵和统计数据拉回提交作业的Driver端,通过二进制文件的方式写入本地,供后续进行单光子成像实验使用。图3展示了一次完整的测量矩阵的生成和评估作业的流程。
图3 算法流程图
工程化实现过程主要是利用Spark中的编程模型,主要用到的算子包括Spark中的mapPartition、parallel、collect等,其算法流程的伪代码如算法1所示。
算法1 测量矩阵生成 输入:矩阵大小,节点数,矩阵数目 输出:经过评估合格的测量矩阵二进制文件1. val sc=new SparkContext()2. val config=sc.getConf.get()3. val partitionNum=sc.getConf.get()4. val matrixNumPerPartition=matrixNum / partitionNum5. val matrix=sc.parallelize(0 until partition,partition).mapPartitions {part=> for(0 to matrixNumPerPartition){ generate matrixes if(evaluate matrixes is qualified) save qualified matrix count++ else regenerate unqualified matrix count++ } Calculate matrix save rate }6. val result=matrix.collect()7. val resultFile=new FileOutputStream()8. resultFile.write(result)
由于实验室单光子成像实验的需要,在实验室搭建了8台服务器搭建的物理集群,并且安装配置好了Spark环境。为了便于进行对比实验,单机实验采用的机器环境和集群中单节点的硬件完全相同,表1中列出了单个节点机器的物理硬件配置,表2中列出了集群机器的主要软件环境版本信息。
表1 Spark集群单节点硬件配置信息
表2 Spark集群单节点软件配置信息
本文的实验选择了单机、2节点、4节点、8节点等4组进行对比的横向对比,选择了三种不同的分辨率进行纵向对比,测量矩阵选择了相对简单的随机测量矩阵。主要选取运行时间、矩阵评估存留率、加速比和拓展性作为评价指标。各评价指标说明如下:
(1) 运行时间(Runtime):该指标是本次实验的主要目的指标,运行时间指从提交作业成功到测量矩阵的二进制文件写入硬盘完毕所需的总时间。
(2) 矩阵评估留存率(Matrix Save Rate):该指标是衡量矩阵生成质量的指标,其值定义为:
(3) 加速比(Speedup):该指标主要是用来验证并行算法的效率,根据阿姆达尔定律,其值在理想的状态下通常被定义为:
(4) 可拓展性(Extension):该指标主要反映出集群节点的变化对该并行算法的性能影响大小,可以通过此指标预判集群数目与该算法的效率匹配性。
为了验证以上的实验指标,分别选择分辨率在512×512、1 024×768、1 920×1 080下进行标准随机测量矩阵算法生成进行实验。
实验1本实验对比了不同节点数N对于生成随机测量矩阵的运行时间(单位:min),实验数据如表3所示。
表3 运行时间实验结果
从表中的实验数据的对比来看,可以看出随着节点数的增加,相同分辨率下测量矩阵的生成时间在减小,大致呈现线性关系;测量矩阵的生成时间随着分辨率的提高而大幅度增加,呈现倍数关系,基本符合预期。
实验2矩阵评估存留率是指生成的测量矩阵经过评估算法之后合格的比例,实验以常用的随机测量矩阵为例,高分辨情况下选择随机偏差小于0.001的矩阵为合格矩阵,保存到最后的测量矩阵结果中,0-1随机测量矩阵的随机偏差通常用以下公式计算:
式中:pos_num是矩阵中正元素的个数,neg_num是矩阵中非正元素的个数。随机测量矩阵的随机偏差一般会随着测量矩阵分辨率的提高而降低。
矩阵评估存留率实验结果如表4所示。
表4 矩阵评估存留率实验结果
可以看出,随着节点数的增加,矩阵评估保留率一直表现比较稳定,没有显著有趋势性的变化,说明利用Spark进行分布式的测量矩阵生成依然能够获得与单机版测量矩阵生成任务质量相近的矩阵;随着测量矩阵分辨率的提高,矩阵评估存留率会逐渐提高,且与单机版实验的指标保持一致。
实验3计算不同节点数目下的加速比关系绘制折线图,如图4所示。
图4 不同节点数下的加速比
从图中可以看出,利用Spark框架生成测量矩阵的过程中,算法的加速比基本是接近线性的,随着分辨率的增加,数据规模成倍的增加,加速比也没有因为数据分散后,最后从各个节点收集数据的时间增加而出现明显下降。这是因为Spark这种运行在JVM上的平台,随着数据在各个节点上的分散,单个节点所需的内存按比例下降,JVM中消耗在内存回收(GC)的时间也会逐渐减少了。说明该算法有非常良好的加速比,可以期待该算法在未来高分辨测量矩阵生成中也具有非常好的表现。
实验4可拓展性分析。根据实验1的数据将集群使用的节点数和作业的运行时间作折线图,如图5所示。
图5 扩展性实验结果图
由图5可以看出,在以上三个分辨率的测量矩阵生成实验中,运行时间随着节点数增加明显下降,基本呈现线性关系。实验说明,该分布式测量矩阵生成算法具有良好的可扩展性。
传统利用MATLAB等软件工具生成测量矩阵的算法在面对高分辨率或高采样率等数据规模较大的情况时,完整生成一次测量矩阵所需的时间成本往往非常巨大,严重影响了成像实验的效率。通过Spark分布式计算框架可以为测量矩阵的生成和评估提供可靠、简单、有效的解决方案,大大缩短了测量矩阵的生成时间,为调试测量矩阵和成像实验提供了有力支撑。实验表明,利用Spark平台通过分布式的生成算法可以近似线性的缩减生成时间,测量矩阵质量基本与单机算法相同,并且可拓展性良好,在可预见的情况下能够支撑更大分辨率的单光子成像实验。