Spaces:
Sleeping
Sleeping
MinAA commited on
Commit ·
3228848
1
Parent(s): 05f7dda
cleanup
Browse files
app.py
CHANGED
|
@@ -302,11 +302,28 @@ def audio_zero_shot_classifier(audio, candidate_labels, model_name):
|
|
| 302 |
labels = [label.strip() for label in candidate_labels.split(",")]
|
| 303 |
result = classifier(audio, candidate_labels=labels)
|
| 304 |
output = "Результаты классификации:\n"
|
| 305 |
-
|
| 306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
return output
|
| 308 |
except Exception as e:
|
| 309 |
-
|
|
|
|
|
|
|
|
|
|
| 310 |
|
| 311 |
@measure_time_and_save("Распознавание речи")
|
| 312 |
def speech_recognition(audio, model_name):
|
|
@@ -331,7 +348,57 @@ def speech_synthesis(text, model_name):
|
|
| 331 |
if not text or not text.strip():
|
| 332 |
raise ValueError("Текст для синтеза не может быть пустым")
|
| 333 |
|
| 334 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
tts = get_pipeline("text-to-speech", model_name)
|
| 336 |
result = tts(text)
|
| 337 |
|
|
@@ -389,9 +456,18 @@ def speech_synthesis(text, model_name):
|
|
| 389 |
|
| 390 |
return (sample_rate, audio_data)
|
| 391 |
else:
|
| 392 |
-
raise ValueError(f"Неожиданный формат результата от pipeline: {type(result)}")
|
| 393 |
except Exception as e:
|
| 394 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
|
| 396 |
# ==================== ЗАДАЧИ С ИЗОБРАЖЕНИЯМИ ====================
|
| 397 |
|
|
@@ -696,15 +772,85 @@ def visual_qa(image, question, model_name):
|
|
| 696 |
def image_zero_shot_classification(image, candidate_labels, model_name):
|
| 697 |
"""Zero-shot классификация изображений"""
|
| 698 |
try:
|
| 699 |
-
classifier = get_pipeline("zero-shot-image-classification", model_name)
|
| 700 |
labels = [label.strip() for label in candidate_labels.split(",")]
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 706 |
except Exception as e:
|
| 707 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 708 |
|
| 709 |
# ==================== ФУНКЦИИ ДЛЯ ИСТОРИИ ====================
|
| 710 |
|
|
|
|
| 302 |
labels = [label.strip() for label in candidate_labels.split(",")]
|
| 303 |
result = classifier(audio, candidate_labels=labels)
|
| 304 |
output = "Результаты классификации:\n"
|
| 305 |
+
|
| 306 |
+
# Обрабатываем разные форматы результатов
|
| 307 |
+
if isinstance(result, dict) and 'labels' in result and 'scores' in result:
|
| 308 |
+
# Формат: {'labels': [...], 'scores': [...]}
|
| 309 |
+
for label, score in zip(result['labels'], result['scores']):
|
| 310 |
+
output += f"{label}: {score:.4f}\n"
|
| 311 |
+
elif isinstance(result, list):
|
| 312 |
+
# Формат: [{'label': '...', 'score': ...}, ...]
|
| 313 |
+
for item in result:
|
| 314 |
+
if isinstance(item, dict):
|
| 315 |
+
label = item.get('label', '')
|
| 316 |
+
score = item.get('score', 0.0)
|
| 317 |
+
output += f"{label}: {score:.4f}\n"
|
| 318 |
+
else:
|
| 319 |
+
return f"Ошибка: Неожиданный формат результата от pipeline: {type(result)}. Ожидался словарь с ключами 'labels' и 'scores' или список словарей."
|
| 320 |
+
|
| 321 |
return output
|
| 322 |
except Exception as e:
|
| 323 |
+
error_msg = str(e)
|
| 324 |
+
if "Could not load model" in error_msg or "Unrecognized" in error_msg:
|
| 325 |
+
return f"Ошибка: Модель '{model_name}' не поддерживается для zero-shot классификации аудио. Попробуйте другую модель, например 'laion/clap-htsat-unfused'."
|
| 326 |
+
return f"Ошибка: {error_msg}"
|
| 327 |
|
| 328 |
@measure_time_and_save("Распознавание речи")
|
| 329 |
def speech_recognition(audio, model_name):
|
|
|
|
| 348 |
if not text or not text.strip():
|
| 349 |
raise ValueError("Текст для синтеза не может быть пустым")
|
| 350 |
|
| 351 |
+
# Проверяем, является ли модель SpeechT5
|
| 352 |
+
if "speecht5" in model_name.lower():
|
| 353 |
+
try:
|
| 354 |
+
# Для SpeechT5 нужны speaker_embeddings
|
| 355 |
+
from transformers import SpeechT5Processor, SpeechT5ForTextToSpeech, SpeechT5HifiGan
|
| 356 |
+
|
| 357 |
+
cache_key = f"tts_speecht5_{model_name}"
|
| 358 |
+
cached = model_cache.get(cache_key)
|
| 359 |
+
if cached is None:
|
| 360 |
+
processor = SpeechT5Processor.from_pretrained(model_name)
|
| 361 |
+
model = SpeechT5ForTextToSpeech.from_pretrained(model_name)
|
| 362 |
+
vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")
|
| 363 |
+
|
| 364 |
+
# Генерируем speaker embeddings используя модель напрямую
|
| 365 |
+
# Используем размерность speaker embeddings из конфигурации модели
|
| 366 |
+
speaker_embedding_dim = model.config.speaker_embedding_dim
|
| 367 |
+
# Создаем случайный speaker embedding (можно заменить на предобученный)
|
| 368 |
+
# Для более стабильного результата используем нормализованный случайный вектор
|
| 369 |
+
speaker_embeddings = torch.randn(1, speaker_embedding_dim)
|
| 370 |
+
speaker_embeddings = speaker_embeddings / torch.norm(speaker_embeddings, dim=1, keepdim=True)
|
| 371 |
+
|
| 372 |
+
cached = (processor, model, vocoder, speaker_embeddings)
|
| 373 |
+
model_cache.put(cache_key, cached)
|
| 374 |
+
|
| 375 |
+
processor, model, vocoder, speaker_embeddings = cached
|
| 376 |
+
inputs = processor(text=text, return_tensors="pt")
|
| 377 |
+
with torch.no_grad():
|
| 378 |
+
speech = model.generate_speech(inputs["input_ids"], speaker_embeddings, vocoder=vocoder)
|
| 379 |
+
|
| 380 |
+
# Конвертируем в numpy и нормализуем
|
| 381 |
+
audio_data = speech.numpy()
|
| 382 |
+
# Убеждаемся, что это 1D массив
|
| 383 |
+
if len(audio_data.shape) > 1:
|
| 384 |
+
audio_data = audio_data.flatten()
|
| 385 |
+
# Нормализуем в диапазон [-1, 1] если нужно
|
| 386 |
+
if audio_data.dtype != np.float32:
|
| 387 |
+
audio_data = audio_data.astype(np.float32)
|
| 388 |
+
# Нормализуем если значения выходят за пределы [-1, 1]
|
| 389 |
+
max_val = np.abs(audio_data).max()
|
| 390 |
+
if max_val > 1.0:
|
| 391 |
+
audio_data = audio_data / max_val
|
| 392 |
+
|
| 393 |
+
sample_rate = 16000
|
| 394 |
+
return (sample_rate, audio_data)
|
| 395 |
+
except Exception as e:
|
| 396 |
+
error_msg = str(e)
|
| 397 |
+
if "ImportError" in str(type(e)) or "ModuleNotFoundError" in str(type(e)):
|
| 398 |
+
raise Exception(f"Ошибка: Не удалось импортировать необходимые модули для SpeechT5. Убедитесь, что transformers установлен: {error_msg}")
|
| 399 |
+
raise Exception(f"Ошибка синтеза речи с SpeechT5: {error_msg}")
|
| 400 |
+
|
| 401 |
+
# Используем стандартный pipeline для других моделей
|
| 402 |
tts = get_pipeline("text-to-speech", model_name)
|
| 403 |
result = tts(text)
|
| 404 |
|
|
|
|
| 456 |
|
| 457 |
return (sample_rate, audio_data)
|
| 458 |
else:
|
| 459 |
+
raise ValueError(f"Неожиданный формат результата от pipeline: {type(result)}. Ожидался словарь с ключами 'audio' и 'sampling_rate' или кортеж (sample_rate, audio_data).")
|
| 460 |
except Exception as e:
|
| 461 |
+
error_msg = str(e)
|
| 462 |
+
if "speaker_embeddings" in error_msg.lower():
|
| 463 |
+
if "speecht5" in model_name.lower():
|
| 464 |
+
return f"Ошибка: Модель SpeechT5 требует speaker_embeddings. Они должны генерироваться автоматически, но произошла ошибка: {error_msg}"
|
| 465 |
+
return f"Ошибка: Модель '{model_name}' требует speaker_embeddings. Для SpeechT5 они генерируются автоматически, но для других моделей может потребоваться дополнительная настройка."
|
| 466 |
+
if "does not appear to have a file named" in error_msg or "Unrecognized model" in error_msg:
|
| 467 |
+
return f"Ошибка: Модель '{model_name}' не поддерживается библиотекой transformers для синтеза речи. Попробуйте использовать модель 'microsoft/speecht5_tts'."
|
| 468 |
+
if "negative output size" in error_msg.lower() or "input size 0" in error_msg.lower():
|
| 469 |
+
return f"Ошибка: Проблема с обработкой текста моделью '{model_name}'. Возможные причины: неподдерживаемый язык, пустой текст после обработки, или проблема с токенизацией. Попробуйте использовать другой текст или модель."
|
| 470 |
+
raise Exception(f"Ошибка синтеза речи: {error_msg}")
|
| 471 |
|
| 472 |
# ==================== ЗАДАЧИ С ИЗОБРАЖЕНИЯМИ ====================
|
| 473 |
|
|
|
|
| 772 |
def image_zero_shot_classification(image, candidate_labels, model_name):
|
| 773 |
"""Zero-shot классификация изображений"""
|
| 774 |
try:
|
|
|
|
| 775 |
labels = [label.strip() for label in candidate_labels.split(",")]
|
| 776 |
+
|
| 777 |
+
# Проверяем, является ли модель LAION
|
| 778 |
+
if "laion/" in model_name.lower() or "laion5b" in model_name.lower():
|
| 779 |
+
# Используем OpenCLIP для LAION моделей
|
| 780 |
+
import open_clip
|
| 781 |
+
|
| 782 |
+
cache_key = f"clip_laion_{model_name}"
|
| 783 |
+
cached = model_cache.get(cache_key)
|
| 784 |
+
if cached is None:
|
| 785 |
+
# Определяем имя модели и веса для OpenCLIP
|
| 786 |
+
if "xlm-roberta-base-ViT-B-32" in model_name or "xlm-roberta-base" in model_name:
|
| 787 |
+
clip_model_name = "xlm-roberta-base-ViT-B-32"
|
| 788 |
+
pretrained = "laion5b_s13b_b90k"
|
| 789 |
+
else:
|
| 790 |
+
# Пытаемся извлечь информацию из имени модели
|
| 791 |
+
clip_model_name = "xlm-roberta-base-ViT-B-32"
|
| 792 |
+
pretrained = "laion5b_s13b_b90k"
|
| 793 |
+
|
| 794 |
+
model, _, preprocess = open_clip.create_model_and_transforms(
|
| 795 |
+
clip_model_name,
|
| 796 |
+
pretrained=pretrained
|
| 797 |
+
)
|
| 798 |
+
tokenizer = open_clip.get_tokenizer(clip_model_name)
|
| 799 |
+
model.eval()
|
| 800 |
+
cached = (model, preprocess, tokenizer)
|
| 801 |
+
model_cache.put(cache_key, cached)
|
| 802 |
+
|
| 803 |
+
model, preprocess, tokenizer = cached
|
| 804 |
+
|
| 805 |
+
# Обрабатываем изображение и тексты
|
| 806 |
+
image_tensor = preprocess(image).unsqueeze(0)
|
| 807 |
+
text_tokens = tokenizer(labels)
|
| 808 |
+
|
| 809 |
+
with torch.no_grad():
|
| 810 |
+
image_features = model.encode_image(image_tensor)
|
| 811 |
+
text_features = model.encode_text(text_tokens)
|
| 812 |
+
# Нормализуем признаки
|
| 813 |
+
image_features = image_features / image_features.norm(dim=-1, keepdim=True)
|
| 814 |
+
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
|
| 815 |
+
# Вычисляем косинусное сходство (логиты)
|
| 816 |
+
logits_per_image = (image_features @ text_features.T) * 100 # Масштабируем для лучшей точности
|
| 817 |
+
probs = logits_per_image.softmax(dim=1)
|
| 818 |
+
|
| 819 |
+
output = "Результаты классификации:\n"
|
| 820 |
+
for label, prob in zip(labels, probs[0]):
|
| 821 |
+
output += f"{label}: {prob.item():.4f}\n"
|
| 822 |
+
return output
|
| 823 |
+
else:
|
| 824 |
+
# Используем стандартный pipeline
|
| 825 |
+
classifier = get_pipeline("zero-shot-image-classification", model_name)
|
| 826 |
+
result = classifier(image, candidate_labels=labels)
|
| 827 |
+
output = "Результаты классификации:\n"
|
| 828 |
+
|
| 829 |
+
# Обрабатываем разные форматы результатов
|
| 830 |
+
if isinstance(result, dict) and 'labels' in result and 'scores' in result:
|
| 831 |
+
# Формат: {'labels': [...], 'scores': [...]}
|
| 832 |
+
for label, score in zip(result['labels'], result['scores']):
|
| 833 |
+
output += f"{label}: {score:.4f}\n"
|
| 834 |
+
elif isinstance(result, list):
|
| 835 |
+
# Формат: [{'label': '...', 'score': ...}, ...]
|
| 836 |
+
for item in result:
|
| 837 |
+
if isinstance(item, dict):
|
| 838 |
+
label = item.get('label', '')
|
| 839 |
+
score = item.get('score', 0.0)
|
| 840 |
+
output += f"{label}: {score:.4f}\n"
|
| 841 |
+
else:
|
| 842 |
+
return f"Ошибка: Неожиданный формат результата от pipeline: {type(result)}. Ожидался словарь с ключами 'labels' и 'scores' или список словарей."
|
| 843 |
+
|
| 844 |
+
return output
|
| 845 |
except Exception as e:
|
| 846 |
+
error_msg = str(e)
|
| 847 |
+
if "Could not load model" in error_msg or "Unrecognized" in error_msg:
|
| 848 |
+
if "laion" in model_name.lower():
|
| 849 |
+
return f"Ошибка: Модель '{model_name}' требует библиотеку open-clip-torch. Убедитесь, что она установлена: pip install open-clip-torch"
|
| 850 |
+
return f"Ошибка: Модель '{model_name}' не поддерживается для zero-shot классификации изображений. Попробуйте другую модель, например 'openai/clip-vit-base-patch32'."
|
| 851 |
+
if "open_clip" in error_msg or "open-clip" in error_msg:
|
| 852 |
+
return f"Ошибка: Для работы с LAION моделями требуется библиотека open-clip-torch. Установите её: pip install open-clip-torch"
|
| 853 |
+
return f"Ошибка: {error_msg}"
|
| 854 |
|
| 855 |
# ==================== ФУНКЦИИ ДЛЯ ИСТОРИИ ====================
|
| 856 |
|