【计算机视觉-作业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个数字
相关推荐
LONGZETECH2 小时前
低空经济背景下:五组核心数据拆解无人机职业教育的机遇与实训破局
大数据·人工智能·系统架构·无人机
aichitang20242 小时前
希尔伯特空间中的正交性
人工智能·学习·机器学习·ai·泛函分析
aiqianji2 小时前
功能全面的教AI短篇小说写作的软件都有哪些?
人工智能·python
格林威2 小时前
C# 相机Burst模式图像采集:使用相机内存配合OpenCvSharp和Halcon实现短时间的高速采集的方法
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
轮到我狗叫了2 小时前
Slurm如何使用
人工智能·python·深度学习·机器学习
AI创界者2 小时前
【开源硬核】comfyUI MiniMax H3 融合模型本地部署一键整合包!单卡撬动 2K 影音全模态输出,附避坑指南
人工智能·aigc
2601_963749102 小时前
越华环保集团|河湖工况下数字化污水治理云边协同采集架构实现
人工智能·架构
闪学it3 小时前
小滴课堂-AI大模型小龙虾-OpenClaw-0基础从入门到实战
人工智能
代码方舟3 小时前
零信任架构实战:基于天远手机携号转网V即时版构建自动化营销风控网关
人工智能·智能手机·架构·自动化
会周易的程序员3 小时前
企业私有 AI 算力服务器架构设计:异构四节点 + QUIC 微服务
运维·服务器·c++·人工智能·微服务·架构