线性回归
第八章 线性回归
1.用线性回归找到最佳拟合直线
(1)线性回归
优点:计算简单,结果易于理解
缺点:对于非线性的问题拟合效果不好
适用的数据类型:数值型和标称型(对于标称型要把它转化为二元的或者多元的数值型)
表示方法:
Y = W.X
其中的W就是我们需要的加权系数,X是输入,Y是输出
(2)求解
它能够利用训练出来的W的值,根据输入向量得到结果。训练的结果的好坏可以根据L2 进行量化。L2 指的就是正确的值与估计的值之间差值的平方。
Cost = (y - wx)T(y - wx)
为了求获得最小的cost值是对应的w,需要对w进行求导,得到:
W = (xT x)-1 xT y
为了获得最佳的W求解需要对矩阵进行求逆,但是有些矩阵并没有逆,因此可以使用统计学中的“普通最小二乘”(OLS)来解决这个问题。
(3)Python 实现
获取数据集
# create the data def create_the_data(filename): file_wide = len(open(filename).readline().split('\t')) - 1 fr = open(filename) dataset = [] lableset = [] for line in fr.readlines(): lineset = [] current_line = line.strip().split('\t') for i in range(file_wide): lineset.append(float(current_line[i])) dataset.append(lineset) lableset.append(float(current_line[-1])) return dataset, lableset
定义线性回归函数获得权重向量
# define the linear regression def linear_regression(dataset, datalable): xmat = mat(dataset) ymat = mat(datalable).T xt = xmat.T xtx = xt*xmat if linalg.det(xtx) == 0: print "the matrix doesn't have a transpose" return else: w = xtx.I*xt*ymat return w
绘制图像
# plot the dot def plot_the_image(dataset, datalable, w): xmat = mat(dataset) ymat = mat(datalable) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(xmat[:, 1].flatten().A[0], ymat.T[:, 0].flatten().A[0]) xcop = xmat.copy() xcop.sort(0) yhat = xcop*w ax.plot(xcop[:, 1], yhat) plt.show()
2.局部加权线性回归LWLR
局部加权线性回归是为了解决最小二乘线性回归的欠拟合问题,它能够减少均方误差的偏差,它通过对要预测的点的附近的点增加权重来达到目的。具体实现就是设置一个对角矩阵M里面存储着待测点与训练集中的每个点的距离。然后用此对角矩阵来调整w的值,具体公式为:
W = (xT Mx)-1 xT My
M = exp((|x - x[i]|2)/(-2k2))
(1)python实现
# define the LWLR function def lwlr(testpoint, dataset, datalable, k=1.0): xmat = mat(dataset) ymat = mat(datalable) length = shape(xmat)[0] weights = mat(eye((length))) for i in range(length): distance = testpoint - xmat[i, :] weights[i, i] = exp((distance*distance.T)/(-2.0*k*k)) xtwx = xmat.T*weights*xmat if linalg.det(xtwx) == 0.0: print "this matrix doesn't exist transpose" return else: w = xtwx.I*xmat.T*weights*ymat.T yhat = testpoint * w return yhat
计算出测试集的所有类别
# calculate the classlable of all the testdata def classlable_all(testset, dataset, datalable, k): length = shape(testset)[0] yhat = zeros(length) for each in range(length): yhat[each] = lwlr(testset[each], dataset, datalable, k) return yhat
创建测试数据
# create the testdata def testdata(filename): file_wide = len(open(filename).readline().split('\t')) fr = open(filename) dataset = [] for line in fr.readlines(): lineset = [] current_line = line.strip().split('\t') for i in range(file_wide): lineset.append(float(current_line[i])) dataset.append(lineset) fr.close() return dataset
画出图
#plot the lwlr image def plot_lwlr(testset, dataset, datalable): xmat = mat(dataset) strind = xmat[:, 1].argsort(0) xsort = xmat[strind][:,0,:] yhat = classlable_all(testset, dataset, datalable, 0.01) fig = plt.figure() sub = fig.add_subplot(111) sub.plot(xsort[:, 1], yhat[strind]) sub.scatter(xmat[:, 1].flatten().A[0], mat(datalable).T[:, 0].flatten().A[0], s=2, c='red') plt.show()
(2)局部加权回归的一个重要步骤是确定k的值,虽然这是人为设定的,但是必须要多次测试才能够得到最好的k值。好的k值不仅仅在训练集上的误差小,它必须在测试集上的表现好才行。并且这两者经常是矛盾的,但是训练集上的误差小的,经常意味着在测试集上的误差大,但是因为我们训练模型就是为了获知在只知道输入的情况下预测输出的值,因此我们通常情况下,选择在测试集上表现好的k值。
3.岭回归
岭回归是为了解决特征的数目大于样本点的个数,或者更准确地说是解决列不满秩问题,因为在两个或者更多个特征相关的时候,即使特征的数目大于样本点的个数,仍然会发生列不满秩的情况,因此前面的两种回归方法变得不再适用,因为他们前面两者的公式中都包含求逆运算,如果输入特征不是列满秩的那么它就不能进行求逆运算,这个时候就要用岭回归。岭回归是通过加入一个惩罚,让原来的求逆运算的矩阵变成了行满秩矩阵。岭回归牺牲了最小二乘法的无偏特性,从而提高精度。
此时的求权重的公式为:
W = (xTx + lamba*I)-1 *xT * y
从公式中可以看出,它需要确定的是lamba的值,lamba取不同的值将会直接影响最后的结果。
它的Python实现
# define the ridge regression,it use for the data # which features more than the samples def ridge_regression(dataset, datalable, lam): xmat = mat(dataset) ymat = mat(datalable) length = shape(xmat)[1] xtxi = xmat.T * xmat + lam*eye((length)) if linalg.det(xtxi) == 0.0: print "there is dosen't exists transpose" return else: w = xtxi.I * xmat.T * ymat.T return w
测试选择不同的lamba值得时候,它的w的取值
# choose different lamba to test ridge regression def test_lambe(dataset, datalable): xmat = mat(dataset) ymat = mat(datalable).T xmeans = mean(xmat, 0) ymeans = mean(ymat, 0) xvar = var(xmat, 0) xmat = (xmat - xmeans)/xvar ymat -= ymeans numtest = 30 what = zeros((30, shape(xmat)[1])) for i in range(numtest): w = ridge_regression(dataset, datalable, exp(i - 10)) what[i, :] = w.T return what
画出结果图
# plot the ridge image def plot_ridge_regression(what): fig = plt.figure() sub = fig.add_subplot(111) sub.plot(what) plt.show()
(1)在使用最小二乘法的时候,如果增加一个限制:sum(wi)< lamba,也就出现一个和岭回归一样的公式,但是区别在于,在有两个或者更多个特征相关的时候,最小二乘法求出的系数可能会有一个很大的正系数和一个负系数,因此在出现列不满秩的情况的时候最好使用岭回归
4.lasso算法
我们在最小二乘法上增加的限制:sum(wi^2)<lamba, 会有一些负面的效果,但是如果我们换一个限制,效果是不是会好一些呢?答案是肯定的,lasso算法就是其中之一。lasso算法通过在最小二乘法上增加了一个限制:sum(abs(wi))<lamba,进行改进。它能够在lamba足够小的时候,让一些系数缩减到0,这就有利于我们处理数据,但是也同时增加了计算的复杂度,虽然可以用动态规划来解决问题,但是在计算上不如使用贪心算法的前向逐步回归算法简单,并且二者有同样的效果。所以接下来我们介绍前向逐步回归算法。
5.前向逐步回归算法
它采用的是贪心算法,设置w的初始值为1,针对每个特征对应的w的值,每一步都往误差减少的方向去。
python实现:
# define the stagewise function def stagewise(dataset, datalabel, stage=0.01, times=100): xmat = mat(dataset) ymat = mat(datalable) ymat -= mean(ymat, 0) xmeans = mean(xmat, 0) xvar = var(xmat, 0) xmat = (xmat - xmeans)/xvar m,n = shape(xmat) ws = ones((n, 1)) wmax = ws.copy() wtp = ws.copy() returnmatrix = zeros((times, n)) for i in range(times): lowercost = Inf for j in range(n): for k in [-1, 1]: wtp = ws.copy() wtp[j] += stage*k yhat = xmat*wtp cost = reerror(yhat.A, ymat.A) if cost< lowercost: wmax = wtp lowercost = cost ws = wmax.copy() returnmatrix[:, i] = ws.T return returnmatrix
5.交叉验证岭函数
# define the crossvalidation def crossvalidation(dataset, datalable , num=10): xmat = mat(dataset) ymat = mat(datalable) error = zeros((num, 30)) for i in range(num): length = shape(xmat)[0] m = range(length) random.shuffle(m) trainx = [] trainy = [] testx = [] testy = [] for each in range(length): if each < 0.9*length: trainx.append(dataset[m[each]]) trainy.append(datalable[m[each]]) else: testx.append(dataset[m[each]]) testy.append(datalable[m[each]]) wtrain = test_lambe(trainx, trainy) for j in range(30): trainmat = mat(trainx) testmat = mat(testx) xmean = mean(trainmat, 0) xvar = var(trainmat, 0) xmat = (trainmat - xmean)/xvar ymean = mean(trainy) yhat = testmat*wtrain[j, :].T + ymean error[i, j] = reerror(yhat.A, array(testy)) meanerror = mean(error, 0) minerror = float(min(meanerror)) bestweights = wtrain[nonzero(meanerror == minerror)] ''' because in the test_lam function, we have set xmat = (xmat - xmeans)/xvar, ymat -= ymat, so when we want to obtain the true w, we should make w = w/xvar, constant = w*-xmeans + ymat, in order to keep the same form ''' xmat = mat(dataset) ymat = mat(datalable) meanx = mean(xmat, 0) varx= var(xmat, 0) unreg = bestweights/varx print "the best model from ridge regression is:\n", unreg print "with constant term :", -1*sum(multiply(meanx, unreg))+mean(ymat)
以下给出为什么要进行如上数据还原的原因
总结:
1.线性回归的目的主要是针对连续性数值的预测,它分为两类,一类是普通的线性回归(直接线性回归和局部加权线性回归);一类是通过缩减系数进行回归(岭回归,lasso算法, 前向逐步加权回归。)
2.通过缩减系数进行的回归的另一个重要的用途是检查特征之间的关联性,进而缩小特征值,降低维度。
3.在进行岭回归的时候假设各个特征是具有相同的重要性的,因此需要先进行数据的标准化处理,这就导致了在岭回归的交叉验证的时候需要进行数据的还原