【机器学习-回归算法】

发布于:2025-03-19 ⋅ 阅读:(10) ⋅ 点赞:(0)

机器学习概述

计算机都特别擅长处理重复的任务。所以计算机能够比人类更高效地读取大量的数据、学习数据的特征并从中找出数据的模式
当我们打算用机器学习做什么事情的时候,首先需要的就是数据。因为机器学习就是从数据中找出特征和模式的技术。

这样的任务也被称为机器学习或者模式识别,主要是学习:

回归(regression)

回归就是在处理连续数据时使用的技术(连续数据:因时而异的观测值,比如股价,身高),从这样的数据中学习它的趋势,求出“明天的股价会变为多少”“今后的趋势会怎样”的方法,当我们要预测什么事情的时候,经常会把对预测有主要影响的数据收集起来进行组合,比如今天股价的波动不仅仅取决于过去的股价,还受到经济状况等因素的影响。

分类(classification)

分类就是鉴别一个事务的类别,但是在此之前需要学习大量带标签的数据,告诉机器这个邮件是垃圾信息,这样的标签往往由人工标注,也可取巧的由用户数据里收集,比如验证码中的橘子,亦或是读取用户的黑名单,举报的邮件等等,当学习到一定的准确度时,就可以脱离标签对一个无标签的事物进行分类.只有两个类别的问题称为二分类,有三个及以上的问题称为多分类,比如数字的识别就属于多分类问题。

聚类(clustering)

聚类与分类相似,但聚类与分类的区别在于数据带不带标签,标签也称为正确答案数据。使用有标签的数据进行的学习称为有监督学习,使用没有标签的数据进行的学习称为无监督学习。
回归和分类是有监督学习,而聚类是无监督学习

打个比方:

在这里插入图片描述
想从这表1-2学习到一个学生是否偏科的结论,而是否偏科在已知的数据集里没有体现,那么这就是聚类无监督
在这里插入图片描述
而想从表1-3里学习到是否为垃圾邮件的结论,而结论在已知的数据集里直接表明,这就是有归类有监督,好比做完测试看答案,对着答案而反思(调参)
也可这样说,A1表如果通过其他科成绩预测物理分数的取值段,由于物理成绩是本身就有的,那么又变成了归类问题

回归章节(以投入的广告费带来的流量即点击量的二元关系为例)

点击量经常变化,投入同样的广告费未必能带来同样的点击量。根据广告费和实际点击量的对应关系数据,可以将两个变量用下面的图展示出来(图2-1)。这些数据称为训练数据(已知数据)。
在这里插入图片描述
假如我们透入200元(虽然图2-1中没有x=200的点),我们可以大致预测点击量y=500,这是我们人眼观测所有数据点后,由心中的直线大致推断而来,即是根据现有的数据,在图上标出了“大概在这里”:
在这里插入图片描述
这就是机器学习。你所做的事情正是从数据中进行学习,然后给出预测值
把图想象为函数。只要知道通过图中各点的函数的形式,就能根据广告费得知点击量了。
注意“点击量中含有噪声”,所以函数并不能完美地通过所有的点,即一次函数不能完全的囊括所有点,存在偏差
在这里插入图片描述
一次函数:y=ax+b,a为斜率,b为截距,而在统计学领域,人们常常使用θ来表示未知数和推测值。
采用θ加数字下标的形式,是为了防止当未知数增加时,表达式中大量出现a、b、c、d…这样的符号。这样不但不易理解,还可能会出现符号本身不够用的情况,所以有预测函数:
在这里插入图片描述
这是一个含有参数θ,并且和变量x相关的函数,fθ(x)就是为了研究y的值才建立的函数
接下来我们就要使用机器学习来求出正确的θ0和θ1的值
首先我们将训练数据中的广告费x1,x2,…代入fθ(x),把得到的点击量fθ(x)与训练数据中的点击量y相比较,然后找出使二者的差最小的θ。
现在我们假定 θ0=1、θ1=2,那么对比算出的f与已知的y,如表2-2:
在这里插入图片描述
我们希望出现的最理想的情况是y与fθ(x)的值一致,即y−fθ(x)=0。这就是说y和fθ(x)之间的误差为0.
现实中,我们所做的只能是让所有点的误差之和尽可能地小(因为噪声的原因,几乎不存在一个一次函数能通过任意点)
而误差最直观的展示是:
在这里插入图片描述

