Tensorflow gradient ส่งคืน nan หรือ Inf
ฉันกำลังพยายามใช้โมเดล WGAN-GP โดยใช้ tensorflow และ keras (สำหรับข้อมูลการฉ้อโกงบัตรเครดิตจาก kaggle )
ส่วนใหญ่ฉันติดตามโค้ดตัวอย่างที่มีให้ในเว็บไซต์ kerasและโค้ดตัวอย่างอื่น ๆ บนอินเทอร์เน็ต (แต่เปลี่ยนจากรูปภาพเป็นข้อมูลของฉัน) และค่อนข้างตรงไปตรงมา
แต่เมื่อฉันต้องการอัปเดตนักวิจารณ์การไล่ระดับน้ำหนักของนักวิจารณ์ที่สูญเสียไปจะกลายเป็นทั้งหมดnanหลังจากผ่านไปไม่กี่แบทช์ และนี่ทำให้น้ำหนักของนักวิจารณ์กลายเป็นnanและหลังจากนั้นน้ำหนักของเครื่องกำเนิดไฟฟ้าก็กลายเป็นnan... ทุกอย่างก็กลายเป็นnan!
ฉันใช้tf.debugging.enable_check_numericsและพบว่าปัญหาเกิดขึ้นเนื่องจากมี-Infการไล่ระดับสีปรากฏขึ้นหลังจากการทำซ้ำบางครั้ง
สิ่งนี้เกี่ยวข้องโดยตรงกับระยะการไล่ระดับสีในการสูญเสียเพราะเมื่อฉันลบปัญหานั้นก็หายไป
โปรดทราบว่าgpตัวเองไม่ได้nanแต่เมื่อฉันได้รับการไล่ระดับสีของน้ำหนักการสูญเสีย WRT นักวิจารณ์ ( c_gradsในรหัสด้านล่าง) มันมีแล้วจะกลายเป็นอย่างใดทั้งหมด-Infnan
ฉันตรวจสอบคณิตศาสตร์และสถาปัตยกรรมเครือข่ายเพื่อหาข้อผิดพลาดที่เป็นไปได้ (เช่นความน่าจะเป็นของการไล่ระดับสีที่หายไป ฯลฯ ) และฉันตรวจสอบรหัสของฉันเพื่อหาจุดบกพร่องที่เป็นไปได้เป็นเวลาหลายชั่วโมง แต่ฉันติดอยู่
ฉันจะขอบคุณมากถ้าใครสามารถหาต้นตอของปัญหาได้
หมายเหตุ: โปรดทราบว่าเอาต์พุตและฟังก์ชันการสูญเสียของนักวิจารณ์นั้นแตกต่างจากกระดาษต้นฉบับเล็กน้อย (เพราะฉันพยายามทำให้เป็นเงื่อนไข) แต่นั่นไม่เกี่ยวข้องกับปัญหาเพราะอย่างที่ฉันพูดไปก่อนหน้านี้ปัญหาทั้งหมดจะหายไป เมื่อฉันลบระยะการลงโทษการไล่ระดับสี
นี่คือนักวิจารณ์ของฉัน:
critic = keras.Sequential([
keras.layers.Input(shape=(x_dim,), name='c-input'),
keras.layers.Dense(64, kernel_initializer=keras.initializers.he_normal(), name='c-hidden-1'),
keras.layers.LeakyReLU(alpha=0.25, name='c-activation-1'),
keras.layers.Dense(32, kernel_initializer=keras.initializers.he_normal(), name='c-hidden-2'),
keras.layers.LeakyReLU(alpha=0.25, name='c-activation-2'),
keras.layers.Dense(2, activation='tanh', name='c-output')
], name='critic')
นี่คือฟังก์ชั่นการไล่ระดับสีของฉัน:
def gradient_penalty(self, batch_size, x_real, x_fake):
# get the random linear interpolation of real and fake data (x hat)
alpha = tf.random.uniform([batch_size, 1], 0.0, 1.0)
x_interpolated = x_real + alpha * (x_fake - x_real)
with tf.GradientTape() as gp_tape:
gp_tape.watch(x_interpolated)
# Get the critic score for this interpolated data
scores = 0.5 * (self.critic(x_interpolated, training=True) + 1.0)
# Calculate the gradients w.r.t to this interpolated data
grads = gp_tape.gradient(scores, x_interpolated)
# Calculate the norm of the gradients
# Gradient penalty enforces the gradient to stay close to 1.0 (1-Lipschitz constraint)
gp = tf.reduce_mean(tf.square(tf.norm(grads, axis=-1) - 1.0))
return gp
และนี่คือรหัสอัปเดตของนักวิจารณ์
# Get random samples from latent space
z = GAN.random_samples((batch_size, self.latent_dim))
# Augment random samples with the class label (1 for class "fraud") for conditioning
z_conditioned = tf.concat([z, tf.ones((batch_size, 1))], axis=1)
# Generate fake data using random samples
x_fake = self.generator(z_conditioned, training=True)
# Calculate the loss and back-propagate
with tf.GradientTape() as c_tape:
c_tape.watch(x_fake)
c_tape.watch(x_real)
# Get the scores for the fake data
output_fake = 0.5 * (self.critic(x_fake) + 1.0)
score_fake = tf.reduce_mean(tf.reduce_sum(output_fake, axis=1))
# Get the scores for the real data
output_real = 0.5 * (self.critic(x_real, training=True) + 1.0)
score_real = tf.reduce_mean((1.0 - 2.0 * y_real) * (output_real[:, 0] - output_real[:, 1]))
# Calculate the gradient penalty
gp = self.gp_coeff * self.gradient_penalty(batch_size, x_real, x_fake)
# Calculate critic's loss (added 1.0 so its ideal value becomes zero)
c_loss = 1.0 + score_fake - score_real + gp
# Calculate the gradients
c_grads = c_tape.gradient(c_loss, self.critic.trainable_weights)
# back-propagate the loss
self.c_optimizer.apply_gradients(zip(c_grads, self.critic.trainable_weights))
หมายเหตุ:อย่างที่คุณเห็นฉันไม่ได้ใช้เอนโทรปีแบบไขว้หรือฟังก์ชันอื่น ๆ ที่เขียนเองโดยเสี่ยงต่อการหารด้วยศูนย์
คำตอบ
ดังนั้นหลังจากขุดค้นในอินเทอร์เน็ตมากขึ้นปรากฎว่านี่เป็นเพราะความไม่แน่นอนของตัวเลขtf.norm(และฟังก์ชันอื่น ๆ ด้วย)
ในกรณีของnormฟังก์ชันปัญหาคือเมื่อคำนวณการไล่ระดับสีค่าของมันจะปรากฏในตัวส่วน ดังนั้นd(norm(x))/dxที่x = 0จะกลายเป็น0 / 0(นี่คือลึกลับdivision-by-zeroผมกำลังมองหา!)
ปัญหาคือบางครั้งกราฟการคำนวณจะลงเอยด้วยสิ่งต่างๆเช่นa / aที่a = 0ซึ่งไม่ได้กำหนดเป็นตัวเลข แต่มีขีด จำกัด อยู่ และเนื่องจากวิธีการทำงานของเทนเซอร์โฟลว์ (ซึ่งคำนวณการไล่ระดับสีโดยใช้กฎลูกโซ่) จึงได้ผลลัพธ์nanเป็น s หรือ+/-Infs
วิธีที่ดีที่สุดอาจเป็นไปได้สำหรับเทนเซอร์โฟลว์ในการตรวจจับรูปแบบเหล่านี้และแทนที่ด้วยค่าเทียบเท่าที่ง่ายต่อการวิเคราะห์ แต่จนกว่าจะเป็นเช่นนั้นเรามีวิธีอื่นและนั่นคือการใช้สิ่งที่เรียกว่าtf.custom_gradientเพื่อกำหนดฟังก์ชันที่กำหนดเองของเราด้วยการไล่ระดับสีที่กำหนดเองของเรา ( ปัญหาที่เกี่ยวข้องกับ github ของพวกเขา )
แม้ว่าในกรณีของฉันจะมีวิธีแก้ปัญหาที่ง่ายกว่านั้น (แม้ว่ามันจะไม่ง่ายนักเมื่อฉันไม่รู้ว่านั่นtf.normคือผู้ร้าย):
แทนที่จะเป็น:
tf.norm(x)
คุณสามารถใช้ได้:
tf.sqrt(tf.reduce_sum(tf.square(x)) + 1.0e-12)
หมายเหตุ:โปรดระวังเกี่ยวกับขนาด (ถ้า x เป็นเมทริกซ์หรือเทนเซอร์และคุณต้องคำนวณบรรทัดฐานแบบแถวหรือคอลัมน์ที่ชาญฉลาด)! นี่เป็นเพียงตัวอย่างโค้ดเพื่อแสดงแนวคิด
หวังว่ามันจะช่วยใครบางคน