ビッグデータが機能する必要があるのはなぜですか?


9

最近、インターンシップのためにビッグデータに関連する新しいプロジェクトに取り組み始めました。私のマネージャーは関数型プログラミングの学習を始めることを推奨しました(彼らはScalaを強く推奨しました)。私はF#を使ってささやかな経験をしましたが、このプログラミングパラダイムを使用することの重要性を理解できませんでした。

ディーンはこのトピックについて興味深い話をし、ここで「ビッグデータ」を使用する理由についての彼の考えを共有しました:http : //www.youtube.com/watch?v=DFAdLCqDbLQ しかし、ビッグデータは意味がないので、あまり便利ではありませんでしたHadoopのみ。

BigDataは非常にあいまいな概念なので。しばらく忘れます。私は、データを処理するときにさまざまな側面を比較するための1つの簡単な例を考え出して、機能的な方法が高価であるかどうかを確認しました。関数型プログラミングが小さなデータに対して高価でメモリを消費する場合、なぜビッグデータに関数型プログラミングが必要なのですか?

派手なツールから遠く離れて、私は3つのアプローチを使用して1つの特定の人気のある問題の解決策を構築しようとしました:命令的な方法と機能的な方法(再帰、コレクションの使用)。時間と複雑さを比較して、3つのアプローチを比較しました。

Scalaを使用してこれらの関数を記述しました。3つのパラダイムを使用してアルゴリズムを記述するのに最適なツールだからです。

def main(args: Array[String]) {
    val start = System.currentTimeMillis()
    // Fibonacci_P
    val s = Fibonacci_P(400000000)
    val end = System.currentTimeMillis()
    println("Functional way: \n the Fibonacci sequence whose values do not exceed four million : %d \n Time : %d ".format(s, end - start))
    val start2 = System.currentTimeMillis()

    // Fibonacci_I
    val s2 = Fibonacci_I(40000000 0)
    val end2 = System.currentTimeMillis();
    println("Imperative way: \n the Fibonacci sequence whose values do not exceed four million : %d \n Time : %d ".format(s2, end2 - start2))
}

機能的な方法:

def Fibonacci_P(max: BigInt): BigInt = {
    //http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream
    //lazy val Fibonaccis: Stream[Long] = 0 #:: 1 #:: Fibonaccis.zip(Fibonaccis.tail).map { case (a, b) => a + b }
    lazy val fibs: Stream[BigInt] = BigInt(0)#::BigInt(1)#::fibs.zip(fibs.tail).map {
        n = > n._1 + n._2
    }
    // println(fibs.takeWhile(p => p < max).toList)
    fibs.takeWhile(p = > p < max).foldLeft(BigInt(0))(_ + _)
}

再帰的な方法:

def Fibonacci_R(n: Int): BigInt = n match {
    case 1 | 2 = > 1
    case _ = > Fibonacci_R(n - 1) + Fibonacci_R(n - 2)
}

命令的な方法:

def Fibonacci_I(max: BigInt): BigInt = {
    var first_element: BigInt = 0
    var second_element: BigInt = 1
    var sum: BigInt = 0

    while (second_element < max) {
        sum += second_element

        second_element = first_element + second_element
        first_element = second_element - first_element
    }

    //Return 
    sum
}

関数型プログラミングが重いことに気づきました!時間がかかり、メモリ内のより多くのスペースを消費します。記事を読んだりトークを見たりすると、データサイエンスで関数型プログラミングを使用するべきだと彼らは言っています。確かに、それは特にデータの世界では、より簡単で生産的です。しかし、それはより多くの時間とより多くのメモリ空間を必要とします。

では、なぜビッグデータで関数型プログラミングを使用する必要があるのでしょうか。ビッグデータに関数型プログラミング(Scala)を使用するためのベストプラクティスは何ですか?


5
関数型プログラミングを使用すると、コードの並列化が容易になるため、1つの操作を1つのスレッドで実行するのに時間がかかる場合でも、並列処理により全体的なパフォーマンスが向上する可能性があります。
ジョルジョ

@Giorgio:並列処理で最高のパフォーマンスを得るには、Actor Modelingとしてさまざまなパラダイムがあります。そう思いませんか?
user3047512 2013

2
hadoopからのmap / reduceアプローチが関数型プログラミングからのアイデアであるという単純な理由からだと思います。
Doc Brown、

1
@ user3047512:たとえば、Erlangはアクターモデルを使用し、大部分は機能します。
ジョルジョ

2
「ビッグデータ」の流行とFPの関係はそれほど簡単ではありません。「ビッグデータ」では、いわゆるmap-reduceアプローチがファッショナブルです。これは、関数型プログラミングの考え方に触発されたものです。ここで類似性が終了します。これら2つの世界の間にこれ以上の接続はありません。
SKロジック

回答:


13

これが私の見方です:

  • 「ビッグデータ」という言葉はかなり漠然とした概念なので、しばらくは無視してみましょう。

  • あなたはHadoopについて言及しました。Hadoopは2つのことを行います。単一の単一のドライブであるかのようにHadoopのAPIを介してアクセスできる、冗長性を備えた複数のマシンに分散される一種の「仮想」ドライブを使用できるようにします。これは、Hadoop分散ファイルシステムのようにHDFSと呼ばれます。Hadoopが行うもう1つのことは、Map-Reduceジョブを実行できるようにすることです(これはMap-Reduceのフレームワークです)。MapReduceのWikipediaページをチェックすると、次のことがわかります。

MapReduceは、クラスター上で並列分散アルゴリズムを使用して大きなデータセットを処理するためのプログラミングモデルです。

...

MapReduceプログラムは、フィルタリングと並べ替えを行うMap()プロシージャで構成されます(たとえば、生徒を名でキューに入れ、名前ごとに1つのキューを作成するなど)。要約操作(数値のカウントなど)を実行するReduce()プロシージャで構成されます。各キューの学生の数、名前の頻度を生成)

...

「MapReduce」は、多数のコンピューターを使用して巨大なデータセット全体で並列化可能な問題を処理するためのフレームワークです。

このページでも、Hadoopは次のように説明されています。

Hadoop、ApacheのMapReduceの無料のオープンソース実装。

現在、Hadoopは関数型言語ではないJavaで記述されています。また、Hadoopのページを見ると、JavaでMapReduceジョブを作成してHadoopクラスターにデプロイする方法の例も見つかります

これは、HadoopのFibonnaci MapReduceジョブのJavaの例です。

これがあなたの質問、つまりBigData、特にフィボナッチを作成するMapReduceジョブが機能する必要がないこと、つまり、必要に応じてOO言語で実装できることを願っています。

もちろん、それはBigDataがオブジェクト指向のみである必要があることを意味するものでもありません。関数型言語を使用して、MapReduceのようなジョブを実装できます。たとえば、必要に応じて、Scaldingを介してHadoopでScalaを使用できます。

私が言及する価値があると思う他のポイント。

Scalaで再帰を行う場合、コードで可能であれば、Scalaは末尾呼び出しの最適化を行います。ただし、JVMは(まだ)tail-call-optimizationをサポートしていないためここで説明するように、Scalaはコンパイル時に、再帰呼び出しをループに相当するコードに置き換えることでこれを実現しています。これが基本的に意味することは、Scalaを使用して再帰的コードベンチマークと非再帰的コードベンチマークを実行しても、実行時に両方とも同じことを行うことに意味がないということです。


2
OPによって提案されたベンチマークを損なうテールコール最適化をサポートしていないJVMについて優れた点を説明します。これは非常に有益な答えです、ありがとう。
maple_shaft

1
はい、ありがとうございます。tail-call-optimizationは、隠れたscala機能の1つです。 stackoverflow.com/questions/1025181/hidden-features-of-scala/…。「ビッグデータ」の問題の1つは、すべての企業が異なる方法で新しいテクノロジーを構築しようとしていることです。しかし、主に2つあります。Hadoopテクノロジーとその他です。あなたが言ったように、それは主観的であり、それ自体の問題に関連しているので、私たちは私たちの専門知識に基づいて適切なプログラミングパラダイムを選ぶべきです。例:リアルタイム予測モデルは、Hadoopプラットフォームではうまく機能しません。
user3047512 2013

9

単一のマシンで実行できる限り、それは「ビッグデータ」ではありません。あなたの例の問題はそれについて何かを示すのに完全に不適切です。

ビッグデータとは、問題のサイズが非常に大きいため、処理の分散が最適化ではなく、基本的な要件であることを意味します。また、関数型プログラミングでは、不変のデータ構造とステートレスにより、正確で効率的な分散コードを簡単に作成できます。


「ビッグデータとは、問題のサイズが非常に大きく、処理の分散が最適化ではなく、基本的な要件であることを意味します。」-1台のマシンを使用して解決できない問題の種類を理解できません。少なくともNが必要です。N> 1 ...
Shivan Dragon

6
@ShivanDragon:単一のシステムで満足することはまったく不可能であるパフォーマンス要件を含む種類の問題。または、データサイズが非常に大きいため、1つのシステムですべてを保存することさえできない場合。
Michael Borgwardt

申し訳ありませんが、あなたの意見は今わかりました。あなたが言及しているのは、具体的には、BigDataの傘下にあるMapReduceであると言って間違いありませんか?
Shivan Dragon、

ご意見ありがとうございます。たぶん私は私の見解を示すのに適した簡単な例を見つけることができませんでした。「ビッグデータ」は、開発者が3Vの定義を考慮してデータを使用して日常の問題を解決する方法です。しばらくは3Vを忘れて、データを扱う非常に単純な側面について話します。機能的な方法でデータを分析するのにコストがかかることがわかった場合、なぜ「ビッグデータ」を機能的にする必要があると言えるのでしょうか。これが私のポイントです。
user3047512 2013

4
たとえば、@ ShivanDragonは、LHCが毎秒数ギガバイトのデータを生成しています。1台のマシンでこのようなスループットを処理できるかどうかはわかりません。
SK-logic

4

私はscalaを知らないので、関数的なアプローチについてコメントすることはできませんが、コードは過剰に見えます。

一方、再帰関数は非効率的です。関数はそれ自体を2回呼び出すため、2 ^ nのオーダーになり、非常に非効率的です。3つのアプローチを比較する場合は、3つの最適な実装を比較する必要があります。

フィボナッチ関数は、関数を1回だけ呼び出すことで再帰的に実装できます。より一般的な定義を見てみましょう:

F(0) = f0
F(1) = f1
F(n) = F(n-1) + F(n-2)

標準の特殊なケースは次のとおりです。

f0 = 0
f1 = 1

一般的な再帰関数は次のとおりです。

function fibonacci($f0, $f1, $n){
    if($n < 0 || !isInt($n)) return false;
    if($n = 0) return $f0;
    if($n = 1) return $f1;
    return fibonacci($f1, $f0 + $f1, $n - 1);
}

ありがとう!あなたは良い点を挙げましたが、反復的な方法でそれを行う効率的な方法はありません。これは非常に一般的な問題です(フィボナッチスイート)。これは、3つの方法を使用して同じ問題に取り組むことの要点です。任意のプログラミング言語を使用してこの問題を解決するより良い方法を提案できますか?scalaを使用してそれを書き直して、同じテストを実行できますか?
user3047512 2013

@ user3047512末尾再帰をサポートする言語の場合、アキュムレータを使用してそれを記述できます。
toasted_flakes 2013

Scalaはまた、隠された機能として、末尾再帰をサポートoldfashionedsoftware.com/2008/09/27/...
user3047512

1
@ user3047512再帰的な解決策は純粋な関数(出力は関数の引数にのみ依存し、他には何も依存しない)であるため、メモ化は優れた解決策です。簡単に言えば、値を返すたびに、引数と結果をキー/値のハッシュに格納し、関数が実行されるたびに、まずそこを確認します。これは、純粋な関数の利点の1つです。この関数を後で呼び出すと、既存のハッシュ値が検出され、ゼロ計算が行われます。これは、結果が変更されないことがわかっているためです。
Izkata

@ user3047512反復バージョンもこの場合は純粋な関数のように見えますが、常にそうであるとは限りません-関数型言語では、言語によって強制される方がよいと思います...
Izkata

0

関数型プログラミングが小さなデータに対して高価でメモリを消費する場合、なぜビッグデータに関数型プログラミングが必要なのですか?

具体的には、これが非常に役立ついくつかのアプリケーションをすでに確認できます。例:統計、つまり、さまざまなパラメーターまたはデータ分析用のパラメーターのセットを使用して、その場でガウス関数を計算する。数値解析などの補間もあります。

ビッグデータに関数型プログラミング(Scala)を使用するためのベストプラクティスは何ですか?

効率について答えるために、空間または時間の効率を高めるのに役立つテクニックもあります。具体的には、再帰、末尾再帰継続渡しスタイル高次関数などです。一部の言語には長所と短所があります(遅延と熱心な例)。フィボナッチシーケンスのような単純なものです。同僚の一部が消極的で、関数型プログラミングにあまり慣れていないために開発時間が長くなる場合があるため、命令的な方法を使用するだけかもしれません...可能な場合は関数型プログラミングを使用します(担当しているアプリケーション))。コードがすばやく、きれいで、「読みやすい」(ただし、この主観的であることがわかります)ためです。

ウィキペディアには、フィボナッチ配列の「高速」バージョンが投稿されています。 https://en.wikipedia.org/wiki/Functional_programming#Scala

def fibTailRec(n: Int): Int = {
  @tailrec def f(a: Int, b: Int, c: Int): Int = if (a == 0) 0 else if(a < 2) c else f(a-1, c, b + c)
  f(n, 0, 1)
}

ストリーム/ホフの使用

val fibStream:Stream[Int] = 0 #:: 1 #:: (fibStream zip fibStream.tail).map{ t => t._1 + t._2 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.