Hub documentation

Spark

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Spark

Spark permet le traitement de données en temps réel et à grande échelle dans un environnement distribué.

Vous pouvez utiliser pyspark_huggingface pour accéder aux dépôts de jeux de données Hugging Face dans PySpark via la source de données « huggingface ».

Essayez Spark Notebooks sur Hugging Face Spaces pour obtenir des Notebooks avec PySpark et pyspark_huggingface préinstallés.

Configuration

Installation

Pour pouvoir lire et écrire dans Hugging Face Datasets, vous devez installer la bibliothèque pyspark_huggingface :

pip install pyspark_huggingface

Cela installera également les dépendances requises, telles que huggingface_hub pour l'authentification et pyarrow pour la lecture et l'écriture des jeux de données.

Authentification

Vous devez vous authentifier sur Hugging Face pour lire des dépôts de jeux de données privés ou à accès restreint, ou pour écrire dans vos dépôts de jeux de données.

Vous pouvez utiliser l’interface en ligne de commande, par exemple :

hf auth login

Il est également possible de fournir votre jeton Hugging Face avec la variable d’environnement HF_TOKEN ou en passant l’option token au lecteur. Pour plus de détails sur l’authentification, consultez ce guide.

Activer la source de données « huggingface »

PySpark 4 est livré avec une nouvelle API de source de données qui permet d’utiliser des jeux de données provenant de sources personnalisées. Si pyspark_huggingface est installé, PySpark l’importe automatiquement et active la source de données « huggingface ».

La bibliothèque rétroporte également l’API Data Source pour la source de données « huggingface » pour PySpark 3.5, 3.4 et 3.3. Cependant, dans ce cas, pyspark_huggingface doit être importé explicitement pour activer la rétroportation et permettre l’utilisation de la source de données « huggingface » :

>>> import pyspark_huggingface
huggingface datasource enabled for pyspark 3.x.x (backport from pyspark 4)

Lire

La source de données « huggingface » permet de lire des jeux de données depuis Hugging Face, en utilisant pyarrow en arrière-plan pour diffuser les données Arrow. Cela est compatible avec tous les jeux de données au format pris en charge sur Hugging Face, comme les jeux de données Parquet.

Par exemple, voici comment charger le jeu de données stanfordnlp/imdb :

>>> import pyspark_huggingface
>>> from pyspark.sql import SparkSession
>>> spark = SparkSession.builder.appName("demo").getOrCreate()
>>> df = spark.read.format("huggingface").load("stanfordnlp/imdb")

Voici un autre exemple avec le jeu de données BAAI/Infinity-Instruct. Il s’agit d’un dépôt à accès restreint : les utilisateurs doivent accepter les conditions d’utilisation avant d’y accéder. Il comporte également plusieurs sous-ensembles, à savoir « 3M » et « 7M ». Il faut donc préciser lequel charger.

Nous utilisons la fonction .format() pour utiliser la source de données « huggingface », et .load() pour charger le jeu de données (plus précisément la configuration ou le sous-ensemble nommé « 7M » contenant 7M échantillons). Nous calculons ensuite le nombre de dialogues par langue et filtrons le jeu de données.

Après s'être connecté pour accéder au dépôt restreint, nous pouvons exécuter :

