2016-02-17 15 views
5

Sto ancora lavorando per implementare l'aggiornamento del gradiente mini-batch sulla mia rete neurale siamese. In precedenza avevo un problema di implementazione, ovvero correctly solved here.Torcia/Lua, quale struttura di rete neurale per l'addestramento in mini-batch?

Ora mi sono reso conto che c'era anche un errore nell'architettura della mia rete neurale, che è legato alla mia incompletezza nella comprensione della corretta implementazione.

Finora, ho sempre utilizzato un approccio di discesa gradiente non minibatch, in cui stavo passando gli elementi di allenamento uno alla volta all'aggiornamento del gradiente. Ora, voglio implementare un aggiornamento gradiente tramite mini-batch, iniziando a dire con minibatch composti da N = 2 elementi.

La mia domanda è: come dovrei cambiare l'architettura della mia rete neurale siamese per renderlo in grado di gestire un mini-lotto di N = 2 elementi invece di un singolo elemento?

Questo è il (semplificato) Architettura della mia rete neurale siamese:

nn.Sequential { 
    [input -> (1) -> (2) -> output] 
    (1): nn.ParallelTable { 
    input 
     |`-> (1): nn.Sequential { 
     |  [input -> (1) -> (2) -> output] 
     |  (1): nn.Linear(6 -> 3) 
     |  (2): nn.Linear(3 -> 2) 
     | } 
     |`-> (2): nn.Sequential { 
     |  [input -> (1) -> (2) -> output] 
     |  (1): nn.Linear(6 -> 3) 
     |  (2): nn.Linear(3 -> 2) 
     | } 
     ... -> output 
    } 
    (2): nn.CosineDistance 
} 

ho:

  • 2 identici reti neurali siamese (superiore e inferiore) unità
  • 6 ingresso
  • 3 unità nascoste
  • 2 unità di uscita
  • coseno funzione distanza che confronta l'uscita delle due reti neurali parallele

Ecco il mio codice:

perceptronUpper= nn.Sequential() 
perceptronUpper:add(nn.Linear(input_number, hiddenUnits)) 
perceptronUpper:add(nn.Linear(hiddenUnits,output_number)) 
perceptronLower= perceptronUpper:clone('weight', 'gradWeights', 'gradBias', 
'bias') 

parallel_table = nn.ParallelTable() 
parallel_table:add(perceptronUpper) 
parallel_table:add(perceptronLower) 

perceptron = nn.Sequential() 
perceptron:add(parallel_table) 
perceptron:add(nn.CosineDistance()) 

Questa architettura funziona molto bene se ho una funzione di aggiornamento gradiente che prende 1 elemento; come dovrebbe modificarlo per farlo gestire un minibatch ?

EDIT: probabilmente dovrebbe usare il nn.Sequencer() class, modificando le ultime due righe del mio codice:

perceptron:add(nn.Sequencer(parallel_table)) 
perceptron:add(nn.Sequencer(nn.CosineDistance())). 

Cosa ne pensate?

risposta

2

Ogni modulo nn può funzionare con minibatch. Alcuni funzionano solo con minibatch, ad es. (Spatial)BatchNormalization. Un modulo sa quante dimensioni deve contenere il suo input (diciamo D) e se il modulo riceve un tensore dimensionale D + 1, assume che la prima dimensione sia la dimensione batch. Ad esempio, un'occhiata al nn.Linear module documentation:

L'ingresso tensore proposta in avanti (ingresso) deve essere o un vettore (1D tensore) o matrice (tensore 2D). Se l'input è una matrice, si presume che ciascuna riga sia un campione di input di un determinato batch.

