Click here to Skip to main content
15,886,199 members
Articles / Desktop Programming / MFC

Neural Network for Recognition of Handwritten Digits

Rate me:
Please Sign up or sign in to vote.
4.97/5 (240 votes)
5 Dec 200668 min read 2M   57.5K   571  
A convolutional neural network achieves 99.26% accuracy on a modified NIST database of hand-written digits.
// Preferences.cpp: implementation of the CPreferences class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MNist.h"
#include "Preferences.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPreferences::CPreferences() : m_pMainApp( NULL )
{
	
}

CPreferences::~CPreferences()
{
	
}

void CPreferences::ReadIniFile(CWinApp *pApp)
{
	ASSERT( m_pMainApp==NULL  &&  pApp!=NULL );
	m_pMainApp = pApp;
	
	// confirm that INI file exists, and write a default one from resources if it does not
	
	WIN32_FIND_DATA  fd;
	HANDLE hFile = ::FindFirstFile( m_pMainApp->m_pszProfileName, &fd );
	
	if ( hFile == INVALID_HANDLE_VALUE )
	{
		// file must not exist --  get default ini file from resources and 
		// write it to ..\YourAppName.ini (which is stored in m_pMainApp->m_pszProfileName)
		
		CFile f;
		CFileException fe;
		if ( f.Open( (LPCTSTR) m_pMainApp->m_pszProfileName, 
			CFile::modeCreate | CFile::modeWrite, &fe ) ) // note: didn't specify modeNoTruncate, so length is zero'd
		{
			HINSTANCE hInst = m_pMainApp->m_hInstance;
			HRSRC hINI = ::FindResource(hInst,
				MAKEINTRESOURCE( IDR_TEXT_DEFAULT_INI ),
				_T("TEXT") );
			
			DWORD size = ::SizeofResource(hInst, hINI);
			HGLOBAL hGlobal = ::LoadResource(hInst, hINI);
			LPVOID t = ::LockResource(hGlobal);
			f.Write( t, size );
			f.Close();
			::FreeResource(hGlobal);
		}
		else
		{		
			::MessageBox( NULL,	
				_T("Error encountered while trying to write default INI file\n")
				_T("Hard-coded baseline parameters will be used instead"),
				_T("INI File Creation Failed"),
				MB_OK | MB_ICONWARNING);
		}
		
	}
	
	::FindClose( hFile );
	
#if 0
	// set default values

	m_nMagicTrainingLabels = 0x00000801;
	m_nMagicTrainingImages = 0x00000803;

	m_nItemsTrainingLabels = 60000;
	m_nItemsTrainingImages = 60000;

	m_nMagicTestingLabels = 0x00000801;
	m_nMagicTestingImages = 0x00000803;

	m_nItemsTestingLabels = 10000;
	m_nItemsTestingImages = 10000;

	m_nRowsImages = g_cImageSize;
	m_nColsImages = g_cImageSize;

	m_nMagWindowSize = 5;
	m_nMagWindowMagnification = 8;

	m_dInitialEtaLearningRate = 0.001;
	m_dLearningRateDecay = 0.794328235;  // 0.794328235 = 0.001 down to 0.00001 in 20 epochs 
	m_dMinimumEtaLearningRate = 0.00001;
	m_nAfterEveryNBackprops = 60000;
	m_cNumBackpropThreads = 2;

	m_cNumTestingThreads = 1;
	
	// parameters for controlling distortions of input image

	m_dMaxScaling = 15.0;  // like 20.0 for 20%
	m_dMaxRotation = 15.0;  // like 20.0 for 20 degrees
	m_dElasticSigma = 8.0;  // higher numbers are more smooth and less distorted; Simard uses 4.0
	m_dElasticScaling = 0.5;  // higher numbers amplify the distortions; Simard uses 34 (sic, maybe 0.34 ??)

	// for limiting the step size in backpropagation, since we are using second order
	// "Stochastic Diagonal Levenberg-Marquardt" update algorithm.  See Yann LeCun 1998
	// "Gradient-Based Learning Applied to Document Recognition" at page 41

	m_dMicronLimitParameter = 0.10;  // since we divide by this, update can never be more than 10x current eta
	m_nNumHessianPatterns = 500;  // number of patterns used to calculate the diagonal Hessian

#endif
	// now read values from the ini file

	CString tSection;

	// Neural Network parameters

	tSection = _T( "Neural Network Parameters" );

	Get( tSection, _T( "Initial learning rate (eta)" ),			m_dInitialEtaLearningRate );
	Get( tSection, _T( "Minimum learning rate (eta)" ),			m_dMinimumEtaLearningRate );
	Get( tSection, _T( "Rate of decay for learning rate (eta)" ), m_dLearningRateDecay );
	Get( tSection, _T( "Decay rate is applied after this number of backprops" ), m_nAfterEveryNBackprops );
	Get( tSection, _T( "Number of backprop threads" ),			m_cNumBackpropThreads );
	Get( tSection, _T( "Number of testing threads" ),			m_cNumTestingThreads );
	Get( tSection, _T( "Number of patterns used to calculate Hessian" ),	m_nNumHessianPatterns );
	Get( tSection, _T( "Limiting divisor (micron) for learning rate amplification (like 0.10 for 10x limit)" ),	m_dMicronLimitParameter );


	// Neural Network Viewer parameters

	tSection = _T( "Neural Net Viewer Parameters" );

	Get( tSection, _T( "Size of magnification window" ),			m_nMagWindowSize );
	Get( tSection, _T( "Magnification factor for magnification window" ), m_nMagWindowMagnification );


	// MNIST data collection parameters

	tSection = _T( "MNIST Database Parameters" );

	Get( tSection, _T( "Training images magic number" ),	m_nMagicTrainingImages );
	Get( tSection, _T( "Training images item count" ),		m_nItemsTrainingImages );
	Get( tSection, _T( "Training labels magic number" ),	m_nMagicTrainingLabels );
	Get( tSection, _T( "Training labels item count" ),		m_nItemsTrainingLabels );

	Get( tSection, _T( "Testing images magic number" ),		m_nMagicTestingImages );
	Get( tSection, _T( "Testing images item count" ),		m_nItemsTestingImages );
	Get( tSection, _T( "Testing labels magic number" ),		m_nMagicTestingLabels );
	Get( tSection, _T( "Testing labels item count" ),		m_nItemsTestingLabels );

	// these two are basically ignored

	UINT uiCount = g_cImageSize;
	Get( tSection, _T( "Rows per image" ),			uiCount );
	m_nRowsImages = uiCount;

	uiCount = g_cImageSize;
	Get( tSection, _T( "Columns per image" ),		uiCount );
	m_nColsImages = uiCount;


	// parameters for controlling pattern distortion during backpropagation

	tSection = _T( "Parameters for Controlling Pattern Distortion During Backpropagation" );
	
	Get( tSection, _T( "Maximum scale factor change (percent, like 20.0 for 20%)" ),		m_dMaxScaling );
	Get( tSection, _T( "Maximum rotational change (degrees, like 20.0 for 20 degrees)" ),	m_dMaxRotation );
	Get( tSection, _T( "Sigma for elastic distortions (higher numbers are more smooth and less distorted; Simard uses 4.0)" ), m_dElasticSigma );
	Get( tSection, _T( "Scaling for elastic distortions (higher numbers amplify distortions; Simard uses 0.34)" ), m_dElasticScaling );


}


