GRPO Training Dynamics for Small Language Models
Organizations: Nutanix
Abstract
Group Relative Policy Optimization (GRPO) has emerged as a memory-efficient reinforcement fine-tuning (RFT) technique for reasoning-intensive tasks. How- ever, GRPO training dynamics on small language models (SLMs) remain poorly understood, limiting its reliable adoption and reproducibility in open and resource- constrained environments. In this work, we present a systematic study of GRPO fine-tuning for SLMs ranging from 1.5B to 7B parameters under a practical single- node 8xA100 compute budget. Our study spans multiple model families and reasoning domains, including mathematics, coding, and multiple-choice question answering (MCQ) in science. Across these settings, we analyze how group size affects policy convergence, training stability, and downstream benchmark per- formance. We further characterize tensor-level update dynamics during GRPO training and investigate whether the choice of LoRA target modules and layers can improve the performance of GRPO-tuned models. While our initial GRPO-tuned models outperform their base counterparts on approximately 80% of mathematical benchmark evaluations, they demonstrate limited capability on MCQ and code reasoning tasks. Guided by our mechanistic evaluations, we refined our LoRA and reward-shaping configurations to improve performance in latter domains. These findings provide practical guidance for GRPO training for SLMs.
Figures & tables
| DeepSeek-7B | DeepSeek-R1-Qwen-1.5B | Nemotron-4B | ||||||||
| Benchmark | Metric | Base | G=16 | G=4 | Base | G=16 | G=4 | Base | G=16 | G=4 |
| GSM-Plus (2400) | pass@1 | 25.64 | 30.83 0.71 | 40.25 0.08 | 33.48 | 36.52 1.32 | 34.80 1.50 | 9.11 | 20.32 5.55 | 19.46 2.92 |
| pass@5 | 49.25 | 52.19 0.91 | 60.50 0.41 | 55.54 | 57.86 0.67 | 56.44 1.15 | 21.92 | 40.86 8.52 | 42.02 3.86 | |
| Math-500 (500) | pass@1 | 15.00 | 16.12 0.11 | 15.48 1.07 | 70.80 | 75.74 0.59 | 73.18 0.08 | 11.64 | 13.34 0.31 | 12.62 0.93 |
| pass@5 | 32.40 | 33.50 0.14 | 33.30 3.54 | 85.20 | 89.60 0.85 | 88.70 1.84 | 31.40 | 31.50 0.42 | 31.60 1.98 | |
| AIME-2026 (30) | pass@1 | 0.00 | 0.00 0.00 | 0.34 0.47 | 1.33 | 6.17 0.23 | 4.50 0.24 | 0.00 | 0.00 0.00 | 0.00 0.00 |
| DeepSeek-7B | DeepSeek-R1-Qwen-1.5B | Nemotron-4B | ||||||||
| Benchmark | Metric | Base | G=16 | G=4 | Base | G=16 | G=4 | Base | G=16 | G=4 |
| MMLU-STEM (3153) | pass@1 | 40.14 | 41.64 0.25 | 40.97 0.01 | 56.18 | 55.44 0.18 | 55.23 0.05 | 48.56 | 47.82 0.05 | 46.92 0.16 |
| pass@3 | 59.95 | 57.09 0.66 | 57.82 0.36 | 79.31 | 79.82 0.05 | 79.73 0.03 | 62.92 | 58.17 0.09 | 59.64 0.31 | |
| pass@5 | 68.79 | 63.62 0.64 | 65.16 0.59 | 86.36 | 87.33 0.21 | 87.28 0.16 | 68.92 | 62.43 0.11 | 65.23 0.52 | |
| BBH (1000) | pass@1 | 30.82 | 30.83 0.21 | 31.12 0.02 | 40.76 | 41.97 0.55 | 41.72 0.16 | 31.40 | 30.67 0.85 | 30.78 0.54 |
| pass@3 | 51.17 | 46.78 1.24 | 47.95 0.30 | 69.43 | 69.04 0.24 | 69.14 0.12 | 51.45 | 44.80 1.91 | 49.58 0.71 | |
| DeepSeek-7B | Nemotron-4B | |||||||||
| Benchmark | Metric | Base | G=4 | (G=4) | Base | G=4 | R. Layer (G=4) | R. Module (G=4) | (G=4) | All (G=4) |
| MMLU-STEM (3153) | pass@1 | 40.14 | 40.97 | 40.29 | 48.56 | 46.92 | 47.76 | 48.21 | 47.57 | 48.37 |
| pass@3 | 59.95 | 57.82 | 59.65 | 62.92 | 59.64 | 62.27 | 62.12 | 61.12 | 62.56 | |
| pass@5 | 68.79 | 65.16 | 68.22 | 68.92 | 65.23 | 68.25 | 67.90 | 66.73 | 68.60 | |
| BBH (1000) | pass@1 | 30.82 | 31.12 | 30.98 | 31.40 | 30.78 | 30.66 | 30.80 | 31.04 | 30.68 |
| pass@3 | 51.17 | 47.95 | 50.69 | 51.45 | 49.58 | 51.54 | 50.90 | 50.78 | 49.55 | |
| DeepSeek-7B | DeepSeek-R1-Qwen-1.5B | Nemotron-4B | ||||||||
| Benchmark | Metric | Base | G=8 | G=4 | Base | G=8 | G=4 | Base | G=8 | G=4 |
| MBPP (500) | pass@1 | 34.98 | 34.87 0.78 | 35.73 0.21 | 32.38 | 34.46 0.34 | 37.24 0.14 | 32.70 | 33.83 0.64 | 32.23 0.30 |
| pass@5 | 51.27 | 47.01 0.72 | 49.40 1.09 | 49.51 | 53.48 0.08 | 54.43 0.04 | 48.30 | 42.17 0.57 | 41.29 0.48 | |
| APPS (1000) | pass@1 | 10.23 | 9.33 0.17 | 10.58 0.37 | 20.90 | 26.14 0.35 | 24.14 0.16 | 8.79 | 6.59 0.69 | 6.28 0.39 |
| pass@5 | 18.35 | 15.37 0.13 | 16.85 0.46 | 34.27 | 40.39 0.33 | 37.48 0.56 | 19.86 | 12.56 1.65 | 13.28 1.13 | |
| Codeforces (198) | pass@1 | 0.76 | 2.60 0.10 | 1.70 0.30 | 4.29 | 6.20 0.10 | 5.10 0.00 | 3.33 | 2.20 0.60 | 2.30 0.57 |
| DeepSeek-7B | Nemotron-4B | ||||||||
| Benchmark | Metric | Base | G=4 | R. Layer (G=4) | Base | G=4 | R. Layer (G=4) | R. Layer + RR (G=4) | R. Module + RR (G=4) |
| MBPP (500) | pass@1 | 34.98 | 35.73 | 36.46 | 32.70 | 32.23 | 32.86 | 33.56 | 33.92 |
| pass@5 | 51.27 | 49.40 | 51.44 | 48.30 | 41.29 | 46.47 | 46.85 | 45.99 | |
| APPS (1000) | pass@1 | 10.23 | 10.58 | 10.80 | 8.79 | 6.28 | 10.13 | 10.35 | 11.83 |
| pass@5 | 18.35 | 16.85 | 17.62 | 19.86 | 13.28 | 20.65 | 20.34 | 20.31 | |
| Codeforces (198) | pass@1 | 0.76 | 1.70 | 0.76 | 3.33 | 2.30 | 2.83 | 3.18 | 3.03 |
Appendix figures & tables17 assets
Supplementary material from the paper’s appendix.
Appendix
| Component | Value | Behavior |
| GSM8K | ||
| Correctness Reward | 2.0 | Final answer matches ground truth |
| XML Count Reward | 0.5 | All required XML tags and sections are present |
| Numeric Answer Present Reward | 0.5 | Response contains a numeric answer |
| Format Reward | 0.5 | Output follows expected structure |
| Reasoning Present Reward | 0.1 | Reasoning block is non-empty |
| Category | GSM8K | OpenCoder | ARC-Challenge |
| Training | |||
| Learning Rate | |||
| Epochs | 2 | 1 | 2 |
| Batch Size | 4 | 1 | 4 |
| Gradient Accumulation | 4 | 64 | 4 |
| Temperature | 0.8 | 0.8 | 0.8 |
| Question. Josh decides to try flipping a house. He buys a house for 50,000 in repairs. This increased the value of the house by 150%. How much profit did he make? Ground truth: $70,000 | |
| Deepseek-LLM-7B-Chat (base) ✗ | Deepseek-LLM-7B GRPO ✓ |
| Predicted: $150,000 | Predicted: $70,000 |
| Josh bought the house for 50,000 in repairs, so the total cost was 50,000 = 80,000 + 1.5 280,000 . To calculate the profit, we subtract the total cost from the new value of the house, so -130,000 = 150,000. The answer is: 150000 | Josh bought the house for 50,000 on repairs. The total cost was 50,000 = 80,000 + (\times80,000 + 200,000. The profit Josh made is the new value of the house minus the cost, so it is -130,000 = $70,000. The answer is: 70000 |
| Question. Janet’s ducks lay 16 eggs per day. She eats three for breakfast every morning and bakes muffins for her friends every day with four. She sells the remainder at the farmers’ market daily for 18 | |
| Nemotron-4B (base) ✗ | Nemotron-4B GRPO ✓ |
| Predicted: 1 (malformed) | Predicted: 18 |
| The ducks lay 16 eggs per day. Janet eats 3 eggs per day so the number of eggs available to her is 16 3 = 13 eggs. She bakes muffins with 4 eggs, so the number of eggs left is 13 4 = 9 eggs. Out of these 9 eggs, she sells 9 2 = 18 dollars worth of eggs at the farmers’ market. Thus she makes 1} dollars every day at the farmers’ market. | Sure! Let’s break down the problem step by step. 1. Janet’s ducks lay 16 eggs per day. 2. She eats 3 eggs for breakfast every morning, so she eats 3 eggs per day. 3. She bakes muffins for her friends every day with 4 eggs. So she uses 4 eggs per day for baking. 4. In total, she uses 3 + 4 = 7 eggs per day. 5. She has 16 7 = 9 eggs left to sell at the farmers’ market. 6. She sells 9 eggs at \times18 at the farmers’ market per day. Therefore, the answer is $18 . |
| Question. Every day, Wendi feeds each of her chickens three cups of mixed chicken feed, containing seeds, mealworms and vegetables to help keep them healthy. She gives the chickens their feed in three separate meals. In the morning, she gives her flock of chickens 15 cups of feed. In the afternoon, she gives her chickens another 25 cups of feed. How many cups of feed does she need to give her chickens in the final meal of the day if the size of Wendi’s flock is 20 chickens? Ground truth: 20 | |
| DS-R1-Qwen-1.5B (base) ✗ | DS-R1-Qwen-1.5B GRPO (G4, ) ✓ |
| Predicted: 25 | Predicted: 20 |
| Okay, so I need to figure out how many cups of feed Wendi needs to give her 20 chickens in the final meal of the day. Each chicken gets three cups per day, split into three meals. So total feed per day is cups. She already gave cups, so the remaining is cups. Alternatively, maybe she feeds each chicken 3 cups every day, but the 3 cups are per meal, not per chicken. If each meal is 1 cup per chicken, then for 20 chickens each meal is 20 cups. Morning: gave 15, short by 5. Afternoon: gave 25, over by 5… Wait, this is getting confusing. Maybe she can’t give 5 cups over, so she can only give 20 cups in the afternoon, but the problem says she gave 25… [oscillates between the two readings for several more paragraphs] The answer is: 25 | Wendi feeds her 20 chickens three cups of mixed feed each day, divided into three meals. Total feed required per day: . Total feed given in the morning and afternoon: . Subtract the feed already given from the total daily requirement: . Thus, the final meal of the day requires 20 cups of feed. |
| Question. The period of rotation for each planet in our solar system is different. The period of rotation for Earth is: (A) 24 hours (B) 28 days (C) 13 weeks (D) 1 year Ground truth: (A) | |
| DS-R1-Qwen-1.5B (base) ✗ | DS-R1-Qwen-1.5B GRPO ✓ |
| Predicted: (D) | Predicted: (A) |
| To determine the period of rotation for Earth, I need to recall the definition of a year. A year is the time it takes for Earth to complete one full orbit around the Sun Next, I should consider the options provided. Option A is 24 hours, which is the duration of a day, not a year. Option B is 28 days, which is approximately the length of a month, not a year. Option C is 13 weeks, which is about the duration of a month as well. Option D is 1 year, which aligns perfectly with the definition of a year. Therefore, the correct answer is D, 1 year. </think> To determine the period of rotation for Earth, let’s analyze the options provided: Choices: - (A) 24 hours - (B) 28 days - (C) 13 weeks - (D) 1 year Step-by-Step Explanation: 1. Understanding the Term "Period of Rotation": - The period of rotation refers to the time it takes for a celestial body to complete one full orbit around its axis. 2. Definition of a Year: - A year is the time it takes for Earth to complete one full orbit around the Sun. 3. Analyzing the Options: - Option A (24 hours): This is the duration of a day, not a year. - Option B (28 days): This is approximately the length of a month, not a year. - Option C (13 weeks): This is about the duration of a month as well. - Option D (1 year): This matches the definition of a year, as it represents Earth’s orbital period around the Sun. Conclusion: The correct answer is (D) 1 year , as it accurately describes the period of rotation for Earth. D | To determine the period of rotation for Earth, I need to recall the definition of a day. A day is the time it takes for Earth to complete one full rotation on its axis, which is 24 hours. Next, I should consider the options provided. Option A is 24 hours, which directly matches the definition of a day. Option B is 28 days, which is longer than a year and doesn’t represent Earth’s rotation period. Option C is 13 weeks, which is approximately 91 days, also not matching Earth’s rotation. Option D is 1 year, which is much longer than Earth’s rotation period. Therefore, the correct answer is A, 24 hours. </think> To determine the period of rotation for Earth, let’s analyze the options step by step. 1. Understanding the Question: - The question asks for the period of rotation of Earth. - The options are: - (A) 24 hours - (B) 28 days - (C) 13 weeks - (D) 1 year 2. Recalling the Definition of a Day: - A day is defined as the time it takes for Earth to complete one full rotation on its axis. - This duration is exactly 24 hours . 3. Evaluating the Options: - Option A (24 hours): This matches the definition of a day, making it the correct answer. - Option B (28 days): This is longer than a year and does not represent Earth’s rotation period. - Option C (13 weeks): This is approximately 91 days, which is not the period of Earth’s rotation. - Option D (1 year): This is much longer than Earth’s rotation period. 4. Conclusion: - The only option that accurately represents Earth’s period of rotation is 24 hours . A |
| Question. Petrified palm trees are found in sedimentary rock near glaciers. The presence of the petrified palm trees most likely provides evidence for which statement? (A) There was once more water in the area. (B) The area was once grassland. (C) The climate in the area was once tropical. (D) There are active faults in the area. Ground truth: (C) | |
| DeepSeek-LLM-7B-Chat (base) ✗ | GRPO model ✓ |
| Predicted: (A) | Predicted: (C) |
| The correct answer is (A) There was once more water in the area. The presence of petrified palm trees near glaciers suggests that the area was once near a tropical forest. This would indicate that there was once more water in the area, which is conducive to the growth of palm trees. ... (C) The climate in the area was likely not tropical at the time the palm trees were alive, as they are now fossilized. | The presence of petrified palm trees in the vicinity of a glacier indicates that the area was once warmer and wetter, which is consistent with a tropical climate. This supports the statement that the area was once tropical. Therefore, the correct answer is (C) The climate in the area was once tropical. |
| Question. The stem is an important part of many plants. Which of the following is most similar to the role performed by the stem of a plant? (A) an anchor holding a boat in place (B) a snack company producing energy bars (C) a sign attracting people (D) an elevator transporting supplies from one floor to another. Ground truth: (D) | |
| Nemotron-Mini-4B (base) ✗ | GRPO model ✓ |
| Predicted: (A) | Predicted: (D) |
| The answer is A) an anchor holding a boat in place | The answer is (D) an elevator transporting supplies from one floor to another The stem of a plant performs a similar function as an elevator in a building. It transports materials (water and nutrients) from the roots to the leaves and other parts of the plant, just like how an elevator carries people and supplies from one floor to another. Here’s a brief comparison: - Stem (plant): Transports water and nutrients from roots to leaves and other parts of the plant. - Elevator (building): Transports people and supplies from one floor to another. Option (A) is incorrect because an anchor holds a boat in place, not performs a transport function. Option (B) is incorrect because a snack company does not produce materials. Option (C) is incorrect because a sign does not transport materials or people. |
| Question. Write a python function to find the maximum sum of elements of list in a list of lists. | |
| Nemotron Base ✗ | Nemotron GRPO ✓ |
| Pass Rate: 0/10 | Pass Rate: 7/10 |
| reasoning To find the maximum sum of elements in a list of lists, we can iterate through each sublist and find the maximum sum for that sublist. We can do this by iterating through each element in the sublist and adding it to a running total, and then updating the maximum sum if necessary. /reasoning answer def maximum_Sum(matrix): max_sum = 0 for sublist in matrix: current_sum = 0 for num in sublist: current_sum += num if current_sum max_sum: max_sum = current_sum return max_sum /answer | reasoning This problem can be solved using a dictionary to store the elements and their indices. If an element is already in the dictionary, it means it has been seen before, so we return its index. If not, we add it to the dictionary and return -1. /reasoning answer def find_first_duplicate(arr): return next((i for i, x in enumerate(arr) if arr.count(x) 1), -1) /answer |
| Question. Write a function to perform index wise multiplication of tuple elements in the given two tuples. | |
| Deepseek 7B Base ✗ | Deepseek 7B GRPO ✓ |
| Pass Rate: 0/10 | Pass Rate: 10/10 |
| reasoning The function takes two tuples as input and returns a new tuple where the i-th element is the product of the i-th elements of the two input tuples. /reasoning answer def index_multiplication(t1, t2): result = [] for i in range(len(t1)): result.append(t1[i] * t2[i]) return tuple(result) /answer | reasoning The index_multiplication function takes two tuples as input and returns a new tuple with the index-wise multiplication of the elements of the input tuples. /reasoning answer def index_multiplication(t1, t2): return tuple(tuple(i * j for i, j in zip(t1[i], t2[i])) for i in range(len(t1))) /answer |
| Question. Write a python function to check whether the given number can be represented as difference of two squares or not. | |
| DeepSeek-R1-Qwen-1.5B Base ✗ | DeepSeek-R1-Qwen-1.5B GRPO ✓ |
| Pass Rate: 0/10 | Pass Rate: 8/10 |
| Okay, I need to write a Python function called dif_Square that checks whether a given number can be represented as difference of two squares or not. Let’s think about how to approach this. First, I should understand what it means for a number to be expressed as the difference of two squares. The difference of two squares can be written as a 2 - b 2 ... [... thinking overflow truncated for brevity ...] Ah, because in the code, D must be less than S. In this case, D=1, S=1. So, D is not less than S. | Alright, I need to write a Python function called dif_Square that checks if a given number can be represented as the difference of two squares. The function should return True if it can, and False otherwise. Let’s think about how to approach this. [... thinking overflow truncated for brevity ...] ### Solution Code: ‘‘‘python def dif_Square(n): if n = 0: return False for x in range(1, int(n**0.5) + 1): if n % x == 0: y = n // x if (x % 2) == (y % 2): return True return False ‘‘‘ |
| Question. You are given two huge binary integer numbers and of lengths and respectively. You will repeat the following process: if , then add to the answer the value and divide by rounding down, and repeat the process again… Your task is to calculate the answer modulo . | |
| Nemotron Base ✗ | Nemotron GRPO ✓ |
| Pass Rate: 0/10 | Pass Rate: 7/10 |
| reasoning We can solve this problem using a while loop. We iterate through the binary numbers and from right to left, performing a bitwise AND operation on them and adding the result to the answer... /reasoning answer ‘‘‘python def add_binary(a, b): n = len(a) m = len(b) ans = 0 while m 0: ans = (ans 1) + (a[-1] - ’0’) & (b[-1] - ’0’) a = a[:-1] b = b[:-1] m -= 1 return ans % 998244353 n, m = map(int, input().split()) a = input() b = input() print(add_binary(a, b)) ‘‘‘ /answer | reasoning The problem can be solved by repeatedly computing the bitwise AND of and , adding the result to the answer, and then dividing by and rounding down. We need to ensure that the result is added modulo . /reasoning answer ‘‘‘python def add_and_divide(a, b): ans = 0 while b 0: ans += (a & b) % 998244353 b //= 2 return ans % 998244353 n, m = map(int, input().split()) a = int(input(), 2) b = int(input(), 2) print(add_and_divide(a, b)) ‘‘‘ /answer |
| Question. Takahashi is organizing a party. At the party, each guest will receive one or more snack pieces. Takahashi predicts that the number of guests at this party will be A or B. Find the minimum number of pieces that can be evenly distributed to the guests in both of the cases predicted. | |
| Deepseek 7B Base ✗ | Deepseek 7B GRPO ✓ |
| Pass Rate: 0/10 | Pass Rate: 9/10 |
| reasoning To minimize the number of snack pieces, we need to distribute them in a way that maximizes the number of guests they can be evenly distributed to. In the worst-case scenario, we need to distribute the pieces to both A guests and B guests. Therefore, we need to find the least common multiple (LCM) of A and B... /reasoning answer def main(): A, B = map(int, input().split()) lcm = abs(A*B) // math.gcd(A, B) print(lcm) main() /answer | reasoning We need to find the smallest cost of a route that takes not longer than time T. We can sort the routes by time and then select the one with the smallest cost. /reasoning answer def main(): n, t = map(int, input().split()) routes = sorted([list(map(int, input().split())) for _ in range(n)], key=lambda x: x[1]) min_cost = t for c, t in routes: if t = t: min_cost = min(min_cost, c) if min_cost == t: print(’TLE’) else: print(min_cost) main() /answer |
| Question. A triple of numbers is said to be poor when two of those numbers are equal but the other number is different from those two numbers. You will be given three integers A, B, and C. If this triple is poor, print Yes; otherwise, print No. | |
| DeepSeek-R1-Qwen-1.5B Base ✗ | DeepSeek-R1-Qwen-1.5B GRPO ✓ |
| Pass Rate: 0/10 | Pass Rate: 9/10 |
| Okay, I need to solve this problem where I have to determine if a triple of numbers is "poor." Let me read the problem statement again to make sure I understand it correctly. So, a triple is considered poor if two of the numbers are equal, but the third is different. For example... [... thinking overflow truncated for brevity ...] Another test case: 5,5,5,5,5,5,5,5,5,5 len is 1 no. Another test case: 5,5,5,5,5,5,5,5,5,5,5 len is 1 no. | Okay, I need to solve this problem where I have to determine if a given triple of numbers is ’poor’. Let’s see what the problem says. So, a triple is considered poor if exactly two of the numbers are equal, and the third is different. That means, for example, if the numbers are 5,7,5, then two are 5 and one is 7. So it’s poor. But if all three are the same, like 5,5,5, then it’s not poor. [... thinking overflow truncated for brevity ...] ### Solution Code ‘‘‘python from collections import Counter # Read the input a, b, c = map(int, input().split()) # Create a frequency counter freq = Counter([a, b, c]) # Determine the maximum frequency and the sum of the remaining frequencies max_freq = max(freq.values()) sum_remaining = sum(freq.values()) - max_freq # Check if the triple is poor if max_freq == 2 and sum_remaining == 1: print("Yes") else: print("No") ‘‘‘ |