Bài 4: Backpropagation | Deep Learning cơ bản
 

Bài 4: Backpropagation

| Posted in Deep Learning cơ bản

Bài trước đã học về mô hình neural networkfeedforward, giờ ta cần đi tìm hệ số W và b. Có thể nghĩ ngay tới thuật toán gradient descent và việc quan trọng nhất trong thuật toán gradient descent là đi tìm đạo hàm của các hệ số đối với loss function. Bài này sẽ tính đạo hàm của các hệ số trong neural network với thuật toán backpropagation.

Bạn nên hoàn thành bài neural network trước khi bắt đầu bài này và bài này là không bắt buộc để theo các bài tiếp theo trong series.

Bài toán XOR với neural network

Bảng chân lý cho toán xử XOR

x1x2x1 XOR x2
110
101
011
000

Bài trước đã chứng minh là mô hình logistic regression trong bài 2 không thể biểu diễn được toán tử XOR. Để biểu diễn toán tử XOR ta cần thêm 1 hidden layer với 2 node.

Model

Nhắc lại kiến thức bài trước:

  • Mô hình trên có 2 layer (số lượng layer của mô hình không tính input layer)
  • Mô hình: 2-2-1, nghĩa là 2 node trong input layer, 1 hidden layer có 2 node và output layer có 1 node.
  • Input layer và hidden layer luôn thêm node 1 để tính bias cho layer sau, nhưng không tính vào số lượng node trong layer
  • Ở mỗi node trong hidden layer và output layer đều thực hiện 2 bước: tính tổng linear và áp dụng activation function.
  • Các hệ số và bias tương ứng được ký hiệu như trong hình

Feedforward

  • z_1^{(1)} = b_1^{(1)} + x_1*w_{11}^{(1)} + x_2*w_{21}^{(1)}
  • a_1^{(1)} = \sigma(z_1^{(1)})
  • z_2^{(1)} = b_2^{(1)} + x_1*w_{12}^{(1)} + x_2*w_{22}^{(1)}
  • a_2^{(1)} = \sigma(z_2^{(1)})
  • z_1^{(2)} = b_1^{(2)} + a_1^{(1)}*w_{11}^{(2)} + a_2^{(1)} * w_{21}^{(2)}
  • \hat{y} = a_1^{(2)} = \sigma(z_1^{(2)})

Viết dưới dạng ma trận

Z^{(1)} = X * W^{(1)} + b^{(1)} \newline A^{(1)} = \sigma(Z^{(1)}) \newline Z^{(2)} = A^{(1)} * W^{(2)} + b^{(2)} \newline \hat{Y} = A^{(2)} = \sigma(Z^{(2)})

Loss function

Hàm loss fucntion vẫn dùng giống như trong bài 2

Với mỗi điểm (x^{[i]}, y_i), gọi hàm loss function

L = -(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i}))

Hàm loss function trên toàn bộ dữ liệu

\displaystyle J = - \sum_{i=1}^{N}(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i}))

Gradient descent

Để áp dụng gradient descent ta cần tính được đạo hàm của các hệ số W và bias b với hàm loss function.

*** Kí hiệu chuẩn về đạo hàm

  • Khi hàm f(x) là hàm 1 biến x, ví dụ: \displaystyle f(x) = 2*x + 1. Đạo hàm của f đối với biến x kí hiệu là \displaystyle\frac{df}{dx}\newline
  • Khi hàm f(x, y) là hàm nhiều biến, ví dụ \displaystyle f(x, y) = x^2 + y^2 . Đạo hàm f với biến x kí hiệu là \displaystyle \frac{\partial f}{\partial x}

Với mỗi điểm (x^{([i]}, y_i), hàm loss function

L = \displaystyle-(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i})) trong đó \hat{y_i} = a_1^{(2)} = \sigma(a_1^{(1)} * w_{11}^{(2)} + a_2^{(1)} * w_{21}^{(2)} + b_1^{(2)}) là giá trị mà model dự đoán, còn y_i là giá trị thật của dữ liệu.

