你好,未来的数据魔法师!你是否曾对机器如何看懂图片、预测股价、甚至诊断疾病感到好奇?这一切的背后,很多时候都离不开机器学习中的一个核心分支——监督学习 (Supervised Learning)。今天,我们将一起揭开它的神秘面纱,并通过大量代码实例,让你亲手体验构建预测模型的乐趣!
想象一下,你正在教一个孩子看图识字。你会给他看一张苹果的图片,并告诉他:“这是苹果”。然后是一张香蕉的图片,告诉他:“这是香蕉”。
监督学习与此类似。我们给机器提供一堆“问题”和对应的“正确答案”。这里的“问题”就是我们的输入数据 (Features),而“正确答案”就是标签 (Labels)。模型的目标就是学习从输入数据到标签之间的映射关系。当模型“学成”之后,我们就可以给它新的、它没见过的问题,让它预测出答案。
核心思想: 利用带有明确标签的训练数据,训练出一个模型,使其能够对新的、未见过的数据进行预测。
监督学习主要解决两大类问题:
接下来,让我们用代码深入探索这两个任务!
线性回归是最简单也最经典的回归算法之一。它假设输入特征和输出标签之间存在线性关系。
场景: 假设我们有一组关于房屋面积 (平方米) 和其对应售价 (万元) 的数据,我们想建立一个模型,通过房屋面积来预测售价。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import train_test_split
# 1. 准备数据 (特征 X: 房屋面积, 标签 y: 售价)
# 假设我们有以下数据
X_data = np.array([50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150]).reshape(-1, 1) # 面积 (平方米)
y_data = np.array([150, 180, 210, 250, 280, 310, 330, 370, 400, 430, 450]) # 售价 (万元)
# 2. 数据可视化 (可选,但推荐)
plt.figure(figsize=(8, 5))
plt.scatter(X_data, y_data, color='blue', label='实际数据')
plt.xlabel("房屋面积 (平方米)")
plt.ylabel("售价 (万元)")
plt.title("房屋面积与售价关系")
plt.legend()
plt.grid(True)
plt.show()
# 3. 划分训练集和测试集 (在数据量较少时,有时会跳过这一步,但规范做法是划分)
# 如果数据量大,通常会划分。这里为了演示完整流程,我们还是划分一下。
# 如果不划分,可以直接用 X_data 和 y_data 进行训练。
X_train, X_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.2, random_state=42)
print(f"训练集样本数: X_train: {X_train.shape[0]}, y_train: {y_train.shape[0]}")
print(f"测试集样本数: X_test: {X_test.shape[0]}, y_test: {y_test.shape[0]}")
# 4. 创建并训练线性回归模型
model = LinearRegression()
model.fit(X_train, y_train) # 核心步骤:模型学习
# 5. 查看模型参数
print(f"\n模型截距 (w0): {model.intercept_:.2f}")
print(f"模型系数 (w1 - 面积的权重): {model.coef_[0]:.2f}")
# 线性回归方程可以表示为: y = w0 + w1*X
# 6. 使用模型进行预测
y_pred_train = model.predict(X_train)
y_pred_test = model.predict(X_test)
# 7. 评估模型
# 对于训练集
mse_train = mean_squared_error(y_train, y_pred_train)
r2_train = r2_score(y_train, y_pred_train)
print(f"\n训练集评估:")
print(f" 均方误差 (MSE): {mse_train:.2f}")
print(f" R² 分数: {r2_train:.2f}")
# 对于测试集
mse_test = mean_squared_error(y_test, y_pred_test)
r2_test = r2_score(y_test, y_pred_test)
print(f"\n测试集评估:")
print(f" 均方误差 (MSE): {mse_test:.2f}")
print(f" R² 分数: {r2_test:.2f}") # R² 越接近1,模型拟合越好
# 8. 结果可视化
plt.figure(figsize=(10, 6))
plt.scatter(X_data, y_data, color='blue', label='实际数据', s=50) # 所有原始数据点
plt.plot(X_train, y_pred_train, color='green', linestyle='--', linewidth=2, label='训练集拟合线')
plt.plot(X_test, y_pred_test, color='red', linewidth=2, label='测试集预测线') # 通常将所有X排序后画一条完整的线
# 为了更清晰地画出拟合线,我们可以对所有X进行预测
X_plot = np.sort(X_data, axis=0)
y_plot_pred = model.predict(X_plot)
plt.plot(X_plot, y_plot_pred, color='orange', linewidth=3, label='整体拟合线')
plt.scatter(X_test, y_test, color='purple', marker='x', s=100, label='测试集实际值')
plt.scatter(X_test, y_pred_test, color='cyan', marker='o', s=100, facecolors='none', label='测试集预测值')
plt.xlabel("房屋面积 (平方米)")
plt.ylabel("售价 (万元)")
plt.title("线性回归模型拟合结果")
plt.legend()
plt.grid(True)
plt.show()
# 9. 使用模型预测新数据
new_area = np.array([[160]]) # 注意:输入需要是二维数组
predicted_price = model.predict(new_area)
print(f"\n预测面积为 {new_area[0][0]} 平方米的房屋售价: {predicted_price[0]:.2f} 万元")
代码解读:
numpy
: 用于高效的数值计算,特别是数组操作。matplotlib.pyplot
: 用于数据可视化。sklearn.linear_model.LinearRegression
: Scikit-learn库提供的线性回归模型。sklearn.metrics.mean_squared_error
: 均方误差,衡量预测值与真实值差异的常用指标。sklearn.metrics.r2_score
: R²分数 (决定系数),表示模型对数据变异性的解释程度,值越接近1越好。sklearn.model_selection.train_test_split
: 用于将数据集划分为训练集和测试集,避免模型在见过的数据上表现好,但在新数据上表现差(过拟合)。model.fit(X_train, y_train)
: 这是训练模型的关键步骤,模型通过学习X_train
和y_train
之间的关系来调整内部参数。model.predict(X_test)
: 使用训练好的模型对新数据X_test
进行预测。其他回归算法: 除了线性回归,还有多项式回归、决策树回归、支持向量回归 (SVR)、K近邻回归等,它们可以处理更复杂的非线性关系。
虽然名字里带“回归”,但逻辑回归实际上是一种非常经典的二分类算法(也可以扩展到多分类)。它通过Sigmoid函数将线性回归的输出映射到(0,1)之间,表示属于某个类别的概率。
场景: 根据学生的学习时长和睡眠时长,预测其是否能通过考试 (通过/不通过)。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler # 特征缩放,对逻辑回归和KNN等算法很重要
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import seaborn as sns # 用于绘制更美观的混淆矩阵
# 1. 准备数据 (特征 X: [学习时长, 睡眠时长], 标签 y: 是否通过考试 [0:未通过, 1:通过])
# 假设我们有以下数据
X_data_clf = np.array([
[2, 7], [3, 8], [5, 6], [6, 7], [8, 5], [1, 4], [2, 5], [4, 3], [3, 6], [7, 4]
])
y_data_clf = np.array([1, 1, 1, 1, 1, 0, 0, 0, 0, 0]) # 1代表通过,0代表未通过
# 2. 数据可视化 (简单示意)
plt.figure(figsize=(8,5))
plt.scatter(X_data_clf[y_data_clf == 1][:, 0], X_data_clf[y_data_clf == 1][:, 1], color='green', marker='o', label='通过 (1)')
plt.scatter(X_data_clf[y_data_clf == 0][:, 0], X_data_clf[y_data_clf == 0][:, 1], color='red', marker='x', label='未通过 (0)')
plt.xlabel("学习时长 (小时)")
plt.ylabel("睡眠时长 (小时)")
plt.title("学习、睡眠与考试结果")
plt.legend()
plt.grid(True)
plt.show()
# 3. 划分训练集和测试集
X_train_clf, X_test_clf, y_train_clf, y_test_clf = train_test_split(X_data_clf, y_data_clf, test_size=0.3, random_state=42, stratify=y_data_clf)
# stratify=y_data_clf 确保训练集和测试集中类别比例与原始数据相似
print(f"训练集样本数: X_train_clf: {X_train_clf.shape[0]}, y_train_clf: {y_train_clf.shape[0]}")
print(f"测试集样本数: X_test_clf: {X_test_clf.shape[0]}, y_test_clf: {y_test_clf.shape[0]}")
# 4. 特征缩放 (非常重要,尤其是对于依赖距离或梯度的算法)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train_clf) # 在训练集上fit并transform
X_test_scaled = scaler.transform(X_test_clf) # 在测试集上只transform,使用训练集的参数
# 5. 创建并训练逻辑回归模型
log_reg_model = LogisticRegression(solver='liblinear', random_state=42) # solver='liblinear' 适用于小数据集
log_reg_model.fit(X_train_scaled, y_train_clf)
# 6. 使用模型进行预测
y_pred_log_reg = log_reg_model.predict(X_test_scaled)
y_pred_proba_log_reg = log_reg_model.predict_proba(X_test_scaled) # 获取属于各类的概率
print("\n逻辑回归预测结果:")
for i in range(len(y_test_clf)):
print(f" 真实值: {y_test_clf[i]}, 预测值: {y_pred_log_reg[i]}, 预测为'通过'的概率: {y_pred_proba_log_reg[i][1]:.2f}")
# 7. 评估模型
accuracy_log_reg = accuracy_score(y_test_clf, y_pred_log_reg)
conf_matrix_log_reg = confusion_matrix(y_test_clf, y_pred_log_reg)
class_report_log_reg = classification_report(y_test_clf, y_pred_log_reg, zero_division=0)
print(f"\n逻辑回归模型评估:")
print(f" 准确率 (Accuracy): {accuracy_log_reg:.2f}")
print(f" 混淆矩阵:\n{conf_matrix_log_reg}")
print(f" 分类报告:\n{class_report_log_reg}")
# 混淆矩阵可视化
plt.figure(figsize=(6,4))
sns.heatmap(conf_matrix_log_reg, annot=True, fmt='d', cmap='Blues', xticklabels=['未通过', '通过'], yticklabels=['未通过', '通过'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title('逻辑回归 混淆矩阵')
plt.show()
代码解读:
sklearn.preprocessing.StandardScaler
: 用于特征标准化,将数据按其属性(列)分别进行标准化,即均值为0,标准差为1。这对于逻辑回归、SVM、KNN等算法非常重要,可以加速收敛并提高性能。LogisticRegression(solver='liblinear', random_state=42)
: solver
参数指定了优化算法,liblinear
适合小数据集。model.predict_proba()
: 返回每个样本属于各个类别的概率。对于二分类,它返回一个 N x 2 的数组,每行两个值分别表示属于类别0和类别1的概率。accuracy_score
: 准确率,即正确预测的样本数占总样本数的比例。confusion_matrix
: 混淆矩阵,清晰展示了模型在每个类别上的预测表现(真阳性、假阳性、真阴性、假阴性)。classification_report
: 提供了更全面的分类指标,如精确率 (Precision)、召回率 (Recall)、F1分数 (F1-score) 等。KNN是一种非常直观的分类(和回归)算法。它的核心思想是“物以类聚,人以群分”。对于一个新的样本,KNN会查看它在特征空间中距离最近的K个训练样本,然后根据这K个邻居的类别来决定新样本的类别(多数表决)。
场景: 同样使用上面的学生考试数据。
# (接上文的X_train_scaled, X_test_scaled, y_train_clf, y_test_clf)
from sklearn.neighbors import KNeighborsClassifier
# 1. 创建并训练KNN模型
k = 3 # 选择邻居数量,通常选择奇数避免平票
knn_model = KNeighborsClassifier(n_neighbors=k)
knn_model.fit(X_train_scaled, y_train_clf)
# 2. 使用模型进行预测
y_pred_knn = knn_model.predict(X_test_scaled)
y_pred_proba_knn = knn_model.predict_proba(X_test_scaled)
print("\nKNN预测结果 (k=3):")
for i in range(len(y_test_clf)):
print(f" 真实值: {y_test_clf[i]}, 预测值: {y_pred_knn[i]}, 预测为'通过'的概率: {y_pred_proba_knn[i][1]:.2f}")
# 3. 评估模型
accuracy_knn = accuracy_score(y_test_clf, y_pred_knn)
conf_matrix_knn = confusion_matrix(y_test_clf, y_pred_knn)
class_report_knn = classification_report(y_test_clf, y_pred_knn, zero_division=0)
print(f"\nKNN模型评估 (k={k}):")
print(f" 准确率 (Accuracy): {accuracy_knn:.2f}")
print(f" 混淆矩阵:\n{conf_matrix_knn}")
print(f" 分类报告:\n{class_report_knn}")
# 混淆矩阵可视化
plt.figure(figsize=(6,4))
sns.heatmap(conf_matrix_knn, annot=True, fmt='d', cmap='Greens', xticklabels=['未通过', '通过'], yticklabels=['未通过', '通过'])
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title(f'KNN (k={k}) 混淆矩阵')
plt.show()
# 尝试不同的k值 (可选,用于调优)
# for k_val in [1, 3, 5]:
# knn_temp = KNeighborsClassifier(n_neighbors=k_val)
# knn_temp.fit(X_train_scaled, y_train_clf)
# y_pred_temp = knn_temp.predict(X_test_scaled)
# acc_temp = accuracy_score(y_test_clf, y_pred_temp)
# print(f"k={k_val}, Accuracy: {acc_temp:.2f}")
代码解读:
KNeighborsClassifier(n_neighbors=k)
: n_neighbors
参数就是K值,即选择多少个最近邻居。K值的选择对模型性能有很大影响,需要通过交叉验证等方法来确定最优K值。其他分类算法: 支持向量机 (SVM)、决策树、随机森林、朴素贝叶斯、神经网络等,它们各有优缺点,适用于不同类型的数据和问题。
一个典型的监督学习项目通常遵循以下步骤:
没有万能的算法。选择算法时需要考虑:
经验法则:
监督学习虽然强大,但也面临一些挑战:
尽管如此,监督学习仍然是机器学习领域应用最广泛、最成功的分支之一。随着数据量的爆炸式增长和算法的不断进步,它将在更多领域大放异彩。
希望这篇充满代码的博客能让你对监督学习有一个更直观和深入的理解!现在,打开你的编辑器,亲自尝试运行这些代码,修改参数,探索不同的数据集,开启你的数据科学之旅吧!记住,实践是学习的最佳途径。
如果你有任何问题或想法,欢迎在评论区交流!