Chapter 13 - Jaringan Back Propagtion Berulang (Time Cycling)






1. Jaringan Waktu Berulang / Diskrit [kembali]

       Struktur berulang dapat dimasukkan ke dalam jaringan saraf propagasi belakang dengan mengumpankan kembali output jaringan ke input setelah masa pembelajaran telah selesai. Fitur berulang ini dalam langkah-langkah terpisah (siklus) perhitungan berat. Ini pertama kali diusulkan oleh Rumelhartet al. (1986) dan kemudian oleh Pineda (1988), Hecht Nielson (1990) dan oleh Hertzet al. (1991). Pengaturan ini memungkinkan penggunaan propagasi belakang dengan sejumlah kecil lapisan tersembunyi (dan karenanya bobot) dengan cara yang secara efektif setara dengan menggunakan kali-banyak lapisan yang banyak jika sepeda dari komputasi berulang digunakan [lih. Fausett, 1993].

      Jaringan propagasi balik berulang (siklus waktu) dijelaskan pada Gambar 13.1. Elemen penundaan (Din Gambar 13.1) dalam loop umpan balik terpisah antara langkah-langkah waktu (zaman, yang biasanya sesuai dengan iterasi tunggal). Pada akhir zaman pertama, output diumpankan kembali ke input. Atau, seseorang dapat mengumpan balik kesalahan output saja di akhir setiap zaman, untuk berfungsi sebagai input untuk zaman berikutnya.
       Jaringan Gambar 13.1 menerima input x1 dan x2 pada berbagai langkah waktu dari satu urutan lengkap (set) yang merupakan zaman pertama (siklus). Bobot dihitung seperti dalam jaringan propagasi balik konvensional dan dijumlahkan pada semua langkah waktu dari suatu zaman tanpa penyesuaian bobot yang sebenarnya sampai akhir zaman itu. Pada setiap langkah waktu, output y1 dan y2 diumpankan kembali untuk digunakan sebagai input untuk langkah waktu berikutnya. Pada akhir satu pemindaian lengkap semua input, zaman berikutnya dimulai dengan pemindaian lengkap baru dari input yang sama dan langkah-langkah waktu seperti pada zaman sebelumnya. Ketika jumlah input berbeda dari jumlah output, maka struktur Gambar 13.2 dapat digunakan.

       Kedua struktur dalam Gambar. 13.1 dan 13.2 setara dengan struktur di mana jaringan dasar (kecuali untuk umpan balik dari satu langkah waktu ke langkah lain) diulangi m-kali, untuk memperhitungkan langkah-langkah waktu dalam struktur berulang. Lihat Gambar 13.3.


2. Jaringan Berulang Sepenuhnya [kembali]

        Jaringan yang berulang sepenuhnya mirip dengan jaringan Sec. 13.1 kecuali bahwa setiap layer diumpankan kembali ke setiap lapisan sebelumnya, seperti pada Gambar 13.4 (daripada mengumpankan kembali dari output jaringan n-layer ke input jaringan, seperti dalam Bab 13.1). Sekarang output di setiap zaman menjadi input ke neuron berulang di zaman berikutnya.



3. Jaringan Back Propagation Berulang-Ulang [kembali]

       Jaringan saraf berbasis propagasi balik yang berulang secara terus menerus menggunakan struktur yang sama seperti pada Gambar. 13.1 dan 13.2 tetapi recurrency diulangi dalam interval waktu yang sangat kecil. Oleh karena itu, recurrency mematuhi perkembangan persamaan diferensial seperti pada jaringan Hopfield berkelanjutan, yaitu


di mana Ï„ adalah koefisien konstanta waktu, xi menjadi input eksternal, g (···) menunjukkan fungsi aktivasi, yi menunjukkan output dan vj menjadi output dari neuron lapisan tersembunyi. Untuk stabilitas diperlukan setidaknya satu solusi stabil Persamaan. (13.1) ada, yaitu




4. Studi Kasus Back Propagation Berulang: Character
Recognition [kembali]


   4.1.  Pengenalan

       Studi kasus ini berkenaan dengan memecahkan masalah pengenalan karakter sederhana menggunakan jaringan saraf propagasi kembali berulang. Tugasnya adalah untuk mengajarkan jaringan saraf untuk mengenali 3 karakter, yaitu, memetakan mereka ke pasangan masing-masing {0,1}, {1,0} dan {1,1}. Jaringan juga harus menghasilkan sinyal kesalahan 0,0 khusus dalam menanggapi karakter lain.

   4.2.  Desain jaringan saraf

