2505140092 4ed4364b14
2026-04-16 15:58:38 +08:00
2026-04-16 15:58:38 +08:00
2026-04-16 15:58:38 +08:00
2026-04-16 13:48:07 +08:00

📚 3-1-2 衔接与拓展:从图像矩阵到文本向量


🎯 本章小结:图像处理的完整流程

在3-1-2中我们学习了图像数据的本质和基本操作。让我来详细回顾一下

图像的表示:为什么图像可以表示为矩阵?

当我们用相机拍摄一张照片时,图像是如何在计算机中存储的?

                    ←——— 宽度W———→
    ┌─────────────────────────────────┐  ┐
    │ 像素点(0,0)  像素点(0,1)  ...   │  │
    │ 像素点(1,0)  像素点(1,1)  ...   │  │ 高度H
    │   ...       ...       ...      │  │
    │ 像素点(H-1,0) ...      ...     │  │
    └─────────────────────────────────┘  ┘

关键点:

  • 图像由像素点组成,每个像素点有一个位置 (x, y)
  • 每个像素点的亮度(灰度图)或颜色(彩色图)可以用数字表示
  • 因此,整张图像可以用一个数字矩阵来表示

灰度图像 → 二维矩阵

import numpy as np

# 假设一张 4x4 的灰度图像
image = np.array([
    [255, 200, 150, 100],
    [180, 140, 110, 80],
    [120, 90,  60,  40],
    [50,  30,  20,  10]
])
# 255 = 最亮0 = 最暗(黑)

图像属性:

  • 形状:(H, W) = (4, 4)
  • 每个元素0~255 的整数uint8类型
  • 本质:一个二维数组/矩阵

彩色图像 → 三维矩阵

彩色图像用RGB三个通道表示颜色

# 彩色图像:高度 x 宽度 x 3红、绿、蓝三通道
color_image = np.array([
    [[255, 0, 0],   [0, 255, 0],   [0, 0, 255]],    # 第1行像素红、绿、蓝
    [[255, 255, 0], [0, 255, 255], [255, 0, 255]],  # 第2行像素黄、青、紫
    [[128, 128, 128],[100, 100, 100],[50, 50, 50]]   # 第3行像素灰度
])

图像属性:

  • 形状:(H, W, 3) = (3, 3, 3)
  • 每个位置有3个数值分别代表R、G、B的分量
  • 本质:一个三维数组/矩阵

📘 NumPy 快速入门

NumPy是Python中用于数值计算的核心库专门处理数组和矩阵。我们的图像处理和文本向量化都离不开它。

安装与导入

pip install numpy
import numpy as np

创建数组

从列表创建

# 一维数组(向量)
vec = np.array([1, 2, 3, 4, 5])
print(vec)  # [1 2 3 4 5]

# 二维数组(矩阵)
mat = np.array([
    [1, 2, 3],
    [4, 5, 6]
])
print(mat)
# [[1 2 3]
#  [4 5 6]]

快速创建特殊数组

# 全0数组 - 常用于初始化
zeros = np.zeros((3, 4))  # 3行4列的全0矩阵
print(zeros)
# [[0. 0. 0. 0.]
#  [0. 0. 0. 0.]
#  [0. 0. 0. 0.]]

# 全1数组
ones = np.ones((2, 3))
# [[1. 1. 1.]
#  [1. 1. 1.]]

# 指定范围内的数组
arr = np.arange(0, 10, 2)  # 0到10步长2
print(arr)  # [0 2 4 6 8]

# 等差数组
lin = np.linspace(0, 1, 5)  # 0到1之间均匀取5个数
print(lin)  # [0.   0.25 0.5  0.75 1.  ]

查看数组属性

a = np.array([[1, 2, 3], [4, 5, 6]])

print(a.ndim)   # 维度数量 = 2是二维数组/矩阵)
print(a.shape)   # 形状 = (2, 3) 2行3列
print(a.size)    # 总元素数 = 6
print(a.dtype)   # 数据类型 = int64

