أعتقد أن هناك خطأ في كيفية حساب تجانس التراجع في NgramModel.
اعتقدت أن حساب التراجع يجب أن يقوم بما يلي ، لنقل السياق "ب":
يقوم هذا بشكل أساسي بتبديل احتمالات unigram لتلك الكلمات التي لم يتم ملاحظتها في سياق bigram ، وتم قياسها بشكل مناسب لملء كتلة الاحتمال المفقودة.
هل فاتني شيء؟ الكود في NgramModel يفعل شيئًا مختلفًا إلى حد ما على ما يبدو ، ولم أستطع فهمه.
أعتقد أنك محق في أن هذا بالفعل خطأ.
إذا افترضنا هذا الإعداد:
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)
يمكننا أن نرى التناقضات بسرعة كبيرة:
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)]
عندما كنت أعمل على NgramModel منذ فترة ، أتذكر أيضًا أنني وجدت الطريقة التي تم بها تنفيذ التراجع مربكة بعض الشيء. الآن بعد أن لم أنظر إليها منذ وقت طويل ، فقدت فهمي الحدسي لكيفية عملها. يبدو لي أننا ندعي أننا نطبق Katz Back-off ، لكن الحسابات مختلفة قليلاً عن تلك الموجودة على ويكيبيديا .
أعتقد أن السبب في ذلك هو أن الوظيفة LidstoneProbDist.discount
استدعاؤها من NgramModel._beta
تأخذ بالفعل التلخيص في الاعتبار ، لكن عليّ النظر في الأمر أكثر.
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)
يبدو لي أن حسابات بيتا هي حيث تسير الأمور بشكل خاطئ ، لأن بيتا على مستوى بيغرام أكبر بكثير من بيتا على مستوى يونيغرام ، مما يجعل النسبة ، ألفا ، إيجابية.
model._beta(('b',))
Out[154]: 0.16666666666666669
model._backoff._beta(())
Out[155]: 0.05063291139240506
model._alpha(('b',))
Out[155]: 3.291666666666667
لقد استبعدت أيضًا أن يكون LidstoneProbDist نفسه هو الذي يعاني من مشكلة:
[(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
سأحاول اكتشاف كيفية ترابط كل هذه الأجزاء مرة أخرى ومعرفة ما إذا كان بإمكاني إصلاح ذلك. على الرغم من أنه إذا كان أي شخص آخر يريد القفز (مثلdesilinguist) ، فأنا أقدر مجموعة أخرى من العيون على هذا.
مرحبا ، وشكرا للتحقق من هذا. فقط بضع أفكار أخرى:
أولاً ، الشيء الوحيد المربك هو المفاهيم المختلفة لـ "الحسم". هناك الخصم الذي تحققه طرق التنعيم المختلفة. على سبيل المثال ، تنعيم Laplacian البسيط (أضف واحدًا) يقلل من احتمالية الكلمات المرصودة وينقل هذه الكتلة إلى الكلمات غير المرصودة. وظيفة الخصم () التي يتم استدعاؤها في وظيفة _beta هي للتسوية التي يقوم بها ProbDist ، وليست (لا أعتقد) ذات صلة بتنعيم التراجع. أعتقد أن فكرة التراجع للخصم تتعلق باحتمالية المجموعة الفرعية من الكلمات "المفقودة" (غير المرصودة) لسياقات مختلفة في نموذج الترتيب الأعلى.
لذلك ، لقد قمت بتعديل الشفرة لأغراضي الخاصة للقيام بما أعتقد أنه الشيء الصحيح ، وقد قمت بمشاركة بعض المقتطفات أدناه. بشكل أساسي ، أحدد المجموعة الفرعية من الكلمات المفقودة في النموذج لسياق معين ، ولهذه المجموعة الفرعية احسب الاحتمالية الإجمالية لهذه الكلمات "المفقودة" والكمية المقابلة في نموذج التراجع. النسبة هي "alpha" ، ولاحظ أن هذه دالة للسياق. أعتقد أن هذا التنفيذ يتوافق مع ما هو موجود في رابط ويكيبيديا الذي تقدمه. أيضًا ، لم تعد وظيفة _beta مستخدمة في حالتي.
آمل أن يكون هذا مفيدًا للمناقشة. شكرا لك مرة أخرى.
# (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
مرحبًا بالجميع ، أردت فقط المشاركة في هذه المناقشة ، والإشارة إلى أن المشكلة أسوأ بكثير من مجرد وجود الاحتمالات التي تفشل في الجمع إلى 1.0
ضع في اعتبارك مثال مثلث الشكل التالي:
#!/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
نعم - هذا الاحتمال الشرطي> 1.0
الجزء السيئ هو أنه كلما تراجعت النماذج ، كلما تضخمت الاحتمالات.
تزداد المشكلة أيضًا سوءًا عندما نضيف المزيد من الأمثلة التدريبية!
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
كما هو الحال ، لا يمكن الاعتماد على NgramModel - على الأقل ليس مع تجانس Lidstone الإضافي.
afourney : أعتقد أن هذا مقصود (يحتوي LidstoneProbDist على سمة SUM_TO_ONE = False
)
afourney أوافق على أنه لا يمكن استخدام NgramModel حقًا حتى يتم إصلاح ذلك. لسوء الحظ ، لم يتح لي الوقت لأخذ طعنة في هذا مؤخرًا.
kmike SUM_TO_ONE خطأ لـ LidstoneProbDist لأنه إذا واجهت حدثًا لم يكن في التوزيع الأولي ولم تقم بتعيين قيمة الحاويات لتكون عدد الأحداث المحتملة ، فلن يتم جمعها إلى واحد. ولكن إذا تم استخدامها بشكل صحيح ، فسيكون مجموعها واحدًا. المشكلة هنا هي حساب NgramModel التجريبي ، وليس LidstoneProbDist نفسه.
kmike : نعم ، لقد لاحظت أن SUM_TO_ONE كان خطأ. كان قلقي هو أن النموذج كان يعيد الاحتمالات الشرطية الفردية (للأحداث الفردية) التي كانت بالفعل أكبر من 1 - قبل دمجها في التجميع.
bcroy أعتقد أن الحل الخاص بك هو النهج الصحيح. وببساطة ، يؤدي _alpha مهمتين هامتين:
ومع ذلك ، سيكون من الجيد أن يقدم NgramModel أيضًا استراتيجية الاستيفاء كبديل لاستراتيجية التراجع. سيمكن هذا من دعم تجانس Jelinek-Mercer أو Witten-Bell - الأخير الذي وجدته بسيطًا ويعمل بشكل جيد. انظر: http://nlp.stanford.edu/~wcmac/papers/20050421-smoothing-tutorial.pdf
هل يمكن لأحد أن يؤكد أن هذا لا يزال خطأ مفتوحًا؟
نعم ، ما زلت أحصل على P (foo | bar، baz) = 2.625
أهلا بالجميع،
هل هناك أي تقدم في هذه القضية؟ هل ما زالت حشرة مفتوحة؟ سأحصل على P (foo | bar، baz) = 2.625 لذا تستمر المشكلة.
أعتقد أن هذه مشكلة مهمة ويجب إصلاحها لأن نماذج اللغة تُستخدم لجميع التطبيقات تقريبًا في البرمجة اللغوية العصبية.
لسوء الحظ ، لم يكن لدي أي وقت للنظر في المشكلات العديدة المتعلقة بـ NgramModel
، ولا أتوقع أن أتمكن من القيام بذلك في أي وقت قريب. حتى يتعامل شخص ما مع هذه الأخطاء ، تمت إزالة NgramModel
من nltk.
دان ، شكرا على الإجابة.
مجرد التحقق من التحديث. يمكنني رؤية بعض المشكلات تم إغلاقها ولكن أريد فقط التأكد من أنها لا تزال بعيدة عن كونها قابلة للاستخدام؟
ZeerakW لسوء الحظ ، كان هناك تقدم ضئيل في نماذج ngram ، ولم يلتزم أحد بمعالجة هذا حتى الآن.
دخول عام 2016 ولم تشهد مشكلة "نموذج ngram" أي تقدم.
يمكننا إغلاق هذا أخيرًا يا رفاق :)
تحديث 2018. تخرج بالفعل وبدأ العمل وما زالت مشكلة Ngram موجودة
صيف!
التعليق الأكثر فائدة
دخول عام 2016 ولم تشهد مشكلة "نموذج ngram" أي تقدم.