逻辑回归原理及代码实现

逻辑回归原理

逻辑回归主要用于解决二分类问题,给定一个输入样本 x x x,输出该样本属于1对应类别的预测概率 y ^ = P ( y = 1 ∣ x ) \hat{y} = P(y=1|x) y^=P(y=1x)
与线性回归相比,逻辑回归增加了非线性函数,如Sigmoid函数,使得输出值在[0,1]的区间中,并设定阈值进行分类。

逻辑回归的主要参数

  1. 输入特征向量: x ∈ R n x \in R^n xRn(表示输入样本具有 n n n个特征值), y ∈ 0 , 1 y \in {0,1} y0,1(表示样本的标签)
  2. 逻辑回归的权重与偏置: w ∈ R n w \in R^n wRn, b ∈ R b \in R bR
  3. 输出的预测结果: y ^ = σ ( w T x + b ) = σ ( w 1 x 1 + w 2 x 2 + . . . + w n x n + b ) \hat{y} = \sigma(w^Tx+b)=\sigma(w_1x_1+w_2x_2+...+w_nx_n+b) y^=σ(wTx+b)=σ(w1x1+w2x2+...+wnxn+b)
    ( σ \sigma σ一般为Sigmoid函数
    在这里插入图片描述

逻辑回归的流程

  1. 准备好训练数据 x ∈ R n × m x \in R^{n \times m} xRn×m( m m m表示样本个数, n n n表示每个样本的特征值),训练标签数据 y ∈ R m y \in R^m yRm
  2. 初始化权重与偏置 w ∈ R n w \in R^n wRn, b ∈ R b \in R bR
  3. 数据前向传播 y ^ = σ ( w T x + b ) \hat{y} = \sigma(w^Tx+b) y^=σ(wTx+b),通过极大似然损失函数计算损失
  4. 采用梯度下降法更新权重 w w w和偏置 b b b,最小化损失函数
  5. 采用预测得到的权重与偏置进行数据前向传播,设定分类阈值,实现二分类任务

逻辑回归的损失函数

线性回归中采用平方误差作为损失函数,而逻辑回归中一般采用极大似然损失函数衡量预测结果与真实值之间的误差。
对于单个样本的损失值计算公式如下:
L ( y ^ , y ) = − y log ⁡ y ^ − ( 1 − y ) log ⁡ ( 1 − y ^ ) L(\hat{y},y)=-y\log\hat{y}-(1-y)\log(1-\hat{y}) L(y^,y)=ylogy^(1y)log(1y^)

  • 若样本属于标签1,则 L ( y ^ , y ) = − log ⁡ y ^ L(\hat{y},y)=-\log\hat{y} L(y^,y)=logy^,预测值越接近1, L ( y ^ , y ) L(\hat{y},y) L(y^,y)的值越小
  • 若样本属于标签0,则 L ( y ^ , y ) = − log ⁡ ( 1 − y ^ ) L(\hat{y},y)=-\log(1-\hat{y}) L(y^,y)=log(1y^),预测值越接近0, L ( y ^ , y ) L(\hat{y},y) L(y^,y)的值越小

对于全体训练样本的损失值计算公式如下:
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) J(w,b)=\frac{1}{m}\sum^m_{i=1}L(\hat{y}^{(i)},y^{(i)}) J(w,b)=m1i=1mL(y^(i),y(i))

梯度下降算法

梯度下降法的目的是最小化损失函数,函数的梯度指出了函数变化最快的方向。

在这里插入图片描述

如上图所示,假设 J ( w , b ) J(w,b) J(w,b)是关于 w w w b b b的函数, w , b ∈ R w,b \in R w,bR
计算初始点处的梯度,设置好学习率,对权重与偏置参数进行更新迭代,最终可以到达函数值最小点。
权重与偏置的更新公式为:
w = w − α ∂ J ( w , b ) ∂ w w=w-\alpha\frac{\partial{J(w,b)}}{\partial{w}} w=wαwJ(w,b)