\displaystyle \frac{\partial L}{\partial\hat{y_i}} = - \frac{\partial(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i}))}{\partial\hat{y_i}}= - (\frac{y_i}{\hat{y_i}} - \frac{1-y_i}{(1-\hat{y})})\newline

Tính đạo hàm L với W^{(2)}, b^{(2)}

Áp dụng chain rule ta có: \displaystyle \frac{\partial L}{\partial b_1^{(2)}} = \frac{dL}{d\hat{y_i}} * \frac{\partial\hat{y_i}}{\partial b_1^{(2)} }

Từ đồ thị ta thấy:

\displaystyle \frac{\partial\hat{y_i}}{\partial b_1^{(2)}} = \hat{y_i} * (1-\hat{y_i})\newline \displaystyle \frac{\partial\hat{y_i}}{\partial w_{11}^{(2)}} = a_1^{(1)}*\hat{y_i} * (1-\hat{y_i})\newline \displaystyle \frac{\partial\hat{y_i}}{\partial w_{21}^{(2)}} = a_2^{(1)}*\hat{y_i} * (1-\hat{y_i})\newline \displaystyle \frac{\partial\hat{y_i}}{\partial a_1^{(1)} }=w_{11} ^{(2)}*\hat{y_i} * (1-\hat{y_i})\newline \displaystyle \frac{\partial\hat{y_i}}{\partial a_2^{(1)} }=w_{21} ^{(2)}*\hat{y_i} * (1-\hat{y_i})\newline

Do đó

\displaystyle \frac{\partial L}{\partial b_1^{(2)}} = \frac{\partial L}{\partial\hat{y_i}} * \frac {\partial\hat{y_i}}{\partial b_1^{(2)}} = - (\frac{y_i}{\hat{y_i}} - \frac{1-y_i}{(1-\hat{y_i})}) * \hat{y_i} * (1-\hat{y_i}) = -(y_i * (1-\hat{y_i}) - (1-y_i) * \hat{y_i})) = \hat{y_i}-y_i

Tương tự

\displaystyle \frac{\partial L}{\partial w_{11} ^ {(2)}} = a_1 ^ {(1)} * (\hat{y_i}-y_i)\newline \newline \displaystyle \frac{\partial L}{\partial w_{21} ^ {(2)}} = a_2 ^ {(1)} * (\hat{y_i}-y_i) \newline \newline \displaystyle \frac{\partial L}{\partial a_1 ^ {(1)}} = w_{11} ^ {(2)} * (\hat{y_i}-y_i) \newline \newline \displaystyle \frac{\partial L}{\partial a_2 ^ {(1)}} = w_{21} ^ {(2)} * (\hat{y_i}-y_i) \newline \newline

Biểu diễn dưới dạng ma trận

*** Lưu ý: đạo hàm của L đối với ma trận W kích thước m*n cũng là một ma trận cùng kích thước m*n.

Do đó, \displaystyle\frac{\partial J}{\partial W^{(2)}} = (A^{(1)})^T * (\hat{Y} - Y), \frac{\partial J}{\partial b^{(2)}} = (sum(\hat{Y} - Y))^T, \frac{\partial J}{\partial A^{(1)}} = (\hat{Y} - Y) * (W^{(2)})^T, phép tính sum tính tổng các cột của ma trận.

Vậy là đã tính xong đạo hàm của L với hệ số W^{(2)}, b^{(2)}. Giờ sẽ đi tính đạo hàm của L với hệ số W^{(1)}, b^{(1)}. Khoan, tưởng chỉ cần tính đạo hàm của L với các hệ số W và bias b, tại sao cần tính đạo hàm của L với A^{(1)} ??? Khi tính đạo hàm của hệ số và bias trong layer trước đấy sẽ cần dùng đến.

Tính đạo hàm L với W^{(1)}, b^{(1)}

Do \displaystyle a_1^{(1)} = \sigma(b_1^{(1)} + x_1*w_{11}^{(1)} + x_2*w_{21}^{(1)})

