Intelligence artificielle

Tweets des trolls russes: classification à l'aide du BERT

Tweets des trolls russes: classification à l'aide du BERT


Les brigades Web, également connues sous le nom de Russian Troll Bots, sont des commentateurs politiques anonymes parrainés par l'État. Ils agissent souvent en diffusant du contenu controversé sur des plateformes de médias sociaux telles que Twitter et Facebook. Les brigades Web ont ciblé des événements tels que le crash du MH17, le conflit en Ukraine en 2014 et les élections de 2016 aux États-Unis. Étant donné que la propagation à grande échelle de contenus qui divisent politiquement peut avoir des conséquences graves, telles que l'ingérence électorale, il est important de développer des méthodes pouvant aider à la détection précise de ces mauvais acteurs. Dans cet article, nous allons construire un classifieur d'apprentissage automatique en utilisant BERT pour prédire si un tweet provient d'un bot russe. Nous allons utiliser le Tweets De Troll Russe ensemble de données de kaggle qui peut être trouvé ici.

Avant d'entrer dans les données, nous allons brièvement passer en revue BERT. BERT signifie «Représentations de codeurs bidirectionnels à partir de transformateurs». L'article décrivant l'algorithme BERT a été publié par Google et peut être trouvé ici. BERT fonctionne en masquant radicalement des jetons de mots et en représentant chaque mot masqué avec un vecteur basé sur son contexte. Les deux applications de BERT sont “pré-formation” et “ajustement”.

BERT DE PRE-FORMATION

Pour l’algorithme BERT de pré-formation, les chercheurs ont formé deux tâches d’apprentissage non supervisées. La première tâche est décrite en tant que Masked LM. Cela fonctionne en masquant au hasard 15% d'un document et en prédisant ces jetons masqués. La deuxième tâche est la prédiction de la phrase suivante (NSP). Ceci est motivé par des tâches telles que la réponse à la question et l'inférence en langage naturel. Ces tâches nécessitent des modèles pour capturer avec précision les relations entre les phrases. Pour y remédier, ils se préparent à une tâche de prédiction binarisée pouvant être générée de manière triviale à partir de n’importe quel corpus dans une seule langue. L'exemple qu'ils donnent dans l'article est le suivant: si vous avez les phrases A et B, 50% du temps A est étiqueté «isNext» et l'autre 50% du temps, il s'agit d'une phrase sélectionnée au hasard dans le corpus. et est étiqueté comme "notNext". Une formation préalable à ces tâches s'avère bénéfique pour les tâches de réponse aux questions et d'inférence de langage naturel.

BERT À LA FIN

Réglage précis BERT fonctionne en encodant les paires de texte concaténées avec une attention particulière. L’auto-attention est le processus d’apprentissage des corrélations entre les mots actuels et les mots précédents. Une des premières applications de ceci est dans le papier LSTM (Long Short Term Memory) (Dong2016) où les chercheurs ont utilisé leur propre attention pour faire la lecture à la machine. La bonne chose à propos de BERT est de capturer l’encodage de textes concaténés avec une attention croisée bidirectionnelle entre deux paires de phrases.

La source

Dans cet article, nous appliquerons BERT pour prédire si un document est une fausse nouvelle. Le nom du jeu de données est Se rendre compte de la fausse nouvelle et on peut le trouver ici. Ce post est inspiré par BERT à la rescousse qui utilise BERT pour la classification des sentiments de l'ensemble de données IMDB. Le code de BERT à la rescousse peut être trouvé ici.

Puisque nous nous intéressons à la classification en une phrase, l'architecture pertinente est la suivante:

La source

Dans la figure ci-dessus, l'entrée de l'algorithme BERT est une séquence de mots et les sorties sont les représentations de mots codés (vecteurs). Pour la classification en phrases simples, nous utilisons la représentation vectorielle de chaque mot en tant qu'entrée dans un modèle de classification.

Maintenant, commençons!

  1. FORFAITS IMPORT
importer des pandas en tant que pd 
importer numpy en tant que np
importer torch.nn comme nn
de pytorch_pretrained_bert import BertTokenizer, BertModel
torche d'importation
de torchnlp.datasets import imdb_dataset
depuis keras.preprocessing.sequence import pad_sequences
from sklearn.metrics import classification_report

2 EXPLORATION DE DONNEES

