Marzo 2019

Volume 34 Numero 3

Il presente articolo è stato tradotto automaticamente.

[Test Run]

Regressione neurale con PyTorch

Dal James McCaffrey

James McCaffreyL'obiettivo di un problema di regressione consiste nel prevedere un singolo valore numerico. Ad esempio, voler stimare il prezzo di una casa in base alla metri quadrati, età, codice postale e così via. In questo articolo illustrato come creare un modello di regressione neurale usando la libreria di codice PyTorch. Il modo migliore per comprendere futuro in questo articolo è esaminiamo il programma di dimostrazione figura 1.  

Regressione neurale usando un'esecuzione di PyTorch Demo
Figura 1 regressione neurale usando un'esecuzione di PyTorch Demo

Il programma demo crea un modello di stima basato su set di dati delle abitazioni Boston, in cui l'obiettivo consiste nello stimare il prezzo mediano casa in uno dei 506 città vicino a Boston. I dati provengono dal 1970s anticipata. Ogni elemento di dati con 13 variabili predittive, ad esempio indice crime della città, numero medio di chat room per ospitare in città e così via. Poiché l'obiettivo consiste nello stimare un singolo valore numerico è il valore di un solo output.

La demo carica 404 elementi di training e test 102 in memoria e quindi crea una rete neurale-(10-10)-1 13. La rete neurale ha due livelli di elaborazione nascosto, ognuno dei quali ha 10 nodi. Il numero di nodi di input e outpui è determinato in base ai dati, ma il numero di livelli nascosti e il numero di nodi in ogni sono gratuiti parametri che devono essere determinati da tentativi ed errori.

La demo esegue il training della rete neurale, ovvero i valori dei pesi e distorsioni che definiscono il comportamento della rete neurale vengono calcolati utilizzando i dati di training, che sono noto l'input corretto e i valori di output. Dopo il training, la demo calcola l'accuratezza del modello sui dati di test (% 75.49, 77 fuori 102 corretta). L'accuratezza dei test è una misura approssimativa delle prestazioni si aspetta il modello da eseguire sui nuovi dati non visti in precedenza.

La demo conclude l'articolo con una stima per la prima città di test. Valori di input non elaborato il 13 sono (0.09266, 34.0,.. 8.67). Quando il modello di regressione neurale è stato eseguito il training, dati normalizzati (ridimensionati in modo che tutti i valori sono compresi tra 0,0 e 1,0) è stati usati, in modo che quando si effettua una stima della demo era necessario usare dati normalizzati, ovvero (0.00097, 0.34,.. 0.191501). Il modello prevede che il prezzo mediano casa è $24,870.07, piuttosto prossimo al prezzo mediano effettivo di $26,400.

Questo articolo si presuppone competenze intermedi o programmazione meglio con un linguaggio C-family e una certa conoscenza di base con machine learning. Il codice completo dimostrativo viene presentato in questo articolo. Il codice sorgente e i due file di dati usati nella demo sono anche disponibili nel download associato. Controllo degli errori è stata rimossa per mantenere le idee principali più chiare possibili.

Installare PyTorch

L'installazione di PyTorch prevede due passaggi principali. Prima di tutto si installa Python e numerosi pacchetti ausiliari necessari, ad esempio NumPy e SciPy, quindi installare PyTorch come un pacchetto Python di componenti aggiuntivi.

Sebbene sia possibile installare Python e i pacchetti necessari per eseguire PyTorch separatamente, è consigliabile installare una distribuzione di Python. Per mia dimostrazione, è stato installato il Anaconda3 5.2.0 distribuzione (che contiene Python 3.6.5) e PyTorch 1.0.0. Se si ha familiarità con Python, tenere presente che l'installazione e la gestione delle dipendenze di pacchetto di componenti aggiuntivi è considerevole.

Dopo l'installazione di Python tramite la distribuzione Anaconda, il pacchetto di PyTorch può essere installato usando la funzione di utilità pip con un file con estensione WhL ("wheel"). PyTorch viene fornito in una versione di solo CPU e una versione GPU. Usato la versione di solo CPU.

Informazioni sui dati

Il set di dati delle abitazioni Boston proviene da un documento di ricerca scritto nel 1978 che studiato inquinamento. È possibile trovare versioni diverse del set di dati in diverse posizioni in Internet. Il primo elemento di dati è:

