Spaces:
Sleeping
Sleeping
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import gradio as gr | |
| from huggingface_hub import hf_hub_download | |
| REPO_ID = "SentilyticsOPJ/lstm_model" | |
| FILENAME = "lstm_model.pt" | |
| MAX_LEN = 80 | |
| EMBEDDING_DIM = 300 | |
| LSTM_UNITS = 96 | |
| SPATIAL_DROPOUT = 0.3 | |
| DROPOUT = 0.5 | |
| class BiLSTM(nn.Module): | |
| def __init__(self, vocab_size, num_classes, embedding_matrix=None, | |
| embedding_dim=EMBEDDING_DIM, lstm_units=LSTM_UNITS, | |
| spatial_dropout=SPATIAL_DROPOUT, dropout=DROPOUT, | |
| padding_idx=0): | |
| super().__init__() | |
| self.padding_idx = padding_idx | |
| self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=padding_idx) | |
| if embedding_matrix is not None: | |
| self.embedding.weight.data.copy_(torch.from_numpy(embedding_matrix)) | |
| self.spatial_dropout = nn.Dropout1d(spatial_dropout) | |
| self.lstm = nn.LSTM( | |
| input_size=embedding_dim, | |
| hidden_size=lstm_units, | |
| num_layers=1, | |
| batch_first=True, | |
| bidirectional=True, | |
| ) | |
| self.attn = nn.Linear(lstm_units * 2, 1) | |
| self.dropout = nn.Dropout(dropout) | |
| self.fc = nn.Linear(lstm_units * 2, num_classes) | |
| def forward(self, x): | |
| emb = self.embedding(x) | |
| emb = emb.transpose(1, 2) | |
| emb = self.spatial_dropout(emb) | |
| emb = emb.transpose(1, 2) | |
| out, _ = self.lstm(emb) | |
| scores = self.attn(out).squeeze(-1) | |
| mask = (x == self.padding_idx) | |
| mask[:, 0] = False | |
| scores = scores.masked_fill(mask, float("-inf")) | |
| weights = F.softmax(scores, dim=1).unsqueeze(-1) | |
| context = (out * weights).sum(dim=1) | |
| return self.fc(self.dropout(context)) | |
| path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME) | |
| obj = torch.load(path, map_location="cpu", weights_only=False) | |
| sd = obj["state_dict"] | |
| word2id = obj["word2id"] | |
| labels = obj["label_classes"] | |
| max_len = obj["max_len"] | |
| model = BiLSTM(len(word2id), len(labels)) | |
| model.load_state_dict(sd) | |
| model.eval() | |
| def predict(text): | |
| if not text.strip(): | |
| return "" | |
| ids = [word2id.get(t, 1) for t in text.lower().split()][:max_len] | |
| ids += [0] * (max_len - len(ids)) | |
| with torch.no_grad(): | |
| probs = torch.softmax(model(torch.tensor(ids).unsqueeze(0)), dim=1)[0] | |
| return labels[int(probs.argmax())] | |
| demo = gr.Interface( | |
| fn=predict, | |
| inputs=gr.Textbox(lines=3, placeholder="Unesi tekst...", label="Text"), | |
| outputs=gr.Label(num_top_classes=1, label="Sentiment"), | |
| title="Sentiment Analysis (BiLSTM, fastText hr)", | |
| description="Five-class sentiment: mixed, negative, neutral, positive, sarcastic.", | |
| examples=["Volim kavu", "Ovaj doktor je loš", "Dan je bio ok"], | |
| ) | |
| demo.launch() |