逻辑回归预测癌症:代码逐行深度拆解与核心概念扫盲
·
📖 导读:
欢迎来到机器学习实战!本篇博客是结合章节宏观架构与微观代码实战的融合产物。
- 宏观上:你将看到这段代码如何对应“线性模型战区”和“模型评估战区”。
- 微观上:我们将对每一行代码进行“手术刀式”的拆解,重点攻克
new_x_train、fit_transform等新手最容易卡壳的概念。- 目标:不仅让你能跑通代码,更要让你理解数据是如何一步步变身,最终让模型学会“看病”的。
文章目录
🗺️ 一、本章定位:我们在学什么?
在之前的《章节全景指南》中,我们提到了机器学习的四大战区。本文件 01_逻辑回归API_预测癌症案例.py 正是第一战区(线性模型)的开篇之作,同时也贯穿了第四战区(模型评估)。
- 核心算法:**逻辑回归 **(Logistic Regression)。
- 误区纠正:名字带“回归”,实则是分类算法之王。它不预测具体数值(如房价),而是预测概率(患癌的可能性是 0 还是 1)。
- 核心流程:
- 数据清洗:处理脏数据(
?缺失值)。 - 特征工程:标准化(Standardization),让数据公平。
- 模型训练:让机器从历史病例中学习规律。
- 模型评估:用准确率打分,但要注意其局限性。
- 数据清洗:处理脏数据(
📦 第二部分:导包(准备工具箱)
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
💡 变量与工具详解
这里没有定义新变量,而是引入了工具库。想象你要做一道菜,这是去厨房拿工具:
- **
pandas**(别名pd):Excel 增强版。用来读取表格数据(CSV),处理行列。它是数据处理的“瑞士军刀”。 - **
numpy**(别名np):数学计算核心。用来处理数字运算,特别是这里的np.nan(代表“非数字/缺失值”)。 train_test_split:切分器。用来把数据切成“练习题”和“考试题”,防止模型作弊。StandardScaler:统一度量衡的工具。用来做标准化处理,这是线性模型成功的关键前置步骤。LogisticRegression:主厨(算法模型)。虽然名字带“回归”,但它是用来做二分类(判断良性还是恶性)的。accuracy_score:阅卷老师。用来给模型的考试结果打分(计算准确率)。
📂 第三部分:读取数据(拿到原材料)
# 1.读取文件获取数据
data = pd.read_csv('data/breast-cancer-wisconsin.csv', sep=',')
print(data.shape, data.ndim) # 形状:(699, 11) 维度:2
🔍 变量详解:data
- 是什么:
data是一个 DataFrame(可以理解为 Python 里的 Excel 表格对象)。 - 从哪来:通过
pd.read_csv读取硬盘上的文件得到。 - 内容:包含了 699 行(699 个病人),11 列(11 项指标,如 ID、半径、纹理、诊断结果等)。
- 状态:此时的数据是原始数据(Raw Data)。
- 隐患:里面可能包含无效字符(如
?),直接喂给模型会报错,必须清洗。
- 隐患:里面可能包含无效字符(如
🧹 第四部分:数据预处理(清洗原材料)
# 2.0 注意:数据中有"?"无效字符,需要先转换为 numpy 中的 nan,然后使用 dropna() 删除或者 fillna() 填充
new_data = data.replace('?', np.nan).dropna()
print(new_data.shape, new_data.ndim) # 形状:(683, 11) 维度:2
🔍 变量详解:new_data
- 是什么:清洗后的 DataFrame。
- 怎么变的(两步走):
data.replace('?', np.nan):翻译。把表格里所有的问号?(人类看得懂,机器看不懂)替换成计算机认可的缺失值标记NaN(Not a Number)。.dropna():丢弃。直接删除任何包含NaN的行。
- 结果变化:
- 行数从 699 变成了 683。
- 含义:说明有 16 个病人的数据不完整(有问号)。为了保证模型学习的严谨性,我们忍痛割爱,把这 16 行直接删掉了。
- 列数保持 11 列不变。
- 为什么这么做:机器学习模型是数学公式,无法处理
?这种符号,也无法处理空缺。要么填补(用平均值),要么删除。这里为了简单直接,选择了删除。
✂️ 第五部分:拆分特征与标签(区分题目和答案)
# 2.1 分别获取特征和标签
# 拓展:iloc 格式 : 数据.iloc[行索引,列索引]
x = new_data.iloc[:, 1:-1]
y = new_data.iloc[:, -1]
print(x.shape, x.ndim) # 形状:(683, 9) 维度:2
print(y.shape, y.ndim) # 形状:(683,) 维度:1
🔍 变量详解:x 和 y
这是机器学习中最重要的两个变量命名习惯,贯穿所有算法:
-
**
x**(小写):**特征矩阵 **(Features) —— “题目”- 含义:包含了病人的各项检查指标(半径、纹理、周长等),是模型用来判断的依据。
- 切片逻辑
iloc[:, 1:-1]:::取所有行(683 个病人)。1:-1:取第 2 列 到 倒数第 2 列。- 去掉第 1 列:通常是 ID 号(如 1001, 1002),这对病情预测毫无用处,属于噪声。
- 去掉最后 1 列:那是答案(诊断结果),训练时如果让模型看到答案,它就学会作弊了。
- 形状:
(683, 9)。表示 683 个样本,每个样本有 9 个特征。
-
**
y**(小写):**标签向量 **(Labels) —— “标准答案”- 含义:包含了病人的确诊结果(2=良性,4=恶性)。
- 切片逻辑
iloc[:, -1]:取所有行的最后一列。 - 形状:
(683,)。表示 683 个答案,是一维数组(向量)。
🎲 第六部分:划分训练集与测试集(模拟考试)
# 2.2 使用 train_test_split() 按比例切割成 4 部分
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=666)
🔍 变量详解:四大金刚
这一行代码把之前的 x 和 y 切成了 4 份。注意变量名变成了大写,这是 sklearn 的惯例,代表经过划分后的数据集。
- **
X_train**(训练集特征):- 含义:“平时的练习题(题目部分)”。
- 大小:占总数据的 80% (约 546 行)。
- 用途:喂给模型,让模型从中学习规律。
- **
y_train**(训练集标签):- 含义:“平时的练习题(答案部分)”。
- 用途:告诉模型,上面的题目对应的正确答案是什么。
- **
X_test**(测试集特征):- 含义:“期末考试题(题目部分)”。
- 大小:占总数据的 20% (约 137 行)。
- 用途:训练时严禁使用!等模型学完后,用它来考模型,检验泛化能力。
- **
y_test**(测试集标签):- 含义:“期末考试题(答案部分/保密卷)”。
- 用途:老师(程序员)拿着它来核对模型的预测结果,计算分数。
- 关键参数解读:
test_size=0.2:切 20% 出来当考题,80% 当练习。random_state=666:随机种子(定海神针)。- 作用:保证每次运行代码,切分出来的题目都一模一样。
- 意义:如果没有它,每次运行结果都可能不同,你就无法判断是模型改进了,还是仅仅因为运气好分到了简单的数据。
⚖️ 第七部分:特征标准化(统一度量衡)⭐核心难点
# 3.特征处理 (标准化)
ss = StandardScaler() # 初始化标准化工具
new_x_train = ss.fit_transform(X_train)
new_x_test = ss.transform(X_test)
❓ 灵魂拷问:new_x_train 到底是什么?
很多教程只说“这是标准化后的数据”,但不解释过程和区别。这里是本文件的最高频考点。
1. 为什么要搞个 new_x_train?
- 原始问题:在
X_train中,不同特征的数值范围(量纲)差异巨大。- 比如“细胞核半径”可能是
10.0到30.0。 - 比如“细胞核周长”可能是
60.0到180.0。 - 后果:如果不处理,数值大的特征(周长)在计算距离或权重时会占据主导地位,数值小的特征(半径)会被忽略。就像让大象和蚂蚁拔河,大象必胜,但这不公平,也不科学。
- 比如“细胞核半径”可能是
- 目标:把所有特征转换成均值为 0,标准差为 1的标准正态分布。让所有特征站在同一起跑线上。
2. new_x_train 是怎么生成的? (fit_transform)
- 代码:
ss.fit_transform(X_train) - 动作分解(两步合一):
- **
fit(学习/制定标准):计算器先扫描X_train,算出每一列的平均值 **(Mean) 和 **标准差 **(Std)。- 注意:这一步只在训练集上做!相当于制定了“评分标准”。
- **
transform**(转换/执行标准):利用刚才算出的均值和标准差,把X_train里的每一个数字都进行公式转换:
- **
- 结果:
new_x_train是一个新的 NumPy 数组(不再是 DataFrame)。里面的数字变了,变成了以 0 为中心的小数(如 -1.2, 0.5, 2.1 等),但行列顺序和X_train保持一致。 - 含义:这是“已经按标准处理好的练习题”,可以直接交给模型学习了。
3. 为什么还有一个 new_x_test?且只用 transform?
- 代码:
ss.transform(X_test) - 动作:
- 注意:这里没有
fit!绝对不能写fit_transform(X_test)。 - 原因:测试集模拟的是未来的未知数据。我们不能让测试集自己算平均值(那是作弊,叫数据泄露)。
- 做法:必须强行使用训练集 (
X_train) 制定的标准(之前fit学到的均值和方差)来转换测试集。
- 注意:这里没有
- 结果:
new_x_test是“用同一套标准处理好的考试题”。
✅ 总结对比表
| 变量名 | 来源 | 处理方式 | 含义 |
|---|---|---|---|
X_train |
原始切分 | 无 | 原始的练习题(数值范围不统一,不能直接喂给线性模型) |
new_x_train |
X_train |
fit + transform | 标准化的练习题(既学习了标准,又完成了转换,模型真正吃进去的数据) |
X_test |
原始切分 | 无 | 原始的考试题 |
new_x_test |
X_test |
仅 transform | 标准化的考试题(沿用练习题的标准进行转换,用于公平考试) |
🤖 第八部分:创建与训练模型(老师上课)
# 4.创建模型
lr_model = LogisticRegression() # 实例化模型
# 5.模型训练
lr_model.fit(new_x_train, y_train)
🔍 过程详解
lr_model = LogisticRegression():- 创建了一个空的逻辑回归模型对象。此时它脑子里空空如也,内部参数(权重和偏置)都是初始值。
lr_model.fit(new_x_train, y_train):- 输入:把**处理好的练习题 **(
new_x_train) 和 **练习答案 **(y_train) 喂给它。 - 内部发生的事:模型开始疯狂计算(通常使用梯度下降法),调整内部的权重参数,试图找到一条数学曲线(决策边界),能最好地把“良性”和“恶性”分开。
- 结果:训练结束后,
lr_model这个对象内部就保存了学到的规律。它现在是一个**“Trained Model **(训练好的模型),具备了预测能力。
- 输入:把**处理好的练习题 **(
📝 第九部分:预测与评估(期末考试与打分)
# 6.模型预测和评估:准确率
y_pred = lr_model.predict(new_x_test) # 1.先预测
print(y_pred) # 打印模型猜的结果
print(y_test.tolist()) # 打印真实答案
print(f"准确率:{accuracy_score(y_test, y_pred)}") # 2.再计算
print('---------------------------------------------------')
print(f"准确率:{lr_model.score(new_x_test, y_test)}") # 底层也是先预测再计算
🔍 变量详解:y_pred
- 是什么:模型的预测结果数组。
- 怎么来的:
lr_model.predict(new_x_test)。- 模型拿着**处理好的考试题 **(
new_x_test),运用之前学到的规律,对每一个病人进行判断。 - 输出结果是一串数字(如
[2, 4, 2, 2, 4, ...]),代表模型认为每个病人是良性 (2) 还是恶性 (4)。
- 模型拿着**处理好的考试题 **(
- 用途:用来和真实答案
y_test做对比。
🌟 核心概念:准确率 (Accuracy)
- 计算逻辑:

- 代码实现:
accuracy_score(y_test, y_pred):y_test:真实答案(老师手里的标准卷)。y_pred:模型的答案(学生交的卷)。- 函数会逐个比对,统计相同的个数,除以总数。
lr_model.score(new_x_test, y_test):- 这是模型对象自带的快捷方法。
- 内部流程:它其实偷偷先执行了
predict(new_x_test)得到预测值,然后再调用accuracy_score计算。所以两行代码输出的结果是一模一样的。
⚠️ 重要提醒:准确率的局限性(进阶思考)
虽然代码输出了准确率(比如 0.96),但在医疗场景下要警惕:
- 场景:如果有 100 个人,只有 5 个癌症(样本不平衡)。
- 摆烂模型:如果模型全部预测“没病”,它能对 95 个,准确率高达 95%。
- 后果:虽然分数高,但 5 个癌症病人全被漏诊了(这是最严重的错误,假阴性)。
- 结论:准确率是一个基础指标。在后续课程中,你还需要关注:
- **召回率 **(Recall):能不能把所有的病人都找出来?(宁可错杀,不可放过)
- **精确率 **(Precision):预测是癌症的人里,真的有多少是癌症?
- AUC 曲线:综合评估模型好坏的金标准。
🎯 全文数据流向总结图
为了让你彻底明白 new_x_train 在整个流程中的位置,请看这个数据变身记:
- 原始文件 (
csv)
⬇️read_csv data(脏表格,含?)
⬇️replace+dropna(清洗)new_data(干净表格)
⬇️iloc切片 (分离题目和答案)x(题目),y(答案)
⬇️train_test_split(划分考场)X_train(原始练习题),X_test(原始考试题)
⬇️StandardScaler(关键变身点:统一度量衡)new_x_train(标准化练习题 👉 喂给模型训练)new_x_test(标准化考试题 👉 喂给模型考试)
⬇️model.predicty_pred(模型给出的猜测)
⬇️ 对比y_test- 准确率 (最终得分)
🚀 下一步行动建议
恭喜你!你已经彻底拿下了逻辑回归的完整流程。
- 复习建议:重点回顾
new_x_train的生成过程(fitvstransform),这是面试和实战中最容易出错的地方。 - 扩展思考:如果数据中缺失值很多,
dropna()会丢失大量信息,有没有更好的填充方法?(提示:均值填充、中位数填充)。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)