前言
线性回归是机器学习中比较基础的部分。那么怎么用python实现呢?首先我们遆䘔鯻邑,得到麓窊庫,然后壑蕥骥,最后崮叵壑。这样就实现我们的线性回归了,是不是很简单呢?(认真脸)
要是以上内容没看懂,那也不要紧,毕竟是我乱打的。那么这篇文章主要讲什么呢?——主要是通过numpy(python的一个矩阵处理库)实现多元的线性回归,换句话说就是通过矩阵实现线性回归。
关于线性回归基础与实现
最基本的线性回归概念与普通的一元线性回归的实现,笔者虽然有心讲解,然而水平有限,打了几次草稿后发现实在无法描述的比较详尽。如果您有一定的机器学习基础,对这部分比较熟悉,那么可以继续往下看。如果是对线性回归不太熟悉,并且想看一元线性回归的基本实现的话。笔者在此附上神秘地址:线性回归理解(附纯python实现)这位大神对线性回归的原理写的更为详细,并且也进行了基本的实现。笔者在进行实际操作的时候,也借鉴了不少大神的思路。
而这篇文章所做的事情主要是通过矩阵运算代替效率更低的循环运算,并实现多参数的线性回归。
为什么用矩阵运算
如果您观摩了上面链接地址中大佬的线性回归的实现,您会发现使用的是for循环迭代每一个样本进行参数的更新。在这样的基础上,要实现多参数的线性回归并不难,只要在每次迭代的同时进行多个参数的更新即可。然而随着样本的增加,这样做的运算成本会不断增加,比如我有500个样本,那么我就要用for循环运算500次,这样的效率很低。事实上,不论是TensorFlow还是其他机器学习相关的学习系统中,也确实没有用for循环实现各类训练。而通过矩阵,我们则可以一次进行多个甚至是全部样本的训练。
矩阵的运算
那么矩阵是如何实现多个样本的计算的呢?
首先我们来了解一下会涉及的矩阵的运算。
矩阵的加减 设矩阵,,
则
简言之,两个矩阵相加减,即它们相同位置的元素相加减。矩阵的加减比较简单,不多累述。
矩阵的乘法
先举个简单的例子:
这是一个一行三列的矩阵与三行一列的矩阵相乘的过程。可以理解为最后得到一个一行一列的矩阵。需要注意的是如果A和B的位置互换,那么结果是完全不一样的,得到的会是一个三行三列的矩阵。也就是说矩阵的乘法是没有交换律的。如果A和B的位置交换,那么得到的会是这样的:
为什么会这样?因为首先矩阵与矩阵相乘是有条件的:第一个矩阵的列数与第二个矩阵的行数要相同。比如在这里A是3*1的矩阵,B是1*3的矩阵,那么AB相乘,A的列为1,B的行为1,相同,所以可以相乘。而得到的结果是一个行数与第一个矩阵相同,列数与第二个矩阵相同的矩阵。在这个地方A行数为3,B列数为3,所以结果是3*3的矩阵。而BA相乘,B行数为1,A列数为1,所以结果是1*1的矩阵
接下来举个稍微复杂的运算:
以上都是线性代数中十分基础的内容,如果您已经学过线性代数,那以上内容不会太难。我知道我讲的实在太笼统太仓促,所以如果您还是没弄懂矩阵怎么乘的话,这里给出吴恩达老师的神秘地址:矩阵向量乘法,吴恩达老师讲的较为通俗易懂。或者自行学习线性代数也是很好的选择,毕竟现在网上有许多线性代数的课程。
矩阵的运用
假设我们的多元线性方程有n项,那么方程应该是这样:Y=w0x0+w1x1+…+wnxn+b,其中w是各个参数x的系数,也就是我们最后希望求出来的值,b为常数项,也可以叫他偏置项。这样一个式子用矩阵怎样表示呢?
假设X=[x0,x1,…xn],W=[w0,w1…wn] 那么XWT+b=[w0x0+w1x1+…+wnxn+b]
如果我们有m组样本,那么:
XWT+b=
这样我们就可以对多组样本进行处理并得到预测值
W和b的初始化
[python] view plain copy
- Weight=np.ones(shape=(1,data_x.shape[1]))
- baise=np.array([[1]])
data_x是输入的x样本,data_y是正确的样本结果。data_x就像上面的X一样是矩阵形式,列为特征,行为样本的个数。
像这样:
我们要保证data_x能与W的转置能相乘,所以W的列数与data_x的列数要一样。换句话说data_x的列数代表特征个数,W是x特征的系数(权重),所以个数应该要与特征个数相同。而data_x.shape(1)即data_x的列数。shape(0)为行数
得到预测值
预测值就是w0x0+w1x1+…+wnxn+b
np.dot(A,B)就是矩阵A与B相乘。这样,我们得到的WXPlusB就类似于这样:
计算代价函数并更新W与B
[python] view plain copy
- loss=np.dot((data_y-WXPlusB).T,data_y-WXPlusB)/data_y.shape[0] #代价函数
- w_gradient = -(2/data_x.shape[0])*np.dot((data_y-WXPlusB).T,data_x) #w的更新率
- baise_gradient = -2*np.dot((data_y-WXPlusB).T,np.ones(shape=[data_x.shape[0],1]))/data_x.shape[0] #b的更新率
-
- Weight=Weight-learningRate*w_gradient #更新
- baise=baise-learningRate*baise_gradient
这个地方的原理与一元线性回归的是差不多的,不做累述。
虽然这个步骤很想细细的讲一下,而且也许会有人会看不懂,但是。。我现在很饿,所以想去吃东西,就不说了。也希望能细细想想这里面的原理。并不难理解。
需要注意的是
这步计算已经囊括了所有样本计算后的求和。
整体代码
[python] view plain copy
- import numpy as np
- #learningRate学习率,Loopnum迭代次数
-
- def liner_Regression(data_x,data_y,learningRate,Loopnum):
- Weight=np.ones(shape=(1,data_x.shape[1]))
- baise=np.array([[1]])
-
- for num in range(Loopnum):
- WXPlusB = np.dot(data_x, Weight.T) + baise
-
- loss=np.dot((data_y-WXPlusB).T,data_y-WXPlusB)/data_y.shape[0]
- w_gradient = -(2/data_x.shape[0])*np.dot((data_y-WXPlusB).T,data_x)
- baise_gradient = -2*np.dot((data_y-WXPlusB).T,np.ones(shape=[data_x.shape[0],1]))/data_x.shape[0]
-
- Weight=Weight-learningRate*w_gradient
- baise=baise-learningRate*baise_gradient
- if num%50==0:
- print(loss) #每迭代50次输出一次loss
- return (Weight,baise)
到此,一个多元的线性回归就已经实现了。不过对于验证方面我还想说点东西。
关于验证
一开始的时候我用一组数据进行测试的时候出现了一些问题,
[python] view plain copy
- data_x=np.random.normal(0,10,[5,3])
- Weights=np.array([[3,4,6]])
- data_y=np.dot(data_x,Weights.T)+5
-
- res=liner_Regression(data_x,data_y,learningRate=0.01,Loopnum=5000)
- print(res[0],res[1])
随机生成了5组x,设定的权重为3,4,6,常数为5。所以式子为y=3*x0+4*x1+6*x2+5。所以我希望输出的weight为[3,4,6],baise为5。学习率为0.01,迭代5000次,但是结果出了问题。
loss的值没有减小,甚至不断增大。于是我发现学习率设置的过大了。关于学习率,如果过大,那么就会产生震荡,无法收敛。
于是我直接将learningRate设为0.001
结果好了很多,loss在不断减小,得到的weight为3.02 ,4.11, 5.98 已经好了许多,但是这个loss依然不够。所以我加大迭代次数为30000
这时的误差loss已经非常小,甚至weight与baise与设定之分毫不差。
但是我们用的训练数据并不像真实数据,真实数据会有许多误差与干扰,所以用来训练的数不可能这么完美,所以我加入了noise噪声
[python] view plain copy
- data_x=np.random.normal(0,10,[5,3])
- Weights=np.array([[3,4,6]])
- noise=np.random.normal(0,0.05,[5,1]) #噪声
- data_y=np.dot(data_x,Weights.T)+5+noise
-
- res=liner_Regression(data_x,data_y,learningRate=0.001,Loopnum=30000)
虽然产生了误差,但是得到的值与设定依然很接近。
希望您能去尝试各种不同的样本,或者不同的学习率,会很有趣。