Add preprocess

This commit is contained in:
itqop 2024-10-23 01:07:56 +03:00
parent a0dcf50a65
commit be286b87f8
5 changed files with 190 additions and 10 deletions

View File

@ -1,8 +1,7 @@
# app/api/routes.py
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from app.models.schemas import TextInput, ToxicityOutput from app.models.schemas import TextInput, ToxicityOutput
from app.core.cache import cache from app.core.cache import cache
from app.core.text_preprocessing import preprocess_text
import json import json
import hashlib import hashlib
import logging import logging
@ -23,7 +22,14 @@ async def assess_toxicity(input: TextInput):
- **text**: Текст для оценки - **text**: Текст для оценки
""" """
cache_key = get_cache_key(input.text) try:
preprocessed_text = preprocess_text(input.text)
logger.info(f"Текст после предобработки: {preprocessed_text}")
except Exception as e:
logger.error(f"Ошибка при предобработке текста: {e}")
raise HTTPException(status_code=400, detail="Ошибка при предобработке текста.")
cache_key = get_cache_key(preprocessed_text)
# Попытка получить результат из кеша # Попытка получить результат из кеша
cached_result = await cache.get(cache_key) cached_result = await cache.get(cache_key)
@ -37,8 +43,8 @@ async def assess_toxicity(input: TextInput):
try: try:
# Отправляем задачу в очередь Celery # Отправляем задачу в очередь Celery
result = assess_toxicity_task.delay(input.text) result = assess_toxicity_task.delay(preprocessed_text)
logger.info(f"Задача отправлена в очередь Celery для текста: {input.text}") logger.info(f"Задача отправлена в очередь Celery для текста: {preprocessed_text}")
toxicity_score = result.get(timeout=10) # Ждем результат до 10 секунд toxicity_score = result.get(timeout=10) # Ждем результат до 10 секунд
# Сохраняем результат в кеш # Сохраняем результат в кеш
@ -48,4 +54,4 @@ async def assess_toxicity(input: TextInput):
return {"toxicity_score": toxicity_score} return {"toxicity_score": toxicity_score}
except Exception as e: except Exception as e:
logger.error(f"Ошибка при обработке текста: {e}") logger.error(f"Ошибка при обработке текста: {e}")
raise HTTPException(status_code=500, detail=str(e)) raise HTTPException(status_code=500, detail="Ошибка при оценке токсичности текста.")

View File

@ -1,5 +1,3 @@
# app/core/cache.py
import asyncio import asyncio
import json import json
import hashlib import hashlib

View File