0.00632, 18.00, 2.310, 0, 0.5380, 6.5750, 65.20
4.0900, 1, 296.0, 15.30, 396.90, 4.98, 24.00

Ogni elemento di dati presenta 14 valori e rappresenta uno dei 506 città vicino Boston. I numeri primi 13 sono i valori delle variabili di predittore e l'ultimo valore è il prezzo mediano casa la città (diviso per 1000). In breve, le variabili di predittore 13 sono: tasso di criminalità Town (città), percentuale molto grandi dimensioni, suddividere in zone per il settore, adiacenza a Charles River, inquinamento, le chat del numero medie per house, informazioni sull'età di casa, distanza e Boston, accessibilità autostrade, percentuale IVA frequenza, ratio allievo e insegnanti proporzione di residenti in nero e percentuale di persone residenti nel low-status.  

Poiché esistono 14 variabili, non è possibile visualizzare il set di dati, ma è possibile ottenere un'idea approssimativa dei dati nel grafico nella figura 2. Il grafico mostra il prezzo mediano casa come una funzione della percentuale di suddividere in zone per settore per gli elementi del set di dati di test 102 Town (città).

Set di dati di Boston parziale Area casa
Figura 2 Boston parziale Area casa Dataset

Quando si lavora con le reti neurali, è necessario codificare i dati non numerici e in modo che i valori di grandi dimensioni, ad esempio un rapporto allievo docente di 20.0, non sovraccaricare i valori di piccole dimensioni, ad esempio la lettura di un inquinamento di 0.538, è necessario normalizzare i dati numerici. La variabile River Charles è un valore categorico archiviato come 0 (Town (città) non è adiacente) o 1 (adiacente). Tali valori sono stati nuovamente codificati come -1 e + 1. Le altre variabili di predittore 12 sono di tipo numeriche. Per ogni variabile, calcolato il valore minimo e il valore massimo, e quindi per ogni valore di x, normalizzato come (x - min) / (numero massimo - minimo). Dopo la normalizzazione min-max, tutti i valori saranno compreso tra 0,0 e 1,0.

I valori mediani casa dati non elaborati sono stati normalizzati già proporzionalmente dividendoli per 1.000, pertanto i valori con l'intervallo compreso 5.0 al 50,0, con più a circa da 25,0. Ho applicato una normalizzazione aggiuntiva dividendo i prezzi per 10 in modo che tutti i prezzi mediani sono stati tra lo 0,5 e 5.0, che rappresenta la maggior parte delle circa 2.5.

Il programma Demo

Il programma di dimostrazione completo, con alcune modifiche di lieve entità per risparmiare spazio, viene presentato nel figura 3. Impostare I rientri in due spazi anziché di solito quattro funzioni per risparmiare spazio. Si noti che usa Python e la ' \' carattere di continuazione di riga. Ho utilizzato il blocco note per modificare un programma, ma molti dei miei colleghi preferisce Visual Studio o Visual Studio Code, che dispongono di un supporto eccellente per Python.

Figura 3 i Boston settore immobiliare programma Demo

# boston_dnn.py
# Boston Area House Price dataset regression
# Anaconda3 5.2.0 (Python 3.6.5), PyTorch 1.0.0
import numpy as np
import torch as T  # non-standard alias
# ------------------------------------------------------------
def accuracy(model, data_x, data_y, pct_close):
  n_items = len(data_y)
  X = T.Tensor(data_x)  # 2-d Tensor
  Y = T.Tensor(data_y)  # actual as 1-d Tensor
  oupt = model(X)       # all predicted as 2-d Tensor
  pred = oupt.view(n_items)  # all predicted as 1-d
  n_correct = T.sum((T.abs(pred - Y) < T.abs(pct_close * Y)))
  result = (n_correct.item() * 100.0 / n_items)  # scalar
  return result 
# ------------------------------------------------------------
class Net(T.nn.Module):
  def __init__(self):
    super(Net, self).__init__()
    self.hid1 = T.nn.Linear(13, 10)  # 13-(10-10)-1
    self.hid2 = T.nn.Linear(10, 10)
    self.oupt = T.nn.Linear(10, 1)
    T.nn.init.xavier_uniform_(self.hid1.weight)  # glorot
    T.nn.init.zeros_(self.hid1.bias)
    T.nn.init.xavier_uniform_(self.hid2.weight)
    T.nn.init.zeros_(self.hid2.bias)
    T.nn.init.xavier_uniform_(self.oupt.weight)
    T.nn.init.zeros_(self.oupt.bias)
  def forward(self, x):
    z = T.tanh(self.hid1(x))
    z = T.tanh(self.hid2(z))
    z = self.oupt(z)  # no activation, aka Identity()
    return z
