Lewati ke konten utama

Cara Fine-Tune FunctionGemma: Panduan Langkah demi Langkah

Pelajari cara fine-tune FunctionGemma dalam waktu kurang dari 10 menit menggunakan GPU gratis Kaggle, mulai dari penyiapan dataset dan evaluasi baseline hingga pelatihan dan validasi pasca fine-tuning.
· 13 mnt Baca

Jelajahi bersama AI

Dalam tutorial ini, kita akan membahas FunctionGemma, model bahasa pemanggilan fungsi yang ringan dari Google DeepMind, dan saya akan menjelaskan mengapa fine-tuning sangat penting untuk mencapai penggunaan tool yang andal dan sesuai skema.

Kita akan mulai dengan menyiapkan lingkungan Kaggle yang mendukung GPU, lalu memuat dan menyiapkan dataset serta model dasar FunctionGemma.

Selanjutnya, kita akan melakukan evaluasi pra–fine-tuning untuk menetapkan baseline pemilihan tool dan akurasi pemanggilan fungsi. Lalu kita akan melakukan fine-tuning FunctionGemma menggunakan pelatihan tersupervisi dan mengevaluasi kinerjanya setelah pelatihan untuk memverifikasi bahwa fine-tuning telah diterapkan dengan benar.

Jika Anda mencari latihan praktik untuk mempelajari fine-tuning, saya merekomendasikan untuk melihat kursus Fine-Tuning with Llama 3.

Apa itu FunctionGemma?

FunctionGemma adalah versi khusus dari Google Gemma 3 270M yang bersifat open model, dirancang khusus untuk pemanggilan fungsi dan penggunaan tool alih-alih percakapan umum.

Model ini menggunakan arsitektur yang sama dengan Gemma 3 tetapi menyertakan format khusus dan fokus pelatihan yang memungkinkannya menghasilkan output terstruktur yang merepresentasikan panggilan fungsi.

FunctionGemma dirilis sebagai model dasar yang dapat di–fine-tune oleh pengembang untuk kasus penggunaan spesifik. Ukuran dan desainnya membuatnya ringan, efisien, dan dapat dijalankan pada perangkat dengan sumber daya terbatas, seperti laptop dan perangkat edge.

Mengapa Melakukan Fine-Tune FunctionGemma?

Meskipun FunctionGemma dilatih untuk pemanggilan fungsi, model berukuran seperti ini bekerja paling baik saat dispesialisasi melalui fine-tuning pada data yang spesifik tugas.

Fine-tuning membantu model mempelajari pola yang stabil untuk memilih fungsi yang benar dari kumpulan tool dan memformat argumen yang sesuai dengan benar untuk kasus penggunaan nyata.

Proses ini menghasilkan output terstruktur yang lebih konsisten dan dapat diprediksi, yang pada gilirannya meningkatkan keandalan dalam alur kerja praktis.

Fine-Tuning FunctionGemma: Panduan Langkah demi Langkah

Mari kita telusuri langkah-langkah yang diperlukan untuk melakukan fine-tuning FunctionGemma.

1. Menyiapkan lingkungan Kaggle

Mulailah dengan membuka Kaggle dan membuat notebook baru. Setelah notebook terbuka, cari panel Session options di sisi kanan dan atur Accelerator ke GPU (T4 ×2).

Ini mengaktifkan akselerasi GPU untuk sesi tersebut dan memungkinkan Anda melakukan fine-tuning model menggunakan sumber daya komputasi gratis Kaggle.

Memilih sesi GPU di Kaggle.

Setelah mengaktifkan GPU, buat sel kode baru dan jalankan perintah berikut untuk memasang semua dependensi Python yang diperlukan. Kernel mungkin membutuhkan waktu sebentar untuk mulai, setelah itu paket akan terpasang secara otomatis.

%pip -q install -U datasets accelerate trl kagglehub sentencepiece huggingface_hub tqdm evaluate jiwer

Selanjutnya, tambahkan token akses Hugging Face Anda secara aman.

Dari menu atas, buka Add-ons → Secrets, lalu klik Add Secret. Atur nama secret menjadi HUGGINGFACE_TOKEN dan tempel kunci API Hugging Face Anda sebagai nilainya.

Menggunakan Kaggle Secrets lebih aman daripada menulis langsung variabel lingkungan atau token di dalam notebook.

Mengatur secret Hugging Face di Kaggle.

Setelah secret disimpan, Kaggle akan menyediakan cuplikan untuk mengaksesnya secara terprogram.

Gunakan kode berikut untuk melakukan autentikasi dengan Hugging Face Hub:

from kaggle_secrets import UserSecretsClient
from huggingface_hub import login

hf_token = UserSecretsClient().get_secret("HUGGINGFACE_TOKEN")
if not hf_token:
    raise ValueError("HUGGINGFACE_TOKEN not found in Kaggle Secrets.")
login(token=hf_token)

Setelah autentikasi berhasil, Anda akan dapat mengakses model dan dataset yang diproteksi, serta mendorong model FunctionGemma yang telah di–fine-tune ke Hugging Face Hub untuk dibagikan dan digunakan kembali.

2. Memuat dataset Hermes Reasoning Tool-Use

Dataset Hermes Reasoning Tool-Use adalah dataset terbuka berbahasa Inggris dengan format penalaran penggunaan tool dan contoh terstruktur yang dirancang untuk melatih dan mengevaluasi model pada tugas pemilihan tool dan pemanggilan fungsi bergaya JSON.

Dataset ini berisi puluhan ribu sampel penalaran penggunaan tool dengan prompt bahasa natural dan skema tool dalam format yang cocok untuk fine-tuning tersupervisi.

