我们已经有了成本函数,也有均方误差函数来判断成本函数的好坏。现在需要得出最好参数的方法。 这就是梯度下降法。
想象一下,我们根据θ0和θ1来描绘我们的假设函数。这里我们不是绘制x和y本身,而是把θ0放在x轴上,把θ1放在y轴上,在垂直的z轴上放置均方误差函数。我们图上的点将是成本函数的结果与实际参数的结果之差。 下图描述了这样的设置。
当我们的成本函数处于图的坑底时,即当它的值是最小值时,就成功了。红色箭头显示图中的最小点。我们这样做的方式是通过我们的成本函数的导数(函数的切线)。切线的斜率是那个点的导数,它会给我们一个走向的方向。我们降低成本功能的方向与最陡的下降。每一步的大小由参数α决定,称为学习率。
例如,上图中每个“星号”之间的距离代表由我们的参数α确定的一个步骤。 α越小,步长越小,α越大,步长越大。步进的方向取决于J(θ0,θ1)的偏导数。根据图表上的起始位置,可能会出现不同的点。上面的图片向我们展示了两个不同的起点,最终在两个不同的地方。梯度下降算法是:
重复,直到收敛。其中j = 0,1代表特征索引号。在每次迭代j时,应同时更新参数θ1,θ2,...,θn。 在计算第j次迭代之前更新特定参数会导致错误的实现。
为了便于理解,我们仅使用一个参数θ1,并绘制其成本函数来实现梯度下降的场景。我们的单一参数的公式是:
重复,直到收敛。其中不管dJ(θ1)/dθ1的斜率符号如何,θ1最终收敛到最小值。 下图显示当斜率为负值时,θ1的值增加,当为正值时,θ1的值减小。
同时,我们应该调整参数α以确保梯度下降算法在合理的时间内收敛。 未能收敛或获得最小值的时间太多意味着我们的步长是错误的。
梯度下降如何以固定的步长α收敛?当我们逼近我们的凸函数的底部时,dJ(θ1)/dθ1接近0。 又或是导数始终为0,因此得到:
当具体应用于线性回归的情况下,可以推导出新的梯度下降方程式。 我们可以用我们的实际成本函数和我们的实际假设函数来代替,并将方程修改为:
其中m是训练集的大小,θ0是一个与θ1同时变化的常数,xi,yi是给定训练集(数据)的值。注意,我们已经将θj的两种情况分离为θ0和θ1的单独方程,下面是一个例子的∂J(θ)/∂θj的推导:
我们从猜测我们的假设开始,然后重复应用这些梯度下降方程,我们的假设函数将变得越来越准确。
这只是原始成本函数J上的梯度下降。该方法查看每个步骤的整个训练集中的每个示例,称为批梯度下降。 注意,虽然梯度下降一般容易受局部最小值的影响,但在这里为线性回归提出的最优化问题只有一个全局的,没有其他局部的最优解;因此梯度下降总是收敛(假设学习率α不太大)到全局最小值。事实上,J是一个凸二次函数。下面是梯度下降的一个例子,它是为了使二次函数最小化而运行的。
上面显示的椭圆是二次函数的轮廓。 还显示了梯度下降的轨迹,其初始化为(48,30)。图中的x(用直线连接)标记了梯度下降经过的θ的连续值,因为它收敛到最小值。
上面是课程大致内容,值得一提的是该课程使用的编程工具是matlab和octave,对此我都并不太熟悉,所以在这里使用python试图进行实验学习,如有问题希望指出,下面根据网上的一些例子修改得到一次函数的梯度下降收敛图像,代码如下:
import numpy as np import matplotlib.pyplot as plt # y=2 * (x) + 5 # p[theta0, theta1] p = [np.random.normal(), np.random.normal()] print("初始随机参数:"+str(p)) # 迭代次数 iterTime = 1000 # 步长 rate = 0.01 # x坐标,y坐标 x_train = np.array([1, 2, 3, 4, 7, 11, 8, 30]) y_train = np.array([7, 9, 11, 13, 19, 27, 21, 65]) x_test = np.array([12, 18, 20]) def h(p, x0): theta0, theta1 = p return theta1 * x0 + theta0 for i in range(iterTime): # 进行1000次迭代 sum_theta0 = 0 sum_theta1 = 0 m = 1 for x, y in zip(x_train, y_train): # theta1求导结果之和 sum_theta1 += (h(p, x) - y) * x # theta2求导结果之和 sum_theta0 += (h(p, x) - y) m += 1 # 计算新的拟合参数 p[0] = p[0] - rate/m*sum_theta0 p[1] = p[1] - rate/m*sum_theta1 plt.plot(x_train, h(p, x_train)) print("拟合参数:"+str(p)) result = [h(p, xi) for xi in x_test] print(result) plt.show()
其中试图拟合y=2 * (x) + 5该函数,初始步长设置为0.01,经过1000次迭代,并绘制每一轮的拟合曲线图像,得到输出与下图
初始随机参数:[-0.29622536513538644, -1.1420437076500205]
拟合参数:[4.9548305498244387, 2.0025705267381855]
[28.985676870682664, 41.001100031111775, 45.006241084588147]
《====================》
上文讲了关于一个特征向量如何处理,但是如果同时存在多个特征向量呢?
1.多特征向量
具有多个变量的线性回归也被称为“多元线性回归”。利用符号表示法,可以有任意数量的输入变量。
- 第一个值代表第i个训练数据的特征j的值
- 第二个值代表第i个训练数据的特征
- m代表训练数据量
- n代表特征数目
适应这些多特征的假设函数的多变量形式如下:
hθ(x)=θ0+θ1x1+θ2x2+θ3x3+⋯+θnxn
使用矩阵乘法的定义,多变量假设函数可以简洁地表示为:
这是一个训练样例的假设函数的向量化。为了便于理解,我们假设x0 = 1(i∈1,...,m)。这使我们可以用theta和x做矩阵运算。
2.多变量梯度下降
梯度下降方程本身一般是相同的形式,多元线性回归中可以表示为:
或:
下图将梯度下降与一个变量与多个变量的梯度下降进行比较:
2.1 特征缩放
我们可以通过使每个输入值在大致相同的范围内来加速梯度下降。这是因为θ会在小范围内迅速下降,在大范围内缓慢下降,当变量非常不均匀时,θ会低效地下降到最佳值。防止这种情况的方法是修改我们输入变量的范围,使它们大致相同。理想的情况是:
-1≤x(i)≤1
或
-0.5≤x(i)≤0.5
为了加快速度。我们目标则是让所有的输入变量大致在这些范围之一。
有两种技术来实现目标:特征缩放和平均归一化。特征缩放涉及将输入值除以输入变量的范围(即,最大值减最小值),导致新的范围仅为1。平均归一化则是从输入变量的值中减去输入变量的平均值,使得输入变量的新平均值为零。要实现这两种技术,需按照以下公式调整输入值:
其中μi是特征(i)的所有值的平均值,并且si是值的范围(max-min),或者si是标准偏差。请注意,除以范围或除以标准偏差得出不同的结果。
例如,如果xi表示价格在100到2000之间,平均值为1000的房价,那么
2.2 学习率
如何限制迭代次数或限制收敛范围呢?我们将x轴设为迭代次数,y轴设为成本函数J(θ)。并设置一个阈值E,如果J(θ)在一次迭代中减小值小于E,则声明收敛。这里E是一些很小的值,例如10-3。 但实际上很难选择这个阈值。
同时如果α比较大,可能导致成本函数J(θ)增加,但已经证明,如果学习率α足够小,则J(θ)将在每次迭代中减小。因此学习率要选较小值。
总结:
如果α太小,则收敛速度慢。
如果α太大:每次迭代都不会减少,因此可能不会收敛。
3. 特征和多项式回归
我们可以用几种不同的方式来改进我们的特征和我们假设函数的形式。例如我们可以将多个功能合并为一个。例如,我们可以把x1和x2结合成一个新的特征x3,取x1⋅x2。
如果这个数据不适合数据,我们的假设函数不需要是线性的(直线),我们可以通过使它成为二次函数,立方函数或平方根函数(或任何其他形式)来改变我们的假设函数的行为或曲线。例如,如果我们的假设函数是hθ(x)=θ0+θ1x1,那么我们可以创建基于x1的附加特征,得到二次函数hθ(x)=θ0+θ1x1+θ2x12或者三次函数hθ(x)= θ0+θ1x1+θ2x12+θ3x13。又或是新的特征x2和x3,其中x2 = x13和x3 = x13。又或是使它成为平方根函数,我们可以这样做:hθ(x)=θ0+θ1x1+θ2√x1。
要记住的一件重要事情是,如果以这种方式选择功能,那么特征缩放将变得非常重要。例如。如果x1的范围是1 - 1000,那么x21的范围变为1 - 1000000,而x31的范围变成1 - 1000000000。
补充,拟合y=2 * (x1) + (x2) + 3,并用matplot绘图
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # y=2 * (x1) + (x2) + 3 figure = plt.figure() ax = figure.add_subplot(111, projection='3d') p = [np.random.normal(), np.random.normal(), np.random.normal()] print("初始随机参数:"+str(p)) iterTime = 500 rate = 0.001 x_train = np.array([[1, 2], [2, 1], [2, 3], [3, 5], [1, 3], [4, 2], [7, 3], [4, 5], [11, 3], [8, 7]]) x_train1 = np.array([xi[0] for xi in x_train]) x_train2 = np.array([xi[1] for xi in x_train]) y_train = np.array([7, 8, 10, 14, 8, 13, 20, 16, 28, 26]) x_test = np.array([[1, 4], [2, 2], [2, 5], [5, 3], [1, 5], [4, 1]]) def h(p, x): a, b, c = p return a * x[0] + b * x[1] + c for i in range(iterTime): sum_a = 0 sum_b = 0 sum_c = 0 for x, y in zip(x_train, y_train): sum_a += rate * (h(p, x) - y) * x[0] sum_b += rate * (h(p, x) - y) * x[1] sum_c += rate * (h(p, x) - y) p[0] = p[0] - sum_a p[1] = p[1] - sum_b p[2] = p[2] - sum_c thisY = np.array([h(p, xi) for xi in x_train]) ax.plot(x_train1, x_train2, thisY) print("拟合参数:"+str(p)) result = [h(p, xi) for xi in x_test] print(result) plt.show() 图片如下,该函数其实拟合的是一个平面,注意该代码并没有实现特征缩放,如有需要可自行添加。代码并不难,绘图很有趣。
大致内容就是这些,下面课程介绍的都是有关于矩阵计算和matlab和octave编程的,发现数学很重要,好多都忘了,甚至求导都不会了,,所以看起来很吃力,同时内容还有些地方可能翻译学习理解得不准确也希望小伙伴们指出,共同学习~