Áp dụng chain rule ta có: \displaystyle \frac{\partial L}{\partial b_1^{(1)}} = \frac{\partial L}{\partial a_1^{(1)}} * \frac{\partial a_1^{(1)}}{\partial b_1^{(1)} }

Ta có:

\displaystyle \frac{\partial a_1^{(1)}}{\partial b_1^{(1)}} = \frac{\partial a_1^{(1)}}{z_1^{(1)}} * \frac{z_1^{(1)}}{\partial b_1^{(1)}} = a_1^{(1)} * (1 - a_1^{(1)})

Do đó

\displaystyle \frac{\partial L}{\partial b_1^{(1)}} = a_1 ^ {(1)} * (1 - a_1^{(1)}) * w_{11}^{(2)} * (\hat{y_i} - y_i)

Tương tự

\displaystyle \frac{\partial L}{\partial w_{11}^{(1)}} = x_1 * a_1 ^ {(1)} * (1 - a_1^{(1)}) * w_{11}^{(2)} * (\hat{y_i} - y_i) \newline \displaystyle \frac{\partial L}{\partial w_{12}^{(1)}} = x_1 * a_2 ^ {(1)} * (1 - a_2^{(1)}) * w_{11}^{(2)} * (\hat{y_i} - y_i) \newline \displaystyle \frac{\partial L}{\partial w_{21}^{(1)}} = x_2 * a_1 ^ {(1)} * (1 - a_1^{(1)}) * w_{21}^{(2)} * (\hat{y_i} - y_i) \newline \displaystyle \frac{\partial L}{\partial w_{22}^{(1)}} = x_2 * a_2^ {(1)} * (1 - a_2^{(1)}) * w_{21}^{(2)} * (\hat{y_i} - y_i) \newline

Viết dưới dạng ma trận

Có thể tạm viết dưới dạng chain rule là \displaystyle\frac{\partial J}{\partial W^{(1)}} = \frac{\partial J}{\partial A^{(1)}} * \frac{\partial A^{(1)}}{\partial Z^{(1)}}* \frac{\partial Z^{(1)}}{\partial W^{(1)}} (1).

Từ trên đã tính được \displaystyle\frac{\partial J}{\partial A^{(1)}} = (\hat{Y} - Y) * (W^{(2)})^T

Đạo hàm của hàm sigmoid \displaystyle\frac{d\sigma(x)}{dx} = \sigma(x) * (1 - \sigma(x))A^{(1)} = \sigma(Z^{(1)}), nên trong (1) có thể hiểu là \displaystyle \frac{\partial A^{(1)}}{\partial Z^{(1)}} = A^{(1)}* (1 - A^{(1)})

Cuối cùng, Z^{(1)} = X * W^{(1)} + b^{(1)} nên có thể tạm hiểu \displaystyle \frac{\partial Z^{(1)}}{\partial W^{(1)}} = X, nó giống như \displaystyle f(x) = a*x + b => \frac{df}{dx} = a vậy.

Kết hợp tất cả lại \displaystyle\frac{\partial J}{\partial W^{(1)}} = X^T * (((\hat{Y} - Y) * (W^{(2)})^T)\otimes A^{(1)}\otimes (1-A^{(1)}) )

Thế khi nào thì dùng element-wise (\otimes), khi nào dùng nhân ma trận (*) ???

  • Khi tính đạo hàm ngược lại qua bước activation thì dùng ( \otimes )
  • Khi có phép tính nhân ma trận thì dùng (*), nhưng đặc biệt chú ý đến kích thước ma trận và dùng transpose nếu cần thiết. Ví dụ: ma trận X kích thước N*3, W kích thước 3*4, Z = X * W sẽ có kích thước N*4 thì \displaystyle \frac{\partial J}{\partial W} = X^T * (\frac{\partial J}{\partial Z})\displaystyle \frac{\partial J}{\partial X} = (\frac{\partial J}{\partial Z}) * W^T