# 对于图像数据,常用 uint8无符号8位整数0-255
image = np.array([[255, 128], [64, 0]], dtype=np.uint8)
print(image.dtype)  # uint8

数组索引和切片

基本索引

a = np.array([10, 20, 30, 40, 50])

print(a[0])   # 第一个元素 = 10
print(a[-1])  # 最后一个元素 = 50
print(a[1:3]) # 第2到第3个元素 = [20, 30]
print(a[:3])  # 前3个元素 = [10, 20, 30]
print(a[2:])  # 第3个到最后 = [30, 40, 50]

二维数组索引

mat = np.array([
    [1,  2,  3,  4],
    [5,  6,  7,  8],
    [9, 10, 11, 12]
])

# 获取特定元素
print(mat[0, 0])  # 第1行第1列 = 1
print(mat[1, 2])  # 第2行第3列 = 7
print(mat[2, -1]) # 第3行最后一列 = 12

# 切片:[行, 列]
print(mat[0, :])   # 第1行所有列 = [1, 2, 3, 4]
print(mat[:, 1])   # 所有行第2列 = [2, 6, 10]
print(mat[0:2, 0:2])  # 取前2行前2列
# [[1 2]
#  [5 6]]

# 负索引
print(mat[-1, :])  # 最后一行 = [9, 10, 11, 12]

数组运算

基础数学运算

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# 加减乘除(对应元素运算)
print(a + b)   # [5 7 9]
print(a - b)   # [-3 -3 -3]
print(a * b)   # [4 10 18]  注意:这是元素乘法,不是矩阵乘法
print(a / b)   # [0.25 0.4  0.5]

# 标量运算(广播)
print(a * 2)   # [2 4 6]
print(a + 10)  # [11 12 13]
print(a ** 2)  # [1 4 9]  平方

矩阵乘法(重要!)

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# 使用 @ 运算符 或 np.dot()
C = A @ B
print(C)
# [[19 22]
#  [41 46]]

# 或者
C = np.dot(A, B)
print(C)

常用统计函数

a = np.array([1, 2, 3, 4, 5])

print(np.sum(a))      # 求和 = 15
print(np.mean(a))     # 平均值 = 3.0
print(np.max(a))      # 最大值 = 5
print(np.min(a))      # 最小值 = 1
print(np.std(a))      # 标准差
print(np.argmax(a))   # 最大值的索引 = 4
print(np.argmin(a))   # 最小值的索引 = 0

# 对于二维数组,可以指定轴
mat = np.array([[1, 2, 3], [4, 5, 6]])
print(np.sum(mat, axis=0))  # 按列求和 = [5 7 9]
print(np.sum(mat, axis=1))  # 按行求和 = [6 15]

数组变形

a = np.array([1, 2, 3, 4, 5, 6])

# 展平flatten: 多维变一维
print(a.flatten())  # [1 2 3 4 5 6]

# 改变形状reshape
b = a.reshape(2, 3)  # 变成2行3列
print(b)
# [[1 2 3]
#  [4 5 6]]

# 转置transpose: 行变列
print(b.T)
# [[1 4]
#  [2 5]
#  [3 6]]

# 翻转
c = np.array([[1, 2, 3], [4, 5, 6]])
print(np.fliplr(c))  # 左右翻转
# [[3 2 1]
#  [6 5 4]]
print(np.flipud(c))  # 上下翻转
# [[4 5 6]
#  [1 2 3]]

条件筛选

a = np.array([10, 20, 30, 40, 50])

# 找出满足条件的元素
print(a > 25)     # [False False False True True]
print(a[a > 25])  # [30 40 50]  只保留大于25的值

# 统计满足条件的数量
print(np.sum(a > 25))  # 3

# 替换值
b = np.where(a > 25, a, 0)  # 大于25保留否则改成0
print(b)  # [0 0 30 40 50]

范数与点积

a = np.array([3, 4])

# L2范数欧几里得距离
norm = np.linalg.norm(a)
print(norm)  # 5.0  (因为 3² + 4² = 25, √25 = 5)

