吴恩达《机器学习》——线性回归代码实现

news2025/1/15 7:04:00

线性回归

    • 1. 单变量线性回归
      • 单变量线性回归公式
      • 损失函数
      • 优化算法——批梯度下降(BGD)
    • 2. 多变量线性回归
      • 特征缩放(标准化)
      • 参数的逆缩放
    • 3. 线性回归算法代码实现
      • 向量实现
      • Python代码
    • 4. 实验结果
      • 单变量回归
      • 多变量回归
      • 实验总结

数据集、源文件可以在Github项目中获得

地址:https://github.com/Raymond-Yang-2001/AndrewNg-Machine-Learing-Homework

1. 单变量线性回归

单变量线性回归找到一维方程,拟合一条直线。

单变量线性回归公式

h w , b ( x ) = b + w x h_{w,b}(x)=b+wx hw,b(x)=b+wx
w w w b b b是参数,为了方便运算,可以给 x x x加上一个 x 0 = 1 x_0=1 x0=1
h w , b ( x ) = b x 0 + w x 1 h_{w,b}(x)=bx_{0}+wx_{1} hw,b(x)=bx0+wx1

损失函数

J ( w , b ) = 1 2 m ∑ i = 1 m ( h w , b ( x ( i ) ) − y ( i ) ) 2 J(w,b)=\frac{1}{2m}\sum_{i=1}^{m}(h_{w,b}(x^{(i)})-y^{(i)})^{2} J(w,b)=2m1i=1m(hw,b(x(i))y(i))2
为了避免不恰当的数据范围带来损失过大或过小,(例如若数据数值过大,损失可能会在 1 0 5 10^5 105或者 1 0 6 10^6 106这个数量级,不适合直观分析)在评估损失的时候,可以对 h w , b ( x ( i ) ) h_{w,b}(x^{(i)}) hw,b(x(i)) y ( i ) y^{(i)} y(i)先进行标准化,使得损失数值在可评估的范围内。但在进行梯度下降时,不进行此操作

优化算法——批梯度下降(BGD)

w j = w j − α ∂ ∂ w j J ( w , b ) = w j − α 1 m ∑ i = 1 m ( h w , b ( x ( i ) ) − y ( i ) ) x ( i ) w_j=w_{j}-\alpha\frac{\partial}{\partial{w_j}}{J(w,b)}=w_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m}{(h_{w,b}(x^{(i)})-y^{(i)})x^{(i)}} wj=wjαwjJ(w,b)=wjαm1i=1m(hw,b(x(i))y(i))x(i)
b j = b j − α ∂ ∂ b j J ( w , b ) = w j − α 1 m ∑ i = 1 m ( h w , b ( x ( i ) ) − y ( i ) ) b_j=b_{j}-\alpha\frac{\partial}{\partial{b_j}}{J(w,b)}=w_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m}{(h_{w,b}(x^{(i)})-y^{(i)})} bj=bjαbjJ(w,b)=wjαm1i=1m(hw,b(x(i))y(i))
这里,我们可以使用 θ \theta θ统一标识参数,包括 w w w b b b

即,第 j j j个参数 θ j \theta_j θj的更新可以写为:
θ j = θ j − α ∂ ∂ w j J ( θ ; x ) = w j − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x ( i ) \theta_{j}=\theta_{j}-\alpha\frac{\partial}{\partial{w_j}}{J(\theta;\mathbf{x})}=w_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m}{(h_{\theta}(x^{(i)})-y^{(i)})x^{(i)}} θj=θjαwjJ(θ;x)=wjαm1i=1m(hθ(x(i))y(i))x(i)
其中 α \alpha α是学习率。

2. 多变量线性回归

多变量线性回归试图找出多个变量和预测值之间的关系。例如,房子大小、房子卧室数量和房价之间的关系。

特征缩放(标准化)

