/*****************************************************************************************************
Code: The PINN Class that creates, trains and evaluates the PINN memory.
Author: Dmitry O. Gorodnichy, 1993-2003
Copyright: The author grants the right to use the code,
provided the appropriate references are acknowledged.
For the description of all parameters in the code, and for the detailed description of the
properties and performance of the PINN memory see http://www.cv.iit.nrc.ca/~dmitry/pinn
and the following papers.
[1] D.O. Gorodnichy. "A Way to Improve Error Correction Capability of Hopfield Associative Memory in the
Case Of Saturation", Proc. of HELNET 94-95 Intern. Workshop on Neural Networks, Vol. I/II, pp.198-212, VU University Press, Amsterdam.
[2] D.O. Gorodnichy and A.M. Reznik. "Increasing Attraction of Pseudo-Inverse Autoassociative Networks",
Neural Processing Letters, volume 5, issue 2, pp. 123-127, 1997.
[3] D.O. Gorodnichy and A.M. Reznik. "Static and Dynamic Attractors of Autoassociative Neural Networks",
Proc. of 9th Intern. Conf. on Image Analysis and Processing (ICIAP'97), Florence, Vol. II, pp. 238-245, 1997.
[3] Dmitry O. Gorodnichy. "The Optimal Value of Self-Connection", CD-ROM Proc. of Intern. Joint Conf. on
Neural Networks (IJCNN'99), Washington DC, July 1999 - "Best Presentation" award.
Details of the code:
- Memorization of the patterns is done using the recursive Greville formula (obtained in [1]),
which is very fast and suitable for on-fly memory update.
- Recognition of the patterns is done using the flood-fill neuro-processing technique (described in [2])
which yields very fast recognition suitable for on-line recognition.
Instructions:
Under Visial C++, the code compiles and runs as it is.
- It creates M random binary samples of size N and stores them in a file.
- It memorizes the patterns in the network,
after which is computes the network's remaining capacity
- Then it retrieves from memory the same patterns corrupted by nNoise percentage of noise,
and prints the result of recognition: remaining noise (nHemming) and projection residue (E)
The size of the network (N), the sample size (M), and the percentage of noise (nNoise) can be changed
in main(). The examples of suitable values are:
N=1000, M=300, nNoise=10% - for regular network
N=1000, M=300, nNoise=30% - for desaturated network
***********************************************************************************************************/
#include
#include
#include
// Utility functions
int compareSamples(signed char *Y, signed char *V, int N);
bool writeRandomBinarySamples(int nNetSize=100, int nSamples=50, char *sFileName="pinn-samples.txt", double dPercentageOfOnes=0.2, int nRndSeed=1);
void getSample(signed char **V, int N, char *string);
void addNoise(signed char *Y, signed char **V, int N, int nNoise);
class CPINN
{
public:
CPINN();
virtual ~CPINN();
void createMemory(int N=100);
void deleteMemory();
bool putInMemory(signed char *V);
void analyzeMemory(); // examine how filled the memory is
void setDesaturationLevel(double dDesaturationLevel);
void desaturateMemory();
void setMemoryToDormantState(); // in which all neurons are in unexcited (-1) state
int retrieveFromMemory(signed char *Y0, signed char **Yattractor);
bool bCreated;
int m_N;
int m_M; // number of stored prototypes in network's memory
float
**m_C, // matrix C of weightes (aka synaptic weights) of the network
*m_S, // S = CY, postsynaptic potentials (PSP) of the network
*m_S0,
m_E; // E = || CY - Y ||
signed char
*m_Y; // state (aka potentials, or neurons, or neuron states) of the network
double m_D;
int *m_aExcited; // buffer which stored indices of excited neurons
int m_nExcitedNow, m_nExcitedLast; // number of excited neurons at current and previous state of the network
bool m_bCycleOccured; // redundant, as m_nExcitedNow>0 => m_bCycleOccured
int m_nIter;
double m_dAveCii, m_dAveCij2, m_dAveAbsCij2;
};
CPINN::CPINN()
{
bCreated = false;
}
CPINN::~CPINN()
{
deleteMemory();
}
void CPINN::deleteMemory()
{
if (bCreated)
{
delete m_Y;
delete m_S;
delete m_S0;
delete m_aExcited;
for (int i=0; i = %.5f,\t = %.5f vs <|Cij|>^=%.5f\n", X,Y,Z);
m_dAveCii = X;
m_dAveCij2 = Y;
m_dAveAbsCij2 = Z;
// Compute approximations of Average values of weights Cii and Cij by formula from Ref [2]
X=(double)m_M/m_N;
Y=(double)m_M*(m_N-m_M)/m_N/m_N/m_N;
printf("vs M/N = %.5f,\tM(N-M)/N3 = %.5f \n", X,Y);
}
void CPINN::setDesaturationLevel(double dDesaturationLevel)
{
m_D = dDesaturationLevel;
}
void CPINN::desaturateMemory()
{
for(int i=0;i0)
m_aExcited[m_nExcitedNow++]=+i+1;
}
// Iterate until the network converges to an attractor, either static or dynamic(cycle).
for(m_nIter=0, bAttractorAchieved=false; !bAttractorAchieved ;m_nIter++)
{
for(int e=0; e0) )
{
m_Y[i]=-1;
if (m_aExcited[m_nExcitedNow]==i+1) // cycle check: see whether this neuron was excited in previous iteration
nOscilatingNeurons++; // if yes, update nOscilatingNeurons
m_aExcited[m_nExcitedNow++]=-i-1; // update buffer
}
else if ( (m_S[i]>=0) && (m_Y[i]<0) ) // another option: (m_S[i]>=0)
{
m_Y[i]=+1;
if (m_aExcited[m_nExcitedNow]==-i-1) // for check of a cycle
nOscilatingNeurons++;
m_aExcited[m_nExcitedNow++]=+i+1;
}
else
i=i;
}
if (m_nExcitedNow==0)
{
m_bCycleOccured = false; // it is a static attractor
bAttractorAchieved = true;
}
if (m_nExcitedNow == m_nExcitedLast && nOscilatingNeurons==m_nExcitedNow)
{
m_bCycleOccured = true;// it is a cycle
bAttractorAchieved = true;
}
} // end of iteration loop
// Check how close the retrieved pattern is to those which were stored: m_E = ||CY-Y||^2
double E;
for(m_E=m_N, i=0, E=0; i^2=%.4f, =%.4f, D=%.2f\n",
pinn.m_dAveCii*pinn.m_dAveCii, pinn.m_dAveCij2, pinn.m_D);
/***
Memory recognizes well when it is not saturated.
Whether the memory is saturated or not can be told by looking at ratio / ,
which increases as the memory learns new patterns.
It is advisable to desaturate the memory when / approaches 1/N.
***/
// Uncomment these two lines to see the improvement due to the desaturation !
//
// pinn.setDesaturationLevel(0.15);
// pinn.desaturateMemory();
pinn.setMemoryToDormantState();
// Recognize(Retrieve) patterns from
printf("\nRetrieval: \n\tN=%i, M=%i, Noise=%i\n", N,M,nNoise);
printf("# \tHemming\t|CV-V|\t\t#Iter \t#Oscilating\n");
fout=fopen("pinn-res.txt", "w");
fprintf(fout, "Retrieval from Noise = Noise=%i\n", nNoise);
fprintf(fout, "# \tHemming\t|CV-V|\t\t#Iter \t#Oscilations\n");
f=fopen(sFileName, "r");
fgets(sPattern, 100, f); /* first comment line */
fgets(sPattern, 100, f); /* first comment line */
for (i=0;i