[PYTHON] Analysons les émotions de Tweet en utilisant Chainer (2ème)

Aperçu

Dernière fois est l'utilisation de base de Chainer, l'implémentation de MLP (perceptron multicouche) et le nombre de nœuds de la couche entièrement connectée de la couche de convolution de CNN. J'ai introduit la formule de calcul nécessaire pour cela.

Cette fois, je vais lire les données Twitter réelles et créer un CNN.

Réseau de neurones à convolution

L'explication du réseau de neurones convolutifs était facile à comprendre ici.

image

Un filtre est appliqué aux données d'image bidimensionnelles pour compresser les caractéristiques. Après cela, la mise en commun est effectuée pour extraire davantage la quantité de fonctionnalités. Il n'y a probablement pas un type de filtre, mais différents filtres sont appliqués pour le nombre de feuilles que vous souhaitez produire.

Cible

Cet article en référence à [http://qiita.com/hogefugabar/items/93fcb2bc27d7b268cbe6) Je penserai à l'imiter.

Aperçu du traitement

Les grandes lignes du processus sont les suivantes.

  1. Lisez Tweet data
  2. Word Embed (Word2Vec est utilisé cette fois)
  3. Division des données d'apprentissage / de test
  4. Définition de CNN
  5. Apprendre avec CNN
  6. Prévisions CNN

La définition et l'apprentissage de CNN utilisent la méthode suivante décrite dans l'article.

image

Cette figure illustre le processus de convolution et de mise en commun pour une seule phrase, $ d $ étant la dimension du vecteur Word et $ s $ étant le nombre de mots de la phrase dans la grande matrice à l'extrême gauche. Et la taille du filtre n'est pas une matrice symétrique, mais une matrice asymétrique de dimensions $ d × m $.

Cependant, je n'ai pas compris même après avoir lu l'article, mais $ s $ est différent pour chaque phrase, donc si je me demandais comment faire cela, cet article / items / 93fcb2bc27d7b268cbe6) prend le nombre maximum de mots pour chaque phrase qui apparaît dans tous les Tweets, alors imitez ceci.

L'acquisition des données

