“Profit and loss problems,” “age problems,” “tree planting problems,” “Newton’s cow-and-grass problems,” “profit problems”... were you ever tortured by various kinds of mathematical word problems during primary school? Don’t worry, machine learning models can now help us solve these word problems. Let’s see what grade it can reach!
This article will provide a baseline for solving primary school Math Word Problems (MWP), trained on the ape210k dataset. It uses a Seq2Seq model to directly generate executable mathematical expressions. Ultimately, the Large version of the model achieves an accuracy of 75%, which is significantly higher than the results reported in the ape210k paper. The term “directly tackling” (hard-coding) refers to the fact that no special transformations were made to the expressions, nor were they processed through templates; the model directly generates readable expressions similar to how humans solve them.
Data Processing
First, let’s observe the format of the ape210k dataset:
{
"id": "254761",
"segmented_text": "Xiao Wang wants to dilute 150 kg of pesticide with a 20% drug content into a pesticide with a 5% drug content. How many kilograms of water need to be added?",
"original_text": "Xiao Wang wants to dilute 150 kg of pesticide with a 20% drug content into a pesticide with a 5% drug content. How many kilograms of water need to be added?",
"ans": "450",
"equation": "x=150*20%/5%-150"
}
{
"id": "325488",
"segmented_text": "The radius of a circular flower bed is 4 meters. Now the flower bed is to be expanded, increasing the radius by 1 meter. How many square meters has the area of the flower bed increased by?",
"original_text": "The radius of a circular flower bed is 4 meters. Now the flower bed is to be expanded, increasing the radius by 1 meter. How many square meters has the area of the flower bed increased by?",
"ans": "28.26",
"equation": "x=(3.14*(4+1)**2)-(3.14*4**2)"
}As we can see, we are primarily interested in the original_text, equation, and ans fields. The original_text is the problem description, the
equation is the calculation process
(usually starting with x=), and ans is the final answer. We hope to train a
model that generates the equation from
the original_text, which can then be
evaluated using Python’s eval() function to obtain the
ans.
However, we need to perform some preprocessing because the equation provided by ape210k cannot always be evaluated directly. For example, in the case above, 150*20%/5%-150 is an invalid expression for Python. The processing I performed is as follows:
1. For percentages like a%, uniformly replace them with (a/100);
2. For mixed fractions like a(b/c), uniformly replace them with (a+b/c);
3. For proper fractions like (a/b), remove the parentheses in the problem text to become a/b;
4. For the ratio colon :, uniformly replace it with /.
After this processing, most equations
can be evaluated directly, and the results can be compared with
ans to retain only the problems with consistent results.
Additionally, there is room for improvement: the resulting expressions
might contain redundant parentheses (i.e., removing them doesn’t change
the value). Therefore, I added a step to remove parentheses by iterating
through each pair; if removing them yields the same result, they are
discarded. This results in shorter average expression lengths, and
shorter sequences are generally easier to generate.
Ultimately, we obtained the following usable dataset:
| Training Set | Validation Set | Test Set | |
|---|---|---|---|
| Original Count | 200488 | 5000 | 5000 |
| Retained Count | 200390 | 4999 | 4998 |
The remaining items are mostly incorrect or garbled problems, which are ignored for now.
Model Introduction
The model itself is straightforward: it takes original_text as input and equation as output, using “BERT+UniLM” as the basic architecture to train a Seq2Seq model. If you have any doubts about the model, please read “From Language Models to Seq2Seq: Transformer is All About Masking”.
Project Link: http://github.com/bojone/ape210k_baseline
My training was conducted on a single 22G TITAN RTX card. The
optimizer used was Adam with a learning rate of 2 \times 10^{-5}. The Base version used a
batch_size of 32 and required about 25 epochs, with each
epoch taking approximately 50 minutes (including evaluation time on the
validation set). The Large version used a batch_size of 16
and required about 15 epochs, with each epoch taking approximately 2
hours.
Regarding the Large version, since UniLM utilizes the weights of the MLM (Masked Language Model) part, we cannot use the RoBERTa-wwm-ext-large released by HFL, as the MLM weights in that version are randomly initialized (though its Base version is normal and usable). For the Large version, I recommend using the weights released by Tencent UER. The original weights are in PyTorch format; I have converted them to TensorFlow format, which can be downloaded here (Extraction code: l0k6).
The results are shown in the table below:
| beam_size | Validation Set | Test Set | |
|---|---|---|---|
| Base | 1 | 71.67% | 71.65% |
| Base | 2 | 71.81% | 72.27% |
| Base | 3 | 71.85% | 72.35% |
| Large | 1 | 74.51% | 74.43% |
| Large | 2 | 74.97% | 74.99% |
| Large | 3 | 75.04% | 75.01% |
The results of the Large model are significantly higher than the 70.20% reported in the ape210k paper “Ape210K: A Large-Scale and Template-Rich Dataset of Math Word Problems”, indicating that our model is a decent baseline. I suspect that using Seq2Seq techniques to mitigate the Exposure Bias problem (refer to “Analysis and Countermeasures of Exposure Bias in Seq2Seq”) could further improve the model. Additionally, introducing a copy mechanism might enhance the consistency between input and output numbers. One could also try to further shorten the sequence length (e.g., replacing the four-character 3.14 with the two-letter pi). These are left for everyone to try!
Standardized Output
From a purely modeling perspective, our task is complete: the model
only needs to output the expression, and evaluation only requires
checking if the eval() result matches the reference answer.
However, from a practical application standpoint, we need to further
standardize the output. That is, we must decide whether the output
should be a decimal, integer, fraction, or percentage based on the
problem. This requires us to: 1. Decide when to output which format;
2. Convert the result according to the specified format.
The first step is relatively simple. Generally, one can judge based on keywords in the problem or the equation. For example, if there are decimals in the expression, the output is usually a decimal. If the problem asks “how many cars,” “how many items,” or “how many people,” the output should be an integer. If it asks for a “fraction” or “percentage,” the output should be formatted accordingly. The more difficult part involves rounding problems, such as: “Each box of cake costs 7.90 yuan. How many boxes of cake can 50 yuan buy at most?” This requires us to floor the result of 50/7.90, but sometimes ceiling is required. Surprisingly, there are no rounding problems in ape210k, so this issue does not exist there. If encountered in other datasets where rule-based judgment is difficult, the most direct method is to include rounding symbols in the equation for the model to predict.
The second step might seem complex, especially for fractions. Average readers might not know how to keep the fractional result of an expression. If you directly run eval(’(1+2)/4’), you get 0.75 (in Python 3), but sometimes we want the fractional result 3/4. In fact, maintaining fractional calculations belongs to the domain of CAS (Computer Algebra System), which performs symbolic rather than numerical computation. Python has a tool for this called SymPy. Using SymPy, we can achieve our goal. See the following example:
from sympy import Integer
import re
r = (Integer(1) + Integer(2)) / Integer(4)
print(r) # Output is 3/4 instead of 0.75
equation = '(1+2)/4'
print(eval(equation)) # Output is 0.75
new_equation = re.sub('(\d+)', 'Integer(\\1)', equation)
print(new_equation) # Output is (Integer(1)+Integer(2))/Integer(4)
print(eval(new_equation)) # Output is 3/4Summary
This article introduced a baseline for solving math word problems using a Seq2Seq model. The main idea is to directly convert problems into evaluatable expressions using “BERT+UniLM,” followed by sharing experiences on result standardization. Using the UniLM based on BERT Large, we achieved an accuracy of 75%, surpassing the results released in the original paper.
So, what grade do you think it can reach now?
Please include the original link when reposting: https://kexue.fm/archives/7809
For more details on reposting, please refer to: Scientific Space FAQ