用代码理解通用逼近定理
通用逼近定理声称,具有包含有限个隐藏神经元的单个隐藏层的标准多层前馈网络能够通过使用任意激活函数来逼近连续函数。(来源)
然而,神经网络逼近将输入映射到输出目标的任何连续函数的能力受到神经元数量、隐藏层和在网络训练过程中使用的许多技术的限制。直觉上,你可以认为这是关于是否可能有足够的计算单元和操作来近似一个连续的函数,该函数可以正确地将输入映射到输出。近似的能力也高度依赖于我们使用的优化程序和损失函数的效率。
建议:下载脚本,自己运行,围绕参数玩。回购是( 此处 )。如果你已经忘记了神经网络,那就来看看吧( 此处 )。
这些决定神经网络的设置和训练的参数通常被称为超参数。
我们可以在代码中调整的超参数示例:
- 网络结构。(隐藏层数,神经元数)
model = nn.Sequential(
nn.Linear(1, n_neurons),
nn.ReLU(),
#nn.Linear(n_neurons,n_neurons),
#nn.ReLU(),
nn.Linear(n_neurons,1),
nn.ReLU()
)
2.历元数(我们浏览所有数据的次数),第 57 行
3.损失函数和优化器,有这么多可用的优化器,查看一下[ 此处 ]:
optimizer = optim.RMSprop(model.parameters(), lr=learning_rate) # define optimizer
#optimizer = optim.SGD(model.parameters(), lr=learning_rate)criterion = nn.MSELoss() # define loss function
代码实验
我们可以在代码中运行一些实验来更好地理解近似的概念。假设我们试图逼近的函数具有 y=x 的关系,我们可以运行一些实验来测量单个隐藏层需要多少个神经元来拟合 y = x 曲线,并调整超参数以搜索最佳结果。
图 1:具有单一隐藏层的前馈神经网络(调整一段时间后)
从上图(图 1)中,我们可以看到在单个隐藏层中有 20 个神经元,神经网络仅通过训练输出值就能够很好地逼近函数。在单个隐藏层中增加到 50 个神经元为我们提供了更好的结果。
简要回顾一下,如果您忘记了,这是一个具有 8 个神经元的单隐层前馈网络架构的简单图示:
图 2:单隐层前馈神经网络的结构
理论上,通用逼近定理意味着当给定适当的组合值时,神经网络可以很好地逼近各种各样的函数。然而,由于在搜索这些值时训练网络时的限制/挑战,学会用适当的值构建网络并不总是可能的。
图 3:具有单一隐藏层的前馈神经网络。超参数的不良调整导致不良训练。
从上图(图 3)来看,同一个架构的单隐层,网络近似的很差。这是因为训练神经网络并不总是为我们提供精确/完美的值。因此,我们必须意识到,尽管理论上神经网络可以逼近非常精确的连续函数映射,但它可能无法逼近预期的连续函数,因为神经网络的训练过程有其自身的挑战。
图 4:具有两个隐藏层的前馈神经网络。超参数的不良调整会导致不好的训练,但结果仍然很好。
运行另一个实验,我们用 20 个神经元和 50 个神经元连接另一个隐藏层,结果可以在上图中看到(图 4)。可以观察到,预测函数的近似更好,而不需要花费太多时间来调整预期的训练参数。在寻找更好的近似时增加神经元和连接是一个很好的启发,但我们必须记住,在训练神经元的过程中也存在一些挑战,这些挑战可能会阻止神经网络学习近似函数所需的最佳值,即使理论上有足够多的节点可用。
图 5:具有单一隐藏层的前馈神经网络。超参数的出色调整带来良好的训练。
实验的另一个重要收获是,通过花更多时间调整神经网络的超参数,我们实际上可以获得一个近乎完美的近似,具有相同的 1 个隐藏层和 50 个神经元的架构,如上图所示(图 5)。可以观察到,结果甚至比使用具有不良超参数的 2 个隐藏层更好。如果我们花更多的时间调整超参数,使用两个隐藏层的实验肯定可以逼近得更好。这表明优化程序和某些超参数对训练网络是多么重要。有了 2 层和更多的神经元,不需要太多的调整就可以得到好的结果,因为有更多的连接和节点可以使用。然而,随着我们添加更多的节点和层,计算成本会变得更高。
最后,如果关系太复杂,那么 1 个具有 50 个神经元的隐藏层甚至可能在理论上不能首先足够好地近似输入到输出的映射。y=x 是一个相对容易近似的关系,但我们可以考虑图像等输入的关系。图像像素值与图像分类之间的关系极其复杂,即使是最好的数学家也不可能找到合适的函数。但是,我们可以通过添加更多的隐藏层和神经元,使用神经网络来近似这种复杂的关系。这催生了深度学习领域,它是机器学习的一个子集,专注于利用具有许多层的神经网络(例如:深度神经网络、深度卷积网络)来学习非常复杂的函数映射。
建议
请尝试其他函数,如sin(x)
或cos(x)
,看看您是否能很好地近似这个关系。您可能会一直失败,直到您得到正确的超参数,但是它会让您对优化超参数有一个很好的了解。我建议,如果函数太难近似,继续添加更多的层和神经元!尝试不同的优化器,如 SGD 和 ADAM,比较结果。
使用日志了解您的计算机系统📃
System.out.println(“到此为止的作品”);
作为一个程序员,你可能已经写了上面的代码段来弄清楚你的代码是怎么回事(这里是 Java)。通常,当我们试图调试代码时,我们会使用这种技巧。我们这样做是为了理解代码、执行流程并识别代码中的错误。记录和理解计算机系统执行的系统方法是系统日志。系统日志不仅仅用于调试。它们用于更具挑战性的计算机系统相关用例。其中包括系统监控、工作流建模、性能调查和异常检测。让我们看看如何使用系统日志来了解我们的计算机系统,以及当它被用作大数据时有多强大。
什么是日志数据?
计算机系统生成由系统运行时信息组成的系统日志。通常,日志文件包含一系列日志行,代表系统中发生的不同事件。系统日志是使用描述系统当前运行时信息的单独代码段写入文件的输出。以下是阿帕奇 SLF4J 登录 Java 的方式。
LOGGER.info(“Participated to a new round of number” + roundNumber + “with rank as “ + nodeRank);
下面给出了可能的原始日志行,它将通过执行上述代码段写入日志文件。
2019–11–18 20:18:29,467 [INFO] distributedConsensus.LeaderCandidate Participated to a new round of number 13 with rank as 263
一个日志文件可能包含成百上千个类似于上面例子的日志,描述你的计算机系统的故事。这些日志行可以是错误消息、警告、调试级别消息或一般信息消息或其他类型的日志消息。为了使日志记录变得简单和一致,在 Java 编程中使用了像 SLF4J 、日志回溯这样的日志记录框架。 Log4js 和 Winston 是 Node.Js 中常用的日志记录工具,这些日志记录框架为您提供了一套 API 来实现简单、一致和健壮的日志记录体验。可以将它们配置为在日志消息中嵌入附加信息,如日期、时间、详细级别和当前运行的进程(如示例所示),而无需在日志消息中明确指定。
日志文件中的原始日志行(取自 loghub 中的 Hadoop 数据集)
原木线上的鸟瞰图
生成的日志行可能包含不同的标题字段(取决于所使用的日志框架、配置和程序员希望与日志一起存储的信息)以及用自然语言编写的日志消息,该日志消息包含程序员希望记录的系统事件的详细信息。
在上面的例子中,2019–11–18 20:18:29,467 [INFO] distributedConsensus.LeaderCandidate
可以被识别为由空格分隔的标题字段的集合。这些字段被嵌入到日志行中,因为程序员已经配置了日志框架来这样做。Participated to a new round of number 13 with rank as 263
是自然语言日志消息,包含程序员想要记录的系统事件信息。这部分的内容完全取决于程序员记录的系统事件和他们使用的(英语)语言。要获得日志数据的真正优势,重要的是日志数据中提到的信息能够被人类容易地理解。
了解日志数据
日志数据可以被视为大数据的半结构化形式之一,因为原始日志行由结构化的标题和非结构化的日志消息组成。尽管我们有大量的日志数据来描述我们系统的整个故事,但是通过阅读日志来理解日志数据是非常困难的。这主要是因为日志消息部分是由人类使用(主要)英语编写的。识别日志行的标题字段并不困难,因为日志行的这一部分具有由日志框架定义的结构。在上面的例子中,我们很容易理解,2019–11–18
是日期,20:18:29,467
是时间戳,[INFO]
是详细级别,distributedConsensus.LeaderCandidate
是执行该事件的进程或组件。
理解日志数据真正困难的部分是理解自然语言日志消息,其中包含关于所记录的系统事件的最重要信息。因此,主要关注自然语言日志消息的结构化。通过观察上面生成日志行的代码段,我们可以理解程序员记录了一个与“参与一轮”相关的事件。我们还可以看到,事件可能在不同的情况下发生,因此,代码中指定的参数值(roundNumber,nodeRank)可能会因日志行的不同而不同。这是因为它们是特定事件的动态决定信息。
同一事件的一组日志消息
在日志分析研究领域中,Participated to a new round of number ..... with rank as .....
部分被识别为日志消息的恒定部分或固定部分,因为它可以在“参与一轮”类型事件的任何日志消息事件中找到。日志消息的这个常量部分完全取决于记录系统事件的程序员所使用的文字和语言。日志消息的这个常量部分的实际用途是,它是“参与一轮”类型事件的所有日志消息的一般代表。也就是说,根据日志代码语句中给变量( roundNumber 和 nodeRank 的值)的不同,同一事件的日志消息会有所不同。因此,出现在日志代码中该变量位置的值(上例中的 13 和 263、34 和 134 …)被标识为可变部分或可变部分。大多数情况下,这些可变部分由数值组成,但并非所有情况都是如此。日志消息的可变部分携带系统的动态运行时信息,因此是非常有价值的信息源。
如果我们能够将日志文件中的日志消息的常量部分与变量部分分开会怎么样?如果可能的话,我们可以生成一个结构良好、比原始日志文件更容易理解的日志报告。这种结构化日志报告可以表示为一个表,该表具有描述日志行的标识符字段的不同列和表示每个日志行实例的行。
更易于理解的结构化日志报告
这种日志报告比原始日志文件更容易理解。这就是为什么许多技术和方法被建议从日志文件生成结构化日志报告。当前日志分析系统中使用的一些技术是频繁模式挖掘方法、日志数据聚类、捕捉日志行中模式的基于启发的方法等。关于日志结构方法的更多细节将在以后的文章中描述。
快乐阅读…!
使用主成分分析(PCA)了解您的数据,并发现潜在模式
超越描述的增强型数据探索
超越均值、分布和相关性的数据探索节省时间、资源并保持健康:利用主成分分析透视变量的表面。它节省了时间和资源,因为它在一个小时的模型训练之前发现了数据问题,并且对程序员的健康有好处,因为她用更愉快的事情来换取对数据的担忧。例如,一个经过充分验证的机器学习模型可能会失败,因为一维数据的方差不足或其他相关问题。PCA 提供了有价值的见解,使您对数据属性及其隐藏的维度充满信心。
本文展示了如何利用 PCA 来理解数据集的关键属性,从而节省时间和资源,最终带来更快乐、更充实的编码生活。我希望这篇文章有助于以一致的方式应用 PCA 并理解其结果。
乔纳森·博尔巴拍摄的照片
TL;速度三角形定位法(dead reckoning)
PCA 提供了超越描述性统计的有价值的见解,并有助于发现潜在的模式。两个 PCA 度量表示 1。有多少个分量获得了方差的最大份额(解释方差),以及 2。,哪些特性与最重要的组件相关(因子加载)。这些指标交叉检查项目工作流程中的先前步骤,例如数据收集,然后可以对其进行调整**。**作为一个快捷易用的工具,我在本笔记本或本脚本中提供了函数do_pca()
,它对准备好的数据集进行 PCA,以在几秒钟内检查其结果。
作为安全网的数据探索
当项目结构类似于下面的结构时,准备好的数据集在 4。一步一步看描述性统计。其中最常见的是所有观察值或子组的平均值、分布和相关性。
常见项目结构
- 收集:收集、检索或加载数据
- 处理:格式化原始数据,处理缺失条目
- 工程:构建和选择特征
- 探索:检查描述符、属性
- 建模:训练、验证和测试模型
- 评估:检查结果,比较模型
当经过几个小时的工作后拥有一个干净的数据集的时刻到来时,许多人已经开始关注将模型应用于数据这一激动人心的步骤。在这个阶段,如果数据没有从天上掉下来,清理和处理,大约 80–90%的项目工作量已经完成。当然,对于建模的渴望是强烈的,但是这里有两个充分的数据探索可以节省时间的原因:
- 捕捉编码错误 →修改特征工程(步骤 3)
- 识别潜在属性 →重新思考数据收集(步骤 1)、预处理(步骤 2)或特征工程(步骤 3)
在几个小时的训练、验证和测试后,担心由于潜在的数据问题而表现不佳的模型就像一个摄影师在片场,不知道他们的模型可能会是什么样子。因此,关键信息是将数据探索视为了解您的数据的机会,了解其优势和劣势。
描述性统计常常揭示编码错误。然而,检测潜在问题可能需要更多。PCA 等分解方法有助于识别这些问题,并能够修正之前的步骤。这确保了向模型构建的平稳过渡。
用 PCA 看表面之下
无论如何,大型数据集通常需要 PCA 来降低维数。这种方法可以捕捉要素间的最大可能方差,并将观测值投影到互不相关的向量(称为分量)上。尽管如此,主成分分析除了降维还有其他用途。它还有助于发现跨特性的潜在模式。
为了专注于 Python 中的实现而不是方法论,我将跳过描述 PCA 的工作方式。有许多关于它的很棒的资源,我引用它们来代替:
- 展示常设仲裁法院运作的动画:https://setosa.io/ev/principal-component-analysis/
- PCA 在一次家庭谈话中解释道:https://stats.stackexchange.com/a/140579
- 史密斯[2]。主成分分析教程:此处可访问。
两个指标对于理解数据探索的 PCA 至关重要:
1。解释方差 衡量一个模型能在多大程度上反映整个数据的方差。主成分试图捕捉尽可能多的差异,这一措施表明,在多大程度上他们可以做到这一点。它有助于看到组件按解释的方差排序,第一个得分最高,所有组件的总和最多为 1。
2。因子加载 表示一个变量与一个组件的相关程度。每个组成部分都由变量的线性组合构成,其中一些变量可能比其他变量更重要。因子载荷以相关系数的形式表示,范围从-1 到 1,并使成分可以解释。
接下来的章节将 PCA 应用于来自行为领域实验的激动人心的数据,并指导使用这些指标来增强数据探索。
负荷数据:对砂砾的随机教育干预(Alan 等人,2019 年)
iris 数据集很好地充当了几种 PCA 的典型例子。为了多样化并使用来自实地研究的新数据,我依赖于 Alan 等人[1]的复制数据。我希望这是值得赞赏的。
它包括来自土耳其学校行为实验的数据,10 岁的孩子参加了一个课程,以提高一种叫做毅力的非认知技能,这种技能被定义为坚持不懈地追求一项任务。作者对个体特征进行了采样,并进行了行为实验,以测量接受项目(grit == 1
)和参加控制治疗(grit == 0
)的人之间的潜在治疗效果。
下面的代码从一个 URL 加载数据,并将其存储为 pandas dataframe。
# To load data from Harvard Dataverse
import io
import requests# load exciting data from URL (at least something else than Iris)
url = ‘[https://dataverse.harvard.edu/api/access/datafile/3352340?gbrecs=false'](https://dataverse.harvard.edu/api/access/datafile/3352340?gbrecs=false')
s = requests.get(url).content# store as dataframe
df_raw = pd.read_csv(io.StringIO(s.decode(‘utf-8’)), sep=’\t’)
预处理和特征工程
为了使 PCA 发挥作用,数据必须是数字的、无遗漏的和标准化的。我将所有步骤放入一个函数(clean_data
)中,该函数返回一个具有标准化特征的数据帧。并执行项目工作流程的步骤 1 至 3(收集、处理和工程)。首先,导入必要的模块和包。
import pandas as pd
import numpy as np# sklearn module
from sklearn.decomposition import PCA# plots
import matplotlib.pyplot as plt
import seaborn as sns
# seaborn settings
sns.set_style("whitegrid")
sns.set_context("talk")# imports for function
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
接下来,定义clean_data()
函数。它提供了一种将原始数据转换为准备好的数据集的快捷方式,该数据集具有(I)选定的要素,(ii)漏项由列表示代替,以及(iii。)标准化变量。
*关于所选功能的说明:*我在(iv)中选择了功能。)根据他们的复制脚本,可在 Harvard Dataverse 上访问,并且仅使用样品 2(公开访问的工作文件中的“样品 B”)。为简明起见,请参考论文中的相关描述(第 30 页,表 2)。
准备数据需要一行代码(v)。
def clean_data(data, select_X=None, impute=False, std=False):
"""Returns dataframe with selected, imputed
and standardized features
Input
data: dataframe
select_X: list of feature names to be selected (string)
impute: If True impute np.nan with mean
std: If True standardize data
Return
dataframe: data with selected, imputed
and standardized features
"""
# (i.) select features
if select_X is not None:
data = data.filter(select_X, axis='columns')
print("\t>>> Selected features: {}".format(select_X))
else:
# store column names
select_X = list(data.columns)
# (ii.) impute with mean
if impute:
imp = SimpleImputer()
data = imp.fit_transform(data)
print("\t>>> Imputed missings")
# (iii.) standardize
if std:
std_scaler = StandardScaler()
data = std_scaler.fit_transform(data)
print("\t>>> Standardized data")
return pd.DataFrame(data, columns=select_X)# (iv.) select relevant features in line with Alan et al. (2019)
selected_features = ['grit', 'male', 'task_ability', 'raven', 'grit_survey1', 'belief_survey1', 'mathscore1', 'verbalscore1', 'risk', 'inconsistent']# (v.) select features, impute missings and standardize
X_std = clean_data(df_raw, selected_features, impute=True, std=True)
现在,数据已准备好供探索。
Scree 图和因子载荷:解释 PCA 结果
PCA 产生与数据探索相关的两个度量:首先,每个分量解释了多少方差(scree plot),其次,变量与分量有多少相关性(因子加载)。以下部分提供了一个实际示例,并通过 PCA 输出提供了一个解释方差的 scree 图和一个因子负荷热图。
解释方差显示变量的维数
如今,数据非常丰富,数据集的规模也在持续增长。数据科学家通常要处理数百个变量。然而,这些变量值得他们记忆吗?换句话说:一个变量捕捉独特的模式还是测量其他变量已经反映的相似属性?
主成分分析可以通过每个成分的解释方差来回答这个问题。它详细说明了观察到最大差异的基础维度的数量。
下面的代码从 sklearn 初始化一个 PCA 对象,并沿着计算出的分量(I .)转换原始数据。此后,检索关于解释的差异的信息(ii。)和印刷(iii。).
# (i.) initialize and compute pca
pca = PCA()
X_pca = pca.fit_transform(X_std)# (ii.) get basic info
n_components = len(pca.explained_variance_ratio_)
explained_variance = pca.explained_variance_ratio_
cum_explained_variance = np.cumsum(explained_variance)
idx = np.arange(n_components)+1df_explained_variance = pd.DataFrame([explained_variance, cum_explained_variance],
index=['explained variance', 'cumulative'],
columns=idx).Tmean_explained_variance = df_explained_variance.iloc[:,0].mean() # calculate mean explained variance# (iii.) Print explained variance as plain text
print('PCA Overview')
print('='*40)
print("Total: {} components".format(n_components))
print('-'*40)
print('Mean explained variance:', round(mean_explained_variance,3))
print('-'*40)
print(df_explained_variance.head(20))
print('-'*40)PCA Overview
========================================
Total: 10 components
----------------------------------------
Mean explained variance: 0.1
----------------------------------------
explained variance cumulative
1 0.265261 0.265261
2 0.122700 0.387962
3 0.113990 0.501951
4 0.099139 0.601090
5 0.094357 0.695447
6 0.083412 0.778859
7 0.063117 0.841976
8 0.056386 0.898362
9 0.052588 0.950950
10 0.049050 1.000000
----------------------------------------
**解释:**第一个成分约占解释方差的 27%。与其他数据集相比,这相对较低,但没关系。它只是表明大部分观察值(100%–27% = 73%)分布在不止一个维度上。接近输出的另一种方法是问:需要多少组件来覆盖超过 X%的方差?例如,我想降低数据的维数,并保留原始数据至少 90%的方差。那么我将不得不包括 9 个组成部分,以达到至少 90%,甚至在这种情况下有 95%的解释方差。在原始数据集中总共有 10 个变量的情况下,降低维度的范围是有限的。此外,这表明 10 个原始变量中的每一个都增加了一些独特的模式,并有限地重复了来自其他变量的信息。
再举一个例子,我列出了“the”葡萄酒数据集的解释方差:
PCA Overview: Wine dataset
========================================
Total: 13 components
----------------------------------------
Mean explained variance: 0.077
----------------------------------------
explained variance cumulative
1 0.361988 0.361988
2 0.192075 0.554063
3 0.111236 0.665300
4 0.070690 0.735990
5 0.065633 0.801623
6 0.049358 0.850981
7 0.042387 0.893368
8 0.026807 0.920175
9 0.022222 0.942397
10 0.019300 0.961697
11 0.017368 0.979066
12 0.012982 0.992048
13 0.007952 1.000000
----------------------------------------
这里,13 个成分中的 8 个足以捕获至少 90%的原始方差。因此,降维的空间更大。此外,它表明一些变量对数据中的方差贡献不大。
一个 scree plot 代替了纯文本,可视化了跨组件的解释方差,并告知每个组件的单独和累积解释方差。下一个代码块创建了这样一个 scree plot,并包括一个选项,当处理大型数据集的数百个组件时,可以将注意力集中在前 X 个组件上。
#limit plot to x PC
limit = int(input("Limit scree plot to nth component (0 for all) > "))
if limit > 0:
limit_df = limit
else:
limit_df = n_componentsdf_explained_variance_limited = df_explained_variance.iloc[:limit_df,:]#make scree plot
fig, ax1 = plt.subplots(figsize=(15,6))ax1.set_title('Explained variance across principal components', fontsize=14)
ax1.set_xlabel('Principal component', fontsize=12)
ax1.set_ylabel('Explained variance', fontsize=12)ax2 = sns.barplot(x=idx[:limit_df], y='explained variance', data=df_explained_variance_limited, palette='summer')
ax2 = ax1.twinx()
ax2.grid(False)ax2.set_ylabel('Cumulative', fontsize=14)
ax2 = sns.lineplot(x=idx[:limit_df]-1, y='cumulative', data=df_explained_variance_limited, color='#fc8d59')ax1.axhline(mean_explained_variance, ls='--', color='#fc8d59') #plot mean
ax1.text(-.8, mean_explained_variance+(mean_explained_variance*.05), "average", color='#fc8d59', fontsize=14) #label y axismax_y1 = max(df_explained_variance_limited.iloc[:,0])
max_y2 = max(df_explained_variance_limited.iloc[:,1])
ax1.set(ylim=(0, max_y1+max_y1*.1))
ax2.set(ylim=(0, max_y2+max_y2*.1))plt.show()
scree 图可能显示从一个组件到另一个组件的明显跳跃。例如,当第一个组成部分比其他部分不成比例地获得更多的方差时,这可能是一个迹象,表明变量提供了相同的潜在因素,或者没有增加额外的维度,但从稍微不同的角度说了相同的事情。
为了给出一个直接的例子,并感受一下跳跃看起来有多明显,我提供了波士顿房价数据集的 scree 图:
PCA 节省时间的两个原因
假设你有数百个变量,应用主成分分析,你会发现前几个成分已经包含了大部分解释的方差。这可能暗示底层维度的数量比变量的数量少得多。最有可能的是,减少几百个变量导致培训、验证和测试的性能提升**。与等待模型本身发现几个变量背后缺乏方差相比,选择一个合适的模型并对其进行改进会有更多的时间。**
除此之外,想象数据是由自己构建的,例如通过 web 抓取,并且抓取器从网页中提取预先指定的信息。在这种情况下,检索到的信息可能是一维的,此时 scraper 的开发人员脑子里只有几个相关的项目,但却忘记了包括揭示问题设置的其他方面的项目。在这个阶段,回到工作流程的第一步并调整数据收集可能是值得的。
发现特性和组件之间相互关联的潜在因素
除了解释方差之外,PCA 还提供了另一个有价值的统计量:每个主成分和一个变量之间的相关性,也称为因子负载。这种统计有助于掌握组件背后的维度。例如,数据集包括关于个人的信息,如数学成绩、反应时间和保持时间。最重要的维度是认知技能,与这些变量密切相关的部分可以被解释为认知技能维度。同样,当数据具有自信、耐心或责任心等特征时,另一个维度可能是非认知技能和个性。捕捉该区域的组件与那些特征高度相关。
以下代码创建了一个热图来检查这些相关性,也称为因素加载矩阵。
# adjust y-axis size dynamically
size_yaxis = round(X_std.shape[1] * 0.5)
fig, ax = plt.subplots(figsize=(8,size_yaxis))# plot the first top_pc components
top_pc = 3
sns.heatmap(df_c.iloc[:,:top_pc], annot=True, cmap="YlGnBu", ax=ax)
plt.show()
第一个因素与任务能力、推理分数( raven )、数学分数、语言分数强烈负相关,并与坚韧不拔的信念( grit_survey1 )积极相关。将此归纳为一个共同的潜在因素是主观的,需要领域知识。在我看来,第一个组成部分主要是捕捉认知技能。
第二个因素与接受治疗(勇气)、性别(男性)负相关,与不一致正相关。解释这个维度不那么清晰,也更具挑战性。然而,它解释了 12%的变异,而不是像第一个组成部分那样解释了 27%,这导致了更难解释的维度,因为它稍微跨越了几个主题领域。接下来的所有组件可能同样难以解释。
变量获取相似维度的证据可能是均匀分布的因子载荷。启发我写这篇文章的一个例子是关于我的项目的,在那里我依靠谷歌趋势数据和关于公司可持续性的自建关键词。第一个主成分的第 15 个最高因子载荷列表显示载荷范围从最高值 0.12 到最低值 0.11。因子载荷的这种均匀分布可能是一个问题。当数据是自己收集的,并且有人预先选择了要收集的内容时,这一点尤其适用。调整此选择可能会增加数据的维数,最终可能会提高模型性能。
PCA 节省时间的另一个原因是
如果数据是自行构建的,则因子加载显示每个特征如何影响基础维度,这有助于提出关于数据收集的 额外观点 以及哪些特征或维度可以增加有价值的差异。不是盲目猜测要添加哪些功能,因素加载导致 明智的数据收集决策 。它们甚至可能是寻找更高级功能的灵感。
结论
总之,PCA 是数据探索工具箱中的一个灵活工具。其主要目的是降低大型数据集的复杂性。但它也有助于观察变量的表面,发现潜在的维度,并将变量与这些维度联系起来,使它们变得可以解释。要考虑的关键指标是解释方差和因子加载。
本文展示了如何利用这些指标进行超出平均值、分布和相关性的数据探索,并建立对数据底层属性的理解。识别跨变量的模式对于重新思考项目工作流程中的先前步骤是有价值的,例如数据收集、处理或特征工程。
感谢阅读!我希望你会发现它和我写这个指南一样有用。我很想知道你对这件事的想法。如果您有任何反馈,我非常感谢您的反馈,并期待收到您的消息。
附录
访问 Jupyter 笔记本
我使用do_pca()
将主成分分析应用于更典型的数据集,如波士顿房地产市场、葡萄酒和虹膜。它展示了小数据集的 PCA 输出。随意下载我的笔记本或者剧本。
关于因子分析与主成分分析的说明
这里有一个经验法则陈述:如果你想把你的相关观察变量减少到一个更小的不相关变量集,使用主成分分析来测试观察变量的潜在因素模型。
尽管这种区分在科学上是正确的,但在实际应用中却变得不那么相关了。PCA 与因子分析密切相关,因子分析通常会得出与我们所关心的数据属性相似的结论。因此,对于数据探索,区别可以放宽。本帖给出了一个应用环境中的示例,另一个带有因子分析实际操作代码的示例附在笔记本中。
最后,对于那些对因子分析和 PCA 之间的差异感兴趣的人,请参考这篇文章。请注意,在整篇文章中,为了准确起见,我从未使用过术语潜在因素。
参考
[1]艾伦,s .,博内瓦,t .,&厄塔克,S. (2019)。曾经失败,再试一次,更成功:一项关于毅力的随机教育干预的结果。经济学季刊,134(3),1121–1162。
[2]史密斯,L. I. (2002 年)。主成分分析教程。
理解 zip()—Python 中隐藏的瑰宝
有效地合并未知数量的列表
由 Fabrizio Verrecchia 在 Unsplash 上拍摄的照片
在 Python 中,有几种方法可以合并 2 个列表/元组/集/字典。我所说的“合并”不仅仅是将一个元素附加到另一个元素上,而是将具有相同索引的元素分组。如果你在面试中,面试官要求你实现以下逻辑,你的第一个“蛮力”回答会是什么?
作者:高
大概你会想出这样的东西。程序遍历city
的长度,每次都从具有相同索引的country
和city
中获取值,然后将它们放入一个元组中。这绝对是一个可行的解决方案,但不是你的面试官所期待的。
稍微改进一下,可以用enumerate
迭代city
。enumerate
返回列表中每个元素的索引和值。所以我们可以用ci
代替city[i]
。但这仍然不是理想的答案。
zip()内置函数
Python 有许多隐藏的宝石,而zip()
就是其中之一。它能够以更干净的方式解决同样的问题。我们都喜欢干净的代码,不是吗?
根据官方 Python 文档,
zip(*iterables)
创建了一个迭代器,它聚集了来自每个迭代器的元素。
向拆包操作员重述(*)
单个星号(*)意味着它解包迭代器。例如,如果您有两个列表list1=["a1","a2"]
和list2=["b1","b2"]
,您不必创建一个新的参数列表并将其传递给类似zip([list1,list2])
的函数,相反,您可以只做zip(list1, list2)
。
当您不知道函数的参数个数时,可以使用解包运算符(*)。比如你可以计算一个未知数量的自变量之和。
另一个拆包操作符是(**)。两个星号代表字典。这意味着每个参数必须有一个键,这就是为什么你通常把**kwargs
(关键字参数)作为输入名。明确一下,你可以给它取任何名字,比如**nums
。
如果你想了解更多关于*args
和**kwargs
的知识,我推荐你阅读 Python args 和 kwargs:来自真实 Python 的去神秘化 。
[## Python 参数和 kwargs:去神秘化——真正的 Python
在这个循序渐进的教程中,您将学习如何在 Python 中使用 args 和 kwargs 来为您的…
realpython.com](https://realpython.com/python-kwargs-and-args/)
**zip()**
vs**zip_longest()**
再来说说zip()
。我们知道zip(*iterables)
的输入是一些迭代器。迭代器可以是一个字符串、列表、*元组、集合或字典。*在内部,zip()
对所有迭代器进行多轮循环。在每一轮中,它对每个迭代器调用next()
函数,并将值放入一个元组中,在这一轮结束时产生元组。然后继续下一轮。如果任何迭代器用尽,循环将结束。也许看代码更容易。Python 文档给出了下面的代码来帮助读者理解zip()
是如何工作的。
zip_implementation.py (来源: python doc )
那我们用zip()
来解决前面的问题吧。每次都会产生一个元组。使用 yield 的一个优点是节省 RAM 的使用,因为我们不需要将整个结果序列存储在内存中。
默认的zip()
遵循坎尼金定律,这意味着合并结果的长度取决于最短的输入迭代。这就是为什么在前面的例子中,您在输出中看不到国家X
。
*康尼金定律(*来源:博客)
但是,如果希望合并后的结果与最长的输入迭代器对齐,可以使用[itertools.zip_longest](https://docs.python.org/3.3/library/itertools.html#itertools.zip_longest)
。在这种情况下,缺少的值将由用户定义的fillvalue
填充。
拉开
既然我们可以“压缩”2 个迭代器,我们也必须能够“解压缩”它。逻辑是unzipped = zip(*zip(*iterables))
。在示例代码中,zipped
和unzipped
对象都有类zip
,它们是不可读的。我们可以将它们转换成一个列表或一个元组。
边缘案例
对输入迭代器的数量没有限制。它可以是 0、1 或更多。拥有 0 个或 1 个输入迭代器的情况并不常见,但这仍然是可能的。
字典中的 **zip()**
以下代码的结果会是什么?在进一步阅读之前思考一下。
答案是("city", "city", "city")
和(“country”, “country”, “country”)
。为什么我们只有钥匙?
根据我们之前看到的,在内部zip()
对每个输入参数执行iter()
。所以问题是iter(nl)
的产量是多少?如果我们执行print(iter(nl))
,我们将得到<dict_keyiterator object at 0x10e1e3f50>
。这就是为什么我们在结果中只接收键。
更好的方法是使用nl.items()
作为输入。在这种情况下,我们能够在循环中接收键和值。
中的混合型**zip()**
迭代器的类型也没有限制。在下面的代码中,我们使用混合类型作为输入参数。结果会怎样?想一想。这个例子只是出于教育目的。它不应该是你见过的最好的代码。🙂
好吧,答案是:
('w', 'Amsterdam', ('key1', 'Netherlands'))
('o', 'Berlin', ('key2', 'Germany'))
('r', 'Chongqing', ('key3', 'China'))
每个ele
是来自 3 个输入参数的 3 个元素的元组。string
的长度应该考虑为 5,而不是 1。每个循环将返回 1 个字符。程序总共循环 3 次,因为输入的最短长度是 3。
如果你能回答这个问题并解释背后的原因,那么你已经掌握了这一章。
结论
zip()
给了我们用干净的代码合并未知数量的迭代器的便利。当我们有许多大小相同的迭代器时,效果最好。zip()
创建一个生成器,因此它能够处理长输入并节省 RAM 的使用。
同时,我们应该注意zip()
的一些行为:
zip()
遵循康尼金定律。它不断产生元组,直到所有迭代器都用完为止。如果迭代器的长度不匹配,它不会抛出异常。itertools.zip_longest
通过用用户自定义的fillvalue
填充缺失值,解决了“长度”问题。- 使用
zip()
中的str
和dict
时要小心。你需要很好地理解iter
如何与str
和dict
一起工作。
我希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。
参考
[## 2.内置函数- Python 3.3.7 文档
Python 解释器内置了许多始终可用的函数和类型。它们被列出…
docs.python.org](https://docs.python.org/3.3/library/functions.html#zip) [## Python zip 示例|用于迭代的 Python zip()函数
Python zip()是一个返回 zip 对象的内置函数,该对象是元组的迭代器,其中第一项…
appdividend.com](https://appdividend.com/2019/04/09/python-zip-example-python-zip-function-tutorial/)
了解 8 种交叉验证
交叉验证及其类型的深入解释
交叉验证也被称为抽样外技术是数据科学项目的一个基本要素。这是一个重采样过程,用于评估机器学习模型,并评估该模型在独立测试数据集上的表现。
在这篇文章中,你可以读到 8 种不同的交叉验证技术,它们各有利弊,如下所列:
- 留 p 不交叉验证
- 漏项交叉验证
- 维持交叉验证
- 重复随机子采样验证
- k 倍交叉验证
- 分层 k 倍交叉验证
- 时间序列交叉验证
- 嵌套交叉验证
在讨论交叉验证技术之前,让我们了解一下为什么要在数据科学项目中使用交叉验证。
为什么交叉验证很重要?
我们经常将数据集随机分为训练数据和测试数据,以开发机器学习模型。训练数据用于训练 ML 模型,并且在独立的测试数据上测试相同的模型,以评估模型的性能。
随着分裂的随机状态的改变,模型的精度也改变,因此我们不能实现模型的固定精度。测试数据应该独立于训练数据,这样就不会发生数据泄漏。在使用训练数据开发 ML 模型期间,需要评估模型性能。这就是交叉验证数据的重要性。
数据需要分成:
- **训练数据:**用于模型开发
- **验证数据:**用于验证同一型号的性能
(图片由作者提供),验证分割
简单来说,交叉验证使我们能够更好地利用我们的数据。你可以进一步阅读、工作和实施 7 种类型的交叉验证技术。
1.留下 p-out 交叉验证:
保留 p-out 交叉验证(LpOCV)是一种详尽的交叉验证技术,它使用 p-观察值作为验证数据,其余数据用于训练模型。以各种方式重复这一过程,以在一组 p 观察值和一组训练集上切割原始样本。
p=2 的 LpOCV 的一种变体称为留对交叉验证,已被推荐为一种近乎无偏的方法,用于估计二元分类器的 ROC 曲线下的面积。
2.留一交叉验证:
留一法交叉验证(LOOCV)是一种详尽的交叉验证技术。这是一个 p=1 的 LpOCV 范畴。
(来源),LOOCV 运营部
对于具有 n 行的数据集,选择第一行进行验证,其余(n-1)行用于训练模型。对于下一次迭代,选择第 2 行进行验证,其余的用于训练模型。类似地,重复该过程,直到 n 个步骤或期望的操作次数。
以上两种交叉验证技术都是穷举交叉验证的类型。穷举交叉验证方法是以所有可能的方式学习和测试的交叉验证方法。它们具有相同的优点和缺点,讨论如下:
优点:
- 简单、易于理解和实施。
缺点:
- 该模型可能导致较低的偏差。
- 所需的计算时间很长。
3.维持交叉验证:
维持技术是一种彻底的交叉验证方法,它根据数据分析将数据集随机拆分为定型数据和测试数据。
(图片由作者提供),70:30 将数据分别拆分为训练数据和验证数据
在维持交叉验证的情况下,数据集被随机分为定型数据和验证数据。一般来说,训练数据的分裂多于测试数据。训练数据用于归纳模型,验证数据用于评估模型的性能。
用于训练模型的数据越多,模型就越好。对于维持交叉验证方法,大量数据是从训练中分离出来的。
优点:
- 和以前一样。
缺点:
- 不适合不平衡的数据集。
- 许多数据是从训练模型中分离出来的。
4.k 倍交叉验证:
在 k 折叠交叉验证中,原始数据集被均等地划分成 k 个子部分或折叠。对于每次迭代,从 k 个折叠或组中选择一个组作为验证数据,并且选择剩余的(k-1)个组作为训练数据。
(来源),k 倍交叉验证
该过程重复 k 次,直到每组被视为有效,并作为训练数据保留。
(图片由作者提供),k 倍交叉验证
通过取 k-模型验证数据的平均精度来计算模型的最终精度。
LOOCV 是 k 倍交叉验证的变体,其中 k=n
优点:
- 该模型具有较低的偏差
- 低时间复杂度
- 整个数据集用于训练和验证。
缺点:
- 不适合不平衡的数据集。
5.重复随机子采样验证:
重复随机子采样验证也称为蒙特卡罗交叉验证,它将数据集随机分为训练和验证。不太可能将数据集 k 倍交叉验证拆分为非分组或折叠,但在这种情况下是随机拆分。
迭代的次数不是固定的,而是由分析决定的。然后对分割的结果进行平均。
(图片由作者提供),重复随机子采样验证
优点:
- 训练和验证分割的比例不依赖于迭代或分区的数量。
缺点:
- 一些样本可能不会被选择用于训练或验证。
- 不适合不平衡的数据集。
6.分层 k 倍交叉验证:
对于上面讨论的所有交叉验证技术,它们可能不适用于不平衡的数据集。分层 k-fold 交叉验证解决了不平衡数据集的问题。
在分层 k 折叠交叉验证中,数据集被划分成 k 个组或折叠,使得验证数据具有相同数量的目标类标签实例。这确保了一个特定的类不会过度出现在验证或训练数据中,尤其是当数据集不平衡时。
(图片由作者提供),分层 k-fold 交叉验证,每个 fold 都有相同的目标类实例
通过取每个折叠得分的平均值来计算最终得分。
优点:
- 适用于不平衡的数据集。
缺点:
- 现在适用于时间序列数据集。
7.时间序列交叉验证:
数据的顺序对于时间序列相关问题非常重要。对于时间相关数据集,将数据随机分割或 k 倍分割成训练和验证可能不会产生好的结果。
对于时间序列数据集,根据时间将数据拆分为训练和验证,也称为正向链接法或滚动交叉验证。对于特定的迭代,训练数据的下一个实例可以被视为验证数据。
(图片由作者提供),时间序列交叉验证
如上图所示,对于第一次迭代,前 3 行被视为训练数据,下一个实例 T4 是验证数据。训练和验证数据选择机会被转发用于进一步的迭代。
8.嵌套交叉验证:
在 k-fold 和分层 k-fold 交叉验证的情况下,我们在训练和测试数据中得到一个差的误差估计。在早期的方法中,超参数调整是单独进行的。当交叉验证同时用于调整超参数和推广误差估计时,需要嵌套交叉验证。
嵌套交叉验证可适用于 k 倍和分层 k 倍变异。阅读下面的文章,了解更多关于嵌套交叉验证及其实现的信息:
[## 使用 Python 进行机器学习的嵌套交叉验证——机器学习掌握
k-fold 交叉验证程序用于评估机器学习模型的性能。
machinelearningmastery.com](https://machinelearningmastery.com/nested-cross-validation-for-machine-learning-with-python/)
结论:
交叉验证用于比较和评估 ML 模型的性能。在本文中,我们讨论了 8 种交叉验证技术及其优缺点。k-fold 和分层 k-fold 交叉验证是最常用的技术。时间序列交叉验证最适用于与时间序列相关的问题。
这些交叉验证的实现可以在 sklearn 包中找到。阅读该 sklearn 文档了解更多详情。
参考资料:
[1]维基百科:https://en . Wikipedia . org/wiki/Cross-validation _(statistics)
感谢您的阅读
通过 R 中的划痕编码的神经网络;新手向导
动手香草建模第二部分
作者图片
如果你曾经深入研究过数据科学的世界,那么我认为你现在肯定在探索机器学习和人工智能或一般数据科学的旅程中的某个时候,在某个地方遇到过术语 【神经网络 ,这并不荒谬。
图一。只有一个隐藏层的神经网络(图片由作者提供)
最被认可的神经网络(NN)定义是*它是一种受大脑启发的计算机架构,包含各种功能的网络拓扑,其中节点以特定的方式相互连接,通过迭代地遵循一系列算法来揭示数据集中的潜在模式(图 1)。*也许,只要有一点数学上的成熟和一点最优化理论的知识,把神经网络称为函数逼近器或回归器就简单得犯罪了。从我以前的文章中,我们通过一个实际的例子看到了 Keras 和 Tensorflow 等高级 API 如何使构建和训练神经网络变得非常简单。然而,Keras 和 Tesnsorflow 所提供的简单性困扰着一个具有顽强依赖性的初学者,并使新手真正难以理解 学习 (前馈-反向传播)过程背后的实际底层数学和推理。从我的文章中,你可能会发现我是一个非常视觉化的人,当谈到学习时,我认为引人入胜的视觉效果有助于更深层次的理解。在这里,我将尝试稍微揭开这个黑匣子,我们将温和地深入到包含梯度下降优化器的神经网络的数学形式中,并全面尝试构建我们自己的网络。如果你对初等线性代数和微分方程不太熟悉,不要担心,因为视觉效果足以让这个概念在你的大脑控制台中滚动。同样,如果你对这些数学前提感到满意,那么你一定会意识到这个模型是多么的夸张。
现在,我们开始吧!!
神经网络 架构
带有一个隐藏层的简单神经网络架构如图 2 所示。第一层叫做 输入层 。数据集的每个特征充当单个 神经元/节点 (红色)。给定一组 权重 ,这些节点的线性组合连同*(蓝色)生成下一个顺向层的单个节点,称为 隐藏层 (黄色)。注意,输入层的 iᵗʰ 节点在形成隐藏层值 hⱼ 的 jᵗʰ 节点中的贡献是 iᵗʰ节点值 Xᵢ 和 jᵗʰ权重元组即 wⱼᵢ.的 iᵗʰ元素的乘积然后对隐藏层和输出层重复类似的方案。*
图二。阐释神经网络(图片由作者提供)
在这一点上,我建议您尝试将权重元组可视化为列向量,组合形成一个矩阵 W 行等于当前层中的节点数,列等于后续层中的节点数。因此,对于动画中的给定网络,对应于输入层和隐藏层的权重矩阵 W 将为:
这种对权重的矩阵理解对于理解即将到来的基础数学很重要。
因为我们都知道神经网络的概念化是受大脑功能的启发,在这一点上,提到脑细胞/神经元的突触末端及其与神经网络的类比是很重要的。由上述操作产生的每个神经元值都类似于脑细胞携带的刺激,并且应该将其传递给下一个连接的神经元。轴突的突触末端有一种机制,决定刺激是否足够强大,可以传递到下一个神经元的树突。类似地,该激活由 NN 中的激活功能执行。因此,准确地说,每个生成的节点值都通过一个激活函数传递,该函数决定是否传递激励以及传递多少激励。
图 3。范围从-inf 到+inf 的 sigmoid 函数(图片由作者提供)
最常用的激活函数有 Sigmoid 、 ReLU、Tanh、和 Softmax (广义的 Sigmoid)。虽然 ReLU 函数只允许传递正值,但是 sigmoid 激活函数, S(x) 唯一地将任意实数 x 映射到范围(0,1)中图 3。数学上,s(x)=1/(1+eˣ)。有趣的是,*的微分即 dS(x)/dx 等于 S(x)(1-S(x)) 。这在计算梯度时带来了极大的数学便利,也是为什么 sigmoid 是最著名的激活函数的原因。这个从一层到另一层的信息转换过程叫做前馈。*
一旦估计了最终输出层,则使用误差指数函数 P 来评估性能。数学上,P =lld—zll/2, 其中 d 是实际/期望输出, z 是估计输出。优化目标是最小化 P. 实践中有许多优化器,如牛顿下降法、ADAM 等。这里我们将重点介绍最流行、应用最广泛的梯度下降法。**
既然 P 是 z 的函数,而后者又是权重Wb .所以要尽量减少 P、 权重、的变化**
图 4(作者图片)
现在让我们看看如何计算这些所需的梯度。因此,如果我们用图 2 中的实例映射这个前奏,这个过程看起来就像下图(图 5)。注意,为了计算性能指数相对于权重的梯度,即 dP/dW ₁和 dP/dW ₂,我们从过程的右端开始应用链式法则,向后传播。(回想一下,性能指标是P =lld—zll*/2。*** )。这个过程被称为**反向传播。****
图 5。反向传播中的链式法则(图片由作者提供)
梯度▽p =σdpᵢ*****/dwᵢ*对于 i = 1,2,3…,k+1 其中 k 为建筑深度。一旦 ▽ P 被评估,权重和偏差分别升级为:
W _ new = W _ current—【η(【P】b _ new = b _ current—【η(【P)****
其中值 η 为学习率。这两个方程非常重要,我们会经常用到它们。有了这些笔记,我们现在将一步一步地制作我们的模型
逐步建模
为了建模一个简单的基于神经网络的分类模型,我将使用 Iris 数据集,该数据集具有四个特征和一个包含三个不同类别的标签向量。我们的模型将包含两个大小为 4 和 3 神经元的隐藏层。该模型如图 6 所示。
图 6。我们的模型(图片由作者提供)
一个热编码
一个热编码,这是类编码的标签被移除并且为每个唯一标签值添加新的二进制变量{0,1}的地方。
*set.seed(123)
data<-as.matrix(iris) #iris datasetclasses<-unique(data[,5])# retreive unique classes###########ONE HOT ENCODING###########
#Initializing an empty matrix
hot_encode<-matrix(0, ncol=length(unique(classes)), byrow = T,
nrow= nrow(data),
dimnames = list(NULL, c(unique(classes))))#Imputing 1 at respective classfor(i in 1:nrow(data)){
for(j in 1:3){
if(data[i,5] == classes[j]){hot_encode[i,j]<-1}
else next
}
}# Combining the data and encoded labels
data<-as.matrix(cbind(iris[,1:4], hot_encode))
data<-data[sample(1:nrow(data)), ]set.seed(123)
seq<-sample(1:nrow(data)) # preserving the shuffle orderhead(data)*
定义功能
我们将需要一个 Sigmoid 激活函数**【S(x)】及其导数函数【S(x)【1-S(x)】**。此外,我们需要规范化的输入数据,即我们的虹膜数据集的特征列。
*############ Sigmoid Activation Function######
activation<-function(x){
y<- 1/(1+exp(-x)) #Sigmaod function
return(y)
}###########Derivative of Sigmoid Function#####
derivative_activation<-function(x){
y<-x*(1-x) #derivative of sigmoid function
return(y)
}###########Normalization Function############
normalize<-function(x){
for(i in 1:ncol(x)){
x[,i]<-(max(x[,i])-x[,i])/(max(x[,i])-min(x[,i]))
}
return(x)
}#Normalizing the input before feeding to an NN is a good practice###
data[, 1:4]<-normalize(data[, 1:4])*
定义模型的结构并初始化权重矩阵
*neuron_input<- 4 #Define number of neurons in Input layer
neuron_layer1<- 4 #Define number of neurons in first hidden layer
neuron_layer2<- 3 #Define number of neurons in second hidden layer
neuron_output<- 3 #Define number of neurons in the output layer#initalizing weight W1 and bias b1
set_weight_1<-matrix(runif(4*4, 0,1),
ncol= neuron_layer1, nrow= neuron_input)
bias_1<-runif(neuron_layer1, 0,1)#initalizing weight W2 and bias b2
set_weight_2<-matrix(runif(4*3, 0,1),
ncol= neuron_layer2, nrow= neuron_layer1)
bias_2<-runif(neuron_layer2, 0,1)#initalizing weight W3 and bias b3
set_weight_3<-matrix(runif(3*3, 0,1),
ncol= neuron_output, nrow= neuron_layer2)
bias_3<-runif(neuron_output, 0,1)################# TRAINING SET #################
input_layer<-data[1:120, 1:4]
label<-data[1:120, 5:7]################# TEST SET #####################
test<-data[121:150, 1:4]
test_label<-as.integer(iris$Species[seq[121:150]])#--------------------------------------------------------#
lr=0.1 # Learning Rate
er<-NA # The performance function value
itr<-1 # Iteration/epoch
accuracy<-NA #Training Accuracy
t.accuracy<-NA #Test Accuracy
loss<-NA #loss vector containing the error value at current epoch*
模型拟合
从这里开始,逐步实现模型拟合。参考图 7,它通过仅显示前 10 个实例来说明梯度是如何计算的。
*while(itr <= 5000){
print(paste("epoch =", itr)) #print the current epoch
itr<-itr+1 #Update the iteration number
###############FORWARD FEED##################################
#-----------------------STEP 1-----------------------------#
hidden_layer_1<-t(t(input_layer %*% set_weight_1) + bias_1)
activated_hidden_layer_1<-activation(hidden_layer_1)
#-----------------------STEP 2-----------------------------#
hidden_layer_2<-t(t(activated_hidden_layer_1 %*% set_weight_2) + bias_2)
activated_hidden_layer_2<-activation(hidden_layer_2)
#-----------------------STEP3------------------------------#
final_layer<-activation(t(t(activated_hidden_layer_2 %*% set_weight_3) + bias_3))
#-----------------------STEP4------------------------------#
er<-sum(((label-final_layer)^2)/2)/120
error<- -(label-final_layer)
loss[itr]<-er
###################BACKPROPOGATION#################################
#-------------------------STEP5-----------------------------#
derivation_final_layer<-derivative_activation(final_layer)
delta_final_layer<- derivation_final_layer * error
#-------------------------STEP6-----------------------------#
derivative_hidden_layer_2<-derivative_activation(activated_hidden_layer_2)
error_layer_2<-delta_final_layer%*%t(set_weight_3)
delta_layer_2<- derivative_hidden_layer_2 * error_layer_2
#-------------------------STEP7------------------------------#
derivative_hidden_layer_1<-derivative_activation(activated_hidden_layer_1)
error_layer_1<- delta_layer_2 %*% t(set_weight_2)
delta_layer_1<- derivative_hidden_layer_1 * error_layer_1
#####################UPDATE##################################
#-------------------------STEP8-----------------------------#
set_weight_3 <-set_weight_3 -
lr*t(activated_hidden_layer_2)%*%delta_final_layer
#---------------------------STEP9--------------------------#
set_weight_2 <-set_weight_2 -
lr*t(activated_hidden_layer_1)%*%delta_layer_2
#--------------------------STEP10--------------------------#
set_weight_1 <-set_weight_1 -
lr*t(input_layer)%*%delta_layer_1
#--------------------------STEP11--------------------------#
bias_3 <- bias_3 - lr* colSums(delta_final_layer)
bias_2 <- bias_2 - lr* colSums(delta_layer_2)
bias_1 <- bias_1 - lr* colSums(delta_layer_1)
#---Storing the accuracy acheived at current epoch-------------#
prediction<-NA
for(i in 1:nrow(final_layer)){
# Among the three values the maximum value indicates the
# instance's corrsponding class
prediction[i]<-(which(final_layer[i,]== max(final_layer[i,])))
}
actual<-as.integer(iris$Species[seq[1:120]])
result<-table(prediction, actual) #Confusion Matrix
accuracy[itr]<- sum(diag(result))/sum(result)
}#--------------------------------------------------------------#
#--------------------------------------------------------------#
#--Prediction function to classify the future test sets--------#predict<-function(test, label){
#Dont Worry, this is just a single forwardfeed using the final #learned weights t.hidden_layer_1<-t(t(test %*% set_weight_1) + bias_1)
t.activated_hidden_layer_1<-activation(t.hidden_layer_1) t.hidden_layer_2<-t(t(t.activated_hidden_layer_1 %*% set_weight_2) + bias_2)
t.activated_hidden_layer_2<-activation(t.hidden_layer_2)
t.final_layer<-activation(t(t(t.activated_hidden_layer_2 %*% set_weight_3) + bias_3)) t.prediction<-NA
for(i in 1:nrow(t.final_layer)){
t.prediction[i]<-(which(t.final_layer[i,]== max(t.final_layer[i,])))
}
t.actual<-label
t.result<-table(t.prediction, t.actual)
colnames(t.result)<-unique(iris$Species)
t.accuracy<- sum(diag(t.result))/sum(t.result)
result<-list(t.result, t.accuracy)
names(result)<- c("Confusion Matrix", "Result")
return(result)
}*
图 7。前馈-反馈传播(图片由作者提供)
是时候测试我们的模型了
*predict(test,test_label)*
对于我们的玩具模型来说,性能相当不错:)。这个模型只给出了一个错误的预测。测试集上的准确率为 0.966。
我们还可以绘制损耗曲线和精度曲线。你可以在这里得到完整的代码。
图 8。训练损失 vs 我们模型的训练精度*(作者图片)***
唷…
我希望这个神经网络的快速概述能帮助你理解这个非常革命性的算法。我总是乐于接受任何有助于提高本文质量的建议,所以如果你遇到任何错误,请告诉我。谢谢你的阅读和快乐的 R-ing。
其他著名读物
虽然互联网上充斥着关于神经网络的信息,但我仍然会参考一些非常简明扼要的文章。
用代码理解 acgan[py torch]
Python-PyTorch
使用 PyTorch 库和 Python 构建 ACGAN 模型并了解更多信息
法比安·格罗斯在 Unsplash 上拍摄的照片
ACGAN 代表辅助分类器生成对抗网络。该网络由谷歌大脑的一组研究人员开发,并在澳大利亚悉尼举行的第 34 届国际机器学习会议上展示。本文简要描述了论文中所阐述的研究工作以及使用 PyTorch 实现的研究工作。
为什么是 ACGAN?
ACGAN 是一种特殊的 GAN,可以在图像合成方面创造奇迹。你告诉它类别标签,它就能生成图像——全部来自完全噪声!ACGAN 的另一个重要特点是,与以前的方法相比,它生成的图像分辨率相当高。但是任何图像都可以用双线性插值来调整大小,并且它的大小可以增加。没错,它们可以,但它们仍然是低分辨率图像的模糊版本,不会比它们更难以分辨。ACGAN 是第一个提出使用预先训练的初始网络来检查图像可辨性的想法。
[注意—此处构建的 PyTorch 模型是 ACGAN 论文的实现,仅包含生成器和鉴别器。自己动手对照预先训练好的初始网络来检查模型。请在评论中告诉我结果:)
体系结构
与任何 GAN 网络一样,ACGAN 由一个发生器和一个鉴别器组成。然而,在 ACGAN 中,除了噪声 z 之外,每个生成的样本都有相应的类别标签 c ~ C (可用类别),该类别标签帮助模型基于传递的标签来合成图像。生成器 G 使用类别标签和噪声 z 来生成图像。生成的图像可以表示为—
生成的图像( 假 因为它必须被鉴别器标记为假)
发电机架构非常简单。它由一系列反卷积层组成,也称为转置卷积层。迷惑?让我来给你解释一下—
转置卷积层与卷积层相同,但在原始输入中添加了填充。因此,当应用步长为 1 且无填充的卷积时,输出的高度和宽度大于输入的高度和宽度。步长为 2 时,我们可以对转置卷积层执行上采样,就像对步长为 2 的卷积层执行下采样一样。
发生器的转置卷积层由 ReLU 非线性支持。
鉴别器包含一组具有 leaky-ReLU 非线性的 2d 卷积模块,后面是线性层,以及用于其每个输出的 softmax 和 sigmoid 函数,用于检测模型的类别和来源。整个模型可以画为—
ACGAN 模型
既然我们已经通过它们的架构模型定义了发生器和鉴别器,我们将得到损失函数。
损失函数
ACGAN 的损失函数分为两部分
- 被检查源的对数可能性—
源损耗
2.被检查类别的对数可能性—
阶级损失
从上面的损失函数可以明显看出,发生器和鉴别器在这个损失函数上“斗争”。生成器和鉴别器都试图最大化类损失。然而,源损耗是一个极小极大问题。发生器试图最小化源损耗并欺骗鉴别器。另一方面,鉴别器试图最大化源损耗,并试图阻止发电机占上风。
模特[PyTorch]
由于我们已经完成了对 ACGAN 论文的分析,现在我们将使用 CIFAR10 数据集构建模型。你可以从这里下载数据集。事实上,我会将下载合并到培训本身,以摆脱麻烦!该数据集包含 60,000 张 32x32 尺寸的图像。有 10 个类,每个类有 6000 个图像。
发电机,写成一个模块—
ACGAN 的发生器模块
注意,在生成器中,卷积网络已经仔细选择了参数,使得输出张量与来自训练集的张量具有相同的维数。这是必要的,因为两者都进入鉴别器进行评估
鉴别器,也写成一个模块—
鉴别器模型
现在,让我们开始训练!
为了进行训练,我将 epochs 的数量设置为 100。学习率设置为 0.0002,批量设置为 100。
理想情况下,为了正确的图像合成,历元的数量应该更多。例如,我已经将它设置为 100
培训模式
结果!
让我们来看看这个小实验的结果
第一个时期的图像(噪声)
第一历元图像
上一个时代的图像—
第 100 代图像
很大的进步,是吧?甘斯的妙处在于,你可以通过影像看到模特的训练。随着模型慢慢了解分布情况,您可以看到跨时代的结构正在形成!你还在等什么?为模型编写自己的代码;用你自己的方式解决问题。如果可以的话,即兴创作解决方案——我们会看看是否能以你的名字命名;)
如果你卡住了,请在评论中告诉我!来帮忙了:)
查看我的博客以获得更快的更新,不要忘记订阅更多高质量的内容:D
https://www.theconvolvedblog.vision/
了解用于决策树的 AdaBoost
用 R 实现
决策树是流行的机器学习算法,用于回归和分类任务。它们之所以受欢迎,主要是因为它们的可解释性和可表达性,因为它们模仿了人脑做出决策的方式。
在我以前的文章中,我已经介绍了一些决策树的集成方法,其目的是将一堆弱分类器转换成一个更强的分类器。在这里,我将详细阐述 Boosting 方法,这是算法在每次迭代中从错误中“学习”的一种方法。更具体地说,我将一步一步地解释 Adaboost 背后的思想以及如何用 r 实现它。
Adaboost 背后的理念
Adaboost 和 bagging 方法(包括随机森林)之间的主要区别在于,在过程结束时,当迭代期间构建的所有分类器都被要求为新观察的目标投票时,将会有比其他树投票更重的树。这些是在所有迭代中表现最好的树(因此,它们表现出很少的错误分类)。比方说,在一天结束的时候,有些树会比其他树说得更多。这种“发言权的重要性”在整个迭代过程中被测量(和更新)。
除此之外,Adaboost 的其他重要特性,如预期的那样,是它从过去的错误中学习的能力,因为在每次迭代中,下一个分类器是基于过去的错误分类错误建立的。
现在让我们看看算法是如何具体工作的。
该算法
假设我们有一个数据集,有 N 个观察值,P 个协变量和一个二元目标变量 y=1,-1。这将是我们的火车布景。
你可以用这个框架设计任何你感兴趣的任务(即,一封电子邮件是否是垃圾邮件,明天的天气是否晴朗……)。
- 我们要做的第一件事是给每一行分配一个权重,这表明该观察值被很好地分类有多重要。我们将这些权重初始化为 1/N,这样在第一次迭代时,当训练第一个分类器时,它们不会产生差异。
- 现在,我们在数据集上训练我们的第一个分类器 G(x ),然后检查哪些是错误分类的观察值,因为我们想要计算总误差,它不同于标准的错误分类误差,因为它是加权求和:
如您所见,我们有一个自标准化的加权误差总和(当目标与拟合值不同时)。请注意,由于其构建方式,总误差将始终介于 0 和 1 之间。有了这个量,我们就可以计算出我们所说的‘话语权的重要性’:
如你所见,误差越大,最后那棵树的投票就越不重要。我们也可以形象化地描述它:
error = seq(0,1,0.001)
alpha = log((1-error)/error)
plot(error,alpha, type='line')
- 有了这个度量α,我们现在可以更新权重,使得对应于错误分类的观察值的权重在下一次迭代中将更高。通过这样做,当我们将在新的加权数据集上拟合下一棵树时,错误分类这些观察值的成本将会更高,并且树将会非常小心地不再错误分类它们(因为这将意味着更高的成本)。因此,对于所有的观察值,i=1,2,…,N,我们更新第 I 个权重如下。
- 如果我们重复这个过程 M 次迭代,在一天结束时,我们将有 M 个分类器,每个分类器有一个加权投票,并且,如果我们想要预测新观察 x*的目标,公式是:
因此,将会有一些树的输出在最终决策中具有很大的决定性(-1 或 1),而其他的树则可以忽略不计。
现在让我们来看看这个过程其余部分的一个非常简短的实现:
data = data.frame( X=c(rnorm(100,0,1),rnorm(100,1,1)), Y=c(rep(0,100),rep(1,100) ) )data$Y =factor(data$Y)
model =adaboost(Y~X, data, 10)
pred =predict( model ,newdata=data)
我们来看看模型的总结和它的预测误差(等于训练集中的误分类误差):
print(pred$error)
print(model)
我们还可以检索最终的“重要性”权重:
model$weights
如你所见,有一些树(像第一棵树)的最终投票非常重要,而其他树(像最后一棵树)则不那么重要。
最后,我们可以将其误差与单棵树的误差进行比较:
single_model=tree(Y∼X, data)
summary(single_model)
如你所见,我们的提升分类器比单一的树分类器更强。
结论
集成方法是强大的技术,可以大大提高决策树的预测准确性。然而,这些方法的警告是,它们使得呈现和解释最终结果变得不太容易。事实上,正如我们在开始时所说的,决策树最显著的特征是它们的可解释性和易于理解性,在一个看起来像“黑盒”的算法世界中,这是一个重要的价值。尽管如此,还是有一些视觉上的选择,如果集合多个树能提高那么多的准确性,那肯定是值得的。
用代码和例子理解 Python 中的高级函数!
通过代码和示例详细了解 python 中的匿名函数和高级函数及其实际实现。
萨法尔·萨法罗夫在 Unsplash 上拍摄的照片
Python 是一种优雅的编程语言,它为用户提供了许多简化代码和缩短代码长度的工具。它是一种面向对象的高级编程语言,早在 1991 年就发布了,具有高度的可解释性和高效性。
我最初是从 C、C++和 Java 这样的语言开始的。当我终于遇到 python 的时候,我发现它相当优雅,简单易学,易于使用。对于任何人来说,Python 都是开始机器学习的最佳方式,甚至对于以前没有编程或编码语言经验的人也是如此。
由于其数据结构、条件循环和函数的简单性,它提供了广泛的通用性来处理各种问题。匿名函数和高级函数是我们今天要讨论的主题,但基本问题是——什么是函数?
函数是写在程序中的一段代码,这样它们可以被多次调用。函数的主要用途是在同一个程序中可以多次重复调用它,而不需要一遍又一遍地编写相同的代码。然而,你也可以用它来为你的程序提供更多的结构和更好的整体外观。
使用关键字 **'def,**定义函数,可以使用已定义或未定义的参数调用这些函数。当您调用特定的函数时,python 编译器会解释将要返回的任何值。
如果您的代码采用这个由几行代码组成的块,并且只用一两行代码就执行了它,会怎么样呢?
这个任务可以通过 python 中的匿名函数来执行。让我们在下一节更深入地理解这个概念。
阿诺德·弗朗西斯卡在 Unsplash 上拍摄的照片
匿名函数:
在 Python 中,匿名函数是定义时没有名字的函数。在 Python 中,普通函数是使用 def 关键字定义的,而匿名函数是使用 lambda 关键字定义的。因此,匿名函数有时也被称为 lambda 函数,它们通常可以互换使用。
该函数的语法如下:
lambda arguments: expression
使用 lambda 函数的主要优点是执行一个 lambda 函数,该函数计算其表达式,然后自动返回其结果。所以总有一个隐式的 return 语句。这就是为什么有些人把 lambdas 称为单表达式函数。在大多数情况下,它对于简化代码非常有用,并且是编程语言不可或缺的一部分。
大多数情况下,lambda 函数是好的,但是如果使用这些函数会使单行代码比预期的长,并且用户很难阅读,那么就应该考虑不使用它们。基本上,当代码的可读性降低时,您应该考虑不使用 lambda 函数。
让我们了解如何准确地使用 lambda 函数中的代码来为我们造福。为了有一个清晰的概念理解,我将列出一些问题陈述,以及它们的带有普通函数和 lambda 函数的代码块解决方案。
问题陈述:
使用函数打印数字的平方。
具有定义功能的代码:
输出:
25
上面的代码块和输出代表了如何通过使用一个普通的 def 函数来计算问题语句来编写下面的代码。
带有 lambda 函数的代码:
输出:
25
在 lambda 函数的帮助下,同样的问题语句可以在两行代码内解决。这就是 lambda 函数简化问题陈述的方式。
Lambda 函数通常与其他函数一起使用,在几行代码而不是一段代码中解决复杂的任务。这些功能即过滤、映射和减少。让我们用单独的问题陈述来分别分析每一个问题。
照片由 Cookie 在 Unsplash 上的 Pom 拍摄
1.过滤器:
顾名思义,filter()函数将创建一个新的列表,只存储满足特定条件的元素,并过滤掉剩余的值。
filter()操作接受一个函数对象和一个 iterable,并创建一个新列表。只要满足条件并返回 true,就会执行成功的计算。
问题陈述:
只打印元素列表的偶数。
具有定义功能的代码:
输出:
[2, 4]
在上面的代码块中,函数接受一个数字列表的输入。然后我们遍历列表,使用条件语句只存储给定列表中的偶数。该函数最终返回包含所有偶数的列表。
带有 lambda 函数的代码:
输出:
[2, 4]
在一行代码中,我们可以借助上面的代码从 iterable 列表中筛选出偶数。返回的输出与前面冗长的函数完全相同。
2.地图:
map()函数遍历给定 iterable 中的所有项,并对每个项执行我们作为参数传递的函数。这类似于 filter()函数。
它可以用来获得条件列表上的布尔值 true 和 false,或者只是将每个元素映射到其各自的输出。让我们用一些代码更详细地理解这一点。
问题陈述:
打印列表中每个元素的方块。
具有定义功能的代码:
输出:
[1, 4, 9, 16, 25]
在上面的代码块中,该函数接受一个类似于前一个问题的数字列表的输入。然后我们遍历列表,计算存储在列表中的每个元素的平方。该函数最后返回包含每个元素的所有平方数的列表。
带有 lambda 函数的代码:
输出:
[1, 4, 9, 16, 25]
map 函数在一行代码中计算并解决问题陈述。列表中的每个元素都映射有各自的方块。
3.减少:
与前两个函数(即 filter()和 map())不同,reduce 函数的工作方式略有不同。它遍历可迭代数字列表,并继续返回一个值。
这个函数在用简单代码而不是较长的代码块执行列表的加法或乘法等计算时非常有用。reduce()函数一次计算列表中的两个元素,直到完成整个 iterable。
问题陈述:
打印列表中所有元素的乘积。
具有定义功能的代码:
输出:
120
在上面的代码块中,该函数接受一个类似于前两个问题的数字列表输入。然后我们遍历列表,计算给定列表中所有数字的乘积。该函数最终返回评估列表后获得的最终产品的输出。
带有 lambda 函数的代码:
输出:
120
reduce()函数需要从 python 3 中的 functools 模块导入。上面的代码块一次执行两个元素的顺序乘法,直到完成整个列表的计算。
结论:
在本文中,我们简要介绍了 python,并对函数在 Python 编程语言中的作用有了基本的了解。
然后,我们进一步讨论了 python 中的匿名函数,并通过一些代码和示例了解了其中可用的每个高级函数选项。
如果你对今天的话题有任何疑问,请在下面的评论中告诉我。我会尽快回复你。如果我错过了什么,请随时告诉我。您希望我在以后的文章中介绍这一点。
看看我的其他一些文章,你可能会喜欢读!
获得一个 GPU 是深度学习的必备条件吗?了解 GPU 及其优势,并探索…
towardsdatascience.com](/do-you-really-need-a-gpu-for-deep-learning-d37c05023226) [## 机器学习和数据科学项目的 10 步终极指南!
详细讨论构建您的机器学习和数据科学项目的最佳方法…
towardsdatascience.com](/10-step-ultimate-guide-for-machine-learning-and-data-science-projects-ed61ae9aa301) [## 分步指南:使用 Python 进行数据科学的比例采样!
了解使用 python 进行数据科学所需的比例采样的概念和实现…
towardsdatascience.com](/step-by-step-guide-proportional-sampling-for-data-science-with-python-8b2871159ae6) [## 2020 年及以后最受欢迎的 10 种编程语言
讨论当今 10 种最流行的编程语言的范围、优缺点
towardsdatascience.com](/10-most-popular-programming-languages-for-2020-and-beyond-67c512eeea73) [## OpenCV:用代码掌握计算机视觉基础的完全初学者指南!
包含代码的教程,用于掌握计算机视觉的所有重要概念,以及如何使用 OpenCV 实现它们
towardsdatascience.com](/opencv-complete-beginners-guide-to-master-the-basics-of-computer-vision-with-code-4a1cd0c687f9)
谢谢你们坚持到最后。我希望你们都喜欢这篇文章。祝大家有美好的一天!
用 PyMC3 了解飞机事故趋势
图片由 Free-Photos 来自 Pixabay
使用 PyMC3 直观地探索历史上的航空事故,应用频率主义者的解释并验证变化的趋势。
前言
今年 8 月 7 日,一架从迪拜(阿联酋)飞往 Kozhikode(印度喀拉拉邦)执行遣返任务的印度航空快运飞机在暴雨中滑出跑道,坠入山谷[1]。
随后 35 英尺的坠落将飞机摔成两半。该航班共搭载了 180 名乘客,其中 18 人在事故中丧生。其余 172 人不同程度受伤,接受治疗[2]。
对这一可怕事故的官方调查自然将是一项事实调查任务,并将试图弄清楚哪里出了问题,谁该负责。
动机
在这个故事之后,我开始谷歌最近的飞机事故,以了解背景,并从全球角度看待这些事件。
这次搜索让我找到了许多网页,上面有飞机坠毁的照片和视频,坠毁统计表,事故调查报告,以及不同航空业专家在此类灾难性事故后的原话。
这次调查的底线是,我们正处于一个日益安全的飞行环境中。监管、设计、机械和电子安全措施比以往任何时候都更加严格,从而使飞行成为一种相对更安全的交通方式。
但是我想亲自用这些数字来验证这个结论。
这个练习的动机问题是—
与过去相比,最近一段时间飞行变得相对安全了吗?
数据源
我在维基百科和 T2 国家运输安全委员会上查看了公开的空难数据,并创建了一个数据集来满足这次演习的需要。
整个练习和数据集可以在我的 GitHub 资源库中找到。
现在切换到第一人称复数……
工作工具
为了回答这个激励性的问题,我们把这个任务分成两部分—
- Python 中的探索性数据分析(EDA)。
- Python 中的概率编程(PyMC3)。
探索性数据分析
在这一部分中,我们回顾了过去的飞机坠毁事件,这些事件构成了我们进行分析的时间序列。一些需要记住的事情-
- 《国际民用航空公约》将飞机事故与飞机事故区分开来。区别主要在于是否发生了死亡事故。
- 在这次练习中,我们的重点仅限于事故的发生,而不是其原因。
- 我们研究了从 1975 年到 2019 年的商业飞机事故。
事故和死亡趋势
图 1-从 1975 年到 2019 年每年的事故和死亡人数。
从历史时间序列来看,我们可以直观地感受到自 1978 年以来每年事故数量的下降。1987 年至 1989 年期间,事故数量似乎略有上升,此后数量稳步下降。2017 年是事故发生率最低的一年,也是航空史上最安全的一年。2017 年后,这些数字似乎略有增加。
另一个明显的趋势是死亡人数随着时间的推移而下降。20 世纪 70 年代和 80 年代是飞行的危险时期,平均每年有 2200 起飞机事故导致死亡。但是随着时间的推移,我们看到这个数字已经急剧减少。
当这种下降趋势被放在航空旅客数量上升的背景下看待时(图 1 中绿色阴影区域),我们对航空安全有了更好的了解。
每百万乘客的死亡人数
图 2——每年百万乘客出行中的死亡人数
当从航空旅客人数上升的角度来看死亡人数的下降时,我们会看到一个明显的下降趋势。每年乘飞机旅行的每百万乘客中的死亡人数已经从百万分之五急剧下降到百万分之一以下。
(免责声明:贝叶斯人,准备好那撮盐)
每起事故死亡人数
图 3-每起飞机事故死亡人数的变化
飞机安全的另一个衡量标准是每起事故的死亡人数。虽然可能有许多外部因素(外部因素)影响特定事故中的死亡人数——天气、碰撞性质、一天中的时间等。——我们仍然将这一指标视为对飞机安全性的粗略评估。
1995 年以后,趋势似乎略有下降,但从图表中并不能立即观察到。我们还看到,1985 年、1996 年、2014 年和 2018 年是涉及重大撞车事故的致命年份,因为每次撞车事故的平均死亡人数很大。
变动率
图 4 —事故数量的年度百分比变化
在我们开始动机问题的概率测试之前,最后一个证据是事故的年变化率。
如果我们真的生活在安全的时代,那么我们期望图表显示一系列连续增加的绿色条。这样的窗口只在 1979-80 年、1980-84 年、1999-00 年、2006-07 年和 2013-14 年观察到。从 1980 年至 1984 年和 1996 年至 2000 年,可以看到相对安全旅行的延长期。
如果我们看一下 1995 年以后的变化率,我们会发现事故同比有很大程度的下降(红色柱很少,绿色柱更多)。
似乎一些外部因素(如飞机设计的变化、民航规章、更好的空中交通管制技术等。)可能导致了 1995 年以后的这种下降。
概率规划
从我们的数据研究中,我们发现每十年的飞机事故数量持续下降,我们用一些统计方法验证了这一趋势。
我们还看到,1995 年可能是航空业的一个转折点。我们如何验证这个假设?
在有限的数据和事件的不可重复性(让我们假设我们无法模拟这些事故一百万次)的情况下,这样做的一个有趣的技术是使用概率技术,如马尔可夫链蒙特卡罗(MCMC)。
实现这些技术的方法之一是借助 Python 中的 PyMC3 库。
快速入门
PyMC3 是 Python 中的一个库,帮助我们进行概率编程。这并不意味着编程是概率性的(它仍然是一个非常确定的过程!相反,我们采用概率分布和贝叶斯方法。
这种技巧是建立在一种的世界观之上的。我们从一个关于某个过程或参数的信念(称为先验概率)开始,并在几千次运行(又称随机抽样)后更新这个信念(称为后验概率)。这种方法与频率主义者看待事物的方式相反(就像我们在 EDA 中所做的)。**
这个过程的第二个基础是 马尔可夫链蒙特卡罗 (MCMC)的随机抽样方法。这是一套算法,允许我们从先验概率分布中取样,并生成数据来测试我们的先验信念并更新它们。
PyMC3 网页上提供的文档和Susan Li的这个实践方法对于高层次理解库和技术是极好的。坎姆·戴维森-皮隆写的《黑客的贝叶斯方法》这本书真的很有帮助,如果你想动手的话。
好吧,让我们测试一下
我们从建立我们对事故的先验信念开始—
飞机事故遵循怎样的分布?
这里我们假设事故遵循泊松分布。
**P(x|lambda) = (lambda^x)*(exp^-lambda)/(lambda!)x: number of accidents
lambda: rate of occurrence of the accident**
发生率会是多少?
给定我们的初始假设,我们进一步假设这个发生率大约是整个数据集的平均发生率的倒数。
换句话说,
**lambda = 1/(mean of number of accidents from 1975 to 2019)**
最初的转折点会是什么?
转折点是前一年发病率高,后一年发病率低。我们最初假设,从 1975 年到 2019 年的每一年都有相等的概率(取自离散的均匀分布)被认为是一个转折点。
有了这些先验的信念,我们实例化了这个模型—
**import pymc3 as pm
import arviz as azyears = np.arange(1975, 2020)
with pm.Model() as accident_model:
alpha = 1/df.loc[df.year>=1975].num_crashes.mean()
# Setting the prior belief for the inflection point
change_point = pm.DiscreteUniform(
'change_point', lower=1975, upper=2020)
# Setting prior belief for the rate of occurrence, ie, lambda, before and after inflection
rate_before = pm.Exponential('before_change', alpha)
rate_after = pm.Exponential('after_change', alpha)
# Allocate appropriate Poisson rates to years before and after current
rate = pm.math.switch(change_point >= years, rate_before, rate_after)accidents = pm.Poisson("accidents", rate, observed=df.loc[df.year>=1975].num_crashes)**
我们用不掉头采样器(螺母)对这些分布进行了至少 10,000 次采样—
**with accident_model:
trace = pm.sample(10000, return_inferencedata=False)**
测试结果
图 5-更新我们对可能的变化点和发生率的先前信念的结果。
我们看到,在采样 10,000 次后,我们最初认为所有年份都有同等机会被视为转折点的想法得到了更新。结果表明,1997 年(而不是 1995 年)最有可能成为航空事故史上的转折点。
并且已经更新了最初的假设,即发生率是 45 年平均值的倒数。1997 年被认为是一个转折点,因为事故发生率从大约每年 300 起变成了每年 165 起!
那么这些预测有多大把握呢?
图 6 —预测值的不确定性
概率编程的 USP 是,预测是用一撮盐做出来的!与频率主义者的预测不同,贝叶斯方法的预测带有不确定性(更现实)。
我们的模型显示,94%的高密度区间(HDI)在 1996 年和 1999 年之间,1997 年是平均值。换句话说,1997 年成为转折点的概率较大。
在这一转折点之前,类似的 94%人类发展指数发生率为每年 295 至 312 起事故;1997 年以后的事故每年在 158 至 172 起之间。
最近的过去
由于我们的激励问题仅限于“最近的时间”,我们将该模型应用于 2000 年至 2019 年的数据(假设最近 20 年足够近)。
图 7-2000 年至 2019 年间事故的模型结果
我们观察到,2012 年很可能是一个转折点(94%的人类发展指数是 2010 年至 2013 年),2012 年之前的事故率接近每年 180 起,2012 年之后大约每年 120 起。
图 8-2000 年至 2019 年间事故模型结果的不确定性
裁决
所以通过进行这个小练习,我能够满足我的好奇心并回答这个激励性的问题—
如果每年的低航空事故率是航空安全的唯一指标,那么在 1997 年后,事故率大幅下降,在过去 20 年中,2012 年后数字进一步下降。
尽管每年的事故数量很少,但与 20 年前相比,现在飞行相对更安全。
参考
再见!
理解 AlphaGo:人工智能如何思考和学习(高级)
是时候了解卷积神经网络、深度学习以及我们的现代人工智能如何赢得顶级围棋选手的比赛了…
“作为一名技术专家,我看到人工智能和第四次工业革命将如何影响人们生活的方方面面。”
费-李非
本文将从深度学习开始,深入到 DeepMind AI 的架构,就像 Alpha Go 一样。到文章结束的时候,我们应该能看懂很多人工智能领域最前沿的论文了,像 Alpha Go Zero 的原论文。蒙特卡罗树搜索(MCTS)和卷积神经网络是我们在理解 Alpha Go 如何工作之前应该熟悉的两个基本概念。如果你有兴趣了解更多,决策树、状态机、强化学习和蒙特卡罗树搜索等概念在了解 alpha go:AI 如何思考和学习(基础)中有解释。
这篇文章的目的是让人们了解 Alpha Go 是如何工作的,但它也可能会提高人们对人工智能领域的兴趣,因为人工智能领域的信息太多,无法包含在一篇中型文章中。我们将要学习更高级的材料,所以要准备好一些可能更难理解的东西。我会尽我所能,用最简单的方式解释这些概念。
我们开始吧!
深度神经网络的灵感来自我们大脑的理论,图片来自 Pixabay
受数学和神经科学启发的深度神经网络
“这是最糟糕的时候,其他人都在做不同的事情。”
——本吉奥,CIFAR 项目的联合主任
神经网络简史
1943 年,神经生理学家沃伦·麦卡洛克和数学家沃尔特·皮茨基于称为阈值逻辑单元(TLU) 的数学算法创建了计算模型,以描述神经元可能如何工作。在 20 世纪 50 年代计算机变得更加先进之前,模拟神经网络是可能的。
在 2000 年之前,这被认为是最糟糕的研究领域之一。LeCun 和 Hinton 以不同的方式提到了在这一时期他们的论文是如何由于他们的主题是神经网络而被拒绝发表的。人工神经元被认为是无用的,因为它的单个神经元甚至不能解决 XOR 逻辑。
这项研究由加拿大高级研究所资助。在该领域所有研究人员的不断努力下,Krizhevsky、Sutskever 和 Hinton 凭借卷积神经网络(CNN)赢得了 2012 年的 ImageNet 竞赛,卷积神经网络是 Yann LeCun 在 1998 年首次创建的模型。此后,深度神经网络几乎出现在每一篇论文中。
神经网络之所以在诞生后的很长一段时间里如此成功,还有其他原因。首先,这些模型严重依赖于计算能力,随着这些年来我们的计算机变得越来越先进,它们最终变得更加可行。更重要的是,为了充分训练这种先进的模型,需要大量的数据。在互联网与社交媒体、电子商务和移动设备一起受到消费者欢迎后,每天都会产生大量数据,这为每个人创造大量数字数据增加了更多机会。这些新技术也创造了对计算机视觉(CV)和自然语言处理(NLP)应用的需求。
在人工智能领域有两种对立的信念——连接主义和象征主义。联结主义认为认知科学的方法希望解释智能行为。另一方面,象征主义依赖于基于数学和逻辑运算的推理。虽然许多数学模型成功地完成了给定的任务,但也有足够多的研究表明,基于认知科学的方法确实加速了人工神经网络的学习过程。
连接主义与象征主义,图片来自 Pixabay
人工神经网络
术语“深度学习”是一个涉及深度神经网络的领域,意思是具有一个或多个隐藏层的人工神经网络。人工神经网络是一个有向无环图(DAG) 由人工神经元的连接层组成。这些人工神经元也被称为“感知器”,因此人工神经网络有时被称为多层感知器(MLP)。这些值将被输入到输入层,这是网络的第一层,结果将从输出层出来,这是最后一层。
人工神经网络(ANN),有时也称为多层感知器(MLP)
我们可以将多层感知器视为一种投票方案,为了得出一个决定,输入层的每个感知器向下一层的感知器发送一个加权投票,再下一层……直到投票在输出层的感知器中完成。激活函数通常在中间很陡,以确保激活值位于两端,从而接近二进制判决值。这里是我们可以可视化人工神经网络如何分类数据的地方。
人工神经网络如何工作
正向传播&激活函数
在每一层中,有许多相互连接的感知器。感知器之间的机制可以由以下术语定义:
- 激活( a ) :感知器的激活是通过对前一层感知器的激活求和,加上一个偏差,然后通过激活函数进行转换来计算的。
- Bias ( b ) :每次感知器接收到一个值,就会对该值应用一个 Bias。
- 权重( w ) :感知器的激活值将被乘以一个权重并相加。
- 激活函数( σ ) :从激活函数计算每个感知器的激活值。
感知器背后的数学
有几种类型的激活功能。Sigmoid 函数(σ),经常与学习曲线有关,是最基本的一个。每个激活功能都有其特点。整流线性单元(ReLU)是最受欢迎的一种。
每个感知器会将其值传播到前进层的感知器中,最终到达输出层。这个过程被称为正向传播。
人工神经网络的前向传播
反向传播&成本函数
前向传播基于权重、偏差和激活函数,但是是什么决定了这些值呢?激活函数是预先选择的,但是对于大型神经网络,不可能手动选择适当的权重和偏差。
在机器学习领域,模型应该自己从数据中“学习”,这个学习过程也称为“训练”。通常,数据被分成 2 个不同的集合——训练集合和测试集合。训练集用于将模型“训练”到更成熟的状态,然后性能将由测试集进行评估。****
传统算法与机器学习的比较
有许多不同的方法来“训练”一个人工神经网络,但最流行的方法是使用反向传播**。**
在反向传播之前,神经网络的权重和偏差通常以正态分布随机初始化。然后,神经网络将执行前向传播。由于权重和偏差是随机初始化的,所以第一次正向传播的结果通常会相差很远。然后使用成本函数来计算预期结果和神经网络输出之间的差异。计算出差异后,它将用于调整前一层的权重和偏差。该过程在层中向后传播,因此它被称为“反向传播”。
人工神经网络的反向传播
这里有一个关于反向传播的更正式的教程,因为它需要一些高等数学来解释。神经网络的解释和代码示例可以在这里找到,这里作者使用矩阵运算来模拟 Python 中的神经网络。
反向传播解释
卷积神经网络(CNN)
为了以更好的效率处理图形数据,Yann LeCun 在 1989 年发明了卷积神经网络。该网络计算 2D 阵列的空间信息。卷积神经网络也非常适合分析其他空间信息重要的 2D 数据,包括棋盘。
卷积神经网络由 3 种类型的层构成——卷积层、池层和全连接层。这些具有不同形状和大小的层在不同的主题上会有不同的表现。对卷积神经网络的研究通常涉及调整这些层及其组成,以优化目标数据集的性能。
卷积层(conv)
卷积层通常作为卷积神经网络的第一层出现。这些类型的图层将使用过滤器扫描源图层,并将总和放入目标图层。一些滤波器擅长检测边缘,一些擅长其他任务,更多关于不同类型卷积滤波器的细节及其在计算机视觉中的应用可以在这里找到。
卷积层(3x3)
池层(pool)
池层遍历源层,并在有界区域内选择一个特定的值。该值通常是该区域内的最大值、最小值和平均值。将信息缩小也称为“缩减采样”。
池层(2x2 最大池)
全连接层(fc)
全连接层本质上是一个多层感知器,它有时被称为“softmax”,本质上做一些被称为“加权和”的事情。
全连接层只是一个人工神经网络
卷积神经网络最常用于计算机视觉领域,它影响了许多领域,如电子商务、金融科技、游戏人工智能、癌症检测、整形外科、精神病学、野火检测等。这里有一篇关于计算机视觉如何影响电子商务的文章和另一篇关于计算机视觉中一些很酷的前端框架的文章。
Alpha Go Zero,用机器学习掌握围棋游戏
“太完美了,简直完美无瑕,毫不留情。…我想我这辈子也追不上它了。”
—柯洁(围棋世界冠军)在输给 Alpha Go Zero 三局后
恭喜你!!!我们已经到了这篇文章的目的所在。现在我们将准备好了解传说中的国际象棋和围棋人工智能是如何从头到脚工作的。
设计 Alpha Go Zero 的架构
Alpha Go Zero 由卷积神经网络和蒙特卡罗树组成。它通过强化学习算法进行自我游戏训练。
阿尔法如何归零转弯
有几个术语我们应该熟悉一下。由于 Medium 不支持大多数字母的下标,所以下标由“_”后括号内的字母表示,类似于 LaTeX。
- 状态(s) :游戏的状态用 s(T)表示,从 s(0)到 s(T),其中 s(T)为终止状态。
- 蒙特卡罗树(α) :蒙特卡罗树α_(θ)用于决定游戏的下一个状态。
- Move (a) :每个状态 s_(t)的移动 a_(t)由搜索概率π_(t)决定。
- 搜索概率(π) :搜索概率π_(t)用于确定在状态 s_(t)下移动 a_(t)。
- 卷积神经网络(f) :卷积神经网络 f_(θ)用于通过分析棋盘输出价值向量 v 和策略向量 p。
- 值向量(v) :值向量 v_(t)表示当前玩家在位置 s_(t)获胜的概率
- 策略标量§ :策略标量 p_(t)表示移动的概率分布。
- 赢家(z) :赢家 z 被传播回去训练模型。
Alpha Go Zero 完全通过自我对弈进行训练,仅使用 1 个卷积神经网络,不同于原始 Alpha Go 需要 2 个卷积神经网络,并借用专业人类对弈的信息。
Alpha Go Zero 将游戏棋盘作为卷积神经网络的原始输入。然后网络输出一个矢量 v 和一个标量 p 。然后,蒙特卡罗树用于计算搜索概率π,该概率用于确定游戏下一阶段的移动。最后,根据游戏规则确定一个获胜者 z,并使用它通过强化学习来训练模型。
到现在为止,我们已经知道了 Alpha Go Zero 是如何工作的!!!更重要的是,我们已经步入了人工智能的世界。
人工智能的现在和未来
“记住我的话——人工智能比核武器危险得多”
—特斯拉& Space X 首席执行官、OpenAI 联合创始人埃隆·马斯克(Elon Musk)等等……
我们在人工智能方面的研究处于一种有趣的状态。在机器学习之前,所有的人工智能都是硬编码的,以我们期望的方式表现。机器学习允许人工智能独立地改进自己,有时会产生意想不到的行为。研究人员观察到,通过将人工智能主体放入环境中,并以一定的规则奖励它们,人工智能主体的智能行为逐渐增加。这可能是迷人的,但想想也很可怕。
开启 AI 多智能体捉迷藏,智能体,学会了在进化算法下的智能行为
游戏环境中的研究就像数学和物理。就其本身而言,这种研究可能只是好奇想知道和尝试的乐趣,但缺乏任何坚实的价值。然而,它们将为其他更具应用性的科学和工程提供必要的基础。未来掌握在你我手中,我们一起将明天变得更加美好。
作者的笔记
我花了相当多的时间才完成文章的第二部分。我忙于学业和找工作。我是人机交互(HCI)专业的硕士生,但我对许多不同的领域都感兴趣。人工智能就是其中之一。我希望这篇文章对你有所帮助。另外,我非常需要一份工作,所以如果你有适合我的工作,请告诉我。写作也是我的爱好,因为我喜欢把事情弄清楚并与他人分享,所以如果你在 Medium 上跟随我,你会看到更多这样的事情。
“未来掌握在你我手中,我们一起将明天变得更加美好。”
—黄慎,本文作者
用例子理解和选择正确的概率分布
概率与统计
举例说明最常见的离散概率分布
作者图片
概率分布
概率分布是一种数学函数,它描述了获取事件可能值的可能性。概率分布可以是离散的,也可以是连续的。在离散分布中,数据只能取某些值,而在连续分布中,数据可以取指定范围(可以是无限的)内的任何值。
有各种各样的离散概率分布。离散概率分布的使用取决于数据的属性。例如,使用:
- 二项式分布,用于计算一个过程的概率,在该过程中,每次试验只可能出现两种可能结果中的一种,例如掷硬币。
- 超几何分布,以寻找在没有替换的情况下 n 次抽奖中 k 次成功的概率。
- 泊松分布,用于衡量给定时间段内给定数量的事件发生的概率,例如图书馆每小时的图书借阅次数。
- 几何分布,用于确定在第一次成功之前进行指定次数试验的概率。
二项分布
二项分布可能是所有离散分布中最广为人知的。这种分配有两种可能的结果。使用二项分布的一个典型例子是抛硬币。抛硬币只有两种可能的结果:正面或反面,每种结果都有相同的 1/2 概率。我们来看看二项分布什么时候可以用!
二项分布的主要特征:
- 这个实验包括 n 次相同的试验。
- 每次试验只有两种可能的结果,即成功或失败。
- 这些试验是相互独立的。
- 用 p 表示成功的概率,它在两次试验之间保持不变,用 q = (1 — p)表示在任何试验中失败的概率。
作者图片
例子
**问:**某仓库发运十台印刷机,其中四台对某公司有缺陷。这家公司随机挑选了五台机器,如果五台机器都没有缺陷,就接受发货。
在抽样替换时,找出公司接受货物的概率。
我要用上面的公式:
试验次数(n)为 5,得到有缺陷机器的概率§为 4/10,所以 q 为 6/10,x = 5。
作者图片
超几何分布
超几何分布是一种非常类似于二项式分布的概率分布。超几何分布和二项式分布都描述了一个事件在固定数量的试验中发生的次数。对于二项分布,每次试验的概率保持不变。相比之下,在超几何分布中,每次试验都会改变后续试验的概率,因为没有替代。
超几何分布的主要特征:
- 考虑一组 N= N1 + N2 的相似物体,其中 N1 属于两个二分类之一,而 N2 属于第二类。
- 从这 N 个对象中随机选择的 n 个对象的集合,并且没有替换。
作者图片
例子
**问:**让我们稍微改变一下之前的问题。如果我们现在不更换样品,公司接受货物的可能性有多大?
我们知道,印刷机的总数 N 是 10,随机选择进行测试的机器数 N 是 5。让我们定义 N1 是无缺陷的,N2 是有缺陷的,那么 N1 =6,N2= 10,N1 =4。为了让公司接受这批货,我们不能有任何有缺陷的机器。选择所有无缺陷机的方式数为 6C5,选择 0 缺陷机的方式数为 4C0。
作者图片
泊松分布
泊松分布帮助我们预测特定事件在某一时间间隔内发生的概率。
泊松分布的主要特征:
- 在不重叠的时间间隔内发生变化的次数是独立的。
- 在长度为 h 的足够短的间隔内恰好发生一次变化的概率约为λh,其中λ>0。
- 在足够短的时间间隔内发生两次或两次以上变化的概率基本为零。
作者图片
请注意,泊松分布是二项式分布的一种极限形式。对于大的 n,我们有 p=λ/n.
例子
**问:**假设某种流感疫苗出现副作用的概率为 0.005。如果 1000 人接种,求最多一人患病的大概概率。
由于 n=1000 是一个很大的数,我们可以用泊松近似二项分布来求解,其中λ =pn = 0.005 * 1000 =5。
P(x≤1) = P(x=0)+P(x=1)
作者图片
**问:**电话在某住所接听为泊松过程,参数λ= 2/小时。如果黛安洗了 10 分钟的澡,电话在这段时间内响起的概率是多少?
假设每 60 分钟有两次通话,我们首先计算每 10 分钟我们期望的通话次数,λ=2 * 10 / 60 = 1/3。现在我们要计算在这 10 分钟内至少接到一个电话的概率,所以本质上我们要计算 P(X≥1),可以写成 1 — P(X=0)。
作者图片
几何分布
几何分布表示在第一次成功之前进行指定次数试验的概率。遵循几何分布的一个典型问题是确定一枚硬币在第一次正面朝上之前正面朝上的次数。
几何分布的主要特征:
- 考虑一系列独立的试验,每个试验都有两种可能的结果,成功或失败。设 p 为成功的概率。定义随机变量 X 为第一次成功的试验。
- 理论上,试验的次数可能会永远持续下去。必须至少有一次审判。
作者图片
例子
**问:**一台机器生产一件次品的概率是 0.01。每件产品在生产时都经过检查。假设这些是独立的试验,并计算必须检查至少 100 个项目才能找到一个有缺陷的项目的概率。
使用上面的公式,P(X≥100) → P(X>99)
作者图片
结论
概率分布是统计学的基础,就像数据结构对于计算机科学一样。在本文中,我总结了几种最常见的离散概率分布的用例。这只是概率分布旅程的开始。要了解更多关于不同概率分布的信息,请看这张令人难以置信的所有单变量分布的详细地图!
参考
- 概率与统计推断(第 9 版)
- 【https://www.statisticshowto.com/probability-and-statistics/
- https://www.onlinemathlearning.com/math-probability.html
如果你喜欢我的内容,请关注我❤️,看看我最近的博客:
作为数据分析师或数据科学家,我们不仅需要知道概率和统计,机器学习算法…
towardsdatascience.com](/how-to-prepare-for-business-case-interview-as-an-analyst-6e9d68ce2fd8) [## 构建电子商务产品推荐系统:第二部分——模型构建
这个博客是我之前工作的延续,在我之前的工作中,我谈到了我是如何收集产品评论和…
medium.com](https://medium.com/@kessiezhang/building-a-product-recommendation-system-for-e-commerce-part-ii-model-building-8b23a9b3ac27) [## 为电子商务建立一个产品推荐系统:第一部分——网络搜集
今天,如果我们想到机器学习在商业中最成功和最广泛的应用,推荐者…
medium.com](https://medium.com/@kessiezhang/building-a-product-recommendation-system-for-e-commerce-part-i-web-scraping-798b6251ab51) [## 如何将 Jupyter 笔记本转换成 PDF
用几行代码将 Jupyter 笔记本转换为 pdf(调试“500:内部服务器错误”)
towardsdatascience.com](/how-to-convert-jupyter-notebooks-into-pdf-5accaef3758)
在 TensorFlow 和 Keras 中理解和实现辍学
技术的
Dropout 是一种常见的正则化技术,在计算机视觉任务(如姿态估计、对象检测或语义分割)的最新解决方案中得到利用。
约翰·马特丘克在 Unsplash 上拍摄的照片
介绍
本文介绍了 dropout 技术的概念,这是一种在深度神经网络(如递归神经网络和卷积神经网络)中使用的技术。
丢弃技术包括在每个训练步骤中从神经网络中省略充当特征检测器的神经元。每个神经元的排除是随机确定的。
G.E Hinton 在 2012 年发表的论文中提出了这个简单的技术:“ 通过防止特征检测器的共同适应来改善神经网络。
在本文中,我们将深入揭示辍学的概念,并了解如何使用 TensorFlow 和 Keras 在神经网络中实施这一技术。
了解辍学技巧
神经网络在其输入和输出层之间有隐藏层,这些隐藏层中嵌入了神经元,神经元内的权重以及神经元之间的互连使神经网络系统能够模拟类似学习的过程。
使用https://playground.tensorflow.org/构建简单的神经网络
一般的想法是,神经网络架构中的神经元和层越多,其代表能力就越强。这种表示能力的提高意味着神经网络可以拟合更复杂的函数,并很好地概括训练数据。
简单地说,在神经网络层中,神经元之间的互连有更多的配置。
使用https://playground.tensorflow.org/构建的复杂神经网络
利用更深层次的神经网络的缺点是它们非常容易过度拟合。
过度拟合是一个常见的问题,它被定义为经过训练的机器学习模型无法很好地推广到看不见的数据,但同一模型在它接受训练的数据上表现良好。
退出的主要目的是最小化训练网络中过度拟合的影响。
Dropout 技术通过随机减少神经网络中互连神经元的数量来工作。在每一个训练步骤中,每个神经元都有可能被遗漏,或者更确切地说,从连接的神经元的整理贡献中被遗漏。
这种技术最小化了过度拟合,因为每个神经元变得独立地足够,在某种意义上,层内的神经元学习不基于其相邻神经元的合作的权重值。
因此,我们减少了对大量互连神经元的依赖,以从训练好的神经网络中产生像样的表示能力。
假设你训练了 7,000 个不同的神经网络架构,要选择最好的一个,你只需取所有 7,000 个训练过的神经网络的平均值。
辍学技术实际上模拟了这个场景。
如果神经元在训练步骤中被遗漏的概率被设置为 0.5;我们实际上是在每个训练步骤中训练各种不同的网络,因为在任何两个训练步骤中排除相同的神经元是非常不可能的。因此,利用退出技术训练的神经网络是在每个训练步骤中出现的所有不同神经元连接组合的平均值。
实际场景
在实际场景中,或者当测试利用了对看不见的数据的丢弃的已训练神经网络的性能时,需要考虑某些项目。
第一个事实是剔除技术实际上并没有在神经网络的每一层上实现;它通常在网络最后几层的神经元中被利用。
在已发表的论文中进行的实验中,有报道称,在 CIFAR-10 数据集上测试时,在最后一个隐藏层使用 dropout 时有 15.6%的错误率。这比在相同的卷积神经网络上测试相同的数据集时报告的 16.6%的错误率有所改善,但是在任何层中都没有包括丢弃技术。
第二点是,在实际场景中,当评估一个训练好的神经网络时,并不使用丢失。由于在评估或测试阶段没有使用丢弃,因此实现了神经网络的全部潜力。这意味着网络中的所有神经元都是活跃的,每个神经元的输入连接都比训练时多。
因此,期望将神经元的权重除以 1,减去丢失超参数值(训练期间使用的丢失率)。因此,如果训练期间的辍学率为 0.5,那么在测试时间内,每个神经元的权重结果减半。
实施辍学技术
使用 TensorFlow 和 Keras,我们配备了工具来实现一个神经网络,该网络通过在神经网络架构中包含脱落层来利用脱落技术。
我们只需要添加一行来在更广泛的神经网络体系结构中包括一个脱落层。Dropout 类有几个参数,但是现在,我们只关心“rate”参数。辍学率是一个超参数,表示在训练步骤中神经元激活被设置为零的可能性。rate 参数可以取 0 到 1 之间的值。
*keras.layers.Dropout(rate=0.2)*
从这一点开始,我们将逐步实现、训练和评估一个神经网络。
- 利用加载工具和库, Keras 和 TensorFlow
*import tensorflow as tf
from tensorflow import keras*
2.加载 FashionMNIST 数据集,归一化图像并将数据集划分为测试、训练和验证数据。
*(train_images, train_labels),(test_images, test_labels) = keras.datasets.fashion_mnist.load_data()
train_images = train_images / 255.0
test_images = test_images / 255.0
validation_images = train_images[:5000]
validation_labels = train_labels[:5000]*
3.使用 Keras 模型类 API 创建一个包含 dropout 层的自定义模型。
*class CustomModel(keras.Model):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.input_layer = keras.layers.Flatten(input_shape=(28,28))
self.hidden1 = keras.layers.Dense(200, activation='relu')
self.hidden2 = keras.layers.Dense(100, activation='relu')
self.hidden3 = keras.layers.Dense(60, activation='relu')
self.output_layer = keras.layers.Dense(10, activation='softmax')
self.dropout_layer = keras.layers.Dropout(rate=0.2)
def call(self, input, training=None):
input_layer = self.input_layer(input)
input_layer = self.dropout_layer(input_layer)
hidden1 = self.hidden1(input_layer)
hidden1 = self.dropout_layer(hidden1, training=training)
hidden2 = self.hidden2(hidden1)
hidden2 = self.dropout_layer(hidden2, training=training)
hidden3 = self.hidden3(hidden2)
hidden3 = self.dropout_layer(hidden3, training=training)
output_layer = self.output_layer(hidden3)
return output_layer*
4.加载实现的模型并初始化优化器和超参数。
*model = CustomModel()
sgd = keras.optimizers.SGD(lr=0.01)
model.compile(loss="sparse_categorical_crossentropy", optimizer=sgd, metrics=["accuracy"])*
5.训练总共 60 个时期的模型
*model.fit(train_images, train_labels, epochs=60, validation_data=(validation_images, validation_labels))*
6.在测试数据集上评估模型
*model.evaluate(test_images, test_labels)*
评估结果将类似于下面的评估结果示例:
*10000/10000 [==============================] - 0s 34us/sample - loss: 0.3230 - accuracy: 0.8812[0.32301584649085996, 0.8812]*
评估结果示例中显示的准确性对应于我们的模型的 88%的准确性。
通过一些微调和使用更重要的历元数进行训练,精确度可以提高几个百分点。
这里有一个 GitHub 存储库,用于存储本文中介绍的代码。
Dropout 是一种常见的正则化技术,在计算机视觉任务(如姿态估计、对象检测或语义分割)的最新解决方案中得到利用。该概念易于理解,并且通过将其包含在 PyTorch、TensorFlow 和 Keras 等许多标准机器/深度学习库中而更容易实现。
如果您对其他正则化技术以及它们是如何实现的感兴趣,请阅读下面的文章。
感谢阅读。
* [## 如何在 TensorFlow(Keras)中实现自定义正则化
了解如何使用 TensorFlow 和 Keras 相对轻松地实现自定义神经网络正则化技术。
towardsdatascience.com](/how-to-implement-custom-regularization-in-tensorflow-keras-4e77be082918) [## 神经网络中的批量标准化(代码)
通过 TensorFlow (Keras)实施
towardsdatascience.com](/batch-normalization-in-neural-networks-code-d7c9b88da9f5)*
理解和实现 LeNet-5 CNN 架构(深度学习)
技术和代码
在本文中,我们使用定制实现的 LeNet-5 神经网络架构在 MNIST 数据集上执行图像分类。
介绍
L eNet 是由 Yann LeCun 、 Leon Bottou 、 Yoshua Bengio 和 Patrick Haffner 在 1998 年的研究论文中提出的。该论文的许多列出的作者继续为深度学习领域提供了几项重要的学术贡献。
扬·勒昆莱昂·博图帕特里克·哈夫纳 ,以及 约华·本吉奥****
本文将介绍原始论文中描述的 LeNet-5 CNN 架构,以及使用 TensorFlow 2.0 实现该架构。
本文最后将利用实现的 LeNet-5 CNN 对来自 MNIST 数据集的图像进行分类。
这篇文章的内容:
- 了解卷积神经网络内的组件
- 深度学习和机器学习常用术语的关键定义
- 原研究论文 中提出的对 LeNet-5 的理解
- 用 TensorFlow 和 Keras 实现神经网络
本文中的内容是为各种水平的深度学习和机器学习学生编写的。
**对于那些渴望获得编码的人,向下滚动到’ LeNet-5 TensorFlow 实现’部分。
卷积神经网络
卷积神经网络是用于解决与图像相关联的任务的神经网络架构的标准形式。针对对象检测、人脸检测、姿态估计等任务的解决方案都有 CNN 架构变体。
CNN 架构的一些特征使得它们在一些计算机视觉任务中更有利。我以前写过深入研究每个特征的文章。
LeNet-5
LeNet-5 CNN 架构由 7 层组成。层组成包括 3 个卷积层、2 个子采样层和 2 个全连接层。
上图显示了 LeNet-5 架构的描述,如原始论文中所示。
**第一层是输入层——这通常不被认为是网络的一层,因为在这一层什么也学不到。输入层的构建是为了接收 32x32,这些是传递到下一层的图像的尺寸。熟悉 MNIST 数据集的人会知道,MNIST 数据集图像的尺寸为 28x28。为了得到满足输入层要求的 MNIST 图像尺寸,对 28x28 图像进行了填充。
研究论文中使用的灰度图像的像素值从 0 到 255 归一化到-0.1 到 1.175 之间。归一化的原因是为了确保该批图像的平均值为 0,标准偏差为 1,这样做的好处是减少了训练时间。在下面的 LeNet-5 图像分类示例中,我们将归一化图像的像素值,使其取 0 到 1 之间的值。
LeNet-5 架构利用了两种重要的层结构:卷积层和子采样层。
在研究论文和下图中,卷积层用’ Cx’ 标识,子采样层用’ Sx’ 标识,其中’ x’ 是层在架构中的顺序位置。 Fx’ 用于识别完全连接的层。这种层识别的方法可以在上图中看到。
**官方的第一层卷积层 C1 产生 6 个特征图作为输出,并且具有 5x5 的核大小。内核/过滤器是窗口的名称,该窗口包含在权重值与输入值的卷积过程中使用的权重值。 5x5 也表示卷积层内每个单元或神经元的局部感受野大小。第一卷积层产生的六个特征图的尺寸是 28x28。
**一个子采样层’ S2’ ‘跟在’ C1’ 层’之后。“S2”层将它从前一层接收的特征地图的维度减半;这就是通常所说的下采样。
**‘S2’层也产生 6 个特征图,每个对应于从前一层作为输入传递的特征图。此链接包含更多关于子采样层的信息。
有关 LeNet-5 其余层的更多信息,请参见实现部分。
下表总结了每一层的主要特性:
LeNet-5 架构特性(按作者)
LeNet-5 张量流实现
我们从导入我们将使用的库开始实施:
- tensor flow:机器学习模型的实现、训练、部署的开源平台。**
- Keras:一个开源库,用于实现在 CPU 和 GPU 上同时运行的神经网络架构。**
- Numpy:一个用 n 维数组进行数值计算的库。**
**import tensorflow as tf
from tensorflow import keras
import numpy as np**
接下来,我们使用 Keras 库加载 MNIST 数据集。Keras 图书馆有一套易于使用的数据集。
我们还需要将数据集划分为测试、验证和训练。下面是每个分区类别的一些快速描述。
- 训练数据集 :这是我们用来直接训练神经网络的一组数据集。训练数据是指在训练期间暴露给神经网络的数据集分区。
- 验证数据集 :这组数据集在训练期间被用来评估网络在各种迭代中的性能。
- 测试数据集 :数据集的这个分区在训练阶段完成后评估我们网络的性能。
还需要将数据集中图像的像素强度从 0–255 到 0–1 的值范围进行归一化。
**(train_x, train_y), (test_x, test_y) = keras.datasets.mnist.load_data()
train_x = train_x / 255.0
test_x = test_x / 255.0train_x = tf.expand_dims(train_x, 3)
test_x = tf.expand_dims(test_x, 3)val_x = train_x[:5000]
val_y = train_y[:5000]**
在上面的代码片段中,我们扩展了训练和数据集的维度。我们这样做的原因是,在训练和评估阶段,网络期望图像成批呈现;额外的维度代表一批图像的数量。
下面的代码是我们实现实际的基于 LeNet-5 的神经网络的主要部分。
Keras 提供了实现分类模型所需的工具。Keras 提出了一种顺序 API,用于将神经网络的层堆叠在彼此之上。
**lenet_5_model = keras.models.Sequential([
keras.layers.Conv2D(6, kernel_size=5, strides=1, activation='tanh', input_shape=train_x[0].shape, padding='same'), #C1
keras.layers.AveragePooling2D(), #S2
keras.layers.Conv2D(16, kernel_size=5, strides=1, activation='tanh', padding='valid'), #C3
keras.layers.AveragePooling2D(), #S4
keras.layers.Conv2D(120, kernel_size=5, strides=1, activation='tanh', padding='valid'), #C5
keras.layers.Flatten(), #Flatten
keras.layers.Dense(84, activation='tanh'), #F6
keras.layers.Dense(10, activation='softmax') #Output layer
])**
我们首先将变量’lenet_5_model'
赋给 tf.keras.Sequential 类构造函数的一个实例。
在类构造函数中,我们接着定义模型中的层。
C1 层由线keras.layers.Conv2D(6, kernel_size=5, strides=1, activation='tanh', input_shape=train_x[0].shape, padding='same')
定义。我们使用 tf.keras.layers.Conv2D 类来构建网络中的卷积层。我们传递几个论点,在这里描述为。
- 激活函数 :将神经元的结果或信号转化为归一化输出的数学运算。激活函数是在网络中引入非线性的神经网络的组成部分。激活函数的引入使得神经网络具有更强的表达能力和解决复杂的函数。
其余的卷积层遵循与 C 1 相同的层定义,只是为参数输入了一些不同的值。
在介绍 LeNet-5 架构的原始论文中,使用了子采样层。在子采样层内,取落在 2x2 池窗口内的像素值的平均值,之后,该值乘以系数值。最终结果中会添加一个偏差,所有这些都是在值通过激活函数之前完成的。
但是在我们实现的 LeNet-5 神经网络中,我们使用了TF . keras . layers . average pooling 2d构造函数。我们不向构造函数传递任何参数,因为在调用构造函数时,必需参数的一些默认值已经初始化。请记住,网络中的池化图层的作用是在要素地图穿过网络时对其进行缩减采样。
网络中还有两种类型的图层,平坦图层和密集图层。
展平层是用类构造器TF . keras . layers . flatten创建的。
该层的目的是将其输入转换为一维数组,该数组可以输入到后续的密集层中。
密集层每层都有特定数量的单元或神经元,F6 有 84 个,而输出层有 10 个单元。
最后一个密集图层有十个单元,对应于 MNIST 数据集中的类的数量。输出层的激活函数是 softmax 激活函数。
- Softmax :激活函数,用于导出输入向量中一组数字的概率分布。softmax 激活函数的输出是一个向量,其中它的一组值表示一个类/事件发生的概率。向量中的值加起来都是 1。
现在我们可以编译和构建模型了。
**lenet_5_model.compile(optimizer=’adam’, loss=keras.losses.sparse_categorical_crossentropy, metrics=[‘accuracy’])**
**Keras 通过我们之前实例化的模型对象提供了’编译’方法。compile 函数支持我们在幕后实现的模型的实际构建,该模型具有一些额外的特征,如损失函数、优化器和指标。
为了训练网络,我们利用损失函数来计算网络提供的预测值和训练数据的实际值之间的差异。
伴随着优化算法( Adam )的损失值有助于对网络内的权重进行多次改变。支持因素,如动量和学习率时间表,通过使损失值尽可能接近零,提供了使网络训练收敛的理想环境。
在训练过程中,我们还将在每个时期后使用之前创建的评估数据集分区来验证我们的模型
**lenet_5_model.fit(train_x, train_y, epochs=5, validation_data=(val_x, val_y))**
训练之后,您会注意到您的模型达到了 90%以上的验证准确率。但是,为了更明确地验证模型在未知数据集上的性能,我们将在之前创建的测试数据集分区上评估经过训练的模型。
**lenet_5_model.evaluate(test_x, test_y)
>> [0.04592850968674757, 0.9859]**
在训练了我的模型之后,我能够在测试数据集上达到 98%的准确率,这对于这样一个简单的网络来说相当有用。
下面是本文代码的 GitHub 链接:
**** [## Richmond alake/tensor flow _ 2 _ 教程
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com。](https://github.com/RichmondAlake/tensorflow_2_tutorials/blob/master/13_lenet-5.ipynb)****
我希望这篇文章对你有用。
要联系我或找到更多类似本文的内容,请执行以下操作:
- 订阅我的 YouTube 频道 即将发布的视频内容 这里
- 跟着我上 中
- 在 LinkedIn 上连接并联系我
从头开始用 Java 理解和实现神经网络💻
学习最流行的概念💪有史以来的强类型语言。
虽然听起来有点吓人,但我们将从头开始用 JAVA 创建一个人工智能程序,我会解释所有的概念,你也可以边阅读边编码,建议编写程序而不是 Ctrl+C 和 Ctrl+V。
简单神经网络(图片由作者提供)
神经网络是一种以较小的方式代表人脑的计算系统。黄色的点叫做 " 神经元 ,连接它们的线叫做 " 突触 ,这些概念取自🧠.系统通过**反向传播 的过程调整突触的**【权值】来模拟大脑的学习过程。****
创建这个系统的第一步是制作我们自己的矩阵库,因为矩阵是神经网络的基本数据结构。
我已经创建了一个开源库,与本文中解释的概念相同,如果感兴趣,你可以贡献出来🙌
从头开始用 Java 理解和实现神经网络💻学习最流行的概念…
github.com](https://github.com/SuyashSonawane/JavaNet)
1.矩阵库
我们将创建一个矩阵类,并为其添加必要的功能,如矩阵的加法、减法、转置和乘法
class Matrix
{
double [][]data;
int rows,cols;
}
我们的矩阵类包含 3 个对象变量。数据是一个 2d 双数组来保存矩阵
2 的内容。保存矩阵行数的行
3。您可能已经注意到,这些列包含列!!
public Matrix(int rows,int cols)
{
data= new double[rows][cols];
this.rows=rows;
this.cols=cols;
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
data[i][j]=Math.random()*2-1;
}
}
}
这里,我们创建了一个构造函数,通过将行和列作为参数传递,用-1 和 1 之间的随机值初始化矩阵对象。
public void add(double scaler)
{
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
this.data[i][j]+=scaler;
}
}
}public void add(Matrix m)
{
if(cols!=m.cols || rows!=m.rows) {
System.out.println("Shape Mismatch");
return;
}
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
this.data[i][j]+=m.data[i][j];
}
}
}
这里,我们创建了一个“add”函数,它重载了两个参数,一个 double 和一个矩阵对象,该对象执行元素相加。
public static Matrix subtract(Matrix a, Matrix b) {
Matrix temp=new Matrix(a.rows,a.cols);
for(int i=0;i<a.rows;i++)
{
for(int j=0;j<a.cols;j++)
{
temp.data[i][j]=a.data[i][j]-b.data[i][j];
}
}
return temp;
}public static Matrix transpose(Matrix a) {
Matrix temp=new Matrix(a.cols,a.rows);
for(int i=0;i<a.rows;i++)
{
for(int j=0;j<a.cols;j++)
{
temp.data[j][i]=a.data[i][j];
}
}
return temp;
}
这是两个函数,用于计算矩阵的减法和转置,作为参数发送给类静态函数。这些函数返回新的矩阵对象。
public static Matrix multiply(Matrix a, Matrix b) {
Matrix temp=new Matrix(a.rows,b.cols);
for(int i=0;i<temp.rows;i++)
{
for(int j=0;j<temp.cols;j++)
{
double sum=0;
for(int k=0;k<a.cols;k++)
{
sum+=a.data[i][k]*b.data[k][j];
}
temp.data[i][j]=sum;
}
}
return temp;
}
public void multiply(Matrix a) {
for(int i=0;i<a.rows;i++)
{
for(int j=0;j<a.cols;j++)
{
this.data[i][j]*=a.data[i][j];
}
}
}
public void multiply(double a) {
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
this.data[i][j]*=a;
}
}
}
好吧!这里发生了很多事情🤯第一个乘法函数接受 2 个矩阵对象,并对各个矩阵执行点积运算,然后返回一个新的矩阵对象,第二个函数执行矩阵的逐元素乘法,最后一个函数通过缩放器对整个矩阵进行缩放或相乘。
public void sigmoid() {
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
this.data[i][j] = 1/(1+Math.exp(-this.data[i][j]));
}
}
public Matrix dsigmoid() {
Matrix temp=new Matrix(rows,cols);
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
temp.data[i][j] = this.data[i][j] * (1-this.data[i][j]);
}
return temp;
}
对于这个例子,我们为我们的人工智能🧠.使用了 Sigmoid 激活函数这两个函数将 sigmoid 和 sigmoid 的导数应用于矩阵的元素。计算反向传播的梯度时需要 Sigmoid 的导数。
public static Matrix fromArray(double[]x)
{
Matrix temp = new Matrix(x.length,1);
for(int i =0;i<x.length;i++)
temp.data[i][0]=x[i];
return temp;
}
public List<Double> toArray() {
List<Double> temp= new ArrayList<Double>() ;
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
temp.add(data[i][j]);
}
}
return temp;
}
这些是在矩阵对象和数组之间进行转换的辅助函数。
现在,✌,我们有了一个运行良好且易于调试的,我们自己的——矩阵库!!
2.构建神经网络
现在,神经网络的真正有趣和令人生畏的概念来了,但不要害怕,我们将通过编码来学习这些概念,你肯定会发现它们非常直观。
public class NeuralNetwork {
Matrix weights_ih , weights_ho , bias_h , bias_o;
double l_rate=0.01;
}
这里我们有一个包含许多变量的 NeuralNetwork 类,让我们逐个定义它们。
1。重量 _ih👉输入和隐藏层的权重矩阵。
2。权重 _ho👉隐藏层和输出层的权重矩阵。
3。bias_h👉隐藏层的偏差矩阵。
4。bias_o👉输出图层的偏差矩阵。
5。低利率👉学习率是一个超参数,用于控制权重优化过程中的学习步骤。
public NeuralNetwork(int i,int h,int o) {
weights_ih = new Matrix(h,i);
weights_ho = new Matrix(o,h);
bias_h= new Matrix(h,1);
bias_o= new Matrix(o,1);
}
这里,我们让构造函数在传递给构造函数的输入、隐藏和输出形状的帮助下初始化所有变量。
检查矩阵的形状很重要,因为点积仅适用于兼容矩阵。有关点积的更多信息,请遵循此
public List<Double> predict(double[] X)
{
Matrix input = Matrix.fromArray(X);
Matrix hidden = Matrix.multiply(weights_ih, input);
hidden.add(bias_h);
hidden.sigmoid();
Matrix output = Matrix.multiply(weights_ho,hidden);
output.add(bias_o);
output.sigmoid();
return output.toArray();
}
这里我们有预测函数,它在神经网络上进行前向传递或前向传播
前向传播是神经网络中的一个计算步骤,其中输入(矩阵)与隐藏层的权重(矩阵)相乘,然后偏差(列矩阵)与前一步骤的点积相加。最后,结果由激活函数按元素处理。这是在每一层上执行的,前一层的结果作为下一层的输入。
这一步也称为前向传递,用于从网络生成预测。
单个神经元的正向传递(图片由作者提供)
该函数接受输入的双数组,然后通过我们的帮助函数将它们转换为列矩阵。然后在两个层上计算向前传递,然后通过另一个帮助函数将输出展平到一个列表中。
public void train(double [] X,double [] Y)
{
Matrix input = Matrix.fromArray(X);
Matrix hidden = Matrix.multiply(weights_ih, input);
hidden.add(bias_h);
hidden.sigmoid();
Matrix output = Matrix.multiply(weights_ho,hidden);
output.add(bias_o);
output.sigmoid();
Matrix target = Matrix.fromArray(Y);
Matrix error = Matrix.subtract(target, output);
Matrix gradient = output.dsigmoid();
gradient.multiply(error);
gradient.multiply(l_rate);
Matrix hidden_T = Matrix.transpose(hidden);
Matrix who_delta = Matrix.multiply(gradient, hidden_T);
weights_ho.add(who_delta);
bias_o.add(gradient);
Matrix who_T = Matrix.transpose(weights_ho);
Matrix hidden_errors = Matrix.multiply(who_T, error);
Matrix h_gradient = hidden.dsigmoid();
h_gradient.multiply(hidden_errors);
h_gradient.multiply(l_rate);
Matrix i_T = Matrix.transpose(input);
Matrix wih_delta = Matrix.multiply(h_gradient, i_T);
weights_ih.add(wih_delta);
bias_h.add(h_gradient);
}
现在,在上面的训练函数中,我们将 X 和 Y 作为双数组,我们将它们转换为矩阵,从矩阵形式的目标 Y 中减去前向传递的输出。相减的结果是当前传递的样本的误差。该误差用于计算反向传播的梯度。sigmoid 函数的导数按元素应用于输出矩阵,输出矩阵返回梯度矩阵,然后乘以输出误差和决定学习步骤的学习速率。
反向传播正好与正向传递相反,在正向传递中,我们对权重矩阵进行转置,然后将它们乘以根据误差计算的梯度,这又返回用于调整当前层中权重的增量。使用梯度更新偏差。
神经网络中的反向传播(图片由sebastianraschka.com提供)
当反向支持从输出层运行到输入层时,我们对网络中的前几层重复相同的步骤。
最后,我们已经更新了当前样本的所有层的权重,这完成了数据集上的 1 个步骤。
public void fit(double[][]X,double[][]Y,int epochs)
{
for(int i=0;i<epochs;i++)
{
int sampleN = (int)(Math.random() * X.length );
this.train(X[sampleN], Y[sampleN]);
}
}
fit 函数接受 2 个 2d 数组,即 X 和 Y,以及历元数,即我们需要在数据集上迭代多少次。在这里,我们使用数据集中的随机数据点重复调用 train 函数,以便我们可以在数据集上概化网络。
这完成了我们的神经网络类🥳,现在我们可以在一个简单的数据集上测试我们的程序。
嗨,我是苏亚士·索纳瓦尼👋这是我的网络作品集
suyashsonawane.me](https://suyashsonawane.me/)
3.测试我们的程序
我们将利用 XOR 逻辑门的数据制作一个简单的 2d 双数组。
XOR 逻辑门(图片由作者提供)
static double [][] X= {
{0,0},
{1,0},
{0,1},
{1,1}
};
static double [][] Y= {
{0},{1},{1},{0}
};
现在,我们将创建一个神经网络对象,并在数据集上进行训练
NeuralNetwork nn = new NeuralNetwork(2,10,1);
nn.fit(X, Y, 50000);
我们使用 50000 个历元,因为数据集非常小,只包含 4 个样本。
跨时代的损失(作者图片)
double [][] input ={{0,0},{0,1},{1,0},{1,1}};
for(double d[]:input)
{
output = nn.predict(d);
System.out.println(output.toString());
}//Output
[0.09822298990353093]
[0.8757877124658147]
[0.8621529792837699]
[0.16860984858200806]
现在我们用双数组形式的输入来测试神经网络。输出看起来很棒,因为大于 0.5 的值类似于 1 输出,而另一个类似于 0 输出。
下面给出了所有的源代码**
结论
因此,我们已经成功地从零开始创建了一个神经网络,我们还创建了自己的矩阵库,可以无缝地处理所有的矩阵运算。我们还在一个小的 XOR 数据集上测试了它,得到了非常好的结果。
作为一名 Python 程序员,我对 Java 编程不是很熟悉,因为它需要太多的注意力在数据类型和类型转换上。😓分号;如果你发现了任何不必要或多余的代码,请在评论中分享,如果你有任何建议,请在下面评论。🤗
这就是所有的乡亲们
跟我来:
作品集:https://suyashsonawane.me/
推特:苏亚什·索纳万(@ SuyashSonawane)/推特
LinkedIn: 苏亚什·索纳万| LinkedIn
Github: 苏亚什·索纳万(Suyash Sonawane)