# 点积dot product
b = np.array([1, 2])
dot = np.dot(a, b)
print(dot)  # 3*1 + 4*2 = 11

# 余弦相似度
def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

v1 = np.array([1, 0, 1])
v2 = np.array([1, 1, 0])
print(cosine_similarity(v1, v2))  # 约 0.707

复制与引用(重要概念)

a = np.array([1, 2, 3])
b = a  # 这是引用,不是复制!
b[0] = 100
print(a[0])  # 100a也跟着变了

# 如果想复制,应该用
c = a.copy()
c[0] = 999
print(a[0])  # 100a不变

图像处理的本质:矩阵运算

当我们对图像进行处理时,实际上是在对矩阵做运算:

1. 亮度和对比度调整

# 亮度调整:所有像素值加一个常数
brighter = image + 30  # 变亮
darker = image - 30    # 变暗

# 对比度调整:像素值乘以一个系数
contrasted = image * 1.5  # 对比度增强

2. 裁剪和旋转(矩阵切片与变换)

# 裁剪:取矩阵的一部分
top_left = image[0:2, 0:2]  # 取左上角2x2区域

# 旋转:矩阵转置和翻转
rotated = np.transpose(image)  # 转置
flipped = np.fliplr(image)     # 左右翻转

3. 模糊和锐化(卷积核)

# 平均模糊用一个3x3的"均值滤波器"
blur_kernel = np.array([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9]
])
# 用这个kernel"扫过"整张图像,求局部平均值

# 锐化:增强边缘
sharpen_kernel = np.array([
    [0, -1, 0],
    [-1, 5, -1],
    [0, -1, 0]
])

图像特征提取:从矩阵到向量

边缘检测 - 找出图像中的"突变"

# 简单的边缘检测:检测相邻像素的亮度变化
sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])

# 应用卷积核后的结果就是边缘图
# 边缘就是亮度发生突变的地方

从特征图到特征向量

# 假设我们提取了边缘特征,得到一个特征图
feature_map = np.array([
    [255, 200, 0, 50],
    [180, 0, 100, 30],
    [0, 90, 150, 0],
    [50, 0, 30, 10]
])

# 把特征图"展平"成一个向量
feature_vector = feature_map.flatten()
# 结果: [255, 200, 0, 50, 180, 0, 100, 30, 0, 90, 150, 0, 50, 0, 30, 10]

# 这个向量就是这张图像的"特征表示"
# 可以用来比较两张图像是否相似

📊 图像数据的特点总结

特性 描述 示例
结构化 有固定的空间位置关系 像素(0,0)紧邻像素(0,1)
局部性 相邻像素通常相关 同一物体的像素值相近
维度固定 H×W×C维度明确 1920×1080×3
连续空间 像素间有物理意义 距离、角度、边缘
视觉直观 人眼可直接理解 看到图片就知道是什么
图像数据的本质:
   ↓
   📐 空间结构化数据
   ↓
   矩阵表示 (H × W × C)
   ↓
   空间关系很重要(邻居关系定义了边缘、纹理)


🚀 转折:如果数据不是图像呢?

我们来看一个实际的AI应用场景

案例:智能新闻推荐系统

假设你是一个新闻App的AI工程师需要做一个"相似文章推荐"功能:

用户点击了一篇文章《Python入门指南》
系统需要找到与它最相似的其他文章

问题来了:文章是文字,不是像素,怎么处理?

文字 → ??? → 向量 → 计算相似度 → 推荐结果

📖 从图像到文本:两种截然不同的数据

图像 vs 文本:数据结构对比

让我们来仔细对比一下图像和文本的本质区别:

图像数据的结构

图像:一幅画
┌─────────────────────────────────┐
│  ████████████████████████████  │
│  ████████████████████████████  │
│  ████████ 主体 ██████████████  │
│  ████████████████████████████  │
│  ████████████████████████████  │
└─────────────────────────────────┘
   
   ↓ 表示为矩阵 ↓

