mlp算法拟合非线性函数效果不佳

这是摘自某博主的代码,输出结果和目标结果差距较大,有没有人能给我讲讲原因以及如何改进?

#include <iostream>
#include <vector>
#include <cmath>
#include <random>

using namespace std;

class MLP
{
private:
    int input_size;                        // 输入层大小
    int hidden_size;                       // 隐藏层大小
    int output_size;                       // 输出层大小
    vector<vector<double>> input_weights;  // 输入层到隐藏层的权重
    vector<double> input_bias;             // 输入层到隐藏层的偏置
    vector<vector<double>> hidden_weights; // 隐藏层到输出层的权重
    vector<double> hidden_bias;            // 隐藏层到输出层的偏置
    double learning_rate;                  // 学习率
    double regularization_rate;            // 正则化率
    string activation_function;            // 激活函数
    double dropout_rate;                   // dropout率

public:
    MLP(int input_size, int hidden_size, int output_size, double learning_rate, double regularization_rate, string activation_function, double dropout_rate)
    {
        this->input_size = input_size;                   // 初始化输入层大小
        this->hidden_size = hidden_size;                 // 初始化隐藏层大小
        this->output_size = output_size;                 // 初始化输出层大小
        this->learning_rate = learning_rate;             // 初始化学习率
        this->regularization_rate = regularization_rate; // 初始化正则化率
        this->activation_function = activation_function; // 初始化激活函数
        this->dropout_rate = dropout_rate;               // 初始化dropout率

        // 初始化输入层到隐藏层的权重和偏置
        input_weights.resize(hidden_size, vector<double>(input_size)); // 初始化输入层到隐藏层的权重
        input_bias.resize(hidden_size);                                // 初始化输入层到隐藏层的偏置
        random_device rd;                                              // 随机数生成器
        mt19937 gen(rd());                                             // 随机数生成器
        uniform_real_distribution<> dis(-0.5, 0.5);                    // 均匀分布
        for (int i = 0; i < hidden_size; i++)
        {
            for (int j = 0; j < input_size; j++)
            {
                input_weights[i][j] = dis(gen); // 随机初始化权重
            }
            input_bias[i] = dis(gen); // 随机初始化偏置dis(gen)
        }

        // 初始化隐藏层到输出层的权重和偏置
        hidden_weights.resize(output_size, vector<double>(hidden_size)); // 初始化隐藏层到输出层的权重
        hidden_bias.resize(output_size);                                 // 初始化隐藏层到输出层的偏置
        for (int i = 0; i < output_size; i++)
        {
            for (int j = 0; j < hidden_size; j++)
            {
                hidden_weights[i][j] = dis(gen); // 随机初始化权重
            }
            hidden_bias[i] = dis(gen); // 随机初始化偏置
        }
    }

    double activation(double x) // 激活函数
    {
        if (activation_function == "relu")
        {
            return max(0.0, x);
        }
        else if (activation_function == "sigmoid")
        {
            return 1 / (1 + exp(-x));
        }
        else if (activation_function == "tanh")
        {
            return tanh(x);
        }
        else
        {
            return x;
        }
    }