Dalam tutorial ini, kita akan menggunakan subset dataset untuk eksperimen yang lebih cepat.

Pertama, tentukan seed acak dan total jumlah contoh yang akan digunakan. Lalu bagi data menjadi split pelatihan dan evaluasi:

from datasets import load_dataset

SEED = 40
N_TOTAL = 3000
N_EVAL  = 300
N_TRAIN = N_TOTAL - N_EVAL

Selanjutnya, muat dataset, acak, dan buat subset train dan eval:

raw = load_dataset("interstellarninja/hermes_reasoning_tool_use", split="train")
raw = raw.shuffle(seed=SEED).select(range(N_TOTAL))
train_ds = raw.select(range(N_TRAIN))
eval_ds  = raw.select(range(N_TRAIN, N_TOTAL))

print("train:", len(train_ds), "eval:", len(eval_ds))

Mengunduh dataset dari Hugging Face

Ini menghasilkan 2.700 sampel pelatihan dan 300 sampel evaluasi, yang cukup untuk mendemonstrasikan fine-tuning sambil menjaga kebutuhan komputasi tetap terkelola.

train: 2700 eval: 300

3. Memuat model dan processor FunctionGemma

Dalam tutorial ini, kita menggunakan KaggleHub untuk memuat model FunctionGemma langsung ke notebook Kaggle.

KaggleHub secara otomatis menentukan lokasi model dan menyediakannya di lingkungan notebook, sehingga tidak perlu mengunduh manual dari Hugging Face.

import kagglehub
from transformers import AutoProcessor, AutoModelForCausalLM

model_path = kagglehub.model_download("google/functiongemma/transformers/functiongemma-270m-it")

Setelah menjalankan sel di atas, berkas model akan muncul di bagian Input pada notebook Kaggle, menandakan bahwa model telah berhasil dimuat dan tersedia secara lokal.

Menggunakan pustaka Kaggle Hub untuk mengakses model functiongemma.

Setelah model tersedia, kita memuat processor dan model ke memori GPU.

Processor menangani template chat dan pemformatan skema tool, sementara model bertanggung jawab menghasilkan panggilan fungsi terstruktur.

Mengatur device_map="auto" memastikan model ditempatkan pada GPU yang tersedia, dan dtype="auto" memilih presisi efisien yang didukung perangkat keras.

processor = AutoProcessor.from_pretrained(model_path, device_map="auto")
model = AutoModelForCausalLM.from_pretrained(model_path, dtype="auto", device_map="auto")
tokenizer = processor.tokenizer if hasattr(processor, "tokenizer") else processor

print("dtype:", model.dtype, "| device:", model.device)

Ini mengonfirmasi bahwa FunctionGemma dimuat dengan benar, ditempatkan pada GPU, dan siap untuk evaluasi serta fine-tuning.

dtype: torch.bfloat16 | device: cuda:1

4. Menormalkan definisi tool dan mengekstrak gold function call

Sebelum melakukan fine-tuning FunctionGemma, kita perlu menstandarkan bagaimana tool direpresentasikan dan secara andal mengekstrak panggilan fungsi (gold) yang benar dari dataset.

Dataset Hermes Reasoning Tool-Use dapat menyimpan definisi tool dalam beberapa format, sehingga langkah ini mengonversi semuanya ke struktur konsisten yang dapat kita gunakan untuk fine-tuning tersupervisi.

Menormalkan field tools

Kita mulai dengan menormalkan field tools. Dalam dataset, tools dapat berupa list Python, string JSON, atau bahkan tidak ada sama sekali.

Fungsi pembantu di bawah ini mengonversi semua representasi yang valid menjadi list Python yang bersih dan dengan aman mengabaikan entri yang rusak atau kosong.

import re, json

def normalize_tools_field(tools):
    if tools is None:
        return []
    if isinstance(tools, list):
        return tools
    if isinstance(tools, str):
        s = tools.strip()
        if not s:
            return []
        try:
            parsed = json.loads(s)
            return parsed if isinstance(parsed, list) else []
        except Exception:
            return []
    return []

Menormalkan tipe parameter

Selanjutnya, kita menormalkan tipe parameter. Definisi tool Hermes sering menggunakan anotasi tipe informal seperti str,int, atau tipe kontainer seperti List[str]. Fungsi di bawah ini mengonversinya menjadi tipe JSON Schema yang valid dan kompatibel dengan pemanggilan fungsi Hugging Face.

def _parse_hermes_type(t) -> dict:
    if t is None:
        return {"type": "string"}
    if isinstance(t, dict):
        return t if "type" in t else {"type": "object"}
    if isinstance(t, list):
        return {"type": "array"}
    if not isinstance(t, str):
        return {"type": "string"}

    t = t.strip()
    prim = {
        "str": "string", "string": "string",
        "int": "integer", "integer": "integer",
        "float": "number", "number": "number",
        "bool": "boolean", "boolean": "boolean",
        "dict": "object", "object": "object",
        "list": "array", "array": "array",
    }
    if t.lower() in prim:
        return {"type": prim[t.lower()]}

    m = re.match(r"List\[(.+)\]$", t)
    if m:
        return {"type": "array", "items": _parse_hermes_type(m.group(1).strip())}

    m = re.match(r"Dict\[(.+),\s*(.+)\]$", t)
    if m:
        return {"type": "object", "additionalProperties": _parse_hermes_type(m.group(2).strip())}

    return {"type": "string"}

Mengonversi tool bergaya Hermes ke skema fungsi Hugging Face

