⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mnistdoc.cpp

📁 基于神经网络的手写体识别程序
💻 CPP
📖 第 1 页 / 共 5 页
字号:
		return;
	}
	
	m_bBackpropThreadAbortFlag = TRUE;
	
	HANDLE hThread[25];
	CString msg;
	DWORD dwTimeOut = 5000;  // 5 second default timeout
	UINT ii;
	
	while ( m_iNumBackpropThreadsRunning > 0 )
	{
		for ( ii=0; ii<m_iNumBackpropThreadsRunning; ++ii )
		{
			hThread[ ii ] = m_pBackpropThreads[ ii ]->m_hThread;
		}
		
		DWORD dwRet = ::WaitForMultipleObjects( m_iNumBackpropThreadsRunning, hThread, FALSE, dwTimeOut );
		DWORD dwErr = ::GetLastError();		// if an error occurred get its value as soon as possible
		
		if ( dwRet==WAIT_FAILED )
		{
			LPVOID lpMsgBuf;
			::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
			
			::MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("Error Waiting For Backpropagation Thread Shutdown"), MB_OK | MB_ICONINFORMATION );
			LocalFree( lpMsgBuf );
		}
		else if ( dwRet==WAIT_TIMEOUT )
		{
			// bad -- no threads are responding
			// give user option of waiting a bit more, or of terminating the threads forcefully
			
			msg.Format( _T("No thread has responded after waiting %d milliseconds\n\n")
				_T("Click \"Retry\" to wait another %d milliseconds and give a thread\n")
				_T("a chance to respond\n\n")
				_T("Click \"Cancel\" to terminate a thread forcibly\n\n")
				_T("Note: Forceful termination is not recommended and might cause memory leaks"), dwTimeOut, dwTimeOut );
			if ( IDCANCEL == ::MessageBox( NULL, msg, _T("No Thread Is Responding"), MB_RETRYCANCEL|MB_ICONEXCLAMATION|MB_DEFBUTTON1 ) )
			{
				// forceful thread termination was selected
				// pick first thread from list and terminate it
				
				::TerminateThread( hThread[0], 98 );	// specify exit code of "98"
			}
			
		}
		else if ( dwRet>=WAIT_ABANDONED_0 && dwRet<=(WAIT_ABANDONED_0+m_iNumBackpropThreadsRunning-1) )
		{
			msg.Format( _T("Thread reports mutex was abandoned") );
			::MessageBox( NULL, msg, _T("Mutex Abandoned"), MB_OK|MB_ICONEXCLAMATION );
		}
		else if ( dwRet>=WAIT_OBJECT_0 && dwRet<=(WAIT_OBJECT_0+m_iNumBackpropThreadsRunning-1) )
		{
			// the most common and expected return value
			// delete the object that signalled
			
			int nDex = dwRet - WAIT_OBJECT_0;
			
			delete m_pBackpropThreads[ nDex ];
			
			for ( ii=nDex; ii<m_iNumBackpropThreadsRunning-1; ++ii )
			{
				m_pBackpropThreads[ ii ] = m_pBackpropThreads[ ii+1 ];
			}
			
			m_iNumBackpropThreadsRunning--;
			
		}
		else
		{
			ASSERT( FALSE );	// shouldn't be able to get here
		}
	}
	
	
	// at this point, all threads have been terminated, so re-set flags to allow
	// for future re-start of the threads
	
	m_bBackpropThreadAbortFlag = TRUE;
	m_bBackpropThreadsAreRunning = FALSE;
	m_iNumBackpropThreadsRunning = 0;
	m_iBackpropThreadIdentifier = 0;
	m_cBackprops = 0;
	
	
	
}




