TRẢ LỜI NÀY : nhằm mục đích cung cấp một mô tả chi tiết, mức độ đồ thị / phần cứng của vấn đề - bao gồm các vòng lặp TF2 so với TF1, bộ xử lý dữ liệu đầu vào và thực thi chế độ Eager so với đồ thị. Để biết tóm tắt về vấn đề và hướng dẫn giải quyết, hãy xem câu trả lời khác của tôi.
XÁC MINH THỰC HIỆN : đôi khi cái này nhanh hơn, đôi khi cái kia, tùy thuộc vào cấu hình. Theo như TF2 so với TF1, họ trung bình ngang nhau, nhưng sự khác biệt đáng kể dựa trên cấu hình vẫn tồn tại và TF1 vượt qua TF2 thường xuyên hơn so với ngược lại. Xem "LỢI ÍCH" bên dưới.
EAGER VS. GRAPH : phần cốt lõi của toàn bộ câu trả lời này đối với một số người: Sự háo hức của TF2 chậm hơn so với TF1, theo thử nghiệm của tôi. Chi tiết tiếp tục xuống.
Sự khác biệt cơ bản giữa hai loại này là: Đồ thị thiết lập một mạng tính toán một cách chủ động và thực thi khi được 'nói với' - trong khi Eager thực thi mọi thứ khi tạo. Nhưng câu chuyện chỉ bắt đầu từ đây:
Háo hức KHÔNG phải là không có đồ thị , và trên thực tế có thể chủ yếu là đồ thị, trái với dự đoán. Phần lớn là gì, được thực hiện Biểu đồ - bao gồm trọng lượng mô hình & tối ưu hóa, bao gồm một phần lớn của biểu đồ.
Háo hức xây dựng lại một phần của đồ thị riêng khi thực hiện ; hậu quả trực tiếp của đồ thị không được xây dựng đầy đủ - xem kết quả hồ sơ. Điều này có một chi phí tính toán.
Háo hức là đầu vào w / Numpy chậm hơn ; theo nhận xét và mã Git này , các đầu vào Numpy trong Eager bao gồm chi phí đầu tư sao chép các bộ căng từ CPU sang GPU. Bước qua mã nguồn, sự khác biệt xử lý dữ liệu là rõ ràng; Háo hức trực tiếp vượt qua Numpy, trong khi đồ thị vượt qua các tenxơ mà sau đó đánh giá thành Numpy; không chắc chắn về quy trình chính xác, nhưng sau đó sẽ liên quan đến tối ưu hóa mức GPU
TF2 Eager chậm hơn TF1 Eager - điều này ... thật bất ngờ. Xem kết quả điểm chuẩn dưới đây. Sự khác biệt kéo dài từ không đáng kể đến đáng kể, nhưng là nhất quán. Không chắc chắn tại sao lại như vậy - nếu một nhà phát triển TF làm rõ, sẽ cập nhật câu trả lời.
TF2 so với TF1 : trích dẫn các phần có liên quan của một nhà phát triển TF, Q. Scott Zhu, phản hồi - với sự nhấn mạnh và viết lại của tôi:
Trong háo hức, bộ thực thi cần thực thi các op và trả về giá trị số cho mỗi dòng mã python. Bản chất của thực hiện bước đơn làm cho nó bị chậm .
Trong TF2, Keras tận dụng tf.feft để xây dựng biểu đồ của nó để đào tạo, đánh giá và dự đoán. Chúng tôi gọi chúng là "hàm thực thi" cho mô hình. Trong TF1, "hàm thực thi" là một FuncGraph, chia sẻ một số thành phần phổ biến là hàm TF, nhưng có cách triển khai khác.
Trong quá trình này, bằng cách nào đó, chúng tôi đã để lại một triển khai không chính xác cho train_on_batch (), test_on_batch () và dự đoán_on_batch () . Chúng vẫn đúng về mặt số , nhưng hàm thực thi cho x_on_batch là một hàm python thuần, chứ không phải là hàm python được bọc bởi tf.feft. Điều này sẽ gây ra sự chậm chạp
Trong TF2, chúng tôi chuyển đổi tất cả dữ liệu đầu vào thành tf.data.Dataset, qua đó chúng tôi có thể thống nhất chức năng thực thi của mình để xử lý loại đầu vào duy nhất. Có thể có một số chi phí trong quá trình chuyển đổi tập dữ liệu và tôi nghĩ rằng đây chỉ là chi phí một lần duy nhất, thay vì chi phí mỗi đợt
Với câu cuối cùng của đoạn cuối ở trên và mệnh đề cuối của đoạn dưới đây:
Để khắc phục sự chậm chạp trong chế độ háo hức, chúng ta có @ tf.feft, nó sẽ biến một hàm python thành một biểu đồ. Khi cung cấp giá trị số như mảng np, phần thân của tf.feft được chuyển đổi thành biểu đồ tĩnh, được tối ưu hóa và trả về giá trị cuối cùng, nhanh và có hiệu suất tương tự như chế độ đồ thị TF1.
Tôi không đồng ý - theo kết quả hồ sơ của tôi, cho thấy quá trình xử lý dữ liệu đầu vào của Đại bàng chậm hơn đáng kể so với đồ thị. Ngoài ra, không chắc chắn về tf.data.Dataset
cụ thể, nhưng Eager liên tục gọi nhiều phương thức chuyển đổi dữ liệu giống nhau - xem hồ sơ.
Cuối cùng, cam kết được liên kết của dev: Số lượng thay đổi đáng kể để hỗ trợ các vòng lặp Keras v2 .
Vòng lặp xe lửa : tùy thuộc vào (1) Háo hức so với đồ thị; (2) định dạng dữ liệu đầu vào, đào tạo sẽ tiến hành một vòng lặp tàu riêng biệt - trong TF2 _select_training_loop()
,, training.py , một trong:
training_v2.Loop()
training_distributed.DistributionMultiWorkerTrainingLoop(
training_v2.Loop()) # multi-worker mode
# Case 1: distribution strategy
training_distributed.DistributionMultiWorkerTrainingLoop(
training_distributed.DistributionSingleWorkerTrainingLoop())
# Case 2: generator-like. Input is Python generator, or Sequence object,
# or a non-distributed Dataset or iterator in eager execution.
training_generator.GeneratorOrSequenceTrainingLoop()
training_generator.EagerDatasetOrIteratorTrainingLoop()
# Case 3: Symbolic tensors or Numpy array-like. This includes Datasets and iterators
# in graph mode (since they generate symbolic tensors).
training_generator.GeneratorLikeTrainingLoop() # Eager
training_arrays.ArrayLikeTrainingLoop() # Graph
Mỗi xử lý phân bổ tài nguyên khác nhau và chịu hậu quả về hiệu suất và khả năng.
Vòng lặp xe lửa: fit
vs train_on_batch
, keras
vstf.keras
.: mỗi trong số bốn vòng sử dụng các vòng tàu khác nhau, mặc dù có lẽ không phải trong mọi kết hợp có thể. keras
' fit
, ví dụ, sử dụng một dạng fit_loop
, ví dụ training_arrays.fit_loop()
, và nó train_on_batch
có thể sử dụng K.function()
. tf.keras
có một hệ thống phân cấp phức tạp hơn được mô tả một phần trong phần trước.
Train Loops: tài liệu - chuỗi tài liệu nguồn có liên quan trên một số phương thức thực hiện khác nhau:
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
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 .
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 )
Bộ xử lý dữ liệu đầu vào : tương tự như trên, bộ xử lý được chọn theo từng trường hợp, tùy thuộc vào các cờ bên trong được đặt theo cấu hình thời gian chạy (chế độ thực thi, định dạng dữ liệu, chiến lược phân phối). Trường hợp đơn giản nhất với Eager, hoạt động trực tiếp với mảng Numpy. Đối với một số ví dụ cụ thể, xem câu trả lời này .
KÍCH THƯỚC MÔ HÌNH, KÍCH THƯỚC SỐ LIỆU:
- Là người quyết đoán; không có cấu hình duy nhất đăng quang chính nó trên tất cả các kích thước mô hình và dữ liệu.
- Kích thước dữ liệu liên quan đến kích thước mô hình là quan trọng; đối với dữ liệu & mô hình nhỏ, chi phí truyền dữ liệu (ví dụ: CPU sang GPU) có thể chiếm ưu thế. Tương tự, các bộ xử lý nhỏ có thể chạy chậm hơn trên dữ liệu lớn trên mỗi lần thống trị thời gian chuyển đổi dữ liệu (xem
convert_to_tensor
trong "HỒ SƠ")
- Tốc độ khác nhau trên các phương tiện xử lý tài nguyên khác nhau của các vòng lặp và bộ xử lý dữ liệu đầu vào.
LỢI ÍCH : thịt xay. - Tài liệu Word - Bảng tính Excel
Thuật ngữ :
- % -less số là tất cả giây
- % được tính là
(1 - longer_time / shorter_time)*100
; lý do: chúng tôi quan tâm bởi yếu tố nào nhanh hơn yếu tố kia; shorter / longer
thực sự là một mối quan hệ phi tuyến tính, không hữu ích để so sánh trực tiếp
- % xác định dấu hiệu:
- TF2 so với TF1:
+
nếu TF2 nhanh hơn
- GvE (Biểu đồ so với háo hức):
+
nếu Biểu đồ nhanh hơn
- TF2 = TensorFlow 2.0.0 + Máy ảnh 2.3.1; TF1 = TensorFlow 1.14.0 + Máy ảnh 2.2.5
HỒ SƠ :
HỒ SƠ - Giải thích : Trình biên dịch Spyder 3.3.6 IDE.
Một số chức năng được lặp lại trong tổ của những người khác; do đó, thật khó để theo dõi sự phân tách chính xác giữa các chức năng "xử lý dữ liệu" và "đào tạo", do đó sẽ có một số sự chồng chéo - như được phát âm trong kết quả cuối cùng.
% số liệu tính toán thời gian chạy wrt trừ thời gian xây dựng
- Xây dựng thời gian được tính bằng cách tính tổng tất cả các thời gian chạy (duy nhất) được gọi là 1 hoặc 2 lần
- Thời gian đào tạo được tính bằng cách tính tổng tất cả các thời gian chạy (duy nhất) được gọi là cùng số lần với số lần lặp và một số thời gian chạy của tổ của chúng
- Các hàm được định hình theo tên gốc của chúng , thật không may (nghĩa là
_func = func
sẽ cấu hình như func
), kết hợp trong thời gian xây dựng - do đó cần phải loại trừ nó
MÔI TRƯỜNG KIỂM TRA :
- Mã được thực thi ở dưới cùng với các tác vụ nền tối thiểu đang chạy
- GPU đã được "làm nóng" với một vài lần lặp trước khi lặp lại thời gian, như được đề xuất trong bài viết này
- CUDA 10.0.130, cuDNN 7.6.0, TensorFlow 1.14.0 và TensorFlow 2.0.0 được xây dựng từ nguồn, cộng với Anaconda
- Python 3.7.4, IDE Spyder 3.3.6
- GTX 1070, Windows 10, RAM 24 GB DDR4 2,4 MHz, CPU i7-7700HQ 2,8 GHz
PHƯƠNG PHÁP :
- Kích thước mô hình & kích thước dữ liệu 'nhỏ', 'trung bình' và 'lớn'
- Sửa # tham số cho từng kích thước mô hình, không phụ thuộc vào kích thước dữ liệu đầu vào
- Mô hình "Lớn hơn" có nhiều tham số và lớp hơn
- Dữ liệu "Lớn hơn" có chuỗi dài hơn, nhưng giống nhau
batch_size
vànum_channels
- Mô hình chỉ sử dụng
Conv1D
, Dense
các lớp 'có thể học được'; RNNs tránh trên mỗi phiên bản TF. sự khác biệt
- Luôn luôn chạy một chuyến tàu phù hợp bên ngoài vòng lặp điểm chuẩn, để bỏ qua mô hình & trình tối ưu hóa xây dựng biểu đồ
- Không sử dụng dữ liệu thưa thớt (ví dụ
layers.Embedding()
) hoặc các mục tiêu thưa thớt (ví dụ:SparseCategoricalCrossEntropy()
GIỚI HẠN : một câu trả lời "hoàn chỉnh" sẽ giải thích mọi vòng lặp và vòng lặp đào tạo có thể, nhưng điều đó chắc chắn vượt quá khả năng thời gian của tôi, tiền lương không tồn tại hoặc sự cần thiết chung. Các kết quả chỉ tốt như phương pháp - giải thích với một tâm trí cởi mở.
MÃ :
import numpy as np
import tensorflow as tf
import random
from termcolor import cprint
from time import time
from tensorflow.keras.layers import Input, Dense, Conv1D
from tensorflow.keras.layers import Dropout, GlobalAveragePooling1D
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
import tensorflow.keras.backend as K
#from keras.layers import Input, Dense, Conv1D
#from keras.layers import Dropout, GlobalAveragePooling1D
#from keras.models import Model
#from keras.optimizers import Adam
#import keras.backend as K
#tf.compat.v1.disable_eager_execution()
#tf.enable_eager_execution()
def reset_seeds(reset_graph_with_backend=None, verbose=1):
if reset_graph_with_backend is not None:
K = reset_graph_with_backend
K.clear_session()
tf.compat.v1.reset_default_graph()
if verbose:
print("KERAS AND TENSORFLOW GRAPHS RESET")
np.random.seed(1)
random.seed(2)
if tf.__version__[0] == '2':
tf.random.set_seed(3)
else:
tf.set_random_seed(3)
if verbose:
print("RANDOM SEEDS RESET")
print("TF version: {}".format(tf.__version__))
reset_seeds()
def timeit(func, iterations, *args, _verbose=0, **kwargs):
t0 = time()
for _ in range(iterations):
func(*args, **kwargs)
print(end='.'*int(_verbose))
print("Time/iter: %.4f sec" % ((time() - t0) / iterations))
def make_model_small(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = Conv1D(128, 40, strides=4, padding='same')(ipt)
x = GlobalAveragePooling1D()(x)
x = Dropout(0.5)(x)
x = Dense(64, activation='relu')(x)
out = Dense(1, activation='sigmoid')(x)
model = Model(ipt, out)
model.compile(Adam(lr=1e-4), 'binary_crossentropy')
return model
def make_model_medium(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = ipt
for filters in [64, 128, 256, 256, 128, 64]:
x = Conv1D(filters, 20, strides=1, padding='valid')(x)
x = GlobalAveragePooling1D()(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)
model.compile(Adam(lr=1e-4), 'binary_crossentropy')
return model
def make_model_large(batch_shape):
ipt = Input(batch_shape=batch_shape)
x = Conv1D(64, 400, strides=4, padding='valid')(ipt)
x = Conv1D(128, 200, strides=1, padding='valid')(x)
for _ in range(40):
x = Conv1D(256, 12, strides=1, padding='same')(x)
x = Conv1D(512, 20, strides=2, padding='valid')(x)
x = Conv1D(1028, 10, strides=2, padding='valid')(x)
x = Conv1D(256, 1, strides=1, padding='valid')(x)
x = GlobalAveragePooling1D()(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)
model.compile(Adam(lr=1e-4), 'binary_crossentropy')
return model
def make_data(batch_shape):
return np.random.randn(*batch_shape), \
np.random.randint(0, 2, (batch_shape[0], 1))
def make_data_tf(batch_shape, n_batches, iters):
data = np.random.randn(n_batches, *batch_shape),
trgt = np.random.randint(0, 2, (n_batches, batch_shape[0], 1))
return tf.data.Dataset.from_tensor_slices((data, trgt))#.repeat(iters)
batch_shape_small = (32, 140, 30)
batch_shape_medium = (32, 1400, 30)
batch_shape_large = (32, 14000, 30)
batch_shapes = batch_shape_small, batch_shape_medium, batch_shape_large
make_model_fns = make_model_small, make_model_medium, make_model_large
iterations = [200, 100, 50]
shape_names = ["Small data", "Medium data", "Large data"]
model_names = ["Small model", "Medium model", "Large model"]
def test_all(fit=False, tf_dataset=False):
for model_fn, model_name, iters in zip(make_model_fns, model_names, iterations):
for batch_shape, shape_name in zip(batch_shapes, shape_names):
if (model_fn is make_model_large) and (batch_shape is batch_shape_small):
continue
reset_seeds(reset_graph_with_backend=K)
if tf_dataset:
data = make_data_tf(batch_shape, iters, iters)
else:
data = make_data(batch_shape)
model = model_fn(batch_shape)
if fit:
if tf_dataset:
model.train_on_batch(data.take(1))
t0 = time()
model.fit(data, steps_per_epoch=iters)
print("Time/iter: %.4f sec" % ((time() - t0) / iters))
else:
model.train_on_batch(*data)
timeit(model.fit, iters, *data, _verbose=1, verbose=0)
else:
model.train_on_batch(*data)
timeit(model.train_on_batch, iters, *data, _verbose=1)
cprint(">> {}, {} done <<\n".format(model_name, shape_name), 'blue')
del model
test_all(fit=True, tf_dataset=False)