.. _sec_calculus:
Calculus (Giải tích)
====================
Tìm diện tích của một đa giác vẫn còn bí ẩn cho đến ít nhất 2.500 năm
trước, khi người Hy Lạp cổ đại chia một đa giác thành tam giác và tổng
hợp các khu vực của chúng. Để tìm khu vực của các hình dạng cong, chẳng
hạn như một vòng tròn, người Hy Lạp cổ đại được ghi đa giác trong các
hình dạng như vậy. Như thể hiện trong :numref:`fig_circle_area`, một
đa giác được ghi với nhiều cạnh có chiều dài bằng nhau tốt hơn gần đúng
vòng tròn. Quá trình này còn được gọi là *phương pháp kiệt thúc*.
.. _fig_circle_area:
.. figure:: ../img/polygon-circle.svg
Find the area of a circle with the method of exhaustion.
Trên thực tế, phương pháp kiệt sức là nơi \* tích phân tích\* (sẽ được
mô tả trong :numref:`sec_integral_calculus`) bắt nguồn từ. Hơn 2.000
năm sau, nhánh khác của giải tích, *vi phân tích*, đã được phát minh.
Trong số các ứng dụng quan trọng nhất của phép tính vi phân, các bài
toán tối ưu hóa xem xét cách làm một cái gì đó \* tốt nhất\*. Như đã
thảo luận trong :numref:`subsec_norms_and_objectives`, những vấn đề
như vậy là phổ biến trong học sâu.
Trong học sâu, chúng tôi \* đào tạo\* mô hình, cập nhật chúng liên tiếp
để chúng trở nên tốt hơn và tốt hơn khi họ thấy ngày càng nhiều dữ liệu.
Thông thường, nhận được tốt hơn có nghĩa là giảm thiểu chức năng *mất *,
một điểm số trả lời câu hỏi “làm thế nào \* xấu\* là mô hình của chúng
tôi?” Câu hỏi này tinh tế hơn nó xuất hiện. Cuối cùng, những gì chúng
tôi thực sự quan tâm là tạo ra một mô hình hoạt động tốt trên dữ liệu mà
chúng tôi chưa từng thấy trước đây. Nhưng chúng ta chỉ có thể phù hợp
với mô hình để dữ liệu mà chúng ta thực sự có thể thấy. Do đó, chúng ta
có thể phân hủy nhiệm vụ lắp các mô hình thành hai mối quan tâm chính:
(i) *tối ưu hóa*: quá trình lắp các mô hình của chúng tôi với dữ liệu
quan sát; (ii) *khái quát hóa*: các nguyên tắc toán học và trí tuệ của
các học viên hướng dẫn về cách tạo ra các mô hình có giá trị vượt quá bộ
dữ liệu chính xác examples ví dụ used to train đào tạo them.
Để giúp bạn hiểu các vấn đề và phương pháp tối ưu hóa trong các chương
sau, ở đây chúng tôi đưa ra một mồi rất ngắn gọn về phép tính vi phân
thường được sử dụng trong deep learning.
Các dẫn xuất và sự khác biệt
----------------------------
Chúng tôi bắt đầu bằng cách giải quyết việc tính toán các dẫn xuất, một
bước quan trọng trong gần như tất cả các thuật toán tối ưu hóa học tập
sâu. Trong deep learning, chúng ta thường chọn các chức năng mất mát có
thể khác biệt đối với các thông số của mô hình của chúng tôi. Nói một
cách đơn giản, điều này có nghĩa là đối với mỗi tham số, chúng ta có thể
xác định mức độ tổn thất sẽ tăng hoặc giảm nhanh như thế nào, chúng tôi
\* tăng\* hoặc \* giảm tham số đó bằng một lượng nhỏ vô hạn.
Giả sử rằng chúng ta có một hàm
:math:`f: \mathbb{R} \rightarrow \mathbb{R}`, có đầu vào và đầu ra là cả
vô hướng. *phái sinh* 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
if this limitgiới hạn exists tồn tại. Nếu :math:`f'(a)` tồn tại,
:math:`f` được cho là *khác biệt* tại :math:`a`. Nếu :math:`f` có thể
khác biệt ở mọi số của một khoảng thời gian, thì chức năng này có thể
phân biệt trong khoảng thời gian này. Chúng ta có thể giải thích đạo hàm
:math:`f'(x)` trong :eq:`eq_derivative` là tỷ lệ thay đổi tức
thường\* của :math:`f(x)` đối với :math:`x`. Cái gọi là tỷ lệ thay đổi
tức thời dựa trên sự thay đổi :math:`h` trong :math:`x`, tiếp cận
:math:`0`.
Để minh họa các dẫn xuất, chúng ta hãy thử nghiệm với một ví dụ. Define
:math:`u = f(x) = 3x^2-4x`.
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
from matplotlib_inline import backend_inline
from mxnet import np, npx
from d2l import mxnet as d2l
npx.set_np()
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import numpy as np
from matplotlib_inline import backend_inline
from d2l import torch as d2l
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import numpy as np
from matplotlib_inline import backend_inline
from d2l import tensorflow as d2l
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
.. raw:: html
Bằng cách thiết lập :math:`x=1` và để :math:`h` tiếp cận :math:`0`, kết
quả số của :math:`\frac{f(x+h) - f(x)}{h}` trong
:eq:`eq_derivative` cách tiếp cận :math:`2`. Mặc dù thí nghiệm này
không phải là một bằng chứng toán học, chúng ta sẽ thấy sau đó đạo hàm
:math:`u'` là :math:`2` khi :math:`2`.
.. raw:: html
.. raw:: html
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
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
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
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
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
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
Chúng ta hãy làm quen với một vài ký hiệu tương đương cho các dẫn xuất.
Cho :math:`y = f(x)`, trong đó :math:`x` và :math:`y` là biến độc lập và
biến phụ thuộc của hàm :math:`f`, tương ứng. Các biểu thức sau là tương
đương:
.. math:: f'(x) = y' = \frac{dy}{dx} = \frac{df}{dx} = \frac{d}{dx} f(x) = Df(x) = D_x f(x),
trong đó các ký hiệu :math:`\frac{d}{dx}` và :math:`D` là \* toán tử
khác biệt\* cho biết hoạt động của *sự khác biệt*. Chúng ta có thể sử
dụng các quy tắc sau để phân biệt các hàm chung:
- :math:`DC = 0` (:math:`C` là một hằng số),
- :math:`Dx^n = nx^{n-1}` (quy tắc điện\*, :math:`n` là bất kỳ số thực
nào),
- :math:`De^x = e^x`,
- :math:`D\ln(x) = 1/x.`
Để phân biệt một hàm được hình thành từ một vài chức năng đơn giản hơn
như các chức năng phổ biến trên, các quy tắc sau đây có thể hữu ích cho
chúng ta. Giả sử rằng các chức năng :math:`f` và :math:`g` đều có thể
phân biệt và :math:`C` là một hằng số, chúng ta có quy tắc nhiều \* hằng
số\*
.. math:: \frac{d}{dx} [Cf(x)] = C \frac{d}{dx} f(x),
quy tắc \* sum\*
.. math:: \frac{d}{dx} [f(x) + g(x)] = \frac{d}{dx} f(x) + \frac{d}{dx} g(x),
quy tắc *sản phẩm*
.. math:: \frac{d}{dx} [f(x)g(x)] = f(x) \frac{d}{dx} [g(x)] + g(x) \frac{d}{dx} [f(x)],
và quy tắc *thương lượng*
.. 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}.
Bây giờ chúng ta có thể áp dụng một vài trong số các quy tắc trên để tìm
:math:`u' = f'(x) = 3 \frac{d}{dx} x^2-4\frac{d}{dx}x = 6x-4`. Do đó,
bằng cách đặt :math:`x = 1`, chúng tôi có :math:`u' = 2`: điều này được
hỗ trợ bởi thí nghiệm trước đó của chúng tôi trong phần này, nơi kết quả
số tiếp cận :math:`2`. Đạo hàm này cũng là độ dốc của đường tiếp tuyến
với đường cong :math:`u = f(x)` khi :math:`x = 1`.
Để hình dung một cách giải thích các dẫn xuất như vậy, chúng ta sẽ sử
dụng ``matplotlib``, một thư viện vẽ phổ biến trong Python. Để cấu hình
các thuộc tính của các số liệu được tạo ra bởi ``matplotlib``, chúng ta
cần xác định một vài chức năng. Sau đây, hàm ``use_svg_display`` chỉ
định gói ``matplotlib`` để xuất các số liệu svg cho hình ảnh sắc nét
hơn. Lưu ý rằng nhận xét ``# @save``\ là một dấu đặc biệt trong đó hàm,
lớp hoặc câu lệnh sau được lưu trong gói ``d2l`` để sau này chúng có thể
được gọi trực tiếp (ví dụ, ``d2l.use_svg_display()``) mà không được định
nghĩa lại.
.. code:: python
def use_svg_display(): #@save
"""Use the svg format to display a plot in Jupyter."""
backend_inline.set_matplotlib_formats('svg')
Chúng tôi xác định hàm ``set_figsize`` để chỉ định kích thước hình. Lưu
ý rằng ở đây chúng tôi trực tiếp sử dụng ``d2l.plt`` vì lệnh nhập khẩu
``from matplotlib import pyplot as plt`` đã được đánh dấu là được lưu
trong gói ``d2l`` trong lời nói đầu.
.. code:: python
def set_figsize(figsize=(3.5, 2.5)): #@save
"""Set the figure size for matplotlib."""
use_svg_display()
d2l.plt.rcParams['figure.figsize'] = figsize
Chức năng ``set_axes`` sau đây đặt các thuộc tính của trục của các con
số được tạo ra bởi ``matplotlib``.
.. code:: python
#@save
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()
Với ba chức năng này cho cấu hình hình, chúng tôi xác định hàm ``plot``
để vẽ nhiều đường cong một cách ngắn gọn vì chúng ta sẽ cần hình dung
nhiều đường cong trong suốt cuốn sách.
.. code:: python
#@save
def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,
ylim=None, xscale='linear', yscale='linear',
fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):
"""Plot data points."""
if legend is None:
legend = []
set_figsize(figsize)
axes = axes if axes else d2l.plt.gca()
# Return True if `X` (tensor 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)
Bây giờ chúng ta có thể vẽ hàm :math:`u = f(x)` và đường tiếp tuyến của
nó :math:`y = 2x - 3` tại :math:`x=1`, trong đó hệ số :math:`2` là độ
dốc của đường tiếp tuyến.
.. raw:: html
.. raw:: html
.. 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_7e7694_35_0.svg
.. raw:: html
.. raw:: html
.. 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_7e7694_38_0.svg
.. raw:: html
.. raw:: html
.. 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_7e7694_41_0.svg
.. raw:: html
.. raw:: html
Phái sinh một phần
------------------
Cho đến nay chúng ta đã xử lý sự khác biệt của các chức năng của chỉ một
biến. Trong deep learning, các hàm thường phụ thuộc vào biến \* nhiều *.
Do đó, chúng ta cần mở rộng các ý tưởng khác biệt cho các chức năng * đa
lộ\* này.
Hãy để :math:`y = f(x_1, x_2, \ldots, x_n)` là một hàm với :math:`n`
biến. Phái sinh một phần\* của :math:`y` đối với tham số
:math:`i^\mathrm{th}` :math:`x_i` của nó 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}.
Để tính toán :math:`\frac{\partial y}{\partial x_i}`, chúng ta chỉ có
thể coi :math:`x_1, \ldots, x_{i-1}, x_{i+1}, \ldots, x_n` là hằng số và
tính toán đạo hàm của :math:`y` đối với :math:`x_i`. Đối với ký hiệu của
các dẫn xuất từng phần, sau đây là 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.
.. _subsec_calculus-grad:
Độ dốc
------
Chúng ta có thể nối các dẫn xuất từng phần của một hàm đa biến đối với
tất cả các biến của nó để có được vectơ *gradient* của hàm. 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à vô hướng. Gradient của hàm :math:`f(\mathbf{x})` đối với
:math:`\mathbf{x}` là một vectơ của :math:`n` dẫn xuất một phần:
.. 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,
trong đó :math:`\nabla_{\mathbf{x}} f(\mathbf{x})` thường được thay thế
bằng :math:`\nabla f(\mathbf{x})` khi không có sự mơ hồ.
Để :math:`\mathbf{x}` là một vector :math:`n` chiều, các quy tắc sau
thường được sử dụng khi phân biệt các chức năng đa biến:
- For all :math:`\mathbf{A} \in \mathbb{R}^{m \times n}`,
:math:`\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x} = \mathbf{A}^\top`,
- For all :math:`\mathbf{A} \in \mathbb{R}^{n \times m}`,
:math:`\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} = \mathbf{A}`,
- For all :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}`.
Tương tự, đối với bất kỳ ma trận :math:`\mathbf{X}`, chúng tôi có
:math:`\nabla_{\mathbf{X}} \|\mathbf{X} \|_F^2 = 2\mathbf{X}`. Như chúng
ta sẽ thấy sau, gradient rất hữu ích cho việc thiết kế các thuật toán
tối ưu hóa trong deep learning.
Quy tắc chuỗi
-------------
Tuy nhiên, độ dốc như vậy có thể khó tìm. Điều này là do các chức năng
đa biến trong học sâu thường là \* composite\ *, vì vậy chúng tôi có thể
không áp dụng bất kỳ quy tắc nào nói trên để phân biệt các chức năng
này. May mắn thay, quy tắc chuỗi * cho phép chúng tôi phân biệt các chức
năng tổng hợp.
Trước tiên chúng ta hãy xem xét các chức năng của một biến duy nhất. Giả
sử rằng các chức năng :math:`y=f(u)` và :math:`u=g(x)` đều có thể khác
biệt, thì quy tắc chuỗi nói rằng
.. math:: \frac{dy}{dx} = \frac{dy}{du} \frac{du}{dx}.
Bây giờ chúng ta hãy chuyển sự chú ý của chúng tôi sang một kịch bản
chung hơn trong đó các chức năng có một số tùy ý của biến. Giả sử rằng
hàm phân biệt :math:`y` có các biến :math:`u_1, u_2, \ldots, u_m`, trong
đó mỗi hàm phân biệt :math:`u_i` có các biến
:math:`x_1, x_2, \ldots, x_n`. Lưu ý rằng :math:`y` là một hàm của
:math:`x_1, x_2, \ldots, x_n`. Sau đó, quy tắc chuỗi cho
.. 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}
for any :math:`i = 1, 2, \ldots, n`.
Tóm tắt
-------
- Giải tích phân và giải tích phân là hai nhánh của giải tích, nơi mà
trước đây có thể được áp dụng cho các bài toán tối ưu hóa phổ biến
trong học sâu.
- Một đạo hàm có thể được hiểu là tốc độ thay đổi tức thời của một hàm
đối với biến của nó. 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 vectơ có thành phần là các dẫn xuất từng phần của một
hàm đa biến đối với tất cả các biến của nó.
- Quy tắc chuỗi cho phép chúng ta phân biệt các hàm tổng hợp.
Bài tập
-------
1. Vẽ chức năng :math:`y = f(x) = x^3 - \frac{1}{x}` và đường tiếp tuyến
của nó khi :math:`x = 1`.
2. Tìm gradient của hàm :math:`f(\mathbf{x}) = 3x_1^2 + 5e^{x_2}`.
3. Gradient của hàm :math:`f(\mathbf{x}) = \|\mathbf{x}\|_2` là gì?
4. Bạn có thể viết ra quy tắc chuỗi cho trường hợp
:math:`u = f(x, y, z)` và :math:`x = x(a, b)`, :math:`y = y(a, b)` và
:math:`z = z(a, b)` không?
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html