Python 现代控制理论 —— 梯度下降法实现的线性回归系统

news2025/1/11 14:08:51

线性回归是有监督学习中的经典问题,其核心在于找到样本的多个特征与标签值之间的线性关系。样本集中的第j个样本可被表示为:

  • 特征向量:X_j=\begin{bmatrix} x_{j1} &x_{j2} & \hdots & x_{jn} \end{bmatrix}
  • 标签值:u_j

而线性回归系统给出权重向量:

w=\begin{bmatrix} w_0 & w_1 & w_2 & \hdots & w_n \end{bmatrix}^T

使得该样本的预测值为:

y_j=w_0+X_jw=w_0+\sum^n_{i=1}x_{ji}w_i

当所有样本的预测值与标签值的误差最小时,即代表该线性回归系统找到了最优的拟合曲线

本文采用了梯度下降法以解决线性回归问题。梯度下降法与现代控制系统相似,现代控制系统实现的梯度下降法如下:

该系统将线性回归问题的权重向量 w 作为状态变量,以损失函数 L(w) 反向传播的梯度 -\frac{dL(w)}{dw} 作为 \frac{dw}{dt},并通过 w= w +\frac{dw}{dt} dt 对权重向量进行更新(其中的 dt 可视为学习率),使得所有样本的误差越来越小

状态空间描述

在前文的讨论中,样本的预测值  表示为“特征向量×权重向量+偏置”的形式。为了后续计算的整洁,将样本的特征向量表示为:

X_j=\begin{bmatrix} 1 & x_{j1} &x_{j2} & \hdots & x_{jn} \end{bmatrix}

则样本的预测值可被重写为:

y_j=X_jw

以样本的预测值与标签值的差值作为误差 e

e=y-u=\begin{bmatrix} 1& x_{11} & x_{12} & \hdots & x_{1n}\\ 1& x_{21}&x_{22} & \hdots & x_{2n}\\ \vdots &\vdots &\vdots & \ddots &\vdots \\ 1& x_{m1}& x_{m2}& \hdots & x_{mn}\end{bmatrix} w - \begin{bmatrix} u_1\\ u_2\\ \hdots\\ u_m \end{bmatrix}

误差e的值域为 (- \infty, \infty),最优值为 0,显然不可直接作为梯度下降法的损失函数。故以其原函数 (MSE) 作为损失函数:

L(w)=\frac{\overline{e^2}}{2} \in [0, \infty)

\frac{\partial L(w)}{\partial w_i}=\overline{(e\frac{\partial e}{\partial w_i})} = \overline{(x_{.i}X)}w - \overline{(x_{.i}u)},\ x_{.0}=1

\frac{dL(w)}{dw}=\begin{bmatrix} 1 & \overline{x_{.1}} & \overline{x_{.2}} & \hdots&\overline{x_{.n}} \\ \overline{x_{.1}} & \overline{x_{.1}^2} & \overline{x_{.1}x_{.2}} & \hdots & \overline{x_{.1}x_{.n}}\\ \vdots &\vdots & \vdots &\ddots &\vdots \\ \overline{x_{.n}} & \overline{x_{.n}x_{.1}} & \overline{x_{.n}x_{.2}} & \hdots & \overline{x_{.n}^2} \end{bmatrix} w - \begin{bmatrix} \overline{u}\\ \overline{(x_{.1}u)}\\ \vdots\\ \overline{(x_{.n}u)} \end{bmatrix}

由梯度下降法可知,以 w = w - \frac{dL(w)}{dw} p 的方式对权重向量进行更新,将使得损失函数 L(w) 逐渐减小。故令 \frac{dw}{dt}=-\frac{dL(w)}{dw},\ dt=p,以 w=w+\frac{dw}{dt}dt 对权重向量进行更新。综上所述,该系统的状态空间描述为:

  • 状态方程: \dot{w}=-\begin{bmatrix} 1 & \overline{x_{.1}} & \overline{x_{.2}} & \hdots&\overline{x_{.n}} \\ \overline{x_{.1}} & \overline{x_{.1}^2} & \overline{x_{.1}x_{.2}} & \hdots & \overline{x_{.1}x_{.n}}\\ \vdots &\vdots & \vdots &\ddots &\vdots \\ \overline{x_{.n}} & \overline{x_{.n}x_{.1}} & \overline{x_{.n}x_{.2}} & \hdots & \overline{x_{.n}^2} \end{bmatrix} w + \begin{bmatrix} \overline{u}\\ \overline{(x_{.1}u)}\\ \vdots\\ \overline{(x_{.n}u)} \end{bmatrix} 1(t)
  • 输出方程:e=\begin{bmatrix} 1& x_{11} & x_{12} & \hdots & x_{1n}\\ 1& x_{21}&x_{22} & \hdots & x_{2n}\\ \vdots &\vdots &\vdots & \ddots &\vdots \\ 1& x_{m1}& x_{m2}& \hdots & x_{mn}\end{bmatrix} w - \begin{bmatrix} u_1\\ u_2\\ \hdots\\ u_m \end{bmatrix} 1(t)

其中,状态方程描述了权重向量 w 随时间变化的速度,输出方程描述了该线性回归系统对每一个样本的误差

实验案例

给定定义域 x \in [-3, 3],在下式所示的曲线上均匀地选取样本点 (上图中蓝色散点),并在纵坐标 (标签值) 上添加噪声:

y=2+x+0.3x^2-0.5x^3+4\sin{x}

横坐标为 x_j 的样本的特征向量被定义为:

X_j = \begin{bmatrix} 1 &x_j & e^{-x_j} & e^{x_j} \end{bmatrix}

将样本的特征向量与标签值输入线性回归系统,该系统将用曲线 y=w_0 + w_1x +w_2 e^{-x} + w_3 e^x 对样本进行拟合。运行Python程序(见附录)后,还得到状态空间表达式中的四个矩阵

最终拟合曲线为上图中的橙色曲线,该拟合曲线的权重向量为:

w=\begin{bmatrix} 2.02 & 6.86 & 1.73 & -1.46 \end{bmatrix}^T

该拟合曲线的方程为:

y=2.02 + 6.86 x +1.73 e^{-x} - 1.46 e^x

代码实现

上述系统主要通过 Auto_Ctrl_System 类实现,该类的实例属性有:

  • A, B, C, D:系统矩阵 A,输入矩阵 B,输出矩阵 C,直接传递矩阵 D
  • w:权重向量
  • dstate, output:状态方程、输出方程的结果

实例方法有:

  • adjust:根据状态方程,调整权重向量
  • predict:利用当前的权重向量,对样本 (可另给定) 给出预测值
  • ns_tran:系统非奇异变换,给定转换矩阵 T 使得 \hat{w}=Tw,对系统的矩阵 A, B, C 进行原地更新
  • ctrl_2:转换系统为能控标准Ⅱ型,T=\begin{bmatrix} B & AB & \hdots & A^{n-1}B \end{bmatrix}
  • jordan:转换系统为 Jordan 标准型 (该系统中的矩阵 A 为对称矩阵,可对角化),T 由矩阵 A 的特征向量构成
  • state_tran:利用矩阵 A 可被对角化这一点,对该系统的状态转移矩阵进行求解
import numpy as np
import sympy as sp
from tqdm import trange

np.set_printoptions(precision=3, suppress=True)
INFO = lambda *args: print(*args, sep='\n')


def simplify(x, eps=1e-5):
    ''' 针对 exp 多项式的简化函数, 不一定普适'''
    if isinstance(x, sp.Add):
        x = list(x.args)
        for i in range(len(x)):
            coef, exp = x[i].args
            coef, exp = map(float, [coef, exp.args[0].args[0]])
            # exp 系数小于阈值, exp 指数项为负数
            if abs(coef) < eps and exp < 0: x[i] = 0
        x = sum(x)
    return x


