Tại sao dữ liệu lớn cần phải có chức năng?


9

Gần đây tôi bắt đầu thực hiện một dự án mới liên quan đến Dữ liệu lớn cho kỳ thực tập của mình. Các nhà quản lý của tôi khuyên bạn nên bắt đầu học lập trình chức năng (Họ rất khuyến khích Scala). Tôi đã có một trải nghiệm khiêm tốn khi sử dụng F #, nhưng tôi không thể thấy được tầm quan trọng của việc sử dụng mô hình lập trình này vì nó đắt tiền trong một số trường hợp.

Dean đã có một cuộc nói chuyện thú vị về chủ đề này và chia sẻ suy nghĩ của mình về lý do tại sao "Dữ liệu lớn" ở đây: http://www.youtube.com/watch?v=DFAdLCqDbLQ Nhưng nó không thuận tiện vì Big Data không có nghĩa chỉ Hadoop.

Như BigData là khái niệm rất mơ hồ. Tôi quên nó một lúc. Tôi đã cố gắng đưa ra một ví dụ đơn giản để so sánh giữa các khía cạnh khác nhau khi chúng ta xử lý dữ liệu, để xem liệu cách thức chức năng có đắt hay không. Nếu lập trình chức năng tốn kém và tiêu tốn bộ nhớ cho dữ liệu nhỏ, tại sao chúng ta cần nó cho Dữ liệu lớn?

Khác xa với các công cụ ưa thích, tôi đã cố gắng xây dựng một giải pháp cho một vấn đề cụ thể và phổ biến bằng ba cách tiếp cận: Cách bắt buộc và cách thức chức năng (đệ quy, sử dụng các bộ sưu tập). Tôi so sánh thời gian và sự phức tạp, để so sánh giữa ba cách tiếp cận.

Tôi đã sử dụng Scala để viết các hàm này vì đây là công cụ tốt nhất để viết thuật toán sử dụng ba mô hình

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))
}

Cách chức năng:

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))(_ + _)
}

Cách đệ quy:

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

Cách bắt buộc:

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
}

Tôi nhận thấy rằng lập trình chức năng là nặng! nó mất nhiều thời gian hơn và tiêu tốn nhiều dung lượng hơn trong bộ nhớ. Tôi bối rối, bất cứ khi nào tôi đọc một bài báo hoặc xem một cuộc nói chuyện, họ nói rằng chúng ta nên sử dụng lập trình chức năng trong khoa học dữ liệu. Đúng, nó dễ dàng và hiệu quả hơn, đặc biệt trong thế giới dữ liệu. nhưng nó cần nhiều thời gian hơn và nhiều không gian bộ nhớ hơn.

Vậy, tại sao chúng ta cần sử dụng lập trình Chức năng trong Dữ liệu lớn? Các thực tiễn tốt nhất để sử dụng lập trình chức năng (Scala) cho Dữ liệu lớn là gì?


5
Lập trình hàm giúp việc song song mã của bạn dễ dàng hơn, do đó, ngay cả khi một thao tác đơn lẻ có thể mất nhiều thời gian hơn để chạy trong một luồng, hiệu suất tổng thể có thể tốt hơn do tính song song.
Giorgio

@Giorgio: Có nhiều mô hình khác nhau như Người mẫu diễn viên để có được hiệu suất tốt nhất để song song. Đừng nghĩ vậy?
dùng3048512

2
Tôi đoán nó đơn giản chỉ vì cách tiếp cận bản đồ / thu nhỏ từ hadoop là một ý tưởng từ lập trình chức năng.
Doc Brown

1
@ user3047512: Ví dụ: Erlang sử dụng mô hình diễn viên và dành cho hầu hết các chức năng.
Giorgio

2
Sự kết nối giữa mốt "dữ liệu lớn" và FP không đơn giản. Trong "Dữ liệu lớn", một cách tiếp cận được gọi là thu nhỏ bản đồ là mốt, do đó, đến lượt nó, đã phần nào lấy cảm hứng từ các đặc điểm lập trình chức năng. Đây là nơi tương tự kết thúc, tôi không thể thấy bất kỳ kết nối nào nữa giữa hai thế giới này.
SK-logic

