Sunteți pe pagina 1din 15

Tehnologii software in Inteligenta Artificiala

Detectarea teolerantei la defectare a unei


retele RNA

1
I. INTRODUCERE

Calculul inteligent este un domeniu al Inteligentei Artificiale care grupeaza tehnici de rezolvare a
problemelor pentru care modelele formale conduc la algoritmi foarte costisitori.
Principalele directii ale calculului inteligent sunt:
Calcul neuronal : este folosit in principal pentru rezolvarea problemelor de asociere
(clasificare, aproximare, predictie etc), bazandu-se pe extragerea, prin invatare, a unui model pornind de
la exemple. Sursa de inspiratie o reprezinta structura si functionarea creierului uman.
Calcul evolutiv : este folosit in principal in rezolvarea problemelor bazate pe cautarea solutiei
intr-un spatiu mare de solutii potentiale (in particular in rezolvarea problemelor de optimizare).
Calcul fuzzy : este folosit atunci cand datele problemei (si relatiile dintre acestea) nu pot fi
descrise exact, ci exista un grad de incertitudine sau fuzziness. Ideea de baza este aceea de a inlocui
valorile exacte cu valori fuzzy descrise prin functii de apartenenta.

Reteaua neuronala este entitatea ce sta la baza calculului neuronal. In cele ce urmeaza se prezinta
cateva caracteristici generale despre retelele neuronale artificiale – RNA.

Din punct de vedere functional o retea neuronala este un sistem ce primeste date de intrare
(corespunzatoare datelor initiale ale unei probleme) si produce date de iesire (ce pot fi interpretate ca
raspunsuri ale problemei analizate). O caracteristica esentiala a retelelor neuronale este capacitatea de a se
adapta la mediul informational corespunzator unei probleme concrete printr-un proces de invatare. In
felul acesta reteaua extrage modelul problemei pornind de la exemple.

Fig.1: Schema de concept a unei retele neuronale multi-strat.

Din punct de vedere structural o retea neuronala este un ansamblu de unitati interconectate
(neuroni) fiecare fiind caracterizata de o functionare simpla. Functionarea unitatilor este influentata de o
serie de parametri adaptabili. Astfel o retea neuronala este un sistem extrem de flexibil.

Elementul principal ce constituire reteaua neuronala este neuronul artificial. Asemenea


omologului sau din realitate, neuronul artificial prezinta urmatoarele componente :

- DENDRITE : canale de intrare a informatiei de prelucrat;


- CORP NEURONAL : contine “motorul intern” al neuronului;

2
- AXON : canal de iesire .

Simplificat, modelul unui neuron artificial este reprezentat printr-un element de


însumare a intrărilor xi şi printr-o funcţie de activare f. În fig. de mai jos este reprezentat un
neuron artificial standard cu intrarile xi, ponderile wi si iesirea y data de functia f, numita si
functia de activare sau de transfer a neuronului.

Fig. 2: Schema conceptuala a unui neuron artificial

Raspunsul sau iesirea unui neuron este dat(a) de ecuatia :

y = f(w0x0 + w1x1 + ... + wnxn), care devine :


y=f( )

Pentru a oferi o viziune completa a acestei valori de iesire, mentionam mai jos cele mai uzuale
functii de activare/transfer :

a. functii binare deterministe :

b. functii binare stohastice :

c. sigmoidala :
i. logistica :

ii. tangenta hiperbolica :

Arhitectura retelei neuronale artificiale se refera la modul in care sunt amaplasate unitatile
functionale (topologie) si la modul in care sunt interconectate.
Dpdv al arhitecturii o retea neuronala poate fi formalizata printr-un graf orientat, etichetat, in
nodurile caruia sunt plasate neuronii artificiali si ale carui arce specifica conexiunile si implicit fluxul
informational. In ceea ce priveste rolul unitatilor functionale, acestea se impart in trei mari categorii :

3
• unitati de intrare : primesc semnale din partea mediului si retransmit semnalul primit
catre urmatoarele unitati din retea, fara a realiza o prelucrare prealabila asupra
acestuia;

• unitati ascunse : conectate doar cu alte unitati ale retelei, fara a comunica direct cu
mediul extern; rolul acestora este de a colecta semnale, de a le prelucra si de a distribui
semnalul prelucrat catre alte unitati ascunse sau de iesire;

• unitati de iesire : colecteaza semnalul de la unitatile ascunse il prelucreaza intr-o ultima


etapa inainte de a-l trimite catre mediul extern.

Modul de interconectare al neuronilor artificiali determina fluxul de semnale prin retea, fiind
totodata un factor esential in stabilirea algoritmului de functionare. Din perspectiva prezentei conexiunilor
inverse grafului asociat, retele se clasifica in :

1. Retele de tip FEED-FORWARD : fara conexinui inverse, fluxul informational fiind


unidrectional dinspre setul unitatilor de intrare catre cel al unitatilor de iesire; conectivitatea
intre nivele poate fi totala sau locala :

Fig. 3: RNA de tip feed-forward

2. Retele recurente. Graful asociat contine conexiuni inverse directe sau indirecte.

Invatarea reprezinta procesul prin care un sistem isi imbunatateste performantele prin achizitie de
cunostinte. In cazul RNA, invatarea se refera la orice modificare a parametrilor acesteia (a ponderilor
fiecarui neuron in parte) ce asigura o mai buna adecvare a comportarii retelei la problema pt. care a fost
proiectata.
Procesul de invatare se bazeaza pe doua elemente :
o multime de informatii denumita si set de antrenare sau sabloane;

o algoritm de adaptare la informatiile primite.

In functie de natura informatiilor primite, invatarea poate fi de mai multe tipuri :

4
1. Nesupervizata (auto-organizare) : sistemul primeste doar semnale de intrare din
partea mediului si pe baza acestora descopera trasaturile statistice ale populatiei
stimulilor, construindu-si o reprezentare codificata in ponderi a mediului. Regula de
ajustare a ponderilor pt acest tip de invatare sugereaza ca in procesul de adaptare a
ponderilor dintre unitatea j si unitatea i, intervin doar semnalele de iesire respectiv de
intrare al celor doua unitati.

2. Supervizata : setul de antrenare consta in perechi de forma (I,D), unde I reprezinta


semnalul de intrare, iar D respunsul corect, sau un indicator de corectitudine.
Procesul de ajustare al ponderilor consta in minimizarea erorii de iesire al intregii
retele pe parcursul a mai multe epoci sau generatrii de antrenare. Regula de ajustare a
ponderilor tine cont de derivata functiei de antrenare.

II. PROIECTAREA RNA

In proiectul prezent s-a optat pentru implementarea unei RNA care sa fie capabila de
recunoasterea anumitor forme din popularul joc Tetris.
Parametrii generali ai retelei sunt prezentati mai jos :

TIP
STRUCTURA

TIP RETEA FEEDFORWARD


STRATURILE 1 strat INPUT – 5 x 5 =25 de neuroni
DE NEURONI 1 strat HIDDEN – 20 de neuroni
1 strat OUTPUT – 3 neuroni
INTRARI valori BINARE {0,1}
FUNCTIA DE
SIGMOID
TRANSFER
METODA DE
SUPERVIZATA
INVATARE
ALGORITM
BACKPROPAGATION
INVATARE
UTILIZARI operatii logice complexe, clasificarea si recunoasterea formelor
Majoritatea reţelelor neuronale utilizate practic sunt de tipul perceptron multistrat (MLP)
şi utilizează algoritmul de backpropagation pentru învăţare. Algoritmul de backpropagation

5
foloseşte eroarea între ieşirile actuale (rezultate prin calcul, propagând înainte valorile de pe
intrări, specificate de şabloane) şi ieşirile aşteptate (cele impuse de şablonul curent), pentru a
ajusta fiecare pondere. Ajustarea ponderilor se face secvenţial, plecând de la ultimul strat (cel de
ieşire), spre primul strat (cel de intrare).

INTRARILE, IESIRILE si ANTRENAREA RNA

În general alegerea intrărilor este o problema dificilă. O regulă empirică de alegere a


intrărilor este următoarea: "cu cât mai multe date, cu atât mai bine!" Această regulă se aplică atât
la numărul intrărilor unei reţele, ca şi la numărul şabloanelor de antrenare. Trebuie ca atunci
când se strâng date şi se definesc intrările reţelei, să nu se furnizeze reţelei 2 vectori similari de
intrări, care să dea la ieşire rezultate conflictuale.
La fel de importantă ca şi strângerea unui număr suficient de date de intrare este şi
modalitatea de prezentare a acestora, reţelei. Marea majoritate a simulatoarelor existente acceptă
intrări ce variază între 0 si 1, sau intre -1 si +1. De aceea, datele reale trebuie să fie preprocesate
pentru a fi aduse în această gamă. Cele mai multe simulatoare realizează chiar ele această
preprocesare. Modul cum se aleg intrările semnificative pentru reţea şi modul de setare al
parametrilor în simulator, au drept rezultat obţinerea unei reţele neuronale performante sau nu.
In cazul de fata, s-a optat pentru intrari binare sub forma unor vectori ce reprezinta, de
fapt, forma simplificata a unor forme din jocul Tetris. Cateva dintre aceste forme sunt evidentiate
mai jos :

