多层感知机MLP - helloworld

由若干个变量,推出一个结果,变量与结果之间是非线性关系。

MLPRegressor,也叫多层感知机(MLP)回归器,是一种基于神经网络的回归模型。

主要用于处理‌回归任务‌,通过学习输入特征与连续值输出之间的非线性关系,实现预测或拟合。

生产用途:

  • 房价预测 ‌:利用房屋特征(面积、位置等)训练模型,预测未来房价走势。 ‌
  • 广告收益预测 ‌:通过分析广告投放数据(如电视、广播等渠道的投入),预测广告收益。 ‌
  • 天气预测 ‌:基于气象数据训练模型,预测温度、湿度等指标。 ‌

DEMO

import numpy as np
import pandas as pd
import math
from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import learning_curve
from sklearn.model_selection import GridSearchCV
import matplotlib.pyplot as plt
from joblib import dump
from joblib import load


def random_data():
    """
    生成随机数,并保存到 CSV 文件中(测试数据)
    :return:
    """
    # 生成测试数据
    np.random.seed(42)
    n_samples = 1000
    # 6个参数,范围[0, 10]
    X = np.random.rand(n_samples, 6) * 10

    # 构造非线性关系(例如:乘积+正弦函数)
    y = (X[:, 0] * 0.5 + X[:, 1] ** 2 * 0.3 + np.sin(X[:, 2]) * 2 +
         X[:, 3] * X[:, 4] * 0.1 + np.log(X[:, 5] + 1) * 1.5)
    # 添加噪声
    y += np.random.normal(0, 0.5, n_samples)

    # 将数据保存到CSV文件
    data = np.column_stack((X, y))
    columns = ['feature1', 'feature2', 'feature3', 'feature4', 'feature5', 'feature6', 'target']
    df = pd.DataFrame(data, columns=columns)

    # 保存到CSV文件
    csv_file = 'synthetic_data.csv'
    df.to_csv(csv_file, index=False)
    print(f"数据已保存到 {csv_file}")


# 数据读取函数
def load_synthetic_data(file_path):
    """
    从CSV文件加载数据

    参数:
        file_path (str): CSV文件路径

    返回:
        X (np.ndarray): 特征矩阵 (n_samples, 6)
        y (np.ndarray): 目标值数组 (n_samples,)
        df (pd.DataFrame): 原始数据框
    """
    df = pd.read_csv(file_path)
    X = df[['feature1', 'feature2', 'feature3', 'feature4', 'feature5', 'feature6']].values
    y = df['target'].values
    return X, y, df


def train(x_train_scaled, y_train):
    """
   训练模型

   在 Scikit-learn 中,大多数模型不支持直接追加新数据继续训练
   包括 MLPRegressor,不支持增量学习或在线学习

   修改 warm_start=True,可以追加新的数据,在旧模型的基础上继续优化,但底层仍可能重新处理所有数据。

    :param x_train_scaled: 训练数据集(缩放之后的数据)
    :param y_train: 训练数据结果集
    :return: 训练好的模型
    """
    # 初始化模型
    mlp = MLPRegressor(
        hidden_layer_sizes=(64, 32),  # 2个隐藏层,分别有64和32个神经元
        activation='relu',  # 激活函数('relu', 'tanh', 'logistic')
        solver='adam',  # 优化算法('adam', 'sgd', 'lbfgs')
        max_iter=500,  # 最大迭代次数
        random_state=42,
        early_stopping=True,  # 提前停止(防止过拟合)
        validation_fraction=0.1  # 验证集比例(用于early stopping)
    )

    # 训练模型
    mlp.fit(x_train_scaled, y_train)

    # 检查是否收敛(若未收敛,可增加max_iter)
    # 模型实际完成的迭代次数(即优化算法真正运行的步数) - 用户预设的最大迭代次数(默认值为 200)。
    print("训练是否收敛:", mlp.n_iter_ < mlp.max_iter)
    return mlp


def train_grid(x_train_scaled, y_train):
    """
    使用网格搜索优化超参数:

    自动化调参:通过交叉验证找到最优超参数组合。
    模型选择:直接获得训练好的最优模型,避免手动配置。
    性能评估:通过验证集 MSE 量化模型优劣,指导后续优化方向。

    :param x_train_scaled: 训练数据集(缩放之后的数据)
    :param y_train: 训练数据结果集
    :return: 训练好的模型
    """

    param_grid = {
        'hidden_layer_sizes': [(64,), (64, 32), (128, 64, 32)],
        'activation': ['relu', 'tanh'],
        'alpha': [0.0001, 0.001, 0.01]
    }

    grid_search = GridSearchCV(
        MLPRegressor(max_iter=1000, random_state=42),
        param_grid,
        cv=3,
        scoring='neg_mean_squared_error'
    )
    grid_search.fit(x_train_scaled, y_train)

    print("最佳参数:", grid_search.best_params_)
    best_mlp = grid_search.best_estimator_
    # 最佳模型
    return best_mlp


def print_regressor(y_test, y_pred):
    """
    模型可靠性分析相关的内容
    打印均方误差、拟合优度等信息
    :param y_test:
    :param y_pred:
    :return:
    """
    # 均方误差 MSE(Mean Squared Error)是预测值与真实值之差的平方的平均值:
    mse = mean_squared_error(y_test, y_pred)
    # 均方根误差,预测值与真实值的平均绝对偏差(开方后单位与预测值一致)。
    # 是否 “误差较小” 需结合业务场景判断,例如:股票价格预测中 ±10 可能很大,而体重预测中 ±10 kg 可能正常。
    rmse = math.sqrt(mse)
    # 拟合优度,0-1 越大越好
    r2 = r2_score(y_test, y_pred)
    print(f"均方误差(MSE): {mse:.4f}")
    print(f"均方根误差(RMSE): {rmse:.4f}")
    print(f"R²分数: {r2:.4f}")

    # 可视化预测 vs 真实值
    plt.figure(figsize=(8, 6))
    plt.scatter(y_test, y_pred, alpha=0.6)
    plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'r--')
    plt.xlabel("True value")  # 真实值
    plt.ylabel("Predicted value")  # 预测值
    plt.title("MLPRegressor Prediction effect")
    plt.savefig("mlp_regressor.png")
    plt.clf()


def print_learning_curve(mlp, x_train_scaled, y_train):
    """
    打印学习曲线分析

    理想状态:两者接近且较高,验证 R² 随数据量增加趋于平稳。

    :param mlp: 深度学习模型
    :param x_train_scaled: 训练数据集(缩放之后的数据)
    :param y_train: 训练数据结果集
    :return:
    """

    # cv=5 使用交叉验证、scoring='r2': 使用 R²(决定系数)
    train_sizes, train_scores, test_scores = learning_curve(
        mlp, x_train_scaled, y_train, cv=5, scoring='r2'
    )

    # 训练集 R² 分数的均值
    plt.plot(train_sizes, np.mean(train_scores, axis=1), label="train R²")
    # 验证集 R² 分数的均值
    plt.plot(train_sizes, np.mean(test_scores, axis=1), label="val R²")
    # 训练样本数量(Example)
    plt.xlabel("Example")
    # R² 分数(R² Score),范围通常在 0~1(越接近 1 越好)。
    plt.ylabel("R² Score")
    plt.legend()
    plt.savefig("mlp_learning_curve.png")
    plt.clf()


def train_mlp():
    """
    训练并另存模型
    :return:
    """
    # 读取测试数据
    x, y, df = load_synthetic_data("synthetic_data.csv")

    # 随机划分训练集和测试集
    x_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

    # 标准化数据(神经网络对尺度敏感),将数据转为标准正态分布 N(0,1)
    scaler = StandardScaler()
    x_train_scaled = scaler.fit_transform(x_train)
    x_test_scaled = scaler.transform(X_test)

    # 训练模型
    mlp = train(x_train_scaled, y_train)
    # 学习曲线分析
    print_learning_curve(mlp, x_test_scaled, y_test)

    # 预测测试集
    y_pred = mlp.predict(x_test_scaled)
    # 可靠性分析
    print_regressor(y_test, y_pred)

    # 存储模型到文件
    dump(mlp, "mlp_regressor_model.joblib")
    print("模型已保存为 mlp_regressor_model.joblib")


def load_mlp():
    """
    加载保存好的模型
    :return:
    """
    loaded_model = load("mlp_regressor_model.joblib")
    return loaded_model


train_mlp()

# # 对测试集预测
# y_pred = mlp.predict(X_test_scaled)  # 或 best_mlp.predict(X_test_scaled)
#
# # 对新数据预测(需相同预处理)
# new_data = [[...]]  # 新样本(特征)
# new_data_scaled = scaler.transform(new_data)  # 必须用训练时的scaler
# new_pred = mlp.predict(new_data_scaled)

posted on 2025-09-02 11:34  疯狂的妞妞  阅读(61)  评论(0)    收藏  举报

导航