b = b − α ∂ J ( w , b ) ∂ b b=b-\alpha\frac{\partial{J(w,b)}}{\partial{b}} b=bαbJ(w,b)

注:其中 α \alpha α为学习率,即每次更新 w , b w,b w,b的步长

基于链式法则的梯度计算

在梯度下降法中介绍了参数更新的公式,可以看到参数更新涉及梯度的计算,本小节将详细介绍梯度计算的流程。
简单起见,作以下假设:

  • 输入数据 x ∈ R 3 × 1 x \in R^{3 \times 1} xR3×1(1个样本,3个特征值)
  • 标签 y ∈ R y \in R^{} yR
  • 权重 w ∈ R 3 × 1 w \in R^{3 \times1} wR3×1
  • 偏置 b ∈ R b \in R bR

为方便理解,采用流程图对逻辑回归的数据流动进行表示:

在这里插入图片描述

1. 计算 J J J关于 z z z的导数

  • ∂ J ∂ z = ∂ J ∂ y ^ ∂ y ^ ∂ z \frac{\partial{J}}{\partial{z}}=\frac{\partial{J}}{\partial{\hat{y}}}\frac{\partial{\hat{y}}}{\partial{z}} zJ=y^Jzy^
  • ∂ J ∂ y ^ = − y y ^ + 1 − y 1 − y ^ \frac{\partial{J}}{\partial{\hat{y}}}=\frac{-y}{\hat{y}}+\frac{1-y}{1-\hat{y}} y^J=y^y+1y^1y \quad \quad ∂ y ^ ∂ z = y ^ ( 1 − y ^ ) \frac{\partial{\hat{y}}}{\partial{z}}=\hat{y}(1-\hat{y}) zy^=y^(1y^)
  • ∂ J ∂ z = ∂ J ∂ y ^ ∂ y ^ ∂ z = − y ( 1 − y ^ ) + ( 1 − y ) y ^ = y ^ − y \frac{\partial{J}}{\partial{z}}=\frac{\partial{J}}{\partial{\hat{y}}}\frac{\partial{\hat{y}}}{\partial{z}}=-y(1-\hat{y})+(1-y)\hat{y}=\hat{y}-y zJ=y^Jzy^=y(1y^)+(1y)y^=y^y

2. 计算 z z z关于 w w w b b b的导数

  • ∂ z ∂ w 1 = x 1 \frac{\partial{z}}{\partial{w_1}}=x1 w1z=x1 \quad \quad ∂ z ∂ w 2 = x 2 \frac{\partial{z}}{\partial{w_2}}=x2 w2z=x2 \quad \quad ∂ z ∂ w 3 = x 3 \frac{\partial{z}}{\partial{w_3}}=x3 w3z=x3
  • ∂ z ∂ b = 1 \frac{\partial{z}}{\partial{b}}=1 bz=1

3. 计算 J J J关于 w w w b b b的导数

  • ∂ J ∂ w 1 = ∂ J ∂ z ∂ z ∂ w 1 = ( y ^ − y ) x 1 \frac{\partial{J}}{\partial{w_1}}=\frac{\partial{J}}{\partial{z}}\frac{\partial{z}}{\partial{w_1}}=(\hat{y}-y)x_1 w1J=zJw1z=(y^y)x1
  • ∂ J ∂ w 2 = ∂ J ∂ z ∂ z ∂ w 2 = ( y ^ − y ) x 2 \frac{\partial{J}}{\partial{w_2}}=\frac{\partial{J}}{\partial{z}}\frac{\partial{z}}{\partial{w_2}}=(\hat{y}-y)x_2 w2J=zJw2z=(y^y)x2
  • ∂ J ∂ w 3 = ∂ J ∂ z ∂ z ∂ w 3 = ( y ^ − y ) x 3 \frac{\partial{J}}{\partial{w_3}}=\frac{\partial{J}}{\partial{z}}\frac{\partial{z}}{\partial{w_3}}=(\hat{y}-y)x_3 w3J=zJw3z=(y^y)x3
  • ∂ J ∂ b = ∂ J ∂ z ∂ z ∂ b = ( y ^ − y ) \frac{\partial{J}}{\partial{b}}=\frac{\partial{J}}{\partial{z}}\frac{\partial{z}}{\partial{b}}=(\hat{y}-y) bJ=zJbz=(y^y)