@ -1,11 +1,10 @@
# app/core/config.py
from pydantic import Field, validator from pydantic import Field, validator
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from typing import Any from typing import Any
from transformers import AutoTokenizer from transformers import AutoTokenizer
import os import os
class Settings(BaseSettings): class Settings(BaseSettings):
MODEL_CHECKPOINT: str = Field( MODEL_CHECKPOINT: str = Field(
'cointegrated/rubert-tiny-toxicity', 'cointegrated/rubert-tiny-toxicity',

View File

@ -0,0 +1,81 @@
import re
import logging
from typing import List
logger = logging.getLogger(__name__)
ENGLISH_TO_RUSSIAN_LETTERS = {
'a': 'а',
'c': 'с',
'e': 'е',
'o': 'о',
'p': 'р',
'x': 'х',
'y': 'у',
'B': 'В',
'M': 'М',
'T': 'Т',
}
LETTER_DIGIT_PATTERN = re.compile(r'([а-яё]+)(\d+)', re.IGNORECASE)
RUSSIAN_LETTER_PATTERN = re.compile(r'[а-яё]', re.IGNORECASE)
def is_majority_russian(word: str) -> bool:
"""Проверяет, составляет ли большинство букв в слове русские буквы."""
letters = re.findall(r'[а-яёА-ЯЁ]', word)
if not letters:
return False
russian_count = len(letters)
total_letters = len(word)
return russian_count > (total_letters / 2)
def replace_similar_english_letters(word: str) -> str:
"""Заменяет похожие английские буквы на русские эквиваленты."""
return ''.join([ENGLISH_TO_RUSSIAN_LETTERS.get(char, char) for char in word])
def replace_digits(word: str) -> str:
"""Заменяет цифры '3' и '0' на буквы 'з' и 'о' соответственно."""
return word.replace('3', 'з').replace('0', 'о')
def split_letters_digits(word: str) -> str:
"""Разделяет буквы и цифры в слове."""
return LETTER_DIGIT_PATTERN.sub(r'\1 \2', word)
def preprocess_text(text: str) -> str:
"""
Предобрабатывает текст:
1. Приводит к нижнему регистру.
2. Заменяет похожие английские буквы на русские.
3. В словах с большинством русских букв заменяет '3' на 'з' и '0' на 'о'.
4. Разделяет буквы и цифры.
"""
logger.debug("Начало предобработки текста.")
text = text.lower()
logger.debug(f"Текст после приведения к нижнему регистру: {text}")
words = text.split()
processed_words: List[str] = []
for word in words:
original_word = word
if is_majority_russian(word):
word = replace_similar_english_letters(word)
logger.debug(f"Слово после замены английских букв: {word}")
word = replace_digits(word)
logger.debug(f"Слово после замены цифр: {word}")
word = split_letters_digits(word)
logger.debug(f"Слово после разделения букв и цифр: {word}")
processed_words.append(word)
logger.debug(f"Слово '{original_word}' преобразовано в '{word}'.")
preprocessed_text = ' '.join(processed_words)
logger.debug(f"Текст после предобработки: {preprocessed_text}")
return preprocessed_text

View File

@ -0,0 +1,96 @@
from app.core.text_preprocessing import preprocess_text
def test_preprocess_lowercase():
"""Тестирование преобразования текста в нижний регистр."""
input_text = "ПрИвет Мир"
expected = "привет мир"
assert preprocess_text(input_text) == expected
def test_preprocess_replace_english_letters():
"""Тестирование замены похожих английских букв на русские."""
input_text = "Hello, how are you?"
expected = "hello, how are you?" # В данном случае, поскольку 'H', 'e', 'l', 'o' заменяются на русские?
# Но согласно ENGLISH_TO_RUSSIAN_LETTERS, 'a' -> 'а', 'c' -> 'с', 'e' -> 'е', 'o' -> 'о', 'p' -> 'р', 'x' -> 'х', 'y' -> 'у', 'B' -> 'В', 'M' -> 'М', 'T' -> 'Т'
# Так как ENGLISH_TO_RUSSIAN_LETTERS не охватывает все, нужно скорректировать
# Лучше использовать пример с заменяемыми буквами
input_text = "Привет a, c, e, o, p, x, y"
expected = "привет а, с, е, о, р, х, у"
assert preprocess_text(input_text) == expected
def test_preprocess_replace_digits_in_russian_word():
"""Тестирование замены цифр '3' и '0' на 'з' и 'о' в словах с преобладанием русских букв."""
input_text = "Привет3 и 0дела"
expected = "привет з и одела"
assert preprocess_text(input_text) == expected
def test_preprocess_split_letters_digits():
"""Тестирование разделения букв и цифр в словах."""
input_text = "привет12 как дела"
expected = "привет 12 как дела"
assert preprocess_text(input_text) == expected
def test_preprocess_mixed_russian_english_letters():
"""Тестирование обработки слов с смешанными русскими и английскими буквами."""
input_text = "а3b0 привет12"
# 'a3b0' -> 'азбо' (замена 'a'->'а', '3'->'з', '0'->'о') и затем разделение букв и цифр
# Но после замены, 'a3b0' -> 'азбо', в котором нет цифр, поэтому разделения не произойдет
# Возможно, другой пример: "a3b0c" -> "азбoc" -> возможно "азбоc", но это не ясно
# Давайте использовать другой пример, где после замены остаются цифры
input_text = "a3b0c123"
# 'a3b0c123' -> 'азбоc123' (замена 'a'->'а', '3'->'з', '0'->'о'), затем разделение
# Но 'c' -> 'с', по словарю 'c' -> 'с', 'a' -> 'а'
# 'a3b0c123' -> 'азбос123' -> 'азбос 123'
expected = "азбос 123"
assert preprocess_text(input_text) == expected
def test_preprocess_no_digits():
"""Тестирование обработки текста без цифр."""
input_text = "как дела сегодня"
expected = "как дела сегодня"
assert preprocess_text(input_text) == expected
def test_preprocess_digits_not_3_or_0():
"""Тестирование замены цифр, отличных от '3' и '0'."""
input_text = "привет12 и 45дела"
expected = "привет 12 и 45дела" # Цифры '1', '2', '4', '5' не заменяются
assert preprocess_text(input_text) == expected
def test_preprocess_only_digits():
"""Тестирование обработки текста, состоящего только из цифр."""
input_text = "12345"
expected = "12345" # Нет букв, ничего не заменяется
assert preprocess_text(input_text) == expected
def test_preprocess_empty_string():
"""Тестирование обработки пустой строки."""
input_text = ""
expected = ""
assert preprocess_text(input_text) == expected
def test_preprocess_special_characters():
"""Тестирование обработки текста с особыми символами."""
input_text = "Привет! Как дела? a3b0c!"
expected = "привет! как дела? азбоc!"
# 'a3b0c' -> 'азбоc', затем разделение, но 'c' -> 'с', так что 'азбос!'
# Исправленный ожидаемый результат: "привет! как дела? азбос!"
expected = "привет! как дела? азбос!"
assert preprocess_text(input_text) == expected
def test_preprocess_multiple_spaces():
"""Тестирование обработки текста с несколькими пробелами."""
input_text = "привет 12 как дела"
expected = "привет 12 как дела"
assert preprocess_text(input_text) == expected
def test_preprocess_mixed_case_and_letters():
"""Тестирование обработки текста с разными регистрами и смешанными буквами."""
input_text = "ПрИвет a3B0C и 0DEFG"
# Приведение к нижнему: "привет a3b0c и 0defg"
# Замена похожих букв: 'a'->'а', 'b'->'б', 'c'->'с', 'y'->'у' и т.д.
# 'a3b0c' -> 'азбос'
# '0defg' -> 'одефg' (замена '0'->'о', 'd','e','f','g' остаются
# Но 'd','e','f','g' не заменены, так как они не в словаре
# Затем разделение: 'привет азбос и odefg'
expected = "привет азбос и odefg"
assert preprocess_text(input_text) == expected