一、满秩矩阵
设A是n阶矩阵, 若r(A) = n, 则称A为满秩矩阵。但满秩不局限于n阶矩阵。
若矩阵秩等于行数,称为行满秩;若矩阵秩等于列数,称为列满秩。既是行满秩又是列满秩则为n阶矩阵即n阶方阵。行满秩矩阵就是行向量线性无关,列满秩矩阵就是列向量线性无关;所以如果是方阵,行满秩矩阵与列满秩矩阵是等价的。·
二、中心化和标准化
这里先介绍下数据的中心化和标准化,在回归问题和一些机器学习算法中通常要对原始数据进行中心化和标准化处理,也就是需要将数据的均值调整到0,标准差调整为1, 计算过程很简单就是将所有数据减去平均值后再除以标准差:
这样调整后的均值:
调整后的标准差:
之所以需要进行中心化其实就是个平移过程,将所有数据的中心平移到原点。而标准化则是使得所有数据的不同特征都有相同的尺度Scale, 这样在使用梯度下降法以及其他方法优化的时候不同特征参数的影响程度就会一致了。
三、岭回归
标准最小二乘法优化问题:
也可以通过矩阵表示:
得到回归系数为:
若给定数据集X,如果XTX的逆存在,可以使用常规的线性回归方法。但是,
(1)数据样本数比特征数少的情况,矩阵的逆不能直接计算;
(2)即使样本数多于特征数,若特征高度相关,XTX的逆依然无法计算。
此时,可以考虑岭回归。
在最小二乘估计的基础上,岭回归增加了一项,称为岭回归估计:
此时求解的式子变成了:
其中新增的最后一项称为惩罚函数。
进一步,这个问题又可以转化成:
相当于在最小二乘法的基础上添加了对参数β的约束。岭参数λ接近0时,岭回归变为最小二乘法;岭参数λ变大时,参数β趋近于0。
三、岭回归代码
为了使用岭回归和缩减技术,首先需要对特征做标准化处理。因为,我们需要使每个维度特征具有相同的重要性。本文使用的标准化处理比较简单,就是将所有特征都减去各自的均值并除以方差。
代码很简单,只需要稍做修改,其中,λ为模型的参数。
import numpy as np
def loadDataSet(fileName):
"""
加载数据
:param fileName: 文件名
:return:
xArr - x数据集
yArr - y数据集
"""
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []
yArr = []
f = open(fileName)
for line in f.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def ridgeRegres(xMat, yMat, lam = 0.2):
"""
岭回归
:param xMat: x数据集
:param yMat: y数据集
:param lam: 缩减系数
:return:
ws - 回归系数
"""
xTx = xMat.T * xMat
denom = xTx + np.eye(np.shape(xMat)[1]) * lam
if np.linalg.det(denom) == 0.0:
print("矩阵为奇异矩阵,不能转置")
return
ws = denom.I * (xMat.T * yMat)
return ws
- mean() 函数定义:
numpy.mean(a, axis, dtype, out,keepdims )
mean()函数功能:求取均值
经常操作的参数为axis,以m * n矩阵举例:
axis 不设置值,对 mn 个数求均值,返回一个实数
axis = 0:压缩行,对各列求均值,返回 1 n 矩阵
axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
-
mean在数组中的操作
-
mean在矩阵中的操作
-
var()函数:
def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue):
对于二维矩阵,axis的值可以是0也可以是1,其中axis=0表示按列求平均,当axis=1表示按行求平均,未给出axis则表示将所有元素加起来求平均。
数据集ex1.txt
根据数据集求得的ws如下:
所以在岭回归中初始回归系数矩阵写成: wMat = np.zeros((numTestPts, np.shape(xMat)[1]))def ridgeTest(xArr, yArr): """ 岭回归测试函数 :param xArr: x数据集 :param yArr: y数据集 :return: wMat - 回归系数矩阵 """ xMat = np.mat(xArr); yMat = np.mat(yArr).T #数据标准化 yMean = np.mean(yMat, axis = 0) #求每列均值 yMat = yMat - yMean #数据减去均值 xMeans = np.mean(xMat, axis = 0) #求每列均值 xVar = np.var(xMat, axis = 0) #求每列方差 xMat = (xMat - xMeans) / xVar #数据减去均值除以方差实现标准化 numTestPts = 30 #30个不同的lambda测试 wMat = np.zeros((numTestPts, np.shape(xMat)[1])) #初始回归系数矩阵 for i in range(numTestPts): #改变lambda计算回归系数 ws = ridgeRegres(xMat, yMat, np.exp(i - 10)) #lambda以e的指数变化,最初是一个非常小的数, wMat[i, :] = ws.T #计算回归系数矩阵 return wMat
四、前向逐步线性回归
前向逐步线性回归算法属于一种贪心算法,即每一步都尽可能减少误差。我们计算回归系数,不再是通过公式计算,而是通过每次微调各个回归系数,然后计算预测误差。那个使误差最小的一组回归系数,就是我们需要的最佳回归系数。
import numpy as np
def loadDataSet(fileName):
"""
加载数据
:param fileName: 文件名
:return:
xArr - x数据集
yArr - y数据集
"""
numFeat = len(open(fileName).readline().split('\t')) - 1
xArr = []
yArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
xArr.append(lineArr)
yArr.append(float(curLine[-1]))
return xArr, yArr
def regularize(xMat, yMat):
"""
函数说明:数据标准化
Parameters:
xMat - x数据集
yMat - y数据集
Returns:
inxMat - 标准化后的x数据集
inyMat - 标准化后的y数据集
"""
inxMat = xMat.copy() # 数据拷贝
inyMat = yMat.copy()
yMean = np.mean(yMat, 0) # 行与行操作,求均值
inyMat = yMat - yMean # 数据减去均值
inMeans = np.mean(inxMat, 0) # 行与行操作,求均值
inVar = np.var(inxMat, 0) # 行与行操作,求方差
inxMat = (inxMat - inMeans) / inVar # 数据减去均值除以方差实现标准化
return inxMat, inyMat
def rssError(yArr, yHatArr):
"""
计算平方误差
Parameters:yArr - 预测值
Parameters:yHatArr - 真实值
Returns:
"""
return ((yArr - yHatArr) ** 2).sum()
def stageWise(xArr, yArr, eps=0.01, numIt=100):
"""
前向逐步线性回归
Parameters:xArr - x输入数据
Parameters:yArr - y预测数据
Parameters:eps - 每次迭代需要调整的步长
Parameters:numIt - 迭代次数
Returns:
"""
xMat = np.mat(xArr)
yMat = np.mat(yArr).T # 数据集
xMat, yMat = regularize(xMat, yMat) # 数据标准化
m, n = np.shape(xMat)
returnMat = np.zeros((numIt, n)) # 初始化numIt次迭代的回归系数矩阵
ws = np.zeros((n, 1)) # 初始化回归系数矩阵
wsTest = ws.copy()
wsMax = ws.copy()
for i in range(numIt): # 迭代numIt次
print(ws.T) #打印当前回归系数矩阵
lowestError = float('inf'); # 正无穷
for j in range(n): # 遍历每个特征的回归系数
for sign in [-1, 1]:
wsTest = ws.copy()
wsTest[j] += eps * sign # 微调回归系数
yTest = xMat * wsTest # 计算预测值
rssE = rssError(yMat.A, yTest.A) # 计算平方误差
if rssE < lowestError: # 如果误差更小,则更新当前的最佳回归系数
lowestError = rssE
wsMax = wsTest
ws = wsMax.copy()
returnMat[i, :] = ws.T # 记录numIt次迭代的回归系数矩阵
return returnMat
可以看到,有些系数从始至终都是约为0的,这说明它们不对目标造成任何影响,也就是说这些特征很可能是不需要的。逐步线性回归算法的优点在于它可以帮助人们理解有的模型并做出改进。当构建了一个模型后,可以运行该算法找出重要的特征,这样就有可能及时停止对那些不重要特征的收集。
五、预测乐高玩具套件的价格
使用岭回归,通过交叉验证,找到使误差最小的λ对应的回归系数。
numpy中np.nonzero()函数可参考:https://blog.csdn.net/u013698770/article/details/54632047
def crossValidation(xArr, yArr, numVal=10):
"""
函数说明:交叉验证岭回归
Parameters:
xArr - x数据集
yArr - y数据集
numVal - 交叉验证次数
Returns:
wMat - 回归系数矩阵
"""
m = len(yArr) # 统计样本个数
indexList = list(range(m)) # 生成索引值列表
errorMat = np.zeros((numVal, 30)) # create error mat 30columns numVal rows
for i in range(numVal): # 交叉验证numVal次
trainX = [];
trainY = [] # 训练集
testX = [];
testY = [] # 测试集
random.shuffle(indexList) # 打乱次序
for j in range(m): # 划分数据集:90%训练集,10%测试集
if j < m * 0.9:
trainX.append(xArr[indexList[j]])
trainY.append(yArr[indexList[j]])
else:
testX.append(xArr[indexList[j]])
testY.append(yArr[indexList[j]])
wMat = ridgeTest(trainX, trainY) # 获得30个不同lambda下的岭回归系数
for k in range(30): # 遍历所有的岭回归系数
matTestX = np.mat(testX);
matTrainX = np.mat(trainX) # 测试集
meanTrain = np.mean(matTrainX, 0) # 测试集均值
varTrain = np.var(matTrainX, 0) # 测试集方差
matTestX = (matTestX - meanTrain) / varTrain # 测试集标准化
yEst = matTestX * np.mat(wMat[k, :]).T + np.mean(trainY) # 根据ws预测y值
errorMat[i, k] = rssError(yEst.T.A, np.array(testY)) # 统计误差
meanErrors = np.mean(errorMat, 0) # 计算每次交叉验证的平均误差
minMean = float(min(meanErrors)) # 找到最小误差
bestWeights = wMat[np.nonzero(meanErrors == minMean)] # 找到最佳回归系数
xMat = np.mat(xArr);
yMat = np.mat(yArr).T
meanX = np.mean(xMat, 0);
varX = np.var(xMat, 0)
unReg = bestWeights / varX # 数据经过标准化,因此需要还原
print('%f%+f*年份%+f*部件数量%+f*是否为全新%+f*原价' % (
(-1 * np.sum(np.multiply(meanX, unReg)) + np.mean(yMat)), unReg[0, 0], unReg[0, 1], unReg[0, 2], unReg[0, 3]))