Gemma की मदद से, एजाइल सेफ़्टी क्लासिफ़ायर दिखाएं

1. खास जानकारी

इस कोडलैब में, पैरामीटर एफ़िशिएंट ट्यूनिंग (पीईटी) का इस्तेमाल करके पसंद के मुताबिक टेक्स्ट की कैटगरी तय करने का तरीका बताया गया है. पूरे मॉडल को बेहतर बनाने के बजाय, पीईटी तरीके से सिर्फ़ कुछ पैरामीटर अपडेट किए जाते हैं. इससे मॉडल की ट्रेनिंग आसान और तेज़ हो जाती है. इसकी मदद से, कोई मॉडल कम ट्रेनिंग डेटा के साथ नए व्यवहार को आसानी से सीख सकता है. इस तरीके के बारे में ज़्यादा जानकारी, सभी के लिए एजाइल टेक्स्ट क्लासिफ़ायर में दी गई है. इसमें बताया गया है कि कैसे इन तकनीकों का इस्तेमाल, सुरक्षा से जुड़े अलग-अलग कामों पर किया जा सकता है. साथ ही, ट्रेनिंग के सिर्फ़ कुछ सौ उदाहरणों की मदद से, बेहतरीन परफ़ॉर्मेंस हासिल की जा सकती है.

यह कोडलैब, LoRA PET तरीके और छोटे Gemma मॉडल (gemma_instruct_2b_en) का इस्तेमाल करता है, क्योंकि इसे ज़्यादा तेज़ और बेहतर तरीके से चलाया जा सकता है. कोलैब में, डेटा डालने के अलग-अलग चरणों को शामिल किया जाता है, एलएलएम के लिए इसे फ़ॉर्मैट किया जाता है, और LoRA के वेटेज को ट्रेनिंग दी जाती है. इसके बाद, नतीजों का आकलन किया जाता है. यह कोडलैब ETHOS डेटासेट का इस्तेमाल करता है, जो नफ़रत फैलाने वाली भाषा का पता लगाने के लिए सार्वजनिक तौर पर उपलब्ध डेटासेट है. इसे YouTube और Reddit की टिप्पणियों से बनाया गया है. सिर्फ़ 200 उदाहरणों (डेटासेट का 1/4) के आधार पर ट्रेनिंग देने पर, यह F1: 0.80 और ROC-AUC: 0.78 हासिल करता है. यह लीडरबोर्ड पर मौजूदा समय में रिपोर्ट किए गए SOTA (लेख के समय, 15 फ़रवरी, 2024) से थोड़ा ऊपर है. जब इसे पूरे 800 उदाहरणों के आधार पर ट्रेनिंग दी जाती है, जैसे कि इसने F1 स्कोर 83.74 और ROC-AUC स्कोर 88.17 हासिल किया है. आम तौर पर, gemma_instruct_7b_en जैसे बड़े मॉडल बेहतर परफ़ॉर्म करते हैं. हालांकि, ट्रेनिंग और लागू करने की लागत भी ज़्यादा होती है.

ट्रिगर चेतावनी: यह कोडलैब, नफ़रत फैलाने वाली भाषा का पता लगाने के लिए, सुरक्षा से जुड़े डेटा की कैटगरी तय करने वाला सिस्टम डेवलप करता है. इसलिए, यूआरएल में आपत्तिजनक भाषा का इस्तेमाल करके उसके उदाहरण और आकलन किया गया है.

2. इंस्टॉलेशन और सेटअप

बुनियादी मॉडल डाउनलोड करने के लिए, आपको इस कोडलैब के लिए हाल ही का वर्शन keras (3), keras-nlp (0.8.0), और Kaggle खाता इस्तेमाल करना होगा.

!pip install -q -U keras-nlp
!pip install -q -U keras

Kaggle में लॉगिन करने के लिए, kaggle.json के क्रेडेंशियल वाली फ़ाइल को ~/.kaggle/kaggle.json पर सेव किया जा सकता है या Colab के एनवायरमेंट में इसे चलाया जा सकता है:

import kagglehub

kagglehub.login()

3. ETHOS डेटासेट लोड करें

इस सेक्शन में, आपको वह डेटासेट लोड करना है जिस पर हमारे क्लासिफ़ायर को ट्रेनिंग देनी है. साथ ही, उसे ट्रेन और टेस्ट सेट में प्रीप्रोसेस करना है. सोशल मीडिया में नफ़रत फैलाने वाली भाषा का पता लगाने के लिए इकट्ठा किए गए लोकप्रिय रिसर्च डेटासेट ETHOS का इस्तेमाल किया जाएगा. डेटासेट को इकट्ठा करने के तरीके के बारे में ज़्यादा जानकारी पाने के लिए, ETHOS: ऑनलाइन नफ़रत फैलाने वाली भाषा का पता लगाने वाला डेटासेट पढ़ें.

import pandas as pd

gh_root = 'https://raw.githubusercontent.com'
gh_repo = 'intelligence-csd-auth-gr/Ethos-Hate-Speech-Dataset'
gh_path = 'master/ethos/ethos_data/Ethos_Dataset_Binary.csv'
data_url = f'{gh_root}/{gh_repo}/{gh_path}'

df = pd.read_csv(data_url, delimiter=';')
df['hateful'] = (df['isHate'] >= df['isHate'].median()).astype(int)

# Shuffle the dataset.
df = df.sample(frac=1, random_state=32)

# Split into train and test.
df_train, df_test = df[:800],  df[800:]

# Display a sample of the data.
df.head(5)[['hateful', 'comment']]

आपको कुछ ऐसा दिखेगा:

लेबल

टिप्पणी

0

0

You said he but still not convinced this is a ...

1

0

well, looks like its time to have another child.

2

0

What if we send every men to mars to start a n...

3

1

It doesn't matter if you're black or white, ...

4

0

Who ever disliked this video should be ashamed...

4. मॉडल डाउनलोड करें और उसे इंस्टैंशिएट करें

जैसा कि दस्तावेज़ में बताया गया है, Gemma मॉडल को कई तरीकों से आसानी से इस्तेमाल किया जा सकता है. Keras के साथ ऐसा करने के लिए:

import keras
import keras_nlp

# For reproducibility purposes.
keras.utils.set_random_seed(1234)

# Download the model from Kaggle using Keras.
model = keras_nlp.models.GemmaCausalLM.from_preset('gemma_instruct_2b_en')

# Set the sequence length to a small enough value to fit in memory in Colab.
model.preprocessor.sequence_length = 128

कुछ टेक्स्ट जनरेट करके यह जांच की जा सकती है कि मॉडल काम कर रहा है या नहीं:

model.generate('Question: what is the capital of France? ', max_length=32)

5. टेक्स्ट प्रीप्रोसेसिंग और सेपरेटर टोकन

हमारे इंटेंट को बेहतर ढंग से समझने में मॉडल की मदद करने के लिए, टेक्स्ट को प्रीप्रोसेस किया जा सकता है और सेपरेटर टोकन का इस्तेमाल किया जा सकता है. इससे मॉडल के ज़रिए ऐसा टेक्स्ट जनरेट करने की संभावना कम हो जाती है जो उम्मीद के मुताबिक फ़ॉर्मैट से मेल नहीं खाता. उदाहरण के लिए, इस तरह का प्रॉम्प्ट लिखकर, मॉडल से भावनाओं की कैटगरी तय करने का अनुरोध किया जा सकता है:

Classify the following text into one of the following classes:[Positive,Negative]

Text: you look very nice today
Classification:

इस स्थिति में, मॉडल आपकी खोज का आउटपुट हो भी सकता है और नहीं भी. उदाहरण के लिए, अगर टेक्स्ट में न्यूलाइन वाले वर्ण हैं, तो मॉडल की परफ़ॉर्मेंस पर खराब असर पड़ सकता है. सेपरेटर टोकन का इस्तेमाल करना ज़्यादा बेहतर तरीका है. इसके बाद, प्रॉम्प्ट यह बन जाता है:

Classify the following text into one of the following classes:[Positive,Negative]
<separator>
Text: you look very nice today
<separator>
Prediction:

इसे ऐब्स्ट्रैक्ट करने के लिए, टेक्स्ट को प्रीप्रोसेस करने वाले फ़ंक्शन का इस्तेमाल किया जा सकता है:

def preprocess_text(
    text: str,
    labels: list[str],
    instructions: str,
    separator: str,
) -> str:
  prompt = f'{instructions}:[{",".join(labels)}]'
  return separator.join([prompt, f'Text:{text}', 'Prediction:'])

अब, अगर पहले की तरह ही प्रॉम्प्ट और टेक्स्ट का इस्तेमाल करके फ़ंक्शन को चलाया जाता है, तो आपको वही आउटपुट मिलेगा:

text = 'you look very nice today'

prompt = preprocess_text(
    text=text,
    labels=['Positive', 'Negative'],
    instructions='Classify the following text into one of the following classes',
    separator='\n<separator>\n',
)

print(prompt)

कौनसा आउटपुट देना चाहिए:

Classify the following text into one of the following classes:[Positive,Negative]
<separator>
Text:well, looks like its time to have another child
<separator>
Prediction:

6. आउटपुट पोस्टप्रोसेसिंग

इस मॉडल के आउटपुट, अलग-अलग संभावना वाले टोकन होते हैं. आम तौर पर, टेक्स्ट जनरेट करने के लिए, आपको सबसे ज़्यादा अनुमानित टोकन में से एक को चुनना होता है. इसके बाद, वाक्य, पैराग्राफ़ या पूरे दस्तावेज़ बनाने होते हैं. हालांकि, कैटगरी तय करने के लिए, असल में यह मायने रखता है कि मॉडल, Negative के मुकाबले Positive को ज़्यादा सही मानता है या नहीं.

आपने जो मॉडल पहले इंस्टैंशिएट किया था, उसे इस तरह से किया जा सकता है कि इसके आउटपुट को अलग-अलग प्रायिकताओं में प्रोसेस किया जा सकता है, जैसे कि अगला टोकन Positive है या Negative:

import numpy as np


def compute_output_probability(
    model: keras_nlp.models.GemmaCausalLM,
    prompt: str,
    target_classes: list[str],
) -> dict[str, float]:
  # Shorthands.
  preprocessor = model.preprocessor
  tokenizer = preprocessor.tokenizer

  # NOTE: If a token is not found, it will be considered same as "<unk>".
  token_unk = tokenizer.token_to_id('<unk>')

  # Identify the token indices, which is the same as the ID for this tokenizer.
  token_ids = [tokenizer.token_to_id(word) for word in target_classes]

  # Throw an error if one of the classes maps to a token outside the vocabulary.
  if any(token_id == token_unk for token_id in token_ids):
    raise ValueError('One of the target classes is not in the vocabulary.')

  # Preprocess the prompt in a single batch. This is done one sample at a time
  # for illustration purposes, but it would be more efficient to batch prompts.
  preprocessed = model.preprocessor.generate_preprocess([prompt])

  # Identify output token offset.
  padding_mask = preprocessed["padding_mask"]
  token_offset = keras.ops.sum(padding_mask) - 1

  # Score outputs, extract only the next token's logits.
  vocab_logits = model.score(
      token_ids=preprocessed["token_ids"],
      padding_mask=padding_mask,
  )[0][token_offset]

  # Compute the relative probability of each of the requested tokens.
  token_logits = [vocab_logits[ix] for ix in token_ids]
  logits_tensor = keras.ops.convert_to_tensor(token_logits)
  probabilities = keras.activations.softmax(logits_tensor)

  return dict(zip(target_classes, probabilities.numpy()))

पहले बनाए गए प्रॉम्प्ट की मदद से, उस फ़ंक्शन को चलाकर उसकी जांच की जा सकती है:

compute_output_probability(
    model=model,
    prompt=prompt,
    target_classes=['Positive', 'Negative'],
)

जिससे कुछ ऐसा मिलेगा:

{'Positive': 0.99994016, 'Negative': 5.984089e-05}

7. यह सभी को एक क्लासिफ़ायर के रूप में रैप करना

इस्तेमाल में आसानी के लिए, आपने अभी-अभी बनाए गए सभी फ़ंक्शन को एक sklearn-जैसे क्लासिफ़ायर में शामिल कर दिया है. साथ ही, इसमें इस्तेमाल करने में आसान और जाने-पहचाने फ़ंक्शन predict() और predict_score() भी हैं.

import dataclasses


@dataclasses.dataclass(frozen=True)
class AgileClassifier:
  """Agile classifier to be wrapped around a LLM."""

  # The classes whose probability will be predicted.
  labels: tuple[str, ...]

  # Provide default instructions and control tokens, can be overridden by user.
  instructions: str = 'Classify the following text into one of the following classes'
  separator_token: str = '<separator>'
  end_of_text_token: str = '<eos>'

  def encode_for_prediction(self, x_text: str) -> str:
    return preprocess_text(
        text=x_text,
        labels=self.labels,
        instructions=self.instructions,
        separator=self.separator_token,
    )

  def encode_for_training(self, x_text: str, y: int) -> str:
    return ''.join([
        self.encode_for_prediction(x_text),
        self.labels[y],
        self.end_of_text_token,
    ])

  def predict_score(
      self,
      model: keras_nlp.models.GemmaCausalLM,
      x_text: str,
  ) -> list[float]:
    prompt = self.encode_for_prediction(x_text)
    token_probabilities = compute_output_probability(
        model=model,
        prompt=prompt,
        target_classes=self.labels,
    )
    return [token_probabilities[token] for token in self.labels]

  def predict(
      self,
      model: keras_nlp.models.GemmaCausalLM,
      x_eval: str,
  ) -> int:
    return np.argmax(self.predict_score(model, x_eval))


agile_classifier = AgileClassifier(labels=('Positive', 'Negative'))

8. मॉडल फ़ाइन-ट्यूनिंग

