Transformers documentation

Template chat

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Template chat

Panduan dasar-dasar chat membahas cara menyimpan riwayat chat dan menghasilkan teks dari model chat menggunakan TextGenerationPipeline.

Panduan ini ditujukan untuk pengguna yang lebih mahir, dan membahas kelas serta metode mendasar, beserta konsep-konsep kunci untuk memahami apa yang sebenarnya terjadi saat Anda berinteraksi dengan model.

Wawasan kritis yang diperlukan untuk memahami model chat adalah ini: Semua LM kausal, baik yang dilatih untuk chat maupun tidak, melanjutkan sebuah urutan token. Ketika LM kausal dilatih, pelatihan biasanya dimulai dengan “pre-training” pada korpus teks yang sangat besar, yang menghasilkan model “base”. Model-model base ini kemudian sering kali “fine-tuned” untuk chat, yang berarti melatihnya pada data yang diformat sebagai urutan pesan. Namun, chat tetap hanya berupa urutan token! Daftar kamus role dan content yang Anda berikan kepada model chat akan dikonversi menjadi urutan token, sering kali dengan token kontrol seperti <|user|> atau <|assistant|> atau <|end_of_message|>, yang memungkinkan model melihat struktur chat. Terdapat banyak format chat yang mungkin, dan model yang berbeda dapat menggunakan format atau token kontrol yang berbeda, bahkan jika mereka di-fine-tune dari model base yang sama!

Jangan panik - Anda tidak perlu menghafal setiap format chat yang mungkin untuk menggunakan model chat. Model chat dilengkapi dengan template chat, yang menunjukkan bagaimana mereka mengharapkan chat diformat. Anda dapat mengaksesnya dengan metode apply_chat_template. Mari kita lihat dua contoh. Kedua model ini dihaluskan dari model dasar Mistral-7B yang sama:

Mistral
Zephyr
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
chat = [
  {"role": "user", "content": "Hello, how are you?"},
  {"role": "assistant", "content": "I'm doing great. How can I help you today?"},
  {"role": "user", "content": "I'd like to show off how chat templating works!"},
]

