Tại sao mô hình máy ảnh dự đoán chậm hơn sau khi biên dịch?


23

máy ảnh tốc độ dự đoán

Về lý thuyết, dự đoán nên không đổi vì các trọng số có kích thước cố định. Làm cách nào để tôi lấy lại tốc độ sau khi biên dịch (mà không cần phải gỡ bỏ trình tối ưu hóa)?

Xem thử nghiệm liên quan: https://nbviewer.jupyter.org/github/off99555/TensorFlowExperiment/blob/master/test-prediction-speed-after-compile.ipynb?flush_cache=true


Tôi nghĩ rằng bạn cần phải phù hợp với mô hình sau khi biên dịch sau đó sử dụng mô hình được đào tạo để dự đoán. Tham khảo tại đây
ngây thơ

@naive Lắp là không liên quan đến vấn đề. Nếu bạn biết mạng thực sự hoạt động như thế nào, bạn sẽ tò mò tại sao dự đoán lại chậm hơn. Khi dự đoán, chỉ các trọng số được sử dụng để nhân ma trận và các trọng số phải được cố định trước và sau khi biên dịch, vì vậy thời gian dự đoán sẽ không đổi.
off99555

Tôi biết điều đó không liên quan đến vấn đề . Và, người ta không cần biết mạng lưới hoạt động như thế nào để chỉ ra rằng các nhiệm vụ bạn đã đưa ra và so sánh độ chính xác thực sự là vô nghĩa. Không phù hợp với mô hình trên một số dữ liệu bạn dự đoán và bạn thực sự đang so sánh thời gian thực hiện. Đây không phải là trường hợp sử dụng thông thường hoặc đúng cho mạng thần kinh
ngây thơ

3
@naive Vấn đề liên quan đến việc hiểu hiệu suất mô hình được biên dịch so với không biên dịch, không liên quan gì đến độ chính xác hoặc thiết kế mô hình. Đây là một vấn đề hợp pháp có thể khiến người dùng TF phải trả giá - Tôi không có manh mối nào cho đến khi vấp phải câu hỏi này.
OverLordGoldDragon

1
@naive Bạn không thể không fitcompile; Trình tối ưu hóa thậm chí không tồn tại để cập nhật bất kỳ trọng lượng nào. predict có thể được sử dụng mà không có fithoặc compilenhư được mô tả trong câu trả lời của tôi, nhưng sự khác biệt về hiệu suất không nên quá lớn - do đó là vấn đề.
OverLordGoldDragon

Câu trả lời:


22

CẬP NHẬT - 1/15/2020 : cách thực hành tốt nhất hiện tại đối với kích thước lô nhỏ phải là cung cấp trực tiếp đầu vào cho mô hình - tức là preds = model(x)và nếu các lớp hoạt động khác nhau khi đào tạo / suy luận , model(x, training=False). Theo cam kết mới nhất, điều này hiện được ghi nhận .

Tôi chưa điểm chuẩn những điều này, nhưng theo thảo luận về Git , nó cũng đáng để thử predict_on_batch()- đặc biệt là với các cải tiến trong TF 2.1.


CULPRIT TUYỆT VỜI : self._experimental_run_tf_function = True. Đó là thử nghiệm . Nhưng nó không thực sự xấu.

Để bất kỳ nhà phát triển TensorFlow đọc: làm sạch mã của bạn . Đó là một mớ hỗn độn. Và nó vi phạm các thực hành mã hóa quan trọng, chẳng hạn như một chức năng làm một việc ; _process_inputsthực hiện nhiều hơn "quy trình đầu vào", tương tự cho _standardize_user_data. "Tôi không trả đủ" - nhưng bạn làm lương, trong hiệp phụ thời gian dành cho sự hiểu biết cụ của riêng bạn, và trong người dùng điền trang Các vấn đề của bạn với lỗi dễ dàng hơn giải quyết với một mã số rõ ràng hơn.


TÓM TẮT : nó chỉ chậm hơn một chút với compile().

