[PYTHON] Dokumentenklassifizierung mit Satzstück

Ich habe Satzstück, einen Tokenizer für die Verarbeitung neuronaler Sprachen, auf die Klassifizierung von Dokumenten angewendet.

Auslösen

Neulich habe ich das Satzstück als Option zur Wortaufteilung in der Verarbeitung natürlicher Sprache kennengelernt. Es scheint, dass die maschinelle Übersetzung eine Punktzahl erreicht hat, die über der herkömmlichen Wortteilungsmethode liegt, und ich war einfach daran interessiert, und ich habe mich gefragt, was mit der Dokumentklassifizierung passieren würde, an der ich gerade arbeite, also habe ich es versucht.

SentencePiece(GitHub) Artikel des Autors taku910 (Qiita)

Daten

Ich habe KNB Analyzed Blog Corpus verwendet. Dies ist ein analysierter Blog-Korpus mit insgesamt 4.186 Sätzen, die in vier Kategorien unterteilt sind: "Kyoto Sightseeing", "Handy", "Sport" und "Gourmet", einschließlich Morphologie und Groß- und Kleinschreibung. Dieses Mal werden wir nur Kategorien und Sätze verwenden, um das Klassifizierungsproblem zu lösen, zu welcher Kategorie jeder Satz gehört. 10% aller Daten wurden als Testdaten aufgeteilt, und die restlichen 90% wurden für das Training mit SentencePiece und neuronalen Netzwerken verwendet.

Implementierung

Ich habe es unter Python 3.6.1 unter Bash unter Windows ausgeführt. Die detaillierte Version des Python-Moduls finden Sie unter require.txt.

Der Code ist in GitHub zusammengefasst. Ich bin noch unreif, daher würde ich mich freuen, wenn Sie auf Fehler hinweisen oder mir Ratschläge geben könnten.

separator.py

Grundsätzlich scheint Satzstück von der Kommandozeile aus verwendet zu werden, aber ich wollte es von Python aus verwenden und ich wollte es einfach mit Mecab verwenden, daher ist es nicht sehr intelligent, aber ich habe es aus dem Unterprozess aufgerufen.

def train_sentencepiece(self, vocab_size):
    cmd = "spm_train --input=" + self.native_text + \
            " --model_prefix=" + self.model_path + \
            " --vocab_size=" + str(vocab_size)
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout_data, stderr_data = p.communicate()

Wenn ich separat mit dem gelernten Modell schreibe, dauert es zu lange, jede Zeile zu verarbeiten. Deshalb schreibe ich sie einmal in eine Textdatei und mache alles auf einmal. Ich habe es diesmal nicht verwendet, aber ich habe auch eine Funktion zum Teilen von Zeichen für Zeichen hinzugefügt, um auf Zeichenebene zu lernen.

Dieses Mal werden wir Satzstück und Mecab + Neologd vergleichen

net.py

Ich wollte LSTM für das Netzwerk verwenden, aber das Lernen nimmt viel Zeit in Anspruch, deshalb habe ich mich diesmal für CNN entschieden. Wir haben ein Netzwerk mit drei Arten von Filtern implementiert: 3, 4 und 5 Wörter.

class CNN(Chain):

    def __init__(self, n_vocab, n_units, n_out, filter_size=(3, 4, 5), stride=1, use_dropout=0.5, ignore_label=-1):
        super(CNN, self).__init__()
        initializer = initializers.HeNormal()
        with self.init_scope():
            self.word_embed=L.EmbedID(n_vocab, n_units, ignore_label=-1)
            self.conv1 = L.Convolution2D(None, n_units, (filter_size[0], n_units), stride, pad=(filter_size[0], 0), initialW=initializer)
            self.conv2 = L.Convolution2D(None, n_units, (filter_size[1], n_units), stride, pad=(filter_size[1], 0), initialW=initializer)
            self.conv3 = L.Convolution2D(None, n_units, (filter_size[2], n_units), stride, pad=(filter_size[2], 0), initialW=initializer)
            self.norm1 = L.BatchNormalization(n_units)
            self.norm2 = L.BatchNormalization(n_units)
            self.norm3 = L.BatchNormalization(n_units)
            self.l1 = L.Linear(None, n_units)
            self.l2 = L.Linear(None, n_out)
        self.use_dropout = use_dropout
        self.filter_size = filter_size

    def forward(self, x, train):
        with using_config('train', train):
            x = Variable(x)
            x = self.word_embed(x)
            x = F.dropout(x, ratio=self.use_dropout)
            x = F.expand_dims(x, axis=1)
            x1 = F.relu(self.norm1(self.conv1(x)))
            x1 = F.max_pooling_2d(x1, self.filter_size[0])
            x2 = F.relu(self.norm2(self.conv2(x)))
            x2 = F.max_pooling_2d(x2, self.filter_size[1])
            x3 = F.relu(self.norm3(self.conv3(x)))
            x3 = F.max_pooling_2d(x3, self.filter_size[2])
            x = F.concat((x1, x2, x3), axis=2)
            x = F.dropout(F.relu(self.l1(x)), ratio=self.use_dropout)
            x = self.l2(x)
        return x

