File size: 2,873 Bytes
b3b4b75
 
5d5427a
b3b4b75
 
 
25d95d8
b3b4b75
 
5d5427a
 
 
 
 
b3b4b75
5d5427a
 
 
 
 
 
b3b4b75
5d5427a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3b4b75
 
5d5427a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3b4b75
 
 
 
 
 
 
 
 
5d5427a
 
 
 
b3b4b75
 
 
bd4ca57
b3b4b75
 
 
 
bd4ca57
b3b4b75
5d5427a
276b006
 
 
bd4ca57
276b006
 
f601439
276b006
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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()