compile()thiết lập một cờ nội bộ gán một chức năng dự đoán khác nhau predict. Hàm này xây dựng một biểu đồ mới theo mỗi cuộc gọi, làm chậm nó xuống so với không biên dịch. Tuy nhiên, sự khác biệt chỉ được phát âm khi thời gian đào tạo ngắn hơn nhiều so với thời gian xử lý dữ liệu . Nếu chúng ta tăng kích thước mô hình lên ít nhất là cỡ trung bình, hai cái sẽ trở nên bằng nhau. Xem mã ở phía dưới.

Sự tăng nhẹ thời gian xử lý dữ liệu này được bù đắp nhiều hơn bởi khả năng đồ thị được khuếch đại. Vì sẽ hiệu quả hơn khi chỉ giữ một biểu đồ mô hình xung quanh, một biểu đồ biên dịch trước bị loại bỏ. Tuy nhiên : nếu mô hình của bạn nhỏ so với dữ liệu, bạn tốt hơn là không cần compile()suy luận mô hình. Xem câu trả lời khác của tôi cho một cách giải quyết.


TÔI NÊN LÀM GÌ?

So sánh hiệu suất mô hình được biên dịch so với không biên dịch như tôi có trong mã ở phía dưới.

  • Biên dịch nhanh hơn : chạy predicttrên một mô hình biên dịch.
  • Biên dịch chậm hơn : chạy predicttrên một mô hình chưa biên dịch.

Có, cả hai đều có thể, và nó sẽ phụ thuộc vào (1) kích thước dữ liệu; (2) kích thước mô hình; (3) phần cứng. Mã ở phía dưới thực sự cho thấy mô hình được biên dịch nhanh hơn, nhưng 10 lần lặp là một mẫu nhỏ. Xem "cách giải quyết" trong câu trả lời khác của tôi để biết "cách thực hiện".


CHI TIẾT :

Điều này mất một lúc để gỡ lỗi, nhưng rất vui. Dưới đây tôi mô tả các thủ phạm chính mà tôi đã phát hiện ra, trích dẫn một số tài liệu có liên quan và hiển thị kết quả hồ sơ dẫn đến nút cổ chai cuối cùng.

( FLAG == self.experimental_run_tf_function, cho ngắn gọn)

  1. Modeltheo mặc định khởi tạo với FLAG=False. compile()đặt nó thành True.
  2. predict() liên quan đến việc có được chức năng dự đoán, func = self._select_training_loop(x)
  3. Không có bất kỳ kwarg đặc biệt nào được chuyển đến predictcompile, tất cả các cờ khác đều như vậy:
    • (A) FLAG==True ->func = training_v2.Loop()
    • (B) FLAG==False ->func = training_arrays.ArrayLikeTrainingLoop()
  4. Từ chuỗi mã nguồn , (A) phụ thuộc nhiều vào biểu đồ, sử dụng chiến lược phân phối nhiều hơn và các op có xu hướng tạo và phá hủy các phần tử biểu đồ, điều này có thể ảnh hưởng đến hiệu suất.

Thủ phạm thực sự : _process_inputs(), chiếm 81% thời gian chạy . Thành phần chính của nó? _create_graph_function(), 72% thời gian chạy . Phương pháp này thậm chí không tồn tại cho (B) . Tuy nhiên, sử dụng một mô hình cỡ trung bình, _process_inputsbao gồm ít hơn 1% thời gian chạy . Mã ở dưới cùng, và kết quả hồ sơ theo sau.


QUY TRÌNH DỮ LIỆU :

(A) : <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>, được sử dụng trong _process_inputs(). Mã nguồn có liên quan

(B) : numpy.ndarray, được trả lại bởi convert_eager_tensors_to_numpy. Mã nguồn có liên quan , và ở đây


CHỨC NĂNG THỰC HIỆN MÔ HÌNH (ví dụ dự đoán)

(A) : chức năng phân phối , và ở đây

(B) : hàm phân phối (khác nhau)tại đây


HỒ SƠ : kết quả cho mã trong câu trả lời khác của tôi, "mô hình nhỏ" và trong câu trả lời này, "mô hình trung bình":

Mô hình nhỏ : 1000 lần lặp,compile()

Mô hình nhỏ : 1000 lần lặp, không compile()

Mô hình trung bình : 10 lần lặp


