I have this code for a python app to create language, code: import tkinter as tk
from tkinter import filedialog
from keras.models import Sequential
from keras.layers import Embedding, LSTM, Dense
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.utils import to_categorical
import numpy as np
import os
import pickle
from keras.layers import Masking

# Data loader and preprocessor function
def preprocess_data(file_paths):
    tokenizer = Tokenizer()
    
    # Load and concatenate content from all selected dataset files
    all_text = ""
    for file_path in file_paths:
        with open(file_path, "r", encoding="utf-8") as file:
            text = file.read()
            all_text += text + "\n"  # Ensure separation between files’ content
    
    sentences = all_text.split("\n")
    
    tokenizer.fit_on_texts(sentences)
    sequences = tokenizer.texts_to_sequences(sentences)

    # Check if we’re using a fixed sequence length
    if sequence_choice_var.get():
        # Read the fixed sequence length from the respective entry field
        sequence_length = int(entry_fixed_length.get())
        padded_sequences = pad_sequences(sequences, maxlen=sequence_length, padding="pre")
    else:
        # Minimal sequence length, could also eliminate the minimum length condition
        sequence_length = min(len(seq) for seq in sequences if len(seq) > 1)
        padded_sequences = sequences
    
    big_sequence = [token for seq in sequences for token in seq]
    input_sequences, output_words = [], []
    
    # Assign a sequence length based on the shortest sentence
    # Note: for better training with variable lengths, consider using sentences directly and/or padding at the end of each batch
    sequence_length = min(len(seq) for seq in sequences if len(seq) > 1)
    
    for i in range(len(big_sequence) - sequence_length):
        input_sequences.append(big_sequence[i:i + sequence_length])
        output_words.append(big_sequence[i + sequence_length])
    
    # Remove pad_sequences call - handle varying sequence lengths directly in the model using masking or by padding at batch end
    
    vocab_size = len(tokenizer.word_index) + 1
    
    output_words = np.array(output_words)
    output_words = to_categorical(output_words, num_classes=vocab_size)
    
    return np.array(input_sequences), output_words, vocab_size, tokenizer


# Function to train and save the model
def train_model():
    num_layers = int(entry_layers.get())
    layer_size = int(entry_size.get())
    model_name = entry_name.get()
    epochs = int(entry_epochs.get())
    data_paths = root.filenames  # Changed to accept multiple filenames
    
    # Preprocess the data
    input_sequences, output_words, vocab_size, tokenizer = preprocess_data(data_paths)
    
    tokenizer_path = os.path.join('tokenizers', f'{model_name}_tokenizer.pickle')
    if not os.path.exists('tokenizers'):
        os.makedirs('tokenizers')
    with open(tokenizer_path, 'wb') as handle:
        pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)
    print(f"Tokenizer saved at {tokenizer_path}")

    # Also save the sequence length if fixed
    if sequence_choice_var.get():
         with open(tokenizer_path, "wb") as handle:
             pickle.dump((tokenizer, sequence_length), handle, protocol=pickle.HIGHEST_PROTOCOL)
    else:
         with open(tokenizer_path, "wb") as handle:
             pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)


    model = Sequential()
    model.add(Embedding(input_dim=vocab_size, output_dim=layer_size))

    if not sequence_choice_var.get():  # Add masking for variable length sequences only
        model.add(Masking(mask_value=0))  # Ignoring padded values (zeros)

    model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
    model.fit(input_sequences, output_words, epochs=epochs)
    
    if not os.path.exists('models'):
        os.makedirs('models')
    model.save(os.path.join('models', f'{model_name}.h5'))
    print(f"Model {model_name} trained and saved successfully!")

# UI Setup
root = tk.Tk()
root.title("Chatbot Language Model Trainer")

# Number of layers
lbl_layers = tk.Label(root, text="Number of layers:")
lbl_layers.pack()
entry_layers = tk.Entry(root)
entry_layers.pack()

# Layer size
lbl_size = tk.Label(root, text="Size of each layer:")
lbl_size.pack()
entry_size = tk.Entry(root)
entry_size.pack()

# Model name
lbl_name = tk.Label(root, text="Model name:")
lbl_name.pack()
entry_name = tk.Entry(root)
entry_name.pack()

# Number of epochs
lbl_epochs = tk.Label(root, text="Number of epochs:")
lbl_epochs.pack()
entry_epochs = tk.Entry(root)
entry_epochs.pack()

# Data file path
lbl_data_path = tk.Label(root, text="Data file path:")
lbl_data_path.pack()
entry_data_path = tk.Entry(root)
entry_data_path.pack()

# Checkbox for sequence length choice
lbl_sequence_choice = tk.Label(root, text="Use fixed sequence length:")
lbl_sequence_choice.pack()
sequence_choice_var = tk.BooleanVar()  # Boolean variable to hold the checkbox state
chk_sequence_choice = tk.Checkbutton(root, text="Fixed length", variable=sequence_choice_var)
chk_sequence_choice.pack()

# Entry for fixed sequence length if the toggle is on
lbl_fixed_length = tk.Label(root, text="Fixed sequence length:")
lbl_fixed_length.pack()
entry_fixed_length = tk.Entry(root)
entry_fixed_length.pack()

# Function to select multiple files
def select_files():
    file_paths = filedialog.askopenfilenames()  # Changed to open multiple files
    root.filenames = file_paths  # Store the list of file paths on the root object
    entry_data_path.delete(0, tk.END)
    entry_data_path.insert(0, "; ".join(file_paths))  # Display all file paths in the entry

btn_browse = tk.Button(root, text="Browse…", command=select_files)  # Changed to select_files
btn_browse.pack()

# Train button
btn_train = tk.Button(root, text="Train Model", command=train_model)
btn_train.pack()

# Start the tkinter loop
root.mainloop()    but the variable sequence_length is not defined, help me with that