First BERT Experiments

In this notebook we do some first experiments with BERT: we finetune a BERT model+classifier on each of our datasets separately and compute the accuracy of the resulting classifier on the test data.

For these experiments we use the pytorch_transformers package. It contains a variety of neural network architectures for transfer learning and pretrained models, including BERT and XLNET.

Two different BERT models are relevant for our experiments:

  • BERT-base-uncased: a relatively small BERT model that should already give reasonable results,
  • BERT-large-uncased: a larger model for real state-of-the-art results.

In [1]:
import torch

from pytorch_transformers.tokenization_bert import BertTokenizer
from pytorch_transformers.modeling_bert import BertForSequenceClassification

BERT_MODEL = 'bert-base-uncased'
BATCH_SIZE = 16 if "base" in BERT_MODEL else 2
GRADIENT_ACCUMULATION_STEPS = 1 if "base" in BERT_MODEL else 8

tokenizer = BertTokenizer.from_pretrained(BERT_MODEL)

Data

We use the same data as for all our previous experiments. Here we load the training, development and test data for a particular prompt.


In [2]:
import ndjson
import glob

prefix = "junkfood_but"
train_file = f"../data/interim/{prefix}_train_withprompt_diverse200.ndjson"
synth_files = glob.glob(f"../data/interim/{prefix}_train_withprompt_*.ndjson")
dev_file = f"../data/interim/{prefix}_dev_withprompt.ndjson"
test_file = f"../data/interim/{prefix}_test_withprompt.ndjson"

with open(train_file) as i:
    train_data = ndjson.load(i)

synth_data = []
for f in synth_files:
    if "allsynth" in f:
        continue
    with open(f) as i:
        synth_data += ndjson.load(i)
    
with open(dev_file) as i:
    dev_data = ndjson.load(i)
    
with open(test_file) as i:
    test_data = ndjson.load(i)

Next, we build the label vocabulary, which maps every label in the training data to an index.


In [3]:
label2idx = {}
idx2label = {}
target_names = []
for item in train_data:
    if item["label"] not in label2idx:
        target_names.append(item["label"])
        idx = len(label2idx)
        label2idx[item["label"]] = idx
        idx2label[idx] = item["label"]
    
print(label2idx)
print(idx2label)


{'Schools providing healthy alternatives': 0, 'Students without choice': 1, 'Schools generate money': 2, 'Student choice': 3, 'Students can still bring/access junk food': 4, 'Unclassified Off-Topic': 5, 'School without generating money': 6}
{0: 'Schools providing healthy alternatives', 1: 'Students without choice', 2: 'Schools generate money', 3: 'Student choice', 4: 'Students can still bring/access junk food', 5: 'Unclassified Off-Topic', 6: 'School without generating money'}

In [4]:
import random

def sample(train_data, synth_data, label2idx, number):
    """Sample a fixed number of items from every label from
    the training data and test data.
    """
    new_train_data = []
    for label in label2idx:
        data_for_label = [i for i in train_data if i["label"] == label]
        
        # If there is more training data than the required number,
        # take a random sample of n examples from the training data.
        if len(data_for_label) >= number:
            random.shuffle(data_for_label)
            new_train_data += data_for_label[:number]
            
        # If there is less training data than the required number,
        # combine training data with synthetic data.
        elif len(data_for_label) < number:
            
            # Automatically add all training data
            new_train_data += data_for_label
            
            # Compute the required number of additional data
            rest = number-len(data_for_label)
            
            # Collect the synthetic data for the label
            synth_data_for_label = [i for i in synth_data if i["label"] == label]
            
            # If there is more synthetic data than required, 
            # take a random sample from the synthetic data.
            if len(synth_data_for_label) > rest:
                random.shuffle(synth_data_for_label)
                new_train_data += synth_data_for_label[:rest]
            # If there is less synthetic data than required,
            # sample with replacement from this data until we have
            # the required number.
            else:
                new_train_data += random.choices(synth_data_for_label, k=rest)
        
    return new_train_data


def random_sample(train_data, train_size):
    random.shuffle(train_data)
    train_data = train_data[:train_size]
    return train_data

