Machine Learning Mastery 线性代数教程(一)
向量空间模型的温和介绍
最后更新于 2021 年 10 月 23 日
向量空间模型是考虑由向量表示的数据之间的关系。它在信息检索系统中很受欢迎,但也可用于其他目的。通常,这允许我们从几何角度比较两个向量的相似性。
在本教程中,我们将了解什么是向量空间模型以及它可以做什么。
完成本教程后,您将知道:
- 什么是向量空间模型和余弦相似性的性质
- 余弦相似度如何帮助你比较两个向量
- 余弦相似度和 L2 距离有什么区别
我们开始吧。
向量空间模型简介 图片由利亚姆弗莱彻提供,版权所有。
教程概述
本教程分为 3 个部分;它们是:
- 向量空间和余弦公式
- 使用向量空间模型进行相似性分析
- 向量空间模型和余弦距离的常用方法
向量空间和余弦公式
向量空间是定义一些向量运算的数学术语。用外行人的话来说,我们可以想象它是一个维的度量空间,其中每个点都由一个维的向量来表示。在这个空间中,我们可以做任何向量加法或标量向量乘法。
考虑向量空间是有用的,因为将事物表示为向量是有用的。例如在机器学习中,我们通常有一个具有多个特征的数据点。因此,我们可以方便地将数据点表示为向量。
有了向量,我们就可以计算出它的范数。最常见的是 L2 范数或向量的长度。在同一个向量空间中有两个向量,我们可以找到它们的区别。假设它是一个三维向量空间,这两个向量分别是和。它们的差是向量,差的 L2 范数是距离或者更准确地说是这两个向量之间的欧几里得距离:
除了距离,我们还可以考虑两个向量之间的角。如果我们把向量看作是从 3D 坐标系中的点到的线段,那么还有另一条从到。它们在交点处形成一个角度:
两条线段之间的角度可以用余弦公式计算:
$ \ cos \ theta = \ frac { a \ CDO b } { \ lvrt a \ servername _ 2 \ lvrt b \ servername _ 2 } { \ t1 }美元
其中是向量点积,而是向量的 L2 范数。这个公式来自于把点积看作向量到向量所指方向的投影。余弦的性质告诉我们,当角度从 0 度增加到 90 度时,余弦从 1 度减少到 0 度。有时我们称为余弦距离,因为当两个向量彼此远离时,余弦距离从 0 到 1。这是我们将在向量空间模型中利用的一个重要特性。
使用向量空间模型进行相似性分析
让我们看一个向量空间模型如何有用的例子。
世界银行收集世界上各个国家和地区的各种数据。虽然每个国家都不同,但我们可以尝试在向量空间模型下比较国家。为了方便起见,我们将使用 Python 中的pandas_datareader模块从世界银行读取数据。您可以使用pip或conda命令安装pandas_datareader:
pip install pandas_datareader
世界银行收集的数据系列由一个标识符命名。例如,“SP。URB。TOTL”是一个国家的城市总人口。许多系列是每年一次的。当我们下载一个系列时,我们必须输入开始和结束年份。通常数据不会及时更新。因此,最好查看几年前的数据,而不是最近一年的数据,以避免丢失数据。
下面我们试着收集一下 2010 年各个国家的一些经济数据:
from pandas_datareader import wb
import pandas as pd
pd.options.display.width = 0
names = [
"NE.EXP.GNFS.CD", # Exports of goods and services (current US$)
"NE.IMP.GNFS.CD", # Imports of goods and services (current US$)
"NV.AGR.TOTL.CD", # Agriculture, forestry, and fishing, value added (current US$)
"NY.GDP.MKTP.CD", # GDP (current US$)
"NE.RSB.GNFS.CD", # External balance on goods and services (current US$)
]
df = wb.download(country="all", indicator=names, start=2010, end=2010).reset_index()
countries = wb.get_countries()
non_aggregates = countries[countries["region"] != "Aggregates"].name
df_nonagg = df[df["country"].isin(non_aggregates)].dropna()
print(df_nonagg)
country year NE.EXP.GNFS.CD NE.IMP.GNFS.CD NV.AGR.TOTL.CD NY.GDP.MKTP.CD NE.RSB.GNFS.CD
50 Albania 2010 3.337089e+09 5.792189e+09 2.141580e+09 1.192693e+10 -2.455100e+09
51 Algeria 2010 6.197541e+10 5.065473e+10 1.364852e+10 1.612073e+11 1.132067e+10
54 Angola 2010 5.157282e+10 3.568226e+10 5.179055e+09 8.379950e+10 1.589056e+10
55 Antigua and Barbuda 2010 9.142222e+08 8.415185e+08 1.876296e+07 1.148700e+09 7.270370e+07
56 Argentina 2010 8.020887e+10 6.793793e+10 3.021382e+10 4.236274e+11 1.227093e+10
.. ... ... ... ... ... ... ...
259 Venezuela, RB 2010 1.121794e+11 6.922736e+10 2.113513e+10 3.931924e+11 4.295202e+10
260 Vietnam 2010 8.347359e+10 9.299467e+10 2.130649e+10 1.159317e+11 -9.521076e+09
262 West Bank and Gaza 2010 1.367300e+09 5.264300e+09 8.716000e+08 9.681500e+09 -3.897000e+09
264 Zambia 2010 7.503513e+09 6.256989e+09 1.909207e+09 2.026556e+10 1.246524e+09
265 Zimbabwe 2010 3.569254e+09 6.440274e+09 1.157187e+09 1.204166e+10 -2.871020e+09
[174 rows x 7 columns]
在上面,我们获得了 2010 年每个国家的一些经济指标。功能wb.download()将从世界银行下载数据,并返回一个熊猫数据帧。同样wb.get_countries()会得到世界银行认定的国家和地区名称,我们会用这个来过滤掉“东亚”“世界”等非国家聚合。Pandas 允许通过布尔索引过滤行,布尔索引df["country"].isin(non_aggregates)给出了布尔向量,表示哪一行在non_aggregates列表中,基于此,df[df["country"].isin(non_aggregates)]只选择那些行。由于各种原因,并非所有国家都有所有数据。因此,我们使用dropna()来删除那些丢失的数据。在实践中,我们可能希望应用一些插补技术,而不仅仅是去除它们。但是作为一个例子,我们继续剩下的 174 个数据点。
为了更好地说明这个想法,而不是隐藏熊猫或 numpy 函数中的实际操作,我们首先提取每个国家的数据作为向量:
...
vectors = {}
for rowid, row in df_nonagg.iterrows():
vectors[row["country"]] = row[names].values
print(vectors)
{'Albania': array([3337088824.25553, 5792188899.58985, 2141580308.0144,
11926928505.5231, -2455100075.33431], dtype=object),
'Algeria': array([61975405318.205, 50654732073.2396, 13648522571.4516,
161207310515.42, 11320673244.9655], dtype=object),
'Angola': array([51572818660.8665, 35682259098.1843, 5179054574.41704,
83799496611.2004, 15890559562.6822], dtype=object),
...
'West Bank and Gaza': array([1367300000.0, 5264300000.0, 871600000.0, 9681500000.0,
-3897000000.0], dtype=object),
'Zambia': array([7503512538.82554, 6256988597.27752, 1909207437.82702,
20265559483.8548, 1246523941.54802], dtype=object),
'Zimbabwe': array([3569254400.0, 6440274000.0, 1157186600.0, 12041655200.0,
-2871019600.0], dtype=object)}
我们创建的 Python 字典将每个国家的名称作为关键字,将经济指标作为 numpy 数组。有 5 个度量,因此每个都是 5 维的向量。
这对我们有帮助的是,我们可以使用每个国家的向量表示来看看它与另一个国家有多相似。让我们尝试差值的 L2 范数(欧几里得距离)和余弦距离。我们选择一个国家,如澳大利亚,并根据选定的经济指标将其与名单上的所有其他国家进行比较。
...
import numpy as np
euclid = {}
cosine = {}
target = "Australia"
for country in vectors:
vecA = vectors[target]
vecB = vectors[country]
dist = np.linalg.norm(vecA - vecB)
cos = (vecA @ vecB) / (np.linalg.norm(vecA) * np.linalg.norm(vecB))
euclid[country] = dist # Euclidean distance
cosine[country] = 1-cos # cosine distance
在上面的 for-loop 中,我们将vecA设置为目标国家(即澳大利亚)的向量,将vecB设置为其他国家的向量。然后我们计算它们的差的 L2 范数作为两个向量之间的欧几里得距离。我们还使用公式计算余弦相似度,并将其从 1 减去,以获得余弦距离。有一百多个国家,我们可以看到哪一个国家到澳大利亚的欧几里得距离最短:
...
import pandas as pd
df_distance = pd.DataFrame({"euclid": euclid, "cos": cosine})
print(df_distance.sort_values(by="euclid").head())
euclid cos
Australia 0.000000e+00 -2.220446e-16
Mexico 1.533802e+11 7.949549e-03
Spain 3.411901e+11 3.057903e-03
Turkey 3.798221e+11 3.502849e-03
Indonesia 4.083531e+11 7.417614e-03
通过对结果进行排序,我们可以看到在欧氏距离下,墨西哥距离澳大利亚最近。然而,用余弦距离,它是哥伦比亚最接近澳大利亚的。
...
df_distance.sort_values(by="cos").head()
euclid cos
Australia 0.000000e+00 -2.220446e-16
Colombia 8.981118e+11 1.720644e-03
Cuba 1.126039e+12 2.483993e-03
Italy 1.088369e+12 2.677707e-03
Argentina 7.572323e+11 2.930187e-03
为了理解为什么这两个距离给出不同的结果,我们可以观察这三个国家的度量标准是如何相互比较的:
...
print(df_nonagg[df_nonagg.country.isin(["Mexico", "Colombia", "Australia"])])
country year NE.EXP.GNFS.CD NE.IMP.GNFS.CD NV.AGR.TOTL.CD NY.GDP.MKTP.CD NE.RSB.GNFS.CD
59 Australia 2010 2.270501e+11 2.388514e+11 2.518718e+10 1.146138e+12 -1.180129e+10
91 Colombia 2010 4.682683e+10 5.136288e+10 1.812470e+10 2.865631e+11 -4.536047e+09
176 Mexico 2010 3.141423e+11 3.285812e+11 3.405226e+10 1.057801e+12 -1.443887e+10
从这张表中,我们看到澳大利亚和墨西哥的指标在数量上非常接近。然而,如果你比较同一个国家内每个指标的比率,哥伦比亚比澳大利亚更匹配。事实上,从余弦公式中,我们可以看到
$ \ cos \ theta = \ frac { a \ CDO b } { \ lvrt a \ servername _ 2 \ lvrt b \ servername _ 2 } = \ frac { a } { \ lvrt a \ servername _ 2 } \ CDO \ frac { b } { \ lvrt b \ servername _ 2 } { \ t1 }美元
这意味着两个向量之间角度的余弦值是对应向量归一化为长度 1 后的点积。因此,余弦距离实际上是在计算距离之前对数据应用缩放器。
综上所述,下面是完整的代码
from pandas_datareader import wb
import numpy as np
import pandas as pd
pd.options.display.width = 0
# Download data from World Bank
names = [
"NE.EXP.GNFS.CD", # Exports of goods and services (current US$)
"NE.IMP.GNFS.CD", # Imports of goods and services (current US$)
"NV.AGR.TOTL.CD", # Agriculture, forestry, and fishing, value added (current US$)
"NY.GDP.MKTP.CD", # GDP (current US$)
"NE.RSB.GNFS.CD", # External balance on goods and services (current US$)
]
df = wb.download(country="all", indicator=names, start=2010, end=2010).reset_index()
# We remove aggregates and keep only countries with no missing data
countries = wb.get_countries()
non_aggregates = countries[countries["region"] != "Aggregates"].name
df_nonagg = df[df["country"].isin(non_aggregates)].dropna()
# Extract vector for each country
vectors = {}
for rowid, row in df_nonagg.iterrows():
vectors[row["country"]] = row[names].values
# Compute the Euclidean and cosine distances
euclid = {}
cosine = {}
target = "Australia"
for country in vectors:
vecA = vectors[target]
vecB = vectors[country]
dist = np.linalg.norm(vecA - vecB)
cos = (vecA @ vecB) / (np.linalg.norm(vecA) * np.linalg.norm(vecB))
euclid[country] = dist # Euclidean distance
cosine[country] = 1-cos # cosine distance
# Print the results
df_distance = pd.DataFrame({"euclid": euclid, "cos": cosine})
print("Closest by Euclidean distance:")
print(df_distance.sort_values(by="euclid").head())
print()
print("Closest by Cosine distance:")
print(df_distance.sort_values(by="cos").head())
# Print the detail metrics
print()
print("Detail metrics:")
print(df_nonagg[df_nonagg.country.isin(["Mexico", "Colombia", "Australia"])])
向量空间模型和余弦距离的常用方法
向量空间模型在信息检索系统中很常见。我们可以将文档(例如,一段、一篇长文章、一本书甚至一句话)作为向量呈现。这个向量可以像计算文档包含的单词(即单词包模型)或复杂的嵌入向量(例如 Doc2Vec)一样简单。然后,可以通过按余弦距离对所有文档进行排序来回答查找最相关文档的查询。应该使用余弦距离,因为我们不想偏爱更长或更短的文档,而是专注于它包含的内容。因此,我们利用随之而来的规范化来考虑文档与查询的相关性,而不是查询中的单词在文档中被提及的次数。
如果我们将文档中的每个单词都视为一个特征,并计算余弦距离,则它是“硬”距离,因为我们不关心具有相似含义的单词(例如,“文档”和“段落”具有相似含义,但不关心“距离”)。嵌入像 word2vec 这样的向量将允许我们考虑本体。用所考虑的词的含义计算余弦距离就是“软余弦距离”。像 gensim 这样的库提供了一种方法。
余弦距离和向量空间模型的另一个用例是在计算机视觉中。想象一下识别手势的任务,我们可以把手的某些部位(如五指)作为关键点。然后,用关键点的(x,y)坐标作为向量,我们可以与现有的数据库进行比较,看看哪个余弦距离最近,并确定它是哪个手势。我们需要余弦距离,因为每个人的手都有不同的大小。我们不希望这影响我们对它所显示的姿态的决定。
正如你所想象的,你可以使用这种技术的例子还有很多。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
书
软件
文章
摘要
在本教程中,您发现了用于测量向量相似性的向量空间模型。
具体来说,您了解到:
- 如何构建向量空间模型
- 如何计算向量空间模型中两个向量之间的余弦相似度以及余弦距离
- 如何解释余弦距离和其他距离度量如欧氏距离之间的差异
- 向量空间模型有什么用*
什么是机器学习中的 Argmax?
最后更新于 2020 年 8 月 19 日
Argmax 是你在应用机器学习中可能会遇到的一个数学函数。
例如,您可能会在一篇用于描述算法的研究论文中看到“ argmax ”或“ arg max ”。您可能还会被指示在算法实现中使用 argmax 函数。
这可能是您第一次遇到 argmax 函数,您可能想知道它是什么以及它是如何工作的。
在本教程中,您将发现 argmax 函数及其在机器学习中的应用。
完成本教程后,您将知道:
- Argmax 是从目标函数中找到给出最大值的参数的操作。
- Argmax 在机器学习中最常用于寻找预测概率最大的类。
- Argmax 可以手动实现,尽管实践中首选 argmax() NumPy 函数。
用我的新书机器学习线性代数启动你的项目,包括循序渐进教程和所有示例的 Python 源代码文件。
我们开始吧。
机器学习中的 argmax 是什么? 伯纳德·斯普拉格摄。新西兰,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 什么是 Argmax?
- Argmax 在机器学习中是如何使用的?
- 如何在 Python 中实现 Argmax
什么是 Argmax?
Argmax 是一个数学函数。
它通常应用于另一个接受参数的函数。例如,给定一个采用参数 x 的函数 g() ,该函数的 argmax 操作将描述如下:
- 结果= argmax(g(x))
argmax 函数返回目标函数的一个或多个参数( arg ,目标函数从目标函数返回最大值( max )。
考虑这样的例子,其中 g(x) 被计算为 x 值的平方,并且输入值的域或范围( x )被限制为从 1 到 5 的整数:
- g(1) = 1² = 1
- g(2) = 2² = 4
- g(3) = 3² = 9
- g(4) = 4² = 16
- g(5) = 5² = 25
我们可以直观地看到函数 g(x) 的 argmax 为 5。
也就是说,从目标函数(25)得到最大值的目标函数 g() 的自变量( x )是 5。Argmax 提供了一种简写方式,可以在不知道特定情况下该值可能是什么的情况下,以抽象方式指定该参数。
- argmax(g(x)) = 5
请注意,这不是从函数返回的值的 max() 。这将是 25。
它也不是参数的 max() ,尽管在这种情况下参数的 argmax 和 max 是相同的,例如 5。 argmax() 是 5,因为当提供 5 时,g 返回最大值(25),而不是因为 5 是最大的参数。
通常,“ argmax ”被写成两个独立的单词,例如“ arg max ”。例如:
- 结果= arg 最大值(g(x))
将 arg max 函数用作目标函数周围没有括号的操作也很常见。这通常是你在研究论文或教科书中看到的操作。例如:
- 结果= arg 最大 g(x)
您也可以使用类似的操作来查找目标函数的参数,这些参数导致目标函数的最小值,称为 argmin 或“ arg min ”
Argmax 在机器学习中是如何使用的?
argmax 函数用于整个数学和机器学习领域。
然而,在一些特定的情况下,您会看到 argmax 被用于应用机器学习,并且可能需要自己实现它。
在应用机器学习中,使用 argmax 最常见的情况是找到导致最大值的数组的索引。
回想一下,数组是数字的列表或向量。
多类分类模型通常预测一个概率向量(或类似概率的值),每个类标签有一个概率。概率表示样本属于每个类别标签的可能性。
对预测概率进行排序,使得索引 0 处的预测概率属于第一类,索引 1 处的预测概率属于第二类,依此类推。
通常,对于多类分类问题,需要从一组预测概率中进行单类标签预测。
这种从预测概率向量到类标签的转换最常用 argmax 操作来描述,最常用 argmax 函数来实现。
让我们用一个例子来具体说明。
考虑一个三类的多类分类问题:“红色”、“蓝色”和“绿色”类标签被映射为用于建模的整数值,如下所示:
- 红色= 0
- 蓝色= 1
- 绿色= 2
每个类标签整数值映射到一个 3 元素向量的索引,该索引可以通过一个模型来预测,该模型指定了一个示例属于每个类的可能性。
假设一个模型对输入样本进行了一次预测,并预测了以下概率向量:
- yhat = [0.4,0.5,0.1]
我们可以看到,这个例子有 40%的概率属于红色,50%的概率属于蓝色,10%的概率属于绿色。
我们可以将 argmax 函数应用于概率向量。向量是函数,函数的输出是概率,函数的输入是向量元素索引或数组索引。
- arg max yhat
我们可以直观地看到,在这种情况下,预测概率向量(yhat)的 argmax 为 1,因为数组索引 1 处的概率是最大值。
请注意,这不是概率的最大值(),可能是 0.5。还要注意,这不是参数的最大值,应该是 2。取而代之的是导致最大值的参数,例如 1 导致 0.5。
- arg max yhat = 1
然后,我们可以将这个整数值映射回一个类标签,它将是蓝色的
- arg max yhat = "blue "
如何在 Python 中实现 Argmax
对于给定的数字向量,argmax 函数可以在 Python 中实现。
从头开始的 Argmax
首先,我们可以定义一个名为 argmax() 的函数,该函数枚举一个提供的向量并返回具有最大值的索引。
下面列出了完整的示例。
# argmax function
def argmax(vector):
index, value = 0, vector[0]
for i,v in enumerate(vector):
if v > value:
index, value = i,v
return index
# define vector
vector = [0.4, 0.5, 0.1]
# get argmax
result = argmax(vector)
print('arg max of %s: %d' % (vector, result))
运行该示例会打印上一节中使用的测试数据的 argmax,在本例中,该数据的索引为 1。
arg max of [0.4, 0.5, 0.1]: 1
带 NumPy 的 Argmax
谢天谢地,NumPy 库提供了一个内置版本的 argmax()函数。
这是你应该在实践中使用的版本。
下面的例子演示了同一个概率向量上的 argmax() NumPy 函数。
# numpy implementation of argmax
from numpy import argmax
# define vector
vector = [0.4, 0.5, 0.1]
# get argmax
result = argmax(vector)
print('arg max of %s: %d' % (vector, result))
如预期的那样,运行该示例将打印索引 1。
arg max of [0.4, 0.5, 0.1]: 1
更有可能的是,你会有多个样本的预测概率的集合。
这将被存储为一个矩阵,其中包含预测概率行,每列代表一个类标签。这个矩阵上 argmax 的期望结果是一个向量,每行预测有一个索引(或类标签整数)。
这可以通过 argmax() NumPy 函数通过设置“轴参数来实现。默认情况下,将为整个矩阵计算 argmax,返回一个数字。相反,我们可以将轴值设置为 1,并计算每一行数据跨列的 argmax。
下面的例子用三个类标签的四行预测概率矩阵来演示这一点。
# numpy implementation of argmax
from numpy import argmax
from numpy import asarray
# define vector
probs = asarray([[0.4, 0.5, 0.1], [0.0, 0.0, 1.0], [0.9, 0.0, 0.1], [0.3, 0.3, 0.4]])
print(probs.shape)
# get argmax
result = argmax(probs, axis=1)
print(result)
运行该示例首先打印预测概率矩阵的形状,确认我们有四行,每行三列。
然后计算矩阵的 argmax 并打印为向量,显示四个值。这就是我们所期望的,每一行都以最大的概率产生一个 argmax 值或索引。
(4, 3)
[1 2 0 2]
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
- num py . argmax API。
- 愤怒的马克斯,维基百科。
摘要
在本教程中,您发现了 argmax 函数以及它是如何在机器学习中使用的。
具体来说,您了解到:
- Argmax 是从目标函数中找到给出最大值的参数的操作。
- Argmax 在机器学习中最常用于寻找预测概率最大的类。
- Argmax 可以手动实现,尽管实践中首选 argmax() NumPy 函数。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
机器学习中数学符号的基础知识
原文:
machinelearningmastery.com/basics-mathematical-notation-machine-learning/
在阅读机器学习方法的描述时,你无法避免数学符号。
通常,所需要的只是一个术语中的一个术语或一个符号片段,以完全破坏您对整个过程的理解。这可能会非常令人沮丧,特别是对于来自开发世界的机器学习初学者。
如果您了解数学符号的一些基本领域以及在论文和书籍中完成机器学习方法描述的一些技巧,那么您可以取得很大的进步。
在本教程中,您将了解在阅读机器学习技术描述时可能遇到的数学符号的基础知识。
完成本教程后,您将了解:
- 算术表示法,包括乘法,指数,根和对数的变化。
- 序列和集合的表示法,包括索引,求和和集合成员资格。
- 5 如果您正在努力学习数学符号,可以使用技巧来获得帮助。
让我们开始吧。
- 更新 May / 2018 :为某些符号添加了图像以使解释更清晰。
机器学习数学符号的基础知识 Christian Collins 的照片,保留一些权利。
教程概述
本教程分为 7 个部分;他们是:
- 数学符号的挫败感
- 算术符号
- 希腊字母表
- 序列表示法
- 设置表示法
- 其他表示法
- 获得更多帮助
您认为我错过了机器学习所需的其他基本数学符号区域吗? 请在下面的评论中告诉我。
数学符号的挫败感
在阅读有关机器学习算法时,您会遇到数学符号。
例如,符号可用于:
- 描述一种算法。
- 描述数据准备。
- 描述结果。
- 描述一个测试工具。
- 描述含义。
这些描述可能出现在研究论文,教科书,博客文章和其他地方。
通常这些术语定义明确,但也有一些您可能不熟悉的数学符号规范。
所需要的只是您不理解的一个术语或一个等式,您对整个方法的理解将会丢失。我自己多次遭遇这个问题,令人非常沮丧!
在本教程中,我们将回顾一些基本的数学符号,这些符号在阅读机器学习方法的描述时将对您有所帮助。
算术符号
在本节中,我们将介绍一些不太明显的基本算术符号以及自学校以来可能忘记的一些概念。
简单的算术
基本算术的表示法就像你写的那样。例如:
- 增加:1 + 1 = 2
- 减法:2 - 1 = 1
- 乘法:2 x 2 = 4
- 分部:2/2 = 1
大多数数学运算都有一个执行逆运算的姐妹运算;例如,减法是加法的倒数,除法是乘法的倒数。
代数
我们经常希望抽象地描述操作,以将它们与特定数据或特定实现分开。
出于这个原因,我们看到代数的大量使用:这是用大写和/或小写字母或单词来表示数学符号中的术语或概念。使用希腊字母表中的字母也很常见。
数学的每个子字段都可以有保留字母:即始终表示相同内容的术语或字母。然而,代数术语应该被定义为描述的一部分,如果不是,它可能只是一个糟糕的描述,而不是你的错。
乘法符号
乘法是一种常见的符号,并且有一些简短的指针。
通常使用一点“x”或星号“*”来表示乘法:
c = a x b
c = a * b
您可能会看到使用点符号;例如:
c = a . b
这与以下相同:
c = a * b
或者,您可能会看到之前定义的术语之间没有操作且没有空格分隔;例如:
c = ab
这也是一回事。
指数和平方根
指数是一个提升到幂的数字。
符号被写为原始数字或基数,第二个数字或指数,显示为上标;例如:
2³
这将被计算为 2 乘以其自身 3 次或立方:
2 x 2 x 2 = 8
一个被提升到 2 的力量据说是它的正方形。
2² = 2 x 2 = 4
可以通过计算平方根来反转数字的平方。这是使用数字的符号和上面的勾号显示的,为简单起见,我将在这里使用“sqrt()”函数。
sqrt(4) = 2
在这里,我们知道结果和指数,我们希望找到基数。
实际上,根操作可用于反转任何指数,只是发生默认平方根假定指数为 2,由平方根刻度前面的下标 2 表示。
例如,我们可以通过取立方根来反转数字的立方(注意,3 这里不是乘法,它是根符号的刻度之前的符号):
2³ = 8
3 sqrt(8) = 2
对数和 e
当我们将 10 提高到整数指数时,我们通常将其称为一个数量级。
10² = 10 x 10 or 100
反转此操作的另一种方法是通过计算结果 100 的对数,假设基数为 10;在表示法中,这写为 log10()。
log10(100) = 2
在这里,我们知道结果和基数,并希望找到指数。
这使我们可以非常轻松地上下移动数量级。考虑到使用计算机中使用的二进制算法,假设基数为 2 的对数也是常用的。例如:
2⁶ = 64
log2(64) = 6
另一个流行的对数是假设称为 e 的自然基数。 e 是保留的,是一个特殊数字或常量,称为欧拉数(发音为“ oy-ler ”),它指的是具有几乎无限精度的值。
e = 2.71828...
将 e 提升为幂称为自然指数函数:
e² = 7.38905...
它可以使用自然对数反转,表示为 ln():
ln(7.38905...) = 2
在不详细描述的情况下,自然指数和自然对数在整个数学中证明是有用的,以抽象地描述某些系统的连续增长,例如:系数呈指数增长,如复利。
希腊字母表
希腊字母在数学符号中用于变量,常量,函数等。
例如,在统计中,我们使用小写的希腊字母 mu 和标准差作为小写的希腊字母 sigma 来讨论平均值。在线性回归中,我们将系数称为小写字母 beta。等等。
知道所有大写和小写希腊字母以及如何发音都很有用。
当我还是一名研究生时,我打印了希腊字母并将其粘贴在我的电脑显示器上,以便记住它。一个有用的技巧!
以下是完整的希腊字母。
希腊字母,取自维基百科
维基百科页面标题为“希腊字母,用于数学,科学和工程”也是一个有用的指南,因为它列出了在数学和科学的不同子领域中每个希腊字母的常见用法。
序列表示法
机器学习符号通常描述对序列的操作。
序列可以是数据数组或术语列表。
索引
读取序列符号的关键是序列中索引元素的表示法。
通常,符号将指定序列的开始和结束,例如 1 到 n,其中 n 将是序列的范围或长度。
序列中的项目由诸如 i,j,k 之类的变量作为下标索引。这就像数组符号。
例如,a_i 是序列 a 的第 i 个元素。
如果序列是二维的,则可以使用两个索引;例如:
b_ {i,j}是序列 b 的第 i,第 j 个元素。
顺序操作
可以在序列上执行数学运算。
对序列执行两个操作,因此它们有自己的简写:总和和乘法。
序列求和
序列的总和表示为大写的希腊字母 sigma。它用 sigma(例如 i = 1)下的序列求和的变量和开始以及 sigma(例如 n)之上的求和结束的索引来指定。
Sigma i = 1, n a_i
这是从元素 1 到元素 n 的序列 a 的总和。
序列乘法
序列上的乘法表示为大写希腊字母 pi。它的分秘籍式与分别在字母下方和上方的操作开始和结束的顺序求和相同。
Pi i = 1, n a_i
这是从元素 1 到元素 n 的序列 a 的乘积。
设置表示法
集合是一组唯一的项目。
我们可能会看到在机器学习中定义术语时使用的集合符号。
一组数字
您可能看到的一个常见集合是一组数字,例如定义为整数集合或实数集合的术语。
您可能会看到的一些常见数字包括:
- 所有自然数的集合:N
- 所有整数集:Z
- 所有实数的集合:R
还有其他套装;请参阅维基百科上的特别集。
在定义术语而不是浮点值时,我们经常谈论实际值或实数,浮点值实际上是计算机操作的离散创造。
设置会员资格
在术语定义中看到集合成员资格是很常见的。
设置成员资格表示为看起来像大写“E”的符号。
a E R
这意味着 a 被定义为集合 R 的成员或实数集合。
还有许多固定的操作;两个常见的集合操作包括:
- 联盟或聚合:A U B.
- 交点或重叠:A ^ B.
在 Wikipedia 上了解有关集的更多信息。
其他表示法
您可能会遇到其他符号。
我试着在本节中介绍一些。
通常在摘要中定义一个方法,然后再将其定义为具有单独表示法的特定实现。
例如,如果我们估计变量 x,我们可以使用修改 x 的表示法来表示它;例如:
相同的符号在不同的上下文中可以具有不同的含义,例如在不同对象或数学子场上的使用。例如,一个常见的混淆点是| x |,根据上下文,它可能意味着:
- | x |:x 的绝对值或正值。
- | x |:向量 x 的长度。
- | x |:集合 x 的基数。
本教程仅介绍了数学符号的基础知识。有一些数学子领域与机器学习更相关,应该进行更详细的审查。他们是:
- 线性代数。
- 统计。
- 可能性。
- 结石。
也许还有一点多变量分析和信息理论。
您认为这篇文章中是否缺少数学符号区域? 请在下面的评论中告诉我。
获得数学符号帮助的 5 个技巧
本节列出了在机器学习中使用数学符号时可以使用的一些技巧。
想想作者
人们写了你正在阅读的论文或书。
那些可能会犯错误,疏忽,甚至让事情变得混乱的人,因为他们并不完全理解他们所写的内容。
放松您正在阅读的符号的限制,并考虑作者的意图。他们想要碰到什么?
也许您甚至可以通过电子邮件,Twitter,Facebook,LinkedIn 等联系作者,并寻求澄清。请记住,学者希望其他人理解和使用他们的工作(主要是)。
检查维基百科
维基百科有一些符号列表,可以帮助缩小您正在阅读的符号的含义或意图。
我建议你开始的两个地方是:
- 维基百科上的数学符号列表
- 维基百科上的数学,科学和工程中使用的希腊字母
代码中的草图
数学运算只是数据的函数。
使用变量,for 循环等将您正在阅读的所有内容映射到伪代码。
您可能希望使用脚本语言,以及小型人为数据甚至是 Excel 电子表格。
随着您对该技术的阅读和理解的提高,您的技术代码草图将更有意义,最后您将拥有一个迷你原型。
直到我在一些 matlab 中用一些人为的数据看到一篇非常复杂的论文的学术草图之前,我从未习惯于采用这种方法。它打破了我的袜子因为我认为系统必须完全编码并使用“真实”数据集运行,唯一的选择是获取原始代码和数据。我错了。另外,回头看,这家伙很有天赋。
我现在一直使用这种方法并在 Python 中使用草图技术。
寻求替代方案
当我试图理解一种新技术时,我会使用一种技巧。
我找到并阅读了所有引用我正在阅读的论文的论文。
阅读其他学者对该技术的解释和重新解释通常可以澄清我在原始描述中的误解。
但并非总是如此。有时它会使水混浊并引入误导性的解释或新的符号。但通常情况下,它会有所帮助。在回到原始论文并重新阅读之后,我经常可以找到后续论文实际上对原始方法产生错误和误解的情况。
发表一个问题
在网上有人喜欢向别人解释数学的地方。认真!
考虑截取您正在努力的符号,写出完整的参考或链接到它,并将其和您的误解区域发布到问答网站。
两个很好的起点是:
通过数学符号你的技巧是什么? 请在下面的评论中告诉我?
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
摘要
在本教程中,您发现了在阅读机器学习技术描述时可能遇到的数学符号的基础知识。
具体来说,你学到了:
- 算术表示法,包括乘法,指数,根和对数的变化。
- 序列和集合的表示法,包括索引,求和和集合成员资格。
- 5 如果您正在努力学习数学符号,可以使用技巧来获得帮助。
你在用数学符号挣扎吗?
这篇文章中的任何符号或提示有帮助吗? 请在下面的评论中告诉我。
NumPy 数组广播的温和介绍
原文:
machinelearningmastery.com/broadcasting-with-numpy-arrays/
不能添加,减去或通常在算术中使用具有不同大小的数组。
克服这个问题的一种方法是复制较小的数组,使其尺寸和大小与较大的数组相同。这称为数组广播,在执行数组运算时可在 NumPy 中使用,这可以大大减少和简化代码。
在本教程中,您将发现数组广播的概念以及如何在 NumPy 中实现它。
完成本教程后,您将了解:
- 具有不同大小的数组的算术问题。
- 广播的解决方案和一维和二维的常见例子。
- 数组广播规则和广播失败时。
让我们开始吧。
使用 NumPy 数组进行广播的介绍 pbkwee 的照片,保留一些权利。
教程概述
本教程分为 4 个部分;他们是:
- 数组运算的局限性
- 数组广播
- 在 NumPy 广播
- 广播的局限性
数组运算的局限性
您可以直接在 NumPy 数组上执行算术运算,例如加法和减法。
例如,可以将两个数组相加以创建一个新数组,其中每个索引的值都加在一起。
例如,数组 a 可以定义为[1,2,3],数组 b 可以定义为[1,2,3],加在一起将产生一个值为[2,4,6]的新数组。
a = [1, 2, 3]
b = [1, 2, 3]
c = a + b
c = [1 + 1, 2 + 2, 3 + 3]
严格地说,算术只能在具有相同尺寸和尺寸的相同尺寸的数组上执行。
这意味着长度为 10 的一维数组只能与另一个长度为 10 的一维数组进行算术运算。
对数组算术的这种限制确实非常有限。值得庆幸的是,NumPy 提供了一个内置的解决方法,允许在具有不同大小的数组之间进行算术运算。
数组广播
广播是 NumPy 用于允许具有不同形状或大小的数组之间的数组运算的方法的名称。
尽管该技术是针对 NumPy 开发的,但它在其他数值计算库中也被广泛采用,例如 Theano , TensorFlow 和 Octave 。
广播通过实际上沿着最后的不匹配维度复制较小的数组来解决不同形状的数组之间的算术问题。
术语广播描述了 numpy 如何在算术运算期间处理具有不同形状的数组。受某些约束的影响,较小的数组在较大的数组上“广播”,以便它们具有兼容的形状。
- 广播,SciPy.org
NumPy 实际上并没有复制较小的数组;相反,它使存储器和计算上有效地使用存储器中的现有结构,实际上实现了相同的结果。
该概念还渗透了线性代数符号,以简化简单操作的解释。
在深度学习的背景下,我们也使用一些不太常规的符号。我们允许添加矩阵和向量,产生另一个矩阵:C = A + b,其中 Ci,j = Ai,j + bj。换句话说,向量 b 被添加到矩阵的每一行。这种简化消除了在添加之前定义将 b 复制到每行中的矩阵的需要。将 b 隐式复制到许多位置称为广播。
- 第 34 页,深度学习,2016 年。
在 NumPy 广播
我们可以通过查看 NumPy 中的三个例子来制作广播。
本节中的示例并非详尽无遗,而是与您可能看到或实现的广播类型相同。
标量和一维数组
单个值或标量可用于具有一维数组的算术。
例如,我们可以设想一维数组“a”,其中三个值[a1,a2,a3]被添加到标量“b”。
a = [a1, a2, a3]
b
标量需要通过将其值重复 2 次来跨一维数组进行广播。
b = [b1, b2, b3]
然后可以直接添加两个一维数组。
c = a + b
c = [a1 + b1, a2 + b2, a3 + b3]
以下示例在 NumPy 中演示了这一点。
# scalar and one-dimensional
from numpy import array
a = array([1, 2, 3])
print(a)
b = 2
print(b)
c = a + b
print(c)
运行该示例首先打印定义的一维数组,然后是标量,然后是结果,其中标量被添加到数组中的每个值。
[1 2 3]
2
[3 4 5]
标量和二维数组
标量值可用于具有二维数组的算术。
例如,我们可以想象一个二维数组“A”,其中 2 行和 3 列添加到标量“b”。
a11, a12, a13
A = (a21, a22, a23)
b
标量将需要在二维数组的每一行上进行广播,方法是将其复制 5 次。
b11, b12, b13
B = (b21, b22, b23)
然后可以直接添加两个二维数组。
C = A + B
a11 + b11, a12 + b12, a13 + b13
C = (a21 + b21, a22 + b22, a23 + b23)
The example below demonstrates this in NumPy.
# scalar and two-dimensional
from numpy import array
A = array([[1, 2, 3], [1, 2, 3]])
print(A)
b = 2
print(b)
C = A + b
print(C)
运行该示例首先打印定义的二维数组,然后打印标量,然后将值为“2”的加法结果添加到数组中的每个值。
[[1 2 3]
[1 2 3]]
2
[[3 4 5]
[3 4 5]]
一维和二维数组
一维数组可用于具有二维数组的算术。
例如,我们可以想象一个二维数组“A”,其中 2 行 3 列添加到具有 3 个值的一维数组“b”。
a11, a12, a13
A = (a21, a22, a23)
b = (b1, b2, b3)
通过创建第二副本以产生新的二维数组“B”,在二维数组的每一行上广播一维数组。
b11, b12, b13
B = (b21, b22, b23)
The two two-dimensional arrays can then be added directly.
C = A + B
a11 + b11, a12 + b12, a13 + b13
C = (a21 + b21, a22 + b22, a23 + b23)
以下是 NumPy 中的一个有效例子。
# one-dimensional and two-dimensional
from numpy import array
A = array([[1, 2, 3], [1, 2, 3]])
print(A)
b = array([1, 2, 3])
print(b)
C = A + b
print(C)
运行该示例首先打印定义的二维数组,然后打印定义的一维数组,接着是结果 C,其中实际上二维数组中的每个值都加倍。
[[1 2 3]
[1 2 3]]
[1 2 3]
[[2 4 6]
[2 4 6]]
广播的局限性
广播是一种方便的快捷方式,在使用 NumPy 数组时在实践中非常有用。
话虽如此,它并不适用于所有情况,实际上强加了一个严格的规则,必须满足广播要执行。
算术(包括广播)只能在数组中每个维度的形状相等或者维度大小为 1 时执行。维度以相反的顺序考虑,从尾随维度开始;例如,在二维情况下查看行之前的列。
当我们考虑 NumPy 在比较数组时实际填充缺少尺寸为“1”的尺寸时,这更有意义。
因此,具有 2 行和 3 列的二维数组“A”与具有 3 个元素的向量“b”之间的比较:
A.shape = (2 x 3)
b.shape = (3)
实际上,这成了一个比较:
A.shape = (2 x 3)
b.shape = (1 x 3)
同样的概念适用于被视为具有所需维数的数组的标量之间的比较:
A.shape = (2 x 3)
b.shape = (1)
这成为以下方面的比较:
A.shape = (2 x 3)
b.shape = (1 x 1)
当比较失败时,不能执行广播,并且引发错误。
下面的示例尝试将两元素数组广播到 2 x 3 数组。这种比较有效:
A.shape = (2 x 3)
b.shape = (1 x 2)
我们可以看到最后的维度(列)不匹配,我们希望广播失败。
The example below demonstrates this in NumPy.
# broadcasting error
from numpy import array
A = array([[1, 2, 3], [1, 2, 3]])
print(A.shape)
b = array([1, 2])
print(b.shape)
C = A + b
print(C)
运行该示例首先打印数组的形状,然后在尝试广播时引发错误,正如我们预期的那样。
(2, 3)
(2,)
ValueError: operands could not be broadcast together with shapes (2,3) (2,)
扩展
本节列出了一些扩展您可能希望探索的教程的想法。
- 使用 NumPy 数组创建三个新的和不同的广播示例。
- 实现您自己的广播功能,以便在一维和二维情况下进行手动广播。
- 基准 NumPy 广播和您自己的自定义广播功能,具有非常大的数组的一维和二维情况。
如果你探索任何这些扩展,我很想知道。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
图书
- 第二章,深度学习,2016 年。
用品
- 广播,NumPy API ,SciPy.org
- TensorFlow 中的广播语义
- 数组广播在 numpy ,EricsBroadcastingDoc 中
- 广播,Theano
- Numpy ,2015 年的广播数组。
- 八度广播
摘要
在本教程中,您发现了数组广播的概念以及如何在 NumPy 中实现。
具体来说,你学到了:
- 具有不同大小的数组的算术问题。
- 广播的解决方案和一维和二维的常见例子。
- 数组广播规则和广播失败时。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
如何在 Python 中从零开始计算主成分分析(PCA)
原文:
machinelearningmastery.com/calculate-principal-component-analysis-scratch-python/
降维的重要机器学习方法称为主成分分析。
这是一种使用线性代数和统计学中的简单矩阵运算来计算原始数据到相同数量或更少维度的投影的方法。
在本教程中,您将发现用于降低维数的主成分分析机器学习方法以及如何在 Python 中从零开始实现它。
完成本教程后,您将了解:
- 计算主成分分析的过程以及如何选择主成分。
- 如何在 NumPy 中从零开始计算主成分分析。
- 如何计算主成分分析,以便在 scikit-learn 中使用更多数据。
让我们开始吧。
- Update Apr / 2018 :修正了 sklearn PCA 属性解释中的拼写错误。由于克里斯。
如何在 Python 中从零开始计算主成分分析 照片由 mickey ,保留一些权利。
教程概述
本教程分为 3 个部分;他们是:
- 主成分分析
- 手动计算主成分分析
- 可重复使用的主成分分析
主成分分析
主成分分析(简称 PCA)是一种减少数据维数的方法。
它可以被认为是一种投影方法,其中具有 m 列(特征)的数据被投影到具有 m 个或更少列的子空间中,同时保留原始数据的本质。
可以使用线性代数工具来描述和实现 PCA 方法。
PCA 是应用于数据集的操作,由 n×m 矩阵 A 表示,其导致 A 的投影,我们将称之为 B.让我们逐步完成此操作的步骤。
a11, a12
A = (a21, a22)
a31, a32
B = PCA(A)
第一步是计算每列的平均值。
M = mean(A)
要么
(a11 + a21 + a31) / 3
M(m11, m12) = (a12 + a22 + a32) / 3
接下来,我们需要通过减去平均列值来使每列中的值居中。
C = A - M
下一步是计算居中矩阵 C 的协方差矩阵。
相关性是两列一起变化的量和方向(正或负)的标准化度量。协方差是跨多列的相关的广义和非标准化版本。协方差矩阵是给定矩阵的协方差的计算,每个列与每个其他列的协方差分数,包括其自身。
V = cov(C)
最后,我们计算协方差矩阵 V 的特征分解。这导致特征值列表和特征向量列表。
values, vectors = eig(V)
特征向量表示 B 的缩小子空间的方向或分量,而特征值表示方向的大小。
特征向量可以按特征值按降序排序,以提供 A 的新子空间的分量或轴的等级。
如果所有特征值都具有相似的值,那么我们就知道现有的表示可能已经被合理地压缩或密集,并且投影可能提供的很少。如果存在接近零的特征值,则它们表示可以被丢弃的 B 的分量或轴。
必须选择总共 m 个或更少的组件来组成所选择的子空间。理想情况下,我们将选择具有 k 个最大特征值的 k 个本征向量,称为主成分。
B = select(values, vectors)
可以使用其他矩阵分解方法,例如奇异值分解或 SVD。因此,通常将这些值称为奇异值,并将子空间的向量称为主要分量。
一旦选择,可以通过矩阵乘法将数据投影到子空间中。
P = B^T . A
其中 A 是我们希望投影的原始数据,B ^ T 是所选主成分的转置,P 是 A 的投影。
这被称为计算 PCA 的协方差方法,尽管有其他方法可以计算它。
手动计算主成分分析
NumPy 中没有 pca()函数,但我们可以使用 NumPy 函数轻松地逐步计算主成分分析。
下面的例子定义了一个小的 3×2 矩阵,将数据置于矩阵中心,计算中心数据的协方差矩阵,然后计算协方差矩阵的特征分解。特征向量和特征值作为主成分和奇异值,用于投影原始数据。
from numpy import array
from numpy import mean
from numpy import cov
from numpy.linalg import eig
# define a matrix
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
# calculate the mean of each column
M = mean(A.T, axis=1)
print(M)
# center columns by subtracting column means
C = A - M
print(C)
# calculate covariance matrix of centered matrix
V = cov(C.T)
print(V)
# eigendecomposition of covariance matrix
values, vectors = eig(V)
print(vectors)
print(values)
# project data
P = vectors.T.dot(C.T)
print(P.T)
运行该示例首先打印原始矩阵,然后打印中心协方差矩阵的特征向量和特征值,最后是原始矩阵的投影。
有趣的是,我们可以看到只需要第一个特征向量,这表明我们可以将 3×2 矩阵投影到 3×1 矩阵上而几乎没有损失。
[[1 2]
[3 4]
[5 6]]
[[ 0.70710678 -0.70710678]
[ 0.70710678 0.70710678]]
[ 8\. 0.]
[[-2.82842712 0\. ]
[ 0\. 0\. ]
[ 2.82842712 0\. ]]
可重复使用的主成分分析
我们可以使用 scikit-learn 库中的 PCA()类计算数据集的主成分分析。这种方法的好处是,一旦计算出投影,它就可以很容易地一次又一次地应用于新数据。
创建类时,可以将组件数指定为参数。
首先通过调用 fit()函数将类放在数据集上,然后通过调用 transform()函数将原始数据集或其他数据投影到具有所选维数的子空间中。
一旦拟合,可以通过explain_variance_和components_属性在 PCA 类上访问特征值和主成分。
下面的示例演示了如何使用此类,首先创建一个实例,将其拟合到 3×2 矩阵上,访问投影的值和向量,以及转换原始数据。
# Principal Component Analysis
from numpy import array
from sklearn.decomposition import PCA
# define a matrix
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
# create the PCA instance
pca = PCA(2)
# fit on data
pca.fit(A)
# access values and vectors
print(pca.components_)
print(pca.explained_variance_)
# transform data
B = pca.transform(A)
print(B)
运行该示例首先打印 3×2 数据矩阵,然后是主要分量和值,然后是原始矩阵的投影。
我们可以看到,通过一些非常小的浮点舍入,我们可以获得与前一个示例中相同的主成分,奇异值和投影。
[[1 2]
[3 4]
[5 6]]
[[ 0.70710678 0.70710678]
[ 0.70710678 -0.70710678]]
[ 8.00000000e+00 2.25080839e-33]
[[ -2.82842712e+00 2.22044605e-16]
[ 0.00000000e+00 0.00000000e+00]
[ 2.82842712e+00 -2.22044605e-16]]
扩展
本节列出了一些扩展您可能希望探索的教程的想法。
- 使用您自己的小设计矩阵值重新运行示例。
- 加载数据集并计算其上的 PCA 并比较两种方法的结果。
- 搜索并找到 PCA 用于机器学习论文的 10 个例子。
如果你探索任何这些扩展,我很想知道。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
图书
API
用品
教程
- 主成分分析与 numpy ,2011。
- PCA 和图像压缩与 numpy ,2011。
- 实现主成分分析(PCA),2014 年。
摘要
在本教程中,您发现了降低维数的主成分分析机器学习方法。
具体来说,你学到了:
- 计算主成分分析的过程以及如何选择主成分。
- 如何在 NumPy 中从零开始计算主成分分析。
- 如何计算主成分分析,以便在 scikit-learn 中使用更多数据。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
面向程序员的计算线性代数回顾
原文:
machinelearningmastery.com/computational-linear-algebra-coders-review/
数值线性代数关注在具有实际数据的计算机中实现和执行矩阵运算的实际意义。
这是一个需要先前线性代数经验的领域,并且重点关注操作的表现和精度。 fast.ai 公司发布了一个名为“_ 计算线性代数 _”的免费课程,主题是数字线性代数,包括旧金山大学录制的 Python 笔记本和视频讲座。
在这篇文章中,您将发现计算线性代数的 fast.ai 免费课程。
阅读这篇文章后,你会知道:
- 课程的动机和先决条件。
- 课程中涵盖的主题概述。
- 这门课程究竟适合谁,不适合谁。
让我们开始吧。
用于编码器审查的计算线性代数 照片由 Ruocaled ,保留一些权利。
课程大纲
课程“_ 编码器计算线性代数 _”是由 fast.ai 提供的免费在线课程。他们是一家致力于提供与深度学习相关的免费教育资源的公司。
该课程最初由旧金山大学的 Rachel Thomas 于 2017 年授课,作为硕士学位课程的一部分。 Rachel Thomas 是旧金山大学的教授,也是 fast.ai 的联合创始人,拥有博士学位。在数学方面。
该课程的重点是线性代数的数值方法。这是矩阵代数在计算机上的应用,并解决了实现和使用方法(如表现和精度)的所有问题。
本课程的重点是:我们如何以可接受的速度和可接受的准确度进行矩阵计算?
本课程使用 Python 作为使用 NumPy,scikit-learn,numba,pytorch 等的示例。
这些材料使用自上而下的方法进行教学,就像 MachineLearningMastery 一样,旨在让人们了解如何做事,然后再解释这些方法的工作原理。
了解如何实现这些算法将使您能够更好地组合和利用它们,并使您可以根据需要自定义它们。
课程先决条件和参考
该课程确实假设熟悉线性代数。
这包括诸如向量,矩阵,诸如矩阵乘法和变换之类的操作之类的主题。
该课程不适用于线性代数领域的新手。
如果您是新的或生锈的线性代数,建议您在参加课程之前查看三个参考文献。他们是:
- 3Blue 1Brown 线性代数本质,视频课程
- 沉浸式线性代数,交互式教科书
- 深度学习第二章,2016 年。
此外,在完成课程的过程中,根据需要提供参考。
预先提出了两个一般参考文本。它们是以下教科书:
课程比赛
本节概述了课程的 8(9)部分。他们是:
- 0.课程物流
- 我们为什么来这里?
- 2.使用 NMF 和 SVD 进行主题建模
- 3.使用强大的 PCA 去除背景
- 4.具有鲁棒回归的压缩感知
- 5.使用线性回归预测健康结果
- 6.如何实现线性回归
- 7.具有特征分解的 PageRank
- 8.实现 QR 分解
实际上,课程只有 8 个部分,因为第一部分是参加旧金山大学课程的学生的管理细节。
讲座细分
在本节中,我们将逐步介绍课程的 9 个部分,并总结其内容和主题,让您对所期待的内容有所了解,并了解它是否适合您。
第零部分。课程后勤
第一堂课不是课程的一部分。
它介绍了讲师,材料,教学方式以及学生对硕士课程的期望。
我将使用自上而下的教学方法,这与大多数数学课程的运作方式不同。通常,在自下而上的方法中,您首先要学习将要使用的所有单独组件,然后逐渐将它们构建为更复杂的结构。这方面的问题是学生经常失去动力,没有“大局”感,也不知道他们需要什么。
本讲座涉及的主题是:
- 讲师背景
- 教学方法
- 技术写作的重要性
- 优秀技术博客列表
- 线性代数评论资源
视频和笔记本:
第一部分。为什么我们在这里?
本部分介绍了本课程的动机,并介绍了矩阵分解的重要性:这些计算的表现和准确率以及一些示例应用程序的重要性。
矩阵无处不在,任何可以放在 Excel 电子表格中的东西都是矩阵,语言和图片也可以表示为矩阵。
本讲中提出的一个重点是,如何将整类矩阵分解方法和一种特定方法(QR 分解)报告为 20 世纪十大最重要算法之一。
20 世纪十大科学与工程算法列表包括:线性代数的矩阵分解方法。它还包括 QR 算法
The topics covered in this lecture are:
- 矩阵和张量积
- 矩阵分解
- 准确率
- 内存使用
- 速度
- 并行化&向量
Videos and Notebook:
第二部分。使用 NMF 和 SVD 进行主题建模
本部分重点介绍矩阵分解在文本主题建模应用中的应用,特别是奇异值分解方法或 SVD。
在这一部分中有用的是从零开始或与 NumPy 和 scikit-learn 库计算方法的比较。
主题建模是开始使用矩阵分解的好方法。
The topics covered in this lecture are:
- 主题频率 - 逆文档频率(TF-IDF)
- 奇异值分解(SVD)
- 非负矩阵分解(NMF)
- 随机梯度下降(SGD)
- PyTorch 介绍
- 截断 SVD
Videos and Notebook:
第三部分。使用强大的 PCA 去除背景
本部分重点介绍使用特征分解和多变量统计的主成分分析方法(PCA)。
重点是在图像数据上使用 PCA,例如将背景与前景分离以隔离变化。这部分还从零开始介绍 LU 分解。
在处理高维数据集时,我们经常利用数据具有低内在维度的事实,以减轻维度和规模的诅咒(可能它位于低维子空间或位于低维流形上)。
The topics covered in this lecture are:
- 加载和查看视频数据
- SVD
- 主成分分析(PCA)
- L1 Norm 引起稀疏性
- 强大的 PCA
- LU 分解
- LU 的稳定性
- 使用 Pivoting 进行 LU 分解
- 高斯消除的历史
- 块矩阵乘法
Videos and Notebook:
第四部分。具有鲁棒回归的压缩感知
这部分介绍了 NumPy 数组(和其他地方)中使用的广播的重要概念以及在机器学习中出现很多的稀疏矩阵。
该部分的应用重点是使用强大的 PCA 在 CT 扫描中去除背景。
术语广播描述了在算术运算期间如何处理具有不同形状的数组。 Numpy 首先使用广播一词,但现在用于其他库,如 Tensorflow 和 Matlab;规则因库而异。
The topics covered in this lecture are:
- 广播
- 稀疏矩阵
- CT 扫描和压缩感知
- L1 和 L2 回归
Videos and Notebook:
第五部分。使用线性回归预测健康结果
本部分重点介绍用 scikit-learn 演示的线性回归模型的开发。
Numba 库也用于演示如何加速所涉及的矩阵操作。
我们想加快速度。我们将使用 Numba,一个直接将代码编译到 C 的 Python 库。
The topics covered in this lecture are:
- sklearn 中的线性回归
- 多项式特征
- 加速 Numba
- 正规化与噪声
Videos and Notebook:
第六部分。如何实现线性回归
本部分介绍如何使用一套不同的矩阵分解方法求解线性回归的线性最小二乘法。将结果与 scikit-learn 中的实现进行比较。
数值分析师推荐通过 QR 进行线性回归作为多年来的标准方法。它自然,优雅,适合“日常使用”。
The topics covered in this lecture are:
- Scikit Learn 是如何做到的?
- 朴素的解决方案
- 正规方程和 Cholesky 分解
- QR 分解
- SVD
- 时间比较
- 调节&稳定性
- 完全与减少的因子分解
- 矩阵反转是不稳定的
Videos and Notebook:
第七部分。具有特征分解的 PageRank
本部分介绍了特征分解以及 PageRank 算法在 Wikipedia 链接数据集中的实现和应用。
QR 算法使用称为 QR 分解的东西。两者都很重要,所以不要让他们感到困惑。
The topics covered in this lecture are:
- SVD
- DBpedia 数据集
- 动力法
- QR 算法
- 寻找特征值的两阶段方法
- Arnoldi Iteration
Videos and Notebook:
第八部分。实现 QR 分解
最后一部分介绍了从零开始实现 QR 分解的三种方法,并比较了每种方法的精度和表现。
我们在计算特征值时使用 QR 分解并计算最小二乘回归。它是数值线性代数中的重要组成部分。
The topics covered in this lecture are:
- 格拉姆 - 施密特
- 住户
- 稳定性例子
Videos and Notebook:
评论课程
我觉得这个课程很棒。
一个有趣的数值线性代数步骤,重点是应用程序和可执行代码。
该课程承诺关注矩阵操作的实际问题,如记忆,速度,精度或数值稳定性。本课程首先仔细研究浮点精度和溢出问题。
在整个过程中,经常在方法之间根据执行速度进行比较。
如果......不要参加这个课程
本课程不是开发人员对线性代数的介绍,如果这是预期,你可能会落后。
该课程确实假设了线性代数,符号和操作的基础知识的合理流畅性。它并没有预先隐藏这个假设。
如果您对深度学习感兴趣或者更多地了解深度学习方法中使用的线性代数运算,我认为这门课程不是必需的。
参加这门课程,如果......
如果您正在自己的工作中实现矩阵代数方法,并且您希望从中获得更多,我强烈推荐这门课程。
如果您通常对矩阵代数的实际意义感兴趣,我也会推荐这门课程。
进一步阅读
如果您希望深入了解,本节将提供有关该主题的更多资源。
课程
参考
- 3Blue 1Brown 线性代数本质,视频课程
- 沉浸式线性代数,交互式教科书
- 深度学习第二章
- 数值线性代数,1997。
- 数值方法,2012。
摘要
在这篇文章中,您发现了计算线性代数的 fast.ai 免费课程。
具体来说,你学到了:
- 课程的动机和先决条件。
- 课程中涵盖的主题概述。
- 这门课程究竟适合谁,不适合谁。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
10 个机器学习中的线性代数示例
原文:
machinelearningmastery.com/examples-of-linear-algebra-in-machine-learning/
线性代数是涉及向量,矩阵和线性变换的数学子领域。
它是机器学习领域的关键基础,从用于描述算法运算的符号到代码中算法的实现。
尽管线性代数是机器学习领域不可或缺的一部分,但紧密关系通常无法解释或使用抽象概念(如向量空间或特定矩阵运算)进行解释。
在这篇文章中,您将发现 10 个常见的机器学习示例,您可能熟悉这些使用,需要并且使用线性代数最好地理解它们。
阅读这篇文章后,你会知道:
- 在处理数据时使用线性代数结构,例如表格数据集和图像。
- 处理数据准备时的线性代数概念,例如单热编码和降维。
- 在深度学习,自然语言处理和推荐系统等子领域中根深蒂固地使用线性代数符号和方法。
让我们开始吧。
10 机器学习中的线性代数示例 照片由 j。 Barbosa ,保留一些权利。
概观
在这篇文章中,我们将回顾机器学习中线性代数的 10 个明显而具体的例子。
我试图选择您可能熟悉或甚至曾经使用过的示例。他们是:
- 数据集和数据文件
- 图像和照片
- 单热编码
- 线性回归
- 正则
- 主成分分析
- 奇异值分解
- 潜在语义分析
- 推荐系统
- 深度学习
你在机器学习中有自己喜欢的线性代数的例子吗? 请在下面的评论中告诉我。
1.数据集和数据文件
在机器学习中,您可以在数据集上拟合模型。
这是一组类似于数字的数字,其中每行代表一个观察,每列代表观察的一个特征。
例如,下面是鸢尾花数据集的片段:
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
这个数据实际上是一个矩阵:线性代数中的关键数据结构。
此外,当您将数据拆分为输入和输出以适应监督机器学习模型(例如测量和花卉种类)时,您有一个矩阵(X)和一个向量(y)。向量是线性代数中的另一个关键数据结构。
每行具有相同的长度,即相同数量的列,因此我们可以说数据是向量化的,其中行可以一次提供给一个模型,也可以批量提供,并且可以预先配置模型以期望行固定宽度。
2.图像和照片
也许您更习惯于在计算机视觉应用中处理图像或照片。
您使用的每个图像本身都是一个具有宽度和高度的表结构,每个单元格中的一个像素值用于黑白图像,或者每个单元格中有 3 个像素值用于彩色图像。
照片是线性代数矩阵的另一个例子。
使用线性代数的符号和操作来描述图像上的操作,例如裁剪,缩放,剪切等。
3.单热编码
有时您在机器学习中使用分类数据。
也许是分类问题的类标签,或者可能是分类输入变量。
通常对分类变量进行编码,以使它们更容易使用并通过某些技术进行学习。分类变量的流行编码是一种热编码。
单热门编码是创建一个表来表示变量,每个类别有一列,数据集中每个例子都有一行。对于给定行的分类值,将在列中添加一个检查或一个值,并将零值添加到所有其他列。
例如,带有 3 行的颜色变量:
red
green
blue
...
可能被编码为:
red, green, blue
1, 0, 0
0, 1, 0
0, 0, 1
...
每行被编码为二进制向量,具有零或一个值的向量,这是稀疏表示的示例,线性代数的整个子场。
4.线性回归
线性回归是统计学中用于描述变量之间关系的旧方法。
它通常用于机器学习中,用于在更简单的回归问题中预测数值。
有许多方法可以描述和解决线性回归问题,即找到一组系数,当这些系数乘以每个输入变量并加在一起时,可以得到输出变量的最佳预测。
如果您使用过机器学习工具或库,最常用的求解线性回归的方法是通过最小二乘优化,使用线性回归的矩阵分解方法求解,例如 LU 分解或奇异值分解,或 SVD 。
即使是总结线性回归方程的常用方法也使用线性代数符号:
y = A . b
其中 y 是输出变量 A 是数据集,b 是模型系数。
5.正规化
在应用机器学习中,我们经常寻求最简单的模型来实现我们问题的最佳技能。
更简单的模型通常更好地从特定示例推广到看不见的数据。
在涉及系数的许多方法中,例如回归方法和人工神经网络,较简单的模型通常以具有较小系数值的模型为特征。
通常用于鼓励模型在系数适合数据时最小化系数的技术称为正则化。常见的实现包括 L2 和 L1 形式的正则化。
这两种形式的正则化实际上是作为向量的系数的大小或长度的度量,并且是直接从称为向量范数的线性代数中提取的方法。
6.主成分分析
通常,数据集具有许多列,可能是数十,数百,数千或更多。
使用许多功能对数据建模具有挑战性,并且根据包含不相关特征的数据构建的模型通常不如从最相关数据训练的模型熟练。
很难知道哪些数据特征是相关的,哪些不相关。
自动减少数据集列数的方法称为降维,也许最流行的方法称为主成分分析,简称 PCA。
该方法用于机器学习,以创建可视化和训练模型的高维数据的投影。
PCA 方法的核心是线性代数的矩阵分解方法。可以使用特征分解,并且更鲁棒的实现可以使用奇异值分解或 SVD。
7.奇异值分解
另一种流行的降维方法是奇异值分解方法,简称 SVD。
如上所述,并且正如该方法的名称所暗示的,它是来自线性代数领域的矩阵分解方法。
它广泛用于线性代数,可直接用于特征选择,可视化,降噪等应用。
我们将在机器学习中使用 SVD 以下两个案例。
8.潜在语义分析
在用于处理称为自然语言处理的文本数据的机器学习的子领域中,通常将文档表示为单词出现的大矩阵。
例如,矩阵的列可以是词汇表中的已知单词,行可以是句子,段落,页面或文本文档,矩阵中的单元格被标记为单词出现次数的计数或频率。
这是文本的稀疏矩阵表示。矩阵分解方法,例如奇异值分解,可以应用于该稀疏矩阵,其具有将表示提取到其最相关的本质的效果。以这种方式处理的文档更容易比较,查询和使用,作为监督机器学习模型的基础。
这种形式的数据准备称为潜在语义分析(简称 LSA),也称为潜在语义索引或 LSI。
9.推荐系统
涉及产品推荐的预测性建模问题称为推荐系统,即机器学习的子领域。
例如,基于您之前在亚马逊上购买和购买的客户推荐的书籍,以及根据您的观看历史记录观看电影和电视节目的建议,以及在 Netflix 上查看像您这样的订阅者的历史记录。
推荐系统的开发主要涉及线性代数方法。一个简单的例子是使用诸如欧几里德距离或点积的距离测量来计算稀疏客户行为向量之间的相似性。
像奇异值分解这样的矩阵分解方法在推荐系统中被广泛使用,以将项目和用户数据提炼到其本质,用于查询和搜索和比较。
10.深度学习
人工神经网络是非线性机器学习算法,其受到大脑中信息处理元素的启发,并且在一系列问题中被证明是有效的,其中最重要的是预测性建模。
深度学习是人工神经网络最近的复兴,它采用了更新的方法和更快的硬件,可以在非常大的数据集上开发和训练更大更深(更多层)的网络。深度学习方法通常可以在一系列具有挑战性的问题上实现最先进的结果,例如机器翻译,照片字幕,语音识别等等。
在其核心,神经网络的执行涉及线性代数数据结构的乘法和加法。扩展到多维度,深度学习方法适用于向量,矩阵,甚至输入和系数的张量,其中张量是具有两个以上维度的矩阵。
线性代数是深度学习方法描述的核心,通过矩阵表示法实现深度学习方法,例如 Google 的 TensorFlow Python 库,其名称中包含“tensor”一词。
摘要
在这篇文章中,您发现了 10 个常见的机器学习示例,您可能熟悉它们并使用线性代数。
具体来说,你学到了:
- 在处理表格数据集和图像等数据时使用线性代数结构。
- 处理数据准备时的线性代数概念,例如单热编码和降维。
- 在深度学习,自然语言处理和推荐系统等子领域中根深蒂固地使用线性代数符号和方法。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
将主成分分析用于人脸识别
最后更新于 2021 年 10 月 30 日
最近机器学习的进步使得人脸识别不是一个难题。但在此之前,研究人员进行了各种尝试,开发了各种技能,使计算机能够识别人。其中一个早期的尝试取得了一定的成功,这就是基于线性代数技术的特征脸。
在本教程中,我们将看到如何使用一些简单的线性代数技术(如主成分分析)来构建一个原始的人脸识别系统。
完成本教程后,您将知道:
- 特征脸技术的发展
- 如何利用主成分分析从图像数据集中提取特征图像
- 如何将任意图像表示为特征图像的加权和
- 如何从主成分的权重来比较图像的相似性
我们开始吧。
使用主成分分析的人脸识别 照片作者: Rach Teo ,版权所有。
教程概述
本教程分为 3 个部分;它们是:
- 图像和人脸识别
- 特征脸概述
- 实现特征脸
图像和人脸识别
在计算机中,图片被表示为一个像素矩阵,每个像素都有一个特定的颜色编码在一些数值中。很自然的会问计算机能不能读懂图片,理解它是什么,如果能,是否能用矩阵数学来描述逻辑。为了不那么雄心勃勃,人们试图将这个问题的范围限制在识别人脸上。人脸识别的早期尝试是将矩阵视为高维细节,我们从中推断出一个低维信息向量,然后尝试在低维中识别该人。在过去,这是必要的,因为计算机功能不强,内存非常有限。然而,通过探索如何将图像压缩到更小的尺寸,我们开发了一种技术来比较两幅图像是否描绘了同一张人脸,即使图片不完全相同。
1987 年,西罗维奇和柯比的一篇论文认为人脸的所有图片都是一些“关键图片”的加权和。西罗维奇和柯比将这些关键图片称为“特征图片”,因为它们是人脸减去平均值后的图片的协方差矩阵的特征向量。在论文中,他们确实提供了矩阵形式的人脸图像数据集的主成分分析算法。并且在加权和中使用的权重确实对应于人脸图像到每个特征图像的投影。
1991 年,特克和彭特兰的一篇论文创造了“特征脸”这个术语。他们建立在西罗维奇和柯比的思想之上,并使用权重和特征图作为特征特征来识别人脸。图尔克和彭特兰的论文提出了一种计算特征图像的有效记忆方法。它还提出了人脸识别系统如何运行的算法,包括如何更新系统以包含新的人脸,以及如何将其与视频捕获系统相结合。该文还指出,特征脸的概念有助于部分遮挡图像的重建。
特征脸概述
在我们进入代码之前,让我们概述使用特征脸进行人脸识别的步骤,并指出一些简单的线性代数技术如何帮助完成任务。
假设我们有一堆人脸图片,都在同一个像素维度(例如,都是 r×c 灰度图像)。如果我们得到 M 张不同的图片并将每张图片矢量化为 L=r×c 像素,我们可以将整个数据集表示为 L×M 矩阵(我们称之为矩阵),其中矩阵中的每个元素都是像素的灰度值。
回想一下,主成分分析(PCA)可以应用于任何矩阵,结果是许多称为主成分的向量。每个主成分的长度与矩阵的列长度相同。同一矩阵的不同主成分是相互正交的,这意味着其中任何两个主成分的矢量点积为零。因此,各种主成分构建了一个向量空间,对于该向量空间,矩阵中的每一列可以表示为主成分的线性组合(即加权和)。
方法是首先取其中是矩阵的平均向量。所以是用平均向量减去的每一列的矩阵。那么协方差矩阵是
从中我们可以找到它的特征向量和特征值。主成分是特征值递减顺序的特征向量。因为矩阵是 L×L 矩阵,我们可以考虑寻找 M×M 矩阵的特征向量来代替,因为的特征向量可以通过转换成的特征向量,除了我们通常更喜欢把写成归一化向量(即的范数是 1)。
的主成分向量,或等价于的特征向量的物理意义是,它们是我们可以构造矩阵的列的关键方向。不同主成分向量的相对重要性可以从相应的特征值中推断出来。特征值越大,主成分向量就越有用(即保存更多关于)的信息。因此,我们只能保留前 K 个主成分向量。如果矩阵是人脸图片的数据集,那么前 K 个主成分向量就是前 K 个最重要的“人脸图片”。我们称它们为特征脸图片。
对于任何给定的人脸图像,我们可以使用矢量点积将其减去平均值的版本投影到特征人脸图像上。结果是这张人脸图片与特征脸的关系有多密切。如果人脸图片与特征脸完全无关,我们会期望它的结果是零。对于 K 个特征脸,我们可以找到任意给定人脸图像的 K 点积。我们可以将结果表示为这张人脸图片相对于特征脸的权重。权重通常表示为向量。
相反,如果我们有一个权重向量,我们可以将每个受权重影响的特征脸相加,重建一个新的脸。让我们将特征面表示为矩阵,它是一个 L×K 矩阵,权重向量是一个列向量。然后对于任何我们可以构建一张脸的图片为
其中是长度为 l 的列向量的结果。因为我们只使用了顶部的 K 个主成分向量,所以我们应该预期得到的人脸图像是失真的,但保留了一些面部特征。
因为特征脸矩阵对于数据集是恒定的,所以变化的权重向量意味着变化的人脸图片。因此,我们可以预期同一个人的照片会提供相似的权重向量,即使照片不完全相同。因此,我们可以利用两个权重向量之间的距离(如 L2 范数)作为衡量两幅图片相似程度的指标。
实现特征脸
现在我们尝试用 numpy 和 Sklearn 实现特征脸的思想。我们也会利用 OpenCV 来读取图片文件。您可能需要使用pip命令安装相关软件包:
pip install opencv-python
我们使用的数据集是 ORL 人脸数据库,这个数据库已经很老了,但是我们可以从 Kaggle 下载:
该文件是一个 4MB 左右的压缩文件。它有 40 个人的照片,每个人有 10 张照片。总计 400 张图片。在下文中,我们假设文件被下载到本地目录并命名为attface.zip。
我们可以提取 zip 文件获取图片,也可以利用 Python 中的zipfile包直接从 zip 文件中读取内容:
import cv2
import zipfile
import numpy as np
faces = {}
with zipfile.ZipFile("attface.zip") as facezip:
for filename in facezip.namelist():
if not filename.endswith(".pgm"):
continue # not a face picture
with facezip.open(filename) as image:
# If we extracted files from zip, we can use cv2.imread(filename) instead
faces[filename] = cv2.imdecode(np.frombuffer(image.read(), np.uint8), cv2.IMREAD_GRAYSCALE)
上面是读取 zip 中的每个 PGM 文件。PGM 是一种灰度图像文件格式。我们通过image.read()将每个 PGM 文件提取为一个字节字符串,并将其转换为一个 numpy 字节数组。然后我们使用 OpenCV 使用cv2.imdecode()将字节串解码成像素阵列。该文件格式将由 OpenCV 自动检测。我们将每张图片保存到 Python 字典faces中以备后用。
这里我们可以用 matplotlib 来看看这些人脸照片:
...
import matplotlib.pyplot as plt
fig, axes = plt.subplots(4,4,sharex=True,sharey=True,figsize=(8,10))
faceimages = list(faces.values())[-16:] # take last 16 images
for i in range(16):
axes[i%4][i//4].imshow(faceimages[i], cmap="gray")
plt.show()
我们还可以找到每张图片的像素大小:
...
faceshape = list(faces.values())[0].shape
print("Face image shape:", faceshape)
Face image shape: (112, 92)
在 Python 字典中,人脸的图片由它们的文件名来标识。我们可以浏览一下文件名:
...
print(list(faces.keys())[:5])
['s1/1.pgm', 's1/10.pgm', 's1/2.pgm', 's1/3.pgm', 's1/4.pgm']
因此我们可以把同一个人的脸放入同一类。共有 40 节课,共 400 张图片:
...
classes = set(filename.split("/")[0] for filename in faces.keys())
print("Number of classes:", len(classes))
print("Number of pictures:", len(faces))
Number of classes: 40
Number of pictures: 400
为了说明使用特征脸进行识别的能力,我们想在生成特征脸之前保留一些图片。我们拿出一个人的所有照片以及另一个人的一张照片作为我们的测试集。剩余的图片被矢量化并转换成 2D 数字阵列:
...
# Take classes 1-39 for eigenfaces, keep entire class 40 and
# image 10 of class 39 as out-of-sample test
facematrix = []
facelabel = []
for key,val in faces.items():
if key.startswith("s40/"):
continue # this is our test set
if key == "s39/10.pgm":
continue # this is our test set
facematrix.append(val.flatten())
facelabel.append(key.split("/")[0])
# Create facematrix as (n_samples,n_pixels) matrix
facematrix = np.array(facematrix)
现在我们可以对这个数据集矩阵进行主成分分析。我们没有一步一步地计算主成分分析,而是利用 Sklearn 中的主成分分析功能,我们可以轻松检索所需的所有结果:
...
# Apply PCA to extract eigenfaces
from sklearn.decomposition import PCA
pca = PCA().fit(facematrix)
我们可以从解释的方差比中确定每个主成分的重要性:
...
print(pca.explained_variance_ratio_)
[1.77824822e-01 1.29057925e-01 6.67093882e-02 5.63561346e-02
5.13040312e-02 3.39156477e-02 2.47893586e-02 2.27967054e-02
1.95632067e-02 1.82678428e-02 1.45655853e-02 1.38626271e-02
1.13318896e-02 1.07267786e-02 9.68365599e-03 9.17860717e-03
8.60995215e-03 8.21053028e-03 7.36580634e-03 7.01112888e-03
6.69450840e-03 6.40327943e-03 5.98295099e-03 5.49298705e-03
5.36083980e-03 4.99408106e-03 4.84854321e-03 4.77687371e-03
...
1.12203331e-04 1.11102187e-04 1.08901471e-04 1.06750318e-04
1.05732991e-04 1.01913786e-04 9.98164783e-05 9.85530209e-05
9.51582720e-05 8.95603083e-05 8.71638147e-05 8.44340263e-05
7.95894118e-05 7.77912922e-05 7.06467912e-05 6.77447444e-05
2.21225931e-32]
或者我们可以简单地组成一个适中的数,比如说 50,然后把这些主成分向量看作特征面。为了方便起见,我们从主成分分析结果中提取特征脸,并将其存储为 numpy 数组。请注意,本征面以行的形式存储在矩阵中。如果我们想展示它,我们可以把它转换回 2D。在下面,我们展示了一些特征脸,看看它们是什么样子的:
...
# Take the first K principal components as eigenfaces
n_components = 50
eigenfaces = pca.components_[:n_components]
# Show the first 16 eigenfaces
fig, axes = plt.subplots(4,4,sharex=True,sharey=True,figsize=(8,10))
for i in range(16):
axes[i%4][i//4].imshow(eigenfaces[i].reshape(faceshape), cmap="gray")
plt.show()
从这张照片中,我们可以看到特征脸是模糊的脸,但实际上每个特征脸都有一些可以用来构建图片的面部特征。
由于我们的目标是建立一个人脸识别系统,我们首先计算每个输入图片的权重向量:
...
# Generate weights as a KxN matrix where K is the number of eigenfaces and N the number of samples
weights = eigenfaces @ (facematrix - pca.mean_).T
上面的代码是用矩阵乘法代替循环。大致相当于以下内容:
...
weights = []
for i in range(facematrix.shape[0]):
weight = []
for j in range(n_components):
w = eigenfaces[j] @ (facematrix[i] - pca.mean_)
weight.append(w)
weights.append(weight)
至此,我们的人脸识别系统已经完成。我们用 39 个人的照片来建立我们的特征脸。我们使用属于这 39 个人中的一个人的测试图片(从训练主成分分析模型的矩阵中拿出的图片)来看它是否能成功识别人脸:
...
# Test on out-of-sample image of existing class
query = faces["s39/10.pgm"].reshape(1,-1)
query_weight = eigenfaces @ (query - pca.mean_).T
euclidean_distance = np.linalg.norm(weights - query_weight, axis=0)
best_match = np.argmin(euclidean_distance)
print("Best match %s with Euclidean distance %f" % (facelabel[best_match], euclidean_distance[best_match]))
# Visualize
fig, axes = plt.subplots(1,2,sharex=True,sharey=True,figsize=(8,6))
axes[0].imshow(query.reshape(faceshape), cmap="gray")
axes[0].set_title("Query")
axes[1].imshow(facematrix[best_match].reshape(faceshape), cmap="gray")
axes[1].set_title("Best match")
plt.show()
上面,我们首先用从主成分分析结果中检索到的平均向量减去矢量化图像。然后我们计算这个减去平均值的向量到每个特征面的投影,并把它作为这张图片的权重。然后,我们将所讨论的图片的权重向量与每个现有图片的权重向量进行比较,并找到具有最小 L2 距离的图片作为最佳匹配。我们可以看到,它确实可以成功地在同一个类中找到最接近的匹配:
Best match s39 with Euclidean distance 1559.997137
我们可以通过并排比较最接近的匹配来可视化结果:
我们可以用 PCA 中第 40 个人的照片再试一次。我们永远不会把它纠正过来,因为它对我们的模型来说是一个新人。但是,我们想看看它的错误程度以及距离度量中的值:
...
# Test on out-of-sample image of new class
query = faces["s40/1.pgm"].reshape(1,-1)
query_weight = eigenfaces @ (query - pca.mean_).T
euclidean_distance = np.linalg.norm(weights - query_weight, axis=0)
best_match = np.argmin(euclidean_distance)
print("Best match %s with Euclidean distance %f" % (facelabel[best_match], euclidean_distance[best_match]))
# Visualize
fig, axes = plt.subplots(1,2,sharex=True,sharey=True,figsize=(8,6))
axes[0].imshow(query.reshape(faceshape), cmap="gray")
axes[0].set_title("Query")
axes[1].imshow(facematrix[best_match].reshape(faceshape), cmap="gray")
axes[1].set_title("Best match")
plt.show()
我们可以看到,它的最佳匹配具有更大的 L2 距离:
Best match s5 with Euclidean distance 2690.209330
但是我们可以看到,错误的结果与所讨论的图片有一些相似之处:
在特克和佩特兰的论文中,建议我们为 L2 距离设置一个阈值。如果最佳匹配的距离小于阈值,我们会认为该人脸被识别为同一个人。如果距离超过了阈值,我们就声称这张照片是我们从未见过的人,即使在数字上可以找到最佳匹配。在这种情况下,我们可以考虑通过记住这个新的权重向量,将它作为一个新的人包含到我们的模型中。
实际上,我们可以更进一步,使用特征脸生成新的人脸,但是结果不是很现实。在下面,我们使用随机权重向量生成一个,并将其与“平均脸”并排显示:
...
# Visualize the mean face and random face
fig, axes = plt.subplots(1,2,sharex=True,sharey=True,figsize=(8,6))
axes[0].imshow(pca.mean_.reshape(faceshape), cmap="gray")
axes[0].set_title("Mean face")
random_weights = np.random.randn(n_components) * weights.std()
newface = random_weights @ eigenfaces + pca.mean_
axes[1].imshow(newface.reshape(faceshape), cmap="gray")
axes[1].set_title("Random face")
plt.show()
特征脸有多好?令人惊讶的是,它因模型的简单性而被超越。然而,特克和彭特兰在各种条件下对其进行了测试。它发现它的精确度是“光线变化时平均 96%,方向变化时平均 85%,尺寸变化时平均 64%。”因此,它作为人脸识别系统可能不太实用。毕竟作为矩阵的图片在放大缩小后会在主成分域失真很多。因此,现代的替代方法是使用卷积神经网络,它对各种变换更加宽容。
将所有内容放在一起,下面是完整的代码:
import zipfile
import cv2
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
# Read face image from zip file on the fly
faces = {}
with zipfile.ZipFile("attface.zip") as facezip:
for filename in facezip.namelist():
if not filename.endswith(".pgm"):
continue # not a face picture
with facezip.open(filename) as image:
# If we extracted files from zip, we can use cv2.imread(filename) instead
faces[filename] = cv2.imdecode(np.frombuffer(image.read(), np.uint8), cv2.IMREAD_GRAYSCALE)
# Show sample faces using matplotlib
fig, axes = plt.subplots(4,4,sharex=True,sharey=True,figsize=(8,10))
faceimages = list(faces.values())[-16:] # take last 16 images
for i in range(16):
axes[i%4][i//4].imshow(faceimages[i], cmap="gray")
print("Showing sample faces")
plt.show()
# Print some details
faceshape = list(faces.values())[0].shape
print("Face image shape:", faceshape)
classes = set(filename.split("/")[0] for filename in faces.keys())
print("Number of classes:", len(classes))
print("Number of images:", len(faces))
# Take classes 1-39 for eigenfaces, keep entire class 40 and
# image 10 of class 39 as out-of-sample test
facematrix = []
facelabel = []
for key,val in faces.items():
if key.startswith("s40/"):
continue # this is our test set
if key == "s39/10.pgm":
continue # this is our test set
facematrix.append(val.flatten())
facelabel.append(key.split("/")[0])
# Create a NxM matrix with N images and M pixels per image
facematrix = np.array(facematrix)
# Apply PCA and take first K principal components as eigenfaces
pca = PCA().fit(facematrix)
n_components = 50
eigenfaces = pca.components_[:n_components]
# Show the first 16 eigenfaces
fig, axes = plt.subplots(4,4,sharex=True,sharey=True,figsize=(8,10))
for i in range(16):
axes[i%4][i//4].imshow(eigenfaces[i].reshape(faceshape), cmap="gray")
print("Showing the eigenfaces")
plt.show()
# Generate weights as a KxN matrix where K is the number of eigenfaces and N the number of samples
weights = eigenfaces @ (facematrix - pca.mean_).T
print("Shape of the weight matrix:", weights.shape)
# Test on out-of-sample image of existing class
query = faces["s39/10.pgm"].reshape(1,-1)
query_weight = eigenfaces @ (query - pca.mean_).T
euclidean_distance = np.linalg.norm(weights - query_weight, axis=0)
best_match = np.argmin(euclidean_distance)
print("Best match %s with Euclidean distance %f" % (facelabel[best_match], euclidean_distance[best_match]))
# Visualize
fig, axes = plt.subplots(1,2,sharex=True,sharey=True,figsize=(8,6))
axes[0].imshow(query.reshape(faceshape), cmap="gray")
axes[0].set_title("Query")
axes[1].imshow(facematrix[best_match].reshape(faceshape), cmap="gray")
axes[1].set_title("Best match")
plt.show()
# Test on out-of-sample image of new class
query = faces["s40/1.pgm"].reshape(1,-1)
query_weight = eigenfaces @ (query - pca.mean_).T
euclidean_distance = np.linalg.norm(weights - query_weight, axis=0)
best_match = np.argmin(euclidean_distance)
print("Best match %s with Euclidean distance %f" % (facelabel[best_match], euclidean_distance[best_match]))
# Visualize
fig, axes = plt.subplots(1,2,sharex=True,sharey=True,figsize=(8,6))
axes[0].imshow(query.reshape(faceshape), cmap="gray")
axes[0].set_title("Query")
axes[1].imshow(facematrix[best_match].reshape(faceshape), cmap="gray")
axes[1].set_title("Best match")
plt.show()
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
报纸
- 长度西罗维奇和 m .柯比(1987)。“人脸表征的低维程序”。美国光学学会杂志 A 。4(3): 519–524.
- 米(meter 的缩写))特克和彭特兰(1991)。"用于识别的特征脸"。认知神经科学杂志。3(1): 71–86.
书
- 线性代数导论,第五版,2016。
蜜蜂
文章
- 维基百科上的特征脸
摘要
在本教程中,您发现了如何使用特征脸构建人脸识别系统,该特征脸是从主成分分析中导出的。
具体来说,您了解到:
- 如何利用主成分分析从图像数据集中提取特征图像
- 如何使用该组特征图像为任何可见或不可见的图像创建权重向量
- 如何利用不同图像的权重向量来度量它们的相似性,并将该技术应用于人脸识别
- 如何从特征图像生成新的随机图像