Câu trả lời:


13

Đây là cách tôi nhìn thấy nó:

  • Chúng ta hãy bỏ qua các từ "dữ liệu lớn" trong một thời gian, vì chúng là một khái niệm khá mơ hồ

  • Bạn đã đề cập đến Hadoop. Hadoop thực hiện 2 điều: cho phép bạn có một loại ổ đĩa "ảo" được phân phối trên nhiều máy, có dự phòng, có thể được truy cập thông qua API của Hadoop như thể nó là một ổ đĩa đơn, đơn nhất. Nó được gọi là HDFS như trong Hệ thống tệp phân tán Hadoop . Một điều khác mà Hadoop làm là cho phép bạn thực hiện các công việc Giảm bản đồ (đó là khung cho Map-Giảm). Nếu chúng tôi kiểm tra trang Wikipedia của MapReduce , chúng tôi sẽ thấy rằng:

MapReduce là một mô hình lập trình để xử lý các tập dữ liệu lớn với thuật toán phân tán, song song trên một cụm.

...

Chương trình MapReduce bao gồm một thủ tục Map () thực hiện lọc và sắp xếp (chẳng hạn như sắp xếp học sinh theo tên thành hàng đợi, một hàng đợi cho mỗi tên) và thủ tục Giảm () thực hiện thao tác tóm tắt (chẳng hạn như đếm số của sinh viên trong mỗi hàng đợi, mang lại tần số tên)

...

'MapReduce' là một khung để xử lý các sự cố song song trên các bộ dữ liệu khổng lồ bằng cách sử dụng một số lượng lớn máy tính

Cũng trên trang này, Hadoop được mô tả là

Hadoop, triển khai MapReduce miễn phí và nguồn mở của Apache.

Bây giờ, Hadoop được viết bằng Java, đây không phải là ngôn ngữ chức năng. Ngoài ra, nếu chúng ta xem trên trang của Hadoop, chúng ta cũng tìm thấy một ví dụ về cách tạo công việc MapReduce trong Java và triển khai nó trong cụm Hadoop .

Đây là một ví dụ Java về công việc MapReduce của Fibonnaci cho Hadoop.

Tôi hy vọng câu trả lời này cho câu hỏi của bạn, cụ thể là BigData, và đặc biệt là công việc MapReduce tạo ra Fibonacci không "cần" để hoạt động, hay bạn có thể triển khai nó bằng ngôn ngữ OO nếu bạn muốn (ví dụ).

Tất nhiên điều đó không có nghĩa là "nhu cầu" của BigData chỉ là OO. Bạn rất có thể sử dụng ngôn ngữ chức năng để triển khai công việc như MapReduce. Ví dụ, bạn có thể sử dụng Scala với Hadoop nếu bạn muốn, thông qua Scalding .

Những điểm khác tôi nghĩ là đáng nói.

Khi thực hiện đệ quy trong Scala, nếu mã của bạn cho phép, Scala sẽ thực hiện tối ưu hóa cuộc gọi đuôi . Tuy nhiên, do JVM không (chưa) hỗ trợ tối ưu hóa cuộc gọi đuôi , Scala đạt được điều này bằng cách thay thế, tại thời điểm biên dịch, các cuộc gọi đệ quy của bạn với mã tương đương với các vòng lặp, như được giải thích ở đây . Điều này về cơ bản có nghĩa là việc thực hiện các tiêu chuẩn mã đệ quy và không đệ quy bằng Scala là vô nghĩa, vì cả hai cuối cùng đều làm điều tương tự trong thời gian chạy.


2
Bạn đưa ra một điểm tuyệt vời về JVM không hỗ trợ tối ưu hóa cuộc gọi đuôi, điều này làm suy yếu các điểm chuẩn được đề xuất bởi OP. Đây là một câu trả lời rất nhiều thông tin, cảm ơn bạn.
maple_shaft

1
Cảm ơn câu trả lời của bạn, Vâng! đuôi gọi tối ưu hóa là một trong những tính năng ẩn của scala. stackoverflow.com/questions/1025181/hidden-features-of-scala/ ,. Một trong những vấn đề của "Dữ liệu lớn" là mọi công ty đang cố gắng xây dựng một công nghệ mới theo cách khác nhau. Nhưng chủ yếu có hai: Hadoop tech và những người khác. Như bạn đã nói, nó chủ quan và liên quan đến các vấn đề tự nó, chúng ta nên chọn mô hình lập trình đúng dựa trên chuyên môn của mình. Ví dụ: Các mô hình Dự đoán thời gian thực không hoạt động tốt trên Nền tảng Hadoop.
dùng3048512

9

Miễn là bạn có thể chạy nó trên một máy duy nhất, đó không phải là "Dữ liệu lớn". Vấn đề ví dụ của bạn là hoàn toàn không phù hợp để chứng minh bất cứ điều gì về nó.

Dữ liệu lớn có nghĩa là kích thước sự cố quá lớn nên việc phân phối xử lý không phải là tối ưu hóa mà là yêu cầu cơ bản. Và lập trình chức năng làm cho việc viết mã phân tán chính xác và hiệu quả dễ dàng hơn nhiều do cấu trúc dữ liệu bất biến và trạng thái không trạng thái.


"Dữ liệu lớn có nghĩa là kích thước sự cố quá lớn nên việc phân phối xử lý không phải là tối ưu hóa mà là yêu cầu cơ bản." - Tôi không hiểu loại vấn đề nào không thể TẤT CẢ được giải quyết bằng một máy và yêu cầu ít nhất N trong đó N> 1 ...
Shivan Dragon

6
@ShivanDragon: Loại vấn đề bao gồm các yêu cầu về hiệu suất hoàn toàn không thể đáp ứng trên một hệ thống. Hoặc khi kích thước dữ liệu lớn đến mức không một hệ thống nào có thể lưu trữ tất cả.
Michael Borgwardt

Tôi xin lỗi, tôi thấy quan điểm của bạn bây giờ. Có đúng không khi nói rằng những gì bạn đang đề cập đến, cụ thể hơn là MapReduce sống dưới sự bảo trợ của BigData?
Shivan Dragon

Cảm ơn bạn đã đóng góp, tôi đồng ý. Có lẽ tôi không thể tìm thấy một ví dụ đơn giản tốt để thể hiện quan điểm của mình. "Dữ liệu lớn" vẫn là cách mà các nhà phát triển sử dụng dữ liệu để giải quyết các vấn đề hàng ngày của chúng tôi khi xem xét định nghĩa 3Vs. Tôi sẽ quên 3V một lúc và nói về khía cạnh rất đơn giản, xử lý Dữ liệu. Nếu chúng ta thấy rằng phân tích dữ liệu theo cách chức năng là tốn kém, tại sao chúng ta nói rằng "Dữ liệu lớn" cần phải có chức năng? Đây là quan điểm của tôi.
dùng3048512

4
@ShivanDragon, ví dụ, LHC đang tạo ra vài gigabyte dữ liệu mỗi giây . Không chắc chắn một máy duy nhất thậm chí có thể xử lý thông lượng như vậy.
SK-logic

4

Tôi không biết scala và do đó tôi không thể nhận xét về cách tiếp cận chức năng của bạn, nhưng mã của bạn có vẻ quá mức cần thiết.

Mặt khác chức năng đệ quy của bạn là không hiệu quả. Bởi vì hàm gọi chính nó hai lần, nên nó có thứ tự 2 ^ n, rất kém hiệu quả. Nếu bạn muốn so sánh ba cách tiếp cận, bạn cần so sánh ba cách thực hiện tối ưu.

Hàm Fibonacci có thể được thực hiện đệ quy với việc gọi hàm chỉ một lần. Chúng ta hãy có một định nghĩa tổng quát hơn:

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

