1. 机器学习优化七日速成课

在机器学习实践中,优化算法扮演着至关重要的角色。无论是调整超参数还是选择特征子集,决策树算法寻找最佳分割点,神经网络优化权重参数,我们都在使用各种计算算法进行优化。

1.1 为什么需要专门学习优化?

许多刚入门的机器学习从业者常把模型视为"黑箱",虽然知道可以调节各种参数,却不清楚如何系统性地寻找最优解。与传统编程不同,机器学习中的最优迭代次数、最佳超参数等往往没有显而易见的规则可循。

优化(或称函数优化)在数学上指寻找函数最大值或最小值的过程。在机器学习中,我们可以将模型参数作为输入,模型算法和数据集作为常量,评估指标作为输出,这样就构建了一个可优化的函数。

1.2 优化在机器学习中的应用场景

  1. 超参数调优 :如决策树的深度、神经网络的学习率等
  2. 特征选择 :从原始特征中找出最有价值的子集
  3. 模型训练 :如神经网络中的权重优化
  4. 集成学习 :确定各基学习器的最佳权重组合

2. 优化基础与网格搜索

2.1 网格搜索原理

网格搜索是最直观的优化方法,特别适合低维问题。其基本思想是在预设的参数范围内,均匀地采样各参数组合,然后逐一评估,最终找到最优解。

以二维函数f(x,y)=x²+y²为例,假设x和y的范围都是[-5,5],步长为0.1,我们需要评估(5-(-5))/0.1=100个x值,同样数量的y值,总共100×100=10,000个点。

from numpy import arange, inf

def objective(x, y):
    return x**2.0 + y**2.0

# 定义输入范围
r_min, r_max = -5.0, 5.0
step = 0.1

# 生成网格样本
sample = []
for x in arange(r_min, r_max+step, step):
    for y in arange(r_min, r_max+step, step):
        sample.append([x,y])

# 评估样本
best_eval = inf
best_x, best_y = None, None
for x,y in sample:
    eval = objective(x,y)
    if eval < best_eval:
        best_x, best_y, best_eval = x, y, eval

print(f'Best: f({best_x:.5f},{best_y:.5f}) = {best_eval:.5f}')

2.2 网格搜索的优缺点

优点

  • 实现简单直观
  • 可以找到全局最优(在给定的搜索范围内)
  • 不需要计算导数

缺点

  • 维度灾难:参数数量增加时计算量呈指数增长
  • 步长选择困难:步长太大可能错过最优解,太小则计算成本高

提示:对于高维问题,建议使用更高效的优化算法,或者先使用大步长网格搜索定位大致范围,再在小范围内进行精细搜索。

3. SciPy中的优化算法

SciPy库提供了多种优化算法,适合不同特性的函数优化问题。

3.1 Nelder-Mead算法

Nelder-Mead(又称下山单纯形法)是一种不需要导数的优化方法,适合解决低维非线性优化问题。

from scipy.optimize import minimize
from numpy.random import rand

def objective(x):
    return x[0]**2.0 + x[1]**2.0

# 定义范围并随机生成初始点
r_min, r_max = -5.0, 5.0
pt = r_min + rand(2) * (r_max - r_min)

# 执行优化
result = minimize(objective, pt, method='nelder-mead')

print(f'Status: {result["message"]}')
print(f'Evaluations: {result["nfev"]}')
print(f'Solution: f({result["x"]}) = {result["fun"]:.5f}')

3.2 算法选择建议

  1. 凸函数 :BFGS或L-BFGS-B
  2. 非凸函数 :模拟退火或遗传算法
  3. 有约束条件 :SLSQP或trust-constr
  4. 高维问题 :L-BFGS-B或CG

4. 基于梯度的优化方法

4.1 BFGS算法

BFGS是一种拟牛顿法,通过近似Hessian矩阵来加速收敛,适合光滑的凸函数优化。

def derivative(x):
    return [x[0]*2, x[1]*2]

result = minimize(objective, pt, method='BFGS', jac=derivative)

4.2 L-BFGS-B算法

L-BFGS-B是BFGS的内存高效版本,支持边界约束。

