Halo, Habr!
Dalam artikel singkat ini saya akan memberi tahu Anda tentang dua jebakan yang mudah berbenturan dengan dan mudah untuk dihancurkan.
Ini adalah tentang membuat jaringan saraf sepele pada Keras, yang dengannya kita akan memprediksi rata-rata aritmatika dari dua angka.
Tampaknya itu bisa lebih mudah. Dan sungguh, tidak ada yang rumit, tetapi ada nuansa.
Untuk siapa topiknya menarik, selamat datang di bawah potongan, tidak akan ada deskripsi yang membosankan, hanya kode pendek dan komentar di atasnya.
Solusinya terlihat seperti ini:
import numpy as np from keras.layers import Input, Dense, Lambda from keras.models import Model import keras.backend as K
Mencoba belajar ... tetapi tidak ada hasilnya. Dan di sini, di tempat ini Anda dapat mengatur tarian dengan rebana dan kehilangan banyak waktu.
Epoch 1/100 1000/1000 [==============================] - 2s 2ms/step - loss: 1044.0806 Epoch 2/100 1000/1000 [==============================] - 2s 2ms/step - loss: 713.5198 Epoch 3/100 1000/1000 [==============================] - 3s 3ms/step - loss: 708.1110 ... Epoch 98/100 1000/1000 [==============================] - 2s 2ms/step - loss: 415.0479 Epoch 99/100 1000/1000 [==============================] - 2s 2ms/step - loss: 416.6932 Epoch 100/100 1000/1000 [==============================] - 2s 2ms/step - loss: 417.2400 [array([[73., 57.]])] [array([[65.]])] [[49.650894]]
49 diprediksi, yang jauh dari 65.
Tapi begitu kita sedikit memperbaiki generator, semuanya mulai bekerja segera.
def train_iterator_1(batch_size=64): x = np.zeros((batch_size, 2)) x_mean = np.zeros((batch_size,)) while True: for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean[::] = (x[::,0] + x[::,1]) / 2 x_mean_ex = np.expand_dims(x_mean, -1) yield [x], [x_mean_ex]
Dan jelas bahwa sudah secara harfiah di era ketiga jaringan sedang konvergen.
Epoch 1/5 1000/1000 [==============================] - 2s 2ms/step - loss: 648.9184 Epoch 2/5 1000/1000 [==============================] - 2s 2ms/step - loss: 0.0177 Epoch 3/5 1000/1000 [==============================] - 2s 2ms/step - loss: 0.0030
Perbedaan utama adalah bahwa dalam kasus pertama, objek x_mean dibuat dalam memori setiap kali, dan dalam kasus kedua, itu muncul ketika generator dibuat dan kemudian hanya digunakan kembali.
Kami memahami lebih lanjut apakah semuanya benar dalam generator ini. Ternyata tidak juga.
Contoh berikut menunjukkan bahwa ada sesuatu yang salah.
def train_iterator(batch_size=1): x = np.zeros((batch_size, 2)) while True: for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield x, x_mean it = train_iterator() print(next(it), next(it))
(array([[44., 2.]]), array([10.])) (array([[44., 2.]]), array([23.]))
Nilai rata-rata dalam panggilan iterator pertama tidak sesuai dengan angka-angka berdasarkan yang dihitung. Bahkan, nilai rata-rata dihitung dengan benar, tetapi karena array dilewatkan dengan referensi, kedua kali iterator dipanggil, nilai-nilai dalam array ditimpa, dan fungsi print () mengembalikan apa yang ada dalam array, dan bukan yang kita harapkan.
Ada dua cara untuk memperbaikinya. Keduanya mahal tetapi benar.
1. Pindahkan pembuatan variabel x di dalam loop sementara, sehingga array baru dibuat di setiap hasil.
def train_iterator_1(batch_size=1): while True: x = np.zeros((batch_size, 2)) for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield x, x_mean it_1 = train_iterator_1() print(next(it_1), next(it_1))
(array([[82., 4.]]), array([43.])) (array([[77., 34.]]), array([55.5]))
2. Kembalikan salinan array.
def train_iterator_2(batch_size=1): x = np.zeros((batch_size, 2)) while True: x = np.zeros((batch_size, 2)) for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield np.copy(x), x_mean it_2 = train_iterator_2() print(next(it_2), next(it_2))
(array([[63., 31.]]), array([47.])) (array([[94., 25.]]), array([59.5]))
Sekarang semuanya baik-baik saja. Silakan.
Apakah expand_dims perlu dilakukan? Mari kita coba untuk menghapus baris ini dan kode baru akan seperti ini:
def train_iterator(batch_size=64): while True: x = np.zeros((batch_size, 2)) for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield [x], [x_mean]
Semuanya belajar dengan baik, meskipun data yang dikembalikan memiliki bentuk yang berbeda.
Misalnya, ada [[49.]], dan itu menjadi [49.], tetapi di dalam Keras ini, tampaknya, dikurangi dengan benar ke dimensi yang diinginkan.
Jadi, kita tahu seperti apa seharusnya penghasil data yang benar, sekarang mari kita bermain-main dengan fungsi lambda, dan melihat perilaku expand_dims di sana.
Kami tidak akan memprediksi apa pun, kami hanya mempertimbangkan nilai yang benar di dalam lambda.
Kode tersebut adalah sebagai berikut:
def calc_mean(x): res = (x[::,0] + x[::,1]) / 2 res = K.expand_dims(res, -1) return res def create_model(): x = Input(name = 'x', shape=(2,)) x_mean = Lambda(lambda x: calc_mean(x), output_shape=(1,))(x) model = Model(inputs=x, outputs=x_mean) return model
Kami memulai dan melihat bahwa semuanya baik-baik saja:
Epoch 1/5 100/100 [==============================] - 0s 3ms/step - loss: 0.0000e+00 Epoch 2/5 100/100 [==============================] - 0s 2ms/step - loss: 0.0000e+00 Epoch 3/5 100/100 [==============================] - 0s 3ms/step - loss: 0.0000e+00
Sekarang mari kita coba sedikit memodifikasi fungsi lambda kita dan menghapus expand_dims.
def calc_mean(x): res = (x[::,0] + x[::,1]) / 2 return res
Saat menyusun model, tidak ada kesalahan pada dimensi yang muncul, tetapi hasilnya sudah berbeda, kerugiannya dianggap tidak bisa dimengerti bagaimana caranya. Jadi di sini expand_dims perlu dilakukan, tidak ada yang akan terjadi secara otomatis.
Epoch 1/5 100/100 [==============================] - 0s 3ms/step - loss: 871.6299 Epoch 2/5 100/100 [==============================] - 0s 3ms/step - loss: 830.2568 Epoch 3/5 100/100 [==============================] - 0s 2ms/step - loss: 830.8041
Dan jika Anda melihat hasil prediksi () yang dikembalikan, Anda dapat melihat bahwa dimensi salah, outputnya [46.], dan diharapkan [[46.]].
Sesuatu seperti itu. Terima kasih untuk semua orang yang membacanya. Dan hati-hati dalam detailnya, efeknya bisa signifikan.