Review my code: import sys
import torch
import torch.nn as nn
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QMessageBox, QFileDialog
from transformers import BertTokenizer, GPT2Tokenizer, XLNetTokenizer
import pandas as pd
import torch.optim as optim


# Define the neural network model.
class RNNModel(nn.Module):
    def __init__(self, input_size, hidden_sizes, layer_types):
        super(RNNModel, self).__init__()
        self.layers = nn.ModuleList()
        for i, layer_type in enumerate(layer_types):
            input_dim = input_size if i == 0 else hidden_sizes[i - 1]
            if layer_type == 'LSTM':
                self.layers.append(nn.LSTM(input_dim, hidden_sizes[i], batch_first=True))
            elif layer_type == 'GRU':
                self.layers.append(nn.GRU(input_dim, hidden_sizes[i], batch_first=True))
            elif layer_type == 'Transformer':
                transformer_layer = nn.TransformerEncoderLayer(d_model=input_dim, nhead=1)
                self.layers.append(nn.TransformerEncoder(transformer_layer, num_layers=1))
            else:
                raise ValueError(f"Unrecognized layer type: {layer_type}")

    def forward(self, x):
        for layer in self.layers:
            if isinstance(layer, nn.TransformerEncoder):
                x = layer(x)
            else:
                x, _ = layer(x)
        return x
    

