nlpkit14 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ include TEST_RESULTS.md
2
+ recursive-include tests *.py
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: nlpkit14
3
+ Version: 0.3.0
4
+ Summary: Execute fourteen NLP practical programs with commented source and expected outputs
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: nltk<4,>=3.9
8
+ Requires-Dist: spacy<4,>=3.8
9
+ Requires-Dist: scikit-learn<2,>=1.5
10
+ Requires-Dist: requests<3,>=2.32
11
+ Requires-Dist: beautifulsoup4<5,>=4.12
12
+ Provides-Extra: models
13
+ Requires-Dist: torch<3,>=2.4; extra == "models"
14
+ Requires-Dist: transformers<5,>=4.45; extra == "models"
15
+ Requires-Dist: benepar<0.3,>=0.2.0; extra == "models"
16
+ Requires-Dist: sentencepiece<1,>=0.2; extra == "models"
17
+
18
+ # nlpkit14 0.3.0
19
+
20
+ Run the fourteen programs from the supplied IA1 question/code/output document.
21
+
22
+ ```python
23
+ import nlpkit14 as n
24
+ n.q1() # Actually runs question 1 and prints computed output
25
+ n.source(1) # Shows the code, question and expected output as comments
26
+ n.questions()
27
+ ```
28
+
29
+ Question numbers 1–7 are B1–B7. Numbers 8–14 are C1–C7. The question, expected output, and extra notes are comments in each bundled standalone Python program. Exercise calls print only the program's output. No expected-output text is substituted for model computation.
30
+
31
+ ## Install and prepare online once
32
+
33
+ Install the shared wheel, then prepare the basic data and spaCy model:
34
+
35
+ ```sh
36
+ python -m pip install ./nlpkit14-0.3.0-py3-none-any.whl
37
+ python -c "import nlpkit14 as n; n.setup()"
38
+ ```
39
+
40
+ For all exercises, install the model extra and run the complete setup (large downloads):
41
+
42
+ ```sh
43
+ python -m pip install './nlpkit14-0.3.0-py3-none-any.whl[models]'
44
+ python -c "import nlpkit14 as n; n.setup(models=True)"
45
+ ```
46
+
47
+ This is a local release of the new nlpkit14 project, not yet uploaded to PyPI. Do not use pip install nlpkit14 until publication succeeds.
48
+
49
+ ## Offline installation
50
+
51
+ On an internet-connected machine with the SAME OS, architecture and Python version as the offline machine, download the wheel and dependencies:
52
+
53
+ ```sh
54
+ python -m pip download --dest wheelhouse './nlpkit14-0.3.0-py3-none-any.whl[models]'
55
+ ```
56
+
57
+ Install and run setup online on that matching machine. Copy the wheelhouse, both installed spaCy model packages (or their installation wheels), and the full data/cache folder to the offline machine. Model data defaults to ~/.nlpkit14; set NLPKIT14_DATA before setup/import to choose a portable folder. Copying the entire prepared Python environment is platform specific; do not copy a Mac environment to Windows.
58
+
59
+ On the offline computer:
60
+
61
+ ```sh
62
+ python -m pip install --no-index --find-links=wheelhouse nlpkit14==0.3.0
63
+ ```
64
+
65
+ Install the spaCy model wheels from local storage too. Keep the copied data folder at ~/.nlpkit14 or point NLPKIT14_DATA to it. Model and dependency archives are not included in this small library wheel.
66
+
67
+ q8 fetches https://www.bnmit.org/ and requires internet even after setup. Other exercises can run without internet after all their requirements are installed. No downloads run implicitly from q1–q14. Missing packages/data raise their real installation errors rather than producing fake results.
68
+
69
+ ## Output fidelity
70
+
71
+ The original algorithms and sample input are preserved. Downloads are moved to explicit setup. HTTP errors are checked. BERT uses evaluation mode and local cached model files. The raw Good–Turing demonstration is not a normalized probability estimator. The document's classifier fits its vocabulary before splitting and is an educational example, not an unbiased evaluation pipeline. Model outputs, parse trees, POS tags and similarity values can differ from the commented reference output. The document's B5 reference omits POS lines that the code actually prints. Read TEST_RESULTS.md for exactly which programs were executed.
@@ -0,0 +1,54 @@
1
+ # nlpkit14 0.3.0
2
+
3
+ Run the fourteen programs from the supplied IA1 question/code/output document.
4
+
5
+ ```python
6
+ import nlpkit14 as n
7
+ n.q1() # Actually runs question 1 and prints computed output
8
+ n.source(1) # Shows the code, question and expected output as comments
9
+ n.questions()
10
+ ```
11
+
12
+ Question numbers 1–7 are B1–B7. Numbers 8–14 are C1–C7. The question, expected output, and extra notes are comments in each bundled standalone Python program. Exercise calls print only the program's output. No expected-output text is substituted for model computation.
13
+
14
+ ## Install and prepare online once
15
+
16
+ Install the shared wheel, then prepare the basic data and spaCy model:
17
+
18
+ ```sh
19
+ python -m pip install ./nlpkit14-0.3.0-py3-none-any.whl
20
+ python -c "import nlpkit14 as n; n.setup()"
21
+ ```
22
+
23
+ For all exercises, install the model extra and run the complete setup (large downloads):
24
+
25
+ ```sh
26
+ python -m pip install './nlpkit14-0.3.0-py3-none-any.whl[models]'
27
+ python -c "import nlpkit14 as n; n.setup(models=True)"
28
+ ```
29
+
30
+ This is a local release of the new nlpkit14 project, not yet uploaded to PyPI. Do not use pip install nlpkit14 until publication succeeds.
31
+
32
+ ## Offline installation
33
+
34
+ On an internet-connected machine with the SAME OS, architecture and Python version as the offline machine, download the wheel and dependencies:
35
+
36
+ ```sh
37
+ python -m pip download --dest wheelhouse './nlpkit14-0.3.0-py3-none-any.whl[models]'
38
+ ```
39
+
40
+ Install and run setup online on that matching machine. Copy the wheelhouse, both installed spaCy model packages (or their installation wheels), and the full data/cache folder to the offline machine. Model data defaults to ~/.nlpkit14; set NLPKIT14_DATA before setup/import to choose a portable folder. Copying the entire prepared Python environment is platform specific; do not copy a Mac environment to Windows.
41
+
42
+ On the offline computer:
43
+
44
+ ```sh
45
+ python -m pip install --no-index --find-links=wheelhouse nlpkit14==0.3.0
46
+ ```
47
+
48
+ Install the spaCy model wheels from local storage too. Keep the copied data folder at ~/.nlpkit14 or point NLPKIT14_DATA to it. Model and dependency archives are not included in this small library wheel.
49
+
50
+ q8 fetches https://www.bnmit.org/ and requires internet even after setup. Other exercises can run without internet after all their requirements are installed. No downloads run implicitly from q1–q14. Missing packages/data raise their real installation errors rather than producing fake results.
51
+
52
+ ## Output fidelity
53
+
54
+ The original algorithms and sample input are preserved. Downloads are moved to explicit setup. HTTP errors are checked. BERT uses evaluation mode and local cached model files. The raw Good–Turing demonstration is not a normalized probability estimator. The document's classifier fits its vocabulary before splitting and is an educational example, not an unbiased evaluation pipeline. Model outputs, parse trees, POS tags and similarity values can differ from the commented reference output. The document's B5 reference omits POS lines that the code actually prints. Read TEST_RESULTS.md for exactly which programs were executed.
@@ -0,0 +1,15 @@
1
+ # Verification of version 0.3.0
2
+
3
+ - All fourteen document programs were extracted, reviewed, compiled, and bundled with their question and expected output as Python comments.
4
+ - The wheel built with Python 3.12 and passed `twine check`.
5
+ - A local no-index, no-dependency installation into a Python 3.12 virtual environment succeeded. The installed q9() computed the document's bigram probability. This is not a complete dependency-resolution test.
6
+ - The execution suite ran under an existing Python 3.13 Anaconda environment with NLTK, scikit-learn, requests and BeautifulSoup. Ten tests passed; one was skipped because WordNet data was not fully downloaded. Programs q1, q8 (controlled HTML response), q9, q10, q11 and q12 executed. Numeric/classification exercises were also run with socket connections blocked.
7
+ - q4 has not completed an end-to-end test because the WordNet download was stopped to keep delivery quick. The incomplete test archive is outside the deliverables.
8
+ - q2, q3, q5, q6, q7, q13 and q14 were syntax-checked but not executed with their required models. spaCy/Benepar/BERT model downloads were not completed. These are not asserted to be error-free across all dependency versions.
9
+ - q8's HTML extraction and real NLTK POS tagging were tested with a controlled HTTP response. Fetching the live BNMIT website was not tested.
10
+ - Windows and Linux were not tested. Full setup(), model extras dependency resolution, and transfer of an offline wheelhouse/model cache have not been end-to-end tested.
11
+ - This release has not been uploaded to PyPI. The previously published version remains separate.
12
+
13
+ Model exercises require their real packages and data. No static sample answer is printed in place of model inference. Unexpected errors are not suppressed.
14
+
15
+ Rename: public distribution and import are now nlpkit14. No upload has been performed.
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nlpkit14"
7
+ version = "0.3.0"
8
+ description = "Execute fourteen NLP practical programs with commented source and expected outputs"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = ["nltk>=3.9,<4", "spacy>=3.8,<4", "scikit-learn>=1.5,<2", "requests>=2.32,<3", "beautifulsoup4>=4.12,<5"]
12
+
13
+ [project.optional-dependencies]
14
+ models = ["torch>=2.4,<3", "transformers>=4.45,<5", "benepar>=0.2.0,<0.3", "sentencepiece>=0.2,<1"]
15
+
16
+ [tool.setuptools.packages.find]
17
+ where = ["src"]
18
+
19
+ [tool.setuptools.package-data]
20
+ nlpkit14 = ["questions.json", "programs/*.py"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,70 @@
1
+ """Run the fourteen supplied NLP practicals, with source available separately."""
2
+ from importlib.resources import files
3
+ import json
4
+ import os
5
+
6
+ __version__ = "0.3.0"
7
+
8
+ def _resource(number):
9
+ if number not in range(1, 15):
10
+ raise ValueError("Choose a question from 1 to 14")
11
+ return files(__package__).joinpath("programs", f"q{number}.py")
12
+
13
+ def _configure():
14
+ path = os.path.expanduser(os.environ.get("NLPKIT14_DATA", "~/.nlpkit14"))
15
+ data = os.path.join(path, "nltk_data")
16
+ os.environ.setdefault("HF_HOME", os.path.join(path, "huggingface"))
17
+ try:
18
+ import nltk
19
+ except ModuleNotFoundError:
20
+ return
21
+ if data not in nltk.data.path:
22
+ nltk.data.path.insert(0, data)
23
+
24
+ def source(number):
25
+ """Print runnable source, including the commented question and expected output."""
26
+ print(_resource(number).read_text(encoding="utf-8"), end="")
27
+
28
+ def questions():
29
+ for row in json.loads(files(__package__).joinpath("questions.json").read_text(encoding="utf-8")):
30
+ print(f"q{row['number']}(): {row['question']}")
31
+
32
+ def _run(number):
33
+ _configure()
34
+ resource = _resource(number)
35
+ namespace = {"__name__": f"nlpkit14.question{number}"}
36
+ exec(compile(resource.read_text(encoding="utf-8"), str(resource), "exec"), namespace)
37
+
38
+ def _make(number):
39
+ def question():
40
+ _run(number)
41
+ question.__name__ = f"q{number}"
42
+ question.__qualname__ = question.__name__
43
+ question.__doc__ = f"Execute question {number}. Use source({number}) to see code and comments."
44
+ return question
45
+
46
+ for _number in range(1, 15):
47
+ globals()[f"q{_number}"] = _make(_number)
48
+
49
+ def setup(models=False):
50
+ """Explicit online preparation. models=True also downloads large parsing/BERT models."""
51
+ _configure()
52
+ import nltk
53
+ import subprocess
54
+ import sys
55
+ directory = os.path.join(os.path.expanduser(os.environ.get("NLPKIT14_DATA", "~/.nlpkit14")), "nltk_data")
56
+ os.makedirs(directory, exist_ok=True)
57
+ for name in ("punkt_tab", "stopwords", "averaged_perceptron_tagger_eng", "wordnet", "omw-1.4"):
58
+ if not nltk.download(name, download_dir=directory, quiet=True):
59
+ raise RuntimeError(f"Could not download {name}; check internet connectivity")
60
+ subprocess.run([sys.executable, "-m", "spacy", "download", "en_core_web_sm"], check=True)
61
+ if models:
62
+ subprocess.run([sys.executable, "-m", "spacy", "download", "en_core_web_md"], check=True)
63
+ import benepar
64
+ if not benepar.download("benepar_en3", download_dir=directory):
65
+ raise RuntimeError("Could not download benepar_en3; check internet connectivity")
66
+ from transformers import BertTokenizer, BertModel
67
+ BertTokenizer.from_pretrained("bert-base-uncased")
68
+ BertModel.from_pretrained("bert-base-uncased")
69
+
70
+ __all__ = ["questions", "source", "setup"] + [f"q{i}" for i in range(1, 15)]
@@ -0,0 +1,30 @@
1
+ # Question 1 (B1): Tokenization and Stop-word Removal using NLTK
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Sentences: ['This is a simple example demonstrating stopword removal in NLP.', 'NLP is interesting.']
5
+ # Words: ['This', 'is', 'a', 'simple', 'example', 'demonstrating', 'stopword', 'removal', 'in', 'NLP', '.', 'NLP', 'is', 'interesting', '.']
6
+ # Filtered Tokens: ['simple', 'example', 'demonstrating', 'stopword', 'removal', 'NLP', 'NLP', 'interesting']
7
+
8
+ import nltk
9
+ from nltk.tokenize import word_tokenize, sent_tokenize
10
+ from nltk.corpus import stopwords
11
+
12
+ # Download required resources
13
+ # Resource download moved to n.setup().
14
+ # Resource download moved to n.setup().
15
+ # Input text
16
+ text = "This is a simple example demonstrating stopword removal in NLP. NLP is interesting."
17
+
18
+ # Sentence tokenization
19
+ sentences = sent_tokenize(text)
20
+
21
+ # Word tokenization
22
+ words = word_tokenize(text)
23
+
24
+ # Stop-word removal
25
+ stop_words = set(stopwords.words('english'))
26
+ filtered = [w for w in words if w.lower() not in stop_words and w.isalnum()]
27
+
28
+ print("Sentences:", sentences)
29
+ print("Words:", words)
30
+ print("Filtered Tokens:", filtered)
@@ -0,0 +1,41 @@
1
+ # Question 10 (C3): Add-One (Laplace) Smoothing for an N-gram Model
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # P(i|<s>) = 0.272727
5
+ # P(like|i) = 0.300000
6
+ # P(apples|like) = 0.200000
7
+ # P(</s>|apples) = 0.300000
8
+ # Vocabulary Size = 8
9
+ # Add-One Sentence Probability = 0.00490909
10
+
11
+ from collections import Counter
12
+
13
+ corpus = [
14
+ ['i', 'like', 'apples'],
15
+ ['i', 'like', 'bananas'],
16
+ ['you', 'eat', 'apples']
17
+ ]
18
+
19
+ unigrams = Counter()
20
+ bigrams = Counter()
21
+
22
+ for sentence in corpus:
23
+ tokens = ['<s>'] + sentence + ['</s>']
24
+ unigrams.update(tokens)
25
+ bigrams.update(zip(tokens[:-1], tokens[1:]))
26
+
27
+ V = len(unigrams) # vocabulary size including boundary tokens
28
+
29
+ def add_one(w1, w2):
30
+ return (bigrams[(w1, w2)] + 1) / (unigrams[w1] + V)
31
+
32
+ test = ['<s>', 'i', 'like', 'apples', '</s>']
33
+ probability = 1.0
34
+
35
+ for w1, w2 in zip(test[:-1], test[1:]):
36
+ p = add_one(w1, w2)
37
+ probability *= p
38
+ print(f"P({w2}|{w1}) = {p:.6f}")
39
+
40
+ print("Vocabulary Size =", V)
41
+ print("Add-One Sentence Probability =", round(probability, 8))
@@ -0,0 +1,39 @@
1
+ # Question 11 (C4): Calculate Good-Turing Frequency
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Raw Good-Turing adjusted frequencies only; this is not a normalized probability model.
4
+ # Expected output:
5
+ # r C*(r)
6
+ # -------------
7
+ # 0 0.0
8
+ # 1 1.3333
9
+ # 2 1.5
10
+ # 3 0.0
11
+
12
+ from collections import Counter
13
+
14
+ def good_turing(counts):
15
+ # counts contains observed frequencies of different events
16
+ freq_of_freq = Counter(counts)
17
+ max_r = max(freq_of_freq.keys())
18
+ adjusted_counts = {}
19
+
20
+ for r in range(0, max_r + 1):
21
+ Nr = freq_of_freq.get(r, 0)
22
+ Nr_plus_1 = freq_of_freq.get(r + 1, 0)
23
+
24
+ if Nr > 0:
25
+ c_star = (r + 1) * (Nr_plus_1 / Nr)
26
+ adjusted_counts[r] = round(c_star, 4)
27
+ else:
28
+ adjusted_counts[r] = 0.0
29
+
30
+ return adjusted_counts
31
+
32
+ # 3 events occur once, 2 events occur twice, 1 event occurs 3 times
33
+ sample_counts = [1, 1, 1, 2, 2, 3]
34
+ result = good_turing(sample_counts)
35
+
36
+ print(f"{'r':<5}{'C*(r)':<10}")
37
+ print("-" * 13)
38
+ for r in sorted(result):
39
+ print(f"{r:<5}{result[r]:<10}")
@@ -0,0 +1,50 @@
1
+ # Question 12 (C5): Text Classification
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Preserves the document example; fit the vectorizer on training data only for unbiased evaluation.
4
+ # Expected output:
5
+ # precision recall f1-score support
6
+ #
7
+ # sports 0.00 0.00 0.00 2
8
+ # technology 0.33 1.00 0.50 1
9
+ #
10
+ # accuracy 0.33 3
11
+ # macro avg 0.17 0.50 0.25 3
12
+ # weighted avg 0.11 0.33 0.17 3
13
+
14
+ from sklearn.feature_extraction.text import CountVectorizer
15
+ from sklearn.model_selection import train_test_split
16
+ from sklearn.linear_model import LogisticRegression
17
+ from sklearn.metrics import classification_report
18
+
19
+ texts = [
20
+ "The CPU and memory are important in a computer",
21
+ "Basketball is a popular sport",
22
+ "Python and Java are programming languages",
23
+ "Football players need strength and speed",
24
+ "Hard disk and RAM are types of hardware",
25
+ "Tennis is played with a racket",
26
+ "Motherboard connects all components",
27
+ "He scored a goal in the last minute"
28
+ ]
29
+
30
+ labels = [
31
+ "technology", "sports", "technology", "sports",
32
+ "technology", "sports", "technology", "sports"
33
+ ]
34
+
35
+ # Convert text into Bag-of-Words vectors
36
+ vectorizer = CountVectorizer()
37
+ X = vectorizer.fit_transform(texts)
38
+
39
+ # Train-test split
40
+ X_train, X_test, y_train, y_test = train_test_split(
41
+ X, labels, test_size=0.3, random_state=42
42
+ )
43
+
44
+ # Train classifier
45
+ clf = LogisticRegression()
46
+ clf.fit(X_train, y_train)
47
+
48
+ # Predict and evaluate
49
+ y_pred = clf.predict(X_test)
50
+ print(classification_report(y_test, y_pred, zero_division=0))
@@ -0,0 +1,25 @@
1
+ # Question 13 (C6): Calculate Vector Similarity in Semantic Space
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Semantic similarity between:
5
+ # "Apples are tasty" and "Bananas are delicious" = 0.8661
6
+
7
+ import spacy
8
+ from sklearn.metrics.pairwise import cosine_similarity
9
+
10
+ # Medium model contains pretrained vectors
11
+ nlp = spacy.load("en_core_web_md")
12
+
13
+ text1 = "Apples are tasty"
14
+ text2 = "Bananas are delicious"
15
+
16
+ doc1 = nlp(text1)
17
+ doc2 = nlp(text2)
18
+
19
+ vec1 = doc1.vector.reshape(1, -1)
20
+ vec2 = doc2.vector.reshape(1, -1)
21
+
22
+ similarity = cosine_similarity(vec1, vec2)[0][0]
23
+
24
+ print(f'Semantic similarity between:')
25
+ print(f'"{text1}" and "{text2}" = {similarity:.4f}')
@@ -0,0 +1,40 @@
1
+ # Question 14 (C7): Identify Context of Ambiguous Words using BERT
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Sentence 1: He sat on the river bank and watched the water flow.
5
+ # Sentence 2: She went to the bank to deposit some money.
6
+ # Contextual similarity of 'bank': 0.4972
7
+
8
+ from transformers import BertTokenizer, BertModel
9
+ import torch
10
+ from sklearn.metrics.pairwise import cosine_similarity
11
+
12
+ # Load pretrained BERT
13
+ tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', local_files_only=True)
14
+ model = BertModel.from_pretrained('bert-base-uncased', local_files_only=True)
15
+ model.eval()
16
+
17
+ sent1 = "He sat on the river bank and watched the water flow."
18
+ sent2 = "She went to the bank to deposit some money."
19
+
20
+ def get_word_embedding(sentence, target_word):
21
+ inputs = tokenizer(sentence, return_tensors='pt')
22
+
23
+ with torch.no_grad():
24
+ outputs = model(**inputs)
25
+
26
+ tokens = tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])
27
+ embeddings = outputs.last_hidden_state.squeeze(0)
28
+
29
+ # Find token position
30
+ idx = tokens.index(target_word)
31
+ return embeddings[idx].numpy()
32
+
33
+ embedding1 = get_word_embedding(sent1, "bank")
34
+ embedding2 = get_word_embedding(sent2, "bank")
35
+
36
+ similarity = cosine_similarity([embedding1], [embedding2])[0][0]
37
+
38
+ print("Sentence 1:", sent1)
39
+ print("Sentence 2:", sent2)
40
+ print("Contextual similarity of 'bank':", round(similarity, 4))
@@ -0,0 +1,33 @@
1
+ # Question 2 (B2): Tokenization and Stop-word Removal using spaCy
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Sentences: ['This is an example sentence, demonstrating tokenization and stop word removal.']
5
+ # Tokens: ['This', 'is', 'an', 'example', 'sentence', ',', 'demonstrating', 'tokenization', 'and', 'stop', 'word', 'removal', '.']
6
+ # Filtered Tokens: ['example', 'sentence', 'demonstrating', 'tokenization', 'stop', 'word', 'removal']
7
+
8
+ import spacy
9
+
10
+ # Load spaCy English model
11
+ nlp = spacy.load("en_core_web_sm")
12
+
13
+ # Sample text
14
+ text = "This is an example sentence, demonstrating tokenization and stop word removal."
15
+
16
+ # Process text
17
+ Doc = nlp(text)
18
+
19
+ # Sentence tokenization
20
+ sentences = [sent.text for sent in Doc.sents]
21
+
22
+ # Word tokenization
23
+ tokens = [token.text for token in Doc]
24
+
25
+ # Remove stop words and punctuation
26
+ filtered_tokens = [
27
+ token.text for token in Doc
28
+ if not token.is_stop and not token.is_punct
29
+ ]
30
+
31
+ print("Sentences:", sentences)
32
+ print("Tokens:", tokens)
33
+ print("Filtered Tokens:", filtered_tokens)
@@ -0,0 +1,29 @@
1
+ # Question 3 (B3): Stemming and Lemmatization using spaCy
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Original Words : ['Studies', 'studying', 'cries', 'cried', 'flying', 'flies']
5
+ # Lemmatized Words : ['study', 'study', 'cry', 'cry', 'fly', 'fly']
6
+ # Stemmed Words : ['studi', 'studi', 'cri', 'cri', 'fli', 'fli']
7
+
8
+ import spacy
9
+ from nltk.stem import PorterStemmer
10
+
11
+ # Load spaCy model and create stemmer
12
+ nlp = spacy.load("en_core_web_sm")
13
+ stemmer = PorterStemmer()
14
+
15
+ sentence = "Studies studying cries cried flying flies"
16
+ doc = nlp(sentence)
17
+
18
+ # Original tokens
19
+ original_words = [token.text for token in doc]
20
+
21
+ # Lemmatization using spaCy
22
+ lemmatized_words = [token.lemma_ for token in doc]
23
+
24
+ # Stemming on spaCy tokens
25
+ stemmed_words = [stemmer.stem(token.text) for token in doc]
26
+
27
+ print("Original Words :", original_words)
28
+ print("Lemmatized Words :", lemmatized_words)
29
+ print("Stemmed Words :", stemmed_words)
@@ -0,0 +1,44 @@
1
+ # Question 4 (B4): Stemming and Lemmatization using NLTK
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Original Words: ['Studies', 'studying', 'cries', 'cried', 'flying', 'flies']
5
+ # Stemmed Words : ['studi', 'studi', 'cri', 'cri', 'fli', 'fli']
6
+ # Lemmatized Words: ['Studies', 'study', 'cry', 'cry', 'fly', 'fly']
7
+
8
+ import nltk
9
+ from nltk.tokenize import word_tokenize
10
+ from nltk.stem import PorterStemmer, WordNetLemmatizer
11
+ from nltk.corpus import wordnet
12
+
13
+ # Resource download moved to n.setup().
14
+ # Resource download moved to n.setup().
15
+ # Resource download moved to n.setup().
16
+ # Resource download moved to n.setup().
17
+ stemmer = PorterStemmer()
18
+ lemmatizer = WordNetLemmatizer()
19
+
20
+ # Map Treebank POS tag to WordNet POS
21
+ def get_wordnet_pos(tag):
22
+ if tag.startswith('J'):
23
+ return wordnet.ADJ
24
+ elif tag.startswith('V'):
25
+ return wordnet.VERB
26
+ elif tag.startswith('N'):
27
+ return wordnet.NOUN
28
+ elif tag.startswith('R'):
29
+ return wordnet.ADV
30
+ return wordnet.NOUN
31
+
32
+ text = "Studies studying cries cried flying flies"
33
+ tokens = word_tokenize(text)
34
+ tagged = nltk.pos_tag(tokens)
35
+
36
+ stems = [stemmer.stem(word) for word in tokens]
37
+ lemmas = [
38
+ lemmatizer.lemmatize(word, get_wordnet_pos(tag))
39
+ for word, tag in tagged
40
+ ]
41
+
42
+ print("Original Words:", tokens)
43
+ print("Stemmed Words :", stems)
44
+ print("Lemmatized Words:", lemmas)
@@ -0,0 +1,33 @@
1
+ # Question 5 (B5): Design a Python Program to Illustrate Corpus
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # --- Document 1 ---
5
+ # Tokens: ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog', '.']
6
+ # POS Tags: [('The', 'DET'), ('quick', 'ADJ'), ('brown', 'ADJ'), ('fox', 'NOUN'), ('jumps', 'VERB'), ('over', 'ADP'), ('the', 'DET'), ('lazy', 'ADJ'), ('dog', 'NOUN'), ('.', 'PUNCT')]
7
+ #
8
+ # --- Document 2 ---
9
+ # Tokens: ['SpaCy', 'is', 'a', 'popular', 'NLP', 'library', 'in', 'Python', '.']
10
+ #
11
+ # --- Document 3 ---
12
+ # Tokens: ['Natural', 'Language', 'Processing', 'enables', 'machines', 'to', 'understand', 'human', 'language', '.']
13
+
14
+ import spacy
15
+
16
+ nlp = spacy.load("en_core_web_sm")
17
+
18
+ # A small corpus: collection of documents
19
+ corpus = [
20
+ "The quick brown fox jumps over the lazy dog.",
21
+ "SpaCy is a popular NLP library in Python.",
22
+ "Natural Language Processing enables machines to understand human language."
23
+ ]
24
+
25
+ for i, text in enumerate(corpus, start=1):
26
+ print(f"\n--- Document {i} ---")
27
+ doc = nlp(text)
28
+
29
+ tokens = [token.text for token in doc]
30
+ pos_tags = [(token.text, token.pos_) for token in doc]
31
+
32
+ print("Tokens:", tokens)
33
+ print("POS Tags:", pos_tags)
@@ -0,0 +1,43 @@
1
+ # Question 6 (B6): Generate a Constituency Parse Tree
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Parse String:
5
+ # (S (NP (DT The) (JJ quick) (JJ brown) (NN fox))
6
+ # (VP (VBZ jumps)
7
+ # (PP (IN over) (NP (DT the) (JJ lazy) (NN dog))))
8
+ # (. .))
9
+ #
10
+ # Pretty Tree:
11
+ # S
12
+ # _______________|__________________________
13
+ # | VP |
14
+ # | _________|___ |
15
+ # NP | PP |
16
+ # ____|__________ | ________|___ |
17
+ # DT JJ JJ NN VBZ IN DT JJ NN .
18
+ # The quick brown fox jumps over the lazy dog .
19
+
20
+ # Run once if required:
21
+ # !pip install spacy benepar
22
+ # !python -m spacy download en_core_web_sm
23
+
24
+ import spacy
25
+ import benepar
26
+ import nltk
27
+
28
+ # Model download moved to n.setup(models=True).
29
+ nlp = spacy.load("en_core_web_sm")
30
+
31
+ if not nlp.has_pipe("benepar"):
32
+ nlp.add_pipe("benepar", config={"model": "benepar_en3"})
33
+
34
+ sentence = "The quick brown fox jumps over the lazy dog."
35
+ doc = nlp(sentence)
36
+
37
+ for sent in doc.sents:
38
+ parse_string = sent._.parse_string
39
+ print("Parse String:\n", parse_string)
40
+
41
+ tree = nltk.Tree.fromstring(parse_string)
42
+ print("\nPretty Tree:")
43
+ tree.pretty_print()
@@ -0,0 +1,31 @@
1
+ # Question 7 (B7): Generate a Dependency Parse Tree
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # Word: The POS: DET Head: team Dep: det
5
+ # Word: cricket POS: NOUN Head: team Dep: compound
6
+ # Word: team POS: NOUN Head: won Dep: nsubj
7
+ # Word: won POS: VERB Head: won Dep: ROOT
8
+ # Word: the POS: DET Head: match Dep: det
9
+ # Word: match POS: NOUN Head: won Dep: dobj
10
+ # Word: in POS: ADP Head: won Dep: prep
11
+ # Word: Mumbai POS: PROPN Head: in Dep: pobj
12
+ # Word: . POS: PUNCT Head: won Dep: punct
13
+
14
+ import spacy
15
+ from spacy import displacy
16
+
17
+ nlp = spacy.load("en_core_web_sm")
18
+ sentence = "The cricket team won the match in Mumbai."
19
+ doc = nlp(sentence)
20
+
21
+ # Print dependency information
22
+ for token in doc:
23
+ print(
24
+ f"Word: {token.text:8} "
25
+ f"POS: {token.pos_:6} "
26
+ f"Head: {token.head.text:8} "
27
+ f"Dep: {token.dep_}"
28
+ )
29
+
30
+ # In Jupyter / Colab
31
+ # displacy.render(doc, style="dep", jupyter=True)
@@ -0,0 +1,39 @@
1
+ # Question 8 (C1): POS Tagging on Text Scraped from a Website
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # POS Tags (sample):
5
+ # [('B.N.M', 'NNP'), ('Institute', 'NNP'), ('of', 'IN'),
6
+ # ('Technology', 'NNP'), ('is', 'VBZ'), ('one', 'CD'),
7
+ # ('of', 'IN'), ('the', 'DT'), ('top', 'JJ'),
8
+ # ('engineering', 'NN'), ('colleges', 'NNS'), ...]
9
+
10
+ import requests
11
+ from bs4 import BeautifulSoup
12
+ import nltk
13
+
14
+ # Resource download moved to n.setup().
15
+ # Resource download moved to n.setup().
16
+ def scrape_text(url):
17
+ response = requests.get(url, timeout=10)
18
+ response.raise_for_status()
19
+ soup = BeautifulSoup(response.content, 'html.parser')
20
+
21
+ # Remove scripts and styles
22
+ for item in soup(['script', 'style']):
23
+ item.extract()
24
+
25
+ return soup.get_text(separator=' ')
26
+
27
+ def pos_tag_text(text):
28
+ tokens = nltk.word_tokenize(text)
29
+ return nltk.pos_tag(tokens)
30
+
31
+ url = "https://www.bnmit.org/"
32
+ scraped_text = scrape_text(url)
33
+ pos_tags = pos_tag_text(scraped_text)
34
+
35
+ print("Scraped Text (first 300 characters):")
36
+ print(scraped_text[:300])
37
+
38
+ print("\nPOS Tags (first 30 tokens):")
39
+ print(pos_tags[:30])
@@ -0,0 +1,36 @@
1
+ # Question 9 (C2): Implement a Bigram Model and Calculate Test-Sentence Probability
2
+ # Output below is the document example; actual model and website results may differ.
3
+ # Expected output:
4
+ # P(i|<s>) = 2/3 = 0.666667
5
+ # P(like|i) = 2/2 = 1.000000
6
+ # P(apples|like) = 1/2 = 0.500000
7
+ # P(</s>|apples) = 2/2 = 1.000000
8
+ # Sentence Probability = 0.33333333
9
+
10
+ from collections import Counter
11
+
12
+ # Training corpus
13
+ corpus = [
14
+ ['i', 'like', 'apples'],
15
+ ['i', 'like', 'bananas'],
16
+ ['you', 'eat', 'apples']
17
+ ]
18
+
19
+ unigrams = Counter()
20
+ bigrams = Counter()
21
+
22
+ for sentence in corpus:
23
+ tokens = ['<s>'] + sentence + ['</s>']
24
+ unigrams.update(tokens)
25
+ bigrams.update(zip(tokens[:-1], tokens[1:]))
26
+
27
+ # Test sentence
28
+ test = ['<s>', 'i', 'like', 'apples', '</s>']
29
+ probability = 1.0
30
+
31
+ for w1, w2 in zip(test[:-1], test[1:]):
32
+ p = bigrams[(w1, w2)] / unigrams[w1]
33
+ probability *= p
34
+ print(f"P({w2}|{w1}) = {bigrams[(w1,w2)]}/{unigrams[w1]} = {p:.6f}")
35
+
36
+ print("Sentence Probability =", round(probability, 8))
@@ -0,0 +1,72 @@
1
+ [
2
+ {
3
+ "number": 1,
4
+ "label": "B1",
5
+ "question": "Tokenization and Stop-word Removal using NLTK"
6
+ },
7
+ {
8
+ "number": 2,
9
+ "label": "B2",
10
+ "question": "Tokenization and Stop-word Removal using spaCy"
11
+ },
12
+ {
13
+ "number": 3,
14
+ "label": "B3",
15
+ "question": "Stemming and Lemmatization using spaCy"
16
+ },
17
+ {
18
+ "number": 4,
19
+ "label": "B4",
20
+ "question": "Stemming and Lemmatization using NLTK"
21
+ },
22
+ {
23
+ "number": 5,
24
+ "label": "B5",
25
+ "question": "Design a Python Program to Illustrate Corpus"
26
+ },
27
+ {
28
+ "number": 6,
29
+ "label": "B6",
30
+ "question": "Generate a Constituency Parse Tree"
31
+ },
32
+ {
33
+ "number": 7,
34
+ "label": "B7",
35
+ "question": "Generate a Dependency Parse Tree"
36
+ },
37
+ {
38
+ "number": 8,
39
+ "label": "C1",
40
+ "question": "POS Tagging on Text Scraped from a Website"
41
+ },
42
+ {
43
+ "number": 9,
44
+ "label": "C2",
45
+ "question": "Implement a Bigram Model and Calculate Test-Sentence Probability"
46
+ },
47
+ {
48
+ "number": 10,
49
+ "label": "C3",
50
+ "question": "Add-One (Laplace) Smoothing for an N-gram Model"
51
+ },
52
+ {
53
+ "number": 11,
54
+ "label": "C4",
55
+ "question": "Calculate Good-Turing Frequency"
56
+ },
57
+ {
58
+ "number": 12,
59
+ "label": "C5",
60
+ "question": "Text Classification"
61
+ },
62
+ {
63
+ "number": 13,
64
+ "label": "C6",
65
+ "question": "Calculate Vector Similarity in Semantic Space"
66
+ },
67
+ {
68
+ "number": 14,
69
+ "label": "C7",
70
+ "question": "Identify Context of Ambiguous Words using BERT"
71
+ }
72
+ ]
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: nlpkit14
3
+ Version: 0.3.0
4
+ Summary: Execute fourteen NLP practical programs with commented source and expected outputs
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: nltk<4,>=3.9
8
+ Requires-Dist: spacy<4,>=3.8
9
+ Requires-Dist: scikit-learn<2,>=1.5
10
+ Requires-Dist: requests<3,>=2.32
11
+ Requires-Dist: beautifulsoup4<5,>=4.12
12
+ Provides-Extra: models
13
+ Requires-Dist: torch<3,>=2.4; extra == "models"
14
+ Requires-Dist: transformers<5,>=4.45; extra == "models"
15
+ Requires-Dist: benepar<0.3,>=0.2.0; extra == "models"
16
+ Requires-Dist: sentencepiece<1,>=0.2; extra == "models"
17
+
18
+ # nlpkit14 0.3.0
19
+
20
+ Run the fourteen programs from the supplied IA1 question/code/output document.
21
+
22
+ ```python
23
+ import nlpkit14 as n
24
+ n.q1() # Actually runs question 1 and prints computed output
25
+ n.source(1) # Shows the code, question and expected output as comments
26
+ n.questions()
27
+ ```
28
+
29
+ Question numbers 1–7 are B1–B7. Numbers 8–14 are C1–C7. The question, expected output, and extra notes are comments in each bundled standalone Python program. Exercise calls print only the program's output. No expected-output text is substituted for model computation.
30
+
31
+ ## Install and prepare online once
32
+
33
+ Install the shared wheel, then prepare the basic data and spaCy model:
34
+
35
+ ```sh
36
+ python -m pip install ./nlpkit14-0.3.0-py3-none-any.whl
37
+ python -c "import nlpkit14 as n; n.setup()"
38
+ ```
39
+
40
+ For all exercises, install the model extra and run the complete setup (large downloads):
41
+
42
+ ```sh
43
+ python -m pip install './nlpkit14-0.3.0-py3-none-any.whl[models]'
44
+ python -c "import nlpkit14 as n; n.setup(models=True)"
45
+ ```
46
+
47
+ This is a local release of the new nlpkit14 project, not yet uploaded to PyPI. Do not use pip install nlpkit14 until publication succeeds.
48
+
49
+ ## Offline installation
50
+
51
+ On an internet-connected machine with the SAME OS, architecture and Python version as the offline machine, download the wheel and dependencies:
52
+
53
+ ```sh
54
+ python -m pip download --dest wheelhouse './nlpkit14-0.3.0-py3-none-any.whl[models]'
55
+ ```
56
+
57
+ Install and run setup online on that matching machine. Copy the wheelhouse, both installed spaCy model packages (or their installation wheels), and the full data/cache folder to the offline machine. Model data defaults to ~/.nlpkit14; set NLPKIT14_DATA before setup/import to choose a portable folder. Copying the entire prepared Python environment is platform specific; do not copy a Mac environment to Windows.
58
+
59
+ On the offline computer:
60
+
61
+ ```sh
62
+ python -m pip install --no-index --find-links=wheelhouse nlpkit14==0.3.0
63
+ ```
64
+
65
+ Install the spaCy model wheels from local storage too. Keep the copied data folder at ~/.nlpkit14 or point NLPKIT14_DATA to it. Model and dependency archives are not included in this small library wheel.
66
+
67
+ q8 fetches https://www.bnmit.org/ and requires internet even after setup. Other exercises can run without internet after all their requirements are installed. No downloads run implicitly from q1–q14. Missing packages/data raise their real installation errors rather than producing fake results.
68
+
69
+ ## Output fidelity
70
+
71
+ The original algorithms and sample input are preserved. Downloads are moved to explicit setup. HTTP errors are checked. BERT uses evaluation mode and local cached model files. The raw Good–Turing demonstration is not a normalized probability estimator. The document's classifier fits its vocabulary before splitting and is an educational example, not an unbiased evaluation pipeline. Model outputs, parse trees, POS tags and similarity values can differ from the commented reference output. The document's B5 reference omits POS lines that the code actually prints. Read TEST_RESULTS.md for exactly which programs were executed.
@@ -0,0 +1,26 @@
1
+ MANIFEST.in
2
+ README.md
3
+ TEST_RESULTS.md
4
+ pyproject.toml
5
+ src/nlpkit14/__init__.py
6
+ src/nlpkit14/questions.json
7
+ src/nlpkit14.egg-info/PKG-INFO
8
+ src/nlpkit14.egg-info/SOURCES.txt
9
+ src/nlpkit14.egg-info/dependency_links.txt
10
+ src/nlpkit14.egg-info/requires.txt
11
+ src/nlpkit14.egg-info/top_level.txt
12
+ src/nlpkit14/programs/q1.py
13
+ src/nlpkit14/programs/q10.py
14
+ src/nlpkit14/programs/q11.py
15
+ src/nlpkit14/programs/q12.py
16
+ src/nlpkit14/programs/q13.py
17
+ src/nlpkit14/programs/q14.py
18
+ src/nlpkit14/programs/q2.py
19
+ src/nlpkit14/programs/q3.py
20
+ src/nlpkit14/programs/q4.py
21
+ src/nlpkit14/programs/q5.py
22
+ src/nlpkit14/programs/q6.py
23
+ src/nlpkit14/programs/q7.py
24
+ src/nlpkit14/programs/q8.py
25
+ src/nlpkit14/programs/q9.py
26
+ tests/test_programs.py
@@ -0,0 +1,11 @@
1
+ nltk<4,>=3.9
2
+ spacy<4,>=3.8
3
+ scikit-learn<2,>=1.5
4
+ requests<3,>=2.32
5
+ beautifulsoup4<5,>=4.12
6
+
7
+ [models]
8
+ torch<3,>=2.4
9
+ transformers<5,>=4.45
10
+ benepar<0.3,>=0.2.0
11
+ sentencepiece<1,>=0.2
@@ -0,0 +1 @@
1
+ nlpkit14
@@ -0,0 +1,87 @@
1
+ import contextlib
2
+ import io
3
+ import unittest
4
+ from zipfile import BadZipFile
5
+ from importlib.resources import files
6
+ from unittest.mock import patch
7
+
8
+ import nlpkit14 as n
9
+
10
+
11
+ class ProgramTests(unittest.TestCase):
12
+ def require_nltk(self, *resources):
13
+ import nltk
14
+ n._configure()
15
+ for resource in resources:
16
+ try:
17
+ nltk.data.find(resource)
18
+ except (LookupError, BadZipFile):
19
+ self.skipTest(f'Install test data: {resource}')
20
+
21
+ def capture(self, number):
22
+ output = io.StringIO()
23
+ with contextlib.redirect_stdout(output):
24
+ getattr(n, f'q{number}')()
25
+ return output.getvalue()
26
+
27
+ def test_all_sources_compile_and_have_comments(self):
28
+ for n in range(1, 15):
29
+ source = files('nlpkit14').joinpath('programs', f'q{n}.py').read_text()
30
+ self.assertTrue(source.startswith(f'# Question {n} '))
31
+ self.assertIn('# Expected output:', source)
32
+ compile(source, f'q{n}.py', 'exec')
33
+
34
+ def test_q9_executes_bigram(self):
35
+ self.assertIn('Sentence Probability = 0.33333333', self.capture(9))
36
+
37
+ def test_q1_executes_tokenization(self):
38
+ self.require_nltk('tokenizers/punkt_tab/english/', 'corpora/stopwords')
39
+ output = self.capture(1)
40
+ self.assertIn("Filtered Tokens: ['simple', 'example', 'demonstrating'", output)
41
+ self.assertNotIn('Expected output', output)
42
+
43
+ def test_q4_executes_stemming(self):
44
+ self.require_nltk('tokenizers/punkt_tab/english/', 'taggers/averaged_perceptron_tagger_eng/', 'corpora/wordnet.zip')
45
+ self.assertIn("['studi', 'studi', 'cri', 'cri', 'fli', 'fli']", self.capture(4))
46
+
47
+ def test_q10_executes_laplace(self):
48
+ output = self.capture(10)
49
+ self.assertIn('Vocabulary Size = 8', output)
50
+ self.assertIn('Add-One Sentence Probability = 0.00490909', output)
51
+
52
+ def test_q11_executes_raw_good_turing(self):
53
+ self.assertIn('1.3333', self.capture(11))
54
+
55
+ def test_q12_trains_classifier(self):
56
+ self.assertIn('accuracy', self.capture(12))
57
+
58
+ def test_source_does_not_execute(self):
59
+ output = io.StringIO()
60
+ with contextlib.redirect_stdout(output):
61
+ n.source(14)
62
+ self.assertIn('# Question 14 ', output.getvalue())
63
+
64
+ def test_all_questions_listed(self):
65
+ output = io.StringIO()
66
+ with contextlib.redirect_stdout(output):
67
+ n.questions()
68
+ self.assertEqual(len(output.getvalue().splitlines()), 14)
69
+
70
+ def test_offline_numeric_exercises(self):
71
+ with patch('socket.socket.connect', side_effect=AssertionError('Network forbidden')):
72
+ for n in (9, 10, 11, 12):
73
+ self.capture(n)
74
+
75
+ def test_q8_tags_supplied_html_without_live_website(self):
76
+ self.require_nltk('tokenizers/punkt_tab/english/', 'taggers/averaged_perceptron_tagger_eng/')
77
+ import requests
78
+ class Response:
79
+ content = b'<p>Cats chase mice.</p>'
80
+ def raise_for_status(self):
81
+ pass
82
+ with patch.object(requests, 'get', return_value=Response()):
83
+ self.assertIn('POS Tags', self.capture(8))
84
+
85
+
86
+ if __name__ == '__main__':
87
+ unittest.main()