Code Monkey home page Code Monkey logo

qdata / textattack Goto Github PK

View Code? Open in Web Editor NEW
2.9K 38.0 384.0 25.92 MB

TextAttack πŸ™ is a Python framework for adversarial attacks, data augmentation, and model training in NLP https://textattack.readthedocs.io/en/master/

Home Page: https://textattack.readthedocs.io/en/master/

License: MIT License

Python 99.91% Makefile 0.09%
machine-learning security natural-language-processing nlp adversarial-machine-learning adversarial-attacks data-augmentation adversarial-examples

textattack's Introduction

TextAttack πŸ™

Generating adversarial examples for NLP models

[TextAttack Documentation on ReadTheDocs]

About β€’ Setup β€’ Usage β€’ Design

Github Runner Covergae Status PyPI version

TextAttack Demo GIF

About

TextAttack is a Python framework for adversarial attacks, data augmentation, and model training in NLP.

If you're looking for information about TextAttack's menagerie of pre-trained models, you might want the TextAttack Model Zoo page.

Slack Channel

For help and realtime updates related to TextAttack, please join the TextAttack Slack!

Why TextAttack?

There are lots of reasons to use TextAttack:

  1. Understand NLP models better by running different adversarial attacks on them and examining the output
  2. Research and develop different NLP adversarial attacks using the TextAttack framework and library of components
  3. Augment your dataset to increase model generalization and robustness downstream
  4. Train NLP models using just a single command (all downloads included!)

Setup

Installation

You should be running Python 3.6+ to use this package. A CUDA-compatible GPU is optional but will greatly improve code speed. TextAttack is available through pip:

pip install textattack

Once TextAttack is installed, you can run it via command-line (textattack ...) or via python module (python -m textattack ...).

Tip: TextAttack downloads files to ~/.cache/textattack/ by default. This includes pretrained models, dataset samples, and the configuration file config.yaml. To change the cache path, set the environment variable TA_CACHE_DIR. (for example: TA_CACHE_DIR=/tmp/ textattack attack ...).

Usage

Help: textattack --help

TextAttack's main features can all be accessed via the textattack command. Two very common commands are textattack attack <args>, and textattack augment <args>. You can see more information about all commands using

textattack --help

or a specific command using, for example,

textattack attack --help

The examples/ folder includes scripts showing common TextAttack usage for training models, running attacks, and augmenting a CSV file.

The documentation website contains walkthroughs explaining basic usage of TextAttack, including building a custom transformation and a custom constraint..

Running Attacks: textattack attack --help

The easiest way to try out an attack is via the command-line interface, textattack attack.

Tip: If your machine has multiple GPUs, you can distribute the attack across them using the --parallel option. For some attacks, this can really help performance. (If you want to attack Keras models in parallel, please check out examples/attack/attack_keras_parallel.py instead)

Here are some concrete examples:

TextFooler on BERT trained on the MR sentiment classification dataset:

textattack attack --recipe textfooler --model bert-base-uncased-mr --num-examples 100

DeepWordBug on DistilBERT trained on the Quora Question Pairs paraphrase identification dataset:

textattack attack --model distilbert-base-uncased-cola --recipe deepwordbug --num-examples 100

Beam search with beam width 4 and word embedding transformation and untargeted goal function on an LSTM:

textattack attack --model lstm-mr --num-examples 20 \
 --search-method beam-search^beam_width=4 --transformation word-swap-embedding \
 --constraints repeat stopword max-words-perturbed^max_num_words=2 embedding^min_cos_sim=0.8 part-of-speech \
 --goal-function untargeted-classification

Tip: Instead of specifying a dataset and number of examples, you can pass --interactive to attack samples inputted by the user.

Attacks and Papers Implemented ("Attack Recipes"): textattack attack --recipe [recipe_name]

We include attack recipes which implement attacks from the literature. You can list attack recipes using textattack list attack-recipes.

To run an attack recipe: textattack attack --recipe [recipe_name]

TextAttack Overview

Attack Recipe Name Goal Function ConstraintsEnforced Transformation Search Method Main Idea

