Harita / Küçült nedir?


84

Özellikle Google'ın büyük ölçüde paralel hesaplama sistemi bağlamında, eşleme / azaltma hakkında çok şey duyuyorum. Tam olarak nedir?


3
@Rinat: Yine de bu hala iyi bir soru.
Bill Karwin

3
Elbette bunu Google'da yapabilirim ve yaptım; ancak (a) SO, tüm önemli sorulara cevap verecek şekilde büyümeye yöneliktir (cevaplarını zaten bildiğimiz soruları göndermeye bile teşvik ediliriz) ve (b) bu ​​topluluğun bu konuyu ele almasını istedim.
Lawrence Dol

Yanıtlar:


69

Google'ın MapReduce araştırma yayın sayfasının özetinden :

MapReduce bir programlama modeli ve büyük veri kümelerini işlemek ve oluşturmak için ilişkili bir uygulamadır. Kullanıcılar, bir dizi ara anahtar / değer çifti oluşturmak için bir anahtar / değer çiftini işleyen bir eşleme işlevini ve aynı ara anahtarla ilişkili tüm ara değerleri birleştiren bir azaltma işlevini belirtir.

MapReduce'un avantajı, işlemenin birden çok işlem düğümünde (birden çok sunucu) paralel olarak gerçekleştirilebilmesi ve dolayısıyla çok iyi ölçeklenebilen bir sistem olmasıdır.

Fonksiyonel programlama modeline dayandığından , mapve reduceadımlarının her birinin herhangi bir yan etkisi yoktur (bir mapsürecin her bir alt bölümünün durumu ve sonuçları diğerine bağlı değildir), bu nedenle eşleştirilen ve indirgenen veri kümelerinin her biri ayrılabilir. çoklu işlem düğümleri üzerinden.

Joel's Programlama Diliniz Bunu Yapabilir mi? parça, Google'da arama motoruna güç veren MapReduce'u bulmak için işlevsel programlamayı anlamanın ne kadar önemli olduğunu tartışıyor. İşlevsel programlamaya ve ölçeklenebilir koda nasıl izin verdiğine aşina değilseniz, bu çok iyi bir okuma.

Ayrıca bakınız: Wikipedia: MapReduce

İlgili soru: Lütfen mapreduce'u basitçe açıklayın


3
Mükemmel bir şekilde açıkladı. Ve Software Monkey için, M / R'yi anladıktan sonra hemen hemen her şeye uygulamak inanılmaz derecede kolaydır ve burada verilen örneklerle sınırlı değildir. Bunu anlamanın birkaç yolu var, biri onu koleksiyonerler ve huniler olarak düşünmek.
Esko


16

Harita, bir listedeki tüm öğelere başka bir işlevi uygulayan, tüm dönüş değerleri ile başka bir liste oluşturan bir işlevdir. ("F'yi x'e uygula" demenin başka bir yolu da "f'yi çağır, x'i geç" tir. Bu nedenle bazen "ara" yerine "uygula" demek daha hoş gelebilir.)

Harita muhtemelen C # ile nasıl yazılır (denir Selectve standart kitaplıkta bulunur):

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

Sen bir Java adamı olduğun için ve Joel Spolsky BÜYÜK YALANLARA Java'nın ne kadar berbat olduğunu söylemeyi seviyor (aslında yalan söylemiyor, berbat, ama seni kazanmaya çalışıyorum), işte benim çok kaba girişimim bir Java sürümü (Java derleyicim yok ve Java sürüm 1.1'i belli belirsiz hatırlıyorum!):

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

Eminim bu, milyonlarca şekilde geliştirilebilir. Ama temel fikir bu.

Azaltma, bir listedeki tüm öğeleri tek bir değere dönüştüren bir işlevdir. Bunu yapmak için, funciki öğeyi tek bir değere dönüştüren başka bir işlevin verilmesi gerekir . İlk iki maddeyi vererek işe yarayacaktır func. Sonra üçüncü maddeyle birlikte bunun sonucu. Sonra bunun sonucu dördüncü maddeyle birlikte ve böylece tüm öğeler gidene ve tek bir değerle kalana kadar devam edecek.

C # 'da küçültme çağrılır Aggregateve yine standart kitaplıkta bulunur. Doğrudan bir Java sürümüne geçeceğim:

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

Bu Java sürümlerinin eklenmesi gereken jenerikler, ancak bunu Java'da nasıl yapacağımı bilmiyorum. Ancak, functors sağlamak için onlara anonim iç sınıfları geçirebilmelisiniz:

string[] names = getLotsOfNames();

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

Umarım jenerikler dökümlerden kurtulur. C # 'daki tip güvenli eşdeğeri:

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

Bu neden "harika"? Büyük hesaplamaları daha küçük parçalara ayırmanın basit yolları, böylece farklı şekillerde bir araya getirilebilir, her zaman harikadır. Google'ın bu fikri uygulama şekli paralelleştirmedir çünkü hem harita hem de küçültme birkaç bilgisayar üzerinden paylaşılabilir.

Ancak temel gereksinim, dilinizin işlevleri değerler olarak ele alabilmesi DEĞİLDİR. Herhangi bir OO dili bunu yapabilir. Paralelleştirme için gerçek gereksinim, eşlemeye funcgeçirdiğiniz ve azalttığınız küçük işlevlerin herhangi bir durumu kullanmaması veya güncellememesidir. Yalnızca kendilerine iletilen argümanlara bağlı bir değer döndürmeleri gerekir. Aksi takdirde, her şeyi paralel olarak çalıştırmaya çalıştığınızda sonuçlar tamamen alt üst olur.


