Hei ada, peminat Python! Pernahkah anda berharap kod Numpy anda berjalan pada kelajuan supersonik? Berjumpa dengan Jax!. Sahabat baru anda dalam pembelajaran mesin, pembelajaran mendalam, dan perjalanan pengkomputeran berangka. Fikirkannya sebagai numpy dengan kuasa besar. Ia secara automatik boleh mengendalikan kecerunan, menyusun kod anda untuk berjalan dengan cepat menggunakan JIT, dan juga berjalan di GPU dan TPU tanpa melanggar peluh. Sama ada anda membina rangkaian saraf, mencetuskan data saintifik, model pengubah tweaking, atau hanya cuba mempercepatkan pengiraan anda, Jax mempunyai belakang anda. Mari menyelam dan lihat apa yang menjadikan Jax begitu istimewa.
Panduan ini memberikan pengenalan terperinci kepada Jax dan ekosistemnya.
Artikel ini diterbitkan sebagai sebahagian daripada Blogathon Sains Data.
Menurut dokumentasi rasmi, Jax adalah perpustakaan Python untuk pengiraan array berorientasikan percepatan dan transformasi program, yang direka untuk pengkomputeran berangka berprestasi tinggi dan pembelajaran mesin berskala besar. Oleh itu, Jax pada dasarnya adalah Numpy pada steroid, ia menggabungkan operasi gaya Numpy yang biasa dengan pembezaan automatik dan pecutan perkakasan. Fikirkan ia sebagai yang terbaik dari tiga dunia.
Apa yang membezakan Jax adalah transformasinya. Ini adalah fungsi yang kuat yang dapat mengubah kod python anda:
Berikut adalah penampilan cepat:
import jax.numpy sebagai jnp Dari graduan import Jax, JIT # Tentukan fungsi mudah @jit # mempercepatkannya dengan penyusunan def square_sum (x): kembali jnp.sum (jnp.square (x)) # Dapatkan fungsi kecerunannya secara automatik gradient_fn = grad (square_sum) # Cubalah x = JNP.Array ([1.0, 2.0, 3.0]) cetak (f "gradien: {gradient_fn (x)}")
Output:
Kecerunan: [2. 4. 6.]
Di bawah ini kita akan mengikuti beberapa langkah untuk memulakan dengan Jax.
Menyediakan Jax adalah mudah untuk kegunaan CPU sahaja. Anda boleh menggunakan dokumentasi JAX untuk maklumat lanjut.
Buat persekitaran konda untuk projek anda
# Buat env conda untuk jax $ conda create -nama jaxdev python = 3.11 #Activate env $ conda mengaktifkan jaxdev # Buat Projek Dir Name Jax101 $ mkdir jax101 # Masuk ke dir. $ CD JAX101
Memasang Jax di persekitaran yang baru dibuat
# Untuk CPU sahaja PIP Pasang -PIPGGRADE PIP PIP INSTALL --upgrade "Jax" # untuk GPU PIP Pasang -PIPGGRADE PIP PIP Pasang --upgrade "Jax [cuda12]"
Sekarang anda sudah bersedia untuk menyelam ke dalam perkara sebenar. Sebelum mendapatkan tangan anda kotor pada pengekodan praktikal mari kita pelajari beberapa konsep baru. Saya akan menerangkan konsep -konsep terlebih dahulu dan kemudian kita akan bersama untuk memahami pandangan praktikal.
Pertama, dapatkan motivasi, dengan cara, mengapa kita belajar perpustakaan baru lagi? Saya akan menjawab soalan itu sepanjang panduan ini secara langkah demi langkah semudah mungkin.
Fikirkan Jax sebagai alat kuasa. Walaupun Numpy adalah seperti tangan yang boleh dipercayai, Jax adalah seperti gergaji elektrik moden. Ia memerlukan sedikit langkah dan pengetahuan, tetapi manfaat prestasi bernilai untuk tugas pengiraan yang intensif.
Di bahagian seterusnya, kami akan menyelam jauh ke dalam transformasi Jax, bermula dengan kompilasi JIT. Transformasi ini adalah apa yang memberi Jax kuasa besarnya, dan memahami mereka adalah kunci untuk memanfaatkan Jax dengan berkesan.
Transformasi Jax adalah apa yang benar -benar membezakannya daripada perpustakaan pengiraan berangka seperti numpy atau scipy. Mari kita meneroka setiap satu dan lihat bagaimana mereka dapat menembusi kod anda.
Kompilasi hanya dalam masa mengoptimumkan pelaksanaan kod dengan menyusun bahagian program semasa runtime dan bukannya lebih awal daripada masa.
Di Jax, Jax.jit mengubah fungsi python ke dalam versi JIT yang dikompilasi. Menghias fungsi dengan @jax.jit menangkap graf pelaksanaannya, mengoptimumkannya, dan menyusunnya menggunakan XLA. Versi yang disusun kemudian dijalankan, menyampaikan kelajuan yang ketara, terutamanya untuk panggilan fungsi berulang.
Inilah cara anda boleh mencubanya.
import jax.numpy sebagai jnp dari jit import jax masa import # Fungsi intensif yang komputasi def slow_function (x): untuk _ dalam julat (1000): x = jnp.sin (x) jnp.cos (x) kembali x # Fungsi yang sama dengan JIT @jit def fast_function (x): untuk _ dalam julat (1000): x = jnp.sin (x) jnp.cos (x) kembali x
Berikut adalah fungsi yang sama, satu hanyalah proses kompilasi Python biasa dan yang lain digunakan sebagai proses kompilasi JIT JAX. Ia akan mengira 1000 titik data jumlah fungsi sinus dan kosinus. Kami akan membandingkan prestasi menggunakan masa.
# Bandingkan prestasi x = jnp.arange (1000) # Jit pemanasan fast_function (x) # panggilan pertama menyusun fungsi # Perbandingan masa mula = time.time () SLOW_RESULT = SLOW_FUNCTION (x) cetak (f "tanpa jit: {time.time () - start: .4f} saat") mula = time.time () fast_result = fast_function (x) cetak (f "dengan jit: {time.time () - start: .4f} saat")
Hasilnya akan mengejutkan anda. Kompilasi JIT adalah 333 kali lebih cepat daripada kompilasi biasa. Ia seperti membandingkan basikal dengan buggati chiron.
Output:
Tanpa JIT: 0.0330 saat Dengan JIT: 0.0010 saat
JIT boleh memberi anda peningkatan pelaksanaan superfast tetapi anda mesti menggunakannya dengan betul jika tidak, ia akan menjadi seperti memandu Bugatti di jalan kampung berlumpur yang tidak menawarkan kemudahan supercar.
JIT berfungsi dengan baik dengan bentuk dan jenis statik. Elakkan menggunakan gelung python dan keadaan yang bergantung kepada nilai array. JIT tidak berfungsi dengan tatasusunan dinamik.
# Buruk - menggunakan aliran kawalan python @jit def bad_function (x): jika x [0]> 0: # ini tidak akan berfungsi dengan baik dengan jit kembali x kembali -x # cetak (bad_function (jnp.array ([1, 2, 3]))) # Baik - menggunakan aliran kawalan jax @jit def good_function (x): Kembalikan JNP.WHERE (X [0]> 0, X, -X) # keadaan JAX -NATIVE Cetak (Good_Function (JNP.Array ([1, 2, 3])))
Output:
Ini bermakna BAD_FUNCTION adalah buruk kerana JIT tidak terletak pada nilai x semasa pengiraan.
Output:
[1 2 3]
Pembezaan automatik, atau autodiff, adalah teknik pengiraan untuk mengira derivatif fungsi dengan tepat dan berkesan. Ia memainkan peranan penting dalam mengoptimumkan model pembelajaran mesin, terutamanya dalam latihan rangkaian saraf, di mana kecerunan digunakan untuk mengemas kini parameter model.
Autodiff berfungsi dengan menggunakan peraturan rantai kalkulus untuk menguraikan fungsi kompleks ke dalam yang lebih mudah, mengira derivatif sub-fungsi ini, dan kemudian menggabungkan hasilnya. Ia merekodkan setiap operasi semasa pelaksanaan fungsi untuk membina graf pengiraan, yang kemudiannya digunakan untuk mengira derivatif secara automatik.
Terdapat dua mod utama auto-diff:
import jax.numpy sebagai jnp dari jax import grad, value_and_grad # Tentukan lapisan rangkaian saraf yang mudah Lapisan DEF (Params, X): Berat, Bias = Params Kembali jnp.dot (x, berat) Bias # Tentukan fungsi kehilangan nilai skalar def loss_fn (params, x): output = lapisan (param, x) kembali jnp.sum (output) # Mengurangkan ke skalar # Dapatkan output dan kecerunan layer_grad = grad (loss_fn, argnums = 0) # gradien berkenaan dengan param Layer_Value_and_grad = value_and_grad (loss_fn, argnums = 0) # kedua -dua nilai dan kecerunan # Contoh penggunaan kunci = jax.random.prngKey (0) x = jax.random.normal (kunci, (3, 4)) berat = jax.random.normal (kunci, (4, 2)) Bias = jax.random.normal (kunci, (2,)) # Mengira kecerunan grads = layer_grad ((berat, bias), x) output, grads = layer_value_and_grad ((berat, bias), x) # Derivatif berganda mudah dua kali_grad = grad (grad (jnp.sin)) x = JNP.Array (2.0) cetak (f "derivatif kedua dosa di x = 2: {twip_grad (x)}")
Output:
Derivatif Kedua Dosa di X = 2: -0.9092974066734314
Dalam Jax, `vMap` adalah fungsi yang kuat yang secara automatik mengira perhitungan, yang membolehkan anda menggunakan fungsi ke atas kumpulan data tanpa menulis gelung secara manual. Ia memetakan fungsi ke atas paksi array (atau pelbagai paksi) dan menilai dengan cekap secara selari, yang boleh membawa kepada penambahbaikan prestasi yang signifikan.
Fungsi VMAP mengautomasikan proses memohon fungsi ke setiap elemen di sepanjang paksi tertentu dari array input sambil mengekalkan kecekapan pengiraan. Ia mengubah fungsi yang diberikan untuk menerima input yang dibatalkan dan melaksanakan pengiraan secara vektor.
Daripada menggunakan gelung eksplisit, VMAP membolehkan operasi dilakukan selari dengan vektori melalui paksi input. Ini memanfaatkan keupayaan perkakasan untuk melaksanakan operasi SIMD (arahan tunggal, pelbagai data), yang boleh menghasilkan kelajuan besar.
import jax.numpy sebagai jnp dari jax import vmap # Fungsi yang berfungsi pada input tunggal def single_input_fn (x): Kembalikan jnp.sin (x) jnp.cos (x) # Vektorkan untuk mengerjakan kelompok batch_fn = vmap (single_input_fn) # Bandingkan prestasi x = jnp.arange (1000) # Tanpa VMAP (menggunakan pemahaman senarai) hasil1 = jnp.array ([Single_input_fn (xi) untuk xi dalam x]) # Dengan VMAP hasil2 = batch_fn (x) # lebih cepat! # Vektor pelbagai argumen def two_input_fn (x, y): kembali x * jnp.sin (y) # Vektori atas kedua -dua input vectorized_fn = vmap (two_input_fn, in_axes = (0, 0)) # Atau vectorize hanya input pertama sebahagiannya_vectorized_fn = vmap (two_input_fn, in_axes = (0, tiada)) # cetak cetak (result1.shape) cetak (result2.shape) cetak (sebahagiannya_vectorized_fn (x, y) .shape)
Output:
(1000,) (1000,) (1000,3)
Jax menyediakan sokongan komprehensif untuk operasi matriks dan aljabar linear, menjadikannya sesuai untuk pengkomputeran saintifik, pembelajaran mesin, dan tugas pengoptimuman berangka. Keupayaan algebra linear Jax adalah serupa dengan yang terdapat di perpustakaan seperti Numpy tetapi dengan ciri-ciri tambahan seperti pembezaan automatik dan kompilasi hanya dalam masa untuk prestasi yang dioptimumkan.
Operasi ini dilakukan matriks elemen yang sama dengan bentuk yang sama.
Tambahan dan penolakan matriks # 1: import jax.numpy sebagai jnp A = JNP.Array ([[1, 2], [3, 4]]) B = JNP.Array ([[5, 6], [7, 8]]) # Penambahan matriks C = ab # Penolakan matriks D = A - B cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "matriks b: \ n {b}") Cetak ("==========================") cetak (f "matriks pemadaman ab: \ n {c}") Cetak ("==========================") cetak (f "substraksi matriks AB: \ n {d}")
Output:
Jax menyokong kedua-dua pendaraban elemen-bijak dan pendaraban matriks berasaskan produk DOR.
# Pendaraban yang bijak E = a * b # Pendaraban Matrix (Produk Dot) F = jnp.dot (a, b) cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "matriks b: \ n {b}") Cetak ("==========================") cetak (f "pendaraban elemen-bijak a*b: \ n {e}") Cetak ("==========================") cetak (f "pendaraban matriks A*b: \ n {f}")
Output:
Transpose matriks boleh didapati menggunakan `jnp.transpose ()`
# Matrik transpose G = jnp.transpose (a) cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "matriks transpose a: \ n {g}")
Output:
Jax menyediakan fungsi untuk penyongsangan matriks menggunakan `jnp.linalg.inv ()`
# Penyongsangan matrik H = jnp.linalg.inv (a) cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "penyongsangan matriks a: \ n {h}")
Output:
Penentu matriks boleh dikira menggunakan `jnp.linalg.det ()`.
# penentu matriks det_a = jnp.linalg.det (a) cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "penentu matriks a: \ n {det_a}")
Output:
Anda boleh mengira nilai eigen dan eigenvectors matriks menggunakan `jnp.linalg.eigh ()`
# Nilai eigen dan vektor eigen import jax.numpy sebagai jnp A = JNP.Array ([[1, 2], [3, 4]]) Nilai eigen, eigenvectors = jnp.linalg.eigh (a) cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "nilai eigen a: \ n {eigenvalues}") Cetak ("==========================") cetak (f "eigenvectors of a: \ n {eigenvectors}")
Output:
SVD disokong melalui `jnp.linalg.svd`, berguna dalam pengurangan dimensi dan pemfaktoran matriks.
# Penguraian nilai tunggal (SVD) import jax.numpy sebagai jnp A = JNP.Array ([[1, 2], [3, 4]]) U, s, v = jnp.linalg.svd (a) cetak (f "matriks a: \ n {a}") Cetak ("==========================") cetak (f "matriks u: \ n {u}") Cetak ("==========================") cetak (f "matriks s: \ n {s}") Cetak ("==========================") cetak (f "matriks v: \ n {v}")
Output:
Untuk menyelesaikan sistem persamaan linear AX = B, kami menggunakan `jnp.linalg.solve ()`, di mana A adalah matriks persegi dan b adalah vektor atau matriks bilangan baris yang sama.
# Menyelesaikan sistem persamaan linear import jax.numpy sebagai jnp A = JNP.Array ([[2.0, 1.0], [1.0, 3.0]]) B = JNP.Array ([5.0, 6.0]) x = jnp.linalg.solve (a, b) cetak (f "nilai x: {x}")
Output:
Nilai x: [1.8 1.4]
Menggunakan pembezaan automatik Jax, anda boleh mengira kecerunan fungsi skalar berkenaan dengan matriks.
Kami akan mengira kecerunan fungsi dan nilai di bawah x
Fungsi
# Mengira kecerunan fungsi matriks Import Jax import jax.numpy sebagai jnp def matrix_function (x): kembali jnp.sum (jnp.sin (x) x ** 2) # Kirakan graduan fungsi grad_f = jax.grad (matrix_function) X = jnp.array ([[1.0, 2.0], [3.0, 4.0]]) kecerunan = grad_f (x) cetak (f "matriks x: \ n {x}") Cetak ("==========================") cetak (f "kecerunan matrix_function: \ n {gradien}")
Output:
Fungsi Jax yang paling berguna ini digunakan dalam pengkomputeran berangka, pembelajaran mesin, dan pengiraan fizik. Terdapat banyak lagi yang tersisa untuk anda meneroka.
Perpustakaan kuat Jax untuk pengkomputeran saintifik, Jax adalah yang terbaik untuk pengkomputeran saintifik untuk ciri-ciri awalnya seperti penyusunan JIT, pembezaan automatik, vektorisasi, penyesuaian, dan pecutan GPU-TPU. Keupayaan Jax untuk menyokong pengkomputeran prestasi tinggi menjadikannya sesuai untuk pelbagai aplikasi saintifik, termasuk simulasi fizik, pembelajaran mesin, pengoptimuman dan analisis berangka.
Kami akan meneroka masalah pengoptimuman dalam bahagian ini.
Marilah kita melalui masalah pengoptimuman langkah di bawah:
# Tentukan fungsi untuk meminimumkan (misalnya, fungsi Rosenbrock) @jit Def Rosenbrock (x): kembali jumlah (100.0 * (x [1:] - x [: - 1] ** 2.0) ** 2.0 (1 - x [: - 1]) ** 2.0)
Di sini, fungsi Rosenbrock ditakrifkan, yang merupakan masalah ujian biasa dalam pengoptimuman. Fungsi ini mengambil array x sebagai input dan mengira valie yang mewakili sejauh mana X adalah dari minimum global fungsi. Penghias @Jit digunakan untuk membolehkan penyusunan JUT-in-time, yang mempercepat pengiraan dengan menyusun fungsi untuk berjalan dengan cekap pada CPU dan GPU.
# Pengoptimuman keturunan kecerunan @jit def gradient_descent_step (x, learning_rate): kembali x - learning_rate * grad (rosenbrock) (x)
Fungsi ini melakukan satu langkah pengoptimuman keturunan kecerunan. Kecerunan fungsi Rosenbrock dikira menggunakan grad (Rosenbrock) (x), yang memberikan derivatif dengan aspek kepada x. Nilai baru x dikemas kini dengan pengurangan kecerunan yang diperkatakan oleh pembelajaran_rate.the @jit melakukan perkara yang sama seperti sebelumnya.
# Mengoptimumkan x = jnp.array ([0.0, 0.0]) # titik permulaan learning_rate = 0.001 untuk I in Range (2000): x = gradient_descent_step (x, learning_rate) jika saya % 100 == 0: cetak (f "langkah {i}, nilai: {rosenbrock (x): 4f}")
Gelung pengoptimuman memulakan titik permulaan X dan melakukan 1000 lelaran keturunan kecerunan. Dalam setiap lelaran, kemas kini fungsi Gradient_descent_Step berdasarkan kecerunan semasa. Setiap 100 langkah, nombor langkah semasa dan nilai fungsi Rosenbrock di x dicetak, memberikan kemajuan pengoptimuman.
Output:
Kami akan mensimulasikan sistem fizikal gerakan pengayun harmonik yang lembap, yang memodelkan perkara-perkara seperti sistem spring massa dengan geseran, penyerap kejutan dalam kenderaan, atau ayunan dalam litar elektrik. Adakah tidak bagus? Mari Lakukan.
Import Jax import jax.numpy sebagai jnp # Tentukan parameter jisim = 1.0 # jisim objek (kg) redaman = 0.1 # pekali redaman (kg/s) spring_constant = 1.0 # pemalar musim bunga (n/m) # Tentukan langkah masa dan jumlah masa dt = 0.01 # langkah masa (s) num_steps = 3000 # bilangan langkah
Jisim, pekali redaman, dan pemalar musim bunga ditakrifkan. Ini menentukan sifat fizikal pengayun harmonik yang lembap.
# Tentukan sistem odes def damped_harmonic_oscillator (state, t): "" "Kirakan derivatif untuk pengayun harmonik yang lembap. Negeri: Arahan yang mengandungi kedudukan dan halaju [x, v] T: Masa (tidak digunakan dalam sistem autonomi ini) "" " x, v = keadaan dxdt = v dvdt = -damping / jisim * v - spring_constant / massa * x kembali jnp.array ([dxdt, dvdt])
Fungsi pengayun harmonik yang lembap mentakrifkan derivatif kedudukan dan halaju pengayun, yang mewakili sistem dinamik.
# Selesaikan ode menggunakan kaedah Euler def euler_step (state, t, dt): "" "Lakukan satu langkah kaedah Euler." "" Derivatif = damped_harmonic_oscillator (Negeri, T) kembali derivatif negeri * dt
Kaedah berangka mudah digunakan untuk menyelesaikan ODE. Ia menghampiri keadaan pada langkah seterusnya berdasarkan keadaan semasa dan derivatif.
# Keadaan awal: [kedudukan, halaju] initial_state = jnp.array ([1.0, 0.0]) # bermula dengan jisim pada x = 1, v = 0 # Evolusi masa negeri = [initial_state] masa = 0.0 untuk langkah dalam julat (num_steps): next_state = euler_step (menyatakan [-1], masa, dt) negeri.append (next_state) masa = dt # Tukar senarai negeri ke array jax untuk analisis negeri = jnp.stack (negeri)
Gelung itu melangkah melalui bilangan langkah masa yang ditentukan, mengemas kini keadaan pada setiap langkah menggunakan kaedah Euler.
Output:
Akhirnya, kita boleh merancang hasilnya untuk memvisualisasikan tingkah laku pengayun harmonik yang lembap.
# Merancang hasilnya import matplotlib.pyplot sebagai PLT plt.style.use ("ggplot") kedudukan = menyatakan [:, 0] halaju = menyatakan [:, 1] time_points = jnp.arange (0, (num_steps 1) * dt, dt) PLT.Figure (figsize = (12, 6)) plt.subplot (2, 1, 1) plt.plot (time_points, posisi, label = "kedudukan") plt.xlabel ("masa (s)") plt.ylabel ("kedudukan (m)") plt.legend () plt.subplot (2, 1, 2) plt.plot (time_points, halaju, label = "halaju", warna = "oren") plt.xlabel ("masa (s)") plt.ylabel ("halaju (m/s)") plt.legend () plt.tight_layout () plt.show ()
Output:
Saya tahu anda tidak sabar -sabar untuk melihat bagaimana rangkaian saraf boleh dibina dengan Jax. Jadi, mari kita menyelam jauh ke dalamnya.
Di sini, anda dapat melihat bahawa nilai -nilai diminimumkan secara beransur -ansur.
Jax adalah perpustakaan yang kuat yang menggabungkan pengkomputeran berangka berprestasi tinggi dengan kemudahan menggunakan sintaks seperti numpy. Bahagian ini akan membimbing anda melalui proses membina rangkaian saraf menggunakan Jax, memanfaatkan ciri-ciri canggihnya untuk pembezaan automatik dan kompilasi hanya dalam masa untuk mengoptimumkan prestasi.
Sebelum kita menyelam untuk membina rangkaian saraf kita, kita perlu mengimport perpustakaan yang diperlukan. Jax menyediakan satu set alat untuk mewujudkan perhitungan berangka yang cekap, sementara perpustakaan tambahan akan membantu pengoptimuman dan visualisasi hasil kami.
Import Jax import jax.numpy sebagai jnp Dari graduan import Jax, JIT dari jax.random import prngkey, normal Import Optax # Perpustakaan Pengoptimuman Jax import matplotlib.pyplot sebagai PLT
Mewujudkan lapisan model yang berkesan adalah penting dalam menentukan seni bina rangkaian saraf kami. Dalam langkah ini, kami akan memulakan parameter untuk lapisan padat kami, memastikan model kami bermula dengan berat dan berat sebelah yang jelas untuk pembelajaran yang berkesan.
def init_layer_params (kunci, n_in, n_out): "" "Inisialisasi parameter untuk lapisan padat tunggal" "" key_w, key_b = jax.random.split (kekunci) # Dia inisialisasi w = normal (key_w, (n_in, n_out)) * jnp.sqrt (2.0 / n_in) b = normal (key_b, (n_out,)) * 0.1 kembali (w, b) def relu (x): "" "Fungsi pengaktifan relu" "" " kembali jnp.maximum (0, x)
Pas ke hadapan adalah asas rangkaian saraf, kerana ia menentukan bagaimana data input mengalir melalui rangkaian untuk menghasilkan output. Di sini, kami akan menentukan kaedah untuk mengira output model kami dengan menggunakan transformasi ke data input melalui lapisan yang diasaskan.
Def Forward (Params, X): "" "Lulus ke hadapan untuk rangkaian saraf dua lapisan" "" (W1, B1), (W2, B2) = Params # Lapisan pertama H1 = relu (jnp.dot (x, w1) b1) # Lapisan output logit = jnp.dot (H1, W2) b2 kembali logit
Fungsi kerugian yang jelas adalah penting untuk membimbing latihan model kami. Dalam langkah ini, kami akan melaksanakan fungsi kerugian kesilapan (MSE) min, yang mengukur seberapa baik output yang diramalkan sepadan dengan nilai sasaran, membolehkan model untuk belajar dengan berkesan.
def loss_fn (params, x, y): "" "Maksud kehilangan kesilapan kuadrat" "" pred = ke hadapan (param, x) kembali jnp.mean ((pred - y) ** 2)
Dengan fungsi seni bina dan kehilangan model kami, kami kini beralih kepada permulaan model. Langkah ini melibatkan penubuhan parameter rangkaian saraf kami, memastikan setiap lapisan bersedia untuk memulakan proses latihan dengan berat dan berat badan yang rawak tetapi sesuai.
def init_model (rng_key, input_dim, hidden_dim, output_dim): key1, key2 = jax.random.split (rng_key) params = [ init_layer_params (key1, input_dim, hidden_dim), init_layer_params (key2, hidden_dim, output_dim), ] kembali param
Latihan Rangkaian saraf melibatkan kemas kini berulang kepada parameternya berdasarkan kecerunan yang dikira fungsi kerugian. Dalam langkah ini, kami akan melaksanakan fungsi latihan yang menggunakan kemas kini ini dengan cekap, yang membolehkan model kami belajar dari data melalui pelbagai zaman.
@jit def train_step (params, opt_state, x_batch, y_batch): kerugian, graduan = jax.value_and_grad (loss_fn) (params, x_batch, y_batch) kemas kini, opt_state = optimizer.update (grads, opt_state) params = optax.apply_updates (params, kemas kini) Kembali param, opt_state, kerugian
Untuk melatih model kami dengan berkesan, kami perlu menjana data yang sesuai dan melaksanakan gelung latihan. Bahagian ini akan meliputi cara membuat data sintetik untuk contoh kami dan bagaimana untuk menguruskan proses latihan merentasi pelbagai kelompok dan zaman.
# Menghasilkan beberapa contoh data kunci = prngkey (0) x_data = normal (kekunci, (1000, 10)) # 1000 sampel, 10 ciri y_data = jnp.sum (x_data ** 2, paksi = 1, keepdims = true) # fungsi nonlinear mudah # Inisialisasi model dan pengoptimum params = init_model (kunci, input_dim = 10, hidden_dim = 32, output_dim = 1) Optimizer = optax.adam (learning_rate = 0.001) opt_state = optimizer.init (params) # LOOP LATIHAN batch_size = 32 num_epochs = 100 num_batches = x_data.shape [0] // batch_size # Array untuk menyimpan nilai zaman dan kerugian epoch_array = [] loss_array = [] untuk zaman dalam julat (num_epochs): Epoch_loss = 0.0 untuk batch dalam julat (num_batches): idx = jax.random.permutation (kunci, batch_size) x_batch = x_data [idx] y_batch = y_data [idx] params, opt_state, kehilangan = train_step (params, opt_state, x_batch, y_batch) Epoch_loss = kehilangan # Simpan kerugian purata untuk zaman avg_loss = epoch_loss / num_batches epoch_array.append (Epoch) loss_array.append (avg_loss) Jika Epoch % 10 == 0: cetak (f "epoch {epoch}, kehilangan: {avg_loss: .4f}")
Menggambarkan hasil latihan adalah kunci untuk memahami prestasi rangkaian saraf kami. Dalam langkah ini, kami akan merancang kerugian latihan ke atas Epochs untuk melihat bagaimana model belajar dan mengenal pasti sebarang isu yang berpotensi dalam proses latihan.
# Plot hasilnya plt.plot (Epoch_array, loss_array, label = "kehilangan latihan") plt.xlabel ("Epoch") plt.ylabel ("kehilangan") PLT.TITLE ("Kerugian Latihan ke atas Epochs") plt.legend () plt.show ()
Contoh -contoh ini menunjukkan bagaimana Jax menggabungkan prestasi tinggi dengan kod yang bersih dan boleh dibaca. Gaya pengaturcaraan berfungsi yang digalakkan oleh Jax menjadikannya mudah untuk menyusun operasi dan memohon transformasi.
Output:
Plot:
Contoh -contoh ini menunjukkan bagaimana Jax menggabungkan prestasi tinggi dengan kod yang bersih dan boleh dibaca. Gaya pengaturcaraan berfungsi yang digalakkan oleh Jax menjadikannya mudah untuk menyusun operasi dan memohon transformasi.
In building neural networks, adhering to best practices can significantly enhance performance and maintainability. This section will discuss various strategies and tips for optimizing your code and improving the overall efficiency of your JAX-based models.
Optimizing performance is essential when working with JAX, as it enables us to fully leverage its capabilities. Here, we will explore different techniques for improving the efficiency of our JAX functions, ensuring that our models run as quickly as possible without sacrificing readability.
Just-In-Time (JIT) compilation is one of the standout features of JAX, enabling faster execution by compiling functions at runtime. This section will outline best practices for effectively using JIT compilation, helping you avoid common pitfalls and maximize the performance of your code.
import jax import jax.numpy as jnp from jax import jit from jax import lax # BAD: Dynamic Python control flow inside JIT @jit def bad_function(x, n): for i in range(n): # Python loop - will be unrolled x = x 1 return x print("===========================") # print(bad_function(1, 1000)) # does not work
This function uses a standard Python loop to iterate n times, incrementing the of x by 1 on each iteration. When compiled with jit, JAX unrolls the loop, which can be inefficient, especially for large n. This approach does not fully leverage JAX's capabilities for performance.
# GOOD: Use JAX-native operations @jit def good_function(x, n): return xn # Vectorized operation print("===========================") print(good_function(1, 1000))
This function does the same operation, but it uses a vectorized operation (xn) instead of a loop. This approach is much more efficient because JAX can better optimize the computation when expressed as a single vectorized operation.
# BETTER: Use scan for loops @jit def best_function(x, n): def body_fun(i, val): return val 1 return lax.fori_loop(0, n, body_fun, x) print("===========================") print(best_function(1, 1000))
This approach uses `jax.lax.fori_loop`, which is a JAX-native way to implement loops efficiently. The `lax.fori_loop` performs the same increment operation as the previous function, but it does so using a compiled loop structure. The body_fn function defines the operation for each iteration, and `lax.fori_loop` executes it from o to n. This method is more efficient than unrolling loops and is especially suitable for cases where the number of iterations isn't known ahead of time.
Output :
=========================== =========================== 1001 =========================== 1001
The code demonstrates different approaches to handling loops and control flow within JAX's jit-complied functions.
Efficient memory management is crucial in any computational framework, especially when dealing with large datasets or complex models. This section will discuss common pitfalls in memory allocation and provide strategies for optimizing memory usage in JAX.
# BAD: Creating large temporary arrays @jit def inefficient_function(x): temp1 = jnp.power(x, 2) # Temporary array temp2 = jnp.sin(temp1) # Another temporary return jnp.sum(temp2)
inefficient_function(x): This function creates multiple intermediate arrays, temp1, temp1 and finally the sum of the elements in temp2. Creating these temporary arrays can be inefficient because each step allocates memory and incurs computational overhead, leading to slower execution and higher memory usage.
# GOOD: Combining operations @jit def efficient_function(x): return jnp.sum(jnp.sin(jnp.power(x, 2))) # Single operation
This version combines all operations into a single line of code. It computes the sine of squared elements of x directly and sums the results. By combining the operation, it avoids creating intermediate arrays, reducing memory footprints and improving performance.
x = jnp.array([1, 2, 3]) print(x) print(inefficient_function(x)) print(efficient_function(x))
Output:
[1 2 3] 0.49678695 0.49678695
The efficient version leverages JAX's ability to optimize the computation graph, making the code faster and more memory-efficient by minimizing temporary array creation.
Debugging is an essential part of the development process, especially in complex numerical computations. In this section, we will discuss effective debugging strategies specific to JAX, enabling you to identify and resolve issues quickly.
The code shows techniques for debugging within JAX, particularly when using JIT-compiled functions.
import jax.numpy as jnp from jax import debug @jit def debug_function(x): # Use debug.print instead of print inside JIT debug.print("Shape of x: {}", x.shape) y = jnp.sum(x) debug.print("Sum: {}", y) return y
# For more complex debugging, break out of JIT def debug_values(x): print("Input:", x) result = debug_function(x) print("Output:", result) return result
Output:
print("===========================") print(debug_function(jnp.array([1, 2, 3]))) print("===========================") print(debug_values(jnp.array([1, 2, 3])))
This approach allows for a combination of in-JIT debugging with debug.print() and more detailed debugging outside of JIT using standard Python print statements.
Finally, we will explore common patterns and idioms in JAX that can help streamline your coding process and improve efficiency. Familiarizing yourself with these practices will aid in developing more robust and performant JAX applications.
# 1. Device Memory Management def process_large_data(data): # Process in chunks to manage memory chunk_size = 100 results = [] for i in range(0, len(data), chunk_size): chunk = data[i : i chunk_size] chunk_result = jit(process_chunk)(chunk) results.append(chunk_result) return jnp.concatenate(results) def process_chunk(chunk): chunk_temp = jnp.sqrt(chunk) return chunk_temp
This function processes large datasets in chunks to avoid overwhelming device memory.
It sets chunk_size to 100 and iterates over the data increments of the chunk size, processing each chunk separately.
For each chunk, the function uses jit(process_chunk) to JIT-compile the processing operation, which improves performance by compiling it ahead of time.
The result of each chunk is concatenated into a single array using jnp.concatenated(result) to form a single list.
Output:
print("===========================") data = jnp.arange(10000) print(data.shape) print("===========================") print(data) print("===========================") print(process_large_data(data))
The function create_traing_state() demonstrates managing random number generators (RNGs) in JAX, which is essential for reproducibility and consistent results.
# 2. Handling Random Seeds def create_training_state(rng): # Split RNG for different uses rng, init_rng = jax.random.split(rng) params = init_network(init_rng) return params, rng # Return new RNG for next use
It starts with an initial RNG (rng) and splits it into two new RNGs using jax.random.split(). Split RNGs perform different tasks: `init_rng` initializes network parameters, and the updated RNG returns for subsequent operations.
The function returns both the initialized network parameters and the new RNG for further use, ensuring proper handling of random states across different steps.
Now test the code using mock data
def init_network(rng): # Initialize network parameters return { "w1": jax.random.normal(rng, (784, 256)), "b1": jax.random.normal(rng, (256,)), "w2": jax.random.normal(rng, (256, 10)), "b2": jax.random.normal(rng, (10,)), } print("===========================") key = jax.random.PRNGKey(0) params, rng = create_training_state(key) print(f"Random number generator: {rng}") print(params.keys()) print("===========================") print("===========================") print(f"Network parameters shape: {params['w1'].shape}") print("===========================") print(f"Network parameters shape: {params['b1'].shape}") print("===========================") print(f"Network parameters shape: {params['w2'].shape}") print("===========================") print(f"Network parameters shape: {params['b2'].shape}") print("===========================") print(f"Network parameters: {params}")
Output:
def g(x, n): i = 0 while i <p> <strong>Output:</strong></p><pre class="brush:php;toolbar:false"> 30
You can use a static argument if JIT compiles the function with the same arguments each time. This can be useful for the performance optimization of JAX functions.
from functools import partial @partial(jax.jit, static_argnames=["n"]) def g_jit_decorated(x, n): i = 0 while i <p>If You want to use static arguments in JIT as a decorator you can use jit inside of functools. partial() function.</p><p> <strong>Output:</strong></p><pre class="brush:php;toolbar:false"> 30
Now, we have learned and dived deep into many exciting concepts and tricks in JAX and overall programming style.
All code used in this article is here
JAX is a powerful tool that provides a wide range of capabilities for machine learning, Deep Learning, and scientific computing. Start with basics, experimenting, and get help from JAX's beautiful documentation and community. There are so many things to learn and it will not be learned by just reading others' code you have to do it on your own. So, start creating a small project today in JAX. The key is to Keep Going, learn on the way.
A. Although JAX feels like NumPy, it adds automatic differentiation, JIT compilation, and GPU/TPU support.
S2. Do I need a GPU to use JAX?A. In a single word big NO, though having a GPU can significantly speed up computation for larger data.
Q3. Is JAX a good alternative to NumPy?A. Yes, You can use JAX as an alternative to NumPy, though JAX's APIs look familiar to NumPy JAX is more powerful if you use JAX's features well.
Q4. Can I use my existing NumPy code with JAX?A. Most NumPy code can be adapted to JAX with minimal changes. Usually just changing import numpy as np to import jax.numpy as jnp.
S5. Is JAX harder to learn than NumPy?A. The basics are just as easy as NumPy! Tell me one thing, will you find it hard after reading the above article and hands-on? I answered it for you. YES hard. Every framework, language, libraries is hard not because it is hard by design but because we don't give much time to explore it. Give it time to get your hand dirty it will be easier day by day.
Media yang ditunjukkan dalam artikel ini tidak dimiliki oleh Analytics Vidhya dan digunakan atas budi bicara penulis.
Atas ialah kandungan terperinci Panduan untuk Jax kilat cepat. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!