Struktur: Jaringan saraf terdiri dari tiga lapisan dengan masing-masing 2 neuron, satu lapisan keluaran dan dua lapisan tersembunyi. Ada 36 input reguler ke jaringan dan 2 input yang terhubung ke 2 kesalahan output. Dengan demikian, secara total ada 38 input ke jaringan saraf. Jaringan saraf seperti pada Sec. 6.A, kecuali bahwa itu adalah jaringan berulang, sehingga outputnya y1 dan y2 diumpankan kembali sebagai input tambahan pada akhir setiap iterasi. Istilah Bias (sama dengan 1) dengan bobot yang dapat dilatih juga termasuk dalam struktur jaringan. Diagram struktural dari jaringan saraf kita diberikan pada Gambar. 13.A.1.


(a) Desain Dataset: Jaringan saraf dirancang untuk mengenali karakter ‘A’, ‘B’, dan ‘C’. Untuk melatih jaringan menghasilkan sinyal kesalahan, kami akan menggunakan 6 karakter lain: ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘H’, dan ‘I’. Untuk memeriksa apakah jaringan telah belajar mengenali kesalahan, kami akan menggunakan karakter ‘X’, ‘Y’, dan ‘Z’. Perhatikan bahwa kami tertarik untuk memeriksa respons jaringan terhadap kesalahan pada karakter yang tidak terlibat dalam prosedur pelatihan. Karakter yang akan dikenali diberikan pada kisi 6 × 6. Masing-masing dari 36 piksel diatur ke 0 atau 1. Matriks 6 × 6 yang sesuai adalah sebagai berikut:

 

(B) Pengaturan Berat: Belajar propagasi kembali digunakan untuk memecahkan masalah. Tujuan dari algoritma ini adalah untuk meminimalkan energi kesalahan pada lapisan output. Pengaturan berat badan seperti pada Back-Propagation, Sec. 6.2 dari Bab. 6 di atas.
 

Kode sumber untuk studi kasus ini (ditulis dalam C ++) diberikan dalam Sec. 13.A.5.

   4.3.  Desain jaringan saraf

(a) Mode Pelatihan
Untuk melatih jaringan untuk mengenali karakter-karakter di atas, kami menerapkan kisi 6 × 6 yang sesuai dalam bentuk vektor 1 × 36 ke input jaringan. Tambahan dua input pada awalnya ditetapkan sama dengan nol dan selama prosedur pelatihan ditetapkan sama dengan kesalahan keluaran saat ini. Karakter dianggap diakui jika kedua output jaringan tidak lebih dari 0,1 dari nilai yang diinginkan masing-masing. Tingkat pembelajaran awal secara eksperimental ditetapkan pada 1,5 dan menurun dengan faktor 2 setelah setiap iterasi ke-100. Seperti halnya dalam propagasi balik reguler (Bagian 6.A), setelah setiap iterasi ke-400 kita mereset tingkat pembelajaran ke nilai awal, untuk mencegah proses pembelajaran dari terjebak pada minimum lokal. Kemudian setelah sekitar 3000 iterasi kami dapat mengenali semua set data dengan benar. Kami, bagaimanapun, melanjutkan sampai 5000 iterasi diselesaikan untuk memastikan bahwa nilai kesalahan energi tidak dapat diturunkan lebih jauh. Pada titik ini kami memperoleh:


Vektor pelatihan 0,1, ..., 8 dalam entri log ini sesuai dengan karakter ‘A’, ‘B’,. . . , ‘I’.

(b) Hasil Pengakuan (tes berjalan)

Deteksi Kesalahan: Untuk memeriksa kinerja deteksi kesalahan, kami menyimpan bobot yang diperoleh ke dalam file data, memodifikasi dataset dalam program menggantikan karakter 'G', 'H' dan 'I' (pelatihan vektor 6, 7 dan 8) oleh karakter 'X', 'Y' dan 'Z'. Kemudian kami menjalankan program, memuat bobot yang sebelumnya disimpan dari file data dan menerapkan input ke jaringan. Perhatikan bahwa kami tidak melakukan pelatihan lebih lanjut.
 

Kami mendapat hasil sebagai berikut:



Ketiga karakter berhasil dipetakan ke sinyal kesalahan {0,0}.
Robustness: Untuk menyelidiki seberapa kuat jaringan saraf kami, kami menambahkan beberapa noise ke input dan mendapatkan hasil berikut. Dalam kasus distorsi 1-bit (dari 36 bit) tingkat pengakuannya adalah:



Dengan 2 bit kesalahan per karakter, kinerjanya bahkan lebih buruk.


   4.4.  Diskusi dan kesimpulan

       Kami dapat melatih jaringan saraf kami sehingga berhasil mengenali tiga karakter yang diberikan dan pada saat yang sama dapat mengklasifikasikan karakter lain sebagai kesalahan. Namun, hasilnya tidak spektakuler untuk dataset input terdistorsi. Karakter 'A', 'B' dan 'C', yang dilatih oleh jaringan kami, berhasil dikenali dengan distorsi 1 dan 2 bit (dengan kemungkinan pengecualian dari karakter 'A' tetapi dapat ditingkatkan dengan meningkatkan jumlah iterasi ). Tetapi pengakuan akan karakter 'sisa dunia' tidak bagus.
 

       Membandingkan hasil ini dengan hasil yang dicapai menggunakan propagasi balik murni, kita dapat melihat bahwa untuk masalah khusus ini, jika bit noise ditambahkan ke data, recurrency memperburuk hasil kinerja pengenalan dibandingkan dengan Propagasi Kembali reguler (tidak berulang). Juga, karena pengenalan input berulang kami harus meningkatkan jumlah total input dengan dua. Ini menghasilkan peningkatan jumlah bobot dalam jaringan dan, karenanya, dalam pembelajaran yang agak lambat.


   4.5.  Source Code(C++)

/*
*/
#include<cmath>
#include<iostream>
#include<fstream>
using namespace std;
#define N_DATASETS 9
#define N_INPUTS 38
#define N_OUTPUTS 2
#define N_LAYERS 3
// {# inputs, # of neurons in L1, # of neurons in L2, # of neurons in
// L3}
short conf[4] = {N_INPUTS, 2, 2, N_OUTPUTS};
// According to the number of layers double **w[3], *z[3], *y[3], *Fi[3], eta; ofstream
ErrorFile("error.txt", ios::out);
// 3 training sets; inputs 36 and 37 (starting from 0) will be used
// for feeding back the output error bool dataset[N_DATASETS][N_INPUTS] = {
{0,0,1,1,0,0, //‘A’
0, 1, 0, 0, 1, 0,
1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 0, 0},
{1,1,1,1,1,0, //‘B’
9
1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0,
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0, 0, 0},
{0,1,1,1,1,1, //‘C’
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0},
{1,1,1,1,1,0, //‘D’
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0, 0, 0},
{1,1,1,1,1,1, //‘E’
1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 0, 0},
{1,1,1,1,1,1, //‘F’
1, 0, 0, 0, 0, 0,

1, 1, 1, 1, 1, 1,
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0},
{0,1,1,1,1,1, //‘G’
1, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0,
1, 0, 1, 1, 1, 1,
1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 0, 0},
{1,0,0,0,0,1, //‘H’
1, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1,
10
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 0, 0},
{0,0,1,1,1,0, //‘I’
0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 1, 1, 1, 0, 0, 0}
// Below are the datasets for checking "the rest of the world". They
// are not the ones the NN was trained on.
/*
{1,0,0,0,0,1, //‘X’
0, 1, 0, 0, 1, 0,
0, 0, 1, 1, 0, 0,
0, 0, 1, 1, 0, 0,
0, 1, 0, 0, 1, 0,
1, 0, 0, 0, 0, 1, 0, 0},
{0,1,0,0,0,1, //‘Y’
0, 0, 1, 0, 1, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0},
{1,1,1,1,1,1, //‘Z’
0, 0, 0, 0, 1, 0,
0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0,
0, 1, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 0, 0}*/
},
datatrue[N_DATASETS][N_OUTPUTS] = {{0,1}, {1,0}, {1,1},
{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}};
// Memory allocation and initialization function void MemAllocAndInit(char S)
{
if(S == ‘A’)
for(int i = 0; i < N_LAYERS; i++)
11
 

{
w[i] = new double*[conf[i + 1]]; z[i] = new double[conf[i + 1]];
y[i] = new double[conf[i + 1]]; Fi[i] = new double[conf[i + 1]];
for(int j = 0; j < conf[i + 1]; j++)
{
}
}
w[i][j] = new double[conf[i] + 1];
// Initializing in the range (-0.5;0.5) (including bias
// weight)
for(int k = 0; k <= conf[i]; k++)
w[i][j][k] = rand()/(double)RAND_MAX - 0.5;
if(S == ‘D’)
{
for(int i = 0; i < N_LAYERS; i++)
{
}
for(int j = 0; j < conf[i + 1]; j++)
delete[] w[i][j];
delete[] w[i], z[i], y[i], Fi[i];
}
}
ErrorFile.close();
// Activation function double FNL(double z)
{
}
double y;
y = 1. / (1. + exp(-z));
return y;
// Applying input
void ApplyInput(short sn)
{
double input;
12
// Counting layers
for(short i = 0; i < N_LAYERS; i++)
// Counting neurons in each layer for(short j = 0; j < conf[i + 1]; j++)
{
z[i][j] = 0.;
// Counting input to each layer (= # of neurons in the previous
// layer)
for(short k = 0; k < conf[i]; k++)
{
// If the layer is not the first one if(i)
input = y[i - 1][k];
else
input = dataset[sn][k];
z[i][j] += w[i][j][k] * input;
}
}
}
z[i][j] += w[i][j][conf[i]]; // Bias term y[i][j] = FNL(z[i][j]);
// Training function, tr - # of runs void Train(int tr)
{
short i, j, k, m, sn;

double eta, prev_output, multiple3, SqErr, eta0;
// Starting learning rate eta0 = 1.5;
eta = eta0;
// Going through all tr training runs for(m = 0; m < tr; m++)
{
SqErr = 0.;
// Each training run consists of runs through each training set for(sn = 0;
sn < N_DATASETS; sn++)
{
13
ApplyInput(sn);
// Counting the layers down
for(i = N_LAYERS - 1; i >= 0; i--)
// Counting neurons in the layer for(j = 0; j < conf[i + 1]; j++)
{
if(i == 2) // If it is the output layer multiple3 = datatrue[sn][j] - y[i][j];
else
{
}
multiple3 = 0.;
// Counting neurons in the following layer for(k = 0; k < conf[i + 2]; k++)
multiple3 += Fi[i + 1][k] * w[i + 1][k][j];
Fi[i][j] = y[i][j] * (1 - y[i][j]) * multiple3;
// Counting weights in the neuron
// (neurons in the previous layer)
for(k = 0; k < conf[i]; k++)
{
{
switch(k)
{
case 36:
if(i) // If it is not a first layer prev_output = y[i - 1][k];
else
prev_output = y[N_LAYERS - 1][0] - datatrue[sn][0];
break;
case 37:
prev_output = y[N_LAYERS - 1][1] - datatrue[sn][1];
break;
default:
prev_output = dataset[sn][k];
}
}
}
w[i][j][k] += eta * Fi[i][j] * prev_output;
14
}
// Bias weight correction w[i][j][conf[i]] += eta * Fi[i][j];
}
SqErr += pow((y[N_LAYERS - 1][0] - datatrue[sn][0]), 2) +
pow((y[N_LAYERS - 1][1] - datatrue[sn][1]), 2);
}
}
ErrorFile << 0.5 * SqErr << endl;