向量化实现梯度计算

在上一节中,针对单个数据样本,介绍了如何计算梯度。但是实际过程中,数据样本不可能只有一个,因此要计算基于全体数据样本损失函数的梯度。本节将采用向量化的方式实现多数据样本的梯度计算,向量化的方式相对于采用for循环的方式,可以节省大量的时间,提高运算的效率。
首先声明下数据的结构:

  • 输入数据 X ∈ R n × m X \in R^{n \times m} XRn×m(m个样本,n个特征值)
  • 标签 Y ∈ R m × 1 Y \in R^{m \times 1} YRm×1
  • 权重 W ∈ R n × 1 W \in R^{n \times1} WRn×1
  • 偏置 b ∈ R b \in R bR

多样本向量化梯度下降的过程如下:

  1. Z = W T X + b Z=W^TX+b Z=WTX+b \quad Z ∈ R 1 × m Z\in R^{1\times m} ZR1×m
  2. Y ^ = σ ( Z ) \hat{Y}=\sigma(Z) Y^=σ(Z) \quad Y ^ ∈ R 1 × m \hat{Y}\in R^{1\times m} Y^R1×m
  3. ∂ J ∂ z = Y ^ − Y \frac{\partial{J}}{\partial{z}}=\hat{Y}-Y zJ=Y^Y \quad ∂ J ∂ z ∈ R 1 × m \frac{\partial{J}}{\partial{z}}\in R^{1\times m} zJR1×m
  4. ∂ J ∂ W = 1 m X ( Y ^ − Y ) T \frac{\partial{J}}{\partial{W}}=\frac{1}{m}X(\hat{Y}-Y)^T WJ=m1X(Y^Y)T \quad ∂ J ∂ W ∈ R n × 1 \frac{\partial{J}}{\partial{W}}\in R^{n\times 1} WJRn×1
  5. ∂ J ∂ b = 1 m n p . s u m ( Y ^ − Y ) \frac{\partial{J}}{\partial{b}}=\frac{1}{m}np.sum(\hat{Y}-Y) bJ=m1np.sum(Y^Y) \quad ∂ J ∂ b ∈ R \frac{\partial{J}}{\partial{b}}\in R bJR
  6. W = W − α ∂ J ∂ W W = W-\alpha \frac{\partial{J}}{\partial{W}} W=WαWJ \quad b = b − α ∂ J ∂ b b= b-\alpha \frac{\partial{J}}{\partial{b}} b=bαbJ

逻辑回归代码实现

获取二分类数据

from sklearn.datasets import load_iris,make_classification
from sklearn.model_selection import train_test_split
import tensorflow as tf
import numpy as np

# 生成500个样本点,样本类别只有2种,每个样本的特征值有4个 
X,Y=make_classification(n_samples=500,n_features=4,n_classes=2)

# 取30%的数据作为测试集,70%数据作为训练集
x_train,x_test,y_train,y_test = train_test_split(X,Y,test_size=0.3)
print("X:",X.shape)
print("Y:",Y.shape)
print("x_train:",x_train.shape)
print("x_test:",x_test.shape)
print("y_train:",y_train.shape)
print("y_test:",y_test.shape)

输出结果为:

X: (500, 4)
Y: (500,)
x_train: (350, 4)
x_test: (150, 4)
y_train: (350,)
y_test: (150,)

定义初始化模块

def initialize_with_zeros(shape):
    """
    创建一个形状为 (shape, 1) 的w参数和b=0.
    return:w, b
    """
    w = np.zeros((shape, 1))
    b = 0
    return w, b

定义损失函数及梯度

