xbe.at

Deep learning / KAN and frameworks

Comparing KAN and MLP Neural Networks with Python

13 min read, 15 sections

Contents
  1. Introduction to KAN and MLP
  2. Multi-Layer Perceptron (MLP) Architecture
  3. Knowledge Augmented Network (KAN) Architecture
  4. Key Differences Between MLP and KAN
  5. Knowledge Representation in KAN
  6. Training Process Comparison
  7. Performance Comparison
  8. Real-Life Example: Image Classification with External Knowledge
  9. Advantages of KAN over MLP
  10. Challenges and Limitations of KAN
  11. Real-Life Example: Natural Language Processing with KAN
  12. Choosing Between MLP and KAN
  13. Future Directions and Research
  14. Conclusion
  15. Additional Resources

Introduction to KAN and MLP

KAN (Knowledge Augmented Network) and MLP (Multi-Layer Perceptron) are both neural network architectures used in machine learning. While MLPs have been around for decades, KANs are a more recent development aimed at incorporating external knowledge into neural networks. This presentation will compare these two approaches, highlighting their strengths and differences.

import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(MLP, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )

    def forward(self, x):
        return self.layers(x)

class KAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(KAN, self).__init__()
        self.mlp = MLP(input_size, hidden_size, hidden_size)
        self.knowledge_layer = nn.Linear(knowledge_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge):
        mlp_output = self.mlp(x)
        knowledge_output = self.knowledge_layer(knowledge)
        combined = torch.cat((mlp_output, knowledge_output), dim=1)
        return self.output_layer(combined)

# Example usage
mlp = MLP(10, 20, 5)
kan = KAN(10, 20, 5, 15)

input_data = torch.randn(1, 10)
knowledge_data = torch.randn(1, 15)

mlp_output = mlp(input_data)
kan_output = kan(input_data, knowledge_data)

print("MLP output shape:", mlp_output.shape)
print("KAN output shape:", kan_output.shape)

Multi-Layer Perceptron (MLP) Architecture

An MLP is a feedforward artificial neural network that consists of at least three layers of nodes: an input layer, one or more hidden layers, and an output layer. Each node in one layer is connected to every node in the following layer, forming a fully connected network. MLPs use backpropagation for training and can learn non-linear relationships in data.

import torch
import torch.nn as nn
import torch.optim as optim

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(MLP, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )

    def forward(self, x):
        return self.layers(x)

# Create a simple dataset
X = torch.randn(100, 10)
y = torch.randn(100, 1)

# Initialize the model, loss function, and optimizer
model = MLP(10, 20, 1)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# Training loop
for epoch in range(100):
    optimizer.zero_grad()
    outputs = model(X)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')

# Final loss
print(f'Final loss: {loss.item():.4f}')

Knowledge Augmented Network (KAN) Architecture

KAN is an extension of traditional neural networks that incorporates external knowledge into the learning process. It combines the power of neural networks with symbolic knowledge representation, allowing the model to leverage domain-specific information. KANs typically consist of a neural network component and a knowledge integration component.

import torch
import torch.nn as nn
import torch.optim as optim

class KAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(KAN, self).__init__()
        self.neural_network = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU()
        )
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge):
        nn_output = self.neural_network(x)
        knowledge_output = self.knowledge_integration(knowledge)
        combined = torch.cat((nn_output, knowledge_output), dim=1)
        return self.output_layer(combined)

# Create a simple dataset with knowledge
X = torch.randn(100, 10)
knowledge = torch.randn(100, 5)  # Simulated knowledge
y = torch.randn(100, 1)

# Initialize the model, loss function, and optimizer
model = KAN(10, 20, 1, 5)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

# Training loop
for epoch in range(100):
    optimizer.zero_grad()
    outputs = model(X, knowledge)
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 10 == 0:
        print(f'Epoch [{epoch+1}/100], Loss: {loss.item():.4f}')

# Final loss
print(f'Final loss: {loss.item():.4f}')

Key Differences Between MLP and KAN

The main difference between MLPs and KANs lies in their ability to incorporate external knowledge. MLPs rely solely on the patterns learned from the training data, while KANs can leverage additional domain-specific information. This allows KANs to potentially achieve better performance in tasks where external knowledge is crucial.

