1
0
mirror of https://github.com/QData/TextAttack.git synced 2021-10-13 00:05:06 +03:00

Merge branch 'master' of github.com:QData/TextAttack into gpu-alloc

This commit is contained in:
Jack Morris
2020-08-18 12:25:28 -04:00
26 changed files with 370 additions and 105 deletions

View File

@@ -22,7 +22,7 @@ copyright = "2020, UVA QData Lab"
author = "UVA QData Lab"
# The full version, including alpha/beta/rc tags
release = "0.2.4"
release = "0.2.7"
# Set master doc to `index.rst`.
master_doc = "index"

View File

@@ -14,16 +14,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<table class=\"ta-notebook-buttons\" align=\"left\">\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://colab.research.google.com/drive/1Q7EzZ_6XDzAokTtlYSAGgdQ8NQnhC105?usp=sharing\">\n",
" <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
" </td>\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://github.com/QData/TextAttack/blob/master/docs/datasets_models/Example_0_tensorflow.ipynb\">\n",
" <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
" </td>\n",
"</table>"
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing)\n",
"\n",
"[![View Source on GitHub](https://img.shields.io/badge/github-view%20source-black.svg)](https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb)"
]
},
{
@@ -437,7 +430,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.6.7"
}
},
"nbformat": 4,

View File

@@ -15,16 +15,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<table class=\"ta-notebook-buttons\" align=\"left\">\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://colab.research.google.com/drive/1cYmJfgyJdAH5Plx-7e2M_99qg1R9J6ds?usp=sharing\">\n",
" <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
" </td>\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://github.com/QData/TextAttack/blob/master/docs/datasets_models/Example_1_sklearn.ipynb\">\n",
" <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
" </td>\n",
"</table>"
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing)\n",
"\n",
"[![View Source on GitHub](https://img.shields.io/badge/github-view%20source-black.svg)](https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb)"
]
},
{
@@ -298,7 +291,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.6.7"
}
},
"nbformat": 4,

View File

@@ -25,16 +25,9 @@
"id": "AyPMGcz0qLfK"
},
"source": [
"<table class=\"ta-notebook-buttons\" align=\"left\">\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://colab.research.google.com/drive/1Inpcc6AymahreBRkOpyQJd3sqOJagtNR?usp=sharing\">\n",
" <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
" </td>\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://github.com/QData/TextAttack/blob/master/docs/datasets_models/Example_2_allennlp.ipynb\">\n",
" <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
" </td>\n",
"</table>"
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing)\n",
"\n",
"[![View Source on GitHub](https://img.shields.io/badge/github-view%20source-black.svg)](https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb)"
]
},
{
@@ -321,7 +314,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.6.7"
}
},
"nbformat": 4,

View File

@@ -13,16 +13,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<table class=\"ta-notebook-buttons\" align=\"left\">\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing\">\n",
" <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
" </td>\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://github.com/QData/TextAttack/blob/master/docs/examples/0_End_to_End.ipynb\">\n",
" <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
" </td>\n",
"</table>"
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing)\n",
"\n",
"[![View Source on GitHub](https://img.shields.io/badge/github-view%20source-black.svg)](https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb)"
]
},
{
@@ -9948,7 +9941,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.6.7"
}
},
"nbformat": 4,

View File

@@ -11,16 +11,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<table class=\"ta-notebook-buttons\" align=\"left\">\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://colab.research.google.com/drive/1ce6kt-a0D_rFy_Ht4Fa7BXazLmLoKKzY?usp=sharing\">\n",
" <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
" </td>\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb\">\n",
" <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
" </td>\n",
"</table>"
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing)\n",
"\n",
"[![View Source on GitHub](https://img.shields.io/badge/github-view%20source-black.svg)](https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb)"
]
},
{
@@ -577,7 +570,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.6.7"
}
},
"nbformat": 4,

View File

@@ -13,16 +13,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<table class=\"ta-notebook-buttons\" align=\"left\">\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://colab.research.google.com/drive/1kJuYTSYgEgZK7AJMwp-oIPqWCp0gg9kB?usp=sharing\">\n",
" <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
" </td>\n",
" <td>\n",
" <a target=\"_blank\" href=\"https://github.com/QData/TextAttack/blob/master/docs/examples/2_Constraints.ipynb\">\n",
" <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
" </td>\n",
"</table>"
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1cBRUj2l0m8o81vJGGFgO-o_zDLj24M5Y?usp=sharing)\n",
"\n",
"[![View Source on GitHub](https://img.shields.io/badge/github-view%20source-black.svg)](https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb)"
]
},
{
@@ -768,7 +761,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.6.7"
}
},
"nbformat": 4,

View File

