希望这里有人能帮我:我正在尝试实现一个神经网络来寻找数据的聚类,这些数据以二维聚类的形式呈现。我试图按照维基百科上描述的标准算法进行操作:我寻找每个数据点的最小距离,并将这个神经元的权重更新为朝向该数据点。我会在总距离足够小时停止这个过程。
我的结果是找到了大部分聚类,但在一些聚类上出现了错误,尽管它计算了一个恒定的距离,但不再收敛。我的错误出在哪里?
typedef struct{ double x; double y;}Data;typedef struct{ double x; double y;}Neuron;typedef struct{ size_t numNeurons; Neuron* neurons;}Network;int main(void){ srand(time(NULL)); Data trainingData[1000]; size_t sizeTrainingData = 0; size_t sizeClasses = 0; Network network; getData(trainingData, &sizeTrainingData, &sizeClasses); initializeNetwork(&network, sizeClasses); normalizeData(trainingData, sizeTrainingData); train(&network, trainingData, sizeTrainingData); return 0;}void train(Network* network, Data trainingData[], size_t sizeTrainingData){ for(int epoch=0; epoch<TRAINING_EPOCHS; ++epoch){ double learningRate = getLearningRate(epoch); double totalDistance = 0; for(int i=0; i<sizeTrainingData; ++i){ Data currentData = trainingData[i]; int winningNeuron = 0; totalDistance += findWinningNeuron(network, currentData, &winningNeuron); //更新权重 network->neurons[i].x += learningRate * (currentData.x - network->neurons[i].x); network->neurons[i].y += learningRate * (currentData.y - network->neurons[i].y); } if(totalDistance<MIN_TOTAL_DISTANCE) break; }}double getLearningRate(int epoch){ return LEARNING_RATE * exp(-log(LEARNING_RATE/LEARNING_RATE_MIN_VALUE)*((double)epoch/TRAINING_EPOCHS));}double findWinningNeuron(Network* network, Data data, int* winningNeuron){ double smallestDistance = 9999; for(unsigned int currentNeuronIndex=0; currentNeuronIndex<network->numNeurons; ++currentNeuronIndex){ Neuron neuron = network->neurons[currentNeuronIndex]; double distance = sqrt(pow(data.x-neuron.x,2)+pow(data.y-neuron.y,2)); if(distance<smallestDistance){ smallestDistance = distance; *winningNeuron = currentNeuronIndex; } } return smallestDistance;}
initializeNetwork(...)
用-1到1之间的随机权重初始化所有神经元。normalizeData(...)
以最大值为1的方式进行归一化处理。
一个例子:如果我向网络输入大约50个(归一化后的)数据点,这些数据点分成3个聚类,剩余的totaldistance
保持在约7.3。当我检查应该代表聚类中心的神经元位置时,有两个是完美的,一个在聚类的边缘。难道算法不应该将其更多地移向中心吗?我多次重复了这个算法,输出总是相似的(在完全相同的错误点上)
回答:
你的代码看起来不像是LVQ,特别是你从未使用获胜的神经元,而你应该只移动这个神经元
void train(Network* network, Data trainingData[], size_t sizeTrainingData){ for(int epoch=0; epoch<TRAINING_EPOCHS; ++epoch){ double learningRate = getLearningRate(epoch); double totalDistance = 0; for(int i=0; i<sizeTrainingData; ++i){ Data currentData = trainingData[i]; int winningNeuron = 0; totalDistance += findWinningNeuron(network, currentData, &winningNeuron); //更新权重 network->neurons[i].x += learningRate * (currentData.x - network->neurons[i].x); network->neurons[i].y += learningRate * (currentData.y - network->neurons[i].y); } if(totalDistance<MIN_TOTAL_DISTANCE) break; }}
你要移动的神经元在winningNeuron
中,但你更新的是第i
个神经元,而i
实际上是在遍历训练样本,我很惊讶你没有超出内存(network->neurons应该小于sizeTrainingData)。我猜你原本的意思是这样的
void train(Network* network, Data trainingData[], size_t sizeTrainingData){ for(int epoch=0; epoch<TRAINING_EPOCHS; ++epoch){ double learningRate = getLearningRate(epoch); double totalDistance = 0; for(int i=0; i<sizeTrainingData; ++i){ Data currentData = trainingData[i]; int winningNeuron = 0; totalDistance += findWinningNeuron(network, currentData, &winningNeuron); //更新权重 network->neurons[winningNeuron].x += learningRate * (currentData.x - network->neurons[winningNeuron].x); network->neurons[winningNeuron].y += learningRate * (currentData.y - network->neurons[winningNeuron].y); } if(totalDistance<MIN_TOTAL_DISTANCE) break; }}