Creating Evals to Validate LLM Training
In a previous article I showed how to teach a local model a new domain through a combination of llm training and RAG. In this article I will discuss my strategy for defining evals to ensure the model is performing correctly.
Project
Just as a quick refresher, my previous article describes how I trained a local model (qwen 3.5 4B) to act as a travel advisor for a fictional subway system. The main idea is to rely on continued pretraining (CPT) to teach the model how to reason about the subway system. But to add support for real-time updates, I combined the internalized knowledge with RAG.
As a visual reference, I have added a picture of the fictional subway map below:
Evals
One of the key components in determining if a model is performing well is to add comprehensive evals around it. I designed my evals around the following two requirements:
I need to measure if the model can generalize well, and answer questions not previously seen during training
I need to measure the model’s accuracy level as a baseline for future improvements or regressions
In my eval solution I ended up with three metrics:
- Exact Match
- Partial Match
- Semantic Match
Exact Match
When possible, exact match is likely the best way to measure success since it allows you to make simple comparisons between model output and some reference data. This is by design a strict comparison since the match must be exact. In some cases, this may be unrealistic, but I will show a technique that worked well in this project:
Initially my model was returning subway itineraries in the form of English sentence. This presented a challenge to exact match comparisons since two correct sentences can vary greatly but still represent the correct answer. As a solution to this, I decided to pivot my solution to instead output the subway itinerary in the form of a structured json schema. In some case this is not an option, but the legs of a subway journey lend themselves well to this format.
I have included a sample of a predicted subway journey consisting of three legs below:
Depending on the strength and size of your model, json output might work out of the box, but smaller models may need some help. In my case I decided to fine tune the model to reliably output the subway itineraries in the form of json.
For the actual fine tuning I am using Unsloth with Supervised Fine Tuning (SFT). The only goal of SFT in this case is to teach the model to output responses in the desired format. I have included a link to the SFT corpus here in case you want to have a look.
The SFT corpus defines entries following the Alpaca format, which is more of a Q&A format, but I did not want to rely on SFT for teaching knowledge, only format. One of the key reasons I want to avoid using SFT to train knowledge is that it usually leads to Q&A memorization that doesn’t generalize well to unseen questions. Instead, I want the subway routing knowledge to continue to come from CPT training.
I experimented a bit with SFT settings, but in the end, I ended up having success with a lightweight LORA setup. I have shared the Unsloth SFT config here. Post SFT I tested the model by running an eval set of 604 routing questions.
Results:
As I have reported previously, model performance is very good. Post CPT and SFT I was able to get the exact match evals up to 98% from 573/604 correct results.
Partial Match
573/604 results are an exact match, but what happened to the remaining 31 questions? In all 31 cases the model provided a partially correct subway route, but one or more legs were missing. It is useful to measure partial successes since it creates a baseline for later improvements or regressions when retraining.
I have added a summary of partial results below. This table shows the partial correctness scores for the 31 route predictions that failed the exact match test.
| Case | Journeys | Correctness | Missing route legs or issue |
|---|---|---|---|
| generated_heldout_route_076 | 2 journey | 33.3% | Missing: north terminal to founder's square on blue line. Extra: north terminal to central station on blue line; central station to founder's square on blue line. |
| generated_heldout_route_103 | 2 journey | 33.3% | Missing: south gardens to university commons on blue line. Extra: south gardens to central station on blue line; central station to university commons on blue line. |
| generated_heldout_route_163 | 2 journey | 33.3% | Missing: founder's square to south gardens on blue line; south gardens to central station on blue line. Extra: founder's square to central station on blue line. |
| generated_heldout_route_171 | 2 journey | 50.0% | Missing: westgate to east harbor on gold line. Extra: westgate to central station on gold line; central station to east harbor on gold line. |
| generated_heldout_route_208 | 3 journey | 60.0% | Missing: central station to founder's square on blue line; founder's square to south gardens on blue line. Extra: central station to south gardens on blue line. |
| generated_heldout_route_219 | 3 journey | 60.0% | Missing: central station to founder's square on blue line; founder's square to south gardens on blue line. Extra: central station to south gardens on blue line. |
| generated_heldout_route_234 | 3 journey | 60.0% | Missing: central station to founder's square on blue line; founder's square to south gardens on blue line. Extra: central station to south gardens on blue line. |
| generated_heldout_route_253 | 3 journey | 80.0% | Missing: founder's square to south gardens on blue line. |
| generated_heldout_route_284 | 3 journey | 50.0% | Missing: old mill station to westgate on gold line; westgate to central station on gold line. Extra: old mill station to central station on gold line. |
| generated_heldout_route_297 | 3 journey | 60.0% | Missing: north terminal to river market on blue line. Extra: north terminal to central station on blue line; central station to river market on blue line. |
| generated_heldout_route_329 | 4 journey | 40.0% | Missing: westgate to east harbor on gold line; east harbor to old mill station on gold line; old mill station to central station on gold line. Extra: westgate to central station on gold line. |
| generated_heldout_route_338 | 4 journey | 71.4% | Missing: river market to south gardens on blue line; south gardens to central station on blue line. Extra: river market to central station on blue line. |
| generated_heldout_route_348 | 4 journey | 85.7% | Missing: south gardens to central station on blue line. Extra: blue_station_s六 to central station on blue line. |
| generated_heldout_route_352 | 4 journey | 50.0% | Missing: central station to founder's square on blue line; founder's square to central station on blue line; central station to lake harmony on green line; lake harmony to central station on green line. |
| generated_heldout_route_366 | 4 journey | 42.9% | Missing: central station to south gardens on blue line; south gardens to central station on blue line; central station to emerald hills on green line; emerald hills to museum district station on green line. Extra: central station to museum district station on green line. |
| generated_heldout_route_380 | 4 journey | 66.7% | Extra: central station to river market on blue line; river market to central station on blue line; central station to sunrise point on gold line; sunrise point to central station on gold line. |
| generated_heldout_route_392 | 4 journey | 75.0% | Missing: sunrise point to old mill station on gold line. Extra: sunrise point to central station on gold line; central station to old mill station on gold line. |
| generated_heldout_route_404 | 4 journey | 42.9% | Missing: central station to founder's square on blue line; founder's square to south gardens on blue line; south gardens to university commons on blue line. Extra: central station to university commons on blue line; university commons to south gardens on blue line; south gardens to founder's square on blue line; founder's square to north terminal on blue line. |
| generated_heldout_route_410 | 4 journey | 57.1% | Missing: east harbor to old mill station on gold line; central station to university commons on blue line. Extra: east harbor to central station on gold line; central station to university commons on green line; central station to old mill station on gold line. |
| generated_heldout_route_451 | 5 journey | 71.4% | Missing: central station to founder's square on blue line; founder's square to south gardens on blue line. Extra: central station to south gardens on blue line. |
| generated_heldout_route_452 | 5 journey | 60.0% | Missing: central station to founder's square on blue line; founder's square to central station on blue line; central station to east harbor on gold line; east harbor to central station on gold line. |
| generated_heldout_route_457 | 5 journey | 77.8% | Missing: central station to founder's square on blue line; founder's square to south gardens on blue line. Extra: central station to south gardens on blue line. |
| generated_heldout_route_458 | 5 journey | 80.0% | Missing: westgate to east harbor on gold line. Extra: westgate to central station on gold line; central station to east harbor on gold line. |
| generated_heldout_route_464 | 5 journey | 54.5% | Missing: sunrise point to old mill station on gold line; central station to east harbor on gold line. Extra: sunrise point to central station on gold line; central station to second_station on gold line; second_station to central station on gold line; central station to old mill station on gold line; central station to east harbor on green line. |
| generated_heldout_route_474 | 5 journey | 75.0% | Missing: central station to north terminal on blue line; north terminal to river market on blue line. Extra: central station to river market on blue line. |
| generated_heldout_route_479 | 5 journey | 77.8% | Missing: east harbor to old mill station on gold line; old mill station to central station on gold line. Extra: east harbor to central station on gold line. |
| generated_heldout_route_482 | 5 journey | 80.0% | Missing: river market to south gardens on blue line. Extra: river market to central station on blue line; central station to south gardens on blue line. |
| generated_heldout_route_490 | 5 journey | 66.7% | Missing: westgate to east harbor on gold line; east harbor to central station on gold line; central station to founder's square on blue line. Extra: westgate to central station on gold line; central station to river market on blue line; river market to founder's square on blue line. |
| generated_heldout_route_492 | 5 journey | 38.9% | Missing: central station to founder's square on blue line; founder's square to central station on blue line; central station to east harbor on gold line. Extra: central station to river market on blue line; river market to central station on blue line; central station to westgate on gold line; westgate to central station on gold line; central station to river market on blue line; river market to central station on blue line; central station to westgate on gold line; westgate to central station on gold line; central station to river market on blue line; river market to central station on blue line; central station to westgate on gold line. |
| generated_heldout_route_498 | 5 journey | 80.0% | Missing: central station to founder's square on blue line; founder's square to central station on blue line. Extra: university commons to central station on blue line; central station to university commons on blue line. |
| generated_heldout_route_499 | 5 journey | 75.0% | Missing: north terminal to river market on blue line; river market to central station on blue line. Extra: north terminal to central station on blue line. |
Semantic Match
The third category is semantic matching, which is less common in a schema-based response, but I noticed a few cases during testing.
In this experiment I noticed that the model would occasionally output superfluous route legs (e.g. an unnecessary but correct intermediate station during a journey). While these additions make the route more verbose, it doesn’t interfere with the factual accuracy of the full route since it’s just additional information. I decided to capture this nuance in the evals, even though the final solution didn’t contain any semantic match cases.
Eval Set
As I mentioned above, the full eval set consists of 604 routing questions. None of these eval cases appear exactly in either CPT or SFT training corpuses. There is, however, some overlap between some route pairs in multi-part journeys (i.e. an itinerary that contains multiple sub journeys).
Out of the 604 eval questions, 457 questions are completely held out with no overlap. 137 eval questions have partial overlap with sub routes defined in the CPT corpus, but only 10 eval questions have full overlap
There is no overlap with the routes defined in the SFT corpus.
Based on this I have included the performance of the respective eval questions below. As you can tell, performance is good regardless of overlap. The table covers both raw exact correctness as well as adjusted correctness with partial matches included.
Held-out evaluation results
| Training exposure | Cases | Exact matches | Exact-match rate | Partial answers | Partial rate | Weighted correctness |
|---|---|---|---|---|---|---|
| Completely held out | 457 | 437 | 95.6% | 20 | 4.4% | 98.3% |
| Partially held out | 137 | 126 | 92.0% | 11 | 8.0% | 96.8% |
| Fully CPT-covered | 10 | 10 | 100.0% | 0 | 0.0% | 100.0% |
| Total | 604 | 573 | 94.9% | 31 | 5.1% | 98.0% |
Conclusion
Overall, the evals tell a good story regarding the trained qwen 3.5:4B model’s ability to act as a travel advisor in this fictional subway domain. As always, the code is available on Github in case you are interested in checking it out.