Machine-Learning-Mastery-线性代数教程-四-

323 阅读1小时+

Machine Learning Mastery 线性代数教程(四)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

线性代数回顾的没有废话的指南

原文: machinelearningmastery.com/no-bullshit-guide-to-linear-algebra-review/

有许多书籍介绍了线性代数领域。

大多数是针对本科生的教科书,并且充满了几乎没有相关性的理论偏见,并且大多分散了初学者或实践者对该领域的注意力。

在这篇文章中,您将发现“无线性代数的废话指南”这本书,它简单介绍了线性代数领域,并假设没有先前的数学知识。

阅读这篇文章后,你会知道:

  • 关于本书对初学者或从业者的目标和好处。
  • 本章内容和每章介绍的一般主题。
  • 一个选定的阅读列表,针对希望快速加速的机器学习从业者。

让我们开始吧。

No Bullshit Guide To Linear Algebra Review

没有废话指南线性代数评论 照片由 Ralf Kayser ,保留一些权利。

图书概述

该书提供了线性代数的介绍,可与本科大学课程相媲美。

这本书的关键方法是没有废话,直截了当。这意味着激光专注于给定的操作或技术,没有(或很少)绕道或离题。

这本书是由 Ivan Savov 编写的,第二版于 2017 年发布.Ivan 拥有电气工程学士学位和硕士和博士学位。在物理学方面,他作为数学和物理学的私人导师在过去的 15 年里一直工作。他知道这个主题以及学生遇到困难的地方。

Amazon Image

没有先决条件数学

这本书对于机器学习从业者来说是一本很好的书是因为这本书是独立的。它没有假设任何先前的数学背景,所有先决条件数学,这是最小的,在第一章标题为“_ 数学基础 _”中有所介绍。

如果你从未学过线性代数,或者你几十年前在学校学过它并且几乎忘记了一切,那么它是完美的书。

练习练习

使本书适合机器学习从业者的另一个方面是它包括练习。

每个部分都以一些 pop-quiz 样式问题结束。

每章最后都有一个问题集供您使用。

最后,附录 A 提供了本书中所有练习的答案。

目录

本节提供了本书目录的摘要。

  1. 数学基础。涵盖了开始学习线性代数所需的必备数学主题。主题包括数字,函数,三角函数,复数和集合表示法。
  2. 线性代数简介。向量和矩阵代数的介绍,是线性代数的基础。主题包括向量和矩阵运算和线性。
  3. 计算线性代数。本章介绍了在开始实现线性代数时遇到的问题,并且必须以任何规模处理操作。主题包括矩阵方程,矩阵乘法和行列式。给出了一些 Python 示例。
  4. 线性代数的几何方面。涵盖了向量代数的几何直觉,这是很常见的。主题包括直线和平面,投影和向量空间。
  5. 线性变换。涵盖线性代数的核心光纤,正如 Ivan 所描述的那样。介绍线性变换。
  6. 理论线性代数。在应用之前涵盖矩阵代数的最后几步。涵盖了矩阵分解方法,矩阵类型等主题。
  7. 申请。本章介绍了线性代数应用于电子,图形,计算机图形等各种领域的令人印象深刻的列表。令人印象深刻的一章让整本书中学到的方法具体化。
  8. 概率论。在包括马尔可夫链和 PageRank 算法的线性代数的背景下提供概率论的速成课程。
  9. 量子力学。通过线性代数的镜头提供量子力学的速成课程,线性代数是作者的专业领域。

机器学习从业者的选择

这本书很棒,如果你真的喜欢它,我建议你从封面到封面阅读。

但是,作为机器学习从业者,您不需要全部阅读。

以下是我建议快速获得线性代数的书中所选阅读的列表:

  • 概念图。页面 v。在目录之后直接提供了思维导图类型图的集合,其中显示了书中的概念,以及实际上线性代数领域中的概念。如果您是视觉思考者,这些可能有助于将各个部分组合在一起。
  • 第 1.15 节,载体。在任何向量代数之前,简要介绍向量。有用的背景。
  • 第二章,线性代数简介。第 101-130 页。阅读整章。它涵盖:
    • 线性代数中术语的定义。
    • 向量运算,如算术运算和向量范数。
    • 矩阵运算,如算术运算和点积运算。
    • 线性以及这个关键概念究竟在线性代数中意味着什么
    • 线性代数(几何,理论等)的不同方面如何相关的概述。
  • 第 3.2 节矩阵方程。第 147.页包括用于计算矩阵运算的解释和清晰图表,尤其是必须知道的矩阵乘法
  • 第 6.1 节特征值和特征向量。第 262 页。介绍特征分解,该特征分解用作主成分分析等方法中的关键操作。
  • 第 6.2 节特殊类型的矩阵。第 275 页介绍了各种不同类型的矩阵,如对角线,对称,正交等。
  • 第 6.6 节矩阵分解。引入矩阵分解方法,重新覆盖特征分解,但也包括 LU,QR 和奇异值分解。
  • 第 7.7 节最小二乘近似解。第 241 页。最小二乘矩阵公式的介绍,称为线性最小二乘法。
  • 附录 B,表示法。数学和线性代数符号的摘要。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

摘要

在这篇文章中,您发现了一本书“线性代数的无废话指南”,该书提供了对线性代数领域的温和介绍,并假设没有先前的数学知识。

具体来说,你学到了:

  • 关于本书对初学者或从业者的目标和好处。
  • 本章内容和每章介绍的一般主题。
  • 一个选定的阅读列表,针对希望快速加速的机器学习从业者。

你读过这本书吗?你觉得呢? 请在下面的评论中告诉我。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

如何在 NumPy 中为行和列设置轴

原文:machinelearningmastery.com/numpy-axis-…

NumPy 数组为在 Python 中存储和操作数据提供了一种快速有效的方法。

它们对于在机器学习中将数据表示为向量和矩阵特别有用。

NumPy 数组中的数据可以通过列和行索引直接访问,这相当简单。然而,有时我们必须对数据数组执行操作,例如按行或列对值求和或求平均值,这需要指定操作的轴。

不幸的是,NumPy 数组上的列和行操作与我们从行和列索引中获得的直觉不匹配,这可能会给初学者和经验丰富的机器学习从业者带来困惑。具体来说,像求和这样的操作可以使用轴=0 按列执行,使用轴=1 按行执行。

在本教程中,您将发现如何按行和按列访问和操作 NumPy 数组。

完成本教程后,您将知道:

  • 如何用数据的行和列定义 NumPy 数组?
  • 如何通过行索引和列索引访问 NumPy 数组中的值。
  • 如何按行轴和列轴对 NumPy 数组执行操作?

我们开始吧。

How to Set NumPy Axis for Rows and Columns in Python

如何在 Python 中为行和列设置 NumPy 轴 图片由乔纳森·卡特勒提供,保留部分权利。

教程概述

本教程分为三个部分;它们是:

  1. 带行和列的 NumPy 数组
  2. NumPy 数组中的数据行和列
  3. 按行和列的 NumPy 数组操作
    1. 轴=无阵列操作
    2. 轴=0 列式操作
    3. 轴=1 行操作

带行和列的 NumPy 数组

在深入讨论 NumPy 阵列轴之前,让我们刷新一下对 NumPy 阵列的了解。

通常在 Python 中,我们使用数字列表或数字列表。例如,我们可以将由两行三个数字组成的二维矩阵定义为数字列表,如下所示:

...
# define data as a list
data = [[1,2,3], [4,5,6]]

NumPy 数组允许我们以有效的方式定义和操作向量和矩阵,例如,比简单的 Python 列表更有效。NumPy 阵列被称为 NDArrays,并且可以具有几乎任意数量的维度,尽管在机器学习中,我们最常用的是 1D 和 2D 阵列(或用于图像的 3D 阵列)。

例如,我们可以通过 asarray()函数将列表矩阵列表转换为 NumPy 数组:

...
# convert to a numpy array
data = asarray(data)

我们可以直接打印数组,并期望看到两行数字,其中每行有三个数字或列。

...
# summarize the array content
print(data)

我们可以通过打印“ shape ”属性来总结一个数组的维度,这个属性是一个元组,其中元组中值的个数定义维度的个数,每个位置的整数定义维度的大小。

例如,对于两行三列,我们期望我们的数组的形状是(2,3)。

...
# summarize the array shape
print(data.shape)

将这些结合在一起,下面列出了一个完整的例子。

# create and summarize a numpy array
from numpy import asarray
# define data as a list
data = [[1,2,3], [4,5,6]]
# convert to a numpy array
data = asarray(data)
# summarize the array content
print(data)
# summarize the array shape
print(data.shape)

运行该示例将我们的数据定义为列表,将其转换为 NumPy 数组,然后打印数据和形状。

我们可以看到,当数组被打印时,它有两行三列的预期形状。然后我们可以看到印刷的形状符合我们的期望。

[[1 2 3]
 [4 5 6]]
(2, 3)

有关 NumPy 数组基础知识的更多信息,请参见教程:

目前为止,一切顺利。

但是我们如何按行或列访问数组中的数据呢?更重要的是,我们如何按行或按列对数组执行操作?

让我们仔细看看这些问题。

NumPy 数组中的数据行和列

形状”属性总结了我们数据的维度。

重要的是,第一维定义行数,第二维定义列数。例如(2,3)定义了一个两行三列的数组,正如我们在上一节中看到的。

我们可以通过从索引 0 枚举到数组形状的第一维,例如形状[0],来枚举数组中的每一行数据。我们可以通过行和列索引来访问数组中的数据。

例如,数据[0,0]是第一行和第一列的值,而数据[0,]是第一行和所有列的值,例如我们矩阵中完整的第一行。

下面的示例枚举数据中的所有行,并依次打印每一行。

# enumerate rows in a numpy array
from numpy import asarray
# define data as a list
data = [[1,2,3], [4,5,6]]
# convert to a numpy array
data = asarray(data)
# step through rows
for row in range(data.shape[0]):
	print(data[row, :])

不出所料,结果显示了第一行数据,然后是第二行数据。

[1 2 3]
[4 5 6]

我们可以为列实现同样的效果。

也就是说,我们可以按列枚举数据。例如,数据[:,0]访问第一列的所有行。我们可以枚举从第 0 列到最后一列的所有列,由“形状属性的第二维度定义,例如形状【1】。

下面的示例通过枚举矩阵中的所有列来演示这一点。

# enumerate columns in a numpy array
from numpy import asarray
# define data as a list
data = [[1,2,3], [4,5,6]]
# convert to a numpy array
data = asarray(data)
# step through columns
for col in range(data.shape[1]):
	print(data[:, col])

运行该示例枚举并打印矩阵中的每一列。

假设矩阵有三列,我们可以看到结果是我们打印了三列,每列都是一维向量。也就是说,第 1 列(索引 0)具有值 1 和 4,第 2 列(索引 1)具有值 2 和 5,第 3 列(索引 2)具有值 3 和 6。

它看起来很有趣,因为我们的专栏看起来不像专栏;它们是侧翻的,而不是垂直的。

[1 4]
[2 5]
[3 6]

现在我们知道如何按列和行访问 numpy 数组中的数据了。

到目前为止,一切都很好,但是按列和数组对数组进行操作呢?那是下一个。

按行和列的 NumPy 数组操作

我们经常需要按列或按行对 NumPy 数组执行操作。

