【计算机视觉-作业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个数字
相关推荐
春日见2 小时前
自动驾驶规划控制决策知识点扫盲
linux·运维·服务器·人工智能·机器学习·自动驾驶
人工智能AI技术2 小时前
【Agent从入门到实践】43 接口封装:将Agent封装为API服务,供其他系统调用
人工智能·python
hjs_deeplearning2 小时前
文献阅读篇#14:自动驾驶中的基础模型:场景生成与场景分析综述(5)
人工智能·机器学习·自动驾驶
nju_spy2 小时前
离线强化学习(一)BCQ 批量限制 Q-learning
人工智能·强化学习·cvae·离线强化学习·双 q 学习·bcq·外推泛化误差
副露のmagic2 小时前
深度学习基础复健
人工智能·深度学习
番茄大王sc3 小时前
2026年科研AI工具深度测评(一):文献调研与综述生成领域,维普科创助手领跑学术严谨性
人工智能·深度学习·考研·学习方法·论文笔记
代码丰3 小时前
SpringAI+RAG向量库+知识图谱+多模型路由+Docker打造SmartHR智能招聘助手
人工智能·spring·知识图谱
独处东汉4 小时前
freertos开发空气检测仪之输入子系统结构体设计
数据结构·人工智能·stm32·单片机·嵌入式硬件·算法
乐迪信息4 小时前
乐迪信息:AI防爆摄像机在船舶监控的应用
大数据·网络·人工智能·算法·无人机