На все, что определено внутри класса, должно указываться полное имя, либо поиск по классу напрямую, либо по его экземпляру. Таким образом, самое простое исправление здесь - это явный вызов NN.generate_random_nodes
для рекурсивного вызова и self.generate_random_nodes
при первоначальных вызовах (только показ методов с изменениями):
@staticmethod
def generate_random_nodes(dropout_prob, size):
temp = np.random.binomial(1, dropout_prob, size)
# Must explicitly qualify recursive call
return temp if not sum(temp) else NN.generate_random_nodes(dropout_prob, size)
def compute_dropout(self, activations, dropout_prob = 0.5):
[...]
mult = np.copy(activations)
# Can call static on self just fine, and avoids hard-coding class name
temp = self.generate_random_nodes(dropout_prob, size=activations.shape[0])
mult[:,i] = temp
activations*=mult
return activations
Обратите внимание, что как деталь реализации CPython в Python 3.x, ссылка __class__
внутри метода, определенного в классе, создает область замыкания, которая дает вам доступ к классу, для которого он был определен, что позволяет избежать повторения явно указав класс, поэтому generate_random_nodes
может быть:
@staticmethod
def generate_random_nodes(dropout_prob, size):
temp = np.random.binomial(1, dropout_prob, size)
# Must qualify recursive call
return temp if not sum(temp) else __class__.generate_random_nodes(dropout_prob, size)
, который имеет пару преимуществ:
- Поиск вложенной области
__class__
немного быстрее, чем поиск глобальной области NN
и
- Если имя вашего
NN
класса изменяется во время разработки, вам вообще не нужно изменять generate_random_nodes
(поскольку он неявно получает ссылку на класс, в котором он был определен).
Вы также можете (не полагаясь на детали реализации CPython) изменить его на classmethod
, чтобы получить то же основное преимущество:
@classmethod
def generate_random_nodes(cls, dropout_prob, size):
temp = np.random.binomial(1, dropout_prob, size)
# Must qualify recursive call
return temp if not sum(temp) else cls.generate_random_nodes(dropout_prob, size)
, поскольку classmethod
s получают ссылку на класс, к которому они были вызваны (класс экземпляра, к которому они были вызваны, если вызван на экземпляре). Это незначительное злоупотребление classmethod
(classmethod
предназначено только для альтернативных конструкторов в иерархиях классов, где подклассы должны иметь возможность конструироваться с использованием альтернативного конструктора без перегрузки его в подклассе); это совершенно законно, просто немного неортодоксально.
Как указано ниже в комментариях:
- Python плох при рекурсии
- Ваше рекурсивное условие обратное (вы возвращаете
temp
, только если sum
его равно 0
, что означает temp
- массив всех нулей), что значительно увеличивает вероятность рекурсии и делает Ошибка рекурсии почти наверняка для достаточно высоких dropout_prob
/ size
аргументов.
Итак, вы хотите изменить temp if not sum(temp) else <recursive call>
на temp if sum(temp) else <recursive call>
, или для лучшей производительности / наглядности, если это массив numpy
, temp if temp.any() else <recursive call>
. И хотя вероятность того, что ошибки рекурсии могут начаться с маловероятной вероятности, весьма мала, если вы хотите быть очень осторожным, просто перейдите на while
подход, основанный на циклах, который не может рисковать неопределенной рекурсией:
@staticmethod
def generate_random_nodes(dropout_prob, size):
while True:
temp = np.random.binomial(1, dropout_prob, size)
if temp.any():
return temp