Trường hợp đặc biệt tiêu chuẩn là:

f0 = 0
f1 = 1

Hàm đệ quy chung là:

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);
}

Cảm ơn! Bạn đã đưa ra một điểm tốt, nhưng không có cách nào hiệu quả để làm điều đó theo cách lặp. Đây là một thăm dò rất phổ biến (bộ Fibonacci). và đây là điểm giải quyết vấn đề tương tự bằng ba cách. Bạn có thể đề xuất cách tốt hơn để giải quyết vấn đề này bằng bất kỳ ngôn ngữ lập trình nào không, tôi có thể viết lại bằng cách sử dụng scala và làm các bài kiểm tra tương tự không?
dùng3048512

@ user3047512 Đối với một ngôn ngữ hỗ trợ đệ quy đuôi, bạn có thể viết nó bằng một bộ tích lũy. Ví dụ
to nướng_flakes

Scala cũng hỗ trợ đệ quy đuôi như tính năng ẩn oldf Fashionedsoftware.com/2008/09/27/ trên
dùng3048512

1
@ user3047512 Vì giải pháp đệ quy là một hàm thuần túy (đầu ra chỉ phụ thuộc vào hàm args và không có gì khác ), ghi nhớ là một giải pháp tốt. Nói một cách đơn giản, mỗi khi nó trả về một giá trị, lưu trữ các đối số và dẫn đến hàm băm khóa / giá trị và mỗi khi hàm được chạy, trước tiên hãy nhìn vào đó. Đây là một trong những lợi thế của các hàm thuần túy - một lệnh gọi trong tương lai của hàm này sẽ tìm giá trị băm có sẵn và thực hiện các phép tính bằng 0 , vì chúng tôi biết kết quả sẽ không thay đổi.
Izkata

@ user3047512 Phiên bản lặp cũng trông giống như một hàm thuần túy trong trường hợp này, nhưng điều đó không phải lúc nào cũng đúng - trong một ngôn ngữ chức năng, tôi tin rằng nó được thi hành tốt hơn bởi ngôn ngữ này ...
Izkata

0

Nếu lập trình chức năng tốn kém và tiêu tốn bộ nhớ cho dữ liệu nhỏ, tại sao chúng ta cần nó cho Dữ liệu lớn?

Cụ thể tôi đã có thể thấy một vài ứng dụng trong đó điều này cực kỳ hữu ích. Ví dụ. Thống kê, tức là tính toán một hàm Gaussian khi đang di chuyển với các tham số khác nhau hoặc một bộ tham số để phân tích dữ liệu. Ngoài ra còn có phép nội suy để phân tích số, v.v.

Các thực tiễn tốt nhất để sử dụng lập trình chức năng (Scala) cho Dữ liệu lớn là gì?

Để trả lời về hiệu quả, cũng có các kỹ thuật giúp tăng hiệu quả của bạn trong không gian hoặc thời gian, cụ thể là đệ quy, đệ quy đuôi , kiểu chuyển tiếp , hàm bậc cao , v.v. Một số ngôn ngữ có ưu và nhược điểm của chúng (ví dụ: lười so với háo hức.) một cái gì đó đơn giản như chuỗi Fibonnacci tôi có thể chỉ sử dụng cách bắt buộc mà đôi khi tôi thấy một số đồng nghiệp của mình miễn cưỡng và có thể không thoải mái với lập trình chức năng và do đó mất nhiều thời gian phát triển hơn ... (tôi vẫn thích sử dụng lập trình chức năng khi tôi có thể [các ứng dụng mà tôi phụ trách]) vì tôi thấy nó nhanh, gọn gàng và "dễ đọc" (mặc dù tôi thấy mã này chủ quan).

Wikipedia có một phiên bản "nhanh" của trình tự sợi được đăng. https://en.wikipedia.org/wiki/Feftal_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)
}

Sử dụng luồng / hof

val fibStream:Stream[Int] = 0 #:: 1 #:: (fibStream zip fibStream.tail).map{ t => t._1 + t._2 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.