2 线性模型

1.Linear Regression

1.1 hypothesis function

线性模型试图学习一个通过属性(特征)的线性组合来进行预测的函数,即:

定义x0 = 1,则:

1.2 cost function

1.3 gradient descent

梯度下降算法是:不断重复下面的式子,直到收敛:

这里需要注意的地方是,需要同时对θ1,θ2,…,θn更新:

1.3.1 batch gradient descent

在应用中,可以使用向量化来批量下降:

因此,梯度下降表示为:

eta = 0.1 # learning rate
n_iterations = 1000
m = 100
theta = np.random.randn(2,1) # random initialization

for iteration in range(n_iterations):
    gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
    theta = theta - eta * gradients

其中,X_b 为在第一列加上偏置项1。

使用 sklearn.linear_model 处理为:

import numpy as np
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

>>> from sklearn.linear_model import LinearRegression
>>> lin_reg = LinearRegression()
>>> lin_reg.fit(X, y)
>>> lin_reg.intercept_, lin_reg.coef_
(array([ 4.21509616]), array([[ 2.77011339]]))
>>> lin_reg.predict(X_new)
array([[ 4.21509616],
[ 9.75532293]])

1.3.2 stochastic gradient descent

当代价函数不是一个凸函数时,梯度下降会有两个难以处理的问题:

  • 如果随机初始化在左边启动,那么它就会收敛到一个局部最小值,而不是一个全局最优解。
  • 如果它从右边开始,那么迭代需要很长时间,如果过早地停止,将永远无法到达最小值。

而使用随机梯度下降,每次只选择一个样本进行迭代,在每次迭代中确定学习速率的函数叫做 learning schedule。

代码简单表示为:

n_epochs = 50
t0, t1 = 5, 50 # learning schedule hyperparameters
def learning_schedule(t):
return t0 / (t + t1)
theta = np.random.randn(2,1) # random initialization

for epoch in range(n_epochs):
    for i in range(m):
        random_index = np.random.randint(m)
        xi = X_b[random_index:random_index+1]
        yi = y[random_index:random_index+1]
        gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
        eta = learning_schedule(epoch * m + i)
        theta = theta - eta * gradients

使用 sklearn.linear_model 表示为:

from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(n_iter=50, penalty=None, eta0=0.1)
sgd_reg.fit(X, y.ravel())

>>> sgd_reg.intercept_, sgd_reg.coef_
(array([ 4.18380366]), array([ 2.74205299]))

1.3.3 mini-batch gradient descent

mini-batch GB不像BGD使用全部的样本,或者SGB使用一个样本,MGB计算梯度使用一个小的随机样本。

1.3.4 三点需要说明:

  • 虽然梯度下降一般都是收敛到局部极小值,但是这里的线性回归的优化问题只有一个全局最小值,没有其他局部最优值。原因在于J是一个凸二次函数
  • 关于α的选择,如果α过小,收敛速度较慢;如果过大,会引起振荡。

  • 特征缩放:如果特征值的差别过大(一般比例在[-3,3],[-1/3,1/3]之间),就需要进行特诊缩放。采用特征缩放和均值归一化将很有帮助。其中μi是输入特征的均值,si是最大值减去最小值,或者是标准偏差。

1.4 normal equation

当为满秩矩阵或者正定矩阵时:

关于正规方程需要知道:

  • 其中X是一个(m,n+1)的矩阵,第一列全为1。即有m个样本,n个特征。
  • 如果不可逆,一般有以下情况:①冗余特征:其中有两个及以上的特征是紧密相关的。②太多的特征(e.g. m≤n):删除一些特征,或者使用正则项。

1.5 gradient descent VS normal equation

1.6 solving the problem of overfitting

这个术语适用于线性和逻辑回归。解决过度拟合的问题有两个主要的选择:

1)减少特性的数量:

  • 手动选择要保留的特性。
  • 使用一个模型选择算法。

2)正则化:

  • 保持所有的特性,但是减少参数θj的大小。
  • 当有很多有用的特性时,正规化就会很好。

对于线性回归,使用L2正则化时:

对应的梯度下降为:

对应正规方程为:

1.7 个人理解

  • 线性回归,如果属于高方差,需要加正则项,则应该加L1正则。L1正则化可以产生稀疏权值矩阵,可以用于特征选择。关于L1、L2正则,可以参考机器学习中正则化项L1和L2的直观理解。
  • 对于线性回归模型,使用L1正则化的模型建叫做Lasso回归,使用L2正则化的模型叫做Ridge回归(岭回归)。关于Lasso回归,Ridge回归,下面有详细说明。

2.Logistic Regression

《机器学习》一书中称作对数几率回归。

2.1 hypothesis representation

对于二分类问题,即y ∈ {0, 1},可以使用”Sigmoid Function”,也可以称为逻辑回归。

2.2 decision boundary