例如,我们可能需要按行或按列对数据矩阵的值求和或计算平均值。

这可以通过使用 sum()mean() NumPy 函数并指定要在其上执行操作的“来实现。

我们可以将轴指定为要执行操作的维度,基于我们如何解释数组的“形状”以及如何索引数组中的数据,这个维度与我们的直觉不匹配。

因此,这给初学者造成了最大的困惑

也就是说,轴=0 将按列执行操作,轴=1 将按行执行操作。我们还可以将轴指定为无,这将对整个数组执行操作。

总之:

  • 轴=无:阵列式应用操作。
  • 轴=0 :对每列的所有行按列应用操作。
  • 轴=1 :逐行应用操作,跨越每行的所有列。

让我们用一个具体的例子来说明这一点。

我们将通过三个轴中的每一个轴对数组中的值求和。

轴=无阵列操作

在 NumPy 数组上执行操作时,设置轴=无将对整个数组执行操作。

这通常是大多数操作的默认值,如求和、求平均值、标准等。

...
# sum data by array
result = data.sum(axis=None)

下面的例子演示了对一个数组中的所有值求和,例如按数组操作。

# sum values array-wise
from numpy import asarray
# define data as a list
data = [[1,2,3], [4,5,6]]
# convert to a numpy array
data = asarray(data)
# summarize the array content
print(data)
# sum data by array
result = data.sum(axis=None)
# summarize the result
print(result)

运行该示例首先打印数组,然后按数组执行求和操作并打印结果。

我们可以看到数组有六个值,如果我们手动将它们相加,它们的总和将达到 21,并且按数组方式执行的求和操作的结果与这个期望值相匹配。

[[1 2 3]
 [4 5 6]]

21

轴=0 列式操作

在 NumPy 数组上执行操作时,将轴设置为 0 将按列执行操作,即跨每列的所有行执行操作。

...
# sum data by column
result = data.sum(axis=0)

例如,假设我们的数据有两行三列:

Data = [[1, 2, 3],
		 4, 5, 6]]

我们预计轴=0 的按列求和将产生三个值,每列一个,如下所示:

  • 第 1 列 : 1 + 4 = 5
  • 第 2 栏 : 2 + 5 = 7
  • 第 3 列 : 3 + 6 = 9

下面的示例演示了按列对数组中的值求和,例如按列操作。

# sum values column-wise
from numpy import asarray
# define data as a list
data = [[1,2,3], [4,5,6]]
# convert to a numpy array
data = asarray(data)
# summarize the array content
print(data)
# sum data by column
result = data.sum(axis=0)
# summarize the result
print(result)

运行该示例首先打印数组,然后逐列执行求和操作并打印结果。

我们可以看到数组有六个值,如预期的那样有两行三列;然后,我们可以看到列式运算的结果是一个有三个值的向量,其中一个值代表与我们的期望相匹配的每一列的总和。

[[1 2 3]
 [4 5 6]]
[5 7 9]

轴=1 行操作

在 NumPy 数组上执行操作时,设置轴=1 将逐行执行操作,即每行的所有列。

...
# sum data by row
result = data.sum(axis=1)

例如,假设我们的数据有两行三列:

Data = [[1, 2, 3],
		 4, 5, 6]]

我们预计轴=1 的行方向求和将产生两个值,每行一个,如下所示:

  • 第 1 排 : 1 + 2 + 3 = 6
  • 第二排 : 4 + 5 + 6 = 15

下面的示例演示了逐行对数组中的值求和,例如逐行操作。

# sum values row-wise
from numpy import asarray
# define data as a list
data = [[1,2,3], [4,5,6]]
# convert to a numpy array
data = asarray(data)
# summarize the array content
print(data)
# sum data by row
result = data.sum(axis=1)
# summarize the result
print(result)

运行该示例首先打印数组,然后逐行执行求和操作并打印结果。

我们可以看到数组有六个值,如预期的那样有两行三列;然后,我们可以在一个向量中看到逐行操作的结果,该向量有两个值,一个值是与我们的期望相匹配的每一行的总和。

[[1 2 3]
 [4 5 6]]
[ 6 15]

我们现在对如何在 NumPy 数组上执行操作时适当地设置 axis 有了具体的想法。

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

教程

蜜蜂

摘要

在本教程中,您发现了如何按行和按列访问和操作 NumPy 数组。

具体来说,您了解到:

  • 如何用数据的行和列定义 NumPy 数组?
  • 如何通过行索引和列索引访问 NumPy 数组中的值。
  • 如何按行轴和列轴对 NumPy 数组执行操作?

你有什么问题吗? 在下面的评论中提问,我会尽力回答。

主成分分析的可视化

原文:machinelearningmastery.com/principal-c…

最后更新于 2021 年 10 月 27 日

主成分分析是一种无监督的机器学习技术。主成分分析最常用的方法可能是降维。除了使用主成分分析作为数据准备技术,我们还可以使用它来帮助可视化数据。一幅画胜过千言万语。随着数据的可视化,我们更容易获得一些见解,并决定机器学习模型的下一步。

在本教程中,您将发现如何使用主成分分析可视化数据,以及如何使用可视化来帮助确定降维参数。

完成本教程后,您将知道:

  • 如何使用可视化的高维数据
  • 什么解释了主成分分析中的差异
  • 从高维数据的主成分分析结果中直观地观察解释的差异

我们开始吧。

可视化主成分分析 图片由莱万·戈卡兹提供,保留部分权利。

教程概述

本教程分为两部分;它们是:

  • 高维数据散点图
  • 可视化解释的差异

先决条件

对于本教程,我们假设您已经熟悉:

高维数据散点图

可视化是从数据中获得洞察力的关键一步。我们可以从可视化中了解到一个模式是否可以被观察到,从而估计哪个机器学习模型是合适的。

用二维来描绘事物是很容易的。通常,x 轴和 y 轴的散点图是二维的。用三维方式描绘事物有点挑战性,但并非不可能。例如,在 matplotlib 中,可以进行 3D 绘图。唯一的问题是在纸上或屏幕上,我们一次只能在一个视口或投影上查看 3D 图。在 matplotlib 中,这是由仰角和方位角的度数控制的。描绘四维或五维的事物是不可能的,因为我们生活在一个三维世界中,不知道如此高维度的事物会是什么样子。

这就是像主成分分析这样的降维技术发挥作用的地方。我们可以把维度缩小到两到三个,这样我们就可以把它可视化。让我们从一个例子开始。

我们从葡萄酒数据集开始,这是一个包含 13 个特征(即数据集是 13 维的)和 3 个类的分类数据集。共有 178 个样本:

from sklearn.datasets import load_wine
winedata = load_wine()
X, y = winedata['data'], winedata['target']
print(X.shape)
print(y.shape)
(178, 13)
(178,)

在 13 个特征中,我们可以选择任意两个并用 matplotlib 绘制(我们使用c参数对不同的类进行颜色编码):

...
import matplotlib.pyplot as plt
plt.scatter(X[:,1], X[:,2], c=y)
plt.show()

或者我们也可以选择任意三个,用 3D 显示:

...
ax = fig.add_subplot(projection='3d')
ax.scatter(X[:,1], X[:,2], X[:,3], c=y)
plt.show()

但这并没有揭示数据的大部分样子,因为大多数特征都没有显示出来。我们现在求助于主成分分析:

...
from sklearn.decomposition import PCA
pca = PCA()
Xt = pca.fit_transform(X)
plot = plt.scatter(Xt[:,0], Xt[:,1], c=y)
plt.legend(handles=plot.legend_elements()[0], labels=list(winedata['target_names']))
plt.show()

这里我们通过主成分分析将输入数据X转化为Xt。我们只考虑包含最多信息的前两列,并以二维方式绘制。我们可以看到紫色类还是挺有特色的,但是还是有一些重叠。如果我们在进行主成分分析之前对数据进行缩放,结果会有所不同:

...
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
pca = PCA()
pipe = Pipeline([('scaler', StandardScaler()), ('pca', pca)])
Xt = pipe.fit_transform(X)
plot = plt.scatter(Xt[:,0], Xt[:,1], c=y)
plt.legend(handles=plot.legend_elements()[0], labels=list(winedata['target_names']))
plt.show()

由于 PCA 对尺度敏感,如果我们通过StandardScaler对每个特征进行归一化,可以看到更好的结果。这里不同的阶层更有特色。通过查看此图,我们确信像 SVM 这样的简单模型可以高精确率地对该数据集进行分类。

将这些放在一起,下面是生成可视化的完整代码:

from sklearn.datasets import load_wine
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import matplotlib.pyplot as plt

# Load dataset
winedata = load_wine()
X, y = winedata['data'], winedata['target']
print("X shape:", X.shape)
print("y shape:", y.shape)

# Show any two features
plt.figure(figsize=(8,6))
plt.scatter(X[:,1], X[:,2], c=y)
plt.xlabel(winedata["feature_names"][1])
plt.ylabel(winedata["feature_names"][2])
plt.title("Two particular features of the wine dataset")
plt.show()

# Show any three features
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(projection='3d')
ax.scatter(X[:,1], X[:,2], X[:,3], c=y)
ax.set_xlabel(winedata["feature_names"][1])
ax.set_ylabel(winedata["feature_names"][2])
ax.set_zlabel(winedata["feature_names"][3])
ax.set_title("Three particular features of the wine dataset")
plt.show()

# Show first two principal components without scaler
pca = PCA()
plt.figure(figsize=(8,6))
Xt = pca.fit_transform(X)
plot = plt.scatter(Xt[:,0], Xt[:,1], c=y)
plt.legend(handles=plot.legend_elements()[0], labels=list(winedata['target_names']))
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.title("First two principal components")
plt.show()

# Show first two principal components with scaler
pca = PCA()
pipe = Pipeline([('scaler', StandardScaler()), ('pca', pca)])
plt.figure(figsize=(8,6))
Xt = pipe.fit_transform(X)
plot = plt.scatter(Xt[:,0], Xt[:,1], c=y)
plt.legend(handles=plot.legend_elements()[0], labels=list(winedata['target_names']))
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.title("First two principal components after scaling")
plt.show()

如果我们在不同的数据集(如 MINST 手写数字)上应用相同的方法,散点图不会显示明显的边界,因此需要更复杂的模型(如神经网络)来分类:

from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import matplotlib.pyplot as plt

digitsdata = load_digits()
X, y = digitsdata['data'], digitsdata['target']
pca = PCA()
pipe = Pipeline([('scaler', StandardScaler()), ('pca', pca)])
plt.figure(figsize=(8,6))
Xt = pipe.fit_transform(X)
plot = plt.scatter(Xt[:,0], Xt[:,1], c=y)
plt.legend(handles=plot.legend_elements()[0], labels=list(digitsdata['target_names']))
plt.show()

可视化解释的差异

主成分分析本质上是通过特征的线性组合来重新排列特征。因此,它被称为特征提取技术。主成分分析的一个特点是第一主成分包含了数据集的大部分信息。第二主成分比第三主成分信息量更大,以此类推。

为了说明这个想法,我们可以分步骤从原始数据集中移除主成分,并查看数据集的外观。让我们考虑具有较少要素的数据集,并在图中显示两个要素:

from sklearn.datasets import load_iris
irisdata = load_iris()
X, y = irisdata['data'], irisdata['target']
plt.figure(figsize=(8,6))
plt.scatter(X[:,0], X[:,1], c=y)
plt.show()