最小二乘法

假设有n个训练数据,那么它们的误差之和可以用这样的表达式表示,最终我们要调参θ,使得误差值E最小。
在这里插入图片描述
稍作解释:
E为目标函数,是误差的英语单词Error的首字母。
(i)是索引,指的是第i个数据点
y是实际的值(已知的标签值),f是假定了θ参数后的值,我们所做的就是调参,使得y与f差值误差尽可能的小
1/2常数项与2次方对应,求导后消失,任意的添常数行为往往是为了最终处理后的表达式尽可能的简洁,只要乘以正的常数,函数的形状就会被横向压扁或者纵向拉长,但最值点不变(提到最值是因为调参的本质就是找到最小的误差,这一行为就是最优化问题,要应用到微分求最值,在后续的内容中还会有似然函数取对数函数单调性不变从而在保证最值点不变的情况下尽可能的简化微分操作)

每一组数据的差值最后必须要平方后才能相加Σ,因为如果上一组数据差值为-5.这一组数据差值为5,那么最终和为0,但这是不允许的,我们要将误差直观的展现出来,不允许抵消的行为,当然绝对值也可取正避免抵消,但求导不好求
在这里插入图片描述

最小二乘法一边随意修改θ的值,一边计算E(θ)并与之前的值相比较太麻烦
引入微分有最速下降法(梯度下降法):

最速下降法(梯度下降法)

比如有一个表达式为g(x)=(x−1)^2的二次函数(图2-10),它的最小值是g(x)=0,出现在x =1时。
导数就是微分后的函数,g(x)的导数为
在这里插入图片描述
所以有增减表:
在这里插入图片描述
在这里插入图片描述
在x=3这一点,为了使g(x)的值变小,我们需要向左移动x,也就是必须减小x
在这里插入图片描述
只要向与导数的符号相反的方向移动x,g(x)就会自然而然地沿着最小值的方向前进了
在这里插入图片描述
解释:
A:=B这种写法是通过B来定义A
η即学习率,是一个正常数,读作“伊塔”。根据学习率的大小,到达最小值的更新次数也会发生变化。换种说法就是收敛速度会不同。有时候甚至会出现完全无法收敛,一直发散的情况。
比如η=1,从x=3开始,那么对于x:
在这里插入图片描述
在这里插入图片描述
此时x的迭代陷入死循环,但是若调整学习率η=0.1(原来为1),那么:
在这里插入图片描述
在这里插入图片描述
不难得出:
如果η较大,那么x:=x−η(2x−2)会在两个值上跳来跳去,甚至有可能远离最小值。这就是发散状态。
而当η较小时,移动量也变小,更新次数就会增加,但是值确实是会朝着收敛的方向而去

所以调整θ要微分,并引入学习率,当我们知道这一点后,再反观目标函数E,发现有包含两个参数的f在其中,即目标函数是拥有θ0和θ1的双变量函数,所以不能用普通的微分,而要用偏微分。如此一来,更新表达式:
在这里插入图片描述
而对E进行偏微分时,因为内部有f,f内有变量,所以要复合函数微分

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据链式法则有:
在这里插入图片描述
分别计算两个偏导数,最后相乘:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对θ1的微分同理,但是只需要更新第二部分,u对v微分部分是一样的:
在这里插入图片描述
在这里插入图片描述
最终我们得到了θ0与θ1的更新表达式:
在这里插入图片描述
根据这个表达式来更新θ0和θ1,就可以找到正确的一次函数fθ(x),即确定了参数θ后,带入训练集的x,得到的f与训练集中的y误差是最小的,由此可以得到带入训练集外的x,误差也会尽可能的小,回归算法此时完成了预测的作用

