| 462 | } |
| 463 | |
| 464 | Status ClFullyConnected::validate(const ITensorInfo *src, |
| 465 | const ITensorInfo *weights, |
| 466 | const ITensorInfo *biases, |
| 467 | const ITensorInfo *dst, |
| 468 | FullyConnectedLayerInfo fc_info) |
| 469 | { |
| 470 | ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(src, weights, dst); |
| 471 | ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(src, 1, DataType::QASYMM8, DataType::QASYMM8_SIGNED, |
| 472 | DataType::F16, DataType::F32); |
| 473 | ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(src, weights, dst); |
| 474 | ARM_COMPUTE_RETURN_ERROR_ON(weights->num_dimensions() > 2); |
| 475 | ARM_COMPUTE_RETURN_ERROR_ON( |
| 476 | fc_info.activation_info.enabled() && is_data_type_quantized(src->data_type()) && |
| 477 | fc_info.activation_info.activation() != ActivationLayerInfo::ActivationFunction::RELU && |
| 478 | fc_info.activation_info.activation() != ActivationLayerInfo::ActivationFunction::BOUNDED_RELU && |
| 479 | fc_info.activation_info.activation() != ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU); |
| 480 | const GPUTarget gpu_target = get_arch_from_target(CLScheduler::get().target()); |
| 481 | |
| 482 | const bool transpose_weights = fc_info.transpose_weights ? !fc_info.are_weights_reshaped : false; |
| 483 | bool is_fc_after_conv = true; |
| 484 | |
| 485 | // When using dynamic weights - use matmul kernels. |
| 486 | // Note: MatMul does not support broadcasting so fallback with batched cases. |
| 487 | const bool is_batched_fc_layer = dst->dimension(1) > 1; |
| 488 | const bool use_matmul = gpu_target != GPUTarget::MIDGARD && !weights->are_values_constant() && |
| 489 | !is_batched_fc_layer && |
| 490 | !(src->num_dimensions() > 1 && (src->data_layout() != fc_info.weights_trained_layout)); |
| 491 | |
| 492 | const ITensorInfo &flatten_src = TensorInfo(src->clone() |
| 493 | ->set_is_resizable(true) |
| 494 | .reset_padding() |
| 495 | .set_tensor_shape(compute_flatten_shape(src)) |
| 496 | .set_data_layout(DataLayout::NCHW)); |
| 497 | const ITensorInfo &reshaped_weights = TensorInfo( |
| 498 | weights->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(compute_transposed_shape(*weights))); |
| 499 | const ITensorInfo &converted_weights = (transpose_weights && !use_matmul) |
| 500 | ? TensorInfo(*reshaped_weights.clone()) |
| 501 | : TensorInfo(weights->clone()->set_is_resizable(true).reset_padding()); |
| 502 | |
| 503 | // With the Fully Connected layer we can have 4 different cases: |
| 504 | // 1) Convolution layer -> Fully Connected layer without batches |
| 505 | // 2) Fully Connected layer -> Fully Connected layer without batches |
| 506 | // 3) Convolution layer -> Fully Connected layer with batches |
| 507 | // 4) Fully Connected layer -> Fully Connected layer with batches |
| 508 | |
| 509 | const ITensorInfo *src_to_use = src; |
| 510 | const ITensorInfo *weights_to_use = weights; |
| 511 | |
| 512 | if (biases != nullptr) |
| 513 | { |
| 514 | ARM_COMPUTE_RETURN_ERROR_ON(biases->num_dimensions() > 1); |
| 515 | if (is_data_type_quantized(src->data_type())) |
| 516 | { |
| 517 | ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(biases, 1, DataType::S32); |
| 518 | } |
| 519 | else |
| 520 | { |
| 521 | ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(src, biases); |
nothing calls this directly
no test coverage detected