MapReduce для нескольких типов хранилищ данных в Google App Engine

Я только что просмотрел Пакетную обработка данных с помощью сеанса App Engine Google I/O 2010, прочитать некоторые части MapReduce статью из Google Research, и теперь я думаю использовать MapReduce в Google App Engine для реализации рекомендательной системы в Python.

Я предпочитаю использовать appengine-mapreduce вместо Task Queue API, потому что первый предлагает простую итерацию по всем экземплярам какого-либо типа, автоматическую пакетную обработку, автоматическую цепочку задач и т. д. Проблема в том, что моей рекомендательной системе необходимо вычислить корреляцию между экземплярами двух разных моделей. , т. е. экземпляры двух различных видов.

Пример: у меня есть две модели: User и Item. Каждый из них имеет список тегов в качестве атрибута. Ниже приведены функции для расчета корреляции между пользователями и элементами. Обратите внимание, что calculateCorrelation следует вызывать для каждой комбинации пользователей и элементов:

def calculateCorrelation(user, item):
    return calculateCorrelationAverage(u.tags, i.tags)

def calculateCorrelationAverage(tags1, tags2):
    correlationSum = 0.0
    for (tag1, tag2) in allCombinations(tags1, tags2):
        correlationSum += correlation(tag1, tag2)
    return correlationSum / (len(tags1) + len(tags2))

def allCombinations(list1, list2):
    combinations = []
    for x in list1:
        for y in list2:
            combinations.append((x, y))
    return combinations             

Но этот calculateCorrelation не является действительным Mapper в appengine-mapreduce, и, возможно, эта функция даже не совместима с концепцией вычислений MapReduce. Тем не менее, я должен быть уверен... для меня было бы очень здорово иметь такие преимущества appengine-mapreduce, как автоматическая пакетная обработка и цепочка задач.

Есть ли решение для этого?

Должен ли я определить свой собственный InputReader? Новый InputReader, который считывает все экземпляры двух разных типов, совместим с текущей реализацией appengine-mapreduce?

Или мне попробовать следующее?

  • Объедините все ключи всех сущностей этих двух типов, по два, в экземпляры новой Модели (возможно, используя MapReduce)
  • Итерация с использованием картографов по экземплярам этой новой модели
  • Для каждого экземпляра используйте ключи внутри него, чтобы получить две сущности разных типов и вычислить корреляцию между ними.

person fjsj    schedule 22.09.2010    source источник
comment
Каковы критерии прохождения в Users and Items? Это любая комбинация пользователя и элемента? Только те, которые каким-то образом связаны? Кроме того, что это за язык? Это не (совсем) Python!   -  person Nick Johnson    schedule 24.09.2010
comment
calculateCorrelation следует вызывать для каждой комбинации пользователя и элемента. А теперь я убрал типы переменных, чтобы не было путаницы.   -  person fjsj    schedule 24.09.2010


Ответы (2)


Следуя предложению Ника Джонсона, я написал свой собственный InputReader. Этот считыватель извлекает объекты двух разных типов. Это дает кортежи со всеми комбинациями этих сущностей. Вот:

class TwoKindsInputReader(InputReader):
    _APP_PARAM = "_app"
    _KIND1_PARAM = "kind1"
    _KIND2_PARAM = "kind2"
    MAPPER_PARAMS = "mapper_params"

    def __init__(self, reader1, reader2):
        self._reader1 = reader1
        self._reader2 = reader2

    def __iter__(self):
        for u in self._reader1:
            for e in self._reader2:
                yield (u, e)

    @classmethod
    def from_json(cls, input_shard_state):
        reader1 = DatastoreInputReader.from_json(input_shard_state[cls._KIND1_PARAM])
        reader2 = DatastoreInputReader.from_json(input_shard_state[cls._KIND2_PARAM])

        return cls(reader1, reader2)

    def to_json(self):
        json_dict = {}
        json_dict[self._KIND1_PARAM] = self._reader1.to_json()
        json_dict[self._KIND2_PARAM] = self._reader2.to_json()
        return json_dict

    @classmethod
    def split_input(cls, mapper_spec):
        params = mapper_spec.params
        app = params.get(cls._APP_PARAM)
        kind1 = params.get(cls._KIND1_PARAM)
        kind2 = params.get(cls._KIND2_PARAM)
        shard_count = mapper_spec.shard_count
        shard_count_sqrt = int(math.sqrt(shard_count))

        splitted1 = DatastoreInputReader._split_input_from_params(app, kind1, params, shard_count_sqrt)
        splitted2 = DatastoreInputReader._split_input_from_params(app, kind2, params, shard_count_sqrt)
        inputs = []

        for u in splitted1:
            for e in splitted2:
                inputs.append(TwoKindsInputReader(u, e))

        #mapper_spec.shard_count = len(inputs) #uncomment this in case of "Incorrect number of shard states" (at line 408 in handlers.py)
        return inputs

    @classmethod
    def validate(cls, mapper_spec):
        return True #TODO

Этот код следует использовать, когда вам нужно обработать все комбинации объектов двух типов. Вы также можете обобщить его для более чем двух типов.

Вот действительный файл mapreduce.yaml для TwoKindsInputReader:

mapreduce:
- name: recommendationMapReduce
  mapper:
    input_reader: customInputReaders.TwoKindsInputReader
    handler: recommendation.calculateCorrelationHandler
    params:
    - name: kind1
      default: kinds.User
    - name: kind2
      default: kinds.Item
    - name: shard_count
      default: 16
person fjsj    schedule 20.10.2010

Трудно понять, что порекомендовать, без более подробной информации о том, что вы на самом деле рассчитываете. Один простой вариант — просто получить связанный объект внутри вызова карты — ничто не мешает вам выполнять там операции с хранилищем данных.

Однако это приведет к большому количеству мелких вызовов. Написание пользовательского InputReader, как вы предлагаете, позволит вам получать оба набора сущностей параллельно, что значительно повысит производительность.

Если вы предоставите более подробную информацию о том, как вам нужно присоединиться к этим организациям, мы сможем предоставить более конкретные предложения.

person Nick Johnson    schedule 22.09.2010