TensorFlow funktsiooni @tf.dekoraatori mõistmine

Sissejuhatus

Treeningtsükli jõudluse parandamine võib masinõppemudelite treenimisel säästa tunde arvutusaega. Üks TensorFlow koodi jõudluse parandamise viise on kasutada tf.function() dekoraator – lihtne üherealine muudatus, mis muudab teie funktsioonid oluliselt kiiremaks.

Selles lühikeses juhendis selgitame, kuidas tf.function() parandab jõudlust ja vaadake mõningaid parimaid tavasid.

Pythoni dekoraatorid ja tf.function()

Pythonis on dekoraator funktsioon, mis muudab teiste funktsioonide käitumist. Oletame näiteks, et kutsute märkmiku lahtris järgmist funktsiooni:

import tensorflow as tf

x = tf.random.uniform(shape=[100, 100], minval=-1, maxval=1, dtype=tf.dtypes.float32)

def some_costly_computation(x):
    aux = tf.eye(100, dtype=tf.dtypes.float32)
    result = tf.zeros(100, dtype = tf.dtypes.float32)
    for i in range(1,100):
        aux = tf.matmul(x,aux)/i
        result = result + aux
    return result

%timeit some_costly_computation(x)
16.2 ms ± 103 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Kui aga kuluka funktsiooni edasi anda a tf.function():

quicker_computation = tf.function(some_costly_computation)
%timeit quicker_computation(x)

Saame quicker_computation() – uus funktsioon, mis toimib palju kiiremini kui eelmine:

4.99 ms ± 139 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

Niisiis, tf.function() modifitseerib some_costly_computation() ja väljastab quicker_computation() funktsiooni. Dekoraatorid muudavad ka funktsioone, nii et see oli loomulik tf.function() ka dekoraator.

Dekoraatori tähistuse kasutamine on sama, mis helistamine tf.function(function):

@tf.function
def quick_computation(x):
  aux = tf.eye(100, dtype=tf.dtypes.float32)
  result = tf.zeros(100, dtype = tf.dtypes.float32)
  for i in range(1,100):
    aux = tf.matmul(x,aux)/i
    result = result + aux
  return result

%timeit quick_computation(x)
5.09 ms ± 283 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

Kuidas tf.function() Töötad?

Miks saame teatud funktsioonid 2–3 korda kiiremini tööle panna?

TensorFlow koodi saab käitada kahes režiimis: innukas režiim ja graafiku režiim. Innukas režiim on standardne interaktiivne viis koodi käitamiseks: iga kord, kui funktsiooni kutsute, käivitatakse see.

Graafikurežiim on aga veidi erinev. Graafikurežiimis loob TensorFlow enne funktsiooni täitmist arvutusgraafiku, mis on andmestruktuur, mis sisaldab funktsiooni täitmiseks vajalikke toiminguid. Arvutusgraafik võimaldab TensorFlow'l arvutusi lihtsustada ja leida paralleelsusvõimalusi. Graafik eraldab funktsiooni ka peal olevast Pythoni koodist, võimaldades seda tõhusalt käivitada paljudes erinevates seadmetes.

Funktsioon, mis on kaunistatud @tf.function täidetakse kahes etapis:

  1. Esimeses etapis käivitab TensorFlow funktsiooni Pythoni koodi ja koostab arvutusgraafiku, mis lükkab edasi mis tahes TensorFlow toimingu täitmist.
  2. Seejärel käivitatakse arvutusgraafik.

Märge: Esimene samm on tuntud kui "jälgimine".

Esimene samm jäetakse vahele, kui pole vaja uut arvutusgraafikut luua. See parandab funktsiooni jõudlust, kuid tähendab ka seda, et funktsioon ei käivitu nagu tavaline Pythoni kood (milles käivitatakse iga käivitatav rida). Näiteks muutkem meie eelmist funktsiooni:

@tf.function
def quick_computation(x):
  print('Only prints the first time!')
  aux = tf.eye(100, dtype=tf.dtypes.float32)
  result = tf.zeros(100, dtype = tf.dtypes.float32)
  for i in range(1,100):
    aux = tf.matmul(x,aux)/i
    result = result + aux
  return result

quick_computation(x)
quick_computation(x)

Selle tulemuseks on:

Only prints the first time!

. print() käivitatakse jälgimisetapi jooksul ainult üks kord, st siis, kui käitatakse tavalist Pythoni koodi. Funktsiooni järgmised väljakutsed teostavad ainult arvutusgraafikult olevaid TenforFlow toiminguid (TensorFlow toimingud).

Kui aga kasutame tf.print() selle asemel:

@tf.function
def quick_computation_with_print(x):
  tf.print("Prints every time!")
  aux = tf.eye(100, dtype=tf.dtypes.float32)
  result = tf.zeros(100, dtype = tf.dtypes.float32)
  for i in range(1,100):
    aux = tf.matmul(x,aux)/i
    result = result + aux
  return result

quick_computation_with_print(x)
quick_computation_with_print(x)

Tutvuge meie praktilise ja praktilise Giti õppimise juhendiga, mis sisaldab parimaid tavasid, tööstusharus aktsepteeritud standardeid ja kaasas olevat petulehte. Lõpetage Giti käskude guugeldamine ja tegelikult õppima seda!

Prints every time!
Prints every time!

TensorFlow sisaldab tf.print() oma arvutusgraafikus, kuna see on TensorFlow toiming – mitte tavaline Pythoni funktsioon.