决策边界是把y=0和y=1分开的直线。它是由我们的假设函数产生的。决策边界不一定是一条直线,可以是其他的任何形状。

2.3 cost function

注意,这里的代价函数,如果使用和线性回归一样的函数,即

而,其中的假设函数为:

这样就会导致J(θ)是一个非凸函数,有许多局部最小值。我们需要得到一个凸函数,便于找到全局最优解,因此需要重新定义一个代价函数。

逻辑回归的代价函数定义如下:

由定义可以得到:

Cost(hθ(x),y)=0 if hθ(x)=y    
Cost(hθ(x),y)→∞ if y=0 and hθ(x)→1    
Cost(hθ(x),y)→∞ if y=1 and hθ(x)→0

直观理解为:当y=1时,绘制J(θ) vs hθ(x)见左图,当y=0时,绘制J(θ) vs hθ(x)见左图:

上式的cost function 联合起来表示为:

因为,总的cost function 就是:

向量化的表示为:

逻辑代价函数的偏导数为:

在sklearn中使用方式为:

from sklearn import datasets
iris = datasets.load_iris()
list(iris.keys())
['data', 'target', 'target_names', 'DESCR', 'feature_names']

X = iris["data"][:, 3:] # petal width
y = (iris["target"] == 2).astype(np.int) # 1 if Iris-Virginica, else 0

from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X, y)

%matplotlib inline
X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)
plt.plot(X_new, y_proba[:, 1], "g-", label="Iris-Virginica")
plt.plot(X_new, y_proba[:, 0], "b--", label="Not Iris-Virginica")

扩展阅读:

实质上,逻辑回归的代价函数是由极大似然法而来,将式中的y视为类后验概率,则可通过“极大似然法”来估计θ:

最大化上式等价于最小化代价函数:

2.4 multiclass classification(Softmax Regression): One-vs-all

如果,分类不是 y = {0,1},而是一个多分类问题,即 y = {0,1…n}。从前面的二分类可以理解到,实质上,预测y=0(即hθ(x) < 0.5),或者y=1(即hθ(x) ≥ 0.5),因此,对于多分类,需要求解每个类别的概率,预测值即为概率最大的类别:

基本方法是先选择了一个类,然后将所有其他的类都归并到一个单独的类中。我们反复地做这个,将二元逻辑回归应用到每一个案例中,然后使用返回最高值作为我们的预测的假设。

Softmax function为:

Softmax Regression classifer prediction为:

在sklearn中使用为:

X = iris["data"][:, (2, 3)] # petal length, petal width
y = iris["target"]

softmax_reg = LogisticRegression(multi_class="multinomial",solver="lbfgs", C=10)
softmax_reg.fit(X, y)

softmax_reg.predict([[5, 2]])
array([2])
softmax_reg.predict_proba([[5, 2]])
array([[  6.33134077e-07,   5.75276067e-02,   9.42471760e-01]])

Softmax Regression decision boundaries为:

2.5 solving the problem of overfitting

加入L2正则的代价函数为:

则对应的梯度下降方法为:

3.Polynomial Regression

多项式回归方程指的是独立变量的指数大于1。使用多项式回归时,特别需要注意过拟合问题。

在sklearn库中使用例子:

import numpy as np
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)

from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)

from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)

lin_reg.intercept_
array([ 1.83949595])
lin_reg.coef_
array([[ 1.00849387,  0.49666808]])

从上面的例子可以看出,模型训练的是y = 0.56 x1^2 + 0.93 x1 + 1.78,而实际的函数为 y = 0.5 x1^2 + 1.0 x1 + 2.0 + Gaussian noise.

在用多项式回归时,根据设置的degree,特征将自由组合,例如有特征a和b两个特征,设置degree=3,则组合为(1,a,b,a^2,b^2,ab,a^2b,ab^2,a^3,b^3)共计10个特征。

对于有n个特征(不包含常数项),degree = d的模型,共计有(n+d)!/(d!n!)个特征(包含常数项)。

4.Stepwise Regression

关于逐步回归,可以参考这篇文章,逐步回归分析

5.Ridge Regression

为了防止过拟合,我们需要减小degree,后者加上正则项。Ridge Regression, Lasso Regression, and Elastic Net,实质上是3种不同的惩罚权重的方法。其中Ridge Regression就是加上L2正则,Lasso Regression就是加上L1正则,Elastic Net是加上L1+L2。

岭回归就是在原来的代价函数基础上加上,这样迫使模型不仅需要尽可能的拟合数据,而且会使特征的权重尽可能的小。

注意:正则项是添加在训练函数的代价函数中,模型训练完成后,如果去评估模型,需要使用没有正则项的评估方法。

在使用岭回归之前,对输入特征的比例进行缩放是很重要的。

在sklearn中使用:

import numpy as np
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)

>>> from sklearn.linear_model import Ridge
>>> ridge_reg = Ridge(alpha=1, solver="cholesky")
>>> ridge_reg.fit(X, y)
>>> ridge_reg.predict([[1.5]])
array([[ 1.55071465]])

使用Stochastic Gradient Descent的方法:

>>> sgd_reg = SGDRegressor(penalty="l2")
>>> sgd_reg.fit(X, y.ravel())
>>> sgd_reg.predict([[1.5]])
array([[ 1.13500145]])

6.Lasso Regression

Least Absolute Shrinkage and Selection Operator Regression(Lasso Regression):是在原来的代价函数上加上L1正则项。

Lasso Regression的代价函数为:

Lasso回归的一个重要特征是它趋向于完全消除最不重要特征的权重。

Lasso代价函数在θi = 0(i=1,2,3,…,n)处不可导,可用 subgradient vector g代替θi = 0,这样梯度下降就可以使用。

在sklearn中使用:

import numpy as np
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)

from sklearn.linear_model import Lasso
lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X, y)
lasso_reg.predict([[1.5]])
array([ 4.76514067])

使用Stochastic Gradient Descent的方法:

from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(penalty="l1")
sgd_reg.fit(X, y.ravel())
sgd_reg.predict([[1.5]])
array([ 4.03888457])

使用Lasso回归,degree =10 对上式进行绘图,发现当α = 1e-07时,曲线表现像一个二次曲线,即将其他的高次特征都惩罚为0了。

7.ElasticNet Regression

Elastic Net是将Ridge回归于Lasso回归的组合,Elastic Net代价函数为:

在sklearn中的例子为:

from sklearn.linear_model import ElasticNet
elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X, y)
elastic_net.predict([[1.5]])
array([ 4.76792495])

8.Learning Curves

这部分在吴恩达老师和 Aurélien Géron 的讲解中,惊人的一致,应该在业界是一种主流的方法。

对于一个回归问题,如果我们使用多项式回归,应该怎样设置degree呢?

可以从Training set 和 Validation set 的均方根误差曲线来进行判别是高偏差(high bias,一般是欠拟合)问题,还是高方差(high variance,一般是过拟合)问题。

  • 使用交叉验证来评估一个模型的泛化能力。如果一个模型和训练集拟合的很好,但是和验证集拟合的差,则这个模型存在过拟合问题;如果模型在训练集和验证集都表现很差,则模型存在欠拟合问题。
  • 另一种方式是通过训练集和验证的训练曲线来进行判别。

8.1 Diagnosing Bias vs. Variance

从下图可以看出多项式的degree和欠拟合,过拟合之间的关系。

训练集误差会随着degree的增加而减少,而验证集的误差会有一个最合适的degree。

8.2 RMSE curves

绘制训练集和验证集的RMSE来进行判断:

import numpy as np
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)

import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
def plot_learning_curves(model, X, y):
    X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)
    train_errors, val_errors = [], []
    for m in range(1, len(X_train)):
        model.fit(X_train[:m], y_train[:m])
        y_train_predict = model.predict(X_train[:m])
        y_val_predict = model.predict(X_val)
        train_errors.append(mean_squared_error(y_train_predict, y_train[:m]))
        val_errors.append(mean_squared_error(y_val_predict, y_val))
    plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
    plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")

使用线性回归模型:

lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)

这是一个典型的欠拟合的曲线例子,两者的曲线已近接近水平,而且非常接近,且值较大。

如果是一个欠拟合的模型,添加再多的样本也没有用,这时,需要使用一个更复杂的模型去训练。

使用一个degree=10的多项式去训练。

from sklearn.pipeline import Pipeline
polynomial_regression = Pipeline((
    ("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
    ("sgd_reg", LinearRegression()),
))
plot_learning_curves(polynomial_regression, X, y)

上图的曲线是书中的截图,可以看出两个曲线之间有很明显的间隙,这是过拟合的标志,如果使用更多的样本,将会使两个曲线更加接近。

我得到的曲线是如下图所示,分析原因应该是划分的训练集和样本集的不同导致。同时,下图的比例多大,不易区分。

8.3 Deciding What to Do Next Revisited

  • 增加训练样本:适合高方差。
  • 尝试减少特征:适合高方差问题。
  • 增加特征:适合高偏差问题。
  • 增加多项式特征:适合高偏差问题。
  • 减小正则项系数lambda:适合高偏差问题。
  • 增大正则项系数lambda:适合高方差问题。

9.附录

为什么正则化能够降低过拟合

30 Questions to test a data scientist on Linear Regression [Solution: Skilltest – Linear Regression]

参考文献:

[1] 机器学习- Andrew Ng

[2] 机器学习-周志华

[3] 7 Types of Regression Techniques you should know!

[4] Hands-On Machine Learning with Scikit-Learn and TensorFlow

你可能感兴趣的:(机器学习)