import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(MLP, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )

    def forward(self, x):
        return self.layers(x)

class KAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(KAN, self).__init__()
        self.neural_network = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU()
        )
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge):
        nn_output = self.neural_network(x)
        knowledge_output = self.knowledge_integration(knowledge)
        combined = torch.cat((nn_output, knowledge_output), dim=1)
        return self.output_layer(combined)

# Example usage
input_data = torch.randn(1, 10)
knowledge_data = torch.randn(1, 5)

mlp = MLP(10, 20, 1)
kan = KAN(10, 20, 1, 5)

mlp_output = mlp(input_data)
kan_output = kan(input_data, knowledge_data)

print("MLP output:", mlp_output.item())
print("KAN output:", kan_output.item())

Knowledge Representation in KAN

In KANs, knowledge is typically represented as a vector or embedding that encodes domain-specific information. This knowledge can be derived from various sources such as expert systems, knowledge graphs, or pre-trained models. The knowledge integration component of the KAN learns to combine this external knowledge with the neural network's learned representations.

import torch
import torch.nn as nn

class KnowledgeEmbedding(nn.Module):
    def __init__(self, vocab_size, embedding_dim):
        super(KnowledgeEmbedding, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim)

    def forward(self, x):
        return self.embedding(x)

class KAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, vocab_size, embedding_dim):
        super(KAN, self).__init__()
        self.neural_network = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU()
        )
        self.knowledge_embedding = KnowledgeEmbedding(vocab_size, embedding_dim)
        self.knowledge_integration = nn.Linear(embedding_dim, hidden_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge_ids):
        nn_output = self.neural_network(x)
        knowledge_emb = self.knowledge_embedding(knowledge_ids)
        knowledge_output = self.knowledge_integration(knowledge_emb)
        combined = torch.cat((nn_output, knowledge_output), dim=1)
        return self.output_layer(combined)

# Example usage
input_data = torch.randn(1, 10)
knowledge_ids = torch.tensor([[1, 2, 3]])  # Simulated knowledge IDs

kan = KAN(10, 20, 1, vocab_size=100, embedding_dim=5)
output = kan(input_data, knowledge_ids)

print("KAN output:", output.item())

Training Process Comparison

The training process for MLPs and KANs differs in how they handle input data. MLPs are trained on a single input dataset, while KANs require both the main input data and the corresponding knowledge representation. This dual-input nature of KANs allows them to learn how to effectively integrate external knowledge with the neural network's learned features.

import torch
import torch.nn as nn
import torch.optim as optim

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(MLP, self).__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )

    def forward(self, x):
        return self.layers(x)

class KAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(KAN, self).__init__()
        self.neural_network = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU()
        )
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge):
        nn_output = self.neural_network(x)
        knowledge_output = self.knowledge_integration(knowledge)
        combined = torch.cat((nn_output, knowledge_output), dim=1)
        return self.output_layer(combined)

# Training function
def train_model(model, X, y, knowledge=None, epochs=100):
    criterion = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=0.01)
    
    for epoch in range(epochs):
        optimizer.zero_grad()
        if knowledge is not None:
            outputs = model(X, knowledge)
        else:
            outputs = model(X)
        loss = criterion(outputs, y)
        loss.backward()
        optimizer.step()
        
        if (epoch + 1) % 10 == 0:
            print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

# Create datasets
X = torch.randn(100, 10)
y = torch.randn(100, 1)
knowledge = torch.randn(100, 5)  # Only for KAN

# Train MLP
mlp = MLP(10, 20, 1)
train_model(mlp, X, y)

# Train KAN
kan = KAN(10, 20, 1, 5)
train_model(kan, X, y, knowledge)

Performance Comparison

Comparing the performance of MLPs and KANs depends on the specific task and the availability of relevant external knowledge. In tasks where domain-specific information is crucial, KANs often outperform MLPs due to their ability to leverage this additional knowledge. However, in tasks where external knowledge is not particularly relevant or available, MLPs may perform equally well or better.

import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import numpy as np

# Define MLP and KAN classes (as in previous slides)

# Generate synthetic data
np.random.seed(42)
X = np.random.randn(1000, 10)
knowledge = np.random.randn(1000, 5)
y = np.sin(X[:, 0]) + np.cos(X[:, 1]) + 0.1 * knowledge[:, 0] + np.random.randn(1000) * 0.1

