1. 为什么把“基础、方法、应用”拆成三块来学
《Python 人工智能 机器学习:基础、方法与应用(一)》这个系列名字看起来很朴实,实际上是我给自己重新整理的一套学习笔记。以前我也跟大多数人一样,打开教程就直接往深度学习框架里扎,结果越学越虚:代码能跑,道理不懂,换个数据就抓瞎。后来我彻底换了一条路线,把学习内容按“基础、方法、应用”三段切开,再往细里填,第一段写完后回头一看,效果比以前闷头学两个月都要好。
为什么这样拆?因为机器学习的知识是分层的。最底下一层是基础能力:Python 语法、数据处理、数学直觉;中间一层是方法体系:数据清洗、特征工程、模型训练、评估调优;最上面才谈得上应用:怎么把一个实际问题翻译成机器学习问题,再变成可交付的结果。大多数入门者的问题不是不够努力,而是三层混在一起学,今天学一个数学公式,明天学一个 API,后天又去看部署工具,互相打断,每一层都只学了个皮毛。
这个系列面向的人我也说清楚:刚学完 Python 基础、想进入人工智能领域的学生;工作中需要做一些数据分析和预测,但还没有系统接触机器学习的开发人员;以及那些和我一样,之前学过一堆碎片知识、想重新搭建知识体系的人。如果你已经能熟练训练深度学习模型,那这套笔记对你帮助不大,因为它解决的是“地基”问题,不是“登顶”问题。
1.1 基础层:Python 不是辅助工具,而是主战场
很多人有个误区,觉得 Python 在机器学习里就是个“调包工具”,会用import就行。实际不是这样。我见过太多人在模型代码里卡住,最后发现不是模型的问题,而是 Python 基本功不扎实——列表推导式写不利索、字典和集合用不熟、函数参数传递不清晰,导致做特征工程的时候频频返工。
在“基础”这一块,我给自己定的标准不是“看懂”,而是“不看文档能写完”。大致包括:
- 列表、元组、字典、集合的区别和各自的使用场景;
- 切片、推导式、
map/filter/zip等常用操作; - 函数默认参数、
*args/**kwargs的实际用途; - 文件读写和路径处理的坑,比如
encoding参数、中文字符串问题; - NumPy 里的数组索引、布尔掩码、广播机制。
其中 NumPy 的广播机制是最容易被忽略的。很多人在做批量数据计算时发现结果对不上,本质就是没搞懂数组维度和广播规则。在进入任何机器学习框架之前,我建议先花一周时间把 NumPy 刷扎实,不是背 API,而是把“形状”这个概念刻进脑海里。后面调模型、调数据的时候,你会感谢自己这块没偷懒。
1.2 方法层:模型不是越难越好,先学会和“笨模型”相处
这个系列叫“基础、方法与应用”,其中“方法”二字最容易被读成“高级算法”。我恰恰想反过来:方法首先是“怎么评价一件事做得好不好”,其次才是“用什么模型”。
我一直坚持一个观点:在拿到复杂模型之前,先老老实实把逻辑回归、决策树这些传统模型用熟。为什么?因为它们的中间过程可以被解释,你可以清楚地看到模型在哪个特征上给出了什么判断,从而反过来检查数据和特征有没有问题。深度学习模型是“黑盒”的,输入进去出来一个结果,如果你的数据本身就有问题,你再调网络结构也没用。
“方法”这部分我拆成四个模块:
- 数据质量检查:缺失值、重复值、异常值、数据分布;
- 特征工程:特征变换、编码、选择、构造;
- 模型与训练:训练集、验证集、测试集划分,交叉验证;
- 评估与诊断:准确率、召回率、AUC、学习曲线、误差分析。
这四块是任何机器学习项目都逃不掉的主干。你去看各种“AI 实战项目”,表面上是模型花哨,实际上大部分时间花在这些地方。这个系列的第一篇,我也把重心放在这条主线上,而不是直接上神经网络。
1.3 应用层:没有场景的模型只是数据玩具
“应用”这个词在标题里排最后,但它才是学习动机的来源。纯学理论很容易中途放弃,因为看不到这些东西解决了什么实际问题。
“应用”的第一层意思,是把手上的实际工作拆解成机器学习能解决的问题。比如某平台要做用户流失预警,需要判断哪些用户下个月可能不再使用;某商店要调整库存,需要预测下一周的销量;某审核系统要对文本风险做初步判断。这些问题没有标准答案可比,但它们的共同点是:有一个明确的预测目标,有一份可用的历史数据,有一个可量化的评价方式。
第二层意思,是学会把一个算法封装成一段可以被别人调用的流程。这个系列的第一篇不聊部署,不聊框架,但我会把一段训练代码写得结构清晰、可复用,保证你做完以后能把它迁移到自己的场景。应用不是最后单独学的一块,而是从第一天开始,你的每一个操作都要问一句:“这个动作,在真实项目里会出现在哪个环节?”
2. 实操第一步:工程化环境搭建与依赖隔离
说完了思路,开始动真格的。这个系列第一篇的实战内容,我从环境搭建开始写。这不是凑篇幅,而是这一关能挡住一批人。我在不同电脑上折腾过好几轮,见过太多人在pip install的时候把系统环境搞乱,最后连最基础的工具都跑不起来。
2.1 环境选择与隔离:别用系统自带 Python 做项目
原则只有一句话:每个项目一套 Python 环境,谁也别污染谁。系统自带 Python 或者直接从官网下的 Python 都适合做临时实验,不适合做项目,因为你今天给它装一个包,明天装另一个包,版本之间互相踩踏是迟早的事。
我目前最常用的两套方案,给你做个对比:
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
venv/virtualenv | 系统自带、轻量、无额外依赖 | 环境管理粒度较粗 | 小项目、脚本实验 |
conda/mamba | 包管理和环境管理一体化 | 体积大、初始安装慢 | 数据科学项目、复杂依赖 |
我的建议:如果你是第一次入机器学习,直接用venv就够了,不要一上来就装 Anaconda 全家桶。Anaconda 很方便,但它的底层依赖很重,有时候会给初学者带来更多困惑。如果你只是做纯 Python 项目,venv完全够用,创建环境只需要一行命令:
python -m venv ml_env创建好之后,激活环境的命令在 Windows 和 Linux/macOS 上不一样:
# Windows ml_env\Scripts\activate # Linux / macOS source ml_env/bin/activate激活后你会看到命令行前面多了一个(ml_env),这就表示你现在已经在这个独立环境里面了。后续安装的一切包都只属于这个环境,不会影响全局 Python。
2.2 依赖清单与版本陷阱:不要相信“最新版”三个字
机器学习相关的 Python 库更新频率非常快,但更新的速度并不同步。最典型的例子是 NumPy 和 Pandas,它们之间、它们和 scikit-learn 之间,有时候会因为版本差异出现无法预料的报错。我的经验是:在项目开始前写死版本号,而不是用pip install numpy这种模糊方式。
下面这个requirements.txt是我在第一篇里固定的版本组合,经过实际测试能稳定运行:
numpy==1.26.0 pandas==2.1.3 scikit-learn==1.3.2 matplotlib==3.8.0 jupyter==1.0.0安装命令很简单:
pip install -r requirements.txt为什么会踩版本坑?我举一个实际经历。有一次我在新环境里安装最新版 scikit-learn,结果代码里一个原本正常的 API 被标记为弃用,功能被挪到了别的模块里,报错信息还写得特别隐晦。我当时看着报错去搜索引擎找答案,翻了好几个页面才发现是版本行为不兼容问题。从那以后,我的所有项目文件第一件事就是锁版本。新项目的依赖可以选最新版,但有旧代码需要继续维护时,必须锁版本。
另外还有个容易被忽略的事情:不要把requirements.txt放在项目根目录就万事大吉。把它当成项目文件的一部分,和源码一起做版本管理。否则你过三个月再看这个项目,完全不知道当时依赖的是什么版本,复现会变成一场灾难。
2.3 数据准备的最小工具链
机器学习第一步不是建模型,而是“看数据”。这个环节最常用的几个库,其实上面已经装好了。
- Pandas:负责数据读取、清洗、聚合、变换,你 80% 的时间都在和它打交道;
- Matplotlib + Seaborn:负责可视化,画分布图、相关图、趋势图;
- NumPy:负责数值计算,是 Pandas 和 scikit-learn 的底层基础;
- Jupyter:负责交互式实验,写一段跑一段,直观看到中间结果。
我在做第一个实验时,第一步永远是执行下面这行代码:
df.head() df.info() df.describe()这三行代码能让你快速感知数据的规模、字段类型、缺失情况和取值范围。df.info()尤其有用,它能一次性告诉你每个字段的非空数量。如果发现某一列非空数量明显少于总行数,那就说明有缺失值问题,需要在建模前处理。
我会在后续的内容里单独讲 EDA(探索性数据分析)到底要“看”什么,这里只强调一点:先看数据,再做假设。有人喜欢一拿到数据就急着跑模型,跑完发现准确率很高,但根本没有检查数据有没有标签泄漏。这是新手最容易掉进去的坑,后面我会专门展开。
3. 核心方法拆解与第一个机器学习实验
环境准备好之后,正式进入方法层。我选了一个非常经典的问题类型作为第一篇的主线任务:二分类问题。具体场景是某平台要预测“用户在未来一个月是否会停止使用服务”,用一份脱敏后的历史行为数据做训练,数据集大概有 3 万行记录,20 个字段。
之所以选二分类,因为它覆盖了机器学习最核心的流程:数据拆分、特征处理、模型训练、结果评估、经验复盘。这套流程你吃透了,后面再做多分类、回归、聚类都会轻松很多。
3.1 问题定义与评估方案:先别写代码,先定标准
很多人上手就写model.fit(x, y),这是不对的。我建议在写任何代码之前,先用几分钟想清楚三件事:预测目标是什么、用什么指标衡量好、这个模型会用在什么场景。
对于“用户流失预测”这个场景,我定的目标是:根据用户过去 30 天的行为特征,判断未来 30 天内是否会流失。这是一个二分类问题,标签只有两个值:1 表示流失,0 表示不流失。
接下来说指标。初学者最容易盯着的指标是“准确率”,但准确率在这个场景下会有误导性。假设这个平台整体流失率只有 10%,你哪怕不做任何训练,直接预测所有人都不流失,准确率也能到 90%。这显然不代表你的模型有价值。
所以我选了三个指标组合,缺一不可:
- 召回率:真实流失的用户中,模型成功找出多少。这个指标在流失预警里要优先保证,宁可误报一些,也别漏掉真正要流失的人;
- 精确率:模型预测为流失的人中,实际流失的比例。它和召回率天然有矛盾,需要平衡;
- AUC:综合评估模型把正样本排在负样本前面的能力。它不依赖某一个阈值,可以更全局地判断模型好坏。
在实际代码里,我直接以 AUC 作为模型选择和调参的优化目标。原因是 AUC 对类别不平衡不那么敏感,比准确率更能体现模型真实的排序能力。
3.2 数据划分与预处理:这一步直接影响模型是否“作弊”
这里我先说一个新手最常犯的错:在划分训练集和测试集之前,就用了全量数据去做标准化、填充缺失值等操作。
你必须记住:测试集要模拟“未来未知数据”。你既然在训练阶段不该看见测试集的信息,那么所有从数据中学出来的统计量——均值、方差、中位数、众数——都必须只从训练集里计算。如果有人先对全量数据做StandardScaler,再拆分数据,那测试集的分布信息已经泄露给了模型,最后拿到的验证分数会虚高。这在真实场景里会让你误以为模型很强,上线后真实效果立刻打回原形。
正确的流程是:
- 先拆分数据:训练集、验证集、测试集;
- 在训练集上计算预处理的统计量(比如均值、方差、缺失值填充数值);
- 用训练集上得到的统计量去变换测试集;
- 再进入模型训练。
我用train_test_split做划分,固定随机种子保证每次实验复现:
from sklearn.model_selection import train_test_split # X 是特征矩阵,y 是标签列 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y )这里有两个细节。第一个是stratify=y,它能让训练集和测试集中的正负样本比例保持一致,避免随机划分导致某一个集合里流失用户占比过少或过多。第二个是random_state=42,这个数字没有特殊含义,只是固定了随机过程,保证每次运行出来的实验结果是同一个,你才能在不同模型之间进行比较。
3.3 模型选择与代码实现:先跑通一条“笨”基准线
模型选择上,我的建议是先用一个最简单的模型建立基准线,比如逻辑回归。它能让你快速判断:“如果我用很朴素的方式做,能到什么水平?”如果后续换成更复杂的模型仍然没有明显提升,那问题多半不在模型复杂度,而在数据或特征。
下面这段代码是我第一篇实验里的核心训练流程,使用了 scikit-learn 的 Pipeline,把预处理和模型封装到一起:
from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression model = Pipeline(steps=[ ('imputer', SimpleImputer(strategy='median')), ('scaler', StandardScaler()), ('clf', LogisticRegression(max_iter=1000, random_state=42)) ]) model.fit(X_train, y_train)Pipeline 的好处是可以一次性对训练集执行整个流程,之后对测试集做同一个流程的预测。这样就不会再犯“漏掉对测试集做处理”的错。在第一次实验里,我得到的验证 AUC 大约是 0.81,这个成绩作为基准线已经可以说明“数据和特征存在可学习的信息”。
我必须强调:不要上来就调参、换模型。先有一个可以直接跑的基准线,然后再思考“能不能更好”。模型的天花板由数据决定,调参只是逼近这个天花板。
3.4 超参数调优与交叉验证:把评估做得“稳”一点
只划分一次训练集和测试集是不太够的,因为这一次划分可能是运气好,也可能是运气差。更稳妥的做法是交叉验证:把训练数据切成几份,轮流用其中一份做验证,其余做训练,最后把结果平均。
我用GridSearchCV对随机森林的少数几个关键参数做搜索:
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [100, 200], 'max_depth': [None, 10, 20], 'min_samples_leaf': [1, 5] } rf = RandomForestClassifier(random_state=42) grid_search = GridSearchCV( rf, param_grid, scoring='roc_auc', cv=5, n_jobs=-1 ) grid_search.fit(X_train, y_train) print(grid_search.best_params_)注意我搜索的参数很少,只选择了三个。为什么?因为参数组合数是乘法关系:两个n_estimators乘以三个max_depth乘以两个min_samples_leaf,就是 12 个组合,每个都要跑 5 折交叉验证,也就是 60 次训练。如果参数太多,时间成本会迅速上升。新手调参很容易一上来就铺一个大网格,结果跑一晚上也出不来。
最终我选择了随机森林作为这个实验的主力模型,因为它对特征数值范围不敏感,能处理非线性关系,而且在这个规模的数据集上训练速度很快。调完以后,测试集 AUC 从逻辑回归的 0.81 提升到了 0.86。提升幅度不算巨大,但足以说明特征里确实有可以被更复杂模型捕获的信号。
4. 常见问题排查与避坑实录
这一章我想把真正踩过的坑写出来,都是文档里不会写的东西。你能少走一个是一个。
4.1 数据泄漏:最隐蔽、最致命的错误
我在第一节里提过数据泄漏,这里展开讲。数据泄漏简单说就是模型在训练时看到了“未来信息”或者“测试集信息”,导致训练时表现很好,实际部署后效果崩盘。
泄漏有三个常见来源:
第一种是在预处理时用了全量数据统计量。解决方案是用 Pipeline,或者手动保证所有fit操作只在训练集上执行。
第二种是把未来信息当特征。比如预测用户下个月是否流失,特征里包含了“这个月是否已经点击了退款链接”,这就是泄漏。因为退款行为发生在你观察期之后,建模时要格外小心时间窗口。
第三种是在做特征选择时用了全量数据。比如你用SelectKBest在拆分前就选择了若干特征,这一步本质也是数据泄漏。特征选择器同样只能放在 Pipeline 里,在训练集上做拟合。
我自己的做法是每次写代码前先在纸上画一条时间线,把所有特征的时间点标出来,和预测目标的时间点比一比。超过目标时间点的信息,一律不进特征列表。
4.2 模型分数高、实际没用的几种情况
新手最容易产生的错觉是:“测试集准确率 95%,我的模型很成功。”这个结论不成立,常见原因有三个:
一是类别极度不均衡。如果 95% 的样本是负样本,你全预测负类,准确率也有 95%。所以我一再强调要看 AUC、混淆矩阵,而不是只盯准确率。
二是数据分布和真实场景不一致。比如你的训练数据都是某一段时间的数据,上线后发现用户行为模式变了,模型就会失效。这在行业里叫“分布漂移”。缓解的办法是持续收集新数据、定期重训练模型,这个我在后续内容里会再展开。
三是评估指标用错了。分类问题用回归问题的指标、排序问题用准确率指标,都会造成误判。方法层里最重要的一件事,就是先搞清楚“我这个问题的正确评价方式是什么”,再开始训练。
4.3 版本兼容与随机性:复现是实验的生命线
前面我提过锁版本,这里再补充一个点:除了 Python 包版本,还要锁定随机种子。很多模型的训练过程带有随机性,比如随机森林抽样、神经网络初始化。如果你不固定随机种子,同一次实验跑两遍结果不同,你就没法判断指标提升到底来自模型改进,还是来自随机运气。
我推荐在项目的入口文件顶部统一设置随机种子。如果只用到 scikit-learn,可以直接在模型参数里传入random_state。如果后面用到了 NumPy 和自带随机模块,最好再加一行:
import random import numpy as np random.seed(42) np.random.seed(42)这些代码看起来不起眼,但真到论文复现或者工作交接时,能省很多麻烦。
我还遇到过一种很典型的报错:ModuleNotFoundError: No module named 'sklearn'。有时候明明环境里有 scikit-learn,却依然报错。排查思路是先确认你在哪个环境里运行:
which python pip list第一条命令看当前用的是哪个 Python 解释器,第二条看当前环境装了哪些包。如果发现解释器路径不对,多半是环境没有激活,或者 IDE 里指定的解释器选错了。
5. 下一阶段“应用”扩展与学习建议
第一篇内容写到这里,已经覆盖了“基础”和“方法”的大半条主线。“应用”的部分我会用接下来的篇幅继续填,但在这个节点上,我想先说清楚“应用”到底要从哪里开始。
5.1 “应用”不等于部署,先把问题映射做对
一说“应用”,很多人想到的是 API 接口、前后端、模型上线。这些确实很重要,但不是第一篇应该考虑的。在能部署一个可靠的模型之前,你必须先回答一个问题:这个模型解决的是分类、回归、聚类还是排序问题?
这其实是一个很多人会做错的前提判断。我见过有朋友拿到一个库存预测的需求,上来就套分类模型的框架,最后发现要预测的是连续数值,才改回归。类似的事情浪费了不少时间。
我的建议是先做一个“问题映射表”:
| 业务需求 | 机器学习问题类型 | 常用评估指标 |
|---|---|---|
| 判断用户是否会流失 | 二分类 | AUC、召回率、精确率 |
| 预测接下来一周的销量 | 回归 | RMSE、MAE |
| 把客户自动分成几类 | 无监督聚类 | 轮廓系数、业务可解释性 |
| 给搜索结果排个顺序 | 排序学习 | NDCG、MRR |
把业务问题还原成数学问题,是整个应用环节的第一步。这一步没有天然的标准答案,需要靠项目经验积累,但每做一次,都会比上一次更熟练。
5.2 后续系列的内容规划
既然这个系列叫“基础、方法与应用(一)”,那么后面必然有(二)、(三)。
我的初步计划是这样:第二篇重点讲特征工程与数据清洗的进阶技巧,包括类别特征编码方式对比、缺失值填充的取舍、时间窗口特征构造、特征重要性分析;第三篇开始进入经典机器学习模型的高级话题,比如决策树与集成学习的原理、偏差与方差权衡、模型解释性工具。
至于深度学习,我不打算太早放进这个系列。原因是:如果你的传统机器学习基础不扎实,直接学深度学习会变成“只会调框架的工具人”。先把每一个传统模型的假设、优缺点、适用场景吃透,深度学习层的学习效率会翻倍。
5.3 给新手的节奏建议
最后给一个可执行的时间节奏,我按每天 2 小时左右来规划:
- 第一周:Python 基础查漏补缺 + 环境搭建,完成《Python 人工智能 机器学习:基础、方法与应用(一)》的环境部分;
- 第二周:Pandas 和 Matplotlib 熟练使用,完成一个数据集的数据清洗和可视化;
- 第三周:跟着这篇笔记从零跑通逻辑回归和随机森林,理解训练、验证、测试的完整流程;
- 第四周:换一个不同场景的数据集,重新走一遍流程,试着把指标提升 0.02 以上。
我特别不建议这段时间同时学太多新东西。机器学习入门最怕的不是慢,而是“什么都想学,最后什么都不会”。把一条主流程走通十遍,比走十条主流程各走一遍要有用得多。
我自己在处理第一篇实验时最深的体会是:一个模型跑得通、指标也不错,这只是起点;真正值钱的,是你有没有能力解释“为什么是这个结果”以及“如果数据变了,它还会不会有效”。带着这两个问题去学后面的内容,你每一段代码都会练得比单纯跑通更有意义。
下一篇我会直接挑一个真实结构更复杂的数据集,把特征工程从“做出来”变成“做得有理有据”,到时候见。