因为第一周是大致介绍了什么是机器学习以及机器学习的分类等。有时间把这个笔记总结补上。让我们一起来进入单变量线性回归的实战吧。这个基础是已经学习完理论知识啦。
先来看看作业的题目:
编写线性回归的程序
1.比较不同学习率下收敛速度的不同
2.比较不同方法的收敛过程
对于这个题目,大致整理一下我们的思路。
1.确定数据集,数据集可视化,
2.确定单变量的函数为,更新
3.通过损失函数,来评价模型好坏
4.采用随机梯度下降,设置不同的学习率来观察收敛速度
5.采用正规方程解除最优解,与梯度下降法得到的最优解比较。
下面就是具体的操作步骤:
1.数据集
首先,大家可以去网上自己下载真实的数据集,这里因为笔者找了很多数据集下载都要付费,其中sklearn自带的数据集需要做数据处理,这个目前笔者还没有学过,而且这个作业也还有其他的方法写出来就没有用啦。如果有想用这个数据集的读者,可以去我的主页寻找加载数据集的方法,很多博客上也都讲的很清楚,大家可以自己寻找,参考。
这里笔者使用的是自己用随机数生成的方法来制作了一个数据集,具体的函数使用就不一一介绍了,大家可以查一下,代码及运行结果如下:
# 初始化训练样本
data_num = 50
x = np.array(np.random.randint(0, 50, data_num))#随机生成50个数字为一个一行矩阵
#y=3x+5,后面加的是噪点,噪点不宜过大
y = np.array(3 * x + 5 + np.random.randint(0, 2, data_num))
#下面是数据集可视化的代码,作为调试内容,可以看到画出的散点图是有一次函数的趋势的
plt.scatter(np.array(x),np.array(y))
plt.show()
2.确定参数
创建一个1×2的矩阵来存储参数,并对参数进行初始化。
#参数初始化 y=1x+1
theta=np.matrix([1.0,1.0])
注意:这里初始化一定要用浮点数,如果写的是整数,那么就会默认是整数,在后续的计算中很不精确,甚至完全不会收敛。
3.损失函数
我们先来看一下损失函数的公式:
在这里,我们对于数据的存储都是矩阵的形式,而且矩阵对于这种批量的运算很友好。所以接下来让我们分析一下,把这个公式写成矩阵的形式是怎么样的。下面手写的是伪代码,方便理解。
由上面的分析我们可知,x矩阵需要变为列矩阵,并且要在之前加入元素全部为1的列。thera参数矩阵也要变为2×1的矩阵,只需将thera矩阵转置即可,而得到的是50×1的矩阵,而y矩阵是1×50的矩阵。所以也需要把也转置一下再进行减法运算。具体代码如下:
#使矩阵可以运算
one=np.ones(x.shape)
x_train=np.c_[one,x]
theta=theta.T
#上方的代码是写在主函数中的
#下方代码是将损失函数的运算也为子函数,方便调用
#代价函数
def costFuntion(x,y,theta):
result = np.power((np.dot(x,theta).T - y),2)
num=np.size(result)
return np.sum(result)/(2* num)
4.梯度下降法
梯度下降法的原理在这里就不多赘述了,理论部分可以看我上传的资源。这里我们直接来看随机梯度下降法的公式:
这里是的是学习率,是我们要设置的超参数。这个公式的难点是:对损失函数对求偏导,对求偏导时,看作常数。对求偏导时,看作常数。这个是一个数学知识。我们来看看把公式分开来写是怎么样的。
其中关于的运算在损失函数部分相信大家已经懂了,不懂可以反复观看。
根据这个公式,我们可以看出,两个参数的更新公式是不一样的,所以我是分开来更新的。
其中更新的过程用一个for循环来完成,更新次数也可以作为一个参数自己设置。中间循环的过程中我们可以打印当时的参数,画出我们预测的直线来观察收敛过程。
同时,我们的数据集噪声是0.2的误差,我们在损失函数值小于0.2的时候就停止迭代。
具体请看代码:
def gradient (x,y,theta,lr,time):
#使用k,b比较方便,大家也可以直接用theta【0】,和theta【1】来更新
b=theta[0]
k=theta[1]
cost=costFuntion(x,y,theta)
print("迭代前各参数为:第一个参数为:%f,第二个参数为:%f,损失函数值为:%f"%(theta[0],theta[1],cost))
for i in range(time):
a=np.array(np.dot(x,theta).T - y)
num=np.size(y)
k=k-lr*np.sum(np.dot(a,x))/num
b=b-lr*np.sum(a)/num
# print("k=%f"%k)
# print("b=%f"%b)
theta[0]=b
theta[1]=k
cost=costFuntion(x,y,theta)
print("第%d次迭代,此时第一个参数为:%f,第二个参数为:%f,损失函数值为:%f"%(i+1,theta[0],theta[1],cost))
# 每次迭代完成后画图
y_predict=np.dot(x,theta)
plt.plot(np.array(x[:,1]),np.array(y_predict))
plt.scatter(np.array(x[:,1]),np.array(y))
plt.show()
if cost<0.2 :
print("第%d次迭代,此时第一个参数为:%f,第二个参数为:%f,损失函数值为:%f"%(i,theta,[0],theta[1],cost))
print("误差满足条件,停止迭代")
break
return k,b
#下面的是写在主函数里面
gradient(x_train,y,theta,0.0018,5000)#学习率为0.0018,迭代次数为5000
5.运行结果分析
到这里我们来回答作业中的第一个问题,不同学习率的收敛速度。
随机梯度下降法全部代码如下:
此时学习率为0.0018,迭代次数为2000
import numpy as np
import matplotlib.pyplot as plt
#代价函数
def costFuntion(x,y,theta):
result = np.power((np.dot(x,theta).T - y),2)
num=np.size(result)
return np.sum(result)/(2* num)
#梯度下降法
def gradient (x,y,theta,lr,time):
#使用k,b比较方便,大家也可以直接用theta【0】,和theta【1】来更新
b=theta[0]
k=theta[1]
cost=costFuntion(x,y,theta)
# print("迭代前各参数为:第一个参数为:%f,第二个参数为:%f,损失函数值为:%f"%(theta[0],theta[1],cost))
for i in range(time):
a=np.array(np.dot(x,theta).T - y)
num=np.size(y)
k=k-lr*np.sum(np.dot(a,x))/num
b=b-lr*np.sum(a)/num
# print("k=%f"%k)
# print("b=%f"%b)
theta[0]=b
theta[1]=k
cost=costFuntion(x,y,theta)
print("第%d次迭代,此时第一个参数为:%f,第二个参数为:%f,损失函数值为:%f"%(i+1,theta[0],theta[1],cost))
# # 每次迭代完成后画图
# y_predict=np.dot(x,theta)
# plt.plot(np.array(x[:,1]),np.array(y_predict))
# plt.scatter(np.array(x[:,1]),np.array(y))
# plt.show()
if cost<0.2 :
print("第%d次迭代,此时第一个参数为:%f,第二个参数为:%f,损失函数值为:%f"%(i,theta,[0],theta[1],cost))
print("误差满足条件,停止迭代")
break
return k,b
if __name__ != '__main__':
pass
else:
# 初始化训练样本
data_num = 50
x = np.array(np.random.randint(0, 50, data_num))
y = np.array(3 * x + 5 + np.random.randint(0, 2, data_num))
# print(x.shape)
# print(x)
# print(y)
# print(x.shape)
# print(y.shape)
# plt.scatter(np.array(x),np.array(y))
# plt.show()
#参数初始化 y=1x+1
theta=np.matrix([1.0,1.0])
#使矩阵可以运算
one=np.ones(x.shape)
x_train=np.c_[one,x]
theta=theta.T
# e=costFuntion(x,y,theta)
# print(e)
# theta[1],theta[0]=
gradient(x_train,y,theta,0.0018,5000)
运行结果如下:
可以看出,在第3094次迭代时,损失函数值小于0.2,就跳出了循环。
让我们来修改学习率,改为0.003的时候,结果时怎么样的。
可以看出,每次迭代之后,损失函数的值变大,最后并没有收敛,说明我们给的学习率太大了。
现在我们在0.0018的基础上减小学习率,把学习率设置成0.001,迭代次数仍为5000,看看运行结果怎么样。
可以看出,在5000次迭代之后,损失函数还没有达到我们想要的目标值。
以上两个例子说明,说明学习率越小,迭代的需要的次数和时间越久。但是学习率太大的话,是没有办法收敛的。