{"paragraphs": [{"config": {"enabled": true, "editorHide": true}, "text": "%md\n\n", "results": {"code": "SUCCESS", "msg": [{"type": "HTML", "data": "
"}]}}, {"config": {"enabled": true, "editorHide": true}, "text": "%md\n# Travaux pratiques - Analyse de textes avec Spark NLP\n\nR\u00e9f\u00e9rences externes utiles :\n\n> - [John Snow Labs NLP](https://nlp.johnsnowlabs.com/) \n- [Documentation Spark](https://spark.apache.org/docs/latest/) \n- [Documentation API Spark en Scala](https://spark.apache.org/docs/latest/api/scala/org/apache/spark/index.html) \n- [Documentation langage Scala](http://docs.scala-lang.org) \n\n\n\nSpark NLP est une biblioth\u00e8que d\u00e9velopp\u00e9e pour Spark par la soci\u00e9t\u00e9 [John Snow Labs NLP](https://nlp.johnsnowlabs.com/) et permettant de r\u00e9aliser un assez grand nombre d\u2019op\u00e9rations de traitement automatique pour plusieurs langues. Cette biblioth\u00e8que est aujourd\u2019hui bien positionn\u00e9e par rapport \u00e0 ses concurrents, voir par exemple [ce comparatif](https://blog.dominodatalab.com/comparing-the-functionality-of-open-source-natural-language-processing-libraries/).\nL\u2019objectif de cette s\u00e9ance de TP est de faire une rapide introduction \u00e0 l\u2019utilisation de Spark NLP.\n\nEx\u00e9cutez les commandes suivantes pour cr\u00e9er l\u2019environnement n\u00e9cessaire. D\u2019abord, il faut cr\u00e9er le r\u00e9pertoire de travail et t\u00e9l\u00e9charger les donn\u00e9es :", "results": {"code": "SUCCESS", "msg": [{"type": "HTML", "data": "R\u00e9f\u00e9rences externes utiles :
\n\n\n \n\n\n
Spark NLP est une biblioth\u00e8que d\u00e9velopp\u00e9e pour Spark par la soci\u00e9t\u00e9 John Snow Labs NLP et permettant de r\u00e9aliser un assez grand nombre d\u2019op\u00e9rations de traitement automatique pour plusieurs langues. Cette biblioth\u00e8que est aujourd\u2019hui bien positionn\u00e9e par rapport \u00e0 ses concurrents, voir par exemple ce comparatif.\nL\u2019objectif de cette s\u00e9ance de TP est de faire une rapide introduction \u00e0 l\u2019utilisation de Spark NLP.
\n\nEx\u00e9cutez les commandes suivantes pour cr\u00e9er l\u2019environnement n\u00e9cessaire. D\u2019abord, il faut cr\u00e9er le r\u00e9pertoire de travail et t\u00e9l\u00e9charger les donn\u00e9es :
\n```scala\nimport sys.process._
\n\n// R\u00e9cup\u00e9ration des donn\u00e9es\n\"wget -nc http://cedric.cnam.fr/vertigo/Cours/RCP216/docs/texteEn.txt -P tpnlp\" !
\n\n\"wget -nc http://cedric.cnam.fr/vertigo/Cours/RCP216/docs/texteFr.txt -P tpnlp\" !\n```
\nJupyter
\n\nAfin d\u2019utiliser Spark NLP, nous devons importer le module. Comme il s\u2019agit d\u2019une d\u00e9pendance externe (c\u2019est-\u00e0-dire un module qui n\u2019est pas inclus de base dans Spark), nous devons r\u00e9cup\u00e9rer le .jar depuis les d\u00e9p\u00f4ts en ligne.\nDe fa\u00e7on analogue \u00e0 Vegas dans les TP pr\u00e9c\u00e9dents, nous pouvons importer SparkNLP dans Jupyter via la commande magique %AddDeps fournie par Apache Toree:
\nscala\n%AddDeps com.johnsnowlabs.nlp spark-nlp_2.11 2.7.5 --transitive\n
Cette commande permet de t\u00e9l\u00e9charger automatiquement et d\u2019utiliser par la suite dans Spark la biblioth\u00e8que SparkNLP ainsi que toutes ses d\u00e9pendances (option \u2013transitive). Nous utilisons la version 2.7.5 de la biblioth\u00e8que compil\u00e9e pour Scala 2.11 (qui est la version compatible avec Spark 2.4.x, que nous utilisons pour les TP).
\n\nPour d\u2019autres versions de Spark veuillez regarder la documentation de Spark NLP.
\nSi vous souhaitez utiliser Spark NLP hors connexion, l\u2019alternative est de cr\u00e9er un .jar
local contenant cette biblioth\u00e8que et ensuite d\u2019utiliser le .jar
. Pour obtenir le .jar
il faut entrer les commandes suivantes (\u00e9tant positionn\u00e9 dans le r\u00e9pertoire tpnlp
) si une version 2.4 de Spark est employ\u00e9e :
bash\ngit clone https://github.com/JohnSnowLabs/spark-nlp\ncd spark-nlp\nsbt -Dis_spark24=true assembly\n
Une fois cr\u00e9\u00e9 le .jar
, il faut lancer spark-shell
avec
bash\nspark-shell --jars spark-nlp/target/scala-2.11/spark-nlp-spark24-assembly-3.3.0.jar\n
Ou bien dans Jupyter :
\ntext\n%AddDeps file:/chemin/vers/spark-nlp/target/scala-2.11/spark-nlp-spark24-assembly-3.3.0.jar\n
Nous appliquerons d\u2019abord certains traitements au fichier en anglais texteEn.txt
(le contenu textuel d\u2019une page Wikipedia).\nDans Spark, il faut donc lire ce fichier dans un DataFrame.
scala\n// enlever tpnlp/ si spark-shell ou Zeppelin sont lanc\u00e9s dans le r\u00e9pertoire tpnlp\nval texteEnTout = spark.read.textFile(\"tpnlp/texteEn.txt\").toDF(\"text\")\n
Une fois ce travail effectu\u00e9, nous allons \u00e9liminer les lignes de texte qui sont vides :
\n```scala\nimport org.apache.spark.sql.functions.length
\n\nval texteEn = texteEnTout.where(length($\"text\") > 0)\n```
\nLe fichier texte a \u00e9t\u00e9 lu dans un DataFrame qui poss\u00e8de une seule colonne appel\u00e9e text
(les traitements pr\u00e9d\u00e9finis que nous regarderons dans la suite attendent une colonne ayant ce nom comme colonne d\u2019entr\u00e9e). Chaque paragraphe (termin\u00e9 par un saut de ligne) est lu dans un item du DataFrame. Avec .where(length(\\$\"value\") > 0)
nous \u00e9liminons les items vides (correspondant aux paragraphes vides entre deux sauts de lignes successifs).
\u00c0 l\u2019aide de Spark, d\u00e9terminer combien le fichier textEn.txt
comporte de lignes non-vides.
Spark NLP propose aussi bien des traitements s\u00e9par\u00e9s (les annotateurs) que des regroupements pr\u00e9d\u00e9finis de plusieurs traitements (les pipelines). La documentation disponible pourrait sans doute \u00eatre plus d\u00e9taill\u00e9e.
\n\nCommen\u00e7ons par importer les modules utiles :
\nscala\nimport org.apache.spark.ml.Pipeline\nimport com.johnsnowlabs.nlp.pretrained.PretrainedPipeline\nimport com.johnsnowlabs.nlp.Finisher\n
Nous appliquons d\u2019abord des traitements pr\u00e9d\u00e9finis dans un pipeline Spark NLP, explain_document_ml
: d\u00e9coupage en phrases, d\u00e9coupage en tokens (mots), lemmatisation, \u00e9tiquetage morpho-syntaxique. Les r\u00e9sultats sont ensuite trait\u00e9s par un annotateur (en terminologie Spark NLP) appel\u00e9 finisher qui rend les r\u00e9sultats plus facilement lisibles par un humain. Pour appliquer le pipeline Spark NLP et ensuite le finisher aux r\u00e9sultats, nous int\u00e9grons les deux dans un pipeline Spark :
scala\n// Charge le mod\u00e8le pr\u00e9-entra\u00een\u00e9 \"explain_document_ml\")\nval explainPipelineModel = PretrainedPipeline(\"explain_document_ml\").model\n// Le Finisher permet d'annoter les r\u00e9sultats de fa\u00e7on lisible par un humain\nval finisherExplainEn = new Finisher().setInputCols(\"token\", \"lemmas\", \"pos\")\n// Cr\u00e9ation d'un Pipeline Spark qui applique le mod\u00e8le puis l'annotateur\nval pipelineExplainEn = new Pipeline().setStages(Array(explainPipelineModel,finisherExplainEn))\n
Le traitement est ensuite appliqu\u00e9 aux donn\u00e9es et les r\u00e9sultats sont affich\u00e9s, vous pouvez les examiner :
\nscala\nval modelExplainEn = pipelineExplainEn.fit(texteEn)\nval annoteTexteEn = modelExplainEn.transform(texteEn).cache()\nannoteTexteEn.show()\n
Quelles sont les diff\u00e9rentes colonnes accessibles dans le DataFrame annoteTexteEn ? \u00c0 quoi correspondent-elles ?
\n\nAfficher les 10 premi\u00e8res lignes pour:
\n\nSpark NLP propose \u00e9galement d\u2019autres pipelines pr\u00e9d\u00e9finis, voir la documentation, dont un qui a pour objectif d\u2019associer \u00e0 chaque phrase une \u00e9tiquette indiquant si les sentiments transmis par la phrase sont positifs ou n\u00e9gatifs. Nous faisons ci-dessous un essai de ce pipeline sur des donn\u00e9es bien plus courtes, \u00e9crites directement dans l\u2019interface de Spark et que vous pouvez modifier \u00e0 loisir :
\nscala\nval sentimentPipelineModel = PretrainedPipeline(\"analyze_sentiment\").model\nval finisherSentiment = new Finisher().setInputCols(\"document\",\"sentiment\")\nval pipelineSentiment = new Pipeline().setStages(Array(sentimentPipelineModel,finisherSentiment))\n
Nous pouvons l\u2019appliquer sur un DataFrame contenant des donn\u00e9es de test, pour exp\u00e9rimenter :
\nscala\nval testSentimentData = Seq(\"The movie is great. But the cinema is quite dirty.\").toDF(\"text\")\nval modelSentiment = pipelineSentiment.fit(testSentimentData)\nval sentimentTestSentimentData = modelSentiment.transform(testSentimentData)\nsentimentTestSentimentData.show(false)\n
\u00c0 quoi correspondent les valeurs de la colonne finished_sentiment du DataFrame sentimentTestSentimentData ?
\n\nModifier la phrase de test de sorte \u00e0 n\u2019avoir que des sentiments positifs.
\nEnfin, examinons maintenant un autre pipeline, explain_document_md
, appliqu\u00e9e au document en fran\u00e7ais texteFr.txt
(l\u2019\u00e9quivalent plus court en fran\u00e7ais de la page Wikipedia en anglais consid\u00e9r\u00e9e plus haut). Ce pipeline inclut \u00e9galement l\u2019identification d\u2019entit\u00e9s nomm\u00e9es (Named Entity Recognition, NER, colonne ner
) et leur extraction (colonne entities
).
Comme pr\u00e9c\u00e9demment, commen\u00e7ons par charger le texte fran\u00e7ais dans un DataFrame en \u00e9liminant les lignes vides.
\nscala\nval texteFr = spark.read.textFile(\"tpnlp/texteFr.txt\").toDF(\"text\").where(length($\"text\") > 0)\nprintln(texteFr.count)\n
Spark NPL propose un mod\u00e8le d\u2019explication de documents pour la langue fran\u00e7aise, que nous pouvons charger de fa\u00e7on analogue au PretrainedPipeline utilis\u00e9 plus haut :
\n```scala\nval explainPipelineModel = PretrainedPipeline(\"explaindocumentmd\",\"fr\").model\nval finisherExplainFr = new Finisher().setInputCols(\"token\", \"lemma\", \"pos\", \"ner\", \"entities\")\nval pipelineExplainFr = new Pipeline().setStages(Array(explainPipelineModel,finisherExplainFr))
\n\nval modelExplainFr = pipelineExplainFr.fit(texteFr)\nval annoteTexteFr = modelExplainFr.transform(texteFr)
\n\nannoteTexteFr.show(5, false)\n```
\nVous pouvez examiner le r\u00e9sultat des op\u00e9rations de lemmatisation (qui ont pour objectif de remplacer chaque mot par sa forme \u00ab canonique \u00bb) et l\u2019identification des entit\u00e9s nomm\u00e9es.
\nExaminez les entit\u00e9s nomm\u00e9es de la premi\u00e8re phrase du document (c\u2019est-\u00e0-dire la premi\u00e8re ligne du DataFrame). Quelles sont-elles ? De quelles types sont-elles ?
\nscala\n// Afficher la premi\u00e8re ligne\ntexteFr.show(1, false)\n// \u00c0 compl\u00e9ter\n// ...\n
Pour terminer, notons que Spark NLP permet d\u2019afficher directement en Scala la liste des ressources disponibles, et notamment des mod\u00e8les d\u2019analyse de texte qui sont disponibles.\nPar exemple, pour afficher tous les Pipelines disponibles pour l\u2019analyse de texte en fran\u00e7ais (lang=fr) :
\n```scala\nimport com.johnsnowlabs.nlp.pretrained.ResourceDownloader
\n\nResourceDownloader.showPublicPipelines(lang=\"fr\")\n```
\nNotez que certains Pipelines ne sont disponibles qu\u2019\u00e0 partir de la version 3.0 de Spark NLP.
\n\nNous nous servirons de Spark NLP dans une s\u00e9ance de TP suivante afin produire des repr\u00e9sentations vectorielles de textes \u00e0 classer.
\n