Spaces:
Running
Running
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) |