Model pada Chapter sebelumnya hanya punya satu parameter yang kita perbarui dengan tangan lewat assign_sub. Model nyata memuat ratusan hingga jutaan parameter yang tersusun dari layer, dan menulis loop manual untuk semuanya membuat kode cepat membesar tanpa perlu. Keras menyediakan dua gaya penyusunan model — Sequential untuk tumpukan lurus dan Functional untuk graf dengan percabangan — dan keduanya menghasilkan objek tf.keras.Model dengan antarmuka latih yang sama. Chapter ini membangun model pertama kita dengan kedua gaya tersebut, membaca model.summary(), menghitung jumlah parameter tiap layer Dense, lalu menjalankan forward pass dengan predict pada data mainan yang diuji nyata.

Sequential API: Tumpukan Layer #

Sequential adalah gaya paling ringkas: kita mendaftar layer dari bawah ke atas, dan data mengalir lurus mengikuti urutan itu. tf.keras.Input(shape=(2,)) mendeklarasikan bahwa setiap contoh data punya dua fitur — sumbu batch tidak ditulis karena ukurannya bebas. Setiap Dense menyimpan matriks bobot dan vektor bias yang bentuknya ditentukan dari bentuk masukan dan jumlah unit. Berkas model_sequential.py membangun jaringan kecil 2 → 4 → 1 lalu memeriksanya:

python
"""Model Sequential: susun layer Dense, baca summary, dan jalankan predict."""

import os

os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

import numpy as np
import tensorflow as tf

tf.keras.utils.set_random_seed(42)

model = tf.keras.Sequential(
    [
        tf.keras.Input(shape=(2,), name="masukan"),
        tf.keras.layers.Dense(4, activation="relu", name="sembunyi"),
        tf.keras.layers.Dense(1, name="keluaran"),
    ]
)
model.summary()

print()
print("Parameter per layer:")
for layer in model.layers:
    bobot, bias = layer.get_weights()
    print(f"  {layer.name:9s}: bobot {bobot.shape} + bias {bias.shape} = {bobot.size + bias.size} parameter")
print("model.count_params() :", model.count_params())

X = np.array([[0.0, 0.0],
              [1.0, 0.0],
              [0.0, 1.0],
              [1.0, 1.0]], dtype=np.float32)
y_pred = model.predict(X, verbose=0)
print()
print("Prediksi model (belum dilatih):")
print(np.round(y_pred, 4))

model_tunda = tf.keras.Sequential([tf.keras.layers.Dense(1)])
print()
print("Sebelum build -> jumlah tensor bobot:", len(model_tunda.weights))
model_tunda.build(input_shape=(None, 2))
print("Setelah build(input_shape=(None, 2)):", len(model_tunda.weights))

Output yang Dihasilkan:

text
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ sembunyi (Dense)                │ (None, 4)              │            12 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ keluaran (Dense)                │ (None, 1)              │             5 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 17 (68.00 B)
 Trainable params: 17 (68.00 B)
 Non-trainable params: 0 (0.00 B)

Parameter per layer:
  sembunyi : bobot (2, 4) + bias (4,) = 12 parameter
  keluaran : bobot (4, 1) + bias (1,) = 5 parameter
model.count_params() : 17

Prediksi model (belum dilatih):
[[0.    ]
 [1.2181]
 [0.5366]
 [0.4845]]

Sebelum build -> jumlah tensor bobot: 0
Setelah build(input_shape=(None, 2)): 2

Ringkasan model menunjukkan arsitektur 2 → 4 → 1 dengan 17 parameter — angka yang bisa kita hitung ulang dari rumus layer Dense: bobot berbentuk (masukan × unit) ditambah bias sebanyak unit. Prediksi empat baris data juga sudah bisa dijalankan meskipun model belum pernah dilatih, karena predict hanya melakukan forward pass — kompilasi loss dan optimizer belum diperlukan pada tahap ini.

Mari kita telaah kode di atas baris per baris:

  1. Baris 10 (tf.keras.utils.set_random_seed(42)): menyetel seed Python, NumPy, TensorFlow, dan Keras sekaligus sehingga inisialisasi bobot serta hasil predict sama pada setiap eksekusi.
  2. Baris 12-18 (tf.keras.Sequential([...])): tiga anggota daftar — Input dua fitur, Dense empat unit dengan aktivasi ReLU, dan Dense satu unit tanpa aktivasi (keluaran linear, standar untuk regresi).
  3. Baris 19 (model.summary()): mencetak tabel layer beserta shape keluaran (None, 4) dan (None, 1); None di posisi pertama berarti ukuran batch bebas, dan kolom Param # menghitung bobot plus bias tiap layer.
  4. Baris 23-26 (get_weights dan count_params): tiap Dense menyimpan dua tensor — bobot (2, 4) dan bias (4,) pada layer tersembunyi, (4, 1) dan (1,) pada layer keluaran. Hitungan manualnya: 2×4 + 4 = 12 dan 4×1 + 1 = 5, total 17 — cocok dengan model.count_params().
  5. Baris 28-35 (model.predict): mengalirkan empat contoh data melalui jaringan. Nilai keluaran seperti 1.2181 dan 0.5366 belum bermakna — bobotnya masih acak — tetapi bentuk mekanismenya sudah lengkap.
  6. Baris 37-41 (model_tunda): model tanpa Input tidak punya bobot sama sekali (0 tensor) sampai build(input_shape=(None, 2)) dipanggil; setelah itu kernel dan bias tercipta (2 tensor). Inilah sebabnya summary kadang tidak bisa menampilkan apa pun sebelum bentuk masukan diketahui.

