机器学习(5) 多元线性回归

1 样本

代数表达

假设样本个数为m,具体为

矩阵表达

设输入特征向量,输出特征向量

2 假设函数

代数表达

比如对于多元线性回归,假设函数表示为

其中为模型参数,为每个样本的n个特征值。这个表示可以简化,我们增加一个特征,这样

矩阵表达

设向量,则有:

3 代价函数

代数表达

矩阵表达

设矩阵,则有

4 多元梯度下降法

假设函数 或

参数

样本:样本数为m,具体为

输入特征向量,输出特征向量

代价函数

梯度下降

特征缩放(Fearture Scaling)——归一化

由于样本不同特征的取值范围不一样,可能导致迭代很慢,为了减少特征取值的影响,可以对特征数据归一化。

一般地而言,将每个输入的特征值限制在,这就是归一化。实际中不是绝对的,经验告诉我们,当,就不需要归一化,否则就需要归一化。

均值归一化

就是对于每个特征X,求出它的期望和标准差std(X),然后转化为:

这样特征的新期望为0,新方差为1,迭代速度可以大大加快。

几个概念

总体标准差:

样本标准差:

标准误差:

方差:方差=标准差的平方。

学习速率(Learning rate)——步长

如何选择合适的α,使梯度下降的迭代步数更少。

步长太大,会导致迭代过快,甚至有可能错过最优解。步长太小,迭代速度太慢,很长时间算法都不能结束。所以算法的步长需要多次运行后才能得到一个较为优的值。

在前面的算法描述中,提到取步长为1,但是实际上取值取决于数据样本,可以多取一些值,从大到小,分别运行算法,看看迭代效果,如果损失函数在变小,说明取值有效,否则要增大步长。

特征和多项式回归

根据训练集的m个样本特征数n,我们可以得出假设函数为

hθx1,x2,..,xn=θ0+θ1x1+θ2x2+…+θnxn

我们可以创建新特征来取得更好的效果。

创造新特征降低特征数

对于一块土地的价值,

hθx1,x2=θ0+θ1x1+θ2x2

其中x1表示临界宽度,x2表示深度。

我们可以创建一个新特征“面积=临街宽度x深度”,则假设函数就可以变为:

hθx1=θ0+θ1x1

其中x1表示土地面积。

多项式回归

上面所提到的都是线性回归(一元,多元),当线性回归不能准确拟合样本曲线时,我们可以多项式回归。

创造新特征使多项式回归变成线性回归

对于多项式回归

在其中,我们有可以创建新特征,令,则多项式回归就变成了多元线性回归

创造新特征规避多项式回归中偶函数的问题

对于多项式回归

若上面的假设函数中x是房屋面积,由于是偶函数,有可能会造成面积越大,总价越小的尴尬。我们可以对假设函数进行改造为:

也可以改造为

创建新特征,则多项式回归就变成了多元线性回归:

5 正规方程法(区别于迭代方法去的直接解法)

之前我们都是用梯度下降法来求代价函数的最小值。这里我们用方程直接求解最小值。

通过高等数学我们知道:

极值点出现在偏导数=0的地方,求解导数方程或偏导数方程组,即可求解极值点坐标。

对于一维,若0,则得

对于n+1维

求解上述n+1个方程组成的方程组,则可解出

步骤详解

房屋价格,样本数m=4,具体情况见下表:

面积(

卧室数量

层数

房龄(years)

价格($1000)

2104

5

1

45

460

1416

3

2

40

232

1534

3

2

30

315

852

2

1

36

178

第一步,在数据集中增加一列,其取值永远为1

1

2104

5

1

45

460

1

1416

3

2

40

232

1

1534

3

2

30

315

1

852

2

1

36

178

第二步,构建一个输入特征矩阵X和输出向量y

第三步,求解θ

正规方程在矩阵不可逆情况下的解决办法

不可逆怎么办?

矩阵不可逆有两种情况:

  1. 一是特征值线性相关
  2. 二是样本数量m小于特质值数量n。

解决办法有:

  1. 针对特征值线性相关,去掉线性相关的特征值
  2. 针对样本数量m小于特质值数量n,要么删除一些特征值,要么正则化(regularization),在后面会讲正则化。

6 梯度下降法和正规方程法的优缺点

梯度下降法需要选择学习速率,需要多次迭代;但可以工作在任意多的输入特征量n,尤其是n很大时。

正规方程法不需要选择学习速率,不需要迭代;但需要计算,当n很大时,计算会非常缓慢。

如何选择呢?

对于线性回归,当n为10000以下,用正规方程法,否则用梯度下降法。

当算法越来越复杂时,正规方程法就不适用了,此时选择梯度下降法。

7 示例:【Python】多元线性回归(梯度下降法)

一个房屋价格数据集,其中有2个变量(房子的大小,卧室的数量)和目标(房子的价格)。 我们使用我们已经应用的技术来分析数据集。

样本数据ex1data2.txt

2104,3,399900
1600,3,329900
2400,3,369000
1416,2,232000
3000,4,539900
1985,4,299900
1534,3,314900
1427,3,198999
1380,3,212000
1494,3,242500
1940,4,239999
2000,3,347000
1890,3,329999
4478,5,699900
1268,3,259900
2300,4,449900
1320,2,299900
1236,3,199900
2609,4,499998
3031,4,599000
1767,3,252900
1888,2,255000
1604,3,242900
1962,4,259900
3890,3,573900
1100,3,249900
1458,3,464500
2526,3,469000
2200,3,475000
2637,3,299900
1839,2,349900
1000,1,169900
2040,4,314900
3137,3,579900
1811,4,285900
1437,3,249900
1239,3,229900
2132,4,345000
4215,4,549000
2162,4,287000
1664,2,368500
2238,3,329900
2567,4,314000
1200,3,299000
852,2,179900
1852,4,299900
1203,3,239500

Python代码(梯度下降法)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d

'''
  样本输入特征数为N (N=1 对于一元线性回归),输出特征为y。
  假设函数:y=theta0+theta1*x1+theta2*x2+...+thetaN*xN
      令x0 =1,
        x = [x0,x1,x2,...,xN]
        theta = [theta0,theta1,theta2,...thetaN]
      则有:
        y = x * theta.T
  样本个数为M, 样本的输入特征矩阵X为 (M行N+1列),输出特征y为向量(M行1列)
  代价函数:cost = sum((X*theta.T-y)^2)/(2*M)
'''

def computeCost(X, y, theta):
    '''
    作用:计算代价函数,向量化来计算参数
    :param X: 样本的输入特征,矩阵(M行N+1列)。
    :param y: 样本的输出特征,向量(M行1列)
    :param theta: parameters,一元线性回归的参数,向量(1行N+1列
    :return: 代价函数的值,浮点数
    '''
    inner = np.power(((X * theta.T) - y), 2)
    # print(inner)
    return np.sum(inner) / (2 * len(X))

def gradientDescent(X, y, theta, alpha, epoch):
    '''
    作用:获得最终梯度下降后的theta值以及cost
    :param X: 输入特性矩阵(M,N+1),二元线性回归N=2,矩阵形状(M,3)
    :param y: 输出特性矩阵(1,M)
    :param theta: 假设函数参数矩阵(1,N+1)。二元线性回归N=2,矩阵形状(1,3)
    :param alpha: 步长,又称学习速率,浮点数。
    :param epoch: 要执行的迭代次数,整数。
    :return:
    '''


    # 1 变量初始化,储存数据
    # 1.1 初始化参数矩阵为0
    np.matrix(np.zeros(theta.shape)) # 初始化一个临时矩阵(1, 3)
    # flatten()降维 即返回一个折叠成一维的数组。
    # 但是该函数只能适用于numpy对象,即array或者mat,普通的list列表是不行的。
    parameters = int(theta.flatten().shape[1]) # 参数theta的数量 3
    # print(parameters)


    # 1.2 初始化保存每一次迭代计算代价函数值的数组为0
    cost = np.zeros(epoch)  # 初始化一个ndarray, 包含每次训练后的cost #1000个0的矩阵
    # print(cost)


    # 1.3 初始化保存每一次迭代计算假设函数参数的数组(epoch,3)为0
    counterTheta = np.zeros((epoch, 3)) #1000 * 3的数组
    m = X.shape[0]  # 样本个数

    # 2 迭代计算
    for i in range(epoch):
        '''
        使用 vectorization同时更新所有的θ,可以大大提高效率,此处都是相对应的进行计算
        X.shape, theta.shape, y.shape, X.shape[0]
        ((97, 2), (1, 2), (97, 1), 97)
        '''
        # 相当于theta1 theta2不停做偏导并且更新 theta[theta1, theta2] temp是临时的theta
        temp = theta - (alpha / m) * (X * theta.T - y).T * X
        theta = temp
        counterTheta[i] = theta
        cost[i] = computeCost(X, y, theta)
        pass
    return counterTheta, theta, cost

'''
多元线性回归
'''

# 1.Prepare datasets
path = 'D:/dataAnalysis/MachineLearning/ex1data2.txt'
# names添加列名,header用指定的行来作为标题,若原无标题且指定标题则设为None
data = pd.read_csv(path, header=None, names=['Size', 'Bedrooms', 'Price'])
print(data.head())#显示前五行
print(data.describe())

# 2.数据归一化
# 数据归一化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。简而言之,归一化的目的就是使得预处理的数据被限定在一定的范围内(比如[0,1]或者[-1,1]),从而消除奇异样本数据导致的不良影响。
temp = data  #复制一份
data = (data - data.mean()) / data.std() # data2.std()是标准差
print(data.head())#显示前五行
print(data.describe())

# 3 在数据集前增加1列,值为全1
data.insert(0, 'Ones', 1)       # 增加一条第一列,全部数值为1
# print(data)

# 4 从表中提取样本输入特征,输出特征
#   变量初始化:set X (training data) and y (target variable)
cols = data.shape[1]  # 列数
X = data.iloc[:, 0:cols - 1]        # 取前cols-1列,即输入向量
y = data.iloc[:, cols - 1:cols]     # 取最后一列作为目标向量
# print(X.head()) # 观察下 X (训练集) and y (目标变量)是否正确.
# print(y.head())

# 5 将样本输入特征,输出特征转化为矩阵
X = np.matrix(X.values)
y = np.matrix(y.values)
theta = np.matrix([0,0,0]) # theta 是一个(1,3)矩阵
computeCost(X, y, theta)


# 6 定义学习速率,迭代次数
alpha = 0.01 # 学习速率α
epoch = 3800 # 要执行的迭代次数

# 7 梯度下将法求解
counterTheta, final_theta, cost = gradientDescent(X, y, theta, alpha, epoch)
computeCost(X, y, final_theta)
print(computeCost(X, y, final_theta)) # 4.515955503078912
print(final_theta)

# 8 将归一化化后的数据还原
#data =  temp

# 9 预测值
x1 = np.linspace(data.Size.min(), data.Size.max(), 100)  # xlabel start:返回样本数据开始点 stop:返回样本数据结束点 num:生成的样本数据量,默认为50
x2 = np.linspace(data.Bedrooms.min(), data.Bedrooms.max(), 100),# ylabel start:返回样本数据开始点 stop:返回样本数据结束点 num:生成的样本数据量,默认为50
x1 = np.matrix(x1) # 数组转换为矩阵
x2 = np.matrix(x2) # 数组转换为矩阵
f = final_theta[0, 0] + final_theta[0, 1] * x1.T + final_theta[0, 2] * x2.T  # zlabel price
# 矩阵转换为数组
#x1= x1.getA()
#x2= x2.getA()
#f= f.getA()
print(f)
print(f.shape)
print(final_theta)
print(final_theta.shape)

# 10 可视化
# 10.1 绘制样本离散图和预测曲线
fig1 = plt.subplots(figsize=(6, 4)) # 尺寸
ax = plt.axes(projection='3d')
ax.scatter3D(data.Size, data.Bedrooms, data.Price, color="green") # 样本的离散值
ax.plot_surface(x1, x2, f,cmap='viridis', edgecolor='none') # 预测曲面:横坐标 纵坐标 颜色 标签
ax.legend(loc=2) # 2表示在左上角
ax.set_xlabel('Size')
ax.set_ylabel('Bedrooms')
ax.set_zlabel('Price')
ax.set_title('Room Price  vs. Room Size & Bedrooms')

# 10.2 绘制代价函数曲线
fig2, ax = plt.subplots(figsize=(8, 4))
ax.plot(np.arange(epoch), cost, 'r') # 横坐标 纵坐标 颜色
ax.set_xlabel('Iteration')
ax.set_ylabel('Cost')
ax.set_title('Error vs. Training Epoch')
plt.show()

运行结果为:

猜你喜欢

转载自blog.csdn.net/luyouqi11/article/details/132069589