首页 > 编程语言 >最小二乘法原理推导+代码实现[Python]

最小二乘法原理推导+代码实现[Python]

时间:2024-08-07 20:38:18浏览次数:19  
标签:arr end 推导 Python self 所示 error np 乘法

0.前言

  • 本文主要介绍了最小二乘法公式推导,并且使用Python语言实现线性拟合。
  • 读者需要具备高等数学、线性代数、Python编程知识。
  • 请读者按照文章顺序阅读。
  • 绘图软件为:geogebra5。

1.原理推导

1.1应用

最小二乘法在购房中的应用通常涉及房价预测和房屋定价方面。这种统计方法通过拟合数据来找到一条最符合实际观测值的直线(或曲线),从而帮助预测房屋的合理市场价格。例如某地的房价与房屋面积大小关系如下图(图1-1)所示。
image
为了方便操作,请读者不要考虑数据是否真实有效,当然这样的房价笔者是不会买。笔者将数据以CSV格式保存,具体数据如下图(1-2)所示。
image

点击查看数据
其中x表示房屋的面积,单位平方米,y表示房屋的价格,单位万元。
x	y
12.3	11.8
14.3	12.7
14.5	13
14.8	11.8
16.1	14.3
16.8	15.3
16.5	13.5
15.3	13.8
17	14
17.8	14.9
18.7	15.7
20.2	18.8
22.3	20.1
19.3	15
15.5	14.5
16.7	14.9
17.2	14.8
18.3	16.4
19.2	17
17.3	14.8
19.5	15.6
19.7	16.4
21.2	19
23.04	19.8
23.8	20
24.6	20.3
25.2	21.9
25.7	22.1
25.9	22.4
26.3	22.6

1.2定义直线方程

image

1.3定义拟合误差

假设房屋面积、房屋价格、预测价格如下图(1-3)所示。
image
此时需要一个函数去衡量房屋预测价格与真实的房屋价格之间的误差,若预测价格和真实价格之间的误差很小,约等于0,则表明该拟合函数预测房屋价格十分准确。具体的误差函数如下所示。
image
有时L又称作损失函数。

1.4梯度下降优化

image
梯度下降思想如下图(图1-4)所示。
image
下面笔者举出一个简单的例子帮助理解。
image
image
image
image
image
比如在x=3这一点,为了使g(x)的值变小,即往山谷方向移动,因此x需要向左移动,即x需要变小,示例图如下(图1-7)所示。
image
例如在x=-1这一点,为了使g(x)值变小,需要x不断变大,往山谷处靠近,示例图如下图(图1-8)所示。
image
在结合x<1、x>1时g'(x)的符号可以总结出以下梯度下降公式。
image
其中上述公式的=是指编程语言中的赋值操作。根据公式不难看出,当x<1时,g'(x)<0,x-g'(x)的值相较于x变大了;当x>1时,g'(x)>0,x-g'(x)的值相较于x变小了,这里非常巧妙,通过不断的计算和赋值,就好像一步一步的走动到山谷,值得注意的是,这里还不算是一小步一小步。
考虑到一种情况,若g'(x)非常大或者非常小,导致赋值后的x太大或太小。例如当x=-1时,计算出来的导数为-9999,那么再执行x=x-g'(x)后x的值为9998,x的值从-1变为9998,这个步子也太大了吧,显然是不合适的,此时可能会出现反复震荡的情况,具体示例如下图(图1-9)所示。

image
为了克服反复震荡的情况,所以要引进学习率。

1.5学习率

对于梯度优化函数x=x-g'(x),引进学习率后如下所示。
image
其中η为学习率,其含义类似于迈步子的力度,力度越大,迈的步子越远,力度越小迈的步子越小,一般情况下,学习率设置为很小(0.001、0.0001)。
例如当x=-1时,g'(x)为-1,η为1000,则x更新后的值为999,若η为0.0001,则x更新后的值为-0.9999,仅仅是挪动了一小小小小步。

1.6更新所有参数

通过上文,相信你已经懂得了梯度更新的原理,那么对于损失函数L来说,怎么进行梯度更新呢?更新公式如下图(图1-10)所示。
image
之前的简单示例是对x求导,这里因为是多元函数,所以要求偏导,只要掌握梯度下降的基本思想,对于二次函数拟合也是类似。

