第五节-逻辑回归原理推导与代码实现(第一章: 机器学习算法精进及其案例应用(课程笔记) )

5.逻辑回归原理推导与代码实现

5.1-原理与化简求解

Sigmoid 函数是一个常用的数学函数,通常用来将输入值映射到一个介于0和1之间的输出值。它的数学表达式如下:

\sigma(x) = \frac{1}{1 + e^{-x}}

其中,x 是输入值,\sigma(x)是 Sigmoid 函数的输出值。

下面是对 Sigmoid 函数的一些关键解释:

  1. S 形状曲线: Sigmoid 函数以一种 S 形状的曲线来表示。这个曲线在 x=0 处取得最大斜率,随着 x 的增加或减小,函数的输出值逐渐趋近于0或1。

  2. 范围: Sigmoid 函数的输出范围在0到1之间,这使得它特别适合用于表示概率或激活神经元输出的概率。

  3. 平滑性: Sigmoid 函数是光滑的,具有连续的导数,这对于许多机器学习和神经网络模型的训练很有用,因为可以使用梯度下降等优化算法来调整模型参数。

  4. 输入变化的敏感性:x 接近正无穷或负无穷时,Sigmoid 函数的导数接近于0,这意味着在极端值附近,函数对输入的变化不敏感,这有时称为梯度消失问题。

  5. 二元分类器: Sigmoid 函数常用于二元分类问题中,其中输出值可以被解释为某个样本属于某一类的概率。通常,当输出值大于0.5时,可以将样本分类为正类,否则分类为负类。

总之,Sigmoid 函数在神经网络和统计建模中被广泛使用,尤其是在二元分类和概率建模中。然而,对于深度神经网络,它有时会面临梯度消失问题,因此在某些情况下,其他激活函数如ReLU和Leaky ReLU可能更为常见。

Logistic Regression(逻辑回归)是一种用于二元分类问题的统计和机器学习算法。它的主要作用和优势如下:

作用:

  1. 二元分类: Logistic Regression 主要用于解决二元分类问题,其中目标是将输入数据点分为两个互斥的类别,通常是正类和负类。

  2. 概率估计: 逻辑回归不仅可以进行分类,还可以估计数据点属于正类的概率。这对于需要估计不确定性或获得类别概率的应用非常有用,如金融风险评估或医疗诊断。

  3. 可解释性: 逻辑回归的模型参数(权重)具有直观的解释,可以帮助理解不同特征对分类的影响程度。

优势:

  1. 简单而有效: 逻辑回归是一种简单但通常非常有效的分类算法。它不需要太多的超参数调整,容易实现和理解。

  2. 线性可分性: 当数据在特征空间中近似线性可分时,逻辑回归表现出色。它可以很好地处理线性关系。

  3. 低风险过拟合: 相对于一些复杂的模型(如深度神经网络),逻辑回归倾向于具有较低的风险过拟合。这使得它在小型数据集上表现良好,并且不太容易受到噪声数据的干扰。

  4. 可解释性强: 逻辑回归的参数权重具有可解释性,可以帮助分析特征对分类的影响。这对于一些领域需要可解释性模型的应用很重要,如医疗和金融领域。

  5. 速度快: 逻辑回归的训练速度通常很快,尤其是在大规模数据集上。这使得它在实时应用或需要快速迭代的情况下很有优势。

尽管逻辑回归在某些情况下可能不适用于复杂的非线性问题,但它仍然是一个强大的分类器,特别适用于那些具有线性可分性或需要可解释性的任务。

5.2-逻辑回归代码实现

import numpy as np  # 导入NumPy库,用于处理数值计算
from scipy.optimize import minimize  # 导入SciPy库中的最小化函数
from utils.features import prepare_for_training  # 导入自定义的特征处理函数
from utils.hypothesis import sigmoid  # 导入自定义的S型函数


