概念
- 机器学习起源于数据的统计和数学建模。
- 机器学习的基本理念是使用过去观测到的数据来预测未知的结果或值。
简单来说
让计算机像人类一样,从经验(数据)中学习规律(建立模型),然后用这个规律应对新情况(预测/决策)
举栗子
结合 下头业务测试小黑子 的发帖,根据水贴时间点和水帖数量,来预测他在某年某个月的水帖数量。
- 模型是机器学习的核心组件,是我们最终要尝试构建的内容。
- 机器学习基于数学和统计学,因此通常会使用数学术语来思考机器学习模型。
- 本质上,机器学习模型是一种软件应用程序,它封装一个函数用于根据一个或多个输入值计算输出值。
- 定义该函数的过程称为训练。
训练和推理涉及的步骤
➊ 训练数据由过去的观测值组成。通常用变量 x 表示特征,y 表示标签 (预测的"答案")。由于一个观测值(如一条数据)往往包含多个特征,因此 x 实际上是一个向量(即由多个值组成的数组),形式如:x = [x₁, x₂, x₃, …]。
如小黑子发水帖的场景中,目标是训练一个根据月份预测小黑子发帖数量的模型。
当月(天数、气温、小黑子 A 股收益、是否出差等)是特征 x,每月水出的帖子数量是标签 y。
➋ 将对数据应用某种算法,以尝试确定特征与标签之间的关系,然后泛化这种关系,以便针对 x 执行计算来计算 y。 使用的具体算法取决于要解决的预测问题的类型(回归问题还是分类问题),但基本原则是尝试将一个函数拟合到数据,其中的特征值可用于计算标签
➌ 算法的结果会被封装为函数 f,用 y = f(x) 表示,即一个模型
➍ 完成训练阶段后,训练的模型可用于推理。 机器学习模型本质上是一个封装了数学函数及其参数的软件对象。 输入一组特征值,得到预测结果作为输出。 由于模型的输出是函数计算的预测值而不是观测值,因此输出设置为 ŷ
任务类型 | 输出形式 | 例子 | 典型算法 | 评估指标 |
---|---|---|---|---|
回归 | 连续数值(如 2.5) | 预测发帖数、回复数 | 线性回归、决策树回归 | MSE, MAE, R² |
二元分类 | 两个类别(如 0/1) | 小黑子类型检测、唯技术论人格诊断 | 逻辑回归、SVM | 准确率, F1, ROC-AUC |
多类分类 | 多个类别(如 A/B/C/D) | 手写数字识别、帖子主题分类 | 随机森林、神经网络、K 近邻 | 准确率, 混淆矩阵 |
训练一个模型是基于单个特征值 (x) 预测数值标签 (y),用某种方式获得了小黑子的发帖简单数据
时间月份 | 次数(x) | 水帖数量 (y ) |
---|---|---|
2024 年 5 月 | 1 | 2 |
2024 年 6 月 | 2 | 5 |
2024 年 7 月 | 3 | 2 |
2024 年 8 月 | 4 | 2 |
2024 年 9 月 | 5 | 4 |
2024 年 10 月 | 6 | 2 |
2024 年 11 月 | 7 | 2 |
2024 年 12 月 | 8 | 1 |
2025 年 1 月 | 9 | 5 |
2025 年 2 月 | 10 | 2 |
2025 年 3 月 | 11 | 2 |
2025 年 4 月 | 12 | 2 |
2025 年 5 月 | 13 | 4 |
# 基于监督式学习的简单线性回归模型
# 使用statsmodels库实现线性回归模型,分析并预测水帖数量
import numpy as np # 用于数值计算
import pandas as pd # 用于数据处理
import matplotlib.pyplot as plt # 用于数据可视化
import statsmodels.api as sm # 用于统计建模
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 数据准备 - 小黑子的训练数据
months = ['2024年5月', '2024年6月', '2024年7月', '2024年8月', '2024年9月',
'2024年10月', '2024年11月', '2024年12月', '2025年1月',
'2025年2月', '2025年3月', '2025年4月', '2025年5月']
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]) # 时间序列(自变量)
y = np.array([2, 5, 2, 2, 4, 2, 2, 1, 5, 2, 2, 2, 4]) # 水帖数量(因变量)
# 创建DataFrame便于数据查看
df = pd.DataFrame({
'时间月份': months,
'序号': x,
'水帖数量': y
})
# 打印数据概览
print("数据概览:")
print(df)
# 计算基本统计量
print("\n基本统计量:")
print(df['水帖数量'].describe()) #describe() 是pandas的一个强大函数,能一次性提供多个统计指标。
print(f"\n数据均值: {np.mean(y):.2f}") #均值反映了数据的中心趋势,表示所有月份水帖数量的平均水平
print(f"数据中位数: {np.median(y):.2f}") #中位数是将数据排序后位于中间的值,不受极端值影响,更能反映数据的典型值
print(f"数据标准差: {np.std(y):.2f}") # 标准差衡量数据的离散程度,值越大表示数据波动越大,分布越分散
print(f"数据最大值: {np.max(y)}")
print(f"数据最小值: {np.min(y)}")
print(f"数据范围: {np.max(y) - np.min(y)}") # 范围反映了数据的总体跨度,是衡量数据变异性的简单指标
plt.figure(figsize=(10, 6))
plt.scatter(x, y, color='blue', alpha=0.7)
plt.title('水帖数量随时间变化散点图')
plt.xlabel('时间序列')
plt.ylabel('水帖数量')
plt.grid(True, linestyle='--', alpha=0.7)
# 添加月份标签
for i, month in enumerate(months):
plt.annotate(month, (x[i], y[i]), textcoords="offset points",
xytext=(0,10), ha='center')
plt.tight_layout()
plt.savefig('水帖数量散点图.png', dpi=300)
plt.show()
# 数据拆分 - 将数据分为训练集(10个)和测试集(3个)
# 设置随机种子以确保结果可重现
X_train, X_test, y_train, y_test, train_indices, test_indices = train_test_split(
x, y, np.arange(len(x)), test_size=3, random_state=42)
# 获取训练集和测试集对应的月份
train_months = [months[i] for i in train_indices]
test_months = [months[i] for i in test_indices]
print("\n数据拆分结果:")
print(f"训练集大小: {len(X_train)} 个样本")
print(f"测试集大小: {len(X_test)} 个样本")
print("\n训练集月份:", train_months)
print("测试集月份:", test_months)
其实这里用人眼看大致能预测了
# 将训练数据转换为适合statsmodels的格式
X_train_sm = X_train.reshape(-1, 1) # 将一维数组转为二维
X_train_sm = sm.add_constant(X_train_sm) # 添加常数项(截距)
# 训练线性回归模型
model = sm.OLS(y_train, X_train_sm).fit() # OLS = 普通最小二乘法
# 获取模型参数
intercept = model.params[0] # 截距,是回归线在y轴上的交点 它为模型提供了"基准值",所有预测都是在此基础上加上斜率与x的乘积
slope = model.params[1] # 斜率
#- 正斜率:表示随着时间增加,水帖数量平均呈上升趋势
#- 负斜率:表示随着时间增加,水帖数量平均呈下降趋势
#- 斜率接近0:表示时间对水帖数量几乎没有影响
print(f"\n线性回归方程: y = {intercept:.4f} + ({slope:.4f}) * x")
print(f"这表示:")
print(f"- 初始水帖数量(截距): {intercept:.4f}")
print(f"- 每增加一个月,水帖数量平均变化: {slope:.4f}")
# 打印模型摘要
print("\n线性回归模型摘要:")
print(model.summary())
- 平均绝对误差 (MAE): 衡量预测值与实际值之间的平均绝对差异。它通过取所有预测错误的绝对值(不考虑正负号)并计算其平均值来获得
- 均方误差 (MSE):是对每个预测误差进行平方后求得的平均值。这使得较大的误差对结果的影响更大,因为它放大了这些误差。
- 均方根误差 (RMSE):是 MSE 的平方根,它将误差转换回原始单位(如水帖的数量),以便更容易理解模型的准确性
- 决定系数 (R²):R² 描述了模型所解释的变异占总变异的比例。它可以看作是衡量模型拟合优度的一个指标,值越接近 1 表示模型越好
# 使用模型进行预测(对训练数据)
y_train_pred = model.predict(X_train_sm)
# 计算训练集上的均方误差(MSE)和决定系数(R²)
mse_train = np.mean((y_train - y_train_pred) ** 2)
r2_train = model.rsquared
# 计算每个训练点的预测值和实际值的差异
residuals_train = y_train - y_train_pred
print("\n训练集上每个月的预测值与实际值比较:")
for i, month in enumerate(train_months):
print(f"{month}: 实际值={y_train[i]}, 预测值={y_train_pred[i]:.2f}, 残差={residuals_train[i]:.2f}")
print(f"\n训练集评估:")
print(f"训练集均方误差 (MSE): {mse_train:.4f} - 越小表示模型预测越准确")
print(f"训练集决定系数 (R²): {r2_train:.4f} - 接近1表示模型拟合度高,接近0表示拟合度低")
# 在测试集上评估模型
X_test_sm = X_test.reshape(-1, 1)
X_test_sm = sm.add_constant(X_test_sm)
y_test_pred = model.predict(X_test_sm)
# 计算测试集上的评估指标
mse_test = np.mean((y_test - y_test_pred) ** 2)
# 手动计算R²,因为model.rsquared只适用于训练集
ss_total = np.sum((y_test - np.mean(y_test)) ** 2)
ss_residual = np.sum((y_test - y_test_pred) ** 2)
r2_test = 1 - (ss_residual / ss_total) if ss_total != 0 else 0
print("\n测试集上每个月的预测值与实际值比较:")
for i, month in enumerate(test_months):
print(f"{month}: 实际值={y_test[i]}, 预测值={y_test_pred[i]:.2f}, 残差={y_test[i] - y_test_pred[i]:.2f}")
print(f"\n测试集评估:")
print(f"测试集均方误差 (MSE): {mse_test:.4f}")
print(f"测试集决定系数 (R²): {r2_test:.4f}")
# 分析模型的泛化能力
print("\n模型泛化能力分析:")
if mse_test > mse_train * 1.5:
print("测试集误差明显大于训练集,模型可能存在过拟合")
elif mse_test < mse_train:
print("测试集误差小于训练集,模型泛化能力良好")
else:
print("测试集误差与训练集相近,模型泛化能力一般")
plt.figure(figsize=(10, 6))
# 绘制训练数据点
plt.scatter(X_train, y_train, color='blue', alpha=0.7, label='训练数据')
# 绘制测试数据点
plt.scatter(X_test, y_test, color='green', alpha=0.7, label='测试数据')
# 绘制拟合线
x_line = np.linspace(min(x), max(x), 100)
X_line = sm.add_constant(x_line.reshape(-1, 1))
y_line = model.predict(X_line)
plt.plot(x_line, y_line, 'r-', label=f'线性回归拟合线 (y = {intercept:.2f} + {slope:.2f}x, R² = {r2_train:.2f})', alpha=0.7)
# 添加月份标签
for i, month in enumerate(months):
idx = np.where(x == i+1)[0][0]
plt.annotate(month, (x[idx], y[idx]), textcoords="offset points",
xytext=(0,10), ha='center')
plt.title('水帖数量线性回归模型拟合结果 (训练集和测试集)')
plt.xlabel('时间序列')
plt.ylabel('水帖数量')
plt.grid(True, linestyle='--', alpha=0.5)
plt.legend()
plt.tight_layout()
plt.savefig('水帖数量回归模型拟合结果_训练测试.png', dpi=300)
plt.show()
next_month = 14 # 2025年6月对应的序号
# 构造预测数据
X_next = np.array([[1, next_month]]) # 1是常数项,next_month是特征值
# 进行预测
prediction = model.predict(X_next)[0]
# 计算预测区间
prediction_summary = model.get_prediction(X_next).summary_frame(alpha=0.05)
lower_bound = prediction_summary['mean_ci_lower'].values[0]
upper_bound = prediction_summary['mean_ci_upper'].values[0]
print(f"\n预测2025年6月的水帖数量:")
print(f"预测值: {prediction:.2f}")
print(f"95%置信区间: [{lower_bound:.2f}, {upper_bound:.2f}]")
print(f"置信区间宽度: {upper_bound - lower_bound:.2f}")
print(f"有95%的把握认为2025年6月的水帖数量在{lower_bound:.2f}到{upper_bound:.2f}之间")
plt.figure(figsize=(10, 6))
# 绘制训练数据点
plt.scatter(X_train, y_train, color='blue', alpha=0.7, label='训练数据')
# 绘制测试数据点
plt.scatter(X_test, y_test, color='green', alpha=0.7, label='测试数据')
# 绘制预测点
plt.scatter(next_month, prediction, color='red', s=100, label='2025年6月预测值')
# 绘制预测区间
plt.fill_between([next_month], [lower_bound], [upper_bound],
color='red', alpha=0.2, label='95%预测区间')
# 绘制拟合线和预测线
x_extended = np.linspace(min(x), next_month, 100)
X_extended = np.column_stack((np.ones(len(x_extended)), x_extended)) # 添加常数项
y_extended = model.predict(X_extended)
plt.plot(x_extended, y_extended, 'r-', label=f'线性回归线 (y = {intercept:.2f} + {slope:.2f}x)', alpha=0.7)
# 添加月份标签
for i, month in enumerate(months):
idx = np.where(x == i+1)[0][0]
plt.annotate(month, (x[idx], y[idx]), textcoords="offset points",
xytext=(0,10), ha='center')
plt.annotate(f"2025年6月: {prediction:.2f}", (next_month, prediction),
textcoords="offset points", xytext=(0,10), ha='center',
color='red', weight='bold')
plt.title('水帖数量预测结果 (基于训练测试集拆分)')
plt.xlabel('时间序列')
plt.ylabel('水帖数量')
plt.grid(True, linestyle='--', alpha=0.5)
plt.legend()
plt.tight_layout()
plt.savefig('水帖数量预测结果_训练测试.png', dpi=300)
plt.show()
print(f"1. 基于线性回归模型的分析,2025年6月的水帖数量预测为: {prediction:.2f}")
print(f"2. 95%置信区间为: [{lower_bound:.2f}, {upper_bound:.2f}]")
print(f"3. 模型在训练集上的决定系数(R²)为: {r2_train:.4f},表示模型解释了{r2_train*100:.2f}%的训练数据变异性")
print(f"4. 模型在测试集上的决定系数(R²)为: {r2_test:.4f},表示模型解释了{r2_test*100:.2f}%的测试数据变异性")
if r2_test < r2_train * 0.7:
print(" 测试集R²明显低于训练集,说明模型可能存在过拟合")
elif r2_test > r2_train:
print(" 测试集R²高于训练集,说明模型泛化能力良好")
print(f"5. 训练集MSE: {mse_train:.4f},测试集MSE: {mse_test:.4f}")
print(f"6. 由于样本量较小(训练集{len(X_train)}个,测试集{len(X_test)}个),预测结果仅供参考")
print(f"7. 红线代表线性回归拟合线,方程为: y = {intercept:.2f} + {slope:.2f}x")
print(f" 斜率为{slope:.4f},表示每增加一个月,水帖数量平均{abs(slope):.4f}{'增加' if slope > 0 else '减少'}")
样本数据太少,概念太多,很多基于数学和统计的知识不懂,难怪搞 AI 的要求硕士,本科不是数学专业的估计也难搞。小孩子不懂事写着玩骗积分的