# Split data
X_train, X_test, k_train, k_test, y_train, y_test = train_test_split(X, knowledge, y, test_size=0.2, random_state=42)

# Convert to PyTorch tensors
X_train, X_test = map(torch.FloatTensor, (X_train, X_test))
k_train, k_test = map(torch.FloatTensor, (k_train, k_test))
y_train, y_test = map(lambda x: torch.FloatTensor(x.reshape(-1, 1)), (y_train, y_test))

# Initialize models
mlp = MLP(10, 20, 1)
kan = KAN(10, 20, 1, 5)

# Train models
train_model(mlp, X_train, y_train, epochs=200)
train_model(kan, X_train, y_train, k_train, epochs=200)

# Evaluate models
mlp.eval()
kan.eval()
with torch.no_grad():
    mlp_preds = mlp(X_test)
    kan_preds = kan(X_test, k_test)

mlp_mse = mean_squared_error(y_test, mlp_preds.numpy())
kan_mse = mean_squared_error(y_test, kan_preds.numpy())

print(f"MLP MSE: {mlp_mse:.4f}")
print(f"KAN MSE: {kan_mse:.4f}")

Real-Life Example: Image Classification with External Knowledge

Consider an image classification task for animal species identification. An MLP would classify based solely on image features, while a KAN could incorporate taxonomic knowledge to improve accuracy, especially for similar-looking species.

import torch
import torch.nn as nn
import torchvision.models as models

class AnimalClassifierMLP(nn.Module):
    def __init__(self, num_classes):
        super(AnimalClassifierMLP, self).__init__()
        self.features = models.resnet18(pretrained=True)
        self.features.fc = nn.Linear(512, num_classes)

    def forward(self, x):
        return self.features(x)

class AnimalClassifierKAN(nn.Module):
    def __init__(self, num_classes, taxonomy_size):
        super(AnimalClassifierKAN, self).__init__()
        self.features = models.resnet18(pretrained=True)
        self.features.fc = nn.Identity()
        self.taxonomy_embedding = nn.Embedding(taxonomy_size, 256)
        self.classifier = nn.Linear(512 + 256, num_classes)

    def forward(self, x, taxonomy_id):
        img_features = self.features(x)
        tax_features = self.taxonomy_embedding(taxonomy_id)
        combined = torch.cat((img_features, tax_features), dim=1)
        return self.classifier(combined)

# Example usage
mlp = AnimalClassifierMLP(num_classes=100)
kan = AnimalClassifierKAN(num_classes=100, taxonomy_size=1000)

# Assume we have an image tensor and taxonomy ID
image = torch.randn(1, 3, 224, 224)
taxonomy_id = torch.tensor([42])

mlp_output = mlp(image)
kan_output = kan(image, taxonomy_id)

print("MLP output shape:", mlp_output.shape)
print("KAN output shape:", kan_output.shape)

Advantages of KAN over MLP

KANs offer several advantages over traditional MLPs:

  1. Incorporation of domain knowledge: KANs can leverage external information, potentially improving performance on domain-specific tasks.
  2. Improved generalization: By using structured knowledge, KANs may generalize better to unseen data.
  3. Interpretability: The knowledge integration component can provide insights into how the model uses external information.
  4. Flexibility: KANs can be adapted to various types of knowledge representations.
import torch
import torch.nn as nn

class InterpretableKAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(InterpretableKAN, self).__init__()
        self.neural_network = nn.Linear(input_size, hidden_size)
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge):
        nn_output = self.neural_network(x)
        knowledge_output = self.knowledge_integration(knowledge)
        combined = torch.cat((nn_output, knowledge_output), dim=1)
        return self.output_layer(combined), nn_output, knowledge_output

# Example usage
model = InterpretableKAN(10, 20, 5, 15)
input_data = torch.randn(1, 10)
knowledge_data = torch.randn(1, 15)

output, nn_contribution, knowledge_contribution = model(input_data, knowledge_data)

print("Neural network contribution:", nn_contribution)
print("Knowledge contribution:", knowledge_contribution)
print("Final output:", output)