class LogisticRegression:
    def __init__(self, data, labels, polynomial_degree=0, sinusoid_degree=0, normalize_data=False):
        """
        1. 对数据进行预处理操作
        2. 先得到所有的特征个数
        3. 初始化参数矩阵
        """
        # 调用prepare_for_training函数,对数据进行预处理,并获取相关信息
        (data_processed,
         features_mean,
         features_deviation) = prepare_for_training(data, polynomial_degree, sinusoid_degree, normalize_data=False)

        self.data = data_processed  # 存储处理后的数据
        self.labels = labels  # 存储标签
        self.unique_labels = np.unique(labels)  # 获取唯一的标签值
        self.features_mean = features_mean  # 存储特征均值
        self.features_deviation = features_deviation  # 存储特征标准差
        self.polynomial_degree = polynomial_degree  # 存储多项式扩展的次数
        self.sinusoid_degree = sinusoid_degree  # 存储正弦扩展的次数
        self.normalize_data = normalize_data  # 存储是否进行数据标准化的标志

        num_features = self.data.shape[1]  # 获取特征数量
        num_unique_labels = np.unique(labels).shape[0]  # 获取唯一标签的数量
        self.theta = np.zeros((num_unique_labels, num_features))  # 初始化参数矩阵

逻辑回归是一种用于二分类问题的机器学习算法。下面是逻辑回归的代码实现:

  • 该代码实现了一个名为LogisticRegression的类,用于执行逻辑回归任务。
  • 使用了NumPy和SciPy库来进行数值计算和参数优化。
  • 代码包含了数据预处理、模型训练、预测和优化等功能。

5.3-训练模块功能

    def train(self, max_iterations=1000):
        cost_histories = []  # 用于存储每个类别的损失函数历史记录
        num_features = self.data.shape[1]  # 获取特征数量
        for label_index, unique_label in enumerate(self.unique_labels):
            current_initial_theta = np.copy(self.theta[label_index].reshape(num_features, 1))
            current_lables = (self.labels == unique_label).astype(float)  # 将标签转换为浮点数形式
            # 调用gradient_descent函数进行训练
            (current_theta, cost_history) = LogisticRegression.gradient_descent(self.data, current_lables,
                                                                                current_initial_theta, max_iterations)
            self.theta[label_index] = current_theta.T  # 更新参数矩阵
            cost_histories.append(cost_history)  # 存储损失函数历史记录

        return self.theta, cost_histories  # 返回训练后的参数和损失函数历史记录

训练模块的功能包括:

  • 预处理数据:对输入数据进行预处理,包括多项式扩展、正弦扩展和数据标准化。
  • 初始化参数:根据标签类别数量和特征数量初始化参数矩阵。
  • 使用梯度下降进行训练:对每个标签类别执行梯度下降优化,更新参数矩阵,同时记录损失函数的历史记录。


5.4-完成预测模块

    def predict(self, data):
        num_examples = data.shape[0]  # 获取样本数量
        data_processed = prepare_for_training(data, self.polynomial_degree, self.sinusoid_degree, self.normalize_data)[
            0]
        prob = LogisticRegression.hypothesis(data_processed, self.theta.T)  # 计算预测概率
        max_prob_index = np.argmax(prob, axis=1)  # 获取最大概率的类别索引
        class_prediction = np.empty(max_prob_index.shape, dtype=object)  # 创建一个用于存储类别预测的数组
        for index, label in enumerate(self.unique_labels):
            class_prediction[max_prob_index == index] = label  # 将预测的类别标签存储到数组中
        return class_prediction.reshape((num_examples, 1))  # 返回类别预测结果

完成预测模块的功能包括:

  • 预测数据:对输入数据进行预测,首先进行与训练数据相同的预处理操作。
  • 计算类别概率:使用逻辑回归的假设函数来计算每个类别的概率。
  • 选择最可能的类别:对于每个样本,选择具有最高概率的类别作为预测结果。

