Nltk: Kesalahan dalam perhitungan pemulusan backoff NgramModel?

Dibuat pada 7 Mar 2013  ·  18Komentar  ·  Sumber: nltk/nltk

Saya yakin ada kesalahan dalam cara penghitungan backoff smoothing di NgramModel.

  • Perhatikan urutan "kata": aaaababaaccbacb dengan kata ['a','b','c']
  • Bangun model bigram (n=2). Untuk kesederhanaan, gunakan pemulusan LidstoneProbDist
  • Khususnya, urutan ini tidak berisi semua bigram yang diawali dengan 'b' atau 'c'. Dengan demikian diperlukan backoff untuk mendapatkan peluang bigram 'bb', 'bc' dan 'ca'
  • Untuk konteks = ['a'], model.prob(w,context) terlihat bagus untuk semua kata dan jumlahkan ke 1
  • Untuk konteks = ['b'], model.prob(w,context) tidak terlihat benar, berjumlah > 1

Saya pikir perhitungan backoff harus melakukan hal berikut, katakanlah untuk konteks 'b':

  • Hitung total probabilitas "hilang" untuk nilai tak terlihat dalam konteks 'b' (mis. bb dan bc), di tingkat bigram, sebut ini Beta2
  • Hitung probabilitas unigram total untuk nilai-nilai tak terlihat ini (yaitu. 'b' dan 'c'), sebut ini Beta1
  • kembali (Beta2 / Beta1) * backoff.prob()

Ini pada dasarnya menukar probabilitas unigram untuk kata-kata yang tidak teramati dalam konteks bigram, diskalakan dengan tepat, untuk mengisi massa probabilitas yang hilang.

Apakah saya melewatkan sesuatu? Kode di NgramModel tampaknya melakukan sesuatu yang agak berbeda, dan saya tidak dapat memahaminya.

language-model

Komentar yang paling membantu

Memasuki tahun 2016 dan isu 'ngram model' belum ada kemajuan.

Semua 18 komentar

Saya yakin Anda benar bahwa ini memang bug.

Jika kita mengasumsikan pengaturan ini:

from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist

word_seq = list('aaaababaaccbacb')
words = ['a', 'b', 'c', '']

est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins=bins)
model = NgramModel(2, word_seq, True, True, est, 4)

Kita bisa melihat dengan cepat perbedaannya:

sum(model.prob(w, ['b']) for w in words)
Out[150]: 2.4583333333333335
sum(model.prob(w, ['a']) for w in words)
Out[151]: 1.0

[(w, model.prob(w, ['b'])) for w in words]
Out[152]: 
[('a', 0.6666666666666667),
 ('b', 0.875),
 ('c', 0.6666666666666667),
 ('', 0.25)]

[(w, model.prob(w, ['a'])) for w in words]
Out[153]: 
[('a', 0.47727272727272724),
 ('b', 0.25),
 ('c', 0.25),
 ('', 0.022727272727272728)]

Ketika saya mengerjakan NgramModel beberapa waktu lalu, saya ingat juga menemukan cara implementasi back-off agak membingungkan. Sekarang saya tidak melihatnya dalam waktu yang lama, saya telah kehilangan pemahaman intuitif yang saya miliki tentang cara kerjanya. Tampaknya bagi saya bahwa kami mengklaim kami menerapkan Katz Back-off, tetapi perhitungannya sedikit berbeda dari yang ada di Wikipedia .

Saya percaya itu karena fungsi LidstoneProbDist.discount dipanggil dari NgramModel._beta sudah memperhitungkan penjumlahan, tetapi saya harus memeriksanya lebih lanjut.

def _alpha(self, tokens):
    return self._beta(tokens) / self._backoff._beta(tokens[1:])

def _beta(self, tokens):
    return (self[tokens].discount() if tokens in self else 1)

Tampak bagi saya bahwa perhitungan beta adalah tempat yang salah, karena beta di tingkat bigram jauh lebih besar daripada beta di tingkat unigram, yang membuat rasio, alfa, positif.

model._beta(('b',))
Out[154]: 0.16666666666666669
model._backoff._beta(())
Out[155]: 0.05063291139240506
model._alpha(('b',))
Out[155]: 3.291666666666667

Saya juga mengesampingkan bahwa LidstoneProbDist itu sendiri yang memiliki masalah:

[(w, model._model[('b',)].prob(w)) for w in words]
Out[159]: 
[('a', 0.6666666666666667),
 ('b', 0.04166666666666667),
 ('c', 0.04166666666666667),
 ('', 0.25)]

sum([model._model[('b',)].prob(w) for w in words])
Out[161]: 1.0

Saya akan mencoba mencari tahu bagaimana semua bagian ini saling berhubungan lagi dan melihat apakah saya dapat memperbaikinya. Meskipun jika ada orang lain yang ingin terjun (seperti @desilinguist), saya akan menghargai pandangan lain tentang ini.

Hai, dan terima kasih telah memeriksa ini. Hanya beberapa pemikiran lagi:

Pertama, satu hal yang membingungkan adalah pengertian yang berbeda dari "diskon". Ada diskon yang dicapai dengan berbagai metode pemulusan. Misalnya, pemulusan Laplacian (tambah satu) sederhana mendiskontokan kemungkinan kata-kata yang diamati dan menggeser massa itu ke kata-kata yang tidak diamati. Fungsi discount() yang dipanggil dalam fungsi _beta adalah untuk perataan yang dilakukan oleh ProbDist, dan tidak (saya rasa) tidak relevan dengan perataan backoff. Saya pikir gagasan backoff tentang diskon berkaitan dengan probabilitas subset kata-kata yang "hilang" (tidak teramati) untuk konteks yang berbeda dalam model tingkat tinggi.

Jadi, saya telah memodifikasi kode untuk tujuan saya sendiri untuk melakukan apa yang menurut saya benar, dan saya telah membagikan beberapa cuplikan di bawah ini. Pada dasarnya, saya mengidentifikasi subset kata yang hilang dalam model untuk konteks tertentu, dan untuk subset itu menghitung probabilitas total untuk kata-kata yang "hilang" ini dan kuantitas yang sesuai dalam model backoff. Rasionya adalah "alpha", dan perhatikan bahwa ini adalah fungsi dari konteksnya. Saya pikir implementasi ini sesuai dengan apa yang ada di tautan Wikipedia yang Anda berikan. Juga, fungsi _beta tidak lagi digunakan dalam kasus saya.

Semoga bermanfaat untuk pembahasannya. Terima kasih lagi.

    # (Code fragment for calculating backoff)

    # Now, for Katz backoff smoothing we need to calculate the alphas
    if self._backoff is not None:
        self._backoff_alphas = dict()

        # For each condition (or context)
        for ctxt in self._cfd.conditions():
            pd = self._model[ctxt] # prob dist for this context

            backoff_ctxt = ctxt[1:]
            backoff_total_pr = 0
            total_observed_pr = 0
            for word in self._cfd[ctxt].keys(): # this is the subset of words that we OBSERVED
                backoff_total_pr += self._backoff.prob(word,backoff_ctxt) 
                total_observed_pr += pd.prob(word)

            assert total_observed_pr <= 1 and total_observed_pr > 0
            assert backoff_total_pr <= 1 and backoff_total_pr > 0

            alpha_ctxt = (1.0-total_observed_pr) / (1.0-backoff_total_pr)

            self._backoff_alphas[ctxt] = alpha_ctxt

# Updated _alpha function, discarded the _beta function
def _alpha(self, tokens):
    """Get the backoff alpha value for the given context
    """
    if tokens in self._backoff_alphas:
        return self._backoff_alphas[tokens]
    else:
        return 1

Hai semua, saya hanya ingin ikut serta dalam diskusi ini, dan untuk menunjukkan bahwa masalahnya jauh lebih buruk daripada sekadar memiliki probabilitas yang gagal menjumlahkan 1,0

Perhatikan contoh trigram berikut:

#!/usr/bin/python
from nltk.model import NgramModel
from nltk.probability import LidstoneProbDist

word_seq = ['foo', 'foo', 'foo', 'foo', 'bar', 'baz']

# Set up a trigram model, nothing special  
est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins)
model = NgramModel(3, word_seq, True, True, est, 3)

# Consider the ngram ['bar', 'baz', 'foo']
# We've never seen this before, so the trigram model will fall back
context = ('bar', 'baz',)
word = 'foo'
print "P(foo | bar, baz) = " + str(model.prob(word,context))

# Result:
# P(foo | bar, baz) = 2.625

Yup -- probabilitas bersyarat ini > 1,0