Challenges and Limitations of KAN

While KANs offer advantages, they also face challenges:

  1. Knowledge representation: Designing effective representations for complex knowledge can be difficult.
  2. Knowledge integration: Balancing the influence of learned features and external knowledge is crucial.
  3. Increased complexity: KANs are generally more complex than MLPs, potentially requiring more computational resources.
  4. Knowledge quality: The performance of KANs depends on the quality and relevance of the incorporated knowledge.
import torch
import torch.nn as nn

class KANWithBalancing(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(KANWithBalancing, self).__init__()
        self.neural_network = nn.Linear(input_size, hidden_size)
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.balancing_factor = nn.Parameter(torch.tensor(0.5))
        self.output_layer = nn.Linear(hidden_size, output_size)

    def forward(self, x, knowledge):
        nn_output = self.neural_network(x)
        knowledge_output = self.knowledge_integration(knowledge)
        balanced = self.balancing_factor * nn_output + (1 - self.balancing_factor) * knowledge_output
        return self.output_layer(balanced)

# Example usage
model = KANWithBalancing(10, 20, 5, 15)
input_data = torch.randn(1, 10)
knowledge_data = torch.randn(1, 15)

output = model(input_data, knowledge_data)
print("Balancing factor:", model.balancing_factor.item())
print("Output:", output)

Real-Life Example: Natural Language Processing with KAN

In natural language processing, a KAN can be used for tasks like sentiment analysis or text classification, incorporating linguistic knowledge or domain-specific terminologies.

import torch
import torch.nn as nn

class TextClassifierKAN(nn.Module):
    def __init__(self, vocab_size, embedding_dim, hidden_size, num_classes, knowledge_size):
        super(TextClassifierKAN, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_size, batch_first=True)
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.classifier = nn.Linear(hidden_size * 2, num_classes)

    def forward(self, text, knowledge):
        embedded = self.embedding(text)
        lstm_out, _ = self.lstm(embedded)
        text_features = lstm_out[:, -1, :]  # Take the last hidden state
        knowledge_features = self.knowledge_integration(knowledge)
        combined = torch.cat((text_features, knowledge_features), dim=1)
        return self.classifier(combined)

# Example usage
vocab_size = 10000
embedding_dim = 100
hidden_size = 128
num_classes = 5
knowledge_size = 50

model = TextClassifierKAN(vocab_size, embedding_dim, hidden_size, num_classes, knowledge_size)

# Simulate input data
text = torch.randint(0, vocab_size, (1, 20))  # Batch size 1, sequence length 20
knowledge = torch.randn(1, knowledge_size)

output = model(text, knowledge)
print("Output shape:", output.shape)

Choosing Between MLP and KAN

The decision to use an MLP or a KAN depends on several factors:

  1. Availability of relevant external knowledge
  2. Complexity of the task
  3. Interpretability requirements
  4. Computational resources
  5. Performance on the specific problem
def choose_model(task_complexity, knowledge_availability, interpretability_needed, computational_resources):
    score_mlp = 0
    score_kan = 0
    
    if task_complexity == "high":
        score_kan += 1
    else:
        score_mlp += 1
    
    if knowledge_availability == "high":
        score_kan += 2
    else:
        score_mlp += 1
    
    if interpretability_needed:
        score_kan += 1
    
    if computational_resources == "limited":
        score_mlp += 1
    else:
        score_kan += 1
    
    return "KAN" if score_kan > score_mlp else "MLP"

# Example usage
result = choose_model(
    task_complexity="high",
    knowledge_availability="high",
    interpretability_needed=True,
    computational_resources="abundant"
)

print(f"Recommended model: {result}")

Future Directions and Research

The field of KANs is still evolving, with several promising research directions:

  1. Dynamic knowledge integration: Developing methods to update and refine knowledge representations during training.
  2. Multi-modal knowledge: Incorporating diverse types of knowledge (text, images, graphs) into a single model.
  3. Scalability: Improving the efficiency of KANs for large-scale applications.
  4. Transfer learning: Exploring how knowledge learned in one domain can be transferred to another.
import torch
import torch.nn as nn

class DynamicKAN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(DynamicKAN, self).__init__()
        self.neural_network = nn.Linear(input_size, hidden_size)
        self.knowledge_integration = nn.Linear(knowledge_size, hidden_size)
        self.knowledge_update = nn.Linear(hidden_size * 2, knowledge_size)
        self.output_layer = nn.Linear(hidden_size * 2, output_size)

    def forward(self, x, knowledge):
        nn_output = self.neural_network(x)
        knowledge_output = self.knowledge_integration(knowledge)
        combined = torch.cat((nn_output, knowledge_output), dim=1)
        updated_knowledge = self.knowledge_update(combined)
        output = self.output_layer(combined)
        return output, updated_knowledge

# Example usage
model = DynamicKAN(10, 20, 5, 15)
input_data = torch.randn(1, 10)
initial_knowledge = torch.randn(1, 15)

output, updated_knowledge = model(input_data, initial_knowledge)
print("Output:", output)
print("Updated knowledge shape:", updated_knowledge.shape)

Conclusion

KANs and MLPs each have their strengths and are suitable for different scenarios. MLPs are simpler and work well for many tasks, while KANs excel in situations where domain-specific knowledge can significantly improve performance. As research in this field progresses, we can expect to see more sophisticated methods for integrating knowledge into neural networks, potentially leading to more powerful and interpretable AI systems.

import torch
import torch.nn as nn

class HybridModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, knowledge_size):
        super(HybridModel, self).__init__()
        self.mlp = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )
        self.kan = nn.Sequential(
            nn.Linear(input_size + knowledge_size, hidden_size),
            nn.ReLU(),
            nn.Linear(hidden_size, output_size)
        )
        self.decision = nn.Linear(output_size * 2, output_size)

    def forward(self, x, knowledge=None):
        mlp_output = self.mlp(x)
        if knowledge is not None:
            kan_input = torch.cat((x, knowledge), dim=1)
            kan_output = self.kan(kan_input)
            combined = torch.cat((mlp_output, kan_output), dim=1)
            return self.decision(combined)
        else:
            return mlp_output