// Decrease learning rate every 100th iteration if(!(m % 100))
eta /= 2.;
// Go back to original learning rate every 400th iteration if(!(m % 400))
eta = eta0;
// Prints complete information about the network void PrintInfo(void)
{
// Counting layers
for(short i = 0; i < N_LAYERS; i++)
{
cout << "LAYER " << i << endl;
// Counting neurons in each layer for(short j = 0; j < conf[i + 1]; j++)
{
cout << "NEURON " << j << endl;
// Counting input to each layer (= # of neurons in the previous
// layer)
for(short k = 0; k < conf[i]; k++)
cout << "w[" << i << "][" << j << "][" << k << "]="
<< w[i][j][k] << ‘ ’;
cout << "w[" << i << "][" << j << "][BIAS]="
<< w[i][j][conf[i]] << ‘ ’ << endl;
cout << "z[" << i << "][" << j << "]=" << z[i][j] << endl;
cout << "y[" << i << "][" << j << "]=" << y[i][j] << endl;
}
}
15
}
// Prints the output of the network void PrintOutput(void)
{
// Counting number of datasets
for(short sn = 0; sn < N_DATASETS; sn++)
{
}
}
ApplyInput(sn);
cout << "TRAINING SET " << sn << ": [ ";
// Counting neurons in the output layer for(short j = 0; j < conf[3]; j++)
cout << y[N_LAYERS - 1][j] << ‘ ’;
cout << "] ";
if(y[N_LAYERS - 1][0] > (datatrue[sn][0] - 0.1)
&& y[N_LAYERS - 1][0] < (datatrue[sn][0] + 0.1)
&& y[N_LAYERS - 1][1] > (datatrue[sn][1] - 0.1)
&& y[N_LAYERS - 1][1] < (datatrue[sn][1] + 0.1))
cout << "--- RECOGNIZED ---";
else
cout << "--- NOT RECOGNIZED ---";
cout << endl;
// Loads weithts from a file void LoadWeights(void)
{
double in;
ifstream file("weights.txt", ios::in);
// Counting layers
for(short i = 0; i < N_LAYERS; i++)
// Counting neurons in each layer for(short j = 0; j < conf[i + 1]; j++)
// Counting input to each layer (= # of neurons in the previous

// layer)
for(short k = 0; k <= conf[i]; k++)
{
16
}
file >> in;
w[i][j][k] = in;
}
file.close();
// Saves weithts to a file void SaveWeights(void)
{
}
ofstream file("weights.txt", ios::out);
// Counting layers
for(short i = 0; i < N_LAYERS; i++)
// Counting neurons in each layer for(short j = 0; j < conf[i + 1]; j++)
// Counting input to each layer (= # of neurons in the previous
// layer)
for(short k = 0; k <= conf[i]; k++)
file << w[i][j][k] << endl;
file.close();
// Gathers recognition statistics for 1 and 2 false bit cases void
GatherStatistics(void)
{
short sn, j, k, TotalCases;
int cou;
cout << "WITH 1 FALSE BIT PER CHARACTER:" << endl; TotalCases = conf[0];
// Looking at each dataset
for(sn = 0; sn < N_DATASETS; sn++)
{
cou=0;
// Looking at each bit in a dataset for(j = 0; j < conf[0]; j++)
{
if(dataset[sn][j])
dataset[sn][j] = 0;
17
}
else
dataset[sn][j] = 1; ApplyInput(sn);
if(y[N_LAYERS - 1][0] > (datatrue[sn][0] - 0.1)
&& y[N_LAYERS - 1][0] < (datatrue[sn][0] + 0.1)
&& y[N_LAYERS - 1][1] > (datatrue[sn][1] - 0.1)
&& y[N_LAYERS - 1][1] < (datatrue[sn][1] + 0.1))
cou++;
// Switching back if(dataset[sn][j])
dataset[sn][j] = 0;
else
dataset[sn][j] = 1;
}
cout << "TRAINING SET " << sn << ": " << cou << ‘/’ << TotalCases
<< " recognitions (" << (double)cou / TotalCases * 100. << "%)" << endl;
cout << "WITH 2 FALSE BITS PER CHARACTER:" << endl;
TotalCases = conf[0] * (conf[0] - 1);

// Looking at each dataset
for(sn = 0; sn < N_DATASETS; sn++)
{
cou=0;
// Looking at each bit in a dataset for(j = 0; j < conf[0]; j++)
for(k = 0; k < conf[0]; k++)
{
if(j == k)
continue;
if(dataset[sn][j])
dataset[sn][j] = 0;
else
dataset[sn][j] = 1;
if(dataset[sn][k])
dataset[sn][k] = 0;
else
dataset[sn][k] = 1;
18
}
ApplyInput(sn);
if(y[N_LAYERS - 1][0] > (datatrue[sn][0] - 0.1)
&& y[N_LAYERS - 1][0] < (datatrue[sn][0] + 0.1)
&& y[N_LAYERS - 1][1] > (datatrue[sn][1] - 0.1)
&& y[N_LAYERS - 1][1] < (datatrue[sn][1] + 0.1))
cou++;
if(dataset[sn][j]) // Switching back dataset[sn][j] = 0;
else
dataset[sn][j] = 1;
if(dataset[sn][k])
dataset[sn][k] = 0;
else
dataset[sn][k] = 1;
}
}
cout << "TRAINING SET " << sn << ": " << cou << ‘/’ << TotalCases
<< " recognitions (" << (double)cou / TotalCases * 100. << "%)" << endl;
// Entry point: main menu int main(void)
{
short ch;
int x;
MemAllocAndInit(‘A’);
do
{
cout << "MENU" << endl;
cout << "1. Apply input and print parameters" << endl;
cout << "2. Apply input (all training sets) and print output" << endl;
cout << "3. Train network" << endl; cout << "4. Load weights" << endl;
cout << "5. Save weights" << endl;
cout << "6. Gather recognition statistics" << endl;
cout << "0. Exit" << endl;
19
cout << "Your choice: ";
cin >> ch; cout << endl; switch(ch)

{
case 1: cout << "Enter set number: ";
cin >> x; ApplyInput(x); PrintInfo(); break;
case 2: PrintOutput();
break;
case 3: cout << "How many training runs?: ";
cin >> x; Train(x); break;
case 4: LoadWeights();
break;
case 5: SaveWeights();
break;
case 6: GatherStatistics();
break;
case 0: MemAllocAndInit(‘D’);
return 0;
}
}
cout << endl;
cin.get();
cout << "Press ENTER to continue..." << endl;
cin.get();
}
while(ch);
20





No comments:

Post a Comment