Attacks on classification tasks, like sentiment classification and entailment:
a2t Untargeted {Classification, Entailment} Percentage of words perturbed, Word embedding distance, DistilBERT sentence encoding cosine similarity, part-of-speech consistency Counter-fitted word embedding swap (or) BERT Masked Token Prediction Greedy-WIR (gradient) from (["Towards Improving Adversarial Training of NLP Models" (Yoo et al., 2021)](https://arxiv.org/abs/2109.00544))
alzantot Untargeted {Classification, Entailment} Percentage of words perturbed, Language Model perplexity, Word embedding distance Counter-fitted word embedding swap Genetic Algorithm from (["Generating Natural Language Adversarial Examples" (Alzantot et al., 2018)](https://arxiv.org/abs/1804.07998))
bae Untargeted Classification USE sentence encoding cosine similarity BERT Masked Token Prediction Greedy-WIR BERT masked language model transformation attack from (["BAE: BERT-based Adversarial Examples for Text Classification" (Garg & Ramakrishnan, 2019)](https://arxiv.org/abs/2004.01970)).
bert-attack Untargeted Classification USE sentence encoding cosine similarity, Maximum number of words perturbed BERT Masked Token Prediction (with subword expansion) Greedy-WIR (["BERT-ATTACK: Adversarial Attack Against BERT Using BERT" (Li et al., 2020)](https://arxiv.org/abs/2004.09984))
checklist {Untargeted, Targeted} Classification checklist distance contract, extend, and substitutes name entities Greedy-WIR Invariance testing implemented in CheckList . (["Beyond Accuracy: Behavioral Testing of NLP models with CheckList" (Ribeiro et al., 2020)](https://arxiv.org/abs/2005.04118))
clare Untargeted {Classification, Entailment} USE sentence encoding cosine similarity RoBERTa Masked Prediction for token swap, insert and merge Greedy ["Contextualized Perturbation for Textual Adversarial Attack" (Li et al., 2020)](https://arxiv.org/abs/2009.07502))
deepwordbug {Untargeted, Targeted} Classification Levenshtein edit distance {Character Insertion, Character Deletion, Neighboring Character Swap, Character Substitution} Greedy-WIR Greedy replace-1 scoring and multi-transformation character-swap attack (["Black-box Generation of Adversarial Text Sequences to Evade Deep Learning Classifiers" (Gao et al., 2018)](https://arxiv.org/abs/1801.04354)
faster-alzantot Untargeted {Classification, Entailment} Percentage of words perturbed, Language Model perplexity, Word embedding distance Counter-fitted word embedding swap Genetic Algorithm Modified, faster version of the Alzantot et al. genetic algorithm, from (["Certified Robustness to Adversarial Word Substitutions" (Jia et al., 2019)](https://arxiv.org/abs/1909.00986))
hotflip (word swap) Untargeted Classification Word Embedding Cosine Similarity, Part-of-speech match, Number of words perturbed Gradient-Based Word Swap Beam search (["HotFlip: White-Box Adversarial Examples for Text Classification" (Ebrahimi et al., 2017)](https://arxiv.org/abs/1712.06751))
iga Untargeted {Classification, Entailment} Percentage of words perturbed, Word embedding distance Counter-fitted word embedding swap Genetic Algorithm Improved genetic algorithm -based word substitution from (["Natural Language Adversarial Attacks and Defenses in Word Level (Wang et al., 2019)"](https://arxiv.org/abs/1909.06723)
input-reduction Input Reduction Word deletion Greedy-WIR Greedy attack with word importance ranking , Reducing the input while maintaining the prediction through word importance ranking (["Pathologies of Neural Models Make Interpretation Difficult" (Feng et al., 2018)](https://arxiv.org/pdf/1804.07781.pdf))
kuleshov Untargeted Classification Thought vector encoding cosine similarity, Language model similarity probability Counter-fitted word embedding swap Greedy word swap (["Adversarial Examples for Natural Language Classification Problems" (Kuleshov et al., 2018)](https://openreview.net/pdf?id=r1QZ3zbAZ))
pruthi Untargeted Classification Minimum word length, Maximum number of words perturbed {Neighboring Character Swap, Character Deletion, Character Insertion, Keyboard-Based Character Swap} Greedy search simulates common typos (["Combating Adversarial Misspellings with Robust Word Recognition" (Pruthi et al., 2019)](https://arxiv.org/abs/1905.11268)
pso Untargeted Classification HowNet Word Swap Particle Swarm Optimization (["Word-level Textual Adversarial Attacking as Combinatorial Optimization" (Zang et al., 2020)](https://www.aclweb.org/anthology/2020.acl-main.540/))
pwws Untargeted Classification WordNet-based synonym swap Greedy-WIR (saliency) Greedy attack with word importance ranking based on word saliency and synonym swap scores (["Generating Natural Language Adversarial Examples through Probability Weighted Word Saliency" (Ren et al., 2019)](https://www.aclweb.org/anthology/P19-1103/))
textbugger : (black-box) Untargeted Classification USE sentence encoding cosine similarity {Character Insertion, Character Deletion, Neighboring Character Swap, Character Substitution} Greedy-WIR ([(["TextBugger: Generating Adversarial Text Against Real-world Applications" (Li et al., 2018)](https://arxiv.org/abs/1812.05271)).
textfooler Untargeted {Classification, Entailment} Word Embedding Distance, Part-of-speech match, USE sentence encoding cosine similarity Counter-fitted word embedding swap Greedy-WIR Greedy attack with word importance ranking (["Is Bert Really Robust?" (Jin et al., 2019)](https://arxiv.org/abs/1907.11932))

Attacks on sequence-to-sequence models:
morpheus Minimum BLEU Score Inflection Word Swap Greedy search Greedy to replace words with their inflections with the goal of minimizing BLEU score (["It’s Morphin’ Time! Combating Linguistic Discrimination with Inflectional Perturbations"](https://www.aclweb.org/anthology/2020.acl-main.263.pdf)
seq2sick :(black-box) Non-overlapping output Counter-fitted word embedding swap Greedy-WIR Greedy attack with goal of changing every word in the output translation. Currently implemented as black-box with plans to change to white-box as done in paper (["Seq2Sick: Evaluating the Robustness of Sequence-to-Sequence Models with Adversarial Examples" (Cheng et al., 2018)](https://arxiv.org/abs/1803.01128))

Recipe Usage Examples

Here are some examples of testing attacks from the literature from the command-line:

TextFooler against BERT fine-tuned on SST-2:

textattack attack --model bert-base-uncased-sst2 --recipe textfooler --num-examples 10

seq2sick (black-box) against T5 fine-tuned for English-German translation:

 textattack attack --model t5-en-de --recipe seq2sick --num-examples 100

Augmenting Text: textattack augment

Many of the components of TextAttack are useful for data augmentation. The textattack.Augmenter class uses a transformation and a list of constraints to augment data. We also offer built-in recipes for data augmentation:

  • wordnet augments text by replacing words with WordNet synonyms
  • embedding augments text by replacing words with neighbors in the counter-fitted embedding space, with a constraint to ensure their cosine similarity is at least 0.8
  • charswap augments text by substituting, deleting, inserting, and swapping adjacent characters
  • eda augments text with a combination of word insertions, substitutions and deletions.
  • checklist augments text by contraction/extension and by substituting names, locations, numbers.
  • clare augments text by replacing, inserting, and merging with a pre-trained masked language model.
  • back_trans augments text by backtranslation approach.
  • back_transcription augments text by back transcription approach.

Augmentation Command-Line Interface

The easiest way to use our data augmentation tools is with textattack augment <args>. textattack augment takes an input CSV file and text column to augment, along with the number of words to change per augmentation and the number of augmentations per input example. It outputs a CSV in the same format with all the augmentation examples corresponding to the proper columns.

For example, given the following as examples.csv:

"text",label
"the rock is destined to be the 21st century's new conan and that he's going to make a splash even greater than arnold schwarzenegger , jean- claud van damme or steven segal.", 1
"the gorgeously elaborate continuation of 'the lord of the rings' trilogy is so huge that a column of words cannot adequately describe co-writer/director peter jackson's expanded vision of j . r . r . tolkien's middle-earth .", 1
"take care of my cat offers a refreshingly different slice of asian cinema .", 1
"a technically well-made suspenser . . . but its abrupt drop in iq points as it races to the finish line proves simply too discouraging to let slide .", 0
"it's a mystery how the movie could be released in this condition .", 0

The command

textattack augment --input-csv examples.csv --output-csv output.csv  --input-column text --recipe embedding --pct-words-to-swap .1 --transformations-per-example 2 --exclude-original

will augment the text column by altering 10% of each example's words, generating twice as many augmentations as original inputs, and exclude the original inputs from the output CSV. (All of this will be saved to augment.csv by default.)

Tip: Just as running attacks interactively, you can also pass --interactive to augment samples inputted by the user to quickly try out different augmentation recipes!

After augmentation, here are the contents of augment.csv:

text,label
"the rock is destined to be the 21st century's newest conan and that he's gonna to make a splashing even stronger than arnold schwarzenegger , jean- claud van damme or steven segal.",1
"the rock is destined to be the 21tk century's novel conan and that he's going to make a splat even greater than arnold schwarzenegger , jean- claud van damme or stevens segal.",1
the gorgeously elaborate continuation of 'the lord of the rings' trilogy is so huge that a column of expression significant adequately describe co-writer/director pedro jackson's expanded vision of j . rs . r . tolkien's middle-earth .,1
the gorgeously elaborate continuation of 'the lordy of the piercings' trilogy is so huge that a column of mots cannot adequately describe co-novelist/director peter jackson's expanded vision of j . r . r . tolkien's middle-earth .,1
take care of my cat offerings a pleasantly several slice of asia cinema .,1
taking care of my cat offers a pleasantly different slice of asiatic kino .,1
a technically good-made suspenser . . . but its abrupt drop in iq points as it races to the finish bloodline proves straightforward too disheartening to let slide .,0
a technically well-made suspenser . . . but its abrupt drop in iq dot as it races to the finish line demonstrates simply too disheartening to leave slide .,0
it's a enigma how the film wo be releases in this condition .,0
it's a enigma how the filmmaking wo be publicized in this condition .,0

The 'embedding' augmentation recipe uses counterfitted embedding nearest-neighbors to augment data.

Augmentation Python Interface

In addition to the command-line interface, you can augment text dynamically by importing the Augmenter in your own code. All Augmenter objects implement augment and augment_many to generate augmentations of a string or a list of strings. Here's an example of how to use the EmbeddingAugmenter in a python script:

>>> from textattack.augmentation import EmbeddingAugmenter
>>> augmenter = EmbeddingAugmenter()
>>> s = 'What I cannot create, I do not understand.'
>>> augmenter.augment(s)
['What I notable create, I do not understand.', 'What I significant create, I do not understand.', 'What I cannot engender, I do not understand.', 'What I cannot creating, I do not understand.', 'What I cannot creations, I do not understand.', 'What I cannot create, I do not comprehend.', 'What I cannot create, I do not fathom.', 'What I cannot create, I do not understanding.', 'What I cannot create, I do not understands.', 'What I cannot create, I do not understood.', 'What I cannot create, I do not realise.']

You can also create your own augmenter from scratch by importing transformations/constraints from textattack.transformations and textattack.constraints. Here's an example that generates augmentations of a string using WordSwapRandomCharacterDeletion:

>>> from textattack.transformations import WordSwapRandomCharacterDeletion
>>> from textattack.transformations import CompositeTransformation
>>> from textattack.augmentation import Augmenter
>>> transformation = CompositeTransformation([WordSwapRandomCharacterDeletion()])
>>> augmenter = Augmenter(transformation=transformation, transformations_per_example=5)
>>> s = 'What I cannot create, I do not understand.'
>>> augmenter.augment(s)
['What I cannot creae, I do not understand.', 'What I cannot creat, I do not understand.', 'What I cannot create, I do not nderstand.', 'What I cannot create, I do nt understand.', 'Wht I cannot create, I do not understand.']

Prompt Augmentation

In additional to augmentation of regular text, you can augment prompts and then generate responses to the augmented prompts using a large language model (LLMs). The augmentation is performed using the same Augmenter as above. To generate responses, you can use your own LLM, a HuggingFace LLM, or an OpenAI LLM. Here's an example using a pretrained HuggingFace LLM:

>>> from textattack.augmentation import EmbeddingAugmenter
>>> from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
>>> from textattack.llms import HuggingFaceLLMWrapper
>>> from textattack.prompt_augmentation import PromptAugmentationPipeline
>>> augmenter = EmbeddingAugmenter(transformations_per_example=3)
>>> model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-small")
>>> tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-small")
>>> model_wrapper = HuggingFaceLLMWrapper(model, tokenizer)
>>> pipeline = PromptAugmentationPipeline(augmenter, model_wrapper)
>>> pipeline("Classify the following piece of text as `positive` or `negative`: This movie is great!")
[('Classify the following piece of text as `positive` or `negative`: This film is great!', ['positive']), ('Classify the following piece of text as `positive` or `negative`: This movie is fabulous!', ['positive']), ('Classify the following piece of text as `positive` or `negative`: This movie is wonderful!', ['positive'])]

Training Models: textattack train

Our model training code is available via textattack train to help you train LSTMs, CNNs, and transformers models using TextAttack out-of-the-box. Datasets are automatically loaded using the datasets package.

Training Examples

Train our default LSTM for 50 epochs on the Yelp Polarity dataset:

textattack train --model-name-or-path lstm --dataset yelp_polarity  --epochs 50 --learning-rate 1e-5

Fine-Tune bert-base on the CoLA dataset for 5 epochs*:

textattack train --model-name-or-path bert-base-uncased --dataset glue^cola --per-device-train-batch-size 8 --epochs 5

To check datasets: textattack peek-dataset

To take a closer look at a dataset, use textattack peek-dataset. TextAttack will print some cursory statistics about the inputs and outputs from the dataset. For example,

textattack peek-dataset --dataset-from-huggingface snli

will show information about the SNLI dataset from the NLP package.

To list functional components: textattack list

There are lots of pieces in TextAttack, and it can be difficult to keep track of all of them. You can use textattack list to list components, for example, pretrained models (textattack list models) or available search methods (textattack list search-methods).

Design

Models

TextAttack is model-agnostic! You can use TextAttack to analyze any model that outputs IDs, tensors, or strings. To help users, TextAttack includes pre-trained models for different common NLP tasks. This makes it easier for users to get started with TextAttack. It also enables a more fair comparison of attacks from the literature.

Built-in Models and Datasets

TextAttack also comes built-in with models and datasets. Our command-line interface will automatically match the correct dataset to the correct model. We include 82 different (Oct 2020) pre-trained models for each of the nine GLUE tasks, as well as some common datasets for classification, translation, and summarization.

A list of available pretrained models and their validation accuracies is available at textattack/models/README.md. You can also view a full list of provided models & datasets via textattack attack --help.

Here's an example of using one of the built-in models (the SST-2 dataset is automatically loaded):

textattack attack --model roberta-base-sst2 --recipe textfooler --num-examples 10

HuggingFace support: transformers models and datasets datasets

We also provide built-in support for transformers pretrained models and datasets from the datasets package! Here's an example of loading and attacking a pre-trained model and dataset:

textattack attack --model-from-huggingface distilbert-base-uncased-finetuned-sst-2-english --dataset-from-huggingface glue^sst2 --recipe deepwordbug --num-examples 10

You can explore other pre-trained models using the --model-from-huggingface argument, or other datasets by changing --dataset-from-huggingface.

Loading a model or dataset from a file

You can easily try out an attack on a local model or dataset sample. To attack a pre-trained model, create a short file that loads them as variables model and tokenizer. The tokenizer must be able to transform string inputs to lists or tensors of IDs using a method called encode(). The model must take inputs via the __call__ method.

Custom Model from a file

To experiment with a model you've trained, you could create the following file and name it my_model.py:

model = load_your_model_with_custom_code() # replace this line with your model loading code
tokenizer = load_your_tokenizer_with_custom_code() # replace this line with your tokenizer loading code

Then, run an attack with the argument --model-from-file my_model.py. The model and tokenizer will be loaded automatically.

Custom Datasets

Dataset from a file

Loading a dataset from a file is very similar to loading a model from a file. A 'dataset' is any iterable of (input, output) pairs. The following example would load a sentiment classification dataset from file my_dataset.py:

dataset = [('Today was....', 1), ('This movie is...', 0), ...]

You can then run attacks on samples from this dataset by adding the argument --dataset-from-file my_dataset.py.

Dataset loading via other mechanism, see: more details at here

import textattack
my_dataset = [("text",label),....]
new_dataset = textattack.datasets.Dataset(my_dataset)

Dataset via AttackedText class

To allow for word replacement after a sequence has been tokenized, we include an AttackedText object which maintains both a list of tokens and the original text, with punctuation. We use this object in favor of a list of words or just raw text.

Attacks and how to design a new attack

We formulate an attack as consisting of four components: a goal function which determines if the attack has succeeded, constraints defining which perturbations are valid, a transformation that generates potential modifications given an input, and a search method which traverses through the search space of possible perturbations. The attack attempts to perturb an input text such that the model output fulfills the goal function (i.e., indicating whether the attack is successful) and the perturbation adheres to the set of constraints (e.g., grammar constraint, semantic similarity constraint). A search method is used to find a sequence of transformations that produce a successful adversarial example.

This modular design unifies adversarial attack methods into one system, enables us to easily assemble attacks from the literature while re-using components that are shared across attacks. We provides clean, readable implementations of 16 adversarial attack recipes from the literature (see above table). For the first time, these attacks can be benchmarked, compared, and analyzed in a standardized setting.

TextAttack is model-agnostic - meaning it can run attacks on models implemented in any deep learning framework. Model objects must be able to take a string (or list of strings) and return an output that can be processed by the goal function. For example, machine translation models take a list of strings as input and produce a list of strings as output. Classification and entailment models return an array of scores. As long as the user's model meets this specification, the model is fit to use with TextAttack.

Goal Functions

A GoalFunction takes as input an AttackedText object, scores it, and determines whether the attack has succeeded, returning a GoalFunctionResult.

Constraints

A Constraint takes as input a current AttackedText, and a list of transformed AttackedTexts. For each transformed option, it returns a boolean representing whether the constraint is met.

Transformations

A Transformation takes as input an AttackedText and returns a list of possible transformed AttackedTexts. For example, a transformation might return all possible synonym replacements.

Search Methods

A SearchMethod takes as input an initial GoalFunctionResult and returns a final GoalFunctionResult The search is given access to the get_transformations function, which takes as input an AttackedText object and outputs a list of possible transformations filtered by meeting all of the attack’s constraints. A search consists of successive calls to get_transformations until the search succeeds (determined using get_goal_results) or is exhausted.

On Benchmarking Attacks

  • See our analysis paper: Searching for a Search Method: Benchmarking Search Algorithms for Generating NLP Adversarial Examples at EMNLP BlackBoxNLP.

  • As we emphasized in the above paper, we don't recommend to directly compare Attack Recipes out of the box.

  • This comment is due to that attack recipes in the recent literature used different ways or thresholds in setting up their constraints. Without the constraint space held constant, an increase in attack success rate could come from an improved search or transformation method or a less restrictive search space.

  • Our Github on benchmarking scripts and results: TextAttack-Search-Benchmark Github

On Quality of Generated Adversarial Examples in Natural Language

  • Our analysis Paper in EMNLP Findings
  • We analyze the generated adversarial examples of two state-of-the-art synonym substitution attacks. We find that their perturbations often do not preserve semantics, and 38% introduce grammatical errors. Human surveys reveal that to successfully preserve semantics, we need to significantly increase the minimum cosine similarities between the embeddings of swapped words and between the sentence encodings of original and perturbed sentences.With constraints adjusted to better preserve semantics and grammaticality, the attack success rate drops by over 70 percentage points.
  • Our Github on Reevaluation results: Reevaluating-NLP-Adversarial-Examples Github
  • As we have emphasized in this analysis paper, we recommend researchers and users to be EXTREMELY mindful on the quality of generated adversarial examples in natural language
  • We recommend the field to use human-evaluation derived thresholds for setting up constraints

Multi-lingual Support

Contributing to TextAttack

We welcome suggestions and contributions! Submit an issue or pull request and we will do our best to respond in a timely manner. TextAttack is currently in an "alpha" stage in which we are working to improve its capabilities and design.

See CONTRIBUTING.md for detailed information on contributing.

Citing TextAttack

If you use TextAttack for your research, please cite TextAttack: A Framework for Adversarial Attacks, Data Augmentation, and Adversarial Training in NLP.

@inproceedings{morris2020textattack,
  title={TextAttack: A Framework for Adversarial Attacks, Data Augmentation, and Adversarial Training in NLP},
  author={Morris, John and Lifland, Eli and Yoo, Jin Yong and Grigsby, Jake and Jin, Di and Qi, Yanjun},
  booktitle={Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing: System Demonstrations},
  pages={119--126},
  year={2020}
}

textattack's People

Contributors

a1noack avatar arrrlex avatar arshdeepsekhon avatar ashiq5 avatar bzh0807 avatar cogeid avatar dangne avatar dependabot[bot] avatar hanyu-liu-123 avatar jakegrigsby avatar jind11 avatar jinyongyoo avatar jxmorris12 avatar k-ivey avatar liuyuyan2717 avatar marcorosa avatar mintforever avatar nithvijay avatar opdoop avatar plasmashen avatar qiyanjun avatar sanchit97 avatar sherlockyyc avatar skorzewski avatar tk-dev11 avatar tsinggggg avatar uvafan avatar vijaykalmath avatar wenh06 avatar willyptrain avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

textattack's Issues

Using with transformers repository

Hi,

Thanks for developing this great repo!!

I just want to ask how can I make this work with models trained with huggingface's transformers repository. I understand the models can be used directly (*bin file), but textattack needs train_args.json file also which is not provided with a transformers model.

How can I use models in transformers repo with textattack, or Do I have to train models with textattack only?

Edit: I see there are two args to load custom models, --model-from-file and --model-from-huggingface. Can you provide example of how to use these two arguments? Or point me to any example in docs?

If I provide path to downloaded model in --model-from-file, I get the error:
ValueError: Failed to import file.

Thanks,

Error when using alzantot recipe

To reproduce:

textattack attack --recipe alzantot --model lstm-imdb --num-examples 1

Error:

File "/usr/local/lib/python3.6/dist-packages/textattack/shared/attack.py", line 127, in _filter_transformations_uncached
    filtered_texts, current_text, original_text=original_text
  File "/usr/local/lib/python3.6/dist-packages/textattack/constraints/constraint.py", line 37, in call_many
    compatible_transformed_texts, current_text, original_text=original_text
  File "/usr/local/lib/python3.6/dist-packages/textattack/constraints/grammaticality/language_models/google_language_model/google_language_model.py", line 76, in _check_constraint_many
    zip(item_indices, get_probs(current_text, this_transformed_texts))
  File "/usr/local/lib/python3.6/dist-packages/textattack/constraints/grammaticality/language_models/google_language_model/google_language_model.py", line 54, in get_probs
    prefix = current_text.words[word_swap_index - 1]
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'

very low-probability IndexError in textfooler attack

while running many iterations of the textfooler attack with different random seeds (during adversarial training) I received the following error:

Attack:  68%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–Œ                                           | 676/1000 [04:33<02:11,  2.47it/s]
Traceback (most recent call last):
  File "/u/jm8wx/.conda/envs/torch/bin/textattack", line 33, in <module>
    sys.exit(load_entry_point('textattack', 'console_scripts', 'textattack')())
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/textattack_cli.py", line 39, in main
    args.func.run(args)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/train_model/train_model_command.py", line 29, in run
    train_model(args)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/train_model/run_training.py", line 580, in train_model
    adv_attack_results = _generate_adversarial_examples(
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/train_model/run_training.py", line 270, in _generate_adversarial_examples
    for adv_ex in tqdm.tqdm(
  File "/u/jm8wx/.conda/envs/torch/lib/python3.8/site-packages/tqdm/std.py", line 1130, in __iter__
    for obj in iterable:
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/shared/attack.py", line 270, in attack_dataset
    result = self.attack_one(goal_function_result)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/shared/attack.py", line 203, in attack_one
    final_result = self.search_method(initial_result)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/search_methods/search_method.py", line 29, in __call__
    return self._perform_search(initial_result)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/search_methods/greedy_word_swap_wir.py", line 92, in _perform_search
    index_order, search_over = self._get_index_order(attacked_text)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/search_methods/greedy_word_swap_wir.py", line 71, in _get_index_order
    leave_one_texts = [
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/search_methods/greedy_word_swap_wir.py", line 72, in <listcomp>
    initial_text.delete_word_at_index(i) for i in range(len_text)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/shared/attacked_text.py", line 233, in delete_word_at_index
    return self.replace_word_at_index(index, "")
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/shared/attacked_text.py", line 228, in replace_word_at_index
    return self.replace_words_at_indices([index], [new_word])
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/shared/attacked_text.py", line 219, in replace_words_at_indices
    return self.generate_new_attacked_text(words)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/shared/attacked_text.py", line 330, in generate_new_attacked_text
    if original_text[0] == " ":
IndexError: string index out of range

Transformers Model "textattack/facebook-bart-large-CoLA" is 3 dimensional, but 2 dimensional is expected.

I copied the code from the examples/text-classification/run_glue.py from transformers and replace the model with your model.
I found that your model cannot evaluate properly with this structure.
How should I modify the program to make it run? Thanks

06/19/2020 19:15:42 - INFO - transformers.configuration_utils -   Model config BartConfig {
  "_num_labels": 3,
  "activation_dropout": 0.0,
  "activation_function": "gelu",
  "add_bias_logits": false,
  "add_final_layer_norm": false,
  "architectures": [
    "BartForSequenceClassification"
  ],
  "attention_dropout": 0.0,
  "bos_token_id": 0,
  "classif_dropout": 0.0,
  "d_model": 1024,
  "decoder_attention_heads": 16,
  "decoder_ffn_dim": 4096,
  "decoder_layerdrop": 0.0,
  "decoder_layers": 12,
  "decoder_start_token_id": 2,
  "dropout": 0.1,
  "encoder_attention_heads": 16,
  "encoder_ffn_dim": 4096,
  "encoder_layerdrop": 0.0,
  "encoder_layers": 12,
  "eos_token_id": 2,
  "finetuning_task": "cola",
  "id2label": {
    "0": "LABEL_0",
    "1": "LABEL_1",
    "2": "LABEL_2"
  },
  "init_std": 0.02,
  "is_encoder_decoder": true,
  "label2id": {
    "LABEL_0": 0,
    "LABEL_1": 1,
    "LABEL_2": 2
  },
  "max_position_embeddings": 1024,
  "model_type": "bart",
  "normalize_before": false,
  "normalize_embedding": true,
  "num_hidden_layers": 12,
  "output_past": false,
  "pad_token_id": 1,
  "prefix": " ",
  "scale_embedding": false,
  "static_position_embeddings": false,
  "task_specific_params": {
    "summarization": {
      "early_stopping": true,
      "length_penalty": 2.0,
      "max_length": 142,
      "min_length": 56,
      "no_repeat_ngram_size": 3,
      "num_beams": 4
    }
  },
  "vocab_size": 50265
}

06/19/2020 19:15:43 - INFO - transformers.modeling_utils -   loading weights file https://cdn.huggingface.co/textattack/facebook-bart-large-CoLA/pytorch_model.bin from cache at /lustre/home/acct-cftest/stu374/.cache/torch/transformers/7a795dbf6f525f7fa750bb8016ae30d73b5112d97458b5e4c795c17aa55c9d25.cef22b55ac9bec2f923ff4b9293e3fc819ffe9d74a2c7383e3986152fa09582d
Traceback (most recent call last):
  File "run_glue.py", line 270, in <module>
    main()
  File "run_glue.py", line 156, in main
    tokenizer, model = tokenmodel()
  File "run_glue.py", line 55, in tokenmodel
    model = AutoModelForSequenceClassification.from_pretrained(str)
  File "/lustre/home/acct-cftest/stu374/trans/lib64/python3.6/site-packages/transformers/modeling_auto.py", line 856, in from_pretrained
    return model_class.from_pretrained(pretrained_model_name_or_path, *model_args, config=config, **kwargs)
  File "/lustre/home/acct-cftest/stu374/trans/lib64/python3.6/site-packages/transformers/modeling_utils.py", line 753, in from_pretrained
    model.__class__.__name__, "\n\t".join(error_msgs)
RuntimeError: Error(s) in loading state_dict for BartForSequenceClassification:
	size mismatch for classification_head.out_proj.weight: copying a param with shape torch.Size([2, 1024]) from checkpoint, the shape in current model is torch.Size([3, 1024]).
	size mismatch for classification_head.out_proj.bias: copying a param with shape torch.Size([2]) from checkpoint, the shape in current model is torch.Size([3]).
import dataclasses
import logging
import os
import sys
from dataclasses import dataclass, field
from typing import Callable, Dict, Optional

import numpy as np

from transformers import AutoConfig, AutoModelForSequenceClassification, AutoTokenizer, EvalPrediction, GlueDataset
from transformers import GlueDataTrainingArguments as DataTrainingArguments
from transformers import (
    HfArgumentParser,
    Trainer,
    TrainingArguments,
    glue_compute_metrics,
    glue_output_modes,
    glue_tasks_num_labels,
    set_seed,
)


logger = logging.getLogger(__name__)

def tokenmodel():
    strs = [
        "textattack/distilbert-base-cased-CoLA",#0.46372927911071965 0
        "Huntersx/cola_model",#err 1
        "textattack/bert-base-uncased-CoLA",#0.5338774230813111 2
        "textattack/facebook-bart-large-CoLA",#err 3
        "textattack/roberta-base-CoLA",#0.5365747584120986 4
        "textattack/xlnet-large-cased-CoLA",#0 5
        "textattack/xlnet-base-cased-CoLA",#0.2279360616503532 6
    ]
    str = strs[2]
    tokenizer = AutoTokenizer.from_pretrained(str)
    model = AutoModelForSequenceClassification.from_pretrained(str)
    return tokenizer, model



@dataclass
class ModelArguments:
    """
    Arguments pertaining to which model/config/tokenizer we are going to fine-tune from.
    """

    model_name_or_path: str = field(
        metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"}
    )
    config_name: Optional[str] = field(
        default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"}
    )
    tokenizer_name: Optional[str] = field(
        default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}
    )
    cache_dir: Optional[str] = field(
        default="cache_dir", metadata={"help": "Where do you want to store the pretrained models downloaded from s3"}
    )
    


def main():
    # See all possible arguments in src/transformers/training_args.py
    # or by passing the --help flag to this script.
    # We now keep distinct sets of args, for a cleaner separation of concerns.

    parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))

    if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):
        # If we pass only one argument to the script and it's the path to a json file,
        # let's parse it to get our arguments.
        model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1]))
    else:
        model_args, data_args, training_args = parser.parse_args_into_dataclasses()

    if (
        os.path.exists(training_args.output_dir)
        and os.listdir(training_args.output_dir)
        and training_args.do_train
        and not training_args.overwrite_output_dir
    ):
        raise ValueError(
            f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome."
        )

    # Setup logging
    logging.basicConfig(
        format="%(asctime)s - %(levelname)s - %(name)s -   %(message)s",
        datefmt="%m/%d/%Y %H:%M:%S",
        level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN,
    )
    logger.warning(
        "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s",
        training_args.local_rank,
        training_args.device,
        training_args.n_gpu,
        bool(training_args.local_rank != -1),
        training_args.fp16,
    )
    logger.info("Training/evaluation parameters %s", training_args)

    # Set seed
    set_seed(training_args.seed)

    try:
        num_labels = glue_tasks_num_labels[data_args.task_name]
        output_mode = glue_output_modes[data_args.task_name]
    except KeyError:
        raise ValueError("Task not found: %s" % (data_args.task_name))

    # Load pretrained model and tokenizer
    #
    # Distributed training:
    # The .from_pretrained methods guarantee that only one local process can concurrently
    # download model & vocab.

    # config = AutoConfig.from_pretrained(
        # model_args.config_name if model_args.config_name else model_args.model_name_or_path,
        # num_labels=num_labels,
        # finetuning_task=data_args.task_name,
        # cache_dir=model_args.cache_dir,
    # )
    # tokenizer = AutoTokenizer.from_pretrained(
        # model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path,
        # cache_dir=model_args.cache_dir,
    # )
    # tokenizer = AutoTokenizer.from_pretrained("textattack/distilbert-base-cased-CoLA")
    # tokenizer = AutoTokenizer.from_pretrained("Huntersx/cola_model")
    # model = AutoModelForSequenceClassification.from_pretrained(
        # model_args.model_name_or_path,
        # from_tf=bool(".ckpt" in model_args.model_name_or_path),
        # config=config,
        # cache_dir=model_args.cache_dir,
    # )
    # model = AutoModelForSequenceClassification.from_pretrained("textattack/distilbert-base-cased-CoLA")
    # model = AutoModelForSequenceClassification.from_pretrained("Huntersx/cola_model")
    tokenizer, model = tokenmodel()
    # model = 
    # Get datasets
    train_dataset = (
        GlueDataset(data_args, tokenizer=tokenizer, cache_dir=model_args.cache_dir) if training_args.do_train else None
    )
    print(model_args.cache_dir)
    eval_dataset = (
        GlueDataset(data_args, tokenizer=tokenizer, mode="dev")
        if training_args.do_eval
        else None
    )
    test_dataset = (
        GlueDataset(data_args, tokenizer=tokenizer, mode="test")
        if training_args.do_predict
        else None
    )

    def build_compute_metrics_fn(task_name: str) -> Callable[[EvalPrediction], Dict]:
        def compute_metrics_fn(p: EvalPrediction):
            if output_mode == "classification":
                preds = np.argmax(p.predictions, axis=1)
            elif output_mode == "regression":
                preds = np.squeeze(p.predictions)
            return glue_compute_metrics(task_name, preds, p.label_ids)

        return compute_metrics_fn

    # Initialize our Trainer
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset,
        eval_dataset=eval_dataset,
        compute_metrics=build_compute_metrics_fn(data_args.task_name),
    )

    # Training
    if False:
        trainer.train(
            model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None
        )
        trainer.save_model()
        # For convenience, we also re-save the tokenizer to the same directory,
        # so that you can share your model easily on huggingface.co/models =)
        if trainer.is_world_master():
            tokenizer.save_pretrained(training_args.output_dir)

    # Evaluation
    eval_results = {}
    if True:
        logger.info("*** Evaluate ***")

        # Loop to handle MNLI double evaluation (matched, mis-matched)
        eval_datasets = [eval_dataset]
        if data_args.task_name == "mnli":
            mnli_mm_data_args = dataclasses.replace(data_args, task_name="mnli-mm")
            eval_datasets.append(
                GlueDataset(mnli_mm_data_args, tokenizer=tokenizer, mode="dev", cache_dir=model_args.cache_dir)
            )

        for eval_dataset in eval_datasets:
            trainer.compute_metrics = build_compute_metrics_fn(eval_dataset.args.task_name)
            eval_result = trainer.evaluate(eval_dataset=eval_dataset)

            output_eval_file = os.path.join(
                training_args.output_dir, f"eval_results_{eval_dataset.args.task_name}.txt"
            )
            if trainer.is_world_master():
                with open(output_eval_file, "w") as writer:
                    logger.info("***** Eval results {} *****".format(eval_dataset.args.task_name))
                    for key, value in eval_result.items():
                        logger.info("  %s = %s", key, value)
                        writer.write("%s = %s\n" % (key, value))

            eval_results.update(eval_result)

    if True:
        logging.info("*** Test ***")
        test_datasets = [test_dataset]
        if data_args.task_name == "mnli":
            mnli_mm_data_args = dataclasses.replace(data_args, task_name="mnli-mm")
            test_datasets.append(
                GlueDataset(mnli_mm_data_args, tokenizer=tokenizer, mode="test", cache_dir=model_args.cache_dir)
            )

        for test_dataset in test_datasets:
            predictions = trainer.predict(test_dataset=test_dataset).predictions
            if output_mode == "classification":
                predictions = np.argmax(predictions, axis=1)

            output_test_file = os.path.join(
                training_args.output_dir, f"test_results_{test_dataset.args.task_name}.txt"
            )
            if trainer.is_world_master():
                with open(output_test_file, "w") as writer:
                    logger.info("***** Test results {} *****".format(test_dataset.args.task_name))
                    writer.write("index\tprediction\n")
                    for index, item in enumerate(predictions):
                        if output_mode == "regression":
                            writer.write("%d\t%3.3f\n" % (index, item))
                        else:
                            item = test_dataset.get_labels()[item]
                            writer.write("%d\t%s\n" % (index, item))
    return eval_results


def _mp_fn(index):
    # For xla_spawn (TPUs)
    main()



if __name__ == "__main__":
    main()

BertTokenizer with HuggingFaceModelWrapper

I was trying to attack a customized hugging face model with textattack. When I used the HuggingFaceModelWrapper i got an error about the BertTokenizer.

From https://github.com/QData/TextAttack/blob/master/textattack/models/wrappers/pytorch_model_wrapper.py, the wrapper is expecting the batch_encode method or the encode method from the tokenizer(if the former does not exist)
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]

I was using a cusomized transformers.BertTokenizer from someone else which does not seem to have the batch_encode method. Therefore it falls back to the encode method.

However according to https://github.com/huggingface/transformers/blob/63144701ed46d6423b5968db40b6d4469d7d9b87/src/transformers/tokenization_utils_base.py#L1278, encode returns a list of int.

def encode( self, text: Union[TextInput, PreTokenizedInput, EncodedInput], text_pair: Optional[Union[TextInput, PreTokenizedInput, EncodedInput]] = None, add_special_tokens: bool = True, padding: Union[bool, str, PaddingStrategy] = False, truncation: Union[bool, str, TruncationStrategy] = False, max_length: Optional[int] = None, stride: int = 0, return_tensors: Optional[Union[str, TensorType]] = None, **kwargs ) -> List[int]:

Therefore, the return of the tokenize method from wrapper when there's no batch encode method would be a list of list instead of list of dicts(which are expected by the huggingface wrapper https://github.com/QData/TextAttack/blob/master/textattack/models/wrappers/huggingface_model_wrapper.py#L20), which causes the bug.

I wonder if i did anything wrong? Thank you very much.

Better naming for CSVLogger

Currently, logging to CSV uses the following format attack-[timestamp].csv. This might be a bit challenging to track results, e.g. it's not clear from the name that what method or recipe was used for the attack.

Because several cmd arguments are already text or string, it might be a good idea to use them for constructing the filename. For example, something like

[model]-[recipe]-[timestamp]

timestamp could also be more readable using yyyy-mm-dd--hh-ms.

Reproducing results in original papers

Hello,

Thanks for this great codebase.

I have been playing around with this repository and found it to work well out of the box. I have a question regarding reproducibility of results from original papers. I know recipes are the full implementations of the papers.

Do you have any stats on reproducing results in original papers?

Thanks,

Passing indices=[] to Attack.attack_dataset attacks the full dataset

Say attack is an instance of the Attack class. Calling attack.attack_dataset(dataset, indices=[1, 3, 5, 7]) will run attacks on indices 1, 3, 5, and 7 of the dataset.

It is common to omit the indices arugment: simply calling attack.attack_dataset(dataset) will run an attack on every sample in the dataset. Consequently, attack.attack_dataset(dataset, indices=None) will also run the attack on each sample in the dataset.

However, now attack.attack_dataset(dataset, indices=[]) attacks each sample in the dataset, too, since in Python, [] and None both evaluate to False. This is bad. If a user passes in indices=[], we don't want to run an attack on every sample– we just want to return an empty list, and potentially print a warning message.

Import error: Cannot import name 'AutoModelForMaskedLM' from 'transformers'

Hi guys. I get the following error when importing textattack for the first time on a Ubuntu 18.04.3 machine (technically a Singularity container).

Some versions of relevant packages: textattack==0.2.4, transformers==2.11.0,nltk==3.4.5

The code

import textattack

And the traceback:

textattack: First time running textattack: downloading remaining required packages.
textattack: Downloading NLTK required packages.
[nltk_data] Downloading package wordnet to /home/tproth/nltk_data...
[nltk_data]   Unzipping corpora/wordnet.zip.
[nltk_data] Downloading package averaged_perceptron_tagger to
[nltk_data]     /home/tproth/nltk_data...
[nltk_data]   Unzipping taggers/averaged_perceptron_tagger.zip.
[nltk_data] Downloading package universal_tagset to
[nltk_data]     /home/tproth/nltk_data...
[nltk_data]   Unzipping taggers/universal_tagset.zip.
[nltk_data] Downloading package stopwords to /home/tproth/nltk_data...
[nltk_data]   Unzipping corpora/stopwords.zip.
wandb: WARNING W&B installed but not logged in.  Run `wandb login` or set the WANDB_API_KEY env variable.
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-34256eb2e278> in <module>
----> 1 import textattack

/opt/conda/lib/python3.7/site-packages/textattack/__init__.py in <module>
----> 1 from . import (
      2     attack_recipes,
      3     attack_results,
      4     augmentation,
      5     commands,

/opt/conda/lib/python3.7/site-packages/textattack/attack_recipes/__init__.py in <module>
      1 from .attack_recipe import AttackRecipe
      2 
----> 3 from .bae_garg_2019 import BAEGarg2019
      4 from .bert_attack_li_2020 import BERTAttackLi2020
      5 from .genetic_algorithm_alzantot_2018 import GeneticAlgorithmAlzantot2018

/opt/conda/lib/python3.7/site-packages/textattack/attack_recipes/bae_garg_2019.py in <module>
----> 1 from textattack.constraints.grammaticality import PartOfSpeech
      2 from textattack.constraints.pre_transformation import (
      3     RepeatModification,
      4     StopwordModification,
      5 )

/opt/conda/lib/python3.7/site-packages/textattack/constraints/__init__.py in <module>
      2 from .constraint import Constraint
      3 
----> 4 from . import grammaticality
      5 from . import semantics
      6 from . import overlap

/opt/conda/lib/python3.7/site-packages/textattack/constraints/grammaticality/__init__.py in <module>
----> 1 from . import language_models
      2 
      3 from .language_tool import LanguageTool
      4 from .part_of_speech import PartOfSpeech

/opt/conda/lib/python3.7/site-packages/textattack/constraints/grammaticality/language_models/__init__.py in <module>
      1 from .language_model_constraint import LanguageModelConstraint
      2 
----> 3 from .google_language_model import Google1BillionWordsLanguageModel
      4 from .gpt2 import GPT2
      5 from .learning_to_write import LearningToWriteLanguageModel

/opt/conda/lib/python3.7/site-packages/textattack/constraints/grammaticality/language_models/google_language_model/__init__.py in <module>
----> 1 from .google_language_model import (
      2     GoogleLanguageModel as Google1BillionWordsLanguageModel,
      3 )

/opt/conda/lib/python3.7/site-packages/textattack/constraints/grammaticality/language_models/google_language_model/google_language_model.py in <module>
      4 
      5 from textattack.constraints import Constraint
----> 6 from textattack.transformations import WordSwap
      7 
      8 from .alzantot_goog_lm import GoogLMHelper

/opt/conda/lib/python3.7/site-packages/textattack/transformations/__init__.py in <module>
     14 from .word_swap_random_character_substitution import WordSwapRandomCharacterSubstitution
     15 from .word_swap_wordnet import WordSwapWordNet
---> 16 from .word_swap_masked_lm import WordSwapMaskedLM
     17 from .word_swap_random_word import RandomSwap
     18 from .random_synonym_insertion import RandomSynonymInsertion

/opt/conda/lib/python3.7/site-packages/textattack/transformations/word_swap_masked_lm.py in <module>
      2 
      3 import torch
----> 4 from transformers import AutoModelForMaskedLM, AutoTokenizer
      5 
      6 from textattack.shared import utils

ImportError: cannot import name 'AutoModelForMaskedLM' from 'transformers' (/opt/conda/lib/python3.7/site-packages/transformers/__init__.py)

Questions about new potential features

Hi, Thanks for the great work. It'd be great to have two new features for the package but I am not sure if it's in the roadmap.

The transformations and constraints based on word embeddings are using paragramcf embedding by default and it seems it's the only option, i wonder if it's possible for user to bring their own embedding?(maybe i can directly change the attributes related to embeddings but i guess this is probably not the expected way of using it)

Also for classification tasks, is it possible for user to define the cut-off probability for deciding labels, more generally the function f(softmax probs) --> labels?(currently it's taking whichever class has the highest prob).Let's say in a binary classification problem, i have a sample with true label 1 and predicted probability of 0.85, normally it's considered as correctly classified and will be skipped during the attack, but if i would like a 0.9 threshold then it's mislabelled.

Thank you!

Faster language model for `alzantot` recipe

The language model for the alzantot recipe is so slow. Running an attack on 1000 samples takes, literally, days. We should add the faster language model from this paper and only use the slow language model if we have to.

ValueError: Cannot perform GradientBasedWordSwap on model BertForSequenceClassification

Hi,

I am getting the following error when I try and run BERT-base for hotflip attack.

raise ValueError(f"Cannot perform GradientBasedWordSwap on model {model}.")
ValueError: Cannot perform GradientBasedWordSwap on model BertForSequenceClassification(

in File text_attack/textattack/shared/validators.py", line 88, in validate_model_gradient_word_swap_compatibility

The reason provided for this is
We can only take the gradient with respect to an individual word if the model uses a word-based tokenizer.

Why should this be true though? Can't we still replace the subwords (from BERT tokenizer) even if they might not make sense in some cases?

Thanks,

CompositeTransformation should print sub-transformations

the CompositeTransformation class aggregates the results of multiple transformations. however, it does not correctly print out its sub-transformations.

the DeepWordBug attack recipe contains a composite of four different transformations.

however, here's how the DeepWordBug attack prints now:

Attack(
  (search_method): GreedyWordSwapWIR(
    (wir_method):  unk
  )
  (goal_function):  UntargetedClassification
  (transformation):  CompositeTransformation
  (constraints): 
    (0): LevenshteinEditDistance(
        (max_edit_distance):  30
      )
    (1): RepeatModification
    (2): StopwordModification
  (is_black_box):  True
)

the transformation just prints CompositeTransformation, with no details. Instead, it should print the list of all of its sub-transformations, and recursively, their parameters (like how the Attack prints a list of its constraints).

Running error when using HuggingFaceModelWrapper and PWWS attacker

I met error when runing the PWWS Attack on huggingface model. My code is showed below:

custom_dataset = [
    ('Malaria deaths in Africa fall by 5% from last year', 0),
    ('Washington Nationals defeat the Houston Astros to win the World Series', 1),
    ('Exxon Mobil hires a new CEO', 0),
    ('Microsoft invests $1 billion in OpenAI', 1),
]
model = AutoModelForSequenceClassification.from_pretrained(model_path)
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)

model_wrapper = HuggingFaceModelWrapper(model, tokenizer, batch_size=100)
attacker = PWWSRen2019.build(model_wrapper)
results_iterable = attacker.attack_dataset(custom_dataset)

logger = CSVLogger(color_method='html')
num_successes = 0
for result in results_iterable:
    if isinstance(result, SuccessfulAttackResult):
        logger.log_attack_result(result)
        num_successes += 1
        print(f'{num_successes} of 10 successes complete.')

And the error is:

Traceback (most recent call last):
  File "/root/CLUE/utils/textattack_test.py", line 36, in <module>
    for result in results_iterable:
  File "/root/miniconda/lib/python3.7/site-packages/textattack/shared/attack.py", line 266, in attack_dataset
    for goal_function_result in examples:
  File "/root/miniconda/lib/python3.7/site-packages/textattack/shared/attack.py", line 251, in _get_examples_from_dataset
    attacked_text, ground_truth_output
  File "/root/miniconda/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py", line 55, in init_attack_example
    result, _ = self.get_result(attacked_text, check_skip=True)
  File "/root/miniconda/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py", line 66, in get_result
    results, search_over = self.get_results([attacked_text], **kwargs)
  File "/root/miniconda/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py", line 83, in get_results
    model_outputs = self._call_model(attacked_text_list)
  File "/root/miniconda/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py", line 180, in _call_model
    outputs = self._call_model_uncached(uncached_list)
  File "/root/miniconda/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py", line 149, in _call_model_uncached
    outputs = self.model(inputs)
  File "/root/miniconda/lib/python3.7/site-packages/textattack/models/wrappers/huggingface_model_wrapper.py", line 44, in __call__
    model_predict, ids, batch_size=self.batch_size
  File "/root/miniconda/lib/python3.7/site-packages/textattack/shared/utils/tensor.py", line 14, in batch_model_predict
    batch_preds = model_predict(batch)
  File "/root/miniconda/lib/python3.7/site-packages/textattack/models/wrappers/huggingface_model_wrapper.py", line 24, in model_predict
    input_dict = {k: [_dict[k] for _dict in inputs] for k in inputs[0]}
  File "/root/miniconda/lib/python3.7/site-packages/textattack/models/wrappers/huggingface_model_wrapper.py", line 24, in <dictcomp>
    input_dict = {k: [_dict[k] for _dict in inputs] for k in inputs[0]}
  File "/root/miniconda/lib/python3.7/site-packages/textattack/models/wrappers/huggingface_model_wrapper.py", line 24, in <listcomp>
    input_dict = {k: [_dict[k] for _dict in inputs] for k in inputs[0]}
IndexError: list index out of range

Mitigations for the attacks

Are there any mitigations available for NLP models to defend against these attacks or make the model robust.I am thinking Adversarial training can be one.

WNLI training bug

textattack (or transformers?) has a tokenizers bug that prevents WNLI models from training:

$ textattack train --model distilbert-base-uncased --dataset glue:wnli --batch-size 128 --epochs 5 --max-length 256 --learning-rate 3e-05

...

Traceback (most recent call last):
  File "/u/jm8wx/.conda/envs/torch/bin/textattack", line 33, in <module>
    sys.exit(load_entry_point('textattack', 'console_scripts', 'textattack')())
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/textattack_cli.py", line 41, in main
    args.func.run(args)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/train_model/train_model_command.py", line 30, in run
    train_model(args)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/train_model/run_training.py", line 108, in train_model
    train_text_ids = batch_encode(tokenizer, train_text)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/train_model/run_training.py", line 28, in batch_encode
    return tokenizer.batch_encode(text_list)
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/models/tokenizers/auto_tokenizer.py", line 57, in batch_encode
    encodings = self.tokenizer.batch_encode_plus(
  File "/u/jm8wx/.conda/envs/torch/lib/python3.8/site-packages/transformers/tokenization_utils.py", line 2451, in batch_encode_plus
    raise ValueError(
ValueError: batch_text_or_text_pairs has to be a list (got <class 'tuple'>)
> textattack train --model distilbert-base-uncased --dataset glue:wnli --batch-size 64 --epochs 5 --max-length 256 --learning-rate 5e-05

Add MORPHEUS as an attack

An attack that we haven't yet implemented but fits well in our framework is "It’s Morphin’ Time!
Combating Linguistic Discrimination with Inflectional Perturbations" [https://arxiv.org/pdf/2005.04364.pdf].

The search method is our GreedyWordSwap search. We need to add the transformation and goal function.

I think the attack can be implemented by adding the following:

  1. Transformation: We can add the inflection-based transformation by adding lemminflect to requirements.txt and creating a transformation similarly to this portion of their code: https://github.com/salesforce/morpheus/blob/master/morpheus/morpheus_base.py

  2. Goal function: the greedy goal of this attack is to minimize the BLEU score of the ground-truth and after-attack output of the model. We can add BLEU score as a text2text goal function with the following thresholds:

Only adversarial examples that degraded the F1 score by 50 and the BLEU score by >15 were considered.

(Once we add a question-answering model & dataset, we can implement a goal function based on F1 score.)

Finally, we'd also need to add a recipe file that utilizes the transformation, goal function, and our GreedyWordSwap search. I don't think this attack has any constraints, though I may have missed them in the paper.

Diagnose CPU vs. GPU precision issues

attacks on CPU and GPU produce different query numbers

attack on CPU: CUDA_VISIBLE_DEVICES="" textattack attack --model lstm-mr --recipe faster-alzantot --num-examples 3 --num-examples-offset 20

+-------------------------------+--------+
| Attack Results                |        |
+-------------------------------+--------+
| Number of successful attacks: | 2      |
| Number of failed attacks:     | 1      |
| Number of skipped attacks:    | 0      |
| Original accuracy:            | 100.0% |
| Accuracy under attack:        | 33.33% |
| Attack success rate:          | 66.67% |
| Average perturbed word %:     | 8.58%  |
| Average num. words per input: | 25.67  |
| Avg num queries:              | 878.33 |
+-------------------------------+--------+

attack on GPU: CUDA_VISIBLE_DEVICES=0 textattack attack --model lstm-mr --recipe faster-alzantot --num-examples 3 --num-examples-offset 20

+-------------------------------+--------+
| Attack Results                |        |
+-------------------------------+--------+
| Number of successful attacks: | 2      |
| Number of failed attacks:     | 1      |
| Number of skipped attacks:    | 0      |
| Original accuracy:            | 100.0% |
| Accuracy under attack:        | 33.33% |
| Attack success rate:          | 66.67% |
| Average perturbed word %:     | 8.58%  |
| Average num. words per input: | 25.67  |
| Avg num queries:              | 859.0  |
+-------------------------------+--------+

UnboundLocalError: local variable 'output' referenced before assignment

Hi,
thank you for the great codebase. It is really helpful.

I attempted to attack a sample dataset of my own on a BERT model. I ran the following line:
!textattack attack --model-from-huggingface bert-base-uncased --recipe textfooler --dataset-from-file att_dataset.py --num-examples 2
on Google Colab. this is the error trace I got.

Traceback (most recent call last):
File "/usr/local/bin/textattack", line 8, in
sys.exit(main())
File "/usr/local/lib/python3.6/dist-packages/textattack/commands/textattack_cli.py", line 41, in main
args.func.run(args)
File "/usr/local/lib/python3.6/dist-packages/textattack/commands/attack/attack_command.py", line 32, in run
run_single_threaded(args)
File "/usr/local/lib/python3.6/dist-packages/textattack/commands/attack/run_attack_single_threaded.py", line 97, in run
attack_log_manager.log_result(result)
File "/usr/local/lib/python3.6/dist-packages/textattack/loggers/attack_log_manager.py", line 35, in log_result
logger.log_attack_result(result)
File "/usr/local/lib/python3.6/dist-packages/textattack/loggers/file_logger.py", line 45, in log_attack_result
self.fout.write(result.str(color_method=color_method))
File "/usr/local/lib/python3.6/dist-packages/textattack/attack_results/attack_result.py", line 61, in str
return "\n\n".join(self.str_lines(color_method=color_method))
File "/usr/local/lib/python3.6/dist-packages/textattack/attack_results/attack_result.py", line 56, in str_lines
lines = [self.goal_function_result_str(color_method=color_method)]
File "/usr/local/lib/python3.6/dist-packages/textattack/attack_results/attack_result.py", line 67, in goal_function_result_str
orig_colored = self.original_result.get_colored_output(color_method)
File "/usr/local/lib/python3.6/dist-packages/textattack/goal_function_results/classification_goal_function_result.py", line 50, in get_colored_output
output, color = self._processed_output
File "/usr/local/lib/python3.6/dist-packages/textattack/goal_function_results/classification_goal_function_result.py", line 26, in _processed_output
return output, color
UnboundLocalError: local variable 'output' referenced before assignment

att_dataset.py contains only one line
dataset = [("This is the worst feeling ever",1), ("It's not too bad I feel alright I guess",0)]

Am I doing something wrong? Thanks in advance

No effect of increased batch size.

Hi,

I have tried increasing the batch_size for textattack attack command with the argument --model-batch-size, but this does not affect anything - neither the GPU used memory increases, and neither does the total speed of attack on the dataset increase.

Have you noticed anything similar?
Thanks,

Multilanguage attack

Hi, I'm surprised by the capabilities of this library and I would like to know if it can handle the generation or test attacks for other languages, e.g. Spanish, German, Japanese. In case it is currently not allowed, what would be your advise to overcome this issue.

Thanks

Make some imports optional

I want to use TextAttack but I don't want to install tensorflow or spacy. I should be able to install TA without those modules and then be prompted to install them if I want to use the spacy tokenizer or tensorflow features.

Goal function should print maximization

the input reduction recipe prints the following:

Attack(
  (search_method): GreedyWordSwapWIR(
    (wir_method):  delete
  )
  (goal_function):  InputReduction(
    (target_num_words):  1
  )
  (transformation):  WordDeletion
  (constraints): 
    (0): RepeatModification
    (1): StopwordModification
  (is_black_box):  True
)

however, it should be clear from the print message that the InputReduction goal function is to be maximized (not achieved)

model-from-file bug when absolute windows path provided.

If an absolute windows path (e.g. C:\test.py) is passed to --model-from-file the following exception is raised:

# attack_args_helpers.py
        if ":" in args.model_from_file:
            model_file, model_name, tokenizer_name = args.model_from_file.split(":")

>>> ValueError: not enough values to unpack (expected 3, got 2)

Not sure what the reasoning of the check on : is but perhaps rather use pathlib.Path to check if valid path instead?

'Run in Colab' buttons not rendered on readthedocs

For some reason, the .ipynb HTML is formatted differently when displayed on Github/jupyter/jupyternb and readthedocs. The bit of HTML in each notebook that's intended to show buttons (like 'Run in Colab') doesn't render properly. We probably just need to fix the indentation, or maybe some setting in nbsphinx.


Screenshot of how it looks on github (good):
Screen Shot 2020-08-12 at 5 09 51 PM


Screenshot of how it looks on readthedocs (bad):
Screen Shot 2020-08-12 at 5 09 55 PM

Is this package working with PyTorch?

I am trying to pip install this package on my ec2 instance, PyTorch with Python 3 environment, but I have below error. I am wondering if this package only works with Tensorflow?

(pytorch_p36) [ec2-user@ip-172-31-7-73 ~]$ pip install textattack
Collecting textattack
  Downloading https://files.pythonhosted.org/packages/36/75/e3ed728d84b8a3599efa392b0503da693a1265521af83bac47741962fef6/textattack-0.1.5-py3-none-any.whl (180kB)
    100% |β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 184kB 15.1MB/s 
Collecting tensorflow>=2 (from textattack)
  Could not find a version that satisfies the requirement tensorflow>=2 (from textattack) (from versions: 0.12.1, 1.0.0, 1.0.1, 1.1.0rc0, 1.1.0rc1, 1.1.0rc2, 1.1.0, 1.2.0rc0, 1.2.0rc1, 1.2.0rc2, 1.2.0, 1.2.1, 1.3.0rc0, 1.3.0rc1, 1.3.0rc2, 1.3.0, 1.4.0rc0, 1.4.0rc1, 1.4.0, 1.4.1, 1.5.0rc0, 1.5.0rc1, 1.5.0, 1.5.1, 1.6.0rc0, 1.6.0rc1, 1.6.0, 1.7.0rc0, 1.7.0rc1, 1.7.0, 1.7.1, 1.8.0rc0, 1.8.0rc1, 1.8.0, 1.9.0rc0, 1.9.0rc1, 1.9.0rc2, 1.9.0, 1.10.0rc0, 1.10.0rc1, 1.10.0, 1.10.1, 1.11.0rc0, 1.11.0rc1, 1.11.0rc2, 1.11.0, 1.12.0rc0, 1.12.0rc1, 1.12.0rc2, 1.12.0, 1.12.2, 1.12.3, 1.13.0rc0, 1.13.0rc1, 1.13.0rc2, 1.13.1, 1.13.2, 1.14.0rc0, 1.14.0rc1, 1.14.0, 2.0.0a0, 2.0.0b0, 2.0.0b1)
No matching distribution found for tensorflow>=2 (from textattack)
You are using pip version 10.0.1, however version 20.2b1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

Finding that the attack.attack_dataset() function gives error "a model 'list' object has no attribute 'size'"

Hi! Just getting started off using this package. I'm hoping you can help me with this error message.

I'm trying to use an attack recipe but I can't seem to do a successful attack. I use PWWSRen2019 below, but I found FasterGeneticAlgorithmJia2019 also fails.

Here is my script and the error message. I am using version 0.2.3.

from textattack.datasets import HuggingFaceNlpDataset
from transformers import AutoModelForSequenceClassification
from textattack.models.tokenizers import AutoTokenizer
from textattack.attack_recipes import PWWSRen2019, FasterGeneticAlgorithmJia2019


dataset = HuggingFaceNlpDataset("imdb", None, "train")
pth = "textattack/bert-base-uncased-imdb"
model = AutoModelForSequenceClassification.from_pretrained(pth)
model.tokenizer = AutoTokenizer(pth)
attack = PWWSRen2019(model)

results_iterable = attack.attack_dataset(dataset)
next(results_iterable)  # error

The error trace:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-14-bf466712a9a4> in <module>
      9 num_successes = 0
     10 while num_successes < 10:
---> 11     result = next(results_iterable)
     12     if isinstance(result, SuccessfulAttackResult):
     13         logger.log_attack_result(result)

~/anaconda3/lib/python3.7/site-packages/textattack/shared/attack.py in attack_dataset(self, dataset, indices)
    270         examples = self._get_examples_from_dataset(dataset, indices=indices)
    271 
--> 272         for goal_function_result in examples:
    273             if goal_function_result.goal_status == GoalFunctionResultStatus.SKIPPED:
    274                 yield SkippedAttackResult(goal_function_result)

~/anaconda3/lib/python3.7/site-packages/textattack/shared/attack.py in _get_examples_from_dataset(self, dataset, indices)
    249                 )
    250                 goal_function_result, _ = self.goal_function.init_attack_example(
--> 251                     attacked_text, ground_truth_output
    252                 )
    253                 yield goal_function_result

~/anaconda3/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py in init_attack_example(self, attacked_text, ground_truth_output)
     53         self.ground_truth_output = ground_truth_output
     54         self.num_queries = 0
---> 55         result, _ = self.get_result(attacked_text, check_skip=True)
     56         return result, _
     57 

~/anaconda3/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py in get_result(self, attacked_text, **kwargs)
     64         """A helper method that queries ``self.get_results`` with a single
     65         ``AttackedText`` object."""
---> 66         results, search_over = self.get_results([attacked_text], **kwargs)
     67         result = results[0] if len(results) else None
     68         return result, search_over

~/anaconda3/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py in get_results(self, attacked_text_list, check_skip)
     81             attacked_text_list = attacked_text_list[:queries_left]
     82         self.num_queries += len(attacked_text_list)
---> 83         model_outputs = self._call_model(attacked_text_list)
     84         for attacked_text, raw_output in zip(attacked_text_list, model_outputs):
     85             displayed_output = self._get_displayed_output(raw_output)

~/anaconda3/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py in _call_model(self, attacked_text_list)
    178                 if text not in self._call_model_cache
    179             ]
--> 180             outputs = self._call_model_uncached(uncached_list)
    181             for text, output in zip(uncached_list, outputs):
    182                 self._call_model_cache[text] = output

~/anaconda3/lib/python3.7/site-packages/textattack/goal_functions/goal_function.py in _call_model_uncached(self, attacked_text_list)
    147         inputs = [at.tokenizer_input for at in attacked_text_list]
    148 
--> 149         outputs = self.model(inputs)
    150 
    151         assert len(inputs) == len(

~/anaconda3/lib/python3.7/site-packages/torch/nn/modules/module.py in __call__(self, *input, **kwargs)
    548             result = self._slow_forward(*input, **kwargs)
    549         else:
--> 550             result = self.forward(*input, **kwargs)
    551         for hook in self._forward_hooks.values():
    552             hook_result = hook(self, input, result)

~/anaconda3/lib/python3.7/site-packages/transformers/modeling_bert.py in forward(self, input_ids, attention_mask, token_type_ids, position_ids, head_mask, inputs_embeds, labels, output_attentions, output_hidden_states)
   1265             inputs_embeds=inputs_embeds,
   1266             output_attentions=output_attentions,
-> 1267             output_hidden_states=output_hidden_states,
   1268         )
   1269 

~/anaconda3/lib/python3.7/site-packages/torch/nn/modules/module.py in __call__(self, *input, **kwargs)
    548             result = self._slow_forward(*input, **kwargs)
    549         else:
--> 550             result = self.forward(*input, **kwargs)
    551         for hook in self._forward_hooks.values():
    552             hook_result = hook(self, input, result)

~/anaconda3/lib/python3.7/site-packages/transformers/modeling_bert.py in forward(self, input_ids, attention_mask, token_type_ids, position_ids, head_mask, inputs_embeds, encoder_hidden_states, encoder_attention_mask, output_attentions, output_hidden_states)
    715             raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
    716         elif input_ids is not None:
--> 717             input_shape = input_ids.size()
    718         elif inputs_embeds is not None:
    719             input_shape = inputs_embeds.size()[:-1]

AttributeError: 'list' object has no attribute 'size'
---------------------------------------------------------

I can run list(results_iterable) and I get the empty list [], not sure if that is relevant or not.

Printing to file should be optional

Every time I run an attack via python -m textattack ..., I see that textattack prints everything to a file in outputs/. Sometimes I just want to see the ouput in stdout and forget about it forever. Printing to file should be optional.

Also, the command-line should log the args to a file, too (at least optionally).

add BERT-based augmenter

We should allow data augmentation using masked-token prediction models through WordSwapMaskedLM. This would leverage the power of transformers like BERT to generate augmented inputs.

Make W&B quiet

Currently, when you run an attack via python -m textattack ..., Weights & Biases pops up and asks you to log in. This should only happen if you've specified the --enable-wandb option and added a Weights & Biases logger.

I think this is happening because we import a library that imports wandb (maybe transformers?) and that import is causing the message. We should silence the wandb logger in the run_attack* scripts.

Option for perturbing specific parts of a multi-sequence input

Right now, models that take multiple sequences as input (like entailment) have them all joined in a TokenizedText and perturbed. Some attacks may wish to perturb specific parts of that input -- for example, just the premise for entailment. That should be an option.

Not able to load my own model

I want to attack my own trained model. As such I used textattack attack --model-from-file my_model.py --recipe bert-attack --num-examples 10. I am getting NameError: name 'load_model' is not defined. Kindly help.

transformers 3.x

transformers and tokenizers have a lovely new update that's broken our tests on master

specifically, anyone who installs right now won't be able to use the GloveTokenizer -- which means the LSTM and CNN are both broken! :(

we need to:

  • fix the error to work with the latest version of tokenizers and transformers
  • set a specific version # so that we can avoid this in the future

Files automatically cached in home folder

TextAttack automatically downloads files to ~/.cache/, which isn't ideal for every user. We should ask for the best cache_dir on the _post_install_hook and use that, instead of just defaulting to ~/.cache/.

Remove `ids` from `TokenizedText`

It seems like the only use of ids for TokenizedText object is for using it when calling the victim model in goal_function. This leads to unnecessary tokenization operations when we're generating TokenizedText from transformations since some of them will get filtered out by the constraints and won't be passed to the victim model.

It also adds confusion as to what the model ids are for for newcomers. For instance, they might wonder if it's for the victim model or one of the language models. I think we should remove the id tokenization from TokenizedText and have goal_function tokenize the text.

Update the example

https://github.com/QData/TextAttack/blob/master/docs/examples/1_Introduction_and_Transformations.ipynb

please update the above example. Your repo is updated but not example. I am summerising what beginners actually want .

actual sentence = "list of sentences"
attack= some_attack_name
//Perform Targeted or Un Targeted attacks
sentence_after attack ="list of attacked sentences"

Now we want dataframe or csv or variable which will return me
actual_sentence attacked sentence score
***Note :- It will work for list of sentences (edited)

If maintain above structure then beginner will easily understand . It's like IBM art black box examples
Please add any End-End example for Targeted and Un-targeted attacks . It would be helpfull

Confusing error message when a model isn't supplied

Output when I specify an attack without a model:

(torch) jxmorris12 12:50 PM > textattack attack --recipe bae
Traceback (most recent call last):
...
  File "/p/qdata/jm8wx/research/text_attacks/textattack/textattack/commands/attack/attack_args_helpers.py", line 343, in parse_model_from_args
    raise ValueError(f"Error: unsupported TextAttack model {args.model}")
ValueError: Error: unsupported TextAttack model None

I think that throwing an error (rather than providing a model by default) is the correct behavior. But this error message is unclear. We should check if any --model* args are supplied, and, if not, throw an error that says "please supply a model" or something like that!

AttributeError: module 'textattack' has no attribute 'shared'

I tried to run this line

from textattack.augmentation import EmbeddingAugmenter

and got the error AttributeError: module 'textattack' has no attribute 'shared'

textattack: Downloading https://textattack.s3.amazonaws.com/config.yaml.
100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 76.0/76.0 [00:00<00:00, 25.0kB/s]
textattack: Copying /root/.cache/textattack/tmpb4gqlzdx.zip to /root/.cache/textattack/config.yaml.
textattack: Successfully saved config.yaml to cache.
textattack: First time importing textattack: downloading remaining required packages.
textattack: Downloading spaCy required packages.
textattack: Downloading NLTK required packages.
βœ” Download and installation successful
You can now load the model via spacy.load('en_core_web_sm')
βœ” Linking successful
/usr/local/lib/python3.6/dist-packages/en_core_web_sm -->
/usr/local/lib/python3.6/dist-packages/spacy/data/en
You can now load the model via spacy.load('en')
[nltk_data] Downloading package wordnet to /root/nltk_data...
[nltk_data] Unzipping corpora/wordnet.zip.
[nltk_data] Downloading package averaged_perceptron_tagger to
[nltk_data] /root/nltk_data...
[nltk_data] Unzipping taggers/averaged_perceptron_tagger.zip.
[nltk_data] Downloading package universal_tagset to /root/nltk_data...
[nltk_data] Unzipping taggers/universal_tagset.zip.
[nltk_data] Downloading package stopwords to /root/nltk_data...
[nltk_data] Unzipping corpora/stopwords.zip.
wandb: WARNING W&B installed but not logged in. Run wandb login or set the WANDB_API_KEY env variable.

AttributeError Traceback (most recent call last)
in ()
----> 1 from textattack.augmentation import EmbeddingAugmenter

9 frames
/usr/local/lib/python3.6/dist-packages/textattack/constraints/semantics/sentence_encoders/bert/bert.py in ()
2
3 from textattack.constraints.semantics.sentence_encoders import SentenceEncoder
----> 4 import textattack.shared.utils as utils
5
6 class BERT(SentenceEncoder):

AttributeError: module 'textattack' has no attribute 'shared'

Issue with Gradient-based Word Swap

Adding

if not self.model.emb_layer.embedding.weight.requires_grad:
            self.model.emb_layer.embedding.weight.requires_grad = True

to end of __init__ of WordSwapGradientBased causes output for hotflip attacks to differ in test.

Not sure why this happens.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.