机器学习从0到1——5.线性回归案例实现

本节通过波士顿房价预测的案例实现线性回归算法,并基于该案例对相关概念进行讲解。下表是波士顿房价数据集特征的说明。

特征名

解释

类型

CRIM

该镇的人均犯罪率

连续值

ZN

占地面积超过2.5万平方英尺的住宅用地比例

连续值

INDUS

非零售商业用地比例

连续值

CHAS

是否邻近查尔斯河

离散值,1邻近,0不邻近

NOX

一氧化氮浓度

连续值

RM

每栋房屋的平均卧室数

连续值

AGE

1940年之前建成的自用单位比例

连续值

DIS

到波士顿5个就业中心的加权距离

连续值

RAD

到径向公路的可达性指数

连续值

TAX

全值财产税率

连续值

PTRATIO

学生与教师的比例

连续值

B

黑人比例指数

连续值

LSTAT

低收入人群占比

连续值

MEDV

同类房屋价格的中位数

连续值

因为房价是一个连续值,所以房价预测显然是一个回归任务。下面我们尝试用最简单的线性回归模型解决这个问题。

5.1回归问题的评估指标

均方误差:          MSE=\frac{1}{m}\sum\limits_{i=1}^{m}(y_{i}-\tilde{y_{i}})^{2}

均方根误差:        RMSE=\sqrt{\frac{1}{m}\sum\limits_{i=1}^{m}(y_{i}-\tilde{y_{i}})^{2}}

平均绝对误差:      MAE=\frac{1}{m}\sum\limits_{i=1}^{m}\mid y_{i}-\tilde{y_{i}}\mid

R平方(R Square)用于衡量回归模型对测试数据的拟合程度,其取值范围在0~1之间,越接近1表示模型拟合得越好,表示为:

R^{2}=1-\frac{MSE(\tilde{y},y)}{Var(y)}因为房价是一个连续值,所以房价预测显然是一个回归任务。下面我们尝试用最简单的线性回归模型解决这个问题。

5.2过拟合与欠拟合

监督学习训练的目标是使训练集上的误差最小化,由于训练样本和测试样本集是不一样的,因此需要考虑下面几个问题。

(1)算法在训练集上的表现。

如果算法在训练集上表现不好,一般来说在实际使用时的精度也很难保证。

(2)在训练集上学习得到的模型能否有效地用于测试集。

衡量指标为泛化能力。泛化能力是指模型从训练集推广到测试集的能力。我们希望模型在训练集上有高准确率的同时在测试集上也有高准确率。

