Lesson 3.线性回归的手动建模实验

在此前的两节课程中,我们已经介绍了关于线性回归模型的基本概念,并且介绍了一个多元线性回归的损失函数求解方法——最小二乘法。在有了这一些列理论推导之后,本节我们将结合Lesson 1中所介绍的机器学习一般建模流程,并首先尝试在一个手动构建的数据集上进行完整的线性回归模型建模。

Lesson 3.1 变量相关性基础理论

在创建好了数据生成器之后,接下来即可进行手动线性回归建模实验。

# 科学计算模块
import numpy as np
import pandas as pd# 绘图模块
import matplotlib as mpl
import matplotlib.pyplot as plt

机器学习的“学习”目标,其实就是数据集中隐藏的数字规律,而又由于这些规律背后代表的是某些事物的真实属性或者运行状态,因此这些规律是具备指导生产生活事件意义的有价值的规律,这也是机器学习算法价值的根本。

  • 相关系基本解释与相关系数计算公式

当然,对于不同数据集来说,是否具备规律、以及规律隐藏的深浅都不一样。对于模型来说,擅长挖掘的规律、以及规律挖掘的能力也都各不相同。而对于线性回归来说,捕捉的实际上是数据集的线性相关的规律。所谓线性相关,简单来说就是数据的同步变化特性。例如此前数据集:

特征和标签就存在着非常明显的同步变化的特性:第二条样本特征增加2、对应标签也增加2,当然,这也就是线性模型本身可解释性的来源——体重越大的鲍鱼、年龄越大,并且体重每增加2、年龄也增加2。这种同步变化特性用更加专业的角度来描述就是变量之间的相关性。这种相关性可以用一个计算公式算得,也就是相关性系数计算公式:
Correlation=Cov(X,Y)Var(X)∗Var(Y)Correlation = \frac{Cov(X, Y)}{\sqrt {Var(X) * Var(Y)}}Correlation=Var(X)∗Var(Y)​Cov(X,Y)​
其中,XXX和YYY是两个随机变量(对应数据集也就代表两个字段),Var(X)、Var(Y)Var(X)、Var(Y)Var(X)、Var(Y)为X、YX、YX、Y的方差,Cov(X,Y)Cov(X,Y)Cov(X,Y)为XXX和YYY这两个变量的协方差,具体计算公式为:
Cov⁡(X,Y)=E(X−E(X))E(Y−E(Y))=E(XY)−E(X)E(Y)\begin{aligned} \operatorname{Cov}(X, Y) &=E(X-E(X)) E(Y-E(Y)) \\ &=E(X Y)-E(X) E(Y) \end{aligned} 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、YX、YX、Y期望计算结果。

关于相关系数的计算有很多种方法,此处介绍的相关系数计算也被称为皮尔逊相关系数,最早由统计学家卡尔·皮尔逊提出,是目前最为通用的相关系数计算方法。

  • 相关系数计算在NumPy中的实现
    当然,在NumPy中也提供了相关系数计算函数corrcoef可用于快速计算两个数组之间的相关系数
A = np.array([[1, 2, 3], [4, 5, 10]]).T
A
#array([[ 1,  4],
#       [ 2,  5],
#       [ 3, 10]])
A[:, 0]
#array([1, 2, 3])
np.corrcoef(A[:, 0], A[:, 1])
#array([[1.        , 0.93325653],
#       [0.93325653, 1.        ]])

  该函数最终返回的是相关系数矩阵A2∗2A_{2*2}A2∗2​,其中ai,ja_{i,j}ai,j​表示第i、j两个变量之间的相关系数。很明显,相关系数矩阵是一个对角线元素全是1的矩阵,并且上三角和下三角元素对应位置元素相等。当然,对于A中的两个数组相关系数计算结果为0.933。

  • 相关系数计算结果解读
    相关系数的计算结果取值为[-1,1]之内,取值为负时代表两个变量同步变化关系为负,也就是其中一个数值增加、另一个数值减少。例如:
A = np.array([[1, 2, 3], [-1, -1.5, -5]]).T
A
#array([[ 1. , -1. ],
#       [ 2. , -1.5],
#       [ 3. , -5. ]])
plt.plot(A[:, 0], A[:, 1])