    double activation_derivative(double x) // 激活函数的导数
    {
        if (activation_function == "relu")
        {
            if (x > 0)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        else if (activation_function == "sigmoid")
        {
            return exp(-x) / pow(1 + exp(-x), 2);
        }
        else if (activation_function == "tanh")
        {
            return 1 - pow(tanh(x), 2);
        }
        else
        {
            return 1;
        }
    }

    vector<double> predict(vector<double> input) // 预测函数
    {
        // 计算隐藏层的值
        vector<double> hidden(hidden_size);
        for (int i = 0; i < hidden_size; i++)
        {
            double sum = 0;
            for (int j = 0; j < input_size; j++)
            {
                sum += input_weights[i][j] * input[j];
            }
            hidden[i] = activation(sum + input_bias[i]); // 计算隐藏层的值
        }

        // 计算输出层的值
        vector<double> output(output_size);
        for (int i = 0; i < output_size; i++)
        {
            double sum = 0;
            for (int j = 0; j < hidden_size; j++)
            {
                sum += hidden_weights[i][j] * hidden[j];
            }
            output[i] = activation(sum + hidden_bias[i]); // 计算输出层的值
        }

        return output;
    }

    void train(vector<vector<double>> inputs, vector<vector<double>> targets, int batch_size) // 训练函数
    {
        int num_batches = inputs.size() / batch_size;
        for (int i = 0; i < num_batches; i++)
        {
            vector<vector<double>> batch_inputs(inputs.begin() + i * batch_size, inputs.begin() + (i + 1) * batch_size);
            vector<vector<double>> batch_targets(targets.begin() + i * batch_size, targets.begin() + (i + 1) * batch_size);

            // 前向传播
            vector<vector<double>> hidden(batch_size, vector<double>(hidden_size));
            vector<vector<double>> output(batch_size, vector<double>(output_size));

            // 计算隐藏层的值
            for (int j = 0; j < batch_size; j++)
            {
                for (int k = 0; k < hidden_size; k++)
                {
                    double sum = 0;
                    for (int l = 0; l < input_size; l++)
                    {
                        sum += input_weights[k][l] * batch_inputs[j][l];
                    }
                    hidden[j][k] = activation(sum + input_bias[k]); // 计算隐藏层的值
                }
            }

            // 计算输出层的值
            for (int j = 0; j < batch_size; j++)
            {
                for (int k = 0; k < output_size; k++)
                {
                    double sum = 0;
                    for (int l = 0; l < hidden_size; l++)
                    {
                        sum += hidden_weights[k][l] * hidden[j][l];
                    }
                    output[j][k] = activation(sum + hidden_bias[k]); // 计算输出层的值
                }
            }

            // 反向传播
            vector<vector<double>> output_error(batch_size, vector<double>(output_size));
            vector<vector<double>> hidden_error(batch_size, vector<double>(hidden_size));

            // 计算输出层的误差
            for (int j = 0; j < batch_size; j++)
            {
                for (int k = 0; k < output_size; k++)
                {
                    output_error[j][k] = (batch_targets[j][k] - output[j][k]) * activation_derivative(output[j][k]); // 计算输出层的误差
                }
            }

            // 计算隐藏层的误差
            for (int j = 0; j < batch_size; j++)
            {
                for (int k = 0; k < hidden_size; k++)
                {
                    double sum = 0;
                    for (int l = 0; l < output_size; l++)
                    {
                        sum += output_error[j][l] * hidden_weights[l][k];
                    }
                    hidden_error[j][k] = sum * activation_derivative(hidden[j][k]); // 计算隐藏层的误差
                }
            }

            // 更新隐藏层到输出层的权重和偏置
            for (int j = 0; j < output_size; j++)
            {
                for (int k = 0; k < hidden_size; k++)
                {
                    double weight_gradient = 0;
                    for (int l = 0; l < batch_size; l++)
                    {
                        weight_gradient += hidden[l][k] * output_error[l][j];
                    }
                    hidden_weights[j][k] += learning_rate * weight_gradient / batch_size - regularization_rate * hidden_weights[j][k]; // 更新隐藏层到输出层的权重
                }
                double bias_gradient = 0;
                for (int k = 0; k < batch_size; k++)
                {
                    bias_gradient += output_error[k][j];
                }
                hidden_bias[j] += learning_rate * bias_gradient / batch_size; // 更新隐藏层到输出层的偏置
            }

             //添加dropout
            for (int j = 0; j < hidden_size; j++)
            {
                for (int k = 0; k < input_size; k++)
                {
                    if (rand() < dropout_rate * RAND_MAX)
                    {
                        input_weights[j][k] = 0;
                    }
                }
                if (rand() < dropout_rate * RAND_MAX)
                {
                    input_bias[j] = 0;
                }
            }

            // 更新输入层到隐藏层的权重和偏置
            for (int j = 0; j < hidden_size; j++)
            {
                for (int k = 0; k < input_size; k++)
                {
                    double weight_gradient = 0;
                    for (int l = 0; l < batch_size; l++)
                    {
                        weight_gradient += batch_inputs[l][k] * hidden_error[l][j];
                    }
                    input_weights[j][k] += learning_rate * weight_gradient / batch_size - regularization_rate * input_weights[j][k]; // 更新输入层到隐藏层的权重
                }
                double bias_gradient = 0;
                for (int k = 0; k < batch_size; k++)
                {
                    bias_gradient += hidden_error[k][j];
                }
                input_bias[j] += learning_rate * bias_gradient / batch_size; // 更新输入层到隐藏层的偏置
            }
        }
    }
    double evaluate(vector<vector<double>> inputs, vector<vector<double>> targets) // 评价函数
    {
        double error = 0;
        for (int i = 0; i < inputs.size(); i++)
        {
            vector<double> output = predict(inputs[i]);
            for (int j = 0; j < output_size; j++)
            {
                error += pow(targets[i][j] - output[j], 2);
            }
        }
        return error / inputs.size();
    }
};

void generate_training_data(vector<vector<double>>& inputs, vector<vector<double>>& targets)
{
    for (double x = -1; x <= 1; x += 0.1)
    {
        double y = pow(x, 3) + 0.5 * x + 0.2;
        inputs.push_back({ x }); // 添加输入
        targets.push_back({ y }); // 添加目标
    }
}

int main()
{
    // 初始化MLP
    int input_size = 1;                   // 输入层大小
    int hidden_size = 100;                 // 隐藏层大小
    int output_size = 1;                  // 输出层大小
    double learning_rate = 0.01;          // 学习率
    double regularization_rate = 0.0001; // 正则化率
    string activation_function = "tanh";  // 激活函数
    double dropout_rate = 0.005;            // dropout率

    MLP mlp(input_size, hidden_size, output_size, learning_rate, regularization_rate, activation_function, dropout_rate); // 创建MLP对象

    // 生成数据
    vector<vector<double>> inputs;
    vector<vector<double>> targets;
    generate_training_data(inputs, targets);

    // 训练MLP
    int batch_size = 10;
    int epochs = 1000;
    for (int i = 0; i < epochs; i++)
    {
        mlp.train(inputs, targets, batch_size); // 训练
        cout << "Error: " << mlp.evaluate(inputs, targets) << endl; // 输出每次训练的误差
    }

    // 使用MLP进行预测
    for (double x = -1; x <= 1; x += 0.1)
    {
        vector<double> input = { x };
        vector<double> output = mlp.predict(input); // 预测
        cout << "inputs: " << x << " targets: " << pow(x, 3) + 0.5 * x + 0.2 << " output: " << output[0] << endl; // 输出结果
    }

    return 0;
}

引用 皆我百晓生 小程序回复内容作答:
MLP(多层感知器)是一种常用的机器学习算法,可以用于非线性函数拟合。根据你提供的代码,以下是可能导致拟合效果不佳的原因以及可能的改进方法:

  1. 隐藏层大小选择不合适:隐藏层的大小决定了模型的表达能力,如果隐藏层太小,可能无法捕捉到复杂的模式。可以尝试增加隐藏层的大小,以提高模型的表达能力。

  2. 学习率选择不合适:学习率过小会导致优化过程非常缓慢,而学习率过大可能会导致优化过程不稳定。建议尝试调整学习率,选择一个合适的值,以平衡模型的收敛速度和稳定性。

  3. 正则化率设置不当:正则化可以帮助避免过拟合,但过高的正则化率可能会导致模型欠拟合。建议尝试调整正则化率,选择一个适当的值,以平衡模型的拟合能力和泛化能力。

  4. 激活函数选择不合适:不同的激活函数适用于不同类型的问题。根据你的需求和数据特点,选择适合的激活函数可能会改善模型的性能。

  5. Dropout率设置不当:Dropout是一种正则化技术,可以随机丢弃网络中的一些神经元,以减少过拟合。然而,过高的Dropout率可能会导致模型欠拟合。建议尝试调整Dropout率,选择一个合适的值,以平衡模型的拟合能力和泛化能力。

  6. 数据集大小和训练时期选择不当:小数据集和过少的训练时期可能无法充分训练模型,导致拟合效果不佳。建议尝试增加数据集大小或增加训练时期,以提高模型的效果。

请注意,这些改进方法仅提供一些常用的调整思路,具体的改进方法还需要根据实际问题和数据来确定。建议进行多次实验和调优以找到最适合你问题的设置。

援引通义千问:
这段代码是一个多层感知器(MLP)的实现,用于拟合非线性函数。然而,输出结果和目标结果差距较大,可能是由于以下原因:

  1. 学习率过高或过低:学习率控制着模型参数的更新速度,如果学习率过高,模型参数可能会在每次迭代中发生剧烈变化,导致模型无法收敛;如果学习率过低,模型参数的更新速度过慢,模型可能需要更多的迭代次数才能收敛。可以尝试调整学习率的值,例如使用动态学习率或者自适应学习率算法。
  2. 正则化率过高或过低:正则化是一种防止过拟合的方法,它通过对模型参数加上惩罚项来限制模型的复杂度。如果正则化率过高,模型可能会过于简单,无法拟合数据;如果正则化率过低,模型可能会过拟合数据。可以尝试调整正则化率的值,例如使用L1正则化或者L2正则化。
  3. 激活函数选择不当:激活函数是非线性函数,用于将线性组合的结果转换为非线性的输出。不同的激活函数有不同的特性,例如ReLU激活函数可以避免梯度消失,而sigmoid激活函数可以将输出限制在0到1之间。可以尝试使用不同的激活函数,例如ReLU、sigmoid、tanh等。
  4. 数据集不足或者数据质量不高:数据集的大小和质量对模型的性能有很大的影响。如果数据集太小,模型可能无法从中学习到足够的信息;如果数据集中存在噪声或者异常值,模型可能会受到这些干扰的影响。可以尝试增加数据集的大小或者清洗数据集,以提高模型的性能。
  5. 模型结构不合适:模型的结构包括输入层、隐藏层和输出层的大小,以及权重和偏置的初始化方式等。如果模型结构不合适,模型可能无法拟合数据或者过拟合数据。可以尝试调整模型的结构,例如增加隐藏层的数量或者减少隐藏层的大小。

为了改进模型的性能,可以尝试以下方法:

  1. 调整学习率和正则化率的值,例如使用动态学习率或者自适应学习率算法,或者调整正则化率的值。
  2. 尝试使用不同的激活函数,例如ReLU、sigmoid、tanh等。
  3. 增加数据集的大小或者清洗数据集,以提高模型的性能。
  4. 调整模型的结构,例如增加隐藏层的数量或者减少隐藏层的大小。
  5. 使用更复杂的模型,例如深度神经网络(DNN)或者卷积神经网络(CNN)等。

看起来是一个简单的多层感知器(MLP)神经网络的实现,用于回归任务
MLP的性能受到网络结构和超参数设置的影响很大。在此代码中,隐藏层大小、学习率、正则化率和dropout率都是可调整的超参数。你可能需要尝试不同的值来找到最佳配置。该代码中使用了tanh激活函数,但对于不同的问题,不同的激活函数可能更合适。尝试使用其他激活函数,如ReLU或Sigmoid,并看看是否对性能有改进。确保输入数据已经归一化,通常在训练神经网络时将输入数据缩放到0到1或-1到1的范围内,可以帮助网络更快地收敛。你可以尝试不同的批处理大小和迭代次数来观察性能的变化。有时,更大的批次和更多的迭代次数可以改善收敛性。

你把你的逻辑最好画个流程图出来看下,发一堆代码不太好看出来

非线性拟合(C++版)
可以试试这个

训练模型无非就几个因素嘛,数据的问题和参数的问题。

【相关推荐】




如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

结合GPT给出回答如下请题主参考
首先,MLP算法是可以拟合非线性函数的,但是需要合适的参数设置和训练方法。如果使用了默认参数或者错误的训练方法,拟合效果可能会很差。

其次,需要看一下具体的代码和数据,才能确定问题出在哪里。以下是一些可能的原因和改进方法:

  1. 数据不足或者不合理:非线性函数可能需要更多的数据来拟合,而且有些非线性函数可能需要更高维度的数据来表示。此外,数据的分布和噪声也会影响拟合效果。可以考虑增加数据量、改变数据分布或者减少噪声。

  2. 参数设置不合理:MLP算法有很多参数可以调节,包括隐藏层数、每层的神经元数量、学习率、正则化等等。默认的参数不一定适合所有的数据和问题,需要根据实际情况进行调整。可以尝试使用网格搜索或者随机搜索等方法找到合适的参数。

  3. 训练方法不合理:MLP算法有不同的训练方法,包括随机梯度下降(SGD)、批量梯度下降(BGD)、动量法、Adam等等。每种方法有不同的优缺点,需要根据实际情况选择合适的方法。可以尝试使用交叉验证等方法来比较不同的训练方法。

  4. 激活函数不合适:MLP算法的每个神经元都需要一个激活函数来将输入转换为输出。不同的激活函数有不同的性质,例如sigmoid函数、ReLU函数、tanh函数等等。需要根据实际情况选择合适的激活函数。可以尝试使用不同的激活函数来比较拟合效果。

  5. 过拟合或欠拟合:MLP算法容易发生过拟合或欠拟合现象。过拟合指模型过于复杂,无法泛化到新数据。欠拟合指模型过于简单,无法拟合数据。可以通过正则化、增加数据量、减少参数数量等方法来避免过拟合或欠拟合。

综上所述,优化MLP算法的拟合效果需要对数据、参数、训练方法、激活函数等方面进行适当的调整和优化。具体的方法和步骤需要根据实际问题来确定。

该回答引用ChatGPT,希望对题主有所帮助,如有帮助,还望采纳。


根据您提供的信息,无法确定具体的目标和输出结果。但是可以看到,该代码使用了一个具有100个隐藏神经元的MLP来拟合一个输入为$x$,输出为$y=x^3+0.5x+0.2$的函数。该代码也包括了使用梯度下降和正则化来训练模型的功能。

然而,该代码可能存在一些问题,可能需要改进:

  1. 训练集和测试集的划分问题:该代码中没有对训练集和测试集进行划分,这可能会导致模型过拟合训练集,并在测试集上表现较差。建议在训练前将数据集分成训练集和测试集,以评估模型的泛化能力。

  2. 隐藏神经元的数量问题:该代码将隐藏层神经元数量设置为100,这可能会导致模型过拟合训练集。建议尝试使用更少的神经元或使用其他正则化技术来防止过拟合。

  3. 参数调整问题:该代码中的学习率、正则化率和dropout率等参数是根据作者自己经验调整的。建议在训练过程中尝试使用不同的参数值,并使用交叉验证等技术来评估模型的性能。

  4. dropout的使用问题:该代码只在输入层到隐藏层之间使用了dropout,而没有在隐藏层到输出层之间使用。建议尝试在所有层之间使用dropout以提高模型的稳定性和防止过拟合。

  5. 代码可读性和可重用性问题:该代码中的某些命名和注释可能不够清晰,不易于理解。建议改进命名和注释,以提高代码的可读性和可重用性。

数据预处理, 确保对数据进行适当的预处理。这包括对输入特征进行归一化、标准化或对数据进行其他形式的预处理,以便使输入特征位于一个共同的量级上。这样可以提高算法的收敛速度并减少过拟合的可能性。
MLP的隐藏层设计对拟合效果有很大影响。如果隐藏层节点数过少,可能会导致模型无法捕捉到数据的复杂模式;如果节点数过多,则可能导致过拟合。可以尝试调整隐藏层节点数,并观察模型性能的变化。

MLP算法详解与实现-CSDN博客 MLP应用示例首先看一个动画展示上面动画中黄色的点代表的是我们想识别的点,墨蓝色的点代表是干扰的点。那为什么我们要识别黄色的点?举个实际的例子,有一批用户,有部分用户是价格敏感的,有部分用户是价格不敏感。那么从商家的角度来考虑,就要找出这部分价格敏感的用户,然后给他们做补贴。再比如,有一批邮件,其中有部分是垃圾邮件,其他都是正常的邮件。所以我们要找出其中的垃圾邮件,将他们过滤掉,节省用户查看邮件的时间,降低邮件中毒的风险。按照我们上面的示例,黄色的点就代表价格敏感用户(或垃圾邮件)._mlp算法 https://blog.csdn.net/lanhongyun1201/article/details/122683114?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169511457216800184156008%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=169511457216800184156008&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-122683114-null-null.142^v94^insert_down1&utm_term=mlp%E7%AE%97%E6%B3%95%E6%8B%9F%E5%90%88%E9%9D%9E%E7%BA%BF%E6%80%A7%E5%87%BD%E6%95%B0%E6%95%88%E6%9E%9C%E4%B8%8D%E4%BD%B3&spm=1018.2226.3001.4187

增加数据量试试

对于神经网络模型,数据预处理非常重要。确保输入数据经过适当的归一化、标准化或者其他预处理操作,以提高模型的拟合效果