Giới thiệu về Tensorflow Framework

Nhằm hỗ trợ người đọc hiểu rõ và thuận lợi trong quá trình triển khai các Project sử dụng Framework Tensorflow, trong bài viết này em sẽ trình bày chi tiết từ những khái niệm cơ bản nhất đến phần triển khai model và đưa ra ứng dụng thực tế.

Nội dung bài viết sẽ gồm các phần sau:

1.  Giới thiệu Tensorflow. Tại sao lựa chọn Tensorflow?
2.  Cài đặt
3.  Các khái niệm cơ bản
4.  Luồng hoạt động
5.  Xây dựng một mô hình học máy dùng Tensorflow
6.  Lợi ích mang lại từ Tensorflow

1. Giới thiệu Tensorflow

TensorFlow là một thư viện mã nguồn mở được tạo ra để phục vụ cho việc tính toán phân tán số sử dụng cách thức tính toán là data flow graphs. TensorFlow được Google phát triển nhằm mục đích hỗ trợ việc nghiên chuyên sâu và phát triển các ứng dụng AI (Machine learning/Deep learning), phiên bản đầu tiên được Google công bố vào tháng 11/2015 và mới đây nhất ngày 6/5/2020 là phiên bản TensorFlow 2.2.0. Phần Core của TensorFlow được viết bằng C++ nhưng người dùng có thể sử dụng cả C++, Python, Java, Javascript để triển khai các mô hình của mình (phổ biến nhất là Python).

Tại sao lựa chọn Tensorflow?

- Tích hợp sẵn rất nhiều các thư viện machine learning

- Có khả năng tương thích và mở rộng tốt. Được Google phát triển cho machine learning phục vụ cả nghiên cứu lẫn xây dựng các ứng dụng thực tế

- Tính phổ biến

Ảnh 1.
 

Tensorflow chia làm các layer cơ bản:

-  Machine Types là các thiết bị cơ bản của thiết bị tính toán như CPU, GPU và hệ điều hành.

-  Tensorflow Kernel là một gói triển khai của Tensorflow (Có cả CPU và GPU) nó là cách giao thức gần nhất đối với các thiết bị phần cứng của tensorflow.

-  Low-Level Tensorflow APIs: chứa các cú pháp hoặc phương thức cơ bản để xây dựng nên các function, hỗ trợ cho nhiều ngôn ngữ khác nhau, từ đó làm tăng tính đa dạng và lựa chọn khi xây dựng các ứng dụng.

-  Mid-Level Tensorflow APIs: các Class được viết dựa trên tầng thấp hơn Low-Level, các Class phổ biến: Dense, Conv1D , Conv2D…

-  High-Level Tensorflow APIs: chứa các API bậc cao giúp chúng ta có thể dễ dàng triển khai các mô hình khác nhau. Vì nó rất đơn giản, do đó chúng ta có thể xây dựng một mô hình với rất ít dòng code và nó cũng được xây dựng dựa trên các tầng thấp hơn.

-  Càng xuống thấp thì độ phức tạp càng tăng lên, các API cấp thấp phù hợp với các sinh viên, nhà nghiên cứu, các kỹ sư… Ngược lại, API cấp cao thì phù hợp với mọi đối tượng, những người mới tiếp cận với học máy hoặc những người không có ý định chuyên sâu mà chỉ muốn xây dựng một ứng dụng nhanh chóng thì đều có thể sử dụng được dễ dàng.

2. Cài đặt

Tensorflow được thử nghiệm và hỗ trợ trên nhiều hệ điều hành:

- Ubuntu 16.04 trở lên
- Windows 7 trở lên
- MacOS 10.12.6

Phiên bản hiện tại: version 1.x và version 2.x

Package: pip, conda.

Nền tảng tính toán: CPU, GPU

Lệnh cài đặt Tensorflow:

  • - Sử dụng pip: pip install tensorflow
  • - Sử dụng conda: conda install tensorflow

3. Các khái niệm cơ bản

3.1. Tensor

Tensor là đối tượng hình học miêu tả quan hệ tuyến tính giữa các đại lượng trong không gian vector. Một tensor chỉ đơn giản là một số, một mảng hoặc danh sách đa chiều, các số, mảng hay danh sách này trong tensorflow đều gọi là các tensor, một tensor được sẽ có ba tham số là: rank, shape, và type.

Rank là bậc hay số chiều của một tensor. Đối với từng bậc rank cụ thể, tensor có những tên gọi riêng như sau:

- Scalar: tensor có rank bằng 0, đại diện cho một số thực.
- Vector: tensor rank 1 (ứng với một mảng hay list trong python).
- Matrix: tensor rank 2 (mảng hai chiều trong python)
- N-Tensor: khi rank của tensor lớn hơn 2, chúng được gọi chung là N-Tensor.

Shape biểu thị cho độ dài của từng chiều (Shape được viết dưới dạng Array)

Ví dụ:

- Tensor = 8 có rank=0 và shape=[]
- Tensor=[2, 5, 8] có rank=1 và shape=[3]
- Tensor=[[2, 5, 8] , [3, 5, 9]] có rank=2 và shape=[2, 3]
- Tensor=[[[2, 5, 8] ], [[3, 5, 9]]] có rank=3 và shape=[2, 1, 3]

DType: là kiểu dữ liệu của các phần tử trong Tensor.

3.2. Các loại Tensor chính

Constant Tensor: là một Tensor có giá trị là một hằng số, được tạo ra bằng cách sử dụng tf.constant().

Ảnh 2.
 

Mặc dù đã khởi tạo giá trị cho a là 5, nhưng giá trị hiển thị của a vẫn là 0. Vì chúng ta mới chỉ tạo ra đồ thị gồm một operation là a nhưng vẫn chưa thực thi đồ thị đó. Nên a vẫn đang giữ giá trị mặc định là 0.

Khi thực thi đồ thị chúng ta cần tạo ra một session để run operation a nhằm kích hoạt luồng xử lý, khi đó giá trị a = 5.

Ảnh 3.
 

Variable: Khác với Constant Tensor ở trên, Variables là các Tensor mà giá trị của nó có thể bị thay đổi, chúng ta có thể tạo Variables bằng cách sử sử dụng tf.Variable() để tạo thành công chúng ta cần phải cung cấp các giá trị khởi tạo.

Khai báo: tf.Variable(value, dtype=None, shape = None, name=None)

Trong đó value là các giá trị của biến và name là tên của operation thể hiện trên đồ thị.

Ảnh 4.
 

Placeholder: Trong trường hợp chúng ta không biết giá trị cũng như shape của dữ liệu ban đầu thì chúng ta có thể sử dụng Placeholder để tạo một Tensor và truyền dữ liệu vào sau, Placeholder được tạo bằng cách sử dụng tf.placeholder().

Ảnh 5.
 

Khai báo: tf.placeholder(dtype, shape=None, name=None)

4. Luồng hoạt động

4.1. Các thành phần cơ bản

Một ứng dụng học máy là kết quả của sự tính toán lặp đi lặp lại các biểu thức toán học phức tạp. Trong TensorFlow, việc tính toán như vậy sẽ được mô tả bằng Data Flow Graphs (Đồ thị luồng dữ liệu). Các phép toán, thao tác được đặt vào trong một đồ thị. Chức năng chính của graph là mô tả sự thay đổi của dữ liệu.

Vì Tensorflow mô tả lại dòng chảy của dữ liệu thông qua graph nên mỗi một điểm giao cắt trong graph thì được gọi là Node (nút). Các Node là điểm đại diện cho việc thay đổi của dữ liệu. Trong đó mỗi Node trong biểu đồ đại diện cho thể hiện của một phép toán (cộng, trừ, nhân, chia, …) và mỗi Edges là một tập dữ liệu đa chiều mà trên đó các hoạt động sẽ được thực hiện. Giờ ta sẽ đi chi tiết hơn về Node và Edges.

Bên cạnh đó, ta cũng quan tâm một số khái niệm trong Data Flow Graph:

- Operation: nếu như các Node đóng vai trò định nghĩa ra các phép toán thì Operation là việc thực hiện các phép toán đấy.

- Kernel: xác định việc thực hiện các Operation trên một thiết bị cụ thể. Ví dụ, một Operation thêm ma trận có thể triển khai ở CPU hoặc GPU.

- Session: để tính toán, một Graph phải được đưa vào trong một Session. Về mặt kỹ thuật, Session đặt hoạt động của Graph trên phần cứng như CPU hoặc GPU và cung cấp các phương thức để thực hiện Graph đó.

4.2. Luồng hoạt động

Mọi ứng dụng của Tensorflow được xây dựng đều dựa trên ý tưởng là Data Flow Graphs, ta không thể chạy một chương trình trong Tensorflow nếu không tạo một Graph. Khi xây dựng một model Tensorflow chúng ta thường thông qua 2 bước:

- Xây dựng đồ thị tính toán (sử dụng tf.Graph): một đồ thị sẽ bao gồm các node và các cạnh. Node của đồ thị sẽ thể hiện chức năng và cách cạnh thể hiện dữ liệu tính toán.
- Chạy Computational Graph (sử dụng tf.Session): việc xây dựng đồ thị tính toán tương tự như cấu trúc nên khung sườn của một cỗ máy. Để hoạt động được, cỗ máy cần có dòng điện chạy qua. Đặt vấn đề trong graph, dòng điện đóng vai trò là luồng dữ liệu dạng Tensor và session đóng vai trò điều tiết dòng chảy Tensor sao cho hợp lý theo cấu trúc đã thiết kế.

Do đó, sau khi xây dựng được đồ thị tính toán, để có thể thực thi các phép tính trên đồ thị, ta cần tạo ra một session.

Ảnh 6.
 

5. Xây dựng một mô hình học máy dùng Tensorflow

5.1. Import các thư viện cần thiết