TÀI LIỆU (gián tiếp) về tác dụng của compile(): nguồn

Không giống như các hoạt động khác của TensorFlow, chúng tôi không chuyển đổi đầu vào số python thành tenxơ. Hơn nữa, một biểu đồ mới được tạo cho mỗi giá trị số python riêng biệt , ví dụ như gọi g(2)g(3)sẽ tạo ra hai biểu đồ mới

function khởi tạo một biểu đồ riêng cho mỗi bộ hình dạng và kiểu dữ liệu đầu vào duy nhất . Ví dụ: đoạn mã sau sẽ dẫn đến ba biểu đồ riêng biệt được theo dõi, vì mỗi đầu vào có hình dạng khác nhau

Một đối tượng tf.feft có thể cần ánh xạ tới nhiều biểu đồ tính toán dưới mui xe. Điều này chỉ được hiển thị khi hiệu suất (biểu đồ theo dõi có chi phí tính toán và bộ nhớ khác không ) nhưng không ảnh hưởng đến tính chính xác của chương trình


QUỐC GIA :

from tensorflow.keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from tensorflow.keras.layers import Flatten, Dropout
from tensorflow.keras.models import Model
import numpy as np
from time import time

def timeit(func, arg, iterations):
    t0 = time()
    for _ in range(iterations):
        func(arg)
    print("%.4f sec" % (time() - t0))

batch_size = 32
batch_shape = (batch_size, 400, 16)
ipt   = Input(batch_shape=batch_shape)
x     = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
x     = LSTM(512, activation='relu', return_sequences=True)(ipt)
x     = Conv1D(128, 400, 1, padding='same')(x)
x     = Flatten()(x)
x     = Dense(256, activation='relu')(x)
x     = Dropout(0.5)(x)
x     = Dense(128, activation='relu')(x)
x     = Dense(64,  activation='relu')(x)
out   = Dense(1,  activation='sigmoid')(x)
model = Model(ipt, out)

X = np.random.randn(*batch_shape)
timeit(model.predict, X, 10)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 10)

Đầu ra :

34.8542 sec
34.7435 sec

1
Kết luận về những gì chúng ta nên làm để có được tốc độ dự đoán nhanh nhất cho bất kỳ kích thước mô hình nào? Có phải chỉ để không làm compile()?
off99555

3
@ off99555 "cho bất kỳ kích thước mô hình" - không có điều đó. Đọc toàn bộ câu trả lời - nếu tôi mất hàng giờ để gỡ lỗi, một vài phút từ người hỏi không nên vô lý.
OverLordGoldDragon

Tôi đã đọc toàn bộ nhưng thật khó hiểu vì tôi không phải là người đã gỡ lỗi mã. Vì vậy, bạn cần đưa ra một kết luận không liên quan đến các biến trung gian mà bạn tìm thấy trong giai đoạn gỡ lỗi. Ví dụ: "Nếu mô hình của bạn nhỏ, thì không sử dụng biên dịch. Nếu mô hình của bạn có kích thước trung bình, bạn có thể sử dụng biên dịch." Một cái gì đó tương tự.
off99555

1
@ off99555 Đủ công bằng; cập nhật. Phần mới là khá phổ biến, nhưng tôi có thể thấy nó không được nhận ra ngay lập tức.
OverLordGoldDragon

1
@ off99555 Không phải tôi đã thử nghiệm, nhưng các mô hình rất lớn (ResNet, v.v.) có thể chạy được biên dịch nhanh hơn đáng kể, đặc biệt. nếu phân phối trên nhiều thiết bị - vì (A) nặng hơn về đồ thị và phân phối. Bài kiểm tra chắc chắn nhất là một bài kiểm tra - giống như trong câu trả lời. Không quen thuộc với TF lite, nhưng đó là một câu hỏi riêng
OverLordGoldDragon

15

CẬP NHẬT : xem câu trả lời thực tế được đăng dưới dạng một câu trả lời riêng biệt; bài đăng này chứa thông tin bổ sung


.compile() thiết lập phần lớn biểu đồ TF / Keras, bao gồm tổn thất, số liệu, độ dốc và một phần trình tối ưu hóa và trọng số của nó - đảm bảo sự chậm lại đáng chú ý.