Dengan utilitas ini, kita mengonversi definisi tool bergaya Hermes menjadi skema Hugging Face {"type": "function", "function": {...}}. Fungsi ini mendukung dua format:

  • Gaya Hermes per-argumen (setiap parameter memiliki spesifikasi kecil sendiri)
  • Tool yang sudah menyerupai skema JSON

Untuk format per-argumen, implementasi ini memperlakukan parameter yang terdaftar sebagai wajib jika daftar required tidak disediakan secara eksplisit.

def hermes_tools_to_hf_schema(tools_field):
    """
    Handles both:
    - Hermes per-arg style: {"parameters": {"x":{"type":"str"}, ...}}
    - Already-JSON-schema style: {"parameters":{"type":"object","properties":...,"required":[...]}}
    """
    hermes_tools = normalize_tools_field(tools_field)
    out = []

    for tool in hermes_tools:
        if not isinstance(tool, dict):
            continue

        name = tool.get("name")
        desc = tool.get("description", "")
        params = tool.get("parameters", {}) or {}

        # If params already look like JSON schema (best case)
        if isinstance(params, dict) and "type" in params and "properties" in params:
            json_schema_params = params
            if "required" not in json_schema_params:
                json_schema_params["required"] = []
        else:
            props, req = {}, []
            if isinstance(params, dict):
                for p_name, p_spec in params.items():
                    p_spec = p_spec or {}
                    if isinstance(p_spec, dict):
                        p_desc = p_spec.get("description", "")
                        p_type = p_spec.get("type", "str")
                    else:
                        p_desc, p_type = "", "str"

                    frag = dict(_parse_hermes_type(p_type))
                    if p_desc:
                        frag["description"] = p_desc
                    props[p_name] = frag
                    req.append(p_name)

            json_schema_params = {"type": "object", "properties": props, "required": req}

        out.append({
            "type": "function",
            "function": {
                "name": name,
                "description": desc,
                "parameters": json_schema_params,
            }
        })

    return out

Fungsi pembantu

Kita mendefinisikan pembantu kecil untuk mengambil skema tool yang telah dikonversi untuk setiap contoh dataset:

def get_tools_hf(ex):
    return hermes_tools_to_hf_schema(ex.get("tools"))

Mengekstrak gold function call

Selanjutnya, kita mengekstrak pemanggilan tool gold dari percakapan. Percakapan Hermes dapat berisi beberapa giliran, jadi kita mengurai blok <tool_call>{...}</tool_call> dan mengekstrak objek panggilan tool valid pertama (nama + argumen). Ini menjaga supervisi tetap sederhana dan konsisten.

TOOL_CALL_RE = re.compile(r"<tool_call>\s*(\{.*?\})\s*</tool_call>", re.DOTALL)

def extract_first_tool_call_obj(text: str):
    if not text:
        return None
    m = TOOL_CALL_RE.search(text)
    if not m:
        return None
    try:
        obj = json.loads(m.group(1))
        if isinstance(obj, dict) and "name" in obj and "arguments" in obj:
            return obj
    except Exception:
        return None
    return None

Menyelaraskan pemanggilan tool dengan tugas pengguna

Untuk menyelaraskan pemanggilan tool dengan tugas pengguna yang benar, kita memindai riwayat percakapan, menemukan pesan pengguna yang cocok dengan task pada dataset, lalu mengambil respons asisten tepat setelahnya. Jika respons asisten berisi pemanggilan tool, kita mengembalikannya sebagai label gold.

def _role(turn):
    return (turn.get("from") or "").lower().strip()

def get_gold_tool_call_task_aligned(ex):
    task = (ex.get("task") or "").strip()
    conv = ex.get("conversations") or []
    if not conv:
        return None

    idx = None
    if task:
        for i, t in enumerate(conv):
            if _role(t) in ["human", "user"]:
                val = (t.get("value") or "").strip()
                if val == task or task in val or val in task:
                    idx = i
                    break
    if idx is None:
        for i in range(len(conv)-1, -1, -1):
            if _role(conv[i]) in ["human", "user"]:
                idx = i
                break
    if idx is None:
        return None

    for j in range(idx+1, len(conv)):
        if _role(conv[j]) in ["gpt", "assistant", "model"]:
            gold = extract_first_tool_call_obj(conv[j].get("value", ""))
            if gold:
                return gold
        if _role(conv[j]) in ["human", "user"]:
            break
    return None

Di akhir langkah ini, setiap contoh dataset yang dapat digunakan memiliki:

  • Kumpulan skema tool yang dinormalkan (format Hugging Face)
  • Gold function call yang bersih dan selaras dengan tugas
  • Struktur konsisten yang siap untuk membangun sampel fine-tuning tersupervisi.

6. Membangun sampel fine-tuning tersupervisi untuk pemanggilan fungsi

Sekarang kita perlu mengonversinya menjadi sampel pelatihan Supervised Fine-Tuning (SFT) dalam format persis seperti yang diharapkan FunctionGemma, menggunakan fungsi pembantu di atas.

Tujuannya adalah membuat contoh di mana input berisi:

  • Pesan developer yang mengaktifkan pemanggilan fungsi
  • Tugas pengguna
  • Deklarasi tool (skema)

Dan targetnya adalah satu panggilan FunctionGemma terstruktur dalam format berikut:

<start_function_call>call:TOOL_NAME{args:<escape>{...}<escape>}<end_function_call>

Langkah 1: Membangun baris yang bersih dan dapat digunakan

Kita mulai dengan mengiterasi dataset dan menyaring contoh apa pun yang tidak dapat digunakan untuk pelatihan.

Fungsi ini hanya mempertahankan contoh di mana:

  • Ada pemanggilan tool gold
  • Ada daftar tool yang valid
  • Nama tool gold muncul dalam daftar tool
  • Argumen adalah dictionary yang valid
  • Jika tool tidak memiliki field wajib, argumen dipaksa menjadi {} untuk menghindari target yang bising atau tidak konsisten
from datasets import Dataset

def build_simple_rows(ds, max_rows=None):
    rows = []
    for ex in ds:
        # 1) task-aligned gold tool call
        gold = get_gold_tool_call_task_aligned(ex)
        if not gold:
            continue

        # 2) tools -> HF schema
        hf_tools = get_tools_hf(ex)
        if not hf_tools:
            continue

        # 3) build required_map: tool_name -> required fields
        required_map = {}
        for t in hf_tools:
            if t.get("type") == "function":
                fn = t.get("function", {})
                name = fn.get("name")
                req = (fn.get("parameters", {}) or {}).get("required", []) or []
                if name:
                    required_map[name] = req

        # 4) guard: gold tool must be in tool list
        tool_names = set(required_map.keys())
        if gold["name"] not in tool_names:
            continue

        # 5) force {} when no required params
        gold_args = gold.get("arguments", {})
        if not isinstance(gold_args, dict):
            gold_args = {}

        req = required_map.get(gold["name"], [])
        if len(req) == 0:
            gold_args = {}  # key fix

        rows.append({
            "user_content": ex.get("task", ""),
            "tool_name": gold["name"],
            "tool_arguments": json.dumps(gold_args, ensure_ascii=False),
            "hf_tools": hf_tools,
        })

        if max_rows and len(rows) >= max_rows:
            break

    return rows

Sekarang kita menghasilkan baris pelatihan dan evaluasi:

simple_train = build_simple_rows(train_ds, max_rows=N_TRAIN)
simple_eval  = build_simple_rows(eval_ds,  max_rows=N_EVAL)
print("usable train:", len(simple_train), "usable eval:", len(simple_eval))

Dalam kasus kita, hasilnya 961 sampel pelatihan dan 109 sampel evaluasi dari total 3.000. Penurunan ini wajar karena proses penyaringan ketat—menghapus contoh dengan panggilan tool yang hilang, definisi tool tidak valid, nama tool tidak cocok, atau argumen yang salah format.

usable train: 961 usable eval: 109

Langkah pembersihan ini sangat penting. Sebelum diterapkan, dataset berisi sampel yang bising dan tidak selaras, yang menyebabkan perilaku pemanggilan fungsi tidak stabil dan hasil buruk bahkan setelah fine-tuning. Setelah disaring hanya ke contoh berkualitas tinggi yang selaras dengan tugas, FunctionGemma menjadi jauh lebih konsisten, dan hasil fine-tuning meningkat signifikan.

Langkah 2: Mengonversi setiap baris menjadi satu sampel teks pelatihan

Berikutnya, kita memformat setiap baris ke struktur prompt-plus-target persis seperti yang dibutuhkan FunctionGemma. Kita menggunakan apply_chat_template milik processor untuk menyuntikkan deklarasi tool dengan benar, lalu menambahkan panggilan fungsi gold sebagai target.

def format_row_as_text(row):
    messages = [
        {"role": "developer", "content": "You are a model that can do function calling with the following functions"},
        {"role": "user", "content": row["user_content"]},
    ]
    prompt = processor.apply_chat_template(
        messages,
        tools=row["hf_tools"],
        add_generation_prompt=True,
        tokenize=False,
    )
    target = (
        f"<start_function_call>call:{row['tool_name']}"
        f"{{args:<escape>{row['tool_arguments']}<escape>}}"
        f"<end_function_call>"
    )
    return prompt + target

Sekarang kita konversi baris menjadi objek Dataset Hugging Face:

train_text_ds = Dataset.from_list([{"text": format_row_as_text(r)} for r in simple_train])
eval_text_ds  = Dataset.from_list([{"text": format_row_as_text(r)} for r in simple_eval])

Langkah 3: Memastikan setiap sampel berupa string yang valid

Beberapa versi TRL mengharuskan formatting_func mengembalikan string, dan pelatihan dapat gagal jika ada sampel menjadi list atau nilai non-string. Pembantu ini memaksa text selalu berupa string bersih.

# ensure text is always a string
def force_text_string(ds):
    def fix(ex):
        t = ex.get("text", "")
        if isinstance(t, list):
            t = "\n".join(map(str, t))
        return {"text": str(t)}
    return ds.map(fix)

train_text_ds = force_text_string(train_text_ds)
eval_text_ds  = force_text_string(eval_text_ds)

Terakhir, cetak satu sampel untuk mengonfirmasi pemformatan:

print(train_text_ds[0]["text"])

Anda akan melihat prompt yang dirender sepenuhnya berisi deklarasi tool, diikuti panggilan fungsi gold serupa dengan:

