在查看单独的一行时,我倾向于看到不清楚您在计算中的位置的代码。例如:
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的大小都相同,那么理想的解决方案就是只占用内存中最多两个变量的大小。
在计算左侧后,是否有Python方式删除右侧使用的所有变量,或者有类似的效果?或者,您是否可以创建一个执行删除和分配(给定作用域限制)的函数,效率折衷是什么?
答案 0 :(得分:2)
理想的解决方案是只占用内存中最多两个变量的大小。
del
在Python中并不经常需要。重复是一种代码气味。不要重复自己(DRY原理)。您可以使用循环删除重复项。
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()
的另一个参数)来添加更多层变得更加容易。方法。
您能创建一个执行删除和分配(给定作用域限制)的函数吗,效率折衷是什么?
除非使用del
(或当它们超出范围时),否则您不能真正删除本地人。但是您可以使用自己的名称空间来代替本地人。这是由dict所支持的,该dict的效率仅比Local稍低,在这里并不重要。
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