【计算机视觉-作业1】从图像到向量:kNN数据预处理完整流程

文章目录

kNN需要向量才能算距离 ,所以必须把图片从 (32, 32, 3) 展平成 (3072,) 向量。每个3072维向量就是一张图片的所有像素信息排成一行。

类比:就像把一张照片从左到右、从上到下扫描一遍,变成一串数字,然后比较两串数字的相似程度。

整体流程:图像文件 → 加载成数组 → 采样减少数量 → 展平成向量 → 计算距离


一、数据流程(5步走)

第1步:原始数据是什么?

CIFAR-10数据集:5万个训练图片 + 1万个测试图片,每张图片32×32像素,RGB彩色。

存储格式 :存在pickle文件里,每个文件存1万张图片。图片被压扁成一串数字:(10000, 3072),3072 = 32×32×3(宽×高×RGB)。

第2步:加载数据(把文件读成数组)

代码cs231n/data_utils.py):

python 复制代码
def load_CIFAR_batch(filename):
    with open(filename, 'rb') as f:
        datadict = load_pickle(f)
        X = datadict['data']      # 形状 (10000, 3072) - 一串数字
        Y = datadict['labels']    # 10000个标签
        
        # 重新整理成图片格式
        X = X.reshape(10000, 3, 32, 32)      # 变成 (10000, 3, 32, 32)
        X = X.transpose(0,2,3,1)             # 变成 (10000, 32, 32, 3)
        X = X.astype("float")                # 转成浮点数
        return X, Y

代码解释

  • reshape(10000, 3, 32, 32):把3072个数字重新排列,变成3个通道、每个32×32的图片
  • transpose(0,2,3,1):调整维度顺序,从(通道,高,宽)变成(高,宽,通道),这样Matplotlib才能正确显示
  • astype("float"):把整数像素值(0-255)转成浮点数,方便计算

加载完整数据集

python 复制代码
def load_CIFAR10(ROOT):
    xs, ys = [], []
    # 加载5个训练批次
    for b in range(1,6):
        X, Y = load_CIFAR_batch(f'data_batch_{b}')
        xs.append(X)  # 每个X形状 (10000, 32, 32, 3)
        ys.append(Y)
    
    # 合并所有训练数据
    Xtr = np.concatenate(xs)  # (50000, 32, 32, 3)
    Ytr = np.concatenate(ys)  # (50000,)
    
    # 加载测试数据
    Xte, Yte = load_CIFAR_batch('test_batch')
    return Xtr, Ytr, Xte, Yte

结果

  • 训练集:5个文件合并 → (50000, 32, 32, 3)(5万张图片)
  • 测试集:1个文件 → (10000, 32, 32, 3)(1万张图片)

第3步:采样(减少数量,加快实验)

代码knn.ipynb):

python 复制代码
# 采样训练数据
num_training = 5000
mask = list(range(num_training))
X_train = X_train[mask]  # 从50000采样到5000
y_train = y_train[mask]

# 采样测试数据
num_test = 500
mask = list(range(num_test))
X_test = X_test[mask]  # 从10000采样到500
y_test = y_test[mask]

代码解释

  • list(range(5000)):生成索引[0,1,2,...,4999]
  • X_train[mask]:用索引切片,只取前5000个样本
  • 为什么采样? 数据太多算得慢,先拿一部分试试效果

结果

  • 训练集:(5000, 32, 32, 3)
  • 测试集:(500, 32, 32, 3)

第4步:展平成向量(最关键!)

代码knn.ipynb):

python 复制代码
# Reshape the image data into rows
X_train = np.reshape(X_train, (X_train.shape[0], -1))
X_test = np.reshape(X_test, (X_test.shape[0], -1))
print(X_train.shape, X_test.shape)
# 输出: (5000, 3072) (500, 3072)

代码解释

  • X_train.shape[0]:第一维大小,即5000(样本数)
  • -1:自动计算,-1 = 32×32×3 = 3072
  • reshape(5000, -1):保持5000行,把后面的维度展平成一列

