线性模型试图学习一个通过属性(特征)的线性组合来进行预测的函数,即:
定义x0 = 1,则:
梯度下降算法是:不断重复下面的式子,直到收敛:
这里需要注意的地方是,需要同时对θ1,θ2,…,θn更新:
在应用中,可以使用向量化来批量下降:
因此,梯度下降表示为:
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]])
当代价函数不是一个凸函数时,梯度下降会有两个难以处理的问题:
而使用随机梯度下降,每次只选择一个样本进行迭代,在每次迭代中确定学习速率的函数叫做 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]))
mini-batch GB不像BGD使用全部的样本,或者SGB使用一个样本,MGB计算梯度使用一个小的随机样本。
关于α的选择,如果α过小,收敛速度较慢;如果过大,会引起振荡。
特征缩放:如果特征值的差别过大(一般比例在[-3,3],[-1/3,1/3]之间),就需要进行特诊缩放。采用特征缩放和均值归一化将很有帮助。其中μi是输入特征的均值,si是最大值减去最小值,或者是标准偏差。
当为满秩矩阵或者正定矩阵时:
关于正规方程需要知道:
这个术语适用于线性和逻辑回归。解决过度拟合的问题有两个主要的选择:
1)减少特性的数量:
2)正则化:
对于线性回归,使用L2正则化时:
对应的梯度下降为:
对应正规方程为:
《机器学习》一书中称作对数几率回归。
对于二分类问题,即y ∈ {0, 1},可以使用”Sigmoid Function”,也可以称为逻辑回归。
决策边界是把y=0和y=1分开的直线。它是由我们的假设函数产生的。决策边界不一定是一条直线,可以是其他的任何形状。
注意,这里的代价函数,如果使用和线性回归一样的函数,即
而,其中的假设函数为:
这样就会导致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视为类后验概率,则可通过“极大似然法”来估计θ:
最大化上式等价于最小化代价函数:
如果,分类不是 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为:
加入L2正则的代价函数为:
则对应的梯度下降方法为:
多项式回归方程指的是独立变量的指数大于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!)个特征(包含常数项)。
关于逐步回归,可以参考这篇文章,逐步回归分析
为了防止过拟合,我们需要减小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]])
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了。
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])
这部分在吴恩达老师和 Aurélien Géron 的讲解中,惊人的一致,应该在业界是一种主流的方法。
对于一个回归问题,如果我们使用多项式回归,应该怎样设置degree呢?
可以从Training set 和 Validation set 的均方根误差曲线来进行判别是高偏差(high bias,一般是欠拟合)问题,还是高方差(high variance,一般是过拟合)问题。
从下图可以看出多项式的degree和欠拟合,过拟合之间的关系。
训练集误差会随着degree的增加而减少,而验证集的误差会有一个最合适的degree。
绘制训练集和验证集的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)
上图的曲线是书中的截图,可以看出两个曲线之间有很明显的间隙,这是过拟合的标志,如果使用更多的样本,将会使两个曲线更加接近。
我得到的曲线是如下图所示,分析原因应该是划分的训练集和样本集的不同导致。同时,下图的比例多大,不易区分。
为什么正则化能够降低过拟合
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