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ì?
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ì?
Câu trả lời:
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 map
và reduce
mỗ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
Nó giải thích tốt hơn những gì tôi có thể. Nó có ích gì không?
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 Select
và 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 func
biế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 func
chứ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ộ.
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 map
và reduce
củ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
}
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
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