第5步:计算L2距离

现在每张图片都是向量了,可以算距离了:

代码knn.ipynb):

python 复制代码
classifier = KNearestNeighbor()
classifier.train(X_train, y_train)  # 只是保存数据,不训练
dists = classifier.compute_distances_two_loops(X_test)
# dists 形状: (500, 5000)
# dists[i, j] = 第i个测试图片和第j个训练图片的距离

距离计算代码k_nearest_neighbor.py):

python 复制代码
def compute_distances_two_loops(self, X):
    num_test = X.shape[0]      # 500
    num_train = self.X_train.shape[0]  # 5000
    dists = np.zeros((num_test, num_train))
    
    for i in range(num_test):
        for j in range(num_train):
            # 计算L2距离:对应位置相减、平方、求和、开根号
            dists[i, j] = np.sqrt(np.sum((X[i] - self.X_train[j]) ** 2))
    return dists

代码解释

  • X[i]:第i个测试样本,形状(3072,)
  • self.X_train[j]:第j个训练样本,形状(3072,)
  • (X[i] - self.X_train[j]):对应位置相减,形状(3072,)
  • ** 2:每个元素平方
  • np.sum(...):求和,得到一个数
  • np.sqrt(...):开根号,得到距离

距离公式 : d = ∑ k = 1 3072 ( x k − y k ) 2 d = \sqrt{\sum_{k=1}^{3072}(x_k - y_k)^2} d=∑k=13072(xk−yk)2 ,就是两个向量对应位置差的平方和再开根号。


二、图片如何被"压扁"?用例子说明

例子1:2×2的小图片(理解原理)

假设有一张2×2像素的RGB图片,形状是 (2, 2, 3)

python 复制代码
# 原始图片 (2, 2, 3)
image = [
    # 第1行
    [[100, 150, 200],   # 像素(0,0): R=100, G=150, B=200
     [110, 160, 210]],  # 像素(0,1): R=110, G=160, B=210
    # 第2行
    [[120, 170, 220],   # 像素(1,0): R=120, G=170, B=220
     [130, 180, 230]]   # 像素(1,1): R=130, G=180, B=230
]

展平过程

python 复制代码
# 用reshape展平
vector = np.reshape(image, (-1,))
# 结果: [100, 150, 200, 110, 160, 210, 120, 170, 220, 130, 180, 230]
#        ↑像素(0,0)↑  ↑像素(0,1)↑  ↑像素(1,0)↑  ↑像素(1,1)↑

规律 :按行优先顺序,从左到右、从上到下,把每个像素的RGB值依次排列。

例子2:32×32的真实图片

一张32×32的RGB图片,形状是 (32, 32, 3)

python 复制代码
# 原始图片 (32, 32, 3)
image = [
    # 第1行(32个像素)
    [[R00, G00, B00], [R01, G01, B01], ..., [R0_31, G0_31, B0_31]],
    # 第2行(32个像素)
    [[R10, G10, B10], [R11, G11, B11], ..., [R1_31, G1_31, B1_31]],
    # ...
    # 第32行(32个像素)
    [[R31_0, G31_0, B31_0], ..., [R31_31, G31_31, B31_31]]
]

展平过程

python 复制代码
vector = np.reshape(image, (-1,))
# 结果: [R00, G00, B00, R01, G01, B01, ..., R0_31, G0_31, B0_31,
#        R10, G10, B10, R11, G11, B11, ..., R1_31, G1_31, B1_31,
#        ...
#        R31_0, G31_0, B31_0, ..., R31_31, G31_31, B31_31]

长度计算:32行 × 32列 × 3通道 = 3072个数字


三、reshape的工作原理

reshape不改变数据,只改变排列方式

python 复制代码
# 原始数据在内存中是一串连续的数字
# reshape只是告诉NumPy如何解释这串数字

# 例子:12个数字
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

# 解释成 2×2×3 的图片
image = np.reshape(data, (2, 2, 3))
# [[[1, 2, 3],    [4, 5, 6]],
#  [[7, 8, 9],    [10, 11, 12]]]

# 解释成 12 的向量(展平)
vector = np.reshape(image, (-1,))
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
# 数据没变,只是形状变了!

关键理解

  • 数据在内存中就是一串连续的数字
  • reshape只是改变如何解释这串数字
  • (32, 32, 3) 变成 (3072,),数据本身没变,只是从"三维表格"变成了"一维列表"

四、实际代码演示

python 复制代码
import numpy as np

# 创建一张2×2的RGB图片(随机值)
image = np.random.randint(0, 255, (2, 2, 3))
print("原始图片形状:", image.shape)  # (2, 2, 3)
print("原始图片:\n", image)
# [[[100 150 200]
#   [110 160 210]]
#  [[120 170 220]
#   [130 180 230]]]

# 展平
vector = np.reshape(image, (-1,))
print("\n展平后形状:", vector.shape)  # (12,)
print("展平后:", vector)
# [100 150 200 110 160 210 120 170 220 130 180 230]

# 验证:可以还原回去
image2 = np.reshape(vector, (2, 2, 3))
print("\n还原后是否相同:", np.array_equal(image, image2))  # True

32×32图片的展平过程(可视化)

复制代码
原始图片 (32, 32, 3):
┌─────────────────────────┐
│ [R G B] [R G B] ...    │ ← 第1行,32个像素
│ [R G B] [R G B] ...    │ ← 第2行,32个像素
│ ...                     │
│ [R G B] [R G B] ...    │ ← 第32行,32个像素
└─────────────────────────┘

         ↓ reshape ↓

展平向量 (3072,):
[R G B R G B ... R G B R G B ... ... R G B ... R G B]
 ↑第1行↑              ↑第2行↑          ↑第32行↑

结果:每张图片变成一个3072维的向量(一串3072个数字)。

数据形状变化表

步骤 训练集 测试集 说明
文件里 (10000, 3072) (10000, 3072) 压扁的数字
加载后 (50000, 32, 32, 3) (10000, 32, 32, 3) 图片格式
采样后 (5000, 32, 32, 3) (500, 32, 32, 3) 减少数量
展平后 (5000, 3072) (500, 3072) 向量格式
距离矩阵 - (500, 5000) 测试×训练

为什么是3072维?

  • 32 × 32 = 1024(像素数)
  • 1024 × 3 = 3072(每个像素有RGB三个通道)
  • 所以每张图片展平后是3072个数字
相关推荐
机器之心5 分钟前
Generalist之后,罗剑岚团队推出LWD,也要变革具身智能训练范式
人工智能·openai
IT_陈寒10 分钟前
Vite的public文件夹放静态资源?这坑我替你踩了
前端·人工智能·后端
传说故事10 分钟前
【论文阅读】Diffusion Forcing: Next-token Prediction Meets Full-Sequence Diffusion
论文阅读·人工智能·diffusion
xixixi7777714 分钟前
三重筑基:5G-A超级上行提速千兆,电联低频共享扫平盲点,800V HVDC算电协同破局
人工智能·5g·ai·大模型·算力·通信·信通院
jkyy201415 分钟前
AI运动数字化:以技术重塑场景,健康有益赋能全域运动健康管理
大数据·人工智能·健康医疗
金融小师妹23 分钟前
4月30日多因子共振节点:鲍威尔“收官效应”与权力结构重塑的预期重构
大数据·人工智能·重构·逻辑回归
2601_9499251828 分钟前
AI Agent如何重构跨境物流的决策?
大数据·人工智能·重构·ai agent·geo优化·物流科技
AI木马人35 分钟前
1.人工智能实战:大模型推理接口响应慢?从模型加载到 FastAPI 部署的完整优化方案
人工智能·python·fastapi
Black蜡笔小新1 小时前
私有化本地化AI模型训推工作站DLTM训推一体工作站赋能多行业智能化落地
人工智能
qq_411262421 小时前
四博 AI 智能音箱 + ESPC3 Tasmota 计量通断器方案
人工智能·智能音箱