Как отследить создание объекта класса 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)