#train_data = train_data + synth_data
#train_data = sample(train_data, synth_data, label2idx, 200)
#train_data = random_sample(train_data, 200)
print("Train data size:", len(train_data))


Train data size: 200

Model

We load the pretrained model and put it on a GPU if one is available. We also put the model in "training" mode, so that we can correctly update its internal parameters on the basis of our data sets.


In [5]:
model = BertForSequenceClassification.from_pretrained(BERT_MODEL, num_labels=len(label2idx))

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
model.train()


Out[5]:
BertForSequenceClassification(
  (bert): BertModel(
    (embeddings): BertEmbeddings(
      (word_embeddings): Embedding(30522, 768, padding_idx=0)
      (position_embeddings): Embedding(512, 768)
      (token_type_embeddings): Embedding(2, 768)
      (LayerNorm): BertLayerNorm()
      (dropout): Dropout(p=0.1)
    )
    (encoder): BertEncoder(
      (layer): ModuleList(
        (0): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (1): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (2): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (3): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (4): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (5): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (6): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (7): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (8): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (9): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (10): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
        (11): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): BertLayerNorm()
              (dropout): Dropout(p=0.1)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): BertLayerNorm()
            (dropout): Dropout(p=0.1)
          )
        )
      )
    )
    (pooler): BertPooler(
      (dense): Linear(in_features=768, out_features=768, bias=True)
      (activation): Tanh()
    )
  )
  (dropout): Dropout(p=0.1)
  (classifier): Linear(in_features=768, out_features=7, bias=True)
)

Preprocessing

We preprocess the data by turning every example to an InputFeatures item. This item has all the attributes we need for finetuning BERT:

  • input ids: the ids of the tokens in the text
  • input mask: tells BERT what part of the input it should not look at (such as padding tokens)
  • segment ids: tells BERT what segment every token belongs to. BERT can take two different segments as input
  • label id: the id of this item's label

In [6]:
import logging
import numpy as np

logging.basicConfig(format = '%(asctime)s - %(levelname)s - %(name)s -   %(message)s',
                    datefmt = '%m/%d/%Y %H:%M:%S',
                    level = logging.INFO)
logger = logging.getLogger(__name__)

MAX_SEQ_LENGTH=100

class InputFeatures(object):
    """A single set of features of data."""

    def __init__(self, input_ids, input_mask, segment_ids, label_id):
        self.input_ids = input_ids
        self.input_mask = input_mask
        self.segment_ids = segment_ids
        self.label_id = label_id
        

def convert_examples_to_features(examples, label2idx, max_seq_length, tokenizer, verbose=0):
    """Loads a data file into a list of `InputBatch`s."""
    
    features = []
    for (ex_index, ex) in enumerate(examples):
        
        # TODO: should deal better with sentences > max tok length
        input_ids = tokenizer.encode("[CLS] " + ex["text"] + " [SEP]")
        segment_ids = [0] * len(input_ids)
            
        # The mask has 1 for real tokens and 0 for padding tokens. Only real
        # tokens are attended to.
        input_mask = [1] * len(input_ids)

        # Zero-pad up to the sequence length.
        padding = [0] * (max_seq_length - len(input_ids))
        input_ids += padding
        input_mask += padding
        segment_ids += padding

        assert len(input_ids) == max_seq_length
        assert len(input_mask) == max_seq_length
        assert len(segment_ids) == max_seq_length

        label_id = label2idx[ex["label"]]
        if verbose and ex_index == 0:
            logger.info("*** Example ***")
            logger.info("text: %s" % ex["text"])
            logger.info("input_ids: %s" % " ".join([str(x) for x in input_ids]))
            logger.info("input_mask: %s" % " ".join([str(x) for x in input_mask]))
            logger.info("segment_ids: %s" % " ".join([str(x) for x in segment_ids]))
            logger.info("label:" + str(ex["label"]) + " id: " + str(label_id))

        features.append(
                InputFeatures(input_ids=input_ids,
                              input_mask=input_mask,
                              segment_ids=segment_ids,
                              label_id=label_id))
    return features

