commit 90080fd4a7c172d4affdd098ee0257f46e300b39 Author: gitea_eternal <401029566@qq.com> Date: Thu Apr 16 13:52:58 2026 +0800 添加讲义: 3-1-2 衔接与拓展 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e7ecb53 --- /dev/null +++ b/README.md @@ -0,0 +1,922 @@ +# 📚 3-1-2 衔接与拓展:从图像矩阵到文本向量 + +--- + +# 🎯 本章小结:图像处理的完整流程 + +在3-1-2中,我们学习了图像数据的本质和基本操作。让我来详细回顾一下: + +## 图像的表示:为什么图像可以表示为矩阵? + +当我们用相机拍摄一张照片时,图像是如何在计算机中存储的? + +``` + ←——— 宽度(W)———→ + ┌─────────────────────────────────┐ ┐ + │ 像素点(0,0) 像素点(0,1) ... │ │ + │ 像素点(1,0) 像素点(1,1) ... │ │ 高度(H) + │ ... ... ... │ │ + │ 像素点(H-1,0) ... ... │ │ + └─────────────────────────────────┘ ┘ +``` + +**关键点:** +- 图像由**像素点**组成,每个像素点有一个位置 (x, y) +- 每个像素点的**亮度**(灰度图)或**颜色**(彩色图)可以用数字表示 +- 因此,整张图像可以用一个**数字矩阵**来表示 + +### 灰度图像 → 二维矩阵 + +```python +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三个通道表示颜色: + +```python +# 彩色图像:高度 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中用于数值计算的核心库,专门处理数组和矩阵。我们的图像处理和文本向量化都离不开它。 + +## 安装与导入 + +```bash +pip install numpy +``` + +```python +import numpy as np +``` + +## 创建数组 + +### 从列表创建 + +```python +# 一维数组(向量) +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]] +``` + +### 快速创建特殊数组 + +```python +# 全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. ] +``` + +## 查看数组属性 + +```python +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 +``` + +## 数组索引和切片 + +### 基本索引 + +```python +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] +``` + +### 二维数组索引 + +```python +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] +``` + +## 数组运算 + +### 基础数学运算 + +```python +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] 平方 +``` + +### 矩阵乘法(重要!) + +```python +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) +``` + +### 常用统计函数 + +```python +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] +``` + +## 数组变形 + +```python +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]] +``` + +## 条件筛选 + +```python +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] +``` + +## 范数与点积 + +```python +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 +``` + +## 复制与引用(重要概念) + +```python +a = np.array([1, 2, 3]) +b = a # 这是引用,不是复制! +b[0] = 100 +print(a[0]) # 100,a也跟着变了 + +# 如果想复制,应该用 +c = a.copy() +c[0] = 999 +print(a[0]) # 100,a不变 +``` + +--- + +## 图像处理的本质:矩阵运算 + +当我们对图像进行处理时,实际上是在对矩阵做运算: + +### 1. 亮度和对比度调整 + +```python +# 亮度调整:所有像素值加一个常数 +brighter = image + 30 # 变亮 +darker = image - 30 # 变暗 + +# 对比度调整:像素值乘以一个系数 +contrasted = image * 1.5 # 对比度增强 +``` + +### 2. 裁剪和旋转(矩阵切片与变换) + +```python +# 裁剪:取矩阵的一部分 +top_left = image[0:2, 0:2] # 取左上角2x2区域 + +# 旋转:矩阵转置和翻转 +rotated = np.transpose(image) # 转置 +flipped = np.fliplr(image) # 左右翻转 +``` + +### 3. 模糊和锐化(卷积核) + +```python +# 平均模糊:用一个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] +]) +``` + +--- + +## 图像特征提取:从矩阵到向量 + +### 边缘检测 - 找出图像中的"突变" + +```python +# 简单的边缘检测:检测相邻像素的亮度变化 +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]]) + +# 应用卷积核后的结果就是边缘图 +# 边缘就是亮度发生突变的地方 +``` + +### 从特征图到特征向量 + +```python +# 假设我们提取了边缘特征,得到一个特征图 +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) 是逻辑顺序 | +| **相邻关系** | 物理上相邻的像素通常相关 | 相邻的词可能在语法上无关 | + +```python +# 图像:你可以说"pixel[10, 20]的邻居是[10,19], [10,21], [9,20], [11,20]" +# 这些邻居在空间上确实紧邻 + +# 文本:你可以说"第5个词的邻居是第4个和第6个词" +# 但这种"邻居"关系远不如图像的像素邻居那么紧密 +``` + +### 区别二:语义组织方式不同 + +**图像:** 语义通过空间结构体现 + +``` +一只猫在沙发上 +┌─────────────────────┐ +│ 猫(位置固定) │ ← 猫的空间位置+形状+颜色 = 猫的概念 +│ ████ │ +│ ██████ 沙发 │ +│ ████████████████████│ +└─────────────────────┘ +``` + +**文本:** 语义通过符号组合体现 + +``` +"一只猫坐在沙发上" +["一只", "猫", "坐", "在", "沙发", "上"] + ↓ ↓ ↓ ↓ ↓ ↓ + 数量 主体 动作 位置 地点 方向 +``` + +**关键洞察:** +- 图像中,"猫"这个概念是通过**像素的空间排列**直接体现的 +- 文本中,"猫"这个概念是通过**符号"猫"**来代表的 +- 文本必须经过"符号→含义"的映射,才能理解语义 + +### 区别三:维度特性不同 + +``` +图像的维度: + - 固定且明确:H × W × C + - 连续空间,维度代表物理尺寸 + +文本的维度: + - 不固定:可以任意长度的句子/文章 + - 离散的符号序列 + - "维度"通常指词汇表大小,而不是"长度" +``` + +### 区别四:相似性判断方式不同 + +**图像相似度:** 通常基于视觉特征 + +```python +# 两张猫的图片相似 +# → 像素级别的特征相似(颜色分布、边缘形状、纹理) +# → 在特征空间中距离近 +``` + +**文本相似度:** 基于语义内容 + +```python +# 两篇关于"猫"的新闻相似 +# → 都包含"猫"这个词 +# → 讨论的主题相关 +# → 即使词语不同,含义可能相近 +``` + +--- + +## 📝 文本数据处理的挑战 + +正因为文本和图像有这些本质区别,文本处理面临独特的挑战: + +### 挑战1:如何把符号变成数字? + +图像天然就是数字(像素值),但文本是符号: + +``` +"Python" → ??? → [?, ?, ?, ...] +``` + +### 挑战2:如何处理"一词多义"? + +``` +"苹果" 可以指: +- 一种水果(营养丰富) +- 一家公司(Apple Inc.) +- 手机品牌(苹果手机) +``` + +同一个词,不同上下文,意思完全不同。 + +### 挑战3:如何处理语法和语义? + +``` +"我爱你" vs "你爱我" +- 词语完全相同,只是顺序不同 +- 意思完全相反 +``` + +### 挑战4:如何处理省略和指代? + +``` +"小明喜欢编程。他觉得代码很有趣。" +- "他"指的是谁? → 需要理解指代关系 +- "代码"是什么? → 需要理解上下文 +``` + +--- + +## 🔑 核心:从符号到向量 + +无论挑战有多少,文本数据处理的核心目标只有一个: + +``` +文本(符号序列)→ 数值向量 → 可计算 → AI模型处理 +``` + +这就是**文本向量化**,它是文本数据处理的第一步,也是最关键的一步。 + +--- + +# 📖 3-2 预告:文本数据处理 + +## 文本向量化的简单例子 + +假设我们有一个很小的词汇表(实际中有上万个词): + +```python +# 词汇表 +vocab = ["Python", "学习", "入门", "指南", "人工智能", "数据"] + +# 两篇文章 +doc1 = "Python学习入门指南" +doc2 = "Python人工智能数据" +doc3 = "数据数据分析" +``` + +**用词频(BoW)方法把文本变成向量:** + +```python +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 表示: + +```python +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 的"图像矩阵": + +```python +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:特征向量计算 + +以下代码创建了两张小型"特征图": + +```python +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. 计算 `vector1` 和 `vector2` 的欧几里得距离 +2. 计算 `vector1` 和 `vector2` 的余弦相似度(公式:`cos = np.dot(a,b) / (np.linalg.norm(a) * np.linalg.norm(b))`) + +--- + +### 练习4:文本向量化(预习挑战) + +下面是一个简单的文本向量化示例(用词频表示文本): + +```python +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. `doc1` 和 `doc2` 的向量分别是什么? +2. 补全代码:计算 `v1` 和 `v2` 的余弦相似度 +3. 修改 `vocab`,加入词 `"机器"`,然后用 `doc3 = "机器学习"` 测试,看看向量是什么 + +--- + +### 🌟 附加题 + +运行以下代码,理解为什么相似度不是1.0: + +```python +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)` 归一化后比较 + +---