📄 mnistdoc.cpp
字号:
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 + -