| 86 | } |
| 87 | |
| 88 | static void quantize_8bits(program& prog, |
| 89 | const target& t, |
| 90 | shape::type_t precision, |
| 91 | const std::vector<parameter_map>& calibration, |
| 92 | const std::unordered_set<std::string>& ins_names) |
| 93 | { |
| 94 | // Run optimize_module() before converting to int8/fp8 to const eval and fold in FP32 to |
| 95 | // avoid loss of precision. |
| 96 | run_passes(prog, {rewrite_rnn{}, normalize_ops{}, optimize_module{}}, quant_tracer()); |
| 97 | |
| 98 | std::shared_ptr<std::vector<std::pair<float, float>>> quant_8bit_params = |
| 99 | std::make_shared<std::vector<std::pair<float, float>>>(); |
| 100 | std::shared_ptr<std::vector<float>> max_abs_vals = std::make_shared<std::vector<float>>(); |
| 101 | std::map<shape::type_t, float> type_ranges = {{shape::type_t::int8_type, 127.0}, |
| 102 | {shape::type_t::fp8e4m3fnuz_type, 240.0}, |
| 103 | {shape::type_t::fp8e4m3fn_type, 448.0}}; |
| 104 | float quantized_range = type_ranges.at(precision); |
| 105 | auto calc_quant_params = [&](std::size_t ins_index, std::vector<argument> args) { |
| 106 | std::pair<float, float> param_pair{64.0f, 0.0f}; |
| 107 | // scale and shift is need for only int8 type, and we do not |
| 108 | // consider shift, so set shift to 0 |
| 109 | std::vector<float> vec_val; |
| 110 | argument arg = t.copy_from(args.front()); |
| 111 | arg.visit([&](auto output) { vec_val.assign(output.begin(), output.end()); }); |
| 112 | auto max_val = *std::max_element(vec_val.begin(), vec_val.end()); |
| 113 | auto min_val = *std::min_element(vec_val.begin(), vec_val.end()); |
| 114 | auto max_abs = std::max(std::fabs(max_val), std::fabs(min_val)); |
| 115 | max_abs_vals->at(ins_index) = std::max(max_abs_vals->at(ins_index), max_abs); |
| 116 | // if all values are 0, no need to do scaling |
| 117 | if(float_equal(max_abs_vals->at(ins_index), 0.0f)) |
| 118 | { |
| 119 | param_pair.first = 1.0f; |
| 120 | } |
| 121 | else |
| 122 | { |
| 123 | param_pair.first = quantized_range / max_abs_vals->at(ins_index); |
| 124 | } |
| 125 | quant_8bit_params->at(ins_index) = param_pair; |
| 126 | }; |
| 127 | |
| 128 | // pass to add capture argument op |
| 129 | std::size_t param_num = 0; |
| 130 | run_passes( |
| 131 | prog, {capture_arguments_pass{ins_names, calc_quant_params, ¶m_num}}, quant_tracer()); |
| 132 | quant_8bit_params->resize(param_num, std::pair<float, float>(64.0f, 0.0f)); |
| 133 | max_abs_vals->resize(param_num, 0.0f); |
| 134 | |
| 135 | // use the calibration data to compute the quantization scale |
| 136 | auto capture_prog = prog; |
| 137 | capture_prog.compile(t); |
| 138 | |
| 139 | // use all calibration data to run the program to calculate the |
| 140 | // quantization scale and shift |
| 141 | for(auto&& arg : calibration) |
| 142 | { |
| 143 | parameter_map m; |
| 144 | for(auto&& x : capture_prog.get_parameter_shapes()) |
| 145 | { |
no test coverage detected