在样本的不同特征数值差异过大的时候,基于梯度的优化方法会出现一些问题。例如存在如下回归方程:
h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 h_{\theta}(x)=\theta_{0}+\theta_{1}x_{1}+\theta_{2}x_{2} hθ(x)=θ0+θ1x1+θ2x2
假设 x 2 x_{2} x2的范围是 0 ∼ 1 0\sim1 01 x 1 x_1 x1的范围是 1 0 3 ∼ 1 0 4 10^3\sim10^4 103104。我们根据梯度同时优化 θ 0 ∼ θ 2 \theta_0\sim\theta_2 θ0θ2,使得其均改变了相同的大小,那么显然在输入样本相同的情况下, θ 1 \theta_1 θ1的变动会比 θ 2 \theta_2 θ2导致更大的输出的变化。这也可以理解为模型对 θ 1 \theta_1 θ1更敏感。如下损失等线图所示, θ 1 \theta_1 θ1的微小变动会带来损失的剧烈变化。这种情况下,参数的优化会更加困难。
在这里插入图片描述
解决这个问题的方法之一就是特征缩放,将两个特征缩放到相同的范围内。例如,可以进行z-score标准化工作:
x n e w = x − μ σ x_{new} = \frac{x-\mu}{\sigma} xnew=σxμ
其中, μ \mu μ是数据集的均值, σ \sigma σ是标准差,新数据的分布是均值为0,标准差为1的分布。
数据标准化后的参数等损失图如下所示:
在这里插入图片描述

参数的逆缩放

由于对数据进行了缩放,所以最后得到的参数也会出现相应的缩放。其具体关系如下:
θ 0 + θ 1 ∼ d + 1 x 1 ∼ d + 1 − μ x σ x = y − μ y σ y \theta_{0}+\theta_{1\sim d+1}\frac{x_{1\sim d+1}-\mu_{x}}{\sigma_{x}}=\frac{y-\mu_{y}}{\sigma_{y}} θ0+θ1d+1σxx1d+1μx=σyyμy
这里我们对 y y y也进行了标准化,事实上也可以不这么做,对性能没有任何影响。但是对y的标准化使得参数变得更小,对于初始化为0的参数能更快达到收敛。

在标准化 y y y这种情况下,参数的逆缩放公式为:
θ 1 ∼ d + 1 n e w = θ 1 ∼ d + 1 σ x σ y \theta_{1\sim d+1}^{new}=\frac{\theta_{1\sim d+1}}{\sigma_{x}}\sigma_{y} θ1d+1new=σxθ1d+1σy
得到:
θ 0 σ y + θ 1 ∼ d + 1 n e w ( x 1 ∼ d + 1 − μ x ) = y − μ y \theta_{0}\sigma_{y}+\theta_{1\sim d+1}^{new}(x_{1\sim d+1}-\mu_{x})=y-\mu_{y} θ0σy+θ1d+1new(x1d+1μx)=yμy

θ 0 n e w = θ 0 σ y + μ y − θ 1 ∼ d + 1 n e w μ x \theta_{0}^{new}=\theta_{0}\sigma_{y}+\mu_{y}-\theta_{1\sim d+1}^{new}\mu_{x} θ0new=θ0σy+μyθ1d+1newμx
其中,在向量化运算时, θ 1 ∼ d + 1 n e w \theta_{1\sim d+1}^{new} θ1d+1new μ x \mu_{x} μx均为(1,d)的向量,乘法应该采用向量内积。

3. 线性回归算法代码实现

向量实现

