【计算机视觉-作业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个数字
相关推荐
美酒没故事°1 天前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
AI攻城狮1 天前
用 Obsidian CLI + LLM 构建本地 RAG:让你的笔记真正「活」起来
人工智能·云原生·aigc
鸿乃江边鸟1 天前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
lpfasd1231 天前
基于Cloudflare生态的应用部署与开发全解
人工智能·agent·cloudflare
俞凡1 天前
DevOps 2.0:智能体如何接管故障修复和基础设施维护
人工智能
comedate1 天前
[OpenClaw] GLM 5 关于电影 - 人工智能 - 的思考
人工智能·电影评价
财迅通Ai1 天前
6000万吨产能承压 卫星化学迎来战略窗口期
大数据·人工智能·物联网·卫星化学
liliangcsdn1 天前
Agent Memory智能体记忆系统的示例分析
数据库·人工智能·全文检索
GISer_Jing1 天前
Page-agent MCP结构
前端·人工智能