Ta chỉ cần 3 gói thư viện là:

  • - tensorflow: thư viện chứa các hàm, api để xây dựng mô hình.
  • - numpy: thư viện để tính toán.
  • - matplotlib: thư viện để visualize data.
Ảnh 7.
 

5.2. Khởi tạo các giá trị hyperparameter

  • - learning_rate: để phục vụ cho khi khởi tạo đối tượng optimizer
  • - epochs: số lần toàn bộ dữ liệu được học.
Ảnh 8.
 

5.3. Khởi tạo các điểm dữ liệu

Ta biết với mô hình Linear Regression thì đầu vào sẽ là một vector x và đầu ra sẽ là các giá trị liên tục y. Hàm init_point() để khởi tạo các cặp x và y:

Ảnh 9.
 

Ta sẽ khởi tạo với 10 điểm:

Ảnh 10.
 

5.4. Khởi tạo các biến weight model và trình giữ chỗ để truyền data vào graph

Ảnh 11.
 

5.5. Định nghĩa hàm loss và thuật toán tối ưu

Ảnh 12.
 

5.6. Training

Ảnh 13.
 

Đầu tiên, ta cần chạy Session cùng với phương thức tf.global_variables_initializer(), để phân bổ bộ nhớ cho Biến và đặt các giá trị ban đầu

Lệnh sess.run(optimizer, feed_dict = {X: x, Y: y}) có nghĩa là thực thi đối tượng optimizer với dữ liệu đầu vào được truyền qua feed_dict. Khi chạy lệnh này các weight w bà bias b sẽ được cập nhật giá trị thông qua đối tượng optimizer.

Ta có thể để ý ở vòng for bên trong sẽ thực hiện truyền từng điểm dữ liệu vào trình tối ưu, như vậy đây chính là kỹ thuật Stochastic gradient descent. Vòng for bên ngoài sẽ thực hiện train lại tập dữ liệu bao nhiêu lần dựa vào số epochs.

Lệnh if chỉ đơn giản cứ 1000 epoch thì sẽ in ra các giá trị một lần, điều này để cho ta tiện theo dõi hiệu quả của thuật toán để có thể có những điều chỉnh phù hợp.

5.7. Visualize kết quả

Ảnh 14.
 
Ảnh 15.
 

5.8. Hiển thị trên graph trên tensorboard

Một trong những lợi thế lớn nhất của TensorFlow là trực quan hóa graph operation (TensorBoard). Để sử dụng TensorBoard, trong phần code xử lý mục 5.7 ta dùng lệnh tf.summary.FileWriter(‘./graphs/liner_regression’, sess.graph) để lưu lại graph của thuật toán để phục cho việc hiển thị trên Tensorboard.

Sau đó, load data graph theo các lệnh sau:

Ảnh 16.
 
Ảnh 17.
 

6. Lợi ích mang lại từ Tensorflow

Việc biểu diễn tính toán bằng đồ thị đi kèm với lợi thế là có thể chạy các đường chuyền xuôi và ngược. Đây là yếu tố cần thiết trong việc đào tạo một mô hình học máy.

Việc biểu diễn sự tính toán bằng biểu đồ mang lại những ưu điểm như:

- Tính song song: thông qua các nút và các cạnh trong Graph, TensorFlow có thể xác định các hoạt động tính toán, từ đó thực hiện được tính toán song song.

- Tối ưu hóa tính toán: là một đồ thị, một cấu trúc dữ liệu nổi tiếng, có thể phân tích với mục đích tối ưu hóa tốc độ thực hiện. Ví dụ, có thể phát hiện các nút không sử dụng trong biểu đồ và loại bỏ chúng, từ đó tối ưu hóa nó cho kích thước hoặc phát hiện các hoạt động dư thừa và thay thế bằng các lựa chọn tốt hơn, qua đó tăng tính kiểm soát cho mô hình.


- Tính di động: đồ thị tính toán không phụ thuộc vào ngôn ngữ và nền tảng. TensorFlow sử dụng Protocol Buffers (Protobuf), đây là một cơ chế ngôn ngữ đơn giản, không phụ thuộc nền tảng và có thể mở rộng để chuẩn hóa dữ liệu có cấu trúc để lưu trữ biểu đồ. Trong thực tế, điều này có nghĩa là một mô hình được viết bằng Python sử dụng TensorFlow có thể được lưu lại và sau đó được sử dụng bên trong một chương trình khác được viết bằng ngôn ngữ khác.


- Thực thi phân tán: mỗi nút của đồ thị có thể được đặt trên một thiết bị độc lập và trên một máy khác. TensorFlow sẽ đảm nhiệm việc giao tiếp giữa các nút và đảm bảo rằng việc thực hiện tính toán trong đồ thị là chính xác. Hơn nữa, bản thân TensorFlow có thể phân vùng biểu đồ trên nhiều thiết bị, tuy vậy việc tính toán vẫn là hiệu quả nhất khi chạy chỉ trên một thiết bị.