Hoiatus: Kõik Pythoni koodid ei käivitu igas funktsiooniga kaunistatud kõnes @tf.function. Pärast jälgimist käivitatakse ainult arvutusgraafiku toimingud, mis tähendab, et meie koodiga tuleb olla ettevaatlik.

Parimad tavad koos @tf.function

Koodi kirjutamine TensorFlow operatsioonidega

Nagu me just näitasime, ignoreerib arvutusgraafik mõningaid koodi osi. See muudab funktsiooni käitumise ennustamise "tavalise" Pythoni koodiga kodeerimisel keeruliseks, nagu me just nägime print(). Ootamatu käitumise vältimiseks on parem kodeerida oma funktsioon vajaduse korral TensorFlow operatsioonidega.

Näiteks for ja while silmuseid võib teisendada samaväärseks TensorFlow ahelaks, kuid ei pruugita. Seetõttu on parem kirjutada "for" tsükkel võimaluse korral vektoriseeritud operatsioonina. See parandab teie koodi jõudlust ja tagab teie funktsiooni õige jälgimise.

Näiteks kaaluge järgmist.

x = tf.random.uniform(shape=[100, 100], minval=-1, maxval=1, dtype=tf.dtypes.float32)

@tf.function
def function_with_for(x):
    summ = float(0)
    for row in x:
      summ = summ + tf.reduce_mean(row)
    return summ

@tf.function
def vectorized_function(x):
  result = tf.reduce_mean(x, axis=0)
  return tf.reduce_sum(result)


print(function_with_for(x))
print(vectorized_function(x))

%timeit function_with_for(x)
%timeit vectorized_function(x)
tf.Tensor(0.672811, shape=(), dtype=float32)
tf.Tensor(0.67281103, shape=(), dtype=float32)
1.58 ms ± 177 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
440 µs ± 8.34 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

TensorFlow toimingutega kood on tunduvalt kiirem.

Vältige viiteid globaalsetele muutujatele

Mõelge järgmisele koodile:

x = tf.Variable(2, dtype=tf.dtypes.float32)
y = 2

@tf.function
def power(x):
  return tf.pow(x,y)

print(power(x))

y = 3

print(power(x))
tf.Tensor(4.0, shape=(), dtype=float32)
tf.Tensor(4.0, shape=(), dtype=float32)

Esimest korda kaunistatud funktsioon power() kutsuti välja, oli väljundväärtus oodatud 4. Kuid teisel korral ignoreeris funktsioon seda väärtust y muudeti. See juhtub seetõttu, et Pythoni globaalsete muutujate väärtus on funktsiooni jaoks pärast jälgimist külmutatud.

Parem viis oleks kasutada tf.Variable() kõigi muutujate jaoks ja edasta mõlemad oma funktsiooni argumentidena.

x = tf.Variable(2, dtype=tf.dtypes.float32)
y = tf.Variable(2, dtype = tf.dtypes.float32)

@tf.function
def power(x,y):
  return tf.pow(x,y)

print(power(x,y))

y.assign(3)

print(power(x,y))
tf.Tensor(4.0, shape=(), dtype=float32)
tf.Tensor(8.0, shape=(), dtype=float32)

Silumine [meiliga kaitstud]_s

Üldiselt soovite oma funktsiooni innukas režiimis siluda ja seejärel nendega kaunistada @tf.function pärast seda, kui teie kood töötab õigesti, kuna innukas režiimis olevad veateated on informatiivsemad.

Mõned levinumad probleemid on tüübi- ja kujuvead. Tüübivead ilmnevad siis, kui toiminguga seotud muutujate tüübid ei ühti:

x = tf.Variable(1, dtype = tf.dtypes.float32)
y = tf.Variable(1, dtype = tf.dtypes.int32)

z = tf.add(x,y)
InvalidArgumentError: cannot compute AddV2 as input #1(zero-based) was expected to be a float tensor but is a int32 tensor [Op:AddV2]

Tüübivead hiilivad kergesti sisse ja neid saab hõlpsasti parandada, kandes muutuja teist tüüpi:

y = tf.cast(y, tf.dtypes.float32)
z = tf.add(x, y) 
tf.print(z) 

Kujuvead tekivad siis, kui teie tensorid ei oma kuju, mida teie toiming nõuab:

x = tf.random.uniform(shape=[100, 100], minval=-1, maxval=1, dtype=tf.dtypes.float32)
y = tf.random.uniform(shape=[1, 100], minval=-1, maxval=1, dtype=tf.dtypes.float32)

z = tf.matmul(x,y)
InvalidArgumentError: Matrix size-incompatible: In[0]: [100,100], In[1]: [1,100] [Op:MatMul]

Üks mugav tööriist mõlemat tüüpi vigade parandamiseks on interaktiivne Pythoni silur, mida saate Jupyteri sülearvutis automaatselt kutsuda %pdb. Seda kasutades saate oma funktsiooni kodeerida ja seda mõnel tavalisel kasutusjuhtumil käivitada. Kui ilmneb tõrge, avaneb interaktiivne viip. See viip võimaldab teil koodis abstraktsioonikihtides üles-alla liikuda ning kontrollida TensorFlow muutujate väärtusi, tüüpe ja kujundeid.

Järeldus

Oleme näinud, kuidas TensorFlow on tf.function() muudab teie funktsiooni tõhusamaks ja kuidas @tf.function dekoraator rakendab funktsiooni teie enda jaoks.

See kiirendamine on kasulik funktsioonide puhul, mida kutsutakse mitu korda, näiteks masinõppemudelite kohandatud koolitusetapid.

Ajatempel:

Veel alates Stackabus