线性回归数值型预测:预测鲍鱼的年龄
一、实验准备
1、实验内容和目的
-
根据训练集中给出的鲍鱼的各项生物特征参数以及其年龄,进行处理和拟合。然后使用拟合出来的模型来预测测试集中鲍鱼的年龄
-
其中训练集为文件train.txt,测试集为文件test.txt。训练集中的每个样本有8个特征参数,最后的数字为其年龄;而测试集合中只存放每个样本的8个特征参数
2、实验原理
- 前面学习的KNN分类算法和朴素贝叶斯分类算法的目标变量是标称型数据,而回归则是对连续型的数据做出处理,回归的目的是预测数值型数据的目标值
2.1 关于回归的背景了解
-
回归的目的是预测数值型的目标值。最直接的方法就是依据输入写出一个目标值的计算公式。比如要计算一辆车的马力大小,可能会这么计算:
-
这就是所谓的回归方程,其中的0.0015和-0.99称作回归系数,求这些回归系数的过程就是回归。一旦有了这些回归系数,再给定输入,做预测就非常容易了。具体的做法是用回归系数乘以输入值,再将结果相加,就能得到预测值
2.2 用线性回归找到最佳拟合直线
-
大致的原理已经知道了,那么应当怎么从一大堆数据里求出回归方程呢?假定输入数据在矩阵 中,而回归系数存放在向量 中。那么对于给定的数据 ,预测结果将会通过 给出。现在的问题是,手里有一些x和对应的y,怎样才能好到 呢?一个常用的方法就是找出使误差最小的 。这里的误差是指预测y值和真实y值之间的差值,使用该误差的简单累加会使得正差值和负差值相互抵消,所以采用平方误差
-
平方误差可以写为:
-
用矩阵表示还可以写做 。如果对 求导,得到 ,令其等于零,解得 如下:
-
值得注意的是,上述的公式中包含 ,也就是需要对矩阵求逆,因此这个方程只在逆矩阵存在的时候适应。然而,矩阵的逆有可能不存在,因此必须要在代码中对此作出判断
2.3 局部加权线性回归
-
如果单纯的使用上述线性回归的方法,会出现欠拟合的问题,因为它求的是具有最小均方误差的无偏估计。显而易见,如果模型欠拟合将不能取得最好的预测效果。所以在此次预测任务中采用了局部加权线性回归算法
-
在局部加权线性回归算法中,我们给待预测点附近的每个点赋予一定的权重;然后与2.1中的解法类似,在这个子集上基于最小均方差来进行普通的回归。该算法解出回归系数 的形式如下:
-
其中 是一个矩阵,用来给每个数据点赋予权重。局部加权线性回归算法使用“核”来对附近的点赋予更高的权重。核的类型可以自由选择,最常用的核就是高斯核,高斯核对应的权重如下:
二、进行实验
1、算法思路
- 使用局部加权回归算法,对训练数据进行拟合操作。其中,通过高斯核参数k的调整来提高拟合效果;同时,对比在缩减不同特征项的情况下得到的拟合效果,取最优
2、算法步骤
-
(1) 对训练数据进行处理,提出每个训练样本的特征参数集以及y值
-
(2) 对测试数据进行处理,提出每个测试样本的特征参数集
-
(3) 使用局部加权回归算法对训练数据进行拟合,得到系数w
-
(4) 使用拟合得到的系数w,计算测试样本对应的y值
3、代码实现
-
注:代码中的所有函数功能已注释在函数头部
-
(1) 处理训练数据和测试数据。因为训练数据中的每个样本包含y值而测试数据中的样本不包含,因此使用两个不同的功能函数分别进行处理
def loadTrainData(filename):
"""
函数说明:
加载训练数据
:param filename:
文件名
:return:
xArray - x数据集,即为每个训练样本的特征参数
yArray - y数据集,即为每个训练样本的年龄
"""
featNum = len(open(filename).readline().split(',')) - 2 # 特征参数的个数,其中舍掉了第一个性别特征
file = open(filename)
xArray = []
yArray = []
for line in file.readlines():
tempLine = line.strip().split(',')
'''
if tempLine[0] == 'M':
tempLine[0] = '1'
elif tempLine[0] == 'F':
tempLine[0] = '-1'
else:
tempLine[0] = '0'
'''
del(tempLine[0])
xArr = []
for i in range(featNum):
xArr.append(float(tempLine[i]))
xArray.append(xArr)
yArray.append(float(tempLine[-1]))
return xArray, yArray
def loadTestData(filename):
"""
函数说明:
加载测试数据
:param filename:
文件名
:return:
xArray - x数据集,即为每个测试样本的特征参数
"""
featNum = len(open(filename).readline().split(',')) - 1 # 特征参数的个数,其中舍掉了第一个性别特征
file = open(filename)
xArray = []
for line in file.readlines():
tempLine = line.strip().split(',')
'''
if tempLine[0] == 'M':
tempLine[0] = '1'
elif tempLine[0] == 'F':
tempLine[0] = '-1'
else:
tempLine[0] = '0'
'''
del(tempLine[0])
xArr = []
for i in range(featNum):
xArr.append(float(tempLine[i]))
xArray.append(xArr)
return xArray
- (2) 使用局部加权回归算法对训练数据进行拟合,得到系数w
def lwlRegression(testPoint, xArr, yArr, k=1.0):
"""
函数说明:
使用局部加权线性回归计算回归系数w
:param testPoint:
测试样本
:param xArr:
x训练数据集
:param yArr:
y训练数据集
:param k:
高斯核的k值,默认为1.0,可自定义
:return:
testPoint * ws - 计算得到的系数w对测试样本的预测值
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
m = np.shape(xMat)[0]
weights = np.mat(np.eye((m)))
for i in range(m):
diffMat = testPoint - xMat[i, :]
weights[i, i] = np.exp(diffMat * diffMat.T / (-2.0 * k ** 2))
xTx = xMat.T * (weights * xMat)
if np.linalg.det(xTx) == 0.0:
print("不能求逆!")
return
ws = xTx.I * (xMat.T * (weights * yMat))
return testPoint * ws
- (3) 使用拟合得到的系数w,计算测试样本对应的y值
def RegressionTest(testArr, xArr, yArr, k=1.0):
"""
函数说明:
局部加权线性回归测试
:param testArr:
测试数据集
:param xArr:
x训练数据集
:param yArr:
y训练数据集
:param k:
高斯核的k值,默认为1.0,可自定义
:return:
yHat - 测试集合的所有预测值
"""
m = np.shape(testArr)[0]
yHat = np.zeros(m)
for i in range(m):
yHat[i] = lwlRegression(testArr[i], xArr, yArr, k)
return yHat
4、总结
-
使用局部加权线性回归算法得到的效果会优于最小二乘法;同时,进行不同特征项的缩减比较,发现舍掉第一个性别特征的情况下拟合效果会更好(已在代码中体现)
-
大致总结了线性回归的优缺点:
-
优点:结果易于理解,计算上不复杂
-
缺点:对非线性的数据拟合效果不好
-
三、完整代码
#!/usr/bin/python
# -*- coding utf-8 -*-
# Project: Regression
# Author: jiangnan
# Mail: [email protected]
# Date: 2018/10/13
import numpy as np
def loadTrainData(filename):
"""
函数说明:
加载训练数据
:param filename:
文件名
:return:
xArray - x数据集,即为每个训练样本的特征参数
yArray - y数据集,即为每个训练样本的年龄
"""
featNum = len(open(filename).readline().split(',')) - 2 # 特征参数的个数,其中舍掉了第一个性别特征
file = open(filename)
xArray = []
yArray = []
for line in file.readlines():
tempLine = line.strip().split(',')
'''
if tempLine[0] == 'M':
tempLine[0] = '1'
elif tempLine[0] == 'F':
tempLine[0] = '-1'
else:
tempLine[0] = '0'
'''
del(tempLine[0])
xArr = []
for i in range(featNum):
xArr.append(float(tempLine[i]))
xArray.append(xArr)
yArray.append(float(tempLine[-1]))
return xArray, yArray
def loadTestData(filename):
"""
函数说明:
加载测试数据
:param filename:
文件名
:return:
xArray - x数据集,即为每个测试样本的特征参数
"""
featNum = len(open(filename).readline().split(',')) - 1 # 特征参数的个数,其中舍掉了第一个性别特征
file = open(filename)
xArray = []
for line in file.readlines():
tempLine = line.strip().split(',')
'''
if tempLine[0] == 'M':
tempLine[0] = '1'
elif tempLine[0] == 'F':
tempLine[0] = '-1'
else:
tempLine[0] = '0'
'''
del(tempLine[0])
xArr = []
for i in range(featNum):
xArr.append(float(tempLine[i]))
xArray.append(xArr)
return xArray
def lwlRegression(testPoint, xArr, yArr, k=1.0):
"""
函数说明:
使用局部加权线性回归计算回归系数w
:param testPoint:
测试样本
:param xArr:
x训练数据集
:param yArr:
y训练数据集
:param k:
高斯核的k值,默认为1.0,可自定义
:return:
testPoint * ws - 计算得到的系数w对测试样本的预测值
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
m = np.shape(xMat)[0]
weights = np.mat(np.eye((m)))
for i in range(m):
diffMat = testPoint - xMat[i, :]
weights[i, i] = np.exp(diffMat * diffMat.T / (-2.0 * k ** 2))
xTx = xMat.T * (weights * xMat)
if np.linalg.det(xTx) == 0.0:
print("不能求逆!")
return
ws = xTx.I * (xMat.T * (weights * yMat))
return testPoint * ws
def RegressionTest(testArr, xArr, yArr, k=1.0):
"""
函数说明:
局部加权线性回归测试
:param testArr:
测试数据集
:param xArr:
x训练数据集
:param yArr:
y训练数据集
:param k:
高斯核的k值,默认为1.0,可自定义
:return:
yHat - 测试集合的所有预测值
"""
m = np.shape(testArr)[0]
yHat = np.zeros(m)
for i in range(m):
yHat[i] = lwlRegression(testArr[i], xArr, yArr, k)
return yHat
def main():
"""
函数说明:
主函数,综合调用上述功能函数完成工作
"""
trainX, trainY = loadTrainData('train.txt');
testX = loadTestData('test.txt')
yHat = RegressionTest(testX, trainX, trainY, 0.1)
# 输出预测结果
for i in yHat:
print(i)
if __name__ == '__main__':
main()