TowardsDataScience 博客中文翻译 2019(九十一)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

为 Twitch Streamers 构建一个关注增长的游戏推荐器

原文:https://towardsdatascience.com/building-a-growth-focused-game-recommender-for-twitch-streamers-7389e3868f2e?source=collection_archive---------21-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Caspar Camille Rubin on Unsplash

将流媒体推荐的传统重点从观众转移到流媒体,优先考虑其频道的增长。

witch 是一个视频流平台,用户可以在这里观看其他用户可以观看的内容,它在过去十年中取得了巨大的成功。2018 年,它共记录了 5600 亿分钟的观看时间,每月有 440 万条独特的流媒体。它可以被简洁地描述为众包直播电视。为了增加用户的保留,它有一个推荐系统,为用户提供与他们过去观看过的类似的频道。然而,Twitch 没有为那些希望继续流媒体播放,但可能对他们目前玩的游戏感到厌倦,但仍然希望扩大观众群的流媒体用户提供任何东西。这篇文章讲述了兰迪·马卡雷格娜塔莎与之间的边界,我试图为这一鸿沟建立一个解决方案。

目标

这个项目的目标是根据流媒体用户以前的流媒体播放历史和观众的接受程度,为流媒体用户推荐游戏。该推荐系统将理想地为提供他们的名字作为输入的流播者输出游戏和理想时间。所有这一切,包括项目构思和数据收集,将在三周的时间内完成。

1.数据

在我们能说出想法之前,我们需要看看我们在做什么。通过 Twitch API ,我们可以抓取一些东西:

  1. 顶级游戏
  2. 每场比赛中彩带的身份
  3. 给定流媒体的观众计数
  4. 每场比赛的艺术框
  5. 每个游戏的类型

所有这些都是以小时为基础的,并且有每 30 秒 100 个电话的限制。对于我们的分析来说,这是一个限制因素,但是为了证明这个概念,我们向前推进了。

2.管道

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了在 API 限制和粒度之间取得平衡,我们选择对排名前 100 的游戏进行每小时一次的清理。我们的管道由以下步骤组成:

  1. 从 Amazon EC2 实例上托管的 Python 脚本调用 Twitch API。使用每小时一次的内置 Linux Cron 作业自动调用脚本和 API。
  2. 将 JSON 对象转换成 SQL 格式,并将数据推送到托管 PostgreSQL 数据库的 Amazon RDS 实例。
  3. 从 RDS 数据库中训练模型,并拥有一个在运行模型时引用此数据库的应用程序。

以下是 RDS 数据库模式:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Database schema, generated on https://dbdiagram.io/home

你也可以在 Tableau 上玩我们搜集的数据:

[## 随着时间的推移,Twitch 的游戏、类型、频道和收视率的趋势。GitHub…

随着时间的推移,Twitch 的游戏、类型、频道和收视率的趋势。GitHub…

随着时间的推移,Twitch 的游戏、类型、频道和收视率的趋势。GitHub…public.tableau.com](https://public.tableau.com/views/TwitchVisualizations/PickYourGame?:embed=y&:display_count=yes&:origin=viz_share_link&source=post_page-----e40d4a45e4ee----------------------&:showVizHome=no)

3.成长分数

一旦我们有了数据,我们想找到一种方法来对推荐的游戏进行排名。要做到这一点,我们需要考虑哪些可量化的属性可能使游戏有利于流媒体频道的增长。我们得出了以下每场比赛的时间函数指标:

  1. 流民亲和力—我们想根据流民以前的游戏推荐一款他们会喜欢的游戏。这通过使用推荐系统(惊喜包,使用相似用户矩阵)来量化
  2. 游戏增长——由于我们不想推荐一款垂死的游戏,我们想量化那些观众增长且得分较高的游戏。一个简单的表示可以通过查看一周前每个频道的观众数量来实现。
  3. 游戏受欢迎程度——因为 Twitch 默认按照观众数量降序排列游戏,所以一个更受欢迎的游戏可能有更好的机会获得新观众,因为它会更容易被寻找新内容的观众看到。我们可以用一场比赛的总观看人数占当时观看人数最多的比赛的观看人数的比例来量化这个。
  4. 市场渗透力 —有时观众会通过各种彩带被带到一个不知名或不太受欢迎的游戏,而彩带会随着忠实观众群从一个游戏跳到另一个游戏。除此之外,有时有些游戏没有人想看,但某个流是他们观看游戏的唯一原因,他们没有兴趣探索其他人。为了量化这一点,我们可以看看前 8 个 streamers 中的观众人数及其占该游戏观众总数的比例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Readable form of our metrics equation

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Hypothetical equation for Growth Score

4.推荐系统

我们使用的框架是 Python 的惊喜包。该软件包需要用户矩阵形式的输入,其中每个用户是一行,他们玩过的每个游戏是该行中的一列,值在 1-5 之间。根据这些分数,它可以使用各种算法(如 KNN、余弦相似度)向用户推荐相似的游戏。

因为我们没有每个流媒体播放者对他们玩的每个游戏的偏好,所以我们根据他们在每个游戏中的观众人数相对于他们播放的观众人数最高的游戏的比例来输入一个指标,然后在 1 到 5 之间进行缩放。这个指标将捕捉流媒体的观众更喜欢看什么,或者什么游戏对流媒体来说最成功。请注意,这个数字对异常值很敏感,可以通过在一段时间内取平均值来改善。

在将我们的矩阵通过惊喜包中可用的不同算法后,我们得到了以下测试 RMSE 结果。这些都是相对于惊喜预期的 1-5 级,因此在 10-20%的准确度范围内。利用这些数据,我们能够补充增长得分中的流光亲和力部分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.时机

基于观众和流媒体兴趣的推荐完成后,我们需要看看我们是否可以预测未来的游戏指标,以便提出流媒体播放时间的建议。为此,我们使用了长短期记忆(LSTM)神经网络。

LSTM 网络是一种递归神经网络(RNN ),具有使用 sigmoid 层“忘记”先前输入的额外能力。这与 RNNs 相比具有优势,因为它不需要在所有先前的(并且可能不重要的)数据点上浪费处理时间和空间,而是只关注具有有价值信号的数据点。你可以在这里了解更多关于 LSTM 的。

对于 LSTM 的输入,我们选择输入前 168 小时和过去 24 小时的模型状态,以便预测下周的性能。这些参数是在我们有限的 2 周数据集内制定的,因此它们肯定会根据建议的预期范围和可用数据进行更改。当预测游戏《英雄联盟》的观众人数时,LSTM 得出的样本内和样本外 RMSE 约为 10%,这意味着它可以很好地推广到未来的新数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过 LSTMs 运行每场比赛的观众数量数据变化和每场比赛的频道数量变化,我们可以计算下周任何时间点的比赛分数,并相应地对比赛和时间进行排名。不幸的是,我们最终没有时间来实现这一点,因为它需要为 100 多个游戏中的每一个训练两个 LSTM 模型,因此超出了我们的时间范围。然而,基本的想法仍然是合理的,并且可以在未来实际实施。

6.事后思考

总之,像我们正在构建的系统将有助于提高流注保留率,同时,理想情况下,也有助于为那些忠实于流注的用户保留用户。

为了量化这种功能的影响,我们可以进行 A/B 测试,将该功能随机分配给一组流媒体工具(可能包括一项调查,看看他们是否会根据我们的推荐播放推荐的游戏)。我们可以添加一个简单的推荐系统,而不需要将增长指标作为另一个测试用例来计算。一段时间后,我们可以比较这些组之间的频道增长,以量化推荐者的效果。

7.附加链接

这是我们在 Heroku 上建立并托管的网站的一个快速演示:

[## 网站演示视频

drive.google.com](https://drive.google.com/file/d/1rEY_Flt0ieiVEc1oqZm-59ydiPmJGkh2/view?usp=sharing)

代码可以在这里找到。

感谢阅读!如果你喜欢这篇文章,看看我的其他项目文章,如量化聊天室毒性

在 Python 中构建更高维度的有效边界

原文:https://towardsdatascience.com/building-a-higher-dimensional-efficient-frontier-in-python-ce66ca2eff7?source=collection_archive---------20-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现代投资组合理论认为,投资者要承担更多的风险,就必须有更高的预期回报。这一假设导致了有效边界的产生,它代表了对于给定的风险值具有最大预期回报的所有投资组合。许多投资组合经理使用这个工具来查看与其他可能的投资组合相比,他们承担了多少风险。然而,比较回报率和方差只是故事的一部分。由于股票市场具有大幅下跌的性质,了解你的投资组合的尾部风险,以及其他可能的投资组合的尾部风险是很重要的。出于这个原因,有效边界的当前构造可以通过包括更高的统计矩如偏度和峰度来修改。

获取数据

我将使用来自 quandl 的真实股票数据来构建这个投资组合。我将在我假设的投资组合中使用苹果、IBM 和亚马逊。

构建矩阵

包含每日日志回报的 log_ret 数据框架将用于构建有效边界。更高维的形式将包含预期收益、波动性、偏度和峰度。目前,python 不具备计算共生矩阵或峭度矩阵的能力,因此我们必须自己构建它们。提醒你一下,偏度和峰度的定义如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中,w 是权重向量,r 是每日回报,M3 是共生矩阵,M4 是峰度矩阵。偏斜度代表分布的不对称程度。正偏斜意味着分布更倾向于正值。由于下跌幅度大于上涨幅度,每日股票回报数据往往呈现负偏态。峰度衡量分布的峰值程度。一个大的峰度意味着一个分布比正态分布有更尖锐的峰值,但是有更厚的尾部。股票回报数据有很大的峰度,因为尾部受大事件支配。为了计算共生矩阵,我们可以运行下面的代码。

我们可以对峰度做同样的事情。

绘制有效边界

现在我们有了需要的矩阵,我们可以建立有效边界。以下将运行 10,000 次随机选择权重的试验,并计算预期收益、方差、偏斜度和峰度。输出是所有试验的结果,在 3 个空间维度上有回报、波动性和峰度,偏度显示为彩色地图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果在 jupyter 笔记本中运行,该图可以旋转,从而可以更清楚地了解分布情况。我们可以复制有效边界的原始形式,但是现在用彩色的偏度和峰度图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,投资组合可以有相同的预期收益和波动性,但有不同的偏度或峰度。

结论

投资组合经理需要对其投资组合中的风险有充分的了解。使用有效边界的标准方法不能为投资专业人士传递足够的信息。当与其他可能的权重分配相比时,将偏度和峰度添加到最优投资组合集合中可以更清楚地了解投资组合的情况。

建立语言毒性分类模型

原文:https://towardsdatascience.com/building-a-language-toxicity-classification-model-b006ae6981a4?source=collection_archive---------30-----------------------

使用谷歌云自动语言

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Left: Jigsaw (Conservation AI). Right: AutoML for Natural Language

在线社区是互联网互动的重要组成部分。这种社区的兴起伴随着对社区节制的需要,以确保参与者遵守他们规定的指导方针,并避免“清楚和明显”的和客观的政策违反。

由于存在怀有恶意的不良行为者,节制工作带来了特殊的挑战。其中之一是由于不良行为者的规模,花费在调节在线社区上的时间。更重要的是,这些违规行为的数量可能会导致他们中的一些人越过版主,并且在特定的社区中可能是极其危险的。一个例子是一个淫秽文本在一个 K-12 儿童的在线社区中通过了版主。

此外,适度的工作可能会导致其他挑战,如判断的一致性,更重要的是,由于不断接触图形和淫秽材料,心理上的危险。

Google Cloud AutoML for Natural Language 为语言识别用例提供了设计和开发定制语言模型的平台。本文使用 Google Cloud AutoML for Natural Language 开发了一个端到端的语言毒性分类模型来识别淫秽文本。使用神经架构搜索和转移学习的概念来寻找最佳网络架构和最佳超参数设置,以提高模型的性能。

关于数据集

这个项目使用的数据来自 Jigsaw 和 Google 在 Kaggle 上发起的有毒评论分类挑战。数据被修改为具有 16,000 个有毒和 16,000 个无毒单词的样本作为输入,以在 AutoML NLP 上构建模型。

第 1 部分:在 GCP 上启用自动语言

(1).去云控制台:【https://cloud.google.com/

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Google Cloud Homepage

(2).通过单击 GCP 仪表盘左上角的三连破折号打开 Cloud AutoML 自然语言。选择人工智能产品部分下的 自然语言

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Open AutoML Natural Language

(3).选择自动自然语言下的文本分类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Text Classification under AutoML Natural Language

(4).设置项目 API、权限和云存储桶,以存储用于建模和其他 AutoML 资产的文本文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Setup Project APIs and Permissions

(5).询问时,从下拉列表中选择您的 GCP 帐单项目。现在我们准备创建一个数据集,用于在 AutoML 上构建定制分类模型。在将原始数据集从 Kaggle 下载到云存储并准备好用于 AutoML 建模的数据后,我们将返回这里。

在这种情况下,自动创建的 bucket 被称为:g s://ekabasandbox-lcm

第 2 部分:将数据集下载到 Google 云存储中

(1).激活云外壳(红色圆圈中)以启动临时 VM 实例来存放从 Kaggle 下载的数据集,将其解压缩并上传到存储桶。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Activate Cloud Shell

(2).安装 Kaggle 命令行界面。这个工具将允许我们从 Kaggle 下载数据集。运行以下代码:

sudo pip install kaggle

但是,请注意,云外壳实例是短暂的,当会话结束时,它不会保存系统范围内的更改。此外,如果数据集特别大,还有其他选择,例如启动计算虚拟机,下载数据集,将其解压缩,然后上传到云存储。可以设计其他高级数据管道,将数据输入 GCP 进行分析/机器学习。

(3).下载 Kaggle API 令牌密钥,使 Kaggle CLI 能够针对 Kaggle 进行身份验证/授权,以下载所需的数据集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Create API Token

  • 将令牌下载到您的本地机器,并将其上传到云 shell。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 将上传的.json键移动到目录.kaggle中。使用下面的代码:
mv kaggle.json .kaggle/kaggle.json

(4).从 Kaggle 下载数据集到 Google 云存储。

kaggle competitions download -c jigsaw-toxic-comment-classification-challenge

(5).解压缩下载的数据集

unzip jigsaw-toxic-comment-classification-challenge.zip

(6).将数据集从临时云外壳实例移动到创建的云存储桶。在此插入您的存储桶名称。

gsutil -m cp -r train.csv test.csv gs://**ekabasandbox-lcm**/toxicity/

第 3 部分:为建模准备数据集

(1).在谷歌云 AI 平台上推出 Jupyter 笔记本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Notebooks of GCP AI Platform

(2).创建新的笔记本实例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Start a new JupyterLab instance

(3).选择实例名称并创建。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Choose an instance name and create

(4).打开 JupyterLab。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Open JupyterLab

(5).在使用 AutoML Cloud 自然语言构建自定义语言分类模型之前,必须以特定格式准备数据集:

  1. 培训输入可以是(.txt)格式或作为(.csv)文件。多个文本可以组合成一个压缩的(。zip)文件。
  2. 对于此项目,文本文件放置在子文件夹中,其输出标签作为文件夹名称。这在以后用于创建包含数据文件路径及其标签的 CSV 文件。文件夹结构示例如下所示:
    →【文件】
    — →【有毒】
    — →【干净】
  3. 接下来,生成一个指向文本路径及其相应标签的 CSV。云 NLP 使用 CSV 文件指向训练文档或单词的位置及其对应的标签。CSV 文件放在配置 AutoML NLP 时创建的同一个 GCS 存储桶中。在我们的例子中,这个桶被命名为 gs:// ekabasandbox-lcm

(6).从 Github 克隆预处理脚本。点击用红色圈出的图标,输入 Github URLhttps://Github . com/dvdbisong/automl-toxity-classification . git,用预处理代码克隆 repo。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Clone preprocessing script

(7).运行笔记本nlp_preprocessing.ipynb中的所有单元格,创建包含图像路径和标签的 CSV 文件,并将该文件上传到云存储。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Run notebook cells

第 4 部分:用 Cloud AutoML 自然语言建模

(1).从 AutoML 自然语言仪表板中单击“新建数据集”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Create New Dataset

(2).填写数据集名称,并从 AutoML 创建的云存储桶中选择 CSV 文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Create Dataset

(3).现在,如果您看到错误消息“找到重复的文件”,您可以退出。据我所知,从文件名来看,情况并非如此。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Cloud AutoML Processed Text

(4).点击列车,如上图红色所示,启动 Cloud AutoML 建模。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Start AutoML Training

(5).开始模特训练。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Train the New Model

(6).模型训练完成后,单击“评估”查看模型的性能指标。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Evaluate model performance

(7).评估性能指标(精确度、召回率和混淆矩阵)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Evaluation page. Left: Precision and Recall score. Right: Confusion matrix and precision, recall graphs

第 5 部分:测试模型

(1).点击预测选项卡测试模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Predict model

(2).我们可以通过从维基百科传入关于尼日利亚的介绍性文本来探索这个模型。我们期望模型预测是**干净的。**通过进一步探索,该模型在“清晰明显”的有毒样本上表现良好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Model test: clean text prediction

定制语言分类模型也作为 REST 或 Python API 公开,用于集成到软件应用程序中,作为推理的预测服务。

第六部分:结论

本文提供了一个通过利用 Google 云平台 AutoML 自然语言为定制用例设计强大的语言分类模型的演练。此外,该模型托管在云上,用于作为预测服务进行推理。这是构建和部署语言分类产品的强大机制。我相信这个示例可以作为使用 AutoML 构建自然语言产品的模板。最后,确保删除不再需要的模型和数据集,这将节省云成本。

本文节选自 Apress (Springer Nature)出版的《在 Google 云平台上构建机器学习和深度学习模型》一书。

在 Go 中构建英雄联盟团队推荐器

原文:https://towardsdatascience.com/building-a-league-of-legends-champions-recommender-system-in-go-and-how-to-deploy-it-in-the-cloud-1ee7a4fb55ee?source=collection_archive---------13-----------------------

如何从头开始构建推荐系统,并部署在云中

自从机器学习的热潮开始以来,我们在线生活的许多方面都成为了它的目标。特别是,它的主要用例之一——推荐系统——在我们的生活中变得如此普遍,以至于有时我们会忘记它们的存在。这些智能平台试图向我们推荐一些东西——在许多情况下,我们甚至不知道它的存在——基于我们以前互动过的其他东西,即购买、选择、看到的东西。购物、选择吃什么、浏览电影库等日常活动是智能推荐功能增强的部分任务。

尽管推荐系统隐约出现在我们数字生活的几个方面,但一个或多或少没有被它触及的领域是视频游戏领域。

迄今为止,一些作者和研究人员已经撰写和开发了关于游戏推荐系统的论文和项目。他们中的大多数探索概念,例如一个人基于其先前的游戏选择购买游戏的可能性有多大,例如,喜欢口袋妖怪的人可能喜欢马里奥,或者基于特征和概念的选择,例如,喜欢运动的人可能喜欢最新的篮球或足球游戏。这些项目的细节是,它们是围绕视频游戏作为一种产品,作为你购买的东西,更像是一个推荐电影的系统,它们都不是游戏本身的实现功能。

很久以前,游戏只是一个简单的软件,你可以把它交上去,完成故事,然后继续前进。今天的游戏已经变得更加复杂互联社交。对于复杂这个术语,我指的是玩家在游戏中必须做出的高级决策选择。关于连接性,我指的是每天都有更多的游戏是“仅限在线”的,甚至提供购买、广告和实施被称为游戏即服务的商业模式。最后,今天的游戏更加社会化——有些游戏在同一个区域有 100 名玩家,有些大型在线游戏有成千上万的用户共享一个世界,甚至有些游戏可以让我们将其体验带到其他外部平台,如专有的类似社交的中心。

将这三种属性混合在一起,我们就可以把它看作是一个数据生成器。诸如玩家在游戏中做出的决定,或者玩家参与的每个队友,以及比赛的每个结果都是我们可以跟踪、分析并最终从中学习的数据点。

在这篇文章中,我介绍了一个我用 Go 为游戏英雄联盟 (LoL)从头开始构建的推荐系统。LoL 是一个多人在线战场游戏(MOBA ),有两个由五名玩家组成的队伍(默认模式)互相竞争。用通俗的话来说,我们可以把游戏的目标总结成下面这句话:每支队伍都必须在对方队伍消灭你的队伍之前到达对方队伍的中枢(被称为 Nexus)并摧毁它。这项任务并不容易,因为每一方都有防御建筑、物品和爪牙来阻碍你的每一步。然而,LoL 的真正复杂性在于战士,或者像他们在游戏中所说的冠军

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Choices

英雄联盟有 141 个冠军(实验时),没有一个是完美的;他们有弱点,也有优点,有些擅长某些任务,有些则很糟糕。在每场比赛开始时,每个选手都要选出自己的冠军。然而,在游戏中选择冠军有特定的规则,更重要的是冠军不能被复制,也就是说每个冠军只能有一个副本。这个规则有一些罕见的例外,甚至更多的限制,例如每个玩家选择其冠军的顺序,但为了简单起见(在这个项目的第一个版本中),系统不会考虑这些情况。

我创建的推荐系统处理那些冠军,它的目的是推荐一个完整的团队组成,也就是每个玩家应该选择的五个冠军。此外,除了拥有一个推荐系统之外,我的目标是构建一个完整的程序,通过 web 服务提供推荐服务。

在整篇文章中,我解释了设计系统的所有步骤,包括数据收集过程、基准测试和推荐算法。此外,我还描述了如何将项目转移到 Docker 容器中,并使用部署在云中的 web 服务公开建议。

哦,我不属于暴乱游戏。这个作品呈现了我为了好玩而做的一个项目:)。

这些建议

这个项目的中心愿景是为英雄联盟比赛推荐一个完整的团队组成,即由五个不同的冠军组成的团队。这些建议,基于以前看到的训练数据,应该(希望!)好到可以用在真实的游戏里。

该算法

该系统采用了一种基于邻域的协同过滤算法,该算法使用 k-NN (k-nearest neighbors)来查找其最近的对象。在训练时,因为 k-NN 是一个懒惰的学习者,所以模型不会学习一个目标函数。相反,一旦向系统发出预测请求,它将“记忆”数据并从中进行归纳。在这个预测阶段,该算法从它的搜索空间循环通过每一个训练样本,并计算它们和输入特征向量之间的距离。一旦完成距离的计算,它们将按降序排列,算法最终返回前 N 个项目。

该算法的输入是一个不完整的团队组成,换句话说,是一个冠军名单,由其中的一个、两个、三个或四个组成。输出是一个完整的团队组成,由算法认为最合适的五个冠军组成。例如,输入向量[ashe, drmundo]可能会返回[ashe, drmundo, masteryi, yasuo, zyra]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The recommender system is wondering what’s the best recommendation. Champions images taken from https://na.leagueoflegends.com/en/game-info/champions/. Robot icon was taken from https://www.flaticon.com/authors/smashicons

数据

我通过 Python 包装库 Riot-Watcher 从 Riot Games 的英雄联盟 API 获得了用于训练系统的数据。获得最终的数据集并不是一个简单的过程,因为我需要的数据是获胜球队的组成,换句话说,我想知道哪五个冠军赢得了比赛。然而,除了返回当前正在进行的特征匹配的端点之外,没有从过去的随机匹配中获得数据的直接方法。那么,我们如何从这里着手呢?

这里的主要目标是获得许多比赛 id,这样我们就可以使用这些 id 来获得关于比赛的信息,特别是球队组成和比赛结果(谁赢了)。我的第一步是使用*“特色游戏”端点来检索所述特色比赛的玩家的召唤师姓名。然后我调用了召唤师(玩家)端点,使用召唤师名字作为参数来获取它的匹配和参与者;这样做的目的是获得尽可能多的帐户 id。这些帐户 id 随后被用来获取更多的匹配 id,这些 id 最终在“match”*端点中被用来获取我需要的数据。

顺便提一下,我应该提到 Riot API 有速率限制,所以收集数据的过程需要几天时间。我还想声明,我这样做是出于好意。在任何时候,我的目标都不是误用或滥用系统,所以这似乎是不道德的,或者如果有人——主要来自 Riot——认为我应该取消这个解释,请与我沟通。

所以,我们终于有了数据。然而,它不是有用的形状,因此作为所有与数据相关的东西,我不得不花一些时间处理我的数据,将它转换成一个由N行(每个观察)和M列组成的数据集,其中每一列代表一个冠军。如果所述冠军是团队配置的一部分,则这些行的值是01。我的最终数据集的形状是 110064 x 141。

推荐引擎

尽管推荐算法是该系统的核心,但其他几个功能改善了整个引擎。首先,在算法中,有距离的概念,这是一个对象(模型搜索空间中的一个团队)与另一个对象之间距离的度量。然后,我们有了我命名为拦截偶然发现推荐以及洗牌操作的概念。这三个是我在系统中实现的增强技术,用于修改推荐输出。最后,该平台还包含一个 web 服务形式的抽象层,用于向用户提供推荐。在接下来的小节中,我将详述这些设计。

距离

在预测期间,该算法计算训练样本和给定输入向量之间的距离,以找到最接近的团队配置。因为这是算法的重要部分,所以我实现了四种不同的距离方法,用户可以在执行预测之前选择这四种方法。这些是欧几里德曼哈顿距离、余弦相似度皮尔逊相关度。对于后一个,皮尔逊相关性,我修改了它,因为我想和其他的保持一致,最小值为零。

洗牌

shuffle 是我添加的一个可选操作,它打乱了算法在向量空间中循环计算距离的顺序。否则,向量空间总是按照加载每个训练示例的顺序来遍历,例如,第 1 行、第 2 行、第 3 行等等。我将在下一个例子中解释我为什么这样做。

假设我们有由三个观察值组成数据集:X,Y,Z(按照精确的顺序),且我们想要得到输入向量I的单个预测(用k=1)。系统所做的是计算(X, I)(Y,I)(Z,I)之间的距离。

现在想象一下(X, I)(Y, I)之间的距离完全相同。在这种情况下,在对距离排序后(如算法部分所述),算法总是推荐团队组成 X,因为它在 y 之前。

然而!如果我们打乱算法遍历训练集的顺序,我们可能会遇到算法首先计算(Y, I)而不是(X, I)之间的距离的情况,在这种情况下,推荐的团队是 y

这个有用吗?很难说。

拦截推荐

为了让推荐更容易阅读和理解,我实现了一个我称之为截取推荐的东西。通常,默认情况下,推荐的输出是 k 个团队组成的列表,其中 k 是所选邻居的数量。这个列表的问题是,它没有直接指出哪一个团队应该是最好的。相反,用户应该根据自己的知识最终决定应该使用 k 组中的哪一个。

截距推荐所做的是计算所有 k 个推荐团队的截距,以返回出现在所有 k 个推荐中的冠军,因此用户不是有几个可能的团队,而是收到一个可能的和最合适的冠军列表,这些冠军对于其团队来说应该是最佳的。

例如,假设一个带有k=3的普通推荐返回[1,2,3,4,5[29,10,2,3,8][3,4,2,22,81]。在截距推荐下,输出将是[2,3],因为这些冠军出现在三个原始推荐中。

偶然推荐

偶然发现的推荐是一种推荐模式,通过向算法的输出添加额外的团队组成(在预测步骤中计算)来增强。在推荐系统中,意外收获的概念代表了获得可能让用户感到惊讶的推荐的想法,或者如论文 通过协作方法引入意外收获 “找到好的或有用的东西,而
没有专门搜索它。”

该算法获得特殊的偶然推荐(在已经计算了正常推荐的项目之后)所采取的步骤如下:

  1. 获取下 k 个最接近的项目,其中 k 是邻居的数量。换句话说,如果是k=3,那么算法会在之前找到的推荐之后寻找三个推荐(有点像使用k=6)。
  2. 构建一个包含所有冠军的频率(出现次数)的数组,以了解每个冠军的计数。
  3. 根据频率使用前 5 名冠军创建新的推荐。

这个特性背后的直觉是增加搜索空间(通过上升k)来找到出现最多的冠军。这种方法的主要优点是,系统正在构建一个全新的团队,否则无法根据频率找到。然而,另一方面,配置可能是可怕的。

网络服务

这个项目的主要目标之一是向外界提供建议。因此,我编写了一个简单的 REST API,将系统的“推荐”组件包装在一个端点下。我命名为“推荐”的这个端点接收一个 JSON 作为输入,它应该遵循以下结构(这是 Go 代码):

type PredictionInput struct {
    Champions   []string `json:"champions"`
    Intercept   bool     `json:"intercept"`
    Shuffle     bool     `json:"shuffle"`
    Serendipity bool     `json:"serendipity"`
}

首先,JSON 需要一个冠军的列表,也就是我们当前的团队,或者我们希望使用推荐完成的团队。除此之外,JSON 接受三个布尔值,指定算法是否应该计算截距或意外发现推荐,或者执行洗牌。

为了编译和运行服务,从项目的根目录执行make run-service-local,或者如果你已经安装了 Docker ,命令make run-service-docker下载并运行图像。

当服务正在运行时(无论是在本地还是从 Docker),执行如下命令以获得建议。:

$ curl -d '{"champions":["jax", "ashe"], "intercept": true}' -H "Content-Type: application/json" -X POST [http://localhost:8080/recommend](http://localhost:8080/recommend)

云部署

有了文档化的推荐引擎和服务于推荐的 web 服务,我的下一步是让每个人都可以访问这个系统。因此我选择将它部署在云平台应用程序 Heroku 中。

在 Heroku 中部署 Docker 映像是一个相当简单的过程。创建新帐户后,转到项目的根目录(或任何包含 docker 文件的目录)并执行:

$ heroku container: login登录注册表容器

这个命令用一个随机生成的名字命名应用程序(我的名字是 evening-citadel

$ heroku container:push web -a name_of_app推送 Docker 图像

$ heroku container:release web -a name_of_app释放它。

如需进一步说明,请访问:https://dev center . heroku . com/articles/container-registry-and-runtime

我的网络应用程序在 http://evening-citadel-74818.herokuapp.com可用

要查询它,运行前面的命令,但是用地址替换 localhost。像这样:

$ curl -d '{"champions": ["ashe", "drmundo"], "intercept": false, "shuffle": true}' -H "Content-Type: application/json" -X POST [http://evening-citadel-74818.herokuapp.com:80/recommend](http://evening-citadel-74818.herokuapp.com:80/recommend)

注意:系统是在自由层机器上运行的,所以如果它关闭或变慢,请提前道歉。还有,不要滥用:)

基准

懒惰学习者算法,比如这个实验中使用的算法,通常比它的对手,渴望学习者要慢。这种系统不会从训练数据中进行归纳,因此出现了“懒惰”一词,直到收到预测请求。然后,在预测阶段,算法必须扫描完整的训练集来计算其结果。为了了解我的系统的延迟,我对一个 4000 行的测试数据集进行了测试,以收集关于预测每个建议所用时间的数据。在下图中,我们可以看到时间(以秒为单位)如何类似于右偏分布,这表明在一些情况下,预测时间远离平均值。然而,由于这些值本身非常小,几毫秒的差异是不明显的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总结和结论

本文一步一步地解释了如何从头构建一个用 Golang 编写的基于邻居的推荐系统来预测英雄联盟冠军,以及如何在 Heroku 中部署它。首先,我提到了视频游戏中推荐系统的现状和这个项目的前景。接下来,我解释了算法、数据和获取数据的步骤。然后我们进入了系统的细节,了解了它的特性和云部署流程。最后,我展示了一些基准。

我应该提到的是,这个引擎既不完美也不完整,它是我们如何为视频游戏建立推荐系统的概念证明。英雄联盟和游戏的冠军选择组件涉及更复杂的过程,如禁止冠军(任何玩家都不能使用的冠军),以及每个玩家选择其冠军的顺序。这些过程需要高水平的专业知识,这是不容易自动化或学习的。

在未来,我想添加更多的功能,如自动更新数据集,按游戏类型过滤推荐,甚至尝试在算法中添加“球员位置”的概念。

关于如何使用该服务的代码、脚本、说明以及本文中介绍的所有内容都可以在我的 GitHub 上获得(见下面的链接),所以我想邀请人们贡献并试验这个项目或服务(同样,请不要滥用它)。

[## juandes/lol-推荐系统

用 Golang 写的基于邻居的推荐系统推荐英雄联盟的队伍…

github.com](https://github.com/juandes/lol-recommendation-system)

感谢阅读,希望你从中有所收获。如果你有任何问题,评论,疑问,或者想聊天,请在这里留下评论,或者在 Twitter 上给我写信,我很乐意帮助你。

[## 胡安·德迪奥斯·桑托斯(@ jdiosantos)|推特

胡安·德迪奥斯·桑托斯的最新推文(@ jdiossantos)。机器学习/数据工程师。还有,口袋妖怪大师,还有…

twitter.com](https://twitter.com/jdiossantos)

用 Python 构建一个最小的区块链

原文:https://towardsdatascience.com/building-a-minimal-blockchain-in-python-4f2e9934101d?source=collection_archive---------4-----------------------

通过编码了解区块链

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Shaojie on Unsplash

区块链不仅仅是比特币或其他加密货币。它也用于数据库,如健康记录。

与某种流行的观念相反,区块链与数据加密无关。事实上,区块链中的所有数据都是透明的。它的特别之处在于它(在一定程度上)防止了回溯和数据篡改。让我们使用 Python 实现一个最小的区块链。下面是我如何构建一个最小的区块链,代码可以在 GitHub 上找到。

因为这是区块链的最小实现,所以在任何分布式网络上都没有算法或工作证明。

散列法

我们想要一个可以代表一个数据块的“键”。我们想要一个很难伪造或暴力破解的密钥,但是很容易验证。这就是哈希的用武之地。散列是满足以下性质的函数 H(x ):

  • 相同的输入x总是产生相同的输出H(x)
  • 不同(甚至相似)的输入x应该产生 完全 不同的输出H(x)
  • 从输入x中获得H(x)在计算上很容易,但要逆转这个过程却很难,即从已知散列H中获得输入x

这就是谷歌如何存储你的“密码”,而不是实际存储你的密码。他们存储你的密码散列H(password),这样他们可以通过散列你的输入和比较来验证你的密码。没有进入太多的细节,我们将使用 SHA-256 算法散列我们的块。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by CMDR Shane on Unsplash

最小块

我们来做一个名为MinimalBlock()的对象类。它是通过提供一个index、一个timestamp、一些你想要存储的data和一个叫做previous_hash的东西来初始化的。前一个哈希是前一个块的哈希(键),它充当指针,这样我们就知道哪个块是前一个块,从而知道块是如何连接的。

换句话说,Block[x]包含索引x、时间戳、一些数据和前一个块 x-1 H(Block[x-1])的散列。现在这个块已经完成了,它可以被散列以生成H(Block[x])作为下一个块中的指针。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Aiden Marples on Unsplash

最小链

区块链本质上是一个区块链,通过存储前一个区块的哈希来建立连接。因此,可以使用 Python 列表实现一个链,而blocks[i]表示第{ i }个块。

当我们初始化一个链时,我们用函数get_genesis_block()自动分配一个第 0 块(也称为 Genesis 块)给这个链。这一块标志着你的链的开始。注意previous_hash在创世纪块中是任意的。添加块可以通过调用add_block()来实现。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Rustic Vegan on Unsplash

数据验证

数据完整性对数据库非常重要,区块链提供了一种验证所有数据的简单方法。在函数verify()中,我们检查以下内容:

  • blocks[i]中的索引是i,因此没有丢失或额外的块。
  • 计算块哈希H(blocks[i]),并与记录的哈希进行交叉检查。即使块中的单个位被改变,计算出的块散列也会完全不同。
  • 验证H(blocks[i])是否正确存储在下一个程序块的previous_hash中。
  • 通过查看时间戳来检查是否有回溯。

分支

在某些情况下,您可能想从一个链中分支出来。这被称为分叉,如代码中的fork()所示。你可以复制一个链(或者链的根),然后分道扬镳。在 Python 中使用deepcopy()至关重要,因为 Python 列表是可变的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by elCarito on Unsplash

外卖

对一个块进行哈希运算会创建一个块的唯一标识符,并且一个块的哈希构成下一个块的一部分,以在块之间建立链接。只有相同的数据才会创建相同的哈希。

如果您想修改第三个块中的数据,第三个块的散列值会改变,第四个块中的previous_hash也需要改变。previous_hash是第 4 个块的一部分,因此它的散列也会改变,依此类推。

相关文章

感谢您的阅读!如果您对 Python 感兴趣,请查看以下文章:

[## 我希望我能早点知道的 5 个 Python 特性

超越 lambda、map 和 filter 的 Python 技巧

towardsdatascience.com](/5-python-features-i-wish-i-had-known-earlier-bc16e4a13bf4) [## 使用交互式地图和动画可视化伦敦的自行车移动性

探索 Python 中的数据可视化工具

towardsdatascience.com](/visualizing-bike-mobility-in-london-using-interactive-maps-for-absolute-beginners-3b9f55ccb59)

最初发布于edenau . github . io

建立模型?这是你应该问的第一个问题

原文:https://towardsdatascience.com/building-a-model-heres-the-first-question-you-should-ask-828befec5ac?source=collection_archive---------12-----------------------

无论你的模型是解释性的还是预测性的,都会对它的设计产生深远的影响

有人正在某个地方建造一个模型。事实上很多很多人。无论是为了商业、学术研究还是个人兴趣,人们已经越来越多地使用数学来模拟现实世界的现象,以便产生洞察力或就如何控制或应对这些现象做出决策。

最近,由于更强大的计算能力,建模变得更加复杂。模型不是 Excel 电子表格中的几个单元格,而是建立在各种平台和各种编程语言上。有的是基于小数据,有的是基于大数据。创建它们的工作从几个小时到持续几个月甚至几年的迭代项目不等。

但往往这些模型的创建者在开始之前并没有问足够多的问题。他们可以不假思索地投入其中。获取一些数据,建立一些公式,你就可以开始了。我在数学和统计学领域工作多年,了解到模型的成功在很大程度上取决于在打开数据文件之前的预先思考。

特别是,有一个问题我总是在一开始就问——我相信分析师、数据科学家和其他建模者也应该总是问:我的模型应该是解释性的还是预测性的?

从文字上来看,这可能是显而易见的,但是一个解释性模型被创建来帮助理解为什么事情会发生。它可以帮助回答这样的问题:为什么这种疾病似乎发生在这些类型的人身上?是什么导致了温度激增?一个预测模型被创建来尽可能准确地预测将会发生什么——它将回答这样的问题:我们预计明天会有多少人来参观这个购物中心?在下次选举中,每个政党将获得多少选票?

说明这一点的一个非常简单的方法是使用柠檬水摊主的类比。柠檬水摊主会使用一个解释性模型来了解她的客户喜欢她的产品的原因,或者为什么她在中午比晚上有更多的客户-她基本上对柠檬水感兴趣,以及它为什么畅销。然而,如果她的主要目标是确保她在本周剩余的时间里有足够的柠檬,她会使用预测模型来帮助她。

模型很少能同时达到这两个目标。我想我从来没有建立过一个模型,既能很好地解释一种现象,又能很好地预测这种现象。这是有充分理由的。在本文中,我将阐述这种选择如何影响您构建模型的每个部分,从最初的数据输入开始,一直到您如何衡量它的有效性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.输入数据的选择(一次性或重复使用)

如果模型是解释性的,那么建模过程只发生一次,或者在将来偶尔发生。当务之急是尽可能深入地理解这个问题。因此,没有超出范围的数据源。格式不良且需要大量清理的数据可以放在这个列表中。即使是不存在电子版本且仍在档案柜中的旧数据,也可以考虑进行数字化处理,以尽可能做到详尽无遗。同样,为了挖掘更深层次的解释变量,某些数据可能会从模型中删除。在医学模型中,年龄可能会被删除,因为它是疾病易感性中的一个已知因素,并且它可能会主导该模型并掩盖其他重要因素。

预测模型设计为反复运行,以便可以利用定型集中确定的关系,根据输入模型的新数据进行预测。因此,数据的选择主要是基于将来在模型中运行的可用性。在许多现代环境中,这通常意味着预测模型被限制为仅使用连接源中的数据,这些数据容易获得并预先格式化以与模型一起工作。此外,通常主要目标是准确预测,因此任何有助于提高预测准确性的数据都在发挥作用(尽管通常应该对预测模型中准确性和归纳偏差之间的权衡进行有益的讨论)。

2.使用的建模技术(可解释的或“黑盒”)

对于解释性模型来说,适合解释的建模技术至关重要。洞察力的控制在解释模型中至关重要。在逻辑回归中,优势比可以帮助我们理解输入变量影响因变量的程度。更简单的决策树模型可能具有有用的解释目的,因为它们可以帮助识别和量化某些决策点对结果的影响。

预测模型很少考虑可解释性。你可能听说过“黑盒模型”这个术语,用来描述一个最大化预测能力的模型,但是这个模型本质上太复杂了,无法梳理出单个输入因素的影响。神经网络是非常常见的黑盒模型。它们在幕后非常复杂,并基于成百上千个模拟和互联的神经元做出决策,每个神经元都根据从训练集中学习到的行为采取行动。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.衡量模型的性能(拟合与准确性)

解释性模型主要通过它们产生的洞察力和整体拟合度来判断。拟合优度是因变量的期望值和实际观察值之间接近程度的度量。即使整体拟合度很差,解释性模型也有可能产生有价值的见解,这确实很常见——例如,在我主要从事的社会科学领域,这很常见。解释性建模结果中使用的典型测量方法包括优势比、R 平方(包括伪 R 平方)、卡方检验和 G 检验。

预测模型的生死取决于它们的准确性。准确性测量通常涉及回归模型中误差的计算,或者分类模型中真阳性和假阳性之间的权衡。平均绝对误差和均方根误差等测量值通常用于描述回归模型的预测效果。精确度、回忆、ROC 曲线下面积或 F1 分数(对于不平衡模型)是用于评估预测准确性的更典型的度量。

多年来,我已经学会了设身处地为柠檬水摊主着想的习惯。我对柠檬水还是柠檬感兴趣?这是一个非常好的习惯,我希望你能养成。

最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在 LinkedIn Twitter上找我。

使用使用谷歌图像创建的数据集的电影流派分类器

原文:https://towardsdatascience.com/building-a-movie-genre-classifier-using-a-dataset-created-using-google-images-4752f75a1d79?source=collection_archive---------20-----------------------

使用 fast-ai

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

source: wallpaperup

在为分类问题构建数据库时,“谷歌图片”是找到相关图片的绝佳来源。就拿电影海报按流派分类的问题来说吧。我们将选三门重叠最少的课:爱情、恐怖和超级英雄。

创建数据集

获取 URL 列表:

第一步是获取一个我们可以下载图片的 URL 列表。为此,进入谷歌图片,搜索你感兴趣的图片。向下滚动,直到你看到所有你想下载的图片,或者直到你看到一个按钮,上面写着“显示更多结果”。你滚动过的所有图片现在都可以下载了。要了解更多信息,请点击按钮,然后继续滚动。Google Images 显示的最大图片数量为 700 张。

在 Windows/Linux 中按 Ctrl+Shift+J,在 Mac 中按 Cmd+Opt+J,打开 JavaScript 控制台。使用以下几行 JS 代码获取 URL 并将它们保存到一个文件中:

urls **=** Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el**=>**JSON.parse(el.textContent).ou);
window.open('data:text/csv;charset=utf-8,' **+** escape(urls.join('\n')));

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source: Horror movie posters on Google images

然后,我们将 URL 的文件上传到我们的工作目录,在为我们的电影数据集创建的文件夹中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下载图像和查看数据:

我们可以使用 fast.ai 提供的download_images函数下载所有使用 URL 文件的图像,并使用‘show _ batch’函数查看它们:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练模型

既然我们的数据集已经准备好了,我们就可以训练模型了。我使用 ResNet-34 模型的预训练权重进行迁移学习。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们现在有 80.5%的准确率。

解释

很多时候,低准确性是由于数据集的问题,例如训练或验证集中的错误标记的图像、可能属于多个高概率类的图像等。我们可以按如下方式查看混淆矩阵和最混淆的图像:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在大多数情况下,我们可以清楚地看到分类器混淆的原因。例如,第一张电影海报(银河护卫队)在我们的数据集中被错误地标记为“浪漫”,第二张图像没有任何恐怖的成分,因此被标记为“恐怖”,第五张海报(蝙蝠侠)看起来很像“恐怖”电影海报,第六张海报似乎有点不相关,第八张海报(侠探杰克)甚至对于一个人来说,仅仅看一眼海报(没有任何其他背景)并将其归类为“超级英雄”电影是不可能的。

清理

既然我们已经确定了导致问题的图像,我们可以通过删除问题图像来清理我们的数据集。我们可以使用 fast.ai 提供的“ ImageCleaner ”小部件,或者编写一个自定义函数来查看损失最大的图像,并删除我们认为有问题的图像(确保不要删除您认为分类器分类错误的图像,尽管它们显然属于给定的标签)。此外,创建原始数据集的副本,以防万一你想回到旧版本。

完成清洁后,再次训练模型,看看是否有任何改进。

以下是我清理数据集并再次训练后的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试我们的分类器

找到一个不属于数据集的图像来测试您的分类器。将其上传到您的工作目录,并按如下方式进行测试:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实验:

为了尝试和改进分类器,我想到并尝试了一些技术:

  1. 减少训练图片的数量:这背后的动机是当你在谷歌图片搜索中向下滚动时,图片变得不那么相关。因此,为了确保我不会在我的数据集中看到许多不相关的图片,我将每个类的图片数量限制为 80 张。解冻前我得到了 68.8%的准确率,解冻后得到了 82.2%的准确率。即使在清理之后,准确率也没有提高多少,事实上,因为图像的数量更少,所以准确率更低了。这清楚地表明,为了有一个训练有素的模型,我们需要有大量的数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.增加训练图像的数量:通过使用 600 张图像,我注意到不解冻的准确率下降到 43%,解冻一次清理后,我能达到的最高准确率是 49.4%。低精度很可能是因为存在更多的不相关图像,如前一点所述。事实证明,最好有较少但相关的图片。

3.**精选图片:**尽管在大多数情况下非常不切实际,但尝试在每个类别中精选大约 100 张图片并观察结果会很有趣。

4.用 resnet50 代替 resnet34 进行迁移学习:在迁移学习中,我们使用的基础模型非常重要。我们可以尝试不同的模型,挑选最好的一个。

5.增加/改变课程:在这个实验中,我注意到恐怖和超级英雄电影海报有一个非常相似的主题(黑暗、阴郁、严肃),尝试更多互斥的课程可能会得到更好的结果。

观察

我们的分类器已经大致了解了这三种类型的典型电影海报的外观。虽然我们已经尽量减少重叠课,但事实证明,很多超级英雄电影海报看起来很恐怖,恐怖电影海报看起来很浪漫等等。然而,87.2%的准确率已经相当不错了,因为我们是从零开始构建数据集的,而且类别重叠的几率很高。

使用熊猫构建电影推荐引擎

原文:https://towardsdatascience.com/building-a-movie-recommendation-engine-using-pandas-e0a105ed6762?source=collection_archive---------9-----------------------

探索推荐引擎背后的基本直觉。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

概观

推荐引擎是基本上计算两个实体之间相似性的程序,在此基础上,它们给我们有针对性的输出。如果我们看看任何推荐引擎的根源,它们都试图找出两个实体之间的相似性。然后,所计算的相似性可以用于推导各种类型的推荐以及它们之间的关系。

推荐引擎大多基于以下技术:

  1. 基于流行度的过滤。
  2. 协作过滤(基于用户/基于项目)。
  3. 基于混合用户项目的协同过滤。
  4. 基于内容的过滤。

基于人气的过滤

推荐引擎最基本的形式是引擎向所有用户推荐最受欢迎的项目。这将是普遍的,因为每个人都会得到类似的建议,因为我们没有个性化的建议。这些类型的推荐引擎是基于的基于流行度的过滤。这种模式的使用案例是新闻网站上当天的“头条新闻”部分,在这里,不管每个用户的兴趣如何,对每个人来说最受欢迎的新闻都是一样的,因为这符合逻辑,因为新闻是一种一般化的东西,与用户的兴趣无关。

协同过滤

在协同过滤中,两个实体基于它们之间的某些相似性来协作推断推荐。这些过滤技术大致有两种类型:

  1. **基于用户的协同过滤:**在基于用户的协同过滤中,我们找出两个用户之间的相似性得分。基于相似性得分,我们将一个用户购买/喜欢的项目推荐给其他用户,假设他可能基于相似性喜欢这些项目。当我们着手实施时,这一点会更加清楚。主要的在线流媒体服务,网飞都有基于用户协同过滤的推荐引擎。
  2. **基于项目的协同过滤:**在基于项目的协同过滤中,项目的相似度是用现有用户正在消费的现有项目来计算的。然后,基于相似性的数量,我们可以说,如果用户 X 喜欢项目 A,并且新的项目 P 与项目 A 最相似,那么我们向用户 X 推荐项目 P 是非常有意义的
  3. **基于混合用户项目的协同过滤:**这种技术基本上是上述两种技术的适当混合,其中推荐不仅仅基于任一种。像亚马逊这样的电子商务网站采用这种技术向顾客推荐商品。
  4. **基于内容的过滤:**在这种技术中,向用户推荐他们以前最用过/看过/喜欢的相似内容。例如,如果用户一直在听相似类型的歌曲(比特率、bps、曲调等)。),他将被推荐属于基于某些特征决定的相同类别的歌曲。这一类别最好的例子是 Pandora Radio ,这是一个音乐流媒体和自动音乐推荐网络电台服务。

编码和实现

我们有一个电影镜头数据库,我们的目标是使用 pandas 从头开始应用各种推荐技术,并找出用户、最受欢迎的电影和基于基于用户的协同过滤的目标用户个性化推荐之间的相似性。(我们只探索其中一种类型,因为这些文章是关于获得推荐引擎背后的基本直觉。)

我们正在从数学库中导入熊猫和一些基本的数学函数,并将数据集导入 dataframe 对象。

**# Importing the required libraries.**
import pandas as pd
from math import pow, sqrt**# Reading ratings dataset into a pandas dataframe object.**
r_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']
ratings = pd.read_csv('data/ratings.dat', sep='::', names=r_cols,
 encoding='latin-1')**# Getting number of users and movies from the dataset.**
user_ids = ratings.user_id.unique().tolist()
movie_ids = ratings.movie_id.unique().tolist()
print('Number of Users: {}'.format(len(user_ids)))
print('Number of Movies: {}'.format(len(movie_ids)))**Output:** Number of Users: **6040**
Number of Movies: **3706**

这是我们的数据集的前 5 行的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Ratings Dataset

在这个数据集中,我们有 4 列和大约 1M 行。除了, **unix_timestamp,**所有的列都是自解释的。无论如何,我们不会在代码中使用这个列。接下来,让我们看看我们的电影数据集是什么样子的。

**# Reading movies dataset into a pandas dataframe object.**
m_cols = ['movie_id', 'movie_title', 'genre']
movies = pd.read_csv('data/movies.dat', sep='::', names=m_cols, encoding='latin-1')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Movie Dataset

所有的列名都是不言自明的。如上面的数据帧所示,类型列包含带有管道分隔符的数据,这些数据不能作为建议进行处理。因此,我们需要为每个流派类型生成列,这样,如果电影属于该流派,其值将为 1,否则为 0(类似于一种热门编码)。此外,我们需要从 movie_title 列中分离出年度发布,并为其生成一个新列,这也是一个新的重要特性。

**# Getting series of lists by applying split operation.**
movies.genre = movies.genre.str.split('|')**# Getting distinct genre types for generating columns of genre type.**
genre_columns = list(set([j for i in movies['genre'].tolist() for j in i]))**# Iterating over every list to create and fill values into columns.**
for j in genre_columns:
    movies[j] = 0
for i in range(movies.shape[0]):
    for j in genre_columns:
        if(j in movies['genre'].iloc[i]):
            movies.loc[i,j] = 1**# Separting movie title and year part using split function.**
split_values = movies['movie_title'].str.split("(", n = 1, expand = True)**# setting 'movie_title' values to title part.**
movies.movie_title = split_values[0]**# creating 'release_year' column.**
movies['release_year'] = split_values[1]**# Cleaning the release_year series.**
movies['release_year'] = movies.release_year.str.replace(')','')**# dropping 'genre' columns as it has already been one hot encoded.**
movies.drop('genre',axis=1,inplace=True)

以下是数据帧处理后的样子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Data Frame View for Movies Dataset After Pre-Processing

现在,让我们写下一些在我们的代码中经常使用的 getter 函数,这样我们就不需要一遍又一遍地编写它们,这也增加了代码的可读性和可重用性。

**# Getting the rating given by a user to a movie.**
**def get_rating_(userid,movieid):**
    return (ratings.loc[(ratings.user_id==userid) & (ratings.movie_id == movieid),'rating'].iloc[0])**# Getting the list of all movie ids the specified user has rated.**
**def get_movieids_(userid):**
    return (ratings.loc[(ratings.user_id==userid),'movie_id'].tolist())**# Getting the movie titles against the movie id.**
**def get_movie_title_(movieid):**
    return (movies.loc[(movies.movie_id == movieid),'movie_title'].iloc[0])

相似性得分

在该实现中,将基于两个用户之间的距离(即欧几里德距离)并通过计算两个用户之间的皮尔逊相关来计算两个用户之间的相似性。

我们将编写两个函数,一个基于欧几里得距离计算相似性,另一个基于皮尔逊相关,这样你就知道我们为什么要编写两个函数了。

**def distance_similarity_score(user1,user2):**
    '''
    user1 & user2 : user ids of two users between which similarity        score is to be calculated.
    '''
    # Count of movies watched by both the users.
    both_watch_count = 0
    for element in ratings.loc[ratings.user_id==user1,'movie_id'].tolist():
        if element in ratings.loc[ratings.user_id==user2,'movie_id'].tolist():
            both_watch_count += 1
    if both_watch_count == 0 :
        return 0

    # Calculating distance based similarity between both the users.
    distance = []
    for element in ratings.loc[ratings.user_id==user1,'movie_id'].tolist():
        if element in ratings.loc[ratings.user_id==user2,'movie_id'].tolist():
            rating1 = get_rating_(user1,element)
            rating2 = get_rating_(user2,element)
            distance.append(pow(rating1 - rating2, 2))
    total_distance = sum(distance)

    # Adding one to the denominator to avoid divide by zero error.
    return 1/(1+sqrt(total_distance))print('Distance based similarity between user ids 1 & 310: {}'.format(distance_similarity_score(1,310)))Output:
Distance based similarity between user ids 1 & 310: **0.14459058185587106**

基于距离计算相似性得分有一个固有的问题。我们没有阈值来决定在计算用户是否足够近或足够远时要考虑两个用户之间的距离。另一方面,皮尔逊相关方法解决了这个问题,因为它总是返回-1 & 1 之间的值,这清楚地为我们提供了我们喜欢的接近度的边界。

**def pearson_correlation_score(user1,user2):**
    '''
    user1 & user2 : user ids of two users between which similarity score is to be calculated.
    '''
    # A list of movies watched by both the users.
    both_watch_count = []

    # Finding movies watched by both the users.
    for element in ratings.loc[ratings.user_id==user1,'movie_id'].tolist():
        if element in ratings.loc[ratings.user_id==user2,'movie_id'].tolist():
            both_watch_count.append(element)

    # Returning '0' correlation for bo common movies.
    if len(both_watch_count) == 0 :
        return 0

    # Calculating Co-Variances.
    rating_sum_1 = sum([get_rating_(user1,element) for element in both_watch_count])
    rating_sum_2 = sum([get_rating_(user2,element) for element in both_watch_count])
    rating_squared_sum_1 = sum([pow(get_rating_(user1,element),2) for element in both_watch_count])
    rating_squared_sum_2 = sum([pow(get_rating_(user2,element),2) for element in both_watch_count])
    product_sum_rating = sum([get_rating_(user1,element) * get_rating_(user2,element) for element in both_watch_count])

    # Returning pearson correlation between both the users.
    numerator = product_sum_rating - ((rating_sum_1 * rating_sum_2) / len(both_watch_count))
    denominator = sqrt((rating_squared_sum_1 - pow(rating_sum_1,2) / len(both_watch_count)) * (rating_squared_sum_2 - pow(rating_sum_2,2) / len(both_watch_count)))

    # Handling 'Divide by Zero' error.
    if denominator == 0:
        return 0
    return numerator/denominatorprint('Pearson Corelation between user ids 11 & 30: {}'.format(pearson_correlation_score(11,30)))Output:
Pearson Corelation between user ids 11 & 30: **0.2042571684752679**

最相似的用户

目标是找出与目标用户最相似的用户。这里,我们有两个指标来计算分数,即距离和相关性。现在,我们将为此编写一个函数。

def most_similar_users_(user1,number_of_users,metric='pearson'):
    '''
    user1 : Targeted User
    number_of_users : number of most similar users you want to user1.
    metric : metric to be used to calculate inter-user similarity score. ('pearson' or else)
    '''
    # Getting distinct user ids.
    user_ids = ratings.user_id.unique().tolist()

    # Getting similarity score between targeted and every other suer in the list(or subset of the list).
    if(metric == 'pearson'):
        similarity_score = [(pearson_correlation_score(user1,nth_user),nth_user) for nth_user in user_ids[:100] if nth_user != user1]
    else:
        similarity_score = [(distance_similarity_score(user1,nth_user),nth_user) for nth_user in user_ids[:100] if nth_user != user1]

    # Sorting in descending order.
    similarity_score.sort()
    similarity_score.reverse()

    # Returning the top most 'number_of_users' similar users. 
    return similarity_score[:number_of_users]print(most_similar_users_(23,5))Output:
**[(0.936585811581694, 61), (0.7076731463403717, 41), (0.6123724356957956, 21), (0.5970863767331771, 25), (0.5477225575051661, 64)]**

正如我们所看到的,输出是元组列表,指示使用用户 id 查询的前 5 个相似数量的用户相对于目标用户的相似性得分。这里使用的指标是皮尔逊相关。

我不知道是否很少有人注意到,最相似的用户的逻辑可以通过考虑其他因素,如年龄、性别、职业等来加强。这里,我们仅根据一个特征(即评级)创建了我们的逻辑。

为目标用户获取电影推荐

概念很简单。首先,我们需要只迭代那些没有被目标用户观看(或评级)的电影,以及基于与目标用户高度相关的用户的子设置项。这里,我们使用了加权相似性方法,其中我们考虑了评级和分数的乘积,以确保高度相似的用户比不太相似的用户对推荐的影响更大。然后,我们根据分数和电影 id 对列表进行排序,并根据这些电影 id 返回电影标题。让我们为此写一个函数。

def get_recommendation_(userid):
    user_ids = ratings.user_id.unique().tolist()
    total = {}
    similariy_sum = {}

    # Iterating over subset of user ids.
    for user in user_ids[:100]:

        # not comparing the user to itself (obviously!)
        if user == userid:
            continue

        # Getting similarity score between the users.
        score = pearson_correlation_score(userid,user)

        # not considering users having zero or less similarity score.
        if score <= 0:
            continue

        # Getting weighted similarity score and sum of similarities between both the users.
        for movieid in get_movieids_(user):
            # Only considering not watched/rated movies
            if movieid not in get_movieids_(userid) or get_rating_(userid,movieid) == 0:
                total[movieid] = 0
                total[movieid] += get_rating_(user,movieid) * score
                similariy_sum[movieid] = 0
                similariy_sum[movieid] += score

    # Normalizing ratings
    ranking = [(tot/similariy_sum[movieid],movieid) for movieid,tot in total.items()]
    ranking.sort()
    ranking.reverse()

    # Getting movie titles against the movie ids.
    recommendations = [get_movie_title_(movieid) for score,movieid in ranking]
    return recommendations[:10]print(get_recommendation_(32))Output:
**['Invisible Man, The ', 'Creature From the Black Lagoon, The ', 'Hellraiser ', 'Almost Famous ', 'Way of the Gun, The ', 'Shane ', 'Naked Gun 2 1/2: The Smell of Fear, The ', "Kelly's Heroes ", 'Official Story, The ', 'Everything You Always Wanted to Know About Sex ']**

正如我们在输出中看到的,我们已经使用度量皮尔逊相关性为用户 id 为 32 的用户获得了前 10 部强烈推荐的电影。你可以用欧几里得距离做同样的练习,我相信结果会不同。

学习和结论

我们通过使用熊猫和基本的数学库函数实现了一个电影推荐引擎。此外,我们了解了推荐引擎背后的基本直觉。显然,推荐引擎的功能远不止这些,因为有多种功能和因素会影响推荐,而不仅仅是评级。此外,在下一篇博客中,我们还将基于用户和电影的其他特征来实现和推断我们的推荐,并探索推荐引擎的臭名昭著的技术,即使用 turicreate 库的矩阵分解

博客中代码的 GitHub 存储库可以在这里找到。

使用 BERT 和 TensorFlow 构建多标签文本分类器

原文:https://towardsdatascience.com/building-a-multi-label-text-classifier-using-bert-and-tensorflow-f188e0ecdc5d?source=collection_archive---------1-----------------------

多标签分类问题中,训练集由实例组成,每个实例可以被分配多个类别,这些类别被表示为一组目标标签,

  • 一条短信可以同时涉及宗教、政治、金融或教育中的任何一个,也可以什么都不涉及。
  • 一部电影可以根据其概要内容分为动作片、喜剧片和爱情片。一部电影有可能分成多种类型,比如浪漫喜剧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

source

多类分类问题有何不同?

多类分类中,每个样本被分配到一个且只有一个标签:水果可以是苹果或梨,但不能同时是两者。让我们考虑一个三类 C= ["太阳,“月亮,云”]的例子。在多类中,每个样本只能属于一个 C 类。在多标签情况下,每个样本可以属于一个或多个类别。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Source

资料组

对于我们的讨论,我们将使用 Kaggle 的 有毒评论分类挑战 数据集,该数据集由大量维基百科评论组成,这些评论已被人类评级者标记为有毒行为。毒性的类型有:

**toxic, severe_toxic, obscene, threat, insult, identity_hate**

示例:

“**Hi! I am back again! Last warning! Stop undoing my edits or die!**”

被标记为[1,0,0,1,0,0]。意思是它既是toxic 又是threat

关于数据集的详细 EDA,请参见此处的。

让我们简单地讨论一下伯特

2018 年 10 月,谷歌发布了一个新的语言表示模型,名为 BERT ,代表来自变形金刚的双向编码器表示。 BERT 基于最近在预训练上下文表示方面的工作,包括半监督序列学习生成性预训练ELMoULMFit 。然而,与这些之前的模型不同,BERT 是第一个深度双向无监督语言表示,仅使用纯文本语料库(维基百科)进行预训练。

预先训练的表示可以是 上下文无关的上下文相关的

  1. 上下文无关模型,如 word2vecGloVe 为词汇表中的每个单词生成单个单词嵌入表示。例如,单词“ bank ”在“ bank account ”和“ bank of the river”中具有相同的上下文无关的表示。
  2. 上下文模型代之以生成基于句子中其他单词的每个单词的表示。上下文表示还可以是单向的或双向的。例如,在句子“我访问了银行账户”中,基于“我访问了”而不是“账户”,单向上下文模型将表示“银行”然而,BERT 使用它的上一个和下一个上下文来表示“银行”——“我访问了 … 账户”——从一个深度神经网络的最底层开始,使它成为深度双向的。

基于双向 LSTM 的语言模型训练标准的从左到右的语言模型,并且还训练从右到左(反向)的语言模型,该语言模型从随后的单词预测先前的单词,如在 ELMO。在 ELMo 中,前向语言模型和后向语言模型各有一个 LSTM。关键的区别在于,LSTM 没有同时考虑前一个和后一个记号。

为什么 BERT 优于其他双向模型?

直观地说,深度双向模型比从左到右模型或者从左到右和从右到左模型的结合更强大。不幸的是,标准的条件语言模型只能从左到右或从右到左进行训练,因为双向条件反射将允许每个单词在多层上下文中间接“看到自己”。

为了解决这个问题,BERT 使用“屏蔽”技术屏蔽掉输入中的一些单词,然后双向调节每个单词以预测被屏蔽的单词。例如:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Forward, Backward, and Masked Language Modeling

BERT 还通过对一个非常简单的任务进行预训练来学习建立句子之间的关系模型,这个任务可以从任何文本语料库中生成:给定两个句子 A 和 B,B 是语料库中 A 后面的下一个句子,还是只是一个随机的句子?例如:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这只是对伯特的一个非常基本的概述。详细内容请参考最初的论文和一些参考文献【1】、【2】。

**好消息:**谷歌已经将 BERT 上传到 TensorFlow Hub 这意味着我们可以直接使用预先训练好的模型来解决我们的自然语言处理问题,无论是文本分类还是句子相似度等。

预测电影评论的例子,二进制分类问题作为示例代码提供在存储库中。在本文中,我们将重点讨论 BERT 在多标签文本分类问题中的应用。因此,我们将基本上修改示例代码,并应用必要的更改,使其适用于多标签场景。

设置

使用!pip install bert-tensorflow安装 BERT

下载预训练的 BERT 模型:这些是权重和其他必要的文件,用于表示 BERT 在预训练中学习到的信息。你需要选择你想要的 BERT 预训练重量。有两种方法可以下载和使用预训练的 BERT 模型:

  1. 直接使用 tensorflow-hub:

以下预先训练的模型可供选择。

  1. BERT-Base, Uncased : 12 层,768 隐,12 头,110M 参数
  2. BERT-Large, Uncased : 24 层,1024 隐,16 头,340 米参数
  3. BERT-Base, Cased : 12 层,768 隐,12 头,110M 参数
  4. BERT-Large, Cased : 24 层,1024 隐,16 头,340 米参数
  5. BERT-Base, Multilingual Case : 104 种语言,12 层,768 隐,12 头,110M 参数
  6. BERT-Base, Chinese:简体中文和繁体中文,12 层,768 隐藏,12 个头,110M 参数

我们将使用基本型号:‘未装箱 _ L-12 _ H-768 _ A-12’
**BERT_MODEL_HUB** = “[https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1](https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1)"

2.手动下载 BERT 模型文件:下载并保存到一个目录中,然后解压缩。以下是英文文件的链接:

  • [BERT-Base, Uncased](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip), [BERT-Base, Cased](https://storage.googleapis.com/bert_models/2018_10_18/cased_L-12_H-768_A-12.zip),
  • [BERT-Large, Cased](https://storage.googleapis.com/bert_models/2018_10_18/cased_L-24_H-1024_A-16.zip), [BERT-Large, Uncased](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-24_H-1024_A-16.zip)

您可以使用任何一种方式,但是让我们看看在预先训练的模型中实际上有哪些文件。当我下载[BERT-Base, Uncased](https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip),时,有如下 3 个重要文件:

**BERT_VOCAB**= ‘uncased-l12-h768-a12/vocab.txt' **BERT_INIT_CHKPNT** = ‘uncased-l12-h768-a12/bert_model.ckpt’ **BERT_CONFIG** = ‘uncased-l12-h768-a12/bert_config.json’

**BERT_VOCAB** : 包含模型词汇【单词到索引的映射】

**BERT_INIT_CHKPNT** : 包含预训练模型的权重

**BERT_CONFIG** : 包含 BERT 模型架构。

标记化

标记化包括将输入文本分解成单个单词。为此,第一步是创建 tokenizer 对象。有两种方法可以做到这一点:

  1. 直接来自张量流中心

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.从手动下载的文件:

使用**BERT_INIT_CHKPNT & BERT_VOCAB files**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建了记号赋予器之后,就该使用它了。让我们来修饰句子:“This here’s an example of using the BERT tokenizer”

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

词汇表的大小:~30K

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据预处理:

让我们首先阅读提供的数据集:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**train.head()**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们需要将数据转换成伯特能理解的格式。为此提供了一些实用函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**create_examples()**,读取数据帧并将输入文本和相应的目标标签加载到**InputExample** 对象中。

使用 tokenizer,我们将在示例中调用**convert_examples_to_features** 方法,将它们转换成 BERT 理解的特性。这个方法添加了特殊的“CLS”和“SEP”标记,BERT 使用它们来标识句子的开始和结束。它还将“索引”和“段”标记附加到每个输入中。因此,根据 BERT 格式化输入的所有工作都由该函数完成。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

BERT input representation. The input embeddings is the sum of the token embeddings, the segmentation embeddings and the position embeddings.

创建模型

这里,我们使用预训练的 BERT 模型,并针对我们的分类任务对其进行微调。基本上,我们加载预训练的模型,然后训练最后一层用于分类任务。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在多标签分类中,我们使用**sigmoid()**而不是**softmax()**来获得概率。

因此,为了计算概率,我们做了如下改变:

**### multi-class case: probabilities = tf.nn.softmax(logits)** **### multi-label case: probabilities = tf.nn.sigmoid(logits)**

为了计算每个示例的损失,tensorflow 提供了另一种方法:

[**tf.nn.sigmoid_cross_entropy_with_logits**](https://www.tensorflow.org/api_docs/python/tf/nn/sigmoid_cross_entropy_with_logits)测量离散分类任务中的概率误差,其中每个类别都是独立的且不互斥。这适用于多标签分类问题[4]。

其余的代码大部分来自 BERT 参考文献[5]。完整的代码可以在 github 获得。

Kaggle 提交分数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

仅仅通过运行 2 个纪元,就获得了非常好的结果。这就是迁移学习的强大之处:使用在庞大数据集上训练过的预训练模型,然后针对特定任务对其进行微调。Kaggle 代码这里

因此,在其他一些数据集上进行试验,运行几个时期[3–4]并查看结果。

感谢阅读。

参考

[1]https://ai . Google blog . com/2018/11/open-sourcing-Bert-state-of-art-pre . html

[2]https://ml explained . com/2019/01/07/paper-parsed-Bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding-explained/

[3]https://stack overflow . com/questions/47034888/how-to-choose-cross-entropy-loss-in-tensor flow

[4]https://www . tensor flow . org/API _ docs/python/TF/nn/sigmoid _ cross _ entropy _ with _ logits

[5]https://github . com/Google-research/Bert/blob/master/run _ classifier . py

[6]https://www . depends-on-the-definition . com/guide-to-multi-label-class ification-with-neural-networks/

[7]https://towards data science . com/journey-to-the-centre-of-multi-label-class ification-384 c 40229 BFF

[8]https://gombru.github.io/2018/05/23/cross_entropy_loss/

用 PyTorch 构建概率矩阵分解的音乐推荐引擎

原文:https://towardsdatascience.com/building-a-music-recommendation-engine-with-probabilistic-matrix-factorization-in-pytorch-7d2934067d4a?source=collection_archive---------4-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

推荐系统是现代社会中最广泛的机器学习形式之一。无论你是在网飞上寻找下一个节目,还是在 Spotify 上收听自动音乐播放列表,推荐系统几乎影响着现代用户体验的所有方面。构建推荐系统最常见的方法之一是使用矩阵分解,这种方法可以根据以前的评分和其他用户的偏好来预测用户对特定产品的评分。在本文中,我将使用一个音乐评级数据集,通过 PyTorch 构建一个音乐推荐引擎,试图阐明概率矩阵分解模型的理论和实现背后的细节。

推荐系统简介

在这一节中,在介绍音乐推荐项目的实际实现和结果之前,我将尝试快速而全面地介绍推荐系统和矩阵分解。

什么是推荐系统?

要创建一个推荐系统,我们需要一个包含用户、项目和评分的数据集。用户可以是从商店购买产品的顾客、从家里观看电影的家庭,或者在我们的例子中,甚至是听音乐的人。类似地,项目是用户正在购买、评级、收听等的产品。因此,推荐系统的数据集通常有许多条目,包括用户-项目对和表示用户对该项目的评级的值,例如下面看到的数据集:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1: Simple Data Set for Recommendation Systems

可以明确地收集用户评级(要求用户给出 5 星评级,要求用户是否喜欢某个产品,等等)。)或隐含地(检查购买历史、鼠标移动、收听历史等。).此外,评分值可以是二进制的(用户购买产品)、离散的(对产品的评分为 1-5 星)或几乎任何其他值。事实上,这个项目使用的数据集将评级表示为用户听一个艺术家的总分钟数(这是一个隐含的评级!).数据集中的每个条目代表具有已知评级的用户项目配对,并且从这些已知的用户项目配对中,推荐系统试图预测未知的用户项目评级。可以通过找到与用户喜欢的项目相似的项目(基于项目)、找到相似的用户并推荐他们喜欢的东西(基于用户)、或者找到用户-项目交互之间的整体关系来创建推荐。以这种方式,模型推荐它认为用户可能喜欢的、用户还没有看过的不同项目。

很容易理解一个好的推荐系统的目的和价值,但是这里有一个很好的图表,我认为它很好地总结了推荐系统:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2: Recommender systems aim to find items that a user enjoys, but has not yet seen [1]

什么是矩阵分解?

矩阵分解是实现推荐系统的一种常用而有效的方法。使用前面提到的用户项目数据集(图 1),矩阵分解试图学习在低维空间中定量表征用户和项目的方法(而不是查看用户曾经评级的每个项目),以便这些表征可以用于预测用户行为和对一组已知的可能项目的偏好。近年来,矩阵分解由于其准确性和可扩展性而变得越来越流行。

使用用户-项目配对的数据集,可以创建一个矩阵,使所有行代表不同的用户,所有列代表不同的项目,矩阵中位置(I,j)处的每个条目代表用户 I 对项目 j 的评级。该矩阵如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3: A User-Item Recommendation Matrix

在上图中,每一行都包含一个用户对可能项目集中的每个项目的评分。但是,其中一些评级可能是空的(用“???").这些空白点代表未知的或者推荐系统试图预测的用户-项目配对。此外,这个被称为稀疏矩阵的部分填充矩阵可以被认为是两个矩阵的乘积。例如,上述 4×4 矩阵可以通过将两个 4×4 矩阵彼此相乘、4×10 矩阵与 10×4 矩阵相乘、4×2 矩阵与 2×4 矩阵相乘等等来创建。将原始矩阵分解为两个矩阵的乘积的过程称为矩阵分解。

推荐系统的矩阵分解

所以,我们知道我们可以通过找到两个可以相乘的矩阵来分解一个矩阵。但是,矩阵分解在推荐系统中实际上是如何工作的呢?

当这两个单独的矩阵被创建时,它们各自携带关于用户、项目以及它们之间的关系的单独信息。也就是说,其中一个矩阵将存储表征用户的信息,而另一个矩阵将存储关于项目的信息。事实上,用户(左)矩阵的每一行都是一个大小为 k 的向量,用于定量描述单个用户,而项目(右)矩阵的每一列都是一个大小为 k 的向量,用于描述单个项目。这些向量的大小 k 被称为潜在维度(或嵌入大小),是矩阵分解模型中必须调整的超参数,较大的潜在维度将允许模型捕捉更复杂的关系并存储更多信息,但也可能导致过度拟合。

这种想法乍一看似乎很奇怪,但如果您仔细研究矩阵如何相乘以创建用户-项目矩阵,它实际上很有意义—用户 I 对项目 j 的评级是通过从左矩阵中找到用户 I 的向量和从右矩阵中找到项目 j 的向量的内积来获得的。因此,如果每个用户和项目的这些矢量嵌入被训练来存储有用的特征信息,内积可以基于项目的特征和用户对这些特征的偏好来准确地预测用户-项目评级,这些都存储在嵌入矢量的值中。下图说明了这一概念:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4: Matrix Factorization with User (left) and Item (right) Matrices

在上图中,请注意产品矩阵的每个条目是如何导出的。例如,表示用户 0 对项目 0 的评分的用户项目矩阵的条目(0,0)是通过取左侧矩阵的第 0 行(用户 0 的嵌入向量)和右侧矩阵的第 0 列(项目 0 的嵌入向量)的内积获得的,这种模式对用户项目矩阵中的每个评分都是如此。因此,如前所述,通过取相应用户和项目嵌入向量的内积来预测每个评级,这是矩阵分解如何为推荐系统工作的核心思想。使用常见的训练方法,如随机梯度下降或交替最小二乘法,可以训练这两个矩阵以及其中的嵌入向量,以产生与原始用户项目矩阵非常相似的产品,从而创建准确的推荐模型。

但是等等…你不是说概率矩阵分解吗

是的,我做到了!别担心,概率矩阵分解和我到目前为止一直在解释的很像。正规矩阵分解和概率矩阵分解的主要区别在于生成矩阵的方式。对于正常的矩阵分解,所有未知值(我们试图预测的等级)被设置为某个常数(通常为 0 ),并且分解的矩阵被训练以再现整个矩阵,包括未知值。不难理解为什么这可能成为一个问题,因为矩阵分解模型预测的是我们实际上不知道的值,而这些值可能是完全错误的。此外,当我们已经训练我们的模型来预测所有用户定义的随机常数时,我们如何预测未知的用户项目评级?

为了解决这个问题,我们有概率矩阵分解。概率矩阵分解不是试图再现整个结果矩阵,而是仅试图再现训练集或已知评级集中的评级。因此,仅使用已知评级来训练模型,并且可以通过取用户和项目嵌入向量的内积来预测未知评级。

PyTorch 中的概率矩阵分解

现在您已经理解了推荐系统和概率矩阵分解背后的基础知识,我将概述如何使用 PyTorch 实现这种推荐系统的模型。如果您不熟悉 PyTorch,它是一个健壮的 python 框架,经常用于深度学习和科学计算。我鼓励任何对 PyTorch 感兴趣的人去查看一下,因为我在我的研究和个人项目中大量使用这个框架。

我们在努力实现什么?

如图 4 所示,我们的概率矩阵分解模型只有两个矩阵——多么简单!其中一个矩阵将代表每个用户的嵌入,而另一个矩阵将包含每个项目的嵌入。每个嵌入是描述用户或项目的 k 个值(k 是潜在维度)的向量。使用这两个矩阵,可以通过取用户的嵌入向量和项目的嵌入向量的内积来获得对用户-项目配对的评级预测,从而产生表示预测评级的单个值。下图显示了这样一个过程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 5: Predicting an unknown user-item rating

如上所述,可以通过从用户(左)和项目(右)矩阵中找到相应的用户和项目向量并计算它们的内积来确定单个评级。在这种情况下,结果显示选择的用户是冰淇淋的忠实粉丝——多么令人惊讶!然而,应该注意,这些矩阵中的每一个都是随机初始化的。因此,为了使这些预测和嵌入准确地表征用户和项目,必须使用一组已知的评级来训练它们,以便预测的准确性可以推广到未知的评级。

使用 PyTorch 中的嵌入类可以相对容易地实现这样一个模型,它创建了一个二维嵌入矩阵。使用这些嵌入中的两个,可以在 PyTorch 模块中创建概率矩阵分解模型,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 6: Simple matrix factorization implementation

矩阵分解模型包含两个嵌入矩阵,这两个嵌入矩阵在模型的构造函数中初始化,然后经过训练以准确预测未知的用户项目评分。在 forward 函数(用于预测评级)中,向模型传递一个小批量的索引值,这些值代表不同用户和项目的标识号(id)。使用这些作为用户-项目索引对传入的“cats”参数(一个 Nx2 矩阵)的索引,通过索引用户和项目嵌入矩阵并取相应向量的内积来获得预测的评级。

添加偏置

图 6 中的模型缺少一个创建强大的推荐系统所需要的重要特性——偏差项。偏差项基本上是分配给系统中每个用户和项目的常数值。当计算用户-项目配对的预测评级时,用户和项目的偏差都被添加到预测评级值中。这个过程可以从下面观察到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 7: Adding bias to the Probabilistic Matrix Factorization Model

从上面可以看出,向模型中添加偏差所需的更改量非常小。必须跟踪每个用户/项目的额外值,并将其添加到模型做出的每个预测的结果中。这些偏差值将与实际嵌入一起训练,以产生准确的预测。

向 PyTorch 中的模型添加偏差需要创建两个额外的嵌入——一个用于用户偏差,一个用于项目偏差。这些嵌入将有一个单独的列,每行将代表用户或项目的偏差值,其 ID 对应于该行索引。类似于嵌入向量,这些偏差值应该使用正向方法中“cats”参数中传递的索引来找到。实现如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 8: Implementing a Probabilistic Matrix Factorization Model with bias

从上面的实现中可以看出,针对用户和项目偏好,在模型中添加了两个新的嵌入。然后将这些偏差向量的值添加到正向方法中每个预测的结果中。

现在,已经实现了一个完整的概率矩阵分解模型——这可以用来创建非常强大的推荐系统,可以为几乎任何业务增加价值。然而,你可能会想,这个模型看起来和上一个非常相似,那么我们为什么要在模型中加入偏见呢?这真的会让事情变得更好吗?

为什么我们甚至需要偏见?

我们将首先考虑每个用户的偏差。如果一个用户有非常高的偏差值,这意味着什么?通常,如果用户经常给项目高的评级,则他们被分配高的偏差值,而经常分配低评级的用户被分配较低的偏差值。这是有意义的,因为一个平均评价很高的用户应该倾向于更高的评价,反之亦然。反过来,这种偏差可以允许模型通过学习适当的偏差值来将用户对项目进行评级的倾向与他们的实际项目偏好分开。

类似地,通常被给予高评级的项目被赋予大的偏差,而具有低平均评级的项目被给予低值。这使得被用户高度评价的项目偏向于高评价,因为大多数用户倾向于喜欢这样的项目,反之亦然。这可以在下图中看到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 9: Bias values for different items. Highly-rated items are given high bias values and vice versa.

用户或项目的平均评级会在数据集中产生不必要的噪声。通过包括偏差,模型可以学习从这种噪声中分别表征用户和项目,允许用户和项目嵌入更准确地反映每个用户或项目的属性和偏好。

创建音乐推荐系统

现在模型已经创建好了,我准备用它来创建一个音乐推荐系统。在这个项目中,我利用了用户和音乐家的数据集,其中每个用户-音乐家配对都根据用户听一个艺术家的时间分配了一个值。然后,我使用图 8 中的概率矩阵分解模型,从这个数据集中创建一个音乐推荐模型。

简单 EDA

我通过使用熊猫对数据集做一些简单的探索性数据分析(EDA)来开始这个实验。我分析的第一步是检查数据样本并检查是否有空值。不存在空值,数据如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 10: A sample from the original music data set

这个数据集看起来像一个普通的推荐系统数据集,它(显然)非常适合我们的模型。每个数据包含一个 userID、一个 artistID 和一个表示用户对该艺术家的评价的值。然而,这个数据集中代表用户-艺术家评级的权重值相当大,因为它们是由用户听一个艺术家所花的分钟数决定的。典型的推荐系统数据集包含 1-5 之间的评级、二进制评级(即 0 或 1)或类似的评级。因此,应该对该数据集中的评级值进行标准化,使其处于较小的范围内,具体做法如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 11: Normalizing weight values within the data set

在权重值被标准化之后,我开始检查数据集的稀疏性,或者与可能的用户-项目组合的数量相比较的已知评级的数量。最初,数据集中只有 0.2%的可能评级是已知的,这是非常稀疏的,可能会对模型的准确性产生负面影响。因此,我选择过滤数据集,只包括至少评价了五位艺术家的用户。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 12: Eliminating users with fewer than five ratings from data set

在数据集上执行上述操作后,它将数据集的密度增加到 1.3%,尽管仍然相对稀疏,但将允许矩阵分解模型做出更准确的预测。

完成这些更改后,在拟合模型之前,我还想做最后一项更改。在这个数据集中,每个用户和艺术家被分配一个唯一的 ID。然而,为了使推荐模型的使用更容易,我想让所有这些 id 都是连续的,这样它们就可以用于嵌入矩阵的索引。这可以通过下面的代码来实现,它确保所有用户和艺术家都有一个连续的、唯一的 ID:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 13: Ensuring that user and item IDs are contiguous

该代码为用户和项目创建了从原始 ID 到新的连续 ID 的映射,使得所有 ID 都落在范围[0,用户/艺术家总数]内。通过执行这种转换,用户和艺术家的 id 可以用作嵌入矩阵的索引,以便于查找和快速预测。

超参数选择/调整

既然已经对数据进行了筛选和预处理,那么就可以实际训练推荐模型了。然而,训练模型有两个必须正确设置的超参数:学习速率和潜在维度。

为了确定最佳的学习速率,我利用了在 fast.ai 深度学习课程中描述的自适应学习速率选择技术。这种技术通过多次迭代训练模型,提高了每次迭代更新模型参数的学习率。然后记录每次迭代的损失,并显示在图表上,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 14: Graph of the Loss (y-axis) vs. Learning Rate (x-axis)

在上图中,最佳初始学习率由损失开始增加之前的学习率的最大值表示,在本例中,该值约为 0.1。因此,在训练模型时,学习率最初被设置为. 1。

使用 20、30、40、50 和 60 的值通过网格搜索来确定最佳潜在维度。在使用这些嵌入大小中的每一个运行三个时期的训练之后,结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 15: Grid Search for the Optimal Embedding Size

在执行网格搜索之后,为音乐推荐系统选择 40 的嵌入大小,因为它在三个时期的训练之后具有最小的验证损失。

拟合模型

现在已经选择了超参数,可以训练模型了。一次进行三个时期的训练,并且每三个时期学习率降低约 2 倍,直到模型收敛。通过逐渐降低学习率,创建了一个简单的学习率调度程序,允许模型微调其参数,并尽可能减少损失。模型在整个训练过程中的损失可以在下图中看到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 16: Training the Recommender Model with Multiple Learning Rates

该模型以 0.1、. 05、. 01、. 005 和 0.001 的学习率训练了 3 个时期,最终 MSE 损失为 0.75。换句话说,所有对用户-艺术家配对的预测平均误差约为 0.86。假设训练和测试数据集中的所有评级都在范围[0,~60]内,平均误差. 86 相对较低,这表明该模型与数据拟合得相对较好!

额外分析

虽然概率矩阵分解在预测用户项目评分方面效果很好,但该模型最有趣的方面之一是它创建的用于定量描述每个用户和项目的嵌入。这些嵌入可以用于获得关于用户或产品的见解,作为其他机器学习模型(如深度神经网络)的输入,甚至可以确定用户最喜欢哪些项目!只是为了好玩,我对通过训练这个模型创建的嵌入进行了一些额外的分析,以查看它们是否携带任何有趣的信息。更具体地说,我检查了为数据集中的每个艺术家产生的偏差值,这些偏差值概括了所有用户对每个艺术家的偏好。在对偏差值及其相关艺术家进行排序后,产生了以下结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 17: Final Bias Values for Artists

可以看出,具有最高偏置值的艺术家是知名和成功的音乐家,如 Britney Spears 和 U2,从而证明了模型嵌入中包含的信息的有用性!

结论

非常感谢您的阅读,我希望您现在对推荐系统有了更好的理解,它们是如何工作的,以及您如何自己实现概率矩阵分解!如果你有兴趣探索这个项目的更多细节,我鼓励你去看看我创建的 GitHub 库,它包含了我在实现推荐系统中使用的所有代码的完整笔记本。此外,欢迎在 LinkedIn 或 Medium 上关注我,了解我未来的文章和工作。

来源/引用

[1]https://johnolamendy . WordPress . com/2015/10/14/collaborative-filtering-in-Apache-spark/

用 C#构建神经网络

原文:https://towardsdatascience.com/building-a-neural-network-framework-in-c-16ef56ce1fef?source=collection_archive---------1-----------------------

创建具有反向传播能力和基于进化的训练的神经网络。

介绍

我们将建立一个能够通过反向传播和进化进行学习的深度神经网络。代码将是可扩展的,以允许网络架构的改变,允许通过代码容易地修改网络执行的方式。

该网络是一个最低限度的可行产品,但可以很容易地扩大。你可以在 GitHub 上找到所有可用的代码,这包括突变和反向传播变体。

我将解释我们将如何设置前馈功能,设置所有需要的数组,并允许突变驱动的学习。

如果你想了解神经网络的工作原理,你需要熟悉一些基本的编码。对于反向传播,你需要熟悉梯度下降和微积分。除非您只想将代码用于您的项目。

概念时间!

我们的深度神经网络由一个输入层、任意数量的隐藏层和一个输出层组成,为了简单起见,我将只使用完全连接的层,但这些可以有许多不同的风格。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

A simple neural network model

神经网络体系结构

上面的模型在输入层上有 5 个神经元,如由 5 个实心圆组成的第一列所示。第二层有 4 个隐藏的神经元,输出层有 3 个输出神经元。这些层的大小和隐藏神经元的数量是任意的。但对于这种视觉模型,我选择了较小的尺寸。

其思想是,来自每个输入节点的值通过每个树突(模型上节点之间的连接线)并乘以相应树突持有的权重,然后传递并保存在下一层的神经元中,然后对每一层继续这一循环,直到获得输出;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个节点还有一个bias(用 b 表示),这有助于网络更好地运行。σ符号是这些乘积之和通过的激活函数**。其中 w =来自树突的权重,a =前一层中每个神经元的激活。这个过程在每个神经元上进行,直到到达输出层。**

当我们到达编码部分时,我们将更详细地讨论每一步,但是如果你对它没有一个基本的了解,我建议你去 youtube 或 Wikipedia 尝试并找到更多。 这个视频可能会有帮助

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

编码时间!

实现这种前馈功能的先决条件是一种存储所有数据的方法。我们将使用一系列阵列来存储所有数据,并确保网络性能始终处于最佳状态。

我们需要什么样的数据类型

一个输入数组来声明网络的大小,这将被标记为层。例如{5,4,3},这将创建一个类似于上图的网络模型。

我们还需要一个神经元阵列,用于保存前馈算法产生的值。这将是一个二维交错数组的形式

我们还需要另一个 2d 交错数组来存储每一层的偏差。

最后,我们需要一个 3d 交错数组来存储与每个树突相关的权重。偏差和权重是可以学习的,所有这些数组都是浮点数。

初始化功能

每当我们创建这个类时,我们需要确保网络的维度已经定义,所有适当的数组都已经初始化。

该函数采用网络维度的输入数组,并填充所有数组

private int[] layers;//layers    
private float[][] neurons;//neurons    
private float[][] biases;//biasses    
private float[][][] weights;//weights    
private int[] activations;//layerspublic float fitness = 0;//fitnesspublic NeuralNetwork(int[] layers)
{        
  this.layers = new int[layers.Length];        
  for (int i = 0; i < layers.Length; i++)        
  {            
    this.layers[i] = layers[i];        
  }        
  InitNeurons();        
  InitBiases();        
  InitWeights();    
}

首先,让我们来处理神经元,它们不需要分配任何值,我们只需要分配存储空间,因为数组的长度是静态的。我们使用列表作为创建数组的临时媒介。

//create empty storage array for the neurons in the network.
private void InitNeurons()
{        
  List<float[]> neuronsList = new List<float[]>();        
  for (int i = 0; i < layers.Length; i++)        
  {            
    neuronsList.Add(new float[layers[i]]);        
  }        
  neurons = neuronsList.ToArray();    
}

然后,我们可以在那里初始化偏差,偏差的大小与神经元的大小相同,只是我们需要填充每个槽,我将生成平均值为 0、标准偏差为 0.5 的每个偏差。当进行反向传播(消失和爆炸)时,有更好的初始化权重和偏差的方法来避免梯度问题,但是这对于遗传实现是不需要的。

//initializes and populates array for the biases being held within the network.
private void InitBiases()    
{        
  List<float[]> biasList = new List<float[]>();        
  for (int i = 0; i < layers.Length; i++)        
  {            
    float[] bias = new float[layers[i]];            
    for (int j = 0; j < layers[i]; j++)            
    {                
      bias[j] = UnityEngine.Random.Range(-0.5f, 0.5f);            
    }            
    biasList.Add(bias);        
  }        
  biases = biasList.ToArray();    
}

权重的生成类似于偏差,只是我们为前一层中的每个神经元的数组添加了另一个维度,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

the hierarchy for weight storage

//initializes random array for the weights being held in the network.
private void InitWeights()   
{        
  List<float[][]> weightsList = new List<float[][]>();        
  for (int i = 1; i < layers.Length; i++)        
  {            
    List<float[]> layerWeightsList = new List<float[]>();   
    int neuronsInPreviousLayer = layers[i - 1];            
    for (int j = 0; j < neurons[i].Length; j++)            
    {                 
      float[] neuronWeights = new float[neuronsInPreviousLayer];
      for (int k = 0; k < neuronsInPreviousLayer; k++)  
      {                                      
        neuronWeights[k] = UnityEngine.Random.Range(-0.5f, 0.5f); 
      }               
      layerWeightsList.Add(neuronWeights);            
    }            
    weightsList.Add(layerWeightsList.ToArray());        
  }        
  weights = weightsList.ToArray();    
}

前馈算法

前面所有的初始化函数都准备好了,是时候讨论实际的前馈算法和相关概念了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如前所述,这是在网络的隐层和输出层中为每个神经元计算的内容。让我们解释一下每个术语。从激活函数** σ开始,这背后的想法是,你传入加权和并返回非线性结果,这提高了性能并保持网络神经元在控制之下,在现实世界中,没有多少事情遵循线性,因此非线性可以帮助近似非线性现象,并允许反向传播。**

加权和,这是所有输入激活函数的数据。这是前一层中的每个节点乘以树突中的权重,数据通过该权重被传送到当前神经元。

****激活功能可由您选择,根据应用,不同的激活功能选择可能更有益。

该函数的常见变体包括:

  1. 身份
  2. 二进制步骤
  3. 乙状结肠的
  4. 双曲正切
  5. 热卢
  6. 泄漏 ReLU
  7. Softmax

现在,我将使用 Tanh 作为我选择的激活函数**,因为它允许正值和负值。尽管其他的将适用于不同的应用。**

Tanh 可以用以下格式表示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tanh function

这个非线性函数返回以下结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tanh Graph

public float activate(float value)    
{        
  return (float)Math.Tanh(value);    
}
//Luckily Unity provides a built in function for Tanh

现在我们来看看前馈函数如何迭代神经元以产生输出。

该程序遍历每个神经元,获得前一层中每个神经元的值,并将该值乘以连接两者的权重,通过激活函数运行该值,然后将其值设置为激活值。这是节点 1 的一个示例

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Example neuron calculation

//feed forward, inputs >==> outputs.
public float[] FeedForward(float[] inputs)    
{        
  for (int i = 0; i < inputs.Length; i++)        
  {            
    neurons[0][i] = inputs[i];        
  }        
  for (int i = 1; i < layers.Length; i++)        
  {            
    int layer = i - 1;            
    for (int j = 0; j < neurons[i].Length; j++)            
    {                
      float value = 0f;               
      for (int k = 0; k < neurons[i - 1].Length; k++)  
      {                    
        value += weights[i - 1][j][k] * neurons[i - 1][k];      
      }                
    neurons[i][j] = activate(value + biases[i][j]);            
    }        
  }        
  return neurons[neurons.Length - 1];    
}

有了它,我们就有了一个工作的神经网络,但目前它还没什么用,除非我们有一种方法来训练它。接下来让我们开始工作吧。

培养

对于这个网络的实现,我们将使用遗传算法。它们非常容易编码,并且很少涉及数学方面,但是,如果您对这个实现不感兴趣,我已经包含了一个反向传播代码示例,并且可能会使用强化学习方法。

遗传算法是训练神经网络很好地执行给定任务的一种方式。它工作得很好,因为你可以给它一个非常简单的适应度函数,来决定网络运行得有多好。它的缺点是在处理大型网络时需要相对较长的训练时间,与反向传播相比可能相当慢,但如果您有大量的计算能力,它可以返回比反向传播更好的结果,因为它们几乎总是能够达到全局最小值。

遗传算法背后的想法是基于达尔文主义的理论和生物进化是如何发生的,尽管我们将实现一个稍微简化的模型,但同样的总体概念也适用。一个物种的生物,他们有一个衡量他们“适合”的标准,从历史上看,这可能是他们的狩猎能力和繁殖能力,但有了我们的网络,这将是它能走多远,例如。然后我们整理种群,将它们分成两半,将上半部分克隆到下半部分,并对它们进行变异,这样网络的性能就趋向于全局最大值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Evolution algorithm

进化驱动的学习实现

网络的大部分管理将通过另一个脚本来完成。因此,目前我们需要添加到网络中的只是一种对网络进行分类的方法,一种将一个网络克隆到另一个网络上的方法,以及一种改变网络的方法。

对于排序,我们让类型 IComparable 的代码,这将允许我们在引用它时直接对它进行排序。它使用网络适应度作为网络排序的值

//Comparing For NeuralNetworks performance.
public int CompareTo(NeuralNetwork other)    
{        
  if (other == null) 
    return 1;    
  if (fitness > other.fitness)            
    return 1;        
  else if (fitness < other.fitness)            
    return -1;        
  else            
    return 0;    
}

我们还需要能够将可学习的值(权重和偏差)克隆到另一个神经网络上。

//this loads the biases and weights from within a file into the neural network.
public void Load(string path)
{        
  TextReader tr = new StreamReader(path);        
  int NumberOfLines = (int)new FileInfo(path).Length;        
  string[] ListLines = new string[NumberOfLines];        
  int index = 1;        
  for (int i = 1; i < NumberOfLines; i++)        
  {            
    ListLines[i] = tr.ReadLine();        
  }        
  tr.Close();        
  if (new FileInfo(path).Length > 0)        
  {            
    for (int i = 0; i < biases.Length; i++)            
    {               
      for (int j = 0; j < biases[i].Length; j++)                
      {                    
        biases[i][j] = float.Parse(ListLines[index]); 
        index++;                                   
      }            
    }             
    for (int i = 0; i < weights.Length; i++)            
    {                
      for (int j = 0; j < weights[i].Length; j++)                
      {                    
        for (int k = 0; k < weights[i][j].Length; k++)
        {                        
          weights[i][j][k] = float.Parse(ListLines[index]);    
          index++;                                        
        }                
      }            
    }        
  }    
}

最后,我们将需要在网络中轻微推动每个可学习的值的能力,这是通过变异来完成的。

//used as a simple mutation function for any genetic implementations.
public void Mutate(int chance, float val)    
{        
  for (int i = 0; i < biases.Length; i++)        
  {            
    for (int j = 0; j < biases[i].Length; j++)            
    {                
      biases[i][j] = (UnityEngine.Random.Range(0f, chance) <= 5) ? biases[i][j] += UnityEngine.Random.Range(-val, val) : biases[i][j]; 
    }        
  }  

  for (int i = 0; i < weights.Length; i++)        
  {            
    for (int j = 0; j < weights[i].Length; j++)            
    {                
      for (int k = 0; k < weights[i][j].Length; k++)                
      {                    
        weights[i][j][k] = (UnityEngine.Random.Range(0f, chance) <= 5) ?  weights[i][j][k] += UnityEngine.Random.Range(-val, val) : weights[i]  [j][k];

      }            
    }        
  }    
}

所有这些代码实现后,我们应该有一个能够学习的工作网络。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Unity 中实现网络

设置场景,这是我创建的一条赛道,带有检查点,我们的神经网络车必须通过这些检查点才能提高体能。在这种情况下,网络的目标是在轨道上导航,并在不与墙壁碰撞的情况下尽可能走远。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在场景中,我们有一个管理器,它产生、变异克隆体并破坏网络。网络的意义在于提高网络的性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时间框架是每一代机器人训练的时间,群体大小是一次训练多少个机器人,突变机会是每个权重或偏差发生突变的变化,突变强度是微移的标准偏差,游戏速度是游戏内的滴答率,如果你有适当的硬件,增加网络的训练时间。

管理者创建一群使用神经网络的预设,然后在每个预设中部署一个神经网络。一段时间后,测试将完成,网络将被分类,以便只有表现最好的被保留,没有表现最好的将被最好的网络复制并变异。然后再次部署,训练循环继续进行。

最后,我们需要制作学习者预置,它的功能有点类似于汽车,可以前后移动,也可以转弯;汽车的这两个输入将是网络的输出。网络的输入将是 5 个距离传感器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

经过几分钟的训练,我们得到了这个结果,这意味着所有的代码都工作了!

所有代码都可以在 GitHub 上找到,我应该注意到 GitHub 上的代码也有保存和加载功能,这些功能保存和加载网络的权重,以允许网络从它停止的地方恢复。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传****外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Google Drive 上的 项目

Github 上的突变项目。

Github 上的反向传播项目。

非常感谢您的阅读,我希望它能有所帮助。我欢迎任何问题或反馈。敬请期待!

通过基于本体的知识搜索构建类似 Alexa 的个人助理

原文:https://towardsdatascience.com/building-a-personal-assistant-like-alexa-open-domain-question-answering-7e9aa1e8ed90?source=collection_archive---------24-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

personal assistant battle

你有没有想过“如何打造一个私人助理?”像 Alexa谷歌个人助理谷歌主页覆盖多个意图以及开放领域问答视角。

在这篇文章中,我将尝试在更高的层次上介绍基于本体的问答知识搜索。

有两种广泛知识搜索方法

  1. 基于本体的知识搜索(主要通过知识图)
  2. 基于开放领域问答的知识搜索

在这篇文章中,我们将在更广的层面上讨论基于本体的知识搜索。

构建像 alexa 这样的个人助理需要各种构建模块,我们将在本文中介绍核心组件的高级架构。并将在接下来的系列中深入探讨。

积木

  1. 可扩展的知识图
  2. 从结构化和非结构化来源到知识图的知识消耗
  3. 结构化查询的自然语言理解
  4. 自然语言生成来回答问题
  5. 语音转文本
  6. 文本到语音
  7. 文本 2 内容模块

问答模块的高级架构(还不包括家庭自动化等)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Question Answering Architecture

该架构非常简单明了,目前还不包括可扩展性,但包含了问题回答模块的核心组件,如“谷歌个人助理”或“Alexa”。

NLU 层

NLU 层的主要目的是从文本中构建图形查询。这是基于本体的知识搜索的瓶颈之一,即从自由文本流中进行结构化查询(在 GQL 中)(开放领域问答试图以神经方式解决这个问题)

机器翻译块

在语言不可知知识图的情况下,这也不是强制性的。有两种方法可以建立语言不可知和语言感知的知识图。大多数公共知识图是语言不可知的(超出了本文的范围。

步伐

  1. 为支持的每种语言训练语音到文本模型
  2. 将结构化知识(主语、谓语、宾语)如维基数据或 dbpedia(抓取的维基百科)存储到知识图
  3. 将来自非结构化新闻文章和其他来源的知识丰富并存储到知识图中。
  4. Text2Intent 层理解(分类)用户意图
  5. 构建 NLU (自然语言理解)层,以理解用户查询或命令或意图(查看 rasa )
  6. 面向意向图查询语言(GQL)的知识图查询适配器层
  7. 与家庭自动化或其他第三方应用程序的集成挂钩
  8. NLG(自然语言生成)
  9. 对话聊天机器人设计
  10. 机器翻译层将英语翻译成支持的语言(可选)。
  11. 支持的每种语言的 Text2Speech 型号

参考故事“揭秘 wiki 本体 -part1”了解 wiki 本体,这有助于将 wiki 数据摄取到我们选择的知识图中。

个人助理的高级架构(基于本体的知识搜索)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Personal assistant architecture

上面是一个个人助理的高级架构,它不同于一个纯粹的开放领域的问题回答助理,因为它并不总是我们想问的问题。

我们还希望我们的助手“预订出租车”、“预订餐馆”、“告诉我我的约会”等。所以我们需要额外的一层意图分类。

让我们逐一探索每个模块,并深入了解可用的选项。前往揭开维基本体和知识图的神秘面纱——第一部分,这将有助于从维基数据中填充知识图。

NLU 层

从上面的架构可以看出,NLU 层仅仅是为了理解开放领域的问题而构建的。文本 2 内容层过滤并仅将开放域问题传递给 NLU 层。

让我们使用 Stanford CoreNLP 来探索一个基于模式的简单 NLU 层,这将是一个 grpc 微服务,并使用 google 协议缓冲区 来交换消息。

斯坦福大学 NLU 分校

让我们看看几个与电影相关的问题和相应的集。

电影《阿拉达纳》的导演是谁?

电影《肖莱》的导演是谁?

电影《肖莱》的制片人是谁?

这类问题可以归为一组,我们问的是在电影中扮演特定角色的人。

对应 conll 文集

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

conll corpus (9 columns for each sentence)

正如我们在上面看到的,在 conll 语料库中的第二列是 NER 标签和使用 regexner 分类为标题(自定义 NER)的导演、制片人等。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Modified architecture add a layer before NLU

如果你把领域压缩得足够小,以理解每个类别中的问题,比如电影问题,regexner 就很棒,如图所示。

人们可以把 regexner 放到斯坦福 corenlp 管道中,如下所示。

Properties props = new Properties();
props.put("annotators", "tokenize, ssplit, pos, lemma, ner, regexner");
props.put("regexner.mapping", "org/foo/resources/jg-regexner.txt");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);

TokenRegex 文件

TokensRegex 是一个通用框架,包含在 Stanford CoreNLP 中,用于定义文本(标记序列)的模式,并将其映射到表示为 Java 对象的语义对象。(摘自 Stanford Corenlp token regex)

//author samrat.saha
//tokensregex.extractor.rulesner = { type: "CLASS", value: "edu.stanford.nlp.ling.CoreAnnotations$NamedEntityTagAnnotation" }tag = { type: "CLASS", value: "edu.stanford.nlp.ling.CoreAnnotations$PartOfSpeechAnnotation" }normalized = { type: "CLASS", value: "edu.stanford.nlp.ling.CoreAnnotations$NormalizedNamedEntityTagAnnotation" }ENV.defaultStringPatternFlags = 2{ruleType: "tokens",pattern: ( ( [ { tag:WP } ] ) ( [ { ner:O} ]* ) ([ { ner:TITLE} ]+ ) ( [ { ner:O} ]* ) ( [ { ner:MOVIE} ]+  ) ( [ { tag:NNP } ]+ )   ),result: Format( "MOVIE_ROLE %s %s %s %s", "##", $$6.text, "##", $$3.text),stage: 1}

Java 代码

**public** **static** **void** main(String[] args) **throws** Exception {String rules = "..../src/main/resources/tokenregexrule.txt";String exampleQuestions = IOUtils.*stringFromFile*("src/main/resources/question.txt");//pipeline have regexner as well as statistical nerProperties properties = PropertiesUtils.*asProperties*("annotators", "tokenize,ssplit,pos,lemma,ner,regexner");properties.setProperty("ssplit.eolonly", "true");properties.setProperty("regexner.mapping", "src/main/resources/movieregex.txt");//tokenregex rule file from above
properties.setProperty("tokensregex.extractor.rules", rules);StanfordCoreNLP pipeline = **new** StanfordCoreNLP(properties);Annotation annotation = **new** Annotation(exampleQuestions);*env* = TokenSequencePattern.*getNewEnv*();*extractor* = CoreMapExpressionExtractor.*createExtractorFromFiles*(*env*, properties.getProperty("tokensregex.extractor.rules"));pipeline.annotate(annotation);*extractConcepts*(annotation);}

使用 tokenregex 提取概念代码

代码会提取主语宾语和我们说的概念,这个叫做关系提取(主语、谓语、宾语)。

在知识图中,主体和客体将是实体,谓词将是关系。

**public** **static** **void** extractConcepts(Annotation annotation) {**boolean** flag = Boolean.***FALSE***;List<CoreMap> sentences = annotation.get(CoreAnnotations.SentencesAnnotation.**class**);**int** id = 0;**for** (CoreMap sentence : sentences) {System.***out***.println(sentence.toString());List<CoreLabel> tokens = sentence.get(CoreAnnotations.TokensAnnotation.**class**);id++;List<MatchedExpression> matchedExpressions = *extractor*.extractExpressions(sentence);**for** (MatchedExpression matched:matchedExpressions) {// Print out matched text and valueSystem.***out***.println("--------------------");//System.out.println(matched.getText());//System.out.println(matched.getValue().toString());String subj = "";String obj = "";CoreMap cm = matched.getAnnotation();String matchedText = matched.getValue().toString();String matchedTextMod = matchedText.replace("(", " ").replace(")", "").replace("STRING", "");StringTokenizer st = **new** StringTokenizer(matchedTextMod);String predicate = st.nextToken("##").trim();subj = st.nextToken("##").trim();obj = st.nextToken("##").trim().replace("-LRB-", "(").replace("-RRB-", ")");**if**(obj.substring(0, obj.length()/2).replaceAll("\\s|\\W", "").equalsIgnoreCase(obj.substring(obj.length()/2, obj.length()).replaceAll("\\s|\\W", ""))){obj = obj.substring(0, obj.length()/2);}System.***out***.println(subj + "->" + predicate + "->" + obj);System.***out***.println("--------------------\n");}}annotation = **null**;**return**;}

输出:

Who is the director of the film Aradhana ?--------------------Aradhana->MOVIE_ROLE->director--------------------Who is the director of the film Sholay ?--------------------Sholay->MOVIE_ROLE->director--------------------Who is the producer of the film Sholay?--------------------Sholay->MOVIE_ROLE->producer--------------------

可以注意到,这对于下面这样的复杂问题是绝对不够的。

多主题问题

汤姆·克鲁斯和布拉德·皮特主演的电影有哪些?

统计 CRF 关系提取器

Stanford coreNLP 还支持关系提取,可用于从简单问题中提取主语、宾语和谓语。

人们需要为他们感兴趣的关系注释 conll 语料库文件,例如参见下面的 conll 语料库。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

movie role relation annotation

上面的注释说,在令牌 3 和 7 之间存在一个我们正在询问的 MOVIE_ROLE 关系。

也看到如何分类阿拉达纳被归类为我们需要为电影数据集定制 NER 模型的人。训练一个 NER 模型超出了这些博客的范围。

使用带注释的数据集训练一个定制模型,如这里提到的。

一旦模型训练完毕,加载模型并使用下面的代码从简单的问题中提取主客体谓词。

**public** CustomRelationExtractAnnotator(Properties props){ *verbose* = Boolean.*parseBoolean*(props.getProperty("sup.relation.verbose", "false"));//Load the model
String relationModel = props.getProperty("sup.relation.model", "model/simple-question-relation-model.ser");**try** {Extractor entityExtractor = **new** SimpleQuestionEntityExtractor();BasicRelationExtractor relationExtractor = BasicRelationExtractor.*load*(relationModel);System.***out***.println("Loading relation model " + relationModel + " and the features are " + relationExtractor.featureFactory);mr = MachineReading.*makeMachineReadingForAnnotation*(**new** SimpleQuestionConllReader(), entityExtractor, relationExtractor, **null**, **null**,**null**, **true**, *verbose*);} **catch**(Exception e){e.printStackTrace();**throw** **new** RuntimeException(e);}}@Override**public** **void** annotate(Annotation annotation) {// extract entities and relationsAnnotation output = mr.annotate(annotation);// transfer entities/relations back to the original annotationList<CoreMap> outputSentences = output.get(SentencesAnnotation.**class**);List<CoreMap> origSentences = annotation.get(SentencesAnnotation.**class**);**for**(**int** i = 0; i < outputSentences.size(); i ++){CoreMap outSent = outputSentences.get(i);CoreMap origSent = origSentences.get(i);// set entitiesList<EntityMention> entities = outSent.get(MachineReadingAnnotations.EntityMentionsAnnotation.**class**);origSent.set(MachineReadingAnnotations.EntityMentionsAnnotation.**class**, entities);**if**(*verbose* && entities != **null**){System.***err***.println("Extracted the following entities:");**for**(EntityMention e: entities){System.***err***.println("\t" + e);}}// set relationsList<RelationMention> relations = outSent.get(MachineReadingAnnotations.RelationMentionsAnnotation.**class**);origSent.set(MachineReadingAnnotations.RelationMentionsAnnotation.**class**, relations);**if**(*verbose* && relations != **null**){System.***err***.println("Extracted the following relations:");**for**(RelationMention r: relations){**if**(! r.getType().equals(RelationMention.***UNRELATED***)){System.***err***.println(r);}}}}}

以上是对 CoreNLP 如何用于从简单问题中提取关系的快速解释。

你会发现很多关于斯坦福 CoreNLP 的在线文章,如果有疑问,请在下面评论澄清。

在这篇文章中,我们使用 tokenregexStanford relation extraction介绍了一些架构选项和基本 NLU 程序块,它们可以提取单个主语、单个谓语和单个宾语。

注意,单个问题可以用多个关系来注释,也可以从单个问题中提取多个关系,这可能导致精度问题。基于你的训练数据有多好,统计的 CRF 方法一般会有较高的召回较低的精度

将在即将到来的博客中讨论高级 NLU 和问题理解。

感谢阅读…

Git Link :https://github.com/iitrsamrat/nlp-stanford

References:

[1] https://www.wikidata.org/wiki/Wikidata:Main_Page

[2] https://wiki.dbpedia.org/

[3] https://en.wikipedia.org/wiki/Knowledge_Graph

[4] https://en.wikipedia.org/wiki/Question_answering

[5] https://www.conll.org/2019

[6] https://stanfordnlp.github.io/CoreNLP/

构建基于照片的个性化推荐应用程序

原文:https://towardsdatascience.com/building-a-photo-based-personalized-recommendations-application-27029418d65e?source=collection_archive---------14-----------------------

使用 Neo4j、图形算法、Yelp 公共数据集和 React

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Applied Graph Algorithms online training course shows how to enhance the functionality of a web application using Neo4j Graph Algorithms.

在设计应用图算法在线培训课程时,我们认为展示如何构建利用图算法的应用程序以使用真实世界的数据集添加智能功能非常重要。这篇博文详细介绍了课程中一个练习背后的技术:构建一个基于照片的个性化推荐应用程序。

这只是练习之一,如果你对此感兴趣,请查看免费的应用图算法在线课程!你可以在这个概述视频中了解更多关于培训的信息。

图形算法

图形算法支持图形分析,分为 5 大类:寻路和搜索、中心性、社区检测、链接预测和相似性。你可以在这里了解更多 Neo4j 图算法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The five basic categories of graph algorithms available in Neo4j.

Yelp 开放数据集

Yelp 开放数据集是 Yelp 公开提供的所有 Yelp 业务、评论和用户数据的子集,可以建模为图表并导入 Neo4j。对于熟悉构建应用程序和使用图表数据来说,这是一个很好的数据集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Yelp Open Dataset is a subset of Yelp data released publicly. This data can be modelled as a graph in Neo4j.

商业评论应用程序

我们从一个简单的 business reviews web 应用程序开始,目标是使用 Neo4j 图算法增强其功能。该应用程序的基本功能允许用户搜索企业和查看企业的评论。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

A business reviews application using the Yelp Open Dataset. It allows the user to search for businesses and view reviews. We explore how to enhance the functionality of this application using Neo4j Graph Algorithms.

web 应用程序是一个 React 应用程序,它使用 Neo4j JavaScript 驱动程序对 Neo4j 数据库实例执行 Cypher 查询并处理结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Basic architecture of the business reviews application: a React frontend uses the Neo4j JavaScript driver to send Cypher queries to a Neo4j database instance.

个性化推荐

个性化推荐在许多行业和应用类型中都非常有用。通过推荐要购买的商品、要阅读的文章、要看的电影或要听的歌曲,用户很乐意拥有与他们的兴趣相关的内容,开发者也很乐意与他们的应用程序有更多的互动。

基于内容与协同过滤

基于内容的推荐和协同过滤是实现个性化推荐系统的两种基本方法。

基于内容的方法使用被推荐项目的属性(电影类型、菜肴类型等),并将这些属性与用户的偏好进行比较,从而做出推荐。计算项目之间相似性的相似性度量对于这种方法是有用的。

另一方面,协同过滤基于用户与项目的交互,例如购买或评级来产生推荐。

在本例中,我们使用基于内容的方法,使用照片标签和 Neo4j 图形算法进行个性化推荐。

基于照片的推荐

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Yelp 开放数据集包括 20 万张用户上传照片的档案,与企业相关。这些照片将作为我们个性化推荐方法的基础。

我们的应用程序将向用户随机显示照片,允许用户选择他们喜欢的照片。然后,我们会找到与用户喜欢的照片相似的照片,并推荐与这些照片相关的商家。

具体来说,这些步骤是:

  1. 使用 Jaccard 相似度识别相似照片
  2. 使用标签传播聚类相似照片
  3. 通过遍历图表,推荐同一个社区中与照片相关的企业。

寻找相似的照片

我们将如何确定照片是否相似?Yelp 数据包括关于每张照片的最少元数据,但我们通过谷歌视觉 API 运行每张照片,该 API 使用机器学习来确定照片的标签。我们的脚本获取每张照片的标签,并在图中创建Label节点:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Adding photos labels to the graph.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Jaccard similarity is defined as the size of the intersection of two sets divided by the size of the union of two sets.

我们可以使用 Jaccard 相似度来计算给定的一对照片有多相似。通常用于查找相似项目的推荐以及链接预测的一部分 Jaccard 相似性度量集合之间的相似性(在我们的例子中是附加到照片的标签集合)。

Jaccard 相似性是一种集合比较算法,通过将两个集合的交集大小除以它们的并集大小来计算。Jaccard 相似性算法在 Neo4j 图形算法库中可用。下面是我们如何在一个 Cypher 查询中使用它,计算所有照片对的相似度。

MATCH (p:Photo)-[:HAS_LABEL]->(label)
WITH {item: id(p), categories: COLLECT(id(label))} AS userData
WITH COLLECT(userData) AS data
CALL algo.similarity.jaccard(data, 
  {topK: 3, similarityCutoff: 0.9, write: true})

这个查询将在图中创建SIMILAR_TO关系,将相似性值存储在一个score属性中:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们看两张标签重叠的照片,看看 Jaccard 相似度得分是如何计算的。在下图中,两张照片有 9 个重叠标签,其中一个标签仅与一张照片相关联:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,Jaccard 相似分数为 9/10 或 0.9

使用标签传播聚类相似照片

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

标签传播是一种社区检测算法,它通过为每个节点分配分区值来将图形分割成分区或社区。它的工作方式是用社区分配播种节点,然后迭代,在每次迭代中将社区分配给相邻节点,直到整个图被划分。

为了在 Neo4j 中运行标签传播,我们传递定义了要在其上运行算法的子图的节点标签和关系类型。在这种情况下,我们希望对由SIMILAR关系定义的子图使用 run 算法:

CALL algo.labelPropagation("Photo", "SIMILAR", "BOTH")

该查询将添加一个partition属性来按社区对照片进行分组。然后我们可以通过社区查询查看Photo节点的分布:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以通过选择一个社区并查看分配给该社区的照片来验证照片的社区分配。这里我们随机选取一个社区,对比照片:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看这些照片,他们似乎都是比萨饼,所以这似乎是一个“比萨饼”集群。看起来我们的方法非常有效!

通过遍历图表,推荐同一社区中与照片相关的业务

现在,我们已经使用标签传播算法将照片分组到社区中,我们准备好生成我们的个性化商业建议。给定用户选择的一些照片,我们从这些照片遍历到他们指定的社区,然后到同一社区中的其他照片,然后遍历到与照片相关的企业,这成为我们的推荐。我们使用一个简单的 Cypher 查询来定义这种遍历:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意我们是如何在这个操作中结合全局和局部图操作的,利用了图算法以及图数据库的近实时性能,即无索引邻接。

最后的结果

这是我们的新功能:

  1. 向用户呈现随机照片的照片库
  2. 用户选择 5 张他们觉得有吸引力的照片
  3. 我们的应用程序遍历图表,找到所选照片的聚类,然后遍历相同聚类中的其他照片,最后遍历与这些照片相关的企业,这些照片成为推荐。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

报名参加免费的应用图形算法课程

Neo4j 应用图形算法免费在线培训教授如何利用图形算法来增强 web 应用程序的功能,包括这个基于照片的推荐示例。立即注册了解更多有关 Neo4j 图算法的信息,如个性化页面排名、相似性度量、社区检测以及添加个性化、推荐和增强搜索结果等功能。

通过注册应用图算法课程,你可以找到这部分提到的所有数据和代码!

使用机器学习构建私人的本地照片搜索应用程序

原文:https://towardsdatascience.com/building-a-private-local-photo-search-app-using-machine-learning-8aeeef8d245c?source=collection_archive---------14-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就是了。这是我做过的最棒的事。我通常不喜欢吹牛,但我为这一点感到非常自豪,我觉得我需要分享它。他们说这是不可能的(实际上没有人这么说),他们说这是不可能的(很多人说这是可能的),但我做到了,而且效果很好!

背景

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

My wife’s library has the other 40,000

自从有了孩子,我就有一个问题。嗯……很多问题,但我会把重点放在与技术相关的问题上。问题是我有 8 万张照片。这并不夸张。在我妻子和我之间,在我们的第一个人类出生后,我们在短短几年内从大约 3000 张照片增加到 80000 张。

很快,我意识到拍照几乎没有任何意义,因为以后再也不可能找到它们了。当时,我所有的照片都在 Dropbox 里。Dropbox 没有任何照片管理功能(在撰写本文时仍然没有),而这正是我迫切需要的。从我在机器学习领域的工作中,我知道你可以从一张照片中收集到很多信息,比如照片中的人是谁,照片是在哪里拍的,照片是什么样的场景;例如,未来的超级恶棍恐吓父母

所以我做了唯一符合逻辑的事。将所有 80,000 张照片移植到 Apple Photos,以便可以运行一些 ML,我可以再次找到我的照片。它非常成功——结束了。再见!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

错误的

假的。这不是结局。可以说,现在我被困在苹果的围墙花园里。我真的很喜欢这个花园,那里有很多漂亮的花,比如的胭脂树的胭脂树。但并不是每个人都这样做,我的好朋友 Jaron Phillips 的一篇文章提醒我,总有另一种方法(除了谷歌有严重的隐私问题)。

在本地运行

让我们讨论一下为什么写这篇文章,以及我是如何以一种不可思议的方式解决了这个惊人的问题(以及你如何也能做到这一点,尽管是以一种远不那么有趣的方式)。

你的照片(可能)非常隐私,包含许多秘密,所以你可能想把它们存在你的本地计算机上*。但是它们是不可搜索的!让我们改变这一切!为了使这个过程有效,它们需要在一个文件夹中,所以只需将它们全部导出到一个文件夹中(如果还没有的话)。如果你对电脑很在行,你可以修改我的脚本,让它也适用于许多子文件夹中的照片。但是最关键的部分是机器学习。*

*我们将使用 Tagbox ,它已经预先训练了成千上万的图像标签,例如,用照片中的内容来标记我们所有的照片;*海洋、日落、沙滩、雾、狗、生日蛋糕、厄运、等。关于标签盒的伟大之处在于它在你的电脑上本地运行。没有云公司窃取你的秘密。

我们要把这些标签存储在该死的文件里。想象一下。!我们为什么要这么做?所以你可以搜索。嘭!冰!其他声音!是不是很神奇?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Where?

这些标签将进入 Spotlight 将索引的“评论”栏,以便您稍后可以搜索这些文件。

该过程

这是我如何在大约一个小时内让它工作起来的。我做的第一件事是把一堆照片移到我电脑上的一个文件夹里,来模拟我以前的照片目录。

然后,我下载、安装并运行了 Tagbox ,这花了几分钟,但那是因为它是我的公司的产品,我很了解它。如果你是一名开发人员或技术专家,这也不会花你太多时间。

接下来,我编写了一个 Go 脚本,该脚本遍历目录,将每个图像发送到标记框,取回标记,并将它们放入文件的注释字段。

然后我运行脚本,瞧——EXTEME EXCELLENCE。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,我可以通过 Mac 上的内容来搜索任何照片。没有必要把我的个人照片发到云上,或者发到谷歌上,或者发给……其他怪人。它在当地跑,跑得很快,跑得非常好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这仅仅是一个开始——既然你是一个出色的开发人员,你可以把它带到下一个层次,构建一个具有其他特性的实际应用程序,或者探索在其他地方注入标签,比如 EXIF 数据或一个单独的 xml sidecar 文件。我在这里给你出主意,你在这里和他们一起做令人惊奇的事情,因为,让我们诚实地说,我是一个糟糕的开发者。

使用 Apache Airflow 构建生产级 ETL 管道平台

原文:https://towardsdatascience.com/building-a-production-level-etl-pipeline-platform-using-apache-airflow-a4cf34203fbd?source=collection_archive---------3-----------------------

使用 Apache Airflow 管理 CernerWorks 中的数据工作流

CernerWorks 企业系统管理团队负责从 Cerner 客户的系统中挖掘系统数据,为 Cerner 内部的各个团队提供所收集数据的可见性,并使用所收集的数据构建监控解决方案。我们的核心任务是帮助提高 Cerner 客户系统的可靠性和安全性。大约三年前,我们的团队开发了一个有效的系统数据收集遥测框架。与此同时,我们看到用例呈指数级增长,我们必须以各种方式转换收集的系统数据,以支持我们的可见性和监控工作。因此,我们迫切需要在我们的数据架构中引入一个专用的 ETL 管道平台。

经过一些研究,我们发现 Apache Airflow 开源框架非常适合我们的需求,因为它是为实现、调度和监控数据工作流而设计的。在 Airflow 上下文中,数据工作流由一个 DAG(有向无环图)表示,它是一组具有无环依赖关系的任务,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

DAG 中的每个任务都使用一个操作符来实现。Airflow 的开源代码库提供了一组通用的操作符,然而,该框架对我们的主要吸引力在于,我们可以实现专门适合 Cerner 数据工作流的自定义操作符。除了能够编写自定义操作符,Airflow 作为一个框架被设计成高度可定制的。

我们的团队在两个方面的需求驱动下进行定制工作:( I)最大限度地减少在我们的平台上安排 Cerner 数据工作流的开发开销,以及(ii)采用健壮可靠的生产架构。我们将我们定制的气流实现命名为 Jetstream。

Jetstream 高效支持 Cerner 数据工作流的设计模式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Jetstream 中实现给定数据工作流的逻辑的核心是它的操作符和模块。这些模块的逻辑可能适用于各种数据工作流。例如,连接到各种数据库、连接到各种外部和内部 API、调度逻辑、批处理逻辑等等。操作符是一类任务的一般化表示。它们从任务 YAML 加载配置数据,并利用模块来实现给定的任务。例如,DatabaseTransferOperator 允许使用者将数据从一个数据库转换和移动到另一个数据库。任务 YAML 捕获与给定任务相关联的专门的和基本的业务逻辑。

我们团队的系统数据可见性和监控工作涉及到与 Cerner 各种团队的合作。我们转换系统数据的许多需求来自这些团队。通过抽象出所涉及的逻辑,我们能够让这些团队以最小的开发开销成为我们的 ETL 平台的消费者。这就是为什么该平台在过去三年中持续显著增长,目前有超过 3000 个日常任务。下面包括几个示例任务 YAMLs,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

特别是因为我们在 Airflow 的开源之旅中很早就引入了 Jetstream,所以开发一个健壮可靠的产品架构一直是一个迭代的过程。让我们讨论一下 Jetstream 架构在过去三年中的一些重大变化的设计动机。

Jetstream 架构:提高调度元数据的可靠性并使用池

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上图显示了我们对最初的概念验证设置所做的两个重要的架构更改。

(1)使用我们的数据仓库存储重要的任务调度元数据。

拥有可靠的定制调度元数据是我们团队的一个重要需求。我们特别希望消除对 Airflow 存储调度元数据的本地选项的依赖。作为气流的早期采用者,我们希望避免重大的气流设计变化,这些变化可能会影响我们利用的任何本机调度元数据逻辑。此外,由于 Cerner 的客户遍布世界各地,我们的许多任务严重依赖建立在我们的调度元数据之上的逻辑来正确处理。例如,Airflow 在其 1.9 版本中采用了 UTC 时间标准( AIRFLOW-289 ),如果我们非常依赖 AIRFLOW 的本地调度选项,这将打破我们的逻辑。

另一个重要的考虑是日程安排元数据的绝对价值。该元数据本质上捕获了与每个任务相关联的数据被处理的时间间隔。假设我们的调度元数据被破坏,这可能导致数据间隙(丢失时间间隔)或重复数据(重新处理的时间间隔)。在我们的 Jetstream 节点上存储如此重要的数据会导致高成本的单点故障。数据仓库是我们团队数据架构的核心组成部分(参见 Cerner 推进大数据分析能力。丹·沃克。企业系统管理总监。因此,我们的数据仓库集群受到严密监控,因此具有高可靠性。通过将这些元数据存储在我们的数据仓库中,即使我们的 Jetstream 节点出现故障,我们也可以重新启动它们并开始我们的工作,而不用担心损坏、不一致或缺少调度元数据。因此,添加这种依赖性,使我们能够显著提高平台的健壮性和可靠性。将这些数据存储在数据仓库中还可以让我们构建基于 SLA 的警报逻辑,并开发性能报告。

(2)利用气流池。

我们最初没有利用由 Airflow 框架提供的执行池支持。当 DAG(有向无环图)任务准备好被调度时,它将被添加到 Airflow 的默认执行池,由下一个可用的执行器执行。问题是,当我们添加一个有一千多个任务的 DAG 时,这个 DAG 的任务会挤掉其他 DAG 的任务。然后,我们开始使用命名执行池,这将限制每个 DAG 可用的工作插槽的数量。这让我们能够对执行并行性进行粒度控制,并防止 DAG 被无意中挤出。

Jetstream 架构:建立一个 Jetstream 集群并增加对近实时调度的支持

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上图代表了 Jetstream 目前的制作架构。

(3)建立 Jetstream 集群。

我们使用 Airflow 的支持来利用芹菜来建立一个生产集群。这让我们能够利用并发处理的优势,从而提高平台的健壮性和效率。每个 DAG 都创建有一个关联的芹菜队列。每个工作节点都被分配了一组芹菜队列。这意味着来自每个 DAG 的任务可以在“监听”相应 DAG 队列的工作节点上执行。我们确保每个队列都有两个或更多分配的工作节点,以提高平台的可靠性,因为我们不想让工作节点成为潜在的单点故障。但是,请注意,主节点仍然是单点故障。请参见“使阿帕奇气流高度可用”以获得此问题的潜在解决方案。解决这个问题还不是设计的重点。

(4)支持接近实时的调度。

我们最近有一些重要的用例在我们的平台上进行接近实时的调度。我们意识到,为了支持近乎实时的数据工作流,我们需要将调度元数据存储在内存中。因此,对于我们的近实时 Dag,我们使用 Redis 而不是我们的数据仓库来存储调度元数据。

Jetstream 的主要开发者:

卡普勒,冈扬。 阿格纽,亚当。菲利普利,卡罗。 Pydi,Aakash

更多阅读请见阿帕奇气流资源权威汇编

构建伪随机数发生器

原文:https://towardsdatascience.com/building-a-pseudorandom-number-generator-9bc37d3a87d5?source=collection_archive---------8-----------------------

在不到 50 行的 Python 代码中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我的文章“ 如何从一个不平衡的 RNG 得到一个无偏的 T2*”*中,我展示了如何从任何来源提取随机性。现在的目标是从头开始构建一个伪随机数生成器!

“为什么我需要一个随机数?”

随机数的重要性不在于数字本身(如果单独来看,它们是普通的数字),而在于它们产生的方式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现代技术是基于这些数字:通信协议,加密,游戏大量使用它们,它们的整体安全性和不可预测性依赖于它们。

假设你想创建一个游戏,系统抛出一枚硬币,玩家在结果上下赌注(用真钱)。如果玩家是正确的,那么他有奖励(金钱),否则他失去了放置的金钱。如果你的硬币真的是公平的(或不可预测的),那么你就没什么好担心的。

但是,如果玩家能够以很高的准确率预测下一次投掷的结果,会发生什么呢?或者他们是否能根据最后三投预测下一个结果?答案显而易见。

自然发电机

大自然提供了一些随机性的来源,但是使用起来非常昂贵。

在宏观环境中,来源很少,如气候变化或宇宙微波背景。这种发电机非常昂贵:我想买一个系统,把你和测量 CMB 变化的卫星连接起来,并在几毫秒内发回结果。成本在 4 ~ 5 亿美元左右。

好吧,我举了一个极端的例子,但是总的来说,考虑到成本和性能,这一点都不方便。

在微观上,整个环境是由混沌驱动的:据我们所知,这是一个概率世界;但是,管理这种资源又是非常昂贵的。完美地平衡一个粒子的量子态这不是一个简单的任务。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

伪随机发生器

由于这些原因,我们总是发现在我们的机器(电脑、智能手机、电视、等……)中内置发电机很方便。此外,有一个更简洁的方法来计算随机字符串总是好的:如果您的系统从以μK 为单位的本地温度中提取一个序列,任何人都可以通过在您的系统附近放置一个传感器来复制相同的序列;甚至任何人都可以操纵检测并控制你的序列。

计算机使用 CPU 来执行指令,而 CPU 是基于确定性机制的。我们如何从一个没有任何随机性来源的环境中创造随机性?

在我回答这个问题之前,让我们定义一个伪随机数发生器(PRNG)。从这里开始,我将处理使用位(0 和 1)的 PRNGs,但对于其他情况,验证其属性是非常容易的,因为可以用一个数来编码一个二进制序列。

背后的理论

给定一个初始种子,PRNG 产生一个比特序列,该序列与由真实随机源产生的序列无法区分。

不可区分是指在一台概率图灵机上没有可以在多项式时间内执行的算法可以决定给定的序列是随机的还是计算出来的。也就是说,没有随机算法能说 PRNG 产生的字符串是确定性计算的还是随机提取的。

因此,这是 PRNG 的一个定义:它是一个在多项式时间内在确定性图灵机上可执行的算法,计算一个函数 G 使得

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

l 作为单调递增函数。这意味着输出总是比输入(种子)长。

同样,对于属于 BPP 类DD中的每个算法,对于每个多项式p和对于每个整数k足够大:****

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个庞大的公式可以这样理解:

概率多项式时间问题(BPP) 类中的算法能够区分真实随机源和 PRNG 之间的序列的概率随着种子长度的增加比任何多项式更快地趋向于零

因此,PRNG 是一种算法,它将种子作为输入,并返回一个更长的字符串,因此没有人能够轻易说出它是否被计算过。算法计算出的函数称为 G

G 的定义说,如果初始种子是一个 k 比特的序列,那么G***l(k)*比特的更长序列。我们是否应该为每一个可能的函数 l 构建一个不同的函数 G

从简单的 PRNG 号开始 H

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有可能以 G 的形式构建任何 PRNG,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中,xᵢ λᵢ是位串,是位串 xᵢ和单个位λᵢ.串联的结果 H 函数从初始种子生成一个比原来长一位的序列。通过调用 H 函数 l(k) 次并从每次迭代中取出最后一位,我们已经生成了一系列的*【l(k)*位。显然这个功能就是 G

我们现在能够构建一个函数,该函数采用 k 位并返回 l(k) 位,使得没有算法能够决定 l(k) 位是否是从真实的随机源生成的。通过这个技巧,我们从构建输出*【k】*位的函数(使用 l( ) 通用多项式函数)的问题转移到只返回 k+1 位的函数。剩下的就是定义 H 函数。

单向置换的 h 函数

选择 H 作为单向置换是个好主意。

H 如果很难求逆就是单向排列:给定y,很难计算出x 使得 H(x) = y**

众所周知且广泛使用的单向置换是模幂运算。给定一个质数 p 和一个整数 x 使得
0<x<p-1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 g 是循环群的生成元

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个循环群的生成元的个数是

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

p 为奇素数时,其中 ϕ欧拉全等函数。现在求这样的 x 需要计算离散对数,这是一个著名的未解计算难题。也就是说,没有计算大整数的离散对数的有效方法是已知的。公钥密码学中的许多算法基于这样一种假设,即没有有效的方法来计算它的安全性。

最后一点

我们发现一个函数从 k 位开始,返回 k 位,并且很难反转。我们需要额外的一位,根据 H 的定义。该位是功能 f核心位。如果已知x,则 f 的核心位有点容易计算,但如果只给定 f(x) 则很难计算。

为了找到它,让我们定义两个 k 位串 xy 之间的新操作:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果*【x】是一个单向排列(就像上面定义的那样) g(x,y) = f(x) ‖ y ,那么 x,yg 的硬核位,其中这是由 Goldreich-Levin 定理陈述的。*****

最后我们找到了 PRNG H 这是它的正式定义:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在哪里

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传****外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

PRNG

现在我们可以根据我们给出的 HG 的定义来构建一个好的 PRNG。对于那些错过的人, G 是一个函数(实际上是 PRNG 本身),给定输入中的一个 k 位串,输出一个 l(k) 位串,并且没有随机算法可以判断产生的字符串是否是由真正的随机源生成的; H 是一个帮助找到那些伪随机位的函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意,我假设生成器 g = 2 。你应该找到一个依赖于 p 的生成器,因为这只是一个例子(即使统计上 2 是一个频繁的生成器)。如果你想为你的 p 找到一个生成器,你可以使用这个在线工具来计算给定素数的根基元模。

现在主要功能 G ,【PRNG:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 l( ) 可以是任意多项式函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于 H 的输入是将被分成两半的位串,所以初始种子 x0 的长度必须是偶数。

请记住,根据上一节的定义,更长的种子会产生更难区分的序列。这意味着更长的种子将产生更不可预测的比特序列。

Python 实现

为了轻松管理位操作,算法的实现在字符串上工作,因此它可以更好地从上面显示的伪代码转换为 Python 代码。但是它可以通过直接处理位来增强,代价是可读性。

我用了模幂运算作为单向排列和
l(k)= k-2k+1

Link to the gist

用法:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

唯一采用的参数是初始种子,它必须是一个二进制字符串,长度不能超过变量SEED_SIZE的值。

如果您想要生成一个数字序列,您可以使用输出的任何部分作为下一次迭代的种子。更大的种子会产生更好的输出,但在使用它之前,请记住重新定义一个合适的 l( ) ,如果您想保持模幂运算为单向变换,请选择一个新的GENERATOR,它是MODULUS的原始根。

构建用于比较数据的 Python UI

原文:https://towardsdatascience.com/building-a-python-ui-for-comparing-data-13c10693d9e4?source=collection_archive---------4-----------------------

如何快速让您的非技术团队能够比较数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by David Werbrouck on Unsplash

在分析或 IT 功能上花足够的时间,很快就会发现处理数据是必须的。收集数据,处理数据,当然还有比较数据。如今,所有这些数据的问题通常在于其数量之大。

除非人们的技术水平足够高,知道如何使用 Python、R 或类似的语言,否则当他们需要处理更大的数据集时,他们会很吃力。不幸(还是幸运?),Excel 就不剪了。这给人们制造了许多障碍。

当谈到数据操作,包括查看数据或比较数据时,我听到的最常见的抱怨之一是 Excel 根本不支持它。太大了,装不下。为此,我写了一些文章解释如何解决这个问题——但是我的大部分文章在本质上都是技术性的。它需要你写代码和使用命令行。

[## 用 Python 从 Excel 到数据库

了解如何使用 Python 进行快速数据分析

medium.com](https://medium.com/financeexplained/from-excel-to-databases-with-python-c6f70bdc509b)

除非你是一个技术人员,否则你不一定会觉得这很容易。同样,如果你想和一个非技术人员分享这个功能。

然而,在我上一篇文章中,我们介绍了如何快速地用 Python 开发 ui,然后与我们更广泛的团队或社区分享它们。反响非常好;你们中的许多人觉得它非常有趣。

[## 了解如何用 Python 快速创建 ui

最后,你可以在 10 分钟内找到一个图书馆

towardsdatascience.com](/learn-how-to-quickly-create-uis-in-python-a97ae1394d5)

在这篇博客中,我们将介绍 PySimpleGUI 的大量可用特性,同时我们还将构建一些东西,让我们的非技术朋友能够快速比较数据。

定义用户界面

我们需要做的第一件事是定义一个简单的 UI,允许用户选择两个文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Choosing the two files to display

一旦定义了这两个文件,我们应该执行一些基本的验证,以确保这两个文件是可比较的。寻找相同的列标题可能是一种方法。然后,我们可以提供这些标题作为潜在的关键字,用户可以选择用于数据比较。

使用此示例文件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Screenshot of example file

将构建以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下是包括输出在内的全部内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图书馆

构建用户界面

为了构建 UI,我们将使用基于 Tkinter、wxPython 和 PyQT 的 PySimpleGUI 库。这段代码再简单不过了。快速偷窥:

import PySimpleGUI as sgsupportedextensions = ['csv','xlsx', 'xlsm' ,'json']layoutprefile = [
    [sg.Text('Select two files to proceed')],
    [sg.Text('File 1'), sg.InputText(), sg.FileBrowse()],
    [sg.Text('File 2'), sg.InputText(), sg.FileBrowse()],
    # *list1,
    [sg.Output(size=(61, 5))],
    [sg.Submit('Proceed'), sg.Cancel('Exit')]
]window = sg.Window('File Compare', layoutprefile)while True:    # The Event Loop
    event, values = window.read()
    if event in (None, 'Exit', 'Cancel'):
        secondwindow = 0
        break
    elif event == 'Proceed':
  print('yay')

阅读文件

我们将使用著名的熊猫图书馆来阅读文件。这将允许我们快速支持 CSV、JSON 和 Excel 文件。一旦文件在数据框中,我们就可以进行必要的操作了。

import re
import pandas as pdfile1 = r"C:/temp/file.csv"
file2 = r"C:/temp/file1.csv" if re.findall('/.+?/.+\.(.+)',file1)[0] == 'csv':
 df1, df2 = pd.read_csv(file1), pd.read_csv(file2)
elif re.findall('/.+?/.+\.(.+)',file1)[0] == 'json':
 df1, df2 = pd.read_json(file1), pd.read_json(file2)
elif re.findall('/.+?/.+\.(.+)',file1)[0] in  ['xlsx', 'xlsm']:
 df1, df2 = pd.read_excel(file1), pd.read_excel(file2)

为了从文件路径中提取扩展名,我使用了 re 库,这是 Python 的正则表达式库。正则表达式给了我们一种模式匹配和提取信息的方法。

[## 正则表达式简介

使用 Python 逐步介绍正则表达式

medium.com](https://medium.com/better-programming/introduction-to-regex-8c18abdd4f70)

比较数据

为了进行比较,我们将使用 DataComPy 库,它为我们提供了一个很好的比较摘要。

[## 如何快速比较数据集

如何快速总结两个数据集之间的差异

towardsdatascience.com](/how-to-quickly-compare-data-sets-76a694f6868a)

代码还是非常简单:

compare = datacompy.Compare(
    df1,
    df2,
    join_columns=definedkey,
    abs_tol=0, #Optional, defaults to 0
    rel_tol=0, #Optional, defaults to 0
    df1_name='Original', #Optional, defaults to 'df1'
    df2_name='New' #Optional, defaults to 'df2'
)
print(compare.report())

共享用户界面

为了共享 UI,我们可以使用 PyInstaller。只需找到您的文件(本例中为 ComPyUI.py)并运行以下命令:

pyinstaller --onefile ComPyUI.py

代码

事不宜迟,只需在本地复制下面的代码,并运行它作为比较工具:

import PySimpleGUI as sg
import re, time
import datacompy
import pandas as pdsupportedextensions = ['csv','xlsx', 'xlsm' ,'json']layoutprefile = [
    [sg.Text('Select two files to proceed')],
    [sg.Text('File 1'), sg.InputText(), sg.FileBrowse()],
    [sg.Text('File 2'), sg.InputText(), sg.FileBrowse()],
    # *list1,
    [sg.Output(size=(61, 5))],
    [sg.Submit('Proceed'), sg.Cancel('Exit')]
]window = sg.Window('File Compare', layoutprefile)while True:    # The Event Loop
    event, values = window.read()
    # print(event, values)  # debug
    if event in (None, 'Exit', 'Cancel'):
        secondwindow = 0
        break
    elif event == 'Proceed':
        #do some checks if valid directories have been provided
        file1test = file2test = isitago = proceedwithfindcommonkeys = None
        file1, file2 = values[0], values[1]
        if file1 and file2:
            file1test = re.findall('.+:\/.+\.+.', file1)
            file2test = re.findall('.+:\/.+\.+.', file2)
            isitago = 1
            if not file1test and file1test is not None:
                print('Error: File 1 path not valid.')
                isitago = 0
            elif not file2test and file2test is not None:
                print('Error: File 2 path not valid.')
                isitago = 0
            #both files to have the same extension
            elif re.findall('/.+?/.+\.(.+)',file1) != re.findall('/.+?/.+\.(.+)',file2):
                print('Error: The two files have different file extensions. Please correct')
                isitago = 0
            #they need to be in a list of supported extensions
            elif re.findall('/.+?/.+\.(.+)',file1)[0] not in supportedextensions or re.findall('/.+?/.+\.(.+)',file2)[0] not in supportedextensions:
                print('Error: File format currently not supported. At the moment only csv, xlsx, xlsm and json files are supported.')
                isitago = 0
            elif file1 == file2:
                print('Error: The files need to be different')
                isitago = 0
            elif isitago == 1:
                print('Info: Filepaths correctly defined.')
                # check if files exist
                try:
                    print('Info: Attempting to access files.')
                    if re.findall('/.+?/.+\.(.+)',file1)[0] == 'csv':
                        df1, df2 = pd.read_csv(file1), pd.read_csv(file2)
                    elif re.findall('/.+?/.+\.(.+)',file1)[0] == 'json':
                        df1, df2 = pd.read_json(file1), pd.read_json(file2)
                    elif re.findall('/.+?/.+\.(.+)',file1)[0] in  ['xlsx', 'xlsm']:
                        df1, df2 = pd.read_excel(file1), pd.read_excel(file2)
                    else:
                        print('How did we get here?')
                    proceedwithfindcommonkeys = 1
                except IOError:
                    print("Error: File not accessible.")
                    proceedwithfindcommonkeys = 0
                except UnicodeDecodeError:
                    print("Error: File includes a unicode character that cannot be decoded with the default UTF decryption.")
                    proceedwithfindcommonkeys = 0
                except Exception as e:
                    print('Error: ', e)
                    proceedwithfindcommonkeys = 0
        else:
            print('Error: Please choose 2 files.')
        if proceedwithfindcommonkeys == 1:
            keyslist1 = [] #This will be the list of headers from first file
            keyslist2 = [] #This will be the list of headers from second file
            keyslist = [] #This will be the list of headers that are the intersection between the two files
            formlists = [] #This will be the list to be displayed on the UI
            for header in df1.columns:
                if header not in keyslist1:
                    keyslist1.append(header)
            for header in df2.columns:
                if header not in keyslist2:
                    keyslist2.append(header)
            for item in keyslist1:
                if item in keyslist2:
                    keyslist.append(item)
            if len(keyslist) == 0:
                print('Error: Files have no common headers.')
                secondwindow = 0
            else:
                window.close()
                secondwindow = 1
                break#################################################
# First screen completed, moving on to second one
if secondwindow != 1:
    exit()#To align the three columns on the UI, we need the max len
#Note: This could be made better by having the max len of each column
maxlen = 0
for header in keyslist:
    if len(str(header)) > maxlen:
        maxlen = len(str(header))if maxlen > 25:
    maxlen = 25
elif maxlen < 10:
    maxlen = 15#we need to split the keys to four columns
for index,item in enumerate(keyslist):
    if index == 0: i =0
    if len(keyslist) >= 4 and i == 0:
        formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None)),sg.Checkbox(keyslist[i+2], size=(maxlen,None)),sg.Checkbox(keyslist[i+3], size=(maxlen,None))])
        i += 4
    elif len(keyslist) > i:
        if len(keyslist) - i - 4>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None)),sg.Checkbox(keyslist[i+2], size=(maxlen,None)),sg.Checkbox(keyslist[i+3], size=(maxlen,None))])
            i += 4
        elif len(keyslist) - i - 3>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None)),sg.Checkbox(keyslist[i+2], size=(maxlen,None))])
            i += 3
        elif len(keyslist)- i - 2>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None)),sg.Checkbox(keyslist[i+1], size=(maxlen,None))])
            i += 2
        elif len(keyslist) - i - 1>= 0:
            formlists.append([sg.Checkbox(keyslist[i], size=(maxlen,None))])
            i += 1
        else:
            sg.Popup('Error: Uh-oh, something\'s gone wrong!')

#The second UI
layoutpostfile = [
    [sg.Text('File 1'), sg.InputText(file1,disabled = True, size = (75,2))],
    [sg.Text('File 2'), sg.InputText(file2,disabled = True, size = (75,2))],
    #[sg.Text('Select the data key for the comparison:')],
    [sg.Frame(layout=[
        *formlists],title = 'Select the Data Key for Comparison',relief=sg.RELIEF_RIDGE
    )],
    [sg.Output(size=(maxlen*6, 20))],
    [sg.Submit('Compare'), sg.Cancel('Exit')]
]window2 = sg.Window('File Compare', layoutpostfile)
datakeydefined = 0
definedkey = []while True:  # The Event Loop
    event, values = window2.read()
    # print(event, values)  # debug
    if event in (None, 'Exit', 'Cancel'):
        break
    elif event == 'Compare':
        definedkey.clear()
        file1test = file2test = isitago = None
        #print('Event', event, '\n', 'Values', values)
        for index, value in enumerate(values):
            if index not in [0,1]:
                if values[index] == True: 
                    datakeydefined = 1
                    definedkey.append(keyslist[index-2])
            #print(index, values[index], keyslist[index-2])
        if len(definedkey) > 0:
            compare = datacompy.Compare(
                    df1,
                    df2,
                    join_columns=definedkey,  #You can also specify a list of columns eg ['policyID','statecode']
                    abs_tol=0, #Optional, defaults to 0
                    rel_tol=0, #Optional, defaults to 0
                    df1_name='Original', #Optional, defaults to 'df1'
                    df2_name='New' #Optional, defaults to 'df2'
            )
            print('########################################################################################################')
            print(compare.report())
        else:
            print('Error: You need to select at least one attribute as a data key')

这种解决方案的局限性

这个解决方案有相当多的限制,但它是一个可以在未来非常容易和快速地增强的解决方案。最好拥有以下物品:

  1. 如果定义 CSV 定界符而不是现在假设一个’,'就好了
  2. 窗口的大小太不稳定,使用很长的标题会产生问题
  3. 使系统能够记住基于文件名的按键选择
  4. 完整的目录核对

我相信还有很多,但是请让我知道你的想法!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值