Views
No views yet
1from transformers import AutoTokenizer, AlbertConfig, AlbertModel
2import torch
3import torch.nn as nn
4import re
5import numpy as np
6from safetensors.torch import load_file # Import safe_load for safetensors
7from huggingface_hub import hf_hub_download # Import hf_hub_download
8
9# Redefine the model class (must be the same as during training)
10class AlbertForPunctuationAndCasing(nn.Module):
11 def __init__(self, config):
12 super().__init__()
13 self.num_punctuation_labels = config.num_punctuation_labels
14 self.num_casing_labels = config.num_casing_labels
15
16 # Initialize AlbertModel directly with the config provided
17 # This config should ideally reflect the true albert-large-v2 architecture
18 self.albert = AlbertModel(config)
19 self.dropout = nn.Dropout(config.classifier_dropout_prob)
20
21 self.punctuation_classifier = nn.Linear(config.hidden_size, self.num_punctuation_labels)
22 self.casing_classifier = nn.Linear(config.hidden_size, self.num_casing_labels)
23
24 def forward(
25 self,
26 input_ids=None,
27 attention_mask=None,
28 token_type_ids=None,
29 position_ids=None,
30 head_mask=None,
31 inputs_embeds=None,
32 casing_labels=None,
33 punctuation_labels=None,
34 output_attentions=None,
35 output_hidden_states=None,
36 return_dict=None,
37 ):
38 return_dict = return_dict if return_dict is not None else True
39
40 outputs = self.albert(
41 input_ids,
42 attention_mask=attention_mask,
43 token_type_ids=token_type_ids,
44 position_ids=position_ids,
45 head_mask=head_mask,
46 inputs_embeds=inputs_embeds,
47 output_attentions=output_attentions,
48 output_hidden_states=output_hidden_states,
49 return_dict=return_dict,
50 )
51
52 sequence_output = outputs[0]
53
54 sequence_output = self.dropout(sequence_output)
55 punctuation_logits = self.punctuation_classifier(sequence_output)
56 casing_logits = self.casing_classifier(sequence_output)
57
58 loss = None
59 if casing_labels is not None and punctuation_labels is not None:
60 loss_fct = nn.CrossEntropyLoss(ignore_index=-100)
61
62 punctuation_loss = loss_fct(punctuation_logits.view(-1, self.num_punctuation_labels), punctuation_labels.view(-1))
63
64 casing_loss = loss_fct(casing_logits.view(-1, self.num_casing_labels), casing_labels.view(-1))
65
66 loss = punctuation_loss + casing_loss
67
68 if not return_dict:
69 output = (punctuation_logits, casing_logits) + outputs[2:]
70 return ((loss,) + output) if loss is not None else output
71
72 result = {
73 "loss": loss,
74 "punctuation_logits": punctuation_logits,
75 "casing_logits": casing_logits,
76 }
77 if outputs.hidden_states is not None:
78 result["hidden_states"] = outputs.hidden_states
79 if outputs.attentions is not None:
80 result["attentions"] = outputs.attentions
81 return result
82
83
84# --- Configuration and Mappings (must be the same as during training) ---
85punctuation_labels = ['O', '.', ',', '?', '!', ';', ':', '(', ')', '/', '\\']
86punctuation_to_id = {label: i for i, label in enumerate(punctuation_labels)}
87id_to_punctuation = {i: label for i, label in enumerate(punctuation_labels)}
88
89casing_labels = ['O', 'CAP', 'UPPER']
90casing_to_id = {label: i for i, label in enumerate(casing_labels)}
91id_to_casing = {i: label for i, label in enumerate(casing_labels)}
92
93model_checkpoint = 'albert-base-v2'
94
95# Define the Hugging Face repository ID
96hf_repo_id = "MihaiPopa-1/ReCasePunct-1.1-Flash-Lite"
97
98# Load tokenizer from Hugging Face Hub
99tokenizer = AutoTokenizer.from_pretrained(hf_repo_id)
100
101# --- CORRECTED MODEL CONFIG LOADING ---
102# 1. Load the base ALBERT Base v2 configuration to get correct architecture defaults (like hidden_size)
103config = AlbertConfig.from_pretrained(model_checkpoint)
104
105# 2. Set the custom labels on this correctly sized config
106config.num_punctuation_labels = len(punctuation_labels)
107config.num_casing_labels = len(casing_labels)
108
109# Instantiate the custom model with the corrected config
110model = AlbertForPunctuationAndCasing(config)
111
112# Download the model.safetensors file from the Hub
113safetensors_path = hf_hub_download(repo_id=hf_repo_id, filename="model.safetensors")
114
115# Load the full state dictionary into the custom model
116model.load_state_dict(load_file(safetensors_path, device='cpu'))
117model.eval()
118
119
120def clean_text(text):
121 """Removes punctuation and converts text to lowercase for the model input."""
122 text = text.lower()
123 text = re.sub(r'[\.,\?!\-;:"\(\)\[\]\{\}\/\\]', '', text) # Remove common punctuation
124 text = re.sub(r'\s+', ' ', text).strip() # Replace multiple spaces with single space
125 return text
126
127def predict_punctuation_and_casing(text, model, tokenizer, id_to_punctuation, id_to_casing):
128 # Clean the input text similar to how training data was prepared
129 cleaned_text_input = clean_text(text)
130 words_in_cleaned_text = cleaned_text_input.split()
131
132 # Tokenize the input
133 tokenized_input = tokenizer(
134 cleaned_text_input,
135 return_offsets_mapping=True,
136 truncation=True,
137 max_length=tokenizer.model_max_length,
138 return_tensors="pt"
139 )
140
141 # Perform inference
142 with torch.no_grad():
143 outputs = model(
144 input_ids=tokenized_input['input_ids'],
145 attention_mask=tokenized_input['attention_mask']
146 )
147
148 punctuation_logits = outputs['punctuation_logits'].squeeze(0).numpy()
149 casing_logits = outputs['casing_logits'].squeeze(0).numpy()
150
151 punctuation_predictions = np.argmax(punctuation_logits, axis=-1)
152 casing_predictions = np.argmax(casing_logits, axis=-1)
153
154 # Initialize output list for reconstructed sentence
155 reconstructed_text_parts = []
156 current_word_idx = 0
157
158 # Iterate over tokens and apply predictions
159 for token_idx, (token_start, token_end) in enumerate(tokenized_input['offset_mapping'].squeeze(0).numpy()):
160 if token_start == 0 and token_end == 0: # Skip special tokens like [CLS], [SEP]
161 continue
162
163 # Get the word from the original cleaned text (not subword)
164 # This requires careful alignment if a single word maps to multiple tokens
165 # and apply label to the last token of a word.
166
167 # Find the actual word from the input_text_single corresponding to this token
168 token_text = cleaned_text_input[token_start:token_end]
169
170 # Check if this token is the beginning of a word we care about
171 if current_word_idx < len(words_in_cleaned_text) and words_in_cleaned_text[current_word_idx].startswith(token_text):
172 word = words_in_cleaned_text[current_word_idx]
173
174 # Apply casing
175 casing_pred_label = id_to_casing[casing_predictions[token_idx]]
176 if casing_pred_label == 'CAP':
177 word = word.capitalize()
178 elif casing_pred_label == 'UPPER':
179 word = word.upper()
180
181 # Apply punctuation (only to the last subword token of a word)
182 # This is a heuristic and might need refinement for complex tokenizations
183 next_token_word_idx = -1
184 if token_idx + 1 < len(tokenized_input['offset_mapping'].squeeze(0).numpy()):
185 next_token_start, _ = tokenized_input['offset_mapping'].squeeze(0).numpy()[token_idx+1]
186 # Check if the next token starts after the current word ends in the cleaned_text_input
187 # or if the next token is a special token
188 if next_token_start >= token_end or (tokenized_input['input_ids'].squeeze(0)[token_idx+1].item() in [tokenizer.cls_token_id, tokenizer.sep_token_id]):
189 # This is likely the last token of the current word
190 punctuation_pred_label = id_to_punctuation[punctuation_predictions[token_idx]]
191 if punctuation_pred_label != 'O':
192 word += punctuation_pred_label
193 else:
194 # Last token in the sequence
195 punctuation_pred_label = id_to_punctuation[punctuation_predictions[token_idx]]
196 if punctuation_pred_label != 'O':
197 word += punctuation_pred_label
198
199 reconstructed_text_parts.append(word)
200 current_word_idx += 1
201
202 return ' '.join(reconstructed_text_parts).replace(' .', '.').replace(' ,', ',').replace(' ?', '?').replace(' !', '!').replace(' ;', ';').replace(' :', ':').replace(' -', '-').replace(' "', '"').replace('( ', '(').replace(' )', ')').replace(' /', '/').replace(' \\', '\\')
203
204# --- Test Case for a single sentence ---
205single_sample_sentence = "replace me by whatever sentence you like"
206
207print(f"Original: {single_sample_sentence}")
208print(f"Predicted: {predict_punctuation_and_casing(single_sample_sentence, model, tokenizer, id_to_punctuation, id_to_casing)}\n")Replace me by whatever sentence you like.| Original Sentence | Predicted Sentence |
|---|---|
| this is a test of punctuation prediction for english how are you doing today | This is a test of punctuation prediction, for English. How are you doing today? |
| i love running this on t4 gpu and so for this goal we might make a better and more accurate model in the future | I love running this on T4 GPU and so for this goal, we might make a better and more accurate model in the future. |
| so imagine this we live in a world with complex models yet this model does punctuation and casing prediction for english and it's very small at just only 18 million parameters | So imagine this, we live in a world with complex models. Yet this model does punctuation and casing prediction for English, and it's very small at just only 18 million parameters |