| 2385 | } |
| 2386 | |
| 2387 | void UndoWeightsShuffling(Model* model) { |
| 2388 | for (const auto& op : model->operators) { |
| 2389 | if (op->type != toco::OperatorType::kFullyConnected) { |
| 2390 | continue; |
| 2391 | } |
| 2392 | const auto& fc_op = static_cast<toco::FullyConnectedOperator&>(*op); |
| 2393 | if (fc_op.weights_format == FullyConnectedWeightsFormat::kDefault) { |
| 2394 | continue; |
| 2395 | } |
| 2396 | const string& weights_name = fc_op.inputs[1]; |
| 2397 | QCHECK_EQ(CountOpsWithInput(*model, weights_name), 1); |
| 2398 | auto& weights_array = model->GetArray(weights_name); |
| 2399 | QCHECK(weights_array.data_type == ArrayDataType::kUint8); |
| 2400 | auto& weights_data = |
| 2401 | weights_array.GetMutableBuffer<toco::ArrayDataType::kUint8>().data; |
| 2402 | const auto& weights_shape = weights_array.shape(); |
| 2403 | QCHECK_EQ(weights_shape.dimensions_count(), 2); |
| 2404 | const int rows = weights_shape.dims(0); |
| 2405 | const int cols = weights_shape.dims(1); |
| 2406 | QCHECK_EQ(rows % 4, 0); |
| 2407 | QCHECK_EQ(cols % 16, 0); |
| 2408 | CHECK_EQ(rows * cols, weights_data.size()); |
| 2409 | // Compute the de-shuffled weights |
| 2410 | std::vector<uint8> deshuffled_data(weights_data.size()); |
| 2411 | uint8* shuffled_data_ptr = weights_data.data(); |
| 2412 | for (int r = 0; r < rows; r += 4) { |
| 2413 | for (int c = 0; c < cols; c += 16) { |
| 2414 | for (int i = 0; i < 4; i++) { |
| 2415 | uint8* deshuffled_data_ptr = |
| 2416 | deshuffled_data.data() + (r + i) * cols + c; |
| 2417 | for (int j = 0; j < 16; j++) { |
| 2418 | uint8 shuffled_val = *shuffled_data_ptr++; |
| 2419 | // Deshuffling isn't only about deshuffling the storage layout, |
| 2420 | // it's also about undoing the flipping of the sign bit, which is |
| 2421 | // performed on the shuffled weights. |
| 2422 | uint8 deshuffled_val = shuffled_val ^ 0x80; |
| 2423 | *deshuffled_data_ptr++ = deshuffled_val; |
| 2424 | } |
| 2425 | } |
| 2426 | } |
| 2427 | } |
| 2428 | CHECK_EQ(shuffled_data_ptr, weights_data.data() + rows * cols); |
| 2429 | // Switch this FC op to using the deshuffled weights. |
| 2430 | weights_data = std::move(deshuffled_data); |
| 2431 | } |
| 2432 | } |
| 2433 | |
| 2434 | void CopyMinMaxAndQuantizationRelatedFields(const Array& src, Array* dst) { |
| 2435 | if (src.minmax) { |
no test coverage detected