机器学习快速参考(二)
原文:
annas-archive.org/md5/ec06d47e5e72c1b4fee4cf53683fcd2b译者:飞龙
第五章:时间序列分析
在本章中,我们将探讨时间序列分析,并学习几种在不同时间点观察和捕捉事件的方法。我们将介绍白噪声的概念,并学习如何在序列中检测它。
我们将取时间序列数据,计算连续观测之间的差异,这将导致形成一个新的序列。这些概念将帮助我们深入了解时间序列分析,并帮助我们对其有更深入的理解。
在本章中,我们将介绍以下主题:
-
时间序列分析简介
-
白噪声
-
随机游走
-
自回归
-
自相关
-
稳定性
-
差分
-
AR 模型
-
移动平均模型
-
自回归积分移动平均
-
参数优化
-
异常检测
时间序列分析简介
在某些情况下,我们可能会尝试在不同的时间点观察和捕捉事件。通常,我们会在相邻观测之间得出一种关联或关联,这种关联无法通过处理独立且同分布数据的方案来处理。这种在数学和统计方法上考虑所有这些因素的方法被称为 时间序列分析。
时间序列分析已在许多领域得到应用,例如汽车、银行和零售业、产品开发等。其应用没有边界,因此分析师和数据科学家正在深入探索这个领域,以从组织中获取最大利益。
在本节中,我们将探讨时间序列分析的一些概念,这将为进一步的深入理解奠定基础。一旦我们建立了这个基础,我们就会进入建模阶段。
白噪声
一个由一系列相互独立的随机变量组成,均值为零,标准差为 σ² 的简单序列被称为 白噪声。在这种情况下,变量是独立且同分布的。所有值都具有相同的方差 σ²。在这种情况下,序列来自高斯分布,被称为 高斯白噪声。
当序列表现为白噪声时,这意味着序列的性质是完全随机的,序列内部没有关联。因此,无法开发模型,在这种情况下无法进行预测。
然而,当我们通常使用非白噪声序列构建时间序列模型时,我们试图在残差或误差中实现白噪声现象。简单来说,无论我们何时尝试构建模型,目的都是从序列中提取最大量的信息,以便变量中不再存在更多信息。一旦我们构建了模型,噪声将始终是其一部分。方程如下:
Y[t ]= X[t ]+ Error
因此,误差序列在本质上应该是完全随机的,这意味着它是白噪声。如果我们已经得到了这些误差作为白噪声,那么我们就可以继续说我们已经从序列中提取了所有可能的信息。
在序列中检测白噪声
我们可以使用以下工具检测白噪声:
-
线形图:一旦我们有了线形图,我们就可以了解序列是否具有恒定的均值和方差
-
自相关图:有一个相关图可以让我们对滞后变量之间是否存在关联有所了解
-
摘要:检查序列的均值和方差与序列中有意义的连续值块的平均值和方差
让我们在 Python 中这样做:
- 首先,我们将按照以下方式导入所有必需的库:
from random import gauss
from random import seed
from pandas import Series
from pandas.tools.plotting import autocorrelation_plot
from matplotlib import pyplot
- 接下来,我们将设置白噪声序列供我们分析,如下所示:
seed(1000)
#creating white noise series
series = [gauss(0.0, 1.0) for i in range(500)]
series = Series(series)
- 让我们使用以下代码来获取其摘要或统计信息:
print(series.describe())
我们将得到以下输出:
在这里,我们可以看到均值接近零,标准差接近 1。
- 现在让我们制作一个线形图来检查趋势,使用以下代码:
series.plot()
pyplot.show()
我们将得到以下输出:
线性图看起来完全是随机的,这里无法观察到任何趋势。
- 是时候制作自相关图了。让我们使用以下代码设置一个:
autocorrelation_plot(series)
pyplot.show()
我们将得到以下输出:
即使在自相关函数图上,相关性也突破了我们的置信区间。这告诉我们它是一个白噪声序列。
随机游走
随机游走是一种时间序列模型,其中当前观测值等于前一个观测值加上一个随机修改。它可以以下述方式描述:
x[t]= x[t-1 ]+ w[t]
在前面的公式中,*w[t]*是一个白噪声序列。
有时,我们可能会遇到反映不规则增长的序列。在这些情况下,预测下一个级别的策略可能是不正确的。相反,尝试预测从一个时期到下一个时期的变化可能更好——也就是说,查看序列的一阶差分可能更好地找出一个显著的模式。以下图显示了随机游走模式:
在每个时间段,从左到右,变量的值独立地随机向上或向下迈出一步,这被称为随机游走。
它也可以用以下方式描述:
*y(t)= b[o ]+ b[1]x[t-1] + w[t]
以下列表解释了前面的公式:
-
y(t):序列中的下一个值
-
b[o]:系数,如果设置为非零数字,则表示随机游走伴随着漂移
-
b[1]:系数,设置为 1
-
w[t]:白噪声
自回归
自回归是一种时间序列模型,通常使用同一序列的先前值作为回归的解释因素,以预测下一个值。假设我们测量并跟踪了随时间变化的指标,称为 y[t],它在时间 t 时测量,并且当这个值回归到同一时间序列的先前值时。例如,y[t] 在 y[t-1] 上:
如前一个方程所示,前一个值 y[t-1] 已成为这里的预测值,而 y[t] 是要预测的响应值。此外,ε[t] 是均值为零,方差为 1 的正态分布。自回归模型的阶数由模型用来确定下一个值的先前值的数量定义。因此,前一个方程是一个一阶自回归,或 AR(1)。如果我们必须推广它,k 阶自回归,写作 AR(k),是一个多元线性回归,其中时间序列在任何时间 (t) 的值是时间 t-1,t-2,…,t-k 的值的(线性)函数。
下面的列表显示了对于 AR(1) 模型,以下值代表什么:
-
当 β[1]= 0,y**t,它等同于白噪声
-
当 β[1]= 1 和 β[0]= 0 时,y[t] 等同于随机游走
-
当 β[1]= 1 和 β[0]≠ 0 时,y[t],它等同于有漂移的随机游走
-
当 β[1] < 1,y[t],它倾向于在正负值之间振荡
自相关
自相关是衡量时间序列滞后值之间相关性的度量。例如,r[1] 是 y[t] 和 y[t-1] 之间的自相关,同样,r[2] 是 y[t] 和 y[t-2] 之间的自相关。这可以总结如下公式:
在前一个公式中,T 是时间序列的长度。
例如,假设我们有以下相关系数,如图所示:
要绘制它,我们得到以下结果:
从这个自相关函数图中可以观察到以下内容:
-
r[4] 比其他延迟更高,这主要是因为季节性模式
-
蓝线是表示相关性是否显著不同于零的指标
-
延迟 0 的自相关始终为 1
平稳性
对于一些时间序列模型,一个常见的假设是数据必须是平稳的。让我们看看平稳性对时间序列意味着什么。
平稳过程是指其均值、方差和自相关结构随时间不变的过程。这意味着数据没有趋势(增加或减少)。
我们可以使用以下公式来描述这一点:
E(x[t])= μ,对于所有 t
E(x[t]²)= σ²,对于所有 t
cov(x[t],x[k])= cov(x[t+s], x[k+s]),对于所有 t,k 和 s
稳定性检测
有多种方法可以帮助我们确定数据是否平稳,如下所示:
- 绘制数据图:根据时间变量绘制数据图可以帮助我们判断是否存在趋势。根据平稳性的定义,数据中的趋势意味着没有恒定的均值和方差。让我们用 Python 来实现这一点。在这个例子中,我们使用的是国际航空公司乘客数据。
首先,让我们加载所有必需的库,如下所示:
from pandas import Series
from matplotlib import pyplot
%matplotlib inline
data = Series.from_csv('AirPassengers.csv', header=0)
series.plot()
pyplot.show()
我们将得到以下输出:
从图中非常清楚地可以看出,这里存在一个上升趋势,并且这证实了我们的假设,即这是一个非平稳序列。
- 分割数据集并计算摘要:下一个方法是将数据序列分为两部分,并计算均值和方差。通过这样做,我们将能够确定均值和方差是否恒定。让我们使用以下代码来实现这一点:
X = data.values
partition =int(len(X) / 2)
X1, X2 = X[0:partition], X[partition:]
mean1, mean2 =np.nanmean(X1),np.nanmean(X2)
var1, var2 = np.nanvar(X1), np.nanvar(X2)
print('mean1=%f, mean2=%f' % (mean1, mean2))
print('variance1=%f, variance2=%f' % (var1, var2))
输出如下:
mean1=182.902778, mean2=377.694444 variance1=2244.087770, variance2=7367.962191
我们可以看到序列 1 和序列 2 的均值和方差不相等,因此我们可以得出结论,该序列是非平稳的。
- Augmented Dickey-Fuller 测试:Augmented Dickey-Fuller 测试是一种统计测试,它倾向于以一定的置信水平指示序列是否平稳。统计测试通过其假设和过程对数据进行测试,以检验我们对数据的假设。最终,它以一定的置信度给出结果,这有助于我们做出决策。
这个测试实际上就是单位根测试,它试图找出时间序列是否受到趋势的影响。它使用自回归(AR)模型,并在不同的滞后值上优化信息准则。
在这里,零假设如下:
- H[o]:时间序列具有单位根,这意味着序列是非平稳的。
替代假设如下:
- H[1]:时间序列没有单位根,因此它是平稳的。
根据假设检验的规则,如果我们为测试选择了 5%的显著性水平,那么结果将被解释如下:
如果 p-value >0.05 =>,则我们未能拒绝零假设。也就是说,序列是非平稳的。
如果 p-value <0.05 =>,则拒绝零假设,这意味着该序列是平稳的。
让我们在 Python 中执行以下操作:
- 首先,我们将加载库,如下所示:
import pandas as pd
import numpy as np
import matplotlib.pylab as plt
%matplotlib inline
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 25, 6
- 接下来,我们按照以下方式加载数据和时间图:
data = pd.read_csv('AirPassengers.csv')
print(data.head())
print('\n Data Types:')
print(data.dtypes)
输出可以在以下图表中看到:
- 然后,我们按照以下方式解析数据:
dateparse = lambda dates: pd.datetime.strptime(dates, '%Y-%m')
data = pd.read_csv('./data/AirPassengers.csv', parse_dates=['Month'], index_col='Month',date_parser=dateparse)
print(data.head())
然后,我们得到以下输出:
ts= data["#Passengers"]
ts.head()
从这里,我们得到以下输出:
- 然后,我们绘制以下图表:
plt.plot(ts)
输出如下所示:
- 让我们创建一个函数来执行以下代码的平稳性测试:
from statsmodels.tsa.stattools import adfuller
def stationarity_test(timeseries):
dftest = adfuller(timeseries, autolag='AIC')
dfoutput = pd.Series(dftest[0:4], index=['Test Statistic','p-value','#Lags Used','Number of Observations Used'])
for key,value in dftest[4].items():
dfoutput['Critical Value (%s)'%key] = value
print(dfoutput)
stationarity_test(ts)
输出如下所示:
在前面的方程中,* ω* 是白噪声项,α 是系数,不能为零。聚合方程如下所示:
有时,我们可能会讨论模型的阶数。例如,我们可能会将 AR 模型描述为阶数 p。在这种情况下,p 代表模型中使用的滞后变量的数量。例如,AR(2) 模型或二阶 AR 模型看起来如下:
在这里,ω 是具有 E(ω[t])=0 和方差 =* σ²* 的白噪声。
为了找出 AR 模型的阶数,我们需要绘制一个偏自相关函数图,然后寻找第一次被上置信水平跨越的滞后。
自回归积分移动平均
自回归积分移动平均(ARIMA)模型是以下元素的组合:
-
自回归算子:我们已经学习了这是什么意思;只是为了重申,它是平稳化序列的滞后。它用 p 表示,这仅仅是自回归项的数量。PACF 图提供了这个组成部分。
-
积分算子:一个需要通过差分使其平稳的序列被称为平稳序列的积分形式。它用d表示,这是将非平稳时间序列转换为平稳序列所需的差分量。这是通过从当前周期的观测值中减去前期的观测值来实现的。如果这个过程只对序列进行了一次,那么它被称为一阶差分。这个过程消除了序列中按恒定速率增长的趋势。在这种情况下,序列是按递增速率增长的,差分序列需要另一轮差分,这被称为二阶差分。
-
移动平均算子:预测误差的滞后,用q表示。它是方程中滞后预测误差的数量。ACF 图将产生这个成分。
ARIMA 模型只能应用于平稳序列。因此,在应用它之前,必须检查序列的平稳性条件。可以使用 ADF 测试来建立这一点。
ARIMA 方程看起来如下:
方程的前一部分(在*-符号之前)是自回归部分,第二部分(在-*符号之后)是移动平均(MA)部分。
我们还可以在 ARIMA 模型中添加季节性成分,这将形成 ARIMA (p,d,q)(p,d,q)[s]。在添加它时,我们需要执行季节性差分,这意味着从季节滞后中减去当前观测值。
让我们绘制自相关函数(ACF)和偏自相关函数(PACF)以找出p和q参数。
在这里,我们取滞后数为 20,并使用statsmodel.tsa.stattools库导入acf和pacf函数,如下所示:
from statsmodels.tsa.stattools import acf,pacf
lag_acf= acf(ts_log_dif,nlags=20)
lag_pacf = pacf(ts_log_dif, nlags=20,method="ols")
现在,我们将使用以下代码通过matplotlib绘制图表:
plt.subplot(121)
plt.plot(lag_acf)
plt.axhline(y=0,linestyle='--',color='gray')
plt.axhline(y=-1.96/np.sqrt(len(ts_log_diff)),linestyle='--',color='gray')
plt.axhline(y=1.96/np.sqrt(len(ts_log_diff)),linestyle='--',color='gray')
plt.title('Autocorrelation Function')
输出如下:
在这里,我们正在测量时间序列与其滞后版本之间的相关性。例如,在滞后 5 的情况下,ACF 将比较时间点t1,t2处的序列与时间点t[1-5],…,*t[2-5]*处的序列。这是一张相关系数与其滞后值的相关图。
如果我们仔细观察前面的图表,我们会看到在滞后 2 时上置信水平线已被穿过。因此,移动平均(MA)的阶数将是2,而q=2。
画出了序列与滞后值之间的部分相关性,并给出了部分自相关函数(PACF)图。这是一个非常有趣的术语。如果我们继续计算Y变量与X3 之间的相关性,同时我们知道Y与X1 和X2 有分离的关联,那么部分相关性就解决了这部分相关性,这部分相关性不能由它们与X1 和X2 的相关性来解释。
在这里,偏相关是平方根(通过添加一个变量(此处为X3)并在其他变量(此处为X1,X2)上对Y进行回归时减少方差)。
在时间序列的情况下,Y 与滞后值 Y[t-3]之间的偏自相关将是未被Y与Y[t-1]和Y[t-2]之间的相关性解释的值,如下代码所示:
#Plot PACF:
plt.subplot(122)
plt.plot(lag_pacf)
plt.axhline(y=0,linestyle='--',color='gray')
plt.axhline(y=-1.96/np.sqrt(len(ts_log_diff)),linestyle='--',color='gray')
plt.axhline(y=1.96/np.sqrt(len(ts_log_diff)),linestyle='--',color='gray')
plt.title('Partial Autocorrelation Function')
plt.tight_layout()
我们将得到以下输出:
如果我们仔细观察前面的图,我们会看到在滞后 2 时上置信水平线已被穿过。因此,AR 的阶数应该是 2,p=2。
让我们尝试一个阶数为*(p=2, d=1, q=0)*的 AR 模型。d值取为 1,因为这是一个一阶差分的情况。同时计算了残差平方和,以判断模型的好坏,并与其他模型进行比较,如下代码所示:
from statsmodels.tsa.arima_model import ARIMA
model1 = ARIMA(ts_log, order=(2, 1, 0))
results_AR = model1.fit(disp=-1)
plt.plot(ts_log_dif)
plt.plot(results_AR.fittedvalues, color='red')
plt.title('RSS: %.4f'% sum((results_AR.fittedvalues-ts_log_dif)**2))
输出如下所示:
现在,我们可以使用以下代码查看描述 AR1 和 AR2 系数的模型摘要:
results_AR.summary()
现在,让我们使用以下代码构建一个阶数为*(p=0,d=1,q=2)*的 MA 模型:
model2 = ARIMA(ts_log, order=(0, 1, 2))
results_MA = model2.fit(disp=-1)
plt.plot(ts_log_dif)
plt.plot(results_MA.fittedvalues, color='red')
plt.title('RSS: %.4f'% sum((results_MA.fittedvalues-ts_log_dif)**2))
输出如下所示:
现在,让我们结合这两个模型,并使用以下代码构建一个 ARIMA 模型:
model3 = ARIMA(ts_log, order=(2, 1, 2))
results_ARIMA = model.fit(disp=-1)
plt.plot(ts_log_dif)
plt.plot(results_ARIMA.fittedvalues, color='red')
plt.title('RSS: %.4f'% sum((results_ARIMA.fittedvalues-ts_log_dif)**2))
输出如下所示:
我们可以从 AR 模型到 ARIMA 模型观察到 RSS 值的下降。现在RSS= 1.0292:
results_ARIMA.summary()
我们可以看到 AR1、AR2、MA1 和 MA2 的系数,并且根据p值,我们可以看到所有这些参数都是显著的,如下截图所示:
让我们使用以下代码将预测值转换成一个序列:
predictions_ARIMA_dif= pd.Series(results_ARIMA.fittedvalues, copy=True)
print(predictions_ARIMA_dif.head())
我们将得到以下输出:
将差分转换为对数尺度的方法是连续将这些差异添加到基数中。一种简单的方法是首先确定索引处的累积和,然后将其添加到基数中。累积和可以使用以下代码找到:
predictions_ARIMA_dif_cumsum = predictions_ARIMA_dif.cumsum()
print(predictions_ARIMA_dif_cumsum.head())
从这个结果,我们将得到以下输出:
我们将创建一个所有值都为基数,并添加差异以添加到基数序列中的序列,如下所示:
predictions_ARIMA_log = pd.Series(ts_log.ix[0], index=ts_log.index)
predictions_ARIMA_log = predictions_ARIMA_log.add(predictions_ARIMA_dif_cumsum,fill_value=0)
predictions_ARIMA_log.head()
下面的代码显示了输出:
现在我们使用以下代码来找出预测结果:
predictions_ARIMA = np.exp(predictions_ARIMA_log)
plt.plot(ts)
plt.plot(predictions_ARIMA)
plt.title('RMSE: %.4f'% np.sqrt(sum((predictions_ARIMA-ts)**2)/len(ts)))
输出如下所示:
参数优化
让我们看看如何优化模型的参数。
AR 模型
import statsmodels.tsa.api as smtsa
aic=[]
for ari in range(1, 3):
obj_arima = smtsa.ARIMA(ts_log_diff, order=(ari,2,0)).fit(maxlag=30, method='mle', trend='nc')
aic.append([ari,2,0, obj_arima.aic])
print(aic)
[[1, 2, 0, -76.46506473849644], [2, 2, 0, -116.1112196485397]]
因此,在这种情况下,AR 模型的最优模型参数是p=2,d=2,q=0,因为这种组合的 AIC 值最小。
ARIMA 模型
即使对于 ARIMA 模型,我们也可以使用以下代码来优化参数:
import statsmodels.tsa.api as smtsa
aic=[]
for ari in range(1, 3):
for maj in range(1,3):
arima_obj = smtsa.ARIMA(ts_log, order=(ari,1,maj)).fit(maxlag=30, method='mle', trend='nc')
aic.append([ari,1, maj, arima_obj.aic])
print(aic)
执行上述代码后,您将得到以下输出:
[[1, 1, 1, -242.6262079840165], [1, 1, 2, -248.8648292320533], [2, 1, 1, -251.46351037666676], [2, 1, 2, -279.96951163008583]]
应选择具有最小赤池信息准则(AIC)的组合。
异常检测
异常本质上是一系列中的异常模式,是不规则偏离预期行为的偏差。例如,我们中的许多人看过板球比赛。在这场比赛中出局的一种方式是被接住,在球直接飞到守门员手中之前,它必须触碰到击球手的球棒。如果体育场非常嘈杂,有时很难判断球是否触碰到球棒。为了解决这个问题,裁判员使用一种称为snickometer的设备来帮助他们做出判断。snickometer 使用球桩麦克风的声波生成麦克风的声波图。如果图是直线,则球没有接触到球棒;否则,图将显示一个尖峰。因此,尖峰是异常的标志。另一个异常的例子可能是扫描中检测到恶性肿瘤。
异常检测是一种我们可以用来确定异常行为的技巧。异常也可以称为离群值。以下列表展示了几个不同的异常类型:
-
点异常:点异常是突破已分配给整个系统以保持监控的阈值边界的点。通常有一个系统在位,当点异常突破这个边界时,会发送警报。例如,金融行业的欺诈检测可以使用点异常检测来检查是否发生了从不同城市到持卡人常用位置的交易。
-
上下文异常:特定上下文的观察被称为上下文异常。例如,在工作日有大量交通是常见的,但如果假日落在周一,可能会看起来像是一个异常。
-
集体异常:一组集体数据实例有助于检测异常。比如说,有人意外地试图从远程机器复制数据到本地主机。在这种情况下,这种异常将被标记为潜在的网络安全攻击。
在本节中,我们将重点关注上下文异常,并尝试使用简单的移动平均来检测它们。
首先,让我们按照以下方式加载所有必需的库:
import numpy as np # vectors and matrices
import pandas as pd # tables and data manipulations
import matplotlib.pyplot as plt # plots
import seaborn as sns # more plots
from sklearn.metrics import mean_absolute_error
import warnings # `do not disturb` mode
warnings.filterwarnings('ignore')
%matplotlib inline
接下来,我们使用以下代码读取数据集。我们保持相同的数据集——即AirPassenger.csv:
data = pd.read_csv('AirPassengers.csv', index_col=['Month'], parse_dates=['Month'])
plt.figure(figsize=(20, 10))
plt.plot(ads)
plt.title('Trend')
plt.grid(True)
plt.show()
我们得到以下输出:
现在我们将编写一个函数,并使用以下代码创建一个用于检测异常的阈值:
def plotMovingAverage(series, window, plot_intervals=False, scale=1.96, plot_anomalies=False):
rolling_mean = series.rolling(window=window).mean()
plt.figure(figsize=(15,5))
plt.title("Moving average\n window size = {}".format(window))
plt.plot(rolling_mean, "g", label="Rolling mean trend")
# Plot confidence intervals for smoothed values
if plot_intervals:
mae = mean_absolute_error(series[window:], rolling_mean[window:])
deviation = np.std(series[window:] - rolling_mean[window:])
lower_bond = rolling_mean - (mae + scale * deviation)
upper_bond = rolling_mean + (mae + scale * deviation)
plt.plot(upper_bond, "r--", label="Upper Bond / Lower Bond")
plt.plot(lower_bond, "r--")
# Having the intervals, find abnormal values
if plot_anomalies:
anomalies = pd.DataFrame(index=series.index, columns=series.columns)
anomalies[series<lower_bond] = series[series<lower_bond]
anomalies[series>upper_bond] = series[series>upper_bond]
plt.plot(anomalies, "ro", markersize=10)
plt.plot(series[window:], label="Actual values")
plt.legend(loc="upper left")
plt.grid(True)
现在,让我们使用以下方法向序列中引入异常:
data_anomaly = data.copy()
data_anomaly.iloc[-20] = data_anomaly.iloc[-20] * 0.2
现在,让我们使用以下代码来绘制它以检测引入的异常:
plotMovingAverage(data_anomaly, 4, plot_intervals=True, plot_anomalies=True)
下图显示了输出结果:
现在,引入的异常可以在 1959 年之后被看作是旅行者数量的下降。然而,需要注意的是,这是一种较为简单的方法。在此场景下,也可以使用 ARIMA 和 Holt-Winters 方法。
摘要
在本章中,我们学习了时间序列分析和白噪声。我们介绍了随机游走、自回归、自相关和平稳性的概念,这些概念描述了如何判断数据是否平稳。
我们还学习了差分法,即对时间序列数据进行处理,计算连续观测值之间的差异,从而形成一个新的序列。本章还讨论了 AR 模型,它是随机过程的一部分,其中特定滞后值*y**[t]被用作预测变量,并回归到y**[t ]*上来估计值。我们还学习了两个优化参数,即 AR 模型和 ARIMA 模型。
在下一章中,我们将学习关于自然语言处理的内容。
第六章:自然语言处理
世界变化有多快?好吧,技术和数据变化同样快。随着互联网和社交媒体的出现,我们对数据的整个看法已经改变。最初,大多数数据分析的范围围绕结构化数据。然而,由于互联网和社交媒体中涌入的大量非结构化数据,分析的范围已经扩大。每秒钟都在生成大量的文本数据、图像、声音和视频数据。它们包含大量需要商业综合的信息。自然语言处理是一种技术,通过它我们可以使机器理解文本或语音。尽管非结构化数据范围广泛,但本章的目的是让你接触文本分析。
结构化数据通常由关系数据库或电子表格中设置的固定观察值和固定列组成,而非结构化数据没有任何结构,不能在关系数据库中设置;相反,它需要一个 NoSQL 数据库,例如视频、文本等。
在本章中,你将了解以下主题:
-
文档-词矩阵
-
观察文本的不同方法
-
情感分析
-
主题建模
-
贝叶斯技术
文本语料库
文本语料库是由单个文档或一组文档形成的文本数据,可以来自任何语言,如英语、德语、印地语等。在当今世界,大部分文本数据来自社交媒体,如 Facebook、Twitter、博客网站和其他平台。移动应用程序现在也被列入此类来源。语料库的规模越大,即称为语料库,分析就越准确。
句子
语料库可以被分解成称为句子的单位。句子承载着语料库的意义和上下文,一旦我们将它们组合在一起。句子的形成是在词性的帮助下进行的。每个句子都由分隔符(如句号)与其他句子分开,我们可以利用它来进一步分解。这被称为句子标记化。
单词
单词是语料库中最小的单位,当我们按照词性顺序排列时,它们就形成了句子。当我们把句子分解成单词时,这被称为词标记化。
词袋
当我们有文本作为输入数据时,我们不能直接处理原始文本。因此,将文本输入数据转换为数字或数字向量,以便使其可用于多种算法,这是至关重要的。
词袋模型是将文本用于算法的一种方法。本质上,它是对文档中单词出现情况的表示。它与结构、顺序和位置无关;此模型只寻找单词作为特征的数量。
该模型背后的思维过程是:内容相似意味着文档相似。
在词袋模型中需要采取的不同步骤如下:
- 构建语料库:在这个步骤中,收集并合并文档以形成一个语料库。例如,这里使用了电视剧《老友记》中的著名歌曲作为语料库:
*我会为你守候
当雨开始倾盆而下
我会为你守候
就像我以前去过那里一样
我会为你守候*
让我们考虑这首歌的每一行作为一个单独的文档。
-
词汇构建:在这个步骤中,我们确定语料库中的独特单词,并创建它们的列表:
-
我
-
将
-
be
-
there
-
for
-
you
-
when
-
the
-
rain
-
starts
-
to
-
pour
-
like
-
have
-
been
-
before
-
-
文档向量创建:现在,是时候将每个文本文档转换为向量了。
做这件事的简单方法是通过布尔路由。这意味着原始文本将通过该文本在相应文档中的存在/不存在转换为文档向量。
例如,如果歌曲的第一行被转换成一个包含I will be there for you的文档,那么文档向量将如下所示:
| 文档向量 | |
|---|---|
| I | 1 |
| will | 1 |
| be | 1 |
| there | 1 |
| for | 1 |
| you | 1 |
| when | 0 |
| the | 0 |
| rain | 0 |
| starts | 0 |
| to | 0 |
| pour | 0 |
| like | 0 |
| have | 0 |
| been | 0 |
| before | 0 |
文档中出现的所有单词都被标记为 1,其余的都被标记为 0。
因此,第一句话的文档向量是*[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0]*。
类似地,第二句话的文档向量是*[0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0]*。
随着语料库的大小持续增加,文档向量中的零的数量也会增加。因此,这导致向量稀疏,成为一个稀疏向量。对于各种算法来说,计算稀疏向量变得非常具有挑战性。数据清洗是应对这一问题的方法之一,在一定程度上:
-
文本清洗:这包括将所有语料库转换为单个大小写(最好是小写)。必须从语料库中去除标点符号。可以采用词干提取,即找到文本的词根,这将能够减少语料库中的独特单词。此外,去除诸如“is”和“of”之类的停用词,可能有助于减轻稀疏性的痛苦。
-
计数向量:创建文档向量的另一种方法是利用文档中单词出现的频率。假设有一个由 N 个文档和 T 个标记(单词)组成的语料库。这些 T 个标记将形成我们的词典。因此,计数向量矩阵的维度将是 N X T。每一行都包含该文档中词典中单词的频率。
例如,假设我们有三个文档:
-
N1: 计数向量中包含计数
-
N2: 计数向量是否比布尔方式创建特征向量更好?
-
N3: 特征向量的创建非常重要
在移除停用词后,计数向量矩阵如下表所示:
| count | vector | got | it | better | than | Boolean | way | creating | feature | creation | important | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| N2 | 1 | 2 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
| N3 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
现在,仔细看看矩阵维度;由于N=3和T=12,这使得这是一个 3 x 12 的矩阵。
我们将看看矩阵是如何形成的。对于文档N1,计数出现的次数是 2,向量出现的次数是 1,等等。将这些频率输入这些值。其他两个文档也完成了类似的过程。
然而,这有一个缺点。一个高频词可能会开始主导文档,甚至整个语料库,这会导致从特征中提取的信息有限。为了解决这个问题,引入了词频逆文档频率(TF-IDF)。
TF-IDF
我们了解到计数向量化的局限性,即一个高频词可能会破坏整体效果。因此,我们的想法是对在大多数文档中频繁出现的词进行惩罚,给它们分配较低的权重,并增加在文档子集中出现的词的权重。这就是 TF-IDF 工作的原理。
TF-IDF 是衡量一个术语相对于文档和整个语料库(文档集合)重要性的度量:
TF-IDF(term) = TF(term) IDF(term)*
词频(TF)是单词在文档中出现的频率,相对于文档中所有单词的总数。例如,如果一个文档中有 1,000 个单词,我们需要找出该文档中出现了 50 次的单词NLP的TF,我们使用以下公式:
TF(NLP)= 50/1000=0.05
因此,我们可以得出以下结论:
TF(term) = 术语在文档中出现的次数/文档中术语的总数
在前面的例子中,包含三个文档N1、N2和N3,如果需要找到文档N1中术语count的TF,它将如下所示:
TF(count) N1= 2/ (2+1+1+1) = 2/5 = 0.4
它表示单词对文档的贡献。
然而,IDF 是衡量该词在整个语料库中重要性的指标:
IDF("count") = log(总文档数/包含术语"count"的文档数)
IDF("count") = log(3/2)= 0.17
现在,让我们计算术语vector的 IDF:
IDF("vector")=log(3/3)= 0
我们如何解释这个结果呢?它意味着如果一个词在所有文档中都出现过,那么它对这个特定文档来说并不相关。但是,如果一个词只出现在文档的子集中,这意味着它在存在的文档中具有一定的相关性。
让我们计算计数和向量的 TF-IDF,如下所示:
文档 N1 的 TF-IDF 计数 = TF 计数 * IDF 计数 = 0.4 * 0.17 = 0.068
文档 N1 的 TF-IDF 向量 = TF 向量 * IDF 向量 = (1/5) * 0 = 0
很明显,由于它给 N1 中的计数分配了更多的权重,所以它比向量更重要。权重值越高,术语越稀有。权重值越小,术语越常见。搜索引擎利用 TF-IDF 检索与查询相关的相关文档。
现在,我们将探讨如何在 Python 中执行计数向量和 TF-IDF 向量器:
执行计数向量器
执行CountVectorizer的步骤如下:
- 导入所需的
CountVectorizer库:
from sklearn.feature_extraction.text import CountVectorizer
- 创建一个文本列表:
text = [" Machine translation automatically translate text from one human language to another text"]
- 将文本列表分词并构建词汇表:
vectorizer.fit(text)
你将得到以下输出:
- 让我们看看创建的词汇表:
print(vectorizer.vocabulary_)
我们得到以下输出:
- 现在,我们必须按照以下方式对其进行编码:
vector = vectorizer.transform(text)
- 让我们总结一下向量的内容,并找出项矩阵:
print(type(vector))
print(vector.toarray())
我们得到以下输出:
在 Python 中执行 TF-IDF
在 Python 中执行 TF-IDF 的步骤如下:
- 按照以下方式导入库:
from sklearn.feature_extraction.text import TfidfVectorizer
- 让我们通过添加四个文档来创建语料库,如下所示:
corpus = ['First document', 'Second document','Third document','First and second document' ]
- 让我们设置向量器:
vectorizer = TfidfVectorizer()
- 我们按照以下方式从文本中提取特征:
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names())
print(X.shape)
输出如下:
- 现在出现了文档-词矩阵;每个列表表示一个文档:
X.toarray()
我们得到以下输出:
情感分析
情感分析是自然语言处理的应用领域之一。它在各个行业和领域中广泛应用,并且在行业中有着巨大的需求。每个组织都致力于关注客户及其需求。因此,为了理解声音和情感,客户成为首要目标,因为了解客户的脉搏可以带来收入。如今,客户通过 Twitter、Facebook 或博客表达他们的情感。需要做一些工作来提炼这些文本数据,使其可消费。让我们看看如何在 Python 中实现这一点。
在这里,电影观众的评论来自 IMDB。这些评论也分享在 GitHub 上。
我们将启动库,如下所示:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(color_codes=True)
import os
print(os.listdir())
我们将按照以下方式加载数据集:
data= pd.read_csv("imdb_master.csv",encoding = "ISO-8859-1")
现在,让我们探索数据和其维度:
print(data.head())
print(data.shape)
我们得到以下输出:
我们只需要两个变量,review和label,来构建模型。我们将保留数据中的这两个变量。已经创建了一个新的数据框,如下所示:
Newdata= data[["review","label"]]
Newdata.shape
现在,这是我们需要检查label中有多少类别的步骤,因为我们只对保留正面和负面类别感兴趣:
g= Newdata.groupby("label")
g.count()
输出如下:
现在,很明显有三个类别,我们将删除unsup,如下所示:
sent=["neg","pos"]
Newdata = Newdata[Newdata.label.isin(sent)]
Newdata.head()
我们得到了以下输出:
我们的数据现在已经设置好了。然而,由于我们删除了一些行,我们将重置数据的索引,因为有时这会引起一些问题:
print(len(Newdata))
Newdata=Newdata.reset_index(drop=True) Newdata.head()
输出如下:
我们已经完成了这个步骤。现在,我们将对label变量进行编码,以便使其可用于机器学习模型。我们必须使用LabelEncode来做到这一点,如下所示:
from sklearn.preprocessing import LabelEncoder
labelencoder = LabelEncoder()
Newdata["label"] = labelencoder.fit_transform(Newdata["label"])
我们必须对数据的一部分进行清洗,以便使其干净和标准化,如下所示:
Newdata["Clean_review"]= Newdata['review'].str.replace("[^a-zA-Z#]", " ")
Newdata.head()
输出如下:
这里,我们试图去除长度小于3的单词,因为大多数长度小于3的单词对意义的影响不大:
Newdata['Clean_review'] = Newdata['Clean_review'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>3]))
Newdata.shape
数据的标记化现在可以进行,如下所示:
tokenized_data = Newdata['Clean_review'].apply(lambda x: x.split())
tokenized_data.shape
我们正在使用词干提取,以便去除相同单词的不同变体。例如,我们将查看 satisfying、satisfy 和 satisfied,如下所示:
from nltk.stem.porter import *
stemmer = PorterStemmer()
tokenized_data = tokenized_data.apply(lambda x: [stemmer.stem(i) for i in x])
tokenized_data.head()
输出如下:
在词干提取后,我们必须将数据重新连接起来,因为我们正在朝着生成文字云的方向前进:
for i in range(len(tokenized_data)):
tokenized_data[i] = ' '.join(tokenized_data[i])
tokenized_data.head()
我们得到了以下输出:
这里,我们将标记化的数据与旧的Newdata数据框合并:
Newdata["Clean_review2"]= tokenized_data
Newdata.head()
以下是对前面代码的输出:
已经生成了一个将所有单词组合在一起的文字云:
all_words = ' '.join([str(text) for text in Newdata['Clean_review2']])
from wordcloud import WordCloud
wordcloud = WordCloud(width=800, height=500, random_state=21, max_font_size=110).generate(all_words)
plt.figure(figsize=(10, 7))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis('off')
plt.show()
输出如下:
现在,我们将分别对正面和负面情感制作文字云,如下所示:
- 对于
负面情感,我们将使用以下方法:
Negative =' '.join([text for text in Newdata['Clean_review2'][Newdata['label'] == 0]])
wordcloud1= WordCloud(width=800, height=500, random_state=21, max_font_size=110).generate(Negative)
plt.figure(figsize=(10, 7))
plt.imshow(wordcloud1, interpolation="bilinear")
plt.title("Word Cloud- Negative")
plt.axis('off')
plt.show()
以下输出显示了负面情感的文字云:
- 我们将使用以下内容用于
正面情感:
Positive=' '.join([text for text in Newdata['Clean_review2'][Newdata['label'] == 1]])
wordcloud2 = WordCloud(width=800, height=500, random_state=21, max_font_size=110).generate(Positive)
plt.figure(figsize=(10, 7))
plt.imshow(wordcloud, interpolation="bilinear")
plt.title("Word Cloud-Positive")
plt.axis('off')
plt.show()
以下输出显示了正面情感的文字云:
情感分类
我们将采用两种方法来进行情感分类(正面和负面),如下所示:
-
TF-IDF
-
计数向量化
让我们看看哪一个能给出更好的结果。
TF-IDF 特征提取
以下代码将提供 TF-IDF 特征提取:
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf= TfidfVectorizer(max_df=0.9,min_df= 2, max_features=1000,
stop_words="english")
tfidfV = tfidf.fit_transform(Newdata['Clean_review2'])
tfidf.vocabulary_
我们将得到以下输出:
计数向量器单词袋特征提取
以下代码将展示单词袋的计数向量器:
from sklearn.feature_extraction.text import CountVectorizer
bow_vectorizer = CountVectorizer(max_df=0.90, min_df=2, max_features=1000, stop_words='english')
# bag-of-words
bow = bow_vectorizer.fit_transform(Newdata['Clean_review2'])
模型构建计数向量
为了构建计数向量,我们可以将数据分为训练集和测试集,如下所示:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score,accuracy_score
# splitting data into training and validation set
xtrain, xtest, ytrain, ytest = train_test_split(bow, Newdata['label'], random_state=42, test_size=0.3)
lreg = LogisticRegression()
lreg.fit(xtrain, ytrain) # training the model
prediction = lreg.predict_proba(xtest) # predicting on the validation set
prediction_int = prediction[:,1] >= 0.3 # if prediction is greater than or equal to 0.3 than 1 else 0
prediction_int = prediction_int.astype(np.int)
print("F1 Score-",f1_score(ytest, prediction_int))
print("Accuracy-",accuracy_score(ytest,prediction_int))
我们将得到以下输出:
在这里,我们达到了 84%的准确率。让我们看看 TF-IDF 方法的表现如何:
from sklearn.linear_model import LogisticRegression
# splitting data into training and validation set
xtraintf, xtesttf, ytraintf, ytesttf = train_test_split(tfidfV, Newdata['label'], random_state=42, test_size=0.3)
lreg = LogisticRegression()
lreg.fit(xtraintf, ytraintf) # training the model
prediction = lreg.predict_proba(xtesttf) # predicting on the test set
prediction_int = prediction[:,1] >= 0.3 # if prediction is greater than or equal to 0.3 than 1 else 0
prediction_int = prediction_int.astype(np.int)
print("F1 Score-",f1_score(ytest, prediction_int))
print("Accuracy-",accuracy_score(ytest,prediction_int))
输出如下:
在这里,准确率达到了 83.8%(略低于计数向量器)。
这完成了情感分类模型的构建。
主题建模
模型是一种用于识别主题并推导出文本语料库所展现的隐藏模式的方法。主题建模类似于聚类,因为我们提供主题的数量作为超参数(类似于聚类中使用的超参数),这恰好是簇的数量(k-means)。通过这种方式,我们试图提取具有某些权重分配的主题数量或文本。
模型的应用领域包括文档聚类、降维、信息检索和特征选择。
实现这一目标有多种方法,如下所示:
-
潜在狄利克雷分配(LDA):它基于概率图模型
-
潜在语义分析(LSA):它基于线性代数(奇异值分解)
-
非负矩阵分解:它基于线性代数
我们将主要讨论 LDA,它被认为是所有中最受欢迎的。
LDA 是一种矩阵分解技术,它基于这样一个假设:文档是由多个主题组成的,而主题是由单词组成的。
在阅读了前面的章节之后,你应该知道任何语料库都可以表示为一个文档-词矩阵。以下矩阵显示了一个包含M个文档和N个单词的词汇量,形成一个M x N矩阵。这个矩阵中的所有单元格都有该特定文档中单词的频率:
这个文档与词的 M x N 矩阵通过 LDA 转换为两个矩阵:文档与主题的 M x X 矩阵和主题与词的 X x N 矩阵。
LDA 架构
在 LDA 架构中,有 M 个文档,包含 N 个单词,这些单词通过被称为LDA的黑色条带进行处理。它提供了X 个主题和单词簇。每个主题都有来自主题的单词的 psi 分布。最后,它还提供了一个文档中主题的分布,用 phi 表示。
以下图表说明了 LDA:
关于Alpha和Beta超参数:alpha 代表文档-主题浓度,beta 代表主题-单词浓度。alpha 的值越高,我们从文档中得到的主题就越多。另一方面,beta 的值越高,一个主题中的单词就越多。这些可以根据领域知识进行调整。
LDA 遍历每篇文档中的每个单词,并为它分配和调整一个主题。基于两个概率的乘积,将一个新的主题X分配给它:p1= (topic t/document d),这意味着分配给主题 t 的文档中单词的比例,以及p2=(word w/topic t),这指的是分配给主题t的分配在整个文档中,其中与单词 w 相关联。
通过遍历次数,我们得到了一个良好的主题-单词和主题-文档的分布。
让我们看看它在 Python 中的执行方式:
- 在这一步,我们正在加载
dataset = fetch_20newsgroups,它来自sklearn:
from sklearn.datasets import fetch_20newsgroups
dataset = fetch_20newsgroups(shuffle=True, random_state=1, remove=('headers', 'footers', 'quotes'))
documents = dataset.data
- 在这一步,我们将清理数据集。为了做到这一点,需要
stopwords和WordNetLemmatizer函数。因此,必须加载相关的库,如下所示:
from nltk.corpus import stopwords
from nltk.stem.wordnet import WordNetLemmatizer
import string
- 确保您已下载以下字典:
import nltk
nltk.download("stopwords")
nltk.download("wordnet")
- 这里,创建了一个
clean函数,将单词转换为小写。移除stopwords并选择长度大于3的单词。它还使其无标点符号。最后,进行词形还原,如下所示:
stop = set(stopwords.words('english'))
punc = set(string.punctuation)
lemma = WordNetLemmatizer()
def clean(doc):
stopw_free = " ".join([i for i in doc.lower().split() if i not in stop and len(i)>3])
punc_free = ''.join(ch for ch in stop_free if ch not in punc)
lemmatized = " ".join(lemma.lemmatize(word) for word in punc_free.split())
return lemmatized
doc_clean = [clean(doc).split() for doc in documents]
- 现在,我们必须在
gensim库的帮助下创建文档-术语矩阵。这个库还将使我们能够执行 LDA:
import gensim
from gensim import corpora
- 在这里创建了一个基于词袋的文档-术语矩阵:
corpus = corpora.Dictionary(doc_clean)
doc_term_matrix = [corpus.doc2bow(doc) for doc in doc_clean]
- 这里,正在使用 TF-IDF 帮助创建一个类似的矩阵:
from gensim import models
tfidf = models.TfidfModel(doc_term_matrix)
corpus_tfidf = tfidf[doc_term_matrix]
- 让我们使用 TF-IDF 矩阵设置模型。主题的数量已经给出为
10:
lda_model1 = gensim.models.LdaMulticore(corpus_tfidf, num_topics=10, id2word=corpus, passes=2, workers=2)
- 让我们看看包含单词的主题:
print(lda_model1.print_topics(num_topics=5, num_words=5))
输出如下:
- 对于词袋模型,我们也将进行类似的练习;稍后,我们将进行比较:
lda_model2 = gensim.models.LdaMulticore(doc_term_matrix, num_topics=10, id2word=corpus, passes=2, workers=2)
print(lda_model2.print_topics(num_topics=5, num_words=5))
我们得到了以下输出:
评估模型
对数困惑度是衡量 LDA 模型好坏的一个指标。困惑度值越低,模型越好:
print("lda_model 1- Perplexity:-",lda_model.log_perplexity(corpus_tfidf))
print("lda_model 2- Perplexity:-",lda_model2.log_perplexity(doc_term_matrix))
对数困惑度的输出如下:
可视化 LDA
为了可视化数据,我们可以使用以下代码:
import pyLDAvis
import pyLDAvis.gensim
import matplotlib.pyplot as plt
%matplotlib inline
pyLDAvis.enable_notebook()
visual1= pyLDAvis.gensim.prepare(lda_model, doc_term_matrix, corpus)
visual1
输出将如下:
我们可以在此启用笔记本,如下所示:
pyLDAvis.enable_notebook()
visual2= pyLDAvis.gensim.prepare(lda_model2, doc_term_matrix, corpus)
visual2
输出如下:
让我们尝试解释这个。
在左侧,我们有主题,在右侧,我们有术语/单词:
-
圆圈的大小越大,主题出现的频率就越高。
-
相互重叠或彼此更接近的主题是相似的。
-
选择一个主题后,可以看到该主题的最具代表性的单词。这反映了单词的频率。可以通过使用滑块来调整每个属性的权重。
-
将鼠标悬停在主题上,将在右侧提供单词对该主题的贡献。点击单词后,我们将看到圆圈大小变化,这反映了该术语在该主题中的频率。
文本分类中的朴素贝叶斯技术
朴素贝叶斯是一种基于贝叶斯定理的监督分类算法。它是一个概率算法。但是,你可能想知道为什么它被称为朴素。这是因为该算法基于一个假设,即所有特征都是相互独立的。然而,我们意识到在现实世界中特征之间的独立性可能不存在。例如,如果我们试图检测一封电子邮件是否为垃圾邮件,我们只寻找与垃圾邮件相关的关键词,如彩票、奖项等。基于这些,我们从电子邮件中提取相关特征,并说如果给定与垃圾邮件相关的特征,该电子邮件将被分类为垃圾邮件。
贝叶斯定理
贝叶斯定理帮助我们找到给定一定条件下的后验概率:
P(A|B)= P(B|A) * P(A)/P(B)
A 和 B 可以被认为是目标变量和特征,分别。
在哪里,P(A|B):后验概率,表示在事件 B 发生的情况下事件 A 的概率:
-
P(B|A):在目标 A 给定的情况下,特征 B 的似然概率
-
P(A):目标 A 的先验概率
-
P(B):特征 B 的先验概率
朴素贝叶斯分类器是如何工作的
我们将通过查看泰坦尼克号的例子来理解所有这些。当泰坦尼克号沉没时,一些类别在获救方面比其他类别有优先权。我们有以下数据集(这是一个 Kaggle 数据集):
| 人员类别 | 生存机会 |
|---|---|
| 女性 | 是 |
| 儿童 | 是 |
| 儿童 | 是 |
| 男性 | 否 |
| 女性 | 是 |
| 女性 | 是 |
| 男性 | 否 |
| 男性 | 是 |
| 儿童 | 是 |
| 女性 | 否 |
| 儿童 | 否 |
| 女性 | 否 |
| 男性 | 是 |
| 男性 | 否 |
| 女性 | 是 |
现在,让我们为前面的信息准备一个似然表:
| 生存机会 | ||||||
|---|---|---|---|---|---|---|
| 否 | 是 | 总计 | ||||
| 类别 | 儿童 | 1 | 3 | 4 | 4/15= | 0.27 |
| 男性 | 3 | 2 | 5 | 5/15= | 0.33 | |
| 女性 | 2 | 4 | 6 | 6/15= | 0.40 | |
| 总计 | 6 | 9 | 15 | |||
| 6/15 | 9/15 | |||||
| 0.40 | 0.6 |
让我们找出哪个类别的人有最大的生存机会:
儿童 - P(Yes|Kid)= P(Kid|Yes) * P(Yes)/P(Kid)
P(Kid|Yes) = 3/9= 0.3
P(Yes) = 9/15 =0.6
P(Kid)= 4/15 =0.27
*P(Yes|kid) = 0.33 0.6/0.27=0.73
女性 - P(Yes|Woman)= P(Woman|Yes) * P(Yes)/P(Woman)
P(Woman|Yes) = 4/9= 0.44
P(Yes) = 9/15 =0.6
P(Woman)= 6/15 =0.4
*P(Yes|Woman) = 0.44 0.6/0.4=0.66
人 - P(Yes|人) = P(人|Yes) * P(Yes)/P(人)
P(人|Yes) = 2/9= 0.22
* P(Yes) = 9/15 =0.6*
* P(人)= 6/15 =0.33*
*P(Yes|人) = 0.22 0.6/0.33=0.4
因此,我们可以看到,孩子有最大的生存机会,而男人有最小的生存机会。
让我们借助朴素贝叶斯进行情感分类,看看结果是否更好或更差:
from sklearn.naive_bayes import MultinomialNB
# splitting data into training and validation set
xtraintf, xtesttf, ytraintf, ytesttf = train_test_split(tfidfV, Newdata['label'], random_state=42, test_size=0.3)
NB= MultinomialNB()
NB.fit(xtraintf, ytraintf)
prediction = NB.predict_proba(xtesttf) # predicting on the test set
prediction_int = prediction[:,1] >= 0.3 # if prediction is greater than or equal to 0.3 than 1 else 0
prediction_int = prediction_int.astype(np.int)
print("F1 Score-",f1_score(ytest, prediction_int))
print("Accuracy-",accuracy_score(ytest,prediction_int))
输出如下:
在这里,我们可以看到我们之前的结果比朴素贝叶斯的结果要好。
摘要
在本章中,我们学习了语料库构建技术,这些技术包括句子和单词,其中包括词袋模型,以便使文本可用于算法。你还了解了 TF-IDF 以及一个术语相对于文档和整个语料库的重要性。我们讨论了情感分析,以及分类和 TF-IDF 特征提取。
你还介绍了主题建模和评估模型,包括可视化 LDA。我们涵盖了贝叶斯定理以及与朴素贝叶斯分类器一起工作。在下一章中,你将学习关于时序和序列模式发现的内容。
第七章:时间和顺序模式发现
我们中的许多人为了家庭需求去过像 Reliance 和 Walmart 这样的零售商店。假设我们计划从 Reliance Digital 购买一部 iPhoneX。我们通常会做的是访问商店的移动部门,搜索型号,然后选择产品并前往结账柜台。
但是,在当今世界,组织的目标是增加收入。这仅仅通过一次向客户推销一个产品就能实现吗?答案是明确的不。因此,组织开始挖掘与频繁购买项目相关的数据。他们试图找出不同项目之间的关联,以及可以一起销售的产品,这有助于正确的产品定位。通常,它会找出哪些产品是共同购买的,组织可以以类似的方式摆放产品。
这就是我们将在本章中讨论的内容。我们如何通过机器学习手段制定这样的规则?我们将在这里讨论多种技术。
在本章中,我们将涵盖以下主题:
-
关联规则
-
频繁模式增长
-
验证
关联规则
关联规则挖掘是一种技术,它关注于从数据库中观察频繁出现的模式和关联,如关系数据库和事务数据库。这些规则并没有说任何关于个人偏好的内容;相反,它们主要依赖于交易中的项目来推断某种关联。每个交易都有一个主键(唯一的 ID),称为,交易 ID。所有这些交易都被作为一个整体进行研究,并从中挖掘模式。
关联规则可以被视为一个如果—那么的关系。为了详细阐述这一点,我们必须制定一条规则:如果客户购买了一个项目A,那么在相同的交易 ID 下(与项目A一起),客户选择项目B的概率被找到。在这里,你需要理解这不是因果关系,而是一种共现模式,它浮出水面。
这些规则有两个要素:
-
前件(如果):这是通常在项集或数据集中找到的项目/项目组
-
后件(那么):这是一个与前件/前件组一起出现的项目
看看以下规则:
{面包,牛奶} ⇒ {黄油}
这条规则的第一个部分被称为前件,第二个部分(在箭头之后)是后件。它能够传达在交易中如果先选择了面包和牛奶,那么选择黄油的可能性。然而,给定前件,后件在项集中出现的百分比概率并不明确。
让我们看看一些有助于我们达到目标的指标:
- 支持度:这是度量项目集在所有交易中的频率。例如,通过零售店如沃尔玛的交易数量,会出现两个项目集:项目集A = {Milk}),项目集B = {laptop})。鉴于支持度是项目集在所有交易中的频率,我们需要找出哪个项目集具有更高的支持度。我们知道项目集A将具有更高的支持度,因为Milk出现在日常杂货清单(以及交易)中的概率比laptop更高。让我们增加一个关联级别,并使用两个新的项目集进行研究:项目集A= {milk, cornflakes}),项目集B= {milk, USB Drive})。milk和cornflakes一起的购买频率将高于milk and USB Drive。这将使A的支持度指标更高。
让我们将这转化为数学表达式:
支持度(A, B) = 包含 A 和 B 的交易/总交易数
这里有一个例子:
-
-
总交易数是 10,000
-
包含A和B的交易 = 500*
-
那么,支持度*(A, B) = 500/10000= 0.05*
-
5%的交易包含A和B一起
-
- 信心:这表示当项目 2 已经被选中时,项目 1 被购买/选中的可能性。换句话说,它衡量的是在先导交易已经存在的情况下,后续交易发生的可能性。换句话说,如果Bread已经参与了该交易,那么它衡量的是交易中发生Butter的概率。很明显,这是在具有先导条件的情况下,后续事件发生的条件概率:
-
-
信心(A ⇒ B) = 包含 A 和 B 的交易/包含 A 的交易
-
信心可以转换为支持度
-
信心(A ⇒ B) = 支持度(A, B)/支持度(A)
-
这里有一个例子:
-
-
包含项目集milk的交易 = 50*
-
包含项目集cereal的交易 = 30*
-
包含milk和cereal的交易 = 10*
-
总交易数 = 100
-
信心(milk ⇒ Cereal) = 10/(50 +10) = 0.167
-
这意味着该事件发生的概率是 16.7%。
信心的一个缺点是它只考虑了项目 1 的流行程度,但没有考虑项目 2。如果项目 2 同样频繁,那么包含项目 1 的交易也包含项目 2 的可能性会更高。因此,这会导致结果被夸大。为了考虑两个构成项目的频率,我们使用一个称为提升的第三个度量。
- 提升度:这是指在项目 A 已经被选中时,项目 B 被选中的可能性,同时关注项目 B 的频率。提升度大于 1 表示项目 A 和项目 B 之间有很强的关联,这意味着如果项目 A 已经在购物车中,那么项目 B 被选中的可能性很大。提升度小于 1 表示如果项目 A 已经存在,项目 B 被选中的可能性很小。如果提升度为零,则表示无法建立任何关联。
Lift(A⇒B) = (包含 A 和 B 的事务数/(包含 A 的事务数))/包含 B 的事务数
意味着:
= Support(A, B)/(Support(A) * Support(B))
Lift(牛奶⇒麦片) = ( 10/(50+10))/0.4
= 0.416
我们将在这里以更好的格式展示它。在知道牛奶已经在购物车中的情况下,购物车中有麦片的概率(称为置信度)= 10/(50+10) = 0.167。
在不知道牛奶在购物车中的情况下,购物车中有麦片的概率为 cart = (30+10)/100 = 0.4.
这意味着知道牛奶已经在购物车中,将选择麦片的机会从 0.4 降低到 0.167。提升度为 0.167/0.4= 0.416,并且小于 1。因此,当牛奶已经在购物车中时,选择麦片的机会非常小。
Apriori 算法
Apriori 是一种经典的算法,用于挖掘频繁项集以推导出各种关联规则。它将帮助以更好的方式设置零售店,从而有助于增加收入。
支持度测量的反单调性是 Apriori 围绕的核心概念之一。它假设以下内容:
-
一个频繁项集的所有子集都必须是频繁的
-
同样,对于任何不频繁的项集,它的所有超集也必须是不频繁的
让我们来看一个例子并解释它:
| 事务 ID | 牛奶 | 黄油 | 麦片 | 面包 | 书籍 |
|---|---|---|---|---|---|
| t1 | 1 | 1 | 1 | 0 | 0 |
| t2 | 0 | 1 | 1 | 1 | 0 |
| t3 | 0 | 0 | 0 | 1 | 1 |
| t4 | 1 | 1 | 0 | 1 | 0 |
| t5 | 1 | 1 | 1 | 0 | 1 |
| t6 | 1 | 1 | 1 | 1 | 1 |
我们已经得到了事务 ID 和如牛奶、黄油、麦片、面包和书籍等项目。1 表示项目是事务的一部分,0 表示它不是。
- 我们为所有项目创建了一个频率表,包括支持(除以 6):
| 项目 | 事务数量 | 支持度 |
|---|---|---|
| 牛奶 | 4 | 67% |
| 黄油 | 5 | 83% |
| 麦片 | 4 | 67% |
| 面包 | 4 | 67% |
| 书籍 | 3 | 50% |
- 我们将设置一个支持度阈值为 60%,这将根据频率过滤项目,因为这些项目可以被视为此场景中的频繁项集:
| 项目 | 事务数量 |
|---|---|
| 牛奶 | 4 |
| 黄油 | 5 |
| 麦片 | 4 |
| 面包 | 4 |
- 同样,我们用这些项目形成组合数(每次两个、三个和四个),并找出频率:
| 项目 | 交易数量 |
|---|---|
| 牛奶,黄油 | 4 |
| 牛奶,谷物 | 3 |
| 牛奶,面包 | 2 |
| 黄油,面包 | 3 |
| 黄油,谷物 | 4 |
| 谷物,面包 | 2 |
现在,我们再次需要找出前面示例的支持度,并通过阈值进行过滤,即支持度为 60%
类似地,必须一次形成三个项目的组合(例如,牛奶、黄油和面包),并计算它们的支持度。最后,我们将通过阈值过滤它们。同样,需要通过每次四个项目的方式执行相同的过程。我们到目前为止所做的是频繁项集生成。
查找关联规则
为了找到关联规则,我们首先需要搜索所有支持度大于阈值支持度的规则。但是问题来了:我们如何找到这些规则?一种可能的方法是暴力搜索,这意味着列出所有可能的关联规则,并计算每个规则的支持度和置信度。然后,移除所有未通过置信度和支持度阈值的规则。
假设集合* I中有n个元素,可能的关联规则总数为3^n - 2^(n+1) + 1*。
如果X是一个包含k个元素的频繁项集,那么将存在2^k - 2个关联规则。
让我们看看如何在 Python 中执行关联规则:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
data = pd.read_csv('association_mining.csv', header = None)
transactions = []
for i in range(0, 7501):
transactions.append([str(data.values[i,j]) for j in range(0, 20)])
如果我们要求一个项目在一天内出现三次,在七天时间内,支持度将是3 x 7/7051。7051是交易总数。我们最初将置信度设置为 20%:
from apyori import apriori
rules = apriori(transactions, min_support = 0.003, min_confidence = 0.2, min_lift = 3, min_length = 2)
results = list(rules)
results
我们可以通过运行前面代码中的results命令来可视化输出:
频繁模式增长
频繁模式增长(FP-growth)是一种频繁项集生成技术(类似于 Apriori)。FP-Growth 构建一个紧凑的树结构,并使用该树进行频繁项集挖掘和生成规则。它比 Apriori 更快,并且可以处理大型数据集。
让我们通过 FP-Growth 的步骤来了解:
- 设置事务:这一步按频率设置项目。但是,项目是垂直设置的,而不是水平设置。这意味着将输入从事务转换为项目:
| t_id | 项目 |
|---|---|
| 1 | (B, C, D, A) |
| 2 | (B, C, D) |
| 3 | (D, A) |
| 4 | (A, B) |
| 5 | (A, C, B) |
- 查找频率:现在我们必须找出每个项目的单独频率:
| 项目 | 频率 |
|---|---|
| A | 4 |
| B | 4 |
| C | 3 |
| D | 3 |
让我们设置最小阈值或最小支持度为 50%:
-
-
最小支持度 = (5*50/100) = 2.5
-
最小支持度的上限 = 2.5 ~ 3
-
- 按频率优先排序项目:由于所有项目的频率都大于或等于最小支持度,所有项目都将包含在内。此外,根据它们的频率,将分配优先级或排名给项目:
| 项目 | 频率 | 排名 |
|---|---|---|
| A | 4 | 1 |
| B | 4 | 2 |
| C | 3 | 3 |
| D | 3 | 4 |
项目的顺序是:A、B、C 和 D(按频率降序排列)
- 按优先级排序项目:现在,项目的顺序将根据基于频率分配给各种项目的优先级来设置。目前,顺序是 A、B、C 和 D:
| t_id | Items | Order by priority |
|---|---|---|
| 1 | (B, C, D, A) | (A, B, C, D) |
| 2 | (B, C, D) | (B, C, D) |
| 3 | (D, A) | (A, D) |
| 4 | (A, B) | (A, B) |
| 5 | (A, C, B) | (A, B, C) |
频繁模式树增长
我们将从以下行研究不同的频繁模式树的增长:
- 第一行: 每个 FP-树都以一个空节点作为根节点。让我们绘制树的第一个行及其频率:
- 第二行: 它包含 {B,C,D}。A 缺失,所以我们不能将其与前面的节点合并。因此,我们必须创建另一个节点,整体情况如下所示:
- 第三行: 它包含 {A,D}。B 和 C 缺失,但我们可以将其与前面的节点绑定。A 遇到重复,因此其频率将发生变化。现在变为 2:
- 第四行: 它包含 {A,B}。我们可以将其与前面的节点绑定,并将遍历应用于前面的节点。A 和 B 遇到重复,因此其频率将发生变化。分别变为 3 和 2:
- 第五行: 它包含 {A,B,C}。再次,它可以与前面的节点绑定,并且 A、B 和 C 出现重复,因此它们的频率将发生变化。分别变为 4、3 和 2:
验证
现在,让我们计算我们得到的最终树的频率,并将每个项目的频率与表格进行比较,以确保我们在表格中得到了正确的频率:
-
A:4
-
B:4
-
C:3
-
D:3
现在,我们将从底部到顶部进行。我们将找出 D 出现的分支:
我们可以看到有三个分支中出现了 D:
-
BC: 1
-
ABC: 1
-
A: 1
这些分支被称为 D 的条件模式基。当我们这样做时,有一些要点需要注意:
-
即使我们从底部向上遍历,我们也以从上到下的方式编写分支
-
D 不是其中的一部分
-
1 代表每个分支中 D 发生的频率
现在,D 的条件模式导致 A、B 和 C 的条件频率,分别是 2、2 和 2。所有这些都小于最小支持度(3)。因此,不能为它创建任何条件 FP-树。
现在,让我们对 C 进行操作。C 出现在以下分支中:
分支最终如下所示:
-
B:1
-
AB:2
这导致 A:2 和 B:3。因此,根据最小支持度,B 符合条件。现在条件树最终如下所示:
同样,对于不同的组合进行条件模式查找。因此,它建立了频繁项集数据集。
让我们看看如何在 Python 中实现。我们将使用一个名为pyfpgrowth的库。此外,我们将在下一节创建一个项集。
导入库
为了进行验证,我们将导入库并构建如所示的交易:
import pyfpgrowth
我们构建我们的交易如下:
transaction = [["bread", "butter", "cereal"],
["butter", "milk"],
["bread", "milk"],
["butter", "cereal", "milk"],
["egg", "bread"],
["egg", "butter"],
["cereal", "milk"],
["bread", "butter", "cereal", "egg"],
["cereal", "bread", "butter"]]
现在定义最小支持度以找到模式。find_frequent_patterns(),其中transactions是每次交易中购买的项目列表,2是设置的最小支持度阈值:
patterns = pyfpgrowth.find_frequent_patterns(transaction, 2)
最后,我们必须定义置信度以获取规则。规则是基于模式生成的,0.5是置信度的最小阈值。然后,我们将规则存储在名为rules的数据框中。rules最初包含一个前件、一个后件和置信度值:
rules = pyfpgrowth.generate_association_rules(patterns, 0.5)
print(rules)
我们得到以下输出:
这就是我们获取规则的方式。FP-growth 通常比 Apriori 有优势,因为它更快、更高效。
摘要
在本章中,我们学习了关联规则。我们还讨论了 Apriori 算法,该算法用于挖掘频繁项集以推导出各种关联规则。我们还了解了频繁模式增长(FP-growth),它与 Apriori 类似,以及频繁项集生成技术,它与 Apriori 算法类似。最后,我们看到了 FP-growth 如何比 Apriori 有优势,因为它更快、更高效,并通过一个例子来说明。
在下一章中,我们将学习概率图模型。我们将深入了解贝叶斯规则和贝叶斯网络。
第八章:概率图模型
在我们深入探讨贝叶斯网络(BN)的概念之前,我们应该了解概率论的理论。因此,我们将尝试简要介绍它们,并建立贝叶斯网络的基础。
我们已经知道,概率是事件发生的确定性/不确定性程度。然而,它也可以被称为信念程度,这在讨论贝叶斯网络时更为常用。
当我们抛一个公平的硬币时,我们说,关于发生正面/反面的信念程度是 0.5。这意味着我们对发生正面的信念程度与发生反面的信念程度一样强。概率可以如下表示:
p(Heads)=p(tails)=0.5
在本章中,我们将涵盖以下主题:
-
贝叶斯规则
-
贝叶斯网络
关键概念
在我们进入本章的主体之前,我们将介绍几个关键概念:
-
在离散分布的情况下,概率质量函数用于找出概率,p(X= x),其中 X 是一个离散随机变量,x 是一个实数值。
-
在连续分布的情况下,概率密度函数用于找出概率 p(X <= x)。在这种情况下,绘制一个概率曲线,曲线下的面积(积分)帮助我们确定概率。
-
条件概率是为了理解这一点,板球比赛可以是一个完美的例子。假设印度和澳大利亚之间有一场比赛安排,我们正在试图传递我们对印度获胜的信念。你认为这个概率会受到印度选择的队伍的影响吗?如果 维拉特·科赫利 和 罗希特·沙尔马 是队伍中的一员,印度赢得比赛的概率会受到什么影响?所以,p(India winning|Rohit and Virat are playing) 表示在 罗希特 和 维拉特 参赛的情况下,印度获胜的概率。本质上,这意味着一个事件的概率依赖于另一个事件的概率。这被称为条件概率。
给定 y,x 的概率可以表示如下:
- 链式法则通过使用随机变量的条件概率来计算一组随机变量的联合分布。从条件概率中,我们知道
。
这意味着如果有 个事件。联合概率分布将呈现如下:
贝叶斯定理
贝叶斯定理是概率论的一个基石。它源于条件概率和联合概率,并扩展了其应用范围。
我们将通过再次从板球中举一个例子,以简单的方式解释这一点。在板球中,场地条件随着从一个地方到另一个地方的变化而变化,这是决定队伍时可能是一个重要的因素。结果也可能依赖于它。
假设印度队去澳大利亚比赛,我们必须预测一名印度球员在这场比赛中得分百(100 分)的信念。如果这名球员有在那国打球的经验,我们可能会非常确信他可能会得分百。但是,还有另一名球员是第一次来这个国家。那么他的先验信念会是什么?当然,很多人会认为他得分百的信念较低。
然而,我们的先验信念会随着我们看到球员的表现而改变。也就是说,随着球员参加更多比赛,我们将有更多关于球员的数据。基于这些,后验信念将不断更新。它变化很大,很大程度上是由于观察或更多数据(称为似然)。贝叶斯定理基于这些概念。
假设 A[i] 与 B 形成互斥事件:
B 的概率如下:
我们从条件概率中得到 B 的概率如下:
因此:
现在,从方程 2 中提取 的值,并将其放入方程 1 中,我们得到以下结果:
在将先前的方程中的 P(B) 值替换后,我们得到以下结果:
首先看看方程 3。这被称为贝叶斯定理。
P(A|B) 被称为后验概率,需要被估计。在先前的例子中,这将是玩家在之前有在该地打球经验的情况下得分百的概率。
P(B|A) 被称为似然,是在给定我们的初始假设的情况下观察新证据的概率。例如,一名球员在之前有打板球经验的情况下得分百的概率。
P(A) 被称为先验概率,是在没有任何额外先验信息的情况下我们假设的概率。
P(B) 被称为边缘似然,是观察证据的总概率。
贝叶斯网络
贝叶斯网络是一种概率图模型,可用于构建解决商业问题的模型。这种应用非常广泛。例如,它可以用于异常检测、预测建模、诊断、自动洞察以及许多其他应用。
对于这里使用的某些词汇,你可能现在还感到陌生。例如,我们在这里所说的“图形化”是什么意思?
图是由一组节点和边组成的。节点用*N={N1,N2……Nn}表示,其中独立变量位于每个节点上。边是节点之间的连接器。边可以用E={E1, E2……En}*表示,并且有两种类型:
-
有向的,表示为
-
无向的,表示为:
通过节点和边,可以展示变量之间的关系。这可能是一个条件独立性关系或条件依赖关系。贝叶斯网络(BN)是一种可以在变量之间引入因果关系的技巧。尽管因果关系不是其本质的一部分,但在网络中拥有这种(因果关系)可以使结构相当紧凑。
让我们通过一个例子来看一下。有许多变量,例如晚起、高速公路上的事故、雨天、交通堵塞,他们将会迟到上班,以及会议迟到。如果一个人起床晚,这意味着上班迟到。高速公路上的事故可以导致交通堵塞,进而导致上班迟到。在雨天,道路更容易发生事故,而且也可能有缓慢移动的交通,这将导致交通堵塞,进而导致上班迟到。以下图表解释了该例子:
这种网络被称为有向无环图。无环意味着网络中没有循环。我们在这里讨论的是变量之间的关系。例如,晚起和会议迟到通常不是独立的。但考虑到上班迟到,它们是条件独立的。
此外,可能看起来晚起与高速公路上的事故之间没有联系和关系。也就是说,它们可能看起来彼此独立。然而,如果你知道上班迟到的重要性,那么这两个可以被称为条件独立。
因此,贝叶斯网络允许节点之间的条件独立性。同时,它是一个高效的联合概率分布表示,这是由链式法则实现的。
假设 X 代表 n 个独立变量或节点。弧或指向箭头代表变量之间的概率依赖或独立性。没有弧表示概率独立性。该网络是一个有向无环图,其中每个节点都保持局部概率分布,这也可以称为条件概率表(CPT)。
如果我们谈论前面的网络,那么我们需要整个网络所需的概率分布。为了简化,我们将所有节点都保持为布尔值。
节点的概率
让我们看看每个节点的概率,并找出会有多少概率出现在那里。
带有晚起和雨天的节点是父节点,因为没有节点指向这样的节点。不同的节点可以在以下要点中看到:
-
节点(晚起):作为父节点之一,我们只需找出晚起的概率。因此,要找到的概率计数在这里是 1。
-
节点(雨天):与晚起节点一样,概率的计数在这里也是 1。
-
节点(高速公路事故):作为雨天节点的子节点,它讨论了在雨天和不是雨天的情况下发生事故的概率。因此,概率计数在这里是 2。
-
节点(交通堵塞):它有两个父母(雨天和事故)。雨天有两个值,即真和假,与事故相同。结合两者将产生四种不同的组合。因此,概率计数将是 4。
-
节点(上班迟到)和节点(开会迟到):这两个节点也有类似的解释。这些概率的计数是 4:
概率的总数是 1 + 2 + 1 + 4 + 4 + 4 = 16。
如果它只是一个普通的联合概率分布而不是贝叶斯网络(BN),我们就会有 2⁶-1 个概率。因此,BN 使得网络相当紧凑。此外,我们还必须注意的一个更基本的假设是,每个节点在给定其直接父母的情况下对其非后裔是条件独立的。例如,起床晚和开会迟到在“上班迟到”也存在的情况下是条件独立的。一般来说,我们可以以下述方式表达 BN,这显示了联合分布如何转化为紧凑的结构:
如果G是图,X[i]是图G中的一个节点,P是*X[i]*节点的父母。
这里有一些关于方程的注意事项:
-
方程式的右侧是链式法则的应用,它展示了条件独立性关系。它是联合概率分布的图结构近似。
-
当然,图必须是循环的。
-
它可以提供显示各种事件之间关系的便利。
现在,让我们通过一个简单的场景来展示 CPT(共变概率定理)。以下是将三个事件组合起来的示例:
如果下雨,狗开始叫,男人会旷工:
-
下雨的概率(是/否)
-
狗会叫的概率(是/否)
-
男人是否会旷工(是/否)
让我们将网络准备成一个有向无环图。所有这些节点都反映一个事件,有向箭头是条件概率。我们将在这里看到如何读取这个图:
-
连接器 1 表示如果下雨狗会叫的概率
-
连接器 2 表示如果狗叫,男人会旷工的概率
以下图表显示了两个概率的流程图:
条件概率表(CPT)
让我们为连接器 1 做条件概率表(CPT):
| 狗叫 | 狗不叫 | 总计 | |
|---|---|---|---|
| 下雨 | 10 | 4 | 14 |
| 不下雨 | 8 | 5 | 13 |
| 总计 | 18 | 9 | 27 |
这里,我们讨论以下场景:
-
概率*(狗叫|下雨) = 10/14*
-
概率*(狗不叫 | 下雨) = 4/14*
-
概率*(狗叫 | 不下雨) = 8/13*
-
概率*(狗不叫 | 不下雨) = 5/13*
| 狗叫 | 狗不叫 | |
|---|---|---|
| 下雨 | 10/14 | 4/14 |
| 不下雨 | 8/13 | 5/13 |
以下图表详细显示了概率:
假设下雨的概率为 P(下雨) = 0.6,那么不下雨的概率为 P(不下雨) = 0.4。
假设男人旷工的条件概率表(CPT)如下:
| 男人旷工 | 男人不旷工 | |
|---|---|---|
| 狗叫 | 0.8 | 0.2 |
| 狗不叫 | 0.3 | 0.7 |
每个事件的概率都必须相对于父节点来计算。
现在,我们应该找出男人旷工、狗叫但不下雨的概率 P (男人旷工, 狗叫, 不下雨):
*= P (男人旷工|狗叫) *P (狗叫|不下雨) P(不下雨)
*=0.8 * (8/13) 0.4
=0.1969
训练集和测试集的示例
让我们用一个案例来演示,并在 Python 中实现它。我们将使用 Kaggle 上的泰坦尼克号数据。
数据已经被分成两组:
-
训练集(
train.csv) -
测试集(
test.csv)
数据是关于在泰坦尼克号上旅行的乘客。它捕捉了他们的特征:
-
pclass: 票等级 1 = 1 等,2 = 2 等,3 = 3 等 -
gender: 性别 -
Age: 年龄(年) -
sibsp: 泰坦尼克号上的兄弟姐妹/配偶数量 -
parch: 泰坦尼克号上的父母/孩子数量 -
ticket: 票号 -
fare Passenger: 乘客票价 -
cabin: 船舱号 -
embarked: 登船港口C = Cherbourg,Q = Queenstown, 和S = Southampton
我们必须构建模型来预测他们是否在泰坦尼克号沉没中幸存。最初,按照以下方式导入参数:
import pandas as pd
import numpy as np
我们在这里加载数据集:
traindf= pd.read_csv("train.csv")
testdf= pd.read_csv("test.csv")
我们必须寻找每个变量的唯一值数量,因为贝叶斯网络是离散模型:
for k in traindf.keys():
print('{0}: {1}'.format(k, len(traindf[k].unique())))
输出如下:
为了避免系统过载和过多的计算,我们将减少变量的数量:
for k in traindf.keys():
if len(traindf[k].unique())<=10:
print(k)
我们得到以下输出:
现在,我们剩下六个变量。
此外,如果需要将连续变量纳入模型,我们必须对它们进行离散化:
import math
def forAge(row):
if row['Age'] < 10:
return '<10'
elif math.isnan(row['Age']):
return "nan"
else:
dec = str(int(row['Age']/10))
return "{0}0's".format(dec)
decade=traindf.apply(forAge, axis=1)
print("Decade: {1}".format(k, len(decade.unique())))
输出结果如下:
现在让我们进行预处理:
def preprocess(df):
# create a dataframe with discrete variables (len<10)
filt=[k for k in df.keys() if len(df[k].unique())<=10]
filtr2=df[filt].copy()
forAge = lambda row: int(row['Age']/10) if not math.isnan(row['Age']) else np.nan
filtr2['Decade']=df.apply(forAge, axis=1)
filtr2=filtr2.dropna()
filtr2['Decade']=filtr2['Decade'].astype('int32')
return filtr2
对于traindf和testdf,我们使用以下方法:
ptraindf= preprocess(traindf)
ptestdf=preprocess(testdf)
我们需要保存这些数据,因为pyAgrum库只接受文件作为输入:
ptraindf.to_csv('post_train.csv', index=False)
ptestdf.to_csv( 'post_test.csv', index=False)
df=pd.read_csv('post_train.csv')
for k in df.keys():
print("{} : {}".format(k, df[k].unique()))
下面的输出结果如下:
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
现在,是时候构建模型了。在这里,选择RangeVariable和LabelizedVariable变量时需要格外小心:
template=gum.BayesNet()
template.add(gum.RangeVariable("Survived", "Survived",0,1))
template.add(gum.RangeVariable("Pclass", "Pclass",1,3))
template.add(gum.LabelizedVariable("Gender", "Gender",0).addLabel("female").addLabel("male"))
template.add(gum.RangeVariable("SibSp", "SibSp",0,8))
template.add(gum.RangeVariable("Parch", "Parch",0,9))
template.add(gum.LabelizedVariable("Embarked", "Embarked",0).addLabel('').addLabel('C').addLabel('Q').addLabel('S'))
template.add(gum.RangeVariable("Decade", "Calculated decade", 0,9))
gnb.showBN(template)
输出结果如下:
对于learnBN()函数,我们使用以下方法:
learner = gum.BNLearner('post_train.csv', template)
bn = learner.learnBN()
bn
下面的输出结果如下:
现在我们有了模型,让我们尝试从中提取信息:
gnb.showInformation(bn,{},size="20")
我们得到的输出结果如下:
变量的熵意味着值越大,变量的边际概率分布的不确定性就越大。熵值越低,不确定性就越低。Decade变量具有最高的熵,这意味着它是均匀分布的。Parch 具有较低的熵,分布不均匀。
熵的计算结果导致熵值在随机变量具有许多模式时往往会变大。
找到推理结果让我们看到了边际概率分布的情况:
gnb.showInference(bn)
输出结果如下:
现在,让我们看看如何进行分类:
gnb.showPosterior(bn,evs={},target='Survived')
我们得到的输出结果如下:
在这里,超过 40%的乘客存活。但我们没有施加任何条件。
假设我们想知道一位年轻男性的存活机会:
gnb.showPosterior(bn,evs={"Gender": "male", "Decade": 3},target='Survived')
下面的输出结果如下:
因此,存活的机会是 20.6%。
如果我们需要找出一位老妇人存活的机会,我们将按照以下步骤进行:
gnb.showPosterior(bn,evs={"Gender": "female", "Decade": 8},target='Survived')
输出结果如下:
现在,为了评估模型以了解其好坏,我们将绘制 ROC 曲线:
from pyAgrum.lib.bn2roc import showROC
showROC(bn, 'post_train.csv','Survived',"1",True,True)
输出结果如下:
在这里,AUC值为0.893508,相当不错。
我们在这里完成了建模部分。此外,我们还学习了概率论、贝叶斯网络、CPT 的计算以及如何在 Python 中执行它。
摘要
本章让我们了解了概率论。同时,概率论的应用也得到了实践。我们了解了贝叶斯定理和贝叶斯网络及其形成方式。我们亲自动手计算了 CPT。最后,我们通过一个案例了解了如何使用贝叶斯网络进行分类。读者现在将具备深入了解贝叶斯定理和贝叶斯网络的能力。
在下一章,我们将研究深度学习中的选定主题。
第九章:深度学习中的选讲主题
在第四章*“训练神经网络”中,我们探讨了什么是人工神经网络(ANN)以及这种模型是如何构建的。你可以认为深度神经网络是 ANN 的延伸版本;然而,它也有自己的一套挑战。
在本章中,我们将学习以下主题:
-
什么是深度神经网络?
-
如何初始化参数
-
对抗网络——生成对抗网络和贝叶斯生成对抗网络
-
深度高斯过程
-
霍 inton 的胶囊网络
深度神经网络
让我们回顾一下我们在第四章*“训练神经网络”中学习的内容。神经网络是机器模拟人脑,被视为一套旨在从数据中提取模式的算法。它有三个不同的层:
-
输入层
-
隐藏层
-
输出层
感官数值数据(以向量的形式)通过输入层,然后通过隐藏层生成它自己的感知和推理,以在输出层产生最终结果。
你能回忆起我们在第四章“训练神经网络”中学到的内容,关于人工神经网络(ANN)中的层数以及我们如何计算它们吗?当我们有如下图中所示的层时,你能计算出层数吗?记住,我们只计算隐藏层和输出层。所以,如果有人问你你的网络中有多少层,在回答时不要包括输入层:
是的,没错——前面的架构中有两层。那么对于下面的网络呢?
这个网络有三个层,包括两个隐藏层。随着层的增加,模型变得更深。
为什么我们需要深度学习模型?
深度学习模型是一个高度非线性的模型,具有多层和多个节点按顺序工作以解决业务问题。每一层都被分配了不同的任务。
例如,如果我们有一个面部检测问题,隐藏层 1 找出图像中存在的哪些边缘。层 2 找出边缘的组合,这些边缘开始呈现出眼睛、鼻子和其他部分的形状。层 3 使对象模型得以创建,从而形成人脸的形状。以下图表显示了不同的隐藏层:
这里,我们得到了一个逻辑回归模型,也称为单层神经网络。有时,它也被称作最浅层网络。这里可以看到的第二个网络有一个两层网络。再次,它是一个浅层网络,但不如前一个浅。下一个架构有三个层,这使得事情更有趣。网络现在变深了。最后一个架构有一个六层架构,由五个隐藏层组成。层数变得更深了。
深度神经网络符号
符号的解释如下:
-
l:层数为 4
-
n^([l]):第 l 层的节点数
对于以下架构,这是如下所示:
-
n ^([0]):输入层的节点数,即 3
-
n ^([1]):5
-
n ^([2]):5
-
n ^([3]):3
-
n ^([4]):1
-
a ^([l]):第 l 层的激活值
正如我们已知的,以下方程通过各层:
z = w^TX + b
因此,我们得到以下结果:
-
激活:a = σ(z)
-
w^([l]):第 l 层的权重
-
b^([l]):第 l 层的偏置
深度网络的正向传播
让我们看看这些方程是如何为第 1 层和第 2 层设置的。如果训练示例集 X 是前述网络中的 (x1, x2, x3)。
让我们看看方程是如何应用于第 1 层的:
第 1 层的激活函数如下:
输入也可以表示如下:
对于第 2 层,输入将是以下内容:
这里应用到的激活函数如下:
同样,对于第 3 层,应用的输入如下:
第 3 层的激活函数如下:
最后,这是最后一层的输入:
这是它的激活:
因此,广义正向传播方程如下:
参数 W 和 b
让我们讨论以下架构。首先,让我们记下我们在上一节中学到的内容。看一下以下图表:
这里,我们可以看到以下内容:
-
l:层数:6
-
n ^([l]):第 l 层的节点数
-
n ^([0]):输入层的节点数:3 ::
-
n ^([1]):第一层的节点数:4 ::
这个方程如下:
n ^([2])= 4 :: n ^([3]) = 4 :: n ^([4]) = 4 :: n ^([5]) =3 :: n ^([6])= 1
实现前向传播意味着隐藏层 1 可以通过以下方程表示:
…..(1)
你能确定 z、w 和 X 的前向传播维度吗?
让我们来讨论这个问题。X表示输入层向量或节点,我们知道有 3 个节点。我们能找出输入层的维度吗?嗯,是的,它是(n^([0]), 1)——或者你也可以说它是(3,1)。
对于第一隐藏层呢?由于第一隐藏层有三个节点,*z^([1])*的维度将是(n^([1]),1)。这意味着维度将是(4,1)。
z^([1])和 X 的维度已经被确定。通过观察前面的方程,很明显z^([1])和w^([1])X的维度必须相同(来自线性代数)。那么,你能推导出*w^([1])*的维度吗?我们知道,从线性代数的角度来看,矩阵 1 和矩阵 2 之间的乘法只有在矩阵 1 的列数等于矩阵 2 的行数时才可能。因此,*w^([1])的列数必须等于矩阵 X 的行数。这将使得w^([1])*的列数为 3。然而,正如我们已经讨论过的,z^([1])和w^([1])X的维度必须相同,因此前者的行数应该等于后者的行数。因此,*w^([1])的行数将变为 4。好的,我们现在已经得到了w^([1])的维度,它是(4,3)。为了使这个结论更普遍,我们也可以说w^([1])*的维度是(*n^([1]),n^([0]))。同样,*w^([2])*的维度将等于(n^([2]),n^([1]))或者(当前层的节点数,前一层节点数)。这将使得w^([2])*的维度为(4,4)。让我们来概括一下。让我们看看以下方程的维度:
w^([1])= (n^([1]),n^([l-1]))
那么,偏置*b^([1])的维度是什么?你能利用线性代数并找出它吗?现在这对你来说应该像吃蛋糕一样简单。是的,你现在可能已经正确猜到了。它具有与z^([1])*相同的维度。让我来解释这一点,为了大家的利益。根据方程,左侧的维度应该等于右侧的维度。此外,w^([1])X + b^([1])是两个矩阵的和,众所周知,只有当两个矩阵具有相同的维度时才能相加;也就是说,它们必须有相同的行数和列数。因此,b^([1])的维度将等于w^([1])X;反过来,它将等于z^([1])(它是(4,1))。
在一般化的意义上,*b^([1])的维度是( n^([1]), 1)。
对于反向传播,情况如下:
-
*d**w^([l])的维度= (n^([l]),n^([l-1]))
-
db*^([l])的维度= (n^([l]), 1)
前向和反向传播
让我通过一个例子向您展示正向传播和反向传播是如何工作的。
我们有一个具有两层(1 个隐藏层和 1 个输出层)的网络。每个层(包括输入层)都有两个节点。它还有偏置节点,如下面的图所示:
上图中使用的符号如下:
-
IL: 输入层
-
HL: 隐藏层
-
OL: 输出层
-
w: 权重
-
B: 偏置
我们已经得到了所有所需字段的值。让我们将这些值输入到网络中,看看它如何流动。这里使用的激活函数是 Sigmoid 函数。
传递给隐藏层第一个节点的输入如下:
InputHL1 = w1IL1 + w3IL2 + B1
InputHL1= (0.20.8)+(0.40.15) + 0.4 =0.62
传递给隐藏层第二个节点的输入如下:
InputHL2 = w2IL1 + w4IL2 + B1
InputHL2 = (0.250.8) +(0.10.15) + 0.4 = 0.615
为了找到输出,我们将使用我们的激活函数,如下所示:
OutputHL1 = = 0.650219
OutputHL2 = = 0.649081
现在,这些输出将作为输入传递到输出层。让我们计算输出层节点的输入值:
InputOL1 = w5Output_HL1 + w7Output_HL2 + B2 = 0.804641
InputOL2= w6Output_HL1 + w8Output_HL2 + B2= 0.869606
现在,让我们计算输出:
Output[OL1] = = 0.690966
Output[OL2] = = 0.704664
误差计算
我们现在可以使用平方误差函数计算每个输出神经元的误差,并将它们相加以得到总误差:
*Etotal = *
EOL1 = 输出层第一个节点的误差 =
=0.021848
EOL2 = 输出层第二个节点的误差 =
=0.182809
Total Error = Etotal= EOL1 + EOL2 = 0.021848 + 0.182809 = 0.204657
反向传播
反向传播的目的是更新网络中每个权重的值,以便它们使实际输出更接近目标输出,从而最小化每个输出神经元和整个网络的误差。
让我们先关注输出层。我们应该找出 w5 的变化对总误差的影响。
这将由决定。它是 Etotal 相对于 w5 的偏导数。
让我们应用链式法则:
= 0.690966 – 0.9 = -0.209034
= 0.213532
InputOL1 = w5OutputHL1 + w7OutputHL2 + B2
= 0.650219
现在,让我们回到旧方程:
为了更新权重,我们将使用以下公式。我们已将学习率设置为 α = 0.1:
同样, 也应该被计算。方法保持不变。我们将留给你计算,因为它将帮助你更好地理解概念。
当涉及到隐藏层和计算时,方法仍然保持不变。然而,公式会有所变化。我会帮你弄清楚公式,但其余的计算必须由你自己完成。
我们在这里取 w1。
让我们在这里应用链式法则:
这个公式必须用于 w2、w3 和 w4。请确保你对 E_total 进行其他权重的偏导数,最后使用学习率公式来获取更新的权重。
前向传播方程
我们知道周围的方程。如果这个输入是 a^([l-1]),那么输出将是 a^([l])。然而,还有一个缓存部分,它就是 z*^([l])*,如下图中所示:
这里,这分解为 w^([1])a^([l-1]) +b^([l]) (记住 a^([0]) 等于 X)。
反向传播方程
执行反向传播需要以下方程:
这些方程将给你一个关于幕后发生的事情的思路。在这里,添加了一个后缀,d,它表示在反向传播期间起作用的偏导数:
参数和超参数
当我们着手构建深度学习模型时,你需要知道如何同时监控参数和超参数。但我们到底理解得有多好呢?
当涉及到参数时,我们拥有权重和偏差。当我们开始训练网络时,首要步骤之一就是初始化参数。
偏置初始化
初始化偏置为零是一种常见做法,因为神经元的对称打破由随机权重的初始化来处理。
超参数
超参数是深度学习网络的基本构建块之一。它是决定网络最佳架构(例如,层数)的元素,也是确保网络如何训练的因素。
以下是深度学习网络的各个超参数:
-
学习率:这是负责确定网络训练速度的。慢速学习率确保平滑收敛,而快速学习率可能不会平滑收敛。
-
周期:周期数是在训练过程中网络消耗整个训练数据的次数。
-
隐藏层数量:这决定了模型的结构,有助于实现模型的最佳容量。
-
节点数(神经元):应权衡使用的节点数。它决定了是否已经提取了所有必要的信息以产生所需输出。节点数将决定过拟合或欠拟合。因此,建议与正则化一起使用。
-
Dropout:Dropout 是一种正则化技术,通过避免过拟合来提高泛化能力。这在第四章《训练神经网络》中进行了详细讨论。Dropout 值可以在 0.2 到 0.5 之间。
-
动量:这决定了向收敛方向下一步的方向。在 0.6 到 0.9 之间,它处理振荡。
-
批量大小:这是输入到网络中的样本数,之后发生参数更新。通常,它取 32、64、128、256。
为了找到最佳的超参数数量,部署网格搜索或随机搜索是明智的。
用例 - 数字识别器
修改后的国家标准与技术研究院(MNIST)实际上是为计算机视觉的“hello world”准备的。考虑到它在 1999 年的发布,这个数据集已经作为基准分类算法的主要基础。
我们的目标是从包含数万张手写图像的数据集中正确识别数字。我们精心制作了一套教程风格的内核,涵盖了从回归到神经网络的所有内容:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import seaborn as sns
%matplotlib inline
from sklearn.model_selection import train_test_split
import itertools
from keras.utils.np_utils import to_categorical # convert to one-hot-encoding
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D
from keras.optimizers import SGD
from keras.preprocessing.image import ImageDataGenerator
sns.set(style='white', context='notebook', palette='deep')
np.random.seed(2)
# Load the data
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")
Y_train = train["label"]
# Drop 'label' column
X_train = train.drop(labels = ["label"],axis = 1)
Y_train.value_counts()
前一段代码的输出如下:
X_train.isnull().any().describe()
这里,我们得到以下输出:
test.isnull().any().describe()
这里,我们得到以下输出:
X_train = X_train / 255.0
test = test / 255.0
通过将图像重塑为 3 维,我们得到以下结果:
Reshape image in 3 dimensions (height = 28px, width = 28px, canal = 1)
X_train = X_train.values.reshape(-1,28,28,1)
test = test.values.reshape(-1,28,28,1)
Encode labels to one hot vectors
Y_train = to_categorical(Y_train, num_classes = 10)
# Split the dataset into train and the validation set
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size = 0.1, random_state=2)
执行以下代码后,我们将能够看到编号的图表:
pic = plt.imshow(X_train[9][:,:,0])
输出如下:
顺序模型现在如下:
model = Sequential()
model.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', activation ='relu', input_shape = (28,28,1)))
model.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', activation ='relu'))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'))
model.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'))
model.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(256, activation = "relu"))
model.add(Dropout(0.5))
model.add(Dense(10, activation = "softmax"))
当我们定义优化器时,我们得到以下输出:
# Define the optimizer
optimizer = SGD(lr=0.01, momentum=0.0, decay=0.0)
当我们编译模型时,我们得到以下输出:
# Compile the model
model.compile(optimizer = optimizer, loss = "categorical_crossentropy", metrics=["accuracy"])
epochs = 5
batch_size = 64
接下来,我们生成图像生成器:
datagen = ImageDataGenerator(
featurewise_center=False, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=False, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=10, # randomly rotate images in the range (degrees, 0 to 180)
zoom_range = 0.1, # Randomly zoom image
width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.1, # randomly shift images vertically (fraction of total height)
horizontal_flip=False, # randomly flip images
vertical_flip=False) # randomly flip images
datagen.fit(X_train)
history = model.fit_generator(datagen.flow(X_train,Y_train, batch_size=batch_size),
epochs = epochs, validation_data = (X_val,Y_val),
verbose = 2, steps_per_epoch=X_train.shape[0] // batch_size)
输出如下所示:
我们预测模型如下:
results = model.predict(test)
# select with the maximum probability
results = np.argmax(results,axis = 1)
results = pd.Series(results,name="Label")
results
输出如下所示:
生成对抗网络
生成对抗网络(GANs)是另一种深度神经网络架构,是两个相互竞争和合作的网络的组合。它由 Ian Goodfellow 和 Yoshua Bengio 于 2014 年提出。
GANs 可以学习模仿任何数据分布,这理想情况下意味着 GANs 可以被训练来创建与现有领域中的任何类似的对象,例如图像、音乐、语音和散文。它可以创建任何从未存在过的物体的照片。从某种意义上说,它们是机器人艺术家,它们的输出令人印象深刻。
它属于无监督学习,其中两个网络在训练过程中各自学习其任务。其中一个网络被称为生成器,另一个被称为判别器。
为了使这更容易理解,我们可以将生成对抗网络(GAN)想象成一个伪造者(生成器)和警察(判别器)的案例。一开始,伪造者向警察展示假币。警察就像侦探一样,发现这些是假币(如果你想了解判别器是如何工作的,也可以把 D 想象成侦探)。警察将他的反馈传递给伪造者,解释为什么这些钱是假的。伪造者根据收到的反馈做一些调整,并基于这些反馈制作新的假币。警察说这些钱仍然是假的,并将他的新反馈与伪造者分享。然后,伪造者根据最新的反馈尝试制作新的假币。这个过程无限循环,直到警察被这些看起来真实的假币欺骗。当创建 GAN 模型时,生成器和判别器从零开始相互学习。它们似乎是对抗的,但实际上它们在互相帮助学习。这两个之间的反馈机制有助于模型变得更加鲁棒。
判别器是一个非常优秀的学习者,因为它能够从现实世界中学习任何东西。也就是说,如果你想让它学习关于猫和狗的图片,以及它被要求区分的 1,000 个不同类别,它将能够轻松做到,就像这样:
噪声进入生成器;然后,生成器的输出通过判别器,我们得到一个输出。同时,判别器正在对狗的图片进行训练。然而,在最开始,即使是狗的图片也可能被判别器错误地分类为非狗图片,并注意到这个错误。这个错误通过网络反向传播。
霍 inton 的胶囊网络
深度学习之父杰弗里·辛顿通过引入一个新的网络在深度学习领域引起了巨大的轰动。这个网络被称为胶囊网络(CapsNet)。还提出了一个训练这个网络的算法,称为胶囊之间的动态路由。辛顿首次在 2011 年的论文《转换自编码器》中提到了它。2017 年 11 月,辛顿和他的团队发表了一篇关于胶囊网络的完整论文。
胶囊网络和卷积神经网络
卷积神经网络(CNN)是深度学习领域的一个重要里程碑。它让每个人都感到兴奋,也是新研究的基础。但是,正如人们所说,世界上没有什么是完美的。我们心爱的 CNN 也不例外。
你能回忆起 CNN 是如何工作的吗?CNN 最重要的任务是执行卷积。这意味着一旦你将一张图片通过 CNN,卷积层就会从图像像素中提取出特征,如边缘和颜色梯度。其他层会将这些特征组合成一个更复杂的特征。而且,一旦密集层被保留,它就使得网络能够执行分类任务。以下图表显示了我们在工作的图像:
上述图表是一个基本的 CNN 网络,它被用来检测图像中的汽车。以下图表显示了同一辆车的完整图像和碎片化图像:
假设我们将这两张图片通过 CNN 网络(用于检测汽车)传递过去——网络对这两张图片的响应会是什么?你能思考一下并给出答案吗?为了帮助你,一辆车有几个组成部分,如车轮、挡风玻璃、引擎盖等,但人类眼睛认为当所有这些部分/组件按顺序排列时,它才是一辆车。然而,对于 CNN 来说,只有特征才是重要的。CNN 不考虑相对位置和方向关系。因此,这两张图片都将被网络分类为汽车,尽管这在人类眼中并非如此。
为了弥补这一点,CNN 包括最大池化,这有助于增加更高层神经元的视野,从而使得检测更高阶特征成为可能。最大池化使得 CNN 能够工作,但同时也发生了信息损失。这是 CNN 的一个大缺点。
摘要
在本章中,我们研究了深度神经网络以及为什么我们需要深度学习模型。我们还学习了前向和反向传播,以及参数和超参数。我们还讨论了 GANs、深度高斯过程、胶囊网络和 CNN。
在下一章中,我们将研究因果推断。