Эффективный способ получения значений «нейрон-нейрон-нейрон» в нейронной сети

#python #neural-network #pytorch

Вопрос:

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

У меня есть несколько нейронных сетей (это один из примеров):

 import torch import torch.nn as nn import torch.optim as optim  class Model(nn.Module):  def __init__(self):  super(Model, self).__init__()  self.fc1 = nn.Linear(1, 2)  self.fc2 = nn.Linear(2, 3)  self.fc3 = nn.Linear(3, 1)   def forward(self, x):  x1 = self.fc1(x)  x = torch.relu(x1)  x2 = self.fc2(x)  x = torch.relu(x2)  x3 = self.fc3(x)  return x3, x2, x1  net = Model()  

Как я могу эффективно получить значения node-edge-node ( neuron-edge-neuron ) в сети? Некоторые из этих сетей имеют большое количество параметров. Обратите внимание, что для первого слоя это будет input-edge-neuron скорее, чем neuron-edge-neuron .

Я попытался сохранить значения каждого узла после fc слоев (т. Е. x1,x2,x3 ), чтобы мне не пришлось их пересчитывать, но я не уверен, как сделать ребра и эффективно сопоставить их с соответствующими нейронами.

Вывод, который я ищу, — это список списков node-edge-node значений. Хотя это также может быть тензор тензоров, если это проще. Например, в приведенной выше сети с первого слоя у меня будет 2 тройки (1×2), со 2-го слоя у меня будет 6 из них (2×3), а в последнем слое у меня будет 3 тройки (3×1). Проблема заключается в эффективном сопоставлении значений узлов (т. е. нейронов) (одного из слоя n-1 и одного из слоя n) с соответствующими ребрами.

Комментарии:

1. В какой форме вы стремитесь к конечному результату и каков ваш текущий подход ?

2. «Вывод, который я ищу, — это список списков node-edge-node значений». Хотя это также может быть тензор тензоров, если это проще. Мой текущий подход состоит в том, чтобы теоретически перебирать fc матрицу каждого слоя и каким-то образом сопоставлять края с соответствующим начальным узлом (с предыдущего слоя) и конечным узлом (со следующего слоя), но я не смог заставить его работать (и я не уверен, что это эффективный способ сделать это

3. Гарантируете ли вы, что в вашей сети есть только линейные слои, и они всегда каскадированы ?

4. .. а как насчет термина предвзятости ? входит ли это в вашу node-edge-node структуру ? Я так не думаю. Это важно ?

5. @ayandas вы можете игнорировать предвзятость

Ответ №1:

Признание: Давайте начнем с того, что я немного изменил ваш код, чтобы сделать его удобным. Вы можете сделать все в том виде, в каком оно было изначально. Я также изменил определенное количество нейронов просто для игры (я уверен, что вы можете вернуть их обратно).

Я создал summary объект (возвращаемый .forward() функцией), который содержит всю трассировку выполнения сети, т. Е. (input, weight, output) кортежи для *каждого слоя.

 class Model(nn.Module):  def __init__(self):  super(Model, self).__init__()  self.fc1 = nn.Linear(3, 5)  self.fc2 = nn.Linear(5, 7)  self.fc3 = nn.Linear(7, 2)   def forward(self, x):  summary = []  running_x = x  for layer in self.children():  out = layer(running_x)  # triplet of (input, weight, output) for each layer  summary.append((running_x, layer.weight, out))  running_x = out   return summary  model = Model() batch_size = 32 X = torch.rand(batch_size, 3) summary = model(X)  

Основная логика заключается только в этом

 for L in summary: # iterate over the (ip, weight, out) tuple for each layer  ip, weight, out = L # unpack them    ip = ip[:, :, None, None].repeat(1, 1, out.shape[-1], 1)  weight = weight.T[None, :, :, None].repeat(batch_size, 1, 1, 1)  out = out[:, None, :, None].repeat(1, ip.shape[1], 1, 1)  triplets = torch.cat([ip, weight, out], -1)  

Таким triplets образом, переменная (по одной для каждого слоя) — это все, что вы ищете. Он имеет размер

 (batch_size, layer_in_dim, layer_out_dim, 3)  

Давайте рассмотрим конкретно triplets первый слой.

 gt;gt; triplets.shape (32, 3, 5, 3)  

Например, учитывая индекс выборки , индекс b = 12 входного нейрона i = 1 и индекс выходного нейрона j = 3 , у вас есть ровно node-edge-node кортежи

 gt;gt; triplets[b][i][j] tensor([0.7080, 0.3442, 0.7344], ...)  

Проверка: Давайте вручную проверим правильность.

Размерность 12 1 st t-го образца равна

 # Its the first layer we are looking, so input comes from user gt;gt; X[12][1] tensor(0.7080)   

проверять.

Соединительный вес между 1 входным нейроном st и 3 выходным нейроном rd для первого слоя

 gt;gt; model.fc1.weight.T[1][3] # weight matrix is transposed, so had to do .T tensor(0.3442, ...)  

проверять.

Вывод 3 rd-нейрона для 12 t-го образца может быть получен из его тензора активации

 gt;gt; _, _, out = summary[0] # first layer's output tensor gt;gt; out[12][3] tensor(0.7344, ...)  

ТАКЖЕ ПРОВЕРЬТЕ.


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

Комментарии:

1. Выглядит неплохо. Несколько вопросов: если бы я хотел также обучить этому, нужно ли мне возвращаться running_x вместе с summary этим ? Кроме того, вы упомянули , что тройняшки имеют размер (batch_size, layer_in_dim, layer_out_dim, 3) , но разве он не должен быть больше этого? Если мы добавляем все тройки из всех слоев. Если только я чего-то не понял неправильно.

2. Если вы хотите иметь правильный градиент w.r.t triplets , вам нужно running_x . summary уже включает в себя все running_x . Кроме того, у нас есть triplets для каждого слоя (я уже упоминал; см. Основной логический for цикл). Мы не можем упаковать все triplets для всех слоев, потому что они имеют разные размеры.

3. .. таким образом, в основном мы получаем список (длина = # слоев) тензоров (т. Е. triplets ). Я не создавал этот список явно в своем коде. Я уверен, что вы можете сделать это сами.

4. Разные размеры? Каждая тройка имеет размер 3. Немного запуталась

5. Нет, но у вас разное количество таких тройняшек. Вы сами написали в вопросе, что «2 для первого слоя, 6 для 2-го слоя, 3 для третьего слоя».