function table_of_tensors_to_batch(tbl) 
    local batch = torch.Tensor(#tbl, unpack(tbl[1]:size():totable())) 
    for i = 1, #tbl do 
     batch[i] = tbl[i] 
    end 
    return batch 
end 

inputs = { 
    torch.Tensor(5):fill(1), 
    torch.Tensor(5):fill(2), 
    torch.Tensor(5):fill(3), 
} 
input_batch = table_of_tensors_to_batch(inputs) 
linear = nn.Linear(5, 2) 
output_batch = linear:forward(input_batch) 

print(input_batch) 
1 1 1 1 1 
2 2 2 2 2 
3 3 3 3 3 
[torch.DoubleTensor of size 3x5] 

print(output_batch) 
0,3128 -1,1384 
0,7382 -2,1815 
1,1637 -3,2247 
[torch.DoubleTensor of size 3x2] 

Ok, ma per quanto riguarda contenitori (nn.Sequential, nn.Paralel, nn.ParallelTable e altri)?Il contenitore stesso non ha a che fare con un input, ma invia l'input (o la sua parte corrispondente) al modulo corrispondente che contiene. ParallelTable, ad esempio, applica semplicemente il modulo membro i-esimo all'elemento di tabella di input i-esimo. Pertanto, se si desidera gestire un batch, ogni input [i] (input è una tabella) deve essere un tensore con la dimensione batch come descritto sopra.

input_number = 5 
output_number = 2 

inputs1 = { 
    torch.Tensor(5):fill(1), 
    torch.Tensor(5):fill(2), 
    torch.Tensor(5):fill(3), 
} 
inputs2 = { 
    torch.Tensor(5):fill(4), 
    torch.Tensor(5):fill(5), 
    torch.Tensor(5):fill(6), 
} 
input1_batch = table_of_tensors_to_batch(inputs1) 
input2_batch = table_of_tensors_to_batch(inputs2) 

input_batch = {input1_batch, input2_batch} 
output_batch = perceptron:forward(input_batch) 

print(input_batch) 
{ 
    1 : DoubleTensor - size: 3x5 
    2 : DoubleTensor - size: 3x5 
} 
print(output_batch) 
0,6490 
0,9757 
0,9947 
[torch.DoubleTensor of size 3] 


target_batch = torch.Tensor({1, 0, 1}) 
criterion = nn.MSECriterion() 
err = criterion:forward(output_batch, target_batch) 
gradCriterion = criterion:backward(output_batch, target_batch) 
perceptron:zeroGradParameters() 
perceptron:backward(input_batch, gradCriterion) 

Perché c'è nn.Sequencer allora? Si può usare invece? Sì, ma è il altamente sconsigliato. Sequencer acquisisce una tabella di sequenza e applica il modulo a ciascun elemento della tabella indipendentemente dall'accelerazione. Inoltre, deve fare copie di quel modulo, quindi tale "modalità batch" è considerevolmente meno efficiente della formazione online (non in gruppo). Sequencer è stato progettato per essere una parte delle reti ricorrenti, non ha senso utilizzarlo nel tuo caso.

+0

Ciao @Alexander, grazie per la risposta. Sto cercando di implementare la tua soluzione, ma rimango bloccato nell'istruzione di aggiornamento gradazione 'perceptron: backward (input_batch, targets)'. 'targets' dovrebbe contenere gli obiettivi del mio allenamento, per esempio' 0,1'. Se 'input_batch' è una lista di 2 DoubleTensor la cui dimensione è 3x5, quali dovrebbero essere le dimensioni corrette di' target'? Grazie –

+0

@ DavideChicco.it, l'obiettivo è ridurre al minimo la distanza tra coppie di input, giusto? Quali sono i tuoi obiettivi allora? Suppongo che sia zero. Da dove proviene '0,1'? –

+0

Sto confrontando coppie di vettori. Ogni vettore è composto da 6 valori reali. Ogni coppia può essere true (target = 1) o false (target = 0). Durante l'allenamento, chiamo 'perceptron: forward (input_batch)' e quindi 'perceptron: zeroGradParameters()' e 'perceptron: backward (input_batch, targets)'. Sto avendo problemi con le dimensioni di 'targets', che devo adattarmi alle nuove impostazioni. Un vettore di #input_batch DoubleTensors di dimensione 1 non funziona, cosa dovrei usare? Grazie –

Problemi correlati