Obtenez les données du Tweet (https://raw.githubusercontent.com/satwantrana/CharSCNN/master/tweets_clean.txt). Dans les données, la première colonne est le drapeau de [0,1] et la deuxième colonne est le Tweet en anglais.

Word Embed

Je veux garder la phrase dans un format d'image bidimensionnel, donc je vais la changer en une représentation distribuée. Cette fois, l'ID intégré fourni avec Chainer n'a pas fonctionné, donc j'utiliserai Word2Vec du package gensim.

Tout d'abord, attribuez un ID à Word à partir des données texte.

#! -*- coding:utf-8 -*-

def read(inp_file,num_sent=None):
        f_in = open(inp_file, 'r')
        lines = f_in.readlines()

        words_map = {}
        word_cnt = 0

        k_wrd = 5 #Fenêtre contextuelle Word

        y = []
        x_wrd = []

        if num_sent is None:
            num_sent = len(lines)
            max_sen_len = 0
        else:
            max_sen_len, num_sent = 0, num_sent


        words_vocab_mat = []

        token_list = []

        for line in lines[:num_sent]:
            words = line[:-1].split()
            tokens = words[1:]
            y.append(int(float(words[0])))
            max_sen_len = max(max_sen_len,len(tokens))
            for token in tokens:
                if token not in words_map:
                    words_map[token] = word_cnt
                    token_list.append(token)
                    word_cnt += 1
            words_vocab_mat.append(tokens)

        cnt = 0
        for line in lines[:num_sent]:
            words = line[:-1].split()
            cnt += 1
            tokens = words[1:]
            word_mat = [-1] * (max_sen_len+k_wrd-1)

            for i in xrange(len(tokens)):
                word_mat[(k_wrd/2)+i] = words_map[tokens[i]]
            x_wrd.append(word_mat)
        max_sen_len += k_wrd-1

        # num_sent:Nombre de documents
        # word_cnt:Nombre de types de mots
        # max_sen_len:Longueur maximale du document
        # x_wrd:Nombre de lignes dans la colonne id du mot d'entrée:Nombre de phrases(num_sent)Le nombre de colonnes:Longueur maximale du document(max_sen_len)
        # k_wrd: window size
        # words_map : key = word,value = id
        # y: 1 or 0 (i.e., positive or negative)
        # words_vocab_mat :Phrase décomposée, le nombre de lignes correspond au nombre de phrases, le nombre de colonnes est variable et le nombre de mots
        # token_list :Liste de jetons, l'index correspond à l'id
        data = (num_sent, word_cnt, max_sen_len, k_wrd, x_wrd, y,words_map,words_vocab_mat,token_list)
        return data

(num_sent, word_cnt, max_sen_len, k_wrd, x_wrd, y,words_map,sentences,token_list) = load.read("data/tweets_clean.txt",10000)

x_wrd est une matrice de nombre de phrases x longueur maximale du document, et chaque élément est l'ID du mot qui apparaît. Préparez words_map, token_list et words_vocab_mat car vous en aurez besoin plus tard.

Ensuite, utilisez Word2Vec pour obtenir une représentation vectorielle de chaque mot, puis créez une «matrice d'image de phrase» (attachée arbitrairement).

    """Créer un espace vectoriel de mots avec Word2Vec"""
    word_dimension = 200
    from gensim.models import Word2Vec
    model_w2v = Word2Vec(sentences,seed=123,size=word_dimension,min_count=0,window=5)
    sentence_image_matrix = np.zeros((len(sentences),1,word_dimension,max_sen_len)) #Initialisation de la matrice d'image de phrase pour la convolution

    """x_Générer un vecteur pour wrd"""
    for i in range(0,len(x_wrd)):
        tmp_id_list = x_wrd[i,:]
        for j in range(0,len(tmp_id_list)):
            """Tourner pour une ligne"""
            id = tmp_id_list[j]
            if id == -1:
                """Aucune information"""
                sentence_image_matrix[i,0,:,j] = [0.] * word_dimension #Insérer 0 vecteur
            else:
                target_word = token_list[id]
                sentence_image_matrix[i,0,:,j] = model_w2v[target_word]

phrase_image_matrix est défini comme un tenseur à 4 dimensions avec la taille (nombre de phrases, 1, dimension vectorielle = 200, longueur maximale de la phrase).

Division des données d'apprentissage / de test

Comme je l'ai appris pour la première fois, train_test_split de sklearn peut également être utilisé pour les tenseurs à 4 dimensions. Probablement parce que je ne regarde que la première dimension.

    """Divisez en données d'entraînement et en données de test"""
    sentence_image_matrix = np.array(sentence_image_matrix,dtype=np.float32)
    N = len(sentence_image_matrix)
    t_n = 0.33
    x_train,x_test,y_train,y_test = train_test_split(sentence_image_matrix,y,test_size=t_n,random_state=123)

Définition de CNN

Le problème est la définition de CNN. Dans cet article, nous utilisons un filtre asymétrique, et la mise en commun est également asymétrique, nous devons donc en tenir compte.

Alors ça ressemble à ça.

class CNNFiltRow(ChainerClassifier):
    """
Un modèle qui déplace toutes les directions de ligne de CNN dans la direction de colonne en tant que filtre
    """

    def _setup_network(self, **params):
        self.input_dim = params["input_dim"] #Dimensions par colonne d'une seule image
        self.in_channels = params["in_channels"] #input channels : default = 1
        self.out_channels = params["out_channels"] #out_channels :Tout
        self.row_dim = params["row_dim"] #Dimension de direction de ligne d'une image=Cela devient le nombre de lignes de Filter
        self.filt_clm = params["filt_clm"] #Nombre de colonnes de filtre
        self.pooling_row = params["pooling_row"] if params.has_key("pooling_row") else 1 #Nombre de lignes de pooling: default = 1
        self.pooling_clm = params["pooling_clm"] if params.has_key("pooling_clm") else int(self.input_dim - 2 * math.floor(self.filt_clm/2.)) #Nombre de colonnes de regroupement: default = math.floor((self.input_dim - 2 * math.floor(self.filt_clm/2.))
        self.batch_size = params["batch_size"] if params.has_key("batch_size") else 100
        self.hidden_dim = params["hidden_dim"]
        self.n_classes = params["n_classes"]

        self.conv1_out_dim = math.floor((self.input_dim - 2 * math.floor(self.filt_clm/2.))/self.pooling_clm)
        network = FunctionSet(
            conv1 = F.Convolution2D(self.in_channels,self.out_channels,(self.row_dim,self.filt_clm)), #Made Filter asymétrique
            l1=F.Linear(self.conv1_out_dim*self.out_channels, self.hidden_dim),
            l2=F.Linear(self.hidden_dim, self.hidden_dim),
            l3=F.Linear(self.hidden_dim, self.n_classes),
        )
        return network

    def forward(self, x, train=True):
        h = F.max_pooling_2d(F.relu(self.network.conv1(x)), (self.pooling_row,self.pooling_clm))
        h1 = F.dropout(F.relu(self.network.l1(h)),train=train)
        h2 = F.dropout(F.relu(self.network.l2(h1)),train=train)
        y = self.network.l3(h2)
        return y

    def output_func(self, h):
        return F.softmax(h)

    def loss_func(self, y, t):
        return F.softmax_cross_entropy(y, t)

    def fit(self, x_data, y_data):
        batchsize = self.batch_size
        N = len(y_data)
        for loop in range(self.n_iter):
            perm = np.random.permutation(N)
            sum_accuracy = 0
            sum_loss = 0
            for i in six.moves.range(0, N, batchsize):
                x_batch = x_data[perm[i:i + batchsize]]
                y_batch = y_data[perm[i:i + batchsize]]
                x = Variable(x_batch)
                y = Variable(y_batch)
                self.optimizer.zero_grads()
                yp = self.forward(x)
                loss = self.loss_func(yp,y)
                loss.backward()
                self.optimizer.update()
                sum_loss += loss.data * len(y_batch)
                sum_accuracy += F.accuracy(yp,y).data * len(y_batch)
            if self.report > 0 and loop % self.report == 0:
                print('loop={}, train mean loss={} , train mean accuracy={}'.format(loop, sum_loss / N,sum_accuracy / N))

        return self

    def fit_test(self, x_data, y_data,x_test,y_test):
        batchsize = self.batch_size
        N = len(y_data)
        Nt = len(y_test)
        train_ac = []
        test_ac = []
        for loop in range(self.n_iter):
            perm = np.random.permutation(N)
            permt = np.random.permutation(Nt)
            sum_accuracy = 0
            sum_loss = 0

            sum_accuracy_t = 0

            """Phase d'apprentissage"""
            for i in six.moves.range(0, N, batchsize):
                x_batch = x_data[perm[i:i + batchsize]]
                y_batch = y_data[perm[i:i + batchsize]]
                x = Variable(x_batch)
                y = Variable(y_batch)
                self.optimizer.zero_grads()
                yp = self.forward(x)
                loss = self.loss_func(yp,y)
                loss.backward()
                self.optimizer.update()
                sum_loss += loss.data * len(y_batch)
                sum_accuracy += F.accuracy(yp,y).data * len(y_batch)

            """Phase de test"""
            for i in six.moves.range(0,Nt,batchsize):
                x_batch = x_test[permt[i:i + batchsize]]
                y_batch = y_test[permt[i:i + batchsize]]
                x = Variable(x_batch)
                y = Variable(y_batch)
                yp = self.forward(x,False)
                sum_accuracy_t += F.accuracy(yp,y).data * len(y_batch)

            if self.report > 0 and loop % self.report == 0:
                print('loop={}, train mean loss={} , train mean accuracy={} , test mean accuracy={}'.format(loop, sum_loss / N,sum_accuracy / N,sum_accuracy_t / Nt))

            train_ac.append(sum_accuracy / N)
            test_ac.append(sum_accuracy_t / Nt)

        return self,train_ac,test_ac

Veuillez vous référer à Article précédent pour Chainer Classifier.

Apprendre CNN

Je voulais également voir la précision des données de test, j'ai donc ajouté la méthode fit_test.

    """Apprentissage de la ligne de filtre CNN"""
    n_iter = 200
    report = 5
    params = {"input_dim":max_sen_len,"in_channels":1,"out_channels":20,"row_dim":word_dimension,"filt_clm":3,"batch_size":100,"hidden_dim":300,"n_classes":2}
    cnn = CNNFiltRow(n_iter=n_iter,report=report,**params)
    cnn,train_ac,test_ac = cnn.fit_test(x_train,y_train,x_test,y_test)

Vous trouverez ci-dessous un graphique de la précision pendant l'entraînement et de la précision des données de test. Il semble que le surapprentissage commence à partir d'environ 100. Pourtant, l'impression que les performances de généralisation sont élevées.

image

Prévisions CNN

Si vous mettez des données de test dans le modèle final et que vous sortez chaque index, ce sera comme suit.

[CNN]P AUC: 0.80 Pres: 0.66 Recl: 0.89 Fscr: 0.76

Le score F était de 0,76 et l'ASC de 0,8, ce qui était plutôt bon.

Comparer avec d'autres modèles

Faites de même avec Random Forest et MLP (Multilayer Perceptron) comme points de repère. Étant donné que les données d'entrée ne sont pas bidimensionnelles dans ce cas, elles sont corrigées en unidimensionnel comme MNIST.

En conséquence, divers indicateurs pour des données de test similaires sont les suivants.

[RF ]P AUC: 0.71 Pres: 0.65 Recl: 0.60 Fscr: 0.62
[MLP]P AUC: 0.71 Pres: 0.64 Recl: 0.69 Fscr: 0.67
[CNN]P AUC: 0.80 Pres: 0.66 Recl: 0.89 Fscr: 0.76

Cette énorme différence de performance de CNN ...

Résumé

Recommended Posts

Analysons les émotions de Tweet en utilisant Chainer (2ème)
Analysons les émotions de Tweet en utilisant Chainer (1er)
Analysons les données de l'enquête par questionnaire [4e: Analyse des émotions]
Supprimons maintenant l'ambiguïté du trait d'union (-) de la commande su! !!
Analysons les émotions de Tweet en utilisant Chainer (2ème)
Analysons les émotions de Tweet en utilisant Chainer (1er)
Affichons la carte en utilisant Basemap
Décidons le gagnant du bingo
Je veux analyser les sentiments des gens qui veulent se rencontrer et trembler
A propos du comportement de enable_backprop de Chainer v2
Implémentation d'un système de dialogue utilisant Chainer [seq2seq]
Tweet à l'aide de l'API Twitter en Python
Implémentation de réseaux neuronaux "flous" avec Chainer
Examinons le mécanisme de la chinchirorine de Kaiji
Raccourcir le temps d'analyse d'Openpose à l'aide du son
Estimation de l'effet des mesures à l'aide des scores de propension
Vérifiez le type de variable que vous utilisez
[Python] Exécutons le module régulièrement en utilisant schedule
Sortie exclusive de l'application Django utilisant ngrok
Jugons les émotions à l'aide de l'API Emotion en Python
Essayez d'utiliser le module de collections (ChainMap) de python3
Déterminez le nombre de classes à l'aide de la formule Starges
J'ai essayé d'utiliser le filtre d'image d'OpenCV
Vérifiez l'état des données à l'aide de pandas_profiling
Gratter les données gagnantes de Numbers à l'aide de Docker
Expliquer Chat Bot annoncé à PyCon 2016 à partir de la base de code (réponse de chat à l'aide de Chainer)