Frage

Ich verwende den folgenden Code, mit verschachtelten Generatoren, ein Textdokument iterieren und Rückkehr Trainingsbeispiele mit get_train_minibatch(). Ich möchte die Generatoren bestehen bleiben (Gurke), so dass ich an der gleichen Stelle im Textdokument zurückbekommen kann. Sie können jedoch nicht Beize Generatoren.

  • Gibt es eine einfache Abhilfe, so dass ich meine Position speichern und starten Sie wieder da, wo ich aufgehört? Vielleicht kann ich get_train_example() ein Singleton machen, so dass ich herumliegen nicht mehrere Generatoren haben. Dann könnte ich eine globale Variable in diesem Modul machen, die verfolgen, wie weit hält entlang get_train_example() ist.

  • Haben Sie einen besseren (Reiniger) Vorschlag, mir zu erlauben, diesen Generator zu beharren?

[edit: Zwei weitere Ideen:

  • Kann ich eine Membervariable / Verfahren zum Generator hinzufügen, so dass ich generator.tell () aufrufen können und den Speicherort der Datei finden? Denn dann habe ich das nächste Mal den Generator erstellen, kann ich es fragen zu diesem Standort zu suchen. Diese Idee klingt das einfachste von allem.

  • Kann ich eine Klasse erstellen, und der Speicherort der Datei haben eine Membervariable sein, und haben dann der Generator innerhalb der Klasse erstellt und aktualisiert den Speicherort der Datei Membervariable jedes Mal, es ergibt? Denn dann kann ich wissen, wie weit in die Datei, um sie es.

]

Hier ist der Code:

def get_train_example():
    for l in open(HYPERPARAMETERS["TRAIN_SENTENCES"]):
        prevwords = []
        for w in string.split(l):
            w = string.strip(w)
            id = None
            prevwords.append(wordmap.id(w))
            if len(prevwords) >= HYPERPARAMETERS["WINDOW_SIZE"]:
                yield prevwords[-HYPERPARAMETERS["WINDOW_SIZE"]:]

def get_train_minibatch():
    minibatch = []
    for e in get_train_example():
        minibatch.append(e)
        if len(minibatch) >= HYPERPARAMETERS["MINIBATCH SIZE"]:
            assert len(minibatch) == HYPERPARAMETERS["MINIBATCH SIZE"]
            yield minibatch
            minibatch = []
War es hilfreich?

Lösung

Der folgende Code sollte tun mehr oder weniger das, was Sie wollen. Die erste Klasse definiert etwas, das wie eine Datei handelt, sondern kann gebeizt werden. (Wenn Sie es unpickle, es wieder öffnet die Datei und sucht nach dem Ort, wo es war, als Sie es gebeizt). Die zweite Klasse ist ein Iterator, Wort Fenster erzeugt.

class PickleableFile(object):
    def __init__(self, filename, mode='rb'):
        self.filename = filename
        self.mode = mode
        self.file = open(filename, mode)
    def __getstate__(self):
        state = dict(filename=self.filename, mode=self.mode,
                     closed=self.file.closed)
        if not self.file.closed:
            state['filepos'] = self.file.tell()
        return state
    def __setstate__(self, state):
        self.filename = state['filename']
        self.mode = state['mode']
        self.file = open(self.filename, self.mode)
        if state['closed']: self.file.close()
        else: self.file.seek(state['filepos'])
    def __getattr__(self, attr):
        return getattr(self.file, attr)

class WordWindowReader:
    def __init__(self, filenames, window_size):
        self.filenames = filenames
        self.window_size = window_size
        self.filenum = 0
        self.stream = None
        self.filepos = 0
        self.prevwords = []
        self.current_line = []

    def __iter__(self):
        return self

    def next(self):
        # Read through files until we have a non-empty current line.
        while not self.current_line:
            if self.stream is None:
                if self.filenum >= len(self.filenames):
                    raise StopIteration
                else:
                    self.stream = PickleableFile(self.filenames[self.filenum])
                    self.stream.seek(self.filepos)
                    self.prevwords = []
            line = self.stream.readline()
            self.filepos = self.stream.tell()
            if line == '':
                # End of file.
                self.stream = None
                self.filenum += 1
                self.filepos = 0
            else:
                # Reverse line so we can pop off words.
                self.current_line = line.split()[::-1]

        # Get the first word of the current line, and add it to
        # prevwords.  Truncate prevwords when necessary.
        word = self.current_line.pop()
        self.prevwords.append(word)
        if len(self.prevwords) > self.window_size:
            self.prevwords = self.prevwords[-self.window_size:]

        # If we have enough words, then return a word window;
        # otherwise, go on to the next word.
        if len(self.prevwords) == self.window_size:
            return self.prevwords
        else:
            return self.next()

