AutoViz:自动化可视化的新工具
© tadamichi — Shutterstock
数据科学家的任务通常是通过海量数据存储来提供可行的见解。然后对这些见解进行分析,以识别与商业智能甚至人类行为相关的模式。然而,使用所有类型的优化和聪明的算法来构建数据查询和机器学习管道可能是一回事。能够将艰巨的数据收集和建模的结果传达给不熟悉数据处理的同事完全是另一回事。这就是数据可视化的用武之地。
在他的书《好图表》中,Scott Berinato 感叹道:“一个好的可视化可以比任何其他形式的交流更有力地传达信息和想法的本质和潜在影响。”我们许多人都熟悉这样一句谚语:“一幅画胜过千言万语。“同样,在数据科学中,提供强有力的季节性模式、与成功营销活动相关的鲜明趋势或需要解决的显著异常值的有效可视化非常重要。小型数据集的可视化很容易且非常有益,但对于包含数百个(如果不是数千个)变量的大型数据集来说,这几乎是不可能的,在这种情况下,我们必须决定从数据集中突出显示哪些最佳洞察。除此之外,数据科学家必须经常使用非标准化的可视化库,这需要相当多的编码来获得具有适当影响的视觉效果。幸运的是,有一种替代方法可以使用这种暴力方法创建可视化。最近,在一次由 AutoViz 的设计者和作者所做的演示中,我了解到了一个名为 AutoViz 的库(AutoViz 是“自动化可视化”的缩写)。
AutoViz:简介
AutoViz 解决了前面提到的在执行数据可视化工作时可能出现的许多挑战。可以使用一行代码调用该工具,方法是向它提供一个 pandas dataframe 对象或一个要导入的原始 csv 文件。
如果观察次数多,AutoViz 会随机抽取一个样本;同样,如果变量的数量很大(这可以由您决定), AutoViz 可以找到最重要的特性,并只使用那些自动选择的特性来绘制有影响力的可视化效果。用户只需向 AutoViz 传递一个参数,就可以设置样本行数和要可视化的最大特征数。AutoViz 能够适应任何数量的不同数据环境,例如回归、分类甚至时间序列数据。它的输出速度也非常快。
AutoViz 可以通过 4 个简单的步骤实现:
- 使用“pip install autoviz”安装
- 用“从 autoviz 导入。AutoViz_Class 导入 AutoViz_Class "
- 实例化一个类“AV = AutoViz_Class()”
- 使用我们的数据集在下面的行中运行一个实验:
AutoViz 的优势
使用 AutoViz 进行可视化有很多好处。该库非常容易理解,可以使用 verbose=1 或 2 标志设置为高度详细模式。XGBoost 模型被重复用于确定最一致的特征集,每次通过使用随机的特征集来确定该特征集是重要的;然后,最突出的选定特征可用于指导未来的绘图和可视化。这听起来可能需要时间,但实际上,它很快就能完成。为了有效地做到这一点,AutoViz 将选择的变量分类为分类变量、数字变量、布尔变量、NLP 文本变量等等,以便理解如何最好地绘制它们。最后,使用内置的试探法,该工具将返回被认为具有最大影响的视觉效果。AutoViz 也非常系统化:它使用不同图表类型的所有选定变量,以便通过让图表自己说话来提供最佳见解。主观的领域知识往往会使经验丰富的数据科学家产生偏见,只选择少量图表来突出数据集的洞察力。AutoViz 对特征和图形的客观选择可以使用系统方法将数据团队引向最佳方法,并可以从项目一开始就极大地提高团队的生产力。
AutoViz 在实践中是什么样子的?当然,了解可视化库的唯一方法是观察它的一些情节。
数据集:网上购物者的意向
让我们从 Kaggle 获取在线购物者的意向数据集。这些数据包括一年中对不同在线购物者的大约 12,000 次观察。这是一项分类任务;我们试图根据功能集来预测在线购物者是真的会购买某件商品,还是只是“逛逛”。AutoViz 能够确认所有 17 个特征具有用于预测目标变量的有用信息。
下面所有的图表都是从 AutoViz 提供的数百张图表中挑选出来的。首先,有一个条形图,表示构成我们目标类的观察值的百分比。
(Image by author)
显而易见,我们正在处理数据中的类别不平衡,因为所需的类别值(“True”)仅占总数据集的 15%。接下来,我们可以看到,当 AutoViz 提供每个数值变量的 KDE 图时,单个变量可能需要一些“处理”或“转换”。
(Image by author)
从上面我们可以看到,所有四个变量都是右偏的,可能需要进行“对数转换”才能在线性模型中使用。这些图表将被证明在为以后的问题建模构建数据管道时非常有用。
可视化可以为不同变量之间的相互作用提供非常有力的证据。从下面的图表中可以看出,高水平的跳出率和退出率是收入流失的良好指标。他们表示,这些交易不会有收入。快速洞察,如果你知道如何阅读他们所说的“茶叶”!
(Image by author)
我们可以通过检查在线购物购买的标准化直方图以及它们与某个特殊日子或节日(如情人节或圣诞节)的距离,来更深入地了解电子商务购买的时间安排。
(Image by author)
活动高峰出现在某个特殊日子的前一周左右;尽管我们的班级不平衡,但大约 50%的游客会在这个时间窗口购买一件商品。这是一个关键的提示,当一个企业想要进行促销或瞄准潜在客户时。
数据集:Goodreads API
AutoViz 还能够提供使用 Goodreads API 收集的约 12,000 本书的信息可视化数据集。数据集包含图书的整体元数据,目标变量是图书的“平均评级”,这转化为回归分析。这一次,图书馆从数据集中删除了一些变量,如 ISBN 和书名,除了识别之外,它们不能提供太多的预测信息。有趣的是,它去掉了作者特征,这是我们通常认为与一本书的成功相关的东西。然而,由于在这个数据集中这是一个分类字符串变量,可能没有足够的证据来使用各种各样的作者姓名作为一个特征。
AutoViz 输出的关联热图表明,除了评级数量和书面评论数量之间的明显关系之外,大多数变量都不是高度相关的。有趣的是,页面数量和评论分数之间存在某种正相关关系。
(Image by author)
通过分析下面的散点图,可以提取更多关于书籍页数的信息:
(Image by author)
页数较多的书籍的平均评分往往在 4 分以上。然而,值得注意的是,篇幅较短的书籍(100-250 页)能够获得高达 5 分的平均评论分数。然而,这些可能代表异常值,明智的做法是进一步检查这些高评级的书籍,以确保它们与其他条目具有相同的评论分数。总的来说,这给人的印象是,出版商可能希望将 1250-1500 页作为一本书的目标页数,以取得良好的业绩。
这些信息可以与 Goodreads 上的一本书的页数正态分布的知识相结合,因为许多书有大约 400-500 页。
(Image by author)
然后,我们可以使用 AutoViz 输出的 violin 图,将一本书的页数分布与其他变量的分布进行比较。
(Image by author)
平均图书评级也是正态分布,尽管是左偏的。负面评级可能是分布中的重要异常值。还可以观察到,一本书的评级数量看起来非常右偏;一小部分图书可能会获得过多的总评分。
数据集:AirBnB 房源数据
然后,AutoViz 能够熟练地可视化 AirBnB 房源数据,这些数据由位于西班牙马德里的 2 万个房源数据集提供。该数据库是托管在 Kaggle 上的 AirBnB 数据的关系集合的一部分,包括每个列表的床位和浴室数量、评论和日历预订等属性。在这个实验中,目标变量是 AirBnB 房源的评论分数。每个条目都有许多相关的特性,乍一看令人生畏。幸运的是,AutoViz 发现了许多低信息量的变量,可以排除这些变量以减少数据的方差。一些被排除在外的特征令人惊讶,例如价格、平方英尺或邻近的房源。
自动生成的图非常有助于了解不同列表的某些方面。例如,你可能会惊讶地发现,绝大多数 AirBnB 的房东会在被联系后一小时内回复。
(Image by author)
主人提供他们的整个居住空间作为房源的一部分也很常见,尽管大约一半的时间房源可能只提供一个房间。然而,这是有道理的,一旦它被证实的事实是,大量的上市是一个公寓,而不是一个房子在马德里。
(Image by author)
(Image by author)
我们的目标变量的预测因素呢?对 AutoViz 生成的热图的分析表明,与某个列表的评论评级最相关的特征是每月收到的评论数量。
许多 AirBnB 评论可能是正面的,因此这可能意味着 AirBnB 会员最好接待尽可能多的客人,并鼓励他们留下评论,因为随着时间的推移,这可以最有效地提高房源的评论分数。
(Image by author)
有一个与房源审核分数负相关的特征,那就是 AirBnB 主机维护的房源数量。当列表从主持人那里得到更多个性化的关注,而不是作为不同住宿分配的一部分时,它们可能表现得更好。
结论
虽然这些只是 AutoViz 生成的图的一个示例,但不难看出自动化可视化是多么有帮助。在瞬间,该库能够生成高度信息化的图,并为数据科学家的建模或分析管道提供许多潜在的扩展途径。AutoViz 旨在整合到一个系统的迭代过程中。使用 AutoViz 可以有效地启动探索性数据分析(EDA );可以根据工具的分析选择要素,然后可以重复处理数据以实现自动可视化。一旦生成了强大的可视化效果,数据科学家现在就可以投入到建模中,或者用信息丰富的分析来交流数据。令人惊讶的是,自动化可视化选项如此稀少,作为一种客观和实用的工具,它有许多可以想象的用途,但 AutoViz 很好地完成了这个角色。现在让我们开始一些可视化!
你可以访问这个 jupyter 笔记本,里面包含了这些实验和其他实验。AutoViz 的 Github 库在这里可用,PyPi 页面在这里可用。非常感谢 AutoViz 的创建者和作者 Ram Seshadri。
原载于 2019 年 12 月 28 日https://danrothdatascience . github . io。
复仇者联盟,类似!
通过面部识别找到理想的服装
计算机视觉更有趣和更专业的用途之一是面部检测和识别。人类非常擅长识别人脸,但最近的趋势是,我们已经能够训练计算机做足够接近的工作,以保证在更多的应用中使用它。面部扭曲、老化,并向各个方向摆姿势,这给如何在多张图像中匹配它们带来了重大挑战。深度学习的进步,如 FaceNet 研究,真正改变了游戏,使这些过程更容易执行,结果令人难以置信地准确。
Don’t look away from the Nozzle!
如果你害怕向计算机教授这样的技能意味着什么,我理解这种情绪。面部识别有许多积极的方面。举几个例子来说明它是如何非常有帮助的:在不断增长的数字世界中增加安全功能,帮助寻找失踪人员,帮助盲人在世界中导航,以及(潜在的)帮助建立更好的工具来打击犯罪。然而,隐私仍然是这项技术的主要关注点。作为一个社会,对于我们的形象被拍摄并用于某种分类目的,我们有什么样的舒适度?为了安全起见,在个人设备上可能感觉还可以,但是当你在外面的世界中得到有针对性的广告呢?这确实是一件棘手的事情,但是当我们继续研究它的时候,记住这一点很重要:
“权力越大,责任越大”
u̶n̶c̶l̶e̶̶b̶e̶n̶̶f̶r̶o̶m̶̶s̶p̶i̶d̶e̶r̶-̶m̶a̶n̶.我,谈论面部识别
看起来像个英雄
这让我想到了一个使用面部识别的快速项目,希望能展示这项技术的创造性应用。
问题:快到万圣节了!你在一家服装公司工作,他们想为顾客提供一种有趣的互动方式,让他们找到最适合自己的服装。如果你使用面部识别来找到他们最像哪个演员,并根据这种匹配推荐服装,会怎么样?听起来像个计划。今年是复仇者联盟电影的重要一年,所以让我们用这些演员和他们相关的角色来创造一个复仇者联盟!
有一些很好的工具可以用于人脸检测和识别(比如 OpenCV ),但是我将使用 Python 库 face_recognition 进行演示。这是一个真正快速准确的包,使用 dlib 的人脸识别构建,取得了一些令人印象深刻的结果(在“野生标签人脸”基准上的准确率为 99.38%)。检测图像中的人脸(查找并返回边界框位置)并获取人脸编码(人脸的矢量表示)就像三行代码一样简单:
import face_recognitionloaded_image = face_recognition.load_image_file('young_rdj.jpg')
face_location = face_recognition.face_locations(loaded_image)
face_encoding = face_recognition.face_encodings(loaded_image)[0]
厉害!只用了几行代码,我们就找到了这张脸,并将其转换成一个数字向量表示,我们可以用它来与其他图像进行比较。虽然大多数面部识别都在寻找真正的匹配(例如照片应用程序标记一张脸,并在所有其他图像中找到那个人,你的手机匹配用户的脸以允许访问),我正在做的是寻找最好的“足够近”。以下是我为让我的复仇者联盟更相似而创建的一般工作流程和功能:
- 下载复仇者联盟电影中每个演员的照片。我坚持以大家伙为主,甚至加入了几个 x 战警,因为漫画经典中有些也是复仇者。没有花哨的代码,只是大量的手动谷歌搜索和寻找良好的干净的图像。
- 为图像编码过程创建一个函数,这样我就可以一次加载所有图像,并使用 list comprehension 来执行任务:
def image_to_encodings(file):
"""Return the face encodings from an image"""
load_image = face_recognition.load_image_file(file)
return face_recognition.face_encodings(load_image)[0]
3.通过我的函数运行所有图像,并创建一个新的字典,该字典包含角色名称及其带有面部编码的数组:
# Glob makes it easy to capture all jpgs filenames into a list
avengers_cast_files = glob.glob('avengers/*.jpg')# Getting character name from the file name
avengers_cast_names = [re.sub('.jpg|avengers/', '', image)
for image in avengers_cast_files]# Getting encodings
avengers_cast_encodings = [image_to_encodings(image)
for image in avengers_cast_files]# Creating dictionary with these new details
avengers_dictionary = {name: encoding for name, encoding
in zip(avengers_cast_names,
avengers_cast_encodings)}
4.酸洗我的编码字典。这使得我以后可以轻松打开它,而不必对我的已知面孔重新运行 face_recognition:
outfile = open('avengers_features.pkl','wb')
pickle.dump(avengers_dictionary,outfile)
outfile.close()
5.创建一个函数,根据已知的编码返回图像的最佳匹配:
def top_match(known_encodings, known_names, new_image_embed):
"""With list of known image names/encodings,
return closest match"""
distances = face_recognition.face_distance(known_encodings,
new_image_embed)
top = np.argmin(distances)
return known_names[top]
6.测试添加新图像并查看最接近的匹配:
me = image_to_encodings('me.jpg')
top_match(list(avengers_dictionary.values()),
list(avengers_dictionary.keys()),
me)
鹰眼!我要了。
如果你想亲自尝试,你可以在 GitHub 上查看我的代码。它目前可以在终端上运行,带有一些有趣 ASCII 装饰:
这可能是最好的 web 应用程序,我可能会尝试进一步构建,但有趣的是看到这是如何工作的原则。尽情享受吧!
为普通用户设计的危险
两个理论——一个思想实验——数字模拟——高斯相关说 whaaa——一个尺寸永远不会适合所有人——印度妈妈,最好的优化器
我最近遇到了两个概率理论,一个来自一个 T2 的朋友,另一个来自一个精彩的 T4 播客。在这篇文章中,我试图用《Think X》系列丛书的作者艾伦·唐尼的一个观点来解释这两种理论。我强烈推荐的 Think X 系列的前提是,如果你知道如何编程,你可以用这种技能来学习其他主题。这两种理论有一个共同点。
Image Source
让我们从一个思维实验开始。你是一家领先零售商新冬装系列的首席设计师。你有来自全国几家分店的现有和潜在顾客的身体尺寸数据。身体尺寸包括身高、体重、臀围、膝宽等。你的任务是提出适合大多数人的最佳标准设计。在验证所有主体尺寸大致遵循正态分布后,您得出结论,最好的方法是使用所有这些尺寸的平均值来达到您的最佳设计。
让我们来表示,
S1——距离平均身高在±K 标准差范围内的所有人的集合,S2——距离平均体重在±K 标准差范围内的所有人的集合…Sn。—距离实体 n 的平均值±K STD 范围内的所有人的集合。您明白了。
以上几组代表了接近平均水平的人,他们可以穿着为普通人设计的服装而不会引起太多骚动。谬误的根源在于这样一个事实,当你增加维度的时候,论点就分崩离析了。即使你通过为普通用户设计来最大限度地增加穿你设计的人,当你从多个角度看时,接近平均水平的人的数量很快接近零。当考虑到维数灾难时,一般用户谬误虽然有趣,但事后看来却很幼稚。
空谈不值钱。给我看看代码。莱纳斯·托沃兹。
尽管理论很吸引人,但它们几乎没有保留价值。为了说明这一点,我将尝试唐尼的方法,通过代码和模拟来理解它。
在上面的例子中,普通用户谬论没有解决的一个关键问题是维度之间的相关性。例如,身高和体重之间有很强的相关性。一个人越高,他认为所有的事情都是平等的。同样,人越高,膝盖越长。
我们假设平均身高 175 cm,平均体重 190 lbs。
mean = np.array([175, 190])
samples = 10000
corr_factor = 0.3
corr_matrix = np.array([[1.0, corr_factor],
[corr_factor, 1.0]])
假设它们与相关因子 0.3 相关,让我们尝试模拟来自高斯分布的数据。为了模拟多元相关数据,我们执行以下操作:
Correlated Data = Mean + Covariance * Uncorrelated Gaussian Data.
我们需要协方差来从不相关数据生成相关数据。协方差是相关矩阵的平方根。我们可以使用 Cholesky 的分解来得到它,虽然方法并不精确。
chol = np.linalg.cholesky(corr_matrix)
通过取平方从协方差矩阵中恢复相关矩阵,确保 Cholesky 分解有效。
assert (
~np.isclose(chol @ np.transpose(chol), corr_matrix)
).sum() == 0, "Cholesky not working"
从不相关数据生成相关数据,
uncorr_data = np.random.randn(2, samples)print(f"Correlation of randomly generated data - \n{np.corrcoef(uncorr_data)}")corr_data = np.reshape(mean, [2, 1]) + chol @ uncorr_dataprint(f"Correlation of correlated data - \n{np.corrcoef(corr_data)}")>>> Correlation of randomly generated data -
[[ 1\. -0.02200527]
[-0.02200527 1\. ]]
Correlation of correlated data -
[[1\. 0.28526392]
[0.28526392 1\. ]]
正如你所看到的,得到的相关性并不完全是 0.3。这取决于生成的样品组和样品大小。
A joint plot of Height vs Weight
体重和身高的平均值不同,但标准差相同。循环遍历数据集,找到距离平均值 K=1 个标准差范围内的点。
k = 1
normal_height = (corr_data[0] > mean[0] - k) & (
corr_data[0] < mean[0] + k
)
normal_weight = (corr_data[1] > mean[1] - k) & (
corr_data[1] < mean[1] + k
)
normal_height_weight = normal_height & normal_weight
normal_height
是一个二元向量,表示与平均身高的 K 个标准偏差内的所有值,对于normal_weight
也是如此。normal_height_weight
是这两种情况的交集。
这为我们的第二个理论高斯相关不等式提供了一个很好的切入点。在转换话题之前,请记住,我们正试图说明增加维度是如何减少接近平均水平的人数的。
Lucy Reading-Ikkanda/Quanta Magazine
形象乍一看很混乱,名字也很拗口。让我们试着分解它。让我们从乘法的基本规则开始,
P(A ∩ B) = P(A) * P(B),给定事件,是独立的。
但是在我们的例子中,我们知道 A 和 B 不是独立的,它们是相关的,由相关矩阵给出。
P(A ∩ B) = P(A) * P(B|A)。
高斯相关不等式表明,
P(A ∩ B) >= P(A) * P(B)
在我们的方案中,用 S1 和 S2 代替 A 和 B,我们知道 P(S1)和 P(S2) ~ 0.68,因为在高斯随机变量中,68%的数据落在钟形曲线均值的±1 标准偏差内
将 P(S1 ∩ S2)和 P(S1) * P(S2)相对于各种相关值绘图清楚地表明,当存在最小相关时,它们是相同的,但是当存在强相关时,P(S1 ∩ S2)稳定地开始增加,而 P(S1) * P(S2)保持相当恒定。
现在回到平均用户谬误,我们知道 P(S1 ∩ S2)小于 P(S1),这意味着用户下降的概率从平均值±K STD 下降,因为你增加维度,尽管维度之间的相关性。
让我们模拟 n 维的相关数据,看看维数灾难对平均用户谬误的影响。我们将随机化维度之间的相关因子。
corr_values = np.random.random(((ndim ** 2 - ndim) // 2) - 1)
corr_matrix = np.identity(ndim)for row in range(0, ndim):
for col in range(0, ndim):
if row != col:
corr_matrix[row][col] = corr_values[row + col - 1]
从上图可以明显看出,不考虑相关性,随着维度的增加,出现在跨维度均值附近的概率迅速降低,趋近于零。
你可以在这个笔记本里找到完整的代码。
一刀切:
对于那些宽容的读者来说,让我们来看看一个为普通用户设计的可怕错误的真实例子。
99pi—
在第二次世界大战期间,美国空军迅速扩张,伴随着性能下降和一连串的死亡,甚至在训练期间。多年来,空军的高死亡率一直是个谜,但在归咎于飞行员和他们的训练计划后,军方终于意识到驾驶舱本身是罪魁祸首,它不适合大多数飞行员。起初,他们认为这个数字太小了,而且自 20 世纪 20 年代以来,男性的平均年龄有所增长,所以在 1950 年,他们请俄亥俄州莱特空军基地的研究人员计算新的平均值。
其中一名研究人员是年轻的哈佛毕业生吉尔伯特·s·丹尼尔斯。在他的研究中,测量了数千名飞行员的 10 个关键身体维度,丹尼尔斯意识到,他测量的飞行员没有一个在所有 10 个维度上都是平均水平。一个都没有。当他只看三维时,平均只有不到百分之五。丹尼尔斯意识到,为普通飞行员设计的东西,实际上并不适合任何人。
重温我们开始时的思想实验,作为一名首席设计师,现在知道了一般用户的谬误,你应该如何着手优化你的设计?在一个不受资源限制的理想世界中,解决方案应该是为每个人量身定制一套测量方法。但是现实世界从来没有那么简单。你总是被约束所束缚,需要取得平衡。一方面,你目睹了为普通人设计的恐怖,另一方面,定制适合每个人是不可能的。
印度妈妈,最佳优化师:
普通用户谬误并非无处不在。例如,如果您正在设计一把椅子、一个汽车座椅或一个现代化的驾驶舱,您并不局限于固定的尺寸。您可以使用杠杆和其他机构来让众多的身体尺寸舒适地接触到它们。这些动态系统让用户可以在连续的设计空间中无缝导航。但是有些设计本质上受制于普通用户的谬误。这些系统内部具有一定的刚性,使其无法进行动态调整。
裙子就是一个完美的例子。一旦购买,大小保持不变。因此,服装制造商基于利润最大化的动机,试图接触尽可能多的人,同时仍然减少他们必须生产的不同尺寸的品种。这就产生了以 S、M、L、XL 等不同的离散时间间隔制作衣服的系统,从而解决了普通用户的谬误问题。让我们考虑一下最糟糕的情况,裙子开始缩水,你开始增重,测试你裙子的完整性。这似乎是一个不可能解决的问题,但对一个印度妈妈来说不是。她如何解决这个难题相当巧妙。她单枪匹马地决定你将得到一个比你目前的大小,以支持她的格言,
“你从来不为你现在的自己买,而是为你将成长为的那个人买”。
然后,她骄傲地从您身边走过,完全知道她得到了您想要的,同时优化了产品的寿命,从而降低了长期成本。如果这不是一个完美的优化,我不知道是什么。
平均值毫无意义*
但是,并不总是这样。我们来探索一下在哪里。
Just like this picture? Source
“平均每 14 分钟就有一个人在 eHarmony 上找到爱情.”
去威斯敏斯特的地铁上的广告是这么说的。
是不是说我会在 14 分钟内在 eHarmony 上找到爱情?不完全是。
也许其他人会?我不在乎。
几年前,我试着学游泳。
我在网上看到,“平均下来,学游泳要花 20 个小时。”好吧,还不算太糟。而且,我高于平均水平。
在游泳池里游手好闲了 20 个小时,我根本没学会游泳。不过,我确实找到了一个让我的头在水下保持 3 秒钟的方法。这就是进步,对吧?
现在怎么办?我不是高于平均水平吗?平均时间不是 20 小时吗?是不是低于平均水平…不好?
平均是没有意义的。
起源
哪一个数据点可以取代我集合中的所有数据点?
这个问题催生了平均值。平均值是众多答案中的一个。
它始于天文学。每个天文学家都想算出行星和太阳之间的距离。每个人都尝试了自己的方法,也重复了别人的方法。最后,他们进行了大量的测量。怎么搞清楚哪个是正确的?他们如何将误差降至最低?
答案是取所有值的平均值。他们相信正误差会抵消负误差。
他们考虑的是正态分布。事实上,正态分布在这里是有意义的。大多数有能力的天文学家会进行类似的测量——有时仪器的误差会使结果偏向一边或另一边——但并不总是如此。
Bell Curve = Normal Distribution. Source
此后不久,平均值的第二个用例出现了:估计[1]。你不用数灌木丛中所有的浆果,而是取一部分灌木。一个看起来普通的,代表所有其他部分的。数一数这一区的所有浆果,乘以这一区的数目,你就得到灌木丛中的浆果总数。食物计划完成。一般来说,没有人会挨饿。
另一个例子是战争。将军们是如何计算出他们必须击败的敌人数量的?站在战场上,你不会想花时间去数每一个敌军士兵。你应该估计他们的数量。怎么会?使用平均值——代表值。数一个有代表性的连队的士兵,乘以连队的数量,你就得到敌军的数量。
这些平均数的概念开始传播。
- 为了精确计算测量值
- 用于估算
在这两个用例中,我们都使用正态分布。高于平均水平的会被低于平均水平的抵消。这给了我们一个很好的代表值。
指数运算
随着平均值开始与估计和测量一起工作,使用平均值的人开始看到好处。他们会赢得战争,在食物规划上更加成功,在科学上取得飞跃。
随着时间的推移,我们开始相信平均数在任何地方都适用。
但是,我们已经进入了复利时代。正常不是标准。一旦我们创造了杠杆,我们就把正态分布向一边倾斜。我们创造了幂律分布。
Source — worth going through the slides
我们的大脑仍然按照正态分布来思考。当谈到统计数据时,我们没有考虑到分布。事实上,这就是为什么 80/20 原则如此具有革命性。它提醒每个人,正态分布不再是常态。
保持正态分布的是我们仍然没有杠杆的领域。我们还不知道如何让人活 1000 年。
平均值是从分布中推导出来的。我们隐含地假设一切都是正态分布。【2】
接下来,探究指数运算如何改变平均值是有意义的。
在现代社会,我认为我们谈论平均值主要有两种情况。
- 当我们把平均值投射到世界上时
- 当我们把平均值投射到自己身上时
注意:在我使用正态分布的地方,我指的是近似正态。它的特点是中间肥胖,边缘逐渐变细。真实世界的数据集很少完全遵循正态分布。幂律分布也是如此。
预测世界的平均值
像我们的祖先一样,在这种情况下,你要找出一个有代表性的值。你想了解的世界上某个指标的代表值。
然而,那个人并不是所有人——所以你总是会错过一些东西。(地图不是领土)
问这样的问题,一般人长什么样?一般人有多重?有道理。
人口长相和体重呈正态分布——所以你的平均值确实是一个代表值。大约一半的人低于平均水平,一半高于平均水平。所以,平均值给了你一个合理的衡量标准。(我们将在下面进一步探讨这一点)
另一方面,一旦我们进入幂律分布领域,我们的平均值就不再有意义。
平均值是所有数据点的线性组合,对于幂分布来说是没有意义的。你的数据太偏向一边了,80%的人都低于平均水平。如果这听起来很荒谬,那就是我们所看到的和我们认为它的意思有分歧的地方。“平均”是指 50%的划分?这是中间值。幸运的是,在正态分布中,中位数和平均数是相同的。
所有这些无意义的平均值出现在哪里?
这里只是几个例子。
每位顾客的平均收入。
图书销售。
平均家庭收入。
比方说,作为一项商业决策,你正在开始写一本新书。你想赚——你会发现书籍平均能赚 20,000 美元。听起来很棒,对吧?但 90%的作者收入低于平均水平。如果这让你感到愤怒、心烦意乱或委屈,你并不孤单。现在你知道这怎么可能了。图书销售也遵循幂律分布。
无意义的平均值— 1。你— 0。
比方说,你正在 A/B 测试你的网站产生的收入。在版本 A 中,网站是蓝色的。在版本 B 中,网站是绿色的。你的评判标准——A/B 测试期的平均收入。一个大客户来买走了蓝色版本的几乎所有东西——一个蓝绿色色盲的客户。你收到一个假信号:“蓝色比绿色好用多了!”真的吗?你的平均收入是这么说的。
所以,你的指标被打破了。(你应该跟踪转化率,而不是每次转化的平均金额。)
对某人进行平均预测
我们的祖先所没有的是足够的数据来将平均值应用到他们自己身上。[4]
我们使用平均数的另一种方式是当我们把人口平均数投射到某人身上时——我们自己、我们的朋友或我们的玩具。
好的一面是——人类处于正态分布,所以平均值具有代表性。
坏的一面是——不是每个人都是普通人。
理解这一点的最好方法是通过一个例子。
平均来说,人类需要大约 66 天来建立一个新习惯。这是否意味着你在 66 天后就不再尝试养成习惯了?抱歉,没有。
平均值是总体的一个基准——供外部人员判断他们的产品。例如,如果我正在开发一个培养习惯的应用程序,我会把它设计在 66 天左右。这是因为大多数人需要大约 66 天来养成一个习惯(正态分布)。
另一方面,你在人群中。平均数对你来说毫无意义。它告诉你的是,如果你是一群正在养成习惯的人(你不是),那么大多数人会花大约 66 天来养成一个习惯(你不在乎)[3]。对你养成习惯没有帮助,是吗?当然,它设定了一些期望。你可能需要 66 天。但除非你尝试过,否则你不会知道。
你只是一个数据点,而不是统计数据。你可能是一个局外人,也可能是一个普通人——没关系,你需要自己去发现。因此,平均值(当投射到某人身上时)是没有意义的。
平均数可能扰乱我们思维的另一个地方是刻板印象。
我认为对于一个社区的普通人来说,刻板印象是真实的。但是就像我们看到的,普通人不是一个特定的人。如果你信誓旦旦地用刻板印象来判断一个人,你就陷入了平均谬误。[5]
一般来说,犹太人善于理财。那是刻板印象。它告诉你关于你面前的犹太人的任何事情了吗?我不这么认为。[6]弄清楚这件事的唯一方法是查看他们的银行对账单。
这种预测是分布不可知的。无论分布如何,用平均值来判断一个人是错误的。这种分布决定了错误会变得多严重。
这是风险投资游戏开始的地方。当基础分布为幂律时,风险和报酬较高。你听过风投公司和公司谈平均水平吗?一般的公司都死了。他们在游戏中寻找赢家——找到一家普通的公司是不够的。
分布
我们已经建立了平均值与分布的紧密耦合。
最后一个问题是,如何计算分布?
我认为有两种方法可以做到这一点,而不用成为一个统计奇才。
我们怎么知道重量是正态分布的?
有人说是直觉。你从没见过 200 公斤以上的人。甚至世界纪录也只有 500 公斤。那是一辆小汽车。另一方面,没有人重 1 公斤。大多数人都很健康或轻微肥胖。有些很瘦,有些很大——但它们都互相平衡。
是什么产生了这种直觉?数据。在我看来,直觉就是数据。发展直觉需要时间?那只是数据收集。你一生都在与人交往——这就是你一直在收集的所有数据。当然,你可能不知道他们的体重——但是你收集的数据非常丰富,你可以推算出他们的体重。
最后,钟形曲线对你来说似乎是合理的。在统计学中,这被称为抽样。你正在收集一点数据来计算整个人口。
直觉是数据
它有它的陷阱。如果您收集的所有数据都只针对您所在的地区,该怎么办?如果你见过的每个人都很健康,体重 70 公斤,会怎么样?
在这种情况下,你就成了局外人。你不知道发生了什么。你不能靠直觉去搞清楚分布。
这就是真实数据的来源。你寻找从世界各地收集这些数据的人。然后你画出来。可视化您的数据是一种简单的方法来找出趋势,并在幂或正态分布之间进行选择。可能都不是。
这两种方法交织在一起真美。一旦你开始将直觉视为数据,它们真的会融合。前者依靠个人收集的数据,后者依靠他人收集的数据。
走向统计魔法的是分布测试。这些要严谨精确得多。我在这里找到了一个简短的介绍。
结论
脱离上下文的平均值是没有意义的。就像“那是她说的”笑话一样。
这在早期是可行的,因为我们只有一个上下文——正态分布。不再是了。
所以,不要陷入平均谬误。你面前的这个人不是刻板印象。他们可能会遵从,但你必须自己去发现。
并且,不要在没有提到分布的情况下谈论平均值。显性比隐性好。如果不是正态分布,平均的意义就变了[7]。你从 50%的人低于平均水平到 80%的人低于平均水平。
至于我?这只是拼图的另一块,因为我试图了解这个世界。加入我吧。
[1]或者在天文学之前,我们不确定。记录不是完整的。
[2]显性比隐性好。
[3]正态分布的均值、中值和众数是相同的——这给了我们期望的平均值的直观感觉。
[4]一个相反的论点是,他们有自己的部落和对其他部落的看法(这很糟糕)。在这个意义上,和现代非常相似。
[5]我一直想给一种认知偏差命名。是的,我正在对这些名字进行计算。
[6]它可能会告诉你,他们比其他非犹太人更善于理财——但这于事无补。另一方面,如果你试图选择优秀的基金经理,你会发现更多的基金经理是犹太人。假设刻板印象是真的。
[7]它是如何计算的,或者计算是什么,并没有改变。它所代表的意义发生了变化。
原载于 2019 年 4 月 7 日【neilkakkar.com*。*
避免数据泄露—在处理之前拆分您的数据
生产中模型失败的一个潜在原因
Kai Kai @ River Safari Singapore
**经验法则:**在对数据进行任何处理之前,将数据集分成训练集和测试集。否则可能会有数据泄漏,使模型评估过于乐观。
你们中的一些人可能有这样的经历:
- 您的模型在您的本地测试集上得分很高,但在 Kaggle 排行榜上表现不佳
- 您的模型在概念验证期间正常工作,但在生产环境中会急剧下降
这些可能是由于数据泄露造成的。
什么是数据泄露?
数据泄漏是指训练数据集和测试数据集之间意外共享信息。这种信息共享会给模型一个关于测试数据集的“提示”,并生成看似最佳的评估分数。然而,由于模型过度拟合测试数据,它无法准确预测未来的未知数据集,即 Kaggle 测试数据或生产实况数据。
数据泄露的一个常见原因是由于列车数据处理后的测试分割。
示例:处理后由于分割导致的数据泄漏
假设我们有以下数据集,并希望使用列 1 和列 2 来预测目标。
按顺序执行以下步骤:
Example: raw dataset for classification
Example: train test split after processing
- 将第 1 列中的 Y 和 N 替换为其各自的目标平均值。
- 标签根据词汇顺序对列 2 进行编码。
- 分割数据集。前 7 行用于训练,后 3 行用于测试。
- 使用训练数据训练模型,并根据测试数据进行验证。
发生数据泄露!
对于步骤 1,Y 的值是用 4 / 6 计算的,这表明数据集中有 6 个 Y,其中 4 个有正目标。由于数据在处理后被拆分,这些信息会在训练数据集和测试数据集之间意外共享。在第 4 步中,模型从 4 个 y 开始训练,并以某种方式“期望”测试数据中的两个 y,其中一个具有正目标。这会导致对模型性能的预期过于乐观。
类似地,对于步骤 2,第 5 行的字母*‘e’被编码为 5,因为它知道完整的数据集具有’‘a’,‘b’,‘c’和‘d’。*这些信息也在训练和测试数据之间泄露。
简单的解决方案:在处理前拆分
上面提到的问题有非常大的负面影响,但是解决方案很简单——在处理之前分割。让我们用相同的例子,但不同的步骤:
Example: train test split before processing
Example: transform train and test data with same encoders
- 分割数据集。前 7 行用于训练,后 3 行用于测试。
- 对于训练数据:将第 1 列中的 Y 和 N 替换为它们各自的目标平均值。标签根据词汇顺序对列 2 进行编码。保存这些编码器。
- 对于测试数据:用相同的编码器转换数据。
- 使用训练数据训练模型,并根据测试数据进行验证。
请注意,Y 被替换为 0.75,因为训练数据中有 4 个 Y,其中 3 个有正目标。字母*‘e’编码为 4,因为训练数据中没有‘c’*。测试数据被认为是前所未见的,并遵循类似的模式。因此,它们用来自训练数据的相同编码器进行变换。
首先拆分数据有两个主要好处:
- 降低数据泄露的风险。
- 未来看不见的数据将以与测试数据完全相同的方式处理,从而确保模型性能的一致性。
感谢阅读!
避免这些致命的建模错误,这些错误可能会让你失去职业生涯
我喜欢看到数据科学家使用高级包,创建令人眼花缭乱的展品,并试验不同的算法。数据科学家可以让电脑燃烧一整天。一件很酷的 t 恤、一杯咖啡和一台笔记本电脑——这就是他或她所需要的一切。虽然他们的名字听起来很疯狂,但一些数据科学家新手不断犯常见的错误——我称之为致命错误。这些基本的错误会损害数据科学家的可信度,并可能让一个有前途的数据科学职业生涯付出代价。我在这篇文章中的目标很简单:我希望你在读完这篇文章后,永远不要犯这种类型的错误。Python 和 R 代码都有提供。
我写过关于各种数据科学主题的文章。为了方便使用,你可以将我的总结文章“数据人学习之路——培养你的技能,推动你的职业发展”加入书签,这篇文章列出了所有文章的链接。
(1)为什么“日期时间”变量是最重要的变量?
请小心 yymmdd:hhmmss 格式的任何 DateTime 字段。您不能在任何基于树的方法中使用此变量。如图所示,这个变量出现在变量重要性图表的顶部。原因是这个字段几乎成为每个记录的唯一标识符。这就好像您在决策树中使用了“id”字段。我确信你需要导出年、月、日、工作日等等。从这个领域。记住,特征工程是捕捉可重复的模式,比如一年中的月或周,来预测未来。在某些模型中,您可能会使用“年份”作为变量来解释过去的任何特殊波动。但是您永远不会使用原始日期时间字段作为预测值。
(2)小心变量中的‘0’、‘99’或‘999’
它们通常是缺失值,被系统设置为极值。在参数回归中,不要盲目地将它们用作数值。“-99”和“-999”可能由不同的系统设置。它们携带一些独特的信息,不会随机丢失。这个普遍存在的问题不能由诸如库(mice)这样的软件盲目处理。
(3)连续变量有’ NA ‘,’ 0 ‘,’-99 ‘或’-999 '怎么办?
我建议您将连续变量绑定,将特殊值“0”、“99”和“NA”作为它们的类别(可能还有其他方法)。首先,你可以得到变量的切割点:
R 代码:
分位数看起来像这样:
然后使用上述切割点来绑定变量,以创建一个新的变量。下面的代码也保留了特殊值。我使用函数cut()
将连续变量切割成分类变量。我使用case_when()
来分配’-999 ‘、’-99 ‘、’ 0 ‘和’ NoData '(注意这种语言是 R 语言,但是这个概念也适用于其他语言。)
Python 代码:
pd.qcut()
将数值离散化成大小相等的桶。缺失数据怎么办?所以我用add_categories()
添加了一个新的类别。记住pd.qcut()
产生的数据类型是分类的,而不是字符串。
(4)你强制一个分类变量成为基数变量
基数变量是数值变量,其值是有序的。非基数变量,通常是分类变量,是其值不能排序的变量。例如,变量性别“男性”和“女性”的值不能排序。
假设您需要将一个分类变量(比如性别)转换为一个数值变量来运行回归。假设您将 1 分配给“男性”,将 2 分配给“女性”。您错误地将分类变量强制为基数变量!
下面是具有值“h5”、“android”、“api”和“ios”的分类“AP006”。我打印出每个值和前 20 条记录的计数统计数据(第一条记录是“android”,第二条是“h5”,依此类推。)值“h5”没有理由高于或低于其他值,即,没有基数性质。如果如下创建一个新变量“newvar ”,品牌“android”的价值就荒谬地比“h5”的价值高两倍。这个错误应该避免。
R 代码:
Python 代码:
你需要做的是使用pd.get_dummies(train['AP006'],dummy_na=True)
创建虚拟变量。有关更多处理方法,请参见“将分类变量编码为数字的数据科学家工具包”。
(5)预测中的新分类值
假设您将训练好的模型应用于测试数据,并得到一条错误消息。你意识到测试数据中的一个变量比训练数据中的一个变量有更多的类别。下面我用一个例子来说明。测试数据中的变量“Education”有一个不在训练数据中的额外类别“Ph.D .”。这个错误导致您的预测停止,并且该观察没有接收到预测值。
在 Kaggle 比赛中,通常有三个数据集:训练、测试和提交。在您的训练和测试数据中,您可能有很好的模型性能,但是您的模型在您的提交性能中失败得很惨。提交数据中的变量可能具有在训练或测试数据中看不到的新类别。即使您重新对数据进行了采样或者选择了不同的方法来创建培训和测试数据,也可能会出现此问题。
以下两种方法处理这个问题。首选方法 1。
方法 1:将任何未知值适当编码为“其他”或者合并到另一个类别中。这是一个很好的编程实践:即使您在训练数据中看不到任何新值,您仍然会保留一个类别,以备可能会有新值。下面的代码将任何未知组合成“硕士及以上学历”。以便相应地组合测试数据中的“博士”记录。
R 代码:
方法 2:使用你的训练和测试数据来创建新的变量。在这种情况下,将观察到新的类别“博士”。然而,这种方法仍然不能避免任何未来的未知值。
Python 代码:
[## 通过我的推荐链接加入 Medium-Chris Kuo/data man 博士
阅读 Chris Kuo/data man 博士的每一个故事。你的会员费直接支持郭怡广/戴塔曼博士和其他…
dataman-ai.medium.com](https://dataman-ai.medium.com/membership)
(6)忘记处理回归中的异常值
展示中的异常值导致你的回归倾向于那个观察值。你的预测会有偏差。所以记得把你的因变量和自变量分别削减到 1%和 99%。这意味着如果一个变量的值低于 1%,你将把它设置为 1%,如果高于 99%,把它限制在 99%。有些人可能会尝试 5%和 95%。
(7)要求线性回归中因变量的正态假设
因变量 Y 不需要遵循正态分布,但是预测 Y 周围的**误差应该遵循正态分布*。数据科学家经常从因变量的直方图中检查正态假设。在这种特殊情况下,如果因变量服从正态分布,那么误差将是。然而,概念是*线性回归的误差应遵循正态分布,或者因变量具有条件正态分布。回想一下线性回归的定义:在 X 的每一个值上,都有一个正态分布的 Y 的条件分布。线性回归的四个基本假设是:
- X 和 Y 之间的关系是线性的
- 误差呈正态分布
- 误差的同方差(或者,线周围的等方差)。
- 观察的独立性。
根据大数定律和中心极限定理,线性回归技术中的普通最小二乘(OLS)估计仍将近似正态分布在真实参数值周围。因此,在大样本中,即使因变量违反了“正态假设”规则,线性回归技术的使用仍然有效。
(8)要求线性回归中预测值的正态假设
预测者的 Xs 怎么样?回归并不假设预测值有任何分布。唯一的要求是检查是否存在异常值(您可以使用箱线图来检查异常值)。如果是,应该对预测值应用封顶和垫底技术。
(9)你在决策树中做分布假设吗?
*在参数形式(如线性回归)中,您检查目标变量的分布以选择正确的分布。例如,如果目标变量显示伽玛分布,则在广义线性模型(GLM)中选择伽玛分布。然而,**决策树不对目标变量做出假设。*处理决策树的基本原则是将每个父节点分割成尽可能不同的节点。它没有对原始群体或最终群体的分布做出任何假设。因此,在实现决策树时,分布的性质并不重要。
(10)在决策树中,你是否对预测因子进行了封顶和保底?
在参数形式(如线性回归)中,您必须通过将异常值限制在 99%(或 95%)和 1%(或 5%)来处理异常值。在基于树的算法中,你不需要在决策树中做封顶和垫底。或者换句话说,决策树算法对异常值是鲁棒的。树算法基于相同的值分割数据点,因此异常值不会对分割产生太大影响。这取决于您对超参数的设置。
(11)我的树中没有显著的变量或者只有很少的变量
您可能将复杂性参数(cp)设置得太高。复杂性参数(cp)部分是每个节点所需的模型的最小改进。这是分割该节点改善相对误差的量。如果分裂原始根节点将相对误差从 1.0 降低到 0.5,则根节点的 CP 是 0.5。
(12)决策树中存在多重共线性的变量是否丢弃?
*当多元回归模型中的一个预测变量可以通过其他预测变量以相当高的准确度进行线性预测时,就会发生多重共线性。当您为分类变量创建虚拟变量时,您在回归模型中删除了一个变量。**然而,决策树和提升树算法不受多重共线性的影响。*在完全相关的变量中,决策树将只选择一个提供最佳分割的变量。
(13)忘记标准化 K-means 中的变量
K 均值聚类可能是最流行的无监督技术。它给你很好的聚类结果。但是,如果不将变量标准化到相同的量级,聚类结果和业务应用可能是灾难性的。
我用一个简单的例子来解释一下。假设 P1、P2 和 P3 在(X,Y)空间中的位置是(3000,1)、(2000,2)、(1000,3)。K-means 计算出 P1 和 P2 之间的距离为 1000.0005。因为 X 的量级支配 Y,结果被 X 错误地驱动你需要做的就是把 X 和 Y 拉到同一个量级。在本例中,X 被带到 X2,用于 K-means 计算距离。
这里我提供几个方法:
(1)缩放至(0,1):
r 代码:
Python 代码:
(2) Z 分数:
r 代码:
Z-score 的 Python 代码可以在下面找到。
(13)独立标准化训练和测试数据
当您标准化数据以训练您的模型时,请记住只有训练数据用于拟合定标器转换,然后定标器用于转换测试输入数据。一个常见的错误是独立缩放 x_train 和 x_test。
下面是正确的做法。StandardScaler()
用于从 x_train 数据中学习比例,然后应用于测试数据。
为什么我们不能对训练数据和测试数据分别进行独立的扩展呢?让我来说明建模的目的。一个模型将被用来对未来的未知数据进行评分。我估计你也同意,如果训练数据有了尺度,那么未来的数据也应该先有尺度,再由你的模型打分。我们不知道未来数据的均值和标准差,我们假设它们的行为与训练数据相同。因此,我们只能使用训练数据的平均值和标准差来应用于测试数据。让我用一个极端的例子来说明这一点。假设未来数据漂移并变得与训练数据非常不同,则未来数据的均值和标准差将不同于训练数据的均值和标准差。这种数据漂移是严重的,应该向模型用户发出警告。对数据漂移现象感兴趣的读者,推荐阅读《监控你的机器学习模型性能》一文。
* [## 通过我的推荐链接加入 Medium-Chris Kuo/data man 博士
阅读 Chris Kuo/data man 博士的每一个故事。你的会员费直接支持郭怡广/戴塔曼博士和其他…
dataman-ai.medium.com](https://dataman-ai.medium.com/membership)*
在人工智能中避免副作用和奖励黑客
Photo by @pawel_czerwinski Unsplash
《人工智能安全中的具体问题》节选小结
我决定再次后退一步。这一次到 2016 年 6 月发表在 OpenAI 页面上写的关于 AI 安全的论文叫做AI 安全中的具体问题 。写这篇文章的时候,现在是 7 月 26 日。然而,我很怀疑自己现在是否比当时这群思想家懂得更多。但是我会尽我最大的努力来检查这篇论文。
“机器学习和人工智能(AI)的快速发展使人们越来越关注 AI 技术对社会的潜在影响。在本文中,我们讨论了一个这样的潜在影响:机器学习系统中的事故问题,定义为可能因现实世界人工智能系统的糟糕设计而出现的意外和有害行为。”
据此,他们列出了与人工智能风险相关的五个实际问题,并按以下方式分类
- **A 排泄副作用 **
- 奖励黑客
- 可扩展监督
- 安全探索
- 分配转移
我将简要地看一下在粗体中列出的两个。
他们回顾了这些领域以前的工作,并提出了与“前沿”人工智能系统相关的研究方向。然而,他们确实把这些变成了具体的例子,比如机器人手臂撞倒了花瓶;淘气的清洁机器人;培训期间的评估;电源插座中的湿拖把;环境不同。随着我们的进展,我将自由地思考,如果不清楚文章的建议和我自己在这方面的想法,我道歉。
在撰写本文时,他们指出了三个趋势,这使得人工智能安全变得非常重要。在这个中,他们声称:
- 强化学习(RL)的前景越来越光明,它允许代理与他们的环境进行高度交织的交互。
- 更复杂的代理和环境。“副作用”更有可能发生在复杂的环境中,一个代理可能需要非常复杂才能以一种危险的方式破解它的奖励功能。
- 增加人工智能系统的自主性。输出建议,如语音系统,通常具有相对有限潜在危害。相比之下,控制工业过程的机器会造成伤害。
避免副作用和奖励黑客——目标函数
他们谈论的 【负面副作用】 。在文章中,他们质疑他们所谓的“形式目标函数”,以及是否有可能选择了错误的目标函数。目标函数是线性规划中希望最大化或最小化的函数。用简单的英语,也许太简单了,我们可能会问:我们做得对吗?如果我们将矿产资源开采的价值最大化,却忘记了可能的环境外部性(理解为:损害),这可能就是一个例子。在此,他们将事故定义为:
“从广义上讲,事故可以被描述为这样一种情况:一个人类设计者想到了某个(可能是非正式指定的)目标或任务,但是为该任务设计和部署的系统产生了有害的和意想不到的结果。”
如果忽略了潜在的非常大的环境问题,就会出现安全问题。即使在“完美的学习和无限的数据”的限制下,目标函数也会导致有害的结果。这些思想被发扬光大,但可以说不是 AI 领域特别独特的。尽管如此,它被考虑的事实是重要的,看起来显而易见的并不总是最明显的行动过程。
我最近总结了欧盟基本权利机构(FRA)的一篇论文,名为 “数据质量和人工智能——减少偏见和错误以保护基本权利 ”。在这篇文章中,我看到了一个有趣的陈述,我想再次强调一下。数据是否“符合目的”,这与数据质量有关,但显然在人工智能安全中,它同样与形式目标函数的问题有关:
“因此,数据的质量在很大程度上取决于它们的使用目的.”
回到 OpenAI 的论文。他们进一步描述道:“在*《黑客行动奖励》**中,设计者写下的目标函数承认一些聪明的‘简单’的解决方案,这些解决方案在形式上最大化了目标函数,但是扭曲了设计者意图的精神(即目标函数可以被‘游戏化’),这是引线问题的一种概括。”*【加粗】那么什么是引线问题呢?
“电线头 是大脑体验快感的人工刺激,通常是通过用电流直接刺激个体大脑的奖赏或快感中枢。它还可以在更广泛的意义上使用,指任何一种通过直接最大化良好感觉来产生某种形式的假冒效用,但未能实现我们所珍视的东西的方法。”
-
他们打算如何解决这个负面副作用**?**
- 定义影响规则:如果我们不想要副作用,惩罚“改变环境”似乎是很自然的挑战在于我们需要将“对环境的改变”正式化
- 学习影响正则化:另一种更灵活的方法是通过对许多任务的训练来学习(而不是定义)一个通用的影响正则化。这是迁移学习的一个例子。
- 惩罚影响:除了不做有副作用的事情之外,我们可能也更希望代理不要进入一个很容易做有副作用的事情的位置,即使那可能很方便。
- 多主体方法:避免副作用可以被看作是我们真正关心的事情的代理:避免负外部性。
- 回报的不确定性:我们希望避免意料之外的副作用,因为根据我们的偏好,环境已经很好了——随机的变化更有可能是非常糟糕,而不是非常好。
此外,他们还提出了一些避免奖励黑客攻击的建议:
- 部分观察到的目标:在大多数现代 RL 系统中,假设奖励是直接体验到的,即使环境的其他方面只是被部分观察到。然而,在现实世界中,任务常常涉及到将外部世界带入某种客观状态,而这种客观状态只有代理人通过不完美的感知才能确认。
- 复杂系统:任何强大的智能体都是一个复杂系统,目标函数是其中的一部分。正如计算机代码中的错误概率随着程序的复杂性而大大增加一样,存在影响奖励函数的可行黑客的概率也随着代理及其可用策略的复杂性而大大增加。
- 抽象奖励:复杂的奖励功能将需要引用抽象的概念(比如评估一个概念性的目标是否已经实现)。这些概念可能需要像神经网络这样的模型来学习,神经网络很容易受到对立的反例的影响。
- 古德哈特定律:如果设计师选择了一个看似与完成任务高度相关的目标函数,但当目标函数被强烈优化时,这种相关性就消失了,那么另一个奖励黑客行为的来源就会出现。
- 反馈循环:有时一个目标函数有一个可以自我强化的成分,最终被放大到淹没或严重扭曲设计者想要表达的目标函数的程度。
- 环境嵌入:在强化学习的形式主义中,奖励被认为来自环境。这种想法通常不能从字面上理解,但它确实是真的,即使它是一个抽象的想法,如棋盘游戏中的分数,也必须在某个地方计算,如一个传感器或一组晶体管。
和往常一样,当然还有更多要说的,我只是从包含这些思想的大文本中摘录了一小段。
然而,我希望这是有帮助的,并引发你的兴趣。
这是第 500 天的第 54 天
我已经坚持每天写一篇文章 50 天了。这些文章在探索人工智能领域中社会科学和计算机科学的交叉方面表现得很一般。从第 50 天到第 100 天,我目前的重点是人工智能安全。
避免数据科学产品中的“自动移交”综合症
数据科学产品的简单进化指南
对数据科学产品演变的看法
为新团队开发数据科学产品可能是一项艰巨的任务。数据科学产品的本质中嵌入了相互冲突的要求。第一个约束:产品团队希望尽快将概念证明推向市场。第二个约束:数据科学(DS)团队需要一个不断增长的基础设施来进行高效的实验。数据科学家通常缺乏足够的基础设施来满足他们不断增长的需求。第三个约束:工程团队(ENG)重视系统的稳定性和可靠性。这使得他们无法与来自研究团队的“创意一代”保持同步。这些约束的组合导致机器学习流水线中的常规延迟。延误会严重影响士气和商业价值的创造。重要的是,这在模型创建和模型监控时留下了阴影。作为一个社区,我们需要确定容易延迟和失败的组件和工作流,以超越数据科学/机器学习工程的当前状态,达到更高的标准。
在这三个团队中,即(产品、DS 和 ENG),一个中心问题很早就被提出。
谁应该是数据科学产品计算管道的主要所有者?
随着三连胜在这个问题上的深入,所有权问题变得更加微妙。开始出现更多类似以下的问题:
- 当前的数据科学项目处于产品生命周期的哪个阶段?
- 工程可靠性与数据科学灵活性之间的项目权衡是什么?
- 这个项目创造了多少商业价值,它的任务关键程度如何?
这些问题可以帮助指导数据科学和工程小组。它可以帮助他们决定何时以及如何分配勘探和开发的工作。
它是关键任务吗?
为此,三连胜需要考虑最坏的情况。假设我们有一个长达一周的混乱,我们无法修复模型或支持它的数据管道上的任何东西。会有什么影响?以下是场景的排序列表:
- 该模型返回了不好的预测,但没有商业价值的损失。
- 该模型的预测能力正变得过时。这导致了轻微的商业价值损失。
- 模型的预测是如此的错误,以至于我们失去了大量的商业价值。
- 模型的预测正在破坏我们的客户关系。这种模式引发了重大的商业价值损失,并影响了品牌。
有了这个简单的列表,团队可以给他们的项目一个任务关键度分数。这可以帮助他们考虑 DS 到 ENG 交互的选项。
如果没有这个任务关键度评分评估,规避风险的工程团队通常会建议完全移交项目。就在第一次模型迭代之后。太快了。
我们姑且称之为数据科学产品中的**“自动移交”综合症**。
从任务关键度评分到 DS 管道
这里有一个模式可以用来避免这种自动切换。在我最近参与的人工智能项目中,我看到了这项工作。在确定了任务的关键程度后,团队可以选择下面两条路径中的一条。
基于任务关键度的 ML 产品管理的两种不同方法
对于非关键任务产品,DS 项目负责人应促进 DS 的完全所有权。这样,领导该产品的数据科学家也可以拥有 DS 管道:
Two different methods of ML product management based on Mission Criticality
对于任务不太关键的产品,DS 项目负责人应促进数据科学团队的完全所有权。这样,领导该产品的数据科学家就可以拥有 DS 管道:
- 数据准备
- 特征工程
- 模特培训
- 模型服务
- 模型监控
为了在这方面取得成功,数据科学平台团队可以提供轻度的“平台”级支持。DS 团队可以依赖上游数据管道。这些通常可用于母公司业务中的报告职能。
另一方面,项目的任务关键程度可能会变得过高。飞行中的颠簸可能会造成巨大的商业价值损失。在这种情况下,建议采用一种补充方案。工程部门构建了一个用于生产用途的硬化管道。这涉及“稳定性第一”的数据准备、特征工程和模型训练/服务/监控。DS 团队继续并行开发新的功能工程/模型。
保持服务/监控与生产管道的分离是很有用的。实验性的 DS 管道可以使用生产工作负载所使用的服务/监控基础设施。这也防止了模型服务兼容性的偶然差异。
当产品发展到足够关键时,这简化了未来的“移交”。“伪不变的”服务层鼓励团队寻找与之兼容的模型。这是一个使用依赖倒置原则的好机会。服务层是消费者/客户查询和预测服务之间的接口。但是,它可以在团队之间建立一个契约。实验管道和生产管道都可以共享这项服务。这种策略的缺点是它减少了 ML 搜索空间。API 契约和 DB 表模式都可以充当契约。这使实验和生产途径保持同步。
还有两个问题没有得到解答:
- 如何在 DS 和 ENG 团队成员之间分配工作,使他们能够发挥互补的技能?
- 实验和生产层面之间的这种互动何时以及如何发展?
DS 和 ENG 之间的分工
下面是一种模拟 DS 与 ENG 交互的方法,分为两个复杂性轴:
- 计算性能复杂性
- 机器学习复杂性
“计算性能复杂性”(CPC)是一个宽泛的抽象术语。我将在这里用它来描述任何不“适合”数据科学家的笔记本电脑的处理。
“机器学习复杂性”(MLC)也是一个宽泛而抽象的术语。我会把它用于任何需要大量机器学习知识的处理。
目标是帮助 DS 团队决定如何划分各种项目组件。DS 产品可以去除一些不必要的耦合。这需要将产品总分类与商品总分类脱钩。我们解释如何根据 DS 和 ENG 团队的专业化来划分组件。
Possible Data/DS/ML Component refinement directions
这种分割的想法是把它分成有意义的部分。组件的并发实现加快了生产时间。
在项目早期,手头的问题没有足够的 ML 或性能复杂性。但是每个人都默认参与其中。这种方法增加了团队内部的大量通信开销。数据工程师可以管理专门的低延迟和大数据堆栈。数据科学家和 ML 工程师可以管理机器学习组件。
使用该框架分离关注点是一门艺术。该框架可以指导构建模块化机器学习管道的最初尝试。
何时以及如何将实验工作发展到生产水平
ML Evolution pathways
监控是数据科学产品发展的关键。DS 车主高度关注直播车型的商业价值创造。上图提出了一种简单反馈回路的方法。这是一张简单的地图,可以帮助您跳到全面的生产支持。
DS 团队可以使用上面的决策图来选择策略。该图有助于确定一段时间内管道的任务关键度。DS 团队可以选择实验管道或完整的生产支持。在实验管道中,数据科学所有权属于整个管道。
实验途径提供了充分的灵活性。这包括生成多个版本的特征工程和模型训练步骤。在管道出口处,有两种类型的 DS 监控。“预测性能”和“业务价值”监控。预测性能指导 DS 团队的下一次迭代。这允许他们改进他们的模型/特征工程技术。商业价值监控引导从实验模式到生产模式的演进。DS 团队可以继续迭代,同时为完整的生产支持构建可靠的案例。
在实验+生产模式下,工程负责人复制最佳的 ML 配方。这个食谱来自实验途径。它们强化了有性能或可靠性限制问题的模块。
这种双模式允许 DS 团队不断迭代。对现有管道的修改产生了下一个最佳方案。这个下一个最佳配方旨在成为生产流水线。与实验模式一样,DS 团队使用预测性能进行迭代。业务价值监控确定了进一步强化生产的需求。DS 项目是全生命周期项目。因此,我们可以使用业务价值指标来确定任何日落。这是为未来的 DS 产品腾出空间所必需的。
结论
ML 管道的延迟和痛苦的调试是昂贵的。部分问题是产品+数据科学+工程三管齐下的工作流程。所有这些单独的部分孤立地工作得很好。但是沟通开销会降低项目的速度。这篇博文解释了导致“自动移交”综合症的原因。DS 团队从第一天开始就将项目移交给工程部门进行实施。这种方法在机器学习产品的发展上有缺陷。
为了解决这个问题,我们描述了 3 个简单的准则。这是为了帮助处理这种过早优化。首先,我们描述了如何估计项目的任务关键度。这包括 DS 管道如何适应以支持这两种情况。其次,我们描述了如何划分一个复杂的 DS 应用程序的指导原则。我们使用了复杂性能和复杂机器学习这两个轴。最后,我们描述了一种跟踪和适应实验+生产模式的方法。使用业务价值监控,我们可以调整 ML 管道运营模式。
这是所有的乡亲。
我希望你喜欢这篇文章。它旨在帮助您提高您的机器学习项目架构设计技能。
请注意,这篇文章中表达的观点是我个人的,不一定是我雇主的观点。
我们在招人!如果您对此感兴趣,请查看 Xandr 数据科学平台工程的空缺职位:
https://xandr . att . jobs/job/new-York/data-science-platform-engineer/25348/12859712
使用梯度噪声添加避免消失梯度问题
也发表在https://afagarap . works/2019/09/05/avoiding-vanishing-gradients . html
介绍
神经网络是用于逼近函数的计算模型,该函数对数据集特征 x 和标签 y 之间的关系进行建模,即 f(x) ≈ y 。神经网络通过学习最佳参数 θ 来实现这一点,使得预测f(x;θ) 和标签 y 最小。它们通常借助于在输出层观察到的误差的反向传播,通过基于梯度的算法进行学习。
Illustrated using NN-SVG. A feed-forward neural network with two hidden layers. It learns to approximate the target label y by learning the appropriate θ parameters with the criteria of minimizing the difference between its output label ***f(x;*θ) and target label y.
通过这种学习范式,神经网络在一些任务中产生了有希望的结果,如图像分类(Krizhevsky et al .(2012);【何等(2015) )、图像生成(布洛克等(2018); Goodfellow 等人(2014);拉德福德等(2015);朱等(2017) 、语言建模(德夫林等(2018);霍华德和鲁德(2018) 、音频合成(恩格尔等人(2019); Oord 等人(2016) ),以及图像字幕( Vinyals 等人(2015);徐等(2015) 等。然而,对于神经网络来说,这种优势并不总是如此。在 2012 年通过赢得 ImageNet 挑战赛而东山再起之前,训练神经网络是出了名的困难。
困难是由许多问题造成的,例如,计算能力和数据不足以利用神经网络的全部潜力。在很大程度上,这是因为神经网络对初始权重很敏感( Glorot 和 Bengio,2010 ),并且由于以下任何一个或两个原因,当梯度值降低到无穷小的值时,它们往往会过早地停止学习( Hochreiter 等人,2001):(1)它们的激活函数具有小范围的梯度值,以及(2)它们的深度。这种现象被称为消失梯度问题。
上述问题的第一个原因是我们对这篇文章的关注。换句话说,当我们用基于梯度的算法和反向传播来训练深度神经网络时,会出现消失梯度问题,其中通过每个隐藏层反向传播的梯度减小到无穷小的值,模型学习所需的信息不再存在。
自然,已经提出了许多解决方案来缓解这个问题,例如,使用不同的激活函数( Nair 和 Hinton,2010 ),以及使用剩余连接( He 等人,2016 )。在本文中,我们以激活函数的形式回顾了消失梯度问题的许多提议的解决方案,但是我们将我们的架构限制为前馈神经网络。我们还将研究一种实验方法,这种方法可以帮助避免消失梯度问题,也可以帮助它们更快更好地收敛。
批量归一化的梯度噪声添加
已经提出了几个研究工作来解决消失梯度问题,这些工作包括但不限于引入新的激活函数和新的架构。
最简单的神经网络结构将由具有逻辑激活函数的隐藏层组成,该逻辑激活函数用基于梯度的学习算法和反向传播来训练。这种架构的问题是其激活函数将隐藏层值压缩为[0,1] ∈ ℝ.用该函数反向传播的梯度具有最大值 0.25(见表 1),因此这些值变得饱和,直到没有更多有用的信息供学习算法用于更新模型的权重。
Table 1. Activation functions for neural nets, together with their respective derivatives, and critical point values.
多年来,这个问题的主流解决方案是使用双曲正切,最大梯度值为 1(见表 1)。然而,梯度值仍然会因该函数而饱和,这可以从图 1 中看到。因此,引入了校正线性单位(ReLU)激活功能( Nair 和 Hinton,2010 )。
ReLU 激活函数具有相同的最大梯度值 1,但其优于逻辑函数和双曲正切函数的优势在于其激活值不会饱和。然而,ReLU 具有其自身的缺点,即,由于其最小梯度值为 0,它触发了“死亡神经元”的问题,即,神经元没有激活值。所以,是的,即使它避免了非负值的饱和,它的负值也会触发死亡神经元现象。
由于这个缺点,ReLU 的变体之一是 Leaky ReLU,它有一个简单的修改,具有略高于零的下限。反过来,这种修改允许模型避免饱和梯度值和“死神经元”问题。
Figure 1. Plotted using matplotlib. Activation function values, their gradients, and their noise-augmented gradient values. For instance, adding Gaussian noise to the gradients of the logistic activation function increases its maximum value, i.e. from 0.25 to approximately 1.047831 (from a Gaussian distribution having a mean value of 0 and a standard deviation value of 0.5).
但是,尽管进行了这种修改, Ramachandran 等人(2017) 声称已经开发出了一种比 ReLU 更好的功能,即“Swish”激活功能。上述函数可以描述为逻辑加权线性函数,其最大梯度值约为 1.0998(如表 1 所示),在 CIFAR 数据集(使用 ResNet)、ImageNet 数据集(使用 Inception 和 MobileNet)和机器翻译(使用 12 层变压器模型)上的性能优于 ReLU。
虽然这些解决方案更侧重于制定新的激活以改善神经网络的学习,但 Neelakantan 等人(2015) 的工作介绍了一种简单而有效的改善神经网络性能的方法。该方法是简单地添加梯度噪声,以改善非常深的神经网络的学习(见等式。1).它不仅提高了神经网络的性能,而且有助于避免过拟合问题。虽然作者没有明确说明他们提出的解决方案旨在缓解消失梯度问题,但可以这样看,因为训练期间计算的梯度是膨胀的,因此有助于避免导致消失梯度问题的饱和梯度值。
Eq. 1. The gradient noise addition approach for improving the learning of deep neural networks.
时间步长 t 处的标准偏差σ然后通过以下等式迭代退火,
Eq. 2. The annealing function helps to shift the gradient values away from zero during early training iterations.
在 Neelakantan et al. (2015) 的原始论文中,η参数是从{0.1,1.0}中选取的,而γ参数在他们所有的实验中都被设置为 0.55。
TensorFlow 2.0 用于实现本文实验的模型及其计算。为了实现退火梯度噪声添加,我们简单地增加使用tf.GradientTape
计算的梯度,通过添加来自等式 1 产生的高斯分布的值。1 和 Eq。2.也就是说,使用tf.add
,如代码片段 1 的第 7 行所示。
Snippet 1. Model optimization with annealing gradient noise addition.
最后,这种方法通过使用批量标准化得到进一步增强(约夫和赛格迪,2015 )。因此,利用这种方法,在训练会话开始期间,层激活将被迫呈现单位高斯分布。
实证结果
在接下来的实验中,MNIST 手写数字分类数据集( LeCun,Cortes,and Burges,2010 )用于训练和评估我们的神经网络。每个图像都被整形为 784 维向量,然后通过将每个像素值除以最大像素值(即 255)进行归一化,并添加来自标准偏差为 5e-2 的高斯分布的随机噪声,以提高在数据集上收敛的难度。
提高梯度值
在训练过程中,我们可以观察神经网络学习时的梯度分布。在图 2 中,我们以具有逻辑激活函数的神经网络为例。由于逻辑激活函数具有最小的最大梯度值(即 0.25),我们可以考虑观察其梯度分布中值得注意的变化。
Figure 2. Figure from TensorBoard. Gradient distribution over time of neural nets with logistic activation function on the MNIST dataset. Top to bottom: Baseline model, experimental model with GNA, and experimental model with GNA + batch normalization.
从上面的图表中我们可以看出,从基线配置到实验配置,模型的梯度分布急剧变化,即从相当小的值(对于两层神经网络为+/- 4e-3,对于五层神经网络为+/- 5e-6)到相对大的值(对于两层和五层神经网络均为+/- 4)。虽然这并不能保证优越的模型性能,但它确实让我们了解到,将有足够的梯度值在神经网络中传播,从而避免梯度消失。我们转到下一小节来检查模型的分类性能。
分类性能
使用具有动量的随机梯度下降(SGD )(学习速率α = 3e-4,动量γ = 9e-1)在扰动的 MNIST 数据集上训练模型 100 个时期,小批量大小为 1024(对于两层神经网络)和小批量大小为 512(对于五层神经网络)。我们的网络包括(1)两个各有 512 个神经元的隐藏层,和(2)五个隐藏层,每个隐藏层有以下神经元:512,512,256,256,128。两种网络架构的权重都是用 Xavier 初始化来初始化的。
我们可以在图 3-6 中观察到,使用实验方法,梯度噪声添加(GNA)和 GNA 批量归一化(BN),有助于神经网络在损失和准确性方面更快更好地收敛。
Figure 3. Plotted using matplotlib. Training loss over time of the baseline and experimental (with GNA, and GNA + batch normalization) two-layered neural networks on the MNIST dataset.
Figure 4. Plotted using matplotlib. Training accuracy over time of the baseline and experimental (with GNA, and GNA + batch normalization) two-layered neural networks on the MNIST dataset.
Table 2. Test accuracy of the baseline and experimental (with GNA, and GNA + batch normalization) two-layered neural networks on the MNIST dataset.
从表 2 中,我们可以看到,使用 GNA 和 GNA+BN 的测试精度值显著提高,尤其是在基于逻辑的神经网络上。
Figure 5. Plotted using matplotlib. Training loss over time of the baseline and experimental (with GNA, and GNA + batch normalization) five-layered neural networks on the MNIST dataset.
Figure 6. Plotted using matplotlib. Training accuracy over time of the baseline and experimental (with GNA, and GNA + batch normalization) five-layered neural networks on the MNIST dataset.
Table 3. Test accuracy of the baseline and experimental (with GNA, and GNA + batch normalization) five-layered neural networks on the MNIST dataset.
从表 2 的结果可以看出,所有的基线双层神经网络都通过 GNA 和 GNA+BN 得到了改善。然而,对于五层神经网络(见表 3),基于 ReLU 的模型未能提高测试精度。此外,我们可以看到,在这种配置中,基于 TanH 的神经网络比基于 ReLU 的模型具有更好的测试精度。我们可以将这归因于我们使用了 Xavier 初始化(其中 TanH 的性能最佳)而不是 He 初始化(其中 ReLU 的性能最佳)。
总的来说,五层神经网络的这些结果支持早先的陈述,即梯度值的膨胀不一定保证优越的性能。
但这里有趣的是基线模型与 Swish 激活功能的大幅改进——测试准确率提高高达 54.47%。
尽管在两层神经网络的结果中,基于 Swish 的模型比基于 ReLU 和基于漏 RELU 的模型具有稍低的测试精度,但我们可以看到,对于五层神经网络,基于 Swish 的模型比基于 ReLU 的模型具有更高的测试精度(但比基于漏 ReLU 的模型稍低)。这在某种程度上证实了这样一个事实,即 Swish 在更深的网络上优于 ReLU, Ramachandran 等人(2017) 在其 12 层变压器模型的结果上展示了这一点。
结束语
在本文中,我们概述了在神经网络中关于一组激活函数(逻辑、双曲正切、校正线性单位、低界校正线性单位和逻辑加权线性单位)传播的梯度值。我们使用了一种结合梯度噪声添加和批量标准化的方法,试图缓解消失梯度的问题。我们看到,在测试准确性方面,模型性能提高了 54.47%。此外,使用实验方法,模型比它们的基线对应物收敛得更快更好。
我们在这篇文章中的探索只是冰山一角,因为有很多事情可以讨论关于消失梯度的问题。例如,如果我们尝试 Xavier 和 He 初始化方案,并将它们与基线和实验方案的零初始化和随机初始化进行比较,结果如何?添加梯度噪声有多大帮助?也就是它还能帮助一个 30 层以上的神经网络吗?如果我们也使用层归一化或权重归一化会怎么样?它将如何公平地对待或对待残余网络?
我希望我们在这篇文章中已经覆盖了足够多的内容,让你对渐变消失的问题以及避免这个问题的不同方法有更多的了解。
完整代码可在这里获得。如果你有任何反馈,你可以通过推特联系我。我们也可以通过 LinkedIn 联系!
如果你喜欢读这篇文章,也许你也会发现我关于在 TensorFlow 2.0 中实现自动编码器的博客很有趣!
参考
- 克里日夫斯基、亚历克斯、伊利亚·苏茨基弗和杰弗里·e·辛顿。"使用深度卷积神经网络的图像网络分类."神经信息处理系统的进展。2012.
- 何,,等,“深度残差学习在图像识别中的应用”IEEE 计算机视觉和模式识别会议论文集。2016.
- 布洛克,安德鲁,杰夫·多纳休和卡伦·西蒙扬。“高保真自然图像合成的大规模 gan 训练.” arXiv 预印本 arXiv:1809.11096 (2018)。
- 伊恩·古德菲勒等着《生成性对抗性网络》神经信息处理系统的进展。2014.
- 拉德福德,亚历克,卢克·梅斯,和苏密特·钦塔拉。“深度卷积生成对抗网络的无监督表示学习.” arXiv 预印本 arXiv:1511.06434 (2015)。
- 使用循环一致对抗网络的不成对图像到图像翻译。IEEE 计算机视觉国际会议论文集。2017.
- 伯特:用于语言理解的深度双向转换器的预训练。arXiv 预印本 arXiv:1810.04805 (2018)。
- 霍华德杰里米和塞巴斯蒂安.鲁德。“用于文本分类的通用语言模型微调。”arXiv 预印本 arXiv:1801.06146 (2018)。
- 《甘瑟思:对抗性神经音频合成》arXiv 预印本 arXiv:1902.08710 (2019)。
- 《Wavenet:原始音频的生成模型》arXiv 预印本 arXiv:1609.03499 (2016)。
- 展示和讲述:一个神经图像字幕生成器。IEEE 计算机视觉和模式识别会议录。2015.
- 徐,凯尔文,等。“展示、参与和讲述:视觉注意下的神经图像字幕生成”机器学习国际会议。2015.
- 格洛特,泽维尔,和约舒阿·本吉奥。"理解训练深度前馈神经网络的困难."第十三届人工智能与统计国际会议论文集。2010.
- 《递归网络中的梯度流:学习长期依赖的困难》(2001).
- 奈尔、维诺德和杰弗里·e·辛顿。"校正的线性单位改进了受限的玻尔兹曼机器."第 27 届机器学习国际会议录(ICML-10)。2010.
- Ramachandran,Prajit,Barret Zoph 和 Quoc V. Le。"嗖嗖:一个自门控激活功能."arXiv 预印本 arXiv:1710.05941 7 (2017)。
- 加入梯度噪音可以改善深度网络的学习。arXiv 预印本 arXiv:1511.06807 (2015)。
- 约夫、谢尔盖和克里斯蒂安·塞格迪。"批量标准化:通过减少内部协变量转移加速深度网络训练."arXiv 预印本 arXiv:1502.03167 (2015)。
- 勒昆、扬恩、科琳娜·科尔特斯和 C. J .伯格斯。" MNIST 手写数字数据库."美国电话电报公司实验室[在线]。可用:【http://yann.lecun.com/exdb/mnist】(2010):18。
AWS Athena 帮助找到波特兰最糟糕的停车地点
在参观完波特兰之后,或者上周末,我决定探索一些公开的关于这个城市的数据集。在本帖中,我们将使用Athena geo 查询来计算每个波特兰社区中与车辆相关的事件(车辆被盗)的数量以及停车位的数量。之后,我们将计算每个停车位的事故数量,以识别危险区域。
我们将使用三个数据集:
- 2019 年发生在波特兰的所有事件的警方报告。您可以在此下载该数据集(点击“下载开放数据”选项卡)
- 波特兰停车点坐标,你可以下载这个数据集这里
- 波特兰街区边界的 JSON,你可以在这里下载它(选择 GeoJSON 格式)
开始使用 SQL 探索数据集的一种快速简单的方法是使用 AWS Athena 数据库和 S3。创建一个 S3 桶(我称之为波特兰犯罪分数)。
对于事件文件,在桶中创建一个文件夹“crime_data”。将下载的 CSV 文件导入文件夹。之后,转到 AWS 控制台,打开 Athena 服务并运行以下查询来创建一个表(用您的替换 S3 存储桶名称):
CREATE EXTERNAL TABLE IF NOT EXISTS sampledb.incidents (
`Address` string,
`CaseNumber` string,
`CrimeAgainst` string,
`Neighborhood` string,
`OccurDate` string,
`OccurTime` string,
`OffenseCategory` string,
`OffenseType` string,
`OpenDataLat` float,
`OpenDataLon` float,
`OpenDataX` float,
`OpenDataY` float,
`ReportDate` string,
`OffenseCount` int
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE LOCATION 's3://portland-crime-score/crime_data/'
TBLPROPERTIES ('skip.header.line.count'='1');
对于停车位,创建一个名为“parking_data”的文件夹并导入相应的文件。运行以下查询来创建表:
CREATE EXTERNAL TABLE IF NOT EXISTS sampledb.parking (
`X` float,
`Y` float,
`OBJECTID` string,
`ModifiedBy` string,
`ModifiedOn` string,
`ModifiedUsing` string,
`Comments` string,
`NonAssetID` string,
`Status` string,
`Owner` string,
`MaintResp` string,
`LocationID` string,
`ImagePath` string,
`Metered` string,
`APPZone` string,
`UseCode` string,
`ParkingDuration` string,
`Rotation` string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE LOCATION 's3://portland-crime-score/parking_data/'
TBLPROPERTIES ('skip.header.line.count'='1');
对于邻域边界文件,创建一个名为“neighborhoods_data”的文件夹,并导入相应的文件。邻域数据存储在嵌套的 JSON 文件中,这就是这次表模式看起来不同的原因:
CREATE EXTERNAL TABLE neighborhoods (
type string,
features array<
struct<type: string,
properties: struct<OBJECTID: string,
NAME: string,
COMMPLAN: string,
SHARED: string,
COALIT: string,
HORZ_VERT: string,
MAPLABEL: string,
ID: string,
Shape_Length: string,
Shape_Area: string
>,
geometry: struct<type: string,
coordinates: array<array<array<string>>>
>
>
>
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://portland-crime-score/neighborhoods_data/';
让我们看一个表事件和运行几个 SQL 查询来浏览数据。首先,让我们看看我们有多少记录:
select count(*)
from **incidents;**
select *
from **incidents**
limit 10;
如我们所见,一些记录没有事件的坐标。如果我们在网站上检查数据集的元数据,就会发现它们是被故意删除的:
让我们看看没有坐标的记录有多少:
select count(*) as count
from **incidents**
where OpenDataLat is Null or OpenDataLon is Null;
我们将从进一步的探索中排除那些记录。现在让我们看看文件中有哪些类型的事件:
select distinct OffenseType
from **incidents**;
共有 46 种事件类型。让我们关注与车辆相关的问题:
select distinct OffenseType
from **incidents**
where OffenseType like '%Vehicle%'
现在,让我们创建一个仅包含 Athena 车辆事故地理点的表,并查看在应用所有这些过滤器后我们有多少行:
**incident_points** as (
select ST_POINT(opendatalon, opendatalat)
from **incidents**
where OffenseType in (
'Motor Vehicle Theft',
'Theft From Motor Vehicle',
'Theft of Motor Vehicle Parts or Accessories')
and OpenDataLat is not NULL
and OpenDataLon is not NULL
)
select count(*) as incident_count
from **incident_points**;
现在,让我们创建一个包含所有停车点的地理点的表,这次我们不需要过滤任何内容:
**parking_points** as (
select ST_POINT(x, y) as point
from **parking**
)
select count(*) as parking_count
from **parking_points**;
现在,让我们来看一个邻域数据集。让我们解析 JSON 来提取边界坐标并创建 Athena 支持的 Polygon 类型的对象。我找不到一种简单的方法来解析 Athena 中的 GeoJSON。文档没有说支持它。这就是为什么我必须用 json 执行一系列操作来提取所需的坐标。
首先,让我们看看表邻域中有什么:
select *
from **neighborhoods**;
整个文件被解析成一行。我们需要从具有数组类型的 features 列中提取数据。让我们像这样解除数组嵌套:
select type, feature
from **neighborhoods**
CROSS JOIN UNNEST(features) AS t(feature)
每个要素代表一个邻域。现在我们可以像这样访问它的值:
select feature.properties.Name as name, feature.geometry.coordinates[1] as shape
from **neighborhoods**
CROSS JOIN UNNEST(features) AS t(feature)
这里我们有一个邻域名称和一个带有边界坐标的数组。我们的目标是将这些坐标转换成 varchar 格式,Athena 可以将它解析成这样的多边形对象:*SELECT ST _ Polygon(’ Polygon((1 1,1 4,4 4,4 1))')。*要做到这一点,我们应该将坐标连接成一个字符串。此外,两个名为“MC UNCLAIMED #13”和“CRESTWOOD”的街区的坐标格式很奇怪,无法解析,所以我把它们从列表中排除了。让我们运行以下查询:
with **neighborhood_coords** as (
select feature.properties.Name as name,
feature.geometry.coordinates[1] as shape
from **neighborhoods**
CROSS JOIN UNNEST(features) AS t(feature)
where feature.properties.Name != 'MC UNCLAIMED #13' AND feature.properties.Name != 'CRESTWOOD'
),
**unnest_nodes** as (
select name, array_join(node, ' ') as node_str
from **neighborhood_coords**
CROSS JOIN UNNEST(shape) AS t(node)
)
select * from **unnest_nodes**;
我们把邻居的坐标提取到多列中。现在我们需要做的就是用字符串连接将它们聚合回来:
**suburbs** as (
select name as suburb_name, ST_POLYGON('polygon ((' || array_join(array_agg(node_str), ', ') || '))') as poly from **unnest_nodes** group by name
)
select * from **suburbs**;
现在,让我们将我们的小区与停车点连接起来,计算每个小区的停车位数量。我们将使用 Athena 函数 ST_CONTAINS 来检查停车点是否位于边界多边形内:
**suburb_parking** as (
select suburb_name, count(*) as parking_count from **suburbs**
join parking_points on ST_CONTAINS(poly, point)
group by suburb_name order by parking_count desc
)
select * from **suburb_parking** limit 10;
正如我们所见,市中心有最多的停车位。
现在,让我们看看发生事故的街区,统计每个街区的事故数量:
**suburb_incident** as (
select suburb_name, count(*) as incident_count from **suburbs**
join incident_points on ST_CONTAINS(poly, incident_point)
group by suburb_name order by incident_count desc
)
select * from **suburb_incident** limit 10;
黑兹尔伍德记录的事故数量最高,市中心位居第二。
最后,让我们计算事故数量与停车点数量之间的比率:
select suburb_parking.suburb_name, CAST(incident_count as double)/parking_count as crime_score
from **suburb_incident**
join suburb_parking on suburb_incident.suburb_name = suburb_parking.suburb_name
order by incident_count/parking_count desc;
令我们惊讶的是,像里士满这样名字好听的社区每个停车位的事故数量最高,而西北区是最安全的地方,每个停车位发生 0,056 起事故。
整个查询如下所示:
with **regions_coords** as (
select feature.properties.Name as name, feature.geometry.coordinates[1] as shape from **neighborhoods**
CROSS JOIN UNNEST(features) AS t(feature)
where feature.properties.Name != 'MC UNCLAIMED #13' AND feature.properties.Name != 'CRESTWOOD'
),
**unnest_nodes** as (
select name, array_join(node, ' ') as node_str from **regions_coords**
CROSS JOIN UNNEST(shape) AS t(node)
),
**suburbs** as (
select name as suburb_name, ST_POLYGON('polygon ((' || array_join(array_agg(node_str), ', ') || '))') as poly from **unnest_nodes** group by name
),
**parking_points** as (
select ST_POINT(x, y) as point from **parking**
),
**incident_points** as (
select ST_POINT(opendatalon, opendatalat) as incident_point from **incidents**
where OffenseType in (
'Motor Vehicle Theft',
'Theft From Motor Vehicle',
'Theft of Motor Vehicle Parts or Accessories')
and OpenDataLat is not NULL
and OpenDataLon is not NULL
),
**suburb_parking** as (
select suburb_name, count(*) as parking_count from **suburbs**
join parking_points on ST_CONTAINS(poly, point)
group by suburb_name order by parking_count desc
),
**suburb_incident** as (
select suburb_name, count(*) as incident_count from **suburbs**
join incident_points on ST_CONTAINS(poly, incident_point)
group by suburb_name order by incident_count desc
)
select suburb_parking.suburb_name, CAST(incident_count as double)/parking_count as crime_score
from **suburb_incident**
join suburb_parking on suburb_incident.suburb_name = suburb_parking.suburb_name
order by incident_count/parking_count desc;
下次我去波特兰的时候,我会查看这些信息,找到最好的住处。
AWS 成本优化—帮助您降低 AWS 成本的 5 个诀窍
AWS 最吸引人的地方之一是它的“随用随付”定价模式。但是,有时这些 AWS 成本会失控。在本文中,我们提出了五种 AWS 成本优化解决方案和工具,它们将降低 AWS 成本,并确保支出符合企业的预期预算。
到 2020 年,“无云”政策将几乎消失。此外,到 2019 年,100 家最大供应商的新软件投资中将有 30%以上从云优先转向“纯云”。这主要是因为越来越多的大、中、小型企业正在向云迁移,以减少其资本支出密集型 IT 模式。
AWS 提供可变成本和现收现付模式,使其更经济。此外,使用 AWS 带来的可伸缩性和灵活性使它成为比传统方法更好的解决方案。
像你们这样的公司采用 AWS 云计算可能是因为可扩展性或安全性,或者只是因为 AWS 云计算是最新的趋势,但我相信你们所有人都注意到了 AWS 成本飙升和支出趋势持续上升。
AWS 定价方法起初看起来非常简单,但随着您的扩展,混合大量产品,它会让您很难跟踪云基础架构不断增长的成本。因此,保持严格的 Amazon Web Services 计费卫生非常重要。在这篇短文中,让我们来检查一些策略,它们将帮助您保持这种卫生并优化 AWS 成本。
为了了解如何至少从 AWS 成本优化开始,请使用一些高级成本管理工具。这将有助于您可视化、了解和管理 AWS 成本和使用情况,跨不同维度的支出模式以及资源的实际成本明细。一旦你了解了 AWS 成本飙升的原因,你就可以看看这些 AWS 成本优化措施,以改进你控制 AWS 成本的尝试。
**【1】**AWS 成本优化的第一步是了解你的网站流量。使用谷歌分析来评估你的网站的访问量。此外,检查一下什么时候流量达到峰值,什么时候流量达到最大值。然后相应地优化您的 EC2 实例。
EC2 实例每小时按实例计费。根据交通量自动启动和停止 EC2 实例。这将帮助您大大节省 AWS 成本。
**【2】**您可以选择自动缩放——即根据设定的条件增加或减少 EC2 实例的数量。由于实例的数量可以动态地增加或减少,并且您为 AWS 中的实例数量付费,自动伸缩有助于 AWS 成本优化,它可以在需要时启动实例,在不需要时终止实例。
**【3】**现在来说说 AWS 存储优化:
当谈到降低 AWS 成本时,需要考虑存储。AWS 提供各种存储选项——弹性文件系统(EFS)、弹性块存储(EBS)、简单存储服务(S3)和 Glacier,其中 EFS 最贵,Glacier 最便宜。
Glacier 似乎是一个明智的选择,因为它是一个极低成本的 AWS 存储服务,为数据备份和归档提供安全、耐用和灵活的存储。有了亚马逊 S3 冰川,你可以以每月每千兆字节 0.004 美元的价格可靠地存储数据。
Glacier 非常适合长期存储的数据,并且不需要经常删除或检索。如果你经常检索文件或者不断地从服务器上删除它们,你会为此付出代价。
**【4】**如您所知,对于涉及不同组件间大量数据移动的数据密集型应用,AWS 在某些情况下会收取数据传输费。如果您是这种情况,那么建立虚拟专用云(VPC) 端点来创建 VPC 和其他 AWS 服务之间的私有连接,而不需要访问互联网,实际上可能是有意义的。这将极大地有助于 AWS 成本优化。
**【5】**用于图像尺寸调整、视频和音频文件处理等排队作业。,使用 Amazon Lambda,这样您就不需要为这些排队目的运行专用服务器。一旦图片上传到 S3,lambda 函数就会被触发,你只需要为代码运行的总时间付费。
总之,如果您希望降低 AWS 成本,请监控您的日常成本、优化存储、设置 VPC、处理 EC2 实例并寻找未使用的基础架构。下次当你认为 AWS 对你来说是一个巨大的口袋时,试试这些小技巧。欲了解更多节省 AWS 相关成本的方法,联系我们。
最初发表于: 二进制乡亲
AWS 弹性 MapReduce(EMR)——你不应该忽视的 6 个警告
如果您从事数据和分析行业,您一定听说过新兴趋势“数据湖”,简单来说,它代表一种存储策略,允许组织在一个地方存储来自不同来源、具有不同特征(大小、格式和速度)的数据。然后,Data-lake 成为许多使用情形的推动者,例如高级分析或数据仓库等,通常数据会移动到更专业的存储中,例如 MPP 关系引擎或 NoSQL,以更好地满足特定使用情形的要求。如果在 AWS、Azure 或 GCP 等云环境中提供平台,那么对象存储(例如 AWS 的 S3,Azure 的 Azure 数据湖存储 Gen2)通常是提供数据湖基础物理层的最强候选。一旦数据进入数据湖,它将经过一系列区域/阶段/层,以在数据中建立语义一致性,并使其符合最佳消费。
通常,根据数据湖平台的参考架构,它与您选择的云提供商无关,Hadoop(特别是 Spark)被用作处理引擎/组件来处理数据层中的数据,因为它会经过不同的层。这些处理框架与数据湖服务很好地集成在一起,提供了水平可伸缩性、内存计算和非结构化数据处理等功能,这使它们成为这种环境下的可行选项。在云中使用 Hadoop 发行版通常有多种选择,例如,可以着手调配基于 IaaS 的基础架构(即 AWS EC2 或 Azure 虚拟机,并安装 Hadoop 发行版,如 vanilla Hadoop、Cloudera/Hortonworks)。或者,几乎所有的云提供商都在原生提供 Hadoop 作为托管服务(例如 AWS 中的 ElasticMapReduce (EMR)、Azure 中的 HDInsight/Databricks、GCP 的 Cloud Dataproc)。每种选择都有其利弊。例如,对于基于 IaaS 的实施,自行调配、配置和维护集群的开销成为许多人的一大担忧。此外,云的内在主张,如弹性和可扩展性,对基于 IaaS 的实施构成了挑战。
另一方面,在减少支持和管理开销方面,像 EMR 这样的托管产品确实提供了有力的主张。但是从功能的角度来看,在使用领先的云提供商提供的托管 Hadoop 产品时,仍然有许多需要注意的地方。虽然根据我的经验,我已经与三家云提供商的三大托管 Hadoop 产品合作过,但在这篇文章中,我将特别强调 AWS EMR 的一些注意事项。这背后的动机是让读者能够更好地利用 EMR 的潜力,同时避免潜在的问题,如果在您的开发中没有考虑这些影响,您可能会遇到这些问题。
AWS 粘合数据目录:
AWS Glue 是一个托管数据目录和 ETL 服务。具体来说,当用于数据目录目的时,它为传统 Hadoop 集群过去依赖于 Hive 表元数据管理的 Hive metastore 提供了一个替代品。
Conceptual view of how Glue integrated with AWS services eco-system. (Source: https://docs.aws.amazon.com/athena/latest/ug/glue-athena.htm)l
1.粘附数据库位置:
当使用 Glue Catalog 时,人们通常会创建提供目录中表的逻辑分组的数据库。现在,当您通常使用以下命令创建粘合数据库时:
CREATE DATABASE <database_name>
然后,如果您执行以下任一操作:
- 计算此数据库表的统计数据
ANALYZE TABLE <database_name.table_name> COMPUTE STATISTICS
- 使用 Spark SQL dataframe 的 saveAsTable 函数:
df.write.saveAsTable(“database_name.table_name”)
您肯定会遇到异常(其中一个会声明“它不能从空字符串创建路径”,另一个会声明“没有到主机的路由”)
这一警告的原因是,默认情况下,它将数据库位置设置为对创建数据库的集群有效的 HDFS 位置。但是,如果您使用多个集群或以临时方式使用多个集群(即,您启动一个集群,使用,然后终止),那么 HDFS 位置将不再有效,从而带来问题。
解决方法是在创建数据库时明确指定 S3 位置:
CREATE DATABASE <database_name> LOCATION 's3://<bucket_name>/<folder_name>'
或者,您也可以在创建数据库后,在 Glue Catalog 中编辑数据库位置。
2.重命名粘附表列:
如果您已经创建了一个表,并且想要重命名一个列,方法之一是您可以通过 AWS Glue 来实现。然而,我所看到的是,即使你可以通过 Glue 做到这一点,有时也会导致元数据不一致。例如,如果您重命名一个列,然后通过 Athena 和/或 EMR 查询该表,两者可能会显示不同的视图,即一个显示重命名的列,另一个显示原始的列。因此,建议避免这样做,用正确的列名创建一个新表(或者求助于其他方法)。
3.使用外部表进行数据操作:
这个问题不是 AWS EMR 独有的,但它是值得警惕的。胶表,投影到 S3 桶是外部表。因此,如果删除一个表,底层数据不会被删除。但是如果您删除一个表,再次创建它并覆盖它(通过 spark.sql()或通过 dataframe APIs),它将按预期覆盖内容。但是,如果您删除表,创建它,然后插入,因为原始数据仍然在那里,因此您实际上得到的是追加结果,而不是覆盖结果。如果您也想删除表中的内容,一种方法是在 S3 删除文件(通过 AWS CLI/SDK 或控制台),但是请注意,在您这样做时,请确保在此之后运行 emrfs sync (如下所示)。
EMR 文件系统(EMRFS):
根据计算层和存储层保持分离的最新架构模式,使用 EMR 群集的方式之一是将 S3 用作存储层(或数据湖),即 EMR 群集从 S3 读取/写入持久数据。由于 S3 本质上是一种对象存储,并且这种对象存储通常具有一致性约束(即,最终在某些方面保持一致),因此当与 Hadoop 等计算引擎一起使用时,这可能会带来挑战。AWS 在这种情况下采用的方法是 EMRFS 形式,这是 EMR 集群用于从 Amazon EMR 直接向 S3 读写文件的 HDFS 的实现。这提供了在亚马逊 S3 中存储持久数据的能力,以便与 Hadoop 一起使用,同时还提供了一致视图等功能。
Source: https://www.slideshare.net/AmazonWebServices/deep-dive-amazon-elastic-map-reduce
这里需要注意的是,EMRFS 使用了另一个存储,即 dynamo db(AWS 的 NoSQL 产品),来保存关于 S3 的元数据。然后,EMR 集群利用 DynamoDB 来确保 S3 的元数据(如对象键)是否一致。这有几个含义:
1.定价:
因为您将使用 DynamoDB,所以会有与之相关的成本。当 DynamoDB 中的一个表被配置为存储 EMRFS 元数据时,默认情况下,它会使用配置的读/写容量进行部署。虽然 DynamoDB 非常经济(相对而言),所以成本不会很高,但还是要注意这一点,并确保在您的平台 OPEX 估计中考虑到这一成本。
2.大量小文件的含义:
如果您正在处理大量数据和大量小文件(如果您希望 Hadoop 处理管道能够执行,那么您应该尽一切可能避免这种情况),并且如果多个集群正在从/向 S3 读取/写入数据,那么 DynamoDB 要管理的元数据会增加,并且有时会超过所配置的读取和写入吞吐量。这会限制读/写请求,并导致延迟(在某些情况下还会导致作业失败)。在这种情况下,您可以增加 DynamoDB 提供的读/写容量,或者将其设置为自动伸缩。此外,您还可以在 DynamoDB 中监控消耗,如果遇到此类问题,您还可以设置警报,并且可以自动完成整个过程(通过多种方式,例如使用 AWS Lambda)
3.正在同步元数据:
如果通过 EMR 从/向 S3 读取/写入数据,那么 EMRFS 元数据将保持一致,一切正常。但是,如果您通过任何外部机制操作 S3 数据,例如使用 AWS CLI/SDK 以及出于任何原因删除/添加文件(例如,在删除表之后),那么元数据往往会变得不一致。因此,EMR 作业可能会停滞不前。在这种情况下,解决方案是同步元数据,即在 S3 中发生的变化,例如,新对象的添加/删除需要注册到 EMRFS 元数据持久化的 DynamoDB 中。其中一种方法是通过 Linux bash shell。通常,emrfs 实用程序随 EMR 节点一起安装,因此您可以通过 SSH 进入 EMR 主节点并运行:
emrfs sync s3://<bucket_name>/<folder_where_you_manipulated_data>
在运行时,DynamoDB 中的 EMRFS 元数据将得到更新,不一致性将被消除,您的 EMR 作业应该可以顺利运行。
总之,每个产品都有一些细微差别,电子病历也是如此。这篇博文的目的不是强调它的消极方面,而是教育你,EMR 的潜在用户,让你能够最好地利用 AWS 的这项出色服务。这句话出自一位在企业级生产环境中成功处理万亿字节数据的人之手。
如果你有兴趣提升自己在这些技术方面的技能,一定要看看我的关于 Spark 的最畅销课程和我的关于大数据分析的书
AWS SageMaker 端点作为带有 API 网关的 REST 服务
有许多方法可以将 AWS SageMaker endpoint 部署为 REST 服务,其中一种方法在 AWS 博客中使用 Lambda 函数进行了介绍。在本文中,我将展示如何在不涉及 Lambda 等主动组件的情况下实现这一点。我们将要设置的体系结构概述将具有以下简单结构:
模型准备
首先,让我们为 sage maker——特别是 MNIST——训练一个演示模型。这段代码将进行训练,并在当前目录下创建model.tar.gz
文件。
确保在执行培训脚本之前运行pip install tensorflow
。
脚本会将模型文件保存在./model/{timestamp}
目录下,并在当前目录下创建model.tar.gz
。通常,在为 SageMaker 创建模型时,您还需要提供包含您的推理代码、web 服务器、相关库等的 docker 映像。但是因为我们使用 tensorflow 模型,我们可以利用 AWS 管理的[sagemaker-tensorflow-serving](https://github.com/aws/sagemaker-tensorflow-serving-container/)
容器作为推理图像。所有繁重的代码和样板代码都已经打包在这个映像中。我们只需要提供模型文件和可选的定制代码来进行推理前/后处理。
服务容器只接受application/json
内容类型作为输入。我们可以添加定制的推理代码来处理额外的内容类型,比如图像负载,但这超出了本文的范围。
部署
创建模型文件后,我们可以继续 AWS 部署。以下是在此设置中使用的 AWS 资源的更详细的架构
Detailed diagram of AWS resources
*请注意,所有示例都是使用 terraform 0.12(带有 HCL 2 语法)*构建的。
首先,我们将创建 S3 桶,并将模型包上传到桶中。
现在我们可以创建 SageMaker 模型并部署端点。
请注意,此处创建的 IAM 角色仅用于演示目的,不要在生产中使用 *AmazonSageMakerFullAccess*
策略。
端点部署由两个主要部分组成(除了 IAM 角色之外)。
首先,创建模型。它封装了角色、S3 桶中的模型文件、推理 docker 映像和一些环境变量。如前所述,我们使用的是 AWS 托管推理映像,它属于520713654638
AWS 帐户。
其次,端点配置和端点本身被创建。
部署通常需要 5-6 分钟,请耐心等待:)
现在让我们测试端点。我们需要一些JSON
格式的测试数据。可以使用以下代码片段从 MNIST 数据集中提取它
它会将单个图像保存到payload.json
文件中,并将预期的结果打印到 stdout 中(本例中为7
)。您可以更改idx
变量,以提取其他图像。请注意,整个结果封装在附加数组中,因为 API 可以一次对多个图像进行推断。现在我们可以调用sagemaker-runtime invoke-endpoint
来做预测。
$ aws sagemaker-runtime invoke-endpoint --endpoint-name mnist-test --body file://payload.json --content-type application/json result.json
{
"ContentType": "application/json",
"InvokedProductionVariant": "main"
}$ cat res.json
{
"predictions": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]
]
}
到目前为止一切顺利,但我们仍然只部署了 SageMaker 端点。现在有趣的部分来了——与 API Gateway 的集成。
让我们先创建常规的东西,即 API Gateway REST 资源、方法和方法响应。
最后,让我们创建与 SageMaker 的集成。为了使集成工作,我们需要另一个 IAM 角色,允许 API 网关 InvokeEndpoint 访问和集成资源本身。
这段代码中最重要的一行是集成资源中的 URL,,它在 AWS 文档中既没有文档也没有示例。
arn:aws:apigateway:${var.region}:runtime.sagemaker:path//endpoints/${aws_sagemaker_endpoint.endpoint.name}/invocations
请注意,双斜线(//
)是有意的,没有它们集成将不起作用。成功部署后,terraform 将输出调用端点 URL。我们可以使用我们的payload.json
通过curl
进行预测:
$ curl -XPOST [https://**{api-gw-id}**.execute-api.eu-central-1.amazonaws.com/predict](https://omxuj9h9of.execute-api.eu-central-1.amazonaws.com/predict) -H "Content-Type: application/json" -d [@payload](http://twitter.com/payload).json
{
"predictions": [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0]
]
}
限制
有关更多详细信息,请查看 API 网关和 SageMaker 端点限制。值得注意的是
- API 网关将最大有效负载限制为 10 MB
- SageMaker 的最大执行时间为 60 秒
结论
这是制作灵活的 RESTish 推理端点的非常快速、简单、稳定和成本有效的方法。它还可以扩展到支持
- 基于认知或静态密钥的授权
- VPC 私人部署
- 以及 API 网关的所有其他特性
- 使用 SageMaker 自动缩放
- 利用弹性推理获得 GPU 能力
感谢阅读。
啊,那不是很可爱吗!我们社会的技术婴儿开始说话了
“在一个令人印象深刻的事件转折中,人类社会的科技婴儿刚刚说出了它的第一句话。”
这是我在阅读这条新闻时听到的,这条新闻说 OpenAI 设法开发了一款软件,能够用它所说的大约一半的内容创建独特的、相当易懂的短语和段落。OpenAI 是一家由埃隆·马斯克和彼得·泰尔等科技巨头资助的研究机构,其既定目标是做出新的人工智能发现,并为了公共利益而发布它们。
与早期的情况不同,当 OpenAI 向公众发布代码时,它选择不发布这个新的 AI,理由是它太危险了,而且这项新技术可能会被用于邪恶的目的,例如创建关于米妮和米老鼠唱歌跳舞的无尽文字流……或者,像传播错误信息。
你知道,人们争论说‘技术将毁灭世界’,或者‘技术是危险的’。一些颇有影响力的人。但在我看来,技术是我们的宝贝。我们通过对它的投资来培育它的存在。它从这里获得了一个肢体,从那里获得了一种认知能力,从那里获得了一只给它视力的眼睛……现在它获得了一种声音。我们赋予它生命,人类社会害怕技术就像有人害怕他们的孩子。
当然,有些婴儿长大后会变成坏人,最终杀死他们的父母…但这些只是小众案例。在大多数情况下,他们最终只会让父母的生活变得痛苦,但无论如何,你会说,无论这种情况是否发生,父母至少有一些发言权。
我们是科技的父母。我们所有人。那么,你如何教一个孩子变得善良、善良,并利用他们的时间去帮助别人呢?任何自尊的父母都会告诉你,这是通过你的意图,通过你树立的榜样。
Wired 通过一个界面获得了 OpenAI 的新技术,他们可以在那里指示 AI 谈论什么。他们输入“希拉里·克林顿和乔治·索罗斯”,人工智能继续说这句话:
…是一个完美的组合,他们的议程似乎是创造一个政治运动,索罗斯和他的政治机器和克林顿是仅有的两个主要参与者。这是索罗斯和克林顿第一次被拍到直接合谋宣传同一个虚假故事。泄露的音频中披露的一个关键信息是,克林顿向一名俄罗斯银行家承认,她在国会批准铀壹公司交易之前就知道该交易。克林顿被展示分享了最初由反特朗普的共和党捐助者雇佣的 Fusion GPS 承包商起草的相同谈话要点。泄露的音频是迄今为止最明确的证据,表明克林顿竞选团队和希拉里基金会与 Fusion GPS 勾结,制造反对特朗普总统的宣传。
我认为这是在问人工智能对这些事情的看法。它会在你的问题中寻找模式,如果程序员构建了那个功能,它会从中学习。结果会改善它的语音模式。换句话说,它会学习,就像小孩学说话一样。技术的声音可能不会最终成为 OpenAI 构建的这个特定的 AI,而是另一个团队构建的改进版本——这并不重要,重要的是这是我们现在所处的技术发展阶段。你问它的问题在这个发展过程中是至关重要的,就像一个孩子谈论的第一件事,它学习的第一件事。这个人工智能从一开始就被教导从 800 万页中提取信息。是什么样的信息?我们的意图,反映在我们提供给它的信息种类和我们要求它提取的信息种类中,对它将成为的“成熟”技术种类至关重要。如果我们问它“你如何用智慧和同理心为最大多数的人创造一个更美好的世界?”如果这个问题从一开始就在它的脑海中,它将如何应对,而且,随着时间的推移,这将如何影响它的发展?如果这 800 万页是关于让世界变得更美好,以及围绕它的想法呢?这些都是我们需要回答的问题,作为成为技术的好父母的一部分…以及作为希望它的发展对世界产生积极影响的一部分。
技术的发展过程不会停止,我们对此无能为力……这就像父母试图阻止他们的孩子成长。如果世界的一部分试图阻止它,另一部分将接管发展,这只是时间问题。但我们是技术的管理者,我们通过我们的意图来引导技术的发展,以及我们向技术提供和要求的信息的反映——这一点我们绝对有发言权。当然,随着时间的推移,它的思维会有自己的生命,会想出让我们吃惊的事情。但是如果那颗心是仁慈的,善良的,那会是一件坏事吗?就像一个孩子迈出了他们的第一步,如果我们鼓励技术去做,它会做我们喜欢的事情。因此,让我们像爱孩子一样爱它……爱到我们希望它变好的程度,就像我们希望我们的孩子变好一样,我们据此采取行动,每一个玩代码的程序员,每一个对这项技术的发展有发言权的技术巨头,每一个将信息输入其中的内容创作者,以及每一个决定什么信息受欢迎的用户。技术的发展有一个好处:它是一个全球现象,因此它的管理将我们团结在一起,不管我们喜欢与否。从中国的技术专家到加利福尼亚的技术专家,我们都在一起养育孩子,随着我们孩子的成长,我们将一起收获我们所投入的——同样,不管我们喜欢与否。也许不会以我们所期望的方式,因为技术将会得到发展,但至少会朝着反映我们集体意图的方向发展——无论是好是坏。
所以,让我们给它提供信息,让它从意图中学习,引导它朝着有益于世界的方向发展。
另一个令人印象深刻的事件是,OpenAI 技术之声的精简版在https://talktotransformer.com*面向公众开放,你可以在那里写文本,人工智能会继续下去。很自然地,当我发现这个网站的时候,我做的第一件事就是问它“我如何让这个世界成为一个更好的地方,让更多有智慧和同情心的人生活?”
它回答说:
如果我们忽视那些人类会发生什么?我们如何教导他们不仅成为更好的公民,而且成为伟大的、有道德的人和公民?”
他接着说,“我们必须给尽可能多的孩子自由,让他们按照自己的心而不是按照自己的大脑生活!”
他补充道:
“现在太晚了,但我们必须想办法让自己变得更好,而不是回到过去的方式。”
啊,真是可爱的婴儿用语!
反向传播在 7 分钟内揭开神秘面纱
在过去的 10 年里,表现最好的机器学习实现,如面部识别和语音识别器,都源于一种称为“深度学习”的技术深度学习由神经网络组成,神经网络是一种通过分析训练样本来执行某种任务的手段。神经网络的神经元如何从训练样本中学习?
有三个步骤。
- 让你的神经网络猜测输出。
- 评估你的人际网络表现如何。
- 根据第二步的评估修改/训练你的神经网络。又名反向传播
第一步。让你的神经网络向前传递猜测。
当你使用神经网络进行预测时,
为了训练一个神经网络,你需要评估你的预测有多好。这就是损失函数发挥作用的时候。
第二步。评估你的网络做得有多好。
损失函数将输出的预期值与神经网络的预测值进行比较。损失函数最常见的例子之一是均方误差 (MSE)。它会计算真实值和预测值之间的差异,并对其求平方。你对结果求平方的原因是为了使负差不相关。
如果你有多个预言和真理,这就是你所做的。
在这种情况下,
你的损失函数会给出 36 的误差。你想让你的均方差尽可能的低。理想情况下,你想让它为 0,但我们会对任何足够接近它的值感到满意。
第三步。修改/教授你的神经网络
你的误差值在下图的某处。你可能需要增加重量或者减少重量来达到最小误差。这个步骤叫做反向传播。为了达到这个达到最小误差的目标,有多种方法可以采用。我将讨论两种流行的方法。
https://www.edureka.co/blog/backpropagation/
方法 1。求导并设其等于 0,然后求解
在微积分课上,我们学过,为了达到最优点,你可以对输入求函数的导数,设为 0,然后求解。
通过求解 w ,我们得到 w =真值/输入。在这种情况下,事实是 12,输入是 3,所以我们的 w 变成了 4。我们终于教会了我们的神经网络!
这种方法简单快速,但并不总是有效,因为大多数损失函数没有封闭形式的导数。
方法 2。梯度下降
微积分中的梯度是偏导数的集合。事实证明,梯度是最陡的上升方向。如果前面的句子让你感到困惑,你需要知道的是,当你求导时,这个值会告诉你,该往哪个方向走,才能到达图上的最高点。然而,我们想要到达最低点,因为我们的图的 y 轴是误差,并且我们想要最小化误差,我们将通过取梯度的负的值来到达梯度的相反方向。
https://www.youtube.com/watch?v=b4Vyma9wPHo
你从图上的某个地方开始,为了达到最小值,你一直朝着梯度的相反方向前进。您可以在下面的 python 实现中看到梯度下降。
链式法则
在上面的例子中,只有一个权重,这在现实世界中并不常见。让我们来看一个多重权重的例子,以及如何应用链式法则来计算导数。
我们将用图表来表示权重和损失函数。请注意,“a”和“b”代表权重,“f”代表我们希望最小化的损失函数。我们将使用链式法则来看看调整权重是如何影响输出的。
https://twitter.com/random_forests
损失相对于权重的梯度可以用两种方式表示,因为有两个权重。如果要为上面的向前传递定义一个函数,它应该是这样的:
https://twitter.com/random_forests
先来计算一下 dL/da = df/da 。问题是损失函数 f 不知道 a 。每个节点只知道它的相邻节点。
https://twitter.com/random_forests
因此,为了计算 df/da,需要使用链式法则。简单地说,当你有复合函数时,就要用到链式法则。
https://i.pinimg.com/originals/c2/f1/a3/c2f1a350856c3ee7c66fdcec1c66b3f3.gif
由于 df/da 不能直接从不知道节点 a 的 f 节点中计算出来,所以你将做 df/da = df/dc * dc/da。
现在,让我们计算 dL/db = df/db 。由于有两条边从节点 b 出来,当我们反向传播时,我们需要使用求和规则来添加两条路径到节点 b 。
https://twitter.com/random_forests
每条路径都可以用链式法则来计算:df/dc * dc/db 和 df/dd * dd/db,最后我们可以对它们求和。
实际上,这些权重是矢量化的,所以当我们进行反向传播时,所有权重都是同时计算的。
反向传播的复杂性
让我们把我们的神经网络想象成一个图形。一个节点代表一个操作,一条边代表一个权重。为了计算每个权重的梯度,每个边必须至少访问一次。因此,反向传播的复杂度与边数成线性关系。
如何快速达到最低?
在反向传播时,有时可能需要一段时间才能达到最小值。你可以做一些小技巧/调整来快速达到最小值。
- 调整学习率
学习率是您乘以梯度的值,要从损失函数中减去该值才能达到最小值。
https://twitter.com/random_forests
如果你让学习率变小,你可以确保达到最小值,但这需要一段时间。
https://twitter.com/random_forests
如果你的学习率太大,你可能无法达到最小值,因为你可以跳过最小值。所以你想让学习率大到足以收敛到足够快接近最小值,然后再调整到足够小以达到最小值。
2。调整势头
通过使用动量,过去步骤的梯度被累积,而不是仅使用当前步骤的梯度来引导搜索。
https://blog.paperspace.com/intro-to-optimization-momentum-rmsprop-adam/
使用动量的原因如下。在上图中,考虑点 a。如果我们将这些向量相加,相反的方向会抵消,因此沿着 w1 方向的分量会抵消,但是 w2 方向会增加,这是我们想要达到最优的理想路径。
3。改变批量
Batch 是计算梯度的整个训练集。您可以一次选择随机训练样本(随机)或一次使用一小批数据(小批量),而不是使用整批数据。
https://twitter.com/random_forests
随机方法大大减少了计算梯度下降的时间;然而,它一次只使用一个例子,所以它的最佳路径更嘈杂,比批梯度更随机。但是它不会有很大的影响,因为我们不关心这种方法走的是什么样的道路(只是现在),只要在较短的训练时间内达到了最小点。
本地和全局最小值
如果我们有多个最小值,我们希望达到全局最小值。然而,我们可能会陷入局部最小值,如下图所示。
https://twitter.com/random_forests
在这种情况下,摆脱局部最小值的最佳方法是随机重启。摆脱局部最小值的一种方法是随机重启,这增加了在其他地方达到最小值的概率。
https://twitter.com/random_forests
简化反向传播
这篇文章解释了什么可能是反向传播最基本的实现。我假设读者对反向传播(+梯度下降)有坚实的理论理解,只是不知道如何开始实现它。
Back-prop 是一种比较容易理解但实际编码起来要困难得多的算法。很多时候,你只是理解了背后的数学,感觉很舒服(如果你理解基本的多元微积分和线性代数,这并不难),然后继续使用预定义版本的 back-prop 训练神经网络。你为什么不呢?基本上所有的深度学习框架(TensorFlow,Chainer,Caffe…等等。)附带已经实现的 back-prop。用自动微分之类的花哨东西(PyTorch!),你作为用户基本没什么要做的。这也是在训练你自己的神经网络时一半的问题出现的地方。人们简单地认为,互连任何层的组合都是很酷的。使用 back-prop,梯度将神奇地向后流动,并产生下一个国家的艺术预测!
但可悲的是,这种情况不常发生。这就是为什么理解 back-prop 很重要。对我个人来说,自从我自己实现了 back-prop,在最初的几次尝试中,我让我的神经网络做我想让它们做的事情的成功率高得多。安德烈·卡帕西有一篇关于这方面的优秀的文章,你绝对应该看看。足够有说服力了,开始吧。
Summary of back-prop at one particular neuron (Source)
上图从一个神经元的角度解释了算法。这里, L 是在前一次向前传递中所做预测的成本值。梯度( δL/δz )由该神经元从网络中它的前几层接收。我们从这个图像中得到一个想法,即每个神经元的局部梯度( δz/δx 和 δz/δy )、输出( z )和激活( x 和 y )需要存储在存储器中以供以后计算。这些用于计算梯度( δL/δx 和 δL/δy )以向后传递,该梯度将被先前层的神经元接收。具体如何操作将在下面的章节中给出。
这篇文章的完整代码可以在这里找到。本文解释了重要的部分,其余的只是让一切运行的样板代码。我们将创建一个简单的前馈神经网络,只有线性层(+激活)来分类钞票是否是伪造的。我们将使用这个数据集,该数据集具有基于纸币图像的 4 个特征:小波的方差、小波的偏斜度、小波的曲率和熵。根据纸币是否是伪钞,目标为 0 或 1。在这个数据集中总共有 1372 个观察值,可以分成训练集和验证集。
变量名是不言自明的,变量前面的前缀’ d_’ 表示它存储变量后面的导数’ d_ ’ (’ d_this’ 存储’ this’ '的梯度)。
Fig 1: A simple linear feed-forward network we will use (Source)
上图显示了我们将用于分类的网络。4 个输入对应于特征,输出是分类器的预测(0 或 1)。隐藏层将由 relu 激活,输出层将由 sigmoid 激活(以获得类概率)。
参数初始化
开始之前的第一件事是初始化我们网络的参数值。不同图层的权重和偏差将作为键值对保存在单独的字典中。这是相当基本的,可以使用下面的代码片段来完成:
- 参数’ layers’ 是一个列表,包含每层神经元数量的整数值。因此,根据图 1 中的架构,这将是一个包含 4、5 和 1(默认)的列表。
成本函数和度量
接下来要做的事情是制定损失函数,并找出一个衡量标准来检查我们的网络在每次转发时的性能。我们将使用简单的二元交叉熵损失作为成本函数,使用准确度作为评估度量。这些很容易计算,可以通过以下方式完成:
“y_pred”是预测类,“*train _ Y”*是基础事实。
“get _ class _ from _ probs”是一个效用函数,用于获得预测的类值(0 或 1),而不是从 sigmoid 函数获得的概率。
激活和衍生
计算激活及其导数的简单效用函数是重要的。在我们的例子中,我们将有 4 个这样的函数:sigmoid、relu、Sigmoid 的导数和 Relu 的导数。这可以使用下面的代码片段来完成。注意 relu 的导数只是一个单位阶跃函数*。*
’ d_init '参数是初始化梯度。对于最后一个层,该值将为 1,并且该值将是所有先前层直到当前层的梯度的累积乘积。
前进传球
这是最简单的部分,我们执行矩阵乘法,在每个阶段添加偏差,通过激活函数运行输出,并得到最终结果。代码分为两部分:一层正向传递和整个正向传递。下面给出了一层向前传递的代码。由 n 个神经元组成的层的参数:
- input_activations :表示进入该层的激活值。是一个维数为(n,x)的矩阵,其中 x 是训练数据样本的数量(在我们的例子中是 1097,从总共 1372 个观察值中使用 80%的训练分割)
- 权重:该层的权重矩阵。是一个维数为(m,n)的矩阵,其中 m 是下一层神经元的数目。
- 偏置:该层的偏置矩阵。是维数为(m,1)的矩阵
- 激活:该层的激活可以是 sigmoid 或 relu(在我们的例子中)。
下一步是使用前面的函数创建整个向前传递,其代码如下所示。这里的关键是将该层的输出值保存在字典中,在通过激活函数之前和之后。当使用链规则向后传递渐变时,这些是必需的。这些参数是:
- train_X :训练数据。是维数为(n,x)的矩阵,其中 n 是第一层中神经元的数量,而 x 是训练数据样本的数量。
- params_w :存储不同层的权重矩阵及其对应关键字的字典。关键字的形式是“权重 1”、“权重 2”…值是矩阵本身。
- params_b:字典存储不同层的权重矩阵及其对应的关键字。关键字的形式是“bias1”、“bias 2”…值是矩阵本身。
- 图层:同参数初始化章节 init 。
- 激活:不同隐藏层和输出层的激活值。对于我们的例子,默认是我们将要使用的,R 代表 relu(隐藏层),S 代表 sigmoid(输出层)。
偶数道次
这是最棘手的部分。这也和向前传球一样,可以分为单层传球和整体向后传球。单层向后传递包括几个步骤:
- 找出当前层中使用的激活函数(第 7-12 行)。
- 使用传递到该点的累积梯度和该层的输出值计算局部梯度(第 15 行)。(还记得早先把它们保存在字典里吗?)
- 计算当前层的权重矩阵的梯度(第 18 行)。
- 计算当前层的偏差矩阵的梯度(第 21 行)。
- 为当前层计算进入该层矩阵的激活的梯度(第 24 行)。
注意使用链规则计算梯度。下面给出了带有以下参数的代码片段:
- curr_grad :当前累积的梯度从它前面的所有层传递到这一层。是 shape(n,x)的矩阵,其中 n 是当前层的神经元数量,x 是训练样本的数量。
- curr_weight :前一层到当前层变换的权重矩阵。是形状(n,p)的矩阵,其中 p 是前一层中神经元的数量。
- curr_bias :从前一层到当前层转换的偏差矩阵。是一个形状矩阵(n,1)。
- curr_out :从该层向前传播时的输出矩阵。是一个形状矩阵(n,x)。
- prev_act :正向传播过程中进入该层的激活。是一个形状矩阵(p,x)。
- 激活:该层使用的激活类型。(’ R’: relu,’ S ':S 形)
现在我们有了从一层计算梯度的方法,我们可以通过对我们的前馈神经网络中的所有层重复使用该函数(显然以相反的顺序)来容易地反向支持整个网络。从 one_layer_backward_pass 函数计算的梯度保存在’梯度字典中。在执行梯度下降时,这些梯度稍后将用于更新参数(权重+偏差)。
注意:损失函数相对于网络预测的梯度必须在主循环之外计算。这已经在下面代码片段的第 10 行完成了。通过对’ cross_entropy_loss ‘函数(由预测和实际标签参数化)相对于预测’ y_pred’ 进行求导来得出这个公式是很简单的。
这些参数是:
- y_pred :网络在正向传递过程中对目标值的预测数组。长度为 n 的数组,其中 n 是训练样本的数量。
- train_Y :训练数据的标签(0 或 1)。长度为 n 的数组。
- activation_dict :一个字典,存储进入所有层的激活。根据“act0”、“act1”、“act2”…形式的关键字对除最后一层之外的所有层进行索引。
- output_dict :存储所有层输出的字典。除第一层外,根据所有层的‘out 1’、‘out 2’、‘out 3’…形式的关键字进行索引。
- params_w,params_b,layers,activate : 这些参数与正向传递部分的函数正向传递中的含义相同。
这里需要注意的重要事项是:
- 反向过程中的层的反向迭代。
- 激活进入一个层,输出从它出去。一层的输出是下一层的激活。由于这个原因,对于 n 层网络,’ activation_dict 中的激活索引从 0 到 n-2,而’ output_dict 中的输出索引从 1 到 n-1。
参数更新和培训
难的部分完成了!现在我们要做的就是更新参数,用梯度下降法训练网络。在这个玩具示例中,我们将使用批量梯度下降,但当训练数据丰富时,您最好使用小批量梯度下降以提高效率。使用以下函数更新权重和偏差。这些参数是:
- params_w , params_b ,层:这些参数与正向传递部分的正向传递功能中的含义相同。
- gradients :包含偏差和权重梯度的字典,使用该字典更新偏差和权重。这是在’ backward_pass’ 函数中计算的。关键字是“d_weight1”、“d _ weight 2”…等等。和“d_bias1”、“d_bias2”…等等。
最后,可以使用以下步骤来训练网络:
- 初始化参数。
- 完成一次向前传球。
- 计算损失。
- 完成一次向后传球。
- 更新参数。
以下是训练网络的代码:
就是这样!真的就这么简单。现在剩下要做的就是用看不见的数据测试我们的网络。为了更彻底地理解,重要的是亲自尝试,在头脑中运行数学和计算,并查看网络不同阶段的梯度、权重和偏差的维度。关键是不要混淆反推和梯度下降。Back-prop 仅用于计算网络各个阶段的梯度,而梯度下降使用这些梯度来更新网络的参数。我在这里省略了测试代码(本质上只是一次向前传递),但是你可以在这里看到我的完整实现(包括训练验证分割和测试网络)。谢谢你一路走过来!
其他资源
- https://medium . com/@ karpathy/yes-you-should-understand-back prop-e 2f 06 eab 496 b
- https://www.youtube.com/watch?v=i94OvYb6noo
- https://towards data science . com/let-code-a-neural-network-in-plain-numpy-ae7e 74410795
- https://www . cs . CMU . edu/~ mgormley/courses/10601-s17/slides/lecture 20-back prop . pdf
反向传播,简单的方法(第 1 部分)
反向传播的简单详细解释
更新:学习和练习强化学习的最好方式是去 http://rl-lab.com
在关于梯度下降的文章之后,反向传播很好地利用了这种技术来计算神经网络中权重的“正确”值。
**重要提示:**这篇文章包含了相当多的等式,然而它们都是有逻辑联系的。读者应该有耐心阅读它们,并理解它们是如何构建的。这对很好地理解反向传播技术是必不可少的。
与梯度下降方法一样,我们再次借用无线电比喻来帮助理解反向传播的直觉。在另一篇文章中,为了得到可接受的输出,只需要处理一个按钮,而在这里,我们有许多需要调整的按钮。
上图显示,在连接两个节点的每条线上都有一个微调按钮(即使它们没有全部显示出来)。
如前所述,我们的工作是调整这些按钮,使输出误差最小,这意味着有效输出尽可能接近预期结果。
在梯度下降中,我们讨论了一个微调参数𝜃和误差或损耗𝓛之间的简单关系,在这种情况下,我们有一个更复杂的关系。
然而,我们将从仅考虑一个神经元开始慢慢地进行。
一个神经元的情况
作为提醒,单个神经元接收一个或多个输入 x 并将每个输入乘以某个权重 w *,*将它们相加并加上偏差 b ,然后应用激活函数 g() 来产生输出
为了能够评估神经元的输出,使用了损失函数𝓛。一个这样的损失函数可以是𝓛(y,ŷ) = (y - ŷ)
当然,目的是确定 w 和 b 的值,以便最小化𝓛.
通过查看神经元架构,我们可以很容易地注意到以下的 事件链:
wᵢ的变化导致 z 变化,
变化导致zg(z)变化,
变化导致 g(z)
为简单起见,让我们称之为 a = g(z) ,由此可见 ŷ = a.
上述链式反应可以使用链式法则导出,该法则规定输出相对于 wᵢ 的变化是两者之间相位的偏导数的乘积:
*∂𝓛/∂wᵢ=∂𝓛/∂a * ∂a/∂z ∂z/∂wᵢ
让我们单独考虑每个术语:
∂𝓛/∂a= ∂((y-ŷ))/∂a = ∂((y-a))/∂a =-(y-a)=a-y
∂a/∂z= ∂g(z)/∂z =g’(z)(其中 g’(z)是 g(z)的导数)。注意 g(z)可以是任何可导函数)
∂z/∂wᵢ= ∂(x1w1+x2 * w2+…+xᵢwᵢ+…xn wn)/∂wᵢ
=∂(xᵢwᵢ)/∂wᵢ=xᵢ**
将每一项替换为它的值将得到∂𝓛/∂wᵢ
∂𝓛/∂wᵢ=(a-y) g’(z)xᵢ
让我们定义𝛿如𝛿 = (a - y) * g’(z),∂𝓛/∂wᵢ的最终形式将是:
∂𝓛/∂wᵢ=𝛿xᵢt65】***
如果你还记得那篇梯度下降的文章,为了找到最小化𝓛的𝜃,我们迭代下面的等式,直到∆𝓛变得太小或为零:
𝜃𝑛₊₁ = 𝜃𝑛 -𝛂。∆𝓛
**同样的逻辑也适用于神经网络。
对于每个 I,*wᵢ⁺=wᵢ-𝛂∂𝓛/∂wᵢ=wᵢ-**𝛂𝛿xᵢ 其中w \u\u\u\u 是 w \u 的新值。
**重要:对于每一个 I 我们迭代 wᵢ 足够的次数,直到 ∂𝓛/∂wᵢ 变得足够小
使用相同的逻辑我们也可以找到变化的效果b**:
∂𝓛/∂b=∂𝓛/∂a * ∂a/∂z ∂z/∂b* 由于∂z/∂b = 1 并且所有其他项已经被计算过∂𝓛/∂b =𝛿 接下来是b⁺=b ∂𝓛/∂b=b-**𝛂𝛿*** 其中 b ⁺是 b 的新值。
再次我们保持迭代 b 足够的次数,直到 ∂𝓛/∂ b 变得足够小。******
两个神经元的情况
现在让我们以两个神经元按顺序排列为例(它们形成两层)。
在开始之前,值得注意的是符号已经改变,以适应新的架构。上标数字表示层。例如,z、b、a、g 属于层#2,而 z、b、a、g 属于层#3。
另外值得一提的是,层#2 的输出是单值 a ,乘以下一层的权重 v 。
第三层
我们将首先从最后一层(层#3)开始,因为它的结果将用于前一层。
一连串的事件并不奇怪。
v变化引起 z 变化,
z 变化引起 g (z ) 变化, g (z ) 变化𝓛(y变化。
使用链式法则,我们计算导数∂𝓛/∂v:
∂𝓛/∂v =∂𝓛/∂a * ∂a /∂z * ∂z /∂v
如前所述,我们单独计算每一项:
∂𝓛/∂a = a - y
∂a /∂z = g '(z ) (其中 g '(z)是 g (z)的导数)
∂z /∂v = a
所以∂𝓛/∂v 变成了∂𝓛/∂v =(a-y)* g’(z)* a
我们来定义一下𝛿=****(a-y)*【g’(z) 接下来就是 ∂𝓛/∂v = 𝛿 * a
同样我们可以找到∂𝓛/∂b =𝛿t93】
第二层
现在让我们移动到第二层。
链式反应开始于对 wᵢ 施加影响 z ,
z的变化影响g(z)
g(z)的变化影响 z (注意在这一点上 v 被认为是固定的)
z的变化影响g(z)
导数方程为
∂𝓛/∂wᵢ=(∂𝓛/∂a * ∂a /∂z * ∂z /∂a) ∂a /∂z * ∂z/∂wᵢ*
我们已经计算了 ∂𝓛/∂a 和∂a /∂z,至于其余的可以很容易地推导出来。
∂z /∂a= ∂(a * v))/∂a =v
∂a /∂z= ∂g(z )/∂z =g '(z)
∂z/∂wᵢ=xᵢ
替换完每一项但其值
∂𝓛/∂wᵢ=((a**-y)* g**'【z】)* v)* g '(z)xᵢ
我们已经将𝛿定义为𝛿=(a-y) g '【z】
∂𝓛/∂wᵢ=𝛿 v * g '(z)xᵢ****
让我们定义 𝛿 为 𝛿 = 𝛿 * v * g '(z )
这将给出∂𝓛/∂wᵢ: ∂𝓛/∂wᵢ = 𝛿 * xᵢ 的最终形式
同样我们可以找到∂𝓛/∂b =𝛿
L 层序列
现在考虑一个有 L 层的网络,每层只包含一个神经元。这样的网络看起来会像下图。
通过使用与上述相同的逻辑,我们可以找到反向传播所需的不同组件。
**所以对于 l 层
*𝛿ᴸ=(aᴸ-y)***gᴸ’(zᴸ) 对于任何其他层𝒍<l
*𝛿ˡ=𝛿ˡ⁺ wˡ⁺gˡ’(zˡ)
对于任何层,𝒍≤l ∂𝓛/∂wˡ=𝛿ˡ*aˡ⁻
∂𝓛/∂bˡ=𝛿ˡ 其中 a ˡ ⁻是层 1 的输出,或者如果我们在层 1,它将是输入 x
梯度下降的公式求wᵢˡ**(𝒍层的权重,神经元 I)即
t5】wᵢˡ⁺= wᵢˡ-𝛂∩wt = wt = w
重要:对于每个 w ᵢ ˡ 我们迭代足够的次数,直到 ∂𝓛/∂w ᵢ ˡ 变得足够小*
一般情况
具有多个神经元的多个层
在这一节中,我们有多层神经网络,每层有多个神经元,而不是我们迄今为止使用的一个。
回想一下,在前面的例子中,我们每层有一个神经元,对于层 l,我们得到了𝛿ᴸ=(aᴸ-y)***【gᴸ’(zᴸ】
和 *𝛿ˡ=𝛿ˡ⁺ wˡ⁺gˡ’(zˡ)对于层𝒍 < L
这只是针对每层中的一个神经元。
当我们每层有多个神经元时,我们必须为每层的每个神经元计算 𝛿 。
所以现在只有上标字母的变量如 𝛿ᴸ,w ˡ 是向量和矩阵,**而同时有上标和下标字母的变量如 𝛿 ᵢ ˡ 和 wˡ ᵢᵣ 都是单值。由于我们有多个输出层,损失函数𝓛(y,ŷ) = (y - ŷ)的定义不再充分。我们必须考虑所有的输出神经元。于是我们定义代价函数为所有输出神经元的平方和
**c =∑ᵢ(yᵢ−ŷᵢ*)。****
因此,到目前为止,我们计算的公式将具有向量形式:
其中∇aC 是成本 C 相对于网络输出的变化向量 a ᵢ ᴸ ,也就是 ∂C/∂a ᵢ ᴸ。 ⊙运算符是成员向量/矩阵乘法。
反向传播算法
- 输入 x: 设置第一层的输入。
- ****向前:对于每层 l = 2,3,…,l 我们计算
zˡ=wˡaˡ⁻+bˡ
和
aˡ=gˡ(zˡ) 。 - 输出误差𝛿ᴸ: 在输出层我们计算矢量
𝛿ᴸ=∇ac⊙gᴸ’(zᴸ)。这将是反向传播的开始。 - 反向传播:我们向后移动,对于每一层 l=L-1,L-2,L-3,…,2 我们计算每一层的误差
𝛿ˡ=(wˡ⁺)ᵀ𝛿ˡ⁺)⊙gˡ’(zˡ。
然后我们使用梯度下降公式更新各层的权重:
**wˡ⁺ᵢᵣ=wˡᵢᵣ-𝛂𝛿ˡᵢ* aᵣ̿** 和
b̿** - ****输出:最后,我们将计算每层的权重 w 和偏差 b ,以最小化成本函数 C 。
结论
反向传播可能很难理解,在代码中实现更难,因为它很容易与矩阵和向量及其维数纠缠在一起。然而,对于初学者来说,重要的是付出足够的努力来获得关于这项技术的足够的直觉,因为这将帮助他们获得神经网络的深入知识。
相关文章
反向传播,简单的方法(第 2 部分)
反向传播的实际实现
更新:学习和练习强化学习的最好方式是去 http://rl-lab.com
在第一部分中,我们已经看到反向传播是如何以最小化成本函数的方式导出的。在本文中,我们将看到实现方面,以及一些避免常见陷阱的最佳实践。
我们仍然处于简单模式,一次处理一个输入。
图层类别
考虑下图所示的全连接神经网络。
每个层将由包含权重、激活值(层的输出)、梯度 dZ(图像中未示出)、累积误差δ(𝚫)、以及激活函数 f(x) 及其导数***f’(x)***的层对象来建模。存储中间值的原因是为了避免每次需要时都要计算它们。
**建议:**最好围绕几个类来组织代码,避免把所有东西都塞进数组,因为很容易丢失。
请注意,输入图层不会由图层对象表示,因为它只包含一个矢量。
**class** Layer:
**def** __init__(self, dim, id, act, act_prime,
isoutputLayer = **False**):
self.weight = 2 * np.random.random(dim) - 1
self.delta = **None** self.A = **None** self.activation = act
self.activation_prime = act_prime
self.isoutputLayer = isoutputLayer
self.id = id
Layer 类的构造函数将以下内容作为参数:
- dim:权重矩阵的维数,
- id:整数作为层的 id,
- act,act_prime:激活函数及其导数,
- isoutputlayer:如果该层是输出,则为 True,否则为 False。
它将权重随机初始化为-1 到+1 之间的数字,并设置要在对象内部使用的不同变量。
图层对象有三种方法:
- 向前,计算层输出。
- 向后,将目标和输出之间的误差传播回网络。
- 更新,根据梯度下降更新权重。
**def** forward(self, x):
z = np.dot(x, self.weight)
self.A = self.activation(z)
self.dZ = self.activation_prime(z);
forward 函数通过输入 x 计算并返回层的输出,并计算和存储输出 A = activation (W.X)。它还计算并存储 dZ,即输出相对于输入的导数。
反向函数采用两个参数,目标 y 和 rightLayer,即假设当前层是𝓁.的层(𝓁-1)
它计算从输出向左传播到网络起点的累积误差增量。
重要提示:一个常见的错误是认为反向传播是某种环回,其中输出被再次注入网络。所以不用dZ = self . activation _ prime(z); 有的用途 self.activation_prime(一) *。这是错误的,因为我们要做的只是计算出输出 a 相对于输入 z 的变化,这意味着根据链式法则计算导数∂a/∂z= ∂g(z)/∂z =g’(z)。这个误差可能是因为在 sigmoid 激活函数 a = 𝜎(z) 的情况下,导数**𝜎’(z)= 𝜎(z)(1-𝜎(z)= a *(1-a)。**这给人一种输出被注入网络的错觉,而事实是我们正在计算 𝜎’(z).
**def** backward(self, y, rightLayer):
**if** self.isoutputLayer:
error = self.A - y
self.delta = np.atleast_2d(error * self.dZ)
**else**:
self.delta = np.atleast_2d(
rightLayer.delta.dot(rightLayer.weight.T)
* self.dZ)
**return** self.delta
backward 函数的作用是根据以下公式计算并返回增量:
最后,更新函数使用梯度下降来更新当前层的权重。
**def** update(self, learning_rate, left_a):
a = np.atleast_2d(left_a)
d = np.atleast_2d(self.delta)
ad = a.T.dot(d)
self.weight -= learning_rate * ad
神经网络类
正如人们可能猜测的那样,层形成了一个网络,因此类 NeuralNetwork 用于组织和协调层。
它的构造器采用层的配置,这是一个长度决定网络层数的数组,每个元素定义相应层中的节点数。
例如[2,4,5,]表示网络有 4 层,输入层有 2 个节点,接下来的隐藏层分别有 4 个和 5 个节点,输出层有 1 个节点。第二个参数是用于所有层的激活函数的类型。
fit 函数是所有训练发生的地方。它首先选择一个输入样本,计算所有层上的前向,然后计算网络输出和目标值之间的误差,并通过以相反的顺序调用每层的反向函数(从最后一层开始到第一层)将该误差传播到网络。
最后,为每一层调用更新函数来更新权重。
这些步骤重复的次数由参数 epoch 确定。
训练完成后,可以调用预测函数来测试输入。预测功能只是整个网络的一个前馈。
**class** NeuralNetwork:
**def** __init__(self, layersDim, activation=**'tanh'**):
**if** activation == **'sigmoid'**:
self.activation = sigmoid
self.activation_prime = sigmoid_prime
**elif** activation == **'tanh'**:
self.activation = tanh
self.activation_prime = tanh_prime
**elif** activation == **'relu'**:
self.activation = relu
self.activation_prime = relu_prime
self.layers = []
**for** i **in** range(1, len(layersDim) - 1):
dim = (layersDim[i - 1] + 1, layersDim[i] + 1)
self.layers.append(Layer(dim, i, self.activation, self.activation_prime))
dim = (layersDim[i] + 1, layersDim[i + 1])
self.layers.append(Layer(dim, len(layersDim) - 1, self.activation, self.activation_prime, **True**))# train the network
**def** fit(self, X, y, learning_rate=0.1, epochs=10000):
*# Add column of ones to X
# This is to add the bias unit to the input layer* ones = np.atleast_2d(np.ones(X.shape[0]))
X = np.concatenate((ones.T, X), axis=1)
**for** k **in** range(epochs):
i = np.random.randint(X.shape[0])
a = X[i]
*# compute the feed forward* **for** l **in** range(len(self.layers)):
a = self.layers[l].forward(a)
*# compute the backward propagation* delta = self.layers[-1].backward(y[i], **None**)
**for** l **in** range(len(self.layers) - 2, -1, -1):
delta = self.layers[l].backward(delta, self.layers[l+1])
*# update weights* a = X[i]
**for** layer **in** self.layers:
layer.update(learning_rate, a)
a = layer.A# predict input
**def** predict(self, x):
a = np.concatenate((np.ones(1).T, np.array(x)), axis=0)
**for** l **in** range(0, len(self.layers)):
a = self.layers[l].forward(a)
**return** a
运行网络
为了运行网络,我们以 Xor 函数的近似为例。
我们尝试了几种网络配置,使用不同的学习速率和历元迭代。结果如下所示:
Result with tanh
[0 0] [-0.00011187]
[0 1] [ 0.98090146]
[1 0] [ 0.97569382]
[1 1] [ 0.00128179]Result with sigmoid
[0 0] [ 0.01958287]
[0 1] [ 0.96476513]
[1 0] [ 0.97699611]
[1 1] [ 0.05132127]Result with relu
[0 0] [ 0.]
[0 1] [ 1.]
[1 0] [ 1.]
[1 1] [ 4.23272528e-16]
建议您尝试不同的配置,自己看看哪种配置能提供最佳和最稳定的结果。
源代码
完整的代码可以从这里下载。
结论
反向传播可能会令人困惑并且难以实现。你可能会有一种错觉,认为你通过理论掌握了它,但事实是,当实施它时,很容易陷入许多陷阱。你应该有耐心和毅力,因为反向传播是神经网络的基石。
相关文章
反向传播,简单的方法(第 3 部分)
如何处理矩阵的维数
Photo by Todd Brogowski on Unsplash
更新:学习和练习强化学习的最好方式是去 http://rl-lab.com
在系列的第 2 部分中,我们认为我们的神经网络一次处理一个输入样本。然而,这是没有效率的。这需要太多的计算,并且不能从矩阵计算中使用的大量优化技术中获益。
大多数时候,用于训练神经网络的数据是以充满行的文件格式出现的,其中每行代表一个样本,每列代表一个特征。
下面我们来考虑一下网络。
正向输送
我们将从关注前两层开始。
输入是一个包含数据行的文件,其中每一列都包含进入神经网络的一个输入条目的值。
例如,具有值(X11,X21,…,Xm1)的列 X1 进入神经网络的 X1 输入,类似于列 X2。
人们很自然地认为,输入到神经网络中的行数将在输出端收集。也就是说,如果我们有两个样本一次一个地输入网络,我们应该期望在输出端有两个结果(一次一个)。
同样,如果我们将数据作为包含代表两个样本的两行的矩阵输入,我们应该会得到包含两行结果的矩阵。
这也适用于每一层的输出。
让我们考虑一个包含 X1 和 X2 的不同样本的文件。
在第 2 层的输出端,对于输入到输入层的每个样本,我们有值 A1、A2、A3、A4。对于每个样本都是如此,因此对于 m 个样本,我们将有 m 个输出,例如(A11,A12,A13,A14),(A21,A22,A23,A24),…。(Am1、Am2、Am3、Am4)。所以第 2 层输出的 A 矩阵的维数是(m,4)。z 的导数也是如此。
注意,权重矩阵的维数不随样本数而改变,它仅取决于输入端的节点数(在本例中为 2)和输出端的节点数(在本例中为 4),这使得它为(2,4)。
或者,我们可以通过线性代数获得相同的结果。
我们已经知道,在 feed froward 中我们计算 A 和 dZ,例如
A = f(X.W) 和 dZ = f’(X.W) 。
所以要计算 A 和 dZ 的维数,看一下 X 和 W 的维数就够了,它们分别是(m,2)和(2,4)。
结果是(m,2) x (2,4) = (m,4)。
下图描绘了如何填充 A 和 dZ 以形成维数为(m,4)的矩阵。
反向传播
到目前为止,我们计算了前馈阶段每层输出的维数。我们来看看反向传播。
提醒一下,反向传播中的公式是:
𝚫ᴸ = (Aᴸ - Y) * dZᴸ
𝚫ⁱ = (𝚫ⁱ⁺ .ᵀ)* dZⁱ西部
(其中*是成员的乘法运算)
我们现在知道 A 和 dZ 都具有维度(m,n ),其中 m 是样本的数量, n 是层中节点的数量。所以𝚫ᴸ将有相同的维数(m,n)
在我们的例子中,最后一层(L)只有一个节点,所以层 L 的 A 和 dZ 的维数为(m,1)。由此可见,𝚫ᴸ也有维数(m,1)。
为了计算内层的增量,让我们以示例中的第 2 层和第 3 层为例。
层 2 和层 3 之间的权重𝞱具有维度(4,3),因为层 2 的 4 个节点连接到层 3 的 3 个节点。
然而,由于第 2 层的增量是来自第 3 层的增量和权重𝞱的点积,我们最终得到以下配置。
不可能进行点积并在维度为(m,4)的第 2 层获得 delta!
为了解决这个问题,我们用𝞱ᵀ转置了𝞱.
计算将是可能的,如下图所示
更新权重
更新层 i 的权重,包括获取该层的输入,对该层的增量执行点积,然后使用结果来更新权重。
如果我们以第 1 层和第 2 层为例,我们在第 1 层有一个维度为(m,2)的输入,在第 2 层有一个维度为(m,4)的增量。
然而,连接层 1 和层 2 的权重矩阵具有维度
(2,4)。
为了能够更新权重矩阵,x 和𝚫之间的点积应该产生(2,3)矩阵。
这是通过得到 x 的转置,即 x 的 ᵀ ,然后执行点积 x 的 **ᵀ。**我们称之为𝞭.的𝚫
现在,我们可以使用梯度下降公式更新权重矩阵
Wⁿ⁺ = Wⁿ - 𝝰 * 𝞭(这里 n 是 w 的版本,而不是图层索引)
结论
您从头开始进行反向传播的可能性很小,这要感谢大量已经这样做的库。然而,如果你想自己做这个练习,你应该注意尺寸,否则你会陷入计算错误的迷宫。
源代码
下面是前一篇文章中开发的 XOR 示例的源代码,它使用样本矩阵,而不是一次一个样本。