数据科学导论——数学基础之优化

第1关:模型优化基础

任务描述
相关知识
什么是模型优化?
常见的模型优化方法
编程要求
测试说明
任务描述
模型优化在一些复杂的训练模型中是不可避免的,本关主要是让大家对模型优化的方法有个基本的了解。

本关任务:根据相关知识,完成右侧选择题任务。

相关知识
我们每个人都会在我们的生活或者工作中遇到各种各样的最优化问题,比如每个企业和个人都要考虑的一个问题“在一定成本下,如何使利润最大化”等。

最优化方法是一种数学方法,它是研究在给定约束之下如何寻求某些因素(的量),以使某一(或某些)指标达到最优的一些学科的总称。

随着学习的深入,我们会发现学习和工作中遇到的大多问题都可以建模成一种最优化模型进行求解,比如我们现在学习的机器学习算法,大部分的机器学习算法的本质都是建立优化模型,通过最优化方法对目标函数(或损失函数)进行优化,从而训练出最好的模型。

什么是模型优化?
模型优化问题:为某种特定的情形寻找最佳模型。举个例子,给定数据集上的模型残差最小时,即为“最佳”。下面是对模型优化的介绍视频:

课程视频《模型优化》

实际应用举例:用多项式函数建模一台家用电器的功耗和运转时间之间的关系。

(1) 使用一次函数建模:y=kx+d

(2) 使用二次函数建模:y=px
2
+qx+t,实际上,二次函数可能是y=x
2
+2x+3或者y=2x
2
+5x+1等等。

(3) 使用三次函数建模:y=ax
3
+bx
2
+cx+t

假设我们选择三次函数建模,那么怎么判定哪一个是最优化模型呢?我们通常采用函数残差作为评价标准。也就是说在y=wx
3
+px
2
+qx+t中哪个函数(哪组参数 [w,p,q,t] )最好?这就是最优化问题,确定了 wpqt 之后,我们就确定了最终的模型。

常见的模型优化方法
常见的最优化方法有:

梯度下降法;
梯度下降法的优化思想是用当前位置负梯度方向作为搜索方向,因为该方向为当前位置的最快下降方向,所以也被称为是“最速下降法“最速下降法越接近目标值,步长越小,前进越慢。

牛顿法和拟牛顿法;
牛顿法是一种在实数域和复数域上近似求解方程的方法,拟牛顿法的本质思想是改善牛顿法每次需要求解复杂的 Hessian 矩阵的逆矩阵的缺陷,它使用正定矩阵来近似 Hessian 矩阵的逆,从而简化了运算的复杂度。

共轭梯度法;
共轭梯度法是介于最速下降法与牛顿法之间的一个方法,它仅需利用一阶导数信息,但克服了最速下降法收敛慢的缺点,又避免了牛顿法需要存储和计算海塞矩阵并求逆的缺点,共轭梯度法不仅是解决大型线性方程组最有用的方法之一,也是解大型非线性最优化最有效的算法之一。

启发式优化方法;
启发式方法指人在解决问题时所采取的一种根据经验规则进行发现的方法。其特点是在解决问题时,利用过去的经验,选择已经行之有效的方法,而不是系统地,以确定的步骤去寻求答案。启发式优化方法种类繁多,包括经典的模拟退火方法,遗传算法,蚁群算法以及粒子群算法等等。

解决约束优化问题 - 拉格朗日乘数法。
作为一种优化算法,拉格朗日乘子法主要用于解决约束优化问题,它的基本思想就是通过引入拉格朗日乘子来将含有 n 个变量和 k 个约束条件的约束优化问题转化为含有 (n+k) 个变量的无约束优化问题。拉格朗日乘子背后的数学意义是其为约束方程梯度线性组合中每个向量的系数。

在下面的关卡中,我们会详细介绍使用最为频繁的梯度下降法。

编程要求
根据相关知识,按照要求完成右侧选择题任务。

测试说明
平台会对你选择的答案进行判断,全对则通过测试。

开始你的任务吧,祝你成功!

BCD  A

第2关:梯度下降

任务描述
相关知识
梯度下降的思想
估算梯度
选择正确步长
随机梯度下降法
编程要求
测试说明
任务描述
本关任务:使用梯度下降拟合输入数据 x,y 。

相关知识
从事数据科学工作时,常常会面临这样的需要:为某种特定的情形寻找最佳模型。“最佳”常常会被解读为某种类似于“最小化模型残差”或者“最大化数据的可能性”。换句话说,它代表了优化某种问题的解决方案。

这意味着我们需要解决一连串的最优化问题。我们采用的方法是一种叫作梯度下降( gradient descent )的技术,下面是对梯度下降的介绍视频:

课程视频《梯度下降》

梯度下降的思想
假设我们拥有某个函数 f ,这个函数输入一个实数向量,输出一个实数。一个简单的例子如下:

