| 97 | { |
| 98 | |
| 99 | void MAEPrinter::init(const luci::Module *first, const luci::Module *second) |
| 100 | { |
| 101 | THROW_UNLESS(first != nullptr, "Invalid module."); |
| 102 | THROW_UNLESS(second != nullptr, "Invalid module."); |
| 103 | |
| 104 | const auto first_output = loco::output_nodes(first->graph()); |
| 105 | const auto second_output = loco::output_nodes(second->graph()); |
| 106 | |
| 107 | assert(first_output.size() == second_output.size()); // FIX_CALLER_UNLESS |
| 108 | |
| 109 | for (uint32_t i = 0; i < first_output.size(); i++) |
| 110 | { |
| 111 | const auto first_node = loco::must_cast<luci::CircleNode *>(first_output[i]); |
| 112 | const auto second_node = loco::must_cast<luci::CircleNode *>(second_output[i]); |
| 113 | |
| 114 | // Create tensors to store intermediate results |
| 115 | _intermediate.emplace_back(); |
| 116 | _intermediate.at(i).dtype(loco::DataType::FLOAT32); |
| 117 | // NOTE Use both first_node and second_node to avoid release build break |
| 118 | _intermediate.at(i).rank(first_node->rank()); |
| 119 | uint32_t num_elems = 1; |
| 120 | for (uint32_t j = 0; j < second_node->rank(); j++) |
| 121 | { |
| 122 | _intermediate.at(i).dim(j) = second_node->dim(j); |
| 123 | num_elems *= second_node->dim(j).value(); |
| 124 | } |
| 125 | _intermediate.at(i).size<loco::DataType::FLOAT32>(num_elems); |
| 126 | |
| 127 | // Check the buffer is initilized with zero |
| 128 | for (uint32_t j = 0; j < num_elems; j++) |
| 129 | assert(_intermediate.at(i).at<loco::DataType::FLOAT32>(j) == 0.0); |
| 130 | |
| 131 | // Save output names for logging |
| 132 | _output_names.emplace_back(first_node->name()); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | void MAEPrinter::accum_absolute_error(uint32_t output_idx, const std::shared_ptr<Tensor> &a, |
| 137 | const std::shared_ptr<Tensor> &b) |