分享

Spark 高级分析:第十一章第2节用Thunder加载数据

本帖最后由 feilong 于 2019-1-25 08:16 编辑

问题导读

1.如何安装Thunder

2.
示例中如何利用Thunder加载数据
3.
什么是像素

4.如何将结果可视化?


Spark 高级分析:第十一章第1节 PySpark概述
http://www.aboutyun.com/forum.php?mod=viewthread&tid=26639



Thunder包有优秀的文档和教程。下面的示例来自所提供的数据集和教程。

Thunder是一个Python工具集,用于处理Spark上的大量空间/时间数据集(即,大型多维矩阵)。它大量使用NumPy进行矩阵计算,还使用MLlib库实现一些统计技术的分布式实现。Python也使得它非常灵活,并且可以被广大用户访问。下面,我们介绍Thunder API,并尝试使用由Thunder和PySpark包装的MLlib的K-Means实现将一些神经迹分类为一组模型。

Thunder需要Spark以及Python库NumPy、SciPy、matplotlib和scikit-learn。安装Thunder可以像pip安装thunder-python一样简单,但是为了使用Spark 1.1和Hadoop 1.x(参见框)之外的任何东西,需要检查git repo本身。Thunder还包含易于在Amazon EC2上部署的脚本,并且已经在传统的HPC环境中演示过。

在编写本文时,Thunder默认是针对Hadoop 1.x API构建的,而不直接支持针对Hadoop 2.x API构建(例如,针对YARN运行所必需的)。通过pip安装Thunder还将包括针对Hadoop 1.x和Spark 1.1预先构建的Thunder JAR编译。要针对Hadoop 2.x进行构建,请更改Thunder repo中的scala/build.sbt文件,以反映所需的Hadoop版本。Thunder Hadoop版本应该与Spark Hadoop版本(也可以在SBT文件中更改)匹配。

在安装并设置SPARK_HOME环境变量之后,可以如下调用Thunder shell

图片1.png
它告诉我们,Thunder命令基本上是在包装PySpark shell。与PySpark类似,大多数计算的开始是ThunderContext变量tsc,它用特定于Thunder的功能包装Python SparkContext。

Thunder的设计特别考虑到了神经成像数据集。因此,它适合于分析来自经常随时间捕获的大量图像的数据。


让我们先从Thunder知识库中提供的一个示例数据集中加载一些斑马鱼大脑的图像。斑马鱼是生物学研究中常用的模型生物。它体积小,繁殖迅速,被用作脊椎动物发育的模型。它也很有趣,因为它具有非常快的再生能力。在神经科学的背景下,斑马鱼是一个伟大的模型,因为它是透明的,大脑足够小,基本上可以以足够高的分辨率对其进行成像,以区分单个神经元。
[mw_shl_code=python,true]path_to_images = (
'path/to/thunder/python/thunder/utils/data/fish/tif-stack')
imagesRDD = tsc.loadImages(path_to_images,
inputformat='tif-stack')
print imagesRDD
print imagesRDD.rdd
...
<thunder.rdds.images.Images object at 0x109aa59d0>
PythonRDD[8] at RDD at PythonRDD.scala:43[/mw_shl_code]

这创建了一个Images对象,它最终包装了一个RDD,可以作为imagesrdd.rdd访问。Images对象还公开了相关的类似功能(如count、take等)。存储在图像中的对象是键值对。
[mw_shl_code=python,true]print imagesRDD.first()
...
(0, array([[[26, 25],
[26, 25],
[26, 25],
...,
[26, 26],
[26, 26],
[26, 26]],
...,
[[25, 25],
[25, 25],
[25, 25],
...,
[26, 26],
[26, 26],
[26, 26]]], dtype=uint8))[/mw_shl_code]
键0对应于集合中的第零个图像(从数据目录按字典顺序排列),值是对应于图像的NumPy数组。Thunder中的所有核心数据类型最终都由keyValue对的Python RDD提供支持,其中键通常是某种元组,值是numpy数组。键和值在整个RDD中始终具有同构类型,即使PySpark允许异类集合的RDD。由于同质性的原因,Images对象公开了描述基础图像的.dims属性:
[mw_shl_code=python,true]print imagesRDD.first()[1].shape #
...
(76, 87, 2) #
print imagesRDD.dims #
...
Dimensions: min=(0, 0, 0), max=(75, 86, 1), count=(76, 87, 2)
print imagesRDD.nimages
...
20[/mw_shl_code]
我们的数据集由20个“图像”组成,其中每个图像是一个76 x 87 x 2的堆栈。Thunder提供了一个维度对象,用于跟踪RDD中数据的形状。

像素、体素和堆栈

