从零开始的Python机器学习指南(二)——监督学习之OLS回归

2023-11-06

介绍

本博客将结合样例介绍监督学习/Supervised Learning/SL下的第一大分支:回归/Regression

开始前的准备

开始前,请先确保你的python环境中有以下包:
pandasnumpysklearn

本文的所有代码可在AnacondaJupyter Lab里运行。

正文

首先来理解下:为什么回归/Regression是一种监督学习?这个问题的本质是什么?

我们首先要理解回归问题的本质。简单来说,回归问题的本质是,对于一个映射 f f f,
f : R n ↦ R f:\mathbb{R}^n\mapsto \mathbb{R} f:RnR
,我们有它定义域/Domain对应域/Codomain的一些数组。我们想要通过这些已有的数据来找到一个最符合这些数据的模型/回归函数。

换个角度看,对于每一条数据,该映射的定义域可以理解为特征集/Feature Set,其对应域可理解为标签集/Label Set。我们要找的就是未知的映射函数,对应着机器学习里的黑箱模型/Blackbox Model

但是要把它变成一个完全的监督学习问题,我们还需要定义下我们要优化的目标是什么。既然我们要找到最符合数据的映射,我们可以考虑最小化误差平方和/Sum of Squared Errors/SSE。对于一个有 m m m条数据的数据集和一个对应的映射 f f f,该映射对于该数据集的误差平方和定义为

ℓ = ∑ i = 1 m ( y ( i ) − f ( x ( i ) ) ) 2 \ell = \sum_{i=1}^m \Big(y^{(i)} - f\big({\bf x}^{(i)}\big)\Big)^2 =i=1m(y(i)f(x(i)))2

,也就是把每一条数据的真实标签和映射的预测标签的差进行平方后求和。如果我们把误差平方和作为监督学习的损失函数/Loss Function,则该回归问题可被称为普通最小二乘法线性回归问题/OLS Linear Regression。我们要做的就是找到映射 f f f,使得误差平方和最小。

我们把这个问题更加严谨地下个定义。让 x {\bf x} x作为特征集, w \bf w w作为每个特征的权重集,则对于线性映射
f ( x ; w ) = w T ⋅ x = w 0 x 0 + w 1 x 1 + . . . + w n x n f({\bf x; w}) = {\bf w}^T\cdot{\bf x}=w_0x_0+w_1x_1+...+w_nx_n f(x;w)=wTx=w0x0+w1x1+...+wnxn
,我们要最小化的损失函数 ℓ \ell 可定义为:
ℓ ( w ) = ∑ i = 1 m ( y ( i ) − f ( x ( i ) ) ) 2 = ∑ i = 1 n ( y ( i ) − w T ⋅ x ( i ) ) 2 = ∑ i = 1 n ( y ( i ) − y ^ ( i ) ) 2 . \ell({\bf w}) = \sum_{i=1}^m \Big(y^{(i)} - f\big({\bf x}^{(i)}\big)\Big)^2 = \sum_{i=1}^n \Big(y^{(i)} - {\bf w}^T\cdot{\bf x}^{(i)}\Big)^2 = \sum_{i=1}^n \Big(y^{(i)} - \hat{y}^{(i)}\Big)^2. (w)=i=1m(y(i)f(x(i)))2=i=1n(y(i)wTx(i))2=i=1n(y(i)y^(i))2.
也就是说,线性回归在监督学习下的本质可以理解为一个优化问题:
w = arg ⁡ min ⁡ w ℓ ( w ) . {\bf w} = \arg\min_{{\bf w}} \ell({\bf w}). w=argwmin(w).

那我们怎么能找到最合适的线性回归函数?
首先,这个问题是有唯一且最优解的,属于P/NP问题中的P。单从这一点来说,线性回归在机器学习界是相对简单和容易解决的问题了。

找到普通最小二乘法线性回归问题/OLS Linear Regression最优解的过程只需要依赖一些线性代数就能解决。推导过程较为繁琐和复杂,博主不在此展示,感兴趣的同学们可以自己搜索了解一下。但结论是,最符合数据(或者最小化误差平方和)的特征权重集 w ∗ {\bf w}^* w,可以通过以下矩阵乘法得出:
w ∗ = ( X T X ) − 1 X T Y {\bf w}^* = ({\bf X}^T {\bf X})^{-1}{\bf X}^T{\bf Y} w=(XTX)1XTY
,其中 X {\bf X} X是特征集(大小为特征种类乘以数据行数), Y {\bf Y} Y是有 m m m个元素的向量(标签集)。对于计算机来说,这种矩阵乘法是小case啦。