[
  [255, 255, 200, 180, ...],   # 第1行像素
  [255, 255, 190, 170, ...],   # 第2行像素
  [200, 190, 150, 140, ...],   # 第3行像素主体部分
  ...
]

特点:

  • 每个像素有明确的位置 (x, y)
  • 位置本身就携带信息(空间关系)
  • 连续分布,相邻像素高度相关
  • 可以用坐标直接访问:pixel = image[100, 200]

文本数据的结构

文本:一篇文章
"Python是一门广泛使用的高级编程语言"

   ↓ 表示为词语序列 ↓

["Python", "是", "一门", "广泛", "使用", "的", "高级", "编程", "语言"]

特点:

  • 由离散的符号(字、词)组成
  • 顺序重要,但位置是逻辑顺序而非空间位置
  • 词语之间通过语法和语义关联,而非物理邻近
  • 无法用坐标直接访问:word = text[3] 只是第3个词

⚠️ 文本和图像的本质区别(重点!)

这是理解文本数据处理的关键,请仔细阅读:

区别一:表示方式不同

维度 图像 文本
基本单位 像素 词语/字符
排列方式 二维空间网格 一维线性序列
位置意义 空间坐标 (x, y) 有物理含义 序列位置 (index) 是逻辑顺序
相邻关系 物理上相邻的像素通常相关 相邻的词可能在语法上无关
# 图像:你可以说"pixel[10, 20]的邻居是[10,19], [10,21], [9,20], [11,20]"
#       这些邻居在空间上确实紧邻

# 文本:你可以说"第5个词的邻居是第4个和第6个词"
#       但这种"邻居"关系远不如图像的像素邻居那么紧密

区别二:语义组织方式不同

图像: 语义通过空间结构体现

一只猫在沙发上
┌─────────────────────┐
│    猫(位置固定)     │  ← 猫的空间位置+形状+颜色 = 猫的概念
│  ████                │
│  ██████   沙发       │
│  ████████████████████│
└─────────────────────┘

文本: 语义通过符号组合体现

"一只猫坐在沙发上"
["一只", "猫", "坐", "在", "沙发", "上"]
  ↓      ↓    ↓    ↓    ↓     ↓
 数量   主体  动作  位置   地点  方向

关键洞察:

  • 图像中,"猫"这个概念是通过像素的空间排列直接体现的
  • 文本中,"猫"这个概念是通过**符号"猫"**来代表的
  • 文本必须经过"符号→含义"的映射,才能理解语义

区别三:维度特性不同

图像的维度:
   - 固定且明确H × W × C
   - 连续空间,维度代表物理尺寸
   
文本的维度:
   - 不固定:可以任意长度的句子/文章
   - 离散的符号序列
   - "维度"通常指词汇表大小,而不是"长度"

区别四:相似性判断方式不同

图像相似度: 通常基于视觉特征

# 两张猫的图片相似
# → 像素级别的特征相似(颜色分布、边缘形状、纹理)
# → 在特征空间中距离近

文本相似度: 基于语义内容

# 两篇关于"猫"的新闻相似
# → 都包含"猫"这个词
# → 讨论的主题相关
# → 即使词语不同,含义可能相近

📝 文本数据处理的挑战

正因为文本和图像有这些本质区别,文本处理面临独特的挑战:

挑战1如何把符号变成数字

图像天然就是数字(像素值),但文本是符号:

"Python" → ??? → [?, ?, ?, ...]

挑战2如何处理"一词多义"

"苹果" 可以指:
- 一种水果(营养丰富)
- 一家公司Apple Inc.
- 手机品牌(苹果手机)

同一个词,不同上下文,意思完全不同。

挑战3如何处理语法和语义

"我爱你" vs "你爱我"
- 词语完全相同,只是顺序不同
- 意思完全相反

挑战4如何处理省略和指代

"小明喜欢编程。他觉得代码很有趣。"
- "他"指的是谁? → 需要理解指代关系
- "代码"是什么? → 需要理解上下文

🔑 核心:从符号到向量

无论挑战有多少,文本数据处理的核心目标只有一个:

文本(符号序列)→ 数值向量 → 可计算 → AI模型处理

这就是文本向量化,它是文本数据处理的第一步,也是最关键的一步。


📖 3-2 预告:文本数据处理

文本向量化的简单例子

假设我们有一个很小的词汇表(实际中有上万个词):

# 词汇表
vocab = ["Python", "学习", "入门", "指南", "人工智能", "数据"]

# 两篇文章
doc1 = "Python学习入门指南"
doc2 = "Python人工智能数据"
doc3 = "数据数据分析"

用词频BoW方法把文本变成向量

import numpy as np

def text_to_vector(text, vocab):
    """把文本转换为词频向量"""
    words = text.split()
    vector = np.zeros(len(vocab))
    for i, word in enumerate(vocab):
        vector[i] = words.count(word)
    return vector

# 词汇表
vocab = ["Python", "学习", "入门", "指南", "人工智能", "数据"]

# 三篇文章的向量
v1 = text_to_vector("Python学习入门指南", vocab)
v2 = text_to_vector("Python人工智能数据", vocab)
v3 = text_to_vector("数据数据分析", vocab)

print("doc1向量:", v1)  # [1, 1, 1, 1, 0, 0]
print("doc2向量:", v2)  # [1, 0, 0, 0, 1, 1]
print("doc3向量:", v3)  # [0, 0, 0, 0, 0, 2]

# 计算相似度(余弦相似度)
def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

print("\n=== 相似度计算 ===")
print(f"doc1 vs doc2: {cosine_similarity(v1, v2):.3f}")  # 应该较低
print(f"doc1 vs doc1: {cosine_similarity(v1, v1):.3f}")  # = 1.0(自己和自己)

运行结果:

doc1向量: [1. 1. 1. 1. 0. 0.]
doc2向量: [1. 0. 0. 0. 1. 1.]
doc3向量: [0. 0. 0. 0. 0. 2.]

=== 相似度计算 ===
doc1 vs doc2: 0.200
doc1 vs doc1: 1.000

结论:

  • doc1和doc1完全相似当然
  • doc1和doc2有一点点相似都有"Python"
  • 这说明向量相似度可以反映文本的内容相似度

图像与文本的本质统一

尽管图像和文本有很大区别,但最终的表示是统一的

        ┌──────────────────────────────────────┐
        │           原始数据                    │
        │   (图像:像素 / 文本:字符/词语)       │
        └─────────────────┬────────────────────┘
                          │
                          ▼
        ┌──────────────────────────────────────┐
        │          数值化/向量化                 │
        │  图像:矩阵 (H×W×C)                   │
        │  文本:向量 (N维)                     │
        └─────────────────┬────────────────────┘
                          │
                          ▼
        ┌──────────────────────────────────────┐
        │            预处理                     │
        │  图像:调整亮度/对比度/裁剪            │
        │  文本:分词/去停用词/标准化            │
        └─────────────────┬────────────────────┘
                          │
                          ▼
        ┌──────────────────────────────────────┐
        │           特征提取                    │
        │  图像:卷积核提取边缘/纹理/形状        │
        │  文本Embedding提取语义特征           │
        └─────────────────┬────────────────────┘
                          │
                          ▼
        ┌──────────────────────────────────────┐
        │           特征向量                    │
        │        (统一的数值表示)                │
        └──────────────────────────────────────┘

核心思想:

  • 图像是"空间结构化"的数据 → 矩阵
  • 文本是"序列符号化"的数据 → 向量
  • 最终都被转化为数值向量供AI模型处理
  • 这就是深度学习的强大之处:统一的表示,统一的处理

📋 3-2 你将学到什么

知识点 内容 与图像的类比
分词 把句子切成词语 就像图像分割,把整图切成区域
停用词过滤 去掉"的、了、在"等无用词 就像图像去噪,去掉干扰信息
词性标注 标出名词、动词、形容词... 就像边缘检测,标记重要特征
实体识别 识别人名、地名、机构名 就像目标检测,定位关键元素
文本向量化 把文本变成数值向量 核心:和图像一样,最终都是向量!
相似度计算 用向量距离判断相似程度 就像特征匹配

📝 预习任务

在3-2开始之前请思考以下问题

  1. 文字如何转成数字?

    • 一段文字"Python很有趣"如何在计算机中表示?
  2. 词语如何切割?

    • 英文 "I love Python" 很好分词
    • 中文 "我喜欢Python" 怎么分?
  3. 哪些词是"噪音"

    • "的"、"了"、"在"这些词有多重要?
    • 和图像去噪类比,文本需要"去"哪些词?
  4. 文本和图像最大的区别是什么?(思考题)

    • 提示:从"结构"和"语义"两个角度思考

📝 课堂练习

学完了图像数据处理,现在来动手写代码吧!

练习1图像矩阵操作

假设有一张 3×3 的灰度图像,用 NumPy 表示:

import numpy as np

image = np.array([
    [100, 150, 200],
    [80,  120, 180],
    [60,  90,  140]
], dtype=np.uint8)

print("原图:")
print(image)

请完成以下操作(写出代码并运行):

  1. 变暗20让图像每个像素值减20
  2. 裁剪左上角:只保留 image[0:2, 0:2]2×2区域
  3. 水平翻转:使用 np.fliplr()

练习2看图写代码

以下代码定义了一个 4×4 的"图像矩阵"

import numpy as np

img = np.array([
    [255, 255, 0,   0  ],
    [255, 255, 0,   0  ],
    [0,   0,   255, 255],
    [0,   0,   255, 255]
], dtype=np.uint8)

请写出代码完成:

  1. 统计图像中白色像素255有多少个黑色像素0有多少个
  2. 将图像水平翻转后打印出来
  3. 将图像逆时针旋转90度提示使用转置和翻转

练习3特征向量计算

以下代码创建了两张小型"特征图"

import numpy as np

# 假设这是从图像中提取的2个特征图
feature_map1 = np.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]])
feature_map2 = np.array([[1, 1, 1], [1, 0, 0], [1, 0, 0]])

# 补全代码:将特征图展平为向量
vector1 = feature_map1.flatten()  # 展平
vector2 = feature_map2.flatten()  # 展平

print("vector1:", vector1)
print("vector2:", vector2)

请补全代码并运行:

  1. 计算 vector1vector2 的欧几里得距离
  2. 计算 vector1vector2 的余弦相似度(公式:cos = np.dot(a,b) / (np.linalg.norm(a) * np.linalg.norm(b))

练习4文本向量化预习挑战

下面是一个简单的文本向量化示例(用词频表示文本):

import numpy as np

# 词汇表
vocab = ["Python", "学习", "数据", "人工智能", "编程"]

# 两句话
doc1 = "Python学习编程"
doc2 = "Python人工智能数据"

def text_to_vector(text, vocab):
    words = text.split()
    vector = np.zeros(len(vocab))
    for i, word in enumerate(vocab):
        vector[i] = words.count(word)
    return vector

v1 = text_to_vector(doc1, vocab)
v2 = text_to_vector(doc2, vocab)

print("doc1向量:", v1)
print("doc2向量:", v2)

请运行代码并回答:

  1. doc1doc2 的向量分别是什么?
  2. 补全代码:计算 v1v2 的余弦相似度
  3. 修改 vocab,加入词 "机器",然后用 doc3 = "机器学习" 测试,看看向量是什么

🌟 附加题

运行以下代码理解为什么相似度不是1.0

import numpy as np

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

v1 = np.array([1, 0, 1, 0])
v2 = np.array([2, 0, 0, 0])

print("v1:", v1)
print("v2:", v2)
print("相似度:", cosine_similarity(v1, v2))

# 思考如何修改v2让相似度变成1.0
# 提示:长度要一样

选做:

  • 尝试用 v2 = v1 * 2 看看结果
  • 再试试用 v2 = v1 / np.linalg.norm(v1) * np.linalg.norm(v2) 归一化后比较

Description
No description provided
Readme 34 KiB