[Nhập môn Machine Learning] Bài 5: Gradient Descent
Ở bài trước chúng ta đã nói đến Cost Function. Bây giờ, ta cần một thuật toán giúp máy tính có thể tìm điểm nhỏ nhất của $J(\theta)$. Vậy nên ở bài này chúng ta sẽ tìm hiểu một trong những thuật toán phổ biến trong Machine Learning chính là Gradient Descent.
Đạo hàm
Ở cấp 3, ta đã biết đến khái niệm đạo hàm. Về cơ bản, đó chính là lượng y sẽ thay đổi theo khi ta thay đổi x (hay còn được biết đến là $\dfrac{dy}{dx}$ ). Một trong những đặc điểm của đạo hàm (hay gradient) là ta có thể coi chúng như một vector và vector này luôn chỉ về hướng ta cần di chuyển x để giá trị của y tăng lên.
Gradient Descent
Ý tưởng chính của Gradient Descent chính là nếu ta ở một điểm bất kỳ và tính đạo hàm của điểm đó. Sau đó, đi ngược lại hướng của vector đạo hàm mà ta đã có được sẽ đưa ta tới vị trí mà tại đó giá trị của y đạt cực tiểu.
Trong Cost Function, ta có $J(\theta)$ là một hàm theo $\theta$. Ta biết rằng Cost Function này luôn có một cực tiểu là giá trị nhỏ nhất của toàn hàm. Bằng cách khởi tạo các giá trị ngẫu nhiên cho các $\theta$ sau đó liên tục di chuyển ngược chiều vector gradient, ta có thể dần dần điều chỉnh các $\theta$ về trọng số tối ưu mà chẳng cần lập phương trình nào cả.
Không chỉ giúp điều chỉnh $\theta$, Gradient Descent còn đảm bảo rằng các trọng số của ta sẽ dừng lại khi đã tới vị trí tối ưu. Nguyên nhân là do càng gần các cực tiểu, đạo hàm càng nhỏ và bằng 0 tại cực tiểu.
Cài đặt Gradient Descent (của hàm một biến)
Thuật toán Gradient Descent bao gồm các trình tự sau:
1. Khởi đầu bằng cách gán cho $\theta$ các giá trị ngẫu nhiên.
2. Tính đạo hàm $\dfrac{d}{d\theta}J(\theta)$ tại vị trí hiện tại.
3. Cập nhập $\theta$ mới bằng cách trừ cho đạo hàm.
\[\theta := \theta - \dfrac{d}{d\theta}J(\theta)\]
4. Lặp lại từ bước 2 cho đến khi đạo hàm tính được bằng 0 hoặc một giá trị cực nhỏ.
Gradient Descent cho Linear Regression
Để minh họa, tôi sẽ lấy lại ví dụ bài trước.
Chúng ta sẽ giải quyết bài toán Linear Regression bằng Gradient Descent. Nhưng đầu tiên, ta cần phải tính đạo hàm Cost Function trước đã. Tôi đã làm việc đó ở bài trước nhưng tôi sẽ tổng quát hóa hơn trong lần này.
\[ \dfrac{d}{d\theta}J(\theta) = \dfrac{1}{m}\sum^{m}_{i=1}( h_\theta(x^{(i)}) - y^{(i)} )x^{(i)}\]
Ở trên chính là công thức đạo hàm cho bài toán Linear Regression. Ta sẽ sử dụng công thức này để nâng cấp các trọng số của ta. Việc tiếp theo là viết code, sẽ rất tuyệt nếu bạn biết một chút Python nhưng nếu bạn có chút kinh nghiệm trong lập trình thì tôi nghĩ đoạn code dưới cũng không quá khó hiểu với bạn
Có lẽ bạn sẽ chỉ cần chú ý tới hàm gradient_descent để thấy được cách cài đặt của thuật toán này. Tôi muốn đề cập tới một số chi tiết kỹ thuật cụ thể như sau:
- Khi chúng ta sử dụng Gradient Descent, đôi khi trọng số của cuả chúng ta chỉ luẩn quẩn quanh vị trí tối ưu với giá trị đạo hàm rất nhỏ chứ không phải 0. Thật ra, đây là một kết quả chấp nhận được vì đến chính ta cũng chỉ có thể tính toán được xấp xỉ của $\theta$ tối ưu. Việc kiểm tra xem giá trị đạo hàm đã thấp hơn một ngưỡng nhất định rồi dừng sẽ giúp ta tiết kiệm được nhiều thời gian hơn.
- Chúng ta cũng không cần giá trị trọng số $\theta$ phải chính xác đến từng chữ số trong số thực. Nó không mang lại sự khác biệt lớn so với chỉ cần chính xác một hoặc hai số sau dấu phẩy.
- alpha là gì? Bạn sẽ để ý rằng tôi đã thêm một biến mới alpha để nhân với gradient trước khi cập nhật $\theta$. Điều này là cần thiết để chống overshooting mà tôi sẽ giải thích ở bài tiếp theo.
Cuối cùng, đây là kết quả mà tôi có được sau khi chạy chương trình:
- Khi chúng ta sử dụng Gradient Descent, đôi khi trọng số của cuả chúng ta chỉ luẩn quẩn quanh vị trí tối ưu với giá trị đạo hàm rất nhỏ chứ không phải 0. Thật ra, đây là một kết quả chấp nhận được vì đến chính ta cũng chỉ có thể tính toán được xấp xỉ của $\theta$ tối ưu. Việc kiểm tra xem giá trị đạo hàm đã thấp hơn một ngưỡng nhất định rồi dừng sẽ giúp ta tiết kiệm được nhiều thời gian hơn.
- Chúng ta cũng không cần giá trị trọng số $\theta$ phải chính xác đến từng chữ số trong số thực. Nó không mang lại sự khác biệt lớn so với chỉ cần chính xác một hoặc hai số sau dấu phẩy.
- alpha là gì? Bạn sẽ để ý rằng tôi đã thêm một biến mới alpha để nhân với gradient trước khi cập nhật $\theta$. Điều này là cần thiết để chống overshooting mà tôi sẽ giải thích ở bài tiếp theo.
Cuối cùng, đây là kết quả mà tôi có được sau khi chạy chương trình:
Tôi cần khoảng 7 lần nâng cấp để hoàn thành Gradient Descent. Số lần này có thể khác nhau sau mỗi lần chạy vì $\theta$ của ta được gán ngẫu nhiên lúc ban đầu. Bạn hãy để ý rằng điều thú vị rằng đạo hàm của chúng ta đổi dấu liên tục và $\theta$ dao động xung quang vị trí tối ưu trước khi đến gần nó hơn và dừng lại. Và chúng ta có thể hình dung rõ hơn điều này với hình vẽ bên dưới.
Nếu ta lập đồ thị của gradient theo những lần cập nhật, ta sẽ thấy:
Nếu ta lập đồ thị của gradient theo những lần cập nhật, ta sẽ thấy:
Chúng ta có thể thấy rằng sau mỗi lần, gradient của ta giảm dần về 0.
Bạn có thể copy đoạn code ở trên và chạy lại trên máy mình để thử nghiệm (nhớ là bạn cần Python và matplotlib để chạy trước trình). Hoặc chạy online trên Google Colab nếu bạn không muốn tốn thời gian với việc cài đặt.
Chúng ta sẽ nói nhiều hơn về Gradient Descent trong bài tiếp theo.
Bạn có thể copy đoạn code ở trên và chạy lại trên máy mình để thử nghiệm (nhớ là bạn cần Python và matplotlib để chạy trước trình). Hoặc chạy online trên Google Colab nếu bạn không muốn tốn thời gian với việc cài đặt.
Chúng ta sẽ nói nhiều hơn về Gradient Descent trong bài tiếp theo.
Nhận xét
Đăng nhận xét