4

ネストされたジェネレーターで次のコードを使用して、テキスト ドキュメントを反復処理し、get_train_minibatch(). テキスト ドキュメントの同じ場所に戻ることができるように、ジェネレーターを永続化 (ピクル) したいと考えています。ただし、ジェネレーターをピクルすることはできません。

  • 簡単な回避策はありますか? ポジションを保存して停止したところから再開できますか? おそらくget_train_example()、シングルトンを作成できるので、いくつかのジェネレーターが横たわっていません。次に、このモジュールで、進行状況を追跡するグローバル変数を作成できますget_train_example()

  • このジェネレーターを永続化できるようにするための、より良い (よりクリーンな) 提案はありますか?

[編集: さらに 2 つのアイデア:

  • ジェネレーターにメンバー変数/メソッドを追加して、generator.tell() を呼び出してファイルの場所を見つけることはできますか? そうすれば、次回ジェネレーターを作成するときに、その場所をシークするように要求できるからです。このアイデアは、すべての中で最も単純に聞こえます。

  • クラスを作成し、ファイルの場所をメンバー変数にしてから、ジェネレーターをクラス内に作成し、生成されるたびにファイルの場所のメンバー変数を更新することはできますか? そうすれば、それがファイルのどこまで進んでいるかを知ることができるからです。

]

コードは次のとおりです。

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 = []
4

6 に答える 6

2

標準の反復子オブジェクトを作成できますが、ジェネレーターほど便利ではありません。イテレータの状態をインスタンスに保存し (ピクルされるように)、次のオブジェクトを返すように next() 関数を定義する必要があります。

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

イテレータ プロトコルは単純で、オブジェクトにメソッドを定義する.next()だけで、for ループなどに渡す必要があります。

Python 3 では、反復子プロトコルは__next__代わりにメソッドを使用します (やや一貫性があります)。

于 2009-12-21T11:09:34.837 に答える
2

次のコードは、多かれ少なかれあなたが望むことをするはずです。最初のクラスは、ファイルのように機能するが、ピクルすることができるものを定義します。(アンピクルすると、ファイルが再度開かれ、ピクルされたときの場所が検索されます)。2 番目のクラスは、単語ウィンドウを生成する反復子です。

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()
于 2009-12-21T18:11:33.577 に答える
0

NLTKのコーパスリーダーの使用を検討することもできます。

-エドワード

于 2009-12-21T18:15:51.853 に答える
0
  1. ジェネレーターを、ジェネレーターコードが__iter__メソッドであるクラスに変換します
  2. ピクルスの処理に、クラスにメソッドを追加__getstate__します。__setstate__ファイルオブジェクトをピクルスにすることはできないことに注意してください。したがって__setstate__、必要に応じてファイルを再度開く必要があります。

ここでは、サンプルコードを使用して、このメソッドについて詳しく説明します。

于 2009-12-22T08:57:16.403 に答える
0

これはオプションではないかもしれませんが、スタックレス Python ( http://stackless.com )では、特定の条件下で関数やジェネレーターなどをピクルすることができます。これはうまくいきます:

foo.py で:

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

foo.txt で:

line1
line2
line3
line4
line5

インタプリタで:

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'

注意すべき点:ファイルの内容をバッファリングし、ファイル オブジェクトを削除する必要があります。これは、ファイルの内容が pickle で複製されることを意味します。

于 2009-12-21T17:34:21.047 に答える
-1

呼び出し可能なオブジェクトを作成してみることができます:

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()

これで、保存する必要があるすべての状態をオブジェクト フィールドに変換し、それらを pickle に公開できます。これは基本的な考え方であり、これが役立つことを願っていますが、まだ自分で試したことはありません。

更新:
残念ながら、私は自分のアイデアを実現できませんでした。提供された例は完全なソリューションではありません。ほら、TrainExampleGenerator状態がありません。この状態を設計し、酸洗いできるようにする必要があります。そして、__call__メソッドはその状態を使用および変更して、オブジェクトの状態によって決定された位置から開始されたジェネレーターを返すようにする必要があります。明らかに、ジェネレーター自体はピクル可能ではありません。しかし、ピクルすることが可能になり、ジェネレーター自体がピクルされたかのようTrainExampleGeneratorに、それを使用してジェネレーターを再作成することができます。

于 2009-12-21T09:33:08.837 に答える