这是虹膜数据集,它只有四个特征。这些功能的比例相当,因此我们可以跳过缩放器。利用 4 特征数据,主成分分析最多可以产生 4 个主成分:

...
pca = PCA().fit(X)
print(pca.components_)
[[ 0.36138659 -0.08452251  0.85667061  0.3582892 ]
 [ 0.65658877  0.73016143 -0.17337266 -0.07548102]
 [-0.58202985  0.59791083  0.07623608  0.54583143]
 [-0.31548719  0.3197231   0.47983899 -0.75365743]]

例如,第一行是在其上创建第一主分量的第一主轴。对于具有特征p=(abcd)p=(a,b,c,d) 的任何数据点的任何数据点p,由于主轴由向量,由于主轴由向量v=(0.36,-0.08,0.86,0.36)表示,因此该数据点的第一个主成分在主轴上的值为0.36˘a0.08˘b+0.86˘c+0.36˘d 0.36 \u 乘以 a–0.08 \u 乘以 b+0.86 \u 乘以 c+0.36 \u 乘以 d。使用向量点积,这个值可以用 pvp \cdot v 表示因此,用数据集XX作为 150 \乘 \乘以 4 矩阵(150 个数据点,每个有 4 个特征),我们可以通过矩阵-向量乘法将每个数据点映射到这个主轴上的值: XvX \cdot v 结果是一个长度为 150 的向量。现在,如果我们沿着主轴向量从每个数据点移除相应的值,那将是 x(x cdotv) cdotvtx –( x \ cdot v)\ cdot v^t ,其中转置向量vTv^T是一行,XvX\cdot v是一列。乘积(Xv)vT(X \cdot v) \cdot v^T遵循矩阵-矩阵乘法,结果是一个150\乘4 150 \乘以 4的矩阵,与XX的维数相同。

如果我们绘制(Xv)vT(X \cdot v) \cdot v^T的前两个特征,它看起来像这样:

...
# Remove PC1
Xmean = X - X.mean(axis=0)
value = Xmean @ pca.components_[0]
pc1 = value.reshape(-1,1) @ pca.components_[0].reshape(1,-1)
Xremove = X - pc1
plt.scatter(Xremove[:,0], Xremove[:,1], c=y)
plt.show()

numpy 数组Xmean是将X的特征移动到零中心。这是 PCA 所必需的。然后通过矩阵向量乘法计算阵列value。 数组value是映射在主轴上的每个数据点的大小。所以如果我们把这个值乘以主轴向量,我们得到一个数组pc1。从原始数据集X中移除这个,我们得到一个新的数组Xremove。在图中,我们观察到散点图上的点破碎在一起,每个类的聚类没有以前那么明显。这意味着我们通过去除第一主成分去除了很多信息。如果我们再次重复同样的过程,这些点会进一步瓦解:

...
# Remove PC2
value = Xmean @ pca.components_[1]
pc2 = value.reshape(-1,1) @ pca.components_[1].reshape(1,-1)
Xremove = Xremove - pc2
plt.scatter(Xremove[:,0], Xremove[:,1], c=y)
plt.show()

这看起来像一条直线,但实际上不是。如果我们再重复一次,所有的点都会折叠成一条直线:

...
# Remove PC3
value = Xmean @ pca.components_[2]
pc3 = value.reshape(-1,1) @ pca.components_[2].reshape(1,-1)
Xremove = Xremove - pc3
plt.scatter(Xremove[:,0], Xremove[:,1], c=y)
plt.show()

这些点都落在一条直线上,因为我们从只有四个特征的数据中去除了三个主成分。因此,我们的数据矩阵成为等级 1 。你可以试着再重复一次这个过程,结果是所有的点都折叠成一个点。当我们去除主成分时,每个步骤中去除的信息量可以通过主成分分析中相应的解释方差比找到:

...
print(pca.explained_variance_ratio_)
[0.92461872 0.05306648 0.01710261 0.00521218]

这里我们可以看到,第一个分量解释了 92.5%的方差,第二个分量解释了 5.3%的方差。如果我们去掉前两个主成分,剩余的方差只有 2.2%,因此从视觉上看,去掉两个成分后的图看起来像一条直线。事实上,当我们查看上面的图时,我们不仅看到点被粉碎,而且当我们移除组件时,x 轴和 y 轴的范围也变小了。

在机器学习方面,我们可以考虑在这个数据集中只使用一个单一的特征进行分类,即第一主成分。使用全套功能时,我们应期望达到不低于 90%的原始精确率:

...
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from collections import Counter
from sklearn.svm import SVC

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
clf = SVC(kernel="linear", gamma='auto').fit(X_train, y_train)
print("Using all features, accuracy: ", clf.score(X_test, y_test))
print("Using all features, F1: ", f1_score(y_test, clf.predict(X_test), average="macro"))

mean = X_train.mean(axis=0)
X_train2 = X_train - mean
X_train2 = (X_train2 @ pca.components_[0]).reshape(-1,1)
clf = SVC(kernel="linear", gamma='auto').fit(X_train2, y_train)
X_test2 = X_test - mean
X_test2 = (X_test2 @ pca.components_[0]).reshape(-1,1)
print("Using PC1, accuracy: ", clf.score(X_test2, y_test))
print("Using PC1, F1: ", f1_score(y_test, clf.predict(X_test2), average="macro"))
Using all features, accuracy:  1.0
Using all features, F1:  1.0
Using PC1, accuracy:  0.96
Using PC1, F1:  0.9645191409897292

解释方差的另一个用途是压缩。给定第一主成分的解释方差很大,如果我们需要存储数据集,我们可以只存储第一主轴上的投影值(XvX\cdot v),以及主轴的向量vv。然后我们可以通过将它们相乘来近似地再现原始数据集: x\近似值(Xv)vTx \近似值(X\cdot v) \cdot v^T 这样,我们只需要为每个数据点存储一个值,而不是为四个特征存储四个值。如果我们将投影值存储在多个主轴上,并将多个主分量相加,则近似更准确。

将这些放在一起,下面是生成可视化的完整代码:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.metrics import f1_score
from sklearn.svm import SVC
import matplotlib.pyplot as plt

# Load iris dataset
irisdata = load_iris()
X, y = irisdata['data'], irisdata['target']
plt.figure(figsize=(8,6))
plt.scatter(X[:,0], X[:,1], c=y)
plt.xlabel(irisdata["feature_names"][0])
plt.ylabel(irisdata["feature_names"][1])
plt.title("Two features from the iris dataset")
plt.show()

# Show the principal components
pca = PCA().fit(X)
print("Principal components:")
print(pca.components_)

# Remove PC1
Xmean = X - X.mean(axis=0)
value = Xmean @ pca.components_[0]
pc1 = value.reshape(-1,1) @ pca.components_[0].reshape(1,-1)
Xremove = X - pc1
plt.figure(figsize=(8,6))
plt.scatter(Xremove[:,0], Xremove[:,1], c=y)
plt.xlabel(irisdata["feature_names"][0])
plt.ylabel(irisdata["feature_names"][1])
plt.title("Two features from the iris dataset after removing PC1")
plt.show()

# Remove PC2
Xmean = X - X.mean(axis=0)
value = Xmean @ pca.components_[1]
pc2 = value.reshape(-1,1) @ pca.components_[1].reshape(1,-1)
Xremove = Xremove - pc2
plt.figure(figsize=(8,6))
plt.scatter(Xremove[:,0], Xremove[:,1], c=y)
plt.xlabel(irisdata["feature_names"][0])
plt.ylabel(irisdata["feature_names"][1])
plt.title("Two features from the iris dataset after removing PC1 and PC2")
plt.show()

# Remove PC3
Xmean = X - X.mean(axis=0)
value = Xmean @ pca.components_[2]
pc3 = value.reshape(-1,1) @ pca.components_[2].reshape(1,-1)
Xremove = Xremove - pc3
plt.figure(figsize=(8,6))
plt.scatter(Xremove[:,0], Xremove[:,1], c=y)
plt.xlabel(irisdata["feature_names"][0])
plt.ylabel(irisdata["feature_names"][1])
plt.title("Two features from the iris dataset after removing PC1 to PC3")
plt.show()

# Print the explained variance ratio
print("Explainedd variance ratios:")
print(pca.explained_variance_ratio_)

# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)

# Run classifer on all features
clf = SVC(kernel="linear", gamma='auto').fit(X_train, y_train)
print("Using all features, accuracy: ", clf.score(X_test, y_test))
print("Using all features, F1: ", f1_score(y_test, clf.predict(X_test), average="macro"))

# Run classifier on PC1
mean = X_train.mean(axis=0)
X_train2 = X_train - mean
X_train2 = (X_train2 @ pca.components_[0]).reshape(-1,1)
clf = SVC(kernel="linear", gamma='auto').fit(X_train2, y_train)
X_test2 = X_test - mean
X_test2 = (X_test2 @ pca.components_[0]).reshape(-1,1)
print("Using PC1, accuracy: ", clf.score(X_test2, y_test))
print("Using PC1, F1: ", f1_score(y_test, clf.predict(X_test2), average="macro"))

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

教程

蜜蜂

摘要

在本教程中,您发现了如何使用主成分分析来可视化数据。

具体来说,您了解到:

  • 使用主成分分析可视化 2D 的高维数据集
  • 如何使用主成分分析维度中的图来帮助选择合适的机器学习模型
  • 如何观察主成分分析的解释方差比
  • 解释的方差比对机器学习意味着什么

在机器学习中学习线性代数的主要资源

原文: machinelearningmastery.com/resources-for-linear-algebra-in-machine-learning/

如何获得机器学习的线性代数帮助?

线性代数是数学领域,是机器学习领域的重要支柱。

对于初学者或几十年来没有看过这个主题的从业者来说,这可能是一个具有挑战性的话题。

在这篇文章中,您将了解如何获得机器学习的线性代数帮助。

阅读这篇文章后,你会知道:

  • 您可以参考线性代数的维基百科文章和教科书。
  • 您可以学习或复习线性代数的大学课程和在线课程。
  • 问答网站,您可以在其中发布有关线性代数主题的问题。

让我们开始吧。

Top Resources for Learning Linear Algebra for Machine Learning

学习用于机器学习的线性代数的主要资源 照片来自 mickey ,保留一些权利。

概观

这篇文章分为 6 个部分;他们是:

  1. 维基百科上的线性代数
  2. 线性代数教科书
  3. 线性代数大学课程
  4. 线性代数在线课程
  5. 询问有关线性代数的问题
  6. NumPy 资源

维基百科上的线性代数

维基百科是一个很好的起点。

涵盖了所有重要主题,描述简洁,方程式一致且可读。缺少的是更多的人类层面描述,如类比和直觉。

然而,当你对线性代数有疑问时,我建议先按维基百科停止。

一些好的高级页面包括:

线性代数教科书

我强烈建议您获得一本关于线性代数主题的优秀教科书,并将其作为参考。

好的教科书的好处是,您所需的各种操作的解释将是一致的(或应该是)。教科书的缺点是它们可能非常昂贵。

一本好的教科书通常很容易被发现,因为它将成为顶尖大学的一系列本科或研究生课程的基础。

我推荐的一些关于线性代数的入门教材包括:

Amazon Image

