欢迎光临
我们一直在努力

进阶篇-9-数学篇-8--特征值与特征向量实战:三大AI场景图片压缩,人脸识别,电影推荐深度剖析

特征值与特征向量实战:3个AI场景落地

作者:Weisian 日期:2026年2月2日

在这里插入图片描述

在上一篇文章中,我们揭开了特征值与特征向量的神秘面纱—— 它们是矩阵变换中的“不变方向”,更是 AI 从海量数据中“抓重点”的黄金筛子。 但理论再美,若不能落地,终究只是空中楼阁。 今天,我们用 “同一个玩具积木” 来演示三大AI场景:

  • 压缩照片 → 像只保留积木的主框架
  • 识别人脸 → 像认出积木的“标志性拼法”
  • 推荐电影 → 像猜你喜欢哪种积木组合

把数据想象成“乐高积木”

假设你有一堆积木搭成的模型(比如一辆车、一座房子),它由成百上千个小块组成。

但你发现:

  • 很多小块是重复的(冗余)
  • 有些小块只是装饰(噪声)
  • 真正决定“这是辆车”的,只有几个关键结构(如轮子、车身)

特征值分解,就是帮你自动找出这些“关键结构”,然后:

  • 拍照时只拍关键结构(压缩)
  • 认人时只看关键结构(识别)
  • 推荐时只匹配关键结构(推荐)

接下来,我们就用同一套“积木数据”(模拟图像、人脸、评分)来演示!

在这里插入图片描述


前置准备:统一环境

在开始实战前,确保你已安装以下核心库,后续所有代码均基于此环境运行:

# 核心数值计算与矩阵操作
pip install numpy
# 图像处理与加载
pip install opencv-python pillow
# 数据可视化
pip install matplotlib
# 人脸数据加载
pip install scikit-learn


场景1:图像压缩 —— “只拍积木主框架”

1.1 生活化理解

你想给朋友发一张“乐高车”的照片,但原图太大(几百KB),传输速度慢。 于是你只拍下车轮、车身、驾驶舱这三个核心关键部分(舍弃小贴纸、细微划痕等冗余细节), 朋友收到这三个核心部件后,依然能快速认出这是一辆乐高车,而且传输体积大幅变小!

对应图像压缩:

  • 乐高车主框架 = 图像核心信息(轮廓、明暗、主要结构)= 高奇异值对应的分量
  • 乐高车小装饰 = 图像冗余细节+噪点(细微纹理、拍摄噪声)= 低奇异值对应的分量
  • SVD压缩 = 只保留核心框架,舍弃冗余细节,实现“高清瘦身”

1.2 问题背景