Bagian yang buruk adalah bahwa semakin banyak model mundur, semakin besar kemungkinannya.

Masalahnya juga menjadi lebih buruk saat kami menambahkan lebih banyak contoh pelatihan!

word_seq = ['foo' for i in range(0,10000)]
word_seq.append('bar')
word_seq.append('baz')

est = lambda freqdist, bins: LidstoneProbDist(freqdist, 0.2, bins)
model = NgramModel(3, word_seq, True, True, est, 3)

# Consider the ngram ['bar', 'baz', 'foo']
# We've never seen this before, so the trigram model will fall back
context = ('bar', 'baz',)
word = 'foo'
print "P(foo | bar, baz) = " + str(model.prob(word,context))

# Result:
P(foo | bar, baz) = 6250.125

Seperti berdiri, NgramModel tidak dapat diandalkan - setidaknya tidak dengan smoothing Lidstone aditif.

@afourney : Saya percaya ini dimaksudkan (LidstoneProbDist memiliki atribut SUM_TO_ONE = False )

@afourney Saya setuju bahwa NgramModel tidak dapat digunakan sampai ini diperbaiki. Sayangnya, saya tidak punya waktu untuk membahas ini baru-baru ini.

@kmike SUM_TO_ONE Salah untuk LidstoneProbDist karena jika Anda menemukan peristiwa yang tidak ada dalam distribusi awal dan Anda tidak menetapkan nilai bins menjadi jumlah kemungkinan peristiwa, maka itu tidak akan berjumlah satu. Tetapi jika digunakan dengan benar, itu memang akan berjumlah satu. Masalahnya di sini adalah perhitungan beta NgramModel, bukan LidstoneProbDist itu sendiri.

@kmike : Ya, saya perhatikan bahwa SUM_TO_ONE salah. Kekhawatiran saya adalah bahwa model itu mengembalikan probabilitas bersyarat individu (untuk peristiwa tunggal) yang sudah lebih besar dari 1 - sebelum memasukkannya ke dalam penjumlahan.

@bcroy Saya pikir solusi Anda adalah pendekatan yang tepat. Secara sederhana, _alpha melakukan dua tugas penting:

  1. Ini menormalkan kembali model backoff untuk konteks yang diberikan sehingga mengecualikan kata-kata yang sudah diperhitungkan oleh model tingkat tinggi saat ini.
  2. Ini menskalakan model backoff yang dinormalisasi ulang agar "sesuai" dengan probabilitas "hilang"/didiskon dari _model saat ini.

Meskipun demikian, alangkah baiknya jika NgramModel juga menawarkan strategi interpolasi sebagai alternatif dari strategi backoff. Ini akan memungkinkan dukungan untuk perataan Jelinek-Mercer atau Witten-Bell -- yang terakhir menurut saya sederhana, dan bekerja dengan cukup baik. Lihat: http://nlp.stanford.edu/~wcmac/papers/20050421-smoothing-tutorial.pdf

Dapatkah seseorang mengkonfirmasi bahwa ini masih merupakan bug yang terbuka?

Ya, saya masih mendapatkan P(foo | bar, baz) = 2,625

Hai semuanya,

Apakah ada kemajuan dengan masalah ini? Apakah ini masih bug yang terbuka? Saya mendapatkan P(foo | bar, baz) = 2,625 sehingga masalah berlanjut.

Saya pikir ini adalah masalah penting dan harus diperbaiki karena model bahasa digunakan untuk hampir semua aplikasi di NLP.

Sayangnya, saya tidak punya waktu untuk melihat banyak masalah dengan NgramModel , dan saya tidak melihat diri saya dapat melakukannya dalam waktu dekat. Sampai seseorang mengatasi bug ini, NgramModel telah dihapus dari nltk.

Dan, terima kasih atas jawabannya.

Hanya memeriksa untuk pembaruan. Saya dapat melihat beberapa masalah telah ditutup tetapi hanya ingin memastikan itu masih jauh dari dapat digunakan?

@ZeerakW Sayangnya, ada sedikit kemajuan dalam model ngram, dan belum ada yang berkomitmen untuk mengatasi ini.

Memasuki tahun 2016 dan isu 'ngram model' belum ada kemajuan.

Teman-teman akhirnya kita bisa menutup ini :)

Pembaruan 2018. Sudah lulus dan mulai bekerja dan masih ada masalah Ngram

Musim panas!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat