оптемезируй и улучши
import json
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.layers import LSTM, Dense, Embedding
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.optimizers import Adam
import sys
import os
import pickle

# Параметры модели и обработки данных
MAX_NUM_WORDS = 10000  # Максимальное количество слов в словаре
MAX_SEQUENCE_LENGTH = 20  # Максимальная длина последовательности
EMBEDDING_DIM = 50  # Размерность векторного представления слов
LSTM_UNITS = 128  # Количество единиц в LSTM слое
EPOCHS = 100  # Количество эпох обучения
BATCH_SIZE = 16  # Размер пакета
MODEL_PATH = 'qa_model.h5'  # Путь для сохранения модели
TOKENIZER_PATH = 'tokenizer.pickle'  # Путь для сохранения токенизатора


class QAModel:
    def __init__(self, data_file):
        self.data_file = data_file
        self.questions = []
        self.answers = []
        self.tokenizer = Tokenizer(num_words=MAX_NUM_WORDS, oov_token="<OOV>")
        self.model = None
        self.word_index = {}
        self.index_word = {}
        self.prepare_data()

        # Попытка загрузить существующую модель и токенизатор
        if os.path.exists(MODEL_PATH) and os.path.exists(TOKENIZER_PATH):
            self.load_model_and_tokenizer()
        else:
            print("Сохраненная модель или токенизатор не найдены. Необходимо обучить модель командой 'train'.")

    def prepare_data(self):
        # Загрузка данных из JSON
        try:
            with open(self.data_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
        except Exception as e:
            print(f"Ошибка при загрузке файла {self.data_file}: {e}")
            sys.exit(1)

        for entry in data:
            # Приведение вопросов и ответов к нижнему регистру для унификации
            self.questions.append(entry['question'].lower())
            self.answers.append(entry['answer'].lower())

        # Создание токенизатора и обучение на вопросах и ответах
        self.tokenizer.fit_on_texts(self.questions + self.answers)
        self.word_index = self.tokenizer.word_index
        self.index_word = {v: k for k, v in self.word_index.items()}

        # Преобразование текстов в последовательности
        self.X = self.tokenizer.texts_to_sequences(self.questions)
        self.y = self.tokenizer.texts_to_sequences(self.answers)

        # Добавление отступов и ограничение длины последовательностей
        self.X = pad_sequences(self.X, maxlen=MAX_SEQUENCE_LENGTH, padding='post')
        self.y = pad_sequences(self.y, maxlen=MAX_SEQUENCE_LENGTH, padding='post')

        # Преобразование y в категориальные данные для многоклассовой классификации
        self.y = tf.keras.utils.to_categorical(self.y, num_classes=MAX_NUM_WORDS)

    def build_model(self):
        self.model = Sequential()
        self.model.add(Embedding(input_dim=MAX_NUM_WORDS, output_dim=EMBEDDING_DIM, input_length=MAX_SEQUENCE_LENGTH))
        self.model.add(LSTM(LSTM_UNITS, return_sequences=True))
        self.model.add(Dense(MAX_NUM_WORDS, activation='softmax'))
        self.model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])
        self.model.summary()

    def train(self):
        if self.model is None:
            self.build_model()

        print("Начало обучения модели...")
        self.model.fit(self.X, self.y, epochs=EPOCHS, batch_size=BATCH_SIZE, verbose=2)
        print("Обучение завершено.")

        # Сохранение модели и токенизатора
        self.model.save(MODEL_PATH)
        with open(TOKENIZER_PATH, 'wb') as handle:
            pickle.dump(self.tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)
        print(f"Модель сохранена как {MODEL_PATH} и токенизатор сохранен как {TOKENIZER_PATH}.")

    def load_model_and_tokenizer(self):
        try:
            self.model = load_model(MODEL_PATH)
            with open(TOKENIZER_PATH, 'rb') as handle:
                self.tokenizer = pickle.load(handle)
            self.word_index = self.tokenizer.word_index
            self.index_word = {v: k for k, v in self.word_index.items()}
            print(f"Модель успешно загружена из {MODEL_PATH}.")
            print(f"Токенизатор успешно загружен из {TOKENIZER_PATH}.")
        except Exception as e:
            print(f"Ошибка при загрузке модели или токенизатора: {e}")
            print("Необходимо обучить модель командой 'train'.")

    def predict(self, question, decode_length=MAX_SEQUENCE_LENGTH):
        if self.model is None:
            print("Модель не обучена. Сначала обучите модель командой 'train'.")
            return

        question = question.lower()
        sequence = self.tokenizer.texts_to_sequences([question])
        padded = pad_sequences(sequence, maxlen=MAX_SEQUENCE_LENGTH, padding='post')

        prediction = self.model.predict(padded, verbose=0)
        predicted_sequence = np.argmax(prediction, axis=-1)[0]

        # Преобразование чисел обратно в слова
        answer = []
        for idx in predicted_sequence:
            word = self.index_word.get(idx, "")
            if word == "<OOV>" or word == "":
                break
            answer.append(word)
            if len(answer) >= decode_length:
                break
        return ' '.join(answer)


def main():
    if len(sys.argv) < 2:
        print("Использование: python qa_rnn.py <data.json>")
        return

    data_file = sys.argv[1]
    qa = QAModel(data_file)

    print("\nКонсольная программа для обучения и использования RNN модели вопросов и ответов.")
    print("Доступные команды:")
    print(" - train : Обучить (или переобучить) модель на предоставленных данных.")
    print(" - ask <ваш вопрос> : Получить ответ на заданный вопрос.")
    print(" - exit : Выйти из программы.\n")

    while True:
        try:
            command = input("Введите команду: ").strip()
        except (EOFError, KeyboardInterrupt):
            print("\nВыход из программы.")
            break

        if command.lower() == "train":
            qa.train()
        elif command.lower().startswith("ask "):
            question = command[4:].strip()
            if not question:
                print("Пожалуйста, введите вопрос после команды 'ask'. Например: ask Как тебя зовут?")
                continue
            answer = qa.predict(question)
            if answer:
                print("Ответ:", answer)
            else:
                print("Извините, я не могу ответить на этот вопрос.")
        elif command.lower() == "exit":
            print("Выход из программы.")
            break
        else:
            print("Неизвестная команда. Доступные команды: train, ask <вопрос>, exit.")


if __name__ == "__main__":
    main()