<bos><start_of_turn>developer
You are a model that can do function calling with the following functions<start_function_declaration>declaration:get_pollution_levels{description:<escape>Retrieve pollution levels information<escape>,parameters:{properties:{location:{description:<escape>The location for which you want to get the pollution levels (e.g., Beijing, London, New York)<escape>,type:<escape>STRING<escape>},pollutant:{description:<escape>Specify a pollutant for pollution levels (e.g., PM2.5, PM10, ozone)<escape>,type:<escape>STRING<escape>}},required:[<escape>location<escape>],type:<escape>DICT<escape>}}<end_function_declaration><start_function_declaration>declaration:get_water_quality{description:<escape>Retrieve water quality information<escape>,parameters:{properties:{location:{description:<escape>The location for which you want to get the water quality (e.g., river, lake, beach)<escape>,type:<escape>STRING<escape>}},required:[<escape>location<escape>],type:<escape>DICT<escape>}}<end_function_declaration><end_of_turn>
<start_of_turn>user
I'm planning a beach cleanup at Zuma Beach this weekend and need to ensure safety. Can you provide the current water quality and if it's poor, check the pollution level of PM2.5 there?<end_of_turn>
<start_of_turn>model
<start_function_call>call:get_water_quality{args:<escape>{"location": "Zuma Beach"}<escape>}<end_function_call>

7. Mengevaluasi FunctionGemma sebelum fine-tuning

Sebelum pelatihan, Anda sebaiknya mengukur seberapa baik model FunctionGemma dasar dalam melakukan pemanggilan fungsi. Baseline ini membantu memastikan bahwa fine-tuning benar-benar meningkatkan pemilihan tool dan pemformatan panggilan fungsi.

Di bagian ini, kita mengevaluasi dua hal:

  • Akurasi nama tool: apakah model memilih nama fungsi yang benar
  • TC-CER (Tool-Call Character Error Rate): seberapa dekat blok pemanggilan fungsi yang dihasilkan dengan blok gold, semakin rendah semakin baik

Kita menggunakan dua regular expression untuk mengekstrak nama tool yang diprediksi dan blok pemanggilan fungsi penuh dari output model, lalu membandingkannya dengan label gold.

import re
import evaluate
import torch
from tqdm.auto import tqdm

cer_metric = evaluate.load("cer")

# Gemma / FunctionGemma-style only
FG_BLOCK_RE = re.compile(r"<start_function_call>.*?<end_function_call>", re.DOTALL)
FG_NAME_RE  = re.compile(r"call:([a-zA-Z0-9_]+)\{", re.DOTALL)

def extract_tool_name(gen: str):
    gen = gen or ""
    m = FG_NAME_RE.search(gen)
    return m.group(1) if m else None

def extract_call_block(gen: str):
    gen = gen or ""
    m = FG_BLOCK_RE.search(gen)
    return m.group(0) if m else ""

def gold_call_block(r):
    return (
        f"<start_function_call>call:{r['tool_name']}"
        f"{{args:<escape>{r['tool_arguments']}<escape>}}"
        f"<end_function_call>"
    )

Loop evaluasi baseline

Fungsi di bawah ini menjalankan inferensi pada subset contoh evaluasi dan menghitung kedua metrik. Kita menonaktifkan sampling (do_sample=False) agar hasil deterministik dan mudah dibandingkan sebelum dan sesudah fine-tuning.

