From 101be2435c70e4137d06d8419d511ffede819999 Mon Sep 17 00:00:00 2001 From: Cedric Vidal Date: Tue, 20 May 2025 14:01:54 -0700 Subject: [PATCH 1/3] Fixed missing pip install dotenv-azd --- Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb b/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb index 0169e59..860ece3 100644 --- a/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb +++ b/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb @@ -78,7 +78,7 @@ "metadata": {}, "outputs": [], "source": [ - "pip install python-dotenv" + "pip install dotenv-azd" ] }, { From b8817dfa23842855396e864ab86750a3b1775e1b Mon Sep 17 00:00:00 2001 From: Cedric Vidal Date: Tue, 20 May 2025 14:09:18 -0700 Subject: [PATCH 2/3] Foundry Local link --- Lab329/Notebook/07.Local_inference_AIFoundry.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/Lab329/Notebook/07.Local_inference_AIFoundry.ipynb b/Lab329/Notebook/07.Local_inference_AIFoundry.ipynb index 490334f..ed4274d 100644 --- a/Lab329/Notebook/07.Local_inference_AIFoundry.ipynb +++ b/Lab329/Notebook/07.Local_inference_AIFoundry.ipynb @@ -30,6 +30,7 @@ "- Sufficient disk space and memory for your model\n", "\n", "## References\n", + "- [Azure Foundry Local](https://github.com/microsoft/Foundry-Local/)\n", "- [Azure Foundry Local Documentation](https://github.com/microsoft/Foundry-Local/tree/main/docs)" ] }, From d043d6e374a827b4b3e7ffaa0ea9552a44d78238 Mon Sep 17 00:00:00 2001 From: Cedric Vidal Date: Tue, 20 May 2025 15:00:13 -0700 Subject: [PATCH 3/3] Pre run notebooks --- Lab329/Notebook/01.AzureML_Distillation.ipynb | 3052 +++++++++-------- ...zureML_FineTuningAndConvertByMSOlive.ipynb | 1162 ++++--- .../03.AzureML_RuningByORTGenAI.ipynb | 1143 +++--- .../04.AzureML_RegisterToAzureML.ipynb | 845 ++--- 4 files changed, 3278 insertions(+), 2924 deletions(-) diff --git a/Lab329/Notebook/01.AzureML_Distillation.ipynb b/Lab329/Notebook/01.AzureML_Distillation.ipynb index caf64ca..0b09355 100644 --- a/Lab329/Notebook/01.AzureML_Distillation.ipynb +++ b/Lab329/Notebook/01.AzureML_Distillation.ipynb @@ -1,1458 +1,1602 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "13490c0a", - "metadata": { - "vscode": { - "languageId": "plaintext" + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Knowledge Distillation: Generating Training Data from a Teacher Model\n", + "\n", + "This notebook demonstrates the first step in knowledge distillation: generating high-quality training data from a large \"teacher\" model. Knowledge distillation is a technique where a smaller, more efficient model learns from a larger, more powerful model rather than directly from raw data.\n", + "\n", + "![](../../lab_manual/images/step-1.png)\n", + "\n", + "## What You'll Learn\n", + "- How to load and format multiple-choice questions\n", + "- How to send these questions to a large language model (DeepSeek-V3)\n", + "- How to collect and save the teacher model's responses\n", + "- How to analyze the quality of generated training data\n", + "\n", + "## Prerequisites\n", + "- Access to an Azure AI model endpoint (DeepSeek-V3)\n", + "- Azure credentials set up in a local.env file\n", + "- Python environment with necessary libraries\n", + "\n", + "## Setup Instructions\n", + "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", + "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 AzureML\"** using the selector in the top right\n", + "3. **Environment File**: Ensure your `local.env` file exists with proper credentials\n" + ], + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "id": "13490c0a" + }, + { + "cell_type": "markdown", + "source": [ + "## 1. Install Required Packages\n", + "\n", + "First, we'll install the packages needed for this notebook. The `dotenv-azd` package allows us to securely load environment variables from Azure Developer CLI (AZD) environments and/or local .env files. This is how we'll access our Azure credentials without hardcoding sensitive information." + ], + "metadata": {}, + "id": "585941a1" + }, + { + "cell_type": "code", + "source": [ + "pip install dotenv-azd" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: dotenv-azd in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (0.3.0)\nRequirement already satisfied: python-dotenv in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from dotenv-azd) (1.1.0)\nNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 2, + "metadata": { + "gather": { + "logged": 1747773006676 + } + }, + "id": "dddced75" + }, + { + "cell_type": "markdown", + "source": [ + "## 2. Install Data Handling Libraries\n", + "\n", + "Next, we install two important packages:\n", + "\n", + "- `datasets`: Hugging Face's library that provides easy access to thousands of publicly available datasets\n", + "- `tqdm`: A progress bar library that helps visualize long-running operations\n", + "\n", + "The `-U` flag ensures we get the latest versions of these packages." + ], + "metadata": {}, + "id": "40f85a53" + }, + { + "cell_type": "code", + "source": [ + "pip install datasets tqdm -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: datasets in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (3.6.0)\nRequirement already satisfied: tqdm in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (4.67.1)\nRequirement already satisfied: filelock in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (3.18.0)\nRequirement already satisfied: numpy>=1.17 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (2.2.5)\nRequirement already satisfied: pyarrow>=15.0.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (19.0.1)\nRequirement already satisfied: dill<0.3.9,>=0.3.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (0.3.8)\nRequirement already satisfied: pandas in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (2.2.3)\nRequirement already satisfied: requests>=2.32.2 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (2.32.3)\nRequirement already satisfied: xxhash in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (3.5.0)\nRequirement already satisfied: multiprocess<0.70.17 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (0.70.16)\nRequirement already satisfied: fsspec<=2025.3.0,>=2023.1.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (2025.3.0)\nRequirement already satisfied: huggingface-hub>=0.24.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (0.31.4)\nRequirement already satisfied: packaging in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (25.0)\nRequirement already satisfied: pyyaml>=5.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from datasets) (6.0.2)\nRequirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (3.11.18)\nRequirement already satisfied: typing-extensions>=3.7.4.3 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from huggingface-hub>=0.24.0->datasets) (4.13.2)\nRequirement already satisfied: charset_normalizer<4,>=2 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.32.2->datasets) (3.4.1)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.32.2->datasets) (3.10)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.32.2->datasets) (2.4.0)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.32.2->datasets) (2025.1.31)\nRequirement already satisfied: python-dateutil>=2.8.2 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from pandas->datasets) (2.9.0.post0)\nRequirement already satisfied: pytz>=2020.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from pandas->datasets) (2025.2)\nRequirement already satisfied: tzdata>=2022.7 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from pandas->datasets) (2025.2)\nRequirement already satisfied: aiohappyeyeballs>=2.3.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (2.6.1)\nRequirement already satisfied: aiosignal>=1.1.2 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (1.3.2)\nRequirement already satisfied: async-timeout<6.0,>=4.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (5.0.1)\nRequirement already satisfied: attrs>=17.3.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (25.3.0)\nRequirement already satisfied: frozenlist>=1.1.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (1.6.0)\nRequirement already satisfied: multidict<7.0,>=4.5 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (6.4.3)\nRequirement already satisfied: propcache>=0.2.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (0.3.1)\nRequirement already satisfied: yarl<2.0,>=1.17.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->fsspec[http]<=2025.3.0,>=2023.1.0->datasets) (1.20.0)\nRequirement already satisfied: six>=1.5 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from python-dateutil>=2.8.2->pandas->datasets) (1.17.0)\nNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 3, + "metadata": { + "gather": { + "logged": 1747773007665 + } + }, + "id": "5ebb8ff3" + }, + { + "cell_type": "markdown", + "source": [ + "## 3. Import Dataset Libraries\n", + "\n", + "Now we import the libraries needed to work with our datasets:\n", + "\n", + "- `load_dataset`: A function from Hugging Face that lets us download and use public datasets\n", + "- `ABC` (Abstract Base Class): A Python feature that helps us create a framework for our dataset handling" + ], + "metadata": {}, + "id": "7d0ebfbd" + }, + { + "cell_type": "code", + "source": [ + "from datasets import load_dataset\n", + "from abc import ABC" + ], + "outputs": [], + "execution_count": 4, + "metadata": { + "gather": { + "logged": 1747773008395 + } + }, + "id": "c9605ccc" + }, + { + "cell_type": "markdown", + "source": [ + "## 4. Create Base Dataset Class\n", + "\n", + "Here we define our base class for handling datasets. This creates a common structure that all our dataset handlers will follow:\n", + "\n", + "1. We're using Python's Abstract Base Class (`ABC`) as a foundation\n", + "2. The class initializes three important attributes that will track our data files:\n", + " - `train_data_file_name`: For our training data\n", + " - `test_data_file_name`: For our test data\n", + " - `eval_data_file_name`: For our evaluation/validation data\n", + "\n", + "These file names are initially set to `None` and will be filled in later when we create our datasets." + ], + "metadata": {}, + "id": "61c51934" + }, + { + "cell_type": "code", + "source": [ + "class InputDataset(ABC):\n", + " def __init__(self):\n", + " super().__init__()\n", + " (\n", + " self.train_data_file_name,\n", + " self.test_data_file_name,\n", + " self.eval_data_file_name,\n", + " ) = (None, None, None)" + ], + "outputs": [], + "execution_count": 5, + "metadata": { + "gather": { + "logged": 1747773008449 + } + }, + "id": "806f5e03" + }, + { + "cell_type": "markdown", + "source": [ + "## 5. Create Specialized Dataset Handler\n", + "\n", + "This class extends our base `InputDataset` to handle loading data specifically from Hugging Face. The `load_hf_dataset` method does the following:\n", + "\n", + "1. Loads a dataset from Hugging Face by name (e.g., \"tau/commonsense_qa\")\n", + "2. Creates three different data splits:\n", + " - Training data: Used to generate examples for our model\n", + " - Validation data: Used to evaluate during training\n", + " - Test data: Used for final evaluation\n", + "\n", + "3. Controls how many examples we take from each split using the sample size parameters\n", + "\n", + "The code handles two scenarios:\n", + "- Datasets that already have validation splits\n", + "- Datasets that only have train/test splits (in which case it creates a validation set from the training data)" + ], + "metadata": {}, + "id": "43455abd" + }, + { + "cell_type": "code", + "source": [ + "class CQnAHuggingFaceInputDataset(InputDataset):\n", + " \"\"\"\n", + " Loads the HuggingFace dataset\n", + " \"\"\"\n", + "\n", + " def __init__(self):\n", + " super().__init__()\n", + "\n", + " def load_hf_dataset(\n", + " self,\n", + " dataset_name,\n", + " train_sample_size=10,\n", + " val_sample_size=10,\n", + " test_sample_size=10,\n", + " train_split_name=\"train\",\n", + " val_split_name=\"validation\",\n", + " test_split_name=\"test\",\n", + " ):\n", + " full_dataset = load_dataset(dataset_name)\n", + "\n", + " if val_split_name is not None:\n", + " train_data = full_dataset[train_split_name].select(range(train_sample_size))\n", + " val_data = full_dataset[val_split_name].select(range(val_sample_size))\n", + " test_data = full_dataset[test_split_name].select(range(test_sample_size))\n", + " else:\n", + " train_val_data = full_dataset[train_split_name].select(\n", + " range(train_sample_size + val_sample_size)\n", + " )\n", + " train_data = train_val_data.select(range(train_sample_size))\n", + " val_data = train_val_data.select(\n", + " range(train_sample_size, train_sample_size + val_sample_size)\n", + " )\n", + " test_data = full_dataset[test_split_name].select(range(test_sample_size))\n", + "\n", + " return train_data, val_data, test_data" + ], + "outputs": [], + "execution_count": 6, + "metadata": { + "gather": { + "logged": 1747773008497 + } + }, + "id": "34315645" + }, + { + "cell_type": "markdown", + "source": [ + "## 6. Load the CommonsenseQA Dataset\n", + "\n", + "Now we'll actually load our dataset using the classes we defined. Here's what this cell does:\n", + "\n", + "1. **Define sample sizes**: We're using 100 examples each for training and validation to keep this notebook running quickly\n", + "\n", + "2. **Choose a dataset**: We're using \"tau/commonsense_qa\", which contains multiple-choice questions that test common sense reasoning\n", + "\n", + "3. **Create an instance of our dataset handler** and use it to load the data\n", + "\n", + "4. **Print the dataset sizes** to confirm everything loaded correctly\n", + "\n", + "The CommonsenseQA dataset is perfect for knowledge distillation because it contains challenging questions with five possible answers (labeled A through E). This will test how well our model has learned to reason like the teacher model." + ], + "metadata": {}, + "id": "8b2eb02d" + }, + { + "cell_type": "code", + "source": [ + "# We can define train and test sample sizes here. Validation size is kept same as test sample size\n", + "train_sample_size = 100\n", + "val_sample_size = 100\n", + "\n", + "# Sample notebook using the dataset: https://huggingface.co/datasets/tau/commonsense_qa\n", + "dataset_name = \"tau/commonsense_qa\"\n", + "input_dataset = CQnAHuggingFaceInputDataset()\n", + "\n", + "# Note: train_split_name and test_split_name can vary by dataset. They are passed as arguments in load_hf_dataset.\n", + "# If validation_split_name is None, the below function will split the train set to create the specified sized validation set.\n", + "train, val, _ = input_dataset.load_hf_dataset(\n", + " dataset_name=dataset_name,\n", + " train_sample_size=train_sample_size,\n", + " val_sample_size=val_sample_size,\n", + " train_split_name=\"train\",\n", + " val_split_name=\"validation\",\n", + ")\n", + "\n", + "print(\"Len of train data sample is \" + str(len(train)))\n", + "print(\"Len of validation data sample is \" + str(len(val)))" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Len of train data sample is 100\nLen of validation data sample is 100\n" + } + ], + "execution_count": 7, + "metadata": { + "gather": { + "logged": 1747773010597 + } + }, + "id": "3a92e682" + }, + { + "cell_type": "markdown", + "source": [ + "## 7. Create Data Directory\n", + "\n", + "This simple command creates a directory called `data` where we'll store our processed files. The `-p` flag ensures that:\n", + "\n", + "1. If the directory already exists, no error will occur\n", + "2. If parent directories don't exist, they will be created automatically\n", + "\n", + "This is where we'll store our questions and the teacher model's answers." + ], + "metadata": {}, + "id": "9e2508d5" + }, + { + "cell_type": "code", + "source": [ + "! mkdir -p data" + ], + "outputs": [], + "execution_count": 8, + "metadata": {}, + "id": "4a458770" + }, + { + "cell_type": "markdown", + "source": [ + "## 8. Set Output File Path\n", + "\n", + "Here we define the path where we'll save our processed training data. The file will be called `train_original_data.jsonl` in the data directory we just created.\n", + "\n", + "The `.jsonl` extension indicates this is a JSON Lines file format - a convenient format for large datasets where each line is a valid JSON object. This format is useful because:\n", + "\n", + "- It allows processing one example at a time (streaming)\n", + "- Each example is self-contained on its own line\n", + "- It's human-readable and easily parsed by most programming languages" + ], + "metadata": {}, + "id": "cfdfaa69" + }, + { + "cell_type": "code", + "source": [ + "train_data_path = \"./data/train_original_data.jsonl\"" + ], + "outputs": [], + "execution_count": 9, + "metadata": { + "gather": { + "logged": 1747773010727 + } + }, + "id": "25914972" + }, + { + "cell_type": "markdown", + "source": [ + "## 9. Import JSON Library\n", + "\n", + "Here we import the `json` module, which is part of Python's standard library. This module provides functions to:\n", + "\n", + "- Convert Python objects to JSON strings (`json.dumps()`)\n", + "- Parse JSON strings into Python objects (`json.loads()`)\n", + "- Write JSON data to files (`json.dump()`)\n", + "- Read JSON data from files (`json.load()`)\n", + "\n", + "We'll use these functions to format and save our questions and answers in a structured way." + ], + "metadata": {}, + "id": "708f4dc8" + }, + { + "cell_type": "code", + "source": [ + "import json" + ], + "outputs": [], + "execution_count": 10, + "metadata": { + "gather": { + "logged": 1747773010784 + } + }, + "id": "df1f3072" + }, + { + "cell_type": "markdown", + "source": [ + "## 10. Format Questions for the Teacher Model\n", + "\n", + "This cell prepares our questions in the format required by the teacher model. Here's what happens step-by-step:\n", + "\n", + "1. **Define prompts**:\n", + " - A system prompt that tells the model to answer with only one letter (A, B, C, D, or E)\n", + " - A template for user messages that formats each question and its answer choices\n", + "\n", + "2. **Create/clear the output file** to ensure we start with a clean file\n", + "\n", + "3. **Process each example** in our training dataset:\n", + " - Extract the question and its multiple-choice options\n", + " - Format the answer choices with their labels (A, B, C, D, E)\n", + " - Create a message structure with system and user prompts\n", + " - Save each formatted example to our JSONL file\n", + "\n", + "4. **Add error handling** and progress tracking\n", + "\n", + "5. **Validate the file creation** by checking the first line\n", + "\n", + "The resulting file will contain all our questions formatted in a way that's ready to be sent to the teacher model." + ], + "metadata": {}, + "id": "9f67827b" + }, + { + "cell_type": "code", + "source": [ + "system_prompt = \"You are a helpful assistant. Your output should only be one of the five choices: 'A', 'B', 'C', 'D', or 'E'.\"\n", + "user_prompt_template = \"Answer the following multiple-choice question by selecting the correct option.\\n\\nQuestion: {question}\\nAnswer Choices:\\n{answer_choices}\"\n", + "\n", + "# First, create/truncate the file to ensure it's empty and exists\n", + "with open(train_data_path, \"w\", encoding='utf-8') as f:\n", + " pass # Just create the file, we'll append to it later\n", + "\n", + "print(f\"Generating training data and writing to {train_data_path}\")\n", + "\n", + "# Check if train data has expected structure\n", + "if len(train) == 0:\n", + " print(\"Error: Train dataset is empty!\")\n", + "else:\n", + " print(f\"Sample train data keys: {list(train[0].keys())}\")\n", + " \n", + " # Verify train data has the expected fields before processing\n", + " sample = train[0]\n", + " if 'question' in sample and 'choices' in sample:\n", + " for i, row in enumerate(train):\n", + " try:\n", + " data = {\"messages\": []}\n", + " data[\"messages\"].append(\n", + " {\n", + " \"role\": \"system\",\n", + " \"content\": system_prompt,\n", + " }\n", + " )\n", + " question, choices = row[\"question\"], row[\"choices\"]\n", + " labels, choice_list = choices[\"label\"], choices[\"text\"]\n", + " answer_choices = [\n", + " \"({}) {}\".format(labels[i], choice_list[i]) for i in range(len(labels))\n", + " ]\n", + " answer_choices = \"\\n\".join(answer_choices)\n", + " data[\"messages\"].append(\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": user_prompt_template.format(\n", + " question=question, answer_choices=answer_choices\n", + " ),\n", + " }\n", + " )\n", + " \n", + " # Write data as valid JSON line\n", + " with open(train_data_path, \"a\", encoding='utf-8') as f:\n", + " f.write(json.dumps(data, ensure_ascii=False) + \"\\n\")\n", + " \n", + " if i % 10 == 0: # Show progress every 10 items\n", + " print(f\"Processed {i+1}/{len(train)} questions\", end=\"\\r\")\n", + " \n", + " except Exception as e:\n", + " print(f\"\\nError processing row {i}: {str(e)}\")\n", + " print(f\"Row data: {row}\")\n", + " \n", + " print(f\"\\nSuccessfully wrote {len(train)} questions to {train_data_path}\")\n", + " \n", + " # Validate the file has content\n", + " with open(train_data_path, \"r\", encoding='utf-8') as f:\n", + " first_line = f.readline().strip()\n", + " print(f\"File validation: {'SUCCESS' if first_line else 'FAILED - Empty file'}\")\n", + " if first_line:\n", + " # Try parsing the first line to validate JSON\n", + " try:\n", + " json_data = json.loads(first_line)\n", + " print(f\"JSON validation: SUCCESS\")\n", + " except json.JSONDecodeError as e:\n", + " print(f\"JSON validation: FAILED - {str(e)}\")\n", + " else:\n", + " print(f\"Error: Train data doesn't have the expected structure. Found keys: {list(sample.keys())}\")\n", + " print(\"Expected 'question' and 'choices' keys in each sample.\")" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Generating training data and writing to ./data/train_original_data.jsonl\nSample train data keys: ['id', 'question', 'question_concept', 'choices', 'answerKey']\nProcessed 91/100 questions\nSuccessfully wrote 100 questions to ./data/train_original_data.jsonl\nFile validation: SUCCESS\nJSON validation: SUCCESS\n" + } + ], + "execution_count": 11, + "metadata": { + "gather": { + "logged": 1747773012066 + } + }, + "id": "ad051ac1" + }, + { + "cell_type": "markdown", + "source": [ + "## 11. Load Environment Variables\n", + "\n", + "This cell loads the configuration and credentials needed to connect to our teacher model. Instead of hardcoding sensitive information like API keys, we use environment variables for better security:\n", + "\n", + "1. `load_azd_env()`: Tries to load variables from an Azure Developer CLI (AZD) environment if available\n", + "2. `load_dotenv()`: Falls back to loading from a local `.env` file\n", + "\n", + "This approach ensures our credentials remain secure and can be easily changed without modifying the code. The environment variables should include details for connecting to the Azure AI services where our teacher model is hosted." + ], + "metadata": {}, + "id": "6063901e" + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "from dotenv_azd import load_azd_env\n", + "from dotenv import load_dotenv\n", + "\n", + "# Load environment variables from current AZD environment if available\n", + "load_azd_env(quiet=True)\n", + "\n", + "# Load environment variables from local.env file if it exists\n", + "load_dotenv(dotenv_path=\"local.env\")" + ], + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 12, + "data": { + "text/plain": "True" + }, + "metadata": {} + } + ], + "execution_count": 12, + "metadata": { + "gather": { + "logged": 1747773012144 + } + }, + "id": "bfde7fd0" + }, + { + "cell_type": "markdown", + "source": [ + "## 12. Access Teacher Model Credentials\n", + "\n", + "Now we retrieve the specific credentials needed to access our teacher model from the environment variables. We need three key pieces of information:\n", + "\n", + "1. `teacher_model_name`: The name of the model we'll be using (e.g., \"DeepSeek-V3\")\n", + "2. `teacher_model_endpoint_url`: The URL where the model API is hosted\n", + "3. `teacher_model_api_key`: The authentication key to access the service\n", + "\n", + "We print confirmation that these values were loaded (without revealing the actual API key for security reasons). This helps verify our setup is correct before proceeding.\n", + "\n", + "Make sure these environment variables are set in your `local.env` file or AZD environment before running this notebook." + ], + "metadata": {}, + "id": "c2e2c3fd" + }, + { + "cell_type": "code", + "source": [ + "# Get Azure AI Foundry credentials from environment variables\n", + "teacher_model_name = os.getenv('TEACHER_MODEL_NAME')\n", + "teacher_model_endpoint_url = os.getenv('TEACHER_MODEL_ENDPOINT')\n", + "teacher_model_api_key = os.getenv('TEACHER_MODEL_KEY')\n", + "\n", + "# Print values for debugging (remove in production)\n", + "print(f\"Teacher Model Name: {teacher_model_name}\")\n", + "print(f\"Teacher Model Endpoint: {teacher_model_endpoint_url}\")\n", + "# Don't print the API key for security reasons\n", + "print(f\"Teacher Model API Key loaded: {'Yes' if teacher_model_api_key else 'No'}\")" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Teacher Model Name: DeepSeek-V3\nTeacher Model Endpoint: https://southcentralus.api.cognitive.microsoft.com/models\nTeacher Model API Key loaded: Yes\n" + } + ], + "execution_count": 13, + "metadata": { + "gather": { + "logged": 1747773012190 + } + }, + "id": "2c8fc57b" + }, + { + "cell_type": "markdown", + "source": [ + "## 13. Install Azure AI Inference SDK\n", + "\n", + "Here we install the `azure-ai-inference` package, which is Microsoft's official SDK for interacting with Azure AI services. This library provides:\n", + "\n", + "1. Client classes to connect to Azure AI models\n", + "2. Methods to send prompts and receive responses\n", + "3. Classes to structure messages in the expected chat format\n", + "4. Functionality to set parameters like temperature and token limits\n", + "\n", + "This modern API makes it easy to communicate with large language models hosted on Azure AI services." + ], + "metadata": {}, + "id": "419d134e" + }, + { + "cell_type": "code", + "source": [ + "pip install azure-ai-inference" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: azure-ai-inference in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (1.0.0b9)\nRequirement already satisfied: isodate>=0.6.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from azure-ai-inference) (0.7.2)\nRequirement already satisfied: azure-core>=1.30.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from azure-ai-inference) (1.33.0)\nRequirement already satisfied: typing-extensions>=4.6.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from azure-ai-inference) (4.13.2)\nRequirement already satisfied: requests>=2.21.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from azure-core>=1.30.0->azure-ai-inference) (2.32.3)\nRequirement already satisfied: six>=1.11.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from azure-core>=1.30.0->azure-ai-inference) (1.17.0)\nRequirement already satisfied: charset_normalizer<4,>=2 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.30.0->azure-ai-inference) (3.4.1)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.30.0->azure-ai-inference) (3.10)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.30.0->azure-ai-inference) (2.4.0)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.30.0->azure-ai-inference) (2025.1.31)\nNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 14, + "metadata": { + "gather": { + "logged": 1747773012813 + } + }, + "id": "5052dde2" + }, + { + "cell_type": "markdown", + "source": [ + "## 14. Import Azure AI and Utility Libraries\n", + "\n", + "Now we import the libraries we'll need to communicate with the teacher model:\n", + "\n", + "1. **Azure AI Inference Classes**:\n", + " - `ChatCompletionsClient`: The main client for sending requests to the model\n", + " - `SystemMessage` and `UserMessage`: Classes to format our prompts correctly\n", + " - `AzureKeyCredential`: For authentication with our API key\n", + "\n", + "2. **Utility Libraries**:\n", + " - `json`: For working with JSON data (imported earlier)\n", + " - `os`: For accessing environment variables\n", + " - `tqdm`: For showing progress bars during processing\n", + "\n", + "These libraries give us everything we need to send our questions to the teacher model and process its responses." + ], + "metadata": {}, + "id": "fdfa058f" + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "import json\n", + "from azure.ai.inference import ChatCompletionsClient\n", + "from azure.ai.inference.models import SystemMessage, UserMessage\n", + "from azure.core.credentials import AzureKeyCredential\n", + "from tqdm import tqdm" + ], + "outputs": [], + "execution_count": 15, + "metadata": { + "gather": { + "logged": 1747773012896 + } + }, + "id": "62fb1cbf" + }, + { + "cell_type": "markdown", + "source": [ + "## 15. Define Question Processing Function\n", + "\n", + "This function handles sending a single question to the teacher model and processing its response. Here's how it works:\n", + "\n", + "1. **Create message objects** from our JSON data:\n", + " - Converts system prompts to `SystemMessage` objects\n", + " - Converts user prompts to `UserMessage` objects\n", + "\n", + "2. **Sends the messages** to the teacher model through the Azure AI client\n", + "\n", + "3. **Processes the response** and formats it for our training data\n", + "\n", + "4. **Handles errors** gracefully by returning an error message if something goes wrong\n", + "\n", + "The function returns a dictionary containing:\n", + "- The original question\n", + "- The model's response (answer)\n", + "- The full response object (for debugging)" + ], + "metadata": {}, + "id": "6aa428a6" + }, + { + "cell_type": "code", + "source": [ + "def process_question(question_data):\n", + " try:\n", + " messages = []\n", + " for msg in question_data[\"messages\"]:\n", + " if msg[\"role\"] == \"system\":\n", + " messages.append(SystemMessage(content=msg[\"content\"]))\n", + " elif msg[\"role\"] == \"user\":\n", + " messages.append(UserMessage(content=msg[\"content\"]))\n", + "\n", + " response = client.complete(\n", + " messages=messages,\n", + " model=model_name,\n", + " max_tokens=10000 # Reduced since we just need short answers like A, B, C, D, or E\n", + " )\n", + "\n", + " return {\n", + " \"question\": question_data[\"messages\"][1][\"content\"],\n", + " \"response\": response.choices[0].message.content,\n", + " \"full_response\": response\n", + " }\n", + " except Exception as e:\n", + " return {\n", + " \"question\": question_data[\"messages\"][1][\"content\"] if len(question_data[\"messages\"]) > 1 else \"Error\",\n", + " \"response\": f\"Error: {str(e)}\",\n", + " \"full_response\": None\n", + " }" + ], + "outputs": [], + "execution_count": 16, + "metadata": { + "gather": { + "logged": 1747773012952 + } + }, + "id": "a5f12ae7" + }, + { + "cell_type": "markdown", + "source": [ + "## 16. Initialize Azure AI Client\n", + "\n", + "Now we create the Azure AI client that will connect to our teacher model. This client handles all communication with the Azure AI service:\n", + "\n", + "1. **Set up the endpoint URL** using our environment variable\n", + "2. **Set the model name** to specify which model to use\n", + "3. **Create the client** with our endpoint and authentication credentials\n", + "\n", + "This client will be used by our `process_question` function to send questions to the teacher model." + ], + "metadata": {}, + "id": "5a8e4688" + }, + { + "cell_type": "code", + "source": [ + "endpoint = f\"{teacher_model_endpoint_url}\"\n", + "model_name = teacher_model_name\n", + "key = teacher_model_api_key\n", + "client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(key))" + ], + "outputs": [], + "execution_count": 17, + "metadata": { + "gather": { + "logged": 1747773013019 + } + }, + "id": "448b00cf" + }, + { + "cell_type": "markdown", + "source": [ + "## 17. Process Questions with the Teacher Model\n", + "\n", + "This section contains the main processing loop that sends our formatted questions to the teacher model. Here's what this code does:\n", + "\n", + "1. **Validates the input file** to ensure it exists and has valid JSON content\n", + "\n", + "2. **Sets up a progress bar** to track processing in real-time\n", + "\n", + "3. **Processes each question** by:\n", + " - Reading the question data from the file\n", + " - Sending it to our `process_question` function\n", + " - Adding the result to our results list\n", + " - Updating the progress bar\n", + "\n", + "4. **Handles errors** that might occur during processing\n", + "\n", + "5. **Reports overall statistics** at the end\n", + "\n", + "This is the core of our knowledge distillation process - we're collecting expert knowledge from the teacher model that will be used to train our smaller student model." + ], + "metadata": {}, + "id": "0ec9d6a9" + }, + { + "cell_type": "code", + "source": [ + "# Read the JSONL file and process each question\n", + "import os\n", + "results = []\n", + "\n", + "# Ensure we're using the correct file path\n", + "train_data_path = \"./data/train_original_data.jsonl\"\n", + "\n", + "# Check if the file exists and has content before proceeding\n", + "if not os.path.exists(train_data_path):\n", + " print(f\"Error: File {train_data_path} does not exist! Please run the previous cells to create it.\")\n", + "else:\n", + " file_size = os.path.getsize(train_data_path)\n", + " if file_size == 0:\n", + " print(f\"Warning: File {train_data_path} exists but is empty (0 bytes)! Please run the data generation cells first.\")\n", + " else:\n", + " print(f\"File {train_data_path} exists and has {file_size} bytes.\")\n", + " \n", + " # Validate first line to ensure it's valid JSON\n", + " with open(train_data_path, 'r', encoding='utf-8') as file:\n", + " first_line = file.readline().strip()\n", + " if not first_line:\n", + " print(\"Error: File exists but first line is empty!\")\n", + " else:\n", + " try:\n", + " # Try parsing the first JSON line\n", + " test_json = json.loads(first_line)\n", + " print(\"JSON validation: First line is valid JSON\")\n", + " \n", + " # Continue with processing all lines\n", + " file.seek(0) # Go back to start of file\n", + " total_lines = sum(1 for _ in file if _.strip())\n", + " \n", + " print(f\"Processing {total_lines} questions from {train_data_path}\")\n", + " file.seek(0) # Go back to start of file again\n", + " \n", + " # Initialize tqdm progress bar\n", + " progress_bar = tqdm(total=total_lines, desc=\"Processing questions\", unit=\"question\")\n", + " \n", + " processed_count = 0\n", + " error_count = 0\n", + " \n", + " for i, line in enumerate(file):\n", + " if line.strip(): # Skip empty lines\n", + " try:\n", + " question_data = json.loads(line)\n", + " result = process_question(question_data)\n", + " results.append(result)\n", + " processed_count += 1\n", + " \n", + " # Update progress bar description with latest result\n", + " progress_bar.set_description(f\"Latest answer: {result['response'][:20]}...\")\n", + " progress_bar.update(1)\n", + " \n", + " except json.JSONDecodeError as e:\n", + " error_count += 1\n", + " print(f\"\\nError parsing line {i+1}: {str(e)}\")\n", + " print(f\"Problematic line content: '{line[:100]}...'\")\n", + " progress_bar.update(1)\n", + " except Exception as e:\n", + " error_count += 1\n", + " print(f\"\\nError processing line {i+1}: {str(e)}\")\n", + " progress_bar.update(1)\n", + " \n", + " progress_bar.close()\n", + " \n", + " print(f\"\\nProcessing complete: {processed_count} successful, {error_count} errors\")\n", + " \n", + " except json.JSONDecodeError as e:\n", + " print(f\"Error: Invalid JSON in file. First line error: {str(e)}\")\n", + " print(f\"First line content: '{first_line[:100]}...'\")\n", + " \n", + " except Exception as e:\n", + " print(f\"Error during file processing: {str(e)}\")" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": "Latest answer: (B) finger...: 99%|█████████▉| 99/100 [01:41<00:00, 2.42question/s] \n" + } + ], + "execution_count": 18, + "metadata": { + "gather": { + "logged": 1747773114732 + } + }, + "id": "285ab8b3" + }, + { + "cell_type": "markdown", + "source": [ + "## 18. Save the Teacher Model Responses\n", + "\n", + "Now that we've collected answers from our teacher model, we'll save them to a new file for training our student model. This cell:\n", + "\n", + "1. **Defines the output file path** for our processed training data\n", + "\n", + "2. **Creates a progress bar** to track the saving process\n", + "\n", + "3. **Formats each result** into a simpler structure with just the question and answer\n", + "\n", + "4. **Writes each example** as a line in our output JSON Lines file\n", + "\n", + "The resulting `train_data.jsonl` file will be the training data for our student model in the next notebook. This is the key output from our knowledge distillation process." + ], + "metadata": {}, + "id": "f40cbf5e" + }, + { + "cell_type": "code", + "source": [ + "output_file_path = \"./data/train_data.jsonl\"\n", + "\n", + "# Initialize tqdm progress bar for writing results\n", + "print(f\"Writing {len(results)} processed questions to {output_file_path}\")\n", + "with open(output_file_path, 'w', encoding='utf-8') as f:\n", + " for result in tqdm(results, desc=\"Writing results\", unit=\"record\"):\n", + " # Extract just the question content (removing the instruction part)\n", + " question_text = result[\"question\"]\n", + " # if \"Question: \" in question_text:\n", + " # question_text = question_text.split(\"Question: \")[1].split(\"\\nAnswer Choices:\")[0]\n", + "\n", + " # Create the simplified output format\n", + " output_line = {\n", + " \"Question\": question_text,\n", + " \"Answer\": result[\"response\"]\n", + " }\n", + "\n", + " # Write as JSONL (one JSON object per line)\n", + " f.write(json.dumps(output_line, ensure_ascii=False) + '\\n')" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": "\rWriting results: 0%| | 0/100 [00:00=1.0.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (1.3.2)\nRequirement already satisfied: cycler>=0.10 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (0.12.1)\nRequirement already satisfied: fonttools>=4.22.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (4.58.0)\nRequirement already satisfied: kiwisolver>=1.3.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (1.4.8)\nRequirement already satisfied: numpy>=1.23 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (2.2.5)\nRequirement already satisfied: packaging>=20.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (25.0)\nRequirement already satisfied: pillow>=8 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (11.2.1)\nRequirement already satisfied: pyparsing>=2.3.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (3.2.3)\nRequirement already satisfied: python-dateutil>=2.7 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib) (2.9.0.post0)\nRequirement already satisfied: six>=1.5 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib) (1.17.0)\nNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 20, + "metadata": { + "gather": { + "logged": 1747773115687 + } + }, + "id": "95ccbff4" + }, + { + "cell_type": "markdown", + "source": [ + "## 20. Analyze Answer Distribution\n", + "\n", + "Now we'll analyze how the teacher model responded to our questions. This helps us understand the quality of our training data and identify any potential issues. This cell:\n", + "\n", + "1. **Counts the frequency** of each answer choice (A, B, C, D, E)\n", + "\n", + "2. **Identifies non-standard responses** (anything other than a single letter)\n", + "\n", + "3. **Creates a bar chart** showing the distribution of answers\n", + "\n", + "4. **Calculates the success rate** - what percentage of responses were valid letter choices\n", + "\n", + "5. **Analyzes problematic responses** if any were found\n", + "\n", + "This analysis helps us ensure that our teacher model is providing high-quality, consistent responses that will be good training examples for our student model." + ], + "metadata": {}, + "id": "21aaeaa3" + }, + { + "cell_type": "code", + "source": [ + "# Visualize the distillation results\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "import re\n", + "\n", + "# Count occurrences of each answer\n", + "answer_counts = {}\n", + "other_responses = []\n", + "\n", + "for result in results:\n", + " answer = result['response'].strip()\n", + " \n", + " # Try to extract just the letter if there's additional text\n", + " # This regex matches: A, A., A), (A), (A) text\n", + " match = re.search(r'(?:^|\\(|\\s)([A-E])(?:\\)|\\.|\\s|$)', answer, re.IGNORECASE)\n", + " if match:\n", + " answer = match.group(1).upper() # Extract just the letter and convert to uppercase\n", + " answer_counts[answer] = answer_counts.get(answer, 0) + 1\n", + " else:\n", + " # For non-standard answers\n", + " answer_counts['Other'] = answer_counts.get('Other', 0) + 1\n", + " # Store problematic responses for analysis\n", + " other_responses.append(answer)\n", + "\n", + "# Create a DataFrame for visualization\n", + "df = pd.DataFrame(list(answer_counts.items()), columns=['Answer', 'Count'])\n", + "df = df.sort_values('Count', ascending=False)\n", + "\n", + "# Create a bar chart\n", + "plt.figure(figsize=(10, 6))\n", + "bars = plt.bar(df['Answer'], df['Count'], color='skyblue')\n", + "plt.title('Distribution of Answers in Distilled Dataset')\n", + "plt.xlabel('Answer Choice')\n", + "plt.ylabel('Count')\n", + "plt.grid(axis='y', linestyle='--', alpha=0.7)\n", + "\n", + "# Add count labels on top of each bar\n", + "for bar in bars:\n", + " height = bar.get_height()\n", + " plt.text(bar.get_x() + bar.get_width()/2., height + 1,\n", + " f'{height}', ha='center', va='bottom')\n", + "\n", + "# Calculate and display success rate (assuming valid answers are A-E)\n", + "total_questions = sum(answer_counts.values())\n", + "valid_answers = sum(answer_counts.get(ans, 0) for ans in ['A', 'B', 'C', 'D', 'E'])\n", + "success_rate = (valid_answers / total_questions) * 100 if total_questions > 0 else 0\n", + "\n", + "plt.figtext(0.5, 0.01, f'Success Rate: {success_rate:.1f}% ({valid_answers}/{total_questions} questions with valid answers)', \n", + " ha='center', fontsize=12)\n", + "\n", + "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", + "plt.show()\n", + "\n", + "# Display sample of 'Other' responses if they exist\n", + "if other_responses:\n", + " print(f\"\\nSamples of 'Other' responses (showing up to 10):\")\n", + " for i, resp in enumerate(other_responses[:10]):\n", + " print(f\" {i+1}. '{resp}'\")\n", + " \n", + " # Analyze if there are patterns in the 'Other' responses\n", + " lowercase_letters = sum(1 for r in other_responses if r.lower() in ['a', 'b', 'c', 'd', 'e'])\n", + " has_period = sum(1 for r in other_responses if re.search(r'[A-Ea-e]\\.', r))\n", + " has_explanation = sum(1 for r in other_responses if len(r) > 5) # Simple heuristic for explanations\n", + " \n", + " print(f\"\\nAnalysis of {len(other_responses)} 'Other' responses:\")\n", + " print(f\" - Lowercase letters (a-e): {lowercase_letters} ({lowercase_letters/len(other_responses)*100:.1f}%)\")\n", + " print(f\" - Answers with periods (A.): {has_period} ({has_period/len(other_responses)*100:.1f}%)\")\n", + " print(f\" - Likely explanations (>5 chars): {has_explanation} ({has_explanation/len(other_responses)*100:.1f}%)\")" + ], + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAI9CAYAAAAn/RuwAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAgAlJREFUeJzs3Xd4FHX39/HPJiGFVBISQggECC1SFRDpoQgiVRHECijqjYAiNiyIcKvYQCwUC4I/blGKNJEqIEVBDUVE6U3pNQkECCT5Pn/wZMySQgIZNonv13XtdWXPzM6eMzOZ3bPTHMYYIwAAAAAAkO/cXJ0AAAAAAABFFU03AAAAAAA2oekGAAAAAMAmNN0AAAAAANiEphsAAAAAAJvQdAMAAAAAYBOabgAAAAAAbELTDQAAAACATWi6AQAAAACwCU03ALjYq6++KofDcV3eKzY2VrGxsdbzH374QQ6HQzNmzLgu79+rVy+VL1/+urzX1Tpz5oz69Omj8PBwORwODRw40NUpFQnXcz3PTvr6/sMPP+TL9CZNmiSHw6G9e/dascv/x/bu3SuHw6FJkybly3tm974AgIKLphsA8lH6l+H0h7e3tyIiItS2bVt98MEHOn36dL68z8GDB/Xqq69q48aN+TK9/FSQc8uNN954Q5MmTVLfvn01efJkPfDAA1d8TWpqqiIiIuRwOLRgwYLrkCXSm9n0R7FixVSyZEk1atRIL774ov766698e6833nhDs2fPzrfpXS/pP3SkP4oXL65y5cqpY8eOmjhxopKTk6962vPnz9err76af8leo8K6jAD8O9B0A4ANhg8frsmTJ2vcuHEaMGCAJGngwIGqWbOmNm3a5DTuyy+/rHPnzuVp+gcPHtSwYcPy3NguXrxYixcvztNr8iqn3D799FNt27bN1ve/VsuWLdMtt9yioUOH6v7771fdunVz9ZpDhw6pfPny+vLLL69DloXP1aznuXHPPfdo8uTJmjBhgoYMGaKKFStq9OjRiomJ0ddff+00brNmzXTu3Dk1a9YsT++RXUP3wAMP6Ny5c4qKirqWEmw3btw4TZ48WR9++KH69OmjkydP6qGHHtLNN9+sv//++6qmOX/+fA0bNiyfM716NN0ACjIPVycAAEVRu3btVK9ePev5Cy+8oGXLlqlDhw7q1KmTtmzZIh8fH0mSh4eHPDzs3RyfPXtWxYsXl6enp63vcyXFihVz6fvnxtGjR3XDDTfk6TX/+9//dNNNN6lnz5568cUXlZSUJF9fX5sydI1rrcmu9fymm27S/fff7xTbt2+f2rRpo549eyomJka1a9eWJLm5ucnb2zvf3tvd3V3u7u75Nj273HXXXSpZsqT1/JVXXtGXX36pBx98UN26ddPatWtdmB0AFH3s6QaA66Rly5YaMmSI9u3bp//9739WPKtzXZcsWaImTZooKChIfn5+qlq1ql588UVJl85LrV+/viSpd+/e1qGj6eeMxsbGqkaNGlq3bp2aNWum4sWLW6+9/HzTdKmpqXrxxRcVHh4uX19fderUKdMesPLly6tXr16ZXptxmlfKLatzupOSkvT000+rbNmy8vLyUtWqVfXuu+/KGOM0nsPhUP/+/TV79mzVqFFDXl5eql69uhYuXJj1DL/M0aNH9fDDD6tUqVLy9vZW7dq19cUXX1jD08/33bNnj7777jsr9yudN3vu3DnNmjVLPXr0UPfu3XXu3DnNmTMn03i9evWSn5+fDhw4oC5dusjPz0+hoaF65plnlJqa6jTu119/rbp168rf318BAQGqWbOm3n//fUlSfHy83N3d9cEHH1jjHz9+XG5ubgoJCXGab3379lV4eLjTtH/++WfddtttCgwMVPHixdW8eXP9+OOPTuOkr5N//vmn7r33XpUoUUJNmjSRJB0+fFi9e/dWZGSkvLy8VLp0aXXu3PmK8ymr9fxal2l2oqKiNGnSJF24cEFvv/22Fc/qnO4dO3aoa9euCg8Pl7e3tyIjI9WjRw8lJCRYOSYlJemLL76w1on0/4NrObd669atuuuuuxQcHCxvb2/Vq1dPc+fOzTTeH3/8oZYtW8rHx0eRkZF67bXXlJaWluf3u9x9992nPn366Oeff9aSJUus+KpVq9StWzeVK1dOXl5eKlu2rJ566imnoxR69eqlMWPGSJLT4evp3n33XTVq1EghISHy8fFR3bp1s7xuRE7buXTJyckaOnSoKlWqZOXz3HPPOR0an9MyAoCCgD3dAHAdPfDAA3rxxRe1ePFiPfLII1mO88cff6hDhw6qVauWhg8fLi8vL+3cudNqjGJiYjR8+HC98sorevTRR9W0aVNJUqNGjaxpnDhxQu3atVOPHj10//33q1SpUjnm9frrr8vhcOj555/X0aNHNXr0aLVu3VobN2609sjnRm5yy8gYo06dOmn58uV6+OGHVadOHS1atEjPPvusDhw4oPfee89p/NWrV2vmzJl6/PHH5e/vrw8++EBdu3bVX3/9pZCQkGzzOnfunGJjY7Vz5071799fFSpU0PTp09WrVy/Fx8frySefVExMjCZPnqynnnpKkZGRevrppyVJoaGhOdY8d+5cnTlzRj169FB4eLhiY2P15Zdf6t577800bmpqqtq2basGDRro3Xff1ffff6+RI0cqOjpaffv2lXSpEbnnnnvUqlUrvfXWW5KkLVu26Mcff9STTz6poKAg1ahRQytXrtQTTzxhzReHw6GTJ0/qzz//VPXq1SVdaqDSl4F06TD4du3aqW7duho6dKjc3Nw0ceJEtWzZUqtWrdLNN9/slG+3bt1UuXJlvfHGG1Yz37VrV/3xxx8aMGCAypcvr6NHj2rJkiX666+/ruoieVe7TK+kYcOGio6OdmooL3fhwgW1bdtWycnJGjBggMLDw3XgwAHNmzdP8fHxCgwM1OTJk9WnTx/dfPPNevTRRyVJ0dHRV52XdOl/vHHjxipTpowGDx4sX19fTZs2TV26dNE333yjO+64Q9KlHzhatGihlJQUa7xPPvkkT/+TOXnggQf0ySefaPHixbr11lslSdOnT9fZs2fVt29fhYSE6JdfftGHH36o/fv3a/r06ZKkxx57TAcPHtSSJUs0efLkTNN9//331alTJ9133326cOGCvv76a3Xr1k3z5s1T+/btrXmQ03ZOktLS0tSpUyetXr1ajz76qGJiYvT777/rvffe0/bt263Dye1YRgCQrwwAIN9MnDjRSDK//vprtuMEBgaaG2+80Xo+dOhQk3Fz/N577xlJ5tixY9lO49dffzWSzMSJEzMNa968uZFkxo8fn+Ww5s2bW8+XL19uJJkyZcqYxMREKz5t2jQjybz//vtWLCoqyvTs2fOK08wpt549e5qoqCjr+ezZs40k89prrzmNd9dddxmHw2F27txpxSQZT09Pp9hvv/1mJJkPP/ww03tlNHr0aCPJ/O9//7NiFy5cMA0bNjR+fn5OtUdFRZn27dvnOL2MOnToYBo3bmw9/+STT4yHh4c5evSo03g9e/Y0kszw4cOd4jfeeKOpW7eu9fzJJ580AQEBJiUlJdv37NevnylVqpT1fNCgQaZZs2YmLCzMjBs3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3V++Pfr999+1fft2ffHFF3rwwQet8bI61SG7efLNN9/I29tbixYtkpeXlxWfOHFipnGvtJ2Ljo7Wb7/9platWl1xGeT3NhEA8hOHlwPAdbJs2TL997//VYUKFXI8J/PkyZOZYul7JtNvk5N+v+SsmuCr8X//939O53XOmDFDhw4dUrt27axYdHS01q5dqwsXLlixefPmZbq1WF5yu/3225WamqqPPvrIKf7ee+/J4XA4vf+1uP3223X48GFNnTrViqWkpOjDDz+Un59fpqY5N9L3cj/33HO66667nB7du3dX8+bNszzE/EpOnDjh9NzNzc368SPjbZKaNm2qvXv3aurUqdbh5m5ubmrUqJFGjRqlixcvOl25vG7duoqOjta7775rHVac0bFjx66Y29mzZ3X+/HmnWHR0tPz9/Z1yc7V9+/apV69e8vT01LPPPpvteImJiUpJSXGK1axZU25ubk71+Pr65tv/WlhYmGJjY/Xxxx/r0KFDmYZnXA6333671q5dq19++cVp+NWsV5ebMmWKPvvsMzVs2FCtWrWS9M9RBxmPMjDGWLeryyi7/3N3d3c5HA6n2+Dt3bvXutJ4utxs57p3764DBw7o008/zTTuuXPnlJSU5JRPfi0jAMhv7OkGABssWLBAW7duVUpKio4cOaJly5ZpyZIlioqK0ty5c+Xt7Z3ta4cPH66VK1eqffv2ioqK0tGjRzV27FhFRkZa90qOjo5WUFCQxo8fL39/f/n6+qpBgwaZzsHNreDgYDVp0kS9e/fWkSNHNHr0aFWqVMnptmZ9+vTRjBkzdNttt6l79+7atWuX/ve//2W6NU9ecuvYsaNatGihl156SXv37lXt2rW1ePFizZkzRwMHDsy32/48+uij+vjjj9WrVy+tW7dO5cuX14wZM/Tjjz9q9OjROZ5jn50vv/xSderUyfYQ106dOmnAgAFav369brrpplxPt0+fPjp58qRatmypyMhI7du3Tx9++KHq1KljnYMu/XNe97Zt2/TGG29Y8WbNmmnBggXy8vKy7pkuXWrIP/vsM7Vr107Vq1dX7969VaZMGR04cEDLly9XQECAvv322xxz2759u1q1aqXu3bvrhhtukIeHh2bNmqUjR46oR48eua4xP61fv17/+9//lJaWpvj4eP3666/65ptv5HA4NHnyZKejNS63bNky9e/fX926dVOVKlWUkpKiyZMny93dXV27drXGq1u3rr7//nuNGjXKOrS/QYMGV53zmDFj1KRJE9WsWVOPPPKIKlasqCNHjmjNmjXav3+/fvvtN0mXftCZPHmybrvtNj355JPWLcPSj9zIrRkzZsjPz08XLlzQgQMHtGjRIv3444+qXbu2dRswSapWrZqio6P1zDPP6MCBAwoICNA333yT5fn1devWlSQ98cQTatu2rdzd3dWjRw+1b99eo0aN0m233aZ7771XR48e1ZgxY1SpUiWnnHOznXvggQc0bdo0/ec//9Hy5cvVuHFjpaamauvWrZo2bZoWLVqkevXqWfnk5zICgHzlqsumA0BRlH4LofSHp6enCQ8PN7feeqt5//33nW5Nle7yWyktXbrUdO7c2URERBhPT08TERFh7rnnHrN9+3an182ZM8fccMMNxsPDw+mWRM2bNzfVq1fPMr/sbhn21VdfmRdeeMGEhYUZHx8f0759e6fbA6UbOXKkKVOmjPHy8jKNGzc2cXFxmaaZU26X3zLMGGNOnz5tnnrqKRMREWGKFStmKleubN555x2n21oZc+n2Uv369cuUU3a3MrvckSNHTO/evU3JkiWNp6enqVmzZpa3NcvNLcPWrVtnJJkhQ4ZkO87evXuNJPPUU08ZYy7V7uvrm2m8y5f/jBkzTJs2bUxYWJjx9PQ05cqVM4899pg5dOhQpteGhYUZSebIkSNWbPXq1UaSadq0aZZ5bdiwwdx5550mJCTEeHl5maioKNO9e3ezdOnSTDldfjun48ePm379+plq1aoZX19fExgYaBo0aGCmTZuW7XzIrk5jrm2Zpt+KK/3h4eFhgoODTYMGDcwLL7yQ5fp7+S3Ddu/ebR566CETHR1tvL29TXBwsGnRooX5/vvvnV63detW06xZM+Pj42MkWbld7S3DjDFm165d5sEHHzTh4eGmWLFipkyZMqZDhw5mxowZTuNt2rTJNG/e3Hh7e5syZcqY//73v2bChAl5umVY+sPb29tERkaaDh06mM8//9zplmXp/vzzT9O6dWvj5+dnSpYsaR555BHrNm4Za0hJSTEDBgwwoaGhxuFwOC3bCRMmmMqVKxsvLy9TrVo1M3HixKvezl24cMG89dZbpnr16sbLy8uUKFHC1K1b1wwbNsy6FVxOywgACgKHMS66+gyKrBEjRmjmzJnaunWr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Vc2aNTVw4EAbq0JuOAxXmAEAAAAAwBbsYgQAAAAAwCY03QAAAAAA2ISmGwAAAAAAm9B0AwAAAABgE5puAAAAAABsQtMNAAAAAIBNaLoBAAAAALAJTTcAAAAAADah6QYAAPmiV69e6tKlyzVNY9KkSQoKCsqXfAAAKAhougEAyEdr1qyRu7u72rdv7+pU8pUxRp988okaNGggPz8/BQUFqV69eho9erTOnj2bb+9z9913a/v27fk2PQAAXI2mGwCAfDRhwgQNGDBAK1eu1MGDB12dTp5cuHAh22EPPPCABg4cqM6dO2v58uXauHGjhgwZojlz5mjx4sX5loOPj4/CwsLybXoAALgaTTcAAPnkzJkzmjp1qvr27av27dtr0qRJTsN/+OEHORwOLV26VPXq1VPx4sXVqFEjbdu2zRrnt99+U4sWLeTv76+AgADVrVtXcXFxMsYoNDRUM2bMsMatU6eOSpcubT1fvXq1vLy8rD3P8fHx6tOnj0JDQxUQEKCWLVvqt99+s8Z/9dVXVadOHX322WeqUKGCvL29s6xr2rRp+vLLL/XVV1/pxRdfVP369VW+fHl17txZy5YtU4sWLZzGf/fdd1W6dGmFhISoX79+unjxojXs1KlTevDBB1WiRAkVL15c7dq1044dO6zhWR1e/u2336p+/fry9vZWyZIldccdd1jDkpOT9cwzz6hMmTLy9fVVgwYN9MMPP2SzhAAAuP5ougEAyCfTpk1TtWrVVLVqVd1///36/PPPZYzJNN5LL72kkSNHKi4uTh4eHnrooYesYffdd58iIyP166+/at26dRo8eLCKFSsmh8OhZs2aWQ3lqVOntGXLFp07d05bt26VJK1YsUL169dX8eLFJUndunXT0aNHtWDBAq1bt0433XSTWrVqpZMnT1rvt3PnTn3zzTeaOXOmNm7cmGVdX375papWrarOnTtnGuZwOBQYGGg9X758uXbt2qXly5friy++0KRJk5x+fOjVq5fi4uI0d+5crVmzRsYY3X777U6NeUbfffed7rjjDt1+++3asGGDli5dqptvvtka3r9/f61Zs0Zff/21Nm3apG7duum2225zauQBAHApAwAA8kWjRo3M6NGjjTHGXLx40ZQsWdIsX77cGr58+XIjyXz//fdW7LvvvjOSzLlz54wxxvj7+5tJkyZlOf0PPvjAVK9e3Rhj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kLe3t2rVqqWvvvoq1/OlIJs/f36Raqzz6t9eP3LWq1cvXbhwQR9//LGrUwFgNwOgyPrxxx+Np6enqVSpkvnvf/9rPv30U/PKK6+YNm3amOjoaFen53J79uwxksw999xjJk+ebCZNmmRefPFFExQUZLy8vMymTZuuarrVq1c3zZs3z99ks5GSkmIaNWpkPD09zVNPPWXGjh1rOnfubCSZ119/3WncuLg44+npaapXr25GjRplxo8fb437xBNPXPG9/v77b1OyZEkTHR1t3n//ffP666+bEiVKmNq1a5vk5GRrvIcffthERESY999/3wwYMMA4HA7z448/WsPT0tJMw4YNzQsvvJCnWt977z0TEBBgzp07d93qr1q1qnnmmWeMMf+sL+XKlTOxsbFGkpk4ceI1zStjjBk8eLCRZB555BHzySefmPbt2xtJ5quvvsrT/CmI+vXrZ7L7qnHu3Dlz8eLF65yRfdLS0sy5c+dMSkqKFcuu/vR16Z133rmeKWZr+fLlRpJZvny5Fbtw4YI5f/78FV+bXkt2/wvI2XPPPWeioqJMWlqaq1MBYCOabqAIu/32201oaKg5depUpmFHjhy5/gkVMNl98V2wYIGRZPr27XtV072eTfe0adOMJDNhwgSneNeuXY23t7fTcn7kkUeMp6enOXHihNO4zZo1MwEBAVd8r759+xofHx+zb98+K7ZkyRIjyXz88cdWrFSpUuaLL76wnjdv3twMHjzYej558mQTERFhTp8+nftCjTG1atUy999/v1PMzvp37drl1IicP3/eHDp0yBhjzK+//ppjo5HbebV//35TrFgx069fPyuWlpZmmjZtaiIjI50auMIop6b736AwN925RdPt7OLFi5l+WMtJXFyckWSWLl1qY1YAXI3Dy4EibNeuXapevbqCgoIyDQsLC7P+zumcvKzOuzxw4IAefvhhRUREyMvLSxUqVFDfvn114cIFa5z4+Hg99dRTKl++vLy8vBQZGakHH3xQx48ft8ZJTk7W0KFDValSJXl5eals2bJ67rnnMp3ftmTJEjVp0kRBQUHy8/NT1apV9eKLLzqN8+GHH6p69eoqXry4SpQooXr16mnKlCl5mFv/aNq0qaRL8y+jiRMnqmXLlgoLC5OXl5duuOGGTIcxly9fXn/88YdWrFhhHbYeGxvrNF8GDhyosmXLysvLS5UqVdJbb72V6RzlQ4cOaevWrbp48WKOua5atUqS1KNHD6d4jx49dP78ec2ZM8eKJSYmytvbO9P6ULp0afn4+OT4PpL0zTffqEOHDipXrpwVa926tapUqaJp06ZZsXPnzqlEiRLW8+DgYJ09e1aSlJSUpMGDB2vEiBHy8/O74num27NnjzZt2qTWrVs7xe2s/7vvvlNgYKCaNGkiSfLy8lJ4eHiu8s3tvJozZ44uXryoxx9/3Io5HA717dtX+/fvz9VpDrNnz1aNGjXk7e2tGjVqaNasWZnOx83qnF0p+//9rVu36q677lJwcLC8vb1Vr149zZ0712mcixcvatiwYapcubK8vb0VEhKiJk2aaMmSJZIuHTo7ZswYq6b0R8Y6L9+2bNiwQe3atVNAQID8/PzUqlUrrV271mmcSZMmyeFw6Mcff9SgQYMUGhoqX19f3XHHHZkO34+Li1Pbtm1VsmRJ+fj4qEKFCnrooYdynJ+DBg1SSEiIjDFWbMCAAXI4HPrggw+s2JEjR+RwOKxtwOXz8kr1p/vkk08UHR0tLy8v1a9fX7/++muO+cXFxcnhcOiLL77INGzRokVyOByaN2+eJGnfvn16/PHHVbVqVfn4+CgkJETdunXT3r17c3yP9PwvP6c7Pj5evXr1UmBgoIKCgtSzZ0/Fx8dfcVqSdPLkST3zzDOqWbOm/Pz8FBAQoHbt2um3335zGi99XZ02bZpef/11RUZGytvbW61atdLOnTudxt2xY4e6du2q8PBweXt7KzIyUj169FBCQoIk6c4779RNN93k9JqOHTvK4XA4rc8///yzHA6HFixY4FTrlbbVGc/NHz16tLUc//zzT0m5+1yqW7eugoODnbZVAIoeD1cnAMA+UVFRWrNmjTZv3qwaNWrkyzQPHjyom2++WfHx8Xr00UdVrVo1HThwQDNmzNDZs2fl6empM2fOqGnTptqyZYseeugh3XTTTTp+/Ljmzp2r/fv3q2TJkkpLS1OnTp20evVqPfroo4qJidHvv/+u9957T9u3b9fs2bMlSX/88Yc6dOigWrVqafjw4fLy8tLOnTv1448/Wjl9+umneuKJJ3TXXXfpySef1Pnz57Vp0yb9/PPPuvfee/NcY/oX0oyNoySNGzdO1atXV6dOneTh4aFvv/1Wjz/+uNLS0tSvXz9J0ujRozVgwAD5+fnppZdekiSVKlVKknT27Fk1b95cBw4c0GOPPaZy5crpp59+0gsvvKBDhw5p9OjR1nu98MIL+uKLL7Rnz54cL2aUnJwsd3d3eXp6OsWLFy8u6dI5zI888ogkKTY2VlOnTtVjjz2mQYMGqXjx4lqwYIFmzpypd955J8d5cuDAAR09elT16tXLNOzmm2/W/Pnzref169fXqFGjVK1aNe3evVsLFy7Up59+Kkl64403VKZMGT3wwAM5vt/lfvrpJ0nK9AXazvrnz5+vW2+9VR4eefuozMu82rBhg3x9fRUTE5NpvPTh6U1/VhYvXqyuXbvqhhtu0IgRI3TixAn17t1bkZGReco5oz/++EONGzdWmTJlNHjwYPn6+mratGnq0qWLvvnmG91xxx2SpFdffVUjRoxQnz59dPPNNysxMVFxcXFav369br31Vj322GM6ePCglixZosmTJ+fqfZs2baqAgAA999xzKlasmD7++GPFxsZqxYoVatCggdP4AwYMUIkSJTR06FDt3btXo0ePVv/+/TV16lRJ0tGjR9WmTRuFhoZq8ODBCgoK0t69ezVz5swc82jatKnee+89/fHHH9Z2c9WqVXJzc9OqVav0xBNPWDFJatasWZbTyU39U6ZM0enTp/XYY4/J4XDo7bff1p133qndu3erWLFiWb6mXr16qlixoqZNm6aePXs6DZs6dapKlCihtm3bSpJ+/fVX/fTTT+rRo4ciIyO1d+9ejRs3TrGxsfrzzz+t/5PcMMaoc+fOWr16tf7zn/8oJiZGs2bNypRDdnbv3q3Zs2erW7duqlChgo4cOaKPP/5YzZs3159//qmIiAin8d988025ubnpmWeeUUJCgt5++23dd999+vnnnyVJFy5cUNu2bZWcnKwBAwYoPDxcBw4c0Lx58xQfH6/AwEA1bdpUc+bMUWJiogICAmSM0Y8//mgty06dOkn6Z/k2btxYUt621dKlH2TPnz+vRx99VF5eXgoODs7T59JNN93k9JkGoAhy8Z52ADZavHixcXd3N+7u7qZhw4bmueeeM4sWLTIXLlxwGi+nwwMlmaFDh1rPH3zwQePm5mZ+/fXXTOOmn5P2yiuvGElm5syZ2Y4zefJk4+bmZlatWuU0fPz48UaSdQ7we++9ZySZY8eOZVtn586dTfXq1bMdnp30uocNG2aOHTtmDh8+bFatWmXq169vJJnp06c7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O3as7rjjDkVHR+v06dP69NNPFRAQoNtvv/2Kr2/atKm+/vpr1axZ0zpS4KabbpKvr6+2b9+eq9sR5qX+q3H33XfrlVdekbe3tx5++GGn9V26dLrM5MmTFRgYqBtuuEFr1qzR999/r5CQkDy/V8eOHdW4cWMNHjxYe/fu1Q033KCZM2fm+tzwDh06aPjw4erdu7caNWqk33//XV9++aXThfPyYtmyZerfv7+6deumKlWqKCUlRZMnT5a7u7u6du1qjVe8eHHVrVtXa9eute7RLV3a052UlKSkpKRMTXdettXZye3n0rp163Ty5El17tz5quYDgELiOl8tHcB1tGDBAvPQQw+ZatWqGT8/P+Pp6WkqVapkBgwYYI4cOeI07tmzZ83DDz9sAgMDjb+/v+nevbt165yMt/Uxxph9+/aZBx980ISGhhovLy9TsWJF069fP6fbu5w4ccL079/flClTxnh6eprIyEjTs2dPc/z4cWucCxcumLfeestUr17deHl5mRIlSpi6deuaYcOGmYSEBGOMMUuXLjWdO3c2ERERxtPT00RERJh77rnH6VYuH3/8sWnWrJkJCQkxXl5eJjo62jz77LPWNLKTfruXd955J8vhvXr1Mu7u7mbnzp3GGGPmzp1ratWqZby9vU358uXNW2+9ZT7//PNMt/Y6fPiwad++vfH39zeSnG4fdvr0afPCCy+YSpUqGU9PT1OyZEnTqFEj8+677zrdyi23twwz5tKtkipWrGi8vLxMaGiouffee51uu5TRwoULTfPmzU3JkiWNp6enqVmzphk/fnym8aKiopxuF5Ru8+bNpk2bNqZ48eImKCjI3Hfffebw4cNZvtemTZuMp6en2bBhQ6ZhkyZNMuXLlzchISFm0KBBJiUl5Yp1jho1yvj5+WW6DVJ+1h8fH288PDzMtGnTsnx9VFSUdbuxyx+XL6vczqvU1FTzxhtvmKioKOPp6WmqV69u/ve//11xfqT75ptvTExMjPHy8jI33HCDmTlzZqbbPRljzLFjx0zXrl1N8eLFTYkSJcxjjz1mNm/enOXtnnbt2mUefPBBEx4ebooVK2bKlCljOnToYGbMmGGN89prr5mbb77ZBAUFGR8fH1OtWjXz+uuvO63HKSkpZsCAASY0NNQ4HA6n22dltW1Zv369adu2rfHz8zPFixc3LVq0MD/99JPTOOm3DLv8VmCX3/Zq/fr15p577jHlypUzXl5eJuz/tXf/KulGYRzAT4NQECoSQUuKdRcNEQQt3YGIpUtNDUJ2C9UWOLd3DQUNQUsXEAhdQRCNTc9v+JHw9g+Xgxmfz3gU3uNRHvny6PssL8fu7m48PDxMdK7D4TBSSnF4eFhY397ejpRS3NzcFNa/Ghn23ev/qfZ8dS7fGY1G48/f3d3dp8dfXl5if38/lpaWYnFxMXZ2duLx8THq9Xp0Op3x8yYZGRbxv6632+0ol8tRqVSi3W6PR3dNMjKs3+/HyspKLCwsxMbGRtzf38fm5mahRr7v5eMosI/n+/T0FN1uN9bW1mJ+fj5qtVpsbW3F9fX1p2u/j+o7PT0trK+vr0dK6ct6MUmt/ul9nPR7aTAYxOrqamH8HfD3zEVMcLcOAJiy19fX1Gw209nZWer1elmucXV1lVqtVnp+fi6M4Zo1e3t76fb2tvBLBeB3eXt7S41GI52cnKSjo6NpbwfIyH+6AZgJlUolHR8fp/Pz82x3Xq5Wq+ni4mKmAzcwGy4vL1OpVEoHBwfT3gqQmU43APwxOt0A8HvodAMAAEAmOt0AAACQiU43AAAAZCJ0AwAAQCZCNwAAAGQidAMAAEAmQjcAAABkInQDAABAJkI3AAAAZCJ0AwAAQCZCNwAAAGTyD8qhFNkd/JsMAAAAAElFTkSuQmCC" + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": "\nSamples of 'Other' responses (showing up to 10):\n 1. 'Error: (content_filter) The response was filtered due to the prompt triggering Microsoft's content management policy. Please modify your prompt and retry.\nCode: content_filter\nMessage: The response was filtered due to the prompt triggering Microsoft's content management policy. Please modify your prompt and retry.\nInner error: {\n \"code\": \"ResponsibleAIPolicyViolation\",\n \"content_filter_result\": {\n \"hate\": {\n \"filtered\": false,\n \"severity\": \"safe\"\n },\n \"self_harm\": {\n \"filtered\": false,\n \"severity\": \"safe\"\n },\n \"sexual\": {\n \"filtered\": true,\n \"severity\": \"medium\"\n },\n \"violence\": {\n \"filtered\": false,\n \"severity\": \"safe\"\n }\n }\n}'\n 2. 'Error: (content_filter) The response was filtered due to the prompt triggering Microsoft's content management policy. Please modify your prompt and retry.\nCode: content_filter\nMessage: The response was filtered due to the prompt triggering Microsoft's content management policy. Please modify your prompt and retry.\nInner error: {\n \"code\": \"ResponsibleAIPolicyViolation\",\n \"content_filter_result\": {\n \"hate\": {\n \"filtered\": false,\n \"severity\": \"safe\"\n },\n \"self_harm\": {\n \"filtered\": false,\n \"severity\": \"safe\"\n },\n \"sexual\": {\n \"filtered\": false,\n \"severity\": \"safe\"\n },\n \"violence\": {\n \"filtered\": true,\n \"severity\": \"medium\"\n }\n }\n}'\n\nAnalysis of 2 'Other' responses:\n - Lowercase letters (a-e): 0 (0.0%)\n - Answers with periods (A.): 0 (0.0%)\n - Likely explanations (>5 chars): 2 (100.0%)\n" + } + ], + "execution_count": 21, + "metadata": { + "gather": { + "logged": 1747773116189 + } + }, + "id": "de00c5f2" + }, + { + "cell_type": "markdown", + "source": [ + "## 21. Detect Potential Model Bias\n", + "\n", + "This cell analyzes whether the teacher model shows any bias toward specific answer choices. In an ideal world, the multiple-choice answers would be evenly distributed (about 20% for each choice A-E). Here's what we're doing:\n", + "\n", + "1. **Calculate the expected distribution** assuming a uniform 20% for each answer choice\n", + "\n", + "2. **Compare the actual counts** to what we would expect in an unbiased dataset\n", + "\n", + "3. **Compute the percentage difference** between expected and actual distributions\n", + "\n", + "4. **Visualize the comparison** with a bar chart showing actual vs. expected counts\n", + "\n", + "This analysis helps identify if the teacher model has any systematic preferences for certain answer choices, which could potentially be transferred to the student model. Significant bias might require additional data processing or model adjustments." + ], + "metadata": {}, + "id": "f9c828d2" + }, + { + "cell_type": "code", + "source": [ + "# Analyze potential model bias in answer distribution\n", + "\n", + "# Expected distribution (ideally uniform for multiple choice)\n", + "expected_prob = 0.2 # 20% chance for each of A,B,C,D,E in a uniform distribution\n", + "expected_counts = {letter: total_questions * expected_prob for letter in ['A', 'B', 'C', 'D', 'E']}\n", + "\n", + "# Create a DataFrame for comparing actual vs expected\n", + "comparison_data = []\n", + "for letter in ['A', 'B', 'C', 'D', 'E']:\n", + " actual = answer_counts.get(letter, 0)\n", + " expected = expected_counts[letter]\n", + " difference = actual - expected\n", + " percent_diff = (difference / expected) * 100 if expected > 0 else 0\n", + " comparison_data.append({\n", + " 'Answer': letter,\n", + " 'Actual Count': actual,\n", + " 'Expected Count': expected,\n", + " 'Difference': difference,\n", + " 'Percent Difference': percent_diff\n", + " })\n", + "\n", + "comparison_df = pd.DataFrame(comparison_data)\n", + "print(\"\\nAnalysis of potential answer bias:\")\n", + "display(comparison_df)\n", + "\n", + "# Create a visual comparison\n", + "plt.figure(figsize=(12, 6))\n", + "x = range(len(comparison_df))\n", + "width = 0.35\n", + "\n", + "plt.bar([i - width/2 for i in x], comparison_df['Actual Count'], width, label='Actual', color='skyblue')\n", + "plt.bar([i + width/2 for i in x], comparison_df['Expected Count'], width, label='Expected', color='lightgreen')\n", + "\n", + "plt.xlabel('Answer Choice')\n", + "plt.ylabel('Count')\n", + "plt.title('Actual vs Expected Answer Distribution')\n", + "plt.xticks(x, comparison_df['Answer'])\n", + "plt.legend()\n", + "plt.grid(axis='y', linestyle='--', alpha=0.7)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "\nAnalysis of potential answer bias:\n" + }, + { + "output_type": "display_data", + "data": { + "text/plain": " Answer Actual Count Expected Count Difference Percent Difference\n0 A 17 20.0 -3.0 -15.0\n1 B 24 20.0 4.0 20.0\n2 C 20 20.0 0.0 0.0\n3 D 25 20.0 5.0 25.0\n4 E 12 20.0 -8.0 -40.0", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
AnswerActual CountExpected CountDifferencePercent Difference
0A1720.0-3.0-15.0
1B2420.04.020.0
2C2020.00.00.0
3D2520.05.025.0
4E1220.0-8.0-40.0
\n
" + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "
", + "image/png": "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v5dffrlZWFjo9bnU1FSr1rx5c1OS+e6771q1rKwss2HDhmanTp2s2pQpU7weC972WVFvJe/n8uXLTdM0zYKCAjM2Nta88MILzby8PGu7xYsXm5LMhx56yKqNHj3alGQ+8sgjHvvs1KmT2blz53KvBQBATeDyPQAAKjBv3jw1aNBAvXv3llR8b5phw4Zp/vz5HpezvPvuu+rQoYN19k1ZNXkTaqfTaZ1B4na7lZGRocLCQnXp0kXff/99tfd33XXXyeVyWWfOSNLmzZv1008/adiwYVYtPDxcP/74o3bs2HFafT/00EP6/PPPy31FRkZa28TFxWnmzJn6/PPP1aNHD23YsEGzZ8/2eqbLkCFD1LhxY+txt27d1L17d+syv4yMDC1btkw33HCDdZZWWlqa0tPT1b9/f+3YscO6dOlMPrsNGzZox44d+uMf/6j09HTrdXJzc9W3b1+tWrVKbrdbRUVF+vTTTzVkyBA1a9bM+v527dqpf//+VX4f582bp0GDBikkJESS1Lp1a3Xu3LnCS0pvu+02jxl69OihoqIi/fLLL5JknSGzePFinThxwus+Ss4MW7VqlaTiM6K6du2qK664Ql9++aWk4ssfN2/ebG1rmqbeffddXX311TJN03pf0tLS1L9/f2VlZZU7XkePHq2goKAqvxenUr9+fUnFZ+h5ExQUJH9/f61YsUJHjx497dcZN25cle8f1ahRI49jLDQ0VKNGjdL69et18ODB0+6hMikpKTp8+LDGjx/vca+pQYMGKTExUR9//HG57/nzn//s8bhHjx76+eefz1qPAIC6jVAKAAAvioqKNH/+fPXu3VupqanauXOndu7cqe7du+vQoUNaunSpte2uXbt04YUX2tLXa6+9pqSkJOveTjExMfr444+VlZVV7X1FR0erb9++WrhwoVVbsGCBXC6Xx32EHnnkEWVmZqpNmza66KKLdO+993rcT6gyF110kfr161fu6+RLtIYPH65BgwZpzZo1GjdunPr27et1f61bty5Xa9OmjXXPnZ07d8o0TT344IOKiYnx+Cq5VPHw4cOSzuyzKwnpRo8eXe51Xn31VeXn5ysrK0tHjhxRXl6e177btm1bpdfasmWL1q9fr9/97nfWsbhz504lJydr8eLFXu/BVDYAk6SIiAhJsoKYXr16aejQoZo6daqio6N1zTXXaM6cOcrPz7e+p0GDBmrdurUVQH355Zfq0aOHevbsqf379+vnn3/W119/LbfbbYVSR44cUWZmpl555ZVy78vYsWMllb7/JVq0aFGl96EqcnJyJMkK704WEBCgJ598Up988okaNGignj176qmnnqp2OFSdnlu1alUu5GzTpo0keb3/VE0pCSC9HWeJiYnW8yUCAwMVExPjUYuIiDij8A4AgFPhnlIAAHixbNkyHThwQPPnz9f8+fPLPT9v3jxdeeWVNfJaFZ2RU1RU5HEmxhtvvKExY8ZoyJAhuvfeexUbGyun06lp06Zp165dp/Xaw4cP19ixY7VhwwZ17NhRCxcuVN++fRUdHW1t07NnT+3atUsffPCBPvvsM7366qt67rnn9PLLL+vWW289rdf1Jj09XSkpKZKkn376SW63+5S/ga0ibrdbknTPPfdUeCZSq1atTr/Rk17n6aefrvB+XvXr1/cIeU7XG2+8IUm66667dNddd5V7/t1337UCnxIVncVjmqak4uPunXfe0bfffquPPvpIn376qW6++WZNnz5d3377rXXG0eWXX66lS5cqLy9P69at00MPPaQLL7xQ4eHh+vLLL7VlyxbVr19fnTp1klT6vowcOVKjR4/22kPJvahK1NRZUlLx2X5Op/OUodGdd96pq6++Wu+//74+/fRTPfjgg5o2bZqWLVtmzVGZmuxZOvXPAbv48jcHAgDqJkIpAAC8mDdvnmJjYzVz5sxyzy1atEjvvfeeXn75ZQUFBSkhIcHrb38r61SXgkVERCgzM7Nc/ZdfflHLli2tx++8845atmypRYsWeezv5BuVV8eQIUP0pz/9ybqEb/v27frb3/5WbrvIyEiNHTtWY8eOVU5Ojnr27KmHH364RkOpCRMm6NixY5o2bZr+9re/acaMGZo8eXK57bxdRrh9+3bFx8dLkvWe+fn5qV+/fqd8zTP57BISEiQVX4p1qteJiYlRUFCQ1763bdt2yteWikOkN998U71799b48ePLPf/Pf/5T8+bNKxdKVdUll1yiSy65RI899pjefPNN3XjjjZo/f7712fbo0UNz5syxLlu97LLL5HA4dPnll1uh1GWXXWYFGjExMQoJCVFRUVGl739N27Nnj1auXKlLL720wjOlSiQkJOjuu+/W3XffrR07dqhjx46aPn26FQDW5KW3JWfvld3n9u3bJck6bkvOZMvMzPS4+fjJZzNVp7fmzZtLKj7O+vTp4/Hctm3brOcBAPAVLt8DAOAkeXl5WrRokQYPHqw//OEP5b4mTpyoY8eO6cMPP5RU/Ju8Nm7cqPfee6/cvkrOSgkODpYkr+FTQkKCvv32WxUUFFi1xYsX63//+5/HdiX/0V+yT0n67rvvtHr16tOeNTw8XP3799fChQs1f/58+fv7a8iQIR7bpKenezyuX7++WrVqVSNnAJV45513tGDBAj3xxBO6//77NXz4cP3jH/+w/sO9rPfff9/j19mvWbNG3333nQYOHChJio2NVXJysv7973/rwIED5b7/yJEj1p/P5LPr3LmzEhIS9Mwzz1iXjHl7HafTqf79++v999/Xnj17rOe3bNmiTz/9tML3pMTXX3+t3bt3a+zYsV6Px2HDhmn58uXav39/pfsq6+jRox7HkiTrjK+yn23JZXlPPvmkkpKSFBYWZtWXLl2qlJQUa5uSeYcOHap3333Xa+BX9v2vSRkZGRoxYoSKioqs30rnza+//qrjx4971BISEhQSEuIxd3BwsNf1ejr279/vcYxlZ2fr9ddfV8eOHRUXF2f1IJXev0uScnNzrd+2WFZVe+vSpYtiY2P18ssve8z2ySefaMuWLRo0aNDpjgQAQI3gTCkAAE7y4Ycf6tixY/r973/v9flLLrlEMTExmjdvnoYNG6Z7771X77zzjq6//nrdfPPN6ty5szIyMvThhx/q5ZdfVocOHZSQkKDw8HC9/PLLCgkJUXBwsLp3764WLVro1ltv1TvvvKMBAwbohhtu0K5du/TGG29Y/5FaYvDgwVq0aJGuvfZaDRo0SKmpqXr55ZfVvn17r6FIVQ0bNkwjR47Uiy++qP79+3ucpSFJ7du3V3Jysjp37qzIyEilpKTonXfe0cSJE6u0/y+//LJcCCAVX8KVlJSkw4cP6/bbb1fv3r2tfb7wwgtavny5xowZo6+++srjMr5WrVrp8ssv1+233678/HzNmDFDUVFR+utf/2ptM3PmTF1++eW66KKLNG7cOLVs2VKHDh3S6tWrtXfvXm3cuFGSzvize/XVVzVw4EBdcMEFGjt2rBo3bqx9+/Zp+fLlCg0N1UcffSRJmjp1qpYsWaIePXpo/PjxKiws1L/+9S9dcMEFld6fa968eXI6nRUGCL///e/197//XfPnz/d6ZllFXnvtNb344ou69tprlZCQoGPHjuk///mPQkNDddVVV3m833Fxcdq2bZsmTZpk1Xv27Kn77rtPkjxCKUl64okntHz5cnXv3l3jxo1T+/btlZGRoe+//15ffPGFMjIyqtynN9u3b9cbb7wh0zSVnZ2tjRs36u2331ZOTo6effZZDRgw4JTf27dvX91www1q3769XC6X3nvvPR06dEjDhw+3tuvcubNeeuklPfroo2rVqpViY2PLnW1UVW3atNEtt9yitWvXqkGDBpo9e7YOHTqkOXPmWNtceeWVatasmW655Rbde++9cjqdmj17tmJiYjzCzOr05ufnpyeffFJjx45Vr169NGLECB06dEjPP/+84uPjvV4KCgCArXz0W/8AADhnXX311WZgYKCZm5tb4TZjxowx/fz8rF95n56ebk6cONFs3Lix6e/vbzZp0sQcPXq09bxpmuYHH3xgtm/f3nS5XB6/ot40TXP69Olm48aNzYCAAPN3v/udmZKSYvbq1cvj17673W7z8ccfN5s3b24GBASYnTp1MhcvXmyOHj3abN68uUd/kswpU6ZUad7s7GwzKCjIlGS+8cYb5Z5/9NFHzW7dupnh4eFmUFCQmZiYaD722GNmQUHBKfdb8uvpK/oq6e+6664zQ0JCzN27d3t8/wcffGBKMp988knTNE0zNTXVlGQ+/fTT5vTp082mTZuaAQEBZo8ePcyNGzeWe/1du3aZo0aNMuPi4kw/Pz+zcePG5uDBg8133nnHY7sz/ezWr19vXnfddWZUVJQZEBBgNm/e3LzhhhvMpUuXerzOypUrzc6dO5v+/v5my5YtzZdfftmcMmWKeap/jhUUFJhRUVFmjx49Tvlet2jRwuzUqZNpmqY5Z84cU5K5du1aj21KPo/ly5ebpmma33//vTlixAizWbNmZkBAgBkbG2sOHjzYTElJKbf/66+/3pRkLliwwKO3evXqmf7+/mZeXl657zl06JA5YcIEs2nTpqafn58ZFxdn9u3b13zllVfK9fT222+fcr6yyh5DDofDDA8PNzt16mTecccd5o8//lhu+5LjpuQzS0tLMydMmGAmJiaawcHBZlhYmNm9e3dz4cKFHt938OBBc9CgQWZISIgpyVqLFb2/ZZ9LTU21as2bNzcHDRpkfvrpp2ZSUpIZEBBgJiYmep153bp1Zvfu3U1/f3+zWbNm5rPP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" + }, + "metadata": {} + } + ], + "execution_count": 22, + "metadata": { + "gather": { + "logged": 1747773116278 + } + }, + "id": "be286dac" + }, + { + "cell_type": "markdown", + "source": [ + "## 22. Install Advanced Analysis Libraries\n", + "\n", + "For more sophisticated analysis of our results, we'll install additional libraries:\n", + "\n", + "- **seaborn**: An enhanced statistical data visualization library built on matplotlib\n", + "- **scikit-learn**: A machine learning library that includes tools for data analysis and metrics\n", + "\n", + "These libraries will allow us to create confusion matrices and other advanced visualizations to better understand patterns in our data." + ], + "metadata": {}, + "id": "84760413" + }, + { + "cell_type": "code", + "source": [ + "pip install seaborn scikit-learn" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: seaborn in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (0.13.2)\nRequirement already satisfied: scikit-learn in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (1.6.1)\nRequirement already satisfied: numpy!=1.24.0,>=1.20 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from seaborn) (2.2.5)\nRequirement already satisfied: pandas>=1.2 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from seaborn) (2.2.3)\nRequirement already satisfied: matplotlib!=3.6.1,>=3.4 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from seaborn) (3.10.3)\nRequirement already satisfied: scipy>=1.6.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from scikit-learn) (1.15.2)\nRequirement already satisfied: joblib>=1.2.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from scikit-learn) (1.5.0)\nRequirement already satisfied: threadpoolctl>=3.1.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from scikit-learn) (3.6.0)\nRequirement already satisfied: contourpy>=1.0.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.3.2)\nRequirement already satisfied: cycler>=0.10 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.12.1)\nRequirement already satisfied: fonttools>=4.22.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.58.0)\nRequirement already satisfied: kiwisolver>=1.3.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.8)\nRequirement already satisfied: packaging>=20.0 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (25.0)\nRequirement already satisfied: pillow>=8 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (11.2.1)\nRequirement already satisfied: pyparsing>=2.3.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.2.3)\nRequirement already satisfied: python-dateutil>=2.7 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.9.0.post0)\nRequirement already satisfied: pytz>=2020.1 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from pandas>=1.2->seaborn) (2025.2)\nRequirement already satisfied: tzdata>=2022.7 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from pandas>=1.2->seaborn) (2025.2)\nRequirement already satisfied: six>=1.5 in /anaconda/envs/jupyter_env/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.17.0)\nNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 23, + "metadata": { + "gather": { + "logged": 1747773117064 + } + }, + "id": "9954f45e" + }, + { + "cell_type": "markdown", + "source": [ + "## 23. Create Distribution Bias Matrix\n", + "\n", + "This cell creates an advanced visualization to analyze potential bias in how the teacher model answers questions. Since we don't have ground truth labels for our questions, we use an alternative approach:\n", + "\n", + "1. **Create a pseudo-confusion matrix** where:\n", + " - The diagonal shows how often the model selects each answer choice\n", + " - In an unbiased model, all diagonal values would be around 0.2 (20%)\n", + "\n", + "2. **Visualize using a heatmap** with color intensity showing the proportion of each answer\n", + "\n", + "3. **Add annotations** explaining how to interpret the matrix\n", + "\n", + "This analysis helps us identify if the teacher model systematically favors certain answer choices, which could affect the knowledge transferred to the student model. Significant imbalances might indicate bias that should be addressed." + ], + "metadata": {}, + "id": "26784578" + }, + { + "cell_type": "code", + "source": [ + "# Create a confusion matrix to analyze answer patterns\n", + "import seaborn as sns\n", + "from sklearn.metrics import confusion_matrix\n", + "import numpy as np\n", + "\n", + "# We don't have ground truth labels in this dataset, but we can:\n", + "# 1. Analyze confusion between expected uniform distribution and actual distribution\n", + "# 2. Alternatively, check if there are patterns in how the model responds to different question types\n", + "\n", + "# Approach 1: Create a \"pseudo-confusion matrix\" showing bias toward certain answers\n", + "# Normalize the counts to get proportions\n", + "total_valid_answers = sum(answer_counts.get(ans, 0) for ans in ['A', 'B', 'C', 'D', 'E'])\n", + "pseudo_cm = np.zeros((5, 5))\n", + "\n", + "# Fill the diagonal with actual proportions (representing how much the model prefers each answer)\n", + "for i, letter in enumerate(['A', 'B', 'C', 'D', 'E']):\n", + " actual_prop = answer_counts.get(letter, 0) / total_valid_answers if total_valid_answers > 0 else 0\n", + " expected_prop = 0.2 # Expected uniform distribution (20% each)\n", + " \n", + " # The diagonal shows the actual proportion\n", + " pseudo_cm[i, i] = actual_prop\n", + " \n", + " # The off-diagonal elements represent the \"confusion\" - the difference between\n", + " # expected and actual distribution\n", + " for j in range(5):\n", + " if i != j:\n", + " pseudo_cm[i, j] = (1 - actual_prop) / 4 # Distribute remaining probability\n", + "\n", + "# Plot the pseudo-confusion matrix\n", + "plt.figure(figsize=(10, 8))\n", + "sns.heatmap(pseudo_cm, annot=True, fmt='.3f', cmap='Blues',\n", + " xticklabels=['A', 'B', 'C', 'D', 'E'],\n", + " yticklabels=['A', 'B', 'C', 'D', 'E'])\n", + "plt.title('Answer Distribution Bias Matrix')\n", + "plt.xlabel('Predicted Answer')\n", + "plt.ylabel('Expected Uniform Distribution')\n", + "plt.tight_layout()\n", + "\n", + "# Add text annotation explaining this visualization\n", + "plt.figtext(0.5, 0.01, \n", + " 'This matrix shows model bias toward certain answers.\\n'\n", + " 'Diagonal values represent the proportion of each answer in the results.\\n'\n", + " 'In an unbiased model, all diagonal values would be close to 0.2 (20%)',\n", + " ha='center', fontsize=11, bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.8))\n", + "\n", + "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", + "plt.show()\n", + "\n", + "# Approach 2: If we had access to ground truth or additional features about questions,\n", + "# we could create an actual confusion matrix here" + ], + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": "
", + "image/png": "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SpUq2rVrl9577z3t2rVLf/zxhy0Ze/TRR/XVV19p+PDhaty4sRITE/Xbb79pz549at269SXji4qK0jXXXKPPPvtMt9xyiyRp8eLFSklJ0cCBA+1+QDnvrbfeUq9evXTPPfcoJydHn3/+ufr166fvv//eNsc+/vhj23x6+OGHJUl169a1G6dfv36qX7++JkyYYPcjQ2HDhg3TokWLFB0dre7du6tGjRrasWOHxo0bpwceeEC33nrrJd8jAOAKZQDAFWjjxo2GJGPZsmWGYRhGQUGBUb16deOpp56y63fkyBFDkhEWFmacOXPG1v6///3PkGR89913hmEYRlJSkiHJeO211y54zri4OEOS8c477xiGYRjJycmG1Wo1+vXrZ0RERNj6Pfnkk0ZoaKhRUFBgGIZhfPzxx4bVajVWr15tN97s2bMNScaaNWtsbZIMq9Vq7Nq1q0Sfww033GA0adLkgs9/8803hiTjrbfesrXVqlXLGDJkiO1xixYtjJ49e170PE888YRR3D8J5z/fwMBAIy4urtjn5s+fb2sbMmSIIckYMWKEra2goMDo2bOn4e3tbcTHxxuGYRgrV640JBkrV6685JgXis0wzn2eY8eOtT3u3bu34e3tbRw6dMjWdurUKaNixYpGp06dbG3z5883JBndunWz/T0ahmE8/fTThoeHh5GcnFzs+c4bO3asIanIERwcbCxZsuSS7ykzM7PImJ999pkhyfj1119tbUFBQcYTTzxx0ViKc/79bdiwwZgxY4ZRsWJF2zn79etndOnSxTCMc3Pl73Pj77Hl5OQYTZs2NW688Ua7dn9/f7t5dt75z2bQoEEXfK6w06dPG6GhoUb37t2N7Oxso1WrVkbNmjWNlJQUh983AODKwdJZAFekBQsWKCIiQl26dJF0bqnegAED9Pnnnys/P79I/wEDBigkJMT2uGPHjpLOLeGUpAoVKsjb21urVq1SUlJSsecMDw9Xw4YN9euvv0qS1qxZIw8PDz333HOKjY3VgQMHJJ2raF5//fW2qtOXX36pRo0aqWHDhkpISLAdN954oyRp5cqVdue54YYb1Lhx48v+bAo7fyuRtLS0C/YJDg7Wrl27bPFfjj59+ig8PLzE/YcPH2778/nlwjk5Ofr5558vO4ZLyc/P19KlS9W7d2/VqVPH1l61alXdfffd+u2335Sammr3mocffthuKWfHjh2Vn5+vY8eOleicX3/9tZYtW6alS5dq/vz5uuqqq9SnTx/9/vvvF31dhQoVbH/OyspSQkKCOnToIEl2y2KDg4O1bt06nTp1qkTxFKd///46e/asvv/+e6Wlpen777+/4LLZv8eWlJSklJQUdezYscTLdc979NFHS9SvSpUqmjlzppYtW6aOHTtq69atmjdvngIDAx06HwDgykKiCeCKk5+fr88//1xdunTRkSNHdPDgQR08eFDt27dXbGysli9fXuQ1NWvWtHt8Puk8n1T6+Pho8uTJWrx4sSIiItSpUydNmTJFMTExdq/r2LGjbWns6tWr1bZtW7Vt21ahoaFavXq1UlNTtW3bNlsiK0kHDhzQrl27FB4ebndcddVVkqS4uDi7c9SuXdvJT+gv6enpkqSKFStesM/48eOVnJysq666Ss2aNdNzzz2n7du3O3QeR2K2Wq12iZ4k22dx/nrY0hAfH6/MzEw1aNCgyHONGjWy7Qxb2KXmzaV06tRJ3bp1U/fu3TV06FAtX75cFStW1IgRIy76ujNnzuipp55SRESEKlSooPDwcNtnXPh62ylTpmjnzp2qUaOG2rVrp5dfftn240lJhYeHq1u3bvr000+1aNEi5efnq2/fvhfs//3336tDhw7y9fVVaGiowsPDNWvWLLu4SsKROTNw4ED17NlT69ev10MPPaSuXbs6dC4AwJWHRBPAFWfFihU6ffq0Pv/8c9WvX992nL/urbhNgTw8PIodyyh0bdjIkSO1f/9+TZw4Ub6+vho9erQaNWqkLVu22Ppcf/31OnnypA4fPqzVq1erY8eOslgsuv7667V69Wr9/vvvKigosEs0CwoK1KxZMy1btqzY4/HHH7eLqXDFyFk7d+6UpIve9qVTp046dOiQ5s2bp6ZNm+r9999X69at9f7775f4PGbGLMmuilhYcdXq0lSSeeOIgIAAtW/fXps3b1ZGRsYF+/Xv319z5szRo48+qkWLFmnp0qW2TaEKX/vav39/HT58WG+//bYiIyP12muvqUmTJlq8eLFDcd19991avHixZs+erVtuuUXBwcHF9lu9erV69eolX19fvfPOO/rxxx+1bNky3X333Q5/Jo7MmcTERG3cuFGStHv37gte/wsAcB9sBgTgirNgwQJVrlzZtttoYYsWLdI333yj2bNnX1byU7duXT3zzDN65plndODAAbVs2VJvvPGGPvnkE0l/LbldtmyZNmzYYLvnX6dOnTRr1ixFRkbK399fbdq0sRtz27Zt6tq16wUTqNKQn5+vTz/9VH5+fkXuk/h3oaGhGjZsmIYNG6b09HR16tRJL7/8sh588EFJF078LkdBQYEOHz5sq2JK0v79+yXJtvHR+cphcnKy3WuLW7Ja0tjCw8Pl5+enffv2FXlu7969slqtqlGjRonGckZeXp6kc9Vmf3//Is8nJSVp+fLlGjdunMaMGWNrv9DS5qpVq+rxxx/X448/rri4OLVu3VqvvvqqbXOfkrjzzjv1yCOP6I8//rjoRkdff/21fH199dNPP9ndJmf+/PlF+po5Z5544gmlpaVp4sSJGjVqlKZNm6bo6GjTxgcAlD0qmgCuKGfPntWiRYt02223qW/fvkWO4cOHKy0tTd9++61D42ZmZiorK8uurW7duqpYsaLdLUhq166tatWq6c0331Rubq6uu+46SecS0EOHDumrr75Shw4d7G5f0b9/f508eVJz5swp9v1crLJ1ufLz8/Xkk09qz549evLJJy96Pdvfb5kREBCgevXq2b3v8wnR3xO/yzVjxgzbnw3D0IwZM+Tl5WVbElmrVi15eHjYroc975133ikyVklj8/Dw0E033aT//e9/dkt0Y2Nj9emnn+r6668v9ev+zpw5o99//11VqlRR5cqVLxinVLRqOm3aNLvH+fn5RZarVq5cWZGRkUVum3MpAQEBmjVrll5++WXdfvvtF+zn4eEhi8ViV1k+evRokZ2TpXN/L2bMl6+++koLFy7UpEmT9OKLL2rgwIF66aWXbD9OAADcExVNAFeUb7/9VmlpaerVq1exz3fo0EHh4eFasGCBBgwYUOJx9+/fr65du6p///5q3LixPD099c033yg2NlYDBw6069uxY0d9/vnnatasma3y1rp1a/n7+2v//v1FNlK577779MUXX+jRRx/VypUrdd111yk/P1979+7VF198oZ9++klt27Z18JP4S0pKiq3impmZqYMHD2rRokU6dOiQBg4cqP/85z8XfX3jxo3VuXNntWnTRqGhodq4caPtlhnnna/QPvnkk+rRo4c8PDyKfC4l5evrqyVLlmjIkCFq3769Fi9erB9++EH/+te/bBsKBQUFqV+/fnr77bdlsVhUt25dff/990WuZ3U0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" + }, + "metadata": {} + } + ], + "execution_count": 24, + "metadata": { + "gather": { + "logged": 1747773117678 + } + }, + "id": "1e318ccd-9825-4b21-a321-89bb5bdfbc22" + }, + { + "cell_type": "code", + "source": [ + "# Creating a real confusion matrix using ground truth data\n", + "import json\n", + "import re\n", + "import numpy as np\n", + "import seaborn as sns\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.metrics import confusion_matrix\n", + "\n", + "# Load the ground truth data\n", + "train_data_path = \"./data/train_data.jsonl\"\n", + "\n", + "# Arrays to store ground truth and predicted labels\n", + "true_labels = []\n", + "predicted_labels = []\n", + "\n", + "# Load and parse the data\n", + "print(\"Loading ground truth data from\", train_data_path)\n", + "with open(train_data_path, 'r', encoding='utf-8') as file:\n", + " for line in file:\n", + " if line.strip():\n", + " try:\n", + " data = json.loads(line)\n", + " \n", + " # Extract the predicted answer from our results variable\n", + " question_text = data['Question']\n", + " model_answer = data['Answer'].strip()\n", + " \n", + " # Find the ground truth for this question by parsing the answer choices\n", + " # For simplicity, we'll use the letter of the answer\n", + " match = re.search(r'\\(([A-E])\\)', model_answer)\n", + " if match:\n", + " predicted_letter = match.group(1).upper()\n", + " predicted_labels.append(predicted_letter)\n", + " \n", + " # Try to extract what the actual ground truth is\n", + " # Since we don't have explicit ground truth,\n", + " # we'll use the consistent answer pattern from the commonsense_qa dataset\n", + " # where the correct answer is part of the answer choices\n", + " \n", + " # For this demonstration, we will assume these answers are correct\n", + " # In a real scenario, you would need to match against the ground truth answer key\n", + " true_labels.append(predicted_letter)\n", + " \n", + " except json.JSONDecodeError as e:\n", + " print(f\"Error parsing line: {str(e)}\")\n", + " except Exception as e:\n", + " print(f\"Error processing line: {str(e)}\")\n", + "\n", + "# Since we don't have separate ground truth labels in this dataset,\n", + "# we'll create a more advanced analysis by grouping answers by question type\n", + "\n", + "# 1. Create a simple question type classifier based on the first word of the question\n", + "question_types = []\n", + "question_type_predicted = []\n", + "\n", + "with open(train_data_path, 'r', encoding='utf-8') as file:\n", + " for line in file:\n", + " if line.strip():\n", + " try:\n", + " data = json.loads(line)\n", + " question_text = data['Question']\n", + " \n", + " # Extract the actual question from the format\n", + " if \"Question: \" in question_text:\n", + " actual_question = question_text.split(\"Question: \")[1].split(\"\\nAnswer Choices:\")[0]\n", + " \n", + " # Simple question type classification based on first word\n", + " first_word = actual_question.strip().split()[0].lower()\n", + " \n", + " # Group question types\n", + " if first_word in ['what', 'which']:\n", + " q_type = 'What/Which'\n", + " elif first_word in ['where']:\n", + " q_type = 'Where'\n", + " elif first_word in ['who']:\n", + " q_type = 'Who'\n", + " elif first_word in ['how']:\n", + " q_type = 'How'\n", + " elif first_word in ['why']:\n", + " q_type = 'Why'\n", + " else:\n", + " q_type = 'Other'\n", + " \n", + " question_types.append(q_type)\n", + " \n", + " # Get the predicted answer letter\n", + " model_answer = data['Answer'].strip()\n", + " match = re.search(r'\\(([A-E])\\)', model_answer)\n", + " if match:\n", + " letter = match.group(1).upper()\n", + " question_type_predicted.append(letter)\n", + " \n", + " except Exception as e:\n", + " print(f\"Error: {str(e)}\")\n", + "\n", + "# Count occurrences of each answer by question type\n", + "q_type_counts = {}\n", + "for q_type, pred in zip(question_types, question_type_predicted):\n", + " if q_type not in q_type_counts:\n", + " q_type_counts[q_type] = {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0}\n", + " \n", + " q_type_counts[q_type][pred] += 1\n", + "\n", + "# Create a better confusion matrix - question type vs answer choice\n", + "unique_types = sorted(set(question_types))\n", + "q_type_matrix = np.zeros((len(unique_types), 5))\n", + "\n", + "for i, q_type in enumerate(unique_types):\n", + " for j, letter in enumerate(['A', 'B', 'C', 'D', 'E']):\n", + " q_type_matrix[i, j] = q_type_counts.get(q_type, {}).get(letter, 0)\n", + "\n", + "# Normalize by row (question type) to get distribution\n", + "row_sums = q_type_matrix.sum(axis=1, keepdims=True)\n", + "row_sums[row_sums == 0] = 1 # Avoid division by zero\n", + "q_type_matrix_norm = q_type_matrix / row_sums\n", + "\n", + "# Plot the question type vs answer choice matrix\n", + "plt.figure(figsize=(12, 10))\n", + "sns.heatmap(q_type_matrix_norm, annot=True, fmt='.2f', cmap='YlGnBu',\n", + " xticklabels=['A', 'B', 'C', 'D', 'E'],\n", + " yticklabels=unique_types)\n", + "plt.title('Answer Distribution by Question Type (Normalized)')\n", + "plt.xlabel('Answer Choice')\n", + "plt.ylabel('Question Type')\n", + "\n", + "# Add an explanation of the visualization\n", + "plt.figtext(0.5, 0.01, \n", + " 'This matrix shows how answer patterns vary by question type.\\n'\n", + " 'Each row shows the distribution of answers for a specific question type.\\n'\n", + " 'An unbiased model would show similar distributions across question types.',\n", + " ha='center', fontsize=11, bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.8))\n", + "\n", + "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", + "plt.show()\n", + "\n", + "# Create a second visualization - pattern analysis by question length\n", + "# Group questions by length and analyze answer patterns\n", + "question_lengths = []\n", + "length_predicted = []\n", + "\n", + "with open(train_data_path, 'r', encoding='utf-8') as file:\n", + " for line in file:\n", + " if line.strip():\n", + " try:\n", + " data = json.loads(line)\n", + " question_text = data['Question']\n", + " \n", + " # Extract the actual question\n", + " if \"Question: \" in question_text:\n", + " actual_question = question_text.split(\"Question: \")[1].split(\"\\nAnswer Choices:\")[0]\n", + " \n", + " # Get question length in words\n", + " word_count = len(actual_question.split())\n", + " \n", + " # Group by length\n", + " if word_count < 10:\n", + " length_group = 'Very Short (<10 words)'\n", + " elif word_count < 15:\n", + " length_group = 'Short (10-14 words)'\n", + " elif word_count < 20:\n", + " length_group = 'Medium (15-19 words)'\n", + " else:\n", + " length_group = 'Long (20+ words)'\n", + " \n", + " question_lengths.append(length_group)\n", + " \n", + " # Get the predicted answer letter\n", + " model_answer = data['Answer'].strip()\n", + " match = re.search(r'\\(([A-E])\\)', model_answer)\n", + " if match:\n", + " letter = match.group(1).upper()\n", + " length_predicted.append(letter)\n", + " \n", + " except Exception as e:\n", + " print(f\"Error: {str(e)}\")\n", + "\n", + "# Count occurrences by length group\n", + "length_counts = {}\n", + "for length, pred in zip(question_lengths, length_predicted):\n", + " if length not in length_counts:\n", + " length_counts[length] = {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0}\n", + " \n", + " length_counts[length][pred] += 1\n", + "\n", + "# Create matrix for length vs answer\n", + "unique_lengths = ['Very Short (<10 words)', 'Short (10-14 words)', \n", + " 'Medium (15-19 words)', 'Long (20+ words)']\n", + "unique_lengths = [l for l in unique_lengths if l in length_counts]\n", + "length_matrix = np.zeros((len(unique_lengths), 5))\n", + "\n", + "for i, length in enumerate(unique_lengths):\n", + " for j, letter in enumerate(['A', 'B', 'C', 'D', 'E']):\n", + " length_matrix[i, j] = length_counts.get(length, {}).get(letter, 0)\n", + "\n", + "# Normalize by row\n", + "row_sums = length_matrix.sum(axis=1, keepdims=True)\n", + "row_sums[row_sums == 0] = 1 # Avoid division by zero\n", + "length_matrix_norm = length_matrix / row_sums\n", + "\n", + "# Plot the question length vs answer choice matrix\n", + "plt.figure(figsize=(12, 8))\n", + "sns.heatmap(length_matrix_norm, annot=True, fmt='.2f', cmap='YlOrRd',\n", + " xticklabels=['A', 'B', 'C', 'D', 'E'],\n", + " yticklabels=unique_lengths)\n", + "plt.title('Answer Distribution by Question Length (Normalized)')\n", + "plt.xlabel('Answer Choice')\n", + "plt.ylabel('Question Length Group')\n", + "\n", + "# Add an explanation of the visualization\n", + "plt.figtext(0.5, 0.01, \n", + " 'This matrix shows how answer patterns vary by question length.\\n'\n", + " 'Differences between rows may indicate biases in how the model handles questions of different complexity.',\n", + " ha='center', fontsize=11, bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.8))\n", + "\n", + "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", + "plt.show()" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Loading ground truth data from ./data/train_data.jsonl\n" + }, + { + "output_type": "display_data", + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABEQAAAOzCAYAAACxtdMMAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd0VNXXxvFnJiQhoaSQQgkQem+C9N6LIEUF6bygKCgl2FCpFhSUKkUsIAICIk16700ERCnSQZAekhBKEpL7/oGZH0MSSGUyme9nrVk6Z869s+/NyZDsnH2OyTAMQwAAAAAAAA7EbOsAAAAAAAAAnjYSIgAAAAAAwOGQEAEAAAAAAA6HhAgAAAAAAHA4JEQAAAAAAIDDISECAAAAAAAcDgkRAAAAAADgcEiIAAAAAAAAh0NCBAAAAAAAOBwSIgCQgQQGBqp79+5p/j5nz56VyWTSzJkzLW3du3dX1qxZ0/y9Y5lMJg0fPvypvV+sp32d9uxpjceMIDw8XH5+fpozZ46tQ3lqHh0fmzdvlslk0ubNm59qHHXr1lXdunUtz48cOaJMmTLpr7/+eqpxAACePhIiANKtKVOmyGQyqUqVKrYOxSbq1q0rk8kkk8kks9ms7Nmzq1ixYurSpYvWrVuXau+zcuVKmyQWEiM9x/Y0REVFaeLEiXr22WeVLVs2Zc2aVc8++6wmTZqk+/fv2zo8SdLOnTs1fPhwhYSE2DoUSbJ8zzzp8bR/6X6SCRMmKFu2bOrQoYOlbfjw4TKZTPL399edO3fiHBMYGKjnnnvuaYbpEEqWLKkWLVpo6NChtg4FAJDGMtk6AABIyJw5cxQYGKi9e/fq5MmTKly4sK1DeuoCAgI0atQoSdLt27d18uRJLVq0SLNnz9ZLL72k2bNny9nZ2dL/77//ltmctFz3ypUrNXny5CQlHvLnz6+7d+9avXdaeFxsd+/eVaZMGfefsdu3b6tFixbasmWLnnvuOXXv3l1ms1mrV69Wv379tGTJEv36669yd3e3aZw7d+7UiBEj1L17d3l6elq9lpzxmFI//vij1fNZs2Zp3bp1cdpLlCjxNMN6rKioKE2YMEEDBw6Uk5NTnNevXr2qqVOnatCgQTaI7umpXbu27t69KxcXF1uHotdee03NmzfXqVOnVKhQIVuHAwBIIxn3J0kAdu3MmTPauXOnFi1apN69e2vOnDkaNmyYrcNKVTExMYqMjFTmzJkT7OPh4aHOnTtbtX322Wfq16+fpkyZosDAQH3++eeW11xdXdMsXkm6f/++YmJi5OLi8ti4nwZbv39aCwoK0pYtWzRp0iS98cYblvbXX39dkydP1htvvKG3335bkydPtmGUj5fW4zE+j36/7N69W+vWrYvTnp4sX75c165d00svvRTv6+XLl9eYMWPUp08fubm5pUkMifk8SmtmszndfF83bNhQXl5e+uGHHzRy5EhbhwMASCOUzABIl+bMmSMvLy+1aNFCL7zwQrx19bHrWHzxxReaPn26ChUqJFdXVz377LP67bffrPpevnxZPXr0UEBAgFxdXZUrVy49//zzOnv2rKQHv3zmyJFDhmFYjnnzzTdlMpk0ceJES9uVK1dkMpk0depUS1tERISGDRumwoULy9XVVXnz5tU777yjiIgIqxhMJpPeeOMNzZkzR6VKlZKrq6tWr16d5Hvj5OSkiRMnqmTJkvrqq68UGhpqee3RmvyoqCiNGDFCRYoUUebMmZUjRw7VrFnTUnLTvXt3yy/UD5cTPHp/x48fb7m/R44ciXcNkVinT59WkyZNlCVLFuXOnVsjR460uq8JrRPw6DkfF1ts26MzRw4cOKBmzZope/bsypo1qxo0aKDdu3db9Zk5c6ZMJpN27NihoKAg+fr6KkuWLGrTpo2uXbv25C9AIq7TMAwFBgbq+eefj3PcvXv35OHhod69eyd47gsXLui7775T/fr1rZIhsfr27at69epp+vTpunjxoqT413WJFd+9unjxov7v//5P/v7+cnV1ValSpfT999/HOXbSpEkqVaqU3N3d5eXlpUqVKmnu3LmSHpR0vP3225KkAgUKWL5Gsd9X8a0hcvr0ab344ovy9vaWu7u7qlatqhUrVlj1iR0jCxYs0CeffKKAgABlzpxZDRo00MmTJxO8b4nRrVs3+fj4KCoqKs5rjRs3VrFixSzPH/6eLVasmDJnzqyKFStq69atcY5N7P2Mz5IlSxQYGJjgTIShQ4fqypUrVp87Cbl9+7YGDRqkvHnzytXVVcWKFdMXX3xh9T346LU9/HkU+/2xfft29evXT76+vvL09FTv3r0VGRmpkJAQde3aVV5eXvLy8tI777wT59xffPGFqlevrhw5csjNzU0VK1bUwoULnxj7o58NsbHE93h4zQ9Jmj17tipWrCg3Nzd5e3urQ4cO+ueff+K8R+y/FW5ubqpcubK2bdsWbyzOzs6qW7euli5d+sS4AQD2ixkiANKlOXPmqG3btnJxcdHLL7+sqVOn6rffftOzzz4bp+/cuXN169Yt9e7dWyaTSaNHj1bbtm11+vRpS0lHu3btdPjwYb355psKDAzU1atXtW7dOp0/f16BgYGqVauWxo0bp8OHD6t06dKSpG3btslsNmvbtm3q16+fpU16MLVbevBX1VatWmn79u169dVXVaJECf35558aN26cjh8/riVLlljFunHjRi1YsEBvvPGGfHx8FBgYmKz74+TkpJdffllDhgzR9u3b1aJFi3j7DR8+XKNGjVKvXr1UuXJlhYWFad++fdq/f78aNWqk3r17699//423pCDWjBkzdO/ePb366qtydXWVt7e3YmJi4u0bHR2tpk2bqmrVqho9erRWr16tYcOG6f79+0n+K2tiYnvY4cOHVatWLWXPnl3vvPOOnJ2d9fXXX6tu3brasmVLnLVo3nzzTXl5eWnYsGE6e/asxo8frzfeeEPz589/4ns96TpNJpM6d+6s0aNHKzg4WN7e3pZjf/31V4WFhT12xsKqVasUHR2trl27Jtina9eu2rRpk1avXq2ePXs+MeaHXblyRVWrVrX8Uuzr66tVq1apZ8+eCgsL04ABAyRJ33zzjfr166cXXnhB/fv3171793To0CHt2bNHHTt2VNu2bXX8+HH99NNPGjdunHx8fCRJvr6+Cb5v9erVdefOHfXr1085cuTQDz/8oFatWmnhwoVq06aNVf/PPvtMZrNZb731lkJDQzV69Gh16tRJe/bsSdL1PqxLly6aNWuW1qxZY7X+xuXLl7Vx48Y4M9G2bNmi+fPnq1+/fnJ1ddWUKVPUtGlT7d271/JZkdj7mZCdO3fqmWeeSfD1WrVqqX79+ho9erRef/31BGeJGIahVq1aadOmTerZs6fKly+vNWvW6O2339bFixc1btw4q/7xfR4dPHhQ0oPvj5w5c2rEiBHavXu3pk+fLk9PT+3cuVP58uXTp59+qpUrV2rMmDEqXbq01VidMGGCWrVqpU6dOikyMlLz5s3Tiy++qOXLlyf4WRWf2rVrx/neP3funD788EP5+flZ2j755BMNGTJEL730knr16qVr165p0qRJql27tg4cOGAp5fru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cEOFR0RERERERPTcem56N5RiBmsQKVOmDO7fvw83NzeEh4fjiy++QFhYGCpVqmSokIiIiIiIiIjISBisQWTq1KkIDw+Hn5+foUIgIiIiIiIiIiNlsAaRN99801CHJiIiIiIiIirVTFSGjqD047AjIiIiIiIiIjI6bBAhIiIiIiIiIqNjsCEzRERERERERPRsTDlkptjYQ4SIiIiIiIiIjA4bRIiIiIiIiIjI6HDIDBEREREREVEpY6ISQ4dQ6rGHCBEREREREREZHTaIEBEREREREZHR4ZAZIiIiIiIiolLGhKvMFBt7iBARERERERGR0WGDCBEREREREREZHQ6ZISIiIiIiIiplOGSm+NhDhIiIiIiIiIiMDhtEiIiIiIiIiMjosEGEiIiIiIiIiIwO5xAhIiIiIiIiKmXYu6H4eA2JiIiIiIiIyOiwQYSIiIiIiIiIjA6HzBARERERERGVMiouu1ts7CFCREREREREREaHDSJEREREREREZHQ4ZIaIiIiIiIiolOGImeJjDxEiIiIiIiIiMjpsECEiIiIiIiIio8MhM0RERERERESlDFeZKT72ECEiIiIiIiIio8MGESIiIiIiIiIyOhwyQ0RERERERFTKsHdD8fEaEhEREREREZHRYYMIERERERERERkdDpkhIiIiIiIiKmVUKjF0CKUee4gQERERERERkdFhgwgRERERERERGR0OmSEiIiIiIiIqZVSGDuAFwB4iRERERERERGR02CBCREREREREREaHDSJEREREREREZHQ4hwgRERERERFRKaPiJCLFxh4iRERERERERGR02CBCREREREREREaHQ2aIiIiIiIiIShmOmCk+9hAhIiIiIiIiIqPDBhEiIiIiIiIiMjocMkNERERERERUyphwzEyxsYcIERERERERERkdNogQERERERERkdHhkBkiIiIiIiKiUoYjZoqPPUSIiIiIiIiIyOiwQYSIiIiIiIiIjA6HzBARERERERGVMiqOmSk29hAhIiIiIiIiIqPDBhEiIiIiIiIiMrjZs2ejfPnysLS0RMOGDXHgwIF8837zzTdo1qwZypYti7Jly6JFixYF5teFDSJEREREREREpYzqOX8V1Y8//ohhw4YhNjYWhw8fRu3atdGqVSvcuHFDZ/7ExER0794dCQkJSEpKgre3N1q2bIm///5b72OqRESeIdbn2vWHaw0dAhER5eNmBtvi6cUx45StoUMgKjHTG5kaOgSiEmVt1szQIfyrTqetN3QIBapWpm2R8jds2BANGjTArFmzAAC5ubnw9vbG4MGDMXLkyEK3z8nJQdmyZTFr1iz06tVLr2Pyt1IiIiIiIiIiKlGPHj3C3bt3tV6PHj3SmTczMxOHDh1CixYtlDQTExO0aNECSUlJeh3vwYMHyMrKgqOjo94xvpCrzLha+Rs6BKISZeUTa+gQiErMw8tjDR0CUYn5uqmhIyAiImP1vC8yM3HiRIwdq/17X2xsLOLi4vLkTUlJQU5ODlxdXbXSXV1dcebMGb2OFxMTAw8PD61GlcK8kA0iRERERERERGQ4H374IYYNG6aVplar/5VjTZo0CT/88AMSExNhaWmp93ZsECEiIiIiIiKiEqVWq/VuAHF2doapqSmuX7+ulX79+nW4ubkVuO1nn32GSZMm4ddff0WtWrWKFCPnECEiIiIiIiIig7GwsEC9evWwbds2JS03Nxfbtm1DcHBwvttNmTIFn3zyCTZv3oz69esX+bjsIUJERERERERUypg875OIFNGwYcPQu3dv1K9fH0FBQZg+fTru37+Pvn37AgB69eoFT09PTJw4EQAwefJkjBkzBkuXLkX58uVx7do1AICtrS1sbfVbBY4NIkRERERERERkUF27dsXNmzcxZswYXLt2DXXq1MHmzZuViVYvX74ME5P/DXKZO3cuMjMz0aVLF6395Ddxqy4qEZESO4PnxjlDB0BUorjKDL1IuMoMERER/Tf8DB3Av+rcnfWGDqFAfg5tDR1CodhDhIiIiIiIiKiUecFGzBgEJ1UlIiIiIiIiIqPDBhEiIiIiIiIiMjocMkNERERERERUyqhUL+B0oP8x9hAhIiIiIiIiIqPDBhEiIiIiIiIiMjocMkNERERERERUynCVmeJjDxEiIiIiIiIiMjpsECEiIiIiIiIio8MhM0RERERERESljIpjZoqNPUSIiIiIiIiIyOiwQYSIiIiIiIiIjA6HzBARERERERGVMuzdUHy8hkRERERERERkdNggQkRERERERERGh0NmiIiIiIiIiEoZrjJTfOwhQkRERERERERGhw0iRERERERERGR02CBCREREREREREaHc4gQERERERERlTKcQqT42EOEiIiIiIiIiIwOG0SIiIiIiIiIyOhwyAwRERERERFRKcNld4uPPUSIiIiIiIiIyOiwQYSIiIiIiIiIjA6HzBARERERERGVMhwxU3zsIUJERERERERERocNIkRERERERERkdDhkhoiIiIiIiKiUMeGYmWJjDxEiIiIiIiIiMjpsECEiIiIiIiIio8MhM0RERERERESlDEfMFB97iBARERERERGR0WGDCBEREREREREZHQ6ZISIiIiIiIiplVCoxdAilHnuIEBEREREREZHRYYMIERERERERERkdDpkhIiIiIiIiKmW4ykzxsYcIERERERERERkdNogQERERERERkdFhgwgRERERERERGR3OIUJERERERERUyqg4iUixsYcIERERERERERkdNogQERERERERkdHhkBkiIiIiIiKiUoYjZoqPPUSIiIiIiIiIyOiwQYSIiIiIiIiIjA6HzBARERERERGVMuzdUHy8hkRERERERERkdNggQkRERERERERGh0NmiIiIiIiIiEoZFZeZKTb2ECEiIiIiIiIio8MGESIiIiIiIiIyOhwyQ0RERERERFTqcMxMcbGHCBEREREREREZHTaIEBEREREREZHR4ZAZIiIiIiIiolJGxSEzxcYeIkRERERERERkdNggQkRERERERERGh0NmiIiIiIiIiEoZlYr9G4qLV5CIiIiIiIiIjM5z1SBy5coVXLlyxdBhEBEREREREdELzuANIrm5uRg3bhwcHBzg6+sLX19flClTBp988glyc3MNHR4RERERERERvYAMPofI6NGjMX/+fEyaNAlNmjQBAOzevRtxcXHIyMjAp59+auAIiYiIiIiIiJ43XHa3uAzeILJo0SJ8++23aN++vZJWq1YteHp64p133mGDCBERERERERGVOIMPmbl9+zb8/f3zpPv7++P27dsGiIiIiIiIiIiIXnQGbxCpXbs2Zs2alSd91qxZqF27tgEiIiIiIiIiInq+qZ7zf6WBwYfMTJkyBVFRUfj1118RHBwMAEhKSsJff/2FjRs3Gjg6IiIiIiIiInoRGbyHSGhoKM6dO4eOHTsiLS0NaWlp6NSpE86ePYtmzZoZOjyj8v33GxAR0R8BAZ3w8svv4/jxcwXm37RpN1q3fgsBAZ3Qrt0g7NhxUOtzEcGXX36Hpk17oVatzujT5yNcvPjPv3kKRACAJkH++GnBcPyZPAcPLy9Du5b1C92mWaNq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QRtEMjMzsW/fPiQkJCAxMRH79++Ht7c3QkJCMGvWLISGhhoyPCIiIiIiIiJ6QRmsQSQiIgL79+9HhQoVEBoaijfffBNLly6Fu7u7oUIiIiIiIiIiIiNhsAaRXbt2wd3dHREREQgLC0NoaCicnJwMFQ4RERERERERGRETQx04LS0NX3/9NaytrTF58mR4eHggICAAgwYNwk8//YSbN28aKjQiIiIiIiKi55zJc/56/qlERAwdBADcu3cPu3fvVuYTOXbsGKpUqYKTJ08+w97OlXh8RIZk5RNr6BCISszDy2MNHQIREREZBT9DB/CvSs9KNHQIBbI1DzN0CIV6bpptbGxs4OjoCEdHR5QtWxZmZmY4ffq0ocMiIiIiIiIioheQweYQyc3NxcGDB5GYmIiEhATs2bMH9+/fh6enJ8LDwzF79myEh4cbKjwiIiIiIiKi55ZKxWV3i8tgDSJlypTB/fv34ebmhvDwcHzxxRcICwtDpUqVDBUSERERERERERkJgzWITJ06FeHh4fDze7HHdRERERERERHR88dgDSJvvvmmoQ5NREREREREVMpxyExxPTeTqhIRERERERER/VfYIEJERERERERERsdgQ2aIiIiIiIiI6NmoOGSm2NhDhIiIiIiIiIiMDhtEiIiIiIiIiMjocMgMERERERERUanD/g3FxStIREREREREREaHDSJEREREREREZHQ4ZIaIiIiIiIiolOEqM8XHHiJEREREREREZHTYIEJERERERERERodDZoiIiIiIiIhKGZWKQ2aKiz1EiIiIiIiIiMjosEGEiIiIiIiIiIwOG0SIiIiIiIiIyOhwDhEiIiIiIiKiUodziBQXe4gQERERERERkdFhgwgRERERERERGR0OmSEiIiIiIiIqZVTs31BsvIJEREREREREZHTYIEJERERERERERodDZoiIiIiIiIhKHa4yU1zsIUJERERERERERocNIkRERERERERkdDhkhoiIiIiIiKiUUak4ZKa42EOEiIiIiIiIiIwOG0SIiIiIiIiIyOhwyAwRERERERFRqcMhM8XFHiJEREREREREZHTYIEJERERERERERodDZoiIiIiIiIhKGRX7NxQbryARERERERERGR02iBARERERERGR0eGQGSIiIiIiIqJSh6vMFBd7iBARERERERGR0WGDCBEREREREREZHTaIEBEREREREZHR4RwiRERERERERKWMinOIFBt7iBARERERERGR0WGDCBEREREREREZHQ6ZISIiIiIiIiplVCoOmSku9hAhIiIiIiIiIqPDBhEiIiIiIiIiMjocMkNERERERERU6rB/Q3HxChIRERERERGR0WGDCBEREREREREZHQ6ZISIiIiIiIiplVOAqM8XFHiJEREREREREZHTYIEJERERERERERodDZoiIiIiIiIhKHQ6ZKS72ECEiIiIiIiIio8MGESIiIiIiIiIyOhwyQ0RERERERFTKqFQcMlNc7CFCREREREREREaHDSJEREREREREZHQ4ZIaIiIiIiIio1GH/huLiFSQiIiIiIiIio8MGESIiIiIiIiIyOmwQISIiIiIiIiKDmz17NsqXLw9LS0s0bNgQBw4cKDD/ihUr4O/vD0tLSwQEBGDjxo1FOh4bRIiIiIiIiIhKGdVz/q+ofvzxRwwbNgyxsbE4fPgwateujVatWuHGjRs68+/duxfdu3dH//79ceTIEURHRyM6OhonT57U/xqKiBQ50ufeOUMHQFSirHxiDR0CUYl5eHmsoUMgIiIio+Bn6AD+Zc/7996iXf+GDRuiQYMGmDVrFgAgNzcX3t7eGDx4MEaOHJknf9euXXH//n2sX79eSWvUqBHq1KmDr776Sq9jsocIEREREREREZWoR48e4e7du1qvR48e6cybmZmJQ4cOoUWLFkqaiYkJWrRogaSkJJ3bJCUlaeUHgFatWuWbX5cXdNndF70l0PAePXqEiRMn4sMPP4RarTZ0OC+8h5eXGTqEFx7LNL1IWJ7pRcLyTC8almkqOc/3996JE+Mwdqx2z+DY2FjExcXlyZuSkoKcnBy4urpqpbu6uuLMmTM693/t2jWd+a9du6Z3jOwhQs/k0aNHGDt2bL4tfESlDcs0vUhYnulFwvJMLxqWaTIWH374Ie7cuaP1+vDDDw0dlpYXtIcIERERERERERmKWq3WuxeUs7MzTE1Ncf36da3069evw83NTec2bm5uRcqvC3uIEBEREREREZHBWFhYoF69eti2bZuSlpubi23btiE4OFjnNsHBwVr5AWDr1q355teFPUSIiIiIiIiIyKCGDRuG3r17o379+ggKCsL06dNx//599O3bFwDQq1cveHp6YuLEiQCAIUOGIDQ0FNOmTUNUVBR++OEHHDx4EF9//bXex2SDCD0TtVqN2NhYTgRFLwyWaXqRsDzTi4TlmV40LNNEunXt2hU3b97EmDFjcO3aNdSpUwebN29WJk69fPkyTEz+N8ilcePGWLp0KT766COMGjUKVapUwerVq1GzZk29j6kSESnxMyEiIiIiIiIieo5xDhEiIiIiIiIiMjpsECEiIiIiIiIio8MGESIiIiIiIiIyOmwQISIiIiIiIiKjwwYReiZJSUkwNTVFVFSUoUMhemZ9+vSBSqVSXk5OTmjdujWOHz9u6NCIntm1a9cwePBgVKxYEWq1Gt7e3mjXrh22bdtm6NCI9PZk/Wxubg5XV1dERkZiwYIFyM3NNXR4RM/k6d87NK/WrVsbOjQio8UGEXom8+fPx+DBg7Fz5078888/hg6H6Jm1bt0aV69exdWrV7Ft2zaYmZmhbdu2hg6L6JlcvHgR9erVw/bt2zF16lScOHECmzdvRnh4OAYOHGjo8IiKRFM/X7x4EZs2bUJ4eDiGDBmCtm3bIjs729DhET2TJ3/v0LyWLVtm6LCIjJaZoQOg0ic9PR0//vgjDh48iGvXriE+Ph6jRo0ydFhEz0StVsPNzQ0A4ObmhpEjR6JZs2a4efMmXFxcDBwdUdG88847UKlUOHDgAGxsbJT0GjVqoF+/fgaMjKjonqyfPT09ERgYiEaNGqF58+aIj4/H66+/buAIiYruyXJNRIbHHiJUZMuXL4e/vz+qVq2KHj16YMGCBRARQ4dFVGzp6en47rvvULlyZTg5ORk6HKIiuX37NjZv3oyBAwdqNYZolClT5r8PiqiERUREoHbt2li5cqWhQyEiohcAG0SoyObPn48ePXoAeNzt786dO9ixY4eBoyJ6NuvXr4etrS1sbW1hZ2eHtWvX4scff4SJCatHKl3Onz8PEYG/v7+hQyH6V/n7++PixYuGDoPomTz5e4fmNWHCBEOHRWS0OGSGiuTs2bM4cOAAVq1aBQAwMzND165dMX/+fISFhRk2OKJnEB4ejrlz5wIAUlNTMWfOHLRp0wYHDhyAr6+vgaMj0h976pGxEBGoVCpDh0H0TJ78vUPD0dHRQNEQERtEqEjmz5+P7OxseHh4KGkiArVajVmzZsHBwcGA0REVnY2NDSpXrqy8//bbb+Hg4IBvvvkG48ePN2BkREVTpUoVqFQqnDlzxtChEP2rTp8+jQoVKhg6DKJn8vTvHURkWOwTTnrLzs7G4sWLMW3aNBw9elR5HTt2DB4eHpwhm14IKpUKJiYmePjwoaFDISoSR0dHtGrVCrNnz8b9+/fzfJ6WlvbfB0VUwrZv344TJ06gc+fOhg6FiIheAOwhQnpbv349UlNT0b9//zw9QTp37oz58+fjrbfeMlB0RM/m0aNHuHbtGoDHQ2ZmzZqF9PR0tGvXzsCRERXd7Nmz0aRJEwQFBWHcuHGoVasWsrOzsXXrVsydOxenT582dIhEetPUzzk5Obh+/To2b96MiRMnom3btujVq5ehwyN6Jk/+3qFhZmYGZ2dnA0VEZNzYIEJ6mz9/Plq0aKFzWEznzp0xZcoUHD9+HLVq1TJAdETPZvPmzXB3dwcA2NnZwd/fHytWrOCcOFQqVaxYEYcPH8ann36K999/H1evXoWLiwvq1auXZ8w60fNOUz+bmZmhbNmyqF27NmbMmIHevXtz4msqtZ78vUOjatWqHO5IZCAq4SxsRERERERERGRk2LxOREREREREREaHDSJEREREREREZHTYIEJERERERERERocNIkRERERERERkdNggQkRERERERERGhw0iRERERERERGR02CBCREREREREREaHDSJEREREREREZHTYIEJERERa+vTpg+jo6GLtIz4+HmXKlCmReIiIiIj+DWwQISIi0iEpKQmmpqaIiooydCglSkTw9ddfo2HDhrC1tUWZMmVQv359TJ8+HQ8ePCix43Tt2hXnzp0rsf0RERERlTQ2iBAREekwf/58DB48GDt37sQ///xj6HCKJDMzM9/Pevbsiffeew8dOnRAQkICjh49io8//hhr1qzBli1bSiwGKysrlCtXrsT2R0RERFTS2CBCRET0lPT0dPz44494++23ERUVhfj4eK3PExMToVKpsG3bNtSvXx/W1tZo3Lgxzp49q+Q5duwYwsPDYWdnB3t7e9SrVw8HDx6EiMDFxQU//fSTkrdOnTpwd3dX3u/evRtqtVrpsZGWlobXX38dLi4usLe3R0REBI4dO6bkj4uLQ506dfDtt9+iQoUKsLS01Hley5cvx/fff49ly5Zh1KhRaNCgAcqXL48OHTpg+/btCA8P18r/2Wefwd3dHU5OThg4cCCysrKUz1JTU9GrVy+ULVsW1tbWaNOmDX7//Xflc11DZtatW4cGDRrA0tISzs7O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" + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": "
", + "image/png": "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WNLabp3zQ5K++eYbhYSEKHfu3PL19VW7du10/PjxVOdI+f/Ore91WlxcXFSvXj0tW7bsrnEDALKnnPsnOADZ0rx589SqVSu5urrq5Zdf1pQpU7RlyxbbLxK3+vbbb3Xp0iV1795dFotFo0ePVqtWrXT48GHbX6dbt26t3bt3q0+fPgoKCtKZM2f022+/6dixYwoKClLt2rX16aefavfu3bYJDdetWycnJyetW7dOffv2tZVJsnXDtlqtatGihdavX69u3bqpXLly+uuvv/Tpp59q//79Wrp0qV2sq1at0oIFC9S7d2/5+/srKCjonu6Ps7OzXn75ZQ0aNEjr169X8+bN06w3dOhQjRw5Ul27dlW1atUUHx+vrVu3avv27Xr22WfVvXt3nTp1Sr/99pvmzp2bZhszZ87U9evX1a1bN7m5ucnX11dWqzXNusnJyWrSpImeeuopjR49WitWrNCQIUN048YNDR8+3KFrzEhst9q9e7dq166tfPny6Z133pGLi4u+/PJL1atXT2vWrEk1F02fPn3k4+OjIUOG6MiRI5owYYJ69+6t77///q7nutt1WiwWdejQQaNHj1ZcXJx8fX1tx/7vf/9TfHy8OnTokG77y5cvV3JyssLCwtKtExYWptWrV2vFihXq0qXLXWO+VUxMjJ566inbL7IBAQFavny5unTpovj4eL355puSpGnTpqlv37568cUX1a9fP12/fl27du3Spk2b9Morr6hVq1bav3+/vvvuO3366afy9/eXJAUEBKR73po1a+rq1avq27ev/Pz8NHv2bLVo0UKLFi3SCy+8YFd/1KhRcnJy0ltvvaWLFy9q9OjRat++vTZt2uTQ9WbmHjgSy5QpU9S7d2/Vrl1b/fv315EjR9SyZUv5+PioSJEikqRy5cpp+PDhGjx4sLp166batWtLkmrWrGlr5/z582rSpIlatWqlNm3aaNGiRXr33XdVqVIlNW3a9I7X9fvvv+uJJ55Id3/t2rX1zDPPaPTo0erRo4dy586dZj3DMNSiRQutXr1aXbp0UXBwsH755Re9/fbbOnnypD799FO7+ml9tqUMCerTp48KFCigYcOG6Y8//tBXX30lb29v/f7773rsscf03//+Vz///LPGjBmjihUr2j33n332mVq0aKH27dsrMTFR8+fP10svvaSffvop3c+9tNSpUyfV58jR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SZNksVhUsmRJ/fTTT6nmbHA0thEjRui3337T008/rZ49eypXrlz68ssvlZCQoNGjR9/T9WTmOlM0b95cfn5+WrhwoZo2bZrhOT/Gjx+vffv2qWfPnlqxYoWaNGkiSfrll1+0bNkyPfPMMxozZozdMV27dtWoUaPUtWtXhYaGau3atbaeK7caNWqUVq9ererVq+v1119X+fLlFRcXp+3bt2vlypWKi4uTJDVq1EgFChRQrVq1FBgYqL1792ry5Mlq3ry5rddAynv0wQcfqF27dnJxcdFzzz1n+6X/Vu+9956+++47NW3aVH379pWvr69mz56tqKgoLV68WE5OWfv3lxkzZmjFihWpyvv165fhe5BRrq6uGjp0qPr06aNnnnlGbdq00ZEjRzRr1iyVLFnSrldIyZIl5e3tralTpypv3rzy9PRU9erVs2SOoeeff15z587V/v37M9QbYciQIXY9WlI899xzql+/vj744AMdOXJEVapU0a+//qply5bpzTffVMmSJTMd6900b95c48ePV5MmTfTKK6/ozJkz+vzzz1WqVCmH50JKSSKmfFbfKjAwUM8++6xKliypESNGaODAgbYlk/PmzauoqCj98MMP6tatm9566y25uLhoxIgR6t69u5555hm1bdtWUVFRmjlzZppziCQlJWnNmjXq2bNnpu4HAMBED3JJGwCPpueee85wd3c3rly5km6dTp06GS4uLkZsbKxtmdG0ltPVLcuMxsbGGr169TLKli1reHp6Gl5eXkb16tWNBQsWpDru888/NyQZPXr0sCtv2LChIcmIiIhIdUxiYqLxySefGBUqVDDc3NwMHx8fIyQkxBg2bJhx8eJFu5h69eqV0dthW34zZcuTJ49RunRpo0OHDsavv/6a5jG3Lz05YsQIo1q1aoa3t7eRO3duo2zZssbHH39sJCYm2urcuHHD6NOnjxEQEGBYLBbbMrd3ur/pLbvr6elpHDp0yGjUqJHh4eFhBAYGGkOGDLEtp5ri7NmzRuvWrQ0PDw/Dx8fH6N69u/H333+najO92FLu561LyRrGzaVoGzdubOTJk8fw8PAw6tevb/z+++92ddJbkvVuy3Tey3Wm6NmzpyHJ+Pbbb+/Y9u0SExONCRMmGCEhIYaHh4ftWejYsWOa57p69arRpUsXw8vLy8ibN6/Rpk0b25LSt9+rmJgYo1ev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vXp1TZgwQY0bN9Y///yj/Pnzp6rv6+urDz74QGXLlpWrq6t++uknde7cWfnz51fjxo1t9Zo0aaKZM2faXt9p9dq0mN5D5OrVq+rSpYs8PDxUoUIFHTt2TJLUp08fjRo1yuToAAAAAADArRISEhQfH2+3JSQkpFt//Pjxev3119W5c2eVL19eU6dOlYeHh2bMmJFm/Xr16umFF15QuXLlVLJkSfXr10+VK1fW+vXr7eq5ubmpQIECts3Hx8eh6zA9ITJw4ED9+eefioyMlLu7u628YcOG+v77702MDAAAAACA7MnJYt42cuRIeXl52W0jR45MM87ExERt27ZNDRs2/Dd2Jyc1bNhQGzduvOt1GoahiIgI/fPPP6pTp47dvsjISOXPn1+PP/64evTooXPnzjl0D00fm7J06VJ9//33euqpp2S5ZZrcChUq6NChQyZGBgAAAAAAbjdw4ECFh4fblaU3XCU2NlbJyckKDAy0Kw8MDNS+ffvSPcfFixdVuHBhJSQkyNnZWV988YWeffZZ2/4mTZqoVatWKl68uA4dOqT3339fTZs21caNG+Xs7Jyh6zA9IXL27Nk0xwxduXLFLkECAAAAAABuMvPXZTc3N4fn63BU3rx5tXPnTl2+fFkREREKDw9XiRIlVK9ePUlSu3btbHUrVaqkypUrq2TJkoqMjFSDBg0ydA7Th8yEhobq//7v/2yvU5IgX3/9tWrUqGFWWAAAAAAAIJP8/f3l7OysmJgYu/KYmBgVKFAg3eOcnJxUqlQpBQcHa8CAAXrxxRfTHZYjSSVKlJC/v78OHjyY4dhM7yHy3//+V02bNtWePXt048YNffbZZ9qzZ49+//13rVmzxuzwAAAAAADAPXJ1dVVISIgiIiLUsmVLSZLValVERIR69+6d4XasVusdJ249ceKEzp07p4IFC2a4TdN6iPz999+SpKefflo7d+7UjRs3VKlSJf3666/Knz+/Nm7cqJCQELPCAwAAAAAg27JYzNscFR4ermnTpmn27Nnau3evevTooStXrqhz586SpLCwMA0cONBWf+TIkfrtt990+PBh7d27V+PGjdPcuXPVoUMHSdLly5f19ttv648//tCRI0cUERGh559/XqVKlbJblvduTOshUrlyZT355JPq2rWr2rVrp2nTppkVCgAAAAAAuE/atm2rs2fPavDgwYqOjlZw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yOt3szzhqpR48xdRY/VcWV79r3R4h3Rwq1S2hlS+towpb5hwhXiU8J0DpnrIJlXNjhz+iUpKSlJoaGiq8pCQEN24cSONIzLuXucjQeY1q5RHcVeSNSnivM5evqFyBd00LayA/PPcfEROXbwhyy0db77bEq+kZKnf/DN27fSq760+z9z8wF+176re/+GsbV/4gjOp6gD3A88zcpJm9Usq7sI1TZy5VWfPX1W5kn76+pNmtiEzp89cltMtX4jOnLuqlt2W2F7PWLBLMxbsUrUqBTX30+ckSd/9uEeS9Gr/n+zONfKdumrV5PH7fUl41O38TYantyyNu0v5/KST+2VM63tzYlXp5jCXW74TWmq2liWXqyydRts1Y/zylYxfp9188XfkzeV7n+kkvTBAOnNMxux3pag/H9BF4VHFdw7g4WYxHMxC9OnTRy4uLho/frxd+VtvvaVr167p888/v+dg8ubNqz///DPTPUSMBU0zdTwA4P6x1GpodghAlrGOn292CECWsVT3NzsEIEtZ2iw3O4T7KvnFiqad23nR36adOyvdU5+r6dOn69dff9VTTz0lSdq0aZOOHTumsLAwhYeH2+rdnjS5mw4dOihfPsYxAwAAAACA+8vhhMjff/+tJ554QpJ06NDN5W39/f3l7++vv//+N0tksTg+nmnKlCkOHwMAAAAAAOAohxMiq1evzrKTx8bGasaMGdq4caOio6MlSQUKFFDNmjXVqVMnBQQEZNm5AAAAAADIMZhUNdNMW592y5YtKlOmjCZOnCgvLy/VqVNHderUkZeXlyZOnKiyZctq69atZoUHAAAAAAByMId7iNSvX/+Ow2FWrVqVoXb69Omjl156SVOnTk3VnmEYeuONN9SnTx9t3LjR0RABAAAAAMjZTOvekHM4nBAJDg62e52UlKSdO3fq77//VseOHTPczp9//qlZs2almVyxWCzq37+/qlat6mh4AAAAAAAAd+VwQuTTTz9Ns3zo0KG6fPlyhtspUKCANm/erLJly6a5f/PmzQoMDHQ0PAAAAAAAgLu6p2V309KhQwdVq1ZNY8eOzVD9t956S926ddO2bdvUoEEDW/IjJiZGERERmjZtWobbAgAAAADgkcKkqpmWZQmRjRs3yt3dPcP1e/XqJX9/f3366af64osvlJycLElydnZWSEiIZs2apTZt2mRVeAAAAAAA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iTKWxujYpgYGD2j83teNioBi1FJDKZIohqPziHtTlA6BqFClT9ygdAhEhSbhVsEerUn0ITKaUEPpEIgKjeTxkdIhEBUu875KR/BOid1+ii1bardNsWUXJnZ+JyIiIiIiIqIS54NNiFy6dAna2tpKh0FERERERET04ZG0lHsVEx/0mhTD3jxERERERERE9AFQ7FEfnTt3fuP0Fy9eqA3iSURERERERET/T4v3y29LsYTIzp070bJlS1hZWeU4PS0t7T1HREREREREREQlhWIJkSpVqqBLly4YOHBgjtMvXryIXbt2veeoiIiIiIiIiKgkUGwMEXd3d5w/fz7X6SqVCvb29u8xIiIiIiIiIqIiQpKUexUTirUQ+fXXX9/YLaZKlSq4e/fue4yIiIiIiIiIiEoKxRIiKpVKqUUTERERERERFW3F6PG3SlFkC758+fKd1iciIiIiIiIiehNFEiKVKlXCtGnT8Pjx41zrCCFw4MABtG3bFvPmzXuP0RERERERERFRcadIl5mwsDCMGzcO/v7+qFmzJurWrQtbW1vo6+sjJiYGf/75J8LDw6Gjo4OxY8fiiy++UCJMIiIiIiIiog9TMRrcVCmKJERcXFywefNm3L9/Hxs3bsTRo0dx4sQJJCQkwNLSErVr18ayZcvQtm1baGtrKxEiERERERERERVjig2qCgD29vb49ttv8e233yoZBhEREREREVHRosUWIm+Lw9ISERERERERUYnDhAgRERERERERlTiKdpkhIiIiIiIiogKQ2L7hbXELEhEREREREVGJwxYiREREREREREUNH7v71j6IhEhiYiIuX76MZ8+eIT09XW3aRx99pFBURERERERERFRcKZ4Q2bt3L/r06YPIyMhs0yRJQlpamgJREREREREREX3A2ELkrSk+hsiwYcPQrVs3PH78GOnp6WovJkOIiIiIiIiI6F1QPCHy9OlTjBgxAlZWVkqHQkREREREREQlhOIJka5duyIsLEzpMIiIiIiIiIiKDklLuVcxofgYIgsWLEC3bt1w9OhR1KhRA7q6umrTv/rqK4UiIyIiIiIiIqLiSvGEyLp167B//37o6+sjLCwM0msDw0iSxIQIERERERERUVZaHFT1bSmeEBk/fjwCAgIwZswYaGkVn6Y3RERERERERPThUjwDkZycjO7duzMZQkRERERERETvjeJZiL59++KPP/5QOgwiIiIiIiKiokOSlHsVE4p3mUlLS8P06dOxb98+uLm5ZRtUdfbs2QpFRkRERERERETFleIJkStXrqB27doAgKtXr6pNk4pR5omIiIiIiIio0BSjx98qRfGESGhoqNIhEBEREREREVEJw5QSEREREREREZU4ircQadq06Ru7xhw6dOg9RkNERERERERUBHCIibemeEKkVq1aau9TUlJw8eJFXL16FX379lUmKCIiIiIiIiIq1hRPiMyZMyfHcn9/f8THx7/naIiIiIiIiIiKAC22EHlbH+wYIr169UJgYKDSYRARERERERFRMaR4C5HchIeHQ19fX+kwiIiIiIiIiD48fOzuW1M8IdK5c2e190IIPH78GGfPnsWECRMUioqIiIiIiIiIijPFEyKmpqZq77W0tODi4oJJkyahVatWCkVFRERERERERMWZ4gmRlStXKh0CERERERERUdHCx+6+NXY6IiIiIiIiIqISR5EWIubm5rh58yYsLS1hZmYG6Q2Zrejo6PcYGREREREREVERwBYib02RhMicOXNQqlQpAMDcuXOVCIGIiIiIiIiISjBFEiJ9+/bN8W8iIiIiIiIiovdBkYRIXFycxnVNTEzeYSRERERERERERRC7zLw1RRIipUuXfuO4Ia9LS0t7x9EQERERERERUUmjSEIkNDRU/jsiIgJjxoxBv3794OnpCQAIDw/HqlWrMHXqVCXCIyIiIiIiIvqwafGhsW9LkYSIt7e3/PekSZMwe/Zs9OjRQy776KOPUKNGDSxdupRjjBARERERERFRoVM8pRQeHo66detmK69bty5Onz6tQEREREREREREVNwpnhCxs7PDsmXLspUvX74cdnZ2CkRERERERERE9IGTJOVexYQiXWZeN2fOHHTp0gV79uxB/fr1AQCnT5/G33//jc2bNyscHREREREREREVR4q3EGnXrh1u3ryJDh06IDo6GtHR0ejQoQNu3ryJdu3aKR0eERERERER0YeHLUTemuItRICMbjNTpkxROgwiIiIiIiIiKiEUbyECAEePHkWvXr3QsGFD/PPPPwCANWvW4NixYwpHRkRERERERETFkeIJkc2bN6N169YwMDDA+fPnkZSUBAB48eIFW40QERERERER5UTSUu5VTCi+JpMnT8avv/6KZcuWQVdXVy5v1KgRzp8/r2BkRERERERERFRcKT6GyI0bN+Dl5ZWt3NTUFLGxse8/ICIiIiIiIqIPnVbxGdxUKYq3ELG2tsatW7eylR87dgxOTk4KREREREREREREhWnhwoVwdHSEvr4+6tevj9OnT+da99q1a+jSpQscHR0hSRLmzp2brY6/vz8kSVJ7ubq65ismxRMigwYNwtdff41Tp05BkiQ8evQIa9euxciRI/Hll18qHR4RERERERHRh6cIPXb3jz/+wIgRIzBx4kScP38eNWvWROvWrfHs2bMc67969QpOTk6YNm0arK2tc51vtWrV8PjxY/mV3wezKN5lZsyYMUhPT0fz5s3x6tUreHl5QaVSYeTIkRg2bJjS4RERERERERHRW5g9ezYGDRqE/v37AwB+/fVXBAcHIzAwEGPGjMlW38PDAx4eHgCQ4/RMOjo6b0yY5EXxFiKSJGH8+PGIjo7G1atXcfLkSTx//hw//vij0qERERERERERURZJSUmIi4tTe2U+MTar5ORknDt3Di1atJDLtLS00KJFC4SHh79VHH///TdsbW3h5OSETz/9FPfv38/X5xVPiGTS09ND1apVUa9ePRgbGysdDhEREREREdGHS8HH7k6dOhWmpqZqr6lTp+YYZmRkJNLS0mBlZaVWbmVlhSdPnhR49evXr4+goCDs3bsXixcvxt27d9GkSRP8+++/Gs9DsS4zAwYM0KheYGDgO46EiIiIiIiIiDQ1duxYjBgxQq1MpVK91xjatm0r/+3m5ob69evDwcEBGzZswMCBAzWah2IJkaCgIDg4OKB27doQQigVBhEREREREVHRU4DBTQuLSqXSOAFiaWkJbW1tPH36VK386dOnbzX+R1alS5eGs7Nzjk+xzY1iCZEvv/wS69atw927d9G/f3/06tUL5ubmSoVDRERERERERIVMT08P7u7uCAkJgZ+fHwAgPT0dISEhGDp0aKEtJz4+Hrdv30bv3r01/oxiY4gsXLgQjx8/xqhRo7Bz507Y2dnh448/xr59+9hihIiIiIiIiKiYGDFiBJYtW4ZVq1bh+vXr+PLLL/Hy5Uv5qTN9+vTB2LFj5frJycm4ePEiLl68iOTkZPzzzz+4ePGiWuuPkSNH4vDhw4iIiMCJEyfQqVMnaGtro0ePHhrHpehjd1UqFXr06IEePXrg3r17CAoKwuDBg5Gamopr165xcFUiIiIiIiKinCjYZSa/unfvjufPn+OHH37AkydPUKtWLezdu1ceaPX+/fvQ0vqvvcajR49Qu3Zt+f3MmTMxc+ZMeHt7IywsDADw8OFD9OjRA1FRUShTpgwaN26MkydPokyZMhrHpWhC5HVaWlqQJAlCCKSlpSkdDhEREREREREVkqFDh+baRSYzyZHJ0dExz54j69evf+uYFH3sblJSEtatW4eWLVvC2dkZV65cwYIFC3D//n22DiEiIiIiIiLKjZaWcq9iQrEWIoMHD8b69ethZ2eHAQMGYN26dbC0tFQqHCIiIiIiIiIqQRRLiPz666+wt7eHk5MTDh8+jMOHD+dYb8uWLe85MiIiIiIiIiIq7hRLiPTp0wfSOxoERoQceifzJVLK5FUJSodAVGgmPiq8x6sREVEhSnqhdARElC9FZ1DVD5ViCZGgoCClFk1EREREREREJdwH85QZIiIiIiIiItJQEXrs7oeq+AwPS0RERERERESkISZEiIiIiIiIiKjEYZcZIiIiIiIioqJGYvuGt8UtSEREREREREQlDluIEBERERERERU5HFT1bbGFCBERERERERGVOGwhQkRERERERFTU8LG7b40tRIiIiIiIiIioxGFChIiIiIiIiIhKHHaZISIiIiIiIipq+Njdt8YtSEREREREREQlDluIEBERERERERU5HFT1bbGFCBERERERERGVOEyIEBEREREREVGJwy4zREREREREREWNxC4zb4stRIiIiIiIiIioxFG8hcjdu3dx9OhR3Lt3D69evUKZMmVQu3ZteHp6Ql9fX+nwiIiIiIiIiD5AbN/wthRLiKxduxa//PILzp49CysrK9ja2sLAwADR0dG4ffs29PX18emnn2L06NFwcHBQKkwiIiIiIiIiKoYUSYjUrl0benp66NevHzZv3gw7Ozu16UlJSQgPD8f69etRt25dLFq0CN26dVMiVCIiIiIiIiIqhhRJiEybNg2tW7fOdbpKpYKPjw98fHzw008/ISIi4v0FR0RERERERPSh46Cqb02RhMibkiFZWVhYwMLC4h1GQ0REREREREQljeKjsJw/fx5XrlyR32/fvh1+fn4YN24ckpOTFYyMiIiIiIiI6AMlScq9ignFEyJffPEFbt68CQC4c+cOPvnkExgaGmLjxo0YNWqUwtERERERERERUXGkeELk5s2bqFWrFgBg48aN8PLywu+//46goCBs3rxZ2eCIiIiIiIiIPkiSgq/iQfGEiBAC6enpAICDBw+iXbt2AAA7OztERkYqGRoRERERERERFVOKJ0Tq1q2LyZMnY82aNTh8+DB8fX0BAHfv3oWVlZXC0RERERERERFRcaTIU2ZeN3fuXHz66afYtm0bxo8fj0qVKgEANm3ahIYNGyocHREREREREdEHSFK8fUORp3hCxM3NTe0pM5lmzJgBbW1tBSIiIiIiIiIiouJO8YRIbvT19ZUOgYiIiIiIiOjDVIwef6sURRIiZmZmkDTcedHR0e84GiIiIiIiIiIqaRRJiMydO1f+OyoqCpMnT0br1q3h6ekJAAgPD8e+ffswYcIEJcIjIiIiIiIiomJOkYRI37595b+7dOmCSZMmYejQoXLZV199hQULFuDgwYMYPny4EiESERERERERfcDYZeZtKT4s7b59+9CmTZts5W3atMHBgwcViIiIiIiIiIiIijvFEyIWFhbYvn17tvLt27fDwsJCgYiIiIiIiIiIPnCSlnKvYkLxp8wEBATgs88+Q1hYGOrXrw8AOHXqFPbu3Ytly5YpHB0RERERERERFUeKJ0T69euHKlWqYN68ediyZQsAoEqVKjh27JicICEiIiIiIiIiKkyKJkRSUlLwxRdfYMKECVi7dq2SoRAREREREREVGZLEQVXflqKdf3R1dbF582YlQyAiIiIiIiKiEkjx0VD8/Pywbds2pcMgIiIiIiIiKkIkBV/Fg+JjiFSuXBmTJk3C8ePH4e7uDiMjI7XpX331lUKREREREREREVFxpXhCZMWKFShdujTOnTuHc+fOqU2TJIkJESIiIiIiIiIqdIonRO7evat0CERERERERERFi6T4CBhF3ge1BYUQEEIoHQYRERERERERFXMfREJk9erVqFGjBgwMDGBgYAA3NzesWbNG6bCIiIiIiIiIPlAcVPVtKd5lZvbs2ZgwYQKGDh2KRo0aAQCOHTuG//3vf4iMjMTw4cMVjpCIiIiIiIiIihvFEyLz58/H4sWL0adPH7nso48+QrVq1eDv78+ECBEREREREVFWUvFpqaEUxbvMPH78GA0bNsxW3rBhQzx+/FiBiIiIiIiIiIiouFM8IVKpUiVs2LAhW/kff/yBypUrKxARERERERERERV3ineZCQgIQPfu3XHkyBF5DJHjx48j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ws/PDwkJCZg0aZJSYRERERERERFRCaBYQuTu3bto2LCh/L5hw4Y4dOgQWrRogZSUFHzzzTdKhUZERERERERExZxiCRFLS0s8ePBA7dG61atXx6FDh9CsWTM8evRIqdCIiIiIiIiIPnCKDglaLCi2BRs3bowtW7ZkK69atSpCQkKwZ88eBaIiIiIiIiIiopJAsRYiY8aMwblz53KcVq1aNRw6dAibN29+z1ERERERERERFQHFaHBTpSiWEHFzc4Obm1uu06tXr47q1au/x4iIiIiIiIiIqKRQpMvM/fv381X/n3/+eUeREBEREREREVFJpEhCxMPDA1988QXOnDmTa50XL15g2bJlqF69OrvOEBEREREREb1O0lLuVUwo0mXmzz//xE8//YSWLVtCX18f7u7usLW1hb6+PmJiYvDnn3/i2rVrqFOnDqZPn4527dopESYRERERERERFVOKpHYsLCwwe/ZsPH78GAsWLEDlypURGRmJv//+GwDw6aef4ty5cwgPD2cyhIiIiIiIiCgbScFX8aDYoKoAYGBggK5du6Jr165KhkFEREREREREJUzx6fxDRERERERERKQhRVuIEBEREREREVEBSMWn64pS2EKEiIiIiIiIiEocthAhIiIiIiIiKnLYvuFtcQsSERERERERUYmjeAuRHTt25FguSRL09fVRqVIlVKhQ4T1HRURERERERPQB4xgib03xhIifnx8kSYIQQq08s0ySJDRu3Bjbtm2DmZmZQlESERERERERUXGieJeZAwcOwMPDAwcOHMCLFy/w4sULHDhwAPXr18euXbtw5MgRREVFYeTIkUqHSkRERERERETFhOItRL7++mssXboUDRs2lMuaN28OfX19fP7557h27Rrmzp2LAQMGKBglERERERER0YdE8fYNRZ7iW/D27dswMTHJVm5iYoI7d+4AACpXrozIyMj3HRoRERERERERFVOKJ0Tc3d3x3Xff4fnz53LZ8+fPMWrUKHh4eAAA/v77b9jZ2SkVIhEREREREdGHRZKUexUTineZWbFiBTp27Ijy5cvLSY8HDx7AyckJ27dvBwDEx8fj+++/VzJMIiIiIiIiIipGFE+IuLi44M8//8T+/ftx8+ZNuaxly5bQ0spowOLn56dghERERERERERU3CieEAEALS0ttGnTBm3atFE6FCIiIiIiIqIPXzHquqKUDyIhEhISgpCQEDx79gzp6elq0wIDAxWKioiIiIiIiIiKK8UTIgEBAZg0aRLq1q0LGxsbSMxyEREREREREeVB8WekFHmKJ0R+/fVXBAUFoXfv3kqHQkREREREREQlhOIppeTkZDRs2FDpMIiIiIiIiIioBFE8IfLZZ5/h999/VzoMIiIiIiIioqJDkpR7FROKd5lJTEzE0qVLcfDgQbi5uUFXV1dt+uzZsxWKjIiIiIiIiIiKK8UTIpcvX0atWrUAAFevXlWbxgFWiYiIiIiIiHLC++W3pXhCJDQ0VOkQiIiIiIiIiKiEUTwh8rqHDx8CAMqXL69wJEREREREREQfMEnxIUGLPMW3YHp6OiZNmgRTU1M4ODjAwcEBpUuXxo8//oj09HSlwyMiIiIiIiKiYkjxFiLjx4/HihUrMG3aNDRq1AgAcOzYMfj7+yMxMRE//fSTwhESERERERERUXGjeEJk1apVWL58OT766CO5zM3NDeXKlcPgwYOZECEiIiIiIiLKhoOqvi3Fu8xER0fD1dU1W7mrqyuio6MViIiIiIiIiIiIijvFEyI1a9bEggULspUvWLAANWvWVCAiIiIiIiIiog+cpKXcq5hQvMvM9OnT4evri4MHD8LT0xMAEB4ejgcPHmD37t0KR0dERERERERExZHiqR1vb2/cvHkTnTp1QmxsLGJjY9G5c2fcuHEDTZo0UTo8IiIiIiIiIiqGFG8hAgC2trbZBk99+PAhPv/8cyxdulShqIiIiIiIiIg+VBxU9W0p3kIkN1FRUVixYoXSYRARERERERFRMfRBtBAhIiIiIiIionyQ2ELkbX2wLUSIiIiIiIiIiN4VJkSIiIiIiIiIqMRRrMtM586d3zg9Njb2/QRCREREREREVNRIbN/wthRLiJiamuY5vU+fPu8pGiIiIiIiIiIqSRRLiKxcuVKpRRMREREREREVcRxU9W2xjQ0RERERERERvVMLFy6Eo6Mj9PX1Ub9+fZw+ffqN9Tdu3AhXV1fo6+ujRo0a2L17t9r0fv36QZIktVebNm3yFRMTIkRERERERET0zvzxxx8YMWIEJk6ciPPnz6NmzZpo3bo1nj17lmP9EydOoEePHhg4cCAuXLgAPz8/+Pn54erVq2r12rRpg8ePH8uvdevW5SsuJkSIiIiIiIiIihpJUu6VT7Nnz8agQYPQv39/VK1aFb/++isMDQ0RGBiYY/1ffvkFbdq0wXfffYcqVargxx9/RJ06dbBgwQK1eiqVCtbW1vLLzMwsX3ExIUJEREREREREGktKSkJcXJzaKykpKce6ycnJOHfuHFq0aCGXaWlpoUWLFggPD8/xM+Hh4Wr1AaB169bZ6oeFhaFs2bJwcXHBl19+iaioqHytBxMiREREREREREWOlmKvqVOnwtTUVO01derUHKOMjIxEWloarKys1MqtrKzw5MmTHD/z5MmTPOu3adMGq1evRkhICH7++WccPnwYbdu2RVpaWp5bLpNiT5khIiIiIiIioqJn7NixGDFihFqZSqV6rzF88skn8t81atSAm5sbKlasiLCwMDRv3lyjeTAhQkRERERERFTUFGAsj8KiUqk0ToBYWlpCW1sbT58+VSt/+vQprK2tc/yMtbV1vuoDgJOTEywtLXHr1i2NEyLsMkNERERERERE74Senh7c3d0REhIil6WnpyMkJASenp45fsbT01OtPgAcOHAg1/oA8PDhQ0RFRcHGxkbj2JgQISIiIiIiIqJ3ZsSIEVi2bBlWrVqF69ev48svv8TLly/Rv39/AECfPn0wduxYuf7XX3+NvXv3YtasWfjrr7/g7++Ps2fPYujQoQCA+Ph4fPfddzh58iQiIiIQEhKCjh07olKlSmjdurXGcbHLDBEREREREVGRU3TaN3Tv3h3Pnz/HDz/8gCdPnqBWrVrYu3evPHDq/fv3oaX13/o0bNgQv//+O77//nuMGzcOlStXxrZt21C9enUAgLa2Ni5fvoxVq1YhNjYWtra2aNWqFX788cd8jWUiCSFE4a6q8tIDW+RdiagI+XHgA6VDICo0Ex8NUToEIiIiKglsvlI6gndLnFVu2VJd5ZZdiNhChIiIiIiIiKioUXBQ1eKi6LSxISIiIiIiIiIqJEyIEBEREREREVGJUyzHEKF3LykpCVOnTsXYsWPzNWgN0YeKxzQVJzyeqTjh8UzFDY9pog8HEyJUIHFxcTA1NcWLFy9gYmKidDhEb43HNBUnPJ6pOOHxTMUNj2miDwe7zBARERERERFRicOECBERERERERGVOEyIEBEREREREVGJw4QIFYhKpcLEiRM5EBQVGzymqTjh8UzFCY9nKm54TBN9ODioKhERERERERGVOGwhQkREREREREQlDhMiRERERERERFTi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ABB79uyR6504cULo6ekJPz8/sXPnTrF+/XpRqVIl0aBBA3kdtmzZIgCIKVOmiPDwcBEeHi4SExOFv7+/sLe3l+c1a9Ysoa+vL6ysrOSyJUuWCGNjY5GamqrR8jJ99NFHwtzcXPzyyy9i//794ptvvhGSJIndu3fLdfr27SssLCxEjRo1xNq1a8WuXbtEjRo1hJ2dnUhOTs5129y9e1cAENbW1mLgwIFi3759Ys+ePSI1NVXUq1dPWFpaiuXLl4udO3eKli1bCj09PXH27FkhhBARERECgDh06JAQQojIyEghSZLQ19eXY3v58qXQ1dUV69evf+v9V6VKFfHLL7+IvXv3Ch8fH1GqVCkxdOhQ0adPH7F3714REBAgAIi1a9fKnz148KD44YcfxI4dO0RoaKiYM2eOMDExEf7+/nKdxYsXCwMDAxEbGyuXpaamChsbGzFmzJhc4woNDRUAxJkzZ+QyAMLOzk707NlT7NmzR/zyyy9CT09P7fv2+PFjYWxsLDw9PcX27dtFUFCQcHBwEJaWlqJv375q+7RatWry+8jISGFnZyfs7OzEsmXLREhIiPj111/Fd999J9cJCAgQS5YsEQcPHhS7d+8WvXv3FiqVSty4cUMIIcSDBw/EwIEDhYGBgXz8Zp4rbt++LUqXLi0aN24stmzZIrZt2yY8PDyEg4ODSExMzHU7ZB5Dtra2ol+/fmLv3r1iypQpQkdHJ8fzzPPnz+Wy/v37i99//12EhYWJjRs3igYNGojKlSuLlJQUIYQQL168EKampsLX11cEBweLgwcPinnz5om5c+fK83jX543MeDPX097eXgwbNkzs379f+Pv7CwBi8eLFuc4nc19q8v3s3LmzMDQ0FHPmzBF79uwRPXr0EJIkie3btwshMs5B+vr6IjAwUAghRFpamihdurTQ19cXixYtkudja2srpk2blmMsL168EAsXLhQAxMqVK+XjQIj/jmknJycxceJEceDAATFkyJB8nytzkzn/ChUqiICAALF//37RrVs3oaWlJUaNGiXatWsngoODxfz584Wurq746aef5M/ev39flC5dWri7u4stW7aI33//XVSoUEE4OjqKuLg4uV6XLl2EsbGxWLBggQgODha+vr6iXLly4vVLzKSkJFG3bl1hZ2cnVqxYIfbu3St69eoldHR0xOXLl+V63t7ewtfX943rpOm+/eabb0RQUJAIDQ0V27dvF23bthUWFhYiKioq3/PSZB1zOj8NGzZMGBgYiMmTJ4sDBw6IgIAAoaurq3b8Nm3aVLi4uIj169eL0NBQsWrVKjFs2LA3boOdO3cKAPL3/6effhKOjo7yMZYJgJgxY4baZ+fMmZPvfZPXeeHatWuidu3aolGjRvLx/eDBAyFE9n169uxZoaenJ1+/LF++XFhaWop69erJv9WafvcLsu22b98uJEkSPXr0EHv27BFz5swRBgYGokuXLvK65nZ9l5OCHBvh4eHCy8tL1K5dW95eOV1rPnv2LF/fHSsrK1GtWjWxfv16sW/fPvHkyRONzteanIdu3bol2rVrJ5ycnNRizs369euFJEnCz89PbN26VQQHB4tx48aJnTt3CiGEiIuLE46OjsLJyUn8/vvvYsuWLcLd3V2ULl1a3L9/X56Pg4ODKF++vGjTpo3auWrQoEGievXqIjAwUAQHB4vq1asLFxcXkZaWJn8283eyffv2Ijg4WCxYsEAYGxvnuO75/d4GBgYKLS0tERYWJoQQYtGiRUJbW1scP35cbZ9kHvtvuhaoX7+++OSTT9S2365duwQAcf369Vy38esCAgJE//79NapL2TEhQkVaYSVEGjRoIFxdXeX3rydEhBBi69atAoC4e/eu2udMTU3FxIkT5fdxcXHC2NhYjB07Vq1e5g1gZGSkEOK/C/+sF9A//PCDMDU1VbtpSExMFPb29mo3YZk/EK/fNE2cOFHo6urKPwZLly4VAMSJEydyXOf09HTh4OAgevTooVa+Z88eIUmSuHr1qhBCCF9fX9G5c+cc55GbzPWbMGGCWrmXl5faBbyXl5do2LChSE9Pl8uuXbsmJEkSwcHBQoj/Low2btyoNq9Dhw4JACIiIkIIIUTHjh3F4MGDhZaWlvwD8umnn4pWrVrla3mZ8923b5/a8rp37y48PDzk93379lXbTkL898N69OjRXLdN5vq0adNGrXz79u0CgNi7d69clpycLOzt7dW2v729vZxc2Lp1qyhXrpzo1KmTfCN84MABAUA8evRICPF2++/1G74rV64IANluwNzd3YWfn1+O80lPTxcpKSnip59+EjY2NnL5ixcvhKGhodr8d+zYIQCImzdv5hpXbgmRevXqqdXr27evqFixovx+9OjRolSpUmoJmJCQEAHgjQmRcePG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" + }, + "metadata": {} + } + ], + "execution_count": 25, + "metadata": { + "gather": { + "logged": 1747773117836 + } + }, + "id": "41839376-61d5-498d-b575-d702356e1521" } - }, - "source": [ - "# Knowledge Distillation: Generating Training Data from a Teacher Model\n", - "\n", - "This notebook demonstrates the first step in knowledge distillation: generating high-quality training data from a large \"teacher\" model. Knowledge distillation is a technique where a smaller, more efficient model learns from a larger, more powerful model rather than directly from raw data.\n", - "\n", - "![](../../lab_manual/images/step-1.png)\n", - "\n", - "## What You'll Learn\n", - "- How to load and format multiple-choice questions\n", - "- How to send these questions to a large language model (DeepSeek-V3)\n", - "- How to collect and save the teacher model's responses\n", - "- How to analyze the quality of generated training data\n", - "\n", - "## Prerequisites\n", - "- Access to an Azure AI model endpoint (DeepSeek-V3)\n", - "- Azure credentials set up in a local.env file\n", - "- Python environment with necessary libraries\n", - "\n", - "## Setup Instructions\n", - "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", - "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 AzureML\"** using the selector in the top right\n", - "3. **Environment File**: Ensure your `local.env` file exists with proper credentials" - ] - }, - { - "cell_type": "markdown", - "id": "585941a1", - "metadata": {}, - "source": [ - "## 1. Install Required Packages\n", - "\n", - "First, we'll install the packages needed for this notebook. The `dotenv-azd` package allows us to securely load environment variables from Azure Developer CLI (AZD) environments and/or local .env files. This is how we'll access our Azure credentials without hardcoding sensitive information." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dddced75", - "metadata": { - "gather": { - "logged": 1745565408411 - } - }, - "outputs": [], - "source": [ - "pip install dotenv-azd" - ] - }, - { - "cell_type": "markdown", - "id": "40f85a53", - "metadata": {}, - "source": [ - "## 2. Install Data Handling Libraries\n", - "\n", - "Next, we install two important packages:\n", - "\n", - "- `datasets`: Hugging Face's library that provides easy access to thousands of publicly available datasets\n", - "- `tqdm`: A progress bar library that helps visualize long-running operations\n", - "\n", - "The `-U` flag ensures we get the latest versions of these packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ebb8ff3", - "metadata": { - "gather": { - "logged": 1745565221871 - } - }, - "outputs": [], - "source": [ - "pip install datasets tqdm -U" - ] - }, - { - "cell_type": "markdown", - "id": "7d0ebfbd", - "metadata": {}, - "source": [ - "## 3. Import Dataset Libraries\n", - "\n", - "Now we import the libraries needed to work with our datasets:\n", - "\n", - "- `load_dataset`: A function from Hugging Face that lets us download and use public datasets\n", - "- `ABC` (Abstract Base Class): A Python feature that helps us create a framework for our dataset handling" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c9605ccc", - "metadata": { - "gather": { - "logged": 1745565223801 - } - }, - "outputs": [], - "source": [ - "from datasets import load_dataset\n", - "from abc import ABC" - ] - }, - { - "cell_type": "markdown", - "id": "61c51934", - "metadata": {}, - "source": [ - "## 4. Create Base Dataset Class\n", - "\n", - "Here we define our base class for handling datasets. This creates a common structure that all our dataset handlers will follow:\n", - "\n", - "1. We're using Python's Abstract Base Class (`ABC`) as a foundation\n", - "2. The class initializes three important attributes that will track our data files:\n", - " - `train_data_file_name`: For our training data\n", - " - `test_data_file_name`: For our test data\n", - " - `eval_data_file_name`: For our evaluation/validation data\n", - "\n", - "These file names are initially set to `None` and will be filled in later when we create our datasets." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "806f5e03", - "metadata": { - "gather": { - "logged": 1745565224814 - } - }, - "outputs": [], - "source": [ - "class InputDataset(ABC):\n", - " def __init__(self):\n", - " super().__init__()\n", - " (\n", - " self.train_data_file_name,\n", - " self.test_data_file_name,\n", - " self.eval_data_file_name,\n", - " ) = (None, None, None)" - ] - }, - { - "cell_type": "markdown", - "id": "43455abd", - "metadata": {}, - "source": [ - "## 5. Create Specialized Dataset Handler\n", - "\n", - "This class extends our base `InputDataset` to handle loading data specifically from Hugging Face. The `load_hf_dataset` method does the following:\n", - "\n", - "1. Loads a dataset from Hugging Face by name (e.g., \"tau/commonsense_qa\")\n", - "2. Creates three different data splits:\n", - " - Training data: Used to generate examples for our model\n", - " - Validation data: Used to evaluate during training\n", - " - Test data: Used for final evaluation\n", - "\n", - "3. Controls how many examples we take from each split using the sample size parameters\n", - "\n", - "The code handles two scenarios:\n", - "- Datasets that already have validation splits\n", - "- Datasets that only have train/test splits (in which case it creates a validation set from the training data)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34315645", - "metadata": { - "gather": { - "logged": 1745565225277 - } - }, - "outputs": [], - "source": [ - "class CQnAHuggingFaceInputDataset(InputDataset):\n", - " \"\"\"\n", - " Loads the HuggingFace dataset\n", - " \"\"\"\n", - "\n", - " def __init__(self):\n", - " super().__init__()\n", - "\n", - " def load_hf_dataset(\n", - " self,\n", - " dataset_name,\n", - " train_sample_size=10,\n", - " val_sample_size=10,\n", - " test_sample_size=10,\n", - " train_split_name=\"train\",\n", - " val_split_name=\"validation\",\n", - " test_split_name=\"test\",\n", - " ):\n", - " full_dataset = load_dataset(dataset_name)\n", - "\n", - " if val_split_name is not None:\n", - " train_data = full_dataset[train_split_name].select(range(train_sample_size))\n", - " val_data = full_dataset[val_split_name].select(range(val_sample_size))\n", - " test_data = full_dataset[test_split_name].select(range(test_sample_size))\n", - " else:\n", - " train_val_data = full_dataset[train_split_name].select(\n", - " range(train_sample_size + val_sample_size)\n", - " )\n", - " train_data = train_val_data.select(range(train_sample_size))\n", - " val_data = train_val_data.select(\n", - " range(train_sample_size, train_sample_size + val_sample_size)\n", - " )\n", - " test_data = full_dataset[test_split_name].select(range(test_sample_size))\n", - "\n", - " return train_data, val_data, test_data" - ] - }, - { - "cell_type": "markdown", - "id": "8b2eb02d", - "metadata": {}, - "source": [ - "## 6. Load the CommonsenseQA Dataset\n", - "\n", - "Now we'll actually load our dataset using the classes we defined. Here's what this cell does:\n", - "\n", - "1. **Define sample sizes**: We're using 100 examples each for training and validation to keep this notebook running quickly\n", - "\n", - "2. **Choose a dataset**: We're using \"tau/commonsense_qa\", which contains multiple-choice questions that test common sense reasoning\n", - "\n", - "3. **Create an instance of our dataset handler** and use it to load the data\n", - "\n", - "4. **Print the dataset sizes** to confirm everything loaded correctly\n", - "\n", - "The CommonsenseQA dataset is perfect for knowledge distillation because it contains challenging questions with five possible answers (labeled A through E). This will test how well our model has learned to reason like the teacher model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3a92e682", - "metadata": { - "gather": { - "logged": 1745565225856 - } - }, - "outputs": [], - "source": [ - "# We can define train and test sample sizes here. Validation size is kept same as test sample size\n", - "train_sample_size = 100\n", - "val_sample_size = 100\n", - "\n", - "# Sample notebook using the dataset: https://huggingface.co/datasets/tau/commonsense_qa\n", - "dataset_name = \"tau/commonsense_qa\"\n", - "input_dataset = CQnAHuggingFaceInputDataset()\n", - "\n", - "# Note: train_split_name and test_split_name can vary by dataset. They are passed as arguments in load_hf_dataset.\n", - "# If validation_split_name is None, the below function will split the train set to create the specified sized validation set.\n", - "train, val, _ = input_dataset.load_hf_dataset(\n", - " dataset_name=dataset_name,\n", - " train_sample_size=train_sample_size,\n", - " val_sample_size=val_sample_size,\n", - " train_split_name=\"train\",\n", - " val_split_name=\"validation\",\n", - ")\n", - "\n", - "print(\"Len of train data sample is \" + str(len(train)))\n", - "print(\"Len of validation data sample is \" + str(len(val)))" - ] - }, - { - "cell_type": "markdown", - "id": "9e2508d5", - "metadata": {}, - "source": [ - "## 7. Create Data Directory\n", - "\n", - "This simple command creates a directory called `data` where we'll store our processed files. The `-p` flag ensures that:\n", - "\n", - "1. If the directory already exists, no error will occur\n", - "2. If parent directories don't exist, they will be created automatically\n", - "\n", - "This is where we'll store our questions and the teacher model's answers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4a458770", - "metadata": {}, - "outputs": [], - "source": [ - "! mkdir -p data" - ] - }, - { - "cell_type": "markdown", - "id": "cfdfaa69", - "metadata": {}, - "source": [ - "## 8. Set Output File Path\n", - "\n", - "Here we define the path where we'll save our processed training data. The file will be called `train_original_data.jsonl` in the data directory we just created.\n", - "\n", - "The `.jsonl` extension indicates this is a JSON Lines file format - a convenient format for large datasets where each line is a valid JSON object. This format is useful because:\n", - "\n", - "- It allows processing one example at a time (streaming)\n", - "- Each example is self-contained on its own line\n", - "- It's human-readable and easily parsed by most programming languages" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25914972", - "metadata": { - "gather": { - "logged": 1745565227374 - } - }, - "outputs": [], - "source": [ - "train_data_path = \"./data/train_original_data.jsonl\"" - ] - }, - { - "cell_type": "markdown", - "id": "708f4dc8", - "metadata": {}, - "source": [ - "## 9. Import JSON Library\n", - "\n", - "Here we import the `json` module, which is part of Python's standard library. This module provides functions to:\n", - "\n", - "- Convert Python objects to JSON strings (`json.dumps()`)\n", - "- Parse JSON strings into Python objects (`json.loads()`)\n", - "- Write JSON data to files (`json.dump()`)\n", - "- Read JSON data from files (`json.load()`)\n", - "\n", - "We'll use these functions to format and save our questions and answers in a structured way." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df1f3072", - "metadata": { - "gather": { - "logged": 1745565227781 - } - }, - "outputs": [], - "source": [ - "import json" - ] - }, - { - "cell_type": "markdown", - "id": "9f67827b", - "metadata": {}, - "source": [ - "## 10. Format Questions for the Teacher Model\n", - "\n", - "This cell prepares our questions in the format required by the teacher model. Here's what happens step-by-step:\n", - "\n", - "1. **Define prompts**:\n", - " - A system prompt that tells the model to answer with only one letter (A, B, C, D, or E)\n", - " - A template for user messages that formats each question and its answer choices\n", - "\n", - "2. **Create/clear the output file** to ensure we start with a clean file\n", - "\n", - "3. **Process each example** in our training dataset:\n", - " - Extract the question and its multiple-choice options\n", - " - Format the answer choices with their labels (A, B, C, D, E)\n", - " - Create a message structure with system and user prompts\n", - " - Save each formatted example to our JSONL file\n", - "\n", - "4. **Add error handling** and progress tracking\n", - "\n", - "5. **Validate the file creation** by checking the first line\n", - "\n", - "The resulting file will contain all our questions formatted in a way that's ready to be sent to the teacher model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ad051ac1", - "metadata": { - "gather": { - "logged": 1745565228450 - } - }, - "outputs": [], - "source": [ - "system_prompt = \"You are a helpful assistant. Your output should only be one of the five choices: 'A', 'B', 'C', 'D', or 'E'.\"\n", - "user_prompt_template = \"Answer the following multiple-choice question by selecting the correct option.\\n\\nQuestion: {question}\\nAnswer Choices:\\n{answer_choices}\"\n", - "\n", - "# First, create/truncate the file to ensure it's empty and exists\n", - "with open(train_data_path, \"w\", encoding='utf-8') as f:\n", - " pass # Just create the file, we'll append to it later\n", - "\n", - "print(f\"Generating training data and writing to {train_data_path}\")\n", - "\n", - "# Check if train data has expected structure\n", - "if len(train) == 0:\n", - " print(\"Error: Train dataset is empty!\")\n", - "else:\n", - " print(f\"Sample train data keys: {list(train[0].keys())}\")\n", - " \n", - " # Verify train data has the expected fields before processing\n", - " sample = train[0]\n", - " if 'question' in sample and 'choices' in sample:\n", - " for i, row in enumerate(train):\n", - " try:\n", - " data = {\"messages\": []}\n", - " data[\"messages\"].append(\n", - " {\n", - " \"role\": \"system\",\n", - " \"content\": system_prompt,\n", - " }\n", - " )\n", - " question, choices = row[\"question\"], row[\"choices\"]\n", - " labels, choice_list = choices[\"label\"], choices[\"text\"]\n", - " answer_choices = [\n", - " \"({}) {}\".format(labels[i], choice_list[i]) for i in range(len(labels))\n", - " ]\n", - " answer_choices = \"\\n\".join(answer_choices)\n", - " data[\"messages\"].append(\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": user_prompt_template.format(\n", - " question=question, answer_choices=answer_choices\n", - " ),\n", - " }\n", - " )\n", - " \n", - " # Write data as valid JSON line\n", - " with open(train_data_path, \"a\", encoding='utf-8') as f:\n", - " f.write(json.dumps(data, ensure_ascii=False) + \"\\n\")\n", - " \n", - " if i % 10 == 0: # Show progress every 10 items\n", - " print(f\"Processed {i+1}/{len(train)} questions\", end=\"\\r\")\n", - " \n", - " except Exception as e:\n", - " print(f\"\\nError processing row {i}: {str(e)}\")\n", - " print(f\"Row data: {row}\")\n", - " \n", - " print(f\"\\nSuccessfully wrote {len(train)} questions to {train_data_path}\")\n", - " \n", - " # Validate the file has content\n", - " with open(train_data_path, \"r\", encoding='utf-8') as f:\n", - " first_line = f.readline().strip()\n", - " print(f\"File validation: {'SUCCESS' if first_line else 'FAILED - Empty file'}\")\n", - " if first_line:\n", - " # Try parsing the first line to validate JSON\n", - " try:\n", - " json_data = json.loads(first_line)\n", - " print(f\"JSON validation: SUCCESS\")\n", - " except json.JSONDecodeError as e:\n", - " print(f\"JSON validation: FAILED - {str(e)}\")\n", - " else:\n", - " print(f\"Error: Train data doesn't have the expected structure. Found keys: {list(sample.keys())}\")\n", - " print(\"Expected 'question' and 'choices' keys in each sample.\")" - ] - }, - { - "cell_type": "markdown", - "id": "6063901e", - "metadata": {}, - "source": [ - "## 11. Load Environment Variables\n", - "\n", - "This cell loads the configuration and credentials needed to connect to our teacher model. Instead of hardcoding sensitive information like API keys, we use environment variables for better security:\n", - "\n", - "1. `load_azd_env()`: Tries to load variables from an Azure Developer CLI (AZD) environment if available\n", - "2. `load_dotenv()`: Falls back to loading from a local `.env` file\n", - "\n", - "This approach ensures our credentials remain secure and can be easily changed without modifying the code. The environment variables should include details for connecting to the Azure AI services where our teacher model is hosted." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bfde7fd0", - "metadata": { - "gather": { - "logged": 1745565229548 - } - }, - "outputs": [], - "source": [ - "import os\n", - "from dotenv_azd import load_azd_env\n", - "from dotenv import load_dotenv\n", - "\n", - "# Load environment variables from current AZD environment if available\n", - "load_azd_env(quiet=True)\n", - "\n", - "# Load environment variables from local.env file if it exists\n", - "load_dotenv(dotenv_path=\"local.env\")" - ] - }, - { - "cell_type": "markdown", - "id": "c2e2c3fd", - "metadata": {}, - "source": [ - "## 12. Access Teacher Model Credentials\n", - "\n", - "Now we retrieve the specific credentials needed to access our teacher model from the environment variables. We need three key pieces of information:\n", - "\n", - "1. `teacher_model_name`: The name of the model we'll be using (e.g., \"DeepSeek-V3\")\n", - "2. `teacher_model_endpoint_url`: The URL where the model API is hosted\n", - "3. `teacher_model_api_key`: The authentication key to access the service\n", - "\n", - "We print confirmation that these values were loaded (without revealing the actual API key for security reasons). This helps verify our setup is correct before proceeding.\n", - "\n", - "Make sure these environment variables are set in your `local.env` file or AZD environment before running this notebook." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c8fc57b", - "metadata": { - "gather": { - "logged": 1745565229865 - } - }, - "outputs": [], - "source": [ - "# Get Azure AI Foundry credentials from environment variables\n", - "teacher_model_name = os.getenv('TEACHER_MODEL_NAME')\n", - "teacher_model_endpoint_url = os.getenv('TEACHER_MODEL_ENDPOINT')\n", - "teacher_model_api_key = os.getenv('TEACHER_MODEL_KEY')\n", - "\n", - "# Print values for debugging (remove in production)\n", - "print(f\"Teacher Model Name: {teacher_model_name}\")\n", - "print(f\"Teacher Model Endpoint: {teacher_model_endpoint_url}\")\n", - "# Don't print the API key for security reasons\n", - "print(f\"Teacher Model API Key loaded: {'Yes' if teacher_model_api_key else 'No'}\")" - ] - }, - { - "cell_type": "markdown", - "id": "419d134e", - "metadata": {}, - "source": [ - "## 13. Install Azure AI Inference SDK\n", - "\n", - "Here we install the `azure-ai-inference` package, which is Microsoft's official SDK for interacting with Azure AI services. This library provides:\n", - "\n", - "1. Client classes to connect to Azure AI models\n", - "2. Methods to send prompts and receive responses\n", - "3. Classes to structure messages in the expected chat format\n", - "4. Functionality to set parameters like temperature and token limits\n", - "\n", - "This modern API makes it easy to communicate with large language models hosted on Azure AI services." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5052dde2", - "metadata": { - "gather": { - "logged": 1745565232660 - } - }, - "outputs": [], - "source": [ - "pip install azure-ai-inference" - ] - }, - { - "cell_type": "markdown", - "id": "fdfa058f", - "metadata": {}, - "source": [ - "## 14. Import Azure AI and Utility Libraries\n", - "\n", - "Now we import the libraries we'll need to communicate with the teacher model:\n", - "\n", - "1. **Azure AI Inference Classes**:\n", - " - `ChatCompletionsClient`: The main client for sending requests to the model\n", - " - `SystemMessage` and `UserMessage`: Classes to format our prompts correctly\n", - " - `AzureKeyCredential`: For authentication with our API key\n", - "\n", - "2. **Utility Libraries**:\n", - " - `json`: For working with JSON data (imported earlier)\n", - " - `os`: For accessing environment variables\n", - " - `tqdm`: For showing progress bars during processing\n", - "\n", - "These libraries give us everything we need to send our questions to the teacher model and process its responses." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "62fb1cbf", - "metadata": { - "gather": { - "logged": 1745565233019 - } - }, - "outputs": [], - "source": [ - "import os\n", - "import json\n", - "from azure.ai.inference import ChatCompletionsClient\n", - "from azure.ai.inference.models import SystemMessage, UserMessage\n", - "from azure.core.credentials import AzureKeyCredential\n", - "from tqdm import tqdm" - ] - }, - { - "cell_type": "markdown", - "id": "6aa428a6", - "metadata": {}, - "source": [ - "## 15. Define Question Processing Function\n", - "\n", - "This function handles sending a single question to the teacher model and processing its response. Here's how it works:\n", - "\n", - "1. **Create message objects** from our JSON data:\n", - " - Converts system prompts to `SystemMessage` objects\n", - " - Converts user prompts to `UserMessage` objects\n", - "\n", - "2. **Sends the messages** to the teacher model through the Azure AI client\n", - "\n", - "3. **Processes the response** and formats it for our training data\n", - "\n", - "4. **Handles errors** gracefully by returning an error message if something goes wrong\n", - "\n", - "The function returns a dictionary containing:\n", - "- The original question\n", - "- The model's response (answer)\n", - "- The full response object (for debugging)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5f12ae7", - "metadata": { - "gather": { - "logged": 1745565234026 - } - }, - "outputs": [], - "source": [ - "def process_question(question_data):\n", - " try:\n", - " messages = []\n", - " for msg in question_data[\"messages\"]:\n", - " if msg[\"role\"] == \"system\":\n", - " messages.append(SystemMessage(content=msg[\"content\"]))\n", - " elif msg[\"role\"] == \"user\":\n", - " messages.append(UserMessage(content=msg[\"content\"]))\n", - "\n", - " response = client.complete(\n", - " messages=messages,\n", - " model=model_name,\n", - " max_tokens=10000 # Reduced since we just need short answers like A, B, C, D, or E\n", - " )\n", - "\n", - " return {\n", - " \"question\": question_data[\"messages\"][1][\"content\"],\n", - " \"response\": response.choices[0].message.content,\n", - " \"full_response\": response\n", - " }\n", - " except Exception as e:\n", - " return {\n", - " \"question\": question_data[\"messages\"][1][\"content\"] if len(question_data[\"messages\"]) > 1 else \"Error\",\n", - " \"response\": f\"Error: {str(e)}\",\n", - " \"full_response\": None\n", - " }" - ] - }, - { - "cell_type": "markdown", - "id": "5a8e4688", - "metadata": {}, - "source": [ - "## 16. Initialize Azure AI Client\n", - "\n", - "Now we create the Azure AI client that will connect to our teacher model. This client handles all communication with the Azure AI service:\n", - "\n", - "1. **Set up the endpoint URL** using our environment variable\n", - "2. **Set the model name** to specify which model to use\n", - "3. **Create the client** with our endpoint and authentication credentials\n", - "\n", - "This client will be used by our `process_question` function to send questions to the teacher model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "448b00cf", - "metadata": { - "gather": { - "logged": 1745565234583 - } - }, - "outputs": [], - "source": [ - "endpoint = f\"{teacher_model_endpoint_url}\"\n", - "model_name = teacher_model_name\n", - "key = teacher_model_api_key\n", - "client = ChatCompletionsClient(endpoint=endpoint, credential=AzureKeyCredential(key))" - ] - }, - { - "cell_type": "markdown", - "id": "0ec9d6a9", - "metadata": {}, - "source": [ - "## 17. Process Questions with the Teacher Model\n", - "\n", - "This section contains the main processing loop that sends our formatted questions to the teacher model. Here's what this code does:\n", - "\n", - "1. **Validates the input file** to ensure it exists and has valid JSON content\n", - "\n", - "2. **Sets up a progress bar** to track processing in real-time\n", - "\n", - "3. **Processes each question** by:\n", - " - Reading the question data from the file\n", - " - Sending it to our `process_question` function\n", - " - Adding the result to our results list\n", - " - Updating the progress bar\n", - "\n", - "4. **Handles errors** that might occur during processing\n", - "\n", - "5. **Reports overall statistics** at the end\n", - "\n", - "This is the core of our knowledge distillation process - we're collecting expert knowledge from the teacher model that will be used to train our smaller student model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "285ab8b3", - "metadata": { - "gather": { - "logged": 1745565289389 - } - }, - "outputs": [], - "source": [ - "# Read the JSONL file and process each question\n", - "import os\n", - "results = []\n", - "\n", - "# Ensure we're using the correct file path\n", - "train_data_path = \"./data/train_original_data.jsonl\"\n", - "\n", - "# Check if the file exists and has content before proceeding\n", - "if not os.path.exists(train_data_path):\n", - " print(f\"Error: File {train_data_path} does not exist! Please run the previous cells to create it.\")\n", - "else:\n", - " file_size = os.path.getsize(train_data_path)\n", - " if file_size == 0:\n", - " print(f\"Warning: File {train_data_path} exists but is empty (0 bytes)! Please run the data generation cells first.\")\n", - " else:\n", - " print(f\"File {train_data_path} exists and has {file_size} bytes.\")\n", - " \n", - " # Validate first line to ensure it's valid JSON\n", - " with open(train_data_path, 'r', encoding='utf-8') as file:\n", - " first_line = file.readline().strip()\n", - " if not first_line:\n", - " print(\"Error: File exists but first line is empty!\")\n", - " else:\n", - " try:\n", - " # Try parsing the first JSON line\n", - " test_json = json.loads(first_line)\n", - " print(\"JSON validation: First line is valid JSON\")\n", - " \n", - " # Continue with processing all lines\n", - " file.seek(0) # Go back to start of file\n", - " total_lines = sum(1 for _ in file if _.strip())\n", - " \n", - " print(f\"Processing {total_lines} questions from {train_data_path}\")\n", - " file.seek(0) # Go back to start of file again\n", - " \n", - " # Initialize tqdm progress bar\n", - " progress_bar = tqdm(total=total_lines, desc=\"Processing questions\", unit=\"question\")\n", - " \n", - " processed_count = 0\n", - " error_count = 0\n", - " \n", - " for i, line in enumerate(file):\n", - " if line.strip(): # Skip empty lines\n", - " try:\n", - " question_data = json.loads(line)\n", - " result = process_question(question_data)\n", - " results.append(result)\n", - " processed_count += 1\n", - " \n", - " # Update progress bar description with latest result\n", - " progress_bar.set_description(f\"Latest answer: {result['response'][:20]}...\")\n", - " progress_bar.update(1)\n", - " \n", - " except json.JSONDecodeError as e:\n", - " error_count += 1\n", - " print(f\"\\nError parsing line {i+1}: {str(e)}\")\n", - " print(f\"Problematic line content: '{line[:100]}...'\")\n", - " progress_bar.update(1)\n", - " except Exception as e:\n", - " error_count += 1\n", - " print(f\"\\nError processing line {i+1}: {str(e)}\")\n", - " progress_bar.update(1)\n", - " \n", - " progress_bar.close()\n", - " \n", - " print(f\"\\nProcessing complete: {processed_count} successful, {error_count} errors\")\n", - " \n", - " except json.JSONDecodeError as e:\n", - " print(f\"Error: Invalid JSON in file. First line error: {str(e)}\")\n", - " print(f\"First line content: '{first_line[:100]}...'\")\n", - " \n", - " except Exception as e:\n", - " print(f\"Error during file processing: {str(e)}\")" - ] - }, - { - "cell_type": "markdown", - "id": "f40cbf5e", - "metadata": {}, - "source": [ - "## 18. Save the Teacher Model Responses\n", - "\n", - "Now that we've collected answers from our teacher model, we'll save them to a new file for training our student model. This cell:\n", - "\n", - "1. **Defines the output file path** for our processed training data\n", - "\n", - "2. **Creates a progress bar** to track the saving process\n", - "\n", - "3. **Formats each result** into a simpler structure with just the question and answer\n", - "\n", - "4. **Writes each example** as a line in our output JSON Lines file\n", - "\n", - "The resulting `train_data.jsonl` file will be the training data for our student model in the next notebook. This is the key output from our knowledge distillation process." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8d6de3fb", - "metadata": { - "gather": { - "logged": 1745565289816 - } - }, - "outputs": [], - "source": [ - "output_file_path = \"./data/train_data.jsonl\"\n", - "\n", - "# Initialize tqdm progress bar for writing results\n", - "print(f\"Writing {len(results)} processed questions to {output_file_path}\")\n", - "with open(output_file_path, 'w', encoding='utf-8') as f:\n", - " for result in tqdm(results, desc=\"Writing results\", unit=\"record\"):\n", - " # Extract just the question content (removing the instruction part)\n", - " question_text = result[\"question\"]\n", - " # if \"Question: \" in question_text:\n", - " # question_text = question_text.split(\"Question: \")[1].split(\"\\nAnswer Choices:\")[0]\n", - "\n", - " # Create the simplified output format\n", - " output_line = {\n", - " \"Question\": question_text,\n", - " \"Answer\": result[\"response\"]\n", - " }\n", - "\n", - " # Write as JSONL (one JSON object per line)\n", - " f.write(json.dumps(output_line, ensure_ascii=False) + '\\n')" - ] - }, - { - "cell_type": "markdown", - "id": "ee1f7457", - "metadata": {}, - "source": [ - "## 19. Install Visualization Libraries\n", - "\n", - "Before we can analyze our results visually, we need to install the `matplotlib` library. This popular Python library will allow us to create charts and graphs to better understand the data we've collected from the teacher model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "95ccbff4", - "metadata": {}, - "outputs": [], - "source": [ - "pip install matplotlib" - ] - }, - { - "cell_type": "markdown", - "id": "21aaeaa3", - "metadata": {}, - "source": [ - "## 20. Analyze Answer Distribution\n", - "\n", - "Now we'll analyze how the teacher model responded to our questions. This helps us understand the quality of our training data and identify any potential issues. This cell:\n", - "\n", - "1. **Counts the frequency** of each answer choice (A, B, C, D, E)\n", - "\n", - "2. **Identifies non-standard responses** (anything other than a single letter)\n", - "\n", - "3. **Creates a bar chart** showing the distribution of answers\n", - "\n", - "4. **Calculates the success rate** - what percentage of responses were valid letter choices\n", - "\n", - "5. **Analyzes problematic responses** if any were found\n", - "\n", - "This analysis helps us ensure that our teacher model is providing high-quality, consistent responses that will be good training examples for our student model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de00c5f2", - "metadata": { - "gather": { - "logged": 1745565290007 - } - }, - "outputs": [], - "source": [ - "# Visualize the distillation results\n", - "import matplotlib.pyplot as plt\n", - "import pandas as pd\n", - "import re\n", - "\n", - "# Count occurrences of each answer\n", - "answer_counts = {}\n", - "other_responses = []\n", - "\n", - "for result in results:\n", - " answer = result['response'].strip()\n", - " \n", - " # Try to extract just the letter if there's additional text\n", - " # This regex matches: A, A., A), (A), (A) text\n", - " match = re.search(r'(?:^|\\(|\\s)([A-E])(?:\\)|\\.|\\s|$)', answer, re.IGNORECASE)\n", - " if match:\n", - " answer = match.group(1).upper() # Extract just the letter and convert to uppercase\n", - " answer_counts[answer] = answer_counts.get(answer, 0) + 1\n", - " else:\n", - " # For non-standard answers\n", - " answer_counts['Other'] = answer_counts.get('Other', 0) + 1\n", - " # Store problematic responses for analysis\n", - " other_responses.append(answer)\n", - "\n", - "# Create a DataFrame for visualization\n", - "df = pd.DataFrame(list(answer_counts.items()), columns=['Answer', 'Count'])\n", - "df = df.sort_values('Count', ascending=False)\n", - "\n", - "# Create a bar chart\n", - "plt.figure(figsize=(10, 6))\n", - "bars = plt.bar(df['Answer'], df['Count'], color='skyblue')\n", - "plt.title('Distribution of Answers in Distilled Dataset')\n", - "plt.xlabel('Answer Choice')\n", - "plt.ylabel('Count')\n", - "plt.grid(axis='y', linestyle='--', alpha=0.7)\n", - "\n", - "# Add count labels on top of each bar\n", - "for bar in bars:\n", - " height = bar.get_height()\n", - " plt.text(bar.get_x() + bar.get_width()/2., height + 1,\n", - " f'{height}', ha='center', va='bottom')\n", - "\n", - "# Calculate and display success rate (assuming valid answers are A-E)\n", - "total_questions = sum(answer_counts.values())\n", - "valid_answers = sum(answer_counts.get(ans, 0) for ans in ['A', 'B', 'C', 'D', 'E'])\n", - "success_rate = (valid_answers / total_questions) * 100 if total_questions > 0 else 0\n", - "\n", - "plt.figtext(0.5, 0.01, f'Success Rate: {success_rate:.1f}% ({valid_answers}/{total_questions} questions with valid answers)', \n", - " ha='center', fontsize=12)\n", - "\n", - "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", - "plt.show()\n", - "\n", - "# Display sample of 'Other' responses if they exist\n", - "if other_responses:\n", - " print(f\"\\nSamples of 'Other' responses (showing up to 10):\")\n", - " for i, resp in enumerate(other_responses[:10]):\n", - " print(f\" {i+1}. '{resp}'\")\n", - " \n", - " # Analyze if there are patterns in the 'Other' responses\n", - " lowercase_letters = sum(1 for r in other_responses if r.lower() in ['a', 'b', 'c', 'd', 'e'])\n", - " has_period = sum(1 for r in other_responses if re.search(r'[A-Ea-e]\\.', r))\n", - " has_explanation = sum(1 for r in other_responses if len(r) > 5) # Simple heuristic for explanations\n", - " \n", - " print(f\"\\nAnalysis of {len(other_responses)} 'Other' responses:\")\n", - " print(f\" - Lowercase letters (a-e): {lowercase_letters} ({lowercase_letters/len(other_responses)*100:.1f}%)\")\n", - " print(f\" - Answers with periods (A.): {has_period} ({has_period/len(other_responses)*100:.1f}%)\")\n", - " print(f\" - Likely explanations (>5 chars): {has_explanation} ({has_explanation/len(other_responses)*100:.1f}%)\")" - ] - }, - { - "cell_type": "markdown", - "id": "f9c828d2", - "metadata": {}, - "source": [ - "## 21. Detect Potential Model Bias\n", - "\n", - "This cell analyzes whether the teacher model shows any bias toward specific answer choices. In an ideal world, the multiple-choice answers would be evenly distributed (about 20% for each choice A-E). Here's what we're doing:\n", - "\n", - "1. **Calculate the expected distribution** assuming a uniform 20% for each answer choice\n", - "\n", - "2. **Compare the actual counts** to what we would expect in an unbiased dataset\n", - "\n", - "3. **Compute the percentage difference** between expected and actual distributions\n", - "\n", - "4. **Visualize the comparison** with a bar chart showing actual vs. expected counts\n", - "\n", - "This analysis helps identify if the teacher model has any systematic preferences for certain answer choices, which could potentially be transferred to the student model. Significant bias might require additional data processing or model adjustments." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "be286dac", - "metadata": { - "gather": { - "logged": 1745565290281 - } - }, - "outputs": [], - "source": [ - "# Analyze potential model bias in answer distribution\n", - "\n", - "# Expected distribution (ideally uniform for multiple choice)\n", - "expected_prob = 0.2 # 20% chance for each of A,B,C,D,E in a uniform distribution\n", - "expected_counts = {letter: total_questions * expected_prob for letter in ['A', 'B', 'C', 'D', 'E']}\n", - "\n", - "# Create a DataFrame for comparing actual vs expected\n", - "comparison_data = []\n", - "for letter in ['A', 'B', 'C', 'D', 'E']:\n", - " actual = answer_counts.get(letter, 0)\n", - " expected = expected_counts[letter]\n", - " difference = actual - expected\n", - " percent_diff = (difference / expected) * 100 if expected > 0 else 0\n", - " comparison_data.append({\n", - " 'Answer': letter,\n", - " 'Actual Count': actual,\n", - " 'Expected Count': expected,\n", - " 'Difference': difference,\n", - " 'Percent Difference': percent_diff\n", - " })\n", - "\n", - "comparison_df = pd.DataFrame(comparison_data)\n", - "print(\"\\nAnalysis of potential answer bias:\")\n", - "display(comparison_df)\n", - "\n", - "# Create a visual comparison\n", - "plt.figure(figsize=(12, 6))\n", - "x = range(len(comparison_df))\n", - "width = 0.35\n", - "\n", - "plt.bar([i - width/2 for i in x], comparison_df['Actual Count'], width, label='Actual', color='skyblue')\n", - "plt.bar([i + width/2 for i in x], comparison_df['Expected Count'], width, label='Expected', color='lightgreen')\n", - "\n", - "plt.xlabel('Answer Choice')\n", - "plt.ylabel('Count')\n", - "plt.title('Actual vs Expected Answer Distribution')\n", - "plt.xticks(x, comparison_df['Answer'])\n", - "plt.legend()\n", - "plt.grid(axis='y', linestyle='--', alpha=0.7)\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "84760413", - "metadata": {}, - "source": [ - "## 22. Install Advanced Analysis Libraries\n", - "\n", - "For more sophisticated analysis of our results, we'll install additional libraries:\n", - "\n", - "- **seaborn**: An enhanced statistical data visualization library built on matplotlib\n", - "- **scikit-learn**: A machine learning library that includes tools for data analysis and metrics\n", - "\n", - "These libraries will allow us to create confusion matrices and other advanced visualizations to better understand patterns in our data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9954f45e", - "metadata": {}, - "outputs": [], - "source": [ - "pip install seaborn scikit-learn" - ] - }, - { - "cell_type": "markdown", - "id": "26784578", - "metadata": {}, - "source": [ - "## 23. Create Distribution Bias Matrix\n", - "\n", - "This cell creates an advanced visualization to analyze potential bias in how the teacher model answers questions. Since we don't have ground truth labels for our questions, we use an alternative approach:\n", - "\n", - "1. **Create a pseudo-confusion matrix** where:\n", - " - The diagonal shows how often the model selects each answer choice\n", - " - In an unbiased model, all diagonal values would be around 0.2 (20%)\n", - "\n", - "2. **Visualize using a heatmap** with color intensity showing the proportion of each answer\n", - "\n", - "3. **Add annotations** explaining how to interpret the matrix\n", - "\n", - "This analysis helps us identify if the teacher model systematically favors certain answer choices, which could affect the knowledge transferred to the student model. Significant imbalances might indicate bias that should be addressed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1e318ccd-9825-4b21-a321-89bb5bdfbc22", - "metadata": { - "gather": { - "logged": 1745565290522 - } - }, - "outputs": [], - "source": [ - "# Create a confusion matrix to analyze answer patterns\n", - "import seaborn as sns\n", - "from sklearn.metrics import confusion_matrix\n", - "import numpy as np\n", - "\n", - "# We don't have ground truth labels in this dataset, but we can:\n", - "# 1. Analyze confusion between expected uniform distribution and actual distribution\n", - "# 2. Alternatively, check if there are patterns in how the model responds to different question types\n", - "\n", - "# Approach 1: Create a \"pseudo-confusion matrix\" showing bias toward certain answers\n", - "# Normalize the counts to get proportions\n", - "total_valid_answers = sum(answer_counts.get(ans, 0) for ans in ['A', 'B', 'C', 'D', 'E'])\n", - "pseudo_cm = np.zeros((5, 5))\n", - "\n", - "# Fill the diagonal with actual proportions (representing how much the model prefers each answer)\n", - "for i, letter in enumerate(['A', 'B', 'C', 'D', 'E']):\n", - " actual_prop = answer_counts.get(letter, 0) / total_valid_answers if total_valid_answers > 0 else 0\n", - " expected_prop = 0.2 # Expected uniform distribution (20% each)\n", - " \n", - " # The diagonal shows the actual proportion\n", - " pseudo_cm[i, i] = actual_prop\n", - " \n", - " # The off-diagonal elements represent the \"confusion\" - the difference between\n", - " # expected and actual distribution\n", - " for j in range(5):\n", - " if i != j:\n", - " pseudo_cm[i, j] = (1 - actual_prop) / 4 # Distribute remaining probability\n", - "\n", - "# Plot the pseudo-confusion matrix\n", - "plt.figure(figsize=(10, 8))\n", - "sns.heatmap(pseudo_cm, annot=True, fmt='.3f', cmap='Blues',\n", - " xticklabels=['A', 'B', 'C', 'D', 'E'],\n", - " yticklabels=['A', 'B', 'C', 'D', 'E'])\n", - "plt.title('Answer Distribution Bias Matrix')\n", - "plt.xlabel('Predicted Answer')\n", - "plt.ylabel('Expected Uniform Distribution')\n", - "plt.tight_layout()\n", - "\n", - "# Add text annotation explaining this visualization\n", - "plt.figtext(0.5, 0.01, \n", - " 'This matrix shows model bias toward certain answers.\\n'\n", - " 'Diagonal values represent the proportion of each answer in the results.\\n'\n", - " 'In an unbiased model, all diagonal values would be close to 0.2 (20%)',\n", - " ha='center', fontsize=11, bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.8))\n", - "\n", - "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", - "plt.show()\n", - "\n", - "# Approach 2: If we had access to ground truth or additional features about questions,\n", - "# we could create an actual confusion matrix here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "41839376-61d5-498d-b575-d702356e1521", - "metadata": { - "gather": { - "logged": 1745565315423 - } - }, - "outputs": [], - "source": [ - "# Creating a real confusion matrix using ground truth data\n", - "import json\n", - "import re\n", - "import numpy as np\n", - "import seaborn as sns\n", - "import matplotlib.pyplot as plt\n", - "from sklearn.metrics import confusion_matrix\n", - "\n", - "# Load the ground truth data\n", - "train_data_path = \"./data/train_data.jsonl\"\n", - "\n", - "# Arrays to store ground truth and predicted labels\n", - "true_labels = []\n", - "predicted_labels = []\n", - "\n", - "# Load and parse the data\n", - "print(\"Loading ground truth data from\", train_data_path)\n", - "with open(train_data_path, 'r', encoding='utf-8') as file:\n", - " for line in file:\n", - " if line.strip():\n", - " try:\n", - " data = json.loads(line)\n", - " \n", - " # Extract the predicted answer from our results variable\n", - " question_text = data['Question']\n", - " model_answer = data['Answer'].strip()\n", - " \n", - " # Find the ground truth for this question by parsing the answer choices\n", - " # For simplicity, we'll use the letter of the answer\n", - " match = re.search(r'\\(([A-E])\\)', model_answer)\n", - " if match:\n", - " predicted_letter = match.group(1).upper()\n", - " predicted_labels.append(predicted_letter)\n", - " \n", - " # Try to extract what the actual ground truth is\n", - " # Since we don't have explicit ground truth,\n", - " # we'll use the consistent answer pattern from the commonsense_qa dataset\n", - " # where the correct answer is part of the answer choices\n", - " \n", - " # For this demonstration, we will assume these answers are correct\n", - " # In a real scenario, you would need to match against the ground truth answer key\n", - " true_labels.append(predicted_letter)\n", - " \n", - " except json.JSONDecodeError as e:\n", - " print(f\"Error parsing line: {str(e)}\")\n", - " except Exception as e:\n", - " print(f\"Error processing line: {str(e)}\")\n", - "\n", - "# Since we don't have separate ground truth labels in this dataset,\n", - "# we'll create a more advanced analysis by grouping answers by question type\n", - "\n", - "# 1. Create a simple question type classifier based on the first word of the question\n", - "question_types = []\n", - "question_type_predicted = []\n", - "\n", - "with open(train_data_path, 'r', encoding='utf-8') as file:\n", - " for line in file:\n", - " if line.strip():\n", - " try:\n", - " data = json.loads(line)\n", - " question_text = data['Question']\n", - " \n", - " # Extract the actual question from the format\n", - " if \"Question: \" in question_text:\n", - " actual_question = question_text.split(\"Question: \")[1].split(\"\\nAnswer Choices:\")[0]\n", - " \n", - " # Simple question type classification based on first word\n", - " first_word = actual_question.strip().split()[0].lower()\n", - " \n", - " # Group question types\n", - " if first_word in ['what', 'which']:\n", - " q_type = 'What/Which'\n", - " elif first_word in ['where']:\n", - " q_type = 'Where'\n", - " elif first_word in ['who']:\n", - " q_type = 'Who'\n", - " elif first_word in ['how']:\n", - " q_type = 'How'\n", - " elif first_word in ['why']:\n", - " q_type = 'Why'\n", - " else:\n", - " q_type = 'Other'\n", - " \n", - " question_types.append(q_type)\n", - " \n", - " # Get the predicted answer letter\n", - " model_answer = data['Answer'].strip()\n", - " match = re.search(r'\\(([A-E])\\)', model_answer)\n", - " if match:\n", - " letter = match.group(1).upper()\n", - " question_type_predicted.append(letter)\n", - " \n", - " except Exception as e:\n", - " print(f\"Error: {str(e)}\")\n", - "\n", - "# Count occurrences of each answer by question type\n", - "q_type_counts = {}\n", - "for q_type, pred in zip(question_types, question_type_predicted):\n", - " if q_type not in q_type_counts:\n", - " q_type_counts[q_type] = {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0}\n", - " \n", - " q_type_counts[q_type][pred] += 1\n", - "\n", - "# Create a better confusion matrix - question type vs answer choice\n", - "unique_types = sorted(set(question_types))\n", - "q_type_matrix = np.zeros((len(unique_types), 5))\n", - "\n", - "for i, q_type in enumerate(unique_types):\n", - " for j, letter in enumerate(['A', 'B', 'C', 'D', 'E']):\n", - " q_type_matrix[i, j] = q_type_counts.get(q_type, {}).get(letter, 0)\n", - "\n", - "# Normalize by row (question type) to get distribution\n", - "row_sums = q_type_matrix.sum(axis=1, keepdims=True)\n", - "row_sums[row_sums == 0] = 1 # Avoid division by zero\n", - "q_type_matrix_norm = q_type_matrix / row_sums\n", - "\n", - "# Plot the question type vs answer choice matrix\n", - "plt.figure(figsize=(12, 10))\n", - "sns.heatmap(q_type_matrix_norm, annot=True, fmt='.2f', cmap='YlGnBu',\n", - " xticklabels=['A', 'B', 'C', 'D', 'E'],\n", - " yticklabels=unique_types)\n", - "plt.title('Answer Distribution by Question Type (Normalized)')\n", - "plt.xlabel('Answer Choice')\n", - "plt.ylabel('Question Type')\n", - "\n", - "# Add an explanation of the visualization\n", - "plt.figtext(0.5, 0.01, \n", - " 'This matrix shows how answer patterns vary by question type.\\n'\n", - " 'Each row shows the distribution of answers for a specific question type.\\n'\n", - " 'An unbiased model would show similar distributions across question types.',\n", - " ha='center', fontsize=11, bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.8))\n", - "\n", - "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", - "plt.show()\n", - "\n", - "# Create a second visualization - pattern analysis by question length\n", - "# Group questions by length and analyze answer patterns\n", - "question_lengths = []\n", - "length_predicted = []\n", - "\n", - "with open(train_data_path, 'r', encoding='utf-8') as file:\n", - " for line in file:\n", - " if line.strip():\n", - " try:\n", - " data = json.loads(line)\n", - " question_text = data['Question']\n", - " \n", - " # Extract the actual question\n", - " if \"Question: \" in question_text:\n", - " actual_question = question_text.split(\"Question: \")[1].split(\"\\nAnswer Choices:\")[0]\n", - " \n", - " # Get question length in words\n", - " word_count = len(actual_question.split())\n", - " \n", - " # Group by length\n", - " if word_count < 10:\n", - " length_group = 'Very Short (<10 words)'\n", - " elif word_count < 15:\n", - " length_group = 'Short (10-14 words)'\n", - " elif word_count < 20:\n", - " length_group = 'Medium (15-19 words)'\n", - " else:\n", - " length_group = 'Long (20+ words)'\n", - " \n", - " question_lengths.append(length_group)\n", - " \n", - " # Get the predicted answer letter\n", - " model_answer = data['Answer'].strip()\n", - " match = re.search(r'\\(([A-E])\\)', model_answer)\n", - " if match:\n", - " letter = match.group(1).upper()\n", - " length_predicted.append(letter)\n", - " \n", - " except Exception as e:\n", - " print(f\"Error: {str(e)}\")\n", - "\n", - "# Count occurrences by length group\n", - "length_counts = {}\n", - "for length, pred in zip(question_lengths, length_predicted):\n", - " if length not in length_counts:\n", - " length_counts[length] = {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0}\n", - " \n", - " length_counts[length][pred] += 1\n", - "\n", - "# Create matrix for length vs answer\n", - "unique_lengths = ['Very Short (<10 words)', 'Short (10-14 words)', \n", - " 'Medium (15-19 words)', 'Long (20+ words)']\n", - "unique_lengths = [l for l in unique_lengths if l in length_counts]\n", - "length_matrix = np.zeros((len(unique_lengths), 5))\n", - "\n", - "for i, length in enumerate(unique_lengths):\n", - " for j, letter in enumerate(['A', 'B', 'C', 'D', 'E']):\n", - " length_matrix[i, j] = length_counts.get(length, {}).get(letter, 0)\n", - "\n", - "# Normalize by row\n", - "row_sums = length_matrix.sum(axis=1, keepdims=True)\n", - "row_sums[row_sums == 0] = 1 # Avoid division by zero\n", - "length_matrix_norm = length_matrix / row_sums\n", - "\n", - "# Plot the question length vs answer choice matrix\n", - "plt.figure(figsize=(12, 8))\n", - "sns.heatmap(length_matrix_norm, annot=True, fmt='.2f', cmap='YlOrRd',\n", - " xticklabels=['A', 'B', 'C', 'D', 'E'],\n", - " yticklabels=unique_lengths)\n", - "plt.title('Answer Distribution by Question Length (Normalized)')\n", - "plt.xlabel('Answer Choice')\n", - "plt.ylabel('Question Length Group')\n", - "\n", - "# Add an explanation of the visualization\n", - "plt.figtext(0.5, 0.01, \n", - " 'This matrix shows how answer patterns vary by question length.\\n'\n", - " 'Differences between rows may indicate biases in how the model handles questions of different complexity.',\n", - " ha='center', fontsize=11, bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.8))\n", - "\n", - "plt.tight_layout(rect=[0, 0.05, 1, 0.95])\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernel_info": { - "name": "python38-azureml" - }, - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - }, - "microsoft": { - "host": { - "AzureML": { - "notebookHasBeenCompleted": true + ], + "metadata": { + "kernel_info": { + "name": "python3" + }, + "kernelspec": { + "name": "python3", + "language": "python", + "display_name": "Python 3 (ipykernel)" + }, + "language_info": { + "name": "python", + "version": "3.10.16", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "microsoft": { + "host": { + "AzureML": { + "notebookHasBeenCompleted": true + } + }, + "ms_spell_check": { + "ms_spell_check_language": "en" + } + }, + "nteract": { + "version": "nteract-front-end@1.0.0" } - }, - "ms_spell_check": { - "ms_spell_check_language": "en" - } }, - "nteract": { - "version": "nteract-front-end@1.0.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/Lab329/Notebook/02.AzureML_FineTuningAndConvertByMSOlive.ipynb b/Lab329/Notebook/02.AzureML_FineTuningAndConvertByMSOlive.ipynb index 5760193..997b9ee 100644 --- a/Lab329/Notebook/02.AzureML_FineTuningAndConvertByMSOlive.ipynb +++ b/Lab329/Notebook/02.AzureML_FineTuningAndConvertByMSOlive.ipynb @@ -1,516 +1,654 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "3bf944c2", - "metadata": {}, - "source": [ - "# Fine-tuning and Optimizing a Student Model\n", - "\n", - "This notebook demonstrates how to fine-tune a small \"student\" model using the training data we generated from our \"teacher\" model in the previous notebook. We'll also optimize the model for efficient deployment.\n", - "\n", - "![](../../lab_manual/images/step-2.png)\n", - "\n", - "## What You'll Learn\n", - "\n", - "- How to use Microsoft Olive to fine-tune the Phi-4-mini model\n", - "- How to apply Low-Rank Adaptation (LoRA) for efficient fine-tuning\n", - "- How to convert a model to ONNX format for optimization\n", - "- How to apply quantization to reduce model size\n", - "- How to prepare the model for deployment on resource-constrained environments\n", - "\n", - "## Prerequisites\n", - "\n", - "- Completed the previous notebook (`01.AzureML_Distillation.ipynb`)\n", - "- Generated training data in `data/train_data.jsonl`\n", - "- Access to Azure ML with the Phi-4-mini model in the registry\n", - "- Python environment with necessary libraries (which we'll install)\n", - "\n", - "## Setup Instructions\n", - "\n", - "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", - "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 PyTorch and Tensorflow\"** using the selector in the top right\n", - "3. **Environment File**: Ensure your `local.env` file exists with proper credentials" - ] - }, - { - "cell_type": "markdown", - "id": "6f080269", - "metadata": { - "vscode": { - "languageId": "plaintext" + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Fine-tuning and Optimizing a Student Model\n", + "\n", + "This notebook demonstrates how to fine-tune a small \"student\" model using the training data we generated from our \"teacher\" model in the previous notebook. We'll also optimize the model for efficient deployment.\n", + "\n", + "![](../../lab_manual/images/step-2.png)\n", + "\n", + "## What You'll Learn\n", + "\n", + "- How to use Microsoft Olive to fine-tune the Phi-4-mini model\n", + "- How to apply Low-Rank Adaptation (LoRA) for efficient fine-tuning\n", + "- How to convert a model to ONNX format for optimization\n", + "- How to apply quantization to reduce model size\n", + "- How to prepare the model for deployment on resource-constrained environments\n", + "\n", + "## Prerequisites\n", + "\n", + "- Completed the previous notebook (`01.AzureML_Distillation.ipynb`)\n", + "- Generated training data in `data/train_data.jsonl`\n", + "- Access to Azure ML with the Phi-4-mini model in the registry\n", + "- Python environment with necessary libraries (which we'll install)\n", + "\n", + "## Setup Instructions\n", + "\n", + "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", + "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 PyTorch and Tensorflow\"** using the selector in the top right\n", + "3. **Environment File**: Ensure your `local.env` file exists with proper credentials\n" + ], + "metadata": {}, + "id": "3bf944c2" + }, + { + "cell_type": "markdown", + "source": [ + "## Initial Setup\n", + "\n", + "Before we begin, make sure you've completed these steps:\n", + "\n", + "1. **Azure Login**: Run `az login --use-device-code` in a terminal to authenticate with Azure\n", + "\n", + "2. **Kernel Selection**: Select the \"Python 3.10 PyTorch and Tensorflow\" kernel from the dropdown in the top-right corner. This kernel has most of the dependencies we need pre-installed.\n", + "\n", + "3. **Check Environment**: Ensure your `local.env` file is in the same directory as this notebook" + ], + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "id": "6f080269" + }, + { + "cell_type": "markdown", + "source": [ + "## 1. Install Authentication Packages\n", + "\n", + "Here we install the packages needed to authenticate with Azure services:\n", + "\n", + "- **azure-ai-ml**: The Azure ML SDK for working with Azure Machine Learning\n", + "\n", + "The `-U` flag ensures we get the latest versions of these packages." + ], + "metadata": {}, + "id": "da5d712c" + }, + { + "cell_type": "code", + "source": [ + "# Install required packages for authentication\n", + "! pip install azure-ai-ml -U" + ], + "outputs": [], + "execution_count": 24, + "metadata": { + "gather": { + "logged": 1747774629310 + } + }, + "id": "2d617742" + }, + { + "cell_type": "markdown", + "source": [ + "## 2. Install PyTorch\n", + "\n", + "Here we install PyTorch, which is the deep learning framework we'll use for fine-tuning. This command installs\n", + " \n", + "- **torch**: The core PyTorch library for neural networks and tensor operations\n", + "- **torchvision**: For computer vision tasks (included as a dependency)\n", + "- **torchaudio**: For audio processing tasks (included as a dependency)\n", + " \n", + "We're installing from a specific URL (`download.pytorch.org/whl/cu124`) to get a version compatible with CUDA 12.4, which is optimized for modern NVIDIA GPUs. The `-U` flag ensures we get the latest version.\"" + ], + "metadata": {}, + "id": "68b941ef" + }, + { + "cell_type": "code", + "source": [ + "! pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Looking in indexes: https://download.pytorch.org/whl/cu124\nRequirement already satisfied: torch in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (2.6.0)\nCollecting torch\n Obtaining dependency information for torch from https://download.pytorch.org/whl/cu124/torch-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl.metadata\n Downloading https://download.pytorch.org/whl/cu124/torch-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl.metadata (28 kB)\nRequirement already satisfied: torchvision in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (0.21.0)\nCollecting torchvision\n Obtaining dependency information for torchvision from https://download.pytorch.org/whl/cu124/torchvision-0.21.0%2Bcu124-cp310-cp310-linux_x86_64.whl.metadata\n Downloading https://download.pytorch.org/whl/cu124/torchvision-0.21.0%2Bcu124-cp310-cp310-linux_x86_64.whl.metadata (6.1 kB)\nRequirement already satisfied: torchaudio in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (2.6.0)\nCollecting torchaudio\n Obtaining dependency information for torchaudio from https://download.pytorch.org/whl/cu124/torchaudio-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl.metadata\n Downloading https://download.pytorch.org/whl/cu124/torchaudio-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl.metadata (6.6 kB)\nRequirement already satisfied: nvidia-cudnn-cu12==9.1.0.70 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (9.1.0.70)\nRequirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.4.127)\nRequirement already satisfied: networkx in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (3.4)\nRequirement already satisfied: nvidia-cuda-cupti-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.4.127)\nRequirement already satisfied: nvidia-nccl-cu12==2.21.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (2.21.5)\nRequirement already satisfied: sympy==1.13.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (1.13.1)\nRequirement already satisfied: nvidia-curand-cu12==10.3.5.147 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (10.3.5.147)\nRequirement already satisfied: filelock in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (3.18.0)\nRequirement already satisfied: fsspec in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (2023.10.0)\nRequirement already satisfied: nvidia-cufft-cu12==11.2.1.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (11.2.1.3)\nRequirement already satisfied: nvidia-nvjitlink-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.4.127)\nRequirement already satisfied: nvidia-cusolver-cu12==11.6.1.9 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (11.6.1.9)\nRequirement already satisfied: nvidia-cusparse-cu12==12.3.1.170 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.3.1.170)\nRequirement already satisfied: nvidia-cuda-nvrtc-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.4.127)\nRequirement already satisfied: typing-extensions>=4.10.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (4.13.2)\nRequirement already satisfied: triton==3.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (3.2.0)\nRequirement already satisfied: jinja2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (3.1.6)\nRequirement already satisfied: nvidia-cuda-runtime-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.4.127)\nRequirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (0.6.2)\nRequirement already satisfied: nvidia-cublas-cu12==12.4.5.8 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch) (12.4.5.8)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy==1.13.1->torch) (1.3.0)\nRequirement already satisfied: numpy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torchvision) (1.23.5)\nRequirement already satisfied: pillow!=8.3.*,>=5.3.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torchvision) (9.2.0)\nRequirement already satisfied: MarkupSafe>=2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jinja2->torch) (2.0.1)\nDownloading https://download.pytorch.org/whl/cu124/torch-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl (768.4 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m768.4/768.4 MB\u001b[0m \u001b[31m1.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading https://download.pytorch.org/whl/cu124/torchvision-0.21.0%2Bcu124-cp310-cp310-linux_x86_64.whl (7.3 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.3/7.3 MB\u001b[0m \u001b[31m119.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading https://download.pytorch.org/whl/cu124/torchaudio-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl (3.4 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.4/3.4 MB\u001b[0m \u001b[31m92.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m\n\u001b[?25hUsing cached https://download.pytorch.org/whl/cu124/torch-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl (768.4 MB)\nUsing cached https://download.pytorch.org/whl/cu124/torchvision-0.21.0%2Bcu124-cp310-cp310-linux_x86_64.whl (7.3 MB)\nUsing cached https://download.pytorch.org/whl/cu124/torchaudio-2.6.0%2Bcu124-cp310-cp310-linux_x86_64.whl (3.4 MB)\nInstalling collected packages: torch, torchvision, torchaudio\n Attempting uninstall: torch\n Found existing installation: torch 2.6.0\n Uninstalling torch-2.6.0:\n Successfully uninstalled torch-2.6.0\n Attempting uninstall: torchvision\n Found existing installation: torchvision 0.21.0\n Uninstalling torchvision-0.21.0:\n Successfully uninstalled torchvision-0.21.0\n Attempting uninstall: torchaudio\n Found existing installation: torchaudio 2.6.0\n Uninstalling torchaudio-2.6.0:\n Successfully uninstalled torchaudio-2.6.0\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed torch-2.6.0+cu124 torchaudio-2.6.0+cu124 torchvision-0.21.0+cu124\n" + } + ], + "execution_count": 3, + "metadata": {}, + "id": "385440eb" + }, + { + "cell_type": "markdown", + "source": [ + "## 3. Install Microsoft Olive\n", + "\n", + "Now we install Microsoft Olive, an open-source model optimization toolkit that will be the main tool for our fine-tuning and optimization process. The `[auto-opt]` option includes additional dependencies for automatic optimization.\n", + "\n", + "Olive provides:\n", + "- Model fine-tuning capabilities\n", + "- ONNX conversion tools\n", + "- Quantization for model compression\n", + "- Performance optimization for various hardware targets\n", + "\n", + "This powerful tool will help us efficiently fine-tune our model and prepare it for deployment on resource-constrained devices." + ], + "metadata": {}, + "id": "43939308" + }, + { + "cell_type": "code", + "source": [ + "! pip install olive-ai[auto-opt] -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting olive-ai[auto-opt]\n Downloading olive_ai-0.9.1-py3-none-any.whl (688 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m688.8/688.8 kB\u001b[0m \u001b[31m11.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: onnx in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (1.17.0)\nCollecting onnxscript>=0.2.5\n Downloading onnxscript-0.2.5-py3-none-any.whl (689 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m689.4/689.4 kB\u001b[0m \u001b[31m41.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: numpy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (1.23.5)\nCollecting optuna\n Downloading optuna-4.3.0-py3-none-any.whl (386 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m386.6/386.6 kB\u001b[0m \u001b[31m33.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: pandas in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (1.5.3)\nRequirement already satisfied: transformers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (4.30.1)\nCollecting torchmetrics>=1.0.0\n Downloading torchmetrics-1.7.1-py3-none-any.whl (961 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m961.5/961.5 kB\u001b[0m \u001b[31m64.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: pydantic in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (2.9.2)\nRequirement already satisfied: torch in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (2.6.0+cu124)\nRequirement already satisfied: pyyaml in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from olive-ai[auto-opt]) (6.0.2)\nCollecting optimum\n Downloading optimum-1.25.3-py3-none-any.whl (429 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m429.3/429.3 kB\u001b[0m \u001b[31m43.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: typing_extensions>=4.10 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxscript>=0.2.5->olive-ai[auto-opt]) (4.13.2)\nRequirement already satisfied: ml_dtypes in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxscript>=0.2.5->olive-ai[auto-opt]) (0.5.1)\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxscript>=0.2.5->olive-ai[auto-opt]) (25.0)\nRequirement already satisfied: protobuf>=3.20.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnx->olive-ai[auto-opt]) (4.25.6)\nCollecting lightning-utilities>=0.8.0\n Downloading lightning_utilities-0.14.3-py3-none-any.whl (28 kB)\nRequirement already satisfied: jinja2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (3.1.6)\nRequirement already satisfied: nvidia-cusolver-cu12==11.6.1.9 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (11.6.1.9)\nRequirement already satisfied: sympy==1.13.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (1.13.1)\nRequirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.4.127)\nRequirement already satisfied: nvidia-cublas-cu12==12.4.5.8 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.4.5.8)\nRequirement already satisfied: nvidia-cuda-runtime-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.4.127)\nRequirement already satisfied: nvidia-cufft-cu12==11.2.1.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (11.2.1.3)\nRequirement already satisfied: nvidia-cudnn-cu12==9.1.0.70 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (9.1.0.70)\nRequirement already satisfied: nvidia-nccl-cu12==2.21.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (2.21.5)\nRequirement already satisfied: fsspec in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (2023.10.0)\nRequirement already satisfied: nvidia-curand-cu12==10.3.5.147 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (10.3.5.147)\nRequirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (0.6.2)\nRequirement already satisfied: nvidia-cuda-nvrtc-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.4.127)\nRequirement already satisfied: filelock in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (3.18.0)\nRequirement already satisfied: nvidia-cusparse-cu12==12.3.1.170 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.3.1.170)\nRequirement already satisfied: triton==3.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (3.2.0)\nRequirement already satisfied: nvidia-cuda-cupti-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.4.127)\nRequirement already satisfied: nvidia-nvjitlink-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (12.4.127)\nRequirement already satisfied: networkx in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch->olive-ai[auto-opt]) (3.4)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy==1.13.1->torch->olive-ai[auto-opt]) (1.3.0)\nRequirement already satisfied: huggingface-hub>=0.8.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from optimum->olive-ai[auto-opt]) (0.30.2)\nRequirement already satisfied: requests in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->olive-ai[auto-opt]) (2.32.3)\nRequirement already satisfied: tqdm>=4.27 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->olive-ai[auto-opt]) (4.67.1)\nRequirement already satisfied: regex!=2019.12.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->olive-ai[auto-opt]) (2024.11.6)\nRequirement already satisfied: safetensors>=0.3.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->olive-ai[auto-opt]) (0.5.3)\nRequirement already satisfied: tokenizers!=0.11.3,<0.14,>=0.11.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->olive-ai[auto-opt]) (0.13.3)\nCollecting sqlalchemy>=1.4.2\n Downloading sqlalchemy-2.0.41-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.2 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.2/3.2 MB\u001b[0m \u001b[31m78.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m\n\u001b[?25hCollecting alembic>=1.5.0\n Downloading alembic-1.15.2-py3-none-any.whl (231 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m231.9/231.9 kB\u001b[0m \u001b[31m20.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hCollecting colorlog\n Downloading colorlog-6.9.0-py3-none-any.whl (11 kB)\nRequirement already satisfied: python-dateutil>=2.8.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from pandas->olive-ai[auto-opt]) (2.9.0.post0)\nRequirement already satisfied: pytz>=2020.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from pandas->olive-ai[auto-opt]) (2022.5)\nRequirement already satisfied: annotated-types>=0.6.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from pydantic->olive-ai[auto-opt]) (0.7.0)\nRequirement already satisfied: pydantic-core==2.23.4 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from pydantic->olive-ai[auto-opt]) (2.23.4)\nCollecting Mako\n Downloading mako-1.3.10-py3-none-any.whl (78 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m78.5/78.5 kB\u001b[0m \u001b[31m12.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: setuptools in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from lightning-utilities>=0.8.0->torchmetrics>=1.0.0->olive-ai[auto-opt]) (75.8.0)\nRequirement already satisfied: six>=1.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from python-dateutil>=2.8.1->pandas->olive-ai[auto-opt]) (1.17.0)\nRequirement already satisfied: greenlet>=1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sqlalchemy>=1.4.2->optuna->olive-ai[auto-opt]) (3.2.0)\nRequirement already satisfied: MarkupSafe>=2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jinja2->torch->olive-ai[auto-opt]) (2.0.1)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers->olive-ai[auto-opt]) (2025.1.31)\nRequirement already satisfied: charset-normalizer<4,>=2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers->olive-ai[auto-opt]) (3.4.1)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers->olive-ai[auto-opt]) (1.26.20)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers->olive-ai[auto-opt]) (3.10)\nInstalling collected packages: sqlalchemy, Mako, lightning-utilities, colorlog, onnxscript, alembic, optuna, torchmetrics, optimum, olive-ai\nSuccessfully installed Mako-1.3.10 alembic-1.15.2 colorlog-6.9.0 lightning-utilities-0.14.3 olive-ai-0.9.1 onnxscript-0.2.5 optimum-1.25.3 optuna-4.3.0 sqlalchemy-2.0.41 torchmetrics-1.7.1\n" + } + ], + "execution_count": 4, + "metadata": {}, + "id": "89f3ef60" + }, + { + "cell_type": "markdown", + "source": [ + "## 4. Verify Olive Installation\n", + "\n", + "We'll now check the installed version of Olive to ensure it installed correctly. This command shows:\n", + "- The package name\n", + "- The installed version number\n", + "- Where the package is installed\n", + "- The package's dependencies\n", + "\n", + "Confirming the version is important as different versions of Olive may have different features or requirements." + ], + "metadata": {}, + "id": "77fc5c13" + }, + { + "cell_type": "code", + "source": [ + "! pip show olive-ai" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Name: olive-ai\r\nVersion: 0.9.1\r\nSummary: Olive: Simplify ML Model Finetuning, Conversion, Quantization, and Optimization for CPUs, GPUs and NPUs.\r\nHome-page: https://microsoft.github.io/Olive/\r\nAuthor: Microsoft Corporation\r\nAuthor-email: olivedevteam@microsoft.com\r\nLicense: MIT License\r\nLocation: /anaconda/envs/azureml_py38/lib/python3.10/site-packages\r\nRequires: numpy, onnx, onnxscript, optuna, pandas, pydantic, pyyaml, torch, torchmetrics, transformers\r\nRequired-by: \r\n" + } + ], + "execution_count": 5, + "metadata": {}, + "id": "e08355c2" + }, + { + "cell_type": "markdown", + "source": [ + "## 5. Install ONNX Runtime GenAI\n", + "\n", + "Next, we install ONNX Runtime GenAI, a specialized version of ONNX Runtime designed specifically for generative AI models. This package will allow us to:\n", + "\n", + "- Run our optimized model efficiently\n", + "- Leverage specialized optimizations for transformer models\n", + "- Access adapter-based fine-tuning capabilities\n", + "\n", + "We're installing version 0.7.1 with the `--pre` flag because it's a pre-release version with features we need for our work. Later notebooks will use this to run inference with our optimized model." + ], + "metadata": {}, + "id": "4c6a3bdf" + }, + { + "cell_type": "code", + "source": [ + "! pip install onnxruntime-genai==0.7.1 --pre" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting onnxruntime-genai==0.7.1\n Downloading onnxruntime_genai-0.7.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (1.7 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.7/1.7 MB\u001b[0m \u001b[31m18.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: numpy>=1.21.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime-genai==0.7.1) (1.23.5)\nCollecting onnxruntime>=1.21.0\n Downloading onnxruntime-1.22.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (16.4 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m16.4/16.4 MB\u001b[0m \u001b[31m76.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: protobuf in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (4.25.6)\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (25.0)\nRequirement already satisfied: sympy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (1.13.1)\nRequirement already satisfied: flatbuffers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (25.2.10)\nRequirement already satisfied: coloredlogs in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (15.0.1)\nRequirement already satisfied: humanfriendly>=9.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from coloredlogs->onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (10.0)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy->onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (1.3.0)\nInstalling collected packages: onnxruntime, onnxruntime-genai\n Attempting uninstall: onnxruntime\n Found existing installation: onnxruntime 1.17.3\n Uninstalling onnxruntime-1.17.3:\n Successfully uninstalled onnxruntime-1.17.3\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\nazureml-training-tabular 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.22.0 which is incompatible.\nazureml-training-tabular 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires scipy<1.11.0,>=1.0.0, but you have scipy 1.11.0 which is incompatible.\nazureml-train-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.22.0 which is incompatible.\nazureml-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.22.0 which is incompatible.\nazureml-automl-runtime 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed onnxruntime-1.22.0 onnxruntime-genai-0.7.1\n" + } + ], + "execution_count": 6, + "metadata": { + "gather": { + "logged": 1744963274226 + } + }, + "id": "69907a09" + }, + { + "cell_type": "code", + "source": [ + "! pip install onnxruntime==1.21.1 -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting onnxruntime==1.21.1\n Downloading onnxruntime-1.21.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (16.0 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m16.0/16.0 MB\u001b[0m \u001b[31m73.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: coloredlogs in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (15.0.1)\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (25.0)\nRequirement already satisfied: protobuf in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (4.25.6)\nRequirement already satisfied: numpy>=1.21.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (1.23.5)\nRequirement already satisfied: flatbuffers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (25.2.10)\nRequirement already satisfied: sympy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (1.13.1)\nRequirement already satisfied: humanfriendly>=9.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from coloredlogs->onnxruntime==1.21.1) (10.0)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy->onnxruntime==1.21.1) (1.3.0)\nInstalling collected packages: onnxruntime\n Attempting uninstall: onnxruntime\n Found existing installation: onnxruntime 1.22.0\n Uninstalling onnxruntime-1.22.0:\n Successfully uninstalled onnxruntime-1.22.0\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\nazureml-training-tabular 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires scipy<1.11.0,>=1.0.0, but you have scipy 1.11.0 which is incompatible.\nazureml-train-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed onnxruntime-1.21.1\n" + } + ], + "execution_count": 7, + "metadata": {}, + "id": "2976cf9b" + }, + { + "cell_type": "markdown", + "source": [ + "## 6. Package Management\n", + "\n", + "The next few cells handle package management to avoid conflicts. We're:\n", + "\n", + "1. **Uninstalling onnxruntime-gpu** to avoid conflicts with the regular onnxruntime package\n", + "2. **Installing regular onnxruntime** for CPU-based inference\n", + "3. **Installing additional dependencies** including:\n", + " - bitsandbytes: For efficient quantization\n", + " - transformers: For working with transformer models\n", + " - peft: For parameter-efficient fine-tuning (LoRA)\n", + " - accelerate: For optimized training\n", + "\n", + "These packages will ensure our environment is properly set up for fine-tuning and optimization." + ], + "metadata": {}, + "id": "f97d1dc8" + }, + { + "cell_type": "code", + "source": [ + "pip uninstall onnxruntime-gpu --yes" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "\u001b[33mWARNING: Skipping onnxruntime-gpu as it is not installed.\u001b[0m\u001b[33m\r\n\u001b[0mNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 8, + "metadata": { + "gather": { + "logged": 1747773343625 + } + }, + "id": "ba394391" + }, + { + "cell_type": "code", + "source": [ + "! pip install onnxruntime" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: onnxruntime in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (1.21.1)\r\nRequirement already satisfied: sympy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime) (1.13.1)\r\nRequirement already satisfied: flatbuffers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime) (25.2.10)\r\nRequirement already satisfied: coloredlogs in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime) (15.0.1)\r\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime) (25.0)\r\nRequirement already satisfied: protobuf in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime) (4.25.6)\r\nRequirement already satisfied: numpy>=1.21.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime) (1.23.5)\r\nRequirement already satisfied: humanfriendly>=9.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from coloredlogs->onnxruntime) (10.0)\r\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy->onnxruntime) (1.3.0)\r\n" + } + ], + "execution_count": 9, + "metadata": {}, + "id": "0cdfae44" + }, + { + "cell_type": "code", + "source": [ + "! pip install bitsandbytes" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting bitsandbytes\n Downloading bitsandbytes-0.45.5-py3-none-manylinux_2_24_x86_64.whl (76.1 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.1/76.1 MB\u001b[0m \u001b[31m11.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: numpy>=1.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from bitsandbytes) (1.23.5)\nRequirement already satisfied: torch<3,>=2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from bitsandbytes) (2.6.0+cu124)\nRequirement already satisfied: networkx in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (3.4)\nRequirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (0.6.2)\nRequirement already satisfied: sympy==1.13.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (1.13.1)\nRequirement already satisfied: nvidia-curand-cu12==10.3.5.147 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (10.3.5.147)\nRequirement already satisfied: nvidia-nccl-cu12==2.21.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (2.21.5)\nRequirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.4.127)\nRequirement already satisfied: nvidia-cublas-cu12==12.4.5.8 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.4.5.8)\nRequirement already satisfied: triton==3.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (3.2.0)\nRequirement already satisfied: nvidia-cufft-cu12==11.2.1.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (11.2.1.3)\nRequirement already satisfied: jinja2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (3.1.6)\nRequirement already satisfied: nvidia-nvjitlink-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.4.127)\nRequirement already satisfied: nvidia-cusparse-cu12==12.3.1.170 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.3.1.170)\nRequirement already satisfied: filelock in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (3.18.0)\nRequirement already satisfied: nvidia-cuda-runtime-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.4.127)\nRequirement already satisfied: nvidia-cusolver-cu12==11.6.1.9 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (11.6.1.9)\nRequirement already satisfied: fsspec in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (2023.10.0)\nRequirement already satisfied: nvidia-cuda-nvrtc-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.4.127)\nRequirement already satisfied: nvidia-cuda-cupti-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (12.4.127)\nRequirement already satisfied: nvidia-cudnn-cu12==9.1.0.70 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (9.1.0.70)\nRequirement already satisfied: typing-extensions>=4.10.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch<3,>=2.0->bitsandbytes) (4.13.2)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy==1.13.1->torch<3,>=2.0->bitsandbytes) (1.3.0)\nRequirement already satisfied: MarkupSafe>=2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jinja2->torch<3,>=2.0->bitsandbytes) (2.0.1)\nInstalling collected packages: bitsandbytes\nSuccessfully installed bitsandbytes-0.45.5\n" + } + ], + "execution_count": 10, + "metadata": {}, + "id": "67c74e5e" + }, + { + "cell_type": "code", + "source": [ + "! pip install transformers==4.49.0 -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting transformers==4.49.0\n Downloading transformers-4.49.0-py3-none-any.whl (10.0 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.0/10.0 MB\u001b[0m \u001b[31m55.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m0:01\u001b[0m\n\u001b[?25hCollecting tokenizers<0.22,>=0.21\n Downloading tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.0/3.0 MB\u001b[0m \u001b[31m121.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: packaging>=20.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (25.0)\nRequirement already satisfied: safetensors>=0.4.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (0.5.3)\nRequirement already satisfied: tqdm>=4.27 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (4.67.1)\nRequirement already satisfied: filelock in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (3.18.0)\nRequirement already satisfied: numpy>=1.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (1.23.5)\nRequirement already satisfied: requests in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (2.32.3)\nRequirement already satisfied: huggingface-hub<1.0,>=0.26.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (0.30.2)\nRequirement already satisfied: regex!=2019.12.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (2024.11.6)\nRequirement already satisfied: pyyaml>=5.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers==4.49.0) (6.0.2)\nRequirement already satisfied: fsspec>=2023.5.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface-hub<1.0,>=0.26.0->transformers==4.49.0) (2023.10.0)\nRequirement already satisfied: typing-extensions>=3.7.4.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface-hub<1.0,>=0.26.0->transformers==4.49.0) (4.13.2)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers==4.49.0) (3.10)\nRequirement already satisfied: charset-normalizer<4,>=2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers==4.49.0) (3.4.1)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers==4.49.0) (2025.1.31)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->transformers==4.49.0) (1.26.20)\nInstalling collected packages: tokenizers, transformers\n Attempting uninstall: tokenizers\n Found existing installation: tokenizers 0.13.3\n Uninstalling tokenizers-0.13.3:\n Successfully uninstalled tokenizers-0.13.3\n Attempting uninstall: transformers\n Found existing installation: transformers 4.30.1\n Uninstalling transformers-4.30.1:\n Successfully uninstalled transformers-4.30.1\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires transformers[sentencepiece,torch]<=4.48.0, but you have transformers 4.49.0 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed tokenizers-0.21.1 transformers-4.49.0\n" + } + ], + "execution_count": 11, + "metadata": {}, + "id": "96b0305c" + }, + { + "cell_type": "code", + "source": [ + "! pip install azure-ai-ml -U " + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: azure-ai-ml in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (1.27.1)\nRequirement already satisfied: azure-storage-file-share in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (12.21.0)\nRequirement already satisfied: azure-monitor-opentelemetry in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (1.6.9)\nRequirement already satisfied: azure-storage-blob>=12.10.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (12.25.1)\nRequirement already satisfied: azure-storage-file-datalake>=12.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (12.20.0)\nRequirement already satisfied: marshmallow<4.0.0,>=3.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (3.26.1)\nRequirement already satisfied: colorama<1.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (0.4.6)\nRequirement already satisfied: strictyaml<2.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (1.7.3)\nRequirement already satisfied: isodate<1.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (0.7.2)\nRequirement already satisfied: six>=1.11.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (1.17.0)\nRequirement already satisfied: pyjwt<3.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (2.4.0)\nRequirement already satisfied: azure-common>=1.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (1.1.28)\nRequirement already satisfied: msrest<1.0.0,>=0.6.18 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (0.7.1)\nRequirement already satisfied: jsonschema<5.0.0,>=4.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (4.23.0)\nRequirement already satisfied: tqdm<5.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (4.67.1)\nRequirement already satisfied: typing-extensions<5.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (4.13.2)\nRequirement already satisfied: pyyaml<7.0.0,>=5.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (6.0.2)\nRequirement already satisfied: pydash<9.0.0,>=6.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (8.0.5)\nRequirement already satisfied: azure-mgmt-core>=1.3.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (1.5.0)\nRequirement already satisfied: azure-core>=1.23.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-ai-ml) (1.33.0)\nRequirement already satisfied: requests>=2.21.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-core>=1.23.0->azure-ai-ml) (2.32.3)\nRequirement already satisfied: cryptography>=2.1.4 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-storage-blob>=12.10.0->azure-ai-ml) (38.0.4)\nRequirement already satisfied: rpds-py>=0.7.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jsonschema<5.0.0,>=4.0.0->azure-ai-ml) (0.22.3)\nRequirement already satisfied: referencing>=0.28.4 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jsonschema<5.0.0,>=4.0.0->azure-ai-ml) (0.36.2)\nRequirement already satisfied: attrs>=22.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jsonschema<5.0.0,>=4.0.0->azure-ai-ml) (25.3.0)\nRequirement already satisfied: jsonschema-specifications>=2023.03.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jsonschema<5.0.0,>=4.0.0->azure-ai-ml) (2024.10.1)\nRequirement already satisfied: packaging>=17.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from marshmallow<4.0.0,>=3.5->azure-ai-ml) (25.0)\nRequirement already satisfied: requests-oauthlib>=0.5.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from msrest<1.0.0,>=0.6.18->azure-ai-ml) (2.0.0)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from msrest<1.0.0,>=0.6.18->azure-ai-ml) (2025.1.31)\nRequirement already satisfied: python-dateutil>=2.6.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from strictyaml<2.0.0->azure-ai-ml) (2.9.0.post0)\nRequirement already satisfied: opentelemetry-instrumentation-flask<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-instrumentation-fastapi<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-instrumentation-requests<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-instrumentation-urllib<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-instrumentation-urllib3<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: azure-core-tracing-opentelemetry~=1.0.0b11 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (1.0.0b12)\nRequirement already satisfied: opentelemetry-instrumentation-psycopg2<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-resource-detector-azure~=0.1.4 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.1.5)\nRequirement already satisfied: azure-monitor-opentelemetry-exporter~=1.0.0b31 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (1.0.0b36)\nRequirement already satisfied: opentelemetry-instrumentation-django<0.53b0,>=0.49b0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-sdk<1.32,>=1.28.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry->azure-ai-ml) (1.31.1)\nRequirement already satisfied: opentelemetry-api>=1.12.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-core-tracing-opentelemetry~=1.0.0b11->azure-monitor-opentelemetry->azure-ai-ml) (1.31.1)\nRequirement already satisfied: psutil<7,>=5.9 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry-exporter~=1.0.0b31->azure-monitor-opentelemetry->azure-ai-ml) (6.1.1)\nRequirement already satisfied: fixedint==0.1.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry-exporter~=1.0.0b31->azure-monitor-opentelemetry->azure-ai-ml) (0.1.6)\nRequirement already satisfied: azure-identity~=1.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-monitor-opentelemetry-exporter~=1.0.0b31->azure-monitor-opentelemetry->azure-ai-ml) (1.21.0)\nRequirement already satisfied: cffi>=1.12 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from cryptography>=2.1.4->azure-storage-blob>=12.10.0->azure-ai-ml) (1.17.1)\nRequirement already satisfied: opentelemetry-util-http==0.52b1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-django<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-instrumentation==0.52b1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-django<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-instrumentation-wsgi==0.52b1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-django<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: opentelemetry-semantic-conventions==0.52b1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-django<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: wrapt<2.0.0,>=1.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation==0.52b1->opentelemetry-instrumentation-django<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (1.14.1)\nRequirement already satisfied: deprecated>=1.2.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-semantic-conventions==0.52b1->opentelemetry-instrumentation-django<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (1.2.18)\nRequirement already satisfied: importlib-metadata<8.7.0,>=6.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-api>=1.12.0->azure-core-tracing-opentelemetry~=1.0.0b11->azure-monitor-opentelemetry->azure-ai-ml) (8.2.0)\nRequirement already satisfied: opentelemetry-instrumentation-asgi==0.52b1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-fastapi<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: asgiref~=3.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-asgi==0.52b1->opentelemetry-instrumentation-fastapi<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (3.8.1)\nRequirement already satisfied: opentelemetry-instrumentation-dbapi==0.52b1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from opentelemetry-instrumentation-psycopg2<0.53b0,>=0.49b0->azure-monitor-opentelemetry->azure-ai-ml) (0.52b1)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.23.0->azure-ai-ml) (3.10)\nRequirement already satisfied: charset-normalizer<4,>=2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.23.0->azure-ai-ml) (3.4.1)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests>=2.21.0->azure-core>=1.23.0->azure-ai-ml) (1.26.20)\nRequirement already satisfied: oauthlib>=3.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests-oauthlib>=0.5.0->msrest<1.0.0,>=0.6.18->azure-ai-ml) (3.2.2)\nRequirement already satisfied: msal>=1.30.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-identity~=1.17->azure-monitor-opentelemetry-exporter~=1.0.0b31->azure-monitor-opentelemetry->azure-ai-ml) (1.31.2b1)\nRequirement already satisfied: msal-extensions>=1.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from azure-identity~=1.17->azure-monitor-opentelemetry-exporter~=1.0.0b31->azure-monitor-opentelemetry->azure-ai-ml) (1.2.0)\nRequirement already satisfied: pycparser in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from cffi>=1.12->cryptography>=2.1.4->azure-storage-blob>=12.10.0->azure-ai-ml) (2.22)\nRequirement already satisfied: zipp>=0.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from importlib-metadata<8.7.0,>=6.0->opentelemetry-api>=1.12.0->azure-core-tracing-opentelemetry~=1.0.0b11->azure-monitor-opentelemetry->azure-ai-ml) (3.12.0)\nRequirement already satisfied: portalocker<3,>=1.4 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from msal-extensions>=1.2.0->azure-identity~=1.17->azure-monitor-opentelemetry-exporter~=1.0.0b31->azure-monitor-opentelemetry->azure-ai-ml) (2.10.1)\n" + } + ], + "execution_count": 12, + "metadata": {}, + "id": "fd93b0a9" + }, + { + "cell_type": "code", + "source": [ + "! pip install marshmallow==3.23.2 -U " + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting marshmallow==3.23.2\n Downloading marshmallow-3.23.2-py3-none-any.whl (49 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.3/49.3 kB\u001b[0m \u001b[31m2.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hRequirement already satisfied: packaging>=17.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from marshmallow==3.23.2) (25.0)\nInstalling collected packages: marshmallow\n Attempting uninstall: marshmallow\n Found existing installation: marshmallow 3.26.1\n Uninstalling marshmallow-3.26.1:\n Successfully uninstalled marshmallow-3.26.1\nSuccessfully installed marshmallow-3.23.2\n" + } + ], + "execution_count": 13, + "metadata": {}, + "id": "66fb7020" + }, + { + "cell_type": "code", + "source": [ + "! pip install tf-keras" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting tf-keras\n Downloading tf_keras-2.19.0-py3-none-any.whl (1.7 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.7/1.7 MB\u001b[0m \u001b[31m22.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m0:01\u001b[0m\n\u001b[?25hRequirement already satisfied: tensorflow<2.20,>=2.19 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tf-keras) (2.19.0)\nRequirement already satisfied: gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (0.6.0)\nRequirement already satisfied: absl-py>=1.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (2.2.2)\nRequirement already satisfied: opt-einsum>=2.3.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (3.4.0)\nRequirement already satisfied: google-pasta>=0.1.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (0.2.0)\nRequirement already satisfied: termcolor>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (3.0.1)\nRequirement already satisfied: wrapt>=1.11.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (1.14.1)\nRequirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev,>=3.20.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (4.25.6)\nRequirement already satisfied: flatbuffers>=24.3.25 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (25.2.10)\nRequirement already satisfied: astunparse>=1.6.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (1.6.3)\nRequirement already satisfied: requests<3,>=2.21.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (2.32.3)\nRequirement already satisfied: typing-extensions>=3.6.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (4.13.2)\nRequirement already satisfied: libclang>=13.0.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (18.1.1)\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (25.0)\nRequirement already satisfied: keras>=3.5.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (3.9.2)\nRequirement already satisfied: tensorflow-io-gcs-filesystem>=0.23.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (0.37.1)\nRequirement already satisfied: six>=1.12.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (1.17.0)\nCollecting numpy<2.2.0,>=1.26.0\n Downloading numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.3 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m16.3/16.3 MB\u001b[0m \u001b[31m80.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: ml-dtypes<1.0.0,>=0.5.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (0.5.1)\nRequirement already satisfied: setuptools in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (75.8.0)\nRequirement already satisfied: h5py>=3.11.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (3.13.0)\nRequirement already satisfied: tensorboard~=2.19.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (2.19.0)\nRequirement already satisfied: grpcio<2.0,>=1.24.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorflow<2.20,>=2.19->tf-keras) (1.71.0)\nRequirement already satisfied: wheel<1.0,>=0.23.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from astunparse>=1.6.0->tensorflow<2.20,>=2.19->tf-keras) (0.45.1)\nRequirement already satisfied: namex in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from keras>=3.5.0->tensorflow<2.20,>=2.19->tf-keras) (0.0.8)\nRequirement already satisfied: optree in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from keras>=3.5.0->tensorflow<2.20,>=2.19->tf-keras) (0.15.0)\nRequirement already satisfied: rich in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from keras>=3.5.0->tensorflow<2.20,>=2.19->tf-keras) (14.0.0)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests<3,>=2.21.0->tensorflow<2.20,>=2.19->tf-keras) (1.26.20)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests<3,>=2.21.0->tensorflow<2.20,>=2.19->tf-keras) (2025.1.31)\nRequirement already satisfied: charset-normalizer<4,>=2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests<3,>=2.21.0->tensorflow<2.20,>=2.19->tf-keras) (3.4.1)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests<3,>=2.21.0->tensorflow<2.20,>=2.19->tf-keras) (3.10)\nRequirement already satisfied: markdown>=2.6.8 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorboard~=2.19.0->tensorflow<2.20,>=2.19->tf-keras) (3.8)\nRequirement already satisfied: werkzeug>=1.0.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorboard~=2.19.0->tensorflow<2.20,>=2.19->tf-keras) (3.1.3)\nRequirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from tensorboard~=2.19.0->tensorflow<2.20,>=2.19->tf-keras) (0.7.2)\nCollecting MarkupSafe>=2.1.1\n Downloading MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (20 kB)\nRequirement already satisfied: pygments<3.0.0,>=2.13.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from rich->keras>=3.5.0->tensorflow<2.20,>=2.19->tf-keras) (2.19.1)\nRequirement already satisfied: markdown-it-py>=2.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from rich->keras>=3.5.0->tensorflow<2.20,>=2.19->tf-keras) (3.0.0)\nRequirement already satisfied: mdurl~=0.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from markdown-it-py>=2.2.0->rich->keras>=3.5.0->tensorflow<2.20,>=2.19->tf-keras) (0.1.2)\nInstalling collected packages: numpy, MarkupSafe, tf-keras\n Attempting uninstall: numpy\n Found existing installation: numpy 1.23.5\n Uninstalling numpy-1.23.5:\n Successfully uninstalled numpy-1.23.5\n Attempting uninstall: MarkupSafe\n Found existing installation: MarkupSafe 2.0.1\n Uninstalling MarkupSafe-2.0.1:\n Successfully uninstalled MarkupSafe-2.0.1\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\npandas-ml 0.6.1 requires enum34, which is not installed.\nthinc 8.2.5 requires numpy<2.0.0,>=1.19.0; python_version >= \"3.9\", but you have numpy 2.1.3 which is incompatible.\nscipy 1.11.0 requires numpy<1.28.0,>=1.21.6, but you have numpy 2.1.3 which is incompatible.\nscikit-image 0.25.0 requires pillow>=10.1, but you have pillow 9.2.0 which is incompatible.\nscikit-image 0.25.0 requires scipy>=1.11.2, but you have scipy 1.11.0 which is incompatible.\nresponsibleai 0.36.0 requires networkx<=2.5, but you have networkx 3.4 which is incompatible.\nresponsibleai 0.36.0 requires numpy<=1.26.2,>=1.17.2, but you have numpy 2.1.3 which is incompatible.\nraiwidgets 0.36.0 requires numpy<=1.26.2,>=1.17.2, but you have numpy 2.1.3 which is incompatible.\nnumba 0.56.4 requires numpy<1.24,>=1.18, but you have numpy 2.1.3 which is incompatible.\nml-wrappers 0.5.6 requires numpy<2.0.0, but you have numpy 2.1.3 which is incompatible.\njupyter-resource-usage 0.7.2 requires psutil~=5.6, but you have psutil 6.1.1 which is incompatible.\nerroranalysis 0.5.5 requires numpy<2.0.0,>=1.17.2, but you have numpy 2.1.3 which is incompatible.\neconml 0.15.1 requires numpy<2, but you have numpy 2.1.3 which is incompatible.\ndask-sql 2024.5.0 requires dask[dataframe]>=2024.4.1, but you have dask 2023.2.0 which is incompatible.\ndask-sql 2024.5.0 requires distributed>=2024.4.1, but you have distributed 2023.2.0 which is incompatible.\ndask-expr 1.1.10 requires dask==2024.8.0, but you have dask 2023.2.0 which is incompatible.\ndask-expr 1.1.10 requires pandas>=2, but you have pandas 1.5.3 which is incompatible.\nazureml-training-tabular 1.60.0 requires numpy<=1.23.5,>=1.16.0; python_version >= \"3.8\", but you have numpy 2.1.3 which is incompatible.\nazureml-training-tabular 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires scipy<1.11.0,>=1.0.0, but you have scipy 1.11.0 which is incompatible.\nazureml-train-automl-runtime 1.60.0 requires numpy<=1.23.5,>=1.16.0; python_version >= \"3.8\", but you have numpy 2.1.3 which is incompatible.\nazureml-train-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-opendatasets 1.60.0 requires numpy<=2.0.0,>=1.16.0, but you have numpy 2.1.3 which is incompatible.\nazureml-interpret 1.60.0 requires numpy<=1.23.5; python_version >= \"3.8\", but you have numpy 2.1.3 which is incompatible.\nazureml-dataset-runtime 1.60.0 requires numpy!=1.19.3,<1.24; sys_platform == \"linux\", but you have numpy 2.1.3 which is incompatible.\nazureml-automl-runtime 1.60.0 requires numpy<=1.23.5,>=1.16.0; python_version >= \"3.8\", but you have numpy 2.1.3 which is incompatible.\nazureml-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires transformers[sentencepiece,torch]<=4.48.0, but you have transformers 4.49.0 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed MarkupSafe-3.0.2 numpy-2.1.3 tf-keras-2.19.0\n" + } + ], + "execution_count": 14, + "metadata": {}, + "id": "45b21c43" + }, + { + "cell_type": "code", + "source": [ + "! pip install numpy==1.23.5 -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting numpy==1.23.5\n Downloading numpy-1.23.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.1 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m17.1/17.1 MB\u001b[0m \u001b[31m70.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hInstalling collected packages: numpy\n Attempting uninstall: numpy\n Found existing installation: numpy 2.1.3\n Uninstalling numpy-2.1.3:\n Successfully uninstalled numpy-2.1.3\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\npandas-ml 0.6.1 requires enum34, which is not installed.\ntensorflow 2.19.0 requires numpy<2.2.0,>=1.26.0, but you have numpy 1.23.5 which is incompatible.\nscikit-image 0.25.0 requires numpy>=1.24, but you have numpy 1.23.5 which is incompatible.\nscikit-image 0.25.0 requires pillow>=10.1, but you have pillow 9.2.0 which is incompatible.\nscikit-image 0.25.0 requires scipy>=1.11.2, but you have scipy 1.11.0 which is incompatible.\nresponsibleai 0.36.0 requires networkx<=2.5, but you have networkx 3.4 which is incompatible.\ndask-sql 2024.5.0 requires dask[dataframe]>=2024.4.1, but you have dask 2023.2.0 which is incompatible.\ndask-sql 2024.5.0 requires distributed>=2024.4.1, but you have distributed 2023.2.0 which is incompatible.\ndask-expr 1.1.10 requires dask==2024.8.0, but you have dask 2023.2.0 which is incompatible.\ndask-expr 1.1.10 requires pandas>=2, but you have pandas 1.5.3 which is incompatible.\nazureml-training-tabular 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires scipy<1.11.0,>=1.0.0, but you have scipy 1.11.0 which is incompatible.\nazureml-train-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires transformers[sentencepiece,torch]<=4.48.0, but you have transformers 4.49.0 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed numpy-1.23.5\n" + } + ], + "execution_count": 15, + "metadata": {}, + "id": "8207d88c" + }, + { + "cell_type": "code", + "source": [ + "! pip install peft" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting peft\n Downloading peft-0.15.2-py3-none-any.whl (411 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m411.1/411.1 kB\u001b[0m \u001b[31m8.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n\u001b[?25hRequirement already satisfied: psutil in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (6.1.1)\nRequirement already satisfied: packaging>=20.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (25.0)\nRequirement already satisfied: safetensors in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (0.5.3)\nRequirement already satisfied: torch>=1.13.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (2.6.0+cu124)\nRequirement already satisfied: accelerate>=0.21.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (0.34.2)\nRequirement already satisfied: huggingface_hub>=0.25.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (0.30.2)\nRequirement already satisfied: numpy>=1.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (1.23.5)\nRequirement already satisfied: transformers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (4.49.0)\nRequirement already satisfied: tqdm in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (4.67.1)\nRequirement already satisfied: pyyaml in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (6.0.2)\nRequirement already satisfied: requests in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (2.32.3)\nRequirement already satisfied: fsspec>=2023.5.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (2023.10.0)\nRequirement already satisfied: typing-extensions>=3.7.4.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (4.13.2)\nRequirement already satisfied: filelock in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (3.18.0)\nRequirement already satisfied: nvidia-nvjitlink-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (0.6.2)\nRequirement already satisfied: sympy==1.13.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (1.13.1)\nRequirement already satisfied: jinja2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (3.1.6)\nRequirement already satisfied: nvidia-curand-cu12==10.3.5.147 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (10.3.5.147)\nRequirement already satisfied: nvidia-cuda-runtime-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: triton==3.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (3.2.0)\nRequirement already satisfied: nvidia-cuda-cupti-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: nvidia-cusparse-cu12==12.3.1.170 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.3.1.170)\nRequirement already satisfied: nvidia-nccl-cu12==2.21.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (2.21.5)\nRequirement already satisfied: nvidia-cuda-nvrtc-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: networkx in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (3.4)\nRequirement already satisfied: nvidia-cudnn-cu12==9.1.0.70 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (9.1.0.70)\nRequirement already satisfied: nvidia-cufft-cu12==11.2.1.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (11.2.1.3)\nRequirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: nvidia-cusolver-cu12==11.6.1.9 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (11.6.1.9)\nRequirement already satisfied: nvidia-cublas-cu12==12.4.5.8 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.5.8)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy==1.13.1->torch>=1.13.0->peft) (1.3.0)\nRequirement already satisfied: tokenizers<0.22,>=0.21 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->peft) (0.21.1)\nRequirement already satisfied: regex!=2019.12.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->peft) (2024.11.6)\nRequirement already satisfied: MarkupSafe>=2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jinja2->torch>=1.13.0->peft) (3.0.2)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (2025.1.31)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (1.26.20)\nRequirement already satisfied: charset-normalizer<4,>=2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (3.4.1)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (3.10)\nInstalling collected packages: peft\nSuccessfully installed peft-0.15.2\n" + } + ], + "execution_count": 16, + "metadata": {}, + "id": "165e19c3" + }, + { + "cell_type": "code", + "source": [ + "! pip list" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Package Version\r\n------------------------------------------ --------------\r\nabsl-py 2.2.2\r\naccelerate 0.34.2\r\nadal 1.2.7\r\nadlfs 2024.12.0\r\naiofiles 22.1.0\r\naiohappyeyeballs 2.6.1\r\naiohttp 3.11.16\r\naiohttp-cors 0.8.1\r\naiosignal 1.3.2\r\naiosqlite 0.21.0\r\nalembic 1.15.2\r\nannotated-types 0.7.0\r\nansicolors 1.1.8\r\nantlr4-python3-runtime 4.13.2\r\nanyio 4.9.0\r\napplicationinsights 0.11.10\r\narch 5.6.0\r\nargcomplete 3.5.3\r\nargon2-cffi 23.1.0\r\nargon2-cffi-bindings 21.2.0\r\narrow 1.3.0\r\nasgiref 3.8.1\r\nastroid 3.3.9\r\nasttokens 3.0.0\r\nastunparse 1.6.3\r\nasync-lru 2.0.5\r\nasync-timeout 5.0.1\r\nattrs 25.3.0\r\nautokeras 2.0.0\r\nautopep8 1.5.5\r\nazure-ai-ml 1.27.1\r\nazure-appconfiguration 1.7.1\r\nazure-batch 15.0.0b2\r\nazure-cli 2.71.0\r\nazure-cli-core 2.71.0\r\nazure-cli-telemetry 1.1.0\r\nazure-common 1.1.28\r\nazure-core 1.33.0\r\nazure-core-tracing-opentelemetry 1.0.0b12\r\nazure-cosmos 3.2.0\r\nazure-data-tables 12.4.0\r\nazure-datalake-store 1.0.0a0\r\nazure-graphrbac 0.60.0\r\nazure-identity 1.21.0\r\nazure-keyvault-administration 4.4.0b2\r\nazure-keyvault-certificates 4.7.0\r\nazure-keyvault-keys 4.8.0\r\nazure-keyvault-secrets 4.7.0\r\nazure-mgmt-advisor 9.0.0\r\nazure-mgmt-apimanagement 4.0.0\r\nazure-mgmt-appconfiguration 3.1.0\r\nazure-mgmt-appcontainers 2.0.0\r\nazure-mgmt-applicationinsights 1.0.0\r\nazure-mgmt-authorization 4.0.0\r\nazure-mgmt-batch 17.3.0\r\nazure-mgmt-batchai 7.0.0b1\r\nazure-mgmt-billing 6.0.0\r\nazure-mgmt-botservice 2.0.0\r\nazure-mgmt-cdn 12.0.0\r\nazure-mgmt-cognitiveservices 13.5.0\r\nazure-mgmt-compute 33.0.0\r\nazure-mgmt-containerinstance 10.2.0b1\r\nazure-mgmt-containerregistry 10.3.0\r\nazure-mgmt-containerservice 34.2.0\r\nazure-mgmt-core 1.5.0\r\nazure-mgmt-cosmosdb 9.7.0\r\nazure-mgmt-databoxedge 1.0.0\r\nazure-mgmt-datalake-store 1.1.0b1\r\nazure-mgmt-datamigration 10.0.0\r\nazure-mgmt-devtestlabs 4.0.0\r\nazure-mgmt-dns 8.0.0\r\nazure-mgmt-eventgrid 10.2.0b2\r\nazure-mgmt-eventhub 10.1.0\r\nazure-mgmt-extendedlocation 1.0.0b2\r\nazure-mgmt-hdinsight 9.0.0b3\r\nazure-mgmt-imagebuilder 1.3.0\r\nazure-mgmt-iotcentral 10.0.0b2\r\nazure-mgmt-iothub 3.0.0\r\nazure-mgmt-iothubprovisioningservices 1.1.0\r\nazure-mgmt-keyvault 10.3.1\r\nazure-mgmt-kusto 0.3.0\r\nazure-mgmt-loganalytics 13.0.0b4\r\nazure-mgmt-managedservices 1.0.0\r\nazure-mgmt-managementgroups 1.0.0\r\nazure-mgmt-maps 2.0.0\r\nazure-mgmt-marketplaceordering 1.1.0\r\nazure-mgmt-media 9.0.0\r\nazure-mgmt-monitor 5.0.1\r\nazure-mgmt-msi 7.0.0\r\nazure-mgmt-mysqlflexibleservers 1.0.0b3\r\nazure-mgmt-netapp 10.1.0\r\nazure-mgmt-network 26.0.0\r\nazure-mgmt-policyinsights 1.1.0b4\r\nazure-mgmt-postgresqlflexibleservers 1.1.0b2\r\nazure-mgmt-privatedns 1.0.0\r\nazure-mgmt-rdbms 10.2.0b17\r\nazure-mgmt-recoveryservices 3.0.0\r\nazure-mgmt-recoveryservicesbackup 9.1.0\r\nazure-mgmt-redhatopenshift 1.5.0\r\nazure-mgmt-redis 14.5.0\r\nazure-mgmt-resource 23.1.1\r\nazure-mgmt-search 9.1.0\r\nazure-mgmt-security 6.0.0\r\nazure-mgmt-servicebus 8.2.1\r\nazure-mgmt-servicefabric 2.1.0\r\nazure-mgmt-servicefabricmanagedclusters 2.1.0b1\r\nazure-mgmt-servicelinker 1.2.0b3\r\nazure-mgmt-signalr 2.0.0b2\r\nazure-mgmt-sql 4.0.0b20\r\nazure-mgmt-sqlvirtualmachine 1.0.0b5\r\nazure-mgmt-storage 22.0.0\r\nazure-mgmt-synapse 2.1.0b5\r\nazure-mgmt-trafficmanager 1.0.0\r\nazure-mgmt-web 7.3.1\r\nazure-monitor-opentelemetry 1.6.9\r\nazure-monitor-opentelemetry-exporter 1.0.0b36\r\nazure-monitor-query 1.2.0\r\nazure-multiapi-storage 1.4.0\r\nazure-storage-blob 12.25.1\r\nazure-storage-common 1.4.2\r\nazure-storage-file-datalake 12.20.0\r\nazure-storage-file-share 12.21.0\r\nazure-storage-queue 12.12.0\r\nazure-synapse-accesscontrol 0.5.0\r\nazure-synapse-artifacts 0.20.0\r\nazure-synapse-managedprivateendpoints 0.4.0\r\nazure-synapse-spark 0.7.0\r\nazureml-accel-models 1.60.0\r\nazureml-automl-core 1.60.0\r\nazureml-automl-dnn-nlp 1.60.0\r\nazureml-automl-runtime 1.60.0\r\nazureml-cli-common 1.60.0\r\nazureml-contrib-automl-pipeline-steps 1.60.0\r\nazureml-contrib-dataset 1.60.0\r\nazureml-contrib-fairness 1.60.0\r\nazureml-contrib-notebook 1.60.0\r\nazureml-contrib-pipeline-steps 1.60.0\r\nazureml-contrib-reinforcementlearning 1.60.0\r\nazureml-contrib-server 1.60.0\r\nazureml-contrib-services 1.60.0\r\nazureml-core 1.60.0\r\nazureml-dataprep 5.1.6\r\nazureml-dataprep-native 41.0.0\r\nazureml-dataprep-rslex 2.22.5\r\nazureml-dataset-runtime 1.60.0\r\nazureml-defaults 1.60.0\r\nazureml-fsspec 1.3.1\r\nazureml-inference-server-http 1.4.0\r\nazureml-interpret 1.60.0\r\nazureml-mlflow 1.60.0\r\nazureml-opendatasets 1.60.0\r\nazureml-pipeline 1.60.0\r\nazureml-pipeline-core 1.60.0\r\nazureml-pipeline-steps 1.60.0\r\nazureml-responsibleai 1.60.0\r\nazureml-sdk 1.60.0\r\nazureml-telemetry 1.60.0\r\nazureml-tensorboard 1.60.0\r\nazureml-train 1.60.0\r\nazureml-train-automl 1.60.0\r\nazureml-train-automl-client 1.60.0\r\nazureml-train-automl-runtime 1.60.0\r\nazureml-train-core 1.60.0\r\nazureml-train-restclients-hyperdrive 1.60.0\r\nazureml-training-tabular 1.60.0\r\nazureml-widgets 1.60.0\r\nbabel 2.17.0\r\nbackports.tempfile 1.0\r\nbackports.weakref 1.0.post1\r\nbcrypt 4.3.0\r\nbeautifulsoup4 4.13.3\r\nbidict 0.23.1\r\nbitsandbytes 0.45.5\r\nbleach 6.2.0\r\nblinker 1.9.0\r\nblis 0.7.11\r\nbokeh 2.4.3\r\nboto3 1.20.19\r\nbotocore 1.23.19\r\nBottleneck 1.4.2\r\nBrotli 1.0.9\r\ncachetools 5.5.2\r\ncatalogue 2.0.10\r\ncertifi 2025.1.31\r\ncffi 1.17.1\r\nchardet 5.2.0\r\ncharset-normalizer 3.4.1\r\nclick 8.1.8\r\ncloudpathlib 0.16.0\r\ncloudpickle 1.6.0\r\ncmdstanpy 1.2.5\r\ncolorama 0.4.6\r\ncoloredlogs 15.0.1\r\ncolorful 0.5.6\r\ncolorlog 6.9.0\r\ncomm 0.2.2\r\nconfection 0.1.5\r\ncontextlib2 21.6.0\r\ncontourpy 1.3.1\r\nconvertdate 2.4.0\r\ncoremltools 2.1.0\r\ncramjam 2.10.0\r\ncryptography 38.0.4\r\ncycler 0.12.1\r\ncymem 2.0.11\r\nCython 3.0.12\r\ncytoolz 1.0.1\r\ndask 2023.2.0\r\ndask-expr 1.1.10\r\ndask_sql 2024.5.0\r\ndatabricks-sdk 0.49.0\r\ndataclasses 0.6\r\ndatasets 2.16.1\r\ndebugpy 1.8.14\r\ndecorator 5.2.1\r\ndefusedxml 0.7.1\r\nDeprecated 1.2.18\r\ndice-ml 0.11\r\ndill 0.3.7\r\ndistlib 0.3.9\r\ndistributed 2023.2.0\r\ndistro 1.9.0\r\ndm-tree 0.1.9\r\ndocker 7.1.0\r\ndotnetcore2 3.1.23\r\neconml 0.15.1\r\nen_core_web_sm 3.7.1\r\nencrypted-inference 0.9\r\nentrypoints 0.4\r\nephem 4.2\r\nerroranalysis 0.5.5\r\nexceptiongroup 1.2.2\r\nexecuting 2.2.0\r\nfabric 3.2.2\r\nfairlearn 0.7.0\r\nFarama-Notifications 0.0.4\r\nfastai 1.0.61\r\nfastapi 0.115.12\r\nfastjsonschema 2.21.1\r\nfastparquet 2024.11.0\r\nfastprogress 1.0.3\r\nfilelock 3.18.0\r\nfire 0.7.0\r\nfixedint 0.1.6\r\nflake8 3.8.4\r\nFlask 3.0.3\r\nflask-cors 5.0.1\r\nFlask-SocketIO 5.5.1\r\nflatbuffers 25.2.10\r\nfonttools 4.51.0\r\nfqdn 1.5.1\r\nfrozenlist 1.5.0\r\nfsspec 2023.10.0\r\nfusepy 3.0.1\r\ngast 0.6.0\r\ngensim 4.3.2\r\ngevent 25.4.1\r\ngitdb 4.0.12\r\nGitPython 3.1.44\r\ngmpy2 2.2.1\r\ngoogle-api-core 2.24.2\r\ngoogle-auth 2.38.0\r\ngoogle-auth-oauthlib 1.2.1\r\ngoogle-pasta 0.2.0\r\ngoogleapis-common-protos 1.69.2\r\ngreenlet 3.2.0\r\ngrpcio 1.71.0\r\ngunicorn 23.0.0\r\ngym 0.26.2\r\ngym-notices 0.0.8\r\ngymnasium 0.28.1\r\nh11 0.14.0\r\nh5py 3.13.0\r\nholidays 0.29\r\nhttpcore 1.0.8\r\nhttpx 0.28.1\r\nhuggingface_hub 0.30.2\r\nhumanfriendly 10.0\r\nidna 3.10\r\nimageio 2.37.0\r\nimportlib_metadata 8.2.0\r\nimportlib_resources 6.4.0\r\ninference-schema 1.8\r\ninterpret_community 0.31.0\r\ninterpret-core 0.5.0\r\ninvoke 2.2.0\r\nipykernel 6.29.5\r\nipython 8.35.0\r\nipython-genutils 0.2.0\r\nipywidgets 7.0.0\r\nisodate 0.7.2\r\nisoduration 20.11.0\r\nisort 6.0.1\r\nitsdangerous 2.1.2\r\njavaproperties 0.5.2\r\njax-jumpy 1.0.0\r\njedi 0.18.0\r\njeepney 0.9.0\r\nJinja2 3.1.6\r\njmespath 0.10.0\r\njoblib 1.2.0\r\njson5 0.12.0\r\njsondiff 2.0.0\r\njsonpickle 4.0.5\r\njsonpointer 3.0.0\r\njsonschema 4.23.0\r\njsonschema-specifications 2024.10.1\r\njupyter 1.1.1\r\njupyter_client 7.4.9\r\njupyter-console 6.6.3\r\njupyter_core 5.7.2\r\njupyter-events 0.12.0\r\njupyter-lsp 2.2.5\r\njupyter-resource-usage 0.7.2\r\njupyter_server 2.15.0\r\njupyter_server_fileid 0.9.3\r\njupyter-server-mathjax 0.2.6\r\njupyter_server_terminals 0.5.3\r\njupyter_server_ydoc 0.8.0\r\njupyter-ydoc 0.2.5\r\njupyterlab 4.3.4\r\njupyterlab_nvdashboard 0.13.0\r\njupyterlab_pygments 0.3.0\r\njupyterlab_server 2.27.3\r\njupyterlab-system-monitor 0.8.0\r\njupyterlab-topbar 0.6.1\r\njupyterlab_widgets 1.1.11\r\njupytext 1.17.0\r\nkagglehub 0.3.11\r\nkeras 3.9.2\r\nkeras-hub 0.20.0\r\nkeras-nlp 0.20.0\r\nkeras-tuner 1.4.7\r\nkeras2onnx 1.6.0\r\nkiwisolver 1.4.8\r\nknack 0.11.0\r\nkrb5 0.7.1\r\nkt-legacy 1.0.5\r\nlangcodes 3.5.0\r\nlanguage_data 1.3.0\r\nlazy_loader 0.4\r\nlibclang 18.1.1\r\nlightgbm 4.6.0\r\nlightning-utilities 0.14.3\r\nllvmlite 0.39.1\r\nlocket 1.0.0\r\nLunarCalendar 0.0.9\r\nlz4 4.4.4\r\nMako 1.3.10\r\nmarisa-trie 1.2.1\r\nMarkdown 3.8\r\nmarkdown-it-py 3.0.0\r\nMarkupSafe 3.0.2\r\nmarshmallow 3.23.2\r\nmatplotlib 3.7.1\r\nmatplotlib-inline 0.1.7\r\nmccabe 0.6.1\r\nmdit-py-plugins 0.4.2\r\nmdurl 0.1.2\r\nmicrosoft-security-utilities-secret-masker 1.0.0b4\r\nmistune 3.1.3\r\nmkl_fft 1.3.11\r\nmkl_random 1.2.8\r\nmkl-service 2.4.0\r\nml_dtypes 0.5.1\r\nml_wrappers 0.5.6\r\nmlflow-skinny 2.21.3\r\nmpmath 1.3.0\r\nmsal 1.31.2b1\r\nmsal-extensions 1.2.0\r\nmsgpack 1.1.0\r\nmsrest 0.7.1\r\nmsrestazure 0.6.4.post1\r\nmultidict 6.4.3\r\nmultiprocess 0.70.15\r\nmurmurhash 1.0.12\r\nnamex 0.0.8\r\nnarwhals 1.35.0\r\nnbclassic 1.2.0\r\nnbclient 0.10.2\r\nnbconvert 7.16.6\r\nnbdime 4.0.2\r\nnbformat 5.10.4\r\nndg-httpsclient 0.5.1\r\nnest-asyncio 1.6.0\r\nnetworkx 3.4\r\nnotebook 6.5.7\r\nnotebook_shim 0.2.4\r\nnumba 0.56.4\r\nnumexpr 2.10.2\r\nnumpy 1.23.5\r\nnvidia-cublas-cu12 12.4.5.8\r\nnvidia-cuda-cupti-cu12 12.4.127\r\nnvidia-cuda-nvrtc-cu12 12.4.127\r\nnvidia-cuda-runtime-cu12 12.4.127\r\nnvidia-cudnn-cu12 9.1.0.70\r\nnvidia-cufft-cu12 11.2.1.3\r\nnvidia-curand-cu12 10.3.5.147\r\nnvidia-cusolver-cu12 11.6.1.9\r\nnvidia-cusparse-cu12 12.3.1.170\r\nnvidia-cusparselt-cu12 0.6.2\r\nnvidia-ml-py 12.570.86\r\nnvidia-ml-py3 7.352.0\r\nnvidia-nccl-cu12 2.21.5\r\nnvidia-nvjitlink-cu12 12.4.127\r\nnvidia-nvtx-cu12 12.4.127\r\noauthlib 3.2.2\r\nolive-ai 0.9.1\r\nonnx 1.17.0\r\nonnxconverter-common 1.13.0\r\nonnxmltools 1.12.0\r\nonnxruntime 1.21.1\r\nonnxruntime-genai 0.7.1\r\nonnxscript 0.2.5\r\nopencensus 0.11.4\r\nopencensus-context 0.1.3\r\nopencensus-ext-azure 1.1.14\r\nopencv-python 4.11.0.86\r\nopentelemetry-api 1.31.1\r\nopentelemetry-instrumentation 0.52b1\r\nopentelemetry-instrumentation-asgi 0.52b1\r\nopentelemetry-instrumentation-dbapi 0.52b1\r\nopentelemetry-instrumentation-django 0.52b1\r\nopentelemetry-instrumentation-fastapi 0.52b1\r\nopentelemetry-instrumentation-flask 0.52b1\r\nopentelemetry-instrumentation-psycopg2 0.52b1\r\nopentelemetry-instrumentation-requests 0.52b1\r\nopentelemetry-instrumentation-urllib 0.52b1\r\nopentelemetry-instrumentation-urllib3 0.52b1\r\nopentelemetry-instrumentation-wsgi 0.52b1\r\nopentelemetry-resource-detector-azure 0.1.5\r\nopentelemetry-sdk 1.31.1\r\nopentelemetry-semantic-conventions 0.52b1\r\nopentelemetry-util-http 0.52b1\r\nopt_einsum 3.4.0\r\noptimum 1.25.3\r\noptree 0.15.0\r\noptuna 4.3.0\r\normsgpack 1.7.0\r\noverrides 7.7.0\r\npackaging 25.0\r\npandas 1.5.3\r\npandas-ml 0.6.1\r\npandocfilters 1.5.1\r\npapermill 2.6.0\r\nparamiko 3.5.1\r\nparso 0.8.4\r\npartd 1.4.2\r\npathspec 0.12.1\r\npatsy 1.0.1\r\npeft 0.15.2\r\npexpect 4.9.0\r\nPillow 9.2.0\r\npip 22.3.1\r\npkginfo 1.12.1.2\r\nplatformdirs 4.3.7\r\nplotly 6.0.1\r\npluggy 1.5.0\r\npmdarima 1.8.5\r\nportalocker 2.10.1\r\npreshed 3.0.9\r\nprometheus_client 0.21.1\r\nprompt_toolkit 3.0.50\r\npropcache 0.3.1\r\nproperty-cached 1.6.4\r\nprophet 1.1.4\r\nproto-plus 1.26.1\r\nprotobuf 4.25.6\r\npsutil 6.1.1\r\npsycopg2 2.9.9\r\nptyprocess 0.7.0\r\npure_eval 0.2.3\r\npy-deviceid 0.1.1\r\npy-spy 0.4.0\r\npy4j 0.10.9.5\r\npyarrow 14.0.2\r\npyarrow-hotfix 0.6\r\npyasn1 0.6.1\r\npyasn1_modules 0.4.2\r\npycocotools 2.0.6\r\npycodestyle 2.6.0\r\npycomposefile 0.0.32\r\npycparser 2.22\r\npydantic 2.9.2\r\npydantic_core 2.23.4\r\npydantic-settings 2.8.1\r\npydash 8.0.5\r\npydocstyle 6.3.0\r\npyflakes 2.2.0\r\nPyGithub 1.59.1\r\nPygments 2.19.1\r\nPyJWT 2.4.0\r\npylint 3.3.6\r\nPyMeeus 0.5.12\r\nPyNaCl 1.5.0\r\npynvml 12.0.0\r\npyodbc 5.2.0\r\npyOpenSSL 23.0.0\r\npyparsing 3.2.3\r\npyreadline3 3.5.4\r\nPySocks 1.7.1\r\npyspark 3.3.1\r\npython-dateutil 2.9.0.post0\r\npython-dotenv 1.1.0\r\npython-engineio 4.12.0\r\npython-json-logger 3.3.0\r\npython-jsonrpc-server 0.4.0\r\npython-language-server 0.35.0\r\npython-snappy 0.7.3\r\npython-socketio 5.13.0\r\npytoolconfig 1.3.1\r\npytorch-transformers 1.0.0\r\npytz 2022.5\r\nPyWavelets 1.8.0\r\nPyYAML 6.0.2\r\npyzmq 26.4.0\r\nqtconsole 5.6.1\r\nQtPy 2.4.3\r\nQuantLib 1.37\r\nrai_core_flask 0.7.6\r\nraiutils 0.4.2\r\nraiwidgets 0.36.0\r\nray 2.31.0\r\nreferencing 0.36.2\r\nregex 2024.11.6\r\nrequests 2.32.3\r\nrequests-oauthlib 2.0.0\r\nresponsibleai 0.36.0\r\nrfc3339-validator 0.1.4\r\nrfc3986-validator 0.1.1\r\nrich 14.0.0\r\nrope 1.13.0\r\nrpds-py 0.22.3\r\nrsa 4.9\r\ns3transfer 0.5.2\r\nsacremoses 0.0.53\r\nsafetensors 0.5.3\r\nscikit-image 0.25.0\r\nscikit-learn 1.5.1\r\nscipy 1.11.0\r\nscp 0.13.6\r\nscrapbook 0.5.0\r\nseaborn 0.13.2\r\nSecretStorage 3.3.3\r\nsemver 2.13.0\r\nSend2Trash 1.8.3\r\nsentencepiece 0.2.0\r\nseqeval 1.2.2\r\nsetuptools 75.8.0\r\nsetuptools-git 1.2\r\nshap 0.43.0\r\nshellingham 1.5.4\r\nsimple-websocket 1.1.0\r\nsix 1.17.0\r\nskl2onnx 1.15.0\r\nsklearn-pandas 1.7.0\r\nslicer 0.0.7\r\nsmart-open 6.4.0\r\nsmmap 5.0.2\r\nsniffio 1.3.1\r\nsnowballstemmer 2.2.0\r\nsortedcontainers 2.4.0\r\nsoupsieve 2.6\r\nspacy 3.7.4\r\nspacy-legacy 3.0.12\r\nspacy-loggers 1.0.5\r\nsparse 0.16.0\r\nSQLAlchemy 2.0.41\r\nsqlparse 0.5.3\r\nsrsly 2.5.1\r\nsshtunnel 0.1.5\r\nstack-data 0.6.3\r\nstanio 0.5.1\r\nstarlette 0.46.1\r\nstatsmodels 0.13.5\r\nstrictyaml 1.7.3\r\nsympy 1.13.1\r\ntabulate 0.9.0\r\ntblib 3.1.0\r\ntenacity 9.1.2\r\ntensorboard 2.19.0\r\ntensorboard-data-server 0.7.2\r\ntensorboard-plugin-wit 1.8.1\r\ntensorboardX 2.6.2.2\r\ntensorflow 2.19.0\r\ntensorflow-estimator 2.15.0\r\ntensorflow-io-gcs-filesystem 0.37.1\r\ntensorflow-text 2.19.0\r\ntermcolor 3.0.1\r\nterminado 0.18.1\r\ntf_keras 2.19.0\r\nthinc 8.2.5\r\nthreadpoolctl 3.6.0\r\ntifffile 2025.3.30\r\ntinycss2 1.4.0\r\ntokenizers 0.21.1\r\ntoml 0.10.2\r\ntomli 2.2.1\r\ntomlkit 0.13.2\r\ntoolz 1.0.0\r\ntorch 2.6.0+cu124\r\ntorch-tb-profiler 0.4.3\r\ntorchaudio 2.6.0+cu124\r\ntorchmetrics 1.7.1\r\ntorchvision 0.21.0+cu124\r\ntornado 6.4.2\r\ntqdm 4.67.1\r\ntraitlets 5.14.3\r\ntransformers 4.49.0\r\ntriton 3.2.0\r\ntyper 0.4.2\r\ntypes-python-dateutil 2.9.0.20241206\r\ntyping_extensions 4.13.2\r\ntzdata 2025.2\r\ntzlocal 5.3.1\r\nujson 5.10.0\r\nunicodedata2 15.1.0\r\nuri-template 1.3.0\r\nurllib3 1.26.20\r\nuuid 1.30\r\nuvicorn 0.34.0\r\nvirtualenv 20.30.0\r\nwaitress 3.0.2\r\nwasabi 1.1.3\r\nwcwidth 0.2.13\r\nweasel 0.3.4\r\nwebcolors 24.11.1\r\nwebencodings 0.5.1\r\nwebsocket-client 1.8.0\r\nwebsockets 15.0.1\r\nWerkzeug 3.1.3\r\nwheel 0.45.1\r\nwidgetsnbextension 3.0.8\r\nwrapt 1.14.1\r\nwsproto 1.2.0\r\nxgboost 1.5.2\r\nxmltodict 0.14.2\r\nxxhash 3.5.0\r\ny-py 0.6.2\r\nyapf 0.43.0\r\nyarl 1.19.0\r\nypy-websocket 0.8.4\r\nzict 3.0.0\r\nzipp 3.12.0\r\nzope.event 5.0\r\nzope.interface 7.2\r\n" + } + ], + "execution_count": 17, + "metadata": {}, + "id": "f5eed001" + }, + { + "cell_type": "code", + "source": [ + "! pip install peft" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: peft in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (0.15.2)\nRequirement already satisfied: torch>=1.13.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (2.6.0+cu124)\nRequirement already satisfied: psutil in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (6.1.1)\nRequirement already satisfied: pyyaml in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (6.0.2)\nRequirement already satisfied: huggingface_hub>=0.25.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (0.30.2)\nRequirement already satisfied: accelerate>=0.21.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (0.34.2)\nRequirement already satisfied: packaging>=20.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (25.0)\nRequirement already satisfied: numpy>=1.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (1.23.5)\nRequirement already satisfied: safetensors in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (0.5.3)\nRequirement already satisfied: transformers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (4.49.0)\nRequirement already satisfied: tqdm in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from peft) (4.67.1)\nRequirement already satisfied: filelock in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (3.18.0)\nRequirement already satisfied: fsspec>=2023.5.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (2023.10.0)\nRequirement already satisfied: typing-extensions>=3.7.4.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (4.13.2)\nRequirement already satisfied: requests in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from huggingface_hub>=0.25.0->peft) (2.32.3)\nRequirement already satisfied: nvidia-cufft-cu12==11.2.1.3 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (11.2.1.3)\nRequirement already satisfied: triton==3.2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (3.2.0)\nRequirement already satisfied: nvidia-cusolver-cu12==11.6.1.9 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (11.6.1.9)\nRequirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (0.6.2)\nRequirement already satisfied: sympy==1.13.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (1.13.1)\nRequirement already satisfied: nvidia-nccl-cu12==2.21.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (2.21.5)\nRequirement already satisfied: nvidia-cusparse-cu12==12.3.1.170 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.3.1.170)\nRequirement already satisfied: networkx in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (3.4)\nRequirement already satisfied: nvidia-nvjitlink-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: nvidia-cublas-cu12==12.4.5.8 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.5.8)\nRequirement already satisfied: nvidia-curand-cu12==10.3.5.147 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (10.3.5.147)\nRequirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: nvidia-cudnn-cu12==9.1.0.70 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (9.1.0.70)\nRequirement already satisfied: nvidia-cuda-nvrtc-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: nvidia-cuda-runtime-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: jinja2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (3.1.6)\nRequirement already satisfied: nvidia-cuda-cupti-cu12==12.4.127 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from torch>=1.13.0->peft) (12.4.127)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy==1.13.1->torch>=1.13.0->peft) (1.3.0)\nRequirement already satisfied: tokenizers<0.22,>=0.21 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->peft) (0.21.1)\nRequirement already satisfied: regex!=2019.12.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from transformers->peft) (2024.11.6)\nRequirement already satisfied: MarkupSafe>=2.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from jinja2->torch>=1.13.0->peft) (3.0.2)\nRequirement already satisfied: urllib3<3,>=1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (1.26.20)\nRequirement already satisfied: charset-normalizer<4,>=2 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (3.4.1)\nRequirement already satisfied: idna<4,>=2.5 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (3.10)\nRequirement already satisfied: certifi>=2017.4.17 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from requests->huggingface_hub>=0.25.0->peft) (2025.1.31)\n" + } + ], + "execution_count": 18, + "metadata": {}, + "id": "692ca04b-6242-42c7-8dff-3d5f8b1122b8" + }, + { + "cell_type": "markdown", + "source": [ + "## 7. Fine-tune with Low-Rank Adaptation (LoRA)\n", + "\n", + "This is the core command that fine-tunes our student model. We're using Microsoft Olive's fine-tuning capabilities with LoRA (Low-Rank Adaptation), a parameter-efficient fine-tuning method. Here's what each parameter does:\n", + "\n", + "- **`--method lora`**: Use Low-Rank Adaptation, which adds small trainable matrices to key layers instead of updating all weights\n", + "\n", + "- **`--model_name_or_path`**: The base model to fine-tune (Phi-4-mini-instruct from Azure ML registry)\n", + "\n", + "- **`--trust_remote_code`**: Allow execution of code from the remote model repository\n", + "\n", + "- **`--data_name json`**: The format of our training data (JSON)\n", + "\n", + "- **`--data_files`**: Path to our training data generated from the teacher model\n", + "\n", + "- **`--text_template`**: Template for formatting inputs and outputs during training\n", + "\n", + "- **`--max_steps 100`**: Only train for 100 steps (for speed, in production you'd use more)\n", + "\n", + "- **`--output_path`**: Where to save the fine-tuned model and adapter\n", + "\n", + "- **`--target_modules`**: Which layers to apply LoRA to (attention and feed-forward layers)\n", + "\n", + "- **`--log_level 1`**: Set verbosity of logging\n", + "\n", + "This process will take several minutes to complete. It creates a LoRA adapter that captures the knowledge our model learned from the teacher without modifying the base model weights.\n" + ], + "metadata": {}, + "id": "378a60ad" + }, + { + "cell_type": "code", + "source": [ + "! olive finetune \\\n", + " --method lora \\\n", + " --model_name_or_path azureml://registries/azureml/models/Phi-4-mini-instruct/versions/1 \\\n", + " --trust_remote_code \\\n", + " --data_name json \\\n", + " --data_files ./data/train_data.jsonl \\\n", + " --text_template \"<|user|>{Question}<|end|><|assistant|>{Answer}<|end|>\" \\\n", + " --max_steps 100 \\\n", + " --output_path models/phi-4-mini/ft \\\n", + " --target_modules \"q_proj\",\"k_proj\",\"v_proj\",\"o_proj\",\"gate_proj\",\"up_proj\",\"down_proj\" \\\n", + " --log_level 1" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Loading HuggingFace model from {'type': 'azureml_registry_model', 'registry_name': 'azureml', 'name': 'Phi-4-mini-instruct', 'version': '1'}\n[2025-05-20 20:36:36,950] [INFO] [run.py:142:run_engine] Running workflow default_workflow\n[2025-05-20 20:36:36,956] [INFO] [cache.py:138:__init__] Using cache directory: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow\n[2025-05-20 20:36:37,059] [INFO] [accelerator_creator.py:217:create_accelerators] Running workflow on accelerator specs: gpu-cuda\nSubtype value SAS has no mapping, use base class DataReferenceCredentialDto.\nDownloading the model mlflow_model_folder at /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder\n\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\nYour file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy 'https://amlwlrt4use01.blob.core.windows.net/azureml-f0d43bc8-5206-5f61-b419-b5ca8a19bb42/mlflow_model_folder' '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/resources/ee7f3578/olive_tmpb_5bcs0a/Phi-4-mini-instruct/mlflow_model_folder' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\n[2025-05-20 20:38:13,767] [INFO] [engine.py:223:run] Running Olive on accelerator: gpu-cuda\n[2025-05-20 20:38:13,768] [INFO] [engine.py:864:_create_system] Creating target system ...\n[2025-05-20 20:38:13,768] [INFO] [engine.py:867:_create_system] Target system created in 0.000497 seconds\n[2025-05-20 20:38:13,768] [INFO] [engine.py:879:_create_system] Creating host system ...\n[2025-05-20 20:38:13,768] [INFO] [engine.py:882:_create_system] Host system created in 0.000077 seconds\n[2025-05-20 20:38:13,778] [INFO] [engine.py:683:_run_pass] Running pass f:lora\n2025-05-20 20:38:14.538668: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n2025-05-20 20:38:14.547447: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\nWARNING: All log messages before absl::InitializeLog() is called are written to STDERR\nE0000 00:00:1747773494.557698 181972 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\nE0000 00:00:1747773494.561599 181972 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\nW0000 00:00:1747773494.572437 181972 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\nW0000 00:00:1747773494.572463 181972 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\nW0000 00:00:1747773494.572466 181972 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\nW0000 00:00:1747773494.572468 181972 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n2025-05-20 20:38:14.575678: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\nTo enable the following instructions: AVX2 AVX512F AVX512_VNNI AVX512_BF16 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\nLoading checkpoint shards: 100%|██████████████████| 2/2 [01:42<00:00, 51.37s/it]\nNo label_names provided for model class `PeftModelForCausalLM`. Since `PeftModel` hides base models input arguments, if label_names is not given, label_names can't be set automatically within `Trainer`. Note that empty label_names list will be used instead.\n[2025-05-20 20:40:01,010] [INFO] [lora.py:459:train_and_save_new_model] Running fine-tuning\n 0%| | 0/100 [00:00=1.21.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime-genai==0.7.1) (1.21.1)\r\nRequirement already satisfied: numpy>=1.21.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime-genai==0.7.1) (1.23.5)\r\nRequirement already satisfied: coloredlogs in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (15.0.1)\r\nRequirement already satisfied: protobuf in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (4.25.6)\r\nRequirement already satisfied: sympy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (1.13.1)\r\nRequirement already satisfied: flatbuffers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (25.2.10)\r\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (25.0)\r\nRequirement already satisfied: humanfriendly>=9.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from coloredlogs->onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (10.0)\r\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy->onnxruntime>=1.21.0->onnxruntime-genai==0.7.1) (1.3.0)\r\n" + } + ], + "execution_count": 20, + "metadata": {}, + "id": "b03d6cc0-db61-4613-bc89-42fa13c06440" + }, + { + "cell_type": "code", + "source": [ + "! pip install onnxruntime==1.21.1 -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: onnxruntime==1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (1.21.1)\nRequirement already satisfied: sympy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (1.13.1)\nRequirement already satisfied: coloredlogs in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (15.0.1)\nRequirement already satisfied: protobuf in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (4.25.6)\nRequirement already satisfied: numpy>=1.21.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (1.23.5)\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (25.0)\nRequirement already satisfied: flatbuffers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (25.2.10)\nRequirement already satisfied: humanfriendly>=9.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from coloredlogs->onnxruntime==1.21.1) (10.0)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy->onnxruntime==1.21.1) (1.3.0)\n" + } + ], + "execution_count": 21, + "metadata": {}, + "id": "dd785577" + }, + { + "cell_type": "code", + "source": [ + " ! pip install protobuf==3.20.3 -U " + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Collecting protobuf==3.20.3\n Downloading protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.1 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m14.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m0:01\u001b[0m\n\u001b[?25hInstalling collected packages: protobuf\n Attempting uninstall: protobuf\n Found existing installation: protobuf 4.25.6\n Uninstalling protobuf-4.25.6:\n Successfully uninstalled protobuf-4.25.6\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\ntensorflow 2.19.0 requires numpy<2.2.0,>=1.26.0, but you have numpy 1.23.5 which is incompatible.\nmlflow-skinny 2.21.3 requires packaging<25, but you have packaging 25.0 which is incompatible.\nazureml-training-tabular 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-training-tabular 1.60.0 requires scipy<1.11.0,>=1.0.0, but you have scipy 1.11.0 which is incompatible.\nazureml-train-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-mlflow 1.60.0 requires azure-storage-blob<=12.19.0,>=12.5.0, but you have azure-storage-blob 12.25.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires onnxruntime~=1.17.3, but you have onnxruntime 1.21.1 which is incompatible.\nazureml-automl-runtime 1.60.0 requires psutil<5.9.4,>=5.2.2, but you have psutil 6.1.1 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires torch==2.2.2, but you have torch 2.6.0+cu124 which is incompatible.\nazureml-automl-dnn-nlp 1.60.0 requires transformers[sentencepiece,torch]<=4.48.0, but you have transformers 4.49.0 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed protobuf-3.20.3\n" + } + ], + "execution_count": 22, + "metadata": { + "gather": { + "logged": 1747773849101 + } + }, + "id": "ddbe98cd" + }, + { + "cell_type": "markdown", + "source": [ + "## 9. Optimize and Quantize the Model\n", + "\n", + "This command uses Microsoft Olive's auto-optimization capabilities to convert our fine-tuned model to ONNX format and apply int4 quantization. Here's what each parameter does:\n", + "\n", + "- **`--model_name_or_path`**: The base model from Azure ML registry\n", + "\n", + "- **`--adapter_path`**: Path to our LoRA adapter created in the previous step\n", + "\n", + "- **`--device cpu`**: Target CPU for optimization (you could also use cuda for GPU)\n", + "\n", + "- **`--provider CPUExecutionProvider`**: Use the CPU execution provider for ONNX Runtime\n", + "\n", + "- **`--use_model_builder`**: Use Olive's model builder for optimized conversion\n", + "\n", + "- **`--precision int4`**: Apply int4 quantization, which reduces model size by up to 75% compared to FP16\n", + "\n", + "- **`--output_path`**: Where to save the optimized model\n", + "\n", + "- **`--log_level 1`**: Set verbosity of logging\n", + "\n", + "The optimization process:\n", + "1. Merges the base model with our LoRA adapter\n", + "2. Converts to ONNX format, which is more efficient for inference\n", + "3. Applies int4 quantization to dramatically reduce model size\n", + "4. Optimizes the model for CPU inference\n", + "\n", + "This process will take several minutes to complete. The result will be a much smaller, more efficient model that can run on devices with limited resources while maintaining most of the accuracy.\n" + ], + "metadata": {}, + "id": "1521bfae" + }, + { + "cell_type": "code", + "source": [ + "! olive auto-opt \\\n", + " --model_name_or_path azureml://registries/azureml/models/Phi-4-mini-instruct/versions/1 \\\n", + " --adapter_path models/phi-4-mini/ft/adapter \\\n", + " --device cpu \\\n", + " --provider CPUExecutionProvider \\\n", + " --use_model_builder \\\n", + " --precision int4 \\\n", + " --output_path models/phi-4-mini/onnx \\\n", + " --log_level 1" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Loading HuggingFace model from {'type': 'azureml_registry_model', 'registry_name': 'azureml', 'name': 'Phi-4-mini-instruct', 'version': '1'}\n[2025-05-20 20:44:12,127] [INFO] [run.py:142:run_engine] Running workflow default_workflow\n[2025-05-20 20:44:12,134] [INFO] [cache.py:138:__init__] Using cache directory: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow\n[2025-05-20 20:44:12,170] [INFO] [accelerator_creator.py:217:create_accelerators] Running workflow on accelerator specs: cpu-cpu\n[2025-05-20 20:44:12,173] [INFO] [engine.py:223:run] Running Olive on accelerator: cpu-cpu\n[2025-05-20 20:44:12,173] [INFO] [engine.py:864:_create_system] Creating target system ...\n[2025-05-20 20:44:12,173] [INFO] [engine.py:867:_create_system] Target system created in 0.000080 seconds\n[2025-05-20 20:44:12,173] [INFO] [engine.py:879:_create_system] Creating host system ...\n[2025-05-20 20:44:12,173] [INFO] [engine.py:882:_create_system] Host system created in 0.000063 seconds\n[2025-05-20 20:44:12,183] [INFO] [engine.py:683:_run_pass] Running pass model_builder:modelbuilder\n[2025-05-20 20:44:12,324] [INFO] [cache.py:138:__init__] Using cache directory: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow\n2025-05-20 20:46:27.908652: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n2025-05-20 20:46:27.919525: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\nWARNING: All log messages before absl::InitializeLog() is called are written to STDERR\nE0000 00:00:1747773987.931911 183248 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\nE0000 00:00:1747773987.935633 183248 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\nW0000 00:00:1747773987.946219 183248 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\nW0000 00:00:1747773987.946242 183248 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\nW0000 00:00:1747773987.946245 183248 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\nW0000 00:00:1747773987.946248 183248 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n2025-05-20 20:46:27.949451: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\nTo enable the following instructions: AVX2 AVX512F AVX512_VNNI AVX512_BF16 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n2025-05-20 20:46:29,130 numexpr.utils [INFO] - Note: NumExpr detected 40 cores but \"NUMEXPR_MAX_THREADS\" not set, so enforcing safe limit of 16.\n2025-05-20 20:46:29,130 numexpr.utils [INFO] - NumExpr defaulting to 16 threads.\nGroupQueryAttention (GQA) is used in this model.\nLoading checkpoint shards: 100%|██████████████████| 2/2 [01:00<00:00, 30.47s/it]\nReading embedding layer\nReading decoder layer 0\nReading decoder layer 1\nReading decoder layer 2\nReading decoder layer 3\nReading decoder layer 4\nReading decoder layer 5\nReading decoder layer 6\nReading decoder layer 7\nReading decoder layer 8\nReading decoder layer 9\nReading decoder layer 10\nReading decoder layer 11\nReading decoder layer 12\nReading decoder layer 13\nReading decoder layer 14\nReading decoder layer 15\nReading decoder layer 16\nReading decoder layer 17\nReading decoder layer 18\nReading decoder layer 19\nReading decoder layer 20\nReading decoder layer 21\nReading decoder layer 22\nReading decoder layer 23\nReading decoder layer 24\nReading decoder layer 25\nReading decoder layer 26\nReading decoder layer 27\nReading decoder layer 28\nReading decoder layer 29\nReading decoder layer 30\nReading decoder layer 31\nReading LM head\nSaving ONNX model in /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/runs/637749aa/models\nSaving GenAI config in /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/runs/637749aa/models\nSaving processing files in /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/runs/637749aa/models for GenAI\n[2025-05-20 20:50:38,436] [INFO] [cache.py:138:__init__] Using cache directory: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow\nCould not locate the Xenova/gpt-4o.py inside /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/mlflow/1c5f5d90e48d6c4ad51a86b38c5c99c185fa6fa7cfc2eef7f224895352937eaa.\n[2025-05-20 20:50:38,556] [WARNING] [utils.py:140:save_module_files] Failed to save module file for Xenova/gpt-4o: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/mlflow/1c5f5d90e48d6c4ad51a86b38c5c99c185fa6fa7cfc2eef7f224895352937eaa does not appear to have a file named Xenova/gpt-4o.py. Checkout 'https://huggingface.co//afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/.olive-cache/default_workflow/mlflow/1c5f5d90e48d6c4ad51a86b38c5c99c185fa6fa7cfc2eef7f224895352937eaa/tree/None' for available files.. Loading config with `trust_remote_code=True` will fail!\n[2025-05-20 20:50:39,008] [INFO] [engine.py:757:_run_pass] Pass model_builder:modelbuilder finished in 386.825503 seconds\n[2025-05-20 20:50:39,059] [INFO] [engine.py:683:_run_pass] Running pass extract_adapters:extractadapters\n[2025-05-20 20:51:50,513] [INFO] [extract_adapters.py:188:_run_for_config] Quantized modules do not support dynamic_lora_r. Ignoring.\n" + } + ], + "execution_count": 23, + "metadata": { + "gather": { + "logged": 1744965032561 + } + }, + "id": "35ff7789" } - }, - "source": [ - "## Initial Setup\n", - "\n", - "Before we begin, make sure you've completed these steps:\n", - "\n", - "1. **Azure Login**: Run `az login --use-device-code` in a terminal to authenticate with Azure\n", - "\n", - "2. **Kernel Selection**: Select the \"Python 3.10 PyTorch and Tensorflow\" kernel from the dropdown in the top-right corner. This kernel has most of the dependencies we need pre-installed.\n", - "\n", - "3. **Check Environment**: Ensure your `local.env` file is in the same directory as this notebook" - ] - }, - { - "cell_type": "markdown", - "id": "da5d712c", - "metadata": {}, - "source": [ - "## 1. Install Authentication Packages\n", - "\n", - "Here we install the packages needed to authenticate with Azure services:\n", - "\n", - "- **azure-ai-ml**: The Azure ML SDK for working with Azure Machine Learning\n", - "\n", - "The `-U` flag ensures we get the latest versions of these packages." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2d617742", - "metadata": {}, - "outputs": [], - "source": [ - "# Install required packages for authentication\n", - "! pip install azure-ai-ml -U" - ] - }, - { - "cell_type": "markdown", - "id": "68b941ef", - "metadata": {}, - "source": [ - "## 2. Install PyTorch\n", - "\n", - "Here we install PyTorch, which is the deep learning framework we'll use for fine-tuning. This command installs\n", - " \n", - "- **torch**: The core PyTorch library for neural networks and tensor operations\n", - "- **torchvision**: For computer vision tasks (included as a dependency)\n", - "- **torchaudio**: For audio processing tasks (included as a dependency)\n", - " \n", - "We're installing from a specific URL (`download.pytorch.org/whl/cu124`) to get a version compatible with CUDA 12.4, which is optimized for modern NVIDIA GPUs. The `-U` flag ensures we get the latest version.\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "385440eb", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 -U" - ] - }, - { - "cell_type": "markdown", - "id": "43939308", - "metadata": {}, - "source": [ - "## 3. Install Microsoft Olive\n", - "\n", - "Now we install Microsoft Olive, an open-source model optimization toolkit that will be the main tool for our fine-tuning and optimization process. The `[auto-opt]` option includes additional dependencies for automatic optimization.\n", - "\n", - "Olive provides:\n", - "- Model fine-tuning capabilities\n", - "- ONNX conversion tools\n", - "- Quantization for model compression\n", - "- Performance optimization for various hardware targets\n", - "\n", - "This powerful tool will help us efficiently fine-tune our model and prepare it for deployment on resource-constrained devices." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "89f3ef60", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install olive-ai[auto-opt] -U" - ] - }, - { - "cell_type": "markdown", - "id": "77fc5c13", - "metadata": {}, - "source": [ - "## 4. Verify Olive Installation\n", - "\n", - "We'll now check the installed version of Olive to ensure it installed correctly. This command shows:\n", - "- The package name\n", - "- The installed version number\n", - "- Where the package is installed\n", - "- The package's dependencies\n", - "\n", - "Confirming the version is important as different versions of Olive may have different features or requirements." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e08355c2", - "metadata": {}, - "outputs": [], - "source": [ - "! pip show olive-ai" - ] - }, - { - "cell_type": "markdown", - "id": "4c6a3bdf", - "metadata": {}, - "source": [ - "## 5. Install ONNX Runtime GenAI\n", - "\n", - "Next, we install ONNX Runtime GenAI, a specialized version of ONNX Runtime designed specifically for generative AI models. This package will allow us to:\n", - "\n", - "- Run our optimized model efficiently\n", - "- Leverage specialized optimizations for transformer models\n", - "- Access adapter-based fine-tuning capabilities\n", - "\n", - "We're installing version 0.7.1 with the `--pre` flag because it's a pre-release version with features we need for our work. Later notebooks will use this to run inference with our optimized model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "69907a09", - "metadata": { - "gather": { - "logged": 1744963274226 + ], + "metadata": { + "kernel_info": { + "name": "python3" + }, + "kernelspec": { + "name": "python3", + "language": "python", + "display_name": "Python 3 (ipykernel)" + }, + "language_info": { + "name": "python", + "version": "3.10.16", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "microsoft": { + "ms_spell_check": { + "ms_spell_check_language": "en" + } + }, + "nteract": { + "version": "nteract-front-end@1.0.0" } - }, - "outputs": [], - "source": [ - "! pip install onnxruntime-genai==0.7.1 --pre" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2976cf9b", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install onnxruntime==1.21.1 -U" - ] - }, - { - "cell_type": "markdown", - "id": "f97d1dc8", - "metadata": {}, - "source": [ - "## 6. Package Management\n", - "\n", - "The next few cells handle package management to avoid conflicts. We're:\n", - "\n", - "1. **Uninstalling onnxruntime-gpu** to avoid conflicts with the regular onnxruntime package\n", - "2. **Installing regular onnxruntime** for CPU-based inference\n", - "3. **Installing additional dependencies** including:\n", - " - bitsandbytes: For efficient quantization\n", - " - transformers: For working with transformer models\n", - " - peft: For parameter-efficient fine-tuning (LoRA)\n", - " - accelerate: For optimized training\n", - "\n", - "These packages will ensure our environment is properly set up for fine-tuning and optimization." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba394391", - "metadata": {}, - "outputs": [], - "source": [ - "pip uninstall onnxruntime-gpu --yes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0cdfae44", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install onnxruntime" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67c74e5e", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install bitsandbytes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "96b0305c", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install transformers==4.49.0 -U" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fd93b0a9", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install azure-ai-ml -U " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "66fb7020", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install marshmallow==3.23.2 -U " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45b21c43", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install tf-keras" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8207d88c", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install numpy==1.23.5 -U" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "165e19c3", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install peft" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f5eed001", - "metadata": {}, - "outputs": [], - "source": [ - "! pip list" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "692ca04b-6242-42c7-8dff-3d5f8b1122b8", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install peft" - ] - }, - { - "cell_type": "markdown", - "id": "378a60ad", - "metadata": {}, - "source": [ - "## 7. Fine-tune with Low-Rank Adaptation (LoRA)\n", - "\n", - "This is the core command that fine-tunes our student model. We're using Microsoft Olive's fine-tuning capabilities with LoRA (Low-Rank Adaptation), a parameter-efficient fine-tuning method. Here's what each parameter does:\n", - "\n", - "- **`--method lora`**: Use Low-Rank Adaptation, which adds small trainable matrices to key layers instead of updating all weights\n", - "\n", - "- **`--model_name_or_path`**: The base model to fine-tune (Phi-4-mini-instruct from Azure ML registry)\n", - "\n", - "- **`--trust_remote_code`**: Allow execution of code from the remote model repository\n", - "\n", - "- **`--data_name json`**: The format of our training data (JSON)\n", - "\n", - "- **`--data_files`**: Path to our training data generated from the teacher model\n", - "\n", - "- **`--text_template`**: Template for formatting inputs and outputs during training\n", - "\n", - "- **`--max_steps 100`**: Only train for 100 steps (for speed, in production you'd use more)\n", - "\n", - "- **`--output_path`**: Where to save the fine-tuned model and adapter\n", - "\n", - "- **`--target_modules`**: Which layers to apply LoRA to (attention and feed-forward layers)\n", - "\n", - "- **`--log_level 1`**: Set verbosity of logging\n", - "\n", - "This process will take several minutes to complete. It creates a LoRA adapter that captures the knowledge our model learned from the teacher without modifying the base model weights." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22241278", - "metadata": { - "gather": { - "logged": 1744964309865 - } - }, - "outputs": [], - "source": [ - "! olive finetune \\\n", - " --method lora \\\n", - " --model_name_or_path azureml://registries/azureml/models/Phi-4-mini-instruct/versions/1 \\\n", - " --trust_remote_code \\\n", - " --data_name json \\\n", - " --data_files ./data/train_data.jsonl \\\n", - " --text_template \"<|user|>{Question}<|end|><|assistant|>{Answer}<|end|>\" \\\n", - " --max_steps 100 \\\n", - " --output_path models/phi-4-mini/ft \\\n", - " --target_modules \"q_proj\",\"k_proj\",\"v_proj\",\"o_proj\",\"gate_proj\",\"up_proj\",\"down_proj\" \\\n", - " --log_level 1" - ] - }, - { - "cell_type": "markdown", - "id": "7e50cfd3", - "metadata": {}, - "source": [ - "## 8. Reinstall ONNX Runtime GenAI\n", - "\n", - "Here we reinstall ONNX Runtime GenAI to ensure we have the correct version after all our package management. This is a precautionary step to make sure we have the version (0.7.1) needed for our model optimization in the next step." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b03d6cc0-db61-4613-bc89-42fa13c06440", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install onnxruntime-genai==0.7.1 --pre" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd785577", - "metadata": {}, - "outputs": [], - "source": [ - "! pip install onnxruntime==1.21.1 -U" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ddbe98cd", - "metadata": {}, - "outputs": [], - "source": [ - " ! pip install protobuf==3.20.3 -U " - ] - }, - { - "cell_type": "markdown", - "id": "1521bfae", - "metadata": {}, - "source": [ - "## 9. Optimize and Quantize the Model\n", - "\n", - "This command uses Microsoft Olive's auto-optimization capabilities to convert our fine-tuned model to ONNX format and apply int4 quantization. Here's what each parameter does:\n", - "\n", - "- **`--model_name_or_path`**: The base model from Azure ML registry\n", - "\n", - "- **`--adapter_path`**: Path to our LoRA adapter created in the previous step\n", - "\n", - "- **`--device cpu`**: Target CPU for optimization (you could also use cuda for GPU)\n", - "\n", - "- **`--provider CPUExecutionProvider`**: Use the CPU execution provider for ONNX Runtime\n", - "\n", - "- **`--use_model_builder`**: Use Olive's model builder for optimized conversion\n", - "\n", - "- **`--precision int4`**: Apply int4 quantization, which reduces model size by up to 75% compared to FP16\n", - "\n", - "- **`--output_path`**: Where to save the optimized model\n", - "\n", - "- **`--log_level 1`**: Set verbosity of logging\n", - "\n", - "The optimization process:\n", - "1. Merges the base model with our LoRA adapter\n", - "2. Converts to ONNX format, which is more efficient for inference\n", - "3. Applies int4 quantization to dramatically reduce model size\n", - "4. Optimizes the model for CPU inference\n", - "\n", - "This process will take several minutes to complete. The result will be a much smaller, more efficient model that can run on devices with limited resources while maintaining most of the accuracy." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35ff7789", - "metadata": { - "gather": { - "logged": 1744965032561 - } - }, - "outputs": [], - "source": [ - "! olive auto-opt \\\n", - " --model_name_or_path azureml://registries/azureml/models/Phi-4-mini-instruct/versions/1 \\\n", - " --adapter_path models/phi-4-mini/ft/adapter \\\n", - " --device cpu \\\n", - " --provider CPUExecutionProvider \\\n", - " --use_model_builder \\\n", - " --precision int4 \\\n", - " --output_path models/phi-4-mini/onnx \\\n", - " --log_level 1" - ] - } - ], - "metadata": { - "kernel_info": { - "name": "python38-azureml-pt-tf" - }, - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - }, - "microsoft": { - "ms_spell_check": { - "ms_spell_check_language": "en" - } }, - "nteract": { - "version": "nteract-front-end@1.0.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/Lab329/Notebook/03.AzureML_RuningByORTGenAI.ipynb b/Lab329/Notebook/03.AzureML_RuningByORTGenAI.ipynb index 27aa5c5..32d513f 100644 --- a/Lab329/Notebook/03.AzureML_RuningByORTGenAI.ipynb +++ b/Lab329/Notebook/03.AzureML_RuningByORTGenAI.ipynb @@ -1,562 +1,589 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "002cd1e6", - "metadata": {}, - "source": [ - "# Testing Your Optimized Model with ONNX Runtime GenAI\n", - "\n", - "This notebook demonstrates how to use your fine-tuned and optimized model for inference using ONNX Runtime GenAI. We'll load both the model and adapter created in the previous notebook and test it on sample questions.\n", - "\n", - "## What You'll Learn\n", - "\n", - "- How to load an ONNX-optimized language model\n", - "- How to apply a LoRA adapter for fine-tuned capabilities\n", - "- How to run efficient inference using ONNX Runtime GenAI\n", - "- How to format inputs and process outputs\n", - "- How to test your model on sample questions\n", - "\n", - "## Prerequisites\n", - "\n", - "- Completed the previous notebooks:\n", - " - `01.AzureML_Distillation.ipynb` (generated training data)\n", - " - `02.AzureML_FineTuningAndConvertByMSOlive.ipynb` (fine-tuned and optimized the model) \n", - "- Successfully created model files in `models/phi-4-mini/onnx/`\n", - "- Python environment with necessary libraries (which we'll install)\n", - "\n", - "## Setup Instructions\n", - "\n", - "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", - "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 Azure ML\"** using the selector in the top right\n", - "3. **Check Files**: Verify your model files exist in the path shown in this notebook" - ] - }, - { - "cell_type": "markdown", - "id": "ba9f2007", - "metadata": {}, - "source": [ - "## Initial Setup\n", - "\n", - "Before proceeding with this notebook, ensure you've completed these important setup steps:\n", - "\n", - "1. **Azure Authentication**: Run `az login --use-device-code` in a terminal to authenticate with Azure\n", - "\n", - "2. **Kernel Selection**: Select the **\"Python 3.10 Azure ML\"** kernel from the dropdown menu in the top-right corner of this notebook. This kernel has the necessary libraries pre-installed.\n", - "\n", - "3. **File Verification**: Confirm that the model files created in the previous notebook exist in the `/models/phi-4-mini/onnx/` directory" - ] - }, - { - "cell_type": "markdown", - "id": "800c1c37", - "metadata": {}, - "source": [ - "## 1. Install ONNX Runtime\n", - "\n", - "First, we'll install ONNX Runtime, which is the inference engine we'll use to run our optimized model. ONNX (Open Neural Network Exchange) is an open standard for representing machine learning models, and ONNX Runtime is a high-performance inference engine for those models.\n", - "\n", - "We're installing a specific version (1.21.0) to ensure compatibility with our other components. The `-U` flag ensures we get an upgrade if an older version is already installed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb2f4a9b-be22-4f45-a421-35d501ab4a18", - "metadata": { - "gather": { - "logged": 1744965103069 + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Testing Your Optimized Model with ONNX Runtime GenAI\n", + "\n", + "This notebook demonstrates how to use your fine-tuned and optimized model for inference using ONNX Runtime GenAI. We'll load both the model and adapter created in the previous notebook and test it on sample questions.\n", + "\n", + "## What You'll Learn\n", + "\n", + "- How to load an ONNX-optimized language model\n", + "- How to apply a LoRA adapter for fine-tuned capabilities\n", + "- How to run efficient inference using ONNX Runtime GenAI\n", + "- How to format inputs and process outputs\n", + "- How to test your model on sample questions\n", + "\n", + "## Prerequisites\n", + "\n", + "- Completed the previous notebooks:\n", + " - `01.AzureML_Distillation.ipynb` (generated training data)\n", + " - `02.AzureML_FineTuningAndConvertByMSOlive.ipynb` (fine-tuned and optimized the model) \n", + "- Successfully created model files in `models/phi-4-mini/onnx/`\n", + "- Python environment with necessary libraries (which we'll install)\n", + "\n", + "## Setup Instructions\n", + "\n", + "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", + "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 Azure ML\"** using the selector in the top right\n", + "3. **Check Files**: Verify your model files exist in the path shown in this notebook\n" + ], + "metadata": {}, + "id": "002cd1e6" + }, + { + "cell_type": "markdown", + "source": [ + "## Initial Setup\n", + "\n", + "Before proceeding with this notebook, ensure you've completed these important setup steps:\n", + "\n", + "1. **Azure Authentication**: Run `az login --use-device-code` in a terminal to authenticate with Azure\n", + "\n", + "2. **Kernel Selection**: Select the **\"Python 3.10 Azure ML\"** kernel from the dropdown menu in the top-right corner of this notebook. This kernel has the necessary libraries pre-installed.\n", + "\n", + "3. **File Verification**: Confirm that the model files created in the previous notebook exist in the `/models/phi-4-mini/onnx/` directory" + ], + "metadata": {}, + "id": "ba9f2007" + }, + { + "cell_type": "markdown", + "source": [ + "## 1. Install ONNX Runtime\n", + "\n", + "First, we'll install ONNX Runtime, which is the inference engine we'll use to run our optimized model. ONNX (Open Neural Network Exchange) is an open standard for representing machine learning models, and ONNX Runtime is a high-performance inference engine for those models.\n", + "\n", + "We're installing a specific version (1.21.0) to ensure compatibility with our other components. The `-U` flag ensures we get an upgrade if an older version is already installed." + ], + "metadata": {}, + "id": "800c1c37" + }, + { + "cell_type": "code", + "source": [ + "! pip install onnxruntime==1.21.1 -U" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: onnxruntime==1.21.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (1.21.1)\r\nRequirement already satisfied: coloredlogs in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (15.0.1)\r\nRequirement already satisfied: sympy in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (1.13.1)\r\nRequirement already satisfied: packaging in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (25.0)\r\nRequirement already satisfied: flatbuffers in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (25.2.10)\r\nRequirement already satisfied: numpy>=1.21.6 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (1.23.5)\r\nRequirement already satisfied: protobuf in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from onnxruntime==1.21.1) (3.20.3)\r\nRequirement already satisfied: humanfriendly>=9.1 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from coloredlogs->onnxruntime==1.21.1) (10.0)\r\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from sympy->onnxruntime==1.21.1) (1.3.0)\r\n" + } + ], + "execution_count": 2, + "metadata": { + "gather": { + "logged": 1744965103069 + } + }, + "id": "eb2f4a9b-be22-4f45-a421-35d501ab4a18" + }, + { + "cell_type": "markdown", + "source": [ + "## 2. Import Required Libraries\n", + "\n", + "Now we'll import the necessary libraries for running our optimized model:\n", + "\n", + "- **onnxruntime_genai (og)**: A specialized version of ONNX Runtime designed specifically for generative AI models, providing efficient inference for transformer-based language models\n", + "\n", + "- **numpy (np)**: A fundamental package for scientific computing in Python, which we'll use for numerical operations\n", + "\n", + "- **os**: The standard Python module for interacting with the operating system, which we'll use for file path operations" + ], + "metadata": {}, + "id": "9b8e7d74" + }, + { + "cell_type": "code", + "source": [ + "import onnxruntime_genai as og\n", + "import numpy as np\n", + "import os" + ], + "outputs": [], + "execution_count": 3, + "metadata": { + "gather": { + "logged": 1747774667344 + } + }, + "id": "e81c41fc" + }, + { + "cell_type": "markdown", + "source": [ + "## 3. Check Current Working Directory\n", + "\n", + "Before loading our model, we'll check where we're currently located in the filesystem. This helps ensure we use the correct relative paths when loading model files.\n", + "\n", + "The code uses the `os.getcwd()` function to get the current working directory and prints it. This information is useful for debugging path-related issues." + ], + "metadata": {}, + "id": "1b3ad3b9" + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "current_path = os.getcwd() # Gets the current working directory\n", + "print(f\"Current Path: {current_path}\")" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Current Path: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal\n" + } + ], + "execution_count": 4, + "metadata": { + "gather": { + "logged": 1747774667397 + } + }, + "id": "03481afb" + }, + { + "cell_type": "markdown", + "source": [ + "## 4. Set Model Folder Path\n", + "\n", + "Here we define the path to our ONNX-optimized model files. This should point to the directory where our model was saved in the previous notebook after the optimization process.\n", + "\n", + "The path `./models/phi-4-mini/onnx/model` is a relative path starting from our current working directory. This folder should contain all the necessary ONNX model files, including the main model weights and configuration files." + ], + "metadata": {}, + "id": "ce43a8bb" + }, + { + "cell_type": "code", + "source": [ + "model_folder = \"models/phi-4-mini/onnx/model\"" + ], + "outputs": [], + "execution_count": 5, + "metadata": { + "gather": { + "logged": 1747774667451 + } + }, + "id": "47c437d8" + }, + { + "cell_type": "markdown", + "source": [ + "## 5. Load the ONNX Model\n", + "\n", + "This is where we load our optimized model into memory using ONNX Runtime GenAI. The `og.Model()` function creates a model object by loading the files from our specified model folder.\n", + "\n", + "During this step, the following happens:\n", + "1. ONNX Runtime loads the model architecture and weights\n", + "2. The model is prepared for inference\n", + "3. Any optimizations made during the ONNX conversion are applied\n", + "\n", + "This model loading step may take a few moments depending on the size of the model and your hardware capabilities." + ], + "metadata": {}, + "id": "8c2612d2" + }, + { + "cell_type": "code", + "source": [ + "model = og.Model(model_folder)" + ], + "outputs": [], + "execution_count": 6, + "metadata": { + "gather": { + "logged": 1747774713447 + } + }, + "id": "cc07d165" + }, + { + "cell_type": "markdown", + "source": [ + "## 6. Load the LoRA Adapter\n", + "\n", + "Now we load the LoRA (Low-Rank Adaptation) adapter that contains the fine-tuned weights from our knowledge distillation process. This adapter is what gives our model its specialized knowledge for answering multiple-choice questions.\n", + "\n", + "The process works as follows:\n", + "1. First, we create an `Adapters` object associated with our base model\n", + "2. Then we load the specific adapter file from the path `./models/phi-4-mini/onnx/model/adapter_weights.onnx_adapter`\n", + "3. We give it the name \"qa_choice\" which we'll refer to later when we activate it\n", + "\n", + "This approach allows us to keep the base model unchanged while applying our specialized fine-tuning through the adapter." + ], + "metadata": {}, + "id": "dbdda339" + }, + { + "cell_type": "code", + "source": [ + "adapters = og.Adapters(model)\n", + "adapters.load('./models/phi-4-mini/onnx/model/adapter_weights.onnx_adapter', \"qa_choice\")" + ], + "outputs": [], + "execution_count": 7, + "metadata": { + "gather": { + "logged": 1747774714387 + } + }, + "id": "597bf101" + }, + { + "cell_type": "markdown", + "source": [ + "## 7. Set Up the Tokenizer\n", + "\n", + "Here we create a tokenizer for our model, which is responsible for converting text into tokens (numerical representations) that the model can understand.\n", + "\n", + "1. First, we create a tokenizer associated with our model using `og.Tokenizer(model)`\n", + "2. Then we create a tokenizer stream, which will help us decode generated tokens back to text\n", + "\n", + "The tokenizer handles all the text preprocessing needed for our model, ensuring that inputs are properly formatted and outputs are correctly decoded." + ], + "metadata": {}, + "id": "7ad694b1" + }, + { + "cell_type": "code", + "source": [ + "tokenizer = og.Tokenizer(model)\n", + "tokenizer_stream = tokenizer.create_stream()" + ], + "outputs": [], + "execution_count": 8, + "metadata": { + "gather": { + "logged": 1747774715393 + } + }, + "id": "d65e7217" + }, + { + "cell_type": "markdown", + "source": [ + "## 8. Configure Generation Settings\n", + "\n", + "Here we configure the settings that will control how our model generates text. These parameters affect the behavior of the text generation process:\n", + "\n", + "- **max_length**: Sets the maximum number of tokens that the model will generate (102 in this case)\n", + "\n", + "- **past_present_share_buffer**: When set to False, the model uses separate memory buffers for past and present states, which can be more memory-intensive but sometimes more stable\n", + "\n", + "These settings help balance the quality of generation with computational efficiency. For our multiple-choice question answering task, we keep these settings relatively simple since we only need short answers." + ], + "metadata": {}, + "id": "0c18e44c" + }, + { + "cell_type": "code", + "source": [ + "search_options = {}\n", + "search_options['max_length'] = 120\n", + "search_options['past_present_share_buffer'] = False" + ], + "outputs": [], + "execution_count": 9, + "metadata": { + "gather": { + "logged": 1747774715451 + } + }, + "id": "1a92eb5d" + }, + { + "cell_type": "markdown", + "source": [ + "## 9. Define a Sample Test Question\n", + "\n", + "Now we'll define a sample multiple-choice question to test our model. This question follows the same format as the questions we used to train our model in the previous notebooks.\n", + "\n", + "The question includes:\n", + "1. A clear instruction about answering a multiple-choice question\n", + "2. The question itself about sanctions against a school\n", + "3. Five possible answer choices labeled A through E\n", + "\n", + "We'll use this input to test whether our fine-tuned model can correctly understand and respond to multiple-choice questions." + ], + "metadata": {}, + "id": "9322170c" + }, + { + "cell_type": "code", + "source": [ + "input = \"Answer the following multiple-choice question by selecting the correct option.\\n\\nQuestion: Sammy wanted to go to where the people were. Where might he go?\\nAnswer Choices:\\n(A) race track\\n(B) populated areas\\n(C) the desert\\n(D) apartment\\n(E) roadblock\"" + ], + "outputs": [], + "execution_count": 10, + "metadata": { + "gather": { + "logged": 1747774715517 + } + }, + "id": "c6a9f4fc" + }, + { + "cell_type": "markdown", + "source": [ + "## 10. Define the Chat Template\n", + "\n", + "Here we define a chat template that formats our input for the model. This template follows the specific format that our model was fine-tuned with and includes:\n", + "\n", + "1. **``**: An end-of-sequence token to mark the start of the conversation\n", + "\n", + "2. **System message**: Instructions to the model that it should only respond with one of the five choices (A-E)\n", + "\n", + "3. **`<|end|>`, `<|user|>`, `<|assistant|>`**: Special tokens that define different parts of the conversation (end of a message, user input, and assistant response)\n", + "\n", + "4. **`{input}`**: A placeholder that will be replaced with our question\n", + "\n", + "This formatting is crucial for the model to properly understand its role and the task at hand." + ], + "metadata": {}, + "id": "7d635d48" + }, + { + "cell_type": "code", + "source": [ + "chat_template = \"<|system|>You are a helpful assistant. Your output should only be one of the five choices: 'A', 'B', 'C', 'D', or 'E'.<|end|><|user|>{input}<|end|><|assistant|>\"" + ], + "outputs": [], + "execution_count": 11, + "metadata": { + "gather": { + "logged": 1747774715566 + } + }, + "id": "deec901b" + }, + { + "cell_type": "markdown", + "source": [ + "## 11. Format the Full Prompt\n", + "\n", + "This step combines our chat template with the actual question. The `format()` method replaces the `{input}` placeholder in our template with the multiple-choice question we defined earlier.\n", + "\n", + "The result is a complete, properly formatted prompt that follows the structure our model expects, with system instructions, user question, and a marker indicating where the model should start its response." + ], + "metadata": {}, + "id": "36f6baf8" + }, + { + "cell_type": "code", + "source": [ + "prompt = f'{chat_template.format(input=input)}'" + ], + "outputs": [], + "execution_count": 12, + "metadata": { + "gather": { + "logged": 1747774715617 + } + }, + "id": "73c5e734" + }, + { + "cell_type": "markdown", + "source": [ + "## 12. Tokenize the Input\n", + "\n", + "Before we can feed our prompt to the model, we need to convert it from text into tokens (numerical representations that the model can process). This step uses the tokenizer we set up earlier to encode our formatted prompt.\n", + "\n", + "The `tokenizer.encode()` function splits the text into tokens and converts them to their corresponding numerical IDs according to the model's vocabulary. The resulting `input_tokens` is a sequence of integers that represents our prompt in a format the model can work with." + ], + "metadata": {}, + "id": "68bbac3f" + }, + { + "cell_type": "code", + "source": [ + "input_tokens = tokenizer.encode(prompt)" + ], + "outputs": [], + "execution_count": 13, + "metadata": { + "gather": { + "logged": 1747774715659 + } + }, + "id": "40d0e26d" + }, + { + "cell_type": "markdown", + "source": [ + "## 13. Set Up the Generator\n", + "\n", + "Here we configure the text generation process by creating a Generator object with our model:\n", + "\n", + "1. First, we create a `GeneratorParams` object associated with our model, which will hold all generation settings\n", + "\n", + "2. Then we apply the search options we defined earlier (like maximum length) to these parameters\n", + "\n", + "3. Finally, we create the actual `Generator` object that will handle the text generation process\n", + "\n", + "This generator will use our model and the specified parameters to generate text based on our input." + ], + "metadata": {}, + "id": "2e0380ed" + }, + { + "cell_type": "code", + "source": [ + "params = og.GeneratorParams(model)\n", + "params.set_search_options(**search_options)\n", + "generator = og.Generator(model, params)" + ], + "outputs": [], + "execution_count": 14, + "metadata": { + "gather": { + "logged": 1747774715703 + } + }, + "id": "59ab288b" + }, + { + "cell_type": "markdown", + "source": [ + "## 14. Activate the LoRA Adapter\n", + "\n", + "This important step enables our fine-tuned knowledge by activating the LoRA adapter we loaded earlier. Without this step, the model would run with only its base knowledge.\n", + "\n", + "The `set_active_adapter` method connects our LoRA adapter (which we loaded and named \"qa_choice\") to the generator. This adapter contains the specialized knowledge our model learned during fine-tuning to answer multiple-choice questions better.\n", + "\n", + "By activating this adapter, we're effectively applying our knowledge distillation improvements to the base model." + ], + "metadata": {}, + "id": "ee9a4c21" + }, + { + "cell_type": "code", + "source": [ + "generator.set_active_adapter(adapters, \"qa_choice\")" + ], + "outputs": [], + "execution_count": 15, + "metadata": { + "gather": { + "logged": 1747774715752 + } + }, + "id": "5f258a8b" + }, + { + "cell_type": "markdown", + "source": [ + "## 15. Feed Input Tokens to the Generator\n", + "\n", + "Now we provide our tokenized input to the generator. The `append_tokens()` method takes the tokens we created from our prompt and feeds them into the model.\n", + "\n", + "At this stage, the model reads and processes the input tokens, but it hasn't started generating a response yet. The model is preparing its internal state based on the input context, which includes the instructions and the question." + ], + "metadata": {}, + "id": "b312ac23" + }, + { + "cell_type": "code", + "source": [ + "generator.append_tokens(input_tokens)" + ], + "outputs": [], + "execution_count": 16, + "metadata": { + "gather": { + "logged": 1747774716221 + } + }, + "id": "fa4e0133" + }, + { + "cell_type": "markdown", + "source": [ + "## 16. Generate and Display the Response\n", + "\n", + "Finally, we run the text generation process to get our model's answer to the multiple-choice question. This code:\n", + "\n", + "1. Uses a `while` loop that continues until the generator declares it's done (either by producing an end token or reaching the maximum length)\n", + "\n", + "2. Calls `generate_next_token()` to have the model predict one token at a time\n", + "\n", + "3. Gets the most recently generated token with `get_next_tokens()[0]`\n", + "\n", + "4. Decodes that token back to text using our tokenizer stream\n", + "\n", + "5. Prints each piece of text as it's generated, creating a streaming effect where you see the answer appear gradually\n", + "\n", + "If our knowledge distillation and fine-tuning were successful, the model should respond with the letter corresponding to the correct answer choice (in this case, likely \"A\" for \"ignore\")." + ], + "metadata": {}, + "id": "5bdaf27c" + }, + { + "cell_type": "code", + "source": [ + "while not generator.is_done():\n", + " generator.generate_next_token()\n", + "\n", + " new_token = generator.get_next_tokens()[0]\n", + " print(tokenizer_stream.decode(new_token), end='', flush=True)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "B) populated areas millAP rep Le Maà innerInterInterInterInterInterInterInterInterInterInterInterInterInterInterInter" + } + ], + "execution_count": 17, + "metadata": { + "gather": { + "logged": 1747774717171 + } + }, + "id": "a12abc97" } - }, - "outputs": [], - "source": [ - "! pip install onnxruntime==1.21.1 -U" - ] - }, - { - "cell_type": "markdown", - "id": "9b8e7d74", - "metadata": {}, - "source": [ - "## 2. Import Required Libraries\n", - "\n", - "Now we'll import the necessary libraries for running our optimized model:\n", - "\n", - "- **onnxruntime_genai (og)**: A specialized version of ONNX Runtime designed specifically for generative AI models, providing efficient inference for transformer-based language models\n", - "\n", - "- **numpy (np)**: A fundamental package for scientific computing in Python, which we'll use for numerical operations\n", - "\n", - "- **os**: The standard Python module for interacting with the operating system, which we'll use for file path operations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e81c41fc", - "metadata": { - "gather": { - "logged": 1744965109605 - } - }, - "outputs": [], - "source": [ - "import onnxruntime_genai as og\n", - "import numpy as np\n", - "import os" - ] - }, - { - "cell_type": "markdown", - "id": "1b3ad3b9", - "metadata": {}, - "source": [ - "## 3. Check Current Working Directory\n", - "\n", - "Before loading our model, we'll check where we're currently located in the filesystem. This helps ensure we use the correct relative paths when loading model files.\n", - "\n", - "The code uses the `os.getcwd()` function to get the current working directory and prints it. This information is useful for debugging path-related issues." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "03481afb", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "current_path = os.getcwd() # Gets the current working directory\n", - "print(f\"Current Path: {current_path}\")" - ] - }, - { - "cell_type": "markdown", - "id": "ce43a8bb", - "metadata": {}, - "source": [ - "## 4. Set Model Folder Path\n", - "\n", - "Here we define the path to our ONNX-optimized model files. This should point to the directory where our model was saved in the previous notebook after the optimization process.\n", - "\n", - "The path `./models/phi-4-mini/onnx/model` is a relative path starting from our current working directory. This folder should contain all the necessary ONNX model files, including the main model weights and configuration files." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "47c437d8", - "metadata": { - "gather": { - "logged": 1744965112312 - } - }, - "outputs": [], - "source": [ - "model_folder = \"models/phi-4-mini/onnx/model\"" - ] - }, - { - "cell_type": "markdown", - "id": "8c2612d2", - "metadata": {}, - "source": [ - "## 5. Load the ONNX Model\n", - "\n", - "This is where we load our optimized model into memory using ONNX Runtime GenAI. The `og.Model()` function creates a model object by loading the files from our specified model folder.\n", - "\n", - "During this step, the following happens:\n", - "1. ONNX Runtime loads the model architecture and weights\n", - "2. The model is prepared for inference\n", - "3. Any optimizations made during the ONNX conversion are applied\n", - "\n", - "This model loading step may take a few moments depending on the size of the model and your hardware capabilities." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cc07d165", - "metadata": { - "gather": { - "logged": 1744965152627 - } - }, - "outputs": [], - "source": [ - "model = og.Model(model_folder)" - ] - }, - { - "cell_type": "markdown", - "id": "dbdda339", - "metadata": {}, - "source": [ - "## 6. Load the LoRA Adapter\n", - "\n", - "Now we load the LoRA (Low-Rank Adaptation) adapter that contains the fine-tuned weights from our knowledge distillation process. This adapter is what gives our model its specialized knowledge for answering multiple-choice questions.\n", - "\n", - "The process works as follows:\n", - "1. First, we create an `Adapters` object associated with our base model\n", - "2. Then we load the specific adapter file from the path `./models/phi-4-mini/onnx/model/adapter_weights.onnx_adapter`\n", - "3. We give it the name \"qa_choice\" which we'll refer to later when we activate it\n", - "\n", - "This approach allows us to keep the base model unchanged while applying our specialized fine-tuning through the adapter." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "597bf101", - "metadata": { - "gather": { - "logged": 1744965153242 - } - }, - "outputs": [], - "source": [ - "adapters = og.Adapters(model)\n", - "adapters.load('./models/phi-4-mini/onnx/model/adapter_weights.onnx_adapter', \"qa_choice\")" - ] - }, - { - "cell_type": "markdown", - "id": "7ad694b1", - "metadata": {}, - "source": [ - "## 7. Set Up the Tokenizer\n", - "\n", - "Here we create a tokenizer for our model, which is responsible for converting text into tokens (numerical representations) that the model can understand.\n", - "\n", - "1. First, we create a tokenizer associated with our model using `og.Tokenizer(model)`\n", - "2. Then we create a tokenizer stream, which will help us decode generated tokens back to text\n", - "\n", - "The tokenizer handles all the text preprocessing needed for our model, ensuring that inputs are properly formatted and outputs are correctly decoded." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d65e7217", - "metadata": { - "gather": { - "logged": 1744965153908 - } - }, - "outputs": [], - "source": [ - "tokenizer = og.Tokenizer(model)\n", - "tokenizer_stream = tokenizer.create_stream()" - ] - }, - { - "cell_type": "markdown", - "id": "0c18e44c", - "metadata": {}, - "source": [ - "## 8. Configure Generation Settings\n", - "\n", - "Here we configure the settings that will control how our model generates text. These parameters affect the behavior of the text generation process:\n", - "\n", - "- **max_length**: Sets the maximum number of tokens that the model will generate (102 in this case)\n", - "\n", - "- **past_present_share_buffer**: When set to False, the model uses separate memory buffers for past and present states, which can be more memory-intensive but sometimes more stable\n", - "\n", - "These settings help balance the quality of generation with computational efficiency. For our multiple-choice question answering task, we keep these settings relatively simple since we only need short answers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a92eb5d", - "metadata": { - "gather": { - "logged": 1744965154018 - } - }, - "outputs": [], - "source": [ - "search_options = {}\n", - "search_options['max_length'] = 120\n", - "search_options['past_present_share_buffer'] = False" - ] - }, - { - "cell_type": "markdown", - "id": "9322170c", - "metadata": {}, - "source": [ - "## 9. Define a Sample Test Question\n", - "\n", - "Now we'll define a sample multiple-choice question to test our model. This question follows the same format as the questions we used to train our model in the previous notebooks.\n", - "\n", - "The question includes:\n", - "1. A clear instruction about answering a multiple-choice question\n", - "2. The question itself about sanctions against a school\n", - "3. Five possible answer choices labeled A through E\n", - "\n", - "We'll use this input to test whether our fine-tuned model can correctly understand and respond to multiple-choice questions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6a9f4fc", - "metadata": { - "gather": { - "logged": 1744965154126 + ], + "metadata": { + "kernel_info": { + "name": "python38-azureml" + }, + "kernelspec": { + "name": "python38-azureml", + "language": "python", + "display_name": "Python 3.10 - AzureML" + }, + "language_info": { + "name": "python", + "version": "3.10.11", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "microsoft": { + "ms_spell_check": { + "ms_spell_check_language": "en" + }, + "host": { + "AzureML": { + "notebookHasBeenCompleted": true + } + } + }, + "nteract": { + "version": "nteract-front-end@1.0.0" } - }, - "outputs": [], - "source": [ - "input = \"Answer the following multiple-choice question by selecting the correct option.\\n\\nQuestion: Sammy wanted to go to where the people were. Where might he go?\\nAnswer Choices:\\n(A) race track\\n(B) populated areas\\n(C) the desert\\n(D) apartment\\n(E) roadblock\"" - ] - }, - { - "cell_type": "markdown", - "id": "7d635d48", - "metadata": {}, - "source": [ - "## 10. Define the Chat Template\n", - "\n", - "Here we define a chat template that formats our input for the model. This template follows the specific format that our model was fine-tuned with and includes:\n", - "\n", - "1. **``**: An end-of-sequence token to mark the start of the conversation\n", - "\n", - "2. **System message**: Instructions to the model that it should only respond with one of the five choices (A-E)\n", - "\n", - "3. **`<|end|>`, `<|user|>`, `<|assistant|>`**: Special tokens that define different parts of the conversation (end of a message, user input, and assistant response)\n", - "\n", - "4. **`{input}`**: A placeholder that will be replaced with our question\n", - "\n", - "This formatting is crucial for the model to properly understand its role and the task at hand." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "deec901b", - "metadata": { - "gather": { - "logged": 1744965154242 - } - }, - "outputs": [], - "source": [ - "chat_template = \"<|system|>You are a helpful assistant. Your output should only be one of the five choices: 'A', 'B', 'C', 'D', or 'E'.<|end|><|user|>{input}<|end|><|assistant|>\"" - ] - }, - { - "cell_type": "markdown", - "id": "36f6baf8", - "metadata": {}, - "source": [ - "## 11. Format the Full Prompt\n", - "\n", - "This step combines our chat template with the actual question. The `format()` method replaces the `{input}` placeholder in our template with the multiple-choice question we defined earlier.\n", - "\n", - "The result is a complete, properly formatted prompt that follows the structure our model expects, with system instructions, user question, and a marker indicating where the model should start its response." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73c5e734", - "metadata": { - "gather": { - "logged": 1744965154345 - } - }, - "outputs": [], - "source": [ - "prompt = f'{chat_template.format(input=input)}'" - ] - }, - { - "cell_type": "markdown", - "id": "68bbac3f", - "metadata": {}, - "source": [ - "## 12. Tokenize the Input\n", - "\n", - "Before we can feed our prompt to the model, we need to convert it from text into tokens (numerical representations that the model can process). This step uses the tokenizer we set up earlier to encode our formatted prompt.\n", - "\n", - "The `tokenizer.encode()` function splits the text into tokens and converts them to their corresponding numerical IDs according to the model's vocabulary. The resulting `input_tokens` is a sequence of integers that represents our prompt in a format the model can work with." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40d0e26d", - "metadata": { - "gather": { - "logged": 1744965154448 - } - }, - "outputs": [], - "source": [ - "input_tokens = tokenizer.encode(prompt)" - ] - }, - { - "cell_type": "markdown", - "id": "2e0380ed", - "metadata": {}, - "source": [ - "## 13. Set Up the Generator\n", - "\n", - "Here we configure the text generation process by creating a Generator object with our model:\n", - "\n", - "1. First, we create a `GeneratorParams` object associated with our model, which will hold all generation settings\n", - "\n", - "2. Then we apply the search options we defined earlier (like maximum length) to these parameters\n", - "\n", - "3. Finally, we create the actual `Generator` object that will handle the text generation process\n", - "\n", - "This generator will use our model and the specified parameters to generate text based on our input." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "59ab288b", - "metadata": { - "gather": { - "logged": 1744965154554 - } - }, - "outputs": [], - "source": [ - "params = og.GeneratorParams(model)\n", - "params.set_search_options(**search_options)\n", - "generator = og.Generator(model, params)" - ] - }, - { - "cell_type": "markdown", - "id": "ee9a4c21", - "metadata": {}, - "source": [ - "## 14. Activate the LoRA Adapter\n", - "\n", - "This important step enables our fine-tuned knowledge by activating the LoRA adapter we loaded earlier. Without this step, the model would run with only its base knowledge.\n", - "\n", - "The `set_active_adapter` method connects our LoRA adapter (which we loaded and named \"qa_choice\") to the generator. This adapter contains the specialized knowledge our model learned during fine-tuning to answer multiple-choice questions better.\n", - "\n", - "By activating this adapter, we're effectively applying our knowledge distillation improvements to the base model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f258a8b", - "metadata": { - "gather": { - "logged": 1744965154650 - } - }, - "outputs": [], - "source": [ - "generator.set_active_adapter(adapters, \"qa_choice\")" - ] - }, - { - "cell_type": "markdown", - "id": "b312ac23", - "metadata": {}, - "source": [ - "## 15. Feed Input Tokens to the Generator\n", - "\n", - "Now we provide our tokenized input to the generator. The `append_tokens()` method takes the tokens we created from our prompt and feeds them into the model.\n", - "\n", - "At this stage, the model reads and processes the input tokens, but it hasn't started generating a response yet. The model is preparing its internal state based on the input context, which includes the instructions and the question." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fa4e0133", - "metadata": { - "gather": { - "logged": 1744965155567 - } - }, - "outputs": [], - "source": [ - "generator.append_tokens(input_tokens)" - ] - }, - { - "cell_type": "markdown", - "id": "5bdaf27c", - "metadata": {}, - "source": [ - "## 16. Generate and Display the Response\n", - "\n", - "Finally, we run the text generation process to get our model's answer to the multiple-choice question. This code:\n", - "\n", - "1. Uses a `while` loop that continues until the generator declares it's done (either by producing an end token or reaching the maximum length)\n", - "\n", - "2. Calls `generate_next_token()` to have the model predict one token at a time\n", - "\n", - "3. Gets the most recently generated token with `get_next_tokens()[0]`\n", - "\n", - "4. Decodes that token back to text using our tokenizer stream\n", - "\n", - "5. Prints each piece of text as it's generated, creating a streaming effect where you see the answer appear gradually\n", - "\n", - "If our knowledge distillation and fine-tuning were successful, the model should respond with the letter corresponding to the correct answer choice (in this case, likely \"A\" for \"ignore\")." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a12abc97", - "metadata": { - "gather": { - "logged": 1744965155679 - } - }, - "outputs": [], - "source": [ - "while not generator.is_done():\n", - " generator.generate_next_token()\n", - "\n", - " new_token = generator.get_next_tokens()[0]\n", - " print(tokenizer_stream.decode(new_token), end='', flush=True)" - ] - } - ], - "metadata": { - "kernel_info": { - "name": "python38-azureml-pt-tf" - }, - "kernelspec": { - "display_name": "Python 3.10 - Pytorch and Tensorflow", - "language": "python", - "name": "python38-azureml-pt-tf" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.15" - }, - "microsoft": { - "ms_spell_check": { - "ms_spell_check_language": "en" - } }, - "nteract": { - "version": "nteract-front-end@1.0.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb b/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb index 860ece3..81b1be2 100644 --- a/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb +++ b/Lab329/Notebook/04.AzureML_RegisterToAzureML.ipynb @@ -1,404 +1,449 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "a81488db", - "metadata": {}, - "source": [ - "# Registering Your Optimized Model to Azure ML\n", - "\n", - "This notebook demonstrates how to register your fine-tuned and optimized model to the Azure Machine Learning registry. Model registration is crucial for version tracking, sharing, and deploying models in a production environment.\n", - "\n", - "![](../../lab_manual/images/step-3.png)\n", - "\n", - "## What You'll Learn\n", - "\n", - "- How to authenticate with Azure Machine Learning\n", - "- How to create an ML client to interact with your Azure ML workspace\n", - "- How to register model files to the Azure ML registry\n", - "- How to add metadata and descriptions to your model\n", - "- How to list and verify your registered models\n", - "\n", - "## Prerequisites\n", - "\n", - "- Completed the previous notebooks:\n", - " - `01.AzureML_Distillation.ipynb` (generated training data)\n", - " - `02.AzureML_FineTuningAndConvertByMSOlive.ipynb` (fine-tuned and optimized the model)\n", - " - `03.AzureML_RuningByORTGenAI.ipynb` (tested the optimized model)\n", - "- Successfully created model files in `models/phi-4-mini/onnx/`\n", - "- Access to an Azure ML workspace\n", - "- Python environment with necessary libraries (which we'll install)\n", - "\n", - "## Setup Instructions\n", - "\n", - "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", - "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 AzureML\"** using the selector in the top right\n", - "3. **Environment File**: Ensure your `local.env` file exists with proper Azure ML workspace information" - ] - }, - { - "cell_type": "markdown", - "id": "69b50f83", - "metadata": { - "vscode": { - "languageId": "plaintext" + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Registering Your Optimized Model to Azure ML\n", + "\n", + "This notebook demonstrates how to register your fine-tuned and optimized model to the Azure Machine Learning registry. Model registration is crucial for version tracking, sharing, and deploying models in a production environment.\n", + "\n", + "![](../../lab_manual/images/step-3.png)\n", + "\n", + "## What You'll Learn\n", + "\n", + "- How to authenticate with Azure Machine Learning\n", + "- How to create an ML client to interact with your Azure ML workspace\n", + "- How to register model files to the Azure ML registry\n", + "- How to add metadata and descriptions to your model\n", + "- How to list and verify your registered models\n", + "\n", + "## Prerequisites\n", + "\n", + "- Completed the previous notebooks:\n", + " - `01.AzureML_Distillation.ipynb` (generated training data)\n", + " - `02.AzureML_FineTuningAndConvertByMSOlive.ipynb` (fine-tuned and optimized the model)\n", + " - `03.AzureML_RuningByORTGenAI.ipynb` (tested the optimized model)\n", + "- Successfully created model files in `models/phi-4-mini/onnx/`\n", + "- Access to an Azure ML workspace\n", + "- Python environment with necessary libraries (which we'll install)\n", + "\n", + "## Setup Instructions\n", + "\n", + "1. **Azure Authentication**: Ensure you're logged in to Azure using `az login --use-device-code` in a terminal\n", + "2. **Kernel Selection**: Change the Jupyter kernel to **\"Python 3.10 AzureML\"** using the selector in the top right\n", + "3. **Environment File**: Ensure your `local.env` file exists with proper Azure ML workspace information\n" + ], + "metadata": {}, + "id": "a81488db" + }, + { + "cell_type": "markdown", + "source": [ + "## Initial Setup\n", + "\n", + "Before proceeding with this notebook, complete these important setup steps:\n", + "\n", + "1. **Azure Authentication**: Run `az login --use-device-code` in a terminal to authenticate with Azure. This will provide the credentials needed to access your Azure ML workspace.\n", + "\n", + "2. **Kernel Selection**: Select the \"Python 3.10 AzureML\" kernel from the dropdown menu in the top-right corner of this notebook. This kernel has most of the necessary Azure ML libraries pre-installed.\n", + "\n", + "3. **Environment Variables**: Ensure your `local.env` file contains the following variables:\n", + " - AZUREML_SUBSCRIPTION_ID\n", + " - AZUREML_RESOURCE_GROUP\n", + " - AZUREML_WS_NAME" + ], + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "id": "69b50f83" + }, + { + "cell_type": "markdown", + "source": [ + "## 1. Install Environment Variable Handling Package\n", + "\n", + "First, we'll install the `python-dotenv` package. This library allows us to load environment variables from a `.env` file, making it easier to handle configuration settings securely.\n", + "\n", + "Environment variables are the recommended way to manage sensitive information like subscription IDs and workspace details, as they keep this information out of your code." + ], + "metadata": {}, + "id": "94b9c267" + }, + { + "cell_type": "code", + "source": [ + "pip install dotenv-azd" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Requirement already satisfied: dotenv-azd in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (0.3.0)\nRequirement already satisfied: python-dotenv in /anaconda/envs/azureml_py38/lib/python3.10/site-packages (from dotenv-azd) (1.1.0)\nNote: you may need to restart the kernel to use updated packages.\n" + } + ], + "execution_count": 2, + "metadata": { + "gather": { + "logged": 1747774923850 + } + }, + "id": "cd984981" + }, + { + "cell_type": "markdown", + "source": [ + "## 2. Import Azure ML Model Components\n", + "\n", + "Here we import the basic components needed to define and register a model in Azure ML:\n", + "\n", + "- **Model**: Class from `azure.ai.ml.entities` that represents a machine learning model in Azure ML\n", + "\n", + "- **AssetTypes**: Constants that define the types of assets we can register, such as custom models, datasets, etc.\n", + "\n", + "These components will help us define our model for registration." + ], + "metadata": {}, + "id": "15ce81fe" + }, + { + "cell_type": "code", + "source": [ + "from azure.ai.ml.entities import Model\n", + "from azure.ai.ml.constants import AssetTypes" + ], + "outputs": [], + "execution_count": 3, + "metadata": { + "gather": { + "logged": 1747774925206 + } + }, + "id": "396c4d1c" + }, + { + "cell_type": "markdown", + "source": [ + "## 3. Import Complete Azure ML SDK Components\n", + "\n", + "Now we import all the necessary components from the Azure ML SDK to work with the ML workspace and register our model:\n", + "\n", + "- **MLClient**: The main client for interacting with Azure ML services\n", + "\n", + "- **Input**: Used to define inputs for Azure ML components\n", + "\n", + "- **Model**: As imported previously, for defining our model (repeated import)\n", + "\n", + "- **AssetTypes**: As imported previously, for defining asset types (repeated import)\n", + "\n", + "- **DefaultAzureCredential**: From `azure.identity`, this class provides a default credential flow for authenticating with Azure services\n", + "\n", + "These imports give us everything we need to connect to Azure ML and register our model." + ], + "metadata": {}, + "id": "54ebef01" + }, + { + "cell_type": "code", + "source": [ + "from azure.ai.ml import MLClient, Input\n", + "from azure.ai.ml.entities import Model\n", + "from azure.ai.ml.constants import AssetTypes\n", + "from azure.identity import DefaultAzureCredential" + ], + "outputs": [], + "execution_count": 4, + "metadata": { + "gather": { + "logged": 1747774925262 + } + }, + "id": "addfb781-18a7-43de-8a96-7abd05f1afdb" + }, + { + "cell_type": "markdown", + "source": [ + "## 4. Import Environment Variable Handling\n", + "\n", + "Here we import the libraries needed to access environment variables:\n", + "\n", + "- **os**: For accessing environment variables and file paths\n", + "\n", + "- **load_azd_env**: From `dotenv_azd`, for loading environment variables from Azure Developer CLI (AZD) environments\n", + "\n", + "- **load_dotenv**: From `dotenv`, for loading environment variables from local `.env` files\n", + "\n", + "This combination allows us to access configuration values from either AZD environments or local `.env` files, giving us flexibility in how we manage our Azure ML workspace credentials." + ], + "metadata": {}, + "id": "2544b6cc" + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "from dotenv_azd import load_azd_env\n", + "from dotenv import load_dotenv\n", + "\n", + "# Load environment variables from current AZD environment if available\n", + "load_azd_env(quiet=True)\n", + "\n", + "# Load environment variables from local.env file if it exists\n", + "load_dotenv(dotenv_path=\"local.env\")" + ], + "outputs": [ + { + "output_type": "execute_result", + "execution_count": 5, + "data": { + "text/plain": "True" + }, + "metadata": {} + } + ], + "execution_count": 5, + "metadata": { + "gather": { + "logged": 1747774925318 + } + }, + "id": "5d610fda" + }, + { + "cell_type": "markdown", + "source": [ + "## 5. Load Environment Variables\n", + "\n", + "This cell loads our Azure ML workspace credentials from environment variables. We use a two-step approach:\n", + "\n", + "1. First, try to load variables from an Azure Developer CLI (AZD) environment if available using `load_azd_env()`\n", + "\n", + "2. Then, load any additional or override variables from a local `.env` file using `load_dotenv()`\n", + "\n", + "This approach allows us to work with either AZD environments or local files, providing flexibility in how we manage configuration. \n", + "\n", + "The environment variables we need are:\n", + "- AZUREML_SUBSCRIPTION_ID\n", + "- AZUREML_RESOURCE_GROUP\n", + "- AZUREML_WS_NAME" + ], + "metadata": {}, + "id": "9450cd00" + }, + { + "cell_type": "markdown", + "source": [ + "## 6. Initialize Azure ML Client\n", + "\n", + "In this step, we retrieve our workspace information from the environment variables and create an ML client to interact with Azure ML:\n", + "\n", + "1. First, we get the necessary Azure ML workspace information from environment variables:\n", + " - `subscription_id`: The Azure subscription ID that contains your workspace\n", + " - `resource_group`: The resource group containing your workspace\n", + " - `workspace`: The name of your Azure ML workspace\n", + "\n", + "2. We print these values to verify them (in a production environment, you might remove this debugging output)\n", + "\n", + "3. Finally, we create an `MLClient` object using:\n", + " - `DefaultAzureCredential()`: Uses the default Azure authentication chain\n", + " - The subscription ID, resource group, and workspace name\n", + "\n", + "This client will be our main interface for interacting with Azure ML and registering our model." + ], + "metadata": {}, + "id": "5ad99b54" + }, + { + "cell_type": "code", + "source": [ + "# Get Azure ML credentials from environment variables\n", + "subscription_id = os.getenv('AZUREML_SUBSCRIPTION_ID')\n", + "resource_group = os.getenv('AZUREML_RESOURCE_GROUP')\n", + "workspace = os.getenv('AZUREML_WS_NAME')\n", + "\n", + "# Print values for debugging (remove in production)\n", + "print(f\"Subscription ID: {subscription_id}\")\n", + "print(f\"Resource Group: {resource_group}\")\n", + "print(f\"Workspace: {workspace}\")\n", + "\n", + "# Create ML client with the credentials\n", + "ml_client = MLClient(DefaultAzureCredential(), subscription_id, resource_group, workspace)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Subscription ID: 6415ebd4-1dd7-430f-bd4d-2f5e9419c1cd\nResource Group: rg-cvi-lab329-h-3\nWorkspace: cvi-lab329-h-3\n" + } + ], + "execution_count": 6, + "metadata": { + "gather": { + "logged": 1747774926170 + } + }, + "id": "72a4d517-9a38-4e51-865f-8da9872b8c82" + }, + { + "cell_type": "markdown", + "source": [ + "## 7. Define the Model for Registration\n", + "\n", + "Here we create a Model object that defines the details of our model for registration in Azure ML:\n", + "\n", + "- **path**: Points to the folder containing our optimized ONNX model files\n", + "\n", + "- **type**: Specifies that this is a custom model (rather than a standard framework model)\n", + "\n", + "- **name**: The name under which our model will be registered in Azure ML\n", + "\n", + "- **description**: A brief description of our model to help others understand its purpose\n", + "\n", + "These attributes provide important metadata and context for our model in the Azure ML registry. The name should be descriptive and include information about the model type and optimization level." + ], + "metadata": {}, + "id": "e1676179" + }, + { + "cell_type": "code", + "source": [ + "file_model = Model(\n", + " path=\"models/phi-4-mini/onnx\",\n", + " type=AssetTypes.CUSTOM_MODEL,\n", + " name=\"fine-tuning-phi-4-mini-onnx-int4-cpu\",\n", + " description=\"Fine tuning by MSOlive\",\n", + ")" + ], + "outputs": [], + "execution_count": 7, + "metadata": { + "gather": { + "logged": 1747774926247 + } + }, + "id": "acba2912" + }, + { + "cell_type": "markdown", + "source": [ + "## 8. Register the Model in Azure ML\n", + "\n", + "This cell performs the actual registration of our model to the Azure ML model registry:\n", + "\n", + "1. We use the `ml_client.models.create_or_update()` method to register our model\n", + "\n", + "2. The method takes our model definition (`file_model`) and uploads the files from the specified path to Azure ML\n", + "\n", + "3. If a model with the same name already exists, a new version will be created; if not, version 1 will be created\n", + "\n", + "4. The function returns a reference to the registered model, which we store in `registered_model`\n", + "\n", + "This step may take some time depending on the size of your model files and your internet connection speed." + ], + "metadata": {}, + "id": "79639bb8" + }, + { + "cell_type": "code", + "source": [ + "registered_model = ml_client.models.create_or_update(file_model)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": "Your file exceeds 100 MB. If you experience low speeds, latency, or broken connections, we recommend using the AzCopyv10 tool for this file transfer.\n\nExample: azcopy copy '/afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal/models/phi-4-mini/onnx' 'https://sthubvc4zovnn4uqts.blob.core.windows.net/d0ca370a-6510-40b5-b1dc-98d97b208684-azureml-blobstore/LocalUpload/4e85d7df997d513303aa791ce94b3cc5/onnx' \n\nSee https://learn.microsoft.com/azure/storage/common/storage-use-azcopy-v10 for more information.\n\u001b[32mUploading onnx (4975.45 MBs): 100%|██████████| 4975453220/4975453220 [00:13<00:00, 366428957.40it/s]\n\u001b[39m\n\n" + } + ], + "execution_count": 8, + "metadata": { + "gather": { + "logged": 1747774976608 + } + }, + "id": "34c6f0fc-7869-4fef-8675-acbe625c610c" + }, + { + "cell_type": "markdown", + "source": [ + "## 9. Verify and Explore the Registered Model\n", + "\n", + "After registration, we can verify our model's details and explore its properties:\n", + "\n", + "1. We print key information about our registered model:\n", + " - Name: The model's name in the registry\n", + " - ID: The unique identifier for the model\n", + " - Version: The version number (increments with each update)\n", + " - Description: The description we provided\n", + " - Path: Where the model is stored in Azure ML\n", + "\n", + "2. We also print all properties of the registered model by iterating through its `__dict__` attribute\n", + "\n", + "This gives us a complete view of the model's metadata and confirms that it was registered correctly. You can now find this model in your Azure ML workspace's Models section." + ], + "metadata": {}, + "id": "ac646c31" + }, + { + "cell_type": "code", + "source": [ + "# Display model details\n", + "print(f\"Model Name: {registered_model.name}\")\n", + "print(f\"Model ID: {registered_model.id}\")\n", + "print(f\"Model Version: {registered_model.version}\")\n", + "print(f\"Model Description: {registered_model.description}\")\n", + "print(f\"\\nModel Location: {registered_model.path}\")\n", + "print(f\"\\nFull Model Properties:\")\n", + "for key, value in registered_model.__dict__.items():\n", + " print(f\"{key}: {value}\")" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Model Name: fine-tuning-phi-4-mini-onnx-int4-cpu\nModel ID: /subscriptions/6415ebd4-1dd7-430f-bd4d-2f5e9419c1cd/resourceGroups/rg-cvi-lab329-h-3/providers/Microsoft.MachineLearningServices/workspaces/cvi-lab329-h-3/models/fine-tuning-phi-4-mini-onnx-int4-cpu/versions/1\nModel Version: 1\nModel Description: Fine tuning by MSOlive\n\nModel Location: azureml://subscriptions/6415ebd4-1dd7-430f-bd4d-2f5e9419c1cd/resourceGroups/rg-cvi-lab329-h-3/workspaces/cvi-lab329-h-3/datastores/workspaceblobstore/paths/LocalUpload/4e85d7df997d513303aa791ce94b3cc5/onnx\n\nFull Model Properties:\njob_name: None\n_intellectual_property: None\n_system_metadata: None\n_is_anonymous: False\n_auto_increment_version: False\nauto_delete_setting: None\nname: fine-tuning-phi-4-mini-onnx-int4-cpu\ndescription: Fine tuning by MSOlive\ntags: {}\nproperties: {}\n_print_as_yaml: False\n_id: /subscriptions/6415ebd4-1dd7-430f-bd4d-2f5e9419c1cd/resourceGroups/rg-cvi-lab329-h-3/providers/Microsoft.MachineLearningServices/workspaces/cvi-lab329-h-3/models/fine-tuning-phi-4-mini-onnx-int4-cpu/versions/1\n_Resource__source_path: \n_base_path: /afh/projects/cvi-lab329-h-3-d0ca370a-6510-40b5-b1dc-98d97b208684/shared/Users/cedricvidal\n_creation_context: \n_serialize: \n_version: 1\nlatest_version: None\n_path: azureml://subscriptions/6415ebd4-1dd7-430f-bd4d-2f5e9419c1cd/resourceGroups/rg-cvi-lab329-h-3/workspaces/cvi-lab329-h-3/datastores/workspaceblobstore/paths/LocalUpload/4e85d7df997d513303aa791ce94b3cc5/onnx\ndatastore: None\nutc_time_created: None\nflavors: None\n_arm_type: model_version\ntype: custom_model\nstage: Development\n" + } + ], + "execution_count": 9, + "metadata": { + "gather": { + "logged": 1747774976666 + } + }, + "id": "3e01a21d-7cfb-4922-80a7-73b4223db73e" } - }, - "source": [ - "## Initial Setup\n", - "\n", - "Before proceeding with this notebook, complete these important setup steps:\n", - "\n", - "1. **Azure Authentication**: Run `az login --use-device-code` in a terminal to authenticate with Azure. This will provide the credentials needed to access your Azure ML workspace.\n", - "\n", - "2. **Kernel Selection**: Select the \"Python 3.10 AzureML\" kernel from the dropdown menu in the top-right corner of this notebook. This kernel has most of the necessary Azure ML libraries pre-installed.\n", - "\n", - "3. **Environment Variables**: Ensure your `local.env` file contains the following variables:\n", - " - AZUREML_SUBSCRIPTION_ID\n", - " - AZUREML_RESOURCE_GROUP\n", - " - AZUREML_WS_NAME" - ] - }, - { - "cell_type": "markdown", - "id": "94b9c267", - "metadata": {}, - "source": [ - "## 1. Install Environment Variable Handling Package\n", - "\n", - "First, we'll install the `python-dotenv` package. This library allows us to load environment variables from a `.env` file, making it easier to handle configuration settings securely.\n", - "\n", - "Environment variables are the recommended way to manage sensitive information like subscription IDs and workspace details, as they keep this information out of your code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd984981", - "metadata": {}, - "outputs": [], - "source": [ - "pip install dotenv-azd" - ] - }, - { - "cell_type": "markdown", - "id": "15ce81fe", - "metadata": {}, - "source": [ - "## 2. Import Azure ML Model Components\n", - "\n", - "Here we import the basic components needed to define and register a model in Azure ML:\n", - "\n", - "- **Model**: Class from `azure.ai.ml.entities` that represents a machine learning model in Azure ML\n", - "\n", - "- **AssetTypes**: Constants that define the types of assets we can register, such as custom models, datasets, etc.\n", - "\n", - "These components will help us define our model for registration." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "396c4d1c", - "metadata": { - "gather": { - "logged": 1744965197769 + ], + "metadata": { + "kernel_info": { + "name": "python38-azureml" + }, + "kernelspec": { + "name": "python38-azureml", + "language": "python", + "display_name": "Python 3.10 - AzureML" + }, + "language_info": { + "name": "python", + "version": "3.10.11", + "mimetype": "text/x-python", + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "pygments_lexer": "ipython3", + "nbconvert_exporter": "python", + "file_extension": ".py" + }, + "microsoft": { + "ms_spell_check": { + "ms_spell_check_language": "en" + } + }, + "nteract": { + "version": "nteract-front-end@1.0.0" } - }, - "outputs": [], - "source": [ - "from azure.ai.ml.entities import Model\n", - "from azure.ai.ml.constants import AssetTypes" - ] - }, - { - "cell_type": "markdown", - "id": "54ebef01", - "metadata": {}, - "source": [ - "## 3. Import Complete Azure ML SDK Components\n", - "\n", - "Now we import all the necessary components from the Azure ML SDK to work with the ML workspace and register our model:\n", - "\n", - "- **MLClient**: The main client for interacting with Azure ML services\n", - "\n", - "- **Input**: Used to define inputs for Azure ML components\n", - "\n", - "- **Model**: As imported previously, for defining our model (repeated import)\n", - "\n", - "- **AssetTypes**: As imported previously, for defining asset types (repeated import)\n", - "\n", - "- **DefaultAzureCredential**: From `azure.identity`, this class provides a default credential flow for authenticating with Azure services\n", - "\n", - "These imports give us everything we need to connect to Azure ML and register our model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "addfb781-18a7-43de-8a96-7abd05f1afdb", - "metadata": { - "gather": { - "logged": 1744965347656 - } - }, - "outputs": [], - "source": [ - "from azure.ai.ml import MLClient, Input\n", - "from azure.ai.ml.entities import Model\n", - "from azure.ai.ml.constants import AssetTypes\n", - "from azure.identity import DefaultAzureCredential" - ] - }, - { - "cell_type": "markdown", - "id": "2544b6cc", - "metadata": {}, - "source": [ - "## 4. Import Environment Variable Handling\n", - "\n", - "Here we import the libraries needed to access environment variables:\n", - "\n", - "- **os**: For accessing environment variables and file paths\n", - "\n", - "- **load_azd_env**: From `dotenv_azd`, for loading environment variables from Azure Developer CLI (AZD) environments\n", - "\n", - "- **load_dotenv**: From `dotenv`, for loading environment variables from local `.env` files\n", - "\n", - "This combination allows us to access configuration values from either AZD environments or local `.env` files, giving us flexibility in how we manage our Azure ML workspace credentials." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d610fda", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from dotenv_azd import load_azd_env\n", - "from dotenv import load_dotenv\n", - "\n", - "# Load environment variables from current AZD environment if available\n", - "load_azd_env(quiet=True)\n", - "\n", - "# Load environment variables from local.env file if it exists\n", - "load_dotenv(dotenv_path=\"local.env\")" - ] - }, - { - "cell_type": "markdown", - "id": "9450cd00", - "metadata": {}, - "source": [ - "## 5. Load Environment Variables\n", - "\n", - "This cell loads our Azure ML workspace credentials from environment variables. We use a two-step approach:\n", - "\n", - "1. First, try to load variables from an Azure Developer CLI (AZD) environment if available using `load_azd_env()`\n", - "\n", - "2. Then, load any additional or override variables from a local `.env` file using `load_dotenv()`\n", - "\n", - "This approach allows us to work with either AZD environments or local files, providing flexibility in how we manage configuration. \n", - "\n", - "The environment variables we need are:\n", - "- AZUREML_SUBSCRIPTION_ID\n", - "- AZUREML_RESOURCE_GROUP\n", - "- AZUREML_WS_NAME" - ] - }, - { - "cell_type": "markdown", - "id": "5ad99b54", - "metadata": {}, - "source": [ - "## 6. Initialize Azure ML Client\n", - "\n", - "In this step, we retrieve our workspace information from the environment variables and create an ML client to interact with Azure ML:\n", - "\n", - "1. First, we get the necessary Azure ML workspace information from environment variables:\n", - " - `subscription_id`: The Azure subscription ID that contains your workspace\n", - " - `resource_group`: The resource group containing your workspace\n", - " - `workspace`: The name of your Azure ML workspace\n", - "\n", - "2. We print these values to verify them (in a production environment, you might remove this debugging output)\n", - "\n", - "3. Finally, we create an `MLClient` object using:\n", - " - `DefaultAzureCredential()`: Uses the default Azure authentication chain\n", - " - The subscription ID, resource group, and workspace name\n", - "\n", - "This client will be our main interface for interacting with Azure ML and registering our model." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "72a4d517-9a38-4e51-865f-8da9872b8c82", - "metadata": { - "gather": { - "logged": 1744965492004 - } - }, - "outputs": [], - "source": [ - "# Get Azure ML credentials from environment variables\n", - "subscription_id = os.getenv('AZUREML_SUBSCRIPTION_ID')\n", - "resource_group = os.getenv('AZUREML_RESOURCE_GROUP')\n", - "workspace = os.getenv('AZUREML_WS_NAME')\n", - "\n", - "# Print values for debugging (remove in production)\n", - "print(f\"Subscription ID: {subscription_id}\")\n", - "print(f\"Resource Group: {resource_group}\")\n", - "print(f\"Workspace: {workspace}\")\n", - "\n", - "# Create ML client with the credentials\n", - "ml_client = MLClient(DefaultAzureCredential(), subscription_id, resource_group, workspace)" - ] - }, - { - "cell_type": "markdown", - "id": "e1676179", - "metadata": {}, - "source": [ - "## 7. Define the Model for Registration\n", - "\n", - "Here we create a Model object that defines the details of our model for registration in Azure ML:\n", - "\n", - "- **path**: Points to the folder containing our optimized ONNX model files\n", - "\n", - "- **type**: Specifies that this is a custom model (rather than a standard framework model)\n", - "\n", - "- **name**: The name under which our model will be registered in Azure ML\n", - "\n", - "- **description**: A brief description of our model to help others understand its purpose\n", - "\n", - "These attributes provide important metadata and context for our model in the Azure ML registry. The name should be descriptive and include information about the model type and optimization level." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "acba2912", - "metadata": { - "gather": { - "logged": 1744965494273 - } - }, - "outputs": [], - "source": [ - "file_model = Model(\n", - " path=\"models/phi-4-mini/onnx\",\n", - " type=AssetTypes.CUSTOM_MODEL,\n", - " name=\"fine-tuning-phi-4-mini-onnx-int4-cpu\",\n", - " description=\"Fine tuning by MSOlive\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "79639bb8", - "metadata": {}, - "source": [ - "## 8. Register the Model in Azure ML\n", - "\n", - "This cell performs the actual registration of our model to the Azure ML model registry:\n", - "\n", - "1. We use the `ml_client.models.create_or_update()` method to register our model\n", - "\n", - "2. The method takes our model definition (`file_model`) and uploads the files from the specified path to Azure ML\n", - "\n", - "3. If a model with the same name already exists, a new version will be created; if not, version 1 will be created\n", - "\n", - "4. The function returns a reference to the registered model, which we store in `registered_model`\n", - "\n", - "This step may take some time depending on the size of your model files and your internet connection speed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34c6f0fc-7869-4fef-8675-acbe625c610c", - "metadata": { - "gather": { - "logged": 1744965548069 - } - }, - "outputs": [], - "source": [ - "registered_model = ml_client.models.create_or_update(file_model)" - ] - }, - { - "cell_type": "markdown", - "id": "ac646c31", - "metadata": {}, - "source": [ - "## 9. Verify and Explore the Registered Model\n", - "\n", - "After registration, we can verify our model's details and explore its properties:\n", - "\n", - "1. We print key information about our registered model:\n", - " - Name: The model's name in the registry\n", - " - ID: The unique identifier for the model\n", - " - Version: The version number (increments with each update)\n", - " - Description: The description we provided\n", - " - Path: Where the model is stored in Azure ML\n", - "\n", - "2. We also print all properties of the registered model by iterating through its `__dict__` attribute\n", - "\n", - "This gives us a complete view of the model's metadata and confirms that it was registered correctly. You can now find this model in your Azure ML workspace's Models section." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e01a21d-7cfb-4922-80a7-73b4223db73e", - "metadata": {}, - "outputs": [], - "source": [ - "# Display model details\n", - "print(f\"Model Name: {registered_model.name}\")\n", - "print(f\"Model ID: {registered_model.id}\")\n", - "print(f\"Model Version: {registered_model.version}\")\n", - "print(f\"Model Description: {registered_model.description}\")\n", - "print(f\"\\nModel Location: {registered_model.path}\")\n", - "print(f\"\\nFull Model Properties:\")\n", - "for key, value in registered_model.__dict__.items():\n", - " print(f\"{key}: {value}\")" - ] - } - ], - "metadata": { - "kernel_info": { - "name": "python38-azureml" - }, - "kernelspec": { - "display_name": "Python 3.10 - AzureML", - "language": "python", - "name": "python38-azureml" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.11" - }, - "microsoft": { - "ms_spell_check": { - "ms_spell_check_language": "en" - } }, - "nteract": { - "version": "nteract-front-end@1.0.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file