def basic_sigmoid(x):
    """
    计算sigmoid函数
    """
    s = 1 / (1 + np.exp(-x))
    return s

def propagate(w, b, X, Y):
    """
    参数:w,b,X,Y:网络参数和数据
    Return:
    损失cost、参数W的梯度dw、参数b的梯度db
    """
    m = X.shape[1]

    # w (n,1), x (n, m)
    A = basic_sigmoid(np.dot(w.T, X) + b)
    # 计算损失
    cost = -1 / m * np.sum(Y * np.log(A) + (1 - Y) * np.log(1 - A))
    dz = A - Y
    dw = 1 / m * np.dot(X, dz.T)
    db = 1 / m * np.sum(dz)

    cost = np.squeeze(cost)

    grads = {"dw": dw,
             "db": db}
    return grads, cost

定义梯度下降算法

def optimize(w, b, X, Y, num_iterations, learning_rate):
    """
    参数:
    w:权重,b:偏置,X特征,Y目标值,num_iterations总迭代次数,learning_rate学习率
    Returns:
    params:更新后的参数字典
    grads:梯度
    costs:损失结果
    """

    costs = []

    for i in range(num_iterations):

        # 梯度更新计算函数
        grads, cost = propagate(w, b, X, Y)

        # 取出两个部分参数的梯度
        dw = grads['dw']
        db = grads['db']

        # 按照梯度下降公式去计算
        w = w - learning_rate * dw
        b = b - learning_rate * db

        if i % 100 == 0:
            costs.append(cost)
            print("损失结果 %i: %f" %(i, cost))


    params = {"w": w,"b": b}

    grads = {"dw": dw,"db": db}
    return params, grads, costs

定义预测模块

def predict(w, b, X):
    '''
    利用训练好的参数预测
    return:预测结果
    '''

    m = X.shape[1]
    y_prediction = np.zeros((1, m))
    w = w.reshape(X.shape[0], 1)

    # 计算结果
    A = basic_sigmoid(np.dot(w.T, X) + b)

    for i in range(A.shape[1]):

        if A[0, i] <= 0.5:
            y_prediction[0, i] = 0
        else:
            y_prediction[0, i] = 1

    return y_prediction

定义逻辑回归模型

def model(x_train, y_train, x_test, y_test, num_iterations=2000, learning_rate=0.0001):
    """
    """

    # 修改数据形状
    x_train = x_train.reshape(-1, x_train.shape[0])
    x_test = x_test.reshape(-1, x_test.shape[0])
    y_train = y_train.reshape(1, y_train.shape[0])
    y_test = y_test.reshape(1, y_test.shape[0])
    print(x_train.shape)
    print(x_test.shape)
    print(y_train.shape)
    print(y_test.shape)

    # 1、初始化参数
    w, b = initialize_with_zeros(x_train.shape[0])

    # 2、梯度下降
    # params:更新后的网络参数
    # grads:最后一次梯度
    # costs:每次更新的损失列表
    params, grads, costs = optimize(w, b, x_train, y_train, num_iterations, learning_rate)

    # 获取训练的参数
    # 预测结果
    w = params['w']
    b = params['b']
    y_prediction_train = predict(w, b, x_train)
    y_prediction_test = predict(w, b, x_test)

    # 打印准确率
    print("训练集准确率: {} ".format(100 - np.mean(np.abs(y_prediction_train - y_train)) * 100))
    print("测试集准确率: {} ".format(100 - np.mean(np.abs(y_prediction_test - y_test)) * 100))

    return None

运行模型

model(x_train, y_train, x_test, y_test, num_iterations=3000, learning_rate=0.01)

设置迭代次数为3000,学习率设置为0.01,运行将得到以下结果:

