Spark中的编程模型
1. Spark中的基本概念 在Spark中,有下面的基本概念。 Application:基于Spark的用户程序,包含了一个driver program和集群中多个executor Driver Program:运行Application的main()函数并创建SparkContext。通常SparkContext代表driver program Executor:为
1. Spark中的基本概念
在Spark中,有下面的基本概念。
Application:基于Spark的用户程序,包含了一个driver program和集群中多个executor
Driver Program:运行Application的main()函数并创建SparkContext。通常SparkContext代表driver program
Executor:为某Application运行在worker node上的饿一个进程。该进程负责运行Task,并负责将数据存在内存或者磁盘上。每个Application都有自己独立的executors
Cluster Manager: 在集群上获得资源的外部服务(例如 Spark Standalon,Mesos、Yarn)
Worker Node: 集群中任何可运行Application代码的节点
Task:被送到executor上执行的工作单元。
Job:可以被拆分成Task并行计算的工作单元,一般由Spark Action触发的一次执行作业。
Stage:每个Job会被拆分成很多组Task,每组任务被称为stage,也可称TaskSet。该术语可以经常在日志中看打。
RDD:Spark的基本计算单元,通过Scala集合转化、读取数据集生成或者由其他RDD经过算子操作得到。
2. Spark应用框架
客户Spark程序(Driver Program)来操作Spark集群是通过SparkContext对象来进行,SparkContext作为一个操作和调度的总入口,在初始化过程中集群管理器会创建DAGScheduler作业调度和TaskScheduler任务调度。
DAGScheduler作业调度模块是基于Stage的高层调度模块(参考:Spark分析之DAGScheduler),DAG全称 Directed Acyclic Graph,有向无环图。简单的来说,就是一个由顶点和有方向性的边构成的图中,从任意一个顶点出发,没有任何一条路径会将其带回到出发的顶点。它为每个Spark Job计算具有依赖关系的多个Stage任务阶段(通常根据Shuffle来划分Stage,如groupByKey, reduceByKey等涉及到shuffle的transformation就会产生新的stage),然后将每个Stage划分为具体的一组任务,以TaskSets的形式提交给底层的任务调度模块来具体执行。其中,不同stage之前的RDD为宽依赖关系。 TaskScheduler任务调度模块负责具体启动任务,监控和汇报任务运行情况。
创建SparkContext一般要经过下面几个步骤:
a). 导入Spark的类和隐式转换
import org.apache.spark.{SparkContext, SparkConf} import org.apache.spark.SparkContext._
b). 构建Spark应用程序的应用信息对象SparkConf
val conf = new SparkConf().setAppName(appName).setMaster(master_url)
c). 利用SparkConf对象来初始化SparkContext
val sc = new SparkContext(conf)
d). 创建RDD、并执行相应的Transformation和action并得到最终结果。
e). 关闭Context
在完成应用的设计和编写后,使用spark-submit来提交应用的jar包。spark-submit的命令行参考如下:
Submitting Applications
./bin/spark-submit \ --class --master \ --deploy-mode \ ... # other options \ [application-arguments]
Spark的运行模式取决于传递给SparkContext的MASTER环境变量的值。master URL可以是以下任一种形式:
Master URL 含义
local 使用一个Worker线程本地化运行SPARK(完全不并行)
local[*] 使用逻辑CPU个数数量的线程来本地化运行Spark
local[K] 使用K个Worker线程本地化运行Spark(理想情况下,K应该根据运行机器的CPU核数设定)
spark://HOST:PORT 连接到指定的Spark standalone master。默认端口是7077.
yarn-client 以客户端模式连接YARN集群。集群的位置可以在HADOOP_CONF_DIR 环境变量中找到。
yarn-cluster 以集群模式连接YARN集群。集群的位置可以在HADOOP_CONF_DIR 环境变量中找到。
mesos://HOST:PORT 连接到指定的Mesos集群。默认接口是5050.
而spark-shell会在启动的时候自动构建SparkContext,名称为sc。
3. RDD的创造
Spark所有的操作都围绕弹性分布式数据集(RDD)进行,这是一个有容错机制并可以被并行操作的元素集合,具有只读、分区、容错、高效、无需物化、可以缓存、RDD依赖等特征。目前有两种类型的基础RDD:并行集合(Parallelized Collections):接收一个已经存在的Scala集合,然后进行各种并行计算。 Hadoop数据集(Hadoop Datasets):在一个文件的每条记录上运行函数。只要文件系统是HDFS,或者hadoop支持的任意存储系统即可。 这两种类型的RDD都可以通过相同的方式进行操作,从而获得子RDD等一系列拓展,形成lineage血统关系图。
(1). 并行化集合
并行化集合是通过调用SparkContext的parallelize方法,在一个已经存在的Scala集合上创建的(一个Seq对象)。集合的对象将会被拷贝,创建出一个可以被并行操作的分布式数据集。例如,下面的解释器输出,演示了如何从一个数组创建一个并行集合。
例如:val rdd = sc.parallelize(Array(1 to 10))
根据能启动的executor的数量来进行切分多个slice,每一个slice启动一个Task来进行处理。
val rdd = sc.parallelize(Array(1 to 10), 5)
指定了partition的数量
(2). Hadoop数据集
Spark可以将任何Hadoop所支持的存储资源转化成RDD,如本地文件(需要网络文件系统,所有的节点都必须能访问到)、HDFS、Cassandra、HBase、Amazon S3等,Spark支持文本文件、SequenceFiles和任何Hadoop InputFormat格式。
a). 使用textFile()方法可以将本地文件或HDFS文件转换成RDD
支持整个文件目录读取,文件可以是文本或者压缩文件(如gzip等,自动执行解压缩并加载数据)。如textFile(”file:///dfs/data”)
支持通配符读取,例如:
val rdd1 = sc.textFile("file:///root/access_log/access_log*.filter"); val rdd2=rdd1.map(_.split("\t")).filter(_.length==6) rdd2.count() ...... 14/08/20 14:44:48 INFO HadoopRDD: Input split: file:/root/access_log/access_log.20080611.decode.filter:134217728+20705903 ......
textFile()可选第二个参数slice,默认情况下为每一个block分配一个slice。用户也可以通过slice指定更多的分片,但不能使用少于HDFS block的分片数。
b). 使用wholeTextFiles()读取目录里面的小文件,返回(用户名、内容)对
c). 使用sequenceFile[K,V]()方法可以将SequenceFile转换成RDD。SequenceFile文件是Hadoop用来存储二进制形式的key-value对而设计的一种平面文件(Flat File)。
d). 使用SparkContext.hadoopRDD方法可以将其他任何Hadoop输入类型转化成RDD使用方法。一般来说,HadoopRDD中每一个HDFS block都成为一个RDD分区。
此外,通过Transformation可以将HadoopRDD等转换成FilterRDD(依赖一个父RDD产生)和JoinedRDD(依赖所有父RDD)等。
4. RDD操作
RDD支持两类操作:
转换(transformation)现有的RDD通关转换生成一个新的RDD,转换是延时执行(lazy)的。
动作(actions)在RDD上运行计算后,返回结果给驱动程序或写入文件系统。
例如,map就是一种transformation,它将数据集每一个元素都传递给函数,并返回一个新的分布数据集表示结果。reduce则是一种action,通过一些函数将所有的元素叠加起来,并将最终结果返回给Driver程序。
— Transformations —
(1). map(func)
Return a new distributed dataset formed by passing each element of the source through a function func.
返回一个新分布式数据集,由每一个输入元素经过func函数转换后组成
(2). filter(func)
Return a new dataset formed by selecting those elements of the source on which func returns true.
返回一个新数据集,由经过func函数计算后返回值为true的输入元素组成
Test:
val num=sc.parallelize(1 to 100) val num2 = num.map(_*2) val num3 = num2.filter(_ % 3 == 0) ...... num3.collect //res: Array[Int] = Array(6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138, 144, 150, 156, 162, 168, 174, 180, 186, 192, 198) num3.toDebugString //res5: String = //FilteredRDD[20] at filter at :16 (48 partitions) // MappedRDD[19] at map at :14 (48 partitions) // ParallelCollectionRDD[18] at parallelize at :12 (48 partitions)
(3). flatMap(func)
Similar to map, but each input item can be mapped to 0 or more output items (so func should return a Seq rather than a single item).
类似于map,但是每一个输入元素可以被映射为0或多个输出元素(因此func应该返回一个序列,而不是单一元素)
Test:
val kv=sc.parallelize(List(List(1,2),List(3,4),List(3,6,8))) kv.flatMap(x=>x.map(_+1)).collect //res0: Array[Int] = Array(2, 3, 4, 5, 4, 7, 9) //Word Count sc.textFile('hdfs://hdp01:9000/home/debugo/*.txt').flatMap(_.split(' ')).map((_,1)).reduceByKey(_+_)
(4). mapPartitions(func)
Similar to map, but runs separately on each partition (block) of the RDD, so func must be of type Iterator
类似于map,但独立地在RDD的每一个分块上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U]。mapPartitions将会被每一个数据集分区调用一次。各个数据集分区的全部内容将作为顺序的数据流传入函数func的参数中,func必须返回另一个Iterator[T]。被合并的结果自动转换成为新的RDD。下面的测试中,元组(3,4)和(6,7)将由于我们选择的分区策略和方法而消失。
The combined result iterators are automatically converted into a new RDD. Please note, that the tuples (3,4) and (6,7) are missing from the following result due to the partitioning we chose
Test:
val nums = sc . parallelize (1 to 9 , 3) def myfunc[T] ( iter : Iterator [T] ) : Iterator [( T , T ) ] = { var res = List [(T , T) ]() var pre = iter.next while ( iter.hasNext ) { val cur = iter . next ; res .::= ( pre , cur ) pre = cur ; } res . iterator } //myfunc: [T](iter: Iterator[T])Iterator[(T, T)] nums.mapPartitions(myfunc).collect //res12: Array[(Int, Int)] = Array((2,3), (1,2), (5,6), (4,5), (8,9), (7,8))
(5). mapPartitionsWithIndex(func)
Similar to mapPartitions, but also provides func with an integer value representing the index of the partition, so func must be of type (Int, Iterator
类似于mapPartitions, 其函数原型是:
def mapPartitionsWithIndex [ U : ClassTag ]( f : ( Int , Iterator [ T ]) => Iterator [ U ] , preservesPartitioning : Boolean = false ) : RDD [ U ],
mapPartitionsWithIndex的func接受两个参数,第一个参数是分区的索引,第二个是一个数据集分区的迭代器。而输出的是一个包含经过该函数转换的迭代器。下面测试中,将分区索引和分区数据一起输出。
Test:
val x = sc . parallelize ( List (1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10) , 3) def myfunc ( index : Int , iter : Iterator [ Int ]) : Iterator [ String ] = { iter . toList . map ( x => index + "-" + x ) . iterator } //myfunc: (index: Int, iter: Iterator[Int])Iterator[String] x . mapPartitionsWithIndex ( myfunc ) . collect() res: Array[String] = Array(0-1, 0-2, 0-3, 1-4, 1-5, 1-6, 2-7, 2-8, 2-9, 2-10)
(6). sample(withReplacement,fraction, seed)
Sample a fraction fraction of the data, with or without replacement, using a given random number generator seed.
根据fraction指定的比例,对数据进行采样,可以选择是否用随机数进行替换,seed用于指定随机数生成器种子。
val a = sc . parallelize (1 to 10000 , 3) a . sample ( false , 0.1 , 0) . count res0 : Long = 960 a . sample ( true , 0.7 , scala.util.Random.nextInt(10000)) . count res1: Long = 7073
(7). union(otherDataset)
Return a new dataset that contains the union of the elements in the source dataset and the argument.
返回一个新的数据集,新数据集是由源数据集和参数数据集联合而成。
(8). intersection(otherDataset)
Return a new RDD that contains the intersection of elements in the source dataset and the argument.
(9). distinct([numTasks]))
Return a new dataset that contains the distinct elements of the source dataset.
返回一个包含源数据集中所有不重复元素的新数据集
Test:
val kv1=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5))) val kv2=sc.parallelize(List(("A",4),("A", 2),("C",3),("A",4),("B",5))) kv2.distinct.collect res0: Array[(String, Int)] = Array((A,4), (C,3), (B,5), (A,2)) kv1.union(kv2).collect res1: Array[(String, Int)] = Array((A,1), (B,2), (C,3), (A,4), (B,5), (A,4), (A,2), (C,3), (A,4), (B,5)) kv1.union(kv2).collect.distinct res2: Array[(String, Int)] = Array((A,1), (B,2), (C,3), (A,4), (B,5), (A,2)) kv1.intersection(kv2).collect res43: Array[(String, Int)] = Array((A,4), (C,3), (B,5))
(10.)groupByKey([numTasks])
When called on a dataset of (K, V) pairs, returns a dataset of (K, Iterable
Note: If you are grouping in order to perform an aggregation (such as a sum or average) over each key, using reduceByKey or combineByKey will yield much better performance.
Note: By default, the level of parallelism in the output depends on the number of partitions of the parent RDD. You can pass an optional numTasks argument to set a different number of tasks.
在一个(K,V)对的数据集上调用,返回一个(K,Seq[V])对的数据集
注意:默认情况下,只有8个并行任务来做操作,但是你可以传入一个可选的numTasks参数来改变它。如果分组是用来计算聚合操作(如sum或average),那么应该使用reduceByKey 或combineByKey 来提供更好的性能。
groupByKey, reduceByKey等transformation操作涉及到了shuffle操作,所以这里引出两个概念宽依赖和窄依赖。
窄依赖(narrow dependencies)
子RDD的每个分区依赖于常数个父分区(与数据规模无关)
输入输出一对一的算子,且结果RDD的分区结构不变。主要是map/flatmap
输入输出一对一的算子,但结果RDD的分区结构发生了变化,如union/coalesce
从输入中选择部分元素的算子,如filter、distinct、substract、sample
宽依赖(wide dependencies)
子RDD的每个分区依赖于所有的父RDD分区
对单个RDD基于key进行重组和reduce,如groupByKey,reduceByKey
对两个RDD基于key进行join和重组,如join
经过大量shuffle生成的RDD,建议进行缓存。这样避免失败后重新计算带来的开销。
注意:reduce是一个action,和reduceByKey完全不同。
(11).reduceByKey(func, [numTasks])
When called on a dataset of (K, V) pairs, returns a dataset of (K, V) pairs where the values for each key are aggregated using the given reduce function func, which must be of type (V,V) => V. Like ingroupByKey, the number of reduce tasks is configurable through an optional second argument.
在一个(K,V)对的数据集上调用时,返回一个(K,V)对的数据集,使用指定的reduce函数,将相同key的值聚合到一起。类似groupByKey,reduce任务个数是可以通过第二个可选参数来配置的
(12).sortByKey([ascending], [numTasks])
When called on a dataset of (K, V) pairs where K implements Ordered, returns a dataset of (K, V) pairs sorted by keys in ascending or descending order, as specified in the boolean ascending argument.
在一个(K,V)对的数据集上调用,K必须实现Ordered接口,返回一个按照Key进行排序的(K,V)对数据集。升序或降序由ascending布尔参数决定
Test:
val kv1=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5))) res0: Array[(String, Int)] = Array((A,1), (A,4), (B,2), (B,5), (C,3)) kv1.sortByKey().collect //注意sortByKey的小括号不能省 res1: Array[(String, Int)] = Array((A,1), (A,4), (B,2), (B,5), (C,3)) kv1.groupByKey().collect res1: Array[(String, Iterable[Int])] = Array((A,ArrayBuffer(1, 4)), (B,ArrayBuffer(2, 5)), (C,ArrayBuffer(3))) kv1.reduceByKey(_+_).collect res2: Array[(String, Int)] = Array((A,5), (B,7), (C,3))
(13). join(otherDataset, [numTasks])
When called on datasets of type (K, V) and (K, W), returns a dataset of (K, (V, W)) pairs with all pairs of elements for each key. Outer joins are also supported through leftOuterJoin and rightOuterJoin.
在类型为(K,V)和(K,W)类型的数据集上调用时,返回一个相同key对应的所有元素对在一起的(K, (V, W))数据集
(14).cogroup(otherDataset, [numTasks])
When called on datasets of type (K, V) and (K, W), returns a dataset of (K, Iterable
在类型为(K,V)和(K,W)的数据集上调用,返回一个 (K, Seq[V], Seq[W])元组的数据集。这个操作也可以称之为groupwith
Test:
val kv1=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5))) val kv3=sc.parallelize(List(("A",10),("B",20),("D",30))) kv1.join(kv3).collect res16: Array[(String, (Int, Int))] = Array((A,(1,10)), (A,(4,10)), (B,(2,20)), (B,(5,20))) kv1.cogroup(kv3).collect res0: Array[(String, (Iterable[Int], Iterable[Int]))] = Array((A,(ArrayBuffer(1, 4),ArrayBuffer(10))), (B,(ArrayBuffer(2, 5),ArrayBuffer(20))), (C,(ArrayBuffer(3),ArrayBuffer())), (D,(ArrayBuffer(),ArrayBuffer(30))))
(15).cartesian(otherDataset)
When called on datasets of types T and U, returns a dataset of (T, U) pairs (all pairs of elements).
笛卡尔积,在类型为 T 和 U 类型的数据集上调用时,返回一个 (T, U)对数据集(两两的元素对)
(16). pipe(command, [envVars])
Pipe each partition of the RDD through a shell command, e.g. a Perl or bash script. RDD elements are written to the process’s stdin and lines output to its stdout are returned as an RDD of strings.
通过POSIX 管道来将每个RDD分区的数据传入一个shell命令(例如Perl或bash脚本)。RDD元素会写入到进程的标准输入,其标准输出会作为RDD字符串返回。
(17).coalesce(numPartitions)
Decrease the number of partitions in the RDD to numPartitions. Useful for running operations more efficiently after filtering down a large dataset.
将RDD分区的数量降低为numPartitions,对于经过过滤后的大数据集的在线处理更加有效。
(18).repartition(numPartitions)
Reshuffle the data in the RDD randomly to create either more or fewer partitions and balance it across them. This always shuffles all data over the network.
随机重新shuffle RDD中的数据,并创建numPartitions个分区。这个操作总会通过网络来shuffle全部数据。
— Actions —
(19). reduce(func)
Aggregate the elements of the dataset using a function func (which takes two arguments and returns one). The function should be commutative and associative so that it can be computed correctly in parallel.
通过函数func(接受两个参数,返回一个参数)聚集数据集中的所有元素。这个功能必须可交换且可关联的,从而可以正确的被并行执行。
(20). collect()
Return all the elements of the dataset as an array at the driver program. This is usually useful after a filter or other operation that returns a sufficiently small subset of the data.
在驱动程序中,以数组的形式,返回数据集的所有元素。这通常会在使用filter或者其它操作并返回一个足够小的数据子集后再使用会比较有用。
(21). count()
Return the number of elements in the dataset.
返回数据集的元素的个数。
(22). first()
Return the first element of the dataset (similar to take(1)).
返回数据集的第一个元素(类似于take(1))
(23). take(n)
Return an array with the first n elements of the dataset. Note that this is currently not executed in parallel. Instead, the driver program computes all the elements.
返回一个由数据集的前n个元素组成的数组。注意,这个操作目前并非并行执行,而是由驱动程序计算所有的元素
(24). countByKey()
Only available on RDDs of type (K, V). Returns a hashmap of (K, Int) pairs with the count of each key.
对(K,V)类型的RDD有效,返回一个(K,Int)对的Map,表示每一个key对应的元素个数
(25). foreach(func)
Run a function func on each element of the dataset. This is usually done for side effects such as updating an accumulator variable (see below) or interacting with external storage systems.
在数据集的每一个元素上,运行函数func进行更新。这通常用于边缘效果,例如更新一个累加器,或者和外部存储系统进行交互,例如HBase.
Test:
val num=sc.parallelize(1 to 10) num.reduce (_ + _) res1: Int = 55 num.take(5) res2: Array[Int] = Array(1, 2, 3, 4, 5) num.first res3: Int = 1 num.count res4: Long = 10 num.take(5).foreach(println) 1 2 3 4 5 val kv1=sc.parallelize(List(("A",1),("B",2),("C",3),("A",4),("B",5),("A",7),("B",7))) val kv1_count=kv1.countByKey() kv1_count: scala.collection.Map[String,Long] = Map(A -> 3, C -> 1, B -> 3)
(26). takeSample(withReplacement,num, seed)
Return an array with a random sample of num elements of the dataset, with or without replacement, using the given random number generator seed.
返回一个数组,在数据集中随机采样num个元素组成,可以选择是否用随机数替换不足的部分,Seed用于指定的随机数生成器种子
(27). takeOrdered(n, [ordering])
Return the first n elements of the RDD using either their natural order or a custom comparator.
返回一个由数据集的前n个元素组成的有序数组,使用自然序或自定义的比较器。
(28). saveAsTextFile(path)
Write the elements of the dataset as a text file (or set of text files) in a given directory in the local filesystem, HDFS or any other Hadoop-supported file system. Spark will call toString on each element to convert it to a line of text in the file.
将数据集的元素,以textfile的形式,保存到本地文件系统,HDFS或者任何其它hadoop支持的文件系统。对于每个元素,Spark将会调用toString方法,将它转换为文件中的文本行
(29). saveAsSequenceFile(path)
Write the elements of the dataset as a Hadoop SequenceFile in a given path in the local filesystem, HDFS or any other Hadoop-supported file system. This is available on RDDs of key-value pairs that either implement Hadoop’s Writable interface. In Scala, it is also available on types that are implicitly convertible to Writable (Spark includes conversions for basic types like Int, Double, String, etc).
将数据集的元素,以Hadoop sequencefile的格式,保存到指定的目录下,本地系统,HDFS或者任何其它hadoop支持的文件系统。这个只限于由key-value对组成,并实现了Hadoop的Writable接口,或者隐式的可以转换为Writable的RDD。(Spark包括了基本类型的转换,例如Int,Double,String,等等)
(30). saveAsObjectFile(path)
Write the elements of the dataset in a simple format using Java serialization, which can then be loaded usingSparkContext.objectFile().
将数据集元素写入Java序列化的可以被SparkContext.objectFile()加载的简单格式中。
当然,transformation和action的操作远远不止这些。其他请参考API文档:
RDD API
5. RDD缓存
Spark可以使用 persist 和 cache 方法将任意 RDD 缓存到内存、磁盘文件系统中。缓存是容错的,如果一个 RDD 分片丢失,可以通过构建它的 transformation自动重构。被缓存的 RDD 被使用的时,存取速度会被大大加速。一般的executor内存60%做 cache, 剩下的40%做task。
Spark中,RDD类可以使用cache() 和 persist() 方法来缓存。cache()是persist()的特例,将该RDD缓存到内存中。而persist可以指定一个StorageLevel。StorageLevel的列表可以在StorageLevel 伴生单例对象中找到:
object StorageLevel { val NONE = new StorageLevel(false, false, false, false) val DISK_ONLY = new StorageLevel(true, false, false, false) val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2) val MEMORY_ONLY = new StorageLevel(false, true, false, true) val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2) val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false) val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2) val MEMORY_AND_DISK = new StorageLevel(true, true, false, true) val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2) val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false) val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2) val OFF_HEAP = new StorageLevel(false, false, true, false) // Tachyon } // 其中,StorageLevel 类的构造器参数如下: class StorageLevel private( private var useDisk_ : Boolean, private var useMemory_ : Boolean, private var useOffHeap_ : Boolean, private var deserialized_ : Boolean, private var replication_ : Int = 1)
Spark的不同StorageLevel ,目的满足内存使用和CPU效率权衡上的不同需求。我们建议通过以下的步骤来进行选择:
·如果你的RDDs可以很好的与默认的存储级别(MEMORY_ONLY)契合,就不需要做任何修改了。这已经是CPU使用效率最高的选项,它使得RDDs的操作尽可能的快。
·如果不行,试着使用MEMORY_ONLY_SER并且选择一个快速序列化的库使得对象在有比较高的空间使用率的情况下,依然可以较快被访问。
·尽可能不要存储到硬盘上,除非计算数据集的函数,计算量特别大,或者它们过滤了大量的数据。否则,重新计算一个分区的速度,和与从硬盘中读取基本差不多快。
·如果你想有快速故障恢复能力,使用复制存储级别(例如:用Spark来响应web应用的请求)。所有的存储级别都有通过重新计算丢失数据恢复错误的容错机制,但是复制存储级别可以让你在RDD上持续的运行任务,而不需要等待丢失的分区被重新计算。
·如果你想要定义你自己的存储级别(比如复制因子为3而不是2),可以使用StorageLevel 单例对象的apply()方法。
在不会使用cached RDD的时候,及时使用unpersist方法来释放它。
6. RDD的共享变量
在应用开发中,一个函数被传递给Spark操作(例如map和reduce),在一个远程集群上运行,它实际上操作的是这个函数用到的所有变量的独立拷贝。这些变量会被拷贝到每一台机器。通常看来,在任务之间中,读写共享变量显然不够高效。然而,Spark还是为两种常见的使用模式,提供了两种有限的共享变量:广播变量和累加器。
(1). 广播变量(Broadcast Variables)
– 广播变量缓存到各个节点的内存中,而不是每个 Task
– 广播变量被创建后,能在集群中运行的任何函数调用
– 广播变量是只读的,不能在被广播后修改
– 对于大数据集的广播, Spark 尝试使用高效的广播算法来降低通信成本
使用方法:
val broadcastVar = sc.broadcast(Array(1, 2, 3))
(2). 累加器
累加器只支持加法操作,可以高效地并行,用于实现计数器和变量求和。Spark 原生支持数值类型和标准可变集合的计数器,但用户可以添加新的类型。只有驱动程序才能获取累加器的值
使用方法:
val accum = sc.accumulator(0) sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum + = x) accum.value val num=sc.parallelize(1 to 100)
参考
:
http://spark.apache.org/docs/latest/programming-guide.html
http://www.oschina.net/translate/spark-configuration
http://shiyanjun.cn/archives/744.html
《Apache Spark API By Example》
原文地址:Spark中的编程模型, 感谢原作者分享。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

