Я пытаюсь заполнить GRU / LSTM вручную определенными параметрами в pytorch.

У меня есть массивы numpy для параметров с формами, как определено в их документации (https: / /pytorch.org/docs/stable/nn.html#torch.nn.GRU ) .

Кажется, работает, но я не уверен, верны ли возвращаемые значения.

Это правильный способ заполнить GRU / LSTM с параметрами numpy?

gru = nn.GRU(input_size, hidden_size, num_layers,
              bias=True, batch_first=False, dropout=dropout, bidirectional=bidirectional)

def set_nn_wih(layer, parameter_name, w, l0=True):
    param = getattr(layer, parameter_name)
    if l0:
        for i in range(3*hidden_size):
            param.data[i] = w[i*input_size:(i+1)*input_size]
    else:
        for i in range(3*hidden_size):
            param.data[i] = w[i*num_directions*hidden_size:(i+1)*num_directions*hidden_size]

def set_nn_whh(layer, parameter_name, w):
    param = getattr(layer, parameter_name)
    for i in range(3*hidden_size):
        param.data[i] = w[i*hidden_size:(i+1)*hidden_size]

l0=True

for i in range(num_directions):
    for j in range(num_layers):
        if j == 0:
            wih = w0[i, :, :3*input_size]
            whh = w0[i, :, 3*input_size:]  # check
            l0=True
        else:
            wih = w[j-1, i, :, :num_directions*3*hidden_size]
            whh = w[j-1, i, :, num_directions*3*hidden_size:]
            l0=False

        if i == 0:
            set_nn_wih(
                gru, "weight_ih_l{}".format(j), torch.from_numpy(wih.flatten()),l0)
            set_nn_whh(
                gru, "weight_hh_l{}".format(j), torch.from_numpy(whh.flatten()))
        else:
            set_nn_wih(
                gru, "weight_ih_l{}_reverse".format(j), torch.from_numpy(wih.flatten()),l0)
            set_nn_whh(
                gru, "weight_hh_l{}_reverse".format(j), torch.from_numpy(whh.flatten()))

y, hn = gru(x_t, h_t)

Numpy массивы определяются следующим образом:

rng = np.random.RandomState(313)
w0 = rng.randn(num_directions, hidden_size, 3*(input_size +
               hidden_size)).astype(np.float32)
w = rng.randn(max(1, num_layers-1), num_directions, hidden_size,
              3*(num_directions*hidden_size + hidden_size)).astype(np.float32)
15
ytrewq 23 Окт 2018 в 12:17

2 ответа

Лучший ответ

Это хороший вопрос, и вы уже дали достойный ответ. Однако он изобретает колесо - существует очень элегантная внутренняя процедура Pytorch, которая позволит вам делать то же самое без особых усилий - и она применима к любой сети.

Ключевой концепцией здесь является state_dict PyTorch. Словарь состояний фактически содержит parameters, организованный древовидной структурой, заданной взаимосвязью nn.Modules и их подмодулей и т. Д.

Краткий ответ

Если вы хотите, чтобы код загружал значение в тензор только с помощью state_dict, попробуйте эту строку (где dict содержит действительный state_dict):

`model.load_state_dict(dict, strict=False)`

Где strict=False имеет решающее значение, если вы хотите загрузить только некоторые значения параметров .

Подробный ответ - включая введение в PyTorch state_dict

Вот пример того, как dict состояния ищет ГРУ (я выбрал input_size = hidden_size = 2, чтобы распечатать весь dict состояния):

rnn = torch.nn.GRU(2, 2, 1)
rnn.state_dict()
# Out[10]: 
#     OrderedDict([('weight_ih_l0', tensor([[-0.0023, -0.0460],
#                         [ 0.3373,  0.0070],
#                         [ 0.0745, -0.5345],
#                         [ 0.5347, -0.2373],
#                         [-0.2217, -0.2824],
#                         [-0.2983,  0.4771]])),
#                 ('weight_hh_l0', tensor([[-0.2837, -0.0571],
#                         [-0.1820,  0.6963],
#                         [ 0.4978, -0.6342],
#                         [ 0.0366,  0.2156],
#                         [ 0.5009,  0.4382],
#                         [-0.7012, -0.5157]])),
#                 ('bias_ih_l0',
#                 tensor([-0.2158, -0.6643, -0.3505, -0.0959, -0.5332, -0.6209])),
#                 ('bias_hh_l0',
#                 tensor([-0.1845,  0.4075, -0.1721, -0.4893, -0.2427,  0.3973]))])

Итак, state_dict все параметры сети. Если у нас есть "вложенный" nn.Modules, мы получаем дерево, представленное именами параметров:

class MLP(torch.nn.Module):      
    def __init__(self):
        torch.nn.Module.__init__(self)
        self.lin_a = torch.nn.Linear(2, 2)
        self.lin_b = torch.nn.Linear(2, 2)


mlp = MLP()
mlp.state_dict()
#    Out[23]: 
#        OrderedDict([('lin_a.weight', tensor([[-0.2914,  0.0791],
#                            [-0.1167,  0.6591]])),
#                    ('lin_a.bias', tensor([-0.2745, -0.1614])),
#                    ('lin_b.weight', tensor([[-0.4634, -0.2649],
#                            [ 0.4552,  0.3812]])),
#                    ('lin_b.bias', tensor([ 0.0273, -0.1283]))])


class NestedMLP(torch.nn.Module):
    def __init__(self):
        torch.nn.Module.__init__(self)
        self.mlp_a = MLP()
        self.mlp_b = MLP()


n_mlp = NestedMLP()
n_mlp.state_dict()
#   Out[26]: 
#        OrderedDict([('mlp_a.lin_a.weight', tensor([[ 0.2543,  0.3412],
#                            [-0.1984, -0.3235]])),
#                    ('mlp_a.lin_a.bias', tensor([ 0.2480, -0.0631])),
#                    ('mlp_a.lin_b.weight', tensor([[-0.4575, -0.6072],
#                            [-0.0100,  0.5887]])),
#                    ('mlp_a.lin_b.bias', tensor([-0.3116,  0.5603])),
#                    ('mlp_b.lin_a.weight', tensor([[ 0.3722,  0.6940],
#                            [-0.5120,  0.5414]])),
#                    ('mlp_b.lin_a.bias', tensor([0.3604, 0.0316])),
#                    ('mlp_b.lin_b.weight', tensor([[-0.5571,  0.0830],
#                            [ 0.5230, -0.1020]])),
#                    ('mlp_b.lin_b.bias', tensor([ 0.2156, -0.2930]))])

Итак - что, если вы хотите не извлекать определение состояния, а изменить его - и тем самым параметры сети? Используйте nn.Module.load_state_dict(state_dict, strict=True) ( ссылку на документы ) Этот метод позволяет вам загрузить весь state_dict с произвольными значениями в экземпляр модели того же типа , если ключи (то есть имена параметров) верны, а значения (то есть параметры) равны { {X1}} правильной формы. Если для параметра strict kwarg установлено значение True (по умолчанию), загружаемый dict должен точно соответствовать исходному dict состояния, за исключением значений параметров. То есть для каждого параметра должно быть одно новое значение.

Для приведенного выше примера с ГРУ нам нужен тензор правильного размера (и правильного устройства, кстати) для каждого из 'weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'. Поскольку иногда мы хотим загрузить только некоторые значения (как я думаю, вы хотите это сделать), мы можем установить kwarg strict на False - и тогда мы сможем загрузить только частичные государственные постановления, например тот, который содержит только значения параметров для 'weight_ih_l0'.

В качестве практического совета я бы просто создал модель, в которую вы хотите загрузить значения, а затем распечатал бы состояние dict (или, по крайней мере, список ключей и соответствующих размеров тензор)

print([k, v.shape for k, v in model.state_dict().items()])

Это говорит вам, какое точное имя параметра вы хотите изменить. Затем вы просто создаете состояние dict с соответствующим именем параметра и тензором и загружаете его:

from dollections import OrderedDict
new_state_dict = OrderedDict({'tensor_name_retrieved_from_original_dict': new_tensor_value})
model.load_state_dict(new_state_dict, strict=False)
10
cleros 20 Авг 2019 в 18:35

Если вы хотите установить определенный вес / смещение (или несколько), мне нравится делать:

model.state_dict()["your_weight_names_here"][:] = torch.Tensor(your_numpy_array)

0
Leo Brueggeman 20 Дек 2019 в 22:42
52945427