simonbb 0.0.2__tar.gz → 0.0.3__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.
- simonbb-0.0.3/PKG-INFO +38 -0
- simonbb-0.0.3/README.md +13 -0
- simonbb-0.0.3/pyproject.toml +35 -0
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis433/gpt.py +1 -4
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis433/transformer.py +2 -4
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis433/utils.py +4 -161
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis438/agentic.py +6 -5
- simonbb-0.0.3/src/simonbb/utils.py +158 -0
- simonbb-0.0.2/PKG-INFO +0 -16
- simonbb-0.0.2/README.md +0 -2
- simonbb-0.0.2/pyproject.toml +0 -23
- {simonbb-0.0.2 → simonbb-0.0.3}/LICENSE +0 -0
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/__init__.py +0 -0
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis433/__init__.py +0 -0
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis433/bert.py +0 -0
- {simonbb-0.0.2 → simonbb-0.0.3}/src/simonbb/cis438/__init__.py +0 -0
simonbb-0.0.3/PKG-INFO
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simonbb
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: A small package to facilitate teaching AI/ML courses
|
|
5
|
+
Project-URL: Homepage, https://simonbb.com
|
|
6
|
+
Author-email: Huaxia Rui <huaxia.rui@simon.rochester.edu>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Requires-Dist: langchain-chroma~=1.1.0
|
|
13
|
+
Requires-Dist: langchain-community~=0.4.1
|
|
14
|
+
Requires-Dist: langchain-core~=1.2.23
|
|
15
|
+
Requires-Dist: langchain-huggingface~=1.2.1
|
|
16
|
+
Requires-Dist: langchain~=1.2.13
|
|
17
|
+
Requires-Dist: langgraph~=1.1.3
|
|
18
|
+
Requires-Dist: matplotlib~=3.10.8
|
|
19
|
+
Requires-Dist: numpy~=2.4.0
|
|
20
|
+
Requires-Dist: sentence-transformers~=5.3.0
|
|
21
|
+
Requires-Dist: torchvision~=0.24.1
|
|
22
|
+
Requires-Dist: torch~=2.9.1
|
|
23
|
+
Requires-Dist: transformers~=4.57.3
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Background
|
|
27
|
+
|
|
28
|
+
This is a pet project out of my own teaching needs at University of Rochester.
|
|
29
|
+
I mainly teach these 2 courses to MBA/MS students who lack technical background.
|
|
30
|
+
|
|
31
|
+
- CIS 433: AI and Deep Learning
|
|
32
|
+
- CIS 438: Agentic AI Applications
|
|
33
|
+
|
|
34
|
+
Accordingly, this package contains 2 modules.
|
|
35
|
+
|
|
36
|
+
- `simonbb.cis433`: this module is useful for anyone teacheing deep learning using PyTorch but doesn't want to bother students with too much nitty-gritty details.
|
|
37
|
+
- `simonbb.cis438`: this module is useful for anyone who teaches agentic AI using langchain.
|
|
38
|
+
|
simonbb-0.0.3/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Background
|
|
2
|
+
|
|
3
|
+
This is a pet project out of my own teaching needs at University of Rochester.
|
|
4
|
+
I mainly teach these 2 courses to MBA/MS students who lack technical background.
|
|
5
|
+
|
|
6
|
+
- CIS 433: AI and Deep Learning
|
|
7
|
+
- CIS 438: Agentic AI Applications
|
|
8
|
+
|
|
9
|
+
Accordingly, this package contains 2 modules.
|
|
10
|
+
|
|
11
|
+
- `simonbb.cis433`: this module is useful for anyone teacheing deep learning using PyTorch but doesn't want to bother students with too much nitty-gritty details.
|
|
12
|
+
- `simonbb.cis438`: this module is useful for anyone who teaches agentic AI using langchain.
|
|
13
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling >= 1.26"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "simonbb"
|
|
7
|
+
version = "0.0.3"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Huaxia Rui", email="huaxia.rui@simon.rochester.edu" },
|
|
10
|
+
]
|
|
11
|
+
description = "A small package to facilitate teaching AI/ML courses"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICEN[CS]E*"]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"matplotlib~=3.10.8",
|
|
22
|
+
"torch~=2.9.1",
|
|
23
|
+
"torchvision~=0.24.1",
|
|
24
|
+
"numpy~=2.4.0",
|
|
25
|
+
"transformers~=4.57.3",
|
|
26
|
+
"langchain-core~=1.2.23",
|
|
27
|
+
"langchain~=1.2.13",
|
|
28
|
+
"langgraph~=1.1.3",
|
|
29
|
+
"langchain-community~=0.4.1",
|
|
30
|
+
"langchain-chroma~=1.1.0",
|
|
31
|
+
"langchain-huggingface~=1.2.1",
|
|
32
|
+
"sentence-transformers~=5.3.0",
|
|
33
|
+
]
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://simonbb.com"
|
|
@@ -198,7 +198,4 @@ if __name__ == "__main__":
|
|
|
198
198
|
out = generate( model=model, idx=encoded_tensor, max_new_tokens=6, context_size=cfg["context_length"] )
|
|
199
199
|
|
|
200
200
|
print("Output:", out)
|
|
201
|
-
print( tokenizer.decode(out.squeeze(0).tolist()) )
|
|
202
|
-
|
|
203
|
-
else:
|
|
204
|
-
print(f'GPT imported from local file "{__name__}.py"')
|
|
201
|
+
print( tokenizer.decode(out.squeeze(0).tolist()) )
|
|
@@ -209,7 +209,7 @@ class Transformer(nn.Module):
|
|
|
209
209
|
return self.final_layer(x) # (batch_size, target_len, tgt_vocab_size)
|
|
210
210
|
|
|
211
211
|
|
|
212
|
-
#%% Sanity Check: run from the parent folder of
|
|
212
|
+
#%% Sanity Check: run from the parent folder of simonbb as "python -m simonbb.cis433.transformer" since we used relative import
|
|
213
213
|
if __name__ == "__main__":
|
|
214
214
|
|
|
215
215
|
from ..utils import TextVectorizer
|
|
@@ -248,6 +248,4 @@ if __name__ == "__main__":
|
|
|
248
248
|
decoder.eval()
|
|
249
249
|
with torch.no_grad():
|
|
250
250
|
print(f"Encoder output shape: {encoder(x).shape}")
|
|
251
|
-
print(f"Decoder output shape: {decoder(x=x, context=x).shape}")
|
|
252
|
-
else:
|
|
253
|
-
print(f'Transformer imported from local file "{__name__}.py"')
|
|
251
|
+
print(f"Decoder output shape: {decoder(x=x, context=x).shape}")
|
|
@@ -5,174 +5,15 @@ A collection of utility functions for teaching deep learning
|
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
7
|
import time
|
|
8
|
-
import numpy as np
|
|
9
8
|
import copy
|
|
10
|
-
import re
|
|
11
|
-
import pickle
|
|
12
|
-
import json
|
|
13
|
-
from collections import Counter
|
|
14
|
-
import urllib.request
|
|
15
9
|
|
|
10
|
+
import numpy as np
|
|
16
11
|
import matplotlib.pyplot as plt
|
|
17
|
-
|
|
18
12
|
import torch
|
|
19
13
|
import torch.nn as nn
|
|
20
14
|
|
|
21
15
|
#%%
|
|
22
16
|
|
|
23
|
-
class TextVectorizer:
|
|
24
|
-
def __init__(self, max_tokens=20000, output_mode="int", output_sequence_length=None, ngrams=None, standardize=None):
|
|
25
|
-
self.max_tokens = max_tokens
|
|
26
|
-
self.output_mode = output_mode
|
|
27
|
-
self.output_sequence_length = output_sequence_length
|
|
28
|
-
self.ngrams = ngrams
|
|
29
|
-
self.standardize = standardize
|
|
30
|
-
self.vocab = None
|
|
31
|
-
self.word_to_idx = None
|
|
32
|
-
self.idx_to_word = None
|
|
33
|
-
self.idf_weights = None
|
|
34
|
-
|
|
35
|
-
def _tokenize(self, text):
|
|
36
|
-
if self.standardize:
|
|
37
|
-
text = self.standardize(text)
|
|
38
|
-
else:
|
|
39
|
-
text = text.lower()
|
|
40
|
-
text = re.sub(r'[^\w\s]', ' ', text)
|
|
41
|
-
tokens = text.split()
|
|
42
|
-
if self.ngrams and self.ngrams >= 2:
|
|
43
|
-
ngram_tokens = []
|
|
44
|
-
for n in range(1, self.ngrams + 1):
|
|
45
|
-
for i in range( len(tokens) - n + 1 ):
|
|
46
|
-
ngram_tokens.append(' '.join(tokens[i:i + n]))
|
|
47
|
-
return ngram_tokens
|
|
48
|
-
return tokens
|
|
49
|
-
|
|
50
|
-
def adapt(self, texts):
|
|
51
|
-
"""Build vocabulary from texts"""
|
|
52
|
-
token_counter = Counter()
|
|
53
|
-
doc_freq = Counter() # for TF-IDF
|
|
54
|
-
num_docs = 0
|
|
55
|
-
print("Adapting TextVectorizer...")
|
|
56
|
-
for text in texts:
|
|
57
|
-
num_docs += 1
|
|
58
|
-
tokens = self._tokenize(text)
|
|
59
|
-
token_counter.update(tokens)
|
|
60
|
-
doc_freq.update(set(tokens))
|
|
61
|
-
# Reserve 0 for padding, 1 for unknown, hence minus 2 to account for them.
|
|
62
|
-
most_common = token_counter.most_common(self.max_tokens - 2 if self.max_tokens else None) # most_common(n=None) returns all elements in the counter if n is omitted or None.
|
|
63
|
-
self.vocab = ['<pad>', '<unk>'] + [word for word, _ in most_common] # 0 for padding, 1 for unknown
|
|
64
|
-
self.word_to_idx = {word: idx for idx, word in enumerate(self.vocab)}
|
|
65
|
-
self.idx_to_word = {idx: word for idx, word in enumerate(self.vocab)}
|
|
66
|
-
# Compute IDF weights for tf-idf mode
|
|
67
|
-
if self.output_mode == "tf_idf":
|
|
68
|
-
self.idf_weights = np.zeros(len(self.vocab))
|
|
69
|
-
for word, idx in self.word_to_idx.items():
|
|
70
|
-
df = doc_freq.get(word, 0) + 1 # add 1 for smoothing
|
|
71
|
-
self.idf_weights[idx] = np.log(num_docs / df) + 1
|
|
72
|
-
print(f"Vocabulary size: {len(self.vocab)}")
|
|
73
|
-
|
|
74
|
-
def __call__(self, texts):
|
|
75
|
-
if isinstance(texts, str):
|
|
76
|
-
texts = [texts]
|
|
77
|
-
if self.output_mode == "int":
|
|
78
|
-
return self._vectorize_int(texts)
|
|
79
|
-
elif self.output_mode == "multi_hot":
|
|
80
|
-
return self._vectorize_multi_hot(texts)
|
|
81
|
-
elif self.output_mode == "tf_idf":
|
|
82
|
-
return self._vectorize_tfidf(texts)
|
|
83
|
-
|
|
84
|
-
def _vectorize_int(self, texts):
|
|
85
|
-
batch_indices = []
|
|
86
|
-
for text in texts:
|
|
87
|
-
tokens = self._tokenize(text)
|
|
88
|
-
indices = [self.word_to_idx.get(t, 1) for t in tokens] # 1 is <unk>
|
|
89
|
-
if self.output_sequence_length:
|
|
90
|
-
if len(indices) < self.output_sequence_length:
|
|
91
|
-
indices = indices + [0] * (self.output_sequence_length - len(indices))
|
|
92
|
-
else:
|
|
93
|
-
indices = indices[:self.output_sequence_length]
|
|
94
|
-
batch_indices.append(indices)
|
|
95
|
-
return np.array(batch_indices)
|
|
96
|
-
|
|
97
|
-
def _vectorize_multi_hot(self, texts):
|
|
98
|
-
result = np.zeros( (len(texts), len(self.vocab)), dtype=np.float32 )
|
|
99
|
-
for i, text in enumerate(texts):
|
|
100
|
-
tokens = self._tokenize(text)
|
|
101
|
-
for t in tokens:
|
|
102
|
-
idx = self.word_to_idx.get(t, 1)
|
|
103
|
-
if idx < len(self.vocab):
|
|
104
|
-
result[i, idx] = 1
|
|
105
|
-
return result
|
|
106
|
-
|
|
107
|
-
def _vectorize_tfidf(self, texts):
|
|
108
|
-
result = np.zeros( (len(texts), len(self.vocab)), dtype=np.float32 )
|
|
109
|
-
for i, text in enumerate(texts):
|
|
110
|
-
tokens = self._tokenize(text)
|
|
111
|
-
tf = Counter(tokens)
|
|
112
|
-
for t, count in tf.items():
|
|
113
|
-
idx = self.word_to_idx.get(t, 1)
|
|
114
|
-
if idx < len(self.vocab):
|
|
115
|
-
result[i, idx] = count * self.idf_weights[idx]
|
|
116
|
-
return result
|
|
117
|
-
|
|
118
|
-
def get_vocabulary(self):
|
|
119
|
-
return self.vocab
|
|
120
|
-
|
|
121
|
-
def save(self, path):
|
|
122
|
-
vec = {'max_tokens': self.max_tokens, 'output_sequence_length': self.output_sequence_length,
|
|
123
|
-
'vocab': self.vocab, 'word_to_idx': self.word_to_idx, 'idx_to_word': self.idx_to_word,
|
|
124
|
-
'has_standardizer': self.standardize is not None}
|
|
125
|
-
with open(path, 'wb') as f:
|
|
126
|
-
pickle.dump(vec, f)
|
|
127
|
-
|
|
128
|
-
# transforms a method into a class method (i.e., bound to the class rather than an individual instance)
|
|
129
|
-
@classmethod
|
|
130
|
-
def load(cls, path, standardize=None):
|
|
131
|
-
data = pickle.load(path, weights_only=False)
|
|
132
|
-
vectorizer = cls( max_tokens=data['max_tokens'], output_sequence_length=data['output_sequence_length'],
|
|
133
|
-
standardize=standardize if data.get('has_standardize') else None )
|
|
134
|
-
vectorizer.vocab = data['vocab']
|
|
135
|
-
vectorizer.word_to_idx = data['word_to_idx']
|
|
136
|
-
vectorizer.idx_to_word = data['idx_to_word']
|
|
137
|
-
return vectorizer
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def query_ollama(prompt, model="llama3", url="http://localhost:11434/api/chat"):
|
|
141
|
-
# Create the data payload as a dictionary
|
|
142
|
-
data = {
|
|
143
|
-
"model": model,
|
|
144
|
-
"messages": [
|
|
145
|
-
{"role": "user", "content": prompt}
|
|
146
|
-
],
|
|
147
|
-
"options": { # Settings below are required for deterministic responses
|
|
148
|
-
"seed": 123,
|
|
149
|
-
"temperature": 0,
|
|
150
|
-
"num_ctx": 2048
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
# Convert the dictionary to a JSON formatted string and encode it to bytes
|
|
155
|
-
payload = json.dumps(data).encode("utf-8")
|
|
156
|
-
|
|
157
|
-
# Create a request object, setting the method to POST and adding necessary headers
|
|
158
|
-
request = urllib.request.Request(url, data=payload, method="POST")
|
|
159
|
-
request.add_header("Content-Type", "application/json")
|
|
160
|
-
|
|
161
|
-
# Send the request and capture the response
|
|
162
|
-
response_data = ""
|
|
163
|
-
with urllib.request.urlopen(request) as response:
|
|
164
|
-
# Read and decode the response
|
|
165
|
-
while True:
|
|
166
|
-
line = response.readline().decode("utf-8")
|
|
167
|
-
if not line:
|
|
168
|
-
break
|
|
169
|
-
response_json = json.loads(line)
|
|
170
|
-
response_data += response_json["message"]["content"]
|
|
171
|
-
|
|
172
|
-
return response_data
|
|
173
|
-
|
|
174
|
-
#%%
|
|
175
|
-
|
|
176
17
|
class ModelCheckpoint:
|
|
177
18
|
def __init__(self, filepath, monitor="val_loss", save_best_only=True, save_optimizer_state=False):
|
|
178
19
|
self.filepath = filepath
|
|
@@ -470,10 +311,12 @@ def evaluate(model, loader, device, loss_fn=nn.CrossEntropyLoss(), accuracy_fn=N
|
|
|
470
311
|
val_accuracy = val_accuracy.item() # extract the value of a single-item tensor
|
|
471
312
|
return {'loss':val_loss, 'accuracy':val_accuracy}
|
|
472
313
|
|
|
473
|
-
#%%
|
|
314
|
+
#%% Sanity Check: run from the parent folder of simonbb as "python -m simonbb.cis433.utils" since we used relative import
|
|
474
315
|
|
|
475
316
|
if __name__ == "__main__":
|
|
476
317
|
|
|
318
|
+
from ..utils import TextVectorizer
|
|
319
|
+
|
|
477
320
|
vectorizer = TextVectorizer( max_tokens=10, output_mode="int" )
|
|
478
321
|
txts = ['how are you']
|
|
479
322
|
vectorizer.adapt( txts )
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# module dependencies: torch torchvision langchain_core langchain langgraph langchain_community langchain_chroma langchain_huggingface sentence_transformers
|
|
2
|
+
# additional packages for class projects: langchain_ollama langchain_text_splitters unstructured streamlit_javascript
|
|
3
|
+
|
|
1
4
|
import os
|
|
2
5
|
import re
|
|
3
6
|
from typing import Annotated, Literal, TypedDict, get_args
|
|
@@ -17,10 +20,9 @@ from langgraph.graph.message import add_messages
|
|
|
17
20
|
|
|
18
21
|
from langchain_chroma import Chroma
|
|
19
22
|
from langchain_huggingface import HuggingFaceEmbeddings
|
|
20
|
-
|
|
23
|
+
from langchain_community.document_loaders import PyPDFLoader, UnstructuredHTMLLoader
|
|
21
24
|
from sentence_transformers import CrossEncoder
|
|
22
25
|
|
|
23
|
-
vstore_path = f'{os.getenv("HOME")}/vstore'
|
|
24
26
|
embedding_model = HuggingFaceEmbeddings(
|
|
25
27
|
model_name="BAAI/bge-large-en-v1.5",
|
|
26
28
|
model_kwargs={'device': device},
|
|
@@ -30,8 +32,7 @@ embedding_model = HuggingFaceEmbeddings(
|
|
|
30
32
|
#%%
|
|
31
33
|
|
|
32
34
|
def load_documents(path):
|
|
33
|
-
"""Load documents from a file or directory."""
|
|
34
|
-
from langchain_community.document_loaders import PyPDFLoader, UnstructuredHTMLLoader
|
|
35
|
+
"""Load documents from a file or directory."""
|
|
35
36
|
|
|
36
37
|
docs = []
|
|
37
38
|
success_cnt = 0
|
|
@@ -78,7 +79,7 @@ def load_documents(path):
|
|
|
78
79
|
return docs, success_cnt, page_cnt
|
|
79
80
|
|
|
80
81
|
reranker_model = CrossEncoder("BAAI/bge-reranker-large")
|
|
81
|
-
def _getRetriever( knowledge_base, query_llm=None, k=3 ):
|
|
82
|
+
def _getRetriever( knowledge_base, query_llm=None, k=3, vstore_path = f'{os.getenv("HOME")}/vstore' ):
|
|
82
83
|
store = Chroma( persist_directory=f'{vstore_path}/{knowledge_base}/BAAI_db/chroma_vector_store', embedding_function=embedding_model )
|
|
83
84
|
base_retriever = store.as_retriever(search_kwargs={"k": k}) # a vector store becomes a retriever through the as_retriever() method
|
|
84
85
|
if query_llm==None:
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import pickle
|
|
3
|
+
import json
|
|
4
|
+
from collections import Counter
|
|
5
|
+
import urllib.request
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TextVectorizer:
|
|
10
|
+
def __init__(self, max_tokens=20000, output_mode="int", output_sequence_length=None, ngrams=None, standardize=None):
|
|
11
|
+
self.max_tokens = max_tokens
|
|
12
|
+
self.output_mode = output_mode
|
|
13
|
+
self.output_sequence_length = output_sequence_length
|
|
14
|
+
self.ngrams = ngrams
|
|
15
|
+
self.standardize = standardize
|
|
16
|
+
self.vocab = None
|
|
17
|
+
self.word_to_idx = None
|
|
18
|
+
self.idx_to_word = None
|
|
19
|
+
self.idf_weights = None
|
|
20
|
+
|
|
21
|
+
def _tokenize(self, text):
|
|
22
|
+
if self.standardize:
|
|
23
|
+
text = self.standardize(text)
|
|
24
|
+
else:
|
|
25
|
+
text = text.lower()
|
|
26
|
+
text = re.sub(r'[^\w\s]', ' ', text)
|
|
27
|
+
tokens = text.split()
|
|
28
|
+
if self.ngrams and self.ngrams >= 2:
|
|
29
|
+
ngram_tokens = []
|
|
30
|
+
for n in range(1, self.ngrams + 1):
|
|
31
|
+
for i in range( len(tokens) - n + 1 ):
|
|
32
|
+
ngram_tokens.append(' '.join(tokens[i:i + n]))
|
|
33
|
+
return ngram_tokens
|
|
34
|
+
return tokens
|
|
35
|
+
|
|
36
|
+
def adapt(self, texts):
|
|
37
|
+
"""Build vocabulary from texts"""
|
|
38
|
+
token_counter = Counter()
|
|
39
|
+
doc_freq = Counter() # for TF-IDF
|
|
40
|
+
num_docs = 0
|
|
41
|
+
print("Adapting TextVectorizer...")
|
|
42
|
+
for text in texts:
|
|
43
|
+
num_docs += 1
|
|
44
|
+
tokens = self._tokenize(text)
|
|
45
|
+
token_counter.update(tokens)
|
|
46
|
+
doc_freq.update(set(tokens))
|
|
47
|
+
# Reserve 0 for padding, 1 for unknown, hence minus 2 to account for them.
|
|
48
|
+
most_common = token_counter.most_common(self.max_tokens - 2 if self.max_tokens else None) # most_common(n=None) returns all elements in the counter if n is omitted or None.
|
|
49
|
+
self.vocab = ['<pad>', '<unk>'] + [word for word, _ in most_common] # 0 for padding, 1 for unknown
|
|
50
|
+
self.word_to_idx = {word: idx for idx, word in enumerate(self.vocab)}
|
|
51
|
+
self.idx_to_word = {idx: word for idx, word in enumerate(self.vocab)}
|
|
52
|
+
# Compute IDF weights for tf-idf mode
|
|
53
|
+
if self.output_mode == "tf_idf":
|
|
54
|
+
self.idf_weights = np.zeros(len(self.vocab))
|
|
55
|
+
for word, idx in self.word_to_idx.items():
|
|
56
|
+
df = doc_freq.get(word, 0) + 1 # add 1 for smoothing
|
|
57
|
+
self.idf_weights[idx] = np.log(num_docs / df) + 1
|
|
58
|
+
print(f"Vocabulary size: {len(self.vocab)}")
|
|
59
|
+
|
|
60
|
+
def __call__(self, texts):
|
|
61
|
+
if isinstance(texts, str):
|
|
62
|
+
texts = [texts]
|
|
63
|
+
if self.output_mode == "int":
|
|
64
|
+
return self._vectorize_int(texts)
|
|
65
|
+
elif self.output_mode == "multi_hot":
|
|
66
|
+
return self._vectorize_multi_hot(texts)
|
|
67
|
+
elif self.output_mode == "tf_idf":
|
|
68
|
+
return self._vectorize_tfidf(texts)
|
|
69
|
+
|
|
70
|
+
def _vectorize_int(self, texts):
|
|
71
|
+
batch_indices = []
|
|
72
|
+
for text in texts:
|
|
73
|
+
tokens = self._tokenize(text)
|
|
74
|
+
indices = [self.word_to_idx.get(t, 1) for t in tokens] # 1 is <unk>
|
|
75
|
+
if self.output_sequence_length:
|
|
76
|
+
if len(indices) < self.output_sequence_length:
|
|
77
|
+
indices = indices + [0] * (self.output_sequence_length - len(indices))
|
|
78
|
+
else:
|
|
79
|
+
indices = indices[:self.output_sequence_length]
|
|
80
|
+
batch_indices.append(indices)
|
|
81
|
+
return np.array(batch_indices)
|
|
82
|
+
|
|
83
|
+
def _vectorize_multi_hot(self, texts):
|
|
84
|
+
result = np.zeros( (len(texts), len(self.vocab)), dtype=np.float32 )
|
|
85
|
+
for i, text in enumerate(texts):
|
|
86
|
+
tokens = self._tokenize(text)
|
|
87
|
+
for t in tokens:
|
|
88
|
+
idx = self.word_to_idx.get(t, 1)
|
|
89
|
+
if idx < len(self.vocab):
|
|
90
|
+
result[i, idx] = 1
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
def _vectorize_tfidf(self, texts):
|
|
94
|
+
result = np.zeros( (len(texts), len(self.vocab)), dtype=np.float32 )
|
|
95
|
+
for i, text in enumerate(texts):
|
|
96
|
+
tokens = self._tokenize(text)
|
|
97
|
+
tf = Counter(tokens)
|
|
98
|
+
for t, count in tf.items():
|
|
99
|
+
idx = self.word_to_idx.get(t, 1)
|
|
100
|
+
if idx < len(self.vocab):
|
|
101
|
+
result[i, idx] = count * self.idf_weights[idx]
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
def get_vocabulary(self):
|
|
105
|
+
return self.vocab
|
|
106
|
+
|
|
107
|
+
def save(self, path):
|
|
108
|
+
vec = {'max_tokens': self.max_tokens, 'output_sequence_length': self.output_sequence_length,
|
|
109
|
+
'vocab': self.vocab, 'word_to_idx': self.word_to_idx, 'idx_to_word': self.idx_to_word,
|
|
110
|
+
'has_standardizer': self.standardize is not None}
|
|
111
|
+
with open(path, 'wb') as f:
|
|
112
|
+
pickle.dump(vec, f)
|
|
113
|
+
|
|
114
|
+
# transforms a method into a class method (i.e., bound to the class rather than an individual instance)
|
|
115
|
+
@classmethod
|
|
116
|
+
def load(cls, path, standardize=None):
|
|
117
|
+
data = pickle.load(path, weights_only=False)
|
|
118
|
+
vectorizer = cls( max_tokens=data['max_tokens'], output_sequence_length=data['output_sequence_length'],
|
|
119
|
+
standardize=standardize if data.get('has_standardize') else None )
|
|
120
|
+
vectorizer.vocab = data['vocab']
|
|
121
|
+
vectorizer.word_to_idx = data['word_to_idx']
|
|
122
|
+
vectorizer.idx_to_word = data['idx_to_word']
|
|
123
|
+
return vectorizer
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def query_ollama(prompt, model="llama3", url="http://localhost:11434/api/chat"):
|
|
127
|
+
# Create the data payload as a dictionary
|
|
128
|
+
data = {
|
|
129
|
+
"model": model,
|
|
130
|
+
"messages": [
|
|
131
|
+
{"role": "user", "content": prompt}
|
|
132
|
+
],
|
|
133
|
+
"options": { # Settings below are required for deterministic responses
|
|
134
|
+
"seed": 123,
|
|
135
|
+
"temperature": 0,
|
|
136
|
+
"num_ctx": 2048
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Convert the dictionary to a JSON formatted string and encode it to bytes
|
|
141
|
+
payload = json.dumps(data).encode("utf-8")
|
|
142
|
+
|
|
143
|
+
# Create a request object, setting the method to POST and adding necessary headers
|
|
144
|
+
request = urllib.request.Request(url, data=payload, method="POST")
|
|
145
|
+
request.add_header("Content-Type", "application/json")
|
|
146
|
+
|
|
147
|
+
# Send the request and capture the response
|
|
148
|
+
response_data = ""
|
|
149
|
+
with urllib.request.urlopen(request) as response:
|
|
150
|
+
# Read and decode the response
|
|
151
|
+
while True:
|
|
152
|
+
line = response.readline().decode("utf-8")
|
|
153
|
+
if not line:
|
|
154
|
+
break
|
|
155
|
+
response_json = json.loads(line)
|
|
156
|
+
response_data += response_json["message"]["content"]
|
|
157
|
+
|
|
158
|
+
return response_data
|
simonbb-0.0.2/PKG-INFO
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: simonbb
|
|
3
|
-
Version: 0.0.2
|
|
4
|
-
Summary: A small package for teaching AI/ML courses
|
|
5
|
-
Project-URL: Homepage, https://simonbb.com
|
|
6
|
-
Project-URL: Issues, https://simonbb.com/issues
|
|
7
|
-
Author-email: Huaxia Rui <huaxia.rui@simon.rochester.edu>
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python: >=3.9
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
|
|
15
|
-
# What's this package for?
|
|
16
|
-
Anyone who teaches deep learning using PyTorch but doesn't want to bother students with too much nitty-gritty details.
|
simonbb-0.0.2/README.md
DELETED
simonbb-0.0.2/pyproject.toml
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["hatchling >= 1.26"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "simonbb"
|
|
7
|
-
version = "0.0.2"
|
|
8
|
-
authors = [
|
|
9
|
-
{ name="Huaxia Rui", email="huaxia.rui@simon.rochester.edu" },
|
|
10
|
-
]
|
|
11
|
-
description = "A small package for teaching AI/ML courses"
|
|
12
|
-
readme = "README.md"
|
|
13
|
-
requires-python = ">=3.9"
|
|
14
|
-
classifiers = [
|
|
15
|
-
"Programming Language :: Python :: 3",
|
|
16
|
-
"Operating System :: OS Independent",
|
|
17
|
-
]
|
|
18
|
-
license = "MIT"
|
|
19
|
-
license-files = ["LICEN[CS]E*"]
|
|
20
|
-
|
|
21
|
-
[project.urls]
|
|
22
|
-
Homepage = "https://simonbb.com"
|
|
23
|
-
Issues = "https://simonbb.com/issues"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|