Fine-Tuning: Teaching a Model a Repeated Behaviour

Fine-tuning changes a model's behaviour by training it on examples of the task you want. It is usually a way to teach format, style or repeated task behaviour—not a good way to keep a changing knowledge base up to date.

For facts, I would start with retrieval. For instructions, examples and output structure that need to work consistently, fine-tuning may be worth testing.

When I would consider it

Fine-tuning is worth considering when the task is frequent, the desired behaviour is clear and prompt changes have stopped producing reliable results. Typical examples are a fixed output format, a consistent classification task or a house style that must be applied repeatedly.

It is not my first choice when the problem is missing knowledge, an unclear requirement or a small number of examples. Those are usually prompting, retrieval or application-code problems.

The workflow

The process is unglamorous: prepare representative examples, upload the dataset, run the job, evaluate the result and compare it with the base model.

A Practical Example: Building a Tone-Tuned Assistant

I’ll fine-tune a model to be a "startup summarizer." It will take technical release notes and rewrite them in an optimistic, slightly informal tone suitable for a startup blog.

1. Preparing the Data

First, I need to create a training dataset. This is a .jsonl file where each line is a JSON object containing a conversation. I need at least 10 examples.

// training-data.jsonl
{"messages": [{"role": "system", "content": "You are a tech copywriter for a startup. Your tone is exciting, optimistic, and highlights user benefits."}, {"role": "user", "content": "The data import process now supports CSV files up to 100MB and includes asynchronous processing."}, {"role": "assistant", "content": "Say goodbye to data import limits! You can now upload massive CSV files (up to 100MB!), and our new asynchronous processing means you can get back to work while we handle the heavy lifting."}]}
{"messages": [{"role": "system", "content": "You are a tech copywriter for a startup. Your tone is exciting, optimistic, and highlights user benefits."}, {"role": "user", "content": "Added a new API endpoint (/api/v2/users) with pagination support."}, {"role": "assistant", "content": "We've just supercharged our API! The new /api/v2/users endpoint is here, complete with pagination, making it easier than ever to work with your user data at scale."}]}
{"messages": [{"role": "system", "content": "You are a tech copywriter for a startup. Your tone is exciting, optimistic, and highlights user benefits."}, {"role": "user", "content": "The dashboard load time has been improved by 30% by optimizing the initial data fetch."}, {"role": "assistant", "content": "Get ready for a speed boost! We've optimized our data fetching, making your dashboard load a blazing 30% faster. Less waiting, more doing!"}]}

2. Running the Fine-Tuning Job

Next, I write a TypeScript script to manage the process. It handles uploading the file, starting the job and monitoring its progress.

// lib/fine-tuning/run-fine-tuning.ts
import OpenAI from 'openai';
import fs from 'fs';
import 'dotenv/config';

const openai = new OpenAI(); // Assumes OPENAI_API_KEY is in your .env

async function main() {
  const trainingFilePath = './training-data.jsonl';

  try {
    // --- Step 1: Upload the training file ---
    console.log('Uploading training file...');
    const file = await openai.files.create({
      file: fs.createReadStream(trainingFilePath),
      purpose: 'fine-tune',
    });
    console.log(`File uploaded successfully. File ID: ${file.id}`);

    // --- Step 2: Create the fine-tuning job ---
    console.log('\nCreating fine-tuning job...');
    let job = await openai.fineTuning.jobs.create({
      training_file: file.id,
      model: 'gpt-3.5-turbo', // You can use gpt-4o, gpt-4-turbo, etc.
    });
    console.log(`Fine-tuning job created. Job ID: ${job.id}`);

    // --- Step 3: Monitor the job status ---
    console.log('\nMonitoring job progress... (This can take some time)');
    const checkStatus = async () => {
      job = await openai.fineTuning.jobs.retrieve(job.id);
      console.log(`- [${new Date().toLocaleTimeString()}] Status: ${job.status}`);
      if (job.status === 'succeeded' || job.status === 'failed' || job.status === 'cancelled') {
        return;
      }
      setTimeout(checkStatus, 30000); // Check every 30 seconds
    };
    await checkStatus();

    // --- Step 4: Use the fine-tuned model ---
    if (job.status === 'succeeded' && job.fine_tuned_model) {
      console.log('\nFine-tuning successful!');
      console.log(`Custom Model ID: ${job.fine_tuned_model}`);

      console.log('\nTesting the new model:');
      const completion = await openai.chat.completions.create({
        messages: [
            { role: 'system', content: 'You are a tech copywriter for a startup. Your tone is exciting, optimistic, and highlights user benefits.'},
            { role: 'user', content: 'Fixed a bug where the login button was sometimes disabled.' }
        ],
        model: job.fine_tuned_model,
      });

      console.log('Test Response:\n', completion.choices[0].message.content);
    } else {
      console.log('\nFine-tuning did not succeed. Final status:', job.status);
    }
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

main();

Key Considerations

  • Cost: Fine-tuning is more expensive than standard API calls. There's a cost for the training process and a higher cost for using the custom model.
  • Data Quality: The success of fine-tuning depends almost entirely on the quality and quantity of your training data. You need at least 10 high-quality examples, but hundreds or thousands are better.
  • Time: The training process isn't instant. It can take anywhere from a few minutes to several hours, depending on the model and the size of your dataset.

Fine-tuning can be useful when you need a model to follow a specific format, tone or task pattern consistently. Use it after simpler options like better prompting, examples and retrieval have been tested.