# ------------------------------------------------------------
def main():
  # 0. Get started
  print("\nBoston regression using PyTorch 1.0 \n")
  T.manual_seed(1);  np.random.seed(1)
  # 1. Load data
  print("Loading Boston data into memory ")
  train_file = ".\\Data\\boston_train.txt"
  test_file = ".\\Data\\boston_test.txt"
  train_x = np.loadtxt(train_file, delimiter="\t",
    usecols=range(0,13), dtype=np.float32)
  train_y = np.loadtxt(train_file, delimiter="\t",
    usecols=[13], dtype=np.float32)
  test_x = np.loadtxt(test_file, delimiter="\t",
    usecols=range(0,13), dtype=np.float32)
  test_y = np.loadtxt(test_file, delimiter="\t",
    usecols=[13], dtype=np.float32)
  # 2. Create model
  print("Creating 13-(10-10)-1 DNN regression model \n")
  net = Net()  # all work done above
  # 3. Train model
  net = net.train()  # set training mode
  bat_size = 10
  loss_func = T.nn.MSELoss()  # mean squared error
  optimizer = T.optim.SGD(net.parameters(), lr=0.01)
  n_items = len(train_x)
  batches_per_epoch = n_items // bat_size
  max_batches = 1000 * batches_per_epoch
  print("Starting training")
  for b in range(max_batches):
    curr_bat = np.random.choice(n_items, bat_size,
      replace=False)
    X = T.Tensor(train_x[curr_bat])
    Y = T.Tensor(train_y[curr_bat]).view(bat_size,1)
    optimizer.zero_grad()
    oupt = net(X)
    loss_obj = loss_func(oupt, Y)
    loss_obj.backward()
    optimizer.step()
    if b % (max_batches // 10) == 0:
      print("batch = %6d" % b, end="")
      print("  batch loss = %7.4f" % loss_obj.item(), end="")
      net = net.eval()
      acc = accuracy(net, train_x, train_y, 0.15)
      net = net.train()
      print("  accuracy = %0.2f%%" % acc)      
  print("Training complete \n")
  # 4. Evaluate model
  net = net.eval()  # set eval mode
  acc = accuracy(net, test_x, test_y, 0.15)
  print("Accuracy on test data = %0.2f%%" % acc)
  # 5. Save model - TODO
  # 6. Use model
  raw_inpt = np.array([[0.09266, 34, 6.09, 0, 0.433, 6.495, 18.4,
    5.4917, 7, 329, 16.1, 383.61, 8.67]], dtype=np.float32)
  norm_inpt = np.array([[0.000970, 0.340000, 0.198148, -1,
    0.098765, 0.562177, 0.159629, 0.396666, 0.260870, 0.270992,
    0.372340, 0.966488, 0.191501]], dtype=np.float32)
  X = T.Tensor(norm_inpt)
  y = net(X)
  print("For a town with raw input values: ")
  for (idx,val) in enumerate(raw_inpt[0]):
    if idx % 5 == 0: print("")
    print("%11.6f " % val, end="")
  print("\n\nPredicted median house price = $%0.2f" %
    (y.item()*10000))
if __name__=="__main__":
  main()

La demo Importa l'intero pacchetto di PyTorch e vi assegna un alias di T. In alternativa è possibile importare solo i moduli e le funzioni necessarie.

La demo definisce una funzione helper denominata accuratezza. Quando si usa un modello di regressione, è presente alcuna definizione intrinseca dell'accuratezza della stima. È necessario definire come chiudere un valore stimato deve essere un valore di destinazione per essere considerata una stima corretta. Il programma demo conta un prezzo stimato casa mediano come corretto se si trova all'interno del 15% del valore reale.

Tutta la logica di controllo per il programma demo è contenuta in una sola funzione principale. L'esecuzione del programma inizia impostando le inizializzazioni casuali NumPy e PyTorch globale in modo che i risultati sia riproducibili.

Il caricamento dei dati in memoria

La demo carica i dati in memoria tramite la funzione loadtxt NumPy:

train_x = np.loadtxt(train_file, delimiter="\t",
  usecols=range(0,13), dtype=np.float32)
train_y = np.loadtxt(train_file, delimiter="\t",
  usecols=[13], dtype=np.float32)
test_x = np.loadtxt(test_file, delimiter="\t",
  usecols=range(0,13), dtype=np.float32)
test_y = np.loadtxt(test_file, delimiter="\t",
  usecols=[13], dtype=np.float32)

Il codice presuppone che i dati si trovano in una sottodirectory denominata dei dati. I dati demo è stata pre-elaborati, suddividendoli in set di training e test. Gestione dei dati non è difficile a livello concettuale, ma è quasi sempre piuttosto che richiedono molto tempo e fastidiosa. Molti dei miei colleghi da usare il pacchetto di pandas (analisi dei dati in Python) per modificare i dati.

La definizione della rete neurale

La demo definisce la rete neurale-(10-10)-1 13 in una classe definito dal programma denominata Net che eredita da nn. Modulo. La funzione __init__ Python è possibile pensare come un costruttore di classe. Si noti che non definire in modo esplicito un livello di input perché i valori di input vengono inseriti direttamente al primo livello nascosto.

La rete ha (13 * 10) + (10 * 10) + (10 * 1) = 240 pesi. Ogni peso viene inizializzato su un valore ridotto casuale usando l'algoritmo di Xavier uniforme. La rete ha 10 + 10 + 1 = 21 distorsioni. Ogni valore della deviazione viene inizializzato su zero.

La funzione di inoltro Net classe definisce come i livelli di calcolo di output. La demo viene utilizzata un'attivazione (tangente iperbolica) tanh su due livelli nascosti e Nessuna attivazione nel livello di output:

def forward(self, x):
  z = T.tanh(self.hid1(x))
  z = T.tanh(self.hid2(z))
  z = self.oupt(z)
  return z

Per l'attivazione del livello nascosto, l'alternativa principale è l'attivazione di unità lineare rettificato (ReLU), ma esistono molte altre funzioni.

Poiché PyTorch lavora a un livello relativamente basso di astrazione, esistono molti modelli di progettazione alternativo, che è possibile usare. Ad esempio, invece di definire una classe Net con __init__ e funzioni di inoltrare e quindi creare un'istanza con net = Net(), è possibile usare la funzione sequenza, come illustrato di seguito:

net = T.nn.Sequential(
  T.nn.Linear(13,10),
  T.nn.Tanh(),
  T.nn.Linear(10,10),
  T.nn.Tanh(),
  T.nn.Linear(10,1))

L'approccio sequenziale è molto più semplice, ma si noti che non hai un controllo diretto sugli algoritmi di inizializzazione peso e distorsione. Dopo avere acquisito familiarità con la libreria, la massima flessibilità che si ottiene quando si usa PyTorch è un vantaggio.

Training del modello

Training del modello inizia con queste sette istruzioni:

net = net.train()  # Set training mode
bat_size = 10
loss_func = T.nn.MSELoss()  # Mean squared error
optimizer = T.optim.SGD(net.parameters(), lr=0.01)
n_items = len(train_x)
batches_per_epoch = n_items // bat_size
max_batches = 1000 * batches_per_epoch

PyTorch presenta due modalità: eseguire il training e valutazione. La modalità predefinita è eseguire il training, ma nella mio parere è buona norma impostata in modo esplicito la modalità. Le dimensioni del batch (spesso chiamati mini-batch) sono degli iperparametri. Per un problema di regressione, significa dell'errore quadratico medio è la funzione di perdita più comune. L'algoritmo del gradiente stocastica (SGD) è la tecnica più elementare e in molte situazioni l'algoritmo di Adam produce i risultati migliori.

Il programma demo utilizza un approccio semplice per gli elementi di training di batch. Per la demo, esistono circa 400 training elementi, quindi, se le dimensioni del batch sono 10, in Media visita ogni elemento di training una sola volta (in genere denominato un periodo nella terminologia di machine learning) richiederà 400 / 10 = 40 invia in batch. Pertanto, per eseguire il training pari a 1.000 epochs, il programma demo deve 1000 * 40 = 40.000 batch.

Le istruzioni di training core sono:

for b in range(max_batches):
  curr_bat = np.random.choice(n_items, bat_size,
    replace=False)
  X = T.Tensor(train_x[curr_bat])
  Y = T.Tensor(train_y[curr_bat]).view(bat_size,1)
  optimizer.zero_grad()
  oupt = net(X)
  loss_obj = loss_func(oupt, Y)
  loss_obj.backward()  # Compute gradients
  optimizer.step()     # Update weights and biases

La funzione scelta Seleziona i 10 indici casuale dagli elementi disponibile formazione 404. Gli elementi vengono convertiti in partendo da matrici NumPy PyTorch tensors. È possibile considerare un tensore come una matrice multidimensionale che può essere elaborata in modo efficiente per una GPU (anche se la demo non usufruire di una GPU). La funzione di visualizzazione denominata stranamente rimodella i valori di destinazione unidimensionale in una tensore bidimensionale. Convertendo matrici NumPy tensors PyTorch e gestione di array e tensore forme è delle principali sfide quando si lavora con PyTorch.

Una volta ogni 4.000 batch verrà visualizzato il programma demo il valore della media al quadrato perdita di errore per il batch corrente di 10 elementi di training e l'accuratezza della stima del modello, usando la correnti pesi e distorsioni l'intero training 404-item set:

if b % (max_batches // 10) == 0:
  print("batch = %6d" % b, end="")
  print("  batch loss = %7.4f" % loss_obj.item(), end="")
  net = net.eval()
  acc = accuracy(net, train_x, train_y, 0.15)
  net = net.train()
  print("  accuracy = %0.2f%%" % acc)

Il "/ /" operatore è la divisione intera in Python. Prima di chiamare la funzione di accuratezza definito dal programma, la demo imposta la rete in modalità di valutazione. Tecnicamente, questo non è necessario perché train e modalità di valutazione solo dare risultati diversi se la rete Usa la normalizzazione dei batch di dropout o un livello.

La valutazione e usando il modello con training

Al termine del training, il programma demo valuta l'accuratezza della stima del modello su set di dati di test:

net = net.eval()  # set eval mode
acc = accuracy(net, test_x, test_y, 0.15)
print("Accuracy on test data = %0.2f%%" % acc)

La funzione eval restituisce un riferimento al modello in cui è applicato, è stato possibile sono stati chiamato senza l'istruzione di assegnazione.

Nella maggior parte dei casi, dopo il training di un modello si desidera salvare il modello per un uso successivo. Salvataggio di un modello con training PyTorch è di tipo bit all'esterno dell'ambito di questo articolo, ma è possibile trovare alcuni esempi nella documentazione di PyTorch.

Lo scopo di training di un modello di regressione è utilizzarlo per effettuare una stima. Il programma demo effettua una stima utilizzando il primo elemento di dati dagli elementi di test 102:

raw_inpt = np.array([[0.09266, 34, 6.09, 0, 0.433, 6.495, 18.4,
  5.4917, 7, 329, 16.1, 383.61, 8.67]], dtype=np.float32)
norm_inpt = np.array([[0.000970, 0.340000, 0.198148, -1,
  0.098765, 0.562177, 0.159629, 0.396666, 0.260870, 0.270992,
  0.372340, 0.966488, 0.191501]], dtype=np.float32)

Quando si dispone di nuovi dati, è necessario normalizzare i valori di predittore nello stesso modo che i dati di training è stati normalizzati. Per la normalizzazione min-max, che significa che è necessario salvare il valore minimo e massimo per tutte le variabili che è stato normalizzato.

La demo è terminata effettuando e visualizzare la stima:

...
  X = T.Tensor(norm_inpt)
  y = net(X)
  print("Predicted = $%0.2f" % (y.item()*10000))
if __name__=="__main__":
  main()

Il valore stimato viene restituito come un tensore con un singolo valore. La funzione item viene utilizzata per accedere al valore in modo che possa essere visualizzato.

Conclusioni

La libreria di PyTorch è un po' meno matura alternative TensorFlow, Keras e CNTK, soprattutto per quanto riguarda l'esempio di codice. Ma tra i miei colleghi, l'uso di PyTorch cresce molto rapidamente. Prevedere questa tendenza per continuare e gli esempi di qualità elevata diventa sempre più disponibili.


Dr. James McCaffreylavora per Microsoft Research Redmond, WA Ha lavorato su diversi prodotti Microsoft fondamentali, tra cui Internet Explorer e Bing. Dr. È possibile contattarlo McCaffrey jamccaff@microsoft.com.

Grazie per i seguenti esperti tecnici Microsoft che ha esaminato in questo articolo: Chris Lee, Ricky Loynd