Posit AI Weblog: Usando módulos de tocha



Posit AI Weblog: Usando módulos de tocha

Inicialmentecomeçamos a aprender sobre torch noções básicas codificando uma rede neural simples do zero, usando apenas um dos torchcaracterí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 moduleS.

Módulos

De outras estruturas (Keras, por exemplo), você pode estar acostumado a distinguir entre modelos e camadas. Em torchambos 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) + b1digamos –, 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á!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *