相关的实验代码在我的github上👉QYHcrossover/ML-numpy: 机器学习算法numpy实现 (github.com) 欢迎star⭐
逻辑回归原理
逻辑回归(Logistic Regression)是一种用于分类问题的统计学习方法,它将输入特征与输出标签之间的关系建模为一个概率模型。它常被用于二元分类问题(如判断一封邮件是否为垃圾邮件),但也可以扩展到多元分类问题(如手写数字识别)。
在逻辑回归中,我们假设输出变量 y y y(也称为目标变量)是由输入变量 x x x(也称为特征)和一组参数 θ \theta θ 决定的,其中 θ \theta θ 是我们需要学习的模型参数。我们的目标是根据训练数据集中的 x x x 和 y y y,找到一组最佳参数 θ \theta θ,使得我们的模型能够准确地预测未知的输入样本的输出标签 y y y。
逻辑回归使用的激活函数是sigmoid函数,它将输入 z z z 映射到一个值 0 ≤ h θ ( x ) ≤ 1 0 \leq h_{\theta}(x) \leq 1 0≤hθ(x)≤1,其中 h θ ( x ) h_{\theta}(x) hθ(x) 是我们模型的预测输出,定义为:
h θ ( x ) = 1 1 + e − θ T x h_{\theta}(x) = \frac{1}{1 + e^{-\theta^Tx}} hθ(x)=1+e−θTx1
我们可以将这个输出理解为样本 x x x 属于正例( y = 1 y=1 y=1)的概率。在训练过程中,我们需要最小化损失函数 J ( θ ) J(\theta) J(θ),它是由交叉熵计算得出的,定义为:
J ( θ ) = − 1 m ∑ i = 1 m y ( i ) log h θ ( x ( i ) ) + ( 1 − y ( i ) ) log ( 1 − h θ ( x ( i ) ) ) J(\theta) = -\frac{1}{m} \sum_{i=1}^{m} y^{(i)} \log h_{\theta}(x^{(i)}) + (1-y^{(i)}) \log (1 - h_{\theta}(x^{(i)})) J(θ)=−m1i=1∑my(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i)))
其中 m m m 是训练集中的样本数。在这个损失函数中,我们希望训练出的模型能够最小化错误分类的样本数,并且对于误分类的样本,它能够更加严格地惩罚。
在训练过程中,我们使用梯度下降算法来最小化损失函数。具体来说,我们首先随机初始化模型的参数 θ \theta θ,然后在每一步迭代中,计算损失函数的梯度,并按照梯度的方向更新参数 θ \theta θ,直到损失函数收敛或达到了最大迭代次数。
逻辑回归是一种简单但非常强大的分类算法,具有许多应用。由于其计算效率高、易于实现和解释,逻辑回归在实际工作中被广泛使用。
逻辑回归代码实现
import numpy as np
class LogisticRegression:
def __init__(self,lr=0.001,thr=1e-2,max_epoch=600): #学习率和阈值
self.lr = lr
self.thr = thr
self.max_epoch = max_epoch
self.best_loss = np.inf
self.best_theta = None
self.losses_process = []
首先导入了numpy库,并定义了一个名为LogisticRegression的类。
- __init__方法初始化了类的实例化对象,接受三个可选参数:学习率(lr)、阈值(thr)和最大迭代次数(max_epoch)。
- 这三个参数分别代表着学习率、梯度下降的阈值和最大的迭代次数。
- best_loss、best_theta和losses_process分别代表着最优的损失值、最优的参数和迭代过程中损失值的历史记录。
def _predict(self,X,theta):
sigmoid = lambda x: 1/(1+np.exp(-x)) if x>=0 else np.exp(x)/(1+np.exp(x))
pX = np.ones([X.shape[0],X.shape[1]+1])
pX[:,:-1] = X
preds = [sigmoid(i) for i in pX@theta.ravel()]
return np.array(preds).reshape(-1,1)
_predict方法用于预测数据的标签。
- sigmoid函数为逻辑函数,将输出压缩在0和1之间。
- pX矩阵为在原始特征矩阵X前增加一列全1矩阵。
- theta.ravel()将theta矩阵降维,用于矩阵相乘。
- 最后返回预测值的矩阵。
def predict(self,X):
preds = self._predict(X,self.best_theta).ravel()
preds[preds<=0.5] = 0
preds[preds>0.5] = 1
return preds
predict方法为调用_predict方法的简单包装器,用于预测标签。将预测值矩阵降维并将概率小于等于0.5的预测为0,大于0.5的预测为1,最后返回预测结果。
def score(self,X,y):
return np.sum(self.predict(X)==y) / len(y)
score方法为评估模型预测效果的方法。该方法返回预测正确的样本数除以样本总数。
fit函数是逻辑回归算法中的核心部分,它用于训练模型参数。下面我们来逐行解释fit函数中的代码含义。
def fit(self,X,y):
y = y.reshape(-1,1) # 调整y的维度
pX = np.ones([X.shape[0],X.shape[1]+1]) # 创建矩阵pX,并将其初始化为1
pX[:,:-1] = X # 将X的值赋给pX的前n列,n为X的列数
上述代码中,我们首先将y的维度调整为(n,1),其中n为y的长度,然后创建矩阵pX,并将其初始化为1。矩阵pX的行数为X的行数,列数为X的列数+1。这里要将X的值赋给pX的前n列,n为X的列数,是因为我们需要在X中添加一列常数1,用于表示截距项。
#构造theta
theta = np.random.randn(X.shape[-1]+1).reshape(-1,1) # 创建theta,并将其初始化为随机值
#开始训练
epoch = 0 # 记录训练轮数
while True:
#计算loss
loss = -1/X.shape[0]*np.squeeze(y.T@np.log(self._predict(X,theta)+1e-6) + (1-y).T@np.log(1-self._predict(X,theta)+1e-6)) # 计算交叉熵损失函数
self.losses_process.append(loss) # 记录损失函数值
if loss<self.best_loss: # 如果当前的损失函数值小于历史最佳值,则更新最佳值和对应的参数
self.best_loss = loss
self.best_theta = theta
# print("第{}epoch,loss为{}".format(epoch,loss))
#计算梯度
grad = pX.T@(self._predict(X,theta)-y) # 计算梯度
#是否收敛
if np.sum(np.abs(grad)) < self.thr or epoch>self.max_epoch: break # 判断是否收敛
#梯度下降
theta -= self.lr * grad # 更新参数
epoch += 1 # 更新训练轮数
接下来,我们构造参数theta,并将其初始化为随机值。然后使用while循环进行训练,直到损失函数收敛或达到最大迭代次数。在每一轮训练中,我们首先计算损失函数值,并将其记录在self.losses_process中,然后判断当前的损失函数值是否小于历史最佳值,如果是,则更新历史最佳值和对应的参数。接着,我们计算梯度并判断是否收敛。如果梯度的绝对值之和小于阈值self.thr,则认为模型已经完全收敛了。
最后介绍主函数的主要流程,首先,我们从sklearn.datasets库中加载了乳腺癌数据集,将数据集分别赋值给X和y。然后,我们对X进行标准化处理,接着使用train_test_split函数随机划分数据集为训练集和测试集。
接下来,我们创建了一个逻辑回归对象lr,并将最大迭代次数max_epoch设置为1000。然后,我们使用训练集对逻辑回归模型进行训练,训练完成后我们输出训练集和测试集的分类准确率。最后,我们使用matplotlib库画出了损失函数值随着迭代次数的变化曲线。
if __name__ == "__main__":
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
breast_cancer = load_breast_cancer()
X = breast_cancer.data
y = breast_cancer.target
#数据标准化
X_std = (X-X.mean(axis=0)) / X.std(axis=0)
#划分为训练集和测试集
X_train,X_test,y_train,y_test = train_test_split(X,y)
#逻辑回归训练
lr = LogisticRegression(max_epoch=1000)
lr.fit(X_train,y_train)
print(f"train score {lr.score(X_train,y_train):.2f}")
print(f"test score {lr.score(X_test, y_test):.2f}")
plt.plot(lr.losses_process)
总结
主要介绍了逻辑回归算法的原理和如何用Python的numpy库实现逻辑回归。其中,主要涵盖了逻辑回归算法的原理、sigmoid函数的作用、损失函数和梯度下降算法的应用。代码中的LogisticRegression
类实现了逻辑回归的训练和预测,并用fit
函数来训练模型。最后在本地运行代码并测试模型的准确率。
如果想要深入学习机器学习和numpy,可以关注作者的GitHub账号👉https://github.com/QYHcrossover/ML-numpy。在作者的仓库中,有许多关于机器学习和numpy的实现代码和实例,相信对读者的学习会有很大的帮助。