2016-09-25 3 views
1

Я занимаюсь немного глубоким обучением, и я хочу захватить значения всех скрытых слоев. Так что я в конечном итоге написания функций, как это:Самый чистый способ объединить сокращение и карту в Python

def forward_pass(x, ws, bs): 
    activations = [] 
    u = x 
    for w, b in zip(ws, bs): 
     u = np.maximum(0, u.dot(w)+b) 
     activations.append(u) 
    return activations 

Если у меня не было, чтобы получить промежуточные значения, я бы использовать гораздо менее многословным вид:

out = reduce(lambda u, (w, b): np.maximum(0, u.dot(w)+b), zip(ws, bs), x) 

Bam. Вся одна линия, хорошая и компактная. Но я не могу сохранить какие-либо промежуточные значения.

Итак, какой способ получить мой торт (приятный компактный однострочный) и съесть его тоже (вернуть промежуточные значения)?

Edit: Мои выводы до сих пор:
В Python 2.x, нет чистой один вкладыш для этого.
В Python 3 есть itertools.accumulate, но он по-прежнему не очень чистый, потому что он не принимает «начальный» вход, как это делает reduce.
В Python 4 Прошу «карту-свертка понимание»:

activations = [u=np.maximum(0, u.dot(w)+b) for w, b in zip(ws, bs) from u=x] 

Что также даст полезную работу в этой from ключевое слово, которое обычно просто сидит, ничего не делая после того, как все импорта выполняются.

+1

И какие из них ваш 'промежуточный values', что вы хотели бы сохранить? – Divakar

+0

Расскажите нам о 'x',' ws', 'bs', особенно о их размерах. Может быть, даже набор образцов с выходом. – hpaulj

+0

Это '(w, b)' дает мне синтаксическую ошибку (в Py3) – hpaulj

ответ

0

В Python 2.x для этого нет чистого однострочного вкладыша.

В Python 3 есть itertools.accumulate, но он по-прежнему не очень чист, потому что он не принимает «начальный» ввод, как это делает сокращение.

Вот функция, которая, хотя и не такая приятная, как встроенный синтаксис понимания, выполняет эту работу.

def reducemap(func, sequence, initial=None, include_zeroth = False): 
    """ 
    A version of reduce that also returns the intermediate values. 
    :param func: A function of the form x_i_plus_1 = f(x_i, params_i) 
     Where: 
      x_i is the value passed through the reduce. 
      params_i is the i'th element of sequence 
      x_i_plus_i is the value that will be passed to the next step 
    :param sequence: A list of parameters to feed at each step of the reduce. 
    :param initial: Optionally, an initial value (else the first element of the sequence will be taken as the initial) 
    :param include_zeroth: Include the initial value in the returned list. 
    :return: A list of length: len(sequence), (or len(sequence)+1 if include_zeroth is True) containing the computed result of each iteration. 
    """ 
    if initial is None: 
     val = sequence[0] 
     sequence = sequence[1:] 
    else: 
     val = initial 
    results = [val] if include_zeroth else [] 
    for s in sequence: 
     val = func(val, s) 
     results.append(val) 
    return results 

Тесты:

assert reducemap(lambda a, b: a+b, [1, 2, -4, 3, 6, -7], initial=0) == [1, 3, -1, 2, 8, 1] 
assert reducemap(lambda a, b: a+b, [1, 2, -4, 3, 6, -7]) == [3, -1, 2, 8, 1] 
assert reducemap(lambda a, b: a+b, [1, 2, -4, 3, 6, -7], include_zeroth=True) == [1, 3, -1, 2, 8, 1] 
2

В общем, itertools.accumulate() сделает то, что reduce() делает, но даст вам промежуточные значения. Тем не менее, аккумулирование не поддерживает начало значение, поэтому оно не применимо в вашем случае.

Пример:

>>> import operator, functools, itertools 
>>> functools.reduce(operator.mul, range(1, 11)) 
3628800 
>>> list(itertools.accumulate(range(1, 11), operator.mul)) 
[1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] 
2

dot говорит мне, что вы используете один или несколько Numpy массивов. Так что я постараюсь:

In [28]: b=np.array([1,2,3]) 
In [29]: x=np.arange(9).reshape(3,3) 
In [30]: ws=[x,x,x] 

In [31]: forward_pass(x,ws,bs) 
Out[31]: 
[array([[ 16, 19, 22], 
     [ 43, 55, 67], 
     [ 70, 91, 112]]), 
array([[ 191, 248, 305], 
     [ 569, 734, 899], 
     [ 947, 1220, 1493]]), 
array([[ 2577, 3321, 4065], 
     [ 7599, 9801, 12003], 
     [12621, 16281, 19941]])] 

В PY3 я должен написать reduce решения, как:

In [32]: functools.reduce(lambda u, wb: np.maximum(0, 
        u.dot(wb[0])+wb[1]), zip(ws, bs), x) 
Out[32]: 
array([[ 2577, 3321, 4065], 
     [ 7599, 9801, 12003], 
     [12621, 16281, 19941]]) 

Это промежуточное значение u, которое передается от одной оценки к другому делает список понимание сложно.

accumulate использует первый элемент в качестве стартового. Я могу работать вокруг, что с помощью функции, как

def foo(u, wb): 
    if u[0] is None: u=x # x from global 
    return np.maximum(0, u.dot(wb[0])+wb[1]) 

Тогда мне нужно добавить дополнительные начальные значения для ws и bs:

In [56]: list(itertools.accumulate(zip([None,x,x,x], np.array([0,1,2,3])), foo)) 
Out[56]: 
[(None, 0), 
array([[ 16, 19, 22], 
     [ 43, 55, 67], 
     [ 70, 91, 112]]), 
array([[ 191, 248, 305], 
     [ 569, 734, 899], 
     [ 947, 1220, 1493]]), 
array([[ 2577, 3321, 4065], 
     [ 7599, 9801, 12003], 
     [12621, 16281, 19941]])] 

Вот список понимания версии, используя внешнюю u:

In [66]: u=x.copy() 
In [67]: def foo1(wb): 
    ...:  v = np.maximum(0, u.dot(wb[0])+wb[1]) 
    ...:  u[:]=v 
    ...:  return v 
    ...: 
In [68]: [foo1(wb) for wb in zip(ws,bs)] 
Out[68]: 
[array([[ 16, 19, 22], 
     [ 43, 55, 67], 
     [ 70, 91, 112]]), 
array([[ 191, 248, 305], 
     [ 569, 734, 899], 
     [ 947, 1220, 1493]]), 
array([[ 2577, 3321, 4065], 
     [ 7599, 9801, 12003], 
     [12621, 16281, 19941]])] 

Нет реального преимущества перед исходным циклом с append.

numpy.ufunc есть метод accumulate, но это непросто для использования с пользовательскими функциями Python. Итак, есть np.maximum.accumulate, но я не уверен, как это можно использовать в этом случае. (также np.cumsum, который составляет np.add.accumulate).

+0

Спасибо за подробный ответ. Внутренний накопитель numpy не очень полезен, учитывая, что различные элементы ws могут иметь разные размеры. Похоже, нет чистого решения. – Peter