Python实现“经典梯度下降算法”训练逻辑回归模型

梯度下降实现要求

在这个任务中,我们将实现经典的梯度下降算法来训练逻辑回归模型。

完成这个任务的算法如下:

  • 基于课程4. 线性模型上计算出的偏导数,我们将编写一个函数来计算模型参数的二元交叉熵梯度

  • 让我们编写一个函数来根据计算出的梯度更新权重

  • 让我们编写一个函数来训练模型

其中:
该模型经过多个周期的训练,在每个周期中,我们根据数据集中每个对象的预测来更新模型的权重。这样的周期被称为“时代”。也就是说,一个 epoch 是根据从数据集中为每个对象计算的模型误差实现的一组权重更新。

您需要分几个时期对模型进行训练。它们的数量由函数参数指定。在每个时期内,需要循环遍历训练样本中的所有对象并更新模型权重。

注意:

  1. 如果您在完成第一项任务时遇到困难,并因此无法完成,建议您详细熟悉课程4. 线性模型的章节 ∂ H ∂ ω i \frac{\partial H}{\partial \omega_i} ωiH 的导数

  2. 请注意,函数参数描述中的对象-特征矩阵表示numpy.array()类型的变量,其每个元素都是numpy.array()类型的对象——相应对象的特征向量。

  3. 假设自由系数 a0 位于列表 alpha 的末尾

代码

import numpy as np

np.random.seed(42)


# 计算梯度的函数
def gradient(y_true: int, y_pred: float, x: np.array) -> np.array:
    """
    y_true - 对于对象 x 的真实响应值
    y_pred - 我们的模型预测的对象 x 属于类别 1 的概率值
    x - 该对象的特征描述向量

    期望在输出中得到关于预测 y_pred 值的模型参数的偏导数向量 H。
    请注意,由于自由系数 a0 的存在,这个梯度的维度应该比 x 的维度大 1。
    """
    # 在特征向量x前添加1,用于计算偏置项的梯度
    x_with_bias = np.concatenate([x, np.array([1])])
    grad = (y_pred - y_true) * x_with_bias
    return grad


# 更新权重的函数
def update(alpha: np.array, gradient: np.array, lr: float):
    """
    alpha: 当前模型参数向量的近似值
    gradient: 计算出的关于模型参数的梯度
    lr: 学习率,在参数更新公式中梯度前的乘数
    """
    alpha_new = alpha - lr * gradient
    return alpha_new


# 训练模型的函数
def train(
    alpha0: np.array, x_train: np.array, y_train: np.array, lr: float, num_epoch: int
):
    """
    alpha0 - 模型参数的初始近似值
    x_train - 训练样本的对象-特征矩阵
    y_train - 训练样本的正确答案
    lr - 学习率,在参数更新公式中梯度前的乘数
    num_epoch - 训练的轮数,即完整“遍历”整个数据集的次数
    """
    alpha = alpha0.copy()
    for epo in range(num_epoch):
        for i, x in enumerate(x_train):
            y_true = y_train[i]
            # 计算预测值,这里使用sigmoid函数
            def sigmoid(z):
                return 1 / (1 + np.exp(-z))

            # 预测值计算
            y_pred = sigmoid(np.dot(alpha, np.concatenate([x, np.array([1])])))
            grad = gradient(y_true, y_pred, x)
            alpha = update(alpha, grad, lr)
    return alpha

解析

1.导入必要的库

import numpy as np

np.random.seed(42)
  • import numpy as np:导入 numpy 库,它是 Python 中用于科学计算的基础库,提供了高效的多维数组对象和处理这些数组的工具。
  • np.random.seed(42):设置随机数种子为 42,这样每次运行代码时生成的随机数序列都是相同的,保证结果的可重复性。

2.定义梯度计算函数

def gradient(y_true: int, y_pred: float, x: np.array) -> np.array:
    """
    y_true - 对于对象 x 的真实响应值
    y_pred - 我们的模型预测的对象 x 属于类别 1 的概率值
    x - 该对象的特征描述向量

    期望在输出中得到关于预测 y_pred 值的模型参数的偏导数向量 H。
    请注意,由于自由系数 a0 的存在,这个梯度的维度应该比 x 的维度大 1。
    """
    # 在特征向量x前添加1,用于计算偏置项的梯度
    x_with_bias = np.concatenate([x, np.array([1])])
    grad = (y_pred - y_true) * x_with_bias
    return grad
  • gradient 函数用于计算逻辑回归损失函数关于模型参数的梯度。
  • y_true 是样本的真实标签(0 或 1),y_pred 是模型预测样本属于类别 1 的概率,x 是样本的特征向量。
  • x_with_bias 是在特征向量 x 后面添加一个 1,用于计算偏置项的梯度。
  • grad 是根据逻辑回归的梯度计算公式 (y_pred - y_true) * x_with_bias 计算得到的梯度向量。

