项目地址
课程地址
个人完成的作业地址
作者原文
ch07中我们成功将一个表达式解析为树结构,这节将其转化为逆波兰表达式,并增加操作数。让表达式能够成功运算。
逆波兰表达式
上节解析的结构为树状,打印时候是中序。
逆波兰表达式是后序遍历。解析结构如下
add(@1,@2) 逆波兰表达式1 2 add
add(mul(@0,@1),@2)逆波兰表达式0 1 mul 2 add
根据逆波兰表达式进行计算:
遇到操作数,将数字入栈。
遇到操作符,从栈中弹出两个数字,再将计算结果入栈。
// 逆波兰 后序遍历
void ReversePolish(const std::shared_ptr<TokenNode>&root_node, std::vector<std::shared_ptr<TokenNode>>& reverse_polish){
if(root_node!=nullptr){
ReversePolish(root_node->left, reverse_polish);
ReversePolish(root_node->right, reverse_polish);\
reverse_polish.push_back(root_node);
}
}
现在的generate函数与上节相比,增加转换为逆波兰表达式部分。
上节返回std::shared_ptr<TokenNode>
这节返回std::vector<std::shared_ptr<TokenNode>>
std::vector<std::shared_ptr<TokenNode>> ExpressionParser::Generate() {
if (this->tokens_.empty()) {
this->Tokenizer(true);
}
int index = 0;
std::shared_ptr<TokenNode> root = Generate_(index);
//以下新增
CHECK(root != nullptr);
CHECK(index == tokens_.size() - 1);
std::vector<std::shared_ptr<TokenNode>> reverse_polish;
ReversePolish(root, reverse_polish); // 逆波兰表达式
return reverse_polish;
}
算子expression_op
上节虽然增加了解析表达式的功能,但没有增加专门的表达式算子,表达式运算层。
class ExpressionOp:public Operator{
public:
explicit ExpressionOp(const std::string &expr);
std::vector<std::shared_ptr<TokenNode>> Generate();
private:
std::shared_ptr<ExpressionParser> parser_;
std::vector<std::shared_ptr<TokenNode>> nodes_;
std::string expr_;
};
运算层expression_layer
class ExpressionLayer:public Layer{
public:
explicit ExpressionLayer(const std::shared_ptr<Operator> &op);
void Forwards(const std::vector<std::shared_ptr<Tensor<float>>> &inputs,
std::vector<std::shared_ptr<Tensor<float>>> &outputs) override;
private:
std::unique_ptr<ExpressionOp> op_;
};
Forwards函数
// 首先检查输入是否为空
CHECK(!inputs.empty());
// 初始化outputs数组中的元素.
const uint32_t batch_size = outputs.size();
CHECK(batch_size != 0);
for (uint32_t i = 0; i < batch_size; ++i) {
CHECK(outputs.at(i) != nullptr && !outputs.at(i)->empty());
outputs.at(i)->Fill(0.f);
}
CHECK(this->op_!=nullptr&&this->op_->op_type_==OpType::kOperatorExpression);
// 用于存储计算结果
// std::vector<std::shared_ptr<Tensor<float>>> 一个batch放在一个vector里面
std::stack<std::vector<std::shared_ptr<Tensor<float>>>> op_stack;
// 生成逆波兰表达式
const std::vector<std::shared_ptr<TokenNode>> &token_nodes = this->op_->Generate();
// 遍历节点
for (const auto &token_node : token_nodes) {
// 1. 如果遇到数字类型,从inputs取batchsize个数字,拼成一个操作数
// 因为tensor最多三维,如果想表达一个batch的tensor,必须用vector装载
if (token_node->num_index >= 0) { // 操作数
uint32_t start_pos = token_node->num_index * batch_size;
std::vector<std::shared_ptr<Tensor<float>>> input_token_nodes;
for (uint32_t i = 0; i < batch_size; ++i) {
CHECK(i + start_pos < inputs.size());
input_token_nodes.push_back(inputs.at(i + start_pos));
}
op_stack.push(input_token_nodes);
// 2. 如果遇到运算符
const int32_t op = token_node->num_index; // 操作符
// 先检查栈中临时结果是否大于等于2(运算数够不够用)
CHECK(op_stack.size() >= 2) << "The number of operand is less than two";
// 取数字
std::vector<std::shared_ptr<Tensor<float>>> input_node1 = op_stack.top();
CHECK(input_node1.size() == batch_size);
op_stack.pop();
std::vector<std::shared_ptr<Tensor<float>>> input_node2 = op_stack.top();
CHECK(input_node2.size() == batch_size);
op_stack.pop();
// 临时计算结果
std::vector<std::shared_ptr<Tensor<float>>> output_token_nodes(batch_size);
for (uint32_t i = 0; i < batch_size; ++i) {
if (op == -int(TokenType::TokenAdd)) {
output_token_nodes.at(i) = ftensor::ElementAdd(input_node1.at(i), input_node2.at(i));
} else if (op == -int(TokenType::TokenMul)) {
output_token_nodes.at(i) = ftensor::ElementMultiply(input_node1.at(i), input_node2.at(i));
} else {
LOG(FATAL) << "Unknown operator type: " << op;
}
}
// 计算结果入栈
op_stack.push(output_token_nodes);
// 检查最终结果
// 最后栈中应该只有一个值
CHECK(op_stack.size() == 1);
std::vector<std::shared_ptr<Tensor<float>>> output_node = op_stack.top();
op_stack.pop();
for (int i = 0; i < batch_size; ++i) {
CHECK(outputs.at(i) != nullptr && !outputs.at(i)->empty());
outputs.at(i) = output_node.at(i);
}
TEST
没有用注册机制
const std::string &expr = "add(mul(@0,@1),@2)";
std::shared_ptr<ExpressionOp> expression_op = std::make_shared<ExpressionOp>(expr);
ExpressionLayer layer(expression_op);
由于generate返回结果变化,shownode函数也需要变化
static void ShowNodes(const std::vector<std::shared_ptr<kuiper_infer::TokenNode>> &nodes) {
for(std::shared_ptr<kuiper_infer::TokenNode> node:nodes){
if (node->num_index < 0) {
if (node->num_index == -int(kuiper_infer::TokenType::TokenAdd)) {
LOG(INFO) << "ADD";
} else if (node->num_index == -int(kuiper_infer::TokenType::TokenMul)) {
LOG(INFO) << "MUL";
}
else if (node->num_index == -int(kuiper_infer::TokenType::TokenDiv)) {
LOG(INFO) << "Div";
}
} else {
LOG(INFO) << "NUM: " << node->num_index;
}
}
}
逆波兰表达式解析结果
layer.Forwards(inputs, outputs)带操作数的运算结果
目前通过的test数为18