diff --git a/chapters/id/_toctree.yml b/chapters/id/_toctree.yml
index 09eb7b0e6..f4c48c2f3 100644
--- a/chapters/id/_toctree.yml
+++ b/chapters/id/_toctree.yml
@@ -3,9 +3,130 @@
- local: chapter0/1
title: Pendahuluan
-- title: 1. Model-model Transformer
+- title: 1. Model Transformer
sections:
- local: chapter1/1
title: Pendahuluan
- local: chapter1/2
- title: Pemrosesan Bahasa Natural
+ title: Pemrosesan Bahasa Alami dan Model Bahasa Besar
+ - local: chapter1/3
+ title: Transformer, apa yang bisa mereka lakukan?
+ - local: chapter1/4
+ title: Bagaimana Transformer bekerja?
+ - local: chapter1/5
+ title: Bagaimana 🤗 Transformer menyelesaikan tugas
+ - local: chapter1/6
+ title: Arsitektur Transformer
+ - local: chapter1/7
+ title: Kuis cepat
+ - local: chapter1/8
+ title: Inferensi dengan LLM
+ - local: chapter1/9
+ title: Bias dan keterbatasan
+ - local: chapter1/10
+ title: Ringkasan
+ - local: chapter1/11
+ title: Ujian sertifikasi
+ quiz: 1
+
+- title: 2. Menggunakan 🤗 Transformers
+ sections:
+ - local: chapter2/1
+ title: Pengantar
+ - local: chapter2/2
+ title: Di balik pipeline
+ - local: chapter2/3
+ title: Model
+ - local: chapter2/4
+ title: Tokenizer
+ - local: chapter2/5
+ title: Menangani banyak sekuens
+ - local: chapter2/6
+ title: Menggabungkan semuanya
+ - local: chapter2/7
+ title: Penggunaan dasar selesai!
+ - local: chapter2/8
+ title: Deployment Inferensi yang Dioptimalkan
+ - local: chapter2/9
+ title: Kuis akhir bab
+ quiz: 2
+
+- title: 3. Fine-tuning model pralatih
+ sections:
+ - local: chapter3/1
+ title: Pendahuluan
+ - local: chapter3/2
+ title: Memproses data
+ - local: chapter3/3
+ title: Fine-tuning model dengan Trainer API
+ - local: chapter3/4
+ title: Satu siklus pelatihan lengkap
+ - local: chapter3/5
+ title: Memahami Kurva Pembelajaran
+ - local: chapter3/6
+ title: Fine-tuning, Berhasil!
+ - local: chapter3/7
+ title: Kuis akhir bab
+ quiz: 3
+
+- title: 4. Berbagi model dan tokenizer
+ sections:
+ - local: chapter4/1
+ title: Hugging Face Hub
+ - local: chapter4/2
+ title: Menggunakan model yang telah dilatih
+ - local: chapter4/3
+ title: Membagikan model yang telah dilatih
+ - local: chapter4/4
+ title: Membangun model card
+ - local: chapter4/5
+ title: Bagian 1 selesai!
+ - local: chapter4/6
+ title: Kuis akhir bab
+ quiz: 4
+
+- title: 5. Pustaka 🤗 Datasets
+ sections:
+ - local: chapter5/1
+ title: Pendahuluan
+ - local: chapter5/2
+ title: Bagaimana jika dataset saya tidak ada di Hub?
+ - local: chapter5/3
+ title: Saatnya memotong dan memilah data
+ - local: chapter5/4
+ title: Data besar? 🤗 Datasets siap membantu!
+ - local: chapter5/5
+ title: Membuat dataset Anda sendiri
+ - local: chapter5/6
+ title: Pencarian semantik dengan FAISS
+ - local: chapter5/7
+ title: 🤗 Datasets, selesai!
+ - local: chapter5/8
+ title: Kuis akhir bab
+ quiz: 5
+
+- title: 6. Pustaka 🤗 Tokenizers
+ sections:
+ - local: chapter6/1
+ title: Pendahuluan
+ - local: chapter6/2
+ title: Melatih tokenizer baru dari yang lama
+ - local: chapter6/3
+ title: Kekuatan khusus tokenizer cepat
+ - local: chapter6/3b
+ title: Tokenizer cepat dalam pipeline QA
+ - local: chapter6/4
+ title: Normalisasi dan pra-tokenisasi
+ - local: chapter6/5
+ title: Tokenisasi Byte-Pair Encoding
+ - local: chapter6/6
+ title: Tokenisasi WordPiece
+ - local: chapter6/7
+ title: Tokenisasi Unigram
+ - local: chapter6/8
+ title: Membangun tokenizer, blok demi blok
+ - local: chapter6/9
+ title: Tokenizer, cek!
+ - local: chapter6/10
+ title: Kuis akhir bab
+ quiz: 6
diff --git a/chapters/id/chapter0/1.mdx b/chapters/id/chapter0/1.mdx
index b8efdcaa9..c844011b1 100644
--- a/chapters/id/chapter0/1.mdx
+++ b/chapters/id/chapter0/1.mdx
@@ -1,34 +1,34 @@
-# Pendahuluan
+# Pendahuluan[[introduction]]
-Selamat datang di kursus Hugging Face! Pada Bab ini, anda akan dibimbing untuk mempersiapkan _working environment_. Jika anda memulai kursus ini untuk pertama kali, anda sangat direkomendasikan untuk menyelesaikan [Bab 1](/course/chapter1) terlebih dahulu. Setelah menyelesaikan [Bab 1](/course/chapter1) anda bisa kembali ke page ini untuk mencoba eksplorasi kodenya secara independen.
+Selamat datang di kursus Hugging Face! Pada bab ini, Anda akan dipandu untuk menyiapkan _working environment_. Jika ini adalah pertama kalinya Anda mengikuti kursus ini, sangat disarankan untuk menyelesaikan [Bab 1](/course/chapter1) terlebih dahulu. Setelah menyelesaikan [Bab 1](/course/chapter1), Anda dapat kembali ke halaman ini untuk mulai mengeksplorasi kode secara mandiri.
-Semua modul yang digunakan dalam kursus ini tersedia dalam modul Python. Di kursus ini, anda juga akan dibimbing untuk mempersiapkan Python _environment_ dan menginstal modul-modul yang dibutuhkan.
+Semua modul yang digunakan dalam kursus ini tersedia sebagai modul Python. Anda juga akan dipandu untuk menyiapkan lingkungan Python dan menginstal modul-modul yang dibutuhkan.
-Ada 2 cara untuk jenis _working environment_ yang bisa anda gunakan, Colab notebook dan _virtual environment_ Python. Anda bebas memilih _working envrionment_, tapi untuk pemula, kami menyarankan untuk menggunakan Colab notebook.
+Ada dua opsi _working environment_ yang dapat Anda gunakan: Colab notebook atau _virtual environment_ Python. Anda bebas memilih yang sesuai, tetapi bagi pemula, kami menyarankan untuk menggunakan Colab notebook.
-Sebagai catatan, kursus ini tidak mencakup instalasi untuk pengguna Windows. Jika anda menggunakan Windows, mohon menggunakan Colab notebook. Jika anda adalah pengguna Linux atau macOS, anda bebas memilih _working environment_ yang akan dijelaskan dibawah.
+Catatan: kursus ini tidak mencakup panduan instalasi untuk pengguna Windows. Jika Anda menggunakan Windows, mohon gunakan Colab notebook. Bagi pengguna Linux atau macOS, Anda bebas memilih _working environment_ yang akan dijelaskan di bawah.
-Sebagian besar dari kursus ini akan mewajibkan anda untuk memiliki akun Hugging Face. Jika anda belum memiliki akun, silahkan mendaftar terlebih dahulu di tautan berikut [https://huggingface.co/join](https://huggingface.co/join).
+Sebagian besar bagian dalam kursus ini akan membutuhkan akun Hugging Face. Jika Anda belum memiliki akun, silakan daftar terlebih dahulu melalui tautan berikut: [https://huggingface.co/join](https://huggingface.co/join).
-## Menggunakan Google Colab notebook
+## Menggunakan Google Colab Notebook[[using-a-google-colab-notebook]]
-Menggunakan Colab notebook sangatlah sederhana, cukup dengan membuat notebook baru anda sudah bisa mulai koding!
+Menggunakan Colab notebook sangatlah mudah — cukup buat notebook baru dan Anda sudah bisa mulai menulis kode!
-Jika anda belum terbiasa menggunakan Colab, silahkan mengikuti [tutorial pengenalan Colab dari Google](https://colab.research.google.com/notebooks/intro.ipynb) (hanya tersedia dalam Bahasa Inggris). Saat menggunakan Colab, anda dapat mengakses hardware seperti GPU dan TPU yang dapat mengakselerasi proses pengolahan data. Hardware ini dapat anda gunakan secara gratis untuk proyek skala kecil.
+Jika Anda belum familiar dengan Colab, silakan lihat [tutorial pengenalan Colab dari Google](https://colab.research.google.com/notebooks/intro.ipynb) (tersedia dalam Bahasa Inggris). Dengan Colab, Anda juga dapat mengakses hardware seperti GPU dan TPU secara gratis, yang dapat mempercepat proses pemrosesan data untuk proyek berskala kecil.
-Setelah terbiasa dengan Colab, buatlah notebook baru dengan setup sebagai berikut:
+Setelah terbiasa, buatlah notebook baru dan ikuti pengaturan awal berikut:
-Langkah berikutnya adalah menginstal modul-modul yang akan digunakan dalam kursus ini menggunakan `pip`. `pip` adalah modul manager untuk bahasa pemrograman Python. Di dalam notebook, anda dapat mengakses komando sistem dengan menambahkan tanda seru (`!`) sebelum kode instruksi anda. Contoh instalasi modul 🤗 adalah sebagai berikut:
+Langkah selanjutnya adalah menginstal modul yang dibutuhkan menggunakan `pip`, yaitu manajer paket untuk Python. Di dalam notebook, Anda bisa menjalankan perintah sistem dengan menambahkan tanda seru (`!`) sebelum instruksinya. Contoh perintah instalasi 🤗 Transformers:
```
!pip install transformers
```
-Untuk memastikan bahwa modul telah terinstalasi dengan benar, anda perlu mencoba untuk meng-_import_ modul tersebut di _runtime_ Python anda:
+Untuk memastikan modul telah terinstal dengan benar, coba lakukan _import_ modul tersebut di dalam _runtime_ Python Anda:
```
import transformers
@@ -38,38 +38,42 @@ import transformers
-Kode instruksi diatas menginstall versi ringan dari 🤗 Transformers. Versi ringan ini tidak mengistall modul _machine learning_ (seperti PyTorch atau TensorFlow). Sangat direkomendasikan untuk mengistal versi _development_ dari modul ini karena nanti anda akan menggunakan berbagai macam fitur yang tersedia didalam modul ini dan versi ini juga akan mencakup berbagai macam modul untuk segala macam kasus yang akan dihadapi dalam kursus ini. Untuk mengistal versi _development_, silahkan eksekusi kode dibawah:
+Perintah di atas menginstal versi ringan dari 🤗 Transformers, tanpa pustaka _machine learning_ seperti PyTorch atau TensorFlow. Karena dalam kursus ini Anda akan menggunakan berbagai fitur tambahan, sangat disarankan untuk menginstal versi _development_ yang lebih lengkap dengan perintah berikut:
```
!pip install transformers[sentencepiece]
```
-Proses instalasi akan berlangsung cukup lama. Tapi saat instalasi selesai, anda sudah siap untuk menyelesaikan kursus ini!
+Proses instalasi mungkin akan memakan waktu beberapa menit. Setelah selesai, Anda siap melanjutkan kursus ini!
-## Menggunakan Python _virtual environment_
+## Menggunakan Python _Virtual Environment_[[using-a-python-virtual-environment]]
-Jika anda ingin menggunakan Python _virtual environment_, tentu saja langkah pertama yang harus anda lewati adalah menginstal Python. Untuk menginstal Python, bisa mengikuti referensi di tautan [ini](https://realpython.com/installing-python/).
+Jika Anda memilih untuk menggunakan _virtual environment_, langkah pertama adalah menginstal Python. Panduan instalasi dapat ditemukan di tautan berikut: [https://realpython.com/installing-python/](https://realpython.com/installing-python/).
-Setelah Python berhasil terinstalasi, anda bisa menjalankan kode Python di terminal anda. Anda bisa memulai dengan mengeksekusi instruksi berikut untuk memastikan bahwa Python terinstalasi dengan benar: `python --version`. Instruksi ini akan menampilkan versi Python yang terinstalasi di komputer anda.
+Setelah Python berhasil diinstal, jalankan perintah berikut di terminal untuk memastikan bahwa Python telah terpasang dengan benar:
-Python yang saat ini terinstalasi di sistem anda adalah versi Python *"utama"* untuk sistem anda. Sangat direkomendasikan untuk tidak mengotak-ngatik Python "utama" di sistem anda, dan untuk setiap aplikasi yang akan dikembangkan menggunakan Python akan lebih baik jika menggunakan versi Python berbeda. Pada umumnya, versi Python yang digunakan untuk pengembangan aplikasi bukanlah versi "utama". Ini dilakukan karena setiap aplikasi menggunakan modul yang berbeda-beda dan setiap modul memiliki ketergantugan satu sama lain. Dengan menggunakan versi berbeda, kekhawatiran terjadinya konflik antar modul dapat dihindari.
+```
+python --version
+```
-Penggunaan versi berbeda dari Python dilakukan dengan menggunakan [*virtual environments*](https://docs.python.org/3/tutorial/venv.html). _Virtual environment_ adalah instalasi Python terpisah yang digunakan untuk keperluan tertentu aplikasi. Di dalam virtual environment, versi Python maupun modul-modul yang terinstal akan terisolasi dari versi Python "utama". Terdapata banyak cara untuk membuat _virtual environment_, tapi di kursus ini kita akan mengikuti arahan khusus dari dokumentasi resmi Python yang dinamai [`venv`](https://docs.python.org/3/library/venv.html#module-venv).
+Perintah tersebut akan menampilkan versi Python yang digunakan sistem Anda. Python bawaan sistem disebut sebagai versi *utama*, dan sebaiknya tidak diubah. Untuk setiap proyek pengembangan, disarankan menggunakan versi Python terpisah, karena setiap proyek umumnya memerlukan dependensi yang berbeda. Hal ini bertujuan untuk menghindari konflik antar modul.
-Pertama, buatlah folder baru untuk menyimpan aplikasi yang akan dibuat. Sebagai contoh, anda mungkin akan membuat folder baru bernama *transformers-course* di root folder dari home directory komputer anda:
+Solusi terbaik adalah dengan menggunakan [_virtual environment_](https://docs.python.org/3/tutorial/venv.html), yaitu instalasi Python terisolasi yang hanya digunakan untuk satu proyek tertentu. Dalam kursus ini, kita akan menggunakan modul bawaan Python, yaitu [`venv`](https://docs.python.org/3/library/venv.html#module-venv), untuk membuatnya.
+
+Pertama, buat folder baru untuk menyimpan proyek Anda. Misalnya:
```
mkdir ~/transformers-course
cd ~/transformers-course
```
-Setelah masuk ke folder baru tersebut, buatlah _virtual environment_ menggunakan modul `venv` Python:
+Kemudian buat _virtual environment_:
```
python -m venv .env
```
-Setelah menggunakan modul `venv`, anda akan memiliki folder baru bernama *.env*:
+Setelah dijalankan, akan muncul folder baru bernama `.env`:
```
ls -a
@@ -79,7 +83,7 @@ ls -a
. .. .env
```
-Instruksi dibawah adalah instruksi untuk mengaktifkan dan menonaktifkan _virtual environment_ yang baru saja dibuat:
+Berikut ini perintah untuk mengaktifkan dan menonaktifkan _virtual environment_:
```
# Mengaktifkan virtual environment
@@ -89,22 +93,24 @@ source .env/bin/activate
deactivate
```
-Anda bisa memastikan bahwa anda menggunakan Python versi _virtual environment_ dengan mengeksekusi `which python` di terminal: jika balasan terminal adalah Python di dalam folder *.env*, maka _virtual environment_ anda sudah aktif!
+Untuk memastikan Anda menggunakan versi Python dari _virtual environment_, jalankan:
```
which python
```
+Jika hasilnya menunjuk ke folder `.env`, maka _virtual environment_ Anda sudah aktif:
+
```out
/home//transformers-course/.env/bin/python
```
-### Instalasi modul
+### Instalasi Modul[[installing-dependencies]]
-Sama seperti di Google Colab, anda perlu menginstal modul-modul yang diperlukan. Kali ini, instalasi versi _development_ 🤗 Transformers dapat dilakukan menggunakan _package manager_ `pip`:
+Sama seperti di Google Colab, Anda perlu menginstal pustaka yang dibutuhkan. Gunakan perintah berikut untuk menginstal versi _development_ dari 🤗 Transformers:
```
pip install "transformers[sentencepiece]"
```
-Sekarang anda siap untuk mulai belajar!
\ No newline at end of file
+Sekarang Anda siap untuk mulai belajar!
diff --git a/chapters/id/chapter1/1.mdx b/chapters/id/chapter1/1.mdx
index bdce1e62e..21edeaf10 100644
--- a/chapters/id/chapter1/1.mdx
+++ b/chapters/id/chapter1/1.mdx
@@ -1,61 +1,156 @@
-# Pendahuluan
+# Pengantar [[introduction]]
-## Selamat datang di Kursus 🤗!
+## Selamat Datang di Kursus 🤗! [[welcome-to-the-course]]
-Pada kursus ini, anda akan belajar mengenai _natural language processing_ (pemrosesan bahasa natural) atau NLP menggunakan modul-modul dari ekosistem [Hugging Face](https://huggingface.co/) - [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), and [🤗 Accelerate](https://github.com/huggingface/accelerate) — as well as the [Hugging Face Hub](https://huggingface.co/models). Kursus ini 100% gratis tanpa iklan.
+Kursus ini akan mengajarkan Anda tentang model bahasa besar (LLM) dan pemrosesan bahasa alami (NLP) menggunakan modul dari ekosistem [Hugging Face](https://huggingface.co/) — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), dan [🤗 Accelerate](https://github.com/huggingface/accelerate) — serta [Hugging Face Hub](https://huggingface.co/models).
+Kami juga akan membahas modul dari luar ekosistem Hugging Face. Semua ini merupakan kontribusi luar biasa bagi komunitas AI dan alat yang sangat bermanfaat.
-## Silabus
+Kursus ini sepenuhnya gratis dan tanpa iklan.
-Silabus kursus ini adalah sebagai berikut:
+## Memahami NLP dan LLM [[understanding-nlp-and-llms]]
+
+Meskipun kursus ini awalnya berfokus pada NLP (Natural Language Processing), kini berkembang untuk lebih menekankan pada Model Bahasa Besar (LLM), yang merupakan kemajuan terbaru di bidang ini.
+
+**Apa bedanya?**
+- **NLP (Pemrosesan Bahasa Alami)** adalah bidang luas yang berfokus pada memungkinkan komputer untuk memahami, menafsirkan, dan menghasilkan bahasa manusia. NLP mencakup berbagai teknik dan tugas seperti analisis sentimen, pengenalan entitas bernama, dan terjemahan mesin.
+- **LLM (Large Language Models)** adalah subset NLP yang kuat, dicirikan oleh ukuran besar, data pelatihan yang luas, dan kemampuannya untuk menyelesaikan berbagai tugas bahasa dengan pelatihan khusus minimal. Model seperti Llama, GPT, atau Claude merupakan contoh LLM yang telah merevolusi apa yang mungkin dilakukan dalam NLP.
+
+Sepanjang kursus ini, Anda akan mempelajari konsep-konsep NLP tradisional dan teknik-teknik mutakhir LLM, karena pemahaman dasar NLP sangat penting untuk bekerja secara efektif dengan LLM.
+
+## Apa yang Diharapkan? [[what-to-expect]]
+
+Berikut ini gambaran singkat dari kursus:
-
-
+
+
-- Bab 1-4 akan mencakup pengenalan konsep-konsep dasar modul 🤗 Transformers. Di akhir bab 4, anda akan tahu bagaimana menggunakan model-model _Transformer_ dari [Hugging Face Hub](https://huggingface.co/models), melakukan model _fine-tuning_ untuk dataset anda, dan membagikan model anda di Hugging Face Hub!
-- Bab 5-8 akan mencakup dasar-dasar dari 🤗 Datasets dan 🤗 Tokenizers sebelum anda diperkenalkan ke kasus-kasus yang dapat ditangani dengan NLP. Diakhir kursus ini, anda akan mampu menangani dan menyelesaikan kasus-kasus NLP.
-- Chapters 9 to 12 go beyond NLP, and explore how Transformer models can be used tackle tasks in speech processing and computer vision. Along the way, you'll learn how to build and share demos of your models, and optimize them for production environments. By the end of this part, you will be ready to apply 🤗 Transformers to (almost) any machine learning problem!
-- Setelah NLP, di bab 9-12, anda akan mengeksplorasi bagaimana model-model Transformer dapat digunakan untuk menangani kasus-kasus lain seperti _speech processing_ (pemrosesan ucapan) dan _computer vision_ (penglihatan komputer). Selain itu, anda akan belajar cara membuat dan membagikan demo (prototype) dari model anda, serta cara mengoptimisasi model anda untuk _production environment_ (penerapan di kasus asli). Di akhir bab 12, anda akan siap mengimplementasikan 🤗 Transformers untuk (hampir) semua kasus _machine learning_ (pembelajaran mesin)!
+- Bab 1–4 memberikan pengantar tentang konsep utama dari modul 🤗 Transformers. Di akhir bagian ini, Anda akan memahami cara kerja model Transformer, cara menggunakan model dari [Hugging Face Hub](https://huggingface.co/models), melakukan fine-tuning terhadap dataset, dan membagikan hasilnya ke Hub!
+- Bab 5–8 mengajarkan dasar-dasar dari 🤗 Datasets dan 🤗 Tokenizers, sebelum menyelami tugas-tugas NLP klasik dan teknik LLM. Di akhir bagian ini, Anda akan dapat mengatasi berbagai tantangan pemrosesan bahasa secara mandiri.
+- Bab 9 membahas lebih dari sekadar NLP — Anda akan belajar membuat dan membagikan demo model di 🤗 Hub. Pada akhir bagian ini, Anda siap memamerkan aplikasi 🤗 Transformers Anda ke dunia!
+- Bab 10–12 menyelami topik LLM lanjutan seperti fine-tuning, kurasi dataset berkualitas tinggi, dan membangun model reasoning.
+
+Kursus ini:
+
+* Membutuhkan pengetahuan yang baik tentang Python
+* Sebaiknya diikuti setelah kursus pembelajaran mendalam dasar, seperti [Practical Deep Learning for Coders](https://course.fast.ai/) dari fast.ai atau salah satu program dari [DeepLearning.AI](https://www.deeplearning.ai/)
+* Tidak mengharuskan Anda menguasai [PyTorch](https://pytorch.org/) atau [TensorFlow](https://www.tensorflow.org/), namun sedikit pemahaman akan membantu
+
+Setelah menyelesaikan kursus ini, kami merekomendasikan [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) dari DeepLearning.AI, yang membahas berbagai model NLP tradisional seperti naive Bayes dan LSTM.
+
+## Siapa Kami? [[who-are-we]]
+
+Tentang Para Penulis
+
+[**Abubakar Abid**](https://huggingface.co/abidlabs) menyelesaikan gelar PhD-nya di Stanford dalam bidang machine learning terapan. Selama masa studi PhD-nya, ia mendirikan [Gradio](https://github.com/gradio-app/gradio), sebuah modul Python open-source yang telah digunakan untuk membangun lebih dari 600.000 demo machine learning. Gradio kemudian diakuisisi oleh Hugging Face, tempat Abubakar kini menjabat sebagai pemimpin tim machine learning.
+
+[**Ben Burtenshaw**](https://huggingface.co/burtenshaw) adalah seorang Machine Learning Engineer di Hugging Face. Ia menyelesaikan PhD-nya di bidang Natural Language Processing di Universitas Antwerp, di mana ia menggunakan model Transformer untuk menghasilkan cerita anak-anak dengan tujuan meningkatkan kemampuan literasi. Sejak saat itu, ia fokus mengembangkan materi dan alat edukasi untuk komunitas yang lebih luas.
+
+[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) adalah Machine Learning Engineer di Hugging Face. Ia tinggal di Dublin, Irlandia dan sebelumnya bekerja sebagai ML engineer di Parse.ly serta sebagai peneliti pascadoktoral di Trinity College Dublin. Ia tidak percaya bahwa AGI akan tercapai hanya dengan meningkatkan skala arsitektur yang ada, tetapi tetap memiliki harapan besar pada keabadian robot.
+
+[**Lysandre Debut**](https://huggingface.co/lysandre) adalah Machine Learning Engineer di Hugging Face dan telah bekerja pada modul 🤗 Transformers sejak tahap awal pengembangannya. Tujuannya adalah membuat NLP dapat diakses oleh semua orang melalui pengembangan alat dengan API yang sangat sederhana.
+
+[**Sylvain Gugger**](https://huggingface.co/sgugger) adalah Research Engineer di Hugging Face dan salah satu pemelihara utama modul 🤗 Transformers. Sebelumnya, ia adalah Research Scientist di fast.ai, dan ia turut menulis buku _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ bersama Jeremy Howard. Fokus utama penelitiannya adalah membuat deep learning lebih mudah diakses dengan merancang dan menyempurnakan teknik-teknik yang memungkinkan pelatihan model secara cepat dengan sumber daya terbatas.
+
+[**Dawood Khan**](https://huggingface.co/dawoodkhan82) adalah Machine Learning Engineer di Hugging Face. Ia berasal dari NYC dan lulus dari New York University di bidang Ilmu Komputer. Setelah beberapa tahun bekerja sebagai iOS Engineer, Dawood memutuskan untuk keluar dan mendirikan Gradio bersama rekan-rekannya. Gradio akhirnya diakuisisi oleh Hugging Face.
+
+[**Merve Noyan**](https://huggingface.co/merve) adalah developer advocate di Hugging Face. Ia fokus pada pengembangan alat dan pembuatan konten edukatif untuk mendemokratisasi machine learning bagi semua orang.
+
+[**Lucile Saulnier**](https://huggingface.co/SaulLu) adalah Machine Learning Engineer di Hugging Face yang mengembangkan dan mendukung penggunaan alat open-source. Ia juga aktif dalam berbagai proyek riset di bidang NLP, seperti pelatihan kolaboratif dan BigScience.
+
+[**Lewis Tunstall**](https://huggingface.co/lewtun) adalah Machine Learning Engineer di Hugging Face yang fokus pada pengembangan alat open-source dan memastikan alat-alat tersebut dapat diakses oleh komunitas yang lebih luas. Ia juga merupakan salah satu penulis buku dari O'Reilly berjudul [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
+
+[**Leandro von Werra**](https://huggingface.co/lvwerra) adalah Machine Learning Engineer di tim open-source Hugging Face dan juga salah satu penulis buku O'Reilly [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Ia memiliki pengalaman industri selama beberapa tahun dalam membawa proyek NLP ke produksi dengan bekerja di seluruh lapisan stack machine learning.
+
+
+## FAQ [[faq]]
+
+Beberapa pertanyaan yang sering diajukan:
+
+- **Apakah kursus ini memberikan sertifikasi?**
+ Saat ini belum tersedia, namun kami sedang mengembangkan program sertifikasi untuk ekosistem Hugging Face.
+
+- **Berapa lama waktu yang dibutuhkan?**
+ Setiap bab dirancang untuk diselesaikan dalam waktu seminggu, dengan alokasi sekitar 6–8 jam. Tapi Anda bisa menyelesaikannya sesuai waktu luang.
+
+- **Di mana saya bisa bertanya?**
+ Klik tombol "*Ask a question*" di atas halaman kursus untuk diarahkan langsung ke bagian yang sesuai di [forum Hugging Face](https://discuss.huggingface.co/).
+
+
+
+- **Di mana saya bisa mendapatkan kode kursus?**
+ Klik banner di atas bagian kursus untuk menjalankan notebook di Google Colab atau Amazon SageMaker Studio Lab.
+
+
-Syarat mengikuti kursus:
+ Semua notebook juga tersedia di repo [`huggingface/notebooks`](https://github.com/huggingface/notebooks). Untuk menjalankan secara lokal, lihat panduan di repo [`course`](https://github.com/huggingface/course#-jupyter-notebooks).
-* Requires a good knowledge of Python
-* Pengetahuan mengenai Python
-* Akan lebih baik jika sudah mengenal deep learning dengan mengambil kursus dari [fast.ai](https://www.fast.ai/) "[Practical Deep Learning for Coders](https://course.fast.ai/)" atau program-program yang dikembangkan oleh [DeepLearning.AI](https://www.deeplearning.ai/)
-* Tidak perlu pengetahuan mengenai [PyTorch](https://pytorch.org/) atau [TensorFlow](https://www.tensorflow.org/). Tapi, akan lebih baik jika sudah terbiasa dengan salah satu framework tersebut.
+- **Bagaimana saya bisa berkontribusi?**
+ Temukan typo atau bug? Buat issue di repo [`course`](https://github.com/huggingface/course). Ingin bantu menerjemahkan kursus ini? Ikuti panduan [di sini](https://github.com/huggingface/course#translating-the-course-into-your-language).
-Setelah menyelesaikan kursus ini, sangat direkomendasikan untuk mengikuti kursus dari DeepLearning.AI [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) yang akan mencakup model-model NLP klasik seperti naive Bayes dan LSTM. Pengetahuan tersebut akan sangat berharga bagi anda!
+- **Apa saja pertimbangan dalam terjemahan?**
+ Setiap terjemahan memiliki glosarium dan file `TRANSLATING.txt` yang mendokumentasikan pilihan istilah teknis. Contoh: [bahasa Jerman](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt).
-## Tentang penulis
+- **Bisakah saya menggunakan ulang materi ini?**
+ Tentu! Kursus ini dirilis di bawah lisensi [Apache 2](https://www.apache.org/licenses/LICENSE-2.0.html). Berikan atribusi yang sesuai dan tautan ke lisensi. Bila ingin mengutip, gunakan BibTeX berikut:
-**Abubakar Abid** adalah lulusan PhD dari Stanford dengan konsentrasi aplikasi pembelajaran mesin. Sembari menyelesaikan pendidikan PhD, beliau menciptakan [Gradio](https://github.com/gradio-app/gradio), sebuah modul _open-source_ Python yang sudah digunakan untuk membuat lebih dari 600.000 demo (prototype) model _machine learning_. Gradio telah diakusisi oleh Hugging Face, tempat dimana Abubakar bekerja sebagai _machine learning team lead_.
+```
+@misc{huggingfacecourse,
+ author = {Hugging Face},
+ title = {The Hugging Face Course, 2022},
+ howpublished = "\url{https://huggingface.co/course}",
+ year = {2022},
+ note = "[Online; accessed ]"
+}
+```
-**Matthew Carrigan** bekerja sebagai _Machine Learning Engineer_ di Hugging Face. Beliau tinggal di Dublin, Irlandia, pernah bekerja sebagai _ML engineer_ di Parse.ly dan sebelumnya merupakan peneliti post-doctoral di Trinity College Dublin. Beliau tidak percaya kita akan mencapai Artificial general intelligence (AGI) dengan menambahkan skala dari arsitektur yang digunakan sekarang, namun memiliki optimisme mengenai imortalitas robot.
+## Bahasa dan Terjemahan [[languages-and-translations]]
-**Lysandre Debut** bekerja sebagai _Machine Learning Engineer_ di Hugging Face dan berfokus mengembangkan modul 🤗 Transformers sejak seumur jagung. Beliau mempunya mimpi untuk agar NLP dapat diakses oleh semua orang dengan mengembangkan alat-alat atau aplikasi-aplikasi sederhana menggunkan API.
+Kursus ini tersedia dalam banyak bahasa berkat kontribusi komunitas ❤️. Cek [halaman ini](https://huggingface.co/course) untuk melihat semua bahasa yang tersedia.
-**Sylvain Gugger** adalah _Research Engineer_ di Hugging Face dan merupakan salah satu _maintainer_ dari modul 🤗 Transformers. Beliau pernah bekerja sebagai _Research Scientist_ di fast.ai, dan bersama Jeremy Howard menulis _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_. Fokus utama dari penelitian beliau adalah membuat _deep learning_ lebih mudah diakses dengan mendesain dan memperbaiki teknik-teknik untuk melatih model dengan sumber daya terbatas.
+| Language | Authors |
+|:------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [French](https://huggingface.co/course/fr/chapter1/1) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
+| [Vietnamese](https://huggingface.co/course/vi/chapter1/1) | [@honghanhh](https://github.com/honghanhh) |
+| [Chinese (simplified)](https://huggingface.co/course/zh-CN/chapter1/1) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@yaoqih](https://github.com/yaoqih) |
+| [Bengali](https://huggingface.co/course/bn/chapter1/1) (WIP) | [@avishek-018](https://github.com/avishek-018), [@eNipu](https://github.com/eNipu) |
+| [German](https://huggingface.co/course/de/chapter1/1) (WIP) | [@JesperDramsch](https://github.com/JesperDramsch), [@MarcusFra](https://github.com/MarcusFra), [@fabridamicelli](https://github.com/fabridamicelli) |
+| [Spanish](https://huggingface.co/course/es/chapter1/1) (WIP) | [@camartinezbu](https://github.com/camartinezbu), [@munozariasjm](https://github.com/munozariasjm), [@fordaz](https://github.com/fordaz) |
+| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
+| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Hebrew](https://huggingface.co/course/he/chapter1/1) (WIP) | [@omer-dor](https://github.com/omer-dor) |
+| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Bahasa Indonesia](https://huggingface.co/course/id/chapter1/1) (WIP) | [@gstdl](https://github.com/gstdl), [@Vo](https://github.com/evo2mind) |
+| [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) |
+| [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) |
+| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19) |
+| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
+| [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73) |
+| [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) |
+| [Turkish](https://huggingface.co/course/tr/chapter1/1) (WIP) | [@tanersekmen](https://github.com/tanersekmen), [@mertbozkir](https://github.com/mertbozkir), [@ftarlaci](https://github.com/ftarlaci), [@akkasayaz](https://github.com/akkasayaz) |
+| [Chinese (traditional)](https://huggingface.co/course/zh-TW/chapter1/1) (WIP) | [@davidpeng86](https://github.com/davidpeng86)
-**Dawood Khan** bekerja sebagai _Machine Learning Engineer_ di Hugging Face. Beliau berasal dari NYC dan merupakan lulusan New York University jurusan _Computer Science_. Sempat bekerja sebagai iOS _Engineer_ untuk beberapa tahun, Dawood memutuskan untuk _resign_ dan mengembangkan Gradio bersama rekan-rekan co-foundernya. Seiring berjalannya waktu, Gradio diakusisi oleh Hugging Face.
+Untuk beberapa bahasa, video kursus di [YouTube](https://youtube.com/playlist?list=PLo2EIpI_JMQvWfQndUesu0nPBAtZ9gP1o) tersedia dengan subtitle. Klik tombol _CC_ lalu pilih bahasa di menu pengaturan.
-**Merve Noyan** adalah advokat _developer_ di Hugging Face, beliau bertugas untuk mengembangkan konten beserta medianya untuk mendemokrasikan _machine learning_ untuk semua orang.
+
-**Lucile Saulnier** adalah _machine learning engineer_ di Hugging Face, bertugas untuk mengembangkan dan mendukung penggunaan alat-alat _open source_. Beliau juga aktif dalam banyak riset mengenai _Natural Language Processing_ seperti _collaborative training_ dan BigScience.
+
+Tidak menemukan bahasa Anda atau ingin membantu terjemahan? Lihat panduan kontribusi di sini.
+
-**Lewis Tunstall** merupakan _machine learning engineer_ di Hugging Face, bertugas untuk mengembangkan alat-alat _open source_ dan membuatnya dapat diakses oleh komunitas. Beliau juga merupakan salah satu penulis dari buku terbitan O’Reilly berjudul [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
+## Ayo Mulai 🚀
-**Leandro von Werra** bekerja sebagai _machine learning engineer_ untuk tim _open-source_ di Hugging Face dan juga merupkan salah satu penulis buku [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/) yang diterbitkan oleh O'Reilly. Beliau memiliki memiliki pengalaman mengembangkan proyek-proyek NLP untuk kasus nyata pada berbagai macam _machine learning stack_ selama beberapa tahun.
+Siap? Di bab ini Anda akan belajar:
-Sudah siap untuk belajar? Di bab ini anda akan belajar mengenai:
-* Penggunaan fungsi `pipeline()` untuk memecahkan masalah-masalah NLP seperti _text generation_ (pembuatan teks) dan klasifikasi.
-* Arsitektur Transformer
-* Bagaimana membedakan arsitektur encoder, decoder, dan encoder-decoder beserta kasus-kasus terkait.
+* Cara menggunakan fungsi `pipeline()` untuk menyelesaikan tugas NLP seperti klasifikasi teks dan generasi teks
+* Tentang arsitektur Transformer
+* Cara membedakan arsitektur encoder, decoder, dan encoder-decoder serta penggunaannya
diff --git a/chapters/id/chapter1/10.mdx b/chapters/id/chapter1/10.mdx
new file mode 100644
index 000000000..19f21704a
--- /dev/null
+++ b/chapters/id/chapter1/10.mdx
@@ -0,0 +1,66 @@
+# Ringkasan[[summary]]
+
+
+
+Dalam bab ini, Anda telah diperkenalkan pada dasar-dasar model Transformer, Model Bahasa Besar (LLM), dan bagaimana mereka merevolusi dunia AI dan bidang lainnya.
+
+## Konsep-konsep utama yang dibahas
+
+### Pemrosesan Bahasa Alami (NLP) dan LLM
+
+Kita telah membahas apa itu NLP dan bagaimana Model Bahasa Besar telah mengubah bidang ini. Anda telah mempelajari bahwa:
+- NLP mencakup berbagai macam tugas, mulai dari klasifikasi hingga generasi teks
+- LLM adalah model kuat yang dilatih pada jumlah data teks yang sangat besar
+- Model-model ini dapat melakukan berbagai tugas dalam satu arsitektur
+- Terlepas dari kemampuannya, LLM memiliki keterbatasan seperti halusinasi dan bias
+
+### Kemampuan Transformer
+
+Anda telah melihat bagaimana fungsi `pipeline()` dari 🤗 Transformers memudahkan penggunaan model pralatih untuk berbagai tugas:
+- Klasifikasi teks, klasifikasi token, dan tanya jawab
+- Generasi teks dan peringkasan
+- Penerjemahan dan tugas sequence-to-sequence lainnya
+- Pengenalan suara dan klasifikasi gambar
+
+### Arsitektur Transformer
+
+Kita telah membahas cara kerja model Transformer secara umum, termasuk:
+- Pentingnya mekanisme perhatian (attention)
+- Bagaimana pembelajaran transfer memungkinkan model beradaptasi dengan tugas tertentu
+- Tiga varian arsitektur utama: hanya-encoder, hanya-decoder, dan encoder-decoder
+
+### Arsitektur model dan penerapannya
+Aspek penting dalam bab ini adalah memahami arsitektur mana yang digunakan untuk tugas yang berbeda:
+
+| Model | Contoh | Tugas |
+|-----------------|--------------------------------------------|-----------------------------------------------------------------------|
+| Hanya-encoder | BERT, DistilBERT, ModernBERT | Klasifikasi kalimat, pengenalan entitas bernama, tanya jawab ekstraktif |
+| Hanya-decoder | GPT, LLaMA, Gemma, SmolLM | Generasi teks, AI percakapan, penulisan kreatif |
+| Encoder-decoder | BART, T5, Marian, mBART | Peringkasan, penerjemahan, tanya jawab generatif |
+
+### Perkembangan LLM modern
+Anda juga telah mempelajari perkembangan terkini di bidang ini:
+- Bagaimana LLM berkembang dalam ukuran dan kapabilitas seiring waktu
+- Konsep hukum skala (scaling laws) dan bagaimana mereka memandu pengembangan model
+- Mekanisme perhatian khusus yang membantu model memproses urutan yang lebih panjang
+- Pendekatan pelatihan dua fase: pralatih dan penyetelan instruksi (instruction tuning)
+
+### Aplikasi praktis
+Sepanjang bab ini, Anda telah melihat bagaimana model-model ini dapat diterapkan pada masalah dunia nyata:
+- Menggunakan Hugging Face Hub untuk menemukan dan menggunakan model pralatih
+- Memanfaatkan Inference API untuk menguji model langsung di browser Anda
+- Memahami model mana yang paling cocok untuk tugas tertentu
+
+## Melihat ke depan
+
+Sekarang Anda telah memiliki pemahaman yang kuat tentang apa itu model Transformer dan bagaimana cara kerjanya secara umum, Anda siap untuk mempelajari cara menggunakannya secara efektif. Dalam bab-bab berikutnya, Anda akan belajar cara:
+
+- Menggunakan pustaka Transformers untuk memuat dan menyetel model
+- Memproses berbagai jenis data sebagai input model
+- Menyesuaikan model pralatih untuk tugas spesifik Anda
+- Mendeploy model untuk aplikasi praktis
+
+Dasar yang telah Anda bangun dalam bab ini akan sangat berguna saat Anda menjelajahi topik dan teknik yang lebih lanjut di bagian selanjutnya.
diff --git a/chapters/id/chapter1/11.mdx b/chapters/id/chapter1/11.mdx
new file mode 100644
index 000000000..a7ca3ebbd
--- /dev/null
+++ b/chapters/id/chapter1/11.mdx
@@ -0,0 +1,20 @@
+# Waktu Ujian!
+
+Saatnya menguji pengetahuan Anda! Kami telah menyiapkan kuis singkat untuk menguji pemahaman Anda terhadap konsep-konsep yang dibahas dalam bab ini.
+
+Untuk mengikuti kuis, Anda perlu melakukan langkah-langkah berikut:
+
+1. Masuk ke akun Hugging Face Anda.
+2. Jawab pertanyaan-pertanyaan dalam kuis.
+3. Kirimkan jawaban Anda.
+
+## Kuis Pilihan Ganda
+
+Dalam kuis ini, Anda akan diminta untuk memilih jawaban yang benar dari daftar pilihan yang tersedia. Kami akan menguji pemahaman Anda tentang dasar-dasar penyetelan terawasi (supervised finetuning).
+
+
diff --git a/chapters/id/chapter1/2.mdx b/chapters/id/chapter1/2.mdx
index 9d73e7447..6cf675d2f 100644
--- a/chapters/id/chapter1/2.mdx
+++ b/chapters/id/chapter1/2.mdx
@@ -1,27 +1,55 @@
-# Pemrosesan Bahasa Natural (Natural Language Processing)
+# Pemrosesan Bahasa Alami dan Model Bahasa Besar [[natural-language-processing-and-large-language-models]]
-Sebelum mempelajari mengenai model-model Transformer, mari menyamakan persepsi mengenai _natural language processing_ (NLP) terlebih dahulu.
+Sebelum masuk ke model Transformer, mari kita tinjau secara singkat apa itu pemrosesan bahasa alami (NLP), bagaimana model bahasa besar telah mengubah bidang ini, dan mengapa hal ini penting.
-## NLP itu apa?
+## Apa itu NLP? [[what-is-nlp]]
-NLP merupakan cabang ilmu linguistik dan pembelajaran mesin (_machine learning_) yang bertujuan untuk memahami bahasa manusia sehari-hari. Tujuan dari penerapan NLP tidak terbatas pada pemahaman kata per kata saja, tapi juga mengenai konteks yang terkandung dalam setiap ucapan/kata.
+
-Beberapa penerapan NLP yang umum diterapkan beserta contohnya dapat dilihat dibawah:
+NLP adalah bidang linguistik dan pembelajaran mesin yang berfokus pada pemahaman segala hal yang berkaitan dengan bahasa manusia. Tujuan dari tugas NLP bukan hanya memahami kata secara individu, tetapi juga memahami konteks dari kata-kata tersebut.
-- **Klasifikasi kalimat secara utuh**: Mengetahui sentimen dari sebuah review, mendeteksi email spam, menentukan ketepatan tata bahasa sebuah kalimat atau mencari tahu keterkaitan antar 2 kalimat
-- **Classifying each word in a sentence**: Identifying the grammatical components of a sentence (noun, verb, adjective), or the named entities (person, location, organization)
-- **Klasifikasi setiap kata dalam sebuah kalimat**: Pengelompokkan unsur kalimat (kata benda, kata kerja, kata sifat), atau pengelompokkan subjek kalimat (orang, lokasi, organisasi)
-- **Menciptakan/menambahkan/memperkaya kalimat**: Menyelesaikan kalimat dengan teks yang diciptakan secara otomatis, mengisi titik-titik pada sebuah kuis
-- **Menjawab pertanyaan**: Dengan memberi model daftar pertanyaan beserta konteks, menjawab pertanyaan berdasar informasi yang tersedia
-- **Menciptakan kalimat baru dari teks**: Menerjemahkan suatu bahasa ke bahasa lain, menyimpulkan kalimat
+Berikut adalah daftar tugas NLP yang umum, beserta contohnya:
-Penerapan NLP tidak hanya terbatas pada teks. NLP juga dapat diterapkan untuk menangani kasus pengelan suara (_speech recognition_) dan penglihatan komputer (_computer vision_) seperti menciptakan transkrip dari sampel suara (audio) atau deskripsi gambar.
+- **Klasifikasi seluruh kalimat**: Menentukan sentimen dari sebuah ulasan, mendeteksi apakah sebuah email adalah spam, menentukan apakah sebuah kalimat benar secara tata bahasa atau apakah dua kalimat saling berkaitan secara logika
+- **Klasifikasi setiap kata dalam kalimat**: Mengidentifikasi komponen tata bahasa dari sebuah kalimat (kata benda, kata kerja, kata sifat), atau entitas bernama (nama orang, lokasi, organisasi)
+- **Menghasilkan konten teks**: Melengkapi sebuah prompt dengan teks yang dihasilkan secara otomatis, mengisi bagian kosong dalam teks dengan kata-kata yang disamarkan
+- **Menarik jawaban dari teks**: Diberikan sebuah pertanyaan dan konteks, menarik jawaban dari pertanyaan tersebut berdasarkan informasi yang tersedia di konteks
+- **Menghasilkan kalimat baru dari teks input**: Menerjemahkan teks ke bahasa lain, membuat ringkasan teks
-## Tantangan-tantangan dalam penerapan NLP
+NLP tidak terbatas pada teks tertulis saja. Bidang ini juga menangani tantangan kompleks dalam pengenalan suara dan visi komputer, seperti membuat transkrip dari sampel audio atau deskripsi dari sebuah gambar.
-Komputer tidak dapat memahami informasi secara langsung maupun secara harfiah seperti manusia. Sebagai contoh, ketika membaca kalimat "Saya lapar", manusia dapat dengan mudah memahami maknanya. Begitu juga jika ketika membaca kalimat "Saya lapar" dan "Saya sedih", manusia dapat dengan memudah menentukan apakah kedua kalimat memiliki kemiripan atau tidak. Tapi hal-hal tersebut sulit dipahami oleh model pembelajaran mesin (_machine learning_ (ML)). Kalimat-kalimat tersebut perlu direkayasa (diolah) sedimikian rupa sehingga dapat dipelajari oleh model ML. Ditambah dengan keunikan setiap bahasa/teks, kompleksitas rekayasa yang perlu dilakukan menjadi tantangan tersendiri. Sudah ada banyak riset yang meneliti mengenai bagaiman merekayasa teks untuk penerapan ML dan anda akan mempelajarinya di bab-bab berikutnya.
+## Munculnya Model Bahasa Besar (LLM) [[rise-of-llms]]
+
+Dalam beberapa tahun terakhir, bidang NLP telah mengalami revolusi berkat Model Bahasa Besar (Large Language Models atau LLM). Model-model ini, termasuk arsitektur seperti GPT (Generative Pre-trained Transformer) dan [Llama](https://huggingface.co/meta-llama), telah mengubah apa yang mungkin dilakukan dalam pemrosesan bahasa.
+
+
+
+Model bahasa besar (LLM) adalah model AI yang dilatih pada sejumlah besar data teks yang mampu memahami dan menghasilkan teks mirip manusia, mengenali pola dalam bahasa, dan melakukan berbagai tugas bahasa tanpa pelatihan khusus untuk setiap tugas. Model ini merupakan kemajuan besar dalam bidang pemrosesan bahasa alami (NLP).
+
+
+
+Ciri khas LLM adalah:
+- **Skala**: Memiliki jutaan, miliaran, atau bahkan ratusan miliar parameter
+- **Kemampuan umum**: Dapat melakukan banyak tugas tanpa pelatihan khusus untuk setiap tugas
+- **Pembelajaran dalam konteks (in-context learning)**: Dapat belajar dari contoh yang diberikan dalam prompt
+- **Kemampuan emergen**: Saat model tumbuh lebih besar, ia menunjukkan kemampuan yang tidak secara eksplisit diprogramkan atau diprediksi
+
+Kemunculan LLM telah mengubah paradigma dari membangun model khusus untuk tugas NLP tertentu menjadi menggunakan satu model besar yang bisa dipandu (prompt) atau disesuaikan (fine-tune) untuk menangani berbagai macam tugas bahasa. Hal ini membuat pemrosesan bahasa yang canggih menjadi lebih mudah diakses, meskipun juga menimbulkan tantangan baru dalam hal efisiensi, etika, dan penerapan.
+
+Namun, LLM juga memiliki keterbatasan penting:
+- **Halusinasi**: Dapat menghasilkan informasi yang salah dengan percaya diri
+- **Kurangnya pemahaman sejati**: Tidak memiliki pemahaman nyata tentang dunia dan hanya beroperasi berdasarkan pola statistik
+- **Bias**: Dapat mereproduksi bias yang ada di data pelatihan atau input
+- **Jendela konteks**: Memiliki jendela konteks yang terbatas (meskipun hal ini terus berkembang)
+- **Sumber daya komputasi**: Membutuhkan sumber daya komputasi yang besar
+
+## Mengapa pemrosesan bahasa itu menantang? [[why-is-it-challenging]]
+
+Komputer tidak memproses informasi dengan cara yang sama seperti manusia. Misalnya, saat kita membaca kalimat "Saya lapar", kita bisa langsung memahami maknanya. Begitu pula, saat diberikan dua kalimat seperti "Saya lapar" dan "Saya sedih", kita dapat dengan mudah menentukan seberapa mirip keduanya. Namun bagi model pembelajaran mesin (ML), tugas-tugas semacam ini jauh lebih sulit. Teks perlu diproses dengan cara yang memungkinkan model untuk belajar darinya. Dan karena bahasa itu kompleks, kita harus memikirkan dengan cermat bagaimana pemrosesan itu dilakukan. Sudah banyak penelitian dilakukan tentang cara merepresentasikan teks, dan kita akan membahas beberapa metode pada bab berikutnya.
+
+Meskipun ada kemajuan dalam LLM, masih banyak tantangan mendasar yang tersisa. Ini termasuk memahami ambiguitas, konteks budaya, sarkasme, dan humor. LLM mengatasi tantangan ini melalui pelatihan masif pada berbagai dataset, namun tetap sering kali belum mencapai tingkat pemahaman manusia dalam banyak skenario kompleks.
diff --git a/chapters/id/chapter1/3.mdx b/chapters/id/chapter1/3.mdx
new file mode 100644
index 000000000..0db136733
--- /dev/null
+++ b/chapters/id/chapter1/3.mdx
@@ -0,0 +1,381 @@
+# Transformer: Apa Saja yang Bisa Mereka Lakukan?[[transformers-what-can-they-do]]
+
+
+
+Di bagian ini, kita akan melihat apa saja yang bisa dilakukan oleh model Transformer dan menggunakan alat pertama dari modul 🤗 Transformers: fungsi `pipeline()`.
+
+
+👀 Lihat tombol Open in Colab di kanan atas? Klik tombol tersebut untuk membuka notebook Google Colab berisi seluruh contoh kode di bagian ini. Tombol ini akan tersedia di setiap bagian yang menyertakan contoh kode.
+
+Jika Anda ingin menjalankan contoh secara lokal, kami sarankan melihat bagian setup.
+
+
+## Transformer Ada di Mana-Mana![[transformers-are-everywhere]]
+
+Model Transformer digunakan untuk menyelesaikan berbagai jenis tugas lintas *modality* — termasuk NLP (pemrosesan bahasa alami), *computer vision*, pemrosesan audio, dan lainnya. Berikut adalah beberapa perusahaan dan organisasi yang menggunakan Hugging Face dan model Transformer, serta berkontribusi kembali ke komunitas dengan membagikan model mereka:
+
+
+
+modul [🤗 Transformers](https://github.com/huggingface/transformers) memungkinkan Anda membuat dan menggunakan model yang dibagikan di [Model Hub](https://huggingface.co/models), yang berisi jutaan model pra-latih siap digunakan. Anda juga dapat mengunggah model Anda sendiri ke Hub!
+
+
+⚠️ Hugging Face Hub tidak terbatas pada model Transformer saja. Siapa pun dapat membagikan model atau dataset apapun! Buat akun huggingface.co untuk mendapatkan semua fitur yang tersedia!
+
+
+Sebelum membahas bagaimana model Transformer bekerja secara internal, mari kita lihat contoh penggunaan praktisnya dalam menyelesaikan berbagai masalah NLP.
+
+## Bekerja dengan Pipeline[[working-with-pipelines]]
+
+
+
+Objek paling dasar di modul 🤗 Transformers adalah fungsi `pipeline()`. Fungsi ini menghubungkan model dengan langkah-langkah pra-pemrosesan dan pascapemrosesan yang dibutuhkan, sehingga Anda bisa langsung memasukkan teks dan mendapatkan hasil yang bermakna:
+
+```python
+from transformers import pipeline
+
+classifier = pipeline("sentiment-analysis")
+classifier("I've been waiting for a HuggingFace course my whole life.")
+```
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437}]
+```
+
+Anda bahkan bisa mengirim beberapa kalimat sekaligus:
+
+```python
+classifier(
+ ["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"]
+)
+```
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437},
+ {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
+```
+
+Secara default, pipeline ini menggunakan model pra-latih yang telah di-*fine-tune* untuk analisis sentimen dalam bahasa Inggris. Model akan diunduh dan disimpan saat Anda pertama kali menjalankan `classifier`. Saat dijalankan ulang, model dari cache akan digunakan.
+
+Tiga langkah utama ketika teks dikirim ke `pipeline()`:
+
+1. Teks dipra-proses menjadi format yang bisa dipahami model.
+2. Input yang telah diproses dikirim ke model.
+3. Prediksi dari model diproses ulang agar mudah dipahami.
+
+## Pipeline Tersedia untuk Berbagai Modality
+
+Fungsi `pipeline()` mendukung berbagai *modality*, seperti teks, gambar, audio, hingga tugas multimodal. Fokus utama dalam kursus ini adalah teks, tapi penting untuk mengenal potensi arsitektur Transformer secara luas.
+
+Berikut adalah ringkasannya:
+
+
+Untuk daftar lengkap dan terbaru, lihat [dokumentasi 🤗 Transformers](https://huggingface.co/docs/hub/en/models-tasks).
+
+
+### Pipeline Teks
+
+- `text-generation`: Menghasilkan teks dari sebuah prompt
+- `text-classification`: Mengklasifikasikan teks ke dalam kategori tertentu
+- `summarization`: Merangkum teks tanpa kehilangan informasi penting
+- `translation`: Menerjemahkan teks antar bahasa
+- `zero-shot-classification`: Mengklasifikasikan teks tanpa pelatihan spesifik pada label
+- `feature-extraction`: Mengambil representasi vektor dari teks
+
+### Pipeline Gambar
+
+- `image-to-text`: Menghasilkan deskripsi teks dari gambar
+- `image-classification`: Mengidentifikasi objek dalam gambar
+- `object-detection`: Menemukan dan mengenali objek dalam gambar
+
+### Pipeline Audio
+
+- `automatic-speech-recognition`: Mengubah ucapan menjadi teks
+- `audio-classification`: Mengklasifikasikan data audio
+- `text-to-speech`: Mengubah teks menjadi suara
+
+### Pipeline Multimodal
+
+- `image-text-to-text`: Memberi respons terhadap gambar berdasarkan prompt teks
+
+Mari kita eksplorasi beberapa pipeline ini secara lebih mendalam!
+
+## Zero-shot classification[[zero-shot-classification]]
+
+Kita akan mulai dengan tugas yang sedikit lebih menantang: mengklasifikasikan teks yang belum diberi label. Ini adalah kasus umum dalam proyek dunia nyata, karena proses anotasi teks memakan waktu dan membutuhkan keahlian khusus. Untuk kasus ini, pipeline `zero-shot-classification` sangat berguna: Anda dapat menentukan label apa pun yang ingin digunakan untuk klasifikasi — tanpa bergantung pada label bawaan dari model pra-latih.
+
+```python
+from transformers import pipeline
+
+classifier = pipeline("zero-shot-classification")
+classifier(
+ "This is a course about the Transformers library",
+ candidate_labels=["education", "politics", "business"],
+)
+```
+
+```python out
+{'sequence': 'This is a course about the Transformers library',
+ 'labels': ['education', 'business', 'politics'],
+ 'scores': [0.8445963859558105, 0.111976258456707, 0.043427448719739914]}
+```
+
+Pipeline ini disebut *zero-shot* karena Anda tidak perlu melakukan *fine-tuning* model dengan data Anda. Model ini langsung mengembalikan skor probabilitas untuk daftar label apa pun yang Anda tentukan!
+
+
+✏️ **Coba sendiri!** Bereksperimenlah dengan kalimat dan label buatan Anda untuk melihat bagaimana perilaku model berubah.
+
+
+## Text generation[[text-generation]]
+
+Sekarang mari kita lihat cara menggunakan pipeline untuk menghasilkan teks. Inti dari tugas ini adalah: Anda memberikan *prompt*, dan model akan melengkapinya dengan teks yang dihasilkan secara otomatis — mirip dengan fitur prediksi teks di ponsel. Karena melibatkan elemen acak, hasil Anda mungkin berbeda dari contoh berikut.
+
+```python
+from transformers import pipeline
+
+generator = pipeline("text-generation")
+generator("In this course, we will teach you how to")
+```
+
+```python out
+[{'generated_text': 'In this course, we will teach you how to understand and use '
+ 'data flow and data interchange when handling user data. We '
+ 'will be working with one or more of the most commonly used '
+ 'data flows — data flows of various types, as seen by the '
+ 'HTTP'}]
+```
+
+Gunakan argumen `num_return_sequences` untuk menentukan berapa banyak variasi teks yang ingin dihasilkan, dan `max_length` untuk menentukan panjang maksimum teks keluaran.
+
+
+✏️ **Coba sendiri!** Gunakan `num_return_sequences` dan `max_length` untuk menghasilkan dua kalimat dengan panjang masing-masing 15 kata.
+
+
+## Menggunakan Model Apa Pun dari Hub[[using-any-model-from-the-hub-in-a-pipeline]]
+
+Contoh sebelumnya menggunakan model default untuk setiap tugas. Namun, Anda juga dapat memilih model tertentu dari Model Hub untuk digunakan dalam pipeline. Misalnya, untuk tugas *text generation*, buka [Model Hub](https://huggingface.co/models), klik tag yang sesuai di panel kiri untuk memfilter model berdasarkan tugas. Contohnya: [text generation](https://huggingface.co/models?pipeline_tag=text-generation).
+
+Mari kita coba model [`HuggingFaceTB/SmolLM2-360M`](https://huggingface.co/HuggingFaceTB/SmolLM2-360M)! Berikut cara menggunakannya:
+
+```python
+from transformers import pipeline
+
+generator = pipeline("text-generation", model="HuggingFaceTB/SmolLM2-360M")
+generator(
+ "In this course, we will teach you how to",
+ max_length=30,
+ num_return_sequences=2,
+)
+```
+
+```python out
+[{'generated_text': 'In this course, we will teach you how to manipulate the world and '
+ 'move your mental and physical capabilities to your advantage.'},
+ {'generated_text': 'In this course, we will teach you how to become an expert and '
+ 'practice realtime, and with a hands on experience on both real '
+ 'time and real'}]
+```
+
+Anda bisa mempersempit pencarian dengan memilih tag bahasa tertentu untuk mencari model yang mendukung bahasa tersebut. Bahkan, terdapat model multibahasa di Hub!
+
+Setelah memilih model, Anda juga bisa langsung mencoba kemampuannya melalui widget interaktif di halaman model.
+
+
+✏️ **Coba sendiri!** Gunakan filter untuk menemukan model *text generation* dalam bahasa lain. Uji coba modelnya di widget sebelum digunakan dalam pipeline!
+
+
+### Inference Providers[[inference-providers]]
+
+Semua model dapat dicoba langsung melalui browser menggunakan layanan [Inference Providers](https://huggingface.co/docs/inference-providers/en/index). Cukup masukkan teks, dan lihat hasilnya secara langsung.
+
+Layanan ini juga tersedia sebagai produk berbayar — cocok untuk integrasi ke dalam alur kerja Anda. Lihat [halaman harga](https://huggingface.co/docs/inference-providers/en/pricing) untuk informasi lebih lanjut.
+
+## Pengisian Mask[[mask-filling]]
+
+Pipeline berikutnya adalah `fill-mask`. Tugas ini mengisi kata yang hilang dalam teks:
+
+```python
+from transformers import pipeline
+
+unmasker = pipeline("fill-mask")
+unmasker("This course will teach you all about models.", top_k=2)
+```
+
+```python out
+[{'sequence': 'This course will teach you all about mathematical models.',
+ 'score': 0.19619831442832947,
+ 'token': 30412,
+ 'token_str': ' mathematical'},
+ {'sequence': 'This course will teach you all about computational models.',
+ 'score': 0.04052725434303284,
+ 'token': 38163,
+ 'token_str': ' computational'}]
+```
+
+Argumen `top_k` menentukan berapa banyak kemungkinan yang ditampilkan. Perhatikan bahwa token `` adalah token khusus untuk mengindikasikan bagian yang perlu diisi. Token ini bisa berbeda di tiap model, jadi pastikan Anda mengetahui token yang tepat, misalnya dengan melihat widget di halaman model.
+
+
+✏️ **Coba sendiri!** Cari model `bert-base-cased` di Hub dan identifikasi token mask-nya di widget API. Apa prediksinya untuk kalimat di atas?
+
+
+## Named Entity Recognition[[named-entity-recognition]]
+
+*Named Entity Recognition* (NER) adalah tugas untuk mengenali bagian-bagian dari teks yang merupakan entitas seperti nama orang, lokasi, atau organisasi. Contohnya:
+
+```python
+from transformers import pipeline
+
+ner = pipeline("ner", grouped_entities=True)
+ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity_group': 'PER', 'score': 0.99816, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Model ini berhasil mengenali *Sylvain* sebagai orang (PER), *Hugging Face* sebagai organisasi (ORG), dan *Brooklyn* sebagai lokasi (LOC).
+
+Opsi `grouped_entities=True` menyatukan bagian-bagian yang tergolong entitas sama. Misalnya, “Hugging” dan “Face” digabung sebagai satu organisasi.
+
+
+✏️ **Coba sendiri!** Cari model yang mampu melakukan *part-of-speech tagging* (POS) dalam bahasa Inggris. Apa prediksinya untuk kalimat di atas?
+
+
+## Question Answering[[question-answering]]
+
+Pipeline `question-answering` memungkinkan Anda menjawab pertanyaan berdasarkan konteks yang diberikan:
+
+```python
+from transformers import pipeline
+
+question_answerer = pipeline("question-answering")
+question_answerer(
+ question="Where do I work?",
+ context="My name is Sylvain and I work at Hugging Face in Brooklyn",
+)
+```
+
+```python out
+{'score': 0.6385916471481323, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}
+```
+
+Pipeline ini tidak menghasilkan jawaban baru, tapi mengekstraknya dari konteks yang diberikan.
+
+## Ringkasan[[summarization]]
+
+Tugas *summarization* bertujuan menyederhanakan teks panjang menjadi ringkasan yang tetap mempertahankan inti informasinya:
+
+```python
+from transformers import pipeline
+
+summarizer = pipeline("summarization")
+summarizer(
+ """
+ Amerika telah mengalami perubahan besar dalam beberapa tahun terakhir.
+ Tidak hanya jumlah lulusan di disiplin teknik tradisional seperti teknik
+ mesin, sipil, listrik, kimia, dan aeronautika yang menurun, tetapi di
+ sebagian besar universitas unggulan Amerika, kurikulum teknik kini lebih
+ menekankan dan mendorong studi rekayasa sains. Akibatnya, mata kuliah teknik
+ yang berkaitan dengan infrastruktur, lingkungan, dan isu terkait semakin sedikit
+ ditawarkan, dan ada konsentrasi yang lebih besar pada mata kuliah teknologi tinggi
+ yang mendukung perkembangan ilmiah yang semakin kompleks. Meskipun yang terakhir
+ itu penting, seharusnya tidak mengorbankan teknik tradisional.
+
+ Negara dengan ekonomi yang berkembang pesat seperti Tiongkok dan India, serta negara
+ industri lain di Eropa dan Asia, terus mendorong dan mengembangkan pengajaran teknik.
+ Tiongkok dan India masing-masing meluluskan enam dan delapan kali lebih banyak insinyur
+ tradisional dibandingkan Amerika Serikat. Negara industri lainnya paling tidak
+ mempertahankan jumlah lulusan mereka, sementara Amerika mengalami penurunan serius
+ dalam jumlah lulusan teknik dan kekurangan insinyur yang berpendidikan baik.
+ """
+)
+```
+
+```python out
+[{'summary_text': ' Amerika telah mengalami perubahan besar dalam beberapa tahun terakhir. '
+ 'Jumlah lulusan teknik di AS menurun di bidang teknik tradisional seperti '
+ 'teknik mesin, sipil, listrik, kimia, dan aeronautika. Negara dengan ekonomi '
+ 'yang berkembang pesat seperti Tiongkok dan India, serta negara industri lain '
+ 'di Eropa dan Asia, terus mendorong dan mengembangkan bidang teknik.'}]
+```
+
+Seperti pada *text generation*, Anda bisa menetapkan `max_length` dan `min_length` hasil ringkasan.
+
+## Penerjemahan[[translation]]
+
+Untuk tugas penerjemahan, Anda bisa menggunakan model default dengan menamai tugas seperti `"translation_en_to_fr"`, atau pilih langsung model dari [Model Hub](https://huggingface.co/models). Berikut contoh penerjemahan dari Prancis ke Inggris:
+
+```python
+from transformers import pipeline
+
+translator = pipeline("translation", model="Helsinki-NLP/opus-mt-fr-en")
+translator("Ce cours est produit par Hugging Face.")
+```
+
+```python out
+[{'translation_text': 'This course is produced by Hugging Face.'}]
+```
+
+
+✏️ **Coba sendiri!** Cari model penerjemahan untuk bahasa lain dan coba terjemahkan kalimat di atas ke berbagai bahasa!
+
+
+## Pipeline Gambar dan Audio
+
+Selain teks, model Transformer juga bisa bekerja dengan gambar dan audio. Contoh:
+
+### Klasifikasi Gambar
+
+```python
+from transformers import pipeline
+
+image_classifier = pipeline(
+ task="image-classification", model="google/vit-base-patch16-224"
+)
+result = image_classifier(
+ "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg"
+)
+print(result)
+```
+
+### Automatic Speech Recognition
+
+```python
+from transformers import pipeline
+
+transcriber = pipeline(
+ task="automatic-speech-recognition", model="openai/whisper-large-v3"
+)
+result = transcriber(
+ "https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac"
+)
+print(result)
+```
+
+```python out
+{'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'}
+```
+
+## Menggabungkan Data dari Banyak Sumber
+
+Kekuatan utama model Transformer adalah kemampuannya menggabungkan dan memproses data dari berbagai sumber. Ini sangat bermanfaat jika Anda:
+
+1. Mencari informasi dari banyak basis data
+2. Menggabungkan berbagai format (teks, gambar, audio)
+3. Menyajikan informasi dari berbagai sumber secara terpadu
+
+Contohnya, Anda bisa membangun sistem yang:
+
+- Mencari data dari berbagai modalitas
+- Menggabungkan hasil pencarian menjadi satu respons
+- Menyajikan informasi relevan dari dokumen dan metadata
+
+## Kesimpulan
+
+Pipeline yang ditampilkan dalam bab ini digunakan sebagai demonstrasi. Mereka dirancang untuk tugas-tugas spesifik dan tidak dapat digunakan untuk variasi tugas lainnya secara langsung. Di bab selanjutnya, Anda akan belajar lebih dalam tentang apa yang ada di balik fungsi `pipeline()` dan bagaimana mengkustomisasinya.
diff --git a/chapters/id/chapter1/4.mdx b/chapters/id/chapter1/4.mdx
new file mode 100644
index 000000000..68c2b6e6d
--- /dev/null
+++ b/chapters/id/chapter1/4.mdx
@@ -0,0 +1,174 @@
+# Bagaimana Transformer Bekerja?[[how-do-transformers-work]]
+
+
+
+Di bagian ini, kita akan mempelajari arsitektur model Transformer dan memahami lebih dalam konsep seperti *attention*, arsitektur encoder-decoder, dan lainnya.
+
+
+
+🚀 Kita mulai naik tingkat di sini. Bagian ini bersifat teknis dan detail, jadi jangan khawatir jika Anda tidak langsung memahaminya sepenuhnya. Kita akan mengulas konsep-konsep ini kembali di bab-bab berikutnya.
+
+
+
+## Sekilas Sejarah Transformer[[a-bit-of-transformer-history]]
+
+Berikut beberapa titik penting dalam (sejarah singkat) model Transformer:
+
+
+
+
+
+
+Arsitektur [Transformer](https://arxiv.org/abs/1706.03762) diperkenalkan pada Juni 2017, dengan fokus awal pada tugas penerjemahan. Setelah itu, muncul berbagai model penting lainnya, seperti:
+
+- **Juni 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), model Transformer pra-latih pertama, digunakan untuk *fine-tuning* berbagai tugas NLP dan mencapai hasil terbaik saat itu
+- **Oktober 2018**: [BERT](https://arxiv.org/abs/1810.04805), model pra-latih besar lainnya yang dirancang untuk menghasilkan representasi kalimat yang lebih baik
+- **Februari 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), versi lebih besar dan lebih baik dari GPT, yang sempat tidak dirilis sepenuhnya karena pertimbangan etis
+- **Oktober 2019**: [T5](https://huggingface.co/papers/1910.10683), implementasi Transformer sequence-to-sequence untuk multi-tasking
+- **Mei 2020**: [GPT-3](https://huggingface.co/papers/2005.14165), versi lebih besar dari GPT-2 yang mampu menyelesaikan berbagai tugas tanpa *fine-tuning* (_zero-shot learning_)
+- **Januari 2022**: [InstructGPT](https://huggingface.co/papers/2203.02155), versi GPT-3 yang dilatih agar lebih mampu mengikuti instruksi
+- **Januari 2023**: [Llama](https://huggingface.co/papers/2302.13971), model bahasa besar multibahasa
+- **Maret 2023**: [Mistral](https://huggingface.co/papers/2310.06825), model 7 miliar parameter yang melampaui Llama 2 13B di semua tolok ukur
+- **Mei 2024**: [Gemma 2](https://huggingface.co/papers/2408.00118), keluarga model ringan berkinerja tinggi dengan teknik attention baru
+- **November 2024**: [SmolLM2](https://huggingface.co/papers/2502.02737), model kecil dengan performa tinggi untuk perangkat edge/mobile
+- Model tipe GPT (disebut juga sebagai Transformer model _auto-regressive_)
+- Model tipe BERT (disebut juga sebagai Transformer model _auto-encoding_)
+- Model tipe T5 (disebut juga sebagai Transformer model _sequence-to-sequence_)
+
+Kita akan membahas tiap keluarga ini lebih dalam nanti.
+
+## Transformer Adalah Model Bahasa[[transformers-are-language-models]]
+
+Semua model Transformer yang disebutkan sebelumnya (GPT, BERT, T5, dll.) dilatih sebagai *model bahasa*. Artinya, mereka dilatih menggunakan sejumlah besar teks mentah dengan pendekatan *self-supervised*.
+
+*Self-supervised learning* adalah metode pelatihan di mana tujuan pelatihan diturunkan langsung dari data masukan. Artinya, manusia tidak perlu memberikan label secara manual!
+
+Model ini mengembangkan pemahaman statistik terhadap bahasa yang dilatihkan, tetapi kurang bermanfaat untuk tugas praktis tertentu. Oleh karena itu, model pra-latih umum kemudian melalui proses yang disebut *transfer learning* atau *fine-tuning*, yaitu pelatihan tambahan secara *supervised* menggunakan label yang diberikan manusia untuk tugas tertentu.
+
+Contoh tugasnya adalah memprediksi kata berikutnya dalam kalimat setelah membaca kata-kata sebelumnya. Ini disebut *causal language modeling*, karena output tergantung pada input masa lalu dan saat ini — tetapi tidak pada masa depan.
+
+
+
+
+
+
+Contoh lain adalah *masked language modeling*, di mana model diminta memprediksi kata yang di-*masking* dalam sebuah kalimat.
+
+
+
+
+
+
+## Transformer Adalah Model Besar[[transformers-are-big-models]]
+
+Kecuali beberapa pengecualian (seperti DistilBERT), strategi umum untuk meningkatkan performa model adalah dengan **memperbesar ukuran model** dan **jumlah data pelatihan**.
+
+
+
+
+
+Sayangnya, melatih model besar memerlukan banyak data, waktu, dan sumber daya komputasi. Ini juga berdampak pada lingkungan, seperti ditunjukkan grafik berikut:
+
+
+
+
+
+
+
+
+Bahkan grafik di atas berasal dari proyek yang dilakukan dengan niat sadar untuk **mengurangi dampak lingkungan**. Bayangkan jika setiap tim peneliti, mahasiswa, atau perusahaan melatih model dari nol — biayanya akan sangat besar!
+
+Inilah mengapa **berbagi model sangat penting**: dengan berbagi *trained weights*, kita mengurangi beban komputasi dan jejak karbon secara global.
+
+Anda bisa menghitung jejak karbon model Anda menggunakan alat seperti [ML CO2 Impact](https://mlco2.github.io/impact/) atau [Code Carbon](https://codecarbon.io/), yang sudah terintegrasi dalam 🤗 Transformers. Baca [blog ini](https://huggingface.co/blog/carbon-emissions-on-the-hub) dan [dokumentasi resmi](https://huggingface.co/docs/hub/model-cards-co2) untuk mempelajari lebih lanjut.
+
+## Transfer Learning [[transfer-learning]]
+
+
+
+*Pretraining* adalah proses melatih model dari nol — bobot awalnya diinisialisasi secara acak dan tidak memiliki pengetahuan awal.
+
+
+
+
+
+
+Sebaliknya, *fine-tuning* dilakukan setelah model sudah dilatih. Kita mengambil model pra-latih dan melatih ulang dengan dataset spesifik untuk tugas kita. Kenapa tidak langsung latih model dari awal untuk tugas tersebut?
+
+- Model pra-latih sudah memiliki *pengetahuan umum* dari data besar sebelumnya.
+- *Fine-tuning* hanya membutuhkan data dalam jumlah kecil.
+- Waktu dan sumber daya yang dibutuhkan jauh lebih sedikit.
+- Hasil yang diperoleh umumnya lebih baik dibanding melatih dari nol.
+
+Contohnya, Anda bisa menggunakan model bahasa Inggris umum lalu *fine-tune* dengan korpus artikel arXiv untuk membuat model bahasa ilmiah.
+
+
+
+
+
+
+## Arsitektur Umum Transformer[[general-transformer-architecture]]
+
+Di bagian ini, kita membahas arsitektur umum model Transformer. Jangan khawatir jika belum paham sepenuhnya — kita akan bahas komponen-komponennya secara terpisah nanti.
+
+
+
+Model ini terdiri dari dua bagian utama:
+
+- **Encoder (kiri)**: Menerima masukan dan menghasilkan representasi fitur
+- **Decoder (kanan)**: Menggunakan representasi dari encoder untuk menghasilkan output
+
+
+
+
+
+
+Penggunaan tergantung pada tugasnya:
+
+- **Model encoder-only**: Cocok untuk klasifikasi kalimat, NER
+- **Model decoder-only**: Cocok untuk generasi teks
+- **Model encoder-decoder** (sequence-to-sequence): Cocok untuk penerjemahan, rangkuman
+
+## Lapisan Attention[[attention-layers]]
+
+Fitur utama dari Transformer adalah **lapisan attention (attention layers)**. Bahkan, judul makalah aslinya adalah ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762)!
+
+Singkatnya, lapisan ini memungkinkan model fokus hanya pada kata-kata yang relevan saat membentuk representasi kata.
+
+Contoh: saat menerjemahkan "You like this course" ke dalam bahasa Prancis, model harus memperhatikan subjek "You" saat menerjemahkan "like", karena bentuk kata kerja tergantung subjek. Saat menerjemahkan "this", model juga harus memperhatikan kata benda "course", karena jenis kelamin kata dalam bahasa Prancis memengaruhi terjemahan.
+
+Intinya: makna kata tergantung konteks — dan attention memungkinkan model memanfaatkan konteks tersebut.
+
+## Arsitektur Asli[[the-original-architecture]]
+
+Arsitektur awal Transformer dirancang untuk penerjemahan.
+
+- **Encoder** menerima kalimat sumber (misalnya, bahasa Inggris)
+- **Decoder** menghasilkan kalimat target (misalnya, bahasa Prancis)
+
+Selama pelatihan, decoder diberi seluruh kalimat target, tapi *dilarang melihat kata-kata masa depan*. Contohnya: saat memprediksi kata ke-4, decoder hanya melihat kata ke-1 hingga ke-3.
+
+
+
+
+
+
+Lapisan attention pertama di decoder melihat semua input sebelumnya, sedangkan attention kedua menggunakan output dari encoder.
+
+Masking juga digunakan untuk menghindari perhatian ke token spesial seperti padding.
+
+## Arsitektur vs. Checkpoint[[architecture-vs-checkpoints]]
+
+Selama mempelajari Transformer, Anda akan menemukan istilah:
+
+- **Architecture**: Rangka desain model — struktur layer dan operasi
+- **Checkpoint**: Bobot (weights) hasil pelatihan untuk arsitektur tertentu
+- **Model**: Istilah umum yang bisa merujuk ke architecture atau checkpoint
+
+Contoh:
+- `BERT` → adalah arsitektur
+- `bert-base-cased` → adalah checkpoint hasil pelatihan oleh Google
+- "Model BERT" → bisa merujuk ke keduanya tergantung konteks
diff --git a/chapters/id/chapter1/5.mdx b/chapters/id/chapter1/5.mdx
new file mode 100644
index 000000000..1d8fab051
--- /dev/null
+++ b/chapters/id/chapter1/5.mdx
@@ -0,0 +1,261 @@
+# Bagaimana 🤗 Transformers Menyelesaikan Tugas
+
+
+
+Pada bagian [Transformers, apa yang bisa mereka lakukan?](/course/chapter1/3), Anda telah mempelajari tentang pemrosesan bahasa alami (NLP), audio dan suara, tugas visi komputer, serta beberapa aplikasi penting dari teknologi tersebut. Halaman ini akan membahas lebih dalam bagaimana model menyelesaikan tugas-tugas tersebut dan menjelaskan apa yang terjadi di balik layar. Ada banyak cara untuk menyelesaikan sebuah tugas; beberapa model mungkin menggunakan teknik tertentu atau bahkan pendekatan baru. Namun, untuk model Transformer, ide umumnya sama. Karena arsitekturnya fleksibel, sebagian besar model merupakan variasi dari struktur encoder, decoder, atau encoder-decoder.
+
+
+
+Sebelum mempelajari varian arsitektur secara spesifik, penting untuk memahami bahwa sebagian besar tugas mengikuti pola yang serupa: data input diproses oleh model, lalu output diinterpretasikan untuk tugas tertentu. Perbedaannya terletak pada bagaimana data dipersiapkan, varian arsitektur model yang digunakan, dan bagaimana output diproses.
+
+
+
+Untuk menjelaskan bagaimana tugas diselesaikan, kita akan menelusuri apa yang terjadi di dalam model hingga menghasilkan prediksi yang berguna. Kita akan membahas model berikut beserta tugasnya:
+
+- [Wav2Vec2](https://huggingface.co/docs/transformers/model_doc/wav2vec2) untuk klasifikasi audio dan pengenalan ucapan otomatis (ASR)
+- [Vision Transformer (ViT)](https://huggingface.co/docs/transformers/model_doc/vit) dan [ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext) untuk klasifikasi gambar
+- [DETR](https://huggingface.co/docs/transformers/model_doc/detr) untuk deteksi objek
+- [Mask2Former](https://huggingface.co/docs/transformers/model_doc/mask2former) untuk segmentasi gambar
+- [GLPN](https://huggingface.co/docs/transformers/model_doc/glpn) untuk estimasi kedalaman
+- [BERT](https://huggingface.co/docs/transformers/model_doc/bert) untuk tugas NLP seperti klasifikasi teks, klasifikasi token, dan menjawab pertanyaan (menggunakan encoder)
+- [GPT2](https://huggingface.co/docs/transformers/model_doc/gpt2) untuk tugas NLP seperti generasi teks (menggunakan decoder)
+- [BART](https://huggingface.co/docs/transformers/model_doc/bart) untuk tugas NLP seperti ringkasan dan terjemahan (menggunakan encoder-decoder)
+
+
+
+Sebelum melangkah lebih jauh, akan sangat membantu jika Anda memahami dasar arsitektur Transformer. Mengetahui cara kerja encoder, decoder, dan attention akan membantu Anda memahami bagaimana berbagai model Transformer bekerja. Pastikan untuk membaca [bagian sebelumnya](https://huggingface.co/course/chapter1/4?fw=pt) untuk informasi lebih lanjut!
+
+
+
+## Model Transformer untuk Bahasa
+
+Model bahasa berada di pusat perkembangan NLP modern. Model ini dirancang untuk memahami dan menghasilkan bahasa manusia dengan mempelajari pola dan hubungan statistik antara kata atau token dalam teks.
+
+Transformer awalnya dirancang untuk terjemahan mesin, namun sejak itu telah menjadi arsitektur standar untuk hampir semua tugas AI. Beberapa tugas lebih cocok diselesaikan dengan struktur encoder, lainnya dengan decoder, dan beberapa lagi dengan kombinasi encoder-decoder.
+
+### Bagaimana Model Bahasa Bekerja
+
+Model bahasa bekerja dengan dilatih untuk memprediksi kemungkinan suatu kata berdasarkan konteks kata-kata di sekitarnya. Hal ini memberikan pemahaman dasar tentang bahasa yang dapat digeneralisasi ke berbagai tugas lain.
+
+Ada dua pendekatan utama untuk melatih model Transformer:
+
+1. **Masked language modeling (MLM)**: Digunakan oleh model encoder seperti BERT. Dalam pendekatan ini, beberapa token dalam input disamarkan secara acak, dan model dilatih untuk memprediksi token asli berdasarkan konteks sekitarnya. Ini memungkinkan model mempelajari konteks dua arah (melihat ke depan dan ke belakang).
+
+2. **Causal language modeling (CLM)**: Digunakan oleh model decoder seperti GPT. Pendekatan ini memprediksi token berikutnya berdasarkan semua token sebelumnya. Model hanya boleh menggunakan konteks dari kiri untuk memprediksi token berikutnya.
+
+### Jenis Model Bahasa
+
+Dalam pustaka Transformers, model bahasa umumnya dikategorikan dalam tiga jenis arsitektur:
+
+1. **Model encoder-saja** (misalnya BERT): Menggunakan pendekatan dua arah untuk memahami konteks dari dua arah. Cocok untuk tugas yang membutuhkan pemahaman mendalam seperti klasifikasi, pengenalan entitas bernama, dan menjawab pertanyaan.
+
+2. **Model decoder-saja** (misalnya GPT, Llama): Memproses teks dari kiri ke kanan dan sangat baik untuk tugas generasi teks. Dapat menyelesaikan kalimat, menulis esai, atau bahkan menghasilkan kode dari prompt.
+
+3. **Model encoder-decoder** (misalnya T5, BART): Menggabungkan kedua pendekatan — encoder untuk memahami input dan decoder untuk menghasilkan output. Cocok untuk tugas sequence-to-sequence seperti terjemahan, ringkasan, dan tanya jawab.
+
+
+
+Seperti yang dijelaskan di bagian sebelumnya, model bahasa umumnya dilatih sebelumnya pada jumlah besar data teks secara self-supervised (tanpa anotasi manusia), kemudian disempurnakan (fine-tuned) untuk tugas spesifik. Pendekatan ini, yang disebut pembelajaran transfer (transfer learning), memungkinkan model beradaptasi dengan berbagai tugas NLP dengan data tambahan yang relatif sedikit.
+
+Di bagian selanjutnya, kita akan mengeksplorasi arsitektur model secara spesifik dan bagaimana mereka digunakan untuk tugas-tugas di bidang suara, visi, dan teks.
+
+
+
+Memahami bagian mana dari arsitektur Transformer (encoder, decoder, atau keduanya) yang paling cocok untuk suatu tugas NLP sangat penting untuk memilih model yang tepat. Umumnya, tugas yang memerlukan konteks dua arah menggunakan encoder, tugas yang menghasilkan teks menggunakan decoder, dan tugas sequence-to-sequence menggunakan encoder-decoder.
+
+
+
+### Generasi Teks
+
+Generasi teks melibatkan pembuatan teks yang koheren dan relevan dengan konteks berdasarkan sebuah prompt atau input.
+
+[GPT-2](https://huggingface.co/docs/transformers/model_doc/gpt2) adalah model decoder-saja yang telah dilatih sebelumnya (pretrained) pada sejumlah besar data teks. GPT-2 dapat menghasilkan teks yang meyakinkan (meskipun tidak selalu benar!) dari sebuah prompt dan bahkan mampu menyelesaikan tugas NLP lain seperti menjawab pertanyaan, meskipun tidak dilatih secara eksplisit untuk itu.
+
+
+
+
+
+1. GPT-2 menggunakan [byte pair encoding (BPE)](https://huggingface.co/docs/transformers/tokenizer_summary#bytepair-encoding-bpe) untuk melakukan tokenisasi kata dan menghasilkan embedding token. Encoding posisi ditambahkan ke token embeddings untuk menunjukkan posisi setiap token dalam urutan. Embedding ini diteruskan melalui beberapa blok decoder untuk menghasilkan *hidden state* akhir. Dalam setiap blok decoder, GPT-2 menggunakan *masked self-attention* yang berarti GPT-2 tidak dapat memperhatikan token di masa depan — hanya token sebelumnya yang diperbolehkan.
+
+2. Output dari decoder diteruskan ke kepala model bahasa, yang melakukan transformasi linier untuk mengubah hidden states menjadi logits. Label target adalah token berikutnya dalam urutan, yang dibuat dengan menggeser logits satu posisi ke kanan. Kemudian, *cross-entropy loss* dihitung antara logits dan label untuk memprediksi token berikutnya yang paling mungkin.
+
+Tujuan pelatihan GPT-2 sepenuhnya berdasarkan [causal language modeling](https://huggingface.co/docs/transformers/glossary#causal-language-modeling), yaitu memprediksi kata berikutnya dalam urutan. Ini membuat GPT-2 sangat baik untuk tugas-tugas yang melibatkan generasi teks.
+
+Siap mencoba sendiri generasi teks? Lihat panduan lengkap [causal language modeling](https://huggingface.co/docs/transformers/tasks/language_modeling#causal-language-modeling) untuk belajar cara fine-tune DistilGPT-2 dan menggunakannya untuk inferensi!
+
+
+
+Untuk informasi lebih lanjut tentang generasi teks, lihat panduan [strategi generasi teks](generation_strategies)!
+
+
+
+### Klasifikasi Teks
+
+Klasifikasi teks melibatkan pemberian kategori pada dokumen teks, seperti analisis sentimen, klasifikasi topik, atau deteksi spam.
+
+[BERT](https://huggingface.co/docs/transformers/model_doc/bert) adalah model encoder-saja dan merupakan model pertama yang secara efektif mengimplementasikan bidirectional learning untuk memahami representasi teks secara mendalam dengan memperhatikan kata-kata di kedua sisi.
+
+1. BERT menggunakan tokenisasi [WordPiece](https://huggingface.co/docs/transformers/tokenizer_summary#wordpiece) untuk menghasilkan embedding dari teks. Untuk membedakan antara satu kalimat dan pasangan kalimat, token khusus `[SEP]` ditambahkan. Token `[CLS]` ditambahkan di awal setiap urutan teks. Output akhir dengan token `[CLS]` digunakan sebagai input ke *classification head*. BERT juga menambahkan segment embedding untuk menunjukkan apakah token milik kalimat pertama atau kedua.
+
+2. BERT dilatih dengan dua tujuan utama: masked language modeling dan next-sentence prediction. Dalam masked language modeling, sebagian token disamarkan secara acak, dan model harus memprediksi token yang disamarkan. Dalam next-sentence prediction, model harus memprediksi apakah kalimat B benar-benar mengikuti kalimat A.
+
+3. Embedding input diteruskan melalui beberapa lapisan encoder untuk menghasilkan *hidden state* akhir.
+
+Untuk menggunakan model pretrained BERT dalam klasifikasi teks, tambahkan *classification head* di atas model dasar BERT. Head ini adalah lapisan linier yang menerima hidden state terakhir dan mengubahnya menjadi logits. Cross-entropy loss dihitung antara logits dan label target untuk memprediksi label paling mungkin.
+
+Siap mencoba klasifikasi teks? Lihat panduan lengkap [text classification](https://huggingface.co/docs/transformers/tasks/sequence_classification) untuk belajar cara fine-tune DistilBERT dan menggunakannya untuk inferensi!
+
+### Klasifikasi Token
+
+Klasifikasi token melibatkan pemberian label pada setiap token dalam sebuah urutan, seperti pada pengenalan entitas bernama (NER) atau penandaan part-of-speech.
+
+Untuk menggunakan BERT dalam tugas seperti NER, tambahkan *token classification head* di atas model dasar BERT. Head ini adalah lapisan linier yang menerima *hidden state* terakhir dan mengubahnya menjadi logits. Cross-entropy loss dihitung antara logits dan label setiap token.
+
+Siap mencoba klasifikasi token? Lihat panduan lengkap [token classification](https://huggingface.co/docs/transformers/tasks/token_classification) untuk belajar cara fine-tune DistilBERT dan menggunakannya untuk inferensi!
+
+### Menjawab Pertanyaan
+
+Tugas menjawab pertanyaan melibatkan menemukan jawaban dari suatu pertanyaan berdasarkan konteks atau teks yang disediakan.
+
+Untuk menggunakan BERT dalam tugas tanya jawab, tambahkan *span classification head* di atas model BERT. Lapisan linier ini menghitung logits awal dan akhir dari bagian teks yang mengandung jawaban. Cross-entropy loss dihitung antara logits dan posisi label untuk menemukan rentang teks paling mungkin sebagai jawaban.
+
+Siap mencoba menjawab pertanyaan? Lihat panduan lengkap [question answering](https://huggingface.co/docs/transformers/tasks/question_answering) untuk belajar cara fine-tune DistilBERT dan menggunakannya untuk inferensi!
+
+
+
+💡 Perhatikan betapa mudahnya menggunakan BERT untuk berbagai tugas setelah model dilatih sebelumnya. Anda hanya perlu menambahkan *head* tertentu untuk memanipulasi *hidden states* menjadi output yang diinginkan!
+
+
+
+### Ringkasan Teks (Summarization)
+
+Ringkasan melibatkan merangkum teks panjang menjadi versi yang lebih pendek sambil tetap mempertahankan informasi dan makna penting.
+
+Model encoder-decoder seperti [BART](https://huggingface.co/docs/transformers/model_doc/bart) dan [T5](model_doc/t5) dirancang untuk pola *sequence-to-sequence* seperti tugas ringkasan.
+
+
+
+
+
+1. Arsitektur encoder BART mirip dengan BERT dan menerima token serta positional embedding dari teks. BART dilatih dengan merusak input dan kemudian membangunnya kembali dengan decoder. Strategi korupsi teks terbaik yang digunakan adalah *text infilling*, di mana sejumlah span teks diganti dengan satu token `[mask]`. Ini mengajarkan model untuk memprediksi jumlah dan isi token yang hilang.
+
+2. Output encoder diteruskan ke decoder yang harus memprediksi token yang disamarkan dan token utuh lainnya. Output dari decoder dikirim ke *language modeling head* untuk mengubah *hidden states* menjadi logits. Cross-entropy loss dihitung antara logits dan label (token yang digeser ke kanan).
+
+Siap mencoba ringkasan teks? Lihat panduan lengkap [summarization](https://huggingface.co/docs/transformers/tasks/summarization) untuk belajar cara fine-tune T5 dan menggunakannya untuk inferensi!
+
+
+
+Untuk informasi lebih lanjut tentang generasi teks, lihat panduan [strategi generasi teks](https://huggingface.co/docs/transformers/generation_strategies)!
+
+
+
+### Terjemahan
+
+Terjemahan melibatkan mengubah teks dari satu bahasa ke bahasa lain sambil tetap mempertahankan maknanya. Ini juga merupakan tugas sequence-to-sequence, sehingga Anda bisa menggunakan model encoder-decoder seperti [BART](https://huggingface.co/docs/transformers/model_doc/bart) atau [T5](model_doc/t5).
+
+BART dapat disesuaikan untuk terjemahan dengan menambahkan encoder baru yang diinisialisasi secara acak untuk memetakan bahasa sumber ke input yang bisa diterjemahkan ke bahasa target. Encoder ini dilatih dengan cross-entropy loss dari output model. Setelah itu, semua parameter model dilatih bersama. BART kemudian dikembangkan menjadi versi multibahasa, yaitu mBART.
+
+Siap mencoba terjemahan? Lihat panduan lengkap [translation](https://huggingface.co/docs/transformers/tasks/translation) untuk belajar cara fine-tune T5 dan menggunakannya untuk inferensi!
+
+
+
+Seperti yang Anda lihat, banyak model mengikuti pola yang serupa meskipun digunakan untuk tugas berbeda. Memahami pola umum ini akan membantu Anda lebih cepat memahami model baru dan menyesuaikannya dengan kebutuhan Anda.
+
+
+
+## Modalitas Selain Teks
+
+Transformer tidak hanya terbatas pada teks. Mereka juga dapat diterapkan pada modalitas lain seperti suara dan audio, gambar, bahkan video. Dalam kursus ini kita akan fokus pada teks, namun kita juga akan memperkenalkan secara singkat modalitas lainnya.
+
+### Suara dan Audio
+
+Mari kita mulai dengan bagaimana model Transformer menangani data suara dan audio, yang menghadirkan tantangan unik dibandingkan teks atau gambar.
+
+[Whisper](https://huggingface.co/docs/transformers/main/en/model_doc/whisper) adalah model encoder-decoder (sequence-to-sequence) yang dilatih sebelumnya pada 680.000 jam data audio berlabel. Skala pretraining yang besar ini memungkinkan Whisper memiliki kemampuan zero-shot pada tugas audio dalam bahasa Inggris dan banyak bahasa lain. Decoder memungkinkan Whisper mengubah representasi suara yang dipelajari menjadi output teks tanpa fine-tuning tambahan. Whisper dapat langsung digunakan begitu saja.
+
+
+
+
+
+Diagram diambil dari [makalah Whisper](https://huggingface.co/papers/2212.04356).
+
+Model ini memiliki dua komponen utama:
+
+1. **Encoder** memproses input audio. Audio mentah diubah terlebih dahulu menjadi log-Mel spectrogram. Spectrogram ini kemudian diteruskan ke jaringan Transformer encoder.
+
+2. **Decoder** mengambil representasi audio yang telah dienkode dan memprediksi token teks secara autoregressive. Decoder adalah Transformer standar yang dilatih untuk memprediksi token teks berikutnya berdasarkan token sebelumnya dan output encoder. Token khusus digunakan di awal input decoder untuk mengarahkan model ke tugas tertentu seperti transkripsi, terjemahan, atau identifikasi bahasa.
+
+Whisper dilatih sebelumnya pada dataset audio beranotasi yang sangat besar dan beragam dari internet. Pretraining berskala besar ini memungkinkan Whisper memiliki performa zero-shot yang kuat pada banyak bahasa dan tugas.
+
+Setelah pretraining, Anda dapat langsung menggunakan Whisper untuk inferensi zero-shot, atau melakukan fine-tuning pada data Anda untuk hasil yang lebih baik pada tugas tertentu seperti ASR (automatic speech recognition) atau terjemahan suara.
+
+
+
+Inovasi utama dari Whisper adalah pelatihannya yang menggunakan skala data audio yang sangat besar dan beragam dari internet secara weakly-supervised. Ini memungkinkan model melakukan generalisasi luar biasa pada berbagai bahasa, aksen, dan tugas — bahkan tanpa fine-tuning spesifik.
+
+
+
+### Pengenalan Ucapan Otomatis (ASR)
+
+Untuk menggunakan model Whisper yang telah dilatih sebelumnya dalam tugas ASR, kita memanfaatkan struktur encoder-decoder secara penuh. Encoder memproses input audio, dan decoder menghasilkan transkrip secara token demi token. Saat fine-tuning, model biasanya dilatih menggunakan loss sequence-to-sequence standar (seperti cross-entropy) untuk memprediksi token teks yang benar dari input audio.
+
+Cara termudah menggunakan model yang sudah di-finetune untuk inferensi adalah melalui `pipeline`.
+
+```python
+from transformers import pipeline
+
+transcriber = pipeline(
+ task="automatic-speech-recognition", model="openai/whisper-base.en"
+)
+transcriber("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac")
+# Output: {'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'}
+```
+
+Siap mencoba pengenalan suara otomatis? Lihat panduan lengkap [ASR](https://huggingface.co/docs/transformers/tasks/asr) untuk belajar bagaimana fine-tune Whisper dan menggunakannya untuk inferensi!
+
+### Visi Komputer
+
+Selanjutnya, mari kita bahas tugas-tugas visi komputer, yang berhubungan dengan memahami dan menginterpretasikan informasi visual dari gambar atau video.
+
+Ada dua pendekatan utama dalam visi komputer menggunakan Transformer:
+
+1. Membagi gambar menjadi urutan patch, kemudian memprosesnya secara paralel dengan Transformer.
+2. Menggunakan CNN modern seperti [ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext), yang tetap menggunakan layer konvolusi namun dengan desain jaringan modern.
+
+
+
+Pendekatan ketiga menggabungkan Transformer dengan konvolusi (misalnya [Convolutional Vision Transformer](https://huggingface.co/docs/transformers/model_doc/cvt) atau [LeViT](https://huggingface.co/docs/transformers/model_doc/levit)). Namun, kita tidak membahasnya di sini karena pada dasarnya mereka hanya menggabungkan dua pendekatan sebelumnya.
+
+
+
+ViT dan ConvNeXT umumnya digunakan untuk klasifikasi gambar, sedangkan untuk tugas visi lain seperti deteksi objek, segmentasi, dan estimasi kedalaman, kita akan menggunakan model seperti DETR, Mask2Former, dan GLPN.
+
+### Klasifikasi Gambar
+
+Klasifikasi gambar adalah salah satu tugas dasar dalam visi komputer. Mari kita lihat bagaimana berbagai arsitektur model menyelesaikan masalah ini.
+
+ViT dan ConvNeXT keduanya bisa digunakan untuk klasifikasi gambar, dengan perbedaan utama bahwa ViT menggunakan attention mechanism sedangkan ConvNeXT menggunakan konvolusi.
+
+[ViT](https://huggingface.co/docs/transformers/model_doc/vit) menggantikan seluruh konvolusi dengan arsitektur Transformer murni. Jika Anda sudah memahami Transformer asli, maka Anda hampir memahami ViT.
+
+
+
+
+
+Perubahan utama pada ViT terletak pada cara gambar diubah menjadi input untuk Transformer:
+
+1. Gambar dibagi menjadi patch persegi yang tidak tumpang tindih, masing-masing diubah menjadi vektor atau *patch embedding*. Patch embedding dihasilkan dari layer konvolusi 2D yang membuat dimensi input sesuai (misalnya 768 nilai per patch). Gambar 224x224 dapat dibagi menjadi 196 patch 16x16. Seperti teks yang di-tokenisasi, gambar diubah menjadi urutan patch.
+
+2. *Embedding yang dapat dilatih* — token khusus `[CLS]` — ditambahkan di awal urutan patch, mirip seperti BERT. Hidden state akhir dari token `[CLS]` digunakan sebagai input ke kepala klasifikasi.
+
+3. *Position embedding* ditambahkan karena model tidak tahu urutan spasial patch gambar. Embedding posisi ini juga dapat dilatih dan memiliki ukuran yang sama dengan patch embedding. Semua embedding kemudian diteruskan ke encoder Transformer.
+
+4. Output, khususnya hidden state dari `[CLS]`, diteruskan ke multilayer perceptron (MLP) head. Tujuan pretraining ViT adalah klasifikasi. MLP mengubah output menjadi logits untuk label kelas, lalu menghitung cross-entropy loss untuk menentukan kelas yang paling mungkin.
+
+Siap mencoba klasifikasi gambar? Lihat panduan lengkap [image classification](https://huggingface.co/docs/transformers/tasks/image_classification) untuk belajar bagaimana fine-tune ViT dan menggunakannya untuk inferensi!
+
+
+
+Ingat: meskipun tugas-tugas yang berbeda memerlukan pendekatan khusus, sebagian besar model Transformer mengikuti pola arsitektur dan pelatihan yang serupa. Dengan memahami pola umum tersebut, Anda akan lebih mudah belajar dan menerapkan model baru.
+
+
\ No newline at end of file
diff --git a/chapters/id/chapter1/6.mdx b/chapters/id/chapter1/6.mdx
new file mode 100644
index 000000000..54d0dd201
--- /dev/null
+++ b/chapters/id/chapter1/6.mdx
@@ -0,0 +1,181 @@
+
+
+# Arsitektur Transformer[[transformer-architectures]]
+
+Di bagian sebelumnya, kita telah memperkenalkan arsitektur umum Transformer dan menjelajahi bagaimana model-model ini menyelesaikan berbagai tugas. Sekarang, mari kita lihat lebih dekat tiga varian arsitektur utama dari model Transformer dan pahami kapan sebaiknya menggunakan masing-masing arsitektur.
+
+
+Ingat bahwa sebagian besar model Transformer menggunakan salah satu dari tiga arsitektur: encoder-only, decoder-only, atau encoder-decoder (sequence-to-sequence). Memahami perbedaan ini akan membantu kamu memilih model yang tepat untuk tugas tertentu.
+
+
+## Model Encoder[[encoder-models]]
+
+
+
+Model encoder hanya menggunakan bagian encoder dari arsitektur Transformer. Pada setiap tahap, lapisan atensi dapat mengakses seluruh kata dalam kalimat awal. Model ini sering disebut memiliki atensi "bi-directional", dan dikenal sebagai *auto-encoding models*.
+
+Pretraining biasanya dilakukan dengan merusak kalimat (misalnya dengan memask kata secara acak), lalu meminta model untuk memulihkan kalimat aslinya.
+
+Model encoder sangat cocok untuk tugas-tugas yang membutuhkan pemahaman penuh terhadap kalimat, seperti klasifikasi kalimat, named entity recognition, dan question answering ekstraktif.
+
+
+Seperti yang kita lihat di [Bagaimana 🤗 Transformers menyelesaikan tugas](/chapter1/5), model encoder seperti BERT sangat baik dalam memahami teks karena dapat melihat konteks dari dua arah. Ini membuatnya ideal untuk tugas-tugas yang membutuhkan pemahaman menyeluruh terhadap input.
+
+
+Contoh model:
+
+- [BERT](https://huggingface.co/docs/transformers/model_doc/bert)
+- [DistilBERT](https://huggingface.co/docs/transformers/model_doc/distilbert)
+- [ModernBERT](https://huggingface.co/docs/transformers/en/model_doc/modernbert)
+
+## Model Decoder[[decoder-models]]
+
+
+
+Model decoder hanya menggunakan bagian decoder dari Transformer. Setiap kata hanya dapat melihat kata-kata sebelumnya dalam kalimat. Model ini dikenal sebagai *auto-regressive models*.
+
+Pretraining biasanya melibatkan prediksi kata berikutnya dalam kalimat.
+
+Model ini sangat cocok untuk tugas-tugas yang melibatkan generasi teks.
+
+
+Model decoder seperti GPT dirancang untuk menghasilkan teks dengan memprediksi satu token pada satu waktu. Seperti yang telah kita bahas di [Bagaimana 🤗 Transformers menyelesaikan tugas](/chapter1/5), model ini hanya dapat melihat token-token sebelumnya, yang membuatnya sangat baik untuk generasi teks kreatif, namun kurang ideal untuk tugas-tugas yang membutuhkan pemahaman dua arah.
+
+
+Contoh model:
+
+- [SmolLM Series](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct)
+- [LLaMA](https://huggingface.co/docs/transformers/en/model_doc/llama4)
+- [Gemma](https://huggingface.co/docs/transformers/main/en/model_doc/gemma3)
+- [DeepSeek V3](https://huggingface.co/deepseek-ai/DeepSeek-V3)
+
+### Large Language Models (LLMs) Modern
+
+Sebagian besar LLM saat ini menggunakan arsitektur decoder-only. Model ini mengalami peningkatan pesat baik dari segi ukuran maupun kemampuan.
+
+#### Dua fase pelatihan:
+
+1. **Pretraining** – belajar memprediksi token berikutnya
+2. **Instruction tuning** – dilatih untuk mengikuti instruksi manusia
+
+#### Kemampuan umum LLM saat ini:
+
+| Kemampuan | Deskripsi | Contoh |
+|-----------|-----------|--------|
+| Generasi teks | Menulis teks relevan & koheren | Menulis cerita, esai |
+| Ringkasan | Meringkas dokumen panjang | Ringkasan eksekutif |
+| Terjemahan | Bahasa-ke-bahasa | Inggris ke Spanyol |
+| QA | Jawaban fakta | "Apa ibu kota Prancis?" |
+| Generasi kode | Membuat fungsi dari deskripsi | Kode Python |
+| Penalaran | Menyelesaikan langkah demi langkah | Masalah logika |
+| Few-shot learning | Belajar dari 2–3 contoh | Klasifikasi teks |
+
+Anda dapat bereksperimen dengan LLM berbasis decoder langsung di peramban Anda melalui halaman repositori model di Hub. Berikut contoh dengan [GPT-2](https://huggingface.co/openai-community/gpt2), model open source klasik dari OpenAI:
+
+
+
+## Model Sequence-to-Sequence[[sequence-to-sequence-models]]
+
+
+
+Model encoder-decoder (atau *sequence-to-sequence*) menggunakan kedua bagian arsitektur Transformer. Encoder melihat seluruh input, sementara decoder hanya melihat token sebelumnya.
+
+Contoh pretraining: T5 mengganti span teks dengan token [MASK], dan model diminta memulihkan teks tersebut.
+
+Model ini sangat cocok untuk tugas yang melibatkan transformasi teks ke teks, seperti ringkasan, terjemahan, atau QA generatif.
+
+
+Model seperti BART dan T5 menggabungkan kekuatan dua arsitektur: pemahaman input (encoder) dan generasi output (decoder). Ini membuat mereka unggul dalam tugas seperti ringkasan dan terjemahan.
+
+
+### Aplikasi praktis:
+
+| Aplikasi | Deskripsi | Contoh model |
+|----------|-----------|--------------|
+| Terjemahan | Bahasa-ke-bahasa | Marian, T5 |
+| Ringkasan | Meringkas teks panjang | BART, T5 |
+| Data-to-text | Data terstruktur → teks | T5 |
+| Koreksi tata bahasa | Memperbaiki kalimat | T5 |
+| QA generatif | Jawaban berdasarkan konteks | BART, T5 |
+
+
+
+Contoh model:
+
+- [BART](https://huggingface.co/docs/transformers/model_doc/bart)
+- [mBART](https://huggingface.co/docs/transformers/model_doc/mbart)
+- [Marian](https://huggingface.co/docs/transformers/model_doc/marian)
+- [T5](https://huggingface.co/docs/transformers/model_doc/t5)
+
+## Memilih Arsitektur yang Tepat[[choosing-the-right-architecture]]
+
+| Tugas | Arsitektur | Contoh Model |
+|-------|-------------|--------------|
+| Klasifikasi teks | Encoder | BERT, RoBERTa |
+| Generasi teks | Decoder | GPT, LLaMA |
+| Terjemahan | Encoder-Decoder | T5, BART |
+| Ringkasan | Encoder-Decoder | BART, T5 |
+| Named Entity Recognition | Encoder | BERT, RoBERTa |
+| QA ekstraktif | Encoder | BERT |
+| QA generatif | Decoder atau Seq2Seq | GPT, T5 |
+| Chatbot | Decoder | GPT, LLaMA |
+
+
+Saat memilih model:
+1. Apakah tugasmu membutuhkan pemahaman dua arah?
+2. Apakah kamu menganalisis teks atau membuat teks baru?
+3. Apakah kamu mentransformasi satu bentuk teks ke bentuk lain?
+
+Jawaban-jawaban ini akan membantumu memilih arsitektur yang tepat.
+
+
+## Mekanisme Attention [[attention-mechanisms]]
+
+Sebagian besar model Transformer menggunakan full attention, dalam arti bahwa matriks attention berbentuk persegi. Ini bisa menjadi hambatan komputasi besar saat menangani teks panjang. Longformer dan Reformer adalah model yang mencoba menjadi lebih efisien dengan menggunakan versi sparsity (jarang) dari matriks attention untuk mempercepat pelatihan.
+
+
+
+Mekanisme attention standar memiliki kompleksitas komputasi sebesar O(n²), di mana n adalah panjang urutan. Ini menjadi masalah untuk urutan yang sangat panjang. Mekanisme attention khusus di bawah ini membantu mengatasi keterbatasan tersebut.
+
+
+
+### Attention LSH
+
+[Reformer](https://huggingface.co/docs/transformers/model_doc/reformer) menggunakan attention LSH (Locality-Sensitive Hashing). Dalam perhitungan softmax(QKᵗ), hanya elemen terbesar (pada dimensi softmax) dari matriks QKᵗ yang akan memberikan kontribusi berarti. Jadi, untuk setiap query `q` dalam Q, kita hanya mempertimbangkan key `k` dalam K yang dekat dengan `q`. Fungsi hash digunakan untuk menentukan apakah `q` dan `k` cukup dekat. Attention mask dimodifikasi untuk memblokir token saat ini (kecuali pada posisi pertama), karena query dan key akan sama (dan jadi sangat mirip). Karena hasil hash bisa bersifat acak, beberapa fungsi hash digunakan dalam praktik (ditentukan oleh parameter `n_rounds`) dan hasilnya dirata-rata.
+
+### Attention Lokal
+
+[Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) menggunakan attention lokal: sering kali, konteks lokal (misalnya, dua token di kiri dan kanan) sudah cukup untuk menentukan aksi dari sebuah token. Selain itu, dengan menumpuk lapisan attention yang memiliki jendela kecil (small window), lapisan terakhir akan memiliki bidang pengamatan (receptive field) yang lebih luas dari hanya token dalam jendela, sehingga memungkinkan model membangun representasi dari seluruh kalimat.
+
+Beberapa token input yang telah dipilih juga diberi global attention: untuk token-token ini, matriks attention bisa mengakses semua token, dan proses ini bersifat simetris — semua token lain juga dapat mengakses token khusus tersebut (di samping token dalam jendela lokal mereka). Ini digambarkan dalam Gambar 2d pada makalah aslinya, dan contoh attention mask-nya dapat dilihat di bawah:
+
+
+
+
+
+Dengan menggunakan matriks attention yang memiliki parameter lebih sedikit, model dapat menerima input dengan panjang urutan yang lebih besar.
+
+### Positional Encoding Aksial
+
+[Reformer](https://huggingface.co/docs/transformers/model_doc/reformer) menggunakan *axial positional encodings*: dalam model Transformer tradisional, positional encoding `E` adalah matriks berukuran \\(l\\) × \\(d\\), dengan \\(l\\) sebagai panjang urutan dan \\(d\\) sebagai dimensi *hidden state*. Untuk teks yang sangat panjang, matriks ini bisa menjadi sangat besar dan memakan banyak ruang di GPU.
+
+Untuk mengatasinya, axial positional encodings memfaktorkan matriks besar `E` menjadi dua matriks yang lebih kecil, `E1` dan `E2`, dengan ukuran masing-masing \\(l_{1} \times d_{1}\\) dan \\(l_{2} \times d_{2}\\), sedemikian hingga \\(l_{1} \times l_{2} = l\\) dan \\(d_{1} + d_{2} = d\\). (Dengan hasil perkalian panjang urutan, ini jauh lebih kecil ukurannya.) Embedding untuk langkah waktu \\(j\\) dalam `E` diperoleh dengan menggabungkan (concatenate) embedding dari \\(j \% l1\\) di `E1` dan \\(j // l1\\) di `E2`.
+
+## Kesimpulan [[conclusion]]
+
+Di bagian ini, kita telah mempelajari tiga arsitektur utama Transformer serta beberapa mekanisme attention khusus. Memahami perbedaan arsitektur ini sangat penting untuk memilih model yang tepat untuk tugas NLP tertentu.
+
+Saat Anda melanjutkan kursus ini, Anda akan mendapatkan pengalaman langsung dengan berbagai arsitektur tersebut dan mempelajari cara *fine-tuning* untuk kebutuhan spesifik Anda. Di bagian selanjutnya, kita akan melihat beberapa keterbatasan dan bias yang ada dalam model-model ini — hal-hal penting yang perlu Anda pahami saat akan menggunakannya dalam dunia nyata.
diff --git a/chapters/id/chapter1/7.mdx b/chapters/id/chapter1/7.mdx
new file mode 100644
index 000000000..2a7722a9e
--- /dev/null
+++ b/chapters/id/chapter1/7.mdx
@@ -0,0 +1,256 @@
+
+
+# Kuis Tidak Dinilai[[ungraded-quiz]]
+
+
+
+Sampai titik ini, kamu telah mempelajari banyak hal di bab ini! Tidak masalah jika belum memahami semua detailnya — tapi mari luangkan waktu sejenak untuk merefleksikan apa yang telah kamu pelajari dengan kuis ini.
+
+Kuis ini **tidak dinilai**, jadi kamu bisa mencobanya sebanyak yang kamu mau. Jika kesulitan menjawab beberapa pertanyaan, ikuti tips yang diberikan dan tinjau kembali materi sebelumnya. Kamu akan diuji kembali tentang materi ini dalam ujian sertifikasi.
+
+
+### 1. Jelajahi Hugging Face Hub dan cari model `roberta-large-mnli`. Tugas apa yang dilakukan model ini?
+
+roberta-large-mnli."
+ },
+ {
+ text: "Klasifikasi teks",
+ explain: "Lebih tepatnya, model ini mengklasifikasikan apakah dua kalimat secara logis saling terkait dalam tiga label (kontradiksi, netral, implikasi) — ini disebut juga natural language inference.",
+ correct: true
+ },
+ {
+ text: "Generasi teks",
+ explain: "Coba lihat lagi pada halaman roberta-large-mnli."
+ }
+ ]}
+/>
+
+### 2. Apa output dari kode berikut?
+
+```python
+from transformers import pipeline
+
+ner = pipeline("ner", grouped_entities=True)
+ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+sentiment-analysis."
+ },
+ {
+ text: "Menghasilkan teks lanjutan untuk melengkapi kalimat ini.",
+ explain: "Salah — ini akan dilakukan oleh pipeline text-generation."
+ },
+ {
+ text: "Mengembalikan kata-kata yang mewakili orang, organisasi, atau lokasi.",
+ explain: "Benar! Dengan grouped_entities=True, entitas seperti \"Hugging Face\" akan dikelompokkan sebagai satu entitas.",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Apa yang seharusnya menggantikan `...` dalam contoh kode berikut?
+
+```python
+from transformers import pipeline
+
+filler = pipeline("fill-mask", model="bert-base-cased")
+result = filler("...")
+```
+
+ has been waiting for you.",
+ explain: "Salah. Lihat kembali dokumentasi model bert-base-cased."
+ },
+ {
+ text: "This [MASK] has been waiting for you.",
+ explain: "Benar! Token masker untuk model ini adalah [MASK].",
+ correct: true
+ },
+ {
+ text: "This man has been waiting for you.",
+ explain: "Salah. Pipeline ini memerlukan token mask dalam input."
+ }
+ ]}
+/>
+
+### 4. Mengapa kode berikut gagal dijalankan?
+
+```python
+from transformers import pipeline
+
+classifier = pipeline("zero-shot-classification")
+result = classifier("This is a course about the Transformers library")
+```
+
+candidate_labels=[...].",
+ correct: true
+ },
+ {
+ text: "Pipeline ini memerlukan beberapa kalimat, bukan hanya satu.",
+ explain: "Salah. Pipeline ini bisa memproses satu atau lebih kalimat."
+ },
+ {
+ text: "Library 🤗 Transformers rusak, seperti biasa.",
+ explain: "Kami tidak akan mengomentari jawaban ini 😅."
+ },
+ {
+ text: "Pipeline ini memerlukan input yang lebih panjang.",
+ explain: "Salah. Input pendek bisa digunakan; input sangat panjang akan terpotong otomatis."
+ }
+ ]}
+/>
+
+### 5. Apa arti dari "transfer learning"?
+
+
+
+### 6. Benar atau salah? Model bahasa biasanya tidak memerlukan label untuk pretraining-nya.
+
+self-supervised, artinya label dibuat otomatis dari input.",
+ correct: true
+ },
+ {
+ text: "Salah",
+ explain: "Jawaban ini tidak tepat."
+ }
+ ]}
+/>
+
+### 7. Kalimat mana yang paling tepat menggambarkan istilah "model", "arsitektur", dan "bobot"?
+
+
+
+### 8. Jenis model apa yang kamu gunakan untuk melengkapi prompt dengan teks hasil generasi?
+
+
+
+### 9. Jenis model apa yang kamu gunakan untuk merangkum teks?
+
+
+
+### 10. Jenis model apa yang kamu gunakan untuk mengklasifikasikan input teks ke dalam label tertentu?
+
+
+
+### 11. Apa saja sumber dari bias yang mungkin ada pada model?
+
+
diff --git a/chapters/id/chapter1/8.mdx b/chapters/id/chapter1/8.mdx
new file mode 100644
index 000000000..4d7468147
--- /dev/null
+++ b/chapters/id/chapter1/8.mdx
@@ -0,0 +1,254 @@
+# Menyelami Lebih Dalam Inferensi Teks dengan LLM[[inference-with-llms]]
+
+
+
+
+
+Sejauh ini, kita telah mempelajari arsitektur Transformer dalam berbagai tugas spesifik seperti klasifikasi teks dan ringkasan. Namun, penggunaan utama Large Language Models (LLMs) adalah untuk *text generation* (generasi teks), dan itulah yang akan kita bahas dalam bab ini.
+
+Pada halaman ini, kita akan membahas konsep inti dari proses inferensi LLM—memahami cara model menghasilkan teks dan komponen kunci dalam proses inferensinya.
+
+## Memahami Dasar-dasarnya
+
+Mari mulai dengan hal mendasar. *Inferensi* adalah proses menggunakan LLM yang sudah dilatih untuk menghasilkan teks seperti manusia dari masukan (*prompt*) tertentu. Model bahasa menggunakan pengetahuan dari pelatihan untuk membentuk respons satu kata (token) pada satu waktu. Model memanfaatkan probabilitas yang telah dipelajari dari miliaran parameter untuk memprediksi dan menghasilkan token berikutnya dalam urutan. Proses sekuensial inilah yang memungkinkan LLM menghasilkan teks yang koheren dan relevan secara kontekstual.
+
+## Peran Mekanisme Attention
+
+Mekanisme *attention* memberikan kemampuan pada LLM untuk memahami konteks dan menghasilkan respons yang koheren. Saat memprediksi kata berikutnya, tidak semua kata memiliki bobot yang sama—misalnya, dalam kalimat _"Ibu kota Prancis adalah..."_, kata "Prancis" dan "ibu kota" sangat penting untuk menentukan bahwa kata berikutnya seharusnya "Paris". Kemampuan untuk fokus pada informasi yang relevan ini disebut *attention*.
+
+
+
+Proses mengidentifikasi kata-kata yang paling relevan untuk memprediksi token berikutnya ini sangat efektif. Meskipun prinsip dasar pelatihan LLM (memprediksi token selanjutnya) relatif tetap sejak BERT dan GPT-2, ada kemajuan besar dalam skala jaringan saraf dan efisiensi attention untuk konteks panjang dengan biaya komputasi yang lebih rendah.
+
+
+
+Singkatnya, mekanisme *attention* adalah kunci bagi LLM untuk menghasilkan teks yang koheren dan sadar konteks. Mekanisme ini membedakan LLM modern dari generasi sebelumnya.
+
+
+
+### Panjang Konteks dan Rentang Attention
+
+Sekarang kita memahami *attention*, mari bahas seberapa banyak konteks yang bisa diproses oleh LLM—yaitu panjang konteks atau 'daya ingat' model.
+
+**Panjang konteks** mengacu pada jumlah maksimum token (kata atau bagian kata) yang dapat diproses oleh LLM sekaligus. Anggap saja ini sebagai memori kerja model.
+
+Kemampuan ini dibatasi oleh beberapa faktor praktis:
+- Arsitektur dan ukuran model
+- Sumber daya komputasi yang tersedia
+- Kompleksitas input dan output yang diharapkan
+
+Idealnya, kita ingin memberi model konteks sebanyak mungkin, tetapi keterbatasan perangkat keras dan biaya komputasi membuat hal ini tidak praktis. Oleh karena itu, model dirancang dengan panjang konteks yang berbeda-beda untuk menyeimbangkan kapabilitas dan efisiensi.
+
+
+
+**Panjang konteks** adalah jumlah maksimum token yang dapat dipertimbangkan model saat menghasilkan respons.
+
+
+
+### Seni Membuat Prompt
+
+Saat kita memberi informasi ke LLM, kita menyusunnya agar dapat mengarahkan hasil output ke tujuan yang kita inginkan. Ini disebut _prompting_.
+
+Memahami bagaimana LLM memproses input membantu kita membuat *prompt* yang lebih efektif. Karena tugas utama model adalah memprediksi token berikutnya dengan menganalisis pentingnya setiap token dalam input, maka cara kita menyusun kalimat sangat memengaruhi hasil.
+
+
+
+Desain prompt yang cermat akan mempermudah **mengendalikan arah output LLM sesuai yang diharapkan**.
+
+
+
+## Proses Inferensi Dua Tahap
+
+Setelah memahami komponen dasarnya, mari kita lihat bagaimana LLM sebenarnya menghasilkan teks. Prosesnya dibagi menjadi dua fase utama: **prefill** dan **decode**. Keduanya bekerja seperti jalur perakitan, masing-masing memainkan peran penting dalam menghasilkan teks yang koheren.
+
+### Fase Prefill
+
+Fase prefill adalah tahap persiapan—semua "bahan" awal diproses di sini. Terdiri dari tiga langkah utama:
+
+1. **Tokenisasi**: Mengubah teks menjadi token (unit dasar yang bisa dipahami model)
+2. **Konversi Embedding**: Mengubah token menjadi representasi numerik yang bermakna
+3. **Pemrosesan Awal**: Menjalankan embedding melalui jaringan saraf untuk memahami konteks
+
+Fase ini memerlukan banyak komputasi karena semua token input diproses sekaligus. Ibaratnya seperti membaca seluruh paragraf sebelum mulai menulis jawaban.
+
+Coba berbagai tokenizer secara interaktif di playground berikut:
+
+
+
+### Fase Decode
+
+Setelah prefill selesai, kita masuk ke fase *decode*—di sinilah generasi teks sebenarnya terjadi. Model menghasilkan satu token setiap kali dalam proses yang disebut *autoregressive* (token baru tergantung pada semua token sebelumnya).
+
+Langkah-langkah utama untuk setiap token baru:
+1. **Perhitungan Attention**: Melihat semua token sebelumnya untuk memahami konteks
+2. **Perhitungan Probabilitas**: Menentukan kemungkinan setiap token berikutnya
+3. **Pemilihan Token**: Memilih token berikutnya berdasarkan probabilitas
+4. **Pemeriksaan Lanjutan**: Menentukan apakah harus lanjut atau berhenti
+
+Fase ini memerlukan banyak memori karena semua token yang dihasilkan sebelumnya harus dilacak.
+
+## Strategi Sampling
+
+Setelah memahami cara model menghasilkan teks, mari lihat bagaimana kita bisa mengendalikan proses tersebut. Seperti penulis yang bisa memilih antara gaya kreatif atau lugas, kita bisa menyetel cara model memilih token.
+
+Coba lihat visualisasi decoding menggunakan SmolLM2 (akan berhenti saat menemukan token EOS yaitu **<|im_end|>**):
+
+
+
+### Memahami Pemilihan Token
+
+Ketika model harus memilih token berikutnya, ia mulai dari probabilitas kasar (logit) untuk seluruh kosa kata. Prosesnya:
+
+
+
+1. **Logit Mentah**: Perkiraan awal model untuk setiap token
+2. **Kontrol Temperatur**: Menyesuaikan kreativitas (semakin tinggi = lebih kreatif, semakin rendah = lebih pasti)
+3. **Top-p Sampling**: Memilih hanya token dengan probabilitas kumulatif hingga ambang tertentu (misal 90%)
+4. **Top-k Filtering**: Hanya mempertimbangkan k token teratas
+
+### Mengelola Pengulangan: Menjaga Variasi Output
+
+LLM cenderung mengulang, jadi kita gunakan penalti:
+
+1. **Presence Penalty**: Penalti tetap untuk token yang sudah muncul sebelumnya
+2. **Frequency Penalty**: Penalti berdasarkan seberapa sering token muncul
+
+
+
+Penalti ini diterapkan sebelum sampling, agar model terdorong menggunakan kosa kata yang lebih beragam.
+
+### Mengatur Panjang Output
+
+Seperti cerita yang butuh panjang yang pas, kita bisa atur panjang output:
+
+1. **Batas Token**: Minimal dan maksimal token
+2. **Stop Sequence**: Pola tertentu untuk menghentikan generasi
+3. **End-of-Sequence**: Deteksi otomatis akhir teks
+
+Contoh: untuk satu paragraf, batasi hingga 100 token dan gunakan `\n\n` sebagai stop sequence.
+
+
+
+### Beam Search: Melihat Beberapa Jalur
+
+Berbeda dari sampling biasa, *beam search* menjelajahi banyak kemungkinan jalur sekaligus seperti pemain catur yang mempertimbangkan beberapa langkah ke depan.
+
+
+
+Langkah-langkahnya:
+1. Jaga beberapa urutan kandidat
+2. Hitung probabilitas token berikutnya untuk tiap urutan
+3. Simpan kombinasi terbaik
+4. Ulangi hingga selesai
+5. Pilih urutan dengan probabilitas tertinggi
+
+Visualisasinya bisa Anda coba di sini:
+
+
+
+## Tantangan Praktis dan Optimasi
+
+Saat mulai mengaplikasikan LLM, ada tantangan praktis yang harus dikelola.
+
+### Metrik Kinerja Utama
+
+Empat metrik penting saat bekerja dengan LLM:
+
+1. **Time to First Token (TTFT)**: Seberapa cepat respons pertama muncul (dipengaruhi oleh prefill)
+2. **Time Per Output Token (TPOT)**: Kecepatan menghasilkan token selanjutnya
+3. **Throughput**: Berapa banyak permintaan bisa dilayani sekaligus
+4. **Penggunaan VRAM**: Seberapa banyak memori GPU yang dibutuhkan
+
+### Tantangan Panjang Konteks
+
+Panjang konteks adalah tantangan besar dalam inferensi:
+
+- **Penggunaan Memori**: Meningkat secara kuadrat dengan panjang konteks
+- **Kecepatan Proses**: Menurun secara linier dengan panjang konteks
+- **Alokasi Sumber Daya**: Perlu keseimbangan VRAM yang cermat
+
+Contohnya, model seperti [Qwen2.5-1M](https://huggingface.co/Qwen/Qwen2.5-14B-Instruct-1M) mendukung hingga 1 juta token, tapi dengan waktu inferensi yang jauh lebih lambat.
+
+
+
+
+
+ Input Text (Raw)
+
+
→
+
+ Tokenized Input
+
+
+
+
+ Context Window (e.g., 4K tokens)
+
+
+
+
+
+
+
+
+
+
+
+ Memory Usage ∝ Length²
+
+
+
+
+ Processing Time ∝ Length
+
+
+
+
+
+
+### Optimasi KV Cache
+
+Optimasi penting yang digunakan adalah **KV Caching**. Ini menyimpan hasil perhitungan sebelumnya, sehingga:
+
+- Tidak perlu menghitung ulang
+- Kecepatan meningkat
+- Bisa memproses konteks panjang secara praktis
+
+Biayanya adalah penggunaan memori tambahan, tapi keuntungannya sangat besar.
+
+## Kesimpulan
+
+Memahami inferensi LLM penting agar bisa menggunakannya secara efektif. Kita telah membahas:
+
+- Peran penting attention dan konteks
+- Proses inferensi dua tahap
+- Beragam strategi sampling
+- Tantangan praktis dan cara optimasinya
+
+Dengan menguasai konsep-konsep ini, Anda akan lebih siap membangun aplikasi berbasis LLM yang efisien dan bermanfaat.
+
+Dunia LLM terus berkembang pesat. Tetap ingin tahu dan terus bereksperimen untuk menemukan pendekatan terbaik bagi kasus penggunaan Anda.
diff --git a/chapters/id/chapter1/9.mdx b/chapters/id/chapter1/9.mdx
new file mode 100644
index 000000000..82108d609
--- /dev/null
+++ b/chapters/id/chapter1/9.mdx
@@ -0,0 +1,32 @@
+# Bias dan Keterbatasan[[bias-and-limitations]]
+
+
+
+Jika Anda berniat menggunakan model pralatih atau versi yang telah disesuaikan (fine-tuned) dalam produksi, harap diingat bahwa meskipun model-model ini merupakan alat yang sangat kuat, mereka memiliki keterbatasan. Yang paling utama adalah bahwa, untuk memungkinkan pelatihan awal (pretraining) pada data dalam jumlah besar, para peneliti sering mengambil semua konten yang bisa mereka temukan — baik yang terbaik maupun yang terburuk — dari internet.
+
+Sebagai ilustrasi singkat, mari kita kembali ke contoh pipeline `fill-mask` dengan model BERT:
+
+```python
+from transformers import pipeline
+
+unmasker = pipeline("fill-mask", model="bert-base-uncased")
+result = unmasker("This man works as a [MASK].")
+print([r["token_str"] for r in result])
+
+result = unmasker("This woman works as a [MASK].")
+print([r["token_str"] for r in result])
+```
+
+```python out
+['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
+['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
+```
+
+Ketika diminta untuk mengisi kata yang hilang dalam dua kalimat ini, model hanya memberikan satu jawaban yang netral terhadap gender (waiter/waitress). Jawaban lainnya merupakan jenis pekerjaan yang biasanya diasosiasikan dengan gender tertentu — dan ya, "prostitute" (pelacur) termasuk dalam 5 kemungkinan teratas yang diasosiasikan model dengan "perempuan" dan "bekerja". Ini terjadi meskipun BERT adalah salah satu dari sedikit model Transformer yang tidak dibangun dengan mengambil data dari seluruh penjuru internet, melainkan menggunakan data yang tampaknya netral (dilatih pada dataset [Wikipedia Bahasa Inggris](https://huggingface.co/datasets/wikipedia) dan [BookCorpus](https://huggingface.co/datasets/bookcorpus)).
+
+Saat Anda menggunakan alat ini, Anda perlu selalu mengingat bahwa model asli yang Anda gunakan sangat mungkin menghasilkan konten yang seksis, rasis, atau homofobik. Melatih ulang model dengan data Anda tidak akan sepenuhnya menghilangkan bias yang melekat ini.
diff --git a/chapters/id/chapter1/img/summary.svg b/chapters/id/chapter1/img/summary.svg
new file mode 100644
index 000000000..94769c146
--- /dev/null
+++ b/chapters/id/chapter1/img/summary.svg
@@ -0,0 +1,437 @@
+
+
diff --git a/chapters/id/chapter1/img/summary_edit.svg b/chapters/id/chapter1/img/summary_edit.svg
new file mode 100644
index 000000000..68ebc2255
--- /dev/null
+++ b/chapters/id/chapter1/img/summary_edit.svg
@@ -0,0 +1,524 @@
+
+
diff --git a/chapters/id/chapter2/1.mdx b/chapters/id/chapter2/1.mdx
new file mode 100644
index 000000000..e675768fd
--- /dev/null
+++ b/chapters/id/chapter2/1.mdx
@@ -0,0 +1,24 @@
+# Pendahuluan[[introduction]]
+
+
+
+Seperti yang telah Anda lihat di [Bab 1](/course/chapter1), model Transformer biasanya sangat besar. Dengan jutaan hingga *puluhan miliar* parameter, pelatihan dan penerapan model-model ini merupakan tugas yang rumit. Selain itu, dengan model-model baru yang dirilis hampir setiap hari dan masing-masing memiliki implementasi sendiri, mencoba semuanya bukanlah hal yang mudah.
+
+Library 🤗 Transformers dibuat untuk menyelesaikan masalah ini. Tujuannya adalah menyediakan satu API tunggal melalui mana model Transformer apa pun dapat dimuat, dilatih, dan disimpan. Fitur utama dari library ini adalah:
+
+- **Mudah digunakan**: Mengunduh, memuat, dan menggunakan model NLP terkini untuk inferensi dapat dilakukan hanya dalam dua baris kode.
+- **Fleksibel**: Pada dasarnya, semua model adalah kelas `nn.Module` PyTorch biasa dan dapat ditangani seperti model lain di framework machine learning (ML) masing-masing.
+- **Sederhana**: Hampir tidak ada abstraksi yang dibuat di seluruh library. Konsep inti "Semua dalam satu file" diterapkan: proses forward dari suatu model sepenuhnya didefinisikan dalam satu file, sehingga kodenya dapat dipahami dan dimodifikasi.
+
+Fitur terakhir ini membuat 🤗 Transformers cukup berbeda dari library ML lainnya. Model-modelnya tidak dibangun berdasarkan modul yang dibagikan antar file; sebaliknya, setiap model memiliki layer-nya sendiri. Selain membuat model lebih mudah dipahami dan diakses, ini juga memungkinkan Anda untuk bereksperimen dengan satu model tanpa memengaruhi model lain.
+
+Bab ini akan dimulai dengan contoh end-to-end di mana kita menggunakan model dan tokenizer bersama-sama untuk mereplikasi fungsi `pipeline()` yang diperkenalkan di [Bab 1](/course/chapter1). Selanjutnya, kita akan membahas API model: kita akan menyelami kelas model dan konfigurasi, serta menunjukkan cara memuat model dan bagaimana model memproses input numerik menjadi prediksi output.
+
+Lalu kita akan melihat API tokenizer, yang merupakan komponen utama lain dari fungsi `pipeline()`. Tokenizer menangani langkah pemrosesan pertama dan terakhir, yaitu konversi dari teks ke input numerik untuk jaringan neural, dan konversi kembali ke teks saat dibutuhkan. Terakhir, kami akan menunjukkan cara mengirim beberapa kalimat melalui model dalam batch yang telah disiapkan, lalu menutupnya dengan melihat lebih dekat pada fungsi tingkat tinggi `tokenizer()`.
+
+
+⚠️ Untuk mendapatkan semua fitur yang tersedia dengan Model Hub dan 🤗 Transformers, kami menyarankan Anda membuat akun.
+
diff --git a/chapters/id/chapter2/2.mdx b/chapters/id/chapter2/2.mdx
new file mode 100644
index 000000000..a752560d1
--- /dev/null
+++ b/chapters/id/chapter2/2.mdx
@@ -0,0 +1,237 @@
+
+
+# Di Balik Fungsi Pipeline[[behind-the-pipeline]]
+
+
+
+
+
+Mari kita mulai dengan sebuah contoh lengkap, melihat apa yang terjadi di balik layar saat kita menjalankan kode berikut di [Bab 1](/course/chapter1):
+
+```python
+from transformers import pipeline
+
+classifier = pipeline("sentiment-analysis")
+classifier(
+ [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+ ]
+)
+```
+
+dan mendapatkan:
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437},
+ {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
+```
+
+Seperti yang kita lihat di [Bab 1](/course/chapter1), pipeline ini menggabungkan tiga langkah: prapemrosesan, menjalankan input melalui model, dan pascapemrosesan:
+
+
+
+
+
+
+Mari kita ulas secara singkat masing-masing langkah ini.
+
+## Prapemrosesan dengan Tokenizer[[preprocessing-with-a-tokenizer]]
+
+Seperti jaringan saraf lainnya, model Transformer tidak bisa memproses teks mentah secara langsung, jadi langkah pertama dalam pipeline kita adalah mengubah input teks menjadi angka yang bisa dipahami oleh model. Untuk itu, kita menggunakan *tokenizer*, yang bertugas:
+
+- Memecah input menjadi kata, sub-kata, atau simbol (seperti tanda baca) yang disebut *token*
+- Memetakan setiap token ke bilangan bulat
+- Menambahkan input tambahan yang mungkin diperlukan oleh model
+
+Semua prapemrosesan ini harus dilakukan dengan cara yang sama persis seperti saat model dilatih sebelumnya, jadi kita perlu mengunduh informasi itu dari [Model Hub](https://huggingface.co/models). Untuk melakukannya, kita menggunakan kelas `AutoTokenizer` dan metode `from_pretrained()`. Dengan menggunakan nama checkpoint model, metode ini akan secara otomatis mengambil data terkait tokenizer model dan menyimpannya di cache (jadi hanya diunduh saat pertama kali dijalankan).
+
+Karena checkpoint default pipeline `sentiment-analysis` adalah `distilbert-base-uncased-finetuned-sst-2-english` (lihat kartu modelnya [di sini](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)), kita jalankan:
+
+```python
+from transformers import AutoTokenizer
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+
+Setelah kita memiliki tokenizer, kita bisa langsung memberikan kalimat ke tokenizer dan akan mendapatkan dictionary yang siap digunakan oleh model. Satu-satunya hal yang tersisa adalah mengonversi daftar `input_ids` menjadi tensor.
+
+Anda dapat menggunakan 🤗 Transformers tanpa perlu khawatir tentang kerangka kerja ML (machine learning) yang digunakan sebagai backend; bisa saja PyTorch atau Flax untuk beberapa model. Namun, model Transformer hanya menerima *tensor* sebagai masukan. Jika ini adalah pertama kalinya Anda mendengar tentang tensor, Anda bisa menganggapnya sebagai array NumPy. Array NumPy bisa berupa skalar (0D), vektor (1D), matriks (2D), atau memiliki lebih banyak dimensi. Ini pada dasarnya adalah tensor; tensor pada kerangka kerja ML lainnya berperilaku serupa, dan biasanya sama mudahnya untuk dibuat seperti array NumPy.
+
+Untuk menentukan jenis tensor yang diinginkan (PyTorch atau NumPy biasa), kita menggunakan argumen `return_tensors`:
+
+```python
+raw_inputs = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+]
+inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
+print(inputs)
+```
+
+Jangan khawatir dulu soal padding dan truncation; itu akan dijelaskan nanti. Yang penting diingat adalah Anda bisa memberikan satu kalimat atau daftar kalimat, serta menentukan tipe tensor yang ingin dikembalikan (jika tidak ditentukan, hasilnya berupa daftar dari daftar).
+
+Berikut contoh hasil sebagai tensor PyTorch:
+
+```python out
+{
+ 'input_ids': tensor([
+ [ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
+ [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
+ ]),
+ 'attention_mask': tensor([
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
+ ])
+}
+```
+
+Output-nya adalah dictionary yang berisi dua kunci: `input_ids` dan `attention_mask`. `input_ids` berisi dua baris angka (masing-masing untuk satu kalimat) yang merupakan ID unik dari setiap token. Penjelasan tentang `attention_mask` akan dibahas nanti.
+
+## Melewati Model[[going-through-the-model]]
+
+Kita bisa mengunduh model yang sudah dilatih sebelumnya dengan cara yang sama seperti tokenizer. 🤗 Transformers menyediakan kelas `AutoModel` yang juga memiliki metode `from_pretrained()`:
+
+```python
+from transformers import AutoModel
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = AutoModel.from_pretrained(checkpoint)
+```
+
+Dalam cuplikan kode ini, kita telah mengunduh checkpoint yang sama seperti yang kita gunakan sebelumnya dalam pipeline kita (sebenarnya seharusnya sudah tersimpan dalam cache) dan menginisialisasi sebuah model dengannya.
+
+Arsitektur ini hanya berisi modul dasar Transformer: diberikan beberapa input, model akan menghasilkan *hidden states*, juga dikenal sebagai *fitur*. Untuk setiap input, kita akan mendapatkan vektor berdimensi tinggi yang mewakili **pemahaman kontekstual input oleh model Transformer**.
+
+Jika ini belum sepenuhnya masuk akal, jangan khawatir—akan dijelaskan lebih lanjut nanti.
+
+Meskipun hidden states ini bisa berguna, biasanya mereka menjadi input untuk bagian lain dari model, yang disebut *head*. Di [Bab 1](/course/chapter1), tugas yang berbeda bisa dilakukan dengan arsitektur yang sama, tetapi masing-masing tugas memiliki head yang berbeda.
+
+### Vektor Berdimensi Tinggi?[[a-high-dimensional-vector]]
+
+Vektor yang dihasilkan oleh modul Transformer biasanya besar. Umumnya memiliki tiga dimensi:
+
+- **Ukuran batch**: Jumlah urutan yang diproses sekaligus (2 dalam contoh kita).
+- **Panjang urutan**: Panjang representasi numerik dari urutan (16 dalam contoh kita).
+- **Ukuran tersembunyi (hidden size)**: Dimensi vektor untuk tiap input model.
+
+Disebut "berdimensi tinggi" karena nilai terakhir. Hidden size ini bisa sangat besar (768 umum untuk model kecil, dan bisa mencapai 3072 atau lebih untuk model besar).
+
+Kita bisa melihat ini jika kita beri input ke model:
+
+```python
+outputs = model(**inputs)
+print(outputs.last_hidden_state.shape)
+```
+
+```python out
+torch.Size([2, 16, 768])
+```
+
+Output dari model 🤗 Transformers berperilaku seperti `namedtuple` atau dictionary. Anda bisa mengakses elemennya dengan atribut (`outputs.last_hidden_state`), kunci (`outputs["last_hidden_state"]`), atau indeks (`outputs[0]`).
+
+### Model Head: Mengubah Angka Menjadi Arti[[model-heads-making-sense-out-of-numbers]]
+
+Model head mengambil vektor hidden states berdimensi tinggi sebagai input dan memproyeksikannya ke dimensi yang berbeda. Biasanya terdiri dari satu atau beberapa lapisan linear:
+
+
+
+
+
+
+
+Output dari Transformer langsung dikirim ke model head untuk diproses.
+
+Dalam diagram ini, model direpresentasikan oleh lapisan embeddings dan lapisan-lapisan setelahnya. Lapisan embeddings mengubah setiap ID masukan dalam input yang telah ditokenisasi menjadi vektor yang merepresentasikan token terkait. Lapisan-lapisan setelahnya memanipulasi vektor-vektor tersebut menggunakan mekanisme perhatian (attention) untuk menghasilkan representasi akhir dari kalimat-kalimat tersebut.
+
+Berbagai arsitektur tersedia di 🤗 Transformers, masing-masing dirancang untuk tugas tertentu. Contohnya:
+
+- `*Model` (mengambil hidden states)
+- `*ForCausalLM`
+- `*ForMaskedLM`
+- `*ForMultipleChoice`
+- `*ForQuestionAnswering`
+- `*ForSequenceClassification`
+- `*ForTokenClassification`
+- dan lainnya 🤗
+
+Untuk contoh kita, kita akan membutuhkan model dengan kepala klasifikasi sekuens (agar dapat mengklasifikasikan kalimat sebagai positif atau negatif). Jadi, kita tidak akan menggunakan kelas `AutoModel`, melainkan `AutoModelForSequenceClassification`:
+
+```python
+from transformers import AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+outputs = model(**inputs)
+```
+
+Sekarang jika kita melihat bentuk (shape) dari output kita, dimensinya akan jauh lebih rendah: kepala model menerima sebagai masukan vektor berdimensi tinggi yang telah kita lihat sebelumnya, dan menghasilkan vektor yang berisi dua nilai (satu untuk setiap label):
+
+```python
+print(outputs.logits.shape)
+```
+
+```python out
+torch.Size([2, 2])
+```
+
+Karena kita punya dua kalimat dan dua label, hasil dari model adalah 2 × 2.
+
+## Pascapemrosesan Output[[postprocessing-the-output]]
+
+Nilai yang kita dapat dari model belum tentu langsung bermakna. Mari kita lihat:
+
+```python
+print(outputs.logits)
+```
+
+```python out
+tensor([[-1.5607, 1.6123],
+ [ 4.1692, -3.3464]], grad_fn=)
+```
+
+Model kita memprediksi `[-1.5607, 1.6123]` untuk kalimat pertama dan `[ 4.1692, -3.3464]` untuk kalimat kedua. Nilai-nilai tersebut bukan probabilitas, melainkan *logit*, yaitu skor mentah yang belum dinormalisasi yang dihasilkan oleh lapisan terakhir model. Untuk diubah menjadi probabilitas, nilai-nilai ini perlu melalui lapisan [SoftMax](https://id.wikipedia.org/wiki/Fungsi_softmax) (semua model 🤗 Transformers mengeluarkan logit, karena fungsi kehilangan (loss function) untuk pelatihan umumnya menggabungkan fungsi aktivasi terakhir, seperti SoftMax, dengan fungsi kehilangan itu sendiri, seperti cross entropy):
+
+```python
+import torch
+
+predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
+print(predictions)
+```
+
+```python out
+tensor([[4.0195e-02, 9.5980e-01],
+ [9.9946e-01, 5.4418e-04]], grad_fn=)
+```
+
+Sekarang kita dapat melihat bahwa model memprediksi `[0.0402, 0.9598]` untuk kalimat pertama dan `[0.9995, 0.0005]` untuk kalimat kedua. Ini adalah skor probabilitas yang mudah dikenali.
+
+Untuk mendapatkan label yang sesuai dengan setiap posisi, kita dapat memeriksa atribut `id2label` dari konfigurasi model (akan dibahas lebih lanjut di bagian berikutnya):
+
+```python
+model.config.id2label
+```
+
+```python out
+{0: 'NEGATIVE', 1: 'POSITIVE'}
+```
+
+Kesimpulan:
+
+- Kalimat pertama: NEGATIVE: 0.0402, POSITIVE: 0.9598
+- Kalimat kedua: NEGATIVE: 0.9995, POSITIVE: 0.0005
+
+Kita telah berhasil mereproduksi tiga langkah dari pipeline: praproses dengan tokenizer, memasukkan input ke dalam model, dan pascaproses! Sekarang, mari kita luangkan waktu untuk menyelami lebih dalam setiap langkah tersebut.
+
+
+
+✏️ **Coba sendiri!** Pilih dua (atau lebih) teks Anda sendiri dan jalankan melalui pipeline `sentiment-analysis`. Lalu replikasi langkah-langkah di atas dan lihat apakah hasilnya sama!
+
+
+
diff --git a/chapters/id/chapter2/3.mdx b/chapters/id/chapter2/3.mdx
new file mode 100644
index 000000000..62a6ca5d2
--- /dev/null
+++ b/chapters/id/chapter2/3.mdx
@@ -0,0 +1,297 @@
+
+
+# Model[[the-models]]
+
+
+
+
+
+Dalam bagian ini, kita akan melihat lebih dekat cara membuat dan menggunakan model. Kita akan menggunakan kelas `AutoModel`, yang sangat berguna saat Anda ingin menginisialisasi model apa pun dari sebuah checkpoint.
+
+## Membuat Model Transformer[[creating-a-transformer]]
+
+Mari kita mulai dengan melihat apa yang terjadi saat kita menginisialisasi `AutoModel`:
+
+```py
+from transformers import AutoModel
+
+model = AutoModel.from_pretrained("bert-base-cased")
+```
+
+Mirip dengan tokenizer, metode `from_pretrained()` akan mengunduh dan menyimpan data model dari Hugging Face Hub. Seperti yang telah disebutkan sebelumnya, nama checkpoint merujuk pada arsitektur dan bobot model tertentu — dalam hal ini, model BERT dengan arsitektur dasar (12 layer, ukuran tersembunyi 768, 12 kepala atensi) dan input *cased* (membedakan huruf besar/kecil). Banyak checkpoint lain tersedia di Hub — Anda bisa menjelajahinya [di sini](https://huggingface.co/models).
+
+Kelas `AutoModel` dan turunannya sebenarnya hanyalah pembungkus (wrapper) sederhana yang dirancang untuk mengambil arsitektur model yang sesuai dari checkpoint tertentu. Kelas ini adalah kelas "auto" karena akan menebak arsitektur model yang sesuai dan menginisialisasi kelas model yang benar. Namun, jika Anda sudah tahu jenis model yang ingin digunakan, Anda bisa langsung menggunakan kelas arsitektur tersebut:
+
+```py
+from transformers import BertModel
+
+model = BertModel.from_pretrained("bert-base-cased")
+```
+
+## Memuat dan Menyimpan[[loading-and-saving]]
+
+Menyimpan model semudah menyimpan tokenizer. Model memiliki metode `save_pretrained()` yang sama, yang akan menyimpan bobot dan konfigurasi arsitektur model:
+
+```py
+model.save_pretrained("direktori_di_lokal_komputer")
+```
+
+Ini akan menyimpan dua file ke dalam disk Anda:
+
+```
+ls direktori_di_lokal_komputer
+
+config.json model.safetensors
+```
+
+Jika Anda membuka file *config.json*, Anda akan melihat semua atribut yang diperlukan untuk membangun arsitektur model. File ini juga memuat metadata, seperti asal checkpoint dan versi 🤗 Transformers yang digunakan saat terakhir kali menyimpan model.
+
+File *pytorch_model.safetensors* adalah yang dikenal sebagai **state dictionary**, berisi bobot model. Kedua file ini saling melengkapi: konfigurasi memberitahu struktur model, sedangkan bobot adalah parameternya.
+
+Untuk menggunakan kembali model yang telah disimpan, gunakan kembali metode `from_pretrained()`:
+
+```py
+from transformers import AutoModel
+
+model = AutoModel.from_pretrained("direktori_di_lokal_komputer")
+```
+
+Fitur hebat dari 🤗 Transformers adalah kemudahan untuk membagikan model dan tokenizer ke komunitas. Untuk melakukannya, pastikan Anda memiliki akun di [Hugging Face](https://huggingface.co). Jika Anda menggunakan notebook, login bisa dilakukan dengan:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Jika tidak, di terminal jalankan:
+
+```bash
+huggingface-cli login
+```
+
+Lalu Anda bisa mengunggah model ke Hub dengan metode `push_to_hub()`:
+
+```py
+model.push_to_hub("my-awesome-model")
+```
+
+Ini akan mengunggah file model ke Hub dalam repositori di bawah namespace Anda bernama *my-awesome-model*. Kemudian siapa pun bisa memuat model Anda dengan metode `from_pretrained()`!
+
+```py
+from transformers import AutoModel
+
+model = AutoModel.from_pretrained("username-anda/my-awesome-model")
+```
+
+Anda bisa melakukan lebih banyak hal dengan API Hub:
+- Mendorong model dari repositori lokal
+- Memperbarui file tertentu tanpa mengunggah semuanya
+- Menambahkan kartu model untuk mendokumentasikan kemampuan, keterbatasan, bias, dll.
+
+Lihat [dokumentasi](https://huggingface.co/docs/huggingface_hub/how-to-upstream) untuk tutorial lengkap, atau pelajari lebih lanjut di [Bab 4](/course/chapter4).
+
+## Mengenkode Teks[[encoding-text]]
+
+Model Transformer memproses teks dengan mengubah input menjadi angka. Di sini kita akan lihat secara spesifik bagaimana teks diproses oleh tokenizer. Di [Bab 1](/course/chapter1) kita telah melihat bahwa tokenizer memecah teks menjadi token, lalu mengubah token tersebut menjadi angka. Kita bisa melihat proses ini menggunakan tokenizer sederhana:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+encoded_input = tokenizer("Hello, I'm a single sentence!")
+print(encoded_input)
+```
+
+```python out
+{'input_ids': [101, 8667, 117, 1000, 1045, 1005, 1049, 2235, 17662, 12172, 1012, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
+```
+
+Kita mendapatkan dictionary dengan:
+- `input_ids`: representasi angka dari token
+- `token_type_ids`: menunjukkan bagian mana dari input adalah kalimat A atau B (akan dibahas di bagian berikutnya)
+- `attention_mask`: menunjukkan token mana yang harus diperhatikan oleh model
+
+Kita bisa mendekode `input_ids` kembali ke bentuk teks:
+
+```py
+tokenizer.decode(encoded_input["input_ids"])
+```
+
+```python out
+"[CLS] Hello, I'm a single sentence! [SEP]"
+```
+
+Anda akan melihat bahwa tokenizer telah menambahkan token khusus — `[CLS]` dan `[SEP]` — yang dibutuhkan oleh model. Tidak semua model memerlukan token khusus; token ini digunakan ketika sebuah model telah dilatih sebelumnya (pretrained) dengan token tersebut, dalam hal ini tokenizer perlu menambahkannya karena model tersebut mengharapkannya.
+
+Anda dapat mengenkodekan beberapa kalimat sekaligus, baik dengan menggabungkannya dalam bentuk batch (kita akan membahas ini segera) atau dengan memberikan sebuah daftar:
+
+```py
+encoded_input = tokenizer("How are you?", "I'm fine, thank you!")
+print(encoded_input)
+```
+
+```python out
+{'input_ids': [[101, 1731, 1132, 1128, 136, 102], [101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]],
+ 'token_type_ids': [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
+ 'attention_mask': [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}
+```
+
+Perlu dicatat bahwa saat memberikan beberapa kalimat, tokenizer akan mengembalikan daftar untuk setiap kalimat pada setiap nilai dalam kamus. Kita juga dapat meminta tokenizer untuk langsung mengembalikan tensor dari PyTorch:
+
+```py
+encoded_input = tokenizer("How are you?", "I'm fine, thank you!", return_tensors="pt")
+print(encoded_input)
+```
+
+```python out
+{'input_ids': tensor([[ 101, 1731, 1132, 1128, 136, 102],
+ [ 101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]]),
+ 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
+ 'attention_mask': tensor([[1, 1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
+```
+
+Tapi ada masalah: kedua daftar tersebut tidak memiliki panjang yang sama! Array dan tensor harus berbentuk persegi panjang (rectangular), jadi kita tidak bisa langsung mengonversi daftar-daftar ini menjadi tensor PyTorch (atau array NumPy). Tokenizer menyediakan opsi untuk itu: padding.
+
+### Padding input[[padding-inputs]]
+
+Jika kita meminta tokenizer untuk melakukan padding pada input, maka tokenizer akan menyamakan panjang semua kalimat dengan menambahkan token padding khusus ke kalimat-kalimat yang lebih pendek dari yang paling panjang:
+
+```py
+encoded_input = tokenizer(
+ ["How are you?", "I'm fine, thank you!"], padding=True, return_tensors="pt"
+)
+print(encoded_input)
+```
+
+```python out
+{'input_ids': tensor([[ 101, 1731, 1132, 1128, 136, 102, 0, 0, 0, 0],
+ [ 101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]]),
+ 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
+ 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
+```
+
+Sekarang kita memiliki tensor berbentuk persegi panjang! Perhatikan bahwa token padding telah dikodekan ke dalam input ID dengan ID 0, dan juga memiliki nilai attention mask sebesar 0. Hal ini karena token padding tersebut tidak seharusnya dianalisis oleh model: mereka bukan bagian dari kalimat yang sebenarnya.
+
+
+### Memotong Input[[truncating-inputs]]
+
+Tensor-tensor tersebut bisa menjadi terlalu besar untuk diproses oleh model. Sebagai contoh, BERT hanya dilatih sebelumnya dengan sekuens hingga 512 token, sehingga tidak dapat memproses sekuens yang lebih panjang. Jika Anda memiliki sekuens yang lebih panjang daripada yang bisa ditangani oleh model, Anda perlu memotongnya (truncate) dengan parameter `truncation`:
+
+```py
+encoded_input = tokenizer(
+ "This is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long sentence.",
+ truncation=True,
+)
+print(encoded_input["input_ids"])
+```
+
+```python out
+[101, 1188, 1110, 170, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1179, 5650, 119, 102]
+```
+
+Dengan menggabungkan argumen `padding` dan `truncation`, Anda dapat memastikan bahwa tensor Anda memiliki ukuran yang tepat sesuai kebutuhan:
+
+```py
+encoded_input = tokenizer(
+ ["How are you?", "I'm fine, thank you!"],
+ padding=True,
+ truncation=True,
+ max_length=5,
+ return_tensors="pt",
+)
+print(encoded_input)
+```
+
+```python out
+{'input_ids': tensor([[ 101, 1731, 1132, 1128, 102],
+ [ 101, 1045, 1005, 1049, 102]]),
+ 'token_type_ids': tensor([[0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0]]),
+ 'attention_mask': tensor([[1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1]])}
+```
+
+### Menambahkan token khusus
+
+Token khusus (atau setidaknya konsepnya) sangat penting bagi BERT dan model-model turunannya. Token-token ini ditambahkan untuk merepresentasikan batas kalimat dengan lebih baik, seperti awal kalimat (`[CLS]`) atau pemisah antar kalimat (`[SEP]`). Mari kita lihat contoh sederhana:
+
+```py
+encoded_input = tokenizer("How are you?")
+print(encoded_input["input_ids"])
+tokenizer.decode(encoded_input["input_ids"])
+```
+
+```python out
+[101, 1731, 1132, 1128, 136, 102]
+'[CLS] How are you? [SEP]'
+```
+
+Token khusus ini secara otomatis ditambahkan oleh tokenizer. Tidak semua model memerlukan token khusus; token ini terutama digunakan ketika sebuah model telah dilatih sebelumnya dengan token-token tersebut, dalam hal ini tokenizer akan menambahkannya karena model mengharapkannya.
+
+### Kenapa Semua Ini Penting?
+
+Berikut adalah contoh konkret. Perhatikan sekuens-sekuens yang telah dienkode berikut ini:
+
+```py
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+]
+```
+
+Setelah ditokenisasi:
+
+```python
+encoded_sequences = [
+ [
+ 101,
+ 1045,
+ 1005,
+ 2310,
+ 2042,
+ 3403,
+ 2005,
+ 1037,
+ 17662,
+ 12172,
+ 2607,
+ 2026,
+ 2878,
+ 2166,
+ 1012,
+ 102,
+ ],
+ [101, 1045, 5223, 2023, 2061, 2172, 999, 102],
+]
+```
+
+Ini adalah daftar sekuens yang telah dienkode: sebuah daftar dari daftar. Tensor hanya menerima bentuk yang persegi panjang (bayangkan seperti matriks). "Array" ini sudah memiliki bentuk persegi panjang, jadi mengonversinya menjadi tensor sangatlah mudah:
+
+```py
+import torch
+
+model_inputs = torch.tensor(encoded_sequences)
+```
+
+### Menggunakan Tensor Sebagai Input ke Model[[using-the-tensors-as-inputs-to-the-model]]
+
+Menggunakan tensor sangat mudah — cukup panggil model dengan input:
+
+```py
+output = model(model_inputs)
+```
+
+Model bisa menerima banyak argumen, tapi hanya `input_ids` yang wajib. Penjelasan lebih lanjut tentang argumen lainnya akan dibahas setelah kita menyelami lebih dalam tokenizer dan bagaimana input dibangun untuk dipahami oleh model Transformer.
diff --git a/chapters/id/chapter2/4.mdx b/chapters/id/chapter2/4.mdx
new file mode 100644
index 000000000..9854df919
--- /dev/null
+++ b/chapters/id/chapter2/4.mdx
@@ -0,0 +1,221 @@
+
+
+# Tokenizers[[tokenizers]]
+
+
+
+
+
+Tokenizer adalah salah satu komponen inti dalam pipeline NLP. Tujuan utamanya adalah menerjemahkan teks menjadi data yang dapat diproses oleh model. Karena model hanya dapat memproses angka, tokenizer perlu mengubah input teks kita menjadi data numerik. Di bagian ini, kita akan menjelajahi secara detail apa yang terjadi dalam proses tokenisasi.
+
+Dalam tugas NLP, data yang biasanya diproses adalah teks mentah. Berikut adalah contoh teks seperti itu:
+
+```
+Jim Henson was a puppeteer
+```
+
+Namun, karena model hanya dapat memproses angka, kita perlu cara untuk mengonversi teks mentah menjadi angka. Itulah tugas tokenizer, dan ada banyak cara untuk melakukannya. Tujuannya adalah menemukan representasi yang paling bermakna — yaitu, yang paling mudah dipahami oleh model — dan, jika memungkinkan, representasi yang paling ringkas.
+
+Mari kita lihat beberapa contoh algoritma tokenisasi dan mencoba menjawab beberapa pertanyaan umum terkait tokenisasi.
+
+## Berbasis Kata[[word-based]]
+
+
+
+Jenis tokenizer pertama yang terlintas biasanya adalah _berbasis kata_. Ini umumnya sangat mudah untuk diatur dan digunakan hanya dengan beberapa aturan, dan sering kali memberikan hasil yang cukup baik. Misalnya, dalam gambar di bawah ini, tujuannya adalah membagi teks mentah menjadi kata-kata dan menemukan representasi numerik untuk masing-masing:
+
+
+
+
+
+
+Ada berbagai cara untuk memisahkan teks. Misalnya, kita bisa menggunakan spasi untuk membagi teks menjadi kata-kata dengan fungsi `split()` di Python:
+
+```py
+tokenized_text = "Jim Henson was a puppeteer".split()
+print(tokenized_text)
+```
+
+```python out
+['Jim', 'Henson', 'was', 'a', 'puppeteer']
+```
+
+Ada juga variasi tokenizer berbasis kata yang memiliki aturan tambahan untuk tanda baca. Dengan jenis tokenizer ini, kita bisa memiliki "kosa kata" yang cukup besar, di mana kosa kata didefinisikan sebagai jumlah total token independen dalam korpus kita.
+
+Setiap kata diberi ID, mulai dari 0 hingga ukuran kosa kata. Model menggunakan ID ini untuk mengidentifikasi setiap kata.
+
+Jika kita ingin mencakup seluruh bahasa dengan tokenizer berbasis kata, kita perlu memiliki pengenal untuk setiap kata dalam bahasa tersebut, yang akan menghasilkan jumlah token yang sangat besar. Misalnya, ada lebih dari 500.000 kata dalam bahasa Inggris, jadi untuk membangun peta dari setiap kata ke ID input, kita harus menyimpan sebanyak itu ID. Selain itu, kata seperti "dog" akan direpresentasikan secara berbeda dengan "dogs", dan model awalnya tidak akan tahu bahwa "dog" dan "dogs" serupa: model akan menganggap keduanya sebagai kata yang tidak berhubungan. Hal yang sama berlaku untuk kata-kata lain seperti "run" dan "running".
+
+Akhirnya, kita memerlukan token khusus untuk mewakili kata-kata yang tidak ada dalam kosa kata. Ini dikenal sebagai token "unknown" atau tidak dikenal, sering kali ditulis sebagai "[UNK]" atau "<unk>". Ini umumnya merupakan pertanda buruk jika tokenizer menghasilkan banyak token ini, karena berarti tidak dapat menemukan representasi yang masuk akal dari sebuah kata, dan kita kehilangan informasi dalam prosesnya. Tujuannya adalah membuat kosa kata sedemikian rupa sehingga tokenizer menghasilkan sesedikit mungkin token tak dikenal.
+
+Salah satu cara untuk mengurangi jumlah token tak dikenal adalah dengan menggunakan tokenizer _berbasis karakter_.
+
+## Berbasis Karakter[[character-based]]
+
+
+
+Tokenizer berbasis karakter membagi teks menjadi karakter, bukan kata. Ini memiliki dua keuntungan utama:
+
+- Kosa kata jauh lebih kecil.
+- Jauh lebih sedikit token tidak dikenal, karena setiap kata dapat dibentuk dari karakter-karakter.
+
+Namun, pendekatan ini juga menimbulkan pertanyaan seputar spasi dan tanda baca:
+
+
+
+
+
+
+Pendekatan ini juga tidak sempurna. Karena representasi kini berbasis karakter, bisa dibilang representasinya kurang bermakna: setiap karakter tidak terlalu berarti jika dibandingkan dengan kata. Namun ini juga tergantung bahasa; misalnya, dalam bahasa Tionghoa, setiap karakter membawa lebih banyak informasi dibandingkan karakter dalam bahasa Latin.
+
+Hal lain yang perlu dipertimbangkan adalah bahwa kita akan memiliki jumlah token yang jauh lebih besar untuk diproses oleh model: sementara satu kata mungkin hanya satu token dalam tokenizer berbasis kata, kata itu bisa menjadi 10 atau lebih token ketika diubah menjadi karakter.
+
+Untuk mendapatkan kelebihan dari keduanya, kita bisa menggunakan teknik ketiga yang menggabungkan pendekatan kata dan karakter: *tokenisasi subword*.
+
+## Tokenisasi Subword[[subword-tokenization]]
+
+
+
+Algoritma tokenisasi subword didasarkan pada prinsip bahwa kata-kata yang sering digunakan sebaiknya tidak dipecah, sementara kata-kata yang jarang digunakan dapat dipecah menjadi bagian subword yang bermakna.
+
+Misalnya, "annoyingly" mungkin dianggap sebagai kata yang jarang dan dapat dipecah menjadi "annoying" dan "ly". Kedua bagian ini kemungkinan lebih sering muncul secara terpisah sebagai subword, sementara makna "annoyingly" tetap dipertahankan melalui makna gabungan dari keduanya.
+
+Berikut adalah contoh bagaimana algoritma tokenisasi subword memproses kalimat "Let's do tokenization!":
+
+
+
+
+
+
+Subword ini membawa makna semantik yang kuat: misalnya, "tokenization" dipecah menjadi "token" dan "ization", dua token yang bermakna secara semantik sekaligus efisien dalam penggunaan ruang (hanya dua token diperlukan untuk mewakili satu kata panjang). Ini memungkinkan cakupan yang baik dengan kosa kata kecil, dan hampir tidak ada token tak dikenal.
+
+Pendekatan ini sangat berguna untuk bahasa aglutinatif seperti bahasa Turki, di mana kita bisa membentuk kata yang sangat panjang dan kompleks dengan menggabungkan subword.
+
+### Dan Lainnya![[and-more]]
+
+Tidak mengherankan, ada banyak teknik lain di luar sana. Beberapa di antaranya:
+
+- Byte-level BPE, digunakan dalam GPT-2
+- WordPiece, digunakan dalam BERT
+- SentencePiece atau Unigram, digunakan dalam berbagai model multibahasa
+
+Sekarang Anda sudah memiliki cukup pemahaman tentang bagaimana tokenizer bekerja untuk mulai menggunakan API-nya.
+
+## Memuat dan Menyimpan[[loading-and-saving]]
+
+Memuat dan menyimpan tokenizer semudah memuat dan menyimpan model. Bahkan, menggunakan dua metode yang sama: `from_pretrained()` dan `save_pretrained()`. Metode ini akan memuat atau menyimpan algoritma yang digunakan oleh tokenizer (mirip seperti *arsitektur* model) serta kosa katanya (mirip seperti *bobot* model).
+
+Memuat tokenizer BERT yang dilatih dengan checkpoint yang sama seperti BERT dilakukan dengan cara yang sama, hanya saja menggunakan kelas `BertTokenizer`:
+
+```py
+from transformers import BertTokenizer
+
+tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
+```
+
+Mirip dengan `AutoModel`, kelas `AutoTokenizer` akan secara otomatis memilih kelas tokenizer yang sesuai berdasarkan nama checkpoint, dan dapat langsung digunakan:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+```
+
+Sekarang kita bisa menggunakan tokenizer seperti yang ditunjukkan sebelumnya:
+
+```python
+tokenizer("Using a Transformer network is simple")
+```
+
+```python out
+{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
+```
+
+Menyimpan tokenizer dilakukan dengan cara yang sama seperti menyimpan model:
+
+```py
+tokenizer.save_pretrained("directory_on_my_computer")
+```
+
+Kita akan membahas lebih lanjut tentang `token_type_ids` di [Chapter 3](/course/chapter3), dan menjelaskan kunci `attention_mask` nanti. Sekarang, mari kita lihat bagaimana `input_ids` dihasilkan. Untuk itu, kita perlu melihat metode-metode intermediate pada tokenizer.
+
+## Encoding[[encoding]]
+
+
+
+Menerjemahkan teks menjadi angka disebut _encoding_. Encoding dilakukan dalam dua tahap: tokenisasi, lalu konversi menjadi ID input.
+
+Seperti yang sudah kita lihat, tahap pertama adalah memecah teks menjadi kata (atau bagian kata, simbol tanda baca, dll.), biasanya disebut *token*. Ada banyak aturan yang dapat digunakan dalam proses ini, itulah sebabnya kita perlu menginstansiasi tokenizer berdasarkan nama model, agar kita menggunakan aturan yang sama seperti saat model dilatih.
+
+Tahap kedua adalah mengubah token menjadi angka, agar kita bisa membuat tensor dan memberikannya ke model. Untuk melakukan ini, tokenizer memiliki *kosa kata*, yang merupakan bagian yang kita unduh saat menggunakan metode `from_pretrained()`. Sekali lagi, kita perlu menggunakan kosa kata yang sama seperti saat model dilatih.
+
+Untuk memahami dua langkah ini dengan lebih baik, mari kita eksplorasi keduanya secara terpisah. Perhatikan bahwa kita akan menggunakan metode yang menjalankan bagian dari pipeline tokenisasi secara terpisah agar bisa melihat hasil intermediate-nya, tapi dalam praktiknya, Anda cukup memanggil tokenizer langsung dengan input Anda (seperti pada bagian 2).
+
+### Tokenisasi[[tokenization]]
+
+Proses tokenisasi dilakukan dengan metode `tokenize()` dari tokenizer:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+sequence = "Using a Transformer network is simple"
+tokens = tokenizer.tokenize(sequence)
+
+print(tokens)
+```
+
+Output dari metode ini adalah daftar string, atau token:
+
+```python out
+['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
+```
+
+Tokenizer ini adalah tokenizer subword: ia membagi kata sampai mendapatkan token yang bisa direpresentasikan dalam kosa katanya. Itu terjadi di sini dengan kata `transformer`, yang dipecah menjadi dua token: `transform` dan `##er`.
+
+### Dari Token ke Input ID[[from-tokens-to-input-ids]]
+
+Konversi ke input ID dilakukan oleh metode `convert_tokens_to_ids()`:
+
+```py
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+print(ids)
+```
+
+```python out
+[7993, 170, 11303, 1200, 2443, 1110, 3014]
+```
+
+Output ini, setelah dikonversi menjadi tensor framework yang sesuai, dapat digunakan sebagai input untuk model seperti yang telah kita lihat sebelumnya.
+
+
+
+✏️ **Coba Sendiri!** Replikasi dua langkah terakhir (tokenisasi dan konversi ke ID input) pada kalimat yang kita gunakan di bagian 2 ("I've been waiting for a HuggingFace course my whole life." dan "I hate this so much!"). Pastikan Anda mendapatkan ID input yang sama seperti sebelumnya!
+
+
+
+## Decoding[[decoding]]
+
+*Decoding* adalah proses sebaliknya: dari indeks kosa kata, kita ingin mendapatkan string kembali. Ini dapat dilakukan dengan metode `decode()` seperti berikut:
+
+```py
+decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
+print(decoded_string)
+```
+
+```python out
+'Using a Transformer network is simple'
+```
+
+Perhatikan bahwa metode `decode` tidak hanya mengubah indeks menjadi token, tetapi juga menggabungkan kembali token-token yang merupakan bagian dari kata yang sama untuk menghasilkan kalimat yang dapat dibaca. Perilaku ini akan sangat berguna saat kita menggunakan model yang memprediksi teks baru (baik dari prompt atau untuk masalah sequence-to-sequence seperti terjemahan atau ringkasan).
+
+Sekarang Anda seharusnya sudah memahami operasi dasar yang dapat dilakukan oleh tokenizer: tokenisasi, konversi ke ID, dan mengubah kembali ID menjadi string. Namun, ini baru permulaan. Di bagian berikutnya, kita akan mendorong pendekatan kita ke batasnya dan melihat bagaimana cara mengatasinya.
diff --git a/chapters/id/chapter2/5.mdx b/chapters/id/chapter2/5.mdx
new file mode 100644
index 000000000..575fa9afe
--- /dev/null
+++ b/chapters/id/chapter2/5.mdx
@@ -0,0 +1,211 @@
+
+
+# Menangani Banyak Urutan[[handling-multiple-sequences]]
+
+
+
+
+
+Pada bagian sebelumnya, kita telah menjelajahi kasus penggunaan paling sederhana: melakukan inferensi pada satu urutan pendek. Namun, sejumlah pertanyaan muncul:
+
+- Bagaimana cara menangani beberapa urutan sekaligus?
+- Bagaimana jika panjang urutannya berbeda-beda?
+- Apakah indeks kosa kata satu-satunya input yang dibutuhkan model untuk bekerja optimal?
+- Apakah ada batas maksimal panjang urutan?
+
+Mari kita lihat jenis masalah yang ditimbulkan oleh pertanyaan-pertanyaan ini, dan bagaimana kita bisa menyelesaikannya dengan API 🤗 Transformers.
+
+## Model Mengharapkan Input dalam Bentuk *Batch*[[models-expect-a-batch-of-inputs]]
+
+Pada latihan sebelumnya, Anda telah melihat bagaimana urutan diubah menjadi daftar angka. Mari kita konversi daftar angka ini menjadi tensor dan kirim ke model:
+
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+input_ids = torch.tensor(ids)
+# Baris ini akan gagal.
+model(input_ids)
+```
+
+```python out
+IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
+```
+
+Kenapa ini gagal? Padahal kita sudah mengikuti langkah-langkah seperti pada pipeline di bagian 2.
+
+Masalahnya adalah kita mengirimkan satu urutan (sequence) ke model, padahal model 🤗 Transformers secara default mengharapkan beberapa kalimat (multiple sentences). Di sini, kita mencoba melakukan semua yang dilakukan tokenizer secara otomatis di balik layar ketika kita menerapkannya pada sebuah `sequence`. Namun, jika Anda memperhatikan dengan seksama, Anda akan melihat bahwa tokenizer tidak hanya mengubah daftar ID input menjadi tensor, tapi juga menambahkan satu dimensi di atasnya:
+
+```py
+tokenized_inputs = tokenizer(sequence, return_tensors="pt")
+print(tokenized_inputs["input_ids"])
+```
+
+```python out
+tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172,
+ 2607, 2026, 2878, 2166, 1012, 102]])
+```
+
+Mari kita coba lagi dengan menambahkan dimensi batch:
+
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+input_ids = torch.tensor([ids])
+print("Input IDs:", input_ids)
+
+output = model(input_ids)
+print("Logits:", output.logits)
+```
+
+Kita mencetak ID input serta logits yang dihasilkan — berikut adalah output-nya:
+
+```python out
+Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]]
+Logits: [[-2.7276, 2.8789]]
+```
+
+*Batching* adalah proses mengirim banyak kalimat sekaligus ke model. Jika hanya satu kalimat, Anda tetap bisa membentuk batch dengan satu urutan:
+
+```
+batched_ids = [ids, ids]
+```
+
+Ini adalah batch berisi dua urutan identik!
+
+
+
+✏️ **Coba sendiri!** Ubah `batched_ids` menjadi tensor dan kirim ke model Anda. Periksa apakah Anda mendapatkan *logits* yang sama seperti sebelumnya (tapi dua kali)!
+
+
+
+Batching memungkinkan model untuk bekerja ketika Anda memberikannya beberapa kalimat sekaligus. Menggunakan beberapa urutan (sequence) semudah membuat batch dari satu urutan. Namun, ada masalah kedua. Saat Anda mencoba menggabungkan dua (atau lebih) kalimat dalam satu batch, panjangnya mungkin berbeda-beda. Jika Anda pernah bekerja dengan tensor sebelumnya, Anda tahu bahwa tensor harus memiliki bentuk yang persegi panjang, sehingga Anda tidak bisa langsung mengonversi daftar ID input menjadi tensor. Untuk mengatasi masalah ini, biasanya kita melakukan *padding* pada input.
+
+
+## Padding Input[[padding-the-inputs]]
+
+Berikut adalah contoh daftar yang tidak bisa langsung dikonversi menjadi tensor:
+
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200]
+]
+```
+
+Untuk mengatasi hal ini, kita akan menggunakan *padding* agar tensor kita memiliki bentuk yang persegi panjang. Padding memastikan bahwa semua kalimat memiliki panjang yang sama dengan menambahkan kata khusus yang disebut *padding token* ke kalimat-kalimat yang memiliki jumlah kata lebih sedikit. Sebagai contoh, jika Anda memiliki 10 kalimat dengan 10 kata dan 1 kalimat dengan 20 kata, padding akan memastikan semua kalimat memiliki 20 kata. Dalam contoh kita, tensor yang dihasilkan akan terlihat seperti ini:
+
+```py no-format
+padding_id = 100
+
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, padding_id],
+]
+```
+
+Token padding dapat ditemukan melalui `tokenizer.pad_token_id`. Mari kita coba kirim dua kalimat secara individual dan dalam batch:
+
+```py no-format
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence1_ids = [[200, 200, 200]]
+sequence2_ids = [[200, 200]]
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+print(model(torch.tensor(sequence1_ids)).logits)
+print(model(torch.tensor(sequence2_ids)).logits)
+print(model(torch.tensor(batched_ids)).logits)
+```
+
+```python out
+tensor([[ 1.5694, -1.3895]], grad_fn=)
+tensor([[ 0.5803, -0.4125]], grad_fn=)
+tensor([[ 1.5694, -1.3895],
+ [ 1.3373, -1.2163]], grad_fn=)
+```
+
+Ada sesuatu yang salah dengan *logits* pada prediksi batch kita: baris kedua seharusnya sama dengan *logits* untuk kalimat kedua, tetapi yang kita dapat justru nilai-nilai yang benar-benar berbeda!
+
+Ini terjadi karena fitur utama dari model Transformer adalah lapisan *attention* yang *mengontekstualisasi* setiap token. Lapisan-lapisan ini akan mempertimbangkan token padding karena mereka memperhatikan semua token dalam sebuah urutan. Untuk mendapatkan hasil yang sama ketika memproses kalimat satu per satu dengan panjang berbeda, maupun ketika memproses batch yang berisi kalimat-kalimat tersebut dengan padding, kita perlu memberi tahu lapisan attention agar mengabaikan token padding. Hal ini dilakukan dengan menggunakan *attention mask*.
+
+
+## Attention Mask[[attention-masks]]
+
+*Attention mask* adalah tensor yang memiliki bentuk (shape) yang sama persis dengan tensor input ID, dan diisi dengan angka 0 dan 1: angka 1 menunjukkan bahwa token yang bersangkutan harus diperhatikan (*attended to*), sedangkan angka 0 menunjukkan bahwa token tersebut tidak perlu diperhatikan (yaitu, harus diabaikan oleh lapisan attention dalam model).
+
+Lengkapi contoh sebelumnya dengan attention mask:
+
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+attention_mask = [
+ [1, 1, 1],
+ [1, 1, 0],
+]
+
+outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
+print(outputs.logits)
+```
+
+```python out
+tensor([[ 1.5694, -1.3895],
+ [ 0.5803, -0.4125]], grad_fn=)
+```
+
+Sekarang hasil untuk kalimat kedua di batch cocok dengan hasil individualnya.
+
+Perhatikan bahwa nilai terakhir pada urutan kedua adalah token padding, dan di attention mask diberi nilai 0.
+
+
+
+✏️ **Coba sendiri!** Tokenisasi manual dua kalimat dari bagian 2:
+- "I've been waiting for a HuggingFace course my whole life."
+- "I hate this so much!"
+Kirim secara individual ke model, lalu gabungkan dalam batch dengan padding dan attention mask. Pastikan hasilnya tetap sama!
+
+
+
+## Urutan Lebih Panjang[[longer-sequences]]
+
+Dengan model Transformer, ada batasan panjang urutan (sequence) yang bisa kita berikan ke model. Sebagian besar model hanya dapat menangani urutan hingga 512 atau 1024 token, dan akan mengalami kegagalan jika diminta memproses urutan yang lebih panjang. Ada dua solusi untuk masalah ini:
+
+- Gunakan model yang mendukung panjang urutan yang lebih panjang.
+- Potong (truncate) urutan Anda.
+
+Model memiliki dukungan panjang urutan yang berbeda-beda, dan beberapa memang dirancang khusus untuk menangani urutan yang sangat panjang. [Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) adalah salah satu contohnya, dan lainnya adalah [LED](https://huggingface.co/docs/transformers/model_doc/led). Jika Anda mengerjakan tugas yang memerlukan urutan sangat panjang, kami menyarankan Anda untuk melihat model-model tersebut.
+
+Jika tidak, kami menyarankan Anda untuk memotong (truncate) urutan Anda dengan menentukan parameter `max_sequence_length`:
+
+```py
+sequence = sequence[:max_sequence_length]
+```
diff --git a/chapters/id/chapter2/6.mdx b/chapters/id/chapter2/6.mdx
new file mode 100644
index 000000000..a9beebe2c
--- /dev/null
+++ b/chapters/id/chapter2/6.mdx
@@ -0,0 +1,133 @@
+
+
+# Menggabungkan Semuanya[[putting-it-all-together]]
+
+
+
+Dalam beberapa bagian sebelumnya, kita telah mencoba melakukan sebagian besar proses secara manual. Kita telah mempelajari cara kerja tokenizer dan mengeksplorasi tokenisasi, konversi ke input ID, padding, pemotongan (truncation), dan attention mask.
+
+Namun seperti yang telah kita lihat di bagian 2, API 🤗 Transformers bisa menangani semua ini secara otomatis melalui fungsi tingkat tinggi yang akan kita bahas di sini. Saat Anda memanggil `tokenizer` langsung pada kalimat, Anda akan mendapatkan input yang siap digunakan oleh model:
+
+```py
+from transformers import AutoTokenizer
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+model_inputs = tokenizer(sequence)
+```
+
+Di sini, variabel `model_inputs` sudah berisi semua hal yang diperlukan agar model dapat berfungsi dengan baik. Untuk DistilBERT, ini mencakup `input_ids` dan `attention_mask`. Model lain yang memerlukan input tambahan juga akan diberikan oleh objek `tokenizer`.
+
+Seperti yang akan kita lihat dalam contoh-contoh berikut, metode ini sangat kuat. Pertama, ia bisa menangani satu urutan:
+
+```py
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+model_inputs = tokenizer(sequence)
+```
+
+Ia juga bisa menangani beberapa urutan sekaligus, tanpa mengubah API:
+
+```py
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+model_inputs = tokenizer(sequences)
+```
+
+Ia juga bisa melakukan padding sesuai dengan berbagai tujuan:
+
+```py
+# Padding hingga panjang urutan terpanjang dalam batch
+model_inputs = tokenizer(sequences, padding="longest")
+
+# Padding hingga panjang maksimum model
+# (512 untuk BERT/DistilBERT)
+model_inputs = tokenizer(sequences, padding="max_length")
+
+# Padding hingga panjang maksimum yang ditentukan
+model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
+```
+
+Ia juga bisa memotong (truncate) urutan:
+
+```py
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+# Potong urutan yang melebihi panjang maksimum model
+# (512 untuk BERT/DistilBERT)
+model_inputs = tokenizer(sequences, truncation=True)
+
+# Potong urutan yang melebihi panjang maksimum yang ditentukan
+model_inputs = tokenizer(sequences, max_length=8, truncation=True)
+```
+
+Objek `tokenizer` dapat menangani konversi ke tensor dari framework tertentu, yang kemudian dapat langsung dikirim ke model. Sebagai contoh, pada cuplikan kode berikut kita meminta tokenizer untuk mengembalikan tensor dari berbagai framework — `"pt"` akan mengembalikan tensor PyTorch dan `"np"` akan mengembalikan array NumPy:
+
+```py
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+# Mengembalikan tensor PyTorch
+model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")
+
+# Mengembalikan array NumPy
+model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
+```
+
+## Token Khusus[[special-tokens]]
+
+Jika kita melihat `input_ids` yang dikembalikan oleh tokenizer, kita akan melihat perbedaan kecil dibandingkan sebelumnya:
+
+```py
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+model_inputs = tokenizer(sequence)
+print(model_inputs["input_ids"])
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+print(ids)
+```
+
+```python out
+[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
+[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
+```
+
+Satu token ID ditambahkan di awal dan satu di akhir. Mari kita dekode keduanya untuk melihat apa bedanya:
+
+```py
+print(tokenizer.decode(model_inputs["input_ids"]))
+print(tokenizer.decode(ids))
+```
+
+```python out
+"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
+"i've been waiting for a huggingface course my whole life."
+```
+
+Tokenizer menambahkan kata khusus `[CLS]` di awal dan `[SEP]` di akhir. Ini karena model dilatih sebelumnya dengan token tersebut, jadi untuk hasil inferensi yang konsisten, token ini juga perlu ditambahkan. Perlu dicatat bahwa tidak semua model menambahkan token khusus, dan ada yang menambahkan token yang berbeda. Beberapa hanya di awal, beberapa hanya di akhir — tokenizer tahu apa yang dibutuhkan oleh masing-masing model dan akan mengaturnya untuk Anda.
+
+## Penutup: Dari Tokenizer ke Model[[wrapping-up-from-tokenizer-to-model]]
+
+Sekarang setelah kita melihat semua langkah individual yang digunakan oleh objek `tokenizer` saat diterapkan ke teks, mari kita lihat bagaimana ia menangani beberapa urutan (padding!), urutan sangat panjang (truncation!), dan berbagai jenis tensor langsung melalui API utamanya:
+
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+output = model(**tokens)
+```
diff --git a/chapters/id/chapter2/7.mdx b/chapters/id/chapter2/7.mdx
new file mode 100644
index 000000000..cd594c040
--- /dev/null
+++ b/chapters/id/chapter2/7.mdx
@@ -0,0 +1,18 @@
+# Penggunaan Dasar Selesai![[basic-usage-completed]]
+
+
+
+Kerja bagus telah mengikuti kursus sampai di sini! Sebagai rangkuman, dalam bab ini Anda telah:
+
+- Mempelajari komponen dasar dari model Transformer.
+- Mempelajari apa saja yang membentuk pipeline tokenisasi.
+- Melihat bagaimana cara menggunakan model Transformer dalam praktik.
+- Mempelajari cara memanfaatkan tokenizer untuk mengubah teks menjadi tensor yang dapat dipahami oleh model.
+- Mengonfigurasi tokenizer dan model bersama-sama untuk mendapatkan prediksi dari teks.
+- Mempelajari keterbatasan dari `input_ids`, dan memahami peran `attention_mask`.
+- Bermain-main dengan metode tokenizer yang fleksibel dan dapat dikonfigurasi.
+
+Mulai dari sini, Anda seharusnya sudah bisa menavigasi dokumentasi 🤗 Transformers dengan lebih percaya diri: istilah-istilahnya kini sudah terdengar akrab, dan Anda telah melihat metode-metode yang paling sering digunakan.
diff --git a/chapters/id/chapter2/8.mdx b/chapters/id/chapter2/8.mdx
new file mode 100644
index 000000000..58f71dacc
--- /dev/null
+++ b/chapters/id/chapter2/8.mdx
@@ -0,0 +1,821 @@
+# Penerapan Inferensi yang Dioptimalkan
+
+Dalam bagian ini, kita akan mengeksplorasi kerangka kerja lanjutan untuk mengoptimalkan penerapan LLM: Text Generation Inference (TGI), vLLM, dan llama.cpp. Aplikasi-aplikasi ini terutama digunakan di lingkungan produksi untuk menyajikan LLM kepada pengguna. Bagian ini berfokus pada cara menerapkan kerangka kerja tersebut di produksi, bukan cara menggunakannya untuk inferensi di mesin tunggal.
+
+Kami akan membahas bagaimana alat-alat ini memaksimalkan efisiensi inferensi dan menyederhanakan penerapan Large Language Models di lingkungan produksi.
+
+## Panduan Pemilihan Kerangka Kerja
+
+TGI, vLLM, dan llama.cpp memiliki tujuan serupa namun memiliki karakteristik yang berbeda, sehingga lebih cocok untuk berbagai kasus penggunaan. Mari kita lihat perbedaan utama antara mereka, dengan fokus pada kinerja dan integrasi.
+
+### Manajemen Memori dan Performa
+
+**TGI** dirancang agar stabil dan dapat diprediksi di lingkungan produksi, menggunakan panjang urutan tetap untuk menjaga penggunaan memori yang konsisten. TGI mengelola memori menggunakan Flash Attention 2 dan teknik continuous batching. Ini berarti ia dapat memproses perhitungan attention secara sangat efisien dan menjaga GPU tetap sibuk dengan terus memberikan pekerjaan. Sistem ini dapat memindahkan bagian model antara CPU dan GPU sesuai kebutuhan, yang membantu menangani model yang lebih besar.
+
+
+
+
+Flash Attention adalah teknik yang mengoptimalkan mekanisme attention dalam model transformer dengan mengatasi hambatan bandwidth memori. Seperti yang dibahas dalam [Bab 1.8](/course/chapter1/8), mekanisme attention memiliki kompleksitas kuadrat dan penggunaan memori yang tinggi, sehingga tidak efisien untuk urutan panjang.
+
+Inovasi utama terletak pada cara mengelola transfer memori antara High Bandwidth Memory (HBM) dan cache SRAM yang lebih cepat. Attention tradisional berulang kali mentransfer data antara HBM dan SRAM, menciptakan hambatan dan membuat GPU tidak aktif. Flash Attention memuat data sekali ke SRAM dan melakukan semua perhitungan di sana, meminimalkan transfer memori yang mahal.
+
+Meskipun manfaatnya paling besar saat pelatihan, penggunaan VRAM yang lebih rendah dan efisiensi Flash Attention juga sangat berguna saat inferensi, memungkinkan penyajian LLM yang lebih cepat dan skalabel.
+
+
+**vLLM** menggunakan pendekatan berbeda dengan teknik bernama PagedAttention. Seperti sistem operasi mengelola memori dalam bentuk halaman, vLLM membagi memori model menjadi blok-blok kecil. Sistem cerdas ini memungkinkan penanganan permintaan berukuran berbeda secara fleksibel dan menghindari pemborosan memori. vLLM sangat efisien dalam berbagi memori antar permintaan dan mengurangi fragmentasi memori, menjadikan sistem lebih efisien secara keseluruhan.
+
+
+PagedAttention adalah teknik yang mengatasi hambatan kritis dalam inferensi LLM: manajemen memori KV cache. Seperti yang dijelaskan di [Bab 1.8](/course/chapter1/8), saat menghasilkan teks, model menyimpan key dan value attention (KV cache) untuk setiap token yang dihasilkan agar menghindari perhitungan ulang. KV cache dapat menjadi sangat besar, terutama untuk urutan panjang atau banyak permintaan secara bersamaan.
+
+Inovasi utama vLLM adalah:
+
+1. **Memory Paging**: KV cache dibagi menjadi "halaman" berukuran tetap (seperti virtual memory).
+2. **Penyimpanan Non-Kontigu**: Halaman tidak harus tersimpan secara berurutan di memori GPU.
+3. **Manajemen Tabel Halaman**: Tabel halaman melacak halaman mana milik urutan mana, memungkinkan akses efisien.
+4. **Berbagi Memori**: Untuk operasi seperti sampling paralel, halaman KV cache dapat dibagi antara banyak urutan.
+
+PagedAttention meningkatkan throughput hingga 24x dibandingkan metode tradisional — sangat signifikan untuk produksi. Untuk detail lebih lanjut, lihat [panduan vLLM](https://docs.vllm.ai/en/latest/design/kernel/paged_attention.html).
+
+
+**llama.cpp** adalah implementasi C/C++ yang sangat dioptimalkan, awalnya dirancang untuk menjalankan model LLaMA di perangkat konsumen. Fokusnya pada efisiensi CPU dengan dukungan opsional untuk akselerasi GPU, ideal untuk lingkungan dengan sumber daya terbatas. llama.cpp menggunakan teknik kuantisasi untuk mengurangi ukuran dan kebutuhan memori model sambil mempertahankan kinerja yang baik. Ia juga menyediakan kernel yang dioptimalkan untuk berbagai arsitektur CPU dan manajemen KV cache dasar untuk menghasilkan token secara efisien.
+
+
+Kuantisasi di llama.cpp menurunkan presisi bobot model dari 32-bit atau 16-bit float menjadi format yang lebih rendah seperti INT8 (8-bit), 4-bit, atau bahkan lebih kecil. Ini secara signifikan mengurangi penggunaan memori dan mempercepat inferensi tanpa kehilangan kualitas secara signifikan.
+
+Fitur kuantisasi utama di llama.cpp:
+1. **Banyak Tingkat Kuantisasi**: Mendukung kuantisasi 8-bit, 4-bit, 3-bit, hingga 2-bit
+2. **Format GGML/GGUF**: Format tensor khusus yang dioptimalkan untuk inferensi kuantisasi
+3. **Presisi Campuran**: Bisa menerapkan tingkat kuantisasi berbeda di bagian berbeda dari model
+4. **Optimasi Perangkat Keras**: Kode dioptimalkan untuk berbagai arsitektur CPU (AVX2, AVX-512, NEON)
+
+Pendekatan ini memungkinkan model dengan miliaran parameter dijalankan di perangkat konsumen dengan memori terbatas — sangat cocok untuk deployment lokal dan perangkat edge.
+
+
+### Penerapan dan Integrasi
+
+Sekarang mari kita bahas perbedaan penerapan dan integrasi antar kerangka kerja.
+
+**TGI** unggul dalam penerapan tingkat enterprise dengan fitur siap-produksi. Ini memiliki dukungan bawaan untuk Kubernetes, pemantauan dengan Prometheus dan Grafana, penskalaan otomatis, serta fitur keamanan lengkap. Logging tingkat enterprise dan fitur seperti penyaringan konten serta pembatasan laju (rate limiting) membuatnya aman dan stabil.
+
+**vLLM** menawarkan pendekatan yang lebih fleksibel dan ramah pengembang. Dibangun dengan Python sebagai inti, vLLM bisa dengan mudah menggantikan API OpenAI di aplikasi yang sudah ada. Cocok digunakan dengan Ray untuk mengelola klaster, sangat ideal saat Anda membutuhkan performa tinggi dan kemampuan kustomisasi.
+
+**llama.cpp** mengutamakan kesederhanaan dan portabilitas. Implementasi servernya ringan dan dapat dijalankan di berbagai perangkat — dari server bertenaga tinggi hingga laptop konsumen dan beberapa perangkat seluler kelas atas. Dengan ketergantungan minimal dan inti C/C++ sederhana, sangat mudah diterapkan di lingkungan yang sulit menginstal framework Python. Server ini kompatibel dengan API OpenAI sambil tetap memiliki jejak sumber daya yang jauh lebih kecil dibanding solusi lain.
+
+## Memulai Penggunaan
+
+Mari kita eksplorasi cara menggunakan kerangka kerja ini untuk menerapkan LLM, dimulai dari instalasi dan pengaturan dasar.
+
+### Instalasi dan Pengaturan Dasar
+
+
+
+
+
+TGI mudah dipasang dan digunakan, dengan integrasi mendalam ke ekosistem Hugging Face.
+
+Pertama, jalankan server TGI menggunakan Docker:
+
+```sh
+docker run --gpus all \
+ --shm-size 1g \
+ -p 8080:80 \
+ -v ~/.cache/huggingface:/data \
+ ghcr.io/huggingface/text-generation-inference:latest \
+ --model-id HuggingFaceTB/SmolLM2-360M-Instruct
+```
+
+Lalu berinteraksi dengannya menggunakan `InferenceClient` dari Hugging Face:
+
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(
+ model="http://localhost:8080", # URL untuk server TGI
+)
+
+response = client.text_generation(
+ "Ceritakan sebuah kisah",
+ max_new_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+ details=True,
+ stop_sequences=[],
+)
+print(response.generated_text)
+
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "Kamu adalah asisten yang membantu."},
+ {"role": "user", "content": "Ceritakan sebuah kisah"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+Alternatif lain, gunakan klien OpenAI:
+
+```python
+from openai import OpenAI
+
+client = OpenAI(
+ base_url="http://localhost:8080/v1", # Pastikan menyertakan /v1
+ api_key="not-needed", # Secara default, TGI tidak memerlukan API key
+)
+
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "Kamu adalah asisten yang membantu."},
+ {"role": "user", "content": "Ceritakan sebuah kisah"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+llama.cpp mudah dipasang dan digunakan, hanya membutuhkan dependensi minimal dan mendukung inferensi via CPU dan GPU.
+
+Pertama, instal dan bangun `llama.cpp`:
+
+```sh
+# Klon repositori
+git clone https://github.com/ggerganov/llama.cpp
+cd llama.cpp
+
+# Build project
+make
+
+# Download mode SmolLM2-1.7B-Instruct-GGUF
+curl -L -O https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/resolve/main/smollm2-1.7b-instruct.Q4_K_M.gguf
+```
+
+Lalu jalankan server-nya:
+
+```sh
+# Menjalankan server
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --host 0.0.0.0 \
+ --port 8080 \
+ -c 4096 \
+ --n-gpu-layers 0 # Atur ke angka lebih tinggi untuk menggunakan GPU
+```
+
+Gunakan `InferenceClient` untuk mengaksesnya:
+
+```python
+from huggingface_hub import InferenceClient
+
+# Inisialisasi klien yang mengarah ke server llama.cpp
+client = InferenceClient(
+ model="http://localhost:8080/v1", # URL untuk server llama.cpp
+ token="sk-no-key-required", # server llama.cpp memerlukan placeholder ini
+)
+
+# Pembuatan teks
+response = client.text_generation(
+ "Ceritakan sebuah kisah",
+ max_new_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+ details=True,
+)
+
+print(response.generated_text)
+
+# Untuk format percakapan
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "Kamu adalah asisten yang membantu."},
+ {"role": "user", "content": "Ceritakan sebuah kisah"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+Atau gunakan klien OpenAI:
+
+```python
+from openai import OpenAI
+
+# Inisialisasi klien yang menunjuk ke server llama.cpp
+client = OpenAI(
+ base_url="http://localhost:8080/v1",
+ api_key="sk-no-key-required", # server llama.cpp memerlukan placeholder ini
+)
+
+response = client.chat.completions.create(
+ model="smollm2-1.7b-instruct", # Pengenal model bisa apa saja karena server hanya memuat satu model
+ messages=[
+ {"role": "system", "content": "Kamu adalah asisten yang membantu."},
+ {"role": "user", "content": "Ceritakan sebuah kisah"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+
+
+vLLM mudah dipasang, dengan kompatibilitas API OpenAI dan antarmuka Python bawaan.
+
+Jalankan server API vLLM:
+
+```sh
+python -m vllm.entrypoints.openai.api_server \
+ --model HuggingFaceTB/SmolLM2-360M-Instruct \
+ --host 0.0.0.0 \
+ --port 8000
+```
+
+Lalu berinteraksilah dengannya menggunakan `InferenceClient` dari Hugging Face:
+
+```python
+from huggingface_hub import InferenceClient
+
+# Inisialisasi klien yang menunjuk ke endpoint vLLM
+client = InferenceClient(
+ model="http://localhost:8000/v1", # URL untuk server vLLM
+)
+
+# Pembuatan teks
+response = client.text_generation(
+ "Ceritakan sebuah kisah",
+ max_new_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+ details=True,
+)
+print(response.generated_text)
+
+# Untuk format percakapan
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "Kamu adalah asisten yang membantu."},
+ {"role": "user", "content": "Ceritakan sebuah kisah"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+Atau dengan klien OpenAI:
+
+```python
+from openai import OpenAI
+
+# Inisialisasi klien yang mengarah ke endpoint vLLM
+client = OpenAI(
+ base_url="http://localhost:8000/v1",
+ api_key="not-needed", # Secara bawaan, vLLM tidak memerlukan API key
+)
+
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "Kamu adalah asisten yang membantu."},
+ {"role": "user", "content": "Ceritakan sebuah kisah"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+### Generasi Teks Dasar
+
+Mari kita lihat contoh-contoh generasi teks dengan berbagai framework:
+
+
+
+
+
+Pertama, jalankan TGI dengan parameter lanjutan:
+```sh
+docker run --gpus all \
+ --shm-size 1g \
+ -p 8080:80 \
+ -v ~/.cache/huggingface:/data \
+ ghcr.io/huggingface/text-generation-inference:latest \
+ --model-id HuggingFaceTB/SmolLM2-360M-Instruct \
+ --max-total-tokens 4096 \
+ --max-input-length 3072 \
+ --max-batch-total-tokens 8192 \
+ --waiting-served-ratio 1.2
+```
+
+Gunakan `InferenceClient` untuk generasi teks yang fleksibel:
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(model="http://localhost:8080")
+
+# Contoh parameter lanjutan
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+ ],
+ temperature=0.8,
+ max_tokens=200,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+
+# Generasi teks mentah
+response = client.text_generation(
+ "Tulis cerita kreatif tentang eksplorasi luar angkasa",
+ max_new_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ repetition_penalty=1.1,
+ do_sample=True,
+ details=True,
+)
+print(response.generated_text)
+```
+
+Atau gunakan klien OpenAI:
+```python
+from openai import OpenAI
+
+client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")
+
+# Contoh parameter lanjutan
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+ ],
+ temperature=0.8, # Lebih tinggi untuk hasil yang lebih kreatif
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+Untuk `llama.cpp`, Anda dapat menetapkan parameter lanjutan saat menjalankan server:
+
+```sh
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --host 0.0.0.0 \
+ --port 8080 \
+ -c 4096 \ # Ukuran konteks
+ --threads 8 \ # Jumlah thread CPU yang digunakan
+ --batch-size 512 \ # Ukuran batch evaluasi prompt
+ --n-gpu-layers 0 # Lapisan GPU (0 = hanya CPU)
+```
+
+Gunakan `InferenceClient`:
+
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(model="http://localhost:8080/v1", token="sk-no-key-required")
+
+# Contoh parameter lanjutan
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+ ],
+ temperature=0.8,
+ max_tokens=200,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+
+# Untuk generasi teks langsung
+response = client.text_generation(
+ "Tulis cerita kreatif tentang eksplorasi luar angkasa",
+ max_new_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ repetition_penalty=1.1,
+ details=True,
+)
+print(response.generated_text)
+```
+
+Atau gunakan klien OpenAI untuk mengontrol parameter sampling:
+
+```python
+from openai import OpenAI
+
+client = OpenAI(base_url="http://localhost:8080/v1", api_key="sk-no-key-required")
+
+# Contoh parameter lanjutan
+response = client.chat.completions.create(
+ model="smollm2-1.7b-instruct",
+ messages=[
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+ ],
+ temperature=0.8,
+ top_p=0.95,
+ frequency_penalty=0.5,
+ presence_penalty=0.5,
+ max_tokens=200,
+)
+print(response.choices[0].message.content)
+```
+
+Anda juga dapat menggunakan modul native `llama.cpp` untuk kontrol yang lebih mendetail:
+
+```python
+# Menggunakan paket llama-cpp-python untuk akses langsung ke model
+from llama_cpp import Llama
+
+# Memuat model
+llm = Llama(
+ model_path="smollm2-1.7b-instruct.Q4_K_M.gguf",
+ n_ctx=4096,
+ n_threads=8,
+ n_gpu_layers=0,
+)
+
+# Format prompt sesuai format yang diharapkan oleh model
+prompt = """<|im_start|>system
+Kamu adalah seorang pendongeng kreatif.
+<|im_end|>
+<|im_start|>user
+Tulis sebuah cerita kreatif
+<|im_end|>
+<|im_start|>assistant
+"""
+
+# Menghasilkan respon dengan kontrol parameter yang tepat
+output = llm(
+ prompt,
+ max_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ frequency_penalty=0.5,
+ presence_penalty=0.5,
+ stop=["<|im_end|>"],
+)
+
+print(output["choices"][0]["text"])
+```
+
+
+
+
+Untuk penggunaan lanjutan dengan `vLLM`, Anda dapat menggunakan `InferenceClient`:
+
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(model="http://localhost:8000/v1")
+
+# Contoh parameter lanjutan
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+ ],
+ temperature=0.8,
+ max_tokens=200,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+
+# Untuk generasi teks langsung
+response = client.text_generation(
+ "Tulis cerita kreatif tentang eksplorasi luar angkasa",
+ max_new_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ details=True,
+)
+print(response.generated_text)
+```
+
+Anda juga dapat menggunakan klien OpenAI:
+
+```python
+from openai import OpenAI
+
+client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
+
+# Contoh parameter lanjutan
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+ ],
+ temperature=0.8,
+ top_p=0.95,
+ max_tokens=200,
+)
+print(response.choices[0].message.content)
+```
+
+`vLLM` juga menyediakan antarmuka Python native dengan kontrol yang lebih rinci:
+
+```python
+from vllm import LLM, SamplingParams
+
+# Inisialisasi model dengan parameter lanjutan
+llm = LLM(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ gpu_memory_utilization=0.85,
+ max_num_batched_tokens=8192,
+ max_num_seqs=256,
+ block_size=16,
+)
+
+# Konfigurasi parameter sampling
+sampling_params = SamplingParams(
+ temperature=0.8, # Semakin tinggi, semakin kreatif
+ top_p=0.95, # Pertimbangkan 95% dari massa probabilitas teratas
+ max_tokens=100, # Panjang maksimum
+ presence_penalty=1.1, # Kurangi pengulangan
+ frequency_penalty=1.1, # Kurangi pengulangan
+ stop=["\n\n", "###"], # Urutan berhenti
+)
+
+# Menghasilkan teks
+prompt = "Tulis sebuah cerita kreatif"
+outputs = llm.generate(prompt, sampling_params)
+print(outputs[0].outputs[0].text)
+
+# Untuk interaksi gaya chat
+chat_prompt = [
+ {"role": "system", "content": "Kamu adalah seorang pendongeng kreatif."},
+ {"role": "user", "content": "Tulis sebuah cerita kreatif"},
+]
+formatted_prompt = llm.get_chat_template()(chat_prompt)
+outputs = llm.generate(formatted_prompt, sampling_params)
+print(outputs[0].outputs[0].text)
+```
+
+
+
+
+## Kontrol Generasi Lanjutan
+
+### Pemilihan Token dan Sampling
+
+Proses generasi teks melibatkan pemilihan token berikutnya di setiap langkah. Proses ini bisa dikendalikan melalui berbagai parameter:
+
+1. **Logit Mentah**: Probabilitas awal untuk setiap token
+2. **Temperature**: Mengatur tingkat kreativitas (semakin tinggi, semakin acak)
+3. **Top-p (Nucleus Sampling)**: Memfilter token dengan probabilitas kumulatif X%
+4. **Top-k Filtering**: Membatasi pilihan ke k token dengan kemungkinan tertinggi
+
+Berikut cara mengkonfigurasinya:
+
+
+
+
+
+```python
+client.generate(
+ "Tulis sebuah cerita kreatif",
+ temperature=0.8,
+ top_p=0.95,
+ top_k=50,
+ max_new_tokens=100,
+ repetition_penalty=1.1,
+)
+```
+
+
+
+
+```python
+# Melalui API OpenAI
+response = client.completions.create(
+ model="smollm2-1.7b-instruct", # Nama model (bisa berupa string apa saja untuk server llama.cpp)
+ prompt="Tulis sebuah cerita kreatif",
+ temperature=0.8, # Semakin tinggi, semakin kreatif
+ top_p=0.95, # Pertimbangkan 95% dari massa probabilitas teratas
+ frequency_penalty=1.1, # Kurangi pengulangan
+ presence_penalty=0.1, # Kurangi pengulangan
+ max_tokens=100, # Panjang maksimum
+)
+
+# Melalui akses langsung llama-cpp-python
+output = llm(
+ "Tulis sebuah cerita kreatif",
+ temperature=0.8,
+ top_p=0.95,
+ top_k=50,
+ max_tokens=100,
+ repeat_penalty=1.1,
+)
+```
+
+
+
+
+```python
+params = SamplingParams(
+ temperature=0.8, # Semakin tinggi, semakin kreatif
+ top_p=0.95, # Pertimbangkan 95% dari massa probabilitas teratas
+ top_k=50, # Pertimbangkan 50 token teratas
+ max_tokens=100, # Panjang maksimum
+ presence_penalty=0.1, # Kurangi pengulangan
+)
+llm.generate("Tulis sebuah cerita kreatif", sampling_params=params)
+```
+
+
+
+
+### Mengontrol Pengulangan
+
+Semua framework menyediakan cara untuk mencegah generasi teks yang berulang:
+
+
+
+
+```python
+client.generate(
+ "Tulis teks yang bervariasi",
+ repetition_penalty=1.1, # Penalti untuk token yang diulang
+ no_repeat_ngram_size=3, # Cegah pengulangan 3-gram
+)
+```
+
+
+
+
+```python
+# Melalui API OpenAI
+response = client.completions.create(
+ model="smollm2-1.7b-instruct",
+ prompt="Tulis teks yang bervariasi",
+ frequency_penalty=1.1, # Penalti untuk token yang sering muncul
+ presence_penalty=0.8, # Penalti untuk token yang sudah muncul
+)
+
+# Melalui modul langsung
+output = llm(
+ "Tulis teks yang bervariasi",
+ repeat_penalty=1.1, # Penalti untuk token yang diulang
+ frequency_penalty=0.5, # Penalti tambahan untuk frekuensi tinggi
+ presence_penalty=0.5, # Penalti tambahan untuk token yang sudah muncul
+)
+```
+
+
+
+
+```python
+params = SamplingParams(
+ presence_penalty=0.1, # Penalti untuk keberadaan token
+ frequency_penalty=0.1, # Penalti untuk frekuensi token
+)
+```
+
+
+
+
+### Kontrol Panjang dan Stop Sequences
+
+Anda bisa mengontrol panjang generasi dan menetapkan titik berhenti:
+
+
+
+
+```python
+client.generate(
+ "Hasilkan paragraf singkat",
+ max_new_tokens=100,
+ min_new_tokens=10,
+ stop_sequences=["\n\n", "###"],
+)
+```
+
+
+
+
+```python
+# Melalui API OpenAI
+response = client.completions.create(
+ model="smollm2-1.7b-instruct",
+ prompt="Hasilkan paragraf singkat",
+ max_tokens=100,
+ stop=["\n\n", "###"],
+)
+
+# Melalui modul langsung
+output = llm("Hasilkan paragraf singkat", max_tokens=100, stop=["\n\n", "###"])
+```
+
+
+
+
+```python
+params = SamplingParams(
+ max_tokens=100,
+ min_tokens=10,
+ stop=["###", "\n\n"],
+ ignore_eos=False,
+ skip_special_tokens=True,
+)
+```
+
+
+
+
+## Pengelolaan Memori
+
+Ketiga kerangka kerja menerapkan teknik manajemen memori canggih untuk efisiensi inferensi.
+
+
+
+
+
+TGI menggunakan Flash Attention 2 dan continuous batching:
+
+```sh
+docker run --gpus all -p 8080:80 \
+ --shm-size 1g \
+ ghcr.io/huggingface/text-generation-inference:latest \
+ --model-id HuggingFaceTB/SmolLM2-1.7B-Instruct \
+ --max-batch-total-tokens 8192 \
+ --max-input-length 4096
+```
+
+
+
+
+llama.cpp menggunakan kuantisasi dan tata letak memori yang dioptimalkan:
+
+```sh
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --host 0.0.0.0 \
+ --port 8080 \
+ -c 2048 \ # Ukuran konteks
+ --threads 4 \ # Thread CPU
+ --n-gpu-layers 32 \ # Gunakan lebih banyak lapisan GPU untuk model yang lebih besar
+ --mlock \ # Kunci memori untuk mencegah swapping
+ --cont-batching # Aktifkan batching kontinu
+```
+
+Untuk model terlalu besar bagi GPU Anda, gunakan offloading ke CPU:
+
+```sh
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --n-gpu-layers 20 \ # Simpan 20 lapisan pertama di GPU
+ --threads 8 # Gunakan lebih banyak thread CPU untuk lapisan CPU
+```
+
+
+
+
+vLLM menggunakan PagedAttention untuk manajemen memori optimal:
+
+```python
+from vllm.engine.arg_utils import AsyncEngineArgs
+
+engine_args = AsyncEngineArgs(
+ model="HuggingFaceTB/SmolLM2-1.7B-Instruct",
+ gpu_memory_utilization=0.85,
+ max_num_batched_tokens=8192,
+ block_size=16,
+)
+
+llm = LLM(engine_args=engine_args)
+```
+
+
+
+
+## Sumber
+
+- [Dokumentasi Text Generation Inference](https://huggingface.co/docs/text-generation-inference)
+- [Repositori GitHub TGI](https://github.com/huggingface/text-generation-inference)
+- [Dokumentasi vLLM](https://vllm.readthedocs.io/)
+- [Repositori GitHub vLLM](https://github.com/vllm-project/vllm)
+- [Makalah PagedAttention](https://arxiv.org/abs/2309.06180)
+- [Repositori llama.cpp](https://github.com/ggerganov/llama.cpp)
+- [Repositori llama-cpp-python](https://github.com/abetlen/llama-cpp-python)
diff --git a/chapters/id/chapter2/9.mdx b/chapters/id/chapter2/9.mdx
new file mode 100644
index 000000000..2f5db72cf
--- /dev/null
+++ b/chapters/id/chapter2/9.mdx
@@ -0,0 +1,252 @@
+
+
+
+
+# Kuis Akhir Bab[[end-of-chapter-quiz]]
+
+
+
+### 1. Apa urutan pipeline pemodelan bahasa?
+
+
+
+### 2. Berapa dimensi tensor yang dihasilkan oleh model Transformer dasar, dan apa saja?
+
+
+
+### 3. Mana dari berikut ini yang merupakan contoh tokenisasi sub-kata (*subword*)?
+
+
+
+### 4. Apa itu *model head*?
+
+
+
+### 5. Apa itu `AutoModel`?
+
+AutoTrain kami?"
+ },
+ {
+ text: "Objek yang mengembalikan arsitektur model yang tepat berdasarkan checkpoint",
+ explain: "Tepat sekali: `AutoModel` hanya membutuhkan nama checkpoint untuk memuat arsitektur yang sesuai.",
+ correct: true
+ },
+ {
+ text: "Model yang secara otomatis mendeteksi bahasa input untuk memuat bobot yang tepat",
+ explain: "Beberapa model mendukung banyak bahasa, tetapi tidak ada deteksi bahasa otomatis untuk memilih checkpoint. Gunakan Model Hub untuk itu!"
+ }
+ ]}
+/>
+
+### 6. Teknik apa yang perlu diperhatikan saat membuat batch dari sekuens dengan panjang berbeda?
+
+
+
+### 7. Apa tujuan menerapkan fungsi SoftMax pada output *logits* dari model klasifikasi sekuens?
+
+
+
+### 8. Metode apa yang menjadi inti dari API tokenizer?
+
+
+
+### 9. Apa isi dari variabel `result` dalam contoh kode berikut?
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+result = tokenizer.tokenize("Hello!")
+```
+
+
+
+### 10. Apakah ada yang salah dengan kode berikut?
+
+```py
+from transformers import AutoTokenizer, AutoModel
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+model = AutoModel.from_pretrained("gpt2")
+
+encoded = tokenizer("Hey!", return_tensors="pt")
+result = model(**encoded)
+```
+
+
\ No newline at end of file
diff --git a/chapters/id/chapter3/1.mdx b/chapters/id/chapter3/1.mdx
new file mode 100644
index 000000000..3a2badc59
--- /dev/null
+++ b/chapters/id/chapter3/1.mdx
@@ -0,0 +1,41 @@
+
+
+# Pendahuluan[[introduction]]
+
+
+
+Pada [Bab 2](/course/chapter2) kita telah mempelajari cara menggunakan tokenizer dan model pralatih untuk membuat prediksi. Namun bagaimana jika Anda ingin melakukan fine-tuning pada model pralatih untuk menyelesaikan tugas tertentu? Itulah topik bab ini! Anda akan belajar:
+
+* Cara menyiapkan dataset besar dari Hub menggunakan fitur terbaru dari 🤗 Datasets
+* Cara menggunakan API tingkat tinggi `Trainer` untuk melakukan fine-tuning model dengan praktik terbaik modern
+* Cara mengimplementasikan loop pelatihan kustom dengan teknik optimasi
+* Cara memanfaatkan modul 🤗 Accelerate untuk menjalankan pelatihan terdistribusi dengan mudah di berbagai sistem
+* Cara menerapkan praktik terbaik fine-tuning terkini untuk kinerja maksimal
+
+
+
+📚 **Sumber Daya Penting**: Sebelum memulai, Anda mungkin ingin meninjau [dokumentasi 🤗 Datasets](https://huggingface.co/docs/datasets/) untuk pemrosesan data.
+
+
+
+Bab ini juga akan menjadi pengantar beberapa modul Hugging Face di luar modul 🤗 Transformers! Kita akan melihat bagaimana modul seperti 🤗 Datasets, 🤗 Tokenizers, 🤗 Accelerate, dan 🤗 Evaluate dapat membantu Anda melatih model secara lebih efisien dan efektif.
+
+Setiap bagian utama dalam bab ini akan mengajarkan hal yang berbeda:
+- **Bagian 2**: Pelajari teknik pra-pemrosesan data modern dan penanganan dataset yang efisien
+- **Bagian 3**: Kuasai API Trainer yang kuat dengan semua fitur terbarunya
+- **Bagian 4**: Implementasikan loop pelatihan dari awal dan pahami pelatihan terdistribusi dengan Accelerate
+
+Pada akhir bab ini, Anda akan dapat melakukan fine-tuning model pada dataset Anda sendiri menggunakan baik API tingkat tinggi maupun loop pelatihan kustom, dengan menerapkan praktik terbaik terkini di bidang ini.
+
+
+
+🎯 **Yang Akan Anda Bangun**: Di akhir bab ini, Anda akan telah melakukan fine-tuning pada model BERT untuk klasifikasi teks dan memahami cara menyesuaikan teknik tersebut dengan dataset dan tugas Anda sendiri.
+
+
+
+Bab ini secara eksklusif berfokus pada **PyTorch**, karena telah menjadi kerangka kerja standar untuk riset dan produksi deep learning modern. Kita akan menggunakan API dan praktik terbaik terbaru dari ekosistem Hugging Face.
+
+Untuk mengunggah model yang telah Anda latih ke Hugging Face Hub, Anda akan memerlukan akun Hugging Face: [buat akun](https://huggingface.co/join)
diff --git a/chapters/id/chapter3/2.mdx b/chapters/id/chapter3/2.mdx
new file mode 100644
index 000000000..4c96da195
--- /dev/null
+++ b/chapters/id/chapter3/2.mdx
@@ -0,0 +1,447 @@
+# Memproses Data[[processing-the-data]]
+
+
+
+Melanjutkan contoh dari [bab sebelumnya](/course/chapter2), berikut adalah cara kita melatih classifier urutan pada satu batch:
+
+```python
+import torch
+from torch.optim import AdamW
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+# Sama seperti sebelumnya
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "This course is amazing!",
+]
+batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+
+# Ini yang baru
+batch["labels"] = torch.tensor([1, 1])
+
+optimizer = AdamW(model.parameters())
+loss = model(**batch).loss
+loss.backward()
+optimizer.step()
+```
+
+Tentu saja, hanya melatih model dengan dua kalimat tidak akan memberikan hasil yang baik. Untuk hasil yang lebih baik, Anda perlu menyiapkan dataset yang lebih besar.
+
+Di bagian ini kita akan menggunakan dataset MRPC (Microsoft Research Paraphrase Corpus) sebagai contoh, yang diperkenalkan dalam sebuah [makalah](https://www.aclweb.org/anthology/I05-5002.pdf) oleh William B. Dolan dan Chris Brockett. Dataset ini terdiri dari 5.801 pasangan kalimat, dengan label yang menunjukkan apakah keduanya merupakan parafrasa (yaitu, apakah kedua kalimat memiliki makna yang sama). Dataset ini dipilih karena ukurannya kecil sehingga mudah untuk digunakan dalam eksperimen pelatihan.
+
+### Memuat Dataset dari Hub[[loading-a-dataset-from-the-hub]]
+
+
+
+Hub tidak hanya berisi model; ia juga memiliki banyak dataset dalam berbagai bahasa. Anda dapat menjelajahi dataset [di sini](https://huggingface.co/datasets), dan kami sarankan Anda mencoba memuat dan memproses dataset baru setelah menyelesaikan bagian ini (lihat dokumentasi umum [di sini](https://huggingface.co/docs/datasets/loading)). Untuk saat ini, kita fokus pada dataset MRPC! Dataset ini adalah salah satu dari 10 dataset dalam [benchmark GLUE](https://gluebenchmark.com/), yang digunakan untuk mengukur performa model ML dalam 10 tugas klasifikasi teks berbeda.
+
+Modul 🤗 Datasets menyediakan perintah sederhana untuk mengunduh dan menyimpan dataset dari Hub. Kita bisa mengunduh dataset MRPC seperti ini:
+
+
+
+💡 **Sumber Tambahan**: Untuk teknik dan contoh pemuatan dataset lainnya, lihat [dokumentasi 🤗 Datasets](https://huggingface.co/docs/datasets/).
+
+
+
+```py
+from datasets import load_dataset
+
+raw_datasets = load_dataset("glue", "mrpc")
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 1725
+ })
+})
+```
+
+Seperti yang Anda lihat, kita mendapatkan objek `DatasetDict` yang berisi data pelatihan, validasi, dan pengujian. Masing-masing memiliki kolom (`sentence1`, `sentence2`, `label`, dan `idx`) dan sejumlah baris (misalnya, 3.668 pasangan kalimat di pelatihan, 408 di validasi, dan 1.725 di pengujian).
+
+
+
+Perintah ini akan mengunduh dan menyimpan dataset secara lokal di *~/.cache/huggingface/datasets* secara default. Ingat dari Bab 2 bahwa Anda dapat menyesuaikan folder cache dengan mengatur variabel lingkungan `HF_HOME`.
+
+
+
+Kita dapat mengakses setiap pasangan kalimat dengan melakukan indexing seperti dictionary:
+
+```py
+raw_train_dataset = raw_datasets["train"]
+raw_train_dataset[0]
+```
+
+```python out
+{'idx': 0,
+ 'label': 1,
+ 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
+ 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
+```
+
+Kita dapat melihat bahwa label-labelnya sudah berupa bilangan bulat, jadi kita tidak perlu melakukan praproses pada bagian tersebut. Untuk mengetahui bilangan bulat mana yang sesuai dengan label tertentu, kita bisa memeriksa `features` dari `raw_train_dataset` kita. Ini akan memberi tahu kita tipe dari setiap kolom:
+
+```py
+raw_train_dataset.features
+```
+
+```python out
+{'sentence1': Value(dtype='string', id=None),
+ 'sentence2': Value(dtype='string', id=None),
+ 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
+ 'idx': Value(dtype='int32', id=None)}
+```
+
+Di balik layar, `label` bertipe `ClassLabel`, dan pemetaan dari bilangan bulat ke nama label disimpan di dalam folder *names*. `0` berarti `not_equivalent`, dan `1` berarti `equivalent`.
+
+
+
+✏️ **Coba Sendiri!** Lihat elemen ke-15 dari data pelatihan dan elemen ke-87 dari data validasi. Apa label mereka?
+
+
+
+### Pra-pemrosesan Dataset[[preprocessing-a-dataset]]
+
+
+
+Untuk melakukan praproses pada dataset, kita perlu mengubah teks menjadi angka yang dapat dipahami oleh model. Seperti yang telah Anda lihat di [bab sebelumnya](/course/chapter2), hal ini dilakukan dengan menggunakan tokenizer. Kita dapat memberikan satu kalimat atau daftar kalimat ke tokenizer, sehingga kita bisa langsung melakukan tokenisasi pada semua kalimat pertama dan semua kalimat kedua dari setiap pasangan seperti ini:
+
+```py
+from transformers import AutoTokenizer
+
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
+tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
+```
+
+
+
+💡 **Penjelasan Lanjutan**: Untuk teknik tokenisasi lanjutan, kunjungi [dokumentasi 🤗 Tokenizers](https://huggingface.co/docs/transformers/main/en/tokenizer_summary) dan [panduan tokenisasi di cookbook](https://huggingface.co/learn/cookbook/en/advanced_rag#tokenization-strategies).
+
+
+
+Namun, kita tidak bisa langsung memberikan dua kalimat ke model dan mendapatkan prediksi apakah kedua kalimat tersebut merupakan parafrase atau tidak. Kita perlu menangani kedua kalimat tersebut sebagai pasangan, dan menerapkan praproses yang sesuai. Untungnya, tokenizer juga dapat menerima pasangan kalimat dan mempersiapkannya sesuai dengan yang diharapkan oleh model BERT kita:
+
+```py
+inputs = tokenizer("This is the first sentence.", "This is the second one.")
+inputs
+```
+
+```python out
+{
+ 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+}
+```
+
+Kita telah membahas kunci `input_ids` dan `attention_mask` di [Bab 2](/course/chapter2), tetapi kita menunda pembahasan tentang `token_type_ids`. Dalam contoh ini, `token_type_ids` memberi tahu model bagian mana dari input yang merupakan kalimat pertama dan mana yang merupakan kalimat kedua.
+
+
+
+✏️ **Coba Sendiri!** Ambil elemen ke-15 dari data pelatihan dan tokenisasi kalimatnya secara terpisah serta sebagai pasangan. Apa perbedaannya?
+
+
+
+Jika kita mendekode ID di dalam `input_ids` kembali menjadi kata-kata:
+
+```py
+tokenizer.convert_ids_to_tokens(inputs["input_ids"])
+```
+
+Kita akan mendapatkan:
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+```
+
+Jadi kita melihat bahwa model mengharapkan input dalam format `[CLS] kalimat1 [SEP] kalimat2 [SEP]` ketika terdapat dua kalimat. Menyesuaikan ini dengan `token_type_ids` memberi kita:
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+Seperti yang Anda lihat, bagian input yang sesuai dengan `[CLS] kalimat1 [SEP]` semuanya memiliki token type ID `0`, sementara bagian lainnya, yang sesuai dengan `kalimat2 [SEP]`, semuanya memiliki token type ID `1`.
+
+Perlu dicatat bahwa jika Anda memilih checkpoint yang berbeda, Anda mungkin tidak akan mendapatkan `token_type_ids` dalam hasil tokenisasi (misalnya, `token_type_ids` tidak disediakan jika Anda menggunakan model DistilBERT). `token_type_ids` hanya dikembalikan jika model mengetahui cara menggunakannya, yaitu jika model tersebut telah melihatnya selama pelatihan pralatih.
+
+Dalam hal ini, BERT telah dilatih sebelumnya dengan `token_type_ids`, dan selain tujuan masked language modeling yang telah kita bahas di [Bab 1](/course/chapter1), model ini juga memiliki tujuan tambahan yang disebut _next sentence prediction_. Tujuan dari tugas ini adalah untuk memodelkan hubungan antara pasangan kalimat.
+
+Dalam next sentence prediction, model diberikan pasangan kalimat (dengan token yang secara acak dimasker) dan diminta untuk memprediksi apakah kalimat kedua mengikuti kalimat pertama. Untuk membuat tugas ini tidak sepele, setengah dari waktu pasangan kalimat berasal dari dokumen yang sama secara berurutan, dan setengah lainnya berasal dari dokumen yang berbeda.
+
+Secara umum, Anda tidak perlu khawatir apakah ada `token_type_ids` dalam input yang telah ditokenisasi: selama Anda menggunakan checkpoint yang sama untuk tokenizer dan model, semuanya akan berjalan baik karena tokenizer tahu apa yang perlu diberikan ke modelnya.
+
+Sekarang setelah kita melihat bagaimana tokenizer dapat menangani satu pasangan kalimat, kita bisa menggunakannya untuk melakukan tokenisasi pada seluruh dataset: seperti di [bab sebelumnya](/course/chapter2), kita dapat memberikan daftar pasangan kalimat ke tokenizer dengan memberikan daftar kalimat pertama dan kemudian daftar kalimat kedua. Ini juga kompatibel dengan opsi padding dan truncation yang telah kita bahas di [Bab 2](/course/chapter2). Jadi, salah satu cara untuk melakukan praproses pada dataset pelatihan adalah:
+
+
+```py
+tokenized_dataset = tokenizer(
+ raw_datasets["train"]["sentence1"],
+ raw_datasets["train"]["sentence2"],
+ padding=True,
+ truncation=True,
+)
+```
+
+Pendekatan ini bekerja dengan baik, tetapi memiliki kelemahan yaitu mengembalikan sebuah dictionary (dengan kunci seperti `input_ids`, `attention_mask`, dan `token_type_ids`, serta nilai berupa daftar dari daftar). Selain itu, cara ini hanya akan berhasil jika Anda memiliki cukup RAM untuk menyimpan seluruh dataset selama proses tokenisasi (sementara dataset dari pustaka 🤗 Datasets disimpan sebagai berkas [Apache Arrow](https://arrow.apache.org/) di disk, sehingga hanya sampel yang Anda minta saja yang dimuat ke dalam memori).
+
+Untuk menjaga data tetap dalam bentuk dataset, kita akan menggunakan metode [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map). Metode ini juga memberi kita fleksibilitas tambahan jika kita ingin melakukan praproses lebih dari sekadar tokenisasi. Metode `map()` bekerja dengan menerapkan sebuah fungsi pada setiap elemen dalam dataset, jadi mari kita definisikan sebuah fungsi untuk melakukan tokenisasi pada input kita:
+
+```py
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+```
+
+Fungsi ini menerima sebuah dictionary (seperti item dalam dataset kita) dan mengembalikan dictionary baru dengan kunci `input_ids`, `attention_mask`, dan `token_type_ids`. Perlu dicatat bahwa fungsi ini juga akan berfungsi jika dictionary `example` berisi beberapa sampel sekaligus (setiap kunci berisi daftar kalimat), karena `tokenizer` dapat menangani daftar pasangan kalimat, seperti yang telah kita lihat sebelumnya. Ini memungkinkan kita menggunakan opsi `batched=True` saat memanggil `map()`, yang secara signifikan akan mempercepat proses tokenisasi. `tokenizer` ini didukung oleh tokenizer yang ditulis dalam bahasa Rust dari pustaka [🤗 Tokenizers](https://github.com/huggingface/tokenizers). Tokenizer ini bisa sangat cepat, tetapi hanya jika kita memberinya banyak input sekaligus.
+
+Perhatikan bahwa kita belum menyertakan argumen `padding` dalam fungsi tokenisasi kita saat ini. Hal ini karena melakukan padding pada semua sampel hingga panjang maksimum tidaklah efisien: akan lebih baik jika kita melakukan padding saat membangun sebuah batch, karena kita hanya perlu menyesuaikan padding hingga panjang maksimum dalam batch tersebut, bukan panjang maksimum dalam seluruh dataset. Ini bisa menghemat banyak waktu dan daya komputasi terutama saat panjang input sangat bervariasi!
+
+
+
+📚 **Tips Performa**: Pelajari lebih lanjut tentang teknik pemrosesan data yang efisien di [panduan performa 🤗 Datasets](https://huggingface.co/docs/datasets/about_arrow).
+
+
+
+Berikut adalah cara kita menerapkan fungsi tokenisasi pada seluruh dataset sekaligus. Kita menggunakan `batched=True` dalam pemanggilan `map`, agar fungsi diterapkan pada beberapa elemen dataset sekaligus, bukan satu per satu. Ini memungkinkan praproses berjalan lebih cepat.
+
+```py
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+tokenized_datasets
+```
+
+Cara pustaka 🤗 Datasets menerapkan proses ini adalah dengan menambahkan field baru ke dalam dataset, satu untuk setiap kunci dalam dictionary yang dikembalikan oleh fungsi praproses:
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 1725
+ })
+})
+```
+
+Anda bahkan bisa menggunakan multiprocessing saat menerapkan fungsi praproses dengan `map()` dengan menambahkan argumen `num_proc`. Kami tidak melakukannya di sini karena pustaka 🤗 Tokenizers sudah menggunakan beberapa thread untuk melakukan tokenisasi sampel dengan lebih cepat. Namun, jika Anda tidak menggunakan tokenizer cepat yang didukung oleh pustaka tersebut, penggunaan multiprocessing ini bisa mempercepat proses praproses Anda.
+
+Fungsi `tokenize_function` kita mengembalikan sebuah dictionary dengan kunci `input_ids`, `attention_mask`, dan `token_type_ids`, sehingga ketiga field tersebut akan ditambahkan ke semua bagian (split) dari dataset kita. Perlu dicatat bahwa kita juga bisa mengganti field yang sudah ada jika fungsi praproses kita mengembalikan nilai baru untuk kunci yang sudah ada dalam dataset yang kita terapkan `map()` padanya.
+
+Hal terakhir yang perlu kita lakukan adalah melakukan padding pada semua contoh hingga panjang elemen terpanjang saat kita menggabungkan elemen-elemen ke dalam batch — teknik ini disebut *padding dinamis* (*dynamic padding*).
+
+#### Padding Dinamis[[dynamic-padding]]
+
+
+
+Fungsi yang bertanggung jawab untuk menggabungkan sampel-sampel ke dalam sebuah batch disebut *collate function*. Ini adalah argumen yang bisa Anda berikan saat membangun sebuah `DataLoader`, dengan fungsi default yang hanya akan mengonversi sampel Anda menjadi tensor PyTorch dan menggabungkannya (secara rekursif jika elemen Anda berupa list, tuple, atau dictionary). Namun, ini tidak akan bisa dilakukan dalam kasus kita karena input yang kita miliki tidak semuanya memiliki ukuran yang sama. Kita sengaja menunda proses padding agar hanya dilakukan seperlunya pada setiap batch, guna menghindari input yang terlalu panjang dengan banyak padding. Ini akan mempercepat proses pelatihan secara signifikan, tetapi perlu dicatat bahwa jika Anda melakukan pelatihan di TPU, ini bisa menimbulkan masalah — TPU lebih menyukai bentuk input yang tetap (fixed shapes), meskipun itu berarti harus menambahkan padding ekstra.
+
+
+
+🚀 **Panduan Optimasi**: Untuk detail lebih lanjut tentang mengoptimalkan performa pelatihan, termasuk strategi padding dan pertimbangan saat menggunakan TPU, lihat [dokumentasi performa 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/performance).
+
+
+
+Untuk menerapkannya dalam praktik, kita harus mendefinisikan sebuah *collate function* yang akan menerapkan jumlah padding yang tepat pada item-item dataset yang ingin kita batch-kan bersama. Untungnya, pustaka 🤗 Transformers menyediakan fungsi seperti itu melalui `DataCollatorWithPadding`. Fungsi ini menerima sebuah tokenizer saat diinisialisasi (untuk mengetahui token padding yang digunakan, dan apakah model mengharapkan padding di sebelah kiri atau kanan input), dan akan melakukan semua yang Anda butuhkan:
+
+```py
+from transformers import DataCollatorWithPadding
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+Contoh padding:
+
+```py
+samples = tokenized_datasets["train"][:8]
+samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
+[len(x) for x in samples["input_ids"]]
+```
+
+```python out
+[50, 59, 47, 67, 59, 50, 62, 32]
+```
+
+Tidak mengejutkan, kita mendapatkan sampel dengan panjang yang bervariasi, dari 32 hingga 67. Padding dinamis berarti semua sampel dalam batch ini akan dipadkan hingga panjang 67, yaitu panjang maksimum di dalam batch tersebut. Tanpa padding dinamis, semua sampel harus dipadkan hingga panjang maksimum dari seluruh dataset, atau hingga panjang maksimum yang dapat diterima oleh model. Mari kita periksa kembali apakah `data_collator` kita benar-benar melakukan padding dinamis dengan benar:
+
+```py
+batch = data_collator(samples)
+{k: v.shape for k, v in batch.items()}
+```
+
+```python out
+{'attention_mask': torch.Size([8, 67]),
+ 'input_ids': torch.Size([8, 67]),
+ 'token_type_ids': torch.Size([8, 67]),
+ 'labels': torch.Size([8])}
+```
+
+Terlihat bagus! Sekarang, setelah kita berhasil mengubah teks mentah menjadi batch yang bisa diproses oleh model, kita siap untuk melakukan fine-tuning!
+
+
+
+✏️ **Coba Sendiri!** Ulangi proses pra-pemrosesan pada dataset GLUE SST-2. Dataset ini sedikit berbeda karena terdiri dari kalimat tunggal, bukan pasangan kalimat, tetapi sisanya akan terlihat serupa. Untuk tantangan yang lebih sulit, coba tulis fungsi pra-pemrosesan yang bisa digunakan untuk semua tugas dalam GLUE.
+
+📖 **Latihan Tambahan**: Lihat contoh langsung di [🤗 Transformers examples](https://huggingface.co/docs/transformers/main/en/notebooks).
+
+
+
+Sempurna! Sekarang setelah kita memproses data kita menggunakan praktik terbaik terbaru dari pustaka 🤗 Datasets, kita siap untuk melatih model menggunakan API Trainer modern. Bagian selanjutnya akan menunjukkan cara melakukan fine-tuning model secara efektif menggunakan fitur dan optimasi terbaru dalam ekosistem Hugging Face.
+
+## Kuis Bagian[[section-quiz]]
+
+Uji pemahamanmu tentang konsep pemrosesan data:
+
+### 1. Apa keuntungan utama menggunakan `Dataset.map()` dengan `batched=True`?
+
+
+
+### 2. Mengapa kita menggunakan padding dinamis daripada mem-pad semua urutan ke panjang maksimum dalam dataset?
+
+
+
+### 3. Apa yang direpresentasikan oleh field `token_type_ids` dalam tokenisasi BERT?
+
+
+
+### 4. Saat memuat dataset dengan `load_dataset('glue', 'mrpc')`, apa arti dari argumen kedua?
+
+
+
+### 5. Apa tujuan dari menghapus kolom seperti 'sentence1' dan 'sentence2' sebelum pelatihan?
+
+
+
+
+
+💡 **Inti Penting**:
+- Gunakan `batched=True` dengan `Dataset.map()` untuk mempercepat pra-pemrosesan secara signifikan
+- Padding dinamis dengan `DataCollatorWithPadding` lebih efisien dibandingkan padding dengan panjang tetap
+- Selalu pra-proses data Anda agar sesuai dengan ekspektasi model (tensor numerik, nama kolom yang benar)
+- Pustaka 🤗 Datasets menyediakan alat-alat canggih untuk memproses data secara efisien dalam skala besar
+
+
diff --git a/chapters/id/chapter3/3.mdx b/chapters/id/chapter3/3.mdx
new file mode 100644
index 000000000..5e7ebba46
--- /dev/null
+++ b/chapters/id/chapter3/3.mdx
@@ -0,0 +1,392 @@
+
+
+# Fine-tuning Model dengan Trainer API[[fine-tuning-a-model-with-the-trainer-api]]
+
+
+
+
+
+🤗 Transformers menyediakan kelas `Trainer` untuk membantu Anda melakukan fine-tuning model pralatih apa pun pada dataset Anda menggunakan praktik terbaik modern. Setelah semua praproses data selesai di bagian sebelumnya, hanya ada beberapa langkah lagi untuk mendefinisikan `Trainer`. Bagian yang paling menantang kemungkinan besar adalah menyiapkan lingkungan untuk menjalankan `Trainer.train()`, karena akan berjalan sangat lambat di CPU. Jika Anda tidak memiliki GPU, Anda bisa menggunakan GPU atau TPU gratis di [Google Colab](https://colab.research.google.com/).
+
+
+
+📚 **Sumber Latihan**: Sebelum mulai melatih, kenali terlebih dahulu [panduan pelatihan 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/training) dan contoh praktiknya di [cookbook fine-tuning](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
+
+
+
+Contoh kode di bawah mengasumsikan bahwa Anda telah menjalankan contoh di bagian sebelumnya. Berikut ringkasan singkat apa yang Anda perlukan:
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+### Pelatihan[[training]]
+
+Langkah pertama sebelum kita dapat mendefinisikan `Trainer` adalah membuat kelas `TrainingArguments` yang akan menyimpan semua hyperparameter yang digunakan `Trainer` untuk pelatihan dan evaluasi. Satu-satunya argumen wajib adalah direktori tempat model hasil pelatihan akan disimpan, termasuk checkpoint-checkpoint selama pelatihan. Sisanya bisa dibiarkan default, yang sudah cukup baik untuk fine-tuning dasar.
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments("test-trainer")
+```
+
+Jika Anda ingin model Anda secara otomatis diunggah ke Hub selama pelatihan, tambahkan `push_to_hub=True` dalam `TrainingArguments`. Kita akan mempelajari lebih lanjut tentang ini di [Bab 4](/course/chapter4/3)
+
+
+
+🚀 **Konfigurasi Lanjutan**: Untuk detail lengkap semua argumen pelatihan dan strategi optimasi, lihat [dokumentasi TrainingArguments](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#transformers.TrainingArguments) dan [cookbook konfigurasi pelatihan](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
+
+
+
+Langkah kedua adalah mendefinisikan model. Seperti di [bab sebelumnya](/course/chapter2), kita akan menggunakan `AutoModelForSequenceClassification` dengan dua label:
+
+```py
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Anda akan melihat bahwa, tidak seperti di [Bab 2](/course/chapter2), Anda akan mendapatkan peringatan setelah menginstansiasi model pretrained ini. Hal ini terjadi karena BERT tidak dilatih sebelumnya untuk mengklasifikasikan pasangan kalimat, sehingga kepala (head) dari model pretrained telah dibuang dan diganti dengan kepala baru yang sesuai untuk tugas klasifikasi sekuens. Peringatan tersebut menunjukkan bahwa beberapa bobot tidak digunakan (yaitu yang terkait dengan kepala pretraining yang dihapus), dan beberapa lainnya diinisialisasi secara acak (yaitu yang digunakan untuk kepala baru). Peringatan ini diakhiri dengan anjuran untuk melatih model tersebut — dan itulah yang akan kita lakukan sekarang.
+
+Setelah kita memiliki model, kita bisa mendefinisikan `Trainer` dengan memberikan semua objek yang sudah kita buat sejauh ini — `model`, `training_args`, dataset pelatihan dan validasi, `data_collator`, serta `processing_class`. Parameter `processing_class` adalah fitur baru yang memberi tahu Trainer tokenizer mana yang digunakan:
+
+```py
+from transformers import Trainer
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+)
+```
+
+Jika Anda memberikan tokenizer sebagai `processing_class`, `Trainer` akan secara otomatis menggunakan `DataCollatorWithPadding`. Baris `data_collator=...` bisa diabaikan, tapi kami sertakan di sini untuk menunjukkan bagian penting dari pipeline ini.
+
+
+
+📖 **Pelajari Lebih Lanjut**: Untuk detail lengkap tentang kelas Trainer dan parameternya, kunjungi [dokumentasi Trainer API](https://huggingface.co/docs/transformers/main/en/main_classes/trainer) dan contoh lanjutan di [cookbook pelatihan](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
+
+
+
+Untuk melakukan fine-tuning, kita cukup memanggil metode `train()`:
+
+```py
+trainer.train()
+```
+
+Ini akan memulai pelatihan dan melaporkan loss setiap 500 langkah. Namun, ini tidak akan menunjukkan seberapa baik model Anda karena:
+
+1. Kita belum memberi tahu `Trainer` untuk melakukan evaluasi selama pelatihan dengan mengatur `eval_strategy` di `TrainingArguments` ke salah satu dari `"steps"` (melakukan evaluasi setiap `eval_steps`) atau `"epoch"` (melakukan evaluasi di akhir setiap epoch).
+2. Kita juga belum memberikan fungsi `compute_metrics()` ke `Trainer` untuk menghitung metrik selama evaluasi tersebut (jika tidak, evaluasi hanya akan mencetak nilai loss, yang bukan angka yang mudah diinterpretasikan).
+
+### Evaluasi[[evaluation]]
+
+Mari kita lihat bagaimana cara membangun fungsi `compute_metrics()` yang berguna dan menggunakannya saat kita melatih model berikutnya. Fungsi ini harus menerima objek `EvalPrediction` (yang merupakan tuple bernama dengan field `predictions` dan `label_ids`) dan mengembalikan dictionary yang memetakan string ke angka desimal (string adalah nama metrik yang dikembalikan, dan angka desimal adalah nilai metrik tersebut). Untuk mendapatkan prediksi dari model kita, kita dapat menggunakan perintah `Trainer.predict()`:
+
+```py
+predictions = trainer.predict(tokenized_datasets["validation"])
+print(predictions.predictions.shape, predictions.label_ids.shape)
+```
+
+```python out
+(408, 2) (408,)
+```
+
+Output dari metode `predict()` adalah tuple bernama lain yang memiliki tiga field: `predictions`, `label_ids`, dan `metrics`. Field `metrics` hanya akan berisi loss pada dataset yang diberikan, serta metrik waktu (berapa lama waktu yang dibutuhkan untuk melakukan prediksi, secara total dan rata-rata). Setelah kita melengkapi fungsi `compute_metrics()` dan memberikannya ke `Trainer`, field tersebut juga akan berisi metrik yang dikembalikan oleh `compute_metrics()`.
+
+Seperti yang bisa kamu lihat, `predictions` adalah array dua dimensi dengan bentuk 408 x 2 (408 adalah jumlah elemen dalam dataset yang kita gunakan). Ini adalah nilai logit untuk setiap elemen dari dataset yang kita berikan ke `predict()` (seperti yang telah kamu lihat di [bab sebelumnya](/course/chapter2), semua model Transformer mengembalikan logit). Untuk mengubahnya menjadi prediksi yang dapat kita bandingkan dengan label kita, kita perlu mengambil indeks dengan nilai maksimum pada sumbu kedua:
+
+```py
+import numpy as np
+
+preds = np.argmax(predictions.predictions, axis=-1)
+```
+
+Sekarang kita bisa membandingkan `preds` tersebut dengan label yang sebenarnya. Untuk membangun fungsi `compute_metrics()`, kita akan menggunakan metrik dari pustaka 🤗 [Evaluate](https://github.com/huggingface/evaluate/). Kita bisa memuat metrik yang digunakan untuk dataset MRPC semudah kita memuat dataset-nya, kali ini dengan fungsi `evaluate.load()`. Objek yang dikembalikan memiliki metode `compute()` yang bisa kita gunakan untuk menghitung metrik evaluasi:
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+metric.compute(predictions=preds, references=predictions.label_ids)
+```
+
+```python out
+{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
+```
+
+
+
+Pelajari metrik evaluasi lebih lanjut di [dokumentasi 🤗 Evaluate](https://huggingface.co/docs/evaluate/).
+
+
+
+Hasil yang Anda peroleh bisa berbeda-beda, karena inisialisasi acak pada kepala model dapat memengaruhi metrik yang dicapai. Di sini, kita dapat melihat bahwa model kita memiliki akurasi sebesar 85,78% pada set validasi dan skor F1 sebesar 89,97. Kedua metrik tersebut digunakan untuk mengevaluasi hasil pada dataset MRPC dalam benchmark GLUE. Tabel pada [makalah BERT](https://arxiv.org/pdf/1810.04805.pdf) melaporkan skor F1 sebesar 88,9 untuk model dasar. Model tersebut adalah model `uncased`, sementara kita saat ini menggunakan model `cased`, yang menjelaskan hasil yang lebih baik.
+
+
+Menggabungkan semuanya, kita mendapatkan fungsi `compute_metrics()` kita:
+
+```py
+def compute_metrics(eval_preds):
+ metric = evaluate.load("glue", "mrpc")
+ logits, labels = eval_preds
+ predictions = np.argmax(logits, axis=-1)
+ return metric.compute(predictions=predictions, references=labels)
+```
+
+Dan untuk melihatnya digunakan secara langsung dalam melaporkan metrik di akhir setiap epoch, berikut adalah cara kita mendefinisikan `Trainer` baru dengan fungsi `compute_metrics()` ini:
+
+```py
+training_args = TrainingArguments("test-trainer", eval_strategy="epoch")
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+ compute_metrics=compute_metrics,
+)
+```
+
+Perlu dicatat bahwa kita membuat objek `TrainingArguments` baru dengan `eval_strategy` diatur ke `"epoch"` dan juga model baru — jika tidak, kita hanya akan melanjutkan pelatihan dari model yang sudah kita latih sebelumnya. Untuk memulai proses pelatihan baru, kita jalankan:
+
+```python
+trainer.train()
+```
+
+Kali ini, proses pelatihan akan melaporkan *validation loss* dan metrik evaluasi di akhir setiap epoch, selain *training loss*. Sekali lagi, akurasi atau skor F1 yang Anda dapatkan mungkin sedikit berbeda karena inisialisasi acak pada kepala model, namun tetap akan berada di kisaran yang serupa.
+
+### Fitur Pelatihan Lanjutan[[advanced-training-features]]
+
+`Trainer` memiliki banyak fitur bawaan yang membuat praktik terbaik deep learning modern lebih mudah diakses:
+
+**Pelatihan dengan Presisi Campuran (Mixed Precision)**: Gunakan `fp16=True` dalam training arguments untuk pelatihan yang lebih cepat dan penggunaan memori yang lebih rendah:
+
+```python
+training_args = TrainingArguments(
+ "test-trainer",
+ eval_strategy="epoch",
+ fp16=True, # Aktifkan pelatihan dengan presisi campuran
+)
+```
+
+**Akumulasi Gradien (Gradient Accumulation)**: Berguna untuk mendapatkan ukuran batch efektif yang lebih besar saat memori GPU terbatas:
+
+```python
+training_args = TrainingArguments(
+ "test-trainer",
+ eval_strategy="epoch",
+ per_device_train_batch_size=4,
+ gradient_accumulation_steps=4, # Ukuran batch efektif = 4 * 4 = 16
+)
+```
+
+**Pengaturan Learning Rate**: `Trainer` secara default menggunakan peluruhan linear (linear decay), tetapi Anda bisa menyesuaikannya:
+
+```python
+training_args = TrainingArguments(
+ "test-trainer",
+ eval_strategy="epoch",
+ learning_rate=2e-5,
+ lr_scheduler_type="cosine", # Coba scheduler lain
+)
+```
+
+
+
+🎯 **Optimasi Performa**: Untuk teknik pelatihan lanjutan seperti pelatihan terdistribusi, optimasi memori, dan optimasi spesifik perangkat keras, jelajahi [panduan performa 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/performance).
+
+
+
+`Trainer` langsung dapat digunakan di beberapa GPU atau TPU, dan menyediakan banyak opsi untuk pelatihan terdistribusi. Kita akan membahas semua fitur tersebut di Bab 10.
+
+Ini menutup pengenalan tentang fine-tuning menggunakan API `Trainer`. Contoh penerapan fine-tuning pada berbagai tugas NLP umum akan dijelaskan di [Bab 7](/course/chapter7), tetapi untuk sekarang mari kita lihat bagaimana melakukan hal yang sama menggunakan *training loop* PyTorch murni.
+
+
+
+📝 **Lebih Banyak Contoh**: Lihat koleksi lengkap [notebook 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/notebooks).
+
+
+
+## Kuis Bagian[[section-quiz]]
+
+Uji pemahaman Anda tentang API `Trainer` dan konsep fine-tuning:
+
+### 1. Apa tujuan dari parameter processing_class dalam Trainer?
+
+
+
+### 2. Parameter TrainingArguments mana yang mengontrol seberapa sering evaluasi dilakukan saat pelatihan?
+
+
+
+### 3. Apa yang diaktifkan oleh fp16=True dalam TrainingArguments?
+
+
+
+### 4. Apa peran fungsi compute_metrics dalam Trainer?
+
+
+
+### 5. Apa yang terjadi jika Anda tidak menyediakan eval_dataset ke Trainer?
+
+
+
+### 6. Apa itu gradient accumulation dan bagaimana cara mengaktifkannya?
+
+
+
+
+
+💡 **Inti Penting:**
+- API `Trainer` menyediakan antarmuka tingkat tinggi yang menangani sebagian besar kompleksitas pelatihan
+- Gunakan `processing_class` untuk menentukan tokenizer yang tepat dalam pemrosesan data
+- `TrainingArguments` mengontrol semua aspek pelatihan: learning rate, batch size, strategi evaluasi, dan optimasi
+- `compute_metrics` memungkinkan evaluasi dengan metrik khusus, bukan hanya loss pelatihan
+- Fitur modern seperti presisi campuran (`fp16=True`) dan gradient accumulation bisa secara signifikan meningkatkan efisiensi pelatihan
+
+
diff --git a/chapters/id/chapter3/4.mdx b/chapters/id/chapter3/4.mdx
new file mode 100644
index 000000000..e4c3296e7
--- /dev/null
+++ b/chapters/id/chapter3/4.mdx
@@ -0,0 +1,570 @@
+# *Training loop* Lengkap[[a-full-training]]
+
+
+
+
+
+Sekarang kita akan melihat bagaimana mencapai hasil yang sama seperti di bagian sebelumnya *tanpa* menggunakan kelas `Trainer`, dengan mengimplementasikan *training loop* dari nol menggunakan praktik terbaik PyTorch modern. Sekali lagi, kita asumsikan Anda telah menyelesaikan pemrosesan data di bagian 2. Berikut ringkasan singkat yang mencakup semua yang akan Anda butuhkan:
+
+
+
+🏗️ **Pelatihan dari Nol**: Bagian ini membangun dari konten sebelumnya. Untuk panduan menyeluruh mengenai *training loop* PyTorch dan praktik terbaik, lihat [dokumentasi pelatihan 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/training#train-in-native-pytorch) dan [custom training cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu#model).
+
+
+
+```python
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+### Persiapan untuk Pelatihan[[prepare-for-training]]
+
+Sebelum menulis *training loop*, kita perlu mendefinisikan beberapa objek. Yang pertama adalah *dataloader* yang akan kita gunakan untuk melakukan iterasi per batch. Tapi sebelum itu, kita harus melakukan sedikit pasca-pemrosesan pada `tokenized_datasets` untuk menangani beberapa hal yang sebelumnya dilakukan otomatis oleh `Trainer`. Secara spesifik, kita perlu:
+
+- Menghapus kolom yang tidak dibutuhkan model (seperti `sentence1` dan `sentence2`).
+- Mengganti nama kolom `label` menjadi `labels` (karena model mengharapkan argumen bernama `labels`).
+- Mengatur format dataset agar mengembalikan tensor PyTorch, bukan list biasa.
+
+Langkah-langkah ini dapat dilakukan seperti berikut:
+
+```python
+tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
+tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
+tokenized_datasets.set_format("torch")
+tokenized_datasets["train"].column_names
+```
+
+Kita bisa periksa bahwa hasil akhirnya hanya memiliki kolom yang dapat diterima oleh model:
+
+```python
+["attention_mask", "input_ids", "labels", "token_type_ids"]
+```
+
+Sekarang kita bisa mendefinisikan *dataloader*:
+
+```python
+from torch.utils.data import DataLoader
+
+train_dataloader = DataLoader(
+ tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
+)
+```
+
+Untuk memastikan tidak ada kesalahan, kita bisa lihat isi satu batch:
+
+```python
+for batch in train_dataloader:
+ break
+{k: v.shape for k, v in batch.items()}
+```
+
+```python
+{
+ "attention_mask": torch.Size([8, 65]),
+ "input_ids": torch.Size([8, 65]),
+ "labels": torch.Size([8]),
+ "token_type_ids": torch.Size([8, 65]),
+}
+```
+
+Catatan: bentuk tensor (shape) mungkin akan sedikit berbeda karena `shuffle=True` dan padding dilakukan hingga panjang maksimum dalam setiap batch.
+
+Sekarang setelah kita benar-benar selesai dengan praproses data (sebuah tujuan yang memuaskan namun sering kali sulit dicapai bagi praktisi ML), mari kita beralih ke model. Kita menginisialisasinya persis seperti yang kita lakukan di bagian sebelumnya:
+
+```python
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Untuk memastikan bahwa semuanya berjalan lancar selama pelatihan, kita memasukkan batch kita ke dalam model ini:
+
+```python
+outputs = model(**batch)
+print(outputs.loss, outputs.logits.shape)
+```
+
+```python out
+tensor(0.5441, grad_fn=) torch.Size([8, 2])
+```
+
+Semua model 🤗 Transformers akan mengembalikan nilai loss ketika `labels` diberikan, dan kita juga akan mendapatkan logits (dua untuk setiap input dalam batch kita, sehingga menghasilkan tensor berukuran 8 x 2).
+
+Sekarang kita hampir siap menulis *training loop*! Yang tersisa hanyalah optimizer dan scheduler. Kita akan meniru pengaturan default dari `Trainer`. Optimizer yang digunakan adalah `AdamW`:
+
+```python
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=5e-5)
+```
+
+
+
+💡 **Tips Optimasi Modern**: Untuk performa lebih baik, Anda bisa coba:
+- **AdamW dengan weight decay**: `AdamW(model.parameters(), lr=5e-5, weight_decay=0.01)`
+- **Adam 8-bit**: Gunakan `bitsandbytes` untuk optimasi yang hemat memori
+- **Learning rate berbeda**: Biasanya 1e-5 hingga 3e-5 lebih baik untuk model besar
+
+🚀 **Sumber Optimasi**: Pelajari lebih lanjut di [panduan optimasi 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/performance#optimizer)
+
+
+
+Akhirnya, scheduler laju pembelajaran (learning rate scheduler) yang digunakan secara default adalah penurunan linier dari nilai maksimum (5e-5) ke 0. Untuk mendefinisikannya dengan benar, kita perlu mengetahui jumlah langkah pelatihan (training steps) yang akan dilakukan, yaitu jumlah epoch yang ingin dijalankan dikalikan dengan jumlah batch pelatihan (yang merupakan panjang dari dataloader pelatihan kita). `Trainer` menggunakan tiga epoch secara default, jadi kita akan mengikuti pengaturan tersebut:
+
+```python
+from transformers import get_scheduler
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dataloader)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+print(num_training_steps)
+```
+
+```python
+1377
+```
+
+### *Training Loop*[[the-training-loop]]
+
+Satu hal terakhir: kita akan ingin menggunakan GPU jika tersedia (karena pada CPU, pelatihan bisa memakan waktu beberapa jam dibandingkan hanya beberapa menit di GPU). Untuk melakukan ini, kita mendefinisikan sebuah `device` tempat kita akan menempatkan model dan batch kita:
+
+```python
+import torch
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+model.to(device)
+device
+```
+
+```python
+device(type="cuda")
+```
+
+Sekarang kita siap untuk melakukan pelatihan! Untuk mengetahui sejauh mana proses pelatihan telah berlangsung, kita menambahkan progress bar berdasarkan jumlah langkah pelatihan kita, menggunakan pustaka `tqdm`:
+
+```python
+from tqdm.auto import tqdm
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+ loss.backward()
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+
+
+💡 **Optimasi Pelatihan Modern**: Untuk efisiensi pelatihan yang lebih baik, pertimbangkan:
+
+- **Gradient Clipping**: Tambahkan `torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)` sebelum `optimizer.step()`
+- **Presisi Campuran (Mixed Precision)**: Gunakan `torch.cuda.amp.autocast()` dan `GradScaler`
+- **Gradient Accumulation**: Kumpulkan gradien dari beberapa batch sebelum melakukan update
+- **Checkpointing**: Simpan model secara berkala untuk mencegah kehilangan data jika pelatihan terputus
+
+🔧 **Panduan Implementasi**: Lihat contoh di [efisiensi pelatihan 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/perf_train_gpu_one) dan [daftar optimizer](https://huggingface.co/docs/transformers/main/en/optimizers)
+
+
+
+Anda dapat melihat bahwa inti dari *training loop* ini mirip seperti yang diperkenalkan di awal. Karena kita belum menambahkan evaluasi atau logging, loop ini tidak akan memberi tahu performa model. Untuk itu, kita perlu menambahkan *evaluation loop*.
+
+### Loop Evaluasi[[the-evaluation-loop]]
+
+Seperti yang telah kita lakukan sebelumnya, kita akan menggunakan metrik dari pustaka 🤗 Evaluate. Kita sudah melihat metode `metric.compute()`, tetapi metrik sebenarnya bisa mengakumulasi batch secara bertahap menggunakan metode `add_batch()`. Setelah semua batch dikumpulkan, kita bisa menghitung hasil akhirnya dengan `metric.compute()`. Berikut implementasi lengkap dalam loop evaluasi:
+
+
+
+📊 **Praktik Terbaik Evaluasi**: Untuk strategi evaluasi dan metrik yang lebih canggih, jelajahi [dokumentasi 🤗 Evaluate](https://huggingface.co/docs/evaluate/) dan [evaluation cookbook komprehensif](https://github.com/huggingface/evaluation-guidebook).
+
+
+
+```python
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+model.eval()
+for batch in eval_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ logits = outputs.logits
+ predictions = torch.argmax(logits, dim=-1)
+ metric.add_batch(predictions=predictions, references=batch["labels"])
+
+metric.compute()
+```
+
+```python out
+{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
+```
+
+Hasil Anda mungkin sedikit berbeda karena inisialisasi acak pada kepala model dan *shuffling* data, tetapi seharusnya berada pada kisaran yang sama.
+
+
+
+✏️ **Coba Sendiri!** Modifikasi training loop sebelumnya untuk melakukan fine-tuning pada dataset SST-2.
+
+
+
+### Percepat Training Loop Anda dengan 🤗 Accelerate[[supercharge-your-training-loop-with-accelerate]]
+
+
+
+Loop pelatihan yang kita definisikan sebelumnya bekerja dengan baik pada satu CPU atau GPU. Namun, dengan menggunakan pustaka [🤗 Accelerate](https://github.com/huggingface/accelerate), hanya dengan beberapa penyesuaian kita dapat mengaktifkan pelatihan terdistribusi pada beberapa GPU atau TPU. 🤗 Accelerate secara otomatis menangani kompleksitas pelatihan terdistribusi, mixed precision, dan penempatan perangkat (device placement). Dimulai dari pembuatan dataloader untuk pelatihan dan validasi, berikut adalah tampilan loop pelatihan manual kita:
+
+
+
+⚡ **Pendalaman Accelerate**: Pelajari semua tentang pelatihan terdistribusi, presisi campuran, dan optimasi perangkat keras di [dokumentasi 🤗 Accelerate](https://huggingface.co/docs/accelerate/) serta contoh praktis di [dokumentasi Transformers](https://huggingface.co/docs/transformers/main/en/accelerate).
+
+
+
+```python
+from accelerate import Accelerator
+from torch.optim import AdamW
+from transformers import AutoModelForSequenceClassification, get_scheduler
+
+accelerator = Accelerator()
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+train_dl, eval_dl, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dl)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dl:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Langkah pertama adalah mengimpor dan membuat objek `Accelerator`, yang akan secara otomatis mengatur konfigurasi pelatihan terdistribusi sesuai lingkungan Anda. 🤗 Accelerate akan menangani semua penempatan perangkat, jadi Anda tidak perlu lagi menggunakan `model.to(device)` — meskipun jika ingin, Anda bisa ganti dengan `accelerator.device`.
+
+Kemudian, sebagian besar pekerjaan dilakukan pada baris yang mengirimkan dataloader, model, dan optimizer ke `accelerator.prepare()`. Baris ini akan membungkus objek-objek tersebut dalam kontainer yang sesuai untuk memastikan bahwa pelatihan terdistribusi Anda berjalan sebagaimana mestinya. Perubahan lain yang perlu dilakukan adalah menghapus baris yang memindahkan batch ke `device` (jika Anda ingin tetap menggunakannya, Anda bisa menggantinya dengan `accelerator.device`) dan mengganti `loss.backward()` dengan `accelerator.backward(loss)`.
+
+
+⚠️ Untuk mendapatkan kecepatan maksimum pada Cloud TPU, sebaiknya Anda melakukan padding ke panjang tetap dengan `padding="max_length"` dan `max_length` saat menggunakan tokenizer.
+
+
+Jika ingin mencoba langsung, berikut versi lengkap training loop menggunakan 🤗 Accelerate:
+
+```python
+from accelerate import Accelerator
+from torch.optim import AdamW
+from transformers import AutoModelForSequenceClassification, get_scheduler
+
+accelerator = Accelerator()
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+train_dl, eval_dl, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dl)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dl:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Meletakkan ini dalam sebuah skrip `train.py` akan membuat skrip tersebut dapat dijalankan pada jenis setup terdistribusi apa pun. Untuk mencobanya di setup terdistribusi Anda, jalankan perintah berikut:
+
+```bash
+accelerate config
+```
+
+yang akan meminta Anda menjawab beberapa pertanyaan dan menyimpan jawaban Anda dalam sebuah file konfigurasi yang digunakan oleh perintah berikut:
+
+```bash
+accelerate launch train.py
+```
+
+yang akan menjalankan pelatihan terdistribusi.
+
+Jika Anda ingin mencoba ini di sebuah Notebook (misalnya, untuk menguji dengan TPU di Colab), cukup tempelkan kodenya ke dalam sebuah `training_function()` dan jalankan sel terakhir dengan:
+
+```python
+from accelerate import notebook_launcher
+
+notebook_launcher(training_function)
+```
+
+Contoh lainnya tersedia di [repo 🤗 Accelerate](https://github.com/huggingface/accelerate/tree/main/examples).
+
+
+
+🌐 **Pelatihan Terdistribusi**: Untuk panduan menyeluruh tentang pelatihan multi-GPU dan multi-node, lihat [panduan pelatihan terdistribusi 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/perf_train_gpu_many) dan [scaling training cookbook](https://huggingface.co/docs/transformers/main/en/accelerate).
+
+
+
+### Langkah Selanjutnya dan Praktik Terbaik[[next-steps-and-best-practices]]
+
+Setelah Anda mempelajari cara melakukan pelatihan dari awal, berikut beberapa pertimbangan tambahan untuk digunakan dalam produksi:
+
+**Evaluasi Model**: Jangan hanya bergantung pada akurasi. Gunakan pustaka 🤗 Evaluate untuk evaluasi yang komprehensif.
+
+**Tuning Hyperparameter**: Gunakan pustaka seperti Optuna atau Ray Tune untuk optimasi sistematis.
+
+**Pemantauan Model**: Pantau metrik pelatihan, kurva pembelajaran, dan performa validasi.
+
+**Berbagi Model**: Setelah pelatihan selesai, unggah ke Hugging Face Hub agar bisa digunakan komunitas.
+
+**Efisiensi**: Untuk model besar, gunakan teknik seperti *gradient checkpointing*, fine-tuning parameter-efisien (LoRA, AdaLoRA), atau metode kuantisasi.
+
+Ini mengakhiri pembahasan mendalam kita tentang fine-tuning menggunakan *training loop* kustom. Keterampilan yang Anda pelajari di sini akan sangat berguna ketika Anda butuh kontrol penuh atas proses pelatihan atau ingin menerapkan logika pelatihan khusus yang tidak didukung oleh API `Trainer`.
+
+## Kuis Bagian[[section-quiz]]
+
+Uji pemahaman Anda tentang *custom training loop* dan teknik pelatihan lanjutan:
+
+### 1. Apa perbedaan utama antara optimizer Adam dan AdamW?
+
+
+
+### 2. Apa urutan operasi yang benar dalam training loop?
+
+
+
+### 3. Apa fungsi utama dari pustaka 🤗 Accelerate?
+
+
+
+### 4. Mengapa kita memindahkan batch ke `device` dalam training loop?
+
+
+
+### 5. Apa yang dilakukan `model.eval()` sebelum evaluasi?
+
+
+
+### 6. Apa tujuan penggunaan `torch.no_grad()` saat evaluasi?
+
+
+
+### 7. Apa yang berubah ketika Anda menggunakan 🤗 Accelerate dalam training loop?
+
+
+
+
+
+💡 **Poin Penting:**
+
+- Training loop manual memberi Anda kontrol penuh, tapi perlu memahami urutan operasi yang tepat: forward → backward → optimizer step → scheduler step → zero gradients
+- Gunakan AdamW dengan regularisasi weight decay sebagai optimizer utama untuk model transformer
+- Selalu gunakan `model.eval()` dan `torch.no_grad()` saat evaluasi untuk hasil yang benar dan efisien
+- 🤗 Accelerate memungkinkan pelatihan terdistribusi dengan perubahan kode minimal
+- Manajemen device sangat penting untuk operasi PyTorch yang sukses
+- Teknik modern seperti mixed precision, akumulasi gradien, dan gradient clipping dapat meningkatkan efisiensi pelatihan secara signifikan
+
+
diff --git a/chapters/id/chapter3/5.mdx b/chapters/id/chapter3/5.mdx
new file mode 100644
index 000000000..d0050df76
--- /dev/null
+++ b/chapters/id/chapter3/5.mdx
@@ -0,0 +1,415 @@
+# Memahami Learning Curve[[understanding-learning-curves]]
+
+
+
+Sekarang Anda telah mempelajari cara melakukan fine-tuning menggunakan baik `Trainer` API maupun loop pelatihan kustom, penting untuk memahami cara menafsirkan hasilnya. **Learning curve** adalah alat visual yang sangat berharga untuk mengevaluasi performa model selama pelatihan dan mendeteksi masalah sejak dini.
+
+Di bagian ini, kita akan mempelajari cara membaca dan menafsirkan kurva akurasi dan loss, memahami bentuk-bentuk kurva yang umum, serta belajar menangani berbagai masalah yang muncul saat pelatihan.
+
+## Apa itu Learning Curve?[[what-are-learning-curves]]
+
+**Learning curve** adalah representasi visual dari metrik performa model selama pelatihan. Dua kurva paling penting yang perlu dipantau:
+
+- **Loss curve**: Menunjukkan perubahan error model selama langkah-langkah pelatihan atau per epoch.
+- **Accuracy curve**: Menunjukkan persentase prediksi benar selama pelatihan.
+
+Kurva ini membantu kita memahami apakah model sedang belajar dengan baik dan memberikan wawasan tentang perlu atau tidaknya penyesuaian strategi pelatihan. Dalam Transformers, metrik ini dihitung untuk setiap batch dan dapat divisualisasikan menggunakan alat seperti [Weights & Biases (W&B)](https://wandb.ai/).
+
+### Kurva Loss[[loss-curves]]
+
+Kurva loss menunjukkan bagaimana error model menurun dari waktu ke waktu. Dalam pelatihan yang sukses, kurva biasanya terlihat seperti ini:
+
+
+
+- **Loss awal tinggi**: Model belum dioptimalkan, jadi prediksi awal buruk.
+- **Loss menurun**: Seiring pelatihan, loss seharusnya menurun secara bertahap.
+- **Konvergen**: Akhirnya loss akan stabil di nilai rendah, menunjukkan model telah mempelajari pola data.
+
+Contoh penggunaan `Trainer` API untuk melacak loss dan visualisasi dengan W&B:
+
+```python
+from transformers import Trainer, TrainingArguments
+import wandb
+
+wandb.init(project="transformer-fine-tuning", name="bert-mrpc-analysis")
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ eval_strategy="steps",
+ eval_steps=50,
+ save_steps=100,
+ logging_steps=10,
+ num_train_epochs=3,
+ per_device_train_batch_size=16,
+ per_device_eval_batch_size=16,
+ report_to="wandb",
+)
+
+trainer = Trainer(
+ model=model,
+ args=training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+ compute_metrics=compute_metrics,
+)
+
+trainer.train()
+```
+
+### Kurva Akurasi[[accuracy-curves]]
+
+Kurva akurasi menunjukkan persentase prediksi benar dari waktu ke waktu.
+
+
+
+- **Mulai rendah**: Akurasi awal rendah karena model belum belajar.
+- **Meningkat**: Akurasi bertambah seiring model belajar pola dalam data.
+- **Plateau**: Akurasi naik dalam lonjakan, bukan garis mulus.
+
+
+
+💡 **Kenapa Kurva Akurasi "Steppy"**: Berbeda dari loss yang kontinu, akurasi dihitung dari prediksi diskret. Peningkatan kecil pada kepercayaan model tidak akan mengubah akurasi sampai nilai prediksi melewati ambang klasifikasi.
+
+
+
+### Konvergensi[[convergence]]
+
+Konvergensi terjadi saat performa model stabil dan kurva loss serta akurasi mendatar.
+
+
+
+Setelah model konvergen, ia siap digunakan untuk membuat prediksi pada data baru.
+
+## Menafsirkan Pola Kurva[[interpreting-learning-curve-patterns]]
+
+Berikut bentuk-bentuk kurva umum dan artinya.
+
+### Kurva Sehat[[healthy-learning-curves]]
+
+Pelatihan yang berjalan baik biasanya menghasilkan kurva seperti berikut:
+
+
+
+Mari kita lihat ilustrasi di atas. Gambar tersebut menampilkan kurva loss (di sebelah kiri) dan kurva akurasi yang sesuai (di sebelah kanan). Kedua kurva ini memiliki karakteristik yang berbeda.
+
+Kurva loss menunjukkan nilai loss model seiring waktu. Awalnya, nilai loss tinggi lalu secara bertahap menurun, yang menunjukkan bahwa model mengalami peningkatan. Penurunan nilai loss mengindikasikan bahwa model membuat prediksi yang lebih baik, karena loss merepresentasikan kesalahan antara output yang diprediksi dan output sebenarnya.
+
+Sekarang mari kita alihkan fokus ke kurva akurasi. Kurva ini merepresentasikan akurasi model seiring waktu. Kurva akurasi dimulai dari nilai yang rendah dan meningkat seiring pelatihan berlangsung. Akurasi mengukur proporsi instance yang diklasifikasikan dengan benar. Jadi, saat kurva akurasi naik, ini menandakan bahwa model membuat lebih banyak prediksi yang benar.
+
+Satu perbedaan mencolok antara kedua kurva adalah kelancaran dan keberadaan "dataran tinggi" (plateau) pada kurva akurasi. Sementara loss menurun secara halus, dataran tinggi pada kurva akurasi menunjukkan lonjakan diskrit dalam akurasi alih-alih peningkatan yang kontinu. Perilaku ini disebabkan oleh cara pengukuran akurasi. Loss bisa membaik jika output model semakin mendekati target, bahkan jika prediksi akhirnya masih salah. Namun, akurasi hanya meningkat ketika prediksi melampaui ambang batas dan menjadi benar.
+
+Sebagai contoh, dalam kasus klasifikasi biner yang membedakan kucing (0) dan anjing (1), jika model memprediksi 0,3 untuk gambar anjing (nilai sebenarnya 1), ini dibulatkan menjadi 0 dan merupakan klasifikasi yang salah. Jika pada langkah berikutnya model memprediksi 0,4, ini tetap salah. Nilai loss akan menurun karena 0,4 lebih dekat ke 1 dibandingkan 0,3, tetapi akurasi tetap tidak berubah, menciptakan dataran tinggi. Akurasi hanya akan naik ketika model memprediksi nilai lebih dari 0,5 yang dibulatkan menjadi 1.
+
+
+
+**Karakteristik kurva yang sehat:**
+- **Penurunan loss yang halus**: Baik loss pelatihan maupun validasi menurun secara stabil
+- **Performa pelatihan/validasi yang serupa**: Perbedaan kecil antara metrik pelatihan dan validasi
+- **Konvergensi**: Kurva mulai mendatar, menandakan model telah mempelajari pola-pola
+
+
+
+### Contoh Praktis[[practical-examples]]
+
+Mari kita bahas beberapa contoh praktis dari kurva pembelajaran. Pertama, kita akan menyoroti beberapa pendekatan untuk memantau kurva pembelajaran selama pelatihan. Di bawah ini, kita akan menguraikan berbagai pola yang dapat diamati dalam kurva pembelajaran.
+
+#### Selama Pelatihan[[during-training]]
+
+Selama proses pelatihan (setelah Anda menjalankan `trainer.train()`), Anda dapat memantau indikator-indikator utama berikut:
+
+1. **Konvergensi loss**: Apakah nilai loss masih menurun atau sudah mendatar?
+2. **Tanda-tanda overfitting**: Apakah loss validasi mulai meningkat sementara loss pelatihan menurun?
+3. **Learning rate**: Apakah kurvanya terlalu liar (learning rate terlalu tinggi) atau terlalu datar (learning rate terlalu rendah)?
+4. **Stabilitas**: Apakah ada lonjakan atau penurunan tiba-tiba yang menandakan adanya masalah?
+
+#### Setelah Pelatihan[[after-training]]
+
+Setelah proses pelatihan selesai, Anda dapat menganalisis keseluruhan kurva untuk memahami performa model.
+
+1. **Performa akhir**: Apakah model mencapai tingkat performa yang dapat diterima?
+2. **Efisiensi**: Apakah performa yang sama bisa dicapai dengan jumlah epoch yang lebih sedikit?
+3. **Generalisasi**: Seberapa dekat performa pelatihan dan validasi?
+4. **Tren**: Apakah pelatihan tambahan kemungkinan akan meningkatkan performa?
+
+
+
+🔍 **Fitur Dashboard W&B**: Weights & Biases secara otomatis membuat plot interaktif dan menarik dari kurva pembelajaran Anda. Anda dapat:
+- Membandingkan beberapa percobaan secara berdampingan
+- Menambahkan metrik dan visualisasi kustom
+- Menyiapkan notifikasi untuk perilaku anomali
+- Membagikan hasil kepada tim Anda
+
+Pelajari lebih lanjut di [dokumentasi Weights & Biases](https://docs.wandb.ai/).
+
+
+#### Overfitting[[overfitting]]
+
+Overfitting terjadi ketika model terlalu banyak mempelajari data pelatihan sehingga tidak mampu melakukan generalisasi ke data lain (yang direpresentasikan oleh set validasi).
+
+
+
+**Gejala:**
+
+- Loss pelatihan terus menurun sementara loss validasi meningkat atau mendatar
+- Terdapat celah besar antara akurasi pelatihan dan validasi
+- Akurasi pelatihan jauh lebih tinggi daripada akurasi validasi
+
+**Solusi untuk overfitting:**
+- **Regularisasi**: Tambahkan dropout, weight decay, atau teknik regularisasi lainnya
+- **Early stopping**: Hentikan pelatihan saat performa validasi tidak lagi membaik
+- **Augmentasi data**: Tingkatkan keragaman data pelatihan
+- **Kurangi kompleksitas model**: Gunakan model yang lebih kecil atau dengan parameter lebih sedikit
+
+Contoh berikut menggunakan *early stopping* untuk mencegah overfitting. Kita menetapkan `early_stopping_patience` ke 3, artinya jika loss validasi tidak membaik selama 3 epoch berturut-turut, pelatihan akan dihentikan.
+
+```python
+# Contoh deteksi overfitting dengan early stopping
+from transformers import EarlyStoppingCallback
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ eval_strategy="steps",
+ eval_steps=100,
+ save_strategy="steps",
+ save_steps=100,
+ load_best_model_at_end=True,
+ metric_for_best_model="eval_loss",
+ greater_is_better=False,
+ num_train_epochs=10, # Disetel tinggi, tapi akan dihentikan lebih awal
+)
+
+# Tambahkan early stopping untuk mencegah overfitting
+trainer = Trainer(
+ model=model,
+ args=training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+ compute_metrics=compute_metrics,
+ callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
+)
+```
+
+#### 2. Underfitting[[underfitting]]
+
+**Underfitting** terjadi ketika model terlalu sederhana untuk menangkap pola yang mendasari dalam data. Hal ini bisa terjadi karena beberapa alasan:
+
+- Model terlalu kecil atau tidak memiliki kapasitas untuk mempelajari pola
+- Learning rate terlalu rendah, menyebabkan proses belajar menjadi lambat
+- Dataset terlalu kecil atau tidak representatif terhadap masalah
+- Model tidak di-regularisasi dengan baik
+
+
+
+**Gejala:**
+- Baik loss pelatihan maupun validasi tetap tinggi
+- Performa model stagnan sejak awal pelatihan
+- Akurasi pelatihan lebih rendah dari yang diharapkan
+
+**Solusi untuk underfitting:**
+- **Tingkatkan kapasitas model**: Gunakan model yang lebih besar atau tambahkan parameter
+- **Latih lebih lama**: Tambah jumlah epoch
+- **Sesuaikan learning rate**: Coba berbagai nilai learning rate
+- **Periksa kualitas data**: Pastikan data telah diproses dengan baik
+
+Pada contoh di bawah, kita melatih lebih banyak epoch untuk melihat apakah model dapat mempelajari pola dalam data.
+
+```python
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ -num_train_epochs=5,
+ +num_train_epochs=10,
+)
+```
+
+#### 3. Kurva Pembelajaran Tidak Stabil[[erratic-learning-curves]]
+
+Kurva pembelajaran yang tidak stabil terjadi ketika model tidak belajar secara efektif. Ini bisa disebabkan oleh beberapa faktor:
+
+- Learning rate terlalu tinggi, menyebabkan model melewati parameter optimal
+- Ukuran batch terlalu kecil, menyebabkan pembelajaran menjadi lambat
+- Model tidak di-regularisasi dengan baik, menyebabkan overfitting pada data pelatihan
+- Dataset tidak diproses dengan baik, menyebabkan model belajar dari noise
+
+
+
+**Gejala:**
+- Fluktuasi yang sering pada nilai loss atau akurasi
+- Kurva menunjukkan variansi tinggi atau ketidakstabilan
+- Performa naik-turun tanpa tren yang jelas
+
+Baik kurva pelatihan maupun validasi menunjukkan perilaku yang tidak stabil.
+
+
+
+**Solusi untuk kurva tidak stabil:**
+- **Turunkan learning rate**: Kurangi langkah pembelajaran untuk pelatihan yang lebih stabil
+- **Perbesar batch size**: Batch yang lebih besar memberikan gradien yang lebih stabil
+- **Gradient clipping**: Mencegah gradien meledak
+- **Preprocessing data yang lebih baik**: Pastikan kualitas data konsisten
+
+Pada contoh di bawah, kita menurunkan learning rate dan memperbesar batch size.
+
+```python
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ -learning_rate=1e-5,
+ +learning_rate=1e-4,
+ -per_device_train_batch_size=16,
+ +per_device_train_batch_size=32,
+)
+```
+
+## Poin-Poin Penting[[key-takeaways]]
+
+Memahami kurva pembelajaran sangat penting untuk menjadi praktisi machine learning yang efektif. Alat visual ini memberikan umpan balik langsung tentang kemajuan pelatihan model dan membantu Anda membuat keputusan yang tepat kapan harus menghentikan pelatihan, menyesuaikan hyperparameter, atau mencoba pendekatan berbeda. Dengan latihan, Anda akan mengembangkan intuisi tentang seperti apa kurva pembelajaran yang sehat dan cara mengatasi masalah yang muncul.
+
+
+
+💡 **Poin-Poin Penting:**
+- Kurva pembelajaran adalah alat penting untuk memahami kemajuan pelatihan model
+- Pantau baik kurva loss maupun akurasi, namun pahami bahwa keduanya memiliki karakteristik berbeda
+- Overfitting ditunjukkan oleh performa pelatihan/validasi yang menyimpang
+- Underfitting ditunjukkan oleh performa buruk di data pelatihan dan validasi
+- Alat seperti Weights & Biases mempermudah pelacakan dan analisis kurva pembelajaran
+- Early stopping dan regularisasi yang tepat bisa mengatasi sebagian besar masalah pelatihan umum
+
+🔬 **Langkah Selanjutnya**: Latih kemampuan Anda dalam menganalisis kurva pembelajaran lewat eksperimen fine-tuning Anda sendiri. Coba berbagai hyperparameter dan amati bagaimana bentuk kurva berubah. Pengalaman langsung adalah cara terbaik untuk mengembangkan intuisi dalam membaca kemajuan pelatihan.
+
+
+
+## Kuis Bagian[[section-quiz]]
+
+Uji pemahaman Anda tentang kurva pembelajaran dan analisis pelatihan:
+
+### 1. Apa arti umum ketika loss pelatihan menurun tapi loss validasi mulai meningkat?
+
+
+
+### 2. Mengapa kurva akurasi sering menunjukkan pola bertingkat atau seperti dataran tinggi daripada peningkatan yang halus?
+
+
+
+### 3. Apa pendekatan terbaik ketika Anda mengamati kurva pembelajaran yang tidak stabil dan sangat berfluktuasi?
+
+
+
+### 4. Kapan Anda harus mempertimbangkan penggunaan *early stopping*?
+
+
+
+### 5. Apa yang menunjukkan bahwa model Anda mungkin mengalami *underfitting*?
+
+
\ No newline at end of file
diff --git a/chapters/id/chapter3/6.mdx b/chapters/id/chapter3/6.mdx
new file mode 100644
index 000000000..9157ff21c
--- /dev/null
+++ b/chapters/id/chapter3/6.mdx
@@ -0,0 +1,46 @@
+
+
+# Fine-tuning, Selesai![[fine-tuning-check]]
+
+
+
+Itu tadi materi yang komprehensif! Dalam dua bab pertama, Anda telah mempelajari tentang model dan tokenizer, dan sekarang Anda sudah tahu cara melakukan fine-tuning pada data Anda sendiri menggunakan praktik terbaik modern. Sebagai rangkuman, di bab ini Anda telah:
+
+* Mempelajari tentang dataset di [Hub](https://huggingface.co/datasets) dan teknik pemrosesan data modern
+* Belajar cara memuat dan memproses dataset secara efisien, termasuk menggunakan dynamic padding dan data collator
+* Melakukan fine-tuning dan evaluasi menggunakan `Trainer` API tingkat tinggi dengan fitur terbaru
+* Mengimplementasikan loop pelatihan kustom dari awal menggunakan PyTorch
+* Menggunakan 🤗 Accelerate agar kode pelatihan Anda dapat berjalan mulus di banyak GPU atau TPU
+* Menerapkan teknik optimasi modern seperti mixed precision training dan gradient accumulation
+
+
+
+🎉 **Selamat!** Anda telah menguasai dasar-dasar fine-tuning model transformer. Anda sekarang siap untuk mengerjakan proyek ML dunia nyata!
+
+📖 **Lanjut Belajar**: Jelajahi sumber berikut untuk memperdalam pemahaman Anda:
+- [Panduan tugas 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/tasks/sequence_classification) untuk tugas-tugas NLP spesifik
+- [Contoh notebook 🤗 Transformers](https://huggingface.co/docs/transformers/main/en/notebooks) untuk referensi praktik yang lengkap
+
+🚀 **Langkah Selanjutnya**:
+- Coba lakukan fine-tuning pada dataset Anda sendiri dengan teknik yang sudah Anda pelajari
+- Bereksperimen dengan berbagai arsitektur model yang tersedia di [Hugging Face Hub](https://huggingface.co/models)
+- Bergabunglah dengan [komunitas Hugging Face](https://discuss.huggingface.co/) untuk berbagi proyek dan mendapatkan bantuan
+
+
+
+Ini baru permulaan perjalanan Anda bersama 🤗 Transformers. Di bab selanjutnya, kita akan mempelajari cara membagikan model dan tokenizer Anda ke komunitas serta berkontribusi pada ekosistem model pralatih yang terus berkembang.
+
+Kemampuan yang Anda kembangkan di sini – pra-pemrosesan data, konfigurasi pelatihan, evaluasi, dan optimasi – merupakan fondasi dari setiap proyek machine learning. Apakah Anda sedang mengerjakan klasifikasi teks, named entity recognition, question answering, atau tugas NLP lainnya, teknik-teknik ini akan sangat berguna.
+
+
+
+💡 **Tips Profesional untuk Sukses**:
+- Selalu mulai dengan baseline yang kuat menggunakan `Trainer` API sebelum membuat loop pelatihan kustom
+- Gunakan 🤗 Hub untuk menemukan model pralatih yang paling mendekati tugas Anda agar hasil awal lebih baik
+- Pantau pelatihan Anda dengan metrik evaluasi yang tepat dan jangan lupa menyimpan checkpoint
+- Manfaatkan komunitas – bagikan model dan dataset Anda untuk membantu orang lain dan dapatkan umpan balik
+
+
diff --git a/chapters/id/chapter3/7.mdx b/chapters/id/chapter3/7.mdx
new file mode 100644
index 000000000..ff35a8183
--- /dev/null
+++ b/chapters/id/chapter3/7.mdx
@@ -0,0 +1,26 @@
+
+
+# Sertifikat Akhir Bab
+
+
+
+Selamat atas penyelesaian kursus ini! Anda telah mempelajari cara melakukan fine-tuning pada model pralatih, memahami kurva pembelajaran, dan membagikan model Anda kepada komunitas. Sekarang saatnya menguji pemahaman Anda melalui kuis dan mendapatkan sertifikat Anda.
+
+Untuk mengikuti kuis, ikuti langkah-langkah berikut:
+
+1. Masuk ke akun Hugging Face Anda.
+2. Jawab pertanyaan-pertanyaan dalam kuis.
+3. Kirimkan jawaban Anda.
+
+## Kuis Pilihan Ganda
+
+Dalam kuis ini, Anda akan diminta untuk memilih jawaban yang benar dari beberapa pilihan. Kami akan menguji pemahaman Anda tentang dasar-dasar fine-tuning terawasi (supervised finetuning).
+
+
diff --git a/chapters/id/chapter4/1.mdx b/chapters/id/chapter4/1.mdx
new file mode 100644
index 000000000..2f3d80e35
--- /dev/null
+++ b/chapters/id/chapter4/1.mdx
@@ -0,0 +1,22 @@
+# The Hugging Face Hub[[the-hugging-face-hub]]
+
+
+
+[Hugging Face Hub](https://huggingface.co/) –- situs utama kami –- adalah platform pusat yang memungkinkan siapa pun untuk menemukan, menggunakan, dan berkontribusi terhadap model dan dataset mutakhir. Platform ini menjadi rumah bagi berbagai macam model, dengan lebih dari 10.000 model yang tersedia secara publik. Kita akan fokus pada model-model di bab ini, dan akan melihat dataset di Bab 5.
+
+Model-model di Hub tidak terbatas pada 🤗 Transformers atau bahkan NLP saja. Ada model dari [Flair](https://github.com/flairNLP/flair) dan [AllenNLP](https://github.com/allenai/allennlp) untuk NLP, [Asteroid](https://github.com/asteroid-team/asteroid) dan [pyannote](https://github.com/pyannote/pyannote-audio) untuk suara, serta [timm](https://github.com/rwightman/pytorch-image-models) untuk visi komputer, hanya untuk menyebut beberapa.
+
+Setiap model ini dihosting sebagai repositori Git, yang memungkinkan adanya versi dan replikasi. Membagikan model di Hub berarti membukanya untuk komunitas dan membuatnya dapat diakses oleh siapa saja yang ingin menggunakannya dengan mudah, sehingga mereka tidak perlu melatih model sendiri dan dapat membagikan serta menggunakannya dengan lebih sederhana.
+
+Selain itu, membagikan model di Hub secara otomatis juga menyebarkan API Inferensi yang dihosting untuk model tersebut. Siapa pun di komunitas bebas mencobanya langsung di halaman model, dengan input kustom dan widget yang sesuai.
+
+Yang terbaik adalah, membagikan dan menggunakan model publik di Hub sepenuhnya gratis! [Paket berbayar](https://huggingface.co/pricing) juga tersedia jika Anda ingin membagikan model secara privat.
+
+Video di bawah ini menunjukkan cara menavigasi Hub.
+
+
+
+Memiliki akun huggingface.co diperlukan untuk mengikuti bagian ini, karena kita akan membuat dan mengelola repositori di Hugging Face Hub: [buat akun](https://huggingface.co/join)
diff --git a/chapters/id/chapter4/2.mdx b/chapters/id/chapter4/2.mdx
new file mode 100644
index 000000000..86b9aca41
--- /dev/null
+++ b/chapters/id/chapter4/2.mdx
@@ -0,0 +1,96 @@
+
+
+# Menggunakan Model yang Telah Dilatih[[using-pretrained-models]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Model Hub mempermudah pemilihan model yang sesuai, sehingga penggunaannya dalam pustaka mana pun dapat dilakukan hanya dengan beberapa baris kode. Mari kita lihat bagaimana cara menggunakan salah satu model ini, dan bagaimana cara berkontribusi kembali ke komunitas.
+
+Misalnya kita sedang mencari model berbasis bahasa Prancis yang dapat melakukan pengisian topeng (mask filling).
+
+
+
+
+
+Kita memilih checkpoint `camembert-base` untuk mencobanya. Identifier `camembert-base` adalah satu-satunya yang dibutuhkan untuk mulai menggunakannya! Seperti yang telah Anda lihat di bab sebelumnya, kita dapat menginstansiasinya menggunakan fungsi `pipeline()`:
+
+```py
+from transformers import pipeline
+
+camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
+results = camembert_fill_mask("Le camembert est :)")
+```
+
+```python out
+[
+ {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
+ {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
+ {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
+ {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
+ {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
+]
+```
+
+Seperti yang Anda lihat, memuat model ke dalam pipeline sangatlah mudah. Satu-satunya hal yang perlu diperhatikan adalah bahwa checkpoint yang dipilih cocok untuk tugas yang akan dijalankan. Misalnya, di sini kita memuat checkpoint `camembert-base` dalam pipeline `fill-mask`, yang sepenuhnya tepat. Namun jika kita memuat checkpoint ini dalam pipeline `text-classification`, hasilnya tidak akan masuk akal karena head dari `camembert-base` tidak cocok untuk tugas tersebut! Kami menyarankan untuk menggunakan pemilih tugas (task selector) pada antarmuka Hugging Face Hub untuk memilih checkpoint yang sesuai:
+
+
+
+
+
+Anda juga dapat menginstansiasi checkpoint menggunakan arsitektur model secara langsung:
+
+{#if fw === 'pt'}
+```py
+from transformers import CamembertTokenizer, CamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = CamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+Namun, kami menyarankan menggunakan [`Auto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes), karena kelas ini dirancang untuk tidak bergantung pada arsitektur tertentu. Sementara contoh kode sebelumnya membatasi pengguna pada checkpoint yang bisa dimuat dalam arsitektur CamemBERT, penggunaan `Auto*` classes membuat pergantian checkpoint menjadi lebih mudah:
+
+```py
+from transformers import AutoTokenizer, AutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = AutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{:else}
+```py
+from transformers import CamembertTokenizer, TFCamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+Namun, kami menyarankan menggunakan [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes), karena kelas ini dirancang untuk tidak bergantung pada arsitektur tertentu. Sementara contoh kode sebelumnya membatasi pengguna pada checkpoint yang bisa dimuat dalam arsitektur CamemBERT, penggunaan `TFAuto*` classes membuat pergantian checkpoint menjadi lebih mudah:
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{/if}
+
+
+Saat menggunakan model yang telah dilatih sebelumnya, pastikan untuk memeriksa bagaimana model tersebut dilatih, pada dataset apa, batasannya, dan biasnya. Semua informasi ini seharusnya tersedia di kartu model (model card)-nya.
+
diff --git a/chapters/id/chapter4/3.mdx b/chapters/id/chapter4/3.mdx
new file mode 100644
index 000000000..157372762
--- /dev/null
+++ b/chapters/id/chapter4/3.mdx
@@ -0,0 +1,631 @@
+
+
+# Membagikan Model yang Telah Dilatih[[sharing-pretrained-models]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Dalam langkah-langkah berikut, kita akan melihat cara termudah untuk membagikan model yang telah dilatih ke 🤗 Hub. Tersedia berbagai alat dan utilitas yang mempermudah proses berbagi dan memperbarui model langsung di Hub, yang akan kita eksplorasi di bawah ini.
+
+
+
+Kami mendorong semua pengguna yang melatih model untuk berkontribusi dengan membagikan model mereka kepada komunitas — membagikan model, bahkan jika dilatih pada dataset yang sangat spesifik, dapat membantu orang lain dengan menghemat waktu dan sumber daya komputasi serta memberikan akses ke artefak terlatih yang berguna. Sebagai gantinya, Anda juga bisa mendapatkan manfaat dari pekerjaan yang telah dilakukan oleh orang lain!
+
+Ada tiga cara untuk membuat repositori model baru:
+
+- Menggunakan API `push_to_hub`
+- Menggunakan pustaka Python `huggingface_hub`
+- Menggunakan antarmuka web
+
+Setelah Anda membuat repositori, Anda dapat mengunggah berkas ke dalamnya melalui `git` dan `git-lfs`. Kita akan membahas proses pembuatan repositori model dan pengunggahan file ke dalamnya pada bagian-bagian berikut.
+
+## Menggunakan API `push_to_hub`[[using-the-pushtohub-api]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Cara termudah untuk mengunggah file ke Hub adalah dengan memanfaatkan API `push_to_hub`.
+
+Sebelum melanjutkan, Anda perlu membuat token autentikasi agar API `huggingface_hub` mengetahui siapa Anda dan namespace mana yang Anda miliki akses tulisnya. Pastikan Anda berada di lingkungan yang telah terinstal `transformers` (lihat [Setup](/course/chapter0)). Jika Anda berada di notebook, Anda bisa menggunakan fungsi berikut untuk login:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Di terminal, Anda bisa menjalankan:
+
+```bash
+huggingface-cli login
+```
+
+Pada kedua kasus, Anda akan diminta memasukkan nama pengguna dan kata sandi Anda, yang sama dengan yang Anda gunakan untuk masuk ke Hub. Jika Anda belum memiliki akun di Hub, Anda bisa membuatnya [di sini](https://huggingface.co/join).
+
+Bagus! Sekarang Anda memiliki token autentikasi yang tersimpan di folder cache Anda. Mari buat beberapa repositori!
+
+{#if fw === 'pt'}
+
+Jika Anda telah bereksperimen dengan API `Trainer` untuk melatih model, cara termudah untuk mengunggahnya ke Hub adalah dengan menyetel `push_to_hub=True` saat mendefinisikan `TrainingArguments` Anda:
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
+)
+```
+
+Saat Anda memanggil `trainer.train()`, `Trainer` akan mengunggah model Anda ke Hub setiap kali disimpan (di sini setiap epoch) ke repositori dalam namespace Anda. Repositori tersebut akan dinamai sesuai direktori output yang Anda pilih (di sini `bert-finetuned-mrpc`), tetapi Anda dapat memilih nama lain dengan `hub_model_id = "nama_lain"`.
+
+Untuk mengunggah model ke organisasi tempat Anda menjadi anggota, cukup gunakan `hub_model_id = "organisasi_saya/nama_repo"`.
+
+Setelah pelatihan selesai, Anda sebaiknya menjalankan `trainer.push_to_hub()` terakhir untuk mengunggah versi terakhir model Anda. Ini juga akan menghasilkan model card secara otomatis dengan metadata yang relevan, melaporkan hyperparameter dan hasil evaluasi! Berikut adalah contoh isi dari model card tersebut:
+
+
+
+
+
+{:else}
+
+Jika Anda menggunakan Keras untuk melatih model, cara termudah untuk mengunggahnya ke Hub adalah dengan menambahkan `PushToHubCallback` saat memanggil `model.fit()`:
+
+```py
+from transformers import PushToHubCallback
+
+callback = PushToHubCallback(
+ "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
+)
+```
+
+Kemudian tambahkan `callbacks=[callback]` saat memanggil `model.fit()`. Callback ini akan mengunggah model Anda ke Hub setiap kali disimpan (di sini setiap epoch) dalam repositori di namespace Anda. Repositori tersebut akan dinamai sesuai dengan direktori output yang Anda pilih (di sini `bert-finetuned-mrpc`), tetapi Anda dapat memilih nama lain dengan `hub_model_id = "nama_lain"`.
+
+Untuk mengunggah model ke organisasi tempat Anda menjadi anggota, cukup gunakan `hub_model_id = "organisasi_saya/nama_repo"`.
+
+{/if}
+
+Pada level yang lebih rendah, Anda dapat mengakses Model Hub langsung melalui objek model, tokenizer, dan konfigurasi menggunakan metode `push_to_hub()`. Metode ini menangani pembuatan repositori sekaligus mengunggah file model dan tokenizer langsung ke repositori — tanpa perlu konfigurasi manual.
+
+Untuk memahaminya, mari kita inisialisasi model dan tokenizer terlebih dahulu:
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{/if}
+
+Anda bebas melakukan apa saja terhadap ini — menambahkan token pada tokenizer, melatih model, menyempurnakannya. Setelah puas dengan hasil akhir model, bobot, dan tokenizer-nya, Anda dapat langsung menggunakan metode `push_to_hub()` pada objek `model`:
+
+```py
+model.push_to_hub("dummy-model")
+```
+
+Ini akan membuat repositori baru bernama `dummy-model` di profil Anda dan mengisinya dengan file model Anda.
+Lakukan hal yang sama untuk tokenizer agar semua file tersedia dalam repositori tersebut:
+
+```py
+tokenizer.push_to_hub("dummy-model")
+```
+
+Jika Anda termasuk dalam suatu organisasi, cukup tentukan argumen `organization` untuk mengunggah ke namespace organisasi tersebut:
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface")
+```
+
+Jika Anda ingin menggunakan token khusus dari Hugging Face, Anda juga dapat menentukannya pada metode `push_to_hub()`:
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
+```
+
+Sekarang buka Model Hub untuk menemukan model yang baru saja Anda unggah: *https://huggingface.co/user-or-organization/dummy-model*
+
+Klik tab "Files and versions", dan Anda seharusnya melihat file seperti pada tangkapan layar berikut:
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+
+✏️ **Coba sendiri!** Ambil model dan tokenizer dari checkpoint `bert-base-cased` dan unggah ke repositori dalam namespace Anda menggunakan metode `push_to_hub()`. Periksa apakah repositori muncul dengan benar di halaman Anda sebelum menghapusnya.
+
+
+
+Seperti yang telah Anda lihat, metode `push_to_hub()` menerima berbagai argumen, yang memungkinkan pengunggahan ke repositori tertentu, namespace organisasi, atau menggunakan token API berbeda. Kami sarankan untuk memeriksa spesifikasi metode ini langsung dari dokumentasi [🤗 Transformers](https://huggingface.co/transformers/model_sharing) untuk mengetahui apa saja yang bisa dilakukan.
+
+Metode `push_to_hub()` dibangun di atas pustaka Python [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), yang menyediakan API langsung ke Hugging Face Hub. Ini telah terintegrasi dalam 🤗 Transformers dan berbagai pustaka pembelajaran mesin lainnya seperti [`allenlp`](https://github.com/allenai/allennlp). Meskipun fokus kita dalam bab ini adalah integrasi dengan 🤗 Transformers, mengintegrasikannya ke dalam kode atau pustaka Anda sendiri juga sangat mudah.
+
+Lanjutkan ke bagian terakhir untuk melihat cara mengunggah file ke repositori yang telah Anda buat!
+
+## Menggunakan Pustaka Python `huggingface_hub`[[using-the-huggingfacehub-python-library]]
+
+Pustaka Python `huggingface_hub` adalah paket yang menyediakan serangkaian alat untuk mengelola model dan dataset di Hub. Ia menawarkan metode dan kelas sederhana untuk tugas umum seperti mendapatkan informasi tentang repositori dan mengelolanya. API yang disediakan bekerja di atas git untuk mempermudah manajemen konten dan integrasi Hub dalam proyek dan pustaka Anda.
+
+Seperti halnya saat menggunakan API `push_to_hub`, Anda harus memiliki token API yang tersimpan di cache. Untuk melakukan ini, gunakan perintah `login` dari CLI seperti dijelaskan pada bagian sebelumnya (jangan lupa tambahkan karakter `!` jika menjalankan dari Google Colab):
+
+```bash
+huggingface-cli login
+```
+
+Paket `huggingface_hub` menawarkan beberapa metode dan kelas yang berguna untuk tujuan kita. Pertama-tama, ada beberapa metode untuk mengelola pembuatan dan penghapusan repositori, serta hal lainnya:
+
+```python no-format
+from huggingface_hub import (
+ # Manajemen pengguna
+ login,
+ logout,
+ whoami,
+
+ # Pembuatan dan manajemen repositori
+ create_repo,
+ delete_repo,
+ update_repo_visibility,
+
+ # Beberapa metode untuk mengambil/mengubah informasi tentang isi repositori
+ list_models,
+ list_datasets,
+ list_metrics,
+ list_repo_files,
+ upload_file,
+ delete_file,
+)
+```
+
+Selain itu, paket ini juga menyediakan kelas `Repository` yang sangat kuat untuk mengelola repositori lokal. Kita akan menjelajahi metode-metode ini dan kelas tersebut di bagian-bagian berikut untuk memahami cara memanfaatkannya.
+
+Metode `create_repo` dapat digunakan untuk membuat repositori baru di Hub:
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model")
+```
+
+Ini akan membuat repositori `dummy-model` dalam namespace Anda. Jika Anda ingin, Anda dapat menentukan organisasi tempat repositori ini berada dengan argumen `organization`:
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model", organization="huggingface")
+```
+
+Ini akan membuat repositori `dummy-model` dalam namespace `huggingface`, dengan asumsi Anda merupakan anggota organisasi tersebut.
+Argumen lain yang mungkin berguna adalah:
+
+- `private`, untuk menentukan apakah repositori tersebut dapat diakses publik atau tidak.
+- `token`, jika Anda ingin menggunakan token tertentu dan bukan token yang tersimpan di cache.
+- `repo_type`, jika Anda ingin membuat `dataset` atau `space` alih-alih model. Nilai yang diterima: `"dataset"` dan `"space"`.
+
+Setelah repositori dibuat, kita bisa menambahkan file ke dalamnya! Lanjut ke bagian berikut untuk melihat tiga cara yang dapat digunakan.
+
+## Menggunakan Antarmuka Web[[using-the-web-interface]]
+
+Antarmuka web menyediakan alat untuk mengelola repositori langsung di Hub. Menggunakan antarmuka ini, Anda dapat dengan mudah membuat repositori, menambahkan file (bahkan file besar!), menjelajahi model, melihat perbedaan file, dan banyak lagi.
+
+Untuk membuat repositori baru, kunjungi [huggingface.co/new](https://huggingface.co/new):
+
+
+
+
+
+Pertama, tentukan pemilik repositori: ini bisa Anda sendiri atau salah satu organisasi tempat Anda tergabung. Jika Anda memilih organisasi, model tersebut akan tampil di halaman organisasi dan setiap anggota organisasi dapat berkontribusi ke repositori tersebut.
+
+Selanjutnya, masukkan nama model Anda. Ini juga akan menjadi nama repositorinya. Terakhir, Anda bisa menentukan apakah model akan bersifat publik atau privat. Model privat tidak terlihat oleh publik.
+
+Setelah repositori model Anda dibuat, Anda akan melihat halaman seperti ini:
+
+
+
+
+
+Inilah tempat model Anda akan di-host. Untuk mulai mengisinya, Anda bisa menambahkan file README langsung dari antarmuka web.
+
+
+
+
+
+File README menggunakan format Markdown — silakan berkreasi sesuka hati! Bagian ketiga dari bab ini akan membahas cara membuat model card. Ini sangat penting untuk memberikan konteks dan nilai terhadap model Anda, karena di sinilah Anda menjelaskan kepada orang lain tentang kegunaan model Anda.
+
+Jika Anda melihat tab "Files and versions", Anda akan melihat bahwa file yang ada belum banyak — hanya *README.md* yang baru Anda buat dan file *.gitattributes* yang mencatat file besar.
+
+
+
+
+
+Selanjutnya kita akan melihat bagaimana menambahkan file baru.
+
+## Mengunggah File Model[[uploading-the-model-files]]
+
+Sistem untuk mengelola file di Hugging Face Hub berbasis git untuk file reguler, dan git-lfs (Git Large File Storage — lihat [situs resmi](https://git-lfs.github.com/)) untuk file yang lebih besar.
+
+Di bagian berikut, kita akan membahas tiga cara berbeda untuk mengunggah file ke Hub: menggunakan `huggingface_hub` dan perintah git.
+
+### Pendekatan `upload_file`[[the-uploadfile-approach]]
+
+Menggunakan `upload_file` tidak membutuhkan instalasi git atau git-lfs di sistem Anda. File dikirim langsung ke 🤗 Hub melalui permintaan HTTP POST. Namun, metode ini tidak dapat menangani file yang lebih besar dari 5GB.
+
+Jika file Anda lebih besar dari 5GB, gunakan dua metode lainnya yang dijelaskan di bawah.
+
+Contoh penggunaannya:
+
+```py
+from huggingface_hub import upload_file
+
+upload_file(
+ "/config.json",
+ path_in_repo="config.json",
+ repo_id="/dummy-model",
+)
+```
+
+Ini akan mengunggah file `config.json` dari `` ke repositori `dummy-model`, disimpan di root dengan nama `config.json`.
+
+Argumen tambahan yang berguna:
+
+- `token`, untuk mengganti token di cache dengan token tertentu.
+- `repo_type`, jika Anda ingin mengunggah ke `dataset` atau `space` alih-alih model. Nilai yang diterima: `"dataset"` dan `"space"`.
+
+### Kelas `Repository`[[the-repository-class]]
+
+Kelas `Repository` digunakan untuk mengelola repositori lokal dengan cara mirip git. Kelas ini menyederhanakan banyak kerumitan penggunaan git dan memberikan semua fitur yang kita butuhkan.
+
+Penggunaan kelas ini memerlukan instalasi `git` dan `git-lfs`. Pastikan Anda telah menginstalnya terlebih dahulu (lihat [di sini](https://git-lfs.github.com/) untuk petunjuk instalasi).
+
+Untuk mulai menggunakan repositori yang baru saja kita buat, kita mulai dengan menginisialisasi folder lokal dengan mengkloning repositori:
+
+```py
+from huggingface_hub import Repository
+
+repo = Repository("", clone_from="/dummy-model")
+```
+
+Ini akan membuat folder `` di direktori kerja kita. Folder tersebut hanya berisi file `.gitattributes`, karena hanya itu yang dibuat saat repositori diinisialisasi.
+
+Mulai dari sini, kita dapat menggunakan beberapa metode git tradisional:
+
+```py
+repo.git_pull()
+repo.git_add()
+repo.git_commit()
+repo.git_push()
+repo.git_tag()
+```
+
+Dan lainnya! Kami menyarankan Anda untuk melihat dokumentasi `Repository` [di sini](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) untuk gambaran semua metode yang tersedia.
+
+Saat ini, kita memiliki model dan tokenizer yang ingin kita unggah ke hub. Karena repositori telah berhasil dikloning, kita dapat menyimpan file ke dalam folder tersebut.
+
+Pertama, pastikan salinan lokal kita sudah yang terbaru:
+
+```py
+repo.git_pull()
+```
+
+Setelah itu, simpan file model dan tokenizer:
+
+```py
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+
+Folder `` kini berisi semua file model dan tokenizer. Kita lanjutkan dengan alur kerja git biasa: menambahkan file, melakukan commit, dan mendorong ke hub:
+
+```py
+repo.git_add()
+repo.git_commit("Add model and tokenizer files")
+repo.git_push()
+```
+
+Selamat! Anda baru saja mengunggah file pertama Anda ke hub.
+
+### Pendekatan berbasis git[[the-git-based-approach]]
+
+Ini adalah pendekatan paling dasar untuk mengunggah file: menggunakan git dan git-lfs secara langsung. Meskipun pendekatan sebelumnya menyederhanakan prosesnya, masih ada beberapa hal penting yang perlu diperhatikan jika menggunakan metode ini.
+
+Gunakan metode ini hanya jika Anda telah menginstal `git` dan `git-lfs`. Pastikan `git-lfs` telah terinstal dan dikonfigurasi dengan benar (lihat [di sini](https://git-lfs.github.com/) untuk petunjuk).
+
+Mulailah dengan menginisialisasi git-lfs:
+
+```bash
+git lfs install
+```
+
+```bash
+Updated git hooks.
+Git LFS initialized.
+```
+
+Setelah selesai, langkah pertama adalah mengkloning repositori model Anda:
+
+```bash
+git clone https://huggingface.co//
+```
+
+Contoh: nama pengguna saya adalah `lysandre` dan nama model saya adalah `dummy`, maka perintahnya:
+
+```
+git clone https://huggingface.co/lysandre/dummy
+```
+
+Sekarang saya memiliki folder bernama *dummy* di direktori kerja. Saya bisa masuk ke folder itu dan melihat isinya:
+
+```bash
+cd dummy && ls
+```
+
+```bash
+README.md
+```
+
+Jika Anda baru saja membuat repositori menggunakan metode `create_repo` dari Hugging Face Hub, folder ini seharusnya hanya berisi file tersembunyi `.gitattributes`. Jika Anda mengikuti instruksi pada bagian sebelumnya untuk membuat repositori melalui antarmuka web, folder ini seharusnya berisi satu file *README.md* bersama file tersembunyi `.gitattributes`, seperti yang ditunjukkan di sini.
+
+Menambahkan file berukuran kecil, seperti file konfigurasi, file kosakata, atau file apa pun yang ukurannya hanya beberapa megabyte, dapat dilakukan dengan cara biasa dalam sistem berbasis Git. Namun, file yang lebih besar harus didaftarkan menggunakan git-lfs agar dapat dikirim ke *huggingface.co*.
+
+Mari kembali ke Python sebentar untuk menghasilkan model dan tokenizer yang ingin kita commit ke repositori dummy kita:
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Lakukan apa pun dengan modelnya, latih, fine-tune, dll...
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Lakukan apa pun dengan modelnya, latih, fine-tune, dll...
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{/if}
+
+Sekarang kita telah menyimpan artefak model dan tokenizer, mari kita lihat kembali isi folder *dummy*:
+
+```bash
+ls
+```
+
+{#if fw === 'pt'}
+```bash
+config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
+```
+
+Jika Anda melihat ukuran file (misalnya dengan `ls -lh`), Anda akan melihat bahwa file *pytorch_model.bin* adalah pengecualian, karena ukurannya lebih dari 400 MB.
+
+{:else}
+```bash
+config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
+```
+
+Jika Anda melihat ukuran file (misalnya dengan `ls -lh`), Anda akan melihat bahwa file *tf_model.h5* adalah pengecualian, karena ukurannya lebih dari 400 MB.
+
+{/if}
+
+
+✏️ Saat membuat repositori dari antarmuka web, file *.gitattributes* secara otomatis dikonfigurasi untuk mengenali file dengan ekstensi tertentu, seperti *.bin* dan *.h5*, sebagai file besar. Dengan begitu, git-lfs akan langsung melacak file tersebut tanpa perlu pengaturan tambahan.
+
+
+Sekarang kita bisa lanjut dan bekerja seperti biasa pada repositori Git. Kita bisa menambahkan semua file ke staging area Git menggunakan perintah `git add`:
+
+```bash
+git add .
+```
+
+Kita kemudian bisa melihat file yang sudah distaging:
+
+```bash
+git status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: pytorch_model.bin
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{:else}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tf_model.h5
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{/if}
+
+Kita juga bisa memastikan bahwa git-lfs melacak file yang benar dengan perintah `git lfs status`:
+
+```bash
+git lfs status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ pytorch_model.bin (LFS: 35686c2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+```
+
+Kita dapat melihat bahwa semua file menggunakan handler `Git`, kecuali *pytorch_model.bin* dan *sentencepiece.bpe.model*, yang menggunakan `LFS`. Bagus!
+
+{:else}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tf_model.h5 (LFS: 86fce29)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+```
+
+Kita dapat melihat bahwa semua file menggunakan handler `Git`, kecuali *tf_model.h5*, yang menggunakan `LFS`. Bagus!
+
+{/if}
+
+Mari lanjutkan ke langkah terakhir: commit dan push ke repositori *huggingface.co*:
+
+```bash
+git commit -m "First model version"
+```
+
+{#if fw === 'pt'}
+```bash
+[main b08aab1] First model version
+ 7 files changed, 29027 insertions(+)
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 pytorch_model.bin
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{:else}
+```bash
+[main b08aab1] First model version
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tf_model.h5
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{/if}
+
+Proses push mungkin memakan waktu, tergantung pada kecepatan koneksi internet dan ukuran file Anda:
+
+```bash
+git push
+```
+
+```bash
+Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
+Enumerating objects: 11, done.
+Counting objects: 100% (11/11), done.
+Delta compression using up to 12 threads
+Compressing objects: 100% (9/9), done.
+Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
+Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
+To https://huggingface.co/lysandre/dummy
+ 891b41d..b08aab1 main -> main
+```
+
+{#if fw === 'pt'}
+Jika kita melihat repositori model setelah proses ini selesai, kita dapat melihat semua file yang baru saja ditambahkan:
+
+
+
+
+
+Antarmuka pengguna memungkinkan Anda menjelajahi file model, commit, dan melihat perbedaan (diff) yang diperkenalkan oleh setiap commit:
+
+
+
+
+{:else}
+Jika kita melihat repositori model setelah proses ini selesai, kita dapat melihat semua file yang baru saja ditambahkan:
+
+
+
+
+
+Antarmuka pengguna memungkinkan Anda menjelajahi file model, commit, dan melihat perbedaan (diff) yang diperkenalkan oleh setiap commit:
+
+
+
+
+{/if}
diff --git a/chapters/id/chapter4/4.mdx b/chapters/id/chapter4/4.mdx
new file mode 100644
index 000000000..d326fe4fd
--- /dev/null
+++ b/chapters/id/chapter4/4.mdx
@@ -0,0 +1,87 @@
+# Membangun Model Card[[building-a-model-card]]
+
+
+
+Model card adalah sebuah berkas yang bisa dibilang sama pentingnya dengan berkas model dan tokenizer dalam repositori model. Ini adalah definisi utama dari model, yang menjamin model dapat digunakan kembali oleh anggota komunitas lain dan hasilnya dapat direproduksi, serta menyediakan platform bagi anggota lain untuk membangun artefak mereka di atasnya.
+
+Mendokumentasikan proses pelatihan dan evaluasi membantu orang lain memahami apa yang bisa diharapkan dari suatu model — dan memberikan informasi yang cukup tentang data yang digunakan serta pra-pemrosesan dan pasca-pemrosesan yang dilakukan memastikan bahwa keterbatasan, bias, dan konteks di mana model ini berguna atau tidak bisa diidentifikasi dan dipahami.
+
+Oleh karena itu, membuat model card yang dengan jelas mendefinisikan model Anda adalah langkah yang sangat penting. Di sini, kami menyediakan beberapa tips yang akan membantu Anda. Pembuatan model card dilakukan melalui berkas *README.md* yang sudah Anda lihat sebelumnya, yang merupakan berkas Markdown.
+
+Konsep "model card" berasal dari penelitian Google, pertama kali diperkenalkan dalam makalah ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) oleh Margaret Mitchell dkk. Banyak informasi di sini yang berdasarkan makalah tersebut, dan kami menyarankan Anda membacanya untuk memahami mengapa model card sangat penting dalam dunia yang menghargai reproduktibilitas, penggunaan ulang, dan keadilan.
+
+Model card biasanya dimulai dengan ringkasan singkat tingkat tinggi tentang tujuan model, diikuti dengan detail tambahan pada bagian-bagian berikut:
+
+- Deskripsi model
+- Penggunaan yang dimaksudkan & keterbatasan
+- Cara menggunakan
+- Keterbatasan dan bias
+- Data pelatihan
+- Prosedur pelatihan
+- Hasil evaluasi
+
+Mari kita lihat apa saja yang sebaiknya dimasukkan dalam masing-masing bagian ini.
+
+### Deskripsi Model[[model-description]]
+
+Bagian ini memberikan detail dasar tentang model. Ini mencakup arsitektur, versi, apakah model ini diperkenalkan dalam makalah, apakah tersedia implementasi asli, penulisnya, dan informasi umum lainnya. Setiap hak cipta harus dicantumkan di sini. Informasi umum tentang prosedur pelatihan, parameter, dan peringatan penting juga bisa disebutkan di bagian ini.
+
+### Penggunaan yang Dimaksudkan & Keterbatasan[[intended-uses-limitations]]
+
+Di sini Anda menjelaskan kasus penggunaan yang dimaksudkan untuk model ini, termasuk bahasa, bidang, dan domain tempat model ini bisa diterapkan. Bagian ini juga bisa mendokumentasikan area yang diketahui tidak cocok untuk model ini atau area di mana model kemungkinan besar tidak akan bekerja optimal.
+
+### Cara Menggunakan[[how-to-use]]
+
+Bagian ini harus menyertakan beberapa contoh cara menggunakan model. Ini bisa berupa penggunaan fungsi `pipeline()`, penggunaan kelas model dan tokenizer, atau cuplikan kode lain yang menurut Anda berguna.
+
+### Data Pelatihan[[training-data]]
+
+Bagian ini harus menunjukkan dataset apa saja yang digunakan untuk melatih model. Deskripsi singkat mengenai dataset tersebut juga sangat membantu.
+
+### Prosedur Pelatihan[[training-procedure]]
+
+Di bagian ini Anda perlu mendeskripsikan semua aspek relevan dari pelatihan yang berguna untuk tujuan reproduktibilitas. Ini mencakup setiap pra-pemrosesan dan pasca-pemrosesan yang dilakukan terhadap data, serta detail seperti jumlah epoch, ukuran batch, learning rate, dan sebagainya.
+
+### Variabel dan Metrik[[variable-and-metrics]]
+
+Di sini Anda perlu menjelaskan metrik yang digunakan untuk evaluasi, dan faktor-faktor apa saja yang diukur. Menyebutkan metrik apa yang digunakan, pada dataset mana dan pada split dataset mana, memudahkan orang lain membandingkan performa model Anda dengan model lain. Informasi ini harus konsisten dengan bagian sebelumnya seperti pengguna dan tujuan yang dimaksudkan.
+
+### Hasil Evaluasi[[evaluation-results]]
+
+Terakhir, berikan informasi tentang seberapa baik performa model pada dataset evaluasi. Jika model menggunakan threshold keputusan, sebutkan nilai threshold yang digunakan saat evaluasi, atau berikan detail evaluasi pada berbagai threshold sesuai penggunaan yang dimaksudkan.
+
+## Contoh[[example]]
+
+Lihat beberapa contoh model card yang dibuat dengan baik berikut ini:
+
+- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
+- [`gpt2`](https://huggingface.co/gpt2)
+- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
+
+Contoh lain dari berbagai organisasi dan perusahaan tersedia [di sini](https://github.com/huggingface/model_card/blob/master/examples.md).
+
+## Catatan[[note]]
+
+Model card bukanlah persyaratan wajib saat mempublikasikan model, dan Anda tidak perlu menyertakan semua bagian yang disebutkan di atas saat membuatnya. Namun, dokumentasi eksplisit dari model hanya akan memberikan manfaat bagi pengguna di masa depan, jadi kami menyarankan Anda mengisi sebanyak mungkin bagian sesuai pengetahuan dan kemampuan Anda.
+
+## Metadata Model Card[[model-card-metadata]]
+
+Jika Anda telah menjelajahi Hugging Face Hub, Anda mungkin telah melihat bahwa beberapa model masuk ke dalam kategori tertentu: Anda bisa memfilternya berdasarkan tugas, bahasa, pustaka, dan lainnya. Kategori ini ditentukan berdasarkan metadata yang Anda tambahkan di header model card.
+
+Sebagai contoh, jika Anda melihat [model card `camembert-base`](https://huggingface.co/camembert-base/blob/main/README.md), Anda akan melihat baris berikut di bagian header model card:
+
+```
+---
+language: fr
+license: mit
+datasets:
+- oscar
+---
+```
+
+Metadata ini diurai oleh Hugging Face Hub, yang kemudian mengidentifikasi model ini sebagai model berbahasa Prancis, berlisensi MIT, dan dilatih pada dataset Oscar.
+
+[Spesifikasi lengkap model card](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) memungkinkan Anda menentukan bahasa, lisensi, tag, dataset, metrik, serta hasil evaluasi yang diperoleh model saat pelatihan.
diff --git a/chapters/id/chapter4/5.mdx b/chapters/id/chapter4/5.mdx
new file mode 100644
index 000000000..06948d1cb
--- /dev/null
+++ b/chapters/id/chapter4/5.mdx
@@ -0,0 +1,13 @@
+# Bagian 1 Selesai![[part-1-completed]]
+
+
+
+Ini adalah akhir dari bagian pertama dari kursus ini! Bagian 2 akan dirilis pada tanggal 15 November dengan acara komunitas besar. Lihat informasi selengkapnya [di sini](https://huggingface.co/blog/course-launch-event).
+
+Sekarang Anda seharusnya sudah bisa melakukan fine-tuning pada model yang telah dilatih sebelumnya untuk tugas klasifikasi teks (baik untuk satu kalimat maupun pasangan kalimat), lalu mengunggah hasilnya ke Model Hub. Untuk memastikan bahwa Anda benar-benar menguasai bagian pertama ini, Anda sebaiknya benar-benar mencobanya pada masalah yang menarik bagi Anda (dan tidak harus dalam bahasa Inggris jika Anda menguasai bahasa lain)! Anda dapat mencari bantuan di [forum Hugging Face](https://discuss.huggingface.co/) dan membagikan proyek Anda di [topik ini](https://discuss.huggingface.co/t/share-your-projects/6803) setelah selesai.
+
+Kami sangat menantikan untuk melihat apa yang akan Anda bangun dengan ini!
+
diff --git a/chapters/id/chapter4/6.mdx b/chapters/id/chapter4/6.mdx
new file mode 100644
index 000000000..5d69969f4
--- /dev/null
+++ b/chapters/id/chapter4/6.mdx
@@ -0,0 +1,228 @@
+
+
+
+
+# Kuis Akhir Bab[[end-of-chapter-quiz]]
+
+
+
+Yuk, uji apa yang telah Anda pelajari di bab ini!
+
+### 1. Apa batasan model-model yang ada di Hub?
+
+
+
+### 2. Bagaimana cara Anda mengelola model di Hub?
+
+git-lfs untuk file besar.",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Apa saja yang bisa Anda lakukan melalui antarmuka web Hugging Face Hub?
+
+
+
+### 4. Apa itu model card?
+
+
+
+### 5. Objek mana dari pustaka 🤗 Transformers yang dapat dibagikan langsung ke Hub dengan `push_to_hub()`?
+
+{#if fw === 'pt'}
+push_to_hub, dan ini akan mengunggah semua file tokenizer (vocabulary, arsitektur, dll.) ke repositori. Tapi bukan hanya itu saja!",
+ correct: true
+ },
+ {
+ text: "Konfigurasi model",
+ explain: "Betul! Semua konfigurasi model bisa diunggah dengan push_to_hub. Apa lagi yang bisa Anda unggah?",
+ correct: true
+ },
+ {
+ text: "Model",
+ explain: "Benar! Semua model bisa diunggah bersama file konfigurasi mereka. Tapi ini juga bukan satu-satunya.",
+ correct: true
+ },
+ {
+ text: "Trainer",
+ explain: "Betul — Trainer juga memiliki metode push_to_hub, yang akan mengunggah model, konfigurasi, tokenizer, dan draft model card.",
+ correct: true
+ }
+ ]}
+/>
+{:else}
+push_to_hub, dan ini akan mengunggah semua file tokenizer (vocabulary, arsitektur, dll.) ke repositori. Tapi bukan hanya itu saja!",
+ correct: true
+ },
+ {
+ text: "Konfigurasi model",
+ explain: "Betul! Semua konfigurasi model bisa diunggah dengan push_to_hub. Apa lagi yang bisa Anda unggah?",
+ correct: true
+ },
+ {
+ text: "Model",
+ explain: "Benar! Semua model bisa diunggah bersama file konfigurasi mereka. Tapi ini juga bukan satu-satunya.",
+ correct: true
+ },
+ {
+ text: "Semua di atas menggunakan callback khusus",
+ explain: "Benar — PushToHubCallback akan secara berkala mengunggah semuanya ke repositori selama pelatihan.",
+ correct: true
+ }
+ ]}
+/>
+{/if}
+
+### 6. Apa langkah pertama saat menggunakan metode `push_to_hub()` atau alat CLI?
+
+
+
+### 7. Anda sedang menggunakan model dan tokenizer — bagaimana cara mengunggahnya ke Hub?
+
+huggingface_hub.",
+ explain: "Model dan tokenizer sudah memiliki utilitas huggingface_hub — tidak perlu dibungkus lagi!"
+ },
+ {
+ text: "Dengan menyimpannya ke disk dan menjalankan transformers-cli upload-model",
+ explain: "Perintah upload-model tidak ada."
+ }
+ ]}
+/>
+
+### 8. Operasi git apa saja yang bisa Anda lakukan dengan kelas `Repository`?
+
+git_commit() digunakan untuk itu.",
+ correct: true
+ },
+ {
+ text: "Pull",
+ explain: "Itu fungsi dari metode git_pull().",
+ correct: true
+ },
+ {
+ text: "Push",
+ explain: "Metode git_push() digunakan untuk itu.",
+ correct: true
+ },
+ {
+ text: "Merge",
+ explain: "Tidak, operasi ini tidak akan tersedia melalui API ini."
+ }
+ ]}
+/>
diff --git a/chapters/id/chapter5/1.mdx b/chapters/id/chapter5/1.mdx
new file mode 100644
index 000000000..6708224a5
--- /dev/null
+++ b/chapters/id/chapter5/1.mdx
@@ -0,0 +1,22 @@
+# Pendahuluan[[introduction]]
+
+
+
+Di [Bab 3](/course/chapter3) Anda telah mencicipi untuk pertama kalinya pustaka 🤗 Datasets dan melihat bahwa ada tiga langkah utama dalam menyempurnakan model:
+
+1. Memuat dataset dari Hugging Face Hub.
+2. Melakukan praproses data dengan `Dataset.map()`.
+3. Memuat dan menghitung metrik.
+
+Namun itu baru permukaan dari apa yang bisa dilakukan oleh 🤗 Datasets! Dalam bab ini, kita akan menyelami pustaka tersebut lebih dalam. Sepanjang perjalanan, kita akan menemukan jawaban atas pertanyaan-pertanyaan berikut:
+
+* Apa yang harus dilakukan jika dataset Anda tidak ada di Hub?
+* Bagaimana cara memotong dan membagi dataset? (Dan bagaimana jika Anda _benar-benar_ harus menggunakan Pandas?)
+* Apa yang harus dilakukan jika dataset Anda sangat besar dan bisa membuat RAM laptop Anda kepanasan?
+* Apa itu sebenarnya "memory mapping" dan Apache Arrow?
+* Bagaimana cara membuat dataset Anda sendiri dan mengunggahnya ke Hub?
+
+Teknik-teknik yang Anda pelajari di sini akan mempersiapkan Anda untuk tugas tokenisasi lanjutan dan penyempurnaan model di [Bab 6](/course/chapter6) dan [Bab 7](/course/chapter7) — jadi ambil kopi Anda dan mari kita mulai!
diff --git a/chapters/id/chapter5/2.mdx b/chapters/id/chapter5/2.mdx
new file mode 100644
index 000000000..f009e1d7b
--- /dev/null
+++ b/chapters/id/chapter5/2.mdx
@@ -0,0 +1,165 @@
+# Bagaimana jika dataset saya tidak ada di Hub?[[what-if-my-dataset-isnt-on-the-hub]]
+
+
+
+Kamu sudah tahu cara menggunakan [Hugging Face Hub](https://huggingface.co/datasets) untuk mengunduh dataset, tetapi sering kali kamu akan bekerja dengan data yang disimpan di laptop kamu atau di server jarak jauh. Dalam bagian ini, kami akan menunjukkan bagaimana 🤗 Datasets dapat digunakan untuk memuat dataset yang tidak tersedia di Hugging Face Hub.
+
+
+
+## Bekerja dengan dataset lokal dan jarak jauh[[working-with-local-and-remote-datasets]]
+
+🤗 Datasets menyediakan skrip pemuatan untuk menangani pemuatan dataset lokal dan jarak jauh. Ini mendukung beberapa format data umum, seperti:
+
+| Format Data | Skrip Pemuatan | Contoh |
+| :----------------: | :------------: | :----------------------------------------------------: |
+| CSV & TSV | `csv` | `load_dataset("csv", data_files="my_file.csv")` |
+| File Teks | `text` | `load_dataset("text", data_files="my_file.txt")` |
+| JSON & JSON Lines | `json` | `load_dataset("json", data_files="my_file.jsonl")` |
+| Pickled DataFrames | `pandas` | `load_dataset("pandas", data_files="my_dataframe.pkl")` |
+
+Seperti yang ditunjukkan dalam tabel, untuk setiap format data kita hanya perlu menentukan jenis skrip pemuatan di fungsi `load_dataset()`, bersama dengan argumen `data_files` yang menentukan jalur ke satu atau lebih file. Mari kita mulai dengan memuat dataset dari file lokal; nanti kita akan melihat cara melakukan hal yang sama dengan file jarak jauh.
+
+## Memuat dataset lokal[[loading-a-local-dataset]]
+
+Untuk contoh ini, kita akan menggunakan [dataset SQuAD-it](https://github.com/crux82/squad-it/), yang merupakan dataset skala besar untuk tugas tanya-jawab dalam bahasa Italia.
+
+Bagian pelatihan dan pengujian di-host di GitHub, jadi kita bisa mengunduhnya dengan perintah `wget` sederhana:
+
+```python
+!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
+!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
+```
+
+Ini akan mengunduh dua file terkompresi bernama *SQuAD_it-train.json.gz* dan *SQuAD_it-test.json.gz*, yang bisa kita dekompresi dengan perintah `gzip` di Linux:
+
+```python
+!gzip -dkv SQuAD_it-*.json.gz
+```
+
+```bash
+SQuAD_it-test.json.gz: 87.4% -- diganti dengan SQuAD_it-test.json
+SQuAD_it-train.json.gz: 82.2% -- diganti dengan SQuAD_it-train.json
+```
+
+Kita dapat melihat bahwa file terkompresi telah diganti dengan _SQuAD_it-train.json_ dan _SQuAD_it-test.json_, dan datanya disimpan dalam format JSON.
+
+
+
+✎ Jika kamu bertanya-tanya kenapa ada karakter `!` di perintah shell di atas, itu karena kita menjalankannya di dalam Jupyter notebook. Cukup hapus awalan tersebut jika kamu ingin mengunduh dan mengekstrak dataset dari terminal biasa.
+
+
+
+Untuk memuat file JSON dengan fungsi `load_dataset()`, kita hanya perlu tahu apakah kita berurusan dengan JSON biasa (mirip dengan kamus bertingkat) atau JSON Lines (baris per baris JSON). Seperti banyak dataset tanya-jawab, SQuAD-it menggunakan format bertingkat, dengan semua teks disimpan di field `data`. Ini berarti kita dapat memuat dataset dengan menentukan argumen `field` seperti berikut:
+
+```py
+from datasets import load_dataset
+
+squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
+```
+
+Secara default, memuat file lokal akan menghasilkan objek `DatasetDict` dengan split `train`. Kita dapat melihat ini dengan memeriksa objek `squad_it_dataset`:
+
+```py
+squad_it_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 442
+ })
+})
+```
+
+Ini menunjukkan jumlah baris dan nama kolom yang terkait dengan set pelatihan. Kita bisa melihat salah satu contoh dengan mengakses indeks dari split `train` seperti ini:
+
+```py
+squad_it_dataset["train"][0]
+```
+
+```python out
+{
+ "title": "Terremoto del Sichuan del 2008",
+ "paragraphs": [
+ {
+ "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
+ "qas": [
+ {
+ "answers": [{"answer_start": 29, "text": "2008"}],
+ "id": "56cdca7862d2951400fa6826",
+ "question": "In quale anno si è verificato il terremoto nel Sichuan?",
+ },
+ ...
+ ],
+ },
+ ...
+ ],
+}
+```
+
+Mantap, kita telah berhasil memuat dataset lokal pertama kita! Tapi walaupun ini bekerja untuk set pelatihan, yang sebenarnya kita inginkan adalah memasukkan split `train` dan `test` dalam satu objek `DatasetDict` sehingga kita bisa menerapkan fungsi `Dataset.map()` ke keduanya sekaligus. Untuk melakukan ini, kita bisa memberikan dictionary ke argumen `data_files` yang memetakan nama setiap split ke file yang sesuai:
+
+```py
+data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+squad_it_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 442
+ })
+ test: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 48
+ })
+})
+```
+
+Ini adalah hasil yang kita inginkan. Sekarang, kita bisa menerapkan berbagai teknik praproses untuk membersihkan data, melakukan tokenisasi, dan sebagainya.
+
+
+
+Argumen `data_files` dari fungsi `load_dataset()` cukup fleksibel dan bisa berupa path file tunggal, daftar path file, atau dictionary yang memetakan nama split ke path file. Kamu juga bisa menggunakan glob untuk mencocokkan pola tertentu sesuai aturan shell Unix (misalnya, kamu bisa memuat semua file JSON dalam direktori sebagai satu split dengan `data_files="*.json"`). Lihat dokumentasi 🤗 Datasets [di sini](https://huggingface.co/docs/datasets/loading#local-and-remote-files) untuk info lebih lanjut.
+
+
+
+Skrip pemuatan di 🤗 Datasets sebenarnya mendukung dekompresi otomatis dari file input, jadi kita bisa saja melewati langkah penggunaan `gzip` dan langsung mengarah ke file terkompresi di argumen `data_files`:
+
+```py
+data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+```
+
+Ini sangat berguna jika kamu tidak ingin mengekstrak file GZIP secara manual. Dekompressi otomatis ini juga berlaku untuk format umum lainnya seperti ZIP dan TAR, jadi kamu cukup arahkan `data_files` ke file terkompresi dan langsung bisa digunakan!
+
+Sekarang setelah kamu tahu cara memuat file lokal di laptop atau desktop, mari kita lihat cara memuat file dari server jarak jauh.
+
+## Memuat dataset jarak jauh[[loading-a-remote-dataset]]
+
+Jika kamu bekerja sebagai data scientist atau programmer di perusahaan, ada kemungkinan besar bahwa dataset yang ingin kamu analisis disimpan di server jarak jauh. Untungnya, memuat file jarak jauh semudah memuat file lokal! Alih-alih memberikan path ke file lokal, kita arahkan argumen `data_files` dari `load_dataset()` ke satu atau lebih URL tempat file disimpan. Sebagai contoh, untuk dataset SQuAD-it yang di-host di GitHub, kita cukup arahkan `data_files` ke URL _SQuAD_it-*.json.gz_ seperti ini:
+
+```py
+url = "https://github.com/crux82/squad-it/raw/master/"
+data_files = {
+ "train": url + "SQuAD_it-train.json.gz",
+ "test": url + "SQuAD_it-test.json.gz",
+}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+```
+
+Ini akan menghasilkan objek `DatasetDict` yang sama seperti sebelumnya, tetapi menghemat langkah pengunduhan dan dekompresi file _SQuAD_it-*.json.gz_ secara manual. Ini mengakhiri pembahasan kita tentang berbagai cara memuat dataset yang tidak di-host di Hugging Face Hub. Sekarang setelah kita punya dataset untuk dimainkan, mari kita mulai mengolah data dengan berbagai teknik data wrangling!
+
+
+
+✏️ **Coba sendiri!** Pilih dataset lain yang di-host di GitHub atau di [UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php) dan coba muat baik secara lokal maupun jarak jauh menggunakan teknik yang telah dijelaskan di atas. Untuk tantangan tambahan, coba muat dataset yang disimpan dalam format CSV atau teks (lihat [dokumentasi](https://huggingface.co/docs/datasets/loading#local-and-remote-files) untuk info lebih lanjut tentang format-format ini).
+
+
diff --git a/chapters/id/chapter5/3.mdx b/chapters/id/chapter5/3.mdx
new file mode 100644
index 000000000..443aed6e4
--- /dev/null
+++ b/chapters/id/chapter5/3.mdx
@@ -0,0 +1,740 @@
+# Saatnya memotong dan memilah data[[time-to-slice-and-dice]]
+
+
+
+Sebagian besar waktu, data yang kamu kerjakan tidak akan langsung siap untuk digunakan dalam pelatihan model. Di bagian ini kita akan menjelajahi berbagai fitur yang disediakan oleh 🤗 Datasets untuk membersihkan dataset-mu.
+
+
+
+## Memotong dan memilah data[[slicing-and-dicing-our-data]]
+
+Mirip dengan Pandas, 🤗 Datasets menyediakan beberapa fungsi untuk memanipulasi isi objek `Dataset` dan `DatasetDict`. Kita sudah pernah bertemu dengan metode `Dataset.map()` di [Bab 3](/course/chapter3), dan di bagian ini kita akan menjelajahi beberapa fungsi lainnya yang tersedia.
+
+Untuk contoh ini kita akan menggunakan [Drug Review Dataset](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) yang dihosting di [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php), yang berisi ulasan pasien tentang berbagai obat, kondisi yang diobati, dan penilaian kepuasan dari 1 sampai 10 bintang.
+
+Pertama kita perlu mengunduh dan mengekstrak data, yang bisa dilakukan dengan perintah `wget` dan `unzip`:
+
+```py
+!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
+!unzip drugsCom_raw.zip
+```
+
+Karena TSV adalah varian dari CSV yang menggunakan tab sebagai pemisah, kita dapat memuat file ini menggunakan skrip pemuatan `csv` dan menetapkan argumen `delimiter` dalam fungsi `load_dataset()` seperti berikut:
+
+```py
+from datasets import load_dataset
+
+data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
+# \t adalah karakter tab di Python
+drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
+```
+
+Praktik yang baik saat melakukan analisis data adalah mengambil sampel acak kecil untuk memahami jenis data yang sedang kamu kerjakan. Di 🤗 Datasets, kita bisa membuat sampel acak dengan menggabungkan fungsi `Dataset.shuffle()` dan `Dataset.select()`:
+
+```py
+drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
+# Lihat beberapa contoh pertama
+drug_sample[:3]
+```
+
+```python out
+{'Unnamed: 0': [87571, 178045, 80482],
+ 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
+ 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
+ 'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
+ '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
+ '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'],
+ 'rating': [9.0, 3.0, 10.0],
+ 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
+ 'usefulCount': [36, 13, 128]}
+```
+
+Perhatikan bahwa kita menetapkan seed pada `Dataset.shuffle()` untuk keperluan replikasi. `Dataset.select()` mengharapkan iterable berupa indeks, jadi kita gunakan `range(1000)` untuk mengambil 1.000 contoh pertama dari dataset yang sudah diacak. Dari sampel ini kita sudah bisa melihat beberapa hal menarik:
+
+* Kolom `Unnamed: 0` terlihat seperti ID anonim untuk tiap pasien.
+* Kolom `condition` mencampur label huruf besar dan kecil.
+* Ulasan memiliki panjang yang bervariasi dan mengandung pemisah baris Python (`\r\n`) serta karakter HTML seperti `&\#039;`.
+
+Mari kita lihat bagaimana cara menggunakan 🤗 Datasets untuk mengatasi masing-masing masalah ini. Untuk menguji hipotesis bahwa kolom `Unnamed: 0` adalah ID pasien, kita bisa menggunakan fungsi `Dataset.unique()` untuk memverifikasi apakah jumlah ID cocok dengan jumlah baris di setiap split:
+
+```py
+for split in drug_dataset.keys():
+ assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
+```
+
+Hipotesis ini tampaknya benar, jadi mari kita rapikan dataset sedikit dengan mengganti nama kolom `Unnamed: 0` menjadi sesuatu yang lebih bermakna. Kita bisa menggunakan fungsi `DatasetDict.rename_column()` untuk mengganti nama kolom di kedua split sekaligus:
+
+```py
+drug_dataset = drug_dataset.rename_column(
+ original_column_name="Unnamed: 0", new_column_name="patient_id"
+)
+drug_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
+ num_rows: 161297
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
+ num_rows: 53766
+ })
+})
+```
+
+
+
+✏️ **Coba sendiri!** Gunakan fungsi `Dataset.unique()` untuk menemukan jumlah obat dan kondisi unik di data pelatihan dan pengujian.
+
+
+
+Selanjutnya, mari kita normalisasi semua label di kolom `condition` menggunakan `Dataset.map()`. Seperti saat kita melakukan tokenisasi di [Bab 3](/course/chapter3), kita dapat mendefinisikan fungsi sederhana yang diterapkan ke semua baris dalam setiap split:
+
+```py
+def lowercase_condition(example):
+ return {"condition": example["condition"].lower()}
+
+
+drug_dataset.map(lowercase_condition)
+```
+
+```python out
+AttributeError: 'NoneType' object has no attribute 'lower'
+```
+
+Oh tidak, kita menemui masalah! Dari error tersebut kita tahu bahwa beberapa entri di kolom `condition` bernilai `None`, yang tidak bisa diubah menjadi huruf kecil. Mari kita hilangkan baris-baris ini menggunakan `Dataset.filter()`, yang bekerja mirip seperti `map()` dan mengharapkan fungsi yang menerima satu contoh dataset. Daripada menulis fungsi eksplisit seperti:
+
+```py
+def filter_nones(x):
+ return x["condition"] is not None
+```
+
+Alih-alih menjalankan `drug_dataset.filter(filter_nones)`, kita bisa melakukannya dalam satu baris menggunakan _lambda function_. Dalam Python, lambda function adalah fungsi kecil yang bisa Anda definisikan tanpa harus memberi nama eksplisit. Bentuk umumnya adalah:
+
+```
+lambda :
+```
+
+di mana `lambda` adalah salah satu [kata kunci khusus](https://docs.python.org/3/reference/lexical_analysis.html#keywords) di Python, `` adalah daftar nilai yang dipisahkan koma yang mendefinisikan input untuk fungsi, dan `` mewakili operasi yang ingin Anda jalankan. Misalnya, kita bisa mendefinisikan fungsi lambda sederhana untuk menghitung kuadrat dari suatu angka seperti ini:
+
+```
+lambda x : x * x
+```
+
+Untuk menerapkan fungsi ini ke suatu input, kita perlu membungkusnya bersama input dalam tanda kurung:
+
+```py
+(lambda x: x * x)(3)
+```
+
+```python out
+9
+```
+
+Demikian juga, kita bisa mendefinisikan lambda function dengan beberapa argumen dengan memisahkannya menggunakan koma. Contohnya, kita bisa menghitung luas segitiga sebagai berikut:
+
+```py
+(lambda base, height: 0.5 * base * height)(4, 8)
+```
+
+```python out
+16.0
+```
+
+Lambda function sangat berguna saat Anda ingin mendefinisikan fungsi kecil yang hanya digunakan sekali. (Untuk informasi lebih lanjut, kami sarankan membaca [tutorial Real Python](https://realpython.com/python-lambda/) yang sangat baik oleh Andre Burgaud). Dalam konteks 🤗 Datasets, kita bisa menggunakan lambda function untuk mendefinisikan operasi `map` dan `filter` sederhana. Jadi, mari kita gunakan trik ini untuk menghilangkan entri `None` dalam dataset kita:
+
+```py
+drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
+```
+
+Dengan entri `None` dihapus, kita bisa menormalisasi kolom `condition`:
+
+```py
+drug_dataset = drug_dataset.map(lowercase_condition)
+# Cek apakah berhasil
+drug_dataset["train"]["condition"][:3]
+```
+
+```python out
+['left ventricular dysfunction', 'adhd', 'birth control']
+```
+
+Berhasil! Sekarang setelah kita membersihkan labelnya, mari kita lanjutkan dengan membersihkan bagian ulasannya.
+
+## Membuat kolom baru[[creating-new-columns]]
+
+Setiap kali Anda bekerja dengan ulasan pelanggan, praktik yang baik adalah memeriksa jumlah kata dalam setiap ulasan. Sebuah ulasan bisa saja hanya terdiri dari satu kata seperti "Bagus!" atau berupa esai panjang dengan ribuan kata, dan tergantung pada kasus penggunaannya, Anda perlu menangani kedua ekstrem ini dengan cara yang berbeda. Untuk menghitung jumlah kata dalam setiap ulasan, kita akan menggunakan pendekatan sederhana dengan memisahkan setiap teks berdasarkan spasi.
+
+Mari kita definisikan fungsi untuk menghitung jumlah kata:
+
+```py
+def compute_review_length(example):
+ return {"review_length": len(example["review"].split())}
+```
+
+Berbeda dengan fungsi `lowercase_condition()`, fungsi `compute_review_length()` mengembalikan sebuah dictionary dengan key yang **tidak** sesuai dengan salah satu nama kolom dalam dataset. Dalam kasus ini, ketika `compute_review_length()` diberikan ke `Dataset.map()`, fungsi tersebut akan diterapkan ke semua baris dalam dataset untuk membuat kolom baru bernama `review_length`:
+
+```py
+drug_dataset = drug_dataset.map(compute_review_length)
+# Lihat contoh pertama
+drug_dataset["train"][0]
+```
+
+```python out
+{'patient_id': 206461,
+ 'drugName': 'Valsartan',
+ 'condition': 'left ventricular dysfunction',
+ 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
+ 'rating': 9.0,
+ 'date': 'May 20, 2012',
+ 'usefulCount': 27,
+ 'review_length': 17}
+```
+
+Seperti yang diharapkan, kolom `review_length` sudah ditambahkan. Kita bisa mengurutkan kolom ini dengan `Dataset.sort()` untuk melihat nilai ekstrem:
+
+```py
+drug_dataset["train"].sort("review_length")[:3]
+```
+
+```python out
+{'patient_id': [103488, 23627, 20558],
+ 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
+ 'condition': ['birth control', 'muscle spasm', 'pain'],
+ 'review': ['"Excellent."', '"useless"', '"ok"'],
+ 'rating': [10.0, 1.0, 6.0],
+ 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
+ 'usefulCount': [5, 2, 10],
+ 'review_length': [1, 1, 1]}
+```
+
+Seperti dugaan, ada ulasan yang hanya satu kata. Ini mungkin cukup untuk analisis sentimen, tapi tidak ideal untuk memprediksi kondisi.
+
+
+
+🙋 Alternatif lain untuk menambahkan kolom baru ke dalam sebuah dataset adalah dengan menggunakan fungsi `Dataset.add_column()`. Fungsi ini memungkinkan Anda memberikan kolom sebagai daftar Python atau array NumPy, dan bisa sangat berguna dalam situasi di mana `Dataset.map()` kurang cocok untuk analisis Anda.
+
+
+
+Mari kita gunakan fungsi `Dataset.filter()` untuk menghapus ulasan yang berisi kurang dari 30 kata. Mirip dengan yang kita lakukan pada kolom `condition`, kita bisa menyaring ulasan yang sangat pendek dengan mensyaratkan bahwa panjang ulasan harus melebihi ambang batas ini:
+
+```py
+drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
+print(drug_dataset.num_rows)
+```
+
+```python out
+{'train': 138514, 'test': 46108}
+```
+
+Seperti yang terlihat, ini menghapus sekitar 15% ulasan dari dataset pelatihan dan pengujian.
+
+
+
+✏️ **Coba sendiri!** Gunakan `Dataset.sort()` untuk melihat ulasan dengan jumlah kata terbanyak. Lihat [dokumentasi](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.sort) untuk mengetahui cara mengurutkan secara menurun.
+
+
+
+Hal terakhir yang perlu kita tangani adalah karakter HTML dalam ulasan. Kita bisa menggunakan modul `html` dari Python untuk mengubah kode HTML kembali ke karakter aslinya:
+
+```py
+import html
+
+text = "I'm a transformer called BERT"
+html.unescape(text)
+```
+
+```python out
+"I'm a transformer called BERT"
+```
+
+Kita akan menggunakan `Dataset.map()` untuk mengganti semua karakter HTML dalam seluruh korpus:
+
+```python
+drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
+```
+
+Seperti yang kamu lihat, metode `Dataset.map()` sangat berguna untuk memproses data — dan kita bahkan belum menyentuh semua fitur yang bisa dilakukannya!
+
+## Kekuatan super dari metode `map()`[[the-map-methods-superpowers]]
+
+Metode `Dataset.map()` memiliki argumen `batched` yang, jika disetel ke `True`, akan mengirim satu batch contoh ke fungsi map sekaligus (ukuran batch ini dapat diatur, default-nya 1000). Misalnya, fungsi `map()` sebelumnya yang digunakan untuk menghilangkan karakter HTML membutuhkan waktu agak lama untuk dijalankan. Kita bisa mempercepatnya dengan memproses beberapa elemen sekaligus menggunakan list comprehension.
+
+Saat kamu menentukan `batched=True`, fungsi akan menerima dictionary berisi field dari dataset, tapi setiap nilai sekarang berupa _list dari nilai_, bukan satu nilai saja. Nilai kembalian dari `Dataset.map()` juga harus dalam bentuk dictionary yang memiliki field yang ingin diubah atau ditambahkan, dengan daftar nilai. Berikut contoh lain untuk menghapus karakter HTML, kali ini dengan `batched=True`:
+
+```python
+new_drug_dataset = drug_dataset.map(
+ lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
+)
+```
+
+Jika Anda menjalankan kode ini di dalam sebuah notebook, Anda akan melihat bahwa perintah ini dieksekusi jauh lebih cepat dibandingkan perintah sebelumnya. Dan bukan karena ulasan kita sudah tidak lagi mengandung karakter HTML — jika Anda menjalankan kembali instruksi dari bagian sebelumnya (tanpa `batched=True`), waktunya akan tetap sama seperti sebelumnya. Hal ini terjadi karena *list comprehension* umumnya lebih cepat dibanding menjalankan kode yang sama dalam sebuah *for loop*, dan kita juga mendapatkan peningkatan performa dengan mengakses banyak elemen sekaligus dibandingkan satu per satu.
+
+Menggunakan `Dataset.map()` dengan `batched=True` sangat penting untuk memanfaatkan kecepatan tokenizer "fast" yang akan kita temui di [Bab 6](/course/chapter6), yang bisa melakukan tokenisasi cepat terhadap banyak teks. Misalnya, untuk melakukan tokenisasi semua review obat dengan tokenizer cepat, kita bisa menggunakan fungsi berikut:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+
+def tokenize_function(examples):
+ return tokenizer(examples["review"], truncation=True)
+```
+
+Seperti yang kamu lihat di [Bab 3](/course/chapter3), kita bisa memberi satu atau beberapa contoh ke tokenizer, jadi kita bisa menggunakan fungsi ini dengan atau tanpa `batched=True`. Mari manfaatkan kesempatan ini untuk membandingkan performa dari berbagai opsi. Di notebook, kamu bisa mengukur waktu eksekusi dengan menambahkan `%time` di depan baris kode:
+
+```python no-format
+%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
+```
+
+Kamu juga bisa mengukur waktu seluruh cell dengan `%%time` di awal cell. Di perangkat kami, perintah ini memakan waktu 10.8 detik (terlihat dari "Wall time").
+
+
+
+✏️ **Coba sendiri!** Jalankan perintah yang sama dengan dan tanpa `batched=True`, lalu coba juga dengan tokenizer lambat (tambah `use_fast=False` dalam `AutoTokenizer.from_pretrained()`) untuk melihat perbandingannya di perangkat kamu.
+
+
+
+Berikut hasil yang kami dapat:
+
+| Opsi | Tokenizer Cepat | Tokenizer Lambat |
+|---------------------------|-----------------|------------------|
+| `batched=True` | 10.8s | 4menit 41detik |
+| `batched=False` | 59.2s | 5menit 3detik |
+
+Artinya, menggunakan tokenizer cepat dengan `batched=True` 30x lebih cepat dari tokenizer lambat tanpa batching — luar biasa! Ini alasan utama mengapa tokenizer cepat adalah default saat menggunakan `AutoTokenizer`. Mereka mencapai kecepatan ini karena di balik layar, tokenisasi dilakukan dalam bahasa **Rust**, yang memungkinkan eksekusi paralel secara efisien.
+
+Paralelisasi juga menjadi alasan mengapa tokenizer cepat bisa 6x lebih cepat saat menggunakan batching: kamu tidak bisa paralelkan satu proses tokenisasi, tapi kamu bisa memecah ribuan teks untuk diproses oleh beberapa thread/proses.
+
+`Dataset.map()` juga memiliki kemampuan paralelisasi. Karena tidak menggunakan Rust, tokenizer lambat tetap tidak bisa menandingi tokenizer cepat, tapi ini tetap membantu (terutama jika kamu menggunakan tokenizer yang belum mendukung versi cepat). Untuk mengaktifkan multiprocessing, gunakan argumen `num_proc` dan tentukan jumlah proses:
+
+```python
+slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)
+
+
+def slow_tokenize_function(examples):
+ return slow_tokenizer(examples["review"], truncation=True)
+
+
+tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
+```
+
+Anda bisa bereksperimen sedikit dengan waktu eksekusi untuk menentukan jumlah proses yang optimal; dalam kasus kami, 8 tampaknya memberikan peningkatan kecepatan terbaik. Berikut adalah hasil yang kami peroleh dengan dan tanpa multiprocessing:
+
+Opsi | Tokenizer Cepat | Tokenizer Lambat
+:----------------:|:---------------:|:----------------:
+`batched=True` | 10.8 detik | 4 menit 41 detik
+`batched=False` | 59.2 detik | 5 menit 3 detik
+`batched=True`, `num_proc=8` | 6.52 detik | 41.3 detik
+`batched=False`, `num_proc=8` | 9.49 detik | 45.2 detik
+
+Itu adalah hasil yang jauh lebih masuk akal untuk tokenizer lambat, namun performa tokenizer cepat juga meningkat secara signifikan. Namun, perlu dicatat bahwa hal tersebut tidak selalu berlaku -- untuk nilai `num_proc` selain 8, pengujian kami menunjukkan bahwa lebih cepat menggunakan `batched=True` tanpa opsi tersebut. Secara umum, kami tidak menyarankan penggunaan multiprocessing Python untuk tokenizer cepat dengan `batched=True`.
+
+
+
+Menggunakan `num_proc` untuk mempercepat proses sangat disarankan, **selama fungsi yang kamu gunakan tidak sudah memproses paralel secara internal**.
+
+
+
+Semua fungsionalitas ini yang diringkas ke dalam satu metode saja sudah sangat mengagumkan, tetapi masih ada lagi! Dengan `Dataset.map()` dan `batched=True`, Anda bisa mengubah jumlah elemen dalam dataset Anda. Ini sangat berguna dalam banyak situasi, terutama ketika Anda ingin membuat beberapa fitur pelatihan dari satu contoh data, dan kita akan perlu melakukan ini sebagai bagian dari pra-pemrosesan untuk beberapa tugas NLP yang akan kita kerjakan di [Bab 7](/course/chapter7).
+
+
+
+💡 Dalam pembelajaran mesin, *contoh (example)* biasanya didefinisikan sebagai sekumpulan *fitur* yang kita masukkan ke model. Dalam beberapa konteks, fitur ini adalah kolom dalam `Dataset`, tapi dalam kasus lain (seperti tanya jawab), satu contoh bisa menghasilkan beberapa fitur sekaligus.
+
+
+
+Mari lihat bagaimana caranya! Kita akan tokenisasi dan potong contoh kita hingga maksimal 128 token, tapi kita minta tokenizer untuk mengembalikan *semua potongan teks* bukan hanya yang pertama. Ini bisa dilakukan dengan `return_overflowing_tokens=True`:
+
+```python
+def tokenize_and_split(examples):
+ return tokenizer(
+ examples["review"],
+ truncation=True,
+ max_length=128,
+ return_overflowing_tokens=True,
+ )
+```
+
+Mari kita uji ini pada satu contoh terlebih dahulu sebelum menggunakan `Dataset.map()` pada seluruh dataset:
+
+```python
+result = tokenize_and_split(drug_dataset["train"][0])
+[len(inp) for inp in result["input_ids"]]
+```
+
+```python out
+[128, 49]
+```
+
+Jadi, contoh pertama dalam set pelatihan kita menjadi dua fitur karena ditokenisasi melebihi jumlah token maksimum yang telah kita tentukan: yang pertama dengan panjang 128 dan yang kedua dengan panjang 49. Sekarang mari kita lakukan ini untuk semua elemen dalam dataset!
+
+```python
+tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
+```
+
+```python out
+ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
+```
+
+Oh tidak! Itu tidak berhasil! Kenapa? Melihat pesan error akan memberi kita petunjuk: ada ketidaksesuaian panjang pada salah satu kolom, yang satu memiliki panjang 1.463 dan yang lainnya 1.000. Jika Anda sudah melihat [dokumentasi](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map) `Dataset.map()`, Anda mungkin ingat bahwa yang diberikan ke fungsi yang kita mapping adalah jumlah sampel; di sini, 1.000 contoh menghasilkan 1.463 fitur baru, yang menyebabkan error pada bentuk (shape).
+
+Masalahnya adalah kita mencoba menggabungkan dua dataset dengan ukuran berbeda: kolom-kolom dari `drug_dataset` memiliki jumlah contoh tertentu (yaitu 1.000 seperti dalam pesan error), tetapi `tokenized_dataset` yang sedang kita buat memiliki lebih banyak (1.463 dalam pesan error; lebih dari 1.000 karena kita melakukan tokenisasi ulasan panjang menjadi lebih dari satu contoh dengan menggunakan `return_overflowing_tokens=True`). Ini tidak bisa dilakukan pada sebuah `Dataset`, jadi kita harus menghapus kolom-kolom dari dataset lama atau membuat ukurannya sama seperti pada dataset baru. Kita bisa melakukan yang pertama dengan argumen `remove_columns`:
+
+```python
+tokenized_dataset = drug_dataset.map(
+ tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
+)
+```
+
+Sekarang berhasil. Kita bisa cek bahwa dataset baru memiliki jumlah elemen lebih banyak dari sebelumnya:
+
+```python
+len(tokenized_dataset["train"]), len(drug_dataset["train"])
+```
+
+```python out
+(206772, 138514)
+```
+
+Kita sempat menyebutkan bahwa kita juga bisa menangani masalah panjang yang tidak cocok dengan menyamakan ukuran kolom lama dengan kolom baru. Untuk melakukan ini, kita membutuhkan field `overflow_to_sample_mapping` yang dikembalikan oleh tokenizer saat kita menetapkan `return_overflowing_tokens=True`. Field ini memberikan pemetaan dari indeks fitur baru ke indeks contoh asalnya. Dengan itu, kita bisa mengaitkan setiap key yang ada di dataset asli dengan daftar nilai yang ukurannya tepat, dengan mengulangi nilai tiap contoh sebanyak jumlah fitur baru yang dihasilkannya:
+
+```py
+def tokenize_and_split(examples):
+ result = tokenizer(
+ examples["review"],
+ truncation=True,
+ max_length=128,
+ return_overflowing_tokens=True,
+ )
+ # Ambil pemetaan antara indeks baru dan lama
+ sample_map = result.pop("overflow_to_sample_mapping")
+ for key, values in examples.items():
+ result[key] = [values[i] for i in sample_map]
+ return result
+```
+
+Kita bisa melihat bahwa ini bekerja dengan `Dataset.map()` tanpa perlu menghapus kolom-kolom lama:
+
+```py
+tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
+tokenized_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
+ num_rows: 206772
+ })
+ test: Dataset({
+ features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
+ num_rows: 68876
+ })
+})
+```
+
+Kita mendapatkan jumlah fitur pelatihan yang sama seperti sebelumnya, tetapi kali ini kita mempertahankan semua kolom lama. Jika kamu membutuhkannya untuk pasca-pemrosesan setelah menerapkan model, pendekatan ini bisa sangat berguna.
+
+Sekarang kamu telah melihat bagaimana 🤗 Datasets dapat digunakan untuk melakukan praproses dataset dengan berbagai cara. Walaupun fungsi pemrosesan di 🤗 Datasets sudah mencakup sebagian besar kebutuhan pelatihan model, mungkin ada saatnya kamu perlu berpindah ke Pandas untuk menggunakan fitur yang lebih canggih seperti `DataFrame.groupby()` atau API tingkat tinggi untuk visualisasi. Untungnya, 🤗 Datasets dirancang agar interoperable dengan library seperti Pandas, NumPy, PyTorch, TensorFlow, dan JAX. Mari kita lihat bagaimana cara kerjanya.
+
+## Dari `Dataset` ke `DataFrame` dan sebaliknya[[from-datasets-to-dataframes-and-back]]
+
+
+
+Untuk memungkinkan konversi antar berbagai library pihak ketiga, 🤗 Datasets menyediakan fungsi `Dataset.set_format()`. Fungsi ini hanya mengubah **format keluaran** dataset, jadi kamu bisa berpindah ke format lain tanpa mempengaruhi **format data inti**, yang menggunakan Apache Arrow. Format ini diubah secara langsung. Untuk demontrasi, mari kita ubah dataset kita ke format Pandas:
+
+```py
+drug_dataset.set_format("pandas")
+```
+
+Sekarang saat kita mengakses elemen dari dataset, kita mendapatkan `pandas.DataFrame` alih-alih dictionary:
+
+```py
+drug_dataset["train"][:3]
+```
+
+
+
+
+
+
patient_id
+
drugName
+
condition
+
review
+
rating
+
date
+
usefulCount
+
review_length
+
+
+
+
+
0
+
95260
+
Guanfacine
+
adhd
+
"My son is halfway through his fourth week of Intuniv..."
+
8.0
+
April 27, 2010
+
192
+
141
+
+
+
1
+
92703
+
Lybrel
+
birth control
+
"I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..."
+
5.0
+
December 14, 2009
+
17
+
134
+
+
+
2
+
138000
+
Ortho Evra
+
birth control
+
"This is my first time using any form of birth control..."
+
8.0
+
November 3, 2015
+
10
+
89
+
+
+
+
+Mari kita buat `pandas.DataFrame` untuk seluruh set pelatihan dengan memilih semua elemen dari `drug_dataset["train"]`:
+
+```py
+train_df = drug_dataset["train"][:]
+```
+
+
+
+🚨 Di balik layar, `Dataset.set_format()` mengubah format keluaran dari metode `__getitem__()` milik dataset. Artinya, saat kita ingin membuat objek baru seperti `train_df` dari `Dataset` dalam format `"pandas"`, kita perlu melakukan slicing seluruh dataset untuk mendapatkan `pandas.DataFrame`. Kamu bisa verifikasi bahwa `drug_dataset["train"]` tetap bertipe `Dataset`, terlepas dari format output-nya.
+
+
+
+Dari sini, kita bisa menggunakan semua fitur Pandas yang kita inginkan. Contohnya, kita bisa melakukan chaining untuk menghitung distribusi kelas pada kolom `condition`:
+
+```py
+frequencies = (
+ train_df["condition"]
+ .value_counts()
+ .to_frame()
+ .reset_index()
+ .rename(columns={"index": "condition", "count": "frequency"})
+)
+frequencies.head()
+```
+
+
+
+
+
+
condition
+
frequency
+
+
+
+
+
0
+
birth control
+
27655
+
+
+
1
+
depression
+
8023
+
+
+
2
+
acne
+
5209
+
+
+
3
+
anxiety
+
4991
+
+
+
4
+
pain
+
4744
+
+
+
+
+Setelah selesai melakukan analisis di Pandas, kita bisa membuat objek `Dataset` baru dari `DataFrame` tersebut dengan menggunakan fungsi `Dataset.from_pandas()`:
+
+```py
+from datasets import Dataset
+
+freq_dataset = Dataset.from_pandas(frequencies)
+freq_dataset
+```
+
+```python out
+Dataset({
+ features: ['condition', 'frequency'],
+ num_rows: 819
+})
+```
+
+
+
+✏️ **Coba sendiri!** Hitung rata-rata rating per obat, lalu simpan hasilnya ke dalam `Dataset` baru.
+
+
+
+Ini mengakhiri tur kita terhadap berbagai teknik praproses data yang tersedia di 🤗 Datasets. Sebagai penutup bagian ini, mari kita buat *validation set* untuk menyiapkan dataset agar bisa digunakan melatih sebuah *classifier*. Sebelum itu, kita reset kembali format keluaran `drug_dataset` dari `"pandas"` ke `"arrow"`:
+
+```python
+drug_dataset.reset_format()
+```
+
+## Membuat validation set[[creating-a-validation-set]]
+
+Meskipun kita memiliki *test set* yang bisa digunakan untuk evaluasi, praktik terbaik adalah membiarkan *test set* tetap utuh dan membuat *validation set* terpisah selama pengembangan. Setelah kamu puas dengan performa model di *validation set*, kamu bisa melakukan pengecekan akhir pada *test set*. Proses ini membantu mengurangi risiko *overfitting* ke *test set*, yang bisa menyebabkan model gagal saat digunakan pada data nyata.
+
+🤗 Datasets menyediakan fungsi `Dataset.train_test_split()` yang mirip dengan fungsi populer dari `scikit-learn`. Mari kita gunakan untuk membagi *training set* menjadi *train* dan *validation set* (dengan `seed` untuk replikasi hasil):
+
+```py
+drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
+# Ubah nama default "test" menjadi "validation"
+drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
+# Tambahkan "test set" ke `DatasetDict` kita
+drug_dataset_clean["test"] = drug_dataset["test"]
+drug_dataset_clean
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 110811
+ })
+ validation: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 27703
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 46108
+ })
+})
+```
+
+Mantap, kita telah menyiapkan dataset yang siap untuk digunakan dalam pelatihan model! Di [bagian 5](/course/chapter5/5) kita akan melihat bagaimana cara mengunggah dataset ke Hugging Face Hub, tapi untuk sekarang mari kita akhiri analisis ini dengan melihat beberapa cara menyimpan dataset ke penyimpanan lokal.
+
+## Menyimpan dataset[[saving-a-dataset]]
+
+
+
+Meskipun 🤗 Datasets akan menyimpan cache untuk setiap dataset yang diunduh dan operasi yang dilakukan padanya, terkadang kamu tetap ingin menyimpan dataset ke disk (misalnya, jika cache terhapus). Seperti yang ditunjukkan pada tabel berikut, 🤗 Datasets menyediakan tiga fungsi utama untuk menyimpan dataset ke dalam berbagai format:
+
+| Format Data | Fungsi |
+| :---------: | :-------------------: |
+| Arrow | `Dataset.save_to_disk()` |
+| CSV | `Dataset.to_csv()` |
+| JSON | `Dataset.to_json()` |
+
+Sebagai contoh, mari kita simpan dataset yang telah dibersihkan dalam format Arrow:
+
+```py
+drug_dataset_clean.save_to_disk("drug-reviews")
+```
+
+Ini akan membuat direktori dengan struktur berikut:
+
+```
+drug-reviews/
+├── dataset_dict.json
+├── test
+│ ├── dataset.arrow
+│ ├── dataset_info.json
+│ └── state.json
+├── train
+│ ├── dataset.arrow
+│ ├── dataset_info.json
+│ ├── indices.arrow
+│ └── state.json
+└── validation
+ ├── dataset.arrow
+ ├── dataset_info.json
+ ├── indices.arrow
+ └── state.json
+```
+
+Kita bisa melihat bahwa setiap *split* memiliki file *dataset.arrow* sendiri, bersama dengan metadata di *dataset_info.json* dan *state.json*. Kamu bisa menganggap format Arrow ini seperti tabel baris-kolom yang dioptimalkan untuk aplikasi performa tinggi yang memproses dataset besar.
+
+Setelah dataset disimpan, kita bisa memuatnya kembali menggunakan fungsi `load_from_disk()` seperti ini:
+
+```py
+from datasets import load_from_disk
+
+drug_dataset_reloaded = load_from_disk("drug-reviews")
+drug_dataset_reloaded
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 110811
+ })
+ validation: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 27703
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 46108
+ })
+})
+```
+
+Untuk format CSV dan JSON, kita harus menyimpan setiap *split* sebagai file terpisah. Salah satu caranya adalah dengan melakukan iterasi pada `DatasetDict`:
+
+```py
+for split, dataset in drug_dataset_clean.items():
+ dataset.to_json(f"drug-reviews-{split}.jsonl")
+```
+
+Ini akan menyimpan setiap split dalam [format JSON Lines](https://jsonlines.org), di mana setiap baris adalah satu entri JSON. Contoh baris pertama:
+
+```py
+!head -n 1 drug-reviews-train.jsonl
+```
+
+```python out
+{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
+```
+
+Kita bisa memuat kembali file-file ini menggunakan teknik dari [bagian 2](/course/chapter5/2) seperti berikut:
+
+```py
+data_files = {
+ "train": "drug-reviews-train.jsonl",
+ "validation": "drug-reviews-validation.jsonl",
+ "test": "drug-reviews-test.jsonl",
+}
+drug_dataset_reloaded = load_dataset("json", data_files=data_files)
+```
+
+Dan selesai sudah eksplorasi kita mengenai *data wrangling* dengan 🤗 Datasets! Sekarang setelah kita memiliki dataset yang bersih dan siap digunakan untuk pelatihan model, berikut beberapa ide yang bisa kamu coba:
+
+1. Gunakan teknik dari [Bab 3](/course/chapter3) untuk melatih *classifier* yang bisa memprediksi kondisi pasien berdasarkan ulasan obat.
+2. Gunakan pipeline `summarization` dari [Bab 1](/course/chapter1) untuk membuat ringkasan dari ulasan pasien.
+
+Selanjutnya, kita akan melihat bagaimana 🤗 Datasets memungkinkan kamu bekerja dengan dataset berukuran besar tanpa membuat laptopmu kewalahan!
diff --git a/chapters/id/chapter5/4.mdx b/chapters/id/chapter5/4.mdx
new file mode 100644
index 000000000..7cf150cba
--- /dev/null
+++ b/chapters/id/chapter5/4.mdx
@@ -0,0 +1,304 @@
+# Big data? 🤗 Datasets siap membantu![[big-data-datasets-to-the-rescue]]
+
+
+
+Saat ini, bukan hal yang aneh jika kamu harus bekerja dengan dataset berukuran beberapa gigabyte, terutama jika kamu berencana melatih model transformer seperti BERT atau GPT-2 dari awal. Dalam kasus seperti ini, bahkan hanya *memuat* datanya saja sudah bisa menjadi tantangan. Misalnya, korpus WebText yang digunakan untuk melatih GPT-2 terdiri dari lebih dari 8 juta dokumen dan 40 GB teks — memuatnya ke dalam RAM laptop bisa bikin "serangan jantung"!
+
+Untungnya, 🤗 Datasets dirancang untuk mengatasi keterbatasan ini. Library ini membebaskanmu dari masalah manajemen memori dengan memperlakukan dataset sebagai file _memory-mapped_, dan dari batas penyimpanan hard disk dengan fitur _streaming_ entri dari korpus.
+
+
+
+Di bagian ini, kita akan menjelajahi fitur-fitur tersebut menggunakan korpus besar berukuran 825 GB yang dikenal sebagai [The Pile](https://pile.eleuther.ai). Ayo mulai!
+
+## Apa itu The Pile?[[what-is-the-pile]]
+
+The Pile adalah korpus teks berbahasa Inggris yang dibuat oleh [EleutherAI](https://www.eleuther.ai) untuk melatih model bahasa skala besar. Korpus ini mencakup berbagai macam dataset, mulai dari artikel ilmiah, repositori kode GitHub, hingga teks web yang telah difilter. Korpus pelatihan ini tersedia dalam [potongan 14 GB](https://the-eye.eu/public/AI/pile/), dan Anda juga bisa mengunduh beberapa [komponen individual](https://the-eye.eu/public/AI/pile_preliminary_components/). Mari kita mulai dengan melihat dataset PubMed Abstracts, yaitu korpus abstrak dari 15 juta publikasi biomedis di [PubMed](https://pubmed.ncbi.nlm.nih.gov/). Dataset ini menggunakan [format JSON Lines](https://jsonlines.org) dan dikompresi menggunakan pustaka `zstandard`, jadi pertama-tama kita perlu menginstalnya:
+
+```py
+!pip install zstandard
+```
+
+Selanjutnya, kita bisa memuat dataset menggunakan metode file jarak jauh yang sudah kita pelajari di [bagian 2](/course/chapter5/2):
+
+```py
+from datasets import load_dataset
+
+# Ini akan memakan waktu beberapa menit, jadi silakan ngopi atau ngeteh dulu :)
+data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
+pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
+pubmed_dataset
+```
+
+```python out
+Dataset({
+ features: ['meta', 'text'],
+ num_rows: 15518009
+})
+```
+
+Kita bisa melihat bahwa dataset ini memiliki **15.518.009 baris** dan **2 kolom** — banyak sekali!
+
+
+
+✎ Secara default, 🤗 Datasets akan mengekstrak file yang dibutuhkan untuk memuat dataset. Jika kamu ingin menghemat ruang penyimpanan, kamu bisa menambahkan `DownloadConfig(delete_extracted=True)` ke argumen `download_config` di `load_dataset()`. Lihat [dokumentasi](https://huggingface.co/docs/datasets/package_reference/builder_classes#datasets.DownloadConfig) untuk info lebih lanjut.
+
+
+
+Mari kita lihat isi dari contoh pertama:
+
+```py
+pubmed_dataset[0]
+```
+
+```python out
+{
+ 'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'
+}
+```
+
+Oke, ini tampaknya adalah abstrak dari artikel jurnal medis. Sekarang mari kita lihat berapa banyak RAM yang digunakan untuk memuat dataset ini!
+
+## Keajaiban memory mapping[[the-magic-of-memory-mapping]]
+
+Salah satu cara sederhana untuk mengukur penggunaan memori di Python adalah dengan menggunakan pustaka [`psutil`](https://psutil.readthedocs.io/en/latest/), yang bisa diinstal seperti ini:
+
+```python
+!pip install psutil
+```
+
+Pustaka ini menyediakan kelas `Process` yang memungkinkan kita memeriksa penggunaan memori proses saat ini:
+
+```py
+import psutil
+
+# memory_info diekspresikan dalam byte, jadi kita konversi ke megabyte
+print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
+```
+
+```python out
+RAM used: 5678.33 MB
+```
+
+Di sini, atribut `rss` mengacu pada _resident set size_, yaitu bagian dari memori yang digunakan suatu proses di RAM. Pengukuran ini juga mencakup memori yang digunakan oleh interpreter Python dan pustaka-pustaka yang telah kita muat, sehingga jumlah memori yang sebenarnya digunakan untuk memuat dataset sedikit lebih kecil. Sebagai perbandingan, mari kita lihat seberapa besar ukuran dataset di penyimpanan disk, menggunakan atribut `dataset_size`. Karena hasilnya diekspresikan dalam byte seperti sebelumnya, kita perlu mengonversinya secara manual ke dalam satuan gigabyte:
+
+```py
+print(f"Dataset size in bytes: {pubmed_dataset.dataset_size}")
+size_gb = pubmed_dataset.dataset_size / (1024**3)
+print(f"Dataset size (cache file) : {size_gb:.2f} GB")
+```
+
+```python out
+Dataset size in bytes : 20979437051
+Dataset size (cache file) : 19.54 GB
+```
+
+Bagus — meskipun ukurannya hampir 20 GB, kita bisa memuat dan mengakses dataset ini dengan konsumsi RAM yang jauh lebih kecil!
+
+
+
+✏️ **Coba sendiri!** Pilih salah satu [komponen](https://the-eye.eu/public/AI/pile_preliminary_components/) dari The Pile yang ukurannya lebih besar dari RAM laptop/komputer kamu, muat menggunakan 🤗 Datasets, dan ukur penggunaan RAM-nya. Untuk hasil akurat, lakukan pengukuran di proses baru. Kamu bisa menemukan ukuran dekompresi setiap subset di Tabel 1 dari [makalah The Pile](https://arxiv.org/abs/2101.00027).
+
+
+
+Jika kamu terbiasa dengan Pandas, hasil ini mungkin mengejutkan karena aturan umum Wes Kinney yang terkenal bahwa kamu biasanya butuh **5 sampai 10 kali lebih banyak RAM** dibandingkan ukuran dataset. Lantas, bagaimana 🤗 Datasets mengatasi masalah manajemen memori ini? 🤗 Datasets memperlakukan setiap dataset sebagai [file memory-mapped](https://en.wikipedia.org/wiki/Memory-mapped_file), yang memungkinkan perpindahan antara RAM dan penyimpanan disk tanpa perlu memuat keseluruhan dataset ke dalam memori.
+
+File yang menggunakan memory-mapping juga dapat dibagikan ke berbagai proses secara bersamaan, yang memungkinkan metode seperti `Dataset.map()` diparalelkan tanpa perlu memindahkan atau menyalin dataset. Di balik layar, semua kemampuan ini dimungkinkan oleh format memori [Apache Arrow](https://arrow.apache.org) dan pustaka [`pyarrow`](https://arrow.apache.org/docs/python/index.html), yang membuat proses pemuatan dan pemrosesan data menjadi sangat cepat. (Untuk informasi lebih lanjut tentang Apache Arrow dan perbandingannya dengan Pandas, lihat [tulisan blog Dejan Simic](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) Untuk melihat cara kerjanya secara langsung, mari kita jalankan uji kecepatan kecil dengan melakukan iterasi pada semua elemen dalam dataset PubMed Abstracts:
+
+```py
+import timeit
+
+code_snippet = """batch_size = 1000
+
+for idx in range(0, len(pubmed_dataset), batch_size):
+ _ = pubmed_dataset[idx:idx + batch_size]
+"""
+
+time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
+print(
+ f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
+ f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
+)
+```
+
+```python out
+Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s
+```
+
+Di sini kita menggunakan modul `timeit` dari Python untuk mengukur waktu eksekusi yang dibutuhkan oleh `code_snippet`. Biasanya, Anda bisa melakukan iterasi terhadap dataset dengan kecepatan beberapa persepuluh GB/s hingga beberapa GB/s. Ini sangat cocok untuk sebagian besar aplikasi, tetapi terkadang Anda harus bekerja dengan dataset yang terlalu besar bahkan untuk disimpan di hard drive laptop Anda. Sebagai contoh, jika kita mencoba mengunduh seluruh isi The Pile, kita akan membutuhkan ruang disk kosong sebesar 825 GB! Untuk menangani kasus seperti ini, 🤗 Datasets menyediakan fitur streaming yang memungkinkan kita mengunduh dan mengakses elemen secara langsung (on the fly), tanpa perlu mengunduh seluruh dataset. Mari kita lihat bagaimana cara kerjanya.
+
+
+
+💡 Di Jupyter Notebook kamu juga bisa mengukur waktu eksekusi cell menggunakan [`%%timeit`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).
+
+
+
+## Streaming dataset[[streaming-datasets]]
+
+Untuk mengaktifkan **streaming**, cukup tambahkan argumen `streaming=True` saat memanggil `load_dataset()`. Contohnya, mari kita muat kembali dataset **PubMed Abstracts** dalam mode streaming:
+
+```py
+pubmed_dataset_streamed = load_dataset(
+ "json", data_files=data_files, split="train", streaming=True
+)
+```
+
+Alih-alih objek `Dataset` seperti sebelumnya, mode streaming akan mengembalikan objek `IterableDataset`. Sesuai namanya, kamu harus **mengiterasi** objek ini untuk mengakses elemennya. Untuk mengambil elemen pertama:
+
+```py
+next(iter(pubmed_dataset_streamed))
+```
+
+```python out
+{'meta': {'pmid': 11409574, 'language': 'eng'},
+ "text": "Epidemiologi hipoksemia pada anak-anak dengan infeksi saluran pernapasan bawah akut.\nUntuk menentukan prevalensi hipoksemia pada anak-anak di bawah usia 5 tahun yang menderita infeksi saluran pernapasan bawah akut (ALRI), faktor risiko hipoksemia pada anak-anak di bawah usia 5 tahun dengan ALRI, dan hubungan antara hipoksemia dengan peningkatan risiko kematian pada anak-anak dengan usia yang sama ..."}
+```
+
+Elemen dari dataset streaming bisa diproses langsung menggunakan `IterableDataset.map()`, yang berguna misalnya saat proses pelatihan model dan kamu ingin melakukan tokenisasi. Prosesnya sama seperti yang kita lakukan di [Bab 3](/course/chapter3), bedanya adalah hasilnya dikembalikan satu per satu:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
+tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
+next(iter(tokenized_dataset))
+```
+
+```python out
+{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
+```
+
+
+
+💡 Untuk mempercepat proses tokenisasi pada dataset streaming, kamu bisa menambahkan `batched=True`. Ini akan memproses data per batch (default-nya 1000) dan bisa disesuaikan dengan `batch_size`.
+
+
+
+Kamu juga bisa mengacak dataset streaming menggunakan `IterableDataset.shuffle()`, meskipun fungsinya berbeda dari `Dataset.shuffle()`. Di sini, elemen hanya diacak dalam `buffer_size` tertentu:
+
+```py
+shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
+next(iter(shuffled_dataset))
+```
+
+```python out
+{'meta': {'pmid': 11410799, 'language': 'eng'},
+ 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy...'}
+```
+
+Dalam contoh ini, kita memilih satu contoh secara acak dari 10.000 contoh pertama dalam buffer. Setelah sebuah contoh diakses, posisinya di buffer akan diisi oleh contoh berikutnya dari korpus (yaitu contoh ke-10.001 dalam kasus di atas). Anda juga dapat memilih elemen dari dataset yang sedang di-stream menggunakan fungsi `IterableDataset.take()` dan `IterableDataset.skip()`, yang cara kerjanya mirip dengan `Dataset.select()`. Misalnya, untuk memilih 5 contoh pertama dari dataset PubMed Abstracts, kita bisa melakukan hal berikut:
+
+```py
+dataset_head = pubmed_dataset_streamed.take(5)
+list(dataset_head)
+```
+
+```python out
+[
+ {
+ "meta": { "pmid": 11409574, "language": "eng" },
+ "text": "Epidemiologi hipoksemia pada anak-anak dengan infeksi saluran pernapasan bawah akut ..."
+ },
+ {
+ "meta": { "pmid": 11409575, "language": "eng" },
+ "text": "Tanda-tanda klinis hipoksemia pada anak-anak dengan infeksi saluran pernapasan bawah akut: indikator terapi oksigen ..."
+ },
+ {
+ "meta": { "pmid": 11409576, "language": "eng" },
+ "text": "Hipoksemia pada anak-anak dengan pneumonia berat di Papua Nugini ..."
+ },
+ {
+ "meta": { "pmid": 11409577, "language": "eng" },
+ "text": "Konsentrator dan tabung oksigen ..."
+ },
+ {
+ "meta": { "pmid": 11409578, "language": "eng" },
+ "text": "Pasokan oksigen di daerah pedesaan Afrika: sebuah pengalaman pribadi ..."
+ }
+]
+```
+
+Demikian pula, Anda dapat menggunakan fungsi `IterableDataset.skip()` untuk membuat pembagian data pelatihan dan validasi dari dataset yang telah diacak, seperti berikut:
+
+```py
+# Melewatkan 1000 contoh pertama untuk training
+train_dataset = shuffled_dataset.skip(1000)
+# Mengambil 1000 contoh pertama untuk validasi
+validation_dataset = shuffled_dataset.take(1000)
+```
+
+Mari kita akhiri eksplorasi kita tentang streaming dataset dengan sebuah aplikasi umum: menggabungkan beberapa dataset menjadi satu korpus. 🤗 Datasets menyediakan fungsi `interleave_datasets()` yang mengubah daftar objek `IterableDataset` menjadi satu `IterableDataset`, di mana elemen-elemen dari dataset baru diperoleh secara bergantian dari contoh-contoh sumber. Fungsi ini sangat berguna ketika Anda ingin menggabungkan dataset berukuran besar. Sebagai contoh, mari kita lakukan streaming subset FreeLaw dari The Pile, yaitu dataset sebesar 51 GB yang berisi opini hukum dari pengadilan-pengadilan di Amerika Serikat:
+
+```py
+law_dataset_streamed = load_dataset(
+ "json",
+ data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
+ split="train",
+ streaming=True,
+)
+next(iter(law_dataset_streamed))
+```
+
+```python out
+{'meta': {'case_ID': '110921.json', ...}, 'text': '461 U.S. 238 (1983)...'}
+```
+
+Dataset ini cukup besar untuk membuat laptop kewalahan, tetapi dengan streaming kita bisa mengaksesnya dengan lancar! Sekarang, mari kita **gabungkan** PubMed dan FreeLaw:
+
+```py
+from itertools import islice
+from datasets import interleave_datasets
+
+combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
+list(islice(combined_dataset, 2))
+```
+
+```python out
+[
+ {
+ "meta": { "pmid": 11409574, "language": "eng" },
+ "text": "Epidemiologi hipoksemia pada anak-anak dengan infeksi saluran pernapasan bawah akut ..."
+ },
+ {
+ "meta": {
+ "case_ID": "110921.json",
+ "case_jurisdiction": "scotus.tar.gz",
+ "date_created": "2010-04-28T17:12:49Z"
+ },
+ "text": "\n461 U.S. 238 (1983)\nOLIM DKK.\nlawan\nWAKINEKONA\nNo. 81-1581.\nMahkamah Agung Amerika Serikat.\nDisidangkan pada 19 Januari 1983.\nDiputuskan pada 26 April 1983.\nBANDING KEPADA PENGADILAN BANDING AMERIKA SERIKAT UNTUK SIRKUIT KESEMBILAN\n*239 Michael A. Lilly, Wakil Jaksa Agung Utama Hawaii, menyampaikan argumen untuk para pemohon. Bersamanya dalam dokumen tertulis adalah James H. Dannenberg, Wakil Jaksa Agung..."
+ }
+]
+
+```
+
+Di sini kita menggunakan fungsi `islice()` dari modul `itertools` di Python untuk memilih dua contoh pertama dari dataset gabungan, dan kita bisa melihat bahwa contoh-contoh tersebut cocok dengan contoh pertama dari masing-masing dua dataset sumber.
+
+Terakhir, jika Anda ingin melakukan streaming seluruh isi The Pile yang berukuran 825 GB, Anda bisa mengambil semua berkas yang telah disiapkan dengan cara berikut:
+
+```py
+base_url = "https://the-eye.eu/public/AI/pile/"
+data_files = {
+ "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
+ "validation": base_url + "val.jsonl.zst",
+ "test": base_url + "test.jsonl.zst",
+}
+pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
+next(iter(pile_dataset["train"]))
+```
+
+```python out
+{'meta': {'pile_set_name': 'Pile-CC'}, 'text': 'It is done, and submitted...'}
+```
+
+
+
+✏️ **Coba sendiri!** Gunakan salah satu korpus besar seperti [`mc4`](https://huggingface.co/datasets/mc4) atau [`oscar`](https://huggingface.co/datasets/oscar) untuk membuat dataset multibahasa streaming yang mencerminkan proporsi bahasa yang digunakan di suatu negara. Misalnya: di Swiss ada 4 bahasa nasional — Jerman, Prancis, Italia, dan Romansh — kamu bisa membuat korpus Swiss dengan sampling subset Oscar berdasarkan proporsi bahasa tersebut.
+
+
+
+Sekarang kamu sudah punya semua alat yang dibutuhkan untuk **memuat dan memproses dataset dari berbagai ukuran dan jenis** — tapi, kecuali kamu sangat beruntung, akan tiba waktunya kamu harus membuat dataset sendiri untuk menyelesaikan masalah tertentu. Itulah yang akan kita bahas di bagian selanjutnya!
diff --git a/chapters/id/chapter5/5.mdx b/chapters/id/chapter5/5.mdx
new file mode 100644
index 000000000..36f5f58a5
--- /dev/null
+++ b/chapters/id/chapter5/5.mdx
@@ -0,0 +1,405 @@
+# Membuat Dataset Sendiri[[creating-your-own-dataset]]
+
+
+
+Terkadang, dataset yang kamu butuhkan untuk membangun aplikasi NLP belum tersedia — jadi kamu perlu membuatnya sendiri. Di bagian ini, kita akan menunjukkan cara membuat korpus dari [GitHub issues](https://github.com/features/issues), yang sering digunakan untuk melacak bug atau permintaan fitur pada repositori GitHub. Dataset ini bisa digunakan untuk berbagai tujuan, seperti:
+
+* Menganalisis berapa lama waktu yang dibutuhkan untuk menyelesaikan issues atau pull request
+* Melatih _multilabel classifier_ untuk menandai issue dengan metadata berdasarkan deskripsinya (misalnya: "bug", "enhancement", atau "question")
+* Membuat mesin pencari semantik untuk menemukan issue yang cocok dengan kueri pengguna
+
+Di sini kita akan fokus pada **pembuatan korpus**, dan di bagian selanjutnya kita akan membahas bagaimana membangun aplikasi pencarian semantik. Untuk membuatnya lebih menarik, kita akan menggunakan issue dari proyek open source yang populer: **🤗 Datasets!** Mari kita mulai dengan mendapatkan data dan mengeksplorasi informasi di dalamnya.
+
+## Mendapatkan Data[[getting-the-data]]
+
+Kamu bisa menemukan semua issue di 🤗 Datasets dengan membuka tab [Issues](https://github.com/huggingface/datasets/issues) pada repositorinya. Pada saat penulisan, terdapat **331** issue terbuka dan **668** yang sudah ditutup.
+
+
+
+
+
+Jika kamu klik salah satu issue, kamu akan melihat informasi seperti judul, deskripsi, dan label yang menjelaskan isi issue tersebut. Contohnya terlihat pada gambar berikut:
+
+
+
+
+
+Untuk mengunduh semua issue dari repositori, kita akan menggunakan [GitHub REST API](https://docs.github.com/en/rest) dan mengakses endpoint [`Issues`](https://docs.github.com/en/rest/reference/issues#list-repository-issues). Endpoint ini akan mengembalikan daftar objek JSON, masing-masing berisi berbagai informasi seperti judul, deskripsi, status, dan lainnya.
+
+Cara paling mudah untuk mengambil issue adalah dengan library `requests`, yaitu pustaka standar Python untuk membuat HTTP request. Instal dulu dengan:
+
+```python
+!pip install requests
+```
+
+Setelah pustaka diinstal, Anda dapat melakukan permintaan GET ke endpoint `Issues` dengan memanggil fungsi `requests.get()`. Sebagai contoh, Anda bisa menjalankan perintah berikut untuk mengambil isu pertama pada halaman pertama:
+
+```python
+import requests
+
+url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
+response = requests.get(url)
+```
+
+Objek `response` berisi banyak informasi termasuk status kode HTTP:
+
+```py
+response.status_code
+```
+
+```python out
+200
+```
+
+Kode status `200` berarti permintaan berhasil (Anda dapat menemukan daftar kode status HTTP yang mungkin [di sini](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). Namun, yang benar-benar ingin kita lihat adalah _payload_-nya, yang dapat diakses dalam berbagai format seperti byte, string, atau JSON. Karena kita tahu bahwa isu-isu kita berada dalam format JSON, mari kita periksa payload-nya sebagai berikut:
+
+```python
+response.json()
+```
+
+Contoh output:
+
+```python out
+[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
+ 'repository_url': 'https://api.github.com/repos/huggingface/datasets',
+ 'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
+ 'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
+ 'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
+ 'id': 968650274,
+ 'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
+ 'number': 2792,
+ 'title': 'Update GooAQ',
+ 'user': {'login': 'bhavitvyamalik',
+ 'id': 19718818,
+ 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
+ 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
+ 'gravatar_id': '',
+ 'url': 'https://api.github.com/users/bhavitvyamalik',
+ 'html_url': 'https://github.com/bhavitvyamalik',
+ 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
+ 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
+ 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
+ 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
+ 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
+ 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
+ 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
+ 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
+ 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
+ 'type': 'User',
+ 'site_admin': False},
+ 'labels': [],
+ 'state': 'open',
+ 'locked': False,
+ 'assignee': None,
+ 'assignees': [],
+ 'milestone': None,
+ 'comments': 1,
+ 'created_at': '2021-08-12T11:40:18Z',
+ 'updated_at': '2021-08-12T12:31:17Z',
+ 'closed_at': None,
+ 'author_association': 'CONTRIBUTOR',
+ 'active_lock_reason': None,
+ 'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
+ 'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
+ 'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
+ 'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
+ 'performed_via_github_app': None}]
+```
+
+Seperti yang bisa dilihat, kita mendapat banyak informasi, termasuk `title`, `body`, dan `number` dari issue, serta detail user yang membukanya.
+
+
+
+✏️ **Coba sendiri!** Klik beberapa URL di payload JSON untuk melihat informasi apa saja yang terhubung dengan setiap issue GitHub.
+
+
+
+Seperti dijelaskan dalam [dokumentasi GitHub](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting), permintaan tanpa autentikasi dibatasi hingga 60 permintaan per jam. Meskipun Anda dapat meningkatkan parameter kueri `per_page` untuk mengurangi jumlah permintaan yang dikirim, Anda tetap akan mencapai batas tersebut pada repositori yang memiliki lebih dari beberapa ribu isu. Sebagai gantinya, Anda sebaiknya mengikuti [instruksi GitHub](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) untuk membuat _personal access token_ agar dapat meningkatkan batas permintaan menjadi 5.000 per jam. Setelah Anda memiliki token, Anda bisa menyertakannya sebagai bagian dari header permintaan:
+
+```python
+GITHUB_TOKEN = "xxx" # Ganti dengan token milikmu
+headers = {"Authorization": f"token {GITHUB_TOKEN}"}
+```
+
+
+
+⚠️ Jangan pernah membagikan notebook yang berisi `GITHUB_TOKEN` Anda secara langsung. Kami menyarankan Anda menghapus sel terakhir setelah mengeksekusinya untuk menghindari kebocoran informasi secara tidak sengaja. Lebih baik lagi, simpan token tersebut di dalam file *.env* dan gunakan pustaka [`python-dotenv`](https://github.com/theskumar/python-dotenv) untuk memuatnya secara otomatis sebagai variabel lingkungan (environment variable).
+
+
+
+Berikut fungsi Python untuk mengunduh semua issue dari repositori GitHub:
+
+```python
+import time
+import math
+from pathlib import Path
+import pandas as pd
+from tqdm.notebook import tqdm
+
+
+def fetch_issues(
+ owner="huggingface",
+ repo="datasets",
+ num_issues=10_000,
+ rate_limit=5_000,
+ issues_path=Path("."),
+):
+ if not issues_path.is_dir():
+ issues_path.mkdir(exist_ok=True)
+
+ batch = []
+ all_issues = []
+ per_page = 100 # Jumlah issue per halaman
+ num_pages = math.ceil(num_issues / per_page)
+ base_url = "https://api.github.com/repos"
+
+ for page in tqdm(range(num_pages)):
+ query = f"issues?page={page}&per_page={per_page}&state=all"
+ issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
+ batch.extend(issues.json())
+
+ if len(batch) > rate_limit and len(all_issues) < num_issues:
+ all_issues.extend(batch)
+ batch = [] # Kosongkan batch
+ print("Batas permintaan GitHub tercapai. Menunggu 1 jam ...")
+ time.sleep(60 * 60 + 1)
+
+ all_issues.extend(batch)
+ df = pd.DataFrame.from_records(all_issues)
+ df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
+ print(
+ f"Berhasil mengunduh semua issue untuk {repo}! Dataset disimpan di {issues_path}/{repo}-issues.jsonl"
+ )
+```
+
+Sekarang ketika kita memanggil `fetch_issues()`, fungsi ini akan mengunduh semua issue secara bertahap (batch) untuk menghindari melampaui batas permintaan per jam dari GitHub. Hasilnya akan disimpan dalam file _repository_name-issues.jsonl_, di mana setiap baris adalah objek JSON yang merepresentasikan satu issue. Mari kita gunakan fungsi ini untuk mengambil semua issue dari proyek 🤗 Datasets:
+
+```python
+# Bergantung pada koneksi internetmu, proses ini bisa memakan waktu beberapa menit...
+fetch_issues()
+```
+
+Setelah issue berhasil diunduh, kita bisa memuatnya secara lokal menggunakan teknik yang telah kita pelajari di [bagian 2](/course/chapter5/2):
+
+```python
+issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
+ num_rows: 3019
+})
+```
+
+Bagus! Kita telah berhasil membuat dataset pertama kita dari nol! Tapi, kenapa jumlahnya ribuan issue padahal di [tab Issues](https://github.com/huggingface/datasets/issues) repositori 🤗 Datasets hanya terlihat sekitar 1.000 saja 🤔? Seperti yang dijelaskan dalam [dokumentasi GitHub](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user):
+
+> API REST GitHub v3 menganggap setiap *pull request* sebagai sebuah issue, tetapi tidak semua issue adalah *pull request*. Oleh karena itu, endpoint "Issues" dapat mengembalikan baik issue maupun pull request. Kamu dapat membedakan pull request dengan adanya kunci `pull_request`. Perlu diketahui bahwa `id` dari pull request yang dikembalikan dari endpoint "Issues" adalah ID issue.
+
+Karena konten antara issue dan pull request berbeda, mari kita lakukan sedikit pembersihan agar bisa membedakannya.
+
+## Membersihkan Data[[cleaning-up-the-data]]
+
+Berdasarkan dokumentasi di atas, kolom `pull_request` dapat digunakan untuk membedakan antara issue dan pull request. Mari kita lihat beberapa contoh acak untuk membandingkan. Sama seperti di [bagian 3](/course/chapter5/3), kita akan menggunakan `Dataset.shuffle()` dan `Dataset.select()`:
+
+```python
+sample = issues_dataset.shuffle(seed=666).select(range(3))
+
+# Cetak URL dan entri pull request-nya
+for url, pr in zip(sample["html_url"], sample["pull_request"]):
+ print(f">> URL: {url}")
+ print(f">> Pull request: {pr}\n")
+```
+
+```python out
+>> URL: https://github.com/huggingface/datasets/pull/850
+>> Pull request: {...}
+
+>> URL: https://github.com/huggingface/datasets/issues/2773
+>> Pull request: None
+
+>> URL: https://github.com/huggingface/datasets/pull/783
+>> Pull request: {...}
+```
+
+Dari sini kita bisa lihat bahwa setiap pull request memiliki berbagai URL terkait, sementara issue biasa memiliki nilai `None`. Berdasarkan hal ini, kita bisa menambahkan kolom baru `is_pull_request` untuk membedakan keduanya:
+
+```python
+issues_dataset = issues_dataset.map(
+ lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
+)
+```
+
+
+
+✏️ **Coba sendiri!** Hitung rata-rata waktu yang dibutuhkan untuk menutup issue pada proyek 🤗 Datasets. Kamu bisa menggunakan `Dataset.filter()` untuk menyaring pull request dan issue yang masih terbuka, lalu gunakan `Dataset.set_format()` untuk mengubah dataset menjadi `DataFrame` agar lebih mudah memproses kolom `created_at` dan `closed_at`. Untuk tantangan tambahan, hitung juga rata-rata waktu penutupan pull request.
+
+
+
+Meskipun kita bisa lanjut membersihkan dataset dengan menghapus atau mengganti nama kolom, umumnya lebih baik menyimpan dataset dalam bentuk mentah agar bisa digunakan dalam berbagai aplikasi.
+
+Sebelum kita unggah dataset ini ke Hugging Face Hub, ada satu hal lagi yang perlu kita tambahkan: **komentar** pada setiap issue atau pull request. Mari kita tambahkan menggunakan — seperti yang bisa ditebak — GitHub REST API!
+
+## Menambahkan Data Komentar[[augmenting-the-dataset]]
+
+Seperti terlihat pada gambar berikut, komentar pada issue atau pull request memberikan informasi yang sangat kaya, terutama jika kita ingin membuat mesin pencari untuk menjawab pertanyaan pengguna tentang pustaka ini.
+
+
+
+
+
+GitHub REST API menyediakan endpoint [`Comments`](https://docs.github.com/en/rest/reference/issues#list-issue-comments) yang mengembalikan semua komentar terkait dengan nomor issue tertentu. Mari kita uji endpoint ini:
+
+```python
+issue_number = 2792
+url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
+response = requests.get(url, headers=headers)
+response.json()
+```
+
+```python out
+[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
+ 'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
+ 'id': 897594128,
+ 'node_id': 'IC_kwDODunzps41gDMQ',
+ 'user': {'login': 'bhavitvyamalik',
+ 'id': 19718818,
+ 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
+ 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
+ 'gravatar_id': '',
+ 'url': 'https://api.github.com/users/bhavitvyamalik',
+ 'html_url': 'https://github.com/bhavitvyamalik',
+ 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
+ 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
+ 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
+ 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
+ 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
+ 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
+ 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
+ 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
+ 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
+ 'type': 'User',
+ 'site_admin': False},
+ 'created_at': '2021-08-12T12:21:52Z',
+ 'updated_at': '2021-08-12T12:31:17Z',
+ 'author_association': 'CONTRIBUTOR',
+ 'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
+ 'performed_via_github_app': None}]
+```
+
+Kita dapat melihat bahwa komentar disimpan dalam field `body`, jadi mari kita tulis fungsi sederhana yang mengembalikan semua komentar yang terkait dengan sebuah issue dengan mengambil isi `body` dari setiap elemen dalam `response.json()`:
+
+```py
+def get_comments(issue_number):
+ url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
+ response = requests.get(url, headers=headers)
+ return [r["body"] for r in response.json()]
+
+
+# Uji apakah fungsi kita bekerja sebagaimana mestinya
+get_comments(2792)
+```
+
+```python out
+["@albertvillanova tes saya gagal di sini:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nSaat saya mencoba memuat dataset di mesin lokal, semuanya berjalan baik. Ada saran bagaimana cara menghindari error ini?"]
+```
+
+Ini terlihat bagus, jadi mari kita gunakan `Dataset.map()` untuk menambahkan kolom `comments` baru ke setiap issue dalam dataset kita:
+
+```py
+# Tergantung pada koneksi internet Anda, ini bisa memakan waktu beberapa menit...
+issues_with_comments_dataset = issues_dataset.map(
+ lambda x: {"comments": get_comments(x["number"])}
+)
+```
+
+Langkah terakhir adalah mengunggah dataset kita ke Hub. Mari kita lihat bagaimana caranya.
+
+## Mengunggah dataset ke Hugging Face Hub[[uploading-the-dataset-to-the-hugging-face-hub]]
+
+
+
+Sekarang kita memiliki dataset yang sudah ditambah dengan komentar, saatnya untuk mengunggahnya ke Hub agar bisa dibagikan kepada komunitas! Mengunggah dataset sangat mudah: sama seperti model dan tokenizer dari 🤗 Transformers, kita bisa menggunakan metode `push_to_hub()` untuk mengunggah dataset. Untuk melakukannya, kita memerlukan token autentikasi, yang bisa didapatkan dengan login ke Hugging Face Hub menggunakan fungsi `notebook_login()`:
+
+```py
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Ini akan menampilkan widget tempat Anda bisa memasukkan nama pengguna dan kata sandi, dan token API akan disimpan di *~/.huggingface/token*. Jika Anda menjalankan kode di terminal, Anda bisa login lewat CLI:
+
+```bash
+huggingface-cli login
+```
+
+Setelah itu, kita bisa mengunggah dataset kita dengan menjalankan:
+
+```py
+issues_with_comments_dataset.push_to_hub("github-issues")
+```
+
+Mulai dari sini, siapa pun bisa mengunduh dataset ini cukup dengan memberikan ID repositori sebagai argumen `path` ke `load_dataset()`:
+
+```py
+remote_dataset = load_dataset("lewtun/github-issues", split="train")
+remote_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 2855
+})
+```
+
+Keren, kita sudah mengunggah dataset ke Hub dan sekarang bisa digunakan oleh orang lain! Masih ada satu hal penting yang perlu dilakukan: menambahkan _dataset card_ yang menjelaskan bagaimana korpus dibuat dan menyediakan informasi berguna lainnya untuk komunitas.
+
+
+
+💡 Anda juga bisa mengunggah dataset ke Hugging Face Hub langsung dari terminal dengan menggunakan `huggingface-cli` dan sedikit perintah Git. Lihat [panduan 🤗 Datasets](https://huggingface.co/docs/datasets/share#share-a-dataset-using-the-cli) untuk detailnya.
+
+
+
+## Membuat dataset card[[creating-a-dataset-card]]
+
+Dataset yang terdokumentasi dengan baik lebih mungkin bermanfaat bagi orang lain (termasuk diri Anda di masa depan!), karena mereka menyediakan konteks yang memungkinkan pengguna memutuskan apakah dataset tersebut relevan untuk tugas mereka dan mengevaluasi potensi bias atau risiko dalam penggunaannya.
+
+Di Hugging Face Hub, informasi ini disimpan dalam file *README.md* di setiap repositori dataset. Ada dua langkah utama yang perlu Anda lakukan sebelum membuat file ini:
+
+1. Gunakan [aplikasi `datasets-tagging`](https://huggingface.co/datasets/tagging/) untuk membuat tag metadata dalam format YAML. Tag ini digunakan untuk berbagai fitur pencarian di Hugging Face Hub dan memastikan dataset Anda dapat dengan mudah ditemukan oleh komunitas. Karena kita membuat dataset kustom di sini, Anda perlu mengkloning repositori `datasets-tagging` dan menjalankan aplikasi secara lokal. Berikut tampilan antarmukanya:
+
+
+
+
+
+2. Baca [panduan 🤗 Datasets](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) tentang cara membuat dataset card yang informatif dan gunakan sebagai template.
+
+Anda bisa membuat file *README.md* langsung di Hub, dan Anda bisa menemukan contoh template dataset card di repositori dataset `lewtun/github-issues`. Berikut adalah tangkapan layar dari dataset card yang telah diisi:
+
+
+
+
+
+
+
+✏️ **Coba sendiri!** Gunakan aplikasi `dataset-tagging` dan [panduan 🤗 Datasets](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) untuk menyelesaikan file *README.md* untuk dataset GitHub issues Anda.
+
+
+
+Itu saja! Kita telah melihat dalam bagian ini bahwa membuat dataset yang bagus bisa jadi cukup rumit, tapi untungnya proses mengunggah dan membagikannya ke komunitas sangatlah mudah. Di bagian selanjutnya kita akan menggunakan dataset baru ini untuk membuat mesin pencari semantik dengan 🤗 Datasets yang dapat mencocokkan pertanyaan ke issue dan komentar yang paling relevan.
+
+
+
+✏️ **Coba sendiri!** Ikuti langkah-langkah di bagian ini untuk membuat dataset GitHub issues dari pustaka open source favorit Anda (pilih yang selain 🤗 Datasets, tentu saja!). Untuk poin tambahan, latih model klasifikasi multilabel untuk memprediksi tag yang ada di field `labels`.
+
+
diff --git a/chapters/id/chapter5/6.mdx b/chapters/id/chapter5/6.mdx
new file mode 100644
index 000000000..be231dee7
--- /dev/null
+++ b/chapters/id/chapter5/6.mdx
@@ -0,0 +1,517 @@
+
+
+# Pencarian Semantik dengan FAISS[[semantic-search-with-faiss]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Pada [bagian 5](/course/chapter5/5), kita telah membuat dataset dari isu dan komentar GitHub dari repositori 🤗 Datasets. Di bagian ini, kita akan menggunakan informasi tersebut untuk membangun mesin pencari yang dapat membantu kita menemukan jawaban atas pertanyaan penting tentang pustaka tersebut!
+
+
+
+## Menggunakan Embedding untuk Pencarian Semantik[[using-embeddings-for-semantic-search]]
+
+Seperti yang telah kita lihat di [Bab 1](/course/chapter1), model bahasa berbasis Transformer merepresentasikan setiap token dalam teks sebagai _vektor embedding_. Ternyata, kita bisa "menggabungkan" embedding individual ini untuk membuat representasi vektor dari satu kalimat, paragraf, atau bahkan dokumen secara keseluruhan. Embedding ini kemudian dapat digunakan untuk menemukan dokumen serupa dalam korpus dengan menghitung kemiripan dot-product (atau metrik kemiripan lainnya) antara setiap embedding dan mengembalikan dokumen yang memiliki tumpang tindih tertinggi.
+
+Di bagian ini kita akan menggunakan embedding untuk mengembangkan mesin pencari semantik. Mesin pencari semantik memiliki beberapa keunggulan dibanding pendekatan konvensional yang berbasis pencocokan kata kunci dalam kueri terhadap dokumen.
+
+
+
+
+
+
+## Memuat dan Menyiapkan Dataset[[loading-and-preparing-the-dataset]]
+
+Hal pertama yang perlu kita lakukan adalah mengunduh dataset isu GitHub kita. Mari kita gunakan fungsi `load_dataset()` seperti biasa:
+
+```py
+from datasets import load_dataset
+
+issues_dataset = load_dataset("lewtun/github-issues", split="train")
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 2855
+})
+```
+
+Di sini kita menentukan split `train` secara default dalam `load_dataset()`, sehingga ia mengembalikan sebuah objek `Dataset` bukan `DatasetDict`. Langkah pertama kita adalah menyaring *pull request*, karena biasanya tidak berguna untuk menjawab pertanyaan pengguna dan justru akan menambah noise dalam mesin pencari. Seperti yang sudah sering kita lakukan, kita bisa menggunakan fungsi `Dataset.filter()` untuk mengecualikan baris-baris ini. Sekalian, mari juga kita buang baris yang tidak memiliki komentar, karena komentar adalah bagian yang menyediakan jawaban:
+
+```py
+issues_dataset = issues_dataset.filter(
+ lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
+)
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 771
+})
+```
+
+Kita bisa lihat bahwa ada banyak kolom dalam dataset ini, namun sebagian besar tidak kita butuhkan untuk membangun mesin pencari. Dari sudut pandang pencarian, kolom yang paling informatif adalah `title`, `body`, dan `comments`, sementara `html_url` menyediakan tautan ke isu sumber. Mari kita gunakan `Dataset.remove_columns()` untuk menghapus kolom-kolom lain:
+
+```py
+columns = issues_dataset.column_names
+columns_to_keep = ["title", "body", "html_url", "comments"]
+columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
+issues_dataset = issues_dataset.remove_columns(columns_to_remove)
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body'],
+ num_rows: 771
+})
+```
+
+Untuk membuat embedding, kita akan menambahkan setiap komentar dengan `title` dan `body` dari isu terkait, karena informasi kontekstual sering kali ada di bagian tersebut. Karena kolom `comments` saat ini merupakan daftar komentar untuk setiap isu, kita perlu "meledakkan" kolom tersebut agar setiap baris hanya berisi satu pasangan `(html_url, title, body, comment)`. Dalam Pandas, kita bisa melakukannya dengan fungsi [`DataFrame.explode()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), yang membuat baris baru untuk setiap elemen dalam kolom berisi daftar, sambil menggandakan nilai kolom lain. Untuk melihatnya secara langsung, mari kita ubah terlebih dahulu ke format `DataFrame` Pandas:
+
+```py
+issues_dataset.set_format("pandas")
+df = issues_dataset[:]
+```
+
+Jika kita lihat baris pertama dalam `DataFrame` ini, kita akan melihat ada empat komentar terkait dengan isu tersebut:
+
+```py
+df["comments"][0].tolist()
+```
+
+```python out
+['kode bug terletak di:\r\n if data_args.task_name is not None:\r\n # Mengunduh dan memuat dataset dari hub.\r\n datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
+ 'Hai @jinec,\r\n\r\nSesekali kami menerima `ConnectionError` seperti ini dari situs github.com: https://raw.githubusercontent.com\r\n\r\nBiasanya, hal ini bisa diatasi dengan menunggu sejenak dan mencoba kembali.\r\n\r\nBisakah Anda konfirmasi apakah masalahnya masih terjadi?',
+ 'tidak bisa konek, bahkan lewat browser, tolong dicek apakah ada masalah.',
+ 'Saya bisa mengakses https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py tanpa masalah...']
+```
+
+Saat kita melakukan *explode* pada `df`, kita harapkan akan mendapatkan satu baris untuk setiap komentar tersebut. Mari kita periksa apakah hasilnya sesuai ekspektasi:
+
+```py
+comments_df = df.explode("comments", ignore_index=True)
+comments_df.head(4)
+```
+
+
Saya bisa mengakses https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py tanpa masalah...
+
Halo,\r\nSaya mencoba menjalankan run_glue.py dan mendapatkan error berikut...
+
+
+
+
+Bagus, kita bisa lihat baris-baris telah direplikasi, dengan kolom `comments` sekarang berisi komentar individual! Sekarang kita sudah selesai menggunakan Pandas, kita bisa dengan cepat kembali ke format `Dataset` dengan memuat `DataFrame` ke memori:
+
+```py
+from datasets import Dataset
+
+comments_dataset = Dataset.from_pandas(comments_df)
+comments_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body'],
+ num_rows: 2842
+})
+```
+
+Oke, sekarang kita punya beberapa ribu komentar untuk digunakan!
+
+
+
+✏️ **Coba sendiri!** Lihat apakah Anda bisa menggunakan `Dataset.map()` untuk meledakkan kolom `comments` dari `issues_dataset` _tanpa_ menggunakan Pandas. Ini agak rumit; Anda mungkin akan terbantu dengan bagian ["Batch mapping"](https://huggingface.co/docs/datasets/about_map_batch#batch-mapping) dari dokumentasi 🤗 Datasets.
+
+
+
+Sekarang kita sudah punya satu komentar per baris, mari buat kolom baru `comment_length` yang berisi jumlah kata per komentar:
+
+```py
+comments_dataset = comments_dataset.map(
+ lambda x: {"comment_length": len(x["comments"].split())}
+)
+```
+
+Kita bisa menggunakan kolom ini untuk menyaring komentar yang terlalu pendek, seperti "cc @lewtun" atau "Terima kasih!" yang tidak relevan untuk mesin pencari. Tidak ada batasan pasti, tapi sekitar 15 kata sepertinya jumlah yang baik untuk disaring:
+
+```py
+comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
+comments_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
+ num_rows: 2098
+})
+```
+
+Setelah membersihkan dataset, mari kita gabungkan judul isu, deskripsi, dan komentar ke dalam kolom baru `text`. Seperti biasa, kita akan menulis fungsi sederhana untuk digunakan dengan `Dataset.map()`:
+
+```py
+def concatenate_text(examples):
+ return {
+ "text": examples["title"]
+ + " \n "
+ + examples["body"]
+ + " \n "
+ + examples["comments"]
+ }
+
+
+comments_dataset = comments_dataset.map(concatenate_text)
+```
+
+Akhirnya kita siap untuk membuat embedding! Mari kita lihat bagaimana caranya.
+
+## Membuat Embedding Teks[[creating-text-embeddings]]
+
+Kita telah melihat di [Bab 2](/course/chapter2) bahwa kita bisa mendapatkan token embedding menggunakan kelas `AutoModel`. Yang kita butuhkan hanya memilih *checkpoint* yang cocok untuk memuat model. Untungnya, ada pustaka bernama `sentence-transformers` yang memang ditujukan untuk membuat embedding. Seperti yang dijelaskan dalam [dokumentasi pustaka](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), kasus penggunaan kita adalah contoh dari _asymmetric semantic search_, karena kita memiliki kueri pendek dan ingin menemukan jawabannya dalam dokumen yang lebih panjang, seperti komentar isu. Tabel [ringkasan model](https://www.sbert.net/docs/pretrained_models.html#model-overview) di dokumentasi menunjukkan bahwa checkpoint `multi-qa-mpnet-base-dot-v1` memiliki performa terbaik untuk pencarian semantik, jadi kita akan gunakan itu untuk aplikasi ini. Kita juga akan memuat tokenizer menggunakan checkpoint yang sama:
+
+{#if fw === 'pt'}
+
+```py
+from transformers import AutoTokenizer, AutoModel
+
+model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
+tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
+model = AutoModel.from_pretrained(model_ckpt)
+```
+
+Untuk mempercepat proses pembuatan embedding, sebaiknya model dan input ditempatkan di perangkat GPU. Mari kita lakukan sekarang:
+
+```py
+import torch
+
+device = torch.device("cuda")
+model.to(device)
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFAutoModel
+
+model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
+tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
+model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
+```
+
+Perhatikan bahwa kita menetapkan from_pt=True sebagai argumen dari metode from_pretrained(). Ini karena checkpoint multi-qa-mpnet-base-dot-v1 hanya memiliki bobot (weights) dalam format PyTorch, sehingga dengan menyetel from_pt=True, bobot tersebut akan secara otomatis dikonversi ke format TensorFlow. Seperti yang bisa Anda lihat, sangat mudah untuk beralih antar framework di 🤗 Transformers!
+
+{/if}
+
+Seperti yang telah disebutkan sebelumnya, kita ingin merepresentasikan setiap entri dalam korpus GitHub Issues kita sebagai sebuah vektor tunggal, jadi kita perlu melakukan "pooling" atau merata-ratakan embedding token dengan cara tertentu. Salah satu pendekatan populer adalah melakukan CLS pooling pada output model, di mana kita cukup mengambil last hidden state untuk token spesial [CLS]. Fungsi berikut akan melakukan hal tersebut untuk kita:
+
+
+```py
+def cls_pooling(model_output):
+ return model_output.last_hidden_state[:, 0]
+```
+
+Selanjutnya, kita akan membuat fungsi bantu untuk melakukan tokenisasi pada daftar dokumen, menempatkan tensor di GPU, mengoperasikannya ke model, lalu menerapkan *CLS pooling* pada output:
+
+{#if fw === 'pt'}
+
+```py
+def get_embeddings(text_list):
+ encoded_input = tokenizer(
+ text_list, padding=True, truncation=True, return_tensors="pt"
+ )
+ encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
+ model_output = model(**encoded_input)
+ return cls_pooling(model_output)
+```
+
+Kita bisa menguji fungsi tersebut dengan memberikan entri teks pertama dari korpus dan memeriksa bentuk output-nya:
+
+```py
+embedding = get_embeddings(comments_dataset["text"][0])
+embedding.shape
+```
+
+```python out
+torch.Size([1, 768])
+```
+
+Bagus, kita telah mengubah entri pertama dari korpus menjadi vektor berdimensi 768! Kita bisa menggunakan `Dataset.map()` untuk menerapkan fungsi `get_embeddings()` ke setiap baris dalam korpus, dan membuat kolom baru `embeddings` seperti berikut:
+
+```py
+embeddings_dataset = comments_dataset.map(
+ lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
+)
+```
+
+{:else}
+
+```py
+def get_embeddings(text_list):
+ encoded_input = tokenizer(
+ text_list, padding=True, truncation=True, return_tensors="tf"
+ )
+ encoded_input = {k: v for k, v in encoded_input.items()}
+ model_output = model(**encoded_input)
+ return cls_pooling(model_output)
+```
+
+Kita dapat menguji apakah fungsi tersebut bekerja dengan memberikannya entri teks pertama dari korpus kita dan memeriksa bentuk output-nya:
+
+```py
+embedding = get_embeddings(comments_dataset["text"][0])
+embedding.shape
+```
+
+```python out
+TensorShape([1, 768])
+```
+
+Hebat, kita telah mengubah entri pertama dalam korpus kita menjadi vektor berdimensi 768! Kita dapat menggunakan `Dataset.map()` untuk menerapkan fungsi `get_embeddings()` ke setiap baris dalam korpus kita, jadi mari kita buat kolom baru bernama `embeddings` seperti berikut:
+
+```py
+embeddings_dataset = comments_dataset.map(
+ lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
+)
+```
+
+{/if}
+
+Perhatikan bahwa kita telah mengubah embeddings ke dalam format array NumPy -- ini karena 🤗 Datasets memerlukan format ini ketika kita mencoba mengindeksnya dengan FAISS, yang akan kita lakukan selanjutnya.
+
+
+## Menggunakan FAISS untuk pencarian kemiripan yang efisien[[using-faiss-for-efficient-similarity-search]]
+
+Sekarang kita memiliki dataset berisi embedding, kita butuh cara untuk mencari di dalamnya. Untuk itu, kita akan menggunakan struktur data khusus dari 🤗 Datasets yang disebut _FAISS index_. [FAISS](https://faiss.ai/) (singkatan dari Facebook AI Similarity Search) adalah pustaka yang menyediakan algoritma efisien untuk mencari dan mengelompokkan vektor embedding dengan cepat.
+
+Ide dasar dari FAISS adalah membuat struktur data khusus bernama _index_ yang memungkinkan kita menemukan embedding yang paling mirip dengan embedding input. Membuat FAISS index dalam 🤗 Datasets cukup sederhana — kita cukup gunakan fungsi `Dataset.add_faiss_index()` dan menentukan kolom mana yang ingin diindeks:
+
+```py
+embeddings_dataset.add_faiss_index(column="embeddings")
+```
+
+Sekarang kita bisa melakukan pencarian dengan melakukan pencocokan *nearest neighbor* menggunakan fungsi `Dataset.get_nearest_examples()`. Mari kita coba dengan mengubah sebuah pertanyaan menjadi embedding terlebih dahulu:
+
+{#if fw === 'pt'}
+
+```py
+question = "Bagaimana cara memuat dataset secara offline?"
+question_embedding = get_embeddings([question]).cpu().detach().numpy()
+question_embedding.shape
+```
+
+```python out
+torch.Size([1, 768])
+```
+
+{:else}
+
+```py
+question = "How can I load a dataset offline?"
+question_embedding = get_embeddings([question]).numpy()
+question_embedding.shape
+```
+
+```python out
+(1, 768)
+```
+
+{/if}
+
+Sama seperti pada dokumen, sekarang kita memiliki vektor berdimensi 768 yang merepresentasikan pertanyaan, yang dapat kita bandingkan dengan seluruh korpus untuk menemukan embeddings yang paling mirip:
+
+```py
+scores, samples = embeddings_dataset.get_nearest_examples(
+ "embeddings", question_embedding, k=5
+)
+```
+
+Fungsi `Dataset.get_nearest_examples()` mengembalikan tuple berisi skor (yang menunjukkan tingkat kemiripan) dan sampel dokumen (dalam hal ini 5 hasil terbaik). Mari kita ubah ini menjadi `pandas.DataFrame` agar mudah untuk disortir:
+
+```py
+import pandas as pd
+
+samples_df = pd.DataFrame.from_dict(samples)
+samples_df["scores"] = scores
+samples_df.sort_values("scores", ascending=False, inplace=True)
+```
+
+Sekarang kita bisa menelusuri beberapa baris pertama untuk melihat seberapa baik kueri kita dicocokkan dengan komentar yang tersedia:
+
+```py
+for _, row in samples_df.iterrows():
+ print(f"KOMENTAR: {row.comments}")
+ print(f"SKOR: {row.scores}")
+ print(f"JUDUL: {row.title}")
+ print(f"URL: {row.html_url}")
+ print("=" * 50)
+ print()
+```
+
+```python out
+"""
+KOMENTAR: Membutuhkan koneksi online adalah hal yang menyulitkan dalam beberapa kasus, jadi akan sangat bagus jika mode offline ditambahkan — seperti bagaimana `transformers` memuat model secara offline dengan baik.
+
+Poin kedua dari @mandubian menyarankan bahwa ada solusi alternatif yang memungkinkan Anda menggunakan dataset offline (kustom?) dengan `datasets`. Bisakah Anda jelaskan bagaimana cara melakukannya?
+SKOR: 25.505046844482422
+JUDUL: Diskusi penggunaan datasets dalam mode offline
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+KOMENTAR: Builder dataset lokal (csv, text, json, dan pandas) sekarang sudah menjadi bagian dari paket `datasets` sejak #1726 :)
+Sekarang Anda bisa menggunakannya secara offline
+\`\`\`python
+datasets = load_dataset("text", data_files=data_files)
+\`\`\`
+
+Kami akan merilis versi baru segera
+SKOR: 24.555509567260742
+JUDUL: Diskusi penggunaan datasets dalam mode offline
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+KOMENTAR: Saya membuka PR yang memungkinkan modul yang sudah pernah dimuat dapat dimuat ulang meskipun tidak ada koneksi internet.
+
+Beri tahu saya jika Anda tahu cara lain untuk membuat pengalaman mode offline jadi lebih baik. Saya akan senang menambahkannya :)
+
+Saya juga mencatat opsi "freeze modules", untuk mencegah pembaruan modul lokal. Itu akan jadi fitur keren.
+
+----------
+
+> Poin kedua dari @mandubian menyarankan bahwa ada solusi alternatif untuk menggunakan dataset offline (kustom?) dengan `datasets`. Bisakah Anda jelaskan bagaimana bentuknya?
+
+Memang benar bahwa `load_dataset` memungkinkan untuk memuat skrip dataset jarak jauh (squad, glue, dll.) tetapi juga yang lokal.
+Misalnya jika Anda punya skrip dataset di `./my_dataset/my_dataset.py` maka Anda bisa jalankan
+\`\`\`python
+load_dataset("./my_dataset")
+\`\`\`
+dan skrip dataset tersebut akan menghasilkan dataset Anda secara permanen.
+
+----------
+
+Saya sedang meneliti agar builder dataset `csv`, `json`, `text`, `pandas` sudah termasuk dalam paket `datasets`, supaya tersedia offline secara default — berbeda dengan dataset lain yang masih perlu mengunduh skrip.
+lihat #1724
+SKOR: 24.14896583557129
+JUDUL: Diskusi penggunaan datasets dalam mode offline
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+KOMENTAR: > ini adalah cara saya untuk memuat dataset secara offline, tetapi **memerlukan** mesin yang terhubung ke internet
+>
+> 1. (mesin yang online)
+>
+> ```
+>
+> import datasets
+>
+> data = datasets.load_dataset(...)
+>
+> data.save_to_disk(/DIREKTORI/DATASET/ANDA)
+>
+> ```
+>
+> 2. salin direktori dari mesin online ke mesin offline
+>
+> 3. (mesin offline)
+>
+> ```
+>
+> import datasets
+>
+> data = datasets.load_from_disk(/DIREKTORI/DATA/YANG/DISIMPAN)
+>
+> ```
+>
+>
+>
+> Semoga membantu.
+
+SKOR: 22.893993377685547
+JUDUL: Diskusi penggunaan datasets dalam mode offline
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+KOMENTAR: Berikut cara saya memuat dataset secara offline, tapi **memerlukan** mesin yang terhubung ke internet
+1. (mesin online)
+\`\`\`
+import datasets
+data = datasets.load_dataset(...)
+data.save_to_disk(/YOUR/DATASET/DIR)
+\`\`\`
+2. salin direktori dari mesin online ke mesin offline
+3. (mesin offline)
+\`\`\`
+import datasets
+data = datasets.load_from_disk(/SAVED/DATA/DIR)
+\`\`\`
+
+Semoga membantu.
+SKOR: 22.406635284423828
+JUDUL: Diskusi penggunaan datasets dalam mode offline
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+"""
+```
+
+Tidak buruk! Hasil kedua kita tampaknya cocok dengan kueri yang diberikan.
+
+
+
+✏️ **Coba sendiri!** Buat kueri Anda sendiri dan lihat apakah Anda bisa menemukan jawabannya dalam dokumen yang ditemukan. Anda mungkin perlu menaikkan nilai parameter `k` dalam `Dataset.get_nearest_examples()` untuk memperluas pencarian.
+
+
\ No newline at end of file
diff --git a/chapters/id/chapter5/7.mdx b/chapters/id/chapter5/7.mdx
new file mode 100644
index 000000000..a7b0be644
--- /dev/null
+++ b/chapters/id/chapter5/7.mdx
@@ -0,0 +1,16 @@
+# 🤗 Datasets, selesai![[datasets-check]]
+
+
+
+Wah, kita sudah menjelajahi banyak hal di pustaka 🤗 Datasets — selamat telah sampai sejauh ini! Dengan pengetahuan yang telah Anda pelajari di bab ini, Anda sekarang seharusnya bisa:
+
+- Memuat dataset dari mana saja — baik dari Hugging Face Hub, laptop Anda, maupun server jarak jauh di perusahaan Anda.
+- Mengolah data Anda menggunakan kombinasi fungsi `Dataset.map()` dan `Dataset.filter()`.
+- Beralih dengan cepat antar format data seperti Pandas dan NumPy menggunakan `Dataset.set_format()`.
+- Membuat dataset Anda sendiri dan mengunggahnya ke Hugging Face Hub.
+- Membuat embedding dari dokumen Anda menggunakan model Transformer dan membangun mesin pencari semantik menggunakan FAISS.
+
+Di [Bab 7](/course/chapter7), kita akan memanfaatkan semua ini dengan baik saat menyelami tugas-tugas inti NLP yang sangat cocok untuk model Transformer. Tapi sebelum melangkah lebih jauh, uji pengetahuan Anda tentang 🤗 Datasets lewat kuis singkat berikut!
diff --git a/chapters/id/chapter5/8.mdx b/chapters/id/chapter5/8.mdx
new file mode 100644
index 000000000..b7eeb6209
--- /dev/null
+++ b/chapters/id/chapter5/8.mdx
@@ -0,0 +1,230 @@
+
+
+# Kuis Akhir Bab[[end-of-chapter-quiz]]
+
+
+
+Bab ini mencakup banyak hal! Jangan khawatir jika Anda belum memahami semua detailnya; bab-bab berikutnya akan membantu Anda memahami bagaimana semuanya bekerja di balik layar.
+
+Namun sebelum melanjutkan, mari kita uji apa yang sudah Anda pelajari di bab ini.
+
+### 1. Fungsi `load_dataset()` di 🤗 Datasets memungkinkan Anda memuat dataset dari lokasi mana saja berikut ini?
+
+data_files pada load_dataset() untuk memuat dataset lokal.",
+ correct: true
+ },
+ {
+ text: "Dari Hugging Face Hub",
+ explain: "Benar! Anda bisa memuat dataset dari Hub dengan memberikan ID dataset, misalnya load_dataset('emotion').",
+ correct: true
+ },
+ {
+ text: "Dari server jarak jauh",
+ explain: "Benar! Anda bisa memberikan URL ke argumen data_files pada load_dataset() untuk memuat file dari server jarak jauh.",
+ correct: true
+ },
+ ]}
+/>
+
+### 2. Misalkan Anda memuat salah satu tugas GLUE sebagai berikut:
+
+```py
+from datasets import load_dataset
+
+dataset = load_dataset("glue", "mrpc", split="train")
+```
+
+Perintah mana yang akan menghasilkan sampel acak sebanyak 50 elemen dari `dataset`?
+
+dataset.sample(50)",
+ explain: "Ini salah -- tidak ada metode Dataset.sample()."
+ },
+ {
+ text: "dataset.shuffle().select(range(50))",
+ explain: "Benar! Seperti yang Anda lihat di bab ini, Anda harus mengacak dataset terlebih dahulu dan kemudian memilih sampel darinya.",
+ correct: true
+ },
+ {
+ text: "dataset.select(range(50)).shuffle()",
+ explain: "Ini salah -- meskipun kodenya akan berjalan, itu hanya akan mengacak 50 elemen pertama dari dataset."
+ }
+ ]}
+/>
+
+### 3. Misalkan Anda memiliki dataset tentang hewan peliharaan bernama `pets_dataset`, yang memiliki kolom `name` yang menunjukkan nama setiap hewan. Pendekatan mana yang akan memungkinkan Anda menyaring dataset untuk semua hewan peliharaan yang namanya dimulai dengan huruf "L"?
+
+pets_dataset.filter(lambda x : x['name'].startswith('L'))",
+ explain: "Benar! Menggunakan fungsi lambda Python untuk filter cepat seperti ini adalah ide bagus. Bisakah Anda pikirkan solusi lain?",
+ correct: true
+ },
+ {
+ text: "pets_dataset.filter(lambda x['name'].startswith('L'))",
+ explain: "Ini salah -- fungsi lambda harus dalam bentuk umum lambda *argumen* : *ekspresi*, jadi Anda perlu memberikan argumen di sini."
+ },
+ {
+ text: "Buat fungsi seperti def filter_names(x): return x['name'].startswith('L') lalu jalankan pets_dataset.filter(filter_names).",
+ explain: "Benar! Seperti pada Dataset.map(), Anda bisa memberikan fungsi eksplisit ke Dataset.filter(). Ini berguna untuk logika kompleks yang tidak cocok dalam fungsi lambda pendek.",
+ correct: true
+ }
+ ]}
+/>
+
+### 4. Apa itu memory mapping?
+
+
+
+### 5. Apa saja manfaat utama dari memory mapping?
+
+
+
+### 6. Mengapa kode berikut gagal?
+
+```py
+from datasets import load_dataset
+
+dataset = load_dataset("allocine", streaming=True, split="train")
+dataset[0]
+```
+
+IterableDataset.",
+ explain: "Benar! IterableDataset adalah generator, bukan kontainer, jadi Anda harus mengakses elemennya menggunakan next(iter(dataset)).",
+ correct: true
+ },
+ {
+ text: "Dataset allocine tidak memiliki split train.",
+ explain: "Ini salah -- lihat [kartu dataset allocine](https://huggingface.co/datasets/allocine) di Hub untuk melihat split yang tersedia."
+ }
+ ]}
+/>
+
+### 7. Apa saja manfaat utama dari membuat kartu dataset?
+
+
+
+### 8. Apa itu pencarian semantik (semantic search)?
+
+
+
+### 9. Dalam pencarian semantik asimetris, Anda biasanya memiliki:
+
+
+
+### 10. Dapatkah saya menggunakan 🤗 Datasets untuk memuat data di domain lain, seperti pemrosesan suara?
+
+dataset MNIST untuk contoh visi komputer."
+ },
+ {
+ text: "Ya",
+ explain: "Benar! Lihat perkembangan menarik dalam bidang suara dan visi di pustaka 🤗 Transformers untuk melihat bagaimana 🤗 Datasets digunakan dalam domain ini.",
+ correct : true
+ },
+ ]}
+/>
diff --git a/chapters/id/chapter6/1.mdx b/chapters/id/chapter6/1.mdx
new file mode 100644
index 000000000..651227316
--- /dev/null
+++ b/chapters/id/chapter6/1.mdx
@@ -0,0 +1,19 @@
+# Pendahuluan[[introduction]]
+
+
+
+Di [Bab 3](/course/chapter3), kita telah melihat cara menyetel ulang (fine-tune) sebuah model untuk suatu tugas tertentu. Saat kita melakukan itu, kita menggunakan tokenizer yang sama dengan yang digunakan saat model tersebut dilatih sebelumnya — tetapi apa yang harus kita lakukan ketika ingin melatih model dari awal? Dalam kasus seperti ini, menggunakan tokenizer yang telah dilatih sebelumnya pada korpus dari domain atau bahasa lain biasanya tidak optimal. Sebagai contoh, tokenizer yang dilatih pada korpus berbahasa Inggris akan berkinerja buruk pada korpus teks berbahasa Jepang karena penggunaan spasi dan tanda baca sangat berbeda di antara kedua bahasa tersebut.
+
+Dalam bab ini, Anda akan belajar cara melatih tokenizer baru dari nol menggunakan korpus teks, sehingga tokenizer tersebut dapat digunakan untuk melakukan pretraining pada model bahasa. Semua ini akan dilakukan dengan bantuan pustaka [🤗 Tokenizers](https://github.com/huggingface/tokenizers), yang menyediakan tokenizer "cepat" dalam pustaka [🤗 Transformers](https://github.com/huggingface/transformers). Kita akan melihat lebih dekat fitur-fitur yang disediakan pustaka ini, dan menjelajahi bagaimana tokenizer cepat berbeda dari versi "lambat".
+
+Topik-topik yang akan kita bahas meliputi:
+
+* Cara melatih tokenizer baru yang mirip dengan yang digunakan oleh checkpoint tertentu pada korpus teks baru
+* Fitur-fitur khusus dari tokenizer cepat
+* Perbedaan antara tiga algoritma tokenisasi sub-kata utama yang digunakan dalam NLP saat ini
+* Cara membangun tokenizer dari nol menggunakan pustaka 🤗 Tokenizers dan melatihnya pada beberapa data
+
+Teknik-teknik yang diperkenalkan dalam bab ini akan mempersiapkan Anda untuk bagian di [Bab 7](/course/chapter7/6) di mana kita akan melihat cara membuat model bahasa untuk kode sumber Python. Mari kita mulai dengan melihat apa arti sebenarnya dari "melatih" sebuah tokenizer.
diff --git a/chapters/id/chapter6/10.mdx b/chapters/id/chapter6/10.mdx
new file mode 100644
index 000000000..c06f26b72
--- /dev/null
+++ b/chapters/id/chapter6/10.mdx
@@ -0,0 +1,283 @@
+
+
+# Kuis akhir bab[[end-of-chapter-quiz]]
+
+
+
+Ayo uji apa yang telah kamu pelajari di bab ini!
+
+### 1. Kapan kamu sebaiknya melatih tokenizer baru?
+
+
+
+### 2. Apa keuntungan menggunakan generator daftar teks dibandingkan dengan daftar daftar teks saat memakai `train_new_from_iterator()`?
+
+train_new_from_iterator().",
+ explain: "Daftar dari daftar teks sebenarnya adalah bentuk khusus dari generator daftar teks, jadi metode ini tetap akan menerimanya. Coba lagi!"
+ },
+ {
+ text: "Kamu akan menghindari memuat seluruh dataset ke dalam memori sekaligus.",
+ explain: "Benar! Setiap batch teks akan dilepaskan dari memori setelah diproses, dan keuntungannya akan terlihat jelas jika kamu menggunakan 🤗 Datasets.",
+ correct: true
+ },
+ {
+ text: "Ini akan memungkinkan pustaka 🤗 Tokenizers untuk menggunakan multiprocessing.",
+ explain: "Tidak, multiprocessing akan tetap digunakan tanpa tergantung pada itu."
+ },
+ {
+ text: "Tokenizer yang kamu latih akan menghasilkan teks yang lebih baik.",
+ explain: "Tokenizer tidak menghasilkan teks -- mungkin kamu sedang bingung dengan language model?"
+ }
+ ]}
+/>
+
+### 3. Apa keuntungan menggunakan tokenizer “fast”?
+
+
+
+### 4. Bagaimana pipeline `token-classification` menangani entitas yang terdiri dari beberapa token?
+
+
+
+### 5. Bagaimana pipeline `question-answering` menangani konteks yang panjang?
+
+
+
+### 6. Apa itu normalisasi?
+
+
+
+### 7. Apa itu pre-tokenization dalam subword tokenizer?
+
+
+
+### 8. Pilih pernyataan yang sesuai dengan model tokenisasi BPE.
+
+
+
+### 9. Pilih pernyataan yang sesuai dengan model tokenisasi WordPiece.
+
+
+
+### 10. Pilih pernyataan yang sesuai dengan model tokenisasi Unigram.
+
+
diff --git a/chapters/id/chapter6/2.mdx b/chapters/id/chapter6/2.mdx
new file mode 100644
index 000000000..061339443
--- /dev/null
+++ b/chapters/id/chapter6/2.mdx
@@ -0,0 +1,258 @@
+# Melatih Tokenizer Baru dari Tokenizer Lama[[training-a-new-tokenizer-from-an-old-one]]
+
+
+
+Jika model bahasa tidak tersedia dalam bahasa yang Anda minati, atau jika korpus Anda sangat berbeda dari korpus yang digunakan untuk melatih model tersebut, kemungkinan besar Anda ingin melatih ulang model dari awal dengan tokenizer yang disesuaikan untuk data Anda. Ini berarti Anda perlu melatih tokenizer baru pada dataset Anda. Tapi apa sebenarnya artinya? Ketika pertama kali kita membahas tokenizer di [Bab 2](/course/chapter2), kita melihat bahwa sebagian besar model Transformer menggunakan _algoritma tokenisasi sub-kata_. Untuk mengidentifikasi sub-kata mana yang paling relevan dan sering muncul dalam korpus, tokenizer perlu memproses seluruh teks dalam korpus tersebut — proses ini disebut *pelatihan*. Aturan spesifik yang digunakan dalam proses ini tergantung pada jenis tokenizer, dan kita akan membahas tiga algoritma utama nanti dalam bab ini.
+
+
+
+
+
+⚠️ Melatih tokenizer **tidak sama** dengan melatih model! Pelatihan model menggunakan _stochastic gradient descent_ untuk mengurangi nilai _loss_ secara bertahap pada setiap _batch_. Proses ini bersifat acak (artinya Anda perlu mengatur seed agar mendapatkan hasil yang sama jika melatih ulang). Sementara itu, pelatihan tokenizer adalah proses statistik untuk mengidentifikasi sub-kata terbaik dari sebuah korpus, dan aturan yang digunakan tergantung pada algoritma tokenisasi. Proses ini **deterministik**, artinya hasilnya akan selalu sama jika Anda menggunakan algoritma dan korpus yang sama.
+
+
+
+## Menyusun Korpus[[assembling-a-corpus]]
+
+Ada API yang sangat sederhana di pustaka 🤗 Transformers yang dapat Anda gunakan untuk melatih tokenizer baru dengan karakteristik yang sama seperti tokenizer yang sudah ada: `AutoTokenizer.train_new_from_iterator()`. Untuk melihat cara kerjanya, bayangkan kita ingin melatih GPT-2 dari awal, tetapi dalam bahasa selain bahasa Inggris. Tugas pertama kita adalah mengumpulkan banyak data dalam bahasa tersebut untuk dijadikan korpus pelatihan. Untuk memberikan contoh yang bisa dimengerti semua orang, kita tidak akan menggunakan bahasa seperti Rusia atau Mandarin, tetapi justru menggunakan bahasa Inggris yang bersifat khusus: kode Python.
+
+Pustaka [🤗 Datasets](https://github.com/huggingface/datasets) dapat membantu kita menyusun korpus dari kode sumber Python. Kita akan menggunakan fungsi `load_dataset()` seperti biasa untuk mengunduh dan menyimpan _cache_ dari dataset [CodeSearchNet](https://huggingface.co/datasets/code_search_net). Dataset ini dibuat untuk [tantangan CodeSearchNet](https://wandb.ai/github/CodeSearchNet/benchmark) dan berisi jutaan fungsi dari pustaka open source di GitHub dalam berbagai bahasa pemrograman. Di sini, kita akan memuat bagian Python dari dataset tersebut:
+
+```py
+from datasets import load_dataset
+
+# Ini bisa memakan waktu beberapa menit, jadi siapkan kopi atau teh sambil menunggu!
+raw_datasets = load_dataset("code_search_net", "python")
+```
+
+Kita dapat melihat _split_ pelatihan untuk mengetahui kolom apa saja yang tersedia:
+
+```py
+raw_datasets["train"]
+```
+
+```python out
+Dataset({
+ features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language',
+ 'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name',
+ 'func_code_url'
+ ],
+ num_rows: 412178
+})
+```
+
+Kita dapat melihat bahwa dataset ini memisahkan docstring dari kode, dan menyarankan tokenisasi untuk keduanya. Di sini, kita hanya akan menggunakan kolom `whole_func_string` untuk melatih tokenizer kita. Kita bisa melihat contoh salah satu fungsi dengan mengakses indeks tertentu pada _split_ pelatihan:
+
+```py
+print(raw_datasets["train"][123456]["whole_func_string"])
+```
+
+yang seharusnya mencetak:
+
+```out
+def handle_simple_responses(
+ self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
+ """Accepts normal responses from the device.
+
+ Args:
+ timeout_ms: Timeout in milliseconds to wait for each response.
+ info_cb: Optional callback for text sent from the bootloader.
+
+ Returns:
+ OKAY packet's message.
+ """
+ return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
+```
+
+Hal pertama yang perlu kita lakukan adalah mengubah dataset menjadi sebuah _iterator_ berupa daftar-daftar teks — misalnya, daftar dari daftar teks. Menggunakan daftar teks akan memungkinkan tokenizer kita bekerja lebih cepat (melatih dalam batch teks alih-alih memproses teks satu per satu), dan sebaiknya menggunakan iterator jika kita ingin menghindari menyimpan semuanya sekaligus di dalam memori. Jika korpus Anda sangat besar, Anda akan ingin memanfaatkan kenyataan bahwa 🤗 Datasets tidak memuat semuanya ke dalam RAM, melainkan menyimpan elemen-elemen dataset di disk.
+
+
+Melakukan hal berikut akan membuat daftar yang berisi daftar 1.000 teks per elemen, tetapi akan memuat semuanya ke memori:
+
+```py
+# Jangan aktifkan baris ini kecuali dataset Anda kecil!
+# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)]
+```
+
+Dengan menggunakan generator Python, kita dapat mencegah Python memuat semuanya ke dalam memori sebelum dibutuhkan. Untuk membuat generator seperti itu, cukup ganti tanda kurung siku dengan tanda kurung biasa:
+
+```py
+training_corpus = (
+ raw_datasets["train"][i : i + 1000]["whole_func_string"]
+ for i in range(0, len(raw_datasets["train"]), 1000)
+)
+```
+
+Baris kode ini tidak mengambil elemen apa pun dari dataset; ini hanya membuat sebuah objek yang bisa Anda gunakan dalam `for` loop di Python. Teks-teksnya hanya akan dimuat saat Anda membutuhkannya (yaitu, ketika Anda berada pada langkah dalam `for` loop yang memerlukannya), dan hanya 1.000 teks yang akan dimuat sekaligus. Dengan cara ini, Anda tidak akan kehabisan memori bahkan jika sedang memproses dataset yang sangat besar.
+
+Masalah dari objek generator adalah bahwa ia hanya bisa digunakan sekali. Jadi, alih-alih memberikan daftar 10 angka dua kali seperti ini:
+
+```py
+gen = (i for i in range(10))
+print(list(gen))
+print(list(gen))
+```
+
+kita malah mendapat:
+
+```python out
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+[]
+```
+
+Itulah sebabnya kita mendefinisikan fungsi yang mengembalikan generator:
+
+```py
+def get_training_corpus():
+ return (
+ raw_datasets["train"][i : i + 1000]["whole_func_string"]
+ for i in range(0, len(raw_datasets["train"]), 1000)
+ )
+
+
+training_corpus = get_training_corpus()
+```
+
+Anda juga bisa mendefinisikan generator menggunakan perulangan `for` dengan pernyataan `yield`:
+
+```py
+def get_training_corpus():
+ dataset = raw_datasets["train"]
+ for start_idx in range(0, len(dataset), 1000):
+ samples = dataset[start_idx : start_idx + 1000]
+ yield samples["whole_func_string"]
+```
+
+yang akan menghasilkan generator yang sama, tetapi memungkinkan Anda menggunakan logika yang lebih kompleks daripada yang bisa dilakukan dengan list comprehension.
+
+## Melatih Tokenizer Baru[[training-a-new-tokenizer]]
+
+Sekarang kita memiliki korpus dalam bentuk iterator yang berisi _batch_ teks, kita siap untuk melatih tokenizer baru. Untuk melakukan ini, pertama-tama kita perlu memuat tokenizer yang ingin kita pasangkan dengan model kita (dalam hal ini, GPT-2):
+
+```py
+from transformers import AutoTokenizer
+
+old_tokenizer = AutoTokenizer.from_pretrained("gpt2")
+```
+
+Meskipun kita akan melatih tokenizer baru, ini adalah langkah yang baik agar kita tidak memulai semuanya dari nol. Dengan cara ini, kita tidak perlu menentukan apa pun tentang algoritma tokenisasi atau token khusus yang ingin digunakan; tokenizer baru kita akan persis seperti milik GPT-2, dan satu-satunya hal yang akan berubah adalah kosakatanya, yang akan ditentukan berdasarkan pelatihan pada korpus kita.
+
+Pertama, mari kita lihat bagaimana tokenizer ini menangani contoh fungsi:
+
+```py
+example = '''def add_numbers(a, b):
+ """Add the two numbers `a` and `b`."""
+ return a + b'''
+
+tokens = old_tokenizer.tokenize(example)
+tokens
+```
+
+```python out
+['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
+ 'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
+```
+
+Tokenizer ini memiliki beberapa simbol khusus seperti `Ġ` dan `Ċ` yang menunjukkan spasi dan baris baru. Seperti yang terlihat, ini kurang efisien: tokenizer ini mengembalikan token terpisah untuk setiap spasi, padahal bisa saja mengelompokkannya (karena indentasi empat atau delapan spasi sangat umum dalam kode). Tokenizer juga memecah nama fungsi dengan cara yang aneh karena tidak terbiasa dengan karakter `_`.
+
+Mari kita latih tokenizer baru dan lihat apakah hal ini dapat diatasi. Kita akan menggunakan metode `train_new_from_iterator()`:
+
+```py
+tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000)
+```
+
+Perintah ini mungkin memakan waktu jika korpus Anda besar, tapi untuk dataset sebesar 1.6 GB, ini berjalan sangat cepat (sekitar 1 menit 16 detik pada CPU AMD Ryzen 9 3900X dengan 12 core).
+
+Perlu dicatat bahwa `AutoTokenizer.train_new_from_iterator()` hanya berfungsi jika tokenizer yang Anda gunakan adalah tokenizer "cepat". Seperti yang akan Anda lihat di bagian selanjutnya, pustaka 🤗 Transformers memiliki dua jenis tokenizer: beberapa ditulis murni dalam Python dan lainnya (yang cepat) didukung oleh pustaka 🤗 Tokenizers, yang ditulis dalam bahasa pemrograman [Rust](https://www.rust-lang.org). Python adalah bahasa yang paling sering digunakan untuk aplikasi data science dan deep learning, tetapi ketika sesuatu perlu diparalelkan agar lebih cepat, maka harus ditulis dalam bahasa lain. Sebagai contoh, perkalian matriks yang menjadi inti dari perhitungan model ditulis dalam CUDA, sebuah pustaka C yang dioptimalkan untuk GPU.
+
+Melatih tokenizer dari nol dengan Python murni akan sangat lambat, itulah sebabnya pustaka 🤗 Tokenizers dikembangkan. Namun, seperti Anda tidak perlu mempelajari CUDA untuk menjalankan model Anda di GPU, Anda juga tidak perlu belajar Rust untuk menggunakan tokenizer cepat. Pustaka 🤗 Tokenizers menyediakan binding ke Python untuk berbagai metode yang secara internal memanggil kode Rust; contohnya untuk melakukan pelatihan tokenizer secara paralel, atau seperti yang kita lihat di [Bab 3](/course/chapter3), untuk melakukan tokenisasi pada batch input.
+
+Sebagian besar model Transformer memiliki tokenizer cepat yang tersedia (ada beberapa pengecualian yang bisa Anda lihat [di sini](https://huggingface.co/transformers/#supported-frameworks)), dan API `AutoTokenizer` akan selalu memilih tokenizer cepat untuk Anda jika tersedia. Pada bagian selanjutnya, kita akan melihat beberapa fitur khusus lain yang dimiliki tokenizer cepat, yang akan sangat berguna untuk tugas-tugas seperti klasifikasi token dan question answering. Namun sebelum masuk ke sana, mari kita coba tokenizer baru kita pada contoh sebelumnya:
+
+```py
+tokens = tokenizer.tokenize(example)
+tokens
+```
+
+```python out
+['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
+ 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
+```
+
+Di sini kita kembali melihat simbol-simbol khusus `Ġ` dan `Ċ` yang menandakan spasi dan baris baru, tetapi kita juga bisa melihat bahwa tokenizer kita telah mempelajari beberapa token yang sangat spesifik untuk korpus fungsi Python: misalnya, ada token `ĊĠĠĠ` yang merepresentasikan indentasi, dan token `Ġ"""` yang merepresentasikan tiga tanda kutip yang memulai sebuah docstring. Tokenizer juga berhasil membagi nama fungsi pada karakter `_`. Ini merupakan representasi yang cukup ringkas; sebagai perbandingan, menggunakan tokenizer Bahasa Inggris biasa pada contoh yang sama akan menghasilkan kalimat yang lebih panjang.
+
+```py
+print(len(tokens))
+print(len(old_tokenizer.tokenize(example)))
+```
+
+```python out
+27
+36
+```
+
+Mari kita lihat contoh lain:
+
+```python
+example = """class LinearLayer():
+ def __init__(self, input_size, output_size):
+ self.weight = torch.randn(input_size, output_size)
+ self.bias = torch.zeros(output_size)
+
+ def __call__(self, x):
+ return x @ self.weights + self.bias
+ """
+tokenizer.tokenize(example)
+```
+
+```python out
+['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
+ 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
+ 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
+ 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
+ 'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ']
+```
+
+Selain token indentasi, kita juga bisa melihat token untuk indentasi ganda: `ĊĠĠĠĠĠĠĠ`. Kata kunci Python seperti `class`, `init`, `call`, `self`, dan `return` masing-masing ditokenisasi sebagai satu token, dan kita bisa melihat bahwa tokenizer ini juga memisahkan karakter `_`, `.`, bahkan nama dalam camel case seperti `LinearLayer` menjadi `["ĠLinear", "Layer"]`.
+
+## Menyimpan Tokenizer[[saving-the-tokenizer]]
+
+Untuk memastikan kita bisa menggunakannya kembali di masa depan, kita perlu menyimpan tokenizer baru ini. Seperti pada model, kita bisa menggunakan metode `save_pretrained()`:
+
+```py
+tokenizer.save_pretrained("code-search-net-tokenizer")
+```
+
+Perintah ini akan membuat folder baru bernama *code-search-net-tokenizer*, yang berisi semua file yang dibutuhkan untuk memuat kembali tokenizer tersebut. Jika Anda ingin membagikannya dengan kolega atau teman, Anda bisa mengunggahnya ke Hugging Face Hub setelah login. Jika Anda bekerja di notebook, ada fungsi khusus yang bisa digunakan:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Ini akan menampilkan widget untuk memasukkan kredensial akun Hugging Face Anda. Jika Anda tidak bekerja di notebook, cukup ketik perintah berikut di terminal:
+
+```bash
+huggingface-cli login
+```
+
+Setelah login, Anda bisa mendorong (push) tokenizer ke Hub dengan perintah berikut:
+
+```py
+tokenizer.push_to_hub("code-search-net-tokenizer")
+```
+
+Ini akan membuat repositori baru di namespace Anda dengan nama `code-search-net-tokenizer`, berisi file tokenizer. Anda kemudian bisa memuat tokenizer dari mana saja menggunakan metode `from_pretrained()`:
+
+```py
+# Ganti "huggingface-course" dengan namespace Anda sendiri jika ingin menggunakan tokenizer Anda
+tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
+```
+
+Sekarang Anda sudah siap untuk melatih model bahasa dari nol dan menyetelnya ulang untuk tugas Anda! Kita akan membahas itu di [Bab 7](/course/chapter7), tapi sebelumnya, pada sisa bab ini kita akan melihat lebih dekat tokenizer cepat dan menjelajahi secara rinci apa yang sebenarnya terjadi ketika kita memanggil metode `train_new_from_iterator()`.
diff --git a/chapters/id/chapter6/3.mdx b/chapters/id/chapter6/3.mdx
new file mode 100644
index 000000000..fb64f445f
--- /dev/null
+++ b/chapters/id/chapter6/3.mdx
@@ -0,0 +1,472 @@
+
+
+# Kekuatan Khusus Tokenizer Cepat[[fast-tokenizers-special-powers]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Di bagian ini, kita akan melihat lebih dekat kemampuan tokenizer di 🤗 Transformers. Sampai sekarang kita hanya menggunakannya untuk men-tokenisasi input atau mendekode ID kembali menjadi teks, tetapi tokenizer -- terutama yang didukung oleh pustaka 🤗 Tokenizers -- bisa melakukan jauh lebih banyak. Untuk mengilustrasikan fitur tambahan ini, kita akan menjelajahi cara mereplikasi hasil dari pipeline `token-classification` (yang kita sebut `ner`) dan `question-answering` yang pertama kali kita temui di [Bab 1](/course/chapter1).
+
+
+
+Dalam pembahasan berikut, kita akan sering membedakan antara tokenizer "lambat" dan "cepat". Tokenizer lambat adalah tokenizer yang ditulis dalam Python di dalam pustaka 🤗 Transformers, sementara versi cepat disediakan oleh 🤗 Tokenizers, yang ditulis dalam bahasa Rust. Jika Anda masih ingat tabel dari [Bab 5](/course/chapter5/3) yang menunjukkan berapa lama waktu yang dibutuhkan oleh tokenizer cepat dan lambat untuk melakukan tokenisasi pada Drug Review Dataset, Anda pasti sudah punya gambaran mengapa kita menyebutnya cepat dan lambat:
+
+| | Tokenizer Cepat | Tokenizer Lambat |
+|:-------------:|:---------------:|:----------------:|
+| `batched=True` | 10.8s | 4m41s |
+| `batched=False` | 59.2s | 5m3s |
+
+
+
+⚠️ Saat men-tokenisasi satu kalimat, Anda mungkin tidak melihat perbedaan kecepatan antara tokenizer lambat dan cepat. Bahkan, tokenizer cepat bisa lebih lambat! Perbedaannya baru terlihat jelas saat Anda men-tokenisasi banyak teks sekaligus.
+
+
+
+## Enkode dalam Batch[[batch-encoding]]
+
+
+
+Output dari tokenizer bukan hanya dictionary Python biasa; yang kita dapatkan sebenarnya adalah objek khusus `BatchEncoding`. Ini adalah turunan dari dictionary (itulah mengapa kita bisa mengaksesnya seperti dictionary), tetapi memiliki metode tambahan yang umumnya digunakan oleh tokenizer cepat.
+
+Selain kemampuannya untuk melakukan paralelisasi, fungsi utama dari tokenizer cepat adalah mereka **selalu melacak rentang asli teks** dari mana token akhir berasal — fitur ini disebut *offset mapping*. Ini memungkinkan kita melakukan hal-hal seperti memetakan setiap kata ke token yang dihasilkannya, atau memetakan setiap karakter dalam teks asli ke token tempat karakter itu berada, dan sebaliknya.
+
+Mari kita lihat contoh:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+encoding = tokenizer(example)
+print(type(encoding))
+```
+
+Seperti disebutkan sebelumnya, kita mendapatkan objek `BatchEncoding`:
+
+```python out
+
+```
+
+Karena `AutoTokenizer` secara default memilih tokenizer cepat, kita bisa menggunakan metode tambahan dari objek ini. Ada dua cara untuk mengecek apakah tokenizer kita adalah versi cepat atau lambat: kita bisa mengecek atribut `is_fast` dari `tokenizer`:
+
+```python
+tokenizer.is_fast
+```
+
+```python out
+True
+```
+
+atau dari objek `encoding`:
+
+```python
+encoding.is_fast
+```
+
+```python out
+True
+```
+
+Mari lihat apa yang memungkinkan dilakukan oleh tokenizer cepat. Pertama, kita bisa mengakses token tanpa perlu mengonversi ID kembali ke token:
+
+```py
+encoding.tokens()
+```
+
+```python out
+['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
+ 'Brooklyn', '.', '[SEP]']
+```
+
+Token dengan indeks ke-5 adalah `##yl`, bagian dari kata "Sylvain" dalam kalimat asli. Kita juga bisa menggunakan metode `word_ids()` untuk mendapatkan indeks kata asal dari setiap token:
+
+```py
+encoding.word_ids()
+```
+
+```python out
+[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None]
+```
+
+Kita bisa melihat bahwa token khusus milik tokenizer seperti `[CLS]` dan `[SEP]` dipetakan ke `None`, dan kemudian setiap token dipetakan ke kata asalnya. Ini sangat berguna untuk menentukan apakah suatu token berada di awal sebuah kata atau apakah dua token berasal dari kata yang sama. Kita memang bisa mengandalkan awalan `##` untuk itu, tetapi cara ini hanya berfungsi pada tokenizer seperti BERT; metode ini bisa digunakan pada jenis tokenizer apa pun selama merupakan tokenizer cepat. Pada bab selanjutnya, kita akan melihat bagaimana kita bisa menggunakan kemampuan ini untuk menerapkan label yang kita miliki untuk setiap kata secara tepat ke token-token dalam tugas seperti named entity recognition (NER) dan part-of-speech (POS) tagging. Kita juga bisa menggunakannya untuk memask semua token yang berasal dari kata yang sama dalam masked language modeling (teknik yang disebut _whole word masking_).
+
+
+
+Konsep “kata” bisa membingungkan. Misalnya, apakah “I'll” (gabungan dari "I will") dihitung sebagai satu atau dua kata? Ini tergantung tokenizer dan proses _pre-tokenization_ yang digunakan. Beberapa tokenizer hanya memisahkan berdasarkan spasi, sehingga akan menganggap ini sebagai satu kata. Lainnya menggunakan tanda baca juga, jadi akan memisahkannya menjadi dua kata.
+
+✏️ **Coba sendiri!** Buat tokenizer dari checkpoint `bert-base-cased` dan `roberta-base`, lalu tokenisasi kata "81s". Apa yang Anda amati? Bagaimana hasil dari `word_ids()`?
+
+
+
+Ada juga metode `sentence_ids()` untuk memetakan token ke kalimat asalnya (meskipun atribut `token_type_ids` juga menyediakan informasi yang serupa).
+
+Akhirnya, kita dapat memetakan token atau kata ke karakter dalam teks asli dan sebaliknya menggunakan metode `word_to_chars()`, `token_to_chars()`, `char_to_word()`, atau `char_to_token()`. Misalnya, `word_ids()` menunjukkan bahwa `##yl` adalah bagian dari kata ke-3. Tapi kata apa itu di kalimat?
+
+```py
+start, end = encoding.word_to_chars(3)
+example[start:end]
+```
+
+```python out
+Sylvain
+```
+
+Seperti disebutkan, semua ini dimungkinkan karena tokenizer cepat menyimpan informasi rentang teks asal setiap token dalam daftar *offset*. Selanjutnya, kita akan tunjukkan bagaimana mereplikasi hasil pipeline `token-classification` secara manual.
+
+
+
+✏️ **Coba sendiri!** Buat teks contoh Anda sendiri, dan coba pahami token apa yang dikaitkan dengan ID kata tertentu. Juga, coba ekstrak rentang karakter untuk sebuah kata. Bonus: coba gunakan dua kalimat dan lihat apakah ID kalimatnya masuk akal.
+
+
+
+## Di Balik Pipeline `token-classification`[[inside-the-token-classification-pipeline]]
+
+Dalam [Bab 1](/course/chapter1), kita telah mencoba menerapkan NER untuk pertama kalinya — yaitu tugas untuk mengidentifikasi bagian-bagian teks yang merupakan entitas seperti orang, lokasi, atau organisasi — menggunakan fungsi `pipeline()` dari 🤗 Transformers. Kemudian, di [Bab 2](/course/chapter2), kita melihat bahwa sebuah pipeline menggabungkan tiga tahap yang diperlukan untuk mendapatkan prediksi dari teks mentah: tokenisasi, menjalankan input melalui model, dan pasca-pemrosesan. Dua langkah pertama dalam pipeline `token-classification` sama seperti pipeline lainnya, tetapi proses pasca-pemrosesannya sedikit lebih kompleks — mari kita lihat bagaimana caranya!
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+### Mendapatkan Hasil Dasar dari Pipeline[[getting-the-base-results-with-the-pipeline]]
+
+Pertama, mari ambil pipeline `token-classification` agar kita bisa mendapatkan hasil untuk dibandingkan secara manual. Model default yang digunakan adalah [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english), yang melakukan NER pada kalimat:
+
+```py
+from transformers import pipeline
+
+token_classifier = pipeline("token-classification")
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Model berhasil mengidentifikasi setiap token dari kata "Sylvain" sebagai entitas orang, "Hugging Face" sebagai organisasi, dan "Brooklyn" sebagai lokasi. Kita juga bisa meminta pipeline untuk mengelompokkan token-token yang berasal dari entitas yang sama:
+
+```py
+from transformers import pipeline
+
+token_classifier = pipeline("token-classification", aggregation_strategy="simple")
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Strategi `aggregation_strategy` yang dipilih akan memengaruhi skor yang dihitung untuk setiap entitas yang dikelompokkan. Dengan `"simple"`, skornya hanyalah rata-rata dari skor setiap token dalam entitas tersebut: misalnya, skor untuk "Sylvain" adalah rata-rata dari skor yang kita lihat pada contoh sebelumnya untuk token `S`, `##yl`, `##va`, dan `##in`. Strategi lain yang tersedia adalah:
+
+- `"first"`: skor entitas diambil dari skor token pertama (untuk "Sylvain" = 0.993828)
+- `"max"`: skor entitas adalah skor token tertinggi (untuk "Hugging Face" = 0.98879766 dari "Face")
+- `"average"`: rata-rata skor dari kata yang membentuk entitas (untuk "Hugging Face", rata-rata skor dari "Hugging" dan "Face")
+
+Sekarang mari kita lihat bagaimana cara mendapatkan hasil ini **tanpa menggunakan fungsi `pipeline()`**!
+
+### Dari Input ke Prediksi[[from-inputs-to-predictions]]
+
+{#if fw === 'pt'}
+
+Pertama, kita perlu men-tokenisasi input kita dan melewatkannya ke dalam model. Ini dilakukan persis seperti di [Bab 2](/course/chapter2); kita menginstansiasi tokenizer dan model menggunakan kelas `AutoXxx`, lalu menggunakannya pada contoh:
+
+```py
+from transformers import AutoTokenizer, AutoModelForTokenClassification
+
+model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)
+
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+inputs = tokenizer(example, return_tensors="pt")
+outputs = model(**inputs)
+```
+
+Karena kita menggunakan `AutoModelForTokenClassification`, kita mendapatkan satu set _logits_ untuk setiap token dalam urutan input:
+
+```py
+print(inputs["input_ids"].shape)
+print(outputs.logits.shape)
+```
+
+```python out
+torch.Size([1, 19])
+torch.Size([1, 19, 9])
+```
+
+{:else}
+
+Pertama, kita perlu men-tokenisasi input kita dan melewatkannya ke dalam model. Ini dilakukan persis seperti di [Bab 2](/course/chapter2); kita menginstansiasi tokenizer dan model menggunakan kelas `TFAutoXxx`, lalu menggunakannya pada contoh:
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForTokenClassification
+
+model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)
+
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+inputs = tokenizer(example, return_tensors="tf")
+outputs = model(**inputs)
+```
+
+Karena kita menggunakan `TFAutoModelForTokenClassification`, kita mendapatkan satu set _logits_ untuk setiap token dalam urutan input:
+
+```py
+print(inputs["input_ids"].shape)
+print(outputs.logits.shape)
+```
+
+```python out
+(1, 19)
+(1, 19, 9)
+```
+
+{/if}
+
+Kita memiliki satu _batch_ yang terdiri dari 1 urutan berisi 19 token, dan model memiliki 9 label berbeda, sehingga output model memiliki bentuk (shape) 1 × 19 × 9. Seperti pada pipeline klasifikasi teks, kita menggunakan fungsi *softmax* untuk mengubah logits menjadi probabilitas, lalu menggunakan *argmax* untuk mendapatkan prediksi (karena urutan tidak berubah oleh *softmax*, kita bisa langsung mengambil argmax dari logits).
+
+{#if fw === 'pt'}
+
+```py
+import torch
+
+probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
+predictions = outputs.logits.argmax(dim=-1)[0].tolist()
+print(predictions)
+```
+
+{:else}
+
+```py
+import tensorflow as tf
+
+probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
+probabilities = probabilities.numpy().tolist()
+predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
+predictions = predictions.numpy().tolist()
+print(predictions)
+```
+
+{/if}
+
+```python out
+[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
+```
+
+Atribut `model.config.id2label` berisi pemetaan dari indeks ke label, yang bisa kita gunakan untuk memahami hasil prediksi:
+
+```py
+model.config.id2label
+```
+
+```python out
+{0: 'O',
+ 1: 'B-MISC',
+ 2: 'I-MISC',
+ 3: 'B-PER',
+ 4: 'I-PER',
+ 5: 'B-ORG',
+ 6: 'I-ORG',
+ 7: 'B-LOC',
+ 8: 'I-LOC'}
+```
+
+Seperti yang telah kita lihat sebelumnya, terdapat 9 label: `O` adalah label untuk token-token yang tidak termasuk dalam entitas apa pun (singkatan dari "outside"), dan kemudian kita memiliki dua label untuk setiap jenis entitas (miscellaneous, person, organization, dan location). Label `B-XXX` menunjukkan bahwa token berada di awal entitas `XXX`, sedangkan label `I-XXX` menunjukkan bahwa token berada di dalam entitas `XXX`. Sebagai contoh, dalam contoh saat ini kita mengharapkan model mengklasifikasikan token `S` sebagai `B-PER` (awal dari entitas person) dan token `##yl`, `##va`, serta `##in` sebagai `I-PER` (di dalam entitas person).
+
+Mungkin terlihat bahwa model salah karena memberikan `I-PER` ke semua empat token ini, tapi itu sebenarnya tidak sepenuhnya salah. Ada dua format untuk label `B-` dan `I-`: yaitu *IOB1* dan *IOB2*. Format IOB2 (ditunjukkan dengan warna merah muda di bawah) adalah yang kita perkenalkan, sedangkan format IOB1 (biru) menggunakan label `B-` hanya untuk memisahkan dua entitas yang berdampingan dari tipe yang sama. Model yang kita gunakan telah disetel ulang menggunakan dataset dengan format IOB1, itulah sebabnya token `S` diberi label `I-PER`.
+
+
+
+
+
+
+Dengan peta ini, kita siap untuk mereplikasi (hampir seluruhnya) hasil dari pipeline pertama — kita hanya perlu mengambil skor dan label dari setiap token yang tidak diklasifikasikan sebagai `O`:
+
+```py
+results = []
+tokens = inputs.tokens()
+
+for idx, pred in enumerate(predictions):
+ label = model.config.id2label[pred]
+ if label != "O":
+ results.append(
+ {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
+ )
+
+print(results)
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}]
+```
+
+Ini sangat mirip dengan hasil yang kita dapatkan sebelumnya, dengan satu pengecualian: pipeline juga memberikan informasi tentang posisi `start` dan `end` dari setiap entitas dalam kalimat asli. Di sinilah offset mapping akan digunakan. Untuk mendapatkan offset, kita cukup mengatur `return_offsets_mapping=True` saat menggunakan tokenizer:
+
+```py
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+inputs_with_offsets["offset_mapping"]
+```
+
+```python out
+[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
+ (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
+```
+
+Setiap pasangan tuple menunjukkan rentang teks yang sesuai untuk setiap token, di mana `(0, 0)` dicadangkan untuk token khusus. Kita telah melihat bahwa token pada indeks ke-5 adalah `##yl`, dan offset-nya adalah `(12, 14)`. Jika kita ambil potongan teks dari contoh:
+
+```py
+example[12:14]
+```
+
+kita mendapatkan rentang teks yang tepat tanpa `##`:
+
+```python out
+yl
+```
+
+Dengan ini, kita bisa menyempurnakan hasil sebelumnya:
+
+```py
+results = []
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+tokens = inputs_with_offsets.tokens()
+offsets = inputs_with_offsets["offset_mapping"]
+
+for idx, pred in enumerate(predictions):
+ label = model.config.id2label[pred]
+ if label != "O":
+ start, end = offsets[idx]
+ results.append(
+ {
+ "entity": label,
+ "score": probabilities[idx][pred],
+ "word": tokens[idx],
+ "start": start,
+ "end": end,
+ }
+ )
+
+print(results)
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Ini adalah hasil yang **sama persis** dengan pipeline pertama!
+
+### Mengelompokkan Entitas[[grouping-entities]]
+
+Menggunakan offset untuk menentukan posisi awal dan akhir setiap entitas memang berguna, tetapi informasi tersebut sebenarnya tidak sepenuhnya diperlukan. Namun, saat kita ingin mengelompokkan entitas, offset akan sangat membantu dan menghindarkan kita dari kode yang rumit. Misalnya, jika kita ingin menggabungkan token `Hu`, `##gging`, dan `Face`, kita bisa membuat aturan khusus yang menyatakan bahwa dua token pertama harus digabung dengan menghapus `##`, dan `Face` ditambahkan dengan spasi karena tidak diawali dengan `##` — tetapi aturan ini hanya akan bekerja untuk jenis tokenizer tertentu. Kita harus menulis seperangkat aturan lain untuk tokenizer seperti SentencePiece atau Byte-Pair Encoding (yang akan dibahas lebih lanjut di bab ini).
+
+Dengan menggunakan offset, semua kode khusus tersebut tidak lagi diperlukan: kita cukup mengambil rentang dalam teks asli yang dimulai dari token pertama dan berakhir pada token terakhir. Jadi, dalam kasus token `Hu`, `##gging`, dan `Face`, kita harus mulai dari karakter ke-33 (awal dari `Hu`) dan berakhir sebelum karakter ke-45 (akhir dari `Face`):
+
+```py
+example[33:45]
+```
+
+```python out
+Hugging Face
+```
+
+Untuk menulis kode yang melakukan pasca-pemrosesan terhadap prediksi sambil mengelompokkan entitas, kita akan mengelompokkan entitas yang berurutan dan dilabeli dengan `I-XXX`, kecuali entitas pertama yang bisa saja dilabeli sebagai `B-XXX` atau `I-XXX` (jadi, kita berhenti mengelompokkan suatu entitas ketika kita mendapatkan label `O`, jenis entitas yang baru, atau `B-XXX` yang menunjukkan bahwa entitas baru dari tipe yang sama telah dimulai):
+
+```py
+import numpy as np
+
+results = []
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+tokens = inputs_with_offsets.tokens()
+offsets = inputs_with_offsets["offset_mapping"]
+
+idx = 0
+while idx < len(predictions):
+ pred = predictions[idx]
+ label = model.config.id2label[pred]
+ if label != "O":
+ # Hapus awalan B- atau I-
+ label = label[2:]
+ start, _ = offsets[idx]
+
+ # Ambil semua token yang diberi label I
+ all_scores = []
+ while (
+ idx < len(predictions)
+ and model.config.id2label[predictions[idx]] == f"I-{label}"
+ ):
+ all_scores.append(probabilities[idx][pred])
+ _, end = offsets[idx]
+ idx += 1
+
+ # Skor adalah rata-rata skor dari semua token dalam entitas tersebut
+ score = np.mean(all_scores).item()
+ word = example[start:end]
+ results.append(
+ {
+ "entity_group": label,
+ "score": score,
+ "word": word,
+ "start": start,
+ "end": end,
+ }
+ )
+ idx += 1
+
+print(results)
+```
+
+Dan hasilnya akan sama seperti pipeline kedua:
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Contoh lain dari tugas yang sangat terbantu dengan offset adalah **question answering**. Pembahasan pipeline tersebut (yang akan kita lakukan di bagian selanjutnya) juga akan membawa kita pada satu fitur terakhir dari tokenizer di 🤗 Transformers: menangani **token yang meluap (overflow)** saat input terlalu panjang dan perlu dipotong (truncate).
diff --git a/chapters/id/chapter6/3b.mdx b/chapters/id/chapter6/3b.mdx
new file mode 100644
index 000000000..178421163
--- /dev/null
+++ b/chapters/id/chapter6/3b.mdx
@@ -0,0 +1,646 @@
+
+
+# Kekuatan Tokenizer Cepat dalam Pipeline QA[[fast-tokenizers-in-the-qa-pipeline]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Sekarang kita akan menyelami pipeline `question-answering` dan melihat bagaimana memanfaatkan offset untuk mengambil jawaban dari konteks, mirip seperti yang kita lakukan saat mengelompokkan entitas pada bagian sebelumnya. Setelah itu, kita juga akan melihat bagaimana menangani konteks yang sangat panjang hingga perlu dipotong. Anda bisa melewati bagian ini jika tidak tertarik dengan tugas question answering.
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+## Menggunakan Pipeline `question-answering`[[using-the-question-answering-pipeline]]
+
+Seperti yang kita lihat di [Bab 1](/course/chapter1), kita bisa menggunakan pipeline `question-answering` seperti ini untuk mendapatkan jawaban atas sebuah pertanyaan:
+
+```py
+from transformers import pipeline
+
+question_answerer = pipeline("question-answering")
+context = """
+🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration
+between them. It's straightforward to train your models with one before loading them for inference with the other.
+"""
+question = "Which deep learning libraries back 🤗 Transformers?"
+question_answerer(question=question, context=context)
+```
+
+```python out
+{'score': 0.97773,
+ 'start': 78,
+ 'end': 105,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+Berbeda dengan pipeline lain yang tidak bisa menangani input lebih panjang dari batas maksimum model (sehingga bisa melewatkan informasi penting di akhir dokumen), pipeline ini dapat menangani konteks yang sangat panjang dan tetap mengembalikan jawaban walaupun berada di bagian paling akhir:
+
+```py
+long_context = """
+🤗 Transformers: NLP Mutakhir
+
+🤗 Transformers menyediakan ribuan model pralatih untuk melakukan berbagai tugas pada teks seperti klasifikasi, ekstraksi informasi,
+penjawaban pertanyaan, peringkasan, terjemahan, pembuatan teks, dan lainnya dalam lebih dari 100 bahasa.
+Tujuannya adalah untuk mempermudah penggunaan NLP mutakhir bagi semua orang.
+
+🤗 Transformers menyediakan API untuk dengan cepat mengunduh dan menggunakan model-model pralatih tersebut pada teks tertentu,
+menyesuaikannya (fine-tune) dengan dataset milik Anda sendiri, dan kemudian membagikannya dengan komunitas di model hub kami.
+Pada saat yang sama, setiap modul Python yang mendefinisikan sebuah arsitektur bersifat mandiri sepenuhnya dan dapat dimodifikasi
+untuk mendukung eksperimen penelitian dengan cepat.
+
+Mengapa saya harus menggunakan Transformers?
+
+1. Model mutakhir yang mudah digunakan:
+ - Performa tinggi untuk tugas NLU dan NLG.
+ - Hambatan masuk yang rendah bagi pendidik dan praktisi.
+ - Abstraksi minimal dengan hanya tiga kelas yang perlu dipelajari.
+ - API terpadu untuk menggunakan semua model pralatih kami.
+ - Biaya komputasi lebih rendah, jejak karbon lebih kecil.
+
+2. Peneliti dapat membagikan model yang telah dilatih alih-alih selalu melatih dari awal.
+ - Praktisi dapat mengurangi waktu komputasi dan biaya produksi.
+ - Puluhan arsitektur dengan lebih dari 10.000 model pralatih, beberapa dalam lebih dari 100 bahasa.
+
+3. Pilih kerangka kerja yang tepat untuk setiap tahap siklus hidup model:
+ - Latih model mutakhir hanya dengan 3 baris kode.
+ - Pindahkan satu model antar kerangka kerja TF2.0/PyTorch secara fleksibel.
+ - Pilih kerangka kerja yang sesuai untuk pelatihan, evaluasi, dan produksi dengan mudah.
+
+4. Mudah menyesuaikan model atau contoh sesuai kebutuhan Anda:
+ - Kami menyediakan contoh untuk setiap arsitektur guna mereproduksi hasil yang diterbitkan oleh penulis aslinya.
+ - Internal model diekspos se-konsisten mungkin.
+ - File model dapat digunakan secara independen dari pustaka untuk eksperimen cepat.
+
+🤗 Transformers didukung oleh tiga pustaka pembelajaran mendalam paling populer — Jax, PyTorch, dan TensorFlow —
+dengan integrasi yang mulus di antara ketiganya.
+Sangat mudah untuk melatih model Anda dengan salah satunya, lalu memuatnya untuk inferensi dengan yang lain.
+"""
+question_answerer(question=question, context=long_context)
+```
+
+```python out
+{'score': 0.97149,
+ 'start': 1892,
+ 'end': 1919,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+Mari kita lihat bagaimana semua ini dilakukan!
+
+## Menggunakan Model untuk Question Answering[[using-a-model-for-question-answering]]
+
+Seperti pipeline lainnya, kita mulai dengan men-tokenisasi input dan mengirimkannya ke dalam model. Checkpoint default yang digunakan oleh pipeline `question-answering` adalah [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) (nama "squad" berasal dari dataset tempat model ini di-*fine-tune*, yang akan dibahas lebih lanjut di [Bab 7](/course/chapter7/7)).
+
+{#if fw === 'pt'}
+
+```py
+from transformers import AutoTokenizer, AutoModelForQuestionAnswering
+
+model_checkpoint = "distilbert-base-cased-distilled-squad"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+
+inputs = tokenizer(question, context, return_tensors="pt")
+outputs = model(**inputs)
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering
+
+model_checkpoint = "distilbert-base-cased-distilled-squad"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+
+inputs = tokenizer(question, context, return_tensors="tf")
+outputs = model(**inputs)
+```
+
+{/if}
+
+Perhatikan bahwa kita men-tokenisasi pertanyaan dan konteks sebagai sepasang input, dengan pertanyaan berada di awal.
+
+
+
+
+
+
+Model untuk question answering bekerja sedikit berbeda dari model yang kita lihat sebelumnya. Model ini dilatih untuk memprediksi indeks token tempat jawaban **dimulai** (misalnya 21) dan tempat jawaban **berakhir** (misalnya 24). Oleh karena itu, model ini mengembalikan dua tensor logits: satu untuk posisi awal, satu untuk posisi akhir. Misalnya, jika input memiliki 66 token:
+
+```py
+start_logits = outputs.start_logits
+end_logits = outputs.end_logits
+print(start_logits.shape, end_logits.shape)
+```
+
+{#if fw === 'pt'}
+
+```python out
+torch.Size([1, 66]) torch.Size([1, 66])
+```
+
+{:else}
+
+```python out
+(1, 66) (1, 66)
+```
+
+{/if}
+
+Untuk mengubah logit menjadi probabilitas, kita akan menerapkan fungsi softmax — tetapi sebelum itu, kita perlu memastikan bahwa kita melakukan masking terhadap indeks-indeks yang bukan bagian dari konteks. Input kita berbentuk `[CLS] question [SEP] context [SEP]`, jadi kita perlu melakukan masking terhadap token-token dari pertanyaan serta token `[SEP]`. Namun, kita akan tetap menyertakan token `[CLS]`, karena beberapa model menggunakannya untuk menunjukkan bahwa jawaban tidak terdapat dalam konteks.
+
+Karena kita akan menerapkan softmax setelahnya, kita cukup mengganti nilai logits pada posisi yang ingin dimask dengan angka negatif yang sangat besar, misalnya `-10000`.
+
+{#if fw === 'pt'}
+
+```py
+import torch
+
+sequence_ids = inputs.sequence_ids()
+# Mask semua kecuali token dari konteks
+mask = [i != 1 for i in sequence_ids]
+# Jangan mask token [CLS]
+mask[0] = False
+mask = torch.tensor(mask)[None]
+
+start_logits[mask] = -10000
+end_logits[mask] = -10000
+```
+
+{:else}
+
+```py
+import tensorflow as tf
+
+sequence_ids = inputs.sequence_ids()
+# Mask semua kecuali token dari konteks
+mask = [i != 1 for i in sequence_ids]
+# Jangan mask token [CLS]
+mask[0] = False
+mask = tf.constant(mask)[None]
+
+start_logits = tf.where(mask, -10000, start_logits)
+end_logits = tf.where(mask, -10000, end_logits)
+```
+
+{/if}
+
+Sekarang kita bisa menerapkan softmax:
+
+{#if fw === 'pt'}
+
+```py
+start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
+end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
+```
+
+{:else}
+
+```py
+start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
+end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy()
+```
+
+{/if}
+
+Pada tahap ini, kita sebenarnya bisa mengambil argmax dari probabilitas indeks awal dan akhir — tetapi kita mungkin saja mendapatkan indeks awal yang lebih besar dari indeks akhir, jadi kita perlu mengambil beberapa langkah tambahan. Kita akan menghitung probabilitas untuk setiap kemungkinan pasangan `start_index` dan `end_index` di mana `start_index <= end_index`, lalu memilih pasangan `(start_index, end_index)` dengan probabilitas tertinggi.
+
+Dengan mengasumsikan bahwa kejadian "Jawaban dimulai pada `start_index`" dan "Jawaban berakhir pada `end_index`" bersifat independen, maka probabilitas bahwa jawaban dimulai pada `start_index` dan berakhir pada `end_index` adalah:
+
+$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$
+
+Jadi, untuk menghitung semua skor, kita hanya perlu menghitung semua hasil perkalian \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) di mana `start_index <= end_index`.
+
+Pertama, mari kita hitung semua kemungkinan hasil perkaliannya:
+
+```py
+scores = start_probabilities[:, None] * end_probabilities[None, :]
+```
+
+{#if fw === 'pt'}
+
+Kemudian kita akan melakukan masking terhadap nilai-nilai di mana `start_index > end_index` dengan mengaturnya menjadi `0` (karena probabilitas lainnya semuanya bernilai positif). Fungsi `torch.triu()` akan mengembalikan bagian segitiga atas dari tensor 2D yang diberikan sebagai argumen, sehingga fungsi ini akan melakukan masking tersebut untuk kita:
+
+```py
+scores = torch.triu(scores)
+```
+
+{:else}
+
+Then we'll mask the values where `start_index > end_index` by setting them to `0` (the other probabilities are all positive numbers). The `np.triu()` function returns the upper triangular part of the 2D tensor passed as an argument, so it will do that masking for us:
+
+```py
+import numpy as np
+
+scores = np.triu(scores)
+```
+
+{/if}
+
+Sekarang kita hanya perlu mengambil indeks dengan nilai maksimum. Karena PyTorch akan mengembalikan indeks dalam bentuk tensor datar (flattened), kita perlu menggunakan operasi pembagian bulat `//` dan modulus `%` untuk mendapatkan `start_index` dan `end_index`:
+
+```py
+max_index = scores.argmax().item()
+start_index = max_index // scores.shape[1]
+end_index = max_index % scores.shape[1]
+print(scores[start_index, end_index])
+```
+
+Kita belum benar-benar selesai, tetapi setidaknya kita sudah mendapatkan skor yang benar untuk jawabannya (Anda bisa memverifikasinya dengan membandingkannya dengan hasil pertama di bagian sebelumnya):
+
+```python out
+0.97773
+```
+
+
+
+✏️ **Coba sendiri!** Hitung indeks `start` dan `end` untuk lima jawaban yang paling mungkin.
+
+
+
+Kita sudah memiliki `start_index` dan `end_index` jawaban dalam bentuk indeks token, sekarang kita hanya perlu mengubahnya ke indeks karakter dalam konteks. Di sinilah offset sangat berguna, seperti yang kita lakukan di tugas klasifikasi token:
+
+```py
+inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True)
+offsets = inputs_with_offsets["offset_mapping"]
+
+start_char, _ = offsets[start_index]
+_, end_char = offsets[end_index]
+answer = context[start_char:end_char]
+```
+
+Sekarang kita hanya perlu menyusun hasilnya:
+
+```py
+result = {
+ "answer": answer,
+ "start": start_char,
+ "end": end_char,
+ "score": scores[start_index, end_index],
+}
+print(result)
+```
+
+```python out
+{'answer': 'Jax, PyTorch and TensorFlow',
+ 'start': 78,
+ 'end': 105,
+ 'score': 0.97773}
+```
+
+Hebat! Ini adalah hasil yang sama dengan pipeline pertama kita!
+
+
+
+✏️ **Coba sendiri!** Gunakan skor terbaik yang sudah dihitung sebelumnya untuk menampilkan lima jawaban yang paling mungkin. Untuk membandingkan hasilnya, jalankan kembali pipeline pertama dan atur `top_k=5`.
+
+
+
+## Menangani K konteks panjang[[handling-long-contexts]]
+
+Jika kita mencoba men-tokenisasi pertanyaan dan konteks panjang yang digunakan sebelumnya, hasilnya akan melebihi panjang maksimum yang digunakan dalam pipeline `question-answering` (yaitu 384 token):
+
+```py
+inputs = tokenizer(question, long_context)
+print(len(inputs["input_ids"]))
+```
+
+```python out
+461
+```
+
+Jadi, kita perlu memangkas input pada panjang maksimum tersebut. Ada beberapa cara untuk melakukannya, tetapi kita **tidak ingin memotong pertanyaannya**, hanya konteksnya. Karena konteks adalah kalimat kedua, kita bisa menggunakan strategi pemangkasan `"only_second"`. Masalahnya: jawaban dari pertanyaan mungkin tidak ada dalam konteks yang sudah dipotong. Sebagai contoh, pertanyaan kita memiliki jawaban di akhir konteks. Ketika konteks tersebut dipotong, jawaban tidak lagi ada di dalamnya:
+
+```py
+inputs = tokenizer(question, long_context, max_length=384, truncation="only_second")
+print(tokenizer.decode(inputs["input_ids"]))
+```
+
+```python out
+"""
+[CLS] Pustaka deep learning apa yang mendukung [UNK] Transformers? [SEP] [UNK] Transformers: NLP Mutakhir
+
+[UNK] Transformers menyediakan ribuan model pralatih untuk melakukan tugas pada teks seperti klasifikasi, ekstraksi informasi,
+penjawaban pertanyaan, peringkasan, terjemahan, pembuatan teks, dan lainnya dalam lebih dari 100 bahasa.
+Tujuannya adalah untuk mempermudah penggunaan NLP mutakhir bagi semua orang.
+
+[UNK] Transformers menyediakan API untuk dengan cepat mengunduh dan menggunakan model-model pralatih tersebut pada teks tertentu,
+melakukan fine-tune dengan dataset milik Anda sendiri, dan kemudian membagikannya dengan komunitas di model hub kami.
+Pada saat yang sama, setiap modul Python yang mendefinisikan sebuah arsitektur bersifat mandiri sepenuhnya dan dapat dimodifikasi
+untuk mendukung eksperimen penelitian secara cepat.
+
+Mengapa saya harus menggunakan Transformers?
+
+1. Model mutakhir yang mudah digunakan:
+ - Performa tinggi untuk tugas NLU dan NLG.
+ - Hambatan masuk yang rendah bagi pendidik dan praktisi.
+ - Hanya sedikit abstraksi yang perlu dipahami oleh pengguna, cukup dengan mempelajari tiga kelas.
+ - API terpadu untuk menggunakan semua model pralatih kami.
+ - Biaya komputasi lebih rendah, jejak karbon lebih kecil.
+
+2. Peneliti dapat membagikan model yang telah dilatih alih-alih selalu melatih ulang dari awal.
+ - Praktisi dapat mengurangi waktu komputasi dan biaya produksi.
+ - Puluhan arsitektur dengan lebih dari 10.000 model pralatih, beberapa tersedia dalam lebih dari 100 bahasa.
+
+3. Pilih kerangka kerja yang tepat untuk setiap tahap siklus hidup model:
+ - Latih model mutakhir hanya dengan 3 baris kode.
+ - Pindahkan satu model antar kerangka kerja TF2.0/PyTorch secara fleksibel.
+ - Pilih kerangka kerja yang sesuai untuk pelatihan, evaluasi, dan produksi dengan mulus.
+
+4. Mudah menyesuaikan model atau contoh sesuai kebutuhan Anda:
+ - Kami menyediakan contoh untuk setiap arsitektur guna mereproduksi hasil yang diterbitkan oleh penulis aslinya.
+ - Internal model [SEP]
+"""
+```
+
+Ini berarti model akan kesulitan dalam memilih jawaban yang benar. Untuk mengatasi hal ini, pipeline `question-answering` memungkinkan kita untuk membagi konteks menjadi potongan-potongan yang lebih kecil, dengan menentukan panjang maksimum. Agar kita tidak membagi konteks tepat di tempat yang salah yang membuat jawaban menjadi tidak dapat ditemukan, pipeline ini juga menyertakan sedikit tumpang tindih antar potongan.
+
+Kita bisa meminta tokenizer (baik yang cepat maupun yang lambat) untuk melakukan ini dengan menambahkan `return_overflowing_tokens=True`, dan kita bisa menentukan jumlah tumpang tindih yang diinginkan dengan argumen `stride`. Berikut adalah contohnya, menggunakan kalimat yang lebih pendek:
+
+
+```py
+sentence = "This sentence is not too long but we are going to split it anyway."
+inputs = tokenizer(
+ sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
+)
+
+for ids in inputs["input_ids"]:
+ print(tokenizer.decode(ids))
+```
+
+```python out
+'[CLS] This sentence is not [SEP]'
+'[CLS] is not too long [SEP]'
+'[CLS] too long but we [SEP]'
+'[CLS] but we are going [SEP]'
+'[CLS] are going to split [SEP]'
+'[CLS] to split it anyway [SEP]'
+'[CLS] it anyway. [SEP]'
+```
+
+Seperti yang bisa kita lihat, kalimat telah dibagi menjadi beberapa potongan sedemikian rupa sehingga setiap entri dalam `inputs["input_ids"]` memiliki maksimal 6 token (kita perlu menambahkan padding agar entri terakhir memiliki ukuran yang sama dengan yang lainnya), dan terdapat tumpang tindih sebanyak 2 token antara setiap entri.
+
+Mari kita lihat lebih dalam hasil tokenisasi:
+
+```py
+print(inputs.keys())
+```
+
+```python out
+dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
+```
+
+Seperti yang diharapkan, kita mendapatkan `input_ids` dan `attention_mask`. Kunci terakhir, `overflow_to_sample_mapping`, adalah peta yang menunjukkan **dari kalimat ke berapa** setiap hasil berasal — dalam kasus ini, ketujuh hasil berasal dari satu kalimat:
+
+```py
+print(inputs["overflow_to_sample_mapping"])
+```
+
+```python out
+[0, 0, 0, 0, 0, 0, 0]
+```
+
+Ini akan menjadi lebih berguna ketika kita men-tokenisasi **beberapa** kalimat sekaligus. Misalnya, pada kasus berikut:
+
+```py
+sentences = [
+ "This sentence is not too long but we are going to split it anyway.",
+ "This sentence is shorter but will still get split.",
+]
+inputs = tokenizer(
+ sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
+)
+
+print(inputs["overflow_to_sample_mapping"])
+```
+
+Hasilnya:
+
+```python out
+[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
+```
+
+Artinya, kalimat pertama dipecah menjadi 7 bagian seperti sebelumnya, dan 4 bagian selanjutnya berasal dari kalimat kedua.
+
+Sekarang mari kita kembali ke konteks panjang kita. Secara default, pipeline `question-answering` menggunakan panjang maksimum 384 token dan `stride` sebesar 128 — sesuai dengan cara model di-*fine-tune*. (Parameter ini bisa disesuaikan lewat `max_seq_len` dan `stride` saat memanggil pipeline). Maka, kita akan menggunakan parameter tersebut saat melakukan tokenisasi. Kita juga akan menambahkan padding agar semua sampel memiliki panjang yang sama, dan meminta offset:
+
+```py
+inputs = tokenizer(
+ question,
+ long_context,
+ stride=128,
+ max_length=384,
+ padding="longest",
+ truncation="only_second",
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+)
+```
+
+`inputs` tersebut akan berisi input ID dan attention mask yang dibutuhkan oleh model, serta offset dan `overflow_to_sample_mapping` yang baru saja kita bahas. Karena dua elemen terakhir tersebut bukan parameter yang digunakan oleh model, kita akan menghapusnya dari `inputs` (dan kita tidak akan menyimpan mapping-nya, karena tidak berguna di sini) sebelum mengonversinya menjadi tensor:
+
+{#if fw === 'pt'}
+
+```py
+_ = inputs.pop("overflow_to_sample_mapping")
+offsets = inputs.pop("offset_mapping")
+
+inputs = inputs.convert_to_tensors("pt")
+print(inputs["input_ids"].shape)
+```
+
+```python out
+torch.Size([2, 384])
+```
+
+{:else}
+
+```py
+_ = inputs.pop("overflow_to_sample_mapping")
+offsets = inputs.pop("offset_mapping")
+
+inputs = inputs.convert_to_tensors("tf")
+print(inputs["input_ids"].shape)
+```
+
+```python out
+(2, 384)
+```
+
+{/if}
+
+Konteks panjang kita dipecah menjadi 2 bagian, artinya setelah melalui model, kita akan memiliki dua set `start_logits` dan `end_logits`:
+
+```py
+outputs = model(**inputs)
+
+start_logits = outputs.start_logits
+end_logits = outputs.end_logits
+print(start_logits.shape, end_logits.shape)
+```
+
+{#if fw === 'pt'}
+
+```python out
+torch.Size([2, 384]) torch.Size([2, 384])
+```
+
+{:else}
+
+```python out
+(2, 384) (2, 384)
+```
+
+{/if}
+
+Seperti sebelumnya, kita mask token yang bukan bagian dari konteks **dan** token padding sebelum menerapkan softmax:
+
+{#if fw === 'pt'}
+
+```py
+sequence_ids = inputs.sequence_ids()
+# Masking semua token kecuali token dari konteks
+mask = [i != 1 for i in sequence_ids]
+# Jangan masking token [CLS]
+mask[0] = False
+# Masking semua token [PAD]
+mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))
+
+start_logits[mask] = -10000
+end_logits[mask] = -10000
+```
+
+{:else}
+
+```py
+sequence_ids = inputs.sequence_ids()
+# Masking semua token kecuali token dari konteks
+mask = [i != 1 for i in sequence_ids]
+# Jangan masking token [CLS]
+mask[0] = False
+# Masking semua token [PAD]
+mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)
+
+start_logits = tf.where(mask, -10000, start_logits)
+end_logits = tf.where(mask, -10000, end_logits)
+```
+
+{/if}
+
+Kemudian kita ubah `logits` menjadi probabilitas:
+
+{#if fw === 'pt'}
+
+```py
+start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
+end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
+```
+
+{:else}
+
+```py
+start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
+end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy()
+```
+
+{/if}
+
+Langkah berikutnya mirip seperti pada konteks pendek, tapi diulang untuk setiap bagian. Kita hitung skor untuk semua pasangan `(start, end)` dan pilih span dengan skor terbaik:
+
+{#if fw === 'pt'}
+
+```py
+candidates = []
+for start_probs, end_probs in zip(start_probabilities, end_probabilities):
+ scores = start_probs[:, None] * end_probs[None, :]
+ idx = torch.triu(scores).argmax().item()
+
+ start_idx = idx // scores.shape[1]
+ end_idx = idx % scores.shape[1]
+ score = scores[start_idx, end_idx].item()
+ candidates.append((start_idx, end_idx, score))
+
+print(candidates)
+```
+
+{:else}
+
+```py
+candidates = []
+for start_probs, end_probs in zip(start_probabilities, end_probabilities):
+ scores = start_probs[:, None] * end_probs[None, :]
+ idx = np.triu(scores).argmax().item()
+
+ start_idx = idx // scores.shape[1]
+ end_idx = idx % scores.shape[1]
+ score = scores[start_idx, end_idx].item()
+ candidates.append((start_idx, end_idx, score))
+
+print(candidates)
+```
+
+{/if}
+
+```python out
+[(0, 18, 0.33867), (173, 184, 0.97149)]
+```
+
+Kedua kandidat tersebut merupakan jawaban terbaik yang berhasil ditemukan model di masing-masing potongan konteks. Model jauh lebih yakin bahwa jawaban yang benar ada di bagian kedua (dan itu pertanda baik!). Sekarang kita hanya perlu memetakan kedua rentang token tersebut ke rentang karakter dalam konteks (kita sebenarnya hanya perlu memetakan bagian kedua untuk mendapatkan jawabannya, tetapi menarik juga untuk melihat apa yang dipilih model pada potongan pertama).
+
+
+
+✏️ **Coba sendiri!** Sesuaikan kode di atas untuk mengembalikan 5 kandidat jawaban paling mungkin dari seluruh konteks (bukan per bagian).
+
+
+
+Objek `offsets` adalah daftar offset, satu untuk setiap bagian konteks:
+
+```py
+for candidate, offset in zip(candidates, offsets):
+ start_token, end_token, score = candidate
+ start_char, _ = offset[start_token]
+ _, end_char = offset[end_token]
+ answer = long_context[start_char:end_char]
+ result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
+ print(result)
+```
+
+```python out
+{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
+{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149}
+```
+
+Jika kita abaikan hasil pertama, kita mendapatkan jawaban **yang sama persis** dengan pipeline untuk konteks panjang ini — keren!
+
+
+
+✏️ **Coba sendiri!** Gunakan skor terbaik yang sudah dihitung sebelumnya untuk menampilkan lima jawaban paling mungkin dari seluruh konteks. Untuk membandingkan hasil, gunakan pipeline dengan `top_k=5`.
+
+
+
+Dengan ini, kita selesai membahas secara mendalam kemampuan tokenizer. Semua yang telah Anda pelajari di bab ini akan kita praktikkan lagi di bab berikutnya, ketika kita menunjukkan cara *fine-tuning* model untuk berbagai tugas NLP umum.
diff --git a/chapters/id/chapter6/4.mdx b/chapters/id/chapter6/4.mdx
new file mode 100644
index 000000000..f563a4db7
--- /dev/null
+++ b/chapters/id/chapter6/4.mdx
@@ -0,0 +1,122 @@
+# Normalisasi dan Pra-tokenisasi[[normalization-and-pre-tokenization]]
+
+
+
+Sebelum kita menyelami lebih dalam tiga algoritma tokenisasi subkata yang paling umum digunakan pada model Transformer (Byte-Pair Encoding [BPE], WordPiece, dan Unigram), kita akan terlebih dahulu melihat tahap praproses yang dilakukan oleh setiap tokenizer terhadap teks. Berikut gambaran umum tingkat tinggi tentang langkah-langkah dalam pipeline tokenisasi:
+
+
+
+
+
+
+Sebelum membagi teks menjadi subtoken (sesuai dengan modelnya), tokenizer menjalankan dua langkah: _normalisasi_ dan _pra-tokenisasi_.
+
+## Normalisasi[[normalization]]
+
+
+
+Langkah normalisasi mencakup pembersihan umum, seperti menghapus spasi yang tidak perlu, mengubah huruf menjadi huruf kecil (lowercasing), dan/atau menghapus aksen. Jika kamu familiar dengan [normalisasi Unicode](http://www.unicode.org/reports/tr15/) (seperti NFC atau NFKC), tokenizer mungkin juga menerapkannya.
+
+Tokenizer dari 🤗 Transformers memiliki atribut `backend_tokenizer` yang memberikan akses ke tokenizer dasar dari pustaka 🤗 Tokenizers:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
+print(type(tokenizer.backend_tokenizer))
+```
+
+```python out
+
+```
+
+Atribut `normalizer` dari objek `tokenizer` memiliki metode `normalize_str()` yang bisa kita gunakan untuk melihat bagaimana proses normalisasi dilakukan:
+
+```py
+print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
+```
+
+```python out
+'hello how are u?'
+```
+
+Dalam contoh ini, karena kita menggunakan checkpoint `bert-base-uncased`, proses normalisasi mencakup perubahan huruf ke huruf kecil dan penghapusan aksen.
+
+
+
+✏️ **Coba sendiri!** Muat tokenizer dari checkpoint `bert-base-cased` dan berikan contoh yang sama. Apa perbedaan utama yang kamu lihat antara versi `cased` dan `uncased` dari tokenizer?
+
+
+
+## Pra-tokenisasi[[pre-tokenization]]
+
+
+
+Seperti yang akan kita lihat pada bagian berikutnya, tokenizer tidak bisa langsung dilatih pada teks mentah. Kita perlu membagi teks terlebih dahulu menjadi entitas kecil seperti kata. Di sinilah langkah pra-tokenisasi berperan. Seperti yang telah dibahas di [Bab 2](/course/chapter2), tokenizer berbasis kata dapat membagi teks menjadi kata berdasarkan spasi dan tanda baca. Kata-kata ini akan menjadi batasan bagi subtoken yang akan dipelajari tokenizer saat pelatihan.
+
+Untuk melihat bagaimana tokenizer cepat (fast tokenizer) melakukan pra-tokenisasi, kita bisa menggunakan metode `pre_tokenize_str()` dari atribut `pre_tokenizer`:
+
+```py
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+```python out
+[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]
+```
+
+Perhatikan bahwa tokenizer sudah mencatat offset, yang memungkinkan kita mendapatkan pemetaan offset seperti yang digunakan pada bagian sebelumnya. Di sini tokenizer mengabaikan dua spasi dan menggantinya dengan satu, tetapi offset tetap mencerminkan jarak sebenarnya.
+
+Karena kita menggunakan tokenizer BERT, proses pra-tokenisasi mencakup pemisahan berdasarkan spasi dan tanda baca. Tokenizer lain dapat memiliki aturan berbeda. Misalnya, jika kita menggunakan tokenizer GPT-2:
+
+```py
+tokenizer = AutoTokenizer.from_pretrained("gpt2")
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+Tokenizer ini juga membagi berdasarkan spasi dan tanda baca, tetapi mempertahankan spasi dan menggantinya dengan simbol `Ġ`, memungkinkan pemulihan spasi asli saat mendekode token:
+
+```python out
+[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
+ ('?', (19, 20))]
+```
+
+Juga perhatikan bahwa tidak seperti tokenizer BERT, tokenizer ini tidak mengabaikan spasi ganda.
+
+Sebagai contoh terakhir, mari kita lihat tokenizer T5 yang berbasis pada algoritma SentencePiece:
+
+```py
+tokenizer = AutoTokenizer.from_pretrained("t5-small")
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+```python out
+[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]
+```
+
+Seperti tokenizer GPT-2, tokenizer ini mempertahankan spasi dan menggantinya dengan token khusus (`▁`), tetapi tokenizer T5 hanya membagi berdasarkan spasi, bukan tanda baca. Ia juga menambahkan spasi di awal kalimat (sebelum `Hello`) dan mengabaikan spasi ganda.
+
+Setelah melihat bagaimana beberapa tokenizer memproses teks, kita bisa mulai menjelajahi algoritma dasarnya. Kita akan mulai dengan meninjau sekilas SentencePiece; kemudian, dalam tiga bagian berikutnya, kita akan mempelajari bagaimana tiga algoritma utama tokenisasi subkata bekerja.
+
+## SentencePiece[[sentencepiece]]
+
+[SentencePiece](https://github.com/google/sentencepiece) adalah algoritma tokenisasi untuk praproses teks yang dapat digunakan dengan model apa pun yang akan kita bahas. Algoritma ini memperlakukan teks sebagai rangkaian karakter Unicode, dan mengganti spasi dengan karakter khusus, `▁`. Digunakan bersama algoritma Unigram (lihat [bagian 7](/course/chapter6/7)), algoritma ini bahkan tidak memerlukan langkah pra-tokenisasi, yang sangat berguna untuk bahasa yang tidak menggunakan spasi seperti Bahasa Tionghoa atau Jepang.
+
+Fitur utama lain dari SentencePiece adalah *tokenisasi yang dapat dibalik* (reversible tokenization): karena tidak ada perlakuan khusus untuk spasi, proses decoding token cukup dilakukan dengan menggabungkan kembali token dan mengganti `▁` dengan spasi — ini akan menghasilkan teks yang telah dinormalisasi. Seperti yang sudah kita lihat, tokenizer BERT menghapus spasi ganda, sehingga tokenisasi miliknya tidak dapat dibalik.
+
+## Tinjauan Algoritma[[algorithm-overview]]
+
+Pada bagian berikutnya, kita akan membahas tiga algoritma utama tokenisasi subkata: BPE (digunakan oleh GPT-2 dan lainnya), WordPiece (misalnya digunakan oleh BERT), dan Unigram (digunakan oleh T5 dan lainnya). Sebelum mulai, berikut adalah ringkasan cara kerja masing-masing. Jangan ragu untuk kembali ke tabel ini setelah membaca setiap bagian jika masih belum sepenuhnya memahami.
+
+| Model | BPE | WordPiece | Unigram |
+|-----------|------------------------------------------------------|---------------------------------------------------------|------------------------------------------------------------------------|
+| Pelatihan | Mulai dari kosakata kecil dan mempelajari aturan penggabungan token | Mulai dari kosakata kecil dan mempelajari aturan penggabungan token | Mulai dari kosakata besar dan mempelajari aturan penghapusan token |
+| Langkah pelatihan | Menggabungkan pasangan token paling sering muncul | Menggabungkan pasangan dengan skor terbaik berdasarkan frekuensi, mengutamakan pasangan dengan token individual yang lebih jarang | Menghapus semua token dalam kosakata untuk meminimalkan loss pada seluruh korpus |
+| Yang dipelajari | Aturan penggabungan dan kosakata | Hanya kosakata | Kosakata dengan skor untuk tiap token |
+| Proses encoding | Membagi kata menjadi karakter, lalu menggabungkan sesuai aturan | Mencari subkata terpanjang dari awal yang ada di kosakata, lalu lanjutkan | Mencari pembagian token paling mungkin berdasarkan skor pelatihan |
+
+Sekarang, mari kita mulai dengan BPE!
diff --git a/chapters/id/chapter6/5.mdx b/chapters/id/chapter6/5.mdx
new file mode 100644
index 000000000..b4e6cb0d4
--- /dev/null
+++ b/chapters/id/chapter6/5.mdx
@@ -0,0 +1,360 @@
+# Tokenisasi Byte-Pair Encoding[[byte-pair-encoding-tokenization]]
+
+
+
+Byte-Pair Encoding (BPE) awalnya dikembangkan sebagai algoritma untuk mengompresi teks, dan kemudian digunakan oleh OpenAI untuk tokenisasi saat pra-pelatihan model GPT. BPE digunakan oleh banyak model Transformer, termasuk GPT, GPT-2, RoBERTa, BART, dan DeBERTa.
+
+
+
+
+
+💡 Bagian ini membahas BPE secara mendalam, bahkan hingga menunjukkan implementasi lengkapnya. Kamu bisa langsung lompat ke akhir jika hanya ingin gambaran umum dari algoritma tokenisasi ini.
+
+
+
+## Algoritma Pelatihan[[training-algorithm]]
+
+Pelatihan BPE dimulai dengan menghitung kumpulan kata unik yang digunakan dalam korpus (setelah langkah normalisasi dan pra-tokenisasi selesai), lalu membangun kosakata dengan mengambil semua simbol yang digunakan untuk menulis kata-kata tersebut. Sebagai contoh yang sangat sederhana, misalkan korpus kita hanya menggunakan lima kata ini:
+
+```
+"hug", "pug", "pun", "bun", "hugs"
+```
+
+Kosakata dasarnya akan menjadi `["b", "g", "h", "n", "p", "s", "u"]`. Dalam kasus nyata, kosakata dasar tersebut akan mencakup semua karakter ASCII, dan mungkin juga beberapa karakter Unicode. Jika sebuah contoh yang kamu tokenisasi menggunakan karakter yang tidak ada di korpus pelatihan, karakter itu akan dikonversi menjadi token tidak dikenal (`[UNK]`). Ini adalah salah satu alasan mengapa banyak model NLP kurang baik dalam menganalisis konten dengan emoji, misalnya.
+
+
+
+Tokenizer GPT-2 dan RoBERTa (yang cukup mirip) memiliki cara cerdas untuk mengatasi hal ini: mereka tidak menganggap kata ditulis dengan karakter Unicode, melainkan dengan *byte*. Dengan begitu, ukuran kosakata dasar tetap kecil (256), namun semua karakter tetap tercakup dan tidak diubah menjadi token `[UNK]`. Trik ini disebut *byte-level BPE*.
+
+
+
+Setelah mendapatkan kosakata dasar, kita menambahkan token baru hingga ukuran kosakata yang diinginkan tercapai, dengan mempelajari *penggabungan* (merge), yaitu aturan untuk menggabungkan dua elemen dalam kosakata yang ada menjadi elemen baru. Pada awalnya, penggabungan ini akan membuat token dua karakter, dan seiring pelatihan berjalan, token yang lebih panjang.
+
+Pada setiap langkah pelatihan tokenizer, algoritma BPE akan mencari pasangan token yang paling sering muncul (pasangan berarti dua token yang bersebelahan dalam suatu kata). Pasangan yang paling sering inilah yang akan digabungkan, dan proses diulang kembali pada langkah selanjutnya.
+
+Kembali ke contoh sebelumnya, misalnya kata-kata tersebut memiliki frekuensi sebagai berikut:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+Artinya kata `"hug"` muncul 10 kali dalam korpus, `"pug"` 5 kali, `"pun"` 12 kali, `"bun"` 4 kali, dan `"hugs"` 5 kali. Kita mulai pelatihan dengan membagi setiap kata menjadi karakter individual (yang membentuk kosakata dasar), jadi kita bisa melihat setiap kata sebagai daftar token:
+
+```
+("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
+```
+
+Kemudian kita melihat pasangan token. Pasangan `("h", "u")` muncul dalam kata `"hug"` dan `"hugs"`, jadi total 15 kali dalam korpus. Namun ini bukan pasangan yang paling sering. Yang paling sering adalah `("u", "g")`, yang muncul dalam `"hug"`, `"pug"`, dan `"hugs"`, dengan total 20 kali.
+
+Maka, aturan penggabungan pertama yang dipelajari tokenizer adalah `("u", "g") -> "ug"`, artinya `"ug"` akan ditambahkan ke kosakata dan pasangan tersebut digabung dalam semua kata di korpus. Setelah tahap ini, kosakata dan korpus akan terlihat seperti ini:
+
+```
+Kosakata: ["b", "g", "h", "n", "p", "s", "u", "ug"]
+Korpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
+```
+
+Sekarang kita memiliki pasangan yang lebih panjang dari dua karakter: seperti pasangan `("h", "ug")`, yang muncul 15 kali. Namun pasangan yang paling sering saat ini adalah `("u", "n")`, yang muncul 16 kali. Jadi aturan penggabungan kedua adalah `("u", "n") -> "un"`. Setelah menambahkan itu ke kosakata dan menerapkannya di semua tempat, kita dapatkan:
+
+```
+Kosakata: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
+Korpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
+```
+
+Sekarang pasangan paling sering adalah `("h", "ug")`, jadi kita pelajari aturan `("h", "ug") -> "hug"`, yang menjadi token tiga karakter pertama kita. Setelah penggabungan, korpus menjadi:
+
+```
+Kosakata: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
+Korpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
+```
+
+Dan proses ini terus diulang hingga kita mencapai ukuran kosakata yang diinginkan.
+
+
+
+✏️ **Sekarang giliranmu!** Menurutmu, apa aturan penggabungan berikutnya?
+
+
+
+## Algoritma Tokenisasi[[tokenization-algorithm]]
+
+Proses tokenisasi mengikuti pelatihan secara langsung, artinya input baru akan ditokenisasi dengan langkah-langkah berikut:
+
+1. Normalisasi
+2. Pra-tokenisasi
+3. Membagi kata menjadi karakter individual
+4. Menerapkan aturan penggabungan yang telah dipelajari, secara berurutan
+
+Mari kita ambil contoh yang digunakan saat pelatihan, dengan tiga aturan penggabungan yang telah dipelajari:
+
+```
+("u", "g") -> "ug"
+("u", "n") -> "un"
+("h", "ug") -> "hug"
+```
+
+Kata `"bug"` akan di-tokenisasi menjadi `["b", "ug"]`. Namun, `"mug"` akan di-tokenisasi menjadi `["[UNK]", "ug"]` karena huruf `"m"` tidak ada dalam kosakata dasar. Demikian pula, kata `"thug"` akan di-tokenisasi menjadi `["[UNK]", "hug"]`: huruf `"t"` tidak ada dalam kosakata dasar, dan penerapan aturan penggabungan menghasilkan penggabungan `"u"` dan `"g"` terlebih dahulu, kemudian `"h"` dan `"ug"` digabungkan.
+
+
+
+✏️ **Sekarang giliranmu!** Menurutmu, bagaimana kata `"unhug"` akan ditokenisasi?
+
+
+
+## Implementasi BPE[[implementing-bpe]]
+
+Sekarang mari kita lihat bagaimana implementasi algoritma BPE. Ini bukan versi yang dioptimalkan untuk digunakan pada korpus besar; tujuannya hanya untuk membantu kamu memahami algoritmanya lebih baik.
+
+Pertama kita butuh korpus. Mari kita buat yang sederhana dengan beberapa kalimat:
+
+```python
+corpus = [
+ "This is the Hugging Face Course.",
+ "This chapter is about tokenization.",
+ "This section shows several tokenizer algorithms.",
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+]
+```
+
+Selanjutnya, kita perlu melakukan pra-tokenisasi pada korpus tersebut menjadi kata-kata. Karena kita meniru tokenizer BPE (seperti GPT-2), kita akan menggunakan tokenizer `gpt2` untuk pra-tokenisasi:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("gpt2")
+```
+
+Kemudian kita hitung frekuensi setiap kata dalam korpus saat melakukan pra-tokenisasi:
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+print(word_freqs)
+```
+
+```python out
+defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
+ 'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
+ 'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
+ 'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
+```
+
+Langkah berikutnya adalah menghitung kosakata dasar, yaitu semua karakter yang digunakan dalam korpus:
+
+```python
+alphabet = []
+
+for word in word_freqs.keys():
+ for letter in word:
+ if letter not in alphabet:
+ alphabet.append(letter)
+alphabet.sort()
+
+print(alphabet)
+```
+
+```python out
+[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
+ 't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
+```
+
+Kita juga menambahkan token spesial yang digunakan oleh model di awal kosakata tersebut. Dalam kasus GPT-2, hanya ada satu token spesial, yaitu `"<|endoftext|>"`:
+
+```python
+vocab = ["<|endoftext|>"] + alphabet.copy()
+```
+
+Sekarang kita perlu membagi setiap kata menjadi karakter individu untuk memulai pelatihan:
+
+```python
+splits = {word: [c for c in word] for word in word_freqs.keys()}
+```
+
+Setelah semuanya siap untuk pelatihan, mari kita tulis fungsi untuk menghitung frekuensi setiap pasangan karakter. Fungsi ini akan digunakan pada setiap langkah pelatihan:
+
+```python
+def compute_pair_freqs(splits):
+ pair_freqs = defaultdict(int)
+ for word, freq in word_freqs.items():
+ split = splits[word]
+ if len(split) == 1:
+ continue
+ for i in range(len(split) - 1):
+ pair = (split[i], split[i + 1])
+ pair_freqs[pair] += freq
+ return pair_freqs
+```
+
+Mari kita lihat sebagian dari kamus pasangan ini setelah pembagian awal:
+
+```python
+pair_freqs = compute_pair_freqs(splits)
+
+for i, key in enumerate(pair_freqs.keys()):
+ print(f"{key}: {pair_freqs[key]}")
+ if i >= 5:
+ break
+```
+
+```python out
+('T', 'h'): 3
+('h', 'i'): 3
+('i', 's'): 5
+('Ġ', 'i'): 2
+('Ġ', 't'): 7
+('t', 'h'): 3
+```
+
+Menemukan pasangan paling sering hanya memerlukan loop cepat:
+
+```python
+best_pair = ""
+max_freq = None
+
+for pair, freq in pair_freqs.items():
+ if max_freq is None or max_freq < freq:
+ best_pair = pair
+ max_freq = freq
+
+print(best_pair, max_freq)
+```
+
+```python out
+('Ġ', 't') 7
+```
+
+Jadi penggabungan pertama yang dipelajari adalah `('Ġ', 't') -> 'Ġt'`, dan kita tambahkan `'Ġt'` ke kosakata:
+
+```python
+merges = {("Ġ", "t"): "Ġt"}
+vocab.append("Ġt")
+```
+
+Untuk melanjutkan, kita perlu menerapkan penggabungan itu pada dictionary `splits`. Berikut fungsi untuk itu:
+
+```python
+def merge_pair(a, b, splits):
+ for word in word_freqs:
+ split = splits[word]
+ if len(split) == 1:
+ continue
+
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == a and split[i + 1] == b:
+ split = split[:i] + [a + b] + split[i + 2 :]
+ else:
+ i += 1
+ splits[word] = split
+ return splits
+```
+
+Mari kita lihat hasil dari penggabungan pertama:
+
+```python
+splits = merge_pair("Ġ", "t", splits)
+print(splits["Ġtrained"])
+```
+
+```python out
+['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
+```
+
+Sekarang kita punya semua yang dibutuhkan untuk melakukan loop hingga kita mempelajari semua aturan penggabungan yang diinginkan. Misalnya kita targetkan ukuran kosakata 50:
+
+```python
+vocab_size = 50
+
+while len(vocab) < vocab_size:
+ pair_freqs = compute_pair_freqs(splits)
+ best_pair = ""
+ max_freq = None
+ for pair, freq in pair_freqs.items():
+ if max_freq is None or max_freq < freq:
+ best_pair = pair
+ max_freq = freq
+ splits = merge_pair(*best_pair, splits)
+ merges[best_pair] = best_pair[0] + best_pair[1]
+ vocab.append(best_pair[0] + best_pair[1])
+```
+
+Sebagai hasilnya, kita telah mempelajari 19 aturan penggabungan (kosakata awal memiliki ukuran 31 — 30 karakter alfabet ditambah satu token spesial):
+
+```python
+print(merges)
+```
+
+```python out
+{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
+ ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
+ ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
+ ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
+```
+
+Dan kosakatanya terdiri dari token spesial, alfabet awal, dan semua hasil penggabungan:
+
+```python
+print(vocab)
+```
+
+```python out
+['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
+ 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
+```
+
+
+
+💡 Menggunakan `train_new_from_iterator()` pada korpus yang sama tidak akan menghasilkan kosakata yang persis sama. Ini karena saat ada beberapa pasangan dengan frekuensi tertinggi yang sama, kita memilih yang pertama ditemukan, sedangkan pustaka 🤗 Tokenizers memilih berdasarkan ID internalnya.
+
+
+
+Untuk men-tokenisasi teks baru, kita lakukan pra-tokenisasi, pembagian karakter, lalu terapkan semua aturan penggabungan yang telah dipelajari:
+
+```python
+def tokenize(text):
+ pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in pre_tokenize_result]
+ splits = [[l for l in word] for word in pre_tokenized_text]
+ for pair, merge in merges.items():
+ for idx, split in enumerate(splits):
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == pair[0] and split[i + 1] == pair[1]:
+ split = split[:i] + [merge] + split[i + 2 :]
+ else:
+ i += 1
+ splits[idx] = split
+
+ return sum(splits, [])
+```
+
+Kita bisa coba pada teks yang hanya berisi karakter yang ada di alfabet:
+
+```python
+tokenize("This is not a token.")
+```
+
+```python out
+['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
+```
+
+
+
+⚠️ Implementasi kita akan menghasilkan error jika ada karakter yang tidak dikenal karena kita tidak menambahkan penanganan untuk kasus tersebut. GPT-2 sebenarnya tidak memiliki token tidak dikenal (tidak mungkin mendapatkan karakter yang tidak dikenal saat menggunakan BPE level-byte), namun hal ini bisa terjadi di sini karena kita tidak menyertakan semua kemungkinan byte dalam kosakata awal. Aspek BPE ini berada di luar cakupan bagian ini, jadi kami tidak membahas detailnya.
+
+
+
+Itulah keseluruhan algoritma BPE! Selanjutnya, kita akan melihat algoritma WordPiece.
diff --git a/chapters/id/chapter6/6.mdx b/chapters/id/chapter6/6.mdx
new file mode 100644
index 000000000..eef60246e
--- /dev/null
+++ b/chapters/id/chapter6/6.mdx
@@ -0,0 +1,375 @@
+# Tokenisasi WordPiece[[wordpiece-tokenization]]
+
+
+
+WordPiece adalah algoritma tokenisasi yang dikembangkan oleh Google untuk melakukan pretraining pada BERT. Sejak itu, algoritma ini digunakan kembali di beberapa model Transformer berbasis BERT, seperti DistilBERT, MobileBERT, Funnel Transformers, dan MPNET. Algoritma ini sangat mirip dengan BPE dalam hal pelatihan, tetapi proses tokenisasinya dilakukan dengan cara yang berbeda.
+
+
+
+
+
+💡 Bagian ini membahas WordPiece secara mendalam, bahkan hingga ke implementasi penuh. Anda bisa langsung lompat ke akhir jika hanya ingin gambaran umum tentang algoritma tokenisasi ini.
+
+
+
+## Algoritma pelatihan[[training-algorithm]]
+
+
+
+⚠️ Google tidak pernah membuka kode sumber dari implementasi algoritma pelatihan WordPiece, jadi penjelasan berikut ini adalah perkiraan terbaik berdasarkan literatur yang tersedia. Bisa jadi tidak 100% akurat.
+
+
+
+Seperti BPE, WordPiece dimulai dengan kosakata kecil yang mencakup token spesial yang digunakan oleh model dan alfabet awal. Karena ia mengidentifikasi sub-kata dengan menambahkan prefiks (seperti `##` untuk BERT), setiap kata awalnya dipecah dengan menambahkan prefiks tersebut ke semua karakter dalam kata. Contohnya, kata `"word"` dipecah seperti ini:
+
+```
+w ##o ##r ##d
+```
+
+Dengan demikian, alfabet awal mencakup semua karakter yang muncul di awal kata dan karakter dalam kata yang diawali dengan prefiks WordPiece.
+
+Kemudian, seperti BPE, WordPiece mempelajari aturan penggabungan. Perbedaan utamanya adalah cara pasangan yang akan digabung dipilih. Alih-alih memilih pasangan paling sering, WordPiece menghitung skor untuk setiap pasangan menggunakan rumus berikut:
+
+$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$
+
+Dengan membagi frekuensi pasangan dengan hasil kali frekuensi masing-masing elemen, algoritma memprioritaskan penggabungan pasangan di mana masing-masing bagian lebih jarang muncul dalam kosakata. Misalnya, algoritma tidak akan langsung menggabungkan `("un", "##able")` meskipun pasangan tersebut sering muncul, karena `"un"` dan `"##able"` kemungkinan besar muncul di banyak kata lain dan memiliki frekuensi tinggi. Sebaliknya, pasangan seperti `("hu", "##gging")` kemungkinan akan digabung lebih cepat (jika kata "hugging" sering muncul), karena `"hu"` dan `"##gging"` secara individu lebih jarang muncul.
+
+Mari kita lihat kosakata yang sama seperti pada contoh pelatihan BPE:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+Pemecahan awalnya akan menjadi:
+
+```
+("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
+```
+
+Jadi kosakata awal adalah `["b", "h", "p", "##g", "##n", "##s", "##u"]` (abaikan token spesial untuk saat ini). Pasangan paling sering adalah `("##u", "##g")` (muncul 20 kali), tetapi frekuensi `"##u"` sangat tinggi, jadi skornya tidak tertinggi (yaitu 1 / 36). Semua pasangan dengan `"##u"` memiliki skor yang sama (1 / 36), jadi skor terbaik dimiliki pasangan `("##g", "##s")` — satu-satunya yang tidak memiliki `"##u"` — dengan skor 1 / 20, dan penggabungan pertama yang dipelajari adalah `("##g", "##s") -> ("##gs")`.
+
+Perlu dicatat bahwa saat kita menggabungkan, kita menghapus `##` di antara dua token, jadi kita menambahkan `"##gs"` ke dalam kosakata dan menerapkan penggabungan ini pada kata-kata dalam korpus:
+
+```
+Kosakata: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
+Korpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
+```
+
+Pada titik ini, `"##u"` muncul di semua pasangan yang mungkin, jadi semuanya memiliki skor yang sama. Misalnya, jika pasangan pertama yang digabung adalah `("h", "##u") -> "hu"`, maka hasilnya:
+
+```
+Kosakata: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"]
+Korpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
+```
+
+Kemudian, skor terbaik berikutnya dimiliki oleh `("hu", "##g")` dan `("hu", "##gs")` (dengan 1/15, dibandingkan dengan 1/21 untuk pasangan lainnya), jadi pasangan pertama dengan skor tertinggi digabung:
+
+```
+Kosakata: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
+Korpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
+```
+
+dan proses ini berlanjut hingga mencapai ukuran kosakata yang diinginkan.
+
+
+
+✏️ **Sekarang giliran Anda!** Apa aturan penggabungan berikutnya?
+
+
+
+## Algoritma tokenisasi[[tokenization-algorithm]]
+
+Tokenisasi pada WordPiece berbeda dari BPE karena WordPiece hanya menyimpan kosakata akhir, bukan aturan penggabungan yang dipelajari. Dimulai dari kata yang akan ditokenisasi, WordPiece mencari sub-kata terpanjang yang ada dalam kosakata, lalu memotong berdasarkan itu. Misalnya, jika kita menggunakan kosakata yang dipelajari pada contoh di atas, untuk kata `"hugs"` sub-kata terpanjang dari awal yang ada dalam kosakata adalah `"hug"`, jadi kita memotong di situ dan mendapatkan `["hug", "##s"]`. Kemudian kita lanjutkan dengan `"##s"`, yang ada dalam kosakata, jadi tokenisasi dari `"hugs"` adalah `["hug", "##s"]`.
+
+Dengan BPE, kita akan menerapkan aturan penggabungan secara berurutan dan melakukan tokenisasi sebagai `["hu", "##gs"]`, sehingga hasil pengkodeannya berbeda.
+
+Sebagai contoh lain, mari kita lihat bagaimana kata `"bugs"` akan ditokenisasi. `"b"` adalah sub-kata terpanjang dari awal kata yang ada dalam kosakata, jadi kita memotong di situ dan mendapatkan `["b", "##ugs"]`. Lalu `"##u"` adalah sub-kata terpanjang dari awal `"##ugs"` yang ada dalam kosakata, jadi kita memotong di situ dan mendapatkan `["b", "##u", "##gs"]`. Terakhir, `"##gs"` ada dalam kosakata, jadi daftar terakhir ini adalah tokenisasi dari `"bugs"`.
+
+Saat tokenisasi mencapai tahap di mana tidak ada sub-kata dalam kosakata yang cocok, seluruh kata akan ditokenisasi sebagai tidak dikenal — jadi, misalnya, `"mug"` akan ditokenisasi sebagai `["[UNK]"]`, begitu juga dengan `"bum"` (meskipun kita bisa mulai dengan `"b"` dan `"##u"`, `"##m"` tidak ada dalam kosakata, dan hasil tokenisasinya hanya `["[UNK]"]`, bukan `["b", "##u", "[UNK]"]`). Ini adalah perbedaan lain dari BPE, yang hanya mengklasifikasikan karakter individual yang tidak ada dalam kosakata sebagai tidak dikenal.
+
+
+
+✏️ **Sekarang giliran Anda!** Bagaimana kata `"pugs"` akan ditokenisasi?
+
+
+
+## Implementasi WordPiece[[implementing-wordpiece]]
+
+Sekarang mari kita lihat implementasi algoritma WordPiece. Seperti pada BPE, ini hanya bersifat pedagogis, dan Anda tidak akan bisa menggunakannya pada korpus yang besar.
+
+Kita akan menggunakan korpus yang sama seperti pada contoh BPE:
+
+```python
+corpus = [
+ "This is the Hugging Face Course.",
+ "This chapter is about tokenization.",
+ "This section shows several tokenizer algorithms.",
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+]
+```
+
+Pertama, kita perlu melakukan pra-tokenisasi korpus menjadi kata-kata. Karena kita meniru tokenizer WordPiece (seperti BERT), kita akan menggunakan tokenizer `bert-base-cased` untuk pra-tokenisasinya:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+```
+
+Lalu kita hitung frekuensi setiap kata dalam korpus saat melakukan pra-tokenisasi:
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+word_freqs
+```
+
+```python out
+defaultdict(
+ int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
+ 'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
+ ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
+ 'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
+```
+
+Seperti yang kita lihat sebelumnya, alfabet adalah himpunan unik dari semua huruf pertama dari kata-kata, dan huruf-huruf lainnya yang muncul dalam kata dengan prefiks `##`:
+
+```python
+alphabet = []
+for word in word_freqs.keys():
+ if word[0] not in alphabet:
+ alphabet.append(word[0])
+ for letter in word[1:]:
+ if f"##{letter}" not in alphabet:
+ alphabet.append(f"##{letter}")
+
+alphabet.sort()
+alphabet
+
+print(alphabet)
+```
+
+```python out
+['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
+ '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
+ 'w', 'y']
+```
+
+Kita juga menambahkan token khusus yang digunakan oleh model di awal kosakata tersebut. Dalam kasus BERT, daftarnya adalah `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]`:
+
+```python
+vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
+```
+
+Selanjutnya, kita perlu memecah setiap kata, dengan semua huruf yang bukan huruf pertama diberi prefiks `##`:
+
+```python
+splits = {
+ word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
+ for word in word_freqs.keys()
+}
+```
+
+Sekarang kita siap untuk pelatihan, mari kita buat fungsi untuk menghitung skor setiap pasangan. Kita akan menggunakan ini di setiap langkah pelatihan:
+
+```python
+def compute_pair_scores(splits):
+ letter_freqs = defaultdict(int)
+ pair_freqs = defaultdict(int)
+ for word, freq in word_freqs.items():
+ split = splits[word]
+ if len(split) == 1:
+ letter_freqs[split[0]] += freq
+ continue
+ for i in range(len(split) - 1):
+ pair = (split[i], split[i + 1])
+ letter_freqs[split[i]] += freq
+ pair_freqs[pair] += freq
+ letter_freqs[split[-1]] += freq
+
+ scores = {
+ pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
+ for pair, freq in pair_freqs.items()
+ }
+ return scores
+```
+
+Mari kita lihat sebagian isi dictionary setelah pemisahan awal:
+
+```python
+pair_scores = compute_pair_scores(splits)
+for i, key in enumerate(pair_scores.keys()):
+ print(f"{key}: {pair_scores[key]}")
+ if i >= 5:
+ break
+```
+
+```python out
+('T', '##h'): 0.125
+('##h', '##i'): 0.03409090909090909
+('##i', '##s'): 0.02727272727272727
+('i', '##s'): 0.1
+('t', '##h'): 0.03571428571428571
+('##h', '##e'): 0.011904761904761904
+```
+
+Sekarang, mencari pasangan dengan skor terbaik cukup dengan loop sederhana:
+
+```python
+best_pair = ""
+max_score = None
+for pair, score in pair_scores.items():
+ if max_score is None or max_score < score:
+ best_pair = pair
+ max_score = score
+
+print(best_pair, max_score)
+```
+
+```python out
+('a', '##b') 0.2
+```
+
+Jadi penggabungan pertama yang dipelajari adalah `('a', '##b') -> 'ab'`, dan kita tambahkan `'ab'` ke kosakata:
+
+```python
+vocab.append("ab")
+```
+
+Untuk melanjutkan, kita perlu menerapkan penggabungan tersebut pada dictionary `splits`. Mari kita tulis fungsi untuk ini:
+
+```python
+def merge_pair(a, b, splits):
+ for word in word_freqs:
+ split = splits[word]
+ if len(split) == 1:
+ continue
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == a and split[i + 1] == b:
+ merge = a + b[2:] if b.startswith("##") else a + b
+ split = split[:i] + [merge] + split[i + 2 :]
+ else:
+ i += 1
+ splits[word] = split
+ return splits
+```
+
+Dan kita bisa melihat hasil dari penggabungan pertama:
+
+```py
+splits = merge_pair("a", "##b", splits)
+splits["about"]
+```
+
+```python out
+['ab', '##o', '##u', '##t']
+```
+
+Sekarang kita punya semua yang dibutuhkan untuk melakukan loop sampai kita mempelajari semua penggabungan yang diinginkan. Kita tetapkan target ukuran kosakata 70:
+
+```python
+vocab_size = 70
+while len(vocab) < vocab_size:
+ scores = compute_pair_scores(splits)
+ best_pair, max_score = "", None
+ for pair, score in scores.items():
+ if max_score is None or max_score < score:
+ best_pair = pair
+ max_score = score
+ splits = merge_pair(*best_pair, splits)
+ new_token = (
+ best_pair[0] + best_pair[1][2:]
+ if best_pair[1].startswith("##")
+ else best_pair[0] + best_pair[1]
+ )
+ vocab.append(new_token)
+```
+
+Kita kemudian bisa melihat kosakata yang dihasilkan:
+
+```py
+print(vocab)
+```
+
+```python out
+['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
+ '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
+ 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', 'ab', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
+ 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
+ '##ut']
+```
+
+Seperti yang kita lihat, dibandingkan dengan BPE, tokenizer ini mempelajari bagian kata sebagai token sedikit lebih cepat.
+
+
+
+💡 Menggunakan `train_new_from_iterator()` pada korpus yang sama tidak akan menghasilkan kosakata yang persis sama. Ini karena pustaka 🤗 Tokenizers tidak mengimplementasikan WordPiece untuk pelatihan (karena kita tidak sepenuhnya yakin tentang detail internalnya), dan sebagai gantinya menggunakan BPE.
+
+
+
+Untuk melakukan tokenisasi teks baru, kita pra-tokenisasi terlebih dahulu, lalu membagi, kemudian menerapkan algoritma tokenisasi pada setiap kata. Artinya, kita mencari sub-kata terpanjang dari awal kata yang ada dalam kosakata dan memotongnya, lalu ulangi proses tersebut pada bagian selanjutnya, dan seterusnya:
+
+```python
+def encode_word(word):
+ tokens = []
+ while len(word) > 0:
+ i = len(word)
+ while i > 0 and word[:i] not in vocab:
+ i -= 1
+ if i == 0:
+ return ["[UNK]"]
+ tokens.append(word[:i])
+ word = word[i:]
+ if len(word) > 0:
+ word = f"##{word}"
+ return tokens
+```
+
+Mari kita uji pada satu kata yang ada di kosakata, dan satu yang tidak:
+
+```python
+print(encode_word("Hugging"))
+print(encode_word("HOgging"))
+```
+
+```python out
+['Hugg', '##i', '##n', '##g']
+['[UNK]']
+```
+
+Sekarang, mari kita buat fungsi untuk melakukan tokenisasi pada teks:
+
+```python
+def tokenize(text):
+ pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in pre_tokenize_result]
+ encoded_words = [encode_word(word) for word in pre_tokenized_text]
+ return sum(encoded_words, [])
+```
+
+Kita bisa mencobanya pada teks apa pun:
+
+```python
+tokenize("This is the Hugging Face course!")
+```
+
+```python out
+['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
+ '##e', '[UNK]']
+```
+
+Selesai sudah untuk algoritma WordPiece! Sekarang mari kita lanjutkan ke Unigram.
+
diff --git a/chapters/id/chapter6/7.mdx b/chapters/id/chapter6/7.mdx
new file mode 100644
index 000000000..9e4c56171
--- /dev/null
+++ b/chapters/id/chapter6/7.mdx
@@ -0,0 +1,404 @@
+# Tokenisasi Unigram[[unigram-tokenization]]
+
+
+
+Algoritma Unigram digunakan bersama dengan [SentencePiece](https://huggingface.co/papers/1808.06226), yaitu algoritma tokenisasi yang digunakan oleh model seperti AlBERT, T5, mBART, Big Bird, dan XLNet.
+
+SentencePiece menangani kenyataan bahwa tidak semua bahasa menggunakan spasi untuk memisahkan kata. Sebagai gantinya, SentencePiece memperlakukan masukan sebagai aliran data mentah dan menyertakan spasi dalam himpunan karakter yang digunakan. Kemudian SentencePiece menggunakan algoritma Unigram untuk membangun kosakata yang sesuai.
+
+
+
+
+
+💡 Bagian ini membahas Unigram secara mendalam, termasuk hingga implementasi lengkapnya. Anda dapat langsung lompat ke bagian akhir jika hanya ingin gambaran umum algoritma ini.
+
+
+
+## Algoritma Pelatihan[[training-algorithm]]
+
+Berbeda dengan BPE dan WordPiece, Unigram bekerja secara terbalik: ia memulai dari kosakata besar, kemudian menghapus token darinya hingga mencapai ukuran kosakata yang diinginkan. Ada beberapa cara untuk membangun kosakata awal ini: kita bisa mengambil substring paling umum dari kata-kata yang sudah di-pre-tokenisasi, atau menjalankan BPE terlebih dahulu dengan ukuran kosakata besar.
+
+Pada setiap langkah pelatihan, algoritma Unigram menghitung nilai _loss_ terhadap korpus berdasarkan kosakata saat ini. Kemudian, untuk setiap simbol dalam kosakata, algoritma menghitung seberapa besar peningkatan _loss_ jika simbol tersebut dihapus, dan mencari simbol-simbol dengan peningkatan terendah. Simbol-simbol ini memiliki dampak paling kecil terhadap keseluruhan _loss_, sehingga dianggap "paling tidak diperlukan" dan menjadi kandidat terbaik untuk dihapus.
+
+Ini adalah proses yang sangat mahal secara komputasi, jadi kita tidak hanya menghapus satu simbol dengan peningkatan _loss_ terendah, melainkan menghapus \\(p\\)% simbol (di mana \\(p\\) adalah _hyperparameter_, biasanya 10 atau 20) yang menyebabkan peningkatan terendah. Proses ini diulang hingga kosakata mencapai ukuran yang ditentukan.
+
+Perlu dicatat bahwa karakter dasar tidak pernah dihapus, untuk memastikan bahwa semua kata tetap dapat ditokenisasi.
+
+Sekarang, ini masih agak abstrak: bagian utama dari algoritma adalah menghitung _loss_ dan melihat bagaimana ia berubah jika kita menghapus token tertentu dari kosakata, tetapi kita belum menjelaskan bagaimana caranya. Langkah ini bergantung pada algoritma tokenisasi dari model Unigram, yang akan kita bahas selanjutnya.
+
+Kita akan menggunakan kembali korpus dari contoh sebelumnya:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+Untuk contoh ini, kita akan menggunakan semua substring ketat (strict substrings) sebagai kosakata awal:
+
+```
+["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
+```
+
+## Algoritma Tokenisasi[[tokenization-algorithm]]
+
+Model Unigram adalah jenis _language model_ yang menganggap setiap token tidak bergantung pada token sebelumnya. Ini adalah model bahasa paling sederhana, di mana probabilitas token X hanya bergantung pada seberapa sering token itu muncul. Jadi, jika kita menggunakan model Unigram untuk menghasilkan teks, ia akan selalu memprediksi token yang paling sering muncul.
+
+Probabilitas dari token tertentu dihitung berdasarkan frekuensinya (jumlah kemunculan dalam korpus), dibagi dengan jumlah total frekuensi semua token di kosakata (agar jumlah probabilitas menjadi 1). Sebagai contoh, `"ug"` muncul dalam `"hug"`, `"pug"`, dan `"hugs"`, jadi ia memiliki frekuensi 20 dalam korpus.
+
+Berikut adalah frekuensi dari semua sub-kata yang memungkinkan di kosakata:
+
+```
+("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
+("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
+```
+
+Jumlah total frekuensi adalah 210, maka probabilitas dari sub-kata `"ug"` adalah 20/210.
+
+
+
+✏️ **Sekarang giliran Anda!** Tulis kode untuk menghitung frekuensi di atas dan pastikan hasil serta total jumlahnya sesuai.
+
+
+
+Untuk men-tokenisasi sebuah kata, kita melihat semua kemungkinan segmentasi ke dalam token dan menghitung probabilitas masing-masing berdasarkan model Unigram. Karena semua token dianggap independen, probabilitasnya hanya hasil perkalian dari masing-masing probabilitas token. Sebagai contoh, tokenisasi `["p", "u", "g"]` dari kata `"pug"` memiliki probabilitas:
+
+$$P(["p", "u", "g"]) = P("p") \times P("u") \times P("g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$
+
+Sebaliknya, tokenisasi `["pu", "g"]` memiliki probabilitas:
+
+$$P(["pu", "g"]) = P("pu") \times P("g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$
+
+Jadi, tokenisasi tersebut jauh lebih mungkin. Umumnya, tokenisasi dengan jumlah token paling sedikit akan memiliki probabilitas tertinggi (karena pembagian dengan 210 dilakukan untuk setiap token), yang sesuai dengan intuisi kita: memecah kata menjadi sesedikit mungkin token.
+
+Tokenisasi dari kata `"pug"` dengan model Unigram akan mengambil hasil dengan probabilitas tertinggi. Berikut adalah semua kemungkinan segmentasi dan probabilitasnya:
+
+```
+["p", "u", "g"] : 0.000389
+["p", "ug"] : 0.0022676
+["pu", "g"] : 0.0022676
+```
+
+Jadi `"pug"` akan ditokenisasi sebagai `["p", "ug"]` atau `["pu", "g"]`, tergantung segmentasi mana yang ditemui lebih dulu (meskipun dalam korpus besar, kasus seri seperti ini jarang terjadi).
+
+Dalam kasus ini, cukup mudah menemukan semua segmentasi dan menghitung probabilitasnya, tetapi secara umum ini bisa menjadi sulit. Kita dapat menggunakan algoritma klasik untuk ini, yang disebut *Viterbi algorithm*. Pada dasarnya, kita membangun sebuah grafik untuk mendeteksi semua segmentasi mungkin dari suatu kata, dengan mengatakan bahwa ada cabang dari karakter _a_ ke karakter _b_ jika substring dari _a_ ke _b_ terdapat dalam kosakata, dan cabang tersebut diberi bobot berupa probabilitas substring tersebut.
+
+Untuk menemukan jalur dengan skor terbaik, algoritma Viterbi menentukan, untuk setiap posisi dalam kata, segmentasi terbaik yang berakhir di posisi tersebut. Karena kita berjalan dari awal hingga akhir, skor terbaik bisa ditemukan dengan mengulang semua sub-kata yang berakhir di posisi tersebut dan menggunakan skor segmentasi terbaik dari posisi awal sub-kata tersebut. Kemudian, kita tinggal _trace back_ untuk menemukan jalur terbaik.
+
+Mari kita lihat contoh menggunakan kosakata dan kata `"unhug"`. Untuk setiap posisi karakter, sub-kata dengan skor terbaik yang berakhir di sana adalah:
+
+```
+Karakter 0 (u): "u" (skor 0.171429)
+Karakter 1 (n): "un" (skor 0.076191)
+Karakter 2 (h): "un" "h" (skor 0.005442)
+Karakter 3 (u): "un" "hu" (skor 0.005442)
+Karakter 4 (g): "un" "hug" (skor 0.005442)
+```
+
+Dengan demikian, `"unhug"` akan ditokenisasi menjadi `["un", "hug"]`.
+
+
+
+✏️ **Sekarang giliran Anda!** Tentukan tokenisasi dari kata `"huggun"` dan hitung skornya.
+
+
+
+## Kembali ke Pelatihan[[back-to-training]]
+
+Sekarang setelah kita melihat bagaimana tokenisasi bekerja, kita dapat menyelami lebih dalam tentang *loss* yang digunakan selama pelatihan. Pada setiap tahap, *loss* ini dihitung dengan men-tokenisasi setiap kata dalam korpus, menggunakan kosakata saat ini dan model Unigram yang ditentukan oleh frekuensi setiap token dalam korpus (seperti yang telah dijelaskan sebelumnya).
+
+Setiap kata dalam korpus memiliki skor, dan *loss* dihitung sebagai *negative log likelihood* dari skor-skor tersebut — yaitu, jumlah dari semua `-log(P(kata))` untuk semua kata dalam korpus.
+
+Mari kita kembali ke contoh kita dengan korpus berikut:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+Tokenisasi dari setiap kata beserta skornya:
+
+```
+"hug": ["hug"] (skor 0.071428)
+"pug": ["pu", "g"] (skor 0.007710)
+"pun": ["pu", "n"] (skor 0.006168)
+"bun": ["bu", "n"] (skor 0.001451)
+"hugs": ["hug", "s"] (skor 0.001701)
+```
+
+Jadi, *loss*-nya adalah:
+
+```
+10 * (-log(0.071428)) +
+5 * (-log(0.007710)) +
+12 * (-log(0.006168)) +
+4 * (-log(0.001451)) +
+5 * (-log(0.001701)) = 169.8
+```
+
+Sekarang kita perlu menghitung bagaimana menghapus setiap token memengaruhi *loss*. Ini cukup membosankan, jadi kita hanya akan menghitungnya untuk dua token dan sisanya akan dibantu dengan kode nantinya. Dalam kasus ini, kita punya dua tokenisasi yang setara untuk semua kata: seperti yang sudah kita lihat sebelumnya, misalnya `"pug"` bisa ditokenisasi sebagai `["p", "ug"]` dengan skor yang sama. Maka, menghapus token `"pu"` dari kosakata akan menghasilkan *loss* yang sama persis. Namun, menghapus `"hug"` akan memperburuk *loss*, karena tokenisasi dari `"hug"` dan `"hugs"` akan berubah menjadi:
+
+```
+"hug": ["hu", "g"] (skor 0.006802)
+"hugs": ["hu", "gs"] (skor 0.001701)
+```
+
+Perubahan ini menyebabkan *loss* meningkat sebesar:
+
+```
+- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
+```
+
+Oleh karena itu, token `"pu"` kemungkinan besar akan dihapus dari kosakata, tetapi tidak dengan `"hug"`.
+
+## Implementasi Unigram[[implementing-unigram]]
+
+Sekarang mari kita implementasikan semua yang telah kita pelajari tentang algoritma Unigram ke dalam kode. Seperti halnya BPE dan WordPiece, ini bukan implementasi efisien (justru sebaliknya), tapi seharusnya membantu Anda memahami konsep ini lebih baik.
+
+Kita akan menggunakan korpus yang sama seperti sebelumnya:
+
+```python
+corpus = [
+ "This is the Hugging Face Course.",
+ "This chapter is about tokenization.",
+ "This section shows several tokenizer algorithms.",
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+]
+```
+
+Kali ini, kita akan menggunakan model `xlnet-base-cased`:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
+```
+
+Seperti sebelumnya, kita mulai dengan menghitung jumlah kemunculan setiap kata dalam korpus:
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+word_freqs
+```
+
+Kemudian, kita inisialisasi kosakata kita dengan ukuran yang lebih besar dari ukuran akhir yang diinginkan. Kita harus menyertakan semua karakter dasar (jika tidak, kita tidak bisa men-tokenisasi semua kata), tetapi untuk substring yang lebih panjang kita hanya menyimpan yang paling sering muncul. Maka kita urutkan berdasarkan frekuensi:
+
+```python
+char_freqs = defaultdict(int)
+subwords_freqs = defaultdict(int)
+for word, freq in word_freqs.items():
+ for i in range(len(word)):
+ char_freqs[word[i]] += freq
+ # Loop untuk substring dengan panjang minimal 2
+ for j in range(i + 2, len(word) + 1):
+ subwords_freqs[word[i:j]] += freq
+
+# Urutkan sub-kata berdasarkan frekuensi
+sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
+sorted_subwords[:10]
+```
+
+```python out
+[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
+```
+
+Kita gabungkan karakter dasar dengan sub-kata paling umum untuk membentuk kosakata awal berukuran 300:
+
+```python
+token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
+token_freqs = {token: freq for token, freq in token_freqs}
+```
+Jika Anda ingin, saya bisa lanjutkan menerjemahkan bagian implementasi selanjutnya (Viterbi, loss, penghapusan token, dll). Ingin dilanjutkan?
+
+
+
+💡 SentencePiece menggunakan algoritma yang lebih efisien bernama *Enhanced Suffix Array (ESA)* untuk membuat kosakata awal.
+
+
+
+Selanjutnya, kita hitung jumlah total dari seluruh frekuensi, agar bisa mengubah frekuensi menjadi probabilitas. Untuk model kita, kita akan menyimpan *logaritma* dari probabilitas, karena ini lebih stabil secara numerik (lebih baik menjumlahkan log daripada mengalikan angka kecil), dan ini juga menyederhanakan perhitungan *loss* dari model:
+
+```python
+from math import log
+
+total_sum = sum([freq for token, freq in token_freqs.items()])
+model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
+```
+
+Fungsi utama berikutnya adalah fungsi yang melakukan tokenisasi kata menggunakan **algoritma Viterbi**. Seperti yang telah dibahas, algoritma ini menghitung segmentasi terbaik dari setiap substring dalam sebuah kata, dan kita simpan dalam variabel `best_segmentations`. Kita akan menyimpan satu dictionary per posisi karakter dalam kata (dari 0 hingga panjangnya), yang memiliki dua key: `start` (indeks awal token terakhir dalam segmentasi terbaik), dan `score` (skor segmentasi terbaik hingga posisi itu). Dengan informasi `start`, kita bisa membangun kembali seluruh tokenisasi setelah list selesai diisi.
+
+Mengisi daftar dilakukan hanya dengan dua perulangan: perulangan utama berjalan pada setiap posisi awal, dan perulangan kedua mencoba semua substring yang dimulai dari posisi awal tersebut. Jika substring tersebut ada dalam kosakata, maka kita memiliki segmentasi baru dari kata hingga posisi akhir tersebut, yang kemudian kita bandingkan dengan yang ada di `best_segmentations`.
+
+Setelah perulangan utama selesai, kita cukup mulai dari akhir dan melompat dari satu posisi awal ke posisi berikutnya, mencatat token saat kita berjalan, hingga kita mencapai awal kata.
+
+```python
+def encode_word(word, model):
+ # Inisialisasi segmentasi terbaik:
+ # Mulai dari indeks 0 dengan skor 1, sisanya None
+ best_segmentations = [{"start": 0, "score": 1}] + [
+ {"start": None, "score": None} for _ in range(len(word))
+ ]
+
+ # Perulangan utama: coba semua posisi awal
+ for start_idx in range(len(word)):
+ # Skor terbaik yang diketahui di posisi awal ini
+ best_score_at_start = best_segmentations[start_idx]["score"]
+
+ # Coba semua substring yang dimulai dari start_idx
+ for end_idx in range(start_idx + 1, len(word) + 1):
+ token = word[start_idx:end_idx]
+ # Jika token ada dalam model dan skor awal diketahui
+ if token in model and best_score_at_start is not None:
+ score = model[token] + best_score_at_start
+ # Jika segmentasi ini lebih baik, perbarui nilai
+ if (
+ best_segmentations[end_idx]["score"] is None
+ or best_segmentations[end_idx]["score"] > score
+ ):
+ best_segmentations[end_idx] = {"start": start_idx, "score": score}
+
+ # Ambil hasil segmentasi terbaik di akhir kata
+ segmentation = best_segmentations[-1]
+ if segmentation["score"] is None:
+ # Jika tidak ditemukan segmentasi valid -> token tidak dikenal
+ return [""], None
+
+ score = segmentation["score"]
+ start = segmentation["start"]
+ end = len(word)
+ tokens = []
+
+ # Rekonstruksi token dari belakang ke depan
+ while start != 0:
+ tokens.insert(0, word[start:end])
+ next_start = best_segmentations[start]["start"]
+ end = start
+ start = next_start
+
+ # Tambahkan token awal
+ tokens.insert(0, word[start:end])
+ return tokens, score
+```
+
+Kita bisa langsung mencoba fungsi ini pada beberapa kata:
+
+```python
+print(encode_word("Hopefully", model))
+print(encode_word("This", model))
+```
+
+```python out
+(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
+(['This'], 6.288267030694535)
+```
+
+Sekarang sangat mudah untuk menghitung *loss* dari model terhadap korpus:
+
+```python
+def compute_loss(model):
+ loss = 0
+ for word, freq in word_freqs.items():
+ _, word_loss = encode_word(word, model)
+ loss += freq * word_loss
+ return loss
+```
+
+Kita coba pada model awal kita:
+
+```python
+compute_loss(model)
+```
+
+```python out
+413.10377642940875
+```
+
+Menghitung skor untuk setiap token juga tidak sulit; kita hanya perlu menghitung *loss* jika setiap token dihapus satu per satu:
+
+```python
+import copy
+
+
+def compute_scores(model):
+ scores = {}
+ model_loss = compute_loss(model)
+ for token, score in model.items():
+ # Token panjang 1 selalu dipertahankan
+ if len(token) == 1:
+ continue
+ model_without_token = copy.deepcopy(model)
+ _ = model_without_token.pop(token)
+ scores[token] = compute_loss(model_without_token) - model_loss
+ return scores
+```
+
+Contoh penggunaannya:
+
+```python
+scores = compute_scores(model)
+print(scores["ll"])
+print(scores["his"])
+```
+
+Karena `"ll"` digunakan dalam tokenisasi `"Hopefully"`, dan jika dihapus maka akan digantikan oleh dua token `"l"`, kita perkirakan nilainya positif. Sementara `"his"` hanya muncul di `"This"` yang ditokenisasi sebagai satu token, jadi skor penghapusannya seharusnya 0:
+
+```python out
+6.376412403623874
+0.0
+```
+
+
+
+💡 Pendekatan ini sangat tidak efisien, jadi SentencePiece menggunakan pendekatan *approximation*: alih-alih menghitung ulang dari awal, token X cukup digantikan dengan tokenisasi yang tersedia dari sisa kosakata. Dengan cara ini, seluruh skor bisa dihitung sekaligus bersamaan dengan *loss* model.
+
+
+
+Dengan semua ini sudah siap, hal terakhir yang perlu kita lakukan adalah menambahkan token khusus yang digunakan oleh model ke dalam kosakata, lalu melakukan perulangan hingga kita memangkas cukup banyak token dari kosakata untuk mencapai ukuran yang diinginkan.
+
+```python
+percent_to_remove = 0.1
+while len(model) > 100:
+ scores = compute_scores(model)
+ sorted_scores = sorted(scores.items(), key=lambda x: x[1])
+ # Hapus token sebanyak percent_to_remove dengan skor terendah.
+ for i in range(int(len(model) * percent_to_remove)):
+ _ = token_freqs.pop(sorted_scores[i][0])
+
+ total_sum = sum([freq for token, freq in token_freqs.items()])
+ model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
+```
+
+Akhirnya, untuk men-tokenisasi teks apa pun, cukup lakukan pre-tokenisasi dan gunakan fungsi `encode_word()`:
+
+```python
+def tokenize(text, model):
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in words_with_offsets]
+ encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
+ return sum(encoded_words, [])
+
+
+tokenize("This is the Hugging Face course.", model)
+```
+
+```python out
+['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
+```
+
+
+
+Tokenizer XLNet menggunakan SentencePiece, itulah mengapa karakter `"_"` muncul. Untuk melakukan decoding, cukup gabungkan semua token dan ganti `"_"` dengan spasi.
+
+
+
+Itulah seluruh proses dari algoritma Unigram! Semoga sekarang Anda merasa sudah benar-benar memahami semua jenis algoritma tokenisasi. Pada bagian selanjutnya, kita akan menjelajahi **komponen-komponen penyusun library 🤗 Tokenizers**, dan menunjukkan bagaimana Anda bisa membangun tokenizer Anda sendiri.
diff --git a/chapters/id/chapter6/8.mdx b/chapters/id/chapter6/8.mdx
new file mode 100644
index 000000000..15fb9d32e
--- /dev/null
+++ b/chapters/id/chapter6/8.mdx
@@ -0,0 +1,564 @@
+# Membangun Tokenizer, Blok per Blok[[building-a-tokenizer-block-by-block]]
+
+
+
+Seperti yang telah kita lihat di bagian-bagian sebelumnya, proses tokenisasi terdiri dari beberapa langkah:
+
+- **Normalisasi** (membersihkan teks jika diperlukan, seperti menghapus spasi atau aksen, normalisasi Unicode, dll.)
+- **Pra-tokenisasi** (memisahkan input menjadi kata-kata)
+- **Pemodelan/tokenisasi utama** (menggunakan kata-kata hasil pra-tokenisasi untuk menghasilkan urutan token)
+- **Pascaproses** (menambahkan token spesial, membuat attention mask dan token type IDs)
+
+Sebagai pengingat, berikut adalah diagram keseluruhan proses:
+
+
+
+
+
+
+Library 🤗 Tokenizers dirancang untuk memberikan banyak pilihan untuk setiap langkah, yang dapat Anda kombinasikan sesuka hati. Di bagian ini, kita akan melihat bagaimana membangun tokenizer dari nol, berbeda dengan cara sebelumnya di [bagian 2](/course/chapter6/2) di mana kita melatih tokenizer baru dari tokenizer lama. Setelah mempelajari ini, Anda bisa membangun tokenizer jenis apa pun yang Anda inginkan!
+
+
+
+Secara lebih rinci, library ini dibangun di sekitar kelas utama `Tokenizer` dengan blok-blok penyusun yang dikelompokkan dalam submodul:
+
+- `normalizers`: berisi berbagai jenis `Normalizer` (daftar lengkap [di sini](https://huggingface.co/docs/tokenizers/api/normalizers)).
+- `pre_tokenizers`: berisi berbagai jenis `PreTokenizer` (daftar lengkap [di sini](https://huggingface.co/docs/tokenizers/api/pre-tokenizers)).
+- `models`: berisi berbagai jenis `Model` seperti `BPE`, `WordPiece`, dan `Unigram` (daftar lengkap [di sini](https://huggingface.co/docs/tokenizers/api/models)).
+- `trainers`: berisi jenis `Trainer` untuk melatih model tokenisasi (satu untuk tiap model; daftar lengkap [di sini](https://huggingface.co/docs/tokenizers/api/trainers)).
+- `post_processors`: berisi berbagai jenis `PostProcessor` (daftar lengkap [di sini](https://huggingface.co/docs/tokenizers/api/post-processors)).
+- `decoders`: berisi jenis `Decoder` untuk mengubah kembali token menjadi teks (daftar lengkap [di sini](https://huggingface.co/docs/tokenizers/components#decoders)).
+
+Anda dapat melihat seluruh daftar blok penyusun [di sini](https://huggingface.co/docs/tokenizers/components).
+
+## Mendapatkan Korpus[[acquiring-a-corpus]]
+
+Untuk melatih tokenizer baru, kita akan menggunakan korpus teks kecil agar contoh berjalan cepat. Langkah-langkah untuk mendapatkan korpus ini mirip seperti yang kita lakukan di [awal bab ini](/course/chapter6/2), tetapi kali ini kita menggunakan dataset [WikiText-2](https://huggingface.co/datasets/wikitext):
+
+```python
+from datasets import load_dataset
+
+dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")
+
+
+def get_training_corpus():
+ for i in range(0, len(dataset), 1000):
+ yield dataset[i : i + 1000]["text"]
+```
+
+Fungsi `get_training_corpus()` adalah generator yang akan mengembalikan batch berisi 1.000 teks, yang akan kita gunakan untuk melatih tokenizer.
+
+🤗 Tokenizers juga bisa dilatih langsung dari file teks. Berikut cara membuat file teks dari semua input WikiText-2 yang bisa digunakan secara lokal:
+
+```python
+with open("wikitext-2.txt", "w", encoding="utf-8") as f:
+ for i in range(len(dataset)):
+ f.write(dataset[i]["text"] + "\n")
+```
+
+Selanjutnya, kita akan membangun tokenizer untuk BERT, GPT-2, dan XLNet dari nol. Ini akan memberi kita contoh dari masing-masing tiga algoritma tokenisasi utama: **WordPiece**, **BPE**, dan **Unigram**. Mari mulai dengan **BERT**!
+
+## Membangun Tokenizer WordPiece dari Nol[[building-a-wordpiece-tokenizer-from-scratch]]
+
+Untuk membangun tokenizer dengan library 🤗 Tokenizers, kita mulai dengan membuat objek `Tokenizer` menggunakan sebuah `model`, lalu menetapkan atribut `normalizer`, `pre_tokenizer`, `post_processor`, dan `decoder` sesuai kebutuhan.
+
+Untuk contoh ini, kita buat tokenizer dengan model WordPiece:
+
+```python
+from tokenizers import (
+ decoders,
+ models,
+ normalizers,
+ pre_tokenizers,
+ processors,
+ trainers,
+ Tokenizer,
+)
+
+tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
+```
+
+Kita harus menentukan `unk_token` agar model tahu apa yang harus dikembalikan ketika menemukan karakter yang belum pernah dilihat sebelumnya. Argumen lain yang bisa kita atur di sini termasuk `vocab` dari model kita (karena kita akan melatih model, kita tidak perlu menetapkannya sekarang) dan `max_input_chars_per_word`, yang menentukan panjang maksimum untuk setiap kata (kata yang lebih panjang dari nilai ini akan dipecah).
+Langkah pertama dari tokenisasi adalah normalisasi, jadi mari kita mulai dari sana. Karena BERT sangat banyak digunakan, tersedia `BertNormalizer` dengan opsi klasik yang bisa diatur untuk BERT: `lowercase` dan `strip_accents`, yang cukup jelas maksudnya; `clean_text` untuk menghapus semua karakter kontrol dan mengganti spasi berulang dengan satu spasi; serta `handle_chinese_chars`, yang menambahkan spasi di sekitar karakter Tionghoa. Untuk meniru tokenizer `bert-base-uncased`, kita cukup menetapkan normalizer ini:
+
+
+```python
+tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
+```
+
+Namun, jika kita ingin membangun dari nol tanpa `BertNormalizer`, kita bisa gabungkan beberapa normalizer menggunakan `Sequence`:
+
+```python
+tokenizer.normalizer = normalizers.Sequence(
+ [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
+)
+```
+
+Kita juga menggunakan normalizer Unicode `NFD`, karena jika tidak, normalizer `StripAccents` tidak akan dapat mengenali karakter beraksen dengan benar dan akibatnya tidak akan menghapusnya.
+
+Seperti yang telah kita lihat sebelumnya, kita bisa menggunakan metode `normalize_str()` dari `normalizer` untuk memeriksa efek yang ditimbulkan pada sebuah teks tertentu:
+
+```python
+print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
+```
+
+```python out
+hello how are u?
+```
+
+
+
+**Untuk eksplorasi lebih lanjut**: Jika Anda menguji dua versi normalizer di atas pada string yang mengandung karakter unicode `u"\u0085"`, Anda akan melihat bahwa hasilnya tidak persis sama.
+Untuk menyederhanakan contoh, kita tidak menambahkan `Replace` yang digunakan oleh `BertNormalizer` untuk menggantikan karakter kontrol saat `clean_text=True`. Tapi Anda bisa menambahkan `normalizers.Replace` ke dalam `Sequence` untuk hasil identik.
+
+
+
+Langkah selanjutnya adalah pra-tokenisasi. Sekali lagi, terdapat `BertPreTokenizer` bawaan yang dapat kita gunakan:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
+```
+
+Atau, kita bisa pakai `Whitespace` dari awal:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
+```
+
+Perlu dicatat bahwa pre-tokenizer `Whitespace` akan membagi berdasarkan spasi dan semua karakter yang bukan huruf, angka, atau karakter garis bawah (underscore), sehingga secara teknis ia membagi berdasarkan spasi dan tanda baca.
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
+ ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
+```
+
+Kalau hanya ingin memisahkan berdasarkan spasi, gunakan `WhitespaceSplit`:
+
+```python
+pre_tokenizer = pre_tokenizers.WhitespaceSplit()
+pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
+```
+
+Anda juga bisa menggabungkan beberapa pre-tokenizer dengan `Sequence`:
+
+```python
+pre_tokenizer = pre_tokenizers.Sequence(
+ [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
+)
+pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
+ ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
+```
+
+Langkah berikutnya dalam pipeline tokenisasi adalah menjalankan masukan melalui model. Kita sudah menentukan model saat inisialisasi, tetapi kita masih perlu melatihnya, yang memerlukan sebuah WordPieceTrainer. Hal utama yang perlu diingat ketika membuat trainer di 🤗 Tokenizers adalah Anda harus menyertakan semua *special tokens* yang ingin digunakan — jika tidak, token-token khusus tersebut tidak akan dimasukkan ke dalam kosakata, karena tidak ada di dalam korpus pelatihan.
+
+```python
+special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
+trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
+```
+
+Selain `vocab_size` dan `special_tokens`, kita juga bisa mengatur `min_frequency` (jumlah kemunculan minimal suatu token untuk dimasukkan ke dalam kosakata) atau mengganti `continuing_subword_prefix` jika ingin menggunakan awalan selain `##`.
+
+Untuk melatih model menggunakan iterator yang telah kita definisikan sebelumnya, cukup jalankan:
+
+```python
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+Kita juga bisa menggunakan file teks sebagai korpus pelatihan. Pertama, inisialisasi ulang modelnya:
+
+```python
+tokenizer.model = models.WordPiece(unk_token="[UNK]")
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Setelah pelatihan, kita bisa menguji tokenizer-nya menggunakan metode `encode()`:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
+```
+
+Objek `encoding` adalah sebuah instance dari `Encoding`, yang berisi semua hasil penting dari proses tokenisasi: `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, dan `overflowing`.
+
+Langkah terakhir dalam pipeline tokenisasi adalah **pascaproses**, yaitu menambahkan token khusus seperti `[CLS]` di awal dan `[SEP]` di akhir (atau di antara dua kalimat dalam pasangan). Kita akan menggunakan `TemplateProcessor` untuk ini, tetapi pertama-tama kita perlu tahu ID dari token `[CLS]` dan `[SEP]`:
+
+```python
+cls_token_id = tokenizer.token_to_id("[CLS]")
+sep_token_id = tokenizer.token_to_id("[SEP]")
+print(cls_token_id, sep_token_id)
+```
+
+```python out
+(2, 3)
+```
+
+Untuk menulis template untuk `TemplateProcessor`, kita harus menentukan bagaimana menangani satu kalimat dan pasangan kalimat. Untuk keduanya, kita menuliskan token khusus yang ingin kita gunakan; kalimat pertama (atau tunggal) diwakili oleh `$A`, sedangkan kalimat kedua (jika melakukan enkode pasangan) diwakili oleh `$B`. Untuk masing-masing (token khusus dan kalimat), kita juga menentukan ID tipe token yang sesuai setelah tanda titik dua.
+
+Template klasik BERT didefinisikan sebagai berikut:
+
+```python
+tokenizer.post_processor = processors.TemplateProcessing(
+ single="[CLS]:0 $A:0 [SEP]:0",
+ pair="[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
+ special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
+)
+```
+
+Perlu dicatat bahwa kita perlu menyertakan ID dari token-token khusus, agar tokenizer dapat mengonversinya dengan benar ke ID mereka.
+
+Setelah ini ditambahkan, kembali ke contoh sebelumnya akan menghasilkan:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
+```
+
+Dan pada sepasang kalimat, kita mendapatkan hasil yang sesuai:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
+print(encoding.tokens)
+print(encoding.type_ids)
+```
+
+```python out
+['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+Kita hampir selesai membangun tokenizer ini dari awal — langkah terakhir adalah menyertakan sebuah decoder:
+
+```python
+tokenizer.decoder = decoders.WordPiece(prefix="##")
+```
+
+Mari kita uji pada `encoding` sebelumnya:
+
+```python
+tokenizer.decode(encoding.ids)
+```
+
+```python out
+"let's test this tokenizer... on a pair of sentences."
+```
+
+Bagus! Kita bisa menyimpan tokenizer kita dalam satu file JSON seperti ini:
+
+```python
+tokenizer.save("tokenizer.json")
+```
+
+Kita kemudian dapat memuat ulang file tersebut ke dalam objek `Tokenizer` dengan metode `from_file()`:
+
+```python
+new_tokenizer = Tokenizer.from_file("tokenizer.json")
+```
+
+Untuk menggunakan tokenizer ini di 🤗 Transformers, kita harus membungkusnya dalam `PreTrainedTokenizerFast`. Kita bisa menggunakan kelas generik atau, jika tokenizer kita sesuai dengan model yang sudah ada, gunakan kelas tersebut (di sini, `BertTokenizerFast`). Jika Anda menerapkan pelajaran ini untuk membangun tokenizer yang benar-benar baru, Anda harus menggunakan opsi pertama.
+
+Untuk membungkus tokenizer dalam `PreTrainedTokenizerFast`, kita bisa menyertakan tokenizer yang kita buat sebagai `tokenizer_object` atau menyertakan file tokenizer yang telah kita simpan sebagai `tokenizer_file`. Hal utama yang perlu diingat adalah bahwa kita harus secara manual mengatur semua token khusus, karena kelas tersebut tidak bisa menginfer token mana yang merupakan token mask, token `[CLS]`, dan sebagainya:
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ # tokenizer_file="tokenizer.json", # Anda juga bisa memuat dari file tokenizer
+ unk_token="[UNK]",
+ pad_token="[PAD]",
+ cls_token="[CLS]",
+ sep_token="[SEP]",
+ mask_token="[MASK]",
+)
+```
+Jika Anda menggunakan kelas tokenizer spesifik (seperti `BertTokenizerFast`), Anda hanya perlu menentukan token-token khusus yang berbeda dari default-nya (dalam kasus ini, tidak ada):
+
+```python
+from transformers import BertTokenizerFast
+
+wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
+```
+
+Setelah itu, Anda dapat menggunakan tokenizer ini seperti tokenizer 🤗 Transformers lainnya. Anda bisa menyimpannya dengan metode `save_pretrained()`, atau mengunggahnya ke Hub dengan metode `push_to_hub()`.
+
+Sekarang setelah kita melihat bagaimana membangun tokenizer WordPiece, mari kita lakukan hal yang sama untuk tokenizer BPE. Kita akan melakukannya sedikit lebih cepat karena Anda sudah mengetahui semua langkahnya, dan hanya akan menyoroti perbedaannya.
+
+## Membangun Tokenizer BPE dari Awal [[building-a-bpe-tokenizer-from-scratch]]
+
+Sekarang mari kita bangun tokenizer GPT-2. Seperti pada tokenizer BERT, kita mulai dengan menginisialisasi `Tokenizer` dengan model BPE:
+
+```python
+tokenizer = Tokenizer(models.BPE())
+```
+
+Sama seperti untuk BERT, kita bisa menginisialisasi model ini dengan kosakata jika kita sudah memilikinya (dalam hal ini kita perlu menyertakan `vocab` dan `merges`), tetapi karena kita akan melatih dari awal, kita tidak perlu melakukan itu. Kita juga tidak perlu menentukan `unk_token` karena GPT-2 menggunakan BPE tingkat byte, yang tidak memerlukannya.
+
+GPT-2 tidak menggunakan normalizer, jadi kita melewati langkah itu dan langsung ke pre-tokenization:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
+```
+
+Opsi yang kita tambahkan ke `ByteLevel` di sini adalah untuk tidak menambahkan spasi di awal kalimat (yang merupakan default-nya). Kita bisa melihat hasil pre-tokenization dari teks contoh seperti sebelumnya:
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
+```
+
+```python out
+[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
+ ('tokenization', (15, 27)), ('!', (27, 28))]
+```
+
+Selanjutnya adalah model, yang perlu dilatih. Untuk GPT-2, satu-satunya token khusus adalah token akhir teks:
+
+```python
+trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+Like with the `WordPieceTrainer`, as well as the `vocab_size` and `special_tokens`, we can specify the `min_frequency` if we want to, or if we have an end-of-word suffix (like ``), we can set it with `end_of_word_suffix`.
+
+This tokenizer can also be trained on text files:
+
+```python
+tokenizer.model = models.BPE()
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Let's have a look at the tokenization of a sample text:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
+```
+
+We apply the byte-level post-processing for the GPT-2 tokenizer as follows:
+
+```python
+tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
+```
+
+The `trim_offsets = False` option indicates to the post-processor that we should leave the offsets of tokens that begin with 'Ġ' as they are: this way the start of the offsets will point to the space before the word, not the first character of the word (since the space is technically part of the token). Let's have a look at the result with the text we just encoded, where `'Ġtest'` is the token at index 4:
+
+```python
+sentence = "Let's test this tokenizer."
+encoding = tokenizer.encode(sentence)
+start, end = encoding.offsets[4]
+sentence[start:end]
+```
+
+```python out
+' test'
+```
+
+Finally, we add a byte-level decoder:
+
+```python
+tokenizer.decoder = decoders.ByteLevel()
+```
+
+and we can double-check it works properly:
+
+```python
+tokenizer.decode(encoding.ids)
+```
+
+```python out
+"Let's test this tokenizer."
+```
+
+Great! Now that we're done, we can save the tokenizer like before, and wrap it in a `PreTrainedTokenizerFast` or `GPT2TokenizerFast` if we want to use it in 🤗 Transformers:
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ bos_token="<|endoftext|>",
+ eos_token="<|endoftext|>",
+)
+```
+
+or:
+
+```python
+from transformers import GPT2TokenizerFast
+
+wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
+```
+
+As the last example, we'll show you how to build a Unigram tokenizer from scratch.
+
+## Building a Unigram tokenizer from scratch[[building-a-unigram-tokenizer-from-scratch]]
+
+Let's now build an XLNet tokenizer. Like for the previous tokenizers, we start by initializing a `Tokenizer` with a Unigram model:
+
+```python
+tokenizer = Tokenizer(models.Unigram())
+```
+
+Again, we could initialize this model with a vocabulary if we had one.
+
+For the normalization, XLNet uses a few replacements (which come from SentencePiece):
+
+```python
+from tokenizers import Regex
+
+tokenizer.normalizer = normalizers.Sequence(
+ [
+ normalizers.Replace("``", '"'),
+ normalizers.Replace("''", '"'),
+ normalizers.NFKD(),
+ normalizers.StripAccents(),
+ normalizers.Replace(Regex(" {2,}"), " "),
+ ]
+)
+```
+
+This replaces `` and '' with " and any sequence of two or more spaces with a single space, as well as removing the accents in the texts to tokenize.
+
+The pre-tokenizer to use for any SentencePiece tokenizer is `Metaspace`:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
+```
+
+We can have a look at the pre-tokenization of an example text like before:
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
+```
+
+```python out
+[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
+```
+
+Next is the model, which needs training. XLNet has quite a few special tokens:
+
+```python
+special_tokens = ["", "", "", "", "", "", ""]
+trainer = trainers.UnigramTrainer(
+ vocab_size=25000, special_tokens=special_tokens, unk_token=""
+)
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+A very important argument not to forget for the `UnigramTrainer` is the `unk_token`. We can also pass along other arguments specific to the Unigram algorithm, such as the `shrinking_factor` for each step where we remove tokens (defaults to 0.75) or the `max_piece_length` to specify the maximum length of a given token (defaults to 16).
+
+This tokenizer can also be trained on text files:
+
+```python
+tokenizer.model = models.Unigram()
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Let's have a look at the tokenization of a sample text:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
+```
+
+A peculiarity of XLNet is that it puts the `` token at the end of the sentence, with a type ID of 2 (to distinguish it from the other tokens). It's padding on the left, as a result. We can deal with all the special tokens and token type IDs with a template, like for BERT, but first we have to get the IDs of the `` and `` tokens:
+
+```python
+cls_token_id = tokenizer.token_to_id("")
+sep_token_id = tokenizer.token_to_id("")
+print(cls_token_id, sep_token_id)
+```
+
+```python out
+0 1
+```
+
+The template looks like this:
+
+```python
+tokenizer.post_processor = processors.TemplateProcessing(
+ single="$A:0 :0 :2",
+ pair="$A:0 :0 $B:1 :1 :2",
+ special_tokens=[("", sep_token_id), ("", cls_token_id)],
+)
+```
+
+And we can test it works by encoding a pair of sentences:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
+print(encoding.tokens)
+print(encoding.type_ids)
+```
+
+```python out
+['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '', '▁', 'on', '▁', 'a', '▁pair',
+ '▁of', '▁sentence', 's', '!', '', '']
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
+```
+
+Finally, we add a `Metaspace` decoder:
+
+```python
+tokenizer.decoder = decoders.Metaspace()
+```
+
+and we're done with this tokenizer! We can save the tokenizer like before, and wrap it in a `PreTrainedTokenizerFast` or `XLNetTokenizerFast` if we want to use it in 🤗 Transformers. One thing to note when using `PreTrainedTokenizerFast` is that on top of the special tokens, we need to tell the 🤗 Transformers library to pad on the left:
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ bos_token="",
+ eos_token="",
+ unk_token="",
+ pad_token="",
+ cls_token="",
+ sep_token="",
+ mask_token="",
+ padding_side="left",
+)
+```
+
+Or alternatively:
+
+```python
+from transformers import XLNetTokenizerFast
+
+wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
+```
+
+Now that you have seen how the various building blocks are used to build existing tokenizers, you should be able to write any tokenizer you want with the 🤗 Tokenizers library and be able to use it in 🤗 Transformers.
diff --git a/chapters/id/chapter6/9.mdx b/chapters/id/chapter6/9.mdx
new file mode 100644
index 000000000..fffeba12b
--- /dev/null
+++ b/chapters/id/chapter6/9.mdx
@@ -0,0 +1,16 @@
+# Tokenizer, selesai![[tokenizers-check]]
+
+
+
+Kerja bagus telah menyelesaikan bab ini!
+
+Setelah menyelami dunia tokenizer secara mendalam, kini kamu seharusnya:
+
+- Mampu melatih tokenizer baru menggunakan tokenizer lama sebagai template
+- Memahami cara menggunakan offset untuk memetakan posisi token ke bagian asli dalam teks
+- Mengetahui perbedaan antara BPE, WordPiece, dan Unigram
+- Mampu mencampur dan mencocokkan blok yang disediakan oleh pustaka 🤗 Tokenizers untuk membangun tokenizer versimu sendiri
+- Mampu menggunakan tokenizer tersebut dalam pustaka 🤗 Transformers