Distilabel is a framework for synthetic data and AI feedback for engineers who need fast, reliable and scalable pipelines based on verified research papers.
See the code[!IMPORTANT]
The original authors have moved on to other projects. While the code might still be functional for its original purpose, please be aware that the original team does not plan to develop new features, bug fixes, or updates. If you'd like to become a maintainer, please open an issue to discuss it.
Distilabel is the framework for synthetic data and AI feedback for engineers who need fast, reliable and scalable pipelines based on verified research papers.
If you just want to get started, we recommend you check the documentation. Curious, and want to know more? Keep reading!
Distilabel can be used for generating synthetic data and AI feedback for a wide variety of projects including traditional predictive NLP (classification, extraction, etc.), or generative and large language model scenarios (instruction following, dialogue generation, judging etc.). Distilabel's programmatic approach allows you to build scalable pipelines for data generation and AI feedback. The goal of distilabel is to accelerate your AI development by quickly generating high-quality, diverse datasets based on verified research methodologies for generating and judging with AI feedback.
Compute is expensive and output quality is important. We help you focus on data quality, which tackles the root cause of both of these problems at once. Distilabel helps you to synthesize and judge data to let you spend your valuable time achieving and keeping high-quality standards for your data.
Ownership of data for fine-tuning your own LLMs is not easy but Distilabel can help you to get started. We integrate AI feedback from any LLM provider out there using one unified API.
Synthesize and judge data with latest research papers while ensuring flexibility, scalability and fault tolerance. So you can focus on improving your data and training your models.
We are an open-source community-driven project and we love to hear from you. Here are some ways to get involved:
Community Meetup: listen in or present during one of our bi-weekly events.
Discord: get direct support from the community in #argilla-general and #argilla-help.
Roadmap: plans change but we love to discuss those with our community so feel encouraged to participate.
The Argilla community uses distilabel to create amazing datasets and models.
pip install distilabel --upgrade
Requires Python 3.9+
In addition, the following extras are available:
anthropic: for using models available in Anthropic API via the AnthropicLLM integration.cohere: for using models available in Cohere via the CohereLLM integration.argilla: for exporting the generated datasets to Argilla.groq: for using models available in Groq using groq Python client via the GroqLLM integration.hf-inference-endpoints: for using the Hugging Face Inference Endpoints via the InferenceEndpointsLLM integration.hf-transformers: for using models available in transformers package via the TransformersLLM integration.litellm: for using LiteLLM to call any LLM using OpenAI format via the LiteLLM integration.llama-cpp: for using llama-cpp-python Python bindings for llama.cpp via the LlamaCppLLM integration.mistralai: for using models available in Mistral AI API via the MistralAILLM integration.ollama: for using Ollama and their available models via OllamaLLM integration.openai: for using OpenAI API models via the OpenAILLM integration, or the rest of the integrations based on OpenAI and relying on its client as AnyscaleLLM, AzureOpenAILLM, and TogetherLLM.vertexai: for using Google Vertex AI proprietary models via the VertexAILLM integration.vllm: for using vllm serving engine via the vLLM integration.sentence-transformers: for generating sentence embeddings using sentence-transformers.mlx: for using MLX models via the MlxLLM integration.outlines: for using structured generation of LLMs with outlines.instructor: for using structured generation of LLMs with Instructor.ray: for scaling and distributing a pipeline with Ray.faiss-cpu and faiss-gpu: for generating sentence embeddings using faiss.text-clustering: for using text clustering with UMAP and Scikit-learn.minhash: for using minhash for duplicate detection with datasketch and nltk.To run the following example you must install distilabel with the hf-inference-endpoints extra:
pip install "distilabel[hf-inference-endpoints]" --upgrade
Then run:
from datasets import load_dataset
from distilabel.models import InferenceEndpointsLLM
from distilabel.pipeline import Pipeline
from distilabel.steps.tasks import TextGeneration
with Pipeline() as pipeline:
TextGeneration(
llm=InferenceEndpointsLLM(
model_id="meta-llama/Meta-Llama-3.1-8B-Instruct",
generation_kwargs={"temperature": 0.7, "max_new_tokens": 512},
),
)
if __name__ == "__main__":
dataset = load_dataset("distilabel-internal-testing/instructions", split="test")
distiset = pipeline.run(dataset=dataset)
distiset.push_to_hub(repo_id="distilabel-example")
If you build something cool with distilabel consider adding one of these badges to your dataset or model card.
[<img src="https://raw.githubusercontent.com/argilla-io/distilabel/main/docs/assets/distilabel-badge-light.png" alt="Built with Distilabel" width="200" height="32"/>](https://github.com/argilla-io/distilabel)
[<img src="https://raw.githubusercontent.com/argilla-io/distilabel/main/docs/assets/distilabel-badge-dark.png" alt="Built with Distilabel" width="200" height="32"/>](https://github.com/argilla-io/distilabel)
To directly contribute with distilabel, check our good first issues or open a new one.
distilabel directory. e.g. python distilabel/pipelines/single_page_qa.py.env file. OPENAI_API_KEY at least is required (but can be None).pipelines/single_page_qa.py. Put new pipelines here. The single page one is a good reference for how to do everything, copy and modifysrc/distilabel/configs/single_pages.py. The config for single page QA, check it out to understand how the pipeline runs and what you can modifysrc/distilabel/pydantics.py. Put Pydantic models here (configs, output formats)src/distilabel/llms/openai_compatible.py, vllm_api.py. The wrapper that handles structured generation with openai compatible endpoints for different providers and vLLM servers as well.src/distilabel/utils/misc.py, prompt_sampler.py, pipe_utils.py, image.py. Check out the prompt sampler and how it works in the config. pipe_utils.py has useful/reusable code for pipelines in general.src/distilabel/steps/columns/pydantic_to_cols.py, .../steps/filtering/filter_rows.py, .../steps/list_to_rows.py, .../tasks/lm_generation.py. You can see each of them imported in the single page qa pipeline. lm_generation.py is important to know of because I use it for the structured generation step using a LM. Kind of obvious, but this is where your custom steps go.path/to/pdf_page_x.pdf). This is atm only an expectation in VLM._format_input() when it is passed to LMGenerationTask.input_formatter, so you can change the input_formatter/override this if you need or just make VLM._format_input() more general.CudaDevicePlacementMixin and breaking the tasks into multiple load stages so that there are enough gpus available during each.constants.py), if it doesn't receive an output batch within that timeframe, it will break and finalize the pipeline execution. Hanging issues are easy to run into so this just closes it in a controlled manner.def process(self, *inputs: StepInput) -> "StepOutput": is guaranteed.NoOp to break it up.invalidate_cache=True to them to tell them to redo things. You can also pass invalidate_distiset=True to the pipeline to not used the distiset which is cached on completion of a pipeline.use_cache=True when creating anything inheriting from _Step to turn it on.use_cache=True/False and invalidate_cache=True/False.
use_cache and invalidate_cache when initializing the Step/Taskuse_cache and invalidate_cache when initializing the OpenAILM.use_cache and invalidate_distiset when calling pipeline.run().DISTILABEL_ENABLE_TIMER=1. You can decorate functions as shown in distilabel/pipeline/base.py.write_buffer.py writes all the final batches to disk in parquets (writing every constants.WRITE_BUFFER_SIZE rows of data). Then it has to reload each of them to make sure they have a matching schema for the whole set. Then, they are loaded into a distiset and returned. This can take e.g. 10 min for 60K batches with 16M rows. Just note the time it takes.Config(use_running_vllm=True) in the config) with the environment variable VLLM_API_BASE_URL.uv to make the environment. Simply use uv sync then uv sync --extra flash from the root dir of the repository. This should run the pipelines I have built though it may be missing dependencies for things like ray (which aren't supported atm anyways).load() method instead of __init__, since __init__ is handled by pydantic and you'll be able to see the inherited args if you don't override it. It also matches the logic of the library better (matching load groups better for instance).**kwargs was a required runtime parameter that needed to be set at pipeline init. The solution I am using is to copy the function signature from the library, though this isn't ideal for maintenance.LMGenerationTask which were due to the parameter add_raw_input=True. Since I overrode the OpenAILLM class to add support for more flexible vision (arbitrary number/order of images in chat format)(and to allow grouping all model providers under a single class), the formatted input was a list of messages, some text, some visual, all in one column (so you can vary the number of images). Pyarrow can't make a table out of this because the structure of a text and an image message are different so it can't make a type for the column. Thus, I have set self.add_raw_input=False in e.g. the LMGenerationTask.
StepResources seems like it might handle scheduling tasks across gpus for you, but I understand this only happens when using Ray, which has some internal scheduling that will respect those resources (there's a section in the documentation about how to use Ray for larger scale distributed generation).
replicas, which is basically just data parallelism for non-generator/global steps/tasks (replicates models as well).Task.unload() calls self.llm.unload() so you don't have to handle it yourself. If you wanted to keep it alive (say the vllm server), you'd need to get around this>> syntax, for each task in the previous stage, it sends the task's completed batches to all of the next stage (or in the case of using a router, it will select some set of the next stage per batch)LMGenerationTask.format_output().LMGenerationTask has an input_formatter callable attribute. It loads the yaml with yaml.FullLoader which won't allow arbitrary python execution (setting the input_formatter). I found Field(exclude=True) in Pydantic to solve this. Then it occurred to me that I should do the same for the configs I was using rather than erasing their signatures. After this, there was another error in resuming because it couldn't initialize the e.g. LMGenerationTask without providing the configs. So, I gave these default initializations. This uncovered another error which was a bug in distilabel, I had no choice but to modify the actual package to fix it. In DAG.from_dict(), they don't set the routing_batch_function._step which is set during Step.connect(), so I just added the line to do that.
pipeline.run(), one of the early steps is self._refresh_pipeline_from_cache() which essentially creates an entirely new dag from the cached information. Then, for excluded or secret fields, it sets them using the values of the current dag. Now that I know this, their design seems reasonable, but it is important that you understand the effect of Field(exclude=True) to get resuming working properly. The need for serialization and deserialization also justifies the extensive use of Pydantic in distilabel.vllm_api field to private so that it didn't try to serialize it in multiprocessing.NoOp step that can serve as a 'junction' before the routing batch function._manage_batch_flow, _BatchManagerStep._get_data(), get_batch() and add_batch() and _initialize_pipeline_execution() which are related to batches in distilabel. I am not sure how exactly to solve this if it happens on something expensive to re-run. Maybe try manually editing the cache if you can find the right information.
os.environ['CUDA_VISIBLE_DEVICES'] fail, when it has been stopped in a weird way. Run it and let it crash, clear the cache and restart it. If you want to avoid clearing the cache, perhaps try finding the device_map file from cuda_device_placement.py and clearing that.LAST_BATCH_SENT_FLAG only to predecessors of a step that had sent a last batch to output. i.e. the step should be done, but only the predecessors get told to quit. When a load stage ends with a step that has multiple replicas, it will wait for the next stage, but not send a signal to tell the other replicas to quit working. I added sending a signal to the step that sent the last batch to output so that all the replicas are closed if needed. (this seems like it should be the way to do things in the first place, but I will settle for adding it on top of existing logic in case there was another reason for it)DISTILABEL_LOG_LEVEL=DEBUG to see a lot of helpful info.if num_rows == 0 and step_name in self.last_batch_received: in _BatchManagerStep (around L:555). Edit: I have made an additional case for this to avoid hanging, but I am unsure if there are unintended consequences.vllm_logs/ for the appropriate rank._thread.lock. In my case, this was due to not having pynvml installed and it crashed when trying to load the LMGenerationTask.@misc{distilabel-argilla-2024,
author = {Álvaro Bartolomé Del Canto and Gabriel Martín Blázquez and Agustín Piqueres Lajarín and Daniel Vila Suero},
title = {Distilabel: An AI Feedback (AIF) framework for building datasets with and for LLMs},
year = {2024},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/argilla-io/distilabel}}
}
Distilabel is a framework for synthetic data and AI feedback for engineers who need fast, reliable and scalable pipelines based on verified research papers.
See the code[!IMPORTANT]
The original authors have moved on to other projects. While the code might still be functional for its original purpose, please be aware that the original team does not plan to develop new features, bug fixes, or updates. If you'd like to become a maintainer, please open an issue to discuss it.
Distilabel is the framework for synthetic data and AI feedback for engineers who need fast, reliable and scalable pipelines based on verified research papers.
If you just want to get started, we recommend you check the documentation. Curious, and want to know more? Keep reading!
Distilabel can be used for generating synthetic data and AI feedback for a wide variety of projects including traditional predictive NLP (classification, extraction, etc.), or generative and large language model scenarios (instruction following, dialogue generation, judging etc.). Distilabel's programmatic approach allows you to build scalable pipelines for data generation and AI feedback. The goal of distilabel is to accelerate your AI development by quickly generating high-quality, diverse datasets based on verified research methodologies for generating and judging with AI feedback.
Compute is expensive and output quality is important. We help you focus on data quality, which tackles the root cause of both of these problems at once. Distilabel helps you to synthesize and judge data to let you spend your valuable time achieving and keeping high-quality standards for your data.
Ownership of data for fine-tuning your own LLMs is not easy but Distilabel can help you to get started. We integrate AI feedback from any LLM provider out there using one unified API.
Synthesize and judge data with latest research papers while ensuring flexibility, scalability and fault tolerance. So you can focus on improving your data and training your models.
We are an open-source community-driven project and we love to hear from you. Here are some ways to get involved:
Community Meetup: listen in or present during one of our bi-weekly events.
Discord: get direct support from the community in #argilla-general and #argilla-help.
Roadmap: plans change but we love to discuss those with our community so feel encouraged to participate.
The Argilla community uses distilabel to create amazing datasets and models.
pip install distilabel --upgrade
Requires Python 3.9+
In addition, the following extras are available:
anthropic: for using models available in Anthropic API via the AnthropicLLM integration.cohere: for using models available in Cohere via the CohereLLM integration.argilla: for exporting the generated datasets to Argilla.groq: for using models available in Groq using groq Python client via the GroqLLM integration.hf-inference-endpoints: for using the Hugging Face Inference Endpoints via the InferenceEndpointsLLM integration.hf-transformers: for using models available in transformers package via the TransformersLLM integration.litellm: for using LiteLLM to call any LLM using OpenAI format via the LiteLLM integration.llama-cpp: for using llama-cpp-python Python bindings for llama.cpp via the LlamaCppLLM integration.mistralai: for using models available in Mistral AI API via the MistralAILLM integration.ollama: for using Ollama and their available models via OllamaLLM integration.openai: for using OpenAI API models via the OpenAILLM integration, or the rest of the integrations based on OpenAI and relying on its client as AnyscaleLLM, AzureOpenAILLM, and TogetherLLM.vertexai: for using Google Vertex AI proprietary models via the VertexAILLM integration.vllm: for using vllm serving engine via the vLLM integration.sentence-transformers: for generating sentence embeddings using sentence-transformers.mlx: for using MLX models via the MlxLLM integration.outlines: for using structured generation of LLMs with outlines.instructor: for using structured generation of LLMs with Instructor.ray: for scaling and distributing a pipeline with Ray.faiss-cpu and faiss-gpu: for generating sentence embeddings using faiss.text-clustering: for using text clustering with UMAP and Scikit-learn.minhash: for using minhash for duplicate detection with datasketch and nltk.To run the following example you must install distilabel with the hf-inference-endpoints extra:
pip install "distilabel[hf-inference-endpoints]" --upgrade
Then run:
from datasets import load_dataset
from distilabel.models import InferenceEndpointsLLM
from distilabel.pipeline import Pipeline
from distilabel.steps.tasks import TextGeneration
with Pipeline() as pipeline:
TextGeneration(
llm=InferenceEndpointsLLM(
model_id="meta-llama/Meta-Llama-3.1-8B-Instruct",
generation_kwargs={"temperature": 0.7, "max_new_tokens": 512},
),
)
if __name__ == "__main__":
dataset = load_dataset("distilabel-internal-testing/instructions", split="test")
distiset = pipeline.run(dataset=dataset)
distiset.push_to_hub(repo_id="distilabel-example")
If you build something cool with distilabel consider adding one of these badges to your dataset or model card.
[<img src="https://raw.githubusercontent.com/argilla-io/distilabel/main/docs/assets/distilabel-badge-light.png" alt="Built with Distilabel" width="200" height="32"/>](https://github.com/argilla-io/distilabel)
[<img src="https://raw.githubusercontent.com/argilla-io/distilabel/main/docs/assets/distilabel-badge-dark.png" alt="Built with Distilabel" width="200" height="32"/>](https://github.com/argilla-io/distilabel)
To directly contribute with distilabel, check our good first issues or open a new one.
distilabel directory. e.g. python distilabel/pipelines/single_page_qa.py.env file. OPENAI_API_KEY at least is required (but can be None).pipelines/single_page_qa.py. Put new pipelines here. The single page one is a good reference for how to do everything, copy and modifysrc/distilabel/configs/single_pages.py. The config for single page QA, check it out to understand how the pipeline runs and what you can modifysrc/distilabel/pydantics.py. Put Pydantic models here (configs, output formats)src/distilabel/llms/openai_compatible.py, vllm_api.py. The wrapper that handles structured generation with openai compatible endpoints for different providers and vLLM servers as well.src/distilabel/utils/misc.py, prompt_sampler.py, pipe_utils.py, image.py. Check out the prompt sampler and how it works in the config. pipe_utils.py has useful/reusable code for pipelines in general.src/distilabel/steps/columns/pydantic_to_cols.py, .../steps/filtering/filter_rows.py, .../steps/list_to_rows.py, .../tasks/lm_generation.py. You can see each of them imported in the single page qa pipeline. lm_generation.py is important to know of because I use it for the structured generation step using a LM. Kind of obvious, but this is where your custom steps go.path/to/pdf_page_x.pdf). This is atm only an expectation in VLM._format_input() when it is passed to LMGenerationTask.input_formatter, so you can change the input_formatter/override this if you need or just make VLM._format_input() more general.CudaDevicePlacementMixin and breaking the tasks into multiple load stages so that there are enough gpus available during each.constants.py), if it doesn't receive an output batch within that timeframe, it will break and finalize the pipeline execution. Hanging issues are easy to run into so this just closes it in a controlled manner.def process(self, *inputs: StepInput) -> "StepOutput": is guaranteed.NoOp to break it up.invalidate_cache=True to them to tell them to redo things. You can also pass invalidate_distiset=True to the pipeline to not used the distiset which is cached on completion of a pipeline.use_cache=True when creating anything inheriting from _Step to turn it on.use_cache=True/False and invalidate_cache=True/False.
use_cache and invalidate_cache when initializing the Step/Taskuse_cache and invalidate_cache when initializing the OpenAILM.use_cache and invalidate_distiset when calling pipeline.run().DISTILABEL_ENABLE_TIMER=1. You can decorate functions as shown in distilabel/pipeline/base.py.write_buffer.py writes all the final batches to disk in parquets (writing every constants.WRITE_BUFFER_SIZE rows of data). Then it has to reload each of them to make sure they have a matching schema for the whole set. Then, they are loaded into a distiset and returned. This can take e.g. 10 min for 60K batches with 16M rows. Just note the time it takes.Config(use_running_vllm=True) in the config) with the environment variable VLLM_API_BASE_URL.uv to make the environment. Simply use uv sync then uv sync --extra flash from the root dir of the repository. This should run the pipelines I have built though it may be missing dependencies for things like ray (which aren't supported atm anyways).load() method instead of __init__, since __init__ is handled by pydantic and you'll be able to see the inherited args if you don't override it. It also matches the logic of the library better (matching load groups better for instance).**kwargs was a required runtime parameter that needed to be set at pipeline init. The solution I am using is to copy the function signature from the library, though this isn't ideal for maintenance.LMGenerationTask which were due to the parameter add_raw_input=True. Since I overrode the OpenAILLM class to add support for more flexible vision (arbitrary number/order of images in chat format)(and to allow grouping all model providers under a single class), the formatted input was a list of messages, some text, some visual, all in one column (so you can vary the number of images). Pyarrow can't make a table out of this because the structure of a text and an image message are different so it can't make a type for the column. Thus, I have set self.add_raw_input=False in e.g. the LMGenerationTask.
StepResources seems like it might handle scheduling tasks across gpus for you, but I understand this only happens when using Ray, which has some internal scheduling that will respect those resources (there's a section in the documentation about how to use Ray for larger scale distributed generation).
replicas, which is basically just data parallelism for non-generator/global steps/tasks (replicates models as well).Task.unload() calls self.llm.unload() so you don't have to handle it yourself. If you wanted to keep it alive (say the vllm server), you'd need to get around this>> syntax, for each task in the previous stage, it sends the task's completed batches to all of the next stage (or in the case of using a router, it will select some set of the next stage per batch)LMGenerationTask.format_output().LMGenerationTask has an input_formatter callable attribute. It loads the yaml with yaml.FullLoader which won't allow arbitrary python execution (setting the input_formatter). I found Field(exclude=True) in Pydantic to solve this. Then it occurred to me that I should do the same for the configs I was using rather than erasing their signatures. After this, there was another error in resuming because it couldn't initialize the e.g. LMGenerationTask without providing the configs. So, I gave these default initializations. This uncovered another error which was a bug in distilabel, I had no choice but to modify the actual package to fix it. In DAG.from_dict(), they don't set the routing_batch_function._step which is set during Step.connect(), so I just added the line to do that.
pipeline.run(), one of the early steps is self._refresh_pipeline_from_cache() which essentially creates an entirely new dag from the cached information. Then, for excluded or secret fields, it sets them using the values of the current dag. Now that I know this, their design seems reasonable, but it is important that you understand the effect of Field(exclude=True) to get resuming working properly. The need for serialization and deserialization also justifies the extensive use of Pydantic in distilabel.vllm_api field to private so that it didn't try to serialize it in multiprocessing.NoOp step that can serve as a 'junction' before the routing batch function._manage_batch_flow, _BatchManagerStep._get_data(), get_batch() and add_batch() and _initialize_pipeline_execution() which are related to batches in distilabel. I am not sure how exactly to solve this if it happens on something expensive to re-run. Maybe try manually editing the cache if you can find the right information.
os.environ['CUDA_VISIBLE_DEVICES'] fail, when it has been stopped in a weird way. Run it and let it crash, clear the cache and restart it. If you want to avoid clearing the cache, perhaps try finding the device_map file from cuda_device_placement.py and clearing that.LAST_BATCH_SENT_FLAG only to predecessors of a step that had sent a last batch to output. i.e. the step should be done, but only the predecessors get told to quit. When a load stage ends with a step that has multiple replicas, it will wait for the next stage, but not send a signal to tell the other replicas to quit working. I added sending a signal to the step that sent the last batch to output so that all the replicas are closed if needed. (this seems like it should be the way to do things in the first place, but I will settle for adding it on top of existing logic in case there was another reason for it)DISTILABEL_LOG_LEVEL=DEBUG to see a lot of helpful info.if num_rows == 0 and step_name in self.last_batch_received: in _BatchManagerStep (around L:555). Edit: I have made an additional case for this to avoid hanging, but I am unsure if there are unintended consequences.vllm_logs/ for the appropriate rank._thread.lock. In my case, this was due to not having pynvml installed and it crashed when trying to load the LMGenerationTask.@misc{distilabel-argilla-2024,
author = {Álvaro Bartolomé Del Canto and Gabriel Martín Blázquez and Agustín Piqueres Lajarín and Daniel Vila Suero},
title = {Distilabel: An AI Feedback (AIF) framework for building datasets with and for LLMs},
year = {2024},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/argilla-io/distilabel}}
}