import streamlit as st import torch import torch.nn as nn import torch.optim as optim import numpy as np # Define the RNN or LSTM Model class LSTMModel(nn.Module): def __init__(self, input_size, hidden_size, output_size, num_layers): super(LSTMModel, self).__init__() self.hidden_size = hidden_size self.num_layers = num_layers self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x, h): out, h = self.lstm(x, h) out = self.fc(out[:, -1, :]) return out, h # Text generation function def generate_text(model, start_str, length, char_to_int, int_to_char, num_layers, hidden_size): model.eval() input_seq = [char_to_int[c] for c in start_str] input_seq = torch.tensor(input_seq, dtype=torch.float32).unsqueeze(0).unsqueeze(-1) h = (torch.zeros(num_layers, 1, hidden_size), torch.zeros(num_layers, 1, hidden_size)) generated_text = start_str for _ in range(length): output, h = model(input_seq, h) _, predicted = torch.max(output, 1) predicted_char = int_to_char[predicted.item()] generated_text += predicted_char input_seq = torch.tensor([char_to_int[predicted_char]], dtype=torch.float32).unsqueeze(0).unsqueeze(-1) return generated_text # Streamlit interface st.title("RNN/LSTM Text Generation") # Inputs text_data = st.text_area("Enter your text data for training:", "To be, or not to be, that is the question:\nWhether 'tis nobler in the mind to suffer\nThe slings and arrows of outrageous fortune,\nOr to take arms against a sea of troubles\nAnd by opposing end them. To die: to sleep;\nNo more; and by a sleep to say we end\nThe heart-ache and the thousand natural shocks\nThat flesh is heir to, 'tis a consummation\nDevoutly to be wish'd. To die, to sleep;\nTo sleep: perchance to dream: ay, there's the rub;\nFor in that sleep of death what dreams may come\nWhen we have shuffled off this mortal coil,\nMust give us pause: there's the respect\nThat makes calamity of so long life;") start_string = st.text_input("Enter the start string for text generation:") seq_length = st.number_input("Sequence length:", min_value=10, value=100) hidden_size = st.number_input("Hidden size:", min_value=50, value=256) num_layers = st.number_input("Number of layers:", min_value=1, value=2) learning_rate = st.number_input("Learning rate:", min_value=0.0001, value=0.003, format="%.4f") num_epochs = st.number_input("Number of epochs:", min_value=1, value=20) generate_length = st.number_input("Generated text length:", min_value=50, value=500) if st.button("Train and Generate"): # Data Preparation text = text_data if len(text) <= seq_length: st.error("Text data is too short for the given sequence length. Please enter more text data.") else: chars = sorted(list(set(text))) char_to_int = {c: i for i, c in enumerate(chars)} int_to_char = {i: c for i, c in enumerate(chars)} # Prepare input-output pairs dataX = [] dataY = [] for i in range(0, len(text) - seq_length): seq_in = text[i:i + seq_length] seq_out = text[i + seq_length] dataX.append([char_to_int[char] for char in seq_in]) dataY.append(char_to_int[seq_out]) if len(dataX) == 0: st.error("Not enough data to create input-output pairs. Please provide more text data.") else: X = np.reshape(dataX, (len(dataX), seq_length, 1)) X = X / float(len(chars)) Y = np.array(dataY) # Convert to PyTorch tensors X_tensor = torch.tensor(X, dtype=torch.float32) Y_tensor = torch.tensor(Y, dtype=torch.long) # Model initialization model = LSTMModel(input_size=1, hidden_size=hidden_size, output_size=len(chars), num_layers=num_layers) # Loss and optimizer criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate) # Training the model for epoch in range(num_epochs): h = (torch.zeros(num_layers, 1, hidden_size), torch.zeros(num_layers, 1, hidden_size)) epoch_loss = 0 for i in range(len(dataX)): inputs = X_tensor[i].unsqueeze(0) # Shape: (1, seq_length, 1) targets = Y_tensor[i].unsqueeze(0) # Shape: (1,) # Forward pass outputs, h = model(inputs, (h[0].detach(), h[1].detach())) loss = criterion(outputs, targets) # Backward pass and optimization optimizer.zero_grad() loss.backward() optimizer.step() epoch_loss += loss.item() avg_loss = epoch_loss / len(dataX) st.write(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {avg_loss:.4f}') # Text generation generated_text = generate_text(model, start_string, generate_length, char_to_int, int_to_char, num_layers, hidden_size) st.subheader("Generated Text") st.write(generated_text)