ANSI Cでのパーセプトロンの実装は次のとおりです。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
float randomFloat()
{
srand(time(NULL));
float r = (float)Rand() / (float)Rand_MAX;
return r;
}
int calculateOutput(float weights[], float x, float y)
{
float sum = x * weights[0] + y * weights[1];
return (sum >= 0) ? 1 : -1;
}
int main(int argc, char *argv[])
{
// X, Y coordinates of the training set.
float x[208], y[208];
// Training set outputs.
int outputs[208];
int i = 0; // iterator
FILE *fp;
if ((fp = fopen("test1.txt", "r")) == NULL)
{
printf("Cannot open file.\n");
}
else
{
while (fscanf(fp, "%f %f %d", &x[i], &y[i], &outputs[i]) != EOF)
{
if (outputs[i] == 0)
{
outputs[i] = -1;
}
printf("%f %f %d\n", x[i], y[i], outputs[i]);
i++;
}
}
system("PAUSE");
int patternCount = sizeof(x) / sizeof(int);
float weights[2];
weights[0] = randomFloat();
weights[1] = randomFloat();
float learningRate = 0.1;
int iteration = 0;
float globalError;
do {
globalError = 0;
int p = 0; // iterator
for (p = 0; p < patternCount; p++)
{
// Calculate output.
int output = calculateOutput(weights, x[p], y[p]);
// Calculate error.
float localError = outputs[p] - output;
if (localError != 0)
{
// Update weights.
for (i = 0; i < 2; i++)
{
float add = learningRate * localError;
if (i == 0)
{
add *= x[p];
}
else if (i == 1)
{
add *= y[p];
}
weights[i] += add;
}
}
// Convert error to absolute value.
globalError += fabs(localError);
printf("Iteration %d Error %.2f %.2f\n", iteration, globalError, localError);
iteration++;
}
system("PAUSE");
} while (globalError != 0);
system("PAUSE");
return 0;
}
私が使用しているトレーニングセット: Data Set
関係のないコードはすべて削除しました。基本的には今何をしているのかtest1.txt
ファイルを作成し、その値を3つの配列x
、y
、outputs
にロードします。
それから パーセプトロン学習アルゴリズム があり、これは何らかの理由で0に収束していません(globalError
は0に収束する必要があります)。したがって、無限のdo whileループが発生します。
小さいトレーニングセット(5ポイントなど)を使用すると、かなりうまく機能します。問題となる可能性のあるアイデアはありますか?
私はこれに非常によく似たこのアルゴリズムを書きました C#パーセプトロンアルゴリズム :
編集:
以下は、より小さいトレーニングセットの例です。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
float randomFloat()
{
float r = (float)Rand() / (float)Rand_MAX;
return r;
}
int calculateOutput(float weights[], float x, float y)
{
float sum = x * weights[0] + y * weights[1];
return (sum >= 0) ? 1 : -1;
}
int main(int argc, char *argv[])
{
srand(time(NULL));
// X coordinates of the training set.
float x[] = { -3.2, 1.1, 2.7, -1 };
// Y coordinates of the training set.
float y[] = { 1.5, 3.3, 5.12, 2.1 };
// The training set outputs.
int outputs[] = { 1, -1, -1, 1 };
int i = 0; // iterator
FILE *fp;
system("PAUSE");
int patternCount = sizeof(x) / sizeof(int);
float weights[2];
weights[0] = randomFloat();
weights[1] = randomFloat();
float learningRate = 0.1;
int iteration = 0;
float globalError;
do {
globalError = 0;
int p = 0; // iterator
for (p = 0; p < patternCount; p++)
{
// Calculate output.
int output = calculateOutput(weights, x[p], y[p]);
// Calculate error.
float localError = outputs[p] - output;
if (localError != 0)
{
// Update weights.
for (i = 0; i < 2; i++)
{
float add = learningRate * localError;
if (i == 0)
{
add *= x[p];
}
else if (i == 1)
{
add *= y[p];
}
weights[i] += add;
}
}
// Convert error to absolute value.
globalError += fabs(localError);
printf("Iteration %d Error %.2f\n", iteration, globalError);
}
iteration++;
} while (globalError != 0);
// Display network generalisation.
printf("X Y Output\n");
float j, k;
for (j = -1; j <= 1; j += .5)
{
for (j = -1; j <= 1; j += .5)
{
// Calculate output.
int output = calculateOutput(weights, j, k);
printf("%.2f %.2f %s\n", j, k, (output == 1) ? "Blue" : "Red");
}
}
// Display modified weights.
printf("Modified weights: %.2f %.2f\n", weights[0], weights[1]);
system("PAUSE");
return 0;
}
現在のコードでは、 perceptron が決定境界の方向を正常に学習しますが、それを変換できません。
y y ^ ^ | -+ \\ + | -\\ + + | -+ \\ + + | -\\ + + + | --\\ + | --\\ + | --+ \\ + | --\\ + + ---------------------> x ---------------- ----> x このように動かなくなった場合、このようにする必要があります
(誰かが指摘したように、これが より正確なバージョン )です
問題は、パーセプトロンにバイアスがないバイアス項、つまり、値1の入力に接続された3番目の重みコンポーネントがあるという事実にあります。
w0 ----- x ----> | | | f | ---->出力(+ 1/-1) y ----> | | w1 ----- ^ w2 1(bias)
以下は私が問題を修正した方法です:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define LEARNING_RATE 0.1
#define MAX_ITERATION 100
float randomFloat()
{
return (float)Rand() / (float)Rand_MAX;
}
int calculateOutput(float weights[], float x, float y)
{
float sum = x * weights[0] + y * weights[1] + weights[2];
return (sum >= 0) ? 1 : -1;
}
int main(int argc, char *argv[])
{
srand(time(NULL));
float x[208], y[208], weights[3], localError, globalError;
int outputs[208], patternCount, i, p, iteration, output;
FILE *fp;
if ((fp = fopen("test1.txt", "r")) == NULL) {
printf("Cannot open file.\n");
exit(1);
}
i = 0;
while (fscanf(fp, "%f %f %d", &x[i], &y[i], &outputs[i]) != EOF) {
if (outputs[i] == 0) {
outputs[i] = -1;
}
i++;
}
patternCount = i;
weights[0] = randomFloat();
weights[1] = randomFloat();
weights[2] = randomFloat();
iteration = 0;
do {
iteration++;
globalError = 0;
for (p = 0; p < patternCount; p++) {
output = calculateOutput(weights, x[p], y[p]);
localError = outputs[p] - output;
weights[0] += LEARNING_RATE * localError * x[p];
weights[1] += LEARNING_RATE * localError * y[p];
weights[2] += LEARNING_RATE * localError;
globalError += (localError*localError);
}
/* Root Mean Squared Error */
printf("Iteration %d : RMSE = %.4f\n",
iteration, sqrt(globalError/patternCount));
} while (globalError > 0 && iteration <= MAX_ITERATION);
printf("\nDecision boundary (line) equation: %.2f*x + %.2f*y + %.2f = 0\n",
weights[0], weights[1], weights[2]);
return 0;
}
...次の出力が表示されます。
Iteration 1 : RMSE = 0.7206
Iteration 2 : RMSE = 0.5189
Iteration 3 : RMSE = 0.4804
Iteration 4 : RMSE = 0.4804
Iteration 5 : RMSE = 0.3101
Iteration 6 : RMSE = 0.4160
Iteration 7 : RMSE = 0.4599
Iteration 8 : RMSE = 0.3922
Iteration 9 : RMSE = 0.0000
Decision boundary (line) equation: -2.37*x + -2.51*y + -7.55 = 0
そして、MATLABを使用した上記のコードの短いアニメーションを次に示します。各反復での---(決定境界 を示しています。
randomFloat
を呼び出すたびに再シードするのではなく、ランダムジェネレーターのシードをyout mainの先頭に配置すると役立つ場合があります。
float randomFloat()
{
float r = (float)Rand() / (float)Rand_MAX;
return r;
}
// ...
int main(int argc, char *argv[])
{
srand(time(NULL));
// X, Y coordinates of the training set.
float x[208], y[208];
私があなたのソースコードで見つけたいくつかの小さなエラー:
int patternCount = sizeof(x) / sizeof(int);
これを次のように変更してください
int patternCount = i;
そのため、適切なサイズにするためにx配列に依存する必要はありません。
Pループ内で反復を増やしますが、元のC#コードはpループ外でこれを行います。 printfとiteration ++をPAUSEステートメントの前のpループの外に移動することをお勧めします。また、PAUSEステートメントを削除するか、次のように変更します。
if ((iteration % 25) == 0) system("PAUSE");
これらすべての変更を行っても、プログラムはデータセットを使用して終了しませんが、出力はより一貫性があり、56〜60のどこかでエラーが発生します。
最後に試すことができるのは、このデータセットで元のC#プログラムをテストすることです。それも終了しない場合は、アルゴリズムに問題があります(データセットが正しいように見えるため、視覚化コメントを参照してください)。
globalError
はゼロにならず、converge toゼロになります。つまり、非常に大きくなります小さい。
ループを次のように変更します。
int maxIterations = 1000000; //stop after one million iterations regardless
float maxError = 0.001; //one in thousand points in wrong class
do {
//loop stuff here
//convert to fractional error
globalError = globalError/((float)patternCount);
} while ((globalError > maxError) && (i<maxIterations));
問題に該当するmaxIterations
およびmaxError
値を指定します。