5.5-优化目标定义

    @staticmethod
    def cost_function(data, labels, theta):
        num_examples = data.shape[0]  # 获取样本数量
        predictions = LogisticRegression.hypothesis(data, theta)  # 计算预测概率
        y_is_set_cost = np.dot(labels[labels == 1].T, np.log(predictions[labels == 1]))  # 正类别的损失
        y_is_not_set_cost = np.dot(1 - labels[labels == 0].T, np.log(1 - predictions[labels == 0]))  # 负类别的损失
        cost = (-1 / num_examples) * (y_is_set_cost + y_is_not_set_cost)  # 计算损失函数
        return cost

优化目标定义的关键部分包括:

  • 损失函数:使用交叉熵损失函数来度量模型的性能,其中包括正类别和负类别的损失项。
  • 目标最小化:通过优化器(这里使用了SciPy的minimize函数)来最小化损失函数,以找到最佳参数。
  • 损失函数的公式和计算:在损失函数中,计算了正类别和负类别的损失,并将它们组合成总损失。

5.6-迭代优化参数

    @staticmethod
    def gradient_descent(data, labels, current_initial_theta, max_iterations):
        cost_history = []  # 用于存储损失函数历史记录
        num_features = data.shape[1]  # 获取特征数量
        result = minimize(
            # 要优化的目标:调用cost_function计算损失函数
            lambda current_theta: LogisticRegression.cost_function(data, labels,
                                                                   current_theta.reshape(num_features, 1)),
            # 初始化的权重参数
            current_initial_theta,
            # 选择优化策略
            method='CG',
            # 梯度下降迭代计算公式:调用gradient_step计算梯度
            jac=lambda current_theta: LogisticRegression.gradient_step(data, labels,
                                                                       current_theta.reshape(num_features, 1)),
            # 记录结果
            callback=lambda current_theta: cost_history.append(
                LogisticRegression.cost_function(data, labels, current_theta.reshape((num_features, 1)))),
            # 迭代次数
            options={'maxiter': max_iterations}
        )
        if not result.success:
            raise ArithmeticError('Can not minimize cost function' + result.message)
        optimized_theta = result.x.reshape(num_features, 1)  # 获取优化后的参数
        return optimized_theta, cost_history  # 返回优化后的参数和损失函数历史记录

在逻辑回归中,迭代优化参数的步骤包括:

  • 初始化参数:为每个标签类别初始化参数向量。
  • 调用优化器:使用优化器(这里使用了共轭梯度方法)来最小化损失函数。
  • 损失函数的迭代记录:在每次迭代中,记录损失函数的值,以便后续分析和可视化。
  • 检查优化成功:检查优化器是否成功找到了最小化损失函数的参数。

5.7-梯度计算

    @staticmethod
    def hypothesis(data, theta):
        predictions = sigmoid(np.dot(data, theta))  # 使用S型函数计算预测概率
        return predictions

    @staticmethod
    def gradient_step(data, labels, theta):
        num_examples = labels.shape[0]  # 获取样本数量
        predictions = LogisticRegression.hypothesis(data, theta)  # 计算预测概率
        label_diff = predictions - labels  # 预测值与实际值之差
        gradients = (1 / num_examples) * np.dot(data.T, label_diff)  # 计算梯度
        return gradients.T.flatten()  # 返回梯度向量

逻辑回归中的梯度计算包括:

  • 假设函数:使用S型函数(sigmoid)来进行预测概率的计算。
  • 梯度下降:使用梯度下降算法来更新参数,以最小化损失函数。
  • 梯度计算公式:计算梯度的公式是关键,它告诉我们如何根据损失函数来更新参数。

5.8-得出最终结果

# 创建一个LogisticRegression的实例(假设名为`model`)
model = LogisticRegression(data, labels, polynomial_degree=2, sinusoid_degree=0, normalize_data=True)

# 进行模型训练
trained_theta, _ = model.train(max_iterations=1000)

# 待预测的新数据(假设名为`new_data`)
new_data = ...

# 使用训练后的模型进行预测
predictions = model.predict(new_data)

# 打印或使用预测结果
print(predictions)

最终结果的获取包括:

  • 预测类别:对新数据进行预测,根据最高概率的类别来分配标签。
  • 返回结果:将预测的类别结果作为函数的输出,以供后续使用或评估模型性能。


