class NN(object):
def __init__(...):
[...] #some intialization of the class
#define a recursive function to return a vector which has atleast one non-zero element
@staticmethod
def generate_random_nodes(dropout_prob, size):
temp = np.random.binomial(1, dropout_prob, size)
return temp if not sum(temp) else generate_random_nodes(dropout_prob, size)
def compute_dropout(self, activations, dropout_prob = 0.5):
[...]
mult = np.copy(activations)
temp = generate_random_nodes(dropout_prob, size = activations.shape[0])
mult[:,i] = temp
activations*=mult
return activations
def fit(self, ...):
compute_dropout(...)
I want to create a function within my class which is called by a class-method. This function is recursive and is meant to return a vector of 0s and 1s only if the vector has atleast one non-zero element
The error I'm getting is "Nameerror: name 'generate_random_nodes' is not defined





На все, что определено внутри класса, нужно ссылаться по полному имени, либо искать его непосредственно в классе, либо в его экземпляре. Таким образом, самое простое исправление здесь — явно вызвать 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 получают ссылку на класс, к которому они были вызваны (класс экземпляра, к которому они были вызваны, если они вызываются к экземпляру). Это небольшое злоупотребление classmethod (единственное предполагаемое использование classmethod - для альтернативных конструкторов в иерархиях классов, где подклассы должны иметь возможность создаваться с использованием альтернативного конструктора, не перегружая его в подклассе); это совершенно законно, просто немного неортодоксально.
Как обсуждается ниже в комментариях:
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
@Josh: Тут ты прав. Python (по крайней мере, эталонный интерпретатор CPython) не может применить оптимизацию хвостового вызова и (для предотвращения фактического переполнения стека C) по умолчанию вызывает исключение, если вы спускаетесь более чем на 1000 кадров (можно проверить с помощью sys.getrecursionlimit(), изменить с помощью sys.setrecursionlimit(), но изменение его просто рискует фактическим сбоем переполнения стека, а не «хорошим» исключением). Рекурсивные решения обычно плохая идея, если они повторяются бесконечно. В этом случае кажется, что подойдет простой цикл while.
Тем не менее, я думаю, что ваша функция обратная. Он продолжает создавать массивы, пока не получит массив всех нулей, а затем вернет нули. Я думаю, вы хотели, чтобы это было наоборот.
Спасибо, я пытался избежать использования цикла while. Но если вы подумаете об этом статистически, я пытаюсь создать вектор из 0 и 1 со случайным распределением, с 50% вероятностью выбора 1.. так что мы действительно не должны столкнуться с неопределенным рекурсивным состоянием.
@Josh: я обновил ответ, чтобы включить эту информацию. Отмечу, что использование sum для теста довольно неэффективно (он не может закоротить, и ему приходится преобразовывать каждое значение numpy в уровень Python int одно за другим по ходу), когда numpy уже предоставляет .any() метод который закорачивает и работает непосредственно со значениями уровня C без преобразования.
Спасибо, я попробую это
Спасибо за такой подробный ответ, если я могу задать еще вопрос, я вижу следующее сообщение, когда я запускаю
RecursionError: maximum recursion depth exceeded while calling a Python object. Это заставляет меня задуматься, не является ли рекурсивное решение проблемы в python оптимальным.