Вызов функции в классе метода python

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
42
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

На все, что определено внутри класса, нужно ссылаться по полному имени, либо искать его непосредственно в классе, либо в его экземпляре. Таким образом, самое простое исправление здесь — явно вызвать 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)

который имеет пару преимуществ:

  1. Поиск вложенной области __class__ немного быстрее, чем поиск глобальной области NN, и
  2. Если имя вашего класса 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 - для альтернативных конструкторов в иерархиях классов, где подклассы должны иметь возможность создаваться с использованием альтернативного конструктора, не перегружая его в подклассе); это совершенно законно, просто немного неортодоксально.

Как обсуждается ниже в комментариях:

  1. Python плохо справляется с рекурсией
  2. Ваше условие рекурсии является обратным (вы возвращаете 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

Спасибо за такой подробный ответ, если я могу задать еще вопрос, я вижу следующее сообщение, когда я запускаю RecursionError: maximum recursion depth exceeded while calling a Python object. Это заставляет меня задуматься, не является ли рекурсивное решение проблемы в python оптимальным.

Josh 05.03.2019 19:39

@Josh: Тут ты прав. Python (по крайней мере, эталонный интерпретатор CPython) не может применить оптимизацию хвостового вызова и (для предотвращения фактического переполнения стека C) по умолчанию вызывает исключение, если вы спускаетесь более чем на 1000 кадров (можно проверить с помощью sys.getrecursionlimit(), изменить с помощью sys.setrecursionlimit(), но изменение его просто рискует фактическим сбоем переполнения стека, а не «хорошим» исключением). Рекурсивные решения обычно плохая идея, если они повторяются бесконечно. В этом случае кажется, что подойдет простой цикл while.

ShadowRanger 05.03.2019 19:56

Тем не менее, я думаю, что ваша функция обратная. Он продолжает создавать массивы, пока не получит массив всех нулей, а затем вернет нули. Я думаю, вы хотели, чтобы это было наоборот.

ShadowRanger 05.03.2019 19:58

Спасибо, я пытался избежать использования цикла while. Но если вы подумаете об этом статистически, я пытаюсь создать вектор из 0 и 1 со случайным распределением, с 50% вероятностью выбора 1.. так что мы действительно не должны столкнуться с неопределенным рекурсивным состоянием.

Josh 05.03.2019 20:01

@Josh: я обновил ответ, чтобы включить эту информацию. Отмечу, что использование sum для теста довольно неэффективно (он не может закоротить, и ему приходится преобразовывать каждое значение numpy в уровень Python int одно за другим по ходу), когда numpy уже предоставляет .any() метод который закорачивает и работает непосредственно со значениями уровня C без преобразования.

ShadowRanger 05.03.2019 20:08

Спасибо, я попробую это

Josh 05.03.2019 20:36

Другие вопросы по теме