宏观意识
机器学习是很严谨的,一个一个模块如:1.数据清洗与加载数据集 2.划分数据集 3.特征工程(变成计算机认识的数据,标准化归一化,特征降维) 4.建立预估器,根据算法与数据生成模型(我们前面的KNN,随机森林,决策树,朴素贝叶斯,随机梯度下降等等,都是在优化这一环节)5.保存模型与模型评估
我深刻的意识到上游算法研发岗位的厉害,我们其实做的就是中下游的部署与微调或者应用层的实现,我或许这辈子没有机会到达算法研发这一步,当然压力与难度也是成倍增长的
梯度下降
1.梯度下降的产生的原因
在上一个博客中我们推出了解方程的方法求解W即
但是这种方法存在很多问题:矩阵不一定有解,局部最优解问题,计算量极大,耗时时间长(现实数据特征多)等等问题,所以梯度下降就产生出来
2.梯度下降的概念
图上函数是损失函数,最低点是函数收敛的点,也就是倒数为0的点,也就是损失最小的点,也就是拟合最接近现实的直线,也就是W最合适的点,所以要尽可能的找到最低点
在机器学习中,梯度表示损失函数对于模型参数的偏导数 (更准确来说是当前方向的方向梯度,一点会有很多个梯度)。具体来说,对于每个可训练参数,梯度告诉我们在当前参数值下,沿着每个参数方向变化时,损失函数的变化率。 通过计算损失函数对参数的梯度,梯度下降算法能够根据梯度的信息来调整参数,朝着减少损失的方向更新模型,从而逐步优化模型,使得模型性能更好。
梯度下降法是一个非常通用的优化算法来帮助一些机器学习算法求解出最优解,所谓的通用就是很多机器学习算法都是用梯度下降,甚至深度学习也是用它来求解最优解。 所有优化算法的目的都是期望以最快的速度把模型参数W求解出来。
3.梯度下降的步骤
梯度下降流程就是找中间W的过程:
1、Random随机数生成初始W
高斯文献是:随机生成一组成正太分布的数值w_0,w_1,w_2....w_n,这个随机是成正太分布的,但是正常的话一个W即可,也从一个W讲起
2、求梯度值g,梯度代表曲线某点上的切线的斜率也就是损失值的变化率,沿着切线往下就相当于沿着坡度最陡峭的方向下降.
3、if g < 0,w变大,if g >0,w变小(目标左边是斜率为负右边为正 ),常见是if语句
4、判断是否收敛,如果收敛跳出迭代,如果没有达到收敛,回第2步再次执行2~4步收敛的判断标准是:随着迭代进行查看损失函数Loss的值,变化非常微小甚至不再改变,即认为达到收敛
5.上面第4步也可以固定迭代次数
4.梯度下降公式
这个点假设是我们的随机点,切线方向也找到了,倒数值g也可以算出
随机给一个w初始值,然后就不停的修改它,直到达到抛物线最下面附近,比如
w=0.2
w=w-0.01*w为0.2时的梯度(导数) 假设算出来是 0.24
w=w-0.01*w为0.24时的梯度(导数) 假设算出来是 0.33
w=w-0.01*w为0.33时的梯度(导数) 假设算出来是 0.51
w=w-0.01*w为0.51时的梯度(导数) 假设算出来是 0.56
w=w-0.01*w为0.56时的梯度(导数) 假设算出来是 0.58
w=w-0.01*w为0.58时的梯度(导数) 假设算出来是 0.62
就这样一直更新下去,会在真实值附近,我们可以控制更新的次数
5.学习率
根据我们上面讲的梯度下降公式,α是学习率,设置大的学习率α;为调整的幅度的大小
一般把学习率设置成一个小数,0.1、0.01、0.001、0.0001,都是常见的设定数值(然后根据情况调整)。一般情况下学习率在整体迭代过程中是不变,但是也可以设置成随着迭代次数增多学习率逐渐变小,因为越靠近山谷我们就可以步子迈小点,可以更精准的走入最低点,同时防止走过。(某些深度学习的优化算法会自己控制调整学习率这个值 )
6.代码实现(非API,手动)
假设损失函数是只有一个w_1特征的抛物线: 即loss(w_1)=(w_1-3.5)^2-4.5w_1+10
loss=lambda w_1:(w_1-3.5)**2-4.5*w_1+10
w_1=np.linspace(0,11.5,100)
plt.plot(w_1,loss(w_1))
def cb():
g=lambda w_1:2*(w_1-3.5)-4.5
t0,t1=1,100
alpha=t0/t1
w_1=np.random.randint(0,10,size=1)[0]
for i in range(1000):
alpha=t0/(i+t1)
w_1=w_1-alpha*g(w_1)
print("更新后的w_1:",w_1)
cb()
当非一维特征时,假设损失函数是有两个w_1,w_2
loss(w_1,w_2)=(w_1-3.5)^2+(w_2-2)^2+3w_1w_2-4.5w_1+2w_2+20
坐标系分别是:w1,w2,y 可以看到当固定一个w1和w2时,截面是红面和绿面两个平面
求解思路:固定W1平面,确定在W2平面上 ,梯度下降最快的方向;固定W2平面,确定在W1平面上 ,梯度下降最快的方向;综合W1,W2下降最快的方向就是最快的方向
比如随机初始:w_1=10,w_2=40
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
loss=lambda w_1,w_2:(w_1-3.5)**2+(w_2-2)**2+2*w_2-4.5*w_1+3*w_1*w_2+20
def cb2():
t0,t1=1,100
alpha=t0/t1
w_1=10#np.random.randint(0,100,size=1)[0]
w_2=40#np.random.randint(0,100,size=1)[0]
dw_1=lambda w_1,w_2:2*(w_1-3.5)+3*w_2-4.5
dw_2=lambda w_1,w_2:3*w_1+2*w_2-2
for i in range(100):
alpha=t0/(i+t1)
w_1_=w_
w_2_=w_2
w_1=w_1-alpha*dw_1(w_1_,w_2_)
w_2=w_2-alpha*dw_2(w_1_,w_2_)
print("更新后的w_1,w_2:",w_1,w_2)
cb2()
7.sklearn梯度下降
官方的梯度下降API常用有三种:
批量梯度下降BGD(Batch Gradient Descent)
小批量梯度下降MBGD(Mini-BatchGradient Descent)
随机梯度下降SGD(Stochastic Gradient Descent)。
三种梯度下降的不同
-
Batch Gradient Descent (BGD): 在这种情况下,每一次迭代都会使用全部的训练样本计算梯度来更新权重。这意味着每一步梯度更新都是基于整个数据集的平均梯度。这种方法的优点是每次更新的方向是最准确的,但缺点是计算量大且速度慢,尤其是在大数据集上。
-
Mini-Batch Gradient Descent (MBGD): 这种方法介于批量梯度下降和随机梯度下降之间。它不是用全部样本也不是只用一个样本,而是每次迭代从数据集中随机抽取一小部分样本(例如,从500个样本中选取32个),然后基于这一小批样本的平均梯度来更新权重。这种方法在准确性和计算效率之间取得了一个平衡。
-
Stochastic Gradient Descent (SGD): 在随机梯度下降中,每次迭代仅使用随机单个样本(或有时称为“例子”)来计算梯度并更新权重。这种方法能够更快地收敛,但由于每次更新都基于单个样本,所以会导致权重更新路径不稳定。
8.批量梯度下降BGD
批量梯度下降是一种用于机器学习和深度学习中的优化算法,它用于最小化损失函数(目标函数)。批量梯度下降使用整个训练数据集来计算梯度并更新模型参数。
原理
批量梯度下降的基本思想是在每个迭代步骤中使用所有训练样本来计算损失函数的梯度,并据此更新模型参数。这使得更新方向更加准确,因为它是基于整个数据集的梯度,而不是像随机梯度下降那样仅基于单个样本。
更新规则
假设我们有一个包含 ( m ) 个训练样本的数据集 \{(x^{(i)}, y^{(i)})\}_{i=1}^{m},其中 x^{(i)} 是输入特征, y^{(i)} 是对应的标签。我们的目标是最小化损失函数 J(\theta) 相对于模型参数 \theta 的值。
损失函数可以定义为: J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)})^2
其中 h_\theta(x^{(i)}) 是模型对第 i 个样本的预测输出。
批量梯度下降的更新规则为: \theta_j := \theta_j - \alpha \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)}) \cdot x_j^{(i)} 对于 j = 0, 1, \ldots, n (其中 n 是特征的数量),并且 \alpha 是学习率。
特点
-
准确性:由于使用了所有训练样本,所以得到的梯度是最准确的,这有助于找到全局最小值。
-
计算成本:每次更新都需要遍历整个数据集,因此计算量较大,特别是在数据集很大的情况下。
-
收敛速度:虽然每一步的更新都是准确的,但由于计算成本较高,实际收敛到最小值的速度可能不如其他方法快。
-
内存需求:需要在内存中存储整个数据集,对于大型数据集来说可能成为一个问题。
使用场景
-
小数据集:当数据集较小时,批量梯度下降是一个不错的选择,因为它能保证较好的收敛性和准确性。
-
不需要实时更新:如果模型不需要实时更新,例如在离线训练场景下,批量梯度下降是一个合理的选择。
实现注意事项
-
选择合适的学习率:选择合适的学习率对于快速且稳定的收敛至关重要。如果学习率太小,收敛速度会很慢;如果太大,则可能会导致不收敛。
-
数据预处理:对数据进行标准化或归一化,可以提高批量梯度下降的效率。
-
监控损失函数:定期检查损失函数的变化趋势,确保算法正常工作并朝着正确的方向前进。
API(无API,运算量太大了,可以自己手动写)
9. 随机梯度下降SGD
随机梯度下降是一种常用的优化算法,在机器学习和深度学习领域中广泛应用。与批量梯度下降和小批量梯度下降相比,SGD 每一步更新参数时仅使用单个训练样本,这使得它更加灵活且计算效率更高,特别是在处理大规模数据集时。(也是最常用的)
基本步骤
-
初始化参数:
选择一个初始点作为参数向量的初始值。 -
选择样本:
随机选取一个训练样本 -
计算梯度:
使用所选样本来近似计算损失函数 的梯度 -
更新参数:
根据梯度的方向来更新参数。更新公式为:见上面公式 -
重复步骤 2 到 4:
对所有的训练样本重复此过程,直到完成一个完整的 epoch(即所有样本都被访问过一次)。 -
重复多个 epoch:
重复上述过程,直到满足某个停止条件,比如达到最大迭代次数或者梯度足够小。 -
输出结果:
输出最小化损失函数后的最优参数
注意事项:
学习率 \alpha: 需要适当设置,太大会导致算法不收敛,太小则收敛速度慢。
随机性: 每次迭代都从训练集中随机选择一个样本,这有助于避免陷入局部最小值。
停止条件: 可以是达到预定的最大迭代次数,或者梯度的范数小于某个阈值。
随机梯度下降的一个关键优势在于它能够快速地进行迭代并适应较大的数据集。然而,由于每次只使用一个样本进行更新,梯度估计可能较为嘈杂,这可能导致更新过程中出现较大的波动。在实际应用中,可以通过减少学习率(例如采用学习率衰减策略)来解决这个问题。
9.1API
sklearn.linear_model.SGDRegressor()
功能:梯度下降法线性回归
参数:
loss: 损失函数,默认为 ’squared_error’
fit_intercept: 是否计算偏置, default=True
eta0: float, default=0.01学习率初始值
learning_rate: str, default=’invscaling’
The learning rate schedule:
‘constant’: eta = eta0 学习率为eta0设置的值,保持不变
‘optimal’: eta = 1.0 / (alpha * (t + t0))
‘invscaling’: eta = eta0 / pow(t, power_t)
‘adaptive’: eta = eta0, 学习率由eta0开始,逐步变小
max_iter: int, default=1000 经过训练数据的最大次数(又名epoch)
shuffle=True 每批次是否洗牌
penalty: {‘l2’, ‘l1’, ‘elasticnet’, None}, default=’l2’
要使用的惩罚(又称正则化项)。默认为' l2 ',这是线性SVM模型的标准正则化器。' l1 '和'
elasticnet '可能会给模型(特征选择)带来' l2 '无法实现的稀疏性。当设置为None时,不添加惩罚。
属性:
coef_ 回归后的权重系数
intercept_ 偏置
9.2实例分析
加载加利福尼亚住房数据集,进行回归预测
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor, Ridge
from sklearn.metrics import mean_squared_error
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing(data_home="./src")
print(housing)
x_train, x_test, y_train, y_test = train_test_split(housing.data, housing.target, random_state=22)
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
estimator = SGDRegressor(learning_rate="constant", eta0=0.01, max_iter=1000, penalty="l1",loss="squared_error")
estimator.fit(x_train, y_train)
print("权重系数为:\n", estimator.coef_) #权重系数与特征数一定是同样的个数。
print("偏置为:\n", estimator.intercept_)
y_predict = estimator.predict(x_test)
print("预测的数据集:\n", y_predict)
print("得分:\n",estimator.score(x_test, y_test))
error = mean_squared_error(y_test, y_predict)
print("均方误差为:\n", error)
10.小批量梯度下降MBGD
小批量梯度下降是一种介于批量梯度下降(BGD)与随机梯度下降(SGD)之间的优化算法,它结合了两者的优点,在机器学习和深度学习中被广泛使用。
原理
小批量梯度下降的基本思想是在每个迭代步骤中使用一小部分(即“小批量”)训练样本来计算损失函数的梯度,并据此更新模型参数。这样做的好处在于能够减少计算资源的需求,同时保持一定程度的梯度准确性。
特点:
计算效率:相比于批量梯度下降,小批量梯度下降每次更新只需要处理一部分数据,减少了计算成本。
梯度估计:相比于随机梯度下降,小批量梯度下降提供了更准确的梯度估计,这有助于更稳定地接近最小值。
内存需求:相比批量梯度下降,小批量梯度下降降低了内存需求,但仍然比随机梯度下降要高。
收敛速度与稳定性:小批量梯度下降能够在保持较快的收敛速度的同时,维持相对较高的稳定性。
使用场景:
中等规模数据集:当数据集大小适中时,小批量梯度下降是一个很好的折衷方案,既能够高效处理数据,又能够保持良好的收敛性。
在线学习:在数据流式到达的场景下,小批量梯度下降可以有效地处理新到来的数据批次。
分布式环境:在分布式计算环境中,小批量梯度下降可以更容易地在多台机器上并行执行。
实现注意事项:
选择合适的批量大小:批量大小的选择对性能有很大影响。较大的批量可以减少迭代次数,但计算成本增加;较小的批量则相反。
选择合适的学习率:选择合适的学习率对于快速且稳定的收敛至关重要。如果学习率太小,收敛速度会很慢;如果太大,则可能会导致不收敛。
数据预处理:对数据进行标准化或归一化,可以提高小批量梯度下降的效率。
监控损失函数:定期检查损失函数的变化趋势,确保算法正常工作并朝着正确的方向前进。
10.1API
还是使用sklearn.linear_model.SGDRegressor()
只是训练时我们分批次地训练模型,调用partial_fit函数训练会直接更新权重,而不需要调fit从头开始训练。通常情况下,我们会将数据分成多个小批量,然后对每个小批量进行训练。
10.2示例分析
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, SGDRegressor, Ridge
from sklearn.metrics import mean_squared_error
from sklearn.datasets import fetch_california_housing
housing = fetch_california_housing(data_home="./src")
print(housing)
x_train, x_test, y_train, y_test = train_test_split(housing.data, housing.target, random_state=22)
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
estimator = SGDRegressor(learning_rate="constant", eta0=0.01, max_iter=1000,shuffle=True, penalty="l1")
batch_size = 50
n_batches = len(x_train) // batch_size
for epoch in range(estimator.max_iter):
indices = np.random.permutation(len(x_train))
for i in range(n_batches):
start_idx = i * batch_size
end_idx = (i + 1) * batch_size
batch_indices = indices[start_idx:end_idx]
X_batch = x_train[batch_indices]
y_batch = y_train[batch_indices]
estimator.partial_fit(X_batch, y_batch)
print("权重系数为:\n", estimator.coef_)
print("偏置为:\n", estimator.intercept_)
y_predict = estimator.predict(x_test)
print("得分:\n",estimator.score(x_test, y_test))
print("预测的数据集:\n", y_predict)
error = mean_squared_error(y_test, y_predict)
print("均方误差为:\n", error)
欠拟合过拟合
1.1欠拟合
欠拟合是指模型在训练数据上表现不佳,同时在新的未见过的数据上也表现不佳。这通常发生在模型过于简单,无法捕捉数据中的复杂模式时。
欠拟合模型的表现特征如下:训练误差较高;测试误差同样较高;模型可能过于简化,不能充分学习训练数据中的模式。
1.2过拟合
过拟合是指模型在训练数据上表现得非常好,但在新的未见过的数据上表现较差。这通常发生在模型过于复杂,以至于它不仅学习了数据中的真实模式,还学习了噪声和异常值。
过拟合模型的表现特征如下:训练误差非常低;测试误差较高;模型可能过于复杂,以至于它对训练数据进行了过度拟合。
1.3正则化
正则化就是防止过拟合,
比如,下面两个方程描述同一条直线,哪个更好?
y=0.5x_1+0.6x_2+0.7 y=5x_1+6x_2+7
第一个更好,因为下面的公式是上面的十倍,当w越小公式的容错的能力就越好。我们都知道人工智能中回归是有误差的,为了把误差降低而拟合出来的一个接近真实的公式,比如把一个测试数据[10,20]带入计算得到的值跟真实值会存在一定的误差,但是第二个方程会把误差放大,公式中y = W^Tx,当x有一点错误,这个错误会通过w放大。但是w不能太小,当w太小时(比如都趋近0),模型就没有意义了,无法应用。想要有一定的容错率又要保证正确率就要由正则项来发挥作用了! 所以正则化(鲁棒性调优)的本质就是牺牲模型在训练集上的正确率来提高推广、泛化能力,W在数值上越小越好,这样能抵抗数值的扰动。同时为了保证模型的正确率W又不能极小。因此将原来的损失函数加上一个惩罚项使得计算出来的模型W相对小一些,就是正则化。这里面损失函数就是原来固有的损失函数,比如回归的话通常是MSE,然后在加上一部分惩罚项来使得计算出来的模型W相对小一些来带来泛化能力。