@@ -64,6 +64,13 @@ TextAttack has some other features that make it a pleasure to use:
Tutorial 1: Transformations <examples/1_Introduction_and_Transformations.ipynb>
Tutorial 2: Constraints <examples/2_Constraints.ipynb>
.. toctree::
:maxdepth: 1
:hidden:
:caption: Multilingual Examples
CamemBERT & French WordNet <multilingual/Example_1_CamemBERT.ipynb>
.. toctree::
:maxdepth: 3
:hidden:

View File

@@ -0,0 +1,222 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Multi-language attacks\n",
"\n",
"TextAttack's four-component framework makes it trivial to run attacks in other languages. In this tutorial, we:\n",
"\n",
"- Create a model wrapper around Transformers [pipelines](https://huggingface.co/transformers/main_classes/pipelines.html) \n",
"- Initialize a pre-trained [CamemBERT](https://camembert-model.fr/) model for sentiment classification\n",
"- Load the AlloCiné movie review sentiment classification dataset (from [`nlp`](https://github.com/huggingface/nlp/))\n",
"- Load the `pwws` recipe, but use French synonyms from multilingual WordNet (instead of English synonyms)\n",
"- Run an adversarial attack on a French language model\n",
"\n",
"Voilà!"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from textattack.attack_recipes import PWWSRen2019\n",
"from textattack.datasets import HuggingFaceNlpDataset\n",
"from textattack.models.wrappers import ModelWrapper\n",
"from transformers import AutoTokenizer, TFAutoModelForSequenceClassification, pipeline\n",
"\n",
"import numpy as np\n",
"\n",
"# Quiet TensorFlow.\n",
"import os\n",
"if \"TF_CPP_MIN_LOG_LEVEL\" not in os.environ:\n",
" os.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\n",
"\n",
"\n",
"class HuggingFaceSentimentAnalysisPipelineWrapper(ModelWrapper):\n",
" \"\"\" Transformers sentiment analysis pipeline returns a list of responses\n",
" like \n",
" \n",
" [{'label': 'POSITIVE', 'score': 0.7817379832267761}]\n",
" \n",
" We need to convert that to a format TextAttack understands, like\n",
" \n",
" [[0.218262017, 0.7817379832267761]\n",
" \"\"\"\n",
" def __init__(self, pipeline):\n",
" self.pipeline = pipeline\n",
" def __call__(self, text_inputs):\n",
" raw_outputs = self.pipeline(text_inputs)\n",
" outputs = []\n",
" for output in raw_outputs:\n",
" score = output['score']\n",
" if output['label'] == 'POSITIVE':\n",
" outputs.append([1-score, score])\n",
" else:\n",
" outputs.append([score, 1-score])\n",
" return np.array(outputs)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"All model checkpoint weights were used when initializing TFCamembertForSequenceClassification.\n",
"\n",
"All the weights of TFCamembertForSequenceClassification were initialized from the model checkpoint at tblard/tf-allocine.\n",
"If your task is similar to the task the model of the ckeckpoint was trained on, you can already use TFCamembertForSequenceClassification for predictions without further training.\n",
"\u001b[34;1mtextattack\u001b[0m: Unknown if model of class <class '__main__.HuggingFaceSentimentAnalysisPipelineWrapper'> compatible with goal function <class 'textattack.goal_functions.classification.untargeted_classification.UntargetedClassification'>.\n",
"\u001b[34;1mtextattack\u001b[0m: Loading \u001b[94mnlp\u001b[0m dataset \u001b[94mallocine\u001b[0m, split \u001b[94mtest\u001b[0m.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"xxxxxxxxxxxxxxxxxxxx Result 1 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (100%)\u001b[0m --> \u001b[91mNegative (53%)\u001b[0m\n",
"\n",
"\u001b[92mMagnifique\u001b[0m épopée, une \u001b[92mbelle\u001b[0m \u001b[92mhistoire\u001b[0m, touchante avec des acteurs \u001b[92mqui\u001b[0m interprètent \u001b[92mtrès\u001b[0m \u001b[92mbien\u001b[0m leur rôles (Mel Gibson, Heath Ledger, Jason Isaacs...), le genre \u001b[92mde\u001b[0m \u001b[92mfilm\u001b[0m \u001b[92mqui\u001b[0m \u001b[92mse\u001b[0m savoure \u001b[92men\u001b[0m \u001b[92mfamille\u001b[0m! :)\n",
"\n",
"\u001b[91mbonnard\u001b[0m épopée, une \u001b[91mbeau\u001b[0m \u001b[91mbobard\u001b[0m, touchante avec des acteurs \u001b[91mlequel\u001b[0m interprètent \u001b[91mmême\u001b[0m \u001b[91macceptablement\u001b[0m leur rôles (Mel Gibson, Heath Ledger, Jason Isaacs...), le genre \u001b[91mgale\u001b[0m \u001b[91mpellicule\u001b[0m \u001b[91mOMS\u001b[0m \u001b[91mConcepteur\u001b[0m savoure \u001b[91mun\u001b[0m \u001b[91msyndicat\u001b[0m! :)\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 2 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[91mNegative (94%)\u001b[0m --> \u001b[92mPositive (91%)\u001b[0m\n",
"\n",
"Je n'ai pas aimé mais pourtant je lui mets \u001b[91m2\u001b[0m étoiles car l'expérience est louable. Rien de conventionnel ici. Une visite E.T. mais jonchée d'idées /- originales. Le soucis, tout ceci avait-il vraiment sa place dans un film de S.F. tirant sur l'horreur ? Voici un film qui, à l'inverse de tant d'autres qui y ont droit, mériterait peut-être un remake.\n",
"\n",
"Je n'ai pas aimé mais pourtant je lui mets \u001b[92m4\u001b[0m étoiles car l'expérience est louable. Rien de conventionnel ici. Une visite E.T. mais jonchée d'idées /- originales. Le soucis, tout ceci avait-il vraiment sa place dans un film de S.F. tirant sur l'horreur ? Voici un film qui, à l'inverse de tant d'autres qui y ont droit, mériterait peut-être un remake.\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 3 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (85%)\u001b[0m --> \u001b[91mNegative (91%)\u001b[0m\n",
"\n",
"Un \u001b[92mdessin\u001b[0m animé qui brille par sa féerie et ses chansons.\n",
"\n",
"Un \u001b[91mbrouillon\u001b[0m animé qui brille par sa féerie et ses chansons.\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 4 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[91mNegative (100%)\u001b[0m --> \u001b[92mPositive (80%)\u001b[0m\n",
"\n",
"\u001b[91mSi\u001b[0m c'est là le renouveau du cinéma français, c'est tout \u001b[91mde\u001b[0m même foutrement chiant. \u001b[91mSi\u001b[0m l'objet est \u001b[91mtrès\u001b[0m stylisé et la tension palpable, le film paraît \u001b[91mplutôt\u001b[0m \u001b[91mcreux\u001b[0m.\n",
"\n",
"\u001b[92maussi\u001b[0m c'est là le renouveau du cinéma français, c'est tout \u001b[92mabolir\u001b[0m même foutrement chiant. \u001b[92mtellement\u001b[0m l'objet est \u001b[92mprodigieusement\u001b[0m stylisé et la tension palpable, le film paraît \u001b[92mpeu\u001b[0m \u001b[92mtrou\u001b[0m.\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 5 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[91mNegative (100%)\u001b[0m --> \u001b[92mPositive (51%)\u001b[0m\n",
"\n",
"Et \u001b[91mpourtant\u001b[0m on s\u001b[91men\u001b[0m Doutait !\u001b[91mSecond\u001b[0m \u001b[91mvolet\u001b[0m \u001b[91mtrès\u001b[0m \u001b[91mmauvais\u001b[0m, sans \u001b[91mfraîcheur\u001b[0m et particulièrement lourdingue. Quel \u001b[91mdommage\u001b[0m.\n",
"\n",
"Et \u001b[92mfin\u001b[0m on s\u001b[92mpostérieurement\u001b[0m Doutait !\u001b[92mmoment\u001b[0m \u001b[92mchapitre\u001b[0m \u001b[92mincroyablement\u001b[0m \u001b[92mdifficile\u001b[0m, sans \u001b[92mimpudence\u001b[0m et particulièrement lourdingue. Quel \u001b[92mprix\u001b[0m.\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 6 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (100%)\u001b[0m --> \u001b[91mNegative (50%)\u001b[0m\n",
"\n",
"Vous reprendrez bien un peu d'été ? Ce film je le voyais comme un mélange de Rohmer et de Rozier, un film de vacances, j'adore ça, un truc beau et pur qui dit des choses sur la vie, l'amour, les filles, les vacances. Un film qui se regarde en sirotant une boisson fraîche en écoutant les grillons ! Sauf qu'en fait \u001b[92mnon\u001b[0m ! On a un film foutraque au \u001b[92mpossible\u001b[0m qui reprend les codes justement de Rohmer voir Godard, enfin la Nouvelle Vague en général dans sa première partie (jusqu'à même finir sur une partie qui ressemblerait à du Kusturica), mais en beaucoup plus léger et décalé. Le film n'en a rien à foutre de rien, il ose tout, n'a peur de rien et ça c'est \u001b[92mbon\u001b[0m. C'est sans doute le film le plus \u001b[92mdrôle\u001b[0m de 2013, mais tout \u001b[92msimplement\u001b[0m l'un des meilleurs tout \u001b[92mcourt\u001b[0m. Le film qui nous sort des dialogues qui pourraient sortir d'un mauvais Godard (oxymore) sur un ton what the fuckesque… raconte des anecdotes débiles au souhait face caméra… et pourtant, il y a quelque chose dans ce film survolté. Il y a du beau. Ces scènes dans la neige, c'est tendre, c'est beau, ça tranche avec le reste et ça donne du coeur à l'amourette, ça aide à le faire paraître comme une évidence. Et puis on a cette scène que je trouve sublime qui m'a profondément émue, cette scène où le docteur Placenta devient tout à coup sérieux et parle de cette date où chaque année il repense à cette fille et au fait qu'une année de plus le sépare d'elle. C'est horrible comme concept et pourtant tellement vrai et sincère. C'est vraiment \u001b[92mtroublant\u001b[0m. Et encore une fois la scène d'avant est très drôle et là, un petit moment de douceur avant de repartir sur le train effréné ! Et il y a ces fesses… Et le plus beau c'est qu'à la fin Vimala Pons a un petit air d'Anna Karina ! Film fout, étonnant, percutant, drôle, beau, triste ! C'est foutrement cool !\n",
"\n",
"Vous reprendrez bien un peu d'été ? Ce film je le voyais comme un mélange de Rohmer et de Rozier, un film de vacances, j'adore ça, un truc beau et pur qui dit des choses sur la vie, l'amour, les filles, les vacances. Un film qui se regarde en sirotant une boisson fraîche en écoutant les grillons ! Sauf qu'en fait \u001b[91mniet\u001b[0m ! On a un film foutraque au \u001b[91mexécutable\u001b[0m qui reprend les codes justement de Rohmer voir Godard, enfin la Nouvelle Vague en général dans sa première partie (jusqu'à même finir sur une partie qui ressemblerait à du Kusturica), mais en beaucoup plus léger et décalé. Le film n'en a rien à foutre de rien, il ose tout, n'a peur de rien et ça c'est \u001b[91mlisse\u001b[0m. C'est sans doute le film le plus \u001b[91mridicule\u001b[0m de 2013, mais tout \u001b[91msauf\u001b[0m l'un des meilleurs tout \u001b[91minsuffisant\u001b[0m. Le film qui nous sort des dialogues qui pourraient sortir d'un mauvais Godard (oxymore) sur un ton what the fuckesque… raconte des anecdotes débiles au souhait face caméra… et pourtant, il y a quelque chose dans ce film survolté. Il y a du beau. Ces scènes dans la neige, c'est tendre, c'est beau, ça tranche avec le reste et ça donne du coeur à l'amourette, ça aide à le faire paraître comme une évidence. Et puis on a cette scène que je trouve sublime qui m'a profondément émue, cette scène où le docteur Placenta devient tout à coup sérieux et parle de cette date où chaque année il repense à cette fille et au fait qu'une année de plus le sépare d'elle. C'est horrible comme concept et pourtant tellement vrai et sincère. C'est vraiment \u001b[91mennuyeux\u001b[0m. Et encore une fois la scène d'avant est très drôle et là, un petit moment de douceur avant de repartir sur le train effréné ! Et il y a ces fesses… Et le plus beau c'est qu'à la fin Vimala Pons a un petit air d'Anna Karina ! Film fout, étonnant, percutant, drôle, beau, triste ! C'est foutrement cool !\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 7 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (55%)\u001b[0m --> \u001b[91mNegative (88%)\u001b[0m\n",
"\n",
"Bon c'est \u001b[92mpas\u001b[0m un grand film mais on passe un bon moment avec ses ado à la recherche de l'orgasme. Y'a que les Allemands pour faire des films aussi barge ! :-)\n",
"\n",
"Bon c'est \u001b[91mniet\u001b[0m un grand film mais on passe un bon moment avec ses ado à la recherche de l'orgasme. Y'a que les Allemands pour faire des films aussi barge ! :-)\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 8 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (100%)\u001b[0m --> \u001b[91mNegative (97%)\u001b[0m\n",
"\n",
"\u001b[92mTerrible\u001b[0m histoire que ces êtres sans amour, ces êtres lisses et frustres qui passent à côté de leur vie. Quelle leçon Monsieur Brizé! Vous avez tout dit, tout filmé jusqu'au moindre détail. \u001b[92mtout\u001b[0m est beau et terrifiant jusqu'à la scène finale qui nous liquéfie, un Vincent Lindon regardant la vie fixement sans oser la toucher ni la prendre dans ses bras, une Hélène Vincent qui attend, qui attend... Mon Dieu Monsieur Brizé, continuez....\n",
"\n",
"\u001b[91mméprisable\u001b[0m histoire que ces êtres sans amour, ces êtres lisses et frustres qui passent à côté de leur vie. Quelle leçon Monsieur Brizé! Vous avez tout dit, tout filmé jusqu'au moindre détail. \u001b[91mrien\u001b[0m est beau et terrifiant jusqu'à la scène finale qui nous liquéfie, un Vincent Lindon regardant la vie fixement sans oser la toucher ni la prendre dans ses bras, une Hélène Vincent qui attend, qui attend... Mon Dieu Monsieur Brizé, continuez....\n",
"\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"xxxxxxxxxxxxxxxxxxxx Result 9 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (100%)\u001b[0m --> \u001b[91mNegative (54%)\u001b[0m\n",
"\n",
"Un \u001b[92mtrès\u001b[0m joli \u001b[92mfilm\u001b[0m, qui ressemble à un téléfilm mais qui a le mérite d'être émouvant et proche de ses personnages. Magimel est \u001b[92mvraiment\u001b[0m très \u001b[92mbon\u001b[0m et l'histoire est touchante\n",
"\n",
"Un \u001b[91mplus\u001b[0m joli \u001b[91mfeuil\u001b[0m, qui ressemble à un téléfilm mais qui a le mérite d'être émouvant et proche de ses personnages. Magimel est \u001b[91mabsolument\u001b[0m très \u001b[91mlisse\u001b[0m et l'histoire est touchante\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 10 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[91mNegative (100%)\u001b[0m --> \u001b[92mPositive (51%)\u001b[0m\n",
"\n",
"Mais comment certaines personnes ont pus lui \u001b[91mmettre\u001b[0m 5/5 et \u001b[91mdonc\u001b[0m dire indirectement \u001b[91mque\u001b[0m c'est un chef-d'œuvre ??? Et comment a-t-il fait pour sortir au cinéma et non en DTV ??? C'est pas un film que l'on regarde dans une salle obscur ça, pour moi ça ressemble plus à un téléfilm que l'on visionne un dimanche pluvieux \u001b[91mpour\u001b[0m que les enfants arrête de nous casser les pieds ! \u001b[91mEt\u001b[0m puis, le \u001b[91mscénario\u001b[0m avec le chien que devient le meilleur ami du gosse, c'est du vu et revu (un cliché) ! L'acteur principal est quant à lui aussi agaçant que son personnage ! Les suites ont l'air \u001b[91maussi\u001b[0m mauvaises que Buddy Star des Paniers étant donné que l'histoire est quasiment la même (pour moi ça c'est pas des suites, c'est \u001b[91mplutôt\u001b[0m une succession \u001b[91mde\u001b[0m petits reboots inutiles). \u001b[91mReste\u001b[0m regardable pour les moins de 10 ans (et encore, même moi à 6 ans, je n'aurais pas aimé).\n",
"\n",
"Mais comment certaines personnes ont pus lui \u001b[92mformuler\u001b[0m 5/5 et \u001b[92md'où\u001b[0m dire indirectement \u001b[92mcar\u001b[0m c'est un chef-d'œuvre ??? Et comment a-t-il fait pour sortir au cinéma et non en DTV ??? C'est pas un film que l'on regarde dans une salle obscur ça, pour moi ça ressemble plus à un téléfilm que l'on visionne un dimanche pluvieux \u001b[92mat\u001b[0m que les enfants arrête de nous casser les pieds ! \u001b[92mpoids\u001b[0m puis, le \u001b[92mfigure\u001b[0m avec le chien que devient le meilleur ami du gosse, c'est du vu et revu (un cliché) ! L'acteur principal est quant à lui aussi agaçant que son personnage ! Les suites ont l'air \u001b[92mmaintenant\u001b[0m mauvaises que Buddy Star des Paniers étant donné que l'histoire est quasiment la même (pour moi ça c'est pas des suites, c'est \u001b[92mpeu\u001b[0m une succession \u001b[92mdu\u001b[0m petits reboots inutiles). \u001b[92mrelique\u001b[0m regardable pour les moins de 10 ans (et encore, même moi à 6 ans, je n'aurais pas aimé).\n",
"\n",
"xxxxxxxxxxxxxxxxxxxx Result 11 xxxxxxxxxxxxxxxxxxxx\n",
"\u001b[92mPositive (100%)\u001b[0m --> \u001b[91mNegative (53%)\u001b[0m\n",
"\n",
"LE film de mon enfance , il a un peu vieilli maintenant , mais l'ours reste toujours impressionnant, il est bien réel contrairement au film 'the Revenant\" . Ce n'est surement pas un chef-d'œuvre mais je le trouve bien réalise , captivant , beaux et accompagné d'une superbe musique. Le gros points noir c'est la facilité qu'ils ont a créer des peaux , des pièges , et rester longtemps sans manger....mais on oublie assez vite ces erreurs grâce a un casting sympathique et aux décors naturels. Un \u001b[92mvieux\u001b[0m film mais qui reste \u001b[92mtoujours\u001b[0m un \u001b[92mbon\u001b[0m \u001b[92mfilm\u001b[0m.\n",
"\n",
"LE film de mon enfance , il a un peu vieilli maintenant , mais l'ours reste toujours impressionnant, il est bien réel contrairement au film 'the Revenant\" . Ce n'est surement pas un chef-d'œuvre mais je le trouve bien réalise , captivant , beaux et accompagné d'une superbe musique. Le gros points noir c'est la facilité qu'ils ont a créer des peaux , des pièges , et rester longtemps sans manger....mais on oublie assez vite ces erreurs grâce a un casting sympathique et aux décors naturels. Un \u001b[91mbancal\u001b[0m film mais qui reste \u001b[91mdéfinitivement\u001b[0m un \u001b[91mpassable\u001b[0m \u001b[91mpellicule\u001b[0m.\n",
"\n"
]
}
],
"source": [
"# Create the model: a French sentiment analysis model.\n",
"# see https://github.com/TheophileBlard/french-sentiment-analysis-with-bert\n",
"model = TFAutoModelForSequenceClassification.from_pretrained(\"tblard/tf-allocine\")\n",
"tokenizer = AutoTokenizer.from_pretrained(\"tblard/tf-allocine\")\n",
"pipeline = pipeline('sentiment-analysis', model=model, tokenizer=tokenizer)\n",
"\n",
"model_wrapper = HuggingFaceSentimentAnalysisPipelineWrapper(pipeline)\n",
"\n",
"# Create the recipe: PWWS uses a WordNet transformation.\n",
"recipe = PWWSRen2019.build(model_wrapper)\n",
"# WordNet defaults to english. Set the default language to French ('fra')\n",
"recipe.transformation.language = 'fra'\n",
"#\n",
"# See \n",
"# \"Building a free French wordnet from multilingual resources\", \n",
"# E. L. R. A. (ELRA) (ed.), \n",
"# Proceedings of the Sixth International Language Resources and Evaluation (LREC08).\n",
"\n",
"recipe.transformation.language = 'fra'\n",
"\n",
"dataset = HuggingFaceNlpDataset('allocine', split='test')\n",
"for idx, result in enumerate(recipe.attack_dataset(dataset, indices=range(11))):\n",
" print(('x' * 20), f'Result {idx+1}', ('x' * 20))\n",
" print(result.__str__(color_method='ansi'))\n",
" print()\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "torch",
"language": "python",
"name": "build_central"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,61 @@
from textattack.attack_recipes import PWWSRen2019
from textattack.datasets import HuggingFaceNlpDataset
from textattack.models.wrappers import ModelWrapper
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification, pipeline
import numpy as np
# Quiet TensorFlow.
import os
if "TF_CPP_MIN_LOG_LEVEL" not in os.environ:
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
class HuggingFaceSentimentAnalysisPipelineWrapper(ModelWrapper):
""" Transformers sentiment analysis pipeline returns a list of responses,
like
[{'label': 'POSITIVE', 'score': 0.7817379832267761}]
We need to convert that to a format TextAttack understands, like
[[0.218262017, 0.7817379832267761]
"""
def __init__(self, pipeline):
self.pipeline = pipeline
def __call__(self, text_inputs):
raw_outputs = self.pipeline(text_inputs)
outputs = []
for output in raw_outputs:
score = output["score"]
if output["label"] == "POSITIVE":
outputs.append([1 - score, score])
else:
outputs.append([score, 1 - score])
return np.array(outputs)
# Create the model: a French sentiment analysis model.
# see https://github.com/TheophileBlard/french-sentiment-analysis-with-bert
model = TFAutoModelForSequenceClassification.from_pretrained("tblard/tf-allocine")
tokenizer = AutoTokenizer.from_pretrained("tblard/tf-allocine")
pipeline = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
model_wrapper = HuggingFaceSentimentAnalysisPipelineWrapper(pipeline)
# Create the recipe: PWWS uses a WordNet transformation.
recipe = PWWSRen2019.build(model_wrapper)
# WordNet defaults to english. Set the default language to French ('fra')
#
# See
# "Building a free French wordnet from multilingual resources",
# E. L. R. A. (ELRA) (ed.),
# Proceedings of the Sixth International Language Resources and Evaluation (LREC08).
recipe.transformation.language = "fra"
dataset = HuggingFaceNlpDataset("allocine", split="test")
for idx, result in enumerate(recipe.attack_dataset(dataset)):
print(("-" * 20), f"Result {idx+1}", ("-" * 20))
print(result.__str__(color_method="ansi"))
print()

View File

@@ -3,5 +3,5 @@
# model on the Yelp dataset.
textattack attack --attack-n --goal-function untargeted-classification \
--model bert-base-uncased-yelp --num-examples 8 --transformation word-swap-wordnet \
--constraints edit-distance:12 max-words-perturbed:max_percent=0.75 repeat stopword \
--constraints edit-distance^12 max-words-perturbed:max_percent=0.75 repeat stopword \
--search greedy

View File

@@ -1,4 +1,4 @@
#!/bin/bash
# Shows how to attack a DistilBERT model fine-tuned on SST2 dataset *from the
# huggingface model repository& using the DeepWordBug recipe and 10 examples.
textattack attack --model-from-huggingface distilbert-base-uncased-finetuned-sst-2-english --dataset-from-nlp glue:sst2 --recipe deepwordbug --num-examples 10
textattack attack --model-from-huggingface distilbert-base-uncased-finetuned-sst-2-english --dataset-from-nlp glue^sst2 --recipe deepwordbug --num-examples 10

View File

@@ -1,4 +1,4 @@
#!/bin/bash
# Trains `bert-base-cased` on the STS-B task for 3 epochs. This is a demonstration
# of how our training script handles regression.
textattack train --model bert-base-cased --dataset glue:stsb --batch-size 128 --epochs 3 --max-length 128 --learning-rate 1e-5
textattack train --model bert-base-cased --dataset glue^stsb --batch-size 128 --epochs 3 --max-length 128 --learning-rate 1e-5

View File

@@ -9,7 +9,6 @@ nlp
nltk
numpy
pandas>=1.0.1
pyarrow<1.0
scikit-learn
scipy==1.4.1
sentence_transformers>0.2.6

View File

@@ -142,6 +142,9 @@ attack_test_params = [
@pytest.mark.slow
def test_command_line_attack(name, command, sample_output_file):
"""Runs attack tests and compares their outputs to a reference file."""
# TEMP: skip mr tests
if "-mr" in command:
return
# read in file and create regex
desired_output = open(sample_output_file, "r").read().strip()
print("desired_output.encoded =>", desired_output.encode())

View File

@@ -10,6 +10,7 @@ from textattack.datasets import TextAttackDataset
def _cb(s):
"""Colors some text blue for printing to the terminal."""
return textattack.shared.utils.color_text(str(s), color="blue", method="ansi")
@@ -42,6 +43,9 @@ def get_nlp_dataset_columns(dataset):
elif {"content", "summary"} <= schema:
input_columns = ("content",)
output_column = "summary"
elif {"label", "review"} <= schema:
input_columns = ("review",)
output_column = "label"
else:
raise ValueError(
f"Unsupported dataset schema {schema}. Try loading dataset manually (from a file) instead."

View File

@@ -1,7 +1,7 @@
import torch
from transformers.modeling_bert import BertForSequenceClassification
from textattack.models.tokenizers import BERTTokenizer
from textattack.models.tokenizers import AutoTokenizer
from textattack.shared import utils
@@ -22,7 +22,7 @@ class BERTForClassification:
self.model.to(utils.device)
self.model.eval()
self.tokenizer = BERTTokenizer(model_file_path)
self.tokenizer = AutoTokenizer(model_file_path)
def __call__(self, input_ids=None, **kwargs):
# The tokenizer will return ``input_ids`` along with ``token_type_ids``

View File

@@ -1,4 +1,3 @@
from .auto_tokenizer import AutoTokenizer
from .bert_tokenizer import BERTTokenizer
from .glove_tokenizer import GloveTokenizer
from .t5_tokenizer import T5Tokenizer

View File

@@ -8,17 +8,26 @@ class AutoTokenizer:
standardizes the functionality for TextAttack.
Args:
name: the identifying name of the tokenizer (see AutoTokenizer,
name: the identifying name of the tokenizer, for example, ``bert-base-uncased``
(see AutoTokenizer,
https://github.com/huggingface/transformers/blob/master/src/transformers/tokenization_auto.py)
max_length: if set, will truncate & pad tokens to fit this length
"""
def __init__(
self, name="bert-base-uncased", max_length=256, use_fast=True,
self, tokenizer_path=None, tokenizer=None, max_length=256, use_fast=True,
):
if not (tokenizer_path or tokenizer):
raise ValueError("Must pass tokenizer path or tokenizer")
if tokenizer_path and tokenizer:
raise ValueError("Cannot pass both tokenizer path and tokenizer")
if tokenizer_path:
self.tokenizer = transformers.AutoTokenizer.from_pretrained(
name, use_fast=use_fast
tokenizer_path, use_fast=use_fast
)
else:
self.tokenizer = tokenizer
self.max_length = max_length
self.save_pretrained = self.tokenizer.save_pretrained

View File

@@ -1,11 +0,0 @@
from textattack.models.tokenizers import AutoTokenizer
class BERTTokenizer(AutoTokenizer):
"""A generic class that convert text to tokens and tokens to IDs.
Intended for fine-tuned BERT models.
"""
def __init__(self, name="bert-base-uncased", max_length=256):
super().__init__(name, max_length=max_length)

View File

@@ -1,4 +1,5 @@
import torch
import transformers
import textattack
@@ -8,6 +9,13 @@ from .pytorch_model_wrapper import PyTorchModelWrapper
class HuggingFaceModelWrapper(PyTorchModelWrapper):
"""Loads a HuggingFace ``transformers`` model and tokenizer."""
def __init__(self, model, tokenizer, batch_size=32):
self.model = model.to(textattack.shared.utils.device)
if isinstance(tokenizer, transformers.PreTrainedTokenizer):
tokenizer = textattack.models.tokenizers.AutoTokenizer(tokenizer=tokenizer)
self.tokenizer = tokenizer
self.batch_size = batch_size
def __call__(self, text_input_list):
"""Passes inputs to HuggingFace models as keyword arguments.

View File

@@ -14,3 +14,11 @@ class ModelWrapper(ABC):
@abstractmethod
def __call__(self, text_list):
raise NotImplementedError()
def tokenize(self, inputs):
"""Helper method that calls ``tokenizer.batch_encode`` if possible, and
if not, falls back to calling ``tokenizer.encode`` for each input."""
if hasattr(self.tokenizer, "batch_encode"):
return self.tokenizer.batch_encode(inputs)
else:
return [self.tokenizer.encode(x) for x in inputs]

View File

@@ -18,12 +18,6 @@ class PyTorchModelWrapper(ModelWrapper):
self.tokenizer = tokenizer
self.batch_size = batch_size
def tokenize(self, inputs):
if hasattr(self.tokenizer, "batch_encode"):
return self.tokenizer.batch_encode(inputs)
else:
return [self.tokenizer.encode(x) for x in inputs]
def __call__(self, text_input_list):
model_device = next(self.model.parameters()).device
ids = self.tokenize(text_input_list)

View File

@@ -105,16 +105,15 @@ logger.propagate = False
def _post_install():
logger.info(
"First time running textattack: downloading remaining required packages."
)
logger.info("Updating TextAttack package dependencies.")
logger.info("Downloading NLTK required packages.")
import nltk
nltk.download("wordnet")
nltk.download("averaged_perceptron_tagger")
nltk.download("universal_tagset")
nltk.download("stopwords")
nltk.download("omw")
nltk.download("universal_tagset")
nltk.download("wordnet")
def set_cache_dir(cache_dir):
@@ -132,7 +131,7 @@ def set_cache_dir(cache_dir):
def _post_install_if_needed():
"""Runs _post_install if hasn't been run since install."""
# Check for post-install file.
post_install_file_path = path_in_cache("post_install_check")
post_install_file_path = path_in_cache("post_install_check_2")
post_install_file_lock_path = post_install_file_path + ".lock"
post_install_file_lock = filelock.FileLock(post_install_file_lock_path)
post_install_file_lock.acquire()

View File

@@ -9,6 +9,7 @@ class WordSwap(Transformation):
some of its words.
letters_to_insert (string): letters allowed for insertion into words
(used by some char-based transformations)
"""
def __init__(self, letters_to_insert=None):

View File

@@ -8,13 +8,17 @@ class WordSwapWordNet(WordSwap):
"""Transforms an input by replacing its words with synonyms provided by
WordNet."""
def __init__(self, language="eng"):
if language not in wordnet.langs():
raise ValueError(f"Language {language} not one of {wordnet.langs()}")
self.language = language
def _get_replacement_words(self, word, random=False):
"""Returns a list containing all possible words with 1 character
replaced by a homoglyph."""
synonyms = set()
for syn in wordnet.synsets(word):
for lemma in syn.lemmas():
syn_word = lemma.name()
for syn in wordnet.synsets(word, lang=self.language):
for syn_word in syn.lemma_names(lang=self.language):
if (
(syn_word != word)
and ("_" not in syn_word)