Son yayınımızda NVIDIA NeMo Curator’ın ön eğitim veya sürekli eğitim kullanım durumları için özel veri kümelerini düzenlemek amacıyla nasıl kullanılacağını ele aldık. büyük dil modelleri (LLM) ve küçük dil modelleri (SLM).
Bu tür eğitim senaryoları LLM gelişiminin önemli bir parçası olsa da, birçok alt akış uygulaması, alan-özel veri kümelerinde mevcut temel modellerin ince ayarını içerir. Bu, LoRA ve p-ayarlama gibi denetlenen ince ayar (SFT) veya parametre açısından verimli ince ayar (PEFT) yöntemleri kullanılarak elde edilebilir.
Bu iş akışlarında, genellikle hızlı bir şekilde yineleme yapmanız ve çeşitli fikirler ve hiperparametre ayarlarıyla denemeler yapmanız ve ayrıca eğitim verilerinin nasıl işlenip modele sunulduğunu belirlemeniz gerekir. Alana özgü verilerinizin nüanslarıyla etkili öğrenmeyi garantilemek için veri kümelerinizin birden fazla varyantını işlemeli ve düzenlemelisiniz.
Bu tür iş akışlarında sınırlı miktarda veri bulunması nedeniyle, esnek bir işlem hattı kullanılarak yüksek kalitede veri toplanması hayati önem taşımaktadır.
Bu gönderi, NeMo Curator kullanarak özel bir veri küratörlüğü hattı oluşturma konusunda size yol gösterir ve özellikle SFT ve PEFT kullanım durumlarına odaklanır. NeMo Curator’ın sağladığı temel yapı taşları hakkında daha fazla bilgi için NVIDIA NeMo Curator ile LLM Eğitimi için Özel Veri Kümelerini Küratörlüğe Alma konusuna bakın.
Genel bakış
Gösterim amaçlı olarak, bu gönderi e-posta sınıflandırmasını içeren bir oyuncak örneğine odaklanmaktadır. Amaç, her kaydın bir e-postadan (konu ve gövde) ve o e-posta için önceden tanımlanmış bir sınıflandırma etiketinden oluştuğu küçük bir metin tabanlı veri kümesini düzenlemektir.
Bu amaçla, her e-postanın sekiz kategoriden birine etiketlendiği Enron e-posta veri kümesini kullandık. Bu veri kümesi HuggingFace’te herkese açık olarak mevcuttur ve ~1.400 kayıt içeriyor.
Veri toplama süreci aşağıdaki üst düzey adımları içerir:
- Veri setini JSONL formatına dönüştürmek için indirici, yineleyici ve çıkarıcı sınıflarını tanımlayın.
- Unicode gösterimini birleştirmek için mevcut araçları kullanın.
- Boş veya çok uzun e-postaları kaldırmak için özel veri kümesi filtreleri tanımlayın.
- Veri setinden kişisel olarak tanımlanabilir tüm bilgileri (PII) sansürleyin.
- Her kayda talimat istemleri ekleyin.
- Küratörlük hattını bir araya getirin.
Bu küratörlük hattının yürütülmesi tüketici sınıfı donanımda 5 dakikadan az sürmelidir. Bu eğitimin tam koduna erişmek için bkz. /NVIDIA/NeMo-Küratör GitHub deposu.
Ön koşullar
Başlamadan önce NeMo Curator çerçevesini yüklemeniz gerekir. Aşağıdaki talimatları izleyin NeMo Curator GitHub README dosyası çerçeveyi kurmak için.
Ardından, kurulumu doğrulamak ve ek bağımlılıkları yüklemek için aşağıdaki komutları çalıştırın:
$ python -c "import nemo_curator; print(nemo_curator);"
$ pip3 install requests
Özel belge oluşturucularını tanımlama
Bir veri setini düzenlemenin ilk adımı, belge oluşturucular veri setini indirip üzerinde yineleme yapabilen.
Veri kümesi indiriliyor
Uygula DocumentDownloader
veri kümesinin URL’sini alan ve onu kullanarak indiren sınıf requests
kütüphane.
import requests
from nemo_curator.download.doc_builder import DocumentDownloader
class EmailsDownloader(DocumentDownloader):
def __init__(self, download_dir: str):
super().__init__()
if not os.path.isdir(download_dir):
os.makedirs(download_dir)
self._download_dir = download_dir
print("Download directory: ", self._download_dir)
def download(self, url: str) -> str:
filename = os.path.basename(url)
output_file = os.path.join(self._download_dir, filename)
if os.path.exists(output_file):
print(f"File '{output_file}' already exists, skipping download.")
return output_file
print(f"Downloading Enron emails dataset from '{url}'...")
response = requests.get(url)
with open(output_file, "wb") as file:
file.write(response.content)
return output_file
İndirilen veri seti bir metin dosyasıdır ve her giriş yaklaşık olarak aşağıdaki biçimi takip eder:
“<s>[system instruction prompts]
Subject:: [email subject]
Body:: [email body]
[category label] <s>”
Bu format, düzenli ifadeler kullanılarak kolayca bileşenlerine ayrılabilir. Unutulmaması gereken en önemli şey, girdilerin dizilerle ayrılmasıdır. “<s> … <s>”
ve her zaman talimat istemleriyle başlayın. Ayrıca, örnek ayırıcı belirteçleri ve sistem istemi belirteçleri, Llama 2 belirteçleyici ailesiyle uyumludur.
Bu verileri özel belirteçleri desteklemeyen diğer belirteçleyiciler veya modellerle kullanabileceğiniz için, ayrıştırma sırasında bu talimatları ve belirteçleri atmak en iyisidir. Bu gönderinin ilerleyen kısımlarında, talimat istemlerinin veya özel belirteçlerin NeMo Curator kullanılarak her bir girdiye nasıl eklenebileceğini gösteriyoruz DocumentModifier
araçlar.
Veri setini ayrıştırma ve yineleme
Uygula DocumentIterator
Ve DocumentExtractor
e-posta konusunu, gövdesini ve kategori (sınıf) etiketlerini çıkarmak için sınıflar:
from nemo_curator.download.doc_builder import (
DocumentExtractor,
DocumentIterator,
)
class EmailsIterator(DocumentIterator):
def __init__(self):
super().__init__()
self._counter = -1
self._extractor = EmailsExtractor()
# The regular expression pattern to extract each email.
self._pattern = re.compile(r"\"<s>.*?<s>\"", re.DOTALL)
def iterate(self, file_path):
self._counter = -1
file_name = os.path.basename(file_path)
with open(file_path, "r", encoding="utf-8") as file:
lines = file.readlines()
# Ignore the first line which contains the header.
file_content = "".join(lines[1:])
# Find all the emails in the file.
it = self._pattern.finditer(file_content)
for email in it:
self._counter += 1
content = email.group().strip('"').strip()
meta = {
"filename": file_name,
"id": f"email-{self._counter}",
}
extracted_content = self._extractor.extract(content)
# Skip if no content extracted
if not extracted_content:
continue
record = {**meta, **extracted_content}
yield record
class EmailsExtractor(DocumentExtractor):
def __init__(self):
super().__init__()
# The regular expression pattern to extract subject/body/label into groups.
self._pattern = re.compile(
r"Subject:: (.*?)\nBody:: (.*?)\n.*\[/INST\] (.*?) <s>", re.DOTALL
)
def extract(self, content: str) -> Dict[str, str]:
matches = self._pattern.findall(content)
if not matches:
return None
matches = matches[0]
return {
"subject": matches[0].strip(),
"body": matches[1].strip(),
"category": matches[2].strip(),
}
Yineleyici, düzenli ifadeyi kullanır \"<s>.*?<s>\"
her örneği bulmak için. Daha sonra dizeyi, düzenli ifadeyi kullanan çıkarıcıya geçirir "Subject:: (.*?)\nBody:: (.*?)\n.*\[/INST\] (.*?) <s>"
Bu ifade gruplama operatörünü kullanır (.*?)
özneyi, gövdeyi ve kategoriyi çıkarmak için.
Çıkarılan bu parçalar, yararlı meta verilerle (örneğin her e-posta için benzersiz bir kimlik) birlikte bir sözlükte saklanır ve arayana geri gönderilir.
Artık bu veri setini, NeMo Curator’ın desteklediği birçok formattan biri olan JSONL formatına dönüştürmeye hazırsınız
Veri kümesini JSONL biçimine yazma
Veri kümesi düz metin dosyası olarak indirilir. Uygula DocumentIterator
Ve DocumentExtractor
Kayıtlar arasında yineleme yapmak, bunları JSONL biçimine dönüştürmek ve her kaydı bir dosyadaki satır olarak depolamak için sınıflar.
import json
def download_and_convert_to_jsonl() -> str:
"""
Downloads the emails dataset and converts it to JSONL format.
Returns:
str: The path to the JSONL file.
"""
# Download the dataset in raw format and convert it to JSONL.
downloader = EmailsDownloader(DATA_DIR)
output_path = os.path.join(DATA_DIR, "emails.jsonl")
raw_fp = downloader.download(DATASET_URL)
iterator = EmailsIterator()
# Parse the raw data and write it to a JSONL file.
with open(output_path, "w") as f:
for record in iterator.iterate(raw_fp):
json_record = json.dumps(record, ensure_ascii=False)
f.write(json_record + "\n")
return output_path
Veri kümesindeki her kayıttan gelen bilgiler birden fazla JSON alanına yazılır:
subject
body
category
- Meta Veri:
Bu gereklidir çünkü NeMo Curator’daki birçok veri düzenleme işlemi her kayıttaki hangi alanda işlem yapılacağını bilmelidir. Bu yapı, NeMo Curator işlemleri için farklı veri kümesi bilgilerinin uygun şekilde hedeflenmesini sağlar.
Veri setini belge oluşturucuları kullanarak yükleme
NeMo Curator’da veri kümeleri, şu türdeki nesneler olarak temsil edilir: DocumentDataset
. Bu, veri kümelerini diskten çeşitli biçimlerde yüklemek için yardımcılar sağlar. Veri kümesini yüklemek ve onunla çalışmaya başlamak için aşağıdaki kodu kullanın:
from nemo_curator.datasets import DocumentDataset
# define `filepath` to be the path to the JSONL file created above.
dataset = DocumentDataset.read_json(filepath, add_filename=True)
Artık özel bir veri kümesi düzenleme hattı tanımlamak ve verileri hazırlamak için gereken her şeye sahipsiniz.
Çevrimiçi kaynaklardan alınan metinlerde tutarsızlıklar veya Unicode hataları olabileceğinden, veri kümelerindeki tüm Unicode sorunlarını düzeltmek genellikle iyi bir uygulamadır.
Belgeleri değiştirmek için NeMo Curator bir DocumentModifier
arayüz ile birlikte Modify
helper, her belgeden verilen metnin nasıl değiştirileceğini tanımlar. Kendi özel belge değiştiricilerinizi uygulama hakkında daha fazla bilgi için, önceki yazıda yer alan Metin temizleme ve birleştirme bölümüne bakın.
Bu örnekte, şunu uygulamak yeterlidir: UnicodeReformatter
veri kümesine. Her kayıtta birden fazla alan olduğundan, işlemi veri kümesindeki her ilgili alana bir kez uygulayın. Bu işlemler, Sequential
sınıf:
Sequential([
Modify(UnicodeReformatter(), text_field="subject"),
Modify(UnicodeReformatter(), text_field="body"),
Modify(UnicodeReformatter(), text_field="category"),
])
Özel veri kümesi filtreleri tasarlama
Birçok PEFT kullanım durumunda, veri setini iyileştirmek, alakasız veya düşük kaliteli olabilecek kayıtları veya belirli uygunsuz niteliklere sahip kayıtları filtrelemeyi içerir. E-posta veri setinde, bazı e-postalar çok uzun veya boştur. Gösterim amaçlı olarak, özel uygulayarak bu tür tüm kayıtları veri setinden kaldırın DocumentFilter
sınıflar:
from nemo_curator.filters import DocumentFilter
class FilterEmailsWithLongBody(DocumentFilter):
"""
If the email is too long, discard.
"""
def __init__(self, max_length: int = 5000):
super().__init__()
self.max_length = max_length
def score_document(self, text: str) -> bool:
return len(text) <= self.max_length
def keep_document(self, score) -> bool:
return score
class FilterEmptyEmails(DocumentFilter):
"""
Detects empty emails (either empty body, or labeled as empty).
"""
def score_document(self, text: str) -> bool:
return (
not isinstance(text, str) # The text is not a string
or len(text.strip()) == 0 # The text is empty
or "Empty message" in text # The email is labeled as empty
)
def keep_document(self, score) -> bool:
return score
The FilterEmailsWithLongBody
sınıf, sağlanan metindeki karakter sayısını sayar ve döndürür True
eğer uzunluk kabul edilebilirse, veya False
aksi takdirde. Bu filtreyi açıkça uygulamanız gerekir body
her kayıt için alan.
The FilterEmptyEmails
sınıf, boş bir e-postayı ifade edip etmediğini belirlemek için verilen metnin türünü ve içeriğini kontrol eder ve döndürür True
e-postanın boş olduğu kabul edilirse veya False
Aksi takdirde. Bu filtreyi tüm ilgili alanlara açıkça uygulamalısınız: subject
, body
Ve category
her kayıt için alanlar.
Döndürülen değer, kod okunabilirliğini artıran sınıfın adlandırmasıyla tutarlıdır. Ancak, amaç boş e-postaları atmak olduğundan, bu filtreden gelen sonuçlar tersine çevrilmelidir. Başka bir deyişle, filtre şunu döndürürse kaydı atın True
ve filtre geri dönerse kaydı tutun False
Bu, ilgili bayrağın sağlanmasıyla yapılabilir. ScoreFilter
yardımcı:
Sequential([
# Apply only to the `body` field.
ScoreFilter(FilterEmailsWithLongBody(), text_field="body", score_type=bool),
# Apply to all fields, also invert the action.
ScoreFilter(FilterEmptyEmails(), text_field="subject", score_type=bool, invert=True),
ScoreFilter(FilterEmptyEmails(), text_field="body", score_type=bool, invert=True),
ScoreFilter(FilterEmptyEmails(), text_field="category", score_type=bool, invert=True),
])
Bayrağı belirtin invert=True
talimat vermek ScoreFilter
filtrenin döndürdüğü belgeleri atmak için True
s’yi belirterekcore_type=bool
her filtre için dönüş türünü açıkça belirtirsiniz; bu da yürütme sırasında tür çıkarımından kaçınılmasını sağlar.
Kişisel olarak tanımlanabilir tüm bilgilerin sansürlenmesi
Sonra, her kaydın konusundan ve gövdesinden tüm kişisel olarak tanımlanabilir bilgileri (PII) sansürlemek için bir işlem adımı tanımlayın. Bu veri kümesi, e-postalar, telefon veya faks numaraları, adlar ve adresler gibi birçok PII örneği içerir.
NeMo Curator, tespit edilecek PII türünü ve her tespit için hangi eylemin gerçekleştirileceğini belirtmeyi kolaylaştırır. Her tespiti özel belirteçlerle değiştirin:
def redact_pii(dataset: DocumentDataset, text_field) -> DocumentDataset:
redactor = Modify(
PiiModifier(
supported_entities=[
"ADDRESS",
"EMAIL_ADDRESS",
"LOCATION",
"PERSON",
"URL",
"PHONE_NUMBER",
],
anonymize_action="replace",
device="cpu",
),
text_field=text_field,
)
return redactor(dataset)
Bu işlemleri şuraya uygulayabilirsiniz: subject
Ve body
Python’u kullanarak alanları ayrı ayrı functools.partial
yardımcı:
from functools import partial
redact_pii_subject = partial(redact_pii, text_field="subject")
redact_pii_body = partial(redact_pii, text_field="body")
Sequential([
redact_pii_subject,
redact_pii_body,
]
)
Talimat istemleri ekleme
Veri küratörlüğü boru hattının son adımı, her kayda talimat istemleri eklemeyi ve her kategori değerinin bir nokta ile sonlanmasını sağlamayı içerir. Bunlar, ilgili DocumentModifier
sınıflar:
from nemo_curator.modifiers import DocumentModifier
class AddSystemPrompt(DocumentModifier):
def modify_document(self, text: str) -> str:
return SYS_PROMPT_TEMPLATE % text
class AddPeriod(DocumentModifier):
def modify_document(self, text: str) -> str:
return text + "."
Kod örneğinde, SYS_PROMPT_TEMPLATE
değişken, metnin etrafına talimat istemleri eklemek için kullanılabilen bir biçimlendirme dizesi içerir. Bu değiştiriciler birbirine zincirlenebilir:
Sequential([
Modify(AddSystemPrompt(), text_field="body"),
Modify(AddPeriod(), text_field="category"),
])
Küratörlük hattını bir araya getirmek
Küratörlük işlem hattının her adımını uyguladıktan sonra, her şeyi bir araya getirme ve her işlemi veri kümesine sırayla uygulama zamanı geldi. Şunu kullanabilirsiniz: Sequential
küratörlük işlemlerini bir arada zincirlemek için sınıf:
curation_steps = Sequential(
[
#
# Unify the text encoding to Unicode.
#
Modify(UnicodeReformatter(), text_field="subject"),
Modify(UnicodeReformatter(), text_field="body"),
Modify(UnicodeReformatter(), text_field="category"),
#
# Filtering
#
ScoreFilter(
FilterEmptyEmails(), text_field="subject", score_type=bool, invert=True
),
ScoreFilter(
FilterEmptyEmails(), text_field="body", score_type=bool, invert=True
),
ScoreFilter(
FilterEmptyEmails(), text_field="category", score_type=bool, invert=True
),
ScoreFilter(FilterEmailsWithLongBody(), text_field="body", score_type=bool),
#
# Redact personally identifiable information (PII).
#
redact_pii_subject,
redact_pii_body,
#
# Final modifications.
#
Modify(AddSystemPrompt(), text_field="body"),
Modify(AddPeriod(), text_field="category"),
]
)
dataset = curation_steps(dataset)
dataset = dataset.persist()
dataset.to_json("/output/path", write_to_filename=True)
NeMo Curator, veri kümesiyle dağıtılmış bir şekilde çalışmak için Dask’ı kullanır. Dask işlemleri tembel olarak değerlendirildiğinden, .persist
Dask’a işlemleri uygulamasını talimat veren işlev. İşlem tamamlandıktan sonra, veri setini JSONL biçiminde diske yazarak çağırabilirsiniz. .to_json
fonksiyonu ve bir çıktı yolu sağlanması.
Sonraki adımlar
Bu eğitimde, NeMo Curator kullanılarak özel bir veri toplama hattının nasıl oluşturulacağı gösterilmiş olup, özellikle SFT ve PEFT kullanım örneklerine odaklanılmıştır.
Kolay erişim için öğreticiyi şuraya yükledik: /NVIDIA/NeMo-Küratör GitHub deposu. En son gelişmelerden haberdar olmak ve yeni özellikler, hata düzeltmeleri ve güncellemeler hakkında bildirimler almak için depoyu yıldızlayın.
Artık verileri düzenlediğinize göre, LoRA ile e-posta sınıflandırması için Llama 2 modeli gibi bir LLM’yi ince ayarlayabilirsiniz. Daha fazla bilgi için bkz. NeMo framework PEFT ile Llama 2 oyun kitabı.
Ayrıca, kuruluşların veri küratörlüğüne her yerden başlamaları için en kolay yolu sağlayan NVIDIA NeMo Curator mikro servisine erişim talebinde bulunabilirsiniz. Başvurmak için NeMo Curator Mikro Servis Erken Erişimi’ne bakın.