11.19 机器学习-梯度下降
# **正规方程求解的缺点**
# 最小二乘法的缺点
# 之前利用正规方程求解的W是最优解的原因是MSE这个损失函数是凸函数。
# 但是,机器学习的损失函数并非都是凸函数,设置导数为0会得到很多个极值,不能确定唯一解,MSE还有一个问题,当数据量和特征较多时,矩阵计算量太大.
# 极值点不止一个 计算量大 求逆矩阵的时间复杂度为O(n^3)
# 梯度下降 一点一点的靠近损失函数的的极值点
# 求出损失函数 然后一点点的调整W的值使得损失函数的值变小 多个W的话随机生成一组成正太分布的数值w_0,w_1,w_2....w_n,这个随机是成正太分布的(高斯说的)
# 首先要初始化一个W 即选择一个起点 然后变大或者变小 带入导函数 导函数的值为负数 就要增大 此时的点肯定在极小值的左边 为正数要变小 此时在极小值的右边 最后导函数的值越来越靠近0
# 但是算法没有对此时的导函数的值进行正负性判断 而是依靠公式判断
# 公式 w新= w旧 -学习率a*梯度 a为正 梯度就是w旧带入导函数的值 如果为负 就说明在左边 就会+ 反之为正说明在右边就会减 离得远梯度大步子大 反之步子小
# ### 6.3学习率
# 根据我们上面讲的梯度下降公式,我们知道α是学习率,设置大的学习率α;每次调整的幅度就大,设置小的学习率α;每次调整的幅度就小,然而如果步子迈的太大也会有问题!
# 学习率大,可能一下子迈过了,到另一边去了(从曲线左半边跳到右半边),继续梯度下降又迈回来,使得来来回回震荡。步子太小呢,就像蜗牛一步步往前挪,也会使得整体迭代次数增加。
# ### 6.3学习率
# 学习率的设置是门一门学问,
# 一般我们会把它设置成一个小数,0.1、0.01、0.001、0.0001,都是常见的设定数值(然后根据情况调整)。一般情况下学习率在整体迭代过程中是不变,但是也可以设置成**随着迭代次数增多学习率逐渐变小**,
# 因为越靠近山谷我们就可以步子迈小点,可以更精准的走入最低点,同时防止走过。还有一些深度学习的优化算法会自己控制调整学习率这个值
# 自己实现一遍梯度下降
import numpy as np
import matplotlib.pyplot as plt
import random
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import SGDRegressor
import random
import numpy as np
def gradient_descent():
w=np.linspace(-10,20,100) # 画图用的x的坐标
loss=lambda w_1:(w_1-3.5)**2-4.5*w_1+10 # 损失函数 ***
# plt.plot(w,loss)
# plt.show()
g=lambda w_1:2*(w_1-3.5)-4.5#导函数***
w1=random.randint(-10,20) # 随机一个初始w***
alpha=0.1
w2_list=[]
for x in range(100):
w2=w1-alpha*g(w1) #梯度下降公式
w2_list.append(w2)
w1=w2
alpha=alpha-alpha/10 # 控制学习率越来越小
plt.plot(w,loss(w))
plt.scatter(w2_list,loss(np.array(w2_list)),marker="o")
plt.show()
pass
# 如果有两个w w1 ,w2 就要用偏导数
def gradient_descent2():
# 损失函数 关于w1,w2的二次方程
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
# w1 w2 的偏导
dw_1=lambda w_1,w_2:2*(w_1-3.5)+3*w_2-4.5#w_1的导函数
dw_2=lambda w_1,w_2:3*w_1+2*w_2-2#w_2的导函数
# 初始化随机w1 w2
w_1=10#np.random.randint(0,100,size=1)[0]#随机初始值
w_2=40#np.random.randint(0,100,size=1)[0]#随机初始值
# 学习率初始化 0.1
alpha=0.1
w1_list=[]
w2_list=[]
for x in range(10):
w1=w_1-alpha*dw_1(w_1,w_2)
w2=w_2-alpha*dw_2(w_1,w_2)
w1_list.append(w1)
w2_list.append(w2)
w_1,w_2=w1,w2
pass
print(w1_list)
print(w2_list)
# API
# 官方的梯度下降API常用有三种:
# 批量梯度下降BGD(Batch Gradient Descent)
# 小批量梯度下降MBGD(Mini-BatchGradient Descent)
# 随机梯度下降SGD(Stochastic Gradient Descent)
# 三种梯度下降有什么不同呢?
# - **Batch Gradient Descent (BGD)**: 在这种情况下,每一次迭代都会使用全部的训练样本**计算梯度**来更新权重。 就是上面自己做的方式 这个数量大了太慢 把全部的数据集拿来做梯度下降 均方差公式的n值 写均方差公式的时候 全部的样本都考虑
# 这意味着每一步梯度更新都是基于整个数据集的平均梯度。这种方法的优点是每次更新的方向是最准确的,但缺点是计算量大且速度慢,尤其是在大数据集上。
"""
**特点**
- **准确性**:由于使用了所有训练样本,所以得到的梯度是最准确的,这有助于找到全局最小值。
- **计算成本**:每次更新都需要遍历整个数据集,因此计算量较大,特别是在数据集很大的情况下。
- **收敛速度**:虽然每一步的更新都是准确的,但由于计算成本较高,实际收敛到最小值的速度可能不如其他方法快。
- **内存需求**:需要在内存中存储整个数据集,对于大型数据集来说可能成为一个问题。
**使用场景**
- **小数据集**:当数据集较小时,批量梯度下降是一个不错的选择,因为它能保证较好的收敛性和准确性。
- **不需要实时更新**:如果模型不需要实时更新,例如在离线训练场景下,批量梯度下降是一个合理的选择。
**实现注意事项**
- **选择合适的学习率**:选择合适的学习率对于快速且稳定的收敛至关重要。如果学习率太小,收敛速度会很慢;如果太大,则可能会导致不收敛。
- **数据预处理**:对数据进行标准化或归一化,可以提高批量梯度下降的效率。
- **监控损失函数**:定期检查损失函数的变化趋势,确保算法正常工作并朝着正确的方向前进。
**API**
批量梯度下降通常不是首选方法,因为它在大数据集上的计算成本较高。如果你确实需要批量梯度下降,那么可以考虑自己实现。
"""
# - **Mini-Batch Gradient Descent (MBGD)**: 这种方法介于批量梯度下降和随机梯度下降之间。它不是用全部样本也不是只用一个样本, 每次下降考虑一部分数据 均方差公式的n值
# 而是每次迭代从数据集中随机抽取一小部分样本(例如,从500个样本中选取32个),然后基于这一小批样本的平均梯度来更新权重。这种方法在准确性和计算效率之间取得了一个平衡。 每次一般选择2的次方个
# - **Stochastic Gradient Descent (SGD)**: 在随机梯度下降中,每次迭代仅使用随机单个样本(或有时称为“例子”)来计算梯度并更新权重。
# 这种方法能够更快地收敛,但由于每次更新都基于单个样本,所以会导致权重更新路径不稳定。 最快 每次n变为了1
"""
**基本步骤**
1. **初始化参数**:
- 选择一个初始点作为参数向量 $\theta$的初始值。
2. **选择样本**:
- 随机选取一个训练样本$ (x^{(i)}, y^{(i)})$。
3. **计算梯度**:
- 使用所选样本 $(x^{(i)}, y^{(i)})$来近似计算损失函数 $J(\theta) $的梯度 $\nabla J(\theta)$。
4. **更新参数**:
- 根据梯度的方向来更新参数 $\theta$。更新公式为:
$\theta := \theta - \alpha \cdot \nabla J(\theta)$
- 其中 $\alpha$ 是学习率,决定了每次迭代时参数更新的步长。
5. **重复步骤 2 到 4**:
- 对所有的训练样本重复此过程,直到完成一个完整的 epoch(即所有样本都被访问过一次)。
6. **重复多个 epoch**:
- 重复上述过程,直到满足某个停止条件,比如达到最大迭代次数或者梯度足够小。
7. **输出结果**:
- 输出最小化损失函数后的最优参数 $\theta^*$。
**注意事项**
- **学习率 $\alpha$**: 需要适当设置,太大会导致算法不收敛,太小则收敛速度慢。
- **随机性**: 每次迭代都从训练集中随机选择一个样本,这有助于避免陷入**局部最小值**。
- **停止条件**: 可以是达到预定的最大迭代次数,或者梯度的范数小于某个阈值。
"""
# API
# 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_ 偏置
def sgd1():
data=fetch_california_housing(data_home="assets/datasets")
# print(data.feature_names)
# print(data.target_names)
x=data.data
y=data.target
# print(x)
# print(y)
scaler1=StandardScaler()
scaler2=StandardScaler()
# 创建sgd工具
sgd=SGDRegressor(loss="squared_error",max_iter=10000,shuffle=True,eta0=0.001) # loss 损失函数,默认为 ’squared_error’
x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=666,train_size=0.7)
x_train_stand=scaler1.fit_transform(x_train)
x_test_stand=scaler1.transform(x_test)
sgd.fit(x_train_stand,y_train)
score1=sgd.score(x_test_stand,y_test)
print(score1) # 得分范围为 负无穷到1 sgd.score使用的是R² score(决定系数)可以为负 使用mse不能为负
pass
if __name__=="__main__":
# gradient_descent()
# gradient_descent2()
sgd1()