Analyse des sentiments d'expression à l'aide de réseaux de neurones

Bonjour à tous!

Toutes les personnes qui reçoivent un enseignement supérieur, sans être expulsées, atteignent néanmoins le stade de rédaction d'un diplôme. Je n'ai pas fait exception. Je voulais mettre en œuvre quelque chose d'intéressant et maîtriser ce qui n'était pas encore exploré, j'ai donc attiré l'attention sur le thème des réseaux de neurones et de l'intelligence artificielle en général. Et la tâche que j'ai résolue à l'aide de celui-ci est l'analyse de la tonalité du texte, qui est déjà largement utilisée dans divers systèmes de surveillance. Je vais essayer de décrire le processus de sa solution dans cet article.

En bref, le but est de comprendre si une phrase a une connotation positive ou négative. Je veux dire tout de suite que ce problème peut être résolu de plusieurs manières, et pas seulement par les réseaux de neurones. Nous pouvons faire des dictionnaires dans lesquels les positions des mots sont marquées, etc. (toutes les méthodes sont en abondance sur le hub), mais chaque méthode peut aller plus loin selon l'article, nous allons donc laisser leur avis pour plus tard.

Les données


La première tâche sur mon chemin a été la collecte et le prétraitement des données pour la formation. Un bon ensemble de données pour un tel cas est le corpus de courts textes de Yu. Rubtsova, précédemment divisé en phrases négatives et positives recueillies sur Twitter. Ce qui est particulièrement pratique - tout cela existe au format CSV.

Préparation à la formation


Faites attention à la forme sous laquelle les données sont présentées - beaucoup d'émoticônes, de liens, de caractères inutiles, de hits. Tout cela n'est pas une information importante et ne gêne que l'apprentissage, de plus, tout doit être supprimé en latin. Par conséquent, le texte serait bon de prétraiter.

def preprocessText(text):
    text = text.lower().replace("", "")
    text = re.sub('((www\.[^\s]+)|(https?://[^\s]+))', ' ', text)
    text = re.sub('@[^\s]+', ' ', text)
    text = re.sub('[^a-zA-Z--1-9]+', ' ', text)
    text = re.sub(' +', ' ', text)
    return text.strip()

Après avoir fui toutes les phrases du fichier, nous les amenons en minuscules, remplaçons "" par "e", les références, les références, les mots anglais sont simplement supprimés par manque de sens. En bref, nous les faisons du même type, en nettoyant les «déchets» qui sont superflus pour la formation.

Outils