多项式回归:

一次函数是直线,但并不是所有的数据都满足一次直线的线性回归,可能要多项式曲线的回归拟合的更好:

在这里插入图片描述
增加函数中多项式的次数,然后再使用函数的分析方法被称为多项式回归:
在这里插入图片描述
在这里插入图片描述
引入θ2,将f定义为二次函数,那么就可以表示上述的曲线了
因为增加了参数θ2,所以需要对该参数推导更新表达式,方法一样,就是对θ2偏微分(E对f导是一样的,我们关注f对θ2导):
在这里插入图片描述
在这里插入图片描述
不难得到规律:
先写出目标函数:
在这里插入图片描述
预测函数选次数(2次):
在这里插入图片描述
在这里插入图片描述
然后求偏导,其中U对V偏导不变,结果为:
在这里插入图片描述
我们只需计算V对θi偏导
在这里插入图片描述
在这里插入图片描述
(注意图中的η不是E对f偏导的结果部分,而是学习率,因为固定要将学习率与偏导相乘,所以画在了一起,本质就是根据调整导数变化幅度,最终影响收敛性质(快慢,发散还是收敛))
更一般地,可以引入更多的项数,n项多项式一共有n+1的参数,每一个x项有一个参系数θ,并且最终表达式要加一个θ0
在这里插入图片描述
但并不是项越多越好,可能会引发过拟合的问题.

多重回归:解决实际生活中变量超过2个的复杂问题

与多项式回归的区别:
多项式回归是增加预测函数f的次数来更好的拟合数据集并预测
多重回归是影响f的变量不止一个,而是多个,故增加了预测函数f的项数,比如点击量带来的流量不仅仅受广告费影响,还有广告的时间段和位置等等,过于细小或者没有出现在f中但确实有影响的因素成为噪声
这类问题往往达到三个变量时(需要四维展示),无法可视化
设广告费为x1、广告栏的宽为x2、广告栏的高为x3,那么fθ可以
表示如下:(此处是三元一次,像这样包含了多个变量(元)的回归称为多重回归)
在这里插入图片描述
与多项式回归的区别(一元二次)
在这里插入图片描述
去求参数θ0,··· ,θ3就是分别求目标函数对θ0,··· ,θ3的偏微分,然后更新参数
而这样的表达式可以简化,通过向量
在这里插入图片描述
每次都像这样写n个x很麻烦,所以我们还可以把参数θ和变量x看作列向量(向量有大小和方向,并要用箭头来表示)
在这里插入图片描述
为了用内积来简化表达式,我们要对齐向量维度(任意加一个参数定义为1,刚好凑成常数项)
在这里插入图片描述

在这里插入图片描述
之前用多项式表示的fθ,可以像这样用向量(py一维数组)来表示。
在这里插入图片描述
设u=E(θ)、v =fθ(x),。u对v微分的部分是一样的,所以只需要求v对θj的微分就好了
在这里插入图片描述
在这里插入图片描述
那么第j个参数的更新表达式就是:
在这里插入图片描述

最速下降法就是对所有的训练数据都重复进行计算
训练数据越多,循环次数也就越多,那么计算起来非常花时间
并且,最致命的缺点就是,最速下降法很容易陷入局部最优

在上述回归内容中,我们使用的是平方误差目标函数E。这个函数形式简单,所以用最速下降法也没有问题。
在这里插入图片描述
在这里插入图片描述
用最速下降法来找函数的最小值时,必须先要决定从哪个x开始找起。
之前用g(x)说明的时候是x=−1开始的
选用随机数作为初始值的情况比较多。不过这样每次初始值都会变,进而导致陷入局部最优解的问题:
在这里插入图片描述
在函数初始值左边的变化率为正,我们朝着相反方向走,即往左边走才能变小,到了第二个点处发现左边递减右边递增,就停下了,实际上这只是一个极值.

