Удаление переменных после использования во время присваивания - PullRequest
0 голосов
/ 28 февраля 2019

Я склонен видеть код, в котором неясно, где вы находитесь в вычислениях, глядя на отдельную строку.Например:

def forward(self, X):
    X = layer(X)
    X = activation(X)
    X = layer(X)
    X = activation(X)
    return X

Очевидно, что строки 2 и 4, а также 3 и 5 неотличимы друг от друга.

Я бы предпочел написать более читаемый код, где ясно, гдевы находитесь в вычислении, когда смотрите на конкретную строку кода.Например (используя запись Эндрю Нга):

def forward(self, X):
    A0 = X
    Z1 = layer(A0)
    A1 = activation(Z1)
    Z2 = layer(A1)
    A2 = activation(Z2)
    return A2

Но это может привести к ошибкам памяти при больших переменных, поэтому может потребоваться выполнить следующее ...

def forward(self, X):
    A0 = X
    Z1 = layer(A0); del A0
    A1 = activation(Z1); del Z1
    Z2 = layer(A1); del A1
    A2 = activation(Z2); del Z2
    return A2

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

Если бы A0, Z1, A1, Z2, A2 были одинакового размера, в идеале было бы решение, которое занимало бы размер не более двухпеременных в памяти.

Есть ли питонический способ удалить все переменные, используемые справа, после вычисления левой части, или иметь какой-то подобный эффект?В качестве альтернативы, вы могли бы создать функцию, которая выполняет удаление и присваивание (учитывая ограничения области видимости) и каков там компромисс эффективности?

1 Ответ

0 голосов
/ 28 февраля 2019

в идеале было бы решение, которое занимало бы размер не более двух переменных в памяти.

del не часто требуется в Python.Повторение - это запах кода.Не повторяйте себя (принцип СУХОГО).Вы можете удалить повторение с помощью цикла.

def forward(self, A):
    for _ in range(2):
        Z = layer(A)
        A = activation(Z)
    return A

Вы будете повторно использовать две переменные A и Z.

Вы можете сжать это дальше, вложиввызовы, которые полностью удаляют Z.

def forward(self, A):
    for _ in range(2):
        A = activation(layer(A))
    return A

Если вы склонны к функциональности, этот паттерн известен как «уменьшить» (иногда также называемый «сгиб»).Это может быть менее "Pythonic", но функциональный стиль все еще довольно часто используется в коде Python.

from functools import reduce

def forward(self, X):
    return reduce(lambda A, _: activation(layer(A)), range(2), X)

Или даже

def forward(self, X):
    return reduce(lambda x, f: f(x), [layer, activation]*2, X)

Популярная библиотека toolz также реализуетэтот шаблон

from toolz.functoolz import thread_first

def forward(self, X):
    return thread_first(X, layer, activation, layer, activation)

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

def forward(self, X):
    return thread_first(
        X,  # A0
        layer, # Z1
        activation, # A1
        layer,  # Z2
        activation,  # A2
    )

Они не применяются или что-то в этом роде.


Фактически, переменные вообще не требуются, за исключением параметра.

def forward(self, X):
    return activate(layer(activate(layer(X))))

Функция действительно настолько проста, и суета вокруг имен переменных, кажется, слишком усложняет ее.

Для всего двух слоев это, вероятно, нормально, но версия с зацикливанием / сокращением упрощает добавление большего количества слоев позже, обновляя аргумент range(), который может быть даже другим параметром для метода .forward().


не могли бы вы создать функцию, которая выполняет удаление и присваивание (с учетом ограничений области видимости) и каков там компромисс эффективности?

Вы не можете на самом делеdместные жители, за исключением del (или когда они выходят за рамки видимости).Но вместо местных жителей вы можете создать собственное пространство имен.Это подкреплено диктовкой, которая лишь немного менее эффективна, чем местные жители, но ее здесь недостаточно.

from types import SimpleNamespace

class MonoNamespace(SimpleNamespace):
    """A namespace that holds only one attribute at a time."""
    def __setattr__(self, name, value):
        vars(self).clear()
        vars(self)[name] = value

def forward(self, X):
    ns = MonoNamespace(A0=X)
    ns.Z1 = layer(ns.A0)
    ns.A1 = activation(ns.Z1)
    ns.Z2 = layer(ns.A1)
    ns.A2 = activation(ns.Z2)
    return ns.A2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...