Как сгладить график расчета цикла с аккумулятором (символическими переменными)?

Я новичок в теано. Но я уже гуглил, читал официальную документацию theano и не нашел подсказки, как решить мою проблему.

Я пытаюсь заново изобрести колесо: я реализую свою собственную пакетную свертку, используя theano. (Я делаю это, чтобы изучить эту библиотеку)

Итак, вот что я пытаюсь сделать:

# lr_all_w is a 3-tensor of <filter #, width, height>
lr_all_w = self._all_W.dimshuffle(('x', 0, 1)).repeat(self._prev_layer._processors_count, axis=0)

# element-wise to self._in_weight_masks
lr_all_w = lr_all_w * self._in_weight_masks
lr_all_w.name = 'lr_all_w'

#convolved = T.tensor3("convolved_batch")
# 'convolved' represents a dense convolved batches using im2col
convolved = T.zeros((self.batch_size, self._processors_count, self._processor_side**self._rec_f_dim))
convolved.name = "convolved_batches"

for batch_idx in range(self.batch_size):
    for i in range(self._prev_layer._processors_count):
        convolved = T.inc_subtensor(convolved[batch_idx], T.dot(lr_all_w[i], im2col_prev_layer[batch_idx, i]))

    # and adding bias
    convolved = T.inc_subtensor(convolved[batch_idx], self._all_B)

Это приводит к очень глубокому графику вычислений, потому что inc_subtensor добавляется поверх каждой предыдущей операции:

inc_subtensor_stepN (inc_subtensor_stepN-1 (inc_subtensor_stepN-2...

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

Я попробовал theano.clone, но это приводит к той же ситуации, что и inc_subtensor.

Затем я попытался использовать theano.scan:

sym_im2col_prev_layer_batch_idx = T.tensor3("sym_im2col_prev_layer_batch_idx")
#TODO replace sym_im2col_prev_layer_batch_idx with concrete substitution afterwards
result, updates = theano.scan( fn=lambda lr_all_w_i, im2col_prev_layer_batch_idx_i: T.dot(lr_all_w_i, im2col_prev_layer_batch_idx_i),
 sequences=[lr_all_w, sym_im2col_prev_layer_batch_idx])

to_substitute = result.sum(0)
to_substitute.name = 'to_substitute'

for batch_idx in range(self.batch_size):
    for i in range(self._prev_layer._processors_count):
        sym_im2col_prev_layer_curr_batch = theano.clone(
            to_substitute, {sym_im2col_prev_layer_batch_idx: im2col_prev_layer[batch_idx]}
        )
        convolved = T.set_subtensor(convolved[batch_idx], sym_im2col_prev_layer_curr_batch)

    # and adding bias
    convolved = T.set_subtensor(convolved[batch_idx], convolved[batch_idx] + self._all_B)

Но тем не менее, я получаю «RuntimeError: максимальная глубина рекурсии превышена в сравнении» прямо в первый раз, когда выполняется sym_im2col_prev_layer_curr_batch = theano.clone.

Последний пример фрагмента кода правильно показывает то, что я собираюсь сделать. Но я понятия не имею, почему я получаю «превышение максимальной глубины рекурсии». Потому что каждый раз, когда я делаю theano.clone, предполагается, что theano заменяет sym_im2col_prev_layer_batch_idx (который уже используется в сканировании) своим точным символическим значением — im2col_prev_layer[batch_idx] и дает мне копию этого подграфа. Я мог что-то упустить...

Как такие (или похожие) задачи решаются в theano и как избежать слишком глубоких расчетных графиков при выполнении таких задач?

Также я пробовал такой подход:

Я пробовал такой подход:

for batch_idx in range(self.batch_size):
    result, updates = theano.scan(fn=lambda lr_all_w_i, im2col_prev_layer_batch_idx_i: T.dot(lr_all_w_i, im2col_prev_layer_batch_idx_i),
                                  sequences=[lr_all_w, im2col_prev_layer[batch_idx]])

    result = result.sum(0)
    convolved = T.set_subtensor(convolved[batch_idx], result)


    # and adding bias
    convolved = T.inc_subtensor(convolved[batch_idx], self._all_B)

Но при попытке напечатать значение «свернуто» сразу после цикла «для» я получаю:

ipdb> theano.printing.debugprint(convolved)
...
*** RuntimeError: maximum recursion depth exceeded while calling a Python object

Итак, та же история.

Увеличение глубины рекурсии для python НЕ вариант.

Любые идеи, как сгладить график расчета для моего случая?


person Oleksandr Khryplyvenko    schedule 31.05.2015    source источник


Ответы (1)


В общем, theano.scan - это решение для рекурсивных ситуаций. В случае, подобном вашему, theano.scan следует использовать для замены цикла Python for, а не в дополнение к циклу for.

Трудно точно понять, чего вы пытаетесь достичь, но широкое использование set_subtensor и inc_subtensor предполагает, что вы думаете об этом таким образом, который не совсем соответствует тому, как Theano хочет работать. theano.scan может позволить вам достичь того, чего вы хотите, используя подход, который вы сейчас используете, но после быстрого просмотра предоставленного вами кода не похоже, что даже theano.scan требуется. Если одна итерация не зависит от результатов предыдущей итерации, как кажется, то вы, вероятно, можете сделать это вообще без каких-либо циклов (ни циклов Python for, ни theano.scan) путем разумного использования тензорных операций Theano. Подход без цикла почти наверняка был бы гораздо более эффективным и быстрым, чем выполнение действий через какой-либо цикл. По общему признанию, это может быть сложнее понять, чем последовательные операции типа «одна строка за раз».

Если вы не видите, как ваши вычисления могут быть достигнуты с помощью простых многомерных тензорных операций без циклов, я бы предложил посмотреть, как вы можете заменить свои for циклы Python как можно меньшим количеством операций theano.scan.

person Daniel Renshaw    schedule 01.06.2015
comment
Хорошие моменты, +1, но упражнение, упомянутое в OP, вероятно, уже противоречит тому, как Theano хочет работать;) Насколько я понимаю (неисполняемый) код выше, он написан для эмуляции T.nnet.conv2d или чего-то подобного. Поэтому прямое использование последнего было бы лучшей практикой. - person eickenberg; 02.06.2015
comment
Прочитав ваш комментарий, я понял, что мне нужно изменить свой взгляд на вещи в Theano (надо сказать, что я получил такой же ответ от вас и в группе theano-users google). невозможно решить эту задачу с помощью сканирования. Иногда осознание того, что «так поступить невозможно», подталкивает к поиску других решений. Итак, я нашел это: ‹код› свернут = T.tensordot(im2col_prev_layer, lr_all_w, ((1, 2), (0, 2))) свернут = свернут.dimshuffle((0, 2, 1)) + self. _all_B.dimshuffle(('x', 0, 1)).repeat(self.batch_size, axis=0) ‹/code› - person Oleksandr Khryplyvenko; 08.06.2015