再看梯度下降/最速下降法的参数更新表达式,它使用了所有训练数据的误差:
在这里插入图片描述
由此引出随机梯度下降算法:

随机梯度下降算法

随机选择一个训练数据,并使用它来更新参数,表达式中的k就是被随机选中的数据索引,也就是去掉了全部取和Σ,转而随机选择一个数据
在这里插入图片描述
最速下降法更新1次参数的时间,随机梯度下降法可以更新n次。
由于训练数据是随机选择的,更新参数时使用的又是选择数据时的梯度,所以不容易陷入目标函数的局部最优解

小批量(mini-batch)梯度下降法:

随机选择m个训练数据来更新参数的做法,设随机选择m个训练数据的索引的集合为K:
在这里插入图片描述
这里又出现了Σ,只是取和的范围是随机选择的m个数据组成的集合

不管是随机梯度下降法还是小批量梯度下降法,我们都必须考虑学习率η。把η设置为合适的值是很重要的。

案例:

安装Matplotlib 与PYQT5来进行数据可视化:

安装
pip install matplotlib
验证
import matplotlib
print(matplotlib.__version__)  # 输出版本号,例如 "3.7.1"
安装
pip install PyQt5

一次函数实现:

训练数据:click.csv(同一文件夹内)

x,y
 235,591
 216,539
 148,413
 35,310
 85,308
 204,519
 49,325
 25,332
 173,498
 191,498
 134,392
 99,334
 117,385
 112,387
 162,425
 272,659
 159,400
 159,427
 59,319
 198,522
import numpy as np
import matplotlib
matplotlib.use('Agg')
# matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt

# 读入训练数据
train = np.loadtxt('click.csv', delimiter=',', skiprows=1)
train_x = train[:, 0]
train_y = train[:, 1]
# 绘图
plt.plot(train_x, train_y, 'o')
# plt.show()
plt.savefig('AI1.png')  # 保存为图片,而非 plt.show()

在这里插入图片描述

实现预测函数f和目标函数E

在这里插入图片描述
对θ0与θ1初始化,先用随机数作为初始值

# 参数初始化
theta0 = np.random.rand()
theta1 = np.random.rand()

# 预测函数
def f(x):
    return theta0 + theta1 * x

# 目标函数
def E(x, y):
    return 0.5 * np.sum((y - f(x)) ** 2)

标准化

然后将训练数据标准化(减去均值除标准差(方差平方根)),也称z-score规范化
在这里插入图片描述

# 训练集标准化
mu = train_x.mean()
sigma = train_x.std()
def standardize(x):
    return (x - mu) / sigma
train_z = standardize(train_x)
#生成標準化後的圖,發現橫軸的刻度改變
plt.plot(train_x, train_y, 'o')
plt.savefig('AI2.png') 

实现参数更新表达式

(参数的更新必须同时进行。θ0更新结束后准备更新θ1时,不能使用更新后的θ0,而必须要使用更新前的θ0。)
在这里插入图片描述
η设置为10^-3试一试:
对目标函数进行微分,不断重复参数的更新.这个重复次数可以指定,也可比较更新前后的目标函数值,大致没什么变化就结束
在这里插入图片描述
…(循环次数和误差的减少量在每次执行时都不一样,这是因为参数的初始值是随机的)
在这里插入图片描述

# 学习率
ETA = 1e-3

# 误差的差值
diff = 1

# 更新次数
count = 0

# 重复学习
error = E(train_z, train_y)
while diff > 1e-2:
    # 更新结果保存到临时变量
    tmp0 = theta0 - ETA * np.sum((f(train_z) - train_y))
    tmp1 = theta1 - ETA * np.sum((f(train_z) - train_y) * train_z)
    # 更新参数
    theta0 = tmp0
    theta1 = tmp1
    # 计算与上一次误差的差值
    current_error = E(train_z, train_y)
    diff = error - current_error
    error = current_error
    # 输出日志
    count += 1
    log = ' 第 {} 次: theta0 = {:.3f}, theta1 = {:.3f}, 差值 = {:.4f}'
    print(log.format(count, theta0, theta1, diff))

确认结果,我们用图来展示一下训练数据和fθ(x):

 # 绘图确认
x = np.linspace(-3, 3, 100)
 plt.plot(train_z, train_y, 'o')
 plt.plot(x, f(x))
 plt.show()

在这里插入图片描述

总体代码:

import numpy as np
import matplotlib

matplotlib.use('Agg')
# matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt

# 读入训练数据
train = np.loadtxt('click.csv', delimiter=',', skiprows=1)
train_x = train[:, 0]
train_y = train[:, 1]
# # 绘图
# plt.plot(train_x, train_y, 'o')
# # plt.show()
# plt.savefig('AI1.png')  # 保存为图片,而非 plt.show()

# 训练集标准化
mu = train_x.mean()
sigma = train_x.std()

def standardize(x):
    return (x - mu) / sigma


train_z = standardize(train_x)
#
# plt.plot(train_z, train_y, 'o')
# plt.savefig('AI2.png')

# 参数初始化
theta0 = np.random.rand()
theta1 = np.random.rand()


# 预测函数
def f(x):
    return theta0 + theta1 * x


# 目标函数
def E(x, y):
    return 0.5 * np.sum((y - f(x)) ** 2)


# 学习率
ETA = 1e-3

# 误差的差值
diff = 1

# 更新次数
count = 0

# 重复学习,直到误差的差值小于0.01为止,重复参数更新
error = E(train_z, train_y)
while diff > 1e-2:
    # 更新结果保存到临时变量
    tmp0 = theta0 - ETA * np.sum((f(train_z) - train_y))
    tmp1 = theta1 - ETA * np.sum((f(train_z) - train_y) * train_z)
    # 更新参数
    theta0 = tmp0
    theta1 = tmp1
    # 计算与上一次误差的差值
    current_error = E(train_z, train_y)
    diff = error - current_error
    error = current_error
    # 输出日志
    count += 1
    log = ' 第 {} 次: theta0 = {:.3f}, theta1 = {:.3f}, 差值 = {:.4f}'
    print(log.format(count, theta0, theta1, diff))

#绘图确认
x = np.linspace(-3, 3, 100)
plt.plot(train_z, train_y, 'o')
plt.plot(x, f(x))
plt.savefig('AI3.png')  # 保存为图片,而非 plt.show()

多项式回归实现:

在这里插入图片描述
使刚才的代码支持多项式回归,只需要增加参数θ2,并替换预测函数
将参数θ和训练数据当作向量:
在这里插入图片描述并且训练数据有很多,则将其变为矩阵
在这里插入图片描述
矩阵乘法则可以表示预测函数f:
在这里插入图片描述

# 参数初始化
theta = np.random.rand(3)

# 创建训练数据的矩阵
def to_matrix(x):
    return np.vstack([np.ones(x.shape[0]), x, x ** 2]).T

X = to_matrix(train_z)

# 预测函数
def f(x):
    return np.dot(x, theta)

参数更新表达式(j=012三个):

在这里插入图片描述
由于Σ部分可以由向量内积实现:
在这里插入图片描述
j=0时有:
在这里插入图片描述
但是有三个参数j=0,1,2
在这里插入图片描述
最终有参数更新表达式(一次更新所有)
在这里插入图片描述

# 重复学习,直到误差的差值小于0.01为止,重复参数更新
error = E(X, train_y)
while diff > 1e-2:
    # 更新参数
    theta = theta - ETA * np.dot(f(X) - train_y,X)
    # 计算与上一次误差的差值
    current_error = E(X,train_y)
    diff = error - current_error
    error = current_error

总体代码

import numpy as np
import matplotlib

matplotlib.use('Agg')
# matplotlib.use('QT5Agg')
import matplotlib.pyplot as plt

# 读入训练数据
train = np.loadtxt('click.csv', delimiter=',', skiprows=1)
train_x = train[:, 0]
train_y = train[:, 1]
# # 绘图
# plt.plot(train_x, train_y, 'o')
# # plt.show()
# plt.savefig('AI1.png')  # 保存为图片,而非 plt.show()

# 训练集标准化
mu = train_x.mean()
sigma = train_x.std()

def standardize(x):
    return (x - mu) / sigma


train_z = standardize(train_x)
#
# plt.plot(train_z, train_y, 'o')
# plt.savefig('AI2.png')

# 参数初始化
theta = np.random.rand(3)

# 创建训练数据的矩阵
def to_matrix(x):
    return np.vstack([np.ones(x.shape[0]), x, x ** 2]).T

X = to_matrix(train_z)

# 预测函数
def f(x):
    return np.dot(x, theta)


# 目标函数
def E(x, y):
    return 0.5 * np.sum((y - f(x)) ** 2)


# 学习率
ETA = 1e-3

# 误差的差值
diff = 1

# 更新次数
count = 0

# 重复学习,直到误差的差值小于0.01为止,重复参数更新
error = E(X, train_y)
while diff > 1e-2:
    # 更新参数
    theta = theta - ETA * np.dot(f(X) - train_y,X)
    # 计算与上一次误差的差值
    current_error = E(X,train_y)
    diff = error - current_error
    error = current_error


#绘图确认
x = np.linspace(-3, 3, 100)
plt.plot(train_z, train_y, 'o')
plt.plot(x, f(to_matrix(x)))
plt.savefig('AI4.png')  # 保存为图片,而非 plt.show()

在这里插入图片描述
以重复次数为横轴、均方误差为纵轴来绘图,应该还会看到曲线不断下降,因为随着迭代次数的增加,均方误差会越来越小:

均方误差表达式:

在这里插入图片描述

在这里插入图片描述

...
# 均方误差
def MSE(x, y):
    return (1 / x.shape[0]) * np.sum((y - f(x)) ** 2)

 # 用随机值初始化参数
theta = np.random.rand(3)

# 均方误差的历史记录
errors = []

# 误差的差值
diff = 1

# 重复学习
errors.append(MSE(X, train_y))
while diff > 1e-2:
   theta = theta - ETA * np.dot(f(X) - train_y, X)
   errors.append(MSE(X, train_y))
   diff = errors[-2] - errors[-1]
# 绘制误差变化图
x = np.arange(len(errors))
plt.plot(x, errors)
plt.savefig('AI5.png')

随机梯度下降算法实现

随机梯度下降法的做法是使用下面表达式来更新参数,表达式中的k是随机选择的。
在这里插入图片描述
现在有了训练数据的矩阵X,把行的顺序随机地予以调整,然后重复应用更新表达式就行了。

更新代码

# 参数初始化
theta = np.random.rand(3)

# 均方误差的历史记录
errors = []

# 误差的差值
diff = 1

# 重复学习
errors.append(MSE(X, train_y))
while diff > 1e-2:
    # 为了调整训练数据的顺序,准备随机的序列
    p = np.random.permutation(X.shape[0])
    # 随机取出训练数据,使用随机梯度下降法更新参数
    for x, y in zip(X[p,:], train_y[p]):
        theta = theta - ETA * (f(x) - y) * x
     # 计算与上一次误差的差值
    errors.append(MSE(X, train_y))
    diff = errors[-2] - errors[-1]

在这里插入图片描述

多重回归实现:

与多项式回归同理使用矩阵,但是要注意对多重回归的变量进行标准化时,必须对每个参数都进行标准化。
如果有变量x1、x2、x3,就要分别使用每个变量的平均值和标准差进行标准化。
在这里插入图片描述
附注:
Iris数据集(统计学领域有一个著名的数据集)
MNIST数据集(收集了大量手写的数字图片,以及图片实际的数字信息)