机器学习作为人工智能的核心分支,已广泛应用于金融、医疗、互联网等多个领域。本教程将通过一个客户流失预测实战项目,带大家从零掌握 Python 机器学习的完整流程。项目采用公开的电信客户数据集,通过数据预处理、特征工程、模型训练与评估等步骤,构建能够预测客户是否会流失的机器学习模型。
教程适合 Python 入门者、机器学习新手,无需深厚的数学基础,只需掌握 Python 基础语法和 Pandas、NumPy 等库的基本使用。全程搭配完整代码、逻辑流程图、可视化图表和 Prompt 示例,帮助大家理解每个环节的核心原理与实操技巧,最终达到 “能复现、能修改、能迁移” 的学习目标。
基于电信客户的历史数据(如套餐类型、消费金额、使用时长等),构建机器学习模型,预测客户未来是否会停止使用公司服务(即客户流失)。通过该模型,企业可提前识别高流失风险客户,针对性地制定留存策略,降低运营成本。
| 工具 / 库 | 核心作用 |
|---|---|
| Python 3.9+ | 项目开发核心编程语言 |
| Pandas | 数据读取、清洗、预处理 |
| NumPy | 数值计算、数组处理 |
| Matplotlib/Seaborn | 数据可视化、特征分布与模型结果展示 |
| Scikit-learn(sklearn) | 特征工程、模型训练、评估与调优 |
| Jupyter Notebook | 代码编写、运行与结果展示(推荐开发环境) |
| Mermaid | 绘制项目流程与模型逻辑流程图 |
graph TD
A[项目启动] --> B[数据加载与探索性分析EDA]
B --> C[数据预处理]
C --> C1[缺失值处理]
C --> C2[异常值检测与处理]
C --> C3[分类变量编码]
C --> C4[数据归一化/标准化]
C1 --> D[特征工程]
C2 --> D
C3 --> D
C4 --> D
D --> D1[特征筛选]
D --> D2[特征衍生]
D1 --> E[数据集划分]
D2 --> E
E --> F[模型选择与训练]
F --> F1[逻辑回归]
F --> F2[决策树]
F --> F3[随机森林]
F --> F4[XGBoost]
F1 --> G[模型评估]
F2 --> G
F3 --> G
F4 --> G
G --> G1[准确率/精确率/召回率/F1分数]
G --> G2[ROC曲线/AUC值]
G --> G3[混淆矩阵]
G1 --> H[模型调优]
G2 --> H
G3 --> H
H --> H1[网格搜索GridSearchCV]
H --> H2[随机搜索RandomizedSearchCV]
H1 --> I[最优模型部署与预测]
H2 --> I
I --> J[项目总结与优化方向]

推荐安装 Python 3.9 或 3.10 版本,可通过Python 官网下载,安装时勾选 “Add Python to PATH”。
打开命令行(Windows CMD/Mac Terminal),执行以下命令安装所需库:
bash
# 基础数据处理库
pip install pandas numpy
# 可视化库
pip install matplotlib seaborn
# 机器学习库
pip install scikit-learn xgboost
# 开发环境(可选,推荐)
pip install jupyter notebook
命令行输入以下命令,自动在浏览器中打开 Jupyter 界面:
bash
jupyter notebook
点击界面右上角 “New”→“Python 3”,创建新的代码文件(.ipynb),即可开始编写代码。
本项目使用公开的电信客户流失数据集(Telco Customer Churn),包含 7043 条客户记录和 21 个特征字段,目标变量为 “Churn”(Yes/No,表示客户是否流失)。
python
运行
import pandas as pd
# 读取在线数据集
url = "https://raw.githubusercontent.com/IBM/telco-customer-churn-on-icp4d/master/data/Telco-Customer-Churn.csv"
df = pd.read_csv(url)
# 查看数据前5行
print("数据前5行:")
print(df.head())
# 查看数据基本信息
print("
数据基本信息:")
print(df.info())
# 查看数据统计描述
print("
数据统计描述:")
print(df.describe())
| 字段名称 | 字段类型 | 说明 |
|---|---|---|
| customerID | 字符串 | 客户唯一 ID(无预测价值) |
| gender | 分类变量 | 性别(Male/Female) |
| SeniorCitizen | 数值变量 | 是否为老年人(0 = 否,1 = 是) |
| Partner | 分类变量 | 是否有配偶(Yes/No) |
| Dependents | 分类变量 | 是否有家属(Yes/No) |
| tenure | 数值变量 | 客户在公司的使用时长(月) |
| PhoneService | 分类变量 | 是否开通电话服务(Yes/No) |
| MultipleLines | 分类变量 | 是否开通多条线路(Yes/No/No phone service) |
| InternetService | 分类变量 | 互联网服务类型(DSL/Fiber/No) |
| OnlineSecurity | 分类变量 | 是否开通在线安全服务(Yes/No/No internet service) |
| OnlineBackup | 分类变量 | 是否开通在线备份服务(Yes/No/No internet service) |
| DeviceProtection | 分类变量 | 是否开通设备保护服务(Yes/No/No internet service) |
| TechSupport | 分类变量 | 是否开通技术支持服务(Yes/No/No internet service) |
| StreamingTV | 分类变量 | 是否开通流媒体电视(Yes/No/No internet service) |
| StreamingMovies | 分类变量 | 是否开通流媒体电影(Yes/No/No internet service) |
| Contract | 分类变量 | 合同类型(Month-to-month/One year/Two year) |
| PaperlessBilling | 分类变量 | 是否使用电子账单(Yes/No) |
| PaymentMethod | 分类变量 | 支付方式(Electronic check/Mailed check/Bank transfer (automatic)/Credit card (automatic)) |
| MonthlyCharges | 数值变量 | 月消费金额(美元) |
| TotalCharges | 字符串 | 总消费金额(美元,需转换为数值型) |
| Churn | 分类变量 | 目标变量,是否流失(Yes/No) |
探索性数据分析(EDA)是机器学习项目的关键步骤,目的是了解数据分布、特征与目标变量的关系,发现数据中的规律和问题,为后续预处理和特征工程提供依据。
python
运行
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体(避免中文乱码)
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # Mac
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 读取数据(若已读取可跳过)
url = "https://raw.githubusercontent.com/IBM/telco-customer-churn-on-icp4d/master/data/Telco-Customer-Churn.csv"
df = pd.read_csv(url)
# 检测缺失值(统计每列缺失值数量)
missing_values = df.isnull().sum()
print("各字段缺失值数量:")
print(missing_values[missing_values > 0]) # 只显示有缺失值的字段
# 可视化缺失值(若有)
if missing_values.sum() > 0:
plt.figure(figsize=(12, 6))
sns.heatmap(df.isnull(), cbar=False, cmap='viridis', yticklabels=False)
plt.title('缺失值分布热力图')
plt.savefig('missing_values_heatmap.png', dpi=300, bbox_inches='tight')
plt.show()
结果说明:本数据集无明显缺失值,但需注意
TotalCharges字段为字符串类型,可能存在空字符串(需后续处理)。
目标变量
Churn的分布的是否均衡,会影响模型训练效果(若严重不均衡,需进行采样处理)。
python
运行
# 统计目标变量分布
churn_distribution = df['Churn'].value_counts()
print("目标变量分布:")
print(churn_distribution)
print(f"流失率:{churn_distribution['Yes'] / len(df):.2%}")
# 可视化目标变量分布(饼图)
plt.figure(figsize=(8, 6))
plt.pie(churn_distribution, labels=churn_distribution.index, autopct='%1.1f%%',
colors=['#66b3ff', '#ff9999'], startangle=90, explode=(0, 0.1))
plt.title('客户流失分布饼图')
plt.axis('equal') # 保证饼图为正圆形
plt.savefig('churn_distribution_pie.png', dpi=300, bbox_inches='tight')
plt.show()
# 可视化目标变量分布(柱状图)
plt.figure(figsize=(8, 6))
sns.countplot(x='Churn', data=df, palette=['#66b3ff', '#ff9999'])
plt.title('客户流失分布柱状图')
plt.xlabel('是否流失')
plt.ylabel('客户数量')
plt.savefig('churn_distribution_bar.png', dpi=300, bbox_inches='tight')
plt.show()
结果说明:数据集中非流失客户(No)5174 人,流失客户(Yes)1869 人,流失率约 26.5%,属于轻度不均衡数据,暂时无需采样处理,后续可观察模型效果再调整。
数值型特征包括
tenure(使用时长)、
MonthlyCharges(月消费)、
TotalCharges(总消费),需分析其分布规律及与目标变量的关系。
python
运行
# 转换TotalCharges为数值型(空字符串转为NaN)
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
# 选择数值型特征
numeric_features = ['tenure', 'MonthlyCharges', 'TotalCharges']
# 绘制直方图(查看分布)
plt.figure(figsize=(15, 5))
for i, feature in enumerate(numeric_features):
plt.subplot(1, 3, i+1)
sns.histplot(df[feature].dropna(), kde=True, color='#66b3ff')
plt.title(f'{feature}分布直方图')
plt.xlabel(feature)
plt.ylabel('频数')
plt.tight_layout()
plt.savefig('numeric_features_hist.png', dpi=300, bbox_inches='tight')
plt.show()
# 绘制箱线图(检测异常值)
plt.figure(figsize=(15, 5))
for i, feature in enumerate(numeric_features):
plt.subplot(1, 3, i+1)
sns.boxplot(y=df[feature].dropna(), color='#ff9999')
plt.title(f'{feature}箱线图(异常值检测)')
plt.ylabel(feature)
plt.tight_layout()
plt.savefig('numeric_features_boxplot.png', dpi=300, bbox_inches='tight')
plt.show()
结果说明:
tenure(使用时长):分布呈双峰,大量客户使用时长接近 0 个月(新客户)或 72 个月(长期客户);
MonthlyCharges(月消费):分布较均匀,无明显异常值;
TotalCharges(总消费):与使用时长正相关,分布呈右偏,无明显异常值。
python
运行
# 绘制小提琴图(特征分布与流失的关系)
plt.figure(figsize=(15, 5))
for i, feature in enumerate(numeric_features):
plt.subplot(1, 3, i+1)
sns.violinplot(x='Churn', y=feature, data=df.dropna(subset=[feature]),
palette=['#66b3ff', '#ff9999'])
plt.title(f'{feature}与客户流失的关系')
plt.xlabel('是否流失')
plt.ylabel(feature)
plt.tight_layout()
plt.savefig('numeric_features_churn_violin.png', dpi=300, bbox_inches='tight')
plt.show()
# 绘制箱线图(特征中位数与流失的关系)
plt.figure(figsize=(15, 5))
for i, feature in enumerate(numeric_features):
plt.subplot(1, 3, i+1)
sns.boxplot(x='Churn', y=feature, data=df.dropna(subset=[feature]),
palette=['#66b3ff', '#ff9999'])
plt.title(f'{feature}与客户流失的关系')
plt.xlabel('是否流失')
plt.ylabel(feature)
plt.tight_layout()
plt.savefig('numeric_features_churn_boxplot.png', dpi=300, bbox_inches='tight')
plt.show()
# 计算数值型特征与流失的相关性
numeric_df = df[numeric_features + ['Churn']].copy()
numeric_df['Churn'] = numeric_df['Churn'].map({'Yes': 1, 'No': 0}) # 目标变量编码为0/1
correlation = numeric_df.corr()['Churn'].sort_values(ascending=False)
print("数值型特征与客户流失的相关性:")
print(correlation)
结果说明:
tenure(使用时长):与流失呈强负相关(相关系数 - 0.35),使用时长越短,客户流失风险越高;
MonthlyCharges(月消费):与流失呈正相关(相关系数 0.23),月消费越高,客户流失风险越高;
TotalCharges(总消费):与流失呈负相关(相关系数 - 0.19),总消费越低,客户流失风险越高。