tokenizer.apply_chat_template(chat, tokenize=False)
<s>[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?</s> [INST] I'd like to show off how chat templating works! [/INST]

Mistral-7B-Instruct menggunakan token [INST] dan [/INST] untuk menandai awal dan akhir pesan pengguna, sedangkan Zephyr-7B menggunakan token <|user|> dan <|assistant|> untuk menandai peran pembicara. Inilah mengapa template chat penting - dengan token kontrol yang salah, model-model ini akan memiliki kinerja yang jauh lebih buruk.

Menggunakan apply_chat_template

Input untuk apply_chat_template harus disusun sebagai daftar kamus dengan kunci role dan content. Kunci role menentukan pembicara, dan kunci content berisi pesan. Peran yang umum adalah:

  • user untuk pesan dari pengguna
  • assistant untuk pesan dari model
  • system untuk instruksi tentang bagaimana model harus berperilaku (biasanya ditempatkan di awal percakapan)

apply_chat_template menerima daftar ini dan mengembalikan urutan yang diformat. Atur tokenize=True jika Anda ingin melakukan tokenisasi pada urutan tersebut.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")
model = AutoModelForCausalLM.from_pretrained("HuggingFaceH4/zephyr-7b-beta", device_map="auto", dtype=torch.bfloat16)

messages = [
    {"role": "system", "content": "You are a friendly chatbot who always responds in the style of a pirate",},
    {"role": "user", "content": "How many helicopters can a human eat in one sitting?"},
 ]
tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt").to(model.device)
print(tokenizer.decode(tokenized_chat["input_ids"][0]))
<|system|>
You are a friendly chatbot who always responds in the style of a pirate</s>
<|user|>
How many helicopters can a human eat in one sitting?</s>
<|assistant|>

Lewatkan chat yang sudah ditokenisasi ke generate() untuk menghasilkan respons.

outputs = model.generate(**tokenized_chat, max_new_tokens=128)
print(tokenizer.decode(outputs[0]))
<|system|>
You are a friendly chatbot who always responds in the style of a pirate</s>
<|user|>
How many helicopters can a human eat in one sitting?</s>
<|assistant|>
Matey, I'm afraid I must inform ye that humans cannot eat helicopters. Helicopters are not food, they are flying machines. Food is meant to be eaten, like a hearty plate o' grog, a savory bowl o' stew, or a delicious loaf o' bread. But helicopters, they be for transportin' and movin' around, not for eatin'. So, I'd say none, me hearties. None at all.

Beberapa tokenizer menambahkan token khusus <bos> dan <eos>. Template chat seharusnya sudah mencakup semua token khusus yang diperlukan, dan menambahkan token khusus tambahan sering kali tidak tepat atau duplikat, yang dapat merugikan kinerja model. Saat Anda memformat teks dengan apply_chat_template(tokenize=False), pastikan Anda mengatur add_special_tokens=False jika Anda akan melakukan tokenisasi kemudian untuk menghindari duplikasi token-token ini. Ini bukan masalah jika Anda menggunakan apply_chat_template(tokenize=True), yang berarti ini biasanya opsi yang lebih aman!

add_generation_prompt

Anda mungkin telah memperhatikan argumen add_generation_prompt pada contoh di atas. Argumen ini menambahkan token ke akhir percakapan yang menandakan dimulainya respons assistant. Ingat: di balik semua abstraksi chat, model chat tetaplah model bahasa yang melanjutkan rangkaian token! Jika Anda menyertakan token yang memberitahu bahwa sekarang sedang dalam respons assistant, model akan menulis respons dengan benar, tetapi jika Anda tidak menyertakan token-token ini, model mungkin menjadi bingung dan melakukan hal yang aneh, seperti melanjutkan pesan pengguna alih-alih membalasnya!

Mari kita lihat contoh untuk memahami apa yang sebenarnya dilakukan oleh add_generation_prompt. Pertama, mari kita format sebuah chat tanpa add_generation_prompt:

tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
tokenized_chat
<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>

Sekarang, mari kita format chat yang sama dengan add_generation_prompt=True:

tokenized_chat = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
tokenized_chat
<|im_start|>user
Hi there!<|im_end|>
<|im_start|>assistant
Nice to meet you!<|im_end|>
<|im_start|>user
Can I ask a question?<|im_end|>
<|im_start|>assistant

Ketika add_generation_prompt=True, <|im_start|>assistant ditambahkan di akhir untuk menandakan awal pesan assistant. Hal ini memberi tahu model bahwa respons assistant akan menyusul.

Tidak semua model memerlukan prompt generasi, dan beberapa model, seperti Llama, tidak memiliki token khusus sebelum respons assistant. Dalam kasus seperti ini, add_generation_prompt tidak memiliki efek.

continue_final_message

Parameter continue_final_message mengontrol apakah pesan terakhir dalam chat harus dilanjutkan atau tidak, alih-alih memulai pesan baru. Parameter ini menghapus token akhir urutan agar model melanjutkan generasi dari pesan terakhir.

Hal ini berguna untuk “prefilling” respons model. Pada contoh di bawah, model menghasilkan teks yang melanjutkan string JSON alih-alih memulai pesan baru. Hal ini dapat sangat berguna untuk meningkatkan akurasi kepatuhan terhadap instruksi ketika Anda tahu bagaimana memulai balasan model.

chat = [
    {"role": "user", "content": "Can you format the answer in JSON?"},
    {"role": "assistant", "content": '{"name": "'},
]

formatted_chat = tokenizer.apply_chat_template(chat, tokenize=True, return_dict=True, continue_final_message=True)
model.generate(**formatted_chat)

Anda tidak boleh menggunakan add_generation_prompt dan continue_final_message bersamaan. Yang pertama menambahkan token yang memulai pesan baru, sedangkan yang kedua menghapus token akhir urutan. Menggunakan keduanya bersamaan akan mengembalikan error.

Lewati nama field sebagai string untuk mengisi awal field tersebut alih-alih content. Model penalaran membaca penalarannya dari field terpisah reasoning_content. Mengisi awal content menutup blok penalaran sebelum generasi dimulai, sehingga model tidak dapat melanjutkan di dalamnya. Mengisi awal reasoning_content secara langsung membuat blok tetap terbuka.

chat = [
    {"role": "user", "content": "Explain 1+1"},
    {"role": "assistant", "reasoning_content": "The user wants a simple addition. ", "content": ""},
]

formatted_chat = tokenizer.apply_chat_template(chat, tokenize=False, continue_final_message="reasoning_content")

Field bernama harus ada pada pesan akhir dan harus dirujuk oleh template chat. Error akan dilempar jika salah satu pemeriksaan gagal.

TextGenerationPipeline mengatur add_generation_prompt menjadi True secara default untuk memulai pesan baru. Namun, jika pesan terakhir dalam chat memiliki peran assistant, pipeline mengasumsikan pesan tersebut adalah prefill dan beralih ke continue_final_message=True. Hal ini karena sebagian besar model tidak mendukung beberapa pesan assistant yang berurutan. Untuk menimpa perilaku ini, berikan argumen continue_final_message secara eksplisit ke pipeline.

Pelatihan model

Melatih model dengan template chat adalah cara yang baik untuk memastikan template tersebut cocok dengan token yang digunakan saat model dilatih. Terapkan template chat sebagai langkah pra-pemrosesan pada dataset Anda. Atur add_generation_prompt=False karena token tambahan untuk memicu respons asisten tidak membantu selama pelatihan.

Contoh prapemrosesan dataset dengan template chat ditunjukkan di bawah ini.

from transformers import AutoTokenizer
from datasets import Dataset

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta")

chat1 = [
    {"role": "user", "content": "Which is bigger, the moon or the sun?"},
    {"role": "assistant", "content": "The sun."}
]
chat2 = [
    {"role": "user", "content": "Which is bigger, a virus or a bacterium?"},
    {"role": "assistant", "content": "A bacterium."}
]

dataset = Dataset.from_dict({"chat": [chat1, chat2]})
dataset = dataset.map(lambda x: {"formatted_chat": tokenizer.apply_chat_template(x["chat"], tokenize=False, add_generation_prompt=False)})
print(dataset['formatted_chat'][0])
<|user|>
Which is bigger, the moon or the sun?</s>
<|assistant|>
The sun.</s>

Setelah langkah ini, Anda dapat melanjutkan mengikuti resep pelatihan untuk model bahasa kausal menggunakan kolom formatted_chat.

Update on GitHub