5

私はここ数日、このjsスクリプトをPythonコードに変換しようとしています。

これまでの私の実装(ほとんどの場合、blindfull cp、あちこちでいくつかのマイナーな修正):

import random
class markov:
    memory = {}
    separator = ' '
    order = 2

    def getInitial(self):
        ret = []
        for i in range(0, self.order, 1):
            ret.append('')
        return ret

    def breakText(self, txt, cb):
        parts = txt.split(self.separator)
        prev = self.getInitial()
        def step(self):
            cb(prev, self.next)
            prev.shift()#Javascript function.
            prev.append(self.next)
        #parts.forEach(step) # - step is the function above.
        cb(prev, '')

    def learn(self, txt):
        mem = self.memory
        def learnPart(key, value):
            if not mem[key]:
                mem[key] = []
            mem[key] = value
            return mem
        self.breakText(txt, learnPart)

    def step(self, state, ret):
        nextAvailable = self.memory[state] or ['']
        self.next = nextAvailable[random.choice(nextAvailable.keys())]
        if not self.next:
            return ret
        ret.append(next)
        nextState = state.slice(1)
        return self.step(nextState, ret)

    def ask(self, seed):
        if not seed:
            seed = self.genInitial()
        seed = seed + self.step(seed, []).join(self.separator)
        return seed

問題:

  1. 私はjavascriptの知識がまったくありません。

  2. 「マルコフ」クラスオブジェクトへのテキストを「学習」しようとすると[例:a = markov(); a.learn( "sdfg");]次のエラーが発生します: "TypeError:unhashable type:'list'"、 "learnPart"関数の"mem"ディクショナリ、"learn"関数のメンバー。

    それで、これまでの私の質問は、なぜこの例外[リストオブジェクトのTypeError、辞書オブジェクト(ハッシュ可能)を誤って参照する]が発生するのかということです。

提案、方向性、ポイント、一般的なヘルプを事前に感謝します:D

4

3 に答える 3

15

スピーチの記事を書いた男。お役に立ててうれしいです!さて、マルコフ連鎖の私の最初の実装は実際にはPythonで行われたので、この答えはよりPython的な方法でそれを書く方法に焦点を当てます。話しやすいので、注文2マルコフ連鎖を作成する方法を示しますが、もちろん、いくつかの変更を加えて注文Nにすることもできます。

データ構造

jsでは、2つの顕著なデータ構造は、汎用オブジェクトと配列(汎用オブジェクトの拡張)です。ただし、Pythonでは、よりきめ細かい制御を行うための他のオプションがあります。2つの実装の主な違いは次のとおりです。

  • チェーン内の状態は、実際にはタプルです。つまり、要素の量が固定された、不変の順序付けられた構造です。私たちは常にn要素(この場合はn=2)が必要であり、それらの順序には意味があります。

  • リストをラップするdefaultdictを使用すると、メモリの操作が簡単になるため、「状態が存在しないかどうかを確認してからXを実行する」をスキップして、代わりにXを実行できます。

したがって、from collections import defaultdict上部にを貼り付けて、markov.memory定義方法を変更します。

memory = defaultdict(list)

ここでmarkov.getInitial、タプルを返すように変更します(これは、順序2のチェーンを説明していることを思い出してください)。

def getInitial(self):
    return ('', '')