train_features = convert_examples_to_features(train_data, label2idx, MAX_SEQ_LENGTH, tokenizer, verbose=0)
dev_features = convert_examples_to_features(dev_data, label2idx, MAX_SEQ_LENGTH, tokenizer)
test_features = convert_examples_to_features(test_data, label2idx, MAX_SEQ_LENGTH, tokenizer, verbose=1)


08/22/2019 13:54:23 - INFO - __main__ -   *** Example ***
08/22/2019 13:54:23 - INFO - __main__ -   text: Schools should not allow junk food to be sold on campus but kids will still bring in unhealthy food
08/22/2019 13:54:23 - INFO - __main__ -   input_ids: 101 2816 2323 2025 3499 18015 2833 2000 2022 2853 2006 3721 2021 4268 2097 2145 3288 1999 4895 20192 24658 2100 2833 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
08/22/2019 13:54:23 - INFO - __main__ -   input_mask: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
08/22/2019 13:54:23 - INFO - __main__ -   segment_ids: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
08/22/2019 13:54:23 - INFO - __main__ -   label:Students without choice id: 1

Next, we initialize data loaders for each of our data sets. These data loaders present the data for training (for example, by grouping them into batches).


In [7]:
import torch
from torch.utils.data import TensorDataset, DataLoader, RandomSampler

def get_data_loader(features, max_seq_length, batch_size, shuffle=True): 

    all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long)
    all_input_mask = torch.tensor([f.input_mask for f in features], dtype=torch.long)
    all_segment_ids = torch.tensor([f.segment_ids for f in features], dtype=torch.long)
    all_label_ids = torch.tensor([f.label_id for f in features], dtype=torch.long)
    data = TensorDataset(all_input_ids, all_input_mask, all_segment_ids, all_label_ids)

    dataloader = DataLoader(data, shuffle=shuffle, batch_size=batch_size)
    
    return dataloader

train_dataloader = get_data_loader(train_features, MAX_SEQ_LENGTH, BATCH_SIZE)
dev_dataloader = get_data_loader(dev_features, MAX_SEQ_LENGTH, BATCH_SIZE)
test_dataloader = get_data_loader(test_features, MAX_SEQ_LENGTH, BATCH_SIZE, shuffle=False)

Evaluation

Our evaluation method takes a pretrained model and a dataloader. It has the model predict the labels for the items in the data loader, and returns the loss, the correct labels, and the predicted labels.


In [8]:
def evaluate(model, dataloader, verbose=False):

    eval_loss = 0
    nb_eval_steps = 0
    predicted_labels, correct_labels = [], []

    for step, batch in enumerate(tqdm(dataloader, desc="Evaluation iteration")):
        batch = tuple(t.to(device) for t in batch)
        input_ids, input_mask, segment_ids, label_ids = batch

        with torch.no_grad():
            tmp_eval_loss, logits = model(input_ids, segment_ids, input_mask, label_ids)

        outputs = np.argmax(logits.to('cpu'), axis=1)
        label_ids = label_ids.to('cpu').numpy()
        
        predicted_labels += list(outputs)
        correct_labels += list(label_ids)
                    
        eval_loss += tmp_eval_loss.mean().item()
        nb_eval_steps += 1

    eval_loss = eval_loss / nb_eval_steps
    
    correct_labels = np.array(correct_labels)
    predicted_labels = np.array(predicted_labels)
        
    return eval_loss, correct_labels, predicted_labels

Training

Let's prepare the training. We set the training parameters and choose an optimizer and learning rate scheduler.


In [9]:
from pytorch_transformers.optimization import AdamW, WarmupLinearSchedule

NUM_TRAIN_EPOCHS = 20
LEARNING_RATE = 1e-5
WARMUP_PROPORTION = 0.1

def warmup_linear(x, warmup=0.002):
    if x < warmup:
        return x/warmup
    return 1.0 - x

num_train_steps = int(len(train_data) / BATCH_SIZE / GRADIENT_ACCUMULATION_STEPS * NUM_TRAIN_EPOCHS)

param_optimizer = list(model.named_parameters())
no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
    {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01},
    {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
    ]

optimizer = AdamW(optimizer_grouped_parameters, lr=LEARNING_RATE, correct_bias=False)
scheduler = WarmupLinearSchedule(optimizer, warmup_steps=100, t_total=num_train_steps)

Now we do the actual training. In each epoch, we present the model with all training data and compute the loss on the training set and the development set. We save the model whenever the development loss improves. We end training when we haven't seen an improvement of the development loss for a specific number of epochs (the patience).

Optionally, we use gradient accumulation to accumulate the gradient for several training steps. This is useful when we want to use a larger batch size than our current GPU allows us to do.


In [10]:
import os
from tqdm import trange
from tqdm import tqdm_notebook as tqdm
from sklearn.metrics import classification_report, precision_recall_fscore_support

OUTPUT_DIR = "/tmp/"
MODEL_FILE_NAME = "pytorch_model.bin"
PATIENCE = 5

global_step = 0
model.train()
loss_history = []
best_epoch = 0
for epoch in trange(int(NUM_TRAIN_EPOCHS), desc="Epoch"):
    tr_loss = 0
    nb_tr_examples, nb_tr_steps = 0, 0
    for step, batch in enumerate(tqdm(train_dataloader, desc="Training iteration")):
        batch = tuple(t.to(device) for t in batch)
        input_ids, input_mask, segment_ids, label_ids = batch
        outputs = model(input_ids, segment_ids, input_mask, label_ids)
        loss = outputs[0]
        
        if GRADIENT_ACCUMULATION_STEPS > 1:
            loss = loss / GRADIENT_ACCUMULATION_STEPS

        loss.backward()

        tr_loss += loss.item()
        nb_tr_examples += input_ids.size(0)
        nb_tr_steps += 1
        if (step + 1) % GRADIENT_ACCUMULATION_STEPS == 0:
            lr_this_step = LEARNING_RATE * warmup_linear(global_step/num_train_steps, WARMUP_PROPORTION)
            for param_group in optimizer.param_groups:
                param_group['lr'] = lr_this_step
            optimizer.step()
            optimizer.zero_grad()
            global_step += 1

    dev_loss, _, _ = evaluate(model, dev_dataloader)
    
    print("Loss history:", loss_history)
    print("Dev loss:", dev_loss)
    
    if len(loss_history) == 0 or dev_loss < min(loss_history):
        model_to_save = model.module if hasattr(model, 'module') else model
        output_model_file = os.path.join(OUTPUT_DIR, MODEL_FILE_NAME)
        torch.save(model_to_save.state_dict(), output_model_file)
        best_epoch = epoch
    
    if epoch-best_epoch >= PATIENCE: 
        print("No improvement on development set. Finish training.")
        break
        
    
    loss_history.append(dev_loss)


Epoch:   0%|          | 0/20 [00:00<?, ?it/s]

Loss history: []
Dev loss: 1.6989148616790772
Epoch:   5%|▌         | 1/20 [00:03<01:09,  3.65s/it]

Loss history: [1.6989148616790772]
Dev loss: 1.4327359676361084
Epoch:  10%|█         | 2/20 [00:07<01:04,  3.59s/it]

Loss history: [1.6989148616790772, 1.4327359676361084]
Dev loss: 1.1781285524368286
Epoch:  15%|█▌        | 3/20 [00:10<01:00,  3.54s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286]
Dev loss: 0.9835451960563659
Epoch:  20%|██        | 4/20 [00:13<00:56,  3.51s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659]
Dev loss: 0.8797368884086609
Epoch:  25%|██▌       | 5/20 [00:17<00:52,  3.49s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609]
Dev loss: 0.8484503865242005
Epoch:  30%|███       | 6/20 [00:20<00:48,  3.47s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005]
Dev loss: 0.694582176208496
Epoch:  35%|███▌      | 7/20 [00:24<00:45,  3.46s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496]
Dev loss: 0.5676077306270599
Epoch:  40%|████      | 8/20 [00:27<00:41,  3.46s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599]
Dev loss: 0.5432976514101029
Epoch:  45%|████▌     | 9/20 [00:31<00:38,  3.46s/it]

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029]
Dev loss: 0.5378826707601547
Epoch:  50%|█████     | 10/20 [00:34<00:34,  3.45s/it]

Epoch:  55%|█████▌    | 11/20 [00:37<00:29,  3.31s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547]
Dev loss: 0.5682449221611023

Epoch:  60%|██████    | 12/20 [00:40<00:25,  3.21s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023]
Dev loss: 0.5825568020343781

Epoch:  65%|██████▌   | 13/20 [00:43<00:21,  3.14s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781]
Dev loss: 0.5587857842445374

Epoch:  70%|███████   | 14/20 [00:46<00:18,  3.09s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374]
Dev loss: 0.5714442491531372

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374, 0.5714442491531372]
Dev loss: 0.5115815937519074
Epoch:  75%|███████▌  | 15/20 [00:49<00:16,  3.20s/it]

Epoch:  80%|████████  | 16/20 [00:52<00:12,  3.13s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374, 0.5714442491531372, 0.5115815937519074]
Dev loss: 0.5132488235831261

Epoch:  85%|████████▌ | 17/20 [00:55<00:09,  3.09s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374, 0.5714442491531372, 0.5115815937519074, 0.5132488235831261]
Dev loss: 0.6706602990627288

Epoch:  90%|█████████ | 18/20 [00:58<00:06,  3.06s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374, 0.5714442491531372, 0.5115815937519074, 0.5132488235831261, 0.6706602990627288]
Dev loss: 0.6222926080226898

Epoch:  95%|█████████▌| 19/20 [01:01<00:03,  3.03s/it]
Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374, 0.5714442491531372, 0.5115815937519074, 0.5132488235831261, 0.6706602990627288, 0.6222926080226898]
Dev loss: 0.5124190628528595

Loss history: [1.6989148616790772, 1.4327359676361084, 1.1781285524368286, 0.9835451960563659, 0.8797368884086609, 0.8484503865242005, 0.694582176208496, 0.5676077306270599, 0.5432976514101029, 0.5378826707601547, 0.5682449221611023, 0.5825568020343781, 0.5587857842445374, 0.5714442491531372, 0.5115815937519074, 0.5132488235831261, 0.6706602990627288, 0.6222926080226898, 0.5124190628528595]
Dev loss: 0.5342526018619538
No improvement on development set. Finish training.

Results

We load the pretrained model, set it to evaluation mode and compute its performance on the training, development and test set. We print out an evaluation report for the test set.

Note that different runs will give slightly different results.


In [11]:
print("Loading model from", output_model_file)
device="cpu"

model_state_dict = torch.load(output_model_file, map_location=lambda storage, loc: storage)
model = BertForSequenceClassification.from_pretrained(BERT_MODEL, state_dict=model_state_dict, num_labels=len(label2idx))
model.to(device)

model.eval()

#_, train_correct, train_predicted = evaluate(model, train_dataloader)
#_, dev_correct, dev_predicted = evaluate(model, dev_dataloader)
_, test_correct, test_predicted = evaluate(model, test_dataloader, verbose=True)

#print("Training performance:", precision_recall_fscore_support(train_correct, train_predicted, average="micro"))
#print("Development performance:", precision_recall_fscore_support(dev_correct, dev_predicted, average="micro"))
print("Test performance:", precision_recall_fscore_support(test_correct, test_predicted, average="micro"))

print(classification_report(test_correct, test_predicted, target_names=target_names))


Loading model from /tmp/pytorch_model.bin
08/22/2019 13:55:29 - INFO - pytorch_transformers.modeling_utils -   loading configuration file https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-config.json from cache at /home/yves/.cache/torch/pytorch_transformers/4dad0251492946e18ac39290fcfe91b89d370fee250efe9521476438fe8ca185.bf3b9ea126d8c0001ee8a1e8b92229871d06d36d8808208cc2449280da87785c
08/22/2019 13:55:29 - INFO - pytorch_transformers.modeling_utils -   Model config {
  "attention_probs_dropout_prob": 0.1,
  "finetuning_task": null,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  "num_labels": 7,
  "output_attentions": false,
  "output_hidden_states": false,
  "torchscript": false,
  "type_vocab_size": 2,
  "vocab_size": 30522
}