class Auto_Ctrl_System:
    n = property(fget=lambda self: self.w.size)
    # 状态方程, 输出方程
    dstate = property(fget=lambda self: self.A @ self.w + self.B)
    output = property(fget=lambda self: self.C @ self.w + self.D)
    # 对输入的矩阵预处理 (添加偏置)
    _process = lambda self, x: np.concatenate([np.ones([x.shape[0], 1]), x], axis=1) if self.bias else x

    def __init__(self, x, y, bias=True):
        x, y = map(lambda i: np.float16(i), [x, y])
        n = x.shape[1] + bias
        self.bias = bias
        # 输出方程的矩阵 C, D
        self.C = self._process(x)
        self.D = - y[:, None]
        # 状态方程的矩阵 A, B
        self.A = - (self.C[..., None] * self.C[:, None]).mean(axis=0)
        self.B = - (self.C * self.D).mean(axis=0, keepdims=True).T
        # 初始化权重向量
        self.w = np.random.random([n, 1])

    def adjust(self, epochs=2e4, dt=1e-3):
        qbar = trange(round(epochs))
        for _ in qbar:
            loss = np.square(self.output).mean()
            qbar.set_description(f'MSE {loss:.2f}')
            self.w += self.dstate * dt

    def predict(self, x=None):
        x = self._process(x) if isinstance(x, np.ndarray) else self.C
        return (x @ self.w).flatten()

    def ns_tran(self, tran):
        tran_inv = np.linalg.inv(tran)
        assert np.all(np.isfinite(tran_inv))
        INFO('非奇异变换矩阵:', tran)
        self.A = tran_inv @ self.A @ tran
        self.B = tran_inv @ self.B
        self.C = self.C @ tran

    def ctrl_2(self):
        tran = [self.B]
        for i in range(self.n - 1): tran.append(self.A @ tran[-1])
        tran = np.concatenate(tran, axis=1)
        # 不能控的状态数
        print(f'不能控状态数: {self.n - sp.Matrix(tran).T.rank()}')
        self.ns_tran(tran)
        INFO('能控标准Ⅱ型:', self)

    def jordan(self):
        tran = np.linalg.eig(self.A)[1]
        self.ns_tran(tran)
        INFO('Jordan 标准型:', self)

    def state_tran(self, response=False):
        t = sp.symbols('t')
        # 对角化的变换矩阵
        tran = np.linalg.eig(self.A)[1]
        tran_inv = np.linalg.inv(tran)
        assert np.all(np.isfinite(tran_inv))
        # 状态转移矩阵
        diag = sp.Matrix(np.eye(self.n) * (tran_inv @ self.A @ tran) * t).exp()
        state_tran = tran @ diag @ tran_inv
        # 对 exp 多项式进行简化
        for i in range(self.n):
            for j in range(self.n):
                state_tran[i, j] = simplify(state_tran[i, j])
        INFO('状态转移矩阵:', np.array(state_tran))
        # fixme: 阶跃响应 (针对性求解, 勿用, 需根据状态转移矩阵设计)
        if response:
            fc, f, gc, g, a, b = sp.symbols('f_c, f, g_c, g, a, b')
            # 利用新变量替代状态转移矩阵中的 exp 多项式
            state_tran = sp.Matrix([[fc, 0, f, f],
                                    [0, gc, -g, g],
                                    [f, -g, a + b, a - b],
                                    [f, g, a - b, a + b]])
            # 零输入响应, 零状态响应
            zero_input = state_tran @ self.w
            zero_state = np.linalg.inv(self.A) @ (state_tran - sp.eye(self.n)) @ self.B
            return zero_input, zero_state
        return state_tran

    def __str__(self):
        return str(np.concatenate([self.A, self.B], axis=1))

    __repr__ = __str__


if __name__ == '__main__':
    import matplotlib.pyplot as plt

    x = np.linspace(-3, 3, 50)
    z = np.stack([x, np.exp(-x), np.exp(x)], axis=1)

    np.random.seed(10)
    # 原函数: x + 0.3 x^2 - 0.5 x^3 + 4 sin(x) + 噪声
    y = 2 + x + 0.3 * np.power(x, 2) - 0.5 * np.power(x, 3) + 4 * np.sin(x) + 5 * (np.random.random(len(x)) - 0.5)
    plt.scatter(x, y, c='deepskyblue', label='true')

    acs = Auto_Ctrl_System(z, y, bias=True)
    # 转化为 Jordan 标准型
    acs.jordan()
    acs.adjust(2e4)

    plt.plot(x, acs.predict(), c='orange', label='pred')
    plt.legend(frameon=False)
    plt.show()

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

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

相关文章

Python采集某网站m3u8内容,美女我来了~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 Pycharm 模块使用: import requests >>> pip install requests import re 正则表达式 解析数据 import json 安装python第三方模块: win R 输入 cmd 点击确…

不止一面的百变 ACE

这个时代&#xff0c;可谓是云原生的黄金时代。 站在这个云原生的风口&#xff0c;年轻一代的开发者如何看待自己所处的环境&#xff1f;他们眼中的云原生未来是什么样&#xff1f; 今天我们就将走近一位年轻的“云原生原住民”&#xff0c;听听他作为开发者的成长经历。 War…

【python3】9.python高阶内容(上)_基础

9.python高阶内容&#xff08;上&#xff09;_基础 2022.12.27 python高阶内容&#xff08;上&#xff09;_基础9.1 字符串的高阶玩法 9.1.1 %百分号模式 %d:整数%i:整数%s:字符%f:小数 【方式1】&#xff1a;前面用格式占位&#xff0c;后面用具体的内容 name "莫烦…

Android设计模式详解之访问者模式

前言 访问者模式是一种将数据操作与数据结构分离的设计模式&#xff1b; 定义&#xff1a;封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作&#xff1b; 使用场景&#xff1a; 对象结构比较稳定&a…

大厂与小厂招人的区别,看完多少有点不敢相信

前两天在头条发了一条招人的感慨&#xff0c;关于大厂招人和小公司招人的区别。 大厂&#xff1a;有影响力&#xff0c;有钱&#xff0c;能够吸引了大量的应聘者。因此&#xff0c;也就有了筛选的资格&#xff0c;比如必须985名校毕业&#xff0c;必须35岁以下&#xff0c;不能…

基于DoIP使用CANoe对ECU进行诊断测试

伴随以太网引入到车载网络中,本文分享通过常用工具CANoe怎么样对ECU进行通信以及测试。 相比在车载CAN总线,以太网又有什么与众不同之处? 1、硬件接口卡(收发器) 以往车载CAN网络较常使用的是VN 16XX 系列,在连接ECU进行通信时,除了配置波特率也要进行通道分配: 而…

7个学习UI、UX设计一定要经历的步骤

我们不是一些有才华的设计师。我们天生就有艺术天赋。后天我们学会了设计技巧。设计的根本目的是解决问题。设计是不断发现和解决问题。 有许多设计领域&#xff1a;UI、UX.产品设计师.平面设计师.交互设计师.信息架构师等&#xff0c;所以要找出你最感兴趣的设计专业。 现在让…

美颜sdk动态贴纸技术、代码分析

目前&#xff0c;美颜sdk动态贴纸已经成了各大直播平台主播的必备“直播伴侣”&#xff0c;在其他的视频拍摄场景动态贴纸的热度同样很高&#xff0c;本篇文章小编将为大家深度盘点一下美颜sdk动态贴纸的技术实现以及代码。 一、多终端适配 对于如今的直播平台终端来说&#x…

CAPL学习之路-测试功能集函数(测试结构化)

用户可以使用如下函数在测试报告中对每一条测试用例设置结构化的输出内容 TestCaseDescription 添加测试用例的描述文本 此函数用于测试用例中,描述文本会添加在固定区域(测试用例title的下方)。多次调用该函数,描述文本会合并显示在固定区域。如果想让描述文本换行,可以…

爆火的Web3.0背后,百度营销如何抓住流量密码?

出品| 大力财经 文 | 魏力 AI、元宇宙、Web3.0、AIGC等新技术、新概念的加持&#xff0c;给传统的流量营销平台带来了前所未有的挑战。尤其是短视频时代的崛起&#xff0c;用户的使用习惯开始改变&#xff0c;完全改变了流量的逻辑和习惯。 从搜索引擎业务起家的百度&#x…

DoIP---车载以太网诊断方面边缘节点的路由策略分析

假期后开工第一天&#xff0c;规划好自己一天需要做的事情&#xff0c;按部就班完成每日任务&#xff0c;做好每日总结。 自己一天一个脚印&#xff0c;这不是鸡血&#xff0c;这是工作态度&#xff01;&#xff01;&#xff01; 惯例分享一段喜欢的文字&#xff1a; 每个人…

目标检测之FCOS算法分析

网络结构 (图片来自原论文&#xff1a;FCOS: Fully Convolutional One-Stage Object Detection) 在ResNet50 Backbone中&#xff0c;C3,C4,C5C3,C4,C5C3,C4,C5是卷积特征图&#xff1b; 在FPN结构中&#xff0c;P3,P4,P5,P6,P7P3,P4,P5,P6,P7P3,P4,P5,P6,P7是最后用于预测的特…

2023跨境出海指南:泰国网红营销白皮书

作为东南亚第二大经济体&#xff0c;泰国一直是旅游和企业出海的热门之地。随着电商经济和互联网的发展&#xff0c;加上疫情的催化&#xff0c;泰国的社交媒体行业也得到了飞速发展&#xff0c;已经成为了主流营销方式之一。本文Nox聚星就从网红营销的角度&#xff0c;和大家探…

代码随想录-46-226.翻转二叉树

目录前言题目1.使用队列思路&#xff08;定义变量&#xff09;2. 本题思路分析&#xff1a;3. 算法实现4. pop函数的算法复杂度5. 算法坑点前言 在本科毕设结束后&#xff0c;我开始刷卡哥的“代码随想录”&#xff0c;每天一节。自己的总结笔记均会放在“算法刷题-代码随想录…

浅谈一下个人基于IRIS后端业务开发框架的理解

文章目录浅谈一下个人基于IRIS后端业务开发框架的理解现状方案具体实现BaseBizDataFilterSqlImp、RefApiUtil总结浅谈一下个人基于IRIS后端业务开发框架的理解现状由于国内使用基于M语言IRIS平台几乎都在医疗行业。医疗系统又非常的庞大和复杂。前期由于快速占领市场&#xff0…

珠城科技在创业板上市:IPO首日跌破发行价,市值相对蒸发约7亿元

12月26日&#xff0c;浙江珠城科技股份有限公司&#xff08;下称“珠城科技”&#xff0c;SZ:301280&#xff09;在深圳证券交易所创业板上市。本次上市&#xff0c;珠城科技的发行价格为67.40元/股&#xff0c;发行数量为1628.34万股&#xff0c;募资总额约为10.98亿元&#x…

java线程

1.创建线程和运行线程 1.1.方式一: 直接使用Thread线程对象创建线程 Slf4j public class TestThread {public static void main(String[] args) {//创建一个线程,并且指定线程名称为"t1"Thread thread new Thread("t1") {Overridepublic void run() {//…

基于JAVA springboot + MYSQL +VUE的项目管理系统(含数据库),包括工时统计、原型预览、效果图管理等

平台介绍 无鱼工时管理系统&#xff0c;是一款轻量级工时记录和管理工具&#xff0c;包括项目管理&#xff0c;工时上报&#xff0c;工时日报&#xff0c;工时统计等功能。 无鱼工时管理系统可通过员工工时上报的方式&#xff0c;来记录项目所花费的工时&#xff0c;帮助企业…

滑块验证 - 使用AJ-Captcha插件【超简单.jpg】

滑块验证实现一、后端1&#xff09;首先引入maven&#xff1a;2&#xff09;再在application.yml中自定义水印&#xff0c;直接启动后前端就可以请求接口了3&#xff09;重写CaptchaCacheServiceRedisImpl①先新建一个文件夹②重写impl二、前端&#xff1a;1&#xff09;复制文…

UML2面向对象分析与设计(第2版) 谭火彬 杂记

首先&#xff0c;来讲讲我对泛化的理解&#xff0c;其实这是站在的视角的不同而表述的不同&#xff0c;泛化是站在父类的角度&#xff0c;父类给孩子的方式叫泛化&#xff0c;而继承是站在孩子的角度&#xff0c;儿子继承父类的方式叫继承。 其实上了谭老师大概一章的课程&…