数学推导+纯Python实现机器学习算法:Lasso回归

数学推导+纯Python实现机器学习算法:Lasso回归
本节我们要介绍的是基于L1正则化的Lasso模型,在正式介绍模型之前,笔者还是想带⼤家复习⼀下过拟合和正则化等机器学习关键问题。
正则化与L1范数
浙江实用医学正则化是防⽌模型过拟合的核⼼技术之⼀,关于⽋拟合和过拟合的问题,这⾥笔者就不再展开来说,不了解的朋友可以看看笔者很早之前写的⼀篇⽂章:。
总的来说,监督机器学习的核⼼原理莫过于如下公式:
该公式可谓是机器学习中最核⼼最关键最能概述监督学习的核⼼思想的公式了:所有的有监督机器学习,⽆⾮就是正则化参数的同时最⼩化经验误差函数。最⼩化经验误差是为了极⼤程度的拟合训练数据,正则化参数是为了防⽌过分的拟合训练数据。你看,多么简约数学哲学。正如之前所说,监督机器
学习是为了让我们建⽴的模型能够发现数据中普遍的⼀般的规律,这个普遍的⼀般的规律⽆论对于训练集还是未知的测试集,都具有较好的拟合性能。
继续回到公式。第⼀项经验误差函数在机器学习中⽆疑地位重要,但它不是笔者今天要讲的,今天要讲的是公式的第⼆项:正则化项。第⼆项中 λ 为正则化系数,通常是⼤于 0 的,是⼀种调整经验误差项和正则化项之间关系的系数。λ = 0 时相当于该公式没有正则化项,模型全⼒讨好第⼀项,将经验误差进⾏最⼩化,往往这也是最容易发⽣过拟合的时候。随着 λ 逐渐增⼤,正则化项在模型选择中的话语权越来越⾼,对模型的复杂性的惩罚也越来越厉害。所以,在实际的训练过程中,λ 作为⼀种超参数很⼤程度上决定了模型⽣死。
系数 λ 说完了,然后就是正则化项,正则化项形式有很多,但常见的也就是 L1 和 L2 正则化。本节我们先来看L1。
在说常见的 L1 和 L2 之前,先来看⼀下 L0 正则化。L0 正则化也就是 L0 范数,即矩阵中所有⾮ 0 元素的个数。如何我们在正则化过程中选择了 L0 范数,那该如何理解这个 L0 呢?其实⾮常简单,L0 范数就是希望要正则化的参数矩阵 W ⼤多数元素都为 0。如此简单粗暴,让参数矩阵 W ⼤多数元素为 0 就是实现稀疏⽽已。说到这⾥,权且打住,想必同样在机器学习领域摸爬滚打的你⼀定想问,据我所知稀疏性不通常都是⽤ L1 来实现的吗?这⾥个中缘由笔者不去细讲了,简单说结论:在机器学
习领域,L0 和 L1 都可以实现矩阵的稀疏性,但在实践中,L1 要⽐ L0 具备更好的泛化求解特性⽽⼴受青睐。先说了 L1,但还没解释 L1 范数是什么,L1 范数就是矩阵中各元素绝对值之和,正如前述所⾔,L1 范数通常⽤于实现参数矩阵的稀疏性。⾄于为啥要稀疏,稀疏有什么⽤,通常是为了特征选择和易于解释⽅⾯的考虑。
Lasso
Lasso的全称叫做Least absolute shrinkage and selection operator,直译过来为最⼩收缩与选择算⼦。其本质就是在常规的线性回归的基础上对参数加了⼀个L1正则化约束。其形式如下所⽰:
规约到线性回归模型上,上式的第⼀项就是MSE损失,第⼆项则是L1正则化项。我们同样按照之前线性回归的打法来对其进⾏实现,只是需要注意⼀下L1正则化项的求导处理。我们来看具体的实现代码。
导⼊相关package并读⼊⽰例数据:
import numpy as np
import pandas as pd
data = np.genfromtxt('mystery.dat', delimiter = ',')
# 选择特征与标签
x = data[:,0:100]
y = data[:,100].reshape(-1,1)
# 加⼀列
X = np.column_stack((np.ones((x.shape[0],1)),x))
# 划分训练集与测试集
X_train, y_train = X[:70], y[:70]南宁pm2.5
X_test, y_test = X[70:], y[70:]
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
定义参数初始化函数:
# 定义参数初始化函数
def initialize(dims):
w = np.zeros((dims, 1))
b = 0
return w, b
定义符号函数并进⾏向量化,⽤于对L1正则化项的梯度计算:
# 定义符号函数
def sign(x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
# 利⽤numpy对符号函数进⾏向量化
vec_sign = np.vectorize(sign)
vec_s((3,1)))
在MSE损失函数的基础上定义Lasso损失:
# 定义lasso损失函数
def l1_loss(X, y, w, b, alpha):
num_train = X.shape[0]
num_feature = X.shape[1]
y_hat = np.dot(X, w) + b
loss = np.sum((y_hat-y)**2)/num_train + np.sum(alpha*abs(w))
dw = np.dot(X.T, (y_hat-y)) /num_train + alpha * vec_sign(w)
db = np.sum((y_hat-y)) /num_train
return y_hat, loss, dw, db
定义Lasso训练过程函数:
# 定义训练过程
def lasso_train(X, y, learning_rate=0.01, epochs=300):
loss_list = []
w, b = initialize(X.shape[1])
for i in range(1, epochs):
y_hat, loss, dw, db = l1_loss(X, y, w, b, 0.1)
w += -learning_rate * dw
b += -learning_rate * db
loss_list.append(loss)
if i % 50 == 0:
print('epoch %d loss %f' % (i, loss))
params = {
'w': w,
'b': b
}
轻武器传奇grads = {
'dw': dw,
'db': db
}
return loss, loss_list, params, grads
执⾏训练:
# 执⾏训练⽰例
loss, loss_list, params, grads = lasso_train(X_train, y_train, 0.01, 500)
可以看到,在L1的约束下,在训练过程中有不少对标签贡献率低的特征的系数都变成了0。这就是L1的作⽤,⼀定程度上可以进⾏特征选择和实现稀疏化。
最后可以简单写⼀个Lasso回归的class来对上述过程进⾏封装:
import numpy as np
ics import r2_score
class Lasso():
def __init__(self):
pass
def prepare_data(self):
data = np.genfromtxt('./example.dat', delimiter = ',')
x = data[:, 0:100]
y = data[:, 100].reshape(-1, 1)
X = np.column_stack((np.ones((x.shape[0], 1)), x))
X_train, y_train = X[:70], y[:70]
X_test, y_test = X[70:], y[70:]
return X_train, y_train, X_test, y_test
def initialize_params(self, dims):
w = np.zeros((dims, 1))
b = 0
return w, b
def sign(self, x):
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def l1_loss(self, X, y, w, b, alpha):
num_train = X.shape[0]
num_train = X.shape[0]
num_feature = X.shape[1]
y_hat = np.dot(X, w) + b
loss = np.sum((y_hat - y) ** 2) / num_train + np.sum(alpha*abs(w))
dw = np.dot(X.T, (y_hat - y)) / num_train + alpha*np.vectorize(self.sign)(w)
db = np.sum((y_hat - y)) / num_train
return y_hat, loss, dw, db
def lasso_train(self, X, y, learning_rate, epochs):
loss_list = []
w, b = self.initialize_params(X.shape[1])
for i in range(1, epochs):
y_hat, loss, dw, db = self.l1_loss(X, y, w, b, 0.1)
w += -learning_rate * dw
b += -learning_rate * db
loss_list.append(loss)
if i % 300 == 0:
print('epoch %d loss %f' % (i, loss))
params = {
'w': w,
'b': b诸病源候论
}
grads = {
'dw': dw,中国电信我的e家
'db': db
氟化钙}
return loss, loss_list, params, grads
def predict(self, X, params):
w = params['w']
b = params['b']
y_pred = np.dot(X, w) + b
return y_pred
if __name__ == '__main__':
lasso = Lasso()
X_train, y_train, X_test, y_test = lasso.prepare_data()
loss, loss_list, params, grads = lasso.lasso_train(X_train, y_train, 0.01, 3000)
print(params)
y_pred = lasso.predict(X_test, params)
print(r2_score(y_test, y_pred))
以上是基于numpy的⼿动实现Lasso的过程,下⾯再来看Lasso在sklearn中的实现。
# 导⼊线性模型模块
from sklearn import linear_model
# 创建lasso模型实例
sk_lasso = linear_model.Lasso(alpha=0.1)
# 对训练集进⾏拟合
sk_lasso.fit(X_train, y_train)
# 打印模型相关系数
print("sklearn Lasso intercept :", sk_lasso.intercept_)
print("\nsklearn Lasso coefficients :\n", f_)
print("\nsklearn Lasso number of iterations :", sk_lasso.n_iter_)

本文发布于:2024-09-22 13:32:45,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/53824.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:正则   模型   化项   学习   训练   拟合
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议