代码详细解释

  1. 添加偏置项
x_with_bias = np.concatenate([x, np.array([1])])

在逻辑回归模型中,除了特征的权重参数,还有一个偏置项(也称为截距)。为了在计算梯度时能统一处理权重和偏置,我们在特征向量 x 的末尾添加一个值为 1 的元素,生成新的向量 x_with_bias。这样,偏置项就可以当作一个特殊的特征,其对应的权重就是偏置项的值。

  1. 计算梯度
grad = (y_pred - y_true) * x_with_bias

逻辑回归通常使用对数损失函数(也叫交叉熵损失函数)。对于单个样本,对数损失函数关于模型参数的梯度可以简化为 (y_pred - y_true) * x_with_bias。具体推导过程如下:

逻辑回归模型的预测概率公式为:
  y ^ = σ ( w T x + b ) \ \hat{y} = \sigma(\mathbf{w}^T\mathbf{x} + b)  y^=σ(wTx+b)
其中, σ ( z ) = 1 1 + e − z \sigma(z)=\frac{1}{1 + e^{-z}} σ(z)=1+ez1 是 sigmoid 函数, w \mathbf{w} w是权重向量, b b b 是偏置项, x \mathbf{x} x 是特征向量。

对数损失函数为:
L ( y , y ^ ) = − [ y log ⁡ ( y ^ ) + ( 1 − y ) log ⁡ ( 1 − y ^ ) ] L(y, \hat{y}) = -[y \log(\hat{y}) + (1 - y) \log(1 - \hat{y})] L(y,y^)=[ylog(y^)+(1y)log(1y^)]

对损失函数关于权重 w \mathbf{w} w求偏导数可得:
∂ L ∂ w = ( y ^ − y ) x \frac{\partial L}{\partial \mathbf{w}} = (\hat{y} - y) \mathbf{x} wL=(y^y)x

对损失函数关于偏置 b b b 求偏导数可得:
∂ L ∂ b = ( y ^ − y ) \frac{\partial L}{\partial b} = (\hat{y} - y) bL=(y^y)

将偏置项当作一个特殊的特征(对应特征值为 1),就可以将上述两个偏导数合并为一个向量:
∇ L = ( y ^ − y ) [ x 1 ] \nabla L = (\hat{y} - y) \begin{bmatrix} \mathbf{x} \\ 1 \end{bmatrix} L=(y^y)[x1]

这里的 grad 就是损失函数关于模型参数(权重和偏置)的梯度向量。

3.定义参数更新函数

def update(alpha: np.array, gradient: np.array, lr: float):
    """
    alpha: 当前模型参数向量的近似值
    gradient: 计算出的关于模型参数的梯度
    lr: 学习率,在参数更新公式中梯度前的乘数
    """
    alpha_new = alpha - lr * gradient
    return alpha_new
  • update 函数用于根据计算得到的梯度更新模型的参数。
  • alpha 是当前模型的参数向量,gradient 是计算得到的梯度向量,lr 是学习率。
  • alpha_new 是更新后的模型参数向量,使用梯度下降法的更新公式 alpha_new = alpha - lr * gradient 进行更新。

4.定义模型训练函数

def train(
    alpha0: np.array, x_train: np.array, y_train: np.array, lr: float, num_epoch: int
):
    """
    alpha0 - 模型参数的初始近似值
    x_train - 训练样本的对象-特征矩阵
    y_train - 训练样本的正确答案
    lr - 学习率,在参数更新公式中梯度前的乘数
    num_epoch - 训练的轮数,即完整“遍历”整个数据集的次数
    """
    alpha = alpha0.copy()
    for epo in range(num_epoch):
        for i, x in enumerate(x_train):
            y_true = y_train[i]
            # 计算预测值,这里使用sigmoid函数
            def sigmoid(z):
                return 1 / (1 + np.exp(-z))

            # 预测值计算
            y_pred = sigmoid(np.dot(alpha, np.concatenate([x, np.array([1])])))
            grad = gradient(y_true, y_pred, x)
            alpha = update(alpha, grad, lr)
    return alpha
  • train 函数用于训练逻辑回归模型。
  • alpha0 是模型参数的初始值,x_train 是训练数据的特征矩阵,y_train 是训练数据的真实标签,lr 是学习率,num_epoch 是训练的轮数。
  • alpha = alpha0.copy():复制初始参数,避免修改原始的初始参数。
  • 外层循环 for epo in range(num_epoch) 控制训练的轮数。
  • 内层循环 for i, x in enumerate(x_train) 遍历训练数据集中的每个样本。
  • sigmoid 函数用于将线性组合的结果转换为概率值,范围在 0 到 1 之间。
  • y_pred = sigmoid(np.dot(alpha, np.concatenate([x, np.array([1])]))):计算样本属于类别 1 的概率。
  • grad = gradient(y_true, y_pred, x):计算当前样本的梯度。
  • alpha = update(alpha, grad, lr):根据梯度更新模型的参数。
  • 最后返回训练好的模型参数。

