DEV Community

Cover image for Cara Membuat LLM dari Awal: Panduan Lengkap & Pembelajaran Penting
Walse
Walse

Posted on • Originally published at apidog.com

Cara Membuat LLM dari Awal: Panduan Lengkap & Pembelajaran Penting

Intisari

Membangun model bahasa minimal dari awal hanya membutuhkan kurang dari 300 baris kode Python. Proses ini mengungkap dengan tepat bagaimana tokenisasi, perhatian, dan inferensi bekerja, yang membuat Anda menjadi konsumen API yang jauh lebih baik saat mengintegrasikan LLM produksi ke dalam aplikasi Anda.

Coba Apidog hari ini

Pendahuluan

Sebagian besar pengembang memperlakukan model bahasa sebagai kotak hitam. Anda mengirim teks masuk, token keluar, dan di antara itu semua, keajaiban terjadi. Model mental itu berfungsi dengan baik sampai Anda perlu men-debug integrasi API yang rusak, menyetel parameter pengambilan sampel, atau mencari tahu mengapa model Anda terus berhalusinasi data terstruktur.

GuppyLM, sebuah proyek yang baru-baru ini mencapai halaman depan HackerNews dengan 842 poin, membuat internalnya terlihat. Ini adalah transformer berparameter 8.7M yang ditulis dari awal dalam Python. Model ini dilatih dalam waktu kurang dari satu jam pada GPU konsumen. Kodenya muat dalam satu file. Tujuannya bukan untuk bersaing dengan GPT-4; tujuannya adalah untuk mengungkap apa sebenarnya yang dilakukan LLM.

Artikel ini menjelaskan langkah demi langkah membangun LLM kecil, komponennya, dan apa yang sebaiknya Anda pahami saat bekerja dengan API AI secara profesional.

💡 Jika Anda menguji integrasi API AI, Skenario Uji Apidog memungkinkan Anda memverifikasi respons streaming, menegaskan struktur token, dan mensimulasikan penyelesaian kasus ekstrem tanpa membakar kredit produksi. Lebih lanjut tentang itu nanti

Apa yang membuat model bahasa "kecil"?

LLM produksi seperti GPT-4 memiliki ratusan miliar parameter. LLM "kecil" berada di kisaran 1M hingga 25M parameter. Contohnya: GuppyLM (8.7M), nanoGPT Karpathy (124M), MicroLM (1-2M).

LLM kecil dapat:

  • Dilatih di laptop atau Google Colab
  • Muat sepenuhnya di memori CPU
  • Diinspeksi, dimodifikasi, di-debug hingga level bobot

LLM kecil tidak dapat:

  • Menangani penalaran kompleks
  • Menghasilkan teks panjang yang koheren secara andal
  • Menyamai kedalaman faktual model produksi

Nilai utamanya adalah pemahaman mendalam ketika Anda membangunnya sendiri.

Komponen inti: bagaimana LLM sebenarnya bekerja

Sebelum coding, pahami 4 bagian utama ini:

Tokenisasi

Tokenizer mengubah teks mentah menjadi ID bilangan bulat. Contoh: "Hello, world!" → [15496, 11, 995, 0]. Setiap bilangan bulat adalah subkata dari kosakata tetap.

Impak ke API: Jumlah token langsung memengaruhi latensi dan biaya. Anda perlu tahu bagaimana tokenizer memecah teks agar prompt Anda selalu muat dalam jendela konteks.

  • GuppyLM: tokenizer karakter sederhana.
  • Model produksi (GPT-4, dsb): BPE (byte-pair encoding), kosakata 50K–100K token.

Lapisan embedding

Lapisan embedding mengubah ID token menjadi vektor padat (misal 384 dimensi di GuppyLM). Vektor ini mengandung makna semantik — token serupa akan berdekatan di ruang vektor.

Positional embedding ditambahkan agar model tahu urutan token.

Blok Transformer

Bagian komputasi utama terdiri dari dua bagian:

  • Self-attention: setiap token melihat semua token lain dalam urutan, memilih mana yang penting, dan memprediksi token berikutnya. GuppyLM memakai 6 head pada 6 layer.
  • Feed-forward network: MLP dua lapis diterapkan pada representasi token setelah attention. GuppyLM memakai ReLU, lebih sederhana dari SwiGLU pada arsitektur baru.

Kepala output

Setelah blok transformer terakhir, layer linear memproyeksikan token ke vektor sebesar kosakata. Softmax diterapkan untuk probabilitas, token berikutnya dipilih (atau sampling), lalu diulang.

Membangun LLM minimal dalam Python

Berikut LLM minimal berbasis GuppyLM. Cukup gunakan PyTorch standar.

import torch
import torch.nn as nn
import torch.nn.functional as F

# Hyperparameters
VOCAB_SIZE = 256     # karakter: satu slot per ASCII
D_MODEL = 128        # dimensi embedding
N_HEADS = 4          # attention heads
N_LAYERS = 3         # transformer blocks
SEQ_LEN = 64         # context window
DROPOUT = 0.1

class SelfAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        self.n_heads = n_heads
        self.head_dim = d_model // n_heads
        self.qkv = nn.Linear(d_model, 3 * d_model, bias=False)
        self.proj = nn.Linear(d_model, d_model, bias=False)
        self.dropout = nn.Dropout(DROPOUT)

    def forward(self, x):
        B, T, C = x.shape
        qkv = self.qkv(x).reshape(B, T, 3, self.n_heads, self.head_dim)
        q, k, v = qkv.unbind(dim=2)
        q = q.transpose(1, 2)
        k = k.transpose(1, 2)
        v = v.transpose(1, 2)
        # Mask kausal: token hanya boleh melihat token sebelumnya
        scale = self.head_dim ** -0.5
        attn = (q @ k.transpose(-2, -1)) * scale
        mask = torch.triu(torch.ones(T, T, device=x.device), diagonal=1).bool()
        attn = attn.masked_fill(mask, float('-inf'))
        attn = F.softmax(attn, dim=-1)
        attn = self.dropout(attn)
        out = (attn @ v).transpose(1, 2).reshape(B, T, C)
        return self.proj(out)

class TransformerBlock(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        self.attn = SelfAttention(d_model, n_heads)
        self.ff = nn.Sequential(
            nn.Linear(d_model, 4 * d_model),
            nn.ReLU(),
            nn.Linear(4 * d_model, d_model),
            nn.Dropout(DROPOUT),
        )
        self.ln1 = nn.LayerNorm(d_model)
        self.ln2 = nn.LayerNorm(d_model)

    def forward(self, x):
        x = x + self.attn(self.ln1(x))
        x = x + self.ff(self.ln2(x))
        return x

class TinyLLM(nn.Module):
    def __init__(self):
        super().__init__()
        self.embed = nn.Embedding(VOCAB_SIZE, D_MODEL)
        self.pos_embed = nn.Embedding(SEQ_LEN, D_MODEL)
        self.blocks = nn.ModuleList([
            TransformerBlock(D_MODEL, N_HEADS) for _ in range(N_LAYERS)
        ])
        self.ln_f = nn.LayerNorm(D_MODEL)
        self.head = nn.Linear(D_MODEL, VOCAB_SIZE, bias=False)

    def forward(self, idx):
        B, T = idx.shape
        tok_emb = self.embed(idx)
        pos = torch.arange(T, device=idx.device)
        pos_emb = self.pos_embed(pos)
        x = tok_emb + pos_emb
        for block in self.blocks:
            x = block(x)
        x = self.ln_f(x)
        logits = self.head(x)
        return logits

# Inisialisasi dan hitung parameter
model = TinyLLM()
total_params = sum(p.numel() for p in model.parameters())
print(f"Model size: {total_params:,} parameters")  # ~1.2M
Enter fullscreen mode Exit fullscreen mode

Loop pelatihan

import torch.optim as optim

def train(model, data, epochs=100, lr=3e-4):
    optimizer = optim.AdamW(model.parameters(), lr=lr)
    model.train()
    for epoch in range(epochs):
        # data: tensor ID token, shape [batch, seq_len+1]
        x = data[:, :-1]   # input: semua token kecuali terakhir
        y = data[:, 1:]    # target: semua token digeser 1
        logits = model(x)
        loss = F.cross_entropy(logits.reshape(-1, VOCAB_SIZE), y.reshape(-1))
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if epoch % 10 == 0:
            print(f"Epoch {epoch}, loss: {loss.item():.4f}")
Enter fullscreen mode Exit fullscreen mode

Inferensi (pembuatan teks)

@torch.no_grad()
def generate(model, prompt_ids, max_new_tokens=50, temperature=1.0, top_k=10):
    model.eval()
    ids = torch.tensor([prompt_ids])
    for _ in range(max_new_tokens):
        idx_cond = ids[:, -SEQ_LEN:]  # potong ke context window
        logits = model(idx_cond)
        logits = logits[:, -1, :] / temperature  # hanya token terakhir
        # top-k sampling
        v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
        logits[logits < v[:, [-1]]] = float('-inf')
        probs = F.softmax(logits, dim=-1)
        next_id = torch.multinomial(probs, num_samples=1)
        ids = torch.cat([ids, next_id], dim=1)
    return ids[0].tolist()
Enter fullscreen mode Exit fullscreen mode

Apa yang diajarkan ini tentang perilaku API AI

Beberapa insight praktis untuk konsumen API setelah membangun LLM sendiri:

Suhu dan sampling bersifat mekanis, bukan magis

suhu membagi logit sebelum softmax. Suhu tinggi = distribusi datar = output acak. Suhu rendah = distribusi tajam = output deterministik. Jika API Anda mengembalikan hasil tidak konsisten pada temperature=0.0, itu bukan bug. Banyak API menurunkan sedikit suhu nol untuk menghindari output yang membosankan.

Jendela konteks adalah batasan keras

Baris idx_cond = ids[:, -SEQ_LEN:] di loop inferensi memperlihatkan: token lama otomatis dihapus. Jangan anggap model mengingat riwayat penuh. Untuk solusi, cek [internal: how-ai-agent-memory-works].

Token streaming = iterasi loop inferensi

API streaming hanya menjalankan loop inferensi dan mengirim token satu per satu. Jika sambungan streaming terputus, tidak bisa dilanjutkan — harus dimulai ulang.

Logit menjelaskan mengapa output terstruktur sulit

Setiap langkah, model menetapkan probabilitas pada semua token. Membuat JSON valid berarti setiap token harus benar. Library seperti Outlines dan Guidance membatasi distribusi logit agar output sesuai tata bahasa. Mode "output terstruktur" di API AI bekerja seperti ini.

Cara menguji integrasi API AI dengan Apidog

Dengan memahami inferensi LLM, Anda dapat membuat pengujian API yang jauh lebih baik. Skenario Uji Apidog memungkinkan Anda merangkai panggilan API dan menegaskan struktur respons AI.

Contoh pengujian API chat streaming:

  1. Buat Skenario Uji di Apidog dengan endpoint /v1/chat/completions Anda.
  2. Tambahkan assertion:
    • response.choices[0].finish_reason == "stop"
    • response.usage.total_tokens < 4096
  3. Tambah langkah untuk mengirim respons sebagai konteks ke giliran berikutnya (simulasi percakapan multi-turn).
  4. Gunakan Smart Mock Apidog untuk endpoint AI dan pengujian penanganan error: Simulasikan finish_reason: "length", finish_reason: "content_filter", dan timeout jaringan di tengah stream.

Dengan cara ini, Anda bisa menguji integrasi AI tanpa membakar kredit API pada setiap run CI. Lihat [internal: api-testing-tutorial] untuk gambaran lebih luas pendekatan pengujian API.

Menguji assertion jumlah token

{
  "assertions": [
    {
      "field": "response.usage.completion_tokens",
      "operator": "less_than",
      "value": 512
    },
    {
      "field": "response.choices[0].finish_reason",
      "operator": "equals",
      "value": "stop"
    },
    {
      "field": "response.choices[0].message.content",
      "operator": "not_empty"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Jalankan assertion ini di beberapa model (GPT-4o, Claude 3.5 Sonnet, Gemini 1.5 Pro) dalam satu skenario untuk menangkap perbedaan skema API sebelum produksi.

Lanjutan: kuantisasi dan optimisasi inferensi

Setelah punya LLM kecil yang berjalan, dua teknik berikut sangat relevan untuk deployment model produksi.

Kuantisasi

Bobot model default-nya float 32 bit. Kuantisasi menurunkan ke INT8 atau INT4, mengurangi penggunaan memori 4–8x dengan sedikit penurunan akurasi.

# Contoh: dynamic INT8 quantization di PyTorch
import torch.quantization
quantized_model = torch.quantization.quantize_dynamic(
    model, {nn.Linear}, dtype=torch.qint8
)
Enter fullscreen mode Exit fullscreen mode

Model produksi biasanya sudah dikuantisasi. Jika Anda lihat perbedaan kualitas pada "versi" model yang sama, kuantisasi sering jadi penyebab.

Cache KV

Dalam inferensi di atas, perhatian dihitung ulang setiap langkah. Sistem produksi menyimpan cache pasangan key-value (KV) token sebelumnya, sehingga setiap token baru hanya perlu satu komputasi attention. Itulah mengapa token pertama pada streaming lebih lambat dari berikutnya.

LLM kecil vs. API produksi: kapan menggunakan masing-masing

Kasus Penggunaan LLM Kecil API Produksi
Mempelajari internal model Terbaik untuk Berlebihan
Membuat prototipe aplikasi baru Kualitas tidak memadai Terbaik untuk
Data pribadi/sensitif Pilihan bagus Tergantung penyedia
Penyebaran offline/edge Layak Tidak mungkin
Sensitif biaya, volume tinggi Mungkin dengan kompromi Mahal pada skala besar
Tugas yang membutuhkan penalaran berat Tidak layak Diperlukan

Sebagian besar pengembang sebaiknya: gunakan API produksi untuk aplikasi Anda, tapi jalankan model kecil untuk memahami mekanisme internal. Keduanya saling melengkapi. Lihat artikel [internal: open-source-coding-assistants-2026] untuk alat dengan mode "bring-your-own-model".

Kesimpulan

Membangun LLM kecil dari awal bisa dilakukan dalam satu akhir pekan. Anda tidak akan mendapat sistem produksi, tapi Anda akan punya model mental yang nyata tentang cara kerja model bahasa — dari GuppyLM hingga GPT-4o. Pemahaman ini sangat berguna saat debugging integrasi streaming, menyetel parameter sampling, atau merancang assertion pengujian API AI.

Proyek GuppyLM sangat direkomendasikan sebagai titik awal. Kloning, latih dengan dataset teks apa pun, dan pelajari loop inferensinya. Setelah itu, integrasi API produksi Anda akan terasa jauh lebih transparan.

Coba Skenario Uji Apidog dan bawa ketelitian pengujian API AI ke level backend production.

FAQ

Berapa banyak parameter yang dibutuhkan LLM "kecil" untuk menghasilkan teks koheren?

Sekitar 10M–50M parameter dengan dataset yang cukup bisa menghasilkan kalimat koheren secara lokal. Di bawah 1M, hasilnya cenderung tidak masuk akal. GuppyLM (8.7M) bisa untuk percakapan singkat di domain pelatihannya (60 topik).

Bisakah saya menjalankan LLM kecil tanpa GPU?

Bisa. Model di bawah 100M parameter berjalan baik di CPU, walaupun lebih lambat. Model contoh di atas (1.2M parameter) menghasilkan token dalam milidetik di laptop.

Dataset apa yang sebaiknya digunakan untuk pelatihan?

Model tingkat karakter cocok untuk teks Project Gutenberg, subset Wikipedia, atau korpus teks biasa. GuppyLM memakai dataset percakapan 60K entri di HuggingFace (arman-bd/guppylm-60k-generic). Untuk pembuatan kode: The Stack atau CodeParrot.

Perbedaan suhu vs. top-k sampling?

Suhu mengubah distribusi logit (mengontrol keacakan). Top-k membatasi pilihan sampling pada k token paling mungkin sebelum suhu diterapkan. Biasanya, top-k diterapkan dulu, baru suhu.

Mengapa LLM saya kadang mengulang output?

Pengulangan adalah mode kegagalan di mana model menetapkan probabilitas tinggi pada token yang baru dibuat. API produksi memakai penalti pengulangan (repetition_penalty=1.1) untuk mengurangi ini.

Berapa lama waktu pelatihan LLM kecil?

Model di atas bisa dilatih hingga output koheren dalam waktu kurang dari 2 jam di satu GPU (RTX 3060 atau setara). GuppyLM dilatih di Colab dalam waktu serupa. Model >100M butuh multi-GPU dan waktu lebih lama.

Cara tercepat beralih dari LLM kecil ke endpoint API nyata?

Ekspor ke format GGUF (skrip konversi llama.cpp), lalu sajikan dengan llama-server — endpoint API kompatibel OpenAI secara lokal. Apidog bisa diarahkan ke sini untuk testing, lihat [internal: rest-api-best-practices].

Bagaimana LLM produksi menangani konteks lebih panjang dari jendela pelatihan?

Teknik seperti RoPE (Rotary Position Embedding) dengan penskalaan, sliding window attention, dan retrieval-augmented generation memperluas konteks efektif. Inti arsitektur transformer tetap, hanya encoding posisi dan window attention yang dimodifikasi.

Top comments (0)