{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Fine-tuning Embeddings for Design Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's see if we can improve the quality of our returned results using a fine-tuned embedding model trained on our designs!\n", "\n", "We'll use SentenceTransformers to fine-tune our embedding model, as it provides a straightforward approach for adapting models to specific domains." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Install required packages if needed\n", "# !pip install sentence-transformers datasets torch matplotlib" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import json\n", "import os\n", "import pandas as pd\n", "import numpy as np\n", "import torch\n", "import matplotlib.pyplot as plt\n", "from pathlib import Path\n", "from sentence_transformers import SentenceTransformer, InputExample, losses\n", "from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator\n", "from torch.utils.data import DataLoader" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Load Design Data\n", "\n", "First, we'll load the design data from our existing dataset." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/Users/owner/Desktop/Projects/ai-maker-space/code/ImagineUI/src/data/designs\n", "Loaded 141 designs\n" ] }, { "data": { "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", "
idtextcategoriesvisual_characteristics
0135Design 135:\\n Description: This des...[Traditional, Elegant, Text-Heavy, Classic][Muted Color Palette, Vertical Layout, Serif T...
1132Design 132:\\n Description: This des...[minimalist, nature-inspired, modern, zen-them...[white background, green accents, illustrative...
2104Design 104:\\n Description: The CSS ...[minimalism, elegance, typography, web design ...[subtle color palette, classic serif fonts, cl...
3103Design 103:\\n Description: This des...[vintage, classical, dramatic, ornate, elegant][dark color palette, gold accents, traditional...
4168Design 168:\\n Description: This des...[Humorous, Educational, Whimsical, Nature-them...[Vibrant color palette, Whimsical illustration...
\n", "
" ], "text/plain": [ " id text \\\n", "0 135 Design 135:\\n Description: This des... \n", "1 132 Design 132:\\n Description: This des... \n", "2 104 Design 104:\\n Description: The CSS ... \n", "3 103 Design 103:\\n Description: This des... \n", "4 168 Design 168:\\n Description: This des... \n", "\n", " categories \\\n", "0 [Traditional, Elegant, Text-Heavy, Classic] \n", "1 [minimalist, nature-inspired, modern, zen-them... \n", "2 [minimalism, elegance, typography, web design ... \n", "3 [vintage, classical, dramatic, ornate, elegant] \n", "4 [Humorous, Educational, Whimsical, Nature-them... \n", "\n", " visual_characteristics \n", "0 [Muted Color Palette, Vertical Layout, Serif T... \n", "1 [white background, green accents, illustrative... \n", "2 [subtle color palette, classic serif fonts, cl... \n", "3 [dark color palette, gold accents, traditional... \n", "4 [Vibrant color palette, Whimsical illustration... " ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def load_design_data():\n", " \"\"\"Load design data from the metadata files\"\"\"\n", " designs_dir = Path.cwd().parent / \"src\" / \"data\" / \"designs\"\n", " print(designs_dir)\n", " designs = []\n", " \n", " # Load all metadata files\n", " for design_dir in designs_dir.glob(\"**/metadata.json\"):\n", " try:\n", " with open(design_dir, \"r\") as f:\n", " metadata = json.load(f)\n", " \n", " # Create a text representation of the design\n", " text = f\"\"\"Design {metadata.get('id', 'unknown')}:\n", " Description: {metadata.get('description', 'No description available')}\n", " Categories: {', '.join(metadata.get('categories', []))}\n", " Visual Characteristics: {', '.join(metadata.get('visual_characteristics', []))}\n", " \"\"\"\n", " \n", " designs.append({\n", " 'id': metadata.get('id', 'unknown'),\n", " 'text': text.strip(),\n", " 'categories': metadata.get('categories', []),\n", " 'visual_characteristics': metadata.get('visual_characteristics', [])\n", " })\n", " except Exception as e:\n", " print(f\"Error processing design {design_dir}: {e}\")\n", " continue\n", " \n", " print(f\"Loaded {len(designs)} designs\")\n", " return designs\n", "\n", "designs = load_design_data()\n", "designs_df = pd.DataFrame(designs)\n", "designs_df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Create Training Pairs\n", "\n", "For fine-tuning, we need to create positive pairs (similar designs) and negative pairs (dissimilar designs). We'll use categories and visual characteristics to determine similarity." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Creating training pairs...\n", "Created 95 training examples and 27 evaluation examples\n" ] } ], "source": [ "from sentence_transformers import InputExample\n", "def create_training_pairs(designs_df, num_pairs=5000):\n", " \"\"\"Create training pairs for fine-tuning\"\"\"\n", " training_pairs = []\n", " design_ids = designs_df['id'].tolist()\n", " \n", " # Calculate similarity between designs based on categories and characteristics\n", " def calculate_similarity(design1, design2):\n", " # Get categories and characteristics for both designs\n", " cats1 = set(design1['categories'])\n", " cats2 = set(design2['categories'])\n", " chars1 = set(design1['visual_characteristics'])\n", " chars2 = set(design2['visual_characteristics'])\n", " \n", " # Calculate Jaccard similarity for categories and characteristics\n", " cat_sim = len(cats1.intersection(cats2)) / max(1, len(cats1.union(cats2)))\n", " char_sim = len(chars1.intersection(chars2)) / max(1, len(chars1.union(chars2)))\n", " \n", " # Weighted similarity\n", " return 0.5 * cat_sim + 0.5 * char_sim\n", " \n", " # Create similarity matrix\n", " import random\n", " train_examples = []\n", " eval_examples = []\n", " \n", " # Create positive pairs (similar designs)\n", " for i in range(len(designs_df)):\n", " design1 = designs_df.iloc[i].to_dict()\n", " similarities = []\n", " \n", " for j in range(len(designs_df)):\n", " if i != j:\n", " design2 = designs_df.iloc[j].to_dict()\n", " sim = calculate_similarity(design1, design2)\n", " similarities.append((j, sim))\n", " \n", " # Sort by similarity\n", " similarities.sort(key=lambda x: x[1], reverse=True)\n", " \n", " # Add top similar designs as positive pairs\n", " for j, sim in similarities[:3]: # Top 3 most similar\n", " if sim > 0.2: # Only if they're somewhat similar\n", " design2 = designs_df.iloc[j].to_dict()\n", " # Create InputExample with texts and similarity score\n", " example = InputExample(texts=[design1['text'], design2['text']], label=float(sim))\n", " \n", " # 80% for training, 20% for evaluation\n", " if random.random() < 0.8:\n", " train_examples.append(example)\n", " else:\n", " eval_examples.append(example)\n", " \n", " print(f\"Created {len(train_examples)} training examples and {len(eval_examples)} evaluation examples\")\n", " return train_examples, eval_examples\n", "\n", "print(\"Creating training pairs...\")\n", "train_examples, eval_examples = create_training_pairs(designs_df) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Fine-tune the Model\n", "\n", "The model I've selected here is the distilbert-base-nli-stsb-mean-tokens model, chosen as a comparison because its BERT training is effective at semantic similarity. Performance isn't too important here, since we have one design per query and we want to return the best match." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting model fine-tuning...\n", "Loading base model: sentence-transformers/distilbert-base-nli-stsb-mean-tokens\n", "\n", "Training configuration:\n", "- Training examples: 95\n", "- Evaluation examples: 27\n", "- Batch size: 16\n", "- Warmup steps: 0\n", "- Using GPU: False\n", "- Model will be saved to: /Users/owner/Desktop/Projects/ai-maker-space/code/ImagineUI/src/fine_tuned_design_embeddings_20250225_161918\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e4315da477764680aaacae97230e6409", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Computing widget examples: 0%| | 0/1 [00:00\n", " \n", " \n", " [6/6 00:36, Epoch 1/1]\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
StepTraining LossValidation LossPearson CosineSpearman Cosine
6No logNo log-0.139605-0.068639

" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Training complete!\n", "Model saved to /Users/owner/Desktop/Projects/ai-maker-space/code/ImagineUI/src/fine_tuned_design_embeddings_20250225_161918\n" ] } ], "source": [ "def fine_tune_model_simple(train_examples, eval_examples, base_model=\"sentence-transformers/distilbert-base-nli-stsb-mean-tokens\"):\n", " \"\"\"Fine-tune a SentenceTransformer model\"\"\"\n", " import os\n", " import torch\n", " from datetime import datetime\n", " from sentence_transformers import SentenceTransformer, losses\n", " from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator\n", " from torch.utils.data import DataLoader\n", " \n", " # Load the base model\n", " print(f\"Loading base model: {base_model}\")\n", " model = SentenceTransformer(base_model)\n", " \n", " # Create training dataloader\n", " train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)\n", " \n", " # Use CosineSimilarityLoss for fine-tuning\n", " train_loss = losses.CosineSimilarityLoss(model)\n", " \n", " # Create evaluator\n", " evaluator = EmbeddingSimilarityEvaluator.from_input_examples(eval_examples)\n", " \n", " # Create timestamped model save path\n", " timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n", " model_save_path = os.path.join(os.getcwd(), \"fine_tuned_design_embeddings_\" + timestamp)\n", " \n", " # Set up warm-up steps\n", " warmup_steps = int(len(train_dataloader) * 0.1)\n", "\n", " print(f\"\\nTraining configuration:\")\n", " print(f\"- Training examples: {len(train_examples)}\")\n", " print(f\"- Evaluation examples: {len(eval_examples)}\")\n", " print(f\"- Batch size: 16\")\n", " print(f\"- Warmup steps: {warmup_steps}\")\n", " print(f\"- Using GPU: {torch.cuda.is_available()}\")\n", " print(f\"- Model will be saved to: {model_save_path}\")\n", " \n", " # Train the model\n", " model.fit(\n", " train_objectives=[(train_dataloader, train_loss)],\n", " evaluator=evaluator,\n", " epochs=1, # Start with just 1 epoch to test\n", " warmup_steps=warmup_steps,\n", " output_path=model_save_path,\n", " show_progress_bar=True\n", " )\n", " \n", " print(f\"\\nTraining complete!\")\n", " print(f\"Model saved to {model_save_path}\")\n", " \n", " return model, model_save_path\n", "\n", "print(\"Starting model fine-tuning...\")\n", "fine_tuned_model, model_path = fine_tune_model_simple(train_examples, eval_examples)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Evaluate Fine-tuned Model vs Base Model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll need nest_asyncio to run the async evaluation inside a Jupyter notebook." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "import nest_asyncio\n", "nest_asyncio.apply()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Now define a synchronous wrapper for our comparison function\n", "def compare_models_sync(base_model_name, fine_tuned_model_path, test_queries):\n", " \"\"\"Synchronous wrapper for compare_models\"\"\"\n", " import asyncio\n", " from langchain_openai import ChatOpenAI\n", " import json\n", " \n", " # Load models\n", " print(f\"Loading base model: {base_model_name}\")\n", " base_model = SentenceTransformer(base_model_name)\n", " \n", " print(f\"Loading fine-tuned model from: {fine_tuned_model_path}\")\n", " fine_tuned_model = SentenceTransformer(fine_tuned_model_path)\n", " \n", " # Initialize evaluator\n", " llm = ChatOpenAI(model=\"gpt-4\", temperature=0)\n", " \n", " # Create a retrieval function using each model\n", " def retrieve_with_model(model, query, k=1):\n", " # Get embeddings for designs\n", " design_texts = designs_df['text'].tolist()\n", " design_embeddings = model.encode(design_texts, convert_to_tensor=True)\n", " \n", " # Get query embedding\n", " query_embedding = model.encode(query, convert_to_tensor=True)\n", " \n", " # Calculate cosine similarities\n", " cos_scores = torch.nn.functional.cosine_similarity(query_embedding.unsqueeze(0), design_embeddings)\n", " \n", " # Get top k designs\n", " top_k_indices = torch.topk(cos_scores, k=k).indices.tolist()\n", " \n", " # Return top k designs\n", " return [designs_df.iloc[i] for i in top_k_indices]\n", " \n", " # Evaluate a design match\n", " async def evaluate_match(query, design):\n", " prompt = f\"\"\"You are evaluating a design recommendation system.\n", " \n", " USER REQUIREMENTS:\n", " {query}\n", " \n", " RECOMMENDED DESIGN:\n", " {design['text']}\n", " \n", " Score how well the recommended design matches the user's requirements on a scale of 0-10.\n", " Provide your score and brief explanation in JSON format exactly like this:\n", " {{\n", " \"score\": 7,\n", " \"reason\": \"The design aligns with the requirements because...\"\n", " }}\n", " \n", " Return only valid JSON, nothing else.\n", " \"\"\"\n", " \n", " try:\n", " response = await llm.ainvoke(prompt)\n", " result = json.loads(response.content)\n", " return result\n", " except Exception as e:\n", " print(f\"Error evaluating match: {e}\")\n", " return {\"score\": 0, \"reason\": f\"Error parsing evaluation: {e}\"}\n", " \n", " # Test with both models\n", " results = []\n", " \n", " # Define the evaluation function\n", " async def evaluate_all_queries():\n", " for i, query in enumerate(test_queries):\n", " print(f\"Evaluating query {i+1}/{len(test_queries)}: {query[:50]}...\")\n", " \n", " # Get top result from each model\n", " base_result = retrieve_with_model(base_model, query)[0]\n", " fine_tuned_result = retrieve_with_model(fine_tuned_model, query)[0]\n", " \n", " # Evaluate matches\n", " base_eval = await evaluate_match(query, base_result)\n", " fine_tuned_eval = await evaluate_match(query, fine_tuned_result)\n", " \n", " # Store results\n", " results.append({\n", " \"query\": query,\n", " \"base_model_id\": base_result['id'],\n", " \"fine_tuned_model_id\": fine_tuned_result['id'],\n", " \"base_score\": base_eval.get(\"score\", 0),\n", " \"base_reason\": base_eval.get(\"reason\", \"Error\"),\n", " \"fine_tuned_score\": fine_tuned_eval.get(\"score\", 0),\n", " \"fine_tuned_reason\": fine_tuned_eval.get(\"reason\", \"Error\"),\n", " \"models_differ\": base_result['id'] != fine_tuned_result['id']\n", " })\n", " \n", " print(f\" Base model: Design {base_result['id']} - Score: {base_eval.get('score', 0)}\")\n", " print(f\" Fine-tuned: Design {fine_tuned_result['id']} - Score: {fine_tuned_eval.get('score', 0)}\")\n", " \n", " # Run the async evaluation using the event loop\n", " loop = asyncio.get_event_loop()\n", " loop.run_until_complete(evaluate_all_queries())\n", " \n", " return pd.DataFrame(results)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading base model: sentence-transformers/distilbert-base-nli-stsb-mean-tokens\n", "Loading fine-tuned model from: /Users/owner/Desktop/Projects/ai-maker-space/code/ImagineUI/src/fine_tuned_design_embeddings_20250225_161918\n", "Evaluating query 1/8: I need a minimalist design with lots of whitespace...\n", " Base model: Design 220 - Score: 8\n", " Fine-tuned: Design 144 - Score: 9\n", "Evaluating query 2/8: Looking for a playful, colorful design with rounde...\n", " Base model: Design 129 - Score: 8\n", " Fine-tuned: Design 129 - Score: 8\n", "Evaluating query 3/8: Need a professional business design with a dark th...\n", " Base model: Design 204 - Score: 8\n", " Fine-tuned: Design 204 - Score: 8\n", "Evaluating query 4/8: Want a nature-inspired design with organic shapes...\n", " Base model: Design 190 - Score: 8\n", " Fine-tuned: Design 215 - Score: 0\n", "Evaluating query 5/8: Looking for a tech-focused design with a futuristi...\n", " Base model: Design 012 - Score: 9\n", " Fine-tuned: Design 012 - Score: 9\n", "Evaluating query 6/8: I want the craziest design you can find...\n", " Base model: Design 008 - Score: 8\n", " Fine-tuned: Design 008 - Score: 8\n", "Evaluating query 7/8: I'd like an eye-catching design for a small busine...\n", " Base model: Design 006 - Score: 8\n", " Fine-tuned: Design 006 - Score: 8\n", "Evaluating query 8/8: I want something clinical and informative...\n", " Base model: Design 130 - Score: 8\n", " Fine-tuned: Design 004 - Score: 8\n" ] }, { "data": { "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", " \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", "
querybase_model_idfine_tuned_model_idbase_scorebase_reasonfine_tuned_scorefine_tuned_reasonmodels_differ
0I need a minimalist design with lots of whites...2201448The design aligns with the user's requirements...9The recommended design matches the user's requ...True
1Looking for a playful, colorful design with ro...1291298The design aligns with the user's requirements...8The design aligns with the user's requirements...False
2Need a professional business design with a dar...2042048The design aligns with the user's requirements...8The design aligns with the user's requirements...False
3Want a nature-inspired design with organic shapes1902158The design aligns with the user's requirements...0The recommended design does not match the user...True
4Looking for a tech-focused design with a futur...0120129The recommended design aligns very well with t...9The recommended design aligns very well with t...False
5I want the craziest design you can find0080088The design aligns with the user's requirements...8The recommended design matches the user's requ...False
6I'd like an eye-catching design for a small bu...0060068The recommended design matches the user's requ...8The recommended design matches the user's requ...False
7I want something clinical and informative1300048The recommended design matches the user's requ...8The design aligns with the user's requirements...True
\n", "
" ], "text/plain": [ " query base_model_id \\\n", "0 I need a minimalist design with lots of whites... 220 \n", "1 Looking for a playful, colorful design with ro... 129 \n", "2 Need a professional business design with a dar... 204 \n", "3 Want a nature-inspired design with organic shapes 190 \n", "4 Looking for a tech-focused design with a futur... 012 \n", "5 I want the craziest design you can find 008 \n", "6 I'd like an eye-catching design for a small bu... 006 \n", "7 I want something clinical and informative 130 \n", "\n", " fine_tuned_model_id base_score \\\n", "0 144 8 \n", "1 129 8 \n", "2 204 8 \n", "3 215 8 \n", "4 012 9 \n", "5 008 8 \n", "6 006 8 \n", "7 004 8 \n", "\n", " base_reason fine_tuned_score \\\n", "0 The design aligns with the user's requirements... 9 \n", "1 The design aligns with the user's requirements... 8 \n", "2 The design aligns with the user's requirements... 8 \n", "3 The design aligns with the user's requirements... 0 \n", "4 The recommended design aligns very well with t... 9 \n", "5 The design aligns with the user's requirements... 8 \n", "6 The recommended design matches the user's requ... 8 \n", "7 The recommended design matches the user's requ... 8 \n", "\n", " fine_tuned_reason models_differ \n", "0 The recommended design matches the user's requ... True \n", "1 The design aligns with the user's requirements... False \n", "2 The design aligns with the user's requirements... False \n", "3 The recommended design does not match the user... True \n", "4 The recommended design aligns very well with t... False \n", "5 The recommended design matches the user's requ... False \n", "6 The recommended design matches the user's requ... False \n", "7 The design aligns with the user's requirements... True " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "test_queries = [\n", " \"I need a minimalist design with lots of whitespace\",\n", " \"Looking for a playful, colorful design with rounded elements\",\n", " \"Need a professional business design with a dark theme\",\n", " \"Want a nature-inspired design with organic shapes\",\n", " \"Looking for a tech-focused design with a futuristic feel\",\n", " \"I want the craziest design you can find\",\n", " \"I'd like an eye-catching design for a small business\",\n", " \"I want something clinical and informative\"\n", "]\n", "\n", "comparison_results = compare_models_sync(\"sentence-transformers/distilbert-base-nli-stsb-mean-tokens\", model_path, test_queries)\n", "comparison_results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this, we can verify the returned design of each model and query. A standout element is the \"0\" scored by the fine-tuned model for query #3. Checking the returned design, it's definitely not the nature-inspired design we were looking for. The model without fine-tuning hasn't missed a query that badly, so it's unclear why the training moved in the wrong direction." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Visualize Comparison Results" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "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166lN998s2bd+PHj8/rzzz+/Zt1+++1XWnjhhUuffvppndfvvvvupd69ezc4x/XdcMMNeZ8PPPBAg+difaStvsUWW6wm7bU/98033zyfz7Ijjzyy1KlTp9J//vOf/PiLL74ozTvvvKX999+/zv4mTJiQ01p7/SqrrJKPq/zacM899+T3ifdvyjlffvnl8/9XX331fJ7C559/ns/ruHHj8jHH/uIclA0dOjQ//9Zbb9Ws++CDD0o9e/YsbbjhhjXrjjjiiPzaJ554ombdxx9/nI8j1r/zzjvNPubGvgeNiXM/99xz599ELPEdOfXUU0sdOnQorbTSSs3+TcTj2Pbll19u8F5xrrfddts6684555z8mquvvrrOb3edddYpzTPPPKVJkybldXEOYrtevXrlc1Nb+VgPOOCAmnXffvttadFFF83Hcdppp9Wsj8+se/fudb5zse2UKVPq7DO2W3DBBUv77rtvzbpyGvr06VP67LPPatbfeuutef2f/vSnmnXx+cbn/I9//KPOfmt/p7/r7w2YvfuI8rWx/JuO7cri2hDrTj755Dr7WnXVVUtDhgypeRzXw9ju97//fZ3t7rrrrkbXNyauvbWvRbXT0FjeNCfy9/I1Oe5byr766qvSUkstNcP8vbFzHvc3v/71r/N1sLzvH/zgB6VNNtmk0fzglltuya/7+c9/Xmd///M//5Ov4+Xje/755/N2Bx10UJ3t9thjjwb3GU095sa+B41pLJ//rvlPeZ9x71jO72rfN5577rkN7v8i752dfPTuu+9ukFeFbbbZprTkkkvWPG5unh/35eX7lHDRRRfl9QsttFCdYzr++OPr3NNEfrj00kuXttxyyzp5Y3wuSyyxROn73/9+g/NcO08OO+20U86Tm/K7onI0L68SUXMczZNiiZLNqBGMUqz6tcK1SwKjRC1KU6OmKUrCyqJGOdx6660zbLobpYNRqrfsssvmkvHyUq7ZrN/ktLYopYwa7j//+c+5Nr4samcXWWSRmgFFyumIWvho8tNcUYJcPiflJUoUZyZKTGuX9kVtf9TARu1ciGtn9Enefvvt8/9rH3uU+Mf5rH0uixYllLWb0cVnGbUP//jHP/LjOOYowY3aydppjZYEUTtR/pw+/PDDXFIctcPRFKns+9///mz1L46S9fjulZvJxftF6XF9kdaoYY8meksuuWTN+qj1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9tknLbvssunvf/97neduuOGGtOuuu6bFFlssvfvuuxVLIwBANVHTDVAhW221Vfrwww/zcv/996fOnTun7bbbrtLJSnPPPXcebfvxxx+vs/53v/tdGjhwYMXSBQBQjQTdABXSrVu3tNBCC+VllVVWSSNHjkzvv/9++uSTT2q2Oe6449L3vve9PAL2kksumUaNGlVnFOzx48enTTbZJPXs2TP16tUrDRkyJD399NM1zz/66KNpgw02yFNXDRgwIB122GHpq6++mmm6IvjfY4890mWXXVaz7p///Gd68MEH8/r6LrjggjRo0KDUtWvXtMwyy6SrrrqqzvNvvPFG2nDDDdNcc82VBg8enO69994G+4jjjlr0eeedN9f677jjjmrTAYA2QdAN0Ap8+eWX6eqrr05LLbVUnTnHI5iOJt+vvPJKOvfcc9Mll1ySzj777Jrn99xzz7Toooump556Kj3zzDM5cO/SpUt+7q233sq16bvsskt64YUX0nXXXZeD8EMOOWSW6dl3333T9ddfnyZPnpwfRxpiXwsuuGCd7W6++eZ0+OGHp6OOOiq99NJL6Sc/+Ulunv7AAw/k56dPn5523nnnHJA/8cQT6cILL8wFCbVFIcKWW26Zj/WRRx5Jjz32WJpnnnny+02dOvU7nlkAgMrSpxugQm6//fYcXIaofV544YXzuo4d/1956Iknnljz/8UXXzwdffTR6dprr03HHntsXvfee++lY445JvfBDksvvXTN9mPHjs1B+RFHHFHz3HnnnZc22mijXDsdNc8zsuqqq+aa9T/+8Y9pr732ykH3WWedld5+++0625155pm5f/pBBx2UH48YMSL97W9/y+ujBv6+++5Lr776arr77rtT//798zannnpq2nrrrWv2EYUBEZxfeumlNdNrXX755bnWO2rXt9hii+90ngEAKklNN0CFRFD6/PPP5+XJJ5/Mtb0RjP7jH/+oE5Cut956uQl6BOgRhEegXRZB7o9//OO0+eabp9NOOy3Xbtdueh7BcryuvMR7RID7zjvvNKm2O4Lfhx56KBcKbLPNNg22icHWIn21xePyIGzxN5q1lwPusM4669TZPtL55ptv5prucjqjifl///vfOscDAFCNBN0AFRywLJqTxxIjl0dNbwS30YQ8xEBmUVMdwW7UgD/33HPphBNOqNPkesyYMenll19O2267bfrLX/6S+0xHk+9yk/Vo7l0O7GOJADf6WEcf7FmJ945a63iPqO2Ovt5FiHRGX/Ta6Yzl9ddfb7QPOQBANdG8HKCViKbV0bT866+/zo//+te/5um5ItAuq10LXhYDrcVy5JFHph/+8Ie5dnqnnXZKq622Wu4LHkH97Ija5h122CH37Y6+2I1Zbrnlch/sYcOG1ayLxxH8l5+PQdJihPZoPh8ikK8t0hk1+v369cuDwQEAtCVqugEqZMqUKWnChAl5iWbYhx56aK713X777Wv6YEdT8ujDHc2soz92uRY7RHAeg6JFv+cIxiPYjQHVItANMWBZBO6xTdQcRw33rbfe2qSB1Mqiefqnn35a02e8vuhPHttEH/HYf/T7vummm3Lf8xDN3qNAIILyqGWPgdJqFyKUa9T79u2bRyyP56PpexxTjLQeo6YDAFQzNd0AFXLXXXfV1P5Gf+YIbG+44Ya08cYb53VRyxy11xEkR4AeTchjyrBo7h06deqU/v3vf6e99947ffTRRzlwjZHCTzrppPz8SiutlPtjR5Ab04aVSqXcrHy33XZrchpjqrFYZmTo0KF5VPUYOC1GMV9iiSVyTXv5GKLmPgoK9ttvv7TmmmvmweCi8CBGJi+L6dAefvjhXEgQ6f/iiy/SIosskjbbbDM13zRbh5P+bzC+1qw0ulTpJAAwB3UoxV1YlZo0aVLq3bt3mjhxYuu7MRvTu4X2M7Fl9gMFWXzkHS2yn3fnan7f3f/OMyC9s96v0hKLLJDm6twhpf6rtkhaoCXFgHBRex8FEvVHjG/V+dgcUMTxC7oBaG35mOblAAAAUBBBNwAAABRE0A0AAAAFEXQDAABAQQTdAAAAUBBBNwAAABRE0A0AAAAFEXQDAABAQQTdAAAAUBBBN0Ars/HGG6cjjjgitTcPPvhg6tChQ/rPf/5T0XSMGTMmrbLKKk3e/t13383pfv755wtNFwBQnTpXOgEAhRjTew6/38RmbT58+PA0bty4BuvfeOONdNNNN6UuXbqkORHcR3B5zjnnpGqx+OKLp3/84x/pmmuuSbvvvnud55Zffvn0yiuvpMsvvzyfXwCA1kBNN0CFbLXVVunDDz+ssyyxxBJp/vnnTz179qx08lqtAQMG5MC6tr/97W9pwoQJae65565YugAAGiPoBqiQbt26pYUWWqjO0qlTpwbNy6N299RTT0377rtvDsYHDhyYLr744jr7ev/999Ouu+6a5p133hy077jjjrnZ84xETfBDDz2Uzj333Nw0OpbY/oorrsj7qO2WW27Jz9dvfn3VVVfltPXu3TvXOn/xxRc120yfPj2NHTs2FyJ07949rbzyyumPf/xjnf3++c9/Tt/73vfy85tssslM01vbnnvumdMex1x22WWX5fWdO9dtwPXee+/lczHPPPOkXr165XP00Ucf1dnmtNNOSwsuuGA+t/vtt1/673//2+A9L7300rTccsulueaaKy277LLpt7/9bZPSCgAg6AaoAr/61a/S6quvnp577rl00EEHpf/93/9Nr732Wn7um2++SVtuuWUOGh955JH02GOP5SAzatKnTp3a6P4i2F5nnXXS/vvvX1PLHjXITfXWW2/lYPz222/PSwTBEbyWRcB95ZVXpgsvvDC9/PLL6cgjj0w/+tGP8nYhAuadd945bb/99rkv9I9//OM0cuTIJr13BMhxvOXm+ZMnT07XXXddLpSoLQL/CLg/++yz/L733ntvevvtt9Nuu+1Ws83111+fCxGiUOPpp59OCy+8cIOA+ve//3362c9+ln7xi1+kv//973nbUaNGNdo9AACgPn26ASokgtUIjsu23nrrdMMNNzS67TbbbJOD7XDcccels88+Oz3wwANpmWWWyQFnBJhRG1uukY7m11FjHYOTbbHFFg32F7XTXbt2TT169Mg17M0V7xe14uVm8HvttVe6//77c2A6ZcqUHJjed999ObAPSy65ZHr00UfTRRddlDbaaKN0wQUXpEGDBuXChBDH8eKLL6bTTz+9Se8fAfZRRx2VTjjhhFyDHvuqP/hZpCf2+c4779QUKERBQPT9fuqpp9Iaa6yR+7NH7XYs4ec//3lOd+3a7tGjR+d0RiFBiNr76DsexzJs2LBmnzsAoH1R0w1QIdGkOmp5y8t55503w21XWmmlmv9HYB2B8scff5wfjx8/Pr355ps5AI4gPpZoYh6BY9RIR+13eX0sUXP7XUWz8tr9zqOGuJyeSEvUPn//+9+v874R8EZ6QtQYr7XWWnX2WQ7Qm2LbbbdNX375ZXr44Ydz0/L6tdzl94hgu3YN/uDBg3NhRDzXlHR89dVXOc0RlNc+lgjOy8cCADAzaroBKiQG/VpqqaWatG390cwj8I7a5hDB55AhQxoNphdYYIFco117Oqtonj0jHTt2TKVSqc66aL7e3PSEO+64Iy2yyCIN+rG3hOi7HbXrUQv9xBNPpJtvvjkVoXwsl1xySYPgPPrfAwDMiqAboMqtttpquYl5v3798mBhjWksuI9gfNq0aQ2C9BgQLWp4yyOBN3f+6ahNjuA6BjGLpuSNiUHJbrvttgYjkDdH1G6feeaZuY/2fPPN1+h7RN/xWMq13dEsPOYBjzSWt4mgfe+99240HVFA0b9//9wXPAZqAwBoLkE3QJWLYPCMM87Ig4adfPLJadFFF81zWcd838cee2x+PKMm4hFwxqjh5SbpUZsb/bx/+tOfpsMOOyw/H323myOanR999NF58LSo/V5//fXTxIkT8wBvUSgQ/aAPPPDA3E/6mGOOyYOoPfPMM81+nwiYP/3005zexmy++eZpxRVXzOcn+m5/++23uV98FATEoHTh8MMPzyO5x+P11lsvtxaIgd+iD3rZSSedlM9F9IOPwemiz3oMuvb555+nESNGNCvNAED7o083QJWLoDP6NsdUYjHYVwSj5amvZlTzHSIwjibSUesbNdxRMx2B99VXX52n84qA9ZprrsmjezfXKaeckkf4jlHMIz0RrEZz8xiELERab7zxxjwCekwnFqOcx+BrzdWnT5885Vhjosn7rbfemmvBN9xwwxyERzAdrQLKopY80hmFE9FEPworYmT42qJQIAapi8Hp4pxE0B4FBOVjAQCYmQ6l+p33qsikSZNyzUPUoMzsxrIixvRuof1MbJn9QEEWH3lHi+zn3bn2aPZr/jvPgPTOer9KSyyyQJqrc4eU+q/aImmBlhSFHzGCegTpMc931eRjc0ARx9/hpP83p3xrVRpdtbdeAMxGPqamGwAAAAoi6AYAAICCCLoBAACgIIJuAAAAKIigGwAAAAoi6AaqV2l6/JOmGwiYVizmKgcA2q/OlU4AwOzqOvmj1PHrz9IHn/dKC/SeK3X9+us8NzO0BjEj59SpU9Mnn3ySOnbsmLp27VrpJAEAFSDoBqpWx9K3aYknR6UPl903fbDAKin9t3ulkwQN9OjRIw0cODAH3gBA+yPoBqpa1/9+mgY+f0b6tmuvNG3E65VODtTRqVOn1LlzZy0wAKAdE3QDVa9DKqUuUyemLnPNVemkAABAHdq6AQAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcA8J385je/SYsvvniaa6650lprrZWefPLJSicJAFoNQTcAMNuuu+66NGLEiDR69Oj07LPPppVXXjltueWW6eOPP6500gCgVehc6QQAANXrrLPOSvvvv3/aZ5998uMLL7ww3XHHHemyyy5LI0eObNI+pk2blpcWMT21ei12rABUxfW8c6UTOWbMmHT11VenCRMmpP79+6fhw4enE088MXXo0KGSSQMAZmHq1KnpmWeeSccff3zNuo4dO6bNN988Pf744w22nzJlSl7KJk2alP/ec889qUePHi2TqDdSq/fnP/+50kkAoAVMnjy59Qfdp59+errgggvSuHHj0vLLL5+efvrpXFLeu3fvdNhhh1UyaQDALHz66ae5AH3BBRessz4ev/rqqw22Hzt2bDrppJMKTdNtP7yt0P3Tzu2wQ2rVbvP9h9aookH3X//617TjjjumbbfdNj+OQViuueYaA7AAQBsUNeLR/7t2TfeAAQPSFltskXr16lXRtEGbsM02lU4BtCuT/v8WW6066F533XXTxRdfnF5//fX0ve99L40fPz49+uijuX8YANC69e3bN3Xq1Cl99NFHddbH44UWWqjB9t26dctLfbGPWIDvyO8I5qim5l0VDbpjgJUoHVh22WVzgqOJ2i9+8Yu05557Nrr9jPqCAQBzXteuXdOQIUPS/fffn4YOHZrXTZ8+PT8+5JBDKp08AGgVKhp0X3/99en3v/99+sMf/pD7dD///PPpiCOOyAOqDRs2rCJ9wRYfeUeL7OfduVLFtdyx7NEi+0ljJs72Sx1LQ++e9n/dMmg5vmcNOZaWPZa2KJqLR569+uqrpzXXXDOdc8456auvvqoZzRwA2ruKBt3HHHNMru3efffd8+MVV1wx/eMf/8jBdWNB94z6ggEAlbHbbrulTz75JP3sZz/LM5Gsssoq6a677mowuBoAtFedKz3EekwtUls0M4+maY2ZUV8wAKByoim55uQA0AqD7u233z734R44cGBuXv7cc8/lQdT23XffSiYLAAAAqj/oPv/889OoUaPSQQcdlD7++OPcl/snP/lJbqIGAAAA1a6iQXfPnj3zgCuxAAAAQFtTt0M1AAAA0GIE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwAAQEEE3QAAAFAQQTcAAAAURNANAAAABRF0AwDN9u6776b99tsvLbHEEql79+5p0KBBafTo0Wnq1KmVThoAtCqdK50AAKD6vPrqq2n69OnpoosuSksttVR66aWX0v7775+++uqrdOaZZ1Y6eQDQagi6AYBm22qrrfJStuSSS6bXXnstXXDBBYJuAKhF0A0AtIiJEyem+eeff4bPT5kyJS9lkyZNmkMpA4DK0acbAPjO3nzzzXT++eenn/zkJzPcZuzYsal37941y4ABA+ZoGgGgEgTdAECNkSNHpg4dOsx0if7ctf3rX//KTc1/8IMf5H7dM3L88cfn2vDy8v7778+BIwKAytK8HACocdRRR6Xhw4fPdJvov132wQcfpE022SStu+666eKLL57p67p165YXAGhPBN0AQI0FFlggL00RNdwRcA8ZMiRdfvnlqWNHDegAoL6K546RYf/oRz9Kffr0yfN8rrjiiunpp5+udLIAgFnk3xtvvHEaOHBgHq38k08+SRMmTMgLANBKaro///zztN566+VS8jvvvDOXrL/xxhtpvvnmq2SyAIBZuPfee/PgabEsuuiidZ4rlUoVSxcAtDYVDbpPP/30PHJpNEkrW2KJJSqZJACgCaLf96z6fgMAFW5eftttt6XVV189j3bar1+/tOqqq6ZLLrlkhtvH3J4xp2ftBQAAAFqrigbdb7/9drrgggvS0ksvne6+++70v//7v+mwww5L48aNa3R783sCAABQTSoadE+fPj2tttpq6dRTT8213AcccECe3/PCCy9sdHvzewJAQw8//HD69ttvG6yPdfEcANBOg+6FF144DR48uM665ZZbLr333nuNbh9ze/bq1avOAgDtXQxI+tlnnzVYHwXU8RwA0E6D7hi5/LXXXquz7vXXX0+LLbZYxdIEANUmRgvv0KFDg/X//ve/09xzz12RNAEArWD08iOPPDKtu+66uXn5rrvump588sl08cUX5wUAmLmdd945/42AO0YSjxZhZdOmTUsvvPBCzmcBgHYadK+xxhrp5ptvzn21Tz755Dxd2DnnnJP23HPPSiYLAKpCDCparunu2bNn6t69e81zXbt2TWuvvXYeKwUAaKdBd9huu+3yAgA0z+WXX57/Lr744unoo4/WlBwAWqGKB90AwHczevToSicBAGiNA6kBAN/dRx99lPbaa6/Uv3//1Llz59SpU6c6CwBQOWq6AaDKxSBqMd3mqFGj8nScjY1kDgBUhqAbAKrco48+mh555JG0yiqrVDopAEA9mpcDQJUbMGBAHsEcAGh9BN0AUOVius2RI0emd999t9JJAQDq0bwcAKrcbrvtliZPnpwGDRqUevTokbp06VLn+c8++6xiaQOA9k7QDQBtoKYbAGidBN0AUOWGDRtW6SQAADOgTzcAtAFvvfVWOvHEE9MPf/jD9PHHH+d1d955Z3r55ZcrnTQAaNcE3QBQ5R566KG04oorpieeeCLddNNN6csvv8zrx48fn0aPHl3p5AFAuyboBoAqFyOX//znP0/33ntv6tq1a836TTfdNP3tb3+raNoAoL0TdANAlXvxxRfTTjvt1GB9v3790qefflqRNAEA/0fQDQBVbt55500ffvhhg/XPPfdcWmSRRSqSJgDg/wi6AaDK7b777um4445LEyZMSB06dEjTp09Pjz32WDr66KPT3nvvXenkAUC7JugGgCp36qmnpmWXXTYNGDAgD6I2ePDgtOGGG6Z11103j2gOAFSOeboBoMrF4GmXXHJJGjVqVHrppZdy4L3qqqumpZdeutJJA4B2T9ANAG3EwIED8wIAtB6CbgCocqVSKf3xj39MDzzwQPr4449zn+7aYu5uAKAyBN0AUOWOOOKIdNFFF6VNNtkkLbjggnkwNQCgioPub775Jo+QOnny5LTAAguk+eefv+VTBgA0yVVXXZVrs7fZZptKJwUAmN3Ry7/44ot0wQUXpI022ij16tUrLb744mm55ZbLQfdiiy2W9t9///TUU081dXcAQAvp3bt3WnLJJSudDABgdoPus846KwfZl19+edp8883TLbfckp5//vn0+uuvp8cffzyNHj06ffvtt2mLLbZIW221VXrjjTeaslsAoAWMGTMmnXTSSenrr7+udFIAgNlpXh412A8//HBafvnlG31+zTXXTPvuu2+68MILc2D+yCOPmKYEAOaQXXfdNV1zzTWpX79+uZC8S5cudZ5/9tlnK5Y2AGjvmhR0R0beFN26dUsHHnjgd00TANAMw4YNS88880z60Y9+ZCA1AGgro5dPmTKlJtAGACrnjjvuSHfffXdaf/31K50UAGB2B1IL9957bx4Zdb755ks9evTIS/w/1t13333N2RUA0EIGDBiQBzkFAKo46B43blwOrmOE1LPPPjvdfvvteYn/zzvvvPm5mLIEAJizfvWrX6Vjjz02vfvuu5VOCgAwu83Lf/GLX6RzzjknHXzwwQ2eGz58eG7SdvLJJ6e99tqrqbsEAFpA9OWePHlyGjRoUG6FVn8gtc8++6xiaQOA9q7JQfd7772Xpwubkc022ywdddRRLZUuAKCJolAcAKjyoDumC/vd736XfvnLXzb6/GWXXZYGDx7ckmkDAJo4ejkAUOVBd/QX22677dJdd92Va7xjSpLw0Ucfpfvvvz+9/fbbefRUAGDOe+utt9Lll1+e/5577rl5zu4777wzDRw4MBecAwCtfCC1jTfeOL300ktp6623znOBRs12LPH/WPfiiy+mDTfcsNjUAgANPPTQQ2nFFVdMTzzxRLrpppvSl19+mdePHz8+jR49utLJA4B2rVnzdC+++OLp9NNPLy41AECzjRw5Mv385z9PI0aMSD179qxZv+mmm6Zf//rXFU0bALR3zZqnGwBofaK12U477dRgfTQx//TTTyuSJgCghYPuaMLWqVOnltodANBE8847b/rwww8brH/uuefSIossUpE0AQAF1HSXSqWW3B0A0AS77757Ou6449KECRNShw4d0vTp09Njjz2Wjj766LT33ntXOnkA0K41uU/3zjvvPNPnJ06cmDN6AGDOOvXUU9PBBx+cBgwYkKZNm5an8Iy/e+yxRzrxxBMrnTwAaNeaHHT/6U9/St///vdrpgqrLzJ3AGDO69q1a7rkkkvSqFGj8kwjMXr5qquumpZeeulKJw0A2r0mB93LLbdc2mWXXdJ+++3X6PPPP/98uv3221sybQBAEzz66KNp/fXXz3NyxwIAVGGf7iFDhqRnn312hs9369ZNRg8AFRBTgy2xxBLppz/9aXrllVcqnRwAYHaC7gsvvDCdccYZM60Jf+edd5q6OwCghXzwwQfpqKOOSg899FBaYYUV0iqrrJLz7H/+85+VThoAtHtNDrqjJrtHjx7FpgYAaLa+ffumQw45JI9Y/tZbb6Uf/OAHady4cWnxxRfPteAAQJVOGbbttts2Oi8oAFAZ0cx85MiR6bTTTksrrrhirv0GAKo06H744YfT119/3XKpAQBmW9R0H3TQQWnhhRfO04VFU/M77rij0skCgHatyaOXAwCt0/HHH5+uvfba3Lc7pvc899xz04477qhbGABUe9C92GKLpS5durRcagCA2Wp5dswxx6Rdd9019+8GANpI0P3SSy+1XEoAgNluVg4AtJGg+8knn0yPP/54mjBhQn680EILpXXWWSetueaaRaQPAGiCGLX8nHPOSX//+9/z48GDB6fDDz88DRo0qNJJA4B2rclB98cff5x22WWXXJo+cODAtOCCC+b1H330UTryyCPTeuutl2688cbUr1+/ItMLANRz9913px122CHPzx35cYj8evnll09/+tOfcj9vAKCVB90xGuq0adNyCfoyyyxT57nXXnst7bvvvunggw9ON9xwQxHpBABmIKYIiwLwmCas/vrjjjtO0A0A1TBlWJSi/+Y3v2kQcIdYd95556W77rqrpdMHAMxCFIjvt99+DdZHgfgrr7xSkTQBAM0Murt165YmTZo0w+e/+OKLvA0AMGctsMAC6fnnn2+wPtbp9gUAVdK8fLfddkvDhg1LZ599dtpss81Sr1698voIxO+///40YsSI9MMf/rDItAIAjdh///3TAQcckN5+++207rrr1vTpPv3003P+DABUQdB91llnpenTp6fdd989ffvtt6lr1655/dSpU1Pnzp1zs7YzzzyzyLQCAI0YNWpU6tmzZ/rVr36Vjj/++Lyuf//+acyYMemwww6rdPIAoF1rctAdTccvuOCCXGr+zDPP1JkybMiQITU13wDAnBMF4X/4wx/SHnvskQdTi+5eIYJwAKAK5+mO4HqTTTYpJjUAQLNEa7MDDzywZn5uwTYAVOFAatdee22Td/j+++/nfmQAwJyx5pprpueee67SyQAAZjfojmblyy23XPrlL39ZU5Je28SJE9Of//zn3LRttdVWS//+97+bslsAoAUcdNBB6aijjkq//vWv0+OPP55eeOGFOgsA0Mqblz/00EPptttuS+eff34eoGXuuedOCy64YJprrrnS559/nvt39+3bNw0fPjy99NJL+TkAYM6IQU5D7UHTOnTokEqlUv47bdq0CqYOANq3Jvfp3mGHHfLy6aefpkcffTT94x//SF9//XUOtlddddW8dOzY5Gm/AYAW8s4771Q6CQBASw2kFkH20KFDm/syAKAgiy22WKWTAAC0VNANALQ+r732Wu4GVh57JcZiOfTQQ9MyyyxT6aQBQLumPTgAVLkbb7wxrbDCCumZZ55JK6+8cl6effbZvC6eAwAqR003AFS5Y489Ng90evLJJ9dZP3r06PzcLrvsUrG0AUB7p6YbAKrchx9+mPbee+8G63/0ox/l5wCAKgy6p06dmvuPffvtty2bIgCgWTbeeOP0yCOPNFgfs41ssMEGFUkTADCbzcsnT56cB2YZN25cfvz666+nJZdcMq9bZJFF0siRI5u7SwDgO4gpPY877rjcp3vttdfO6/72t7+lG264IZ100knptttuq7MtANCKg+7oMzZ+/Pj04IMPpq222qpm/eabb57GjBkj6AaAOeyggw7Kf3/729/mpbHnQocOHdK0adPmePoAoD1rdtB9yy23pOuuuy6XpEfmXbb88sunt956q6XTBwDMwvTp0yudBACgpfp0f/LJJ6lfv34N1n/11Vd1gnAAAABo75pd07366qunO+64I/fhDuVA+9JLL03rrLNOy6cQAJilp556Kj3wwAPp448/blDzfdZZZ1UsXQDQ3jU76D711FPT1ltvnV555ZU8cvm5556b///Xv/41PfTQQ8WkEgCYad584oknpmWWWSYtuOCCdVqeaYUGAFUWdK+//vp5ILWxY8emFVdcMd1zzz1ptdVWS48//nh+DADMWVEAftlll6Xhw4dXOikAwHcJur/55pv0k5/8JI0aNSpdcsklzXkpAFCQjh07pvXWW6/SyQAAvutAal26dEk33nhjc14CABTsyCOPTL/5zW8qnQwAoCWalw8dOjRPGxYZPABQeUcffXTadttt06BBg9LgwYNzIXltN910U8XSBgDtXbOD7qWXXjqdfPLJ6bHHHktDhgxJc889d53nDzvssJZMHwAwC5H3xsjlm2yySerTp4/B0wCgmoPu3/3ud2neeedNzzzzTF5qi0xe0A0Ac9a4ceNy96+o7QYAqjzofuedd4pJCQAwW+aff/7ctBwAqPKB1OorlUp5AQAqZ8yYMWn06NFp8uTJlU4KANASQfeVV16Z5+Tu3r17XlZaaaV01VVXzc6uAIDv6Lzzzkt33nlnWnDBBXP+vNpqq9VZAIAqal5+1lln5Xm6DznkkJo5QR999NF04IEHpk8//dSo5gAwh8XMIgBAGwm6zz///HTBBRekvffeu2bdDjvskJZffvncvE3QDQBzVjQtr6QpU6aktdZaK40fPz4999xzaZVVVqloegCgqpuXf/jhh2nddddtsD7WxXMAQPty7LHHpv79+1c6GQDQNoLupZZaKl1//fUN1l933XV5Dm8AYM6Yb7758sjls1qKFH3J77nnnnTmmWcW+j4A0G6al5900klpt912Sw8//HBNn+7HHnss3X///Y0G4wBAMc4555yKvv9HH32U9t9//3TLLbekHj16NKkZeixlkyZNKjiFAFCFQfcuu+ySnnjiiXT22WfnTDYst9xy6cknn0yrrrpqEWkEABoxbNiwir13TBk6fPjwPJDq6quvnt59991Zvmbs2LG58B4A2pNmB91hyJAh6eqrr2751AAAFTVy5Mh0+umnz3Sbv//977lJ+RdffJGOP/74Ju87th0xYkSdmu4BAwZ8p/QCQJsLuv/85z+nTp06pS233LLO+rvvvjtNnz49bb311i2ZPgBgDjrqqKNyDfbMLLnkkukvf/lLevzxx1O3bt3qPBe13nvuuWcaN25cg9fFtvW3B4C2rvPslICfdtppjTYzi+cE3QBQvRZYYIG8zMp5552Xfv7zn9c8/uCDD3KBfAysGtOHAQCzGXS/8cYbafDgwQ3WL7vssunNN99s7u4AgCo0cODAOo/nmWee/HfQoEFp0UUXrVCqAKANTBnWu3fv9PbbbzdYHwH33HPP3VLpAgCaaerUqem1115L3377baWTAgDMbtC94447piOOOCK99dZbdQLu6AO2ww47NHd3AMB3NHny5LTffvvlabuWX3759N577+X1hx56aKNdwoqw+OKL565mq6yyyhx5PwBos0H3L3/5y1yjHc3Jl1hiibzElGF9+vRJZ555ZjGpBABmOir4+PHj04MPPpjmmmuumvWbb7557mMNAFRRn+5oXv7Xv/413XvvvTmD7969e1pppZXShhtuWEwKAYCZuuWWW3Jwvfbaa6cOHTrUrI9a79ot0wCAKqjpDpGhb7HFFumYY45JhxxySIsE3NH8LfYbTdcBgKb75JNPUr9+/Rqs/+qrr+oE4QBAKw66Yy7O22+/vc66K6+8Mjcvj4z+gAMOSFOmTJmtRDz11FPpoosuyjXmAEDzxNzYd9xxR83jcqB96aWXpnXWWaeCKQMAmty8/OSTT04bb7xx2m677fLjF198MQ/aMnz48Nyn+4wzzkj9+/dPY8aMaVYCvvzyy7TnnnumSy65pM58nwBA05x66qlp6623Tq+88koeufzcc8/N/4/uYA899FClkwcA7VqTa7qff/75tNlmm9U8vvbaa9Naa62Vg+URI0ak8847L11//fXNTsDBBx+ctt122zzYy6xETfqkSZPqLADQ3q2//vp5nJUIuFdcccV0zz335FZo0UptyJAhlU4eALRrTa7p/vzzz9OCCy5Y8zhKzqNUvWyNNdZI77//frPePAL3Z599Njcvb4qxY8emk046qVnvAQBt2TfffJN+8pOfpFGjRuWCcACgSmu6I+B+55138v+nTp2ag+UYJbXsiy++SF26dGnyG0eAfvjhh6ff//73daY3mdWUKBMnTqxZmhvkA0BbE3nvjTfeWOlkAADfNejeZptt0siRI9MjjzySg98ePXqkDTbYoOb5F154IQ0aNKipu0vPPPNM+vjjj9Nqq62WOnfunJeoPY9m6vH/adOmNXhNt27dUq9eveosANDeDR06NE8bBgBUcfPyU045Je28885po402SvPMM08aN25c6tq1a83zl112WZ5GrKmif3gMxlbbPvvsk5Zddtl03HHHpU6dOjV5XwDQni299NJ5wNPHHnss9+Gee+656zx/2GGHVSxtANDeNTno7tu3b3r44Ydzs+4IuusHxTfccENe31Q9e/ZMK6ywQp11cZPQp0+fBusBgBn73e9+l+add97ciiyW2mL6MEE3AFRB0F3Wu3fvRtfPP//8LZEeAKCZymOuAABtIOgu0oMPPljpJABAVSuVSjU13ABAFQ2kBgC0XldeeWWeo7t79+55WWmlldJVV11V6WQBQLvXqmq6AYDmO+uss/I83Yccckhab7318rpHH300HXjggenTTz9NRx55ZKWTCADtlqAbAKrc+eefny644IK0995716zbYYcd0vLLL5/GjBkj6AaACtK8HACq3IcffpjWXXfdButjXTwHAFSOoBsAqtxSSy2Vrr/++gbrr7vuujyHNwBQOZqXA0CVO+mkk9Juu+2WHn744Zo+3Y899li6//77Gw3GAYA5R003AFS5XXbZJT3xxBOpb9++6ZZbbslL/P/JJ59MO+20U6WTBwDtmppuAGgDhgwZkq6++upKJwMAqEdNNwBUuT//+c/p7rvvbrA+1t15550VSRMA8H8E3QBQ5UaOHJmmTZvWYH2pVMrPAQCVI+gGgCr3xhtvpMGDBzdYv+yyy6Y333yzImkCAP6PoBsAqlzv3r3T22+/3WB9BNxzzz13RdIEAPwfQTcAVLkdd9wxHXHEEemtt96qE3AfddRRaYcddqho2gCgvRN0A0CV++Uvf5lrtKM5+RJLLJGX5ZZbLvXp0yedeeaZlU4eALRrpgwDgDbQvPyvf/1ruvfee9P48eNT9+7d00orrZQ23HDDSicNANo9QTcAtAEdOnRIW2yxRV4AgNZD83IAqFKPP/54uv322+usu/LKK3Pz8n79+qUDDjggTZkypWLpAwAE3QBQtU4++eT08ssv1zx+8cUX03777Zc233zzPD/3n/70pzR27NiKphEA2jtBNwBUqeeffz5tttlmNY+vvfbatNZaa6VLLrkkjRgxIp133nnp+uuvr2gaAaC9E3QDQJX6/PPP04ILLljz+KGHHkpbb711zeM11lgjvf/++xVKHQAQBN0AUKUi4H7nnXfy/6dOnZqeffbZtPbaa9c8/8UXX6QuXbpUMIUAgKAbAKrUNttsk/tuP/LII+n4449PPXr0SBtssEHN8y+88EIaNGhQRdMIAO2dKcMAoEqdcsopaeedd04bbbRRmmeeedK4ceNS165da56/7LLLTCEGABUm6AaAKtW3b9/08MMPp4kTJ+agu1OnTnWev+GGG/J6AKByBN0AUOV69+7d6Pr5559/jqcFAKhLn24AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AYLbdcccdaa211krdu3dP8803Xxo6dGilkwQArUrnSicAAKhON954Y9p///3TqaeemjbddNP07bffppdeeqnSyQKAVkXQDQA0WwTYhx9+eDrjjDPSfvvtV7N+8ODBFU0XALQ2mpcDAM327LPPpn/961+pY8eOadVVV00LL7xw2nrrrdV0A0A9gm4AoNnefvvt/HfMmDHpxBNPTLfffnvu073xxhunzz77rNHXTJkyJU2aNKnOAgBtnaAbAKgxcuTI1KFDh5kur776apo+fXre/oQTTki77LJLGjJkSLr88svz8zfccEOj+x47dmzq3bt3zTJgwIA5fHQAMOfp0w0A1DjqqKPS8OHDZ7rNkksumT788MMGfbi7deuWn3vvvfcafd3xxx+fRowYUfM4aroF3gC0dYJuAKDGAgsskJdZiZrtCLJfe+21tP766+d133zzTXr33XfTYost1uhrYvtYAKA9EXQDAM3Wq1evdOCBB6bRo0fn2uoItGMk8/CDH/yg0skDgFZD0A0AzJYIsjt37pz22muv9PXXX6e11lor/eUvf8kDqgEA/0fQDQDMli5duqQzzzwzLwBA44xeDgAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAADQFoPusWPHpjXWWCP17Nkz9evXLw0dOjS99tprlUwSAAAAtI2g+6GHHkoHH3xw+tvf/pbuvffe9M0336QtttgiffXVV5VMFgAAALSIzqmC7rrrrjqPr7jiilzj/cwzz6QNN9ywYukCAACANtene+LEifnv/PPPX+mkAAAAQHXXdNc2ffr0dMQRR6T11lsvrbDCCo1uM2XKlLyUTZo0aQ6mEAAAAKq0pjv6dr/00kvp2muvnenAa717965ZBgwYMEfTCAAAAFUXdB9yyCHp9ttvTw888EBadNFFZ7jd8ccfn5ugl5f3339/jqYTAAAAqqZ5ealUSoceemi6+eab04MPPpiWWGKJmW7frVu3vAAAAEA16FzpJuV/+MMf0q233prn6p4wYUJeH03Hu3fvXsmkAQAAQHU3L7/gggtyM/GNN944LbzwwjXLddddV8lkAQAAQNtoXg4AAABtVasYSA0AAADaIkE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3AAAAFETQDQAAAAURdAMAAEBBBN0AAABQEEE3ADBbXn/99bTjjjumvn37pl69eqX1118/PfDAA5VOFgC0KoJuAGC2bLfddunbb79Nf/nLX9IzzzyTVl555bxuwoQJlU4aALQagm4AoNk+/fTT9MYbb6SRI0emlVZaKS299NLptNNOS5MnT04vvfRSpZMHAK2GoBsAaLY+ffqkZZZZJl155ZXpq6++yjXeF110UerXr18aMmRIpZMHAK1G50onAACoPh06dEj33XdfGjp0aOrZs2fq2LFjDrjvuuuuNN988zX6milTpuSlbNKkSXMwxQBQGWq6AYAa0Vw8AuqZLa+++moqlUrp4IMPzoH2I488kp588skcgG+//fbpww8/bHTfY8eOTb17965ZBgwYMMePD76TUql1L0CrpKYbAKhx1FFHpeHDh890myWXXDIPnnb77benzz//PI9cHn7729+me++9N40bNy4H7/Udf/zxacSIEXVqugXeALR1gm4AoMYCCyyQl1mJAdNCNCuvLR5Pnz690dd069YtLwDQnmheDgA02zrrrJP7bg8bNiyNHz8+z9l9zDHHpHfeeSdtu+22lU4eALQagm4AoNn69u2bB0378ssv06abbppWX3319Oijj6Zbb701z9cNAPwfzcsBgNkSgfbdd99d6WQAQKumphsAAAAKIugGAACAggi6AQAAoCCCbgAAACiIoBsAAAAKIugGAACAggi6AQAAoCCCbgAAACiIoBsAAAAKIugGAACAggi6AQAAoCCCbgAAAGjLQfdvfvObtPjii6e55porrbXWWunJJ5+sdJIAAACg+oPu6667Lo0YMSKNHj06Pfvss2nllVdOW265Zfr4448rnTQAAACo7qD7rLPOSvvvv3/aZ5990uDBg9OFF16YevTokS677LJKJw0AAACqN+ieOnVqeuaZZ9Lmm2/+/xLUsWN+/Pjjj1cyaQAAAPCddU4V9Omnn6Zp06alBRdcsM76ePzqq6822H7KlCl5KZs4cWL+O2nSpBZL0/Qpk1tkP5M6lFpkP+k7HJtjaePH0oLf+++iVX023/GctKpjaS3fM8fSyI5a7rdX/h2XSi2UtipTPu7Wcj0DgCLy8YoG3c01duzYdNJJJzVYP2DAgNTa9G6pHZ3WYnuabY6llR7LOalN6d1GPpc29z1rqR05lpn64osvUu/elT9Hc1ocd2vNxwGgpfLxigbdffv2TZ06dUofffRRnfXxeKGFFmqw/fHHH58HXSubPn16+uyzz1KfPn1Shw4dUmsq8YgbiPfffz/16tUrVTPH0jq1pWNpa8fjWFqn1nosUTIeGXX//v1TexTHHZ9Jz549W1U+3tq/N+35WNrKcbSlY2krxxEcS+szqZUfR1Pz8YoG3V27dk1DhgxJ999/fxo6dGhNIB2PDznkkAbbd+vWLS+1zTvvvKm1ii9Ga/xyzA7H0jq1pWNpa8fjWFqn1ngs7bGGu/Y4Losuumhq7Vrj96a9H0tbOY62dCxt5TiCY2l9erXi42hKPl7x5uVRcz1s2LC0+uqrpzXXXDOdc8456auvvsqjmQMAAEA1q3jQvdtuu6VPPvkk/exnP0sTJkxIq6yySrrrrrsaDK4GAAAA1abiQXeIpuSNNSevVtEEfvTo0Q2awlcjx9I6taVjaWvH41hap7Z0LMw5bel701aOpa0cR1s6lrZyHMGxtD7d2shxdCi113lKAAAAoGAdi34DAAAAaK8E3QAAAFAQQTcAAAAURNDdgh5++OG0/fbb58nRO3TokG655ZZUrcaOHZvWWGON1LNnz9SvX788j/prr72WqtEFF1yQVlpppZr5/dZZZ5105513prbgtNNOy9+1I444IlWbMWPG5LTXXpZddtlUrf71r3+lH/3oR6lPnz6pe/fuacUVV0xPP/10qkaLL754g88mloMPPjhVm2nTpqVRo0alJZZYIn8ugwYNSqecckoynAntJU9vK/l5W83L5eOtR1vIx+XfrVerGL28rYj5xVdeeeW07777pp133jlVs4ceeij/QCOj/vbbb9NPf/rTtMUWW6RXXnklzT333KmaLLroojlTW3rppfMPddy4cWnHHXdMzz33XFp++eVTtXrqqafSRRddlG9CqlWc//vuu6/mcefO1XlJ+vzzz9N6662XNtlkk3wTuMACC6Q33ngjzTfffKlav1uR2ZW99NJL6fvf/376wQ9+kKrN6aefnm/W43cf37e4gdpnn31S796902GHHVbp5NGKtZU8va3k520xL5ePtx5tJR+Xf7diMXo5LS9O7c0331xqKz7++ON8TA899FCpLZhvvvlKl156aalaffHFF6Wll166dO+995Y22mij0uGHH16qNqNHjy6tvPLKpbbguOOOK62//vqltiq+X4MGDSpNnz69VG223Xbb0r777ltn3c4771zac889K5Ymqk9bytPbUn5ezXm5fLx1aav5uPy79dC8nCaZOHFi/jv//POnahalf9dee22uwYimadUqai223XbbtPnmm6dqFqXI0XRzySWXTHvuuWd67733UjW67bbb0uqrr55LkqP55qqrrpouueSS1BZMnTo1XX311bm2L5qoVZt111033X///en111/Pj8ePH58effTRtPXWW1c6aVARbSE/bwt5uXy8dWmL+bj8u3WpzjYgzFHTp0/PfY2i2c0KK6yQqtGLL76YM+b//ve/aZ555kk333xzGjx4cKpGcaPx7LPP5iZE1WyttdZKV1xxRVpmmWXShx9+mE466aS0wQYb5KZQ0fewmrz99tu5CdSIESNy0834bKLpU9euXdOwYcNSNYt+rP/5z3/S8OHDUzUaOXJkmjRpUu5n2KlTp3yz/otf/CLfHEJ7U+35eVvJy+XjrU9bzMfl361Mpava26q21BTtwAMPLC222GKl999/v1StpkyZUnrjjTdKTz/9dGnkyJGlvn37ll5++eVStXnvvfdK/fr1K40fP75mXbU2S6vv888/L/Xq1asqmwp26dKltM4669RZd+ihh5bWXnvtUrXbYostStttt12pWl1zzTWlRRddNP994YUXSldeeWVp/vnnL11xxRWVThpVpK3k6dWen7eFvFw+3jq1xXxc/t26CLoL0lYy6IMPPjh/4d9+++1SW7LZZpuVDjjggFK1ie9UfLc6depUs8TjDh065P9/++23pWq2+uqr5xupajNw4MDSfvvtV2fdb3/721L//v1L1ezdd98tdezYsXTLLbeUqlVcv37961/XWXfKKaeUlllmmYqlierTFvL0tpifV2NeLh9vndpaPi7/bn00L6dRcY9x6KGH5qZbDz74YB6uv601sZsyZUqqNptttlluXldbjOQYTW+OO+643PymWn355ZfprbfeSnvttVeqNtFUs/4UPNEHabHFFkvV7PLLL89926LfYbWaPHly6tix7vAl8TuJawC0B205P6/GvFw+3jq1tXxc/t36CLpb+GLz5ptv1jx+55130vPPP58HKxk4cGCqtgE+/vCHP6Rbb70198uZMGFCXh/D9MdcedXk+OOPz4MuxGfwxRdf5OOKG4+77747VZv4LOr3w4spX2JOyWrrn3f00UfnOXAjQ/vggw/S6NGj88X0hz/8Yao2Rx55ZB7w49RTT0277rprevLJJ9PFF1+cl2oVmVpk2tGXrVqngAnxHYs+YPH7jylHYnqhs846Kw8sA+0hT28r+Xlbycvl461TW8rH5d+tVKWr2tuSBx54IDcRqr8MGzasVG0aO45YLr/88lK1iekGog9b165dSwsssEBujnbPPfeU2opq7Qu22267lRZeeOH8uSyyyCL58ZtvvlmqVn/6059KK6ywQqlbt26lZZddtnTxxReXqtndd9+df/OvvfZaqZpNmjQp/z6i6eBcc81VWnLJJUsnnHBC7hsK7SFPbyv5eVvOy+XjrUNbycfl361Th/in0oE/AAAAtEXm6QYAAICCCLoBAACgIIJuAAAAKIigGwAAAAoi6AYAAICCCLoBAACgIIJuAAAAKIigGwAAAAoi6AYAAICCCLqhjXr//ffTvvvum/r375+6du2aFltssXT44Yenf//735VOGgAwC/JxaDsE3dAGvf3222n11VdPb7zxRrrmmmvSm2++mS688MJ0//33p3XWWSd99tlnhb331KlTC9s3ALQH8nFoWwTd0AYdfPDBuVT8nnvuSRtttFEaOHBg2nrrrdN9992X/vWvf6UTTjghb9ehQ4d0yy231HntvPPOm6644oo6Je277rprXj///POnHXfcMb377rs1zw8fPjwNHTo0/eIXv8il8csss0w6+eST0worrNAgXausskoaNWpUoccOANVOPg5ti6Ab2pgo/b777rvTQQcdlLp3717nuYUWWijtueee6brrrkulUmmW+/rmm2/SlltumXr27JkeeeSR9Nhjj6V55pknbbXVVnVKwqPk/bXXXkv33ntvuv3223NzuL///e/pqaeeqtnmueeeSy+88ELaZ599WviIAaDtkI9D29O50gkAWlY0RYuMeLnllmv0+Vj/+eefp08++WSW+4pMffr06enSSy/Npenh8ssvz6XlDz74YNpiiy3yurnnnjtvE6XyZZHJx7ZrrLFGzeuitH7JJZdsoSMFgLZHPg5tj5puaKNmVQJeO2OdkfHjx+d+ZFFCHiXjsUTTtP/+97/prbfeqtluxRVXbLC//fffP/dDi22jNP0Pf/hDLjkHAGZNPg5th5puaGOWWmqpXJodzcJ22mmnBs/H+gUWWCCXcsd29TP1aIpW9uWXX6YhQ4ak3//+9w32E/soixLy+rbffvvUrVu3dPPNN+eMPPb7P//zPy1whADQdsnHoe0RdEMb06dPn/T9738//fa3v01HHnlknf5gEyZMyBlvDNBSznA//PDDOk3aJk+eXPN4tdVWy03T+vXrl3r16tWsdHTu3DkNGzYsN0eLzHr33Xdv0DcNAKhLPg5tj+bl0Ab9+te/TlOmTMn9sR5++OE8culdd92VM/Hvfe976Wc/+1nebtNNN83bxuAoTz/9dDrwwANTly5davYTg7X07ds3j3QaA7C88847uQ/YYYcdlv75z3/OMh0//vGP01/+8pf83pqkAUDTyMehbRF0Qxu09NJL5xFHY7CTmCZkscUWy1ONREZdHrk0/OpXv0oDBgxIG2ywQdpjjz3S0UcfnXr06FGzn/h/ZPYxVcnOO++cB2/Zb7/9cv+uppSYRzrWXXfdtOyyy6a11lqr0GMGgLZCPg5tS4dSU+YbAKre6NGj01lnnZWnA1l77bXnyHvG5SUy7Jj2ZMSIEXPkPQGgLZKPQ/USdEM7Ev2yJk6cmJuVdexYbEOXmMrk2muvTccff3xuFjfffPMV+n4A0NbJx6E6CbqBQsSIqtGP7Nxzz81N3gCA6iEfh5Yj6AYAAICCGEgNAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAACiLoBgAAgIIIugEAAKAggm4AAAAoiKAbAAAAUjH+P8sbCa8div6lAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Average Base Model Score: 8.12\n", "Average Fine-tuned Model Score: 7.25\n", "Average Improvement: -0.88 (-10.8%)\n" ] } ], "source": [ "# Visualize comparison results\n", "plt.figure(figsize=(10, 6))\n", "\n", "# Calculate improvement\n", "comparison_results['improvement'] = comparison_results['fine_tuned_score'] - comparison_results['base_score']\n", "\n", "# Bar chart comparing scores\n", "plt.subplot(1, 2, 1)\n", "x = np.arange(len(comparison_results))\n", "width = 0.35\n", "\n", "plt.bar(x - width/2, comparison_results['base_score'], width, label='Base Model')\n", "plt.bar(x + width/2, comparison_results['fine_tuned_score'], width, label='Fine-tuned Model')\n", "\n", "plt.xlabel('Query')\n", "plt.ylabel('Score (0-10)')\n", "plt.title('Base vs Fine-tuned Model Performance')\n", "plt.xticks(x, range(1, len(comparison_results) + 1))\n", "plt.legend()\n", "\n", "# Improvement chart\n", "plt.subplot(1, 2, 2)\n", "colors = ['green' if x > 0 else 'red' for x in comparison_results['improvement']]\n", "plt.bar(range(1, len(comparison_results) + 1), comparison_results['improvement'], color=colors)\n", "plt.axhline(y=0, color='k', linestyle='-', alpha=0.3)\n", "plt.xlabel('Query')\n", "plt.ylabel('Score Improvement')\n", "plt.title('Fine-tuned Model Improvement')\n", "\n", "plt.tight_layout()\n", "plt.show()\n", "\n", "# Show overall improvement\n", "avg_base_score = comparison_results['base_score'].mean()\n", "avg_fine_tuned_score = comparison_results['fine_tuned_score'].mean()\n", "avg_improvement = avg_fine_tuned_score - avg_base_score\n", "\n", "print(f\"Average Base Model Score: {avg_base_score:.2f}\")\n", "print(f\"Average Fine-tuned Model Score: {avg_fine_tuned_score:.2f}\")\n", "print(f\"Average Improvement: {avg_improvement:.2f} ({avg_improvement/avg_base_score*100:.1f}%)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "We were able to train a fine-tuned model on our queries and designs, but the results were disappointing. We don't want to launch an embedding model that hurts our performance so we'll stick with the existing rag agent. But with more queries, more data, and testing more models, there may be a way to find improvements." ] } ], "metadata": { "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.11.11" } }, "nbformat": 4, "nbformat_minor": 4 }