이달 초 MIT와 기타 기관의 연구자들은 MLP에 대한 매우 유망한 대안인 KAN을 제안했습니다. KAN은 정확성과 해석성 측면에서 MLP보다 뛰어납니다. 그리고 매우 적은 수의 매개변수로 더 많은 수의 매개변수를 사용하여 실행되는 MLP보다 성능이 뛰어날 수 있습니다. 예를 들어 저자는 KAN을 사용하여 더 작은 네트워크와 더 높은 수준의 자동화로 DeepMind의 결과를 재현했다고 밝혔습니다. 구체적으로 DeepMind의 MLP에는 약 300,000개의 매개변수가 있는 반면 KAN에는 약 200개의 매개변수만 있습니다. KAN은 MLP와 같이 강력한 수학적 기반을 가지고 있으며, KAN은 Kolmogorov-Arnold 표현 정리를 기반으로 합니다. 아래 그림과 같이 KAN은

대규모 언어 모델(LLM)을 인간의 가치와 의도에 맞추려면 인간의 피드백을 학습하여 유용하고 정직하며 무해한지 확인하는 것이 중요합니다. LLM 정렬 측면에서 효과적인 방법은 인간 피드백 기반 강화 학습(RLHF)입니다. RLHF 방법의 결과는 훌륭하지만 몇 가지 최적화 문제가 있습니다. 여기에는 보상 모델을 훈련한 다음 해당 보상을 극대화하기 위해 정책 모델을 최적화하는 것이 포함됩니다. 최근 일부 연구자들은 더 간단한 오프라인 알고리즘을 탐구했는데, 그 중 하나가 직접 선호 최적화(DPO)입니다. DPO는 RLHF의 보상 기능을 매개변수화하여 선호도 데이터를 기반으로 직접 정책 모델을 학습하므로 명시적인 보상 모델이 필요하지 않습니다. 이 방법은 간단하고 안정적입니다.

소프트웨어 기술의 선두에 있는 UIUC Zhang Lingming 그룹은 BigCode 조직의 연구원들과 함께 최근 StarCoder2-15B-Instruct 대규모 코드 모델을 발표했습니다. 이 혁신적인 성과는 코드 생성 작업에서 획기적인 발전을 이루었으며 CodeLlama-70B-Instruct를 성공적으로 능가하고 코드 생성 성능 목록의 최상위에 올랐습니다. StarCoder2-15B-Instruct의 독창성은 순수한 자체 정렬 전략에 있습니다. 전체 훈련 프로세스는 개방적이고 투명하며 완전히 자율적이고 제어 가능합니다. 이 모델은 값비싼 수동 주석에 의존하지 않고 StarCoder-15B 기본 모델을 미세 조정한 것에 대한 응답으로 StarCoder2-15B를 통해 수천 개의 명령을 생성합니다.

1. 소개 지난 몇 년 동안 YOLO는 계산 비용과 감지 성능 간의 효과적인 균형으로 인해 실시간 객체 감지 분야에서 지배적인 패러다임이 되었습니다. 연구원들은 YOLO의 아키텍처 설계, 최적화 목표, 데이터 확장 전략 등을 탐색하여 상당한 진전을 이루었습니다. 동시에 사후 처리를 위해 NMS(비최대 억제)에 의존하면 YOLO의 엔드투엔드 배포가 방해되고 추론 대기 시간에 부정적인 영향을 미칩니다. YOLO에서는 다양한 구성 요소의 설계에 포괄적이고 철저한 검사가 부족하여 상당한 계산 중복이 발생하고 모델 기능이 제한됩니다. 이는 최적이 아닌 효율성을 제공하며 성능 향상을 위한 상대적으로 큰 잠재력을 제공합니다. 이 작업의 목표는 사후 처리와 모델 아키텍처 모두에서 YOLO의 성능 효율성 경계를 더욱 향상시키는 것입니다. 이를 위해