综上所述,这段代码通过梯度下降法迭代更新模型的参数,使得模型的预测结果逐渐接近真实标签,从而实现逻辑回归模型的训练。

<think>好的,我现在需要分析用户提供的这段JavaScript代码,并解释它的功能。首先,我会通读代码,理解每个部分的作用。 这段代码定义了一个名为getQ的函数。函数一开始获取了user_nickname输入框的值,存储在userid变量中。如果userid是null或undefined,函数返回false。否则,发起一个AJAX POST请求到../services/Qxgl.ashx,传递action和id参数。请求头里有一个Authorization令牌,看起来像是某种认证信息。 成功回调函数处理返回的msg数据,遍历msg.ds数组,对每个item,通过typeid设置对应的复选框为选中状态。然后收集所有选中的复选框的值,用逗号拼接后去掉最后一个逗号,将结果填入typename输入框。 接下来,我需要确认各个部分的细节是否正确。例如,检查jQuery选择器是否正确,AJAX调用的参数是否合理,以及数据处理是否有潜在错误。比如,$.each遍历msg.ds是否正确,假设msg.ds是一个数组。另外,拼接结果时使用substring去除末尾逗号是否可靠,是否有更稳妥的方法。 还要注意可能的安全问题,比如硬编码的Authorization头,这可能存在安全风险,但用户可能已经处理了。此外,用户权限验证是否在服务端进行,这里只是前端控制,需提醒用户不能仅依赖前端验证。 最后,将这些分析整理成结构清晰的解释,分步骤说明代码的功能和潜在注意事项,确保用户能够理解每个部分的作用及可能的改进点。</think>这是一个典型的基于jQuery的AJAX请求实现,主要用于获取并设置用户权限信息。让我们分步骤解析这个函数的功能: 1. **函数定义与用户ID获取** ```javascript var getQ = function () { var userid = $('#user_nickname').val(); // 获取用户输入框的值 ``` 2. **空值校验** ```javascript if (userid == null || userid == undefined) { // 验证用户ID有效性 return false; } ``` 3. **AJAX请求配置** ```javascript $.ajax({ type: "POST", // 使用POST方法 url: "../services/Qxgl.ashx", // 后端接口地址 headers: { // 请求头认证信息 "Authorization": "0772A4AA051DDEBB393DC164B19A32E57C4FEEAB27A77820" }, ``` 4. **请求参数传递** ```javascript data: { action: "getQ", // 执行动作类型 id: userid // 传递用户ID参数 }, ``` 5. **成功回调处理** ```javascript success: function (msg) { // 遍历返回数据集 $.each(msg.ds, function (index, item) { // 根据typeid勾选对应复选框 $('#a' + item.typeid).attr("checked", true); }) // 拼接选中值 var result = ""; $("input[name='select_checkbox']:checked").each(function () { result += $(this).val() + ','; }); // 去除末尾逗号 if (result != "") { result = result.substring(0, result.lastIndexOf(',')); } // 填充到显示框 $("#typename").val(result); } ``` **功能总结**: - 这是一个用户权限查询功能 - 通过用户输入的用户名查询关联的权限配置 - 将查询结果转换为复选框勾选状态 - 汇总选中权限显示在文本框中 **潜在注意事项**: 1. 硬编码的`Authorization`头存在安全风险,建议动态生成 2. 缺少错误处理逻辑(error回调) 3. 使用字符串拼接处理结果可以考虑改用`Array.join()` 4. 权限控制不能仅依赖前端验证,必须配套后端验证 5. 对`msg.ds`的数据结构有强依赖,建议增加数据校验 6. 建议使用`.prop()`代替`.attr()`操作复选框状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥都鼓捣的小yao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值