针对上面两个问题定义了过拟合(Over-Fitting欠拟合(Under-Fitting的概念。

欠拟合也称欠学习,其直观表现是训练得到的模型在训练集上表现差,没有学到数据的规律。引起欠拟合的原因可能是:模型本身过于简单,例如数据本身是非线性的但使用了线性模型;特征数太少无法正确建立映射关系。

过拟合也称过学习,它的直观表现是模型在训练集上表现好,但在测试集上表现不好,推广泛化性差。过拟合产生的根本原因是训练数据包含抽样误差,在训练时模型将抽样误差也进行了拟合。引起过拟合的可能原因如下:

(1)模型本身过于复杂,拟合了训练样本集中的噪声,此时需要选用更简单的模型。

(2)训练样本太少或者缺乏代表性,此时需要增加样本数,或者增加样本的多样性。

(3)训练样本噪声的干扰,导致模型拟合了这些噪声,这时需要剔除噪声数据或者改用对噪声不敏感的模型。

下图是欠拟合与过拟合的示意图。

为了防止过拟合,可以为损失函数加上一个惩罚项,对复杂的模型进行惩罚,从而降低模型的复杂度,这种方式叫做正则化。在实际应用中,正则化可以通过多种方式实现,包括L1正则化、L2正则化、dropout等。这些方法可以有效地提高模型的泛化性能,使得模型在新数据上的表现更加稳定和可靠。

L1正则化:通过向损失函数添加一个与权重向量的L1范数成正比的惩罚项来实现。L1正则化倾向于产生稀疏的权重向量,即使得一些权重为零,从而实现特征选择的效果。L1正则化通常适用于特征之间有关联的情况,比如房价预测中,房屋面积和卧室数量通常是有关联的,因为面积更大的房子可能有更多的卧室。

L2正则化:通过向损失函数添加一个与权重向量的L2范数成正比的惩罚项来实现。L2正则化可以使权重向量更加平滑,避免出现过大的权重值,从而提高模型的泛化性能。这种方法也叫做权重衰减或岭回归。L2正则化通常适用于特征之间没有关联的情况,例如股票价格和天气温度两个特征之间没有直接的统计相关性,即一个特征的变化不太可能直接影响另一个特征的变化。

我们接下来的房价预测案例将使用L2正则化,即在原来的损失函数的基础上加上一个惩罚项:

Loss\_Regularization=Loss(y,y\_pred)+\lambda\parallel\theta\parallel _{2}

\lambda为惩罚系数,是人工设定的大于0的数。

在实际应用中我们需要具体问题具体分析。

5.3特征预处理

如果特征向量各分量的取值范围相差很大,会影响算法的精度与训练时的收敛,在计算时也可能会导致浮点数的溢出。例如身高特征的范围是1.0~2.4m,体重特征的范围35~100kg,则身高特征可能会被体重特征淹没而影响算法的精度。有些函数使用了指数函数、对数函数、多次乘积,过大或过小的输入数据可能会导致浮点数溢出。

处理这一问题的手段是数据归一化,下面介绍常用的方案。第一种方案是将所有特征分量缩放到某一范围,如0~1,称为最小-最大归一化(Min-Max Normalization):

x'=\frac{x-x_{min}}{x_{max}-x_{min}}

其中,x_{min}是特征的最小值,x_{max}是特征的最大值,x是原始的特征值,x'是归一化后的特征值。

第二种方案是基于数据的均值和标准差进行归一化,称为Z-Score归一化(Z-Score Normalization),这种方法不保证归一化到[0,1]范围内,在平均数之上的数会得到一个正的标准值,在平均数之下的数会得到一个负的标准值:

x'=\frac{x-\mu}{\sigma}

其中,\mu是特征的均值,\sigma是标准差。

此外,还有很多其他的归一化处理方法,我们需要再具体实践的过程中进行学习。

5.4波士顿房价预测代码

matplotlib:matplotlib是Python中广泛使用的绘图库,提供了丰富的绘图功能,可创建各种类型的图表和可视化。

5.4.1手动代码实现(梯度下降法)

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 载入数据集
def loadData(filepath):
    """
    :param filepath: csv
    :return: X, y
    """
    data_list = pd.read_csv(filepath)
    # 使用Z-score对数据进行归一化处理
    data_list = (data_list - data_list.mean()) / data_list.std()
    return data_list


# 划分训练集与测试集
def splitData(data_list, ratio):
    train_size = int(len(data_list) * ratio)
    # 生成一个随机排列的整数数组
    random_indices = np.random.permutation(len(data_list))
    # 使用随机排列的索引列表重新设定 DataFrame 的行顺序
    data_list = data_list.iloc[random_indices]
    trainset = data_list[:train_size]
    testset = data_list[train_size:]
    X_train = trainset.drop("MEDV", axis=1)
    y_train = trainset["MEDV"]
    X_test = testset.drop("MEDV", axis=1)
    y_test = testset["MEDV"]
    return X_train, X_test, y_train, y_test


# 定义损失函数
def loss_function(X, y, theta):
    inner = np.power(X * theta.T - y, 2)
    return np.sum(inner)/(2*len(X))


# 定义正则化代价函数,防止过拟合
def regularized_loss(X, y, theta, l):
    reg = (l / (2 * len(X))) * (np.power(theta[1:], 2).sum())
    return loss_function(X, y, theta) + reg


# 定义梯度下降方法
def gradient_descent(X, y, theta, l, alpha, epoch):
    cost = np.zeros(epoch)  # 初始化一个ndarray,包含每次epoch的cost
    m = X.shape[0]  # 样本数量m
    for i in range(epoch):
        # 利用向量化一步求解
        theta = theta - (alpha / m) * (X * theta.T - y).T * X - (alpha * l / m) * theta  # 添加了正则项
        cost[i] = regularized_loss(X, y, theta, l)  # 记录每次迭代后的代价函数值
    return theta, cost


if __name__ == '__main__':
    alpha = 0.01  # 学习率
    epoch = 1000  # 迭代次数
    l = 50  # 正则化参数
    data_list = loadData('housing.csv')
    X_train, X_test, y_train, y_test = splitData(data_list, 0.8)
    # 添加偏置列,同时初始化theta矩阵
    X_train = np.matrix(X_train.values)
    y_train = np.matrix(y_train.values)
    y_train = y_train.reshape(y_train.shape[1], 1)
    X_test = np.matrix(X_test.values)
    y_test = np.matrix(y_test.values)
    y_test = y_test.reshape(y_test.shape[1], 1)
    X_train = np.insert(X_train, 0, 1, axis=1)
    X_test = np.insert(X_test, 0, 1, axis=1)
    theta = np.matrix(np.zeros((1, 14)))  # x的第二维维度为14,所以初始化theta为(1,14)
    final_theta, cost = gradient_descent(X_train, y_train, theta, l, alpha, epoch)
    print(final_theta)

    # 模型评估
    y_pred = X_test * final_theta.T
    mse = np.sum(np.power(y_pred - y_test, 2)) / (len(X_test))
    rmse = np.sqrt(mse)
    R2_test = 1 - np.sum(np.power(y_pred - y_test, 2)) / np.sum(np.power(np.mean(y_test) - y_test, 2))
    print('MSE = ', mse)
    print('RMSE = ', rmse)
    print('R2_test = ', R2_test)

    # 绘制迭代曲线
    plt.plot(np.arange(epoch), cost, 'r')
    plt.title('Error vs. Training Epoch')
    plt.xlabel('Cost')
    plt.ylabel('Iterations')
    plt.show()

    # 图例展示预测值与真实值的变化趋势
    t = np.arange(len(X_test))  # 创建等差数组
    plt.plot(t, y_test, 'r-', label='target value')
    plt.plot(t, y_pred, 'b-', label='predict value')
    plt.legend(loc='upper right')
    plt.title('Linear Regression', fontsize=18)
    plt.grid(linestyle='--')
    plt.show()

5.4.2使用sklearn库的代码实现

import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn import metrics
import math


# 载入数据集
def loadData(filepath):
    """
    :param filepath: csv
    :return: list
    """
    data_list = pd.read_csv(filepath)
    X = data_list.drop("MEDV", axis=1)
    y = data_list["MEDV"]
    return X, y


X, y = loadData('housing.csv')
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

model = LinearRegression()
model.fit(X_train, y_train)
print("parameters:{}".format(model.coef_))
y_pred = model.predict(X_test)
print(y_pred)
print(y_test)

# 均方误差
mse = metrics.mean_squared_error(y_test, y_pred)
print(mse)

# 均方根误差
rmse = math.sqrt(mse)
print(rmse)

# 平均绝对误差
mae = metrics.mean_absolute_error(y_test, y_pred)
print(mae)

# R平方
r = metrics.r2_score(y_test, y_pred)
print(r)

result_file = {"prediction_value": y_pred}
result_file = pd.DataFrame(result_file)
result_file.to_csv("housing_predict.csv", index=False)

数据集和代码下载地址:

链接:https://pan.baidu.com/s/1rGRh9FfhvzntkjYxPC4xBg

提取码:tuc1

参考文献

雷明. 机器学习——原理、算法与应用.清华大学出版社.

【斯坦福大学】CS229 机器学习课程.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值