Làm thế nào để áp dụng cắt gradient trong TensorFlow?


96

Xem xét mã ví dụ .

Tôi muốn biết Cách áp dụng tính năng cắt chuyển màu trên mạng này trên RNN, nơi có khả năng bùng nổ các gradient.

tf.clip_by_value(t, clip_value_min, clip_value_max, name=None)

Đây là một ví dụ có thể được sử dụng nhưng tôi giới thiệu cái này ở đâu? Trong định nghĩa của RNN

    lstm_cell = rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0)
    # Split data because rnn cell needs a list of inputs for the RNN inner loop
    _X = tf.split(0, n_steps, _X) # n_steps
tf.clip_by_value(_X, -1, 1, name=None)

Nhưng điều này không có ý nghĩa vì tensor _X là đầu vào chứ không phải grad là những gì sẽ được cắt?

Tôi có phải xác định Trình tối ưu hoá của riêng mình cho việc này hay có tuỳ chọn đơn giản hơn không?

Câu trả lời:


143

Việc cắt gradient cần phải xảy ra sau khi tính toán các gradient, nhưng trước khi áp dụng chúng để cập nhật các thông số của mô hình. Trong ví dụ của bạn, cả hai điều đó đều được xử lý bởi AdamOptimizer.minimize()phương thức.

Để cắt các gradient của bạn, bạn sẽ cần tính toán, cắt và áp dụng chúng một cách rõ ràng như được mô tả trong phần này trong tài liệu API của TensorFlow . Cụ thể, bạn sẽ cần thay thế lời gọi minimize()phương thức bằng một thứ như sau:

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
gvs = optimizer.compute_gradients(cost)
capped_gvs = [(tf.clip_by_value(grad, -1., 1.), var) for grad, var in gvs]
train_op = optimizer.apply_gradients(capped_gvs)

4
Styrke, cảm ơn cho bài viết. Bạn có biết các bước tiếp theo là gì để thực sự chạy một lần lặp lại trình tối ưu hóa không? Thông thường, trình tối ưu hóa được khởi tạo optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) và sau đó lặp lại trình tối ưu hóa được thực hiện optimizer.run()nhưng việc sử dụng optimizer.run()dường như không hoạt động trong trường hợp này?
applecider

6
Ok đã nhận nó optimizer.apply_gradients(capped_gvs)mà cần phải được giao cho một cái gì đó x = optimizer.apply_gradients(capped_gvs)sau đó trong phiên, bạn có thể đào tạo nhưx.run(...)
applecider

3
Hãy gửi lời cảm ơn tới @ repeat-cuingnet để có gợi ý chỉnh sửa hay . (Mà không may đã bị từ chối bởi người nhận xét vội vàng)
Styrke

Điều này mang lại cho tôi UserWarning: Converting sparse IndexedSlices to a dense Tensor with 148331760 elements. This may consume a large amount of memory.Vì vậy, bằng cách nào đó các gradient thưa thớt của tôi được chuyển thành dày đặc. Bất kỳ ý tưởng làm thế nào để khắc phục vấn đề này?
Pekka

8
Trên thực tế đúng cách để gradient clip (theo tài liệu tensorflow, các nhà khoa học máy tính, và logic) là có tf.clip_by_global_norm, theo đề nghị của @danijar
gdelab

116

Bất chấp những gì có vẻ phổ biến, bạn có thể muốn cắt toàn bộ gradient theo tiêu chuẩn toàn cầu của nó:

optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = zip(*optimizer.compute_gradients(loss))
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimize = optimizer.apply_gradients(zip(gradients, variables))

Việc cắt từng ma trận gradient riêng lẻ sẽ thay đổi tỷ lệ tương đối của chúng nhưng cũng có thể:

optimizer = tf.train.AdamOptimizer(1e-3)
gradients, variables = zip(*optimizer.compute_gradients(loss))
gradients = [
    None if gradient is None else tf.clip_by_norm(gradient, 5.0)
    for gradient in gradients]
optimize = optimizer.apply_gradients(zip(gradients, variables))

Trong TensorFlow 2, một đoạn băng tính toán các độ dốc, các trình tối ưu hóa đến từ Keras và chúng tôi không cần lưu trữ bản cập nhật vì nó chạy tự động mà không cần chuyển nó đến một phiên:

optimizer = tf.keras.optimizers.Adam(1e-3)
# ...
with tf.GradientTape() as tape:
  loss = ...
variables = ...
gradients = tape.gradient(loss, variables)
gradients, _ = tf.clip_by_global_norm(gradients, 5.0)
optimizer.apply_gradients(zip(gradients, variables))

10
Ví dụ tốt với clip_by_global_norm()! Điều này cũng được mô tả như the correct way to perform gradient clippingtrong tài liệu về tensorflow
MZHm

9
@Escachator Nó theo kinh nghiệm và sẽ phụ thuộc vào mô hình của bạn và có thể là nhiệm vụ. Những gì tôi làm là hình dung tiêu chuẩn gradient tf.global_norm(gradients)để xem nó là phạm vi bình thường và sau đó cắt một chút trên đó để ngăn chặn các ngoại lệ làm rối loạn đào tạo.
danijar

1
bạn vẫn sẽ gọi opt.minimize()sau hay bạn sẽ gọi một cái gì đó khác như opt.run()được đề xuất trong một số nhận xét về các câu trả lời khác?
reese0106

