TensorFlown @tf.function Decoratorin ymmärtäminen

esittely

Harjoitussilmukan suorituskyvyn parantaminen voi säästää tunteja laskenta-aikaa opetettaessa koneoppimismalleja. Yksi tapa parantaa TensorFlow-koodin suorituskykyä on käyttää tf.function() decorator – yksinkertainen, yhden rivin muutos, joka voi nopeuttaa toimintojasi huomattavasti.

Tässä lyhyessä oppaassa selitämme kuinka tf.function() parantaa suorituskykyä ja tutustu parhaisiin käytäntöihin.

Python Decorators ja tf.function()

Pythonissa koristelu on funktio, joka muokkaa muiden funktioiden käyttäytymistä. Oletetaan esimerkiksi, että kutsut seuraavaa funktiota muistikirjan solussa:

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)

Kuitenkin, jos siirrämme kalliin toiminnon a tf.function():

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

Saamme quicker_computation() – uusi toiminto, joka toimii paljon nopeammin kuin edellinen:

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

Niin, tf.function() muuttaa some_costly_computation() ja tulostaa quicker_computation() toiminto. Sisustajat muokkaavat myös toimintoja, joten se oli luonnollista tehdä tf.function() myös sisustaja.

Sisustajan merkinnän käyttäminen on sama kuin kutsuminen 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)

Kuinka tf.function() Tehdä työtä?

Miten voimme saada tietyt toiminnot toimimaan 2-3 kertaa nopeammin?

TensorFlow-koodia voidaan ajaa kahdessa tilassa: innokas tila ja kaaviotila. Eager-tila on tavallinen interaktiivinen tapa suorittaa koodia: aina kun kutsut funktiota, se suoritetaan.

Graafitila on kuitenkin hieman erilainen. Kaaviotilassa TensorFlow luo ennen funktion suorittamista laskentakaavion, joka on tietorakenne, joka sisältää funktion suorittamiseen tarvittavat toiminnot. Laskentakaavion avulla TensorFlow voi yksinkertaistaa laskelmia ja löytää mahdollisuuksia rinnakkaisuudelle. Kaavio myös eristää toiminnon päällä olevasta Python-koodista, mikä mahdollistaa sen tehokkaan suorittamisen monilla eri laitteilla.

Toiminto, joka on koristeltu @tf.function suoritetaan kahdessa vaiheessa:

  1. Ensimmäisessä vaiheessa TensorFlow suorittaa funktion Python-koodin ja kokoaa laskentakaavion, mikä viivästyttää minkä tahansa TensorFlow-toiminnon suorittamista.
  2. Tämän jälkeen ajetaan laskentakaavio.

Huomautus: Ensimmäinen askel tunnetaan nimellä "jäljitys".

Ensimmäinen vaihe ohitetaan, jos uutta laskentakaaviota ei tarvitse luoda. Tämä parantaa funktion suorituskykyä, mutta tarkoittaa myös sitä, että toimintoa ei suoriteta kuten tavallista Python-koodia (jossa jokainen suoritettava rivi suoritetaan). Muokataan esimerkiksi aiempaa toimintoamme:

@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)

Tämä johtaa:

Only prints the first time!

- print() suoritetaan vain kerran jäljitysvaiheen aikana, eli kun tavallinen Python-koodi ajetaan. Seuraavat funktion kutsut suorittavat vain TenforFlow-operaatioita laskentakaaviosta (TensorFlow-operaatiot).

Kuitenkin, jos käytämme tf.print() sen sijaan:

@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)

Tutustu käytännönläheiseen, käytännölliseen Gitin oppimisoppaaseemme, jossa on parhaat käytännöt, alan hyväksymät standardit ja mukana tuleva huijauslehti. Lopeta Git-komentojen googlailu ja oikeastaan oppia se!

Prints every time!
Prints every time!

TensorFlow sisältää tf.print() sen laskentakaaviossa, koska se on TensorFlow-toiminto – ei tavallinen Python-funktio.

Varoitus: Kaikkea Python-koodia ei suoriteta jokaisessa funktion kutsussa, joka on koristeltu @tf.function. Jäljityksen jälkeen ajetaan vain operaatiot laskennallisesta graafista, mikä tarkoittaa, että koodissamme on oltava varovainen.

Parhaat käytännöt kanssa @tf.function

Koodin kirjoittaminen TensorFlow-toiminnoilla

Kuten olemme juuri osoittaneet, jotkin koodin osat jätetään huomioimatta laskentakaaviossa. Tämä tekee vaikeaksi ennustaa funktion käyttäytymistä koodattaessa "normaalilla" Python-koodilla, kuten olemme juuri nähneet print(). On parempi koodata funktiosi TensorFlow-operaatioilla tarvittaessa odottamattoman toiminnan välttämiseksi.

Esimerkiksi for ja while silmukat voidaan tai ei saa muuntaa vastaavaksi TensorFlow-silmukaksi. Siksi on parempi kirjoittaa "for"-silmukasi vektorisoituna operaationa, jos mahdollista. Tämä parantaa koodisi suorituskykyä ja varmistaa, että toimintosi jäljitetään oikein.

Harkitse esimerkiksi seuraavaa:

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-toimintojen koodi on huomattavasti nopeampi.

Vältä viittauksia globaaleihin muuttujiin

Harkitse seuraavaa koodia:

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)

Ensimmäistä kertaa koristeltu toiminto power() kutsuttiin, lähtöarvo oli odotettu 4. Kuitenkin toisella kerralla funktio ei huomioinut, että arvo y vaihdettiin. Tämä johtuu siitä, että Pythonin globaalien muuttujien arvo jäädytetään funktiolle jäljityksen jälkeen.

Parempi tapa olisi käyttää tf.Variable() kaikille muuttujillesi ja välitä molemmat argumenteiksi funktiollesi.

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)

Virheenkorjaus [sähköposti suojattu]_s

Yleensä haluat tehdä virheenkorjauksen toiminnollasi innokkaasti ja koristella ne sitten @tf.function sen jälkeen, kun koodisi toimii oikein, koska innokas-tilassa olevat virheilmoitukset ovat informatiivisempia.

Joitakin yleisiä ongelmia ovat tyyppivirheet ja muotovirheet. Tyyppivirheitä tapahtuu, kun operaatioon liittyvien muuttujien tyypit eivät täsmää:

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]

Tyyppivirheet hiipivät sisään helposti, ja ne voidaan helposti korjata lähettämällä muuttuja eri tyyppiin:

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

Muotovirheitä tapahtuu, kun tensorisi eivät ole toiminnasi vaatimaa muotoa:

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]

Yksi kätevä työkalu molempien virheiden korjaamiseen on interaktiivinen Python-debuggeri, jota voit kutsua automaattisesti Jupyter-muistikirjassa käyttämällä %pdb. Sen avulla voit koodata funktiosi ja suorittaa sen yleisten käyttötapausten läpi. Jos tapahtuu virhe, interaktiivinen kehote avautuu. Tämän kehotteen avulla voit siirtyä ylös ja alas koodisi abstraktiokerroksissa ja tarkistaa TensorFlow-muuttujien arvot, tyypit ja muodot.

Yhteenveto

Olemme nähneet kuinka TensorFlow on tf.function() tehostaa toimintaasi ja miten @tf.function sisustaja soveltaa toimintoa omaan.

Tämä nopeuttaminen on hyödyllinen toiminnoissa, joita kutsutaan useita kertoja, kuten koneoppimismallien mukautetuissa koulutusvaiheissa.

Aikaleima:

Lisää aiheesta Stackabus