机器学习近年来备受瞩目,每天都有新的应用和模型涌现。全球的研究人员不断发布实验结果,展示了机器学习领域取得的巨大进步。
许多技术人员参加各种课程,收集资料,希望通过这些新技术提升他们的应用水平。然而,要深入了解机器学习,往往需要深厚的数学背景。这使得那些虽具备良好算法技能但数学基础薄弱的程序员面临较高的入门门槛。
为了更好地理解和掌握机器学习的核心原理,有必要复习并建立所有基本的逻辑知识体系,包括统计学、概率论和微积分。
接下来,我们将从一些基础的统计概念入手。
统计学是利用数据样本来提取和验证更大数据集结论的学科。考虑到机器学习是研究数据属性和数据估值的重要组成部分,本书将广泛运用统计学概念来定义和证明各种方法。
我们将从描述性统计学的基本操作和方法开始,将基本概念作为起点。
平均值是统计学中直观且常用的指标。给定一组数字,这组数字的平均值是所有元素之和除以元素数量。
平均值的公式如下:
[ text{平均值} = frac{sum{i=1}^{n} xi}{n} ]
尽管这是一个简单的概念,但本书提供了Python代码示例,以帮助读者更好地理解。在这个示例中,我们将创建样本集,并用线图表示它,将整个集合的平均值标记为一条线,这条线应位于样本的加权中心。这不仅可以介绍Python语法,还可以展示如何在Jupyter Notebook中进行实验。代码如下:
```python import matplotlib.pyplot as plt
def mean(sampleset): total = 0 for element in sampleset: total += element return total / len(sampleset)
myset = [2., 10., 3., 6., 4., 6., 10.] mymean = mean(myset) plt.plot(myset) plt.plot([mymean] * 7) ```
该程序将输入数据集元素的时间序列,然后在平均高度上绘制一条线。
如图1.6所示,平均值是一种简洁(单值)地描述样本集趋势的方法。
图1.6:用平均值描述样本集趋势
由于在第一个例子中,我们使用了一个非常均匀的样本集,所以平均值可以有效地反映这些样本值。
接下来,我们可以尝试用一个非常分散的样本集(鼓励读者尝试这些值)进行实验,如图1.7所示。
图1.7:分散样本集的趋势
正如后文所述,平均值不足以描述非均匀或非常分散的数据样本。
为了用一个单一的值来描述样本值的分散程度,我们需要引入方差的概念。它需要以样本集的平均值为基础,然后计算样本值到平均值的距离的平均值。方差越大,样本集越分散。
方差的定义如下:
[ text{方差} = frac{sum{i=1}^{n} (xi - bar{x})^2}{n} ]
以下示例代码演示了这个概念。为了清晰起见,这里重复了mean
函数的声明。代码如下:
```python import math
def mean(sampleset): total = 0 for element in sampleset: total += element return total / len(sampleset)
def variance(sampleset): total = 0 setmean = mean(sampleset) for element in sampleset: total += (math.pow(element - setmean, 2)) return total / len(sampleset)
myset1 = [2., 10., 3., 6., 4., 6., 10.] myset2 = [1., -100., 15., -100., 21.]
print("方差的第一组样本:" + str(variance(myset1))) print("方差的第二组样本:" + str(variance(myset2))) ```
代码将输出以下结果:
方差的第一组样本:8.69387755102
方差的第二组样本:3070.64
正如结果所示,当样本值非常分散时,第二组的方差更高。由于计算距离平方的平均值是一个二次运算,它有助于反映这些差异。
标准差是对方差中使用的均方值进行正则化的一种手段,它有效地将该项线性化。这种方法可用于其他更复杂的操作。
标准差的表示方式如下:
[ text{标准差} = sqrt{text{方差}} ]
概率与随机变量对于理解本书涉及的概念至关重要。
概率(Probability) 是一门数学学科,其主要目的是研究随机事件。从实际角度来看,概率试图从所有可能发生的事件中量化事件发生的确定性(或不确定性)。
为了理解概率,我们首先定义事件。在给定的实验中,执行确定的动作可能会出现不同的结果。事件是该实验中所有可能结果的子集。
例如,掷骰子时出现特定数字,或在线装配过程中出现某种产品缺陷,都是事件的例子。
(1)概率
根据定义,概率是事件发生的可能性。概率被量化为0到1之间的实数。当事件发生的可能性增加时,概率P也按接近于1的趋势增加。事件发生概率的数学表达式为P(E)。
(2)随机变量和分布
在分配事件概率时,可以尝试覆盖整个样本空间,并为样本空间中的每个事件分配一个概率值。
这个过程具有函数的所有特征。对于每一个随机变量,都会为其可能的事件结果分配一个值。这个函数称为随机函数。
这些变量有两种类型: - 离散(Discrete):结果的数量是有限的或可数无限的。 - 连续(Continuous):结果集属于连续区间。
这个概率函数也称为概率分布(Probability Distribution)。
在众多可能的概率分布中,有些函数因其特殊性质或它们所代表问题的普遍性而受到特别关注。
本书将介绍一些常见的概率分布。它们对机器学习的发展具有重要意义。
(1)伯努利分布(Bernoulli Distribution)
从一个简单的分布开始:像抛硬币一样,它具有二分类结果。
这个分布表示单个事件。该事件中1(正面)的概率为p,0(反面)的概率为1-p。
为了实现可视化,可以使用NumPy库生成大量伯努利分布的事件,并绘制该分布的趋势。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
distro = np.random.binomial(1, 0.6, 10000) / 0.5 plt.hist(distro, 2, normed=1) ```
图1.8:二项分布
可以看到,结果概率具有互补趋势。
现在,我们用更多可能的结果来扩展模型。当结果的数量大于2时,采用多项式分布(Multinomial Distribution)。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
distro = np.random.binomial(100, 0.6, 10000) / 0.01 plt.hist(distro, 100, normed=1) plt.show() ```
图1.9:100种可能结果的多项式分布
(2)均匀分布(Uniform Distribution)
这种非常常见的分布是本书介绍的第一个连续分布。顾名思义,对于域的任何区间,它都有一个恒定的概率值。
a和b是函数的极值,为了使函数积分为1,这个概率值为1/(b-a)。
以下代码生成均匀分布的样本。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
uniformlow = 0.25 uniformhigh = 0.8 plt.hist(np.random.uniform(uniformlow, uniformhigh, 10000), 50, normed=1) plt.show() ```
图1.10:均匀分布
(3)正态分布(Normal Distribution)
这是一种常见的连续随机函数,也称为高斯函数(Gaussian Function)。尽管表达式较为复杂,但它只需要用均值和方差来定义。
这是函数的标准形式:
[ f(x) = frac{1}{sqrt{2pisigma^2}} e^{-frac{(x-mu)^2}{2sigma^2}} ]
以下代码生成正态分布的样本。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
mu = 0 sigma = 2 distro = np.random.normal(mu, sigma, 10000) plt.hist(distro, 100, normed=True) plt.show() ```
图1.11:正态分布
(4)Logistic分布(Logistic Distribution)
它类似于正态分布,但在形状上有显著差异,具有较长的尾部。它的重要性在于其累积分布函数(Cumulative Distribution Function,CDF),将在后续章节中使用,读者会发现它非常熟悉。
以下代码展示了基本的Logistic分布。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
mu = 0.5 sigma = 0.5 distro2 = np.random.logistic(mu, sigma, 10000) plt.hist(distro2, 50, normed=True) distro = np.random.normal(mu, sigma, 10000) plt.hist(distro, 50, normed=True) plt.show() ```
图1.12:Logistic分布(绿色)和正态分布(蓝色)
如前所述,计算Logistic分布的累积分布函数时,读者会看到一个非常熟悉的图形,即Sigmoid曲线。在回顾神经网络激活函数时,还将再次看到它。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
mu = 0.5 sigma = 0.5 logisticcumulative = np.random.logistic(mu, sigma, 10000) / 0.02 plt.hist(logisticcumulative, 50, normed=1, cumulative=True) plt.show() ```
图1.13:逆Logistic分布
本节将介绍概率中的常见统计度量。首先是均值和方差,其定义与前面在统计学中看到的定义相同。
偏度表示一个概率分布的横向偏移,即偏离中心的程度或对称性。通常,如果偏度为负,则表示向右偏移;如果为正,则表示向左偏移。
图1.14:偏度的统计分布
峰度显示了分布的中心聚集程度。它定义了中心区域的尖锐程度,也可以理解为函数尾部的分布方式。
峰度的表达式如下:
[ text{峰度} = frac{sum{i=1}^{n} (xi - bar{x})^4}{(sum{i=1}^{n} (xi - bar{x})^2)^2} ]
图1.15:分布形状对峰度的影响
为了涵盖机器学习的基础知识,特别是像梯度下降(Gradient Descent)这样的学习算法,本书将介绍涉及微分学的概念。
介绍覆盖梯度下降理论所需的微积分术语需要多个章节,因此我们假设读者已经了解连续函数的概念,如线性、二次、对数和指数函数,以及极限的概念。
为了清楚起见,我们将从一元函数的概念开始,然后简要提及多元函数。
在前一节中介绍了函数的概念。除了在整个域中定义的常值函数外,所有函数的值都是动态的。这意味着在x确定的情况下,f(x1)与f(x2)的值是不同的。
微分学的目的是衡量变化。对于这个特定的任务,17世纪的许多数学家(莱布尼茨和牛顿是杰出的倡导者)努力寻找一个简单的模型来衡量和预测符号定义的函数如何随时间变化。
这项研究将引出一个巧妙的概念——一个具有重要意义的结果,在一定条件下,表示在某个点上函数变化的程度及变化方向。这就是导数的概念。
如果想测量函数随时间的变化,首先取一个函数值,在其后的点上测量函数。第一个值减去第二个值,就得到函数随时间变化的程度。代码如下:
```python import matplotlib.pyplot as plt import numpy as np
def quadratic(var): return 2 * var**2
x = np.arange(0, 0.5, 0.1) plt.plot(x, quadratic(x)) plt.plot([1, 4], [quadratic(1), quadratic(4)], linewidth=2.0) plt.plot([1, 4], [quadratic(1), quadratic(1)], linewidth=3.0, label="Change in x") plt.plot([4, 4], [quadratic(1), quadratic(4)], linewidth=3.0, label="Change in y") plt.legend() plt.plot(x, 10*x - 8) plt.show() ```
代码示例首先定义了一个二次方程(2x^2),然后定义了arange函数的域(0到0.5,步长0.1)。定义一个区间,测量y随x的变化,并画出测量的直线,如图1.16所示。
图1.16:求导操作起始设置的初始描述
在x=1和x=4处测量函数,并定义这个区间的变化率。
根据公式,示例程序的运行结果是(36-0)/3=12。
这个方法可以用来近似测量,但它太依赖于测量的点,并且必须在每个时间间隔都进行测量。
为了更好地了解函数的动态变化,需要定义和测量函数域中每个点的瞬时变化率。由于是测量瞬时变化,所以需要减少域x值之间的间隔,使各点之间的间隔尽量延长。我们使用初始值x和后续值x + Δx来表示这个方法。
以下代码逐步减小Δx来逼近差分值。代码如下:
python
initial_delta = 0.1
x1 = 1
for power in range(1, 6):
delta = initial_delta ** power
derivative_approx = (quadratic(x1 + delta) - quadratic(x1)) / ((x1 + delta) - x1)
print("Delta:", delta, ", estimated derivative:", derivative_approx)
在以下代码中,首先定义了初始增量Δ,从而获得初始近似值。然后在差分函数中,对0.1进行乘方运算,幂逐渐增大,Δ的值逐渐减小,得到如下结果:
Delta: 0.1, estimated derivative: 4.2
Delta: 0.01, estimated derivative: 4.02
Delta: 0.001, estimated derivative: 4.002
Delta: 0.0001, estimated derivative: 4.0002
Delta: 1e-05, estimated derivative: 4.00002
随着Δ值的逐渐减小,变化率将波动在4左右。但这个过程何时停止呢?理论上,这个过程可以是有限的,至少在数值意义上是这样。
这就引出了极限的概念。在定义极限的过程中,使Δ无限小,得到的结果称为f(x)的导数或f'(x),公式如下:
[ f'(x) = lim_{{Delta x to 0}} frac{f(x+Delta x) - f(x)}{Delta x} ]
但数学家们并未停止繁琐的计算。他们进行了大量的数值运算(大多是在17世纪手工完成的),并希望进一步简化这些操作。
现在构建一个函数,它可以经过交换x的值来得到相应的导数。对于不同的函数族,从抛物线(y=x^2+b)开始,出现了更复杂的函数(见图1.17),这一巨大的进步发生在17世纪。
图1.17:复杂函数
在函数导数符号确定后,一个非常重要的结果是链式法则。莱布尼茨在1676年的一篇论文中首次提到这个公式。它可以非常简单优雅地求解复合函数的导数,从而简化复杂函数的求解。
为了定义链式法则,假设有一个函数f,F=f(g(x)),那么导数可以定义如下:
[ frac{df}{dx} = frac{df}{dg} cdot frac{dg}{dx} ]
链式法则允许对方程进行导数计算,其中输入值是另一个函数。这与搜索函数之间关联的变化率是一致的。链式法则是神经网络训练阶段的主要理论概念之一。由于在这些分层结构中,第一层神经元的输入将是下一层的输入。在大多数情况下,复合函数包含一层以上的嵌套。
到目前为止,本书一直使用单变量函数。但从现在开始,我们将主要介绍多变量函数。因为数据集将不止包含一个列,并且每一列都将代表不同的变量。
在许多情况下,需要知道函数在一个维度中的变化情况,这将涉及数据集的一列如何对函数的变化产生影响。
偏导数的计算包括将已知的导数规则应用于多变量函数,并将未被求导的变量视为常数。
让我们看一下幂函数求导法则的应用:
[ f(x, y) = 2x^3y ]
当这个函数对x求导时,y作为常量。可以将其重写为3 ∙ 2 y x^2,并将导数应用于变量x,得到以下结果:
[ frac{partial}{partial x}(f(x, y)) = 6y cdot x^2 ]
使用这些方法,可以处理更复杂的多变量函数。这些函数通常是特征集的一部分,通常由两个以上的变量组成。
本书的目标读者是那些希望掌握机器学习相关内容、了解主要的基本概念、运用算法思想并掌握正式数学定义的开发人员。本书使用Python实现了代码概念,Python语言接口的简约性及其提供的方便且丰富的工具,将有助于我们处理这些代码,而有其他编程语言经验的程序员也能理解书中的代码。
读者将学会运用不同类型的算法来解决自己的机器学习相关问题,并了解如何使用这些算法优化模型以获得最佳结果。如果想要了解当前的机器学习知识和一门友好的编程语言,并真正进入机器学习的世界,那么这本书一定能对读者有所帮助。
本书主要关注机器学习的相关概念,并使用Python语言(版本3)作为编码工具。本书通过Python 3和Jupyter Notebook构建工作环境,可以通过编辑和运行它们来更好地理解这些概念。我们专注于如何以最佳方式应用各种Python库来构建实际应用程序。本着这种精神,我们尽力使所有代码保持友好性和可读性,使读者可以轻松地理解代码并在不同的场景中使用它们。