更新3-2-1文本数据处理导论(v2版本)
This commit is contained in:
756
README.md
756
README.md
@@ -6,100 +6,414 @@
|
|||||||
|
|
||||||
| 目标 | 内容 |
|
| 目标 | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **理解** | 为什么文本不能直接用于计算机计算 |
|
| **理解** | 什么是文本数据,以及计算机如何读取文本 |
|
||||||
|
| **掌握** | 为什么文本不能直接用于计算机计算 |
|
||||||
|
| **理解** | 向量、相似度的基本概念(零基础讲解) |
|
||||||
| **掌握** | 文本向量化的核心思想与数学原理 |
|
| **掌握** | 文本向量化的核心思想与数学原理 |
|
||||||
| **了解** | 从 BoW → TF-IDF → Embedding 的演进逻辑 |
|
| **了解** | 从 BoW → TF-IDF → Embedding 的演进逻辑 |
|
||||||
| **认识** | 文本处理的完整流程框架 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 📖 第一部分:为什么文本处理这么难?
|
# 📖 第一部分:什么是文本数据?
|
||||||
|
|
||||||
## 1.1 计算机的"语言障碍"
|
## 1.1 文本数据的定义
|
||||||
|
|
||||||
**计算机擅长什么?**
|
**文本数据**是由文字、符号组成的序列信息,是人类语言在计算机中的表示形式。
|
||||||
|
|
||||||
|
### 文本数据的例子
|
||||||
|
|
||||||
```
|
```
|
||||||
数字计算: 1 + 2 = 3 ✅
|
一句话:"今天天气真好"
|
||||||
图像处理: 像素矩阵运算 ✅
|
一篇文章:一篇新闻报道
|
||||||
逻辑判断: if A > B then ... ✅
|
一条评论:"这家餐厅的菜太好吃了!"
|
||||||
|
一段对话:"你好,请问这本书多少钱?"
|
||||||
|
一首诗:"床前明月光,疑是地上霜"
|
||||||
```
|
```
|
||||||
|
|
||||||
**计算机不擅长什么?**
|
### 文本数据的特征
|
||||||
|
|
||||||
|
| 特征 | 说明 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| **离散符号** | 由离散的字符/词组成 | "hello" 由 h,e,l,l,o 这5个字符组成 |
|
||||||
|
| **序列性** | 符号按特定顺序排列 | "我爱你" ≠ "你爱我" |
|
||||||
|
| **语义丰富** | 同样的词不同场景意思不同 | "苹果"可以是水果或手机品牌 |
|
||||||
|
| **上下文相关** | 词的意思依赖上下文 | "他打了猫,猫跑了" 中两个"猫"意思相同 |
|
||||||
|
|
||||||
|
## 1.2 计算机如何"读取"文本?
|
||||||
|
|
||||||
|
### 对比:图像 vs 文本
|
||||||
|
|
||||||
|
**图像数据的读取:**
|
||||||
|
|
||||||
```
|
```
|
||||||
文本理解:
|
图像文件(.jpg/.png)
|
||||||
"今天天气真好!" → ??? → 计算机无法直接"理解"
|
↓
|
||||||
"Python是世界上最好的语言" → ??? → 计算机不知道这是什么意思
|
计算机读取像素值
|
||||||
|
↓
|
||||||
|
存储为3维矩阵 [高度, 宽度, 通道]
|
||||||
|
↓
|
||||||
|
一张 1920×1080 的彩色图 = 1920 × 1080 × 3 = 6,220,800 个数字
|
||||||
```
|
```
|
||||||
|
|
||||||
**核心问题:**
|
**文本数据的读取:**
|
||||||
|
|
||||||
> 计算机只能处理数字,**文本是符号**,符号不能直接用于计算。
|
|
||||||
|
|
||||||
## 1.2 文本 vs 图像:本质区别
|
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────┐
|
文本文件(.txt/.md)
|
||||||
│ 图像数据 │
|
↓
|
||||||
├─────────────────────────────────────────────────────────┤
|
计算机读取字符编码(ASCII/UTF-8)
|
||||||
│ 📐 空间结构化数据 │
|
↓
|
||||||
│ ・像素 = 数字(0-255) │
|
存储为字符序列(每个字符是一个数字编码)
|
||||||
│ ・位置关系 = 空间关系(上下左右) │
|
↓
|
||||||
│ ・相邻像素通常相关 │
|
"Python" → [80, 121, 116, 104, 111](ASCII编码)
|
||||||
│ ・直接可以用矩阵表示 │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────┐
|
|
||||||
│ 文本数据 │
|
|
||||||
├─────────────────────────────────────────────────────────┤
|
|
||||||
│ 📝 序列符号化数据 │
|
|
||||||
│ ・词语/字符 = 离散符号 │
|
|
||||||
│ ・位置关系 = 逻辑顺序(不是物理位置) │
|
|
||||||
│ ・符号本身无"数值含义" │
|
|
||||||
│ ・无法直接用数字运算 │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 1.3 文本处理的核心挑战
|
### 字符编码:用数字表示字符
|
||||||
|
|
||||||
### 挑战1:符号到数字的转换
|
计算机并不能直接"认识"字符,它只认识数字。所以需要一种**映射规则**把字符变成数字:
|
||||||
|
|
||||||
```
|
```
|
||||||
"Python" → ??? → [?, ?, ?, ...]
|
ASCII编码(英文):
|
||||||
|
'A' → 65, 'B' → 66, ..., 'Z' → 90
|
||||||
|
'a' → 97, 'b' → 98, ..., 'z' → 122
|
||||||
|
'0' → 48, '1' → 49, ..., '9' → 57
|
||||||
|
|
||||||
|
UTF-8编码(支持中文):
|
||||||
|
'中' → 20013, '文' → 25991, 'P' → 80, 'y' → 121
|
||||||
```
|
```
|
||||||
|
|
||||||
### 挑战2:一词多义
|
### 文本的"存储形式"
|
||||||
|
|
||||||
```
|
```python
|
||||||
"苹果" 可以指:
|
# 文本在计算机中的存储方式
|
||||||
・一种水果(营养丰富)
|
text = "Hello"
|
||||||
・一家公司(Apple Inc.)
|
|
||||||
・手机品牌(苹果手机)
|
# 如果我们看它的"数字形式":
|
||||||
|
print([ord(c) for c in text])
|
||||||
|
# 输出: [72, 101, 108, 108, 111]
|
||||||
|
# 72='H', 101='e', 108='l', 111='o'
|
||||||
|
|
||||||
|
# 中文例子
|
||||||
|
text_cn = "你好"
|
||||||
|
print([ord(c) for c in text_cn])
|
||||||
|
# 输出: [20320, 22909]
|
||||||
|
# 20320='你', 22909='好'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 挑战3:词序敏感
|
## 1.3 计算机擅长什么?不擅长什么?
|
||||||
|
|
||||||
|
### 计算机擅长的任务
|
||||||
|
|
||||||
```
|
```
|
||||||
"我爱你" vs "你爱我"
|
✅ 数字计算:1 + 2 = 3
|
||||||
・词语完全相同
|
✅ 逻辑判断:if (a > b) then ...
|
||||||
・顺序不同
|
✅ 矩阵运算:图像卷积、矩阵乘法
|
||||||
・意思完全相反
|
✅ 精确匹配:字符串完全相同比较
|
||||||
|
✅ 模式识别:符合规则的数据查找
|
||||||
```
|
```
|
||||||
|
|
||||||
### 挑战4:同义表达
|
### 计算机不擅长的任务
|
||||||
|
|
||||||
```
|
```
|
||||||
"电脑" vs "计算机" vs "计算机器"
|
❌ 语义理解:计算机不知道"今天天气真好"是好心情还是讽刺
|
||||||
・不同符号
|
❌ 情感判断:计算机不知道"真是绝了"是夸人还是骂人
|
||||||
・相同含义
|
❌ 模糊推理:"大概"、"也许"、"差不多"无法精确处理
|
||||||
|
❌ 创意创作:写诗、写小说、编笑话
|
||||||
|
❌ 常识理解:"水往低处流"这种常识计算机不懂
|
||||||
|
```
|
||||||
|
|
||||||
|
### 为什么计算机不擅长理解文本?
|
||||||
|
|
||||||
|
**原因一:文本是"符号",不是"数值"**
|
||||||
|
|
||||||
|
```
|
||||||
|
计算机大脑 = 计算器(专门处理数字)
|
||||||
|
文本 = 一堆符号(对计算机来说就像乱码)
|
||||||
|
|
||||||
|
数字:1, 2, 3, 100.5, -7 → 计算机直接能算
|
||||||
|
文本:"好"、"bad"、"hello" → 计算机不知道啥意思
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因二:语义不是显式表达的**
|
||||||
|
|
||||||
|
```
|
||||||
|
文本:"他今天心情不太好,因为下雨了"
|
||||||
|
|
||||||
|
计算机看到:[他, 今天, 心情, 不, 好, 因为, 下雨, 了]
|
||||||
|
↓
|
||||||
|
人类理解:他在不开心,因为外面下雨了(影响心情)
|
||||||
|
↓
|
||||||
|
计算机:???不理解下雨和心情的因果关系
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因三:同样的符号,不同的语境,不同的意思**
|
||||||
|
|
||||||
|
```
|
||||||
|
"苹果真好吃"
|
||||||
|
→ 说的是水果
|
||||||
|
|
||||||
|
"苹果手机真贵"
|
||||||
|
→ 说的是手机品牌
|
||||||
|
|
||||||
|
计算机怎么知道?因为有上下文!
|
||||||
|
但计算机理解上下文的能力很弱
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 📖 第二部分:文本向量化的核心思想
|
# 📖 第二部分:向量基础入门(零基础科普)
|
||||||
|
|
||||||
## 2.1 核心目标:把所有文本变成"向量"
|
## 2.1 什么是向量?
|
||||||
|
|
||||||
|
### 向量的直观理解
|
||||||
|
|
||||||
|
**向量 = 有方向的量**
|
||||||
|
|
||||||
|
```
|
||||||
|
在日常生活中:
|
||||||
|
・速度:每小时60公里,向北走
|
||||||
|
・力:10牛顿,向右推
|
||||||
|
・风向:东南风,每秒5米
|
||||||
|
|
||||||
|
这些都是有"方向"和"大小"的量,就是向量!
|
||||||
|
```
|
||||||
|
|
||||||
|
### 向量在数学中的表示
|
||||||
|
|
||||||
|
**一维向量(数轴上的点):**
|
||||||
|
|
||||||
|
```
|
||||||
|
←———————————|———————————→
|
||||||
|
-3 -2 -1 0 1 2 3
|
||||||
|
|
||||||
|
点A在位置 2 → 向量A = [2]
|
||||||
|
点B在位置 -3 → 向量B = [-3]
|
||||||
|
```
|
||||||
|
|
||||||
|
**二维向量(平面上的点):**
|
||||||
|
|
||||||
|
```
|
||||||
|
y
|
||||||
|
↑
|
||||||
|
|
|
||||||
|
3 | * A(2,3)
|
||||||
|
|
|
||||||
|
2 |
|
||||||
|
|
|
||||||
|
1 | * B(4,1)
|
||||||
|
|
|
||||||
|
0---+—————————————→ x
|
||||||
|
0 1 2 3 4 5
|
||||||
|
|
||||||
|
向量A = [2, 3] (横坐标2,纵坐标3)
|
||||||
|
向量B = [4, 1]
|
||||||
|
```
|
||||||
|
|
||||||
|
**三维向量(立体空间):**
|
||||||
|
|
||||||
|
```
|
||||||
|
z
|
||||||
|
↑ * C(1,2,3)
|
||||||
|
| /
|
||||||
|
| /
|
||||||
|
| /
|
||||||
|
|/__________→ y
|
||||||
|
/
|
||||||
|
/
|
||||||
|
/_________→ x
|
||||||
|
(0,0,0)
|
||||||
|
|
||||||
|
向量C = [1, 2, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python中创建向量
|
||||||
|
|
||||||
|
```python
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 一维向量
|
||||||
|
v1 = np.array([3]) # 只有1个数字
|
||||||
|
print(f"v1 = {v1}")
|
||||||
|
|
||||||
|
# 二维向量
|
||||||
|
v2 = np.array([2, 3]) # 2个数字,表示平面上的一个点
|
||||||
|
print(f"v2 = {v2}")
|
||||||
|
|
||||||
|
# 三维向量
|
||||||
|
v3 = np.array([1, 2, 3]) # 3个数字,表示立体空间的一个点
|
||||||
|
print(f"v3 = {v3}")
|
||||||
|
|
||||||
|
# 更多维向量(机器学习中常用)
|
||||||
|
v100 = np.array([0.1, 0.5, -0.3, 0.8, ...]) # 100维!
|
||||||
|
print(f"v100有 {len(v100)} 个元素")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.2 向量的基本运算
|
||||||
|
|
||||||
|
### 加法和减法
|
||||||
|
|
||||||
|
```python
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 向量加法:对应位置相加
|
||||||
|
a = np.array([1, 2, 3])
|
||||||
|
b = np.array([4, 5, 6])
|
||||||
|
c = a + b # [1+4, 2+5, 3+6] = [5, 7, 9]
|
||||||
|
|
||||||
|
print(f"a + b = {c}") # [5, 7, 9]
|
||||||
|
|
||||||
|
# 直观理解:
|
||||||
|
# a = [1, 2, 3] 从原点出发走1步、再走2步、再走3步
|
||||||
|
# b = [4, 5, 6] 从原点出发走4步、再走5步、再走6步
|
||||||
|
# a + b = 从原点走完a再走b = [5, 7, 9]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数乘(标量乘法)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 向量乘以一个数字(标量)
|
||||||
|
v = np.array([1, 2, 3])
|
||||||
|
result = v * 2 # [1*2, 2*2, 3*2] = [2, 4, 6]
|
||||||
|
|
||||||
|
print(f"v * 2 = {result}") # [2, 4, 6]
|
||||||
|
|
||||||
|
# 直观理解:
|
||||||
|
# v = [1, 2, 3] 表示"方向"
|
||||||
|
# v * 2 = [2, 4, 6] 方向不变,长度变成2倍
|
||||||
|
```
|
||||||
|
|
||||||
|
### 向量的长度(模/范数)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 向量的长度 = 从原点到终点的距离
|
||||||
|
v = np.array([3, 4])
|
||||||
|
|
||||||
|
# 用勾股定理:3² + 4² = 25,√25 = 5
|
||||||
|
length = np.linalg.norm(v)
|
||||||
|
print(f"向量 [3, 4] 的长度 = {length}") # 5.0
|
||||||
|
|
||||||
|
# 另一个例子
|
||||||
|
v2 = np.array([1, 1])
|
||||||
|
length2 = np.linalg.norm(v2)
|
||||||
|
print(f"向量 [1, 1] 的长度 = {length2:.2f}") # 1.41
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.3 点积(Dot Product)— 最重要的运算
|
||||||
|
|
||||||
|
### 什么是点积?
|
||||||
|
|
||||||
|
**点积 = 对应位置相乘,再求和**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 两个向量对应位置相乘,然后加起来
|
||||||
|
a = np.array([1, 2, 3])
|
||||||
|
b = np.array([4, 5, 6])
|
||||||
|
|
||||||
|
# 点积计算过程:
|
||||||
|
# 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
|
||||||
|
|
||||||
|
dot = np.dot(a, b)
|
||||||
|
print(f"点积 = {dot}") # 32
|
||||||
|
|
||||||
|
# 或者用 @ 运算符
|
||||||
|
print(f"a @ b = {a @ b}") # 32
|
||||||
|
```
|
||||||
|
|
||||||
|
### 点积的直观理解
|
||||||
|
|
||||||
|
```
|
||||||
|
点积 = a的长度 × b的长度 × cos(夹角)
|
||||||
|
|
||||||
|
如果 a 和 b 方向相同:夹角 = 0°,cos(0) = 1,点积 = |a|×|b|(最大)
|
||||||
|
如果 a 和 b 垂直: 夹角 = 90°,cos(90) = 0,点积 = 0(最小)
|
||||||
|
如果 a 和 b 相反: 夹角 = 180°,cos(180) = -1,点积 = -|a|×|b|(负最大)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.4 余弦相似度 — 用点积判断"像不像"
|
||||||
|
|
||||||
|
### 什么是相似度?
|
||||||
|
|
||||||
|
**相似度 = 两个向量有多"像"**
|
||||||
|
|
||||||
|
```
|
||||||
|
相似度高的例子:
|
||||||
|
・"猫"和"狗":都是动物,都四只脚,都会叫
|
||||||
|
・"红色"和"黄色":都是颜色,都是暖色调
|
||||||
|
・"跑步"和"游泳":都是运动
|
||||||
|
|
||||||
|
相似度低的例子:
|
||||||
|
・"猫"和"石头":一个是动物,一个不是
|
||||||
|
・"热"和"冷":意思相反
|
||||||
|
・"太阳"和"细菌":几乎没有共同点
|
||||||
|
```
|
||||||
|
|
||||||
|
### 余弦相似度公式
|
||||||
|
|
||||||
|
```
|
||||||
|
A · B
|
||||||
|
cos(θ) = ──────────
|
||||||
|
|A| × |B|
|
||||||
|
|
||||||
|
・A · B = 向量A和B的点积
|
||||||
|
・|A| = 向量A的长度
|
||||||
|
・|B| = 向量B的长度
|
||||||
|
・cos(θ) = 相似度,范围是 [-1, 1]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 余弦相似度的值代表什么?
|
||||||
|
|
||||||
|
| cos(θ) 值 | 夹角 θ | 相似程度 | 示例 |
|
||||||
|
|----------|--------|---------|------|
|
||||||
|
| 1.0 | 0° | 完全相同 | 同一向量 |
|
||||||
|
| 0.8~0.99 | 0~37° | 非常相似 | "猫" vs "狗" |
|
||||||
|
| 0.5~0.8 | 0~60° | 比较相似 | "跑步" vs "运动" |
|
||||||
|
| 0.3~0.5 | 60~72° | 有些相似 | "苹果" vs "水果" |
|
||||||
|
| 0 | 90° | 毫不相关 | "猫" vs "石头" |
|
||||||
|
| -1.0 | 180° | 完全相反 | "高" vs "矮" |
|
||||||
|
|
||||||
|
### Python计算余弦相似度
|
||||||
|
|
||||||
|
```python
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def cosine_similarity(a, b):
|
||||||
|
"""计算余弦相似度"""
|
||||||
|
dot = np.dot(a, b) # 点积
|
||||||
|
norm_a = np.linalg.norm(a) # 向量a的长度
|
||||||
|
norm_b = np.linalg.norm(b) # 向量b的长度
|
||||||
|
return dot / (norm_a * norm_b)
|
||||||
|
|
||||||
|
# 例子1:方向完全相同的向量
|
||||||
|
a = np.array([1, 2, 3])
|
||||||
|
b = np.array([2, 4, 6]) # b是a的两倍,方向完全相同
|
||||||
|
print(f"相似度 = {cosine_similarity(a, b):.3f}") # 1.000
|
||||||
|
|
||||||
|
# 例子2:方向完全相反的向量
|
||||||
|
a = np.array([1, 2, 3])
|
||||||
|
b = np.array([-1, -2, -3]) # b是a的相反方向
|
||||||
|
print(f"相似度 = {cosine_similarity(a, b):.3f}") # -1.000
|
||||||
|
|
||||||
|
# 例子3:垂直的向量
|
||||||
|
a = np.array([1, 0])
|
||||||
|
b = np.array([0, 1])
|
||||||
|
print(f"相似度 = {cosine_similarity(a, b):.3f}") # 0.000
|
||||||
|
|
||||||
|
# 例子4:日常生活中的"相似"
|
||||||
|
print("\n=== 语义相似度示例 ===")
|
||||||
|
# 假设这些是词的"意义向量"(简化版)
|
||||||
|
# 维度:[是否是动物, 是否有生命, 是否能移动]
|
||||||
|
cat = np.array([0.9, 0.9, 0.8]) # 猫:动物,有生命,能移动
|
||||||
|
dog = np.array([0.8, 0.9, 0.8]) # 狗:动物,有生命,能移动
|
||||||
|
apple = np.array([0.1, 0.3, 0.0]) # 苹果:植物,略有生命,不能移动
|
||||||
|
|
||||||
|
print(f"猫 vs 狗: {cosine_similarity(cat, dog):.3f}") # 非常接近1
|
||||||
|
print(f"猫 vs 苹果: {cosine_similarity(cat, apple):.3f}") # 接近0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📖 第三部分:文本向量化的核心思想
|
||||||
|
|
||||||
|
## 3.1 核心目标:把所有文本变成"向量"
|
||||||
|
|
||||||
```
|
```
|
||||||
文本(符号) → 数值向量 → 计算机可以计算 → AI模型处理
|
文本(符号) → 数值向量 → 计算机可以计算 → AI模型处理
|
||||||
@@ -112,78 +426,66 @@
|
|||||||
・向量加减:v1 + v2 = ?
|
・向量加减:v1 + v2 = ?
|
||||||
・向量点积:v1 · v2 = ?
|
・向量点积:v1 · v2 = ?
|
||||||
・向量距离:||v1 - v2|| = ?
|
・向量距离:||v1 - v2|| = ?
|
||||||
・矩阵运算:A × B = ?
|
・余弦相似度:cos(θ) = ?
|
||||||
|
|
||||||
但计算机不擅长:
|
但计算机不擅长:
|
||||||
・字符串比较:"Python" == "Java" ?
|
・字符串比较:"Python" == "Java" ?
|
||||||
・词语推理:"猫" 类似于 "狗" ?
|
・词语推理:"猫" 类似于 "狗" ?
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2.2 向量化示例:从"词"到"数"
|
**文本向量化的意义:**
|
||||||
|
|
||||||
### 简单例子:把词变成向量
|
```
|
||||||
|
"猫"和"狗"在文本中可能完全不相关
|
||||||
|
但如果我们知道它们都是"动物",向量就会接近
|
||||||
|
计算机就能"理解"它们的相似性了!
|
||||||
|
```
|
||||||
|
|
||||||
假设我们只有一个很小的词汇表:
|
## 3.2 向量化示例:从"词"到"数"
|
||||||
|
|
||||||
|
### 简单例子:把词变成向量(位置编码)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
# 假设我们有一个很小的词汇表(只有5个词)
|
||||||
vocab = ["猫", "狗", "鱼", "苹果", "香蕉"]
|
vocab = ["猫", "狗", "鱼", "苹果", "香蕉"]
|
||||||
|
|
||||||
# "猫" → [1, 0, 0, 0, 0] # 猫在这个词表中的位置
|
# 位置编码:每个词对应一个位置
|
||||||
# "狗" → [0, 1, 0, 0, 0] # 狗在这个词表中的位置
|
# "猫" → [1, 0, 0, 0, 0] # 第1个位置是1,其他是0
|
||||||
# "苹果" → [0, 0, 0, 1, 0] # 苹果在这个词表中的位置
|
# "狗" → [0, 1, 0, 0, 0] # 第2个位置是1,其他是0
|
||||||
|
# "苹果" → [0, 0, 0, 1, 0] # 第4个位置是1,其他是0
|
||||||
```
|
```
|
||||||
|
|
||||||
**问题**:这只是"位置编码",没有语义信息!
|
**问题**:这只是"位置编码",没有语义信息!
|
||||||
|
|
||||||
### 更智能的例子:考虑语义
|
```
|
||||||
|
"猫" = [1, 0, 0, 0, 0]
|
||||||
|
"狗" = [0, 1, 0, 0, 0]
|
||||||
|
|
||||||
|
余弦相似度 = 0 (完全不相似)
|
||||||
|
|
||||||
|
但实际上"猫"和"狗"都是动物,应该很相似!
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更智能的例子:语义向量
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 理想情况:
|
# 语义编码:每个词用"含义"来表示
|
||||||
# "猫" → [0.9, 0.1, 0.2] # 语义:动物、毛茸茸、会喵喵叫
|
# 维度:[动物性, 植物性, 可食用性, 宠物性]
|
||||||
# "狗" → [0.8, 0.3, 0.1] # 语义:动物、毛茸茸、会汪汪叫
|
|
||||||
# "苹果" → [0.1, 0.2, 0.9] # 语义:水果、甜的、红色/绿色
|
|
||||||
|
|
||||||
# 这样"猫"和"狗"的向量更接近(都是动物)
|
cat = np.array([0.9, 0.1, 0.7, 0.9]) # 猫:动物性高,可食用(猫肉?),是宠物
|
||||||
# "猫"和"苹果"的向量更远(一个是动物,一个是水果)
|
dog = np.array([0.8, 0.2, 0.6, 0.9]) # 狗:动物性高,可食用(狗肉?),是宠物
|
||||||
|
apple = np.array([0.1, 0.9, 0.9, 0.0]) # 苹果:植物性高,可食用,不是宠物
|
||||||
|
|
||||||
|
# 计算相似度
|
||||||
|
print(f"猫 vs 狗: {cosine_similarity(cat, dog):.3f}") # ≈ 0.97(很相似!)
|
||||||
|
print(f"猫 vs 苹果: {cosine_similarity(cat, apple):.3f}") # ≈ 0.15(不太相似)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2.3 相似度计算:向量的威力
|
**这就是文本向量化的威力:把"语义"变成"可计算的数值"!**
|
||||||
|
|
||||||
**核心应用:文本相似度**
|
## 3.3 向量化方法演进
|
||||||
|
|
||||||
```python
|
### 演进概览
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def cosine_similarity(v1, v2):
|
|
||||||
"""计算两个向量的余弦相似度"""
|
|
||||||
return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
|
|
||||||
|
|
||||||
# 假设这些是词的向量表示
|
|
||||||
cat = np.array([0.9, 0.1, 0.2]) # 猫
|
|
||||||
dog = np.array([0.8, 0.3, 0.1]) # 狗
|
|
||||||
apple = np.array([0.1, 0.2, 0.9]) # 苹果
|
|
||||||
|
|
||||||
print("猫 vs 狗:", cosine_similarity(cat, dog)) # 应该较高(都是动物)
|
|
||||||
print("猫 vs 苹果:", cosine_similarity(cat, apple)) # 应该较低(动物 vs 水果)
|
|
||||||
```
|
|
||||||
|
|
||||||
**输出结果:**
|
|
||||||
```
|
|
||||||
猫 vs 狗: 0.862
|
|
||||||
猫 vs 苹果: 0.149
|
|
||||||
```
|
|
||||||
|
|
||||||
**结论**:
|
|
||||||
- 相似度 0.862 → "猫"和"狗"语义相近 ✅
|
|
||||||
- 相似度 0.149 → "猫"和"苹果"语义相差很远 ✅
|
|
||||||
|
|
||||||
> 🎯 **这就是文本向量化的威力:把"语义"变成"可计算的数值"!**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 📖 第三部分:从 BoW 到 Embedding:演进之路
|
|
||||||
|
|
||||||
## 3.1 演进概览
|
|
||||||
|
|
||||||
```
|
```
|
||||||
文本向量化的三种主要方法:
|
文本向量化的三种主要方法:
|
||||||
@@ -195,11 +497,9 @@ print("猫 vs 苹果:", cosine_similarity(cat, apple)) # 应该较低(动物 v
|
|||||||
无语义 部分语义 深度语义
|
无语义 部分语义 深度语义
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3.2 BoW(词袋模型)—— 最简单的方法
|
### BoW(词袋模型)—— 最简单的方法
|
||||||
|
|
||||||
### 原理
|
**原理**:把文本看成"一袋词",不考虑顺序,只管词出现了几次。
|
||||||
|
|
||||||
把文本看成"一袋词",不考虑顺序,只管词出现了几次。
|
|
||||||
|
|
||||||
```
|
```
|
||||||
文本1: "Python 是 编程 语言"
|
文本1: "Python 是 编程 语言"
|
||||||
@@ -211,25 +511,40 @@ print("猫 vs 苹果:", cosine_similarity(cat, apple)) # 应该较低(动物 v
|
|||||||
**向量化:**
|
**向量化:**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 统计每个词出现的次数
|
from sklearn.feature_extraction.text import CountVectorizer
|
||||||
text1_vec = [1, 0, 1, 1, 1] # Python=1, Java=0, 是=1, 编程=1, 语言=1
|
|
||||||
text2_vec = [0, 1, 1, 1, 1] # Python=0, Java=1, 是=1, 编程=1, 语言=1
|
# 文档集合
|
||||||
|
docs = [
|
||||||
|
"Python 是 编程 语言",
|
||||||
|
"Java 是 编程 语言",
|
||||||
|
]
|
||||||
|
|
||||||
|
# BoW 向量化
|
||||||
|
vectorizer = CountVectorizer()
|
||||||
|
bow_matrix = vectorizer.fit_transform(docs)
|
||||||
|
|
||||||
|
print("词表:", vectorizer.get_feature_names_out())
|
||||||
|
# 输出: ['Python', 'Java', '是', '编程', '语言']
|
||||||
|
|
||||||
|
print("BoW矩阵:")
|
||||||
|
print(bow_matrix.toarray())
|
||||||
|
# 输出:
|
||||||
|
# [[1 0 1 1 1] # Python文档: Python=1, Java=0, 是=1, 编程=1, 语言=1
|
||||||
|
# [0 1 1 1 1]] # Java文档: Python=0, Java=1, 是=1, 编程=1, 语言=1
|
||||||
```
|
```
|
||||||
|
|
||||||
### 优缺点
|
### BoW 的优缺点
|
||||||
|
|
||||||
| 优点 | 缺点 |
|
| 优点 | 缺点 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 简单直观 | 忽略词序 |
|
| 简单直观 | 忽略词序 |
|
||||||
| 容易实现 | "我爱你"和"你爱我"向量完全相同 |
|
| 容易实现 | "我爱你"和"你爱我"向量完全相同 |
|
||||||
| 计算速度快 | 所有词同等重要 |
|
| 计算速度快 | 所有词同等重要 |
|
||||||
|
| 适合基线模型 | 无法捕捉语义 |
|
||||||
|
|
||||||
## 3.3 TF-IDF —— 加入词的重要性
|
### TF-IDF —— 加入词的重要性
|
||||||
|
|
||||||
### 原理
|
**问题**:BoW 中"Python"和"的"权重相同,这不合理!
|
||||||
|
|
||||||
BoW 的问题是:所有词同等重要。
|
|
||||||
"Python"和"的"在 BoW 中权重相同,这不合理。
|
|
||||||
|
|
||||||
**TF-IDF = 词频(TF) × 逆文档频率(IDF)**
|
**TF-IDF = 词频(TF) × 逆文档频率(IDF)**
|
||||||
|
|
||||||
@@ -240,26 +555,43 @@ IDF = 这个词在所有文档中多不多(越多越不重要)
|
|||||||
TF-IDF = TF × IDF
|
TF-IDF = TF × IDF
|
||||||
```
|
```
|
||||||
|
|
||||||
### 为什么有效?
|
**为什么有效?**
|
||||||
|
|
||||||
```
|
```
|
||||||
・"Python":在少数文档中高频出现 → TF高, IDF高 → TF-IDF高 ✅ 重要词
|
・高频出现 ≠ 重要
|
||||||
・"的":在所有文档中都出现 → TF高, IDF低 → TF-IDF低 ❌ 停用词
|
"的"在所有文章都出现 → TF高, IDF低 → TF-IDF低 ❌ 停用词
|
||||||
```
|
|
||||||
|
|
||||||
### 直观理解
|
・罕见词 ≠ 不重要
|
||||||
|
"TensorFlow"只在AI文章出现 → TF低, IDF高 → TF-IDF高 ✅ 重要词
|
||||||
|
|
||||||
```
|
|
||||||
TF-IDF = 词的重要性 × 词的独特性
|
|
||||||
|
|
||||||
・高频出现 ≠ 重要("的"在所有文章都出现)
|
|
||||||
・罕见词 ≠ 不重要("TensorFlow"只在AI文章出现)
|
|
||||||
・既高频又独特 = 真正重要的词
|
・既高频又独特 = 真正重要的词
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3.4 Word Embedding(词嵌入)—— 蕴含语义
|
**TF-IDF 示例:**
|
||||||
|
|
||||||
### BoW 和 TF-IDF 的根本问题
|
```python
|
||||||
|
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||||
|
|
||||||
|
docs = [
|
||||||
|
"Python 编程 语言",
|
||||||
|
"Python Python Python", # Python出现3次
|
||||||
|
"Java 编程 语言",
|
||||||
|
]
|
||||||
|
|
||||||
|
tfidf_vectorizer = TfidfVectorizer()
|
||||||
|
tfidf_matrix = tfidf_vectorizer.fit_transform(docs)
|
||||||
|
|
||||||
|
print("词表:", tfidf_vectorizer.get_feature_names_out())
|
||||||
|
print("\nTF-IDF矩阵:")
|
||||||
|
print(tfidf_matrix.toarray())
|
||||||
|
|
||||||
|
# 观察:Python在第2篇文档中出现3次,但TF-IDF值不是最高的
|
||||||
|
# 因为"Python"在所有文档都出现了,IDF值较低
|
||||||
|
```
|
||||||
|
|
||||||
|
### Word Embedding(词嵌入)—— 蕴含语义
|
||||||
|
|
||||||
|
**BoW 和 TF-IDF 的根本问题**
|
||||||
|
|
||||||
```
|
```
|
||||||
"猫" → [1, 0, 0, ...] # 只是"位置编码"
|
"猫" → [1, 0, 0, ...] # 只是"位置编码"
|
||||||
@@ -267,9 +599,9 @@ TF-IDF = 词的重要性 × 词的独特性
|
|||||||
"小猫" → [0, 0, 1, ...] # 但它们语义相近,向量却正交!
|
"小猫" → [0, 0, 1, ...] # 但它们语义相近,向量却正交!
|
||||||
```
|
```
|
||||||
|
|
||||||
**问题**:BoW 和 TF-IDF **无法表达语义相似性**!
|
**问题**:无法表达语义相似性!
|
||||||
|
|
||||||
### Embedding 的思想
|
**Embedding 的思想**
|
||||||
|
|
||||||
```
|
```
|
||||||
不再用"位置"表示词,而是用"语义空间"表示词
|
不再用"位置"表示词,而是用"语义空间"表示词
|
||||||
@@ -279,49 +611,35 @@ TF-IDF = 词的重要性 × 词的独特性
|
|||||||
动物 植物
|
动物 植物
|
||||||
| ↑ 猫 ↑ 狗 ↑ 苹果
|
| ↑ 猫 ↑ 狗 ↑ 苹果
|
||||||
| ↗ ↗ ↑
|
| ↗ ↗ ↑
|
||||||
| ↗ ↗ ↑
|
|
||||||
| ↗ ↗ ← 语义相近的词距离近 →
|
| ↗ ↗ ← 语义相近的词距离近 →
|
||||||
|──────────→────────────────────────────→
|
| ↗ ↗ →
|
||||||
|
|——————————————————————————————————————→
|
||||||
0 抽象 具体
|
0 抽象 具体
|
||||||
| ↑
|
|
||||||
| ↑
|
|
||||||
| ↑ 人
|
|
||||||
|
|
|
||||||
└────────────────────────────────────→
|
|
||||||
|
|
||||||
"猫"和"狗"距离近(都是动物)
|
|
||||||
"猫"和"苹果"距离远(动物 vs 植物)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 词嵌入的实际表示
|
**词嵌入的效果**
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# 实际中,词向量通常是 50/100/300 维
|
# 假设我们用预训练模型得到了词的语义向量
|
||||||
# 这里用 3 维举例
|
# 实际中向量通常是 50/100/300 维,这里用3维举例
|
||||||
|
|
||||||
cat = [0.9, 0.1, 0.2] # 猫:动物属性高,植物属性低
|
word_vectors = {
|
||||||
dog = [0.8, 0.3, 0.1] # 狗:动物属性高
|
"猫": [0.9, 0.1, 0.2], # 动物属性高
|
||||||
apple = [0.1, 0.2, 0.9] # 苹果:植物属性高
|
"狗": [0.8, 0.3, 0.1], # 动物属性高
|
||||||
|
"苹果": [0.1, 0.2, 0.9], # 水果属性高
|
||||||
|
"Python": [0.1, 0.0, 0.9], # 编程属性高
|
||||||
|
"Java": [0.1, 0.0, 0.85], # 编程属性高
|
||||||
|
}
|
||||||
|
|
||||||
# 计算相似度
|
print("=== 语义相似度 ===")
|
||||||
cosine_similarity(cat, dog) # ≈ 0.97 → 非常相似
|
print(f"猫 vs 狗: {cosine_similarity(word_vectors['猫'], word_vectors['狗']):.3f}")
|
||||||
cosine_similarity(cat, apple) # ≈ 0.15 → 很不相似
|
# 输出: 猫 vs 狗: 0.972(非常相似!都是动物)
|
||||||
```
|
|
||||||
|
|
||||||
### Word2Vec:如何得到词向量?
|
print(f"猫 vs 苹果: {cosine_similarity(word_vectors['猫'], word_vectors['苹果']):.3f}")
|
||||||
|
# 输出: 猫 vs 苹果: 0.149(不太相似!)
|
||||||
|
|
||||||
```
|
print(f"Python vs Java: {cosine_similarity(word_vectors['Python'], word_vectors['Java']):.3f}")
|
||||||
训练方式1:CBOW
|
# 输出: Python vs Java: 0.998(非常相似!都是编程语言)
|
||||||
・上下文:[猫 __ 鱼] → 预测中间词"吃"
|
|
||||||
・学习目标:调整向量使得能正确预测
|
|
||||||
|
|
||||||
训练方式2:Skip-gram
|
|
||||||
・中心词:[吃] → 预测上下文[猫, 鱼]
|
|
||||||
・学习目标:调整向量使得能正确预测
|
|
||||||
|
|
||||||
原理:"相似上下文中的词,语义相似"
|
|
||||||
・猫和狗经常出现在相似的上下文中("猫吃鱼"、"狗吃肉")
|
|
||||||
・所以它们的向量也会相似
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -373,7 +691,7 @@ cosine_similarity(cat, apple) # ≈ 0.15 → 很不相似
|
|||||||
|
|
||||||
```
|
```
|
||||||
原始文本:
|
原始文本:
|
||||||
"今天天气真不错!!Python是一门很棒的语言,PYTHON也很重要!!!"
|
"今天天气真不错!!Python是一门很棒的语言,python也很重要!!!"
|
||||||
|
|
||||||
预处理后:
|
预处理后:
|
||||||
["今天", "天气", "不错", "Python", "语言"]
|
["今天", "天气", "不错", "Python", "语言"]
|
||||||
@@ -384,7 +702,7 @@ cosine_similarity(cat, apple) # ≈ 0.15 → 很不相似
|
|||||||
| 步骤 | 输入 | 输出 | 作用 |
|
| 步骤 | 输入 | 输出 | 作用 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| 分词 | "今天天气不错" | ["今天", "天气", "不错"] | 把文本切成词 |
|
| 分词 | "今天天气不错" | ["今天", "天气", "不错"] | 把文本切成词 |
|
||||||
| 去停用词 | ["今天", "天气", "不错"] | ["天气", "不错"] | 去掉无意义词 |
|
| 去停用词 | ["今天", "天气", "不错"] | ["天气", "不错"] | 去掉"的、了、在"等无意义词 |
|
||||||
| 统一大小写 | ["Python", "python"] | ["python", "python"] | 归一化 |
|
| 统一大小写 | ["Python", "python"] | ["python", "python"] | 归一化 |
|
||||||
| 去标点 | ["语言!!!"] | ["语言"] | 清理噪音 |
|
| 去标点 | ["语言!!!"] | ["语言"] | 清理噪音 |
|
||||||
|
|
||||||
@@ -489,57 +807,79 @@ cosine_similarity(cat, apple) # ≈ 0.15 → 很不相似
|
|||||||
|
|
||||||
| 方法 | 公式 | 含义 |
|
| 方法 | 公式 | 含义 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| TF | TF(t) = 词t在文档中出现次数 | 词在本文中多不多 |
|
| 向量加法 | [1,2] + [3,4] = [4,6] | 对应位置相加 |
|
||||||
| IDF | IDF(t) = log(总文档数 / 含词t的文档数) | 词是否罕见 |
|
| 向量数乘 | 2 × [1,2] = [2,4] | 每个元素乘以标量 |
|
||||||
| TF-IDF | TF-IDF(t) = TF(t) × IDF(t) | 词的重要程度 |
|
| 向量点积 | [1,2] · [3,4] = 11 | 对应相乘再求和 |
|
||||||
| 余弦相似度 | cos(θ) = (A·B) / (|A|×|B|) | 向量相似程度 |
|
| 向量长度 | |[3,4]| = 5 | √(3²+4²) |
|
||||||
|
| 余弦相似度 | cos(θ) = (A·B) / (\|A\|×\|B\|) | 向量相似程度 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 📝 预习任务
|
# 📝 课后作业
|
||||||
|
|
||||||
在下一节课之前,请思考以下问题:
|
## 第一部分:文本数据基础(1-2道)
|
||||||
|
|
||||||
## 问题1:分词挑战
|
### 题目1
|
||||||
|
在Python中,用两种方式表示"Hello":
|
||||||
|
1. 用 `ord()` 函数打印每个字符的ASCII码
|
||||||
|
2. 用 `chr()` 函数验证:字符65对应的是大写字母A
|
||||||
|
|
||||||
```
|
### 题目2
|
||||||
英文分词很简单(空格分隔):
|
思考题:为什么计算机擅长处理图像矩阵,却不擅长处理文本?请从"数据的表示形式"和"语义理解"两个角度说明原因。
|
||||||
"I love Python" → ["I", "love", "Python"]
|
|
||||||
|
|
||||||
但中文分词很难:
|
---
|
||||||
"我爱你中国" 可以切成:
|
|
||||||
["我", "爱", "你", "中国"] ?
|
|
||||||
["我爱你", "中国"] ?
|
|
||||||
["我爱", "你中国"] ?
|
|
||||||
|
|
||||||
请思考:为什么中文分词比英文难?
|
## 第二部分:向量基础(1-2道)
|
||||||
|
|
||||||
|
### 题目3
|
||||||
|
已知向量 A = [3, 4],B = [1, 2]:
|
||||||
|
1. 计算 A + B 的结果
|
||||||
|
2. 计算 2 × A 的结果
|
||||||
|
3. 计算 A 的长度(模)
|
||||||
|
|
||||||
|
### 题目4
|
||||||
|
有两个向量:A = [1, 2, 3],B = [4, 5, 6]
|
||||||
|
1. 计算它们的点积 A · B
|
||||||
|
2. 计算它们的余弦相似度
|
||||||
|
3. 如果 A = [1, 0],B = [0, 1],它们的余弦相似度是多少?为什么?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第三部分:文本向量化(1-2道)
|
||||||
|
|
||||||
|
### 题目5
|
||||||
|
假设有以下3个文档:
|
||||||
|
- Doc1: "Python 是 编程 语言"
|
||||||
|
- Doc2: "Java 是 编程 语言"
|
||||||
|
- Doc3: "Python Python Python"
|
||||||
|
|
||||||
|
使用BoW模型,词表是什么?每个文档的向量表示是什么?
|
||||||
|
|
||||||
|
### 题目6
|
||||||
|
思考题:BoW模型有哪些缺点?请至少列出2个,并说明为什么这些缺点在某些场景下会成为问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附加题(选做)
|
||||||
|
|
||||||
|
### 题目7
|
||||||
|
阅读以下代码,理解TF-IDF的工作原理:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||||
|
|
||||||
|
docs = ["Python 编程", "Java 编程", "Python Python"]
|
||||||
|
tfidf = TfidfVectorizer()
|
||||||
|
matrix = tfidf.fit_transform(docs)
|
||||||
|
|
||||||
|
print("词表:", tfidf.get_feature_names_out())
|
||||||
|
print("TF-IDF矩阵:")
|
||||||
|
print(matrix.toarray())
|
||||||
```
|
```
|
||||||
|
|
||||||
## 问题2:向量化思考
|
运行代码并回答:
|
||||||
|
1. 为什么"Python"在Doc3中的TF-IDF值不是最高(假设Doc1和Doc2的值更小)?
|
||||||
```
|
2. "Java"在Doc2中的TF-IDF值是多少?解释原因。
|
||||||
假设我们有3个文档:
|
|
||||||
Doc1: "Python是编程语言"
|
|
||||||
Doc2: "Java是编程语言"
|
|
||||||
Doc3: "猫是动物"
|
|
||||||
|
|
||||||
用BoW模型,词表是:[Python, Java, 是, 编程, 语言, 猫, 动物]
|
|
||||||
|
|
||||||
问题:
|
|
||||||
1. Doc1 和 Doc2 的相似度是多少?为什么?
|
|
||||||
2. Doc1 和 Doc3 的相似度是多少?为什么?
|
|
||||||
3. 这个结果合理吗?为什么?
|
|
||||||
```
|
|
||||||
|
|
||||||
## 问题3:工具安装
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 请在命令行安装以下工具
|
|
||||||
pip install jieba scikit-learn numpy matplotlib
|
|
||||||
|
|
||||||
# 验证安装成功
|
|
||||||
python -c "import jieba; import sklearn; print('安装成功!')"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user