def sum_of_squares(v):
return sum(v_i ** 2 for v_i in v)
我们常常需要最大化(或最小化)这个函数。这意味着我们需要找出能计算出最大(或最小)可能值的输入 v。对我们的函数来说,梯度给出了输入值的方向,在这个方向上,函数增长得最快。

相应地,最大化函数的算法首先从一个随机初始点开始,计算梯度,在梯度方向上跨越一小步,再从一个新的初始点开始重复这个过程。同样,你也可以在相反方向上逐步最小化函数,如下图所示:

需要注意,如果一个函数有一个全局最小点,那么梯度下降很可能会找到它。但是函数有多个(局部)最小点,那么梯度下降可能找不到这个点,但是可以通过多尝试一些初始点来重复运行。如果一个函数没有最小点,计算也许会陷入死循环。

估算梯度
如果 f 是单变量函数,那么它在 x 点的导数衡量了当 x 发生变化时,f(x) 变化了多少。导数通过差商的极限来定义:

def difference_quotient(f, x, h):
return (f(x + h) - f(x)) / h

其中 h 趋近于 0 。如上图所示:导数就是在点 (x, f (x)) 的切线的斜率,而差商就是通过点 (x, f (x)) 和点 (x+h, f (x+h)) 的割线的斜率。当 h 越来越小,割线与切线就越来越接近。

很多函数可以精确地计算导数,比如平方函数 square :

def square(x):#计算平方
return x * x
它的导数为:

def derivative(x):
return 2 * x
python 中无法直接运算极限,但可以通过计算一个很小的变动 e 的差商来估算微分。

import matplotlib.pyplot as plt
from functools import partial
plt.rcParams[‘font.sans-serif’]=[‘simhei’]
plt.rcParams[‘font.family’]=‘sans-serif’
plt.rcParams[‘axes.unicode_minus’]=False #画图可中文
derivative_estimate = partial(difference_quotient, square, h=0.00001)

绘出导入matplotlib.pyplot作为plt的基本相同的形态

x = range(-10,10)
plt.title(“精确的导数值与估计值”)
plt.plot(x, list(map(derivative, x)), ‘rx’, label=‘Actual’) # 用 x 表示
plt.plot(x, list(map(derivative_estimate, x)), ‘b+’, label=‘Estimate’) # 用 + 表示
plt.legend(loc=9)
plt.show()

当 f 是一个多变量函数时,它有多个偏导数,每个偏导数表示仅有一个输入变量发生微小变化时函数 f 的变化。我们把导数看成是其第 i 个变量的函数,其他变量保持不变,以此来计算它第 i 个偏导数:

def partial_difference_quotient(f, v, i, h):
w = [v_j + (h if j == i else 0) # 只对v的第i个元素增加h
for j, v_j in enumerate(v)]
return (f(w) - f(v)) / h
再以同样的方法估算它的梯度函数:

def estimate_gradient(f, v, h=0.00001):
return [partial_difference_quotient(f, v, i, h)
for i, _ in enumerate(v)]
选择正确步长
尽管向梯度的反向移动的逻辑已经清楚了,但移动多少还不明了。事实上,选择合适的步长更像艺术而非科学。主流的选择方法有:

使用固定步长;

随时间增长逐步减小步长;

在每一步中通过最小化目标函数的值来选择合适的步长。

随机梯度下降法
通常而言,我们有一些 target_fn 函数,需要对其进行最小化,也有梯度函数 gradient_fn 。比如,函数 target_fn 可能代表模型的残差,它是参数的函数。我们可能需要找到能使残差尽可能小的参数。此外,假设我们(以某种方式)为参数 theta_0 设定了某个初始值,那么可以如下使用批量梯度下降:

def minimize_batch(target_fn, gradient_fn, theta_0, tolerance=0.000001):
step_sizes = [100, 10, 1, 0.1, 0.01, 0.001, 0.0001, 0.00001]
theta = theta_0 # 设定theta为初始值
target_fn = safe(target_fn) # target_fn的安全版
value = target_fn(theta) # 我们试图最小化的值
while True:
gradient = gradient_fn(theta)
next_thetas = [step(theta, gradient, -step_size)
for step_size in step_sizes]

选择一个使残差函数最小的值

next_theta = min(next_thetas, key=target_fn)
next_value = target_fn(next_theta)

当“收敛”时停止

if abs(value - next_value) < tolerance:
return theta
else:
theta, value = next_theta, next_value
有时候,我们需要最大化某个函数,这只需要最小化这个函数的负值(相应的梯度函数也需取负):

def negate(f):
return lambda *args, **kwargs: -f(*args, **kwargs)
def negate_all(f):
return lambda *args, **kwargs: [-y for y in f(*args, **kwargs)]
def maximize_batch(target_fn, gradient_fn, theta_0, tolerance=0.000001):
return minimize_batch(negate(target_fn),negate_all(gradient_fn),theta_0,tolerance)
在每一步梯度计算中,都会搜索整个数据集,这使每一步都会耗费很长的时间。现在,这些残差函数常常具有可加性,意味着整个数据集上的预测残差恰好是每个数据点的预测残差之和。