2.代码解释

image

3.完整代码

点击查看代码
import numpy as np
import matplotlib.pyplot as plt
plt.switch_backend('TkAgg')
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签SimHei
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号


class LinerRegression():

    #初始化类
    #learning_rate:学习率                  浮点数
    #end_error:相邻两轮损失函数之间的差值    浮点数
    #max_it:最大迭代次数                   整形
    def __init__(self, learning_rate=0.00001, end_error=0.01,max_it=1000):
        self.f_lr = learning_rate       # 学习率
        self.f_end_error=end_error      # 前一轮loss与当前轮loss之差小于等于end_error时结束迭代
        self.f_diff=2147483647          # 保存前一轮loss与当前轮loss之差
        self.f_w=np.random.normal(0,1)  # y=wx+b中的w
        self.f_b=np.random.uniform(0,1) # y=wx+b中的b
        self.i_max_iterator=max_it      # 最大迭代次数,当end_error与max_iterator其一满足便会停止迭代

    #获得w和b的偏导数
    def get_partial_derivative(self):
        f_p_w =(2/len(self.arr_x)) * np.sum( (self._f(self.f_w,self.arr_x,self.f_b)-self.arr_y)*self.arr_x  )
        f_p_b =(2/len(self.arr_x)) * np.sum(  self._f(self.f_w,self.arr_x,self.f_b)-self.arr_y              )
        return f_p_w, f_p_b

    def standardize(self):  # 标准化
        f_mu =    np.mean(self.arr_x)
        f_sigma = np.std(self.arr_x)
        self.arr_x= (self.arr_x - f_mu) / f_sigma

    def fit(self, x, y):
        self.arr_x = x
        self.arr_y = y

        #标准化
        # self.standardize()

        #当前损失值
        f_origin_loss=self.get_loss(y_true=self.arr_y,y_pred=self._f(self.f_w,self.arr_x,self.f_b))
        i_it_cnt=0#迭代次数

        while self.f_diff>self.f_end_error and i_it_cnt<self.i_max_iterator:

            self.next_step()#更新w b
            #学习后的损失值
            f_cur_loss=self.get_loss(y_true=self.arr_y,y_pred=self._f(self.f_w,self.arr_x,self.f_b))
            #损失值之差
            f_diff=f_origin_loss-f_cur_loss
            self.f_diff=f_diff
            i_it_cnt+=1
            print("第{}次训练,w={:.2f},b={:.2f},loss={:.2f},diff={}".format(i_it_cnt,self.f_w,self.f_b,f_cur_loss,f_diff))

        print("训练结果函数式:y={:.2f}x+{:.2f}".format(self.f_w,self.f_b))

        #绘制结果图
        plt.scatter(self.arr_x, self.arr_y)
        arr_new_x = np.linspace(10,28,28-10+1)
        arr_new_y = self.f_w * arr_new_x + self.f_b
        plt.plot(arr_new_x, arr_new_y,'r--')
        plt.show()

    #一元线性函数
    #w:斜率   浮点数
    #x:自变量 整形/浮点型/整形数组/浮点型数组
    #b:截距   浮点数
    #返回值:  整形/浮点型/整形数组/浮点型数组
    def _f(self, w, x, b):
        return w*x+b

    def predict(self, new_x):
        """预测"""
        y_pred = self._f(self.f_w, new_x, self.f_b)
        return y_pred

    def get_loss(self, y_true, y_pred):
        """损失
            y_true:[x,x,x,x,x]  <class 'numpy.ndarray'>
            y_pred:[x,x,x,x,x]  <class 'numpy.ndarray'>
        """
        return (1/len(y_true))*np.sum((y_pred-y_true)**2)

    def next_step(self):
        """梯度学习,往前走"""
        d_w, d_b = self.get_partial_derivative()
        self.f_w = self.f_w - self.f_lr * d_w
        self.f_b = self.f_b - self.f_lr * d_b

if __name__ == '__main__':

    train = np.loadtxt('./Datasets/白话机器学习/线性回归.csv',delimiter=',', dtype='float', skiprows=1)
    x = train[:,0]
    y = train[:,1]

    lg=LinerRegression(learning_rate=1e-5,end_error=1e-3,max_it=1e3)
    lg.fit(x,y)
    print("x=21时,预测为{}".format(lg.predict(new_x=np.array([21]))))

4.运行结果

控制台输出:
image
拟合结果:
image
损失(代码中没有):
image

标签:arr,end,推导,Python,self,所示,error,np,乘法
From: https://www.cnblogs.com/hello-nullptr/p/18347123

相关文章

  • python opencv图片简单操作
    一、从文件读取图片cv2.imread(filename,flags) 参数: filepath:读入image的完整路径 flags:标志位,{cv2.IMREAD_COLOR,cv2.IMREAD_GRAYSCALE,cv2.IMREAD_UNCHANGED} cv2.IMREAD_COLOR:默认参数,读入一副彩色图片,忽略alpha通道,可用1作为实参替代 cv2.IMREAD_GRAYSCALE:读入......
  • Python 中的排序与 ASCII 编码解析
    1.引言    不知道你有没有想过用Python进行一些排序的工作,对于一些数量比较小的数字集合(例如:1、15、32、79、6、55)我们可以迅速发现最大的79和最小的1,但当这个数量非常大的时候,我们找大小就很费劲了,而这种繁琐的工作就应该派计算机出马了2.比大小  a.常规数字比......
  • Python使用Memcached示例
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • 2024年华为OD机试真题-欢乐的周末-Python-OD统一考试(C卷D卷)
    2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述:小华和小为是很要好的朋友,他们约定周末一起吃饭。通过手机交流,他们在地图上选择了多个聚餐地点(由于自然地形等原因,部分聚餐地点不可达),求小华和小为都能到达的聚餐地点有多少个?输入描述......
  • Python并发编程
    简介多线程:threading,利用cpu和io可以同时执行的原理,让CPU不会等待IO完成多进程:multiprocess,利用多核CPU的能力,真正的并行执行任务异步IO:asynio,在单线程利用CPU和IO同时执行的原理,实现函数异步执行 使用Lock对共享资源加锁,防止冲突访问使用Queue实现不......
  • 19.python之自定义函数
    python之自定义函数一、函数的介绍1、函数定义:函数是一个组织好,可重复使用,实现单一或联合的代码段。2、函数作用:a、降低代码的冗余、b、增加代码的复用性c、提高程序的拓展性d、封装二、python的结构三、函数的使用1、格式:def函数名(变量):执行语句函数名(实际参数)#调......
  • python装饰器提高代码复用,减少代码量,简洁易懂
    装饰器提高代码复用,减少代码量对于一个程序程序,无论是c、java、go还是python,组成这段程序的代码需要越简单越好,要知道程序的代码越简单,代码量越少,出错的概率就小,维护起来也简单。针对python语言,装饰器是我最近发现的针对简化代码,特别有帮助的工具。下面我用两段代码,演示一下同样......
  • python,怎么用工厂模式设计代码?
    工厂模式打造工厂模式,需要抽象工厂和具体工厂。怎么理解?抽象工厂就是接口的定义,但不负责具体的实现。而具体工厂则需要负责定义的接口的实现。就好比你爸爸让你上街时带一瓶酱油,而具体买什么牌子的由你决定。”你爸爸让带一瓶酱油“就是接口的定义函数,这个函数只负责定义”要求“......
  • python-深拷贝和浅拷贝
     浅拷贝list_name=["李琪",["周义杰","毛绍祺"]]data01=list_name[:]#触发浅拷贝:只复制第一层,共享深层数据data01[0]="琪琪"#修改第一层,数据2份,互不影响data01[1][0]="义杰"#修改深层,数据1份,数据互相影响print(list_name)print(data01) 深拷贝 作用:互不......
  • Python 循环引用与内存泄漏:深度解析
    Python循环引用与内存泄漏:深度解析在Python编程中,循环引用和内存泄漏是两个需要特别注意的问题。本文将深入探讨Python中的循环引用现象、其导致的内存泄漏问题,并提供详细的解决思路与方法。同时,我们还将分析一些常见场景,并分享扩展与高级技巧,帮助读者全面理解和应对这一......