“像素”是“图片元素”的集合。数字图像可以建模为简单的二维(2D)强度值矩阵,矩阵中的每个元素都是一个像素。(彩色图像需要这些矩阵中的3个,每个矩阵对应一个红色、绿色和蓝色通道。)然而,由于大脑是一个三维物体,单个二维切片还不足以捕捉其活动。为了解决这一问题,多种技术可以在不同的平面上互相获取多个二维图像(一个Z堆栈),有些甚至可以直接生成三维信息(例如,光场显微镜)。这最终会生成一个强度值的3D矩阵,其中每个值表示一个“体积元素”或“体素”。与此一致,Thunder将所有图像建模为二维或三维矩阵,具体取决于特定的数据类型,并且可以读取像tif这样可以本机表示三维堆栈的文件格式。

在Python中工作的一个特点是,在使用RDD的同时,我们可以很容易地可视化数据,在本例中,我们使用古老的Matplotlib库。
[mw_shl_code=python,true]import matplotlib.pyplot as plt
img = imagesRDD.values().first()
plt.imshow(img[:, : ,0], interpolation='nearest', aspect='equal',
cmap='gray')[/mw_shl_code]

图片2.png
图像API提供了以分布式方式处理图像数据的有用方法,例如,对每个图像进行二次抽样:

[mw_shl_code=python,true]subsampled = imagesRDD.subsample((5, 5, 1)) #
plt.imshow(subsampled.first()[1][:, : ,0], interpolation='nearest',
aspect='equal', cmap='gray')
print subsampled.dims
...
Dimensions: min=(0, 0, 0), max=(15, 17, 1), count=(16, 18, 2)[/mw_shl_code]

图片3.png
虽然分析图像集合可能对某些操作有用(例如,以某些方式规范化图像),但很难考虑图像的时间关系。为此,我们宁愿将图像数据作为像素/体素时间序列的集合来使用。这正是Thunder Series对象的用途,有一种简单的转换方法:
[mw_shl_code=python,true]seriesRDD = imagesRDD.toSeries()[/mw_shl_code]
此操作将数据大规模重组为一个序列对象,这是一个键值对的RDD,其中键是每个图像坐标的元组(即体素标识符),值是对应于时间序列值的一维NumPy数组。
[mw_shl_code=python,true]print seriesRDD.dims
print seriesRDD.index
print seriesRDD.count()
...
Dimensions: min=(0, 0, 0), max=(75, 86, 1), count=(76, 87, 2)
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
13224[/mw_shl_code]
尽管ImagesRDD是一个由20个具有尺寸(76 x 87 x 2)的图像组成的集合,但seriesRDD是一个由13224(76 x 87 x 2)个长度为20的时间序列组成的集合。还要注意,执行seriesrdd.dims会引发作业,因为只能通过分析series对象的所有键值来计算维度。seriesrdd.index属性是一个panda风格的索引,可用于引用每个数组。因为我们的原始图像是三维的,所以关键是三元组:
[mw_shl_code=python,true]print seriesRDD.rdd.takeSample(False, 1, 0)[0]
...
((30, 84, 1), array([35, 35, 35, 35, 35, 35, 35, 35, 34, 34,
34, 35, 35, 35, 35, 35, 35, 35, 35, 35], dtype=uint8))[/mw_shl_code]
SeriesAPI提供了许多跨时间序列执行计算的方法,无论是在每个序列级别还是跨所有序列。例如,
[mw_shl_code=python,true]print seriesRDD.max()
...
array([158, 152, 145, 143, 142, 141, 140, 140, 139, 139, 140, 140,
142, 144, 153, 168, 179, 185, 185, 182], dtype=uint8)[/mw_shl_code]
在每个时间点计算所有体素的最大值,而
[mw_shl_code=python,true]stddevRDD = seriesRDD.seriesStdev()
print stddevRDD.take(3)
print stddevRDD.dims
...
[((0, 0, 0), 0.4), ((1, 0, 0), 0.0), ((2, 0, 0), 0.0)]
Dimensions: min=(0, 0, 0), max=(75, 86, 1), count=(76, 87, 2)[/mw_shl_code]
计算每个时间序列的标准偏差,并将结果作为RDD返回,保留所有键。我们还可以将序列局部重新打包成尺寸形状(在本例中为76 x 87 x 2)
[mw_shl_code=python,true]repacked = stddevRDD.pack()
plt.imshow(repacked[:,:,0], interpolation='nearest', cmap='gray',
aspect='equal')
print type(repacked)
print repacked.shape
...
<type 'numpy.ndarray'>
(76, 87, 2)[/mw_shl_code]
它允许我们使用相同的空间关系绘制每个体素的标准差。我们应该注意确保我们不会试图向客户机返回太多的数据,因为它将消耗大量的网络和内存资源。
图片4.png
[mw_shl_code=python,true]plt.plot(seriesRDD.center().subset(50).T)[/mw_shl_code]
图片5.png
使用apply方法将任何用户定义的函数(包括lambda函数)应用到每个系列(包括lambda函数)也是非常容易的,该方法在下面调用RDD的.values().map()。
[mw_shl_code=python,true]seriesRDD.apply(lambda x: x.argmin())[/mw_shl_code]



最新经典文章,欢迎关注公众号










已有(1)人评论

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条