Python机器学习基础教程 - 学习笔记
第1章 引言
核心思想: 机器学习是从数据中提取知识的方法,Python配合scikit-learn库提供了简单高效的机器学习实现途径。
1.1 机器学习是什么
是什么? 从数据中提取知识的交叉学科领域,也称为预测分析或统计学习。
为什么需要? 传统"if/else"规则系统存在两个缺点:
作用? 自动从数据中学习规律,用于推荐系统、图像识别、医疗诊断等。
1.2 为何选择Python
是什么? 机器学习领域最常用的编程语言之一。
为什么需要?
- 拥有丰富的科学计算库(NumPy、SciPy、pandas、matplotlib)
1.3 scikit-learn
是什么? Python中最广泛使用的机器学习库。
作用?
- 提供统一的API设计(fit/predict/transform)
对应代码:
# 基本导入import numpy as npimport matplotlib.pyplot as pltimport pandas as pdimport mglearn# 检查版本import sklearnprint("scikit-learn version: {}".format(sklearn.__version__))
1.4 第一个应用:鸢尾花分类
是什么? 经典的机器学习入门案例,根据鸢尾花的测量数据预测品种。
问题类型: 监督学习、分类问题、三分类
对应代码:
from sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom sklearn.neighbors import KNeighborsClassifier# 加载数据iris_dataset = load_iris()# 查看数据print("Keys of iris_dataset: \n{}".format(iris_dataset.keys()))print("Target names: {}".format(iris_dataset['target_names']))print("Feature names: \n{}".format(iris_dataset['feature_names']))print("Shape of data: {}".format(iris_dataset['data'].shape))# 划分训练集和测试集X_train, X_test, y_train, y_test = train_test_split( iris_dataset['data'], iris_dataset['target'], random_state=0)# 构建模型knn = KNeighborsClassifier(n_neighbors=1)knn.fit(X_train, y_train)# 预测与评估y_pred = knn.predict(X_test)print("Test set score: {:.2f}".format(knn.score(X_test, y_test)))
第2章 监督学习
核心思想: 监督学习从带标签数据中学习映射关系,关键是理解模型复杂度与泛化能力之间的权衡。
2.1 分类与回归
是什么?
对比表格:
2.2 泛化、过拟合与欠拟合
是什么?
为什么需要理解? 这是模型调参的核心依据。
[由此引出:模型复杂度选择]→ 通过训练集和测试集性能对比判断→ 使用交叉验证评估泛化能力→ 通过正则化控制模型复杂度
模型复杂度与精度的关系:
2.3 k近邻算法(k-NN)
是什么? 最简单的机器学习算法,通过找到最近的k个邻居进行预测。
为什么需要? 作为基准模型,易于理解和实现。
作用? 分类任务采用投票法,回归任务采用平均值。
局限?
[由此引出:特征缩放的重要性]→ 使用StandardScaler、MinMaxScaler等进行预处理
对应代码:
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor# k-NN分类clf = KNeighborsClassifier(n_neighbors=3)clf.fit(X_train, y_train)print("Test set accuracy: {:.2f}".format(clf.score(X_test, y_test)))# k-NN回归reg = KNeighborsRegressor(n_neighbors=3)reg.fit(X_train, y_train)print("Test set R^2: {:.2f}".format(reg.score(X_test, y_test)))
k值选择的影响:
2.4 线性模型
是什么? 通过特征的线性组合进行预测的模型。
为什么需要? 简单、快速、可解释性强,是首选的基准模型。
2.4.1 线性回归
公式:
对应代码:
from sklearn.linear_model import LinearRegression, Ridge, Lasso# 普通线性回归lr = LinearRegression()lr.fit(X_train, y_train)print("Coefficients: {}".format(lr.coef_))print("Intercept: {}".format(lr.intercept_))# Ridge回归(L2正则化)ridge = Ridge(alpha=1.0)ridge.fit(X_train, y_train)# Lasso回归(L1正则化)lasso = Lasso(alpha=1.0)lasso.fit(X_train, y_train)
2.4.2 线性分类
Logistic回归:
公式:
线性SVM:
对应代码:
from sklearn.linear_model import LogisticRegressionfrom sklearn.svm import LinearSVC# Logistic回归logreg = LogisticRegression(C=1.0)logreg.fit(X_train, y_train)# 线性SVMlinear_svm = LinearSVC(C=1.0)linear_svm.fit(X_train, y_train)
正则化对比:
正则化参数C:
2.5 朴素贝叶斯
是什么? 基于贝叶斯定理的分类器,假设特征之间相互独立。
为什么需要? 训练速度快,适用于高维稀疏数据(如文本)。
作用? 作为基准模型,常用于文本分类。
局限? 假设特征独立,实际中很少成立。
对应代码:
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB# 高斯朴素贝叶斯(连续特征)gnb = GaussianNB()gnb.fit(X_train, y_train)# 多项式朴素贝叶斯(计数特征)mnb = MultinomialNB(alpha=0.1)mnb.fit(X_train, y_train)
2.6 决策树
是什么? 通过一系列if/else问题对数据进行划分的模型。
为什么需要? 易于理解和可视化,不需要特征缩放。
作用? 分类和回归任务,特征重要性分析。
局限?
[由此引出:决策树集成方法]→ 随机森林→ 梯度提升树
对应代码:
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor# 决策树分类tree = DecisionTreeClassifier(max_depth=4, random_state=0)tree.fit(X_train, y_train)# 特征重要性print("Feature importances:\n{}".format(tree.feature_importances_))# 决策树回归tree_reg = DecisionTreeRegressor(max_depth=4)tree_reg.fit(X_train, y_train)
预剪枝参数:
2.7 决策树集成
是什么? 合并多个决策树构建更强大的模型。
为什么需要? 单棵决策树容易过拟合,集成可以提高泛化能力。
2.7.1 随机森林
是什么? 多棵决策树的集合,每棵树使用随机特征子集。
作用? 减少过拟合,提高泛化能力。
对应代码:
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor# 随机森林分类forest = RandomForestClassifier(n_estimators=100, random_state=0)forest.fit(X_train, y_train)# 随机森林回归forest_reg = RandomForestRegressor(n_estimators=100)forest_reg.fit(X_train, y_train)
2.7.2 梯度提升树
是什么? 串行训练多棵树,每棵树纠正前一棵树的错误。
作用? 通常比随机森林精度更高。
局限? 训练速度慢,需要更多参数调节。
对应代码:
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor# 梯度提升分类gbrt = GradientBoostingClassifier(random_state=0)gbrt.fit(X_train, y_train)# 梯度提升回归gbrt_reg = GradientBoostingRegressor(random_state=0)gbrt_reg.fit(X_train, y_train)
集成方法对比:
2.8 核支持向量机(SVM)
是什么? 通过核函数将数据映射到高维空间进行分类。
为什么需要? 处理非线性可分数据。
作用? 复杂决策边界,适用于中小规模数据集。
局限?
对应代码:
from sklearn.svm import SVC, SVR# SVM分类svm = SVC(kernel='rbf', C=1.0, gamma=0.1)svm.fit(X_train, y_train)# SVM回归svm_reg = SVR(kernel='rbf', C=1.0, gamma=0.1)svm_reg.fit(X_train, y_train)
核函数对比:
2.9 神经网络(深度学习)
是什么? 多层感知机,通过非线性激活函数学习复杂模式。
为什么需要? 可以学习非常复杂的非线性关系。
作用? 图像、文本等复杂数据的建模。
局限?
对应代码:
from sklearn.neural_network import MLPClassifier, MLPRegressor# 多层感知机分类mlp = MLPClassifier(hidden_layer_sizes=[100, 100], random_state=0, max_iter=1000)mlp.fit(X_train, y_train)# 多层感知机回归mlp_reg = MLPRegressor(hidden_layer_sizes=[100], random_state=0, max_iter=1000)mlp_reg.fit(X_train, y_train)
2.10 分类器的不确定度估计
是什么? 评估分类器对预测结果的置信程度。
为什么需要? 了解预测的可靠性。
方法:
- decision_function
- predict_proba
对应代码:
# 决策函数print("Decision function:\n{}".format(gbrt.decision_function(X_test)[:6]))# 预测概率print("Predicted probabilities:\n{}".format(gbrt.predict_proba(X_test)[:6]))
2.11 算法选择指南
对比表格:
第3章 无监督学习与预处理
核心思想: 无监督学习从无标签数据中提取知识,常用于数据探索和预处理。
3.1 预处理与缩放
是什么? 对特征进行缩放和变换,使其适合机器学习算法。
为什么需要? 许多算法对特征的尺度敏感(如SVM、神经网络、k-NN)。
对比表格:
对应代码:
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler# StandardScalerscaler = StandardScaler()X_scaled = scaler.fit_transform(X)# MinMaxScalerscaler = MinMaxScaler()X_scaled = scaler.fit_transform(X)# 重要:训练集和测试集使用相同的缩放scaler = StandardScaler().fit(X_train)X_train_scaled = scaler.transform(X_train)X_test_scaled = scaler.transform(X_test)
3.2 主成分分析(PCA)
是什么? 降维技术,找到数据中方差最大的方向。
为什么需要?
作用? 将数据投影到低维空间,保留最大方差。
局限? 只捕捉线性关系。
[由此引出:非线性降维方法]→ t-SNE用于可视化→ 核PCA捕捉非线性关系
对应代码:
from sklearn.decomposition import PCA# PCA降维pca = PCA(n_components=2)X_pca = pca.fit_transform(X)# 解释方差比print("Explained variance ratio: {}".format(pca.explained_variance_ratio_))# 选择保留90%方差的组件数pca = PCA(n_components=0.90, whiten=True)X_pca = pca.fit_transform(X_train)X_test_pca = pca.transform(X_test)
3.3 非负矩阵分解(NMF)
是什么? 将数据分解为非负分量的乘积。
为什么需要? 适用于非负数据(如图像、文本),结果具有可解释性。
作用? 提取数据的"部分"或"极值"。
局限? 要求数据必须非负。
对应代码:
from sklearn.decomposition import NMFnmf = NMF(n_components=15, random_state=0)nmf.fit(X_train)X_train_nmf = nmf.transform(X_train)X_test_nmf = nmf.transform(X_test)
3.4 t-SNE
是什么? 流形学习算法,用于高维数据的可视化。
为什么需要? PCA无法捕捉数据的非线性结构。
作用? 将高维数据映射到2D/3D进行可视化。
局限?
对应代码:
from sklearn.manifold import TSNEtsne = TSNE(random_state=42)X_tsne = tsne.fit_transform(X)plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y)plt.xlabel("t-SNE feature 0")plt.ylabel("t-SNE feature 1")
3.5 聚类
是什么? 将数据划分为不同的组,每组包含相似的样本。
为什么需要?
3.5.1 k均值聚类
是什么? 将数据划分为k个簇,使簇内样本到中心的距离最小。
作用? 快速聚类,易于实现。
局限?
对应代码:
from sklearn.cluster import KMeanskmeans = KMeans(n_clusters=3, random_state=0)kmeans.fit(X)# 簇分配labels = kmeans.labels_# 簇中心print("Cluster centers:\n{}".format(kmeans.cluster_centers_))# 预测新数据predictions = kmeans.predict(X_new)# 到簇中心的距离distance_features = kmeans.transform(X)
3.5.2 凝聚聚类
是什么? 自底向上合并最相似的簇,直到达到指定簇数。
作用? 提供层次化的聚类结果。
链接方式:
对应代码:
from sklearn.cluster import AgglomerativeClusteringagg = AgglomerativeClustering(n_clusters=3)assignment = agg.fit_predict(X)
3.5.3 DBSCAN
是什么? 基于密度的聚类,不需要预先指定簇数。
为什么需要? 可以识别任意形状的簇,找出异常点。
作用? 发现密集区域,将低密度点标记为噪声。
局限? 对参数eps和min_samples敏感。
对应代码:
from sklearn.cluster import DBSCANdbscan = DBSCAN(eps=0.5, min_samples=5)clusters = dbscan.fit_predict(X)# -1表示噪声点print("Cluster memberships:\n{}".format(clusters))
聚类算法对比:
第4章 数据表示与特征工程
核心思想: 数据的表示方式对模型性能有重大影响,特征工程是从原始数据中提取有效特征的过程。
4.1 分类变量
是什么? 取值为离散类别的特征。
为什么需要处理? 机器学习模型需要数值输入。
4.1.1 One-Hot编码
是什么? 将分类变量转换为二进制特征。
作用? 使分类变量可以被模型使用。
局限? 高基数的分类变量会产生大量特征。
对应代码:
from sklearn.preprocessing import OneHotEncoder# One-Hot编码enc = OneHotEncoder(sparse=False)enc.fit(X_categorical)X_onehot = enc.transform(X_categorical)# pandas的get_dummiesimport pandas as pdX_onehot = pd.get_dummies(df, columns=['category'])
4.2 分箱(离散化)
是什么? 将连续特征划分为若干个区间。
为什么需要? 帮助线性模型学习非线性关系。
作用? 将连续特征转换为有序类别。
对应代码:
from sklearn.preprocessing import KBinsDiscretizer# 分箱enc = KBinsDiscretizer(n_bins=10, encode='onehot-dense')X_binned = enc.fit_transform(X)# 结合原始特征和分箱特征X_combined = np.hstack([X, X_binned])
4.3 交互特征与多项式特征
是什么? 通过原始特征的组合创建新特征。
为什么需要? 帮助线性模型学习特征之间的关系。
作用? 增加模型的表达能力。
对应代码:
from sklearn.preprocessing import PolynomialFeatures# 多项式特征poly = PolynomialFeatures(degree=2, include_bias=False)X_poly = poly.fit_transform(X)print("X_poly.shape: {}".format(X_poly.shape))print("Polynomial feature names:\n{}".format(poly.get_feature_names()))
4.4 单变量非线性变换
是什么? 对特征应用数学函数(log、exp、sin等)。
为什么需要? 调节数据的相对比例,使其更接近高斯分布。
常用变换:
对应代码:
# log变换X_log = np.log(X + 1)# exp变换X_exp = np.exp(X)
4.5 自动化特征选择
是什么? 自动选择最有用的特征子集。
为什么需要? 减少特征数量,提高模型可解释性,减少过拟合。
4.5.1 单变量统计
是什么? 基于单变量统计检验选择特征。
对应代码:
from sklearn.feature_selection import SelectKBest, f_classif# 选择k个最佳特征select = SelectKBest(f_classif, k=10)X_selected = select.fit_transform(X_train, y_train)# 查看选中的特征mask = select.get_support()print(mask)
4.5.2 基于模型的特征选择
是什么? 使用监督学习模型评估特征重要性。
对应代码:
from sklearn.feature_selection import SelectFromModelfrom sklearn.ensemble import RandomForestClassifier# 使用随机森林选择特征select = SelectFromModel( RandomForestClassifier(n_estimators=100, random_state=42), threshold="median")select.fit(X_train, y_train)X_selected = select.transform(X_train)
4.5.3 迭代特征选择
是什么? 通过迭代构建模型来选择特征。
对应代码:
from sklearn.feature_selection import RFE# 递归特征消除select = RFE(RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=40)select.fit(X_train, y_train)X_selected = select.transform(X_train)
第5章 模型评估与改进
核心思想: 正确评估模型性能是机器学习成功的关键,交叉验证和网格搜索是模型选择和调参的核心工具。
5.1 交叉验证
是什么? 将数据多次划分训练集和验证集进行评估。
为什么需要? 更可靠地评估模型泛化能力。
作用? 减少数据划分的随机性对评估结果的影响。
对应代码:
from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold# 基本交叉验证scores = cross_val_score(LogisticRegression(), X, y, cv=5)print("Cross-validation scores: {}".format(scores))print("Average cross-validation score: {:.2f}".format(scores.mean()))# 分层k折交叉验证(分类问题)stratified_kfold = StratifiedKFold(n_splits=5)scores = cross_val_score(LogisticRegression(), X, y, cv=stratified_kfold)
交叉验证策略对比:
5.2 网格搜索
是什么? 系统地遍历多种参数组合,寻找最佳参数。
为什么需要? 自动化参数调优过程。
5.2.1 简单网格搜索
对应代码:
from sklearn.model_selection import GridSearchCV# 定义参数网格param_grid = {'C': [0.001, 0.01, 0.1, 1, 10, 100],'gamma': [0.001, 0.01, 0.1, 1, 10, 100]}# 网格搜索grid_search = GridSearchCV(SVC(), param_grid, cv=5)grid_search.fit(X_train, y_train)print("Best parameters: {}".format(grid_search.best_params_))print("Best cross-validation score: {:.2f}".format(grid_search.best_score_))print("Test set score: {:.2f}".format(grid_search.score(X_test, y_test)))
5.2.2 随机搜索
是什么? 随机采样参数组合,而非遍历所有组合。
为什么需要? 参数空间大时,网格搜索计算量过大。
对应代码:
from sklearn.model_selection import RandomizedSearchCVfrom scipy.stats import randint# 定义参数分布param_dist = {'C': randint(1, 100),'gamma': randint(1, 100)}# 随机搜索random_search = RandomizedSearchCV(SVC(), param_dist, n_iter=20, cv=5)random_search.fit(X_train, y_train)
网格搜索 vs 随机搜索:
5.3 评估指标
是什么? 量化模型性能的指标。
为什么需要? 不同问题需要不同的评估标准。
5.3.1 二分类指标
混淆矩阵:
重要指标:
准确率:
精确率:
召回率:F1分数:
对应代码:
from sklearn.metrics import (confusion_matrix, accuracy_score, precision_score, recall_score, f1_score, classification_report, roc_curve, auc)# 混淆矩阵confusion = confusion_matrix(y_test, pred)print("Confusion matrix:\n{}".format(confusion))# 分类报告print(classification_report(y_test, pred, target_names=["class 0", "class 1"]))# ROC曲线fpr, tpr, thresholds = roc_curve(y_test, model.predict_proba(X_test)[:, 1])plt.plot(fpr, tpr, label="ROC Curve")plt.xlabel("FPR")plt.ylabel("TPR (recall)")plt.legend(loc=4)
不平衡数据的评估:
5.3.2 多分类指标
对应代码:
# 多分类指标print(classification_report(y_test, pred))# 多分类混淆矩阵confusion = confusion_matrix(y_test, pred)
5.3.3 回归指标
对应代码:
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_errorprint("R^2: {:.2f}".format(r2_score(y_test, pred)))print("MSE: {:.2f}".format(mean_squared_error(y_test, pred)))print("MAE: {:.2f}".format(mean_absolute_error(y_test, pred)))
第6章 算法链与管道
核心思想: 管道将多个处理步骤串联起来,确保训练集和测试集的处理一致,简化工作流程。
6.1 为什么需要管道
是什么? 将数据预处理和模型训练封装为一个对象。
为什么需要?
[由此引出:管道的重要性]→ 在网格搜索中正确应用预处理→ 简化模型部署
6.2 构建管道
对应代码:
from sklearn.pipeline import Pipelinefrom sklearn.preprocessing import StandardScalerfrom sklearn.svm import SVC# 构建管道pipe = Pipeline([("scaler", StandardScaler()), ("svm", SVC())])# 拟合管道pipe.fit(X_train, y_train)# 预测print("Test set score: {:.2f}".format(pipe.score(X_test, y_test)))
6.3 在网格搜索中使用管道
对应代码:
# 定义参数网格param_grid = {'svm__C': [0.001, 0.01, 0.1, 1, 10, 100],'svm__gamma': [0.001, 0.01, 0.1, 1, 10, 100]}# 网格搜索grid = GridSearchCV(pipe, param_grid=param_grid, cv=5)grid.fit(X_train, y_train)print("Best cross-validation accuracy: {:.2f}".format(grid.best_score_))print("Test set score: {:.2f}".format(grid.score(X_test, y_test)))print("Best parameters: {}".format(grid.best_params_))
6.4 便捷的管道创建
对应代码:
from sklearn.pipeline import make_pipeline# 简化语法pipe = make_pipeline(StandardScaler(), SVC())# 查看步骤名称print("Pipeline steps:\n{}".format(pipe.steps))
6.5 访问管道属性
对应代码:
# 访问步骤属性pipe.fit(X_train, y_train)print("StandardScaler mean:\n{}".format(pipe.named_steps["scaler"].mean_))# 访问网格搜索中的属性print("Best estimator:\n{}".format(grid.best_estimator_))print("Scaler step:\n{}".format(grid.best_estimator_.named_steps["scaler"]))print("SVC step:\n{}".format(grid.best_estimator_.named_steps["svm"]))
第7章 处理文本数据
核心思想: 文本数据需要通过特征提取转换为数值表示,词袋模型是最基础也是最有效的方法之一。
7.1 词袋模型
是什么? 将文本表示为单词出现次数的向量。
为什么需要? 机器学习模型需要数值输入。
处理步骤:
- 分词
- 构建词表
- 编码
局限?
[由此引出:改进文本表示]→ 停用词过滤→ tf-idf加权→ n-gram特征
7.2 CountVectorizer
对应代码:
from sklearn.feature_extraction.text import CountVectorizer# 创建向量化器vect = CountVectorizer()vect.fit(text_train)# 转换文本X_train = vect.transform(text_train)print("X_train:\n{}".format(repr(X_train)))# 查看词表feature_names = vect.get_feature_names()print("Number of features: {}".format(len(feature_names)))# 限制词表大小vect = CountVectorizer(min_df=5) # 至少出现在5个文档中X_train = vect.fit_transform(text_train)
7.3 停用词
是什么? 常见但信息量低的词(如"the"、"is"、"and")。
为什么需要? 减少特征数量,去除噪声。
对应代码:
# 使用停用词vect = CountVectorizer(min_df=5, stop_words="english")X_train = vect.fit_transform(text_train)
7.4 tf-idf
是什么? 词频-逆文档频率,对常见词降权。
公式:
作用? 突出在特定文档中重要但在整个语料库中不常见的词。
对应代码:
from sklearn.feature_extraction.text import TfidfVectorizer# tf-idf向量化vect = TfidfVectorizer(min_df=5)X_train = vect.fit_transform(text_train)
7.5 n-gram
是什么? 考虑连续的n个单词作为特征。
为什么需要? 捕捉局部词序信息。
对应代码:
# 使用n-gramvect = CountVectorizer(ngram_range=(1, 2)) # 1-gram和2-gramX_train = vect.fit_transform(text_train)print("Vocabulary size: {}".format(len(vect.vocabulary_)))
7.6 文本分类完整示例
对应代码:
from sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.linear_model import LogisticRegressionfrom sklearn.pipeline import make_pipelinefrom sklearn.model_selection import GridSearchCV# 构建管道pipe = make_pipeline(TfidfVectorizer(min_df=5), LogisticRegression())# 参数网格param_grid = {'logisticregression__C': [0.001, 0.01, 0.1, 1, 10]}# 网格搜索grid = GridSearchCV(pipe, param_grid, cv=5)grid.fit(text_train, y_train)print("Best cross-validation score: {:.2f}".format(grid.best_score_))print("Test set score: {:.2f}".format(grid.score(text_test, y_test)))
总结:机器学习工作流程
1. 数据加载与探索 ↓2. 数据预处理(缺失值、编码、缩放) ↓3. 特征工程(选择、提取、变换) ↓4. 模型选择(交叉验证比较) ↓5. 超参数调优(网格搜索/随机搜索) ↓6. 模型评估(测试集) ↓7. 模型部署
核心概念速查表
监督学习算法
| | |
|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | n_estimators, learning_rate |
| | |
预处理
模型评估
交叉验证与调参