设数据 x \boldsymbol{x} x的维度是 ( n , d ) (n,d) (n,d),其中n是样本数量,d是样本特征的维度。为了计算方便,我们在样本上添加一个额外数值全为1的特征维度,使其维度变为 ( n , d + 1 ) (n,d+1) (n,d+1)

  1. 预测
    令参数 θ \boldsymbol{\theta} θ的维度为(1, d+1),则 x θ ⊤ \boldsymbol{x\theta^{\top}} xθ ( θ x ⊤ ) ⊤ \boldsymbol{(\theta x^{\top})^{\top}} (θx)可以得到维度为 ( n , 1 ) (n,1) (n,1)的预测结果 h θ ( x ) h_{\boldsymbol{\theta}}(\boldsymbol{x}) hθ(x)
  2. 梯度下降
    第j个参数的梯度下降公式为:
    θ j = θ j − α ∂ ∂ w j J ( θ ; x ) = w j − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x ( i ) \theta_{j}=\theta_{j}-\alpha\frac{\partial}{\partial{w_j}}{J(\theta;\mathbf{x})}=w_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m}{(h_{\theta}(x^{(i)})-y^{(i)})x^{(i)}} θj=θjαwjJ(θ;x)=wjαm1i=1m(hθ(x(i))y(i))x(i)
    事实上,这里我们把 θ 0 \theta_{0} θ0作为偏置,其梯度应该为:
    ∂ ∂ w 0 J ( θ ; x ) = w 0 − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) \frac{\partial}{\partial{w_0}}{J(\theta;\mathbf{x})}=w_{0}-\alpha \frac{1}{m}\sum_{i=1}^{m}{(h_{\theta}(x^{(i)})-y^{(i)})} w0J(θ;x)=w0αm1i=1m(hθ(x(i))y(i)),由于我们在数据中补充了全为1的特征维度 x 0 x_0 x0,所以其可以和其他参数一样使用上面的公式计算。
    e r r o r error error矩阵 β \boldsymbol{\beta} β h θ ( x ) − x h_{\boldsymbol{\theta}}(\boldsymbol{x})-\boldsymbol{x} hθ(x)x,维度为 ( n , 1 ) (n,1) (n,1),则 x ⊤ β / n \boldsymbol{x^{\top}\beta} /n xβ/n是维度为 ( d + 1 , 1 ) (d+1,1) (d+1,1)的梯度矩阵。
    x ⊤ β = [ x ( 1 ) x ( 2 ) ⋯ ] × [ h ( x ( 1 ) ) − y ( 1 ) h ( x ( 2 ) ) − y ( 2 ) ⋮ ] = [ ∑ i = 1 n ( h ( x ( i ) ) − y ( i ) ) x 0 ( 1 ) ∑ i = 1 n ( h ( x ( i ) ) − y ( i ) ) x 1 ( 1 ) ⋮ ] \boldsymbol{x^{\top}\beta}= \left[ \begin{matrix} x^{(1)}& x^{(2)} &\cdots \end{matrix} \right] \times \left[ \begin{matrix} h(x^{(1)})-y^{(1)}\\ h(x^{(2)})-y^{(2)}\\ \vdots \end{matrix} \right] =\left[ \begin{matrix} \sum_{i=1}^{n}{(h(x^{(i)})-y^{(i)})x_{0}^{(1)}}\\ \sum_{i=1}^{n}{(h(x^{(i)})-y^{(i)})x_{1}^{(1)}}\\ \vdots \end{matrix} \right] xβ=[x(1)x(2)]× h(x(1))y(1)h(x(2))y(2) = i=1n(h(x(i))y(i))x0(1)i=1n(h(x(i))y(i))x1(1)
    x ⊤ β / n \boldsymbol{x^{\top}\beta} /n xβ/n中的每个元素就是对应参数的梯度。

Python代码

import numpy as np


def square_loss(pred, target):
    """
    计算平方误差
    :param pred: 预测
    :param target: ground truth
    :return: 损失序列
    """
    return np.sum(np.power((pred - target), 2))


def compute_loss(pred, target):
    """
    计算归一化平均损失
    :param pred: 预测
    :param target: ground truth
    :return: 损失
    """
    pred = (pred - pred.mean(axis=0)) / pred.std(axis=0)
    target = (pred - target.mean(axis=0)) / target.std(axis=0)
    loss = square_loss(pred, target)
    return np.sum(loss) / (2 * pred.shape[0])