le Tweets De Troll Russe l'ensemble de données ne comprend que les tweets Troll russes; nous devons donc extraire des données supplémentaires que nous pouvons qualifier de "non bots" ou de tweets "normaux". J'ai utilisé Tweepy, l'API python de Twitter, pour extraire des tweets supplémentaires à l'aide de mots clés populaires. Pour un tutoriel complet sur l'utilisation de Tweepy, vous pouvez lire un autre article que j'ai écrit: Sentiment du patient pour les médicaments pharmaceutiques de Twitter. Les mots clés que j'ai utilisés étaient '#followfriday', '#tuesdaymotivation', '#thankful', '#birthday', '#pet', '#funny' et '#influencer'. Ils ont été extraits d'une liste de hashtags populaires. sur twitter à partir de 2018 qui peut être trouvé ici. Le code permettant de tirer des tweets «normaux» sera disponible sur GitHub.

Lisons maintenant les données dans une base de données et imprimons les cinq premières lignes. Nous pouvons également définir le nombre maximal de colonnes d’affichage sur «Aucun». Pour plus de simplicité, examinons les colonnes "texte" et "type":

pd.set_option ('display.max_columns', Aucun)
df = pd.read_csv ("training_data.csv")
df = df[['text', 'type']]
print (df.head ())

3 PRÉPARATION DES DONNÉES

Nous pouvons avoir une idée de la distribution en valeurs "bot" et "normale" dans nos données en utilisant la méthode "Counter":

print (Compteur (df['type'].valeurs))

Laissons également les valeurs de ‘NaN’:

df.dropna (inplace = True)

Ensuite, nous voulons équilibrer notre ensemble de données de manière à avoir un nombre égal de types «bot» et «normal». Nous devrions également mélanger les cibles au hasard:

