用 Python 读取 NetCDF 数据
访问一种有点混乱但功能强大的数据格式
由 Waldemar Brandt 在 Unsplash 上拍摄的照片
网络通用数据格式(NetCDF)通常用于存储多维地理数据。这些数据的一些例子是温度、降雨量和风速。存储在 NetCDF 中的变量经常在大面积(大陆)地区每天被测量多次。由于每天进行多次测量,数据值会快速积累,变得难以处理。当每个值还被分配给一个地理位置时,数据管理会变得更加复杂。NetCDF 为这些挑战提供了解决方案。本文将带您开始使用 Python 从 NetCDF 文件中读取数据。
装置
NetCDF 文件可以用一些不同的 Python 模块读取。最受欢迎的是netCDF4
和gdal
。对于这篇文章,我们将严格集中在netCDF4
上,因为这是我个人的偏好。
有关如何使用 xarray 和 rioxarray 在 Python 中读取和绘制 NetCDF 数据的信息,请查看本文。
安装很简单。我通常建议使用 anaconda Python 发行版来消除依赖关系和版本控制带来的混乱。要安装 anaconda (conda),只需输入conda install netCDF4
。或者,你可以用pip
安装。
为了确保您的netCDF4
模块安装正确,在终端中启动一个交互式会话(键入python
并按下‘Enter’)。然后import netCDF4 as nc
。
加载 NetCDF 数据集
加载数据集很简单,只需将一个 NetCDF 文件路径传递给netCDF4.Dataset()
。对于这篇文章,我使用了一个包含来自 Daymet 的气候数据的文件。
import netCDF4 as ncfn = '/path/to/file.nc4'
ds = nc.Dataset(fn)
通用文件结构
NetCDF 文件有三个基本部分:元数据、维度和变量。变量包含元数据和数据。netCDF4
允许我们访问与 NetCDF 文件相关的元数据和数据。
访问元数据
打印数据集ds
,为我们提供文件中包含的变量及其尺寸的信息。
print(ds)
和输出。。。
<class 'netCDF4._netCDF4.Dataset'>root group (NETCDF4_CLASSIC data model, file format HDF5):start_year: 1980source: Daymet Software Version 3.0Version_software: Daymet Software Version 3.0Version_data: Daymet Data Version 3.0Conventions: CF-1.6citation: Please see [http://daymet.ornl.gov/](http://daymet.ornl.gov/) for current Daymet data citation informationreferences: Please see [http://daymet.ornl.gov/](http://daymet.ornl.gov/) for current information on Daymet referencesdimensions(sizes): time(1), nv(2), y(8075), x(7814)variables(dimensions): float32 time_bnds(time,nv), int16 lambert_conformal_conic(), float32 lat(y,x), float32 lon(y,x), float32 prcp(time,y,x), float32 time(time), float32 x(x), float32 y(y)groups:
上面您可以看到文件格式、数据源、数据版本、引用、维度和变量的信息。我们感兴趣的变量是lat
、lon
、time
和prcp
(降水量)。有了这些变量,我们就能找到给定时间、给定地点的降雨量。该文件仅包含一个时间步长(时间维度为 1)。
元数据也可以作为 Python 字典访问,这(在我看来)更有用。
print(ds.__dict__)OrderedDict([('start_year', 1980), ('source', 'Daymet Software Version 3.0'), ('Version_software', 'Daymet Software Version 3.0'), ('Version_data', 'Daymet Data Version 3.0'), ('Conventions', 'CF-1.6'), ('citation', 'Please see [http://daymet.ornl.gov/](http://daymet.ornl.gov/) for current Daymet data citation information'), ('references', 'Please see [http://daymet.ornl.gov/](http://daymet.ornl.gov/) for current information on Daymet references')])
那么任何元数据项都可以用它的键来访问。例如:
print(ds.__dict__['start_year']1980
规模
对维度的访问类似于文件元数据。每个维度都存储为包含相关信息的维度类。可以通过遍历所有可用维度来访问所有维度的元数据,如下所示。
for dim in ds.dimensions.values():
print(dim)<class 'netCDF4._netCDF4.Dimension'> (unlimited): name = 'time', size = 1<class 'netCDF4._netCDF4.Dimension'>: name = 'nv', size = 2<class 'netCDF4._netCDF4.Dimension'>: name = 'y', size = 8075<class 'netCDF4._netCDF4.Dimension'>: name = 'x', size = 7814
单个维度是这样访问的:ds.dimensions['x']
。
可变元数据
访问变量元数据的方式与访问维度的方式相同。下面的代码展示了这是如何做到的。我放弃了输出,因为它很长。
for var in ds.variables.values():
print(var)
下面为prcp
(降水)演示了访问特定变量信息的程序。
print(ds['prcp'])
和输出。。。
<class 'netCDF4._netCDF4.Variable'>float32 prcp(time, y, x)_FillValue: -9999.0coordinates: lat longrid_mapping: lambert_conformal_conicmissing_value: -9999.0cell_methods: area: mean time: sum within days time: sum over daysunits: mmlong_name: annual total precipitationunlimited dimensions: timecurrent shape = (1, 8075, 7814)
访问数据值
通过数组索引访问实际的降水数据值,并返回一个numpy
数组。所有变量数据返回如下:
prcp = ds['prcp'][:]
或者可以返回一个子集。下面的代码返回一个 2D 子集。
prcp = ds['prcp'][0, 4000:4005, 4000:4005]
这是 2D 子集的结果。
[[341.0 347.0 336.0 329.0 353.0][336.0 339.0 341.0 332.0 349.0][337.0 340.0 334.0 336.0 348.0][342.0 344.0 332.0 338.0 350.0][350.0 351.0 342.0 346.0 348.0]]
结论
NetCDF 文件通常用于地理时间序列数据。最初,由于包含大量数据,并且与最常用的 csv 和光栅文件的格式不同,使用它们可能有点吓人。NetCDF 是记录地理数据的好方法,因为它内置了文档和元数据。这使得最终用户可以毫不含糊地准确理解数据所代表的内容。NetCDF 数据以 numpy 数组的形式访问,这为分析和整合到现有工具和工作流中提供了许多可能性。
阅读这本书,抓住你的第一份数据科学工作
这本书里的 5 个技巧改变了我研究数据科学的方式,并启发了我成为一名中型作家的旅程。
由 Rashtravardhan Kataria 在 Unsplash 拍摄的照片
让我们抛开这些事实:现在是一个怪异的活着时间。特别是对于求职者来说,当你也申请和面试工作时,可能很难专注于看似琐碎的事情,如再练习 10 个技术性面试问题、获得另一个在线认证或建立你的数据科学作品集(我相信中世纪国王没有将工作面试作为一种酷刑的唯一原因是因为他们还没有想到这一点)。
虽然屈服于集体低迷、一品脱一品脱地吃低热量冰淇淋、看《抖音》直到你的头脑融化可能很诱人,但我在这里是为了让你意识到更好的东西。老实说,当我读它的时候,我感觉好像找到了捷径。
仅在两个月前,Emily Robinson 和 Jacqueline Nolis 出版了在数据科学领域建立职业生涯,探索数据科学职业的方方面面。这本书有 281 页(加上 30 页的技术面试问题和解释),对任何刚入门的人来说,它是实践知识和灵感的彻底结合。
这是在我踏入数据科学行业的第一步时完全启发了我的 5 大技巧。如果你喜欢这些,请随意查看这本书(使用代码 buildbook40% 可享受 40%的折扣),并在 Twitter 上为作者的出色工作大声喊出来!
免责声明: 不知作者也没有靠这本书的销售赚钱。所有的想法和个人轶事都是我自己的。
1)✂️的问题不在于不够“前沿”
许多人被数据科学吓退了,因为它太宽泛了,学习所有的东西意味着一条不可能的学习曲线。没人能做到。因此,更重要的是深入钻研基础知识,而不是浪费精力过早地投入复杂的东西。
尤其是刚起步的时候,你确实而不是需要担心成为“数据科学独角兽”(也就是说,进入一家公司,可以执行交给他们的每一项模糊的数据科学相关任务的人)。在工作日的大部分时间里,数据科学家不是做那些花哨的高级任务,而是执行数据库查询和准备数据进行分析。出于这个原因,成为一名出色的数据科学家的方法是能够快速准确地执行简单的任务。
我最近阅读了一份统计数据,表明数据科学家一天中 80%的时间用于数据清理和组织,只有 20%的时间用于实际分析。再者,面试问题往往涉及编写 SQL 查询,对 Python 或 R 有基本了解,或者能够向面试官解释 p 值;不要担心复杂的东西,担心真正了解基本知识,真正好。
2)创建一个文件夹(作为一种学习策略)📝
当我获得经济学学位毕业时,我从未被告知在找工作时拥有一份投资组合的重要性。在接受了弗吉尼亚大学数据科学硕士项目的一个名额,并与美联储、特勤局和司法部(我个人对公共部门感兴趣)的潜在未来雇主交谈后,我反复听到一条建议:带一个作品集给我们看。
面试不是发现全明星员工的好方法。即使是技术面试,帮助也不大;您很少会将 SQL 查询写成白板,而不仅仅是键入一个。不记得怎么解 FizzBuzz 了?谷歌搜索只需要 0.2 秒——如果你在面试中回忆不起,那又有什么关系呢?
有了投资组合,你就完成了三件事:1)你的个人项目有一个清晰的开始、中间和结束,2)你让人们感觉到你的技能,3)你向人们宣传你是我们中的一员。数据科学家不断发布他们学到的交易技巧,因此通过创建投资组合,这表明你在获得数据科学家头衔之前很久就已经加入了社区。
3)宣传自己📰
当我在高中二年级的时候,我们做了一整节关于简爱的节目,以电影结尾(你不怀念以前学校有电影的时候吗?).简是一个相貌平平、文静的孤女,她在附近的一个庄园里找到了一份家庭教师的工作,这让她的朋友们大为吃惊。
“你是怎么做到的?他们问道,简回答道,“我登了广告。”
宣传自己是一件事。对我们许多人来说,这也是可怕和不可思议的。你看到它的工作效率只有百万分之一,并认为这种可能性对你来说太大了,所以你不能尝试。你没有人脉,没有互联网知识,或者在它流行起来之前就开始做媒体。
我不知道如何宣传自己,所以我采纳了这本书的建议,只关注高回报领域。首先,我花了三天时间创建了一个个人网站。然后我创建了一个 Twitter,开始分享我喜欢的数据科学博客。自然地,这种对博客的持续吸收将我引向了 Medium,过了一会儿,我屈服了,写下了我的第一篇关于 SQL 的博客文章(这是我三天前刚刚学到的)。
在 15 天内,我又写了 5 篇文章,我的文章有超过 2500 次的浏览量,有 100 多个追随者。一旦我把我的 LinkedIn 链接到我的媒介,我的 LinkedIn 页面浏览量也开始飙升。我收到了无数来自我所在领域的热门数据科学家的请求。
我不是边缘案例,给自己做广告不一定要吓人。从小处着手,你会在不知不觉中拥有一个强大的忠实追随者基础。
4)保持管道充满🚰
让我们面对它;只有一小部分约会会让你找到“那个人”,工作也是如此。有时候他们不喜欢你,有时候你不喜欢他们。有时你确实喜欢他们,但他们仍然挂念着另一个人,或者有你没有意识到的偏见(比如在内部雇佣某人,因为文书工作更容易)。
永远不要做“真正的”面试。通过把每一次面试都当作是下一次面试的练习,你就卸下了那份“独一无二”的工作所带来的压力,并且可以获得更多的乐趣。这些人想尽办法给你有趣的数据科学难题,他们可能会请你吃午饭,有什么不喜欢的呢?
面试官不仅在寻找合适的人,他们也在寻找一个有趣的同事。通过不断申请工作来保持你的渠道畅通,直到你有一份工作在手,这也将有助于减轻压力,让你感觉你不得不在任何一场面试中胜出。
Van Tay Media 在 Unsplash 上的照片
5)适用于您≥ 60% 所胜任的工作💼
逻辑很简单:对许多公司来说,列出的工作描述与其说是要求,不如说是愿望清单。我敢肯定,我们都希望能熟练掌握每一种编程语言,成为数据可视化的主沟通者,成为机器学习和自然语言处理的天才。总的来说,我们都想成为数据科学的独角兽(或者至少被视为独角兽)。
公司通常会列出比角色所需更多的技能,希望能吸引独角兽。出于这个原因,艾米丽·罗宾逊和杰奎琳·诺里斯建议申请那些你 60%或者更有资格的工作。如果你满足了 60%或更多的要求,你就已经为这份工作做好了准备。
我个人对这个建议非常兴奋,因为它消除了事情的猜测;我已经开始强调我在工作描述中“附带”的所有技能,然后将我的资格视为所列要点的一小部分。如果这个数字≥60%,我就要了!如果没有,对我们俩来说都不合适。
最后,不要出售你没有的技能也很重要,因为你认为它会让你更受欢迎;如果你交叉手指,他们没有问你简历上的 x、y 和 z 线,你应该认真考虑你是否应该有这些线。做真实的自己,专注于你所拥有的令人敬畏的技能:不多不少。
最后的想法🤔
你觉得这个建议怎么样?它是明显的还是新颖的?这会改变你的求职策略吗?我很乐意在下面的评论中听到你的想法!
谢谢,
阿曼达
读者使用条款和条件
最后更新于 2023 年 3 月
阅读我们的媒体出版物时你应该知道的事情
走向数据科学公司是一家在加拿大注册的公司。通过使用 Medium,我们为成千上万的人提供了一个交流思想和扩展我们对数据科学的理解的平台。
由于 TDS 在过去几年中发展如此之快,我们决定整理一个术语列表,解释读者与 TDS 的关系( TDS 读者使用条款和条件—2023 年 3 月)。
这些条款绝不会改变、修改或取代您与 Medium 的关系,您与 Medium 的关系受 Medium 服务条款管辖。
如果您有任何问题,可以在这里联系我们。你也可以看看我们之前的版本:2022 年 8 月,以及2020 年 5 月。
用 Python 读取图像(不使用特殊的库)
就告别 OpenCV,Matplotlib,PIL 除了 Numpy!
照片由克里斯蒂安娜·特斯顿在 Unsplash 拍摄
嗯,上面这张照片很棒,不是吗?。对我来说这是一部杰作。尽管如此,你有没有想过或者问过自己这个一百万美元的问题:
图像是由什么组成的?
现在,在阅读整个教程之前,我希望你们在评论区 回答这个问题 ,这样你们可以给大脑一些工作。无论如何,对于那些从未回答过问题或不知道答案的人,通过阅读这篇文章,你最终可以毫不犹豫地回答这个问题。炒作够了,我们开始吧!。顺便说一下,本教程的完整代码也可以在下面我的 GitHub 资源库 中找到:
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/Tanu-N-Prabhu/Python/blob/master/Reading_An_Image_In_Python_%28Without_Using_Special_Libraries%29.ipynb)
故事
我在图像处理课上(毕业时)有一个作业,我必须从一个文件中读取一个原始图像,使用单点重采样和加权平均重采样算法将图像调整到指定的尺寸。有人给了我一张纸条,上面写着“ 不要使用任何外部库 ”用于你的程序,尤其是读取和保存图像。只有选定的算法被允许使用,python 中的一个这样的算法是**numpy**
。这听起来很痛苦,但无论如何我不得不这么做,我做到了。在本教程中,我们将只专注于读取和保存图像,没有什么花哨的。
问题
编写一个程序,从文件中读取 raw 格式的灰度图像,并将新图像保存到 raw 格式的新文件中。(我只是把问题说得简短些,因为这是你今天要学的内容)。
图像源
下面是灰度 8 位图像的来源。我将使用 第 4 张 样图 芭芭拉,8 位灰度(512×512)。raw ”。其他样本图像的链接可以在 这里找到 。
图书馆的使用
正如我所说的,我不会使用任何特殊的 python 库。图像处理领域中一些最受欢迎和使用最广泛的库包括:
有趣的是,通过使用 OpenCV , PIL 和 Matplotlib 库,我们只需两行代码就可以读取图像并将其保存到文件中。但是这里的问题是不要使用特殊的库和特殊的方法。 Numpy 是唯一会被贯穿使用的标准库( 你就知道为什么 )。
密码
这是你们期待已久的时刻。
导入库
使用**numpy**
库的唯一原因是将 raw 图像转换为像素 的 数组。**主要用于存储数组中的值。****
**# Necessary library used for this tutorial** import numpy as np
初始化图像的行和列值
图像的维度由行和列组成。
**# For example a particular image in this case would contain (512*512) rows and columns meaning 262,144 pixels** ROWS = 512
COLS = 512**# Different images have different dimensions. Change it accordingly**
打开输入图像文件(RAW 格式)
**# Opening the input image (RAW)** fin = open('barbara_gray.raw')
print(fin)
这将提示输出如下:
**<_io.TextIOWrapper name='barbara_gray.raw' mode='r' encoding='UTF-8'>**
现在,我们需要从原始数据(图像)构建一个 2D 数组。必须使用一种高效的方法来读取已知数据类型的二进制数据,这可以在**numpy**
库的帮助下完成。
加载输入图像
这是真正的乐趣开始的地方。感谢**numpy**
**# Loading the input image** print("... Load input image")
img = np.fromfile(fin, dtype = np.uint8, count = ROWS * COLS)
print("Dimension of the old image array: ", img.ndim)
print("Size of the old image array: ", img.size)
执行此操作后,您将获得
**... Load input image** Dimension of the old image array: **1**
Size of the old image array: **262144**
从 1D 到 2D 阵列的转换
有一种方法可以将 1D 阵列转换为 2D 阵列,例如用图像的行和列或列和列(任何一种都可以)来划分像素总数。这可以借助一个公式来写:
**img = tp // (cols, cols)**
tp =总像素; cols 代表图像的行和列。我们之所以使用地板除法而不是除法,是因为我们需要将数值四舍五入为整数。
**# Conversion from 1D to 2D array** img.shape = (img.size // COLS, COLS)
print("New dimension of the array:", img.ndim)
print("----------------------------------------------------")
print(" The 2D array of the original image is: \n", img)
print("----------------------------------------------------")
print("The shape of the original image array is: ", img.shape)
执行此操作后,您将获得
New dimension of the array: **2**
----------------------------------------------------
The 2D array of the original image is:
**[[240 240 240 ... 230 230 230]
[240 240 239 ... 230 229 229]
[240 239 238 ... 231 232 232]
...
[ 50 50 50 ... 50 50 50]
[ 50 50 50 ... 50 50 50]
[ 50 50 50 ... 50 50 50]]**
----------------------------------------------------
The shape of the original image array is: **(1000, 1600)**
将新图像以 raw 格式保存到新文件中
**# Save the output image** print("... Save the output image")
img.astype('int8').tofile('NewImage.raw')
print("... File successfully saved")
**# Closing the file** fin.close()
这就是你如何以 raw 格式保存新图像。这将保存在您的本地系统中。
... Save the output image
... File successfully saved
注意:进入图像的行和列时。例如,如果一幅图像的分辨率是 1600x1000 ,那么就意味着宽度是 1600 而高度是 1000 。类似地,在上面的程序中,行将是 1000 ,而列将是 1600 。
输出
成功执行上述代码后,您现在可以读取原始图像,然后保存原始图像。耶!!!。现在,如果你不相信我,你可以使用一些在线图像打开工具(或其他工具)打开保存的图像。我通常使用光电,这是我得到的。
塔努·南达·帕布摄
先提醒一下,上面是工具输出的截图,主要输出的图像是灰度的,叫做“ 【芭芭拉 ”(一个举起一只手的女人或者其他什么)。
结论
在离开之前,尝试阅读其他格式的图像,如 JPEG (JPG),PNG,看看你会得到什么价值。此外,尝试阅读彩色(RGB)图像。反正现在你可以轻松回答教程开头的上述问题了。让我再次提醒你。 图像是由什么组成的? ”。答案是大家都看到了,“ 图像是由一组像素值 ”组成的。值的数量取决于给定图像的行和列。
今天到此为止,希望大家喜欢阅读教程“ (不使用特殊库) ”的内容。正如承诺的那样,除了**numpy**
,我没有使用任何其他库。嗯,咄!你需要使用**numpy**
来处理数组。如果你们对教程有任何意见或担忧,请在下面的评论区告诉我。直到那时再见,保持安全:)
使用 Python 读取和可视化 GeoTiff |卫星图像
用于处理 GeoTiff 或卫星影像的 python 库简介。
在 python 中读取图像非常容易,因为有许多库可用于读取、编辑和可视化不同格式的图像。这些库的例子有 Matplotlib、OpenCV、Pillow 等。这些库可以很好地处理流行的图像格式,比如*。png,。tiff,。jpg,。jpeg,*等。但它们似乎对 GeoTiff 图像不起作用。
如果我们了解 GeoTiff 格式与其他图像格式的不同之处,就很容易猜到原因。下面给出了一个简单的定义。
GeoTIFF 是一种标准的
.tif
或图像文件格式,它包括嵌入在。tif 文件作为标签。
通过标签,这意味着它有一些额外的元数据,如空间范围、CRS、分辨率等。以及像素值。是卫星和航空 摄影 影像的一种流行分发格式。
本文讨论了使用 jupyter 笔记本通过 python 读取和可视化这些图像的不同方法。使用的库有 GDAL、rasterio、georaster 和 Matplotlib(用于可视化)。这些库将帮助我们将这些图像转换为简单的 numpy 数组格式,从那里我们还可以使用 numpy 或 TensorFlow 执行其他图像转换,尽管我在本文中没有讨论转换。
这些库的文档中提到了安装步骤,我在参考资料部分提供了它们的链接,请仔细阅读。
使用 RasterIO
RasterIO 由 Mapbox 实现,它提供了用于读取地理参考数据集的 python API。此外,文档中提到,与 GDAL python 绑定不同,它没有悬空 C 指针和其他可能导致程序崩溃的指针问题。
import rasterio
from rasterio.plot import showfp = r'GeoTiff_Image.tif'
img = rasterio.open(fp)
show(img)
不要把自己和 x 轴和 y 轴的刻度值混淆,它们只是经度和纬度值。如果你想读取单个波段,使用下面的代码。
fp = r'GeoTiff_Image.tif'
img = rasterio.open(fp) # mention band no. in read() method starting from 1 not 0
show(img.read(3))
使用 RasterIO 的图像相关数据
# No. of Bands and Image resolution
dataset.count, dataset.height, dataset.width# Coordinate Reference System
dataset.crs
使用 RasterIO 的图像相关数据
使用地理控制器
georaster 安装了 pip 的版本为 1.25。这个版本可能会给你带来一些问题,如果你遇到任何问题,先去你的 georaster 安装目录。下面的代码会告诉你安装目录路径。
import georaster
print(georaster.__file__)
现在,在那里找到 georaster.py 文件,并用这个 georaster 中的内容更改它的内容。py 文件。
下面是可视化具有多个波段(或通道)的图像的代码。
import georaster
import matplotlib.pyplot as plt# Use SingleBandRaster() if image has only one band
img = georaster.MultiBandRaster('GeoTiff_Image.tif')# img.r gives the raster in [height, width, band] format
# band no. starts from 0
plt.imshow(img.r[:,:,2])
不使用 matplotlib 打印图像
我们可以直接使用绘图方法,而不是选择和可视化单个波段。
地理绘图法
使用 GDAL
这是处理 GeoTiff 图像时最受欢迎的库,但有时很难安装,需要花费大量精力才能最终使用它。GDAL 的大部分方法和类都是用 C++实现的,我们在这里使用它的 python 绑定。像 georaster 这样的大多数库都利用 GDAL,并为它提供了一个漂亮而简单的 python 接口。
from osgeo import gdal
import matplotlib.pyplot as pltdataset = gdal.Open('GeoTiff_Image.tif', gdal.GA_ReadOnly) # Note GetRasterBand() takes band no. starting from 1 not 0
band = dataset.GetRasterBand(1)arr = band.ReadAsArray()
plt.imshow(arr)
使用 GDAL 的图像相关数据
# For no. of bands and resolution
img.RasterCount, img.RasterXSize, img.RasterYSize# stats about image
img.GetStatistics( True, True )
Jupyter 笔记本实现图像统计
参考
安装
- https://mothergeo-py . readthedocs . io/en/latest/development/how-to/gdal-Ubuntu-pkg . html
- https://github.com/GeoUtils/georaster
- https://rasterio.readthedocs.io/en/latest/installation.html
- https://pypi.org/project/GDAL/
单据
- https://geo raster . readthe docs . io/en/latest/API . html # multi band raster
- https://rasterio.readthedocs.io/en/latest/quickstart.html
- https://gdal.org/python/index.html
阅读色盲图表:深度学习和计算机视觉
转换数据,使其与类似的数据集兼容
有很多在线教程,你可以学习训练一个神经网络,使用 MNIST 数据集对手写数字进行分类,或者区分猫和狗。我们人类总是非常擅长这些任务,可以轻松地匹配或击败训练有素的模型的性能。
情况并非总是如此。在这个项目中,我想探索一个我个人非常纠结的任务。我有轻度红绿色盲。所以,这样的图表对我来说通常很难看到,如果不是不可能的话:
这是一个 6(我想?)
有没有可能让我训练一个模型来帮我完成这项任务,而不用我眯着眼睛,不可避免地把问题弄错?
乍一看,这个任务似乎很简单。我们可以拍摄一些图像,将它们分成训练集和测试集,然后训练一个卷积神经网络。除了…没有数据集。在网上,我只能找到 54 个不同的图像,这对于一个训练集来说是不够的,因为有 9 个类别(数字 1-9)。
如何解决这个问题?我们仍然有包含单个手写数字图像的 MNIST 数据集。我们可以使用这个数据集来训练一个擅长分类单个数字的神经网络。通过一些 OpenCV 转换,我们可以让色盲图表看起来类似于 MNIST 图像,就像这样:
手写 5
我们开始吧!
在 MNIST 数据集上训练卷积神经网络
有很多关于这方面的教程,但我还是会给出一个关于这是如何完成的高层次概述。
首先,我们需要安装 Tensorflow,它可以通过 pip 获得。如果没有安装,请在终端中运行以下命令。
pip install tensorflow
或者如果你有一个图形处理器:
pip install tensorflow-gpu
现在,我们将创建一个 mnist.py 文件并获取我们的训练数据,由于 mnist 数据集的流行,这些数据内置于 tensorflow 中:
接下来,我们将建立我们的卷积神经网络。我不会详细介绍这些是如何在内部工作的,但是网上有很多资源可以阅读,比如这个。我们将使用一个 Conv2D 层,然后是 MaxPooling 和 Dropout。然后,我们的 2D 输出被展平并通过具有 128 个单元的密集层,接着是具有 10 个类(编号 0–9)的分类层。输出将是长度为 10 的向量,以指示预测。例如,2 会被预测成这样:
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
因为 1 在第二个索引中。以下是型号代码:
现在,我们编译模型,在训练数据上运行它,在测试数据上评估它,并将模型作为. h5 文件保存在我们的目录中:
当我们运行这段代码时,我们得到了大约 99%的训练准确率和 98%的测试集准确率:
还不错!现在,您应该在您的目录中看到一个 mnist.h5 文件。现在,让我们进入下一步。
OpenCV 图表处理
下一个目标是获取现有的图表,并将其转换为与 MNIST 数据尽可能相似的形式,我们保存的模型在分类方面非常出色。对于这一部分,我们还需要几个库:
pip install opencv-python
pip install imutils
pip install numpy
pip install sklearn
pip install scikit-image
我们的目标是这样的转变:
经过一些实验后,下面是我们需要做的处理:
- 增加对比度,使手指的颜色更明显。
- 应用中值和高斯模糊来平滑我们在原始图像中看到的小圆圈。
- 应用 K-均值颜色聚类获得一致的颜色斑点。
- 转换为灰度
- 应用阈值处理(这将是棘手的)来得到一个只用黑白的图像。
- 更多模糊和阈值处理
- 形态学开放、封闭、侵蚀
- 骨骼化
- 扩张
哇,太多了。让我们从对比开始。我从网上复制了一个函数,它接收一幅图像并应用定制的亮度和对比度变换。我把它放在一个文件 ContrastBrightness.py 中,并使它成为一个类:
在高水平上,增加亮度会将值添加到图像的 RGB 通道,而增加对比度会将值乘以某个常数。我们将只使用对比功能。
我们算法的另一个复杂部分是聚类。同样,我创建了一个文件 Clusterer.py,并将我在网上获得的必要代码放入其中:
这段代码将接受一个图像和一个数字作为输入。这个数字 K 将决定我们将使用多少颜色聚类。你可以在这里阅读更多关于 K-means 聚类的内容。现在,让我们创建最后一个文件 main.py。我们将从导入开始:
请注意,我们正在导入刚刚创建的两个类。现在,请从 my Github 下载图表目录中的图片。
这些都被分类(在没有色盲的人的帮助下)到适当的文件夹里。
现在,我们将遍历路径中的所有图像,并应用变换 1–4。我对我的代码做了大量的注释。
下面是一些转换后的图像:
这是一个明显的进步。数字都很清楚,连我都看得一清二楚。仍然存在两个问题:
- 它们看起来不是手写的,而且太厚了
- 它们在黑色背景上不是全白的
所以,我们将门槛。然而,由于图像的各种颜色,每一种都需要不同的阈值来工作。我们将自动搜索最佳阈值。我注意到,根据像素数,一个数字通常占整个图像的 10–28%。
因此,我们将尝试不同的阈值,直到白色百分比在 10–28%的范围内。首先,我们将定义一个函数,它告诉我们输入图像中白色所占的百分比。有更短的方法可以做到这一点,但我想明确地展示这是在做什么。我们正在计算所有值为 255 的像素(255 表示白色,0 表示黑色)。
现在,我们将从阈值 0 开始,以 10 为增量增加到 255,直到我们处于 0.1–0.28 区域(此代码进入 for 循环):
厉害!终点在望。现在我们得到这样的图像(如果我们使用我们找到的阈值):
大多数图像看起来都很不错!然而,有些是有问题的。结果是一些数字比背景暗,就像你看到的右上角的 9。因此,阈值处理使它们变黑,而不是变白!从未达到 0.1–0.28 区域。
我们可以通过变量值来检查我们的阈值是否成功。如果 threshold 变量的值是 260,这意味着 while 循环没有找到一个完美的阈值就结束了。对于这些图像,我们将有一个单独的过程。
本质上,我们会
- 反转图像,使内部比背景明亮
- 转换成黑白
- 创建一个圆形蒙版来遮盖背景(当我们反转时,背景从黑色变成白色)。
这是视觉过程:
最后一步是最难的,所以我在代码中对它进行了注释。这是我们的整个函数:
我们将调整代码以使用新函数,并执行步骤 6–7。这些都是内置的 OpenCV 转换,所以没什么好奇怪的:
让我们看看图像是什么样的!
厉害!清晰可辨。最初,我们预计精度约为 10%,因为模型输出大小为 10,并且其输出基本上是随机的,因为原始图像看起来一点也不像模型所训练的 MNIST 数据集。有了这些变换后的图像,该模型肯定比随机模型做得更好。如果我们停在这里,我们的准确率大约是 63%,比随机准确率高 6 倍!然而,我们可以做得更多一点。
我们将骨骼化和扩大我们的图像。这将使我们的宽度一致,整体看起来更加统一。让我们开始吧:
这段代码是我们放在大 for 循环中的最后一段代码。结果如下所示:
它们有点参差不齐,但笔迹也是。这些输入对神经网络来说应该不成问题。现在,我们只需重塑我们的列表,加载模型,并进行评估:
将这段代码放在 for 循环下面。概括地说,我们加载我们的模型,重塑数据,并在评估后打印准确性。
结果
运行代码后,打印出来的内容如下:
我们来看看吧!我们得到了…的总体精确度。78%!这比随机测试好 7-8 倍,比中度到重度色盲的人好得多。这是杰出的!
如果我们看看我们对数字的回忆(正确预测的正面观察与实际类别中所有观察的比率),我们会看到我们对 1-5 和 9 有很好的表现。我们在 8 分的时候表现还可以,我们的神经网络在 6 分和 7 分的时候真的很挣扎。
这种方法显然有局限性,并且我列出的变换并不适用于所有可能的色盲图像(实际上,在阈值化步骤之后,在数据集中有一个图像不适用)。试着打印所有处理过的 9,你会看到阈值步骤产生的比率在 0.1 和 0.28 之间,但那是因为背景变成了部分白色。我没有试图找到解决这个问题的方法,因为这只影响了一张图片。
结论
我希望本教程能够提供关于如何使用相似的数据集对不同的数据集进行预测的信息。另外,我希望本教程能帮助初学者更好地掌握 OpenCV、Tensorflow 和 Python。
要查看完整代码并下载图片和模型,请查看 my Github 。
参考
[1]数据科学栈交换(2020),链接
真正的人工智能:理解外推 vs 概括
来源。图片免费分享。
不要混淆这两者
机器学习模型不需要很智能——它们的大多数应用都需要执行像推荐 YouTube 视频或预测客户下一步行动这样的任务。理解外推法和一般化/内插法之间的区别很重要,这样才能理解模型智能的真正含义,并避免混淆这两者的常见问题,这通常是许多模型实现失败的根本原因。
泛化是机器学习的全部要点。该模型被训练来解决一个问题,试图利用从该任务中学到的模式来解决相同的任务,只是略有不同。打个比方,假设一个孩子正在学习如何进行一位数加法。概括是执行相同难度和性质的任务的行为。这也可以称为插值,虽然泛化是一个更常用和理解的术语。
由作者创建。
另一方面,外推是指模型能够从较低维度的训练中获得更高维度的洞察力。例如,考虑一个一年级学生,他被教授一位数加法,然后面临一个多位数加法问题。一年级学生想,“好的,所以当个位数加起来大于 10 时,有一个 10 的部分和一个 1 的部分。我会考虑到这一点,并在十位数上加 1。”当然,这是算术所基于的关键见解,如果你足够努力地思考加法意味着什么,并且理解了位置值,你就能弄明白它。然而,大多数一年级学生从来没有意识到这一点,很少发现如何独立完成学业。
由作者创建。
重要的是要认识到外推是困难的。甚至许多人都无法成功地进行外推——事实上,智力确实是一种能够进行外推的衡量标准,或者能够将在较低维度中解释的概念应用于较高维度(当然,维度是指复杂程度,而不是字面意义)。大多数智商测试都是基于这个前提:以只有真正的推断者才能理解的方式使用标准概念。
就机器学习而言,外推的一个例子可以被认为是在一定范围的数据上进行训练,并能够在不同范围的数据上进行预测。对于简单的模式,例如简单的正数/负数或在一个圆内/不在一个圆内的分类,这可能很容易。
由作者创建。
然而,传统的机器学习方法限制了模型推断更复杂模式的能力。例如,考虑棋盘问题,在这个问题中,二维平面上交替的正方形被着色为 0 或 1。对人类来说,这种关系是清楚的,只要给定一个更小的有限的 x 乘 x 棋盘的规则,就可以继续给一个无限的棋盘着色。虽然棋盘问题是由人类的一组规则定义的(没有两个共用一边的正方形可以是相同的颜色),但数学上它是这样定义的:
由作者创建。
这是一种非常不直观的思维方式,大多数模型都不认为会产生严格的数学推断定义。相反,常规算法本质上试图以几何方式分割特征空间,给定任务,这可能有效也可能无效。不幸的是,在这种情况下,他们不能外推至他们接受训练的坐标之外的坐标。甚至许多神经网络都无法完成这项任务。
KNN 和决策树在 20x20 棋盘上训练并被告知预测 40x40 空间时的结果。由作者创建。
然而,有时候,外推法与其说是为了识别复杂的关系,不如说是为了找到一个智能的、可外推法的解决方案来执行国外范围的任务。例如,棋盘问题可以通过画对角线来解决。例如,已经观察到用神经网络集成发现了这种解决方案。
神经网络来自“通过预测性能加权的竞争学习神经网络集成”。由作者创建。
在机器智能的辩论中,你会听到一个常见的论点,即“机器只能做好一件事。”这确实是插值或泛化的定义——在一套预定义的规则中执行任务*。然而外推法需要对概念有如此坚实的理解,以至于它们可以被应用到额外的领域,或者在教授的领域之外。当前的机器学习模型很少能够可靠地进行推断;通常,那些有希望的人在几何学上更容易推断出一个问题,而在其他问题上失败。所有的人工智能方法本质上都是插值的,即使人工构建一个外推(“智能”)算法也是有争议的。*
外推很少是建模或机器学习的目标,但它经常与泛化互换使用-最明显的当然是线性回归,在这种情况下,当有多个维度在起作用时,将其趋于无穷大的预测作为黄金更常见,也更不明显(多元回归)。
外推法的另一个例子是,比如说,公司将在无异常值的数据上训练一个模型,然后在现实生活中实现它,在现实生活中,异常值要丰富得多,不能简单地忽略。这在模型的实现中很常见,常常不被发现,但这可能是您的模型在现实生活中可能无法达到测试结果的一个重要原因。当模型的定型数据和模型的预测数据之间存在差异时,您可能会要求模型进行外推。
XKCD 。有时甚至人类也是糟糕的推断者。图片免费分享。
模型通常不能很好地外推,无论是在符号智能的测量中还是在实际应用中。确保您的模型没有面临外推任务非常重要-当前的算法,即使像神经网络一样复杂和强大,也不能很好地执行外推任务。检查外推任务的一个不错的方法是绘制出训练和测试集中每一列的分布,然后查看测试集是否与训练集明显不兼容。
在我们能够创建一种能够概括所有问题的机器学习算法之前,类似于神经网络(当然,具有一些不同的架构)的概念如何能够独自解决几乎任何概括问题,它们将永远不会真正能够“智能”并在它们接受培训的狭窄范围之外执行任务。
如果你喜欢的话,你可能会对外推-概括讨论的应用感兴趣,来解释 ReLU 是如何工作的:
神经网络最喜欢的功能背后的直觉
medium.com](https://medium.com/analytics-vidhya/if-rectified-linear-units-are-linear-how-do-they-add-nonlinearity-40247d3e4792)
房地产咨询数据项目
使用 Zillow 数据为房地产投资公司提供建议
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
最近,我接受了一项任务,根据从 Zillow 获得的数据集获得的见解,为一家虚构的房地产投资公司提供关于前 5 个邮政编码的投资建议,该数据集包含 14,723 个邮政编码中每个邮政编码的平均房屋销售价格,时间窗口从 1996 年 4 月开始,到 2018 年 4 月结束。该项目的分析和建模是结合使用 Python 和 R Jupyter 笔记本完成的。数据和项目笔记本可在项目存储库中的这里获得。
由于该项目使用的数据于 2018 年 4 月结束,因此可以想象这是提供咨询的时间,因为世界上最近的发展对金融和房地产市场产生了广泛的影响,而且没有办法将项目数据与当前事件联系起来。可以说,重要的是要记住,宏观经济和社会政治问题会在市场上造成不可预见的冲击,任何预测都应考虑到这种冲击造成的波动集群。幸运的是,这些数据跨越了包括 2007 年至 2010 年金融和房地产市场崩溃在内的时间框架,提供了一个观察单个邮政编码如何经受住上次风暴的机会,让人们对那些有望显示出对未来不利经济和市场条件的弹性的邮政编码有所了解。许多邮政编码在数据库的时间段内没有完整的数据,这使得不可能将它们的行为与那些有完整数据的行为进行适当的比较,因此被排除在考虑范围之外。
投资建议基于两个主要标准:高预期资本增值率和低预期方差,以及在投资期限内在邮政编码范围内租赁物业的盈利能力。为了预测每个邮政编码的资本增值率,设计了一种方法来为每个邮政编码快速生成适当的 SARIMAX 模型,以便可以对投资期限内的预测平均回报率和波动率进行比较。为了比较每个邮政编码内租赁物业的盈利能力,从 2017 年 Zillow research 的一篇文章中获得了一个数据库,其中包含各个大都市地区的 P2R(房价与租金)比率。这些比率可以与全国平均水平进行比较,并给出一个在给定区域内多少年的租金支付可以付清房产价值的概念。
代码块将遵循本文中展示的工作流进行演示。对于所用方法的更深入的研究,请参见资源库中的 Jupyter 笔记本。本文将介绍一种在 Python 工作流中为时间序列数据生成良好拟合的 SARIMAX 模型的有效方法,该工作流足够快,允许对每个邮政编码单独建模,以便可以比较这些模型,找到具有最佳前景的邮政编码。该方法将 Python 和 R(分别为 statsmodels 和 forecast)的时间序列建模包的优势结合起来,通过使用 rpy2 包从 Python 工作流中调用 R 的 forecast 包中 auto.arima 函数的快速模型生成功能,以便为 SARIMAX 模型生成最佳阶数和系数,然后使用获得的值平滑 statsmodels SARIMAX 模型。我们发现,这种方法可以生成始终令人满意的模型,避免了使用循环进行网格搜索以获得最佳模型阶数的耗时过程,并且可以利用 statsmodels 的所有统计明确性和功能,以及在 Python 环境中工作。
请注意,本文的内容仅旨在演示与房地产环境中的时间序列数据分析和建模相关的数据科学工作流程和方法,不得作为任何形式的投资建议。除非另有说明,所有图片都是作者的财产。
加载数据和初始观测值
首先,我们需要导入所有要使用的库;我们将使用许多标准的 Python 数据科学包,尤其是 pandas 和 statsmodels。
现在用熊猫来读取数据:
查看原始数据,我们可以看到有两列用 5 位数来标识“区域”在谷歌上快速搜索几下,就会发现“地区名”一栏包含了邮政编码。另一栏对我们没用。我们还可以观察到,数据是所谓的“宽格式”,其中每个邮政编码的日期及其对应值包含在向右扩展数据框架的列中,这就是它有 273 列的原因。要将数据转换为长格式,可以使用 pandas 的 melt 方法。出于我们的目的,我们需要长格式的数据。测试发现 sizeRank 功能没有用。
该操作复制了在列中找到的每个日期的每个邮政编码,从而得到一个包含 3,901,595 行的数据帧。在这一点上,可以使用多索引数据框架来处理面板数据,但是这里我将使用 GroupBy 对象。首先,需要将“date”列转换为 datetime,并且我们需要检查是否缺少值。
最后一个操作显示在“值”列中有 156,891 个丢失的值,我们需要记住这些值。现在,让我们创建我们的 GroupBy 对象,并首先查看一些邮政编码。
首先看一些数据,我们可以看到各个邮政编码有许多不同之处。只看 14,723 个中的前 6 个,就能看出平均值和增长率的差异。我们还可以看到他们正在遵循一个总体趋势,价值不断上升,直到 2007 年至 2010 年间的次级抵押贷款危机导致市场崩溃,随后在 2013 年左右开始复苏。只看这 6 个,我们可以看到一些邮编比其他的更好地经受住了风暴,这对于那些意识到未来的低迷可能是由不可预见的宏观经济影响引起的投资者来说是一个有吸引力的特征。
我们现在有了一些想法,在确定向投资者推荐的“5 个最佳邮政编码”时,我们可能会寻找什么:增长率和对糟糕经济环境的适应能力;但是还有其他的要考虑。首先,如果预期的增长率与较低的预期波动性相辅相成,人们会对投资最有信心。第二,最好是在持有期间将自有物业出租给租户,这样资本就不会无所事事。这意味着我们应该推荐这样的邮政编码,它不仅具有强劲的预期增长、较低的预期方差和对恶劣经济条件的弹性,而且还具有高于平均水平的租赁盈利能力。为了确定这一点,我们可以求助于一个叫做房价租金比(P2R)的指标。这一比率让我们了解到在某一地区购买和拥有房产的价格与租房者每年愿意支付的金额之间的比例。贾米·安德森撰写的这篇 Zillow research 文章提供了房地产投资者在租赁房产的盈利能力方面应该考虑的更多问题,并提供了我们现在将要导入的数据。
我们可以看到,在这篇文章发表时(2017 年 6 月 19 日,方便地接近我们数据集的末尾),全国平均 P2R 为 11.44。根据这篇文章,大多数“主要市场”的大多数房屋都可以出租获利,因此有理由认为,在 P2R 低于全国平均水平的任何邮政编码进行房地产投资,不仅对业主来说租金是有利可图的,而且盈利能力也将好于全国平均水平。
另一个丰富的房地产投资建议来源于 Sam Dogen 写的这篇金融武士文章。在这篇文章中,多根建议投资者遵循他所谓的“关键房地产投资规则”:购买公用设施,租用奢侈品(伯尔)。多根解释说,豪华房产(尤其是在沿海城市)往往不像公用事业房屋(尤其是在中西部)那样通过租赁获得利润,而且通常根本没有租赁利润,租金无法覆盖每年的拥有成本。多根引用了上面 Zillow research 的文章,并使用 P2R 比率来区分奢侈品和效用,他将 P2R 低于 9.6 的所有东西都标记为效用,P2R 高于 13.3 的所有东西都标记为奢侈品。这意味着我们应该寻找 P2R 低于全国平均水平的邮政编码,如果可能的话,希望低于 9.6。显而易见,由于预期资本增值率是我们的标准之一,我们可能无法获得 P2R 低于 9.6 的所有邮政编码,因为预期增长率通常反映在资本价格中,但这为我们的竞争者提供了一个坚实的目标。同样可以肯定的是,被视为公用事业的住宅所在的邮政编码的平均销售价格不会超过 50 万美元,因此这也将有助于缩小我们的列表。
对单个邮政编码建模
在我们能够遍历所有的邮政编码并为每个邮政编码生成一个模型之前,我们需要一种好的方法来快速生成一个适合单个邮政编码的模型。Statsmodels 为我们提供了 SARIMAX 模型的强大功能,但它不包含确定这种模型的最佳阶数的方法。使用循环和比较 AIC 分数的网格搜索非常慢,特别是如果要考虑 AR 和 MA 项的滞后,比如说 5 或 6。幸运的是,R 的预测包有一个 auto.arima 函数,它可以快速找到给定时间序列的最佳阶数和系数。这样,我们可以选择采用建议的阶数并让 statsmodels 估计系数,或者用给定的系数平滑建议阶数的 statsmodels SARIMAX。在这项研究中发现,由 auto.arima 函数估计的系数始终比使用 statsmodels 估计的系数产生更低的 AIC 分数,因此我们将使用前者来制作我们的模型。为了了解我们将如何做到这一点,我们首先需要看看如何使用 rpy2 从 Python 中调用 R 函数。我们将从进口必要的商品开始。
我们现在可以在 Python 工作流中使用 forecast 包中的函数,但是要利用输出结果还需要一点修补。系数和它们的名字可以在附加到返回对象的 vectors 中获得,但是顺序并不是以这样一种方便的方式存储的。让我们看看 auto.arima 函数返回的打印输出是什么样的,使用测试数据集的第一个邮政编码。请注意,我们必须将时间序列转换为 FloatVector,以便它与 R 函数兼容。另外,请注意如何从 pandas GroubBy 对象访问时间序列,方法是通过名称(邮政编码)获取一个组,将日期列设置为索引,然后获取值列。还要注意,Python 去掉了这些“较低”邮政编码的第一个零,因为它们是数值,但我们知道它实际上是一个前面带零的 5 位数邮政编码。在金融时间序列分析中,通常的做法是对一个序列的收益建模,而不是对价格建模,因为这为我们提供了一个比较所有邮政编码的通用尺度。为了数学上的方便,对数回报通常比百分比增长更受青睐,因为它们可以随着时间的推移进行累积求和。这些是通过取一系列的差分对数值而产生的。这会在我们要删除的序列的第一天产生一个 NaN 值。此外,频率“MS”被分配给产生的返回序列,因为这告诉 pandas 数据是每月的。
这里我们可以看到第一个邮政编码的返回流。我们可以看到序列有趋势,方差不是常数,意味着它不是平稳的。趋势可以通过另一阶差分来消除,但异方差将会保留,不可避免地导致我们模型中的异方差误差。尽管残差将是异方差的,但是只要误差在时间上以零为中心,具有大致的正态分布,并且不是序列相关的,那么该模型将是最有效的。该模型将根据给定估计参数的数据的对数似然性来估计参数,因此受不一致波动性影响的将是该模型的 sigma2(方差)估计值,该模型将在早年的较低方差和 2009 年前后的较高方差之间找到一个合适的中间值。这在我们的预测考虑中非常重要,因为该模型将基于方差的估计值生成置信区间,该估计值在某种程度上偏向于方差较低的过时市场制度,导致置信区间比实际上可能合适的区间更窄。
然而,尽管预测置信区间存在这一问题,但当比较许多在崩溃期间和之后经历不同程度的方差波动的邮政编码的模型时,使用整个时间序列来估计模型参数是有优势的,即使存在异方差,因为崩溃期间更强烈的波动期将导致模型中更高的方差估计值,这些模型适合没有很好处理崩溃的邮政编码。这意味着,一旦所有的模型都生成了,那些对未来方差前景更乐观的人将是那些被提供了波动性没有那么大峰值的数据的人;换句话说,那些在危机中表现出弹性的模型,意味着模型的比较仍然有利于我们找到我们想要的东西。人们可以在事后对预测的有偏 sigma2 估计进行调整,在执行预测之前,仅将平滑步骤中的该参数更改为更能反映近年来的变化的值,但由于这是一个假设项目,已经涵盖了很多内容,因此我将省略它。
寻找 ARMA 模型阶次的传统方法是 Box-Jenkins 方法,它使用 ACF 和 PACF 直观地寻找自相关中的尖峰。我们将在 auto.arima 函数中使用更现代的方法,但看看 ACF 和 PACF 的第一个邮政编码以及建议的订单会很有意思。请记住,回报将需要一个差分顺序,因此我们在查看要建模的自相关之前应用该顺序,并删除前导 NaN 值。
我们可以看到序列中确实存在一些序列相关性,但是很难确切地说出什么样的顺序最适合模拟它。看起来每年的季节性在 12 个滞后时是明显的,在 5 个滞后时有很强的自相关性。让我们看看从 auto.arima 调用返回的对象是什么样子的。请注意,在 R 中使用 ts 函数创建时间序列对象时,频率被指定为 12,以表示每月的数据。
Series: structure(c(-0.00265604405811537, -0.00177462335836864, -0.00266785396119396, -0.00178253166628295, -0.00178571476023492, -0.000894054596880522, -0.000894854645842713, 0, 0.00178890924272324, 0.00178571476023492, 0.00178253166628295, 0.0017793599000786, 0.002663117419484, 0.0017714796483812, 0.0026513493216207, 0.00264433825308963, 0.00263736416608573, 0.00263042676877312, 0.00262352577238545, 0.00261666089117085, 0.0034782643763247, 0.00346620797648711, 0.0025917941074276, 0.00258509407210461, 0.00171969087952739, 0.00171673861905397, 0.000857265376119187, 0.000856531101616653, 0.000855798083895465, 0.0017094021256483, 0.00170648505575954, 0.00170357792478271, 0.00254993763327249, 0.004235499766855, 0.00337553063128126, 0.00336417474563255, 0.00335289501031077, 0.00417537141048108, 0.00332779009267448, 0.00414422474602638, 0.00330305832925681, 0.00329218404347742, 0.00328138112317689, 0.00408664238545242, 0.00407000968829685, 0.00567032706008774, 0.00483482005458313, 0.00401123682645377, 0.00399521106728784, 0.0039793128514809, 0.00396354066245586, 0.00473560474583401, 0.00392927813988919, 0.00469484430420763, 0.00544960476756629, 0.00464756839654612, 0.00616334770766791, 0.0068886609951857, 0.00608366895361456, 0.00604688161489264, 0.00526119214336163, 0.00523365680611043, 0.00520640819057405, 0.00444116199996714, 0.00515654917924557, 0.00513009553102961, 0.00510391191817661, 0.0065241260769433, 0.00719945514285492, 0.00857148105014005, 0.00849863472146239, 0.00842701616188002, 0.00835659459094273, 0.00828734024856992, 0.00958255108099593, 0.00949159668157051, 0.00873367996875452, 0.00932097294306367, 0.0112027530765531, 0.0123739774874423, 0.00902067972593201, 0.00575633185257551, 0.00572338605268641, 0.00632113356336994, 0.00753299230754578, 0.00747667034301891, 0.00680485149838539, 0.0067588582951057, 0.00610502506680355, 0.0072771697738947, 0.00782429510752891, 0.0077635504899245, 0.00711324872451868, 0.0076493459184892, 0.00817284175587396, 0.00925932541279728, 0.0097449897009394, 0.010780246243792, 0.0112234623698484, 0.011650617219976, 0.0120615497338186, 0.0119178008640368, 0.0101795682134629, 0.0100769879503577, 0.0110208410142789, 0.0119326967118454, 0.0128108336717592, 0.0146578312983845, 0.014940516954951, 0.0156942897625889, 0.0149725086303825, 0.01380645591966, 0.012685159527317, 0.0120651115518431, 0.0100964602510096, 0.00682596507039968, 0.0049762599862273, 0.00270392233240102, 0.00134922440076579, 0.000449337235149727, -0.000898876465017295, -0.0013498314760465, -0.00225377602725274, -0.00225886700972744, -0.00181077460070966, -0.000453206443289389, 0, 0.000453206443289389, 0.00181077460070966, 0.00225886700972744, 0.00180342699915137, 0.000900495333249651, -0.0013510472669811, -0.00270758288154482, -0.00452899324870693, -0.00500569873443624, -0.00503088186627743, -0.0045955963233375, -0.00415417167913823, -0.00463607691747825, -0.00605921219926842, -0.00703732672057633, -0.00756147270057639, -0.00809721023262, -0.00623652776946138, -0.00482393603085285, -0.00436152860052985, -0.00389294895540893, -0.00390816325475818, -0.00294117859081666, -0.0024576075284326, -0.00345082915773354, -0.00395844158642866, -0.00347653690108984, -0.00348866538730341, -0.0040020063418531, -0.00351494210744541, -0.00453744161395342, -0.00354341043998474, -0.000507228009860583, 0.00202737018250154, 0.00303336936332954, 0.00252079790656623, -0.00151171608532152, -0.00201918291723047, -0.000505433419908385, 0.00252461633713885, 0.00402617554439999, 0.00300902935161851, -0.00100200409185014, -0.00301205046999264, -0.00706360151874996, -0.00813425939068146, -0.0123268124806586, -0.0135277817381336, -0.00683314091078202, 0.00263365967346196, 0.000525900616906938, -0.00263227317032033, -0.00422610243350618, -0.00424403820047914, -0.00266169973486008, 0.00159786984729493, 0.00318810046864648, -0.00106157122495887, -0.0042575902526405, 0, 0.00159872136369721, -0.00320000273067222, -0.00535619920052355, -0.00215053846322988, -0.00269469308842396, -0.00704037568232785, -0.00873367996875452, -0.00660068403135128, -0.00442478598035656, 0, 0.00442478598035656, 0.00495459312468327, 0.00383667228648576, 0.00327690080231591, 0.00163443239871519, -0.00217983737542049, 0.00109051264896465, 0.00705949171296005, 0.0118344576470033, 0.0085197533447694, 0.00264760546505549, -0.00211752328990755, -0.00318471606752091, -0.00533050302693994, -0.00643433855264597, -0.00431267514794698, -0.00162206037186685, 0.00054097918549445, -0.0021656749124972, 0, 0.0032467560988696, 0.00377460680028641, 0.00322407587175277, 0.00267881221002675, 0.00267165534280522, 0.00266453661509658, 0.00477834729198179, 0.00528263246442684, 0.00525487283835879, 0.00366204960651473, 0.00364868791549178, 0.00104004169541305, -0.0015604684570949, -0.00312826115380993, -0.00261438057407126, 0.00104657257067053, 0.00573665197743445, 0.00673752224774127, 0.00515199490942919, 0.0035906681307285, 0.00306748706786131, 0, 0.00102040825180616, 0.00508648095637376, 0.00455581653586101, 0.00654419652421367, 0.00899106955985651, 0.00496032763096999, 0.000989119764124524, 0.00148184764088199, 0.000493461643371162, -0.000493461643371162, 0.00639608576582695, 0.0102464912091715, 0.0106229873912866, 0.0109864969415678, 0.00945633524203515, 0.00656662772389005, 0.00837993730674924, 0.0115235200038608, 0.00866991513344573, 0.00453309933098467, 0.00271002875886417, 0, 0, 0.00450045764105589, 0.00403316701762257), .Tsp = c(1, 22.9166666666667, 12), class = "ts")
ARIMA(3,1,1)(0,0,2)[12]
Coefficients:
ar1 ar2 ar3 ma1 sma1 sma2
-0.5119 0.0622 -0.3987 0.9257 -0.4116 -0.3586
s.e. 0.0611 0.0649 0.0578 0.0259 0.0666 0.0703
sigma^2 estimated as 2.524e-06: log likelihood=1320.17
AIC=-2626.35 AICc=-2625.91 BIC=-2601.34
我们可以看到这有点乱,但我们需要的一切都在那里。如前所述,参数估计值和它们的名称可以方便地作为易于访问的向量附加到对象上。但是,订单不方便存储,需要通过将输出转换成字符串并对其进行索引来提取所需的信息。下面是两个函数,一个可以提取参数,另一个可以从这个对象中提取订单。
现在我们有了助手函数来从 auto.arima 输出中提取我们想要的信息。还有一个问题需要处理,那就是当 auto.arima 输出有一个常量时,我们需要以一个向量的形式向 statsmodels SARIMAX 对象提供一个外部变量,系数将被分配给这个向量。此外,创建我们的 auto.arima 响应对象可能要简单得多。我们可以创建另一个助手函数,一步提供创建 statsmodels SARIMAX 模型所需的一切,而不是每次都生成这些函数,如下所示:
我们现在拥有了快速生成时间序列的适当模型所需的一切。让我们在我们的第一个邮政编码的日志返回中尝试一下,看看它是否有效:
我们可以看到这个模型有一些问题,但它是相当有效的。正如预期的那样,残差是异方差的,在滞后 5 的残差中有一些轻微的自相关,这不是理想的。然而,残差以零为中心,尽管尖峰值(过度峰度,指由波动性聚类引起的细长峰)导致正态分布残差的 JB 检验零假设被拒绝,但它们不会太远,具有合理的偏斜和峰度。在其他几个邮政编码上测试这一过程表明,生成的模型一般不具有自相关残差,接近但不是正态分布的残差,异方差遵循相同的随时间增加的波动性的一般模式,以及由此产生的稀疏性。有很多讨论集中在这样一个事实上,即具有适合金融时间序列的完美 SARIMAX 模型并不常见,考虑到这个领域,尤其是考虑到它们生成的速度,这些模型是相当可靠的。
比较所有邮政编码的型号
我们现在希望将这个建模过程应用于所有具有完整数据的邮政编码,以及最近平均销售价格低于 500,000 美元的邮政编码。每个模型都将用于预测 5 年内的情况,然后我们将取每个模型预测期内预测平均值和置信下限的平均值进行比较。这两个指标对于邮政编码比较来说是一个很好的组合,因为较高的预期回报均值告诉我们,该模型对未来的回报前景持乐观态度,当这与同一时期较高的置信下限均值配对时,这意味着邮政编码不仅对收益有积极的前景,而且具有相对紧密的置信区间,从而具有较低的预期方差。在金融危机期间经历了不太强烈的波动聚集的邮政编码将在其模型中具有较低的 sigma2 估计值,因此在其预测中具有更紧密的置信区间。这种方法应该会引导我们找到我们投资标准中 3 个最重要的邮政编码:高预期资本增值率,低预期波动性,以及在危机期间对恶劣经济条件的弹性。一旦我们有了竞争对手,我们就可以查看他们的 P2R 比率,找出那些也能满足我们租赁盈利能力需求的比率。
尽管我们有一种新颖高效的方法来生成模型,但 14,723 个邮政编码仍然很多,为每个邮政编码生成一个模型需要几个小时。以下代码将运行适当的循环来编译指标的数据帧,我们将使用该数据帧来选择我们的顶级邮政编码候选项。
0 - 1001
(3, 1, 1)(0, 0, 2, 12) model with no constant
1 - 1002
(0, 1, 3)(0, 0, 2, 12) model with no constant
2 - 1005
(2, 1, 1)(0, 0, 2, 12) model with no constant
3 - 1007
(4, 1, 3)(1, 0, 1, 12) model with no constant
4 - 1008
(0, 1, 3)(0, 0, 1, 12) model with no constant...and so on forever
非常好。短短几个小时后,我们有了一些不错的预测指标,可以引导我们找到更好的投资邮政编码。我们采用的信息量最大的指标是整个 5 年预测中置信下限的平均值。这个数字包含了很多信息,因为如果没有一段时间内的高平均预测回报率以及低预期方差,它就不可能很高。这意味着我们可以按照这个指标降序排列我们的结果,找到目前为止我们的最佳竞争者,然后包括 P2R,以最终确定我们投资的前 5 个邮政编码的列表。
选择前 5 位邮政编码
结果出来了,但我们需要一个更具描述性的数据框架,我们还需要 P2R 比率。之前,我们看到 P2R 数据帧在“区域名称”列中记录了位置,这是地铁的名称以及州的缩写。我们可以从原始数据帧中创建一个类似的列,将 metro 和 state 列组合起来,这样我们就可以合并这两个数据帧。让我们将结果限制在前 200 名,然后遍历索引并创建一个 info dataframe,然后将所有内容合并在一起。
现在我们在一个地方就有了我们需要的所有信息。我们可以看到,P2R 一栏中有缺失值,因此我们无法对这些邮政编码的租赁盈利能力进行评论,我们将专注于那些我们有 P2R 信息的项目。让我们将我们的结果过滤到 P2R 低于全国平均水平的所有东西,然后从那里开始。
现在我们的搜寻即将结束。接下来要做的是分别检查每个邮政编码的模型,为投资者找到最好的机会。为了节省一点空间,这个列表中的前两个邮政编码具有历史上较低的波动性,然后在最近一两个月出现了巨大的意想不到的增长率,这些因素的结合使得模型预测在短期内相当乐观。然而,最好是建议客户投资于近几个月回报率较高的邮政编码,所以我们继续往下看。圣安东尼奥有很高的 P2R 比率,接近多根推荐的 9.6。由于我们的目标 P2R 是 10 左右,让我们考虑一下这意味着什么:需要 10 年的租金支付才能还清拥有一套房产的成本。这意味着,如果投资者持有房产 10 年,并在此期间将其出租,他们将已经还清了初始投资,并且仍然可以以潜在的更高价值出售房产!这给了他们巨大的投资回报。让我们看看圣安东尼奥的模型,并对未来 10 年进行预测。
我们可以看到,除了该数据集中任何邮政编码预期的典型异方差性之外,该模型拟合得相当好。残差接近正态分布,并且残差不是自相关的。让我们看看样本内和样本外的 10 年预测。
这是一个 10 年的样本预测,带我们回到危机中期,那时事情最不确定。
我们可以看到,尽管我们的 sigma2 可能有点低估,但我们模型的置信带包含了 2008 年 4 月以后的所有情况,除了最近的增长峰值。让我们看看这个模型对未来 10 年的预测。
这看起来很不错。预测的平均值为每月 2%左右,置信区间大多在零线以上。阴影区域中低于零线的面积比零线以上的面积小得多,这让投资者相信他们在未来 10 年不太可能亏损。
在这一点上,选择前 5 个邮政编码是通过检查每个邮政编码的单个模型手工完成的。只需在最初的 get_group 调用中更改邮政编码,就可以调整上面的三个代码块来为任何邮政编码生成模型及其预测。在选择前 5 名时,避免选择同一城市的两个邮政编码,因为分散投资在资本投资中通常是一件好事,这样,如果某个地方的市场受到不利影响,投资组合就不会过度暴露。然而,达拉斯/沃斯堡地铁的两个邮政编码被选中,尽管在不同的地区,一个是郊区,一个是市区。最好的 5 个邮政编码是:
所有这些邮政编码都有很好的平均回报预测,较低的估计波动性,以及低于全国平均水平的 P2R 比率。根据 SARIMAX 模型,由于这些邮政编码在预测中也具有最高的平均置信区间下限,因此它们代表了最不可能在未来失去价值的区域。这些邮政编码区的房产可以通过大约 10 年的租金来偿还所有权成本,因此,假设房屋除了保持其价值之外什么也不做,投资者可以在这段时间内获得 100%的利润,房产的任何资本收益都将是额外的。考虑到这些邮政编码具有最低的贬值概率,它们对于房地产投资者来说是相当有吸引力的选择。让我们一起来看看它们的价格曲线。
这让我们很好地了解了这些邮政编码对市场崩溃的弹性,以及我们所期待的近期增长。让我们看看其余前 5 位邮政编码的型号。
钟形扣,TN:
德克萨斯州达拉斯:
马克尔,在:
德克萨斯州花丘:
结论
在这个项目中,一个虚构的房地产投资公司被告知要投资的前五个邮政编码。为了回答这个问题,将领域知识与为每个邮政编码的返回序列生成的 SARIMAX 模型的比较相结合。为了在合理的时间框架内生成如此大量的模型,使用 Python 演示了一种为大量时间序列生成良好拟合模型的有效方法。通过在 Python 环境中使用 R 的 forecast 包中的 auto.arima 函数,可以使用 Python 工作流和 statsmodels 功能,并快速生成适当的模型。邮政编码是由他们对资本增值的展望评估的,寻找低预期方差的强劲增长。然后,具有最佳前景的邮政编码的 P2R 比率被用于筛选结果,同时具有良好前景和高于平均租赁利润率的邮政编码被手动选择用于推荐。由于目标 P2R 约为 10 年,因此生成了 10 年预测,以了解持有期内模型预期是什么,在持有期内,所有者可以预期支付财产所有权的成本。由于所选的邮政编码(78210、37020、75228、46770 和 75028)是数据集中 14,723 个邮政编码中最不可能贬值的,因此它们是房地产投资者的最佳选择。
辨别人工智能的真实例子
人工智能算法展示偏见和成见的真实例子
马库斯·斯皮斯克在 Unsplash 上的照片
目录
- 介绍
- 人工智能偏见的三个真实例子
- 我们能从这一切中学到什么?
介绍
人工智能。
有些人说这是一个没有什么实际意义的时髦词。其他人说这是人类灭亡的原因。
事实是,人工智能(AI)正在引发一场技术革命,尽管 AI 尚未接管世界,但我们已经遇到了一个更紧迫的问题:AI 偏见。
什么是 AI 偏见?
人工智能偏见是用于创建人工智能算法的数据中的潜在偏见,最终可能导致歧视和其他社会后果。
让我举一个简单的例子来阐明这个定义:假设我想创建一个算法来决定一个申请人是否被一所大学录取,而我的输入之一是地理位置。假设来说,如果一个人的位置与种族高度相关,那么我的算法将间接有利于某些种族。这是人工智能偏见的一个例子。
这是危险的。歧视破坏了平等机会,加剧了压迫。我可以肯定地说这一点,因为已经有几个例子表明人工智能偏见确实做到了这一点。
在这篇文章中,我将分享三个真实的例子,说明人工智能算法何时对他人表现出偏见和歧视。
人工智能偏见的三个真实例子
1.美国医疗保健中的种族主义
2019 年 10 月,研究人员发现,在美国医院对超过 2 亿人使用的一种算法预测哪些病人可能需要额外的医疗护理,这种算法严重倾向于白人病人而不是黑人病人。虽然种族本身不是这个算法中使用的变量,但另一个与种族高度相关的变量是,即医疗费用历史。基本原理是,成本总结了一个特定的人有多少医疗保健需求。出于各种原因,平均而言,在同样的情况下,黑人患者比白人患者花费的医疗费用要低。
令人欣慰的是,研究人员与 Optum 合作,将偏差水平降低了 80%。但是如果他们一开始没有被审问,人工智能偏见会继续严重歧视。
2.康派斯牌衬衫
比尔·牛津在 Unsplash 上的照片
可以说,人工智能偏见最显著的例子是 COMPAS(替代制裁的矫正罪犯管理概况)算法,该算法在美国法院系统中用于预测被告成为惯犯的可能性。
由于所使用的数据、所选择的模型以及创建算法的整个过程,模型预测黑人罪犯再犯的误报率(45%)是白人罪犯的两倍(23%) 。
3.亚马逊的招聘算法
布莱恩·安杰洛在 Unsplash 上拍摄的照片
亚马逊是世界上最大的科技巨头之一。因此,毫不奇怪,他们是机器学习和人工智能的大量用户。2015 年,亚马逊意识到他们用于雇用员工的算法被发现对女性有偏见。这是因为该算法是基于过去十年提交的简历数量,由于大多数申请人是男性,它被训练成更倾向于男性而不是女性。
我们能从这一切中学到什么?
很明显,制作无偏见的算法很难。为了创建无偏见的算法,使用的数据必须是无偏见的,创建这些算法的工程师需要确保他们没有泄露任何自己的偏见。也就是说,这里有一些小贴士可以减少偏见:
- 一个人使用的数据需要代表“应该是什么”而不是“是什么”。我的意思是,随机抽样的数据会有偏差,这是很自然的,因为我们生活在一个有偏差的世界,在那里平等的机会仍然是一个幻想。但是,我们必须主动确保我们使用的数据平等地代表每个人,并且不会对特定人群造成歧视。例如,对于亚马逊的招聘算法,如果男性和女性的数据量相等,该算法可能不会有这么大的区别。
- 应该强制实施某种数据治理。由于个人和公司都有某种社会责任,我们有义务规范我们的建模过程,以确保我们在实践中是道德的。这可能意味着几件事,比如雇佣一个内部合规团队对每个创建的算法进行某种审计,就像 Obermeyer 的团队所做的那样。
- 模型评估应包括社会团体的评估。从上面的例子中,我们应该努力确保在比较不同的社会群体(无论是性别、种族还是年龄)时,真实准确率和假阳性率等指标是一致的。
你们还觉得怎么样?每个人都应该采取哪些最佳做法来最大限度地减少人工智能偏差!留下评论,大家一起讨论!
感谢阅读!
特伦斯·申
创始人ShinTwin|我们来连线一下LinkedIn|项目组合这里是。
真实或虚假的关系:你约会的有魅力的人更令人讨厌
因果推理和虚假关联
使用 Python 模拟假相关性
魅力-个性相关性
这里有一个普遍的直觉:你已经非常仔细地挑选了你的约会对象,基于他们看起来有多好,以及他们是否有很好的个性。但不知何故,在你约会过的人中,那些更有魅力的人往往性格更差。吸引力和个性之间有很强的负相关性。
在你断定有魅力的人一定是令人讨厌的之前,我们可以使用一些基本的数据科学技能来测试我们的直觉,看看是否有什么地方出了可怕的问题(剧透:是的,有什么地方出了问题)。
如果你喜欢像这样的因果推理问题和悖论,看看洛德悖论(另一个常见的推理问题):
[## 数据科学:因果推理和洛德悖论:改变分数还是协变量?
hausetutorials.netlify.app](https://hausetutorials.netlify.app/posts/2020-04-11-causal-inference-and-lords-paradox-change-score-or-covariate/)
模拟数据以获得洞察力
但是我们怎么知道吸引力和性格之间的负相关是真的呢?数据科学家定期处理相关性,获得更多关于数据的直觉和学习分析方法的好方法是通过模拟。所以我们模拟一些数据来检验我们的直觉。
Python 模拟
首先,我们导入常用的数据科学模块:numpy
、pandas
、seaborn
(用于可视化仿真结果)。
import numpy as np
import pandas as pd
import seaborn as sns
让我们指定几个模拟参数,模拟一些数据。我们将设置一个随机种子,以便您可以复制我的结果,但如果您愿意,可以跳过它。你的模拟也可以工作,但是你的结果不会和我的一样,但是会非常相似。
# set seed so you can replicate my results
np.random.seed(1)# let’s pretend you have 500 potential dates to choose from
n = 500# generate personality and attractiveness data
# assume data comes from normal distribution
data = {"personality": np.random.randn(n),
"attract": np.random.randn(n)}# store data in pandas dataframe
df = pd.DataFrame(data)
因为个性和吸引力是使用np.random.randn()
随机生成的,所以模拟值来自正态分布,这两种分布并不相关。
sns.jointplot('attract', 'personality', *data*=df, *kind*='reg')
在你的 500 个潜在约会对象中,吸引力和个性之间没有关联。
现在让我们具体说明你的约会标准。这个参数反映了你有多挑剔。我们将它设置为 0.80,这意味着你将只和那些在你的 500 个潜在约会对象中,综合吸引力和个性值排在前 20%的人约会。
quantile = 0.80 # dating criterion
现在让我们决定你实际上和谁约会。
# add personality and attract values
df['personality_attract'] = df['personality'] + df['attract']# determine the 0.80 quantile personality_attract cutoff value
cutoff = df['personality_attract'].quantile(quantile)# determine who to date/reject
# if higher than our cutoff value/criterion, date person
# else (lower than cutoff value/criterion, reject person
# save 'dated'/'rejected' in new 'outcome' column
df['outcome'] = np.where(df['personality_attract'] > cutoff, 'dated', 'rejected')
过滤数据,使其只包含你约会过的人。
# select only rows/people you chosen to date
dated = df.query('outcome == "dated"')
dated
数据帧有 100 行(可以用dated.shape
验证)。在你可能约会的 500 个人中,你和其中的 100 个人约会了(前 20%的人)。
可视化模拟结果
让我们画出你约会过的人的personality
和attract
特征之间的相关性。记住,你选择约会对象是基于他们看起来有多好(‘吸引’)和他们是否有伟大的个性(‘个性’)。
sns.regplot('attract', 'personality', *data*=dated)
假设你之前和我一样使用了相同的随机种子,attract
和personality
之间的相关性应该大约是–0.48,你可以使用 pandas .corr()
方法:dated.corr()
来验证
你约会过的有魅力的人显然个性更差。
另外,如果您经常在 Python 中使用统计模型,我强烈推荐 Pingouin 库。
[## 用于统计测试的新 Python 库—比 statsmodels 更简单,比 scipy.stats 更丰富
统计建模变得更简单
towardsdatascience.com](/new-python-library-for-statistical-tests-simpler-than-statsmodels-richer-than-scipy-stats-ff380d4673c0)
是不是偶然?运行更多模拟
优秀的数据科学家非常了解机会和概率。上面的负相关可能都是由于偶然,所以总是有必要运行多个模拟来看看我们是否能重复上面的结果。
定义模拟功能
为了避免重复代码(软件开发中的 DRY 原则——不要重复自己),让我们定义一个运行模拟的函数。我们基本上是把上面所有的代码放在一个函数里。
下面是我们新定义的simulate
函数的注意事项:
- 模拟参数现在是函数参数
simulations
参数让我们指定运行多少模拟simulate
函数返回所有模拟的相关值
# define function to run simulations
def simulate(*simulations*=1000, *n*=500, *quantile*=0.80):
# initialize array to store simulated correlations
correlations = np.zeros(simulations)
# loop
for i in range(simulations): # same code described above in the article
data = {"personality": np.random.randn(n),
"attract": np.random.randn(n)}
df = pd.DataFrame(data) # identify who you want to date
df['personality_attract'] = df['personality'] + \
df['attract']
cutoff = df['personality_attract'].quantile(quantile)
df['outcome'] = np.where(df['personality_attract'] > cutoff,
'dated', 'rejected')
dated = df.query('outcome == "dated"') # compute correlation between attract and personality
correlations[i] = dated.corr()['personality'][1] return correlations
运行并绘制 1000 次模拟的结果
np.random.seed(1) # set seed so you get same results as me
correlations = simulate()
sns.distplot(correlations).set_title('Histogram of correlations')
下面是来自 1000 次模拟的 1000 个相关值的直方图。直方图的中点约为–0.65,所有模拟的相关性都小于 0。也就是说,在所有 1000 个模拟中,相关性都是负面的:你约会过的有魅力的人性格更差!
吸引力和个性之间的负相关模拟。
用不同的参数再次模拟
如果负相关是因为我们的模拟参数呢?现在,让我们假设你有一个更小的可供选择的人群(300 人),你决定对你的约会标准更加宽松(0.7:你愿意和高于 0.7 分位数的人约会)。
np.random.seed(2) # set seed so you get same results as me
correlations = simulate(n=300, quantile=0.7)
sns.distplot(correlations).set_title('Histogram of correlations')
同样,我们所有的模拟在吸引力和个性之间有负相关(大约-0.60)。
吸引力和个性之间的负相关模拟。
在你下结论之前,多思考和模拟
你认为负相关是真实的还是虚假的?也就是说,有魅力的人真的倾向于性格更差吗,还是我们正在处理的数据的性质有问题?剧透:是后者,但我不会告诉你为什么(因为答案可以在数据中找到),除了告诉你这是一个因果推断问题。
在您结束之前,请试验并调整上面的代码,模拟更多的数据,并更改参数(甚至是代码本身)。至关重要的是,以不同的方式绘制模拟数据和结果,你可能会在数据中看到有趣的模式。
我期待着你的想法,评论,或回答如下。如果你喜欢这篇文章,请关注我的更多数据科学文章,我会在以后的文章中解释因果推理如何帮助我们解决这个(cor)关系问题!
如果您对提高数据科学技能感兴趣,以下文章可能会有所帮助:
[## 在 Python 中重塑 numpy 数组—一步一步的图形教程
本教程和备忘单提供了可视化效果,帮助您理解 numpy 如何重塑数组。
towardsdatascience.com](/reshaping-numpy-arrays-in-python-a-step-by-step-pictorial-tutorial-aed5f471cf0b) [## 在 Python 中更有效地循环的两种简单方法
使用枚举和压缩编写更好的 Python 循环
towardsdatascience.com](/two-simple-ways-to-loop-more-effectively-in-python-886526008a70) [## 使用终端多路复用器 tmux 提高编码和开发效率
简单的 tmux 命令来提高您的生产力
medium.com](https://medium.com/better-programming/code-and-develop-more-productively-with-terminal-multiplexer-tmux-eeac8763d273)
更多帖子, 订阅我的邮件列表 。
具有深度学习的实时 3D 人脸跟踪
我在尝试复制 Snapchat 镜头和抖音特效背后的人工智能技术时学到的东西
电脑眼中的我
napchat 通过在人们的头上戴上有趣的狗耳朵、交换面部表情和其他把戏变得流行起来,这些把戏不仅有趣,看起来也不可能,甚至还有的魔法。我在数字视觉特效行业工作,所以我对这种魔力很熟悉…以及了解它在幕后如何运作的渴望。
魔术背后
修改人脸是好莱坞视觉特效中的常规工作,这是一项当今广为人知的技术,但通常需要数十名数字艺术家才能实现逼真的人脸变换。我们如何实现自动化?
以下是这些艺术家遵循的步骤的简单分类:
- 追踪3D 中人脸相对于摄像机的位置、形状和运动
- 3D 模型的动画捕捉到被跟踪的人脸(如狗鼻子)
- 将 3D 模型的光照和渲染成 2D 图像
- 将渲染的 CGI 图像的与真人动作镜头合成
步骤 2 和 3 的自动化与视频游戏中发生的事情没有太大的不同,它相对简单。合成可以简化为 3D 前景超过现场背景,很容易。挑战在于追踪,一个程序如何“看到”人类头部的复杂运动?
用人工智能追踪人脸
计算机科学界长期以来一直试图自动跟踪人脸,但这很难。近年来,机器学习来拯救我们,每年都有许多关于深度学习的论文发表。我花了一段时间寻找“艺术状态”,并意识到实时做到这一点是非常困难的!这是一个尝试应对挑战的好理由(这将与我已经实现的 AR 美颜模式配合得很好)。
“试图追踪面孔…这很难…实时做这个很辛苦!”
我是这样做的。
设计网络
卷积神经网络常用于图像的视觉分析,并且通常用于诸如对象检测和图像识别的应用。
基本的卷积神经网络架构(来自本刊 ⁹)
对于要实时评估的深度神经网络(至少每秒 30 次),需要紧凑的网络。随着机器学习和智能手机的普及,每年都会发现新的模型,推动效率的极限——在计算精度和开销之间进行权衡。在这些模型中,MobileNet、SqueezeNet 和 ShuffleNet 由于其紧凑性而在移动设备上的应用中很受欢迎。
针对不同复杂程度的 ShuffleNet V2 架构(来自作者)
ShuttleNet V2 最近推出,提供最先进的表演,有各种尺寸,以平衡速度和准确性。它配有 PyTorch,这是选择该型号的又一个原因。
选择要学习的功能
面部标志(摘自本文)
现在我需要找到 CNN 应该学习的特性。一种常见的方法是为面部的不同关键部位定义一系列锚点,也称为“面部标志”。
这些点被编号并战略性地围绕着眼睛、眉毛、鼻子、嘴和下颌线。我想训练网络来识别每个点的坐标,以便稍后可以基于它们重建遮罩或几何网格。
构建训练数据集
因为我想用 3D 效果增强视频,所以我寻找了一个具有 3D 地标坐标的数据集。300W-LP 是少数几个带有 3D 位置的数据集之一,它非常大,并且作为一个额外的好处,它提供了很好的面部角度多样性。我想将我的解决方案与最先进的技术进行对比,最近的出版物在 aflw 2000–3D 上测试了他们的模型,因此我选择 300W-LP 进行训练,并在 aflw 2000–3D 上进行测试以进行比较。
来自数据集 300W-LP 的图像,以数学方式生成剖面图
关于这些数据集的说明,它们是为研究社区准备的,通常不会免费用于商业用途。
扩充数据集
数据集扩充通过向已有的集合添加更多变化来提高训练的准确性。我对每个图像和地标进行了如下的变换,以随机的数量创建新的图像和地标:围绕中心旋转-/+ 40 度,平移和缩放 10%,水平翻转。我在内存中对每张图片和每一个学习阶段(时期)应用不同的随机变换来进行额外的增强。
还需要将输入图像裁剪到界标的边界框附近,以便 CNN 识别界标的相对位置。这是为了节省训练期间从磁盘加载的时间而做的预处理。
设计损失函数
不同参数设置下的机翼损失函数图(图片来自出版物 ⁴)
典型地,L2 损失函数用于测量标志位置的预测误差。最近的出版物 ⁴描述了一个所谓的机翼损失函数,它在这个应用中表现更好,我可以验证。我按照作者的建议,用 w=10 和ε = 2 对其进行参数化,并对所有地标坐标的结果求和。
训练网络
训练深度神经网络是非常昂贵的操作,需要强大的计算机。实际上,使用我的笔记本电脑一个培训阶段要花费数周时间,而建立一个像样的系统要花费数千美元。我决定利用云,这样我就可以只为我需要的计算能力付费。
我选择了 Genesis Cloud ,它提供了极具竞争力的价格和 50 美元的免费积分。我用 GeForce GTX 1080 Ti 构建了一个 Linux 虚拟机,准备了一个操作系统和存储映像,在那里我安装了 PyTorch 并上传了我的代码和数据集,所有这些都是通过 ssh 完成的。一旦系统设置完毕,就可以根据需要启动和关闭,创建快照允许从我离开的地方继续工作。
每个历元的平均误差图
内部训练循环处理 32 个图像的小批量,以最大限度地提高 GPU 上的并行计算。一个学习过程(epoch)处理大约 60,000 个图像的整个集合,并且花费大约 4 分钟。训练集中在 70 个纪元左右,所以为了安全起见,我让它整夜运行 100 个纪元。
我使用流行的 Adam 优化器,它可以自动调整学习率,从 0.001 开始。我发现设置正确的初始学习率是至关重要的,如果它太小,训练过早地收敛于次优解。如果它太大,就很难收敛。我通过反复试验找到了价值,这很费时间…实际上,为每次使用支付云费用是非常昂贵的!
估价
所有这些努力都得到了回报,通过更大的网络洗牌网 V2 2x,我在 aflw 2000–3D 上获得了 2.796 的归一化平均误差(NME)。在该数据集上,这比最先进的模型 ⁵及其 3.07 的非市场经济性要好很多,尽管该模型要重得多!💪
预测的界标与地面真实情况的比较证实了理论结果,界标甚至在大角度下也能精确地找到它们的路径(尽管 aflw 2000–3D 包含我的模型失败的极端角度的挑战性案例)。
在 aflw 2000–3D 上评估我的模型,红色标记来自地面实况,绿色标记预测
我的 MacBook Pro 上没有大的 CUDA GPU 进行推理(对模型的评价)。为了最大限度地利用硬件资源,我将模型转换为可移植格式 ONNX,并使用微软的库 ONNX 运行时,它可以在我的机器上更有效地推断模型(是的,它在 OSX 上运行!).在 CPU 上,推理时间在 100 毫秒以下,这已经很不错了,尽管不是实时的。但是我要记住,我在这里使用最大版本的 ShuffleNet V2 (2x)以获得最大的精度,我可以选择较小的版本以提高速度(例如 1x 版本快 4 倍)。我也可以在 GPU 上运行更好的数字。
这一切都是为了一个强大的面部跟踪系统…至少我是这么认为的。
视频的局限性
我终于可以把训练好的网络接入视频流了。在使用单独的模型来检测面部边界框时需要一个额外的步骤,因此我们可以在接近地标的位置裁剪图像,就像在训练数据集中一样。经过大量的研究,我选择使用这款轻量级人脸检测器,它的评估速度非常快,而且非常精确(我也试过其他的)。
尽管我尽了一切努力,但非常遗憾的是,我发现视频上的结果一点也不稳定:标记抖动很大,随时间漂移,检测不到眨眼。😱
我做错了什么?
摇晃的马克笔
进一步挖掘后,我意识到这是什么。训练数据集仍然是照片,而不是视频帧!它似乎确实在剧照上工作得很好,但视频就不同了。它们代表了额外的挑战:时间一致性(和运动模糊)。
注释不精确(来自本刊 ⁶)
正如来自悉尼科技大学和 Facebook Reality Labs 的这项工作 ⁶所指出的,来自训练数据集的地面真相标记是在每张照片上手动标注的,这不是很精确。如上所示,不同的注释器将标记放置在稍微不同的位置。
我们的网络实际上学习了不精确的地标位置——这导致在预测期间在小区域内随机抖动,而不是精确的位置。
来自互动演示的 1€ 滤镜的图示
解决这个问题的常见方法是在事件发生后稳定预测的标记。为了达到这种效果,正如谷歌研究的另一个出版物 ⁷所建议的那样,我使用了1⁸的€滤波器来平滑运动噪声。我发现它比传统的卡尔曼 滤波器给出了更好的结果,并且更容易实现。我还用它来稳定面部检测器返回的盒子,因为它也抖动很多,这没有帮助。
运动过滤器有很大的不同。但它并不完美,特别是像我笔记本电脑那样的低质量网络摄像头会产生嘈杂的视频,这种模式似乎不太喜欢。
上面提到的来自 UTS 和脸书的论文建议基本上如下微调训练模型:
前向-后向通信方案(摘自本刊 ⁶)
- 通过计算跨帧的光流来预测地标位置(OpenCV 可以做到这一点)
- 从该结果反向计算光流
- 基于来自光流的预测和来自预训练模型的预测之间的距离来计算损失函数
我自己没有尝试过,但是演示视频看起来很有前景。
僵硬的眼睛和眉毛
经过训练的网络受到眼睛运动的影响,它看不到眨眼,也不能完全“关闭”眼睛标志。与他们正在跟踪的相比,眉毛界标在它们的运动中似乎也是僵硬的。
我的宽面角模型
我的模型变得非常擅长预测轮廓,因为数据集中有大量的轮廓(实际上在面部角度方面非常平衡),但没有那么好地学习眼睛。如果 CNN 学习的特征倾向于生活在一致的位置周围,它可以表现得更好,这就是为什么我们必须在推理之前检测人脸边界框,并在训练期间围绕地标进行裁剪:这使得人脸每次都处于中心。
随着脸部角度的变化,脸部内部的特征——眼睛、嘴巴和鼻子——在帧中的形状和位置会发生变化,甚至可以在广角下消失。这使得模型每次都很难识别它们。我试着增加极端眼部姿势(闭着眼,睁着眼)的数据集,但收效甚微。
后续步骤
这个项目很有趣,但耗费时间和金钱(除了最初的 50 美元免费信贷外,我还自掏腰包 15 美元)。这是一个很好的学习机会,但我不打算重新发明轮子。自从我开始这段旅程以来,谷歌已经开源了 MediaPipe ,提供了他们模型的跨平台实现。虽然 MediaPipe 的初始演示显示了一些漂移、僵硬和抖动,这验证了我的结果。
不过,我有一些想法来改进我的方法(从上面提到的基于光流的微调开始)。
3D 可变形人脸模型
4DFM 变形模型
为了能够构建像 Snapchat 这样的应用程序,我们实际上需要的不仅仅是几个 3D 点。一种常见的方法是使用 3D 可变形人脸模型(3DMM)并将其拟合在带注释的 3D 点上。数据集 300W-LP 其实就有这样的数据。网络可以学习 3D 模型的所有顶点,而不是少量的界标,这样 3D 人脸将由模型直接预测,并准备好用于一些 AR 乐趣!
多重网络
为了提高整体精度,前面提到的机翼损失论文,以及谷歌研究的工作,建议用第一个轻量级模型预测一些地标。由于初始信息,我们可以垂直裁剪和对齐面部,以帮助第二个模型处理较少的姿势变化。
我相信,我们可以将专业模型的概念进一步推广到多个网络。我们可以为特定的面部角度训练不同的 CNN,比如一个网络用于大角度(60 +),一个用于中间角度(30 到 60),一个用于正面角度(0 到 30),然后以某种方式将它们结合起来。
或者,不同的网络可以学习不同的面部部分,一个用于眼睛,一个用于嘴,等等…希望简化模型的工作能提高精确度。第一遍可以如上所述检测头部的粗略位置和方向,然后为相关的专家网络裁剪和对准感兴趣的区域。
结论
深度学习在人脸跟踪中的应用是一个活跃的研究领域,在过去几年中取得了很多进展。仍有改进的空间,尤其是在跟踪视频方面。预测的面部特征的时间一致性以及眼睛和嘴巴等关键区域的精度仍然是一个挑战。
我很期待看到这个话题的未来!
[1]: N .马等, ShuffleNet V2:高效 CNN 架构设计实用指南 (2018),
[2]: X .董等,风格聚合网络人脸标志点检测 (2018)
[3]: X .朱等,大姿态人脸对齐:一种 3D 解决方案 (2016),
[4]: Z. Feng 等,用卷积神经网络实现鲁棒的人脸标志点定位 (2018),
[5]: J. Guo 等人,使用双变压器的堆叠密集 U 形网实现鲁棒的人脸对齐 (2018),
[6]: X .董等,注册监督:一种提高人脸标志点检测器精度的无监督方法 (2018)
[7]: Y. Kartynnik 等人,基于移动 GPU 的单目视频实时人脸表面几何 (2019),CVPR
[8]: G. Casiez 等, 1 €滤波器:一种简单的基于速度的低通滤波器,用于交互系统中的噪声输入 (2012),迟
[9]: V.H. Phung 等人,一种用于小数据集上云图像斑块分类的卷积神经网络的高精度模型平均集成 (2019),应用科学
实时自定义对象检测
创建自定义对象检测器指南—第 2 部分
亚采克·迪拉格在 Unsplash 上的照片
在这篇文章中,我们将测试我上一篇文章中定制的暗网模型
建议:视频输出可以通过彭博快带在 YouTube 上获得。一个窗口的图像是我的个人电脑的屏幕截图。输出图像来源取自 Kaggle 的开源数据集。非常感谢 Shauryasikt Jena
在我的上一篇文章中,我们看到了如何使用 darknet 创建一个自定义的蒙版检测器。如果能看到它的行动,那会更有趣,不是吗?)
因此,让我们使它工作,是的,这些步骤比训练模型要容易得多,因为如果您已经按照我以前的文章安装了所需的库(唷!).
如果没有,保持冷静:),可以上我的文章详细查一下。
这是我以前的文章—
自定义暗网完全指南
medium.com](https://medium.com/@tejas.khare99/custom-object-detection-using-darknet-9779170faca2)
使用 CPU 执行整个代码**。如果您正在编写视频输出,您不需要 GPU,视频是根据您首选的每秒帧数值编写的。要编写视频文件,请查看步骤 10。**
内容
- 导入库
- 从培训中获取生成的文件
- 阅读网络
- 输入图像的一些预处理
- 置信度得分、类标识符、边界框的坐标
- 非最大抑制(NMS)
- 绘制边界框
- 使用
- 写入文件(可选)
好吧…让我们成功吧!请通读整篇文章,以免遗漏任何内容。谢谢:)
1.导入库
请导入这些库。
import tensorflow as tf
import numpy as np
import cv2
import pandas as pd
import time
import os
import matplotlib.pyplot as plt
from PIL import Image
N 注:你还需要 ffmpeg==4.2.2+ 来写视频输出文件。如果你有任何问题,请浏览我以前的文章。一旦你有了 ffmpeg,确保你在安装了 ffmpeg 的同一个 anaconda 环境中运行所有的东西。
这就是你所需要的,让我们进入重要的下一步!
2.从暗网和训练模型中收集文件
这是我们的目标探测器运行的非常关键的一步。我在下面列出这些文件,确保你有这些文件。
- 习俗。名称文件
- 习俗。cfg 文件
注:我们在培训前创建了这些文件,所以如果你错过了其中的任何一个,你的模型将会给你带来麻烦。这两个文件对于您的自定义对象检测器来说是非常特定的,我以前的文章将指导您可以进行哪些更改。你可以冷静下来!创建这些文件只需一分钟,如果遵循每个细节:)
3.习俗。权重文件
好吧…让我们在这里暂停一分钟来理解你是如何得到它的。
这个文件被称为重量文件,它通常是一个大文件,也取决于你的训练大小(对我来说是 256mb)。当你的训练完成后,你就会得到这个文件**。**在我的例子中,我使用的文件名是 yolov 3 _ custom _ train _ 3000 . weights。在这里,“3000”意味着文件是在完成 3000 个历元后生成的。如果你已经通过了。cfg 文件,您会发现纪元设置为 6000。
那么我为什么没有用‘yolov 3 _ custom _ train _ 6000 . weights’呢?
这是因为经过一些测试后,我发现 3000 个历元后生成的权重文件在实际生成的所有权重文件中具有最好的准确性,而不仅仅是“6000”的那个。
好。因此,更多的时代应该意味着更高的准确性,对不对?
不尽然:(
更多的历元也意味着过拟合,这会大大降低精度。我的训练数据可能有一些重复的图像,或者我可能将错误地标记为****(是的,我知道…这是一个乏味的任务,所以…你知道思想是如何偏离正确的),这确实对准确性有直接影响。
3。阅读网
现在…测试部分开始。我会尽我所能让它简单易懂,显然,理解并排:)
幸运的是,cv2 有一个内置函数。
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)
ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
这些漂亮的功能通过直接读取存储在 Darknet 模型文件中的网络模型并为我们的探测器代码设置它们,使我们的日常工作变得更加容易。!).
有关该函数的更多信息—
cv::dnn::blobFromImage(input array image,double scalefactor=1.0,const Size &size= Size(),const Scalar & mean=…
docs.opencv.org](https://docs.opencv.org/3.4/d6/d0f/group__dnn.html#gafde362956af949cce087f3f25c6aff0d)
您还需要从“yolo.names”文件中获取标签…
LABELS = open(labelsPath).read().strip().split("\n")
N 注:configPath、weightsPath 和 labelsPath 包含各自文件的路径
4.输入图像的一些预处理
这些是我们需要为我们的模型做的一些步骤,以获得一些预处理的图像。预处理包括 均值减法和缩放 。
cv::dnn::blobFromImage(input array image,double scalefactor=1.0,const Size &size= Size(),const Scalar & mean=…
docs.opencv.org](https://docs.opencv.org/3.4/d6/d0f/group__dnn.html#ga29f34df9376379a603acd8df581ac8d7)
(H, W) = image.shape[:2]
blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416),
swapRB=True, crop=False)
net.setInput(blob)
layerOutputs = net.forward(ln)# Initializing for getting box coordinates, confidences, classid boxes = []
confidences = []
classIDs = []
threshold = 0.15
5.获得一些自信
是的…实际上在这一步之后,我们会对我们的代码有一些信心,并且更好地理解我们已经做了什么,以及在这之后我们要做什么。
for output in layerOutputs:
for detection in output:
scores = detection[5:]
classID = np.argmax(scores)
confidence = scores[classID] if confidence > threshold:
box = detection[0:4] * np.array([W, H, W, H])
(centerX, centerY, width, height) = box.astype("int")
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
boxes.append([x, y, int(width), int(height)])
confidences.append(float(confidence))
classIDs.append(classID)
那么这段代码到底在做什么呢?
包含一个巨大的 2D 浮点数数组,从中我们需要“将要”绘制的边界框的坐标、classid 和每个预测的置信度得分,或者我们可以说是检测:)
6.非最大抑制(NMS)
对…最初,当我没有向它提供正确的输入数据类型时,这一步给了我很大的困难。我也试过 NMS 的一些预写功能,但是我的物体检测太慢了…
尼克·艾布拉姆斯在 Unsplash 上拍摄的照片
在撞了我的头一段时间后(不是字面上的…),我能够通过为这个超快救生功能编写上一步中给出的代码来获得正确的输入数据类型。
idxs = cv2.dnn.NMSBoxes(boxes, confidences, threshold, 0.1)
你可以在这里找到一些信息—
[## OpenCV:深度神经网络模块
cv::dnn::blobFromImage(input array image,double scalefactor=1.0,const Size &size= Size(),const Scalar & mean=…
docs.opencv.org](https://docs.opencv.org/master/d6/d0f/group__dnn.html#gaeec27cb32195e71e6d88032bda193162)
那么什么是 NMS 呢?
该模型返回多个预测,因此单个对象存在多个框。我们当然不希望这样。多亏了 NMS,它为该对象返回了一个单一的最佳包围盒。
为了深入了解 NMS 及其运作方式—
一种消除目标检测中重复和误报的技术
towardsdatascience.com](/non-maximum-suppression-nms-93ce178e177c)
7.绘制边界框
Aahhaa…有趣的部分。现在让我们的探测器开始工作吧
mc = 0
nmc = 0if len(idxs) > 0:
for i in idxs.flatten():
(x, y) = (boxes[i][0], boxes[i][1])
(w, h) = (boxes[i][2], boxes[i][3]) if LABELS[classIDs[i]] == 'OBJECT_NAME_1'):
mc += 1
color = (0, 255, 0)
cv2.rectangle(image, (x, y), (x + w, y + h), color, 1)
text = "{}".format(LABELS[classIDs[i]])
cv2.putText(image, text, (x + w, y + h),
cv2.FONT_HERSHEY_SIMPLEX,0.5, color, 1)
if (LABELS[classIDs[i]] == 'OBJECT_NAME_2'):
nmc += 1
color = (0, 0, 255)
cv2.rectangle(image, (x, y), (x + w, y + h), color, 1)
text = "{}".format(LABELS[classIDs[i]])
cv2.putText(image, text, (x + w, y + h),
cv2.FONT_HERSHEY_SIMPLEX,0.5, color, 1)text1 = "No. of people wearing masks: " + str(mc)
text2 = "No. of people not wearing masks: " + str(nmc)
color1 = (0, 255, 0)
color2 = (0, 0, 255)cv2.putText(image, text1, (2, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color1, 2)
cv2.putText(image, text2, (2, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color2, 2)
搞定了。!这段代码会给你一个包含你的边界框的图片/框架
N 注:请务必根据您的对象名称更改OBJECT_NAME_1
和OBJECT_NAME_2
。这将使你更好地理解你的代码;)
T ip:我建议你创建一个函数,在其中传递一个图像,因为以后你可以将这个函数用于视频以及图像输入;)
9.使用
上面的代码有两种用途—
- 实时,即将视频传递给探测器
这可以通过从视频中读取帧来完成,如果你愿意,你也可以调整它的大小,以便你的“cv2.imshow”以更快的速度显示输出帧,即每秒帧。用 cv2 阅读视频——
[## 视频入门- OpenCV-Python 教程 1 文档
学会看视频,显示视频,保存视频。学会从相机捕捉并显示它。你会学到这些…
opencv-python-tutro als . readthedocs . io](https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_gui/py_video_display/py_video_display.html)
注意:你不需要将获得的帧转换成灰度。
现在只需将框架传递给功能(在提示中提到)和吊杆…你有你的实时物体探测器准备好了!
输出视频—
以 20fps 写入的视频输出
N 注:上面的视频输出很流畅,因为我已经以每秒 20 帧(fps)的速度将这些帧写入了一个. mp4 文件
2.图像
你也可以通过传递一张图片来测试你的物体检测器。(是啊…没那么好玩)。用 cv2 来读取图像—
[## 图像入门- OpenCV-Python 教程 1 文档
cv2.waitKey()是一个键盘绑定函数。它的参数是以毫秒为单位的时间。该功能等待…
opencv-python-tutro als . readthedocs . io](https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_gui/py_image_display/py_image_display.html)
输出图像—
物体探测器的单图像输出
10.写入文件(可选)
你可能想知道我是如何让视频输出如此流畅的,对吗?这里有一个技巧,你可以用它来获得流畅的视频输出…
OpenCV 有一个函数叫做 cv2。VideoWriter(),您可以通过指定文件名、codecid、fps 和与输入字段相同的分辨率来编写帧。
[## OpenCV: cv::VideoWriter 类引用
视频编剧类。更大的…默认构造函数。构造函数初始化视频编写器。在 Linux FFMPEG 上…
docs.opencv.org](https://docs.opencv.org/3.4/dd/d9e/classcv_1_1VideoWriter.html)
在 while 循环之外定义变量out
,在该循环中读取视频的每一帧
out = cv2.VideoWriter('file_name.mp4', -1, fps,
(int(cap.get(3)),int(cap.get(4))))
注意:第二个参数’-1 '是要给出的 codecid,但是它在我的计算机上工作得很好。计算机上的 codecid 可能不同。请访问这个网站进行调试—
[## 无法使用 OpenCV 和 Python 编写和保存视频文件
感谢贡献一个堆栈溢出的答案!请务必回答问题。提供详细信息并分享…
stackoverflow.com](https://stackoverflow.com/questions/38397964/cant-write-and-save-a-video-file-using-opencv-and-python)
最后一个参数将帮助您获得输入视频的分辨率。之后,将下面的代码放入调用检测器函数的 while 循环中。
while True:
....
....
image = detector(frame)
out.write(image)
....
....
注意:你的探测器函数应该返回一个“图像”
ip:你也可以用“moviepy”将你的画面写入视频…
MoviePy 依赖于 Python 模块 Numpy、imageio、Decorator 和 tqdm,这些模块将在…
pypi.org](https://pypi.org/project/moviepy/)
pjreddie 暗网的推理时间图
原来如此!我希望你现在已经有了自己的自定义对象检测器。干杯!
感谢您通读整篇文章,希望您能从中受益。如果你有任何反馈,他们是最受欢迎的!**
使用 Azure 的 Power BI 中的实时数据流
使用 Azure 创建无缝的实时 Power BI 仪表板
背景
Azure Event Hubs 是一个大数据流平台和事件摄取服务,每秒可以跟踪和处理数千个事件。来自活动中心的数据可以使用实时分析服务进行转换和存储。 Azure Stream Analytics 是一款实时分析服务,旨在帮助分析和处理可用于触发警报和行动的数据流。Power BI 是一项商业分析服务,提供交互式可视化。
目标
利用 Azure 数据接收/查询服务,如事件中心和流分析——成功创建自流式 Power BI 实时仪表板
设置 Azure
- 首先,访问 Azure 门户,通过以下链接设置您的 Azure 免费试用订阅:https://azure.microsoft.com/en-us/free/
- 一旦您的免费试用帐户被激活,请访问 azure 门户网站:https://portal.azure.com/#home.仔细检查右上角,确保您已使用您的免费试用 azure 帐户登录。如果不是,请确保登录,然后再执行后续步骤。
- 在左上角的标题下:“微软 Azure”,点击创建资源。
4.在“搜索市场”搜索栏中,键入“资源组”。点击标题为“资源组”的第一个结果,然后点击创建。
5.点击 create 后,您将转到一个页面,在该页面中您可以设置新资源组的设置。为您的新资源组键入任何名称,然后点击下面标题为“Review+Create”的蓝色按钮。要进入资源组,请单击位于门户右上角的通知图标。然后点击“转到资源组”。
6.现在,再次单击门户右上角的 create a new resource 按钮。
7.在搜索栏中,输入“活动中心”。点击标题为“活动中心”的第一个结果****
8.这将再次把您带到一个页面,您可以在这里设置新的活动中心的设置。为您的活动中心键入任何名称。将定价等级设置为标准。启用 Kafka,并确保“使该命名空间区域冗余”被关闭。订阅应设置为免费试用。选择您在之前步骤中创建的现有资源组。保持位置设置为美国西部,并将吞吐量单位设置为 4。同时启用自动充气。
8.点击“创建”后,需要几分钟时间来部署事件中心。单击右上角的通知图标,查看事件中心部署的状态。一旦部署成功,单击通知描述中的事件中心链接名称**。**
点击您的活动中心的名称。在我的例子中,我将点击文本 TestEventHub56
9.单击后,它会将您带到您的部署页面。只需单击转到该资源即可访问您的事件中心名称空间。****
10.进入 event hub 名称空间后,查看右侧列中的各种资源部分。在实体下查找标题为**“事件中心”的部分。**
11.
转到活动中心页面后,单击单词活动中心旁边的+图标。点击后,您将进入活动中心创建设置页面。键入新事件中心的名称(不同于您的命名空间名称),并将分区计数和消息保留时间设置为 4。将捕获设置为关闭**。然后点击创建。将立即创建事件中心,并且您将被转移回您的名称空间中的事件中心页面。要访问新的事件中心**实例,单击其名称。
12.下一个设置是流分析作业。要进行设置,单击左上角的 create resource 按钮。然后,搜索上游分析作业。点击第一个结果,然后点击创建。您将再次转到流分析作业设置页面。在“设置”页面中,输入您的 ASA 作业名称,并选择您创建的资源组。保持托管环境**为云,并将流式单元移动到 4。**
13.部署您的 ASA(Azure Stream Analytics)作业需要几分钟时间。一旦部署成功,点击通知栏中的转到资源。
14.进入流分析作业后,单击右侧栏中作业拓扑下的输入。****
在输入页面中,点击页面顶部的新流输入**。一旦点击,它将带来三个不同的流输入供您选择。点击“活动中心”。**
**15. **1。**点击活动中心后,将显示输入设置页面。为您的输入别名设置一个名称,并保持其他设置不变。确保将“**事件序列化格式”设置为 JSON。接下来,点击保存。这将自动开始测试与 ASA 和您的活动中心的连接。它将很快弹出一个通知,说你的连接是成功的。
16.接下来,转到“作业拓扑”下的输出部分****
一旦你点击输出。选择页面顶部的添加按钮。向下滚动选择 Power BI 作为输出。在 Power BI 设置页面中,设置 Power BI 输出别名的名称。然后点击授权。登录您的 Power BI 个人/工作帐户。登录后,为数据集选择一个名称。运行流分析作业会在 Power BI 中自动创建流数据集。数据集名称将是该流式数据集的名称。还要选择一个表名。
17.一旦您单击 save,它将自动开始测试到输出的连接。它应该说连接成功。
18.现在,回到您的事件中心名称空间。点击右上角“创建资源”下的主页图标。确保选择“事件中心名称空间”而不是实例。名称空间是您在本教程中创建的第二个资源。单击后,您将位于事件中心名称空间内。在右侧栏中,点击设置下的共享访问策略**。**
****
Python 设置
我们将使用 Python 向我们的 Azure 事件中心发布数据。请随意使用最适合您的 IDE。流行 Python ide:py charm,notepad++
我们将使用 pandas 读取 excel 文件中的数据,然后将数据值发布到 Azure Event Hub。
建立一个包含行和列的简单 Excel 表格。至少有 15-20 行数据供 Python 脚本读取。我的数据将包含产量、SKU、序列号、产品重量、皮带速度等列。
负载字典应该分别具有列名和行值数据类型的键值对。
确保安装所有必需的软件包,否则程序将无法运行。
现在您的 python 程序已经设置好了,可以运行了。
运行程序并确认您的结果没有任何错误。
流分析和 Power BI
- 现在继续你在 Azure 门户中的流分析工作。点击“职位拓扑”下的查询****
- 从这里,将以下代码复制到您的流分析查询中:
用您的列名替换 SKU、产量、BeltEfficiency 等。确保相应地调整数据类型。
流分析查询数据类型的完整列表可以在这里找到
3.现在点击你的输入名旁边的 3 个省略号。接下来,单击来自输入的样本数据。
4。等待您的输入被采样。采样完成后,您将会收到一个通知,告知您的样本已经成功。现在点击页面顶部的 test。
5.一旦测试成功,它应该会在查询的底部弹出您的结果。现在,单击 ASA 作业右侧的概述。然后点击开始。这将启动您的流式作业。
6.ASA 作业需要一段时间才能开始流式传输,但一旦开始,将会在 Power BI 工作区的数据集部分创建一个新的流式数据集。在这里,您可以创建一个新的仪表板,并使用您的流数据集创建视觉效果。
联系人:
索希尔·巴蒂亚
使用 OpenCV 和 Dlib 的实时眼睛跟踪
在本教程中,学习通过 python 中的网络摄像头创建一个实时凝视探测器。
第一步是下载所需的包。通过 pip 安装:
pip install opencv-python
pip install dlib
或者,如果您使用 Anaconda,那么使用 conda:
conda install -c conda-forge opencv
conda install -c menpo dlib
除此之外,我们需要一个面部关键点检测器,可以实时检测眼睛。为此,我们将使用 dlib 库中预先训练好的网络,该网络可以检测出本论文中提出的“68 个关键点”。所需的预训练模型可以从这里下载。使用 Dlib 是因为它可以实时给出预测,不像 CNN 模型,它对我来说非常重要,因为我正在为在线监督制作 AI。
Dlib 面部关键点。图片取自此处。
使用 Dlib 的眼睛检测
首先要做的是找到眼睛,然后我们才能继续进行图像处理,并找到我们需要找到一张脸的眼睛。面部关键点检测器将 dlib 模块的一个矩形对象作为输入,它只是一张脸的坐标。为了找到人脸,我们可以使用 dlib 内置的正面人脸检测器。您可以使用任何分类器来完成这项任务。如果你想要高精度和速度对你来说不是问题,那么我会建议你使用 CNN,因为它会提供更好的精度,特别是对于非正面人脸和部分遮挡的人脸,如下面链接的文章所示。
Dlib 提供了一个很好的面部标志检测器,但是当面部处于陡峭的角度时,它不能很好地工作。学习如何…
towardsdatascience.com](/robust-facial-landmarks-for-occluded-angled-faces-925e465cbf2e)
import cv2
import dlibimg = cv2.imread('image.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert to grayscale detector = dlib.get_frontal_face_detector()
rects = detector(gray, 1) # rects contains all the faces detected
这时我们有了矩形的人脸对象,我们可以把它传递给关键点检测器。
def shape_to_np(shape, dtype="int"):
coords = np.zeros((68, 2), dtype=dtype)
for i in range(0, 68):
coords[i] = (shape.part(i).x, shape.part(i).y)
return coordspredictor = dlib.shape_predictor('shape_68.dat')
for (i, rect) in enumerate(rects):
shape = predictor(gray, rect)
shape = shape_to_np(shape)
for (x, y) in shape:
cv2.circle(img, (x, y), 2, (0, 0, 255), -1)
获得输出。
关于 dlib 的更详细的描述可以在这篇很棒的文章中找到,这篇文章是我在做这个项目时参考的。
利用 OpenCV 寻找眼球中心
照片由 Anastase Maragos 在 Unsplash 拍摄
我们将通过网络摄像头获得现场直播。要打开网络摄像头,读取帧并显示它,您可以使用以下代码:
import cv2cap = cv2.VideoCapture(0)
while(True)
ret, img = cap.read()
cv2.imshow("Output", img)
if cv2.waitKey(1) & 0xFF == ord('q'): # escape when q is pressed
break
所以,现在我们如何从网络摄像头读取帧,是时候对他们进行工作,以达到我们的目标。我们创建一个新的黑色遮罩,使用与我们的摄像头框架相同尺寸的 NumPy。存储来自关键点阵列形状的左右眼点的(x,y)坐标,并使用cv2.fillConvexPoly
将其绘制在蒙版上。它接受一个图像,点作为一个 NumPy 数组,数据类型= np.int32
和颜色作为参数,并返回一个图像,这些点之间的区域用该颜色填充。
def eye_on_mask(mask, side):
points = [shape[i] for i in side]
points = np.array(points, dtype=np.int32)
mask = cv2.fillConvexPoly(mask, points, 255)
return maskleft = [36, 37, 38, 39, 40, 41] # keypoint indices for left eye
right = [42, 43, 44, 45, 46, 47] # keypoint indices for right eye
mask = np.zeros(img.shape[:2], dtype=np.uint8)
mask = eye_on_mask(mask, left)
mask = eye_on_mask(mask, right)
这样做之后,我们有了一个黑色的蒙版,眼睛区域是白色的。这个白色区域用一个形态学操作cv2.dilate
扩大了一点。使用cv2.bitwise_and
和我们的蒙版作为我们图像上的蒙版,我们可以分割出眼睛。将所有的(0,0,0)像素转换为(255,255,255),这样只有眼球是唯一剩下的黑暗部分。将结果转换为灰度,使图像为阈值处理做好准备。
kernel = np.ones((9, 9), np.uint8)
mask = cv2.dilate(mask, kernel, 5)
eyes = cv2.bitwise_and(img, img, mask=mask)
mask = (eyes == [0, 0, 0]).all(axis=2)
eyes[mask] = [255, 255, 255]
eyes_gray = cv2.cvtColor(eyes, cv2.COLOR_BGR2GRAY)
阈值处理用于创建二元掩模。因此,我们的任务是找到一个最佳阈值,根据这个阈值,我们可以从眼睛的其余部分中分割出眼球,然后我们需要找到它的中心。但是阈值对于不同的照明条件是不同的,所以我们可以制作一个可调节的跟踪条来控制阈值。平心而论,我从 Stepan Filonov 那里得到了这个想法,他也试图在这篇文章中解决这个凝视检测的问题,并使用了 Haar cascade 和 Blob 检测。阈值处理步骤,即腐蚀,膨胀和中值模糊也是从他那里得到的,但他的最终结果并不令人信服,所以我做了这个解决方案。
def nothing(x):
pass
cv2.namedWindow('image')
cv2.createTrackbar('threshold', 'image', 0, 255, nothing)
threshold = cv2.getTrackbarPos('threshold', 'image')
_, thresh = cv2.threshold(eyes_gray, threshold, 255, cv2.THRESH_BINARY)
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=4)
thresh = cv2.medianBlur(thresh, 3)
用跟踪条反转后显示阈值
我们已经到了项目的最后一步。眼球被分割出来,我们可以利用cv2.findContours
来找到它们。现在我们的背景是白色的,眼球是黑色的。但是在 OpenCV 的cv2.findContours()
方法中,要查找的对象应该是白色的,背景是黑色的。所以我们需要使用cv2.bitwise_not
来反转我们的阈值。现在我们可以找到轮廓。理论上,我们可以说,我们现在需要做的是找到两个最大的轮廓,这些应该是我们的眼球。然而,这为假阳性留下了一点空间,可以通过找到眼睛之间的中点并将图像除以该点来解决。然后我们在那些划分中找到最大的轮廓,应该是我们的眼球。关键点 40 和 43(在 Python 中是 39 和 42,因为 index 从零开始)用于寻找中点。用cv2.contourArea
排序,找到中点两边最大的等高线。我们可以利用cv2.moments
找到眼球的中心。
def contouring(thresh, mid, img, right=False):
cnts, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnt = max(cnts, key = cv2.contourArea) # finding contour with #maximum area
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if right:
cx += mid # Adding value of mid to x coordinate of centre of #right eye to adjust for dividing into two parts
cv2.circle(img, (cx, cy), 4, (0, 0, 255), 2)# drawing over #eyeball with redmid = (shape[39][0] + shape[42][0]) // 2
contouring(thresh[:, 0:mid], mid, img)
contouring(thresh[:, mid:], mid, img, True)
在运行时,我们的代码会抛出几个类似于max() arg is an empty sequence
或division by zero
的错误,分别在没有找到轮廓或M['m00']
为零时抛出。要解决这个问题,请将轮廓绘制函数包含在 try 块中。如果只在没有检测到眼睛时才会出现错误,我们不需要做任何事情。新的轮廓绘制函数将如下所示:
def contouring(thresh, mid, img, right=False):
cnts, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
try:
cnt = max(cnts, key = cv2.contourArea)
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if right:
cx += mid
cv2.circle(img, (cx, cy), 4, (0, 0, 255), 2)
except:
pass
一切都结束了。只需显示 img 和 thresh 并相应地设置阈值跟踪栏,就可以享受了。完整代码:
使用人工智能进行在线监督的完整代码可以在我的 Github 上找到。
使用 TensorFlow、Keras 和 OpenCV 的实时人脸检测器
了解如何使用 Tensorflow、Keras 和 OpenCV 以及您的网络摄像头或移动摄像头创建实时面具检测器
在本文中,我将使用由 Prajna Bhandary 创建的面具数据集。该数据集由属于with mask
和without mask
2 类的 1376 幅图像组成。
我们的主要焦点是检测一个人是否戴着面具,而不需要靠近他们。
概观
简而言之,首先,我们获得带有人脸的图像,并将其通过级联分类器。分类器将给出面部的感兴趣区域(高度和宽度)。其次,我们将把感兴趣区域的大小调整为一个100x100
,并将其传递给预先训练好的 CNN,它将把概率作为输出给我们。
步骤 1:数据预处理
我们使用的数据集由不同颜色、不同大小和不同方向的图像组成。因此,我们需要将所有图像转换为灰度,因为我们需要确保颜色不应该成为检测蒙版的临界点。之后,我们需要在将它应用到神经网络之前,使所有的图像都具有相同的大小(100x100
)。
数据预处理
第二步:训练 CNN
这包括 2 个卷积层(两个 Convo2D 100 @ 3x3)。首先,您必须从数据预处理加载数据集。然后你必须配置卷积架构。我已经包括了一个model.add(Dropout(0.5))
来消除过度拟合。由于我们有两个类别(带面具和不带面具),我们可以使用binary_crossentropy
。你从一个模型关卡开始训练 20 个纪元。
训练 CNN
请记住,您可以使用更多的卷积层或使用外部训练器,如 MobileNetV2 以获得更好的准确性。
您可以绘制图表来做出更好的验证决策。我已经把它放在我的知识库里了。请参考。
步骤 3:检测戴面具和不带面具的人脸
首先,您必须加载我们创建的模型。然后我们把想要的相机设置为默认。
可选—您可以将手机摄像头(Android/IOS)连接到 OpenCV。为您的手机和 PC 下载 DroidCam 应用程序。并更改代码
*source = cv2.VideoCapture(1)*
其次,我们需要标注两个概率(0 代表with_mask
,1 代表without_mask
)。之后,我们需要使用 RGB 值设置边框颜色。我给了红色和绿色两种颜色。
在一个无限循环中,我们将从相机中一帧一帧地读取,并将它们转换成灰度并检测人脸。并且它将通过一个for
循环来运行,以针对每张脸检测感兴趣的区域,调整其大小并将其重塑为 4D,因为训练网络期望 4D 输入。对于模型,我们将使用可用的最佳模型来获得结果。这个结果由带面具或不带面具的概率(result=[P1, P2]
)组成。之后会贴上标签。
检测遮罩
让我们看看演示,
演示
编辑描述
drive.google.com](https://drive.google.com/open?id=1oLIPBOkCuhej-iNTre_qnlQ3b-fWZeEy) [## ravindu 9701/面罩检测器
在 GitHub 上创建一个帐户,为 ravindu 9701/面罩探测器的开发做出贡献。
github.com](https://github.com/ravindu9701/Face-Mask-Detector.git)
结论
如果使用 MobileNetV2 进行微调,可以提高精确度。我们也可以将这个模型部署到嵌入式系统中。如果我们正确部署它,我们可以帮助确保您和他人的安全。
文章到此为止。希望对某个人有用。谢谢,注意安全!
资源
物体上的实时手指检测器—工作示例
普里西拉·杜·普里兹在 Unsplash 上的照片
介绍
最近,我有机会构建一个 PoC(概念验证——演示)来解决一个特定的计算机视觉问题,这是一个很酷的体验,为什么不分享一下呢?
演示的目标是实时检测,以摄像机的视频流作为输入,如果特定对象(假身份证)上有手指,覆盖的信息可以在帧完成后用于 OCR 任务。
换句话说,一个可以帮助自动捕获 id 文档的系统确信文档上的所有相关数据都是可见的。
不
不
好的
我们将涵盖的内容
简而言之,这些是我们将涉及的要点:
- 构建一个工作示例(即使不完美)来验证方法的重要性
- 如何使用一个标准框架——CRISP-DM——即使是在一个轻量级的版本中,也可以非常有用地指导项目的所有重要阶段
- 工作码
重要提示:在这个项目的制作过程中没有手指受伤:)
所以让我们开始吧!
构建一个工作示例
创建一个工作模型的目标是验证一种方法,看看第一个结果是否指向正确的方向,或者很快失败并尝试不同的东西。
PoC 是一个工作示例,既可以在开发期间内部使用,也可以向某人展示——猜猜是什么——证明方法的有效性。
但是在开始编码之前,理解问题、可用的数据和所有需要预先知道的东西是必要的。
为了做到这一点,使用结构化的方法会有所帮助,这里 CRISP-DM 开始发挥作用。
使用基于 CRISP-DM 的方法
CRISP-DM(数据挖掘的跨行业标准流程)是一个框架,用于在解决面向数据的问题时拥有一个精确且可重复的步骤和任务列表。
顾名思义,它用于自 1999 年就存在的数据挖掘项目——但也可以用于今天的 ML 项目,因为它们也是高度面向数据的。
在这里,您可以看到迭代过程的主要阶段,从业务问题到生产部署。
Kenneth Jensen —自己的工作基于:FTP://public . dhe . IBM . com/software/analytics/SPSS/documentation/modeler/18.0/en/modelercrispdm . pdf
因此,让我们按照它来描述在 PoC 中做了什么,跳过对演示没有用的步骤,如准备项目计划或评估成本/收益分析。
商业理解
业务需求是自动执行捕获文档的过程,并确保检索到的数据可用于进一步的用途:特别是,包含整个文档的图片,没有任何东西覆盖它的一部分。
由于该过程是使用相机完成的,而卡的所有者将文档放在它的前面,所以目标是确保我们可以用有用的数据来终止捕获会话。
作为一个在短短几天内开发出来的 PoC,引入了约束以保持其简单性:
- 固定捕捉区域,以避免跟踪对象和处理与相机的距离的必要性——我想这一限制也可以用于制作系统。在演示中,一个简单的蓝色方框将指示屏幕上感兴趣的区域
- 在这篇文章中,出于隐私的原因,使用了一个假文件,而不是一个真正的身份证
- 仅考虑了文档的侧面部分——稍后将详细介绍
- 不太关注焦点——使用了基于拉普拉斯方差的简单模糊阈值检查函数,但结果表明它非常依赖于光线条件,因此不能一概而论。作为高级预处理步骤,这一部分专用 ML 解决方案可能更有效
- 只有手指被认为是不需要的物体
- 有限的评估和训练数据集,使用特定的摄像头捕获—仅使用我的笔记本电脑来捕获和评估数据
的成功标准是,基于准确性度量(客观)和实例如何工作(经验)。
数据理解
输入数据是从使用笔记本电脑摄像头创建的视频流中获取的图像。
数据准备
捕获大量几乎相同的帧可能会令人烦恼,因此引入了一个参数来捕获或评估视频流中每 x 帧的一个帧。
作为输入,来自原始帧的两个子集被考虑用于捕获和评估,即左右边界和上下边界,通过选择和组合特定的图像部分来构建。
左+右捕捉
顶部+底部捕捉
没有做进一步的预处理。
除了彩色图像矩阵表示之外,没有添加其他特征。
注意:可能值得研究 hog 表示(基于边缘检测)是如何工作的
建模
显而易见的方法是考虑一个受监督的问题,并为训练建立四个不同的标记数据数据集(ok 上下图像、ko 上下图像、ok 左右图像、ko 左右图像)。
使用了两个简单的 CNNs 因为它是处理图像识别任务的事实上的标准——具有相同的架构但不同的输入形状,一个用于上下图像,一个用于左右图像。回想起来,一个更好的解决方案可以使用一个独特的图像,建立旋转的左帧和右帧,并用自上而下的一个进行组装,因此只能使用一个 CNN。
一旦捕获了足够的标记数据,CNN 就被训练和评估。如果在测试实况会话期间,一个主要的分类错误是明显的,则该图像被添加到正确的类,并且该网络用添加的新数据从头开始再次训练。
估价
该评估基于准确性,使用与训练和测试数据分离的维持(验证)集。
在我的例子中,由于严重的过度拟合,结果非常高——太高了,因为只使用了几百张图像。
除了数值之外,作为一个概念验证,甚至现场观看也是证明它可以工作的必要条件。
这是一个例子。让我们描述一下你所看到的:
- 主区域窗口,带有指示放置卡的位置的蓝色框
- 两个框架窗口—上下和左右图像—带有一个小的彩色框,指示在评估模式下图像是否正常(绿色)或不正常(红色)。这个想法是,如果上下和左右都可以,那么对于连续数量的帧来说,整个帧都可以
让我们看看它的实际表现(https://youtu.be/rzG7LHYfiho
手指探测器在工作
部署
PoC 在笔记本电脑上本地运行,但很容易将分类器想象为一个公开的 API,或者使用 TensorFlow Lite 等在本地部署到特定的目标设备。
这两种方法各有利弊,选择超出了 PoC 的范围,主要是因为需要更多多样化的数据来决定什么是最好的。
代码
该代码由以下文件组成:
- py:它处理视频流。可用于捕获帧,用于训练目的,或使用预测器实例评估帧以进行预测
- py:它包含用于预测的模型
- trainer _ CNN . py:CNN 及相关加载、训练、保存和评估方法
- properties.py:检测器和训练器使用的属性列表
- utils.py:处理缓存图像和处理文件的辅助方法
- 在捕获模式下启动程序
- main_predict.py:以评估模式启动程序
- py:从头开始训练和评估 cnn,并使用文件夹中所有可用的图像
代码可从 GitLab 的这里获得
它是如何工作的
1 —运行main _ capture . pyT5。它将创建必要的目录并启动相机。按“c”启动捕获阶段,将出现两个以上的窗口,显示上下和左右帧的捕获图像,这些图像将保存在 sample_left_right 和 sample_top_bottom 文件夹中。
再次按下“c”停止捕捉。改变位置,再次按“c”开始另一个记录会话。如果捕获了太多模糊图像,请提高 capture _ fuzzy _ threshold 属性值。
记录有效图像(没有手指,只有文档边框可见)和无效图像(手指可见)。完成后按“q”。**
2 —目视评估捕获的图像,并将它们移动到正确的文件夹中(ok_left_right 等)。重复步骤 1 和 2,直到有足够的数据可用
3 —运行main _ train _ CNN . py*。它会将训练图像保存在磁盘缓存上以便更快地检索(此处未使用,因为从头开始构建),编译和训练 CNN 模型,使用维持集执行评估,打印结果并将模型保存在磁盘上。*
4 —运行main _ predict . py*。它将使用上一步中保存的模型创建一个预测器,并启动摄像机。按“e”开始评估。将出现另外两个窗口,显示上下和左右框架的分类结果。一旦达到一定数量的 ok 帧(均为绿色),将显示结果图像*
结论
我在开发这个 PoC 的过程中得到了很多乐趣,甚至因为我以前从未使用过 OpenCV,所以我有机会在解决这个问题的同时学到了很多东西。
更一般地说,尝试解决一个真正的问题比只是看教程或看书更有用,所以找到问题,理解它们,然后实施可行的解决方案!
利用机器学习进行实时欺诈检测
内部 AI
随着我们的生活和金融从物理世界转向数字世界,实时欺诈检测将占据中心位置。
Bermix 工作室在 Unsplash 拍摄的照片
与我们的父母和祖父母不同,我们生活和呼吸在数字世界中。最初是在线论坛上的讨论,然后是聊天和电子邮件,现在我们整个生活和金融交易的大部分都是以数字模式进行的。
随着风险越来越高,事后发现欺诈是不够的。想象一下,某人拥有一些关于您的银行或信用卡详细信息的机密信息,能够执行欺诈交易。银行和保险公司需要工具和技术来实时检测欺诈,以便采取适当的措施。
当我们超越三维空间时,我们人类失去了解释和观想的感觉。
如今,金融交易涉及数百个参数,如交易金额、过去的交易趋势、交易的 GPS 位置、交易时间、商户名称等。我们需要考虑许多参数来实时检测异常和欺诈。
Scikit-Learn 中实施的隔离森林算法有助于实时识别欺诈,避免经济损失。在这篇文章中,我将讨论一个带有机器学习的欺诈交易的一步一步的过程。
第一步:我们需要导入将要使用的包。我们将使用“make_blobs”来生成我们的测试数据,并将使用 accuracy_score 来测量拟合模型的准确性。
**from sklearn.datasets import make_blobs
from sklearn.metrics import accuracy_score
from sklearn.ensemble import IsolationForest**
**第二步:**在现实生活中,我们基于数百万和数十亿的过去交易和数百个参数来建立模型。在本文中,我们将考虑一百个样本和四个特性来理解核心概念和过程。
**X, y = make_blobs(n_samples=[4,96], centers=[[5,3,3,10],[9,3,6,11]], n_features=4, random_state=0, shuffle="True")**
数组 X 保存 100 条记录的四个参数的值,y 存储它是欺诈交易还是正常交易。
步骤 3: 我们将使用集合中的 300 个基本估计量(树)和来自数据集的 10 个样本来训练每个基本估计量。
**clf = IsolationForest(n_estimators=300,max_samples=10,
random_state=0,max_features=4,contamination=0.1).fit(X)**
此外,我们将为模型使用所有四个特征值(“max_feature”参数)。在项目中,通过特征工程,确定每个参数的重要性,并确定模型所基于的特征列表。我不会在本文中讨论特性工程的细节,我会在另一篇文章中讨论它。 IsolationForest 模型进一步拟合样本数据集。
我们基于历史数据中异常的比例和针对错误警报的丢失异常的风险来设置参数“污染”的值。假设历史数据集中欺诈交易的比例为 0.05 %,且这是一个非常高风险的交易。在这种情况下,我们可能希望将污染值设置为 0.25 到 0.35。将污染值设置为历史数据记录中异常比例的 5 到 7 倍将确保没有欺诈交易被错误地分类。事实上,与异常比例相比,设置较高的污染值也会导致错误警报的增加。在风险较低的情况下,我们可能会错过一些欺诈交易,但通过较低的污染值减少错误警报。
步骤 4: 在下面的代码中,fitted IsolationForest 模型预测交易是欺诈还是正常交易。IsolationForest 预测异常为“-1”,正常事务为“1”。在我们的样本测试数据集中,欺诈交易编码为“0”,正常交易编码为“1”。
**y_pred=clf.predict(X)
y_pred[y_pred == -1] = 0**
为了将模型预测准确性与样本数据集中的实际分类进行比较,我们将对预测的欺诈交易进行从“-1”到“0”的分类。
**第五步:**由于现在欺诈交易在样本和预测集中被标记为“0”,因此我们可以直接用 accuracy_score 函数比较模型的预测精度。
**fraud_accuracy_prediction= round(accuracy_score(y,y_pred),2)
print("The accuracy to detect fraud is {accuracy} %" .format (accuracy=fraud_accuracy_prediction*100))**
该模型似乎以 93%的准确率识别了欺诈交易。乍一看,该模型的预测准确性可能不够好,但请记住,由于风险更高,因此我们可以接受很少的假警报(假阳性)。这些假警报牺牲了预测的准确性,但它比错过一些欺诈交易更安全。
第 6 步:我们将使用混淆矩阵更深入地研究预测。
**from sklearn.metrics import confusion_matrix
print(confusion_matrix(y, y_pred))**
在样本数据集中的总共 100 笔交易中,该模型可以识别所有四笔真实的欺诈交易。
由于模型中污染(安全系数)参数为 0.1,模型将七个真实交易标记为欺诈(假警报)。我们将污染值设置为高于历史数据中欺诈交易的实际比例,因为在风险更高的情况下,安全比遗憾更好。
步骤 7: 我们已经编写了一个小函数来实时检测新交易是否是欺诈。它将新交易馈送的参数值输入到已训练的模型中,以检测交易的真实性。
**def frauddetection(trans):
transaction_type=(clf.predict([trans]))
if transaction_type[0] < 0:
print("Suspect fraud")
else:
print("Normal transaction")
return**
步骤 8: 在新交易时收集各种交易参数。
**frauddetection([7,4,3,8])
frauddetection([10,4,5,11])**
通过调用前面用事务参数定义的函数来确定事务的真实性。
我已经简化了一些事情,如交易中的特征数量,历史交易的数量以适应模型,特征工程等。来解释核心概念。我们已经看到隔离林算法如何帮助实时检测欺诈性交易。
如果您想知道我们如何利用探索性数据分析执行特征工程,请阅读关于探索性数据分析(EDA)的高级可视化的文章。
Python 中的实时头部姿态估计
使用 Python 和 OpenCV 创建一个头部姿态估计器,它可以告诉你头部朝向的角度。
头部姿态估计器
头部姿态估计在计算机视觉中是一个具有挑战性的问题,因为需要各种步骤来解决它。首先,我们需要在帧中定位面部,然后定位各种面部标志。如今,识别人脸似乎是一件微不足道的事情,面对镜头的人脸也是如此。当面部有角度时,问题就出现了。此外,由于头部的运动,一些面部标志不可见。在这之后,我们需要将这些点转换到 3D 坐标来找到倾斜度。听起来工作量很大?不要担心,我们将一步一步来,并提到两个伟大的资源,这将使我们的工作容易得多。
目录
- 要求
- 人脸检测
- 面部标志检测
- 姿态估计
要求
对于这个项目,我们需要 OpenCV 和 Tensorflow,所以让我们安装它们。
#Using pip
pip install opencv-python
pip install tensorflow#Using conda
conda install -c conda-forge opencv
conda install -c conda-forge tensorflow
人脸检测
我们的第一步是在图像中找到面部标志。对于这个任务,我们将使用 OpenCV 的 DNN 模块的 Caffe 模型。如果你想知道它与其他模型如 Haar Cascades 或 Dlib 的正面人脸检测器相比表现如何,或者你想深入了解它,那么你可以参考这篇文章:
一个关于用 Python 实现不同人脸检测模型的完整教程,通过比较,找出最好的…
towardsdatascience.com](/face-detection-models-which-to-use-and-why-d263e82c302c)
你可以从我的 GitHub 库下载需要的模型。
import cv2
import numpy as npmodelFile = "models/res10_300x300_ssd_iter_140000.caffemodel"
configFile = "models/deploy.prototxt.txt"
net = cv2.dnn.readNetFromCaffe(configFile, modelFile)img = cv2.imread('test.jpg')
h, w = img.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0,
(300, 300), (104.0, 117.0, 123.0))
net.setInput(blob)
faces = net.forward()#to draw faces on image
for i in range(faces.shape[2]):
confidence = faces[0, 0, i, 2]
if confidence > 0.5:
box = faces[0, 0, i, 3:7] * np.array([w, h, w, h])
(x, y, x1, y1) = box.astype("int")
cv2.rectangle(img, (x, y), (x1, y1), (0, 0, 255), 2)
使用cv2.dnn.readNetFromCaffe
加载网络,并将模型的层和权重作为参数传递。它在大小调整为 300x300 的图像上表现最佳。
面部标志检测
最常用的一个是 Dlib 的面部标志检测,它给我们 68 个标志,但是,它没有给出很好的准确性。相反,我们将在这个 Github repo 中使用尹提供的面部标志检测器。它还提供了 68 个地标,这是一个在 5 个数据集上训练的 Tensorflow CNN!预先训练好的模型可以在这里找到。作者只写了一系列解释包括背景、数据集、预处理、模型架构、训练和部署的帖子,可以在这里找到。我在这里提供了一个非常简短的摘要,但是我强烈建议您阅读它们。
在第一个系列中,他描述了视频中面部标志的稳定性问题,然后列出了现有的解决方案,如 OpenFace 和 Dlib 的面部标志检测以及可用的数据集。第三篇文章是关于数据预处理和准备使用的。在接下来的两篇文章中,工作是提取人脸并在其上应用面部标志,以使其准备好训练 CNN 并将它们存储为 TFRecord 文件。在第六篇文章中,使用 Tensorflow 训练了一个模型。在本文中,我们可以看到损失函数在训练中有多重要,因为他首先使用了tf.losses.mean_pairwise_squared_error
,该函数在最小化损失时使用点之间的关系作为优化的基础,并且不能很好地概括。相比之下,当使用tf.losses.mean_squared_error
时,它工作得很好。在最后一篇文章中,模型被导出为 API,并展示了如何在 Python 中使用它。
该模型采用大小为 128x128 的正方形盒子,其中包含人脸并返回 68 个人脸标志。下面提供的代码取自这里,它也可以用来绘制 3D 注释框。该代码被修改以在所有的脸上绘制面部标志,而不像原始代码那样只在一张脸上绘制。
这个代码将在人脸上绘制面部标志。
绘制面部标志
使用draw_annotation_box()
,我们也可以绘制如下所示的注释框。
带注释框
姿态估计
这是一篇关于 Learn OpenCV 的很棒的文章,它解释了图像上的头部姿态检测,其中有很多关于将点转换到 3D 空间的数学知识,并使用cv2.solvePnP
来找到旋转和平移向量。快速通读这篇文章将有助于理解其内在的工作原理,因此我在这里只简单地写一下。
我们需要面部的六个点,即鼻尖、下巴、嘴唇的最左和最右点,以及左眼的左角和右眼的右角。我们采用这些面部标志的标准 3D 坐标,并尝试估计鼻尖处的合理和平移向量。现在,为了进行精确的估计,我们需要摄像机的固有参数,如焦距、光学中心和径向失真参数。我们可以估计前两个,并假设最后一个不存在,以使我们的工作更容易。获得所需的向量后,我们可以将这些 3D 点投影到 2D 表面上,这就是我们的图像。
如果我们只使用可用的代码,并找到与 x 轴的角度,我们可以获得如下所示的结果。
结果
它非常适合记录头部上下移动,而不是左右移动。那么如何做到这一点呢?嗯,上面我们已经看到了一个标注框的脸。如果我们能利用它来测量左右运动。
带注释框
我们可以找到两条深蓝色线中间的线作为我们的指针,找到与 y 轴的角度,找到运动的角度。
结果
把两者结合起来,我们就可以得到我们想要的结果。完整的代码也可以在我的 GitHub 库找到,还有在线监督解决方案的各种其他子模型。
在 i5 处理器上测试时,即使显示图像,我也能获得每秒 6.76 帧的健康帧,而面部标志检测模型只需 0.05 秒即可找到它们。
现在我们已经创建了一个头部姿势检测器,你可能想做一个眼睛凝视跟踪器,然后你可以看看这篇文章:
在本教程中,学习通过 python 中的网络摄像头创建一个实时凝视探测器。
towardsdatascience.com](/real-time-eye-tracking-using-opencv-and-dlib-b504ca724ac6)