# Define the custom GUI widget for configuring the language model.
class LanguageModelConfigurator(QtWidgets.QWidget):
    def __init__(self):
        super(LanguageModelConfigurator, self).__init__()
        self.model = None
        self.train_data_loader = None
        self.data_filepath = None
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Language Model Configuration')
        self.seq_length_spinbox = QtWidgets.QSpinBox()
        self.seq_length_spinbox.setMinimum(1)
        self.seq_length_spinbox.setMaximum(512)
        self.seq_length_spinbox.setValue(128)

        vbox = QtWidgets.QVBoxLayout(self)

        seq_length_layout = QtWidgets.QHBoxLayout()
        seq_length_label = QtWidgets.QLabel("Sequence Length:", self)
        seq_length_layout.addWidget(seq_length_label)
        seq_length_layout.addWidget(self.seq_length_spinbox)

        vbox.addLayout(seq_length_layout)

        layer_config_group = QtWidgets.QGroupBox("Layer Configuration")
        layer_config_layout = QtWidgets.QVBoxLayout(layer_config_group)

        self.layer_list_widget = QtWidgets.QListWidget()
        layer_config_layout.addWidget(self.layer_list_widget)

        hbox_layers = QtWidgets.QHBoxLayout()
        self.layer_type_combo = QtWidgets.QComboBox()
        self.layer_type_combo.addItems(['LSTM', 'GRU', 'Transformer'])
        self.layer_size_spinbox = QtWidgets.QSpinBox()
        self.layer_size_spinbox.setMinimum(1)
        self.layer_size_spinbox.setMaximum(1024)

        hbox_layers.addWidget(self.layer_type_combo)
        hbox_layers.addWidget(self.layer_size_spinbox)

        add_layer_button = QtWidgets.QPushButton('Add Layer')
        add_layer_button.clicked.connect(self.add_layer)
        hbox_layers.addWidget(add_layer_button)

        layer_config_layout.addLayout(hbox_layers)
        vbox.addWidget(layer_config_group)

        tokenizer_config_group = QtWidgets.QGroupBox("Tokenizer Configuration")
        tokenizer_config_layout = QtWidgets.QVBoxLayout(tokenizer_config_group)

        tokenizer_label = QtWidgets.QLabel('Choose a tokenizer:', self)
        tokenizer_config_layout.addWidget(tokenizer_label)

        self.tokenizer_combo = QtWidgets.QComboBox()
        self.tokenizer_combo.addItems([
            'bert-base-cased',
            'gpt2-medium',
            'xlnet-base-cased'
        ])
        tokenizer_config_layout.addWidget(self.tokenizer_combo)

        self.status_label = QtWidgets.QLabel("")

        vbox.addWidget(tokenizer_config_group)
        vbox.addWidget(self.status_label)

        submit_button = QtWidgets.QPushButton('Generate Model', self)
        submit_button.clicked.connect(self.generate_model)
        vbox.addWidget(submit_button)

        self.setLayout(vbox)
        self.setStyleSheet("""
            QWidget {
                font-size: 14px;
            }
            QPushButton {
                background-color: #007BFF;
                border-style: none;
                padding: 6px 12px;
                color: white;
                border-radius: 4px;
            }
            QPushButton:hover {
                background-color: #0056b3;
            }
            QLabel {
                padding-bottom: 4px;
            }
        """)

        # Data file line edit and browse button
        self.data_file_line_edit = QtWidgets.QLineEdit(self)
        browse_button = QtWidgets.QPushButton('Browse Data File', self)
        browse_button.clicked.connect(self.browse_data_file)

        data_file_layout = QtWidgets.QHBoxLayout()
        data_file_layout.addWidget(self.data_file_line_edit)
        data_file_layout.addWidget(browse_button)

        vbox.addLayout(data_file_layout)

        # Epochs spinbox
        num_epochs_layout = QtWidgets.QHBoxLayout()
        num_epochs_label = QtWidgets.QLabel("Number of Epochs:", self)
        self.num_epochs_spinbox = QtWidgets.QSpinBox()
        self.num_epochs_spinbox.setMinimum(1)
        self.num_epochs_spinbox.setMaximum(100)
        num_epochs_layout.addWidget(num_epochs_label)
        num_epochs_layout.addWidget(self.num_epochs_spinbox)

        vbox.addLayout(num_epochs_layout)

        # Training button layout
        training_layout = QtWidgets.QHBoxLayout()
        self.train_button = QtWidgets.QPushButton('Train Model', self)
        self.train_button.clicked.connect(self.on_train_click)
        training_layout.addWidget(self.train_button)

        vbox.addLayout(training_layout)


    def browse_data_file(self):
        filepath, _ = QFileDialog.getOpenFileName(self, "Open Data File", "", "Data Files (*.csv *.txt)")
        if filepath:
            self.data_file_line_edit.setText(filepath)
            self.data_filepath = filepath

    def load_data(self):
        if 'inputs' not in data_df.columns or 'labels' not in data_df.columns:
           self.showMessage("Error", "Data file does not contain 'inputs' and 'labels' columns.")
           return None

        if self.data_filepath:
            try:
                if self.data_filepath.endswith(".csv"):
                    data_df = pd.read_csv(self.data_filepath)
                    return data_df
                elif self.data_filepath.endswith(".txt"):
                    with open(self.data_filepath, "r", encoding="utf-8") as file:
                        text_data = file.read()
                    return text_data
                else:
                    self.showMessage("Error", "Unsupported file type selected.")
                return None
            except Exception as e:
                self.showMessage("Error", f"Failed to load data: {e}")
        else:
            self.showMessage("Error", "No data file selected.")

    def create_data_loader(self, data):
        if isinstance(data, pd.DataFrame):
            # Convert DataFrame to tensors
            inputs = torch.tensor(data['inputs'].values)
            labels = torch.tensor(data['labels'].values)
            dataset = torch.utils.data.TensorDataset(inputs, labels)
        elif isinstance(data, str):
            # Tokenize text data and convert to tensors
            tokenizer_name = self.tokenizer_combo.currentText()
            tokenizer_class = {
            'bert-base-cased': BertTokenizer,
            'gpt2-medium': GPT2Tokenizer,
            'xlnet-base-cased': XLNetTokenizer
            }.get(tokenizer_name)
            tokenizer = tokenizer_class.from_pretrained(tokenizer_name)
            encoded_data = tokenizer.encode_plus(
            data,
            add_special_tokens=True,
            padding='max_length',
            truncation=True,
            max_length=self.seq_length_spinbox.value(),
            return_tensors='pt'
            )
            inputs = encoded_data['input_ids']
            labels = encoded_data['attention_mask'] # Replace with actual labels
            dataset = torch.utils.data.TensorDataset(inputs, labels)
            return dataset
        else:
            self.showMessage("Error", "Unsupported file type selected.")
            return None

    # Create data loader
        batch_size = 32  # Adjust batch size as needed
        data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
        return data_loader

    def add_layer(self):
        layer_type = self.layer_type_combo.currentText()
        layer_size = self.layer_size_spinbox.value()
        if layer_type not in ['LSTM', 'GRU', 'Transformer']:
           self.showMessage("Error", "Invalid layer type.")
           return
        if layer_size < 1 or layer_size > 1024:
           self.showMessage("Error", "Invalid layer size.")
           return
        self.layer_list_widget.addItem(f'{layer_type} - Size: {layer_size}')


    def showMessage(self, title, message):
        QMessageBox.information(self, title, message)

    def generate_model(self):
        self.status_label.setText("Generating model…")

        layers = [
            (item.text().split(' - Size: ')[0], int(item.text().split(' - Size: ')[1]))
            for item in self.layer_list_widget.findItems('*', QtCore.Qt.MatchWildcard)
        ]
        layer_types, hidden_sizes = zip(*layers) if layers else ([], [])

        tokenizer_name = self.tokenizer_combo.currentText()
        tokenizer_class = {
            'bert-base-cased': BertTokenizer,
            'gpt2-medium': GPT2Tokenizer,
            'xlnet-base-cased': XLNetTokenizer
        }.get(tokenizer_name)
        if not tokenizer_class:
            self.showMessage("Error", "Unsupported tokenizer selected.")
            return

        if self.model is None:
           self.showMessage("Error", "Failed to generate model.")
           return


        self.status_label.setText("Model generated successfully!")
        tokenizer = tokenizer_class.from_pretrained(tokenizer_name)
        input_size = self.seq_length_spinbox.value()

        try:
            self.model = RNNModel(input_size, list(hidden_sizes), list(layer_types))
            self.status_label.setText("Model generated successfully!")
        except ValueError as e:
           self.showMessage("Error", f"Invalid layer configuration: {e}")
                  
    def on_train_click(self):
        loaded_data = self.load_data()
        if loaded_data is not None:
           self.train_data_loader = self.create_data_loader(loaded_data)
           if self.train_data_loader is None:
               self.showMessage("Error", "Failed to create a DataLoader for the training data.")
               return
           self.train_model(self.model, self.train_data_loader)

        
        # Check if the model is defined.
           if self.model is None:
            self.showMessage("Error", "Model not defined.")
            return
        
        # Start the training process.
           self.train_model(self.model, self.train_data_loader)
    
    def train_model(self, model, train_data_loader):
        if model is None:
          self.showMessage("Error", "Model not defined.")
          return
 
        num_epochs = self.num_epochs_spinbox.value()
        criterion = nn.CrossEntropyLoss()
        optimizer = optim.Adam(model.parameters(), lr=0.001)

        model.train()
        for epoch in range(num_epochs):
           running_loss = 0.0
           for i, batch in enumerate(train_data_loader):
            for batch in train_data_loader:
                inputs, labels = batch
                
                optimizer.zero_grad()
                outputs = model(inputs)
                loss = criterion(outputs, labels)
                loss.backward()
                optimizer.step()
            
        running_loss += loss.item()
        if i % 100 == 99:  # print every 100 mini-batches
                   print(f"Batch {i + 1}, Loss: {running_loss / 100}")
                   running_loss = 0.0



class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Model Training')
        self.language_model_configurator = LanguageModelConfigurator()
        self.setCentralWidget(self.language_model_configurator)
        self.show()


def main():
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()