Как отследить создание объекта класса python?

Как отследить создание объекта класса python?


В библиотеке глубокого обучения pytorch есть вот такая конструкция, которая позволяет собирать нейронную сеть из разных слоев.

import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x) # activation function
        x = self.conv2(x)
        return F.relu(x)

Я пишу свою библиотеку, которая внешне похожа на pytorch ( всё делается в учебных целях, заодно разбираюсь каждый раз в новых аспектах глубокого обучения.) Меня заинтересовали строки

self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)

Мы создаем два объекта класса Conv2d, присваиваем ссылки на них объекту класса Model. В процессе обучения(обратного распространения ошибки), градиент последовательно с конца проходит по всем слоям такой сети (сначала по self.conv2, потом по self.conv1 ). Отсюда следует вопрос, каким образом алгоритм узнает о всех слоях объекта класса ModelЭто обсуждение частично отвечает на мой вопрос. Я понял, что есть еще один класс, который собирает в некоторый список ссылки на объекты классов-"слоев" при их создании. У себя я реализовал это так

class Parameter:
    layers = []
    calling = dict()
    number_of_classes = 0

    def __init__(self, info):
        Parameter.layers.append(info[0])
        Parameter.calling[info[0]] = info[1:]


class Conv2d:
    def __init__(self, input_channels: int, output_channels: int, kernel_size: tuple, bias = True):
        # something happen here
        Parameter([self, self.kernel_array, self.bias_array])

Такой способ работает. Однако, пока объект класса Model ровно один. Как только создается еще один объект (ещё одна нейронка), то в Parameter.layers уже хранятся ссылки для разных объектов класса Model и соответственно всё перестает работать.

Есть ли у вас идеи, как это можно избежать?



С помощью замыканий я смог у себя всё реализовать.

В отдельном модуле сделал всё ту же вспомогательную сущность Parameter, но уже как замыкание

def ParameterObj():
    class Parameter:
        layers = []
        calling = dict()
        def __init__(self, info):
            Parameter.layers.append(info[0])
            Parameter.calling[info[0]] = info[1:]
    return Parameter

В основном модуле решил использовать глобальную переменную Parameter, которая будет изменяться при инициализации объекта дочернего класса (дочернего к Module), а также при вызове __call__.

Так как обучение (back propagation) происходит только после прямого прохождения по сети (forward propagation, вызова __call__), то ожидается что глобальная переменная Parameter будет ссылаться на именно тот класс Parameter, который был создан при создании нового объекта и соответственно при вызове self._constructor_Parameter = ParameterObj()

Parameter = None

class Module:
    def __init__(self):
        self._constructor_Parameter = ParameterObj()
        global Parameter
        Parameter = self._constructor_Parameter

class Conv:
    def __init__(self, input_channels: int, output_channels: int, kernel_size: tuple, bias = True):
        # something happen here
        Parameter([self, self.filter_array, np.zeros(self.n_filters)])

И теперь создание нейронной сети выглядит так

import nn

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Сonv(3, 5)
        self.act1 = nn.Relu()
        self.conv2 = nn.Conv(5, 5)
        self.sftmx = nn.Softmax()

    def forward(self, x):
        x = self.conv1(x)
        x = self.act1(x)
        x = self.conv2(x)
        x = self.sftmx(x)
        return x

model = SimpleNet()
prediction = model(x)



Report Page