np.corrcoef(A[:, 0], A[:, 1])
#array([[ 1.        , -0.91766294],
#       [-0.91766294,  1.        ]])

总体来说,相关系数绝对值越大两个变量的相关性越强,绝对值为1时候代表完全相关,两个变量完全线性同步变化,其中一个变量可以由另一个变量线性表出。而绝对值为0时,则表示完全线性无关,两个变量没有线性同步变化规律,这两个变量没有线性关系。当绝对值介于0和1之间时候,相关性强弱可以表示如下:

如果是双变量的相关性,我们也可以用一组函数关系及图像来进行表示

np.random.randn(20)
#array([-1.64976142, -0.87343737,  0.07530987, -1.42079571, -0.83262953,
#        1.21936676, -0.75871775,  0.44775161,  0.46307329,  1.44154581,
#        0.79686385, -1.50887509, -0.53100092,  2.41405101, -0.28564285,
#       -1.51317621, -0.90461468, -0.45806723,  1.0310925 , -0.58551109])
X = np.random.randn(20)
y = X + 1
#很明显,此时X和y完全正相关
np.corrcoef(X, y)
#array([[1., 1.],
#       [1., 1.]])
# 对应点图
plt.plot(X, y, 'o')


当然,如果我们想在y基础上创建一个稍微弱化线性相关关系的数组,可以在y基础上加入一个随机数作为扰动项。例如:

a = y.shape
a
#(20,)
np.random.normal?
# 创建一个和y一样形状的服从标准正态分布的随机数组
np.random.normal(size=a)
#array([ 0.49622906,  1.3573347 , -0.20178063,  0.87805077, -1.42474422,
#       -1.70856044, -1.0952294 , -0.58293826,  1.09455328, -0.68583135,
#       -0.64713056,  0.26123903, -0.47562764,  1.39130696,  0.6881981 ,
#        0.30883974, -0.19414512,  1.6188312 , -2.05761665,  0.14654045])
np.random.normal?
ran = np.random.normal(size = X.shape)
ran
#array([ 0.26042618, -1.04154116, -0.08313493, -0.79742972, -0.13280839,
#        1.27921862,  0.48826155, -0.60279756,  0.60330237, -0.71903143,
#        0.2286587 ,  1.9293763 ,  2.45620622,  0.78343275, -0.37187501,
#        0.91938857,  1.79980253, -0.45157682, -0.37647247,  1.03357355])

接下来,创建一个控制扰动项大小的变量delta

delta = 0.5
#因此,扰动项最终计算过程为:
r = ran * delta
r
#array([-0.70326304,  0.39202727,  0.63362961, -0.28215417, -0.30074036,
#       -0.59024454, -0.33660292, -0.10770454,  0.1688505 ,  1.13219149,
#        0.2886254 ,  0.19396348,  0.08446266, -0.05326953, -0.45031793,
#       -0.59683277,  0.28079264,  0.62728816,  0.24544957, -0.46301168])
y1 = y + r
y1
#array([-1.68723915,  0.76532326,  2.045763  , -0.74109006,  0.32887279,
#        0.35244733, -2.68058767, -0.67867186,  1.29879589,  1.50933817,
#        0.27243497,  1.19472997,  2.79165527,  2.9877919 , -0.92177274,
#       -0.66149061, -0.69079609,  1.26800102,  2.62706381,  0.32901834])

此处,y1就是在y基础上加入扰动项之后的标签。由于有一个随性扰动项的存在,会使得y1和X的线性关系被削弱。

从更根本的角度来说,加入扰动项削弱线性相关性,也是因为扰动项本身无规律可循,加入数据之后只会掩盖原始规律。类似扰动项我们也可称其为白噪声。白噪声其实也有一定的实际背景,在很多情况下,我们采集的数据本身就是包含一定随机误差的,或者说是包含了无法帮助提取有效规律的信息。

plt.subplot(121)
plt.plot(X, y, 'o')
plt.title('y=x+1')
plt.subplot(122)
plt.plot(X, y1, 'o')
plt.title('y=x+1+r')


