【机器学习】拟合一元二次方程

多项式回归实现

实验目的

基于单变量线性回归模型实现拟合一个多项式函数

\[y = ax^2 + bx + c \]

需要实现的函数

  • compute_model_output : 计算 \(y = ax^2 + bx + c\) 的值
  • compute_cost: 计算一组 a,b 的均方误差
  • compute_gradient : 计算偏导数
  • gradient_descent : 梯度下降算法
  • 绘制图像

数学公式推导

数学模型:

\[f_{a,b,c}(x) = ax^2 + bx +c \tag{1} \]

损失函数

与单源线性回归相同,采用均方误差来作为损失函数

\[J(a,b,c) = \frac{1}{2m} \sum_{i=0}^{m-1}(f_{a,b,c}(x^{(i)}) - y^{(i)})^2 \tag{2} \]

\[f_{a,b,c}(x^{(i)} = a(x^{(i)})^{2} + bx^{(i)} + c \tag{3} \]

梯度计算

\[\begin{align} \frac{\partial J(a,b,c)}{\partial a} &= \frac{1}{m} \sum_{i=0}^{m-1}(f_{a,b,c}(x^{(i)}) - y^{(i)})(x^{(i)})^2 \tag{4}\\ \frac{\partial J(a,b,c)}{\partial b} &= \frac{1}{m} \sum_{i=0}^{m-1}(f_{a,b,c}(x^{(i)}) - y^{(i)})x^{(i)} \tag{5}\\ \frac{\partial J(a,b,c)}{\partial c} &= \frac{1}{m} \sum_{i=0}^{m-1}(f_{a,b,c}(x^{(i)}) - y^{(i)}) \tag{6} \\ \end{align} \]

梯度下降算法

\[\begin{align*} \text{repeat}&\text{ until convergence:} \; \lbrace \\ a &= a - \alpha \frac{\partial J(a,b,c)}{\partial w} \tag{7} \\ b &= b - \alpha \frac{\partial J(a,b,c)}{\partial b} \\ c &= c - \alpha \frac{\partial J(a,b,c)}{\partial c} \\ \rbrace \end{align*} \]

遇到的问题

  1. doTrainp_hist[:,0] 我们接受的 p_hist 这个参数如果不用 p_hist = np.array(p_hist) 转换的话,是无法用切片 [:,0] 获取全部的值

  2. 第一次体会到了,人工智能中的调参,一直调迭代的次数,然后看a,b,c三个参数的收敛情况,然后来调学习率

    学习率过大的情况
    1

    学习率过小的情况
    2
    合适
    3

代码与数据

main.py

"""
    @Time    : 2022/8/6 19:23
    @Author  : Hoppz
    @File    : 单变量高次函数拟合.py
    @Description : 数据 y = ax^2 + b 这个函数
"""
import math, copy
import numpy as np
import matplotlib.pyplot as plt
import hoppz_plt_util
from matplotlib.colors import LogNorm
from mpl_toolkits.mplot3d import axes3d, Axes3D
import pandas as pd

x_train = np.array([1, 3.0])
y_train = np.array([6.0, 8.0])

# 计算 y = ax^2 + bx + c
def compute_model_output(x,a,b,c):
    m = x.shape[0]

    f_abc = np.zeros(m)
    for i in range (m):
        f_abc[i] = a * (x**2) + b * x[i] + c

    return f_abc

# 计算均方误差
def compute_cost(x,y,a,b,c):
    m = x.shape[0]

    cost_sum = 0
    for i in range(m):
        f_abc = a * (x[i]**2) + b * x[i] + c

        cost = (f_abc - y[i]) ** 2
        cost_sum = cost_sum + cost_sum + cost
    total_cost = (1/(2 * m)) * cost_sum

    return total_cost


#  梯度计算
def compute_gradient(x,y,a,b,c):
    m = x.shape[0]

    dj_da = 0
    dj_db = 0
    dj_dc = 0
    for i in range(m):
        f_abc = a * (x[i]**2) + b * x[i] + c
        dj_da_i = (f_abc - y[i]) * (x[i]**2)
        dj_db_i = (f_abc - y[i]) * (x[i])
        dj_dc_i = (f_abc - y[i])
        dj_da = dj_da + dj_da_i
        dj_db = dj_db + dj_db_i
        dj_dc = dj_dc + dj_dc_i
    dj_da = dj_da / m
    dj_db = dj_db / m
    dj_dc = dj_dc / m

    return dj_da,dj_db,dj_dc

# 梯度下降算法
def gradient_descent(x,y,a_in,b_in,c_in,alpha, num_iters, cost_function, gradient_function):

    # 便于之后画图,记录每次迭代的数据
    # J: 本次均方误差的值
    # p: 记录迭代后 w,b 的值
    J_history = []
    p_history = []
    portray_history = []
    a = a_in
    b = b_in
    c = c_in

    # 迭代 num_iters 次
    for i in range(num_iters):
        # 计算当前 w, b 情况下的偏导数
        dj_da, dj_db, dj_dc = gradient_function(x, y, a, b, c)

        # 由损失函数的返回值来更新 w,b
        a = a - alpha * dj_da
        b = b - alpha * dj_db
        c = c - alpha * dj_dc
        # 记录每次迭代的结果
        if i < 100000:
            J_history.append(cost_function(x, y, a, b, c))
            p_history.append([a, b, c])

        # 打印当前迭代结果
        if i % math.ceil(num_iters / 10) == 0:
            portray_history.append([a, b, c])
            print(f"Iterator {i:4}:Cost {J_history[-1]:0.2e}",
                  f"dj_da: {dj_da: 0.3e}, dj_db: {dj_db: 0.3e} , dj_dc: {dj_dc: 0.3e}  ",
                  f"a: {a: 0.3e}, b:{b: 0.5e}, c:{c: 0.5e}")

    return a, b, c, J_history, p_history, portray_history
# 读入数据并存入 Numpy 数组中
def loadTxtData():
    # ---绘图设置参数---
    font = {
        'family': 'SimHei',
        'weight': 'bold',
        'size': '16'
    }
    plt.rc('font', **font)
    plt.rc('axes', unicode_minus=False)

    data_loc = "D:\workProgram\PaperSpace\gesture\PythonCode\work\single_data.txt"
    na = np.loadtxt(data_loc)
    print(f"shape is {na.shape}")
    global x_train, y_train
    x_train = na[:, 0]
    y_train = na[:, 1]

#  对数据进行归一化处理
def normalize(x):
    tx = x.copy()
    num_max = tx.max()
    num_min = tx.min()
    print(f'min : {num_min}')
    print(f'max : {num_max}')
    m = x.shape[0]

    for i in range(m):
        tx[i] = (x[i] - num_min) / (num_max - num_min)
    return tx


def doTrain():
    # ------梯度下降算法
    a_init = 0
    b_init = 0
    c_init = 0
    # some gradient descent settings
    iterations = 150000
    tmp_alpha = 5.0e-5  # 0.001

    # 归一化
    global x_train,y_train
    x_train = normalize(x_train)
    y_train = normalize(y_train)

    a_final, b_final,c_final, J_hist, p_hist, portray = gradient_descent(x_train, y_train, a_init, b_init,c_init, tmp_alpha,
                                                                 iterations, compute_cost, compute_gradient)
    print(f"(a,b,c) found by gradient descent: ({a_final:8.4f},{b_final:8.4f},{c_final:8.4f})")
    p_hist = np.array(p_hist) # 如果不转换无法用切片访问第一列的所有元素
    ##
    plt.subplot(2,3,1)
    x = np.linspace(0, 1, 50)
    y = a_final * (x**2) + b_final * x + c_final
    hoppz_plt_util.single_scatter(x_train, y_train, "size of house", "price of house")
    hoppz_plt_util.draw_line_abc(x,y , "the predict model")
    ##
    plt.subplot(2,3,4)
    hoppz_plt_util.draw_line_abc(p_hist[:,0], J_hist, "the predict model")
    plt.subplot(2,3,5)
    hoppz_plt_util.draw_line_abc(p_hist[:, 1], J_hist, "the predict model")
    plt.subplot(2,3,6)
    hoppz_plt_util.draw_line_abc(p_hist[:, 2], J_hist, "the predict model")
    plt.show()



if __name__ == '__main__':

    loadTxtData()
    doTrain()

hoppz_plt_util.py 绘制图像的自定义包

import math, copy
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from mpl_toolkits.mplot3d import axes3d, Axes3D

plt.rcParams['font.sans-serif']=['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False     # 用来正常显示负号

# fmt = '[marker][line][color]'
"""
    # marker ( portray point )
        '.' : 实心原点  'o' : 实心圆   '^' : 上三角   's' : 正方形
        'p' : 五边形   '*' : 星号    '+' : 加号    '...'
    # color ( the color of line )
        'r' : red   'g' : green     'b' : blue      'c' : 青色
        'm' : 品红    'y' : yellow    'k' : black     'w' : white
    # style ( the style of line ) 
        '-' : 实线    ':' : 虚线    '-.' : 点划线    '--' : 破折线
    # size of marker
        'ms' : marker size 
        'mf' : marker face color , the inner color of point
        'mec' : marker edge color ,the border color of point
"""

# 默认设置


# 单元散点图
def single_scatter(x,y,x_label,y_label):
    np.random.seed(19680801)

    N = x.shape[0]
    colors = np.random.rand(N)
    area = (5 * np.random.rand(x.shape[0])) ** 2
    # alpha 透明度
    plt.scatter(x, y, s=area, c=colors, alpha=1)

# 画线
def draw_line_wb(x,w,b,line_lable):
    plt.plot(x,w*x+b,linewidth=1)

# 一元二次方程
def draw_line_abc(x,y,line_lable):
    plt.plot(x,y,linewidth=1)

single_data.txt

6.1101 17.592
5.5277 9.1302
8.5186 13.662
7.0032 11.854
5.8598 6.8233
8.3829 11.886
7.4764 4.3483
8.5781 12
6.4862 6.5987
5.0546 3.8166
5.7107 3.2522
14.164 15.505
5.734 3.1551
8.4084 7.2258
5.6407 0.71618
5.3794 3.5129
6.3654 5.3048
5.1301 0.56077
6.4296 3.6518
7.0708 5.3893
6.1891 3.1386
20.27 21.767
5.4901 4.263
6.3261 5.1875
5.5649 3.0825
18.945 22.638
12.828 13.501
10.957 7.0467
13.176 14.692
22.203 24.147
5.2524 -1.22
6.5894 5.9966
9.2482 12.134
5.8918 1.8495
8.2111 6.5426
7.9334 4.5623
8.0959 4.1164
5.6063 3.3928
12.836 10.117
6.3534 5.4974
5.4069 0.55657
6.8825 3.9115
11.708 5.3854
5.7737 2.4406
7.8247 6.7318
7.0931 1.0463
5.0702 5.1337
5.8014 1.844
11.7 8.0043
5.5416 1.0179
7.5402 6.7504
5.3077 1.8396
7.4239 4.2885
7.6031 4.9981
6.3328 1.4233
6.3589 -1.4211
6.2742 2.4756
5.6397 4.6042
9.3102 3.9624
9.4536 5.4141
8.8254 5.1694
5.1793 -0.74279
21.279 17.929
14.908 12.054
18.959 17.054
7.2182 4.8852
8.2951 5.7442
10.236 7.7754
5.4994 1.0173
20.341 20.992
10.136 6.6799
7.3345 4.0259
6.0062 1.2784
7.2259 3.3411
5.0269 -2.6807
6.5479 0.29678
7.5386 3.8845
5.0365 5.7014
10.274 6.7526
5.1077 2.0576
5.7292 0.47953
5.1884 0.20421
6.3557 0.67861
9.7687 7.5435
6.5159 5.3436
8.5172 4.2415
9.1802 6.7981
6.002 0.92695
5.5204 0.152
5.0594 2.8214
5.7077 1.8451
7.6366 4.2959
5.8707 7.2029
5.3054 1.9869
8.2934 0.14454
13.394 9.0551
5.4369 0.61705
posted @ 2022-08-06 21:40  Hoppz  阅读(529)  评论(0编辑  收藏  举报