class LinearRegression:
    """
    线性回归类
    """

    def __init__(self, x, y, val_x, val_y, epoch=100, lr=0.1):
        """
        初始化
        :param x: 样本, (sample_number, dimension)
        :param y: 标签, (sample_numer, 1)
        :param epoch: 训练迭代次数
        :param lr: 学习率
        """
        self.theta = None
        self.loss = []
        self.val_loss = []
        self.n = x.shape[0]
        self.d = x.shape[1]

        self.epoch = epoch
        self.lr = lr

        t = np.ones(shape=(self.n, 1))

        self.x_std = x.std(axis=0)
        self.x_mean = x.mean(axis=0)
        self.y_mean = y.mean(axis=0)
        self.y_std = y.std(axis=0)

        x_norm = (x - self.x_mean) / self.x_std
        y_norm = (y - self.y_mean) / self.y_std

        self.y = y_norm
        self.x = np.concatenate((t, x_norm), axis=1)

        self.val_x = val_x
        self.val_y = val_y

    def init_theta(self):
        """
        初始化参数
        :return: theta (1, d+1)
        """
        self.theta = np.zeros(shape=(1, self.d + 1))

    def validation(self, x, y):
        x = (x - x.mean(axis=0)) / x.std(axis=0)
        y = (y - y.mean(axis=0)) / y.std(axis=0)
        outputs = self.predict(x)
        curr_loss = square_loss(outputs, y) / (2 * y.shape[0])
        return curr_loss

    def gradient_decent(self, pred):
        """
        实现梯度下降求解
        """
        # error (n,1)
        error = pred - self.y
        # gradient (d+1, 1)
        gradient = np.matmul(self.x.T, error)
        # gradient (1,d+1)
        gradient = gradient.T / pred.shape[0]
        # update parameters
        self.theta = self.theta - (self.lr / self.n) * gradient

    def train(self):
        """
        训练线性回归
        :return: 参数矩阵theta (1,d+1); 损失序列 loss
        """
        self.init_theta()

        for i in range(self.epoch):
            # pred (1,n); theta (1,d+1); self.x.T (d+1, n)
            pred = np.matmul(self.theta, self.x.T)
            # pred (n,1)
            pred = pred.T
            curr_loss = square_loss(pred, self.y) / (2 * self.n)
            val_loss = self.validation(self.val_x, self.val_y)
            self.gradient_decent(pred)
            
            self.val_loss.append(val_loss)
            self.loss.append(curr_loss)
            print("Epoch: {}/{}\tTrain Loss: {:.4f}\tVal loss: {:.4f}".format(i + 1, self.epoch, curr_loss, val_loss))

        # un_scaling parameters
        self.theta[0, 1:] = self.theta[0, 1:] / self.x_std.T * self.y_std[0]
        self.theta[0, 0] = self.theta[0, 0] * self.y_std[0] + self.y_mean[0] - np.dot(self.theta[0, 1:], self.x_mean)
        return self.theta, self.loss, self.val_loss

    def predict(self, x):
        """
        回归预测
        :param x: 输入样本 (n,d)
        :return: 预测结果 (n,1)
        """
        # (d,1)
        t = np.ones(shape=(x.shape[0], 1))
        x = np.concatenate((t, x), axis=1)
        pred = np.matmul(self.theta, x.T)
        return pred.T


4. 实验结果

单变量回归

数据集可视化
在这里插入图片描述
训练集与测试集划分
blog.csdnimg.cn/c92b41d0e88f4afab794315c992525a0.png)

from LinearRegression import LinearRegression

epochs = 200
alpha = 1
linear_reg = LinearRegression(x=train_x_ex,y=train_y_ex,val_x=val_x_ex, val_y=val_y_ex, lr=alpha,epoch=epochs)
start_time = time.time()
theta,loss, val_loss = linear_reg.train()
end_time = time.time()
Train Time: 0.0309s
Val Loss: 6.7951

训练过程可视化
在这里插入图片描述
与sk-learn比较预测曲线
在这里插入图片描述

多变量回归

数据可视化与训练集、验证集
在这里插入图片描述

from LinearRegression import LinearRegression