UINT CMNistDoc::BackpropagationThread(LPVOID pVoid)
{
	// thread for backpropagation training of NN
	//
	// thread is "owned" by the doc, and accepts a pointer to the doc
	// continuously backpropagates until m_bThreadAbortFlag is set to TRUE
	
	CMNistDoc* pThis = reinterpret_cast< CMNistDoc* >( pVoid );
	
	ASSERT( pThis != NULL );
	
	// set thread name (helps during debugging)
	
	char str[25] = {0};  // must use chars, not TCHARs, for SetThreadname function
	sprintf( str, "BACKP%02d", pThis->m_iBackpropThreadIdentifier++ );
	SetThreadName( -1, str );
	
	// do the work
	
	double inputVector[841] = {0.0};  // note: 29x29, not 28x28
	double targetOutputVector[10] = {0.0};
	double actualOutputVector[10] = {0.0};
	double dMSE;
	UINT scaledMSE;
	unsigned char grayLevels[g_cImageSize * g_cImageSize] = { 0 };
	int label = 0;
	int ii, jj;
	UINT iSequentialNum;
	
	std::vector< std::vector< double > > memorizedNeuronOutputs;
	
	while ( pThis->m_bBackpropThreadAbortFlag == FALSE )
	{
		int iRet = pThis->GetNextTrainingPattern( grayLevels, &label, TRUE, TRUE, &iSequentialNum );
		
		if ( label < 0 ) label = 0;
		if ( label > 9 ) label = 9;
		
		// post message to the dialog, telling it which pattern this thread is currently working on
		
		if ( pThis->m_hWndForBackpropPosting != NULL )
		{
			::PostMessage( pThis->m_hWndForBackpropPosting, UWM_BACKPROPAGATION_NOTIFICATION, 1L, (LPARAM)iSequentialNum );
		}
		
		
		// pad to 29x29, convert to double precision
		
		for ( ii=0; ii<841; ++ii )
		{
			inputVector[ ii ] = 1.0;  // one is white, -one is black
		}
		
		// top row of inputVector is left as zero, left-most column is left as zero 
		
		for ( ii=0; ii<g_cImageSize; ++ii )
		{
			for ( jj=0; jj<g_cImageSize; ++jj )
			{
				inputVector[ 1 + jj + 29*(ii+1) ] = (double)((int)(unsigned char)grayLevels[ jj + g_cImageSize*ii ])/128.0 - 1.0;  // one is white, -one is black
			}
		}
		
		// desired output vector
		
		for ( ii=0; ii<10; ++ii )
		{
			targetOutputVector[ ii ] = -1.0;
		}
		targetOutputVector[ label ] = 1.0;
		
		// now backpropagate

		pThis->BackpropagateNeuralNet( inputVector, 841, targetOutputVector, actualOutputVector, 10, 
			&memorizedNeuronOutputs, pThis->m_bDistortTrainingPatterns );

		
		// calculate error for this pattern and post it to the hwnd so it can calculate a running 
		// estimate of MSE
		
		dMSE = 0.0;
		for ( ii=0; ii<10; ++ii )
		{
			dMSE += ( actualOutputVector[ii]-targetOutputVector[ii] ) * ( actualOutputVector[ii]-targetOutputVector[ii] );
		}
		dMSE /= 2.0;

		scaledMSE = (UINT)( sqrt( dMSE ) * 2.0e8 );  // arbitrary large pre-agreed upon scale factor; taking sqrt is simply to improve the scaling
		
		if ( pThis->m_hWndForBackpropPosting != NULL )
		{
			::PostMessage( pThis->m_hWndForBackpropPosting, UWM_BACKPROPAGATION_NOTIFICATION, 2L, (LPARAM)scaledMSE );
		}
		
		
		// determine the neural network's answer, and compare it to the actual answer.
		// Post a message if the answer was incorrect, so the dialog can display mis-recognition
		// statistics
		
		int iBestIndex = 0;
		double maxValue = -99.0;
		
		for ( ii=0; ii<10; ++ii )
		{
			if ( actualOutputVector[ ii ] > maxValue )
			{
				iBestIndex = ii;
				maxValue = actualOutputVector[ ii ];
			}
		}

		if ( iBestIndex != label )
		{
			// pattern was mis-recognized.  Notify the testing dialog
			if ( pThis->m_hWndForBackpropPosting != NULL )
			{
				::PostMessage( pThis->m_hWndForBackpropPosting, UWM_BACKPROPAGATION_NOTIFICATION, 8L, (LPARAM)0L );
			}
		}
		
	}  // end of main "while not abort flag" loop
	
	return 0L;
}







BOOL CMNistDoc::StartTesting(UINT iStartingPattern, UINT iNumThreads, HWND hWnd, BOOL bDistortPatterns,
							 UINT iWhichImageSet /* =1 */ )
{
	// creates and starts testing threads
	
	if ( m_bTestingThreadsAreRunning != FALSE )
		return FALSE;
	
	m_bTestingThreadAbortFlag = FALSE;
	m_bTestingThreadsAreRunning = TRUE;
	m_iNumTestingThreadsRunning = 0;
	m_iTestingThreadIdentifier = 0;
	
	m_iNextTestingPattern = iStartingPattern;
	m_hWndForTestingPosting = hWnd;
	m_iWhichImageSet = iWhichImageSet;
	
	if ( m_iWhichImageSet > 1 )
		m_iWhichImageSet = 1;
	if ( m_iWhichImageSet < 0 )  // which is not possible, since m_iWhichImageSet is a UINT
		m_iWhichImageSet = 0;
	
	if ( m_iNextTestingPattern < 0 ) 
		m_iNextTestingPattern = 0;
	if ( m_iNextTestingPattern >= ::GetPreferences().m_nItemsTestingImages )
		m_iNextTestingPattern = ::GetPreferences().m_nItemsTestingImages - 1;
	
	if ( iNumThreads < 1 ) 
		iNumThreads = 1;
	if ( iNumThreads > 10 )  // 10 is arbitrary upper limit
		iNumThreads = 10;
	
	m_bDistortTestingPatterns = bDistortPatterns;
	
	for ( UINT ii=0; ii<iNumThreads; ++ii )
	{
		CWinThread* pThread = ::AfxBeginThread( TestingThread, (LPVOID)this, 
			THREAD_PRIORITY_BELOW_NORMAL, 0, CREATE_SUSPENDED, NULL );
		
		if ( pThread == NULL )
		{
			// creation failed; un-do everything
			
			StopTesting();
			return FALSE;
		}
		
		pThread->m_bAutoDelete = FALSE;		 
		m_pTestingThreads[ ii ] = pThread;
		pThread->ResumeThread();
		m_iNumTestingThreadsRunning++;
	}
	
	return TRUE;
}

void CMNistDoc::StopTesting()
{
	// stops all the testing threads
	
	if ( m_bTestingThreadsAreRunning == FALSE )
	{
		// it's curious to select "stop" if no threads are running, but perform some
		// shutdown safeguards, just to be certain
		
		m_bTestingThreadAbortFlag = TRUE;
		m_bTestingThreadsAreRunning = FALSE;
		m_iNumTestingThreadsRunning = 0;
		m_iTestingThreadIdentifier = 0;
		return;
	}
	
	m_bTestingThreadAbortFlag = TRUE;
	
	HANDLE hThread[25];
	CString msg;
	DWORD dwTimeOut = 5000;  // 5 second default timeout
	UINT ii;
	
	while ( m_iNumTestingThreadsRunning > 0 )
	{
		for ( ii=0; ii<m_iNumTestingThreadsRunning; ++ii )
		{
			hThread[ ii ] = m_pTestingThreads[ ii ]->m_hThread;
		}
		
		DWORD dwRet = ::WaitForMultipleObjects( m_iNumTestingThreadsRunning, hThread, FALSE, dwTimeOut );
		DWORD dwErr = ::GetLastError();		// if an error occurred get its value as soon as possible
		
		if ( dwRet==WAIT_FAILED )
		{
			LPVOID lpMsgBuf;
			::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
			
			::MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("Error Waiting For Testing Thread Shutdown"), MB_OK | MB_ICONINFORMATION );
			LocalFree( lpMsgBuf );
		}
		else if ( dwRet==WAIT_TIMEOUT )
		{
			// bad -- no threads are responding
			// give user option of waiting a bit more, or of terminating the threads forcefully
			
			msg.Format( _T("No thread has responded after waiting %d milliseconds\n\n")
				_T("Click \"Retry\" to wait another %d milliseconds and give a thread\n")
				_T("a chance to respond\n\n")
				_T("Click \"Cancel\" to terminate a thread forcibly\n\n")
				_T("Note: Forceful termination is not recommended and might cause memory leaks"), dwTimeOut, dwTimeOut );
			if ( IDCANCEL == ::MessageBox( NULL, msg, _T("No Thread Is Responding"), MB_RETRYCANCEL|MB_ICONEXCLAMATION|MB_DEFBUTTON1 ) )
			{
				// forceful thread termination was selected
				// pick first thread from list and terminate it
				
				::TerminateThread( hThread[0], 98 );	// specify exit code of "98"
			}
			
		}
		else if ( dwRet>=WAIT_ABANDONED_0 && dwRet<=(WAIT_ABANDONED_0+m_iNumTestingThreadsRunning-1) )
		{
			msg.Format( _T("Thread reports mutex was abandoned") );
			::MessageBox( NULL, msg, _T("Mutex Abandoned"), MB_OK|MB_ICONEXCLAMATION );
		}
		else if ( dwRet>=WAIT_OBJECT_0 && dwRet<=(WAIT_OBJECT_0+m_iNumTestingThreadsRunning-1) )
		{
			// the most common and expected return value
			// delete the object that signalled
			
			int nDex = dwRet - WAIT_OBJECT_0;
			
			delete m_pTestingThreads[ nDex ];
			
			for ( ii=nDex; ii<m_iNumTestingThreadsRunning-1; ++ii )
			{
				m_pTestingThreads[ ii ] = m_pTestingThreads[ ii+1 ];
			}
			
			m_iNumTestingThreadsRunning--;
			
		}
		else
		{
			ASSERT( FALSE );	// shouldn't be able to get here
		}
	}
	
	
	// at this point, all threads have been terminated, so re-set flags to allow
	// for future re-start of the threads
	
	m_bTestingThreadAbortFlag = TRUE;
	m_bTestingThreadsAreRunning = FALSE;
	m_iNumTestingThreadsRunning = 0;
	m_iTestingThreadIdentifier = 0;
	
	
}