Tương tự, \displaystyle\frac{\partial L}{\partial b^{(1)}} = sum(((\hat{Y} - Y) * (W^{(2)})^T)\otimes A^{(1)})^T

Vậy là đã tính xong hết đạo hàm của loss function với các hệ số W và bias b, giờ có thể áp dụng gradient descent để giải bài toán.

Giờ thử tính \displaystyle \frac{\partial L}{\partial x_1}, ở bài này thì không cần vì chỉ có 1 hidden layer, nhưng nếu nhiều hơn 1 hidden layer thì bạn cần tính bước này để tính đạo hàm với các hệ số trước đó.

Đường màu đỏ cho w_{11}^{(1)} , đường màu xanh cho x_1

Ta thấy w_{11}^{(1)} chỉ tác động đến a_1^{(1)}, cụ thể là \displaystyle a_1^{(1)} = \sigma(b_1^{(1)} + x_1*w_{11}^{(1)} + x_2*w_{21}^{(1)})

Tuy nhiên x_1 không những tác động đến a_1^{(1)} mà còn tác động đến a_2^{(1)}, nên khi áp dụng chain rule tính đạo hàm của L với x_1 cần tính tổng đạo hàm qua cả a_1^{(1)}a_2^{(1)} .

Do đó:

\displaystyle \frac{\partial L}{\partial x_1} = \frac{\partial L}{\partial a_1^{(1)}} * \frac{\partial a_1^{(1)}}{\partial x_1} + \frac{\partial L}{\partial a_2^{(1)}} * \frac{\partial a_2^{(1)}}{\partial x_1} = w_{11}^{(1)}* a_1 ^ {(1)} * (1 - a_1^{(1)}) * w_{11}^{(2)} * (y_i - \hat{y_i}) + w_{12}^{(1)}* a_2 ^ {(1)} * (1 - a_2^{(1)}) * w_{21}^{(2)} * (y_i - \hat{y_i}) \newline

Mô hình tổng quát

Mô hình neural network

Bạn có thể xem lại biểu diễn dạng ma trận với neural network ở bài trước.

Bước 1: Tính \displaystyle \frac{\partial J}{\partial \hat{Y}}, trong đó \hat{Y} = A^{(3)}

Bước 2: Tính \displaystyle \frac{\partial J}{\partial \hat{W^{(3)}}}= (A^{(2)})^T * (\frac{\partial J}{\partial \hat{Y}} \otimes \frac{\partial A^{(3)}}{\partial Z^{(3)}}), \frac{\partial J}{\partial \hat{b^{(3)}}}= (sum( \frac{\partial J}{\partial \hat{Y}} \otimes \frac{\partial A^{(3)}}{\partial Z^{(3)}}))^T và tính \displaystyle \frac{\partial J}{\partial \hat{A^{(2)}}}= ( \frac {\partial J}{\partial \hat{Y}} \otimes \frac{\partial A^{(3)}}{\partial Z^{(3)}}) * (W^{(3)})^T

Bước 3: Tính \displaystyle \frac{\partial J}{\partial \hat{W^{(2)}}}= (A^{(1)})^T * (\frac{\partial J}{\partial A^{(2)}} \otimes \frac{\partial A^{(2)}}{\partial Z^{(2)}}), \frac{\partial J}{\partial \hat{b^{(2)}}}= (sum (\frac{\partial J}{\partial A^{(2)}} \otimes \frac{\partial A^{(2)}}{\partial Z^{(2)}}))^T và tính \displaystyle \frac{\partial J}{\partial \hat{A^{(1)}}}= ( \frac {\partial J}{\partial A^{(2)}} \otimes \frac{\partial A^{(2)}}{\partial Z^{(2)}}) * (W^{(2)})^T

Bước 4: Tính \displaystyle \frac{\partial J}{\partial \hat{W^{(1)}}}= (A^{(0)})^T * (\frac{\partial J}{\partial A^{(1)}} \otimes \frac{\partial A^{(1)}}{\partial Z^{(1)}}), \frac{\partial J}{\partial \hat{b^{(1)}}}= (sum (\frac{\partial J}{\partial A^{(1)}} \otimes \frac{\partial A^{(1)}}{\partial Z^{(1)}}))^T , trong đó \displaystyle A^{(0)} = X