Functional API: Graf Eksplisit #

Gaya Functional membangun model seperti merangkai kalimat: kita membuat tensor masukan, memanggil layer atas tensor itu untuk menghasilkan tensor baru, lalu membungkus ujung ke ujungnya menjadi satu Model. Kekuatan gaya ini baru terasa ketika graf bercabang — beberapa masukan, beberapa keluaran, atau sambungan lompatan yang akan kita jumpai pada section CNN. Alur bentuk data pada model di bawah ini dapat dilukiskan sebagai:

Berkas model_functional.py membangun graf tersebut — klasifikasi tiga kelas dengan keluaran softmax:

python
"""Functional API: susun graf layer eksplisit lalu bungkus jadi Model."""

import os

os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

import numpy as np
import tensorflow as tf

tf.keras.utils.set_random_seed(42)

masukan = tf.keras.Input(shape=(2,), name="masukan")
sembunyi = tf.keras.layers.Dense(8, activation="relu", name="sembunyi")(masukan)
keluaran = tf.keras.layers.Dense(3, activation="softmax", name="probabilitas")(sembunyi)

model = tf.keras.Model(inputs=masukan, outputs=keluaran, name="klasifikasi_mini")
model.summary()

X = np.array([[0.5, -1.0],
              [-2.0, 0.3],
              [1.5, 1.5]], dtype=np.float32)
y_pred = model.predict(X, verbose=0)
print()
print("Probabilitas tiga kelas (belum dilatih):")
print(np.round(y_pred, 4))
print("Jumlah tiap baris:", np.round(y_pred.sum(axis=1), 4))

Output yang Dihasilkan:

text
Model: "klasifikasi_mini"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ masukan (InputLayer)            │ (None, 2)              │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ sembunyi (Dense)                │ (None, 8)              │            24 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ probabilitas (Dense)            │ (None, 3)              │            27 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 51 (204.00 B)
 Trainable params: 51 (204.00 B)
 Non-trainable params: 0 (0.00 B)

Probabilitas tiga kelas (belum dilatih):
[[0.3168 0.3299 0.3534]
 [0.4793 0.2363 0.2844]
 [0.2186 0.3055 0.4758]]
Jumlah tiap baris: [1. 1. 1.]

Mari kita telaah kode di atas baris per baris:

  1. Baris 12 (masukan = tf.keras.Input(shape=(2,))): membuat tensor simbolik yang menjadi titik masuk graf; belum ada data sungguhan yang mengalir.
  2. Baris 13-14 (pemanggilan layer): Dense(8, activation="relu")(masukan) memanggil layer atas tensor masukan dan mengembalikan tensor keluaran baru — pola layer(tensor) inilah tanda tangan Functional API. Tensor itu langsung dipakai layer berikutnya hingga menghasilkan keluaran berukuran tiga dengan softmax.
  3. Baris 16 (tf.keras.Model(...)): membungkus jalur masukan → keluaran menjadi model utuh; argumen name="klasifikasi_mini" muncul sebagai judul pada summary.
  4. Baris 17 (model.summary()): berbeda dari Sequential, ringkasan menampilkan baris InputLayer eksplisit dengan nol parameter. Hitungan parameternya: 2×8 + 8 = 24 dan 8×3 + 3 = 27, total 51.
  5. Baris 19-26 (predict): tiga contoh data menghasilkan matriks probabilitas 3×3. Baris terakhir memverifikasi sifat softmax — jumlah probabilitas tiap baris tepat 1.0, sehingga ketiga kelas saling melengkapi.

Kapan Memakai Gaya yang Mana #

Ketiga gaya menghasilkan tf.keras.Model yang sama-sama bisa di-compile, di-fit, dan di-predict; pilihan gaya urusan bentuk graf dan kenyamanan, bukan kemampuan. Tabel berikut menjadi panduan singkat:

GayaCocok untukContoh pemakaian
Sequentialtumpukan lurus tanpa cabangMLP regresi dan klasifikasi dasar
Functionalgraf bercabang, multi masukan/keluaranmodel dua masukan, sambungan lompatan CNN
Subclassingperilaku forward yang sepenuhnya kustomarsitektur riset dengan logika khusus

Gaya Subclassing — mewarisi tf.keras.Model dan menulis metode call() sendiri — kita bahas lebih dalam ketika tiba waktunya menulis komponen kustom. Dokumentasi resmi ketiga gaya tersedia di tensorflow.org/guide/keras , dan rincian layer Dense di tf.keras.layers.Dense .

Ringkasan #

Kita telah menyusun model dua gaya: Sequential membangun tumpukan 2 → 4 → 1 dengan 17 parameter yang jumlahnya bisa diverifikasi manual, sementara Functional merangkai graf 2 → 8 → 3 dengan 51 parameter dan keluaran softmax. model.summary(), get_weights(), count_params(), dan build(input_shape=...) memberi kita kaca pembesar untuk membaca isi model sebelum dilatih. Namun model-model ini masih belum tahu cara belajar: prediksinya acak karena tidak ada ukuran kesalahan dan aturan pembaruan bobot. Kedua bahan itulah — loss dan optimizer — yang menjadi topik Chapter berikutnya, disatukan lewat model.compile().