我推荐的一些更高级的教科书包括:

Amazon Image

我还推荐一本关于多元统计的好教科书,它是线性代数和数值统计方法的交集。一些好的入门教材包括:

Amazon Image

还有许多由学者撰写的免费在线书籍。有关广泛(且令人印象深刻)的阅读列表,请参阅 Wikipedia 上线性代数页面的末尾。

线性代数大学课程

关于线性代数的大学课程是有用的,因为它们布置了本科生应该知道的主题。

作为一个机器学习从业者,它不仅仅是你需要的,而是为你需要知道的元素提供上下文。

许多大学课程现在提供 PDF 版本的演讲幻灯片,笔记和阅读材料。有些人甚至提供预先录制的视频讲座,这些讲座非常宝贵。

我鼓励您通过浸入课程来手术使用大学课程材料,以获得有关特定主题的更深入的知识。我认为通过端到端的特定课程进行操作太耗费时间,并且对于普通的机器学习从业者来说太多了。

美国顶尖学校的一些推荐课程包括:

线性代数在线课程

在线课程与大学课程不同。

它们专为远程教育而设计,通常不如完整的本科课程完整或不那么严格。对于希望快速掌握主题的机器学习从业者而言,这是一个很好的功能。

如果课程很短,可能值得通过端到端进行。一般而言,和大学课程一样,我建议对主题进行外科手术并根据需要进行深入研究。

我推荐的一些在线课程包括:

询问有关线性代数的问题

鉴于当前丰富的问答平台,有很多地方可以在线询问有关线性代数的问题。

以下是我建议发布问题的热门地点列表。请务必在发布之前搜索您的问题,以防以前被询问和回答。

NumPy 资源

在 Python 中实现线性代数时,您可能需要 NumPy 的帮助。

NumPy API 文档非常好,下面是一些资源,您可以使用它们来了解有关 NumPy 如何工作或如何使用特定 NumPy 函数的更多信息。

如果您正在寻找对 NumPy 和 SciPy 使用的更广泛理解,以下书籍提供了一个很好的起始参考:

摘要

在这篇文章中,您发现了如何获得机器学习的线性代数帮助。

具体来说,您了解到:

  • 您可以参考线性代数的维基百科文章和教科书。
  • 您可以学习或复习线性代数的大学课程和在线课程。
  • 问答网站,您可以在其中发布有关线性代数主题的问题。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

浅谈机器学习的奇异值分解

原文: machinelearningmastery.com/singular-value-decomposition-for-machine-learning/

矩阵分解,也称为矩阵分解,涉及使用其组成元素描述给定矩阵。

也许最着名和最广泛使用的矩阵分解方法是奇异值分解或 SVD。所有矩阵都有一个 SVD,这使得它比其他方法更稳定,例如特征分解。因此,它经常用于各种应用,包括压缩,去噪和数据缩减。

在本教程中,您将发现用于将矩阵分解为其组成元素的奇异值分解方法。

完成本教程后,您将了解:

  • 奇异值分解是什么以及涉及什么。
  • 如何计算 SVD 并从 SVD 元素重建矩形和方形矩阵。
  • 如何使用 SVD 计算伪逆并执行降维

让我们开始吧。

  • 更新 Mar / 2018 :修复了重建中的拼写错误。为清晰起见,将代码中的 V 更改为 VT。修正了伪逆方程中的拼写错误。

A Gentle Introduction to Singular-Value Decomposition

奇异值分解 照片由 Chris Heald 拍摄,保留一些权利。

教程概述

本教程分为 5 个部分;他们是:

  1. 奇异值分解
  2. 计算奇异值分解
  3. 从 SVD 重构矩阵
  4. 伪逆的 SVD
  5. 用于降维的 SVD

奇异值分解

奇异值分解(简称 SVD)是一种矩阵分解方法,用于将矩阵减少到其组成部分,以使某些后续矩阵计算更简单。

为简单起见,我们将重点关注实值矩阵的 SVD,并忽略复数的情况。

A = U . Sigma . V^T

其中 A 是我们希望分解的真实 mxn 矩阵,U 是 mxm 矩阵,Sigma(通常由大写希腊字母 Sigma 表示)是 mxn 对角矩阵,V ^ T 是 nxn 矩阵的转置,其中 T 是一个上标。

奇异值分解是线性代数的一个亮点。

Sigma 矩阵中的对角线值称为原始矩阵 A 的奇异值.U 矩阵的列称为 A 的左奇异向量,V 列称为 A 的右奇异向量。

通过迭代数值方法计算 SVD。我们不会详细介绍这些方法。每个矩形矩阵都具有奇异值分解,尽管得到的矩阵可能包含复数,浮点运算的局限性可能会导致某些矩阵无法整齐地分解。

奇异值分解(SVD)提供了另一种将矩阵分解为奇异向量和奇异值的方法。 SVD 允许我们发现一些与特征分解相同的信息。但是,SVD 更普遍适用。

SVD 广泛用于计算其他矩阵运算,例如矩阵逆运算,但也作为机器学习中的数据简化方法。 SVD 还可用于最小二乘线性回归,图像压缩和去噪数据。

奇异值分解(SVD)在统计学,机器学习和计算机科学中有许多应用。将 SVD 应用于矩阵就像在 X 射线视觉中查看它...

计算奇异值分解

可以通过调用 svd()函数来计算 SVD。

该函数采用矩阵并返回 U,Sigma 和 V ^ T 元素。 Sigma 对角矩阵作为奇异值的向量返回。 V 矩阵以转置的形式返回,例如, V.T.

下面的示例定义了 3×2 矩阵并计算奇异值分解。

# Singular-value decomposition
from numpy import array
from scipy.linalg import svd
# define a matrix
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
# SVD
U, s, VT = svd(A)
print(U)
print(s)
print(VT)

首先运行该示例打印定义的 3×2 矩阵,然后打印 3×3U 矩阵,2 元素 Sigma 向量和从分解计算的 2×2V ^ T 矩阵元素。

[[1 2]
 [3 4]
 [5 6]]

[[-0.2298477   0.88346102  0.40824829]
 [-0.52474482  0.24078249 -0.81649658]
 [-0.81964194 -0.40189603  0.40824829]]

[ 9.52551809  0.51430058]

[[-0.61962948 -0.78489445]
 [-0.78489445  0.61962948]]

从 SVD 重构矩阵

可以从 U,Sigma 和 V ^ T 元素重建原始矩阵。

从 svd()返回的 U,s 和 V 元素不能直接相乘。

必须使用 diag()函数将 s 向量转换为对角矩阵。默认情况下,此函数将创建一个相对于原始矩阵 m x m 的方阵。这导致问题,因为矩阵的大小不符合矩阵乘法的规则,其中矩阵中的列数必须与后续矩阵中的行数匹配。

在创建方形 Sigma 对角矩阵之后,矩阵的大小相对于我们正在分解的原始 m x n 矩阵,如下所示:

U (m x m) . Sigma (m x m) . V^T (n x n)

事实上,我们要求:

U (m x m) . Sigma (m x n) . V^T (n x n)

我们可以通过创建所有零值 m x n(例如更多行)的新 Sigma 格式来实现这一点,并用通过 diag()计算的方形对角矩阵填充矩阵的前 n x n 部分。

# Reconstruct SVD
from numpy import array
from numpy import diag
from numpy import dot
from numpy import zeros
from scipy.linalg import svd
# define a matrix
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
# create m x n Sigma matrix
Sigma = zeros((A.shape[0], A.shape[1]))
# populate Sigma with n x n diagonal matrix
Sigma[:A.shape[1], :A.shape[1]] = diag(s)
# reconstruct matrix
B = U.dot(Sigma.dot(VT))
print(B)

首先运行该示例打印原始矩阵,然后打印从 SVD 元素重建的矩阵。

[[1 2]
 [3 4]
 [5 6]]

[[ 1\.  2.]
 [ 3\.  4.]
 [ 5\.  6.]]

上述与 Sigma 对角线的复杂性仅存在于 m 和 n 不相等的情况下。当重建方形矩阵时,可以直接使用对角矩阵,如下所述。

# Reconstruct SVD
from numpy import array
from numpy import diag
from numpy import dot
from scipy.linalg import svd
# define a matrix
A = array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
# create n x n Sigma matrix
Sigma = diag(s)
# reconstruct matrix
B = U.dot(Sigma.dot(VT))
print(B)

运行该示例打印原始 3×3 矩阵和直接从 SVD 元素重建的版本。

[[1 2 3]
 [4 5 6]
 [7 8 9]]

[[ 1\.  2\.  3.]
 [ 4\.  5\.  6.]
 [ 7\.  8\.  9.]]

伪逆的 SVD

伪逆是矩形矩阵到矩形矩阵的矩阵逆的推广,其中行和列的数量不相等。

在该方法的两个独立发现者或广义逆之后,它也被称为 Moore-Penrose 逆。

没有为非正方形的矩阵定义矩阵求逆。 [...]当 A 的列数多于行数时,使用 pseudoinverse 求解线性方程式提供了许多可能的解决方案之一。

伪逆表示为 A ^ +,其中 A 是被反转的矩阵,+是上标。

使用 A 的奇异值分解计算伪逆:

A^+ = V . D^+ . U^T

或者,没有点符号:

A^+ = VD^+U^T

其中 A ^ +是伪逆,D ^ +是对角矩阵 Sigma 的伪逆,U ^ T 是 U 的转置。

我们可以通过 SVD 操作获得 U 和 V.

A = U . Sigma . V^T

可以通过从 Sigma 创建对角矩阵来计算 D ^ +,计算 Sigma 中每个非零元素的倒数,并且如果原始矩阵是矩形则采用转置。

         s11,   0,   0
Sigma = (  0, s22,   0)
           0,   0, s33
       1/s11,     0,     0
D^+ = (    0, 1/s22,     0)
           0,     0, 1/s33

伪逆提供了一种求解线性回归方程的方法,特别是当行数多于列时,通常就是这种情况。

NumPy 提供函数 pinv()来计算矩形矩阵的伪逆。

下面的示例定义了一个 4×2 矩阵并计算伪逆。

# Pseudoinverse
from numpy import array
from numpy.linalg import pinv
# define matrix
A = array([	[0.1, 0.2],
	[0.3, 0.4],
	[0.5, 0.6],
	[0.7, 0.8]])
print(A)
# calculate pseudoinverse
B = pinv(A)
print(B)

首先运行示例打印定义的矩阵,然后打印计算的伪逆。

[[ 0.1  0.2]
 [ 0.3  0.4]
 [ 0.5  0.6]
 [ 0.7  0.8]]

[[ -1.00000000e+01  -5.00000000e+00   9.04289323e-15   5.00000000e+00]
 [  8.50000000e+00   4.50000000e+00   5.00000000e-01  -3.50000000e+00]]

我们可以通过 SVD 手动计算伪逆,并将结果与​​pinv()函数进行比较。

首先,我们必须计算 SVD。接下来,我们必须计算 s 数组中每个值的倒数。然后可以将 s 数组转换为具有添加的零行的对角矩阵,以使其成为矩形。最后,我们可以从元素中计算出伪逆。

具体实现是:

A^+ = V . D^+ . U^V

下面列出了完整的示例。

# Pseudoinverse via SVD
from numpy import array
from numpy.linalg import svd
from numpy import zeros
from numpy import diag
# define matrix
A = array([
	[0.1, 0.2],
	[0.3, 0.4],
	[0.5, 0.6],
	[0.7, 0.8]])
print(A)
# calculate svd
U, s, VT = svd(A)
# reciprocals of s
d = 1.0 / s
# create m x n D matrix
D = zeros(A.shape)
# populate D with n x n diagonal matrix
D[:A.shape[1], :A.shape[1]] = diag(d)
# calculate pseudoinverse
B = VT.T.dot(D.T).dot(U.T)
print(B)

首先运行示例打印定义的矩形矩阵和与 pinv()函数匹配上述结果的伪逆。

[[ 0.1  0.2]
 [ 0.3  0.4]
 [ 0.5  0.6]
 [ 0.7  0.8]]

[[ -1.00000000e+01  -5.00000000e+00   9.04831765e-15   5.00000000e+00]
 [  8.50000000e+00   4.50000000e+00   5.00000000e-01  -3.50000000e+00]]

用于降维的 SVD

SVD 的一种流行应用是降低尺寸。

具有大量特征的数据(例如,比观察(行)更多的特征(列))可以减少到与预测问题最相关的较小特征子集。

结果是具有较低等级的矩阵,据说接近原始矩阵。

为此,我们可以对原始数据执行 SVD 操作,并在 Sigma 中选择前 k 个最大奇异值。这些列可以从 Sigma 和从 V ^ T 中选择的行中选择。

然后可以重建原始向量 A 的近似 B.

B = U . Sigmak . V^Tk

在自然语言处理中,该方法可以用于文档中的单词出现或单词频率的矩阵,并且被称为潜在语义分析或潜在语义索引。

在实践中,我们可以保留并使用名为 T 的数据的描述子集。这是矩阵或投影的密集摘要。

T = U . Sigmak

此外,可以计算该变换并将其应用于原始矩阵 A 以及其他类似的矩阵。

T = V^Tk . A

下面的示例演示了使用 SVD 减少数据。

首先定义 3×10 矩阵,列数多于行数。计算 SVD 并仅选择前两个特征。重新组合元素以给出原始矩阵的准确再现。最后,变换以两种不同的方式计算。

from numpy import array
from numpy import diag
from numpy import zeros
from scipy.linalg import svd
# define a matrix
A = array([
	[1,2,3,4,5,6,7,8,9,10],
	[11,12,13,14,15,16,17,18,19,20],
	[21,22,23,24,25,26,27,28,29,30]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
# create m x n Sigma matrix
Sigma = zeros((A.shape[0], A.shape[1]))
# populate Sigma with n x n diagonal matrix
Sigma[:A.shape[0], :A.shape[0]] = diag(s)
# select
n_elements = 2
Sigma = Sigma[:, :n_elements]
VT = VT[:n_elements, :]
# reconstruct
B = U.dot(Sigma.dot(VT))
print(B)
# transform
T = U.dot(Sigma)
print(T)
T = A.dot(VT.T)
print(T)

首先运行该示例打印定义的矩阵然后重建近似,然后是原始矩阵的两个等效变换。

[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]]

[[  1\.   2\.   3\.   4\.   5\.   6\.   7\.   8\.   9\.  10.]
 [ 11\.  12\.  13\.  14\.  15\.  16\.  17\.  18\.  19\.  20.]
 [ 21\.  22\.  23\.  24\.  25\.  26\.  27\.  28\.  29\.  30.]]

[[-18.52157747   6.47697214]
 [-49.81310011   1.91182038]
 [-81.10462276  -2.65333138]]

[[-18.52157747   6.47697214]
 [-49.81310011   1.91182038]
 [-81.10462276  -2.65333138]]

scikit-learn 提供了一个直接实现此功能的 TruncatedSVD 类。

可以创建 TruncatedSVD 类,您必须在其中指定要选择的所需要素或组件的数量,例如, 2.一旦创建,您可以通过调用 fit()函数来拟合变换(例如,计算 V ^ Tk),然后通过调用 transform()函数将其应用于原始矩阵。结果是上面称为 T 的 A 的变换。

下面的示例演示了 TruncatedSVD 类。

from numpy import array
from sklearn.decomposition import TruncatedSVD
# define array
A = array([
	[1,2,3,4,5,6,7,8,9,10],
	[11,12,13,14,15,16,17,18,19,20],
	[21,22,23,24,25,26,27,28,29,30]])
print(A)
# svd
svd = TruncatedSVD(n_components=2)
svd.fit(A)
result = svd.transform(A)
print(result)

首先运行示例打印定义的矩阵,然后打印矩阵的转换版本。

我们可以看到值与上面手动计算的值匹配,除了某些值上的符号。考虑到所涉及的计算的性质以及所使用的底层库和方法的差异,我们可以预期在符号方面存在一些不稳定性。只要对变换进行了重复训练,这种符号的不稳定性在实践中就不应成为问题。

[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]]

[[ 18.52157747   6.47697214]
 [ 49.81310011   1.91182038]
 [ 81.10462276  -2.65333138]]

扩展

本节列出了一些扩展您可能希望探索的教程的想法。

  • 在您自己的数据上试验 SVD 方法。
  • 研究并列出了 SVD 在机器学习中的 10 个应用。
  • 将 SVD 作为数据缩减技术应用于表格数据集。

如果你探索任何这些扩展,我很想知道。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

图书

API

用品

摘要

在本教程中,您发现了奇异值分解方法,用于将矩阵分解为其组成元素。

具体来说,你学到了:

  • 奇异值分解是什么以及涉及什么。
  • 如何计算 SVD 并从 SVD 元素重建矩形和方形矩阵。
  • 如何使用 SVD 计算伪逆并执行降维。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

如何用线性代数求解线性回归

原文: machinelearningmastery.com/solve-linear-regression-using-linear-algebra/

线性回归是一种用于对一个或多个自变量与因变量之间的关系进行建模的方法。

它是统计学的主要内容,通常被认为是一种很好的入门机器学习方法。它也是一种可以使用矩阵表示法重新构造并使用矩阵运算求解的方法。

在本教程中,您将发现线性回归的矩阵公式以及如何使用直接和矩阵分解方法来解决它。

完成本教程后,您将了解:

  • 线性回归与正规方程的矩阵重构。
  • 如何使用 QR 矩阵分解求解线性回归。
  • 如何使用 SVD 和伪逆求解线性回归。

让我们开始吧。

How to Solve Linear Regression Using Linear Algebra

如何使用线性代数解决线性回归 照片来自 likeaduck ,保留一些权利。

教程概述

本教程分为 6 个部分;他们是:

  1. 线性回归
  2. 线性回归的矩阵公式
  3. 线性回归数据集
  4. 直接解决
  5. 通过 QR 分解解决
  6. 通过奇异值分解求解

线性回归

线性回归是一种建模两个标量值之间关系的方法:输入变量 x 和输出变量 y。

该模型假设 y 是线性函数或输入变量的加权和。

y = f(x)

或者,用系数表示。

y = b0 + b1 . x1

在给定多个输入变量(称为多元线性回归)的情况下,该模型还可用于对输出变量进行建模(下面,为了便于阅读,添加了括号)。

y = b0 + (b1 . x1) + (b2 . x2) + ...

创建线性回归模型的目的是找到系数值(b)的值,以最小化输出变量 y 的预测误差。

线性回归的矩阵公式

线性回归可以使用 Matrix 符号表示;例如:

y = X . b

或者,没有点符号。

y = Xb

其中 X 是输入数据并且每列是数据特征,b 是系数的向量,y 是 X 中每行的输出变量的向量。

     x11, x12, x13
X = (x21, x22, x23)
     x31, x32, x33
     x41, x42, x43

     b1
b = (b2)
     b3

     y1
y = (y2)
     y3
     y4

重新制定后,问题就变成了一个线性方程组,其中 b 向量值是未知的。这种类型的系统被称为超定,因为存在比未知数更多的方程,即每个系数用于每行数据。

分析解决这个问题是一个具有挑战性的问题,因为存在多种不一致的解决方案,例如:系数的多个可能值。此外,所有解决方案都会有一些错误,因为没有任何线路几乎可以通过所有点,因此求解方程的方法必须能够处理。

通常实现这种方法的方法是找到一种解决方案,其中模型中 b 的值最小化平方误差。这称为线性最小二乘法。

||X . b - y||² = sum i=1 to m ( sum j=1 to n Xij . bj - yi)²

只要输入列是独立的(例如不相关的),该秘籍就具有独特的解决方案。

我们不能总是得到错误 e = b - Ax 降到零。当 e 为零时,x 是 Ax = b 的精确解。当 e 的长度尽可能小时,xhat 是最小二乘解。

在矩阵表示法中,使用所谓的正规方程来表达这个问题:

X^T . X . b = X^T . y

这可以重新安排,以便为 b 指定解决方案:

b = (X^T . X)^-1 . X^T . y

这可以直接求解,但是假设存在矩阵逆可能在数值上具有挑战性或不稳定。

线性回归数据集

为了探索线性回归的矩阵公式,我们首先将数据集定义为上下文。

我们将使用一个简单的 2D 数据集,其中数据易于作为散点图可视化,并且模型很容易可视化为试图拟合数据点的线。

下面的示例定义了一个 5×2 矩阵数据集,将其拆分为 X 和 y 分量,并将数据集绘制为散点图。

from numpy import array
from matplotlib import pyplot
data = array([	[0.05, 0.12],
	[0.18, 0.22],
	[0.31, 0.35],
	[0.42, 0.38],
	[0.5, 0.49],
	])
print(data)
X, y = data[:,0], data[:,1]
X = X.reshape((len(X), 1))
# plot dataset
pyplot.scatter(X, y)
pyplot.show()

首先运行该示例将打印定义的数据集。

[[ 0.05  0.12]
 [ 0.18  0.22]
 [ 0.31  0.35]
 [ 0.42  0.38]
 [ 0.5   0.49]]

然后创建数据集的散点图,显示直线不能精确拟合此数据。

Scatter Plot of Linear Regression Dataset

线性回归数据集的散点图

直接解决

第一种方法是尝试直接解决回归问题。

也就是说,给定 X,当乘以 X 时,系数 b 的集合将给出 y。正如我们在前一节中看到的那样,正规方程定义了如何直接计算 b。

b = (X^T . X)^-1 . X^T . y

这可以使用 inv()函数直接在 NumPy 中计算,以计算矩阵求逆。

b = inv(X.T.dot(X)).dot(X.T).dot(y)

一旦计算出系数,我们就可以用它们来预测给定 X 的结果。

yhat = X.dot(b)

将其与上一节中定义的数据集放在一起,下面列出了完整的示例。

# solve directly
from numpy import array
from numpy.linalg import inv
from matplotlib import pyplot
data = array([
	[0.05, 0.12],
	[0.18, 0.22],
	[0.31, 0.35],
	[0.42, 0.38],
	[0.5, 0.49],
	])
X, y = data[:,0], data[:,1]
X = X.reshape((len(X), 1))
# linear least squares
b = inv(X.T.dot(X)).dot(X.T).dot(y)
print(b)
# predict using coefficients
yhat = X.dot(b)
# plot data and predictions
pyplot.scatter(X, y)
pyplot.plot(X, yhat, color='red')
pyplot.show()

运行该示例执行计算并打印系数向量 b。

[ 1.00233226]

