Вызов функции в классе Python метода - PullRequest
1 голос
/ 05 марта 2019
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(...)

Я хочу создать функцию в своем классе, которая вызывается класс-метод. Эта функция является рекурсивной и предназначена для возврата вектор 0 и 1, только если вектор имеет по крайней мере один ненулевой элемент

Ошибка, которую я получаю: "Nameerror: name 'generate_random_nodes' is не определено

1 Ответ

2 голосов
/ 05 марта 2019

На все, что определено внутри класса, должно указываться полное имя, либо поиск по классу напрямую, либо по его экземпляру. Таким образом, самое простое исправление здесь - это явный вызов 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 s получают ссылку на класс, к которому они были вызваны (класс экземпляра, к которому они были вызваны, если вызван на экземпляре). Это незначительное злоупотребление 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...