Spaces:
Build error
Build error
| import gradio as gr | |
| from collections import Counter | |
| import nltk | |
| import torch | |
| import numpy as np | |
| import pandas as pd | |
| import re | |
| import string | |
| import json | |
| from typing import Tuple | |
| from dataclasses import dataclass | |
| from nltk.corpus import stopwords | |
| from pymystem3 import Mystem | |
| from torch import nn | |
| # Загрузка необходимых ресурсов NLTK | |
| nltk.download('stopwords', quiet=True) | |
| # Регулярное выражение для поиска стандартных эмодзи и текстовых смайлов | |
| emoji_pattern = re.compile( | |
| u"([" | |
| u"\U0001F600-\U0001F64F" # смайлики | |
| u"\U0001F300-\U0001F5FF" # символы и пиктограммы | |
| u"\U0001F680-\U0001F6FF" # транспортные и маппинг символы | |
| u"\U0001F1E0-\U0001F1FF" # флаги | |
| u"]+)|" | |
| r"(:\)|:\(|;\)|:\-\)|:\-\(|\(:|\):|:D|:P|:\]|O_O|XD|\^\^|<3|:\*|\(\(|\)\))" # текстовые смайлы | |
| ) | |
| # Функция для извлечения смайлов | |
| def extract_emojis(text): | |
| matches = emoji_pattern.findall(text) | |
| return ' '.join([match[0] if match[0] else match[1] for match in matches]) | |
| # Предобработка текста | |
| mystem = Mystem() | |
| stop_words = set(stopwords.words('russian')) - {'не', 'ни'} | |
| # Загрузка списка матерных слов | |
| with open('list.txt', 'r', encoding='utf-8') as f: | |
| swear_words = set(f.read().splitlines()) | |
| # Загрузка данных | |
| data = pd.read_csv('data.csv') | |
| # Функция для извлечения смайлов | |
| data['emojis'] = data['text'].apply(extract_emojis) | |
| print(data[['text', 'emojis']].head(10)) | |
| # Подсчет наиболее часто используемых смайлов | |
| positive_emojis = ' '.join(data[data['sentiment'] == 1]['emojis']) | |
| negative_emojis = ' '.join(data[data['sentiment'] == 0]['emojis']) | |
| positive_emojis_count = Counter(positive_emojis.split()) | |
| negative_emojis_count = Counter(negative_emojis.split()) | |
| print("Most common emojis in positive tweets:", positive_emojis_count.most_common(10)) | |
| print("Most common emojis in negative tweets:", negative_emojis_count.most_common(10)) | |
| # Паттерн для поиска имен пользователей (если нужен) | |
| username_pattern = re.compile(r"@\w+") | |
| def data_preprocessing(text: str, positive_emoji_placeholder: str = 'отлично', negative_emoji_placeholder: str = 'плохо') -> Tuple[str, int]: | |
| """Preprocessing string: lowercase, removing html-tags, punctuation, stopwords, and replacing emojis. | |
| Also calculates the count of swear words. | |
| """ | |
| def replace_emoji(match): | |
| emoji = match.group() | |
| if emoji in positive_emojis: | |
| return f" {positive_emoji_placeholder} " | |
| elif emoji in negative_emojis: | |
| return f" {negative_emoji_placeholder} " | |
| return emoji | |
| text = emoji_pattern.sub(replace_emoji, text.lower()) | |
| text = re.sub(r"<.*?>", "", text) | |
| text = "".join([c if c not in string.punctuation else ' ' for c in text]) | |
| text = re.sub(r'\b[a-zA-Z]+\b', '', text) | |
| text = username_pattern.sub('', text) | |
| text = re.sub(r'\s+', ' ', text).strip() | |
| text = "".join(mystem.lemmatize(text)).strip() | |
| text = re.sub(r'\b\w*\d\w*\b', '', text) | |
| text = " ".join(word for word in text.split() if word not in stop_words) | |
| swear_count = sum(1 for word in text.split() if word in swear_words) | |
| return text, swear_count | |
| # Преобразование текста в числовое представление | |
| def preprocess_for_model(text: str, vocab_to_int: dict, seq_len: int) -> np.array: | |
| """Convert cleaned text to padded numerical representation.""" | |
| words = text.split() | |
| indices = [vocab_to_int.get(word, 0) for word in words] | |
| if len(indices) < seq_len: | |
| indices = [0] * (seq_len - len(indices)) + indices | |
| else: | |
| indices = indices[:seq_len] | |
| return np.array(indices) | |
| # Инициализация конфигурации и модели | |
| class ConfigRNN: | |
| vocab_size: int | |
| device: str | |
| n_layers: int | |
| embedding_dim: int | |
| hidden_size: int | |
| seq_len: int | |
| bidirectional: bool | |
| # Сначала создаем конфигурацию | |
| net_config = ConfigRNN( | |
| vocab_size=10000, # Placeholder, так как vocab_to_int еще не загружен | |
| device='cpu', | |
| n_layers=1, | |
| embedding_dim=8, | |
| hidden_size=16, | |
| seq_len=110, | |
| bidirectional=False | |
| ) | |
| # Загрузка модели и словаря | |
| class RNNNet(nn.Module): | |
| def __init__(self, rnn_conf): | |
| super().__init__() | |
| self.rnn_conf = rnn_conf | |
| self.embedding = nn.Embedding(self.rnn_conf.vocab_size, self.rnn_conf.embedding_dim) | |
| self.rnn_cell = nn.RNN( | |
| input_size=self.rnn_conf.embedding_dim, | |
| hidden_size=self.rnn_conf.hidden_size, | |
| batch_first=True, | |
| bidirectional=self.rnn_conf.bidirectional, | |
| num_layers=self.rnn_conf.n_layers | |
| ) | |
| self.bidirect_factor = 2 if self.rnn_conf.bidirectional else 1 | |
| self.linear = nn.Sequential( | |
| nn.Linear(self.rnn_conf.hidden_size * self.bidirect_factor, 16), | |
| nn.Tanh(), | |
| nn.Linear(16, 1) | |
| ) | |
| def forward(self, x): | |
| x = self.embedding(x.to(self.rnn_conf.device)) | |
| output, hidden = self.rnn_cell(x) | |
| hidden = torch.cat((hidden[-2,:,:], hidden[-1,:,:]), dim=1) if self.rnn_conf.bidirectional else hidden[-1,:,:] | |
| out = self.linear(hidden) | |
| return out | |
| def load_vocab_and_model(): | |
| # Загрузка словаря | |
| try: | |
| with open('vocab_to_int.json', 'r', encoding='utf-8') as f: | |
| vocab_to_int = json.load(f) | |
| except FileNotFoundError: | |
| raise RuntimeError("Файл vocab_to_int.json не найден.") | |
| # Обновление конфигурации с реальным размером словаря | |
| net_config.vocab_size = len(vocab_to_int) + 1 | |
| # Загрузка модели | |
| model = RNNNet(net_config) | |
| try: | |
| model.load_state_dict(torch.load("model_rnn.pth", map_location=torch.device('cpu'))) | |
| except FileNotFoundError: | |
| raise RuntimeError("Файл модели model_rnn.pth не найден.") | |
| model.eval() | |
| return model, vocab_to_int | |
| # Теперь загружаем модель и словарь | |
| model, vocab_to_int = load_vocab_and_model() | |
| def predict(text: str): | |
| cleaned_text, _ = data_preprocessing(text) | |
| numerical_representation = preprocess_for_model(cleaned_text, vocab_to_int, net_config.seq_len) | |
| input_tensor = torch.from_numpy(numerical_representation).unsqueeze(0) # Add batch dimension | |
| with torch.no_grad(): | |
| output = model(input_tensor) | |
| prediction = torch.sigmoid(output).item() | |
| return "Positive 👍" if prediction > 0.45 else "Negative 👎" | |
| # Создание интерфейса с кнопкой Submit и другими элементами | |
| interface = gr.Interface( | |
| fn=predict, | |
| inputs=gr.Textbox(lines=2, placeholder="Введите текст здесь..."), | |
| outputs=gr.Textbox(label="Предсказание"), | |
| title="Текстовый Анализатор Настроений", | |
| description="Введите текст, чтобы узнать, является ли он положительным или отрицательным. Попробуйте добавить эмодзи для более интересного анализа!", | |
| theme="compact", | |
| examples=[ | |
| ["Сегодня был отличный день! 🌟 Прекрасное настроение и много позитивных эмоций! 😊 Спасибо всем за поддержку и вдохновение!"], | |
| ["Не могу поверить, как всё пошло не так сегодня. все очень плохо((#тяжелыйдень #"] | |
| ] | |
| ) | |
| # Запуск приложения | |
| interface.launch(share=True) | |