目录
Python和ML基础
前言
手写AI推出的全新保姆级从零手写自动驾驶CV课程,链接。记录下个人学习笔记,仅供自己参考。
本次课程主要学习闭包(即返回函数的函数)、导数的相关概念以及利用导数求解sqrt(2)
课程大纲可看下面的思维导图。
1.闭包
1.1 基本概念
闭包是指函数中定义另一个函数,并且内部函数可以访问外部函数的局部变量,即使外部函数已经执行完毕,内部函数仍然可以使用外部函数的变量,这种函数就称为闭包函数。(from chatGPT)
闭包函数通常有两个特点:
- 内部函数可以访问外部函数的局部变量
- 外部函数返回内部函数
以下是一个简单的闭包函数的示例:
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
add_5 = out_function(5)
print(add_5(3)) #输出 8
在上述代码中,outer_function
返回了一个内部函数inner_function
,而且inner_function
可以访问outer_function
中的变量x
。最后,我们将outer_function
的返回值赋给了变量add_5
,这个变量表示加5的函数,当我们调用它们时,它们都会返回相应的结果。
1.2 作业
作业6:自定义一个计算函数compution,定义一个返回函数的函数timer,使用timer来修改compution,并通过timer统计compution的执行耗时
示例代码如下:
import time
# 定义计算函数
def compution(count, a, b):
sum = 0
for i in range(count):
sum += a + b
return sum
# 统计耗时
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
sum = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.3f} 秒")
return sum
return wrapper
compution = timer(compution)
# 调用计算函数,并输出结果
compution(1000000, 5, 100)
compution(2000000, 3, 200)
输出结果如下:
2.sqrt(2)
作业[ML-1]:求解 2 \sqrt{2} 2
2.1 传统方法
跟着杜老师提供的思路进行实现。
思路:
-
找一个初始值 t t t
-
评估 t t t 与 x \sqrt{x} x 之间的diff
-
问题转换为 t 2 t^2 t2 与 x x x 之间的diff
-
循环,直到diff的误差在允许的范围类
- 如果 diff > 0 说明此时的 t t t 大了,应该将 t t t 减小,但是又不能减太小
- 如果 diff < 0 说明此时的 t t t 小了,应该将 t t t 增大,但是又不能增太多
-
最后将满足条件的 t t t 返回即可
示例代码如下:
def difference(t, x): # t与sqrt(x)相差趋近0 ==> t^2 - x -> 0
return t ** 2 - x
def sqrt(x):
t = x / 2 # 初始值需要选好
diff = difference(t, x)
lr = 0.01
while abs(diff) > 1e-5:
if(diff > 0): # 说明 t 大了
t = t - lr * t
if(diff < 0): # 说明 t 小了
t = t + lr * t
diff = difference(t, x)
return t
sqrt_2 = sqrt(2)
print(f"sqrt(2) = {sqrt_2}")
运行效果如下:
2.2 梯度下降法
思考:
- 在之前的方法中,如何寻找更合适的 t t t 进行优化呢?
- 优化的方向其实没有明确,斜率其实确定了正确的方向,而某点的斜率就是函数在该点处的导数值
- 加斜率值为变化量时,函数值会逐渐变大,梯度
上升
法;减去斜率值为变化量时,函数值会逐渐变小,梯度下降
法
那么通过梯度下降法求解 x \sqrt{x} x 的步骤如下:(from algoc++)
- 第一步:转化问题,将 x \sqrt{x} x 转化为求取函数 L ( t ) = ( t 2 − x ) 2 L(t) = (t^2-x)^2 L(t)=(t2−x)2 的零点
- 第二步:寻找合适的解,找 t t t 使得满足函数 L ( t ) = 0 L(t) = 0 L(t)=0
- 第三步:找到的 t t t,就是问题的解
那么如何去寻找 t t t 使得 L ( t ) = 0 L(t) = 0 L(t)=0 就是我们需要考虑的,具体如下:
- 第一步:随机给定一个初始值 t 0 t_0 t0
- 第二步:观察, t t t 应该如何调整使得函数值减小,即 L ( t 1 ) < L ( t 0 ) L(t_1)<L(t_0) L(t1)<L(t0)
- 第三步:不断根据第二步调整,直到 L ( t n ) L(t_n) L(tn) 足够接近于0
具体如何调整呢?我们可以在 t t t 的领域内对比 L ( t − Δ t ) L(t-\Delta t) L(t−Δt)与 L ( t + Δ t ) L(t+\Delta t) L(t+Δt)的值,考虑的 t t t 的领域内因此 Δ t \Delta t Δt 是大于0的无穷小数。
若 L ( t − Δ t ) < L ( t ) L(t-\Delta t)<L(t) L(t−Δt)<L(t),则 t 1 = t − Δ t t1=t-\Delta t t1=t−Δt;若 L ( t + Δ t ) < L ( t ) L(t+\Delta t)<L(t) L(t+Δt)<L(t),则 t 1 = t + Δ t t1=t+\Delta t t1=t+Δt,可改写为
若 L ( t ) − L ( t − Δ t ) > 0 L(t)-L(t-\Delta t)>0 L(t)−L(t−Δt)>0,则 t 1 = t − Δ t t_1=t-\Delta t t1=t−Δt;若 L ( t + Δ t ) − L ( t ) < 0 L(t+\Delta t)-L(t)<0 L(t+Δt)−L(t)<0,则 t 1 = t + Δ t t_1=t+\Delta t t1=t+Δt
将其进行合并如下式:
t
1
=
t
−
L
(
t
+
Δ
t
)
−
L
(
t
)
∣
L
(
t
+
Δ
t
)
−
L
(
t
)
∣
Δ
t
(1)
t_{1}=t-\frac{L(t+\Delta t)-L(t)}{|L(t+\Delta t)-L(t)|} \Delta t \tag1
t1=t−∣L(t+Δt)−L(t)∣L(t+Δt)−L(t)Δt(1)
对式子进行变换:
t
1
=
t
−
L
(
(
t
+
Δ
t
)
−
L
(
t
)
Δ
t
∣
L
(
t
+
Δ
t
)
−
L
(
t
)
Δ
t
∣
Δ
t
=
t
−
L
(
t
+
Δ
t
)
−
L
(
t
)
Δ
t
Δ
t
∣
L
(
t
+
Δ
t
)
−
L
(
t
)
Δ
t
∣
(2)
t_{1}=t-\frac{\frac{L((t+\Delta t)-L(t)}{\Delta t}}{\left|\frac{L(t+\Delta t)-L(t)}{\Delta t}\right|} \Delta t=t-\frac{L(t+\Delta t)-L(t)}{\Delta t} \frac{\Delta t}{\left|\frac{L(t+\Delta t)-L(t)}{\Delta t}\right|} \tag2
t1=t−
ΔtL(t+Δt)−L(t)
ΔtL((t+Δt)−L(t)Δt=t−ΔtL(t+Δt)−L(t)
ΔtL(t+Δt)−L(t)
Δt(2)
t 1 = t − α L ( t + Δ t ) − L ( t ) Δ t ( α → 0 + ) (3) t_{1}=t-\alpha \frac{L(t+\Delta t)-L(t)}{\Delta t} \quad\left(\alpha \rightarrow 0^{+}\right) \tag3 t1=t−αΔtL(t+Δt)−L(t)(α→0+)(3)
t 1 = t − α L ′ ( t ) ( α → 0 + ) (4) t_1 = t - \alpha L'(t) \quad\left(\alpha \rightarrow 0^{+}\right) \tag4 t1=t−αL′(t)(α→0+)(4)
得出结论,新的 t t t 只要等于 t − α L ′ ( t ) t-\alpha L'(t) t−αL′(t) 就能确保 L L L 逐渐下降,直到逼近0为止, α \alpha α 被称为学习率或步长。
关于学习率 α \alpha α 有以下几点说明:
- 当 α \alpha α 取无穷小时,虽然一定保证下降,但效率太慢
- 日常设计的很多函数,可以使用相对大一些的学习率,比如 α = 0.01 \alpha = 0.01 α=0.01 ,原因在于虽然步长大了可能跳过合适位置,但是在下一时刻,依旧可能跳回来
- 大的学习率不能保证一定收敛,但是大部分时候是可以很好的工作
示例代码如下:
def sqrt(x):
t = x / 2 # 初始值
L = (t**2 - x)**2 # 问题转换求函数L的零点即求极小值
alpha = 0.01 # 学习率
while(L > 1e-5):
delta = 2 * (t**2 - x) * 2 * t # L函数在t处的导数
t = t - alpha * delta # 新的t
L = (t**2 - x)**2
return t
sqrt_2 = sqrt(2)
print(f"sqrt(2) = {sqrt_2}")
运行效果如下:
2.3 牛顿法
牛顿法是一种迭代法,通过不断逼近函数的零点来求解方程的根。
具体实现:
-
先转换问题,将 x \sqrt{x} x 转化为求解函数 L ( t ) = ( t 2 − x ) L(t) = (t^2-x) L(t)=(t2−x) 的零点
-
考虑 L ( t ) L(t) L(t) 在 t 0 t_0 t0 处的切线与x轴交点作为 t 1 t_1 t1,从而不断逼近函数的零点
-
如下图所示,考虑以 o 2 ( t 0 , L ( t 0 ) ) o2(t_0,L(t_0)) o2(t0,L(t0)) 为原点,则切线可表示为 k = L ′ ( t 0 ) , b = 0 k=L^{\prime}\left(t_{0}\right),b=0 k=L′(t0),b=0,而与x轴交点可表示为:
k = d ( o 2 , t 0 ) d ( t 1 , t 0 ) = L ( t 0 ) t 0 − t 1 = L ′ ( t 0 ) (1) k=\frac{d\left(o 2, t_{0}\right)}{d\left(t_{1}, t_{0}\right)}=\frac{L\left(t_{0}\right)}{t_{0}-t_{1}}=L^{\prime}\left(t_{0}\right) \tag1 k=d(t1,t0)d(o2,t0)=t0−t1L(t0)=L′(t0)(1)
t 0 − t 1 = L ( t 0 ) L ′ ( t 0 ) t 1 = t 0 − L ( t 0 ) L ′ ( t 0 ) (2) t_{0}-t_{1}=\frac{L\left(t_{0}\right)}{L^{\prime}\left(t_{0}\right)} \tag2 \\ t_{1}=t_{0}-\frac{L\left(t_{0}\right)}{L^{\prime}\left(t_{0}\right)} t0−t1=L′(t0)L(t0)t1=t0−L′(t0)L(t0)(2)
示例代码如下:
def sqrt(x):
t = x / 2 # 初始值
L = t ** 2 - x # 问题转化求L的零点
while(abs(L) > 1e-5):
dL = 2 * t # L的导数
t = t - L / dL # 新的t
L = t ** 2 - x
return t
sqrt_2 = sqrt(2)
print(f"sqrt(2) = {sqrt_2}")
运行效果如下:
3.拓展
3.1 常用函数的导数
1. ( c ) ′ = 0 ; 2. ( x α ) ′ = α x α − 1 ( α ∈ R ) ; 3. ( sin x ) ′ = cos x , ( cos x ) ′ = − sin x ; 4. ( tan ) ′ = sec 2 x , ( cot ) ′ = − csc 2 x ; 5. ( a x ) ′ = a x ln a , ( e x ) ′ = e x ; 6. ( log a x ) ′ = 1 x ln a , ( ln x ) ′ = 1 x ; 7. (arcsin x ) ′ = 1 1 − x 2 , (arccos x ) ′ = − 1 1 − x 2 ; \begin{matrix} 1.&(c)'=0;\\ \\ 2.&(x^\alpha)'=\alpha x^{\alpha-1}(\alpha\in R); \\ \\ 3.&(\sin x)'=\cos x,(\cos x)'=-\sin x;\\ \\ 4.&(\tan)'=\sec^2x,(\cot)'=-\csc^2x; \\ \\ 5.&(a^x)'=a^x\ln a,(e^x)'=e^x;\\ \\ 6.&(\log_a x)'=\dfrac1{x\ln a},(\ln x)'=\dfrac1x; \\ \\ 7.&\text{(arcsin}x)'=\frac{1}{\sqrt{1-x^2}},\text{(arccos}x)'=-\frac1{\sqrt{1-x}^2}; \end{matrix} 1.2.3.4.5.6.7.(c)′=0;(xα)′=αxα−1(α∈R);(sinx)′=cosx,(cosx)′=−sinx;(tan)′=sec2x,(cot)′=−csc2x;(ax)′=axlna,(ex)′=ex;(logax)′=xlna1,(lnx)′=x1;(arcsinx)′=1−x21,(arccosx)′=−1−x21;
3.2 链式法则
链式法则(chain rule)是微积分中的一个重要概念,它用于求一个复合函数的导数。对于一个由多个函数组合而成的函数,它的导数可以通过链式法则来计算。具体来说,若函数
h
(
x
)
=
f
(
u
)
h(x)=f(u)
h(x)=f(u)和
u
=
g
(
x
)
u=g(x)
u=g(x)都可导,则复合函数
f
(
g
(
x
)
)
f(g(x))
f(g(x))也可导,通过链式法则有(拉格朗日符号):(from wiki)
h
′
(
x
)
=
f
′
(
g
(
x
)
)
g
′
(
x
)
.
h'(x)=f'(g(x))g'(x).
h′(x)=f′(g(x))g′(x).
链式法则也可以用莱布尼茨的符号来表达。如果一个变量
z
z
z 取决于变量
y
y
y,而
y
y
y 本身又依赖于变量
x
x
x (也就是说,
y
y
y 和
z
z
z 是因变量),那么
z
z
z 也会通过中间变量
y
y
y 依赖于
x
x
x。在这种情形下,链式法则可表达为:
d
z
d
x
=
d
z
d
y
⋅
d
y
d
x
\dfrac{dz}{dx}=\dfrac{dz}{dy}\cdot\dfrac{dy}{dx}
dxdz=dydz⋅dxdy
换句话说,链式法则指出,复合函数的导数等于外层函数在内层函数的导数的基础上再乘以内层函数的导数。
链式法则在微积分中有着广泛的应用,尤其是在求解一些复杂函数的导数时,可以通过链式法则将复杂函数分解为若干个简单函数的组合,从而更加便于求解。
3.3 作业
作业(补充):求解 y = ( 3 x 2 − 5 ) 2 y=(3x^2-5)^2 y=(3x2−5)2的导数
通过链式法则进行求解
-
第一步:令 u = 3 x 2 − 5 u=3x^2-5 u=3x2−5,那么 y = u 2 y=u^2 y=u2
-
第二步:计算 d y d x = d y d u ⋅ d u d x \dfrac{dy}{dx}=\dfrac{dy}{du}\cdot\dfrac{du}{dx} dxdy=dudy⋅dxdu
-
第三步:通过常用函数的导数计算 d y d u = 2 u \dfrac{dy}{du}=2u dudy=2u, d u d x = 6 x \dfrac{du}{dx}=6x dxdu=6x
-
第四步:得出结果 d y d x = d y d u ⋅ d u d x = 2 u ⋅ 6 x = 2 ( 3 x 2 − 5 ) ⋅ 6 x = 36 x 3 − 60 x \dfrac{dy}{dx}=\dfrac{dy}{du}\cdot\dfrac{du}{dx}=2u\cdot6x=2(3x^2-5)\cdot6x=36x^3-60x dxdy=dudy⋅dxdu=2u⋅6x=2(3x2−5)⋅6x=36x3−60x
总结
本次课程学习了python中闭包的概念,以及通过求解 2 \sqrt{2} 2 了解了梯度下降法对后续的学习打下基础,同时复习了导数的相关概念,重温了一遍常用函数的求导以及链式法则。