Có gì bất ngờ là mức độ của suy thoái - gấp 10 lần trên thí nghiệm của riêng tôi, và cho predict(), mà không cập nhật bất kỳ trọng lượng. Nhìn vào mã nguồn của TF2, các phần tử biểu đồ xuất hiện đan xen chặt chẽ, với các tài nguyên không nhất thiết phải được phân bổ "công bằng".

Các nhà phát triển có thể bỏ qua predicthiệu năng của một mô hình chưa biên dịch, vì các mô hình thường được sử dụng được biên dịch - nhưng trong thực tế , đây là một sự khác biệt không thể chấp nhận được. Cũng có thể đó là một "điều ác cần thiết", vì có một cách giải quyết đơn giản (xem bên dưới).

Đây không phải là một câu trả lời hoàn chỉnh và tôi hy vọng ai đó có thể cung cấp nó ở đây - nếu không, tôi khuyên bạn nên mở một vấn đề Github trên TensorFlow. (OP có; ở đây )


Giải pháp thay thế : đào tạo một mô hình, lưu trọng số của nó , xây dựng lại mô hình mà không cần biên dịch, tải trọng số. Đừng không lưu toàn bộ mô hình (ví dụ model.save()), vì nó sẽ load biên soạn - thay vì sử dụng model.save_weights()model.load_weights().

Cách giải quyết 2 : ở trên, nhưng sử dụng load_model(path, compile=False); tín dụng gợi ý: D. Möller


CẬP NHẬT : để làm rõ, trình tối ưu hóa không khởi tạo đầy đủ compile, kể cả nó weightsupdatestensors - điều này được thực hiện khi cuộc gọi đầu tiên để một chức năng phù hợp được thực hiện ( fit, train_on_batch, vv), thông qua model._make_train_function().

Các hành vi quan sát được như vậy thậm chí còn kỳ lạ hơn. Tệ hơn nữa, việc xây dựng trình tối ưu hóa không gợi ra bất kỳ sự chậm lại nào nữa (xem bên dưới) - cho thấy "kích thước biểu đồ" không phải là lời giải thích chính ở đây.


BIÊN TẬP : trên một số mô hình, chậm 30 lần . TensorFlow, những gì bạn đã làm. Ví dụ dưới đây:

from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
import numpy as np
from time import time

def timeit(func, arg, iterations):
    t0 = time()
    for _ in range(iterations):
        func(arg)
    print("%.4f sec" % (time() - t0))

ipt   = Input(shape=(4,))
x     = Dense(2, activation='relu')(ipt)
out   = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)

X = np.random.randn(32,4)

timeit(model.predict, X, 1000)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 1000)
model._make_train_function()  # build optimizer
timeit(model.predict, X, 1000)

Đầu ra :

0.9891 sec
29.785 sec
29.521 sec

1
Nó thật thú vị. Đã có lúc tôi muốn thử nghiệm đào tạo với một biểu đồ tĩnh model.fit()so với một vòng lặp động với thực thi háo hức để xem liệu mất hiệu suất có quá lớn không ...
Daniel Möller

1
Trước đây tôi có thể nhận thấy sự khác biệt đáng kể về tốc độ giữa Keras và PyTorch (cách PyTorch nhanh hơn).
Daniel Möller

1
Tôi đã mở một vấn đề ở đây: github.com/tensorflow/tensorflow/issues/33340
off99555

2
Đúng. Đó là một lựa chọn thiết kế tồi mà bạn đặt mã đào tạo liên quan vào dự đoán. Bởi vì người dùng sẽ sử dụng chức năng dự đoán này tuần tự nhiều lần trong sản xuất. Nó nên hoạt động nhanh nhất để gây ra ít bất ngờ nhất. So sánh với việc thực hiện numpy, bạn chỉ cần nhân một ma trận, thêm một thiên vị, kích hoạt và đó là một lớp dày đặc. Không cần phải quan tâm đến bất kỳ chức năng mất.
off99555

1
Gợi ý, bạn có thể sử dụng load_model(name, compile=False), nó đơn giản hơn việc lưu / tải trọng lượng và tạo lại mô hình.
Daniel Möller
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.