Andere Tipps

Sie können ein Standard-Iterator-Objekt erstellen, wird es einfach nicht so bequem sein, als der Generator; Sie müssen die Iterators Zustand auf dem instace speichern (so dass es gebeizt) und definieren eine nächste () Funktion das nächste Objekt zurück:

class TrainExampleIterator (object):
    def __init__(self):
        # set up internal state here
        pass
    def next(self):
        # return next item here
        pass

Das Iterator-Protokoll ist einfach, dass die Definition der .next() Methode für ein Objekt ist alles, was Sie brauchen, um es zu passieren, um für Schleifen etc.

In Python 3, das Iterator-Protokoll verwendet die __next__ Methode statt (etwas konsequente).

Das kann keine Option für Sie sein, aber Stackless Python ( http://stackless.com ) funktioniert können Sie Dinge wie Funktionen und Generatoren beizen, unter bestimmten Bedingungen. Dies funktioniert:

In foo.py:

def foo():
    with open('foo.txt') as fi:
        buffer = fi.read()
    del fi
    for line in buffer.split('\n'):
        yield line

In foo.txt:

line1
line2
line3
line4
line5

Im Interpreter:

Python 2.6 Stackless 3.1b3 060516 (python-2.6:66737:66749M, Oct  2 2008, 18:31:31) 
IPython 0.9.1 -- An enhanced Interactive Python.

In [1]: import foo

In [2]: g = foo.foo()

In [3]: g.next()
Out[3]: 'line1'

In [4]: import pickle

In [5]: p = pickle.dumps(g)

In [6]: g2 = pickle.loads(p)

In [7]: g2.next()
Out[7]: 'line2'

Einige Dinge zu beachten: Sie muss Puffer, um den Inhalt der Datei, und löschen Sie die Datei-Objekt. Dies bedeutet, dass der Inhalt der Datei wird in der Beize dupliziert werden.

  1. Konvertieren des Generators in eine Klasse, in der der Generator-Code ist die __iter__ Methode
  2. Add __getstate__ und __setstate__ Methoden der Klasse, Beizen Handhabung. Denken Sie daran, dass Sie nicht Dateiobjekte Beize kann. So __setstate__ wird wieder zu öffnen, Dateien müssen, falls erforderlich.

Ich beschreibe diese Methode in mehr Tiefe, mit Beispielcode, hier .

Sie können versuchen, aufrufbare Objekt erstellen:

class TrainExampleGenerator:

    def __call__(self):
        for l in open(HYPERPARAMETERS["TRAIN_SENTENCES"]):
            prevwords = []
            for w in string.split(l):
                w = string.strip(w)
                id = None
                prevwords.append(wordmap.id(w))
                if len(prevwords) >= HYPERPARAMETERS["WINDOW_SIZE"]:
                    yield prevwords[-HYPERPARAMETERS["WINDOW_SIZE"]:]

get_train_example = TrainExampleGenerator()

Jetzt können Sie alle Zustand drehen, dass der Bedarf in Objektfelder und setzen sie gerettet werden beizen. Dies ist eine grundlegende Idee und ich hoffe, das hilft, aber ich habe nicht das selbst noch nicht ausprobiert.

UPDATE:
Leider scheiterte, ich meine Idee zu liefern. Vorausgesetzt Beispiel ist nicht vollständige Lösung. Sie sehen, TrainExampleGenerator keinen Staat haben. Sie müssen diesen Zustand entwickeln und zur Verfügung stellen zum Beizen. Und __call__ Methode soll diesen Zustand, so dass verwenden und ändert Generator zurückzukehren, die von der Position von Objektzustand bestimmt gestartet. Offensichtlich Generator selbst nicht Beize-fähig sein. Aber TrainExampleGenerator möglich sein wird, beizen und Sie werden damit neu erstellen Generator in der Lage sein, als ob Generator selbst gebeizt wurde.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top