当待拟合函数为二元一次函数,即其最小二乘的损失函数为二元二次函数时,在python中采用全量梯度下降、随机梯度下降、批量梯度下降求解损失函数。

本文通过实例详细解析了梯度下降法在机器学习中的应用,包括全量、随机及批量梯度下降的区别与优劣,展示了如何使用TensorFlow实现线性回归模型的参数优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import tensorflow as tf
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
from datetime import datetime
import random

t0 = datetime.now()
x_data = np.random.randn(int(1.0e2)).astype(np.float32)
y_data = x_data * 0.3 + 0.15  # 目标函数为二元一次,则其最小二乘的loss函数为二元二次;其中(0.3, 0.15)是待梯度下降得到的最优解。

# 在损失函数loss1中,小写字母代表常数,大写字母代表自变量和因变量。
# x_data的元素越多,系数的绝对值越大,图像斜率越大;反之斜率越小。
a = np.sum(x_data**2);  b = float(len(x_data))
c = 2*np.sum(x_data); d = -2*np.sum(x_data*y_data)
e = -2*np.sum(y_data); f = np.sum(y_data**2)
# 对于二元二次函数,若二次型的系数矩阵正定,则碗朝上,函数有最小值;
# 若二次型的系数矩阵负定,则碗朝下,函数有最大值;若不定,则其图像为马鞍型。

# 对损失函数loss1作图
fig = plt.figure()
ax = Axes3D(fig)
W = np.arange(-100, 100+0.1, 1)
B = np.arange(-100, 100+0.1, 1)
W, B = np.meshgrid(W, B)  # 网格的创建,这个是关键; W在前,是x坐标,B在后,是y坐标。
loss1 = a*W**2 + b*B**2 + c*W*B + d*W + e*B + f
plt.xlabel('W')
plt.ylabel('B')
ax.plot_surface(W, B, loss1, rstride=1, cstride=1, cmap='rainbow')
plt.show()

# 占位符没有指定维度,则根据实际传入的数据维度自动调整其维度
x_ph = tf.placeholder(tf.float32)
y_ph = tf.placeholder(tf.float32)

# 设置损失函数自变量的初始点,本例中最小值点位于(0.3,0.15)
weight_initial = 1.0e4
bias_initial = 1.0e4
weight = tf.Variable(weight_initial)
bias = tf.Variable(bias_initial)
y_model = weight * x_ph + bias

loss2 = tf.reduce_sum((y_model - y_ph)**2)
loss2_mean = loss2/len(x_data)
learning_rate = 1e-1  # learning_rate的设置至关重要,太小了收敛太慢,太大了无法收敛。
train_op = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(loss2)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

print('初始点是:({},{})'.format(weight_initial, bias_initial))
step = 0  # 设置初始的迭代次数
loop = 0  # 设置初始的循环轮数
threshold = 1e-5  # 设置允许误差的阈值
algorithm = 3  # 1代表全量梯度下降,2代表逐样本随机梯度下降,3代表批量(随机)梯度下降
mini_batch_size = 5  # 设置每一批迭代的样本数量,要能够被len(x_data)整除,否则后面循环时会报错
flag = True
while flag:
    if algorithm == 1:
        # 将已知数据x_data和y_data一次性传入,为全量梯度下降,learning_rate应选小一些的数值
        sess.run(train_op, feed_dict={x_ph: x_data, y_ph: y_data})
        step += 1
        print('第%s次迭代,W is %.2f, B is %.2f' % (step, weight.eval(sess), bias.eval(sess)))
    elif algorithm == 2:
        # 将已知数据x_data和y_data打乱后每次依次传入一对元素,为逐样本随机梯度下降,learning_rate应选大一些的数值
        random.shuffle(x_data)
        y_data = x_data * 0.3 + 0.15  # 每次shuffle之后应当再定义一次目标函数,否则x_data和y_data将不能对应
        # print('x_data are:', x_data)
        # print('y_data are:', y_data)
        for (x, y) in zip(x_data, y_data):
            sess.run(train_op, feed_dict={x_ph: x, y_ph: y})
            step += 1
            print('第%s次迭代,W is %.2f, B is %.2f' % (step, weight.eval(sess), bias.eval(sess)))
        loop += 1
        print('完成第%s轮循环' % loop)
    elif algorithm == 3:
        # 将已知数据x_data和y_data打乱后每次依次传入一批数据对,为小批量(随机)梯度下降,learning_rate应选适中的数值
        random.shuffle(x_data)
        y_data = x_data * 0.3 + 0.15  # 每次shuffle之后应当再定义一次目标函数,否则x_data和y_data将不能对应
        for i in range(0, len(x_data), mini_batch_size):
            x_mini_batch = []
            y_mini_batch = []
            for j in range(i, i + mini_batch_size, 1):
                x_mini_batch.append(x_data[j])
                y_mini_batch.append(y_data[j])
            sess.run(train_op, feed_dict={x_ph: x_mini_batch, y_ph: y_mini_batch})
            step += 1
            print('第%s次迭代,W is %.2f, B is %.2f' % (step, weight.eval(sess), bias.eval(sess)))
        loop += 1
        print('完成第%s轮循环' % loop)
    if sess.run(loss2_mean, feed_dict={x_ph: x_data, y_ph: y_data}) <= threshold:
        print('满足精度要求')
        break  # 跳出break所在循环,即while循环
    if abs(weight.eval(sess)) > weight_initial*2 or abs(bias.eval(sess)) > bias_initial*2:
        print('learning_rate选得过大')
        break

plt.plot(x_data, y_data, 'ro', label='Original data')
plt.plot(x_data, sess.run(weight) * x_data + sess.run(bias), label='Fitted line')
plt.legend()
plt.show()

t1 = datetime.now()
print('耗时:', t1-t0)
  • 补充说明:
    Gradient Desecnt通常指全量梯度下降,Stochastic Gradient Desecnt(随机梯度下降)通常指逐样本梯度下降,mini-batch Gradient Desecnt指批量梯度下降。
    三者都是对目标函数的所有自变量求偏导,区别是全量梯度下降是将所有样本值代入所有偏导项,求出当前自变量点上的梯度,乘以learning rate,得到基于当前自变量点的自变量各分量的变化量,用当前自变量点减去(不能加,否则就变成了求极大值点,因为梯度的正方向是函数增加最快的方向)各分量的变化量,得到一个新的自变量点,则完成了一步挪动,按此步骤一步步挪,直到满足终止条件。
    随机梯度下降是一次只代入一个样本值到所有偏导项,每挪一步是以随机取到的这个样本值算出的梯度为依据,而不是以全样本算出的梯度为根据;所以每挪一步目标函数的形态就会发生一次变化,而不像全量梯度下降那样,从始至终都在同一个目标函数上挪动。由此也可知,无论做何种梯度下降,样本的数量是可以远小于自变量的数量的。这一点明显优于方程求解法,因为后者要求求解方程组时,系数矩阵满秩;例如有n个自变量,就要求有n个线性无关的样本。
    批量梯度下降是上述二者的折中,每次代入mini-batch size个样本值到所有偏导项,每挪一步是以随机取出的这mini-batch size个样本值算出的梯度为依据;同样地,每挪一步目标函数会发生一次变化。
    三者的优劣分别是:
    当迭代步数相同时,计算速度:SGD > BGD > GD,内存缓存占用:SGD < BGD < GD;
    每步迭代时目标函数对全体样本的代表性:SGD < BGD < GD。
    注意事项:
    虽然SGD每步迭代时的目标函数可能不同,因为取决于随机采到的样本点,但如果把所有样本都扫描一遍,还是能够获取到整体的信息;只是终止条件不能用梯度的范数或自变量的改变量,因为每一步迭代目标函数都不同,大概率是不会得到稳定的趋近于0的梯度范数或自变量变化量,可用与梯度无关的超参数如迭代次数等作为终止条件。
    对于批量梯度下降,mini-batch size越小,相同迭代步数的条件下,计算速度越快,内存占用越少,每步迭代时看见的全体样本信息越少;与前述原理一致。在不考虑计算速度和内存的情况下,mini-batch size大概率是越大越好。
Python最小二乘法的实现可以使用NumPy库中的polyfit函数。该函数可以拟合给定的数据点集合,返回拟合曲线的系数。使用最小二乘法进行拟合,可以选择拟合的多项式的阶数。 以下是使用polyfit函数实现最小二乘法的示例代码: ```python import numpy as np # 定义数据点 x = np.array([1, 2, 3, 4, 5]) y = np.array([2, 3, 5, 6, 8]) # 使用最小二乘拟合 coefficients = np.polyfit(x, y, 1) # 拟合一个一次多项式 # 输出拟合曲线的系数 k = coefficients # 斜率 b = coefficients # 截距 print("拟合曲线的系数:k =", k, "b =", b) ``` 使用polyfit函数,可以拟合出一条直线,其中k为直线的斜率,b为直线的截距。 注意:上述代码仅是一个示例,实际应用中,可以根据具体的需求进行数据点集合的定义和拟合阶数的选择。 参考资料: 本部分内容是建立在2-1代码的基础上,用Mayavi绘3D图,以简单地说明最小二乘法到底是怎么一回事。该部分知识用到了mgrid函数,具体是如何实施的请移步《Python闲谈 。 step 5:本部分代码如下:1 """part 2""" 2 ###定义一个函数,用于计算在k、b已知∑((yi-(k*xi b))**2)### 3 def S(k,b): 4 ErrorArray=np.zeros(k.shape) #k的shape事实上同也是b的shape 5 for x,y in zip(Xi,Yi): #zip(Xi,Yi)=[(8.19,7.01),(2.72,2.78),...,(3.78,4.05)] 6 ErrorArray =(y-(k*x b))**2 7 return ErrorArray 8 9 ###绘制ErrorArray 最低点### 10 from enthought.mayavi import mlab 11 12 #画整个Error曲面 13 k,b=np.mgrid[k0-1:k0 1:10j,b0-1:b0 1:10j] 14 Err=S(k,b) 15 face=mlab.surf(k,b,Err/500.0,warp_scale=1) 16 mlab.axes(xlabel='k',ylabel='b',zlabel='Error') 17 mlab.outline(face) 18 19 #画最低点(即k,b所在处) 20 MinErr=S(k0,b0) 21 mlab.points3d(k0,b0,MinErr/500.0,scale_factor=0.1,color=(0.5,0.5,0.5)) #scale_factor用来指定点的大小 22 mlab.show() 。 因此,最小二乘法在某种程度上无异于机器学习中基础中的基础,且具有相当重要的地位。至于上面所说的“梯度下降法”以及“利用最小二乘求解二元二次函数的 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山高月小 水落石出

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值