From 17906093fce864ab58106fa9e0df243077ca9776 Mon Sep 17 00:00:00 2001 From: dasparic Date: Sat, 4 Oct 2025 10:52:57 +0530 Subject: [PATCH 1/3] newLP --- .../1.png | Bin 0 -> 23134 bytes .../_index.md | 66 +++ .../_next-steps.md | 8 + .../example-picture.png | Bin 0 -> 63167 bytes .../how-to-1.md | 65 +++ .../how-to-2.md | 100 ++++ .../how-to-3.md | 121 ++++ .../how-to-4.md | 546 ++++++++++++++++++ .../how-to-5.md | 109 ++++ .../how-to-6.md | 53 ++ .../how-to-7.md | 34 ++ 11 files changed, 1102 insertions(+) create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/1.png create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_index.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_next-steps.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/example-picture.png create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-1.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-2.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-4.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/1.png b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/1.png new file mode 100644 index 0000000000000000000000000000000000000000..aa05c2005081772730aff18b10c66c31707c5673 GIT binary patch literal 23134 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 q-Mv3pxc`&l*gyNt{xlEs;Qs>q{#983 literal 0 HcmV?d00001 diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-1.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-1.md new file mode 100644 index 0000000000..299522fa67 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-1.md @@ -0,0 +1,65 @@ +--- +title: Overview +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## What is Fine-Tuning +Fine-tuning in the context of large language models (LLMs) refers to the process of further training a pre-trained LLM on domain-specific or task-specific data to enhance its performance for a particular application. LLMs, such as GPT, BERT, and LLaMA, are initially trained on massive corpora containing billions of tokens, enabling them to develop a broad linguistic understanding. Fine-tuning refines this knowledge by exposing the model to specialized datasets, allowing it to generate more contextually relevant and accurate responses. Rather than training an LLM from scratch, fine-tuning leverages the pre-existing knowledge embedded in the model, optimizing it for specific use cases such as customer support, content generation, legal document analysis, or medical text processing. This approach significantly reduces computational requirements and data needs while improving adaptability and efficiency in real-world applications. + +## Advantage of Fine-Tuning +Fine-tuning is essential for optimizing large language models (LLMs) to meet specific application requirements, enhance performance, and reduce computational costs. While pre-trained LLMs have broad linguistic capabilities, they may not always produce domain-specific, contextually accurate, or application-tailored responses +- Customization for Specific Domains +- Improved Response Quality and Accuracy +- Task-Specific Adaptation +- Reduction in Computational and Data Requirements +- Enhanced Efficiency in Real-World Applications +- Alignment with Ethical, Regulatory, and Organizational Guidelines + +## Fine-Tuning Methods +Fine-tuning LLM uses different techniques based on the various use cases, computational constraints, and efficiency requirements. Below are the key fine-tuning methods: + +### Full Fine-Tuning (Supervised Learning Approach) +It involves updating all parameters of the LLM using task-specific data, requiring significant computational power and large labeled datasets, which provides the highest level of customization. + +### Instruction Fine-Tuning +Instruction fine-tuning is a supervised learning method. A pre-trained large language model (LLM) is further trained on instruction-response pairs to improve its ability to follow human instructions accurately. Instruction Fine-Tuning has some key features using Labeled Instruction-Response Pairs, Enhances Model Alignment with Human Intent, Commonly Used in Chatbots and AI Assistants, and Prepares Models for Zero-Shot and Few-Shot Learning. + +### Parameter-Efficient Fine-Tuning (PEFT) +It is a optimized approaches that reduce the number of trainable parameters while maintaining high performance: + +- ###### LoRA (Low-Rank Adaptation) + - Introduces small trainable weight matrices (rank decomposition) while freezing the main model weights. + - It will significantly reduce GPU memory usage and training time. + +- ###### QLoRA (Quantized LoRA) + - It will use quantization (e.g., 4-bit or 8-bit precision) to reduce memory footprint while applying LoRA fine-tuning. + - It is Ideal for fine-tuning large models on limited hardware. + +- ###### Adapter Layers + - Inserts small trainable layers between existing layers of the model and Keeps most parameters frozen, reducing computational overhead. + +- ###### Reinforcement Learning from Human Feedback (RLHF) + - Fine-tunes models based on human preferences using reinforcement learning. + +- ###### Domain-Specific Fine-Tuning + - Fine-tunes the LLM with domain-specific datasets and Improves accuracy and relevance in specialized applications. + +- ###### Multi-Task Learning (MTL) Fine-Tuning + - Trains the model on multiple tasks simultaneously, enabling generalization across different applications. + + + +## Fine-Tuning Implementaion +The following steps need to be performed to implement fine-tuning: + + +![example image alt-text#center](1.png "Figure 1. Fine-Tuning Implementaion") + +- Base Model Selection: Choose a pre-trained model based on your use cases. You can find pre-trained models at [Hugging Face](https://huggingface.co/). +- Fine-Tuning Method Finalization: Select the most appropriate fine-tuning method (e.g., supervised, instruction-based, PEFT) based on your use case and dataset. You can typically find various datasets on [Hugging Face](https://huggingface.co/datasets) and [Kaggle](https://www.kaggle.com/datasets). +- Dataset Prepration:Organize your data for your use case-specific training, ensuring it aligns with the model's required format. +- Training:Utilize frameworks such as TensorFlow and PyTorch to fine-tune the model. +- Evaluate: Evaluate the model, refine it as needed, and retrain to enhance performance. \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-2.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-2.md new file mode 100644 index 0000000000..5d49778381 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-2.md @@ -0,0 +1,100 @@ +--- +title: Large Language Model - Setup Environment +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Large Language Model - Setup Environment + +#### Plartform Required +- An AWS Graviton4 r8g.16xlarge instance to test Arm performance optimizations, or any [Arm based instance](/learning-paths/servers-and-cloud-computing/csp/) from a cloud service provider or an on-premise Arm server or Arm based laptop. +- An Arm-powered smartphone with the i8mm feature running Android, with 16GB of RAM. +- A USB cable to connect your smartphone to your development machine. + +#### Set Up Required Libraries +The following commands install the necessary libraries for the task, including Hugging Face Transformers, Datasets, and fine-tuning methods. These libraries facilitate model loading, training, and fine-tuning + +###### The transformers library (by Hugging Face) provides pre-trained LLMs +```python +!pip install transformers + +``` +###### This installs transformers along with PyTorch, ensuring that models are trained and fine-tuned using the Torch backend. +```python +!pip install transformers[torch] +``` +###### The datasets library (by Hugging Face) provides access to a vast collection of pre-built datasets + +```python +!pip install datasets +``` +###### The evaluate library provides metrics for model performance assessment + +```python +!pip install evaluate +``` +###### Speed up fine-tuning of Large Language Models (LLMs) +Use a library designed to speed up fine-tuning of Large Language Models (LLMs) while reducing computational costs, optimizes training efficiency, particularly for LoRA (Low-Rank Adaptation) fine-tuning + + +``` +Used pre-fine-tuned customer support model +``` + +###### AWS Graviton3 Instance Setup +Launch EC2 Instance: + + +``` +Recommended instance: c7g.xlarge or c7g.2xlarge +- c7g.xlarge: 4 vCPUs, 8 GB RAM +- c7g.2xlarge: 8 vCPUs, 16 GB RAM + +Connect to your instance +ssh -i your-key.pem ubuntu@your-instance-ip +``` + +###### System Setup: +Launch EC2 Instance: + + +``` +Update system +sudo apt update && sudo apt upgrade -y + +Install essential packages +sudo apt install -y build-essential git wget curl \ + python3-pip python3-venv htop + +Verify ARM architecture +uname -m # Should show: aarch64 +``` + +###### Python Environment Setup + + + +``` +Create virtual environment +python3 -m venv chatbot_env +source chatbot_env/bin/activate + +Upgrade pip +pip install --upgrade pip + +Install PyTorch for ARM/CPU +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu + +Install Transformers and utilities +pip install transformers==4.36.0 +pip install datasets accelerate +pip install huggingface-hub +pip install flask psutil +pip install numpy pandas + +Verify installation +python -c "import torch; print(f'PyTorch version: {torch.__version__}')" +python -c "import torch; print(f'CPU available: {torch.cpu.device_count()}')" +``` \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md new file mode 100644 index 0000000000..90bc72ca33 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md @@ -0,0 +1,121 @@ +--- +title: Model Loading & Optimization for ARM +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Model Loading & Optimization for ARM + +###### Hugging Face Authentication +```bash +Login to Hugging Face (needed for Llama models) +pip install huggingface_hub +huggingface-cli login + +Enter your token when prompted +Get token from: https://huggingface.co/settings/tokens + +``` + +###### Load a pre-fine-tuned model (from Hugging Face) +- Example: meta-llama/Llama-3-8B-Instruct or a customer-support fine-tuned variant + +###### Model Optimization for ARM (Understanding Quantization) +- Reduces model precision (e.g., 32-bit → 8-bit) +- Decreases memory footprint (~4x reduction) +- Speeds up inference on CPU +- Minimal accuracy loss for most tasks + +###### Apply Dynamic Quantization +- Create optimize_model.py + +```python +import torch +from transformers import AutoModelForCausalLM, AutoTokenizer +from torch.quantization import quantize_dynamic +import time +import os + +def load_base_model(model_name): + """Load the base model""" + print(f"Loading base model: {model_name}") + + tokenizer = AutoTokenizer.from_pretrained(model_name) + tokenizer.pad_token = tokenizer.eos_token + + model = AutoModelForCausalLM.from_pretrained( + model_name, + torch_dtype=torch.float32, + device_map=None, + low_cpu_mem_usage=True + ) + model.eval() + + return model, tokenizer + +def apply_quantization(model): + """Apply dynamic quantization""" + print("Applying dynamic quantization...") + + quantized_model = quantize_dynamic( + model, + {torch.nn.Linear}, # Quantize linear layers + dtype=torch.qint8 + ) + + return quantized_model + +def test_model(model, tokenizer, prompt): + """Test model with a sample prompt""" + inputs = tokenizer(prompt, return_tensors="pt") + + start_time = time.time() + with torch.no_grad(): + outputs = model.generate( + inputs.input_ids, + max_new_tokens=100, + do_sample=False, + pad_token_id=tokenizer.eos_token_id + ) + inference_time = time.time() - start_time + + response = tokenizer.decode(outputs[0], skip_special_tokens=True) + + return response, inference_time + +def main(): + model_name = "meta-llama/Meta-Llama-3-8B-Instruct" + + # Load base model + base_model, tokenizer = load_base_model(model_name) + + # Test base model + test_prompt = "How do I track my order?" + print("\nTesting base model...") + response, base_time = test_model(base_model, tokenizer, test_prompt) + print(f"Base model inference time: {base_time:.2f}s") + + # Apply quantization + quantized_model = apply_quantization(base_model) + + # Test quantized model + print("\nTesting quantized model...") + response, quant_time = test_model(quantized_model, tokenizer, test_prompt) + print(f"Quantized model inference time: {quant_time:.2f}s") + print(f"Speedup: {base_time / quant_time:.2f}x") + + # Save quantized model + save_dir = "./models/quantized_llama3" + os.makedirs(save_dir, exist_ok=True) + + torch.save(quantized_model.state_dict(), f"{save_dir}/model.pt") + tokenizer.save_pretrained(save_dir) + + print(f"\nQuantized model saved to: {save_dir}") + +if __name__ == "__main__": + main() + +``` \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-4.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-4.md new file mode 100644 index 0000000000..bbe8b69ede --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-4.md @@ -0,0 +1,546 @@ +--- +title: Large Language Model - Inference Benchmarking +weight: 5 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Latency Benchmarking +- Time-to-first-token (TTFT) +- Tokens per second throughput +- End-to-end response time +- Compare across different batch sizes + +###### Additional Optimization Techniques +- Create advanced_optimization.py +```python +import torch +from transformers import AutoModelForCausalLM, AutoTokenizer + +def optimize_generation_config(model, tokenizer): + """Optimize generation parameters for faster inference""" + + generation_config = { + # Use greedy decoding for speed + "do_sample": False, + + # Enable KV cache for efficiency + "use_cache": True, + + # Limit max tokens + "max_new_tokens": 256, + + # Set padding + "pad_token_id": tokenizer.eos_token_id, + + # Early stopping + "eos_token_id": tokenizer.eos_token_id, + } + + return generation_config + +def benchmark_generation_strategies(model, tokenizer): + """Compare different generation strategies""" + + prompt = "What is your return policy?" + inputs = tokenizer(prompt, return_tensors="pt") + + strategies = [ + {"name": "Greedy", "do_sample": False}, + {"name": "Sampling (temp=0.7)", "do_sample": True, "temperature": 0.7}, + {"name": "Top-k", "do_sample": True, "top_k": 50}, + {"name": "Top-p", "do_sample": True, "top_p": 0.9}, + ] + + results = [] + + for strategy in strategies: + start_time = time.time() + + with torch.no_grad(): + outputs = model.generate( + inputs.input_ids, + max_new_tokens=100, + pad_token_id=tokenizer.eos_token_id, + **{k: v for k, v in strategy.items() if k != "name"} + ) + + elapsed = time.time() - start_time + results.append({ + "strategy": strategy["name"], + "time": elapsed + }) + + print(f"{strategy['name']}: {elapsed:.2f}s") + + return results + +if __name__ == "__main__": + model_name = "meta-llama/Meta-Llama-3-8B-Instruct" + + tokenizer = AutoTokenizer.from_pretrained(model_name) + model = AutoModelForCausalLM.from_pretrained( + model_name, + torch_dtype=torch.float32, + device_map=None + ) + model.eval() + + print("Benchmarking generation strategies...\n") + results = benchmark_generation_strategies(model, tokenizer) +``` + +###### Comprehensive Benchmarking +- This is the core module showcasing ARM performance. + +```python +import torch +import time +import psutil +import json +import numpy as np +from transformers import AutoModelForCausalLM, AutoTokenizer +from torch.quantization import quantize_dynamic +from datetime import datetime +import os + +class GravitonBenchmark: + """Comprehensive benchmarking suite for Graviton3""" + + def __init__(self, model_name, use_quantization=False): + self.model_name = model_name + self.device = "cpu" + self.use_quantization = use_quantization + + # Load model and tokenizer + print(f"Loading model: {model_name}") + self.tokenizer = AutoTokenizer.from_pretrained(model_name) + self.tokenizer.pad_token = self.tokenizer.eos_token + + self.model = AutoModelForCausalLM.from_pretrained( + model_name, + torch_dtype=torch.float32, + device_map=None, + low_cpu_mem_usage=True + ) + + if use_quantization: + print("Applying quantization...") + self.model = quantize_dynamic( + self.model, + {torch.nn.Linear}, + dtype=torch.qint8 + ) + + self.model.eval() + print("Model loaded successfully!\n") + + def warmup(self, num_iterations=3): + """Warm up the model""" + print("Warming up model...") + prompt = "Hello" + inputs = self.tokenizer(prompt, return_tensors="pt") + + for _ in range(num_iterations): + with torch.no_grad(): + _ = self.model.generate( + inputs.input_ids, + max_new_tokens=10, + pad_token_id=self.tokenizer.eos_token_id + ) + print("Warmup complete\n") + + def benchmark_latency(self, prompts, max_new_tokens=128, num_runs=5): + """Benchmark inference latency""" + print(f"Running latency benchmark ({num_runs} runs)...") + + all_latencies = [] + all_tokens_per_sec = [] + + for prompt in prompts: + latencies = [] + tokens_per_sec = [] + + for run in range(num_runs): + inputs = self.tokenizer(prompt, return_tensors="pt") + + start_time = time.time() + + with torch.no_grad(): + outputs = self.model.generate( + inputs.input_ids, + max_new_tokens=max_new_tokens, + do_sample=False, + use_cache=True, + pad_token_id=self.tokenizer.eos_token_id + ) + + end_time = time.time() + elapsed = end_time - start_time + + # Calculate tokens generated + generated_tokens = outputs.shape[1] - inputs.input_ids.shape[1] + tps = generated_tokens / elapsed + + latencies.append(elapsed) + tokens_per_sec.append(tps) + + all_latencies.extend(latencies) + all_tokens_per_sec.extend(tokens_per_sec) + + return { + "avg_latency_sec": np.mean(all_latencies), + "std_latency_sec": np.std(all_latencies), + "min_latency_sec": np.min(all_latencies), + "max_latency_sec": np.max(all_latencies), + "p50_latency_sec": np.percentile(all_latencies, 50), + "p95_latency_sec": np.percentile(all_latencies, 95), + "p99_latency_sec": np.percentile(all_latencies, 99), + "avg_tokens_per_sec": np.mean(all_tokens_per_sec), + "max_tokens_per_sec": np.max(all_tokens_per_sec) + } + + def benchmark_throughput(self, prompts, max_new_tokens=128): + """Benchmark throughput with multiple queries""" + print("Running throughput benchmark...") + + total_queries = len(prompts) + total_tokens = 0 + + start_time = time.time() + + for prompt in prompts: + inputs = self.tokenizer(prompt, return_tensors="pt") + + with torch.no_grad(): + outputs = self.model.generate( + inputs.input_ids, + max_new_tokens=max_new_tokens, + do_sample=False, + use_cache=True, + pad_token_id=self.tokenizer.eos_token_id + ) + + total_tokens += outputs.shape[1] + + total_time = time.time() - start_time + + return { + "total_queries": total_queries, + "total_time_sec": total_time, + "queries_per_sec": total_queries / total_time, + "avg_time_per_query": total_time / total_queries, + "total_tokens_generated": total_tokens, + "tokens_per_sec": total_tokens / total_time + } + + def benchmark_resource_usage(self, prompt, max_new_tokens=128, num_runs=3): + """Benchmark CPU and memory usage""" + print("Measuring resource usage...") + + process = psutil.Process() + cpu_percentages = [] + memory_usages = [] + + for _ in range(num_runs): + # Measure before + mem_before = process.memory_info().rss / 1024**3 # GB + cpu_before = psutil.cpu_percent(interval=0.1) + + # Run inference + inputs = self.tokenizer(prompt, return_tensors="pt") + + with torch.no_grad(): + _ = self.model.generate( + inputs.input_ids, + max_new_tokens=max_new_tokens, + do_sample=False, + pad_token_id=self.tokenizer.eos_token_id + ) + + # Measure after + cpu_after = psutil.cpu_percent(interval=0.1) + mem_after = process.memory_info().rss / 1024**3 # GB + + cpu_percentages.append(cpu_after) + memory_usages.append(mem_after) + + return { + "avg_cpu_percent": np.mean(cpu_percentages), + "max_cpu_percent": np.max(cpu_percentages), + "avg_memory_gb": np.mean(memory_usages), + "max_memory_gb": np.max(memory_usages) + } + + def benchmark_scaling(self, prompt, token_counts=[32, 64, 128, 256]): + """Benchmark how performance scales with output length""" + print("Running scaling benchmark...") + + results = [] + + for max_tokens in token_counts: + inputs = self.tokenizer(prompt, return_tensors="pt") + + start_time = time.time() + + with torch.no_grad(): + outputs = self.model.generate( + inputs.input_ids, + max_new_tokens=max_tokens, + do_sample=False, + pad_token_id=self.tokenizer.eos_token_id + ) + + elapsed = time.time() - start_time + actual_tokens = outputs.shape[1] - inputs.input_ids.shape[1] + + results.append({ + "requested_tokens": max_tokens, + "actual_tokens": actual_tokens, + "time_sec": elapsed, + "tokens_per_sec": actual_tokens / elapsed + }) + + return results + + def run_comprehensive_benchmark(self, save_results=True): + """Run all benchmarks""" + print("=" * 60) + print("COMPREHENSIVE BENCHMARK SUITE") + print("=" * 60) + print(f"Model: {self.model_name}") + print(f"Quantized: {self.use_quantization}") + print(f"Device: {self.device}") + print("=" * 60) + print() + + # Warmup + self.warmup() + + # Test prompts (customer support scenarios) + test_prompts = [ + "I need help tracking my order. It was supposed to arrive yesterday.", + "How do I return an item I purchased last week?", + "What is your refund policy for defective products?", + "I can't log into my account. Can you help me reset my password?", + "My payment didn't go through. What should I do?" + ] + + results = { + "metadata": { + "model_name": self.model_name, + "quantized": self.use_quantization, + "device": self.device, + "timestamp": datetime.now().isoformat(), + "cpu_count": psutil.cpu_count(), + "total_memory_gb": psutil.virtual_memory().total / 1024**3 + } + } + + # 1. Latency Benchmark + results["latency"] = self.benchmark_latency(test_prompts[:3]) + print("\nLatency Results:") + print(f" Avg: {results['latency']['avg_latency_sec']:.2f}s") + print(f" P95: {results['latency']['p95_latency_sec']:.2f}s") + print(f" Throughput: {results['latency']['avg_tokens_per_sec']:.2f} tok/s") + print() + + # 2. Throughput Benchmark + results["throughput"] = self.benchmark_throughput(test_prompts * 4) + print("\nThroughput Results:") + print(f" Queries/sec: {results['throughput']['queries_per_sec']:.2f}") + print(f" Tokens/sec: {results['throughput']['tokens_per_sec']:.2f}") + print() + + # 3. Resource Usage + results["resources"] = self.benchmark_resource_usage(test_prompts[0]) + print("\nResource Usage:") + print(f" CPU: {results['resources']['avg_cpu_percent']:.1f}%") + print(f" Memory: {results['resources']['avg_memory_gb']:.2f} GB") + print() + + # 4. Scaling Benchmark + results["scaling"] = self.benchmark_scaling(test_prompts[0]) + print("\nScaling Results:") + for item in results["scaling"]: + print(f" {item['requested_tokens']} tokens: {item['time_sec']:.2f}s ({item['tokens_per_sec']:.1f} tok/s)") + print() + + # Save results + if save_results: + quant_str = "quantized" if self.use_quantization else "base" + filename = f"benchmark_{quant_str}_{int(time.time())}.json" + + with open(filename, "w") as f: + json.dump(results, f, indent=2) + + print(f"\nResults saved to: {filename}") + + print("\n" + "=" * 60) + print("BENCHMARK COMPLETE") + print("=" * 60) + + return results + + +def compare_configurations(): + """Compare base vs quantized model""" + model_name = "meta-llama/Meta-Llama-3-8B-Instruct" + + print("\n" + "=" * 60) + print("COMPARING BASE VS QUANTIZED MODEL") + print("=" * 60) + print() + + # Benchmark base model + print(">>> BENCHMARKING BASE MODEL <<<\n") + base_bench = GravitonBenchmark(model_name, use_quantization=False) + base_results = base_bench.run_comprehensive_benchmark() + + print("\n" + "=" * 60) + print() + + # Benchmark quantized model + print(">>> BENCHMARKING QUANTIZED MODEL <<<\n") + quant_bench = GravitonBenchmark(model_name, use_quantization=True) + quant_results = quant_bench.run_comprehensive_benchmark() + + # Generate comparison report + print("\n" + "=" * 60) + print("COMPARISON SUMMARY") + print("=" * 60) + + speedup = base_results["latency"]["avg_latency_sec"] / quant_results["latency"]["avg_latency_sec"] + memory_reduction = (1 - quant_results["resources"]["avg_memory_gb"] / base_results["resources"]["avg_memory_gb"]) * 100 + throughput_improvement = (quant_results["throughput"]["queries_per_sec"] / base_results["throughput"]["queries_per_sec"] - 1) * 100 + + print(f"\nSpeedup: {speedup:.2f}x faster") + print(f"Memory Reduction: {memory_reduction:.1f}%") + print(f"Throughput Improvement: +{throughput_improvement:.1f}%") + + # Save comparison + comparison = { + "base_model": base_results, + "quantized_model": quant_results, + "comparison": { + "speedup": speedup, + "memory_reduction_percent": memory_reduction, + "throughput_improvement_percent": throughput_improvement + } + } + + with open("comparison_report.json", "w") as f: + json.dump(comparison, f, indent=2) + + print("\nComparison report saved to: comparison_report.json") + print("=" * 60) + + +if __name__ == "__main__": + # Run single benchmark + model_name = "meta-llama/Meta-Llama-3-8B-Instruct" + + benchmark = GravitonBenchmark(model_name, use_quantization=True) + results = benchmark.run_comprehensive_benchmark() + + + +``` +###### Analyze Results + +```python +import json +import matplotlib.pyplot as plt +import numpy as np + +def load_results(filename): + with open(filename, 'r') as f: + return json.load(f) + +def create_performance_report(results): + """Generate a comprehensive performance report""" + + print("\n" + "=" * 60) + print("PERFORMANCE ANALYSIS REPORT") + print("=" * 60) + + # Latency Analysis + print("\n1. LATENCY METRICS") + print("-" * 40) + latency = results["latency"] + print(f"Average Latency: {latency['avg_latency_sec']:.3f}s") + print(f"Median (P50): {latency['p50_latency_sec']:.3f}s") + print(f"P95 Latency: {latency['p95_latency_sec']:.3f}s") + print(f"P99 Latency: {latency['p99_latency_sec']:.3f}s") + print(f"Std Deviation: {latency['std_latency_sec']:.3f}s") + print(f"Tokens/sec: {latency['avg_tokens_per_sec']:.2f}") + + # Throughput Analysis + print("\n2. THROUGHPUT METRICS") + print("-" * 40) + throughput = results["throughput"] + print(f"Queries/sec: {throughput['queries_per_sec']:.2f}") + print(f"Avg Query Time: {throughput['avg_time_per_query']:.3f}s") + print(f"Total Tokens/sec: {throughput['tokens_per_sec']:.2f}") + + # Resource Usage + print("\n3. RESOURCE UTILIZATION") + print("-" * 40) + resources = results["resources"] + print(f"Avg CPU Usage: {resources['avg_cpu_percent']:.1f}%") + print(f"Max CPU Usage: {resources['max_cpu_percent']:.1f}%") + print(f"Avg Memory: {resources['avg_memory_gb']:.2f} GB") + print(f"Max Memory: {resources['max_memory_gb']:.2f} GB") + + # Scaling Analysis + print("\n4. SCALING BEHAVIOR") + print("-" * 40) + scaling = results["scaling"] + for item in scaling: + efficiency = (item['tokens_per_sec'] / scaling[0]['tokens_per_sec']) * 100 + print(f"{item['requested_tokens']:3d} tokens: {item['time_sec']:6.2f}s | {item['tokens_per_sec']:5.1f} tok/s | {efficiency:5.1f}% efficiency") + + # System Info + print("\n5. SYSTEM INFORMATION") + print("-" * 40) + metadata = results["metadata"] + print(f"Model: {metadata['model_name']}") + print(f"Quantized: {metadata['quantized']}") + print(f"CPU Cores: {metadata['cpu_count']}") + print(f"Total RAM: {metadata['total_memory_gb']:.1f} GB") + + print("\n" + "=" * 60) + +def calculate_cost_efficiency(results, instance_cost_per_hour=0.145): + """Calculate cost per 1000 queries""" + + queries_per_sec = results["throughput"]["queries_per_sec"] + queries_per_hour = queries_per_sec * 3600 + cost_per_1000_queries = (instance_cost_per_hour / queries_per_hour) * 1000 + + print("\n6. COST EFFICIENCY") + print("-" * 40) + print(f"Instance Cost: ${instance_cost_per_hour}/hour") + print(f"Queries/hour: {queries_per_hour:,.0f}") + print(f"Cost/1K queries: ${cost_per_1000_queries:.4f}") + print(f"Monthly (24/7): ${instance_cost_per_hour * 24 * 30:.2f}") + + return cost_per_1000_queries + +if __name__ == "__main__": + # Load most recent benchmark results + import glob + + files = glob.glob("benchmark_*.json") + if not files: + print("No benchmark results found. Run benchmark_suite.py first.") + exit(1) + + latest_file = max(files, key=lambda x: os.path.getctime(x)) + print(f"Analyzing: {latest_file}\n") + + results = load_results(latest_file) + create_performance_report(results) + calculate_cost_efficiency(results) +``` diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md new file mode 100644 index 0000000000..6a1b60274a --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md @@ -0,0 +1,109 @@ +--- +title: Mobile Plartform for Fine Tuning Large Language Model +weight: 6 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Development environment +You will learn to build the ExecuTorch runtime for fine-tuning models using KleidiAI, create JNI libraries for an mobile application, and integrate these libraries into the application. + +The first step is to set up a development environment with the necessary software: +- Python 3.10 or later +- Git +- Java 17 JDK +- Latest Version of Android Studio +- Android NDK + +###### Installation of Android Studio and Android NDK +- Download and install the latest version of Android Studio +- Launch Android Studio and open the Settings dialog. +- Go to Languages & Frameworks > Android SDK. +- In the SDK Platforms tab, select Android 14.0 ("UpsideDownCake"). +- Install the required version of Android NDK by first setting up the Android command line tools. + +###### Install Java 17 JDK +- Open the [Java SE 17 Archive Downloads](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) Downloads page in your browser. +- Choose the appropriate version for your operating system. +- Downloads are available for macOS and Linux. + +###### Install Git and cmake + +For macOS use [Homebrew](https://brew.sh/): + +``` bash +brew install git cmake +``` + +For Linux, use the package manager for your distribution: + +``` bash +sudo apt install git-all cmake +``` + +###### Install Python 3.10 + +For macOS: + +``` bash +brew install python@3.10 +``` + +For Linux: + +``` bash +sudo apt update +sudo apt install software-properties-common -y +sudo add-apt-repository ppa:deadsnakes/ppa +sudo apt install Python3.10 python3.10-venv +``` + + +###### Setup the [Executorch](https://pytorch.org/executorch/stable/intro-overview.html) Environments +For mobile device execution, [ExecuTorch](https://pytorch.org/executorch/stable/intro-overview.html) is required. It enables efficient on-device model deployment and execution + +- Python virtual environment creation + +```bash +python3.10 -m venv executorch +source executorch/bin/activate +``` + +The prompt of your terminal has `executorch` as a prefix to indicate the virtual environment is active. + +- Conda virtual environment creation + +Install Miniconda on your development machine by following the [Installing conda](https://conda.io/projects/conda/en/latest/user-guide/install/index.html) instructions. + +Once `conda` is installed, create the environment: + +```bash +conda create -yn executorch python=3.10.0 +conda activate executorch +``` + +###### Clone ExecuTorch and install the required dependencies + +From within the conda environment, run the commands below to download the ExecuTorch repository and install the required packages: + +- You need to download Executorch from this [GitHub repository](https://github.com/pytorch/executorch/tree/main) +- Download the executorch.aar file from [executorch.aar](https://ossci-android.s3.us-west-1.amazonaws.com/executorch/release/executorch-241002/executorch.aar ) +- Add a libs folder in this path \executorch-main\executorch-main\examples\demo-apps\android\LlamaDemo\app\libs and add executorch.aar + +``` bash +git submodule sync +git submodule update --init +./install_requirements.sh +./install_requirements.sh --pybind xnnpack +./examples/models/llama/install_requirements.sh +``` + +###### Mobile Device Setup +- Enable the mobile device in [Android Studio](https://support.google.com/android/community-guide/273205728/how-to-enable-developer-options-on-android-pixels-6-secret-android-tips?hl=en) +- On the Android phone, enable Developer Options + - First, navigate to Settings > About Phone. + - At the bottom, locate Build Number and tap it seven times. A message will appear confirming that you are now a developer.(if only it were that easy to become one XD) + - Access Developer Options by navigating to Settings > System > Developer Options. + - You will see a large number of options, I repeat: DO NOT TOUCH ANYTHING YOU DO NOT KNOW. + - Enable USB Debugging to connect your mobile device to Android Studio. diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md new file mode 100644 index 0000000000..904c27539a --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md @@ -0,0 +1,53 @@ +--- +title: Fine Tune Large Language Model and Quantization +weight: 7 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +#### Llama Model +Llama is a family of large language models designed for high-performance language processing tasks, trained using publicly available data. When fine-tuned, Llama-based models can be optimized for specific applications, enhancing their ability to generate accurate and context-aware responses. Fine-tuning enables the model to adapt to domain-specific data, improving performance in tasks such as: + +- Language translation – Enhancing fluency and contextual accuracy. +- Question answering – Providing precise and relevant responses. +- Text summarization – Extracting key insights while maintaining coherence. + +Fine-tuned LLaMA models are also highly effective in generating human-like text, making them valuable for: + +- Chatbots – Enabling intelligent and context-aware interactions. +- Virtual assistants – Enhancing responsiveness and personalization. +- Creative writing – Generating compelling and structured narratives. + +By fine-tuning Llama based models, their adaptability and relevance can be significantly improved, allowing seamless integration into specialized AI applications.Please note that the models are subject to the [acceptable use policy](https://github.com/facebookresearch/llama/blob/main/USE_POLICY.md) and [this responsible use guide](https://ai.meta.com/static-resource/responsible-use-guide/). + +#### Results + +Since LLaMA 2 and LLaMA 3 models require at least 4-bit quantization to accommodate the memory constraints of certain smartphones + +#### Quantization + +To optimize models for smartphone memory constraints, 4-bit groupwise per-token dynamic quantization can be applied to all linear layers. In this approach: + +- Dynamic quantization is used for activations, where quantization parameters are computed at runtime based on the min/max range. +- Static quantization is applied to weights, which are per-channel groupwise quantized using 4-bit signed integers. + +This method ensures efficient memory usage while maintaining model performance on resource-constrained devices. + +For further information, refer to [torchao: PyTorch Architecture Optimization](https://github.com/pytorch-labs/ao/). + +The table below evaluates WikiText perplexity using [LM Eval](https://github.com/EleutherAI/lm-evaluation-harness). + +The results are for two different groupsizes, with max_seq_len 2048, and 1000 samples: + +|Model | Baseline (FP32) | Groupwise 4-bit (128) | Groupwise 4-bit (256) +|--------|-----------------| ---------------------- | --------------- +|Llama 2 7B | 9.2 | 10.2 | 10.7 +|Llama 3 8B | 7.9 | 9.4 | 9.7 + +Note that groupsize less than 128 was not enabled in this example, since the model was still too large. This is because current efforts have focused on enabling FP32, and support for FP16 is under way. + +What this implies for model size is: + +1. Embedding table is in FP32. +2. Quantized weights scales are FP32. \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md new file mode 100644 index 0000000000..9b22537273 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md @@ -0,0 +1,34 @@ +--- +title: Prepared the Fine Tune Large Language Model for ExecuTorch and Mobile Deployment +weight: 8 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +#### Fine Tune Model Preparation + +- From the [Huggingface](https://huggingface.co/) need to apply for Repo access [Meta's Llama 3.2 language models](https://huggingface.co/meta-llama/Llama-3.2-1B). +- Download params.json and tokenizer.model from [Llama website](https://www.llama.com/llama-downloads/) or [Hugging Face](https://huggingface.co/meta-llama/Llama-3.2-1B). +- After fine-tuning the model, export the adapter_model.safetensors file locally and convert it to the adapter_model.pth format to .pte format. + +```python + python -m examples.models.llama.export_llama \ + --checkpoint \ + -p \ + -kv \ + --use_sdpa_with_kv_cache \ + -X \ + -qmode 8da4w \ + --group_size 128 \ + -d fp32 \ + --metadata '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}' \ + --embedding-quantize 4,32 \ + --output_name="llama3_kv_sdpa_xnn_qe_4_32.pte" +``` + +- Build the Llama Runner binary for [Android](https://learn.arm.com/learning-paths/mobile-graphics-and-gaming/build-llama3-chat-android-app-using-executorch-and-xnnpack/5-run-benchmark-on-android/). +- Build and Run [Android](https://learn.arm.com/learning-paths/mobile-graphics-and-gaming/build-llama3-chat-android-app-using-executorch-and-xnnpack/6-build-android-chat-app/). +- Open Android Studio and choose "Open an existing Android Studio project" to navigate to examples/demo-apps/android/LlamaDemo and Press Run (^R) to build and launch the app on your phone. +- Tap the Settings widget to select a model, configure its parameters, and set any prompts. +- After choosing the model, tokenizer, and model type, click "Load Model" to load it into the app and return to the main Chat activity. \ No newline at end of file From 88e8020091ad09ebe17fa9cd7c0b11abd773f9c3 Mon Sep 17 00:00:00 2001 From: dasparic Date: Sat, 4 Oct 2025 11:06:40 +0530 Subject: [PATCH 2/3] newLPlan --- .../_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_index.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_index.md index 747b55dfcb..bc1ce9c677 100644 --- a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_index.md +++ b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_index.md @@ -27,7 +27,7 @@ author: Parichay Das ### Tags skilllevels: Introductory -subjects: GenAI +subjects: ML armips: - Neoverse From b5b4131fa9db42eaefb6c4faab755b81b39f894a Mon Sep 17 00:00:00 2001 From: dasparic Date: Thu, 16 Oct 2025 13:00:32 +0530 Subject: [PATCH 3/3] newLPlanv1 --- .../_index.md | 65 +++ .../_next-steps.md | 0 .../example-picture.png | Bin .../how-to-1.md | 34 ++ .../how-to-2.md | 79 +++ .../how-to-3.md | 58 +- .../how-to-4.md | 31 + .../how-to-5.md | 42 ++ .../how-to-6.md | 107 ++++ .../how-to-7.md | 45 ++ .../1.png | Bin 23134 -> 0 bytes .../_index.md | 66 --- .../how-to-1.md | 65 --- .../how-to-2.md | 100 ---- .../how-to-4.md | 546 ------------------ .../how-to-5.md | 109 ---- .../how-to-6.md | 53 -- .../how-to-7.md | 34 -- 18 files changed, 452 insertions(+), 982 deletions(-) create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/_index.md rename content/learning-paths/embedded-and-microcontrollers/{finetuning-llms-on-arm-powered-mobile-devices-with-executorch => customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices}/_next-steps.md (100%) rename content/learning-paths/embedded-and-microcontrollers/{finetuning-llms-on-arm-powered-mobile-devices-with-executorch => customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices}/example-picture.png (100%) create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-1.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-2.md rename content/learning-paths/embedded-and-microcontrollers/{finetuning-llms-on-arm-powered-mobile-devices-with-executorch => customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices}/how-to-3.md (54%) create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-4.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-5.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-6.md create mode 100644 content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-7.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/1.png delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_index.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-1.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-2.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-4.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md delete mode 100644 content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/_index.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/_index.md new file mode 100644 index 0000000000..dba8f68506 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/_index.md @@ -0,0 +1,65 @@ +--- +title: Customer Support Chatbot with Llama and ExecuTorch on Arm-Based Mobile Devices (with Agentic AI Capabilities) +minutes_to_complete: 60 + +who_is_this_for: This learning plan is designed for developers with basic knowledge of Python, Mobile development, and machine learning concepts.It guides you through creating an on-device customer support chatbot using Meta's Llama models deployed via PyTorch's ExecuTorch runtime.The focus is on Arm-based Android devices.The chatbot will handle common customer queries (e.g., product info, troubleshooting) with low latency, privacy (no cloud dependency), and optimized performance.Incorporates agentic AI capabilities, transforming the chatbot from reactive (simple Q&A) to proactive and autonomous. Agentic AI enables the bot to plan multi-step actions, use external tools,reason over user intent, and adapt responses dynamically. This is achieved by extending the core LLM with tool-calling mechanisms and multi-agent orchestration. + +learning_objectives: + - Explain the architecture and capabilities of Llama models (e.g., Llama 3.2 1B/3B) for mobile use. + - Master the process of quantizing LLMs (e.g., 4-bit PTQ) to reduce model size and enable efficient inference on resource-constrained mobile devices. + - Gain proficiency in using ExecuTorch to export PyTorch models to .pte format for on-device deployment. + - Learn to leverage Arm-specific optimizations (e.g., XNNPACK, KleidiAI) to achieve 2-3x faster inference on Arm-based Android devices. + - Implement real-time inference with Llama models, enabling seamless customer support interactions (e.g., handling FAQs, troubleshooting). + +prerequisites: + - Basic Understanding of Machine Learning & Deep Learning (Familiarity with concepts like supervised learning, neural networks, transfer learning and Understanding of model training, validation, & overfitting concepts). + - Familiarity with Deep Learning Frameworks (Experience with PyTorch for building, training neural networks and Knowledge of Hugging Face Transformers for working with pre-trained LLMs. + - An Arm-powered smartphone with the i8mm feature running Android, with 16GB of RAM. + - A USB cable to connect your smartphone to your development machine. + - An AWS Graviton4 r8g.16xlarge instance to test Arm performance optimizations, or any [Arm based instance](/learning-paths/servers-and-cloud-computing/csp/) from a cloud service provider or an on-premise Arm server or Arm based laptop. + - Android Debug Bridge (adb) installed on your device. Follow the steps in [adb](https://developer.android.com/tools/adb) to install Android SDK Platform Tools. The adb tool is included in this package. + - Java 17 JDK. Follow the steps in [Java 17 JDK](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) to download and install JDK for host. + - Android Studio. Follow the steps in [Android Studio](https://developer.android.com/studio) to download and install Android Studio for host. + - Python 3.10. + +author: Parichay Das + +### Tags +skilllevels: Introductory +subjects: ML +armips: + - Neoverse + +tools_software_languages: + - LLM + - GenAI + - Python + - PyTorch + - ExecuTorch +operatingsystems: + - Linux + - Windows + - Android + + +further_reading: + - resource: + title: Hugging Face Documentation + link: https://huggingface.co/docs + type: documentation + - resource: + title: PyTorch Documentation + link: https://pytorch.org/docs/stable/index.html + type: documentation + - resource: + title: Android + link: https://www.android.com/ + type: website + + +### FIXED, DO NOT MODIFY +# ================================================================================ +weight: 1 # _index.md always has weight of 1 to order correctly +layout: "learningpathall" # All files under learning paths have this same wrapper +learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. +--- diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_next-steps.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/_next-steps.md similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/_next-steps.md rename to content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/_next-steps.md diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/example-picture.png b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/example-picture.png similarity index 100% rename from content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/example-picture.png rename to content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/example-picture.png diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-1.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-1.md new file mode 100644 index 0000000000..224fa4013e --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-1.md @@ -0,0 +1,34 @@ +--- +title: Overview +weight: 2 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Understanding Llama: Meta’s Large Language Model +Llama is a family of large language models trained using publicly available datasets. These models demonstrate strong performance across a range of natural language processing (NLP) tasks, including language translation, question answering, and text summarization. + +In addition to their analytical capabilities, Llama models can generate human-like, coherent, and contextually relevant text, making them highly effective for applications that rely on natural language generation. Consequently, they serve as powerful tools in areas such as chatbots, virtual assistants, and language translation, as well as in creative and content-driven domains where producing natural and engaging text is essential. + +Please note that the models are subject to the [acceptable use policy](https://github.com/meta-llama/llama/blob/main/USE_POLICY.md) and this [responsible use guide](https://github.com/meta-llama/llama/blob/main/RESPONSIBLE_USE_GUIDE.md) . + + + +## Quantization +A practical approach to make models fit within smartphone memory constraints is through 4-bit groupwise per-token dynamic quantization of all linear layers. In this technique, dynamic quantization is applied to activations—meaning the quantization parameters are computed at runtime based on the observed minimum and maximum activation values. Meanwhile, the model weights are statically quantized, where each channel is quantized in groups using 4-bit signed integers. This method significantly reduces memory usage while maintaining model performance for on-device inference. + +This method ensures efficient memory usage while maintaining model performance on resource-constrained devices. + +For further information, refer to [torchao: PyTorch Architecture Optimization](https://github.com/pytorch-labs/ao/). + +The table below evaluates WikiText perplexity using [LM Eval](https://github.com/EleutherAI/lm-evaluation-harness). + +The results are for two different groupsizes, with max_seq_len 2048, and 1000 samples: + +|Model | Baseline (FP32) | Groupwise 4-bit (128) | Groupwise 4-bit (256) +|--------|-----------------| ---------------------- | --------------- +|Llama 2 7B | 9.2 | 10.2 | 10.7 +|Llama 3 8B | 7.9 | 9.4 | 9.7 + +Note that groupsize less than 128 was not enabled in this example, since the model was still too large. This is because current efforts have focused on enabling FP32, and support for FP16 is under way. diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-2.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-2.md new file mode 100644 index 0000000000..195ad5f4cc --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-2.md @@ -0,0 +1,79 @@ +--- +title: Environment Setup +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Android NDK and Android Studio - Environment Setup + +#### Plartform Required +- An AWS Graviton4 r8g.16xlarge instance to test Arm performance optimizations, or any [Arm based instance](/learning-paths/servers-and-cloud-computing/csp/) from a cloud service provider or an on-premise Arm server or Arm based laptop. +- An Arm-powered smartphone with the i8mm feature running Android, with 16GB of RAM. +- A USB cable to connect your smartphone to your development machine. + +The installation and configuration of Android Studio can be accomplished through the following steps: +1. Download and install the latest version of [Android Studio](https://developer.android.com/studio). +2. Launch Android Studio and access the Settings dialog. +3. Navigate to Languages & Frameworks → Android SDK. +4. Under the SDK Platforms tab, ensure that Android 14.0 (“UpsideDownCake”) is selected. + +Next, proceed to install the required version of the Android NDK by first setting up the Android Command Line Tools. +Linux: +```bash +curl https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -o commandlinetools.zip +unzip commandlinetools.zip +./commandlinetools/bin/sdkmanager --install "ndk;26.1.10909697" +``` +Install the NDK in the same directory where Android Studio has installed the SDK, which is typically located at ~/Library/Android/sdk by default. Then, configure the necessary environment variables as follows: +```bash +export ANDROID_HOME="$(realpath ~/Library/Android/sdk)" +export PATH=$ANDROID_HOME/cmdline-tools/bin/:$PATH +sdkmanager --sdk_root="${ANDROID_HOME}" --install "ndk;28.0.12433566" +export ANDROID_NDK=$ANDROID_HOME/ndk/28.0.12433566/ +``` + +#### Install Java 17 JDK +1. Open the Java SE 17 Archive [Downloads](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) page in your browser. +2. Select an appropriate download for your development machine operating system. + +#### Install Git and cmake +```bash +sudo apt-get install git cmake +``` + +#### Install Python 3.10 +```bash +sudo apt-get install python3.10 +``` + +#### Set up ExecuTorch +ExecuTorch is an end-to-end framework designed to facilitate on-device inference across a wide range of mobile and edge platforms, including wearables, embedded systems, and microcontrollers. As a component of the PyTorch Edge ecosystem, it streamlines the efficient deployment of PyTorch models on edge devices. For further details, refer to the [ExecuTorch Overview](https://pytorch.org/executorch/stable/overview/). + +It is recommended to create an isolated Python environment to install the ExecuTorch dependencies. Instructions are available for setting up either a Python virtual environment or a Conda virtual environment—you only need to choose one of these options. + +##### Install Required Tools ( Python environment setup) +```python +python3 -m venv exec_env +source exec_env/bin/activate +pip install torch torchvision torchaudio +pip install executorch +``` +##### Clone Required Repositories +```bash +git clone https://github.com/pytorch/executorch.git +git clone https://github.com/pytorch/text.git +``` +##### Download Pretrained Model (Llama 3.1 Instruct) +Download the quantized model weights optimized for mobile deployment from either the Meta AI Hub or Hugging Face. +``` +git lfs install +git clone https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct +``` + +##### Verify Arm SDK Path +``` +ANDROID_SDK_ROOT=/Users//Library/Android/sdk +ANDROID_NDK_HOME=$ANDROID_SDK_ROOT/ndk/26.1.10909125 +``` \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-3.md similarity index 54% rename from content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md rename to content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-3.md index 90bc72ca33..c05ddda728 100644 --- a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-3.md +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-3.md @@ -1,24 +1,64 @@ --- -title: Model Loading & Optimization for ARM +title: Model Preparation and Conversion weight: 4 ### FIXED, DO NOT MODIFY layout: learningpathall --- -## Model Loading & Optimization for ARM +To begin working with Llama 3, the pre-trained model parameters can be accessed through Meta’s Llama Downloads page. Users are required to request access by submitting their details and reviewing and accepting the Responsible Use Guide. Upon approval, a license and a download link—valid for 24 hours—are provided. For this exercise, the Llama 3.2 1B Instruct model is utilized; however, the same procedures can be applied to other available variants with only minor modifications. -###### Hugging Face Authentication -```bash -Login to Hugging Face (needed for Llama models) -pip install huggingface_hub -huggingface-cli login +Convert the model into an ExecuTorch-compatible format optimized for Arm devices +## Script the Model -Enter your token when prompted -Get token from: https://huggingface.co/settings/tokens +```python +import torch +from transformers import AutoModelForCausalLM + +model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B-Instruct", torch_dtype=torch.float16) +scripted_model = torch.jit.script(model) +scripted_model.save("llama_exec.pt") + +``` + +Install the llama-stack package from pip. +```python +pip install llama-stack +``` + +Run the command to download, and paste the download link from the email when prompted. +```python +llama model download --source meta --model-id Llama3.2-1B-Instruct +``` + +When the download is finished, the installation path is printed as output. +```python +Successfully downloaded model to //.llama/checkpoints/Llama3.2-1B-Instruct +``` +Verify by viewing the downloaded files under this path: +``` +ls $HOME/.llama/checkpoints/Llama3.2-1B-Instruct +checklist.chk consolidated.00.pth params.json tokenizer.model ``` +Export the model and generate a .pte file by running the appropriate Python command. This command will export the model and save the resulting file in your current working directory. +```python +python3 -m examples.models.llama.export_llama \ +--checkpoint $HOME/.llama/checkpoints/Llama3.2-1B-Instruct/consolidated.00.pth \ +--params $HOME/.llama/checkpoints/Llama3.2-1B-Instruct/params.json \ +-kv --use_sdpa_with_kv_cache -X --xnnpack-extended-ops -qmode 8da4w \ +--group_size 64 -d fp32 \ +--metadata '{"get_bos_id":128000, "get_eos_ids":[128009, 128001, 128006, 128007]}' \ +--embedding-quantize 4,32 \ +--output_name="llama3_1B_kv_sdpa_xnn_qe_4_64_1024_embedding_4bit.pte" \ +--max_seq_length 1024 \ +--max_context_length 1024 +``` + +Because Llama 3 has a larger vocabulary size, it is recommended to quantize the embeddings using the parameter --embedding-quantize 4,32. This helps to further optimize memory usage and reduce the overall model size. + + ###### Load a pre-fine-tuned model (from Hugging Face) - Example: meta-llama/Llama-3-8B-Instruct or a customer-support fine-tuned variant diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-4.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-4.md new file mode 100644 index 0000000000..e2291f4795 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-4.md @@ -0,0 +1,31 @@ +--- +title: Building the Chatbot Logic + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Conversation Framework (Python prototype) +```python +from transformers import AutoTokenizer +tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct") + +def generate_response(model, query, context): + prompt = f"### Context:\n{context}\n### User Query:\n{query}\n### Assistant Response:" + inputs = tokenizer(prompt, return_tensors="pt") + outputs = model.generate(**inputs, max_new_tokens=200) + return tokenizer.decode(outputs[0], skip_special_tokens=True) +``` + +###### Context Memory (Simple JSON Store) + +```python +import json + +def update_memory(user_id, query, response): + memory = json.load(open("chat_memory.json", "r")) + memory[user_id].append({"query": query, "response": response}) + json.dump(memory, open("chat_memory.json", "w")) + +``` + diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-5.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-5.md new file mode 100644 index 0000000000..b155e4245b --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-5.md @@ -0,0 +1,42 @@ +--- +title: Adding Agentic AI Capabilities +weight: 6 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- +Enable the chatbot to perform reasoning, make decisions, and execute actions autonomously + +## Define Agentic Loop +```python +class AgenticChatbot: + def __init__(self, model): + self.model = model + + def observe(self, input): + return f"User said: {input}" + + def think(self, observation): + return f"Decide best next step based on intent." + + def act(self, decision): + if "refund" in decision: + return "Processing refund..." + elif "troubleshoot" in decision: + return "Let's check your device settings." + else: + return "Connecting you with an agent." + + def respond(self, query): + obs = self.observe(query) + thought = self.think(obs) + action = self.act(thought) + return f"Reasoning: {thought}\nAction: {action}" +``` +## Integrate Llama with Reasoning Loop +```python +def generate_agentic_response(query, context): + reasoning = agent.respond(query) + model_response = generate_response(model, query, context) + return reasoning + "\n\n" + model_response +``` \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-6.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-6.md new file mode 100644 index 0000000000..dbd78eff20 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-6.md @@ -0,0 +1,107 @@ +--- +title: Android Integration +weight: 7 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +#### Integration +Build the Llama Runner Binary for Android +Cross-compile the Llama Runner to enable execution on Android by following the steps outlined below. + +#### Android NDK +Configure the environment variable to reference the Android NDK +``` +export ANDROID_NDK=$ANDROID_HOME/ndk/28.0.12433566/ +``` + +Ensure that $ANDROID_NDK/build/cmake/android.toolchain.cmake is accessible so CMake can perform cross-compilation. + +#### Use KleidiAI to build ExecuTorch and the required libraries for Android deployment +build ExecuTorch for Android, leveraging the performance optimizations offered by [KleidiAI](https://gitlab.arm.com/kleidi/kleidiai) kernels + +Use cmake to cross-compile ExecuTorch: +``` +cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-23 \ + -DCMAKE_INSTALL_PREFIX=cmake-out-android \ + -DEXECUTORCH_ENABLE_LOGGING=1 \ + -DCMAKE_BUILD_TYPE=Release \ + -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=ON \ + -DEXECUTORCH_BUILD_EXTENSION_MODULE=ON \ + -DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \ + -DEXECUTORCH_BUILD_EXTENSION_FLAT_TENSOR=ON \ + -DEXECUTORCH_BUILD_XNNPACK=ON \ + -DEXECUTORCH_BUILD_KERNELS_OPTIMIZED=ON \ + -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ + -DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON \ + -DEXECUTORCH_BUILD_KERNELS_LLM=ON \ + -DEXECUTORCH_BUILD_EXTENSION_LLM_RUNNER=ON \ + -DEXECUTORCH_BUILD_EXTENSION_LLM=ON \ + -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \ + -DEXECUTORCH_XNNPACK_ENABLE_KLEIDI=ON \ + -DXNNPACK_ENABLE_ARM_BF16=OFF \ + -DBUILD_TESTING=OFF \ + -Bcmake-out-android . + +cmake --build cmake-out-android -j7 --target install --config Release +``` +Beginning with ExecuTorch version 0.7 beta, KleidiAI is enabled by default. The option -DEXECUTORCH_XNNPACK_ENABLE_KLEIDI=ON is active, providing built-in support for KleidiAI kernels within ExecuTorch when using XNNPack. + +#### Build Llama runner for Android +``` +cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-23 \ + -DCMAKE_INSTALL_PREFIX=cmake-out-android \ + -DCMAKE_BUILD_TYPE=Release \ + -DPYTHON_EXECUTABLE=python \ + -DEXECUTORCH_BUILD_XNNPACK=ON \ + -DEXECUTORCH_BUILD_KERNELS_OPTIMIZED=ON \ + -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ + -DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON \ + -DSUPPORT_REGEX_LOOKAHEAD=ON \ + -DBUILD_TESTING=OFF \ + -Bcmake-out-android/examples/models/llama \ + examples/models/llama + +cmake --build cmake-out-android/examples/models/llama -j16 --config Release + +``` +Execute on Android using adb shell. +You will need an Arm-based Android smartphone with the i8mm feature and at least 16GB of RAM. The steps below were validated on a Google Pixel 8 Pro. +#### Create New Android Project +Open Android Studio → New Project → Empty Activity + +#### Add ExecuTorch Runtime to build.gradle +``` +dependencies { + implementation files('libs/executorch.aar') +} +``` + +#### Android phone connection +Connect your Android device to your computer using a USB cable. + +Ensure that USB debugging is enabled on your device. You can follow the Configure on-device developer options guide to enable it. + +After enabling USB debugging and connecting the device via USB, run the following command: +``` +adb devices +``` + +#### model, tokenizer, and Llama runner +``` +adb shell mkdir -p /data/local/tmp/llama +adb push llama3_1B_kv_sdpa_xnn_qe_4_64_1024_embedding_4bit.pte /data/local/tmp/llama/ +adb push $HOME/.llama/checkpoints/Llama3.2-1B-Instruct/tokenizer.model /data/local/tmp/llama/ +adb push cmake-out-android/examples/models/llama/llama_main /data/local/tmp/llama/ + +``` + +#### Model Running +``` +adb shell "cd /data/local/tmp/llama && ./llama_main --model_path llama3_1B_kv_sdpa_xnn_qe_4_64_1024_embedding_4bit.pte --tokenizer_path tokenizer.model --prompt '<|start_header_id|>system<|end_header_id|>\nYour name is Cookie. you are helpful, polite, precise, concise, honest, good at writing. You always give precise and brief answers up to 32 words<|eot_id|><|start_header_id|>user<|end_header_id|>\nHey Cookie! how are you today?<|eot_id|><|start_header_id|>assistant<|end_header_id|>' --warmup=1 --cpu_threads=5" +``` \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-7.md b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-7.md new file mode 100644 index 0000000000..422f8c8d78 --- /dev/null +++ b/content/learning-paths/embedded-and-microcontrollers/customer-support-chatbot-with-llama-and-executorch-on-arm-based-mobile-devices/how-to-7.md @@ -0,0 +1,45 @@ +--- +title: Run, Testing and Benchmarking +weight: 8 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +#### Build the Android (AAR) +You can use the Android demo application included in the ExecuTorch repository, [LlamaDemo](https://github.com/pytorch/executorch/tree/main/examples/android/LlamaDemo), to showcase local inference with ExecuTorch + +Open a terminal and navigate to the root directory of the ExecuTorch repository.Then, set the following environment variables: + +```bash +export ANDROID_NDK=$ANDROID_HOME/ndk/28.0.12433566/ +export ANDROID_ABI=arm64-v8a +``` +Run the following commands to set up the required JNI library: +```bash +pushd extension/android +./gradlew build +popd +pushd examples/demo-apps/android/LlamaDemo +./gradlew :app:setup +popd +``` +Check if the files are available on the phone: +```bash +adb shell "ls -la /data/local/tmp/llama/" +``` +If not, copy them: +``` +adb shell mkdir -p /data/local/tmp/llama +adb push /data/local/tmp/llama/ +adb push /data/local/tmp/llama/ +``` + +#### Build the Android Package Kit using Android Studio +- Open Android Studio and choose Open an existing Android Studio project. +- Navigate to examples/demo-apps/android/LlamaDemo and open it. +- Run the app (^R) to build and launch it on your connected Android device. + +#### Measure Inference Latency +- adb shell am start -n com.example.chatbot/.MainActivity +- adb shell dumpsys meminfo com.example.chatbot diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/1.png b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/1.png deleted file mode 100644 index aa05c2005081772730aff18b10c66c31707c5673..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23134 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 BENCHMARKING BASE MODEL <<<\n") - base_bench = GravitonBenchmark(model_name, use_quantization=False) - base_results = base_bench.run_comprehensive_benchmark() - - print("\n" + "=" * 60) - print() - - # Benchmark quantized model - print(">>> BENCHMARKING QUANTIZED MODEL <<<\n") - quant_bench = GravitonBenchmark(model_name, use_quantization=True) - quant_results = quant_bench.run_comprehensive_benchmark() - - # Generate comparison report - print("\n" + "=" * 60) - print("COMPARISON SUMMARY") - print("=" * 60) - - speedup = base_results["latency"]["avg_latency_sec"] / quant_results["latency"]["avg_latency_sec"] - memory_reduction = (1 - quant_results["resources"]["avg_memory_gb"] / base_results["resources"]["avg_memory_gb"]) * 100 - throughput_improvement = (quant_results["throughput"]["queries_per_sec"] / base_results["throughput"]["queries_per_sec"] - 1) * 100 - - print(f"\nSpeedup: {speedup:.2f}x faster") - print(f"Memory Reduction: {memory_reduction:.1f}%") - print(f"Throughput Improvement: +{throughput_improvement:.1f}%") - - # Save comparison - comparison = { - "base_model": base_results, - "quantized_model": quant_results, - "comparison": { - "speedup": speedup, - "memory_reduction_percent": memory_reduction, - "throughput_improvement_percent": throughput_improvement - } - } - - with open("comparison_report.json", "w") as f: - json.dump(comparison, f, indent=2) - - print("\nComparison report saved to: comparison_report.json") - print("=" * 60) - - -if __name__ == "__main__": - # Run single benchmark - model_name = "meta-llama/Meta-Llama-3-8B-Instruct" - - benchmark = GravitonBenchmark(model_name, use_quantization=True) - results = benchmark.run_comprehensive_benchmark() - - - -``` -###### Analyze Results - -```python -import json -import matplotlib.pyplot as plt -import numpy as np - -def load_results(filename): - with open(filename, 'r') as f: - return json.load(f) - -def create_performance_report(results): - """Generate a comprehensive performance report""" - - print("\n" + "=" * 60) - print("PERFORMANCE ANALYSIS REPORT") - print("=" * 60) - - # Latency Analysis - print("\n1. LATENCY METRICS") - print("-" * 40) - latency = results["latency"] - print(f"Average Latency: {latency['avg_latency_sec']:.3f}s") - print(f"Median (P50): {latency['p50_latency_sec']:.3f}s") - print(f"P95 Latency: {latency['p95_latency_sec']:.3f}s") - print(f"P99 Latency: {latency['p99_latency_sec']:.3f}s") - print(f"Std Deviation: {latency['std_latency_sec']:.3f}s") - print(f"Tokens/sec: {latency['avg_tokens_per_sec']:.2f}") - - # Throughput Analysis - print("\n2. THROUGHPUT METRICS") - print("-" * 40) - throughput = results["throughput"] - print(f"Queries/sec: {throughput['queries_per_sec']:.2f}") - print(f"Avg Query Time: {throughput['avg_time_per_query']:.3f}s") - print(f"Total Tokens/sec: {throughput['tokens_per_sec']:.2f}") - - # Resource Usage - print("\n3. RESOURCE UTILIZATION") - print("-" * 40) - resources = results["resources"] - print(f"Avg CPU Usage: {resources['avg_cpu_percent']:.1f}%") - print(f"Max CPU Usage: {resources['max_cpu_percent']:.1f}%") - print(f"Avg Memory: {resources['avg_memory_gb']:.2f} GB") - print(f"Max Memory: {resources['max_memory_gb']:.2f} GB") - - # Scaling Analysis - print("\n4. SCALING BEHAVIOR") - print("-" * 40) - scaling = results["scaling"] - for item in scaling: - efficiency = (item['tokens_per_sec'] / scaling[0]['tokens_per_sec']) * 100 - print(f"{item['requested_tokens']:3d} tokens: {item['time_sec']:6.2f}s | {item['tokens_per_sec']:5.1f} tok/s | {efficiency:5.1f}% efficiency") - - # System Info - print("\n5. SYSTEM INFORMATION") - print("-" * 40) - metadata = results["metadata"] - print(f"Model: {metadata['model_name']}") - print(f"Quantized: {metadata['quantized']}") - print(f"CPU Cores: {metadata['cpu_count']}") - print(f"Total RAM: {metadata['total_memory_gb']:.1f} GB") - - print("\n" + "=" * 60) - -def calculate_cost_efficiency(results, instance_cost_per_hour=0.145): - """Calculate cost per 1000 queries""" - - queries_per_sec = results["throughput"]["queries_per_sec"] - queries_per_hour = queries_per_sec * 3600 - cost_per_1000_queries = (instance_cost_per_hour / queries_per_hour) * 1000 - - print("\n6. COST EFFICIENCY") - print("-" * 40) - print(f"Instance Cost: ${instance_cost_per_hour}/hour") - print(f"Queries/hour: {queries_per_hour:,.0f}") - print(f"Cost/1K queries: ${cost_per_1000_queries:.4f}") - print(f"Monthly (24/7): ${instance_cost_per_hour * 24 * 30:.2f}") - - return cost_per_1000_queries - -if __name__ == "__main__": - # Load most recent benchmark results - import glob - - files = glob.glob("benchmark_*.json") - if not files: - print("No benchmark results found. Run benchmark_suite.py first.") - exit(1) - - latest_file = max(files, key=lambda x: os.path.getctime(x)) - print(f"Analyzing: {latest_file}\n") - - results = load_results(latest_file) - create_performance_report(results) - calculate_cost_efficiency(results) -``` diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md deleted file mode 100644 index 6a1b60274a..0000000000 --- a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-5.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: Mobile Plartform for Fine Tuning Large Language Model -weight: 6 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -## Development environment -You will learn to build the ExecuTorch runtime for fine-tuning models using KleidiAI, create JNI libraries for an mobile application, and integrate these libraries into the application. - -The first step is to set up a development environment with the necessary software: -- Python 3.10 or later -- Git -- Java 17 JDK -- Latest Version of Android Studio -- Android NDK - -###### Installation of Android Studio and Android NDK -- Download and install the latest version of Android Studio -- Launch Android Studio and open the Settings dialog. -- Go to Languages & Frameworks > Android SDK. -- In the SDK Platforms tab, select Android 14.0 ("UpsideDownCake"). -- Install the required version of Android NDK by first setting up the Android command line tools. - -###### Install Java 17 JDK -- Open the [Java SE 17 Archive Downloads](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) Downloads page in your browser. -- Choose the appropriate version for your operating system. -- Downloads are available for macOS and Linux. - -###### Install Git and cmake - -For macOS use [Homebrew](https://brew.sh/): - -``` bash -brew install git cmake -``` - -For Linux, use the package manager for your distribution: - -``` bash -sudo apt install git-all cmake -``` - -###### Install Python 3.10 - -For macOS: - -``` bash -brew install python@3.10 -``` - -For Linux: - -``` bash -sudo apt update -sudo apt install software-properties-common -y -sudo add-apt-repository ppa:deadsnakes/ppa -sudo apt install Python3.10 python3.10-venv -``` - - -###### Setup the [Executorch](https://pytorch.org/executorch/stable/intro-overview.html) Environments -For mobile device execution, [ExecuTorch](https://pytorch.org/executorch/stable/intro-overview.html) is required. It enables efficient on-device model deployment and execution - -- Python virtual environment creation - -```bash -python3.10 -m venv executorch -source executorch/bin/activate -``` - -The prompt of your terminal has `executorch` as a prefix to indicate the virtual environment is active. - -- Conda virtual environment creation - -Install Miniconda on your development machine by following the [Installing conda](https://conda.io/projects/conda/en/latest/user-guide/install/index.html) instructions. - -Once `conda` is installed, create the environment: - -```bash -conda create -yn executorch python=3.10.0 -conda activate executorch -``` - -###### Clone ExecuTorch and install the required dependencies - -From within the conda environment, run the commands below to download the ExecuTorch repository and install the required packages: - -- You need to download Executorch from this [GitHub repository](https://github.com/pytorch/executorch/tree/main) -- Download the executorch.aar file from [executorch.aar](https://ossci-android.s3.us-west-1.amazonaws.com/executorch/release/executorch-241002/executorch.aar ) -- Add a libs folder in this path \executorch-main\executorch-main\examples\demo-apps\android\LlamaDemo\app\libs and add executorch.aar - -``` bash -git submodule sync -git submodule update --init -./install_requirements.sh -./install_requirements.sh --pybind xnnpack -./examples/models/llama/install_requirements.sh -``` - -###### Mobile Device Setup -- Enable the mobile device in [Android Studio](https://support.google.com/android/community-guide/273205728/how-to-enable-developer-options-on-android-pixels-6-secret-android-tips?hl=en) -- On the Android phone, enable Developer Options - - First, navigate to Settings > About Phone. - - At the bottom, locate Build Number and tap it seven times. A message will appear confirming that you are now a developer.(if only it were that easy to become one XD) - - Access Developer Options by navigating to Settings > System > Developer Options. - - You will see a large number of options, I repeat: DO NOT TOUCH ANYTHING YOU DO NOT KNOW. - - Enable USB Debugging to connect your mobile device to Android Studio. diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md deleted file mode 100644 index 904c27539a..0000000000 --- a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-6.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Fine Tune Large Language Model and Quantization -weight: 7 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -#### Llama Model -Llama is a family of large language models designed for high-performance language processing tasks, trained using publicly available data. When fine-tuned, Llama-based models can be optimized for specific applications, enhancing their ability to generate accurate and context-aware responses. Fine-tuning enables the model to adapt to domain-specific data, improving performance in tasks such as: - -- Language translation – Enhancing fluency and contextual accuracy. -- Question answering – Providing precise and relevant responses. -- Text summarization – Extracting key insights while maintaining coherence. - -Fine-tuned LLaMA models are also highly effective in generating human-like text, making them valuable for: - -- Chatbots – Enabling intelligent and context-aware interactions. -- Virtual assistants – Enhancing responsiveness and personalization. -- Creative writing – Generating compelling and structured narratives. - -By fine-tuning Llama based models, their adaptability and relevance can be significantly improved, allowing seamless integration into specialized AI applications.Please note that the models are subject to the [acceptable use policy](https://github.com/facebookresearch/llama/blob/main/USE_POLICY.md) and [this responsible use guide](https://ai.meta.com/static-resource/responsible-use-guide/). - -#### Results - -Since LLaMA 2 and LLaMA 3 models require at least 4-bit quantization to accommodate the memory constraints of certain smartphones - -#### Quantization - -To optimize models for smartphone memory constraints, 4-bit groupwise per-token dynamic quantization can be applied to all linear layers. In this approach: - -- Dynamic quantization is used for activations, where quantization parameters are computed at runtime based on the min/max range. -- Static quantization is applied to weights, which are per-channel groupwise quantized using 4-bit signed integers. - -This method ensures efficient memory usage while maintaining model performance on resource-constrained devices. - -For further information, refer to [torchao: PyTorch Architecture Optimization](https://github.com/pytorch-labs/ao/). - -The table below evaluates WikiText perplexity using [LM Eval](https://github.com/EleutherAI/lm-evaluation-harness). - -The results are for two different groupsizes, with max_seq_len 2048, and 1000 samples: - -|Model | Baseline (FP32) | Groupwise 4-bit (128) | Groupwise 4-bit (256) -|--------|-----------------| ---------------------- | --------------- -|Llama 2 7B | 9.2 | 10.2 | 10.7 -|Llama 3 8B | 7.9 | 9.4 | 9.7 - -Note that groupsize less than 128 was not enabled in this example, since the model was still too large. This is because current efforts have focused on enabling FP32, and support for FP16 is under way. - -What this implies for model size is: - -1. Embedding table is in FP32. -2. Quantized weights scales are FP32. \ No newline at end of file diff --git a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md b/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md deleted file mode 100644 index 9b22537273..0000000000 --- a/content/learning-paths/embedded-and-microcontrollers/finetuning-llms-on-arm-powered-mobile-devices-with-executorch/how-to-7.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Prepared the Fine Tune Large Language Model for ExecuTorch and Mobile Deployment -weight: 8 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -#### Fine Tune Model Preparation - -- From the [Huggingface](https://huggingface.co/) need to apply for Repo access [Meta's Llama 3.2 language models](https://huggingface.co/meta-llama/Llama-3.2-1B). -- Download params.json and tokenizer.model from [Llama website](https://www.llama.com/llama-downloads/) or [Hugging Face](https://huggingface.co/meta-llama/Llama-3.2-1B). -- After fine-tuning the model, export the adapter_model.safetensors file locally and convert it to the adapter_model.pth format to .pte format. - -```python - python -m examples.models.llama.export_llama \ - --checkpoint \ - -p \ - -kv \ - --use_sdpa_with_kv_cache \ - -X \ - -qmode 8da4w \ - --group_size 128 \ - -d fp32 \ - --metadata '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}' \ - --embedding-quantize 4,32 \ - --output_name="llama3_kv_sdpa_xnn_qe_4_32.pte" -``` - -- Build the Llama Runner binary for [Android](https://learn.arm.com/learning-paths/mobile-graphics-and-gaming/build-llama3-chat-android-app-using-executorch-and-xnnpack/5-run-benchmark-on-android/). -- Build and Run [Android](https://learn.arm.com/learning-paths/mobile-graphics-and-gaming/build-llama3-chat-android-app-using-executorch-and-xnnpack/6-build-android-chat-app/). -- Open Android Studio and choose "Open an existing Android Studio project" to navigate to examples/demo-apps/android/LlamaDemo and Press Run (^R) to build and launch the app on your phone. -- Tap the Settings widget to select a model, configure its parameters, and set any prompts. -- After choosing the model, tokenizer, and model type, click "Load Model" to load it into the app and return to the main Chat activity. \ No newline at end of file