исправь ошибки обучения
import json
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.layers import (
    Input, LSTM, Dense, Embedding, Bidirectional, Concatenate, AdditiveAttention
)
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
import sys
import os
import pickle
import string
import re
from sklearn.model_selection import train_test_split

# Параметры модели и обработки данных
MAX_NUM_WORDS_Q = 10000          # Максимальное количество слов в токенизаторе вопросов
MAX_NUM_WORDS_A = 10000          # Максимальное количество слов в токенизаторе ответов
MAX_SEQUENCE_LENGTH = 20         # Максимальная длина последовательности
EMBEDDING_DIM = 100              # Размерность векторного представления слов
LSTM_UNITS = 256                 # Количество единиц в LSTM слое
EPOCHS = 100                     # Количество эпох обучения
BATCH_SIZE = 16                  # Размер пакета
MODEL_PATH = 'qa_model_seq2seq.h5'      # Путь для сохранения модели
TOKENIZER_Q_PATH = 'tokenizer_q.pickle' # Путь для сохранения токенизатора вопросов
TOKENIZER_A_PATH = 'tokenizer_a.pickle' # Путь для сохранения токенизатора ответов

def preprocess_text(text):
    """
    Простая предобработка текста: удаление пунктуации, приведение к нижнему регистру и удаление лишних пробелов.
    """
    text = text.lower()
    text = re.sub(f'[{re.escape(string.punctuation)}]', '', text)
    text = re.sub(r'\s+', ' ', text).strip()
    return text

class QAModel:
    def __init__(self, data_file):
        self.data_file = data_file
        self.questions = []
        self.answers = []
        self.tokenizer_q = Tokenizer(num_words=MAX_NUM_WORDS_Q, oov_token="<OOV>")
        self.tokenizer_a = Tokenizer(num_words=MAX_NUM_WORDS_A, oov_token="<OOV>")
        self.model = None
        self.encoder_model = None
        self.decoder_model = None
        self.max_encoder_seq_length = MAX_SEQUENCE_LENGTH
        self.max_decoder_seq_length = MAX_SEQUENCE_LENGTH
        self.prepare_data()

        # Попытка загрузить существующую модель и токенизаторы
        if os.path.exists(MODEL_PATH) and os.path.exists(TOKENIZER_Q_PATH) and os.path.exists(TOKENIZER_A_PATH):
            self.load_model_and_tokenizers()
        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:
            q = preprocess_text(entry['question'])
            a = preprocess_text(entry['answer'])
            self.questions.append(q)
            # Добавляем специальные токены начала и конца предложения для декодера
            a = '<start> ' + a + ' <end>'
            self.answers.append(a)

        # Разделение данных на обучающую и валидационную выборки
        self.questions_train, self.questions_val, self.answers_train, self.answers_val = train_test_split(
            self.questions, self.answers, test_size=0.2, random_state=42
        )

        # Токенизация вопросов и ответов
        self.tokenizer_q.fit_on_texts(self.questions_train)
        self.tokenizer_a.fit_on_texts(self.answers_train)

        # Сохранение словаря
        self.word_index_q = self.tokenizer_q.word_index
        self.word_index_a = self.tokenizer_a.word_index
        self.index_word_a = {v: k for k, v in self.word_index_a.items()}

        # Преобразование текстов в последовательности
        self.encoder_input_train = self.tokenizer_q.texts_to_sequences(self.questions_train)
        self.encoder_input_val = self.tokenizer_q.texts_to_sequences(self.questions_val)

        self.decoder_input_train = self.tokenizer_a.texts_to_sequences(self.answers_train)
        self.decoder_input_val = self.tokenizer_a.texts_to_sequences(self.answers_val)

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

        self.decoder_input_train = pad_sequences(self.decoder_input_train, maxlen=self.max_decoder_seq_length, padding='post')
        self.decoder_input_val = pad_sequences(self.decoder_input_val, maxlen=self.max_decoder_seq_length, padding='post')

        # Создание выходных данных для декодера, смещенных на один токен вправо
        self.decoder_target_train = self._create_decoder_target(self.decoder_input_train)
        self.decoder_target_val = self._create_decoder_target(self.decoder_input_val)

    def _create_decoder_target(self, decoder_input):
        """
        Создает целевые выходные данные для декодера путем смещения на один токен.
        """
        decoder_target = np.zeros_like(decoder_input)
        decoder_target[:, :-1] = decoder_input[:, 1:]
        decoder_target[:, -1] = 0
        return np.expand_dims(decoder_target, -1)

    def build_model(self):
        """
        Строит модель Seq2Seq с использованием механизмов Attention.
        """
        # Энкодер
        encoder_inputs = Input(shape=(self.max_encoder_seq_length,), name='encoder_inputs')
        encoder_embedding = Embedding(input_dim=MAX_NUM_WORDS_Q, output_dim=EMBEDDING_DIM,
                                      mask_zero=True, name='encoder_embedding')(encoder_inputs)
        encoder_lstm = Bidirectional(LSTM(LSTM_UNITS, return_sequences=True, return_state=True, name='encoder_lstm'))
        encoder_outputs, forward_h, forward_c, backward_h, backward_c = encoder_lstm(encoder_embedding)
        state_h = Concatenate()([forward_h, backward_h])
        state_c = Concatenate()([forward_c, backward_c])

        # Декодер
        decoder_inputs = Input(shape=(self.max_decoder_seq_length,), name='decoder_inputs')
        decoder_embedding = Embedding(input_dim=MAX_NUM_WORDS_A, output_dim=EMBEDDING_DIM,
                                      mask_zero=True, name='decoder_embedding')(decoder_inputs)
        decoder_lstm = LSTM(LSTM_UNITS * 2, return_sequences=True, return_state=True, name='decoder_lstm')
        decoder_outputs, _, _ = decoder_lstm(decoder_embedding, initial_state=[state_h, state_c])

        # Механизм внимания (AdditiveAttention)
        attention_layer = AdditiveAttention(name='attention_layer')
        attention_output = attention_layer([decoder_outputs, encoder_outputs])
        decoder_concat_input = Concatenate(axis=-1, name='concat_layer')([decoder_outputs, attention_output])

        # Выходной слой
        decoder_dense = Dense(MAX_NUM_WORDS_A, activation='softmax', name='output_dense')
        decoder_outputs = decoder_dense(decoder_concat_input)

        # Модель обучения
        self.model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
        self.model.compile(optimizer=Adam(), loss='sparse_categorical_crossentropy', metrics=['accuracy'])
        self.model.summary()

    def build_inference_models(self):
        """
        Строит отдельные энкодер и декодер модели для инференса.
        """
        # Проверка, загружена ли модель
        if self.model is None:
            print("Модель не загружена!")
            return

        # Энкодер модель
        encoder_inputs = self.model.get_layer('encoder_inputs').input
        encoder_embedding = self.model.get_layer('encoder_embedding')(encoder_inputs)
        encoder_lstm = self.model.get_layer('encoder_lstm')
        encoder_outputs, forward_h, forward_c, backward_h, backward_c = encoder_lstm(encoder_embedding)
        state_h = Concatenate()([forward_h, backward_h])
        state_c = Concatenate()([forward_c, backward_c])
        self.encoder_model = Model(encoder_inputs, [encoder_outputs, state_h, state_c])

        # Декодер модель
        decoder_inputs = self.model.get_layer('decoder_inputs').input
        decoder_state_input_h = Input(shape=(LSTM_UNITS * 2,), name='decoder_state_input_h')
        decoder_state_input_c = Input(shape=(LSTM_UNITS * 2,), name='decoder_state_input_c')
        encoder_outputs_inf = Input(shape=(self.max_encoder_seq_length, LSTM_UNITS * 2), name='encoder_outputs_inf')

        decoder_embedding = self.model.get_layer('decoder_embedding')(decoder_inputs)
        decoder_lstm = self.model.get_layer('decoder_lstm')
        decoder_outputs, state_h_dec, state_c_dec = decoder_lstm(decoder_embedding, initial_state=[decoder_state_input_h, decoder_state_input_c])

        # Механизм внимания (AdditiveAttention)
        attention_layer = self.model.get_layer('attention_layer')
        attention_output = attention_layer([decoder_outputs, encoder_outputs_inf])
        decoder_concat_input = Concatenate(axis=-1, name='concat_layer')([decoder_outputs, attention_output])

        # Выходной слой
        decoder_dense = self.model.get_layer('output_dense')
        decoder_outputs = decoder_dense(decoder_concat_input)

        # Модель декодера
        self.decoder_model = Model(
            [decoder_inputs, encoder_outputs_inf, decoder_state_input_h, decoder_state_input_c],
            [decoder_outputs, state_h_dec, state_c_dec]
        )

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

        # Коллбэки
        early_stopping = EarlyStopping(monitor='val_loss', patience=10, verbose=1, restore_best_weights=True)
        model_checkpoint = ModelCheckpoint(MODEL_PATH, monitor='val_loss', save_best_only=True, verbose=1)

        print("Начало обучения модели...")
        history = self.model.fit(
            [self.encoder_input_train, self.decoder_input_train],
            self.decoder_target_train,
            batch_size=BATCH_SIZE,
            epochs=EPOCHS,
            validation_data=([self.encoder_input_val, self.decoder_input_val], self.decoder_target_val),
            callbacks=[early_stopping, model_checkpoint],
            verbose=2
        )
        print("Обучение завершено.")

        # Сохранение токенизаторов
        with open(TOKENIZER_Q_PATH, 'wb') as handle:
            pickle.dump(self.tokenizer_q, handle, protocol=pickle.HIGHEST_PROTOCOL)
        with open(TOKENIZER_A_PATH, 'wb') as handle:
            pickle.dump(self.tokenizer_a, handle, protocol=pickle.HIGHEST_PROTOCOL)
        print(f"Токенизаторы сохранены как {TOKENIZER_Q_PATH} и {TOKENIZER_A_PATH}.")

        # После обучения построим инференс модели
        self.build_inference_models()

    def load_model_and_tokenizers(self):
        try:
            # Загружаем модель
            self.model = load_model(MODEL_PATH)
            print(f"Модель успешно загружена из {MODEL_PATH}.")

            # Загружаем токенизаторы
            with open(TOKENIZER_Q_PATH, 'rb') as handle:
                self.tokenizer_q = pickle.load(handle)
            with open(TOKENIZER_A_PATH, 'rb') as handle:
                self.tokenizer_a = pickle.load(handle)
            print(f"Токенизаторы успешно загружены из {TOKENIZER_Q_PATH} и {TOKENIZER_A_PATH}.")

            # Восстанавливаем индексы
            self.word_index_q = self.tokenizer_q.word_index
            self.word_index_a = self.tokenizer_a.word_index
            self.index_word_a = {v: k for k, v in self.word_index_a.items()}

            # После загрузки модели построим инференс модели
            self.build_inference_models()

        except Exception as e:
            print(f"Ошибка при загрузке модели или токенизаторов: {e}")
            print("Необходимо обучить модель командой 'train'.")

    def predict(self, question, decode_length=MAX_SEQUENCE_LENGTH):
        if self.encoder_model is None or self.decoder_model is None:
            print("Инференс модели не готов. Убедитесь, что модель обучена и инференс модели построены.")
            return

        question = preprocess_text(question)
        sequence = self.tokenizer_q.texts_to_sequences([question])
        padded = pad_sequences(sequence, maxlen=self.max_encoder_seq_length, padding='post')

        # Получение состояний энкодера
        encoder_outputs, state_h, state_c = self.encoder_model.predict(padded, verbose=0)

        # Инициализация декодера с токеном <start>
        target_seq = np.array([[self.tokenizer_a.word_index.get('<start>', 1)]])
        decoded_sentence = []

        # Генерация ответа по одному токену за раз
        for _ in range(decode_length):
            output_tokens, h, c = self.decoder_model.predict(
                [target_seq, encoder_outputs, state_h, state_c],
                verbose=0
            )

            # Выбор токена с наибольшей вероятностью
            sampled_token_index = np.argmax(output_tokens[0, -1, :])
            sampled_word = self.index_word_a.get(sampled_token_index, '<OOV>')

            if sampled_word == '<end>' or sampled_word == '<OOV>':
                break

            decoded_sentence.append(sampled_word)

            # Обновление целевой последовательности (добавляем сгенерированный токен)
            target_seq = np.array([[sampled_token_index]])

            # Обновление состояний декодера
            state_h, state_c = h, c

        return ' '.join(decoded_sentence)

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

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

    print("\nКонсольная программа для обучения и использования Seq2Seq модели вопросов и ответов.")
    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()