SABLON INTRARE BINARA IESIRE RNA

0,1,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0 0,0,1

0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0 0,1,0

0,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0 0,1,1

6
Reteaua prezinta 25 de neuroni de intrare, 2 neuroni de iesire si 20 de neuroni in stratul
ascuns.
Ponderile sunt salvate intr-un fisier text, ponderi.txt, sabloanele de intrare si de iesire se
preiau din fisier text,sabloane2.txt; la fel si intrarile.
Programul prezinta o interfata simpla pentru utilizator, intrebandu-ul in fiecare moment
ce doreste sa faca, astfel:
- daca doreste antrenarea retelei, acesta putand raspunde prin “da” sau “nu”;
-

Listingul programului este urmatorul:

7
package rna;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.lang.String.*;

class DefectareRN
{
static final int N=5;//dimensiunea matricii de pixeli, pe intrare
static final int NNI=N*N;
static final int NNO=2;
static final int NNH=20;
static final int NTS=3*NNO;//nr total de sabloane 3 pt fiecare clasa de iesire
static final double Epsilon=0.01;//eroare impusa
static final int NME=20000;//nr max de epoci
static double w1[][]=new double[NNH][NNI];
static double w2[][]=new double[NNO][NNH];
static double sabloaneIn[][]=new double[NTS][NNI];
static double sabloaneOut[][]=new double[NTS][NNO];
static double yH[]=new double[NNH];
static double yO[]=new double[NNO];
static double intrari[]=new double[NNI];

public static void main (String args[])


{
String raspuns;
for(;;)
{
raspuns=JOptionPane.showInputDialog("Antrenare ?(Da/Nu)");
if((raspuns.charAt(0)=='d')||(raspuns.charAt(0)=='D'))
{
boolean converge;
initPonderi();
converge=antrenare();
if(converge==false) System.out.println("Nu a convers in numarul de
epoci specificat!");
else System.out.println("A convers!");
}
raspuns=JOptionPane.showInputDialog("Testare ?(Da/Nu)");
if((raspuns.charAt(0)=='d')||(raspuns.charAt(0)=='D'))
{
citirePonderi();
testare();
}
raspuns=JOptionPane.showInputDialog("Defectare ?(Da/Nu)");
if((raspuns.charAt(0)=='d')||(raspuns.charAt(0)=='D'))
{
defectarePonderi();
testare();
}
raspuns=JOptionPane.showInputDialog("Iesire ?(Da/Nu)");
if((raspuns.charAt(0)=='d')||(raspuns.charAt(0)=='D'))

8
System.exit(0);
}//for infinit
}//main

private static void initPonderi()


{
int i,j;
Random r=new Random();
//initializare primul strat de ponderi
for(i=0;i<NNH;i++)
for(j=0;j<NNI;j++)
w1[i][j]=r.nextDouble()-0.5;
//initializare al doilea strat de ponderi
for(i=0;i<NNO;i++)
for(j=0;j<NNH;j++)
w2[i][j]=r.nextDouble()-0.5;
}//init ponderi

private static void incarcaSabloane()


{
FileReader fr=null;
BufferedReader bfr=null;
try{
fr=new FileReader("sabloane2.txt");
bfr=new BufferedReader(fr);
//Stim ca fisierul contine 2 * NR_TOTAL_SABLOANE de linii.
//(pentru fiecare sablon, sunt 2 linii: linia cu intrarile sablonului
// si linia cu iesirile sablonului.)
String linieCrt;
for(int ex=0;ex<NTS;ex++)
{
//pt. fiecare sablon ex, citim 2 linii:
linieCrt=bfr.readLine();
//S-a citit linia cu intrarile sablonului ex.
if(linieCrt==null)
{
System.out.println("Structura fisierului de sabloane nu este corecta!");
System.exit(1);
}
//Citim valorile din sablon, pt. fiecare neuron de intrare:
/*Pentru a separa un string in articolele sale componente (separator implicit este caracterul blank),
se foloseste clasa StringTokenizer si metoda nextToken( ).*/
StringTokenizer st=new StringTokenizer(linieCrt);
for(int i=0;i<NNI;i++)
{
double x= Double.parseDouble(st.nextToken(“,”));

//Cea mai mare valoare din fisierul de sabloane: 1.0


//Cea mai mica valoare din fisierul de sabloane: 0.0
x=(x-0.0)/(10.0-0.0);//scalare in plaja 0.0, 1.0
sabloaneIn[ex][i]=x;
}
//Citim linia cu iesirile sablonului ex:
linieCrt=bfr.readLine();
if(linieCrt==null)
{

9
System.out.println("Structura fisierului de sabloane nu este corecta!");
System.exit(1);
}
//Citim valorile din sablon, pt. fiecare neuron de iesire:
st=new StringTokenizer(linieCrt);
for(int i=0;i<NNO;i++)
sabloaneOut[ex][i]=Double.valueOf(st.nextToken()).doubleValue();
//Double.valueOf(string) scoate un obiect Double.
//Extragem tipul primitiv double din Double,
// cu: Double.doubleValue();
}//for i
bfr.close();
}
catch(IOException e)
{
System.out.println(e);
System.exit(1);
}}//incarca sabloane
private static boolean antrenare()
{
//declare variabile locale
double eroareIesireSablonCrt[]=new double[NTS];
double eroareTotala;
int nrEpoci=0;
boolean converge=false;
boolean stop=false;
int i,ex;
while(!stop)
{
for(ex=0;ex<NTS;ex++)
{
calculIesiriNeuroni(yH,yO,ex);
eroareIesireSablonCrt[ex]=0;
for(i=0;i<NNO;i++)
eroareIesireSablonCrt[ex]+=0.5*(yO[i]-sabloaneOut[ex]
[i])*(yO[i]-sabloaneOut[ex][i]);
modificaPonderi(yO,yH,ex);
}//for ex
nrEpoci++;
eroareTotala=0;
for(ex=0;ex<NTS;ex++)
eroareTotala+=eroareIesireSablonCrt[ex];
System.out.println("La epoca nr.: "+nrEpoci+" eroarea totala este de
"+eroareTotala);
if(eroareTotala<=Epsilon)
{
converge=true;
stop=true;
salvarePonderi();
}
if(nrEpoci>NME)stop=true;
}//while
return converge;
}//antrenare

private static void calculIesiriNeuroni(double yH[],double yO[],int ex)

10
{
int i,j;
double xTotalI;
for(i=0;i<NNH;i++)
{
xTotalI=0;
for(j=0;j<NNI;j++)
xTotalI=xTotalI+sabloaneIn[ex][j]*w1[i][j];
yH[i]=1/(1+Math.exp(-xTotalI));
}//for i
for(i=0;i<NNO;i++)
{
xTotalI=0;
for(j=0;j<NNH;j++)
xTotalI=xTotalI+yH[j]*w2[i][j];
yO[i]=1/(1+Math.exp(-xTotalI));
}//for i
}//calcul iesiri neuroni

private static void modificaPonderi(double yO[],double yH[],int ex)


{
double deltaO[]=new double[NNO];
double deltaH[]=new double[NNH];
int i,j;
double sumaPonderataDeltaFii;
double alfa=1.0;
double eta=1.0;
//ajustari setul al doilea de ponderi
for(i=0;i<NNO;i++)
deltaO[i]=(yO[i]-sabloaneOut[ex][i])*(1-yO[i])*yO[i];
for(i=0;i<NNO;i++)
for(j=0;j<NNH;j++)
w2[i][j]=alfa*w2[i][j]-eta*deltaO[i]*yH[j];
//ajustari setul intai de ponderi
for(i=0;i<NNH;i++)
{
sumaPonderataDeltaFii=0;
for(j=0;j<NNO;j++)
sumaPonderataDeltaFii+=w2[j][i]*deltaO[j];
deltaH[i]=sumaPonderataDeltaFii*(1-yH[i])*yH[i];
}//for i
for(i=0;i<NNH;i++)
for(j=0;j<NNI;j++)
w1[i][j]=alfa*w1[i][j]-eta*deltaH[i]*sabloaneIn[ex][j];
}//modifica ponderi

private static void salvarePonderi()


{
FileWriter fw=null;
BufferedWriter bfw=null;
int i,j;
String s;
try
{
fw=new FileWriter("ponderi.txt");

11
bfw=new BufferedWriter(fw);
for(i=0;i<NNH;i++)
{
s="";
for(j=0;j<NNI;j++)
s=s+w1[i][j]+" ";
bfw.write(s);
bfw.newLine();
}

for(i=0;i<NNO;i++)
{
s="";
for(j=0;j<NNH;j++)
s=s+w2[i][j]+" ";
bfw.write(s);
bfw.newLine();
}
bfw.close();
fw.close();
}//try
catch(IOException e)
{
System.out.println("Eroare!!!"+e);
System.exit(1);
}
}//salveaza ponderi

private static void testare()


{
citireIntrari();
for(int j=0;j<NNI;j++)
System.out.println("intrarea "+j+" este "+intrari[j]);
double xTotalI;
for(int i=0;i<NNH;i++)
{
xTotalI=0;
for(int j=0;j<NNI;j++)
xTotalI=xTotalI+intrari[j]*w1[i][j];
yH[i]=1/(1+Math.exp(-xTotalI));
}//for i
for(int i=0;i<NNO;i++)
{
xTotalI=0;
for(int j=0;j<NNH;j++)
xTotalI=xTotalI+yH[j]*w2[i][j];
yO[i]=1/(1+Math.exp(-xTotalI));
}//for i
int indiceMax=0;
double max=yO[indiceMax];
for(int i=1;i<NNO;i++)
if(yO[i]>max)
{
max=yO[i];
indiceMax=i;

12
}
if(indiceMax==0) System.out.println("Este forma TETRIS! Cu precizia de "+max*100+"
%");
}//testare

private static void citirePonderi()


{
int i,j;
FileReader fr=null;
BufferedReader bfr=null;
try
{
fr=new FileReader("ponderi.txt");
bfr=new BufferedReader(fr);
String linieCrt;
for(i=0;i<NNH;i++)
{
linieCrt=bfr.readLine();
if(linieCrt==null)
{
System.out.println(" Fisierului de ponderi incorect!");
System.exit(1);
}
StringTokenizer linie=new StringTokenizer(linieCrt," ");
for(j=0;j<NNI;j++)
w1[i][j]=Double.parseDouble(linie.nextToken());
}
for(i=0;i<NNO;i++)
{
linieCrt=bfr.readLine();
if(linieCrt==null)
{
System.out.println(" Fisierului de ponderi incorect!");
System.exit(1);
}
StringTokenizer linie=new StringTokenizer(linieCrt," ");
for(j=0;j<NNH;j++)
w2[i][j]=Double.parseDouble(linie.nextToken());
}
bfr.close();
fr.close();
}//try
catch(IOException e)
{
System.out.print("ATENTIE!"+e);
System.exit(1);
}
}//citirePonderi

private static void citireIntrari()


{
int i,j;
FileReader fr=null;
BufferedReader bfr=null;
try

13
{
fr=new FileReader("intrare.txt");
bfr=new BufferedReader(fr);
String linieCrt;
for(i=0;i<N;i++)
{
linieCrt=bfr.readLine();
if(linieCrt==null)
{
System.out.println(" Fisier de intrare incorect!");
System.exit(1);
}
StringTokenizer st=new StringTokenizer(linieCrt," ");
for(j=0;j<N;j++)
intrari[i*N+j]=Integer.parseInt(st.nextToken());
}//for i
bfr.close();
fr.close();
}//try
catch(IOException e)
{
System.out.print("ATENTIE!"+e);
System.exit(1);
}
}//citire intrari

private static void defectarePonderi()


{
Random r=new Random();
double procent=Double.parseDouble(JOptionPane.showInputDialog("Introduceti
procentul de defectari!"));
int i,j,pondere;
int index=0;
int v1[]=new int[NNI*NNH];
int v2[]=new int[NNH*NNO];
for(i=0;i<NNI*NNH;i++)
v1[i]=1;
for(i=0;i<NNH*NNO;i++)
v2[i]=1;
//defectare ponderi din primul strat
while(index<((procent*NNI*NNH)/100))
{
pondere=r.nextInt(NNI*NNH);
System.out.println("w1 "+pondere);
if(v1[pondere]==1)
{
v1[pondere]=0;
w1[pondere/NNI][pondere%NNI]=0;
index++;
}
}//while w1
index=0;
//defectare ponderi din stratul al doilea
while(index<((procent*NNH*NNO)/100))
{

14
pondere=r.nextInt(NNH*NNO);
System.out.println("w2 "+pondere);
if(v2[pondere]==1)
{
v2[pondere]=0;
w2[pondere/NNH][pondere%NNH]=0;
index++;
}
}//while w2
}//defectare ponderi

}//class DefectareRN

15