Bản đồ / Rút gọn là gì?


84

Tôi nghe nói nhiều về bản đồ / thu nhỏ, đặc biệt là trong bối cảnh hệ thống máy tính song song khổng lồ của Google. Chính xác thì nó là gì?


3
@Rinat: Tuy nhiên, đây vẫn là một câu hỏi hay.
Bill Karwin 23/12/08

3
Chắc chắn tôi có thể và đã làm với Google điều này; nhưng (a) SO có mục đích phát triển để có câu trả lời cho tất cả các câu hỏi quan trọng (thậm chí chúng tôi được khuyến khích đăng các câu hỏi mà chúng tôi đã có câu trả lời) và (b) Tôi muốn cộng đồng này đảm nhận.
Lawrence Dol 23/12/08

Câu trả lời:


69

Từ phần tóm tắt của trang xuất bản nghiên cứu MapReduce của Google :

MapReduce là một mô hình lập trình và một triển khai liên kết để xử lý và tạo các tập dữ liệu lớn. Người dùng chỉ định một hàm bản đồ xử lý một cặp khóa / giá trị để tạo ra một tập hợp các cặp khóa / giá trị trung gian và một hàm giảm kết hợp tất cả các giá trị trung gian được liên kết với cùng một khóa trung gian.

Ưu điểm của MapReduce là việc xử lý có thể được thực hiện song song trên nhiều nút xử lý (nhiều máy chủ) nên nó là một hệ thống có thể mở rộng quy mô rất tốt.

Vì nó dựa trên mô hình lập trình chức năng , các bước mapreducemỗi bước không có bất kỳ tác dụng phụ nào (trạng thái và kết quả từ mỗi phần con củamap quy trình không phụ thuộc vào quy trình khác), do đó, tập dữ liệu được ánh xạ và thu gọn có thể được tách biệt. qua nhiều nút xử lý.

Ngôn ngữ lập trình của Joel có thể làm được điều này không? phần thảo luận về cách hiểu lập trình chức năng là điều cần thiết trong Google để tạo ra MapReduce, hỗ trợ công cụ tìm kiếm của nó. Đây là một bài đọc rất hay nếu bạn không quen với lập trình chức năng và cách nó cho phép mã có thể mở rộng.

Xem thêm: Wikipedia: MapReduce

Câu hỏi liên quan: Vui lòng giải thích mapreduce một cách đơn giản


3
Giải thích một cách xuất sắc. Và đối với Software Monkey, M / R cực kỳ dễ thực hiện chỉ với bất kỳ thứ gì một khi bạn hiểu về nó và không giới hạn ở các ví dụ được đưa ra ở đây. Có một số cách để bạn hiểu được nó, người ta sẽ nghĩ nó như những người sưu tập và những cái phễu.
Esko 23/12/08


16

Bản đồ là một hàm áp dụng một hàm khác cho tất cả các mục trong danh sách, để tạo ra một danh sách khác với tất cả các giá trị trả về trên đó. (Một cách khác để nói "áp dụng f vào x" là "gọi f, chuyển nó x". Vì vậy, đôi khi nói "áp dụng" thay vì "gọi" nghe hay hơn).

Đây là cách bản đồ có thể được viết bằng C # (nó được gọi Selectvà nằm trong thư viện chuẩn):

public static IEnumerable<R> Select<T, R>(this IEnumerable<T> list, Func<T, R> func)
{
    foreach (T item in list)
        yield return func(item);
}

Vì bạn là một người yêu thích Java, và Joel Spolsky thích nói với DANH SÁCH KHÔNG CÔNG BẰNG DUY NHẤT về việc Java tồi tệ như thế nào (thực ra, anh ấy không nói dối, nó thật tồi tệ, nhưng tôi đang cố gắng thu phục bạn), đây là nỗ lực rất thô bạo của tôi về một phiên bản Java (Tôi không có trình biên dịch Java và tôi mơ hồ nhớ phiên bản Java 1.1!):