result = minimize(objective, pt, method='L-BFGS-B', jac=derivative, 
                 bounds=[(-5,5), (-5,5)])

4.3 梯度下降实现

梯度下降是神经网络训练的基础算法,核心思想是沿着梯度反方向更新参数。

def gradient_descent(objective, derivative, bounds, n_iter, step_size):
    solution = bounds[:,0] + rand(len(bounds)) * (bounds[:,1]-bounds[:,0])
    
    for i in range(n_iter):
        gradient = derivative(solution)
        solution -= step_size * gradient
        print(f'>{i} f({solution}) = {objective(solution):.5f}')
    
    return solution

bounds = asarray([[-5.0,5.0], [-5.0,5.0]])
solution = gradient_descent(objective, derivative, bounds, 40, 0.1)

5. 无梯度优化方法

5.1 爬山算法

爬山算法是一种简单的局部搜索方法,适合解决低维优化问题。

def hillclimbing(objective, bounds, n_iterations, step_size):
    # 生成初始解
    solution = bounds[:,0] + rand(len(bounds)) * (bounds[:,1]-bounds[:,0])
    solution_eval = objective(solution)
    
    for i in range(n_iterations):
        # 生成候选解
        candidate = solution + randn(len(bounds)) * step_size
        candidate_eval = objective(candidate)
        
        # 接受更好的解
        if candidate_eval < solution_eval:
            solution, solution_eval = candidate, candidate_eval
            print(f'>{i} f({solution}) = {solution_eval:.5f}')
    
    return [solution, solution_eval]

5.2 模拟退火算法

模拟退火通过引入"温度"概念来避免陷入局部最优,适合非凸函数优化。

def simulated_annealing(objective, bounds, n_iterations, step_size, temp):
    best = bounds[:,0] + rand(len(bounds)) * (bounds[:,1]-bounds[:,0])
    best_eval = objective(best)
    curr, curr_eval = best, best_eval
    
    for i in range(n_iterations):
        candidate = curr + randn(len(bounds)) * step_size
        candidate_eval = objective(candidate)
        
        if candidate_eval < best_eval:
            best, best_eval = candidate, candidate_eval
        
        diff = candidate_eval - curr_eval
        t = temp / float(i + 1)
        metropolis = exp(-diff / t)
        
        if diff < 0 or rand() < metropolis:
            curr, curr_eval = candidate, candidate_eval
    
    return [best, best_eval]

6. 优化算法性能比较

6.1 测试函数选择

  1. 凸函数 :f(x,y) = x² + y²
  2. 非凸函数 :Ackley函数
    def ackley(v):
        x, y = v
        return (-20.0 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) - 
                exp(0.5 * (cos(2*pi*x) + cos(2*pi*y))) + e + 20)
    

6.2 实验结果对比

算法 凸函数收敛速度 非凸函数找到的解质量 内存需求 适用维度
网格搜索 全局最优 低(<5)
Nelder-Mead 中等 可能陷入局部最优 低(<10)
BFGS 可能陷入局部最优 中等 中等
L-BFGS-B 可能陷入局部最优
爬山算法 中等 可能陷入局部最优 低(<20)
模拟退火 较好 中等

7. 优化实践建议

  1. 了解你的问题 :首先分析目标函数的性质(凸性、连续性、可导性等)
  2. 从小规模开始 :先用少量数据或简化版本测试优化算法
  3. 监控进度 :记录每次迭代的目标函数值,绘制收敛曲线
  4. 超参数调优 :优化算法本身也可能有需要调整的参数
  5. 组合策略 :可以先使用全局搜索定位大致区域,再用局部搜索精细优化

在机器学习项目中,优化算法的选择往往取决于问题的规模和性质。对于超参数优化,网格搜索和随机搜索仍然很流行;对于神经网络训练,梯度下降及其变种(如Adam)是标准选择;对于复杂的非凸问题,可能需要考虑进化算法等更高级的方法。

优化是机器学习的核心,掌握各种优化算法不仅能帮助你更好地训练模型,还能加深对机器学习本质的理解。建议从简单的例子开始,逐步尝试更复杂的优化问题,积累实践经验。

更多推荐