# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # 模拟梯度下降法

# ## 梯度下降法的封装和实现

import numpy as np
import matplotlib.pyplot as plt

plot_x = np.linspace(-1, 6, 141) # [-1, 6]等间距取141个点
plot_x

plot_y = (plot_x - 2.5)**2 - 1 # 损失函数J

plt.plot(plot_x, plot_y)
plt.show()


# theta对应的导数
def dJ(theta):
    return 2*(theta - 2.5)
    


# theta对应的损失函数
def J(theta):
    try:
        return (theta - 2.5)**2 - 1
    except:
        return float("inf") # 防止梯度下降法结果收敛不了


theta = 0.0
eta = 0.1 # 学习率η
epsilon = 1e-8 # 接近0的定义

# +
while True:
    gradient = dJ(theta) # 获取导数变换值
    last_theta = theta 
    theta = theta - eta * gradient # eta即η, theta即θ(指的是特征系数的取值，一个一维数组，不断变化使得J(θ)最小)
    
    # 比较差值是否越来越小.当变换接近于最小精度时，已经可以认为到达导数为0的点了
    if(abs(J(theta) - J(last_theta)) < epsilon):
        break
        
print(theta)
print(J(theta))

# +
theta = 0.0
theta_history = [theta]
while True:
    gradient = dJ(theta) # 求导
    last_theta = theta 
    theta = theta - eta * gradient # -η*dJ
    # 把每次的theta存储到数组中，方便下面进行绘图
    theta_history.append(theta)
    
    # 比较损失函数的差值是否越来越小.当变化接近于最小精度时，已经可以认为到达导数为0的点了
    if(abs(J(theta) - J(last_theta)) < epsilon):
        break
        
plt.plot(plot_x, J(plot_x)) # 画出整个曲线
plt.plot(np.array(theta_history), J(np.array(theta_history)), color = 'r', marker = '+') # 画出所有梯度下降法经过的点
plt.show()
# -

len(theta_history) # 执行了46次，得到了导数为0的点


def gradient_descent(initial_theta,eta,epsilon=1e-8):
    """
    梯度下降法的封装
    """
    theta = initial_theta
    theta_history = [initial_theta]
    while True:
        gradient = dJ(theta)
        last_theta = theta 
        theta = theta - eta * gradient
        # 把每次的theta存储到数组中，方便下面进行绘图
        theta_history.append(theta)
        # print(theta)
        # 比较差值是否越来越小.当变换接近于最小经度时，已经可以人为到达导数为0的点了
        if(abs(J(theta) - J(last_theta)) < epsilon):
            print("累计查找的次数："+str(len(theta_history)))
            plot_theta_history(theta_history)
            break


def plot_theta_history(theta_history):
    plt.plot(plot_x, J(plot_x))
    plt.plot(np.array(theta_history), J(np.array(theta_history)), color = 'r', marker = '+')
    plt.show()


# ## 下面分别对不同的η(即eta)进行测试，看下不同的η对收敛速度和收敛性的影响
# > 一般η初始取0.01比较好

eta = 0.8 # 分别自己试试0.1、0.01、0.001；0.8、、
gradient_descent(0.,eta)

eta = 0.1 # 分别自己试试0.1、0.01、0.001；0.8、、
gradient_descent(0.,eta)

eta = 0.01 # 分别自己试试0.1、0.01、0.001；0.8、、
gradient_descent(0.,eta)

eta = 0.001 # 分别自己试试0.1、0.01、0.001；0.8、、
gradient_descent(0.,eta)

eta = 1.1 # 分别自己试试0.1、0.01、0.001；0.8；1.1的时候越蹦越高，收敛不了了
gradient_descent(0.,eta)


def gradient_descent(initial_theta,eta,epsilon=1e-8,max_iters=10000): # max_iters:最大循环次数，防止无法首先时陷入死循环
    theta = initial_theta
    theta_history = [initial_theta]
    for i in range(0, max_iters): # 只梯度下降指定的次数，这样就不会出现上面死循环的情况了
        gradient = dJ(theta)
        last_theta = theta 
        theta = theta - eta * gradient
        # 把每次的theta存储到数组中，方便下面进行绘图
        theta_history.append(theta)
        # 比较差值是否越来越小.当变换接近于最小经度时，已经可以人为到达导数为0的点了
        if(abs(J(theta) - J(last_theta)) < epsilon):
            print("累计查找的次数："+str(len(theta_history)))
            plot_theta_history(theta_history)
            break
        if i==max_iters-1: # 一旦到达最大梯度下降的次数就直接绘图，不再继续梯度下降下去了
            plot_theta_history(theta_history)


eta = 1.1 # 分别自己试试0.1、0.01、0.001；0.8；1.1的时候越蹦越高，收敛不了了,所以不会打印出图,这里指定迭代的最大次数
gradient_descent(0.,eta,max_iters=100)

eta = 1.1 # 分别自己试试0.1、0.01、0.001；0.8；1.1的时候越蹦越高，收敛不了了,所以不会打印出图，这里指定迭代的最大次数
gradient_descent(0.,eta,max_iters=10) # 梯度下降次数少地时候还是可以看到最开始的损失函数J曲线地

StarcoderdataJupyterScriptsDedupFiltered