// represents a function that takes one arg and returns a result
public interface IFunctor
{
    object invoke(object arg);
}

public static object[] map(object[] list, IFunctor func)
{
    object[] returnValues = new object[list.length];

    for (int n = 0; n < list.length; n++)
        returnValues[n] = func.invoke(list[n]);

    return returnValues;
}

Tôi chắc rằng điều này có thể được cải thiện theo một triệu cách. Nhưng đó là ý tưởng cơ bản.

Giảm là một hàm biến tất cả các mục trên danh sách thành một giá trị duy nhất. Để làm điều này, nó cần được cung cấp một chức năng khác funcbiến hai mục thành một giá trị duy nhất. Nó sẽ hoạt động bằng cách cho hai mục đầu tiên vào func. Sau đó, kết quả của điều đó cùng với mục thứ ba. Sau đó, kết quả của điều đó với mục thứ tư, và cứ tiếp tục như vậy cho đến khi tất cả các mục đã hết và chúng ta chỉ còn lại một giá trị.

Trong C # giảm được gọi là Aggregate và lại nằm trong thư viện chuẩn. Tôi sẽ chuyển thẳng đến phiên bản Java:

// represents a function that takes two args and returns a result
public interface IBinaryFunctor
{
    object invoke(object arg1, object arg2);
}

public static object reduce(object[] list, IBinaryFunctor func)
{
    if (list.length == 0)
        return null; // or throw something?

    if (list.length == 1)
        return list[0]; // just return the only item

    object returnValue = func.invoke(list[0], list[1]);

    for (int n = 1; n < list.length; n++)
        returnValue = func.invoke(returnValue, list[n]);

    return returnValue;
}

Các phiên bản Java này cần bổ sung các số liệu chung, nhưng tôi không biết cách thực hiện điều đó trong Java. Nhưng bạn có thể vượt qua chúng các lớp bên trong ẩn danh để cung cấp các bộ điều khiển:

string[] names = getLotsOfNames();

string commaSeparatedNames = (string)reduce(names, 
   new IBinaryFunctor {
       public object invoke(object arg1, object arg2)
           { return ((string)arg1) + ", " + ((string)arg2); }
   }

Hy vọng rằng thuốc chung sẽ loại bỏ được các phôi. Tương đương với an toàn loại trong C # là:

string commaSeparatedNames = names.Aggregate((a, b) => a + ", " + b);

Tại sao điều này là "mát mẻ"? Những cách đơn giản để chia nhỏ các phép tính lớn hơn thành các phần nhỏ hơn, để chúng có thể ghép lại với nhau theo nhiều cách khác nhau, luôn thú vị. Cách Google áp dụng ý tưởng này là song song hóa, vì cả bản đồ và bản thu gọn đều có thể được chia sẻ trên nhiều máy tính.

Nhưng yêu cầu quan trọng KHÔNG phải là ngôn ngữ của bạn có thể coi các hàm như các giá trị. Bất kỳ ngôn ngữ OO nào cũng có thể làm được điều đó. Yêu cầu thực tế đối với song song hóa là các funcchức năng nhỏ bạn chuyển để ánh xạ và thu gọn không được sử dụng hoặc cập nhật bất kỳ trạng thái nào. Chúng phải trả về một giá trị chỉ phụ thuộc vào (các) đối số được chuyển cho chúng. Nếu không, kết quả sẽ hoàn toàn sai lệch khi bạn cố gắng chạy song song toàn bộ.


2
Nói chung là một câu trả lời tốt, có giá trị +1; Tuy nhiên, không thích jab ở Java - nhưng tôi đã bỏ lỡ các giá trị hàm kể từ khi chuyển sang Java từ C và đồng ý rằng tính khả dụng của chúng đã quá hạn trong Java.
Lawrence Dol 23/12/08

1
Không phải là một lỗi nghiêm trọng đối với Java - nó có ba lỗ hổng đủ để khiến tôi thích C # hơn ngay bây giờ, nhưng C # cũng có một danh sách các lỗi có thể sẽ khiến tôi thích ngôn ngữ khác vào một ngày nào đó.
Daniel Earwicker 23/12/08

Nhân tiện, tôi rất thích nếu ai đó có thể chỉnh sửa các ví dụ để họ sử dụng chung Java, nếu điều đó thực sự có thể. Hoặc nếu bạn không thể chỉnh sửa thì hãy đăng các đoạn trích ở đây và tôi sẽ chỉnh sửa.
Daniel Earwicker

Tôi đã bắt đầu chỉnh sửa, nhưng phương thức map () tạo ra một mảng kiểu trả về; Java không cho phép tạo mảng có kiểu chung chung. Tôi có thể đã thay đổi nó để sử dụng một danh sách (và có thể chuyển nó thành một mảng), nhưng tôi đã hết tham vọng ngay lúc đó.
Michael Myers

1
Cú pháp đóng tương tự như (a, b) => a + "," + b là điều tôi thực sự mong đợi trong Java 7, đặc biệt là với một số công cụ API mới có vẻ như nó sẽ đi vào. Cú pháp đó sẽ đã làm cho những thứ như thế này sạch hơn rất nhiều; quá tệ, nó không giống như nó sẽ xảy ra.
Adam Jaskiewicz

2

Sau khi cảm thấy thất vọng nhất với waffley rất dài hoặc các bài đăng blog mơ hồ rất ngắn, tôi cuối cùng đã phát hiện ra bài báo súc tích chặt chẽ rất tốt này .

Sau đó, tôi tiếp tục và làm cho nó ngắn gọn hơn bằng cách dịch sang Scala, nơi tôi đã cung cấp trường hợp đơn giản nhất trong đó người dùng chỉ cần chỉ định các phần mapreducecủa ứng dụng. Nói một cách chính xác, trong Hadoop / Spark, một mô hình lập trình phức tạp hơn được sử dụng yêu cầu người dùng chỉ định rõ ràng 4 chức năng khác được nêu tại đây: http://en.wikipedia.org/wiki/MapReduce#Dataflow

import scalaz.syntax.id._

trait MapReduceModel {
  type MultiSet[T] = Iterable[T]

  // `map` must be a pure function
  def mapPhase[K1, K2, V1, V2](map: ((K1, V1)) => MultiSet[(K2, V2)])
                              (data: MultiSet[(K1, V1)]): MultiSet[(K2, V2)] = 
    data.flatMap(map)

  def shufflePhase[K2, V2](mappedData: MultiSet[(K2, V2)]): Map[K2, MultiSet[V2]] =
    mappedData.groupBy(_._1).mapValues(_.map(_._2))

  // `reduce` must be a monoid
  def reducePhase[K2, V2, V3](reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)])
                             (shuffledData: Map[K2, MultiSet[V2]]): MultiSet[V3] =
    shuffledData.flatMap(reduce).map(_._2)

  def mapReduce[K1, K2, V1, V2, V3](data: MultiSet[(K1, V1)])
                                   (map: ((K1, V1)) => MultiSet[(K2, V2)])
                                   (reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)]): MultiSet[V3] =
    mapPhase(map)(data) |> shufflePhase |> reducePhase(reduce)
}

// Kinda how MapReduce works in Hadoop and Spark except `.par` would ensure 1 element gets a process/thread on a cluster
// Furthermore, the splitting here won't enforce any kind of balance and is quite unnecessary anyway as one would expect
// it to already be splitted on HDFS - i.e. the filename would constitute K1
// The shuffle phase will also be parallelized, and use the same partition as the map phase.  
abstract class ParMapReduce(mapParNum: Int, reduceParNum: Int) extends MapReduceModel {
  def split[T](splitNum: Int)(data: MultiSet[T]): Set[MultiSet[T]]