但是,没有学习的过程,还算机器学习吗?
很多同学,包括博主自己,在学习到这里的时候都有这个问题。模型似乎没有迭代和进步的过程,而是一步从零跳跃到最优解。但严格意义上来说,学习的过程是有的。模型获取了特征集和标签集,并且用这些数据提高了自己对数据的泛化能力。虽然没有一步一步走向更好的性能,但是电脑仍旧以有效的方式学习到了数据的特征。所以总而言之,线性回归也是一种机器学习。

对于一个有未知回归函数的数据集,如果我们通过上述方法取得了最优回归函数(即最小化误差平方和),那么我们对于新的数据就有一定的预测能力。

那线性回归有什么应用场景吗?
很多啊,比如:

  1. 房价预测。如果你有过去房市的数据(比如年平均气温,日平均光照时间,离最近便利店的距离,等等)以及每年的房价,并且你如果认为房价和这些房间特征有(多维)线性关系,那么你可以用OLS来预测房产的价格。
  2. 股票分析。类似房价,股票的一些特征(比如公司净利润,开盘时间,涨停次数等等)也是对于股价的重要指标。但要注意的是,由于时间和一些无法获得的数据(比如公司老板打算摆烂的概率)对股价的影响非常大,简单的线性回归模型可能不太适合预测股票这如此复杂的问题。
  3. 顾客终身价值预测(CLV)。一个顾客给一个企业带来的收益总和很大程度上与顾客的收入水平、年龄、日均消费等信息挂钩。如果有顾客信息作为特征集和对应的对企业的总收益作为标签集,那么就可以用这些信息预测它们的CLV。

代码

理解了原理后,我们可以用python实现上述的回归学习算法。

首先,我们来生成一些简单的数据。

import numpy as np # 用来进行一些数学运算
import pandas as pd # 用来用数据框的方式储存数据
import matplotlib # 用来画图的
import matplotlib.pyplot as plt
from sklearn import linear_model # 我们需要的模型

# X和Y包含生成的数据。X可以理解为特征/Features,Y可以看作标签/Label
# 我们要做的就是训练一个模型,使它能够通过X准确预测Y的对应值
# coeff包含生成函数所使用的系数
X, Y, coeff = skd.make_regression(n_samples=5000, n_features=2, 
                                  noise=5.0, 
                                  coef=True,
                                  random_state=114514)

print(f"特征集大小为{X.shape},标签集大小为{Y.shape}。")
# 特征集大小为(5000,2),标签集大小为(5000,)。

在生成好数据后,我们可以检查下数据内容:

from mpl_toolkits.mplot3d import Axes3D # 一个用来画3D图形的库

fig = plt.figure()
plot3d = fig.gca(projection='3d') # 调用matplotlib的对象
plot3d.view_init(elev=15., azim=35) # 观察角度

x1 = X[:, 0] # 选中X里第0列的所有行
x2 = X[:, 1] # 选中X里第1列的所有行

# 画出数据散点。注意x1,x2,Y的行数要相同
plot3d.scatter3D(x1, x2, Y, c=Y, cmap='Greens', label='Noised Samples');

# 画出真实参考平面
x1min, x1max = int(np.floor(min(x1))), int(np.ceil(max(x1)))
x2min, x2max = int(np.floor(min(x2))), int(np.ceil(max(x2)))
x1plane = np.linspace(x1min, x1max, 2000)
x2plane = np.linspace(x2min, x2max, 2000)
xx1, xx2 = np.meshgrid(range(x1min, x1max), range(x2min, x2max))
x12_coord = np.array([xx1, xx2])
y = coeff[0] * x12_coord[0] + coeff[1] + x12_coord[1]
surf = plot3d.plot_surface(xx1, xx2, y, alpha=0.2, label="True model")

# 给3D图形加上标签
surf._facecolors2d = surf._facecolor3d
surf._edgecolors2d = surf._edgecolor3d 
plot3d.legend()

# 画出坐标轴标签
plot3d.set_xlabel('$x_1$')
plot3d.set_ylabel('$x_2$')
plot3d.set_zlabel('$y$')

plt.show()

我们得到了以下图形:
模型图形
接下来就是用sklearn训练线性回归模型了。代码非常简单,只有两行。

# 创造线性回归对象
OLS = linear_model.LinearRegression(fit_intercept=True)

# 训练模型
OLS.fit(X, Y)

我们可以手动比较一下真实模型和训练模型的差距:

print('真实模型参数:')
print('\tw0: {:.2f}, w1: {:.2f}, w2: {:.2f}'.format(0, *coeff))
print('训练模型参数:')
print('\tw0: {:.2f}, w1: {:.2f}, w2: {:.2f}'.format(0, *OLS.coef_))
'''
真实模型参数:
	w0: 0.00, w1: 12.56, w2: 7.64
训练模型参数:
	w0: 0.00, w1: 12.58, w2: 7.77
'''

更加科学和全面的方法是用交叉验证/Cross-validation。这种方法的原理是:每次把数据随机分成训练集和验证集,并代入模型评估模型在训练和验证下的错误。数学上来说,这种方法缩小了随机取样验证的随机性,是对于模型性能更加全面的评估。代码如下:

from sklearn.model_selection import cross_validate

# 这里的cv指的是把数据分成几份。比如,cv=10就是把数据分成十份,取一份作为验证集,九份作为训练集
cv_results = cross_validate(OLS, X, Y, cv=10, scoring="r2",
                            return_train_score=True)

print('Mean test score: {:.3f} (std: {:.3f})'
      '\nMean train score: {:.3f} (std: {:.3f})'.format(
                                              np.mean(cv_results['test_score']),
                                              np.std(cv_results['test_score']),
                                              np.mean(cv_results['train_score']),
                                              np.std(cv_results['train_score'])))
'''
Mean test score: 0.894 (std: 0.009)
Mean train score: 0.895 (std: 0.001)
'''

我们的模型有89.4%的验证准确率和89.5%的训练准确率,总体来说是一个较为准确、没有过拟合也没有欠拟合的模型。

我们还可以用训练好的模型进行预测,比如:

print(OLS.predict([[42,24]])[0]) # 模型预测结果
print(coeff[0]*42+coeff[1]*24) # 真实值
'''
714.8098105630355
710.6836496552116
'''

可以观察到,虽然保证了最优解,但是模型的预测仍不是百分百准确的。这个现象的成因来源于数据集的随机噪声/Random Noise,也就是数据本身也没有百分百符合其潜在模型。有些时候我们可以减小噪声对算法的影响。比如,对于图像处理来说,我们可以使用高斯滤波/Gaussian Filter来减少图像噪点对学习算法的干扰。

结语

在下一篇博客博主会介绍如何用ML方法解决简单的分类/Classification问题。有任何问题和建议请随时评论或私信。码字不易,喜欢博主内容的话请点赞支持!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从零开始的Python机器学习指南(二)——监督学习之OLS回归 的相关文章

  • 默认情况下在 Jupyter 笔记本中配置第一个单元

    有没有办法为 Jupyter 笔记本中的特定 python 内核配置默认的第一个单元 我同意默认的 python 导入违背了良好的编码实践 那么 我可以配置笔记本 使新的 python 笔记本的第一个单元始终是 import numpy a
  • python的_random是什么?

    如果你打开random py看看它是如何工作的 它的类Random子类 random Random import random class Random random Random Random number generator base
  • 从终端调用时 uvicorn 不工作

    我尝试通过 pip3 在系统上安装 uvicorn 这有效 但是我无法从命令行运行相同的命令 有关如何解决此问题的任何指示 Requirement already satisfied uvicorn in home vhawk19 loca
  • cv2.face.mindistancepredictcollector() 错误

    我已经安装了带有额外模块的 opencv 3 1 0 但是当我尝试使用 gt gt gt s cv2 face MinDistancePredictCollector 它返回一个错误 Traceback most recent call l
  • 确定Python模块中的函数是否可用

    我正在研究一些使用Python套接字的代码socket fromfd http docs python org library socket html socket fromfd功能 但是 此方法并非在所有平台上都可用 因此我正在编写一些后
  • Python变量赋值问题

    a b 0 1 while b lt 50 print b a b b a b 输出 1 2 4 8 16 32 wheras a b 0 1 while b lt 50 print b a b b a b 输出 正确的斐波那契数列 1 1
  • 如何抑制 pyinstaller 生成的可执行文件窗口中的所有警告

    我已经使用 pyinstaller 从 python 文件生成了可执行文件 该程序按其应有的方式工作 但在我想隐藏的窗口中出现了一条警告消息 当 python 文件在 IDE 中运行时 以下行会抑制所有警告消息 warnings filte
  • Cython:为什么 size_t 比 int 快?

    更改某些 Cython 变量的类型int输入size t可以显着减少某些功能的时间 30 但我不明白为什么 例如 cimport numpy as cnp import numpy as np def sum int cnp int64 t
  • 不重复的Python组合

    我有一个数字列表 我想从中进行组合 如果我有清单 t 2 2 2 2 4 c list itertools combinations t 4 结果是 2 2 2 2 2 2 2 4 2 2 2 4 2 2 2 4 2 2 2 4 但我想得到
  • 在 (i)python 脚本中从 jupyter 内核获取输出

    我想从单个 ipython 会话中打开多个内核 在这些内核上运行代码 然后收集结果 但我不知道如何收集结果 甚至不知道如何查看 stdout stderr 我怎样才能做这些事情呢 到目前为止我所得到的 我已经使用如下代码管理了前两个步骤 打
  • Python3.1中的视图?

    Python3 1中的视图到底是什么 它们的行为方式似乎与迭代器类似 并且它们也可以具体化为列表 迭代器和视图有何不同 据我所知 视图仍然附加到创建它的对象上 对原始对象的修改会影响视图 来自docs http docs python or
  • 如何在树莓派上更新到最新的 python 3.5.1 版本?

    我昨天拿到了 Raspberry Pi 我已经在尝试用它来编写代码了 我有一个计划在其上运行的程序 但它仅与 Python 版本 3 5 0 或 3 5 1 兼容 并且我在互联网上找到的所有内容似乎都已经过时 与 Python 2 有关 或
  • 如何将时间间隔划分为不同长度的部分?

    我有一个从 0 到t 我想把这个区间分成一个以2 25 2 25 1 5为周期的累积序列 方法如下 input start 0 stop 19 output sequence 0 2 25 4 5 6 8 25 10 5 12 14 25
  • 多线程写入文件

    前几天刚开始使用 python 对多线程的整个概念还很陌生 我在多线程时写入文件时遇到问题 如果我按照常规方式执行此操作 它会不断覆盖正在写入的内容 使用 5 个线程写入文件的正确方法是什么 不降低性能的最佳方法是在所有线程之间使用队列 每
  • import numpy 和 import numpy as np 之间的区别

    我明白 如果可能的话 应该使用 import numpy as np 这有助于避免由于命名空间引起的任何冲突 但我注意到虽然下面的命令有效 import numpy f2py as myf2py 以下不 import numpy as np
  • 收到的标签值 1 超出了 [0, 1) 的有效范围 - Python、Keras

    我正在使用具有张量流背景的 keras 开发一个简单的 cnn 分类器 def cnnKeras training data training labels test data test labels n dim print Initiat
  • 在 Django/python 中,如何将内存缓存设置为无限时间?

    cache set key value 9999999 但这并不是无限的时间 def get memcache timeout self timeout Memcached deals with long gt 30 days timeou
  • 从 sublime_plugin.WindowCommand 获取当前文件名

    我开发插件sublime text 3 并想要获取当前打开的文件路径 absolute1 self window view file name 在哪里self is sublime plugin WindowCommand 但失败了 Att
  • 用于桌面数据库应用程序的 Python 框架

    是否有一个框架可以为Python开发桌面数据库应用程序 一些带有CRUD屏幕的屏幕 我正在寻找类似于 Windows 窗体的东西 能够将 TextField Combos 和其他 UI 隐喻与datasets连接到关系数据库例如 MySQL
  • Python pip 安装错误 [SSL: CERTIFICATE_VERIFY_FAILED]

    我已经尝试解决这个问题有一段时间了 由于某种原因 我陷入了 ssl 问题 并且不知道发生了什么 问题 我已经安装了 python2 7 和 easy install2 7 但是当尝试使用 easy install2 7 安装 pip 时 出

随机推荐

  • 在javascript中,slice与splice的区别

    介绍 众所周知 Javascript中的数组是能够保存多个值的变量 我们有多种方法来处理数组 其中最常用的是 slice 和 splice 有时人们会混淆这两者 因此 在本博客中 我们将了解这两种方法以及它们之间的区别 Slice slic
  • Linux网络编程-客户端与服务器端通信

    Linux网络编程 客户端连接服务器端让我们已经看到了client与server之间是如何建立连接的 接下来介绍它们之间如何建立tcp协议交互通信 先看看服务器端代码 tcpserver c include
  • 15个经典面试问题及回答思路,知乎上转疯了!

    一 简介 Handler机制是一套Android消息传递机制 在Android开发多线程的应用场景中 将工作线程中需更新UI的操作信息 传递到 UI主线程 从而实现 工作线程对UI的更新处理 最终实现异步消息的处理 在Android开发中
  • Linux 初始组(主组)和附加组详解

    Linux 初始组 主组 和附加组详解 介绍 在Linux系统中 每个用户都有一个主组和多个附加组 初始组 主组 是用户创建后默认分配的组 而附加组则可以根据需要进行添加或删除 本文将介绍Linux系统中初始组 主组 和附加组的方法 并探讨
  • tar.xz文件如何解压

    创建或解压tar xz文件的方法 习惯了 tar czvf 或 tar xzvf 的人可能碰到 tar xz也会想用单一命令搞定解压或压缩 其实不行 tar里面没有征对xz格式的参数比如 z是针对 gzip j是针对 bzip2 创建tar
  • vue el-table 树形数据懒加载每次点击子集父级只展示一行

    功能需求 每次只打开一个同级数据节点展开 之前展开的自动收起 ps 找了好久都没有找到完全符合需求的组件 展示效果 vue el table 数据懒加载实现每次子集只展示一行 实现代码 模板 使用load配合expand change使用
  • IDEA创建SpringBoot无法连接https://start.spring.io

    解决办法 将网址改成 http start spring io
  • 小白学股票基金_4_ETF

    ETF Exchange Traded Funds 中文名称为交易型开放式指数基金 是一种在交易所上市交易的开放式指数基金 兼具股票 开放式指数基金及封闭式指数基金的优势 属于高效的指数化投资工具 也就是说 如果我们买一手科技ETF 就可以
  • Intellij IDEA使用技巧,去掉拼写检查和unused提示

    在setting里面搜索spell将其中的拼写检查的 号去掉 搜索never used 关键字将其中的unused的检查去掉
  • 模块化软件设计

    模块化的基本原理 模块化 Modularity 是在软件系统设计时保持系统内各部分相对独立 以便每一个部分可以被独立地进行设计和开发 这个做法背后的基本原理是关注点的分离 SoC Separation of Concerns 关注点的分离在
  • 第5天-[21天学Python]-Python中自定义函数及调用的方法

    本章内容主要包括 声明函数 调用自定义函数 变量作用域 各种类型的函数参数应用 使用lambda建立匿名函数 Python其他常用内建函数 1 使用函数 1 1 声明函数 在python中 函数必须先声明 然后才能调用它 使用函数时 只要按
  • Multisim14.0仿真(八)LM555制作流水灯

    一 仿真原理图 二 仿真运行效果
  • mysql-数据页结构

    数据页结构 数据删除后记录并没有马上被删除 而是被打上了删除标记 并被记录到一个垃圾链表中 之后若有新纪录来 它们则可能覆盖被删除的记录占用的存储空间 页内数据组成单向链表 且再次进行了分组 每组最后一条数据顺序存储在靠近页尾部的地方 这种
  • react-jsx语法上使用switch匹配不同渲染组件

    这里主要讲的是jsx语法使用switch 的js语句 一般jsx语法执行的是简单的运算和三元表达式 如果想要执行条件判断如if语句和switch语句以及函数等 直接使用是会报错的 这里应该使用函数立即执行的语法写法 如果需要根据不同判断渲染
  • 关于javascript中的函数作用域

    var scope global function f alert log scope 输出 undefined 而不是 global var scope local 变量在这里赋初值 单变量本身在函数体内任何地方军事有定义的 consol
  • vue配置页面预渲染(将页面静态化,便于seo读取)

    在项目中安装prerender spa plugin npm install save prerender spa plugin 找到bulid目录下的webpack prod conf js文件 在其中写入以下内容 在文件的上方写入 co
  • tensorflow学习笔记(四)

    代码学习有点吃力 学习了YOLOv1的代码 主要是训练部分的代码 对yolo的又有了进一步的理解 其文件夹下主要包含py文件为 train py yolo net py pascal voc 下面是比较详细的代码解读 但是还是有一些内容理解
  • 《曾国藩家书》读书手记(修身篇二)

    致诸弟 劝弟谨记进德修业 吾人只有进德 修业两事靠得住 进德 则孝弟仁义是也 修业 则诗文作字是也 这个是说进德修业是很重要的东西 这两个东西是越积累越多的 只增不减的 致诸弟 劝弟切勿恃才傲物 故吾人用功 力除傲气 力戒自满 毋为人所冷笑
  • 基于PaddleOCR开发Auto.js Pro文字识别插件

    目录 目的 准备工作 插件开发 1 项目结构对比 2 插件SDK集成 3 调整assets资源 4 删除无用的Activity文件 5 修改AndroidManifest xml 6 修改Predictor文件 7 修改包名 8 新建OCR
  • 从零开始的Python机器学习指南(二)——监督学习之OLS回归

    介绍 本博客将结合样例介绍监督学习 Supervised Learning SL下的第一大分支 回归 Regression 开始前的准备 开始前 请先确保你的python环境中有以下包 pandas numpy sklearn 本文的所有代