Improve results and memory error checking in C wrapper

- Add ctc_check_result function to check the status of CTC library operations - Rename loop variables to better fit the context - Change PyExc_ValueError exceptions to PyExc_MemoryError when memory allocation errors occur - Add OpenMP verification and usage when setting up ctcOptions - Add proper freeing of allocated memory when errors occur Signed-off-by: 's avatarJoão Victor Tozatti Risso <joaovictor.risso@gmail.com>
上级 e75e8a8e
#section support_code #section support_code
int ctc_check_result(ctcStatus_t retcode, const char * msg, int * status)
{
if( CTC_STATUS_SUCCESS != retcode )
{
// Get error message from underlying library
const char * ctc_msg = ctcGetStatusString( retcode );
PyErr_Format( PyExc_RuntimeError,
"%s | CTC library error message: %s",
msg,
ctc_msg );
*status = 1;
}
*status = 0;
}
void create_contiguous_input_lengths( PyArrayObject * input_lengths_arr, void create_contiguous_input_lengths( PyArrayObject * input_lengths_arr,
int ** input_lengths ) int ** input_lengths )
{ {
...@@ -10,9 +26,9 @@ void create_contiguous_input_lengths( PyArrayObject * input_lengths_arr, ...@@ -10,9 +26,9 @@ void create_contiguous_input_lengths( PyArrayObject * input_lengths_arr,
if ( NULL == (*input_lengths) ) if ( NULL == (*input_lengths) )
return; return;
for( int i = 0; i < num_elements; ++i ) for( int elem_idx = 0; elem_idx < num_elements; ++elem_idx )
{ {
(*input_lengths)[i] = *( (int *) PyArray_GETPTR1( input_lengths_arr, i ) ); (*input_lengths)[elem_idx] = *( (int *) PyArray_GETPTR1( input_lengths_arr, elem_idx ) );
} }
} }
...@@ -35,19 +51,19 @@ void create_flat_labels( PyArrayObject * label_matrix, int ** flat_labels, ...@@ -35,19 +51,19 @@ void create_flat_labels( PyArrayObject * label_matrix, int ** flat_labels,
} }
int label_index = 0; int label_index = 0;
for( int i = 0; i < rows; ++i ) for( int row_idx = 0; row_idx < rows; ++row_idx )
{ {
int label_length = 0; int label_length = 0;
for( int j = 0; j < cols; ++j ) for( int col_idx = 0; col_idx < cols; ++col_idx )
{ {
int label = *( (int *) PyArray_GETPTR2( label_matrix, i, j ) ); int label = *( (int *) PyArray_GETPTR2( label_matrix, row_idx, col_idx ) );
if ( label >= 0 ) // negative values are assumed to be padding if ( label >= 0 ) // negative values are assumed to be padding
{ {
(*flat_labels)[ label_index++ ] = label; (*flat_labels)[ label_index++ ] = label;
++label_length; ++label_length;
} }
} }
(*label_lengths)[ i ] = label_length; (*label_lengths)[ row_idx ] = label_length;
} }
} }
...@@ -59,12 +75,6 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations, ...@@ -59,12 +75,6 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations,
PyArrayObject ** out_costs, PyArrayObject ** out_costs,
PyArrayObject ** out_gradients) PyArrayObject ** out_gradients)
{ {
// setup CTC computation parameters
ctcOptions ctc_options;
memset( &ctc_options, 0, sizeof(ctcOptions) );
ctc_options.loc = CTC_CPU;
ctc_options.num_threads = 1;
npy_float32 * activations = NULL; npy_float32 * activations = NULL;
PyArrayObject * activations_copy = NULL; PyArrayObject * activations_copy = NULL;
...@@ -81,7 +91,7 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations, ...@@ -81,7 +91,7 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations,
} }
else else
{ {
PyErr_Format( PyExc_ValueError, PyErr_Format( PyExc_MemoryError,
"Could not create a contiguous copy of activations array." ); "Could not create a contiguous copy of activations array." );
return 1; return 1;
} }
...@@ -95,7 +105,7 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations, ...@@ -95,7 +105,7 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations,
if ( NULL == input_lengths ) if ( NULL == input_lengths )
{ {
PyErr_Format( PyExc_ValueError, PyErr_Format( PyExc_MemoryError,
"Could not allocate storage for input lengths" ); "Could not allocate storage for input lengths" );
return 1; return 1;
} }
...@@ -105,7 +115,10 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations, ...@@ -105,7 +115,10 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations,
if ( ( NULL == label_lengths ) || ( NULL == flat_labels ) ) if ( ( NULL == label_lengths ) || ( NULL == flat_labels ) )
{ {
PyErr_Format( PyExc_ValueError, // Free previously allocated memory for input lengths
free( input_lengths );
PyErr_Format( PyExc_MemoryError,
"Could not allocate storage for labels and their lengths" ); "Could not allocate storage for labels and their lengths" );
return 1; return 1;
} }
...@@ -113,43 +126,38 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations, ...@@ -113,43 +126,38 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations,
npy_int minibatch_size = PyArray_DIMS( in_activations )[1]; npy_int minibatch_size = PyArray_DIMS( in_activations )[1];
npy_int alphabet_size = PyArray_DIMS( in_activations )[2]; npy_int alphabet_size = PyArray_DIMS( in_activations )[2];
void * ctc_cpu_workspace = NULL;
npy_float32 * costs = NULL; npy_float32 * costs = NULL;
npy_intp cost_size = minibatch_size; npy_intp cost_size = minibatch_size;
if ( NULL == (*out_costs) ) if ( (*out_costs) == NULL || // Symbolic variable has no memory backing
{ PyArray_NDIM( *out_costs ) != 1 || // or, matrix has the wrong size
// Symbolic variable has no memory backing, so we create one PyArray_DIMS( *out_costs )[0] != cost_size )
*out_costs = (PyArrayObject *) PyArray_ZEROS( 1, &cost_size, NPY_FLOAT32, 0 );
}
else if ( PyArray_NDIM( *out_costs ) != 1 ||
PyArray_DIMS( *out_costs )[0] != cost_size ) // matrix has the wrong size
{ {
Py_XDECREF( *out_costs ); Py_XDECREF( *out_costs );
// Allocate new matrix // Allocate new matrix
*out_costs = (PyArrayObject *) PyArray_ZEROS( 1, &cost_size, NPY_FLOAT32, 0 ); *out_costs = (PyArrayObject *) PyArray_ZEROS( 1, &cost_size, NPY_FLOAT32, 0 );
}
if ( NULL == (*out_costs) ) if ( NULL == (*out_costs) )
{ {
PyErr_Format( PyExc_ValueError, // Free previously allocated memory for input and label lengths, and
// labels
free( input_lengths );
free( label_lengths );
free( flat_labels );
PyErr_Format( PyExc_MemoryError,
"Could not allocate storage for CTC costs" ); "Could not allocate storage for CTC costs" );
return 1; return 1;
} }
}
costs = (npy_float32 *) PyArray_DATA( *out_costs ); costs = (npy_float32 *) PyArray_DATA( *out_costs );
if ( NULL == (*out_gradients) ) if ( NULL == (*out_gradients) || // Symbolic variable has no real backing
{ PyArray_NDIM( *out_gradients ) != 3 ||
// Symbolic variable has no real backing, so create one. PyArray_DIMS( *out_gradients )[0] != PyArray_DIMS( in_activations )[0] ||
*out_gradients = (PyArrayObject*) PyArray_ZEROS( 3, PyArray_DIMS( in_activations ), PyArray_DIMS( *out_gradients )[1] != PyArray_DIMS( in_activations )[1] ||
NPY_FLOAT32, 0 ); PyArray_DIMS( *out_gradients )[2] != PyArray_DIMS( in_activations )[2] )
}
else if ( PyArray_NDIM( *out_gradients ) != 3
|| PyArray_DIMS( *out_gradients )[0] != PyArray_DIMS( in_activations )[0]
|| PyArray_DIMS( *out_gradients )[1] != PyArray_DIMS( in_activations )[1]
|| PyArray_DIMS( *out_gradients )[2] != PyArray_DIMS( in_activations )[2] )
{ {
// Existing matrix is the wrong size. Make a new one. // Existing matrix is the wrong size. Make a new one.
// Decrement ref counter to existing array // Decrement ref counter to existing array
...@@ -157,47 +165,72 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations, ...@@ -157,47 +165,72 @@ int APPLY_SPECIFIC(ctc_cost_cpu)(PyArrayObject * in_activations,
// Allocate new array // Allocate new array
*out_gradients = (PyArrayObject *) PyArray_ZEROS(3, PyArray_DIMS( in_activations ), *out_gradients = (PyArrayObject *) PyArray_ZEROS(3, PyArray_DIMS( in_activations ),
NPY_FLOAT32, 0); NPY_FLOAT32, 0);
}
if ( NULL == (*out_gradients) ) if ( NULL == (*out_gradients) )
{ {
PyErr_Format( PyExc_ValueError, // Free previously allocated memory for input and label lengths,
// labels and output costs
free( input_lengths );
free( label_lengths );
free( flat_labels );
Py_XDECREF( *out_costs );
PyErr_Format( PyExc_MemoryError,
"Could not allocate storage for CTC gradients!" ); "Could not allocate storage for CTC gradients!" );
return 1; return 1;
} }
}
npy_float32 * gradients = (npy_float32 *) PyArray_DATA( *out_gradients ); npy_float32 * gradients = (npy_float32 *) PyArray_DATA( *out_gradients );
ctcStatus_t status; // setup CTC computation parameters
ctcOptions ctc_options;
memset( &ctc_options, 0, sizeof(ctcOptions) );
ctc_options.loc = CTC_CPU;
#if defined(_OPENMP)
ctc_options.num_threads = omp_get_num_threads();
#else
ctc_options.num_threads = 1;
#endif
size_t cpu_workspace_size; size_t cpu_workspace_size;
int ctc_error;
status = get_workspace_size( label_lengths, input_lengths, alphabet_size, ctc_check_result( get_workspace_size( label_lengths, input_lengths,
minibatch_size, ctc_options, &cpu_workspace_size ); alphabet_size, minibatch_size, ctc_options, &cpu_workspace_size ),
"Failed to obtain CTC workspace size!",
&ctc_error );
if ( CTC_STATUS_SUCCESS != status ) if ( ctc_error ) // Exception is set by ctc_check_result, return error here
{
PyErr_Format( PyExc_ValueError,
"Could not compute the CTC workspace size!" );
return 1; return 1;
}
ctc_cpu_workspace = malloc( cpu_workspace_size ); void * ctc_cpu_workspace = malloc( cpu_workspace_size );
if ( NULL == ctc_cpu_workspace )
status = compute_ctc_loss( activations, gradients, flat_labels,
label_lengths, input_lengths, alphabet_size, minibatch_size,
costs, ctc_cpu_workspace, ctc_options );
if ( CTC_STATUS_SUCCESS != status )
{ {
PyErr_Format( PyExc_ValueError, "Failed to compute CTC loss!" ); // Free previously allocated memory for input and label lengths,
// labels, output costs and gradients
free( input_lengths );
free( label_lengths );
free( flat_labels );
Py_XDECREF( *out_costs );
Py_XDECREF( *out_gradients );
PyErr_Format( PyExc_MemoryError,
"Failed to allocate memory for CTC workspace!" );
return 1; return 1;
} }
if ( NULL != activations_copy ) ctc_check_result( compute_ctc_loss( activations, gradients, flat_labels,
{ label_lengths, input_lengths, alphabet_size, minibatch_size, costs,
ctc_cpu_workspace, ctc_options ),
"Failed to compute CTC loss function!",
&ctc_error );
if ( ctc_error ) // Exception is set by ctc_check_result, return error here
return 1;
Py_XDECREF( activations_copy ); Py_XDECREF( activations_copy );
}
free( input_lengths ); free( input_lengths );
free( flat_labels ); free( flat_labels );
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论