【计算机视觉-作业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个数字
相关推荐
一点一木7 小时前
深度体验TRAE SOLO移动端7天:作为独立开发者,我把工作流揣进了兜里
前端·人工智能·trae
Lee川8 小时前
mini-cursor 揭秘:从 Tool 定义到 Agent 循环的完整实现
前端·人工智能·后端
weelinking8 小时前
【产品】00_产品经理用Claude实现产品系列介绍
数据库·人工智能·sql·数据挖掘·github·产品经理
Agent产品评测局8 小时前
制造业模具管理AI系统,主流产品能力对比详解:2026年智能制造选型深度洞察
人工智能·ai·chatgpt·制造
研华科技Advantech9 小时前
如何用一套实训设备,打通工业AI预测性维护技术全流程?
人工智能
Lab_AI9 小时前
AI for Science: MaXFlow AI Agent+ 报告体验双升级,让AI智能体更高效易用!
人工智能·ai for science·ai agent·ai智能体
李坤9 小时前
让 Codex 和 Claude 互相 Review:告别手动复制
人工智能·openai·claude
南屹川9 小时前
【API设计】GraphQL实战:从REST到GraphQL的演进
人工智能
KJ_BioMed10 小时前
当计算生物学遇上生成式AI:从头设计生物分子的“新范式”初探
人工智能·从头设计·生命科学·生物医药·科研干货·科晶生物
明月醉窗台10 小时前
深度学习(17)YOLO训练中的超参数详解
人工智能·深度学习·yolo