De la rezolvarea ecuațiilor la învățarea profundă: un tutorial TensorFlow Python
Publicat: 2022-03-11Au existat câteva evoluții remarcabile în ultima vreme în lumea inteligenței artificiale, de la progresul mult mediatizat cu mașinile cu conducere autonomă până la mașini care compun acum imitații Chopin sau pur și simplu sunt foarte buni la jocurile video.
În centrul acestor progrese se află o serie de instrumente pentru a ajuta la obținerea învățării profunde și a altor modele de învățare automată, cu Torch, Caffe și Theano printre cele în prim-plan. Cu toate acestea, de când Google Brain a devenit open source în noiembrie 2015 cu propriul cadru, TensorFlow, am văzut că popularitatea acestei biblioteci de software a crescut vertiginos pentru a fi cel mai popular cadru de învățare profundă.
De ce sa întâmplat asta? Motivele includ multitudinea de asistență și documentație disponibilă, pregătirea pentru producție, ușurința de a distribui calculele pe o gamă largă de dispozitive și un instrument excelent de vizualizare: TensorBoard.
În cele din urmă, TensorFlow reușește să combine un set cuprinzător și flexibil de caracteristici tehnice cu o mare ușurință de utilizare.
În acest articol, veți obține o înțelegere a mecanicii acestui instrument utilizându-l pentru a rezolva o problemă numerică generală, destul de în afara ceea ce implică de obicei învățarea automată, înainte de a introduce utilizările sale în învățarea profundă cu o simplă implementare a rețelei neuronale.
Înainte de a începe
Se presupune o cunoaștere de bază a metodelor de învățare automată. Dacă aveți nevoie să vă recuperați, consultați această postare foarte utilă.
Pe măsură ce vom demonstra API-ul Python, înțelegerea Numpy este, de asemenea, benefică.
Pentru a configura TensorFlow, urmați instrucțiunile găsite aici.
Dacă utilizați Windows, trebuie remarcat că, la momentul scrierii, trebuie să utilizați Python 3.4+, nu 2.7.
Apoi, când sunteți gata, ar trebui să puteți importa biblioteca cu:
import tensorflow as tf
Pasul 1 din 2 la o soluție TensorFlow: Creați un grafic
Construcția programelor TensorFlow constă, în general, în doi pași majori, primul dintre care este construirea unui grafic de calcul, care va descrie calculele pe care doriți să le efectuați, dar să nu le efectuați sau să păstrați nicio valoare.
Ca și în cazul oricărui grafic, avem noduri și muchii. Muchiile reprezintă tensori, un tensor reprezentând o matrice n-dimensională. De exemplu, un tensor cu dimensiunea (sau rangul în TensorFlow speak) 0 este un scalar, rangul 1 un vector, rangul 2 o matrice și așa mai departe.
Nodurile reprezintă operații care produc un tensor de ieșire, luând tensori ca intrări dacă este necesar. Astfel de operații includ adunări ( tf.add
), înmulțiri de matrice ( tf.matmul
) și, de asemenea, crearea de constante ( tf.constant
).
Deci, să combinăm câteva dintre acestea pentru primul nostru grafic.
a = tf.constant([2.5, 2]) b = tf.constant([3, 6], dtype=tf.float32) total = tf.add(a, b)
Aici am creat trei operațiuni, două dintre ele pentru a crea tablouri constante 1-d.
Tipurile de date sunt deduse din argumentul de valori transmis sau le puteți denota cu argumentul dtype
. Dacă nu aș fi făcut acest lucru pentru b
, atunci un int32
ar fi fost dedus și o eroare aruncată ca tf.add
ar fi încercat să definească o adăugare pe două tipuri diferite.
Pasul 2 din 2 la o soluție TensorFlow: Execută operațiunile
Graficul este definit, dar pentru a face efectiv calcule asupra lui (sau a oricărei părți a acestuia) trebuie să setăm o Sesiune TensorFlow.
sess = tf.Session()
Alternativ, dacă rulăm o sesiune într-un shell interactiv, cum ar fi IPython, atunci folosim:
sess = tf.InteractiveSession()
Metoda run
a obiectului sesiune este o modalitate de a evalua un Tensor.
Prin urmare, pentru a evalua calculul de adunare definit mai sus, trecem „total”, Tensorul de preluat, care reprezintă rezultatul tf.add
.
print(sess.run(total)) # [ 5.5 8. ]
În acest moment, introducem clasa Variable a lui TensorFlow. În timp ce constantele sunt o parte fixă a definiției graficului, variabilele pot fi actualizate. Constructorul clasei necesită o valoare inițială, dar chiar și atunci, variabilele au nevoie de o operație pentru a le inițializa în mod explicit înainte ca orice alte operații asupra lor să fie efectuate.
Variabilele dețin starea graficului într-o anumită sesiune, așa că ar trebui să observăm ce se întâmplă cu mai multe sesiuni folosind același grafic pentru a înțelege mai bine variabilele.
# Create a variable with an initial value of 1 some_var = tf.Variable(1) # Create op to run variable initializers init_op = tf.global_variables_initializer() # Create an op to replace the value held by some_var to 3 assign_op = some_var.assign(3) # Set up two instances of a session sess1 = tf.Session() sess2 = tf.Session() # Initialize variables in both sessions sess1.run(init_op) sess2.run(init_op) print(sess1.run(some_var)) # Outputs 1 # Change some_var in session1 sess1.run(assign_op) print(sess1.run(some_var)) # Outputs 3 print(sess2.run(some_var)) # Outputs 1 # Close sessions sess1.close() sess2.close()
Am configurat graficul și două sesiuni.
După ce am executat inițializarea pe ambele sesiuni (dacă nu rulăm aceasta și apoi evaluăm variabila, am lovit o eroare) executăm opțiunea de atribuire doar pe o singură sesiune. După cum se poate vedea, valoarea variabilei persistă, dar nu între sesiuni.
Alimentarea graficului pentru a aborda problemele numerice
Un alt concept important al TensorFlow este substituentul. În timp ce variabilele dețin starea, substituenții sunt utilizați pentru a defini ce intrări se poate aștepta graficul și tipul lor de date (și opțional forma lor). Apoi putem introduce date în grafic prin intermediul acestor substituenți atunci când rulăm calculul.
Graficul TensorFlow începe să semene cu rețelele neuronale pe care vrem să le antrenăm în cele din urmă, dar înainte de asta, să folosim conceptele pentru a rezolva o problemă numerică comună din lumea financiară.
Să presupunem că vrem să găsim y
într-o ecuație ca aceasta:
pentru un v
dat (cu C
și P
constante).
Aceasta este o formulă pentru calcularea randamentului până la scadență ( y
) pentru o obligațiune cu valoarea de piață v
, principalul P
și cuponul C
plătite semestrial, dar cu fluxurile de numerar actualizate cu capitalizare continuă.
Practic, trebuie să rezolvăm o ecuație ca aceasta cu încercare și eroare și vom alege metoda bisecției la zero pe valoarea noastră finală pentru y
.
În primul rând, vom modela această problemă ca un grafic TensorFlow.
C
și P
sunt constante fixe și fac parte din definiția graficului nostru. Dorim să avem un proces care rafinează limitele inferioare și superioare ale lui y
. Prin urmare, aceste limite (notate a
și b
) sunt candidați buni pentru variabilele care trebuie modificate după fiecare estimare a lui y
(luat ca fiind punctul de mijloc al lui a
și b
).
# Specify the values our constant ops will output C = tf.constant(5.0) P = tf.constant(100.0) # We specify the initial values that our lower and upper bounds will be when initialised. # Obviously the ultimate success of this algorithm depends on decent start points a = tf.Variable(-10.0) b = tf.Variable(10.0) # We expect a floating number to be inserted into the graph v_target = tf.placeholder("float") # Remember the following operations are definitions, # none are carried out until an operation is evaluated in a session! y = (a+b)/2 v_guess = C*tf.exp(-0.5*y) + C*tf.exp(-y) + C*tf.exp(-1.5*y) + (C + P)*tf.exp(-2*y) # Operations to set temporary values (a_ and b_) intended to be the next values of a and b. # eg if the guess results in av greater than the target v, # we will set a_ to be the current value of y a_ = tf.where(v_guess > v_target, y, a) b_ = tf.where(v_guess < v_target, y, b) # The last stage of our graph is to assign the two temporary vals to our variables step = tf.group( a.assign(a_), b.assign(b_) )
Deci acum avem o listă de operațiuni și variabile, oricare dintre acestea poate fi evaluată în raport cu o anumită sesiune. Unele dintre aceste operațiuni se bazează pe alte operațiuni care urmează să fie executate, așa că rularea, să zicem, v_guess
va declanșa o reacție în lanț pentru a avea alți tensori, cum ar fi C
și P
, să fie evaluați mai întâi.

Unele dintre aceste operațiuni se bazează pe un substituent pentru care trebuie specificată o valoare, deci cum alimentăm de fapt acea valoare?
Acest lucru se face prin argumentul feed_dict
din funcția de run
în sine.
Dacă dorim să evaluăm a_
, introducem valoarea pentru substituentul nostru v_target
, astfel:
sess.run(a_, feed_dict={v_target: 100})
oferindu-ne 0.0.
Conectați un v_target
de 130 și obținem -10.0.
Este operația noastră „pas” care de fapt necesită ca toate celelalte operațiuni să fie efectuate ca o condiție prealabilă și, de fapt, execută întregul grafic. Este, de asemenea, o operațiune care schimbă de fapt starea reală în sesiunea noastră. Prin urmare, cu cât parcurgem mai mult pasul, cu atât deplasăm mai mult variabilele noastre a
și b
către valoarea reală a lui y
.
Deci, să presupunem că valoarea noastră pentru v
din ecuația noastră este egală cu 95. Să setăm o sesiune și să executăm graficul nostru pe ea de 100 de ori.
# Set up a session and initialize variables sess = tf.Session() tf.global_variables_initializer().run() # Run the step operation (and therefore whole graph) 100 times for i in range (100): sess.run(step, feed_dict={v_target:95})
Dacă evaluăm tensorul y
acum, obținem ceva care seamănă cu un răspuns dezirabil
print(sess.run(y)) # 0.125163
Rețele neuronale
Acum că avem o înțelegere a mecanicii lui TensorFlow, putem aduce acest lucru împreună cu câteva operațiuni suplimentare de învățare automată încorporate în TensorFlow pentru a antrena o rețea neuronală simplă.
Aici, am dori să clasificăm punctele de date pe un sistem de coordonate 2d, în funcție de faptul că acestea se încadrează într-o anumită regiune - un cerc cu raza 0,5 centrat la origine.
Desigur, acest lucru poate fi verificat în mod concret verificând doar un anumit punct (a,b)
dacă a^2 + b^2 < 0.5
, dar în scopul acestui experiment de învățare automată, am dori în schimb să trecem un set de antrenament: o serie de puncte aleatoare și dacă acestea se încadrează în regiunea dorită. Iată o modalitate de a crea asta:
import numpy as np NO_OF_RANDOM_POINTS = 100 CIRCLE_RADIUS = 0.5 random_spots = np.random.rand(NO_OF_RANDOM_POINTS, 2) * 2 - 1 is_inside_circle = (np.power(random_spots[:,0],2) + np.power(random_spots[:,1],2) < CIRCLE_RADIUS).astype(int)
Vom realiza o rețea neuronală cu următoarele caracteristici:
- Constă dintr-un strat de intrare cu două noduri, în care alimentăm seria noastră de vectori bidimensionali conținute în „random_spots”. Acesta va fi reprezentat de un substituent care așteaptă datele de antrenament.
- Stratul de ieșire va avea, de asemenea, două noduri, așa că trebuie să introducem seria noastră de etichete de antrenament („is_inside_circle”) într-un substituent pentru un scalar și apoi să convertim acele valori într-un vector bidimensional unic.
- Vom avea un strat ascuns format din trei noduri, așa că va trebui să folosim variabile pentru matricele noastre de ponderi și vectorii de polarizare, deoarece acestea sunt valorile care trebuie să fie rafinate atunci când se efectuează antrenamentul.
INPUT_LAYER_SIZE = 2 HIDDEN_LAYER_SIZE = 3 OUTPUT_LAYER_SIZE = 2 # Starting values for weights and biases are drawn randomly and uniformly from [-1, 1] # For example W1 is a matrix of shape 2x3 W1 = tf.Variable(tf.random_uniform([INPUT_LAYER_SIZE, HIDDEN_LAYER_SIZE], -1, 1)) b1 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE], -1, 1)) W2 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE, OUTPUT_LAYER_SIZE], -1, 1)) b2 = tf.Variable(tf.random_uniform([OUTPUT_LAYER_SIZE], -1, 1)) # Specifying that the placeholder X can expect a matrix of 2 columns (but any number of rows) # representing random spots X = tf.placeholder(tf.float32, [None, INPUT_LAYER_SIZE]) # Placeholder Y can expect integers representing whether corresponding point is in the circle # or not (no shape specified) Y = tf.placeholder(tf.uint8) # An op to convert to a one hot vector onehot_output = tf.one_hot(Y, OUTPUT_LAYER_SIZE)
Pentru a finaliza definiția graficului nostru, definim câteva operațiuni care ne vor ajuta să antrenăm variabilele pentru a ajunge la un clasificator mai bun. Acestea includ calculele matriceale, funcțiile de activare și optimizatorul.
LEARNING_RATE = 0.01 # Op to perform matrix calculation X*W1 + b1 hidden_layer = tf.add(tf.matmul(X, W1), b1) # Use sigmoid activation function on the outcome activated_hidden_layer = tf.sigmoid(hidden_layer) # Apply next weights and bias (W2, b2) to hidden layer and then apply softmax function # to get our output layer (each vector adding up to 1) output_layer = tf.nn.softmax(tf.add(tf.matmul(activated_hidden_layer, W2), b2)) # Calculate cross entropy for our loss function loss = -tf.reduce_sum(onehot_output * tf.log(output_layer)) # Use gradient descent optimizer at specified learning rate to minimize value given by loss tensor train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)
După ce ne-am configurat graficul, este timpul să configurați o sesiune și să rulați „train_step” (care rulează, de asemenea, orice operațiuni prealabile). Unele dintre aceste operațiuni folosesc substituenți, așa că trebuie furnizate valori pentru acestea. Acest pas de antrenament reprezintă o epocă în algoritmul nostru de învățare și, ca atare, este în buclă peste numărul de epoci pe care dorim să le rulăm. Putem rula alte părți ale graficului, cum ar fi tensorul „pierderii” în scopuri informaționale.
EPOCH_COUNT = 1000 sess = tf.Session() tf.global_variables_initializer().run() for i in range(EPOCH_COUNT): if i%100 == 0: print('Loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle}))) sess.run(train_step, feed_dict={X: random_spots, Y: is_inside_circle}) print('Final loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle})))
Odată ce am antrenat algoritmul, putem alimenta un punct și obținem rezultatul rețelei neuronale astfel:
sess.run(output_layer, feed_dict={X: [[1, 1]]}) # Hopefully something close to [1, 0] sess.run(output_layer, feed_dict={X: [[0, 0]]}) # Hopefully something close to [0, 1]
Putem clasifica punctul ca în afara cercului dacă primul membru al vectorului de ieșire este mai mare de 0,5, în interior altfel.
Prin rularea tensorului output_layer
pentru mai multe puncte, ne putem face o idee despre modul în care elevul ia în considerare regiunea care conține punctele clasificate pozitiv. Merită să vă jucați cu dimensiunea setului de antrenament, rata de învățare și alți parametri pentru a vedea cât de aproape ne putem apropia de cercul pe care ne-am propus.
Rata de învățare: 0,01
Epoci: 1000
Rata de învățare: 0,01
Epoci: 1000
Rata de învățare: 0,01
Epoci: 10000
Rata de învățare: 0,001
Epoci: 10000
Învelire
Aceasta este o lecție bună că un set de antrenament crescut sau o sumă de epocă nu este o garanție pentru un cursant bun - rata de învățare ar trebui ajustată în mod corespunzător.
Sperăm că aceste demonstrații v-au oferit o perspectivă bună asupra principiilor de bază ale TensorFlow și oferă o bază solidă în care să implementați tehnici mai complexe.
Nu am acoperit concepte precum Tensorboard sau nu am pregătit modelele noastre pe GPU-uri, dar acestea sunt bine acoperite în documentația TensorFlow. O serie de rețete pot fi găsite în documentație care vă pot ajuta să vă puneți la curent cu abordarea sarcinilor interesante de învățare profundă folosind acest cadru puternic!