(4, 350)
(4, 150)
(1, 350)
(1, 150)
损失结果 0: 0.693147
损失结果 100: 0.685711
损失结果 200: 0.681650
损失结果 300: 0.679411
损失结果 400: 0.678165
损失结果 500: 0.677465
损失结果 600: 0.677069
损失结果 700: 0.676843
损失结果 800: 0.676713
损失结果 900: 0.676639
损失结果 1000: 0.676595
损失结果 1100: 0.676570
损失结果 1200: 0.676556
损失结果 1300: 0.676547
损失结果 1400: 0.676542
损失结果 1500: 0.676539
损失结果 1600: 0.676538
损失结果 1700: 0.676537
损失结果 1800: 0.676536
损失结果 1900: 0.676536
损失结果 2000: 0.676535
损失结果 2100: 0.676535
损失结果 2200: 0.676535
损失结果 2300: 0.676535
损失结果 2400: 0.676535
损失结果 2500: 0.676535
损失结果 2600: 0.676535
损失结果 2700: 0.676535
损失结果 2800: 0.676535
损失结果 2900: 0.676535
训练集准确率: 60.57142857142857 
测试集准确率: 56.0 

损失函数值的变化如下图所示:
在这里插入图片描述

总结

本文详细介绍了逻辑回归的原理,并利用Python实现了逻辑回归的案例。从运行结果可以看到,随着迭代次数的增加,损失函数的值并未一直下降到接近0的位置,而是稳定在0.6附近。同时,对于训练集的预测的准确性为 60.57%,对于测试集的预测的准确性为56%。因此逻辑回归虽然简单容易理解,模型的可解释性非常好,但是由于模型的形式比较简单,无法很好地拟合数据的真实分布,所以准确性往往不是很高。

逻辑回归是一种用于分类问题的机器学习算法,其原理是基于线性回归的思想,通过使用逻辑函数(也称为sigmoid函数)将预测结果映射到[0,1]之间的概率值。以下是一个使用Python实现逻辑回归的示例代码: ```python import numpy as np import matplotlib.pyplot as plt # 定义sigmoid函数 def sigmoid(z): return 1 / (1 + np.exp(-z)) # 定义损失函数 def loss(h, y): return (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean() # 定义逻辑回归模型 class LogisticRegression: def __init__(self, lr=0.01, num_iter=100000, fit_intercept=True, verbose=False): self.lr = lr self.num_iter = num_iter self.fit_intercept = fit_intercept self.verbose = verbose def add_intercept(self, X): intercept = np.ones((X.shape[0], 1)) return np.concatenate((intercept, X), axis=1) def fit(self, X, y): if self.fit_intercept: X = self.add_intercept(X) # 初始化权重参数 self.theta = np.zeros(X.shape[1]) for i in range(self.num_iter): z = np.dot(X, self.theta) h = sigmoid(z) gradient = np.dot(X.T, (h - y)) / y.size self.theta -= self.lr * gradient if(self.verbose == True and i % 10000 == 0): z = np.dot(X, self.theta) h = sigmoid(z) print(f'loss: {loss(h, y)}') def predict_prob(self, X): if self.fit_intercept: X = self.add_intercept(X) return sigmoid(np.dot(X, self.theta)) def predict(self, X, threshold=0.5): return self.predict_prob(X) >= threshold # 示例:使用逻辑回归对二分类数据进行训练和预测 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成二分类数据集 X, y = make_classification(n_samples=100, n_features=2, n_informative=2, n_redundant=0, random_state=42) # 将数据集划分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 创建逻辑回归模型并进行训练 model = LogisticRegression(lr=0.1, num_iter=300000) model.fit(X_train, y_train) # 在测试集上进行预测 y_pred = model.predict(X_test) # 计算准确率 accuracy = (y_pred == y_test).mean() print(f'Accuracy: {accuracy}') ``` 这段代码首先定义了sigmoid函数和损失函数,然后实现了一个LogisticRegression类,其中包含了fit方法用于训练模型,以及predict_prob和predict方法用于预测。最后,示例代码使用sklearn库生成了一个二分类数据集,将数据集划分为训练集和测试集,并使用逻辑回归模型进行训练和预测。最后计算了预测的准确率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值