Concepts / Scoring Model Responses

Scoring Model Responses

generate_model_scores builds a prompt for each entry, asks a model for a 0 to 100 score, converts the reply to an integer, and returns the collected list.

  • Programming

Why Models Need Scoring

After a model has been fine-tuned, someone has to judge how good its answers are. One practical approach is to ask another model to grade each answer on a numeric scale. The function studied here does exactly that. It takes a dataset of entries, asks a model to score each stored response, and returns the list of scores it collected. The function is called generate_model_scores, and it is the core of the evaluation step in this chapter.

What do you think happens?

A grader model is asked to reply with a number, but for one entry it replies with the word eighty instead. Given that the loop must keep going, what do you think gets printed and what ends up in the final list?

  • The function crashes and returns nothing

  • The word eighty is stored in the list as text

  • A message is printed, eighty is left out, and the remaining entries are still scored

Reveal answer
Answer:

A message is printed, eighty is left out, and the remaining entries are still scored.

The conversion to an integer fails for eighty. The except branch prints Could not convert score followed by the reply, then continue moves the loop on. The value never reaches the list, and the run is not stopped.

Building the Prompt for Each Entry

The function is written so that you can read it top to bottom. It starts with an empty list, loops over the entries, builds a prompt for each entry, asks the model for a score, converts that score to an integer, and stores it. Every entry produces its own prompt.

The prompt contains three pieces of information: the formatted input of the entry, the correct output stored with that entry, and the model response that the entry holds under the key passed in as json_key. The prompt then asks for a score on a scale from 0 to 100, where 100 is the best score, and it demands an integer only. Asking for an integer only is what makes the next step, the conversion, possible at all.

Because the response being graded is read from json_key, the same function can score any response field in the dataset. The source calls it with the key model_response, and the model argument defaults to llama3 when it is not given. The helpers format_input and query_model are called here but are not shown in the source, so treat them as black boxes: format_input returns a formatted string, and query_model returns a model reply.

The Conversion Step

The conversion is the fragile step. Inside the loop, the function tries scores.append(int(score)). If the reply can be converted, the integer is stored. If it cannot, Python raises a ValueError, and the except ValueError branch runs instead. That branch prints the message Could not convert score, followed by the offending reply, and then continue moves the loop to the next entry. The function does not stop, and it does not store anything for the bad entry.

Model replystring from the graderint(reply) succeeds?Append integere.g. 85 storedNext entryloop continuesPrint Could notconvert scoreValueError caughtyesno, ValueErrorcontinue
When the model's reply is converted to an integer, the success step stores the value and the failure step prints a message. Both paths end at the next entry in the loop.

Trace the two paths with the reply eighty. The reply is the string eighty, not the integer 85. Calling int on that string fails, so the success path is closed. The failure path prints the message and continues. Now trace the reply 85. It is the string 85, which int converts to the integer 85, so it is appended and the loop moves on. The same check runs for every reply, so each entry ends at exactly one of the two paths.

Skipping Bad Scores Without Stopping

The try and except pattern lets one unusable reply be reported and skipped while the rest of the test set is still scored. Here is a small sketch of that pattern. It loops over three replies, where the middle reply is the word eighty. The replies are invented for this teaching example, because the source does not list the real replies of its test set.

python
Output (expected)
Could not convert score: eighty
[85, 42]
Notes on the code
  • The try block holds only the risky conversion and the store.
  • int("eighty") raises ValueError, so control jumps to the except branch.
  • continue moves the loop to the next reply instead of ending the run.
Entry 1reply 85 storedCollected list[85, 42]Entry 2reply eighty skippedEntry 3reply 42 storednextcontinueappendappend
Entry one and entry three are stored. Entry two fails the conversion, is reported, and is skipped, and the loop still reaches entry three.

The verified run of this error path printed exactly two lines. The first line is the message from the except branch for the reply eighty. The second line is the list of integers the function collected, [85, 42]. The value eighty never reaches the list, and the entries that converted cleanly are kept in their original order.

Following One Entry From Prompt to List

It helps to follow a single entry through the whole function. The entry is formatted into an input string and combined with its correct output and its stored model response to build the prompt. The prompt goes to the model, and the model's reply comes back as text. The reply is converted to an integer. If the conversion works, the integer is added to the list that the function returns at the end.

Entryinput, correct output,responseScoring promptasks for integer 0 to 100Model replytext such as 85Integerint(reply)Returned liststored in orderformat_inputquery_modelint()append
A single entry moves from prompt construction, through the model reply, into an integer, and finally into the returned list.

Reading the Run Report

The source applies the function to the entire test_data set in a single call. It reports that this run takes about one minute on an M3 MacBook Air, so the time cost is real and should be expected when you evaluate a model. The reporting code that follows the call prints two things. The first line compares the number of scores produced with the number of entries in test_data. If the two numbers differ, some entries were not turned into scores. The second line prints the average of the scores.

Rule

Here $s_i$ is each stored score and $n$ is the number of stored scores. The denominator is the length of the returned list, not the number of entries in test_data. For the illustration above, the stored scores are 85 and 42. Their sum is 127 and their count is 2, so the average is $\frac{127}{2} = 63.5$. The average is computed over the usable scores only, so it describes the entries the model answered in a convertible way, not the whole test set.

Mistakes Beginners Make With Skipped Scores

  • Common mistake

    Assuming an unusable reply stops the whole run

    What it looks like

    The reply eighty is reported and the loop still scores the entry that follows it.

    Why it fails

    The except branch ends with continue, which moves the loop on to the next entry instead of ending the function.

    Do this instead

    Read the except branch as a skip. The run keeps going and only the failed entry is left out of the list.

  • Common mistake

    Dividing the sum by the number of entries instead of the number of scores

    What it looks like

    With three entries and two stored scores, dividing 127 by 3 gives the wrong average.

    Why it fails

    A skipped entry adds nothing to the sum, so counting it in the denominator pulls the average down for no reason.

    Do this instead

    Divide by len of the returned list, which is the number of stored scores.

  • Common mistake

    Putting the whole function body inside the try block

    What it looks like

    A try block that wraps the prompt building and the model call makes every error look like a conversion failure.

    Why it fails

    The except ValueError branch is meant for the conversion step. Other errors would be hidden or misreported.

    Do this instead

    Keep the try block small and focused on the step that can fail, here the int conversion and the append.

  • Common mistake

    Ignoring the printed count when it differs from the number of entries

Practice: Trace a Run

MEDIUM

A test set has four entries. The model replies 90, then seventy, then 60, then 100. Predict the printed error line, the final list, and the average. Explain which step handled the bad reply and where the loop went next.

Hints
  • Look at each reply in order and decide whether int can convert it.
  • Only the converted integers go into the list, so the average uses only those values.
  • The printed count of 3 would differ from the 4 entries, which signals one skip.

Key Takeaways

  • generate_model_scores builds a prompt for each entry from its formatted input, its correct output, and its stored response, then asks the model for an integer from 0 to 100.
  • The conversion scores.append(int(score)) either stores an integer or raises ValueError, which the except branch reports and skips with continue.
  • The try and except pattern lets one unusable reply be reported while the rest of the test set is still scored.
  • The average is the sum of the stored scores divided by how many scores were stored, so a lower printed count means the average covers fewer entries.

Related concepts

More concepts starting with S