Bien sûr, si vous avez un supercalculateur à la maison, vous pouvez faire défiler cette section plus loin, à la recherche d'une partie intéressante. Je conseille le reste du service Google Colab , qui vous permet d'exécuter Jupyter Notebooks (et qui n'en a pas entendu parler, pour aider le moteur de recherche) en utilisant uniquement un navigateur, et tout le travail est effectué sur une machine virtuelle dans le cloud.
La taille temporaire de la session que vous devez travailler est limitée à 12 heures - vous pouvez terminer plus tôt, après quoi tout est réinitialisé.

Nous écrivons notre beau code


Comme tout nouveau venu dans l'apprentissage automatique, j'ai choisi Python - parce que c'est simple, et les bibliothèques sont un nuage entier.

Tout d'abord, le gestionnaire de paquets exécutera une commande importante, dont je vais expliquer la signification un peu plus tard.

image

Ensuite, nous importons les bibliothèques que nous utiliserons lors de la formation de la grille et de la préparation des données, je pense que beaucoup d'entre elles vous sont familières.

image

Enfin au point.

Alors, pourquoi avons-nous téléchargé et importé la bibliothèque de texte Tensorflow? Le fait est que les phrases ne peuvent pas être «introduites» dans la grille sous la forme dans laquelle elles nous sont lisibles. C'est là que Word Embedding entre en jeu, un terme pour lequel je n'ai pas trouvé de traduction adéquate, et en général je doute de son existence. Mais en gros, nous parlons de faire correspondre un vecteur à un mot. C'est bien dit ici .

Nous devons convertir des phrases entières en un vecteur, nous utilisons donc une solution prête à l'emploi de Google - Universal Phrase Encoder.

image

Vous pouvez le télécharger à partir du hub ici . Au fait, il existe de nombreuses solutions prêtes à l'emploi plus intéressantes qui peuvent être utilisées lors de l'apprentissage d'un réseau de neurones, afin de ne pas vous déranger.

image

Tous les tweets sont classés par classe - mauvais / bon. Nous créons un pandas-dataframe dans lequel ils sont triés par classe (les mauvais ne sont pas visibles dans l'image, car ils s'intègrent).

image

Nous avons préparé les données - passons au modèle lui-même. Pour ce faire, utilisez le framework Keras.

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential, load_model
model = tf.keras.Sequential()

model.add(
  tf.keras.layers.Dense(
    units=256,
    input_shape=(X_train.shape[1], ),
    activation='relu'
  )
)
model.add(
  tf.keras.layers.Dropout(rate=0.5)
)

model.add(
  tf.keras.layers.Dense(
    units=128,
    activation='relu'
  )
)
model.add(
  tf.keras.layers.Dropout(rate=0.5)
)

model.add(tf.keras.layers.Dense(2, activation='softmax'))
model.compile(
    loss='categorical_crossentropy',
    optimizer=tf.keras.optimizers.Adam(0.001),
    metrics=['accuracy']
)

history = model.fit(
    X_train, y_train,
    epochs=10,
    batch_size=16,
    validation_split=0.1,
    verbose=1,
    shuffle=True
)

model.evaluate(X_test, y_test)

Un peu sur le modèle lui-même. Il a des couches d'entrée, cachées et de sortie.

Chaque couche a sa propre fonction d'activation.

Une petite explication: dans les réseaux de neurones artificiels, la fonction d'activation des neurones détermine le signal de sortie, qui est déterminé par le signal d'entrée ou un ensemble de signaux d'entrée. Vous pouvez en savoir plus ici , en passant, il en existe beaucoup pour différentes tâches, mais nous ne travaillerons qu'avec 2. Nous attribuons la

fonction d'activation ReLu aux 2 premières couches . Le jour de congé est Softmax .

En plus d'ajouter des couches, vous pouvez remarquer le mot «abandon». Qu'Est-ce que c'est? Curieusement, mais en plus du problème de sous-apprentissage d'un réseau de neurones, lorsque ses prédictions ne sont pas vraies, il y a le problème de la sur-formation - le modèle n'explique bien que des exemples de l'échantillon de formation, s'adaptant aux exemples de formation, au lieu d'apprendre à classer les exemples qui n'ont pas participé à la formation. C'est ringard sur les nouvelles données, votre magnifique mannequin, qui avait superbement fait son travail auparavant, «vole» tout simplement et commence à vous surprendre désagréablement. Ainsi, Dropout est engagé dans le fait qu'avec une probabilité spécifiée, il «désactive» les neurones de la grille, de sorte qu'ils cessent de participer au processus d'apprentissage. Ensuite, les résultats de plusieurs réseaux sont moyennés (lorsqu'un neurone est exclu du réseau, un nouveau est obtenu).

Au fait, un excellent article pour ceux qui sont intéressés.

Vous pouvez commencer à apprendre!