由此可见,在加入了扰动项之后,模型线性相关性明显变弱。据此,当然,伴随delta的增加,噪声数据的绝对值会越来越大,掩盖原始数据线性相关规律的趋势会更加明显。我们可以简单用一组图像来展示不同相关性时双变量呈现的分布情况:

# delta系数取值数组
dl = [0.5, 0.7, 1, 1.5, 2, 5]
# 空列表容器
yl = []          # 不同delta下y的取值所组成的列表
cl = []          # 不同y下相关系数矩阵所组成的列表
# 计算不同delta下y和相关系数计算情况
for i in dl:yn = X + 1 + (ran * i)cl.append(np.corrcoef(X, yn))yl.append(yn)
cl
#[array([[1.        , 0.95223181],
#        [0.95223181, 1.        ]]),
# array([[1.       , 0.9177165],
#        [0.9177165, 1.       ]]),
# array([[1.        , 0.86249014],
#        [0.86249014, 1.        ]]),
# array([[1.        , 0.77666269],
#        [0.77666269, 1.        ]]),
# array([[1.        , 0.70615124],
#        [0.70615124, 1.        ]]),
# array([[1.        , 0.49724359],
#        [0.49724359, 1.        ]])]
yl
#[array([-1.68723915,  0.76532326,  2.045763  , -0.74109006,  0.32887279,
#         0.35244733, -2.68058767, -0.67867186,  1.29879589,  1.50933817,
#         0.27243497,  1.19472997,  2.79165527,  2.9877919 , -0.92177274,
#        -0.66149061, -0.69079609,  1.26800102,  2.62706381,  0.32901834]),
# array([-1.96854437,  0.92213417,  2.29921484, -0.85395173,  0.20857664,
#         0.11634951, -2.81522884, -0.72175367,  1.36633609,  1.96221477,
#         0.38788513,  1.27231536,  2.82544033,  2.96648409, -1.10189991,
#        -0.90022372, -0.57847904,  1.51891629,  2.72524364,  0.14381367]),
# array([-2.39050219,  1.15735053,  2.6793926 , -1.02324423,  0.02813242,
#        -0.23779721, -3.01719059, -0.78637639,  1.46764639,  2.64152967,
#         0.56106037,  1.38869345,  2.87611793,  2.93452237, -1.37209067,
#        -1.25832338, -0.41000345,  1.89528918,  2.87251338, -0.13399334]),
# array([-3.09376523,  1.5493778 ,  3.31302221, -1.30539839, -0.27260794,
#        -0.82804176, -3.35379351, -0.89408093,  1.63649689,  3.77372116,
#         0.84968577,  1.58265694,  2.96058059,  2.88125284, -1.8224086 ,
#        -1.85515614, -0.12921082,  2.52257734,  3.11796295, -0.59700502]),
# array([-3.79702828,  1.94140507,  3.94665182, -1.58755256, -0.5733483 ,
#        -1.4182863 , -3.69039643, -1.00178546,  1.80534739,  4.90591265,
#         1.13831118,  1.77662042,  3.04504325,  2.82798331, -2.27272653,
#        -2.45198891,  0.15158182,  3.1498655 ,  3.36341253, -1.06001671]),
# array([-8.01660653,  4.2935687 ,  7.74842947, -3.28047755, -2.37779046,
#        -4.95975357, -5.71001396, -1.64801267,  2.81845039, 11.6990616 ,
#         2.87006359,  2.94040133,  3.55181922,  2.50836614, -4.97463411,
#        -6.03298551,  1.83633764,  6.91359447,  4.83610996, -3.83808681])]
plt.plot(X, yl[0], 'o')

plt.subplot(231)
plt.plot(X, yl[0], 'o')
plt.plot(X, y, 'r-')
plt.subplot(232)
plt.plot(X, yl[1], 'o')
plt.plot(X, y, 'r-')
plt.subplot(233)
plt.plot(X, yl[2], 'o')
plt.plot(X, y, 'r-')
plt.subplot(234)
plt.plot(X, yl[3], 'o')
plt.plot(X, y, 'r-')
plt.subplot(235)
plt.plot(X, yl[4], 'o')
plt.plot(X, y, 'r-')
plt.subplot(236)
plt.plot(X, yl[5], 'o')
plt.plot(X, y, 'r-')


