利用知识库、BERT 和句子聚类构建有效的 FAQ
如何识别和暴露重要的知识
成功的企业几乎总是由高质量的知识和专业技能推动的。现代组织通过对话界面(如机器人和专家系统)公开他们的知识,因此客户、合作伙伴和员工可以立即访问推动成功的知识。作为数据科学家和工程师,我们有责任实现这一目标。我们需要回答一个简单的问题:您如何表示业务知识,使其易于使用?有许多方法和可能的策略来展示知识。在这篇文章中,我想深入探究古老而优秀的FfrequencyAskedQquestions system,并讨论如何用最新的人工智能技术实现它。
在史前时代,网站曾经有一个 FAQ 页面,上面有一个冗长乏味的无用问题列表。只有真正的乐观主义者才会搜索这个列表,为他们面临的问题找到可能的解决办法。那些年已经过去了。今天,现代网站有一个用户用来提问的集成机器人。起初,这些机器人非常糟糕,所以用户不愿意,但现在随着对话界面背后的技术(和人工智能)的改善,用户认为机器人和搜索是提问的主要界面。
知识库
像 Azure QnA Maker 和 Google Dialogflow 知识库这样的知识库是人工智能技术的两个例子,你可以用它们来构建一个机器人,从一系列问答对中回答问题。这些服务可以将用户的问题匹配到列表中最合适的条目,并给出正确的答案。您还可以按层次结构组织问题,并构建一个对话流,引导系统找到正确的 QnA 对。这很好,但核心还是有一个问题和答案的列表。某人需要建立和维护的列表。这样的列表不能太长,因为长列表是不可能维护的,尤其是现代商业是动态的,正确的答案一直在变。显然,如果我们想让客户觉得它有用并得到一个有效的答案,这个列表不能太短。那么,我们如何找到平衡,并保持问题的重要性呢?
NLU 问答
问答是 NLU 网络旨在解决的核心任务之一。在过去的几年里,随着 transformer 和 BERT 类架构的引入,NLU 空间发生了革命性的变化。更多细节请阅读我的关于 BERT 及其实现的文章。今天,有了像拥抱脸/变形金刚 QnA 管道这样的项目,我们可以建立 QnA 系统,在文章中找到一个句子,可以被认为是对一个问题的回答,并有一定的信心。这很好,但有两个基本挑战。
1)类伯特网络具有固定大小(有限)的输入大小,因此它不是为处理非常长的文本(即上下文)而设计的,例如书籍甚至文章。
2)答案永远是原文中的一句话。从我们的日常经验中我们都知道,一个好的答案往往至少是原文中多个地方的句子的集合,甚至是一些原文中不存在的新文本。这样的答案应该是从原文表达的思想中构建出来的。尽管这是一个活跃的研究领域,但是到今天为止,NLU 问答网络还不能做到这一点。欲了解更多详情,请搜索 ELI5 研究。对于第一个挑战,有一些部分的解决方案。在高层次上回答一个问题,从一个大的文本语料库,我们使用搜索索引之前,NLU 网络。换句话说,我们首先使用一些搜索索引来索引文档,然后当用户提交问题时,我们使用搜索引擎来检索一些(短的)候选文本,我们将在这些文本上执行 BERT 网络。最后,我们将返回置信度最高的答案。有很多这样的实现,我想推荐由 train 创建的一个。
FAQ 系统架构
直接的结论是,没有一种方法是完美的,因此您可能需要两种方法来开发 FAQ 应用程序。首先,您将与您的领域专家合作,开发一个问答配对列表,假设这些是“经常”被问到的问题,因此它们涵盖了“大多数”客户需求。答案将是深刻和全面的,因为它们是由人类专家开发的。使用上面提到的知识库技术之一,您将实现系统的第一层。用户提交的任何问题都将首先由知识库处理。希望你的假设是正确的,大多数问题将在这里得到成功处理。显然,列表中会有没有答案的问题。这些将延续到基于 NLU 方法的下一层。您将收集并索引所有可能包含客户问题答案的相关文档、手册和文章。当一个问题到来时,系统会找到候选文章并执行类似 BERT 的问题回答来给出答案。第二层的执行时间将比第一层长得多,并且答案不太准确,但是有可能许多答案足以满足最终用户。希望这样一来,未回答问题的比例会非常小。
记录是关键
正如我们所看到的,这个系统是建立在假设之上的。你想不断验证你的假设。要做到这一点,你必须记录每一个出现的问题。这些日志将为您提供关于您的客户真正在寻找什么的宝贵见解,并且同样重要的是将验证您的假设。您将看到问题是如何回答的,由哪一层回答的,甚至可能从最终用户那里获得一些感悟。
如何找到真正的 FAQ
假设您已经记录了所有内容,现在您需要确定需要进一步关注的问题。这些问题在你的知识库中并不存在,而且出现的频率足够高,所以你需要投入时间,找出能在知识库中找到它们的答案。起初,你可能认为统计日志中文本出现的次数是一件微不足道的事情,但是再看一眼你就会发现生活并没有那么简单。令人惊讶的是,用户仅仅问同一个简单的问题,就能想出这么多不同的答案。要处理大量数据,您需要找到所有这些变化,并将它们聚集成有意义的组。一旦聚类,保存它们是很重要的,这样您以后可以将变体输入知识库,因为现代知识库知道如何将不同的变体分组到单个问题中。不过,你需要解决问题聚类的问题。
句子聚类有两个阶段:
- 使用类似 BERT 的语言模型将文本转换成数字向量(即句子嵌入)。
- 使用您选择的聚类算法对向量进行聚类。
为了执行句子嵌入,你需要将你的句子插入到一个类似伯特的网络中,并查看 CLS 令牌。幸运的是, SentenceTransformer 项目完成了所有繁重的工作,因此您可以用两行代码完成这个复杂的任务!!!
第二阶段对于没有多少 ML 经验的人来说应该是简单明了的。我正在使用 sklearn.cluster 的 KMeans。
def RunClustering(userQuestions, num_clusters):# extract the text line from the userQuestions object
scorpus = [item[‘QuestionText’] for item in userQuestions]# convert the text into a numeric vector (i.e. sentence embedding) using a BERT-like language modelembedder = SentenceTransformer(‘bert-base-nli-mean-tokens’)
corpus_embeddings = embedder.encode(corpus)# cluster the vectors
clustering_model = KMeans(n_clusters=num_clusters)
clustering_model.fit(corpus_embeddings)
cluster_assignment = clustering_model.labels_# organize the clusters
clustered_sentences = [[] for i in range(num_clusters)]
for sentence_id, cluster_id in enumerate(cluster_assignment):
clustered_sentences[cluster_id].append(userQuestions[sentence_id])return [{“cluster”: i, “questions”: cluster} for i, cluster in enumerate(clustered_sentences)]
问题聚类的结果
现在,您可以手动分析每个集群,并确定需要解决的问题。可选地,你可以使用像 Faiss 这样的库对每个簇中的向量进行进一步的聚类和相似性搜索。我发现第一级集群已经足够了,但是您的场景可能需要更细粒度的集群。
结论
知识库是支持你的 FAQ bot 的一个很好的工具,但是后端的问答列表不可能覆盖所有的问题,它必须不断地维护。NLU 问答可以填补这一空白,通过句子聚类,您可以识别重要的问题。
我希望这篇文章能帮助你建立下一个成功的 FAQ 系统。
最后说明:为了能够继续发布新的故事,Medium 现在要求作者拥有最低数量的关注者,所以请帮助我继续发布,并按下我名字旁边的“关注”按钮。
利用阿里云构建端到端客户细分解决方案
莎伦·麦卡琴在 Unsplash 上的照片
想象一下,我们有一个销售各种产品的零售店。为了在您的业务中取得更大的成功,我们必须充分了解我们的客户。尤其是在当今竞争激烈的世界。这样我们就可以回答:
-谁是我们最好的客户?
-谁是我们的潜在客户?
-哪些客户需要成为目标并保留下来?
-我们客户的特点是什么?
了解我们客户的一种方法是进行客户细分。细分是根据共同特征将客户分成几组的过程。我们可以使用许多变量来细分我们的客户。客户人口统计、地理、心理、技术和行为等信息通常被用作区分客户的区分因素。
通过在业务中实现客户细分,我们将能够个性化您的战略,以适应每个细分市场的特点。从而可以最大限度地保留客户,改善客户体验,获得更好的广告效果,并最大限度地降低营销成本。
那么,如何才能做到这种客户细分呢?
我们将应用无监督的机器学习技术对零售数据集进行客户细分。我们将使用新近性、频率和货币(RFM)作为客户交易行为的有用指标。
新近性、频率和货币定义。Bima 提供的图片
我们将利用以下产品来构建此使用案例。
-对象存储服务(OSS)。OSS 是一种加密、安全、经济且易于使用的对象存储服务,使您能够在云中存储、备份和归档大量数据,并保证耐用性。
- MaxCompute (之前称为 ODPS)是一个通用的、完全托管的多租户数据处理平台,用于大规模数据仓库。MaxCompute 支持各种数据导入解决方案和分布式计算模型,使用户能够有效查询海量数据集,降低生产成本,保障数据安全。
- DataWorks 是阿里云推出的大数据平台产品。它提供一站式大数据开发、数据权限管理、离线作业调度等功能。此外,它还提供全方位的服务,包括数据集成、DataStudio、数据地图、数据质量和 DataService Studio。
- 面向 AI 的机器学习平台 (PAI)提供端到端的机器学习服务,包括数据处理、特征工程、模型训练、模型预测和模型评估。人工智能的机器学习平台结合了所有这些服务,使人工智能比以往任何时候都更容易获得。
- Data Lake Analytics 是一种利用无服务器架构的交互式分析服务。DLA 使用 SQL 接口与用户服务客户端交互,这意味着它符合标准的 SQL 语法,并提供各种类似的功能。DLA 允许您从多个数据源或位置(如 OSS 和 Table Store)检索和分析数据,以优化数据处理、分析和可视化,从而提供更好的见解并最终指导更好的决策。
我们将首先准备数据,然后进行模型训练,接着创建一个为模型服务的管道。
端到端流程。图片由 Bima 提供
数据准备
首先,让我们了解一下我们的数据。我们使用来自 UCI 机器学习知识库的在线零售数据(你可以从这个链接下载这些数据)。该数据集由 2010 年和 2011 年的交易数据组成。
数据集概述。图片由 Bima 提供
第一步数据收集
我们将使用 2010 年的数据进行模型训练,并将该文件重命名为 transaction_train.csv。然后,作为需要处理的日常数据示例的数据是 2011 年,我们将其重命名为 transaction_daily.csv。我们需要将该数据集存储到阿里巴巴对象存储服务(OSS)中。
第二步数据摄取
然后,我们使用 MaxCompute 和 Data Works 来编排流程。该步骤旨在使用 Data Works 将 OSS 中的数据导入 MaxCompute。
首先,我们需要创建一个表来存储 MaxCompute 中的数据。这个表应该与我们的数据集模式相匹配。我们可以通过右键单击业务流并选择 Create Table 来实现。然后,我们可以使用 DDL 语句创建模式,并遵循这个过程。
****
现在我们已经准备好桌子了。我们需要创建一个数据集成节点,将我们的数据从 OSS 同步到 MaxCompute。在这个节点中,我们需要设置源和目标,然后映射每个列。
然后,我们可以运行该组件将数据从 OSS 接收到 MaxCompute,并将我们的数据放在适当的位置。
步骤 3 数据清理和转换
我们的数据需要在用于模型训练之前进行清理和转换。首先,我们将清除数据中的无效值。然后,我们通过计算每个客户的近期、频率和货币价值来转换数据。
在 DataWorks 中,我们将使用一个 SQL 节点来执行此任务。我们可以首先创建一个新表来存储结果,然后创建节点并编写一个 DML 查询来完成任务。
这项任务的结果是,我们将获得每个客户的 RFM 值。
从原始数据到 RFM 要素的数据转换。图片由 Bima 提供
我们将在模型训练和模型服务过程中使用这个数据准备步骤。在模型训练中,我们只做一次,直到有了模型。但是,这种数据准备将在模型服务管道中每天批处理运行。
模特培训
我们将使用 K-Means 无监督机器学习算法。K-means 聚类是应用最广泛的聚类算法,它将 n 个对象划分为 k 个簇,以保持每个簇内的高相似性。相似性是基于聚类中对象的平均值来计算的。
该算法随机选择 k 个对象,每个对象最初代表一个聚类的平均值或中心。然后,该算法根据剩余对象与每个聚类中心的距离将它们分配到最近的聚类中,并重新计算每个聚类的平均值。这个过程重复进行,直到标准函数收敛。
K-means 聚类算法假设我们从空间向量中获取对象属性,其目标是确保每个组内的均方误差和最小。
在这一步中,我们将使用 PAI studio 创建一个实验,并使用数据科学研讨会(DSW)进行优化。派工作室和 DSW 是阿里巴巴人工智能机器学习平台的一部分。
在 PAI 工作室中创建一个实验相对简单。PAI Studio 已经有几个功能作为组件,我们可以将它们拖放到实验窗格中。然后我们只需要连接每个组件。下图显示了我们将构建的制作模型的实验。
模型训练步骤。图片由 Bima 提供
第一步数据探索
在这一步,我们的目标是理解数据。我们将通过生成描述性统计数据、创建直方图和创建散点图来研究我们的数据,以检查变量之间的相关性。我们可以在读取数据后创建一个组件来完成这些任务。
数据探索结果。图片由 Bima 提供
因此,我们知道我们的频率和货币数据有偏差。因此,我们需要在创建模型之前进行特征工程。
特征工程
我们将进行日志转换来处理数据中的偏差。在使用它进行建模之前,我们还需要对值进行标准化。因为 K-意味着用距离作为度量,我们需要我们的每个参数都在同一个尺度上。为此,我们需要创建一个特性转换组件和标准化组件。
然后在这个节点下,我们应该将标准化的参数保存到一个表中,这样我们就可以在部署过程中使用它。
特征转换数据之旅。图片由 Bima 提供
步骤 3 模型创建
现在是时候创建我们的模型了。我们将使用 K-Means 来发现客户群。为此,我们需要输入集群的数量作为我们的模型超参数。
为了找到最佳聚类数,我们需要使用 Data Science Workbench 使用不同数量的聚类迭代建模,并通过生成肘图来找到最佳值。最佳聚类数是平方和误差开始变平的地方。
DSW 是一个类似木星笔记本的环境。这里我们需要通过编写 python 脚本来创建一个实验,并生成肘图。
找出最佳聚类的肘形图。图片由 Bima 提供
结果,我们发现我们的最佳集群是五个。然后,我们使用它作为 PAI studio 中 K-Means 组件的超参数,并运行该组件。
这个组件的结果,我们对每个客户都有一个 cluster_index。我们还可以将结果可视化为散点图的形式,散点图已经用 cluster_index 进行了着色。该组件还生成一个稍后将被提供的模型。
分割结果。图片由 Bima 提供
步骤 4 保存聚类结果
这里我们将把原始数据与 k-means 组件的结果结合起来。然后,我们通过创建 Write MaxCompute Table 组件将结果保存到 MaxCompute 表中,作为我们的实验输出。
标记客户群
我们将使用 SQL 组件来计算每个聚类的平均新近度、频率和货币,以了解每个聚类的特征,并表示每个聚类的名称。
每个聚类的平均值。图片由 Bima 提供
细分市场详情。图片由 Bima 提供
现在,我们回到 Data Works,创建一个 PAI 节点来运行我们的实验。然后,我们使用一个 SQL 节点编写一个 DML 来对每个集群进行分段。最后,我们创建另一个数据集成节点,将数据从 MaxCompute 发送回 OSS。
数据工作中的业务流程。图片由 Bima 提供
模型培训总结
这是使用阿里云产品(如 OSS、MaxCompute、DataWorks 和人工智能机器学习平台)创建模型训练需要完成的五个步骤。总之,下图显示了从数据准备到模型训练的模型训练架构。
客户细分模型培训架构。图片由 Bima 提供
模型服务
用户新近性、频率和货币数据可能会随着时间的推移而改变。这意味着我们需要根据用户当前的特征不断给他们贴标签。如果我们手动进行分割,这可能是一项具有挑战性的任务。因此,我们将每天一次批量自动部署和创建细分流程。下图总结了服务模型的步骤。
服务于架构的客户细分模型。图片由 Bima 提供
我们首先将日常数据放入 OSS,然后使用 Data Works 服务将这些数据从 OSS 传输到 Max Compute。之后,我们清理和转换数据到 RFM 形式,和以前一样。
然后我们创建一个新的实验作为 PAI 节点。在这个实验中,我们做了相同的特征工程过程,如日志转换和标准化。但是我们使用在训练过程中保存的标准化参数。
接下来,我们使用之前创建的 RFM 模型的预测组件来预测我们的新数据。然后,我们将分段结果与原始参数结合起来,并将它们保存到最大计算表中。
之后,我们创建一个 SQL 节点,用各自的段名标记我们的客户。然后我们将结果以*的形式发送回 OSS。csv 文件,方法是创建一个数据集成节点。下图显示了 Data Works 和 PAI 实验的整体流程。
DataWorks 和 PAI 中的服务业务流模型。图片由 Bima 提供
最后,数据湖分析(DLA)将用于连接开放源码软件文件和外部可视化工具,如 Tableau。数据湖分析(DLA)是一个无服务器的云原生交互式查询和分析服务。因此,我们可以将细分结果提交给我们的业务团队进行监控和使用。下图显示了在 Tableau 中创建的客户细分仪表板。
客户细分仪表板。图片由 Bima 提供
结论
结果,确定了五个集群。那些是忠诚者,潜在,流失,潜在损失,和损失。因此,我们的业务团队可以设计和执行一个营销策略来参与每个细分市场。
在这个过程中,我们还了解到如何结合各种阿里云产品来制作端到端的机器学习管道,并构建业务用例。
文献学
——https://www.alibabacloud.com/product/maxcompute
**——【https://www.alibabacloud.com/product/ide **
**——【https://www.alibabacloud.com/product/data-lake-analytics **
-https://www.alibabacloud.com/product/oss
30 分钟内建立人脸识别模型
实践教程
微调 VGG-16 以建立在三重损失函数上训练的用于面部识别任务的暹罗网络
介绍
在这篇博文中,我将介绍人脸识别模型的一些实现细节。我还设计了一个基于浏览器的 UI,用于向数据库添加一个新人。解释 web 开发部分超出了本博客的范围。
这篇文章假设读者理解连体网络模型和三重损失函数。如果您喜欢先运行模型,那么您可以从我的库这里克隆它。
从库中试用工作模型
这篇博文的结构如下:
- 模型架构
- 数据集
- 三胎生成
- 其他详细信息
- 结论
模型架构
我们都知道从头开始训练卷积神经网络(CNN)需要大量数据和计算能力。因此,我们转而使用迁移学习,根据我们的要求对基于类似数据训练的模型进行微调。牛津大学的视觉几何小组(VGG)已经建立了三个模型——VGG-16、雷斯内特-50 和塞内特-50,用于人脸识别和人脸分类。我使用了 VGG-16 模型,因为它是一个较小的模型,实时预测可以在我的本地系统上工作,无需 GPU。
**注:**为了避免混淆 VGG-16 深度学习模型和牛津视觉几何小组(VGG),我将把后者称为牛津小组。
这个实现将 Keras 中的整个模型与 TensorFlow v1.14 作为后端。我计划在 TensorFlow v2.3 中构建相同的模型,所以我在本地系统中创建了一个 virtualenv 并提取了模型权重。这些提取的权重存储在vgg_face_weights.h5
中,随后加载到未经训练的 VGG-16(在 TensorFlow v2.3 中)网络上,如本文的所示。如果您希望使用 ResNet-50 或 SeNet-50,那么您可以使用 Refik Can Malli 的存储库来获取模型和重量。
VGG-16 模型是在这篇论文中显示的数据集上训练的,他们已经在 2622 张不同的人脸上训练了分类模型。倒数第二层有 4096 个密集单元,我们在其上附加一个 128 个单元的密集层,没有偏差项,并删除包含 2622 个单元的分类/softmax 层。128 密层之前的所有层都被冻结(trainable = False
),只需要训练新加入的密层。
加载 VGG-16 预训练的重量,然后定制模型
现在为了训练这个网络,我们使用一个三重损失函数。三重损失函数采用从上述网络生成的三个 128 维特征。让这三个被称为锚,积极和消极的地方
- 主持人:一个人的形象,将用于比较。
- 正面:与主播同一个人的形象。
- 负面:与主播不同的人的形象。
三重损失函数— 论文
三重态损失试图减少锚和正对之间的距离,增加锚和负对之间的距离。还有另一个参数alpha = 0.2
,它增加了一个余量,从而使训练更加困难,并给出更好的收敛性。参数128D
密集单元和损失函数参数alpha
是根据本论文中的分析选择的。
三重损失函数的实现
总结到现在吧!!VGG-16 网络为我们提供了锚、正和负图像的 128D 特征,这些特征然后被馈送到损失函数。
现在对于训练,一种选择是在每个锚、正面和负面图像上调用相同的模型三次,然后将值赋予损失函数。然而,一个接一个地运行它们并不是一个好主意。因此,我将它们包装在一个扩展了tf.keras.Model
的 Siamese Network 类中,并将并行化留给了 TensorFlow。此外,还有一件事添加到模型中, L2 正则化应用于 128D 密集层的输出。
暹罗网络类
我已经在 SiameseNetwork 类中添加了一个函数get_features
,这只是一个优化,在测试过程中会很有用。
太好了,我们建立了一个模型!!现在让我们来看看用于训练的数据集。
资料组
由 2622 个不同的名人图像组成的 VGGFace 数据集用于训练上面使用的 VGG-16 模型。后来,牛津小组还发布了由 8631 个名人图像组成的 VGGFace2 用于训练,测试中有 500 个,每个都很独特。由于训练集是 39GB,所以我只下载了测试集,是 2BG,训练了最后一个密集层。
虽然使用一个测试集进行训练听起来可能有违直觉,但这是关于他们训练的模型的测试集。至于我,我已经把它作为一个训练集,并在我的家人和朋友身上测试了我的模型。
预处理通常取决于底层模型。因此,为了训练和测试,输入图像必须经过由牛津小组实施的 VGG-16 模型定义的相同预处理。输入到模型中的图像首先通过这篇论文中描述的人脸检测器,然后发送到这里给出的preprocess_input
函数。在我的实现中,我使用了 dlib 库提供的正面人脸检测器,然后将图像发送到preprocess_input
函数。
**注意:**这里定义的预处理 _ 输入函数与 ImageNet 上训练的 VGG-16 使用的不同。因此,在我的存储库中,预处理的代码取自 pip installed VGGFace 库。
现在,我将展示数据集的目录结构,因为它成为在训练期间优化内存的一种方式。让我们先来看看下载的数据集目录结构。在下面的目录结构中,每个目录(n000001,n000009 等。)被分配给一个名人的所有图像。
.
└── vggface2_test
└── test
├── n000001
│ ├── 0001_01.jpg
│ ├── 0002_01.jpg
│ ├── 0003_01.jpg ...
├── n000009
│ ├── 0001_01.jpg
│ ├── 0002_01.jpg
│ ├── 0003_01.jpg ...
(so on and so forth)
如上所述,我们使用 dlib 的正面人脸检测器来检测包含人脸的图像,并将它们存储在一个名为 dataset 的不同文件夹中。下面是人脸检测图像的目录树。本笔记本也有同样的实现。
.
└── dataset
└── list.txt
└── images
├── n000001
│ ├── 0001_01.jpg
│ ├── 0002_01.jpg
│ ├── 0003_01.jpg ...
├── n000009
│ ├── 0001_01.jpg
│ ├── 0002_01.jpg
│ ├── 0003_01.jpg ...
(so on and so forth)
vggface_test 和 dataset 的目录结构几乎是相似的。但是,数据集目录可能包含更少的图像,因为一些面部可能没有被 dlib 的检测器检测到。此外,在数据集目录中有一个文件list.txt
,它包含每个图像的如下数据directory-name/image-name
。这个 list.txt 用于训练时的内存优化。
三胞胎世代
为了训练,模型需要三个图像——锚、正面和负面图像。我脑海中的第一个想法是生成所有可能的三胞胎对。这似乎给出了很多数据,但研究文献表明这是低效的。所以我用了一个随机数发生器来选择锚,积极和消极的图像对。我使用了一个在训练循环中产生数据的数据生成器。如果你不熟悉数据生成器,请参考这个博客。
**有趣的事实:**我花了比模型训练更多的时间来编写 DataGenerator 类。
三元组数据发生器
__getitem__
是最重要的功能。然而,为了理解这一点,让我们也检查一下构造函数和其他方法。
- init: 构造函数采用前面小节中定义的数据集目录的路径。构造函数使用
list.txt
来创建一个字典。这个字典将目录名作为它的键,将目录中的一系列图像作为它的值。正是在这里,在混洗步骤中,list.txt 成为我们了解数据集概况的一种简单方式,从而避免了加载图像进行混洗。 - getitem: 我们从上面的字典键中获取人名。对于第一批,前 32 个(批量大小)人物图像用作锚,同一个人的不同图像用作阳性。从任何其他目录中选择一个负面图像用于训练。对于所有的三胞胎,锚,积极和消极的形象是随机选择的。接下来的 32 人将成为下一批的主播。
- **策展 _ 数据集:**创建在
__init__
中解释的字典 - on_epoch_end: 在每个 epoch 结束时,人的顺序被打乱,因此在下一个 epoch 中,前 32 个图像与前一个 epoch 中看到的图像不同。
- get_image: 获取图像功能在将图像调整到(224 x 224)大小后使用
preprocess_input
。 - len: 这将返回定义一个时期的批次数量。
搞定了。!!
培训和测试
我已经用 tqdm 使用了一个定制的训练循环(你仍然可以感觉到 Keras ),并训练了 50 个时期的模型。在 colab 上,每个历元的训练时间是 **24 秒,**所以是的,训练相当快。
对于测试,您可以将家人、朋友和您自己的图像保存在一个目录中,还可以存储从每个人的密集图层中生成的 128D 特征。您可以使用get_features()
函数,该函数在此处的 SiameseNetwork 类中定义。此外,为了节省您的时间,我制作了一个笔记本 Real-time-prediction.ipynb,,它加载模型检查点,并提供收集图像以进行动态测试并在网络摄像头视频上预测它们的说明。
杂项详细信息
提高 Colab 的训练速度
在 DataGenerator 中,并不是所有的图像都加载到内存中,而是加载它们的索引进行操作。如果您有自己的 GPU,那么这一小节中的细节可能不太相关。
我最初认为从 colab 到 drive 的读写操作应该很快,但结果是它们变得比我的甚至没有 GPU 的本地系统慢。为了解决这个问题,我将数据集压缩到dataset.7z
并上传到我的硬盘上。然后将 zip 文件从我的 Google drive 复制到 colab 的空间,在那里提取,然后用于训练。使用 colab 的空间大大提高了训练过程的速度。
但是,我的 tensorboard 摘要和模型检查点存储在驱动器中,因为它们每个时期都被访问一次,不会显著降低性能。
基于用户界面的工具
我想学习一些网络技术,比如 HTML、CSS 和 Javascript。最好的学习方法是做一个小项目。因此,我试图开发一个基于 UI 的工具来收集测试和预测数据。运行相同程序的步骤在我的库中有解释。
结论
在这篇博客中,我们讨论了关于微调现有网络和在其上构建连体网络的关键细节。当前模型的结果比预期好得多,但是我们也可以通过手动创建好的三胞胎来改进它们。也可以下载整个训练数据集来训练模型。文献表明,手动选择一组硬三元组将显著减少训练时间并提高模型的收敛速度。
你可以参考我的知识库来尝试基于浏览器的工具以及查看培训用的笔记本。该工具还可以检测多人!!
参考
O.M. Parkhi,A. Vedaldi,A. Zisserman,深度人脸识别,英国机器视觉大会,2015。
Q. Cao,L. Shen,W. Xie,O. M. Parkhi,A. Zisserman, VGGFace2:跨姿态和年龄的人脸识别数据集 **,**自动人脸和手势识别国际会议,2018。
F.Schroff,D. Kalenichenko,J. Philbin, FaceNet:用于人脸识别和聚类的统一嵌入, CVPR,2015 年。
G.Koch,R. Zemel,R. Salakhutdinov,用于一次性图像识别的连体神经网络, ICML 深度学习研讨会。第二卷。2015.
使用 Keras Functional Framework v2+模型的 Oxford VGGFace 实现是从原来的 caffe 网络转换而来的…
github.com](https://github.com/rcmalli/keras-vggface) [## 使用 Keras 的数据生成器的详细示例
python keras 2 fit _ generator Afshine Amidi 和 Shervine Amidi 的大型数据集多重处理您是否曾经不得不…
stanford.edu](https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly) [## 在 Google Colab 上加速你的图像训练
获得一个因素 20 加速训练猫对狗分类器免费!
medium.com](https://medium.com/datadriveninvestor/speed-up-your-image-training-on-google-colab-dc95ea1491cf) [## 用 PyTorch - neptune.ai 中的连体网络实现基于内容的图像检索
图像检索是寻找与给定查询相关的图像的任务。对于基于内容的图像检索,我们指的是…
海王星. ai](https://neptune.ai/blog/content-based-image-retrieval-with-siamese-networks)
培养习惯并进入数据科学领域
苹果 | 谷歌 | SPOTIFY | 其他
拉塞尔·波拉里在 TDS 播客上
编者按:迈向数据科学播客的“攀登数据科学阶梯”系列由 Jeremie Harris 主持。Jeremie 帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:
我们大多数人都想改变自己的身份。我们通常对自己有一个理想化的版本,我们渴望成为——一个更健康、更聪明、更健康、更有名、更富有、更集中的人,或者其他人。
但是如果不改变你在日常生活中所做的事情,你就无法从根本上改变你的身份。不经常锻炼,你不会变得更健康。不定期学习是不会变聪明的。
要改变自己,首先要改变习惯。但是你是怎么做到的呢?
最近,像《原子习惯》和《深度工作》这样的书集中在用一般术语回答这个问题,它们绝对值得一读。但是,在数据科学、分析、机器学习和创业的背景下,习惯的形成伴随着一系列独特的挑战,其本身就值得关注。这也是为什么我想和今天的嘉宾拉塞尔·波拉里坐在一起。
拉塞尔现在可能是世界上最大的收入分享导师市场的首席技术官(也是我每天工作的公司!)但他曾经——不久前——是一名几乎没有任何编程能力的物理学博士生,是研究生院忧郁的典型案例。为了取得今天的成就,他不得不学习很多东西,在他寻求优化这一过程的过程中,他将很多注意力放在了在技术、数据科学和创业背景下的习惯养成和自我完善上。以下是我最大的收获:
- 不是每个人都适合读研,中途放弃读研计划也不会让你失败。知道什么时候放弃很重要,这样你就不会为了一个你可能并不感兴趣的研究项目而牺牲掉接触现实世界问题的宝贵时间。
- 优秀的数据科学家像工程师一样思考,而不是物理学家。物理学家希望所有事情都从第一原理出发进行推理(如果他们像我一样,他们甚至有从头开始编写 ML 模型的冲动!).工程师乐于使用他们还不太了解的现成工具,这样他们就可以从现实世界的反馈中学习并快速迭代。当你需要的时候,你总能学到你需要的理论。
- 在数据科学中构建项目的价值很大一部分是对你需要学习的东西施加约束。这个空间如此广阔,如此开放,以至于很容易在兔子洞里迷路,所以限制你正在构建的范围——以及你需要专注的技能范围——是避免旋转轮子的一个好方法。
- 为了投身于工业,Russell 必须学习的两件最重要的事情是:1)版本控制,以及 2)学习如何编码以便其他人能够理解他的工作(例如,不使用
thing_1
和x_12
作为变量名)。有趣的是,这两者都反映了由于大多数人的研究生院环境的孤独性质而产生的盲点:当你独自工作时,很容易注意不到你的代码有多糟糕,因为你知道你所有有趣的变量名意味着什么。 - 养成习惯的一个很好的方法是采取创业的心态,从小处着手。让你每天编码或申请工作的最基本的可行习惯是什么?答案可以(而且经常应该!)声音小得让人觉得有点可笑。“我每天会花 5 分钟申请工作”一开始看起来似乎是一件毫无意义的事情,但是好习惯会随着时间的推移而复合。
你可以点击这里在 Twitter 上关注 Russell,你也可以点击这里在 Twitter 上关注我
订阅《走向数据科学》的月刊,直接在你的邮箱✨中接收我们最好的文章、视频和播客
利用 Google Cloud AutoML 进行建筑物图像检测
零编码知识如何搭建图像检测 app
就在最近,谷歌推出了谷歌自动标记语言(https://cloud.google.com/vision/overview/docs#automl-vision),这使我们能够不用任何代码就能建立图像分类甚至物体检测。该功能不仅仅限于培训,还包括部署,无论是云计算还是边缘计算,如 android、ios 或 web 浏览器。
在这篇文章中,我将演示如何用 AutoML 视觉建立图像分类。今天我们要建立一个零食分类器,一个机器学习系统,能够对图片中的零食进行分类。为了简单起见,我们把可能的零食限定为五个选项(以后我们将此称为标签,分别是:好时光、 Piattos 、 Cheetos 、 Qtela 和 richeseNabati。我选择这些小吃并没有什么特别的原因,这完全是我在超市过道上随意浏览的结果。
我的零食并排放着,等着被吃掉。图片作者。
入门
在我们开始之前,需要准备三样东西,
- 快餐
- 捕捉小吃图像的设备
- Google Cloud 帐户(如果您没有帐户,您有资格获得长达 1 年的 300 美元免费积分)
图像采集
为了在 Google Cloud AutoML 中构建图像分类器,我们需要为每个标签准备至少十张图像。由于我有点精力充沛(我想这是因为我的拿铁咖啡里的糖),我为每个标签收集了总共 30 张图片。为了有好的图像分类器,建议你捕捉不同角度的物体。
在这种情况下,我用手机拍了照片,然后把它们传输到我的笔记本电脑上。在您的笔记本电脑中有图像后,我们可以继续下一步。
图像标记
对于这一部分,直到本文结束,我们将进入谷歌云控制台。
进入云愿景的切入点
首先登录您的谷歌云控制台,然后转到视觉部分。你可以通过点击汉堡 图标(在谷歌云平台旁边的左上方)→人工智能→视觉,或者更简单的方法是使用控制台中的搜索栏。
创建新数据集框
我们需要做的第一件事是创建数据集。当我们创建数据集时,有三个选项可供选择:
- 单一标签分类 每张图片只有一个标签,即山/湖/海滩。
- 多标签分类 每幅图像都有多个标签,例如,我们希望从一幅画中预测画家、流派和情感。
- 物体检测 识别图像中的每一个物体,例如,我们想从一张图像中找到每一辆卡车/公共汽车/小汽车。
在我们的例子中,每个图像都有一个标签(Cheetos/qtela/等。),所以我们就用**单标签分类。**创建新的数据集后,下一步将是导入数据。
导入数据集页面
在导入数据集页面中,我们需要做两件事。首先,我们需要上传图片,然后设置谷歌云存储桶,我们将保存我们的文件。
添加可能的标签
图像上传需要一段时间才能完成,尤其是当我们上传大量高质量的图像时。当上传过程完成时,我们可以在图像选项卡中看到所有图像。一开始,我们没有标记任何数据,所以我们可以看到在未标记的部分有 150 个图像。在我们将图像分配给标签之前,我们需要先创建标签,为此点击添加新标签
将每个图像分配给标签
添加标签后,我们可以将图像分配给标签。选择属于一个标签的多个图像,然后点击**分配标签,**选择正确的标签,然后瞧~。
重复这个过程,直到我们完成标记所有的图像。我知道这是一个缓慢、乏味和痛苦的过程,尤其是当你有大量图像的时候。请耐心等待,因为拥有大量正确标记的图像是拥有良好图像分类器的关键。
完成指定的所有图像
唷!在这一阶段,我们已经完成了所有 150 张图片的标签,下一步可能是最有趣的部分,那就是“鼓声效果”
…构建我们的图像分类器
模特培训
在我们进行模型训练之前,一个至关重要的部分是将我们的数据集分成三个部分:
- 我们将用来训练模型的训练集
数据。 - 用于超参数调整和寻找最佳模型的验证集
数据。 - 测试最终数据,看看我们的模型在现实世界中的表现有多好。
当我们标记我们的图像时,Cloud Vision 已经自动将图像分配给训练/验证/测试。虽然有一个自动的特性很好,但是到目前为止,我们还没有办法将一个图像从训练转移到验证或者从验证转移到测试集。
我们可以看到,平均来说,我们在训练中有大约 24 幅图像,在验证中有大约 3 幅,在测试中有大约 3 幅。虽然这个数字很低,但对于我们的演示来说已经足够了。
配置模型名称,如果我们没有训练任何模型,我们将不会在图像的左侧看到“snack_single_label”模型。
好了,我们终于建立了真正的机器学习模型。对于学习步骤,我们需要做两件事。首先,我们需要设置我们的模型名称(在这种情况下,我使用 snack_single_label ),然后决定如何部署模型,选项有:
- 云托管
- 该模型将部署在谷歌云内,我们可以使用 gcloud UI,gcloud CLI 或 REST API 访问该模型
- 边缘
- 我们将能够下载模型并在 edge 设备中运行,比如安卓、ios 甚至浏览器
在这个演示中,我们将在 google cloud 内部部署,所以我们选择第一个选项
AutoML 的独特之处在于,谷歌会自动训练不同的模型架构和参数,并为我们提供性能最佳的模型。我们唯一需要配置的是我们能够负担的节点小时数。最小值是八个节点小时(使用八个核心机器一个小时)。
如果我们使用一个新的谷歌云账户,我们将有免费的 40 个节点小时来训练我们的 ML 模型。因为我们还有一个空闲层,所以我们选择最小节点小时数。
我们填写完节点工时预算后,点击开始培训,等待培训结束。我们可以在节点小时预算文本框下找到培训何时结束。
模型评估
在我们部署(或服务)我们的机器学习模型之前,最后一步是找出我们的模型在真实世界场景中的表现有多好。
我们可以通过进入评估选项卡来了解我们的模型有多好。有一些有用的指标,例如:
- 精度
在所有的正面预测结果中,有多少百分比实际上是正面的? - 回想一下
在所有实际的阳性病例中,我们能预测多少百分比是阳性的?
我尽量保持这种精确和召回尽可能短,更详细的解释你可以看看这里的谷歌机器学习课程(https://developers . Google . com/machine-learning/crash-course/classification/precision-and-recall)。 - 混淆矩阵
精确度和召回率给出的是百分比,混淆矩阵给出的是更精细的结果。行显示图像的真实标签,而列显示预测结果。让我们举一个例子,第 3 行第 2 列表示有多少奇多图像被模型识别为 qtela。
如果我们看一下指标,我们有 100%的准确率和 100%的召回率,这是非常好的,但是因为我们的测试集中只有非常小的数据集,所以很难相信这些数字
模型服务
当我们决定我们的模型足够好的时候,我们终于可以部署模型了(耶!),为此,让我们转到测试&使用选项卡。
模型服务页面
在尝试我们的模型之前,我们需要首先部署它。请注意,因为我们没有在培训后不勾选将模型部署到 1 个节点的复选框,所以我们需要通过单击部署模型来手动部署它
单击“部署模型”后的“部署模型”框
为了进行部署,我们需要设置将要使用的节点数量。Cloud vision 已经告诉我们每个节点每秒大约可以处理多少个请求,所以只需要估计进入这个 API 的流量有多大。
例如,在此图中,我们可以看到,对于每个节点,它可以每秒处理 3.2 个请求,因此,如果我们的生产流量约为每秒 10 个请求,可以肯定地说,我们至少需要 4 个节点来处理流量。
部署模型需要几分钟时间,完成后,您将看到下面的页面。
部署过程完成后使用测试页
在所有这些数据准备、训练和部署之后,我们最终能够测试我们的图像分类器。模型有多种使用方式,我们可以使用 REST API,也可以使用 google vision python 库,还可以在这个页面上传一张图片。
让我们上传一张图片,把 REST API 和 python 客户端留待另一天:d .我要在我们的训练/验证/测试集之外拍一张新的图片,希望模型会返回正确的预测。
耶模型正确地将图像分类为奇多
我上传了我拿着一个奇多的图片,幸运的是,这个模型正确的归类为奇多(唷!~)
结论
虽然 Google Vision AutoML 被证明易于训练和部署,但在使用它之前,您需要考虑一些限制。
- 昂贵的
所有这些无代码功能都需要一些成本,云托管培训的最低价格为每小时25.2美元(最低为每小时八核,每核3.15美元)。
对于部署,我们至少需要在 1 个节点中部署,即每小时1.25美元,这意味着每月900美元。 - 完全黑箱
虽然我们可以方便地让 AutoML 决定什么是最好的模型,但是我们无法控制模型的构建和部署。最重要的是,我们不能下载模型并在本地理解模型。 - 云部署没有自动扩展
到目前为止,没有在云部署中启用自动扩展的选项。对于那些希望在面向客户的生产环境中实现这一点的人来说,这可能是一个倒胃口的事情
这就是我们今天旅程的结束。对于下一个主题,我将写关于 Google AutoML 视觉中的对象检测,以及我们如何使用 tensorflow 2.0 在本地重新创建这个零食分类器。我希望你喜欢这篇文章,如果你有任何意见或想法,请随时给我留言
使用 Excel 构建交互式仪表板
使用数据透视表在 Microsoft Excel 上构建交互式仪表板的分步介绍
曾经想免费构建一个仪表板,但找不到合适的工具?如果是,那么这篇文章无疑是写给你的。流行的仪表盘软件/工具如 Tableau、Power BI 和 Infogram 为交互式仪表盘提供了优秀的平台。但是它们中的大多数需要高级/付费会员资格来构建和与他人共享仪表板。
在这种情况下,有几个备选方案,Microsoft Excel 是可能的备选方案之一(与其他工具相比,它确实有一些缺点,我将在本文末尾讨论)。
在本文中,我将一步一步地详细介绍如何使用 Excel 构建交互式仪表板。由于有很多关于新冠肺炎及其趋势的讨论,我决定参照给定的数据建立一个跟踪全球案例的仪表板。
注:
- 这篇文章假设你对使用 Excel 创建数据透视表和图表有很好的了解。
- 正在使用 Microsoft Excel 版。因此,这里显示的一些功能可能与旧版本不兼容。
开始之前,这里有一个最终仪表板的预览:
所以我们开始吧!
简单地说,我们需要分别设计单独的图形/视觉效果,然后在一个新的工作表中安排一切。
第一步:
如果您现在猜对了,有 3 个不同的数据集(病例、死亡和康复)需要关联。这是必需的,因为仪表板中的过滤器会影响所有 3 个数据集的图表/表格。对此有不同的方法,我将讨论其中一种方法(通过创建数据模型)。
首先,通过从 cases 表中复制 Country/Region 列来创建所有国家的列表,并删除所有重复项。(数据->删除重复)。我们现在将该列表与所有 3 个表相关联,因此将使用该列表在仪表板中应用过滤器。
接下来,将所有 3 个数据集格式化为表格(Ctrl+t ),然后转到第一个表格并选择 Power Pivot 菜单->添加到数据模型。对第二个、第三个表和国家列表重复同样的操作。现在,将 countries 列表中的 country/region 列拖动并连接到所有 3 个表的 country/region 列。一旦一切都完成了,我们将得到以下内容:
现在,我们已经使用 power pivot 视图成功地连接了所有 3 个表。
第二步:
要使用我们创建的数据模型插入数据透视表,请单击空白单元格,然后插入->数据透视表->使用此工作簿的数据模型。
使用上面的过程,创建数据透视表(如果你对这 5 个步骤不熟悉,请参考一些数据透视表教程):
- 确诊病例总数
- 死亡总数
- 回收总额
- 国家/地区的病例数
- 病例数的时间序列
第三步:
从创建的数据透视表中插入国家/地区案例数量和时间序列的图表。Excel 只有几个图表选项可以直接插入数据透视表,因此地图不能直接插入。因此,通过引用数据透视表的各个单元格创建另一个表,然后使用新表插入地图(我们引用数据透视表的值,因为只有这样,仪表板中应用的任何更改/过滤才会影响图表)。
查看公式栏以供参考
第四步:
为最终仪表板添加新工作表,并粘贴所有创建的视觉效果。对于病例、死亡和康复的总数,请引用相应数据透视表中单元格的值。
第五步:
现在我们将插入一个切片器,使用国家/地区来过滤值。为此,插入->切片器->数据模型,然后选择数据模型。然后从我们创建的 countries 表中选择 country/region 列。
现在我们的切片机准备好了。
第六步:
是时候做些最后的润色了:
- 首先,安排所有的视觉和切片在一个适当的格式。
- 添加颜色并为您的仪表板添加标题
- 检查文本的对齐方式和字体大小。
- 隐藏所有其他工作表,只保留仪表板
- 最后,转到视图菜单,删除公式栏、标题和网格线。
仪表板准备好了!
结论
我们已经讨论了如何使用 Excel 构建交互式仪表板。这是一种简单的方法,可以很容易地应用于任何其他数据。正如我们所看到的,Excel 仪表板简单且易于构建,具有许多功能,最重要的是,它可以免费构建并与他人共享。但是正如我前面提到的,与 Tableau、Power BI 和 Infogram 等其他高级工具相比,使用 Excel 作为仪表板有很多缺点。一些缺点是:
- 与 excel 相比,高级工具为仪表板提供了更好的 UI/UX。
- 与其他工具不同,Excel 仪表板不能直接链接到 web,并且您每次都需要共享 excel 文件。当其他人使用不同版本的 Excel 时,这有时可能会导致问题。
- 高级工具提供了更好的交互性。
- 如果文件变大,Excel 有时会开始崩溃/变慢。
还有许多其他因素使高级工具更适合仪表板,因此 Excel 不是完全的替代品。但是,如果您正在为个人项目或任何其他小规模用例寻找一些经济高效的解决方案,Excel 无疑是一个不错的选择。因此,知道如何使用 Excel 构建有用的仪表板是一项值得掌握的技能,我希望本文为此提供了一个很好的介绍。
请随意评论任何关于这方面的帮助,我很乐意帮助你。
在这里找到最后使用的 Excel 文档。(下载文件,并取消隐藏所有的工作表,看看工作)
用 Python 构建交互式地图——初学者指南
使用气候数据的简单实践
欢迎来到用 Python 构建交互式地图的初学者指南
在这篇文章中,我将向你展示如何使用历史气候数据创建交互式气候地图,在这里你可以可视化、检查和探索这些数据。数据可视化在表示数据方面起着重要的作用。创建可视化有助于以更容易理解的形式呈现您的分析。尤其是在处理大型数据集时,很容易迷失方向,这时我们就可以看到数据可视化的威力了。在本练习中,我们将使用 Kaggle 的气候数据。我们将制作两张交互式气候地图。第一个将显示每个国家的气候变化,第二个将显示温度随时间的变化。让我们开始吧,我们有很多事情要做!
Kaggle 是世界上最大的数据科学社区,拥有强大的工具和资源来帮助您实现数据科学目标。
目录:
- Plotly
- 理解数据
- 数据清理
- 数据预处理
- 数据可视化
Plotly
Plotly 是一个 Python 图形库,可以制作交互式的、出版物质量的图形。如何制作折线图、散点图、面积图、条形图、误差线、箱线图、直方图、热图、支线图、多轴图、极坐标图和气泡图的示例。它也是一个开源库。
了解更多关于 Plotly: Plotly 图形库
理解数据
伯克利地球表面温度研究结合了来自 16 个现有档案的 16 亿份温度报告。它被很好地打包,并允许分割成有趣的子集(例如按国家)。他们发布源数据和他们应用的转换代码。
数据集可在以下链接找到:气候数据
数据文件夹包括以下数据集:
- 按国家分列的全球平均陆地温度
- 各州全球平均陆地温度
- 主要城市的全球陆地温度
- 按城市划分的全球土地温度
- 全球陆地和海洋与陆地温度
我们将使用“按国家划分的全球平均陆地温度”数据集,该数据更符合我们的目标,因为我们将构建交互式气候地图,按国家过滤数据将使我们的生活更加轻松。
图书馆
我们需要三个主要的库来开始。当我们谈到可视化时,我会要求您导入更多的子库,也称为库组件。现在,我们将导入以下库:
import numpy as np
import pandas as pd
import plotly as py
如果您没有这些库,也不用担心。安装它们非常容易,如下图所示:
pip install numpy pandas plotly
读出数据
df = pd.read_csv("data/GlobalLandTemperaturesByCountry.csv")print(df.head())
头
print(df.tail())
尾巴
# Checking the null values in each column
df.isnull().sum()
无效的
数据清理
数据科学更多的是理解数据,数据清洗是这个过程中非常重要的一部分。什么让数据更有价值,取决于我们能从中获得多少。做好数据准备会让你的数据分析结果更加准确。
先说清洁流程。首先,让我们从删除“AverageTemperatureUncertainty”列开始,因为我们不需要它。
df = df.drop("AverageTemperatureUncertainty", axis=1)
然后,让我们重新命名列名,以便看起来更好。正如你在上面看到的,我们使用了一种叫做重命名的方法。重命名列名是不是很容易?
df = df.rename(columns={'dt':'Date'})
df = df.rename(columns={'AverageTemperature':'AvTemp'})
最后,对于数据清理,让我们删除具有空值的行,这样它们就不会影响我们的分析。如前所述,我们在 AverageTemperature 列中有大约 32000 行的值为空。我们总共有大约 577000 行,所以删除它们没什么大不了的。但是在某些情况下,有一些其他的方法来处理空值。
df = df.dropna()
现在,让我们看看我们的数据框架。我将使用 head 方法打印前 10 行。
df.head(10)
结果
数据预处理
这一步也被称为数据操作,我们过滤数据,以便我们可以专注于特定的分析。尤其是在处理大数据集时,数据预处理/过滤是必须的。例如,我们的历史气候数据显示了从 1744 年到 2013 年所有 12 个月的温度,所以这实际上是一个非常大的范围。使用数据过滤技术,我们将集中在一个更小的范围内,如 2000 年至 2002 年。
比较运算符
- <
- <=
-
=
- ==
- !=
我们将使用这些运算符将特定值与列中的值进行比较。结果将是一系列的布尔值:真和假。如果比较正确,则为真,如果比较不正确,则为假。
分组依据
在这一步中,我们按照国家名称和日期列对数据帧进行分组。此外,按日期从最近到最早对值进行排序。
df_countries = df.groupby( ['Country','Date']).sum().reset_index().sort_values('Date', ascending=False
)
结果
通过数据范围屏蔽
start_date = '2000-01-01'
end_date = '2002-01-01' mask = (df_countries['Date'] > start_date) & (df_countries['Date'] <= end_date) df_countries = df_countries.loc[mask] df_countries.head(10)
结果
正如你在上面看到的,数据框看起来很棒。按日期排序并按国家名称过滤。通过查看这个数据图表,我们可以找到每个国家每个月的平均温度。有趣的部分来了,这就是数据可视化。你准备好了吗?
数据可视化
Plotly 的组件
在我们开始之前,如前所述,有几个子库需要导入以享受数据可视化。这些子库也称为组件。
#Plotly Componentsimport plotly.express as px
import plotly.graph_objs as go
from plotly.subplots import make_subplots
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
气候变化图
很好,现在通过运行下面的代码,你将会看到奇迹的发生。
#Creating the visualization
fig = go.Figure(data=go.Choropleth( locations = df_countries['Country'], locationmode = 'country names', z = df_countries['AvTemp'], colorscale = 'Reds', marker_line_color = 'black', marker_line_width = 0.5, ))fig.update_layout( title_text = 'Climate Change', title_x = 0.5, geo=dict( showframe = False, showcoastlines = False, projection_type = 'equirectangular' ) ) fig.show()
气候变化互动地图
按时间线划分的气候变化
# Manipulating the original dataframe
df_countrydate = df_countries.groupby(['Date','Country']). sum().reset_index() #Creating the visualization
fig = px.choropleth(df_countrydate, locations="Country", locationmode = "country names", color="AvTemp", hover_name="Country", animation_frame="Date" ) fig.update_layout( title_text = 'Average Temperature Change', title_x = 0.5, geo=dict( showframe = False, showcoastlines = False, )) fig.show()
结果
两张图是一样的,在第一张图中你可以看到平均温度的变化。在第二张图中,我将鼠标悬停在一些国家的上方,这将显示每个国家的更多详细信息。
互动地图 1
互动地图 2
感谢你阅读这篇文章,我希望你喜欢并且今天学到了一些新的东西。如果您在执行代码时有任何问题,请随时通过我的博客联系我。我非常乐意帮忙。你可以找到更多我发表的与 Python 和机器学习相关的帖子。保持安全和快乐的编码!
我是贝希克·居文,我喜欢分享关于创造力、编程、动力和生活的故事。
相关职位
使用亚马逊股票数据的简单实践
towardsdatascience.com](/python-for-finance-the-complete-beginners-guide-764276d74cef) [## 用 Python 构建语音识别器
使用谷歌云语音 API 将您的音频文件转换为文本
towardsdatascience.com](/building-a-speech-recognizer-in-python-2dad733949b4)
构建 Jarvis,一个有态度的生成型聊天机器人
我使用了一个在亚马逊 SageMaker、亚马逊 Polly、谷歌语音 API 和一个音频路由工具上训练的神经机器翻译模型,来构建 Jarvis——一个可以在视频会议电话中与你交谈的聊天机器人。
我工作的公司 Carsales.com 正在举办一场黑客马拉松活动。这是一年一度的活动,每个人(技术或非技术)都聚集在一起,组成一个团队,建造任何东西——任何东西。嗯,最好你能建立一些有商业目的的东西,但这真的取决于你。这个聊天机器人的想法实际上来自 carsales.com 公司的首席信息官杰森·布莱克曼。
Carsales 黑客马拉松
鉴于我们的下一个黑客马拉松是一个在线活动,感谢新冠肺炎,如果我们能举办一个 Zoom 网络研讨会,任何 carsales.com 的员工都可以加入进来,与一个我们可以称为贾维斯的人工智能机器人聊天,他将永远与你聊天,这不是很酷吗?
头脑风暴
经过反复思考,我想出了一个高层次的范围。Jarvis 需要有一个视觉形象,就像网上研讨会的参与者一样。他需要能够倾听你说的话,并用声音回应上下文。
我希望他的回复尽可能有创意,并且能够即时回复。大多数聊天机器人系统是基于检索的,这意味着它们有数百或数千个准备好的句子对(源和目标),这些句子对形成了它们的知识库。当机器人听到一个句子时,它会试图从知识库中找到最相似的源句子,并简单地返回配对的目标句子。基于检索的机器人,如亚马逊 Alexa 和谷歌 Home,更容易构建,并且可以很好地完成特定的任务,如预订餐厅或开灯或关灯,而对话范围有限。然而,出于娱乐目的,如闲聊,他们的回答与生成性回答相比缺乏创造性。
出于这个原因,我想为 Jarvis 创建一个基于生成的系统。我充分意识到我很可能不会取得好结果。但是,我很想知道现在的生成式聊天机器人技术已经走了多远,能做什么。
建筑
好吧,我知道我想要什么。现在是时候认真思考我到底该如何建造这个机器人了。
我们知道,第一个需要的组件是路由音频和视频的机制。我们的机器人需要能够听到 Zoom 上的对话,所以我们需要一种方法将音频从 Zoom 发送到我们的机器人。然后,这个音频需要被传递到语音识别模块,这将为我们提供文本形式的对话。然后,我们需要将这些文本传递到我们的生成式人工智能模型中,以获得回复,通过使用文本到语音技术,回复将被转换为语音。当播放音频回复时,我们需要一个动画化身,除了坐立不安之外,还可以与音频播放同步移动他的嘴唇。化身动画和音频回放需要被发送回缩放,以便所有与会者听到和看到。哇!这确实是一个相当复杂的系统。
贾维斯的建筑图
总而言之,我们需要以下组件:
- 音频/视频路由
- 语音识别
- 生成人工智能模型
- 文本到语音
- 动画化身
- 控制器
音频/视频路由
我喜欢别人为我做艰苦的工作。 Loopback 是一款音频工具,允许你将音频从任何应用程序重定向到虚拟麦克风。我只需要两条音频路径。第一个是将音频从 Zoom 应用程序发送到一个虚拟麦克风,我的机器人可以从这个麦克风中监听。
音频路由 1 图
第二个路由是将聊天机器人的音频输出路由到另一个虚拟麦克风,Zoom 应用程序和我们的头像工具都可以听到。很明显,Zoom 需要监听这段音频。然而,为什么我们的头像工具需要这个音频呢?嘴唇同步,这样我们的化身就可以根据音频播放移动他的嘴唇。在这篇博客的后面部分,你会看到更多的细节。
音频路由 2 图
语音识别
该模块负责通过虚拟麦克风处理来自 Zoom 的音频,并将其转换为文本。有几个离线和在线语音识别框架可供选择。我最终使用的是谷歌语音 API。这是一个在线 API,具有令人敬畏的 python 接口,提供了极高的准确性,更重要的是,允许您以块的形式传输和识别音频,从而大大减少了处理时间。我想强调的是,延迟(机器人响应查询所需的时间)对于聊天机器人来说非常重要。一个反应缓慢的机器人可能看起来非常机器人化和不切实际。
大多数情况下,谷歌语音 API 会在一个句子被完全听到后不到一秒钟内返回一个响应。
生成人工智能模型
这是我花了大部分时间的部分。在花了一两天时间了解生成聊天机器人技术的最新发展后,我发现神经机器翻译模型最近似乎非常流行。
其概念是向编码器-解码器 LSTM 模型提供来自输入句子的单词嵌入,并且能够生成上下文输出句子。这种技术通常用于语言翻译。然而,考虑到这项工作只是简单地将一个句子映射到另一个句子,它也可以用于生成对一个句子的回复(理论上)。
神经机器翻译模型体系结构
通俗地说,就是把一个输入的句子分解成单词。然后,每个单词被映射到一个整数 id,该整数 id 随后被传递到嵌入层。在训练期间,嵌入层学习将 id 列表转换成嵌入向量列表,其大小为“x”维。这个向量是以这样一种方式构建的,即具有相似含义的单词产生相似的向量,这将提供更深层次的信息,而不仅仅是单个整数值。这些向量然后被传递到 LSTM 编码器层,该层将它们转换成思维向量(有些人称之为潜在向量),其中包含关于整个输入句子的信息。请注意,有一个流行的误解,认为有许多 LSTM 层或块,而事实上只有一个。上图中的许多块显示了同一个 LSTM 块在一个接一个的逐字处理后被一次性调用。
模型右手边的解码器负责把这个思维向量变成输出句子。句子的特殊开头单词作为初始输入与思想向量一起被传递到 LSTM 层,以生成第一个单词,该第一个单词作为输入被转发到相同的 LSTM 层,以生成下一个单词,等等。
解码器输出 Softmax
稍微深入到技术领域,LSTM 解码器单元的输出实际上是一个传递到 softmax(分类)层的数字,该层返回我们词汇表中每个可能单词的概率。具有最高概率的字(在上面的情况中,它是“I”)是被挑选出来作为输出字的一个,并且也作为输入被传递到 LSTM 解码器层以生成下一个字。
网上有一些关于如何构建这种模型架构的例子。然而,如果别人已经为你做了艰苦的工作,为什么还要建立一个呢?介绍:亚马逊 SageMaker !Amazon SageMaker 是一个工具和管道的集合,可以加速构建 ML 模型,并带有大量令人惊叹的内置算法,如图像分类,对象检测,神经风格转移和 seq2seq,它是神经机器翻译的一个近似变体,但具有额外的注意力机制。
亚马逊 SageMaker
亚马逊 SageMaker seq2seq 模型是高度可定制的。您可以选择使用多少 LSTM 单位、隐藏单元的数量、嵌入向量维数、LSTM 层数等。,这给了我足够的灵活性来试验不同的参数以获得更好的结果。
获取训练集
训练集的选择对于你的聊天机器人成功地做出有上下文和有意义的回复是至关重要的。训练集需要是双方之间的对话交流的集合。具体来说,我们需要为每个条目构建一对句子,一个源句子和一个目标句子。比如‘来源:你好吗?’目标:我很好。来源:你住在哪里?目标:我住在澳大利亚。如果没有大量的人工清理工作,这种类型的训练集是很难获得的。人们使用的最流行的数据集是康奈尔电影对话语料库数据集,它并不出色(稍后你会明白为什么),但却是目前你能得到的最好的数据集。
康奈尔电影对话语料库
这个数据集由取自一部电影对白的 22 万行对话组成。每行包括一个或多个人的对话。下面的三行示例向您展示了一些很好的例子,在这些例子中,对话以一个人的问题(粗体)开始,然后是另一个人的回答。
你懂法语?当然……我妈妈来自加拿大
你要去哪里?如果你一定要知道的话,我们正试图参加一个由朋友组成的小型学习小组。
有多少人去这里?几千。大多数都是邪恶的
然而,还有很多糟糕的例子,因为你需要前一段对话的上下文或视觉参考才能理解后面的句子。例如,请参见以下内容:
最坏的是什么?你得到了那个女孩。
不开玩笑。他是个罪犯。
**这是肺癌问题。**她最喜欢的叔叔
没有一种简单的方法可以在不投入体力劳动的情况下去除坏的线条,而我并不真的准备这么做。即使我这样做了,我得到的数据集也会少得多;不足以训练我的人工智能模型。因此,我决定不顾一切地继续下去,看看我能走多远。
我从每个对话行和每两个连续的句子中生成了多对源句子和目标句子,不管是谁说的。
最糟糕的是什么?你破产了?“下一步做什么,”会变成两对对话线。
最糟糕的是什么?你破产了?
你破产了?下一步怎么办?
这样,我设法扩大了我的数据集。我完全意识到,我可能会错误地将同一个人所说的源句子和目标句子配对。然而,有一半时间,如果目标句子在别人的回复中,它仍然是有意义的,如下例所示。
最糟糕的是什么?你破产了?接下来做什么?
使用 nltk 库,可以在两行代码中完成对句子的分词/拆分。
tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
sentences = tokenizer.tokenize(text)
我还修剪了一个超过 20 个单词的句子,因为我的模型只能读取最多 20 个单词的输入和 20 个单词的输出。除此之外,更长的句子意味着更大的上下文和更高的变化,这对于人工智能模型来说更难学习,因为我们的训练集规模不是那么大。
用上面的方法,我设法得到了 441k 对对话行。
预处理训练集
下一步是进一步预处理这个训练集,这包括几个步骤。
第一步是从字符串对中删除和替换不需要的字符串,比如所有的 xml 和 html 标签以及多个点和破折号。
接下来就是扩展缩略词,例如,你会被扩展为你会,我是,我是等等。,这增加了我的人工智能模型准确性。这样做的原因是,在后面的步骤中,通过对分隔符(如空格、换行符或制表符)进行拆分,句子将被转换为单词列表。所有独特的单词将构成我们的词汇。像“I’m”这样的缩写将被认为是词汇表中的新单词,这将不必要地增加我们的词汇量,并由于碎片化而降低我们的训练集的有效性;因此让我们的人工智能更难学习。
我使用了一个非常方便的 python 框架,叫做“缩写”,它用一行代码扩展了一个句子中的缩写。
text = contractions.fix(text)
标点符号是下一个要解决的问题。我把标点符号从句子中分离出来(例如,“你好吗?”变成了“你好吗?”)这样做的原因类似于收缩;确保标点符号本身会被视为一个单词,而不会与前一个单词合并为一个新单词,例如“you?在上面的例子中。
通过以上所有步骤,我获得了大约 441k 对训练集和 56k 个词汇。
作为最后一步,我添加了一个词汇表剪枝,这样我就可以控制词汇表中支持的最大单词数。删除不常用的单词很容易做到。较小的词汇量和较大的训练集更有利。这很有意义——想象一下,通过给孩子 100 个例句来教他们 5 个新单词,而不是用同样的 100 个例句来教 100 个新单词,这有多难。在更多的句子中更频繁地使用更少的单词当然会帮助你学得更好。
训练数据准备管道
从我的许多实验中,我发现 15k 单词的词汇量给了我最好的结果,产生了 346k 训练集,等于 23 的比率,而不是最初的 441k/56k =7.9。
为 Seq2Seq 模型定型
由于 Amazon SageMaker 已经提供了一个关于如何训练 Seq2Seq 模型的示例 Jupyter 笔记本,因此启动培训非常容易。
我只需要定制我的训练文件所在的 S3 桶,并添加我的数据预处理代码。接下来,我将向您展示在 SageMaker 中训练 seq2seq 模型是多么容易,方法是向您展示这里和那里的代码片段。如果你想看完整的源代码,可以看看我的 jupyter 笔记本。
首先,你需要让 SageMaker 知道你想要使用哪个内置算法。每个算法都被封装起来,可以从一个特定的 URL 获得。
from sagemaker.amazon.amazon_estimator import get_image_uri
container = get_image_uri(region_name, 'seq2seq')
接下来,您需要构建培训工作描述,它提供了一些关于培训工作的重要信息。首先,您需要设置训练集的位置以及存储最终模型的位置。
"InputDataConfig": [
{
"ChannelName": "train",
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": "s3://{}/{}/train/".format(bucket, prefix),
"S3DataDistributionType": "FullyReplicated"
}
},
},
....
"OutputDataConfig": {
"S3OutputPath": "s3://{}/{}/".format(bucket, prefix)
},
在这之后,您需要选择您想要在什么机器或实例上运行这个培训。Seq2seq 是一款相当重的机型,需要一台 GPU 机。我推荐的是 ml.p3.8xlarge,有四个 NVIDIA V100 GPUs。
"ResourceConfig": {
"InstanceCount": 1,
"InstanceType": "ml.p3.8xlarge",
"VolumeSizeInGB": 50
}
最后,您需要设置超参数。这是我花了 30%时间的地方,几乎是第二次尝试数据准备策略。我在不同的设置下构建了各种模型,并比较了它们的性能,以得出最佳配置。
记住,我选择把我的句子限制在 20 个单词以内。下面的前两行是原因。我的 LSTM 模型只被训练识别最多 20 个单词的输入和最多 20 个单词的输出。
"HyperParameters": {
"max_seq_len_source": "20",
"max_seq_len_target": "20",
"optimized_metric": "bleu",
"bleu_sample_size": "1000",
"batch_size": "512",
"checkpoint_frequency_num_batches": "1000",
"rnn_num_hidden": "2048",
"num_layers_encoder": "1",
"num_layers_decoder": "1",
"num_embed_source": "512",
"num_embed_target": "512",
"max_num_batches": "40100",
"checkpoint_threshold": "3"
},
通常,批量越大(如果你的 GPU RAM 可以处理的话)越好,因为你可以一次训练更多的数据来加快训练过程。512 似乎是 p3.8xlarge 的最大尺寸。有些人可能会认为不同的批量大小会产生稍微不同的精度,但是我的目标不是在这里获得诺贝尔奖,所以小的精度差异对我来说并不重要。
我为每个编码器和解码器使用一层,每个编码器和解码器有 2048 个隐藏单元,字嵌入大小为 512。然后在每批 1,000 个样本中进行检查点和评估,这相当于每批 1,000×512 个样本(512k 对样本)。因此,训练将达到总训练集的 1.5 倍(总共 346k),直到它针对该模型执行评估。在每个检查点,最好的评估模型被保留,然后最终保存到我们的输出 S3 桶中。SageMaker 也有提前终止的能力。例如,如果模型在连续三个检查点(’ checkpoint_threshold ')后没有改善,则训练将停止。
此外,“最大数量批次”是一个安全网,无论如何都可以停止训练。在你的模型永远保持改进的情况下(这是一件好事),这将保护你,使培训费用不会打破你的银行(这不是一件好事)
训练模型
只需要两行代码就可以开始训练。然后你只需要耐心等待几个小时,这取决于你使用的实例类型。我用 p3.8xlarge 实例花了一个半小时。
sagemaker_client = boto3.Session().client(service_name='sagemaker')
sagemaker_client.create_training_job(**create_training_params)
正如您在下面看到的,验证指标在第八个检查点找到了性能最好的模型。
Bleu 评估指标
模型评估
我之前有意跳过了对“optimized_metric”的讨论,因为它值得有自己的部分来适当地解释它。评估一个生成式人工智能模型并不是一项简单的任务。通常,问题和答案之间没有一对一的关系。比如一个问题,十个不同的答案都一样好,这就导致了映射的范围非常广。对于语言翻译任务,映射范围要窄得多。然而,对于聊天机器人来说,范围显著增加,特别是当你使用电影对话作为训练集时。你要去哪里?'可以是以下任何一种:
- 去地狱
- 为什么这么问?
- 我不会告诉你的。
- 什么?
- 你能再说一遍吗?
亚马逊 SageMaker 中的 seq2seq 算法有两个流行的评测指标可供选择:困惑度和 bleu。performance 通过在我们的训练集中使用概率分布模型逐字随机抽取样本来评估生成的句子,这在这篇文章中有很好的解释。
Bleu 通过对单词 n-gram 匹配进行评分来针对目标句子评估生成的句子,并且如果生成的句子比目标句子短,则惩罚生成的句子。这篇文章解释了它是如何工作的,并建议不要使用它,有一个很好的理由。相反,我发现它比困惑更有效,因为生成的句子的质量与人工检查时 bleu 的分数密切相关。
测试模型
当训练完成时,您可以创建一个用于推断的端点。从那里,用几行代码就可以生成一个带推理的文本。
response = runtime.invoke_endpoint(EndpointName=endpoint_name,
ContentType='application/json',
Body=json.dumps(payload))
response = response["Body"].read().decode("utf-8")
response = json.loads(response)
for i, pred in enumerate(response['predictions']):
print(f"Human: {sentences[i]}\nJarvis: {pred['target']}\n")
这些结果不会让我赢得奖牌(大约 60%的回答与上下文无关,20%还过得去)。然而,让我兴奋的是,另外的 20%出奇的好。它回答了正确的上下文和语法。因此,它向我展示了它可以在一定程度上学习英语,甚至可以像我们一样骂人。这也很好地提醒了你的孩子可以从电影中学到什么。
我最喜欢的一个例子是当机器人被问到,谁是你最好的朋友他回答,‘我的妻子’。我已经检查过了,这些句子中的大多数甚至不在训练集中,所以人工智能模型确实学到了一点创造力,而不仅仅是记住了训练集。
Jarvis 聊天日志(很好的例子)
这里有一些不好的。
Jarvis 聊天日志(不好的例子)
通过试验几个不同的超参数,我发现:
- 增加更多的编码器和解码器层会使情况变得更糟。有趣的是,生成的句子结构更加复杂。然而,问题答案的相关性很差。
- 减小单词嵌入大小也降低了生成质量。
- 将词汇量减少到 15,000 个单词提高了质量,而进一步减少会降低质量。
- 扩展单词缩写和分隔标点符号无疑提高了回答的质量。
- 虽然这可能更多的是一个预处理步骤,而不是一个超参数设置。值得一提的是,添加字节对编码(正如 SageMaker notebook 所建议的)会降低响应的质量。
语音生成和动画化身
我需要的下一个模块是文本到语音和动画化身。我用了一个很棒的 Amazon Polly 来生成文本到语音。同样,它是一个在线 API。然而,它具有超强的灯光响应时间(大多数情况下不到 300 毫秒)和听起来自然的高质量语音。
亚马逊 Polly 文本到语音
鉴于我之前作为特效和动作捕捉软件工程师的工作,我非常想自己制作动画化身。实际上,我在另一个项目中构建了一个简单的虚拟化身系统:构建一个为我蹒跚学步的孩子播放视频的机器人。谢天谢地,我的大部分意识到,如果我继续这个旅程,而不是使用一个很棒的 3D 化身软件, Loom.ai ,这将花费我多长时间,它带有音频对口型功能!你只需要向 Loom.ai 应用程序发送一个音频剪辑,它就会制作一个 3D 化身的动画,与提供的音频进行口型同步。多棒啊。该应用程序还带有一个假的摄像机驱动程序,它可以传输渲染输出。我只需要在缩放应用程序设置中选择假摄像机,这样我就可以在缩放会议中包含动画。
贾维斯动画化身
结果
随着所有模块的完成,我需要做的就是建立一个控制器系统,将一切结合起来。第一次测试运行得相当好,除了贾维斯偷走了所有的对话。他不客气地插话,并回答视频会议中任何人说的每一句话,惹恼了每个人。嗯,有一件事我忘了训练贾维斯——礼貌和社交技巧!
与贾维斯进行视频会议聊天
与其建立另一个社交技能模块,我不知道如何开始,一个简单的解决办法是教贾维斯通过语音命令开始和停止说话。当他听到“贾维斯,别说话”时,他停止了反应,直到他听到“贾维斯,开始说话”。有了这个新的重要技能,每个人都开始更喜欢贾维斯了。
这种生成模型在最初的几次交流中很有趣,因为他的一些回答让我们惊讶于他的创造力和他的粗鲁。然而,这种新鲜感很快就消失了,因为对话持续的时间越长,我们听到的断章取义的回应就越多,直到最终我们意识到我们根本没有在进行一场有意义的对话。出于这个原因,我最终使用了由 AIML 语言模型驱动的基于模式的聊天机器人技术,它令人惊讶地提供了比普通的基于检索的模型更好的创造力,并且可以从过去的信息中回忆上下文!这篇文章清楚地解释了它是什么,也许它会成为我改天讲述的故事。
在我的超级棒的黑客马拉松团队成员的帮助下,我们甚至扩展了 Jarvis 的能力,使其成为按需会议助理,可以帮助记录笔记、分配给相关个人的行动要点、通过电子邮件发送给与会者以及安排后续会议。
结论
有时候机器人生成的文本质量很好。然而,仅仅从两次对话中(即使是好的对话),你就能清楚地看出有些事情不正常。
- 它没有过去的背景。每个回答都是从所提问题的上下文中产生的。它没有考虑之前的对话,而大多数人可以很容易地做到这一点。显然,目前的技术还不足以建立一个可以考虑过去对话历史的模型。至少我还没有听说过。
- 一半以上的时间里,它给出的都是不着边际的回应。虽然可能还需要更好的模型架构,但是我最大的怀疑是训练集。电影对话具有广泛的主题,这使得人工智能模型很难学习,特别是当源句子和目标句子有时没有意义时(例如,没有事先的上下文,需要视觉参考或配对不正确)。像餐馆预订对话这样的训练集可能工作得更好,因为餐馆预订的范围非常有限。然而,对话交流可能会不那么有趣。
鉴于以上所述,我相信一个完全实用的、可生成的聊天机器人还需要几年或几十年的时间。我现在完全可以理解为什么我们要花很多年去学习一门语言。
未来生成聊天机器人
然而,这是一个非常有趣的项目,我从中学到了很多。
这个项目的代码可以从 github 获得。
从结构化来源构建知识图
KGs 洞察
关于集成异构数据所采用的映射方法的说明
构图——原图来自 Pixabay
K 知识图(KGs)被标记为有向多重图,以与特定领域或组织相关的实体和关系的形式编码信息。kg 是捕获和组织大量结构化和多关系数据的有效工具,这些数据可以使用查询机制进行研究。考虑到这些特性,KGs 正在成为不同研究领域和工业应用中的 Web 和遗留信息系统的主干。关于 KG 特性的更多细节,包括 RDF、本体和 SPARQL,可以在下面的文章中找到:
利用语义图视角,将人类知识融入智能系统
towardsdatascience.com](/knowledge-graphs-at-a-glance-c9119130a9f0)
将数据发布到 KGs 是一个复杂的过程,因为它需要提取和整合来自不同来源的信息。整合这些来源的目标是协调它们的数据,并导致对整体信息的一致看法。异构数据源包括从非结构化数据(如纯文本)到结构化数据(包括表格式,如 CSV 和关系数据库)以及树结构格式(如 JSONs 和 XML)。本文的贡献是通过基于映射的方法将结构化数据源中的数据发布到 KGs 中。
结构化来源的整合
结构化数据源在数据生态系统中扮演着重要的角色,因为组织和网络中的许多有价值(且可重用)的信息都是结构化数据。
不像其他来源,比如纯文本,结构化信息可以通过一个语义集成过程映射到 kg。语义网(SW)社区应用这一过程的共同策略是采用参考本体作为全局模式。然后,构建映射来描述目标数据源的全局模式和局部模式之间的关系。从数据集成的角度来看,这种方法被归类为全局视图(GAV)。
从这个角度来看,数据集成性能基于作为全局模式的本体的一致性和表达能力。为了阐明真实场景中的映射过程,下一节将介绍公共采购领域中的一个运行示例。
公共采购示例:数据源和参考本体
公共采购是指公共机构和行政部门从公司购买货物或服务的过程。在这一过程中,公共当局宣布招标,公司带着具体的标书参加招标,公共当局将授予其中一份标书。
假设我们有一个目标数据源 ds ,包括一组属性 ds{a ₁ ,a ₂ ,a ₃ ,…} 。在公共采购中,目标数据源是表示特定公共合同的 JSON 文件。JSON (JavaScript Object Notation)是一种独立于语言的数据交换格式。它使用人类可读的文本来存储和传输以属性-值对和任何可序列化的数据类型(包括数组)为特征的数据对象。
{
"contract_id":"Z4ADEA9DE4",
"contract_object":"MANUTENZIONE ORDINARIA MEZZI DI TRASPORTO",
"proposing_struct":{
"business_id":"80004990927",
"business_name":"Ministero dell'Interno"
},
"participants":[
{
"business_id":"08106710158",
"business_name":"CAR WASH CARALIS"
}
]
}
该 JSON 描述了以下数据:
- contract_id 包括所需服务的标识符(“Z4ADEA9DE4”)。
- 合同对象包括服务的描述(“MANUTENZIONE ORDINARIA MEZZI DI TRASPORTO”)。
- business_id 和 business_name ,嵌套在 proposing_struct 字段*,*包括提出招标的公共机构的标识符(“80004990927”)和名称(“Ministero dell’Interno”)。
- business_id 和 business_name ,嵌套在参与者字段中,包括标识符(“Z4ADEA9DE4”)和名称(“洗车卡利斯”)。
映射过程需要参考本体或作为全局模式。捷克 OpenData.cz 倡议发布了公共采购中采用的最常见的本体之一,可在 GitHub 上获得:https://github.com/opendatacz/public-contracts-ontology。
与我们的运行示例相关的映射过程中涉及的本体公理可以用 Turtle 格式表示如下:
@prefix dcterms: <[http://purl.org/dc/terms/](http://purl.org/dc/terms/)> .
[@prefix](http://twitter.com/prefix) gr: <[http://purl.org/goodrelations/v1#](http://purl.org/goodrelations/v1#)> .
[@prefix](http://twitter.com/prefix) owl: <[http://www.w3.org/2002/07/owl#](http://www.w3.org/2002/07/owl#)> .
[@prefix](http://twitter.com/prefix) pc: <[http://purl.org/procurement/public-contracts#](http://purl.org/procurement/public-contracts#)> .
[@prefix](http://twitter.com/prefix) rdf: <[http://www.w3.org/1999/02/22-rdf-syntax-ns#](http://www.w3.org/1999/02/22-rdf-syntax-ns#)> .
[@prefix](http://twitter.com/prefix) rdfs: <[http://www.w3.org/2000/01/rdf-schema#](http://www.w3.org/2000/01/rdf-schema#)> .#######
####### Classes
#######pc:Contract a owl:Class .
gr:Offering a owl:Class .
pc:Tender a owl:Class ;
rdfs:subClassOf gr:Offering .#######
####### Relations (or Object Properties)
#######pc:contractingAuthority a owl:FunctionalProperty, owl:ObjectProperty ;
rdfs:domain pc:Contract ;
rdfs:range gr:BusinessEntity .pc:tender a owl:ObjectProperty ;
rdfs:domain pc:Contract ;
rdfs:range pc:Tender .pc:awardedTender a owl:FunctionalProperty, owl:ObjectProperty ;
rdfs:subPropertyOf pc:tender .pc:bidder a owl:ObjectProperty ;
rdfs:domain pc:Tender ;
rdfs:range gr:BusinessEntity .#######
####### Datatype properties
#######dcterms:identifier a owl:DatatypeProperty ;
rdfs:domain pc:Contract ;
rdfs:domain gr:BusinessEntity ;
rdfs:range rdfs:Literal .rdfs:label a owl:DatatypeProperty ;
rdfs:domain pc:Contract ;
rdfs:domain gr:BusinessEntity ;
rdfs:range rdfs:Literal .rdfs:description a owl:DatatypeProperty ;
rdfs:domain pc:Contract ;
rdfs:range rdfs:Literal .
映射过程的详细信息
整个映射过程包括两个主要步骤。第一步是在目标数据源的本地模式和参考本体之间创建一个映射。第二步是将源数据具体化为 KG 语句或虚拟化对源的访问,定义遗留信息的基于图形的视图。物化的语句可以直接发布到 KG 中,而基于图形和虚拟化的访问允许我们检索和浏览目标数据源的数据,就像它是 KG 一样。
映射步骤最广泛采用的方法是基于所谓的定制映射。这些方法利用用声明性语言编写的可定制文档来执行地图生成步骤。声明性语言利用 SW 形式来描述局部模式和全局模式之间的关系。
研究团体采用的最突出的语言是 R2RML,它表示用 RDF 编写的关系数据库到 kg 之间的定制映射。这种语言的扩展,称为 RML,是一种更通用的映射语言,其适用性扩展到其他类型的表,如 CSV 文件和树结构模式。其他类型的语言,如 TARQL 和 JARQL,分别采用 SPARQL 语法为特定格式(如 CSV 和 JSON 文件)创建映射。描述 ds 和 O 之间映射的 JARQL 示例如下:
[@prefix](http://twitter.com/prefix) dcterms: <[http://purl.org/dc/terms/](http://purl.org/dc/terms/)> .
[@prefix](http://twitter.com/prefix) pc: <[http://purl.org/procurement/public-contracts#](http://purl.org/procurement/public-contracts#)> .
[@prefix](http://twitter.com/prefix) gr: <[http://purl.org/goodrelations/v1#](http://purl.org/goodrelations/v1#)> .
[@prefix](http://twitter.com/prefix) rdf: <[http://www.w3.org/1999/02/22-rdf-syntax-ns#](http://www.w3.org/1999/02/22-rdf-syntax-ns#)> .CONSTRUCT {
?BusinessEntity0 dcterms:identifier ?proposing_struct__business_id ;
rdf:type gr:BusinessEntity. ?BusinessEntity1 dcterms:identifier ?participants__business_id ;
rdf:type gr:BusinessEntity . ?Tender0 pc:bidder ?BusinessEntity1 . ?Contract0 dcterms:identifier ?contract_id ;
rdf:type pc:Contract ;
pc:contractingAuthority ?BusinessEntity0 ;
pc:tender ?Tender0 .
}WHERE {
?root a jarql:Root.
OPTIONAL { ?root jarql:contract_id ?contract_id . }
OPTIONAL { ?root jarql:proposing_struct ?proposing_struct . }
OPTIONAL { ?proposing_struct jarql:proposing_struct__business_id
?proposing_struct__business_id . }
OPTIONAL { ?root jarql:participants ?participants . }
OPTIONAL { ?participants jarql:participants__business_id
?participants__business_id . }
BIND (URI(CONCAT('[http://purl.org/procurement/public-contracts/contract/'](http://purl.org/procurement/public-contracts/contract/'),
?contract_id)) as ?Contract0)
BIND (URI(CONCAT('[http://purl.org/goodrelations/v1/businessentity/'](http://purl.org/goodrelations/v1/businessentity/'),
?proposing_struct__business_id)) as ?BusinessEntity0)
BIND (URI(CONCAT('[http://purl.org/goodrelations/v1/businessentity/'](http://purl.org/goodrelations/v1/businessentity/'),
?participants__business_id)) as ?BusinessEntity1)
BIND (URI(CONCAT('[http://purl.org/procurement/public-contracts/tender/'](http://purl.org/procurement/public-contracts/tender/')
?contract_id + + '_' participants__business_id)) as ?Tender0)
}
这个 JARQL 文件包括 3 个主要部分。第一个包含在 CONSTRUCT 部分,而其他的包含在 WHERE 部分。
CONSTRUCT 部分描述了对语义类型进行编码的图形模式,例如
?BusinessEntity0 dcterms:identifier ?proposing_struct__business_id .
和语义关系,如
?Contract0 pc:contractingAuthority ?BusinessEntity0 .
WHERE 部分的第一部分包括可选的操作符,描述了如何解析 JSON 文件以提取创建 KG 语句所需的数据。例如:模式
?proposing_struct jarql:proposing_struct__business_id ?proposing_struct__business_id .
表明变量。proposing_struct__business_id 必须替换为 JSON 的 proposing_struct__business_id 属性。
WHERE 部分的第二段包括不同的绑定操作符,声明如何为从 JSON 中提取的数据生成实体 URIs。这条线
BIND (URI(CONCAT(‘[http://purl.org/procurement/public-contracts/contract/'](http://purl.org/procurement/public-contracts/contract/'), ?contract\_id)) as ?Contract0) .
指示合同的 URIs 是结合 http://purl.org/procurement/public-contracts/contract/和从绑定到?合同 id 变量。
语言驱动的引擎旨在按照使用声明性语言编写的映射文档的指令,物化或虚拟化 KG 语句。这些引擎执行两个不同的任务:第一个任务是将目标数据源字段链接到由引用本体定义的类或属性。一旦创建了这个链接,这些引擎就会具体化或虚拟化 URIs 和 KG 语句,检索数据源中包含的遗留信息。JARQL 文件描述了属性集 ds{a ₁ ,a ₂ ,a ₃ ,…} 和由 o 表示的全局模式之间的链接,允许具体化以下语句:
[@prefix](http://twitter.com/prefix) dcterms: <[http://purl.org/dc/terms/](http://purl.org/dc/terms/)> .
[@prefix](http://twitter.com/prefix) pc: <[http://purl.org/procurement/public-contracts#](http://purl.org/procurement/public-contracts#)> .
[@prefix](http://twitter.com/prefix) gr: <[http://purl.org/goodrelations/v1#](http://purl.org/goodrelations/v1#)> .
[@prefix](http://twitter.com/prefix) rdf: <[http://www.w3.org/1999/02/22-rdf-syntax-ns#](http://www.w3.org/1999/02/22-rdf-syntax-ns#)> .
[@prefix](http://twitter.com/prefix) contract: <[http://purl.org/procurement/public-contracts/contract/](http://purl.org/procurement/public-contracts/contract/)> .
[@prefix](http://twitter.com/prefix) be: <[http://purl.org/goodrelations/v1/businessentity/](http://purl.org/goodrelations/v1/businessentity/)> .
[@prefix](http://twitter.com/prefix) tender: <[http://purl.org/procurement/public-contracts/tender/](http://purl.org/procurement/public-contracts/tender/)> .# ?BusinessEntity0 dcterms:identifier ?proposing_struct__business_id ;
# rdf:type gr:BusinessEntity .be:08106710158 dcterms:identifier 08106710158 ;
rdf:type gr:BusinessEntity .# ?BusinessEntity1 dcterms:identifier ?participants__business_id ;
# rdf:type gr:BusinessEntity .be:08106710158 dcterms:identifier 08106710158 ;
rdf:type gr:BusinessEntity .# ?Tender0 pc:bidder ?BusinessEntity1 .tender:Z4ADEA9DE4-80004990927 pc:bidder be:08106710158 .# ?Contract0 dcterms:identifier ?contract_id ;
# rdf:type pc:Contract ;
# pc:contractingAuthority ?BusinessEntity0 ;
# pc:tender ?Tender0 .contract:Z4ADEA9DE4 dcterms:identifier Z4ADEA9DE4 ;
rdf:type pc:Contract ;
pc:contractingAuthority 80004990927 ;
pc:tender tender:Z4ADEA9DE4-80004990927 .
为了阐明 JARQL 文件支持的转换过程,本例将包含在其 CONSTRUCT 部分的图形模式作为注释进行报告。使用 JARQL 作为声明性语言的主要原因之一是它能够创建 URIs,组合位于 JSON 树结构不同层次的数据。在运行的示例中,投标人的 URI 是结合位于 JSON 根级别的合同 id(“z 4 ade a9 de 4”)和位于 JSON 嵌套结构中的业务实体 id(“80004990927”)构建的。
下一步是什么
在下一篇文章中,我将向您展示一种基于语义模型的特殊方法,用于捕获具有基于图的结构的数据源的语义。
用基于图的结构捕获数据源的语义
towardsdatascience.com](/semantic-models-for-constructing-knowledge-graphs-38c0a1df316a)
在 R 中构建克里金模型
利用空间统计算法进行空间预测
西蒙·米加吉在 Unsplash 上的照片
介绍
在当今世界,我们可以访问大量地理标记数据。我们能够以各种方式利用这些信息,创造新的信息,从而做出更好的决策,而不是将它们存放在数据库或文本文件中。
空间统计模型的优势之一是能够通过计算邻近点和感兴趣点之间的相关性来分析空间中的任何点。有许多空间模型可以使用,但我最喜欢的是克里金法。虽然地理加权回归(GWR)或反距离加权(IDW)等其他方法可能是有用的探索工具,但根据我的经验,克里金法往往会随着数据的增加而扩展得更好,并且在许多情况下,可以做出更准确的预测。
在本文中,我将展示如何使用 R 中的克里金法进行预测,并使用芝加哥市警察局的数据(在此处找到数据)提供提示&技巧。目标是对芝加哥市任一给定点的犯罪频率进行插值。这种空间统计技术允许我们利用现有的数据,构建一个模型来解释该区域的空间结构,并基于该空间结构对空间中任何其他纬度/经度点进行预测。
数据准备
在我们开始之前,我想讨论一下数据集,这样我们就能理解预测的是什么。数据可以在这里找到,我将 csv 文件下载到我的本地驱动器上。该文件有 22 个变量和超过 700 万行,但对于本教程,我们将只利用坐标点(纬度和经度)、犯罪的主要类型和犯罪的描述。
Nikola Johnny Mirkovic 在 Unsplash 拍摄的照片
**【Tips&Tricks】**重要的是克里格只有特定类别类型的值。这意味着我们一次只能为一种犯罪类型插入点,否则插入的点没有任何意义。因此,我们将重点关注的特定犯罪是对车辆的刑事损坏,我们将使用克里金法来插值芝加哥市此类犯罪的频率。
数据准备的 r 代码:
library(data.table)# fast read/write function
library(dplyr)df_1 <- reported_crimes %>% filter(PrimaryType=='CRIMINAL DAMAGE') %>% filter(Description=='TO VEHICLE')df_2 <- df_1 %>%
group_by(Latitude, Longitude) %>%
count() %>%
ungroup() %>%
inner_join(df_1, by=c('Latitude', 'Longitude')) %>%
distinct(Latitude, Longitude, .keep_all = TRUE) %>%
select(Latitude, Longitude, n) %>%
mutate(location_id = group_indices(., Latitude, Longitude))
一旦我们过滤了数据,在每个唯一的位置(纬度和经度)计算犯罪的频率(变量名为 n ,并为每个唯一的位置添加一个索引( location_id) ,数据将类似于图 1。
图 1:前 10 行准备好的数据(图片由作者提供)
克里金法背后的思想是使用一组有限的数据点来预测给定区域中的其他邻近点。这种方法允许该领域的科学家只对少量数据点进行采样,并对其余数据点进行插值,从而节省了时间和金钱。因此,为了模拟这种方法,我们可以随机选取 10,000 个点作为训练集来构建模型(如图 2(a)所示)。
用于创建训练集的 R 代码:
random_generator <- sample(1:nrow(df_2), round(nrow(df_2)*.3), replace=F)train_df <- df_2 %>%
filter(!location_id %in% random_generator) %>%
slice(1:10000)
然后,我们可以在感兴趣区域周围的点网格上使用该模型,如图 2(b)所示。点网格是通过构建一个多边形并在该多边形内创建超过 76,000 个纬度/经度点自动生成的(为简洁起见,本文不包含该代码)。
图 2:训练与网格(图片由作者提供)
构建变异函数
准备好数据后,第一步是构建变异函数并对其拟合曲线函数,该函数可用于对点格网的值进行插值。幸运的是,有了 R 中的 gstat 包,这可以使用变差函数轻松完成。
建立变差函数模型的 R 代码:
coordinates(train_df) <- c("Longitude", "Latitude")
proj4string(train_df) <- CRS("+proj=longlat +datum=WGS84")lzn.vgm <- variogram(log(n) ~ Latitude + Longitude, train_df, width=0.1)lzn.fit = fit.variogram(lzn.vgm, vgm(c("Gau", "Sph", "Mat", "Exp")), fit.kappa = TRUE)
**【T21 技巧】**log 函数用于确保所有插值保持正值。exp 函数将用于归一化本文“克里格”部分的结果。
可以绘制变差函数和曲线函数来了解我们区域的空间结构,如图 3 所示。直观的说,我们可以看到曲线函数(Matern)可以比较好的描述点之间的自相关性。我们可以看到,在 1.1 km 左右,点对不再具有空间相关性。
图 3:变异函数模型(图片由作者提供)
让我们克里格
现在,我们可以使用 gstat 包中的克里格函数使用这些信息来克里格点格网。如前所述,点的网格已经生成,为了简洁起见,本文不再赘述。但是,数据中未包含在训练集中的任何其他点都可以用于插值点和测试精度。
克里金法的 r 代码:
coordinates(grid) <- c("Longitude", "Latitude")
proj4string(grid) <- CRS("+proj=longlat +datum=WGS84")lzn.kriged <-krige(log(n) ~ Latitude + Longitude, train_df, grid, model = lzn.fit, maxdist=10, nmax=50)# Retrieve interpolated values
predicted <- lzn.kriged@data$var1.pred %>%
as.data.frame() %>%
rename(krige_pred = 1) %>%
mutate(krige= exp(krige))variance <- lzn.kriged@data$var1.var %>%
as.data.frame() %>%
mutate(variance = exp(variance))
图 4(a)显示了用于构建模型的点,并放大到芝加哥住宅区以便更好地查看数据点。一旦应用了代码并对网格点进行了克里格处理,我们就可以看到图 4(b)中所示的预测。显示的是模型计算出对车辆的犯罪损害的最高频率的热点,以及该犯罪发生频率可能较低的区域。克里金法填补了我们没有的数据空白。虽然在图 4(a)中我们可能有许多数据点可供选择,但并没有涵盖该区域的每个点。对于所有这些我们没有的点,克里金法开始发挥作用并填充空洞,从而增加区域的覆盖范围,如图 4(b)所示。
图 4:芝加哥住宅区的预测点(图片由作者提供)
结论
由 Esri 雄辩地陈述,
空间分析使您能够解决复杂的定位问题,并更好地了解您的世界中正在发生的位置和事件。它不仅仅是绘制地图,还能让你研究地方的特征以及它们之间的关系。空间分析为您的决策提供了新的视角。
在空间上理解我们的数据是进行预测的重要一步。这也是我喜欢克里金法的原因之一。当我们构建变差函数时(如图 3 所示),很清楚点之间的自相关何时停止,这告诉我们在什么距离上数据不再相关。然后利用变差函数对新数据进行插值。
虽然视觉效果总是令人兴奋,但在现实世界中实施之前,测试模型的准确性是至关重要的。虽然本文中没有讨论,但是我们使用训练/测试分割方法对数据(14%训练和 86%测试)进行了测试,以测量模型的准确性,RMSE 为 1.81,MAE 为 0.80。
有了空间统计,我们可以凭直觉判断数据的空间相关性,并通过数学方法精确计算相关性的终点。我希望这篇文章能帮助您集思广益,找出其他哪些数据集可能是空间相关的,以及克里金法如何帮助您更好地理解它!
使用线性代数构建线性回归(最小二乘法)
使用 excel 或 numpy 解决线性回归问题的全线性代数方法。
迪米特里·卡拉斯泰列夫在 Unsplash 上的照片
有了 python 和 R 中大量复杂的包供我们使用,我们不太可能在每次必须拟合大量数据点时都要经历算法背后的数学运算。但是,有时学习数学并手动从头开始求解算法是很有用的,这样我们就能够直观地了解它是如何在后台完成的。在我为 ISB-CBA 的课程工作中,有一堂统计学课涉及使用线性代数在 excel 上用矩阵乘法求解多元线性回归的截距、系数和 R 平方值。在此之前,我一直使用 python 中的 statmodel OLS 或 R 上的 lm() 命令来获取截距和系数,看一眼 R 平方值就知道它有多好。
自从我的课程结束后,我早就忘记了如何使用 excel 解决这个问题,所以我想重温一下这些概念,并写下这篇文章,以便对其他人也有用。
我在我的 K aggle 笔记本上使用 numpy 完成了这篇文章。如果你觉得这篇文章有用,请查看我的笔记本并投赞成票!
使用矩阵乘法求解线性回归的数学基础
让我们从一个简单的线性回归开始。我们想通过一组数据点找到最佳拟合线:(x1,y1),(x2,y2),……(xn,yn)。但是最适合是什么意思呢?
如果我们能找到一条穿过所有可能数据点的直线的斜率和截距,那么这条直线就是最佳拟合直线。但是在大多数情况下,这样的线是不存在的!所以我们决心找到一条线,使得当从数据点到回归线平行于 y 轴画一条连接线时,测量每个数据点的误差,所有这些误差的总和应该是最小的。简单,嗯?
在图中,错误分别用红、蓝、绿、黄和紫线表示。为了将此公式化为矩阵求解问题,考虑下面给出的线性方程,其中β0 是截距,β是斜率。
为了简化这种表示法,我们将把β0 加到β向量上。这是通过在 X 矩阵中添加一个额外的 1 列,并在 Beta 向量中添加一个额外的变量来实现的。因此,矩阵形式将为:
那么最小二乘矩阵问题就是:
让我们考虑我们的初始方程:
两边乘以 X _ 转置矩阵:
其中:
那是一大堆方程式。但是当我们用下面一个简单的案例来解决它时,它就足够简单了。
一个玩具简单线性回归问题的求解
为简单起见,我们将从一个简单的线性回归问题开始,它有 4 个数据点(1,1),(2,3),(3,3)和(4,5)。X = [1,2,3,4],y = [1,3,3,5]。当我们转换成如上所述的矩阵形式时,我们得到:
下面是实现这个简单解决方案的 numpy 代码:
多元线性回归
多元线性回归的求解也非常类似于简单线性回归,我们遵循 6 个步骤:
- 为 X 矩阵中的截距添加一个从全 1 开始的新列
- 取 X 矩阵的转置
- 乘以 X 转置矩阵和 X 矩阵
- 求这个矩阵的逆矩阵
- 将 X 转置与 y 矩阵相乘
- 将两个矩阵相乘,找出截距和系数
为了解决多元线性回归问题,我从 kaggle 获取了一个数据集,其中包含英国二手车销售价格。
我在 excel 中手工计算了所有的计算。我已经从大众数据集中取出了前 300 行,并且只取出了其中的数字变量。回归得出的 r 平方得分为 0.77。
数据年份,里程,税,mpg,发动机尺寸,价格 2019,13904,145,49.6,225000 2019,4562,145,49.6,226883…
docs.google.com](https://docs.google.com/spreadsheets/d/10zoOfiQLryz0Y7T1Zbd1grvOSdDGEgEEyECZ5sVyKJ8/edit#gid=212170771)
我敦促你下载 excel 工作簿,并按照计算(谷歌表的新数学字体格式不好。可以下载在 MS excel 上查看,可读性更好)。在“解释”表中,我用矩阵乘以 X_Transpose 和 X。这包含了我们计算模型参数(如 R 平方值)所需的所有信息。
请参考 kaggle 笔记本的第三节这里:https://www . ka ggle . com/gireeshs/DIY-build-linear-regression-with-linear-algebra # Part-3:-Multiple-linear-regression在这里我用矩阵乘法解决了这个问题。
参考资料:
- 【https://www.youtube.com/watch?v=Lx6CfgKVIuE
- 完整的商业统计手册
- 我的课程工作为 ISB CBA
图像:
【http://onlinestatbook.com/2/regression/intro.html 号
为什么 Alteryx 最适合构建线性回归模型?
多亏了 Alteryx,数据准备、争论和建模变得高效且无需代码!
改变一切— 改变
在商业分析领域,Alteryx 不仅是一个领导者,也是一个无代码的终结者。当涉及到数据准备、转换、争论或构建模型时,Alteryx 在每个领域都大放异彩。
谈到自动化和分析自动化,Alteryx 已经成为市场的领跑者。一两个月前我偶然遇到了 Alteryx,从那时起,Alteryx 就是我的首选数据准备和分析工具。Alteryx 最好的特性之一是它在构建预测分析模型时的效率。除此之外,它还提供了无代码建模。
用 Alteryx 建立线性回归模型
问题陈述:
一家公司正准备在未来几个月内寄出今年的目录。该公司的邮件列表中有 250 个新客户,他们希望将目录发送给他们。您的经理被要求确定向这些客户发送目录可以为公司带来多少利润。你被要求预测这 250 个新客户的预期利润。除非预期利润贡献超过 10,000 美元,否则管理层不想将目录发送给这些新客户。
- 输入数据
有两个数据集:
客户-现有客户数据集。
邮寄名单-新的 250 个客户,他们想发送目录。
您可以在下面找到数据集和项目链接。
在 Alteryx 中输入文件
2。数据争论
一旦数据集在空间中被提出,就可以使用“选择”工具并查看数据集的数据类型和列。大多数情况下,当我们需要更改数据集中列的数据类型时,这很方便。
使用选择工具查看所有列
3。线性回归
可以从 Alteryx 提供的“预测”工具中带入“线性回归”工具,输入“目标”和“预测”变量,做一个线性回归方程。
在左边,可以给出目标变量和预测变量。
4。浏览结果
工作流运行后,您可以通过点击“浏览”工具(即工作流最右侧的工具)来查看输出。
运行工作流后,您可以看到结果
通过检查结果和篡改预测变量,可以建立有效的线性回归模型。有效的线性回归模型的 p 值必须小于 0.05。在上面的例子中,“客户细分”和“Avg_Num_Products_Purchases”的 p 值远小于 0.005。所以线性方程是有效的。
5。得分
一旦建立了一个强线性方程,就引入了“得分”工具,并将其与用于预测平均销售额的数据集相连接。“评分”工具输出“是”和“否”两列,即客户在看到发送的目录后购买的可能性。
6。结果
看到分数后,使用线性回归模型的“平均销售额”必须乘以“Score_Yes”来计算新客户的“预期销售额”。
之后,使用利润率 50%计算利润,每个目录的成本为 6.50 美元,因此适用于所有 250 个客户。如果总利润大于 10,000 美元,目录将发送给新客户。
预测目录需求的最终 Alteryx 模型
使用 Alteryx 构建无代码模型变得更加容易,您可以尝试构建自己的线性回归模型。奥特莱斯的奇迹还有很多。但是现在,享受“线性回归”模型吧。
用 DEEL-LIP 构建 Lipschitz 约束网络
光谱标准化变得简单
什么是李普希茨函数?
以 Rudolf Lipschitz 命名的函数称为 k-Lipschitz,当它的一阶导数以某个常数 k 为界时,这个常数 k 的最小值称为函数的 Lipschitz 常数。这种函数有一些有趣的特性,其中包括:
这意味着,如果 x1 和 x2 彼此接近,那么,它们的预测 f(x1)和 f(x2)也将接近。由于神经网络近似函数,我们将集中于 1-Lipschitz 网络。
为什么网络的李普希兹常数很重要
这些类型的网络在现代机器学习中有许多用途:它们已经被证明对敌对攻击具有鲁棒性[1]。
来源:《解释和利用对立的例子》,古德菲勒等人,ICLR,2015 年。
对抗性攻击是找到一个 x2 (“长臂猿”图像)接近我们最初的 x1 (“熊猫”图像)的行为,这样他们的预测 f(x1) 和 f(x2) 就不同了。主要问题是,构建这种对立例子所增加的噪声非常小。控制网络的 Lipschitz 常数对对抗的鲁棒性有很大的影响:Lipschitz 常数越低,为了建立对抗的例子就需要越多的噪声。请随意查看这篇关于对抗性鲁棒性的温和介绍来了解更多关于这个主题的信息。
但这并不是 Lipschitz 网络的唯一用途:它们也用于 Wasserstein 距离估计。Wasserstein 度量允许测量两个分布之间的距离。计算这个度量需要优化 1-Lipschitz 网络。这在 Wasserstein-GAN [2]中被特别使用。
不同级别的 Lipschitz 约束
前面的等式告诉我们,我们函数的梯度最多等于 1。但是强制实施这个约束并不一定意味着梯度将在某个点有效地达到 1。
根据用例,我们可能需要不同级别的 Lipschitz 约束:
- “软”1-Lipschitz 约束:我们强制梯度函数接近 1 并且平均等于 1。在这种情况下,在输入域的某些特定点处,梯度可以大于 1。在训练期间使用这样的约束并不保证最终的网络将是 1-Lipschitz,但是允许正则化训练。
- “硬”1-Lipschitz 约束:在输入域的每个点上强制梯度小于或等于 1。在某些点上,梯度可以小于 1。这在处理对抗性鲁棒性时使用。
- “梯度几乎处处等于 1”:强制梯度几乎处处等于 1。这种特殊类型的网络不适合传统的 ML。然而,当使用 Wasserstein 距离估计时,这是必要的[3]。
我们将关注最后两个层次的约束。
我们如何在神经网络上应用这些约束
众所周知,计算神经网络的 Lipschitz 常数是一个 NP 难问题。然而,已经提出了几种方法来在训练期间实施这种“硬”约束。这些方法中的大多数依赖于约束每层的 Lipschitz 常数,然后可以使用下面的组成属性:
对于线状图层(如密集图层),有多种方法可以实现该属性,如权重裁剪或归一化。
我们现在将探索现有的方法,以此为密集层。回想一下稠密层的定义,它是由非线性激活函数组成的线性算子:
我们将首先关注线性核 k。我们稍后将看到如何包括激活函数,以及如何将这些方法扩展到其他类型的层。
重量剪裁
这是在每个线性图层级别强制执行 Lipschitz 约束的简单方法。例如,具有 m 个输入和 n 个输出的密集层是 1-Lipschitz,如果所有权重在以下间隔内被限幅:
这足以保证该层的 Lipschitz 常数低于 1。然而,在实践中,训练数据点的梯度通常远低于 1。这是因为在某一点上使渐变等于 1 的唯一方法是将所有权重设置为它们的剪裁值,这是非常严格的。换句话说,这意味着在这种约束下训练的网络具有小于 1 的 Lipschitz 常数,但是许多 1-Lipschitz 网络不满足这种约束。
那么我们能找到一个没有这些缺点的约束吗?
光谱归一化
该方法通过使用频谱归一化解决了权重削波的限制。数学上,这种类型的归一化依赖于奇异值分解:
由于中心矩阵是对角的,并且由于 U 和 V 不影响 K 的梯度,因此我们可以说 K 的梯度由下式限定:
如果我们将所有权重除以最大 sigma 值,则梯度以 1 为界:
这种标准化提供了非常强的保证:
- 梯度不能大于 1。
- 输入域中存在一组点,使得梯度等于 1。
此外,使用幂迭代法【4】可以快速计算最高奇异值。
它还提供了一种表达“梯度几乎处处等于 1”约束的正式方式:
在实践中,也可以通过使用bj rck 正交归一化算法[5]来获得该约束
激活功能呢?
为了获得 1-Lipschitz 神经网络,所使用的激活函数也必须是 1-Lipschitz。这些大部分已经是 1-Lipschitz: ReLU ELU,sigmoid,tanh,logSigmoid…有些需要适当参数化,比如 leakyRelu,PReLU…最后有些根本不是 1-Lipschitz。
其他层呢?
在层上应用合成属性要求每个层都遵守 1-Lipschitz 约束。我们展示了密集图层的标准化示例,但是这适用于任何图层类型吗?
**卷积层:**有人可能认为归一化卷积层的内核(通过裁剪或光谱归一化)就足够了,但是有一个问题:卷积层使用填充、步长、膨胀…所有这些操作都会影响输出的范数,从而改变层的 Lipschitz 常数。为了捕捉这种现象,可以通过计算这些参数来计算校正系数[6]。
**汇集层:**这些层可以被视为卷积的特例,因此我们也可以应用一个校正因子。
**批量规范:**当它执行重缩放时,该层不受约束。此外,1-lipschitz 约束下的学习减少了对这些层的需求。使用它只对校正每层后引起的偏差有用,这也可以通过设置每层的 use_bias 参数来完成。
Dropout: 允许一种正则化,切换到层输出的零部分。然而,在推断中,应用了一个比例因子来补偿丢失因子,这破坏了 Lipschitz 属性。
用 DEEL-LIP 简化 k-Lipschitz 神经网络
DEEL-LIP 是一个基于 Tensorflow 构建的库,它扩展了常见的 Keras 元素,如层、初始化器或激活,允许用户轻松构建 1-Lipschitz 网络。提供的层使用光谱归一化和/或 Bjö rck 正交归一化。需要时,计算并应用适当的校正系数。
怎么用?
首先,可以用 pip 安装库
下面的代码演示了如何用 DEEL-LIP 构建和编译一个神经网络。它非常类似于标准的 Keras 代码。
使用 DEEL-LIP 和 Keras 构建 NN 的示例
DEEL-LIP 的开发旨在方便 Keras 用户使用:
- DEEL-LIP 遵循与 Tensorflow/Keras 相同的封装结构。
- 所有元素(层、激活、初始化器……)都与标准的 Keras 元素兼容。
- 当一个层覆盖一个标准的 Keras 元素时,它使用相同的参数实现相同的接口。唯一的区别在于控制层的 Lipschitz 常数的参数。
处理 Lipschitz 图层时有什么重要的?
由于网络的 Lipschitz 常数是每一层常数的乘积,所以每一层都必须遵守 Lipschitz 约束。增加一个不满足 Lipschitz 约束的单层,就足以打破整个网络的 Lipschitz 约束。
为了方便起见,从 DEEL-LIP 导入的任何图层都可以安全使用:如果可以导入,就可以使用。对于层的参数也是如此:层上的错误设置(即,打破 Lipschitz 约束)将引起错误。
可用层的完整列表可在文档中找到。
反向传播/推断期间的开销是多少?
**在反向传播期间,**权重的归一化增加了每次迭代的开销。然而,在反向传播期间,谱归一化算法被直接添加到图中。这被称为可微分约束,它产生更有效的优化步骤。
在推理期间,完全没有增加开销:只有在反向传播期间,规范化才是强制的。DEEL-LIP 提供了一个导出功能,可用于从深唇模型创建标准的 Keras 模型。然后,每个密集层或 Conv2D 层被转换为标准 Keras 层。通过导出模型,可以使用使用 DEEL-LIP 训练的网络进行推理,甚至不需要安装 DEEL-LIP。
结论
Lipschitz 约束网络是具有有界导数的神经网络。它们有许多应用,从对抗鲁棒性到 Wasserstein 距离估计。有各种方式来实施这样的约束。光谱归一化和 Bjö rck 正交归一化是最有效的方法,DEEL-LIP 库提供了一种训练和使用这些约束图层的简单方法。
感谢阅读!
参考
[1]穆斯塔法·西塞、皮奥特尔·博扬诺夫斯基、爱德华·格雷夫、扬恩·多芬、尼古拉·乌苏尼尔。帕塞瓦尔网络:提高对抗例子的鲁棒性。 arXiv:1704.08847 【统计。
[2]马丁·阿约夫斯基,苏史密斯·钦塔拉,莱昂·博图。瓦瑟斯坦·甘。 arXiv:1701.07875 【统计。ML]
【3】塞德里克·维拉尼。最佳运输:旧与新。数学科学小组。施普林格柏林海德堡,2008。
【4】维基百科:幂迭代法
【5】杰姆·阿尼尔,詹姆斯·卢卡斯,罗杰·格罗斯。整理李普希兹函数逼近。arXiv:1811.05381T17【cs。[LG]
[6]马蒂厄·塞鲁里埃、弗兰克·马马莱特、阿尔贝托·冈萨雷斯-桑斯、蒂博·布瓦辛、让-米歇尔·卢贝斯、尤斯塔西奥·德尔巴里奥。利用铰链正则化的最佳传输实现分类的稳健性。arXiv:2006.06520T22【cs。LG]
构建机器学习管道
管道的 A-Z
ML 管道自动化工作流程。但是,这意味着什么呢?关键是,它们帮助开发从一个估算器/转换器到下一个的顺序数据流,直到它到达最终的预测算法。
它确保训练集、测试集和验证集之间没有数据泄漏。流水线还使程序更自动化,可以用作功能代码。
我们将根据步骤的复杂程度介绍 3 种不同类型的管道:
- 简单管道
- 带自定义功能的流水线 -顺序应用
- 带定制选择器和功能的流水线 -并行应用
作者图片:ML 管道可重用性
让我们根据应用程序的复杂性来看看不同类型的管道:
简单管道
我们将使用网格搜索来调整超参数并生成输出。对于参数网格值,写入“管道步骤”的名称,后跟 2 '_ ',然后是要优化的超参数。就这么简单!
作者图片:简单管道
为管道转换设置自定义函数
如果我们希望在某些列上进行一些自定义转换,而有一个您想调用的函数来代替呢?
作者图片:管道中的自定义函数
column transformer是 scikit-learn 中的一个类,它允许有选择地应用数据准备转换。它将接受要执行转换的列名,以及转换器本身。Param " remainder "指定如何处理未提及的其余列。ColumnTransformer 按顺序应用每个转换
在管道中设置自定义选择器和转换器
让我们更进一步说,我们甚至不想提前选择列。相反,我们需要管道来为我们做选择。现在,我们引入 feature union——它连接了并行发生的多个转换的结果!
作者图片:复杂管道
由于 pipeline 希望类作为具有 fit、transform 方法的选择器,我们将扩展基类 BaseEstimator &TransformerMixin。
我们本可以在所有这些应用网格搜索、随机搜索和交叉验证!你现在是管道专家了…祝贺你!
完整的代码可以在这里找到。
建立原发性胆汁性肝硬化患者的医学预后模型
如何将机器学习技术应用于医学数据集并建立评估它们的指标。
摘要
医学预测是人工智能直接应用的新兴领域。这篇文章讨论了人工智能在医学预测方面的想法,并提供了构建预测模型的详细教程。预后是一个医学术语,用于预测疾病可能或预期的发展,包括体征和症状是否会随着时间的推移而改善或恶化或保持稳定。
在这篇文章中,我将解释我为原发性胆汁性肝硬化(PBC)患者建立风险模型的工作。我们将使用在这里维护的 PBC 数据集。
预测模型如何工作?
预后模型基于一个基本原理,即在给定患者档案的情况下,患者在给定时间后的生存概率。预测模型可以大致分为两类:
- 建立在患者群体水平上
- 在单个患者层面构建
在这里,我们将在单个患者的水平上工作,并将使用两种不同的方法,然后对它们进行比较。下面提到了这两种方法:
- 使用 Cox 比例风险模型的线性比例方法。
- 使用随机存活森林模型的基于树的方法。
建立 Cox 比例风险模型
导入必要的库和包
import sklearn
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from lifelines import CoxPHFitter
from lifelines.utils import concordance_index as cindex
from sklearn.model_selection import train_test_split
我们使用的是生命线库,这是一个开源库,用于导入生存模型中使用的模型和度量。
加载数据集并执行数据预处理
df = pd.read_csv('pbc.csv')
df.head()
df = df.drop('id', axis=1)
df = df[df.status != 1]
df.loc[:, 'status'] = df.status / 2.0
df.loc[:, 'time'] = df.time / 365.0
df.loc[:, 'trt'] = df.trt - 1
df.loc[:, 'sex'] = df.sex.map({'f':0.0, 'm':1.0})
df = df.dropna(axis=0)
让我们看看我们的数据
时间以天为单位,因此我们将其转换为年,并删除“id”列,因为它不是必需的。我们转换和缩放“状态”和“trt”列以及编码“性别”列的标签。
np.random.seed(0)
df_dev, df_test = train_test_split(df, test_size = 0.2)
df_train, df_val = train_test_split(df_dev, test_size = 0.25)
在继续建模之前,让我们归一化连续协变量,以确保它们在相同的尺度上。为了防止数据泄漏,我们应该使用从训练数据计算的统计数据来标准化测试数据。
continuous_columns = ['age', 'bili', 'chol', 'albumin', 'copper', 'alk.phos', 'ast', 'trig', 'platelet', 'protime']
mean = df_train.loc[:, continuous_columns].mean()
std = df_train.loc[:, continuous_columns].std()
df_train.loc[:, continuous_columns] = (df_train.loc[:, continuous_columns] - mean) / std
df_val.loc[:, continuous_columns] = (df_val.loc[:, continuous_columns] - mean) / std
df_test.loc[:, continuous_columns] = (df_test.loc[:, continuous_columns] - mean) / std
Cox 比例风险模型基于以下数学方程:
𝜆(𝑡,𝑥)=𝜆₀(𝑡)exp(𝛉ᵀXᵢ)
𝜆₀项是一个基线风险,包含了随时间变化的风险,等式中的第二项包含了因个人协变量/特征引起的风险。拟合模型后,我们可以使用个人相关风险项(即 exp(𝛉ᵀXᵢ)对个人进行排名
让我们将分类列转换为编码,以便在线性模型中使用它们。我们有两列,“水肿”和“分期”,它们在本质上是分类的,因此我们对它们进行转换。
columns=['edema','stage']
one_hot_train=pd.get_dummies(df_train, columns=columns, drop_first=True)
one_hot_test=pd.get_dummies(df_test, columns=columns, drop_first=True)
one_hot_val=pd.get_dummies(df_val, columns=columns, drop_first=True)
现在,经过这些转换和预处理步骤后,我们的数据看起来像这样:
训练我们的 Cox 比例风险模型
现在,我们已经准备好让我们的数据符合 Cox 比例风险模型,我们将从从生命线库构造一个 CoxPHFitter 对象开始。
cph = CoxPHFitter()
cph.fit(one_hot_train, duration_col = 'time', event_col = 'status', step_size=0.1)cph.print_summary()
经过训练的 CoxPH 模型看起来像:
让我们开始分析上表,该表描述了我们的训练模型,提供了根据训练数据优化的所有系数。
在这里,我们可以看到具有负系数的特征是有益的,即它们有助于降低风险系数/输出值,而具有正系数的特征有助于增加风险系数/输出值。从上表我们可以推断出**【TRT】【腹水】【蜘蛛】****【白蛋白】和【alk . phos】**具有负系数,因此,这些特征的高值将成比例地降低危险系数。
我们可以比较治疗变量的预测存活曲线
- y 轴是存活率
- x 轴是时间
治疗变量的预测生存曲线
我们可以清楚地看到,“TRT”= 0 的生存曲线比“TRT”= 1 的生存曲线低。这是因为“trt”变量或治疗特征是一个负协变量,即它在拟合模型中具有负系数,因此变量的较高值将产生较低的风险评分。
接下来,我们比较“水肿 1.0”变量的预测存活曲线。
水肿的预测生存曲线 _1.0 变量
这里,可以清楚地注意到两条曲线被一个大的值分开,并且“水肿 _ 1.0”= 1 的曲线比“水肿 _ 1.0”= 0 的曲线低。这是因为“水肿 _1”变量是一个正协变量,即它在拟合模型中有一个正系数,因此该变量的值越高,风险得分越高。两条曲线之间的较大差距是由于对应于“水肿 _1.0”的系数值相对较高。
危险比率
两个病人之间的风险比是一个病人比另一个病人更危险的可能性。它可以用数学公式表示为:
𝜆₁(𝑡)/𝜆₂(𝑡)=𝑒xp(𝜃(𝑋₁−𝑋₂)ᵀ
哪里,𝜆₁(𝑡)=𝜆₀(𝑡)𝑒xp(𝜃𝑋₁ᵀ)
还有,𝜆₂(𝑡)=𝜆₀(𝑡)𝑒xp(𝜃𝑋₂ᵀ)
现在,我们将使用数据集中的任意 2 名患者来计算风险比。
i = 1
patient_1 = one_hot_train.iloc[i, :].drop(['time', 'status']).values
j = 5
patient_5 = one_hot_train.iloc[j, :].drop(['time', 'status']).values
cox_params=cph.params_.values
hr = np.exp(np.dot(cox_params,(patient_1-patient_5)))
print(hr)
运行上面这段代码后,我们得到病人 1 和病人 5 的风险比**15.029017732492221**
。这意味着与患者 5 相比,患者 1 的风险更高,因为两者之间的风险比大于 1。类似地,我们可以计算任意两个患者之间的风险比,并比较他们的风险评分。
衡量我们模型的性能
衡量一个经过训练的模型的性能是机器学习管道中不可或缺的一部分,这里我们将使用 Harrell 的 C 指数作为我们模型性能的衡量标准。生存环境中的 c 指数或和谐指数是这样的概率,给定一对随机选择的个体,较早死亡的个体具有较高的风险评分。在接下来的几行代码中,我们将编写自己的 C-index 版本。但是在进入代码之前,让我介绍一个重要的概念,这是理解预测模型所必需的,即审查。审查是指在研究过程中没有发生死亡事件,这可能是由以下两个原因造成的:
- 患者在目睹事件/死亡之前选择退出研究
- 研究在患者目睹事件/死亡之前结束
因此,如果患者的个人资料被审查,则在数据集的“事件”列中以值“0”表示。如果它未经审查,则用值“1”表示。
哈勒尔 C 指数的数学公式为:
哈勒尔 C 指数=
在哪里,
PermissiblePairs =满足以下任一条件的患者对总数:对于(patient_i,patient_j)对,
- 两个病人都有未经审查的数据
- 如果任何一个病人的数据被审查,时间[审查]>时间[未审查]
ConcordantPairs =满足以下两个条件的患者对总数:对于(patient_i,patient_j)对,
- 允许患者配对
- 如果 risk score[病人 _ I]> risk score[病人 _j],那么,
time[patient_i]
risks=满足以下条件的患者对总数:对于(patient _ I,patient_j)对,
- 允许患者配对
- risk score[病人 I]= risk score[病人 j]
event=one_hot_train['status'].values
y_true=one_hot_train['time'].values
scores=scores.valuesn = len(y_true)
assert (len(scores) == n and len(event) == n)
concordant = 0.0
permissible = 0.0
ties = 0.0
result = 0.0
for i in range(n):
for j in range(i+1, n):
if event[i]==1 or event[j]==1:
pass
if event[i]==1 and event[j]==1:
permissible+=1
if scores[i]==scores[j]:
ties+=1
elif (y_true[i]>y_true[j] and scores[i]<scores[j]):
concordant+=1
elif (y_true[i]<y_true[j] and scores[i]>scores[j]):
concordant+=1 elif (event[i]!=event[j]):
if event[j] == 0:
censored = j
uncensored = i
if event[i] == 0:
censored = i
uncensored = j
if y_true[censored]>=y_true[uncensored]:
permissible+=1
if scores[i]==scores[j]:
ties+=1
if scores[censored]<scores[uncensored]:
concordant+=1
c-index = (concordant+ties*0.5)/permissibleprint(c-index)
在上面这段代码中,我们在计算了可允许的、一致的和风险关系对的数量之后,计算了训练数据的 Harrell C 指数。我们训练数据对应的 Harrell 的 C 指数是0.82639116202946
类似地,我们可以计算 Harrell 的 C 指数来验证和测试数据。
**Val: 0.8544776119402985
Test: 0.8478543563068921**
我们得到了一个可观的 C 指数,它告诉我们,我们的模型表现得相当好,但我们可以通过使用一种基于树的方法来提高我们的预测模型的性能,我将在下一节中讨论这种方法。
建立随机生存森林模型
随机生存森林模型是一种基于树的机器学习方法,它构建了许多估计器/树,并在投票的基础上做出决策。这种基于树的方法优于线性模型,如我们已经讨论过的 Cox 比例风险模型。Cox 比例风险模型假设线性风险函数,这使得不可能表示非线性风险函数,而非线性风险函数在所讨论的数据集中非常普遍。因此,让我们从头开始构建我们的模型。
在人群中,不同的患者可能有不同的风险函数。因此,为了优化我们的预测,随机生存森林模型首先通过遍历树来预测该患者的风险函数,然后使用该风险函数计算风险得分。
导入必要的库
我们将使用 R 中预先安装的库来构建我们的随机生存森林模型。您可以在这里找到这个库,并将其下载到您的本地机器上,以便在您的项目中使用。为了从 Python 中调用 R 函数,我们将使用“r2py”包。在构建实际模型之前,让我们开始一步一步地导入它们。
%load_ext rpy2.ipython
%R require(ggplot2)from rpy2.robjects.packages import importr
base = importr('base')
utils = importr('utils')
import rpy2.robjects.packages as rpackages forest = rpackages.importr('randomForestSRC', lib_loc='R')from rpy2 import robjects as ro
R = ro.rfrom rpy2.robjects import pandas2ri
pandas2ri.activate()
在基于树的模型中,不需要对变量进行编码,因为树可以很好地处理原始分类数据。所以我们马上开始训练我们的模型。
培训和评估模型
model = forest.rfsrc(ro.Formula('Surv(time, status) ~ .'), data=df_train, ntree=300, nodedepth=5, seed=-1)
在拟合我们的数据之后,让我们看看我们的训练模型及其参数。
print(model)
模型描述
现在让我们通过计算 Harrell 的 C 指数来评估我们的随机生存森林模型的性能。本文前面提到的代码也可以用来根据 RSF 模型预测的分数计算哈勒尔的 C 指数,这些分数是用下面提到的代码计算的。
result = R.predict(model, newdata=df_train)
scores = np.array(result.rx('predicted')[0])
对应于训练数据的 Harrell 的 C 指数为 0.8769230769230769 ,这是一个很好的分数,我们的预测模型的性能得到了改善。让我们看看它是如何处理验证和测试数据的。
result = R.predict(model, newdata=df_val)
scores_val = np.array(result.rx('predicted')[0])
result = R.predict(model, newdata=df_test)
scores_test = np.array(result.rx('predicted')[0])
为了计算测试和验证数据的 C 指数,使用“scores_val”和 scores_test”来计算 Harrell 的 C 指数。运行上述步骤后,我们得到以下分数/c 指数:
**Survival Forest Validation Score: 0.8769230769230769
Survival Forest Testing Score: 0.8621586475942783**
因此,我们的模型在验证和测试数据上都表现很好,并且优于 Cox 比例风险模型。
深入挖掘
让我们更深入地研究一下我们刚刚训练和评估的 RSF 模型,以便我们可以了解我们的模型在其风险得分预测中优先考虑的功能。为此,我们将使用随机生存模型自带的内置函数,称为“可变重要性”或 VIMP。
VIMPS = np.array(forest.vimp(model).rx('importance')[0])y = np.arange(len(VIMPS))
plt.barh(y, np.abs(VIMPS))
plt.yticks(y, df_train.drop(['time', 'status'], axis=1).columns)
plt.title("Variable Importance (absolute value)")
plt.show()
VIMP 条形图
看到上面的图表,我们可以推断出“毕丽”变量在预测风险评分中最重要,其次是“水肿”和“保护时间”。如果我们将上表中的值与 Cox 模型中的系数进行比较,我们会注意到“水肿”在两个模型中都是一个重要的特征,但是“毕丽”似乎只在随机存活森林中重要。
结论
在这篇文章中,我们目睹了医疗预后模型的概述,然后详细说明了建立风险模型的两种机器学习方法。最后,我们使用可视化插图和图形评估了这两个模型,并使用 Harrell 的 C 指数比较了它们的性能。
在这篇文章的结尾,我将分享我在这个研究领域的想法,首先引用一位知名人士吴恩达的名言
很难想出一个 AI 不会改造的主要行业。这包括医疗保健、教育、交通、零售、通信和农业。人工智能在所有这些行业都有令人惊讶的清晰路径。
人工智能在医学和医疗保健领域的范围是巨大的。在预后模型的帮助下,卫生从业者可以通过分析随时间计算的风险得分来规划患者的未来治疗,从而大大改善患者的健康。该领域的研究正在以非常快的速度发展,如果我们计划在现实生活中实施这些方法,我们将很快见证领先的医院和医疗诊所有效地使用这些模型。
使用 Keras 构建模型
神经网络 API 简介
照片由拍摄,另一个摄影爱好者在像素
Keras 是用于在 python 中构建神经网络的高级 API。API 支持顺序神经网络、递归神经网络和卷积神经网络。由于其模块化、用户友好性和可扩展性,它还允许简单快速的原型制作。在本帖中,我们将通过使用 Keras 为回归和分类任务构建序列神经网络的过程。Keras 的文档可以在这里找到。
我们开始吧!
回归
数据准备
我们将用于回归的数据是加州房价数据集。数据可以在这里找到。
首先,让我们导入数据并打印前五行:
import pandas as pd
df = pd.read_csv("housing.csv")
print(df.head())
现在,让我们定义我们的输入和目标变量。我们将使用经度、纬度、住房中位数年龄、总房间数、总卧室数、人口数、家庭数和收入中位数来预测房屋中位数价值。
import numpy as np
X = np.array(df[['longitude', 'latitude', 'housing_median_age', 'total_rooms', 'total_bedrooms', 'population', 'households', 'median_income']])
y = np.array(df['median_house_value'])
然后,我们将分割数据用于训练和测试:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
现在我们所有必要的变量都定义好了。让我们建立一些模型!
定义模型
顺序模型是层的线性堆叠。
**from** keras.models **import** Sequential
model = Sequential()
如果出现以下错误:
尝试在此次导入和所有后续导入中从 tensorflow 导入 Keras:
**from** tensorflow.keras.models **import** Sequential
model = Sequential()
添加图层
我们可以使用。add()方法来添加层。我们将添加需要单独导入的密集层:
**from** keras.layers **import** Dense
模型应该知道第一层中预期的输入形状。因此,您需要传递关于模型中特征数量的信息。因为我们有 8 个特征,我们需要传入一个输入形状(8,)。我们将添加一个包含 8 个节点的密集层:
model.add(Dense(8, input_shape = (8,)))
让我们添加一个额外的隐藏层。对于我们的隐藏层,我们将使用 relu 函数:
model.add(Dense(8, activation = 'relu'))
最后,让我们添加我们的输出层。对于回归问题,我们通常将输出层中的激活函数定义为线性。此外,输出图层有一个用于回归问题的节点:
model.add(Dense(1, activation = 'linear'))
编译
我们要做的下一件事是配置学习过程。这是使用 compile 方法完成的。在编译方法中,我们必须传递以下参数:
- 损失函数(Loss Function ):这是一个函数,用于评估您的算法对数据集的建模效果。
- 优化器:这是一种找到最小化你的损失函数的权重的方法。
- 度量:对于回归,我们通常将度量定义为损失函数。这允许我们在模型被训练时跟踪损失。
我们将对优化器使用均方根传播算子,对损失函数使用均方误差,对指标使用均方误差:
model.compile(optimizer='rmsprop', loss='mse', metrics =['mse'])
我们可以查看模型摘要来分析我们的神经网络架构:
print(model.summary())
拟合
对于模型训练,我们将使用。fit()方法:
model.fit(X_train, y_train)
我们应该得到以下输出:
我们可以传递一个历元数的值(数据的迭代次数)来尝试提高准确性:
model.fit(X_train, y_train, epochs = 10)
您可以调整纪元的数量,以尽量减少误差:
model.fit(X_train, y_train, epochs = 50)
您可以对隐藏层的数量进行同样的操作。让我们尝试添加两个额外的隐藏层:
model.add(Dense(8, input_shape = (8,)))
model.add(Dense(8, activation = 'relu'))
model.add(Dense(8, activation = 'relu'))
model.add(Dense(8, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='rmsprop', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 50)
我们看到最后五个时期具有较低的均方误差。我们还可以尝试添加更多节点。让我们试试 16 个节点,而不是 8 个:
model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='rmsprop', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 50)
最后,让我们尝试使用不同的优化器。让我们试试“亚当”优化器:
model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='adam', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 50)
最终模型的性能大致相同。最后,让我们尝试大量的 epochs,比如 500 个,并传递一个更大的 batch_size。让我们传递 100 的 batch_size:
model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='adam', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 500, batch_size=100)
结果我们看到了一些改进。虽然我们在最小化均方误差,但我们可以显示不同的误差指标,如平均绝对百分比误差:
model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='adam', loss='mse', metrics =['mape'])
model.fit(X_train, y_train, epochs = 500, batch_size=100)
我们看到最后一个时期的平均绝对百分比误差为 28%。
预测
为了生成预测,我们执行以下操作:
y_pred = model.predict(X_test)
我们可以使用 matplotlib 可视化我们的预测:
import matplotlib.pyplot as pltplt.clf()
fig = plt.figure()
fig.suptitle('Scatter plot of Actual versus Predicted')
plt.scatter(x=y_pred, y=y_test, marker='.')
plt.xlabel('Predicted')
plt.ylabel('Actual ')
plt.show()
预测值和实际值之间的关系越像直线,我们的模型就越准确。就优化我们的模型而言,我们还可以尝试更多的东西,但我会把这些留给你去做。
分类
现在让我们来完成构建分类模型的相同过程。工作流程中有许多相似之处,但有一些小差异!
数据准备
我们将用于分类的数据是电信客户流失数据集。这里可以找到。
首先,让我们导入数据并打印前五行:
import pandas as pd
df = pd.read_csv("Customer_Churn.csv")
print(df.head())
为简单起见,我们将使用所有的分类和数字数据来预测客户流失。首先,我们需要将分类列转换为神经网络可以处理的数值。例如,对于性别,我们有:
df.gender = pd.Categorical(df.gender)
df['gender_code'] = df.gender.cat.codes
现在让我们定义输入和输出数组:
import numpy as npfeatures = ['gender_code', 'SeniorCitizen_code', 'PhoneService_code', 'MultipleLines_code',
'InternetService_code', 'Partner_code', 'Dependents_code', 'PaymentMethod_code',
'PaymentMethod_code', 'PaperlessBilling_code','Contract_code', 'StreamingMovies_code',
'StreamingTV_code', 'TechSupport_code', 'DeviceProtection_code', 'OnlineBackup_code',
'OnlineSecurity_code', 'Dependents_code', 'Partner_code','tenure', 'MonthlyCharges']X = np.array(df[features])
y = np.array(df['Churn_code'])
让我们也标准化输入:
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X)
然后,我们将分割数据用于训练和测试:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)
现在我们所有必要的变量都定义好了。让我们建立一些模型!
定义模型&添加图层
让我们从一个 8 节点输入图层开始,其输入形状对应于要素的数量:
model = Sequential()
model.add(Dense(8, input_shape = (len(features),)))
让我们添加一个隐藏层:
model.add(Dense(8, activation='relu'))
接下来,让我们添加我们的输出层。对于二元分类,我们使用 1 个节点作为输出和 sigmoid 激活函数:
model.add(Dense(1, activation='sigmoid'))
编译
现在让我们编译我们的模型。我们将使用“Adam”传播算子、损失的二进制交叉熵和度量的“准确性”。Keras 文档建议我们将指标设置为值“准确性”:
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
让我们打印我们模型的摘要:
print(model.summary())
试衣
对于模型训练,我们将使用。fit()方法:
model.fit(X_train, y_train)
我们应该得到以下输出:
与回归问题类似,您可以随意调整节点数、层数、时期数、batch_size 和优化器类型。
预测
在预测步骤中,我们希望将输出(数组)转换为浮点数,然后使用列表理解对值进行舍入:
y_pred = [round(float(x)) for x in model.predict(X_test)]
我们可以使用度量分类报告来可视化预测:
from sklearn import metrics
print(metrics.classification_report(y_test, y_pred))
我们还可以查看 roc_auc_score 和 f1_scores:
在这两种情况下,等于 1.0 的值都是完美的。通过调整模型参数,可以显著提高性能。我鼓励你尝试增加神经元(节点)、时期、层的数量,并设计额外的功能。
结论
在这篇文章中,我们介绍了使用 Keras 神经网络 API 构建回归和分类模型的过程。我们回顾了定义模型对象、添加层、用编译方法配置模型、训练模型、进行预测和评估模型性能的过程。我鼓励您尝试回归和分类的神经网络架构。一旦你觉得舒服了,就试着把你的知识应用到其他数据集和预测问题上。我希望这篇文章有趣。这篇文章的代码将会在 GitHub 上发布。感谢阅读,机器学习快乐!