Nếu network có nhiều layer hơn thì cứ tiếp tục cho đến khi tính được đạo hàm của loss function J với tất cả các hệ số W và bias b.

Nếu hàm activation là sigmoid thì \displaystyle\frac{\partial A^{(i)}}{\partial Z^{(i)}} = A^{(i)} \otimes (1-A^{(i)})

Ở bài trước quá trình feedforward

Quá trình feedforward

Thì ở bài này quá trình tính đạo hàm ngược lại

Quá trình backpropagation

Đấy là vì sao thuật toán được gọi là backpropagation (lan truyền ngược)

Python code

Code và dữ liệu mọi người lấy ở đây.

# Thêm thư viện
import numpy as np
import pandas as pd

# Hàm sigmoid
def sigmoid(x):
        return 1/(1+np.exp(-x))
 
   
# Đạo hàm hàm sigmoid
def sigmoid_derivative(x):
        return x*(1-x)


# Lớp neural network
class NeuralNetwork:
    def __init__(self, layers, alpha=0.1):
		# Mô hình layer ví dụ [2,2,1]
      self.layers = layers 
      
      # Hệ số learning rate
      self.alpha = alpha
		
      # Tham số W, b
      self.W = []
      self.b = []
      
      # Khởi tạo các tham số ở mỗi layer
      for i in range(0, len(layers)-1):
            w_ = np.random.randn(layers[i], layers[i+1])
            b_ = np.zeros((layers[i+1], 1))
            self.W.append(w_/layers[i])
            self.b.append(b_)
            
    
	# Tóm tắt mô hình neural network
    def __repr__(self):
        return "Neural network [{}]".format("-".join(str(l) for l in self.layers))
    
	
	# Train mô hình với dữ liệu
    def fit_partial(self, x, y):
        A = [x]
        
        # quá trình feedforward
        out = A[-1]
        for i in range(0, len(self.layers) - 1):
            out = sigmoid(np.dot(out, self.W[i]) + (self.b[i].T))
            A.append(out)
        
        # quá trình backpropagation
        y = y.reshape(-1, 1)
        dA = [-(y/A[-1] - (1-y)/(1-A[-1]))]
        dW = []
        db = []
        for i in reversed(range(0, len(self.layers)-1)):
            dw_ = np.dot((A[i]).T, dA[-1] * sigmoid_derivative(A[i+1]))
            db_ = (np.sum(dA[-1] * sigmoid_derivative(A[i+1]), 0)).reshape(-1,1)
            dA_ = np.dot(dA[-1] * sigmoid_derivative(A[i+1]), self.W[i].T)
            dW.append(dw_)
            db.append(db_)
            dA.append(dA_)
        
        # Đảo ngược dW, db
        dW = dW[::-1]
        db = db[::-1]
        
		# Gradient descent
        for i in range(0, len(self.layers)-1):
            self.W[i] = self.W[i] - self.alpha * dW[i]
            self.b[i] = self.b[i] - self.alpha * db[i]
      
    def fit(self, X, y, epochs=20, verbose=10):
        for epoch in range(0, epochs):
            self.fit_partial(X, y)
            if epoch % verbose == 0:
                loss = self.calculate_loss(X, y)
                print("Epoch {}, loss {}".format(epoch, loss))
    
	# Dự đoán
    def predict(self, X):
        for i in range(0, len(self.layers) - 1):
            X = sigmoid(np.dot(X, self.W[i]) + (self.b[i].T))
        return X

	# Tính loss function
    def calculate_loss(self, X, y):
        y_predict = self.predict(X)
        #return np.sum((y_predict-y)**2)/2
        return -(np.sum(y*np.log(y_predict) + (1-y)*np.log(1-y_predict)))


Deep Learning cơ bản ©2024. All Rights Reserved.
Powered by WordPress. Theme by Phoenix Web Solutions