# Example usage
model = HybridModel(10, 20, 5, 15)
input_data = torch.randn(1, 10)
knowledge_data = torch.randn(1, 15)

output_with_knowledge = model(input_data, knowledge_data)
output_without_knowledge = model(input_data)

print("Output with knowledge:", output_with_knowledge)
print("Output without knowledge:", output_without_knowledge)

Additional Resources

For those interested in diving deeper into the comparison between KANs and MLPs, as well as the broader field of neural networks and knowledge integration, the following resources are recommended:

  1. "Neural-Symbolic Learning and Reasoning: A Survey and Interpretation" by Besold et al. (2017) ArXiv: https://arxiv.org/abs/1711.03902
  2. "Knowledge-Augmented Deep Learning: A Survey" by Zhang et al. (2023) ArXiv: https://arxiv.org/abs/2306.09307
  3. "A Survey of Deep Learning Techniques for Neural Machine Translation" by Stahlberg (2020) ArXiv: https://arxiv.org/abs/1912.02047

These papers provide comprehensive overviews of the field and discuss various approaches to integrating knowledge into neural networks, including KANs and related architectures.

More in KAN and frameworks

  1. Deep Learning and TensorFlow13 min

    Deep Learning is a subset of Machine Learning that uses neural networks with multiple layers to learn from data. It has revolutionized various fields...

  2. Calculus and Optimization in Neural Networks8 min

    The foundation of neural networks rests on calculus principles, particularly in optimization during training. Understanding these mathematical concepts is...

  3. Deep Learning Slideshow with TensorFlow and PyTorch11 min

    Deep Learning is a subset of machine learning that uses artificial neural networks with multiple layers to learn and make decisions. It's inspired by the...

  4. Building Simple Neural Networks with TensorFlow6 min

    TensorFlow is a powerful open-source library for machine learning and deep learning. This slideshow will guide you through the process of creating basic...

  5. Deep Learning Titans TensorFlow vs. PyTorch in Python9 min

    TensorFlow and PyTorch are two of the most popular deep learning frameworks in the world of artificial intelligence and machine learning. Both offer powerful...

  6. Building a Neural Network for MNIST Dataset10 min

    In this presentation, we'll explore the process of creating a neural network from the ground up and applying it to the MNIST dataset. We'll cover data...