私は単純なdouble XOR=ニューラルネットワークを機能させるために努力しており、本当に単純なフィードフォワードニューラルネットワークをトレーニングするためのバックプロパゲーションの取得に問題があります。
私はニューラルネットワークを取得する際に主に this ガイドに従っていますが、せいぜい非常に遅い速度で学習するプログラムを作成しています。
私がニューラルネットワークを理解しているとき:
これ は、私が動作させようとしている現在のコードです。他にもさまざまな試みが混ざっていますが、私が機能させようとしている主なバックプロパゲーション関数は、Net.cppの293行目です。
ニューラルネットワークを実装するための15の手順 をご覧ください。
以下で確認できる簡単な「チュートリアル」を作成しました。
これは、パーセプトロンモデルの単純な実装です。パーセプトロンは、ニューロンが1つだけのニューラルネットワークと考えることができます。私がC++で記述したテスト可能な呪いのコードがあります。私はコードを一歩一歩順を追って説明するので、問題はありません。
パーセプトロンは実際には「ニューラルネットワーク」ではありませんが、使い始めたい場合に非常に役立ち、完全なニューラルネットワークの仕組みをよりよく理解するのに役立ちます。
お役に立てば幸いです。乾杯! ^ _ ^
この例では、C++でのパーセプトロンモデルの実装について説明します。これにより、それがどのように機能するかをよりよく理解できます。
まず最初に、やりたいことの簡単なアルゴリズムを書き留めておくことをお勧めします。
アルゴリズム:
超シンプルなアルゴリズムを作成したら、次に、必要な関数のいくつかを作成しましょう。
さて、これ以上苦労せずに、すぐに始めましょう。
パーセプトロンクラスを作成することから簡単に始めましょう:
class perceptron
{
public:
private:
};
次に、必要な関数を追加しましょう。
class perceptron
{
public:
perceptron(float eta,int epochs);
float netInput(vector<float> X);
int predict(vector<float> X);
void fit(vector< vector<float> > X, vector<float> y);
private:
};
関数fitがどのようにvector <float>のベクトルを引数として取るかに注意してください。これは、トレーニングデータセットが入力のマトリックスであるためです。基本的に、その行列は、いくつかのベクトルxが別の行列の上に積み重ねられ、その行列の各列が特徴であると想像できます。
最後に、クラスに必要な値を追加しましょう。重みを保持するベクトルwなど、epochsの数は、トレーニングデータセット。そして定数etaは、この値をダイヤルアップするか、または etaが高すぎるため、理想的な結果を得るためにダイヤルダウンできます(パーセプトロンのほとんどのアプリケーションでは、eta値0.1をお勧めします)。
class perceptron
{
public:
perceptron(float eta,int epochs);
float netInput(vector<float> X);
int predict(vector<float> X);
void fit(vector< vector<float> > X, vector<float> y);
private:
float m_eta;
int m_epochs;
vector < float > m_w;
};
これでクラスが設定されました。関数のそれぞれを書く時が来ました。
コンストラクタから始めましょう(perceptron(float eta、int epochs);)
perceptron::perceptron(float eta, int epochs)
{
m_epochs = epochs; // We set the private variable m_epochs to the user selected value
m_eta = eta; // We do the same thing for eta
}
ご覧のとおり、非常に単純な処理を行います。それでは、別の単純な関数に移りましょう。予測関数(int predict(vector X);)。 all predict関数が行うことは、netInputが0より大きい場合、正味入力を取り、値1を返すことを覚えておいてくださいそれ以外の場合は-1。
int perceptron::predict(vector<float> X)
{
return netInput(X) > 0 ? 1 : -1; //Step Function
}
簡単にするために、インラインifステートメントを使用したことに注意してください。インラインifステートメントの機能は次のとおりです。
条件?if_true:else
ここまでは順調ですね。 netInput関数の実装に移りましょう(float netInput(vector X);)
NetInputは次のことを行います。 入力ベクトルに重みベクトルの転置を乗算します
* x * wT *
言い換えると、入力ベクトルxの各要素に重みのベクトルwの対応する要素を乗算してから、それらの合計とバイアスを追加します。
*(x1 * w1 + x2 * w2 + ... + xn * wn)+バイアス*
*バイアス= 1 * w0 *
float perceptron::netInput(vector<float> X)
{
// Sum(Vector of weights * Input vector) + bias
float probabilities = m_w[0]; // In this example I am adding the perceptron first
for (int i = 0; i < X.size(); i++)
{
probabilities += X[i] * m_w[i + 1]; // Notice that for the weights I am counting
// from the 2nd element since w0 is the bias and I already added it first.
}
return probabilities;
}
さて、これでほぼ完了しました。最後に、重みを変更するfit関数を記述する必要があります。
void perceptron::fit(vector< vector<float> > X, vector<float> y)
{
for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term
{
m_w.Push_back(0); // Setting each weight to 0 and making the size of the vector
// The same as the number of features (X[0].size()) + 1 for the bias term
}
for (int i = 0; i < m_epochs; i++) // Iterating through each Epoch
{
for (int j = 0; j < X.size(); j++) // Iterating though each vector in our training Matrix
{
float update = m_eta * (y[j] - predict(X[j])); //we calculate the change for the weights
for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; } // we update each weight by the update * the training sample
m_w[0] = update; // We update the Bias term and setting it equal to the update
}
}
}
本質的にはそれでした。 3つの関数のみで、予測を行うために使用できるパーセプトロンクラスが機能します。
コードをコピーして貼り付けたい場合は、試してください。これがクラス全体です(重みベクトルと各エポックのエラーの印刷などの追加機能を追加し、重みをインポート/エクスポートするオプションを追加しました)。
これがコードです:
クラスヘッダー:
class perceptron
{
public:
perceptron(float eta,int epochs);
float netInput(vector<float> X);
int predict(vector<float> X);
void fit(vector< vector<float> > X, vector<float> y);
void printErrors();
void exportWeights(string filename);
void importWeights(string filename);
void printWeights();
private:
float m_eta;
int m_epochs;
vector < float > m_w;
vector < float > m_errors;
};
関数を含むクラス.cppファイル:
perceptron::perceptron(float eta, int epochs)
{
m_epochs = epochs;
m_eta = eta;
}
void perceptron::fit(vector< vector<float> > X, vector<float> y)
{
for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term
{
m_w.Push_back(0);
}
for (int i = 0; i < m_epochs; i++)
{
int errors = 0;
for (int j = 0; j < X.size(); j++)
{
float update = m_eta * (y[j] - predict(X[j]));
for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; }
m_w[0] = update;
errors += update != 0 ? 1 : 0;
}
m_errors.Push_back(errors);
}
}
float perceptron::netInput(vector<float> X)
{
// Sum(Vector of weights * Input vector) + bias
float probabilities = m_w[0];
for (int i = 0; i < X.size(); i++)
{
probabilities += X[i] * m_w[i + 1];
}
return probabilities;
}
int perceptron::predict(vector<float> X)
{
return netInput(X) > 0 ? 1 : -1; //Step Function
}
void perceptron::printErrors()
{
printVector(m_errors);
}
void perceptron::exportWeights(string filename)
{
ofstream outFile;
outFile.open(filename);
for (int i = 0; i < m_w.size(); i++)
{
outFile << m_w[i] << endl;
}
outFile.close();
}
void perceptron::importWeights(string filename)
{
ifstream inFile;
inFile.open(filename);
for (int i = 0; i < m_w.size(); i++)
{
inFile >> m_w[i];
}
}
void perceptron::printWeights()
{
cout << "weights: ";
for (int i = 0; i < m_w.size(); i++)
{
cout << m_w[i] << " ";
}
cout << endl;
}
また、例を試してみたい場合は、ここに私が作った例があります:
main.cpp:
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <string>
#include <math.h>
#include "MachineLearning.h"
using namespace std;
using namespace MachineLearning;
vector< vector<float> > getIrisX();
vector<float> getIrisy();
int main()
{
vector< vector<float> > X = getIrisX();
vector<float> y = getIrisy();
vector<float> test1;
test1.Push_back(5.0);
test1.Push_back(3.3);
test1.Push_back(1.4);
test1.Push_back(0.2);
vector<float> test2;
test2.Push_back(6.0);
test2.Push_back(2.2);
test2.Push_back(5.0);
test2.Push_back(1.5);
//printVector(X);
//for (int i = 0; i < y.size(); i++){ cout << y[i] << " "; }cout << endl;
perceptron clf(0.1, 14);
clf.fit(X, y);
clf.printErrors();
cout << "Now Predicting: 5.0,3.3,1.4,0.2(CorrectClass=-1,Iris-setosa) -> " << clf.predict(test1) << endl;
cout << "Now Predicting: 6.0,2.2,5.0,1.5(CorrectClass=1,Iris-virginica) -> " << clf.predict(test2) << endl;
system("PAUSE");
return 0;
}
vector<float> getIrisy()
{
vector<float> y;
ifstream inFile;
inFile.open("y.data");
string sampleClass;
for (int i = 0; i < 100; i++)
{
inFile >> sampleClass;
if (sampleClass == "Iris-setosa")
{
y.Push_back(-1);
}
else
{
y.Push_back(1);
}
}
return y;
}
vector< vector<float> > getIrisX()
{
ifstream af;
ifstream bf;
ifstream cf;
ifstream df;
af.open("a.data");
bf.open("b.data");
cf.open("c.data");
df.open("d.data");
vector< vector<float> > X;
for (int i = 0; i < 100; i++)
{
char scrap;
int scrapN;
af >> scrapN;
bf >> scrapN;
cf >> scrapN;
df >> scrapN;
af >> scrap;
bf >> scrap;
cf >> scrap;
df >> scrap;
float a, b, c, d;
af >> a;
bf >> b;
cf >> c;
df >> d;
X.Push_back(vector < float > {a, b, c, d});
}
af.close();
bf.close();
cf.close();
df.close();
return X;
}
アイリスデータセットのインポート方法はあまり理想的ではありませんが、機能するものが欲しかっただけです。
データファイルは次の場所にあります here。
この情報がお役に立てば幸いです。
注:上記のコードは一例です。 juzzlinで述べたように、const vector<float> &X
を使用し、一般にvector
/vector<vector>
オブジェクトを参照で渡すことが重要です。これは、データが非常に大きくなり、値で渡すと、それのコピー(これは非効率的です)。
あなたがバックプロップに苦労しているように聞こえますが、上記で説明した内容は、私がそれを機能させるために理解している方法と完全には一致しておらず、あなたの説明は少しあいまいです。
予測と実際の値の差に伝達関数の導関数を掛けたものとして、バックプロパゲートする出力エラー項を計算します。その後、後方に伝播するのはそのエラー値です。シグモイドの導関数は、y(1-y)として簡単に計算されます。ここで、yは出力値です。Webには、その証明がたくさんあります。
内層のノードの場合、その出力エラーに2つのノード間の重みを掛け、それらすべての積を、外層から内層のノードに伝播される合計エラーとして合計します。次に、内部ノードに関連する誤差に、元の出力値に適用された伝達関数の導関数が乗算されます。ここにいくつかの擬似コードがあります:
total_error = sum(output_errors * weights)
node_error = sigmoid_derivative(node_output) * total_error
このエラーは、入力レイヤーの重みを介して、同じ方法で後方に伝播されます。
重みはこれらの誤差項とノードの出力値を使用して調整されます
weight_change = outer_error * inner_output_value
入力データのすべてのパターン/行/観測について重みの変化が計算されるため、学習率は重要です。各行の重みの変更をモデレートして、重みが単一の行によって過度に変更されないようにし、すべての行が重みに影響を与えるようにします。学習率はあなたにそれを与え、あなたはそれを掛けることによって体重変化を調整します
weight_change = outer_error * inner_output_value * learning_rate
エポック(反復)間のこれらの変更を記憶し、その一部を変更に追加することも通常です。追加された部分は運動量と呼ばれ、あまり変化のないエラーサーフェスの領域を高速化し、細部がある場合は低速化するはずです。
weight_change = (outer_error*inner_output_value*learning_rate) + (last_change*momentum)
トレーニングの進行に合わせて学習率と勢いを調整するアルゴリズムがあります。
次に、変更を追加することで重みが更新されます
new_weight = old_weight + weight_change
私はあなたのコードを調べましたが、修正して投稿するのではなく、自分でコードを記述できるように、あなたのためにプロップを説明する方が良いと思いました。それを理解すれば、状況に合わせて調整することもできます。
HTHと幸運。
このオープンソースコードはどうですか。単純な1つの非表示レイヤーネット(2つの入力、2つの非表示、1つの出力)を定義し、XOR問題を解決します。