Andere Parameter wurden wie folgt eingestellt.

Anzahl der Einheiten Mini-Chargengröße max epoch WeightDecay GradientClipping Optimizer
256 32 30 0.001 5.0 Adam

Ergebnis

Tokenizer mecab+neologd SentencePiece
Beste Genauigkeit 0.68496418 0.668257773

Hmmm ... nicht sehr gut War die Anzahl der Sätze zu gering, da nur die Textdaten für den Zug zum Lernen von Satzteil verwendet wurden? Ich habe versucht, das Modell des Satzstücks unter jawiki (2017/05/01 neueste Version) zu lernen.

Tokenizer mecab+neologd SentencePiece SentencePiece(Lerne mit jawiki)
Beste Genauigkeit 0.68496418 0.668257773 0.758949876

Diesmal sieht es gut aus.

Die Genauigkeit für jede Epoche ist wie folgt.

img.png

Trennung

Ich habe versucht, einige Sätze zu probieren, die tatsächlich geteilt waren

【SentencePiece】
 Es ist zu klein / te / press / press / würzig /.
 Wie / wie / Speer / nimm / ga / Fortsetzung / ku / kana / a / ♪
 Ein anderer / einer / hartnäckig / Spannung / ri /, / ich denke / ich denke /.

 [Satzstück (gelernt auf jawiki)]
 Klein / Sa / Too / Te / Button / Press / Spicy / Spicy / of /.
 Do / no / ku / rai / ya / ri / take / continue / no / kana / a / ♪
 A / und / Bereits / Eins / Hartnäckig / Zhang / Ri /, / Shi / Yo / Ka / To / Think / U /.

【mecab + neologd】
 Klein / zu / te / Knopf / drücken / würzig / von / ist / ist /.
 Wie viel / wie viel / Austausch / ist / geht weiter / / oder / hey / ♪
 Ah / und ein anderer / einer / mein Bestes geben /, / lass uns / oder / denken / denken /.

Selbst mit dem gleichen Satzstück habe ich das Gefühl, dass das Lernen mit Jawiki detaillierter ist. Mecab + neologd ist dem Menschen sinnlich nahe, aber es ist interessant, dass dies nicht bedeutet, dass das Lernen neuronaler Netze zu guten Ergebnissen führt.

von jetzt an

Dieses Mal wurden alle Parametereinstellungen wie die Anzahl der Einheiten festgelegt, daher denke ich, dass es notwendig ist, die richtigen Einstellungen vorzunehmen und mit den besten zu vergleichen. Wie ich im Abschnitt über Trennzeichen ein wenig erwähnt habe, möchte ich auch einen Vergleich mit dem Lernen auf Charakterebene versuchen. Danach habe ich einen Satz verwendet, der sich von den Zugdaten unterscheidet, um Satzstück zu lernen, diesmal Jawiki, aber ich möchte überprüfen, wie andere Sätze die Genauigkeit beeinflussen.

Memo

Zuerst habe ich es unter CentOS (Docker) versucht, aber ich konnte Satzstück nicht gut installieren. Ich habe aufgegeben, aber es scheint, dass es unter CentOS installiert werden kann, wenn die folgenden Elemente zusätzlich zur GitHub-Prozedur enthalten sind.

$ yum install protobuf-devel boost-devel gflags-devel lmdb-devel

Recommended Posts

Dokumentenklassifizierung mit Satzstück
Dokumentklassifizierung mit toch Text von PyTorch
Satzerzeugung mit GRU (Keras)
Dokumentieren Sie Python-Code mit Doxygen
[Textklassifizierung] Ich habe versucht, Faltungsneurale Netze für die Satzklassifizierung mit Chainer zu implementieren
Ich habe versucht, die Satzklassifizierung durch Self Attention mit PyTorch zu implementieren
Maschinelles Lernen mit Python (1) Gesamtklassifizierung
Einfaches Klassifizierungsmodell mit neuronalem Netz
Lernen Sie die Kategorisierung von Dokumenten mit spaCy CLI
Bildklassifizierung mit Weitwinkel-Fundusbilddatensatz
Natürliche Sprache: Doc2Vec Part2 --Dokumentklassifizierung
Ich habe versucht, Sätze mit GPT-2 zu generieren