从垃圾箱到灯泡:将家庭垃圾转化为本地清洁能源
在这里查看我的项目:https://github . com/tcastanley/MSW-to-Energy-feed-Analysis!
对于我在 熨斗学校的沉浸式数据科学项目 *,*中的最后一个顶点项目,我决定测试我的新技能,并继续推进我对数据、浪费和能源之间关系的个人调查。最近,我一直在学习更多关于城市固体废物转化为能源的各种方法。迄今为止,我遇到的最有前途和最有效的技术是等离子弧气化。在我的研究中,我发现了解用作原料的城市固体废物的具体组成细节是设计等离子气化设备的许多关键步骤之一。我开始为我的顶点项目做的是,看看我是否能找到一些 MSW 收集数据集并进行原料分析,目的是计算每个样品的特定 废物类型成分 、 能量密度(kWh/kg ) 和 总能量(kWh) 。
等离子气化
在我们进入计算近似城市固体垃圾能量值所需的数据分析之前,让我们多谈一点关于范式转换技术,等离子气化。
*“在缺氧环境中将含碳物质转化为气体产品,以生产能源产品和副产品。”*城市固体废物到能源的转化过程”,Gary C. Young,2010 年
上面的技术定义是不言而喻的,尽管可以通过将这一过程视为能够获取地球上几乎任何材料(除了金属和玻璃)并将其转化为基本分子成分来进一步简化。这种产品叫做 【合成气】 ,主要由一氧化碳和**氢气【H2】**组成。合成气可以进一步加工成许多不同的有价值的化学产品,尽管这取决于设备的用途和设计。我有兴趣了解如何从城市垃圾发电。
气化设施的提案需要收入预测,就像大多数商业企业一样。这些预测取决于对用作气化过程原料的燃料中的能量的理解。这就是通过原料分析确定能量密度发挥作用的地方。我想建立一个模型,它可以接收特定的废物样本,根据一系列物品的重量进行测量,并预测其能量密度。
这一过程的另一个副产品是玻璃化炉渣或生物炭,它们是气化过程的结果。根据城市固体废物原料的具体成分,这种产品的收入存在不同的市场。
总而言之,使用这项技术作为解决全球日益增长的城市垃圾问题的一部分是显而易见的。洗涤技术安装在整个气化过程中,以确保空气排放符合当地健康和安全法规。当人们将此与室外厌氧消化的替代长期解决方案进行比较时,会向大气中释放无数吨有害甲烷,等离子体气化就变成了一个商业问题,以及如何使这一解决方案可持续并为所有利益相关者带来利润。
数据清理和建模
作者图片
本项目的原料分析测量从伯利兹、所罗门群岛和瓦努阿图收集的城市固体废物样本数据中的能量密度(kWh/kg) 和家庭固体废物总能量(kWh) 。
这些数据是由亚太水资源顾问公司 (APWC)委托 英联邦垃圾项目 (CLiP)在 环境渔业和水产养殖科学中心 (CEFAS)的支持下收集的。收集这些数据的最初目的是支持地方政府制定更好的海洋废物管理战略。
每个国家的每个数据集都有超过 95,000 行需要清理和重新格式化。数据集中有许多重复的项目,因为每个 MSW 样品都是用 3 种不同的方法测量的:重量、计数和体积。为了计算能量密度,我只需要查看重量值(kg ),对此进行过滤可以大大减少每个数据集中的行数。在删除了所有多余的和不必要的值,并重塑数据集后,我准备建模。用于两个顶级模型的数据集的最终形状是(438,29),代表 438 个不同的家庭生活垃圾样本,每个样本具有 29 个项目特征的特定组合。这些来自 3 个不同国家的数据,被用来训练和测试我的项目中表现最好的模型。
作为过程的一部分,我经历了许多不同的模型类型。我使用 Statsmodels 和 Sci-kit Learn 从一些基本的线性回归模型开始,这两种模型都不理想,因为它们大量地过度拟合了数据。从那以后,我继续研究决策树、随机森林和 XGBoost 模型,它们都比基本的线性回归模型表现得更好。最后,我用我最喜欢的架构结束了我的建模工作,这是一个多层感知器(MLP) ,或者最近被称为神经网络。
图一。XGBoost 模型特征重要性— 作者提供的家庭生活垃圾总能量(kWh)图像
我想强调的一个值得注意的建模特性是特性重要性方法,它是 XGBoost 模型的标准。我直观地绘制了这些信息(图 1 ),这些信息代表了模型中每个特定特征的重要性或相对影响。
正如上面清楚显示的那样,我们看到前 3 个重要特性的顺序是:
- 食物
- 其他有机物
- 尿布
那么这意味着什么呢?既然这个模型已经过训练和测试,可以预测代表以 kWh 为单位的总能量的因变量,我们应该可以看到那些对每个家庭的总能量值影响最大的项目。碰巧的是,这三种物质本质上都是有机物,这意味着它们都含有相对较高的碳含量。因此,我们可以得出结论,这个模型在捕捉和评估碳含量较高的项目方面比碳含量较低的项目做得好。
图二。伯利兹模型——左图:每个地点的平均垃圾成分和能量密度(千瓦时/千克),右图:收集地点,每个地点都有垃圾详情。作者图片
现在应该指出的是,这就是像这样的数据科学分析中偏见蔓延的危险所在。标记这些数据所需的计算的本质是使用预定的科学公式(参见 净热值维基 )来完成的,根据定义,这些公式将高含碳材料的相对重要性插入到数据中。这是一个很好的迹象,模型反映了这一点,因为这是一个科学事实,碳浓度越高的材料越富含能源。然而,记住这样的偏差总是存在于数据中仍然是至关重要的,记住不断地欣赏和反思这一事实对于理解每个生成的模型的尽可能多的细微差别是至关重要的。
结果
下面是我对每个国家的顶级模型的快照比较,以及综合了所有 3 个国家数据的最终模型。训练-测试分割是模型被训练和测试的数据实例的数量。对于每个因变量,我显示每个数据集的中值,与每个模型各自的**均方根误差(RMSE)进行比较。**在描述这类回归问题的模型预测准确性时,RMSE 是一个被广泛接受的主要指标。基本上,这意味着我的模型预期它的预测会偏离真实值的平均值{\ F3 。}
图三。顶级模型比较—最佳模型是组合模型,通过增加数据来吹捧最准确的指标!作者图片
如上所述,最终的组合型号是所有型号中性能最好的型号,远远超过其他 3 种型号。
家庭生活垃圾总能量(kWh) r 平方: 0.9943
能量密度(千瓦时/千克)r 平方: 0.9575
这一结果并不令人惊讶,因为有更多数据进行训练的模型能够看到更多独特的特征组合,因此在进行未来预测时可以更好地解释它们。不管怎样,看到现实沿着预期的轨迹发展,我还是很放心的,我对我的最终模型非常满意。
那又怎样?结论和未来工作
你可能会问自己,那又怎样?这到底意味着什么?哪里来的精力!?嗯,从这个项目的结果中可以得出一些不同的见解。
**城市固体废物成分:**了解每个家庭以及特定地区的平均城市固体废物成分,可以帮助参与城市固体废物管理的所有利益相关者做出决策。从垃圾收集机构到家庭用户,更好地了解垃圾成分可以提供行为洞察以及潜在的社会经济洞察。然而,这些见解需要更多的定性数据分析,因为这超出了我的项目范围。
模式 1 -家庭生活垃圾总能量(kWh): 我认为,改变人们处理垃圾的方式的关键一步是改变思维方式。我希望这个模型能够成为用户应用的基础,通过它,有适当激励的房主可以输入关于他们自己的城市生活垃圾的数据,并获得潜在的能源输出(也许有一天还会有能源信用)。像这样的想法有发展的途径,我相信在城市固体垃圾和电力公司之间存在一个三角洲,在那里用户可以开始把他们的*【废物】*视为一种能源。我相信,为对垃圾更负责任的用户开发和整合更好的激励计划,将有助于提醒人们我们星球的循环性质,记住他们的垃圾可以在以后被更好或更坏地利用,而不是简单地被扔掉和遗忘。
模型 2 -能量密度(kWh/kg): 该模型可以为所有城市固体废物管理利益相关者提供许多与模型 1 相同的见解。然而,最大的区别在于能量密度作为一种度量标准的更普遍的性质。该模型使用每种废物的相对重量来确定一个样品的含量,而不是试图预测总共存在多少能量。这是一个更有价值的衡量标准,尤其是对那些投资于城市固体废物转化为能源的企业而言。我希望将这个模型发展到这样一个程度,即可以建立一个精细管理的城市生活垃圾项目列表作为模型的特征。在此基础上,可以轻松完成潜在设施的原料分析,并可靠地得出每个地区的准确能源密度分数。
我从这个项目中获得了很多乐趣,我也越来越习惯写这些博客了!对于那些走到这一步的人,非常感谢!我计划很快开始一个新的博客系列,以帮助我的数据科学修订和未来的学习,请继续关注!如果任何人有任何问题,评论,或者只是想聊天,请随时给我发电子邮件@ tcastanley@gmail.com!干杯!
使用 Spotipy 观察 80/90 年代音乐和现代嘻哈音乐之间的差异
实践教程
早期和当代嘻哈音乐的结构性差异。
回到 80/90 年代,hip-hip 走上了布朗克斯的街头,成为当今世界上最知名的流派之一。从那以后,对音乐的处理和接受随着美国文化一起发展。由于没有完全体验过 80/90 年代的音乐,我开始思考 80/90 年代和我们今天所听的音乐之间的结构性差异。当我读到 Kish Lal 的这篇文章时,我对 hip-hop 取得的巨大进步很感兴趣。大多数 80/90 年代的说唱歌手会把“反叛”嘻哈作为一种社会政治运动来表达黑人的斗争;现在,Kanye West 为越来越受欢迎的“情感”嘻哈音乐铺平了道路。
“西方代表着未来。这些变化会被听到,但也会导致文化的改变。”
那么,嘻哈改变了哪些特质?由于听一堆 80/90 年代的嘻哈音乐需要一段时间,所以我利用了 Spotify 的 API,这样我就可以一次提取多个曲目的“音乐属性”。**我在这里的目标是检查音乐数据,找出嘻哈的哪些成分发生了变化;通过使用 Spotify 的 API 和数据可视化工具,我希望获得关于与今天相比是什么让嘻哈音乐在 80/90 年代流行以及它在新一代中扮演的角色的见解。**有了数据可视化,很容易画出这两种类型之间的差异,并以图形方式显示嘻哈喜好的变化。
我的过程
- 我在我的 Spotify 帐户下创建了 2 个播放列表。一个包含 100 首流行的 80/90 年代嘻哈歌曲;另一个包含了 100 首流行的新嘻哈歌曲。我使用了多个 Spotify 管理的播放列表中的歌曲,以及当时和现在一些最受欢迎的艺术家的歌曲。
- 要使用 Spotify API,我需要访问我的 Spotify 凭据。通过进入 Spotify For Developers ,我登录了我的 Spotify 账户,点击了“创建一个应用”。之后,我能够看到并记下我的客户 ID 和客户机密。
- 计算机编程语言
在 Jupyter Notebook 中,我使用 spot ipy(Spotify Web API 的 Python 库)从每个播放列表中获取音乐数据。我还使用熊猫来创建数据框并保存数据集。
import pandas as pd
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import timeclient_id = '<YOUR CLIENT ID>'
client_secret = '<YOUR CLIENT SECRET>'
client_credentials_manager = SpotifyClientCredentials('<YOUR CLIENT ID>', '<YOUR CLIENT SECRET>')sp = spotipy.Spotify(client_credentials_manager = client_credentials_manager)
在这之后,我使用一个函数来获取我的播放列表中每首曲目的 id。为此,需要您的 Spotify 用户名和 Spotify 播放列表 URI。要获得播放列表 URI 你去播放列表,点击 3 点,点击共享,然后“复制 Spotify URI”。
def getTrackIDs(user, playlist_id):
ids = []
playlist = sp.user_playlist(user, playlist_id)
for item in playlist['tracks']['items']:
track = item['track']
ids.append(track['id'])
return idsids = getTrackIDs('<YOUR USERNAME>', '<SPOTIFY PLAYLIST URI>')
下一部分检索所有的曲目属性,如发行日期、流行度、专辑和所有可以在 Spotify API 中找到的音频特性。我使用的属性列表如下,稍后我将展示每个属性的定义。
def getTrackFeatures(id):
meta = sp.track(id)
features = sp.audio_features(id)name = meta['name']
artist = meta['album']['artists'][0]['name']
length = meta['duration_ms']# features
acousticness = features[0]['acousticness']
danceability = features[0]['danceability']
energy = features[0]['energy']
instrumentalness = features[0]['instrumentalness']
liveness = features[0]['liveness']
loudness = features[0]['loudness']
speechiness = features[0]['speechiness']
tempo = features[0]['tempo']track = [name, artist, length, danceability, acousticness, danceability, energy, instrumentalness, liveness, loudness, speechiness, tempo]
return track
我循环播放曲目,应用上面的函数,并将数据集保存到一个 CSV 文件中,因为我将使用 RStudio 来绘制数据集。
# loop
tracks = []
for i in range(len(ids)):
time.sleep(.5)
track = getTrackFeatures(ids[i])
tracks.append(track)track# create dataset
df = pd.DataFrame(tracks, columns = ['name', 'artist', 'length', 'danceability', 'acousticness', 'danceability', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'tempo'])df.to_csv("<your file name>.csv", sep = ',')
4.我对现代 hip hop 播放列表重复了上述过程,并使用 RStudio 绘制图表,比较并找出 80/90 年代 hip hop 和新 hip hop 之间的差异。
以下是我在此过程中使用的特性/属性列表(您可以在这里看到所有属性):
- **可跳舞性:**描述了一首曲目根据音乐元素的组合适合跳舞的程度,包括速度、节奏稳定性、节拍强度和整体规律性。越接近 1.0,越适合跳舞。
- **语音:**检测音轨中是否存在语音。越是类似语音的录音(例如脱口秀、有声读物、诗歌),属性值就越接近 1.0。
- **乐器性:**预测音轨是否不包含人声。“Ooh”和“aah”在这种情况下被视为乐器。Rap 或口语词轨道明显是“有声的”。越接近 1.0,轨道越可能不包含人声内容。
- **价:**描述一首曲目所传达的音乐积极性。高价(接近 1)的音轨听起来更积极(例如,开心、愉快、欣快),而低价(接近 0)的音轨听起来更消极(例如,悲伤、沮丧、愤怒)。
- **能量:**代表强度和活动性的感知度量。通常,高能轨道感觉起来很快,很响,很嘈杂。例如,死亡金属具有高能量,而巴赫前奏曲在音阶上得分较低。越接近 1.0,强度和活跃度越大。
- **速度:**轨道的总体估计速度,单位为每分钟节拍数(BPM)。在音乐术语中,速度是给定作品的速度或步调,直接来源于平均节拍持续时间。
- **响度:**一个音轨的整体响度,单位为分贝(dB)。响度值是整个轨道的平均值,可用于比较轨道的相对响度。越接近 0,音轨的声音越大。
- **声学:**0.0 到 1.0 的音轨是否声学的置信度度量。越接近 1,轨道的声音越大。
调查的结果
在绘制图表后,我注意到在可跳舞性、语言能力和乐器演奏能力上的差异可以忽略不计。大多数关键的差异来自于像能量、化合价、速度、响度和声音这样的属性。
我们将关注哪些关系:
- 化合价和能量
- 化合价和节奏
- 效价和响度
- 化合价和声音
我选择将这些属性与效价进行比较的原因是,80/90 年代和现在的音乐效价差异最大,能量、节奏、响度和声音是最有可能改变歌曲积极性/情绪的因素。
化合价和能量
作者图片
作者图片
新 Hip Hop: (趋势)无明确趋势且多为零散;(观察)这些歌曲的模式接近低价值(0.3)和中等能量值(0.5),这意味着这些歌曲大多有一个否定/压抑的声音,有些强烈/充满活力。
老嘻哈:(趋势)似乎没有趋势;(观察)歌曲的调式具有高价值(0.7)和中高能值(0.7),这意味着该播放列表中的大多数曲目是更快乐/乐观的歌曲,具有活力和更响亮的感觉。一些价值较低的轨道也有较高的能量值,这就是为什么没有具体的趋势。
化合价和节奏
作者图片
作者图片
新嘻哈:(趋势)价越高,赛道越快;(观察)轨道的模式包含快节奏 (~140 bpm)和中等价(~0.5)。此播放列表中的曲目节奏不同;听起来越消极的声音通常越慢,听起来积极的声音越快。
老嘻哈:(趋势)没有明确的趋势。(观察)这个播放列表中的曲目模式接近于一个慢节奏 (~90 bpm)和一个非常高的价(~0.8)。这首歌中听起来更快乐的歌曲大多是较慢的歌曲,但一些低价的歌曲也在 90 bpm 左右。
效价和响度
作者图片
作者图片
新嘻哈:(潮流)无潮流。(观察)尽管有价值,但几乎所有的曲目都响 (~-5 分贝)这可能是由于现在的音乐家比过去有更多的制作技巧和设备。
老嘻哈:(趋势)没有明确的趋势。(观察)高价歌曲似乎从-15 分贝到-4 分贝不等,但大多数歌曲都有高价的中到高响度 (~-6 分贝)。
化合价和声音
作者图片
作者图片
新嘻哈:(潮流)无潮流。(观察)尽管有价值,但大多数音轨都有中低音质(~ 0–0.3)。较新的音乐似乎包含很多更自然的声音,如钢琴、吉他、未经处理的人声。
老嘻哈:(潮流)无潮流。(观察)变化要小得多,因为该播放列表中的大多数曲目都具有高价和低音质(~0.0)。这些曲目大多不太自然,比如电子拍子和吉他。
总结
我在上面谈到了新嘻哈和 80/90 年代嘻哈的一些最重要的区别,现在我将通过提供一些例子和解释/结论来更深入地谈论它们,说明嘻哈偏好是如何变化的。
化合价和能量
80/90 年代和新嘻哈之间的主要区别是效价。我们这一代人更喜欢不太乐观、令人沮丧的歌曲,而 80/90 年代的嘻哈则更积极、更有前瞻性,与更高的强度和能量水平相关。两个播放列表都有一个非常明显的趋势,即一首曲目的价值越低,它包含的能量就越少。
80/90 年代嘻哈
80/90 年代积极嘻哈音乐的最佳范例是 Nas 的《世界是你的》,奎恩·拉提法的《联合国青年队》,公敌的《与权力作斗争》,停止暴力运动的《自我毁灭》,以及西海岸全明星乐队的《我们都一样》。80/90 年代的嘻哈是一场“社会政治运动”,这些只是许多乐观和充满希望的歌曲中的一部分,这些歌曲谈论黑人对黑人犯罪,制止帮派暴力,鼓励团结,生活课程,以及对年轻黑人的鼓舞人心的积极信息。
新嘻哈
新嘻哈比原来的嘻哈少了很多积极/活力。一些例子包括 Cordae ft 的“Gifted”。罗迪·里奇,Juice Wrld 的《许愿池》,NF 的《瘫痪》,未来派的《没有别人》,逻辑的《完美》。其中一个区别是,今天大多数嘻哈艺术家谈论金钱和名誉,以及与毒瘾、心理健康、他们的过去、努力成为最好、与其他说唱歌手竞争等的斗争。他们不会隐瞒自己内心的黑暗面和生活的丰富面。不是说今天的说唱歌手对反对种族主义的运动没有贡献;许多人仍然这样做,但他们所有的歌曲很可能不是围绕一个点。他们在音乐中谈论的东西有更多的变化,粉丝们似乎喜欢这种开放性和多样性。
新嘻哈音乐中关于黑人运动的音乐似乎正在兴起,尤其是现在,歌曲包括黑人思想的“思想与每个人”,Lil Baby 的“更大的画面”,Anime 的“根”,Beyonce ft 的“自由”。肯德里克·拉马尔。因此,“社会-政治运动”可能会在嘻哈音乐中回归,但他们很可能会比 80/90 年代更“不快乐”。
声学
较新的嘻哈音乐有更多的音响效果,这意味着更多的曲目由吉他、钢琴、管弦乐或自然人声组成。80/90 年代的嘻哈音乐由电吉他、合成器、鼓和自动调音人声等电音组成。
80/90 年代嘻哈
电子放克、EDM 和 techno 是当时流行的嘻哈电子子流派,所以大多数音乐很可能包含电子音乐的元素;这就是为什么尽管它们有价值,但声学值却很低的原因。一些带有电子元素的歌曲包括马索·曼恩和莱德曼的《达·洛克怀尔德》、塞浦瑞斯·希尔的《大脑中的疯狂》、莫布·迪普的《适者生存》和图帕克的《我四处走动》。
新嘻哈
我们可以看到,较新的嘻哈歌手创作的歌曲在图中的价值上变化更大。由于艺术家现在制作不太积极的歌曲,旧音乐中的电子元素不适合新艺术家试图带来的新音调和情感。像钢琴、吉他和自然声音这样的声学元素使用得更频繁,尽管有价。沮丧的歌曲倾向于使用更多的音响作为背景,因为它融入了柔和的元素,甚至更高价的歌曲也使用一些音响。在我看来,声学的使用是激发听众情绪的最佳方式,并使他们更容易以艺术家希望的方式与音乐联系起来。例子包括麦克莫尔的《橘子酱》、罗辑的《灵魂食物 II》、NF 的《我怀念那些日子》、YoungBoy 的《不易》永不再断;这些大多使用自然的人声或钢琴作为背景。声学现在是一个更受欢迎的使用元素,尤其是在嘻哈音乐的低保真度子类别中,那里通常没有人声或文字。
拍子
较新的嘻哈音乐通常比 80/90 年代的嘻哈音乐节奏更快。如果你听 80/90 年代的 hip hop,并将其与今天的 hip hop 进行比较,这一点是最容易理解的。你可以注意到,一般来说,像 Nas、2Pac、IceCube 和德瑞医生这样的老艺人说唱速度稍慢,并拉长了他们的歌词,而今天的许多艺人说唱速度快得多,如 NF、Logic、Quadeca、Drake、Migos 等。较新的 hip hop 中的高价歌曲具有快速的节奏以及低价歌曲,并且许多低价歌曲比更积极的歌曲快得多(NF(不太积极)vs Future(更积极)就是一个例子)。似乎有一个总的趋势,节奏越慢,歌曲就越不罗嗦,你会发现新嘻哈的节奏比 80/90 年代的嘻哈有更多的变化。
音量
对于该属性,新 Hip Hop 和 80/90 年代 Hip Hop 关于它们不同的价数值具有几乎相同的响度值。大多数新嘻哈的价值较低,声音较大;大多数 80/90 后嘻哈的价值更高,也更响亮。可能导致新嘻哈比旧嘻哈更响亮的主要原因之一是乐器和嘻哈节拍的使用增加,包括贝斯、高频打击乐、鼓和增加新嘻哈活力的新元素。
结论
- 一首流行的 80/90 年代嘻哈音乐=高能量,低声音,慢节奏,安静
- 一首流行的现代嘻哈音乐=变化的能量(大部分是低能量),高音质,快节奏,更大声
在整个项目中,我能够通过使用数据可视化来比较两代人(80/90 年代和今天)的嘻哈音乐,以发现嘻哈文化、艺术家和一代偏好的转变带来了哪些实质性的变化。我也逐渐明白了在每一代人中,什么会成为流行歌曲。到底什么是嘻哈音乐,这可能仍然令人困惑;但有趣的是,hip-hop 的特点和喜好不可避免地会随着时间而变化。嘻哈是一种前瞻性的音乐类型,因为久而久之,更多的艺术家在他们的音乐中使用新的技术或强调不同的音乐属性。不可想象的数量的人也在网上制作歌曲,推动嘻哈文化,他们每个人都可能给这种风格带来新的东西,给音乐行业带来更多的变化。
我尝试用深度学习来预测股市
用神经网络预测股票价格
Vladimir Solomyani 在 Unsplash 上的照片
我想象一下,如果你能够知道一只股票下周是涨是跌,然后用你剩余的现金,你会把所有的钱投资或做空这只股票。玩了股票市场之后,知道了股票会升值还是贬值,你可能会成为百万富翁!
可惜这是不可能的,因为没有人能知道未来。然而,我们可以根据我们现在和过去对任何股票的信息做出估计和明智的预测。根据股票价格过去的走势和模式做出的估计称为 技术分析 。我们可以使用技术分析( TA )来预测一只股票的价格方向,然而,这并不是 100%准确的。事实上,一些交易者批评 TA,并说它在预测未来方面和占星术一样有效。但是也有其他交易者相信它,并且建立了长期成功的交易生涯。
在我们的例子中,我们将使用的神经网络将利用 TA 来帮助它做出明智的预测。我们将实现的特定神经网络被称为 递归神经网络——LSTM。之前我们利用 RNN 来预测比特币价格(见下面的文章):
使用神经网络预测比特币价格
towardsdatascience.com](/predicting-bitcoin-prices-with-deep-learning-438bc3cf9a6f)
在文章中,我们探索了 LSTM 预测比特币价格的用法。我们稍微研究了一下 LSTM 模型的背景,并给出了如何编程来预测 BTC 价格的说明。然而,我们将输入数据限制在比特币自身的价格历史上,不包括其他变量,如成交量或移动平均线等技术指标。
多变量输入
由于我们构建的最后一个 RNN 只能接受一个序列(过去的收盘价)来预测未来,我们想看看是否有可能向神经网络添加更多的数据。也许这些其他数据可以提高我们的价格预测?也许通过在我们的数据集中添加 TA 指标,神经网络可能能够做出更准确的预测?—这正是我们想要在这里完成的。
在接下来的几个部分中,我们将构建一个新的递归神经网络,它能够以技术指标的形式接收多条信息,从而预测股票市场的未来价格。
价格历史和技术指标
为了使用神经网络来预测股票市场,我们将利用来自***&【p500】***的价格。这将使我们对股票市场有一个总体的了解,通过使用 RNN,我们也许能够判断出市场的走向。
下载价格历史记录
要为我们的神经网络检索正确的数据,你需要去雅虎财经 下载间谍 的价格。我们将为 SPY 下载五年的价格历史,作为一个方便的.csv
文件。
另一种选择是使用财务数据 API,如 EOD 历史数据 。注册是免费的,你可以访问大量的数据集。披露:我通过上面的链接从任何购买中赚取一小笔佣金。
技术指标
在我们下载了 SPY 的价格历史之后,我们可以应用技术分析 Python 库来生成技术指标值。此处更深入地介绍了我们能够从中检索指标值的流程:
使用 Python 创建比特币的技术指标
towardsdatascience.com](/technical-indicators-on-bitcoin-using-python-c392b4a33810)
上面的文章详细介绍了我们为了检索 SPY 的指标值而使用的确切的 TA Python 库。
编码神经网络
导入库
让我们通过首先导入一些库来开始编码我们的神经网络:
首先,我们导入了一些常用的 Python 库( numpy、pandas 等)。接下来,我们导入了之前用来创建 BTC 技术指标的技术分析库(上文中的提到了*)。然后,我们从tensor flow Keras导入神经网络库。导入必要的库后,我们将加载从雅虎财经下载的文件。*
预处理数据
日期时间转换
加载数据后,我们需要执行一些预处理,以便为神经网络准备数据,我们需要做的第一件事是将数据帧的索引转换为日期时间格式。然后,我们将数据中的Date
列设置为 DF 的索引。
创建技术指标
接下来,我们将使用ta
库创建一些技术指标。为了涵盖尽可能多的技术分析,我们将使用库中所有可用的指标。然后,从数据集中删除除了指标和收盘* 价格之外的所有数据。*
最近的数据
一旦我们创建了技术指标值,我们就可以从原始数据集中删除一些行。我们将只包括最后 1000 行数据,以便更准确地反映当前的市场环境。
缩放数据
当缩放我们的数据时,有多种方法可以确保我们的数据仍然被准确地表示。试验不同的缩放器以查看它们对模型性能的影响可能是有用的。
在我们的例子中,我们将利用RobustScaler
来扩展我们的数据。这样做是为了使极端的异常值几乎没有影响,并有望改善训练时间和整体模型性能。
使用强大的缩放器缩放收盘价
助手功能
在我们开始构建神经网络之前,让我们创建一些辅助函数来更好地优化这个过程。我们将详细解释每个功能。
split_sequence
—此函数分割多元时间序列。在我们的例子中,输入值将是股票的收盘价和指标。这将把这些值拆分成我们的 X 和 y 变量。 X 值将包含过去的收盘价和技术指标。 y 值将包含我们的目标值(仅未来收盘价)。visualize_training_results
—这个函数将帮助我们评估刚刚创建的神经网络。我们在评估我们的 NN 时要寻找的东西是 收敛 。随着训练的进行,损失和准确度的验证值和常规值必须开始与对齐。如果它们不收敛,那么这可能是过度拟合/欠拟合的迹象。我们必须回过头来修改神经网络的结构,这意味着改变层/节点的数量,改变优化函数等。layer_maker
—这个函数构造了我们的神经网络的主体。在这里,我们可以自定义层数和节点数。它还有一个正则化选项,可以在必要时添加漏失层,以防止过拟合/欠拟合。validater
—该函数使用特定日期范围的预测值创建 DF。这个范围随着每个循环向前滚动。范围的间隔是可定制的。我们使用这个 DF 来评估模型的预测,稍后将它们与实际值进行比较。val_rmse
—该函数将返回模型预测值与实际值相比的均方根误差( RMSE )。返回值代表我们的模型预测的平均偏差。总体目标是降低我们模型预测的 RMSE。
拆分数据
为了适当地格式化我们的数据,我们需要将数据分成两个序列。这些序列的长度可以修改,但我们将使用过去 90 天的值来预测未来 30 天的价格。然后,split_sequence
函数会将我们的数据格式化成适当的 X 和 y 变量,其中 X 包含过去 90 天的收盘价和指标,而 y 包含未来 30 天的收盘价。
# How many periods looking back to learn
n_per_in = 90# How many periods to predict
n_per_out = 30# Features
n_features = df.shape[1]# Splitting the data into appropriate sequences
X, y = split_sequence(df.to_numpy(), n_per_in, n_per_out)
我们的神经网络将对这些信息做的是了解过去 90 天的收盘价和技术指标值如何影响未来 30 天的收盘价。
神经网络建模
现在我们可以开始构建我们的神经网络了!下面的代码是我们如何用自定义层和节点构建神经网络的。
这是我们开始试验以下参数的地方:
- 层数
- 节点数量
- 不同的激活功能
- 不同的优化器
- 时期数和批量大小
我们为这些参数中的每一个输入的值都必须被探究,因为每一个值都会对整个模型的质量产生显著的影响。可能有一些方法可以找到每个参数的最佳值。对于我们的例子,我们主观地测试了每个参数的不同值,我们发现的最佳值可以在上面的代码片段中看到。
如果你希望了解更多关于这些变量背后的推理和概念,那么建议你阅读我们之前的 关于深度学习和比特币 的文章。
可视化损失和准确性
训练后,我们将使用自定义助手函数来可视化神经网络的进展:
visualize_training_results(res)
随着我们的网络训练,我们可以看到损失减少和准确性增加。一般来说,随着历元数量的增加,我们会寻找两条线 汇聚或 对齐。如果他们没有,那么这是一个迹象,表明该模型是不充分的,我们将需要回去,改变一些参数。
模型验证
我们评估模型预测质量的另一种方法是对照实际值进行测试,并使用我们的自定义帮助函数val_rmse
计算 RMSE。
在这里,我们将预测值与实际值绘制在一起,看看比较的效果如何。如果预测值的曲线非常偏离实际值,那么我们知道我们的模型是有缺陷的。但是,如果这些值看起来非常接近,并且 RMSE 非常低,那么我们可以得出结论,我们的模型是可以接受的。
我们的模型似乎在开始做得很好,但它不能捕捉或模拟价格的剧烈波动。这可能是为什么最后三个预测看起来非常遥远。也许通过更多的训练和实验,我们的模型可以预测这些运动。
预测未来
一旦我们对模型的表现感到满意,我们就可以用它来预测未来的值。这一部分相对于我们已经做的事情来说相当简单。
这里我们只是从下载的.csv
文件中预测出最近的值。运行代码后,我们将看到以下预测:
下个月间谍价格预测
我们做到了!—间谍的预测价格。用这些知识做你想做的事情,但是记住一件事:股票市场是不可预测的。这里预测的值是而不是确定的。它们可能比随机猜测要好,因为这些值是基于过去的技术指标和价格模式的有根据的猜测。
关闭
我们能够成功地构建一个 LSTM 层的递归神经网络,它能够接受多个输入而不是一个。模型的质量可能因人而异,取决于他们希望在模型上花费多少时间。即使预测未来是不可能的,但这些预测对于那些希望了解股票未来价格走势的人来说非常有用。但是,它很可能认为这种方式比随机猜测要好。
在 eodhistoricaldata.com 另外出版
来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
资源
使用时间序列模型:萨里玛和 FB 先知预测比特币价格:使用递归神经网络:LSTM…
github.com](https://github.com/marcosan93/Price-Forecaster) [## 我尝试了深度学习模型来预测比特币价格
使用神经网络预测比特币价格
towardsdatascience.com](/predicting-bitcoin-prices-with-deep-learning-438bc3cf9a6f) [## 我是如何为比特币的技术指标编码的
使用 Python 创建比特币的技术指标
towardsdatascience.com](/technical-indicators-on-bitcoin-using-python-c392b4a33810) [## 如何开发用于时间序列预测的 LSTM 模型-机器学习掌握
长短期记忆网络,简称 LSTMs,可用于时间序列预测。有很多种…
machinelearningmastery.com](https://machinelearningmastery.com/how-to-develop-lstm-models-for-time-series-forecasting/)
使用深度学习进行端到端多类文本分类
图片由 Anrita1705 来自 Pixabay
你有没有想过有毒评论是如何在 Quora 或 Reddit 这样的平台上被自动标记的?或者邮件如何被标记为垃圾邮件?或者是什么决定了向你展示哪些在线广告?
以上都是文本分类在不同领域应用的例子。文本分类是自然语言处理(NLP)中的一项常见任务,它将长度不定的文本序列转换成单一类别。
从上面的例子中出现的一个主题是所有的都有一个二进制的目标类。比如要么评论有毒没毒,要么评论假没假。简而言之,只有两个目标类,因此称为二进制。
但情况并非总是如此,有些问题可能有两个以上的目标类。这些问题被方便地称为多类分类,这就是我们在这篇文章中要关注的问题。多类分类的一些例子包括:
- 评论的情绪:积极、消极或中立(三类)
- 新闻按类型分类:娱乐、教育、政治等。
另外,如果你对在工作中使用的定制深度学习工作站或服务器感兴趣,Exxact Corporation 有一系列基于人工智能的解决方案,起价为 3700 美元,带有几个英伟达 RTX 30 系列 GPU,3 年保修和深度学习软件堆栈。
在本帖中,我们将使用各种深度学习方法来解决一个多类文本分类问题。
数据集/问题描述
在这篇文章中,我使用了来自 Kaggle 的 UCI ML 药物评论数据集。它包含超过 200,000 个患者药物评论,以及相关条件。数据集有许多列,但是我们将只使用其中的两列来完成 NLP 任务。
所以,我们的数据集看起来像这样:
任务:我们希望根据药物综述对主要疾病进行分类。
word2vec 嵌入入门:
在我们进一步研究文本分类之前,我们需要一种在词汇表中用数字表示单词的方法。为什么?因为我们大部分的 ML 模型都需要数字,而不是文本。
实现这个目标的一种方法是使用单词向量的一次性编码,但这不是正确的选择。给定大量的词汇,这种表示将占用大量的空间,并且它不能准确地表达不同单词之间的相似性,例如如果我们想要找到数字单词 x 和 y 之间的余弦相似性:
给定独热编码向量的结构,不同单词之间的相似度总是为 0。
Word2Vec 通过为我们提供一个固定长度(通常比词汇表大小小得多)的单词向量表示来克服上述困难。它还捕捉不同单词之间的相似性和类比关系。
单词向量的学习方式允许我们学习不同的类比。这使我们能够对单词进行代数运算,这在以前是不可能的。
比如:什么是王者——男人+女人?结果是皇后。
Word2Vec 向量也帮助我们找到单词之间的相似性。如果我们寻找与“好”相似的词,我们会发现很棒,很棒,等等。word2vec 的这一特性使得它在文本分类中具有不可估量的价值。有了这个,我们的深度学习网络就明白了“好”和“伟大”是意思相近的词。
简单地说,word2vec 为单词创建了固定长度的向量,为字典中的每个单词(和常见的二元模型)提供了一个 d 维向量。
这些词向量通常是预先训练好的,并由其他人在像维基百科、推特等大型文本语料库上训练后提供。最常用的预训练词向量有手套和 300 维词向量的快速文本。在这篇文章中,我们将使用手套词向量。
数据预处理
在大多数情况下,文本数据并不完全干净。来自不同来源的数据具有不同的特征,这使得文本预处理成为分类管道中最关键的步骤之一。例如,来自 Twitter 的文本数据不同于在 Quora 或其他新闻/博客平台上找到的文本数据,每种数据都需要区别对待。然而,我们将在这篇文章中讨论的技术对于你在 NLP 的丛林中可能遇到的几乎任何类型的数据都足够通用。
a)清除特殊字符并删除标点符号
我们的预处理管道很大程度上依赖于我们将用于分类任务的 word2vec 嵌入。原则上,我们的预处理应该匹配在训练单词嵌入之前使用的预处理。由于大多数嵌入没有为标点符号和其他特殊字符提供向量值,所以我们要做的第一件事就是去掉文本数据中的特殊字符。
# Some preprocesssing that will be common to all the text classification methods you will see.import re
def clean_text(x):
pattern = r'[^a-zA-z0-9\s]'
text = re.sub(pattern, '', x)
return x
b)清洁编号
为什么我们要用#s 代替数字?因为包括 Glove 在内的大多数嵌入都以这种方式对其文本进行了预处理。
小 Python 绝招: 我们在下面的代码中使用 if 语句来预先检查文本中是否存在数字,因为if
总是比re.sub
命令快,而且我们的大部分文本都不包含数字。
def clean_numbers(x):
if bool(re.search(r'\d', x)):
x = re.sub('[0-9]{5,}', '#####', x)
x = re.sub('[0-9]**{4}**', '####', x)
x = re.sub('[0-9]**{3}**', '###', x)
x = re.sub('[0-9]**{2}**', '##', x)
return x
c)消除收缩
缩写是我们用撇号写的单词。缩写的例子是像“不是”或“不是”这样的词。因为我们想标准化我们的文本,所以扩展这些缩写是有意义的。下面我们使用收缩映射和正则表达式函数来完成。
contraction_dict = {"ain't": "is not", "aren't": "are not","can't": "cannot", "'cause": "because", "could've": "could have"}def _get_contractions(contraction_dict):
contraction_re = re.compile('(**%s**)' % '|'.join(contraction_dict.keys()))
return contraction_dict, contraction_re
contractions, contractions_re = _get_contractions(contraction_dict)def replace_contractions(text):
def replace(match):
return contractions[match.group(0)]
return contractions_re.sub(replace, text)*# Usage*
replace_contractions("this's a text with contraction")
除了以上技巧,你可能还想做拼写纠正。但是因为我们的帖子已经很长了,我们现在就离开它。
数据表示:序列创建
使深度学习成为 NLP 的首选的一件事是,我们不必从我们的文本数据中手工设计特征;深度学习算法将一系列文本作为输入,像人类一样学习其结构。由于机器不能理解文字,它们希望数据是数字形式的。因此,我们需要将文本数据表示为一系列数字。
为了理解这是如何做到的,我们需要了解一些关于 Keras Tokenizer 函数的知识。其他记号赋予器也是可行的,但是 Keras 记号赋予器对我来说是个不错的选择。
a)记号赋予器
简而言之,记号赋予器是一个将句子拆分成单词的实用函数。keras.preprocessing.text.Tokenizer
将文本标记(拆分)成标记(单词),同时只保留文本语料库中出现次数最多的单词。
#Signature:
Tokenizer(num_words=None, filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
lower=True, split=' ', char_level=False, oov_token=None, document_count=0, **kwargs)
num_words 参数只保留文本中预先指定数量的单词。这是有帮助的,因为我们不希望我们的模型因为考虑不经常出现的单词而受到很多干扰。在真实世界的数据中,我们使用 num_words 参数留下的大多数单词通常都是拼写错误的单词。缺省情况下,记号赋予器还会过滤掉一些不想要的记号,并将文本转换成小写。
一旦适合数据,tokenizer 还保留一个单词索引(一个我们可以用来为单词分配唯一编号的字典),可以通过 tokenizer.word_index 访问。索引词典中的单词按出现频率排列。
因此,使用记号赋予器的全部代码如下:
from keras.preprocessing.text import Tokenizer## Tokenize the sentences
tokenizer = Tokenizer(num_words=max_features)
tokenizer.fit_on_texts(list(train_X)+list(test_X))
train_X = tokenizer.texts_to_sequences(train_X)
test_X = tokenizer.texts_to_sequences(test_X)
其中train_X
和test_X
是语料库中的文档列表。
b)填充序列
通常,我们的模型期望每个文本序列(每个训练示例)具有相同的长度(相同数量的单词/标记)。我们可以使用maxlen
参数对此进行控制。
例如:
train_X = pad_sequences(train_X, maxlen=maxlen)
test_X = pad_sequences(test_X, maxlen=maxlen)
现在我们的训练数据包含了一个数字列表。每个列表都有相同的长度。我们还有word_index
,它是文本语料库中出现最多的单词的字典。
c)编码目标变量的标签
Pytorch 模型期望目标变量是一个数字,而不是一个字符串。我们可以使用来自sklearn
的标签编码器来转换我们的目标变量。
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
train_y = le.fit_transform(train_y.values)
test_y = le.transform(test_y.values)
负载嵌入
首先,我们需要加载所需的手套嵌入。
def load_glove(word_index):
EMBEDDING_FILE = '../input/glove840b300dtxt/glove.840B.300d.txt'
def get_coefs(word,*arr): return word, np.asarray(arr, dtype='float32')[:300]
embeddings_index = dict(get_coefs(*o.split(" ")) for o in open(EMBEDDING_FILE))
all_embs = np.stack(embeddings_index.values())
emb_mean,emb_std = -0.005838499,0.48782197
embed_size = all_embs.shape[1]nb_words = min(max_features, len(word_index)+1)
embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))
for word, i in word_index.items():
if i >= max_features: continue
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
embedding_matrix[i] = embedding_vector
else:
embedding_vector = embeddings_index.get(word.capitalize())
if embedding_vector is not None:
embedding_matrix[i] = embedding_vector
return embedding_matrixembedding_matrix = load_glove(tokenizer.word_index)
一定要把下载这些手套向量的文件夹路径放进去。embeddings_index
包含什么?它是一个字典,其中的键是单词,值是单词向量,一个长度为 300 的np.array
。这部词典的长度大约是十亿。
因为我们只想要嵌入在我们的word_index
中的单词,我们将使用来自我们的标记器的单词索引创建一个只包含所需嵌入的矩阵。
深度学习模型
1.TextCNN
在 Yoon Kim 的论文用于句子分类的卷积神经网络中首次提出了使用 CNN 对文本进行分类的想法。
表示:这个想法的中心概念是把我们的文档看作图像。但是怎么做呢?假设我们有一个句子,我们有maxlen
= 70,嵌入大小= 300。我们可以创建一个形状为 70×300 的数字矩阵来表示这个句子。图像也有一个矩阵,其中各个元素是像素值。但是任务的输入不是图像像素,而是用矩阵表示的句子或文档。矩阵的每一行对应一个单词向量。
卷积思想:对于图像,我们移动我们的 conv。水平和垂直过滤,但对于文本,我们将内核大小固定为 filter_size x embed_size,即(3,300),我们将垂直向下移动卷积,同时查看三个单词,因为我们在这种情况下的过滤器大小为 3。这个想法似乎是正确的,因为我们的卷积滤波器不分裂字嵌入;它会查看每个单词的完整嵌入。此外,我们可以将过滤器的大小想象成一元、二元、三元等。因为我们正在分别查看 1、2、3 和 5 个单词的上下文窗口。
这里是 CNN 网络编码在 Pytorch 的文本分类。
class CNN_Text(nn.Module):
def __init__(self):
super(CNN_Text, self).__init__()
filter_sizes = [1,2,3,5]
num_filters = 36
n_classes = len(le.classes_)
self.embedding = nn.Embedding(max_features, embed_size)
self.embedding.weight = nn.Parameter(torch.tensor(embedding_matrix, dtype=torch.float32))
self.embedding.weight.requires_grad = False
self.convs1 = nn.ModuleList([nn.Conv2d(1, num_filters, (K, embed_size)) for K in filter_sizes])
self.dropout = nn.Dropout(0.1)
self.fc1 = nn.Linear(len(filter_sizes)*num_filters, n_classes)def forward(self, x):
x = self.embedding(x)
x = x.unsqueeze(1)
x = [F.relu(conv(x)).squeeze(3) for conv in self.convs1]
x = [F.max_pool1d(i, i.size(2)).squeeze(2) for i in x]
x = torch.cat(x, 1)
x = self.dropout(x)
logit = self.fc1(x)
return logit
2.双向 RNN (LSTM/GRU)
TextCNN 很适合文本分类,因为它处理近距离的单词。比如它可以一起看《纽约》。然而,它仍然不能处理特定文本序列中提供的所有上下文。它仍然没有学习数据的顺序结构,其中每个单词都依赖于前一个单词或前一个句子中的一个单词。
RNNs 可以帮助我们。他们可以使用隐藏状态记住以前的信息,并将其与当前任务联系起来。
长短期记忆网络(LSTM)是 RNN 的一个子类,专门用于长时间记忆信息。此外,双向 LSTM 保持了两个方向的上下文信息,这在文本分类任务中非常有用(但是,它不适用于时间序列预测任务,因为在这种情况下我们无法看到未来)。
为了简单解释双向 RNN,可以把 RNN 单元想象成一个黑盒,它接受一个隐藏状态(向量)和一个字向量作为输入,给出一个输出向量和下一个隐藏状态。这个盒子有一些权重需要使用损耗的反向传播来调整。此外,相同的单元格应用于所有单词,以便句子中的单词共享权重。这种现象被称为重量共享。
Hidden state, Word vector ->(RNN Cell) -> Output Vector , Next Hidden state
对于像“你永远不会相信”这样的长度为 4 的序列,RNN 单元给出 4 个输出向量,这些向量可以连接起来,然后用作密集前馈架构的一部分。
在双向 RNN 中,唯一的变化是我们以通常的方式以及相反的方式阅读文本。所以我们并行堆叠两个 rnn,我们得到 8 个输出向量来附加。
一旦我们得到了输出向量,我们就将它们发送到一系列密集层,最后是 softmax 层,以构建文本分类器。
在大多数情况下,您需要了解如何在神经网络中堆叠一些层以获得最佳结果。如果性能更好,我们可以在网络中尝试多个双向 GRU/LSTM 层。
由于 RNNs 的局限性,例如不记得长期依赖关系,在实践中,我们几乎总是使用 LSTM/GRU 来建模长期依赖关系。在这种情况下,您可以将上图中的 RNN 单元格想象为 LSTM 单元格或 GRU 单元格。
以下是 Pytorch 中用于该网络的一些代码:
class **BiLSTM**(nn.Module):
def __init__(self):
super(BiLSTM, self).__init__()
self.hidden_size = 64
drp = 0.1
n_classes = len(le.classes_)
self.embedding = nn.Embedding(max_features, embed_size)
self.embedding.weight = nn.Parameter(torch.tensor(embedding_matrix, dtype=torch.float32))
self.embedding.weight.requires_grad = False
self.lstm = nn.LSTM(embed_size, self.hidden_size, bidirectional=True, batch_first=True)
self.linear = nn.Linear(self.hidden_size*4 , 64)
self.relu = nn.ReLU()
self.dropout = nn.Dropout(drp)
self.out = nn.Linear(64, n_classes)
def forward(self, x):
*#rint(x.size())*
h_embedding = self.embedding(x)
*#_embedding = torch.squeeze(torch.unsqueeze(h_embedding, 0))*
h_lstm, _ = self.lstm(h_embedding)
avg_pool = torch.mean(h_lstm, 1)
max_pool, _ = torch.max(h_lstm, 1)
conc = torch.cat(( avg_pool, max_pool), 1)
conc = self.relu(self.linear(conc))
conc = self.dropout(conc)
out = self.out(conc)
return out
培养
下面是我们用来训练 BiLSTM 模型的代码。代码注释的很好,请大家通读代码理解。你可能也想看看我在 Pytorch 上的帖子。
n_epochs = 6
model = BiLSTM()
loss_fn = nn.CrossEntropyLoss(reduction='sum')
optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.001)
model.cuda()
*# Load train and test in CUDA Memory*
x_train = torch.tensor(train_X, dtype=torch.long).cuda()
y_train = torch.tensor(train_y, dtype=torch.long).cuda()
x_cv = torch.tensor(test_X, dtype=torch.long).cuda()
y_cv = torch.tensor(test_y, dtype=torch.long).cuda()
*# Create Torch datasets*
train = torch.utils.data.TensorDataset(x_train, y_train)
valid = torch.utils.data.TensorDataset(x_cv, y_cv)
*# Create Data Loaders*
train_loader = torch.utils.data.DataLoader(train, batch_size=batch_size, shuffle=True)
valid_loader = torch.utils.data.DataLoader(valid, batch_size=batch_size, shuffle=False)
train_loss = []
valid_loss = []
for epoch **in** range(n_epochs):
start_time = time.time()
*# Set model to train configuration*
model.train()
avg_loss = 0\.
for i, (x_batch, y_batch) **in** enumerate(train_loader):
*# Predict/Forward Pass*
y_pred = model(x_batch)
*# Compute loss*
loss = loss_fn(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
avg_loss += loss.item() / len(train_loader)
*# Set model to validation configuration -Doesn't get trained here*
model.eval()
avg_val_loss = 0.
val_preds = np.zeros((len(x_cv),len(le.classes_)))
for i, (x_batch, y_batch) **in** enumerate(valid_loader):
y_pred = model(x_batch).detach()
avg_val_loss += loss_fn(y_pred, y_batch).item() / len(valid_loader)
*# keep/store predictions*
val_preds[i * batch_size:(i+1) * batch_size] =F.softmax(y_pred).cpu().numpy()
*# Check Accuracy*
val_accuracy = sum(val_preds.argmax(axis=1)==test_y)/len(test_y)
train_loss.append(avg_loss)
valid_loss.append(avg_val_loss)
elapsed_time = time.time() - start_time
print('Epoch **{}**/**{}** **\t** loss=**{:.4f}** **\t** val_loss=**{:.4f}** **\t** val_acc=**{:.4f}** **\t** time=**{:.2f}**s'.format(
epoch + 1, n_epochs, avg_loss, avg_val_loss, val_accuracy, elapsed_time))
培训输出如下所示:
结果/预测
import scikitplot as skplt
y_true = [le.classes_[x] for x **in** test_y]
y_pred = [le.classes_[x] for x **in** val_preds.argmax(axis=1)]
skplt.metrics.plot_confusion_matrix(
y_true,
y_pred,
figsize=(12,12),x_tick_rotation=90)
以下是 BiLSTM 模型结果的混淆矩阵。我们可以看到,我们的模型做得相当好,在验证数据集上有 87%的准确率。
有趣的是,即使在模型表现不佳的地方,这也是可以理解的。例如,该模型混淆了减肥和肥胖、抑郁和焦虑、抑郁和双相情感障碍。我不是专家,但是这些病确实感觉挺像的。
结论
在这篇文章中,我们介绍了用于文本分类的深度学习架构,如 LSTM 和 CNN,并解释了 NLP 深度学习中使用的不同步骤。
仍然有很多可以做的来改进这个模型的性能。改变学习速率、使用学习速率表、使用额外的特征、丰富嵌入、去除拼写错误等。我希望这段样板代码能为您可能面临的任何文本分类问题提供一个参考基准。
你可以在 Github 或者这个 Kaggle 内核上找到完整的工作代码。
如果你想了解更多关于自然语言处理的知识,我想从 DeepLearning.ai 中调出一门关于自然语言处理的优秀课程,一定要去看看。
我以后也会写更多这样的帖子。让我知道你对这个系列的看法。在 中 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。
此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
这个故事最初发表于这里。
使用组织学载玻片上的深度学习来筛选癌症生物标志物
深度学习能从基本的 H&E 染色中提取新的见解吗?
来源:作者
我们采访了来自 Pixel Scientia Labs 的 Heather Couture ,她分享了她最近关于在 H & E 载玻片上使用深度学习来筛选癌症生物标志物的研究见解。
基因组分析和 H&E 染色简介
传统上,科学家分析疾病,如癌症,集中于单个基因(基因测试)。但今天,他们越来越多地使用基因组分析来研究构成癌症的所有不同基因,并提供该疾病的全面概况。
虽然这种基因组分析可以提供准确、具体的见解,通过识别正确的癌症亚型来帮助治疗疾病,但它也是缓慢而昂贵的。
相比之下,分析 H & E 污渍要简单得多,也便宜得多。这包括在标准显微镜下观察染色的组织或细胞样本。H & E 将细胞核变成蓝色,细胞质变成粉红色,这样病理学家可以更容易地解读载玻片。但是它提供了不同于基因组分析的信息。
Heather 利用深度学习,开发了基于 H&E 图像预测癌细胞的一些基因组属性的过程。这意味着病理学家可以更快、更经济地识别癌症生物标志物。
机器学习与快速、廉价的 H&E 染色相结合,可以实现更昂贵的个性化医学基因组测试的一些好处。图片来源:作者
希瑟为我们描述了这一过程:
“对于乳腺癌分析,我们预测肿瘤的分子特性。这可以用于个性化治疗。如果你知道肿瘤的确切亚型,这可能意味着一种特定的药物比另一种更可能有效。
通常,这些评估是通过基因组数据完成的。我们在北卡罗来纳大学的团队发现,我们可以从 H & E 组织切片——在显微镜下染色和成像的切片——预测其中一些特性。这不是一个完美的结果,但在投入时间和金钱进行完整的基因组测试之前,它可以作为一个筛选过程。”
[ 注册我们的时事通讯,了解研究人员如何将机器学习应用于他们的分析管道。]
机器学习可以做出医生做不到的预测
这不仅仅是因为机器通常效率更高,能够处理更大的数据集。基于容易获得的 H&E 载玻片预测分子性质根本不是人类可以做到的。正如希瑟所说:
病理学家可以使用一种不同类型的染色,并能够获得蛋白质生物标记。有很多方法可以做到这一点,这是实验室的标准,但不是来自 H & E,这是最常见的染色诊断方法。”
这并不意味着算法会取代人类。希瑟预言了一个人类和机器携手工作的世界。但正如她指出的那样,只有时间才能告诉我们这种伙伴关系将如何运作:
“这大概不应该是艾对病理学家吧。应该是 AI 和病理学家联合。他们是如何合作的?是不是 AI 先看图像,找到病理学家应该看的区域?AI 会检查病理学家的回答来寻找错误吗?或者他们做不同的任务:人工智能可以做一些平凡的事情——比如计数细胞,寻找细胞分裂,或者类似的一些小事——然后病理学家接管。”
这是一个快速发展的研究领域,所以希望我们能很快找到这些问题的答案。希瑟报道了最近的兴趣激增:
“一篇又一篇论文展示了使用机器学习进行生物标记分析的各种方法。仅在上周,《自然》杂志就有两篇论文对几种癌症的概念进行了测试。本周还有另一个也在使用机器学习来预测分子特性。因此,这一特殊领域似乎终于突然起飞了。”
深度学习如何推进 H&E 幻灯片的自动化分析
使用机器学习来自动化分析的想法并不新鲜,但深度学习的最新进展给该领域带来了新的生命。具体来说,深度学习最近彻底改变了图像分析领域,允许机器识别图像中的模式——包括 H & E 污渍。
Heather 描述了深度学习如何使团队从手工制作机器学习功能(一个缓慢、不准确的过程)过渡到将完整图像输入深度学习算法,然后自动识别有意义的片段:
“我们之前在皮肤癌上使用了手工制作的特征。我们将分割单个的细胞和细胞核,描述它们的形状、结构、排列等等。我们用它来试图预测不同的样本是否有突变——但我们做不到。我们根本没有得到有统计学意义的结果。
深度学习已经出现,但工具包还没有出现。没有张量流;没有 PyTorch。根据我的经验,从手工制作的功能到深度学习是造成差异的原因。我猜其他队也是如此。”
让人工智能模型进入临床:三大挑战
尽管这些进步令人兴奋,但仍有许多挑战需要克服。Heather 提醒我们,她的 H&E 研究可能还需要数年时间才能被临床采用:
“使用深度学习来预测分子生物标志物在很大程度上仍处于研究领域。离临床使用还差得远。这需要做更多的工作。”
任何进步都可能是一步一步来的。希瑟认为第一个真正的用例可能是一个简单的筛选工具,以建议人类病理学家下一步该做什么:
“结果可能是,‘这很可能是 x 亚型,没有 y 突变。’由此,病理学家可以决定进行更详细的筛查,或者支付基因组测试的费用。”
Heather 概述了在 H&E 幻灯片上的深度学习被广泛应用于临床之前仍然需要克服的一些主要障碍。
挑战 1:使用不同的实验室设备获得稳定的结果
鲁棒性——以可预测的方式跨不同设置执行的能力——通常是机器学习管道的一个挑战。对于 H&E 载玻片来说尤其如此,因为用于创建数据的设备是高度敏感的,并且不总是在不同的实验室之间标准化。
任何在真实世界环境中对 H&E 幻灯片使用深度学习的解决方案都需要能够处理这些差异。目前,如果不同实验室的测量结果不一致,那么根据一套设备的数据训练的模型将无法理解不同实验室的数据。正如希瑟所解释的:
“算法需要是健壮的。它需要推广到不同的扫描仪、不同的显微镜和不同的染色技术。H & E 染色的一个问题是,如果由不同的实验室进行,染色的强度可能会略有不同。
随着时间的推移,污渍可能会褪色,如果用不同的扫描仪扫描,效果会有所不同。应对这些挑战是需要解决的事情,但这是一个不小的问题。”
挑战 2:在小数据集上使用深度学习算法
与 Heather 研究的气候变化数据集相比,H&E 数据集通常很小。这对机器学习来说是一个问题,机器学习通常依赖于庞大的数据集(数十万个样本)来找到可概括的模式。正如希瑟所说:
“我们正在处理更小的数据集。在某些情况下,数百名患者可能被视为“大型”数据集。1000 是可以的,但有时我们只能得到 1000。这与机器学习的其他应用领域截然不同。”
样本数量往往很少,每个单独的样本也很“广”(包含大量信息),这给构建可靠的深度学习算法的挑战增加了额外的一层复杂性。然而,希瑟对克服这一挑战持乐观态度:
“你可以用不同的算法。这些图像很大,所以我们在每个图像中有更多的图像块。对于训练一个 CNN ,其实很棒。有些事情我们需要以不同的方式来做——比如我们不能一次将整个图像通过 GPU。我们需要将它分成更小的块,有时需要二级分类器来整合来自更小块的预测或特征。
但是有解决方案,这是一个活跃的研究领域。”
挑战 3:建立具有所需专业知识的团队
使用机器学习和 H&E 幻灯片需要几个领域的深厚和广泛的专业知识。作为一名顾问,希瑟经常发现自己在填补空白,做从咨询工作到编码机器学习模型的一切事情。她描述了她所做的各种工作:
“一方面,我的角色可能纯粹是一名顾问。我每周会见我的客户一次,帮助他们解决他们的问题,提出建议,或者给他们指出已经存在的图书馆和研究。
或者我可能会替他们编码:实现一个概念证明或者帮助他们调试一些东西。或者在另一端,我可能会领导他们的机器学习工作。我可能是他们团队中第一个机器学习的人,让他们起来运行,帮助他们建立一个团队。”
希瑟专门帮助公司建立概念证明——他们的目标是最终解决方案的精简版本。然后,她将这些交给机器学习工程师团队,他们可以将它们转化为生产化的解决方案。她说,一个常见的挑战是,公司从学术界雇佣的人可能没有使用过真实世界的数据集:
“作为课程的一部分,他们在学习时使用了学术基准数据集,但他们通常没有真正处理过真实数据的挑战。真实数据是有噪声的。不平衡。标签可能是错误的;地面真理可能是错误的。”
寻找具有重叠经验的多学科专家是一项挑战。但正如希瑟所说,这是必要的:
“构建这些解决方案需要来自许多不同领域的人:不仅仅是机器学习,还有病理学、遗传学、统计学,根据你正在做的事情,可能还有许多其他领域。”
使用深度学习从 X 射线图像中检测由 NCOV-19 引起的肺炎
开发一个初步的诊断模型,有可能对抗疫情病毒
左图:新冠肺炎正面 x 光片。 右图:链球菌感染。(均以 CC-NC-SA 授权)。两幅图像都显示肺炎。什么能把他们区分开来?图像是作为正在建设的开源数据集的一部分收集的。
3 月 11 日,在本文撰写的四天前,世界卫生组织(W.H.O .)宣布冠状病毒疾病 2019 (NCOV-19)为疫情,其特征是这种新型冠状病毒在全球范围内快速和全球性传播。由于各国政府争相关闭边境,实施接触者追踪,并提高个人卫生意识,以努力控制病毒的传播,不幸的是,由于每个国家实施这些政策的标准不同,在开发和部署疫苗之前,预计病毒的传播仍会增加。
由于预计全球每天的实际病例将会增加,限制诊断的一个重要因素是病毒病理学测试的持续时间,这些测试通常在市中心的实验室进行,需要耗时的精确度。这导致了严重的问题,主要是携带者不能更早地被隔离,因此他们能够在无限制流动的关键时期感染更多的人。另一个问题是当前诊断程序的高成本大规模实施。可以说,最脆弱的是发展中国家偏远地区的人们,他们通常拥有较差的医疗保健和诊断服务。单一感染可能对这些社区有害,获得诊断至少会给他们对抗病毒的机会。
城市和农村地区的差别不仅仅在于人口数量,还在于获得医疗资源的机会。在疫情的这个时代,这种接触的缺乏可能是致命的。(左:高波 via Flickr 。右:danboardviaFlickr)(都被-NC-ND 2.0 授权为 CC)
王等人的一项新研究。艾尔。展示了在计算机断层扫描(CT)扫描中使用深度学习来扫描新冠肺炎的前景,并且它已经被推荐为预先存在的诊断系统的实用组件。该研究在 1,119 次 CT 扫描中使用了初始卷积神经网络(CNN)的迁移学习。该模型的内部和外部验证准确率分别为 89.5%和 79.3%。主要目标是允许模型提取新冠肺炎的放射性特征。
虽然这项研究在他们的模型上取得了惊人的准确性,但我决定使用不同的架构来训练和实现一个模型,希望提高准确性。我决定使用胸片图像(CXR)而不是 CT 扫描,原因有三:
- 对人们来说,接受 CXRs 比接受 CT 扫描更容易,尤其是在农村和偏远地区。还会有更多的潜在数据可用。
- 在放射科医生和医疗专业人员无法控制病毒的情况下(例如,如果他们自己生病),人工智能系统对于继续实施诊断至关重要。
- 当该模型生效时,它可以通过测试初始阴性病例来最小化现有基于实验室的诊断中的假 negatives⁴问题。
与作为诊断来源的 CT 扫描相比,使用 CXRs 的主要障碍是缺乏来自新冠肺炎的可以视觉验证的细节。很难看到新冠肺炎症状,如肺结节,这在 CT 扫描中很容易看到。我想测试一个有足够层数的模型是否能检测出质量较低但更实用的图像中的特征。因此,我的模型是一个关于 Resnet CNN 模型是否可以使用相对便宜的 cxr 有效检测新冠肺炎的概念验证。
新冠肺炎肺部扫描数据集目前是有限的,但我发现的最好的数据集,我用于这个项目,来自新冠肺炎开源数据集。它包括从公开可用的研究中废弃的新冠肺炎图像,以及不同肺炎引起的疾病(如 SARS、链球菌和肺囊虫)的肺部图像。如果你有任何知识库可以接受的合适的扫描图像,以及它们的引用和元数据, 请贡献 来建立数据集,以改善依赖它的人工智能系统。
我只训练我的模型查看 cxr 的后前视图(PA) ,这是最常见的 x 射线扫描类型。我在一台 Resnet 50 CNN model 上使用了迁移学习(我的损失在 Resnet 34 上的几个时代后爆发),我总共使用了 339 张图像进行训练和验证。所有的实现都是使用 fastai 和 Pytorch 完成的。
预期警告:由于缺乏新冠肺炎图像,数据严重失真。数据被随机分成 25%后拍摄。测试集从一开始就设置为 78。图片作者。
在撰写本文时,由于缺乏可用的公共数据,数据严重失真(新冠肺炎有 35 张图像,非新冠肺炎有 226 张图像,包括正常和患病肺的图像)。我决定将所有非新冠肺炎图像组合在一起,因为我只有不同疾病的稀疏图像。在将数据随机分成 25%之前,我继续使用来自这个 Kaggle 数据集的健康肺部的 x 射线图像来增加标记为“其他”的 x 射线扫描的大小。训练集由 196 幅图像组成,验证集由 65 幅图像组成,测试集由 78 幅图像组成(完全由额外的数据集组成)。已验证外部数据集不包含开源数据集中的任何重复图像。所有图像都被调整为 512 x 512 像素,因为根据我构建不同分类器的经验,它的性能优于 1024 x 1024 像素的图像。
左:第一次迭代的训练有两个时代。培训损失远远高于验证损失,这是不适应的明显迹象。右图:训练的第二次迭代。所有列都与左边的列相同。我运行了 30 个纪元。图片作者。
使用 fastai 的 fit_one_cycle 策略的实现运行最初的几个纪元表明,拟合不足从一开始就是一个问题。我决定增加历元的数量,并得到一个学习率的范围。在其余的训练中,我采用了一种积极的策略,即训练大量的周期,使用 fastai 的 **lrfinder,**重新调整学习率,然后继续训练更多的周期,直到训练损失下降到与验证损失相当。
图片作者。
最后,在几十次迭代之后,我设法将训练损失降低到与验证损失相似的水平,同时保持高精度。模型的最终内部验证准确率记录为 93.8%。
左图:模型在验证集上运行良好,有一些假阳性(FP)和假阴性(FN);右图:该模型在测试集上运行,fn 为零,但 FPs 为三。偏斜分布确实会影响真阳性的结果,需要更多的数据来验证。列表示与左混淆矩阵相同的标签。图片作者。
正如上面的混淆矩阵所示,该模型在验证集上的表现非常好,只有两种情况分别出现假阳性和假阴性。转到测试数据,在 78 个图像中,75 个被正确预测,这是 96.2%的外部验证准确性。虽然我们在测试数据中得到三个假阳性,但模型对所有这些情况的预测接近 50%。这表明它混淆了 x 射线图像的一些特征,但它并不能非常决定性地得出对新冠肺炎有利的结论。然而,当它预测出正确的阳性病例时,最令人惊讶的事情发生了。 该模型从未在这些图像上训练过,但仍然成功地以 99%的确定性预测出图像中的肺是新冠肺炎阳性。
这是深度学习模型在分类新冠肺炎扫描时前所未有的准确性,但这只是一个明显缺乏 x 射线数据的初步实验,并且尚未得到外部健康组织或专业人士的验证。我将在未来针对假阴性对模型进行调优,尽管我更关心假阳性,目前数据的稀缺阻止了进一步的调优。
同样,该模型是一个概念验证,但如果进一步开发,这将有巨大的潜力来解决现有的有限诊断范围和未来可能的人力短缺。它可以帮助阻止这种疫情的传播,并创造新的方法来防止未来的。
为此,我让我的代码、模型和结果可供研究人员使用。我计划在更多的数据公开后,通过在更平衡的数据集上测试来更新模型。我还计划实现一个 GRAD-CAM 来直观地查看模型关注的特性。我希望我的模型能够在外部得到验证,并且不仅仅是一个宠物项目,尤其是在这个非常紧急的时候。如果有任何问题,或者如果你能帮我联系上某个组织或专业人士,可以让从这个模式中受益并改进这个模式,请随时联系我。
- 我在训练后意识到,很大一部分训练和测试图像来自儿科患者,这可能会影响模型在成人扫描上的性能。但其性能仍需更多数据验证。该模型还可以通过未来的培训进行改进。
- 我发现其中一个新冠肺炎阳性图像也在训练集中,这意味着结果的准确性是无效的。然而,它仍然给出了其他三个新冠肺炎图像 99%的可能性。
- 最近,我用细菌性和病毒性肺炎的图片测试了这个模型。 我目前正在等待更多的数据来进一步训练它。
- ⁴ 梅奥诊所:假阴性新冠肺炎检测结果可能导致虚假安全感
- 注:我应该对阳性病例进行上采样,以解决这些图像的缺乏,我应该包括更多细菌性和病毒性肺炎的图像,以使其更加有力。如果你想改进这个模型,请这样做。
- 注意:由于研究新冠肺炎的放射性标志物是一个活跃的研究领域,还有许多有待发现,我提醒大家不要解读结果,该模型已经确定了新冠肺炎独有的正确特征。
使用深度学习来…生成双关语?
自然语言处理中最新方法的有趣应用,展示了该领域的发展历程。
自然语言处理(NLP)作为一个领域已经有了前所未有的发展(尤其是在过去两年中,由于 BERT 的出版)。虽然许多研究都集中在具有巨大影响的任务上(例如,问答、文本摘要),但我们应该始终记住,自然语言处理研究也有一些有趣的应用。比如生成双关语。
让我们深入研究一下何等人最近的一篇论文。这为双关语的生成提供了一个非常强大的模型。附言:如果你只是想看看这个模型最终产生的一些双关语,我已经在帖子的结尾包括了作者给出的双关语。
**数据集。**首先要做的事情。像任何深度学习问题一样,生成双关语显然需要一个不小的数据集。现在,就我个人而言,我甚至不认为双关语的数据集存在。无论如何,作者发现了一个很好的双关语集合:来自 2017 年 SemEval task 的双关语数据集,包含 1099 个不同的双关语。尽管这只是数据集的一小部分,但它足以训练出一个性能可接受的模型。
昨天我吞下了一些食用色素。医生说我没事,但是我感觉我好像里面染了一点。(“染”vs“死”)
如何生成双关语?因此,对于这项任务,作者决定将重点放在同音字双关上(上面给出的例子)。在同音双关语中,目标是用发音相同但意义不同的替代词(“染色”)来替换双关语(在本例中为“死亡”)。为了产生这些双关语,作者提出了 3 个主要步骤:
- 给定一个双关语,从预先存在的句子语料库中找到包含该词的句子。对于这一步,它就像通过 BookCorpus 进行基本搜索一样简单,这是一个流行的数据库,里面有成千上万本书的内容。从包含双关语的句子列表中,该模型选择双关语最接近句尾的句子(这是基于作者的假设,即如果双关语“惊喜”出现得更晚,双关语会更好)。
- 用同音异义词替换双关语(这样就产生了双关语)。下一步也是关键的一步是用同音词替换双关语。例如,这里有一些作者在他们的论文中给出的谐音:“和平”和“块”,“面粉”和“花”,“等待”和“重量”。
- **从第一个单词中选择一个,用另一个能让双关语有意义的单词替换它。**这可能是最重要的一步,因为双关语需要铺垫。以上面给出的例子为例:如果双关语是“昨天我吞下了一些鸡肉”,那就不好笑了。医生说我没事,但是我感觉好像里面染了一点。”如果没有早期提到的“食用色素”,这个双关语就没有什么意义,这也是这一步要解决的问题。通过包含早期的主题词,提供了一些上下文,这将设置后期双关词的揭示。为了做到这一点,作者使用了一个跳格模型来寻找与同音词相关的单词(例如,“屠夫”和“肉”,“船”和“费用”)。
下面是作者给出的这些步骤的示例演练:
产生双关语的主要步骤。注意,在这个例子中,“hare”是原始单词“hair”的同音字替换。图片来源于论文作者何等。
所以在这个例子中,第一步是找到一个包含单词“hair”的句子。该模型识别句子“该男子停下来理发。”接下来,原来的双关语(“头发”)被同音字(“野兔”)取代,产生了更新后的句子“那个人停下来剪了一只野兔。”但是这还没有意义,所以进入第三步。通过将“man”改为“greyhound”插入主题,并给出完整的双关语:
“灰狗停下来剪野兔.”’
LOL 好双关。
结果呢?为了了解他们的模型是多么的微不足道,作者召集了土耳其机械工人。对于 150 个不同的双关语对(一个来自人类,一个来自他们的模型),每个双关语要求五个工人从三个方面对双关语进行评级:
- 成功(是或否)。基本上不管给定的双关语实际上是不是一个双关语。
- 有趣(从 1 到 5 分)。不言自明:衡量一个双关语有多好笑。在这方面,如果双关语没有任何意义,员工可以表示“不适用”。
- 语法(从 1 到 5 分)。衡量一个双关语的语法程度。必须确保双关语语法正确!
事实证明,他们的模型相当不错——该模型在 10.7%的双关语的搞笑方面和 8%的双关语的语法方面超过了人类。考虑到生成双关语是一项相当困难的任务,我实际上对计算机能比人更好地生成一个双关语印象深刻,更不用说百分之十了。但无论如何,让我们看看模型错过了点。
模型所犯错误的分类。图片鸣谢论文原作者何等人。
该死,模特把双关语搞砸了!因此,基于作者的模型所犯错误的饼状图,看起来模型所犯的错误很大一部分来自于选择一个糟糕的双关语或不能选择一个相关的主题词来提供双关语的上下文。考虑到这些都是相当困难的问题(让我当场想出一个双关语,我可能会绞尽脑汁),对我来说,模型在这些问题上遇到麻烦是有道理的。
**结论。**因此,作者做了大量工作,提出了一个相对强大的生成双关语的模型。他们专注于通过用一个给定的句子替换两个单词来产生同音词双关语,其中一个是双关语/同音词,另一个是为双关语提供上下文。他们的结果非常好,并且在展示 NLP 方法的强大方面确实令人鼓舞。
展望未来,我相信研究人员将继续为他们的高性能算法寻找新的有趣的应用!
延伸阅读:
- 【何等原创论文】
- [这项工作的 GitHub 库](https://github. com/hhexiy/pungen.)
- 又一篇关于双关语生成的好论文。
- 什么让双关语变得有趣?
额外有趣的东西(报纸上的双关语列表)。[模型]表明作者明确提到双关语是由模型产生的。[人类]表明作者明确提到双关语是由人类产生的。否则我不确定是模型生成的双关语还是人为生成的双关语。
昨天我不小心吞下了一些食用色素。医生说我没事,但是我感觉我内心有点染(死)了。
老屠夫永远不会死,他们只会面对命运。
谈判者只是一个试图恢复正常生活的女人。
嗯,美食家做到了,他想,这最好是正确的。[型号]
为什么牛奶场会搅动?说得越少越好……[人类]
那是因为谈判者和平地把我的车还给了我。[型号]
生活是一个谜;在这里寻找遗失的和平(片)。[人类]
黄油想知道这两位姑娘是谁,圣粉(花)的新成员。[型号]
贝蒂·克罗克是一个面粉(花)孩子。[人类]
即使从外面看,我也能看出他已经瘦了一些。[型号]
耐心是一种等待的美德。[人类]
使用分布式机器学习对大数据进行高效建模
图像来源
随着分布式计算成为数据科学家越来越受欢迎的技能,在 AWS EMR 集群上运行 Apache Spark 逐渐成为业内处理大数据的常用方式。
PySpark API 允许我们轻松地用 Python 编写 Spark,同时让 Spark 在后台并行计算数据。在本文中,我们将使用来自 Kaggle 的 4g 旧金山自行车共享数据集来实时模拟共享自行车的可用性。
这篇文章将涵盖:
- AWS EMR 或本地环境上的火花初始化
- 用 Spark 和 Plotly 进行探索性数据分析
- 使用 Spark SQL 对数据进行预处理
- 建模:在 Spark ML 中使用随机 Fores 回归器
- 为优化运行时间配置您的 AWS EMR 集群
1.AWS EMR 或本地环境上的火花初始化
要使用 spark,我们可以在 AWS EMR 集群上运行它,或者如果您只是想试用它,也可以在您本地的 Jupiter 笔记本上运行它。有很多关于如何在 AWS EMR 上设置你的笔记本以使用 PySpark 的文章,比如这篇。EMR 集群配置也将在很大程度上影响您的运行时,我将在最后一部分提到这一点。
2。探索性数据分析
为了预处理数据,我将使用 Spark RDD 操作来执行探索性的数据分析和可视化。
温度对订户的影响比对非订户的影响小
其余的 Spark 预处理代码和 Plotly 可视化代码可以在 Github repo 上找到,但这里是我们最初探索性分析的图表。
用户倾向于在工作日更频繁地使用共享自行车。
非订户每次出行使用自行车的时间要长得多,这可能是因为订户大多是通勤者,而不是游客。
用户的行程持续时间范围较小,集中在 10 分钟左右。
显然,你可以把共享单车带上火车
最常见的出行路线是通勤者在办公室和地铁站之间使用它。
3。使用 Spark SQL 对数据进行预处理
正如我们在步骤 2 中看到的,使用火花 RDD 进行数据预处理可能很难理解或可视化。这里,我们将使用 Spark SQL,它允许我们从 RDD 创建数据帧。
为此,我们首先创建一个模式,并用列名、数据类型和 nullable 定义 StructField。或者,您也可以在 RDD 上使用 toDF() 让 Spark 推断模式。
weather_df(显示在 Jupyter 笔记本中)
然后,我们可以使用像 select()、agg()、join() 这样的函数来操作 Spark SQL 数据帧,就像处理熊猫数据帧一样。
经过更多的数据预处理后,我们为机器学习模型准备好了数据框架。我们希望通过以下功能预测特定时间内给定站点的可用自行车数量:
○车站信息
○天气状况
○一天的类型
○一天中的某个小时
○该地区的人口
4。建模:使用 Spark ML 中的随机森林回归器
a)特征工程
PySpark 支持许多常用函数来做特性工程,我们将使用 StringIndexer() 来对我们的分类变量 rain_identifier 执行标签编码。
在将特性转换成数字类型后,我们可以使用 VectorAssembler() 将所有特性组合成一个向量列。这对于训练 ML 模型很有用,因为您将在后面的 RandomForestRegressor 中看到,它只接受一个 inputCol 参数。
现在,DataFrame 将我们所有的特征转换成一个向量,还有我们的目标变量:可用自行车的数量。
b)创建模型和模型评估
创建训练和测试集时,调用**。cache()** 将数据帧缓存到内存中以加速训练过程。(这里有一个关于什么时候在 Spark 中使用缓存的更全面的阅读。)
c)模型调整
Spark ML 还支持使用 CrossValidator() 和 ParamGridBuilder() 进行交叉验证和超参数调优。
我也在全笔记本里玩过 Spark ML 的梯度提升回归器,决策树,线性回归,但是随机森林回归器在这些模型中表现最好。
**5。**配置您的 AWS EMR 集群以获得最佳运行时间
选择集群节点的实例类型和节点数量可能很棘手,这取决于数据的大小和代码所需的计算能力。根据 AWS 的文档,
计算密集型集群可能受益于在高 CPU 实例上运行,这些实例的 CPU 比 RAM 多。数据库和内存缓存应用程序可能会受益于在高内存实例上运行。
规划集群实例的一种方法是用一组有代表性的样本数据运行一个测试集群,并监视集群中节点的利用率。
为了进行简单的比较,我运行了同一个 jupyter 笔记本,其中包括这四个不同 EMR 集群的所有预处理和 ML 建模代码。
我们可以看到,增加节点数量(每个节点代表 EMR 集群下的一个 EC2 实例)并不一定意味着内存优化实例的运行时间更短。
摘要
希望本文对您使用 Spark 处理 Python 中的大数据和大规模运行机器学习模型有所帮助。Spark SQL 和基于数据帧的 Spark ML API 也省去了我们处理 Spark RDD 的麻烦,因为我们可以将数据存储在数据帧中。
如果你对 SF 共享自行车可用性建模的预测结果和关键发现感兴趣,这里有 Github repo 。也可以在 LinkedIn 上给我发消息。
这是三藩市大学 MSDS 697 与黛安·伍德布里奇教授的一个小组项目。小组成员是阿坎卡莎。、刘品言、孙清怡和林海洋。
在 python 中使用 Docopt,这是最用户友好的命令行解析库
来源:普里西拉·杜·普里兹, Unsplash
在每个项目的运行文件中,将参数解析成 python 文件是必不可少的。本文向您展示了 Docopt 的实践
当你处理一个有很多参数的文件时,总是在 python 文件中改变这些参数是很费时间的,而且对用户也不友好。已经实现了一些库来解决这个问题,称为命令行解析库。在 python 中,它们是三种不同的解决方案: Docopt 、 Argparse 和 Click 。
在本文中,我将深入探讨 Docopt 的使用,而不是 Argparse 或 Click。我做出这个选择是因为在使用了所有这三种方法之后,我意识到 Docopt 是迄今为止将参数解析到文件中最用户友好的方式。
我展示 Docopt 库的方式是向您展示一个带有 setup.py 和 requirements.txt 文件的 docopt.py 文件示例。
### docopt.py file '''
Usage:docopt.py square [options] [operation] [square-option]docopt.py rectangle [options] [operation] [triangle-option]operation:
--area=<bool> Calculate the area if the argument==True
--perimeter=<bool> Calculate the perimeter if the argument==Truesquare-option:
--edge=<float> Edge of the square. [default: 2]rectangle-option:
--height=<float> Height of the rectangle
--width=<float> Width of the rectangle '''
from docopt import docoptdef area(args):
"""
This method computes the area of a square or a rectangle
"""
if bool(args['square']):
if args['--edge']:
area = float(args['edge']) ** 2
else:
print("You must specify the edge")
elif bool(args['rectangle']):
if args['--height'] and args['--width']:
area = float(args['--height']) * float(args['--width'])
else:
print("You have forgotten to specify either the height or the width or both")
return area def perimeter(args):
"""
This method computes the perimeter of a square or a rectangle
"""
if bool(args['square']):
if args['--edge']:
perimeter = float(args['edge']) * 2
else:
print("You must specify the edge")
elif bool(args['rectangle']):
if args['--height'] and args['--width']:
perimeter = float(args['--height']) + float(args['--width'])
else:
print("You have forgotten to specify either the height or the width or both")
return perimeterdef main():
args = docopt(__doc__) if args['--area']:
area(args)
elif args['--perimeter']:
perimeter(args)
else:
print("command not found")if __name__=='__main__':
main()
它们是 docopt.py 文件中的两部分:
- 文档字符串中参数的定义
- 您想要/需要调用的不同方法
参数定义
在用法部分,你写下你的命令行在终端中的调用格式。因此,如果我想在边长为 5 的正方形上调用面积运算,我将执行以下操作。:
python -m docopt.py square --area=True --edge=5
如您所见,在“用法”部分,我们可以定义默认值,但是如果我们像使用“ — edge 参数那样在命令行中指定值,这些值将被覆盖。
所以让我们分解命令行:
- docopt.py :是您想要运行的 python 文件的名称
- 正方形或矩形是我们需要在命令行中给出的两个必要参数,以正确运行它
- 【运算】:由两个不同的参数组成,分别是“—面积”和“—周长”。对于这两个参数,我们需要在命令行中输入的值是一个布尔值。’ = < bool >'是可选的,但是给出了用户需要输入哪种元素的提示。
- 【方形选项】:由一个参数组成,默认值为
方法
在这一部分,我将通过’区’和’周边’的方法。但是在这样做之前,您注意到您需要导入 docopt 包,并且在此之前您需要安装它。然而,暂时忘记安装,我会给你一个干净的方法来做。
您会注意到,在这两种方法中,当我需要来自 docopt 的参数时,我需要将其转换为正确的类型。例如 args[‘square’]是一个布尔值,但我需要显式地将其转换为布尔值。
然后,计算面积和周长的方法是非常基本的方法,但是是可选的方法。
然而,在文件的末尾有两行是正确运行该文件所必需的:
if __name__=='__main__':
main()
现在我们已经创建了我们的 docopt.py 文件,我们可以创建一个 setup.py 文件,只要一个 requirements.txt 文件,使我们的项目更加用户友好。
一个 setup.py 文件是一个 python 文件,在其中你描述了你的模块向 Distutils 的分布,以便在你的模块上操作的各种命令做正确的事情。在这个文件中,您可以指定要运行项目中的哪些文件,以及运行项目所需的哪些包。
在进入 setup.py 文件之前,您的项目中需要有以下架构:
/docopt-demo
docopt.py
requirements.txt
setup.py
这里有一个关于这个项目的 setup.py 文件的例子:
### setup.py file from setuptools import setupwith open("requirements.txt", "r") as fh:
requirements = fh.readlines()setup(
name = 'docopt-demo',
author = 'JonathanL',
description = 'Example of the setup file for the docopt demo',
version = '0.1.0',
packages = ['docopt-demo'],
install_requires = [req for req in requirements if req[:2] != "# "],
include_package_data=True,
entry_points = {
'console_scripts': [
'docopt = docopt-demo.docopt:main'
]
}
)
这是 requirements.txt 文件:
# Argument parser packagesdocopt==0.6.2
解释 setup.py 文件
你必须做的第一个元素是从 setuptools 中导入设置,但是这个包是 python 自带的,所以如果你的电脑上有 python,它就会存在。
然后,您需要创建一个所有必需包的列表,您将在安装模块的 install_requires 参数中解析这些包。
在设置模块中,您需要了解一些要素:
- 包:应该和你所在的文件夹同名,所以对我们来说是‘doc opt-demo’
- 在’ console_script 中,您可以为特定文件指定快捷方式。在我们的例子中,不是在终端中键入:
python -m docopt square --area=True --edge=5
我们只需要输入:
docopt square --area=True --edge=5
事实上,我们将单词’ docopt '与操作’doc opt-demo . doc opt:main’相关联,该操作启动 docopt.py 文件中的主函数。
解释要求. txt 文件
在这个文件中,您指定了正确运行项目所需安装的所有包。
您有两种选择来指定版本:
- 要么你告诉确切的版本,你想使用,你会使用’ == '符号
- 或者您也可以使用像’ > = '这样的操作符来指定包的旧版本
结论
在本文中,我将展示如何使用 Docopt python 包,这是一种将参数解析到文件中的非常好的方法。
我希望你在这篇文章中找到了你来这里的目的,并在接下来的图像识别之旅中与我在一起!
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名灵媒成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你使用[我的链接](http://If you enjoy reading stories like these and want to support me as a writer consider signing up to become a Medium member. It’s $5 a month, giving you unlimited access to stories on Medium. If you sign up using my link, I will earn a small commission and you will still pay $5. Thank you !! https://medium.com/@jonathan_leban/membership)注册,我将赚取一小笔佣金,你仍需支付 5 美元。谢谢大家!!
阅读乔纳森·莱班的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
medium.com](https://medium.com/@jonathan_leban/membership)
PS:我目前是 Unity Technologies 的机器学习工程师,刚刚从加州大学伯克利分校的工程硕士毕业,如果你想讨论这个话题,请随时联系我。 这里的 是我的邮箱。
使用实验数据的点状图
这篇文章将展示如何用点状图以一种通俗易懂的方式来解释你的实验发现
数字实验一直是科学领域的事情。尽管实验分析师可能会花大量时间充实假设,并与利益相关者一起构思,但正确地完成实验需要对显著性测试有一个核心和基本的理解。此外,实验的结果必须以一种不会使我们合作的利益相关者困惑——并随之脱离——的方式进行翻译。这时,适当设计的数据可视化是必不可少的。
当我们试图接受或拒绝一个使用多个成功指标的假设时,事情会变得混乱。在我们的实验中,对于给定的变体,我们可能有几个想要评估的参数,但是由于缩放和/或标准化问题,将这些参数呈现在同一个图上可能会有问题。此外,我们可能设计出来的东西可能只有数据人能够理解,而当我们希望在一个组织中向具有不同技术能力的人分享发现时,这远不是更好的选择。一个很好的例子是直方图,它显示了给定指标下我们的实验桶的重叠分布。对我来说,这是一种很好的方式来说明独立集团之间的差异,但我被告知,它们对股东并不友好:
使用这种可视化的另一个缺点是,它将只代表一个度量的分布,当我们可能试图用多个性能测量的结果来评估我们的假设时。当然,我们可以应用一个方面并显示多个发行版,但是这会变得很混乱,这不是我们想要传播给大众的格式。
图一。马特·克鲁克的新冠肺炎症状分析的点状图
现在,虽然这些图是在考虑 Covid 的情况下设计的,但在对最近结束的实验进行事后分析时,它们具有很好的实用性。当一个实验暂停时,您可能希望确定在给定的桶中有多少比例的用户触发了某些事件,并在实验条件之间比较这些比例。这些点状图是向利益相关者展示这些信息一种很好的方式。我将分解下面的过程,但在此之前,你需要克隆 中型点阴谋 Github 回购。如果你创建一个新的 Jupyter 笔记本,你可以使用下面的代码;确保您已经将它保存在与克隆的存储库相同的工作目录中。
首先导入必要的包进行分析:
%load_ext autoreload
%autoreload 2import pandas as pd
import os
现在创建一个字典,其中包含您要查看的事件的类,对于这个示例,我们将使用:’ signed_in_page_view ‘,’ signed_out_page_view ‘,’ page_view ‘和’ not _ fire '。字典将以类名作为键,并记录触发这些事件的人数。请注意,在激发的事件和事件量方面,您的实验将与此示例不同。以下数据是我们实验的变体 A(对照)的数据:
event_dict_control = {}event_dict_control['signed_in_page_view'] = 2000
event_dict_control['signed_out_page_view'] = 45000
event_dict_control['page_view'] = 45000
event_dict_control['didnt_fire'] = 8000
对变量数据执行相同的操作:
event_dict_variant = {}event_dict_variant['signed_in_page_view'] = 8000
event_dict_variant['signed_out_page_view'] = 35000
event_dict_variant['page_view'] = 75000
event_dict_variant['didnt_fire'] = 10000
查看两个词典:
event_dict_control>>> {'signed_in_page_view': 2000,
'signed_out_page_view': 45000,
'page_view': 45000,
'didnt_fire': 8000}event_dict_variant>>> {'signed_in_page_view': 8000,
'signed_out_page_view': 35000,
'page_view': 75000,
'didnt_fire': 5000}
对课程进行排序:
class_labels = [‘signed_in_page_view’, ‘signed_out_page_view’,
‘page_view’, ‘didnt_fire’]
从 Github 文件夹中的相对路径位置导入点图包:
cwd = os.getcwd()
os.chdir('../src')
from dot_plot_code import create_dot_plot
os.chdir(cwd)
我们现在将渲染我们的标记点图,用于两个对照:
color_dict = {
‘signed_in_page_view’: ‘purple’,
‘signed_out_page_view’: ‘lightblue’,
‘didnt_fire’: ‘pink’,
‘page_view’: ‘grey’}dotplot_figure = create_dot_plot(
class_labels=class_labels,
data_dict=event_dict_control,
color_dict=color_dict,
reversed_rows=[0],
ignore_labels=False)
和变体:
color_dict = {
‘signed_in_page_view’: ‘purple’,
‘signed_out_page_view’: ‘lightblue’,
‘didnt_fire’: ‘pink’,
‘page_view’: ‘grey’}dotplot_figure = create_dot_plot(
class_labels=class_labels,
data_dict=event_dict_variant,
color_dict=color_dict,
reversed_rows=[0],
ignore_labels=False)
现在,我们已经成功地绘制了两种实验条件下每 100 人的事件分布图。由此我们可以总结如下:
在控制条件下,每 100 人中触发某个事件的用户数量。
在实验条件下,每 100 人中触发某个事件的用户数量。
可以看出,尽管相同比例的用户根本不触发事件,但在我们的实验条件下(变体),更多的人触发了页面视图事件,这表明我们的变体之间存在变化。类似地,在登录页面视图中有一个明显的差异,这表明我们的实验条件导致更多的用户在整个实验中登录。
当然,这些图表和汇总统计数据不能用来替代显著性检验或其他方法,它们只是一种向利益相关者解释我们实验条件差异的可翻译的方法。理想的情况是结合/在执行方差分析或任何相关的实验显著性测试之后使用这些点状图。此外,通过一个简单的图表,我们可以查看一个存储桶在多个事件中的表现,当考虑多个成功指标时,这为我们提供了一个更加综合的实验功效视图。
用 dotenv 隐藏 Python 中的敏感信息
教程| Python | dotenv
隐藏您的密码和 API 令牌,使您的代码更加安全
一只藏起来的猫,就像你的证件一样!|照片由托马斯·博曼斯在 Unsplash 拍摄
在之前的帖子中,我在脚本中留下了一个不和谐令牌。这不是理想的编码方式,尤其是如果我不想每次共享代码时都重新生成令牌的话。在您的数据科学和编程项目中使用凭证和 API 令牌是不可避免的,但是暴露它们是可以避免的。使用dotenv
python 模块可以帮助保护这些敏感信息免受窥探。
在之前的一篇文章中,我使用 python 中的 Discord.py 创建了一个 Discord 机器人。这个例子将在这里继续,但同样的想法可以用在任何使用敏感凭证或 API 令牌的代码中
[## 你好世界!如何使用 Discord.py 制作一个简单的不和谐机器人
使用 Python 在短短 30 分钟内运行您自己的不和谐机器人!
medium.com](https://medium.com/python-in-plain-english/hello-world-how-to-make-a-simple-discord-bot-using-discord-py-c532611681ba)
问题是
您已经完成了全新的 Discord bot,并希望与全世界分享您的代码。很自然,你把它上传到 GitHub,然后把链接发出去。很快机器人停止工作。为什么?您在代码中留下了 API 令牌,有人接管了您的 bot。
您将 API 令牌留在了代码中,有人接管了您的 bot
API 令牌为控制应用程序提供了巨大的能力。对于某些 API,使用情况会受到监控,甚至会对您的使用进行计费!幸运的是,Discord 不会向您收费,但是在您的编码项目中使用凭据时,您仍然需要保证它们的安全。
解决方案
输入环境变量。Jim Medlock 在他的环境变量介绍和如何使用它们中对它们有很好的解释。在那篇文章中,他说:
环境变量 是一个变量,其值在程序外部设置,通常通过操作系统或微服务内置的功能来设置。环境变量由一个名称/值对组成,可以创建任意数量的名称/值对,并在某个时间点提供引用。
我们将使用它们在 GitHub 提交中安全地保留我们的不和谐令牌。同样的过程也可以用于添加到 bot 中的任何其他敏感信息,甚至是您正在处理的其他项目。例如,如果你把你的用户名和密码放在机器人的代码中进行网页抓取,你会想用dotenv
来保证它的安全。
装置
让我们从安装模块开始。我们可以使用 python 的包安装程序来完成这项工作,通常称为 pip。
$ pip install python-dotenv
我已经装了,就是这么好!
。环境设置
现在让我们设置我们的.env
文件。该文件应始终位于项目文件夹的顶层文件夹或根目录中。在那个位置创建一个新的文本文件,将其重命名为.env
。Windows 将要求您确认是否要更改文件类型。单击是。
如果 Windows 不让你命名。env,改为命名为 discord.env
接下来,用文本编辑器打开.env
文件。我更喜欢 Atom 或者 Notepad++ ,但是 Notepad 也可以。按照约定,环境变量全部大写,单词之间用下划线隔开。我们可以将我们的命名为 TUTORIAL_BOT_TOKEN 并粘贴我们的令牌。保存并关闭文件。
TUTORIAL_BOT_TOKEN=NzUzNzU3Mjg0NTI1NTM5MzQ4.X1q1LA.4xD7lSQrN0EqJivrrogLUScNdfY
添加到。gitignore
现在我的假设是你正在使用 GitHub 作为你的数据科学项目的版本控制。如果你不是,你应该是!如果出现问题,这是回滚代码更改的好方法。GitHub 还支持与其他人的协作。你不希望他们也有你的令牌,所以我们有一些工作要做。如果您没有为项目设置存储库或者不知道如何设置,不要担心,我将在另一篇文章中讨论这个问题。您可以安全地进入下一部分。
GitHub 将保存对代码的所有修改,但是我们需要排除.env
文件,因为它包含了我们的令牌。为了确保.env
文件没有被提交到存储库中,打开项目文件夹中的.gitignore
文件并添加下面一行。
*.env
添加这一行将忽略我们之前创建的.env
文件。通过使用星号(*),我们排除了顶级文件夹或根目录中扩展名为.env
的所有文件提交到存储库。
加载环境变量
现在我们有了包含我们敏感信息的.env
文件,它不会被提交到您的存储库中,我们需要在我们的 python 脚本中实际使用它。
首先我们需要加载dotenv
和os
模块。对于这个例子,我们可以将它们添加到我的 Hello World Discord.py 教程的脚本中。
# Imports
from dotenv import load_dotenv
import os
load_dotenv
将用于将.env
文件加载到环境变量中。os
将用于引用代码中的那些变量。先说load_dotenv
。这将加载。环境文件。
# Credentials
load_dotenv('.env')
要使用环境变量,我们可以在 bot 脚本末尾的client.run
命令中使用os.getenv
。
client.run(os.getenv('TUTORIAL_BOT_TOKEN'))
测试代码
现在一切都设置好了,在命令行中导航到 bot 脚本位置,运行脚本来启动 bot。
$ cd path\to\bot
$ python 02_Discord_dotenv.py
您将看到机器人名称和 ID 打印到控制台上。为了彻底起见,请使用 bot 进入服务器并测试!helloworld 命令,应该可以完美运行!
不出所料,该命令工作正常!
结束语
dotenv
模块是隐藏敏感信息的好方法。它不必局限于与不和谐机器人一起使用。任何代码使用登录凭证、数据库连接信息、API 令牌或任何您不想与外界共享的东西的地方,请将其放在您的。环境文件。
要了解更多关于dotenv
python 模块的信息,请查看 GitHub 知识库,其中包含更多关于该模块的文档。
寻找更多的项目创意?
看看我的另一篇文章,帮助你产生一些想法,在那里你可以应用这里讨论的概念。让您的项目在 GitHub 上脱颖而出,同时保证您的证书安全!
提高您的数据科学项目并获得关注的简单步骤
towardsdatascience.com](/6-ways-to-make-your-data-science-projects-stand-out-1eca46f5f76f) [## 通过我的推荐链接加入 Medium-Drew Seewald
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
realdrewdata.medium.com](https://realdrewdata.medium.com/membership)
用 dotenv 隐藏 R 中的敏感信息
教程| R | dotenv
隐藏您的密码和 API 令牌,使您的共享代码更加安全
一只藏起来的猫,就像你的证件一样!|图片由 Thomas Bormans 在 Unsplash 上拍摄
通常,当学习一个新的包或测试一些代码时,我会发现自己在代码中用纯文本编写了我的凭证和 API 令牌。这不是理想的编码方式,尤其是如果我不想每次共享代码时都重新生成令牌和更改密码。在您的数据科学和编程项目中使用凭证和 API 令牌是不可避免的,但是暴露它们是可以避免的。在 R 中使用dotenv
包是保护这些敏感信息免受窥探的一种方式。
问题是
在之前的一个项目中,我使用了[mapsapi](https://cran.rstudio.com/web/packages/mapsapi/vignettes/intro.html)
包来访问谷歌地图 API 。我有一个包含兴趣点、城市、州和国家的数据框,我想对其进行地理编码,或者获取其经度和纬度。为了从 Google Maps API 获取这些信息,我需要一个 API 令牌。这个 API 令牌是特定于我的,对于更高数量的 API 调用,您需要付费。
因此,假设您创建了一个类似的项目,并将其上传到 GitHub 。你很快就会看到你的信用卡上有一大笔费用。为什么?您将 API 令牌留在了代码中,有人滥用了您对 API 的访问,并对您的帐户收取了高额费用。
您将 API 令牌留在了代码中,有人对您的帐户收取了高额费用
API 令牌为控制应用程序提供了巨大的能力。对于某些 API,使用情况会受到监控,您将为您的使用付费!记住这一点,在您的编码项目中使用凭据时,确保它们的安全是非常重要的。
一个解决方案
输入环境变量。 Jim Medlock 在他的环境变量介绍和如何使用它们中对它们有很好的解释。在那篇文章中,他说:
一个 环境变量 是一个变量,它的值是在程序之外设置的,通常是通过操作系统或微服务内置的功能来设置的。环境变量由一个名称/值对组成,可以创建任意数量的名称/值对,并在某个时间点提供引用。
在这里,我将使用它们来使我的 Google Maps API 令牌安全地远离我的 GitHub 提交。同样的过程也可以用于添加到代码中的任何其他敏感信息。例如,如果您需要使用您的用户名或密码来访问一个数据库,您会想要使用类似于dotenv
包的东西来保证它的安全。
注意:我说解决方案是因为做任何事情都有多种方法。这是我发现对我的项目简单而有用的一个
在开始之前
在我们开始之前,先快速浏览一下我们将使用环境变量改进的代码片段。它加载所需的包,以纯文本的形式定义 API 键,并向 API 发送一个针对places
字符向量中每个位置的请求。
library(mapsapi)G_API_KEY <- "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"geo_obs <- mp_geocode(places, key=G_API_KEY)
我们的目标是消除在这个脚本中保留令牌的需要,但是仍然能够在我们的代码中使用它。
装置
让我们从安装包开始。我们可以使用install.packages()
功能来实现。
> install.packages("dotenv")
。环境设置
现在让我们设置我们的.env
文件。该文件应始终位于项目的顶层文件夹或根目录中。在那个位置创建一个新的文本文件,将其重命名为.env
。Windows 将要求您确认是否要更改文件类型。单击是。
如果 Windows 不让你命名。env,将其命名为 cred.env,类似于
接下来,用文本编辑器打开.env
文件。我更喜欢 Atom 或者 Notepad++ ,但是 Notepad 也可以。按照惯例,环境变量都是大写的,单词之间用下划线隔开。我们可以将我们的命名为 GOOGLE_MAPS_API 并粘贴我们的令牌。保存并关闭文件。
GOOGLE_MAPS_API=XXXXXXXXXXXXXXXXXXXXXXXXXXXX
注意:确保在这个文件的末尾创建一个空行,否则 R 在处理这个文件时会有问题
添加到。gitignore
现在这里的假设是,您在数据科学项目中使用 GitHub 进行版本控制。如果你不是,你应该是!如果出现问题,这是回滚代码更改的好方法。GitHub 还支持与其他人的协作。你不希望他们也有你的令牌,所以我们有一些工作要做。如果您没有为项目设置存储库或者不知道如何设置,不要担心,我将在另一篇文章中讨论这个问题。您可以安全地进入下一部分。
GitHub 将保存对代码的所有更改,但是我们需要排除.env
文件,因为它包含我们的令牌。为了确保.env
文件没有被提交到存储库中,打开项目文件夹中的.gitignore
文件并添加下面一行。
*.env
添加这一行将忽略我们之前创建的.env
文件。通过使用星号(*),我们排除了顶级文件夹或根目录中扩展名为.env
的所有文件提交到存储库。
加载环境变量
现在我们有了包含敏感信息的.env
文件,它不会被提交到我们的存储库中,我们需要在 R 脚本中实际使用它。
首先我们需要加载dotenv
包。将此添加到示例脚本中。
library(dotenv)
load_dot_env()
函数将用于将.env
文件加载到环境变量中。该函数将在当前工作目录中寻找.env
文件。
load_dot_env()
如果您必须将文件命名为除.env
之外的名称,函数调用应该如下所示,用您的文件名替换cred.env
。
load_dot_env("cred.env")
为了在我们的脚本中使用环境变量,我们可以在我们的mp_geocode()
函数中使用Sys.getenv()
函数(或者在任何使用环境变量的地方)。
geo_obs <- mp_geocode(places, key=Sys.getenv("GOOGLE_MAPS_API"))
最后要做的事情是删除具有纯文本 API 键的行。之后,最终的脚本将如下所示:
library(mapsapi)
library(dotenv)load_dot_env()geo_obs <- mp_geocode(places, key=Sys.getenv("GOOGLE_MAPS_API"))
结束语
这个包是隐藏敏感信息的好方法。它不必局限于与 Google Maps API 一起使用。任何代码使用登录凭证、数据库连接信息、API 令牌或任何您不想与外界共享的东西的地方,将其放在您的.env
文件中。
关于dotenv
R 包的更多信息,请查看该包的 GitHub 库。它包含了更多关于这个包的文档。
[## 通过我的推荐链接加入 Medium-Drew Seewald
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
realdrewdata.medium.com](https://realdrewdata.medium.com/membership)
利用动态规划帮助特朗普赢得选举
由 Pixabay 上的 fajarbudi86 拍摄的照片
Python 中优化选举宣传的动态规划
说明:2020 年美国大选在本文中仅作为背景。这个故事意在向你展示计算机编程中的这种思维方式。我既不是在表达我的政治观点,也不是在用任何观点进行说服。本文中使用的所有数据都是由假设构成的,无论如何都不一定反映事实。
在我写这篇文章的时候,乔·拜登已经稳操胜券了。我既不是美国人,也没有在美国生活,所以我不会在政治上对此发表评论,尽管我认为大多数阅读我文章的人可能会支持拜登。我哪里知道?就看看加州吧:)
好的。现在我们想象一下,我们被聘为特朗普选举团队的一员。假设离投票开始还有 10 天。大家都知道,最重要的是争取摇摆州。我不太喜欢政治,所以我对这方面的知识有限,但幸运的是,我从维基百科上得到了被认为是 2020 年美国大选摇摆州的 11 个州。
我们就随便编个数据,把所有的州,他们的选举人票数量,以及在这个州推广需要的天数一起放在一个字典里。
states_dict = [
{'name': 'Maine', 'votes': 5, 'days': 1},
{'name': 'Nevada', 'votes': 6, 'days': 1},
{'name': 'Minnesota', 'votes': 10, 'days': 2},
{'name': 'New Hampshire', 'votes': 4, 'days': 1},
{'name': 'Michigan', 'votes': 16, 'days': 3},
{'name': 'Pennsylvania', 'votes': 20, 'days': 4},
{'name': 'Wisconsin', 'votes': 10, 'days': 2},
{'name': 'Florida', 'votes': 29, 'days': 5},
{'name': 'Arizona', 'votes': 11, 'days': 2},
{'name': 'North Carolina', 'votes': 15, 'days': 3},
{'name': 'Georgia', 'votes': 16, 'days': 3},
]
假设是,如果我们在相应的州度过该天数,我们将赢得该州。
问题是川普在最后 10 天能拿到多少选举人票最多?所以,如果我们把所有红色州的选票加起来,我们就能估计出特朗普最终会有多少张选举人票。
有几种方法可以解决这个问题。最直观的方式大概就是递归函数了。然而,它绝对不是最好的解决方案。
在本文中,我将提供递归解决方案和动态规划解决方案。将给出解释来说明为什么动态规划比前者更先进。
递归解
递归解决方案的思想相对简单。也就是说,对于每个州,我们有两个选择——去或不去。
因此,函数将是关于
在接下来的几天里从其他州获得最多的选举人票
如果我们选择去这个州,我们将在剩下的几天里得到选举人票+其他州的最高选举人票。否则,我们应该在接下来的几天里从其他州退回最多的选举人票。
代码如下:
为了防止 gist 有时不能很好地工作,我也将代码简单地发布如下:
def max_vote_recursive(states, days_left, index):
# Terminating conditions
if len(states) == 0 or index >= len(states) or days_left <= 0:
return 0# If we have enough days, go to this state
votes_if_go = 0
if states[index]['days'] <= days_left:
votes_if_go = states[index]['votes'] + max_vote_recursive(states, days_left - states[index]['days'], index + 1)# If we don't go to this state
votes_if_not_go = max_vote_recursive(states, days_left, index + 1) return max(votes_if_go, votes_if_not_go)
如果我们运行函数为max_vote_recursive(states_dict, 10, 0)
,我们应该得到 56 张选举人票的结果。
为什么我说这个递归函数不够高效?我可以直观地展示给你看。
我所做的是将最后一个返回语句return max(votes_if_go, votes_if_not_go)
改为下面的代码片段。
if votes_if_go > votes_if_not_go:
print(f'There are {days_left} days left. We should go to {states[index]["name"]}')
return votes_if_go
else:
print(f'There are {days_left} days left. We should not go to {states[index]["name"]}')
return votes_if_not_go
因此,递归的每一步都将输出还剩多少天以及当前状态。更改之后,通过再次运行该函数,我们将得到如下所示的大量输出日志。
There are 2 days left. We should not go to Georgia
There are 2 days left. We should not go to North Carolina
There are 2 days left. We should go to Arizona
There are 2 days left. We should not go to Florida
There are 2 days left. We should not go to Wisconsin
There are 2 days left. We should not go to Pennsylvania
There are 1 days left. We should not go to Georgia
There are 1 days left. We should not go to North Carolina
There are 1 days left. We should not go to Arizona
There are 1 days left. We should not go to Florida
There are 1 days left. We should not go to Wisconsin
There are 1 days left. We should not go to Georgia
There are 1 days left. We should not go to North Carolina
...
请注意,在最初的几行中,我们已经可以找到一个重复的输出There are 1 days left. We should not go to Georgia
。我所做的是将输出放入一个数组,然后计算有多少重复的输出。排序后的结果如下:
('There are 1 days left. We should not go to Georgia', 74),
('There are 2 days left. We should not go to Georgia', 63),
('There are 3 days left. We should go to Georgia', 51),
('There are 1 days left. We should not go to North Carolina', 45),
('There are 2 days left. We should not go to North Carolina', 41),
('There are 4 days left. We should go to Georgia', 40),
('There are 3 days left. We should not go to North Carolina', 35),
('There are 4 days left. We should not go to North Carolina', 29),
('There are 5 days left. We should go to Georgia', 28),
('There are 1 days left. We should not go to Arizona', 24),
('There are 2 days left. We should go to Arizona', 23),
('There are 5 days left. We should not go to North Carolina', 22),
('There are 3 days left. We should not go to Arizona', 21),
('There are 6 days left. We should go to Georgia', 19),
('There are 4 days left. We should not go to Arizona', 18),
('There are 3 days left. We should not go to Florida', 16),
('There are 6 days left. We should go to North Carolina', 16),
('There are 2 days left. We should not go to Florida', 15),
('There are 4 days left. We should not go to Florida', 15),
('There are 5 days left. We should go to Arizona', 14),
('There are 1 days left. We should not go to Florida', 13),
('There are 5 days left. We should go to Florida', 13),
('There are 7 days left. We should go to Georgia', 12),
('There are 6 days left. We should not go to Arizona', 11),
('There are 6 days left. We should not go to Florida', 11),
('There are 7 days left. We should go to North Carolina', 11),
...
请注意,更多的行被截断,但仍然非常明显的是,一些“子问题”重复了很多次。比如下面的子问题,被“计算”了 74 次。
我们还有一天时间,我们应该/可以去乔治亚州吗?
幸运的是,我们只有 11 个摇摆州。如果将这类问题转移到另一个领域,我们可能会有数量巨大的节点。递归函数的缺点是重复计算子问题,这可能使问题在合理的硬件资源下无法解决。
动态规划解决方案
您可能已经意识到递归函数实际上是一种“自顶向下”的解决方案。相反,动态规划指的是以“自下而上”的结构解决问题的方式。它具有以下特点:
- 要解决的问题必须分成子问题。
- 在自下而上的推理过程中,我们必须确保每一步(子问题)在给定的条件下都是全局最优的。
我们先来看看代码。
万一要点加载有问题,这里是明码。请将它复制粘贴到您的文本编辑器中,以便进行漂亮的缩进。
def max_vote_dynamic_planning(states, total_days):
dp_matrix = [[0 for days_left in range(total_days + 1)] for index in range(len(states) + 1)]for index in range(1, len(states) + 1):
for days_left in range(1, total_days + 1):
if states[index-1]['days'] <= days_left: # If we have enough days left
votes_if_go = dp_matrix[index-1][days_left - states[index-1]['days']] + states[index-1]['votes']
votes_if_not_go = dp_matrix[index-1][days_left]
# Save the maximum votes into cache
dp_matrix[index][days_left] = max(votes_if_go, votes_if_not_go)
else: # We don't have any days left
dp_matrix[index][days_left] = dp_matrix[index-1][days_left]
return dp_matrix[-1][-1]max_vote_dynamic_planning(states_dict, 10)
然后矩阵计算如下,我们能得到的最大选举人票就是最后一个值——56。
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[0, 6, 11, 11, 11, 11, 11, 11, 11, 11, 11],
[0, 6, 11, 16, 21, 21, 21, 21, 21, 21, 21],
[0, 6, 11, 16, 21, 25, 25, 25, 25, 25, 25],
[0, 6, 11, 16, 22, 27, 32, 37, 41, 41, 41],
[0, 6, 11, 16, 22, 27, 32, 37, 42, 47, 52],
[0, 6, 11, 16, 22, 27, 32, 37, 42, 47, 52],
[0, 6, 11, 16, 22, 29, 35, 40, 45, 51, 56],
[0, 6, 11, 17, 22, 29, 35, 40, 46, 51, 56],
[0, 6, 11, 17, 22, 29, 35, 40, 46, 51, 56],
[0, 6, 11, 17, 22, 29, 35, 40, 46, 51, 56]]
在这里,想法如下:
- 初始化一个二维矩阵,使用状态数作为行,天数作为列。
- 从第一个状态(索引= 1)开始,剩余天数为 1。
- 如果我们去目前的州,我们应该把这个州的选举人票和剩下的日子里我们能得到的最大票数加起来。剩余天数应该是当前剩余天数减去我们在当前状态下需要提升的天数。
对于这一点,让我们假设我们正在计算第二个州,内华达州,还有 2 天(见dp_matrix[2][2]
)。如果我们选择去内华达州,我们会得到 6 张选举人票,但我们会用 1 天时间来得到它们。所以,我们还有 1 天时间。因为我们还剩 1 天,我们可以从前一个州缅因州得到的最多票数是 5 。因此,该步骤的优化结果是 11 票。
- 如果我们选择不去当前状态,那么我们应该只使用我们已经为先前状态优化的最大票数。
你迷惑了吗?:)我举个例子,动态规划是怎么知道什么时候不应该去状态的。
假设我们正在计算明尼苏达州,还有 2 天(dp_matrix[3][2]
)。如果我们选择去明尼苏达州,我们会得到1016】张选票。然而,在那之后,我们还剩下2–2 = 0天。另一方面,如果我们选择不去明尼苏达州,我们还有 2 天,我们可以从前两个州获得的最高票数是 11 。
因此,随着 for 循环的进行,对于每一个单独的循环,我们可以确保它是到目前为止优化的。这也是为什么你可以看到矩阵是水平和垂直排序的。因此,全局优化数应该在右下角,即 56。
简单的比较
我不应该只谈论动态规划有多有效。让我展示一些证据。
这是递归函数的运行时间。
这是一个动态规划解决方案。
在这种特殊情况下,动态规划大约快 10 倍。如果我们有更多的节点和可能性的问题,差异将是巨大的。
摘要
由 cocoparisienne 在 Pixabay 上拍摄的照片
在这篇文章中,我提出了一个在有限的几天内优化选举宣传的问题。我们可以很轻松的用递归函数解决问题,但是事实证明效率不高,因为有太多的子问题被计算了多次。
不是使用“自上而下”的方法(递归),而是使用“自下而上”的方法解决问题的动态规划。它从最小的子问题开始,并确保每一步都在当前条件下给出优化结果,因此下一步将建立在当前步骤的基础上。因此,它没有重复计算,算法复杂度小得多。
在给定的问题中,表明动态规划解比递归函数解快 10 倍。
一个小笑话
照片由 www_slon_pics 在 Pixabay 上拍摄
当你告诉特朗普我们可以用 10 天时间拿到 56 张选举人票的时候,他好像挺开心的。
他问你:“好。那么,我们的行程是什么?哪些是我们应该去的州。”
你:“嗯,我暂时没有那个信息。让我调整一下我的代码……”
特朗普:“不,让我来。相信我。没有人比我更了解 Python!”
😃
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@qiuyujx/membership)
如果你觉得我的文章有帮助,请考虑加入灵媒会员来支持我和成千上万的其他作家!(点击上面的链接)
使用 face-api.js 构建一个在人脸图像上叠加一个遮罩的应用程序
构建应用程序教程
下面是我如何制作一个应用程序,自动为你的个人资料照片添加遮罩。#马斯肯
来自 Pexels 的蒂姆·莫斯霍尔德的照片
你听说了吗? CDC 和医务总监现在建议并鼓励每个人在公共场合外出时戴口罩。不幸的是,戴口罩仍然会带来很多耻辱。“如果人们认为我生病了怎么办?”“如果他们冲我大喊大叫让我呆在家里怎么办?”这些想法会阻止一个人戴上面具,即使这有助于保护自己和他人。为了帮助宣传戴面具是明智和正确的事情,并让它变得足够普遍,从而成为一种新的社会规范,我决定制作一个应用程序,自动为你的个人资料照片添加面具。我是这样做的。
https://maskonme.herokuapp.com/待建 app 截图
首先,计划
我首先想到了应用程序的核心功能。这包括:
- 用户上传自己的照片或粘贴照片链接
- 网站检测照片中的人脸及其地标
- 网站在鼻子、嘴和下巴区域覆盖了一个面具
- 用户下载最终图像。
在谷歌了一番之后,我有了两个选择:1)使用 Flask & OpenCV 用 Python 开发一个应用,或者 2)使用 face-api.js 用 JavaScript 开发一个独立的应用。尽管我更喜欢 Python,但我还是决定使用 face-api.js & JavaScript,因为它可以在现代浏览器上运行,不需要后端服务器来承担繁重的工作。另外, Vincent Mühler 已经有了一个很棒的人脸和地标检测的例子,可以轻松覆盖步骤 1 和 2。
左)face-api.js 的人脸&地标检测 app 右)幽灵面具
接下来,我做了一个快速搜索,看看是否有任何在脸上覆盖面具的例子,并找到了使用 face-api.js 的https://ghost-masks . netlify . app,我认为只需要一些调整就可以完成第 3 步。
把所有的放在一起
在 faceLandmarkDetection 演示中,我花了一些时间来理解 face-api.js 是如何工作的。一些基础工作包括用<script src=”face-api.js”></script>
在 head 部分加载 face-api.js,并找到加载输入图像和绘图覆盖的部分。
<div style="position: relative" class="margin">
<img id="inputImg" src="" style="max-width: 800px;" />
<canvas id="overlay" />
</div>
此外,这里是调用faceapi
来检测输入图像inputImgEl
中的人脸并保存结果的部分。
async function **updateResults()** {
...
const results = await **faceapi.detectAllFaces**(inputImgEl, options).withFaceLandmarks()
...
}
我还在《幽灵面具》教程的 maskify.js 中指出了面具是如何被操纵和调整以放置在人脸之上的。它使用 face-api.js 中的方法来获取地标的坐标。例如,要得到鼻子,你可以运行const nose = landmarks.getNose();
或者使用const jawline = landmarks.getJawOutline();.
得到下颌线,见这里的原始代码。利用这个,我能够快速制作出一个原型,它将检测人脸,并用一个面具覆盖它。点击这里查看完整代码!
这个应用程序把一个面具放在你的脸上。申请可从 https://maskonme.herokuapp.com face-API . js 获得…
github.com](https://github.com/jcheong0428/maskon)
不幸的是,我很快意识到这个自动覆盖的面具不会是完美的。面部轻微的旋转和歪斜会导致稍微不准确的下颌线,这会使面具向某个方向倾斜太多。因此,我决定添加一些控件来移动遮罩。
还不错,但肯定可以使用一点手动调整。下颌线的估计并不是 100%准确。
事实证明,手动调整无论如何都是必要的,因为地标可能不会总是检测到下巴的整个轮廓。
因此,我决定添加按钮,这将改变小增量的面具的位置。
例如,我添加了一个向右移动蒙版的按钮
<button class=”waves-effect waves-light btn” onclick=”moveright_mask();”>Zoom In</button>
由函数定义
function moveright_mask() {
const myNode = document.getElementById("maskdiv").children;
for (var i = 0; i < myNode.length; i++) {
var tableChild = myNode[i];
// Do stuff
var left = parseFloat(tableChild.style.left)+2;
tableChild.style.left = `${left}px`;
};
}
这将更新maskdiv.
中的掩码
亲自尝试一下或观看视频:
后续步骤
这是一个非常有趣的项目,可以在疫情开始的时候分散我的注意力。当我建造这个的时候,我最终也找到了 https://socialdistancing.works/的号,它实际上做了同样的事情,除了它纯粹是手动调整。 MaskOnMe 的另一个很好的功能是它还可以同时检测多张脸,这样你就可以发布和每个戴面具的人的合影了!
当我有时间的时候,我的下一步是在一个 AR 版本上工作,其中一个将覆盖一个来自网络摄像头的面具!感谢您的阅读,如果您想了解更多关于人脸图像处理的内容,请随时查看我的其他帖子。
[## 如何从任何 Youtube 视频中提取面部表情、头部姿势和凝视
使用 Google Colab & OpenFace 从 Youtube 视频中提取面部特征的教程,无需安装一个…
towardsdatascience.com](/how-to-extract-facial-expressions-head-pose-and-gaze-from-any-youtube-video-2aa6590c2bb6) [## 量化时间序列数据之间同步性的四种方法
用于计算同步指标的样本代码和数据,包括皮尔逊相关、时滞交叉相关…
towardsdatascience.com](/four-ways-to-quantify-synchrony-between-time-series-data-b99136c4a9c9) [## 通过我的推荐链接加入 Medium 金贤昌博士
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
jinhyuncheong.medium.com](https://jinhyuncheong.medium.com/membership)
利用脸书的预言家预测超过 30000 件沃尔玛产品的销售(Kaggle 铜牌)
用脸书预言家预测沃尔玛的销售。亨特·哈里特在 Unsplash 上的照片
在这篇文章中,我将解释我是如何使用脸书的开源预测模型 Prophet 在 T4 M5 预测大赛上获得铜牌的。
什么是脸书先知?
很久以前我就想尝试一下脸书的先知。引用脸书的文档:“Prophet 是一种基于加法模型预测时间序列数据的程序,其中非线性趋势符合每年、每周和每天的季节性,加上假日效应。它最适用于具有强烈季节效应的时间序列和几个季节的历史数据。Prophet 对缺失数据和趋势变化具有稳健性,通常能很好地处理异常值。
简而言之,它是一个自动化的时间序列生成器。由于在这场 Kaggle 比赛中有超过 30,000 个时间序列,这个自动化方面真的很棒:数据太大了,无法单独查看每个时间序列。
如果您想跟随完整的笔记本,这里有两个链接:
卡格尔竞赛和数据
M5 预测竞赛为我们提供了沃尔玛的分层销售数据。目标是预测美国三个州(加利福尼亚州、德克萨斯州和威斯康星州)商店未来 28 天的日销售额。数据包括项目级别、部门、产品类别和商店详细信息。此外,我们还有解释变量,如价格、促销、星期几和特殊事件。
销售数据
我将销售数据放在每列一个产品和每行一天的格式中。
训练数据包含每个产品/商店组合的 30490 列,行是日期:
测试数据的格式与列车数据相同,但包含连续的 28 天:
在 Prophet 中添加假日和事件数据
我准备了假期和事件数据,以符合 Prophet 的假期论点。格式必须是固定列假日和 ds 的数据帧。在 ds 中,你给出了这个假期发生的日期的列表,包括过去和将来(直到你的预测发生)。
假日的 Kaggle 数据看起来像这样,每天一天的格式。
为了将该数据传递给 Prophet,必须对其进行重新格式化,以仅包含节假日,并为每个节假日列出该节假日发生的日期:
在 Prophet 中添加更多独立变量
还提供了价格数据。我没有把它包括在内,尽管在 Prophet 中添加更多数据是可能的。我相信它可以取得更好的结果,但由于时间限制,我没有尝试
脸书先知超参数
我用不同的方法对超参数做了两次尝试:第一次尝试使用默认的超参数,第二次尝试使用小的优化。我很想用更大的优化做第三次尝试,但是我没有计算时间来运行 30,000 次。
1.具有所有默认超参数的 Prophet
用 Prophet 运行 30,000 个时间序列花了我大量的时间(我有 6 个 Kaggle 笔记本,每个运行 5000 个时间序列,仍然花了我几个小时)。第一次提交使用了 Prophet 的所有默认设置。
如何使用 Prophet 的基础知识:
- 使用假期数据(以准备好的形式)初始化 Prophet
- 让先知适应训练数据
- 制作一个未来的数据框架
- 使用 Prophet 对未来数据框架进行预测
然后,我们可以将此应用于所有超过 30000 个时间序列,其中包括使用 imap,如下所示:
第一次提交给我的分数是 0.83(加权均方根误差)。虽然它在排行榜上是最差的 30%,但我对它的第一个结果很满意。
2.具有超参数调谐的 Prophet
我使用超参数调整进行了第二次提交。已经花了几个小时来运行默认值,并且有许多超参数和设置可以在 Prophet 中修复。
要在 Prophet 中调谐的超参数
经过一些尝试后,以下超参数似乎有所不同:
- 每周季节性的傅立叶顺序
- 月度季节性的傅立叶顺序
- 年度季节性的傅立叶顺序
- 季节性 _ 先验 _ 标度
- 假日 _ 先验 _ 标度
- 变点 _ 先验 _ 标度
对所有这些进行调优会使代码太慢。作为一个解决方案,我通过网格搜索只对 4 种不同的组合进行了调整,如下所示:
Prophet 中的交叉验证
Prophet 中有一种交叉验证的方法。然而,我更喜欢使用如下的列车验证测试方法:
- 拟合训练数据的所有超参数组合
- 测量验证数据的误差并选择最佳模型
- 使用最佳模型的超参数对添加到验证数据的训练数据重新拟合模型
- 使用此模型对测试数据进行预测
使用超参数构建一个模型的代码:
在一列上进行预测的代码,包括训练-验证-测试方法:
然后我使用和上面写的一样的 imap 方法来拟合所有的预测。
结论
网格搜索法让我有了显著的提高,新的分数是 0.68。这种方法用相对少的时间让我在比赛中获得了铜牌(5558 名中的第 501 名)。
记得看一下完整的笔记本:带默认值的 先知笔记本 和带超参数调优的 先知笔记本 。
当然还有改进的空间,但我已经很乐意分享我所能做到的。希望这对你也有用。感谢阅读,不要犹豫,继续关注更多!
使用 FaceNet 在 Android 设备上进行人脸识别
📱移动机器学习
利用 Android 的 FaceNet 和 Firebase MLKit 的强大功能。
随着对许多人进行即时识别和分类的需求增加,对人脸识别系统的需求与日俱增。无论是办公室的考勤系统,还是手机摄像头中的简单人脸检测器,人脸检测系统无处不在。对于边缘设备,这种需求甚至更大,因为它们有许多用途,如监控人群、机场乘客、公交车站等。
今天,我们将创建一个类似的人脸识别应用程序,完全从零开始。但是等等,有些特别的东西!
想象一下,你正在办公室里使用人脸检测系统。对于你办公室的 10 名员工来说,这个系统运行得非常好。但是,现在你的一个朋友来到了你的办公室。该系统可能无法识别您的朋友,因为它没有被编程这样做。所以,对于每一个新员工,你都需要修改系统并重新编程。
如果,
你不需要重新训练系统!为什么你不能保存这 10 名员工和你朋友的照片,而应用程序也能立即识别你的朋友呢?无需重新编写应用程序或任何其他系统,一切都可以在 Android 上运行!当你有新员工时,继续在单独的文件夹中添加他们的图像,应用程序就可以识别他们了。
直到故事结束,我们将创建这样一个应用程序!
GitHub 项目->
[## Shu bham 0204/face recognition _ With _ FaceNet _ Android
存储你想认识的人的图像,应用程序将使用这些图像对这些人进行分类。我们…
github.com](https://github.com/shubham0204/FaceRecognition_With_FaceNet_Android)
作者的项目/博客。
先决条件
在我们的应用程序中,我们将使用 CameraX、Firebase MLKit 和 TensorFlow Lite。如果你以前没有使用过这些库,一定要看看它们。
FaceNet 上的一点
1.将 Keras 模型转换为 TFLite 模型
FaceNet Keras 型号在n yoki-MTL/Keras-FaceNet回购上有售。下载完.h5
模型后,我们将使用tf.lite.TFLiteConverter
API 将我们的 Keras 模型转换成 TFLite 模型。
将 Keras 模型转换为 TFLite。
2.使用 CameraX 设置预览和 ImageAnalyser
为了实现一个实时的摄像机提要,我们使用 CameraX。我使用了官方文件中的代码。接下来,我们创建一个实现了ImageAnalysis
类的FrameAnalyser
类,它将帮助我们检索相机帧并对它们进行推理。
正在设置 FrameAnalyser 类。
我们所有的分类代码都将来自于analyze
方法。首先,使用 Firebase MLKit,我们将得到相机帧中所有面的边界框(一个Bitmap
对象)。我们将创建一个FirebaseVisionFaceDetector
,它在一个FirebaseVisionInputImage
对象上运行人脸检测模型。
实现 FirebaseVisionFaceDetector。
3.使用 FaceNet 生成人脸嵌入并进行比较。
首先,我们将使用我们的 FaceNet 模型生成人脸嵌入。在此之前,我们将创建一个助手类来处理 FaceNet 模型。这个助手类将,
- 使用我们从 Firebase MLKit 获得的边界框(如
Rect
)裁剪给定的相机帧。 - 使用标准化的像素值将此裁剪图像从
Bitmap
转换为ByteBuffer
。 - 最后,使用 TF Lite Android 库提供的
Interpreter
类将ByteBuffer
提供给我们的 FaceNet 模型。
在下面的代码片段中,可以看到封装了上述所有步骤的getFaceEmbedding()
方法。
为 FaceNet 实现一个助手类
现在,我们有了一个类,它将返回给定图像中所有人脸的 128 维嵌入。我们回到一个FrameAnalyser
的analyze()
方法。使用刚刚创建的 helper 类,我们将生成人脸嵌入,并将它们与我们已经拥有的一组嵌入进行比较。
在此之前,我们需要获得一组预定义的嵌入,对吗?这些嵌入指的是我们需要认识的人。因此,该应用程序将读取用户设备内部存储中的images
文件夹。如果用户想要识别两个用户,即 Rahul 和 Neeta ,那么他/她需要在images
文件夹中创建两个单独的目录。然后,他/她必须将 Rahul 和 Neeta 的图像放在各自的子目录中。
我们的目的是读取这些图像,并产生一个HashMap<String,FloatArray>
对象,其中键(String
)将主题的名字像拉胡尔或尼塔和值(FloatArray
)将相应的脸嵌入。通过查看下面的代码,您将对这个过程有所了解。
读取和生成设备存储器中存在的图像的嵌入。
下一步是将嵌入与合适的度量进行比较。我们可以使用 L2 范数或余弦相似性度量。使用metricToBeUsed
变量选择 L2 范数或余弦相似度。我们计算每张图片的分数。然后我们计算每个用户的平均分数。平均分最好的用户就是我们的产出。
来自 README.md 的快照。
然后将predictions
数组提供给boundingBoxOverlay
类,该类绘制边界框并显示标签。在BoundingBoxOverlay.kt
班。这里,我们使用两个Matrix
来转换输出并显示在屏幕上。对于前置摄像头,我们必须翻转边界框的坐标,否则我们将在覆盖图中看到边界框的镜像。
显示边界框和标签。
结果呢
我试着用这个应用程序识别杰夫·贝索斯和埃隆·马斯克的脸,
应用程序的运行。
而且,我已经把图像存储在我的内部存储器中,
文件结构
更多资源
结束了
我希望你喜欢这个故事。感谢阅读!
使用 faiss 在多维空间中搜索
如何围绕 faiss 构建服务并避免问题?
2019 年秋天, Avito 的自动审核团队推出了一项基于faiss 库的虚假广告检测服务。它帮助我们检测一个图像是否已经在另一个广告中使用过,即使它被以任何方式修改过——模糊、裁剪等。
我想谈谈我们在实施这项服务的过程中遇到的问题以及我们解决这些问题的方法。
在本文中,我将主要关注技术方面,假设您对多维空间中的搜索有所了解。
问题是
当我接到开发图像检索系统的任务时,我首先想到的是它的规格和局限性:
- 条目的数量。从一开始,我们有大约 1.5 亿个载体,现在有超过 2.4 亿个。
- 搜索时间限制。在我们的例子中,第 95 百分位为 300 毫秒。
- 内存限制。该指数必须足够小,以适应普通服务器,包括未来两年的预期增长。
- 可维护性。因为我们所有的服务都是在 Kubernetes 上实现的,所以我真的不想构建一个需要在物理硬件上运行的系统。
- 可以添加图像,但不能删除或修改。
我们系统的架构
因为我们需要足够快地在数亿个向量中搜索,穷举搜索不是一个选项——我们需要一个在 RAM 中的索引。为了适应那里的向量,它们需要被压缩。
一旦索引在 RAM 中,为了防止丢失,我们偶尔会制作备份副本并存储在外部。备份应该不需要将内存大小增加一倍,即使是暂时的,因为我们谈论的是几十千兆字节。
剩下的就是提供水平缩放。在我们的情况下,最简单的解决方案是相同的最终一致的索引。那么服务实例不需要知道彼此的存在,数据库将是唯一的同步点。
我们的服务是用 Python 3.7 编写的,PostgreSQL 用于存储向量,并且选择了一个 MinIO 作为备份存储。Faiss 作为一个索引库起着关键作用。
为了与外部世界进行交互,我们使用 avio http 框架,这是我们围绕 aiohttp 的内部包装器。
由于 asyncio 不能很好地处理我们备份所需的 fork 调用,并且在异步服务中调用长时间阻塞操作是不好的做法,所有与索引的交互都被转移到一个单独的进程中,我们使用多处理与它进行交互。管道。
选择索引结构
为了压缩,我们决定使用乘积量化。这种方法允许我们将原始向量压缩到 64 字节。
产品量化允许我们将向量分割成多个部分,并建立独立的聚类。这些数字来自克里斯·麦考密克的一篇文章
为了加快搜索过程,我们选择了基于倒排文件和 HNSW 的混合方法。
左图来自红点游戏,右图来自于的文章。a .马尔科夫,D. A .亚舒宁
结果,我们得到了一个索引结构,用 faiss 的术语来说就是用神秘的线来描述:IVF262144_HNSW32,PQ64。这意味着我们为 262144 个簇创建一个反向文件,其中最接近的簇将使用具有 32 个邻居的 HNSW 来选择,并且所有向量都使用乘积量化方法压缩到 64 字节。
这里有链接到 faiss 的开发者提供的一个索引选择指南和一个基准测试。
看基准测试结果要注意的:
- 对索引进行一次查询以搜索 10,000 个向量,并给出每个向量的计时。
- 显示了 16 个 OpenMP 线程的时间。如果只使用一个线程进行搜索,时间会长 16 倍左右,因为 faiss 库内部的并行性是由#pragma omp parallel for 提供的。
估计内存成本
用相对值来估计内存开销是最合适的——每个向量的字节数。通过这种方式,我们可以识别内存泄漏(如果发生的话),并轻松地将它们与插入导致的有机增长区分开来。
存储器主要用于存储向量,在 IVF262144_HNSW32,PQ64 的情况下,每个向量为 80 字节:
- 64 字节存储描述向量的代码。
- 8 个字节存储 id(存储为 int64)。
- 8 个字节来存储指向簇内向量的指针。
可以使用以下公式计算相对内存成本:
*int(faiss.get_mem_usage_kb() * 1024 / index.ntotal)*
预先计算的距离表会消耗一些内存,但是当估计的大小超过 2Gb 时,它不再自动计算。它的大小可以估计为 nlist × pq。M × pq.ksub × float。在我们的例子中,262,144 × 64 × 256 × 4 ≈ 17Gb,其中 pq。m 是乘积量化分量的数量,pq.ksub 是 256,因为可以用一个字节来描述这么多的簇。
***估算内存成本要注意什么。*以上数据仅为静态索引:如果添加和删除条目,内存消耗至少要乘以 2。我怀疑这是由于倒排文件中向量的动态内存分配,但我还没有找到支持这一假设的证据。如果你知道答案,请在下面的评论中分享。
推出服务后每个矢量的字节数的收敛:
如果您正在处理的问题不涉及删除和修改向量,您可以将它们中的大部分移动到静态索引归档中,只在较小的索引中插入新的向量,不时地将其与较大的索引合并,并向两个索引发送搜索查询。基本上,经典的信息检索方法。
查询并行化
尽管在 faiss 中使用了 OpenMP,但是发送到大型集群的任何长查询都会在相当长的时间内阻塞索引。为了避免系统不规则,我们使用ThreadPoolExecutor(faiss 是友好的,释放 GIL)。
nginx 原创图
为了让 faiss 在多线程环境中正确运行,需要做两件事。首先,不允许并行执行修改操作(添加、删除)和任何其他操作。其次,在执行读取查询期间限制 OpenMP 线程的数量,以使线程总数不超过指定的 CPU 内核限制。否则,性能损失是必然的。
把写负载和读负载分开最方便的方法是 RWlock ,它允许很多读者进入临界区,但只能有一个写者。对于 Python,我推荐这个包。使用 faiss.omp_set_num_threads 函数可以调整正在运行的 OpenMP 线程的数量。
为了获得最佳性能,以最大化每秒查询次数的方式选择每个线程的批处理大小是有意义的。我们使用的批量大小为 5。
值得注意的是,使用多线程时,内存消耗会略有增加。好像是个 bug(这里是各自的问题)。
有关多线程支持的更多信息,请查看这篇 faiss wiki 文章。
带宽和操作时间
新的向量被添加到数据库中,每个实例每 5 秒钟从数据库中独立地读取这些向量,并以 10,000 个为一批(或者自上次更新以来累积的更小的一批)插入到索引中。索引中的插入速度高到足以让数据库成为瓶颈:大约每分钟 80 万个向量。
当我们优化延迟时,一个 20 核实例能够处理单个图像的 20 个最近邻居的多达 150 rps 的搜索,如果我们优化吞吐量,则大约 550 qps。负载伸缩非常简单:只需添加更多的实例,因为它们之间互不了解。
索引备份
因为索引在 RAM 中,所以如果机器停机,它可能会丢失。为避免此问题,请定期将其转储到外部存储。为此,我们使用 MinIO。
最方便的方法是创建一个 fork 并使用 Copy-on-Write 来获得一个索引的副本,该副本在内存消耗和对操作时间的影响方面几乎是免费的。然后将该索引保存到磁盘并上传到存储器。这里,我建议从静态索引大小开始——在我们的例子中,每个向量大约 80 字节。
因此,在启动应用程序时,您只需要从存储库中加载最新的副本,并从数据库中插入缺失的数据。
使用 GPU
我们还没有在产品中使用 GPU 搜索,但我做了一些测量来了解操作时间的比率。
数据: SIFT1M ,维度 128。
查询:我们正在为 10,000 个具有不同 nprobe 值的向量寻找 100 个最近邻。
你要注意的:
- 索引必须完全适合 GPU 内存。虽然有很多方法可以使用多个 GPU 进行搜索,但我认为这不是最好的方法,因为这样的解决方案更难维护。
- 平面索引在 GPU 上运行得更快,但它们仅适用于相对少量的数据(在 128 的维度上多达数千万个向量)。
- 使用 PQ64 索引时,GPU 只有在轮询大量集群时才有优势。
结论
Faiss 可能是当今最好的近似搜索开源工具,但像任何复杂的工具一样,它需要时间来适应。
尽管 faiss 文档非常好,但是最好还是通过不时地检查代码来学习它。您还可以通过 issues 向开发人员发送问题。通常,他们的反应相当快。
如果你正在考虑在预定的机器上部署这种系统,我建议看看 JD.com 的 vearch 系统。他们已经完成了大部分的脏活累活,并把他们的解决方案开源,尽管文档仍然非常简陋。