在此前的两节课程中,我们已经介绍了关于线性回归模型的基本概念,并且介绍了一个多元线性回归的损失函数求解方法——最小二乘法。在有了这一些列理论推导之后,本节我们将结合【机器学习-01】机器学习一般建模流程,并首先尝试在一个手动构建的数据集上进行完整的线性回归模型建模。
不过,在进行线性回归的手动建模实验过程中,一如既往我们需要补充很多关于机器学习的基础理论以及一些常用技巧,同时,我们也需要搭建一个更加完善的建模实验环境,因此本节内容总共分为四个小节,分别是:
线性回归的数学模型是
0 线性回归模型介绍
在【机器学习-01】机器学习一般建模流程中我们已经对线性回归模型做了介绍,这里简单描述一下线性回归模型,线性回归的数学模型为:
y
i
=
β
0
+
β
1
x
1
+
.
.
.
+
β
p
−
1
x
p
−
1
+
ε
i
,
i
=
1
,
2
,
.
.
.
,
n
y_i = \beta_0+\beta_1x_1+...+\beta_{p-1}x_{p-1}+\varepsilon_i, i=1,2,...,n
yi=β0+β1x1+...+βp−1xp−1+εi,i=1,2,...,n
上述等式的矩阵形式是
y
=
X
β
+
ε
,
y
∈
R
n
,
X
∈
R
n
∗
p
,
β
∈
R
p
,
ε
∈
R
n
y= X\beta+\varepsilon, y \in R^n, X \in R^{n*p}, \beta \in R^p, \varepsilon \in R^n
y=Xβ+ε,y∈Rn,X∈Rn∗p,β∈Rp,ε∈Rn
其中 Y 是反应变量, X 是解释变量,β 是参数。特别地,当 p = 2 时,该模型收缩为一元线性回归。
求 β 的值有很多方法,大多都是基于让误差,即ϵ 最小的原理。其中典型的方法是最小二乘法,即:
m
i
n
.
∣
∣
Y
−
X
β
∣
∣
min.||Y-X\beta||
min.∣∣Y−Xβ∣∣
随后就可以得出的 β 估计值
β
^
=
(
X
T
X
)
−
1
X
T
y
\hat{\beta} = (X^TX)^{-1}X^Ty
β^=(XTX)−1XTy
1 变量相关性基础理论(协方差矩阵)
变量彼此之间的相关性,是我们探究数据规律的重要手段。尽管相关性理论发源于经典统计理论体系,但对于机器学习,相关性也是数据探索、特征工程环节的重要理论。本节将先介绍关于连续变量相关性的基础理论,并且在该理论基础之上,探讨关于规律捕捉和规律创造的相关话题,为下一节创建数据生成器做准备。
# 科学计算模块
import numpy as np
import pandas as pd
# 绘图模块
import matplotlib as mpl
import matplotlib.pyplot as plt
机器学习的“学习”目标,其实就是数据集中隐藏的数字规律,而又由于这些规律背后代表的是某些事物的真实属性或者运行状态,因此这些规律是具备指导生产生活事件意义的有价值的规律,这也是机器学习算法价值的根本。
- 相关系基本解释与相关系数计算公式
当然,对于不同数据集来说,是否具备规律、以及规律隐藏的深浅都不一样。对于模型来说,擅长挖掘的规律、以及规律挖掘的能力也都各不相同。而对于线性回归来说,捕捉的实际上是数据集的线性相关的规律。所谓线性相关,简单来说就是数据的同步变化特性。例如此前数据集:
Whole weight | Rings |
---|---|
1 | 2 |
3 | 4 |
特征和标签就存在着非常明显的同步变化的特性:第二条样本特征增加2、对应标签也增加2,当然,这也就是线性模型本身可解释性的来源——体重越大的鲍鱼、年龄越大,并且体重每增加2、年龄也增加2。这种同步变化特性用更加专业的角度来描述就是变量之间的相关性。这种相关性可以用一个计算公式算得,也就是相关性系数计算公式:
C
o
r
r
e
l
a
t
i
o
n
=
C
o
v
(
X
,
Y
)
V
a
r
(
X
)
∗
V
a
r
(
Y
)
Correlation = \frac{Cov(X, Y)}{\sqrt {Var(X) * Var(Y)}}
Correlation=Var(X)∗Var(Y)Cov(X,Y)
其中,
X
X
X和
Y
Y
Y是两个随机变量(对应数据集也就代表两个字段),
V
a
r
(
X
)
、
V
a
r
(
Y
)
Var(X)、Var(Y)
Var(X)、Var(Y)为
X
、
Y
X、Y
X、Y的方差,
C
o
v
(
X
,
Y
)
Cov(X,Y)
Cov(X,Y)为
X
X
X和
Y
Y
Y这两个变量的协方差,具体计算公式为:
C
o
v
(
X
,
Y
)
=
E
(
X
−
E
(
X
)
)
E
(
Y
−
E
(
Y
)
)
=
E
(
X
Y
)
−
E
(
X
)
E
(
Y
)
\begin{align} Cov(X, Y) &= E(X-E(X))E(Y-E(Y)) \\ &=E(XY)-E(X)E(Y) \end{align}
Cov(X,Y)=E(X−E(X))E(Y−E(Y))=E(XY)−E(X)E(Y)
其中 E ( X ) 、 E ( Y ) E(X)、E(Y) E(X)、E(Y)为 X 、 Y X、Y X、Y期望计算结果。
关于相关系数的计算有很多种方法,此处介绍的相关系数计算也被称为皮尔逊相关系数,最早由统计学家卡尔·皮尔逊提出,是目前最为通用的相关系数计算方法。
- 相关系数计算在NumPy中的实现
当然,在NumPy中也提供了相关系数计算函数corrcoef
可用于快速计算两个数组之间的相关系数
A = np.array([[1, 2, 3], [4, 5, 10]]).T
np.corrcoef(A[:, 0], A[:, 1])
output: array([[1. , 0.93325653],
[0.93325653, 1. ]])
该函数最终返回的是相关系数矩阵 A 2 ∗ 2 A_{2*2} A2∗2,其中 a i , j a_{i,j} ai,j表示第i、j两个变量之间的相关系数。很明显,相关系数矩阵是一个对角线元素全是1的矩阵,并且上三角和下三角元素对应位置元素相等。当然,对于A中的两个数组相关系数计算结果为0.933。
- 相关系数计算结果解读
相关系数的计算结果取值为[-1,1]之内,取值为负时代表两个变量同步变化关系为负,也就是其中一个数值增加、另一个数值减少。
总体来说,相关系数绝对值越大两个变量的相关性越强,绝对值为1时候代表完全相关,两个变量完全线性同步变化,其中一个变量可以由另一个变量线性表出。而绝对值为0时,则表示完全线性无关,两个变量没有线性同步变化规律,这两个变量没有线性关系。当绝对值介于0和1之间时候,相关性强弱可以表示如下:
|Cor| | 相关性 |
---|---|
0~0.09 | 没有相关性 |
0.1~0.3 | 弱相关 |
0.3~0.5 | 中等相关 |
0.5~1.0 | 强相关 |
2 数据生成器与Python模块编写
在有了相关性理论基础之后,我们即可创建一个可用于回归模型实验的数据生成器。
- 自定义数据生成器
为了方便后续练习的展开,我们尝试自己创建一个数据生成器,用于自主生成一些符合某些条件、难度可控、具备某些特性的数据集。机器学习发展至今,在追求模型效果提升的过程中,模型本身可解释性逐渐变弱,对于很多集成类算法,很多模型构建思路也在朝向深度学习靠拢。这使得很多模型内部逐渐呈现灰箱甚至是黑箱的状态。但是,在初学过程中,我们仍然需要通过类似控制变量的方法、通过设计一些实验,去深入理解算法运行原理及一些优化方法的实际作用,这就需要我们自己动手,创建一些数据用于实验的原材料,通过一些实验深入了解模型原理,从“炼丹师”朝着“化学家”更进一步。 - 手动生成数据
我们先尝试生成两个特征、存在偏差,自变量和因变量存在线性关系的数据集
# 科学计算模块
import numpy as np
import pandas as pd
# 绘图模块
import matplotlib as mpl
import matplotlib.pyplot as plt
num_inputs = 2 # 两个特征
num_examples = 1000 # 总共一千条数据
然后尝试通过线性方程,确定自变量和因变量的真实关系
np.random.seed(24) # 设置随机数种子
np.random.randn(2, 2)
线性方程系数
w_true = np.array([2, -1]).reshape(-1, 1)
b_true = np.array(1)
array([[ 1.32921217, -0.77003345],
[-0.31628036, -0.99081039]])
# 扰动项相关
delta = 0.01
# 创建数据集的特征和标签取值
features = np.random.randn(num_examples, num_inputs)
labels_true = features.dot(w_true) + b_true
labels = labels_true + np.random.normal(size = labels_true.shape) * delta
注意,此时labels_true和features满足严格意义上的线性方程关系
y
=
2
x
1
−
x
2
+
1
y = 2x_1-x_2+1
y=2x1−x2+1
但我们实际使用的标签labels,则是在labels_true的基础上增添了一个扰动项,np.random.normal(size = labels_true.shape) * delta
,这其实也符合我们一般获取数据的情况:真实客观世界或许存在某个规律,但我们搜集到的数据往往会因为各种原因存在一定的误差,无法完全描述真实世界的客观规律,这其实也是模型误差的来源之一(另一个误差来源是模型本身捕获规律的能力)。这其中,
y
=
2
x
1
−
x
2
+
1
y=2x_1-x_2+1
y=2x1−x2+1相当于我们从上帝视角创建的数据真实服从的规律,而扰动项,则相当于人为创造的获取数据时的误差。
- 创建生成回归类数据的函数
def arrayGenReg(num_examples = 1000, w = [2, -1, 1], bias = True, delta = 0.01, deg = 1):
"""回归类数据集创建函数。
:param num_examples: 创建数据集的数据量
:param w: 包括截距的(如果存在)特征系数向量
:param bias:是否需要截距
:param delta:扰动项取值
:param deg:方程最高项次数
:return: 生成的特征数组和标签数组
"""
if bias == True:
num_inputs = len(w)-1 # 数据集特征个数
features_true = np.random.randn(num_examples, num_inputs) # 原始特征
w_true = np.array(w[:-1]).reshape(-1, 1) # 自变量系数
b_true = np.array(w[-1]) # 截距
labels_true = np.power(features_true, deg).dot(w_true) + b_true # 严格满足人造规律的标签
features = np.concatenate((features_true, np.ones_like(labels_true)), axis=1) # 加上全为1的一列之后的特征
else:
num_inputs = len(w)
features = np.random.randn(num_examples, num_inputs)
w_true = np.array(w).reshape(-1, 1)
labels_true = np.power(features, deg).dot(w_true)
labels = labels_true + np.random.normal(size = labels_true.shape) * delta
return features, labels
随机数种子的使用
此处我们使用了一个随机数种子,以确保随机结果的可重复性。当我们设置某个随机数种子之后,每次随机过程都是可重复的:我们只需要再次调用相同随机种子,就可以重复此前随机过程。
np.random.randn(9)
array([ 1.62779574, 0.35449279, 1.03752763, -0.38568351, 0.519818 ,
1.68658289, -1.32596315, 1.4289837 , -2.08935428])
np.random.seed(24)
np.random.randn(9)
array([ 1.32921217, -0.77003345, -0.31628036, -0.99081039, -1.07081626,
-1.43871328, 0.56441685, 0.29572189, -1.62640423])
np.random.randn(9)
array([ 0.2195652 , 0.6788048 , 1.88927273, 0.9615384 , 0.1040112 ,
-0.48116532, 0.85022853, 1.45342467, 1.05773744])
np.random.seed(24)
np.random.randn(9)
array([ 1.32921217, -0.77003345, -0.31628036, -0.99081039, -1.07081626,
-1.43871328, 0.56441685, 0.29572189, -1.62640423])
np.random.randn(9)
array([ 0.2195652 , 0.6788048 , 1.88927273, 0.9615384 , 0.1040112 ,
-0.48116532, 0.85022853, 1.45342467, 1.05773744])
当然,不同随机数种子所诞生的随机过程是不一样的,这也是“种子”的由来。此外,不同库中的随机过程需要用的不同随机种子也是不同的,比如上述我们用np.random.seed来规定numpy中相关随机过程,但如果是其他第三方库,如random库所定义的随机过程,就需要使用random库中的随机种子来确定。
3 线性回归的手动实现
- Step 1.数据准备
首先,是准备数据集。我们利用数据生成器创建一个扰动项不太大的数据集:调用上面的arrayGenReg()函数生成。
# 设置随机数种子
np.random.seed(24)
# 扰动项取值为0.01
features, labels = arrayGenReg(delta=0.01)
其中,features也被称为特征矩阵、labels也被称为标签数组
- Step 2.模型选取
接下来,选取模型对上述回归类问题数据进行建模。此处我们选取带有截距项的多元线性回归方程进行建模,基本模型为:
f ( x ) = w 1 x 1 + w 2 x 2 + b f(x) = w_1x_1+w_2x_2+b f(x)=w1x1+w2x2+b
令 w ^ = [ w 1 , w 2 , b ] T \hat w = [w_1,w_2,b]^T w^=[w1,w2,b]T, x ^ = [ x 1 , x 2 , 1 ] T \hat x = [x_1,x_2, 1]^T x^=[x1,x2,1]T,则上式可写为
f ( x ) = w ^ T x ^ f(x) = \hat w^T\hat x f(x)=w^Tx^
- 根据机器学习建模流程构建线性回归;
- 线性回归模型局限与解决思路;
注,此处如果要构建一个不带截距项的模型,则可另X为原始特征矩阵带入进行建模。
- Step 3.构造损失函数
对于线性回归来说,我们可以参照SSE、MSE或者RMSE的计算过程构造损失函数。由于目前模型参数还只是隐式的值(在代码中并不显示),我们可以简单尝试,通过人工设置一组 w ^ \hat w w^来计算SSE。 - 另 w ^ \hat w w^为一组随机值,计算SSE
np.random.seed(24)
w = np.random.randn(3).reshape(-1, 1)
y_hat = features.dot(w)
y_hat[:10]
据此,根据公式 S S E = ∣ ∣ y − X w ^ ∣ ∣ 2 2 = ( y − y ^ ) T ( y − y ^ ) SSE= ||y - X\hat w||_2^2 = (y - \hat y)^T(y - \hat y) SSE=∣∣y−Xw^∣∣22=(y−y^)T(y−y^),SSE计算结果为:
# 计算MSE
(labels - y_hat).T.dot(labels - y_hat) / len(labels)
array([[2.0935294]])
能够看出,在当前参数取值下,模型输出结果和真实结果相距甚远。
不过,为了后续快速计算SSE,我们可以将上述SSE计算过程封装为一个函数,令其在输入特征矩阵、标签数组和真实参数情况下即可输出SSE计算结果:
def SSELoss(X, w, y):
"""
SSE计算函数
:param X:输入数据的特征矩阵
:param w:线性方程参数
:param y:输入数据的标签数组
:return SSE:返回对应数据集预测结果和真实结果的误差平方和
"""
y_hat = X.dot(w)
SSE = (y - y_hat).T.dot(y - y_hat)
return SSE
- Step 4.利用最小二乘法求解损失函数
接下来,我们需要在SSELoss中找到一组最佳的参数取值,另模型预测结果和真实结果尽可能接近。此处我们利用Lesson 2中介绍的最小二乘法来进行求解,最小二乘法求解模型参数公式为:
w ^ = ( X T X ) − 1 X T y \hat w = (X^TX)^{-1}X^Ty w^=(XTX)−1XTy
值得注意的是,最小二乘法在进行求解过程中,需要特征矩阵的交叉乘积可逆,也就是 X T X X^TX XTX必须存在逆矩阵。我们可以通过计算其行列式来判断该条件是否满足:
行列式不为0,因此 X T X X^TX XTX逆矩阵存在,可以通过最小二乘法求解。具体求解方法分为两种,其一是使用NumPy中线性代数基本方法,根据上述公式进行求解,同时也可以直接使用lstsq函数进行求解。 - 基础方法求解
w = np.linalg.inv(features.T.dot(features)).dot(features.T).dot(labels)
w
array([[ 1.99961892],
[-0.99985281],
[ 0.99970541]])
即可算出模型最优参数w。所谓模型最优参数,指的是参数取得任何其他数值,模型评估结果都不如该组参数时计算结果更好。首先,我们也可以计算此时模型SSE指标:
SSELoss(features, w, labels)
array([[0.09300731]])
当然,由于数据集本身是依据 y = 2 x 1 + x 2 − 1 y=2x_1+x_2-1 y=2x1+x2−1规律构建的,因此从模型参数也能够看出模型预测效果较好。
模型评估指标中SSE、MSE、RMSE三者反应的是一个事实,我们根据SSE构建损失函数只是因为SSE计算函数能够非常方便的进行最小值推导,SSE取得最小值时MSE、RMSE也取得最小值。
- lstsq函数求解
当然,我们也可以利用lstsq函数进行最小二乘法结果求解。二者结果一致。
np.linalg.lstsq(features, labels, rcond=-1)[0]
array([[ 1.99961892],
[-0.99985281],
[ 0.99970541]])
至此,我们即完成了整个线性回归的机器学习建模流程。
4 机器学习模型结果可信度理论
- 机器学习模型结果可信度基础理论
在前面的机器学习系列的博客中我们曾说,模型评估指标是用于评估模型效果好坏的数值指标,例如SSE就是评估回归类模型拟合效果的指标。但是否是评估指标好的模型就一定能用呢?其实并不一定。这里会涉及到一个关于评估指标可信度、或者说了解模型真实性能的重要命题。
其实,要了解模型的性能其实并不简单,固然我们会使用某些指标去进行模型评估,但其实指标也只是我们了解模型性能的途径而不是模型性能本身。而要真实、深刻的评判模型性能,就必须首先了解机器学习的建模目标,并在此基础之上熟悉我们判断模型是否能够完成目标的一些方法,当然,只有真实了解的模型性能,我们才能进一步考虑如何提升模型性能。因此,在正式讲解模型优化方法之前,我们需要花些时间讨论机器学习算法的建模目标、机器学习算法为了能够达到目标的一般思路,以及评估模型性能的手段,也就是模型评估指标。
无论是机器学习还是传统的统计分析模型,核心使命就是探索数字规律,而有监督学习则是希望在探索数字规律的基础上进一步对未来进行预测,当然,在数字的世界,这个预测未来,也就是预测未来某项事件的某项数值指标,如某地区未来患病人次、具备某种数字特征的图片上的动物是哪一类,此处的未来也并非指绝对意义上的以后的时间,而是在模型训练阶段暂时未接触到的数据。正是因为模型有了在未知标签情况下进行预判的能力,有监督学习才有了存在的价值,但我们知道,基本上所有的模型,都只能从以往的历史经验当中进行学习,也就是在以往的、已经知道的数据集上进行训练(如上述利用已知数据集进行模型训练,如利用过往股票数据训练时间序列模型),这里的核心矛盾在于,在以往的数据中提取出来的经验(也就是模型),怎么证明能够在接下来的数据中也具备一定的预测能力呢?或者说,要怎么训练模型,才能让模型在未知的数据集上也拥有良好的表现呢?
目的相同,但在具体的实现方法上,传统的数理统计分析建模和机器学习采用了不同的解决方案。
首先,在统计分析领域,我们会假设现在的数据和未来的数据其实都属于某个存在但不可获得的总体,也就是说,现在和未来的数据都是从某个总体中抽样而来的,都是这个总体的样本。而正式因为这些数据属于同一个总体,因此具备某些相同的规律,而现在挖掘到的数据规律也就在某些程度上可以应用到未来的数据当中去,不过呢,不同抽样的样本之间也会有个体之间的区别,另外模型本身也无法完全捕获规律,而这些就是误差的来源。
虽然样本和总体的概念是统计学概念,但样本和总体的概念所假设的前后数据的“局部规律一致性”,却是所有机器学习建模的基础。试想一下,如果获取到的数据前后描绘的不是一件事情,那么模型训练也就毫无价值(比如拿着A股走势预测的时间序列预测某地区下个季度患病人次)。因此,无论是机器学习所强调的从业务角度出发,要确保前后数据描述的一致性,还是统计分析所强调的样本和总体的概念,都是建模的基础。
在有了假设基础之后,统计分析就会利用一系列的数学方法和数理统计工具去推导总体的基本规律,也就是变量的分布规律和一些统计量的取值,由于这个过程是通过已知的样本去推断未知的总体,因此会有大量的“估计”和“检验”,在确定了总体的基本分布规律之后,才能够进一步使用统计分析模型构建模型(这也就是为什么在数理统计分析领域,构建线性回归模型需要先进行一系列的检验和变换的原因),当然,这些模型都是在总体规律基础之上、根据样本具体的数值进行的建模,我们自然有理由相信这些模型对接下来仍然是从总体中抽样而来的样本还是会具备一定的预测能力,这也就是我们对统计分析模型“信心”的来源。简单来说,就是我们通过样本推断总体的规律,然后结合总体的规律和样本的数值构建模型,由于模型也描绘了总体规律,所以模型对接下来从总体当中抽样而来的数据也会有不错的预测效果,这个过程我们可以通过下图来进行表示。
而对于机器学习来说,并没有借助“样本-总体”的基本理论,而是简单的采用了一种后验的方法来判别模型有效性,前面说到,我们假设前后获取的数据拥有规律一致性,但数据彼此之间又略有不同,为了能够在捕捉规律的同时又能考虑到“略有不同”所带来的误差,机器学习会把当前能获取到的数据划分成训练集(trainSet)和测试集(testSet),在训练集上构建模型,然后带入测试集的数据,观测在测试集上模型预测结果和真实结果之间的差异。这个过程其实就是在模拟获取到真实数据之后模型预测的情况,此前说到,模型能够在未知标签的数据集上进行预测,就是模型的核心价值,此时的测试集就是用于模拟未来的未知标签的数据集。如果模型能够在测试集上有不错的预测效果,我们就“简单粗暴”的认为模型可以在真实的未来获取的未知数据集上有不错的表现。其一般过程可以由下图表示。
虽然对比起数理统计分析,机器学习的证明模型有效性的过程更加“简单”,毕竟只要一次“模拟”成功,我们就认为模型对未来的数据也拥有判别效力,但这种“简单”的处理方式却非常实用,可以说,这是一种经过长期实践被证明的行之有效的方法。这也是为什么机器学习很多时候也被认为是实证类的方法,而在以后的学习中,我们也将了解到,机器学习有很多方法都是“经验总结的结果”。相比数理统计分析,确实没有“那么严谨”,但更易于理解的理论和更通用的方法,却使得机器学习可以在更为广泛的应用场景中发挥作用。(当然,负面影响却是,机器学习在曾经的很长一段时间内并不是主流的算法。)
据此,我们称模型在训练集上误差称为训练误差,在测试集上的误差称为泛化误差,不过毕竟在测试集上进行测试还只是模拟演习,我们采用模型的泛化能力来描述模型在未知数据上的判别能力,当然泛化能力无法准确衡量(未知的数据还未到来,到来的数据都变成了已知数据),我们只能通过模型在训练集和测试集上的表现,判别模型泛化能力,当然,就像此前说的一样,最基本的,我们会通过模型在测试集上的表现来判断模型的泛化能力。
5 线性回归的完整代码
科学计算模块
import numpy as np
import pandas as pd
# 回归数据创建函数
def arrayGenReg(num_examples = 1000, w = [2, -1, 1], bias = True, delta = 0.01, deg = 1):
"""回归类数据集创建函数。
:param num_examples: 创建数据集的数据量
:param w: 包括截距的(如果存在)特征系数向量
:param bias:是否需要截距
:param delta:扰动项取值
:param deg:方程最高项次数
:return: 生成的特征张和标签张量
"""
if bias == True:
num_inputs = len(w)-1 # 数据集特征个数
features_true = np.random.randn(num_examples, num_inputs) # 原始特征
w_true = np.array(w[:-1]).reshape(-1, 1) # 自变量系数
b_true = np.array(w[-1]) # 截距
labels_true = np.power(features_true, deg).dot(w_true) + b_true # 严格满足人造规律的标签
features = np.concatenate((features_true, np.ones_like(labels_true)), axis=1) # 加上全为1的一列之后的特征
else:
num_inputs = len(w)
features = np.random.randn(num_examples, num_inputs)
w_true = np.array(w).reshape(-1, 1)
labels_true = np.power(features, deg).dot(w_true)
labels = labels_true + np.random.normal(size = labels_true.shape) * delta
return features, labels
# SSE计算函数
def SSELoss(X, w, y):
"""
SSE计算函数
:param X:输入数据的特征矩阵
:param w:线性方程参数
:param y:输入数据的标签数组
:return SSE:返回对应数据集预测结果和真实结果的误差平方和
"""
y_hat = X.dot(w)
SSE = (y - y_hat).T.dot(y - y_hat)
return SSE
# 数据集随机切分函数
def array_split(features, labels, rate=0.7, random_state=24):
"""
训练集和测试集切分函数
:param features: 输入的特征张量
:param labels:输入的标签张量
:param rate:训练集占所有数据的比例
:random_state:随机数种子值
:return Xtrain, Xtest, ytrain, ytest:返回特征张量的训练集、测试集,以及标签张量的训练集、测试集
"""
np.random.seed(random_state)
np.random.shuffle(features) # 对特征进行切分
np.random.seed(random_state)
np.random.shuffle(labels) # 按照相同方式对标签进行切分
num_input = len(labels) # 总数据量
split_indices = int(num_input * rate) # 数据集划分的标记指标
Xtrain, Xtest = np.vsplit(features, [split_indices, ])
ytrain, ytest = np.vsplit(labels, [split_indices, ])
return Xtrain, Xtest, ytrain, ytest