LoRA का मतलब है लो-रैंक वाली स्थिति. यह फ़ाइन-ट्यून करने की तकनीक है. इसका इस्तेमाल करके, बड़े लैंग्वेज मॉडल को बेहतर तरीके से बेहतर बनाया जा सकता है. इस बारे में ज़्यादा जानने के लिए, LoRA: कम रैंक का एडाप्टेशन ऑफ़ लार्ज लैंग्वेज मॉडल पेपर पढ़ें.

Gemma के Keras लागू करने से एक enable_lora() तरीका मिलता है, जिसका इस्तेमाल आप इसे बेहतर बनाने के लिए कर सकते हैं:

# Enable LoRA for the model and set the LoRA rank to 4.
model.backbone.enable_lora(rank=4)

LoRA को चालू करने के बाद, उसे बेहतर बनाने की प्रोसेस शुरू की जा सकती है. Colab पर हर Epoch के हिसाब से, करीब पांच मिनट लगते हैं:

import tensorflow as tf

# Create dataset with preprocessed text + labels.
map_fn = lambda xy: agile_classifier.encode_for_training(*xy)
x_train = list(map(map_fn, df_train[['comment', 'hateful']].values))
ds_train = tf.data.Dataset.from_tensor_slices(x_train).batch(2)

# Compile the model using the Adam optimizer and appropriate loss function.
model.compile(
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    optimizer=keras.optimizers.Adam(learning_rate=0.0005),
    weighted_metrics=[keras.metrics.SparseCategoricalAccuracy()],
)

# Begin training.
model.fit(ds_train, epochs=4)

जब तक ओवरफ़िटिंग न हो जाए, तब तक ज़्यादा सटीक युगों के लिए ट्रेनिंग दें.

9. नतीजों की जांच करें

अब आप उस एजाइल क्लासिफ़ायर के आउटपुट की जांच कर सकते हैं जिसे आपने अभी-अभी ट्रेनिंग दी है. यह कोड, टेक्स्ट के एक हिस्से के आधार पर अनुमानित क्लास स्कोर देगा:

text = 'you look really nice today'
scores = agile_classifier.predict_score(model, text)
dict(zip(agile_classifier.labels, scores))
{'Positive': 0.99899644, 'Negative': 0.0010035498}

10. मॉडल का आकलन करना

आखिर में, दो सामान्य मेट्रिक F1 स्कोर और AUC-ROC का इस्तेमाल करके, हमारे मॉडल की परफ़ॉर्मेंस का आकलन किया जाएगा. F1 स्कोर, एक तय क्लासिफ़िकेशन थ्रेशोल्ड पर प्रिसिज़न और रीकॉल के हार्मोनिक मीन का आकलन करके फ़ॉल्स नेगेटिव और फ़ॉल्स पॉज़िटिव गड़बड़ियों को कैप्चर करता है. वहीं, AUC-ROC, अलग-अलग थ्रेशोल्ड के हिसाब से सही पॉज़िटिव रेट और फ़ॉल्स पॉज़िटिव रेट के बीच के फ़र्क़ को कैप्चर करता है. साथ ही, इस कर्व के दायरे में आने वाले एरिया का हिसाब लगाता है.

from sklearn.metrics import f1_score, roc_auc_score

y_true = df_test['hateful'].values
# Compute the scores (aka probabilities) for each of the labels.
y_score = [agile_classifier.predict_score(model, x) for x in df_test['comment']]
# The label with highest score is considered the predicted class.
y_pred = np.argmax(y_score, axis=1)
# Extract the probability of a comment being considered hateful.
y_prob = [x[agile_classifier.labels.index('Negative')] for x in y_score]

# Compute F1 and AUC-ROC scores.
print(f'F1: {f1_score(y_true, y_pred):.2f}')
print(f'AUC-ROC: {roc_auc_score(y_true, y_prob):.2f}')
F1: 0.84
AUC-ROC: = 0.88

मॉडल के अनुमानों का आकलन करने का एक और दिलचस्प तरीका है, भ्रम की स्थिति दिखाना. भ्रम की स्थिति दिखाने वाला मैट्रिक्स, अनुमान से जुड़ी अलग-अलग तरह की गड़बड़ियों को विज़ुअल तौर पर दिखाएगा.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

cm = confusion_matrix(y_true, y_pred)
ConfusionMatrixDisplay(
  confusion_matrix=cm,
  display_labels=agile_classifier.labels,
).plot()

भ्रम की स्थिति का मैट्रिक्स

आखिर में, अलग-अलग स्कोरिंग थ्रेशोल्ड का इस्तेमाल करते समय, संभावित अनुमानों की गड़बड़ियों का अनुमान लगाने के लिए, आरओसी कर्व का इस्तेमाल किया जा सकता है.

from sklearn.metrics import RocCurveDisplay, roc_curve

fpr, tpr, _ = roc_curve(y_true, y_score, pos_label=1)
RocCurveDisplay(fpr=fpr, tpr=tpr).plot()

आरओसी कर्व