OpenAI를 겨냥한 프랑스 AI 유니콘 MistralAI가 새로운 행보를 보였습니다. 최초의 대형 코드 모델인 Codestral이 탄생했습니다. 코드 생성 작업을 위해 특별히 설계된 개방형 생성 AI 모델인 Codestral은 지침 및 완성 API 엔드포인트를 공유하여 개발자가 코드를 작성하고 코드와 상호 작용할 수 있도록 돕습니다. Codestral의 코딩 및 영어 능력을 통해 소프트웨어 개발자는 고급 AI 애플리케이션을 설계할 수 있습니다. Codestral의 매개변수 크기는 22B이며 새로운 MistralAINon-ProductionLicense를 준수하며 연구 및 테스트 목적으로 사용할 수 있지만 상업적인 사용은 금지됩니다. 현재 해당 모델은 HuggingFace에서 다운로드 가능합니다. 다운로드 링크

스탠포드 리 페이페이(Stanford Li Feifei)는 창업 후 처음으로 신개념 '공간지능'을 공개했다. 이는 그녀의 기업가적 방향일 뿐만 아니라 그녀를 이끄는 '북극성'이기도 하다. 그녀는 이를 '인공지능 문제를 해결하는 핵심 퍼즐 조각'이라고 생각한다. 시각화는 통찰력으로 이어지고, 보는 것은 이해로 이어지고, 이해는 행동으로 이어진다. 리페이페이(Li Feifei)의 15분간 TED 강연을 바탕으로 수억 년 전 생명진화의 기원부터 인간이 자연이 부여한 것에 만족하지 못하고 인공지능을 발전시키는 방법, 생명체를 만드는 방법까지 낱낱이 공개합니다. 다음 단계는 공간 지능입니다. 9년 전, Li Feifei는 같은 무대에서 새롭게 탄생한 ImageNet을 세상에 소개했습니다. 이는 이번 딥 러닝 폭발의 출발점 중 하나입니다. 그녀 자신도 네티즌들에게 “두 영상을 모두 보시면 지난 10년간의 컴퓨터 비전을 이해할 수 있을 것”이라고 격려하기도 했다.