>>> import pyspark_huggingface
>>> from pyspark.sql import SparkSession
>>> spark = SparkSession.builder.appName("demo").getOrCreate()
>>> df = spark.read.format("huggingface").option("config", "7M").load("BAAI/Infinity-Instruct")
>>> df.show()
+---+----------------------------+-----+----------+--------------------+        
| id|               conversations|label|langdetect|              source|
+---+----------------------------+-----+----------+--------------------+
|  0|        [{human, def exti...|     |        en|      code_exercises|
|  1|        [{human, See the ...|     |        en|                flan|
|  2|        [{human, This is ...|     |        en|                flan|
|  3|        [{human, If you d...|     |        en|                flan|
|  4|        [{human, In a Uni...|     |        en|                flan|
|  5|        [{human, Read the...|     |        en|                flan|
|  6|        [{human, You are ...|     |        en|          code_bagel|
|  7|        [{human, I want y...|     |        en|          Subjective|
|  8|        [{human, Given th...|     |        en|                flan|
|  9|[{human, 因果联系原则是法...|     |     zh-cn|          Subjective|
| 10|        [{human, Provide ...|     |        en|self-oss-instruct...|
| 11|        [{human, The univ...|     |        en|                flan|
| 12|        [{human, Q: I am ...|     |        en|                flan|
| 13|        [{human, What is ...|     |        en|      OpenHermes-2.5|
| 14|        [{human, In react...|     |        en|                flan|
| 15|        [{human, Write Py...|     |        en|      code_exercises|
| 16|        [{human, Find the...|     |        en|            MetaMath|
| 17|        [{human, Three of...|     |        en|            MetaMath|
| 18|        [{human, Chandra ...|     |        en|            MetaMath|
| 19|[{human, 用经济学知识分析...|     |     zh-cn|          Subjective|
+---+----------------------------+-----+----------+--------------------+

Cela charge le jeu de données en mode streaming, et le DataFrame de sortie comporte une partition par fichier de données du jeu de données afin de permettre un traitement distribué efficace.

Pour calculer le nombre de dialogues par langue, nous exécutons ce code qui utilise l’option columns et une opération groupBy(). L’option columns est utile pour ne charger que les données dont nous avons besoin, car PySpark n’active pas le predicate push-down avec l’API Data Source. Il existe également une option filters pour ne charger que les données dont les valeurs se situent dans une certaine plage.

>>> df_langdetect_only = (
...     spark.read.format("huggingface")
...     .option("config", "7M")
...     .option("columns", '["langdetect"]')
...     .load("BAAI/Infinity-Instruct")
... )
>>> df_langdetect_only.groupBy("langdetect").count().show()
+----------+-------+                                                            
|langdetect|  count|
+----------+-------+
|        en|6697793|
|     zh-cn| 751313|
+----------+-------+

Pour filtrer le jeu de données et ne conserver que les dialogues en chinois :

>>> df_chinese_only = (
...     spark.read.format("huggingface")
...     .option("config", "7M")
...     .option("filters", '[("langdetect", "=", "zh-cn")]')
...     .load("BAAI/Infinity-Instruct")
... )
>>> df_chinese_only.show()
+---+----------------------------+-----+----------+----------+                  
| id|               conversations|label|langdetect|    source|
+---+----------------------------+-----+----------+----------+
|  9|[{human, 因果联系原则是法...|     |     zh-cn|Subjective|
| 19|[{human, 用经济学知识分析...|     |     zh-cn|Subjective|
| 38| [{human, 某个考试共有A、...|     |     zh-cn|Subjective|
| 39|[{human, 撰写一篇关于斐波...|     |     zh-cn|Subjective|
| 57|[{human, 总结世界历史上的...|     |     zh-cn|Subjective|
| 61|[{human, 生成一则广告词。...|     |     zh-cn|Subjective|
| 66|[{human, 描述一个有效的团...|     |     zh-cn|Subjective|
| 94|[{human, 如果比利和蒂芙尼...|     |     zh-cn|Subjective|
|102|[{human, 生成一句英文名言...|     |     zh-cn|Subjective|
|106|[{human, 写一封感谢信,感...|     |     zh-cn|Subjective|
|118| [{human, 生成一个故事。}...|     |     zh-cn|Subjective|
|174|[{human, 高胆固醇水平的后...|     |     zh-cn|Subjective|
|180|[{human, 基于以下角色信息...|     |     zh-cn|Subjective|
|192|[{human, 请写一篇文章,概...|     |     zh-cn|Subjective|
|221|[{human, 以诗歌形式表达对...|     |     zh-cn|Subjective|
|228|[{human, 根据给定的指令,...|     |     zh-cn|Subjective|
|236|[{human, 打开一个新的生成...|     |     zh-cn|Subjective|
|260|[{human, 生成一个有关未来...|     |     zh-cn|Subjective|
|268|[{human, 如果有一定数量的...|     |     zh-cn|Subjective|
|273| [{human, 题目:小明有5个...|     |     zh-cn|Subjective|
+---+----------------------------+-----+----------+----------+

Il est également possible d’appliquer des filtres ou de supprimer des colonnes sur le DataFrame chargé, mais il est plus efficace de le faire lors du chargement, en particulier sur les jeux de données Parquet. En effet, Parquet contient des métadonnées au niveau du fichier et du groupe de lignes, ce qui permet d’ignorer des parties entières du jeu de données qui ne contiennent pas d’échantillons satisfaisant les critères. Les colonnes dans Parquet peuvent également être chargées indépendamment, ce qui permet d’ignorer les colonnes exclues et d’éviter de charger des données inutiles.

Options

Voici la liste des options disponibles que vous pouvez passer à read..option() :

  • config (string) : sélectionne un sous-ensemble/une configuration du jeu de données
  • split (string) : sélectionne un split du jeu de données (par défaut, « train »)
  • token (string) : votre jeton Hugging Face

Au lieu de spécifier une configuration ou une partition, vous pouvez sélectionner manuellement les fichiers à charger :

  • data_dir (string) : sélectionnez un répertoire
  • data_files (string) : sélectionnez un ou plusieurs fichiers, par exemple "data/*.parquet" ou '["part1.parquet", "par2.parquet"]'

Pour les jeux de données Parquet :

  • columns (string) : sélectionne un sous-ensemble de colonnes à charger, par exemple '["id"]'
  • filters (string) : pour ignorer les fichiers et les groupes de lignes qui ne correspondent pas à un critère, par exemple '[("source", "=", "code_exercises")]'. Les filtres sont transmis à pyarrow.parquet.ParquetDataset.

Toute autre option est transmise en tant qu'argument à [datasets.load_dataset] (https://huggingface.co/docs/datasets/en/package_reference/loading_methods#datasets.load_dataset)

Exécuter des requêtes SQL

Une fois votre PySpark Dataframe prêt, vous pouvez exécuter des requêtes SQL à l'aide de spark.sql :

>>> import pyspark_huggingface
>>> from pyspark.sql import SparkSession
>>> spark = SparkSession.builder.appName("demo").getOrCreate()
>>> df = (
...     spark.read.format("huggingface")
...     .option("config", "7M")
...     .option("columns", '["source"]')
...     .load("BAAI/Infinity-Instruct")
... )
>>> spark.sql("SELECT source, count(*) AS total FROM {df} GROUP BY source ORDER BY total DESC", df=df).show()
+--------------------+-------+
|              source|  total|
+--------------------+-------+
|                flan|2435840|
|          Subjective|1342427|
|      OpenHermes-2.5| 855478|
|            MetaMath| 690138|
|      code_exercises| 590958|
|Orca-math-word-pr...| 398168|
|          code_bagel| 386649|
|        MathInstruct| 329254|
|python-code-datas...|  88632|
|instructional_cod...|  82920|
|        CodeFeedback|  79513|
|self-oss-instruct...|  50467|
|Evol-Instruct-Cod...|  43354|
|CodeExercise-Pyth...|  27159|
|code_instructions...|  23130|
|  Code-Instruct-700k|  10860|
|Glaive-code-assis...|   9281|
|python_code_instr...|   2581|
|Python-Code-23k-S...|   2297|
+--------------------+-------+

Là encore, il n'est pas nécessaire de spécifier l'option columns, mais cela est utile pour éviter de charger des données inutiles et accélérer la requête.

Écrire

Vous pouvez écrire un Dataframe PySpark vers Hugging Face à l’aide de la source de données « huggingface ». Il téléverse les fichiers Parquet en parallèle de manière distribuée et n’engage les fichiers que lorsque tous ont été téléversés. Voici comment cela fonctionne :

>>> import pyspark_huggingface
>>> df.write.format("huggingface").save("username/dataset_name")

Voici comment nous pouvons utiliser cette fonction pour écrire la version filtrée du jeu de données BAAI/Infinity-Instruct sur Hugging Face.

Tout d'abord, vous devez créer un dépôt de jeu de données, par exemple username/Infinity-Instruct-Chinese-Only (vous pouvez le rendre privé si vous le souhaitez). Ensuite, assurez-vous d'être authentifié et que vous pouvez utiliser la source de données « huggingface », définissez le mode sur « overwrite » (ou « append » si vous souhaitez étendre un jeu de données existant), et poussez sur Hugging Face avec .save() :

>>> df_chinese_only.write.format("huggingface").mode("overwrite").save("username/Infinity-Instruct-Chinese-Only")

Mode

Deux modes sont disponibles lors de la publication d'un jeu de données sur Hugging Face :

  • “overwrite” : écraser le jeu de données s'il existe déjà
  • “append” : ajouter le jeu de données à un jeu de données existant

Options

Voici la liste des options disponibles que vous pouvez passer à write.option() :

  • token (string) : votre jeton Hugging Face

Les contributions sont les bienvenues pour ajouter d'autres options ici, en particulier subset et split.

Bacs de stockage

Il est courant de traiter des données brutes dans des Storage Buckets et d’y mener des expériences avant de publier des données prêtes pour l’IA dans des dépôts de jeux de données.

Accédez aux buckets de stockage de la même manière que pour les dépôts de Dataset, mais avec le préfixe buckets/ et les options data_dir ou data_files :

>>> df = spark.read.format("huggingface").option("data_dir", "data").load("buckets/username/my-bucket")
>>> # OR with a glob pattern
>>> # df = spark.read.format("huggingface").option("data_files", "data/*.parquet").load("buckets/username/my-bucket")
>>> df.write.format("huggingface").option("data_dir", "new-data").save("buckets/username/my-bucket")
Update on GitHub