Inicialmentecomeçamos a aprender sobre torch
noções básicas codificando uma rede neural simples do zero, usando apenas um dos torch
características de:
tensores.
Entãosimplificamos imensamente a tarefa, substituindo a retropropagação handbook por
autograduação. Hoje, nós modularizar rede – tanto no sentido routine quanto no literal: operações matriciais de baixo nível são trocadas por torch
module
S.
Módulos
De outras estruturas (Keras, por exemplo), você pode estar acostumado a distinguir entre modelos e camadas. Em torch
ambos são exemplos de
nn_Module()
e, portanto, têm alguns métodos em comum. Para aqueles que pensam em termos de “modelos” e “camadas”, estou dividindo artificialmente esta seção em duas partes. Na realidade, porém, não existe dicotomia: novos módulos podem ser compostos de módulos existentes até níveis arbitrários de recursão.
Módulos básicos (“camadas”)
Em vez de escrever uma operação afim à mão – x$mm(w1) + b1
digamos –, como temos feito até agora, podemos criar um módulo linear. O trecho a seguir instancia uma camada linear que espera entradas de três recursos e retorna uma única saída por observação:
O módulo possui dois parâmetros, “peso” e “viés”. Ambos agora vêm pré-inicializados:
$weight
torch_tensor
-0.0385 0.1412 -0.5436
( CPUFloatType{1,3} )
$bias
torch_tensor
-0.1950
( CPUFloatType{1} )
Os módulos podem ser chamados; chamar um módulo executa seu ahead()
método, que, para uma camada linear, multiplica a matriz de entrada e pesos e adiciona o viés.
Vamos tentar isso:
information <- torch_randn(10, 3)
out <- l(information)
Sem surpresa, out
agora contém alguns dados:
torch_tensor
0.2711
-1.8151
-0.0073
0.1876
-0.0930
0.7498
-0.2332
-0.0428
0.3849
-0.2618
( CPUFloatType{10,1} )
Além disso, este tensor sabe o que precisará ser feito, caso seja solicitado a calcular gradientes:
AddmmBackward
Observe a diferença entre tensores retornados por módulos e aqueles autocriados. Ao criarmos tensores nós mesmos, precisamos passar
requires_grad = TRUE
para acionar o cálculo do gradiente. Com módulos,
torch
assume corretamente que desejaremos realizar a retropropagação em algum momento.
Até agora, porém, não ligamos backward()
ainda. Assim, nenhum gradiente ainda foi calculado:
l$weight$grad
l$bias$grad
torch_tensor
( Tensor (undefined) )
torch_tensor
( Tensor (undefined) )
Vamos mudar isso:
Error in (operate (self, gradient, keep_graph, create_graph) :
grad will be implicitly created just for scalar outputs (_make_grads at ../torch/csrc/autograd/autograd.cpp:47)
Por que o erro? Autogradação espera que o tensor de saída seja escalar, enquanto em nosso exemplo, temos um tensor de tamanho (10, 1)
. Este erro não ocorrerá com frequência na prática, onde trabalhamos com lotes de insumos (às vezes, apenas um único lote). Mesmo assim, é interessante ver como resolver isso.
Para fazer o exemplo funcionar, introduzimos uma etapa de agregação remaining – digital – calculando a média, digamos. Vamos chamá-lo avg
. Se tal média fosse tomada, seu gradiente em relação a l$weight
seria obtido através da regra da cadeia:
( start{equação*} frac{partial avg}{partial w} = frac{partial avg}{partial out} frac{partial out}{partial w} finish{equação*} )
Das quantidades do lado direito, estamos interessados na segunda. Precisamos fornecer o primeiro, como ficaria se realmente estivéssemos entendendo o que é mau:
d_avg_d_out <- torch_tensor(10)$`repeat`(10)$unsqueeze(1)$t()
out$backward(gradient = d_avg_d_out)
Agora, l$weight$grad
e l$bias$grad
fazer contém gradientes:
l$weight$grad
l$bias$grad
torch_tensor
1.3410 6.4343 -30.7135
( CPUFloatType{1,3} )
torch_tensor
100
( CPUFloatType{1} )
Além de nn_linear()
, torch
fornece praticamente todas as camadas comuns que você poderia esperar. Mas poucas tarefas são resolvidas por uma única camada. Como você os combina? Ou, no jargão traditional: Como você constrói
modelos?
Módulos de contêiner (“modelos”)
Agora, modelos são apenas módulos que contêm outros módulos. Por exemplo, se todas as entradas devem fluir através dos mesmos nós e ao longo das mesmas arestas, então nn_sequential()
pode ser usado para construir um gráfico simples.
Por exemplo:
mannequin <- nn_sequential(
nn_linear(3, 16),
nn_relu(),
nn_linear(16, 1)
)
Podemos usar a mesma técnica acima para obter uma visão geral de todos os parâmetros do modelo (duas matrizes de pesos e dois vetores de polarização):
$`0.weight`
torch_tensor
-0.1968 -0.1127 -0.0504
0.0083 0.3125 0.0013
0.4784 -0.2757 0.2535
-0.0898 -0.4706 -0.0733
-0.0654 0.5016 0.0242
0.4855 -0.3980 -0.3434
-0.3609 0.1859 -0.4039
0.2851 0.2809 -0.3114
-0.0542 -0.0754 -0.2252
-0.3175 0.2107 -0.2954
-0.3733 0.3931 0.3466
0.5616 -0.3793 -0.4872
0.0062 0.4168 -0.5580
0.3174 -0.4867 0.0904
-0.0981 -0.0084 0.3580
0.3187 -0.2954 -0.5181
( CPUFloatType{16,3} )
$`0.bias`
torch_tensor
-0.3714
0.5603
-0.3791
0.4372
-0.1793
-0.3329
0.5588
0.1370
0.4467
0.2937
0.1436
0.1986
0.4967
0.1554
-0.3219
-0.0266
( CPUFloatType{16} )
$`2.weight`
torch_tensor
Columns 1 to 10-0.0908 -0.1786 0.0812 -0.0414 -0.0251 -0.1961 0.2326 0.0943 -0.0246 0.0748
Columns 11 to 16 0.2111 -0.1801 -0.0102 -0.0244 0.1223 -0.1958
( CPUFloatType{1,16} )
$`2.bias`
torch_tensor
0.2470
( CPUFloatType{1} )
Para inspecionar um parâmetro particular person, make the most of sua posição no modelo sequencial. Por exemplo:
torch_tensor
-0.3714
0.5603
-0.3791
0.4372
-0.1793
-0.3329
0.5588
0.1370
0.4467
0.2937
0.1436
0.1986
0.4967
0.1554
-0.3219
-0.0266
( CPUFloatType{16} )
E assim como nn_linear()
acima, este módulo pode ser chamado diretamente nos dados:
Em um módulo composto como este, chamar backward()
irá retropropagar através de todas as camadas:
out$backward(gradient = torch_tensor(10)$`repeat`(10)$unsqueeze(1)$t())
# e.g.
mannequin((1))$bias$grad
torch_tensor
0.0000
-17.8578
1.6246
-3.7258
-0.2515
-5.8825
23.2624
8.4903
-2.4604
6.7286
14.7760
-14.4064
-1.0206
-1.7058
0.0000
-9.7897
( CPUFloatType{16} )
E colocar o módulo composto na GPU moverá todos os tensores para lá:
mannequin$cuda()
mannequin((1))$bias$grad
torch_tensor
0.0000
-17.8578
1.6246
-3.7258
-0.2515
-5.8825
23.2624
8.4903
-2.4604
6.7286
14.7760
-14.4064
-1.0206
-1.7058
0.0000
-9.7897
( CUDAFloatType{16} )
Agora vamos ver como usar nn_sequential()
pode simplificar nossa rede de exemplo.
Rede simples usando módulos
### generate coaching information -----------------------------------------------------
# enter dimensionality (variety of enter options)
d_in <- 3
# output dimensionality (variety of predicted options)
d_out <- 1
# variety of observations in coaching set
n <- 100
# create random information
x <- torch_randn(n, d_in)
y <- x(, 1, NULL) * 0.2 - x(, 2, NULL) * 1.3 - x(, 3, NULL) * 0.5 + torch_randn(n, 1)
### outline the community ---------------------------------------------------------
# dimensionality of hidden layer
d_hidden <- 32
mannequin <- nn_sequential(
nn_linear(d_in, d_hidden),
nn_relu(),
nn_linear(d_hidden, d_out)
)
### community parameters ---------------------------------------------------------
learning_rate <- 1e-4
### coaching loop --------------------------------------------------------------
for (t in 1:200) {
### -------- Ahead go --------
y_pred <- mannequin(x)
### -------- compute loss --------
loss <- (y_pred - y)$pow(2)$sum()
if (t %% 10 == 0)
cat("Epoch: ", t, " Loss: ", loss$merchandise(), "n")
### -------- Backpropagation --------
# Zero the gradients earlier than working the backward go.
mannequin$zero_grad()
# compute gradient of the loss w.r.t. all learnable parameters of the mannequin
loss$backward()
### -------- Replace weights --------
# Wrap in with_no_grad() as a result of this can be a half we DON'T need to report
# for computerized gradient computation
# Replace every parameter by its `grad`
with_no_grad({
mannequin$parameters %>% purrr::stroll(operate(param) param$sub_(learning_rate * param$grad))
})
}
O passe para frente parece muito melhor agora; no entanto, ainda percorremos os parâmetros do modelo e atualizamos cada um deles manualmente. Além disso, você já deve estar suspeitando que torch
fornece abstrações para funções de perda comuns. Na próxima e última parte desta série, abordaremos ambos os pontos, fazendo uso de torch
perdas e otimizadores. Até lá!