@torch.inference_mode()
def eval_tool_and_cer(proc, mdl, rows, n=50, max_new_tokens=128):
    mdl.eval()
    n = min(n, len(rows))
    tool_ok = 0
    preds, refs = [], []

    for i in tqdm(range(n)):
        r = rows[i]
        messages = [
            {"role": "developer", "content": "You are a model that can do function calling with the following functions"},
            {"role": "user", "content": r["user_content"]},
        ]

        inputs = proc.apply_chat_template(
            messages,
            tools=r["hf_tools"],
            add_generation_prompt=True,
            return_dict=True,
            return_tensors="pt",
        )
        inputs = {k: v.to(mdl.device) for k, v in inputs.items()}

        out = mdl.generate(
            **inputs,
            do_sample=False,
            max_new_tokens=max_new_tokens,
            pad_token_id=proc.eos_token_id,
        )

        gen = proc.decode(out[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True).strip()

        if extract_tool_name(gen) == r["tool_name"]:
            tool_ok += 1

        preds.append(extract_call_block(gen))
        refs.append(gold_call_block(r))

    return {
        "n_eval": n,
        "tool_accuracy": tool_ok / n,
        "TC-CER (lower is better)": cer_metric.compute(predictions=preds, references=refs),
    }

Jalankan evaluasi baseline:

pre_metrics = eval_tool_and_cer(processor, model, simple_eval, n=50, max_new_tokens=128)
print("PRE metrics:", pre_metrics)

Bahkan sebelum fine-tuning, FunctionGemma menunjukkan kinerja baseline yang kuat pada tugas ini.

Pada subset 50 contoh evaluasi, model mencapai akurasi nama tool 88%, yang berarti memilih fungsi yang benar dalam sebagian besar kasus.

Selain itu, Tool-Call Character Error Rate (TC-CER) sekitar 33% untuk blok pemanggilan fungsi penuh, yang menunjukkan bahwa meskipun tool yang benar sering dipilih, argumen dan pemformatan yang dihasilkan masih menyimpang dari target gold.

PRE metrics: {'n_eval': 50, 'tool_accuracy': 0.88, 'TC-CER (lower is better)': 0.33399307273626916}

Hasil ini menegaskan bahwa model FunctionGemma dasar sudah memahami mekanisme pemanggilan fungsi.

Namun, tingkat kesalahan karakter yang relatif tinggi menyoroti perlunya fine-tuning untuk meningkatkan kelengkapan argumen, konsistensi pemformatan, dan ketepatan struktural keseluruhan dari panggilan fungsi yang dihasilkan.

Memeriksa satu prediksi

Metrik itu berguna, tetapi penting juga untuk melihat output kualitatif. Pembantu di bawah ini menjalankan inferensi untuk satu contoh dan mencetak panggilan fungsi yang diprediksi berdampingan dengan referensi gold.

import torch

@torch.inference_mode()
def infer_one(proc, mdl, rows, idx=0, max_new_tokens=128):
    r = rows[idx]

    messages = [
        {"role": "developer", "content": "You are a model that can do function calling with the following functions"},
        {"role": "user", "content": r["user_content"]},
    ]

    inputs = proc.apply_chat_template(
        messages,
        tools=r["hf_tools"],
        add_generation_prompt=True,
        return_dict=True,
        return_tensors="pt",
    )
    inputs = {k: v.to(mdl.device) for k, v in inputs.items()}

    out = mdl.generate(
        **inputs,
        do_sample=False,
        max_new_tokens=max_new_tokens,
        pad_token_id=proc.eos_token_id,
    )

    gen = proc.decode(
        out[0][inputs["input_ids"].shape[-1]:],
        skip_special_tokens=True
    ).strip()

    pred_tool = extract_tool_name(gen)

    return {
        "task": r["user_content"],
        "tool_match": (pred_tool == r["tool_name"]),
        "predicted": extract_call_block(gen),
        "gold": gold_call_block(r),
    }

Jalankan pada satu contoh:

pre = infer_one(processor, model, simple_eval, idx=15)


print(f"""
TASK:
{pre['task']}

TOOL MATCH: {pre['tool_match']}

--- PREDICTED ---
{pre['predicted']}

--- GOLD ---
{pre['gold']}
""")

Output:

TASK:
I'm reviewing the schedule in the surgical unit. Can you fetch the surgical nursing details for patient ID 12345 undergoing a 'Cardiac Bypass' today? If the procedure is handled by Nurse Ratched, let's record a post-operative care task for this evening.

TOOL MATCH: True

--- PREDICTED ---
<start_function_call>call:get_surgical_nursing_information{nurse_id:<escape>Ratched<escape>,patient_id:<escape>12345<escape>,procedure_type:<escape>Cardiac Bypass<escape>}<end_function_call>

--- GOLD ---
<start_function_call>call:get_surgical_nursing_information{args:<escape>{"patient_id": "12345", "procedure_type": "Cardiac Bypass"}<escape>}<end_function_call>

Pada contoh ini, model memilih fungsi yang tepat, sehingga tool match bernilai True.

Namun, panggilan yang dihasilkan menyertakan argumen tambahan dan tidak mengikuti struktur target persis seperti yang digunakan dalam pelatihan.

Walau argumen tersebut secara semantik masuk akal, argumen itu tidak cocok dengan format panggilan fungsi gold, yang berkontribusi pada tingkat kesalahan karakter yang lebih tinggi. Ini menggambarkan mengapa fine-tuning diperlukan untuk meningkatkan konsistensi struktural dan kepatuhan skema, bukan hanya pemilihan tool.

8. Menyiapkan trainer model untuk fine-tuning

Sekarang kita memiliki dataset SFT yang bersih, langkah berikutnya adalah mengonfigurasi trainer yang akan melakukan fine-tuning FunctionGemma. Kita menggunakan SFTTrainer dari TRL, yang ringan dan andal untuk melakukan fine-tuning model gaya chat pada contoh teks.

Kita juga menetapkan direktori keluaran agar Kaggle menyimpan checkpoint hasil fine-tuning ke lokasi persisten.

from trl import SFTConfig, SFTTrainer

OUT_DIR = "/kaggle/working/functiongemma-hermes-ft"

Untuk mengurangi penggunaan VRAM saat pelatihan, kita mengaktifkan gradient checkpointing dan menonaktifkan KV cache.

# VRAM savings
model.gradient_checkpointing_enable()
model.config.use_cache = False

Konfigurasi trainer

Kita mendefinisikan konfigurasi pelatihan menggunakan SFTConfig. Pengaturan ini dipilih untuk menyeimbangkan stabilitas dan efisiensi pada GPU Kaggle, sekaligus menjaga waktu pelatihan tetap singkat.

cfg = SFTConfig(
    output_dir=OUT_DIR,
    max_length=512,
    packing=False,
    num_train_epochs=1,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=8,
    learning_rate=5e-5,
    lr_scheduler_type="cosine",
    warmup_ratio=0.03,
    logging_steps=10,
    eval_strategy="steps",
    eval_steps=10,
    report_to="none",
    fp16=(model.dtype == torch.float16),
    bf16=(model.dtype == torch.bfloat16),
    optim="adamw_torch_fused",
)

Fungsi formatting memastikan setiap contoh pelatihan dikembalikan sebagai satu string, yang disyaratkan oleh versi TRL saat ini.

def formatting_func(example):
    # MUST return a STRING (not list) for your TRL version
    t = example.get("text", "")
    if isinstance(t, list):
        t = "\n".join(map(str, t))
    return str(t)

Inisialisasi trainer

Terakhir, kita menginisialisasi SFTTrainer dengan model, konfigurasi, dataset, dan tokenizer.

trainer = SFTTrainer(
    model=model,
    args=cfg,
    train_dataset=train_text_ds,
    eval_dataset=eval_text_ds,
    processing_class=tokenizer,
    formatting_func=formatting_func,
)

9. Melakukan Fine-Tuning FunctionGemma

Dengan trainer yang telah dikonfigurasi, sekarang kita dapat memulai fine-tuning model. Langkah ini menjalankan pelatihan tersupervisi pada dataset yang disiapkan dan mengevaluasi model secara berkala selama pelatihan.

trainer.train()
trainer.save_model(OUT_DIR)
processor.save_pretrained(OUT_DIR)
print("Saved to:", OUT_DIR)

Setelah pelatihan selesai, kita menyimpan model yang telah di–fine-tune dan processor ke direktori keluaran agar dapat digunakan kembali untuk inferensi atau diunggah ke Hugging Face Hub.

Kerugian pelatihan dan validasi model

Selama pelatihan, baik loss pelatihan maupun validasi menurun secara stabil. Akurasi token rata-rata juga meningkat dari waktu ke waktu, yang mengindikasikan bahwa model berhasil belajar menghasilkan output pemanggilan fungsi yang lebih akurat dan konsisten.

Menerbitkan Model FunctionGemma yang Telah Di–Fine-Tune ke Hugging Face

Setelah fine-tuning selesai, kita dapat menerbitkan model agar dapat digunakan kembali, dibagikan, atau dideploy dengan mudah. Mendorong model ke Hugging Face Hub memungkinkan orang lain memuatnya langsung menggunakan API Transformers standar.

HF_REPO_ID = "kingabzpro/functiongemma-hermes-3k-ft"
model.push_to_hub(HF_REPO_ID)
processor.push_to_hub(HF_REPO_ID)
print("Pushed to:", HF_REPO_ID)

Setelah unggahan selesai, model yang telah di–fine-tune tersedia publik di Hugging Face:

Pushed to: kingabzpro/functiongemma-hermes-3k-ft

Model yang telah di–fine-tune dipublikasikan di Hugging Face Hub.

Sumber: kingabzpro/functiongemma-hermes-3k-ft · Hugging Face

Evaluasi Pasca Fine-Tuning dan Analisis Kesalahan

Setelah fine-tuning selesai, kita memuat ulang model dan processor yang disimpan dari disk dan menjalankan pipeline evaluasi yang sama seperti sebelum pelatihan. Ini memastikan perbandingan antara kinerja pra- dan pasca–fine-tuning adil dan konsisten.

from transformers import AutoProcessor, AutoModelForCausalLM

ft_processor = AutoProcessor.from_pretrained(OUT_DIR, device_map="auto")
ft_model = AutoModelForCausalLM.from_pretrained(OUT_DIR, dtype="auto", device_map="auto")

Kemudian kita mengevaluasi model yang telah di–fine-tune pada set evaluasi menggunakan akurasi nama tool dan TC-CER.

post_metrics = eval_tool_and_cer(ft_processor, ft_model, simple_eval, n=50, max_new_tokens=64)
print("POST metrics:", post_metrics)

Hasilnya menunjukkan peningkatan yang jelas dibanding baseline:

POST metrics: {'n_eval': 50, 'tool_accuracy': 0.98, 'TC-CER (lower is better)': 0.1454725383473528}

Akurasi nama tool meningkat dari 88 persen menjadi 98 persen, dan tingkat kesalahan karakter untuk blok pemanggilan fungsi penuh berkurang lebih dari setengah. Ini menunjukkan bahwa model tidak hanya lebih andal dalam memilih tool yang benar, tetapi juga menghasilkan output yang lebih mendekati skema target.

Untuk lebih memahami peningkatan tersebut, kita memeriksa prediksi individual. Pada contoh di bawah, model memilih fungsi yang benar dan menghasilkan panggilan yang terstruktur dengan argumen yang lebih lengkap.

post = infer_one(ft_processor, ft_model, simple_eval, idx=15)

print(f"""
TASK:
{post['task']}

TOOL MATCH: {pre['tool_match']}

--- PREDICTED ---
{post['predicted']}

--- GOLD ---
{post['gold']}
""")

Walaupun panggilan yang diprediksi mungkin menyertakan argumen tambahan yang melampaui target gold, struktur keseluruhan dan pemformatan argumen jauh lebih konsisten dibanding sebelum fine-tuning. Ini mencerminkan meningkatnya kesadaran skema dan penyelarasan yang lebih kuat antara maksud pengguna dan panggilan fungsi yang dihasilkan.

TASK:
I'm reviewing the schedule in the surgical unit. Can you fetch the surgical nursing details for patient ID 12345 undergoing a 'Cardiac Bypass' today? If the procedure is handled by Nurse Ratched, let's record a post-operative care task for this evening.

TOOL MATCH: True

--- PREDICTED ---
<start_function_call>call:get_surgical_nursing_information{args:<escape>{"patient_id": "12345", "procedure_type": "Cardiac Bypass", "nurse_id": "Ratched", "task": "Post-operative care"}<escape>}<end_function_call>

--- GOLD ---
<start_function_call>call:get_surgical_nursing_information{args:<escape>{"patient_id": "12345", "procedure_type": "Cardiac Bypass"}<escape>}<end_function_call>

Mari telusuri sampel lain.

post = infer_one(ft_processor, ft_model, simple_eval, idx=25)

print(f"""
TASK:
{post['task']}

TOOL MATCH: {pre['tool_match']}

--- PREDICTED ---
{post['predicted']}

--- GOLD ---
{post['gold']}
""")

Contoh kedua menunjukkan panggilan fungsi yang bersih dan benar untuk tugas ekstraksi URL, menunjukkan bahwa model yang di–fine-tune melakukan generalisasi dengan baik di berbagai tool.

TASK:
I would like to extract details from a LinkedIn company page. Could you assist me in fetching the information from this URL: https://www.linkedin.com/company/abc-corporation?

TOOL MATCH: True

--- PREDICTED ---
<start_function_call>call:extract{args:<escape>{"url": "https://www.linkedin.com/company/abc-corporation"}<escape>}<end_function_call>

--- GOLD ---
<start_function_call>call:extract{args:<escape>{"url": "https://www.linkedin.com/company/abc-corporation", "html": true}<escape>}<end_function_call>

Secara keseluruhan, hasil ini mengonfirmasi bahwa fine-tuning berhasil meningkatkan akurasi dan ketepatan struktural untuk tugas pemanggilan fungsi.

Notebook Kaggle lengkap untuk tutorial ini tersedia di:https://www.kaggle.com/code/kingabzpro/finetuning-functiongemma

Jika Anda mengalami masalah, Anda dapat mengklon notebook, menambahkan secret Hugging Face Anda, dan menjalankannya dari awal hingga akhir.

Penutup

Jika Anda mencoba melatih FunctionGemma pada dataset generasi teks generik, sebaiknya hentikan lebih awal. FunctionGemma tidak dirancang untuk berperilaku seperti model bahasa serbaguna.

Untuk generasi teks terbuka, keluarga model Gemma 3 adalah pilihan yang lebih baik.

FunctionGemma dibuat khusus untuk pemanggilan fungsi, dengan tujuan memilih tool yang benar, menghasilkan argumen yang sesuai skema, dan berinteraksi secara andal dengan sistem eksternal seperti API, basis data, dan layanan.

Ukuran FunctionGemma yang kecil adalah pilihan desain yang disengaja. Dengan 270 juta parameter, model ini dioptimalkan untuk deployment berlatensi rendah dan hemat sumber daya, termasuk mesin lokal, perangkat edge, dan infrastruktur privat. Ini membuatnya sangat cocok untuk pemanggilan fungsi waktu nyata dan alur kerja agen, di mana ketepatan dan struktur lebih penting daripada prosa yang fasih.

Dalam tutorial ini, kita mempelajari cara melakukan fine-tuning FunctionGemma pada dataset Hermes Reasoning Tool-Use. Pelajaran pentingnya adalah bahwa penyiapan data dan evaluasi lebih penting daripada waktu pelatihan mentah.

Dengan membersihkan dataset secara cermat, menyelaraskan tugas dengan panggilan tool gold, dan menegakkan konsistensi skema yang ketat, kita dapat secara drastis meningkatkan perilaku model dengan jumlah sampel berkualitas tinggi yang relatif kecil.

Hasilnya jelas menunjukkan dampak fine-tuning. Akurasi nama tool meningkat dari 88 persen menjadi 98 persen, dan tingkat kesalahan karakter untuk blok pemanggilan fungsi penuh berkurang lebih dari setengah. Yang lebih penting, model menjadi jauh lebih konsisten dalam menghasilkan panggilan fungsi yang terstruktur dengan baik dan dapat diprediksi yang selaras dengan penggunaan dunia nyata.

FunctionGemma FAQs

Apa itu FunctionGemma dan apa bedanya dengan Gemma 3?

FunctionGemma adalah versi khusus dari open model Gemma 3 270M. Sementara Gemma 3 standar dirancang untuk generasi teks, FunctionGemma secara eksplisit dilatih untuk menerjemahkan bahasa natural menjadi pemanggilan fungsi terstruktur (permintaan API), menjadikannya "router" khusus untuk alur kerja agen alih-alih chatbot percakapan.

hat hardware do I need to run FunctionGemma?

Karena merupakan model dengan 270M parameter, FunctionGemma sangat ringan (~300MB setelah quantized). Model ini dapat berjalan efisien pada perangkat edge seperti laptop, ponsel, dan perangkat tertanam (misalnya NVIDIA Jetson Nano) tanpa memerlukan GPU cloud khusus.

Mengapa saya perlu melakukan fine-tune FunctionGemma jika model ini sudah terspesialisasi?

Walaupun model dasar memahami konsep penggunaan tool, fine-tuning diperlukan untuk mengajarkannya skema API dan logika bisnis spesifik Anda. Benchmark Google menunjukkan bahwa fine-tuning dapat meningkatkan akurasi pemanggilan fungsi dari ~58% (dasar) menjadi lebih dari 85% pada tugas spesifik domain.

Apakah FunctionGemma dapat menangani percakapan multi-giliran?

FunctionGemma dioptimalkan terutama untuk eksekusi perintah satu giliran (Instruksi Pengguna → Panggilan Tool). Untuk penalaran multi-giliran yang kompleks atau mempertahankan riwayat percakapan panjang, model ini paling baik digunakan sebagai "pengatur lalu lintas" yang mendelegasikan kueri sulit ke model yang lebih besar (seperti Gemma 3 27B) sambil menangani tindakan sederhana secara lokal.

Abid Ali Awan

Penulis

Abid Ali Awan

Sebagai data scientist tersertifikasi, saya bersemangat memanfaatkan teknologi mutakhir untuk menciptakan aplikasi machine learning yang inovatif. Dengan latar belakang kuat di pengenalan ucapan, analisis dan pelaporan data, MLOps, conversational AI, dan NLP, saya mengasah keterampilan dalam mengembangkan sistem cerdas yang berdampak nyata. Selain keahlian teknis, saya juga komunikator andal yang mampu menyederhanakan konsep kompleks menjadi bahasa yang jelas dan ringkas. Karena itu, saya menjadi blogger yang dicari di bidang data science, membagikan wawasan dan pengalaman kepada komunitas profesional data yang terus berkembang. Saat ini, saya berfokus pada pembuatan dan penyuntingan konten, bekerja dengan large language model untuk mengembangkan konten yang kuat dan menarik agar membantu bisnis dan individu memaksimalkan data mereka.

Topik

Terkait

blogs

Tutorial Korelasi di R

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026