Check this code : import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
import json

# Define pair-bit tokenizer
def tokenize(text):
    tokens = text.split()
    token_pairs = []
    for i in range(len(tokens)-1):
        token_pairs.append((tokens[i], tokens[i+1]))
    return token_pairs

# Load training data from JSON file
try:
    with open('Chatdata.json', 'r') as file:
        training_data = json.load(file)
except FileNotFoundError:
    print("Training data file not found.")
    exit()

# Tokenization using pair-bit tokenizer
vocab = set()
for item in training_data:
    token_pairs = tokenize(item['input'])
    token_pairs += tokenize(item['output'])
    vocab.update(token_pairs)
vocab_size = len(vocab) 
index_to_char = list(vocab)
char_to_index = {char: index for index, char in enumerate(index_to_char)}

# Convert input and output sequences to numerical representation and pad them
max_seq_length = max(max(len(tokenize(item['input'])), len(tokenize(item['output']))) for item in training_data)
input_sequences = torch.zeros((len(training_data), max_seq_length), dtype=torch.long)
output_sequences = torch.zeros((len(training_data), max_seq_length), dtype=torch.long)

for i, item in enumerate(training_data):
    input_seq = [char_to_index[pair] for pair in tokenize(item['input'])]
    output_seq = [char_to_index[pair] for pair in tokenize(item['output'])]
    input_sequences[i, :len(input_seq)] = torch.tensor(input_seq)
    output_sequences[i, :len(output_seq)] = torch.tensor(output_seq)

# Define LSTM model
class ChatbotLSTM(nn.Module):
    def __init__(self, vocab_size, hidden_size):
        super(ChatbotLSTM, self).__init__()
        self.hidden_size = hidden_size
        self.embedding = nn.Embedding(vocab_size, hidden_size)
        self.lstm = nn.LSTM(hidden_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, vocab_size)

    def forward(self, input_seq):
        embedded = self.embedding(input_seq)
        lstm_out, _ = self.lstm(embedded)
        output = self.fc(lstm_out)
        return output

# Initialize the model
hidden_size = 512
model = ChatbotLSTM(vocab_size, hidden_size)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# Create DataLoader for batch processing
batch_size = 64
dataset = TensorDataset(input_sequences, output_sequences)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

# Train the model
model.train()
for epoch in range(200):
    for input_batch, target_batch in dataloader:
        optimizer.zero_grad()
        output = model(input_batch)
        
        # Reshape the output and target for CrossEntropyLoss, needs batches to come first
        loss = criterion(output.view(-1, vocab_size), target_batch.view(-1))
        loss.backward()
        optimizer.step()

    # Print loss after each epoch
    print(f'Epoch {epoch+1}, Loss: {loss.item()}')

model_name = "Chatbot_model_A1"

# Save model parameters (weights and biases)
torch.save(model.state_dict(), f"{model_name}.pth")

# Model configuration
model_config = {
    "vocab_size": vocab_size,
    "hidden_size": hidden_size,
    "num_layers": 1,  # Assuming we have 1 layer in LSTM; change accordingly if you have more
    # Add any other relevant configuration parameters if necessary
}

# Save the model configuration
with open(f"{model_name}_config.json", "w") as config_file:
    json.dump(model_config, config_file)

# Saving char_to_index and index_to_char mappings as JSON
with open(f"{model_name}_vocab.json", "w") as vocab_file:
    json.dump({"char_to_index": char_to_index, "index_to_char": index_to_char}, vocab_file)

# You can load the model parameters and the vocab later using:
# model.load_state_dict(torch.load(‘chatbot_model_params.pth’))
# with open(‘chatbot_vocab.json’, ‘r’) as vocab_file:
#     vocab_data = json.load(vocab_file)
#     char_to_index = vocab_data[‘char_to_index’]
#     index_to_char = vocab_data[‘index_to_char’]  and check the error i get : Traceback (most recent call last):
  File "c:\Users\L14\Desktop\Projets\Experimental-Learning\main-5.py", line 103, in <module>
    json.dump({"char_to_index": char_to_index, "index_to_char": index_to_char}, vocab_file)
  File "C:\Users\L14\AppData\Local\Programs\Python\Python310\lib\json\__init__.py", line 179, in dump
    for chunk in iterable:
  File "C:\Users\L14\AppData\Local\Programs\Python\Python310\lib\json\encoder.py", line 431, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "C:\Users\L14\AppData\Local\Programs\Python\Python310\lib\json\encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "C:\Users\L14\AppData\Local\Programs\Python\Python310\lib\json\encoder.py", line 376, in _iterencode_dict
    raise TypeError(f'keys must be str, int, float, bool or None, '
TypeError: keys must be str, int, float, bool or None, not tuple