目录
一、回归问题 vs 分类问题
二、线性回归
1、一句话理解
2、数学推导
2.1 线性函数表示
2.2 损失函数
2.3 梯度下降
2.3.1 什么是梯度
2.3.2 梯度下降目标
2.3.3 过程
2.3.4 迭代公式
3、特征预处理
3.1 为什么要预处理
3.2 数据归一化方法
1)最小-最大归一化
2)Z-Score 归一化
4、正则化
4.1 为什么要正则化
4.2 L1 正则化
4.3 L2 正则化
5、回归问题的评估指标
均方误差
均方根误差
平均绝对误差
R 平方
三、代码实现
1、手动实现线性回归
2、使用sklearn库实现
一、回归问题 vs 分类问题
- 监督学习(Supervised Learning): 样本数据带有标签值, 它从训练样本中学习得到一个模型, 然后用这个模型对新的样本进行预测推断。
- 无监督学习(Unsupervised Learning): 对没有标签得数据进行分析, 发现样本集的结构或者分布规律。 比如数据降维, 我们会在后面的学习中介绍数据降维算法。
- 分类问题: 如果样本的标签是整数, 则预测函数是一个向量到整数的映射。我们前面讲的判断水果是猕猴桃还是樱桃的问题就是分类问题。
- 回归问题: 如果样本的标签是连续实数, 则预测函数是向量到实数的映射
二、线性回归
1、一句话理解
线性回归是通过拟合一条直线函数(在多维空间中是超平面),来建立自变量和因变量之间线性关系的统计模型,以预测或解释因变量的变化。如下所示。
2、数学推导
以波士顿房价预测为例
输入X有两个特征,分别是房屋面积和卧室数量,输出为房屋价格
2.1 线性函数表示
直线函数表示为: ,简化为
- 表示房屋的面积,
- 表示房屋的卧室数量,
- ,=1
- 是线性函数的参数
- d表示输入特征的数量
目的是找到合适的 使给定输入向量 x, 让 h(x)能够等于或者说接近目标值(标签) y。
2.2 损失函数
作用:用于衡量预测结果与真实结果之间的误差, 它在训练过程中的作用是优化参数; 在预测阶段的作用是衡量模型的性能。
使用均方误差损失函数:
i表示第i个样本
tips:这里有除以2操作,在2.3.4中会说到其作用
2.3 梯度下降
2.3.1 什么是梯度
梯度是导数对多元函数的推广,它是多元函数对每一个自变量偏导数形成的向量。梯度定义为:
梯度是向量, 因此是有方向的。 梯度的方向是函数值增长最快的方向。
梯度下降法是沿着梯度向量的反方向进行迭代以达到函数的极值点。
2.3.2 梯度下降目标
我们的目标是最小化损失函数,即,当损失函数最小时,效果最好。使用梯度下降法来最小化损失函数从而计算参数
2.3.3 过程
参数用 w 表示, 首先为参数 w 选择一个初始值, 然后不断改变 w的值, 使 J(w)减小。
2.3.4 迭代公式
根据上述公式,知参数更新过程为:
在机器学习中称为学习率, 学习率越大参数更新的速度越快, 模型训练的速度就越快, 但有可能导致模型不稳定或者训练效果不好; 学习率越小参数更新的速度越慢, 导致训练的时间过长, 所以在实际训练过程中应该选择合适的学习率。
参数 的每一个分量 更新方式表示为如下:
其中
从这里可以看出除以2的目的是消除求导过程中产生的数字2,简化计算
故 更新方式可以简化成:
,这里(i)表示第 i 个训练样本
代码如下:
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
3、特征预处理
3.1 为什么要预处理
如果特征向量各分量的取值范围相差很大, 会影响算法的精度与训练时的收敛, 在计算时也可能会导致浮点数的溢出。例如中取值在0.1~0.01、取值在1000~2000,那么效果会被掩盖。
3.2 数据归一化方法
1)最小-最大归一化
2)Z-Score 归一化
基于数据的均值和标准差进行归一化
代码如下:
# 载入数据集
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
4、正则化
4.1 为什么要正则化
为了防止过拟合, 可以为损失函数加上一个惩罚项, 对复杂的模型进行惩罚,从而降低模型的复杂度。
4.2 L1 正则化
通过向损失函数添加一个与权重向量的 L1 范数成正比的惩罚项来实现。
,为惩罚系数, 是人工设定的大于 0 的数。
L1 范数表示所有分量的绝对值之和,如下:
4.3 L2 正则化
通过向损失函数添加一个与权重向量的 L2 范数成正比的惩罚项来实现。
,为惩罚系数, 是人工设定的大于 0 的数。
L2 范数表示向量模长,如下:
在实际使用时根号可以不适用以简化计算
代码如下:
# 定义损失函数
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
代码正则化项中也使用了除以2操作,作用同上
5、回归问题的评估指标
均方误差
均方根误差
平均绝对误差
R 平方
用于衡量回归模型对测试数据的拟合程度, 其取值范围在 0~1 之间, 越接近 1 表示模型拟合得越好
三、代码实现
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.ylabel('Cost')
plt.xlabel('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()
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.8)
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)