Apprentissage par descente de gradiant: XOR AND OR.

Programmation avancée de jeux en PureBasic
Avatar de l’utilisateur
Fig
Messages : 1176
Inscription : jeu. 14/oct./2004 19:48

Apprentissage par descente de gradiant: XOR AND OR.

Message par Fig »

Premier essais d'apprentissage supervisé d'un réseau de neurones artificiels par descente de gradient.
En haut à gauche, une image en niveau de gris de la proression de l'apprentissage: noir=0, blanc=1.
Basiquement, il apprend ici la fonction logique XOR:
0/0=>0
1/0=>1
0/1=>1
1/1=>0
donc coins gauche/haut et bas/droit noir, blanc les 2 autres coins.

Créer une Dll nommée "NeuroNetLib"

Code : Tout sélectionner

;create a new neural net with nblayer deep
CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
   #S=4
CompilerElse
   #S=8
CompilerEndIf
#SD=8
#TypeInput=0
#TypeSigmoid=1
#TypeSwish1=2
#TypeSoftMax=3
#TypeLeakyRelu=4
#TypeTanH=5
#real=0
#positive=1

ProcedureDLL CreateNet()
   *net=AllocateMemory(#S)
   If *net=0:MessageRequester("Error","Allocate memory failed"):EndIf
   PokeI(*net,0)
   ;*net => nblayer
   ;*net+#S*1 => pointer to inputs layer (layer n°0)
   ;*net+#S*2 => pointer To the second layer etc... (layer n°1)
   ProcedureReturn *net
EndProcedure

ProcedureDLL EndLayer(*net)
   nblayer.i=PeekI(*net)
   *Layer=PeekI(*net+#S*nblayer)
   NbNeuron.i=PeekI(*layer)
   type.i=PeekI(*layer+#S*7)
   
   *PLayer=PeekI(*net+#S*(nblayer-1))
   NbNeuronPrevOutput.i=PeekI(*PLayer)
   target=#SD*NbNeuron
   output=#SD*NbNeuron
   error=#SD*NbNeuron
   weight=#SD*NbNeuron*NbNeuronPrevOutput
   bias=#SD*NbNeuron
   som=#SD*NbNeuron
   *layer=ReAllocateMemory(*layer,#S*8+output+weight+bias+error+target+som)
   If *layer=0:MessageRequester("Error","ReAllocate memory failed"):EndIf
   
   *startBias=*layer+#S*8+weight
   *startOutput=*layer+#S*8+weight+bias
   *startError=*layer+#S*8+weight+bias+output
   *startTarget=*layer+#S*8+weight+bias+output+error
   *startSom=*layer+#S*8+weight+bias+output+error+target
   PokeI(*layer+#S*0,NbNeuron)
   PokeI(*layer+#S*1,NbNeuronPrevOutput)
   PokeI(*layer+#S*2,*startBias)
   PokeI(*layer+#S*3,*startOutput)
   PokeI(*layer+#S*4,*startError)
   PokeI(*layer+#S*5,*startTarget)
   PokeI(*layer+#S*6,*startSom)
   PokeI(*layer+#S*7,type)
   
   PokeI(*net+#S*nblayer,*layer)
   ProcedureReturn 1
EndProcedure   

;specify composition of each layer
ProcedureDLL AddLayer(*net,NbNeuron.i,type.i)
   nblayer.i=PeekI(*net)
   nblayer+1
   PokeI(*net,nblayer)
   *net=ReAllocateMemory(*net,#S+#S*nblayer)
   If *net=0:MessageRequester("Error","Allocate memory failed"):EndIf
   
   If type=#TypeInput
      *layer=AllocateMemory(#S*8+#SD*NbNeuron)
      If *layer=0:MessageRequester("Error","Allocate memory failed (input)"):EndIf
      PokeI(*net+#S*nblayer,*layer)
      nbprevneuron.i=0
      *startBias=0
      *startOutput=*layer+#S*8
      *startTarget=0
      *startSom=0
      PokeI(*layer+#S*0,NbNeuron)
      PokeI(*layer+#S*1,nbprevneuron) ;nb neuron previous layer
      PokeI(*layer+#S*2,*startBias)
      PokeI(*layer+#S*3,*startOutput)   
      PokeI(*layer+#S*4,*startError)
      PokeI(*layer+#S*5,*startTarget)
      PokeI(*layer+#S*6,*startSom)
      PokeI(*layer+#S*7,type)
      ProcedureReturn *net
   EndIf   
   
   *PLayer=PeekI(*net+#S*(nblayer-1))
   NbNeuronPrevOutput.i=PeekI(*PLayer)
   output=#SD*NbNeuron
   error=#SD*NbNeuron
   weight=#SD*NbNeuron*NbNeuronPrevOutput
   bias=#SD*NbNeuron
   som=#SD*NbNeuron
   *layer=AllocateMemory(#S*8+output+weight+bias+error+target+som)
   If *layer=0:MessageRequester("Error","Allocate memory failed"):EndIf
   *startBias=*layer+#S*8+weight
   *startOutput=*layer+#S*8+weight+bias
   *startError=*layer+#S*8+weight+bias+output
   *startTarget=*layer+#S*8+weight+bias+output+error
   *startSom=*layer+#S*8+weight+bias+output+error+target
   PokeI(*layer,NbNeuron)
   PokeI(*layer+#S,NbNeuronPrevOutput)
   PokeI(*layer+#S*2,*startBias)
   PokeI(*layer+#S*3,*startOutput)
   PokeI(*layer+#S*4,*startError)
   PokeI(*layer+#S*5,*startTarget)
   PokeI(*layer+#S*6,*startSom)
   PokeI(*layer+#S*7,type)
   
   PokeI(*net+#S*nblayer,*layer)
   ProcedureReturn *net
EndProcedure

;initiation weights With randoms values
ProcedureDLL InitWeight(*net,RandomType.i)
   nblayer.i=PeekI(*net)
   For i=2 To nblayer
      *layer=PeekI(*net+#S*i)
      nbneuron=PeekI(*layer)
      nbprevoutput=PeekI(*layer+#S)
      *weight=*layer+#S*8
      *bias=PeekI(*layer+#S*2)
      ;change randomly weights
      For t=0 To nbneuron*nbprevoutput-1
         If RandomType=0
            Repeat
               a.d=(Random(200)-100)/100
            Until a<>0.0
         ElseIf Randomtype=1
            Repeat
               a.d=Random(100)/100
            Until a<>0.0
         EndIf
         PokeD(*weight+#SD*t,a) ;write random weight between -1;+1
      Next t
      ;change randomly bias' weights
      For t=0 To nbneuron-1
         If RandomType=0
            Repeat
               a.d=(Random(200)-100)/100
            Until a<>0.0
         ElseIf Randomtype=1
            Repeat
               a.d=Random(100)/100
            Until a<>0.0
         EndIf
         PokeD(*bias+#SD*t,a) ;write random weight between -1;+1
      Next t
   Next i
EndProcedure

ProcedureDLL ReadNbNeuron(*net,layer.i)
   *layer=PeekI(*net+#S*layer)
   ProcedureReturn PeekI(*layer)
EndProcedure

ProcedureDLL ReadPrevNbNeuron(*net,layer.i)
   *layer=PeekI(*net+#S*layer)
   ProcedureReturn PeekI(*layer+#S)
EndProcedure

ProcedureDLL.d ReadOutput(*net,layer.i,neuron.i)
   *layer=PeekI(*net+#S*layer)
   *startoutput=PeekI(*layer+#S*3)
   neuron-1
   neuron*#SD
   *startoutput=*startoutput+neuron
   value.d=PeekD(*startoutput)
   ProcedureReturn value
EndProcedure

ProcedureDLL.d ReadSomme(*net,layer.i,neuron.i)
   *Layer=PeekI(*net+#S*layer)
   *StartSom=PeekI(*Layer+#S*6)
   neuron-1
   neuron*#SD
   som.d=PeekD(*StartSom+neuron)
   ProcedureReturn som
EndProcedure

ProcedureDLL.d ReadBiasWeight(*net,layer.i,neuron.i)
   *Layer=PeekI(*net+#S*layer)
   *StartBias=PeekI(*Layer+#S*2)
   neuron-1
   neuron*#SD
   *StartBias+neuron
   bias.d=PeekD(*StartBias)*1
   ProcedureReturn bias
EndProcedure

ProcedureDLL.d ReadError(*net,layer.i,neuron.i)
   *layer=PeekI(*net+#S*layer)
   *startError=PeekI(*layer+#S*4)
   neuron-1
   neuron*#SD
   *startError+neuron
   value.d=PeekD(*startError)
   ProcedureReturn value
EndProcedure   

ProcedureDLL.d ReadTarget(*net,neuron.i)
   nblayer.i=PeekI(*net)
   *layer=PeekI(*net+#S*nblayer)
   *startTarget=PeekI(*layer+#S*5)
   neuron-1
   neuron*#SD
   *startTarget+neuron
   value.d=PeekD(*startTarget)
   ProcedureReturn value
EndProcedure

ProcedureDLL.d ReadWeight(*net,layer.i,neuron.i,weight.i)
   *layer=PeekI(*net+#S*layer)
   nbPrevouput.i=PeekI(*layer+#S)
   *startWeight=*layer+#S*8
   weight-1
   weight*#SD
   neuron-1
   neuron*nbPrevouput
   neuron*#SD
   *startWeight+neuron
   *startWeight+weight
   value.d=PeekD(*startWeight)
   ProcedureReturn value
EndProcedure

ProcedureDLL DeleteNet(*net)
   nblayer=PeekI(*net)
   For i=1 To nblayer
      *layer=PeekI(*net+#S*i)
      FreeMemory(*layer)
   Next i
   FreeMemory(*net)
   ProcedureReturn 1
EndProcedure

ProcedureDLL SetWeight(*net,layer.i,neuron.i,weight.i,value.d)
   *layer=PeekI(*net+#S*layer)
   nbPrevouput.i=PeekI(*layer+#S)
   *startWeight=*layer+#S*8
   weight-1
   weight*#SD
   neuron-1
   neuron*nbPrevouput
   neuron*#SD
   *startWeight+neuron
   *startWeight+weight
   PokeD(*startWeight,value)
EndProcedure

;activation functions https://en.wikipedia.org/wiki/Activation_function
;here, Sigmoïd :
Procedure.d Sigmoid(output.d)
   output.d=1/(1+Exp(-output))
   ProcedureReturn output
EndProcedure

;here, Swish1 (Beta=1) :
Procedure.d Swish1(output.d)
   output.d=output/(1+Exp(-output))
   ProcedureReturn output
EndProcedure

;here, LeakyReLu :
Procedure.d LeakyReLu(output.d)
   If output<0
      output*0.01
   EndIf
   ProcedureReturn output
EndProcedure

;define inputs
ProcedureDLL SetInput(*net,Input.i,value.d)
   *layer=PeekI(*net+#S)
   *StartOutput=PeekI(*layer+#S*3)
   input-1
   input*#SD
   *StartOutput+Input
   PokeD(*StartOutput,value)
   ProcedureReturn 1
EndProcedure

ProcedureDLL.d SetBiasWeight(*net,layer.i,neuron.i,bias.d)
   *Layer=PeekI(*net+#S*layer)
   *StartBias=PeekI(*Layer+#S*2)
   neuron-1
   neuron*#SD
   *StartBias+neuron
   PokeD(*StartBias,bias)
EndProcedure

;define desired output
ProcedureDLL SetDesireOutput(*net,Output.i,value.d)
   nblayer.i=PeekI(*net)
   *layer=PeekI(*net+#S*nblayer)
   *startTarget=PeekI(*layer+#S*5)
   output-1
   output*#SD
   *startTarget+Output
   PokeD(*startTarget,value)
   ProcedureReturn 1
EndProcedure

ProcedureDLL.d CalculError(*net)
   ;*net      => nblayer
   ;*net+#S*1 => pointer to layer 1=> inputs
   ;*net+#S*2 => pointer to layer 2
   ;*net+#S*3 => pointer To the third layer etc...
   nblayer=PeekI(*net)
   ;calcul erreurs sur la couche des sorties
   *layer=PeekI(*net+#S*nblayer)
   nbneuron=PeekI(*layer)
   *startOutput=PeekI(*layer+#S*3)
   *startError=PeekI(*layer+#S*4)
   *startTarget=PeekI(*layer+#S*5)
   For i=0 To nbneuron-1
;       target.d=PeekD(*startTarget+i*#SD)
;       output.d=PeekD(*startOutput+i*#SD)
;       error.d=(1-target)/(1-output)
;       error=(target/output)+error
;       error=-error
      error.d=PeekD(*startTarget+i*#SD)-PeekD(*startOutput+i*#SD)
      PokeD(*startError+#SD*i,error)
   Next i
   ;calcul erreurs des couches cachées
   For i=nblayer To 3 Step -1
      *layer=PeekI(*net+#S*i)
      nbneuron=PeekI(*layer)
      *startOutput=PeekI(*layer+#S*3)
      *startError=PeekI(*layer+#S*4)
      *startTarget=PeekI(*layer+#S*5)
      *startWeight=*Layer+#S*8
      
      *Player=PeekI(*net+#S*(i-1))
      Pnbneuron=PeekI(*Player)
      *PstartError=PeekI(*Player+#S*4)
      For j=0 To Pnbneuron-1
         Terror.d=0
         For t=0 To nbneuron-1
            Error.d=PeekD(*startError+t*#SD)
            Weight.d=PeekD(*startWeight+j*#SD)
            Terror=Terror+Error*weight
         Next t
         PokeD(*PstartError+j*#SD,Terror)
      Next j
   Next i
   ;cross entropy
;    Error=0
;    For i=0 To nbneuron-1
;       target.d=PeekD(*startTarget+i*#SD)
;       output.d=PeekD(*startOutput+i*#SD)
;       Error=(target*Log(output))+((1-target)*Log(1-output))+Error
;    Next i
;    Error=-Error
;    ProcedureReturn Error
EndProcedure 

;calculate outputs
ProcedureDLL Propagate(*net)
   nblayer.i=PeekI(*net)
   For i=2 To nblayer
      *Layer=PeekI(*net+#S*i)
      
      nbneuron.i=PeekI(*Layer)
      nbprevoutput.i=PeekI(*Layer+#S)
      *StartBias=PeekI(*Layer+#S*2)
      *StartOutput=PeekI(*Layer+#S*3)
      *StartSom=PeekI(*layer+#S*6)
      type.i=PeekI(*Layer+#S*7)
      *StartWeight=*Layer+#S*8
      
      *PLayer=PeekI(*net+#S*(i-1))
      *PStartOutput=PeekI(*PLayer+#S*3)
      
      ;SoftMax
      If type=#TypeSoftMax
         ;calcul les sommes de chaque neurone
         somsom.d=0
         For j=0 To nbneuron-1
            output.d=0.0
            For k=0 To nbprevoutput-1
               input.d=PeekD(*PStartOutput+k*#SD)
               weight.d=PeekD(*StartWeight+k*#SD+j*nbprevoutput*#SD)
               output=output+weight*input
            Next k
            bias.d=PeekD(*StartBias+j*#SD)*1
            output=output+bias
            PokeD(*StartSom+j*#SD,output)
            ;calcul la sortie théorique de chaque neurone
            output=Exp(output)
            PokeD(*StartOutput+j*#SD,output)
            somsom=somsom+output
         Next j
         For j=0 To nbneuron-1
            output=PeekD(*StartOutput+j*#SD)
            output=output/somsom
            PokeD(*StartOutput+j*#SD,output)
         Next j
         Continue
      EndIf
      
      ;Sigmoid/Swish1/LeakyReLu/TanH
      For j=0 To nbneuron-1
         output.d=0.0
         For k=0 To nbprevoutput-1
            input.d=PeekD(*PStartOutput+k*#SD)
            weight.d=PeekD(*StartWeight+k*#SD+j*nbprevoutput*#SD)
            output=output+weight*input
         Next k
         bias.d=PeekD(*StartBias+j*#SD)*1
         output=output+bias
         PokeD(*StartSom+j*#SD,output)
         
         If type=#TypeSigmoid
            PokeD(*StartOutput+j*#SD,Sigmoid(output))
         ElseIf type=#TypeSwish1
            PokeD(*StartOutput+j*#SD,Swish1(output))
         ElseIf type=#TypeLeakyReLu
            PokeD(*StartOutput+j*#SD,LeakyReLu(output))
         ElseIf type=#TypeTanH
            PokeD(*StartOutput+j*#SD,TanH(output))
         EndIf
         
      Next j
   Next i   
EndProcedure

ProcedureDLL GradientDescent(*net,LearningRate.d,drop_rate.i)
   nblayer=PeekI(*net)
   For i=nblayer To 2 Step -1
      *layer=PeekI(*net+#S*i)
      nbneuron=PeekI(*layer)
      *startBias=PeekI(*Layer+#S*2)
      *startOutput=PeekI(*layer+#S*3)
      *startError=PeekI(*layer+#S*4)
      *startSom=PeekI(*layer+#S*6)
      type.i=PeekI(*layer+#S*7)
      
      *startWeight=*Layer+#S*8
      *Player=PeekI(*net+#S*(i-1))
      Pnbneuron=PeekI(*Player)
      *PstartOutput=PeekI(*Player+#S*3)
      *PstartError=PeekI(*Player+#S*4)
      If type=#TypeSoftMax
         somExp.d=0
         For j=0 To nbneuron-1
            som.d=PeekD(*startSom+j*#SD)
            somExp.d=somExp+Exp(som)
         Next j
      EndIf
      For j=0 To nbneuron-1
         If i<nblayer And Random(100)<drop_rate:Continue:EndIf
         erreur.d=PeekD(*startError+j*#SD)
         outputneuron.d=PeekD(*startOutput+j*#SD)
         If type=#TypeSigmoid
            formula.d=1-outputneuron                 ;(1-f(x))
            formula=outputneuron*formula             ;f'(x)=f(x) * (1-f(x))
         ElseIf type=#TypeSwish1
            som.d=PeekD(*startSom+j*#SD)
            formula.d=1-outputneuron                 ;(1-f(x))
            formula=outputneuron/som*formula         ;(f(x)/x) * (1-f(x))
            formula=outputneuron+formula             ;f'(x) = f(x) + (f(x)/x) * (1-f(x))
         ElseIf type=#TypeTanH
            formula.d=1-(outputneuron*outputneuron) 
         ElseIf type=#TypeLeakyRelu
            som.d=PeekD(*startSom+j*#SD)
            If som<0.0
               formula.d=0.01
            Else
               formula.d=1.0
            EndIf
         ElseIf type=#TypeSoftMax
            som.d=PeekD(*startSom+j*#SD)
            somsquare.d=somExp*somExp
            som2.d=somExp-Exp(som)
            formula.d=(som2*Exp(som))/somsquare
         EndIf
         For k=0 To Pnbneuron-1
            prevoutput.d=PeekD(*PstartOutput+k*#SD)
            weight.d=PeekD(*startWeight+j*#SD*Pnbneuron+k*#SD)
            gradient.d=-erreur*formula*prevoutput*LearningRate
            newWeight.d=weight-gradient
            PokeD(*startWeight+j*#SD*Pnbneuron+k*#SD,newWeight)
         Next k
         gradient=-erreur*formula*LearningRate
         bias.d=PeekD(*startBias+j*#SD)
         newBias.d=bias-gradient
         PokeD(*startBias+j*#SD,newBias)
      Next j
   Next i
EndProcedure

ProcedureDLL SaveNN(*net,name$)
   nblayer.i=PeekI(*net)
   CreateFile(0,name$)
   WriteInteger(0,nblayer)     ;nblayer
   For i=1 To nblayer
      *layer=PeekI(*net+#S*i)
      nbneuron.i=PeekI(*layer)
      WriteInteger(0,nbneuron) ;nb neuron
      WriteInteger(0,PeekI(*layer+#S)) ;nb prev neuron
      WriteInteger(0,PeekI(*layer+#S*7)) ;type
      ;if input layer, continue
      nbneuron-1
      If i=1:Continue:EndIf
      *endSom=PeekI(*layer+#S*6)+nbneuron*#SD
      *layer=*layer+#S*8
      For j=*layer To *endsom Step #SD
         WriteDouble(0,PeekD(j))
      Next j
   Next i
   CloseFile(0)
EndProcedure

ProcedureDLL LoadNN(name$)
   If ReadFile(0,name$)=0:ProcedureReturn 0:EndIf
   nblayer.i=ReadInteger(0)
   *net=CreateNet() 
   For i=1 To nblayer
      nbneuron.i=ReadInteger(0)
      nbprevneuron.i=ReadInteger(0)
      type.i=ReadInteger(0)
      *net=AddLayer(*net,nbneuron,type)
      *layer=PeekI(*net+#S*i)
      nbneuron-1
      ;if input layer, continue
      If i=1:Continue:EndIf
      *endSom=PeekI(*layer+#S*6)+nbneuron*#SD
      *layer=*layer+#S*8
      For j=*layer To *endsom Step #SD
         PokeD(j,ReadDouble(0))
      Next j
   Next i
   EndLayer(*net)
   CloseFile(0)
   ProcedureReturn *net
EndProcedure

Code : Tout sélectionner

;program To visualize the neural net
CompilerIf #PB_Compiler_Processor=#PB_Processor_x86
   #S=4
CompilerElse
   #S=8
CompilerEndIf
#SD=8
#TypeInput=0
#TypeSigmoid=1
#TypeSwish1=2
#TypeSoftMax=3
#TypeLeakyRelu=4
#TypeTanH=5
#real=0
#positive=1
If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse() = 0 Or OpenWindow(0, 0, 0, 800, 600, "Neural Network Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)=0 Or OpenWindowedScreen(WindowID(0),0,0,800,600,0,0,0,#PB_Screen_NoSynchronization )=0
   MessageRequester("Error", "Can't open the sprite system", 0)
   End
EndIf

Import "NeuroNetLib.lib"
   CreateNet()
   AddLayer(*net,NbNeuron.i,type.i)
   InitWeight(*net,RandomType.i)
   EndLayer(*net)
   ReadOutput.d(*net,layer.i,neuron.i)
   ReadSomme.d(*net,layer.i,neuron.i)
   ReadBiasWeight.d(*net,layer.i,neuron.i)
   ReadError.d(*net,layer.i,neuron.i)
   ReadWeight.d(*net,layer.i,neuron.i,weight.i)
   ReadTarget.d(*net,neuron.i)
   ReadNbNeuron(*net,layer.i)
   ReadPrevNbNeuron(*net,layer.i)
   SetWeight(*net,layer.i,neuron.i,weight.i,value.d)
   SetBiasWeight(*net,layer.i,neuron.i,bias.d)
   DeleteNet(*net)
   activation.d(output.d)
   Propagate(*net)
   SetInput(*net,Input.i,value.d)
   SetDesireOutput(*net,Output.i,value.d)
   CalculError.d(*net)
   GradientDescent(*net,LearningRate.d,drop_rate.i)
   LoadNN(name$)
   SaveNN(*net,name$)
EndImport
Procedure testxor(*net,p1.i,p2.i)
   nblayer=PeekI(*net)
   SetInput(*net,1,p1)
   SetInput(*net,2,p2)
   Propagate(*net)
   r.i=Round(ReadOutput(*net,nblayer,1),#PB_Round_Nearest)
   sxor=#Red
   If r=p1!p2
      Sxor=#Green
   EndIf
   ProcedureReturn sxor
EndProcedure

Procedure testand(*net,p1.i,p2.i)
   nblayer=PeekI(*net)
   SetInput(*net,1,p1)
   SetInput(*net,2,p2)
   Propagate(*net)
   r.i=Round(ReadOutput(*net,nblayer,2),#PB_Round_Nearest)
   sand=#Red
   If r=p1&p2
      Sand=#Green
   EndIf
   ProcedureReturn sand
EndProcedure

Procedure testor(*net,p1.i,p2.i)
   nblayer=PeekI(*net)
   SetInput(*net,1,p1)
   SetInput(*net,2,p2)
   Propagate(*net)
   r.i=Round(ReadOutput(*net,nblayer,3),#PB_Round_Nearest)
   sor=#Red
   If r=p1|p2
      Sor=#Green
   EndIf
   ProcedureReturn sor
EndProcedure

;create neural network
#nbLayer=3
#nbInput=2
LearningRate.d=0.01
*net=CreateNet()
*net=AddLayer(*net,#nbInput,#TypeInput) ;layer 1
*net=AddLayer(*net,6,#TypeSwish1)       ;layer 2
*net=AddLayer(*net,3,#TypeSigmoid)    ;layer 3
EndLayer(*net)
InitWeight(*net,#real)

;test data
Structure dataset
   input1.d
   input2.d
   result1.d
   result2.d
   result3.d
EndStructure
Dim dataset.dataset(11)

;XOR AND OR
For i=0 To 1
   For j=0 To 1
      dataset(a)\input1=i
      dataset(a)\input2=j
      bool.i=i!j
      dataset(a)\result1=bool
      bool.i=i&j
      dataset(a)\result2=bool
      bool.i=i|j
      dataset(a)\result3=bool
      a+1
   Next j
Next i

Repeat
   Repeat:Until WindowEvent()=0
   FlipBuffers()
   ClearScreen(#Black)
   ExamineKeyboard()
   
   ;train the network
   For u=1 To 10
      RandomizeArray(dataset())
      For ti=0 To 3
         SetInput(*net,1,dataset(ti)\input1)
         SetInput(*net,2,dataset(ti)\input2)
         Propagate(*net)
         SetDesireOutput(*net,1,dataset(ti)\result1) ;XOR
         SetDesireOutput(*net,2,dataset(ti)\result2) ;AND
         SetDesireOutput(*net,3,dataset(ti)\result3) ;OR
         CalculError(*net)
         GradientDescent(*net,LearningRate,0)
      Next ti
   Next u
   
   StartDrawing(ScreenOutput())
   ;display inputs
   y.i=100-(#nbInput<<1)*100
   min.d=10000
   a1=0:a2=0:a3=0
   For i=1 To #nblayer
      nbneuron.i=ReadNbNeuron(*net,i)
      nbprevneuron.i=ReadPrevNbNeuron(*net,i)
      y=200-(nbneuron>>1)*100
      yy=200-(nbprevneuron>>1)*100
      For j=1 To nbneuron
         For t=1 To nbprevneuron
            dx1=100+i*150
            dy1=j*120+y-7+t
            dx2=100+(i-1)*150
            dy2=t*120+yy-7+t
            dx3=(dx1-dx2)/3+dx2
            dy3=(dy1-dy2)/3+dy2-10
            LineXY(dx1,dy1,dx2,dy2)
            DrawText(dx3,dy3,StrD(readWeight(*net,i,j,t),3))
         Next t
      Next j
   Next i
   y.i=200-(#nbInput>>1)*100
   For i=1 To #nbInput
      Circle(250,i*120+y,40)  
      Circle(250,i*120+y,38,#Black) 
      DrawText(234,i*120+y-7,StrD(readOutput(*net,1,i),3)) 
   Next i
   
   For i=2 To #nblayer
      nbneuron=ReadNbNeuron(*net,i)
      nbprevneuron=ReadprevNbNeuron(*net,i)
      y.i=200-(nbneuron>>1)*100
      yy.i=200-(nbprevneuron>>1)*100
      For j=1 To nbneuron
         Circle(100+i*150,j*120+y,40)  
         Circle(100+i*150,j*120+y,38,#Black) 
         DrawText(84+i*150,j*120+y-40,StrD(readOutput(*net,i,j),3),#Green)
         If i=#nblayer
            DrawText(84+i*150,j*120+y-20,StrD(readTarget(*net,j),3),#White)
         EndIf
         DrawText(84+i*150,j*120+y,StrD(readError(*net,i,j),3),#Red)
         DrawText(84+i*150,j*120+y+20,StrD(readSomme(*net,i,j),3),#Yellow)
         DrawText(84+i*150,j*120+y+40,StrD(ReadBiasWeight(*net,i,j),3),#Blue)
      Next j
   Next i
   ;display square
   For i=0 To 100
      For t=0 To 100
         SetInput(*net,1,i/100)
         SetInput(*net,2,t/100)
         Propagate(*net)
         color1.d=readOutput(*net,#nblayer,1)*255
         color2.d=readOutput(*net,#nblayer,2)*255
         color3.d=readOutput(*net,#nblayer,3)*255
         Plot(600+i,t+170,RGB(color1,color1,color1))
         Plot(600+i,t+300,RGB(color2,color2,color2))
         Plot(600+i,t+440,RGB(color3,color3,color3))
      Next t
   Next i
   DrawingMode(#PB_2DDrawing_Transparent) 
   DrawText(600,170,"0,0=0",testxor(*net,0,0))
   DrawText(665,170,"1,0=1",testxor(*net,1,0))
   DrawText(600,80+170,"0,1=1",testxor(*net,0,1))
   DrawText(665,80+170,"1,1=0",testxor(*net,1,1))
   DrawText(640,40+170,"XOR",#White)
   DrawText(600,300,"0,0=0",testand(*net,0,0))
   DrawText(665,300,"1,0=0",testand(*net,1,0))
   DrawText(600,80+300,"0,1=0",testand(*net,0,1))
   DrawText(665,80+300,"1,1=1",testand(*net,1,1))
   DrawText(640,40+300,"AND",#White)
   DrawText(600,440,"0,0=0",testor(*net,0,0))
   DrawText(665,440,"1,0=1",testor(*net,1,0))
   DrawText(600,80+440,"0,1=1",testor(*net,0,1))
   DrawText(665,80+440,"1,1=1",testor(*net,1,1))
   DrawText(640,40+440,"OR",#White)
   StopDrawing()
Until KeyboardPushed(#PB_Key_Escape)
Dernière modification par Fig le sam. 17/mars/2018 12:46, modifié 7 fois.
Il y a deux méthodes pour écrire des programmes sans erreurs. Mais il n’y a que la troisième qui marche.
Version de PB : 6.00LTS - 64 bits
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Apprentissage par descente de gradiant: XOR

Message par Ollivier »

Si déjà mon propre réseau de neurones parvient à écrire un message, ce sera un pas en avant !

Pardon... J'ai réussi. Ben félicitation Fig : tu m'as mis KO pour le réveil ! J'ai essayé de comprendre, mais j'ai, comme qui dirait échoué.

Il ne me reste plus que l'option " télécharge et exécute ", action que je ferai quelquepart ce week-end.
Avatar de l’utilisateur
Fig
Messages : 1176
Inscription : jeu. 14/oct./2004 19:48

Re: Apprentissage par descente de gradiant: XOR

Message par Fig »

Bonjour, Olliver.

Effectivement ça mérite une petite explication.
La Dll permet de créer un réseau de neurones formels (https://fr.wikipedia.org/wiki/Neurone_formel).
Ces neurones sont organisés en couches.
Ils sont des approximateurs universels de fonctions: ils sont capables de reproduire n'importe quelle fonction mathématique. Ceci est utilisé dans une grande diversité d'application d'intelligence artificielle.
Pour cela ils doivent être paramétrés. A cet effet différentes techniques peuvent être employées.
Par exemple:
Algorithme génétique de selection comme je l'ai fait avec les voitures sur le circuit.
Algorithme de retropropagation comme je le fais ici.

Basiquement, on affecte des valeurs aléatoires au réseau pour commencer.
Ensuite, on présente un jeu d'entrées au réseau.
On contrôle la ou les sorties. On calcule l'erreur faite sur cette ou ces sorties par rapport à la valeur attendue connue.
Cette erreur est ensuite calculée à rebours , couche par couche jusqu'a l'entrée du réseau.
A partir de cette erreur, on calcule le gradient qui permet d'ajuster les valeurs des poids d'entrée de chaque neurone pour se diriger vers la bonne valeur de sortie.

Au bout d'un moment, le réseau converge vers les réponses souhaitées: Il a appris une fonction.
Ici je lui fais apprendre la fonction XOr.
J'affiche sous forme d'une image l'apprentissage. les deux entrées sont représentés par les ordonnées et absisses de 0 à 1 (0 à 100 pixels) la sortie par une nuance de gris de noir (0) à blanc (1).
Deux formes d'apprentissages également valables:
Image
Image

Ce type de réseau peut apprendre à reconnaître des caractères manuscrits à partir d'exemples puis généraliser cet apprentissage pour reconnaître n'importe quel caractère tracé. (le système de lecture d'adresse de la Poste fonctionne ainsi).
C'est ma prochaine étape.
Dernière modification par Fig le sam. 24/févr./2018 11:00, modifié 2 fois.
Il y a deux méthodes pour écrire des programmes sans erreurs. Mais il n’y a que la troisième qui marche.
Version de PB : 6.00LTS - 64 bits
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Apprentissage par descente de gradiant: XOR

Message par djes »

Un grand merci Fig ! C'est le genre de choses que j'envisage d'étudier depuis des lustres, et je n'ai jamais dépassé les prémices. C'est un défrichage bien utile.
Avatar de l’utilisateur
Fig
Messages : 1176
Inscription : jeu. 14/oct./2004 19:48

Re: Apprentissage par descente de gradiant: XOR

Message par Fig »

Merci pour vos encouragements.
On ne peut plus faire deux pas sans entendre parler de "DeepLearning".
On va voir jusqu'où on peut aller ainsi... Il va falloir d'autres types de fonction pour les neurones (actuellement sigmoïd) pour la reconnaissance d'images plus intéressantes. (le passage en Assembleur de la Dll sera simple vu comme je l'ai écrite... Puis la réécrire en shader opengl -arg !-)
Chaque chose en son temps mais c'est très enthousiasmant.
Il y a deux méthodes pour écrire des programmes sans erreurs. Mais il n’y a que la troisième qui marche.
Version de PB : 6.00LTS - 64 bits
Avatar de l’utilisateur
Ar-S
Messages : 9472
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: Apprentissage par descente de gradiant: XOR

Message par Ar-S »

Fascinant, trop perché pour moi mais fascinant.
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Avatar de l’utilisateur
Fig
Messages : 1176
Inscription : jeu. 14/oct./2004 19:48

Re: Apprentissage par descente de gradiant: XOR AND OR.

Message par Fig »

MAJ: Fonction OR, AND et XOR en même temps.
MAJ Dll, ajout des fonctions d'activation Swish, LeakyRelu et TanH.
MAJ DLL ajout de fonction de sauvegarde et chargement d'un réseau
Il y a deux méthodes pour écrire des programmes sans erreurs. Mais il n’y a que la troisième qui marche.
Version de PB : 6.00LTS - 64 bits
Répondre