machine-learning
パーセプトロン
サーチ…
パーセプトロンとは何ですか?
その中心にあるパーセプトロンモデルは、 バイナリ分類のための最も簡単な教師付き学習アルゴリズムの1つです。 線形分類器の一種であり、線形予測子関数に基づく予測を特徴ベクトルと組み合わせる分類アルゴリズムである。より直感的な考え方は、 ニューロンが1つしかないニューラルネットワークのようなものです。
それが動作する方法は非常に簡単です。各要素がデータセットの特徴である入力値xのベクトルを取得します。
例:
オブジェクトが自転車か車かを分類したいとします。この例では、2つのフィーチャを選択したいとします。オブジェクトの高さと幅。その場合、 x = [x1、x2]ここで、 x1は高さであり、 x2は幅です。
そして、入力ベクトルxをいったん取得すると、そのベクトルの各要素に重みを掛けることになります。通常、体重の値が高いほど特徴が重要です。例えば、特徴x3として色を使用し、赤色の自転車と赤い車がある場合、パーセプトロンは、色が最終予測に影響を与えないように、パーセプトロンの重みを非常に低く設定します。
さて、2つのベクトルxとwを掛け合わせ、ベクトルを返しました。今度は、このベクトルの要素を合計する必要があります。これを行うスマートな方法は、 xをw倍に単純にするのではなく、 xにwTを掛けることができます。ここで、 Tは転置を表します。ベクトルの回転したバージョンとしてのベクトルの転置を想像することができます。詳細については、Wikipediaのページをご覧ください 。基本的に、我々は代わりの1xNのNx1のベクトルを取得するベクトルwの転置を取ることによって。したがって、入力ベクトルを1xNのサイズでこのNx1ウェイトベクトルに乗算すると、 x1 * w1 + x2 * w2 + ... + xn * wnに等しくなる1x1ベクトル(または単なる値)が得られます。それをして、我々は今我々の予測を持っている。しかし、最後に1つのことがあります。この予測は、新しいサンプルを分類するためには、おそらく単純な1または-1ではありません。つまり、予測が0より大きい場合、サンプルはクラス1に属し、そうでなければ予測がゼロよりも小さい場合、サンプルはクラス-1に属していると言います。これはステップ関数と呼ばれます 。
しかし、正しい予測をするためには、どのようにして正しい重みを得るのでしょうか?つまり、パーセプトロンモデルをどのように訓練するのでしょうか?
パーセプトロンの場合、私たちのモデルを訓練するためのファンシーな数式は必要ありません。私たちの体重は次の式で調整することができます:
Δw=η*(y予測)* x(i)
ここで、 x(i)は私たちの特徴です(たとえばx1は重み1、x2はw2など)。
また、学習率であるetaという変数があることに気付きました。学習率は、体重の変化がどれくらい大きくなるかを想像することができます。良い学習率は、速い学習アルゴリズムをもたらす。 ηの値が高すぎると、各エポックで誤差が増加し、モデルが実際に悪い予測をして収束しない結果になります。学習率が低すぎると、モデルが収束するのに時間がかかりすぎる可能性があります。 (通常、パーセプトロンモデルのηを設定するのに良い値は0.1ですが、ケースごとに異なる場合があります)。
最後に、最初の入力が定数(1)でw0が乗算されていることに気づいた人もいます。だからそれはどういう意味ですか?良い予測を得るためにバイアスを追加する必要があります。それはまさにその定数です。
バイアス項の重みを変更するには、他の重みに対して行ったのと同じ式を使用しますが、この場合は入力にそれを掛けません(入力は定数1なので、そうする必要はありません)。
Δw=η*(y予測)
それは基本的に単純なパーセプトロンモデルの仕組みです。いったん体重を鍛えると、新しいデータを与えて予測することができます。
注意:
パーセプトロンモデルには大きな欠点があります。データが直線的に分離できない場合、それは収束しません(完璧な重みを見つける)。これは、フィーチャ空間内の2つのクラスを直線で分けることができることを意味します。そのため、固定された回数の反復を追加して、完全にチューニングされないウェイトの調整にモデルが詰まっていないようにすることを避けるために、
C ++でのPerceptronモデルの実装
この例では、C ++でのパーセプトロンモデルの実装について説明します。これにより、パーセプトロンモデルの動作をよりよく理解できるようになります。
まず最初に、私たちがやりたいことの簡単なアルゴリズムを書き留めることは良い習慣です。
アルゴリズム:
- ウェイトのベクトルを作成し、0に初期化する(バイアス項を追加することを忘れないでください)
- 0のエラーまたは低いエラーカウントが得られるまで、重みを調整し続けます。
- 見えないデータを予測する。
超簡単なアルゴリズムを書いたら、必要な関数のいくつかを書きましょう。
- ネットの入力を計算する関数が必要です(ei x * wTは入力にウェイトを乗じます)
- 1または-1の予測を得るためのステップ関数
- そして、重みの理想的な値を見つける関数。
だから、それ以上の苦労なしに、それに入るようにしましょう。
パーセプトロンクラスを作成することで簡単に始めましょう:
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>のベクトルを引数として取る方法に注目してください。それは、私たちのトレーニングデータセットがインプットのマトリックスであるからです。本質的には、ベクトルが2つのベクトルxとして行列を積み重ね、その行列を別の行列の上に積み重ね、その行列の各列が特徴であると想像することができます。
最後に、クラスに必要な値を追加しましょう。ウェイトを保持するベクトルwのように、トレーニングデータセットに対して実行するパス数を示すエポックの数です。そして、この値をダイヤルすることによって訓練手順を速くするために、またはηが高すぎる場合には、それをダイヤルして理想的な結果を得るために、各体重更新を掛ける学習率である一定のη (ほとんどのアプリケーションパーセプトロンIのη値は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予測(ベクトルX); )。 netInputが0より大きい場合は-1を、それ以外なら-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)+ bias
バイアス= 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;
}
さて、最後にやったことは、重みを変更するフィット関数を書くことです。
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;
}
私がアイリスデータセットをインポートした方法は本当に理想的ではありませんが、私はちょうど働いたものがほしいと思っていました。
データファイルはここにあります。
私はあなたがこれが役に立つと思ったことを願っています
バイアスとは何ですか?
バイアスとは何ですか?
パーセプトロンは、入力(実数値)ベクトルxを出力値f(x) (バイナリ値)にマッピングする関数として見ることができます。
wは実数重みのベクトルであり、 bはバイアス値である。偏りは、決定境界を原点(0,0)からずらし、入力値に依存しない値です。
空間的な偏りを考えると、バイアスは決定境界の位置(向きではないが)を変更する。以下に、バイアスによってシフトされた同じカーブの例を示します。