![]()
Back Propagation (Low level code)
2022.10.17 13:08
Very old code, yet still valuable.
(Original Source) : http://ktiml.mff.cuni.cz/~bozovsky/en/bp.htm
| Pascal Exemplary Implementation of Back Propagation |
program BackPropagation; const MaxLayer = 5; { max. number of layers } MaxNeuron = 15; { max. number of neurons in one layer } MaxPattern = 50; { max. number of patterns } type Layers = 0..MaxLayer; { available layers } Neurons = 1..MaxNeuron; { available neurons } NeurThrs = 0..MaxNeuron; { neurons including thresholds source } Patterns = 1..MaxPattern; { usable patterns } Weights = array [Layers,NeurThrs,Neurons] of real; { Weights[i,j,k] : } { if j>0 ... weight from neuron j in layer i to } { neuron k in layer i+1 } { if j=0 ... threshold of neuron k in layer i+1 } var w,wold : Weights; { values of weights in time t and t-1 } x : array [Layers,NeurThrs] of real; { x[i,j] : } { if j>0 ... output value of neuron j in layer i } { if j=0 ... value -1 used as a threshold source } delta : array [Layers,Neurons] of real; { delta[i,j] = see remark after Eq.(18), concerning now neuron j in layer i } lmax : Layers; { layers = 0 [bottom]..lmax [top] } n : array [Layers] of Neurons; { number of neurons in each layer } t : Patterns; { number of learning patterns } xt, yt : array [Patterns,Neurons] of real; { all input and expected output patterns from T } y : array [Neurons] of real; { expected output pattern for one chosen pair from T } eta, alpha : real; { parameters of the algorithm - see Eq.(21) } Iters : integer; { number of iterations } Cycles : integer; { number of cycles } function S ( ksi:real ) : real; { neuron sigmoid transfer function } const lambda = 1; { sigmoid gain } RB = 30; { where to extrapolate the sigmoid by a constant } var inp : real; begin inp:=lambda*ksi; if inp>30 then S:=1 else if inp<-30 then S:=0 else S:=1/(1+exp(-inp)); end; procedure State; { new state of the network } var l : Layers; j : NeurThrs; k : Neurons; ksi : real; { neuron potential } begin for l:=1 to lmax do for k:=1 to n[l] do begin ksi:=0; for j:=0 to n[l-1] do ksi:=ksi+w[l-1,j,k]*x[l-1,j]; { neuron potential } x[l,k]:=S(ksi) { neuron output } end end; { x[lmax,k] is an actual output of the network } procedure ChangeWeights ( l:Layers ); { new weights for one layer } var j : NeurThrs; k : Neurons; saveW : real; begin for k:=1 to n[l+1] do for j:=0 to n[l] do begin saveW:=w[l,j,k]; w[l,j,k]:=w[l,j,k]- eta*delta[l+1,k]*x[l,j] + alpha*(w[l,j,k]-wold[l,j,k]); wold[l,j,k]:= saveW; end; end; procedure MakeDelta ( l:Layers ); { new delta's for one layer } var j, k : Neurons; CumulEr : real; { cumulative error over neurons in a layer } begin for j:=1 to n[l] do begin if l=lmax { top layer } then CumulEr:=x[lmax,j]-y[j] else begin CumulEr:=0; { calculate from previous layer } for k:=1 to n[l+1] do CumulEr:=CumulEr+delta[l+1,k]*w[l,j,k]; end; delta[l,j]:=x[l,j]*(1-x[l,j])*CumulEr end end; procedure NewWeights; { network new weights } var l : Layers; begin for l:=lmax-1 downto 0 do begin MakeDelta(l+1); { set up delta's in upper layer } ChangeWeights(l); { calculate weights in this layer } end end; function GlobalError : real; { global error over all layers of the network } var p : Patterns; j : Neurons; Er : real; begin Er:=0; for p:=1 to t do begin for j:=1 to n[0] do x[0,j]:=xt[p,j]; for j:=1 to n[lmax] do y[j]:=yt[p,j]; State; for j:=1 to n[lmax] do Er:=Er+Sqr(x[lmax,j]-y[j]); end; GlobalError:=Er; end; procedure Training; { provides learning of the patterns } var p : Patterns; j : Neurons; Error : real; { cumulative error for one iteration } iter, cycle : integer; begin writeln; { format for printed information } writeln('Iteration LayerError Pattern Cycle GlobalError'); for cycle:=1 to Cycles do begin write(chr(13),cycle:38,GlobalError:14:5); { prints of values } for p:=1 to t do begin write(chr(13),p:29); for j:=1 to n[0] do x[0,j]:=xt[p,j]; for j:=1 to n[lmax] do y[j]:=yt[p,j]; for iter:=1 to Iters do begin State; Error:=0; for j:=1 to n[lmax] do Error:=Error+Sqr(x[lmax,j]-y[j]); NewWeights; write(chr(13),iter:5,Error:16:5); end; end; end; writeln(chr(13),GlobalError:52:5); end; procedure Testing; { you can try how well the network is learned, } { specifying on the request one or more input vectors } var i : Neurons; c : char; begin writeln; repeat write('Enter network inputs (',n[0],' values) : '); for i:=1 to n[0] do read(x[0,i]); readln; State; write('Output of the network is',':':9); for i:=1 to n[lmax] do write(x[lmax,i]:5:2); write(' More testing [Y/N] ? '); read(c); until (c='N')or(c='n'); writeln; end; procedure InitNetwork; { !! network parameters initialization routine } var l : Layers; { this is the only task dependent procedure !! } j : NeurThrs; k : Neurons; f : text; begin lmax:=2; { the program will deal with the 4-2-4 network } n[0]:=4; n[1]:=2; n[2]:=4; RandSeed:=3456; {! remove the following brackets numbered 1 if you want to start always !} {! with new random weights; if you wish to repeat your experiments again !} {! using the same initialization of weights, let them be there !} {1 Randomize; 1} for l:=0 to lmax-1 do for j:=0 to n[l] do for k:=1 to n[l+1] do w[l,j,k]:=6*(Random-0.5)/10; wold:=w; eta:=0.3; alpha:=0.7; { choice of learning parameters } Iters:=15; Cycles:=40; { choice of number of iterations and cycles } {! remove brackets 2 if you do not want to create your own file of patterns!} {! according to similar template. After removing the brackets 2, you will !} {! train the net on identity of vertices of 4-dimensional cube as listed; !} {! note that the file starts with the number of training pairs. !} { copy patterns into file PATTERNS } {2 assign(f,'PATTERNS'); rewrite(f); writeln(f,5); writeln(f,'1 1 0 0 1 1 0 0'); writeln(f,'0 0 1 1 0 0 1 1'); writeln(f,'1 0 1 0 1 0 1 0'); writeln(f,'0 1 0 1 0 1 0 1'); writeln(f,'0 0 0 0 0 0 0 0 '); close(f); 2} end; procedure InitImpl; { implementation init routine } var l : Layers; begin for l:=0 to lmax-1 do x[l,0]:=-1; { used as a threshold source for next layer } end; procedure InitPatterns; { learning patterns init routine } var p : Patterns; j : Neurons; f : text; begin assign(f,'PATTERNS'); reset(f); { use your own file of training set } read(f,t); writeln; { number of patterns } for p:=1 to t do begin for j:=1 to n[0] do begin read(f,xt[p,j]); { read inputs from PATTERNS } write(xt[p,j]:5:2); { and print them on screen } end; write(' '); for j:=1 to n[lmax] do begin read(f,yt[p,j]); { read outputs from PATTERNS } write(yt[p,j]:5:2); { and print them on screen } end; readln(f); writeln; end; close(f); end; begin { BackPropagation } InitNetwork; InitImpl; InitPatterns; Training; Testing; end. { BackPropagation } |
| No. | Subject | Author | Date | Views |
|---|---|---|---|---|
| 5 | CAI's Hypotenuse example's pre-trained model loading for use. | me | 2024.03.02 | 193 |
| 4 |
Kohonen Feature Maps (Low level code)
| me | 2022.10.17 | 248 |
| » | Back Propagation (Low level code) [1] | me | 2022.10.17 | 306 |
| 2 |
CNN demo with CIFAR100 (Delphi only)
| me | 2022.10.17 | 218 |
| 1 | CAI NEURAL API - the best one. | me | 2022.10.17 | 268 |
// Other Resource in Turbo Pascal 6
{This is the source code for the simple neural network written in Turbo Pascal 6.0. The simplest way to download this file is to go the 'View Source Code' option of your browser and select the 'Save' option from the file menu. It should run without any changes. (Ignore the HTML and PRE tokens below - this is just to ensure that your Web browser doesn't mess around with the text formatting
} program simple_neural_net; uses Crt; {This is a unit which contains the ReadKey routine. If you can implement this a different way, you won't need this line} const MAX_INP = 4; {Maximum number of nodes in input layer} MAX_HID = 4; {Maximum number of nodes in hidden layer} MAX_OUT = 2; {Maximum number of nodes in output layer} MAX_PAT = 4; {Maximum number of training patterns} {These two arrays contain the training input patterns and the corresponding output patterns. In this case they form a binary encoder, changing 0010 into 01 and 1000 into 11 etc. but feel free to change them into anything else you want. That is why they are arrays of real values rather than bytes or integers} INP_PATTERNS : array [1..MAX_PAT,1..MAX_INP] of real = ((0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0)); OUT_PATTERNS : array [1..MAX_PAT,1..MAX_OUT] of real = ((0,0),(0,1),(1,0),(1,1)); type {An array to hold any input patterns that are used for training or typed in} inp_pattern_type = array [1..MAX_INP] of real; {An array for holding output patterns} out_pattern_type = array [1..MAX_OUT] of real; {All neurons are defined as having MAX_INP weights from the previous layer, an output value and a threshold} weights_type = array [1..MAX_INP] of real; neuron_type = record w : weights_type; {The actual weights themselves} change : weights_type; {Changes in weights - used in training only} threshold,a : real; t_change : real; {Change in threshold - used in training only} E : real {Error for this node, used in} end; {training} var test_pat : inp_pattern_type; desired : out_pattern_type; ipl : array [1..MAX_INP] of neuron_type; {Input layer} hl : array [1..MAX_HID] of neuron_type; {Hidden layer} ol : array [1..MAX_OUT] of neuron_type; {Output layer} {Ask the user whether to continue or not. Returns true or false} function continue : boolean; var k : char; begin writeln; writeln(' Do you want another test pattern? (Press Y or N)'); repeat k:=ReadKey until (k='Y') or (k='y') or (k='N') or (k='n'); if (k='Y') or (k='y') then continue:=true else continue:=false end; {Ask the user to type a pattern which is to be used for testing} procedure get_test_pattern; var i : byte; begin writeln; writeln('Please enter the values for the test pattern.'); writeln('You should type in ',MAX_INP,' values (press Enter after each one).'); for i:=1 to MAX_INP do begin write(' Please enter value ',i,' : '); readln(test_pat[i]) end end; {THE FOLLOWING PROCEDURES ARE FOR RUNNING THE NETWORK (FORWARD PROPAGATION)} function sigmoid (x : real) : real; begin if abs(x)<38 {Handle possible overflow} then sigmoid:=1/(1+exp(-x)) {exp only valid between -39 and 38} else if x>=38 then sigmoid:=1 else sigmoid:=0 end; procedure run_input_layer; var i,j : byte; sum : real; begin for i:=1 to MAX_INP do with ipl[i] do begin sum:=0; for j:=1 to MAX_INP do sum:=sum+w[j]*test_pat[j]; a:=sigmoid(sum-threshold) end end; procedure run_hidden_layer; var i,j : byte; sum : real; begin for i:=1 to MAX_HID do with hl[i] do begin sum:=0; for j:=1 to MAX_INP do sum:=sum+w[j]*ipl[j].a; a:=sigmoid(sum-threshold) end end; procedure run_output_layer; var i,j : byte; sum : real; begin for i:=1 to MAX_OUT do with ol[i] do begin sum:=0; for j:=1 to MAX_HID do sum:=sum+w[j]*hl[j].a; a:=sigmoid(sum-threshold) end end; procedure run_the_network; begin run_input_layer; run_hidden_layer; run_output_layer end; {This procedure displays the results of the test on the screen.} procedure display_the_results; var i : byte; begin writeln; write('Inputs: '); for i:=1 to MAX_INP do write(test_pat[i],' '); writeln; write('Outputs: '); for i:=1 to MAX_OUT do write(ol[i].a,' '); writeln end; {THE FOLLOWING PROCEDURES ARE FOR TESTING THE NETWORK EITHER BEFORE OR AFTER TRAINING} procedure test_the_network; begin writeln; writeln('I will ask you for test patterns. At the end of each test you will'); writeln('be asked if you want to do another test.'); repeat get_test_pattern; run_the_network; display_the_results until not continue end; {THE FOLLOWING PROCEDURES ARE FOR TRAINING THE NETWORK} procedure calculate_output_layer_errors; var j : byte; begin for j:=1 to MAX_OUT do with ol[j] do E:=(desired[j] - a) * a * (1 - a) end; procedure calculate_hidden_layer_errors; var i,j : byte; sum : real; begin for i:=1 to MAX_HID do with hl[i] do begin sum:=0; {Find sum of error products for output layer} for j:=1 to MAX_OUT do sum:=sum + ol[j].E * ol[j].w[i]; E:=a * (1 - a) * sum end end; procedure calculate_input_layer_errors; var i,j : byte; sum : real; begin for i:=1 to MAX_INP do with ipl[i] do begin sum:=0; {Find sum of error products for output layer} for j:=1 to MAX_HID do sum:=sum + hl[j].E * hl[j].w[i]; E:=a * (1 - a) * sum end end; {You will notice that the lines changing the threshold values have been bracketed out in the following procedure - they should be there according to the theory, but if I include them, the network doesn't produce the correct values. If I miss them out, as I have now, it produces correct values. If anyone can throw any light on this, I would be most grateful!} procedure weight_change; const BETA = 0.9; {Learning rate} M = 0.9; {Momentum parameter} var i,j : byte; {for loop variables} begin {First tackle weights from hidden layer to output layer} {i refers to a node in hidden layer, j refers to node in output layer} for j:=1 to MAX_OUT do {go through all output nodes} with ol[j] do begin for i:=1 to MAX_HID do {Adapt all weights} begin change[i]:=BETA * E * hl[i].a + M * change[i]; {this is the previous value ------^} w[i]:=w[i] + change[i] end; {Now adapt threshold as if from a node with activation 1} t_change:=BETA * E * 1 + M * t_change; {threshold:=threshold + t_change} end; {Now tackle weights from input layer to hidden layer} {i refers to a node in input layer, j refers to node in hidden layer} for j:=1 to MAX_HID do {go through all hidden layer nodes} with hl[j] do begin for i:=1 to MAX_INP do {Adapt all weights} begin change[i]:=BETA * E * ipl[i].a + M * change[i]; {this is the previous value ------^} w[i]:=w[i] + change[i] end; {Now adapt threshold as if from a node with activation 1} t_change:=BETA * E * 1 + M * t_change; {threshold:=threshold + t_change} end; {Now tackle weights from input to net to input layer} {i refers to a pattern input, j refers to node in input layer} for j:=1 to MAX_INP do {go through all input layer nodes} with ipl[j] do begin for i:=1 to MAX_INP do {Adapt all weights} begin change[i]:=BETA * E * test_pat[i] + M * change[i]; {this is the previous value ------^} w[i]:=w[i] + change[i] end; {Now adapt threshold as if from a node with activation 1} t_change:=BETA * E * 1 + M * t_change; {threshold:=threshold + t_change} end end; {Perform back propagation on the network} procedure back_propagate; begin calculate_output_layer_errors; calculate_hidden_layer_errors; calculate_input_layer_errors; weight_change end; {Set the weights and thresholds for all the nodes to small random values in the range 0 to 1} procedure random_weights; var i,j : byte; begin for i:=1 to MAX_INP do with ipl[i] do begin for j:=1 to MAX_INP do w[j]:=random(1000)/1000; threshold:=random(1000)/1000 end; for i:=1 to MAX_HID do with hl[i] do begin for j:=1 to MAX_INP do w[j]:=random(1000)/1000; threshold:=random(1000)/1000 end; for i:=1 to MAX_OUT do with ol[i] do begin for j:=1 to MAX_HID do w[j]:=random(1000)/1000; threshold:=random(1000)/1000 end end; {At the start of back propagation, there are no weight changes to influence the next cycle, so clear the arrays} procedure blank_changes; var i,j : byte; begin for j:=1 to MAX_INP do with ipl[j] do begin for i:=1 to MAX_INP do change[i]:=0; t_change:=0 end; for j:=1 to MAX_HID do with hl[j] do begin for i:=1 to MAX_INP do change[i]:=0; t_change:=0 end; for j:=1 to MAX_OUT do with ol[j] do begin for i:=1 to MAX_HID do change[i]:=0; t_change:=0 end end; procedure train_the_network; var pat : byte; {This cycles through all the patterns} i : byte; {General 'for' loop variable} num_cycles,loop : longint; {Might be VERY big value!} begin writeln; write('Enter the number of training cycles (typically 100) : '); readln(num_cycles); blank_changes; {Clear all 'previous' weight changes} for loop:=1 to num_cycles do for pat:=1 to MAX_PAT do begin for i:=1 to MAX_INP do {Copies input pattern into} test_pat[i]:=INP_PATTERNS[pat,i]; {'test_pat' array} for i:=1 to MAX_OUT do {Copies output pattern into} desired[i]:=OUT_PATTERNS[pat,i]; {'desired' array} run_the_network; {Determine the outputs} back_propagate end end; begin randomize; random_weights; writeln; writeln('To start with, you should type in a few test patterns to see'); writeln('how the network performs before it is trained.'); test_the_network; train_the_network; writeln; writeln('Now you should type in a few more test patterns to see how the'); writeln('network performs after it has been trained.'); test_the_network end.