UINT CMNistDoc::TestingThread(LPVOID pVoid)
{
	// thread for testing of Neural net
	// Continuously get the doc's next pattern, puts it through the neural net, and
	// inspects the output.  As the thread goes through the patterns, it post messages to the
	// m_hWndForTestingPosting, which presumably is the dialog that shows testing results,
	// advising it of the current pattern being tested.  If the actual output from the 
	// neural net differs from the desired output, another message is posted, advising the 
	// m_hWndForTestingPosting of the identity of the mis-recognized pattern
	
	// thread is owned by the doc and accepts a pointer to the doc as a parameter
	
	
	CMNistDoc* pThis = reinterpret_cast< CMNistDoc* >( pVoid );
	
	ASSERT( pThis != NULL );
	
	// set thread name (helps during debugging)
	
	char str[25] = {0};  // must use chars, not TCHARs, for SetThreadname function
	sprintf( str, "TEST%02d", pThis->m_iTestingThreadIdentifier++ );
	SetThreadName( -1, str );
	
	// do the work
	
	double inputVector[841] = {0.0};  // note: 29x29, not 28x28
	double targetOutputVector[10] = {0.0};
	double actualOutputVector[10] = {0.0};

	double dPatternMSE = 0.0;
	double dTotalMSE = 0.0;
	UINT scaledMSE = 0;
	UINT iPatternsProcessed = 0;
	
	unsigned char grayLevels[g_cImageSize * g_cImageSize] = { 0 };
	int label = 0;
	int ii, jj;
	UINT iPatNum, iSequentialNum;
	
	while ( pThis->m_bTestingThreadAbortFlag == FALSE )
	{
		// testing image set or training image set
		
		if ( pThis->m_iWhichImageSet == 1 )
		{
			// testing set
			
			iPatNum = pThis->GetNextTestingPattern( grayLevels, &label, TRUE );
			
			// post message to the dialog, telling it which pattern this thread is currently working on
			
			if ( pThis->m_hWndForTestingPosting != NULL )
			{
				::PostMessage( pThis->m_hWndForTestingPosting, UWM_TESTING_NOTIFICATION, 1L, (LPARAM)iPatNum );
			}
		}
		else
		{
			// training set
			
			iPatNum = pThis->GetNextTrainingPattern( grayLevels, &label, TRUE, FALSE, &iSequentialNum );
			
			// post message to the dialog, telling it which pattern this thread is currently wo

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -