我有一个学校项目,对多层感知器进行编程,将数据分为三类。我已经实现了 http://home.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html 的反向传播算法。我已经检查了我的算法(通过手动计算反向传播的每个步骤)是否确实满足这个解释的步骤并且满足。
对于分类,我使用 one-hot 代码,输入由具有 2 个值的向量和三个输出神经元(每个神经元用于单个类)组成。在每个纪元之后,我都会对输入数据进行洗牌。对于分类,我使用 sigmoid 函数。我也尝试过实现softmax,但我还没有找到导数softmax的样子。权重调整时需要导数softmax吗?为了检查网络是否成功对输入进行分类,我正在比较具有输出神经元最大输出的输出神经元的位置是否对应于等于 1 的当前输入 one-hot 代码向量的位置。
但是我的实现并没有训练这个神经网络。我正在研究这个问题并调试了几天,并在互联网上查找我做错了什么,但我还没有找到答案。我真的不知道我哪里错了。当我有 10 个输入时,我的神经网络将成功训练,但当我有 100、200、400 和 800 个输入时,它会在有一半好的分类输入时开始循环。正如我所说,我的反向传播算法很好。 Visual Studio 2010 中带有输入文件的整个 C++ 项目位于:http://www.st.fmph.uniba.sk/~vajda10/mlp.zip
结构:
struct input {
vector<double> x;
vector<double> cls;
};
struct neuron {
double output;
double error;
neuron(double o, double e): output(o), error(e) { };
};
全局变量:
double alpha = 0.5;
vector<vector<input>> data;
vector<vector<neuron>> hiddenNeurons;
vector<neuron> outputNeurons;
vector<vector<vector<double>>> weights;
这是我的反向传播算法代码:
for (int b = 0; b < data[0].size(); b++) {
// calculate output of hidden neurons
for (int i = 0; i < hiddenNeurons.size(); i++) {
for (int j = 0; j < hiddenNeurons[i].size(); j++) {
double activation = neuronActivation(0, b, i, j);
hiddenNeurons[i][j].output = sigmoid(activation);
}
}
double partError = 0;
// calculate output and errors on output neurons
for (int k = 0; k < outputNeurons.size(); k++) {
double activation = neuronActivation(0, b, hiddenNeurons.size(), k);
outputNeurons[k].output = sigmoid(activation);
outputNeurons[k].error = data[0][b].cls[k] - outputNeurons[k].output;
partError += pow(outputNeurons[k].error, 2);
}
error += sqrt(partError)/outputNeurons.size();
// if classification is wrong
if (data[0][b].cls[maxOutputIndex(outputNeurons)] != 1) {
wrongClass++;
// error backpropagation
for (int i = hiddenNeurons.size()-1; i >= 0; i--) {
for (int j = 0; j < hiddenNeurons[i].size(); j++) {
hiddenNeurons[i][j].error = 0.0;
if (i < hiddenNeurons.size()-1) {
for (int k = 0; k < hiddenNeurons[i+1].size(); k++) {
hiddenNeurons[i][j].error += hiddenNeurons[i+1][k].error * weights[i+1][j][k];
}
}
else {
for (int k = 0; k < outputNeurons.size(); k++) {
hiddenNeurons[i][j].error += outputNeurons[k].error * weights[i+1][j][k];
}
}
}
}
// adjust weights
for (int i = 0; i < weights.size(); i++) {
int n;
if (i < weights.size()-1) {
n = hiddenNeurons[i].size();
}
else {
n = outputNeurons.size();
}
for (int k = 0; k < n; k++) {
for (int j = 0; j < weights[i].size(); j++) {
double y;
if (i == 0) {
y = data[0][b].x[j];
}
else {
y = hiddenNeurons[i-1][j].output;
}
if (i < weights.size()-1) {
weights[i][j][k] += alpha * hiddenNeurons[i][k].error * derivedSigmoid(hiddenNeurons[i][k].output) * y;
}
else {
weights[i][j][k] += alpha * outputNeurons[k].error * derivedSigmoid(outputNeurons[k].output) * y;
}
}
}
}
}
}
请问,谁能告诉我我做错了什么,或者给我一些建议,告诉我必须在哪里寻找错误?我希望我已经讲述了所有重要的事情。请原谅我糟糕的英语。
最佳答案
高斯分类器(如 BackpropNN)只会对连续样本集进行样条处理。
由于您的网络是在小型示例集上学习的,因此我假设该小型示例集没有不连续性。
这里例如。是训练样本集中的不连续性(输入向量--->输出向量):
[0,1,0,1,1,0,1,0] ---> [0,1,0]
[0,1,0,1,1,0,1,0] ---> [1,1,0]
算法无法对此进行分类(样条)。给定输入向量的输出向量必须是唯一的(连续的)。
如果您随机生成样本,这可以解释为什么小集合似乎总是有效 - 生成不连续性的可能性很低。更大的集合将保证这个问题。
因此,如果这确实是问题所在,只需扫描并删除任何问题示例即可。请记住,传递函数实际上是一个标准化器,因此看起来不同的实际输入向量可能会标准化为同一性。
如果您仍然陷入局部最大值或最小值,请尝试更改 epsilon(学习率)。您已将其硬编码为 .5 尝试其他值。
作为最后的努力,我还建议用阶跃函数替换 sigmoid 传递函数。 sigmoid 只是这个数字函数的生物模拟。直接使用数字传输(阶跃函数)来消除此转换。
在反向传播中使用 sigmoid 的原因是 Hinton 的原创工作来自认知科学,神经元的传递函数是 sigmoid - 最接近数字函数的自然模拟。
关于classification - 多层感知器 - 反向传播,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10018821/