File size: 8,322 Bytes
a487402
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import torch
import nltk
nltk.download('stopwords')

import os
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

import re
import string
from collections import Counter

from nltk.corpus import stopwords

import streamlit as st

import torch
from torch.utils.data import DataLoader, TensorDataset
import torch.nn as nn
import torchutils as tu

from dataclasses import dataclass
russian_stopwords = stopwords.words("russian")

from dataclasses import dataclass
import json
import time
with open('pages/models/vocab_to_int.json', 'r') as json_file:
    json_data = json_file.read()

vocab_to_int = json.loads(json_data)

def data_preprocessing(text: str) -> str:
    """preprocessing string: lowercase, removing html-tags, punctuation and stopwords

    Args:
        text (str): input string for preprocessing

    Returns:
        str: preprocessed string
    """

    text = text.lower()
    text = re.sub("<.*?>", "", text)  # html tags
    text = "".join([c for c in text if c not in string.punctuation])
    splitted_text = [word for word in text.split() if word not in russian_stopwords]
    text = " ".join(splitted_text)
    return text

def padding(review_int: list, seq_len: int) -> np.array:
    """Make left-sided padding for input list of tokens

    Args:
        review_int (list): input list of tokens
        seq_len (int): max length of sequence, it len(review_int[i]) > seq_len it will be trimmed, else it will be padded by zeros

    Returns:
        np.array: padded sequences
    """
    features = np.zeros((36591, seq_len), dtype=int)
    for i, review in enumerate(review_int):
        if len(review) <= seq_len:
            zeros = list(np.zeros(seq_len - len(review)))
            new = zeros + review
        else:
            new = review[:seq_len]
        features[i, :] = np.array(new)

    return features

def preprocess_single_string(
        input_string: str, 
        seq_len: int, 
        vocab_to_int: dict = vocab_to_int
        ) -> torch.Tensor:
    """Function for all preprocessing steps on a single string

    Args:
        input_string (str): input single string for preprocessing
        seq_len (int): max length of sequence, it len(review_int[i]) > seq_len it will be trimmed, else it will be padded by zeros
        vocab_to_int (dict, optional): word corpus {'word' : int index}. Defaults to vocab_to_int.

    Returns:
        list: preprocessed string
    """    

    preprocessed_string = data_preprocessing(input_string)
    result_list = []
    for word in preprocessed_string.split():
        try: 
            result_list.append(vocab_to_int[word])
        except KeyError as e:
            print(f'{e}: not in dictionary!')
    result_padded = padding([result_list], seq_len)[0]

    return torch.Tensor(result_padded)

class RNNNet(nn.Module):    
    '''
    vocab_size: int, размер словаря (аргумент embedding-слоя)
    emb_size:   int, размер вектора для описания каждого элемента последовательности
    hidden_dim: int, размер вектора скрытого состояния, default 0
    batch_size: int, размер batch'а

    '''
    
    def __init__(
            self, 
            vocab_size: int, 
            emb_size: int,
            hidden_dim: int, 
            seq_len: int, 
            n_layers: int = 1
                ) -> None:
        super().__init__()
        
        self.seq_len  = seq_len 
        self.emb_size = emb_size 
        self.hidden_dim = hidden_dim
        self.n_layers = n_layers
        self.vocab_size = vocab_size

        self.embedding = nn.Embedding(self.vocab_size, self.emb_size)
        self.rnn_cell  = nn.RNN(
            input_size  = self.emb_size, 
            hidden_size = self.hidden_dim, 
            batch_first = True, 
            num_layers  = n_layers
            )
        self.linear    = nn.Sequential(

            nn.Linear(self.hidden_dim * self.seq_len, 16),
            nn.Tanh(),
            nn.Linear(16, 3)
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # self.input = x.size(0)
        x = self.embedding(x.to(rnn_conf.device))
        output, _ = self.rnn_cell(x) 
        # print(f'RNN output: {output.shape}')
        output = output.contiguous().view(output.size(0), -1)
        # print(f'Flatten output: {output.shape}')
        out = self.linear(output.squeeze(0))
        return out

SEQ_LEN = 100

@dataclass
class ConfigRNN:
    vocab_size: int
    device : str
    n_layers : int
    embedding_dim : int
    hidden_size : int
    seq_len : int

rnn_conf = ConfigRNN(
    vocab_size = len(vocab_to_int)+1,
    device='cpu',
    n_layers=1,
    embedding_dim=8, 
    hidden_size=16,
    seq_len = SEQ_LEN
)

rnn_model = RNNNet(
    vocab_size=rnn_conf.vocab_size,
    emb_size=rnn_conf.embedding_dim,
    hidden_dim=rnn_conf.hidden_size,
    seq_len=rnn_conf.seq_len,
    n_layers=rnn_conf.n_layers

)

rnn_model.load_state_dict(torch.load("pages/models/weights.pt"))


result = {1: "Нейтральный", 2: "Положительный", 0: "Отрицательный" }

# rnn_model.eval()
# probability = rnn_model(preprocess_single_string('Сказать, что я разочарован — ничего не сказать. Сценаристу за адаптацию шедевральной книги Р. Д. Уоллера надо что-нибудь оторвать. Нелогичные поступки, из-за невозможности перенести на экран мысли людей (не можешь не берись, на самом деле). Важные мысли и сцены из книги убраны, добавлены новые, ни к селу. Все хорошее в сценарии — из книги. Все нелепое — от сценариста. Да и затянуть до 2,15 часа короткую книгу тоже не лучший ход.\n\nК кастингу тоже вопросы. Мэрис Стрип прекрасная актриса, но Франческа — итальянка, и Стрип пришлось прибегать к ужимкам в стиле Маргариты Тереховой, что меня просто коробило. С Иствудом отдельная тема. При прочтении именно Иствуд («Роберт был высокий, худой и сильный, а двигался, как трава под ветром, плавно, без усилий. Серебристо-седые волосы прикрывали уши и шею, и, надо сказать, выглядел он всегда слегка растрепанным, как будто только что сошел на землю после путешествия по бурному морю и пытался ладонью привести волосы в порядок. Узкое лицо, высокие скулы и лоб, наполовину прикрытый волосами, на фоне которых голубые глаза смотрелись особенно ярко.») выглядел идеальным актером на роль Кинкейда. Но вот беда — Иствуд постарел. Играть в 65 пятидесятилетнего мужчину нелегко. Лет десять назад было бы намного лучше.\n\nНу и режиссура. Слабо, к сожалению. Очень поверхностно. Получилась простенькая мелодрама. А жаль, книга более чем достойная.', seq_len=SEQ_LEN).unsqueeze(0).long().to(rnn_conf.device)).sigmoid()
# print(f'{result[torch.argmax(probability).item()]} Вероятность: {probability.max():.3f}')


def pred(text):
    start_time = time.time()  
    probability = rnn_model(preprocess_single_string(text, seq_len=SEQ_LEN).unsqueeze(0).long().to(rnn_conf.device)).sigmoid()
    end_time = time.time()

    inference_time = end_time - start_time
    return f' RNN {result[torch.argmax(probability).item()]} {inference_time} секунд'