// overloaded "Get" functions.  Each of these functions should be called with a default
// value already stored in the data type; if the entry exists in the INI file then that value 
// is returned, else the default value is unchanged and it is also stored into the INI file
// for future use
// NOTE: Do not use with the MS data type BOOL.  If used with type BOOL, then the "int" version
//       will be called.  Use the c-standard data type "bool" instead

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, UINT &uiVal)
{
	union { UINT ui;	int ii; } iInput, iRet;

	iInput.ui = uiVal;
	
	iRet.ii = m_pMainApp->GetProfileInt( strSection, strEntry, iInput.ii );

	uiVal = iRet.ui;
}

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, int &iVal)
{
	int iRet = m_pMainApp->GetProfileInt( strSection, strEntry, iVal );

	iVal = iRet;
}

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, float &fVal)
{
	CString strTemp;
	double dVal = (double)fVal;
	strTemp.Format( _T("g"), dVal );

	CString sRet = m_pMainApp->GetProfileString( strSection, strEntry, strTemp );

	dVal = _tcstod( (LPCTSTR)sRet, NULL );

	fVal = (float)dVal;
}

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, double &dVal)
{
	CString strTemp;
	strTemp.Format( _T("g"), dVal );

	CString sRet = m_pMainApp->GetProfileString( strSection, strEntry, strTemp );

	dVal = _tcstod( (LPCTSTR)sRet, NULL );
}

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, LPTSTR pStrVal)
{
	CString sRet = m_pMainApp->GetProfileString( strSection, strEntry, pStrVal );

	_tcscpy( pStrVal, (LPCTSTR)sRet );
}

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, CString &strVal)
{
	CString sRet = m_pMainApp->GetProfileString( strSection, strEntry, strVal );

	strVal = sRet;
}

void CPreferences::Get(LPCTSTR strSection, LPCTSTR strEntry, bool &bVal)
{
	CString strTemp;

	strTemp = ( bVal==false ) ? ( _T( "No" ) ) : ( _T( "Yes" ) );

	CString sRet = m_pMainApp->GetProfileString( strSection, strEntry, strTemp );

	// check for "no", "false" or zero("0")

	sRet.MakeUpper();

	if ( sRet[0] == _T( 'N' ) ||
		 sRet[0] == _T( 'F' ) ||
		 sRet[0] == _T( '0' ) )
	{
		bVal = FALSE;
	}
	else
	{
		bVal = TRUE;
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Mike O'Neill is a patent attorney in Southern California, where he specializes in computer and software-related patents. He programs as a hobby, and in a vain attempt to keep up with and understand the technology of his clients.

Comments and Discussions