Train on 53082 samples, validate on 5898 samples
Epoch 1/10
53082/53082 [==============================] - 12s 223us/sample - loss: 0.5451 - accuracy: 0.7207 - val_loss: 0.5105 - val_accuracy: 0.7397
Epoch 2/10
53082/53082 [==============================] - 11s 213us/sample - loss: 0.5129 - accuracy: 0.7452 - val_loss: 0.5000 - val_accuracy: 0.7523
Epoch 3/10
53082/53082 [==============================] - 11s 215us/sample - loss: 0.4885 - accuracy: 0.7624 - val_loss: 0.4914 - val_accuracy: 0.7538
Epoch 4/10
53082/53082 [==============================] - 11s 215us/sample - loss: 0.4686 - accuracy: 0.7739 - val_loss: 0.4865 - val_accuracy: 0.7589
Epoch 5/10
53082/53082 [==============================] - 11s 214us/sample - loss: 0.4474 - accuracy: 0.7889 - val_loss: 0.4873 - val_accuracy: 0.7616
Epoch 6/10
53082/53082 [==============================] - 11s 216us/sample - loss: 0.4272 - accuracy: 0.8004 - val_loss: 0.4878 - val_accuracy: 0.7603
Epoch 7/10
53082/53082 [==============================] - 11s 213us/sample - loss: 0.4081 - accuracy: 0.8111 - val_loss: 0.4986 - val_accuracy: 0.7594
Epoch 8/10
53082/53082 [==============================] - 11s 215us/sample - loss: 0.3899 - accuracy: 0.8241 - val_loss: 0.5101 - val_accuracy: 0.7564
Epoch 9/10
53082/53082 [==============================] - 11s 215us/sample - loss: 0.3733 - accuracy: 0.8315 - val_loss: 0.5035 - val_accuracy: 0.7633
Epoch 10/10
53082/53082 [==============================] - 11s 215us/sample - loss: 0.3596 - accuracy: 0.8400 - val_loss: 0.5239 - val_accuracy: 0.7620
6554/6554 [==============================] - 0s 53us/sample - loss: 0.5249 - accuracy: 0.7524
[0.5249265961105736, 0.752365]

Ainsi, 10 époques se sont écoulées. Pour ceux qui ne sont pas du tout familiarisés avec de tels concepts, j'expliquerai la définition sur Internet: L'ère est une itération dans le processus d'apprentissage, y compris la présentation de tous les exemples de l'ensemble de formation et, éventuellement, la vérification de la qualité de la formation sur l'ensemble de contrôle. Donc, toutes nos données ont parcouru 10 fois complètement tout le processus.

Résultat


image

Bien sûr, le réseau sera nécessaire plus d'une fois et ce serait bien de savoir comment le sauvegarder pour la postérité, afin qu'ils n'aient pas à le recycler et tout ça.

La structure est enregistrée au format JSON et les poids sont écrits dans un fichier h5 .

Le moteur de recherche regorge de guides sur la façon de lancer le processus inverse d'initialisation du réseau à partir de ces fichiers, donc je ne le décrirai pas.

En utilisant la méthode de prédiction, nous essaierons de connaître l'opinion du réseau et la composante tonale de 2 phrases évidemment différentes à cet égard. Certes, ils doivent d'abord être réduits à une forme matricielle, mais nous savons déjà comment le faire en utilisant une solution prête à l'emploi.

À la sortie, nous voyons 2 nombres - la probabilité que la phrase appartienne aux classes «négatives» / «positives». Je pense que l'image montre clairement qu'il y a une différence) Donc, des mots similaires étaient finalement et le réseau a fait un excellent travail avec leur relation avec leurs classes.

Conclusion


Donc, je veux dire que maîtriser des outils avancés pour développer des réseaux de neurones et résoudre des problèmes simples, après avoir correctement déterminé les étapes nécessaires pour le résoudre et avoir lu un peu la théorie, semble être une tâche assez facile. Je voudrais noter que j'ai vu plusieurs articles sur le sujet de l'analyse tonale sur Habré, mais c'était quand même intéressant d'essayer quelque chose de plus simple et sans une énorme masse de texte, même si la théorie doit être étudiée sans condition :)

Vous pouvez trouver le code ici si vous mettez un astérisque sur le projet, ce sera génial. Si vous avez besoin de fichiers avec des poids et une structure de réseau, ainsi que des données traitées - écrivez dans les commentaires, ajoutez-les au référentiel.

All Articles