Course materials ยท Introduction to Machine Learning

[Week 13] Why Deep Learning Changed the World: High-Dimensional Image Classification ๐Ÿ“ธ

English translation

Welcome to Week 13!
So far, we have worked with data containing relatively few numbers and features,
such as Iris and Wine.

Today, we will experience the power of Deep Learning and why it changed the world.
We will work with High-Dimensional data, specifically image data.

Using the Fashion-MNIST dataset,
compare a simple linear model (Logistic Regression) with a Deep Learning model (MLP).
Today we will find out "Why is Deep Learning powerful?"

๐Ÿ–ผ๏ธ Module 1: Image Data Is Numbers

What Is the Fashion-MNIST Dataset?

How Do We Treat Images as 'Data'?

[Step 1] Prepare and Explore the Data

Besides the load_ function,
scikit-learn can load larger and more varied online datasets using fetch_openml.

# Import required libraries
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_openml # Load an OpenML dataset
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression # Linear model for comparison
from sklearn.neural_network import MLPClassifier # Deep Learning model
from sklearn.preprocessing import StandardScaler # Scaling
from sklearn.metrics import accuracy_score, classification_report

# 1. Load Fashion-MNIST data
# fetch_openml: Load a dataset by name from OpenML.org.
# name='Fashion-MNIST': Dataset name
# version=1: Dataset version
# as_frame=False: Return NumPy arrays rather than a Pandas DataFrame
# parser='auto': (Current scikit-learn recommendation)
fashion_mnist = fetch_openml('Fashion-MNIST', version=1,
                    as_frame=False, parser='auto')
X, y = fashion_mnist.data, fashion_mnist.target

print("๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ!")
# X has shape (70000, 784); y has shape (70000,)
print(f"X (์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ) Shape: {X.shape}")
print(f"y (์ •๋‹ต ๋ ˆ์ด๋ธ”) Shape: {y.shape}")

# Inspect y labels (strings '0' ~ '9')
print(f"\n๋ ˆ์ด๋ธ” ์ข…๋ฅ˜ (์›๋ณธ): {np.unique(y)}")
# Convert y from strings to integers for model training
y = y.astype(int)
print(f"๋ ˆ์ด๋ธ” ์ข…๋ฅ˜ (์ •์ˆ˜ํ˜•): {np.unique(y)}")
๋ฐ์ดํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ!
X (์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ) Shape: (70000, 784)
y (์ •๋‹ต ๋ ˆ์ด๋ธ”) Shape: (70000,)

๋ ˆ์ด๋ธ” ์ข…๋ฅ˜ (์›๋ณธ): ['0' '1' '2' '3' '4' '5' '6' '7' '8' '9']
๋ ˆ์ด๋ธ” ์ข…๋ฅ˜ (์ •์ˆ˜ํ˜•): [0 1 2 3 4 5 6 7 8 9]

Select and visualize one data point
to check whether the 784 numbers in X really form an image.

# Select the first image from X (784 numbers)
first_image_data = X[0]
# Reshape the 1-dimensional vector of 784 elements into a 28x28 2-dimensional matrix
first_image = first_image_data.reshape(28, 28)

# Define the names of the 10 classes
class_names = ["T-shirt/top", "Trouser", "Pullover",
               "Dress", "Coat", "Sandal", "Shirt",
               "Sneaker", "Bag", "Ankle boot"]

# plt.imshow() : Display a 2D array as an image
plt.figure(figsize=(6, 6))
# cmap='gray': Use the grayscale colormap
plt.imshow(first_image, cmap='gray')
plt.title(f"Label: {y[0]} ({class_names[y[0]]})")
plt.colorbar(label="Pixel Intensity")
plt.show()

print(f"X[0]์˜ Shape: {first_image_data.shape}")
print(f"first_image์˜ Shape: {first_image.shape}")
<Figure size 600x600 with 2 Axes>
์‹คํ–‰ ๊ฒฐ๊ณผ
X[0]์˜ Shape: (784,)
first_image์˜ Shape: (28, 28)

Module 2: Data Preprocessing and a Baseline Model (Logistic Regression)

[Step 2] Data Scaling and Splitting

1. Scaling

2. Split Data

# 1. Scaling (0-1 Normalization)
# Divide by the maximum pixel value, 255.0, to obtain the 0~1 range.
X_scaled = X / 255.0

# Inspect scaling results
print(f"์Šค์ผ€์ผ๋ง ์ „ (Min: {X.min()}, Max: {X.max()})")
print(f"์Šค์ผ€์ผ๋ง ํ›„ (Min: {X_scaled.min()}, Max: {X_scaled.max()})")

# 2. Split data
# Set Test size to 10000 and Train size to 60000
X_train, X_test, y_train, y_test = train_test_split(
    X_scaled, y, test_size=10000, random_state=42, stratify=y
)

print(f"\nTrain ๋ฐ์ดํ„ฐ Shape: {X_train.shape}")
print(f"Test ๋ฐ์ดํ„ฐ Shape: {X_test.shape}")
์Šค์ผ€์ผ๋ง ์ „ (Min: 0, Max: 255)
์Šค์ผ€์ผ๋ง ํ›„ (Min: 0.0, Max: 1.0)

Train ๋ฐ์ดํ„ฐ Shape: (60000, 784)
Test ๋ฐ์ดํ„ฐ Shape: (10000, 784)

[Step 3] Train the Baseline Model: Logistic Regression

To assess how good a Deep Learning model is,
you need a baseline for comparison.

Here, we will use a baseline that takes 784 pixel values as inputs and classifies them into 10 classes:
a Logistic Regression model.

# 1. Create and train a Logistic Regression model
# solver='saga'
# max_iter=30 : Set the iteration count for this large dataset (30 iterations due to time constraints)
print("๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘... (๋ช‡ ๋ถ„ ์ •๋„ ์†Œ์š”๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)")
model_lr = LogisticRegression(
    solver='saga',
    max_iter=30, # More iterations may be needed in practice
    random_state=42
)
model_lr.fit(X_train, y_train)

# 2. Evaluate performance
y_pred_lr = model_lr.predict(X_test)
accuracy_lr = accuracy_score(y_test, y_pred_lr)

print("\n--- ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ์„ฑ๋Šฅ ํ‰๊ฐ€ (Test Data) ---")
print(f"์ •ํ™•๋„: {accuracy_lr:.4f}")
print("\nClassification Report:")
print(classification_report(y_test, y_pred_lr,
                            target_names=class_names))
๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘... (๋ช‡ ๋ถ„ ์ •๋„ ์†Œ์š”๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

--- ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ์„ฑ๋Šฅ ํ‰๊ฐ€ (Test Data) ---
์ •ํ™•๋„: 0.8546

Classification Report:
              precision    recall  f1-score   support

 T-shirt/top       0.78      0.84      0.81      1000
     Trouser       0.98      0.96      0.97      1000
    Pullover       0.76      0.74      0.75      1000
       Dress       0.84      0.85      0.84      1000
        Coat       0.74      0.82      0.78      1000
      Sandal       0.95      0.94      0.95      1000
       Shirt       0.65      0.56      0.60      1000
     Sneaker       0.93      0.93      0.93      1000
         Bag       0.96      0.95      0.96      1000
  Ankle boot       0.95      0.95      0.95      1000

    accuracy                           0.85     10000
   macro avg       0.85      0.85      0.85     10000
weighted avg       0.85      0.85      0.85     10000

/usr/local/lib/python3.12/dist-packages/sklearn/linear_model/_sag.py:348: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge
  warnings.warn(

๐Ÿš€ Module 3: Classifying High-Dimensional Images with MLP

[Step 4] Train and Evaluate the Deep Learning Model (MLP)

Now train MLP on the same data.
MLP takes 784 pixels as inputs
and learns their complex combinations (patterns) in hidden layers.

# 1. Create an MLP model
# hidden_layer_sizes=(200, 100, 50, 10): 4 hidden layers with 200, 100, 50, 10 neurons
# This structure learns combinations (patterns) of the 784 pixels.
print("MLP ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘... (์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)")
model_mlp = MLPClassifier(
    hidden_layer_sizes=(200, 100, 50, 10),
    activation='relu',
    max_iter=30,
    random_state=42,
    solver='adam'
)

# 2. Train the model
model_mlp.fit(X_train, y_train)

# 3. Evaluate performance
y_pred_mlp = model_mlp.predict(X_test)
accuracy_mlp = accuracy_score(y_test, y_pred_mlp)

print("\n--- MLPClassifier ์„ฑ๋Šฅ ํ‰๊ฐ€ (Test Data) ---")
print(f"์ •ํ™•๋„: {accuracy_mlp:.4f}")
print("\nClassification Report:")
print(classification_report(y_test, y_pred_mlp,
                            target_names=class_names))
MLP ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘... (์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

--- MLPClassifier ์„ฑ๋Šฅ ํ‰๊ฐ€ (Test Data) ---
์ •ํ™•๋„: 0.8947

Classification Report:
              precision    recall  f1-score   support

 T-shirt/top       0.84      0.88      0.86      1000
     Trouser       0.99      0.97      0.98      1000
    Pullover       0.82      0.81      0.81      1000
       Dress       0.91      0.91      0.91      1000
        Coat       0.85      0.79      0.82      1000
      Sandal       0.97      0.95      0.96      1000
       Shirt       0.70      0.75      0.72      1000
     Sneaker       0.94      0.95      0.95      1000
         Bag       0.96      0.98      0.97      1000
  Ankle boot       0.96      0.96      0.96      1000

    accuracy                           0.89     10000
   macro avg       0.90      0.89      0.90     10000
weighted avg       0.90      0.89      0.90     10000

/usr/local/lib/python3.12/dist-packages/sklearn/neural_network/_multilayer_perceptron.py:691: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (30) reached and the optimization hasn't converged yet.
  warnings.warn(

[Step 5] Compare and Visualize Results

Compare the performance of a baseline model (Logistic Regression) and an MLP,
and examine what the models confuse.

# 1. Compare performance
print("--- ์ตœ์ข… ์„ฑ๋Šฅ ๋น„๊ต ---")
print(f"๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ (์„ ํ˜• ๋ชจ๋ธ) ์ •ํ™•๋„: {accuracy_lr:.4f}")
print(f"MLP (๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ) ์ •ํ™•๋„: {accuracy_mlp:.4f}")
print(f"\n์„ฑ๋Šฅ ํ–ฅ์ƒ: {accuracy_mlp - accuracy_lr:.4f}")
print("\n=> MLP๊ฐ€ ์€๋‹‰์ธต์„ ํ†ตํ•ด '์Šค์Šค๋กœ' ํ”ฝ์…€ ํŒจํ„ด์„ ํ•™์Šตํ•˜์—ฌ")
print("   ๋‹จ์ˆœ ์„ ํ˜• ๋ชจ๋ธ๋ณด๋‹ค ํ›จ์”ฌ ๋†’์€ ์„ฑ๋Šฅ์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค!")


# 2. Visualize incorrect MLP predictions
# Find indices where y_test and y_pred_mlp differ.
misclassified_indices = np.where(y_test != y_pred_mlp)[0]

# Randomly select 6 incorrect predictions
plt.figure()
for i, idx in enumerate(np.random.choice(misclassified_indices,
                                         6, replace=False)):
    plt.subplot(2, 3, i + 1)
    # Since X_test is scaled, restore the original pixel values (0~255) for visualization
    # by multiplying by 255.
    image = (X_test[idx] * 255.0).reshape(28, 28)
    plt.imshow(image, cmap='gray')

    actual_label = class_names[y_test[idx]]
    predicted_label = class_names[y_pred_mlp[idx]]

    plt.title(f"Actual: {actual_label}\nPredicted: {predicted_label}",
              fontsize=10)
    plt.axis('off')

plt.suptitle("Mistakes", fontsize=16)
plt.show()

print("\n=> ์‚ฌ๋žŒ์ด ๋ณด๊ธฐ์—๋„ ํ—ท๊ฐˆ๋ฆด ๋งŒํ•œ ์ด๋ฏธ์ง€๋“ค(e.g., Pullover/Shirt/Coat)")
--- ์ตœ์ข… ์„ฑ๋Šฅ ๋น„๊ต ---
๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ (์„ ํ˜• ๋ชจ๋ธ) ์ •ํ™•๋„: 0.8546
MLP (๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ) ์ •ํ™•๋„: 0.8947

์„ฑ๋Šฅ ํ–ฅ์ƒ: 0.0401

=> MLP๊ฐ€ ์€๋‹‰์ธต์„ ํ†ตํ•ด '์Šค์Šค๋กœ' ํ”ฝ์…€ ํŒจํ„ด์„ ํ•™์Šตํ•˜์—ฌ
   ๋‹จ์ˆœ ์„ ํ˜• ๋ชจ๋ธ๋ณด๋‹ค ํ›จ์”ฌ ๋†’์€ ์„ฑ๋Šฅ์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค!
<Figure size 640x480 with 6 Axes>
์‹คํ–‰ ๊ฒฐ๊ณผ
=> ์‚ฌ๋žŒ์ด ๋ณด๊ธฐ์—๋„ ํ—ท๊ฐˆ๋ฆด ๋งŒํ•œ ์ด๋ฏธ์ง€๋“ค(e.g., Pullover/Shirt/Coat)

๐Ÿ’ก [Supplement] "If CNN broke the winter, why does MLP work well?"

In the Week 2 history lecture, we mentioned the 'AI winter'.
(More precisely, the 'second AI winter' of the 1990s.)
We learned that 'AlexNet' (a type of CNN) ended this winter in 2012.

Why, then, did using an MLP rather than a CNN today
yield strong performance exceeding 88% (far better than the linear model)
on complex, 784-dimensional image data?


This question gets to the heart of Deep Learning.

The main technical reason MLPs failed in the 90s
was the 'Vanishing Gradient' problem.
As models became deeper,
the learning signal (Gradient) vanished toward 0 during Backpropagation,
leaving neurons in earlier layers unable to learn.

This was a mathematical limitation of the Sigmoid activation function used at the time.


AlexNet (CNN) broke this winter in 2012
not only through CNN's strong architecture (learning spatial information),
but also by using key technologies that addressed the underlying causes of the AI winter:

  1. ReLU Activation Function: Use ReLU instead of Sigmoid to address vanishing gradients.
  2. GPU parallel processing: Fast large-scale computation.
  3. Large datasets: Sufficient training data such as 'ImageNet'.

The MLP we used today is not the MLP of the '90s.
Our MLPClassifier used the same key technologies that broke the AI winter.

In other words, technical advances made it possible to train MLPs even when stacked deeply,
allowing hidden layers to learn complex patterns from 784 pixel inputs on their own.

๐Ÿง  Module 4: A Taste of CNNs, Image Specialists (PyTorch)

CNN (Convolutional Neural Network)
Although MLP performed much better than the linear model,
it is still not optimized for image classification.

Fundamental Limitations of MLPs:

The Idea behind CNNs (Convolutional Neural Networks):

Why CNNs are powerful:

  1. Preserve Spatial Information: Using the 2D structure directly lets it learn neighboring relationships between pixels.
  2. Parameter Sharing: A 'single' filter scans the 'entire' image.
    • Learn a filter for the 'heel' feature just once,
      and the same filter can detect it on either side of the image.
    • This dramatically reduces the number of parameters to learn compared with an MLP,
      enabling much more efficient and powerful learning.

A CNN stacks multiple (Convolution + pooling) layers,
learning simple lines/shapes first and then combining them into
more complex, abstract features (e.g., eyes, noses, wheels).
This is how AlexNet ended the AI winter in 2012.

[Reference] CNN Implementation Example Using PyTorch

scikit-learn does not support CNNs.
A dedicated Deep Learning library such as PyTorch or TensorFlow is required.

Below is an example implementation of a simple CNN using PyTorch.
(It may take several minutes or longer to run.)

You do not need to fully understand this code yet.
The goal is to understand how much its results (accuracy) differ from MLP's.
Pay particular attention to the structure of class SimpleCNN(nn.Module):.

# Import PyTorch
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

# 1. Reshape data for PyTorch Tensors
# Convert NumPy arrays to PyTorch tensors
X_train_tensor = torch.Tensor(X_train)
y_train_tensor = torch.LongTensor(y_train) # Classification targets use LongTensor
X_test_tensor = torch.Tensor(X_test)
y_test_tensor = torch.LongTensor(y_test)

# CNN requires 4D inputs: (sample count, channel count, height, width).
# (60000, 784) -> (60000, 1, 28, 28) (1 is the grayscale channel)
X_train_cnn = X_train_tensor.reshape(-1, 1, 28, 28)
X_test_cnn = X_test_tensor.reshape(-1, 1, 28, 28)

# DataLoader: Utility for grouping data into minibatches
train_dataset = TensorDataset(X_train_cnn, y_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_dataset = TensorDataset(X_test_cnn, y_test_tensor)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)


# 2. Define the CNN model (PyTorch nn.Module)
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        # 1. Convolution Layer: Takes 1 grayscale channel as input,
        # and scans the 28x28 image with 16 'scanners'
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=16,
                               kernel_size=3, stride=1, padding=1)
        self.relu = nn.ReLU()
        # 2. MaxPooling Layer: Extracts the strongest feature in each 2x2 region,
        # reducing image size (28x28 -> 14x14)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
        # 3. Flatten Layer: Convert 2D feature maps -> 1D vector (for MLP input)
        self.flatten = nn.Flatten()
        # 4. Dense Layer (MLP): Compress a vector of size 16*14*14 into 128 neurons
        self.fc1 = nn.Linear(16 * 14 * 14, 128)
        # 5. Output Layer (MLP): Classify 128 neurons into 10 classes (0~9)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        # Define the data flow
        x = self.pool(self.relu(self.conv1(x))) # Conv -> ReLU -> Pool
        x = self.flatten(x)                     # Flatten
        x = self.relu(self.fc1(x))            # Dense -> ReLU
        x = self.fc2(x)                         # Output
        return x

model_cnn = SimpleCNN()
criterion = nn.CrossEntropyLoss() # Cost function for multiclass classification
optimizer = optim.Adam(model_cnn.parameters(), lr=0.001) # Optimization algorithm

# 3. Train the model (only 10 Epochs due to time constraints)
print("\nPyTorch CNN ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘...")
model_cnn.train() # Set the model to training mode
for epoch in range(10): # Train over the entire dataset 10 times
    for inputs, labels in train_loader:
        optimizer.zero_grad() # Reset gradients
        outputs = model_cnn(inputs) # Forward pass
        loss = criterion(outputs, labels) # Calculate error
        loss.backward() # Backpropagation
        optimizer.step() # Update weights
    print(f"Epoch {epoch+1}/10, Loss: {loss.item():.4f}")

# 4. Evaluate the model
model_cnn.eval() # Set the model to evaluation mode
correct = 0
total = 0
with torch.no_grad(): # Disable gradient computation
    for inputs, labels in test_loader:
        outputs = model_cnn(inputs)
        _, predicted = torch.max(outputs.data, 1) # Select the class with the highest probability
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy_cnn = correct / total
print(f"\n--- CNN ์„ฑ๋Šฅ ํ‰๊ฐ€ (Test Data) ---")
print(f"์ •ํ™•๋„: {accuracy_cnn:.4f}")

# 5. Compare final performance
print("\n--- ์ตœ์ข… ์„ฑ๋Šฅ ๋น„๊ต ---")
print(f"๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ (์„ ํ˜• ๋ชจ๋ธ) ์ •ํ™•๋„: {accuracy_lr:.4f}")
print(f"MLP (๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ) ์ •ํ™•๋„: {accuracy_mlp:.4f}")
print(f"CNN (๋”ฅ๋Ÿฌ๋‹ ์ „๋ฌธ๊ฐ€) ์ •ํ™•๋„: {accuracy_cnn:.4f}")
print("\n=> CNN์ด ์ด๋ฏธ์ง€์˜ '๊ณต๊ฐ„ ์ •๋ณด'์™€ 'ํŒจํ„ด'์„ ํ›จ์”ฌ ํšจ์œจ์ ์œผ๋กœ ํ•™์Šตํ•˜์—ฌ")
print("   MLP๋ณด๋‹ค ๋” ๋†’์€ ์„ฑ๋Šฅ์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค!")
PyTorch CNN ๋ชจ๋ธ ํ•™์Šต ์‹œ์ž‘...
Epoch 1/10, Loss: 0.3730
Epoch 2/10, Loss: 0.2957
Epoch 3/10, Loss: 0.3789
Epoch 4/10, Loss: 0.3020
Epoch 5/10, Loss: 0.2208
Epoch 6/10, Loss: 0.0739
Epoch 7/10, Loss: 0.0374
Epoch 8/10, Loss: 0.0779
Epoch 9/10, Loss: 0.1845
Epoch 10/10, Loss: 0.0245

--- CNN ์„ฑ๋Šฅ ํ‰๊ฐ€ (Test Data) ---
์ •ํ™•๋„: 0.9189

--- ์ตœ์ข… ์„ฑ๋Šฅ ๋น„๊ต ---
๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ (์„ ํ˜• ๋ชจ๋ธ) ์ •ํ™•๋„: 0.8546
MLP (๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ) ์ •ํ™•๋„: 0.8947
CNN (๋”ฅ๋Ÿฌ๋‹ ์ „๋ฌธ๊ฐ€) ์ •ํ™•๋„: 0.9189

=> CNN์ด ์ด๋ฏธ์ง€์˜ '๊ณต๊ฐ„ ์ •๋ณด'์™€ 'ํŒจํ„ด'์„ ํ›จ์”ฌ ํšจ์œจ์ ์œผ๋กœ ํ•™์Šตํ•˜์—ฌ
   MLP๋ณด๋‹ค ๋” ๋†’์€ ์„ฑ๋Šฅ์„ ๋‹ฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค!

โœจ Wrap-up

Today, we worked with high-dimensional, 784-dimensional image data.

This is why Deep Learning, especially CNNs, ended the AI winter and transformed image and speech recognition.

Next time, we will undertake a final project that brings this entire process together.