3
@ reese0106 Không, optimizer.minimize(loss)chỉ là một cách viết tắt để tính toán và áp dụng các gradient. Bạn có thể chạy ví dụ trong câu trả lời của tôi với sess.run(optimize).
danijar

1
Vì vậy, nếu tôi đang sử dụng tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)trong một chức năng thử nghiệm, thì của bạn optimizesẽ thay thế train_opchính xác của tôi ? Ngay bây giờ tôi train_op = optimizer.minimize(loss, global_step=global_step))vì vậy tôi đang cố gắng để đảm bảo rằng tôi điều chỉnh cho phù ...
reese0106

10

Điều này thực sự được giải thích đúng trong tài liệu. :

Việc gọi Minimum () đảm nhận cả việc tính toán các gradient và áp dụng chúng cho các biến. Nếu bạn muốn xử lý các gradient trước khi áp dụng chúng, thay vào đó, bạn có thể sử dụng trình tối ưu hóa theo ba bước:

  • Tính toán độ dốc bằng compute_gradients ().
  • Xử lý các gradient như bạn muốn.
  • Áp dụng các gradient đã xử lý với apply_gradients ().

Và trong ví dụ mà họ cung cấp, họ sử dụng 3 bước sau:

# Create an optimizer.
opt = GradientDescentOptimizer(learning_rate=0.1)

# Compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(loss, <list of variables>)

# grads_and_vars is a list of tuples (gradient, variable).  Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]

# Ask the optimizer to apply the capped gradients.
opt.apply_gradients(capped_grads_and_vars)

Đây MyCapperlà bất kỳ chức năng nào bao hàm gradient của bạn. Danh sách các chức năng hữu ích (khác tf.clip_by_value()) có ở đây .


bạn vẫn sẽ gọi opt.minimize()sau hay bạn sẽ gọi một cái gì đó khác như opt.run()được đề xuất trong một số nhận xét về các câu trả lời khác?
reese0106

@ reese0106 Không, bạn cần gán opt.apply_gradients(...)cho một biến như train_stepví dụ (giống như bạn sẽ cho opt.minimize()Và trong vòng lặp chính của bạn, bạn gọi nó như thường lệ để đào tạo.sess.run([train_step, ...], feed_dict)
dsalaj

Hãy nhớ rằng gradient được định nghĩa là vectơ của các đạo hàm của wrt mất mát cho tất cả các tham số trong mô hình. TensorFlow đại diện cho nó như một danh sách Python chứa một bộ giá trị cho mỗi biến và độ dốc của nó. Điều này có nghĩa là để cắt định mức gradient, bạn không thể cắt từng tensor riêng lẻ, bạn cần xem xét danh sách cùng một lúc (ví dụ: sử dụng tf.clip_by_global_norm(list_of_tensors)).
danijar

8

Đối với những người muốn hiểu ý tưởng của việc cắt gradient (theo tiêu chuẩn):

Bất cứ khi nào định mức gradient lớn hơn một ngưỡng cụ thể, chúng tôi cắt định mức gradient để nó nằm trong ngưỡng. Ngưỡng này đôi khi được đặt thành 5.

Đặt gradient là g và max_norm_threshold là j .

Bây giờ, nếu || g || > j , chúng tôi làm:

g = ( j * g ) / || g ||

Đây là việc triển khai được thực hiện trong tf.clip_by_norm


Nếu tôi cần chọn ngưỡng bằng tay, có phương pháp phổ biến nào để thực hiện việc này không?
ningyuwhut

Đây là một loại ma thuật đen được đề xuất trong một số bài báo. Nếu không, bạn phải thực hiện nhiều thử nghiệm và tìm ra cái nào hoạt động tốt hơn.
kmario23

4

IMO giải pháp tốt nhất là kết hợp trình tối ưu hóa của bạn với trình trang trí ước tính của TF tf.contrib.estimator.clip_gradients_by_norm:

original_optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
optimizer = tf.contrib.estimator.clip_gradients_by_norm(original_optimizer, clip_norm=5.0)
train_op = optimizer.minimize(loss)

Bằng cách này, bạn chỉ phải xác định điều này một lần và không phải chạy nó sau mỗi lần tính toán độ dốc.

Tài liệu: https://www.tensorflow.org/api_docs/python/tf/contrib/estimator/clip_gradients_by_norm


2

Gradient Clipping về cơ bản giúp ích trong trường hợp các gradient phát nổ hoặc biến mất. Nói rằng tổn thất của bạn quá cao sẽ dẫn đến các gradient theo cấp số nhân chảy qua mạng có thể dẫn đến các giá trị Nan. Để khắc phục điều này, chúng tôi cắt chuyển sắc trong một phạm vi cụ thể (-1 đến 1 hoặc bất kỳ phạm vi nào tùy theo điều kiện).

clipped_value=tf.clip_by_value(grad, -range, +range), var) for grad, var in grads_and_vars

trong đó grads _and_vars là các cặp gradient (mà bạn tính toán qua tf.compute_gradients) và các biến của chúng sẽ được áp dụng.

Sau khi cắt, chúng tôi chỉ cần áp dụng giá trị của nó bằng cách sử dụng trình tối ưu hóa. optimizer.apply_gradients(clipped_value)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.