स्वागत है
एक हाथों-हाथ NLP पाठ में आपका स्वागत है।
आप शुरुआत से एक काम करने वाला अंग्रेजी स्टेमर बनाने जा रहे हैं: एक एल्गोरिदम जो शब्दों को उनके मूल रूप तक छीन देता है।
अंत तक, आपके पास एक वास्तविक, परीक्षित एल्गोरिदम होगा जो शब्दों को रूपांतरित करता है जैसे running → run, happiness → happi, & organizational → organ।
आप अपने स्टेमर के लिए इकाई परीक्षण, एकीकरण परीक्षण, और कार्यात्मक परीक्षण भी लिखेंगे: क्योंकि एक अपरीक्षित एल्गोरिदम सिर्फ एक अनुमान है।
स्टेमिंग क्या है?
समस्या
खोज इंजनों को एक मौलिक समस्या का सामना करना पड़ता है: एक उपयोगकर्ता "running" के लिए खोज करता है लेकिन दस्तावेज़ में "run" या "runs" या "runner" होता है। ये सभी एक ही अवधारणा हैं: लेकिन ये विभिन्न स्ट्रिंग हैं।
स्टेमिंग विभक्त शब्दों को एक सामान्य आधार रूप (स्टेम) तक कम करता है। इसे वास्तविक शब्द होने की आवश्यकता नहीं है: इसे सिर्फ सुसंगत होने की आवश्यकता है।
| शब्द | स्टेम |
|---|---|
| running | run |
| runs | run |
| runner | runner |
| happiness | happi |
| happily | happi |
| happy | happi |
ध्यान दें कि happi एक वास्तविक अंग्रेजी शब्द नहीं है। कोई समस्या नहीं। स्टेमिंग अर्थ के बारे में नहीं, समूहीकरण के बारे में है। जब तक happiness, happily, & happy सभी एक ही स्टेम में परिणत होते हैं, खोज & पुनः प्राप्ति में सुधार होता है।
ज़ेलिग हैरिस और वितरणात्मक विश्लेषण
कम्प्यूटेशनल स्टेमिंग की उत्पत्ति
1955 में, भाषाविद् ज़ेलिग हैरिस ने From Phoneme to Morpheme प्रकाशित किया, जिसमें शब्दों में अर्थपूर्ण इकाइयों (आकृतिविज्ञान) की सीमाएँ खोजने की एक विधि का वर्णन किया गया था।
उनकी अंतर्दृष्टि वितरणात्मक थी: यदि आप एक बड़े अंग्रेजी शब्दों के कॉर्पस को देखते हैं, तो एक स्टेम & एक प्रत्यय के बीच की सीमा एक सांख्यिकीय संकेत के रूप में दिखाई देती है।
उत्तराधिकारी विविधता विधि
किसी भी शब्द के उपसर्ग के लिए, कॉर्पस में इसके बाद कितने विभिन्न वर्ण दिखाई देते हैं, इसकी गणना करें। हैरिस ने इसे उत्तराधिकारी विविधता कहा।
एक कॉर्पस में उपसर्ग "work" पर विचार करें जिसमें शामिल है: worked, worker, working, works, workshop।
| उपसर्ग | क्या अनुसरण करता है | उत्तराधिकारी विविधता |
|---|---|---|
| w | o | 1 |
| wo | r | 1 |
| wor | k | 1 |
| work | e, i, s, sh | 4 |
| worke | d, r | 2 |
"work" के बाद, चार अलग-अलग वर्ण अनुसरण कर सकते हैं: विविधता में एक वृद्धि। यह वृद्धि एक आकृतिविज्ञान सीमा को चिह्नित करती है। स्टेम work है और इसके बाद सब कुछ एक प्रत्यय है।
यह 1955 में क्रांतिकारी था। कोई भाषाई नियम नहीं, कोई शब्दकोश नहीं: बस गिनती। हैरिस ने दिखाया कि भाषा की संरचना वितरण के माध्यम से स्वयं को प्रकट करती है।
उत्तराधिकारी विविधता को समझना
हैरिस की विधि किसी भी भाषा पर काम करती है। आपको व्याकरण को जानने की आवश्यकता नहीं है: आँकड़े आकृतिविज्ञान सीमाओं को प्रकट करते हैं।
व्यावहारिक रूप से, शुद्ध उत्तराधिकारी विविधता को एक बड़े कॉर्पस और सावधानीपूर्वक शिखर पहचान की आवश्यकता होती है। बाद के शोधकर्ता: लोविन्स (1968), पोर्टर (1980): दृष्टिकोण को नियम-आधारित प्रत्यय स्ट्रिपिंग में सरल किया: एक कॉर्पस से उत्तराधिकारी विविधता की गणना करने के बजाय, उन्होंने प्रत्यय नियमों को सीधे एन्कोड किया।
आज आप एक नियम-आधारित प्रत्यय स्ट्रिपर बनाएंगे जो हैरिस की अंतर्दृष्टि से प्रेरित है। आप प्रत्ययों को स्पष्ट रूप से परिभाषित करेंगे, फिर उन्हें शब्दों से छीन देंगे। यह वह तरीका है जिस तरह से अधिकांश उत्पादन स्टेमर काम करते हैं।
आपका पहला प्रत्यय स्ट्रिपर
आइए कोड करें
सरल शुरू करें। एक फ़ंक्शन stem लिखें जो एक शब्द लेता है & इन प्रत्ययों को छीन देता है (इस क्रम में):
1. -ing (running → runn)
2. -ed (walked → walk)
3. -ly (quickly → quick)
4. -s (cats → cat)
नियम:
- पहले शब्द को लोअरकेस में परिवर्तित करें
- केवल एक प्रत्यय छीन लें (ऊपर के क्रम में पहला मिलान)
- केवल छीन लें यदि शेष स्टेम कम से कम 3 वर्ण लंबा हो
- स्टेम को वापस करें
उदाहरण:
def stem(word):
word = word.lower()
# your suffix stripping logic here
return word
किनारे के मामलों को संभालना
स्टेमर को स्मार्ट बनाना
आपके मूल स्ट्रिपर की एक समस्या है: running → runn & hoping → hop। हमें दो परिष्कार की आवश्यकता है:
1. दोहरे व्यंजन सफाई: यदि -ing या -ed को छीन देने से अंत में एक दोहरा व्यंजन रहता है (runn की तरह), अंतिम अक्षर को हटाएँ → run
2. मूक-e बहाली: यदि -ing को छीन देने से व्यंजन में समाप्त होने वाला स्टेम रहता है (स्वर नहीं), & मूल के पास एक मूक e हो सकता था (hop from hoping की तरह), e को वापस जोड़ें → hope
मूक-e नियम के लिए, इसे सरल रखें: -ing को छीन देने के बाद, यदि स्टेम 3+ वर्ण है, व्यंजन में समाप्त होता है, & दूसरे-से-अंतिम वर्ण एक स्वर है (hop, mak, tak जैसे पैटर्न), e को वापस जोड़ें।
ये नए प्रत्यय भी जोड़ें (-ing, -ed, -ly, -s से पहले इन्हें जाँचें):
5. -tion (organization → organiza)
6. -ness (happiness → happi)
7. -ment (movement → move)
8. -able (readable → read)
9. -ible (sensible → sens)
अद्यतन प्रत्यय प्राथमिकता: -tion, -ness, -ment, -able, -ible, -ing, -ed, -ly, -s
न्यूनतम स्टेम लंबाई नियम रखें: केवल छीन लें यदि शेष स्टेम 3+ वर्ण है।
-ies & -ier नियम
अधिक आकृतिविज्ञान
अंग्रेजी में एक और सामान्य पैटर्न है: -y में समाप्त होने वाले शब्द विभक्त होने पर -ies, -ied, या -ier में बदल जाते हैं।
| शब्द | स्टेम करना चाहिए |
|---|---|
| babies | babi |
| carried | carri |
| earlier | earli |
| flies | fli |
| studied | studi |
-s & -ed जाँच से पहले ये नियम जोड़ें:
- -ies → छीन लें & i जोड़ें (babies → babi)
- -ied → छीन लें & i जोड़ें (carried → carri)
- -ier → छीन लें & i जोड़ें (earlier → earli)
समान न्यूनतम स्टेम लंबाई नियम: केवल परिवर्तित करें यदि परिणाम 3+ वर्ण है।
परीक्षण क्यों करें?
परीक्षण वैकल्पिक नहीं है
आपके पास एक काम करने वाला स्टेमर है। आप कैसे जानते हैं कि यह वास्तव में काम करता है? अभी आप कुछ उदाहरणों को हाथ से चला रहे हैं। यह स्केल नहीं करता।
व्यावसायिक सॉफ़्टवेयर परीक्षण के तीन स्तर का उपयोग करता है:
इकाई परीक्षण: एक फ़ंक्शन को अलगाव में ज्ञात इनपुट और अपेक्षित आउटपुट के साथ परीक्षण करें। तेज़, अनेक, विशिष्ट।
एकीकरण परीक्षण: परीक्षण करें कि कई घटक एक साथ काम करते हैं। एक स्टेमर के लिए, इसका अर्थ शब्दों के एक बैच के विरुद्ध परीक्षण करना और सत्यापित करना है कि परिणाम सुसंगत हैं।
कार्यात्मक परीक्षण: बाहर से सिस्टम का परीक्षण करें, जैसे एक उपयोगकर्ता करेगा। एक स्टेमर के लिए, इसका अर्थ वास्तविक पाठ को खिलाना और सत्यापित करना है कि आउटपुट खोज जैसे वास्तविक उपयोग मामले के लिए समझदारी रखता है।
आप सभी तीन लिखेंगे।
इकाई परीक्षण लिखें
इकाई परीक्षण
एक फ़ंक्शन run_unit_tests लिखें जो आपके stem फ़ंक्शन को कम से कम 15 परीक्षण मामलों के साथ कवर करता है:
1. मूल प्रत्यय स्ट्रिपिंग: -ing, -ed, -ly, -s में समाप्त होने वाले शब्द
2. जटिल प्रत्यय: -tion, -ness, -ment, -able, -ible
3. Y-विभक्ति: -ies, -ied, -ier
4. किनारे के मामले: छोटे शब्द जिन्हें छीन नहीं जाना चाहिए, कोई प्रत्यय नहीं वाले शब्द, पहले से stemmmed शब्द
5. दोहरे व्यंजन सफाई: running → run, sitting → sit
6. मूक-e बहाली: hoping → hope
7. केस असंवेदनशीलता: अपरकेस इनपुट को लोअरकेस किया जाना चाहिए
अपने परीक्षणों को इस तरह संरचित करें:
def run_unit_tests():
tests = [
('running', 'run'),
('cats', 'cat'),
# ... at least 15 test cases
]
passed = 0
failed = 0
for word, expected in tests:
result = stem(word)
if result == expected:
passed += 1
else:
failed += 1
print(f'FAIL: stem({word}) = {result}, expected {expected}')
print(f'{passed}/{passed + failed} unit tests passed')
return failed == 0
एकीकरण परीक्षण लिखें
एकीकरण परीक्षण
इकाई परीक्षण व्यक्तिगत इनपुट को सत्यापित करते हैं। एकीकरण परीक्षण सत्यापित करते हैं कि घटक सही तरीके से एक साथ काम करते हैं।
एक स्टेमर के लिए, एक महत्वपूर्ण एकीकरण संपत्ति सुसंगतता है: यदि आप एक ही शब्द को दो बार stem करते हैं, तो आपको एक ही परिणाम मिलता है। और शब्द जो एक साथ समूहबद्ध होने चाहिए, उसे समान स्टेम उत्पन्न करना चाहिए।
एक फ़ंक्शन run_integration_tests लिखें जो परीक्षण करता है:
1. Idempotency: एक पहले से stemmmed शब्द को stem करने से एक ही स्टेम को वापस करना चाहिए। stem(stem(word)) == stem(word) सभी शब्दों के लिए।
2. समूहीकरण: शब्द जो एक स्टेम को साझा करते हैं, वास्तव में करते हैं। कम से कम 3 शब्द परिवारों का परीक्षण करें (उदाहरण के लिए, run/runs/running/runner को सभी एक स्टेम साझा करना चाहिए)।
3. बैच प्रोसेसिंग: 20+ शब्दों की एक सूची को प्रोसेस करें और सत्यापित करें कि कोई क्रैश नहीं, खाली स्ट्रिंग नहीं, कोई मान None नहीं है।
def run_integration_tests():
# Test 1: idempotency
# Test 2: word family grouping
# Test 3: batch stability
...
कार्यात्मक परीक्षण लिखें
कार्यात्मक परीक्षण
कार्यात्मक परीक्षण सत्यापित करते हैं कि सिस्टम अपने इच्छित उपयोग केस के लिए काम करता है। आपका स्टेमर खोज में सुधार के लिए मौजूद है: तो परीक्षण करें।
एक फ़ंक्शन run_functional_tests लिखें जो:
1. खोज सिमुलेशन: दस्तावेज़ों की एक सूची और एक क्वेरी शब्द को देखते हुए, दस्तावेज़ों और क्वेरी दोनों को stem करें, फिर सत्यापित करें कि stemmmed क्वेरी शब्द stemmmed दस्तावेज़ों में दिखाई देते हैं। परीक्षण करें कि 'running' के लिए खोज से 'run' और 'runner' वाले दस्तावेज़ मिलते हैं।
2. सटीकता जाँच: सत्यापित करें कि स्टेमिंग गलती से असंबंधित शब्दों को समूहबद्ध नहीं करता है। 'university' और 'universe' एक स्टेम साझा कर सकते हैं: जाँचें कि आपका स्टेमर इसे कैसे संभालता है (यह ठीक है यदि यह उन्हें समूहबद्ध करता है; व्यवहार को दस्तावेज़ित करें)।
3. वास्तविक पाठ प्रोसेसिंग: वास्तविक अंग्रेजी पाठ के एक पैराग्राफ में हर शब्द को stem करें। सत्यापित करें कि आउटपुट उचित है: कोई खाली स्ट्रिंग नहीं, कोई क्रैश नहीं, आउटपुट में इनपुट के समान संख्या में शब्द हैं।
def run_functional_tests():
# Test 1: search finds related documents
# Test 2: precision: check over-stemming
# Test 3: real paragraph processing
...
आपने क्या बनाया
आपने क्या बनाया
आपने निम्नलिखित के साथ एक काम करने वाला अंग्रेजी स्टेमर लागू किया:
- 12 प्रत्यय नियम (-tion, -ness, -ment, -able, -ible, -ies, -ied, -ier, -ing, -ed, -ly, -s)
- दोहरे व्यंजन सफाई
- मूक-e बहाली
- इकाई परीक्षण, एकीकरण परीक्षण, & कार्यात्मक परीक्षण
वंश
आपका स्टेमर काम की एक लाइन में उतरता है जो 1955 में ज़ेलिग हैरिस के साथ शुरू हुई:
- हैरिस (1955): की खोज की कि आकृतिविज्ञान सीमाएँ सांख्यिकीय संकेतों के रूप में दिखाई देती हैं (उत्तराधिकारी विविधता)
- लोविन्स (1968): पहला प्रकाशित स्टेमिंग एल्गोरिदम, 294 प्रत्यय नियम
- पोर्टर (1980): 5 चरणों में ~60 नियमों में सरल किया, दशकों के लिए मानक बन गया
- स्नोबॉल (2001): पोर्टर की framework को कई भाषाओं में सामान्यीकृत किया
- आपका स्टेमर (आज): 12 नियम, समान मूल सिद्धांत
आप आगे क्या कर सकते हैं
- पूर्ण पोर्टर एल्गोरिदम लागू करें (यह अच्छी तरह से दस्तावेज़ित है & एक बेहतरीन अभ्यास)
- अपने स्टेमर को C में port करें 100x गति सुधार के लिए
- एक सरल खोज इंजन बनाएं जो पाठ फ़ाइलों को अनुक्रमित & क्वेरी करने के लिए आपके स्टेमर का उपयोग करता है
- अपने स्टेमर के आउटपुट को NLTK के PorterStemmer के विरुद्ध सटीकता को मापने के लिए तुलना करें
आपने जो कोड लिखा है वह आज हर खोज इंजन के अंदर चलने वाली मौलिक ऑपरेशन के समान है। एक दिन के काम के लिए बुरा नहीं।