能够明显看出,伴随delta取值越来越大,数据相关性越来越弱,当然,我们也能通过观察cl的取值来查看各组变量的相关系数

cl
#[array([[1.        , 0.95223181],
#        [0.95223181, 1.        ]]),
# array([[1.       , 0.9177165],
#        [0.9177165, 1.       ]]),
# array([[1.        , 0.86249014],
#        [0.86249014, 1.        ]]),
# array([[1.        , 0.77666269],
#        [0.77666269, 1.        ]]),
# array([[1.        , 0.70615124],
#        [0.70615124, 1.        ]]),
# array([[1.        , 0.49724359],
#        [0.49724359, 1.        ]])]

根据Lesson 2补充材料所介绍,简单线性回归就是在二维平面中通过构建一条直线来试图捕捉平面当中的点,线性相关性越弱、线性模型越难捕捉到这些所有点,模型效果就越差。换而言之,就是数据集之间线性相关性越明显,数据规律越明显,模型越容易捕捉到这些规律。

在这个基础理论之上,我们有以下两方面衍生的应用:

其一:我们可以在构建线性模型之前先探查数据本身的线性相关性,如果自变量和因变量存在很好的相关性,那就一定可以顺利的构建线性回归模型对数据进行拟合。而如果线性相关性不强,则说明当前数据并不适合构建线性回归模型,或者在构建模型之前我们需要对数据进行一些“不影响真实规律”的转化,令其表现出线性的分布趋势。当然,如果需要更换模型,就需要在我们学习了更多其他模型之后来进行讨论,而如果是围绕数据进行修改,则会涉及特征工程相关理论;
  
其二:上述几组不同的数据,实际上就代表着对线性回归模型建模难度各不相同的几组数据,delta越大对线性回归模型来说建模就更加困难。据此,我们可以生成一个手动创建数据集的函数,该函数可以输出不同建模难度(规律深浅)的数据集,来辅助我们测试模型和优化算法的性能。

当然,线性相关性的减弱,不仅是对于线性回归模型,对于很多回归类问题都会造成更大的建模困难。

Lesson 3.2 数据生成器与Python模块编写

在有了相关性理论基础之后,我们即可创建一个可用于回归模型实验的数据生成器。

# 科学计算模块
import numpy as np
import pandas as pd# 绘图模块
import matplotlib as mpl
import matplotlib.pyplot as plt

一、自定义数据生成器

为了方便后续练习的展开,我们尝试自己创建一个数据生成器,用于自主生成一些符合某些条件、难度可控、具备某些特性的数据集。机器学习发展至今,在追求模型效果提升的过程中,模型本身可解释性逐渐变弱,对于很多集成类算法,很多模型构建思路也在朝向深度学习靠拢。这使得很多模型内部逐渐呈现灰箱甚至是黑箱的状态。但是,在初学过程中,我们仍然需要通过类似控制变量的方法、通过设计一些实验,去深入理解算法运行原理及一些优化方法的实际作用,这就需要我们自己动手,创建一些数据用于实验的原材料,通过一些实验深入了解模型原理,从“炼丹师”朝着“化学家”更进一步。

和Lesson 1中介绍的一样,课程中案例分为三类,分别是基础阶段的手动创建数据集、实战阶段的竞赛数据集和企业应用实战阶段的企业数据集。

1.手动生成数据

我们先尝试生成两个特征、存在偏差,自变量和因变量存在线性关系的数据集

num_inputs = 2               # 两个特征
num_examples = 1000          # 总共一千条数据
#然后尝试通过线性方程,确定自变量和因变量的真实关系
np.random.seed(24)       # 设置随机数种子
np.random.randn(2, 2)
#array([[ 1.32921217, -0.77003345],
#       [-0.31628036, -0.99081039]])
# 线性方程系数
w_true = np.array([2, -1]).reshape(-1, 1)
b_true = np.array(1)
# 扰动项相关
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=2x1−x2+1y = 2x_1-x_2+1y=2x1​−x2​+1
但我们实际使用的标签labels,则是在labels_true的基础上增添了一个扰动项,np.random.normal(size = labels_true.shape) * delta,这其实也符合我们一般获取数据的情况:真实客观世界或许存在某个规律,但我们搜集到的数据往往会因为各种原因存在一定的误差,无法完全描述真实世界的客观规律,这其实也是模型误差的来源之一(另一个误差来源是模型本身捕获规律的能力)。这其中,y=2x1−x2+1y=2x_1-x_2+1y=2x1​−x2​+1相当于我们从上帝视角创建的数据真实服从的规律,而扰动项,则相当于人为创造的获取数据时的误差。
简单查看我们创建的数据集

features[: 10]
#array([[-1.07081626, -1.43871328],
#       [ 0.56441685,  0.29572189],
#       [-1.62640423,  0.2195652 ],
#       [ 0.6788048 ,  1.88927273],
#       [ 0.9615384 ,  0.1040112 ],
#       [-0.48116532,  0.85022853],
#       [ 1.45342467,  1.05773744],
#       [ 0.16556161,  0.51501838],
#       [-1.33693569,  0.56286114],
#       [ 1.39285483, -0.06332798]])
labels[: 10]
#array([[ 0.29161817],
#       [ 1.83851813],
#       [-2.46058022],
#       [ 0.44394438],
#       [ 2.8133944 ],
#       [-0.8109745 ],
#       [ 2.85143778],
#       [ 0.83156296],
#       [-2.22624102],
#       [ 3.84279053]])plt.subplot(121)
plt.scatter(features[:, 0], labels)          # 第一个特征和标签的关系
plt.subplot(122)
plt.scatter(features[:,, 1] labels)          # 第二个特征和标签的关系


不难看出,两个特征和标签都存在一定的线性关系,并且跟特征的系数绝对值有很大关系。当然,若要增加线性模型的建模难度,可以增加扰动项的数值比例,从而削弱线性关系。

# 设置随机数种子
np.random.seed(24)   # 修改因变量
labels1 = labels_true + np.random.normal(size = labels_true.shape) * 2# 可视化展示# 扰动较小的情况
plt.subplot(221)
plt.scatter(features[:, 0], labels)             # 第一个特征和标签的关系
plt.subplot(222)
plt.plot(features[:, 1], labels, 'ro')          # 第二个特征和标签的关系# 扰动较大的情况
plt.subplot(223)
plt.scatter(features[:, 0], labels1)             # 第一个特征和标签的关系
plt.subplot(224)
plt.plot(features[:, 1], labels1, 'yo')          # 第二个特征和标签的关系


当然,我们也能生成非线性关系的数据集,此处我们创建满足y=2x2+1y=2x^2+1y=2x2+1规律的数据集。

np.power([2, 3], 2)
#array([4, 9], dtype=int32)
# 设置随机数种子
np.random.seed(24)   num_inputs = 1               # 一个特征
num_examples = 1000          # 总共一千条数据# 线性方程系数
w_true = np.array(2)
b_true = np.array(1)# 特征和标签取值
features = np.random.randn(num_examples, num_inputs)
labels_true = np.power(features, 2) * w_true + b_true
labels = labels_true + np.random.normal(size = labels_true.shape) * 2# 可视化展示
plt.scatter(features, labels)

关于曲线相关,其实也可将其转化为线性相关,例如上例中,我们只需要新增一列x2=xx_2=xx2​=x即可构建一个线性模型y=x2+1y=x_2+1y=x2​+1,该方法也是特征工程中的一种特征衍生的方法。

此时需要注意的是,无论是创建了曲线规律的数据集,还是增加了扰动项绝对数值,都会对建模造成困难。但二者的造成困难的方式是不同的。如果是曲线规律的数据集,则代表规律本身更加复杂,此时需要使用更加复杂的模型来进行规律提取,该部分属于模型选取和参数调优的技术内容;而如果是扰动项比较大,则代表规律被掩盖的比较深,此时需要采用其他技术手段进行白噪声的处理,该部分属于特征工程技术内容。但本节内,无论是曲线规律还是白噪声数值过大,都会对线性方程建模造成困难。

这也就是控制数据集建模难度的最基础的抓手。

2.创建生成回归类数据的函数

为了方便后续使用,我们将上述过程封装在一个函数内

  • 定义创建函数
A = np.arange(4).reshape(2, 2)
A
#array([[0, 1],
#       [2, 3]])
np.power(A, 3)
#array([[ 0,  1],
#       [ 8, 27]], dtype=int32)
A.dot(2)
#array([[0, 2],
#       [4, 6]])
np.ones_like(A)
#array([[1, 1],
#       [1, 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) * deltareturn features, labels

注:上述函数默认创建的是一个满足y=2x1−x2+1y=2x_1-x_2+1y=2x1​−x2​+1的数据集。

  • 测试函数性能

首先查看扰动项较小的时候的数据情况

# 设置随机数种子
np.random.seed(24)   # 扰动项取值为0.01
f, l = arrayGenReg(delta=0.01)f
#array([[ 1.32921217, -0.77003345,  1.        ],
#       [-0.31628036, -0.99081039,  1.        ],
#       [-1.07081626, -1.43871328,  1.        ],
#       ...,
#       [ 1.5507578 , -0.35986144,  1.        ],
#       [-1.36267161, -0.61353562,  1.        ],
#       [-1.44029131,  0.50439425,  1.        ]])
# 绘制图像查看结果
plt.subplot(121)
plt.scatter(f[:, 0], l)             # 第一个特征和标签的关系
plt.subplot(122)
plt.scatter(f[:, 1], l)             # 第二个特征和标签的关系


然后查看扰动项较大时数据情况

# 设置随机数种子
np.random.seed(24)   # 扰动项取值为2
f, l = arrayGenReg(delta=2)# 绘制图像查看结果
plt.subplot(121)
plt.scatter(f[:, 0], l)             # 第一个特征和标签的关系
plt.subplot(122)
plt.scatter(f[:, 1], l)             # 第二个特征和标签的关系

# 设置随机数种子
np.random.seed(24)   # 最高次项为2
f, l = arrayGenReg(deg=2)# 绘制图像查看结果
plt.subplot(121)
plt.scatter(f[:, 0], l)             # 第一个特征和标签的关系
plt.subplot(122)
plt.scatter(f[:, 1], l)             # 第二个特征和标签的关系


在定义好数据创建函数之后,即可开始尝试手动实现线性回归模型。

该数据生成器并非为线性回归模型量身定制,而是通用与此后我们学习的所有回归类模型。关于分类模型数据集的创建,我们将在介绍逻辑回归时讲解。

此外,对于机器学习模型,我们将更多的从数据规律和模型提取规律角度出发,并不会太多顾及模型可解释性等问题。上述数据本身也是从规律角度出发构建的数据,并不具备“解释性”。

随机数种子的使用
  此处我们使用了一个随机数种子,以确保随机结果的可重复性。当我们设置某个随机数种子之后,每次随机过程都是可重复的:我们只需要再次调用相同随机种子,就可以重复此前随机过程。

np.random.randn(9)
#array([ 0.19023418, -0.81483068,  1.72482389, -0.15554173, -0.56159453,
#        0.42370057, -1.13493113,  0.37872101, -2.37427885])
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库中的随机种子来确定。

import random
l = list(range(5))
l
#[0, 1, 2, 3, 4]
random.seed(24)
random.shuffle(l)
l
#[4, 2, 0, 1, 3]
random.shuffle(l)
l
#[1, 4, 0, 3, 2]
l = list(range(5))
l
#[0, 1, 2, 3, 4]
random.seed(24)
random.shuffle(l)
l
#[4, 2, 0, 1, 3]
random.shuffle(l)
l
#[1, 4, 0, 3, 2]
l = list(range(5))
l
#[0, 1, 2, 3, 4]
np.random.seed(24)
random.shuffle(l)
l
#[4, 0, 3, 2, 1]
random.shuffle(l)
l
#[0, 4, 3, 1, 2]
l = list(range(5))
l
#[0, 1, 2, 3, 4]
np.random.seed(24)
random.shuffle(l)
l
#[4, 2, 3, 1, 0]

二、Python模块的编写与调用

根据课程安排,本节定义的函数将后续课程中将经常使用,因此需要将其封装为一个模块方便后续调用。封装为模块有以下几种基本方法:

  • 打开文本编辑器,将写好并测试完成的函数写入其中,并将文本的拓展名改写为.py;
  • 在spyder或者pycharm中复制相关函数,并保存为.py文件;

然后将文件保存在jupyter主目录下,并取名为ML_basic_function,后续即可通过import ML_basic_function进行调用。如果是jupyterlab用户,也可按照如下方式进行编写:

Step 1.打开左侧文件管理栏页,点击新建

Step 2.在新建目录中,选择Test File,并通过重命名将其命名为ML_basic_function.py

Step 3.在打开的文本编辑器中输入代码

并且需要注意,需要在py文件内导入相关的包。

Step 4.测试能否调用

首先重启当前jupyter
然后尝试导入自定义模块

from ML_basic_function import *
#查看相关函数说明并使用
arrayGenReg?
#Signature: arrayGenReg(num_examples=1000, w=[2, -1, 1], bias=True, delta=0.01, deg=1)
#Docstring:
#回归类数据集创建函数。#:param num_examples: 创建数据集的数据量
#:param w: 包括截距的(如果存在)特征系数向量
#:param bias:是否需要截距
#:param delta:扰动项取值
#:param deg:方程最高项次数
#:return: 生成的特征张和标签张量
#File:      f:\code file\机器学习实战\ml_basic_function.py
#Type:      function
arrayGenReg()

至此就完成了自定义模块的编写与保存,后续我们还将陆续写入其他函数。

Lesson 3.3 线性回归手动实现与模型局限

在创建好了数据生成器之后,接下来即可进行手动线性回归建模实验。

一、线性回归的手动实现

接下来,我们尝试进行线性回归模型的手动建模实验。建模过程将遵照机器学习的一般建模流程,并且借助NumPy所提供的相关工具来进行实现。通过本次实验,我们将进一步深化对机器学习建模流程的理解,并且也将进一步熟悉对编程基础工具的掌握。

1.根据机器学习建模流程构建线性回归模型

Step 1.数据准备
  首先,是准备数据集。我们利用数据生成器创建一个扰动项不太大的数据集:

# 设置随机数种子
np.random.seed(24)   # 扰动项取值为0.01
features, labels = arrayGenReg(delta=0.01)features
#array([[ 1.32921217, -0.77003345,  1.        ],
#       [-0.31628036, -0.99081039,  1.        ],
#       [-1.07081626, -1.43871328,  1.        ],
#       ...,
#       [ 1.5507578 , -0.35986144,  1.        ],
#       [-1.36267161, -0.61353562,  1.        ],
#       [-1.44029131,  0.50439425,  1.        ]])

其中,features也被称为特征矩阵、labels也被称为标签数组

  • Step 2.模型选取

接下来,选取模型对上述回归类问题数据进行建模。此处我们选取带有截距项的多元线性回归方程进行建模,基本模型为:
f(x)=w1x1+w2x2+bf(x) = w_1x_1+w_2x_2+b f(x)=w1​x1​+w2​x2​+b
令w^=[w1,w2,b]T\hat w = [w_1,w_2,b]^Tw^=[w1​,w2​,b]T,x^=[x1,x2,1]T\hat x = [x_1,x_2, 1]^Tx^=[x1​,x2​,1]T,则上式可写为
f(x)=w^Tx^f(x) = \hat w^T\hat x f(x)=w^Tx^

注,此处如果要构建一个不带截距项的模型,则可另X为原始特征矩阵带入进行建模。

  • Step 3.构造损失函数

对于线性回归来说,我们可以参照SSE、MSE或者RMSE的计算过程构造损失函数。由于目前模型参数还只是隐式的值(在代码中并不显示),我们可以简单尝试,通过人工设置一组

LESSON 3 线性回归的手动实现相关推荐

  1. Lesson 1. 线性回归模型的一般实现形式

    文章目录 一.线性回归模型建模准备 1. 数据准备 2. 模型准备 二.线性回归模型训练 1. 模型训练的本质:有方向的参数调整 1.1 模型训练与模型参数调整 1.2 模型评估指标与损失函数 1.3 ...

  2. Lesson 12.3 线性回归建模实验

    Lesson 12.3 线性回归建模实验 一.深度学习建模流程   数据准备就绪,接下来就是建模实验环节,在实际深度学习建模过程中,无论是手动实现还是调库实现,我们都需要遵循深度学习建模一般流程.在此 ...

  3. 机器学习---线性回归推导以及python实现

    机器学习笔记 线性回归 对于给定的特征X和标签y,可以直接调用sklearn里的LinearRegression()类初始化一个线性回归模型,之后通过fit()函数在给定的数据上做拟合. # 实例化一 ...

  4. python绘图库seaborn_Matplotlib Toolkits:python高级绘图库seaborn

    Seaborn介绍 seaborn (Not distributed with matplotlib) seaborn is a highlevel interface for drawing sta ...

  5. 【机器学习】逻辑回归案例一:保险与年龄之间关系分析

    逻辑回归案例一:保险与年龄之间关系分析 逻辑回归案例一:保险与年龄之间关系分析 1 数据加载 2 数据切分 3 模型创建与应用 4 逻辑回归和线性回归关系 手动反爬虫,禁止转载: 原博地址 https ...

  6. Lesson 4.6 逻辑回归的手动实现

    Lesson 4.6 逻辑回归的手动实现 讨论完梯度下降的相关内容之后,接下来我们尝试使用梯度下降算法求解逻辑回归损失函数,并且通过一系列实验来观察逻辑回归的模型性能. # 科学计算模块 import ...

  7. Lesson 4.34.4 梯度下降(Gradient Descent)基本原理与手动实现随机梯度下降与小批量梯度下降

    Lesson 4.3 梯度下降(Gradient Descent)基本原理与手动实现 在上一小节中,我们已经成功的构建了逻辑回归的损失函数,但由于逻辑回归模型本身的特殊性,我们在构造损失函数时无法采用 ...

  8. Lesson 15.1 学习率调度基本概念与手动实现方法

    Lesson 15.1 学习率调度基本概念与手动实现方法   从本节开始,我们将介绍深度学习中学习率优化方法.学习率作为模型优化的重要超参数,在此前的学习中,我们已经看到了学习率的调整对模型训练在诸多 ...

  9. 如何理解statsmodels.ols的输出结果?ols计算的线性回归结果以及手动计算的结果的对比

    手动计算 def linegress(l_1,l_2): #求两列数据的线性回归参数import numpy as npfrom numpy.linalg import solvesumx = np. ...

最新文章

  1. R pdf大小_怎样给PDF添加批注并同步到手机里?
  2. 宋祖儿面对粉丝不停撩头发,手上的书本亮了,是要转行当程序员?
  3. WinExec、ShellExecute用法详解
  4. sqlmap完成简单的sql注入
  5. 求1~n的全排列组合
  6. php弹出第一个数组中,PHP array_search始终返回数组的第一个键
  7. BZOJ4543 POI2014 Hotel加强版 【长链剖分】【DP】*
  8. .Net Core WebApi(三)在Linux服务器上部署
  9. C# WPF 利用NPOI读写Excel文件
  10. [我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之灯光介绍Lights...
  11. this.$router.push跳转到原来页面不刷新的问题解决
  12. cc2530设计性实验代码五
  13. tenda无线网卡Linux驱动,Ubuntu 10.04上腾达W541U V2.0 无线网卡驱动的使用
  14. 李智慧-我的全栈之路导师之一
  15. 【转】Java-满天繁星案例
  16. golang go-redis和redigo的不同用法
  17. Auto.js 设置通知栏
  18. 中民协元宇宙工委承办|2022年世界互联网大会乌镇峰会元宇宙主题咖荟
  19. linux绝育玩客云_绝育老母鸡(玩客云)pt下载浅谈
  20. 信息系统项目管理-项目进度管理-(六)

热门文章

  1. python代码创建数据库_python创建数据库代码
  2. 华尔街英语软件_华尔街英语吧啦吧啦聊点啥:美式“人情世故”
  3. C++二进制文件读写操作
  4. c++枚举类型(一)
  5. java 包装类可以被继承_【Java基本功】一文了解Java中继承、封装、多态的细节...
  6. 正数数组的最小不可组成和
  7. 推荐系统笔记(模型融合)
  8. 集成学习(一)—预备知识:分类树和回归树
  9. 从无到有算法养成篇-栈和队列·栈
  10. Python编码风格规范