Figure 1. Eager Execution vs. Graph Execution
(https://towardsdatascience.com/eager-execution-vs-graph-execution-which-is-better-38162ea4dbf6)
Tensorflow cung cấp cách thực thi chương trình mô hình của mình dưới hai dạng:
- Eager excecution
- Graph execution
Trong các phiên bản Tensorflow phiên bản 1.*, graph execution là cách thức thực thi mặc định. Tuy nhiên từ các phiên bản Tensorflow phiên bản 2.*, eager execution lại trở thành cách thực thi mặc định thay thế cho graph execution. Trong bài viết ngày hôm nay, mình cùng các bạn tìm hiểu về hai cách thức, sự khác nhau giữa chúng và tại sao lại có sự thay đổi giữa hai phiên bản 1.* và 2.* như vậy nhé.
A. Eager execution
1. Định nghĩa.
Theo định nghĩa từ trang chủ Tensorflow, Tensorflow's eager execution là một môi trường lập trình có thể thực thi mô hình và ngay lập tức trả về giá trị cụ thể thay vì xây dựng một đồ thị biểu diễn tính toán để chạy sau (graph execution). Thực thi ở chế độ Eager đơn giản giúp những người mới làm quen với thư viện Tensorflow hoặc mới làm quen với học máy có thể bắt đầu dễ dàng lập trình và sửa lỗi, phù hợp với trong quá trình nghiên cứu, thí nghiệm khi mã nguồn cần thay đổi liên tục.
2. Tính năng
Chế độ thực thi Eager cung cấp những tính năng sau đây:
- Giao diện trực quan (intuitive interface) do sử dụng kiến trúc giống như Python
- Dễ dàng sửa lỗi: có thể gọi trực tiếp các operation để quan sát mô hình chạy như thế này, thay đổi ra sao. Có thể sử dụng các công cụ sửa lỗi chuẩn của Python trong quá trình sửa lỗi.
- Luồng thực thi dễ hiểu giống như Python
- Hỗ trợ thực thi bằng phần cứng chuyên dụng như GPU và TPU
3. Ví dụ
Ví dụ code thực thi ở chế độ Eager excecution:
import tensorflow as tf
x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m)) # => "hello, [[4.]]"
B. Eager execution
1. Định nghĩa
Eager execution thân thiện, dễ dùng nhưng có nhược điểm tốc độ thực thi chậm hơn đáng kể so với graph execution.
Graph execution cho phép thực thi mã nguồn dưới dạng đồ thị được gọi là Tensorflow graph không cần đến môi trường python. Tensorflow graph là một kiến trúc dữ liệu biểu diễn mô hình của bạn dưới dạng dòng chảy của dữ liệu qua các phép tính được định nghĩa dưới một context tf.Graph.
- tf.Operation đại diện cho các phép tính
- tf.Tensor đại diện cho các lớp dữ liệu
Ví dụ lớp add_1 bên trên đại diện cho phép cộng với input chính là hai output từ phép MatMul_1 và add_1_re.
2. Tính năng.
- Tính linh hoạt (flexibility): Tensorflow graph có thể hoạt động trên môi trường không cần Python do đó có thể dễ dàng chạy dưới các nền tảng như điện thoại, thiết bị nhúng.
- Dễ dàng tối ưu: Có thể dễ dàng tối ưu bằng một số phương pháp như
- Constant folding. Constant folding là phương pháp tối ưu bằng cách tính toán trước những phép tính có giá trị không đổi vào thời điểm biên dịch chứ không phải tại thời điểm chạy.
- Chia nhỏ các đơn vị tính toán độc lập, chạy song song chúng trên nhiều luồng và thiết bị khác nhau
- Loại bỏ biểu thức con chung (eliminate common subexpressions)
- Hỗ trợ chạy trên phần cứng chuyên dụng như GPU, TPU .
- Tốc độ tính toán nhanh: Sự chênh lệch tốc độ tính toán tùy thuộc vào thiết bị bạn sử dụng. Ở trong một số trường hợp, code ở Graph execution chạy lâu hơn đáng kể so với eager execution do thời gian để khởi tạo graph lâu hơn nhiều lần so với thời gian thực thi graph. Do đó điều kiện tuyệt vời nhất là bạn tránh khởi tạo graph nhiều lần.
3. Ví dụ:
# Define a Python function.
def a_regular_function(x, y, b): x = tf.matmul(x, y) x = x + b return x # `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function) # Make some tensors.
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0) orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)
Có một điểm lưu ý đặc biệt là ở phiên bản Tensorflow 2.*, khi dùng Graph execution chúng ta không cần thực thi Session hoặc khởi tạo biến bằng tf.Placeholder mà các đối tượng được sử dụng tương tư đối tượng trong Python. Đồng thời chúng ta không cần định nghĩa trực tiếp đồ thị bằng tf.Graph mà chỉ cần dùng tf.function, các biến cũng không cần yêu cầu chia sẻ qua các phạm vi (scopes).
tf.Function có thể hoạt động trên cả hai chế độ Eager execution và Graph execution và chạy ở chế độ mặc định của Tensorflow 2.* là Graph execution. Do đó để chắc chắn bạn đang thực thi ở chế độ Eager, hãy dùng thêm câu lệnh bên dưới nhé.
tf.config.run_functions_eagerly(True)
C. Từ Graph execution đến Eager execution ?
Theo một số ý kiến, trước phiên bản 2.0 Tensorflow ưu tiên Graph execution hơn vì ưu điểm tốc độ xử lý, hiệu quả và linh hoạt hơn. Tuy nhiên, chúng không thân thiện với những người mới lập trình. Trong lúc đó, một đáng thủ đáng gờm của Tensorflow là Pytorch để sử dụng chiến thuật tận dụng điểm yếu đó và thu hút được một lượng lớn người dùng.
TensorFlow vs. PyTorch Google Search Results by Google Trends
(https://towardsdatascience.com/eager-execution-vs-graph-execution-which-is-better-38162ea4dbf6)
Đọc qua, cái mà chúng ta có thể nghĩ ngay là tuyệt vời nhất là lập trình trên Eager và chạy trên Graph execution. Chúng ta hoàn toàn có thể làm điều đó với Tensorflow 2.0, cách lập trình ở graph execution đã được đơn giản hóa rất nhiều. Tuy nhiên có một cái mình vẫn lấn cấn khi code bằng Tensorflow là sự khác biệt giữa mỗi phiên bản của chúng quá nhiều, code rất dễ lỗi nếu khác phiên bản. Nếu có được sự ổn định như Pytorch thì hay biết mấy 😄. Và tất nhiên lựa trên bên nào là quyết định ở bạn....