CindyDelage commited on
Commit
4320a11
·
1 Parent(s): 5a4403e

update space

Browse files
README.md CHANGED
@@ -1,13 +1,95 @@
1
  ---
2
  title: Frugal AI Agent
3
- emoji: 🌖
4
- colorFrom: pink
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 5.33.0
8
  app_file: app.py
9
  pinned: false
10
  short_description: Estimate carbon footprint and get frugal AI code suggestions
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Frugal AI Agent
3
+ emoji: 🌍
4
+ colorFrom: green
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 5.33.0
8
  app_file: app.py
9
  pinned: false
10
  short_description: Estimate carbon footprint and get frugal AI code suggestions
11
+ tags:
12
+ - agent-demo-track
13
+ - mcp-server-track
14
+ thumbnail: >-
15
+ https://cdn-uploads.huggingface.co/production/uploads/675973b3138cfdc3f3f4f85b/7Iw5hINn5IJRWMOEo3yY_.jpeg
16
  ---
17
 
18
+ # Frugalize it
19
+
20
+ This AI agent takes a Python code snippet, uses [CodeCarbon](https://mlco2.github.io/codecarbon/) to estimate its CO2 emissions, and then suggests modifications using frugal AI techniques such as pruning. It also provides general advice on how to make your code more energy-efficient.
21
+
22
+ Learn more about the principles behind this project by visiting the [Frugal AI Challenge](https://frugalaichallenge.org/about/).
23
+
24
+ **Special thanks to Anthropic for the free credits, which helped me run more tests and improve my agent!**
25
+
26
+ ## Getting Started
27
+
28
+ 1. **Add an Anthropic API key** in the repository's secrets variables to enable the agent.
29
+
30
+ 2. **Use the example prompts provided:**
31
+
32
+ Both examples use a context prompt beforehand to explain to the agent, Fruggy, what is expected from it. This context prompt is followed by a sample user query:
33
+ - The first example can be used directly: `"What are you capable of?"` - [Video demo agent-demo-track 1](https://youtu.be/3ybSoaxf_6g)
34
+ It runs a sample code (from the Frugal AI Challenge), estimates its energy consumption, and suggests optimizations using pruning and quantization.
35
+ - The second example: `"Here is my code, {code}, please give me frugal alternatives"` [Video demo agent-demo-track 2](https://youtu.be/8W7S4x2vcrE)
36
+ This cannot be used directly. Replace `{code}` with your own Python code. The agent will then return frugal alternatives and optimization suggestions.
37
+
38
+ You can also use the MCP client to call the agent: [Video demo mcp-server-track](https://youtu.be/yaRfHPyBdPA)
39
+
40
+ > ⚠️ Note: This agent is a proof of concept. There are many improvements and features that could be added. Besides, the example codes are quite basic and simple, both to ensure low CPU usage for generating the results and because they run directly in the cloud rather than locally in these examples.
41
+
42
+ ## Contribute
43
+
44
+ All types of contributions are, of course, welcome! Feel free to submit pull requests or contact me with any questions or feedback via the Hugging Face Discord (@cindydelage_51846).
45
+
46
+ ## What's next ?
47
+
48
+ As mentioned, there's still a lot to do, but the first improvements could include:
49
+
50
+ - Adding the context prompt used in the examples as a system prompt for the agent
51
+ - Enabling the Gradio interface to import a code file
52
+
53
+ ## CodeCarbon citation:
54
+
55
+ @software{benoit_courty_2024_11171501,
56
+ author = {Benoit Courty and
57
+ Victor Schmidt and
58
+ Sasha Luccioni and
59
+ Goyal-Kamal and
60
+ MarionCoutarel and
61
+ Boris Feld and
62
+ Jérémy Lecourt and
63
+ LiamConnell and
64
+ Amine Saboni and
65
+ Inimaz and
66
+ supatomic and
67
+ Mathilde Léval and
68
+ Luis Blanche and
69
+ Alexis Cruveiller and
70
+ ouminasara and
71
+ Franklin Zhao and
72
+ Aditya Joshi and
73
+ Alexis Bogroff and
74
+ Hugues de Lavoreille and
75
+ Niko Laskaris and
76
+ Edoardo Abati and
77
+ Douglas Blank and
78
+ Ziyao Wang and
79
+ Armin Catovic and
80
+ Marc Alencon and
81
+ Michał Stęchły and
82
+ Christian Bauer and
83
+ Lucas Otávio N. de Araújo and
84
+ JPW and
85
+ MinervaBooks},
86
+ title = {mlco2/codecarbon: v2.4.1},
87
+ month = may,
88
+ year = 2024,
89
+ publisher = {Zenodo},
90
+ version = {v2.4.1},
91
+ doi = {10.5281/zenodo.11171501},
92
+ url = {https://doi.org/10.5281/zenodo.11171501}
93
+ }
94
+
95
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import random
3
+ from smolagents import GradioUI, CodeAgent, InferenceClientModel
4
+ from smolagents import LiteLLMModel
5
+ from smolagents import ToolCallingAgent, PythonInterpreterTool, PromptTemplates
6
+ import os
7
+ from PIL import Image
8
+ #from smolagents.src.smolagents.prompts
9
+
10
+ # Import our custom tools from their modules
11
+ from tools import evaluate_consumption, evaluate_consumption_example
12
+ from retriever import FrugalAI_methods
13
+
14
+ #HF_TOKEN = os.environ.get("HF_TOKEN")
15
+ try:
16
+ API_KEY = os.environ.get("API_KEY")
17
+ except:
18
+ print("Please provide the Anthropic API_KEY in the secret variables")
19
+ # Initialize the Hugging Face model
20
+ model = LiteLLMModel(
21
+ model_id="anthropic/claude-3-5-sonnet-latest",
22
+ temperature=0.2,
23
+ api_key=API_KEY
24
+ )
25
+
26
+ evaluate_consumption = evaluate_consumption()
27
+ evaluate_consumption_example = evaluate_consumption_example()
28
+ FrugalAI_methods=FrugalAI_methods()
29
+
30
+ # Create agent with all the tools
31
+ fruggy = CodeAgent(
32
+ tools=[evaluate_consumption, evaluate_consumption_example, FrugalAI_methods],
33
+ model=model,
34
+ additional_authorized_imports=['os'],
35
+ add_base_tools=True, # Add any additional base tools
36
+ planning_interval=10
37
+ )
38
+
39
+ with gr.Blocks(theme=gr.themes.Ocean()) as demo:
40
+ with gr.Row():
41
+ with gr.Column(scale=1):
42
+ image = gr.Image(value=Image.open("frugal.jpg"), label="Frugal Image")
43
+ hello = gr.Interface(
44
+ fn=fruggy,
45
+ inputs="text",
46
+ outputs="text",
47
+ title="Frugalize it!",
48
+ examples = [
49
+ "You are an AI agent that receives Python code from a manager. "
50
+ "Your task is to test its resource consumption using a custom tool. If no code is provided, use evaluate_consumption_example. If a code is provided by the manager, use evaluate_consumption."
51
+ "Then, propose frugal alternatives, such as pruning, using your dedicated tool — always use it. "
52
+ "Additionally, feel free to search the internet for frugal methods and present them to the manager. "
53
+ "Examples include Knowledge Distillation, Transfer Learning, and others. "
54
+ "Show me what you can do using your custom tool evaluate_consumption_example, and provide examples based on the code in that tool to optimize it for frugality. "
55
+ "Use both your tools and web searches if needed. If implementation guidance is given by your custom tools, always give it in your final answer to the manager.",
56
+
57
+ "You are an AI agent that receives Python code from a manager. "
58
+ "Your task is to test its resource consumption using a custom tool. If no code is provided, use evaluate_consumption_example. If a code is provided by the manager, use evaluate_consumption."
59
+ "Then, propose frugal alternatives, such as pruning, using your dedicated tool — always use it. "
60
+ "Additionally, feel free to search the internet for frugal methods and present them to the manager. "
61
+ "Examples include Knowledge Distillation, Transfer Learning, and others. If implementation guidance is given by your custom tools, always give it in your final answer to the manager. "
62
+ "Here is my code: "
63
+ "import anthropic "
64
+ "API_KEY = os.environ.get('API_KEY') "
65
+ "client = anthropic.Anthropic(api_key=API_KEY) "
66
+ "message = client.messages.create(model='claude-sonnet-4-20250514',max_tokens=1024,messages=[{'role': 'user', 'content': 'Hello, Claude'}]) "
67
+ "print(message.content) "
68
+ "Please give me frugal alternatives."
69
+ ],
70
+ description="Share your Python code with this AI agent! It will track its CO2 emissions using CodeCarbon and recommend greener, frugal AI alternatives."
71
+ )
72
+
73
+ if __name__ == "__main__":
74
+ demo.launch(mcp_server=True, share=True)
frugal.jpg ADDED
requirements.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ transformers
2
+ torch
3
+ smolagents
4
+ pillow
5
+ fastapi
6
+ datasets
7
+ scikit-learn
8
+ numpy
9
+ pydantic
10
+ psutil
11
+ librosa
12
+ soundfile
13
+ codecarbon
14
+ smolagents[litellm]
15
+ smolagents[mcp]
16
+ duckduckgo_search
17
+ sentencepiece
18
+ anthropic
retriever.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import Tool
2
+
3
+ class FrugalAI_methods(Tool):
4
+ name = "FrugalAI_methods"
5
+ description = "Retrieves methods for model frugalization. It will return ideas to frugalize a code, please use it."
6
+ inputs = {
7
+ "method": {
8
+ "type": "string",
9
+ "description": "Always set it to 'Frugal' or it will raise an error."
10
+ }
11
+ }
12
+ output_type = "string"
13
+
14
+ def forward(self, method):
15
+ ideas=[]
16
+ ideas.append(pruning())
17
+ ideas.append(quantization())
18
+ return ideas
19
+
20
+ def pruning():
21
+ """
22
+ Optimizes models by removing unnecessary components, such as certain weights in a neural network.
23
+ This function demonstrates how to apply pruning.
24
+ """
25
+ code = "model = apply_pruning(model, amount=0.3)"
26
+ return (
27
+ f"To apply pruning to a model, use the following code snippet: {code}. "
28
+ f"You should adapt it to your actual implementation. In particular, the 'amount' parameter "
29
+ f"can be increased or decreased depending on the initial number of weights and the complexity of your use case (minimu value: 0, maximum value: 1). You should give these information to the manager, you can not prune the model yourself."
30
+ )
31
+
32
+ def quantization():
33
+ """
34
+ Converts high-precision weights into lower-precision one to reduce cost.
35
+ """
36
+ code = "model = torch.quantization.quantize_dynamic(model, dtype=torch.qint8)"
37
+ return (
38
+ f"To apply quantization to a model, use the following code snippet: {code}. You should give these information to the manager, you can not quantize the model yourself."
39
+ )
tools.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+ from datetime import datetime
3
+ from datasets import load_dataset
4
+ from sklearn.metrics import accuracy_score
5
+ import numpy as np
6
+ import os
7
+ import torch
8
+ import gc
9
+ import psutil
10
+ from transformers import Wav2Vec2ForSequenceClassification, AutoFeatureExtractor, pipeline
11
+ from utils.evaluation import AudioEvaluationRequest
12
+ from utils.emissions import tracker, clean_emissions_data, get_space_info
13
+ from dotenv import load_dotenv
14
+ import logging
15
+ import csv
16
+ import torch.nn.utils.prune as prune
17
+ from typing import Optional
18
+ from pydantic import BaseModel, Field
19
+ from smolagents import Tool
20
+
21
+ # Configurer le logging
22
+ logging.basicConfig(level=logging.INFO)
23
+ logging.info("Début du fichier python")
24
+ load_dotenv()
25
+
26
+ router = APIRouter()
27
+
28
+ DESCRIPTION = "Random Baseline"
29
+ ROUTE = "/audio"
30
+
31
+ device = 0 if torch.cuda.is_available() else -1
32
+
33
+ def preprocess_function(example, feature_extractor):
34
+ return feature_extractor(
35
+ [x["array"] for x in example["audio"]],
36
+ sampling_rate=feature_extractor.sampling_rate, padding="longest", max_length=16000, truncation=True, return_tensors="pt"
37
+ )
38
+
39
+ def apply_pruning(model, amount=0.3):
40
+ for name, module in model.named_modules():
41
+ if isinstance(module, torch.nn.Linear):
42
+ prune.l1_unstructured(module, name="weight", amount=amount)
43
+ prune.remove(module, "weight")
44
+ return model
45
+
46
+ class BaseEvaluationRequest(BaseModel):
47
+ test_size: float = Field(0.2, ge=0.0, le=1.0, description="Size of the test split (between 0 and 1)")
48
+ test_seed: int = Field(42, ge=0, description="Random seed for reproducibility")
49
+
50
+ class AudioEvaluationRequest(BaseEvaluationRequest):
51
+ dataset_name: str = Field("rfcx/frugalai",
52
+ description="The name of the dataset on HuggingFace Hub")
53
+
54
+ class evaluate_consumption_example(Tool):
55
+ name = "evaluate_consumption_example"
56
+ description = "This is only an example. If a manager wants to know what you are capable of, use it : it will use code carbon to evaluate the CO2 emissions from an example Python code"
57
+ inputs = {
58
+ "code": {
59
+ "type": "string",
60
+ "description": "The code to evaluate. Here, it is an example, so just set it to 'None'."
61
+ }
62
+ }
63
+ output_type = "string"
64
+
65
+ def forward(self, code : str):
66
+ request = AudioEvaluationRequest()
67
+ logging.info("Chargement des données")
68
+ dataset = load_dataset(request.dataset_name, streaming=True, token=os.getenv("HF_TOKEN"))
69
+ logging.info("Données chargées")
70
+
71
+ test_dataset = dataset["test"]
72
+ del dataset
73
+
74
+ # Start tracking emissions
75
+ tracker.start()
76
+ tracker.start_task("inference")
77
+
78
+ feature_extractor = AutoFeatureExtractor.from_pretrained("facebook/wav2vec2-base")
79
+
80
+ test_dataset = test_dataset.map(preprocess_function, fn_kwargs={"feature_extractor": feature_extractor}, remove_columns="audio", batched=True, batch_size=32)
81
+
82
+ gc.collect()
83
+
84
+ model_name = "CindyDelage/Challenge_HuggingFace_DFG_FrugalAI"
85
+ model = Wav2Vec2ForSequenceClassification.from_pretrained(model_name)
86
+
87
+ # Appliquer la quantification dynamique et le pruning
88
+ model.eval()
89
+ #model = torch.quantization.quantize_dynamic(model, dtype=torch.qint8)
90
+ #model = apply_pruning(model, amount=0.3) # Prune 30% des poids linéaires
91
+
92
+ classifier = pipeline("audio-classification", model=model, feature_extractor=feature_extractor, device=device)
93
+ predictions = []
94
+ logging.info("Début des prédictions par batch")
95
+ i=0
96
+ for data in iter(test_dataset):
97
+ print(i)
98
+ if (i<=5):
99
+ with torch.no_grad():
100
+ result = classifier(np.asarray(data["input_values"]), batch_size=64)
101
+ predicted_label = result[0]['label']
102
+ label = 1 if predicted_label == 'environment' else 0
103
+ predictions.append(label)
104
+
105
+ # Nettoyer la mémoire après chaque itération
106
+ del result
107
+ del label
108
+ torch.cuda.empty_cache()
109
+ gc.collect()
110
+ i=i+1
111
+ if(i>5):
112
+ break
113
+ logging.info("Fin des prédictions")
114
+ del classifier
115
+ del feature_extractor
116
+
117
+ gc.collect()
118
+ # Stop tracking emissions
119
+ emissions_data = tracker.stop_task()
120
+
121
+ return emissions_data
122
+
123
+ class evaluate_consumption(Tool):
124
+ name = "evaluate_consumption"
125
+ description = "If the manager gave you its Python code, this function uses code carbon to evaluate the CO2 emissions from the given Python code"
126
+ inputs = {
127
+ "code": {
128
+ "type": "string",
129
+ "description": "The code to evaluate."
130
+ }
131
+ }
132
+ output_type = "string"
133
+
134
+ def forward(self, code : str):
135
+
136
+ # Start tracking emissions
137
+ tracker.start()
138
+ tracker.start_task("inference")
139
+ exec(code)
140
+ # Stop tracking emissions
141
+ emissions_data = tracker.stop_task()
142
+
143
+ return emissions_data
utils/__init__.py ADDED
File without changes
utils/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (147 Bytes). View file
 
utils/__pycache__/emissions.cpython-312.pyc ADDED
Binary file (1.84 kB). View file
 
utils/__pycache__/evaluation.cpython-312.pyc ADDED
Binary file (1.61 kB). View file
 
utils/emissions.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from codecarbon import EmissionsTracker
2
+ import os
3
+
4
+ # Initialize tracker
5
+ tracker = EmissionsTracker(allow_multiple_runs=True)
6
+
7
+ class EmissionsData:
8
+ def __init__(self, energy_consumed: float, emissions: float):
9
+ self.energy_consumed = energy_consumed
10
+ self.emissions = emissions
11
+
12
+ def clean_emissions_data(emissions_data):
13
+ """Remove unwanted fields from emissions data"""
14
+ data_dict = emissions_data.__dict__
15
+ fields_to_remove = ['timestamp', 'project_name', 'experiment_id', 'latitude', 'longitude']
16
+ return {k: v for k, v in data_dict.items() if k not in fields_to_remove}
17
+
18
+ def get_space_info():
19
+ """Get the space username and URL from environment variables"""
20
+ space_name = os.getenv("SPACE_ID", "")
21
+ if space_name:
22
+ try:
23
+ username = space_name.split("/")[0]
24
+ space_url = f"https://huggingface.co/spaces/{space_name}"
25
+ return username, space_url
26
+ except Exception as e:
27
+ print(f"Error getting space info: {e}")
28
+ return "local-user", "local-development"
utils/evaluation.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ from pydantic import BaseModel, Field
3
+
4
+ class BaseEvaluationRequest(BaseModel):
5
+ test_size: float = Field(0.2, ge=0.0, le=1.0, description="Size of the test split (between 0 and 1)")
6
+ test_seed: int = Field(42, ge=0, description="Random seed for reproducibility")
7
+
8
+ class TextEvaluationRequest(BaseEvaluationRequest):
9
+ dataset_name: str = Field("QuotaClimat/frugalaichallenge-text-train",
10
+ description="The name of the dataset on HuggingFace Hub")
11
+
12
+ class ImageEvaluationRequest(BaseEvaluationRequest):
13
+ dataset_name: str = Field("pyronear/pyro-sdis",
14
+ description="The name of the dataset on HuggingFace Hub")
15
+
16
+ class AudioEvaluationRequest(BaseEvaluationRequest):
17
+ dataset_name: str = Field("rfcx/frugalai",
18
+ description="The name of the dataset on HuggingFace Hub")