import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler # 定义sigmoid函数 def sigmoid(z): return 1 / (1 + np.exp(-z)) # 定义逻辑回归的梯度下降函数 def gradient_descent(X, y, theta, learning_rate, num_iterations): m = len(y)#元素个数 cost_history = [] for i in range(num_iterations): h = sigmoid(X.dot(theta)) gradient = X.T.dot(h - y) / m theta = theta - learning_rate * gradient cost = -np.sum(y * np.log(h) + (1 - y) * np.log(1 - h)) / m cost_history.append(cost) return theta, cost_history # 读取数据 data = pd.read_csv('credit-overdue.csv') X = data[['debt', 'income']].values y = data['overdue'].values # # 标准化 # scaler = StandardScaler() # X = scaler.fit_transform(X) # 数据预处理,添加偏置项 X = np.hstack((np.ones((X.shape[0], 1)), X))#np.ones方法生成了一列1,hstack将这一列1和原来的矩阵和起来,现在的x多一个1,参数多一个b # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 初始化参数 theta = np.zeros(X_train.shape[1])#初始化参数 learning_rate = 0.01#学习率 num_iterations = 1000#迭代次数 # 执行梯度下降 theta, cost_history = gradient_descent(X_train, y_train, theta, learning_rate, num_iterations) # 预测 y_pred = sigmoid(X_test.dot(theta)) y_pred = (y_pred >= 0.5).astype(int)#将布尔值转化为整数 # 输出分类报告 print(classification_report(y_test, y_pred)) # 绘制损失函数的迭代过程 plt.plot(range(num_iterations), cost_history) plt.xlabel('Iterations') plt.ylabel('Cost') plt.title('Gradient Descent Progress') plt.show() # 绘制分类线 x1_min, x1_max = X[:, 1].min(), X[:, 1].max()#最原始数据x的第一列 x2_min, x2_max = X[:, 2].min(), X[:, 2].max() # 生成网格坐标点(用于绘制决策边界) xx1, xx2 = np.meshgrid( np.linspace(x1_min, x1_max, 100), # 在x1范围内生成100个等间距点,xx1每列数据完全重复,第i列所有元素值 = 第i个x1坐标值(因为按行分配点) np.linspace(x2_min, x2_max, 100) # 在x2范围内生成100个等间距点,xx2每行数据完全重复,第i行所有元素值 = 第i个x2坐标值(因为按列分配点) ) # xx1和xx2均为100x100的矩阵, # 计算网格点上逻辑回归的预测概率(为了画阈值为0.5的等高线,可视化参数) Z = sigmoid(np.c_[np.ones(xx1.ravel().shape[0]), xx1.ravel(), xx2.ravel()].dot(theta))#ravel,按行将二维展成一维列向量的转置 #相当于ones生成1w长度的列全1,np.c_用于连接数组,将多个列,组成一个二维矩阵,dot矩阵乘法,10000*3的矩阵*3*1的矩阵10000的一个列向量 #调用上面的逻辑回归函数,生成预测值的结果向量 Z = Z.reshape(xx1.shape) # 将预测结果Z恢复为100x100的网格形状 plt.figure(figsize=(10, 6)) # 创建画布 # 绘制未逾期样本(标签为0)的散点图 plt.scatter(X[y == 0, 1], X[y == 0, 2], # 选择标签为0的样本的x1和x2,最开始的数据 c='b', # 蓝色标记 marker='o', # 圆形标记 label='Not Overdue') # 图例标签 # 绘制逾期样本(标签为1)的散点图 plt.scatter(X[y == 1, 1], X[y == 1, 2], # 选择标签为1的样本的x1和x2 c='r', # 红色标记 marker='x', # 叉形标记 label='Overdue') # 图例标签 # 添加决策边界线(σ(z)=0.5对应的等高线) plt.contour(xx1, xx2, Z, # 根据预测概率,绘制一条阈值为0.5的等高线,将训练后得到的theta可视化处理 levels=[0.5], # 指定绘制σ(z)=0.5的等高线 colors='black', # 黑色边界线 linestyles='dashed') # 虚线样式 # 设置坐标轴标签和标题 plt.xlabel('Debt') # x轴:债务(假设X[:,1]代表债务) plt.ylabel('Income') # y轴:收入(假设X[:,2]代表收入) plt.title('Logistic Regression Decision Boundary') # 标题 plt.legend() # 显示图例 plt.grid(True, linestyle='--', alpha=0.5) # 添加半透明虚线网格 plt.show() # 显示图形为这段代码编写一个标准化代码
时间: 2025-06-15 21:04:53 浏览: 34
<think>嗯,用户让我帮忙分析他们的Python代码,尤其是关于逻辑回归的实现。首先,我需要仔细阅读他们提供的代码,理解每一部分的功能,然后按照他们的要求生成标准化的代码和解释。用户还提到要使用中文回答,结构清晰,分步解决问题,所以我要确保我的回答有条理,容易理解。
首先,用户导入了一些必要的库,比如numpy、pandas、sklearn中的一些模块,还有matplotlib。这些库通常用于数据处理、机器学习模型训练和可视化。接下来,他们定义了sigmoid函数和梯度下降函数,这部分是逻辑回归的核心算法。我需要检查这些函数的实现是否正确,比如梯度下降的更新公式和成本函数的计算。
然后,用户读取了数据,进行了标准化和数据预处理,包括添加偏置项。不过看起来他们注释掉了标准化部分,可能需要提醒他们是否应该取消注释,或者解释标准化的作用。数据预处理部分添加了一列1,这是为了对应偏置项theta0,正确。
接下来划分训练集和测试集,参数初始化,执行梯度下降,进行预测,并输出分类报告。这些步骤都是标准的机器学习流程,看起来没有问题。但可能需要检查学习率、迭代次数是否合理,以及梯度下降的实现是否正确,比如是否使用了向量化操作。
可视化部分,用户绘制了损失函数的变化曲线和分类决策边界。这里需要注意网格点的生成和预测值的计算是否正确,特别是np.meshgrid和ravel的使用是否恰当。决策边界是通过等高线绘制的,阈值为0.5,这部分代码看起来没问题,但需要确认Z的计算是否正确应用了训练后的theta参数。
用户希望为这段代码编写一个标准化代码,可能是指代码的结构优化、注释补充、符合PEP8规范等。可能需要检查代码的格式,比如空格、缩进、变量命名是否符合标准,并添加必要的注释和文档字符串,使其更易读和规范。
另外,用户可能没有注意到数据标准化被注释掉了,是否需要根据数据情况决定是否启用标准化?如果特征尺度差异大,标准化可以帮助梯度下降收敛更快。但代码中目前没有使用,可能需要提醒用户这一点。
在编写标准化代码时,应该包括以下部分:导入库、数据加载、数据预处理(包括标准化)、模型定义、训练、评估、可视化。同时,函数和变量命名要有描述性,添加必要的注释,确保代码结构清晰。
可能存在的错误点:梯度下降中的成本函数计算是否正确?这里使用的是对数损失函数,看起来公式是对的。另外,预测时是否应用了和训练时相同的预处理步骤,比如标准化?如果启用了标准化,测试集也应该用相同的scaler进行转换,而不仅仅是训练集。但当前代码中标准化被注释掉了,所以暂时没问题,但如果用户取消注释,需要调整代码,确保测试集也标准化。
绘制决策边界时,如果进行了标准化,生成网格点也需要标准化处理,否则边界的位置会不正确。但当前代码在注释掉标准化的情况下,直接使用原始数据范围生成网格点,这是正确的。但如果启用了标准化,网格点也需要用相同的scaler进行转换,否则绘制的边界位置会有偏差。这部分可能需要提醒用户,根据是否标准化调整绘图代码。
总结,我需要将用户的代码整理成更规范的结构,补充必要的注释,检查可能的问题点,并确保代码逻辑正确,可视化部分正确处理数据。同时,解释代码的每一步骤,帮助用户理解实现细节和潜在改进点。</think>以下是针对该代码的标准化重构版本,包含规范注释和结构调整:
```python
"""
逻辑回归模型完整实现示例
包含数据预处理、模型训练、评估及可视化
数据集格式假设:CSV文件包含'debt', 'income', 'overdue'三列
"""
# 标准库导入
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Scikit-learn模块导入
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
# 设置全局样式
plt.style.use('seaborn-v0_8')
def sigmoid(z: np.ndarray) -> np.ndarray:
"""
Sigmoid激活函数
参数:
z -- 线性变换结果
返回:
经过S形变换的概率值
"""
return 1 / (1 + np.exp(-z))
def gradient_descent(
X: np.ndarray,
y: np.ndarray,
theta: np.ndarray,
learning_rate: float,
num_iterations: int
) -> tuple[np.ndarray, list]:
"""
批量梯度下降优化算法
参数:
X -- 特征矩阵 (m, n+1)
y -- 标签向量 (m,)
theta -- 初始参数向量 (n+1,)
learning_rate -- 学习率
num_iterations -- 迭代次数
返回:
优化后的参数向量
损失历史记录
"""
m = y.size
cost_history = []
for _ in range(num_iterations):
h = sigmoid(X @ theta) # 假设预测 (m,)
gradient = X.T @ (h - y) / m # 梯度计算 (n+1,)
theta -= learning_rate * gradient
# 交叉熵损失计算
epsilon = 1e-8 # 防止log(0)
cost = -(y @ np.log(h + epsilon) + (1 - y) @ np.log(1 - h + epsilon)) / m
cost_history.append(cost)
return theta, cost_history
def main():
# 数据加载与预处理 --------------------------------
data = pd.read_csv('credit-overdue.csv')
features = data[['debt', 'income']].values
target = data['overdue'].values
# 特征标准化(根据数据分布决定是否启用)
# scaler = StandardScaler()
# features = scaler.fit_transform(features)
# 添加偏置项
X = np.hstack([np.ones((features.shape[0], 1)), features])
# 数据集划分 --------------------------------
X_train, X_test, y_train, y_test = train_test_split(
X, target,
test_size=0.2,
random_state=42,
stratify=target # 保持类别比例
)
# 模型参数初始化 --------------------------------
theta = np.zeros(X_train.shape[1])
hyperparams = {
'learning_rate': 0.01,
'num_iterations': 1000
}
# 模型训练 --------------------------------
theta, cost_history = gradient_descent(
X_train, y_train, theta,
**hyperparams
)
# 模型评估 --------------------------------
y_prob = sigmoid(X_test @ theta)
y_pred = (y_prob >= 0.5).astype(int)
print("模型评估报告:")
print(classification_report(y_test, y_pred))
# 可视化部分 --------------------------------
# 损失曲线
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
ax1.plot(cost_history, color='darkorange')
ax1.set(xlabel='迭代次数', ylabel='损失值',
title='训练损失变化曲线')
ax1.grid(ls=':')
# 决策边界
x1_min, x1_max = X[:, 1].min()-1, X[:, 1].max()+1
x2_min, x2_max = X[:, 2].min()-1, X[:, 2].max()+1
xx1, xx2 = np.meshgrid(
np.linspace(x1_min, x1_max, 200),
np.linspace(x2_min, x2_max, 200)
)
# 生成预测网格
grid = np.c_[np.ones(xx1.size), xx1.ravel(), xx2.ravel()]
Z = sigmoid(grid @ theta).reshape(xx1.shape)
# 绘制散点
scatter_kwargs = {
'edgecolor': 'w',
's': 60,
'alpha': 0.8
}
ax2.scatter(*X[y==0, 1:].T, c='royalblue', label='正常', **scatter_kwargs)
ax2.scatter(*X[y==1, 1:].T, c='crimson', marker='^', label='逾期', **scatter_kwargs)
# 绘制决策边界
ax2.contour(xx1, xx2, Z, levels=[0.5], colors='black', linestyles='--')
ax2.set(xlabel='债务水平', ylabel='收入水平', title='决策边界可视化')
ax2.legend()
plt.tight_layout()
plt.show()
if __name__ == '__main__':
main()
```
主要改进点说明:
1. **代码结构优化**:
- 使用函数式结构,将逻辑封装到`main()`函数
- 添加类型注解和文档字符串
- 分离可视化逻辑到独立代码块
2. **健壮性增强**:
- 在损失计算中添加极小值ε防止log(0)
- 使用`@`运算符代替`dot()`提高可读性
- 数据集划分添加stratify参数保持类别平衡
3. **可视化增强**:
- 使用subplots创建组合图
- 添加更专业的样式设置
- 优化散点图的标记样式和透明度
4. **可维护性改进**:
- 集中管理超参数
- 使用解包操作传递参数
- 添加全局样式设置
使用时注意:
1. 根据实际数据分布决定是否启用特征标准化(取消注释对应代码)
2. 如果启用标准化,决策边界绘制需要相应调整坐标变换逻辑
3. 可通过调整hyperparams字典中的参数进行调优
建议后续改进方向:
- 添加学习率衰减机制
- 实现早停法防止过拟合
- 增加正则化项
- 添加特征工程步骤
- 实现不同的优化算法(如随机梯度下降)
阅读全文
相关推荐

