  override def mapPhase[K1, K2, V1, V2](map: ((K1, V1)) => MultiSet[(K2, V2)])
                                       (data: MultiSet[(K1, V1)]): MultiSet[(K2, V2)] = {
    val groupedByKey = data.groupBy(_._1).map(_._2)
    groupedByKey.flatMap(split(mapParNum / groupedByKey.size + 1))
    .par.flatMap(_.map(map)).flatten.toList
  }

  override def reducePhase[K2, V2, V3](reduce: ((K2, MultiSet[V2])) => MultiSet[(K2, V3)])
                             (shuffledData: Map[K2, MultiSet[V2]]): MultiSet[V3] =
    shuffledData.map(g => split(reduceParNum / shuffledData.size + 1)(g._2).map((g._1, _)))
    .par.flatMap(_.map(reduce))
    .flatten.map(_._2).toList
}


0

Bản đồ là một phương thức JS gốc có thể được áp dụng cho một mảng. Nó tạo ra một mảng mới do một số hàm được ánh xạ tới mọi phần tử trong mảng ban đầu. Vì vậy, nếu bạn ánh xạ một hàm (phần tử) {return element * 2;}, nó sẽ trả về một mảng mới với mỗi phần tử được nhân đôi. Mảng ban đầu sẽ không được sửa đổi.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Reduce là một phương thức JS gốc cũng có thể được áp dụng cho một mảng. Nó áp dụng một hàm cho một mảng và có giá trị đầu ra ban đầu được gọi là bộ tích lũy. Nó lặp lại qua từng phần tử trong mảng, áp dụng một hàm và giảm chúng xuống một giá trị duy nhất (bắt đầu dưới dạng bộ tích lũy). Nó hữu ích vì bạn có thể có bất kỳ đầu ra nào bạn muốn, bạn chỉ cần bắt đầu với loại bộ tích lũy đó. Vì vậy, nếu tôi muốn giảm bớt thứ gì đó thành một đối tượng, tôi sẽ bắt đầu với một bộ tích lũy {}.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?v=a


0

MapReduce:

Để chạy một cái gì đó lớn, chúng ta có thể sử dụng sức mạnh tính toán của các máy tính khác nhau trong văn phòng của mình. Phần khó khăn là phân chia nhiệm vụ giữa các máy tính khác nhau, do thư viện MapReduce thực hiện.

Ý tưởng cơ bản là bạn chia công việc thành hai phần: Bản đồ và Giảm bớt. Về cơ bản, bản đồ giải quyết vấn đề, chia nó thành các phần con và gửi các phần con đến các máy khác nhau - vì vậy tất cả các phần đều chạy cùng một lúc. Reduce lấy kết quả từ các phần phụ và kết hợp chúng lại với nhau để có một câu trả lời duy nhất.

Đầu vào là danh sách các bản ghi. Kết quả của tính toán bản đồ là danh sách các cặp khóa / giá trị. Giảm lấy từng tập hợp giá trị có cùng một khóa và kết hợp chúng thành một giá trị duy nhất. Bạn không thể biết liệu công việc được chia thành 100 phần hay 2 phần; kết quả cuối cùng trông khá giống kết quả của một bản đồ.

Vui lòng xem bản đồ đơn giản và chương trình rút gọn:

Chức năng bản đồ được sử dụng để áp dụng một số chức năng trong danh sách ban đầu của chúng tôi và một danh sách mới do đó được tạo ra. Hàm map () trong Python nhận một hàm và một danh sách làm đối số. Một danh sách mới được trả về bằng cách áp dụng chức năng cho từng mục trong danh sách.

li = [5, 7, 4, 9] 
final_list = list(map(lambda x: x*x , li)) 
print(final_list)  #[25, 49, 16, 81]

Hàm Reduce () trong Python nhận một hàm và một danh sách làm đối số. Hàm được gọi với một hàm lambda và một danh sách và một kết quả giảm mới được trả về. Thao tác này thực hiện một thao tác lặp lại trên các cặp của danh sách.

#reduce func to find product/sum of list
x=(1,2,3,4)
from functools import reduce
reduce(lambda a,b:a*b ,x) #24
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.