然后使用模型的线图创建数据集的散点图,显示数据的合理拟合。

Scatter Plot of Direct Solution to the Linear Regression Problem

线性回归问题直接解的散点图

这种方法的一个问题是矩阵逆,它在计算上既昂贵又在数值上不稳定。另一种方法是使用矩阵分解来避免这种操作。我们将在以下部分中查看两个示例。

通过 QR 分解解决

QR 分解是将矩阵分解为其组成元素的方法。

A = Q . R

其中 A 是我们希望分解的矩阵,Q 是尺寸为 m×m 的矩阵,R 是尺寸为 m×n 的上三角矩阵。

QR 分解是解决线性最小二乘方程的常用方法。

跨越所有推导,可以使用 Q 和 R 元素找到系数,如下所示:

b = R^-1 . Q.T . y

该方法仍然涉及矩阵求逆,但在这种情况下仅在更简单的 R 矩阵上。

可以使用 NumPy 中的 qr()函数找到 QR 分解。 NumPy 中系数的计算如下:

# QR decomposition
Q, R = qr(X)
b = inv(R).dot(Q.T).dot(y)

将其与数据集结合在一起,下面列出了完整的示例。

# least squares via QR decomposition
from numpy import array
from numpy.linalg import inv
from numpy.linalg import qr
from matplotlib import pyplot
data = array([
[0.05, 0.12],
[0.18, 0.22],
[0.31, 0.35],
[0.42, 0.38],
[0.5, 0.49],
])
X, y = data[:,0], data[:,1]
X = X.reshape((len(X), 1))
# QR decomposition
Q, R = qr(X)
b = inv(R).dot(Q.T).dot(y)
print(b)
# predict using coefficients
yhat = X.dot(b)
# plot data and predictions
pyplot.scatter(X, y)
pyplot.plot(X, yhat, color='red')
pyplot.show()

首先运行该示例打印系数解决方案并使用模型绘制数据。

[ 1.00233226]

QR 分解方法比直接计算正规方程计算效率更高,数值更稳定,但不适用于所有数据矩阵。

Scatter Plot of QR Decomposition Solution to the Linear Regression Problem

QR 分解解对线性回归问题的散点图

通过奇异值分解求解

奇异值分解(简称 SVD)是一种像 QR 分解一样的矩阵分解方法。

X = U . Sigma . V^*

其中 A 是我们希望分解的真实 nxm 矩阵,U 是 amxm 矩阵,Sigma(通常由大写希腊字母 Sigma 表示)是 mxn 对角矩阵,V ^ 是 nxn 矩阵的共轭转置,其中是一个上标。

与 QR 分解不同,所有矩阵都具有 SVD 分解。作为求解线性回归线性方程组的基础,SVD 更稳定,是首选方法。

一旦分解,就可以通过计算输入矩阵 X 的伪逆并将其乘以输出向量 y 来找到系数。

b = X^+ . y

伪逆的计算方法如下:

X^+ = U . D^+ . V^T

其中 X ^ +是 X 的伪逆,+是上标,D ^ +是对角矩阵 Sigma 的伪逆,V ^ T 是 V ^ *的转置。

没有为非正方形的矩阵定义矩阵求逆。 [...]当 A 的列数多于行数时,使用 pseudoinverse 求解线性方程式提供了许多可能的解决方案之一。

我们可以通过 SVD 操作获得 U 和 V.可以通过从 Sigma 创建对角矩阵并计算 Sigma 中每个非零元素的倒数来计算 D ^ +。

         s11,   0,   0
Sigma = (  0, s22,   0)
           0,   0, s33

     1/s11,     0,     0
D = (    0, 1/s22,     0)
         0,     0, 1/s33

我们可以手动计算 SVD,然后计算伪逆。相反,NumPy 提供了我们可以直接使用的函数 pinv()。

下面列出了完整的示例。

# least squares via SVD with pseudoinverse
from numpy import array
from numpy.linalg import pinv
from matplotlib import pyplot
data = array([
	[0.05, 0.12],
	[0.18, 0.22],
	[0.31, 0.35],
	[0.42, 0.38],
	[0.5, 0.49],
	])
X, y = data[:,0], data[:,1]
X = X.reshape((len(X), 1))
# calculate coefficients
b = pinv(X).dot(y)
print(b)
# predict using coefficients
yhat = X.dot(b)
# plot data and predictions
pyplot.scatter(X, y)
pyplot.plot(X, yhat, color='red')
pyplot.show()

运行该示例将打印系数并使用红线绘制数据,以显示模型中的预测。

[ 1.00233226]

实际上,NumPy 提供了一个函数来替换你可以直接使用的 lstsq()函数中的这两个步骤。

Scatter Plot of SVD Solution to the Linear Regression Problem

SVD 解对线性回归问题的散点图

扩展

本节列出了一些扩展您可能希望探索的教程的想法。

  • 使用内置的 lstsq()NumPy 函数实现线性回归
  • 在您自己的小型人为数据集上测试每个线性回归。
  • 加载表格数据集并测试每个线性回归方法并比较结果。

如果你探索任何这些扩展,我很想知道。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

图书

API

用品

教程

摘要

在本教程中,您发现了线性回归的矩阵公式以及如何使用直接和矩阵分解方法来解决它。

具体来说,你学到了:

  • 线性回归与正规方程的矩阵重构。
  • 如何使用 QR 矩阵分解求解线性回归。
  • 如何使用 SVD 和伪逆求解线性回归。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

机器学习中的稀疏矩阵的温和介绍

原文: machinelearningmastery.com/sparse-matrices-for-machine-learning/

主要包含零值的矩阵称为稀疏矩阵,与大多数值非零的矩阵不同,称为密集。

大型稀疏矩阵通常是常见的,尤其是在应用机器学习中,例如包含计数的数据,将类别映射到计数的数据编码,甚至在机器学习的整个子字段中,例如自然语言处理。

表示和使用稀疏矩阵就好像它们是密集的一样计算成本很高,并且通过使用专门处理矩阵稀疏性的表示和操作可以实现表现的大大改进。

在本教程中,您将发现稀疏矩阵,它们出现的问题以及如何直接在 Python 中使用它们。

完成本教程后,您将了解:

  • 稀疏矩阵主要包含零值,与密集矩阵不同。
  • 无数区域中您可能会遇到数据,数据准备和机器学习子字段中的稀疏矩阵。
  • 有许多有效的方法来存储和使用稀疏矩阵,SciPy 提供了可以直接使用的实现。

让我们开始吧。

A Gentle Introduction to Sparse Matrices for Machine Learning

机器学习稀疏矩阵的温和介绍 摄影: CAJC:在落基山脉,保留一些权利。

教程概述

本教程分为 5 个部分;他们是:

  • 稀疏矩阵
  • 稀疏性问题
  • 机器学习中的稀疏矩阵
  • 使用稀疏矩阵
  • Python 中的稀疏矩阵

稀疏矩阵

稀疏矩阵是主要由零值组成的矩阵。

稀疏矩阵不同于具有大多数非零值的矩阵,其被称为密集矩阵。

如果矩阵的许多系数为零,则矩阵是稀疏的。对稀疏性的兴趣之所以产生,是因为它的利用可以带来巨大的计算节省,并且因为在实践中出现的许多大矩阵问题都很少。

矩阵的稀疏度可以用分数量化,分数是矩阵中零值的数量除以矩阵中元素的总数。

sparsity = count zero elements / total elements

下面是一个小的 3 x 6 稀疏矩阵的示例。

     1, 0, 0, 1, 0, 0
A = (0, 0, 2, 0, 0, 1)
     0, 0, 0, 2, 0, 0

该示例具有矩阵中 18 个元素的 13 个零值,使该矩阵的稀疏度得分为 0.722 或约 72%。

稀疏性问题

稀疏矩阵可能导致空间和时间复杂性方面的问题。

空间复杂性

非常大的矩阵需要大量内存,而我们希望使用的一些非常大的矩阵是稀疏的。

在实践中,大多数大型矩阵都是稀疏的 - 几乎所有条目都是零。

非常大的矩阵的一个例子是一个链接矩阵,它显示从一个网站到另一个网站的链接。

较小的稀疏矩阵的示例可以是针对所有已知英语单词的一本书中的单词的单词或术语出现矩阵。

在这两种情况下,包含的矩阵都是稀疏的,其值比数据值多得多。将这些稀疏矩阵表示为密集矩阵的问题是需要存储器,并且必须为矩阵中的每个 32 位或甚至 64 位零值分配存储器。

这显然是浪费内存资源,因为这些零值不包含任何信息。

时间复杂性

假设一个非常大的稀疏矩阵可以适合内存,我们将希望对该矩阵执行操作。

简单地说,如果矩阵主要包含零值,即没有数据,则在该矩阵上执行操作可能花费很长时间,其中所执行的大部分计算将涉及将零值相加或相乘在一起。

在这些问题上使用线性代数的一般方法是浪费的,因为大多数用于求解方程组或反转矩阵的 O(N ^ 3)算术运算涉及零操作数。

这是矩阵运算的时间复杂度增加的问题,其随着矩阵的大小而增加。

当我们考虑到即使是微不足道的机器学习方法可能需要对每行,每列甚至整个矩阵进行许多操作时,这个问题也变得复杂,导致执行时间大大延长。

机器学习中的稀疏矩阵

稀疏矩阵在应用机器学习中出现了很多变化。

在本节中,我们将介绍一些常见示例,以激励您了解稀疏性问题。

数据

稀疏矩阵出现在某些特定类型的数据中,最显着的是记录活动发生或计数的观察。

三个例子包括:

  • 用户是否观看了电影目录中的电影。
  • 用户是否在产品目录中购买了产品。
  • 歌曲目录中歌曲的听众数量。

数据准备

稀疏矩阵出现在用于准备数据的编码方案中。

三个常见的例子包括:

  • 单热编码,用于将分类数据表示为稀疏二进制向量。
  • 计数编码,用于表示文档词汇表中单词的频率
  • TF-IDF 编码,用于表示词汇表中的归一化词频分数。

学习领域

机器学习中的一些研究领域必须开发专门的方法来直接解决稀疏性,因为输入数据几乎总是稀疏的。

Three examples include:

  • 处理文本文档的自然语言处理。
  • 用于在目录中处理产品使用的推荐系统。
  • 处理包含大量黑色像素的图像时的计算机视觉。

如果语言模型中有 100,000 个单词,则特征向量的长度为 100,000,但对于短消息,几乎所有要素都将计为零。

使用稀疏矩阵

表示和使用稀疏矩阵的解决方案是使用备用数据结构来表示稀疏数据。

可以忽略零值,并且仅需要存储或操作稀疏矩阵中的数据或非零值。

有多种数据结构可用于有效地构造稀疏矩阵;下面列出了三个常见的例子。

  • 键字典。使用字典,其中行和列索引映射到值。
  • 名单。矩阵的每一行都存储为一个列表,每个子列表包含列索引和值。
  • 坐标列表。每个元组都存储一个元组列表,其中包含行索引,列索引和值。

还有一些数据结构更适合执行有效的操作;下面列出了两个常用的例子。

  • 压缩稀疏行。对于非零值,行的范围和列索引,使用三个一维数组表示稀疏矩阵。
  • 压缩稀疏列。与压缩稀疏行方法相同,除了列索引被压缩并在行索引之前首先读取。

压缩稀疏行(简称 CSR)通常用于表示机器学习中的稀疏矩阵,因为它支持高效的访问和矩阵乘法。

Python 中的稀疏矩阵

SciPy 提供了使用多个数据结构创建稀疏矩阵的工具,以及将密集矩阵转换为稀疏矩阵的工具。

在 NumPy 数组上运行的许多线性代数 NumPy 和 SciPy 函数可以透明地在 SciPy 稀疏数组上运行。此外,使用 NumPy 数据结构的机器学习库也可以在 SciPy 稀疏数组上透明地运行,例如用于一般机器学习的 scikit-learn 和用于深度学习的 Keras。

通过调用csr_matrix()函数,可以使用 CSR 表示将存储在 NumPy 数组中的密集矩阵转换为稀疏矩阵。

在下面的示例中,我们将 3 x 6 稀疏矩阵定义为密集数组,将其转换为 CSR 稀疏表示,然后通过调用todense()函数将其转换回密集数组。

# dense to sparse
from numpy import array
from scipy.sparse import csr_matrix
# create dense matrix
A = array([[1, 0, 0, 1, 0, 0], [0, 0, 2, 0, 0, 1], [0, 0, 0, 2, 0, 0]])
print(A)
# convert to sparse matrix (CSR method)
S = csr_matrix(A)
print(S)
# reconstruct dense matrix
B = S.todense()
print(B)

运行该示例首先打印定义的密集数组,然后是 CSR 表示,然后是重建的密集矩阵。

[[1 0 0 1 0 0]
 [0 0 2 0 0 1]
 [0 0 0 2 0 0]]

  (0, 0)	1
  (0, 3)	1
  (1, 2)	2
  (1, 5)	1
  (2, 3)	2

[[1 0 0 1 0 0]
 [0 0 2 0 0 1]
 [0 0 0 2 0 0]]

NumPy 不提供计算矩阵稀疏度的函数。

然而,我们可以通过首先找到矩阵的密度并从中减去矩阵来轻松地计算它。 NumPy 数组中的非零元素的数量可以由count_nonzero()函数给出,并且数组中元素的总数可以由数组的 size 属性给出。因此,可以将数组稀疏度计算为

sparsity = 1.0 - count_nonzero(A) / A.size

下面的示例演示了如何计算数组的稀疏性。

# calculate sparsity
from numpy import array
from numpy import count_nonzero
# create dense matrix
A = array([[1, 0, 0, 1, 0, 0], [0, 0, 2, 0, 0, 1], [0, 0, 0, 2, 0, 0]])
print(A)
# calculate sparsity
sparsity = 1.0 - count_nonzero(A) / A.size
print(sparsity)

首先运行该示例打印定义的稀疏矩阵,然后是矩阵的稀疏性。

[[1 0 0 1 0 0]
 [0 0 2 0 0 1]
 [0 0 0 2 0 0]]

0.7222222222222222

扩展

本节列出了一些扩展您可能希望探索的教程的想法。

  • 开发自己的示例,将密集数组转换为稀疏数组并计算稀疏性。
  • 为 SciPy 支持的每个稀疏矩阵表示方法开发一个示例。
  • 选择一个稀疏表示方法并从零开始自己实现。

如果你探索任何这些扩展,我很想知道。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

图书

API

用品

摘要

在本教程中,您发现了稀疏矩阵,它们出现的问题以及如何直接在 Python 中使用它们。

具体来说,你学到了:

  • 稀疏矩阵主要包含零值,与密集矩阵不同。
  • 无数区域中您可能会遇到数据,数据准备和机器学习子字段中的稀疏矩阵。
  • 有许多有效的方法来存储和使用稀疏矩阵,SciPy 提供了可以直接使用的实现。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

利用奇异值分解构建推荐系统

原文:machinelearningmastery.com/using-singu…

最后更新于 2021 年 10 月 29 日

奇异值分解是一种非常流行的线性代数技术,用于将矩阵分解为几个较小矩阵的乘积。事实上,这是一种有许多用途的技术。一个例子是,我们可以使用奇异值分解来发现项目之间的关系。由此可以很容易地建立一个推荐系统。

在本教程中,我们将看到如何使用线性代数技术构建推荐系统。

完成本教程后,您将知道:

  • 奇异值分解对矩阵做了什么
  • 如何解释奇异值分解的结果
  • 单个推荐系统需要哪些数据,如何利用支持向量机进行分析
  • 我们如何利用奇异值分解的结果提出建议

我们开始吧。

Using Singular Value Decomposition to Build a Recommender System

使用奇异值分解构建推荐系统 图片由罗伯托·阿里亚斯提供,保留部分权利。

教程概述

本教程分为 3 个部分;它们是:

  • 奇异值分解综述
  • 奇异值分解在推荐系统中的意义
  • 实现推荐系统

奇异值分解综述

就像 24 这样的数可以分解为因子 24=2×3×4 一样,一个矩阵也可以表示为其他一些矩阵的乘法。因为矩阵是数字的数组,所以它们有自己的乘法规则。因此,它们有不同的分解方式,或称为分解。QR 分解或 LU 分解是常见的例子。另一个例子是奇异值分解,它对要分解的矩阵的形状或属性没有限制。

奇异值分解假设一个矩阵MM(例如,一个m\乘n m \乘以 n矩阵)被分解为 m=u cdot sigma cdotvtm = u \ cdot \ sigma \ cdot v^t 其中UU是一个m\乘M m \乘以 M矩阵,Σ\Sigma是一个m\乘n m \乘以 n的对角矩阵,而VTV^T是一个n\乘n n \乘以 n矩阵。对角线矩阵\适 \适马是一个有趣的矩阵,它可以是非正方形的,但是只有对角线上的条目可以是非零的。矩阵UUVTV^T正交矩阵。意味着UU的列或VV的行是(1)相互正交的,并且是(2)单位向量。如果任意两个向量的点积为零,则向量相互正交。如果向量的 L2 范数为 1,则向量是单位向量。正交矩阵具有转置是逆矩阵的性质。换句话说,由于UU是正交矩阵,UT=U1U^T = U^{-1}UUT=UTU=IU\cdot U^T=U^T\cdot U=I,其中II是单位矩阵。

奇异值分解得名于\适 \适马上的对角条目,它们被称为矩阵 M的奇异值。事实上,它们是MT的奇异值。事实上,它们是 M^T.矩阵特征值的平方根就像分解成素数的数一样,矩阵的奇异值分解揭示了该矩阵的许多结构。

但实际上上面描述的叫做全 SVD 。还有一个版本叫做缩小版 SVD 或者紧凑型 SVD 。我们仍然必须写M=UΣVTM = U\cdot\Sigma\cdot V^T,但是我们有 sigma\ sigma a r\乘r r \乘以 r正方形对角矩阵,其中rr是矩阵MM,它通常小于或等于mmnn中较小的一个。矩阵UU是一个mr˘阵,而 m \\ r \u 矩阵,而V^T是一个是一个 r \ n \u 矩阵。因为矩阵UUVTV^T是非正方形的,所以它们被称为半正交,意思是UTU=IU^T\cdot U=IVTV=IV^T\cdot V=I,两种情况下的II都是r\乘r r \乘以 r的单位矩阵。

奇异值分解在推荐系统中的意义

如果矩阵MM是秩rr,那么我们可以证明矩阵MMTM\cdot M^TMTMM^T\cdot M都是秩rr。在奇异值分解(简化的 SVD)中,矩阵UU的列是 M^T的特征向量,矩阵的特征向量,矩阵V^T的行是的行是M^T\cdot 的特征向量。有趣的是,MMTM\cdot M^TMTMM^T\cdot M可能大小不同(因为 matrix MM可以是非方形的),但它们有相同的特征值集合,即Σ\Sigma对角线上的值的平方。

这就是为什么奇异值分解的结果可以揭示矩阵的很多信息。

想象一下,我们收集了一些书评,书是列,人是行,条目是一个人给一本书的评分。在这种情况下,MMTM\cdot M^T将是一个人对人的表格,其中的条目将意味着一个人给出的评分与另一个人匹配的总和。类似地,MTMM^T\cdot M将是一个书与书之间的表,其中的条目是与另一本书所获得的评级相匹配的所获得的评级的总和。人和书之间有什么潜在的联系?这可能是类型,或作者,或类似性质的东西。

实现推荐系统

让我们看看如何利用奇异值分解的结果来构建一个推荐系统。首先,让我们从这个链接下载数据集(注意:它有 600 兆字节大)

该数据集是来自推荐系统和个性化数据集的“社交推荐数据”。它包含用户对图书馆事物上的书籍的评论。我们感兴趣的是用户给一本书的“星星”数量。

如果我们打开这个 tar 文件,我们会看到一个名为“reviews.json”的大文件。我们可以提取它,或者动态读取包含的文件。下面显示了前三行 reviews.json:

import tarfile

# Read downloaded file from:
# http://deepyeti.ucsd.edu/jmcauley/datasets/librarything/lthing_data.tar.gz
with tarfile.open("lthing_data.tar.gz") as tar:
    print("Files in tar archive:")
    tar.list()

    with tar.extractfile("lthing_data/reviews.json") as file:
        count = 0
        for line in file:
            print(line)
            count += 1
            if count > 3:
                break

上面会打印:

Files in tar archive:
?rwxr-xr-x julian/julian 0 2016-09-30 17:58:55 lthing_data/
?rw-r--r-- julian/julian 4824989 2014-01-02 13:55:12 lthing_data/edges.txt
?rw-rw-r-- julian/julian 1604368260 2016-09-30 17:58:25 lthing_data/reviews.json
b"{'work': '3206242', 'flags': [], 'unixtime': 1194393600, 'stars': 5.0, 'nhelpful': 0, 'time': 'Nov 7, 2007', 'comment': 'This a great book for young readers to be introduced to the world of Middle Earth. ', 'user': 'van_stef'}\n"
b"{'work': '12198649', 'flags': [], 'unixtime': 1333756800, 'stars': 5.0, 'nhelpful': 0, 'time': 'Apr 7, 2012', 'comment': 'Help Wanted: Tales of On The Job Terror from Evil Jester Press is a fun and scary read. This book is edited by Peter Giglio and has short stories by Joe McKinney, Gary Brandner, Henry Snider and many more. As if work wasnt already scary enough, this book gives you more reasons to be scared. Help Wanted is an excellent anthology that includes some great stories by some master storytellers.\\nOne of the stories includes Agnes: A Love Story by David C. Hayes, which tells the tale of a lawyer named Jack who feels unappreciated at work and by his wife so he starts a relationship with a photocopier. They get along well until the photocopier starts wanting the lawyer to kill for it. The thing I liked about this story was how the author makes you feel sorry for Jack. His two co-workers are happily married and love their jobs while Jack is married to a paranoid alcoholic and he hates and works at a job he cant stand. You completely understand how he can fall in love with a copier because he is a lonely soul that no one understands except the copier of course.\\nAnother story in Help Wanted is Work Life Balance by Jeff Strand. In this story a man works for a company that starts to let their employees do what they want at work. It starts with letting them come to work a little later than usual, then the employees are allowed to hug and kiss on the job. Things get really out of hand though when the company starts letting employees carry knives and stab each other, as long as it doesnt interfere with their job. This story is meant to be more funny then scary but still has its scary moments. Jeff Strand does a great job mixing humor and horror in this story.\\nAnother good story in Help Wanted: On The Job Terror is The Chapel Of Unrest by Stephen Volk. This is a gothic horror story that takes place in the 1800s and has to deal with an undertaker who has the duty of capturing and embalming a ghoul who has been eating dead bodies in a graveyard. Stephen Volk through his use of imagery in describing the graveyard, the chapel and the clothes of the time, transports you into an 1800s gothic setting that reminded me of Bram Stokers Dracula.\\nOne more story in this anthology that I have to mention is Expulsion by Eric Shapiro which tells the tale of a mad man going into a office to kill his fellow employees. This is a very short but very powerful story that gets you into the mind of a disgruntled employee but manages to end on a positive note. Though there were stories I didnt like in Help Wanted, all in all its a very good anthology. I highly recommend this book ', 'user': 'dwatson2'}\n"
b"{'work': '12533765', 'flags': [], 'unixtime': 1352937600, 'nhelpful': 0, 'time': 'Nov 15, 2012', 'comment': 'Magoon, K. (2012). Fire in the streets. New York: Simon and Schuster/Aladdin. 336 pp. ISBN: 978-1-4424-2230-8\. (Hardcover); $16.99.\\nKekla Magoon is an author to watch (http://www.spicyreads.org/Author_Videos.html- scroll down). One of my favorite books from 2007 is Magoons The Rock and the River. At the time, I mentioned in reviews that we have very few books that even mention the Black Panther Party, let alone deal with them in a careful, thorough way. Fire in the Streets continues the story Magoon began in her debut book. While her familys financial fortunes drip away, not helped by her mothers drinking and assortment of boyfriends, the Panthers provide a very real respite for Maxie. Sam is still dealing with the death of his brother. Maxies relationship with Sam only serves to confuse and upset them both. Her friends, Emmalee and Patrice, are slowly drifting away. The Panther Party is the only thing that seems to make sense and she basks in its routine and consistency. She longs to become a full member of the Panthers and constantly battles with her Panther brother Raheem over her maturity and ability to do more than office tasks. Maxie wants to have her own gun. When Maxie discovers that there is someone working with the Panthers that is leaking information to the government about Panther activity, Maxie investigates. Someone is attempting to destroy the only place that offers her shelter. Maxie is determined to discover the identity of the traitor, thinking that this will prove her worth to the organization. However, the truth is not simple and it is filled with pain. Unfortunately we still do not have many teen books that deal substantially with the Democratic National Convention in Chicago, the Black Panther Party, and the social problems in Chicago that lead to the civil unrest. Thankfully, Fire in the Streets lives up to the standard Magoon set with The Rock and the River. Readers will feel like they have stepped back in time. Magoons factual tidbits add journalistic realism to the story and only improves the atmosphere. Maxie has spunk. Readers will empathize with her Atlas-task of trying to hold onto her world. Fire in the Streets belongs in all middle school and high school libraries. While readers are able to read this story independently of The Rock and the River, I strongly urge readers to read both and in order. Magoons recognition by the Coretta Scott King committee and the NAACP Image awards are NOT mistakes!', 'user': 'edspicer'}\n"
b'{\'work\': \'12981302\', \'flags\': [], \'unixtime\': 1364515200, \'stars\': 4.0, \'nhelpful\': 0, \'time\': \'Mar 29, 2013\', \'comment\': "Well, I definitely liked this book better than the last in the series. There was less fighting and more story. I liked both Toni and Ricky Lee and thought they were pretty good together. The banter between the two was sweet and often times funny. I enjoyed seeing some of the past characters and of course it\'s always nice to be introduced to new ones. I just wonder how many more of these books there will be. At least two hopefully, one each for Rory and Reece. ", \'user\': \'amdrane2\'}\n'

reviews.json 中的每一行都是一条记录。我们将提取每个记录的“用户”、“工作”和“星星”字段,只要这三个字段中没有缺失数据。尽管有名字,但记录不是格式良好的 JSON 字符串(最明显的是它使用单引号而不是双引号)。因此,我们不能使用 Python 中的json包,而是使用ast来解码这样的字符串:

...
import ast

reviews = []
with tarfile.open("lthing_data.tar.gz") as tar:
    with tar.extractfile("lthing_data/reviews.json") as file:
        for line in file:
            record = ast.literal_eval(line.decode("utf8"))
            if any(x not in record for x in ['user', 'work', 'stars']):
                continue
            reviews.append([record['user'], record['work'], record['stars']])
print(len(reviews), "records retrieved")
1387209 records retrieved

现在我们应该制作一个不同用户如何评价每本书的矩阵。我们利用熊猫库来帮助将我们收集的数据转换成表格:

...
import pandas as pd
reviews = pd.DataFrame(reviews, columns=["user", "work", "stars"])
print(reviews.head())
            user      work  stars
0       van_stef   3206242    5.0
1       dwatson2  12198649    5.0
2       amdrane2  12981302    4.0
3  Lila_Gustavus   5231009    3.0
4      skinglist    184318    2.0

例如,为了节省时间和内存,我们尽量不使用所有数据。这里我们只考虑那些评论超过 50 本书的用户,也考虑那些被超过 50 个用户评论的书。通过这种方式,我们将数据集修剪到原始大小的 15%以下:

...
# Look for the users who reviewed more than 50 books
usercount = reviews[["work","user"]].groupby("user").count()
usercount = usercount[usercount["work"] >= 50]
print(usercount.head())
            work
user
              84
-Eva-        602
06nwingert   370
1983mk        63
1dragones    194
...
# Look for the books who reviewed by more than 50 users
workcount = reviews[["work","user"]].groupby("work").count()
workcount = workcount[workcount["user"] >= 50]
print(workcount.head())
          user
work
10000      106
10001       53
1000167    186
10001797    53
10005525   134
...
# Keep only the popular books and active users
reviews = reviews[reviews["user"].isin(usercount.index) & reviews["work"].isin(workcount.index)]
print(reviews)
                user     work  stars
0           van_stef  3206242    5.0
6            justine     3067    4.5
18           stephmo  1594925    4.0
19         Eyejaybee  2849559    5.0
35       LisaMaria_C   452949    4.5
...              ...      ...    ...
1387161     connie53     1653    4.0
1387177   BruderBane    24623    4.5
1387192  StuartAston  8282225    4.0
1387202      danielx  9759186    4.0
1387206     jclark88  8253945    3.0

[205110 rows x 3 columns]

然后我们可以利用熊猫中的“透视表”功能将其转换成矩阵:

...
reviewmatrix = reviews.pivot(index="user", columns="work", values="stars").fillna(0)

结果是 5593 行 2898 列的矩阵

这里我们用一个矩阵表示了 5593 个用户和 2898 本书。然后我们应用奇异值分解(这需要一段时间):

...
from numpy.linalg import svd
matrix = reviewmatrix.values
u, s, vh = svd(matrix, full_matrices=False)

默认情况下,svd()返回一个完整的奇异值分解。我们选择缩小版,这样我们可以使用更小的矩阵来节省内存。vh的栏目对应书籍。我们可以基于向量空间模型来找出哪本书与我们正在看的那本最相似:

...
import numpy as np
def cosine_similarity(v,u):
    return (v @ u)/ (np.linalg.norm(v) * np.linalg.norm(u))

highest_similarity = -np.inf
highest_sim_col = -1
for col in range(1,vh.shape[1]):
    similarity = cosine_similarity(vh[:,0], vh[:,col])
    if similarity > highest_similarity:
        highest_similarity = similarity
        highest_sim_col = col

print("Column %d is most similar to column 0" % highest_sim_col)

在上面的例子中,我们试图找到与第一列最匹配的书。结果是:

Column 906 is most similar to column 0

在推荐系统中,当用户挑选一本书时,我们可以根据上面计算的余弦距离向她展示几本与她挑选的书相似的书。

取决于数据集,我们可以使用截断奇异值分解来降低矩阵vh的维数。本质上,这意味着在我们使用它来计算相似度之前,我们正在删除vh上的几行,即s中对应的奇异值很小。这可能会使预测更加准确,因为一本书中那些不太重要的特征被排除在考虑之外。

请注意,在分解M=UΣVTM=U\cdot\Sigma\cdot V^T中,我们知道UU的行是用户,而VTV^T的列是书籍,我们无法识别UU的列或VTV^T的行(相当于Σ\Sigma)的含义。例如,我们知道它们可能是在用户和书籍之间提供某种潜在联系的流派,但我们无法确定它们到底是什么。然而,这并不妨碍我们在推荐系统中将它们作为特征使用。

以下是完整的代码:

import tarfile
import ast
import pandas as pd
import numpy as np

# Read downloaded file from:
# http://deepyeti.ucsd.edu/jmcauley/datasets/librarything/lthing_data.tar.gz
with tarfile.open("lthing_data.tar.gz") as tar:
    print("Files in tar archive:")
    tar.list()

    print("\nSample records:")
    with tar.extractfile("lthing_data/reviews.json") as file:
        count = 0
        for line in file:
            print(line)
            count += 1
            if count > 3:
                break

# Collect records
reviews = []
with tarfile.open("lthing_data.tar.gz") as tar:
    with tar.extractfile("lthing_data/reviews.json") as file:
        for line in file:
            try:
                record = ast.literal_eval(line.decode("utf8"))
            except:
                print(line.decode("utf8"))
                raise
            if any(x not in record for x in ['user', 'work', 'stars']):
                continue
            reviews.append([record['user'], record['work'], record['stars']])
print(len(reviews), "records retrieved")

# Print a few sample of what we collected
reviews = pd.DataFrame(reviews, columns=["user", "work", "stars"])
print(reviews.head())

# Look for the users who reviewed more than 50 books
usercount = reviews[["work","user"]].groupby("user").count()
usercount = usercount[usercount["work"] >= 50]

# Look for the books who reviewed by more than 50 users
workcount = reviews[["work","user"]].groupby("work").count()
workcount = workcount[workcount["user"] >= 50]

# Keep only the popular books and active users
reviews = reviews[reviews["user"].isin(usercount.index) & reviews["work"].isin(workcount.index)]
print("\nSubset of data:")
print(reviews)

# Convert records into user-book review score matrix
reviewmatrix = reviews.pivot(index="user", columns="work", values="stars").fillna(0)
matrix = reviewmatrix.values

# Singular value decomposition
u, s, vh = np.linalg.svd(matrix, full_matrices=False)

# Find the highest similarity
def cosine_similarity(v,u):
    return (v @ u)/ (np.linalg.norm(v) * np.linalg.norm(u))

highest_similarity = -np.inf
highest_sim_col = -1
for col in range(1,vh.shape[1]):
    similarity = cosine_similarity(vh[:,0], vh[:,col])
    if similarity > highest_similarity:
        highest_similarity = similarity
        highest_sim_col = col

print("Column %d (book id %s) is most similar to column 0 (book id %s)" %
        (highest_sim_col, reviewmatrix.columns[col], reviewmatrix.columns[0])
)

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

蜜蜂

文章

摘要

在本教程中,您发现了如何使用奇异值分解来构建推荐系统。

具体来说,您了解到:

  • 奇异值分解对矩阵意味着什么
  • 如何解释奇异值分解的结果
  • 从奇异值分解得到的矩阵VTV^T列中找出相似性,并根据相似性提出建议