(さらに拡張したい場合は、非常に優れたPythonトリックを使用できますtuple([''] * 2)。同じものが返されます。空の文字列の代わりに、を使用できますNone

使用するものをgenInitial少し変更します。

歩留まりと反復

jsには(まだ)存在しないがPythonには存在する強力な概念は、yield演算子です(詳細な説明については、この質問を参照してください)。

Pythonのもう1つの機能は、その汎用forループです。ジェネレーター(を使用する関数)を含め、ほとんどすべてのものを非常に簡単に調べることができますyield。2つを組み合わせると、次のように再定義できますbreakText

def breakText(self, txt):
    #our very own (ε,ε)
    prev = self.getInitial()

    for word in txt.split(self.separator):
        yield prev, word
        #will be explained in the next paragraph
        prev = (prev[1], word)

    #end-of-sentence, prev->ε
    yield prev, ''

上記の魔法の部分は、prev = (prev[1], word)例によって最もよく説明できます。

>>> a = (0, 1)
>>> a
(0, 1)
>>> a = (a[1], 2)
>>> a
(1, 2)

それが私たちが単語リストを進める方法です。そして今、私たちは、を使用するものに移り、 :breakTextの再定義に移ります。markov.learn

def learn(self, txt):
    for part in self.breakText(txt):
        key = part[0]
        value = part[1]

        self.memory[key].append(value)

私たちの記憶はであるためdefaultdict、キーが存在しないことを心配する必要はありません。

道路脇のおしっこ休憩

OK、チェーンの半分が実装されました。実際に動作するのを見てみましょう。これまでのところ:

from collections import defaultdict

class Markov:
    memory = defaultdict(list)
    separator = ' '

    def learn(self, txt):
        for part in self.breakText(txt):
            key = part[0]
            value = part[1]

            self.memory[key].append(value)

    def breakText(self, txt):
        #our very own (ε,ε)
        prev = self.getInitial()

        for word in txt.split(self.separator):
            yield prev, word
            prev = (prev[1], word)

        #end-of-sentence, prev->ε
        yield (prev, '')

    def getInitial(self):
        return ('', '')

(クラスが小文字で始まるたびにうずくまるので、クラス名をからに変更しましたmarkov) 。Markov私はそれをとして保存し、brain.pyPythonをロードしました。

>>> import brain
>>> bob = brain.Markov()
>>> bob.learn('Mary had a little lamb')
>>> bob.memory
defaultdict(<class 'list'>, {('had', 'a'): ['little'], ('Mary', 'had'): ['a'], ('', ''): ['Mary'], ('little', 'lamb'): [''], ('a', 'little'): ['lamb'], ('', 'Mary'): ['had']})

成功!結果をもっと注意深く見て、正しい結果が得られたことを確認しましょう。

{ ('', ''): ['Mary'],
  ('', 'Mary'): ['had'],
  ('Mary', 'had'): ['a'],
  ('a', 'little'): ['lamb'],
  ('had', 'a'): ['little'],
  ('little', 'lamb'): ['']}

ジップアップ運転する準備はできましたか?私たちはまだこのチェーンを使わなければなりません!

step機能の変更

リメイクに必要なものはすでに満たされていstepます。defaultdictがあるので、random.choiceすぐに使用できます。チェーンの順序がわかっているので、少しごまかすことができます。再帰を(いくらかの悲しみを伴って)連鎖をたどる単一のステップを踏む関数として見れば、再帰を取り除くこともできます(元の記事では私の悪い-悪い名前の関数)。

def step(self, state):
    choice = random.choice(self.memory[state] or [''])

    if not choice:
        return None

    nextState = (state[1], choice)
    return choice, nextState

空のリストについてうめき声を上げるor ['']ので、残念ながら追加しました。random.choice最後に、ロジックの大部分をask(文の実際の構成)に移動します。

def ask(self, seed=False):
    ret = []

    if not seed:
        seed = self.getInitial()

    while True:
        link = self.step(seed)

        if link is None:
            break

        ret.append(link[0])
        seed = link[1]

    return self.separator.join(ret)

はい、少し厄介です。もっといい名前を付けstepて発電機にすることもできたのですが、けん引されている車の中でストーブに火をつけたまま出産しようとしている妊娠中の妻との面会に遅れました!急いだほうがいい!

グランドフィナーレ

しかし、最初に、ボブとの話:

from collections import defaultdict
import random

class Markov:
    memory = defaultdict(list)
    separator = ' '

    def learn(self, txt):
        for part in self.breakText(txt):
            key = part[0]
            value = part[1]

            self.memory[key].append(value)

    def ask(self, seed=False):
        ret = []

        if not seed:
            seed = self.getInitial()

        while True:
            link = self.step(seed)

            if link is None:
                break

            ret.append(link[0])
            seed = link[1]

        return self.separator.join(ret)

    def breakText(self, txt):
        #our very own (ε,ε)
        prev = self.getInitial()

        for word in txt.split(self.separator):
            yield prev, word
            prev = (prev[1], word)

        #end-of-sentence, prev->ε
        yield (prev, '')

    def step(self, state):
        choice = random.choice(self.memory[state] or [''])

        if not choice:
            return None

        nextState = (state[1], choice)
        return choice, nextState

    def getInitial(self):
        return ('', '')

そしてそれをロードします:

>>> import brain
>>> bob = brain.Markov()
>>> bob.learn('Mary had a little lamb')
>>> bob.ask()
'Mary had a little lamb'
>>> bob.learn('Mary had a giant crab')
>>> bob.ask(('Mary', 'had'))
'a giant crab'

もちろん、このコンセプトには改善と拡張の余地があります。しかし、私があなたに答えを与えただけでは、それは楽しいことではありません。

うまくいけば、これは4か月後も役立つでしょう。

于 2013-06-25T17:38:18.633 に答える
1

複雑な答え

ここでの問題learnPartは、リストであるgetInitialの戻り値を辞書のキーとして使用しようとしていることです。リストは変更可能であるため、ハッシュ化できません。つまり、辞書のキーとして使用することはできません。

この行をに追加してみてくださいlearnPart

def learnPart(key, value):
    key = tuple(key) #<-----Try adding this line
    if not mem[key]:
        mem[key] = []
    mem[key] = value
    return mem

しかし、それですべての問題が解決するとは思いません。

簡単な答え

Pythonで書かれたマルコフ連鎖の実装はたくさんあります。Githubをすばやく検索すると、168のプロジェクトが見つかりました: https ://github.com/search?l = Python&q = markov + chain

于 2013-02-11T17:41:34.163 に答える
1

私はコードの簡略化されたバージョンを作成しました:

import re
class Brain():
    H = ''
    def learn(self, txt):
        self.H = txt
    def ask(self,ask):
        H=self.H
        ask = re.compile(r"%s(.*)"%(ask),re.I|re.DOTALL)
        m = ask.search(H)
        print m.group(1)

実行は次のとおりです。

>>> import brain
>>> bob = brain.Brain()
>>> bob.learn('Mary had a little lamb' )
>>> bob.ask('Mary had')
'a little lamb'

これが正確にマルコフ連鎖アルゴリズムではないことに同意しますが、いくつかの利点があります:

私。ask()上記のように生のテキストを提供できます。

ii。コードの行数が少なくなります。

iii。そしてうまくいけば、それは理解しやすいです。

于 2016-04-30T11:42:16.497 に答える