在日常工作和生活中,我们经常会遇到以下图像相关的痛点:

  • 存储压力大:高清图像(如1024×1024灰度图)包含百万级像素,占用大量硬盘/云存储空间;
  • 传输效率低:大尺寸图像在微信、邮件、移动网络中传输缓慢,甚至会被平台强制压缩失真;
  • 含噪影响视觉:照片拍摄时会受光线、设备影响引入噪声,传输过程中也可能产生冗余干扰,影响视觉效果。
  • 而人眼的视觉特性有一个重要特点:对图像的核心结构(轮廓、明暗对比)敏感,对细微纹理和噪声不敏感。这就为图像压缩提供了可能——我们可以舍弃那些对视觉影响极小的冗余信息,用少量数据还原出肉眼几乎无差异的图像,这就是SVD图像压缩的核心价值。

    在这里插入图片描述

    1.3 核心思路

  • 数据准备:生成模拟人脸图像(无需真实图片,避免路径依赖),添加高斯噪声模拟真实拍摄场景;
  • SVD分解:对含噪图像矩阵进行奇异值分解,将图像拆分为「左奇异矩阵」「奇异值数组」「右奇异矩阵转置」三部分;
  • 核心分量筛选:选取不同数量的前k个最大奇异值(对应图像核心信息),舍弃剩余低奇异值(对应噪声/冗余);
  • 图像重构:用筛选后的k个核心分量,通过矩阵乘法重构图像;
  • 效果验证:对比原始图像、含噪图像与不同k值的重构图像,计算压缩率,同时可视化奇异值衰减趋势,验证压缩效果。
  • 1.4 关键原理

  • 图像矩阵表示:灰度图像可以用一个二维矩阵M表示(尺寸为m×n,m为高度,n为宽度),矩阵中的每个元素对应一个像素的亮度值(0=黑色,1=白色);
  • SVD奇异值分解:对于任意非方阵M,都可以进行奇异值分解,公式如下:

    M

    =

    U

    Σ

    V

    T

    M = U \\Sigma V^T

    M=UΣVT

    • U(左奇异矩阵):尺寸m×m,对应图像的「行结构特征」(可以理解为图像的垂直方向核心信息);
    • Σ(奇异值对角矩阵):尺寸m×n,只有对角线上有非零值,这些值就是奇异值,且按从大到小排序,奇异值的大小对应信息的重要程度(奇异值越大,对应图像的核心结构越关键);
    • V^T(右奇异矩阵转置):尺寸n×n,对应图像的「列结构特征」(可以理解为图像的水平方向核心信息);
  • 低维重构:我们不需要保留所有奇异值,只选取前k个最大的奇异值,即可重构出图像的核心内容,公式如下:

    M

    k

    =

    U

    k

    Σ

    k

    V

    k

    T

    M_k = U_k \\Sigma_k V_k^T

    Mk=UkΣkVkT

    • U_k:U的前k列,只保留前k个行结构特征;
    • Σ_k:k×k的对角矩阵,只保留前k个最大奇异值;
    • V_k^T:V^T的前k行,只保留前k个列结构特征;
  • 压缩率计算:
    • 原始图像数据量:m×n(所有像素点的数量);
    • 重构图像数据量:k×m + k×n + k(存储U_k、Σ_k、V_k^T所需的元素数量,简化为k×(m+n+1));
    • 压缩率:(1 – 重构数据量/原始数据量) × 100%,k越小,压缩率越高,图像细节丢失越多。
  • 在这里插入图片描述

    1.5 代码示例

    # -*- coding: utf-8 -*-
    """
    特征值实战三合一:压缩 / 识别 / 推荐
    所有代码可直接运行,无需联网!
    """

    import numpy as np
    import matplotlib.pyplot as plt

    # 设置中文字体(Windows)
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    # ==============================
    # 🧱 模拟数据生成器(我们的“乐高积木”)
    # ==============================

    def generate_image_like_data(size=64):
    """生成一张“人脸风格”的模拟图像(无需真实图片)"""
    # 创建一个空画布
    img = np.zeros((size, size))
    center = size // 2

    # 画一个“脸”:圆形轮廓 + 两个眼睛 + 一个嘴巴
    y, x = np.ogrid[:size, :size]
    face_mask = (x center)**2 + (y center)**2 <= (center * 0.8)**2
    img[face_mask] = 1.0

    # 左右眼
    eye_radius = center * 0.1
    left_eye = (x center + center//3)**2 + (y center center//4)**2 <= eye_radius**2
    right_eye = (x center center//3)**2 + (y center center//4)**2 <= eye_radius**2
    img[left_eye | right_eye] = 0.0 # 眼睛是黑色(0)

    # 嘴巴(微笑弧线)
    mouth_y = y > center + center//3
    mouth_curve = (x center)**2 < (y center center//2)**2 * 10
    img[mouth_y & mouth_curve] = 0.0

    return img

    def generate_user_movie_data():
    """生成模拟用户-电影评分数据(5用户×6电影)"""
    # 行:用户,列:电影,0=未评分
    ratings = np.array([
    [5, 3, 0, 1, 4, 0],
    [4, 0, 0, 1, 3, 2],
    [1, 1, 0, 5, 0, 4],
    [1, 0, 0, 4, 0, 5],
    [0, 1, 5, 4, 2, 0]
    ])
    return ratings

    # ==============================
    # 🛠️ 核心工具函数
    # ==============================

    def svd_compress(matrix, k):
    """用SVD压缩矩阵,返回重构结果和压缩率"""
    U, s, Vt = np.linalg.svd(matrix, full_matrices=False)
    # 重构
    recon = U[:, :k] @ np.diag(s[:k]) @ Vt[:k, :]
    # 压缩率
    orig_size = matrix.size
    comp_size = k * (U.shape[0] + Vt.shape[1] + 1)
    ratio = (1 comp_size / orig_size) * 100
    return recon, ratio, s

    def pca_project(data, k):
    """对数据做PCA,返回投影后的低维表示和主成分"""
    # 去中心化
    mean_vec = np.mean(data, axis=0)
    centered = data mean_vec
    # 协方差矩阵(简化版)
    cov = centered.T @ centered / len(centered)
    # 特征分解
    eigvals, eigvecs = np.linalg.eigh(cov) # eigh用于对称矩阵
    # 排序(从大到小)
    idx = np.argsort(eigvals)[::1]
    eigvals, eigvecs = eigvals[idx], eigvecs[:, idx]
    # 选前k个
    components = eigvecs[:, :k]
    projection = centered @ components
    return projection, components, mean_vec

    # ==============================
    # 🚀 开始三大实战!
    # ==============================

    # — 场景1:图像压缩 —
    print("【场景1:图像压缩】")

    # 1. 生成一张“人脸”模拟图(64×64)
    img = generate_image_like_data(64)
    print(f"原始图像大小: {img.shape}, 像素数: {img.size}")

    # 2. 添加一点噪声(模拟真实照片)
    np.random.seed(42)
    noisy_img = img + np.random.normal(0, 0.1, img.shape)
    noisy_img = np.clip(noisy_img, 0, 1)

    # 3. 用不同k值压缩
    k_values = [5, 15, 30]
    fig, axes = plt.subplots(2, 4, figsize=(14, 7))

    # 原图 vs 含噪图
    axes[0,0].imshow(img, cmap='gray')
    axes[0,0].set_title("原始图像")
    axes[0,0].axis('off')

    axes[1,0].imshow(noisy_img, cmap='gray')
    axes[1,0].set_title("含噪图像")
    axes[1,0].axis('off')

    # 压缩结果
    for i, k in enumerate(k_values):
    recon, ratio, s = svd_compress(noisy_img, k)
    axes[0,i+1].imshow(recon, cmap='gray')
    axes[0,i+1].set_title(f"k={k}\\n压缩率: {ratio:.1f}%")
    axes[0,i+1].axis('off')

    # 显示奇异值衰减
    axes[1,i+1].plot(s[:50], 'b-')
    axes[1,i+1].axvline(x=k, color='r', linestyle='–')
    axes[1,i+1].set_title(f"前50个奇异值\\n(红线=k)")
    axes[1,i+1].grid(True)

    plt.tight_layout()
    plt.show()

    print("✅ 观察:k=15时,图像清晰且压缩率>70%,噪声也被滤除!")

    1.6 运行结果

    在这里插入图片描述

    1.7 结果解读

  • 视觉效果:k=5时,图像轮廓可见但模糊,压缩率超80%;k=15时,图像细节基本还原,压缩率约50%;k=30时,图像与含噪原图几乎无差异,且噪声被大幅去除(低奇异值对应噪声,被舍弃);
  • 降噪原理:噪声对应的奇异值通常很小,筛选高奇异值重构时,自然过滤了噪声分量,实现“压缩+降噪”一举两得;
  • 实战优化:
    • 无需手动指定k,可通过“累计奇异值占比”自动确定(如保留累计占比95%的奇异值);
    • 彩色图像可分通道(R/G/B)分别进行SVD处理,再合并重构;
    • 对于超大图像,可先分块再进行SVD,减少内存占用。
  • 在这里插入图片描述

    1.8 核心代码解释

    代码片段核心作用
    np.linalg.svd(…) 执行奇异值分解,返回左奇异矩阵、奇异值数组、右奇异矩阵转置
    np.diag(s[:k]) 构建前k个奇异值的对角矩阵,保留核心信息量
    np.dot(U_k, np.dot(Sigma_k, Vt_k)) 矩阵乘法重构图像,仅使用前k个核心分量
    (1 – compressed_size / original_size) * 100 计算压缩率,量化存储优化效果

    场景2:人脸特征提取 —— “认出积木的标志性拼法”

    2.1 生活化理解

    你有10个朋友,每人用乐高搭了一张“笑脸”:

    • 朋友A的笑脸是“宽眼距+弯嘴”;
    • 朋友B的笑脸是“窄眼距+方嘴”;
    • 虽然积木颜色、小装饰有差异,但眼距、嘴型这些“标志性拼法”很稳定。 你记住这些核心特征后,下次看到新笑脸,不用看所有积木细节,只对比“眼距+嘴型”就能认出是谁搭的!

    对应人脸特征提取:

    • 乐高笑脸的“标志性拼法” = 人脸的核心特征(眼距、脸型、鼻梁高度)= 特征脸(PCA提取的主成分);
    • 新笑脸的“拼法对比” = 人脸验证(将待验证人脸投影到特征脸空间,对比特征向量的相似度);
    • PCA特征值分解 = 从海量人脸像素中,提取这些稳定的“标志性拼法”,实现快速识别。

    2.2 问题背景

    人脸识别是安防、考勤、手机解锁的核心技术,但直接用“像素对比”会遇到以下痛点:

  • 维度灾难:一张64×64的人脸图有4096个像素(4096维数据),直接对比计算量大、效率低;
  • 环境干扰:光照变化、表情差异、轻微遮挡会导致像素值大幅波动,但人脸的核心结构(眼距、脸型)是稳定的;
  • 冗余信息多:人脸像素中包含大量冗余细节(如皮肤纹理、细微皱纹),这些信息对识别无用,还会干扰结果。
  • 而PCA(基于特征值分解)的核心价值是:从高维像素中提取低维、稳定的核心特征(特征脸),用少量特征替代海量像素,实现高效、抗干扰的人脸识别。

    在这里插入图片描述

    2.3 核心思路

  • 数据准备:加载人脸数据集(内置模拟数据,无需网络),统一人脸尺寸,将每张人脸转为一维向量;
  • 去中心化:消除人脸整体光照/姿态的偏差,让特征提取更聚焦于人脸本身的结构;
  • 特征值分解:计算人脸数据的协方差矩阵,分解得到特征值和特征向量(特征脸);
  • 核心特征筛选:保留累计方差95%的前k个特征脸(只保留核心结构信息);
  • 人脸投影:将训练人脸和待验证人脸,都投影到特征脸空间,得到低维特征向量;
  • 验证对比:计算待验证人脸与训练人脸特征向量的欧式距离,对比阈值判断是否为同一人;
  • 可视化结果:展示特征脸、训练样本、验证结果,直观呈现识别逻辑。
  • 2.4 关键原理

  • 人脸的高维表示:一张h×w的人脸图,可转为h×w维的一维向量(如64×64→4096维),多张人脸构成高维数据矩阵X(形状:n_faces × (h×w));
  • 去中心化:对数据矩阵的每一列(每个像素维度)减去均值,得到去中心化矩阵X_centered,消除整体光照等全局干扰;
  • 协方差矩阵:计算X_centered^T × X_centered(简化版协方差矩阵),量化不同像素维度之间的关联程度;
  • 特征值分解:对协方差矩阵做特征值分解,得到:
    • 特征值:对应特征脸的“信息量大小”(特征值越大,该特征脸包含的人脸结构信息越多);
    • 特征向量:即“特征脸”,每个特征向量对应人脸的一个核心结构(如脸型、眼距);
  • 特征脸筛选:按特征值从大到小排序,选取前k个特征脸(累计方差≥95%),保留核心信息,舍弃冗余;
  • 人脸投影:将人脸向量与特征脸矩阵相乘,得到低维特征向量(如4096维→k维),实现“高维像素→低维核心特征”的转化;
  • 相似度计算:用欧式距离量化两个低维特征向量的相似度(距离越小,越可能是同一人),结合阈值判断结果。
  • 在这里插入图片描述

    2.5 代码示例

    # 导入所有需要的库(一站式导入,无需额外补充)
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.datasets import fetch_olivetti_faces
    from sklearn.utils import Bunch

    # 设置中文字体(Windows)
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    # ===================== 修复:解决数据集下载报错(两种方案,按需选择) =====================
    def get_face_dataset():
    """
    获取人脸数据集(优先本地,本地无则用内置简化数据,避免下载报错)
    :return: 符合sklearn格式的人脸数据集Bunch对象
    """

    try:
    # 方案1:尝试加载Olivetti数据集(若本地已缓存,可直接读取;若未缓存,跳过)
    # data_home='./sklearn_data':指定本地缓存路径,避免重复下载
    dataset = fetch_olivetti_faces(shuffle=True, random_state=42, data_home='./sklearn_data')
    print("成功加载Olivetti人脸数据集(本地缓存)")
    return dataset
    except Exception as e:
    # 方案2:内置简化人脸数据(无需下载,直接运行,解决网络报错问题)
    print(f"跳过远程下载(报错:{str(e)[:50]}…),使用内置简化人脸数据")
    np.random.seed(42)
    n_faces = 40 # 40张人脸
    h, w = 64, 64 # 人脸尺寸64×64
    # 生成模拟人脸数据(高斯分布,模拟人脸灰度图)
    faces = np.random.rand(n_faces, h, w) * 0.5 + 0.2 # 像素值集中在[0.2, 0.7],更贴近真实人脸
    # 模拟标签(4个人,每人10张图)
    targets = np.repeat(range(4), 10)
    # 构建符合sklearn格式的Bunch对象
    dataset = Bunch(
    images=faces,
    target=targets,
    data=faces.reshape(n_faces, h*w)
    )
    return dataset

    # ===================== 完整实现:人脸特征提取与验证 =====================
    def pca_eigenface_verification():
    """
    基于PCA(特征值分解)实现人脸验证,一站式输出对比结果
    """

    # ——————– 步骤1:加载并预处理人脸数据集(核心:统一尺寸,转一维向量) ——————–
    # 1.1 获取人脸数据集(修复下载报错,直接运行)
    dataset = get_face_dataset()
    faces = dataset.images
    targets = dataset.target
    h, w = faces[0].shape # 获取人脸图像尺寸
    n_faces, n_persons = len(faces), len(np.unique(targets))

    print(f"\\n人脸数据集信息:{n_persons}人,每人{int(n_faces/n_persons)}张图,图像尺寸{h}×{w}")

    # 1.2 预处理:将每张人脸转为一维向量(高维数据矩阵)
    # 形状:n_faces × (h*w) → 每张人脸是一行,每一列是一个像素维度
    face_vectors = faces.reshape(n_faces, h * w)

    # 1.3 去中心化(核心:消除整体光照/姿态偏差,让特征提取更准确)
    # 每一列(像素维度)减去均值,使每个像素维度的均值为0
    face_vectors_mean = np.mean(face_vectors, axis=0) # 计算每个像素维度的均值
    face_vectors_centered = face_vectors face_vectors_mean

    # 1.4 选取样本(以第0个人的人脸为例,进行训练与验证)
    person_id = 0
    person_face_indices = np.where(targets == person_id)[0] # 找到第0个人的所有人脸索引

    # 确保有足够的样本(至少6张:5张训练,1张验证)
    if len(person_face_indices) < 6:
    person_face_indices = np.concatenate([person_face_indices] * 2)[:6]
    train_faces = face_vectors_centered[person_face_indices[:5]] # 前5张作为训练样本
    test_face = face_vectors_centered[person_face_indices[5]] # 第6张作为验证样本
    test_face_original = faces[person_face_indices[5]] # 验证人脸的原始图像(用于可视化)

    print(f"选取第{person_id}个人:{len(person_face_indices)}张图,训练用5张,验证用1张")

    # ——————– 步骤2:特征值分解提取特征脸(核心:从协方差矩阵中抓核心特征) ——————–
    # 2.1 计算简化协方差矩阵(核心:避免高维矩阵内存溢出,等价于原始协方差矩阵)
    # 原始协方差矩阵形状:(h*w)×(h*w)(4096×4096),计算量大;简化为n×n(5×5),效率更高
    cov_matrix = np.dot(train_faces.T, train_faces) / len(train_faces)

    # 2.2 特征值分解(得到特征值和特征向量,特征向量就是"特征脸")
    eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)

    # 2.3 处理复数类型(修复新手常见报错:部分场景下会生成复数,转为实数)
    eigenvalues = np.real(eigenvalues)
    eigenvectors = np.real(eigenvectors)

    # 2.4 自动筛选k值(保留累计方差95%的核心特征脸,无需手动指定)
    total_variance = np.sum(eigenvalues)
    cumulative_variance = np.cumsum(eigenvalues) / total_variance
    k = np.argmax(cumulative_variance >= 0.95) + 1 # 找到第一个累计方差≥95%的索引
    k = max(k, 2) # 确保k至少为2,避免无有效特征脸
    print(f"保留累计方差95%,需要选取{k}个特征脸")

    # 2.5 排序:特征值从大到小,对应特征脸的重要性从高到低
    sorted_indices = np.argsort(eigenvalues)[::1] # 倒序排序,获取索引
    eigenvalues_sorted = eigenvalues[sorted_indices]
    eigenvectors_sorted = eigenvectors[:, sorted_indices]

    # 2.6 构建特征脸矩阵(k个核心特征脸,形状:k × (h*w))
    eigenfaces = eigenvectors_sorted[:, :k].T

    # ——————– 步骤3:人脸投影与验证(核心:对比低维特征向量的相似度) ——————–
    # 3.1 训练人脸投影到特征脸空间(转为低维特征向量)
    train_projections = np.dot(train_faces, eigenfaces.T) # 形状:5×k,每张训练人脸对应一个k维特征向量

    # 3.2 计算训练样本的平均特征向量(作为"标准模板")
    mean_train_projection = np.mean(train_projections, axis=0)

    # 3.3 待验证人脸投影到特征脸空间
    test_projection = np.dot(test_face, eigenfaces.T) # 形状:1×k,验证人脸的低维特征向量

    # 3.4 计算欧式距离(量化相似度:距离越小,越可能是同一人)
    distance = np.linalg.norm(test_projection mean_train_projection)

    # 3.5 自动设定阈值(经验公式:训练样本距离均值的1.5倍,避免手动调参)
    train_distances = np.linalg.norm(train_projections mean_train_projection, axis=1)
    threshold = np.mean(train_distances) * 1.5
    is_same_person = distance < threshold

    # ——————– 步骤4:可视化所有结果(一站式对比,无需分开查看) ——————–
    fig = plt.figure(figsize=(18, 8))

    # 4.1 子图1:前16张核心特征脸(可视化"核心调料")
    ax1 = fig.add_subplot(1, 3, 1)
    n_show_eigenfaces = min(16, k)
    for i in range(n_show_eigenfaces):
    # 特征脸转图像格式,绘制到画布
    eigenface = eigenvectors_sorted[:, i].reshape(h, w)
    ax1.imshow(eigenface, cmap='gray', extent=[(i%4)*w, (i%4+1)*w, (3i//4)*h, (4i//4)*h])
    ax1.set_title(f"前{n_show_eigenfaces}张核心特征脸(累计方差95%)", fontsize=12)
    ax1.axis('off')

    # 4.2 子图2:训练样本与待验证人脸
    ax2 = fig.add_subplot(1, 3, 2)
    # 绘制训练样本(5张人脸横向排列)
    train_faces_show = train_faces.reshape(5, h, w)
    for i in range(5):
    ax2.imshow(train_faces_show[i], cmap='gray', extent=[i*w, (i+1)*w, 0, h])
    # 绘制待验证人脸(在训练样本下方)
    ax2.imshow(test_face_original, cmap='gray', extent=[1*w, 2*w, h, 0])
    ax2.text(2.5*w, h/2, "待验证人脸", ha='center', fontsize=10)
    ax2.set_title(f"训练样本(第{person_id}人)+ 待验证人脸", fontsize=12)
    ax2.axis('off')

    # 4.3 子图3:验证结果(直观展示是否为同一人)
    ax3 = fig.add_subplot(1, 3, 3)
    ax3.text(0.5, 0.7, f"欧式距离:{distance:.4f}", ha='center', fontsize=14)
    ax3.text(0.5, 0.5, f"阈值:{threshold:.4f}", ha='center', fontsize=14)
    result_text = "是" if is_same_person else "否"
    result_color = "green" if is_same_person else "red"
    ax3.text(0.5, 0.3, f"是否为同一人:{result_text}", ha='center', fontsize=14, color=result_color)
    ax3.set_title("人脸验证结果", fontsize=12)
    ax3.axis('off')

    # 4.4 调整布局,显示图像
    plt.tight_layout()
    plt.show()

    # ===================== 运行函数(直接执行,无需额外修改) =====================
    if __name__ == "__main__":
    pca_eigenface_verification()

    2.6 运行结果

    在这里插入图片描述

    2.7 结果解读

  • 特征脸可视化:子图1的“特征脸”是人脸的核心结构(如第1张对应整体脸型,后续对应眼距、嘴型等局部特征),虽然看起来模糊,但包含了识别所需的关键信息;
  • 样本与验证:子图2展示了第0人的5张训练样本和1张待验证人脸,训练样本的像素细节有差异,但核心结构一致;
  • 验证逻辑:子图3中,欧式距离<阈值,判定为同一人——这是因为待验证人脸的核心特征(投影后的低维向量)与训练样本的平均特征高度相似;
  • 抗干扰性:即使待验证人脸有轻微表情/光照变化,只要核心结构(特征脸对应的维度)稳定,就能正确识别。
  • 在这里插入图片描述

    2.8 核心代码解释

    代码片段核心作用
    face_vectors = faces.reshape(n_faces, h*w) 将二维人脸图像转为一维向量,构建高维数据矩阵
    centered = data – mean_vec 去中心化,消除光照等全局干扰,聚焦人脸本身结构
    np.linalg.eigh(cov) 对协方差矩阵做特征值分解,得到特征值(信息量)和特征向量(特征脸)
    eigenfaces = eigvecs[:, :k] 选取前k个特征脸,保留累计方差95%的核心信息
    test_proj = test_face @ eigenfaces 将待验证人脸投影到特征脸空间,得到低维核心特征
    np.linalg.norm(test_proj – mean_train_proj) 计算欧式距离,量化特征向量的相似度

    2.9 实战优化

  • 数据集扩展:用真实人脸数据集(如LFW)替代模拟数据,提升特征脸的泛化能力;
  • 阈值优化:通过交叉验证确定更合理的阈值,平衡识别准确率与召回率;
  • 结合LDA:在PCA后加入线性判别分析(LDA),进一步增强不同人脸的区分度;
  • 实时应用:将特征脸预存为模板,待验证人脸实时投影后对比距离,实现快速识别。

  • 场景3:电影推荐 —— “猜你喜欢哪种积木组合”

    3.1 生活化理解

    你有5个朋友,每人喜欢的积木组合不同:

    • 朋友A喜欢“带轮子+红色”的积木;
    • 朋友B喜欢“带翅膀+蓝色”的积木; 现在新到一款“带轮子+红色”的积木套装,你不用问朋友A,也能直接推荐给他!

    对应电影推荐:

    • 朋友的“积木偏好” = 用户核心特征(如喜欢动作片/喜剧片)= SVD分解后的用户低维特征;
    • 积木的“组合属性” = 电影核心特征(如动作/喜剧标签)= SVD分解后的电影低维特征;
    • SVD推荐 = 匹配用户特征与电影特征,预测用户对未看电影的喜好程度,推荐评分最高的电影。

    3.2 问题背景

    在电商、视频平台的推荐系统中,直接用“用户-电影评分”做推荐会遇到以下痛点:

  • 数据稀疏:用户通常只给少数电影评分(如5个用户仅对6部电影的部分条目评分),直接对比用户/电影相似度会因数据缺失导致结果失真;
  • 维度冗余:每部电影是一个独立维度,若有1万部电影则是1万维数据,计算效率极低;
  • 个性化不足:简单的“热门推荐”无法匹配用户的独特偏好(如用户0喜欢电影0、4,但热门电影可能不符合其口味)。
  • 而SVD(奇异值分解)的核心价值是:从稀疏评分矩阵中提取用户和电影的低维核心特征,补全缺失评分,实现精准的个性化推荐。

    在这里插入图片描述

    3.3 核心思路

  • 数据准备:构建稀疏用户-电影评分矩阵(0表示未评分),模拟真实场景的评分缺失;
  • 数据预处理:填充用户平均评分(SVD对0值敏感,填充后提升分解稳定性);
  • SVD分解:对填充后的评分矩阵做奇异值分解,提取用户低维特征、电影低维特征;
  • 核心特征筛选:保留累计方差90%的前k个奇异值(只保留用户/电影的核心特征);
  • 评分补全:用低维特征重构评分矩阵,补全用户未评分电影的预测分数;
  • 个性化推荐:筛选用户未观看的电影,按预测评分排序,推荐Top N电影;
  • 可视化结果:展示原始评分矩阵、推荐结果及推荐理由,直观呈现推荐逻辑。
  • 3.4 关键原理

  • 稀疏评分矩阵:用二维矩阵R表示用户-电影评分(行=用户,列=电影),元素值1-5是评分,0是未评分(稀疏值);
  • SVD奇异值分解:对填充后的评分矩阵R做SVD分解:

    R

    =

    U

    Σ

    V

    T

    R = U \\Sigma V^T

    R=UΣVT

    • U(用户特征矩阵):行=用户,列=用户核心特征(如“喜欢动作片”“喜欢喜剧片”);
    • Σ(奇异值矩阵):对角线上的奇异值对应“用户-电影特征的关联强度”(值越大,该特征对评分的影响越显著);
    • V^T(电影特征矩阵):行=电影核心特征,列=电影(每列对应一部电影的核心特征);
  • 低维重构:保留前k个奇异值,用低维特征重构评分矩阵:

    R

    k

    =

    U

    k

    Σ

    k

    V

    k

    T

    R_k = U_k \\Sigma_k V_k^T

    Rk=UkΣkVkT 重构后的R_k中,0值被补全为预测评分(代表用户对未看电影的喜好程度);

  • 个性化推荐:对指定用户,筛选原始矩阵中为0的列(未看电影),按R_k中的预测评分排序,取Top N即为推荐结果。
  • 在这里插入图片描述

    3.5 代码示例

    # 导入所有需要的库(一站式导入,无需额外补充)
    import numpy as np
    import matplotlib.pyplot as plt

    # 设置中文字体(Windows)
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False

    # ===================== 完整实现:SVD个性化电影推荐 =====================
    def svd_movie_recommendation(top_n=2):
    """
    用SVD实现电影评分补全与个性化推荐,一站式输出对比结果
    :param top_n: 推荐Top N部电影
    """

    # ——————– 步骤1:构建稀疏用户-电影评分矩阵(核心:模拟真实稀疏数据) ——————–
    # 1.1 模拟数据:5个用户,6部电影,评分1-5,0表示未评分(稀疏值)
    user_movie_ratings = np.array([
    [5, 3, 0, 1, 4, 0], # 用户0:喜欢电影0、4,不喜欢电影3
    [4, 0, 0, 1, 3, 2], # 用户1:喜欢电影0、4,喜欢电影5
    [1, 1, 0, 5, 0, 4], # 用户2:喜欢电影3、5,不喜欢电影0、1
    [1, 0, 0, 4, 0, 5], # 用户3:喜欢电影3、5,不喜欢电影0
    [0, 1, 5, 4, 2, 0] # 用户4:喜欢电影2、3,喜欢电影4
    ])

    n_users, n_movies = user_movie_ratings.shape
    user_names = [f"用户{i}" for i in range(n_users)]
    movie_names = [f"电影{i}" for i in range(n_movies)]

    # 1.2 打印原始信息
    print(f"评分矩阵信息:{n_users}个用户,{n_movies}部电影")
    print("原始稀疏评分矩阵(0=未评分):")
    print(user_movie_ratings)

    # 1.3 预处理:填充用户平均评分(SVD对0值敏感,填充后提升分解效果)
    user_means = []
    for i in range(n_users):
    # 提取用户i的有效评分(非0值),计算平均值
    valid_ratings = user_movie_ratings[i][user_movie_ratings[i] != 0]
    user_mean = np.mean(valid_ratings) if len(valid_ratings) > 0 else 3.0 # 无有效评分则填充3.0(中等评分)
    user_means.append(user_mean)

    # 1.4 构建填充后的评分矩阵
    ratings_filled = user_movie_ratings.copy().astype(np.float64)
    for i in range(n_users):
    # 对用户i的未评分项(0)填充用户平均评分
    ratings_filled[i, ratings_filled[i] == 0] = user_means[i]

    # 1.5 打印填充后的矩阵
    print("\\n填充用户均值后的评分矩阵(优化SVD效果):")
    print(np.round(ratings_filled, 2))

    # ——————– 步骤2:SVD分解与评分矩阵重构(核心:提取核心特征,补全缺失评分) ——————–
    # 2.1 SVD分解(紧凑版,减少计算量)
    U, s, Vt = np.linalg.svd(ratings_filled, full_matrices=False)

    # 2.2 自动筛选k值(保留累计方差90%的核心特征,无需手动指定)
    total_variance = np.sum(s ** 2)
    cumulative_variance = np.cumsum(s ** 2) / total_variance
    k = np.argmax(cumulative_variance >= 0.9) + 1 # 找到第一个累计方差≥90%的索引
    print(f"\\n保留累计方差90%,需要选取{k}个奇异值")

    # 2.3 构建低维特征矩阵
    U_k = U[:, :k] # 用户低维特征矩阵:n_users × k
    Sigma_k = np.diag(s[:k]) # 奇异值矩阵:k × k
    Vt_k = Vt[:k, :] # 电影低维特征矩阵:k × n_movies

    # 2.4 重构评分矩阵(补全缺失评分,核心公式:Rk = Uk × Σk × Vtk)
    ratings_reconstructed = np.dot(U_k, np.dot(Sigma_k, Vt_k))

    # 2.5 限制评分范围在[1, 5](符合实际评分逻辑,避免出现0或6以上的无效评分)
    ratings_reconstructed = np.clip(ratings_reconstructed, 1, 5)

    # 2.6 打印补全后的矩阵
    print("\\n补全后的评分矩阵(预测评分):")
    print(np.round(ratings_reconstructed, 2))

    # ——————– 步骤3:个性化电影推荐(核心:为指定用户推荐Top N电影) ——————–
    # 3.1 选择待推荐用户(以用户0为例,可修改为其他用户)
    target_user = 0
    print(f"\\n为{user_names[target_user]}进行电影推荐")

    # 3.2 筛选用户未评分的电影(原始评分矩阵中为0的项)
    unrated_movie_indices = np.where(user_movie_ratings[target_user] == 0)[0]
    unrated_movies = [movie_names[i] for i in unrated_movie_indices]
    unrated_ratings = [ratings_reconstructed[target_user][i] for i in unrated_movie_indices]

    # 3.3 按预测评分倒序排序,选取Top N电影
    recommend_pairs = sorted(zip(unrated_movies, unrated_ratings), key=lambda x: x[1], reverse=True)
    top_recommendations = recommend_pairs[:top_n]

    # ——————– 步骤4:可视化所有结果(一站式对比,补充详细描述) ——————–
    fig = plt.figure(figsize=(22, 10)) # 适当放大画布,容纳更多描述信息

    # 4.1 子图1:原始稀疏评分矩阵(带数值标注+详细含义描述)
    ax1 = fig.add_subplot(1, 2, 1)
    im1 = ax1.imshow(user_movie_ratings, cmap='YlOrRd', vmin=0, vmax=5)
    ax1.set_xticks(range(n_movies))
    ax1.set_yticks(range(n_users))
    ax1.set_xticklabels(movie_names)
    ax1.set_yticklabels(user_names)
    ax1.set_title("原始稀疏评分矩阵(0=未评分)", fontsize=14)

    # 添加数值标注,直观查看
    for i in range(n_users):
    for j in range(n_movies):
    ax1.text(j, i, int(user_movie_ratings[i, j]), ha='center', va='center', fontsize=10)

    # 补充原始矩阵含义描述(位于子图下方,左对齐)
    matrix_explain = [
    "📌 矩阵含义:",
    f" – 行:{n_users}个用户,列:{n_movies}部电影",
    " – 数值1-5:用户对电影的评分(分数越高越喜欢)",
    " – 数值0:用户未观看/未评分(稀疏缺失值)",
    f" – 示例:用户0给电影0打5分(最喜欢),给电影3打1分(最不喜欢)"
    ]
    # 把描述文本添加到子图下方(调整y坐标,避免遮挡图表)
    for idx, text in enumerate(matrix_explain):
    ax1.text(1, n_users + 0.2 + idx*0.5, text, ha='left', va='top', fontsize=10, transform=ax1.transData)
    # 调整x轴范围,容纳左侧描述文本
    ax1.set_xlim(2, n_movies)
    ax1.set_ylim(1, n_users + len(matrix_explain)*0.5 + 0.5)

    plt.colorbar(im1, ax=ax1, shrink=0.8)

    # 4.2 子图2:个性化推荐结果(补充推荐理由+详细说明)
    ax2 = fig.add_subplot(1, 2, 2)
    ax2.text(0.5, 0.9, f"为{user_names[target_user]}推荐Top{top_n}电影",
    ha='center', fontsize=16, fontweight='bold')

    # 打印Top N推荐
    for i, (movie, rating) in enumerate(top_recommendations):
    ax2.text(0.5, 0.75 i*0.12, f"{i+1}. {movie},预测评分:{rating:.2f}",
    ha='center', fontsize=14, color='blue')

    # 补充推荐理由(核心逻辑说明)
    recommend_reason = [
    "📌 推荐逻辑:",
    " 1. 用SVD分解提取用户/电影核心特征(保留90%信息量)",
    " 2. 重构评分矩阵,补全用户未评分电影的预测分数",
    " 3. 筛选未观看电影中预测评分最高的Top N部",
    f" 4. 本次推荐:基于用户0的观影偏好(喜欢电影0、4),预测其对{top_recommendations[0][0]}评分最高",
    f" 5. 未观看电影:{', '.join(unrated_movies)}(按预测评分从高到低排序)"
    ]
    # 把推荐理由添加到下方(左对齐,居中展示)
    for idx, text in enumerate(recommend_reason):
    ax2.text(0.5, 0.5 idx*0.08, text, ha='center', va='top', fontsize=11, color='darkgreen')

    ax2.set_title("个性化电影推荐结果(附推荐理由)", fontsize=14)
    ax2.axis('off')

    # 4.3 调整布局,显示图像
    plt.tight_layout()
    plt.show()

    # 4.4 打印详细推荐报告
    print("\\n=== 个性化电影推荐报告 ===")
    print(f"用户:{user_names[target_user]}")
    print(f"未观看电影:{', '.join(unrated_movies)}")
    print(f"Top{top_n}推荐:")
    for i, (movie, rating) in enumerate(top_recommendations):
    print(f" {i+1}. {movie},预测评分:{rating:.2f}")

    # ===================== 运行函数(直接执行,无需额外修改) =====================
    if __name__ == "__main__":
    svd_movie_recommendation(top_n=2)

    3.6 运行结果

    在这里插入图片描述

    3.7 结果解读

  • 原始矩阵含义:子图1清晰展示了5个用户对6部电影的评分,0代表未观看,数值1-5代表喜好程度;
  • 推荐逻辑:子图2明确了推荐的核心步骤——SVD提取特征→补全评分→筛选Top N,且说明推荐理由(用户0的偏好与电影2的特征高度契合);
  • 推荐效果:用户0未观看“电影2、5”,SVD预测其对电影2的评分(3.51)高于电影5(2.95),因此优先推荐电影2,符合用户的潜在喜好。
  • 在这里插入图片描述

    3.8 核心代码解释

    代码片段核心作用
    ratings_filled[i][ratings[i] == 0] = mean 填充用户平均评分,解决SVD对0值敏感的问题
    np.linalg.svd(ratings_filled) 对填充后的矩阵做SVD分解,提取用户/电影核心特征
    ratings_completed = U_k @ Sigma_k @ Vt_k 用低维特征重构评分矩阵,补全未评分项的预测分数
    unrated_indices = np.where(user_movie_ratings[target_user] == 0) 筛选用户未观看的电影
    sorted(zip(unrated_movies, unrated_ratings), reverse=True) 按预测评分倒序排序,选取Top N推荐电影

    3.9 实战优化

  • 真实数据集:用MovieLens、Netflix等真实评分数据集替代模拟数据,提升推荐准确性;
  • 正则化SVD:使用FunkSVD(带正则项的SVD),避免过拟合,提升泛化能力;
  • 实时更新:定期重新训练SVD模型,结合用户最新评分动态调整推荐结果;
  • 多特征融合:加入电影类型、用户年龄等额外特征,进一步提升推荐的个性化程度。

  • 三大场景对比总结

    场景数据类型核心操作保留什么?舍弃什么?
    图像压缩 图像矩阵(非方阵) SVD分解 高奇异值(轮廓、结构) 低奇异值(噪声、纹理)
    人脸识别 人脸向量矩阵(方阵协方差) PCA特征分解 高特征值(五官比例) 低特征值(光照、表情微变)
    电影推荐 评分矩阵(稀疏非方阵) SVD分解 高奇异值(用户/电影隐因子) 低奇异值(随机评分噪声)

    1 三大场景的核心思路

    三个场景本质都是**“从海量/稀疏数据中抓核心,用少数关键信息解决问题”**:

    • 图像压缩:抓“图像核心结构”,舍弃“噪声/冗余细节”;
    • 人脸识别:抓“人脸稳定特征”,舍弃“光照/表情干扰”;
    • 电影推荐:抓“用户/电影核心偏好”,舍弃“稀疏评分噪声”。

    2 三大场景的统一流程(5步通用逻辑)

    无论场景如何变化,特征值/奇异值分解的落地流程高度一致:

  • 数据准备:将原始数据(图像、人脸、评分)转为矩阵格式;
  • 预处理:消除全局干扰(图像归一化、人脸去中心化、评分填充均值);
  • 分解提取核心:用特征值分解(PCA)或奇异值分解(SVD),得到“核心方向(特征向量)”和“重要程度(特征值/奇异值)”;
  • 筛选核心信息:保留前k个高重要程度的分量(累计方差90%-95%);
  • 应用核心信息:
    • 图像压缩:用核心分量重构图像;
    • 人脸识别:用核心特征做相似度对比;
    • 电影推荐:用核心特征补全评分并推荐。
  • 3 三大场景的底层原理(共同数学本质)

    所有操作都围绕**“特征值/奇异值的‘重要性排序’”**展开:

    • 特征值/奇异值越大,对应“信息越核心、越稳定”;
    • 特征向量对应“数据的核心方向”(图像结构、人脸特征、用户偏好);
    • 只保留高值分量,就能用少量信息还原/解决问题,同时过滤冗余与噪声。

    在这里插入图片描述

    最终提炼:“抓核心”的通用思维

    共同本质: 用少数“稳定方向”(特征向量) + “重要程度”(特征值),代替海量原始数据。


    给新手的终极建议

  • 不要怕数学:特征值 = “重要程度排序”,特征向量 = “核心方向”,就这么简单;
  • 先跑通代码:复制上面任意一段,改改 k 值,看效果变化;
  • 联系生活:每次遇到高维数据,就问自己:“哪些是‘轮子’,哪些是‘装饰贴纸’?”
  • 特征值分解,不是魔法,而是一种聪明的偷懒—— 它帮我们在信息爆炸的世界里,只关注真正重要的东西。

    在这里插入图片描述


    结语:从“数学公式”到“生活智能”

    当你打开手机相册自动压缩的照片、刷到精准匹配你口味的短视频、解锁手机时的人脸验证——其实这些日常智能的背后,都藏着“特征值/奇异值分解”的身影。

    它没有复杂的神经网络那么“炫酷”,却像一把**“信息筛子”**:帮我们从冗余的像素里抓出图像的轮廓,从杂乱的评分里摸透用户的偏好,从多变的人脸中锁定稳定的五官特征。

    在这里插入图片描述

    本篇三个场景的本质,都是**“用最少的‘核心信息’,解决最多的‘实际问题’”**——这不仅是AI的底层逻辑,更是我们应对复杂世界的思维方式:

    • 不用纠结所有细节,先抓“稳定的核心方向”;
    • 不用保留所有信息,只留“重要的关键分量”。

    下次再遇到“数据太多、干扰太杂”的问题时,不妨想想这三个场景:先找“特征向量”(核心方向),再看“特征值”(重要程度)——或许你也能像AI一样,轻松抓住问题的本质。

    (如果觉得这篇实战对你有帮助,欢迎点赞收藏,后续会分享更多“数学落地AI”的干货~)

    赞(0)
    未经允许不得转载:171主机测评 » 进阶篇-9-数学篇-8--特征值与特征向量实战:三大AI场景图片压缩,人脸识别,电影推荐深度剖析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址