[PYTHON] Ich habe den Code für die japanische Satzgenerierung mit DeZero geschrieben

1. Zuallererst

Im April dieses Jahres wurde ** "Deep Learning3 Framework von Grund auf neu" ** veröffentlicht. Ich habe ** Zero Work ** als ** 1.2 ** gelesen und viel gelernt, deshalb habe ich mich entschlossen, diesmal die Framework Edition herauszufordern.

Also habe ich kürzlich ein Buch gekauft, aber bevor ich Schritt für Schritt anfing zu studieren, habe ich beschlossen, Code zu schreiben, um einen schnellen Überblick über das ** Framework ** zu erhalten.

Weitere Informationen finden Sie in der ** Bibliothek ** und im ** Beispiel ** von ** DeZero ** unter Github. Ich habe beim Durchsuchen ** dieses ** einen einfachen Code für die Verarbeitung natürlicher Sprache in Google Colab geschrieben, daher werde ich ihn als Memorandum belassen.

Der von mir erstellte ** Google Colab-Code ** wird auf ** Github ** veröffentlicht. Wenn Ihnen also [** dieser Link **] gefällt (https://github.com/cedro3/google_colab/blob/master/generate_japanese_text) Klicken Sie auf .ipynb), um es zu verschieben.

2. Japanischer Datensatz Neko-Klasse

Als ich versuchte, Japanisch in natürlicher Sprache zu verarbeiten, hielt ich es für praktisch, wenn es etwas gab, das einfach zu verwenden war, wie z. B. ** MNIST ** für die Bildverarbeitung, also machte ich etwas Ähnliches.

Laden Sie für die zu erstellende ** Datensatzklasse ** ** "Ich bin eine Katze" ** von Aozora Bunko herunter. Nachdem Sie den unnötigen Teil gelöscht haben, schreiben Sie mit ** janome **, erstellen Sie ein Wörterbuch und einen Korpus und erstellen Sie dann ** Zeitreihendaten ** und ** die nächsten richtigen Antwortdaten **.

Installieren Sie als vorläufige Vorbereitung das ** Framework dezero ** mit ! Pip install dezero und die ** morphologische Analysebibliothek janome ** mit! Pip install janome.

Setzen Sie den Klassennamen des Datensatzes auf ** Neko **, erben Sie ** Dataset-Klasse ** gemäß der Methode von dezero, schreiben Sie ** Verarbeitungsinhalt ** in def prepare () und verarbeiten Sie dann ** Schreiben Sie die gewünschte Funktion ** in.

import numpy as np
import dezero
from dezero.datasets import Dataset
from dezero.utils import get_file, cache_dir
import zipfile
import re
from janome.tokenizer import Tokenizer

class Neko(Dataset):
    
    def prepare(self):
        url = 'https://www.aozora.gr.jp/cards/000148/files/789_ruby_5639.zip'
        file = get_file(url)  
        data = self.unzip(cache_dir + '/' + '789_ruby_5639.zip')  
        self.text = self.preprocess(cache_dir + '/' + 'wagahaiwa_nekodearu.txt')
        self.wakati = self.keitaiso(self.text)
        self.corpus, self.word_to_id, self.id_to_word = self.process(self.wakati)
        self.data = np.array(self.corpus[:-1])
        self.label = np.array(self.corpus[1:])
    
    def unzip(self, file_path):
        with zipfile.ZipFile(file_path) as existing_zip:
            existing_zip.extractall(cache_dir)
            
    def preprocess(self, file_path):
        binarydata = open(file_path, 'rb').read()
        text = binarydata.decode('shift_jis')        
                   
        text = re.split(r'\-{5,}', text)[2]  #Header löschen
        text = re.split('Unteres Buch:',text)[0]   #Fußzeile entfernen
        text = re.sub('|', '', text)  # |Löschen
        text = re.sub('[.+?]', '', text)  #Eingabenotiz löschen
        text = re.sub(r'《.+?》', '', text)  #Rubinentfernung
        text = re.sub(r'\u3000', '', text)  #Leerzeichen entfernen
        text = re.sub(r'\r\n', '', text)  #Zeilenumbrüche entfernen
        text = text[1:]  #Löschen Sie das erste Zeichen (Anpassung)
        return text
 
    def keitaiso(self, text):
        t = Tokenizer()
        output = t.tokenize(text, wakati=True)
        return output
     
    def process(self, text):
        # word_to_id, id_to_Ein Wort erstellen
        word_to_id, id_to_word = {}, {}
        for word in text:
            if word not in word_to_id:
                new_id = len(word_to_id)
                word_to_id[word] = new_id
                id_to_word[new_id] = word

        #Korpus erstellen
        corpus = np.array([word_to_id[W] for W in text])
        return corpus, word_to_id, id_to_word

Der ** Konstruktor ** (bei def __init __ ()) der ** geerbten ** Dataset-Klasse ** sagt self.prepare (), daher ist die Neko-Klasse eine ** Instanz. Dann wird def prepare () ** funktionieren **.

def prepare () verwendetget_file (url)in der dezero-Bibliothek, um die Datei von der angegebenen url herunterzuladen und in cache_dir zu speichern. Für Google Colab ist "cache_dir" "/ root / .dezero".

Danach werden vier Funktionen nacheinander aufgerufen, um die Verarbeitung durchzuführen. Ersetzen Sie schließlich ** corpus ** gemäß der Methode durch self.data (Zeitreihendaten) und self.label (nächstbeste Antwortdaten).

Die Variablen "text, wakati, corpus, word_to_id, id_to_word" werden jeweils "self" gegeben, so dass sie als ** Attribute ** aufgerufen werden können, sobald die Neko-Klasse ** instanziiert ** ist. ..

def unzip () ist eine Funktion, die die heruntergeladene ** Zip-Datei ** entpackt. def preprocess () ist eine Funktion, die die dekomprimierte Datei liest und den Text zurückgibt, wobei ** unnötige Teile wie Ruby und Zeilenumbrüche ** entfernt werden. def keitaiso () ist eine Funktion, die Text morphologisch analysiert und ** Fraktionierung ** zurückgibt. def process () ist eine Funktion, die ** Wörterbücher ** und ** Korpus ** aus Brüchen erstellt.

Bewegen wir es tatsächlich.

3. Versuchen Sie, die Neko-Klasse auszuführen

スクリーンショット 2020-06-15 08.33.26.png ** Instanziiere ** die Neko-Klasse mit neko = Neko (), um die Datei herunterzuladen und ** den Prozess zu starten **. Die Fertigstellung dauert einige zehn Sekunden, da die Verarbeitung von Janomes Division etwas Zeit in Anspruch nimmt. Wenn Sie fertig sind, lassen Sie es uns sofort verwenden. スクリーンショット 2020-06-14 19.08.47.png neko.text kann ** Text ** anzeigen, neko.wakati kann ** Brüche ** anzeigen und neko.corpus kann ** Korpus ** anzeigen. Der Text ist so genannt fest, die Unterteilung ist eine wortweise Liste und der Korpus ist die Zahl vom Anfang des Unterteilungswortes (keine Vervielfältigung). Schauen wir uns übrigens das Wörterbuch an. スクリーンショット 2020-06-14 19.16.38.png neko.waord_to_id [] ist ein Wörterbuch, das ** Wörter in Wörter umwandelt **, und neko.id_to_word [] ist ein Wörterbuch, das Zahlen ** in Wörter ** umwandelt. Schauen wir uns die Trainingsdaten an. スクリーンショット 2020-06-14 19.22.01.png Sie können sehen, dass neko.data und neko.label um eins versetzt sind. Schauen wir uns zum Schluss die Länge der Daten und die Anzahl der Wörter im Wörterbuch an. スクリーンショット 2020-06-14 19.54.03.png  Die ** Datenlänge ** beträgt 205.815 und die Anzahl der Wörter im Wörterbuch ** vovab_size ** beträgt 13.616.

Schreiben wir nun den Code des Hauptteils.

4. Körpercode

import numpy as np
import dezero
from dezero import Model
from dezero import SeqDataLoader
import dezero.functions as F
import dezero.layers as L
import random
from dezero import cuda 
import textwrap

max_epoch = 70
batch_size = 30 
vocab_size = len(neko.word_to_id)  
wordvec_size = 650  
hidden_size = 650
bptt_length = 30  

class Lstm_nlp(Model):
    def __init__(self, vocab_size, wordvec_size, hidden_size, out_size):
        super().__init__()
        self.embed = L.EmbedID(vocab_size, wordvec_size)
        self.rnn = L.LSTM(hidden_size)
        self.fc = L.Linear(out_size)

    def reset_state(self):  #Zustand zurücksetzen
        self.rnn.reset_state()

    def __call__(self, x):  #Beschreiben Sie den Verbindungsinhalt der Ebene
        y = self.embed(x) 
        y = self.rnn(y)
        y = self.fc(y)
        return y

Das Modell hat eine einfache Struktur aus ** Einbettungsschicht + LSTM-Schicht + Lineare Schicht **. Geben Sie die EmbedID als wortbasierte Zahl (Ganzzahl) ein.

Die Größe der Worteinbettungsmatrix für EmbedID beträgt ** vocal_size x wordvec_size **, also 13616 x 650. Die hidden_size von LSTM ist 650, was mit wordvec_size identisch ist. Und die lineare Ausgabegröße "out_size" ist 13616, was mit vocab_size identisch ist.

Beschreibe ** den Verbindungsinhalt jeder Schicht ** in def __call __ (). Der hier beschriebene Inhalt kann aufgerufen werden, indem der erstellten Instanz wie eine Funktion Argumente gegeben werden. Wenn Sie beispielsweise mit "model = Lstm_nlp (....)" instanziieren, können Sie den Teil von "def __call __ ()" mit "y = model (x)" verschieben. Mit anderen Worten kann damit eine sogenannte Vorhersage erreicht werden. Das ist klug.

model = Lstm_nlp(vocab_size, wordvec_size, hidden_size, vocab_size)  #Modellgenerierung
dataloader = SeqDataLoader(neko, batch_size=batch_size)  #Generierung von Datenladern
seqlen = len(neko)
optimizer = dezero.optimizers.Adam().setup(model)  #Die Optimierungsmethode ist Adam

#Anwesenheits- / Abwesenheitsbeurteilung und Verarbeitung der GPU
if dezero.cuda.gpu_enable:  #Wenn die GPU aktiviert ist, gehen Sie wie folgt vor
    dataloader.to_gpu()  #Datenlader zur GPU
    model.to_gpu()  #Modell zur GPU

Der Datenlader verwendet "SeqDataLoader" für Zeitreihendaten. Da sich die Reihenfolge der Zeitreihendaten beim Mischen ändert, wird das Verfahren zum Extrahieren mehrerer Daten durch Teilen der Zeitreihendaten in regelmäßigen Intervallen angewendet.

Wenn die GPU verfügbar ist, ist if dezero.cuda.gpu_enable: True. In diesem Fall werden der Datenlader und das Modell an die GPU gesendet.

#Lernschleife
for epoch in range(max_epoch):
    model.reset_state()
    loss, count = 0, 0

    for x, t in dataloader:
        y = model(x)  #Vorwärtsausbreitung

        #Erscheinungsgrad des nächsten Wortes y(vocab_Größe dimensionale Vektor)Ist softmax verarbeitet und korrekt(Ein heißer Vektor)Verlustberechnung mit
        #Die Eingabe t ist jedoch die Indexnummer, in der 1 des einen heißen Vektors steht.(ganze Zahl)
        loss += F.softmax_cross_entropy_simple(y, t)  
        count += 1

        if count % bptt_length == 0 or count == seqlen:
            model.cleargrads()  #Initialisierung der Differenzierung
            loss.backward()  #Backpropagation
            loss.unchain_backward()  #Kehren Sie zum Berechnungsdiagramm zurück und trennen Sie die Verbindung
            optimizer.update()  #Gewichtsaktualisierung
    avg_loss = float(loss.data) / count
    print('| epoch %d | loss %f' % (epoch + 1, avg_loss))

    #Satzerzeugung
    model.reset_state()  #Zustand zurücksetzen
    with dezero.no_grad():  #Gewichte nicht aktualisieren
         text = []
         x = random.randint(0,vocab_size)  #Wählen Sie zufällig die erste Wortnummer
         while len(text)  < 100:  #Wiederholen Sie bis 100 Wörter
               x = np.array(int(x))
               y = model(x)  #y ist der Grad des Auftretens des nächsten Wortes(vocab_Größe dimensionale Vektor)
               p = F.softmax_simple(y, axis=0)  #Mit softmax multiplizieren, um die Erscheinungswahrscheinlichkeit zu erhalten
               xp = cuda.get_array_module(p)  #XP mit GPU=xp ohne cp=np
               sampled = xp.random.choice(len(p.data), size=1, p=p.data)  #Zahlen unter Berücksichtigung der Wahrscheinlichkeit des Auftretens(Index)Wählen
               word = neko.id_to_word[int(sampled)]  #Zahlen in Wörter umwandeln
               text.append(word)  #Fügen Sie dem Text ein Wort hinzu
               x = sampled  #Stellen Sie Sampling auf den nächsten Eingang ein
         text = ''.join(text)
         print(textwrap.fill(text, 60))  #Anzeige mit einer Pause von 60 Zeichen

Es ist eine Lernschleife. ** Vorwärtsausbreitung ** mit y = Modell (x) und Verlust mit Verlust + = F.softmax_cross_entropy_simple (y, t) berechnen.

Zu diesem Zeitpunkt ist y ein ** Vektor ** (vocab_size-Dimension), der den ** Erscheinungsgrad ** des nächsten Wortes darstellt, der mit softmax multipliziert wird, um die ** Erscheinungswahrscheinlichkeit ** zu erhalten, und ** einen heißen nächsten. Der Verlust wird aus den richtigen Antwortdaten berechnet **. Die Eingabe t ist jedoch die ** Zahl (Ganzzahl) ** des One-Hot-Vektors, in dem 1 steht.

Wenn count ein ganzzahliges Vielfaches von bptt_length ist oder mit if count% bptt_length == 0 oder count == seqlen: zum Ende geht, wird eine Backpropagation durchgeführt und das Gewicht aktualisiert.

Als nächstes werden 100 Wörter für jede Eopch generiert. Verwenden Sie zuerst model.reset_state (), um den Status zurückzusetzen, und with dezero.no_grad ():, um die Gewichte unverändert zu lassen. Dann wird mit "x = random.randint (0, voc_size)" der Anfangswert des Wortes zufällig aus einer ganzen Zahl von 0 bis vocal_size bestimmt und das nächste Wort vorhergesagt. Ein Satz wird erzeugt, indem eine weitere Vorhersage basierend auf dem vorhergesagten Wort wiederholt wird.

p = F.softmax_simple (y, axis = 0) multipliziert y mit softmax, um die Eintrittswahrscheinlichkeit des nächsten Wortes zu ermitteln, und xp.random.choice () folgt zufällig der Eintrittswahrscheinlichkeit. Ist ausgewählt.

Der Grund, warum xp.random.choice () mit ** xp ** beginnt, ist ** np ** (numpy), wenn das erste Zeichen von der CPU verschoben wird, und ** cp, wenn es von der GPU verschoben wird. Dies liegt daran, dass es in ** (cupy) geändert werden muss. Beurteilen Sie daher nach "xp = cuda.get_array_module (p)" und ersetzen Sie die CPU durch xp = np und die GPU durch xp = cp.

Bewegen wir nun die Haupteinheit.

5. Versuchen Sie, den Hauptteilcode zu verschieben

Wenn Sie den Hauptteilcode ausführen, lernt er die Wortreihenfolge "Ich bin eine Katze" und generiert für jede Epoche einen Satz. Es dauert ungefähr 1 bis 2 Minuten pro Epoche. Nach dem Lernen sieht es so aus. スクリーンショット 2020-06-15 17.00.54.png Es macht auch Spaß zu sehen, wie die Sätze nach und nach mehr so werden.

6. Zusammenfassung

Der Eindruck, den Code durch Nachahmung zu schreiben, ist, dass es sich um ein ** einfaches Framework ** handelt, das in ** ganz Python ** geschrieben ist, sodass der Inhalt leicht zu verstehen ist ** und der Code leicht zu schreiben ist, aber der Freiheitsgrad hoch ist * Ich hatte einen guten Eindruck von *. In dieser Zeit möchte ich den Inhalt des DeZero-Frameworks untersuchen.

Recommended Posts

Ich habe den Code für die japanische Satzgenerierung mit DeZero geschrieben
Ich habe den Code für Gibbs Sampling geschrieben
Ich habe gerade das Originalmaterial für den Python-Beispielcode geschrieben
Ich habe versucht, Sätze mit GPT-2 zu generieren
Ich habe vorerst mit Floydhub gespielt
Code für TensorFlow MNIST Anfänger / Experte mit japanischen Kommentaren
Ich habe dir geschrieben, dass du das Signal mit Go sehen sollst
Ich habe versucht, den für TensorFlow geschriebenen Code nach Theano zu portieren
Ich habe GP mit Numpy geschrieben
Ich war 2 Minuten lang süchtig nach Python-Debugger-PDF
Ich habe Python auf Japanisch geschrieben
Ich habe die grundlegende Grammatik von Python in Jupyter Lab geschrieben
Satzerzeugung mit GRU (Keras)
Ich möchte mit Numpy die japanische Flagge in die Palau-Flagge ändern
Überprüfen Sie den Code mit flake8
Ich habe die Grundoperation von matplotlib in Jupyter Lab geschrieben
Lassen Sie das japanische BERT-Modell den Zentraltest und die Satzgenerierung durchführen
Ich habe den Code geschrieben, um den Brainf * ck-Code in Python zu schreiben
[Textklassifizierung] Ich habe versucht, Faltungsneurale Netze für die Satzklassifizierung mit Chainer zu implementieren
[Lass uns mit Python spielen] Ziel ist die automatische Satzgenerierung ~ Abschluss der automatischen Satzgenerierung ~
Überprüfen Sie den Speicherschutz von Linux Kern mit Code für ARM
Ich habe die Grundoperation von Pandas im Jupyter Lab geschrieben (Teil 1)
Vorerst möchte ich jede Datei mit ffmpeg konvertieren !!
Ich habe die grundlegende Operation von Pandas im Jupyter Lab geschrieben (Teil 2).
Ich habe versucht, das TensorFlow-Tutorial mit Kommentaren auszuführen (_TensorFlow_2_0_Einführung für Anfänger).
QR-Code mit CNN entschlüsseln
Ich mochte den Tweet mit Python. ..
Ich kann kein Japanisch mit Pyperclip verwenden
Ich habe die Warteschlange in Python geschrieben
[PyTorch] Japanische Satzgenerierung mit Transformer
Ich habe den Stack in Python geschrieben
Ich habe das Sudachi-Synonymwörterbuch mit Pandas gelesen und versucht, nach Synonymen zu suchen
AtCoder-Autor Ich habe ein Skript geschrieben, das Wettbewerbe für jeden Autor zusammenfasst
Ich habe versucht, den Authentifizierungscode der Qiita-API mit Python abzurufen.
[Lass uns mit Python spielen] Ziel ist die automatische Satzgenerierung ~ Morphologische Analyse durchführen ~
Ich habe die Bibliothek mit Visual Studio Code installiert, konnte sie jedoch nicht importieren
Impressionen und Memorandum bei der ersten Arbeit mit VScode
Ich habe die Geschwindigkeit der Listeneinschlussnotation für und während mit Python2.7 gemessen.
VS Code friert ein und der PC stürzt ab, wenn der Server mit go gestartet wird
Ich habe versucht, die Genauigkeit der japanischen BERT- und der japanischen Distil-BERT-Satzklassifizierung mit PyTorch & Einführung der BERT-Technik zur Verbesserung der Genauigkeit zu vergleichen
Fragen Sie mit dem Befehl bc nach Pi
Ich habe zum ersten Mal Tensorflow ausprobiert
Suchen Sie nach Dateien mit der angegebenen Erweiterung
[Scikit-learn] Ich habe mit der ROC-Kurve gespielt
Japanisches Diagramm mit VS Code + matplotlib anzeigen
Schriftliche Auswahlsortierung in C.
Ich habe einen Unit-Test für verschiedene Sprachen geschrieben
Die dritte Nacht der Runde mit für
Ich habe versucht, mit Pillow mit dem Bild zu spielen
Die zweite Nacht der Runde mit für
[Python-Anfänger] Ich habe die Artikel gesammelt, die ich geschrieben habe
Ich habe den Gleitflügel in der Schöpfung geschrieben.
Ich kann das Paket nicht mit pip installieren.
[Mit japanischem Modell] Satzvektormodell empfohlen für Personen, die 2020 natürliche Sprache verarbeiten
Irgendwie hat der Code, den ich geschrieben habe, funktioniert und ich war beeindruckt, also werde ich ihn veröffentlichen
Ich habe eine Animation geschrieben, bei der das lineare System mit tödlich schmutzigem Code schrumpft
Ich habe es mit Visual Studio Code (hauptsächlich für Python) angepasst, daher werde ich es zusammenfassen
Suchen Sie im aktuellen Verzeichnis nach Dateien mit dem Zeilenvorschubcode CR + LF
Eine Geschichte, von der ich sehr überzeugt war, als ich den Code für das Monty Hall-Problem schrieb und die Gewinnrate berechnete