2
Genel olarak iyi bir cevap, +1 değerinde; Java'daki jab'ı beğenmedim - ancak C'den Java'ya taşındığımdan beri işlev değerlerini kaçırdım ve Java'da kullanılabilirliklerinin çok gecikmiş olduğunu kabul ediyorum.
Lawrence Dol

1
Java'da ciddi bir aşırılık değildi - şu anda C # tercih etmeme yetecek kadar üç ya da daha fazla kusuru var, ancak C # da bir gün beni başka bir dilden önce yapmamı sağlayacak kusurların bir listesine sahip.
Daniel Earwicker

Bu arada, eğer gerçekten mümkünse, Java jeneriklerini kullanacak şekilde birisinin örnekleri düzenleyebilmesini çok isterim. Veya düzenleyemiyorsanız, snippet'leri buraya gönderin ve ben düzenleyeceğim.
Daniel Earwicker

Düzenlemeye başladım, ancak map () yöntemi dönüş türünde bir dizi oluşturuyor; Java, genel tür dizilerinin oluşturulmasına izin vermez. Listeyi kullanmak için değiştirebilirdim (ve muhtemelen bir diziye dönüştürebilirdim), ama o sırada hırsım tükendi.
Michael Myers

1
(A, b) => a + "," + b'ye benzer kapanış sözdizimi Java 7'de gerçekten dört gözle beklediğim bir şeydi, özellikle içeri girecek gibi görünen bazı yeni API öğelerinde. Bu sözdizimi bunun gibi şeyleri çok daha temiz hale getirdi; çok kötü olacak gibi görünmüyor.
Adam Jaskiewicz

2

Çok uzun waffley veya çok kısa belirsiz blog gönderileriyle en çok hayal kırıklığına uğradıktan sonra, sonunda bu çok iyi titiz özlü makaleyi keşfettim. .

Sonra devam ettim ve Scala'ya çevirerek daha kısa hale getirdim, burada bir kullanıcının sadece uygulamanın mapve reducebölümlerini belirttiği en basit durumu sağladım . Hadoop / Spark'ta, tam anlamıyla, kullanıcının burada ana hatları verilen 4 işlevi daha açıkça belirtmesini gerektiren daha karmaşık bir programlama modeli kullanılır: 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

Harita, bir diziye uygulanabilen yerel bir JS yöntemidir. Orijinal dizideki her öğeye eşlenen bazı işlevlerin bir sonucu olarak yeni bir dizi oluşturur. Dolayısıyla, bir işlevi (öğe) {dönüş öğesi * 2;} eşlediyseniz, her öğe iki katına çıkarılmış yeni bir dizi döndürür. Orijinal dizi değiştirilmez.

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

Azaltma, bir diziye de uygulanabilen yerel bir JS yöntemidir. Bir diziye bir işlev uygular ve biriktirici adı verilen bir başlangıç ​​çıkış değerine sahiptir. Dizideki her öğe arasında döngü oluşturur, bir işlev uygular ve bunları tek bir değere (toplayıcı olarak başlar) indirger. Kullanışlıdır çünkü istediğiniz herhangi bir çıktıya sahip olabilirsiniz, sadece bu tür bir akümülatörle başlamanız gerekir. Yani bir şeyi bir nesneye indirgemek istersem, bir akümülatörle başlarım {}.

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


0

Harita indirgeme:

Büyük bir şeyi çalıştırmak için ofisimizdeki farklı bilgisayarın hesaplama gücünü kullanabiliriz. İşin zor kısmı, görevi farklı bilgisayarlar arasında bölmektir.MapReduce kütüphanesi tarafından yapılır.

Temel fikir, işi iki bölüme ayırmanızdır: Harita ve Azaltma. Harita temelde sorunu alır, alt parçalara ayırır ve alt parçaları farklı makinelere gönderir - böylece tüm parçalar aynı anda çalışır. Azaltma, sonuçları alt bölümlerden alır ve tek bir yanıt almak için bunları tekrar birleştirir.

Giriş, kayıtların bir listesidir. Harita hesaplamasının sonucu, anahtar / değer çiftlerinin bir listesidir. Azaltma, aynı anahtara sahip her bir değer kümesini alır ve bunları tek bir değerde birleştirir. İşin 100 parçaya mı yoksa 2 parçaya mı bölündüğünü bilemezsiniz; sonuç, tek bir haritanın sonucuna çok benziyor.

Lütfen basit haritaya bakın ve programı azaltın:

Harita işlevi, orijinal listemize bazı işlevleri uygulamak için kullanılır ve bu nedenle yeni bir liste oluşturulur. Python'daki map () işlevi, bağımsız değişken olarak bir işlevi ve bir listeyi alır. Listenin her öğesine işlev uygulanarak yeni bir liste döndürülür.

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

Python'daki azaltma () işlevi, bağımsız değişken olarak bir işlevi ve bir listeyi alır. İşlev, bir lambda işlevi ve bir liste ile çağrılır ve yeni bir indirgenmiş sonuç döndürülür. Bu, listenin çiftleri üzerinde tekrarlayan bir işlem gerçekleştirir.

#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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.