在这种情形下,我们转而使用一种称为随机梯度下降的技术,它每次仅计算一个点的梯度,这个计算会反复循环,直到达到一个停止点。在每个循环中,我们都会在整个数据集上按照一个随机序列迭代:

def in_random_order(data):
indexes = [i for i, _ in enumerate(data)] # 生成索引列表
random.shuffle(indexes) # 随机打乱数据
for i in indexes: # 返回序列中的数据
yield data[i]
我们对每个数据点都会进行一步梯度计算。这种方法留有这样一种可能性,即也许会在最小值附近一直循环下去,所以,每当停止获得改进,我们都会减小步长并最终退出:

def minimize_stochastic(target_fn, gradient_fn, x, y, theta_0, alpha_0=0.01):
data = zip(x, y)
theta = theta_0 # 初始值猜测
alpha = alpha_0 # 初始步长
min_theta, min_value = None, float(“inf”) # 迄今为止的最小值
iterations_with_no_improvement = 0

如果循环超过100次仍无改进,停止

while iterations_with_no_improvement < 100:
    value = sum( target_fn(x_i, y_i, theta) for x_i, y_i in data )
    if value < min_value:
        # 如果找到新的最小值,记住它
        # 并返回到最初的步长
        min_theta, min_value = theta, value
        iterations_with_no_improvement = 0
        alpha = alpha_0
    else:
    # 尝试缩小步长,否则没有改进
        iterations_with_no_improvement += 1
        alpha *= 0.9

在每个数据点上向梯度方向前进一步

    for x_i, y_i in in_random_order(data):
        gradient_i = gradient_fn(x_i, y_i, theta)
        theta = vector_subt\fract(theta, scalar_multiply(alpha, gradient_i))

return min_theta
随机化通常比批处理化快很多。当然,我们也希望获得最大化的结果:

def maximize_stochastic(target_fn, gradient_fn, x, y, theta_0, alpha_0=0.01):
return minimize_stochastic(negate(target_fn),negate_all(gradient_fn),x, y, theta_0, alpha_0)
编程要求
请仔细阅读右侧代码,结合相关知识,在 Begin-End 区域内进行代码补充,根据输入数据 x,y 使用批梯度下降计算出 a,b 的值。公式为:

y=a∗x+b

测试说明
平台会对你编写的代码进行测试,为了评判准确请将结果四舍五入转换为整数:

测试输入:

[5,9,12,35,21,16,7,33]
[25, 37, 46, 115, 73, 58, 31, 109]

预期输出:

3
10

开始你的任务吧,祝你成功!

def student(x, y):
    epsilon = 0.00001  # 迭代阀值,当两次迭代损失函数之差小于该阀值时停止迭代
    alpha = 0.01  # 学习率
    a = 0
    b = 0
    m = len(x)
    error0 = 0
    error1 = 0
    # ********* Begin *********#
    while True:
        # 参数迭代计算
        for i in range(m):
            # 计算残差
            diff1 = ((a * x[i] + b) - y[i]) * x[i] * (2 / m)
            diff2 = ((a * x[i] + b) - y[i]) * (2 / m)
            a -= alpha * diff1
            b -= alpha * diff2
        # 计算损失函数
        error1 = 0
        for lp in range(len(x)):
            error1 += (y[lp] - (a * x[lp] + b))
        if abs(error1 - error0) < epsilon:
            print(int(round(a)))
            print(int(round(b)))
            break
        else:
            error0 = error1
        # ********* End *********#
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据可视化是数据科学领域中重要的一环,它通过图表、图形和其他可视化工具将数据呈现出来,以便能够更好地理解和分析数据。 首先,数据可视化可以帮助我们更好地理解数据。通过将数据可视化成图表或图像,我们可以直观地看到数据的分布、趋势和模式。这些可视化结果可以让我们更容易地发现数据中的有意义的信息,并且帮助我们深入理解数据所传达的含义。 其次,数据可视化还可以帮助我们发现数据中的异常值。通过对数据进行可视化,我们可以快速地发现与众不同的数据点或异常模式。这些异常值可能是我们的数据收集过程中的错误,或者是数据背后隐藏的重要信息。通过识别和处理这些异常值,我们可以提高数据质量和分析的准确性。 此外,数据可视化也有助于数据的传达和沟通。在现实世界中,不同背景的人对于数据的理解能力和统计分析的能力有所差异。数据可视化可以帮助我们以更直观的方式向其他人传达数据,使得他们更容易理解和接受数据的分析结果。通过合适的图表和图形,我们可以有效地解释数据背后的趋势和关系,并与其他人进行更有意义的讨论和决策。 综上所述,数据可视化在数据科学中起着重要的作用。它不仅帮助我们更好地理解和分析数据,发现异常值,还能够向他人有效传达和沟通数据。因此,在进行数据科学工作时,数据可视化是必不可少的一项技能和工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值