df_bot = df[df[df['type'] == 'bot'] 
df_normal = df[df[df['type'] == 'normal']
df_bot = df_bot.sample (n = len (df_normal))
df = df_normal.append (df_bot)
df = df.sample (frac = 1, random_state = 24) .reset_index (drop = True)

Encore une fois, en vérifiant que nous obtenons le résultat souhaité:

print (Compteur (df['type'].valeurs))

Nous souhaitons ensuite formater les données de manière à ce qu’elles puissent être utilisées comme entrée dans notre modèle BERT. Nous avons divisé nos données en ensembles de formation et de test. Nous utilisons un total de 100 enregistrements pour la formation et les tests:

train_data = df.head (50)
test_data = df.tail (50)

Nous générons une liste de dictionnaires avec les clés ‘text’ et ‘type’:

train_data = [{'text': text, 'type': type_data } for text in list(train_data['text']) pour type_data dans la liste (train_data['type'])]
test_data = [{'text': text, 'type': type_data } for text in list(test_data['text']) pour type_data dans la liste (test_data['type'])]

Générez une liste de tuples à partir de la liste des dictionnaires:

train_texts, train_labels = liste (zip (* carte (lambda d: (d['text'], ré['type']), train_data)))
test_texts, test_labels = liste (zip (* map (lambda d: (d['text'], ré['type']), données de test)))

Générer des jetons et des identificateurs de jeton:

tokenizer = BertTokenizer.from_pretrained ('bert-base-uncased', do_lower_case = True)
train_tokens = list (map (lambda t: ['[CLS]']+ tokenizer.tokenize (t)[:511]train_texts))
test_tokens = list (map (lambda t: ['[CLS]']+ tokenizer.tokenize (t)[:511], test_texts))
train_tokens_ids = list (map (tokenizer.convert_tokens_to_ids, train_tokens))
test_tokens_ids = list (map (tokenizer.convert_tokens_to_ids, test_tokens))
train_tokens_ids = pad_sequences (train_tokens_ids, maxlen = 128, truncating = "post", padding = "post", dtype = "int")
test_tokens_ids = pad_sequences (test_tokens_ids, maxlen = 128, truncating = "post", padding = "post", dtype = "int")

Notez que nous tronquons les chaînes d'entrée à 128 caractères. La longueur maximale que BERT peut gérer est de 512, mais dans l’intérêt du temps de calcul, nous travaillerons avec 128.

Enfin, générez un tableau booléen basé sur la valeur de ‘type’ pour nos ensembles de test et de formation:

train_y = np.array (train_labels) == 'bot'
test_y = np.array (test_labels) == 'bot'

4 MODÈLE DE CONSTRUCTION

Nous créons notre classificateur BERT qui contient une méthode d’initialisation et une méthode d’avant qui renvoie les probabilités de jetons:

classe BertBinaryClassifier (nn.Module):
def __init __ (auto, abandon = 0.1):
super (BertBinaryClassifier, self) .__ init __ ()
self.bert = BertModel.from_pretrained ('bert-base-uncased')self.dropout = nn.Dropout (abandon)
self.linear = nn.Linear (768, 1)
self.sigmoid = nn.Sigmoid ()

def forward (auto, jetons, masques = aucun):
_, pooled_output = self.bert (jetons, attention_mask = masques, output_all_encoded_layers = False)
dropout_output = self.dropout (pooled_output)
linear_output = self.linear (dropout_output)
proba = self.sigmoid (sortie_linéaire)
retour proba

Nous générons ensuite des masques de formation et de test:

train_masks = [[float(i > 0) for i in ii] pour ii dans train_tokens_ids]
test_masks = [[float(i > 0) for i in ii] pour ii dans test_tokens_ids]
train_masks_tensor = torch.tensor (train_masks)
test_masks_tensor = torch.tensor (test_masks)

Générez des tenseurs de jetons pour la formation et les tests:

train_tokens_tensor = torch.tensor (train_tokens_ids)
train_y_tensor = torch.tensor (train_y.reshape (-1, 1)). float ()
test_tokens_tensor = torch.tensor (test_tokens_ids)
test_y_tensor = torch.tensor (test_y.reshape (-1, 1)). float ()

et enfin, préparez nos chargeurs de données:

train_dataset = torch.utils.data.TensorDataset (train_tokens_tensor, train_masks_tensor, train_y_tensor)
train_sampler = torch.utils.data.RandomSampler (train_dataset)
train_dataloader = torch.utils.data.DataLoader (train_dataset, sampler = train_sampler, batch_size = BATCH_SIZE)
test_dataset = torch.utils.data.TensorDataset (test_tokens_tensor, test_masks_tensor, test_y_tensor)
test_sampler = torch.utils.data.SequentialSampler (test_dataset)
test_dataloader = torch.utils.data.DataLoader (test_dataset, sampler = test_sampler, batch_size = BATCH_SIZE)

5 RÉGLAGE FIN

Nous utilisons l’optimiseur Adam pour minimiser la perte d’entropie binaire croisée et nous nous entraînons avec une taille de lot de 1 à 1 EPOCHS:

BATCH_SIZE = 1
EPOCHS = 1
bert_clf = BertBinaryClassifier ()
optimiseur = torch.optim.Adam (bert_clf.parameters (), lr = 3e-6)
pour epoch_num in range (EPOCHS):
bert_clf.train ()
train_loss = 0
pour step_num, batch_data in énumérer (train_dataloader):
token_ids, masks, labels = tuple (t pour t dans batch_data)
probas = bert_clf (identificateurs de jeton, masques)
loss_func = nn.BCELoss ()
batch_loss = loss_func (probas, étiquettes)
train_loss + = batch_loss.item ()
bert_clf.zero_grad ()
batch_loss.backward ()
optimizer.step ()
print ('Epoch:', epoch_num + 1)
print (" r" + "{0} / {1} pertes: {2}" .format (step_num, len (train_data) / BATCH_SIZE, train_loss / (step_num + 1)))

Et nous évaluons notre modèle:

bert_clf.eval ()
bert_predicted = []
all_logits = []
avec torch.no_grad ():
pour step_num, batch_data in enumerate (test_dataloader):
token_ids, masks, labels = tuple (t pour t dans batch_data)logits = bert_clf (identificateurs de jeton, masques)
loss_func = nn.BCELoss ()
loss = loss_func (logits, étiquettes)
numpy_logits = logits.cpu (). detach (). numpy ()

bert_predicted + = list (numpy_logits[:, 0] > 0.5)
all_logits + = list (numpy_logits[:, 0])

print (classification_report (test_y, bert_predicted))

Rapport de classification

Étant donné que nous n’avons pas beaucoup de données d’entraînement, la précision des performances des données s’est avérée assez faible. Essayez d’ajuster le modèle à plus de données pour voir si vous pouvez obtenir des améliorations en termes de précision. De plus, vous pouvez essayer d’améliorer les étiquettes «normales» avec des tweets supplémentaires à l’aide de Tweepy et de certains mots clés couramment utilisés.

Une lecture plus approfondie du code peut être trouvée dans BERT à la rescousse. Le code de cet article peut être trouvé sur GitHub. Merci d'avoir lu et joyeux apprentissage machine!

Afficher plus

SupportIvy

SupportIvy.com : Un lieu pour partager le savoir et mieux comprendre le monde. Meilleure plate-forme de support gratuit pour vous, Documentation &Tutoriels par les experts.

Articles similaires

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Bouton retour en haut de la page
Fermer