.. raw:: html
.. raw:: html
.. raw:: html
.. _sec_calculus:
Giải tích
=========
.. raw:: html
Tìm diện tích của một đa giác vẫn là một bí ẩn cho tới ít nhất
:math:`2.500` năm trước, khi người Hy Lạp cổ đại chia đa giác thành các
tam giác và cộng diện tích của chúng lại. Để tìm diện tích của các hình
cong, như hình tròn, người Hy Lạp cổ đại đặt các đa giác nội tiếp bên
trong các hình cong đó. Như trong :numref:`fig_circle_area`, một đa
giác nội tiếp với càng nhiều cạnh bằng nhau thì càng xấp xỉ đúng hình
tròn. Quy trình này còn được biết đến như *phương pháp vét kiệt*.
.. raw:: html
.. _fig_circle_area:
.. figure:: ../img/polygon_circle.svg
Tìm diện tích hình tròn bằng phương pháp vét kiệt.
.. raw:: html
Phương pháp vét kiệt chính là khởi nguồn của *giải tích tích phân* (sẽ
được miêu tả trong :numref:`sec_integral_calculus`). Hơn :math:`2.000`
năm sau, nhánh còn lại của giải tích, *giải tích vi phân*, ra đời. Trong
những ứng dụng quan trọng nhất của giải tích vi phân, các bài toán tối
ưu hoá sẽ tìm *cách tốt nhất* để thực hiện một công việc nào đó. Như đã
bàn đến trong :numref:`subsec_norms_and_objectives`, các bài toán như
vậy vô cùng phổ biến trong học sâu.
.. raw:: html
Trong học sâu, chúng ta *huấn luyện* các mô hình, cập nhật chúng liên
tục để chúng ngày càng tốt hơn khi học với nhiều dữ liệu hơn. Thông
thường, trở nên tốt hơn tương đương với cực tiểu hoá một *hàm mất mát*,
một điểm số sẽ trả lời câu hỏi “mô hình của ta đang *tệ* tới mức nào?”
Câu hỏi này lắt léo hơn ta tưởng nhiều. Mục đích cuối cùng mà ta muốn là
mô hình sẽ hoạt động tốt trên dữ liệu mà nó chưa từng nhìn thấy. Nhưng
chúng ta chỉ có thể khớp mô hình trên dữ liệu mà ta đang có thể thấy. Do
đó ta có thể chia việc huấn luyện mô hình thành hai vấn đề chính: i)
*tối ưu hoá*: quy trình huấn luyện mô hình trên dữ liệu đã thấy. ii)
*tổng quát hoá*: dựa trên các nguyên tắc toán học và sự uyên thâm của
người huấn luyện để tạo ra các mô hình mà tính hiệu quả của nó vượt ra
khỏi tập dữ liệu huấn luyện.
.. raw:: html
Để giúp bạn hiểu các bài toán tối ưu hóa và các phương pháp tối ưu hóa
trong các chương sau, ở đây chúng tôi sẽ cung cấp một chương ngắn vỡ
lòng về các kĩ thuật giải tích vi phân thông dụng trong học sâu.
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Đạo hàm và Vi phân
------------------
.. raw:: html
Chúng ta bắt đầu bằng việc đề cập tới khái niệm đạo hàm, một bước quan
trọng của hầu hết các thuật toán tối ưu trong học sâu. Trong học sâu, ta
thường chọn những hàm mất mát khả vi theo các tham số của mô hình. Nói
đơn giản, với mỗi tham số, ta có thể xác định hàm mất mát tăng hoặc giảm
nhanh như thế nào khi tham số đó *tăng* hoặc *giảm* chỉ một lượng cực
nhỏ.
.. raw:: html
Giả sử ta có một hàm :math:`f: \mathbb{R} \rightarrow \mathbb{R}` có đầu
vào và đầu ra đều là số vô hướng. *Đạo hàm* của :math:`f` được định
nghĩa là
.. math:: f'(x) = \lim_{h \rightarrow 0} \frac{f(x+h) - f(x)}{h},
:label: eq_derivative
.. raw:: html
nếu giới hạn này tồn tại. Nếu :math:`f'(a)` tồn tại, :math:`f` được gọi
là *khả vi* (*differentiable*) tại :math:`a`. Nếu :math:`f` khả vi tại
mọi điểm trong một khoảng, thì hàm này được gọi là khả vi trong khoảng
đó. Ta có thể giải nghĩa đạo hàm :math:`f'(x)` trong
:eq:`eq_derivative` như là tốc độ thay đổi *tức thời* của hàm
:math:`f` theo biến :math:`x`. Cái gọi là tốc độ thay đổi tức thời được
dựa trên độ biến thiên :math:`h` trong :math:`x` khi :math:`h` tiến về
:math:`0`.
.. raw:: html
Để minh họa cho khái niệm đạo hàm, hãy thử với một ví dụ. Định nghĩa
:math:`u = f(x) = 3x^2-4x`.
.. code:: python
%matplotlib inline
from d2l import mxnet as d2l
from IPython import display
from mxnet import np, npx
npx.set_np()
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
Cho :math:`x=1` và :math:`h` tiến về :math:`0`, kết quả của phương trình
:math:`\frac{f(x+h) - f(x)}{h}` trong :eqref:``eq_derivative`` tiến về
:math:`2`. Dù thử nghiệm này không phải là một dạng chứng minh toán học,
lát nữa ta sẽ thấy rằng quả thật đạo hàm của :math:`u'` là :math:`2` khi
:math:`x=1`.
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print('h=%.5f, numerical limit=%.5f' % (h, numerical_lim(f, 1, h)))
h *= 0.1
.. parsed-literal::
:class: output
h=0.10000, numerical limit=2.30000
h=0.01000, numerical limit=2.03000
h=0.00100, numerical limit=2.00300
h=0.00010, numerical limit=2.00030
h=0.00001, numerical limit=2.00003
.. raw:: html
.. raw:: html
.. raw:: html
Hãy làm quen với một vài ký hiệu cùng được dùng để biểu diễn đạo hàm.
Cho :math:`y = f(x)` với :math:`x` và :math:`y` lần lượt là biến độc lập
và biến phụ thuộc của hàm :math:`f`. Những biểu diễn sau đây là tương
đương nhau:
.. math:: f'(x) = y' = \frac{dy}{dx} = \frac{df}{dx} = \frac{d}{dx} f(x) = Df(x) = D_x f(x),
.. raw:: html
với ký hiệu :math:`\frac{d}{dx}` và :math:`D` là các *toán tử vi phân*
(*differentiation operator*) để chỉ các phép toán *vi phân*. Ta có thể
sử dụng các quy tắc lấy đạo hàm của các hàm thông dụng sau đây:
.. raw:: html
- :math:`DC = 0` (:math:`C` là một hằng số),
- :math:`Dx^n = nx^{n-1}` (*quy tắc lũy thừa*, :math:`n` là số thực bất
kỳ),
- :math:`De^x = e^x`,
- :math:`D\ln(x) = 1/x.`
.. raw:: html
Để lấy đạo hàm của một hàm được tạo từ vài hàm đơn giản hơn, ví dụ như
từ những hàm thông dụng ở trên, có thể dùng các quy tắc hữu dụng dưới
đây. Giả sử hàm :math:`f` và :math:`g` đều khả vi và :math:`C` là một
hằng số, ta có *quy tắc nhân hằng số*
.. math:: \frac{d}{dx} [Cf(x)] = C \frac{d}{dx} f(x),
.. raw:: html
*quy tắc tổng*
.. math:: \frac{d}{dx} [f(x) + g(x)] = \frac{d}{dx} f(x) + \frac{d}{dx} g(x),
.. raw:: html
*quy tắc nhân*
.. math:: \frac{d}{dx} [f(x)g(x)] = f(x) \frac{d}{dx} [g(x)] + g(x) \frac{d}{dx} [f(x)],
.. raw:: html
và *quy tắc đạo hàm phân thức*
.. math:: \frac{d}{dx} \left[\frac{f(x)}{g(x)}\right] = \frac{g(x) \frac{d}{dx} [f(x)] - f(x) \frac{d}{dx} [g(x)]}{[g(x)]^2}.
.. raw:: html
Bây giờ ta có thể áp dụng các quy tắc ở trên để tìm đạo hàm
:math:`u' = f'(x) = 3 \frac{d}{dx} x^2-4\frac{d}{dx}x = 6x-4`. Vậy nên,
với :math:`x = 1`, ta có :math:`u' = 2`: điều này đã được kiểm chứng với
thử nghiệm lúc trước ở mục này khi kết quả có được cũng tiến tới
:math:`2`. Giá trị đạo hàm này cũng đồng thời là độ dốc của đường tiếp
tuyến với đường cong :math:`u = f(x)` tại :math:`x = 1`.
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Để minh họa cách hiểu này của đạo hàm, ta sẽ dùng ``matplotlib``, một
thư viện vẽ biểu đồ thông dụng trong Python. Ta cần định nghĩa một số
hàm để cấu hình thuộc tính của các biểu đồ được tạo ra bởi
``matplotlib``. Trong đoạn mã sau, hàm ``use_svg_display`` chỉ định
``matplotlib`` tạo các biểu đồ ở dạng svg để có được chất lượng ảnh sắc
nét hơn.
.. code:: python
# Saved in the d2l package for later use
def use_svg_display():
"""Use the svg format to display a plot in Jupyter."""
display.set_matplotlib_formats('svg')
.. raw:: html
Ta định nghĩa hàm ``set_figsize`` để chỉ định kích thước của biểu đồ.
Lưu ý rằng ở đây ta đang dùng trực tiếp ``d2l.plt`` do câu lệnh
``from matplotlib import pyplot as plt`` đã được đánh dấu để lưu vào gói
``d2l`` trong phần Lời nói đầu.
.. code:: python
# Saved in the d2l package for later use
def set_figsize(figsize=(3.5, 2.5)):
"""Set the figure size for matplotlib."""
use_svg_display()
d2l.plt.rcParams['figure.figsize'] = figsize
.. raw:: html
Hàm ``set_axes`` sau cấu hình thuộc tính của các trục biểu đồ tạo bởi
``matplotlib``.
.. code:: python
# Saved in the d2l package for later use
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
"""Set the axes for matplotlib."""
axes.set_xlabel(xlabel)
axes.set_ylabel(ylabel)
axes.set_xscale(xscale)
axes.set_yscale(yscale)
axes.set_xlim(xlim)
axes.set_ylim(ylim)
if legend:
axes.legend(legend)
axes.grid()
.. raw:: html
Với ba hàm cấu hình biểu đồ trên, ta định nghĩa hàm ``plot`` để vẽ nhiều
đồ thị một cách nhanh chóng vì ta sẽ cần minh họa khá nhiều đồ thị xuyên
suốt cuốn sách.
.. code:: python
# Saved in the d2l package for later use
def plot(X, Y=None, xlabel=None, ylabel=None, legend=[], xlim=None,
ylim=None, xscale='linear', yscale='linear',
fmts=['-', 'm--', 'g-.', 'r:'], figsize=(3.5, 2.5), axes=None):
"""Plot data points."""
d2l.set_figsize(figsize)
axes = axes if axes else d2l.plt.gca()
# Return True if X (ndarray or list) has 1 axis
def has_one_axis(X):
return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
and not hasattr(X[0], "__len__"))
if has_one_axis(X):
X = [X]
if Y is None:
X, Y = [[]] * len(X), X
elif has_one_axis(Y):
Y = [Y]
if len(X) != len(Y):
X = X * len(Y)
axes.cla()
for x, y, fmt in zip(X, Y, fmts):
if len(x):
axes.plot(x, y, fmt)
else:
axes.plot(y, fmt)
set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
.. raw:: html
Giờ ta có thể vẽ đồ thị của hàm số :math:`u = f(x)` và đường tiếp tuyến
của nó :math:`y = 2x - 3` tại :math:`x=1`, với hệ số :math:`2` là độ dốc
của tiếp tuyến.
.. code:: python
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
.. figure:: output_calculus_vn_509ba6_13_0.svg
.. raw:: html
.. raw:: html
.. raw:: html
Đạo hàm riêng
-------------
.. raw:: html
Cho tới giờ, ta đã làm việc với đạo hàm của các hàm một biến. Trong học
sâu, các hàm lại thường phụ thuộc vào *nhiều* biến. Do đó, ta cần mở
rộng ý tưởng của đạo hàm cho các hàm *nhiều biến* đó.
.. raw:: html
Cho :math:`y = f(x_1, x_2, \ldots, x_n)` là một hàm với :math:`n` biến.
*Đạo hàm riêng* của :math:`y` theo tham số thứ :math:`i`, :math:`x_i`,
là
.. math:: \frac{\partial y}{\partial x_i} = \lim_{h \rightarrow 0} \frac{f(x_1, \ldots, x_{i-1}, x_i+h, x_{i+1}, \ldots, x_n) - f(x_1, \ldots, x_i, \ldots, x_n)}{h}.
.. raw:: html
Để tính :math:`\frac{\partial y}{\partial x_i}`, ta chỉ cần coi
:math:`x_1, \ldots, x_{i-1}, x_{i+1}, \ldots, x_n` là các hằng số và
tính đạo hàm của :math:`y` theo :math:`x_i`. Để biểu diễn đạo hàm riêng,
các ký hiệu sau đây đều có ý nghĩa tương đương:
.. math:: \frac{\partial y}{\partial x_i} = \frac{\partial f}{\partial x_i} = f_{x_i} = f_i = D_i f = D_{x_i} f.
.. raw:: html
.. raw:: html
.. raw:: html
Gradient
--------
.. raw:: html
Chúng ta có thể ghép các đạo hàm riêng của mọi biến trong một hàm nhiều
biến để thu được vector *gradient* của hàm số đó. Giả sử rằng đầu vào
của hàm :math:`f: \mathbb{R}^n \rightarrow \mathbb{R}` là một vector
:math:`n` chiều :math:`\mathbf{x} = [x_1, x_2, \ldots, x_n]^\top` và đầu
ra là một số vô hướng. Gradient của hàm :math:`f(\mathbf{x})` theo
:math:`\mathbf{x}` là một vector gồm :math:`n` đạo hàm riêng đó:
.. math:: \nabla_{\mathbf{x}} f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_n}\bigg]^\top.
.. raw:: html
Biểu thức :math:`\nabla_{\mathbf{x}} f(\mathbf{x})` thường được viết gọn
thành :math:`\nabla f(\mathbf{x})` trong trường hợp không sợ nhầm lẫn.
.. raw:: html
Cho :math:`\mathbf{x}` là một vector :math:`n`-chiều, các quy tắc sau
thường được dùng khi tính vi phân hàm đa biến:
.. raw:: html
- Với mọi :math:`\mathbf{A} \in \mathbb{R}^{m \times n}`,
:math:`\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x} = \mathbf{A}^\top`,
- Với mọi :math:`\mathbf{A} \in \mathbb{R}^{n \times m}`,
:math:`\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} = \mathbf{A}`,
- Với mọi :math:`\mathbf{A} \in \mathbb{R}^{n \times n}`,
:math:`\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} \mathbf{x} = (\mathbf{A} + \mathbf{A}^\top)\mathbf{x}`,
- :math:`\nabla_{\mathbf{x}} \|\mathbf{x} \|^2 = \nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{x} = 2\mathbf{x}`.
.. raw:: html
Tương tự, với bất kỳ ma trận :math:`\mathbf{X}` nào, ta đều có
:math:`\nabla_{\mathbf{X}} \|\mathbf{X} \|_F^2 = 2\mathbf{X}`. Sau này
ta sẽ thấy, gradient sẽ rất hữu ích khi thiết kế thuật toán tối ưu trong
học sâu.
.. raw:: html
.. raw:: html
.. raw:: html
.. raw:: html
Quy tắc dây chuyền
------------------
.. raw:: html
Tuy nhiên, những gradient như thế có thể khó để tính toán. Đó là bởi vì
các hàm nhiều biến trong học sâu đa phần là những *hàm hợp*, nên ta
không thể áp dụng các quy tắc đề cập ở trên để lấy vi phân cho những hàm
này. May mắn thay, *quy tắc dây chuyền* cho phép chúng ta lấy vi phân
của các hàm hợp.
.. raw:: html
Trước tiên, chúng ta hãy xem xét các hàm một biến. Giả sử hai hàm
:math:`y=f(u)` và :math:`u=g(x)` đều khả vi, quy tắc dây chuyền được mô
tả như sau
.. math:: \frac{dy}{dx} = \frac{dy}{du} \frac{du}{dx}.
.. raw:: html
Giờ ta sẽ xét trường hợp tổng quát hơn đối với các hàm nhiều biến. Giả
sử một hàm khả vi :math:`y` có các biến số
:math:`u_1, u_2, \ldots, u_m`, trong đó mỗi biến :math:`u_i` là một hàm
khả vi của các biến :math:`x_1, x_2, \ldots, x_n`. Lưu ý rằng :math:`y`
cũng là hàm của các biến :math:`x_1, x_2, \ldots, x_n`. Quy tắc dây
chuyền cho ta
.. math:: \frac{dy}{dx_i} = \frac{dy}{du_1} \frac{du_1}{dx_i} + \frac{dy}{du_2} \frac{du_2}{dx_i} + \cdots + \frac{dy}{du_m} \frac{du_m}{dx_i}
.. raw:: html
cho mỗi :math:`i = 1, 2, \ldots, n`.
.. raw:: html
Tóm tắt
-------
.. raw:: html
- Vi phân và tích phân là hai nhánh con của giải tích, trong đó vi phân
được ứng dụng rộng rãi trong các bài toán tối ưu hóa của học sâu.
- Đạo hàm có thể được hiểu như là tốc độ thay đổi tức thì của một hàm
số đối với các biến số. Nó cũng là độ dốc của đường tiếp tuyến với
đường cong của hàm.
- Gradient là một vector có các phần tử là đạo hàm riêng của một hàm
nhiều biến theo tất cả các biến số của nó.
- Quy tắc dây chuyền cho phép chúng ta lấy vi phân của các hàm hợp.
.. raw:: html
Bài tập
-------
.. raw:: html
*dịch đoạn phía trên* 1. Vẽ đồ thị của hàm số
:math:`y = f(x) = x^3 - \frac{1}{x}` và đường tiếp tuyến của nó tại
:math:`x = 1`. 1. Tìm gradient của hàm số
:math:`f(\mathbf{x}) = 3x_1^2 + 5e^{x_2}`. 1. Gradient của hàm
:math:`f(\mathbf{x}) = \|\mathbf{x}\|_2` là gì? 1. Có thể dùng quy tắc
dây chuyền cho trường hợp sau đây không: :math:`u = f(x, y, z)`, với
:math:`x = x(a, b)`, :math:`y = y(a, b)` và :math:`z = z(a, b)`?
.. raw:: html
.. raw:: html
.. raw:: html
Thảo luận
---------
- `Tiếng Anh `__
- `Tiếng Việt `__
Những người thực hiện
---------------------
Bản dịch trong trang này được thực hiện bởi:
- Đoàn Võ Duy Thanh
- Lê Khắc Hồng Phúc
- Phạm Hồng Vinh
- Vũ Hữu Tiệp
- Nguyễn Cảnh Thướng
- Phạm Minh Đức
- Tạ H. Duy Nguyên