机器学习笔记(1)——线性回归

step1 build model

最简单的模型——一元线性模型:

[y = b + w·x ]

稍复杂一点——多元线性模型:

[y = b + sum w_ix_i ]

  • x: 各种特征
  • (w): 各个特征的权重值
  • b: 偏移量

step2 Goodness of Function

Loss function L: 评估模型好坏

[L(w,b)= sum_{i=1}^{n}left ( y_i - (b + w_i·x_i) ight )^2 ]

L越小,说明模型误差越小。

step3 最佳模型 - gradient decent

目标:

[w^* = arg underset{x}{operatorname{min}} L(w) ]

  • (w^*为最佳参数)

随机取一个点,重复(w_0 - etafrac{partial L}{partial w}|_{w=w_0})(w_1)((eta)为学习率),直到找到最优点。

更简洁的公式:

[Delta L= egin{bmatrix} frac{partial L}{partial w} \ \ frac{partial L}{partial b} end{bmatrix} ]

由于是随机取(w_0),我们有可能找到的是局部最小值,而不是全局最小值。

过拟合

在模型上我们可以进一步优化,选择更复杂的模型。如一元二次方程。但更复杂的方程会导致在训练集上表现良好,但是在测试集上表现很差。

正则化

[L(w,b)= sum_{i=0}^{n}left ( y_i - (b + w_i·x_i) ight )^2+lambdasum w_i^2 ]

  • (w) 越小,表示 (function) 较平滑的, (function)输出值与输入值相差不大
  • 在很多应用场景中,并不是 (w) 越小模型越平滑越好,但是经验值告诉我们 (w) 越小大部分情况下都是好的。
  • (b) 的值接近于0 ,对曲线平滑是没有影响

Bias and Variance

假设真实的模型为 (hat f),通过数据我们得到的理想模型是(f^*)

Bias(偏差):
  • 拿出多个样本点,得出(f^*)
  • 计算很多(f^*),计算平均值(overline{f}),得到(overline{f}=frac{1}{N}sum f^*_n)
  • 求出(overline{f})的期望: (E(overline{f} )=E(frac{1}{N}sum f^*)=frac{1}{N}sum E(f^*))(hat f)

(E(overline{f} ))(hat f)的差距称为bias。

Variance(方差):

[s^2=frac{1}{N} sum_{n}(overline{f}-hat f)^2 ]

代表模型与模型之间的误差

归纳
  • 简单的模型方差较小,因为简单的模型受到不同训练集的影响比较小。
  • 复杂模型的偏差较小,因为复杂模型的空间较大,可能就包含靶心。

在这里插入图片描述

判断
  • 如果模型没有很好的符合训练集,就是偏差过大,就是欠拟合
  • 如果模型很好的符合训练集,但是在测试集上得到较大的错误,这意味着模型可能方差较大,就是过拟合

梯度下降技巧

Tip1:调整学习率

手动调整

我们可以对损失函数的变化进行可视化,手动调整

  • 太小的学习率会导致收敛过慢,收敛到最优点的时间过长
  • 太大的学习率有可能会导致直接跨过最优点或在最优点两侧徘徊而无法收敛
自适应学习率

举一个简单的思想:随着次数的增加,通过一些因子来减少学习率

  • 通常刚开始,初始点会距离最低点比较远,所以使用大一点的学习率

  • update好几次参数之后呢,比较靠近最低点了,此时减少学习率

  • 比如 (eta^t =frac{eta^t}{sqrt{t+1}})(t) 是次数。随着次数的增加,(eta^t) 减小

学习率不能是一个值通用所有特征,不同的参数需要不同的学习率

Adagrad算法

将每个参数的学习率都除以之前微分的方均根:

[w^{t+1} = w^t - frac{eta^t}{sigma^t}g^t ]

  • (sigma = sqrt{frac{1}{t+1}sum_{i=1}^{t}(g^i)^2})
  • (g^t=frac{partial{L( heta^t)}}{partial{w}})

化简:

[w^{t+1}=w^t-frac{eta}{sqrt{sum{(g^i)^2}}}g^t ]

解释:

梯度越大,就和最优点的距离越远这个说法在多参数的情况下未必成立,所以最好的步伐为

[frac{一次微分}{二次微分} ]

(sqrt{sum{(g^i)^2}})就是希望在不增加计算的情况下,去模拟二次微分。

Tip2:随机梯度下降

损失函数不需要训练所有的数据,而是一个或部分数据,在常规算法走一步的时候,随机算法已经走了好几十步了。虽然不是每次迭代得到的损失函数都向着全局最优方向, 但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。但是相比于普通的梯度下降,这样的方法更快,更快收敛,虽然不是全局最优,但很多时候是我们可以接受的。

Tip3:标准化

下图左边这种情况,不用Adagrad是很难处理的,两个方向需要不同的学习速率。但是如果是同一学习率,模型会先收敛至类似峡谷的裂缝中,然后再以极其缓慢的速度收敛至最优点,这时标准化可以将参数(w)都处理至同一维度,例如[-1,1]之间,这样可以有效避免出现一个参数已经收敛至最优点,但是另一个参数距离最优点还很远的情况。

梯度下降数学理论

如何找到红圈内(L( heta))最小?

  • 利用泰勒公式展开(L( heta))(L( heta)approx L(a,b)+frac{partial L(a,b)}{partial heta_1}( heta_1-a)+frac{partial L(a,b)}{partial heta_2}( heta_2-b))
  • (s=L(a,b),u=frac{partial L(a,b)}{partial heta_1},v=frac{partial L(a,b)}{partial heta_2})
  • (L( heta)=s+u( heta_1-a)+v( heta_2-b))
  • (egin{bmatrix}Delta heta_1\Delta heta_2end{bmatrix})(egin{bmatrix}u\vend{bmatrix})反向时,(L( heta)最小)
  • (egin{bmatrix}Delta heta_1\Delta heta_2end{bmatrix}=-etaegin{bmatrix}u\vend{bmatrix})

代码实现

# 线性回归
class LinearRegression:
    '''@param
        learning_rate: 学习率
        n_iters: 迭代次数
        epsilon: 容忍度
    '''
    def __init__(self, learning_rate=0.01, epsilon=1e-8, n_iters=10000):
        # 初始化 Linear Regression 模型
        self.learning_rate = learning_rate
        self.epsilon = epsilon	
        self.n_iters = n_iters	
        self.w = None	# 模型参数

    def fit(self, x_train, y_train):
        # 定义损失函数
        def cost(w, x_b, y):
            try:
                return np.sum((y - x_b@w) ** 2) / len(y)
            except:
                return float('inf')

        # 对损失函数求 w和b的导
        def Derivatives(w, x_b, y):
            return (-2 * (y - x_b @ w).T @ x_b / len(y)).reshape(x_b.shape[1], 1)
            
        def gradient_descent(x_b, y, w):
            for i in range(self.n_iters):
                gradient = Derivatives(w, x_b, y)	# 计算梯度
                last_theta = w
                w = w - self.learning_rate * gradient
                if (abs(cost(w, x_b, y) - cost(last_theta, x_b, y)) < self.epsilon):
                    break
            return w

        x_b = np.hstack([x_train, np.ones((len(x_train), 1))])
        self.w = np.zeros((x_b.shape[1], 1))   # 初始化 w
        self.w = gradient_descent(x_b, y_train, self.w)

    def predict(self, x_predict):
        # 给定待预测数据集X_predict,返回表示X_predict的结果向量
        x_b = np.hstack([x_predict, np.ones((len(x_predict), 1))])
        return x_b@self.w

    def score(self, x_test, y_test):
        # 根据测试数据集 x_test 和 y_test 确定当前模型的准确度
        y_predict = self.predict(x_test)
        # 计算y_test和y_predict之间的MSE
        MSE = np.sum((y_test - y_predict) ** 2) / len(y_test)
        # 计算y_test和y_predict之间的R Square
        return 1 - MSE / np.var(y_test)

(如果觉得有用请点个赞吧)

原文地址:https://www.cnblogs.com/ghost222/p/12625649.html