alpha = 0.1
epochs = 1000
multi_lr = LinearRegression(train_x,train_y_ex,val_x=val_x,val_y=val_y_ex, epoch=epochs,lr=alpha)
start_time = time.time()
theta, loss, val_loss = multi_lr.train()
end_time = time.time()
Train Time: 0.1209s
Val Loss: 4.187(采用归一化后数据计算损失)

训练过程可视化
在这里插入图片描述

预测平面(与sk-learn对比)
其中蓝色为本算法的预测平面,灰色为sk-learn的预测平面
在这里插入图片描述

实验总结

对于线性回归算法的实现打到了较好的性能,可以尝试调节学习率或者迭代次数来获得更好的性能。由于采用了矩阵运算代替了循环,所以训练时间大大缩短,但仍未到达sk-learn库函数的水平。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用C#开发Word VSTO外接程序示例

目标:实现类似word中导航栏视图的功能。 创建新项目,选择模板“Word VSTO 外接程序”。选择工程,新建项目---新建功能区(可视化设计器)双击打开新建的可视化编辑器,编辑容器RibbonGroup在容器RibbonGroup里…

ReactJS入门之Model层

目录 一:分层 二:使用DVA进行数据分层管理 三:在model中请求数据 四:mock数据 一:分层 上图中,左侧是服务端代码的层次结构,由Controller、Service、Data Access 三层组成服务端系统&#…

Docker图文 | Docker、Dockerfile、Docker-compose、Docker镜像仓库操作 | 系统性学习 | 无知的我费曼笔记

Dokcer和Linux一样都是一些死命令,不必花费过多的时间去学习。 也不必花费过多时间专项地记忆,在实际运用中随用随取即可。 还不如将省下来的时间更多地花费在于培养思维上。 文章目录Docker1.初识Docker1.1.Docker意义1.1.1.应用部署的环境问题描述1.1…

Antd UI Switch组件 中 checked与defaultChecked踩坑记录

目录 需求分析 问题发现 解决方法 总结 需求分析 需求其实很简单,就是在对应的表格行内添加一个人switch 滑块,用于开启或关闭单点登录入口。只需要修改一下对应的表格,添加一个滑块组件,新增一个接口。于是菜鸡博主&#xf…

MySQL库的基本操作与数据库的备份

目录 1、什么是数据库 2、数据库的基本使用 <1>本地连接服务器 <2>创建数据库 <3>创建数据库实例 <4>字符集和校验规则 3、操纵数据库 <1> 查看数据库 <2> 显示创建语句 <3> 修改数据库 <4> 数据库删除 <5>…

遇到的问题

一、git 1. git push之前忘记git pull:需要指定如何协调不同的分支。 解决&#xff1a; (1) git config pull.rebase false (2) git status 用于查看在你上次提交之后是否有对文件进行再次修改 (3) git stash 将当前的工作状态保存到git栈 2. 删除本地分支 # 删除本地分支 git…

【C++初阶】C++的IO流

文章目录C语言的输入输出流CIO流C标准IO流C文件IO流stringstream的简单介绍所有的测试代码C语言的输入输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放在变量中。printf(): 将指定的文字/字…

Python最适合做什么?

Python最适合做什么&#xff1f; 最近我在Reddit上讨论了为什么有人会使用Python而不是其他编程语言。这个讨论非常好&#xff0c;因此我想写一篇关于它的文章。 首先&#xff0c;让我告诉你我对Python的看法。Python是我喜欢的一种语言&#xff0c;可以用于各种各样的应用&a…

spark读取elasticSerach

搭建参考:我的这篇 elasticsearch搭建_我要用代码向我喜欢的女孩表白的博客-CSDN博客 为了方便测试&#xff0c;我们先建立个索引&#xff0c;如果没有索引&#xff0c;他也能插入&#xff0c;只是走的是默认插入格式。 不过虽然接触es已经4年了&#xff0c;但是在工作中&…

XSS攻击原理及预防方法

XSS攻击通常都是通过跨站指令代码攻击网站的后台漏洞。它和信息隐性代码攻击攻击的目标不同。前者是透过从Web前端输入信息至网站&#xff0c;导致网站输出了被恶意控制的网页内容&#xff0c;使得系统安全遭到破坏。而后者则是输入了足以改变系统所执行之SQL语句内容的字串&am…