08/22/2019 13:55:30 - INFO - pytorch_transformers.modeling_utils -   loading weights file https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-pytorch_model.bin from cache at /home/yves/.cache/torch/pytorch_transformers/aa1ef1aede4482d0dbcd4d52baad8ae300e60902e88fcb0bebdec09afd232066.36ca03ab34a1a5d5fa7bc3d03d55c4fa650fed07220e2eeebc06ce58d0e9a157
Test performance: (0.7581699346405228, 0.7581699346405228, 0.7581699346405228, None)
                                           precision    recall  f1-score   support

   Schools providing healthy alternatives       0.91      0.93      0.92        75
                  Students without choice       0.70      0.64      0.67        33
                   Schools generate money       0.89      1.00      0.94         8
                           Student choice       0.43      0.86      0.57         7
Students can still bring/access junk food       0.00      0.00      0.00         3
                   Unclassified Off-Topic       0.42      0.45      0.43        11
          School without generating money       0.55      0.38      0.44        16

                              avg / total       0.75      0.76      0.75       153

/opt/anaconda3/lib/python3.7/site-packages/sklearn/metrics/classification.py:1135: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples.
  'precision', 'predicted', average, warn_for)

In [12]:
c = 0
for item, predicted, correct in zip(test_data, test_predicted, test_correct):
    assert item["label"] == idx2label[correct]
    c += (item["label"] == idx2label[predicted])
    print("{}#{}#{}".format(item["text"], idx2label[correct], idx2label[predicted]))
    
print(c)
print(c/len(test_data))


Schools should not allow junk food to be sold on campus but kids will still bring in unhealthy food#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but some think students should be able to choose what they eat#Student choice#Student choice
Schools should not allow junk food to be sold on campus but maybe on certain special occasions or at events#Unclassified Off-Topic#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but students may bring it anyway#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but they can choose to sell food that are nutritious and healthy#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but ultimately it is up to the individual student what they eat or drink#Students without choice#Student choice
Schools should not allow junk food to be sold on campus but should provide healthier choices#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but Can sell healthier options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but familes should have a say in what their kids eat#Students without choice#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but there should be alternatives they sell#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but students should be allowed to bring it to school#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but they could sell healthy options elsewhere#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should have treats on special days#Students without choice#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they could just have a small amount of it instead of the amount they have been selling at#School without generating money#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but they should inform the students as to why#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but serious consideration must be made in choosing replacements for it#Schools providing healthy alternatives#School without generating money
Schools should not allow junk food to be sold on campus but it is good to have a balance of healthy and junk food#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should also incorporate greater amounts of exercise into the school day#School without generating money#School without generating money
Schools should not allow junk food to be sold on campus but allow them to take it with them to school if they so wish#School without generating money#Students without choice
Schools should not allow junk food to be sold on campus but should provide healthy, nutritious food instead#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but kids are fond of junk foods#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but schools make a sizable profit by selling junk food items in vending machines#School without generating money#Schools generate money
Schools should not allow junk food to be sold on campus but they have to get an incentive to stop offering it#School without generating money#School without generating money
Schools should not allow junk food to be sold on campus but can offer healthy food choices in vending machines#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but I can see how the schools want to give the kids a choice#Student choice#Student choice
Schools should not allow junk food to be sold on campus but in the long run, a person's health choices are personal, and government institutions should not be able to dictate such choices#Unclassified Off-Topic#Student choice
Schools should not allow junk food to be sold on campus but parents should also provide education and rules at home around food choices#Unclassified Off-Topic#Students without choice
Schools should not allow junk food to be sold on campus but should allow children to bring it in their lunch boxes if their parents send it in their lunch#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but the sale of junk food can generate a significant amount of profit#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but they could allow healthier alternatives#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should be allowed to bring it from home#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but could offer alternative healthy foods in their place#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should instead promote healthy eating options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but it does generate money#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but give alternate snacks that are healthier#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should provided healthy alternatives#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they probably will continue to do so#School without generating money#School without generating money
Schools should not allow junk food to be sold on campus but provide a healthy alternative to sugary snack foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but students should be allowed to choose what they eat#Student choice#Student choice
Schools should not allow junk food to be sold on campus but they should not prohibit it if students bring it on their own#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but it does provide the school system with extra revenue#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but if people want it it, they will still find a way to have it#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but should allow for healthy food to be available#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but healthy snacks should be added to school vending machines as part of an initiative aimed at changing kids' eating and fitness habits#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but offer different alternatives#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but some say students should be able to make their own choices of what to eat#Student choice#Student choice
Schools should not allow junk food to be sold on campus but there could be an argument for teaching kids to make decisions in the face of temptation by allowing the food to remain#Students without choice#Student choice
Schools should not allow junk food to be sold on campus but they should make sure the healthier options are things kids will actually enjoy#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should offer healthy alternatives and encourage children to make better decisions#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but I think that giving the kids healthy choices would be a great replacement#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but instead should provide healthier food alternatives#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but allowed to bring healthier food to the school#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should have greater accessibility to nutrient dense foods#School without generating money#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but it needs to follow through at home with their own families#Unclassified Off-Topic#Students without choice
Schools should not allow junk food to be sold on campus but instead they should bring some healthy snacks and foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but selling snacks does raise money for the school#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but also not punish students for possessing junk food, since it is readily available and does not constitute an inherent threat that would warrant harsh punishment#Students without choice#School without generating money
Schools should not allow junk food to be sold on campus but should provide healthy snacks instead to raise revenue#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they may offer more choices of healthy food#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but it should be noted that every child has their own personal decision of what they want to eat#Students without choice#Student choice
Schools should not allow junk food to be sold on campus but it does help the school make money#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but allow students to bring in whatever foods their family allows#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but many kids prefer junk food#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but should have snacks that are healthy alternatives as treats#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but I think they should sell other healthy snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but instead should offer a wider variety of healthier snacks, so there is something for all students to enjoy#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should be allowed to provide healthy snacks and drinks for students to enjoy#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they can slowly integrate healthier choices such as baked potato chips instead of fried#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should incorporate healthy initiatives#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should have healthy alternatives for the kids to turn too when they do want a snack#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but certain snacks are alright#Schools providing healthy alternatives#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but let students do what they want#Students without choice#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but should respect students' right of free choice#Student choice#Student choice
Schools should not allow junk food to be sold on campus but they should teach students accurate health information#Schools providing healthy alternatives#School without generating money
Schools should not allow junk food to be sold on campus but they should have healthy snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but offer healthy alternatives#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but if a student really wants junk food, they can make he more conscious decision to purchase their junk food at a local grocery store#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but should instead offer healthier options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but at the end of the day, it's the students choice#Student choice#Students without choice
Schools should not allow junk food to be sold on campus but they should allow students to bring whatever food they wish to consume from home, as long as the school makes an effort to educate students about the risks of unhealthy eating habits#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but should also be limited on what quasi-healthy options are available as well (eg#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but some people think students should be able to choose what they eat#Student choice#Student choice
Schools should not allow junk food to be sold on campus but should offer healthy alternatives like fruit#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should make sure there are healthy options for snacks to replace the junk food#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should offer healthy alternatives to junk food#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should having vending machines that sell healthy options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they do#School without generating money#School without generating money
Schools should not allow junk food to be sold on campus but students should be allowed to bring junk food to campus if they desire#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but healthy snacks should be#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should be allowed to sell snacks and soda#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should offer reasonable alternative to the type of snacks that you would expect to find at a school#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should sell healthy options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should offer healthy alternative snacks to purchase#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should allow students to choose what they eat by bringing their own snacks if they want#Students can still bring/access junk food#Student choice
Schools should not allow junk food to be sold on campus but they should be offering healthy snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but educating students about good food choices still needs to happen because they will have access to junk food off campus#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but have healthy options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should allow kids to bring their own if they want#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but provide alternative healthy snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but here schools are, still selling this junk food that only promotes poor eating habits which could lead to a lot of health complications#School without generating money#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should make sure that something is available to purchase if the children need to#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but they tried to give us a choice between food#School without generating money#Student choice
Schools should not allow junk food to be sold on campus but provide public health education of its dangers#Schools providing healthy alternatives#School without generating money
Schools should not allow junk food to be sold on campus but in some cases junk food can be sold once a month#Unclassified Off-Topic#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but there could be certain times or events where some junk food may be purchased#Unclassified Off-Topic#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but be allowed to sell healthier alternative snacks and food#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they are allowed to bring it themselves#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but ITS NOT GOOD#Unclassified Off-Topic#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but many schools do it because they do not see it as their job to change the diet of their students#School without generating money#School without generating money
Schools should not allow junk food to be sold on campus but in some cases junk food is less expensive than healthier options so junk is perhaps all some can afford#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should not interfere with what students bring for lunch from home#School without generating money#Students without choice
Schools should not allow junk food to be sold on campus but the school allow snack machines with more nutritious foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they could offer healthy options in place of junk food#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they would be losing a good revenue source#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but it should also educate students and staff alike on why it has opted to do so#Students without choice#School without generating money
Schools should not allow junk food to be sold on campus but it is acceptable to eat it there#Unclassified Off-Topic#Students without choice
Schools should not allow junk food to be sold on campus but maybe a few things#Unclassified Off-Topic#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but educate kids about healthy eating#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they could offer the option to buy snacks once a week#School without generating money#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but a slight compromise should be sought to make everyone happy#Schools providing healthy alternatives#Students without choice
Schools should not allow junk food to be sold on campus but they could offer some snack foods that are low in calories or not as salty or sugary but to be able to still give them a choice#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they cannot enforce eating habits outside of school#School without generating money#School without generating money
Schools should not allow junk food to be sold on campus but if it is sold it should be really expensive so that only people that really want junk food will buy it#Unclassified Off-Topic#Students without choice
Schools should not allow junk food to be sold on campus but if kids choose to bring so on their own, that is their family's decision#Students can still bring/access junk food#Student choice
Schools should not allow junk food to be sold on campus but the schools make money off of it#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but they should at have least allow children to choose between healthy food and snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but make the food less sugary and salty to make it healthy#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but if its taken away, kids will find a way to bring it back to campus#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but it is also important to focus on changing kids' fitness habits#Students without choice#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but they should be prepared for community backlash#School without generating money#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but they can replaced the junk foods by selling some nutritional foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should be transparent about their reasoning and teach kids why unhealthy foods aren't good#Students without choice#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but students should still have the freedom to bring snacks to school for lunch, everyone has a right to eat what they choose#Students can still bring/access junk food#Students without choice
Schools should not allow junk food to be sold on campus but should make an effort to teach the kids about better eating habits#Students without choice#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should instead promote healthy eating by filling snack machines with nutritious foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should have alternative healthy snacks available#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should provide healthier snacks for students to have#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but the schools could fill snack machines with more nutritious foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but if they insist it could be healthy options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but instead fill snack machines with nutritious foods#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should allow kids to bring it from home#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but if a club or organization has a fundraiser it may be allowed on special occasions#Unclassified Off-Topic#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but schools could fill the snack machines with healthy choices#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but it is the children's decision to do whatever they want after they are not in school#Students without choice#Students without choice
Schools should not allow junk food to be sold on campus but selling junk foods generates money for the school#Schools generate money#Schools generate money
Schools should not allow junk food to be sold on campus but they could sell a variety of healthy snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but provide healthy snacks instead#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but we cannot protect our kids from everything bad for them out there#Students without choice#Unclassified Off-Topic
Schools should not allow junk food to be sold on campus but teens can make decisions about what they eat when they are not at school#School without generating money#Student choice
Schools should not allow junk food to be sold on campus but provide healthy options#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but offer healthy snacks#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but instead offer healthier options to students for purchase#Schools providing healthy alternatives#Schools providing healthy alternatives
Schools should not allow junk food to be sold on campus but should instead replace it with healthy alternatives#Schools providing healthy alternatives#Schools providing healthy alternatives
116
0.7581699346405228

In [ ]: