"""================================================================================
FILE: exp1(MNIST).py
================================================================================
import numpy as np
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
import matplotlib.pyplot as plt
print("Loading and preprocessing data...")
(x_train_orig, y_train), (x_test_orig, y_test) = mnist.load_data()
x_train = x_train_orig.reshape(60000, 784).astype('float32') / 255
x_test = x_test_orig.reshape(10000, 784).astype('float32') / 255
y_train_cat = to_categorical(y_train, 10)
y_test_cat = to_categorical(y_test, 10)
class NeuralNetwork:
    def __init__(self, in_s, hid_s, out_s):
        self.W1 = np.random.randn(in_s, hid_s) * np.sqrt(2. / in_s)
        self.b1 = np.zeros((1, hid_s))
        self.W2 = np.random.randn(hid_s, out_s) * np.sqrt(2. / hid_s)
        self.b2 = np.zeros((1, out_s))
    def _relu(self, Z):
        return np.maximum(0, Z)
    def _relu_deriv(self, Z):
        return (Z > 0) * 1
    def _softmax(self, Z):
        exp_Z = np.exp(Z - np.max(Z, axis=1, keepdims=True))
        return exp_Z / np.sum(exp_Z, axis=1, keepdims=True)
    def _compute_loss(self, Y_true, Y_pred):
        m = Y_true.shape[0]
        log_p = -np.log(Y_pred[range(m), np.argmax(Y_true, axis=1)] + 1e-9)
        return np.sum(log_p) / m
    def forward_pass(self, X):
        self.X = X
        self.Z1 = X.dot(self.W1) + self.b1
        self.A1 = self._relu(self.Z1)
        self.Z2 = self.A1.dot(self.W2) + self.b2
        self.A2 = self._softmax(self.Z2)
        return self.A2
    def backward_pass(self, Y_true):
        m = Y_true.shape[0]
        dZ2 = self.A2 - Y_true
        self.dW2 = (1/m) * self.A1.T.dot(dZ2)
        self.db2 = (1/m) * np.sum(dZ2, axis=0, keepdims=True)
        dA1 = dZ2.dot(self.W2.T)
        dZ1 = dA1 * self._relu_deriv(self.Z1)
        self.dW1 = (1/m) * self.X.T.dot(dZ1)
        self.db1 = (1/m) * np.sum(dZ1, axis=0, keepdims=True)
    def update_params(self, learning_rate):
        self.W1 -= learning_rate * self.dW1
        self.b1 -= learning_rate * self.db1
        self.W2 -= learning_rate * self.dW2
        self.b2 -= learning_rate * self.db2
    def predict(self, X):
        Y_pred_probs = self.forward_pass(X)
        return np.argmax(Y_pred_probs, axis=1)
    def evaluate(self, X, Y_true_one_hot):
        predictions = self.predict(X)
        true_labels = np.argmax(Y_true_one_hot, axis=1)
        return np.mean(predictions == true_labels)
    def train(self, X_train, Y_train, epochs, learning_rate, batch_size):
        n_samples = X_train.shape[0]
        for epoch in range(epochs):
            permutation = np.random.permutation(n_samples)
            X_shuffled = X_train[permutation]
            Y_shuffled = Y_train[permutation]
            for i in range(0, n_samples, batch_size):
                X_batch = X_shuffled[i:i + batch_size]
                Y_batch = Y_shuffled[i:i + batch_size]
                self.forward_pass(X_batch)
                self.backward_pass(Y_batch)
                self.update_params(learning_rate)
            Y_pred_full = self.forward_pass(X_train)
            loss = self._compute_loss(Y_train, Y_pred_full)
            accuracy = self.evaluate(X_train, Y_train)
            print(f"Epoch {epoch+1}/{epochs} - Loss: {loss:.4f} - Train Acc: {accuracy:.4f}")
INPUT_SIZE = 784
HIDDEN_SIZE = 64
OUTPUT_SIZE = 10
LEARNING_RATE = 0.1
EPOCHS = 10
BATCH_SIZE = 64
print("Initializing and training network...")
nn = NeuralNetwork(INPUT_SIZE, HIDDEN_SIZE, OUTPUT_SIZE)
nn.train(x_train, y_train_cat, EPOCHS, LEARNING_RATE, BATCH_SIZE)
test_accuracy = nn.evaluate(x_test, y_test_cat)
print("\nTraining complete!")
print(f"Final Test Accuracy: {test_accuracy * 100:.2f}%")
print("\nDisplaying a random test prediction...")
idx = np.random.randint(0, x_test.shape[0])
img_flat = x_test[idx:idx+1]
img_2d = x_test_orig[idx]
true_label = y_test[idx]
prediction = nn.predict(img_flat)[0]
plt.imshow(img_2d, cmap='gray')
title_color = 'green' if prediction == true_label else 'red'
plt.title(f"Prediction: {prediction}\nTrue Label: {true_label}", color=title_color)
plt.axis('off')
plt.show()


================================================================================
FILE: exp2(MLP).py
================================================================================
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.optimizers import Adam
print("Loading and preparing Iris dataset...")
iris = load_iris()
X = iris.data
y = iris.target.reshape(-1, 1)
X_scaled = StandardScaler().fit_transform(X)
y_onehot = OneHotEncoder(sparse_output=False).fit_transform(y)
X_train, X_val, y_train, y_val = train_test_split(
    X_scaled, y_onehot, test_size=0.2, random_state=42
)
def build_model(hidden_layers, activation_fn, input_shape, output_shape):
    model = Sequential()
    model.add(Input(shape=input_shape))
    for layer_size in hidden_layers:
        model.add(Dense(layer_size, activation=activation_fn))
    model.add(Dense(output_shape, activation='softmax'))
    model.compile(
        optimizer=Adam(learning_rate=0.001),
        loss='categorical_crossentropy',
        metrics=['accuracy']
    )
    return model
def plot_history(history, title):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
    fig.suptitle(title, fontsize=16)
    ax1.plot(history.history['accuracy'], label='Train Acc')
    ax1.plot(history.history['val_accuracy'], label='Val Acc')
    ax1.set_title('Model Accuracy')
    ax1.set_xlabel('Epoch')
    ax1.set_ylabel('Accuracy')
    ax1.legend(loc='lower right')
    ax2.plot(history.history['loss'], label='Train Loss')
    ax2.plot(history.history['val_loss'], label='Val Loss')
    ax2.set_title('Model Loss')
    ax2.set_xlabel('Epoch')
    ax2.set_ylabel('Loss')
    ax2.legend(loc='upper right')
    plt.tight_layout(rect=[0, 0.03, 1, 0.95])
    plt.show()
HIDDEN_LAYERS = [32, 16, 8]
ACTIVATION_FN = 'sigmoid'
EPOCHS = 50
BATCH_SIZE = 8
title = f"Config: {HIDDEN_LAYERS}, Activation: {ACTIVATION_FN}"
print(f"--- RUNNING EXPERIMENT: {title} ---")
model = build_model(
    hidden_layers=HIDDEN_LAYERS,
    activation_fn=ACTIVATION_FN,
    input_shape=(X_train.shape[1],),
    output_shape=y_onehot.shape[1]
)
history = model.fit(
    X_train, y_train,
    epochs=EPOCHS,
    batch_size=BATCH_SIZE,
    validation_data=(X_val, y_val),
    verbose=0
)
val_loss, val_acc = model.evaluate(X_val, y_val, verbose=0)
print(f"Final Validation Accuracy: {val_acc*100:.2f}%")
print(f"Final Validation Loss: {val_loss:.4f}")
plot_history(history, title)



================================================================================
FILE: exp3(SGD).py
================================================================================
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Input
from tensorflow.keras.optimizers import SGD, Adam
from tensorflow.keras.utils import to_categorical
print("Loading and preparing CIFAR-10 dataset...")
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train = X_train.astype('float32') / 255.0
X_test = X_test.astype('float32') / 255.0
y_train_cat = to_categorical(y_train, 10)
y_test_cat = to_categorical(y_test, 10)
INPUT_SHAPE = X_train.shape[1:]
print(f"Input shape: {INPUT_SHAPE}")
print(f"Train samples: {X_train.shape[0]}")
print(f"Test samples: {X_test.shape[0]}")
def build_cnn(input_shape):
    model = Sequential()
    model.add(Input(shape=input_shape))
    model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
    model.add(MaxPooling2D((2, 2)))
    model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
    model.add(MaxPooling2D((2, 2)))
    model.add(Flatten())
    model.add(Dense(64, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(10, activation='softmax'))
    return model
EPOCHS = 5 
BATCH_SIZE = 64
print("\n--- Training with SGD (Momentum) ---")
model_sgd = build_cnn(INPUT_SHAPE)
optimizer_sgd = SGD(learning_rate=0.01, momentum=0.9)
model_sgd.compile(
    optimizer=optimizer_sgd,
    loss='categorical_crossentropy',
    metrics=['accuracy']
)
history_sgd = model_sgd.fit(
    X_train, y_train_cat,
    epochs=EPOCHS,
    batch_size=BATCH_SIZE,
    validation_data=(X_test, y_test_cat),
    verbose=1
)
print("\n--- Training with Adam ---")
model_adam = build_cnn(INPUT_SHAPE)
optimizer_adam = Adam(learning_rate=0.001)
model_adam.compile(
    optimizer=optimizer_adam,
    loss='categorical_crossentropy',
    metrics=['accuracy']
)
history_adam = model_adam.fit(
    X_train, y_train_cat,
    epochs=EPOCHS,
    batch_size=BATCH_SIZE,
    validation_data=(X_test, y_test_cat),
    verbose=1
)
print("Training complete!")
print("\nGenerating comparison plots...")
epoch_range = np.arange(1, EPOCHS + 1)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 7))
fig.suptitle("Optimizer Comparison: SGD (Momentum) vs. Adam", fontsize=16)
ax1.plot(epoch_range, history_sgd.history['loss'], label='SGD Train Loss', color='C0', linestyle='-')
ax1.plot(epoch_range, history_sgd.history['val_loss'], label='SGD Test Loss', color='C1', linestyle='-')
ax1.plot(epoch_range, history_adam.history['loss'], label='Adam Train Loss', color='C2', linestyle='-')
ax1.plot(epoch_range, history_adam.history['val_loss'], label='Adam Test Loss', color='C3', linestyle='-')
ax1.set_title('Loss over Epochs')
ax1.set_xlabel('Epoch')
ax1.set_ylabel('Loss')
ax1.legend()
ax1.set_xticks(epoch_range[::1])
ax2.plot(epoch_range, history_sgd.history['accuracy'], label='SGD Train Acc', color='C0', linestyle='-')
ax2.plot(epoch_range, history_sgd.history['val_accuracy'], label='SGD Test Acc', color='C1', linestyle='-')
ax2.plot(epoch_range, history_adam.history['accuracy'], label='Adam Train Acc', color='C2', linestyle='-')
ax2.plot(epoch_range, history_adam.history['val_accuracy'], label='Adam Test Acc', color='C3', linestyle='-')
ax2.set_title('Accuracy over Epochs')
ax2.set_xlabel('Epoch')
ax2.set_ylabel('Accuracy')
ax2.legend()
ax2.set_xticks(epoch_range[::1])
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()

================================================================================
FILE: exp4(CNN).py
================================================================================
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
EPOCHS = 5
BATCH_SIZE = 64
LEARNING_RATE = 0.001
classes = ('plane', 'car', 'bird', 'cat', 'deer',
           'dog', 'frog', 'horse', 'ship', 'truck')
print("Loading and preparing CIFAR-10 data...")
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=BATCH_SIZE,
                                          shuffle=True, num_workers=2)
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=BATCH_SIZE,
                                         shuffle=False, num_workers=2)
print("Data loaded.")
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, padding=1)
        self.relu1 = nn.ReLU()
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, padding=1)
        self.relu2 = nn.ReLU()
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(in_features=32 * 8 * 8, out_features=128)
        self.relu3 = nn.ReLU()
        self.fc2 = nn.Linear(in_features=128, out_features=10)
    def forward(self, x):
        x = self.conv1(x)
        x = self.relu1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.relu2(x)
        x = self.pool2(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu3(x)
        x = self.fc2(x)
        return x
model = SimpleCNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
print("Model, Loss, and Optimizer initialized.")
print(model)
print("\n--- Starting Training ---")
for epoch in range(EPOCHS):
    running_loss = 0.0
    for i, data in enumerate(train_loader, 0):
        inputs, labels = data
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
        if i % 200 == 199:
            print(f'[Epoch {epoch + 1}, Batch {i + 1:5d}] loss: {running_loss / 200:.3f}')
            running_loss = 0.0
print("--- Finished Training ---")
print("\n--- Evaluating Model ---")
correct = 0
total = 0
model.eval()
with torch.no_grad():
    for data in test_loader:
        images, labels = data
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
accuracy = 100 * correct / total
print(f'Accuracy of the network on the 10000 test images: {accuracy:.2f} %')
print("\n--- Visualizing Filters from 'conv1' ---")
def normalize_filter(f):
    f_min, f_max = f.min(), f.max()
    return (f - f_min) / (f_max - f_min)
filters = model.conv1.weight.data.clone()
filters = filters.cpu().numpy()
num_filters_to_plot = 8
fig, axes = plt.subplots(1, num_filters_to_plot, figsize=(15, 2))
fig.suptitle("First 8 Filters of 'conv1' Layer", fontsize=16)
for i, ax in enumerate(axes):
    if i >= filters.shape[0]:
        break
    filt = filters[i, :, :, :]
    filt = filt.transpose(1, 2, 0)
    filt = normalize_filter(filt)
    ax.imshow(filt)
    ax.set_title(f"Filter {i+1}")
    ax.axis('off')
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()


================================================================================
FILE: exp5(VGG).py
================================================================================
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, random_split
from torchvision import models, transforms, datasets
import matplotlib.pyplot as plt
import numpy as np
import os
import shutil
import time
DATA_DIR = './data/dogs-vs-cats/'
TRAIN_DIR = os.path.join(DATA_DIR, 'train')
SUBSET_DIR = os.path.join(DATA_DIR, 'subset')
SUBSET_SIZE_PER_CLASS = 1000
EPOCHS = 5
BATCH_SIZE = 32
MODEL_LIST = ['vgg', 'resnet', 'googlenet']
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
def create_data_subset(source_dir, dest_dir, num_per_class):
    print(f"Checking/Creating data subset at {dest_dir}...")
    if os.path.exists(dest_dir):
        shutil.rmtree(dest_dir)
    for class_name in ['cat', 'dog']:
        os.makedirs(os.path.join(dest_dir, 'all', class_name), exist_ok=True)
        source_class_dir = os.path.join(source_dir, class_name)
        if not os.path.exists(source_class_dir):
            print(f"ERROR: Source directory not found: {source_class_dir}")
            print("Please download Kaggle 'Dogs vs. Cats' data and unzip.")
            return False
        dest_class_dir = os.path.join(dest_dir, 'all', class_name)
        filenames = os.listdir(source_class_dir)
        np.random.shuffle(filenames)
        for i, filename in enumerate(filenames):
            if i >= num_per_class:
                break
            shutil.copy(
                os.path.join(source_class_dir, filename),
                os.path.join(dest_class_dir, filename)
            )
    print(f"Subset created with {num_per_class*2} total images.")
    return True
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}
def get_dataloaders(subset_data_dir, batch_size):
    all_data = datasets.ImageFolder(os.path.join(subset_data_dir, 'all'))
    train_size = int(0.8 * len(all_data))
    val_size = len(all_data) - train_size
    train_dataset, val_dataset = random_split(all_data, [train_size, val_size])
    train_dataset.dataset.transform = data_transforms['train']
    val_dataset.dataset.transform = data_transforms['val']
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)
    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=2)
    print(f"Data loaded: {len(train_dataset)} train, {len(val_dataset)} val images.")
    return train_loader, val_loader
def get_model(model_name, num_classes=2):
    model = None
    params_to_update = []
    if model_name == 'vgg':
        model = models.vgg16(weights=models.VGG16_Weights.DEFAULT)
        for param in model.parameters(): param.requires_grad = False
        num_ftrs = model.classifier[6].in_features
        model.classifier[6] = nn.Linear(num_ftrs, num_classes)
        params_to_update = model.classifier[6].parameters()
    elif model_name == 'resnet':
        model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
        for param in model.parameters(): param.requires_grad = False
        num_ftrs = model.fc.in_features
        model.fc = nn.Linear(num_ftrs, num_classes)
        params_to_update = model.fc.parameters()
    elif model_name == 'googlenet':
        model = models.googlenet(weights=models.GoogLeNet_Weights.DEFAULT)
        for param in model.parameters(): param.requires_grad = False
        num_ftrs = model.fc.in_features
        model.fc = nn.Linear(num_ftrs, num_classes)
        params_to_update = list(model.fc.parameters())
        if model.aux1:
            model.aux1.fc2 = nn.Linear(model.aux1.fc2.in_features, num_classes)
            params_to_update.extend(list(model.aux1.fc2.parameters()))
        if model.aux2:
            model.aux2.fc2 = nn.Linear(model.aux2.fc2.in_features, num_classes)
            params_to_update.extend(list(model.aux2.fc2.parameters()))
    else:
        raise ValueError("Invalid model name")
    return model, params_to_update
def train_model(model_name, model, dataloaders, criterion, optimizer, num_epochs):
    print(f"\n--- Training {model_name.upper()} ---")
    start_time = time.time()
    history = {'val_acc': []}
    for epoch in range(num_epochs):
        print(f'Epoch {epoch+1}/{num_epochs}', end=' | ')
        model.train()
        for inputs, labels in dataloaders['train']:
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            if model_name == 'googlenet':
                outputs, aux1, aux2 = model(inputs)
                loss1 = criterion(outputs, labels)
                loss2 = criterion(aux1, labels)
                loss3 = criterion(aux2, labels)
                loss = loss1 + 0.3 * loss2 + 0.3 * loss3
            else:
                outputs = model(inputs)
                loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
        model.eval()
        running_corrects = 0
        with torch.no_grad():
            for inputs, labels in dataloaders['val']:
                inputs, labels = inputs.to(device), labels.to(device)
                outputs = model(inputs)
                _, preds = torch.max(outputs, 1)
                running_corrects += torch.sum(preds == labels.data)
        epoch_acc = running_corrects.double() / len(dataloaders['val'].dataset)
        print(f'Val Acc: {epoch_acc:.4f}')
        history['val_acc'].append(epoch_acc.item())
    time_elapsed = time.time() - start_time
    print(f'Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')
    return history
if not create_data_subset(TRAIN_DIR, SUBSET_DIR, SUBSET_SIZE_PER_CLASS):
    exit()
train_loader, val_loader = get_dataloaders(SUBSET_DIR, BATCH_SIZE)
dataloaders_dict = {'train': train_loader, 'val': val_loader}
all_histories = {}
criterion = nn.CrossEntropyLoss()
for model_name in MODEL_LIST:
    model, params_to_update = get_model(model_name)
    model = model.to(device)
    optimizer = optim.Adam(params_to_update, lr=0.001)
    history = train_model(
        model_name, model, dataloaders_dict, 
        criterion, optimizer, num_epochs=EPOCHS
    )
    all_histories[model_name] = history
print("\n--- Generating Comparison Plot ---")
fig, ax = plt.subplots(1, 1, figsize=(10, 7))
epoch_range = np.arange(1, EPOCHS + 1)
ax.set_title(f'Validation Accuracy Comparison on Dogs vs. Cats (Subset)')
for model_name, history in all_histories.items():
    val_acc = np.array(history['val_acc'])
    ax.plot(epoch_range, val_acc * 100, 'o-', label=f'{model_name.upper()} Val Acc')
ax.set_xlabel('Epoch')
ax.set_ylabel('Accuracy (%)')
ax.legend()
ax.set_xticks(epoch_range)
ax.set_ylim(bottom=max(70, ax.get_ylim()[0]))
plt.tight_layout()
plt.show()



================================================================================
FILE: exp6(BIRNN).py
================================================================================
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
LOOKBACK_SEQ_LENGTH = 5
EPOCHS = 200
LEARNING_RATE = 0.001
HIDDEN_SIZE = 50
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
def load_and_preprocess_data(url, seq_length):
    print("Loading and preprocessing data...")
    try:
        df = pd.read_csv(url)
        data = df['Passengers'].values.astype(float)
    except Exception as e:
        print(f"Error loading data: {e}")
        print("Please check your internet connection or the URL.")
        return None, None, None, None, None
    scaler = MinMaxScaler(feature_range=(-1, 1))
    data_normalized = scaler.fit_transform(data.reshape(-1, 1))
    X, y = [], []
    for i in range(len(data_normalized) - seq_length):
        X.append(data_normalized[i:i + seq_length])
        y.append(data_normalized[i + 1:i + seq_length + 1])
    X, y = [], []
    for i in range(len(data_normalized) - seq_length):
        seq = data_normalized[i : i + seq_length]
        label = data_normalized[i + seq_length : i + seq_length + 1]
        X.append(seq)
        y.append(label)
    X = np.array(X)
    y = np.array(y)
    train_size = int(len(X) * 0.70)
    train_X, test_X = X[:train_size], X[train_size:]
    train_y, test_y = y[:train_size], y[train_size:]
    train_X = torch.tensor(train_X, dtype=torch.float32)
    train_y = torch.tensor(train_y, dtype=torch.float32)
    test_X = torch.tensor(test_X, dtype=torch.float32)
    test_y = torch.tensor(test_y, dtype=torch.float32)
    print(f"Data ready: {train_X.shape[0]} train samples, {test_X.shape[0]} test samples")
    return train_X, train_y, test_X, test_y, scaler
class FFNNModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(FFNNModel, self).__init__()
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, output_size)
    def forward(self, x):
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x
class BiRNNModel(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super(BiRNNModel, 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, bidirectional=True)
        self.fc = nn.Linear(hidden_size * 2, output_size)
    def forward(self, x):
        output, _ = self.lstm(x)
        last_step_output = output[:, -1, :]
        out = self.fc(last_step_output)
        return out
def train_model(model_name, model, train_X, train_y):
    print(f"\n--- Training {model_name} ---")
    model.to(device)
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
    train_data = TensorDataset(train_X, train_y)
    train_loader = DataLoader(train_data, batch_size=16, shuffle=True)
    for epoch in range(EPOCHS):
        model.train()
        for X_batch, y_batch in train_loader:
            X_batch, y_batch = X_batch.to(device), y_batch.to(device)
            y_pred = model(X_batch)
            loss = criterion(y_pred, y_batch)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        if (epoch + 1) % 50 == 0:
            print(f'Epoch [{epoch+1}/{EPOCHS}], Loss: {loss.item():.4f}')
    print(f"--- {model_name} Training Finished ---")
    return model
data_url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv'
train_X, train_y, test_X, test_y, scaler = load_and_preprocess_data(data_url, LOOKBACK_SEQ_LENGTH)
if train_X is None:
    print("Exiting due to data loading error.")
    exit()
ffnn_model = FFNNModel(input_size=LOOKBACK_SEQ_LENGTH, hidden_size=HIDDEN_SIZE, output_size=1)
ffnn_model = train_model("FFNN", ffnn_model, train_X, train_y)
birnn_model = BiRNNModel(input_size=1, hidden_size=HIDDEN_SIZE, num_layers=2, output_size=1)
birnn_model = train_model("BiRNN", birnn_model, train_X, train_y)
print("\n--- Evaluating and Plotting Results ---")
def get_predictions(model, test_X):
    model.eval()
    with torch.no_grad():
        test_X_device = test_X.to(device)
        preds = model(test_X_device)
        return preds.cpu().numpy()
ffnn_preds_scaled = get_predictions(ffnn_model, test_X)
birnn_preds_scaled = get_predictions(birnn_model, test_X)
ffnn_preds = scaler.inverse_transform(ffnn_preds_scaled)
birnn_preds = scaler.inverse_transform(birnn_preds_scaled)
actuals = scaler.inverse_transform(test_y.numpy())
ffnn_rmse = np.sqrt(mean_squared_error(actuals, ffnn_preds))
birnn_rmse = np.sqrt(mean_squared_error(actuals, birnn_preds))
print(f"FFNN Model RMSE: {ffnn_rmse:.2f}")
print(f"BiRNN Model RMSE: {birnn_rmse:.2f}")
plt.figure(figsize=(14, 7))
plt.title('BiRNN vs Feedforward NN on Airline Passenger Data')
plt.plot(actuals, label='Actual', color='C0')
plt.plot(birnn_preds, label='BiRNN Prediction', color='C1')
plt.plot(ffnn_preds, label='FFNN Prediction', color='C2')
plt.xlabel('Time Step')
plt.ylabel('Passengers')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()


================================================================================
FILE: exp7(RNN_CNN).py
================================================================================
import torch
from transformers import VisionEncoderDecoderModel, ViTImageProcessor, AutoTokenizer
from PIL import Image
from transformers.utils import hub

hub.HUGGINGFACE_HUB_HTTP_TIMEOUT=60
# Set extended download timeout

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

model = VisionEncoderDecoderModel.from_pretrained("nlpconnect/vit-gpt2-image-captioning")
processor = ViTImageProcessor.from_pretrained("nlpconnect/vit-gpt2-image-captioning")
tokenizer = AutoTokenizer.from_pretrained("nlpconnect/vit-gpt2-image-captioning")

model.to(device)
model.eval()

def generate_caption(image_path, max_length=30, num_beams=4):
    image = Image.open(image_path).convert("RGB")
    pixel_values = processor(images=image, return_tensors="pt").pixel_values.to(device)
    output_ids = model.generate(pixel_values,
                                max_length=max_length,
                                num_beams=num_beams,
                                early_stopping=True)
    caption = tokenizer.decode(output_ids[0], skip_special_tokens=True)
    return caption

if __name__ == "__main__":
    img_path = "download.jpeg"  # Replace with any image file
    print("Caption:", generate_caption(img_path))

================================================================================
FILE: exp8(VAE).py
================================================================================
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from torchvision.utils import save_image, make_grid
import matplotlib.pyplot as plt
import os
IMAGE_SIZE = 64
IMAGE_CHANNELS = 3
LATENT_DIM = 100
BATCH_SIZE = 128
EPOCHS = 5
LEARNING_RATE = 1e-3
KLD_WEIGHT = 0.00025
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
sample_dir = 'vae_samples'
os.makedirs(sample_dir, exist_ok=True)
print("Loading CelebA dataset...")
transform = transforms.Compose([
    transforms.Resize(IMAGE_SIZE),
    transforms.CenterCrop(IMAGE_SIZE),
    transforms.ToTensor(),
])
try:
    dataset = torchvision.datasets.CelebA(
        root='./data',
        split='all',
        target_type='attr',
        transform=transform,
        download=True
    )
except RuntimeError as e:
    print(f"Error downloading dataset: {e}")
    print("Please check your internet connection or try manually downloading the dataset.")
    exit()
dataloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)
print("Dataset loaded!")
class VAE(nn.Module):
    def __init__(self):
        super(VAE, self).__init__()
        self.enc_conv1 = nn.Conv2d(IMAGE_CHANNELS, 32, kernel_size=4, stride=2, padding=1)
        self.enc_conv2 = nn.Conv2d(32, 64, kernel_size=4, stride=2, padding=1)
        self.enc_conv3 = nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1)
        self.enc_conv4 = nn.Conv2d(128, 256, kernel_size=4, stride=2, padding=1)
        self.enc_fc = nn.Linear(256 * 4 * 4, 1024)
        self.fc_mu = nn.Linear(1024, LATENT_DIM)
        self.fc_log_var = nn.Linear(1024, LATENT_DIM)
        self.dec_fc = nn.Linear(LATENT_DIM, 256 * 4 * 4)
        self.dec_conv1 = nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1)
        self.dec_conv2 = nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1)
        self.dec_conv3 = nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1)
        self.dec_conv4 = nn.ConvTranspose2d(32, IMAGE_CHANNELS, kernel_size=4, stride=2, padding=1)
        self.relu = nn.ReLU()
        self.sigmoid = nn.Sigmoid()
    def encode(self, x):
        h = self.relu(self.enc_conv1(x))
        h = self.relu(self.enc_conv2(h))
        h = self.relu(self.enc_conv3(h))
        h = self.relu(self.enc_conv4(h))
        h = h.view(-1, 256 * 4 * 4)
        h = self.relu(self.enc_fc(h))
        return self.fc_mu(h), self.fc_log_var(h)
    def reparameterize(self, mu, log_var):
        std = torch.exp(0.5 * log_var)
        eps = torch.randn_like(std)
        return mu + eps * std
    def decode(self, z):
        h = self.relu(self.dec_fc(z))
        h = h.view(-1, 256, 4, 4)
        h = self.relu(self.dec_conv1(h))
        h = self.relu(self.dec_conv2(h))
        h = self.relu(self.dec_conv3(h))
        return self.sigmoid(self.dec_conv4(h))
    def forward(self, x):
        mu, log_var = self.encode(x)
        z = self.reparameterize(mu, log_var)
        x_reconstructed = self.decode(z)
        return x_reconstructed, mu, log_var
def vae_loss_function(x_reconstructed, x, mu, log_var):
    BCE = nn.functional.binary_cross_entropy(x_reconstructed.view(-1, IMAGE_SIZE*IMAGE_SIZE*IMAGE_CHANNELS), 
                                             x.view(-1, IMAGE_SIZE*IMAGE_SIZE*IMAGE_CHANNELS), 
                                             reduction='sum')
    KLD = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp())
    return BCE + KLD_WEIGHT * KLD
model = VAE().to(device)
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
fixed_z = torch.randn(64, LATENT_DIM).to(device)
print("--- Starting Training ---")
for epoch in range(EPOCHS):
    model.train()
    total_loss = 0
    for i, (images, _) in enumerate(dataloader):
        images = images.to(device)
        x_reconstructed, mu, log_var = model(images)
        loss = vae_loss_function(x_reconstructed, images, mu, log_var)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
        if (i + 1) % 100 == 0:
            print(f'Epoch [{epoch+1}/{EPOCHS}], Step [{i+1}/{len(dataloader)}], Loss: {loss.item()/images.size(0):.4f}')
    print(f'====> Epoch: {epoch+1} Average Loss: {total_loss / len(dataset):.4f} ====>')
    model.eval()
    with torch.no_grad():
        generated_images = model.decode(fixed_z)
        grid = make_grid(generated_images, nrow=8, padding=2, normalize=True)
        save_path = os.path.join(sample_dir, f'generated_images-epoch_{epoch+1}.png')
        save_image(grid, save_path)
        print(f'Saved generated images to {save_path}')
print("--- Training Finished ---")
print("Displaying images from the final epoch...")
try:
    final_image_path = os.path.join(sample_dir, f'generated_images-epoch_{EPOCHS}.png')
    img = plt.imread(final_image_path)
    plt.figure(figsize=(8, 8))
    plt.imshow(img)
    plt.title(f'Generated Images - Epoch {EPOCHS}')
    plt.axis('off')
    plt.show()
except FileNotFoundError:
    print(f"Could not find final image at {final_image_path}")


================================================================================
FILE: exp9(LSTM).py
================================================================================
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
import requests
import time
import os
DATA_URL = "https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt"
DATA_FILE = "tinyshakespeare.txt"
SEQ_LENGTH = 100
HIDDEN_SIZE = 512
EMBED_SIZE = 128
NUM_LAYERS = 2
EPOCHS = 3
BATCH_SIZE = 64
LEARNING_RATE = 0.002
CLIP_VALUE = 5
GENERATE_LENGTH = 500
SEED_TEXT = "shall i compare thee to a summer's day?"
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Using device: {device}")
print("Loading data...")
if not os.path.exists(DATA_FILE):
    try:
        response = requests.get(DATA_URL)
        response.raise_for_status()
        with open(DATA_FILE, 'w', encoding='utf-8') as f:
            f.write(response.text)
        print("Downloaded tinyshakespeare.txt")
    except requests.exceptions.RequestException as e:
        print(f"Error downloading data: {e}")
        exit()
with open(DATA_FILE, 'r', encoding='utf-8') as f:
    text = f.read()
chars = tuple(set(text))
vocab_size = len(chars)
int2char = dict(enumerate(chars))
char2int = {ch: ii for ii, ch in int2char.items()}
encoded = np.array([char2int[ch] for ch in text])
print(f"Data loaded. Corpus length: {len(text)}, Vocab size: {vocab_size}")
def get_batches(arr, batch_size, seq_length):
    chars_per_batch = batch_size * seq_length
    num_batches = len(arr) // chars_per_batch
    arr = arr[:num_batches * chars_per_batch]
    arr = arr.reshape((batch_size, -1))
    for n in range(0, arr.shape[1], seq_length):
        x = arr[:, n:n + seq_length]
        y = np.zeros_like(x)
        try:
            y[:, :-1] = x[:, 1:]
            y[:, -1] = arr[:, n + seq_length]
        except IndexError:
            y[:, :-1] = x[:, 1:]
            y[:, -1] = arr[:, 0]
        yield x, y
class CharLSTM(nn.Module):
    def __init__(self, vocab_size, embed_size, hidden_size, num_layers, dropout=0.5):
        super().__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.embed = nn.Embedding(vocab_size, embed_size)
        self.lstm = nn.LSTM(embed_size, hidden_size, num_layers, 
                            dropout=dropout, batch_first=True)
        self.dropout = nn.Dropout(dropout)
        self.fc = nn.Linear(hidden_size, vocab_size)
    def forward(self, x, hidden):
        embeds = self.embed(x)
        lstm_out, hidden = self.lstm(embeds, hidden)
        lstm_out = lstm_out.contiguous().view(-1, self.hidden_size)
        out = self.dropout(lstm_out)
        out = self.fc(out)
        return out, hidden
    def init_hidden(self, batch_size):
        return (torch.zeros(self.num_layers, batch_size, self.hidden_size).to(device),
                torch.zeros(self.num_layers, batch_size, self.hidden_size).to(device))
model = CharLSTM(vocab_size, EMBED_SIZE, HIDDEN_SIZE, NUM_LAYERS).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
print("--- Starting Training ---")
model.train()
for epoch in range(EPOCHS):
    start_time = time.time()
    h = model.init_hidden(BATCH_SIZE)
    for i, (x, y) in enumerate(get_batches(encoded, BATCH_SIZE, SEQ_LENGTH)):
        x = torch.from_numpy(x).long().to(device)
        y = torch.from_numpy(y).long().to(device)
        h = tuple([each.data for each in h])
        optimizer.zero_grad()
        output, h = model(x, h)
        loss = criterion(output, y.view(-1))
        loss.backward()
        nn.utils.clip_grad_norm_(model.parameters(), CLIP_VALUE)
        optimizer.step()
        if i % 100 == 0:
            print(f'Epoch: {epoch+1}/{EPOCHS}, Step: {i}, Loss: {loss.item():.4f}')
    print(f'Epoch {epoch+1} finished in {time.time() - start_time:.2f}s')
print("--- Training Finished ---")
def generate(model, seed_text, length=500, top_k=5):
    model.eval()
    chars = list(seed_text)
    h = model.init_hidden(1)
    for char in seed_text:
        char_tensor = torch.tensor([[char2int[char]]]).long().to(device)
        output, h = model(char_tensor, h)
    last_char = chars[-1]
    for _ in range(length):
        char_tensor = torch.tensor([[char2int[last_char]]]).long().to(device)
        output, h = model(char_tensor, h)
        p = F.softmax(output, dim=1).data.cpu().numpy().flatten()
        if top_k is not None:
            p_top, top_indices = torch.topk(torch.from_numpy(p), k=top_k)
            p_top = p_top.numpy()
            top_indices = top_indices.numpy()
            p = p_top / np.sum(p_top)
            char_idx = np.random.choice(top_indices, p=p)
        else:
            char_idx = np.random.choice(vocab_size, p=p)
        char = int2char[char_idx]
        chars.append(char)
        last_char = char
    return "".join(chars)
print("\n--- Generating Text ---")
generated_text = generate(model, SEED_TEXT, length=GENERATE_LENGTH)
print("Output:\n")
print(f"Generated Text:\n{generated_text}")


================================================================================
FILE: exp10(TLN).py
================================================================================
import tensorflow as tf
from tensorflow.keras import layers, models, losses, optimizers
import numpy as np
import matplotlib.pyplot as plt
import os
IMG_WIDTH = 32
IMG_HEIGHT = 32
IMG_CHANNELS = 3
IMG_SHAPE = (IMG_WIDTH, IMG_HEIGHT, IMG_CHANNELS)
LATENT_DIM = 100
EPOCHS = 10
BATCH_SIZE = 128
BUFFER_SIZE = 60000
print("Loading CIFAR-10 data...")
(train_images, _), (_, _) = tf.keras.datasets.cifar10.load_data()
train_images = (train_images.astype('float32') - 127.5) / 127.5
train_dataset = tf.data.Dataset.from_tensor_slices(train_images)\
    .shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
print("Data loaded and preprocessed.")
def build_generator():
    model = models.Sequential(name="Generator")
    model.add(layers.Dense(4 * 4 * 256, use_bias=False, input_shape=(LATENT_DIM,)))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    model.add(layers.Reshape((4, 4, 256)))
    model.add(layers.Conv2DTranspose(128, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    model.add(layers.Conv2DTranspose(3, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
    return model
def build_discriminator():
    model = models.Sequential(name="Discriminator")
    model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=IMG_SHAPE))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))
    model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))
    model.add(layers.Conv2D(256, (5, 5), strides=(2, 2), padding='same'))
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3))
    model.add(layers.Flatten())
    model.add(layers.Dense(1, activation='sigmoid'))
    return model
cross_entropy = losses.BinaryCrossentropy()
def discriminator_loss(real_output, fake_output):
    real_loss = cross_entropy(tf.ones_like(real_output), real_output)
    fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
    total_loss = real_loss + fake_loss
    return total_loss
def generator_loss(fake_output):
    return cross_entropy(tf.ones_like(fake_output), fake_output)
generator_optimizer = optimizers.Adam(learning_rate=1e-4)
discriminator_optimizer = optimizers.Adam(learning_rate=1e-4)
generator = build_generator()
discriminator = build_discriminator()
@tf.function
def train_step(images):
    noise = tf.random.normal([BATCH_SIZE, LATENT_DIM])
    with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
        generated_images = generator(noise, training=True)
        real_output = discriminator(images, training=True)
        fake_output = discriminator(generated_images, training=True)
        gen_loss = generator_loss(fake_output)
        disc_loss = discriminator_loss(real_output, fake_output)
    grads_gen = gen_tape.gradient(gen_loss, generator.trainable_variables)
    grads_disc = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
    generator_optimizer.apply_gradients(zip(grads_gen, generator.trainable_variables))
    discriminator_optimizer.apply_gradients(zip(grads_disc, discriminator.trainable_variables))
num_examples_to_generate = 16
seed = tf.random.normal([num_examples_to_generate, LATENT_DIM])
os.makedirs("gan_images", exist_ok=True)
def generate_and_save_images(model, epoch, test_input):
    predictions = model(test_input, training=False)
    fig = plt.figure(figsize=(4, 4))
    for i in range(predictions.shape[0]):
        plt.subplot(4, 4, i + 1)
        img = (predictions[i, :, :, :] * 0.5) + 0.5
        plt.imshow(img)
        plt.axis('off')
    plt.suptitle(f'Generated Images - Epoch {epoch}')
    plt.savefig(f'gan_images/epoch_{epoch:04d}.png')
    if epoch in [1, 5, EPOCHS]:
        plt.show()
    else:
        plt.close(fig)
print("--- Starting Training ---")
for epoch in range(EPOCHS):
    print(f"Epoch {epoch+1}/{EPOCHS}")
    for i, image_batch in enumerate(train_dataset):
        train_step(image_batch)
        if (i+1) % 100 == 0:
            print(f"  Batch {i+1}/{len(train_dataset)}")
    generate_and_save_images(generator, epoch + 1, seed)
print("--- Training Finished ---")
final_fig = plt.figure(figsize=(4, 4))
plt.suptitle(f'Generated Images - Final (Epoch {EPOCHS})')
for i in range(16):
    plt.subplot(4, 4, i + 1)
    img = (generator(seed, training=False)[i, :, :, :] * 0.5) + 0.5
    plt.imshow(img)
    plt.axis('off')
plt.savefig('gan_images/final.png')
plt.show()
"""
print("hello, World!")