5.9-鸢尾花数据集多分类任务

import numpy as np  # 导入NumPy库,用于数值计算
import pandas as pd  # 导入Pandas库,用于数据处理和分析
import matplotlib.pyplot as plt  # 导入Matplotlib库,用于数据可视化

from logistic_regression import LogisticRegression  # 导入自定义的逻辑回归模块

# 从CSV文件读取数据
data = pd.read_csv('../data/iris.csv')

# 不同类型的鸢尾花类别
iris_types = ['SETOSA', 'VERSICOLOR', 'VIRGINICA']

# 选择用于散点图的 x 和 y 轴特征
x_axis = 'petal_length'
y_axis = 'petal_width'

# 针对每个鸢尾花类别,创建散点图
for iris_type in iris_types:
    plt.scatter(data[x_axis][data['class'] == iris_type],  # x轴数据:petal_length
                data[y_axis][data['class'] == iris_type],  # y轴数据:petal_width
                label=iris_type  # 类别标签
                )

plt.show()  # 显示散点图

  1. 读取数据:使用Pandas的read_csv函数从CSV文件加载鸢尾花数据集,将数据存储在名为data的DataFrame中。

  2. 定义鸢尾花类别:使用iris_types列表定义了三个不同的鸢尾花类别,分别是SETOSA、VERSICOLOR和VIRGINICA。

  3. 选择特征:通过定义x_axisy_axis变量,选择了用于散点图的两个特征,即petal_length和petal_width。

  4. 创建散点图:使用循环针对每个鸢尾花类别,在散点图上绘制了相应类别的数据点。每个类别的数据点在散点图上以不同的颜色表示,并使用类别标签进行标注。

  5. 显示图形:最后,通过调用plt.show()函数显示生成的散点图,以可视化不同鸢尾花类别在两个特征上的分布情况。

5.10-训练多分类模型

...
num_examples = data.shape[0]  # 获取数据集中的样本数量
x_train = data[[x_axis, y_axis]].values.reshape((num_examples, 2))  # 提取特征并重塑为矩阵形式
y_train = data['class'].values.reshape((num_examples, 1))  # 提取类别标签并重塑为矩阵形式

max_iterations = 1000  # 设置最大迭代次数
polynomial_degree = 0  # 多项式扩展的次数(在此处为0表示不进行多项式扩展)
sinusoid_degree = 0  # 正弦扩展的次数(在此处为0表示不进行正弦扩展)

logistic_regression = LogisticRegression(x_train, y_train, polynomial_degree, sinusoid_degree)  # 创建逻辑回归模型的实例
thetas, cost_histories = logistic_regression.train(max_iterations)  # 使用训练数据进行模型训练,获取参数和损失历史记录
labels = logistic_regression.unique_labels  # 获取模型的唯一标签类别

# 绘制损失函数历史记录的折线图
plt.plot(range(len(cost_histories[0])), cost_histories[0], label=labels[0])
plt.plot(range(len(cost_histories[1])), cost_histories[1], label=labels[1])
plt.plot(range(len(cost_histories[2])), cost_histories[2], label=labels[2])
plt.show()  # 显示折线图


5.11-决策边界绘制

# 预测训练数据的类别标签
y_train_prections = logistic_regression.predict(x_train)

# 计算训练精度(准确率)
precision = np.sum(y_train_prections == y_train) / y_train.shape[0] * 100

# 打印训练精度
print('precision:', precision)

# 定义可视化范围和采样点数量
x_min = np.min(x_train[:, 0])
x_max = np.max(x_train[:, 0])
y_min = np.min(x_train[:, 1])
y_max = np.max(x_train[:, 1])
samples = 150

# 在x和y的范围内生成均匀采样点
X = np.linspace(x_min, x_max, samples)
Y = np.linspace(y_min, y_max, samples)

# 初始化三个类别的分类结果矩阵
Z_SETOSA = np.zeros((samples, samples))
Z_VERSICOLOR = np.zeros((samples, samples))
Z_VIRGINICA = np.zeros((samples, samples))

# 遍历所有采样点
for x_index, x in enumerate(X):
    for y_index, y in enumerate(Y):
        data = np.array([[x, y]])
        # 对每个采样点进行预测
        prediction = logistic_regression.predict(data)[0][0]
        # 将预测的类别映射到相应的分类结果矩阵中
        if prediction == 'SETOSA':
            Z_SETOSA[x_index][y_index] = 1
        elif prediction == 'VERSICOLOR':
            Z_VERSICOLOR[x_index][y_index] = 1
        elif prediction == 'VIRGINICA':
            Z_VIRGINICA[x_index][y_index] = 1

上述代码部分主要涉及以下内容的简要概述:

  1. 计算训练精度

    • 使用训练好的多分类逻辑回归模型对训练数据进行预测,得到y_train_predictions,即模型对训练数据的类别预测结果。
    • 计算训练精度(准确率),以了解模型在训练数据上的性能。
  2. 可视化决策边界

    • 定义了可视化的范围,即x和y轴的最小值和最大值,以及采样点数量。
    • 在x和y的范围内生成均匀的采样点,并存储在XY中。
  3. 初始化分类结果矩阵

    • 为三个不同类别(SETOSA、VERSICOLOR和VIRGINICA)分别创建了对应的分类结果矩阵(Z_SETOSAZ_VERSICOLORZ_VIRGINICA)。
  4. 遍历采样点

    • 使用嵌套循环遍历所有的采样点,其中外循环迭代x轴上的采样点,内循环迭代y轴上的采样点。
    • 对每个采样点,将其坐标构成一个数据点(data)并使用训练好的模型进行类别预测。
  5. 分类结果映射

    • 根据模型的类别预测结果,将对应类别的分类结果矩阵的相应位置置为1,表示该点属于该类别。

这段代码的主要目的是通过可视化展示模型的决策边界,即在二维特征空间中,模型如何将不同类别的样本分隔开。同时,计算训练精度以评估模型在训练数据上的性能。

5.12-非线性决策边界

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math

# 导入自定义的逻辑回归类
from logistic_regression import LogisticRegression

# 从CSV文件读取数据
data = pd.read_csv('../data/microchips-tests.csv')

# 定义类别标签,这里有两个类别:0 和 1
validities = [0, 1]

# 选择两个特征作为 x 和 y 轴
x_axis = 'param_1'
y_axis = 'param_2'

# 创建散点图,按类别不同绘制不同颜色的点
for validity in validities:
    plt.scatter(
        data[x_axis][data['validity'] == validity],  # x轴数据
        data[y_axis][data['validity'] == validity],  # y轴数据
        label=validity  # 类别标签
    )

# 设置 x 和 y 轴标签
plt.xlabel(x_axis)
plt.ylabel(y_axis)
# 设置图表标题
plt.title('Microchips Tests')
# 添加图例
plt.legend()
# 显示图表
plt.show()

# 获取数据集中的样本数量
num_examples = data.shape[0]

# 提取特征数据和标签数据,并进行reshape以匹配逻辑回归模型的输入
x_train = data[[x_axis, y_axis]].values.reshape((num_examples, 2))
y_train = data['validity'].values.reshape((num_examples, 1))

# 设置训练参数
max_iterations = 100000  # 最大迭代次数
regularization_param = 0  # 正则化参数
polynomial_degree = 5  # 多项式特征的最高次数
sinusoid_degree = 0  # 正弦特征的次数

# 创建逻辑回归对象
logistic_regression = LogisticRegression(x_train, y_train, polynomial_degree, sinusoid_degree)

# 训练逻辑回归模型
(thetas, costs) = logistic_regression.train(max_iterations)

# 创建列名列表用于标识每个theta参数
columns = []
for theta_index in range(0, thetas.shape[1]):
    columns.append('Theta ' + str(theta_index))

# 绘制损失函数随梯度下降步骤的变化图像
labels = logistic_regression.unique_labels
plt.plot(range(len(costs[0])), costs[0], label=labels[0])
plt.plot(range(len(costs[1])), costs[1], label=labels[1])

plt.xlabel('Gradient Steps')
plt.ylabel('Cost')
plt.legend()
plt.show()

# 使用训练好的模型进行预测
y_train_predictions = logistic_regression.predict(x_train)

# 计算训练精度
precision = np.sum(y_train_predictions == y_train) / y_train.shape[0] * 100

# 打印训练精度
print('Training Precision: {:5.4f}%'.format(precision))

# 定义用于绘制决策边界的网格点数和范围
num_examples = x_train.shape[0]
samples = 150
x_min = np.min(x_train[:, 0])
x_max = np.max(x_train[:, 0])
y_min = np.min(x_train[:, 1])
y_max = np.max(x_train[:, 1])

# 生成用于绘制决策边界的网格点坐标
X = np.linspace(x_min, x_max, samples)
Y = np.linspace(y_min, y_max, samples)
Z = np.zeros((samples, samples))

# 绘制决策边界
for x_index, x in enumerate(X):
    for y_index, y in enumerate(Y):
        data = np.array([[x, y]])
        Z[x_index][y_index] = logistic_regression.predict(data)[0][0]

# 提取正类别和负类别样本
positives = (y_train == 1).flatten()
negatives = (y_train == 0).flatten()

# 绘制散点图显示正类别和负类别样本点
plt.scatter(x_train[negatives, 0], x_train[negatives, 1], label='0')
plt.scatter(x_train[positives, 0], x_train[positives, 1], label='1')

# 绘制决策边界的等高线图
plt.contour(X, Y, Z)

# 设置 x 和 y 轴标签
plt.xlabel('param_1')
plt.ylabel('param_2')
# 设置图表标题
plt.title('Microchips Tests')
# 添加图例
plt.legend()
# 显示图表
plt.show()

逻辑回归是一个用于分类问题的机器学习算法,通常用于二分类任务,但也可以扩展到多类别分类。在上述代码中,逻辑回归被用于解决二分类问题,其中目标是通过非线性决策边界将两个类别的数据点分开。以下是逻辑回归做非线性决策边界的原理和优势的简要概述:

原理:

  1. 假设函数: 逻辑回归使用一个假设函数,通常称为sigmoid函数或logistic函数,将输入特征的线性组合映射到0到1之间的概率值。这个函数的形状使其适合用于二分类问题。

  2. 决策边界: 逻辑回归的目标是找到一个决策边界,它可以将两个不同类别的数据点分开。这个边界可以是线性的,也可以是非线性的,具体取决于特征和模型的选择。

  3. 损失函数: 逻辑回归使用对数似然损失函数来衡量模型预测与实际标签之间的差距。优化过程旨在最小化这个损失函数,以找到最佳的决策边界。

  4. 梯度下降: 通过梯度下降或其他优化算法,逻辑回归调整模型参数,使损失函数最小化。这导致了逻辑回归在训练数据上学习出一个最佳的决策边界。

优势:

  1. 非线性决策边界: 逻辑回归具有天然的非线性建模能力。通过引入多项式特征、正弦特征等非线性特征,逻辑回归可以捕捉到复杂的决策边界,从而更好地拟合非线性关系的数据。

  2. 可解释性强: 逻辑回归的模型参数(权重)具有直观的解释性,可以用于理解每个特征对分类决策的影响。这有助于解释模型的预测结果。

  3. 计算效率高: 逻辑回归的训练相对简单和快速,尤其适用于大规模数据集。它不需要大量的计算资源,因此可以在资源受限的环境中使用。

  4. 适用于概率输出: 逻辑回归输出的概率值可以用于评估不确定性,而不仅仅是硬分类结果。这对于许多应用,如医疗诊断或风险评估,非常有用。

总之,逻辑回归是一种强大的分类算法,它可以适应非线性数据并学习非线性决策边界,同时具有较强的可解释性和计算效率。这使得逻辑回归成为许多分类问题的首选算法之一。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值