표적 탐지 시스템의 벤치마크 YOLO 시리즈가 다시 한 번 대대적인 업그레이드를 받았습니다. 올해 2월 YOLOv9이 출시된 이후 YOLO(YouOnlyLookOnce) 시리즈의 지휘봉은 칭화대학교 연구진의 손에 넘어갔다. 지난 주말 YOLOv10 출시 소식이 AI 커뮤니티의 관심을 끌었다. 컴퓨터 비전 분야의 획기적인 프레임워크로 간주되며 실시간 엔드투엔드 개체 감지 기능으로 유명하며 효율성과 정확성을 결합한 강력한 솔루션을 제공함으로써 YOLO 시리즈의 유산을 이어갑니다. 논문 주소: https://arxiv.org/pdf/2405.14458 프로젝트 주소: https://github.com/THU-MIG/yo

올해 2월 Google은 엔지니어링 및 인프라 최적화, MoE 아키텍처 및 기타 전략을 통해 성능과 속도를 크게 향상시킨 다중 모드 대형 모델 Gemini 1.5를 출시했습니다. 더 긴 컨텍스트, 더 강력한 추론 기능, 교차 모달 콘텐츠 처리 능력이 향상되었습니다. 이번 금요일에 Google DeepMind는 Flash 버전과 기타 최신 업그레이드를 다루는 Gemini 1.5의 기술 보고서를 공식적으로 발표했습니다. 이 문서의 길이는 153페이지입니다. 기술 보고서 링크: https://storage.googleapis.com/deepmind-media/gemini/gemini_v1_5_report.pdf 이 보고서에서 Google은 Gemini1을 소개합니다.