【解决方案】一种简单且实用的化工厂人员定位系统

化工厂人员定位系统是推进我国安全生产状况持续稳定好转的有效载体&#xff0c;对化工行业的科学发展、安全发展起着重要的促进作用。 化工厂安全责任重于泰山&#xff0c;一旦发生事故后果不堪设想。目前&#xff0c;化工企业还存在着缺乏实时监督、缺乏主动干预、缺乏精准救援…

SpringCloud(9)— Elasticsearch聚合和自动补全

SpringCloud&#xff08;9&#xff09;— Elasticsearch聚合和自动补全 一 数据聚合 1.聚合的分类 聚合&#xff08;aggregations&#xff09;可以实现对文档数据的统计&#xff0c;分析&#xff0c;运算。常见的聚合有三种&#xff1a; 1.桶聚合&#xff08;Bucket&#x…

磁场传感器调研报告

目录 一.磁场传感器 二.磁场传感器工作原理 2.1霍尔效应原理 2.2霍尔传感器工作原理 三.磁场传感器分类介绍 3.1磁阻敏感器 3.2磁性液体加速度传感器 3.3磁性液体水平传感器 四.磁性传感器的应用 4.1汽车 4.2消费类电子产品 4.3工业智能控制和自动化 五、总结 一.…

iftop工具(网卡流量监控软件)的使用

直接运行iftop&#xff0c;不加任何参数 界面显示 界面上面显示的是类似刻度尺的刻度范围&#xff0c;为显示流量图形的长条作标尺用的。 中间的< >这两个左右箭头&#xff0c;表示的是流量的方向。 TX&#xff1a;发送流量 RX&#xff1a;接收流量 TOTAL&#xff1a;总…

open-local部署之后k8s的kube-scheduler挂掉问题

搭建一套k8s集群之后&#xff0c;本地存储化方案选择了阿里巴巴的open-local&#xff0c;没部署open-local&#xff0c;k8s 的kube-scheduler一切正常&#xff0c;只要按照官方文档部署了open-local&#xff0c;k8s的kube-scheduler就会挂掉&#xff0c;不是被kill掉&#xff…

Go的并发模型

Go的并发模型 文章目录Go的并发模型一、GO并发模型的三要素1.1 操作系统的用户空间和内核空间1.2 线程模型的实现&#xff08;1&#xff09;用户级线程模型&#xff08;2&#xff09;内核级线程模型&#xff08;3&#xff09;两级线程模型1.3 GO线程实现模型MPG二、什么是gorou…

选择题

目录 1058:选择题 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 代码长度限制: 时间限制: 内存限制: 思路: 1.选择题结构体 1.2 选择题结构体代码 2.判断选择题是否做对函数 2.1 判断选择题是否做对函数代码: 3.选择题的输入…

【并发】深入理解JMM并发三大特性(二)

t【并发】深入理解JMM&并发三大特性&#xff08;二&#xff09; 我们在上一篇文章中提到了JMM内存模型&#xff0c;并发的三大特性&#xff0c;其中对可见性做了详细的讲解&#xff01; 这一篇文章&#xff0c;将会站在硬件层面继续深入讲解并发的相关问题&#xff01; …

将整数字符串转成整数值

题目&#xff1a; 给定一个字符串 str&#xff0c;如果str符合日常书写的整数形式&#xff0c;并且属于 32 位整数的范围&#xff0c;返回 str 所代表的整数值&#xff0c;否则返回 0 。 举例&#xff1a; str "123" 返回 123 str "023" 返回 23 …

springboot整合之统一异常处理

特别说明&#xff1a;本次项目整合基于idea进行的&#xff0c;如果使用Eclipse可能操作会略有不同&#xff0c;不过总的来说不影响。 springboot整合之如何选择版本及项目搭建 springboot整合之版本号统一管理 springboot整合mybatis-plusdurid数据库连接池 springboot整合…