Lan truyền Xuôi trong Mạng Nơ-ron: Hướng dẫn Hoàn chỉnh
Tìm hiểu cách lan truyền xuôi hoạt động trong mạng nơ-ron, từ nền tảng toán học đến triển khai thực tiễn trong Python. Nắm vững khái niệm học sâu thiết yếu này với ví dụ mã và trực quan hóa.
Mạng nơ-ron đã làm thay đổi cục diện trí tuệ nhân tạo, cung cấp sức mạnh cho mọi thứ từ nhận diện hình ảnh đến xử lý ngôn ngữ tự nhiên. Ở trung tâm của những mô hình mạnh mẽ này là một quy trình nền tảng gọi là lan truyền thuận (forward propagation). Hướng dẫn này sẽ khám phá khái niệm cốt lõi đó, đưa bạn từ những nguyên lý cơ bản đến triển khai thực tế.
Lan truyền thuận là quá trình trong đó mạng nơ-ron biến đổi dữ liệu đầu vào thành dự đoán hoặc đầu ra. Hãy coi đây là giai đoạn "suy nghĩ" của mạng nơ-ron — khi được đưa một đầu vào (như hình ảnh hoặc văn bản), lan truyền thuận là cách mạng xử lý thông tin qua các tầng để tạo ra kết quả.
Về mặt kỹ thuật, đây là phép tính tuần tự di chuyển dữ liệu từ tầng đầu vào, qua các tầng ẩn, và cuối cùng đến tầng đầu ra. Trong hành trình này, dữ liệu được biến đổi bởi các kết nối có trọng số và các hàm kích hoạt, cho phép mạng nắm bắt các mẫu phức tạp.
Vì sao việc hiểu lan truyền thuận lại quan trọng
Hiểu lan truyền thuận rất quan trọng vì nhiều lý do:
Nền tảng cho việc học: Bạn không thể nắm được cách mạng nơ-ron học nếu chưa hiểu trước cách chúng tạo dự đoán. Lan truyền thuận là tiền đề để hiểu lan truyền ngược (backpropagation), thuật toán cho phép việc học.
Gỡ lỗi và tối ưu: Khi mạng nơ-ron hoạt động kém, hiểu dòng chảy dữ liệu trong mạng giúp bạn xác định và khắc phục vấn đề.
Thiết kế mô hình: Thiết kế kiến trúc hiệu quả đòi hỏi phải hiểu cách thông tin lan truyền qua các cấu hình tầng khác nhau.
Hiệu quả triển khai: Tối ưu lan truyền thuận là điều thiết yếu khi triển khai mô hình trong môi trường hạn chế tài nguyên.
Bạn sẽ học được gì trong hướng dẫn này
Kết thúc hướng dẫn toàn diện này, bạn sẽ:
Hiểu toán học đằng sau các phép tính của mạng nơ-ron
Có thể tự triển khai lan truyền thuận từ đầu bằng NumPy
Biết cách dùng các framework hiện đại như TensorFlow và PyTorch để lan truyền thuận hiệu quả
Hình dung cách thông tin biến đổi khi di chuyển qua mạng
Kết nối lan truyền thuận với quá trình học tổng thể trong mạng nơ-ron
Triển khai một ví dụ hoàn chỉnh trên dữ liệu thực
Yêu cầu tiên quyết
Để tận dụng tối đa hướng dẫn này, bạn nên có:
Hiểu biết cơ bản về lập trình Python
Quen thuộc với các khái niệm toán học nền tảng (vector, ma trận và giải tích cơ bản)
Một chút trải nghiệm với các khái niệm học máy
Nếu bạn cần củng cố nền tảng, hãy cân nhắc các tài nguyên sau:
Ngay cả khi chưa có nền tảng sâu, chúng tôi đã thiết kế hướng dẫn này theo kiểu xây dựng dần khái niệm, giúp mạng nơ-ron trở nên dễ tiếp cận với những người học quyết tâm. Hãy bắt đầu thôi!
Nền tảng của Lan Truyền Thuận
Để hiểu lan truyền thuận, ta cần bắt đầu từ các khối xây dựng cơ bản của nó. Hãy khởi đầu với đơn vị tính toán nhỏ nhất trong mạng nơ-ron và dần mở rộng đến các cấu trúc phức tạp hơn.
Nơ-ron đơn: điểm khởi đầu của mọi thứ
Hành trình của mạng nơ-ron bắt đầu với một sự song song thú vị với sinh học. Giống như bộ não con người gồm hàng tỷ nơ-ron liên kết, mạng nơ-ron nhân tạo được xây dựng từ các mô hình toán học lấy cảm hứng từ những tế bào sinh học này.
Một nơ-ron sinh học nhận tín hiệu từ các nơ-ron khác thông qua đuôi gai (dendrite), xử lý các tín hiệu này trong thân tế bào, rồi truyền kết quả qua sợi trục (axon) đến các nơ-ron khác. Trong mô hình tính toán, chúng ta phản chiếu quy trình này với:
Đầu vào: Các tín hiệu đi vào nơ-ron (như đuôi gai nhận tín hiệu).
Trọng số: Mức độ quan trọng gán cho mỗi đầu vào (tương tự độ mạnh của kết nối synap).
Độ chệch (bias): Tham số bổ sung giúp nơ-ron kích hoạt hoặc giữ im lặng bất kể đầu vào.
Hàm kích hoạt: Quyết định nơ-ron "bắn" tín hiệu hay không và mạnh đến mức nào dựa trên đầu vào (giống như thân tế bào quyết định có tạo điện thế hoạt động hay không).
Đầu ra: Tín hiệu gửi đến tầng tiếp theo (như sợi trục truyền tới các nơ-ron khác).
Hãy trực quan hóa một nơ-ron đơn để cụ thể hóa điều này:
Đơn vị tính toán đơn giản này là nền tảng của cả những mạng nơ-ron phức tạp nhất. Nhưng chính xác một nơ-ron biến đổi đầu vào thành đầu ra như thế nào? Đây là lúc toán học xuất hiện.
Nền tảng toán học của một nơ-ron đơn
Hoạt động của một nơ-ron có thể được mô tả bằng một phương trình đơn giản:
Phân tích chi tiết:
x là vector đầu vào [x_1, x_2, x_3, …]
w là vector trọng số [w_1, w_2, …, w_n]
b là độ chệch (một giá trị vô hướng)
z là tổng có trọng số cộng độ chệch (thường gọi là "tiền kích hoạt" hoặc "logit")
f là hàm kích hoạt
a là đầu ra cuối cùng của nơ-ron
Hãy làm một ví dụ cụ thể với số thực. Giả sử ta có một nơ-ron với ba đầu vào:
Inputs: x =[2,5,-1]
Weights: w =[0.5,-1,2]
Bias: b =0.5Được hỗ trợ bởi
Đầu tiên, ta tính tổng có trọng số cộng độ chệch:
Tiếp theo, ta áp dụng một hàm kích hoạt. Hãy dùng hàm ReLU (Rectified Linear Unit) phổ biến, được định nghĩa như sau:
Với giá trị tiền kích hoạt âm, nơ-ron ReLU của chúng ta cho ra 0, nghĩa là nó không "bắn" tín hiệu cho đầu vào này.
Hàm kích hoạt rất quan trọng vì nó đưa phi tuyến tính vào mạng. Nếu không có nó, mạng nơ-ron sẽ chỉ học được các quan hệ tuyến tính, bất kể có bao nhiêu tầng. Các hàm kích hoạt thường dùng gồm:
Sigmoid: — Cho đầu ra trong khoảng 0 đến 1, hữu ích cho phân loại nhị phân
ReLU: — Đơn giản, hiệu quả, và giúp giảm vấn đề mất dần gradient
Tanh: — Cho đầu ra trong khoảng -1 đến 1, thường dùng ở các tầng ẩn
Mỗi hàm kích hoạt có điểm mạnh và trường hợp sử dụng riêng, chúng ta sẽ khám phá thêm khi triển khai mạng nơ-ron.
Từ nơ-ron đến tầng
Các nơ-ron riêng lẻ đã mạnh mẽ, nhưng sức mạnh thực sự của mạng nơ-ron xuất hiện khi nơ-ron được tổ chức thành các tầng. Một tầng đơn giản là một tập hợp nơ-ron xử lý đầu vào song song. Thường có ba loại tầng trong một mạng nơ-ron:
Tầng đầu vào: Nhận dữ liệu thô và chuyển sang tầng tiếp theo
Các tầng ẩn: Xử lý thông tin từ các tầng trước
Tầng đầu ra: Tạo ra dự đoán hoặc phân loại cuối cùng
Khi có nhiều nơ-ron trong một tầng, mỗi nơ-ron nhận cùng đầu vào nhưng với trọng số và độ chệch khác nhau, ta có thể biểu diễn hiệu quả bằng các phép toán ma trận. Hãy xem điều này hoạt động ra sao.
Hình dung ta có một tầng với 3 giá trị đầu vào và 4 nơ-ron. Mỗi nơ-ron có bộ trọng số và độ chệch riêng:
Inputs: x =[x₁, x₂, x₃]
Weights for neuron 1: w₁ =[w₁₁, w₁₂, w₁₃]
Weights for neuron 2: w₂ =[w₂₁, w₂₂, w₂₃]
Weights for neuron 3: w₃ =[w₃₁, w₃₂, w₃₃]
Weights for neuron 4: w₄ =[w₄₁, w₄₂, w₄₃]
Biases: b =[b₁, b₂, b₃, b₄]Được hỗ trợ bởi
Ta có thể sắp xếp các trọng số này thành một ma trận W:
Giờ ta có thể tính toàn bộ các giá trị tiền kích hoạt của nơ-ron cùng lúc bằng một phép nhân ma trận:
Trong đó:
X là vector đầu vào [x₁, x₂, x₃]
W^T là chuyển vị của ma trận trọng số
B là vector độ chệch [b₁, b₂, b₃, b₄]
Z là vector tiền kích hoạt thu được [z₁, z₂, z₃, z₄]
Sau đó ta áp dụng hàm kích hoạt theo từng phần tử để nhận đầu ra cuối cùng:
Biểu diễn ma trận không chỉ là sự tao nhã về mặt toán học; đó còn là hiệu quả tính toán. Phần cứng hiện đại (đặc biệt là GPU) được tối ưu cho phép toán ma trận, giúp cách tiếp cận này nhanh hơn nhiều so với việc tính đầu ra của từng nơ-ron riêng lẻ.
Khả năng xếp chồng các tầng — với đầu ra của tầng này trở thành đầu vào của tầng kế tiếp — chính là điều mang lại cho mạng nơ-ron năng lực đáng kinh ngạc để học các mẫu phức tạp. Khi kết nối các khối xây dựng này, chúng ta đã sẵn sàng khám phá cách lan truyền thuận hoạt động xuyên suốt một mạng nơ-ron hoàn chỉnh.
Lan Truyền Thuận Qua Một Mạng Hoàn Chỉnh
Giờ khi đã hiểu nơ-ron riêng lẻ và các tầng, hãy lùi lại để xem lan truyền thuận hoạt động như thế nào trên toàn bộ mạng nơ-ron. Đây là nơi sức mạnh thực sự của học sâu bộc lộ.
Mạng nhiều tầng: bức tranh toàn diện
Một mạng nơ-ron đầy đủ gồm tầng đầu vào, một hoặc nhiều tầng ẩn, và tầng đầu ra. Thuật ngữ "sâu" trong học sâu (deep learning) ám chỉ các mạng có nhiều tầng ẩn. Mỗi tầng biến đổi dữ liệu theo cách ngày càng trừu tượng, cho phép mạng học các biểu diễn phức tạp.
Hãy xét một mạng nơ-ron đơn giản với:
Một tầng đầu vào với 4 đặc trưng
Hai tầng ẩn với lần lượt 3 và 2 nơ-ron
Một tầng đầu ra với 3 nơ-ron (có thể cho phân loại đa lớp)
Trực quan, mạng này trông như sau:
Khi dữ liệu chảy qua mạng này, ta thực hiện một loạt phép tính ở mỗi tầng. Nếu ký hiệu:
Vector đầu vào là X = [x_1, x_2, x_3, x_4]
Các ma trận trọng số là (cho tầng ẩn) và (cho tầng đầu ra)
Các vector độ chệch là và
Các hàm kích hoạt là f1 (cho tầng ẩn) và f2 (cho tầng đầu ra)
Ta có thể biểu diễn lan truyền thuận qua toàn bộ mạng này như sau:
1. Tính tiền kích hoạt của tầng ẩn:
2. Áp dụng hàm kích hoạt để lấy đầu ra tầng ẩn:
3. Tính tiền kích hoạt của tầng đầu ra:
4. Áp dụng hàm kích hoạt để lấy đầu ra cuối cùng:
Đầu ra cuối cùng A^[2] biểu thị dự đoán của mạng. Với bài toán phân loại, đó có thể là xác suất cho mỗi lớp; với hồi quy, có thể là giá trị dự đoán.
Các tầng khác nhau thường dùng các hàm kích hoạt khác nhau. Chẳng hạn, các tầng ẩn thường dùng ReLU, trong khi tầng đầu ra có thể dùng:
Sigmoid cho phân loại nhị phân
Softmax cho phân loại đa lớp
Tuyến tính (không kích hoạt) cho hồi quy
Điều tuyệt vời của cấu trúc nhiều tầng là mỗi tầng có thể học biểu diễn các khía cạnh khác nhau của dữ liệu. Các tầng đầu thường phát hiện đặc trưng đơn giản, trong khi các tầng sâu hơn kết hợp chúng thành các mẫu phức tạp hơn. Chính học tập phân cấp này làm cho mạng nơ-ron mạnh mẽ với các tác vụ phức tạp như nhận diện hình ảnh và giọng nói.
Thuật toán lan truyền thuận
Hãy chính thức hóa quy trình lan truyền thuận thành một thuật toán. Với một mạng nơ-ron có L tầng, lan truyền thuận theo các bước sau:
# Pseudocode for forward propagationdefforward_propagation(X, parameters):"""
X: Input data (batch_size, n_features)
parameters: Dictionary containing weights and biases for each layer
Returns: The final output and all intermediate activations
"""# Store all activations for later use (e.g., in backpropagation)
activations ={'A0': X}# A0 is the input# Loop through L-1 layers (excluding the output layer)for l inrange(1, L):# Get previous activation
A_prev = activations[f'A{l-1}']# Get weights and biases for current layer
W = parameters[f'W{l}']
b = parameters[f'b{l}']# Compute pre-activation
Z = np.dot(A_prev, W.T)+ b
# Apply activation function (e.g., ReLU for hidden layers)
A = relu(Z)# Store values for later use
activations[f'Z{l}']= Z
activations[f'A{l}']= A
# Compute output layer (layer L)
A_prev = activations[f'A{L-1}']
W = parameters[f'W{L}']
b = parameters[f'b{L}']# Compute pre-activation for output layer
Z = np.dot(A_prev, W.T)+ b
# Apply output activation function (depends on the task)if task =='binary_classification':
A = sigmoid(Z)elif task =='multiclass_classification':
A = softmax(Z)elif task =='regression':
A = Z # Linear activation# Store output layer values
activations[f'Z{L}']= Z
activations[f'A{L}']= A
return A, activationsĐược hỗ trợ bởi
Thuật toán này làm nổi bật một vài khía cạnh quan trọng của lan truyền thuận:
Xử lý tuần tự: Thông tin chảy nghiêm ngặt theo hướng tiến, tầng này đến tầng kế tiếp.
Lưu trữ kích hoạt: Ta lưu không chỉ đầu ra cuối cùng mà cả các kích hoạt trung gian, rất quan trọng để huấn luyện mạng bằng lan truyền ngược.
Đầu ra theo tác vụ: Hàm kích hoạt của tầng đầu ra được chọn dựa trên bài toán cụ thể (phân loại so với hồi quy).
Thuật toán lan truyền thuận rất đơn giản, nhưng cho phép mạng nơ-ron xấp xỉ các hàm cực kỳ phức tạp. Khi kết hợp với quá trình huấn luyện đúng đắn qua lan truyền ngược, quy trình đơn giản này giúp mạng học từ dữ liệu và đưa ra dự đoán ngày càng chính xác.
Hãy theo dõi một ví dụ cụ thể để củng cố hiểu biết. Xét đầu vào X = [0.5, -0.2, 0.1] đi qua mạng ví dụ của chúng ta với:
Trọng số tầng ẩn W^[1] (ma trận 4×3) và độ chệch b^[1] (vector 4 giá trị)
Trọng số tầng đầu ra W^[2] (ma trận 2×4) và độ chệch b^[2] (vector 2 giá trị)
Để đơn giản, giả sử tất cả trọng số đều là 0.1 và tất cả độ chệch bằng 0:
X =[0.5,-0.2,0.1]
W[1]=[[0.1,0.1,0.1],[0.1,0.1,0.1],[0.1,0.1,0.1],[0.1,0.1,0.1]]
b[1]=[0,0,0,0]
W[2]=[[0.1,0.1,0.1,0.1],[0.1,0.1,0.1,0.1]]
b[2]=[0,0]Được hỗ trợ bởi
Theo thuật toán của chúng ta:
Bước đầu tiên:
Bước thứ hai với ReLU:
Bước thứ ba:
Bước thứ tư dùng sigmoid:
Điều này cho ra dự đoán cuối cùng của chúng ta. Trong bối cảnh phân loại nhị phân, các giá trị gần 0.5 cho thấy sự không chắc chắn giữa hai lớp.
Ở phần tiếp theo, chúng ta sẽ triển khai lan truyền thuận bằng Python để xem các phép tính này hoạt động thực tế.
Triển Khai Lan Truyền Thuận bằng Python
Giờ khi đã hiểu lý thuyết đằng sau lan truyền thuận, hãy áp dụng thực tế bằng cách triển khai nó trong Python. Chúng ta sẽ bắt đầu với triển khai "từ đầu" chỉ dùng NumPy, sau đó xem các framework học sâu hiện đại đơn giản hóa quy trình này như thế nào.
Xây dựng từ đầu với NumPy
NumPy cung cấp các phép toán mảng hiệu quả cho phép chúng ta triển khai các phép tính ma trận đã thảo luận. Hãy xây dựng một lớp mạng nơ-ron đơn giản thực hiện lan truyền thuận qua nhiều tầng.
Trước tiên, ta cần import các thư viện cần thiết:
import numpy as np
import matplotlib.pyplot as plt
# For reproducibility
np.random.seed(42)Được hỗ trợ bởi
Giờ hãy định nghĩa các hàm kích hoạt sẽ dùng trong mạng của chúng ta:
defrelu(Z):"""ReLU activation function: max(0, Z)"""return np.maximum(0, Z)defsigmoid(Z):"""Sigmoid activation function: 1/(1 + e^(-Z))"""return1/(1+ np.exp(-Z))defsoftmax(Z):"""Softmax activation function for multi-class classification"""# Subtract max for numerical stability (prevents overflow)
exp_Z = np.exp(Z - np.max(Z, axis=1, keepdims=True))return exp_Z / np.sum(exp_Z, axis=1, keepdims=True)Được hỗ trợ bởi
Tiếp theo, hãy tạo một lớp cho mạng nơ-ron của chúng ta:
classNeuralNetwork:def__init__(self, layer_dims, activations):"""
Initialize a neural network with specified layer dimensions and activations
Parameters:
- layer_dims: List of integers representing the number of neurons in each layer
(including input and output layers)
- activations: List of activation functions for each layer (excluding input layer)
"""
self.L =len(layer_dims)-1# Number of layers (excluding input layer)
self.layer_dims = layer_dims
self.activations = activations
self.parameters ={}# Initialize parameters (weights and biases)
self.initialize_parameters()definitialize_parameters(self):"""Initialize weights and biases with small random values"""for l inrange(1, self.L +1):# He initialization for weights - helps with training deep networks
self.parameters[f'W{l}']= np.random.randn(self.layer_dims[l], self.layer_dims[l-1])* np.sqrt(2/ self.layer_dims[l-1])
self.parameters[f'b{l}']= np.zeros((self.layer_dims[l],1))defforward_propagation(self, X):"""
Perform forward propagation through the network
Parameters:
- X: Input data (n_features, batch_size)
Returns:
- AL: Output of the network
- caches: Dictionary containing all activations and pre-activations
"""
caches ={}
A = X # Input layer activation
caches['A0']= X
# Process through L-1 layers (excluding output layer)for l inrange(1, self.L):
A_prev = A
# Get weights and biases for current layer
W = self.parameters[f'W{l}']
b = self.parameters[f'b{l}']# Forward propagation for current layer
Z = np.dot(W, A_prev)+ b
# Apply activation function
activation_function = self.activations[l-1]if activation_function =="relu":
A = relu(Z)elif activation_function =="sigmoid":
A = sigmoid(Z)# Store values for backpropagation
caches[f'Z{l}']= Z
caches[f'A{l}']= A
# Output layer
W = self.parameters[f'W{self.L}']
b = self.parameters[f'b{self.L}']
Z = np.dot(W, A)+ b
# Apply output activation function
activation_function = self.activations[self.L-1]if activation_function =="sigmoid":
AL = sigmoid(Z)elif activation_function =="softmax":
AL = softmax(Z)elif activation_function =="linear":
AL = Z # No activation for regression# Store output layer values
caches[f'Z{self.L}']= Z
caches[f'A{self.L}']= AL
return AL, cachesĐược hỗ trợ bởi
Lớp NeuralNetwork mà chúng ta đã triển khai ở trên cung cấp một khung đầy đủ để tạo và sử dụng mạng nơ-ron với kiến trúc tùy biến. Hãy phân tách các thành phần chính:
Khởi tạo: Mạng được cấu hình với kích thước các tầng và hàm kích hoạt chỉ định. Nó tự động khởi tạo trọng số và độ chệch bằng phương pháp He để cải thiện hiệu suất huấn luyện.
Lan truyền thuận: Phương thức forward_propagation vừa triển khai là trái tim của khả năng dự đoán của mạng. Nó:
Nhận dữ liệu đầu vào và truyền qua từng tầng
Áp dụng hàm kích hoạt phù hợp ở mỗi tầng
Lưu trữ các giá trị trung gian trong bộ nhớ đệm để dùng cho lan truyền ngược
Trả về cả đầu ra cuối cùng và tất cả giá trị đã lưu
3. Hàm kích hoạt: Mạng hỗ trợ nhiều hàm kích hoạt, bao gồm ReLU, sigmoid, và softmax, cho phép xử lý các loại bài toán khác nhau (hồi quy hoặc phân loại).
4. Kiến trúc linh hoạt: Triển khai này cho phép các mạng có độ sâu và độ rộng tùy ý, phù hợp với nhiều nhiệm vụ học máy.
Triển khai này tuân theo mẫu thiết kế mạng nơ-ron tiêu chuẩn, trong đó dữ liệu chảy tiến qua mạng, mỗi tầng biến đổi dữ liệu trước khi chuyển cho tầng tiếp theo.
Giờ hãy kiểm thử triển khai bằng một mạng ví dụ nhỏ:
# Create a sample network# Input layer: 3 features# Hidden layer 1: 4 neurons with ReLU activation# Output layer: 2 neurons with sigmoid activation (binary classification)
layer_dims =[3,4,2]
activations =["relu","sigmoid"]
nn = NeuralNetwork(layer_dims, activations)# Create sample input data - 3 features for 5 examples
X = np.random.randn(3,5)# Perform forward propagation
output, caches = nn.forward_propagation(X)print(f"Input shape: {X.shape}")print(f"Output shape: {output.shape}")print(f"Output values:\n{output}")Được hỗ trợ bởi
Kết quả:
Input shape:(3,5)
Output shape:(2,5)
Output values:[[0.003867840.543430140.396618930.50.51056934][0.118770490.325410930.448406990.50.45586633]]Được hỗ trợ bởi
Hãy trực quan hóa cách dữ liệu biến đổi khi chảy qua mạng:
defvisualize_activations(caches, example_idx=0):"""Visualize the activations for a single example through the network"""
plt.figure(figsize=(15,6))# Plot input
plt.subplot(1,3,1)
plt.bar(range(caches['A0'].shape[0]), caches['A0'][:, example_idx])
plt.title('Input Layer')
plt.xlabel('Feature')
plt.ylabel('Value')# Plot hidden layer activation
plt.subplot(1,3,2)
plt.bar(range(caches['A1'].shape[0]), caches['A1'][:, example_idx])
plt.title('Hidden Layer (ReLU Activation)')
plt.xlabel('Neuron')
plt.ylabel('Activation')# Plot output layer
plt.subplot(1,3,3)
plt.bar(range(caches['A2'].shape[0]), caches['A2'][:, example_idx])
plt.title('Output Layer (Sigmoid Activation)')
plt.xlabel('Neuron')
plt.ylabel('Activation')
plt.tight_layout()
plt.show()# Visualize the first example
visualize_activations(caches)Được hỗ trợ bởi
Hình trực quan này giúp chúng ta hiểu cách dữ liệu đầu vào được biến đổi khi lan truyền qua mạng. Ta có thể thấy rằng:
Các đặc trưng đầu vào có thể mang giá trị dương hoặc âm
Sau kích hoạt ReLU ở tầng ẩn, mọi kích hoạt đều không âm
Kích hoạt sigmoid ở tầng đầu ra giới hạn giá trị trong khoảng 0 đến 1
Triển khai "từ đầu" cho thấy các nguyên lý cốt lõi của lan truyền thuận, nhưng các framework học sâu hiện đại cung cấp công cụ hiệu quả và linh hoạt hơn để xây dựng mạng nơ-ron.
Sử dụng các framework học sâu
Giờ hãy triển khai cùng một mạng nơ-ron bằng các framework học sâu phổ biến: TensorFlow và PyTorch. Những framework này tối ưu hiệu năng và cung cấp các trừu tượng cấp cao, giúp xây dựng mô hình phức tạp dễ dàng hơn.
Trước tiên, hãy xem triển khai bằng TensorFlow/Keras:
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Input
# For reproducibility
tf.random.set_seed(42)# Create the same network architecture
tf_model = Sequential([
Input(shape=(3,)),# Input layer with 3 features
Dense(4, activation='relu'),# Hidden layer with 4 neurons
Dense(2, activation='sigmoid')# Output layer with 2 neurons])
tf_model.summary()Được hỗ trợ bởi
# Sample input data with the shape expected by Keras
X_tf = np.random.randn(5,3)# 5 examples, 3 features# Forward propagation in TensorFlow
tf_output = tf_model.predict(X_tf)print(f"TensorFlow output shape: {tf_output.shape}")print(f"TensorFlow output values:\n{tf_output}")Được hỗ trợ bởi
TensorFlow output shape:(5,2)
TensorFlow output values:[[0.68553080.7139542][0.49523550.5231934][0.501749040.5198488][0.443318250.6860926][0.66245890.5385444]]Được hỗ trợ bởi
Chúng ta vừa triển khai mạng nơ-ron bằng TensorFlow/Keras và thực hiện lan truyền thuận trên một số dữ liệu mẫu thành công. Bản tóm tắt mô hình cho thấy kiến trúc với một tầng đầu vào nhận 3 đặc trưng, một tầng ẩn có 4 nơ-ron dùng kích hoạt ReLU, và một tầng đầu ra có 2 nơ-ron dùng kích hoạt sigmoid. Tổng cộng, mô hình có 26 tham số có thể huấn luyện.
Kết quả lan truyền thuận cho thấy kích thước đầu ra (5, 2) tương ứng với 5 mẫu đầu vào, mỗi mẫu tạo ra 2 giá trị đầu ra. Các đầu ra này bị giới hạn trong khoảng 0 đến 1 nhờ hàm sigmoid ở tầng đầu ra.
Tiếp theo, hãy xem cách ta có thể triển khai cùng kiến trúc bằng PyTorch, một framework học sâu phổ biến khác, để so sánh cách tiếp cận.
import torch
import torch.nn as nn
# For reproducibility
torch.manual_seed(42)# Create a network in PyTorchclassPyTorchNN(nn.Module):def__init__(self):super(PyTorchNN, self).__init__()
self.hidden = nn.Linear(3,4)# 3 inputs, 4 hidden neurons
self.relu = nn.ReLU()
self.output = nn.Linear(4,2)# 4 inputs from hidden, 2 outputs
self.sigmoid = nn.Sigmoid()defforward(self, x):
x = self.relu(self.hidden(x))
x = self.sigmoid(self.output(x))return x
# Instantiate the model
torch_model = PyTorchNN()# Print model structureprint(torch_model)Được hỗ trợ bởi
# Compare dimensions and structure of outputsprint("\nComparison of implementations:")print(f"NumPy output shape: {output.shape}")print(f"TensorFlow output shape: {tf_output.shape}")print(f"PyTorch output shape: {torch_output.shape}")Được hỗ trợ bởi
Đầu ra:
Comparison of implementations:
NumPy output shape:(2,5)
TensorFlow output shape:(5,2)
PyTorch output shape: torch.Size([5,2])Được hỗ trợ bởi
Những khác biệt chính giữa các cách triển khai của chúng ta:
Cấu trúc và khả năng đọc:
NumPy: Triển khai tường minh từng bước giúp kiểm soát hoàn toàn và minh bạch
TensorFlow/Keras: Các trừu tượng cấp cao ẩn chi tiết triển khai nhưng khiến mã đơn giản hơn
PyTorch: Cách tiếp cận hướng đối tượng với phương thức forward tường minh
Hiệu năng:
TensorFlow và PyTorch tối ưu các phép toán để tăng tốc trên GPU
Cả hai framework đều triển khai tính toán gradient hiệu quả phục vụ huấn luyện
Các framework tối ưu sử dụng bộ nhớ cho mạng lớn
Tính mở rộng:
Các framework cung cấp sẵn thành phần dựng sẵn (layer, optimizer, v.v.)
Công cụ tích hợp để lưu/tải mô hình, trực quan hóa và triển khai
Cộng đồng rộng lớn và tài liệu phong phú
Với các mạng đơn giản, triển khai bằng NumPy của chúng ta hoạt động ổn, nhưng khi độ phức tạp tăng, các framework học sâu trở nên thiết yếu. Chúng xử lý tối ưu hóa tầng thấp và cung cấp công cụ cho toàn bộ quy trình học máy, từ phát triển đến triển khai. Để tìm hiểu thêm về các framework này, hãy xem các hướng dẫn riêng của chúng tôi:
Ở phần tiếp theo, chúng ta sẽ xây dựng một ví dụ hoàn chỉnh áp dụng lan truyền xuôi cho một bài toán thực tế.
Ví dụ về Lan truyền Xuôi
Giờ đây khi đã triển khai lan truyền xuôi cả từ đầu lẫn bằng các framework phổ biến, hãy xem nó hoạt động như thế nào trên một bài toán thực tế. Chúng ta sẽ xây dựng một ví dụ hoàn chỉnh và tìm hiểu cách tối ưu quy trình để có hiệu năng tốt hơn.
Ví dụ hoạt động hoàn chỉnh
Hãy áp dụng hiểu biết của chúng ta vào một bài toán kinh điển trong học máy: nhận dạng chữ số viết tay với bộ dữ liệu MNIST. Bộ dữ liệu này chứa 70.000 ảnh chữ số viết tay (0–9), mỗi ảnh có kích thước 28x28 pixel.
Trước tiên, hãy tải và chuẩn bị dữ liệu:
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
# Load MNIST dataset(X_train, y_train),(X_test, y_test)= mnist.load_data()# Normalize pixel values to range [0, 1]
X_train = X_train /255.0
X_test = X_test /255.0# Reshape images to vectors (flatten 28x28 to 784)
X_train_flat = X_train.reshape(X_train.shape[0],-1).T # Shape: (784, 60000)
X_test_flat = X_test.reshape(X_test.shape[0],-1).T # Shape: (784, 10000)# Convert labels to one-hot encodingdefone_hot_encode(y, num_classes=10):
one_hot = np.zeros((num_classes, y.size))
one_hot[y, np.arange(y.size)]=1return one_hot
y_train_one_hot = one_hot_encode(y_train)# Shape: (10, 60000)
y_test_one_hot = one_hot_encode(y_test)# Shape: (10, 10000)# Display sample images
plt.figure(figsize=(10,5))for i inrange(5):
plt.subplot(1,5, i+1)
plt.imshow(X_train[i], cmap='gray')
plt.title(f"Label: {y_train[i]}")
plt.axis('off')
plt.tight_layout()
plt.show()Được hỗ trợ bởi
Bây giờ, hãy xây dựng một mạng nơ-ron cho tác vụ này bằng triển khai NumPy của chúng ta. Chúng ta sẽ tạo một mạng với:
Tầng đầu vào: 784 nơ-ron (28x28 pixel)
Tầng ẩn: 128 nơ-ron với kích hoạt ReLU
Tầng đầu ra: 10 nơ-ron với kích hoạt softmax (cho 10 chữ số)
Chúng ta đạt 7% độ chính xác, thậm chí còn tệ hơn mô hình đoán ngẫu nhiên. Nhưng điều đó là bình thường vì chúng ta chỉ thực hiện lan truyền xuôi — chưa có học nào diễn ra.
Giờ hãy trực quan hóa cách mạng xử lý một ảnh qua từng tầng:
Hình minh họa này cho thấy thông tin chảy qua mạng của chúng ta như sau:
Tầng đầu vào: Các pixel ảnh được làm phẳng (giá trị từ 0 đến 1)
Tầng ẩn: Các nơ-ron kích hoạt ReLU nắm bắt các đặc trưng khác nhau của chữ số
Tầng đầu ra: Kích hoạt softmax trả về phân phối xác suất trên 10 chữ số có thể
Nơ-ron đầu ra hoạt động mạnh nhất tương ứng với dự đoán của mạng. Ngay cả với trọng số ngẫu nhiên (vì chúng ta chưa huấn luyện mạng), ta có thể thấy lan truyền xuôi biến đổi dữ liệu đầu vào thành một dự đoán như thế nào.
Trong một ví dụ đầy đủ hơn, chúng ta sẽ huấn luyện mạng này bằng lan truyền ngược để điều chỉnh trọng số và độ chệch, cải thiện dự đoán theo thời gian. Tuy nhiên, ngay cả khi chưa huấn luyện, ví dụ này vẫn minh họa quy trình lan truyền xuôi trên dữ liệu thực.
Tối ưu hóa lan truyền xuôi
Khi các mạng nơ-ron lớn hơn và bộ dữ liệu trở nên đồ sộ hơn, tối ưu hóa lan truyền xuôi trở nên tối quan trọng. Dưới đây là các chiến lược then chốt để khiến lan truyền xuôi hiệu quả hơn:
1. Xử lý theo lô
Thay vì xử lý từng mẫu một, chúng ta tính lan truyền xuôi trên các lô mẫu đồng thời. Điều này tận dụng khả năng xử lý song song của phần cứng hiện đại.
2. Vector hóa
Sử dụng phép toán ma trận thay cho vòng lặp giúp tăng tốc tính toán đáng kể. Đó là lý do tại sao triển khai của chúng ta dùng các phép toán mảng của NumPy.
3. Quản lý bộ nhớ
Với các mạng lớn, cần cẩn trọng về sử dụng bộ nhớ. Các kỹ thuật bao gồm:
Tái sử dụng bộ nhớ cho các kích hoạt trung gian.
Tính kích hoạt theo từng tầng mà không lưu toàn bộ trung gian.
Dùng biểu diễn độ chính xác thấp hơn (ví dụ, số thực 16-bit thay cho 32-bit).
4. Tăng tốc bằng GPU
GPU vượt trội trong các phép toán ma trận. Các framework hiện đại có thể chuyển việc tính toán sang GPU một cách liền mạch để tăng tốc đáng kể:
# TensorFlow example of GPU accelerationimport tensorflow as tf
# Check for available GPUsprint("GPUs Available:", tf.config.list_physical_devices('GPU'))# TensorFlow automatically uses available GPUswith tf.device('/GPU:0'):# Explicitly specify GPU if multiple are available# Computation runs on GPU if available
result = tf_model.predict(X_tf)Được hỗ trợ bởi
5. Phần cứng chuyên dụng
Ngoài GPU, phần cứng như TPU (Tensor Processing Unit) của Google được thiết kế đặc thù cho các phép toán mạng nơ-ron.
6. Tối ưu hóa thuật toán
Một số hàm kích hoạt và kiến trúc mạng được thiết kế để hiệu quả tính toán:
ReLU tính nhanh hơn sigmoid hoặc tanh.
Tích chập tách chiều sâu (depthwise separable) giảm tính toán so với tích chập chuẩn.
Cắt tỉa mạng (pruning) loại bỏ kết nối không cần thiết, giảm tính toán.
7. Tối ưu hóa từ framework
Các framework học sâu triển khai nhiều tối ưu hóa tầng thấp:
Các phép toán hợp nhất (fused) gộp nhiều bước vào một kernel tối ưu duy nhất.
Thuật toán tự điều chỉnh chọn triển khai nhanh nhất cho phần cứng cụ thể.
Biên dịch JIT (Just-In-Time) tạo mã tối ưu cho cấu hình mạng cụ thể.
Khi mạng ngày càng sâu và rộng, tối ưu hóa lan truyền xuôi càng trở nên quan trọng. Các kiến trúc hiện đại như ResNet, Transformer và EfficientNet tích hợp các lựa chọn thiết kế nhằm khiến lan truyền xuôi hiệu quả hơn trong khi vẫn duy trì hoặc cải thiện độ chính xác của mô hình.
Ở phần tiếp theo, chúng ta sẽ khám phá cách lan truyền xuôi liên kết với lan truyền ngược (backpropagation), hoàn thiện bức tranh về cách mạng nơ-ron học.
Mối quan hệ với Lan truyền Ngược
Chúng ta đã tìm hiểu kỹ về lan truyền xuôi, nhưng đó chỉ là một nửa câu chuyện khi nói đến mạng nơ-ron. Hãy xem nhanh cách lan truyền xuôi liên kết với lan truyền ngược, thuật toán giúp mạng nơ-ron học.
Kết nối lan truyền xuôi và lan truyền ngược
Lan truyền xuôi và lan truyền ngược hoạt động như các quy trình bổ sung cho nhau trong mạng nơ-ron:
Lan truyền xuôi nhận đầu vào và tạo ra dự đoán, di chuyển từ trái sang phải qua mạng.
Lan truyền ngược nhận lỗi và tạo ra cập nhật trọng số, di chuyển từ phải sang trái qua mạng.
Hai quy trình này không thể tách rời trong quá trình học. Lan truyền ngược không thể diễn ra nếu chưa thực hiện lan truyền xuôi, vì nó cần:
Các kích hoạt từ mỗi tầng (mà chúng ta đã lưu trong cache).
Dự đoán cuối cùng để so sánh với nhãn đúng.
Kiến trúc mạng và trọng số đã dùng trong lan truyền xuôi.
Hãy nghĩ về lan truyền xuôi như cách mạng nơ-ron đưa ra phỏng đoán tốt nhất dựa trên hiểu biết hiện tại, còn lan truyền ngược là cách nó tinh chỉnh hiểu biết đó dựa trên sai sót mắc phải.
Vòng lặp học
Toàn bộ quá trình học tuân theo một chu trình lặp:
Lan truyền xuôi: Mạng xử lý dữ liệu đầu vào và đưa ra dự đoán.
Tính hàm mất mát: So sánh dự đoán với mục tiêu thực để tính lỗi.
Lan truyền ngược: Lan truyền lỗi ngược qua mạng để xác định mỗi trọng số đã góp phần vào lỗi như thế nào.
Cập nhật trọng số: Điều chỉnh trọng số để giảm lỗi.
Lặp lại: Tiếp tục quy trình với dữ liệu mới cho đến khi mạng hoạt động tốt.
Điều làm cho quy trình này mạnh mẽ là lan truyền xuôi cung cấp ngữ cảnh cần thiết cho lan truyền ngược. Trong lan truyền xuôi, mạng không chỉ đưa ra dự đoán mà còn theo dõi tất cả giá trị và quyết định trung gian. Lan truyền ngược sau đó dùng thông tin này để điều chỉnh có mục tiêu.
Điều đẹp đẽ ở hệ thống này là việc học phức tạp nảy sinh từ hai quy trình tương đối đơn giản phối hợp cùng nhau. Lan truyền xuôi khá trực quan — chỉ là nhân ma trận và áp dụng hàm kích hoạt tuần tự. Lan truyền ngược phức tạp hơn nhưng xuất phát trực tiếp từ các nguyên lý giải tích.
Kết hợp lại, chúng cho phép mạng nơ-ron học hầu như bất kỳ mẫu nào nếu có đủ dữ liệu và tài nguyên tính toán. Khả năng học này đã thúc đẩy các đột phá trong thị giác máy tính, xử lý ngôn ngữ tự nhiên, chơi game và vô số lĩnh vực khác.
Hiểu sâu về lan truyền xuôi, như chúng ta đã làm trong bài viết này, cung cấp nền tảng cần thiết để nắm bắt toàn bộ quá trình học trong mạng nơ-ron. Nếu bạn muốn đào sâu hơn vào khía cạnh học, hướng dẫn của chúng tôi vềlan truyền ngược cung cấp một khám phá chi tiết về cách mạng học từ sai lầm.
Kết luận và Bước tiếp theo
Lan truyền xuôi là quy trình nền tảng cho phép mạng nơ-ron biến đổi đầu vào thành dự đoán. Như chúng ta đã tìm hiểu, nó bao gồm các phép nhân ma trận và hàm kích hoạt tuần tự để biến đổi dữ liệu dần qua các tầng của mạng. Hiểu quy trình này là rất quan trọng dù bạn đang tự triển khai mạng, dùng các framework hiện đại, hay khắc phục sự cố hiệu năng mô hình.
Bằng việc nắm vững lan truyền xuôi, bạn đã tiến một bước quan trọng hướng tới xây dựng và hiểu các mô hình học sâu có thể giải quyết những vấn đề phức tạp trên nhiều lĩnh vực.
Để tiếp tục hành trình học sâu của bạn, hãy khám phá các tài nguyên toàn diện của DataCamp:
Đối với lộ trình học có cấu trúc,Deep Learning in Python cung cấp mọi thứ bạn cần để thành thạo xây dựng và triển khai mạng nơ-ron cho các ứng dụng thực tế.
Câu hỏi thường gặp về Lan truyền Xuôi
Sự khác nhau giữa lan truyền xuôi và lan truyền ngược là gì?⌄
Lan truyền xuôi là quá trình đưa dữ liệu đi qua mạng nơ-ron từ đầu vào tới đầu ra để tạo dự đoán. Lan truyền ngược di chuyển theo hướng ngược lại, tính gradient để cập nhật trọng số dựa trên lỗi dự đoán. Chúng phối hợp trong quá trình huấn luyện - lan truyền xuôi tạo dự đoán, lan truyền ngược giúp mạng học từ sai lầm.
Tại sao chúng ta cần các hàm kích hoạt trong lan truyền xuôi?⌄
Các hàm kích hoạt đưa tính phi tuyến vào mạng nơ-ron, cho phép chúng học các mẫu phức tạp vượt ra ngoài quan hệ tuyến tính đơn giản. Không có hàm kích hoạt, một mạng nơ-ron với bao nhiêu tầng đi nữa cũng sẽ hành xử như một tầng tuyến tính duy nhất, hạn chế nghiêm trọng những gì nó có thể học từ dữ liệu.
Lan truyền xuôi được triển khai hiệu quả trong các framework hiện đại như thế nào? ⌄
Các framework hiện đại tối ưu lan truyền xuôi thông qua xử lý theo lô, tăng tốc GPU, phép toán vector hóa, kỹ thuật quản lý bộ nhớ và thuật toán chuyên biệt. TensorFlow và PyTorch tự động xử lý các tối ưu hóa này, cho phép bạn xây dựng mạng phức tạp mà không cần quản lý chi tiết tầng thấp.
Có thể dùng lan truyền xuôi mà không huấn luyện mạng không?⌄
Có. Khi một mạng nơ-ron đã được huấn luyện, chỉ cần lan truyền xuôi trong giai đoạn suy luận (dự đoán trên dữ liệu mới). Đây là lý do tại sao hiểu lan truyền xuôi rất quan trọng cho việc triển khai mô hình, vì huấn luyện với lan truyền ngược chỉ cần trong giai đoạn phát triển.
Lựa chọn hàm kích hoạt ảnh hưởng đến lan truyền xuôi như thế nào?⌄
Các hàm kích hoạt khác nhau ảnh hưởng cả khả năng học của mạng lẫn hiệu quả tính toán. ReLU tính nhanh hơn và giúp tránh hiện tượng tiêu biến gradient, sigmoid hữu ích cho đầu ra phân loại nhị phân, còn softmax chuẩn hóa đầu ra cho bài toán đa lớp. Lựa chọn này ảnh hưởng đến cả những mẫu mạng có thể học và tốc độ xử lý dữ liệu.
Tôi là người sáng tạo nội dung về khoa học dữ liệu với hơn 2 năm kinh nghiệm và là một trong những tài khoản có lượng người theo dõi lớn nhất trên Medium. Tôi thích viết các bài chuyên sâu về AI và ML với chút giọng điệu mỉa mai, vì bạn cũng phải làm gì đó để chúng bớt khô khan. Tôi đã xuất bản hơn 130 bài viết và một khóa học trên DataCamp, cùng với một khóa nữa đang được thực hiện. Nội dung của tôi đã được hơn 5 triệu lượt xem, trong đó 20 nghìn người đã trở thành người theo dõi trên cả Medium và LinkedIn.
Tiếp tục hành trình học máy của quý vị sang lĩnh vực học sâu. Sử dụng thư viện PyTorch để xây dựng mạng nơ-ron nhằm mô hình hóa các loại dữ liệu khác nhau.
Mạng nơ-ron đã làm thay đổi cục diện trí tuệ nhân tạo, cung cấp sức mạnh cho mọi thứ từ nhận diện hình ảnh đến xử lý ngôn ngữ tự nhiên. Ở trung tâm của những mô hình mạnh mẽ này là một quy trình nền tảng gọi là lan truyền thuận (forward propagation). Hướng dẫn này sẽ khám phá khái niệm cốt lõi đó, đưa bạn từ những nguyên lý cơ bản đến triển khai thực tế.
Nếu bạn đang tìm một hướng dẫn thực hành về nhiều khái niệm được đề cập ở đây, đừng bỏ qua lộ trình kỹ năng Deep Learning in Python của chúng tôi.
Forward Propagation là gì?
Lan truyền thuận là quá trình trong đó mạng nơ-ron biến đổi dữ liệu đầu vào thành dự đoán hoặc đầu ra. Hãy coi đây là giai đoạn "suy nghĩ" của mạng nơ-ron — khi được đưa một đầu vào (như hình ảnh hoặc văn bản), lan truyền thuận là cách mạng xử lý thông tin qua các tầng để tạo ra kết quả.
Về mặt kỹ thuật, đây là phép tính tuần tự di chuyển dữ liệu từ tầng đầu vào, qua các tầng ẩn, và cuối cùng đến tầng đầu ra. Trong hành trình này, dữ liệu được biến đổi bởi các kết nối có trọng số và các hàm kích hoạt, cho phép mạng nắm bắt các mẫu phức tạp.
Vì sao việc hiểu lan truyền thuận lại quan trọng
Hiểu lan truyền thuận rất quan trọng vì nhiều lý do:
Bạn sẽ học được gì trong hướng dẫn này
Kết thúc hướng dẫn toàn diện này, bạn sẽ:
Yêu cầu tiên quyết
Để tận dụng tối đa hướng dẫn này, bạn nên có:
Nếu bạn cần củng cố nền tảng, hãy cân nhắc các tài nguyên sau:
Ngay cả khi chưa có nền tảng sâu, chúng tôi đã thiết kế hướng dẫn này theo kiểu xây dựng dần khái niệm, giúp mạng nơ-ron trở nên dễ tiếp cận với những người học quyết tâm. Hãy bắt đầu thôi!
Nền tảng của Lan Truyền Thuận
Để hiểu lan truyền thuận, ta cần bắt đầu từ các khối xây dựng cơ bản của nó. Hãy khởi đầu với đơn vị tính toán nhỏ nhất trong mạng nơ-ron và dần mở rộng đến các cấu trúc phức tạp hơn.
Nơ-ron đơn: điểm khởi đầu của mọi thứ
Hành trình của mạng nơ-ron bắt đầu với một sự song song thú vị với sinh học. Giống như bộ não con người gồm hàng tỷ nơ-ron liên kết, mạng nơ-ron nhân tạo được xây dựng từ các mô hình toán học lấy cảm hứng từ những tế bào sinh học này.
Nguồn: Deep Learning - Visual Approach
Một nơ-ron sinh học nhận tín hiệu từ các nơ-ron khác thông qua đuôi gai (dendrite), xử lý các tín hiệu này trong thân tế bào, rồi truyền kết quả qua sợi trục (axon) đến các nơ-ron khác. Trong mô hình tính toán, chúng ta phản chiếu quy trình này với:
Hãy trực quan hóa một nơ-ron đơn để cụ thể hóa điều này:
Nguồn: Deep Learning - Visual Approach
Đơn vị tính toán đơn giản này là nền tảng của cả những mạng nơ-ron phức tạp nhất. Nhưng chính xác một nơ-ron biến đổi đầu vào thành đầu ra như thế nào? Đây là lúc toán học xuất hiện.
Nền tảng toán học của một nơ-ron đơn
Hoạt động của một nơ-ron có thể được mô tả bằng một phương trình đơn giản:
Phân tích chi tiết:
Hãy làm một ví dụ cụ thể với số thực. Giả sử ta có một nơ-ron với ba đầu vào:
Đầu tiên, ta tính tổng có trọng số cộng độ chệch:
Tiếp theo, ta áp dụng một hàm kích hoạt. Hãy dùng hàm ReLU (Rectified Linear Unit) phổ biến, được định nghĩa như sau:
Với giá trị tiền kích hoạt âm, nơ-ron ReLU của chúng ta cho ra 0, nghĩa là nó không "bắn" tín hiệu cho đầu vào này.
Hàm kích hoạt rất quan trọng vì nó đưa phi tuyến tính vào mạng. Nếu không có nó, mạng nơ-ron sẽ chỉ học được các quan hệ tuyến tính, bất kể có bao nhiêu tầng. Các hàm kích hoạt thường dùng gồm:
Mỗi hàm kích hoạt có điểm mạnh và trường hợp sử dụng riêng, chúng ta sẽ khám phá thêm khi triển khai mạng nơ-ron.
Từ nơ-ron đến tầng
Các nơ-ron riêng lẻ đã mạnh mẽ, nhưng sức mạnh thực sự của mạng nơ-ron xuất hiện khi nơ-ron được tổ chức thành các tầng. Một tầng đơn giản là một tập hợp nơ-ron xử lý đầu vào song song. Thường có ba loại tầng trong một mạng nơ-ron:
Khi có nhiều nơ-ron trong một tầng, mỗi nơ-ron nhận cùng đầu vào nhưng với trọng số và độ chệch khác nhau, ta có thể biểu diễn hiệu quả bằng các phép toán ma trận. Hãy xem điều này hoạt động ra sao.
Hình dung ta có một tầng với 3 giá trị đầu vào và 4 nơ-ron. Mỗi nơ-ron có bộ trọng số và độ chệch riêng:
Ta có thể sắp xếp các trọng số này thành một ma trận W:
Giờ ta có thể tính toàn bộ các giá trị tiền kích hoạt của nơ-ron cùng lúc bằng một phép nhân ma trận:
Trong đó:
Sau đó ta áp dụng hàm kích hoạt theo từng phần tử để nhận đầu ra cuối cùng:
Biểu diễn ma trận không chỉ là sự tao nhã về mặt toán học; đó còn là hiệu quả tính toán. Phần cứng hiện đại (đặc biệt là GPU) được tối ưu cho phép toán ma trận, giúp cách tiếp cận này nhanh hơn nhiều so với việc tính đầu ra của từng nơ-ron riêng lẻ.
Khả năng xếp chồng các tầng — với đầu ra của tầng này trở thành đầu vào của tầng kế tiếp — chính là điều mang lại cho mạng nơ-ron năng lực đáng kinh ngạc để học các mẫu phức tạp. Khi kết nối các khối xây dựng này, chúng ta đã sẵn sàng khám phá cách lan truyền thuận hoạt động xuyên suốt một mạng nơ-ron hoàn chỉnh.
Lan Truyền Thuận Qua Một Mạng Hoàn Chỉnh
Giờ khi đã hiểu nơ-ron riêng lẻ và các tầng, hãy lùi lại để xem lan truyền thuận hoạt động như thế nào trên toàn bộ mạng nơ-ron. Đây là nơi sức mạnh thực sự của học sâu bộc lộ.
Mạng nhiều tầng: bức tranh toàn diện
Một mạng nơ-ron đầy đủ gồm tầng đầu vào, một hoặc nhiều tầng ẩn, và tầng đầu ra. Thuật ngữ "sâu" trong học sâu (deep learning) ám chỉ các mạng có nhiều tầng ẩn. Mỗi tầng biến đổi dữ liệu theo cách ngày càng trừu tượng, cho phép mạng học các biểu diễn phức tạp.
Hãy xét một mạng nơ-ron đơn giản với:
Trực quan, mạng này trông như sau:
Khi dữ liệu chảy qua mạng này, ta thực hiện một loạt phép tính ở mỗi tầng. Nếu ký hiệu:
Ta có thể biểu diễn lan truyền thuận qua toàn bộ mạng này như sau:
1. Tính tiền kích hoạt của tầng ẩn:
2. Áp dụng hàm kích hoạt để lấy đầu ra tầng ẩn:
3. Tính tiền kích hoạt của tầng đầu ra:
4. Áp dụng hàm kích hoạt để lấy đầu ra cuối cùng:
Đầu ra cuối cùng A^[2] biểu thị dự đoán của mạng. Với bài toán phân loại, đó có thể là xác suất cho mỗi lớp; với hồi quy, có thể là giá trị dự đoán.
Các tầng khác nhau thường dùng các hàm kích hoạt khác nhau. Chẳng hạn, các tầng ẩn thường dùng ReLU, trong khi tầng đầu ra có thể dùng:
Điều tuyệt vời của cấu trúc nhiều tầng là mỗi tầng có thể học biểu diễn các khía cạnh khác nhau của dữ liệu. Các tầng đầu thường phát hiện đặc trưng đơn giản, trong khi các tầng sâu hơn kết hợp chúng thành các mẫu phức tạp hơn. Chính học tập phân cấp này làm cho mạng nơ-ron mạnh mẽ với các tác vụ phức tạp như nhận diện hình ảnh và giọng nói.
Thuật toán lan truyền thuận
Hãy chính thức hóa quy trình lan truyền thuận thành một thuật toán. Với một mạng nơ-ron có L tầng, lan truyền thuận theo các bước sau:
Thuật toán này làm nổi bật một vài khía cạnh quan trọng của lan truyền thuận:
Thuật toán lan truyền thuận rất đơn giản, nhưng cho phép mạng nơ-ron xấp xỉ các hàm cực kỳ phức tạp. Khi kết hợp với quá trình huấn luyện đúng đắn qua lan truyền ngược, quy trình đơn giản này giúp mạng học từ dữ liệu và đưa ra dự đoán ngày càng chính xác.
Hãy theo dõi một ví dụ cụ thể để củng cố hiểu biết. Xét đầu vào X = [0.5, -0.2, 0.1] đi qua mạng ví dụ của chúng ta với:
Để đơn giản, giả sử tất cả trọng số đều là 0.1 và tất cả độ chệch bằng 0:
Theo thuật toán của chúng ta:
Điều này cho ra dự đoán cuối cùng của chúng ta. Trong bối cảnh phân loại nhị phân, các giá trị gần 0.5 cho thấy sự không chắc chắn giữa hai lớp.
Ở phần tiếp theo, chúng ta sẽ triển khai lan truyền thuận bằng Python để xem các phép tính này hoạt động thực tế.
Triển Khai Lan Truyền Thuận bằng Python
Giờ khi đã hiểu lý thuyết đằng sau lan truyền thuận, hãy áp dụng thực tế bằng cách triển khai nó trong Python. Chúng ta sẽ bắt đầu với triển khai "từ đầu" chỉ dùng NumPy, sau đó xem các framework học sâu hiện đại đơn giản hóa quy trình này như thế nào.
Xây dựng từ đầu với NumPy
NumPy cung cấp các phép toán mảng hiệu quả cho phép chúng ta triển khai các phép tính ma trận đã thảo luận. Hãy xây dựng một lớp mạng nơ-ron đơn giản thực hiện lan truyền thuận qua nhiều tầng.
Trước tiên, ta cần import các thư viện cần thiết:
Giờ hãy định nghĩa các hàm kích hoạt sẽ dùng trong mạng của chúng ta:
Tiếp theo, hãy tạo một lớp cho mạng nơ-ron của chúng ta:
Lớp
NeuralNetworkmà chúng ta đã triển khai ở trên cung cấp một khung đầy đủ để tạo và sử dụng mạng nơ-ron với kiến trúc tùy biến. Hãy phân tách các thành phần chính:forward_propagationvừa triển khai là trái tim của khả năng dự đoán của mạng. Nó:3. Hàm kích hoạt: Mạng hỗ trợ nhiều hàm kích hoạt, bao gồm ReLU, sigmoid, và softmax, cho phép xử lý các loại bài toán khác nhau (hồi quy hoặc phân loại).
4. Kiến trúc linh hoạt: Triển khai này cho phép các mạng có độ sâu và độ rộng tùy ý, phù hợp với nhiều nhiệm vụ học máy.
Triển khai này tuân theo mẫu thiết kế mạng nơ-ron tiêu chuẩn, trong đó dữ liệu chảy tiến qua mạng, mỗi tầng biến đổi dữ liệu trước khi chuyển cho tầng tiếp theo.
Giờ hãy kiểm thử triển khai bằng một mạng ví dụ nhỏ:
Kết quả:
Hãy trực quan hóa cách dữ liệu biến đổi khi chảy qua mạng:
Hình trực quan này giúp chúng ta hiểu cách dữ liệu đầu vào được biến đổi khi lan truyền qua mạng. Ta có thể thấy rằng:
Triển khai "từ đầu" cho thấy các nguyên lý cốt lõi của lan truyền thuận, nhưng các framework học sâu hiện đại cung cấp công cụ hiệu quả và linh hoạt hơn để xây dựng mạng nơ-ron.
Sử dụng các framework học sâu
Giờ hãy triển khai cùng một mạng nơ-ron bằng các framework học sâu phổ biến: TensorFlow và PyTorch. Những framework này tối ưu hiệu năng và cung cấp các trừu tượng cấp cao, giúp xây dựng mô hình phức tạp dễ dàng hơn.
Trước tiên, hãy xem triển khai bằng TensorFlow/Keras:
Chúng ta vừa triển khai mạng nơ-ron bằng TensorFlow/Keras và thực hiện lan truyền thuận trên một số dữ liệu mẫu thành công. Bản tóm tắt mô hình cho thấy kiến trúc với một tầng đầu vào nhận 3 đặc trưng, một tầng ẩn có 4 nơ-ron dùng kích hoạt ReLU, và một tầng đầu ra có 2 nơ-ron dùng kích hoạt sigmoid. Tổng cộng, mô hình có 26 tham số có thể huấn luyện.
Kết quả lan truyền thuận cho thấy kích thước đầu ra (5, 2) tương ứng với 5 mẫu đầu vào, mỗi mẫu tạo ra 2 giá trị đầu ra. Các đầu ra này bị giới hạn trong khoảng 0 đến 1 nhờ hàm sigmoid ở tầng đầu ra.
Tiếp theo, hãy xem cách ta có thể triển khai cùng kiến trúc bằng PyTorch, một framework học sâu phổ biến khác, để so sánh cách tiếp cận.
Kết quả:
Kết quả:
Hãy so sánh các triển khai của chúng ta:
Đầu ra:
Những khác biệt chính giữa các cách triển khai của chúng ta:
Với các mạng đơn giản, triển khai bằng NumPy của chúng ta hoạt động ổn, nhưng khi độ phức tạp tăng, các framework học sâu trở nên thiết yếu. Chúng xử lý tối ưu hóa tầng thấp và cung cấp công cụ cho toàn bộ quy trình học máy, từ phát triển đến triển khai. Để tìm hiểu thêm về các framework này, hãy xem các hướng dẫn riêng của chúng tôi:
Ở phần tiếp theo, chúng ta sẽ xây dựng một ví dụ hoàn chỉnh áp dụng lan truyền xuôi cho một bài toán thực tế.
Ví dụ về Lan truyền Xuôi
Giờ đây khi đã triển khai lan truyền xuôi cả từ đầu lẫn bằng các framework phổ biến, hãy xem nó hoạt động như thế nào trên một bài toán thực tế. Chúng ta sẽ xây dựng một ví dụ hoàn chỉnh và tìm hiểu cách tối ưu quy trình để có hiệu năng tốt hơn.
Ví dụ hoạt động hoàn chỉnh
Hãy áp dụng hiểu biết của chúng ta vào một bài toán kinh điển trong học máy: nhận dạng chữ số viết tay với bộ dữ liệu MNIST. Bộ dữ liệu này chứa 70.000 ảnh chữ số viết tay (0–9), mỗi ảnh có kích thước 28x28 pixel.
Trước tiên, hãy tải và chuẩn bị dữ liệu:
Bây giờ, hãy xây dựng một mạng nơ-ron cho tác vụ này bằng triển khai NumPy của chúng ta. Chúng ta sẽ tạo một mạng với:
Chúng ta đạt 7% độ chính xác, thậm chí còn tệ hơn mô hình đoán ngẫu nhiên. Nhưng điều đó là bình thường vì chúng ta chỉ thực hiện lan truyền xuôi — chưa có học nào diễn ra.
Giờ hãy trực quan hóa cách mạng xử lý một ảnh qua từng tầng:
Hình minh họa này cho thấy thông tin chảy qua mạng của chúng ta như sau:
Nơ-ron đầu ra hoạt động mạnh nhất tương ứng với dự đoán của mạng. Ngay cả với trọng số ngẫu nhiên (vì chúng ta chưa huấn luyện mạng), ta có thể thấy lan truyền xuôi biến đổi dữ liệu đầu vào thành một dự đoán như thế nào.
Trong một ví dụ đầy đủ hơn, chúng ta sẽ huấn luyện mạng này bằng lan truyền ngược để điều chỉnh trọng số và độ chệch, cải thiện dự đoán theo thời gian. Tuy nhiên, ngay cả khi chưa huấn luyện, ví dụ này vẫn minh họa quy trình lan truyền xuôi trên dữ liệu thực.
Tối ưu hóa lan truyền xuôi
Khi các mạng nơ-ron lớn hơn và bộ dữ liệu trở nên đồ sộ hơn, tối ưu hóa lan truyền xuôi trở nên tối quan trọng. Dưới đây là các chiến lược then chốt để khiến lan truyền xuôi hiệu quả hơn:
1. Xử lý theo lô
Thay vì xử lý từng mẫu một, chúng ta tính lan truyền xuôi trên các lô mẫu đồng thời. Điều này tận dụng khả năng xử lý song song của phần cứng hiện đại.
2. Vector hóa
Sử dụng phép toán ma trận thay cho vòng lặp giúp tăng tốc tính toán đáng kể. Đó là lý do tại sao triển khai của chúng ta dùng các phép toán mảng của NumPy.
3. Quản lý bộ nhớ
Với các mạng lớn, cần cẩn trọng về sử dụng bộ nhớ. Các kỹ thuật bao gồm:
4. Tăng tốc bằng GPU
GPU vượt trội trong các phép toán ma trận. Các framework hiện đại có thể chuyển việc tính toán sang GPU một cách liền mạch để tăng tốc đáng kể:
5. Phần cứng chuyên dụng
Ngoài GPU, phần cứng như TPU (Tensor Processing Unit) của Google được thiết kế đặc thù cho các phép toán mạng nơ-ron.
6. Tối ưu hóa thuật toán
Một số hàm kích hoạt và kiến trúc mạng được thiết kế để hiệu quả tính toán:
7. Tối ưu hóa từ framework
Các framework học sâu triển khai nhiều tối ưu hóa tầng thấp:
Khi mạng ngày càng sâu và rộng, tối ưu hóa lan truyền xuôi càng trở nên quan trọng. Các kiến trúc hiện đại như ResNet, Transformer và EfficientNet tích hợp các lựa chọn thiết kế nhằm khiến lan truyền xuôi hiệu quả hơn trong khi vẫn duy trì hoặc cải thiện độ chính xác của mô hình.
Ở phần tiếp theo, chúng ta sẽ khám phá cách lan truyền xuôi liên kết với lan truyền ngược (backpropagation), hoàn thiện bức tranh về cách mạng nơ-ron học.
Mối quan hệ với Lan truyền Ngược
Chúng ta đã tìm hiểu kỹ về lan truyền xuôi, nhưng đó chỉ là một nửa câu chuyện khi nói đến mạng nơ-ron. Hãy xem nhanh cách lan truyền xuôi liên kết với lan truyền ngược, thuật toán giúp mạng nơ-ron học.
Kết nối lan truyền xuôi và lan truyền ngược
Lan truyền xuôi và lan truyền ngược hoạt động như các quy trình bổ sung cho nhau trong mạng nơ-ron:
Hai quy trình này không thể tách rời trong quá trình học. Lan truyền ngược không thể diễn ra nếu chưa thực hiện lan truyền xuôi, vì nó cần:
Hãy nghĩ về lan truyền xuôi như cách mạng nơ-ron đưa ra phỏng đoán tốt nhất dựa trên hiểu biết hiện tại, còn lan truyền ngược là cách nó tinh chỉnh hiểu biết đó dựa trên sai sót mắc phải.
Vòng lặp học
Toàn bộ quá trình học tuân theo một chu trình lặp:
Điều làm cho quy trình này mạnh mẽ là lan truyền xuôi cung cấp ngữ cảnh cần thiết cho lan truyền ngược. Trong lan truyền xuôi, mạng không chỉ đưa ra dự đoán mà còn theo dõi tất cả giá trị và quyết định trung gian. Lan truyền ngược sau đó dùng thông tin này để điều chỉnh có mục tiêu.
Điều đẹp đẽ ở hệ thống này là việc học phức tạp nảy sinh từ hai quy trình tương đối đơn giản phối hợp cùng nhau. Lan truyền xuôi khá trực quan — chỉ là nhân ma trận và áp dụng hàm kích hoạt tuần tự. Lan truyền ngược phức tạp hơn nhưng xuất phát trực tiếp từ các nguyên lý giải tích.
Kết hợp lại, chúng cho phép mạng nơ-ron học hầu như bất kỳ mẫu nào nếu có đủ dữ liệu và tài nguyên tính toán. Khả năng học này đã thúc đẩy các đột phá trong thị giác máy tính, xử lý ngôn ngữ tự nhiên, chơi game và vô số lĩnh vực khác.
Hiểu sâu về lan truyền xuôi, như chúng ta đã làm trong bài viết này, cung cấp nền tảng cần thiết để nắm bắt toàn bộ quá trình học trong mạng nơ-ron. Nếu bạn muốn đào sâu hơn vào khía cạnh học, hướng dẫn của chúng tôi về lan truyền ngược cung cấp một khám phá chi tiết về cách mạng học từ sai lầm.
Kết luận và Bước tiếp theo
Lan truyền xuôi là quy trình nền tảng cho phép mạng nơ-ron biến đổi đầu vào thành dự đoán. Như chúng ta đã tìm hiểu, nó bao gồm các phép nhân ma trận và hàm kích hoạt tuần tự để biến đổi dữ liệu dần qua các tầng của mạng. Hiểu quy trình này là rất quan trọng dù bạn đang tự triển khai mạng, dùng các framework hiện đại, hay khắc phục sự cố hiệu năng mô hình.
Bằng việc nắm vững lan truyền xuôi, bạn đã tiến một bước quan trọng hướng tới xây dựng và hiểu các mô hình học sâu có thể giải quyết những vấn đề phức tạp trên nhiều lĩnh vực.
Để tiếp tục hành trình học sâu của bạn, hãy khám phá các tài nguyên toàn diện của DataCamp:
Đối với lộ trình học có cấu trúc, Deep Learning in Python cung cấp mọi thứ bạn cần để thành thạo xây dựng và triển khai mạng nơ-ron cho các ứng dụng thực tế.